<commit_before>// Copyright (c) 2019 The Jaeger Authors.
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"io"
	"log"
	"os"

	"github.com/opentracing/opentracing-go"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	jaegerClientConfig "github.com/uber/jaeger-client-go/config"
	jaegerClientZapLog "github.com/uber/jaeger-client-go/log/zap"
	"github.com/uber/jaeger-lib/metrics"
	_ "go.uber.org/automaxprocs"
	"go.uber.org/zap"

	agentApp "github.com/jaegertracing/jaeger/cmd/agent/app"
	agentRep "github.com/jaegertracing/jaeger/cmd/agent/app/reporter"
	agentGrpcRep "github.com/jaegertracing/jaeger/cmd/agent/app/reporter/grpc"
	"github.com/jaegertracing/jaeger/cmd/all-in-one/setupcontext"
	collectorApp "github.com/jaegertracing/jaeger/cmd/collector/app"
	"github.com/jaegertracing/jaeger/cmd/docs"
	"github.com/jaegertracing/jaeger/cmd/env"
	"github.com/jaegertracing/jaeger/cmd/flags"
	queryApp "github.com/jaegertracing/jaeger/cmd/query/app"
	"github.com/jaegertracing/jaeger/cmd/query/app/querysvc"
	"github.com/jaegertracing/jaeger/pkg/config"
	"github.com/jaegertracing/jaeger/pkg/version"
	ss "github.com/jaegertracing/jaeger/plugin/sampling/strategystore"
	"github.com/jaegertracing/jaeger/plugin/storage"
	"github.com/jaegertracing/jaeger/ports"
	"github.com/jaegertracing/jaeger/storage/dependencystore"
	"github.com/jaegertracing/jaeger/storage/spanstore"
	storageMetrics "github.com/jaegertracing/jaeger/storage/spanstore/metrics"
)

// all-in-one/main is a standalone full-stack jaeger backend, backed by a memory store
func main() {

	setupcontext.SetAllInOne()

	svc := flags.NewService(ports.CollectorAdminHTTP)

	if os.Getenv(storage.SpanStorageTypeEnvVar) == "" {
		os.Setenv(storage.SpanStorageTypeEnvVar, "memory") // other storage types default to SpanStorage
	}
	storageFactory, err := storage.NewFactory(storage.FactoryConfigFromEnvAndCLI(os.Args, os.Stderr))
	if err != nil {
		log.Fatalf("Cannot initialize storage factory: %v", err)
	}
	strategyStoreFactory, err := ss.NewFactory(ss.FactoryConfigFromEnv())
	if err != nil {
		log.Fatalf("Cannot initialize sampling strategy store factory: %v", err)
	}

	v := viper.New()
	command := &cobra.Command{
		Use:   "jaeger-all-in-one",
		Short: "Jaeger all-in-one distribution with agent, collector and query in one process.",
		Long: `Jaeger all-in-one distribution with agent, collector and query. Use with caution this version
by default uses only in-memory database.`,
		RunE: func(cmd *cobra.Command, args []string) error {
			if err := svc.Start(v); err != nil {
				return err
			}
			logger := svc.Logger                     // shortcut
			rootMetricsFactory := svc.MetricsFactory // shortcut
			metricsFactory := rootMetricsFactory.Namespace(metrics.NSOptions{Name: "jaeger"})
			tracerCloser := initTracer(rootMetricsFactory, svc.Logger)

			storageFactory.InitFromViper(v)
			if err := storageFactory.Initialize(metricsFactory, logger); err != nil {
				logger.Fatal("Failed to init storage factory", zap.Error(err))
			}

			spanReader, err := storageFactory.CreateSpanReader()
			if err != nil {
				logger.Fatal("Failed to create span reader", zap.Error(err))
			}
			spanWriter, err := storageFactory.CreateSpanWriter()
			if err != nil {
				logger.Fatal("Failed to create span writer", zap.Error(err))
			}
			dependencyReader, err := storageFactory.CreateDependencyReader()
			if err != nil {
				logger.Fatal("Failed to create dependency reader", zap.Error(err))
			}

			strategyStoreFactory.InitFromViper(v)
			if err := strategyStoreFactory.Initialize(metricsFactory, logger); err != nil {
				logger.Fatal("Failed to init sampling strategy store factory", zap.Error(err))
			}
			strategyStore, err := strategyStoreFactory.CreateStrategyStore()
			if err != nil {
				logger.Fatal("Failed to create sampling strategy store", zap.Error(err))
			}

			aOpts := new(agentApp.Builder).InitFromViper(v)
			repOpts := new(agentRep.Options).InitFromViper(v, logger)
			grpcBuilder := agentGrpcRep.NewConnBuilder().InitFromViper(v)
			cOpts := new(collectorApp.CollectorOptions).InitFromViper(v)
			qOpts := new(queryApp.QueryOptions).InitFromViper(v, logger)

			// collector
			c := collectorApp.New(&collectorApp.CollectorParams{
				ServiceName:    "jaeger-collector",
				Logger:         logger,
				MetricsFactory: metricsFactory,
				SpanWriter:     spanWriter,
				StrategyStore:  strategyStore,
				HealthCheck:    svc.HC(),
			})
			c.Start(cOpts)

			// agent
			grpcBuilder.CollectorHostPorts = append(grpcBuilder.CollectorHostPorts, cOpts.CollectorGRPCHostPort)
			agentMetricsFactory := metricsFactory.Namespace(metrics.NSOptions{Name: "agent", Tags: nil})
			builders := map[agentRep.Type]agentApp.CollectorProxyBuilder{
				agentRep.GRPC: agentApp.GRPCCollectorProxyBuilder(grpcBuilder),
			}
			cp, err := agentApp.CreateCollectorProxy(agentApp.ProxyBuilderOptions{
				Options: *repOpts,
				Logger:  logger,
				Metrics: agentMetricsFactory,
			}, builders)
			if err != nil {
				logger.Fatal("Could not create collector proxy", zap.Error(err))
			}
			agent := startAgent(cp, aOpts, logger, metricsFactory)

			// query
			querySrv := startQuery(
				svc, qOpts, qOpts.BuildQueryServiceOptions(storageFactory, logger),
				spanReader, dependencyReader,
				rootMetricsFactory, metricsFactory,
			)

			svc.RunAndThen(func() {
				agent.Stop()
				cp.Close()
				c.Close()
				querySrv.Close()
				if closer, ok := spanWriter.(io.Closer); ok {
					err := closer.Close()
					if err != nil {
						logger.Error("Failed to close span writer", zap.Error(err))
					}
				}
				tracerCloser.Close()
			})
			return nil
		},
	}

	command.AddCommand(version.Command())
	command.AddCommand(env.Command())
	command.AddCommand(docs.Command(v))

	config.AddFlags(
		v,
		command,
		svc.AddFlags,
		storageFactory.AddFlags,
		agentApp.AddFlags,
		agentRep.AddFlags,
		agentGrpcRep.AddFlags,
		collectorApp.AddFlags,
		queryApp.AddFlags,
		strategyStoreFactory.AddFlags,
	)

	if err := command.Execute(); err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
}

func startAgent(
	cp agentApp.CollectorProxy,
	b *agentApp.Builder,
	logger *zap.Logger,
	baseFactory metrics.Factory,
) *agentApp.Agent {

	agent, err := b.CreateAgent(cp, logger, baseFactory)
	if err != nil {
		logger.Fatal("Unable to initialize Jaeger Agent", zap.Error(err))
	}

	logger.Info("Starting agent")
	if err := agent.Run(); err != nil {
		logger.Fatal("Failed to run the agent", zap.Error(err))
	}

	return agent
}

func startQuery(
	svc *flags.Service,
	qOpts *queryApp.QueryOptions,
	queryOpts *querysvc.QueryServiceOptions,
	spanReader spanstore.Reader,
	depReader dependencystore.Reader,
	rootFactory metrics.Factory,
	baseFactory metrics.Factory,
) *queryApp.Server {
	spanReader = storageMetrics.NewReadMetricsDecorator(spanReader, baseFactory.Namespace(metrics.NSOptions{Name: "query"}))
	qs := querysvc.NewQueryService(spanReader, depReader, *queryOpts)
	server, err := queryApp.NewServer(svc.Logger, qs, qOpts, opentracing.GlobalTracer())
	if err != nil {
		svc.Logger.Fatal("Could not start jaeger-query service", zap.Error(err))
	}
	go func() {
		for s := range server.HealthCheckStatus() {
			svc.SetHealthCheckStatus(s)
		}
	}()
	if err := server.Start(); err != nil {
		svc.Logger.Fatal("Could not start jaeger-query service", zap.Error(err))
	}
	return server
}

func initTracer(metricsFactory metrics.Factory, logger *zap.Logger) io.Closer {
	traceCfg := &jaegerClientConfig.Configuration{
		ServiceName: "jaeger-query",
		Sampler: &jaegerClientConfig.SamplerConfig{
			Type:  "const",
			Param: 1.0,
		},
		RPCMetrics: true,
	}
	traceCfg, err := traceCfg.FromEnv()
	if err != nil {
		logger.Fatal("Failed to read tracer configuration", zap.Error(err))
	}
	tracer, closer, err := traceCfg.NewTracer(
		jaegerClientConfig.Metrics(metricsFactory),
		jaegerClientConfig.Logger(jaegerClientZapLog.NewLogger(logger)),
	)
	if err != nil {
		logger.Fatal("Failed to initialize tracer", zap.Error(err))
	}
	opentracing.SetGlobalTracer(tracer)
	return closer
}
<commit_msg>Only add the collector port if it was not explicitly set (#2396)<commit_after>// Copyright (c) 2019 The Jaeger Authors.
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"io"
	"log"
	"os"

	"github.com/opentracing/opentracing-go"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	jaegerClientConfig "github.com/uber/jaeger-client-go/config"
	jaegerClientZapLog "github.com/uber/jaeger-client-go/log/zap"
	"github.com/uber/jaeger-lib/metrics"
	_ "go.uber.org/automaxprocs"
	"go.uber.org/zap"

	agentApp "github.com/jaegertracing/jaeger/cmd/agent/app"
	agentRep "github.com/jaegertracing/jaeger/cmd/agent/app/reporter"
	agentGrpcRep "github.com/jaegertracing/jaeger/cmd/agent/app/reporter/grpc"
	"github.com/jaegertracing/jaeger/cmd/all-in-one/setupcontext"
	collectorApp "github.com/jaegertracing/jaeger/cmd/collector/app"
	"github.com/jaegertracing/jaeger/cmd/docs"
	"github.com/jaegertracing/jaeger/cmd/env"
	"github.com/jaegertracing/jaeger/cmd/flags"
	queryApp "github.com/jaegertracing/jaeger/cmd/query/app"
	"github.com/jaegertracing/jaeger/cmd/query/app/querysvc"
	"github.com/jaegertracing/jaeger/pkg/config"
	"github.com/jaegertracing/jaeger/pkg/version"
	ss "github.com/jaegertracing/jaeger/plugin/sampling/strategystore"
	"github.com/jaegertracing/jaeger/plugin/storage"
	"github.com/jaegertracing/jaeger/ports"
	"github.com/jaegertracing/jaeger/storage/dependencystore"
	"github.com/jaegertracing/jaeger/storage/spanstore"
	storageMetrics "github.com/jaegertracing/jaeger/storage/spanstore/metrics"
)

// all-in-one/main is a standalone full-stack jaeger backend, backed by a memory store
func main() {

	setupcontext.SetAllInOne()

	svc := flags.NewService(ports.CollectorAdminHTTP)

	if os.Getenv(storage.SpanStorageTypeEnvVar) == "" {
		os.Setenv(storage.SpanStorageTypeEnvVar, "memory") // other storage types default to SpanStorage
	}
	storageFactory, err := storage.NewFactory(storage.FactoryConfigFromEnvAndCLI(os.Args, os.Stderr))
	if err != nil {
		log.Fatalf("Cannot initialize storage factory: %v", err)
	}
	strategyStoreFactory, err := ss.NewFactory(ss.FactoryConfigFromEnv())
	if err != nil {
		log.Fatalf("Cannot initialize sampling strategy store factory: %v", err)
	}

	v := viper.New()
	command := &cobra.Command{
		Use:   "jaeger-all-in-one",
		Short: "Jaeger all-in-one distribution with agent, collector and query in one process.",
		Long: `Jaeger all-in-one distribution with agent, collector and query. Use with caution this version
by default uses only in-memory database.`,
		RunE: func(cmd *cobra.Command, args []string) error {
			if err := svc.Start(v); err != nil {
				return err
			}
			logger := svc.Logger                     // shortcut
			rootMetricsFactory := svc.MetricsFactory // shortcut
			metricsFactory := rootMetricsFactory.Namespace(metrics.NSOptions{Name: "jaeger"})
			tracerCloser := initTracer(rootMetricsFactory, svc.Logger)

			storageFactory.InitFromViper(v)
			if err := storageFactory.Initialize(metricsFactory, logger); err != nil {
				logger.Fatal("Failed to init storage factory", zap.Error(err))
			}

			spanReader, err := storageFactory.CreateSpanReader()
			if err != nil {
				logger.Fatal("Failed to create span reader", zap.Error(err))
			}
			spanWriter, err := storageFactory.CreateSpanWriter()
			if err != nil {
				logger.Fatal("Failed to create span writer", zap.Error(err))
			}
			dependencyReader, err := storageFactory.CreateDependencyReader()
			if err != nil {
				logger.Fatal("Failed to create dependency reader", zap.Error(err))
			}

			strategyStoreFactory.InitFromViper(v)
			if err := strategyStoreFactory.Initialize(metricsFactory, logger); err != nil {
				logger.Fatal("Failed to init sampling strategy store factory", zap.Error(err))
			}
			strategyStore, err := strategyStoreFactory.CreateStrategyStore()
			if err != nil {
				logger.Fatal("Failed to create sampling strategy store", zap.Error(err))
			}

			aOpts := new(agentApp.Builder).InitFromViper(v)
			repOpts := new(agentRep.Options).InitFromViper(v, logger)
			grpcBuilder := agentGrpcRep.NewConnBuilder().InitFromViper(v)
			cOpts := new(collectorApp.CollectorOptions).InitFromViper(v)
			qOpts := new(queryApp.QueryOptions).InitFromViper(v, logger)

			// collector
			c := collectorApp.New(&collectorApp.CollectorParams{
				ServiceName:    "jaeger-collector",
				Logger:         logger,
				MetricsFactory: metricsFactory,
				SpanWriter:     spanWriter,
				StrategyStore:  strategyStore,
				HealthCheck:    svc.HC(),
			})
			c.Start(cOpts)

			// agent
			// if the agent reporter grpc host:port was not explicitly set then use whatever the collector is listening on
			if len(grpcBuilder.CollectorHostPorts) == 0 {
				grpcBuilder.CollectorHostPorts = append(grpcBuilder.CollectorHostPorts, cOpts.CollectorGRPCHostPort)
			}
			agentMetricsFactory := metricsFactory.Namespace(metrics.NSOptions{Name: "agent", Tags: nil})
			builders := map[agentRep.Type]agentApp.CollectorProxyBuilder{
				agentRep.GRPC: agentApp.GRPCCollectorProxyBuilder(grpcBuilder),
			}
			cp, err := agentApp.CreateCollectorProxy(agentApp.ProxyBuilderOptions{
				Options: *repOpts,
				Logger:  logger,
				Metrics: agentMetricsFactory,
			}, builders)
			if err != nil {
				logger.Fatal("Could not create collector proxy", zap.Error(err))
			}
			agent := startAgent(cp, aOpts, logger, metricsFactory)

			// query
			querySrv := startQuery(
				svc, qOpts, qOpts.BuildQueryServiceOptions(storageFactory, logger),
				spanReader, dependencyReader,
				rootMetricsFactory, metricsFactory,
			)

			svc.RunAndThen(func() {
				agent.Stop()
				cp.Close()
				c.Close()
				querySrv.Close()
				if closer, ok := spanWriter.(io.Closer); ok {
					err := closer.Close()
					if err != nil {
						logger.Error("Failed to close span writer", zap.Error(err))
					}
				}
				tracerCloser.Close()
			})
			return nil
		},
	}

	command.AddCommand(version.Command())
	command.AddCommand(env.Command())
	command.AddCommand(docs.Command(v))

	config.AddFlags(
		v,
		command,
		svc.AddFlags,
		storageFactory.AddFlags,
		agentApp.AddFlags,
		agentRep.AddFlags,
		agentGrpcRep.AddFlags,
		collectorApp.AddFlags,
		queryApp.AddFlags,
		strategyStoreFactory.AddFlags,
	)

	if err := command.Execute(); err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
}

func startAgent(
	cp agentApp.CollectorProxy,
	b *agentApp.Builder,
	logger *zap.Logger,
	baseFactory metrics.Factory,
) *agentApp.Agent {

	agent, err := b.CreateAgent(cp, logger, baseFactory)
	if err != nil {
		logger.Fatal("Unable to initialize Jaeger Agent", zap.Error(err))
	}

	logger.Info("Starting agent")
	if err := agent.Run(); err != nil {
		logger.Fatal("Failed to run the agent", zap.Error(err))
	}

	return agent
}

func startQuery(
	svc *flags.Service,
	qOpts *queryApp.QueryOptions,
	queryOpts *querysvc.QueryServiceOptions,
	spanReader spanstore.Reader,
	depReader dependencystore.Reader,
	rootFactory metrics.Factory,
	baseFactory metrics.Factory,
) *queryApp.Server {
	spanReader = storageMetrics.NewReadMetricsDecorator(spanReader, baseFactory.Namespace(metrics.NSOptions{Name: "query"}))
	qs := querysvc.NewQueryService(spanReader, depReader, *queryOpts)
	server, err := queryApp.NewServer(svc.Logger, qs, qOpts, opentracing.GlobalTracer())
	if err != nil {
		svc.Logger.Fatal("Could not start jaeger-query service", zap.Error(err))
	}
	go func() {
		for s := range server.HealthCheckStatus() {
			svc.SetHealthCheckStatus(s)
		}
	}()
	if err := server.Start(); err != nil {
		svc.Logger.Fatal("Could not start jaeger-query service", zap.Error(err))
	}
	return server
}

func initTracer(metricsFactory metrics.Factory, logger *zap.Logger) io.Closer {
	traceCfg := &jaegerClientConfig.Configuration{
		ServiceName: "jaeger-query",
		Sampler: &jaegerClientConfig.SamplerConfig{
			Type:  "const",
			Param: 1.0,
		},
		RPCMetrics: true,
	}
	traceCfg, err := traceCfg.FromEnv()
	if err != nil {
		logger.Fatal("Failed to read tracer configuration", zap.Error(err))
	}
	tracer, closer, err := traceCfg.NewTracer(
		jaegerClientConfig.Metrics(metricsFactory),
		jaegerClientConfig.Logger(jaegerClientZapLog.NewLogger(logger)),
	)
	if err != nil {
		logger.Fatal("Failed to initialize tracer", zap.Error(err))
	}
	opentracing.SetGlobalTracer(tracer)
	return closer
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/util"
	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
	"strings"
)

func init() {
	commands = append(commands, cli.Command{
		Name:      "delete",
		Usage:     "Delete a given server, disc, group, account or key.",
		UsageText: "bytemark delete account|disc|group|key|server",
		Description: `Deletes the given server, disc, group, account or key. Only empty groups and accounts can be deleted.
If the --purge flag is given and the target is a cloud server, will permanently delete the server. Billing will cease and you will be unable to recover the server or its data.
If the --force flag is given, you will not be prompted to confirm deletpaion.
The undelete server command may be used to restore a deleted (but not purged) server to its state prior to deletion.
`,
		Action: cli.ShowSubcommandHelp,
		Subcommands: []cli.Command{{
			Name:        "disc",
			Usage:       "Delete the given disc",
			UsageText:   "bytemark delete disc <virtual machine name> <disc label>",
			Description: "Deletes the given disc. To find out a disc's label you can use the `bytemark show server` command or `bytemark list discs` command.",
			Aliases:     []string{"disk"},
			Action: With(VirtualMachineNameProvider, DiscLabelProvider, AuthProvider, func(c *Context) (err error) {
				if !(global.Config.Force() || util.PromptYesNo("Are you sure you wish to delete this disc? It is impossible to recover.")) {
					return &util.UserRequestedExit{}
				}

				return global.Client.DeleteDisc(c.VirtualMachineName, *c.DiscLabel)
			}),
		}, {
			Name:      "group",
			Usage:     "Deletes the given group",
			UsageText: "bytemark delete group [--recursive] <group name>",
			Description: `Deletes the given group.
If --recursive is specified, all servers in the group will be purged. Otherwise, if there are servers in the group, will return an error.`,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "recursive",
					Usage: "If set, all servers in the group will be irrevocably deleted.",
				},
			},
			Action: With(GroupProvider, func(c *Context) (err error) {
				recursive := c.Bool("recursive")
				if len(c.Group.VirtualMachines) > 0 && recursive {
					running := 0
					for _, vm := range c.Group.VirtualMachines {
						if vm.PowerOn {
							running++
						}
					}
					prompt := fmt.Sprintf("The group '%s' has %d virtual machines in it", c.GroupName.Group, len(c.Group.VirtualMachines))
					if running != 0 {
						prompt = fmt.Sprintf("The group '%s' has %d running virtual machines in it", c.GroupName.Group, running)
					}

					if global.Config.Force() || PromptYesNo(prompt+" - are you sure you wish to delete this group?") {
						// TODO(telyn): Prompt
						err = recursiveDeleteGroup(c.GroupName, c.Group)
						if err != nil {
							return
						}
					}
				} else if !recursive {
					err = &util.WontDeleteNonEmptyGroupError{Group: c.GroupName}
					return
				}
				err = global.Client.DeleteGroup(c.GroupName)
				return
			}),
		}, {
			Name:        "key",
			Usage:       "Deletes the specified key",
			UsageText:   "bytemark delete key [--user <user>] <key>",
			Description: "Keys may be specified as just the comment part or as the whole key. If there are multiple keys with the comment given, an error will be returned",
			Action: With(func(c *Context) error {
				user := global.Config.GetIgnoreErr("user")

				key := strings.Join(c.Args(), " ")
				if key == "" {
					return c.Help("You must specify a key to delete.\r\n")
				}

				err := EnsureAuth()
				if err != nil {
					return err
				}

				err = global.Client.DeleteUserAuthorizedKey(user, key)
				if err == nil {
					log.Log("Key deleted successfullly")
					return err
				} else {
					return err
				}
			}),
		}, {
			Name:        "server",
			Usage:       "Delete the given server",
			UsageText:   `bytemark delete server [--purge] <server name>`,
			Description: "Deletes the given server. Deleted servers still exist and can be restored. To ensure a server is fully deleted, use the --purge flag.",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "purge",
					Usage: "If set, the server will be irrevocably deleted.",
				},
			},
			Action: With(VirtualMachineProvider, func(c *Context) (err error) {
				purge := c.Bool("purge")
				vm := c.VirtualMachine

				if vm.Deleted && !purge {
					log.Errorf("Server %s has already been deleted.\r\nIf you wish to permanently delete it, add --purge\r\n", vm.Hostname)
					// we don't return an error because we want a 0 exit code - the deletion request has happened, just not now.
					return
				}

				if !global.Config.Force() {
					fstr := fmt.Sprintf("Are you certain you wish to delete %s?", vm.Hostname)
					if purge {
						fstr = fmt.Sprintf("Are you certain you wish to permanently delete %s? You will not be able to un-delete it.", vm.Hostname)

					}
					if !util.PromptYesNo(fstr) {
						err = &util.UserRequestedExit{}
						return

					}
				}

				err = global.Client.DeleteVirtualMachine(c.VirtualMachineName, purge)
				if err != nil {
					return
				}
				if purge {
					log.Logf("Server %s purged successfully.\r\n", vm.Hostname)
				} else {
					log.Logf("Server %s deleted successfully.\r\n", vm.Hostname)
				}
				return
			}),
		}},
	})
}

func recursiveDeleteGroup(name *lib.GroupName, group *lib.Group) error {
	log.Log("WARNING: The following servers will be permanently deleted, without any way to recover or un-delete them:")
	for _, vm := range group.VirtualMachines {
		log.Logf("\t%s\r\n", vm.Name)
	}
	log.Log("", "")
	if util.PromptYesNo("Are you sure you want to continue? The above servers will be permanently deleted.") {
		vmn := lib.VirtualMachineName{Group: name.Group, Account: name.Account}
		for _, vm := range group.VirtualMachines {
			vmn.VirtualMachine = vm.Name
			err := global.Client.DeleteVirtualMachine(&vmn, true)
			if err != nil {
				return err
			} else {
				log.Logf("Server %s purged successfully.\r\n", name)
			}

		}
	} else {
		return &util.UserRequestedExit{}
	}
	return nil
}

/*log.Log("usage: bytemark delete account <account>")
	log.Log("       bytemark delete disc <server> <label>")
	log.Log("       bytemark delete group [--recursive] <group>")
	//log.Log("       bytemark delete user <user>")
	log.Log("       bytemark delete key [--user=<user>] <public key identifier>")
	log.Log("       bytemark delete server [--force] [---purge] <server>")
	log.Log("       bytemark undelete server <server>")
}*/
<commit_msg>Fix bug in delete group's new prompting<commit_after>package main

import (
	"fmt"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/util"
	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
	"strings"
)

func init() {
	commands = append(commands, cli.Command{
		Name:      "delete",
		Usage:     "Delete a given server, disc, group, account or key.",
		UsageText: "bytemark delete account|disc|group|key|server",
		Description: `Deletes the given server, disc, group, account or key. Only empty groups and accounts can be deleted.
If the --purge flag is given and the target is a cloud server, will permanently delete the server. Billing will cease and you will be unable to recover the server or its data.
If the --force flag is given, you will not be prompted to confirm deletpaion.
The undelete server command may be used to restore a deleted (but not purged) server to its state prior to deletion.
`,
		Action: cli.ShowSubcommandHelp,
		Subcommands: []cli.Command{{
			Name:        "disc",
			Usage:       "Delete the given disc",
			UsageText:   "bytemark delete disc <virtual machine name> <disc label>",
			Description: "Deletes the given disc. To find out a disc's label you can use the `bytemark show server` command or `bytemark list discs` command.",
			Aliases:     []string{"disk"},
			Action: With(VirtualMachineNameProvider, DiscLabelProvider, AuthProvider, func(c *Context) (err error) {
				if !(global.Config.Force() || util.PromptYesNo("Are you sure you wish to delete this disc? It is impossible to recover.")) {
					return &util.UserRequestedExit{}
				}

				return global.Client.DeleteDisc(c.VirtualMachineName, *c.DiscLabel)
			}),
		}, {
			Name:      "group",
			Usage:     "Deletes the given group",
			UsageText: "bytemark delete group [--recursive] <group name>",
			Description: `Deletes the given group.
If --recursive is specified, all servers in the group will be purged. Otherwise, if there are servers in the group, will return an error.`,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "recursive",
					Usage: "If set, all servers in the group will be irrevocably deleted.",
				},
			},
			Action: With(GroupProvider, func(c *Context) (err error) {
				recursive := c.Bool("recursive")
				if len(c.Group.VirtualMachines) > 0 && recursive {
					running := 0
					for _, vm := range c.Group.VirtualMachines {
						if vm.PowerOn {
							running++
						}
					}
					prompt := fmt.Sprintf("The group '%s' has %d virtual machines in it", c.GroupName.Group, len(c.Group.VirtualMachines))
					if running != 0 {
						prompt = fmt.Sprintf("The group '%s' has %d running virtual machines in it", c.GroupName.Group, running)
					}

					if global.Config.Force() || util.PromptYesNo(prompt+" - are you sure you wish to delete this group?") {
						// TODO(telyn): Prompt
						err = recursiveDeleteGroup(c.GroupName, c.Group)
						if err != nil {
							return
						}
					}
				} else if !recursive {
					err = &util.WontDeleteNonEmptyGroupError{Group: c.GroupName}
					return
				}
				err = global.Client.DeleteGroup(c.GroupName)
				return
			}),
		}, {
			Name:        "key",
			Usage:       "Deletes the specified key",
			UsageText:   "bytemark delete key [--user <user>] <key>",
			Description: "Keys may be specified as just the comment part or as the whole key. If there are multiple keys with the comment given, an error will be returned",
			Action: With(func(c *Context) error {
				user := global.Config.GetIgnoreErr("user")

				key := strings.Join(c.Args(), " ")
				if key == "" {
					return c.Help("You must specify a key to delete.\r\n")
				}

				err := EnsureAuth()
				if err != nil {
					return err
				}

				err = global.Client.DeleteUserAuthorizedKey(user, key)
				if err == nil {
					log.Log("Key deleted successfullly")
					return err
				} else {
					return err
				}
			}),
		}, {
			Name:        "server",
			Usage:       "Delete the given server",
			UsageText:   `bytemark delete server [--purge] <server name>`,
			Description: "Deletes the given server. Deleted servers still exist and can be restored. To ensure a server is fully deleted, use the --purge flag.",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "purge",
					Usage: "If set, the server will be irrevocably deleted.",
				},
			},
			Action: With(VirtualMachineProvider, func(c *Context) (err error) {
				purge := c.Bool("purge")
				vm := c.VirtualMachine

				if vm.Deleted && !purge {
					log.Errorf("Server %s has already been deleted.\r\nIf you wish to permanently delete it, add --purge\r\n", vm.Hostname)
					// we don't return an error because we want a 0 exit code - the deletion request has happened, just not now.
					return
				}

				if !global.Config.Force() {
					fstr := fmt.Sprintf("Are you certain you wish to delete %s?", vm.Hostname)
					if purge {
						fstr = fmt.Sprintf("Are you certain you wish to permanently delete %s? You will not be able to un-delete it.", vm.Hostname)

					}
					if !util.PromptYesNo(fstr) {
						err = &util.UserRequestedExit{}
						return

					}
				}

				err = global.Client.DeleteVirtualMachine(c.VirtualMachineName, purge)
				if err != nil {
					return
				}
				if purge {
					log.Logf("Server %s purged successfully.\r\n", vm.Hostname)
				} else {
					log.Logf("Server %s deleted successfully.\r\n", vm.Hostname)
				}
				return
			}),
		}},
	})
}

func recursiveDeleteGroup(name *lib.GroupName, group *lib.Group) error {
	log.Log("WARNING: The following servers will be permanently deleted, without any way to recover or un-delete them:")
	for _, vm := range group.VirtualMachines {
		log.Logf("\t%s\r\n", vm.Name)
	}
	log.Log("", "")
	if util.PromptYesNo("Are you sure you want to continue? The above servers will be permanently deleted.") {
		vmn := lib.VirtualMachineName{Group: name.Group, Account: name.Account}
		for _, vm := range group.VirtualMachines {
			vmn.VirtualMachine = vm.Name
			err := global.Client.DeleteVirtualMachine(&vmn, true)
			if err != nil {
				return err
			} else {
				log.Logf("Server %s purged successfully.\r\n", name)
			}

		}
	} else {
		return &util.UserRequestedExit{}
	}
	return nil
}

/*log.Log("usage: bytemark delete account <account>")
	log.Log("       bytemark delete disc <server> <label>")
	log.Log("       bytemark delete group [--recursive] <group>")
	//log.Log("       bytemark delete user <user>")
	log.Log("       bytemark delete key [--user=<user>] <public key identifier>")
	log.Log("       bytemark delete server [--force] [---purge] <server>")
	log.Log("       bytemark undelete server <server>")
}*/
<|endoftext|>


<commit_before>package main

import (
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/util"
	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/lib/billing"
	"github.com/BytemarkHosting/bytemark-client/lib/spp"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
	"strings"
)

func init() {
	commands = append(commands, cli.Command{
		Name:      "signup",
		Usage:     "sign up for Bytemark's hosting service",
		UsageText: "bytemark signup",
		Description: `This will create a new SSO and billing account and set your credit card details.

If you are creating an account on behalf of an organisation needing a different payment method, you'll need to email Bytemark support instead.

If you have previously used the client, you'll have a login and will need to add the --force flag in order to create a new account`,
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "card",
				Usage: "card reference string to use. If not specified you will be prompted for card details",
			},
			cli.BoolFlag{
				Name:  "force",
				Usage: "sign up for a new account & login despite already having a login.",
			},
		},
		Action: With(func(c *Context) error {

			// TODO(telyn): check a terminal is attached to stdin to try to help prevent fraudy/spammy crap just in case
			ssoExists := false
			token := global.Config.GetIgnoreErr("token")
			if token != "" {
				ssoExists = true
			}
			user, err := global.Config.GetV("user")
			if err == nil && user.Source != "ENV USER" {
				ssoExists = true
			}

			if ssoExists && !c.Bool("force") {
				return c.Help("You already have a login configured, you may wish to use 'create account' to add another account to your user, or add the force flag.")
			}
			cardRef := c.String("card")
			creditCardForm := true
			if cardRef != "" {
				creditCardForm = false
			}

			fields, frm, signup := util.MakeSignupForm(creditCardForm)

			err = frm.Run()
			if err != nil {
				return err
			}

			if !*signup {
				return util.UserRequestedExit{}
			}

			if problems, ok := frm.Validate(); !ok {
				log.Log(strings.Join(problems, "\r\n"))
				return util.UserRequestedExit{}
			}

			// TODO(telyn): this whoole section should be moved into a function in util/form.go - CreateAPIObjectsFromSignupForm(*Form) (Account, CreditCard) or something.
			account := lib.Account{}

			account.Owner = &billing.Person{
				Username:             fields[util.FormFieldOwnerName].Value(),
				Password:             fields[util.FormFieldOwnerPassword].Value(),
				Email:                fields[util.FormFieldOwnerEmail].Value(),
				FirstName:            fields[util.FormFieldOwnerFirstName].Value(),
				LastName:             fields[util.FormFieldOwnerLastName].Value(),
				Address:              fields[util.FormFieldOwnerAddress].Value(),
				City:                 fields[util.FormFieldOwnerCity].Value(),
				Postcode:             fields[util.FormFieldOwnerPostcode].Value(),
				Country:              fields[util.FormFieldOwnerCountryCode].Value(),
				Phone:                fields[util.FormFieldOwnerPhoneNumber].Value(),
				MobilePhone:          fields[util.FormFieldOwnerMobileNumber].Value(),
				Organization:         fields[util.FormFieldOwnerOrgName].Value(),
				OrganizationDivision: fields[util.FormFieldOwnerOrgDivision].Value(),
				VATNumber:            fields[util.FormFieldOwnerOrgVATNumber].Value(),
			}

			if creditCardForm {
				card := spp.CreditCard{
					Number: fields[util.FormFieldCreditCardNumber].Value(),
					Name:   fields[util.FormFieldCreditCardName].Value(),
					Expiry: fields[util.FormFieldCreditCardExpiry].Value(),
					CVV:    fields[util.FormFieldCreditCardCVV].Value(),
				}
				cardRef, err = global.Client.CreateCreditCard(&card)
				if err != nil {
					return err
				}
			}
			account.CardReference = cardRef
			createdAccount, err := global.Client.RegisterNewAccount(&account)

			if _, ok := err.(lib.AccountCreationDeferredError); ok {
				log.Log(err.Error())
				return nil
			} else if err != nil {
				log.Log("Couldn't create an account for you")
				return err
			}
			log.Logf("Account created successfully - you'll now be able to log in as '%s' and set up some servers! You should also be receiving a welcome email shortly.\r\n", createdAccount.Owner.Username)
			return nil

		}),
	})
}
<commit_msg>Make signup form use new SPP token code<commit_after>package main

import (
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/util"
	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/lib/billing"
	"github.com/BytemarkHosting/bytemark-client/lib/spp"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
	"strings"
)

func init() {
	commands = append(commands, cli.Command{
		Name:      "signup",
		Usage:     "sign up for Bytemark's hosting service",
		UsageText: "bytemark signup",
		Description: `This will create a new SSO and billing account and set your credit card details.

If you are creating an account on behalf of an organisation needing a different payment method, you'll need to email Bytemark support instead.

If you have previously used the client, you'll have a login and will need to add the --force flag in order to create a new account`,
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "card",
				Usage: "card reference string to use. If not specified you will be prompted for card details",
			},
			cli.BoolFlag{
				Name:  "force",
				Usage: "sign up for a new account & login despite already having a login.",
			},
		},
		Action: With(func(c *Context) error {

			// TODO(telyn): check a terminal is attached to stdin to try to help prevent fraudy/spammy crap just in case
			ssoExists := false
			token := global.Config.GetIgnoreErr("token")
			if token != "" {
				ssoExists = true
			}
			user, err := global.Config.GetV("user")
			if err == nil && user.Source != "ENV USER" {
				ssoExists = true
			}

			if ssoExists && !c.Bool("force") {
				return c.Help("You already have a login configured, you may wish to use 'create account' to add another account to your user, or add the force flag.")
			}
			cardRef := c.String("card")
			creditCardForm := true
			if cardRef != "" {
				creditCardForm = false
			}

			fields, frm, signup := util.MakeSignupForm(creditCardForm)

			err = frm.Run()
			if err != nil {
				return err
			}

			if !*signup {
				return util.UserRequestedExit{}
			}

			if problems, ok := frm.Validate(); !ok {
				log.Log(strings.Join(problems, "\r\n"))
				return util.UserRequestedExit{}
			}

			// TODO(telyn): this whoole section should be moved into a function in util/form.go - CreateAPIObjectsFromSignupForm(*Form) (Account, CreditCard) or something.
			account := lib.Account{}

			account.Owner = &billing.Person{
				Username:             fields[util.FormFieldOwnerName].Value(),
				Password:             fields[util.FormFieldOwnerPassword].Value(),
				Email:                fields[util.FormFieldOwnerEmail].Value(),
				FirstName:            fields[util.FormFieldOwnerFirstName].Value(),
				LastName:             fields[util.FormFieldOwnerLastName].Value(),
				Address:              fields[util.FormFieldOwnerAddress].Value(),
				City:                 fields[util.FormFieldOwnerCity].Value(),
				Postcode:             fields[util.FormFieldOwnerPostcode].Value(),
				Country:              fields[util.FormFieldOwnerCountryCode].Value(),
				Phone:                fields[util.FormFieldOwnerPhoneNumber].Value(),
				MobilePhone:          fields[util.FormFieldOwnerMobileNumber].Value(),
				Organization:         fields[util.FormFieldOwnerOrgName].Value(),
				OrganizationDivision: fields[util.FormFieldOwnerOrgDivision].Value(),
				VATNumber:            fields[util.FormFieldOwnerOrgVATNumber].Value(),
			}

			if creditCardForm {
				card := spp.CreditCard{
					Number: fields[util.FormFieldCreditCardNumber].Value(),
					Name:   fields[util.FormFieldCreditCardName].Value(),
					Expiry: fields[util.FormFieldCreditCardExpiry].Value(),
					CVV:    fields[util.FormFieldCreditCardCVV].Value(),
				}

				token, err := global.Client.GetSPPTokenWithAccount(account)
				if err != nil {
					return err
				}

				cardRef, err = global.Client.CreateCreditCardWithToken(&card, token)
				if err != nil {
					return err
				}
			}
			account.CardReference = cardRef
			createdAccount, err := global.Client.RegisterNewAccount(&account)

			if _, ok := err.(lib.AccountCreationDeferredError); ok {
				log.Log(err.Error())
				return nil
			} else if err != nil {
				log.Log("Couldn't create an account for you")
				return err
			}
			log.Logf("Account created successfully - you'll now be able to log in as '%s' and set up some servers! You should also be receiving a welcome email shortly.\r\n", createdAccount.Owner.Username)
			return nil

		}),
	})
}
<|endoftext|>


<commit_before>package main

import (
	"doozer"
	"doozer/util"
	"flag"
	"fmt"
	"net"
	"os"
)

// Flags
var (
	listenAddr  = flag.String("l", "127.0.0.1:8046", "The address to bind to.")
	attachAddr  = flag.String("a", "", "The address of another node to attach to.")
	webAddr     = flag.String("w", ":8080", "Serve web requests on this address.")
	clusterName = flag.String("c", "local", "The non-empty cluster name.")
)

func Usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] <cluster-name>\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "\nOptions:\n")
	flag.PrintDefaults()
}
func main() {
	util.LogWriter = os.Stderr
	flag.Parse()
	flag.Usage = Usage

	if *listenAddr == "" {
		fmt.Fprintln(os.Stderr, "require a listen address")
		flag.Usage()
		os.Exit(1)
	}

	listener, err := net.Listen("tcp", *listenAddr)
	if err != nil {
		panic(err)
	}

	var wl net.Listener
	if *webAddr != "" {
		wl, err = net.Listen("tcp", *webAddr)
		if err != nil {
			panic(err)
		}
	}

	doozer.Main(*clusterName, *attachAddr, listener, wl, make(chan int))
}
<commit_msg>unbreak the build<commit_after>package main

import (
	"doozer"
	"doozer/util"
	"flag"
	"fmt"
	"net"
	"os"
)

// Flags
var (
	listenAddr  = flag.String("l", "127.0.0.1:8046", "The address to bind to.")
	attachAddr  = flag.String("a", "", "The address of another node to attach to.")
	webAddr     = flag.String("w", ":8080", "Serve web requests on this address.")
	clusterName = flag.String("c", "local", "The non-empty cluster name.")
)

func Usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] <cluster-name>\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "\nOptions:\n")
	flag.PrintDefaults()
}
func main() {
	util.LogWriter = os.Stderr
	flag.Parse()
	flag.Usage = Usage

	if *listenAddr == "" {
		fmt.Fprintln(os.Stderr, "require a listen address")
		flag.Usage()
		os.Exit(1)
	}

	listener, err := net.Listen("tcp", *listenAddr)
	if err != nil {
		panic(err)
	}

	var wl net.Listener
	if *webAddr != "" {
		wl, err = net.Listen("tcp", *webAddr)
		if err != nil {
			panic(err)
		}
	}

	doozer.Main(*clusterName, *attachAddr, listener, wl)
}
<|endoftext|>


<commit_before>package flow

import (
	"context"
	"github.com/coreos/etcd/clientv3"
	"github.com/pkg/errors"
	"strings"
	"time"
)

type Service interface {
	//save state directory tree for navigation about templates
	FlowDirectoryTreeSave(string) (error)
	//get state directory tree for navigation about templates
	FlowDirectoryTreeGet() (string, error)
	// Remove all flow configs when is directory path "/templates/.../.../"
	FlowsDirectoryDelete(flowsDirectoryDeleteRequest) (bool, error)
	// Remove flow config
	FlowDelete(flowDeleteRequest) (bool, error)
	// add new flow config
	FlowPost(flowPostRequest) (bool, error)
	// update flow config
	FlowPut(flowPutRequest) (bool, error)
	//get flow config
	FlowGet(id string) (string, error)
}

type service struct {
	etcdClientApi clientv3.KV
}

func NewService(etcdClientApi clientv3.KV) Service {
	return &service{etcdClientApi}
}

func (f *service) FlowDirectoryTreeSave(treeState string) (error) {
	ctxForEtcd, _ := context.WithTimeout(context.Background(), 10*time.Second)
	_, err := f.etcdClientApi.Put(ctxForEtcd, "treeDirectoryState", treeState)
	if err != nil {
		return errors.Wrap(err, "can not save directory tree state")
	}
	return nil
}

func (f *service) FlowDirectoryTreeGet() (string, error) {
	ctxForEtcd, _ := context.WithTimeout(context.Background(), 10*time.Second)
	gr, err := f.etcdClientApi.Get(ctxForEtcd, "treeDirectoryState")
	if err != nil {
		return "", errors.Wrap(err, "can not get directory tree state")
	}
	if len(gr.Kvs) == 0 {
		return "{\"name\": \"templates\",\"root\": true,\"isOpen\": true,\"children\": []}\n", nil
	}
	return string(gr.Kvs[0].Value), nil
}

func (f *service) FlowsDirectoryDelete(conf flowsDirectoryDeleteRequest) (bool, error) {

	if conf.Path == "" {
		return false, errors.New("path is required")
	}
	if !strings.HasPrefix(conf.Path, "/templates/") || !strings.HasSuffix(conf.Path, "/") {
		return false, errors.New("path should start with '/template/' and end with '/'")
	}
	ctxForEtcd, _ := context.WithTimeout(context.Background(), 10*time.Second)
	gr, err := f.etcdClientApi.Delete(ctxForEtcd, conf.Path, clientv3.WithPrefix())
	if err != nil {
		return false, errors.Wrap(err, "can not delete directory ["+conf.Path+"]")
	}
	if gr.Deleted == 0 {
		return false, errors.New("no directory with path [" + conf.Path + "]")
	}
	return true, nil
}

func (f *service) FlowDelete(conf flowDeleteRequest) (bool, error) {

	if conf.Id == "" {
		return false, errors.New("id is required")
	}
	if !strings.HasPrefix(conf.Id, "/templates/") {
		return false, errors.New("id should start with '/template/'")
	}
	ctxForEtcd, _ := context.WithTimeout(context.Background(), 10*time.Second)
	gr, err := f.etcdClientApi.Delete(ctxForEtcd, conf.Id)
	if err != nil {
		return false, errors.Wrap(err, "can not delete id ["+conf.Id+"]")
	}
	if gr.Deleted == 0 {
		return false, errors.New("no config with id [" + conf.Id + "]")
	}
	return true, nil
}

func (f *service) FlowPost(conf flowPostRequest) (bool, error) {
	if conf.Id == "" {
		return false, errors.New("id is required")
	}
	if !strings.HasPrefix(conf.Id, "/") {
		return false, errors.New("id should start with '/'")
	}
	ctxForEtcd, _ := context.WithTimeout(context.Background(), 10*time.Second)
	gr, err := f.etcdClientApi.Get(ctxForEtcd, conf.Id)
	if err != nil {
		return false, errors.Wrap(err, "can not create id ["+conf.Id+"]")
	}
	if len(gr.Kvs) == 0 {
		_, err := f.etcdClientApi.Put(ctxForEtcd, conf.Id, conf.Config)
		if err != nil {
			return false, errors.Wrap(err, "can not create id ["+conf.Id+"]")
		}
		return true, nil
	} else {
		return false, errors.New("config with id [" + conf.Id + "] already exist")
	}
}

func (f *service) FlowPut(conf flowPutRequest) (bool, error) {

	if conf.Id == "" {
		return false, errors.New("id is required")
	}
	if !strings.HasPrefix(conf.Id, "/") {
		return false, errors.New("id should start with '/'")
	}
	ctxForEtcd, _ := context.WithTimeout(context.Background(), 10*time.Second)
	gr, err := f.etcdClientApi.Get(ctxForEtcd, conf.Id)
	if err != nil {
		return false, errors.Wrap(err, "can not update id ["+conf.Id+"]")
	}
	if len(gr.Kvs) == 0 {
		return false, errors.New("id [" + conf.Id + "] not found")
	}
	_, err = f.etcdClientApi.Put(ctxForEtcd, conf.Id, conf.Config)
	if err != nil {
		return false, errors.Wrap(err, "can not update id ["+conf.Id+"]")
	}
	return true, nil
}

func (f *service) FlowGet(id string) (string, error) {
	if id == "" {
		return "", errors.New("id is required")
	}
	if !strings.HasPrefix(id, "/") {
		return "", errors.New("id should start with '/'")
	}

	ctxForEtcd, _ := context.WithTimeout(context.Background(), 10*time.Second)
	gr, err := f.etcdClientApi.Get(ctxForEtcd, id)
	if err != nil {
		return "", errors.Wrap(err, "can not get id ["+id+"]")
	}
	if len(gr.Kvs) == 0 {
		return "", errors.New("config with id [" + id + "] not found")
	}

	return string(gr.Kvs[0].Value), nil
}
<commit_msg>add reset version for tree state<commit_after>package flow

import (
	"context"
	"github.com/coreos/etcd/clientv3"
	"github.com/pkg/errors"
	"strings"
	"time"
)

type Service interface {
	//save state directory tree for navigation about templates
	FlowDirectoryTreeSave(string) (error)
	//get state directory tree for navigation about templates
	FlowDirectoryTreeGet() (string, error)
	// Remove all flow configs when is directory path "/templates/.../.../"
	FlowsDirectoryDelete(flowsDirectoryDeleteRequest) (bool, error)
	// Remove flow config
	FlowDelete(flowDeleteRequest) (bool, error)
	// add new flow config
	FlowPost(flowPostRequest) (bool, error)
	// update flow config
	FlowPut(flowPutRequest) (bool, error)
	//get flow config
	FlowGet(id string) (string, error)
}

type service struct {
	etcdClientApi clientv3.KV
}

func NewService(etcdClientApi clientv3.KV) Service {
	return &service{etcdClientApi}
}

func (f *service) FlowDirectoryTreeSave(treeState string) (error) {
	ctxForEtcd, _ := context.WithTimeout(context.Background(), 10*time.Second)
	_, err := f.etcdClientApi.Delete(ctxForEtcd, "treeDirectoryState")
	if err != nil {
		return errors.Wrap(err, "can not save directory tree state")
	}
	_, err = f.etcdClientApi.Put(ctxForEtcd, "treeDirectoryState", treeState)
	if err != nil {
		return errors.Wrap(err, "can not save directory tree state")
	}
	return nil
}

func (f *service) FlowDirectoryTreeGet() (string, error) {
	ctxForEtcd, _ := context.WithTimeout(context.Background(), 10*time.Second)
	gr, err := f.etcdClientApi.Get(ctxForEtcd, "treeDirectoryState")
	if err != nil {
		return "", errors.Wrap(err, "can not get directory tree state")
	}
	if len(gr.Kvs) == 0 {
		return "{\"name\": \"templates\",\"root\": true,\"isOpen\": true,\"children\": []}\n", nil
	}
	return string(gr.Kvs[0].Value), nil
}

func (f *service) FlowsDirectoryDelete(conf flowsDirectoryDeleteRequest) (bool, error) {

	if conf.Path == "" {
		return false, errors.New("path is required")
	}
	if !strings.HasPrefix(conf.Path, "/templates/") || !strings.HasSuffix(conf.Path, "/") {
		return false, errors.New("path should start with '/template/' and end with '/'")
	}
	ctxForEtcd, _ := context.WithTimeout(context.Background(), 10*time.Second)
	gr, err := f.etcdClientApi.Delete(ctxForEtcd, conf.Path, clientv3.WithPrefix())
	if err != nil {
		return false, errors.Wrap(err, "can not delete directory ["+conf.Path+"]")
	}
	if gr.Deleted == 0 {
		return false, errors.New("no directory with path [" + conf.Path + "]")
	}
	return true, nil
}

func (f *service) FlowDelete(conf flowDeleteRequest) (bool, error) {

	if conf.Id == "" {
		return false, errors.New("id is required")
	}
	if !strings.HasPrefix(conf.Id, "/templates/") {
		return false, errors.New("id should start with '/template/'")
	}
	ctxForEtcd, _ := context.WithTimeout(context.Background(), 10*time.Second)
	gr, err := f.etcdClientApi.Delete(ctxForEtcd, conf.Id)
	if err != nil {
		return false, errors.Wrap(err, "can not delete id ["+conf.Id+"]")
	}
	if gr.Deleted == 0 {
		return false, errors.New("no config with id [" + conf.Id + "]")
	}
	return true, nil
}

func (f *service) FlowPost(conf flowPostRequest) (bool, error) {
	if conf.Id == "" {
		return false, errors.New("id is required")
	}
	if !strings.HasPrefix(conf.Id, "/") {
		return false, errors.New("id should start with '/'")
	}
	ctxForEtcd, _ := context.WithTimeout(context.Background(), 10*time.Second)
	gr, err := f.etcdClientApi.Get(ctxForEtcd, conf.Id)
	if err != nil {
		return false, errors.Wrap(err, "can not create id ["+conf.Id+"]")
	}
	if len(gr.Kvs) == 0 {
		_, err := f.etcdClientApi.Put(ctxForEtcd, conf.Id, conf.Config)
		if err != nil {
			return false, errors.Wrap(err, "can not create id ["+conf.Id+"]")
		}
		return true, nil
	} else {
		return false, errors.New("config with id [" + conf.Id + "] already exist")
	}
}

func (f *service) FlowPut(conf flowPutRequest) (bool, error) {

	if conf.Id == "" {
		return false, errors.New("id is required")
	}
	if !strings.HasPrefix(conf.Id, "/") {
		return false, errors.New("id should start with '/'")
	}
	ctxForEtcd, _ := context.WithTimeout(context.Background(), 10*time.Second)
	gr, err := f.etcdClientApi.Get(ctxForEtcd, conf.Id)
	if err != nil {
		return false, errors.Wrap(err, "can not update id ["+conf.Id+"]")
	}
	if len(gr.Kvs) == 0 {
		return false, errors.New("id [" + conf.Id + "] not found")
	}
	_, err = f.etcdClientApi.Put(ctxForEtcd, conf.Id, conf.Config)
	if err != nil {
		return false, errors.Wrap(err, "can not update id ["+conf.Id+"]")
	}
	return true, nil
}

func (f *service) FlowGet(id string) (string, error) {
	if id == "" {
		return "", errors.New("id is required")
	}
	if !strings.HasPrefix(id, "/") {
		return "", errors.New("id should start with '/'")
	}

	ctxForEtcd, _ := context.WithTimeout(context.Background(), 10*time.Second)
	gr, err := f.etcdClientApi.Get(ctxForEtcd, id)
	if err != nil {
		return "", errors.Wrap(err, "can not get id ["+id+"]")
	}
	if len(gr.Kvs) == 0 {
		return "", errors.New("config with id [" + id + "] not found")
	}

	return string(gr.Kvs[0].Value), nil
}
<|endoftext|>


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

package main

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

	"github.com/go-darwin/hdiutil"
)

func main() {
	img := "/Users/zchee/.docker/machine/cache/boot2docker.iso"
	deviceNode, err := hdiutil.Attach(img, hdiutil.AttachMountPoint("./test"), hdiutil.AttachNoVerify, hdiutil.AttachNoAutoFsck)
	if err != nil {
		log.Fatal(err)
	}

	log.Println(hdiutil.RawDeviceNode(deviceNode))
	log.Println(hdiutil.DeviceNumber(deviceNode))

	if err := hdiutil.Detach(deviceNode); err != nil {
		log.Fatal(err)
	}

	if err := hdiutil.Create("test", hdiutil.CreateMegabytes(20), hdiutil.CreateAPFS, hdiutil.CreateSPARSEBUNDLE); err != nil {
		log.Fatal(err)
	}

	if _, err := filepath.Glob("test.*"); err != nil {
		log.Fatal(err)
	}

	defer func() {
		files, _ := filepath.Glob("test.*")
		for _, file := range files {
			os.RemoveAll(file)
		}
	}()
}
<commit_msg>cmd/go-hdiutil: fix img to use the self-created HFS+ image<commit_after>// Copyright 2017 The go-darwin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"log"
	"os"

	"github.com/go-darwin/hdiutil"
)

func main() {
	img := "test.sparsebundle"

	if err := hdiutil.Create("test", hdiutil.CreateMegabytes(20), hdiutil.CreateHFSPlus, hdiutil.CreateSPARSEBUNDLE); err != nil {
		log.Fatal(err)
	}
	if _, err := os.Stat(img); err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(img)

	deviceNode, err := hdiutil.Attach(img, hdiutil.AttachMountPoint("./test"), hdiutil.AttachNoVerify, hdiutil.AttachNoAutoFsck)
	if err != nil {
		log.Fatal(err)
	}

	log.Println(hdiutil.RawDeviceNode(deviceNode))
	log.Println(hdiutil.DeviceNumber(deviceNode))

	if err := hdiutil.Detach(deviceNode); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"path/filepath"

	"github.com/cosiner/gohper/goutil"
	"github.com/cosiner/gohper/goutil/ast"
	"github.com/cosiner/gohper/sortedmap"
	"github.com/cosiner/gohper/strings2"
)

type Table struct {
	Name   string
	Fields sortedmap.Map
}

type Visitor map[string]*Table

// add an model and it's field to parse result
func (v Visitor) add(model, table, field, col string) {
	if table == "" {
		table = strings2.ToSnake(model)
	}

	if col == "" {
		col = strings2.ToSnake(field)
	}

	t, has := v[model]
	if !has {
		t = &Table{Name: table}
		v[model] = t
	}

	t.Fields.Set(field, col)
}

// parse ast tree to find exported struct and it's fields
func (v Visitor) parseFiles(files ...string) error {
	for _, file := range files {
		err := v.parseFile(file)
		if err != nil {
			return err
		}
	}

	return nil
}

func (v Visitor) parseDir(dir string) error {
	return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		return v.parseFile(path)
	})
}

func (v Visitor) parseFile(file string) error {
	return ast.Parser{
		Struct: func(a *ast.Attrs) (err error) {
			if !goutil.IsExported(a.TypeName) {
				err = ast.TYPE_END
			} else if table := a.S.Tag.Get("table"); table == "-" {
				err = ast.TYPE_END
			} else if col := a.S.Tag.Get("column"); col != "-" {
				v.add(a.TypeName, table, a.S.Field, col)
			}

			return
		},
	}.ParseFile(file)
}

// buildModelFields build model map from parse result
func (v Visitor) buildModelFields() map[*Model][]*Field {
	names := make(map[*Model][]*Field, len(v))

	for model, table := range v {
		m := NewModel(model, table.Name)
		fields := table.Fields
		for field, index := range fields.Indexes {
			names[m] = append(names[m], NewField(m, field, fields.Elements[index].(string)))
		}
	}

	return names
}
<commit_msg>cmd/gomodel: fix bug for fields order<commit_after>package main

import (
	"os"
	"path/filepath"

	"github.com/cosiner/gohper/goutil"
	"github.com/cosiner/gohper/goutil/ast"
	"github.com/cosiner/gohper/sortedmap"
	"github.com/cosiner/gohper/strings2"
)

type Table struct {
	Name   string
	Fields sortedmap.Map
}

type Visitor map[string]*Table

// add an model and it's field to parse result
func (v Visitor) add(model, table, field, col string) {
	if table == "" {
		table = strings2.ToSnake(model)
	}

	if col == "" {
		col = strings2.ToSnake(field)
	}

	t, has := v[model]
	if !has {
		t = &Table{Name: table}
		v[model] = t
	}

	t.Fields.Set(field, col)
}

// parse ast tree to find exported struct and it's fields
func (v Visitor) parseFiles(files ...string) error {
	for _, file := range files {
		err := v.parseFile(file)
		if err != nil {
			return err
		}
	}

	return nil
}

func (v Visitor) parseDir(dir string) error {
	return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		return v.parseFile(path)
	})
}

func (v Visitor) parseFile(file string) error {
	return ast.Parser{
		Struct: func(a *ast.Attrs) (err error) {
			if !goutil.IsExported(a.TypeName) {
				err = ast.TYPE_END
			} else if table := a.S.Tag.Get("table"); table == "-" {
				err = ast.TYPE_END
			} else if col := a.S.Tag.Get("column"); col != "-" {
				v.add(a.TypeName, table, a.S.Field, col)
			}

			return
		},
	}.ParseFile(file)
}

// buildModelFields build model map from parse result
func (v Visitor) buildModelFields() map[*Model][]*Field {
	names := make(map[*Model][]*Field, len(v))

	for model, table := range v {
		m := NewModel(model, table.Name)
		fields := table.Fields
		for _, field := range fields.Elements {
			names[m] = append(names[m], NewField(m, field.Key, field.Value.(string)))
		}
	}

	return names
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"html/template"
	"io"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"path"
	"strings"

	"github.com/steder/gophernaut"
)

var hostname = fmt.Sprintf("http://127.0.0.1:%d", 8080)
var executable = fmt.Sprintf("python -m SimpleHTTPServer %d", 8080)

func startProcess(events chan int) {
	commandParts := strings.Split(executable, " ")
	command := exec.Command(commandParts[0], commandParts[1:]...)
	fmt.Printf("Command: %v\n", command)

	stdout, err := command.StdoutPipe()
	if err != nil {
		fmt.Println("Unable to read output from command...")
	}
	stderr, err := command.StderrPipe()
	if err != nil {
		fmt.Println("Unable to read output from command...")
	}

	go io.Copy(os.Stdout, stdout)
	go io.Copy(os.Stderr, stderr)
	command.Start()

	for {
		_, ok := <-events
		if !ok {
			command.Process.Kill()
		}
	}
}

func myHandler(w http.ResponseWriter, myReq *http.Request) {
	requestPath := myReq.URL.Path

	targetURL, _ := url.Parse(hostname)
	director := func(req *http.Request) {
		targetQuery := targetURL.RawQuery
		req.URL.Scheme = targetURL.Scheme
		// TODO: adjust request host to assign the request to the appropriate child process
		req.URL.Host = targetURL.Host

		// clean up but preserve trailing slash:
		trailing := strings.HasSuffix(req.URL.Path, "/")
		req.URL.Path = path.Join(targetURL.Path, req.URL.Path)
		if trailing && !strings.HasSuffix(req.URL.Path, "/") {
			req.URL.Path += "/"
		}

		// preserve query string:
		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
	}

	proxy := &httputil.ReverseProxy{Director: director}

	staticHandler := http.StripPrefix("/static", http.FileServer(http.Dir("static")))
	adminTemplate := template.Must(template.ParseFiles("templates/admin.html"))
	adminHandler := func(w http.ResponseWriter, req *http.Request) {
		adminTemplate.Execute(w, nil)
	}

	//fmt.Printf("path: %s\n", request_path)
	switch {
	case requestPath == "/admin":
		//fmt.Printf("admin path...\n")
		adminHandler(w, myReq)
		return
	case strings.HasPrefix(requestPath, "/static"):
		//fmt.Printf("static path...\n")
		staticHandler.ServeHTTP(w, myReq)
		return
	}
	//fmt.Printf("proxy path...\n")
	proxy.ServeHTTP(w, myReq)
}

func main() {
	// Test reading a config yaml:
	c := gophernaut.ReadConfig()
	fmt.Printf("Host %s and Port %d\n", c.Host, c.Port)

	eventsChannel := make(chan int)
	go startProcess(eventsChannel) // TODO MANY PROCESSES, MUCH POOLS
	fmt.Printf("Gophernaut is gopher launch!\n")
	http.ListenAndServe(":8483", http.HandlerFunc(myHandler))
	// TODO: our own ReverseProxy implementation of at least, ServeHTTP so that we can
	// monitor the response codes to track successes and failures
}
<commit_msg>Start trying to handle signals and do a graceful shutdown<commit_after>package main

import (
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"strings"

	"github.com/steder/gophernaut"
)

// Event is basically just an enum
type Event int

// Events that can be generated by our child processes
const (
	Start Event = iota
	Shutdown
	PiningForTheFjords
)

// TODO look into "go generate stringer -type Event"
func (e Event) String() string {
	return fmt.Sprintf("Event(%d)", e)
}

var hostname = fmt.Sprintf("http://127.0.0.1:%d", 8080)
var executable = fmt.Sprintf("python -m SimpleHTTPServer %d", 8080)

func startProcess(control chan Event, events chan Event) {
	commandParts := strings.Split(executable, " ")
	command := exec.Command(commandParts[0], commandParts[1:]...)
	fmt.Printf("Command: %v\n", command)

	stdout, err := command.StdoutPipe()
	if err != nil {
		fmt.Println("Unable to read output from command...")
	}
	stderr, err := command.StderrPipe()
	if err != nil {
		fmt.Println("Unable to read output from command...")
	}

	go io.Copy(os.Stdout, stdout)
	go io.Copy(os.Stderr, stderr)
	command.Start()

	for {
		_, ok := <-control
		if !ok {
			fmt.Println("Killing worker process after receiving close event.")
			command.Process.Kill()
			events <- Shutdown
			break
		}
	}
}

func myHandler(w http.ResponseWriter, myReq *http.Request) {
	requestPath := myReq.URL.Path

	targetURL, _ := url.Parse(hostname)
	director := func(req *http.Request) {
		targetQuery := targetURL.RawQuery
		req.URL.Scheme = targetURL.Scheme
		// TODO: adjust request host to assign the request to the appropriate child process
		req.URL.Host = targetURL.Host

		// clean up but preserve trailing slash:
		trailing := strings.HasSuffix(req.URL.Path, "/")
		req.URL.Path = path.Join(targetURL.Path, req.URL.Path)
		if trailing && !strings.HasSuffix(req.URL.Path, "/") {
			req.URL.Path += "/"
		}

		// preserve query string:
		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
	}

	proxy := &httputil.ReverseProxy{Director: director}

	staticHandler := http.StripPrefix("/static", http.FileServer(http.Dir("static")))
	adminTemplate := template.Must(template.ParseFiles("templates/admin.html"))
	adminHandler := func(w http.ResponseWriter, req *http.Request) {
		adminTemplate.Execute(w, nil)
	}

	//fmt.Printf("path: %s\n", request_path)
	switch {
	case requestPath == "/admin":
		//fmt.Printf("admin path...\n")
		adminHandler(w, myReq)
		return
	case strings.HasPrefix(requestPath, "/static"):
		//fmt.Printf("static path...\n")
		staticHandler.ServeHTTP(w, myReq)
		return
	}
	//fmt.Printf("proxy path...\n")
	proxy.ServeHTTP(w, myReq)
}

func main() {
	// Test reading a config yaml:
	c := gophernaut.ReadConfig()
	fmt.Printf("Host %s and Port %d\n", c.Host, c.Port)

	controlChannel := make(chan Event)
	eventsChannel := make(chan Event)

	// Handle signals to try to do a graceful shutdown:
	receivedSignals := make(chan os.Signal, 1)
	signal.Notify(receivedSignals, os.Interrupt) // , syscall.SIGINT, syscall.SIGTERM)
	go func() {
		for sig := range receivedSignals {
			fmt.Printf("Received signal, %s, shutting down workers...\n", sig)
			break
		}
		close(controlChannel)
		signal.Stop(receivedSignals)
	}()

	// Actually start some processes
	go startProcess(controlChannel, eventsChannel) // TODO MANY PROCESSES, MUCH POOLS

	// wait for child processes to exit before shutting down:
	processCount := 1
	stoppedCount := 0
	go func() {
		for event := range eventsChannel {
			if event == Shutdown {
				stoppedCount++
			}
			if processCount == stoppedCount {
				fmt.Printf("%d workers stopped, shutting down.\n", processCount)
				os.Exit(1)
			}
		}
	}()

	fmt.Printf("Gophernaut is gopher launch!\n")
	// TODO: our own ReverseProxy implementation of at least, ServeHTTP so that we can
	// monitor the response codes to track successes and failures
	log.Fatal(http.ListenAndServe(":8483", http.HandlerFunc(myHandler)))
}
<|endoftext|>


<commit_before>package cmd

import (
	"context"
	"crypto/tls"
	"github.com/pkg/errors"
	"github.com/zrepl/zrepl/logger"
	"io"
	"log/syslog"
	"net"
	"time"
)

type WriterOutlet struct {
	Formatter EntryFormatter
	Writer    io.Writer
}

func (h WriterOutlet) WriteEntry(ctx context.Context, entry logger.Entry) error {
	bytes, err := h.Formatter.Format(&entry)
	if err != nil {
		return err
	}
	_, err = h.Writer.Write(bytes)
	h.Writer.Write([]byte("\n"))
	return err
}

type TCPOutlet struct {
	Formatter     EntryFormatter
	Net, Address  string
	Dialer        net.Dialer
	TLS           *tls.Config
	RetryInterval time.Duration
	conn          net.Conn
	retry         time.Time
}

func (h *TCPOutlet) WriteEntry(ctx context.Context, e logger.Entry) error {

	b, err := h.Formatter.Format(&e)
	if err != nil {
		return err
	}

	if h.conn == nil {
		if time.Now().Sub(h.retry) < h.RetryInterval {
			return nil // this is not an error toward the logger
			//return errors.New("TCP hook reconnect prohibited by retry interval")
		}

		if h.TLS != nil {
			h.conn, err = tls.DialWithDialer(&h.Dialer, h.Net, h.Address, h.TLS)
		} else {
			h.conn, err = h.Dialer.DialContext(ctx, h.Net, h.Address)
		}
		if err != nil {
			h.conn = nil
			h.retry = time.Now()
			return errors.Wrap(err, "cannot dial")
		}
	}

	_, err = h.conn.Write(b)
	if err == nil {
		_, err = h.conn.Write([]byte("\n"))
	}
	if err != nil {
		return errors.Wrap(err, "cannot write")
		h.conn.Close()
		h.conn = nil
		return err
	}

	return nil
}

type SyslogOutlet struct {
	Formatter          EntryFormatter
	RetryInterval      time.Duration
	writer             *syslog.Writer
	lastConnectAttempt time.Time
}

func (o *SyslogOutlet) WriteEntry(ctx context.Context, entry logger.Entry) error {

	bytes, err := o.Formatter.Format(&entry)
	if err != nil {
		return err
	}

	s := string(bytes)

	if o.writer == nil {
		now := time.Now()
		if now.Sub(o.lastConnectAttempt) < o.RetryInterval {
			return nil // not an error toward logger
		}
		o.writer, err = syslog.New(syslog.LOG_LOCAL0, "zrepl")
		o.lastConnectAttempt = time.Now()
		if err != nil {
			o.writer = nil
			return err
		}
	}

	switch entry.Level {
	case logger.Debug:
		return o.writer.Debug(s)
	case logger.Info:
		return o.writer.Info(s)
	case logger.Warn:
		return o.writer.Warning(s)
	case logger.Error:
		return o.writer.Err(s)
	default:
		return o.writer.Err(s) // write as error as reaching this case is in fact an error
	}

}
<commit_msg>tcp outlet: fix error handling on write failure<commit_after>package cmd

import (
	"context"
	"crypto/tls"
	"github.com/pkg/errors"
	"github.com/zrepl/zrepl/logger"
	"io"
	"log/syslog"
	"net"
	"time"
)

type WriterOutlet struct {
	Formatter EntryFormatter
	Writer    io.Writer
}

func (h WriterOutlet) WriteEntry(ctx context.Context, entry logger.Entry) error {
	bytes, err := h.Formatter.Format(&entry)
	if err != nil {
		return err
	}
	_, err = h.Writer.Write(bytes)
	h.Writer.Write([]byte("\n"))
	return err
}

type TCPOutlet struct {
	Formatter    EntryFormatter
	Net, Address string
	Dialer       net.Dialer
	TLS          *tls.Config
	// Specifies how much time must pass between a connection error and a reconnection attempt
	// Log entries written to the outlet during this time interval are silently dropped.
	RetryInterval time.Duration
	// nil if there was an error sending / connecting to remote server
	conn net.Conn
	// Last time an error occurred when sending / connecting to remote server
	retry time.Time
}

func (h *TCPOutlet) WriteEntry(ctx context.Context, e logger.Entry) error {

	b, err := h.Formatter.Format(&e)
	if err != nil {
		return err
	}

	if h.conn == nil {
		if time.Now().Sub(h.retry) < h.RetryInterval {
			// cool-down phase, drop the log entry
			return nil
		}

		if h.TLS != nil {
			h.conn, err = tls.DialWithDialer(&h.Dialer, h.Net, h.Address, h.TLS)
		} else {
			h.conn, err = h.Dialer.DialContext(ctx, h.Net, h.Address)
		}
		if err != nil {
			h.conn = nil
			h.retry = time.Now()
			return errors.Wrap(err, "cannot dial")
		}
	}

	_, err = h.conn.Write(b)
	if err == nil {
		_, err = h.conn.Write([]byte("\n"))
	}
	if err != nil {
		h.conn.Close()
		h.conn = nil
		h.retry = time.Now()
		return errors.Wrap(err, "cannot write")
	}

	return nil
}

type SyslogOutlet struct {
	Formatter          EntryFormatter
	RetryInterval      time.Duration
	writer             *syslog.Writer
	lastConnectAttempt time.Time
}

func (o *SyslogOutlet) WriteEntry(ctx context.Context, entry logger.Entry) error {

	bytes, err := o.Formatter.Format(&entry)
	if err != nil {
		return err
	}

	s := string(bytes)

	if o.writer == nil {
		now := time.Now()
		if now.Sub(o.lastConnectAttempt) < o.RetryInterval {
			return nil // not an error toward logger
		}
		o.writer, err = syslog.New(syslog.LOG_LOCAL0, "zrepl")
		o.lastConnectAttempt = time.Now()
		if err != nil {
			o.writer = nil
			return err
		}
	}

	switch entry.Level {
	case logger.Debug:
		return o.writer.Debug(s)
	case logger.Info:
		return o.writer.Info(s)
	case logger.Warn:
		return o.writer.Warning(s)
	case logger.Error:
		return o.writer.Err(s)
	default:
		return o.writer.Err(s) // write as error as reaching this case is in fact an error
	}

}
<|endoftext|>


<commit_before>package providers

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
)

type GitHubProvider struct {
	*ProviderData
	Org  string
	Team string
}

func NewGitHubProvider(p *ProviderData) *GitHubProvider {
	p.ProviderName = "GitHub"
	if p.LoginUrl.String() == "" {
		p.LoginUrl = &url.URL{
			Scheme: "https",
			Host:   "github.com",
			Path:   "/login/oauth/authorize",
		}
	}
	if p.RedeemUrl.String() == "" {
		p.RedeemUrl = &url.URL{
			Scheme: "https",
			Host:   "github.com",
			Path:   "/login/oauth/access_token",
		}
	}
	if p.ValidateUrl.String() == "" {
		p.ValidateUrl = &url.URL{
			Scheme: "https",
			Host:   "api.github.com",
			Path:   "/user/emails",
		}
	}
	if p.Scope == "" {
		p.Scope = "user:email"
	}
	return &GitHubProvider{ProviderData: p}
}
func (p *GitHubProvider) SetOrgTeam(org, team string) {
	p.Org = org
	p.Team = team
	if org != "" || team != "" {
		p.Scope += " read:org"
	}
}

func (p *GitHubProvider) hasOrgAndTeam(accessToken string) (bool, error) {

	var teams []struct {
		Name string `json:"name"`
		Slug string `json:"slug"`
		Org  struct {
			Login string `json:"login"`
		} `json:"organization"`
	}

	params := url.Values{
		"access_token": {accessToken},
	}

	req, _ := http.NewRequest("GET", "https://api.github.com/user/teams?"+params.Encode(), nil)
	req.Header.Set("Accept", "application/vnd.github.moondragon+json")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return false, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return false, err
	}

	if err := json.Unmarshal(body, &teams); err != nil {
		return false, err
	}

	for _, team := range teams {
		if p.Org == team.Org.Login {
			if p.Team == "" || p.Team == team.Slug {
				return true, nil
			}
		}
	}
	return false, nil
}

func (p *GitHubProvider) GetEmailAddress(body []byte, access_token string) (string, error) {

	var emails []struct {
		Email   string `json:"email"`
		Primary bool   `json:"primary"`
	}

	params := url.Values{
		"access_token": {access_token},
	}

	// if we require an Org or Team, check that first
	if p.Org != "" || p.Team != "" {
		if ok, err := p.hasOrgAndTeam(access_token); err != nil || !ok {
			return "", err
		}
	}

	resp, err := http.DefaultClient.Get("https://api.github.com/user/emails?" + params.Encode())
	if err != nil {
		return "", err
	}
	body, err = ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return "", err
	}

	if err := json.Unmarshal(body, &emails); err != nil {
		return "", err
	}

	for _, email := range emails {
		if email.Primary {
			return email.Email, nil
		}
	}

	return "", nil
}

func (p *GitHubProvider) ValidateToken(access_token string) bool {
	return validateToken(p, access_token, nil)
}
<commit_msg>github: handle users part of an Org not on a team<commit_after>package providers

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
)

type GitHubProvider struct {
	*ProviderData
	Org  string
	Team string
}

func NewGitHubProvider(p *ProviderData) *GitHubProvider {
	p.ProviderName = "GitHub"
	if p.LoginUrl.String() == "" {
		p.LoginUrl = &url.URL{
			Scheme: "https",
			Host:   "github.com",
			Path:   "/login/oauth/authorize",
		}
	}
	if p.RedeemUrl.String() == "" {
		p.RedeemUrl = &url.URL{
			Scheme: "https",
			Host:   "github.com",
			Path:   "/login/oauth/access_token",
		}
	}
	if p.ValidateUrl.String() == "" {
		p.ValidateUrl = &url.URL{
			Scheme: "https",
			Host:   "api.github.com",
			Path:   "/user/emails",
		}
	}
	if p.Scope == "" {
		p.Scope = "user:email"
	}
	return &GitHubProvider{ProviderData: p}
}
func (p *GitHubProvider) SetOrgTeam(org, team string) {
	p.Org = org
	p.Team = team
	if org != "" || team != "" {
		p.Scope += " read:org"
	}
}

func (p *GitHubProvider) hasOrg(accessToken string) (bool, error) {
	// https://developer.github.com/v3/orgs/#list-your-organizations

	var orgs []struct {
		Login string `json:"login"`
	}

	params := url.Values{
		"access_token": {accessToken},
		"limit": {"100"},
	}

	req, _ := http.NewRequest("GET", "https://api.github.com/user/orgs?"+params.Encode(), nil)
	req.Header.Set("Accept", "application/vnd.github.moondragon+json")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return false, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return false, err
	}

	if err := json.Unmarshal(body, &orgs); err != nil {
		return false, err
	}

	for _, org := range orgs {
		if p.Org == org.Login {
			return true, nil
		}
	}
	return false, nil
}

func (p *GitHubProvider) hasOrgAndTeam(accessToken string) (bool, error) {
	// https://developer.github.com/v3/orgs/teams/#list-user-teams

	var teams []struct {
		Name string `json:"name"`
		Slug string `json:"slug"`
		Org  struct {
			Login string `json:"login"`
		} `json:"organization"`
	}

	params := url.Values{
		"access_token": {accessToken},
		"limit": {"100"},
	}

	req, _ := http.NewRequest("GET", "https://api.github.com/user/teams?"+params.Encode(), nil)
	req.Header.Set("Accept", "application/vnd.github.moondragon+json")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return false, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return false, err
	}

	if err := json.Unmarshal(body, &teams); err != nil {
		return false, err
	}

	for _, team := range teams {
		if p.Org == team.Org.Login {
			if p.Team == "" || p.Team == team.Slug {
				return true, nil
			}
		}
	}
	return false, nil
}

func (p *GitHubProvider) GetEmailAddress(body []byte, access_token string) (string, error) {

	var emails []struct {
		Email   string `json:"email"`
		Primary bool   `json:"primary"`
	}

	// if we require an Org or Team, check that first
	if p.Org != "" {
		if p.Team != "" {
			if ok, err := p.hasOrgAndTeam(access_token); err != nil || !ok {
				return "", err
			}
		} else {
			if ok, err := p.hasOrg(access_token); err != nil || !ok {
				return "", err
			}
		}
	}

	params := url.Values{
		"access_token": {access_token},
	}
	resp, err := http.DefaultClient.Get("https://api.github.com/user/emails?" + params.Encode())
	if err != nil {
		return "", err
	}
	body, err = ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return "", err
	}

	if err := json.Unmarshal(body, &emails); err != nil {
		return "", err
	}

	for _, email := range emails {
		if email.Primary {
			return email.Email, nil
		}
	}

	return "", nil
}

func (p *GitHubProvider) ValidateToken(access_token string) bool {
	return validateToken(p, access_token, nil)
}
<|endoftext|>