<commit_before>package boltdbpool

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

	"github.com/boltdb/bolt"
)

var (
	DefaultFileMode     = 0644
	DefaultErrorHandler = func(err error) {
		log.Printf("error: %v", err)
	}
	DefaultCloseSleep = 250 * time.Millisecond
)

type Connection struct {
	DB *bolt.DB

	pool       *Pool
	path       string
	count      int64
	closeDelay time.Duration
	closeTime  time.Time
	mu         *sync.Mutex
}

func (c *Connection) Close() error {
	c.decrement()
	if c.count <= 0 {
		if c.closeDelay == 0 {
			return c.removeFromPool()
		}

		c.mu.Lock()
		defer c.mu.Unlock()

		c.closeTime = time.Now().Add(c.closeDelay)
	}
	return nil
}

func (c *Connection) increment() {
	c.mu.Lock()
	defer c.mu.Unlock()

	c.closeTime = time.Time{}
	c.count++
}

func (c *Connection) decrement() {
	c.mu.Lock()
	defer c.mu.Unlock()

	c.count--
}

func (c *Connection) removeFromPool() error {
	return c.pool.remove(c.path)
}

type Options struct {
	BoltOptions  *bolt.Options
	FileMode     os.FileMode
	CloseDelay   time.Duration
	ErrorHandler func(err error)
}

type Pool struct {
	Options *Options

	errorChannel chan error
	connections  map[string]*Connection
	mu           *sync.Mutex
}

func (p *Pool) Close() {
	for _, c := range p.connections {
		p.errorChannel <- c.removeFromPool()
	}
	close(p.errorChannel)
}

func New(options *Options) *Pool {
	if options == nil {
		options = &Options{}
	}
	if options.FileMode == 0 {
		options.FileMode = os.FileMode(DefaultFileMode)
	}
	if options.ErrorHandler == nil {
		options.ErrorHandler = DefaultErrorHandler
	}
	p := &Pool{
		Options:      options,
		errorChannel: make(chan error),
		connections:  map[string]*Connection{},
		mu:           &sync.Mutex{},
	}
	go func() {
		for {
			for _, c := range p.connections {
				if !c.closeTime.IsZero() && c.closeTime.Before(time.Now()) {
					p.errorChannel <- c.removeFromPool()
				}
			}
			time.Sleep(DefaultCloseSleep)
		}
	}()
	go func() {
		for err := range p.errorChannel {
			if err != nil {
				p.Options.ErrorHandler(err)
			}
		}
	}()
	return p
}

func (p *Pool) Get(path string) (*Connection, error) {
	p.mu.Lock()
	defer p.mu.Unlock()

	if c, ok := p.connections[path]; ok {
		c.increment()
		return c, nil
	}
	db, err := bolt.Open(path, p.Options.FileMode, p.Options.BoltOptions)
	if err != nil {
		return nil, err
	}
	c := &Connection{
		DB:         db,
		path:       path,
		pool:       p,
		closeDelay: p.Options.CloseDelay,
		mu:         &sync.Mutex{},
	}
	p.connections[path] = c

	c.increment()
	return c, nil
}

func (p *Pool) Has(path string) bool {
	p.mu.Lock()
	defer p.mu.Unlock()

	_, ok := p.connections[path]
	return ok
}

func (p *Pool) remove(path string) error {
	p.mu.Lock()
	defer p.mu.Unlock()

	c, ok := p.connections[path]
	if !ok {
		return fmt.Errorf("boltdbpool: Unknown DB %s", path)
	}
	delete(p.connections, path)
	return c.DB.Close()
}
<commit_msg>Add comments<commit_after>/*
Package boltdbpool implements a pool container for BoltDB github.com/boltdb/bolt databases.
Pool elements called connections keep reference counts for each database to close it
when it when the count is 0. Database is reused or opened based on database file path. Closing
the database must not be done directly, instead Connection.Close() method should be used.
Database is removed form the pool and closed by the goroutine in the background in respect to
reference count and delay in time if it is specified.

Example:

    package main

    import (
        "fmt"
        "time"

        "resenje.org/boltdbpool"
    )

    func main() {
        pool := boltdbpool.New(&boltdbpool.Options{
            ConnectionExpires: 5 * time.Second,
            ErrorHandler: func(err error) {
                fmt.Printf("error: %v", err)
            }
        })
        defer p.Close()

        ...

        c, err := pool.Get("/tmp/db.bolt")
        if err != nil {
            panic(err)
        }
        defer c.Close()

        ...

        c.DB.Update(func(tx *bolt.TX) error {
            ...
        })
    }
*/
package boltdbpool // import "resenje.org/boltdbpool"

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

	"github.com/boltdb/bolt"
)

var (
	// DefaultFileMode is used in bolt.Open() as file mode for database file
	// if FileMode is not specified in boltdbpool.Options.
	DefaultFileMode = 0644

	// DefaultErrorHandler is a function that accepts errors from
	// goroutine that closes the databases if ErrorHandler is not specified in
	// boltdbpool.Options.
	DefaultErrorHandler = func(err error) {
		log.Printf("error: %v", err)
	}

	// DefaultCloseSleep is a time between database closing iterations.
	defaultCloseSleep = 250 * time.Millisecond
)

// Connection encapsulates bolt.DB and keeps reference counter and closing time information.
type Connection struct {
	DB *bolt.DB

	pool      *Pool
	path      string
	count     int64
	expires   time.Duration
	closeTime time.Time
	mu        *sync.Mutex
}

// Close function on Connection decrements reference counter and closes the database if needed.
func (c *Connection) Close() {
	c.decrement()
	if c.count <= 0 {
		if c.expires == 0 {
			c.pool.errorChannel <- c.removeFromPool()
			return
		}

		c.mu.Lock()
		defer c.mu.Unlock()

		c.closeTime = time.Now().Add(c.expires)
	}
}

func (c *Connection) increment() {
	c.mu.Lock()
	defer c.mu.Unlock()

	// Reset the closing time
	c.closeTime = time.Time{}
	c.count++
}

func (c *Connection) decrement() {
	c.mu.Lock()
	defer c.mu.Unlock()

	c.count--
}

func (c *Connection) removeFromPool() error {
	return c.pool.remove(c.path)
}

// Options are used when a new pool is created that.
type Options struct {
	// BoltOptions is used on bolt.Open().
	BoltOptions *bolt.Options

	// FileMode is used in bolt.Open() as file mode for database file. Deafult: 0640.
	FileMode os.FileMode

	// ConnectionExpires is a duration between the reference count drops to 0 and
	// the time when the database is closed. It is useful to avoid frequent
	// openings of the same database. If the value is 0 (default), no caching is done.
	ConnectionExpires time.Duration

	// ErrorHandler is a function that accepts errors from goroutine that closes the databases.
	ErrorHandler func(err error)
}

// Pool keeps track of connections.
type Pool struct {
	options      *Options
	errorChannel chan error
	connections  map[string]*Connection
	mu           *sync.Mutex
}

// New creates new pool with provided options and also starts database closing goroutone
// and goroutine for errors handling to ErrorHandler.
func New(options *Options) *Pool {
	if options == nil {
		options = &Options{}
	}
	if options.FileMode == 0 {
		options.FileMode = os.FileMode(DefaultFileMode)
	}
	if options.ErrorHandler == nil {
		options.ErrorHandler = DefaultErrorHandler
	}
	p := &Pool{
		options:      options,
		errorChannel: make(chan error),
		connections:  map[string]*Connection{},
		mu:           &sync.Mutex{},
	}
	go func() {
		for {
			for _, c := range p.connections {
				if !c.closeTime.IsZero() && c.closeTime.Before(time.Now()) {
					p.errorChannel <- c.removeFromPool()
				}
			}
			time.Sleep(defaultCloseSleep)
		}
	}()
	go func() {
		for err := range p.errorChannel {
			if err != nil {
				p.options.ErrorHandler(err)
			}
		}
	}()
	return p
}

// Get returns a connection that contains a database or creates a new connection
// with newly opened database based on options specified on pool creation.
func (p *Pool) Get(path string) (*Connection, error) {
	p.mu.Lock()
	defer p.mu.Unlock()

	if c, ok := p.connections[path]; ok {
		c.increment()
		return c, nil
	}
	db, err := bolt.Open(path, p.options.FileMode, p.options.BoltOptions)
	if err != nil {
		return nil, err
	}
	c := &Connection{
		DB:      db,
		path:    path,
		pool:    p,
		expires: p.options.ConnectionExpires,
		mu:      &sync.Mutex{},
	}
	p.connections[path] = c

	c.increment()
	return c, nil
}

// Has returns true if a database with a file path is in the pool.
func (p *Pool) Has(path string) bool {
	p.mu.Lock()
	defer p.mu.Unlock()

	_, ok := p.connections[path]
	return ok
}

// Close function closes and removes from the pool all databases. After the execution
// pool is not usable.
func (p *Pool) Close() {
	for _, c := range p.connections {
		p.errorChannel <- c.removeFromPool()
	}
	close(p.errorChannel)
}

func (p *Pool) remove(path string) error {
	p.mu.Lock()
	defer p.mu.Unlock()

	c, ok := p.connections[path]
	if !ok {
		return fmt.Errorf("boltdbpool: Unknown DB %s", path)
	}
	delete(p.connections, path)
	return c.DB.Close()
}
<|endoftext|>


<commit_before>package cmd

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"text/tabwriter"
	"time"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/apis/batch"

	"github.com/gogo/protobuf/types"
	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/pkg/config"
	"github.com/pachyderm/pachyderm/src/client/version"
	"github.com/pachyderm/pachyderm/src/client/version/versionpb"
	pfscmds "github.com/pachyderm/pachyderm/src/server/pfs/cmds"
	"github.com/pachyderm/pachyderm/src/server/pkg/cmdutil"
	deploycmds "github.com/pachyderm/pachyderm/src/server/pkg/deploy/cmds"
	"github.com/pachyderm/pachyderm/src/server/pkg/metrics"
	ppscmds "github.com/pachyderm/pachyderm/src/server/pps/cmds"

	log "github.com/Sirupsen/logrus"
	"github.com/spf13/cobra"
	"github.com/ugorji/go/codec"
	"golang.org/x/net/context"
	"golang.org/x/sync/errgroup"
	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"
)

// PachctlCmd creates a cobra.Command which can deploy pachyderm clusters and
// interact with them (it implements the pachctl binary).
func PachctlCmd() (*cobra.Command, error) {
	var verbose bool
	var noMetrics bool
	rootCmd := &cobra.Command{
		Use: os.Args[0],
		Long: `Access the Pachyderm API.

Environment variables:
  ADDRESS=<host>:<port>, the pachd server to connect to (e.g. 127.0.0.1:30650).
`,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			if !verbose {
				// Silence any grpc logs
				l := log.New()
				l.Level = log.FatalLevel
				grpclog.SetLogger(l)
			}
		},
	}
	rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Output verbose logs")
	rootCmd.PersistentFlags().BoolVarP(&noMetrics, "no-metrics", "", false, "Don't report user metrics for this command")

	pfsCmds := pfscmds.Cmds(&noMetrics)
	for _, cmd := range pfsCmds {
		rootCmd.AddCommand(cmd)
	}
	ppsCmds, err := ppscmds.Cmds(&noMetrics)
	if err != nil {
		return nil, sanitizeErr(err)
	}
	for _, cmd := range ppsCmds {
		rootCmd.AddCommand(cmd)
	}
	deployCmds := deploycmds.Cmds(&noMetrics)
	for _, cmd := range deployCmds {
		rootCmd.AddCommand(cmd)
	}

	versionCmd := &cobra.Command{
		Use:   "version",
		Short: "Return version information.",
		Long:  "Return version information.",
		Run: cmdutil.RunFixedArgs(0, func(args []string) (retErr error) {
			if !noMetrics {
				start := time.Now()
				startMetricsWait := metrics.StartReportAndFlushUserAction("Version", start)
				defer startMetricsWait()
				defer func() {
					finishMetricsWait := metrics.FinishReportAndFlushUserAction("Version", retErr, start)
					finishMetricsWait()
				}()
			}
			writer := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
			printVersionHeader(writer)
			printVersion(writer, "pachctl", version.Version)
			writer.Flush()

			cfg, err := config.Read()
			if err != nil {
				log.Warningf("error loading user config from ~/.pachderm/config: %v", err)
			}
			pachdAddress := client.GetAddressFromUserMachine(cfg)
			versionClient, err := getVersionAPIClient(pachdAddress)
			if err != nil {
				return sanitizeErr(err)
			}
			ctx, cancel := context.WithTimeout(context.Background(), time.Second)
			defer cancel()
			version, err := versionClient.GetVersion(ctx, &types.Empty{})

			if err != nil {
				buf := bytes.NewBufferString("")
				errWriter := tabwriter.NewWriter(buf, 20, 1, 3, ' ', 0)
				fmt.Fprintf(errWriter, "pachd\t(version unknown) : error connecting to pachd server at address (%v): %v\n\nplease make sure pachd is up (`kubectl get all`) and portforwarding is enabled\n", pachdAddress, sanitizeErr(err))
				errWriter.Flush()
				return errors.New(buf.String())
			}

			printVersion(writer, "pachd", version)
			return writer.Flush()
		}),
	}
	deleteAll := &cobra.Command{
		Use:   "delete-all",
		Short: "Delete everything.",
		Long: `Delete all repos, commits, files, pipelines and jobs.
This resets the cluster to its initial state.`,
		Run: cmdutil.RunFixedArgs(0, func(args []string) error {
			client, err := client.NewOnUserMachine(!noMetrics, "user")
			if err != nil {
				return sanitizeErr(err)
			}
			fmt.Printf("Are you sure you want to delete all repos, commits, files, pipelines and jobs? yN\n")
			r := bufio.NewReader(os.Stdin)
			bytes, err := r.ReadBytes('\n')
			if err != nil {
				return err
			}
			if bytes[0] == 'y' || bytes[0] == 'Y' {
				return client.DeleteAll()
			}
			return nil
		}),
	}
	var port int
	var uiPort int
	var uiWebsocketPort int
	var kubeCtlFlags string
	portForward := &cobra.Command{
		Use:   "port-forward",
		Short: "Forward a port on the local machine to pachd. This command blocks.",
		Long:  "Forward a port on the local machine to pachd. This command blocks.",
		Run: cmdutil.RunFixedArgs(0, func(args []string) error {

			var eg errgroup.Group

			eg.Go(func() error {
				stdin := strings.NewReader(fmt.Sprintf(`
pod=$(kubectl %v get pod -l app=pachd | awk '{if (NR!=1) { print $1; exit 0 }}')
kubectl %v port-forward "$pod" %d:650
`, kubeCtlFlags, kubeCtlFlags, port))
				return cmdutil.RunIO(cmdutil.IO{
					Stdin:  stdin,
					Stderr: os.Stderr,
				}, "sh")
			})

			eg.Go(func() error {
				stdin := strings.NewReader(fmt.Sprintf(`
pod=$(kubectl %v get pod -l app=dash | awk '{if (NR!=1) { print $1; exit 0 }}')
kubectl %v port-forward "$pod" %d:8080
`, kubeCtlFlags, kubeCtlFlags, uiPort))
				if err := cmdutil.RunIO(cmdutil.IO{
					Stdin: stdin,
				}, "sh"); err != nil {
					return fmt.Errorf("UI not enabled, deploy with --dashboard")
				}
				return nil
			})

			eg.Go(func() error {
				stdin := strings.NewReader(fmt.Sprintf(`
pod=$(kubectl %v get pod -l app=dash | awk '{if (NR!=1) { print $1; exit 0 }}')
kubectl %v port-forward "$pod" %d:8081
`, kubeCtlFlags, kubeCtlFlags, uiWebsocketPort))
				cmdutil.RunIO(cmdutil.IO{
					Stdin: stdin,
				}, "sh")
				return nil
			})

			fmt.Printf("Pachd port forwarded\nDash websocket port forwarded\nDash UI port forwarded, navigate to localhost:%v\nCTRL-C to exit", uiPort)
			return eg.Wait()
		}),
	}
	portForward.Flags().IntVarP(&port, "port", "p", 30650, "The local port to bind to.")
	portForward.Flags().IntVarP(&uiPort, "ui-port", "u", 38080, "The local port to bind to.")
	portForward.Flags().IntVarP(&uiWebsocketPort, "proxy-port", "x", 38081, "The local port to bind to.")
	portForward.Flags().StringVarP(&kubeCtlFlags, "kubectlflags", "k", "", "Any kubectl flags to proxy, e.g. --kubectlflags='--kubeconfig /some/path/kubeconfig'")

	garbageCollect := &cobra.Command{
		Use:   "garbage-collect",
		Short: "Garbage collect unused data.",
		Long: `Garbage collect unused data.

When a file/commit/repo is deleted, the data is not immediately removed from the underlying storage system (e.g. S3) for performance and architectural reasons.  This is similar to how when you delete a file on your computer, the file is not necessarily wiped from disk immediately.

To actually remove the data, you will need to manually invoke garbage collection.  The easiest way to do it is through "pachctl garbage-collecth".

Currently "pachctl garbage-collect" can only be started when there are no active jobs running.  You also need to ensure that there's no ongoing "put-file".  Garbage collection puts the cluster into a readonly mode where no new jobs can be created and no data can be added.
`,
		Run: cmdutil.RunFixedArgs(0, func(args []string) (retErr error) {
			client, err := client.NewOnUserMachine(!noMetrics, "user")
			if err != nil {
				return err
			}

			return client.GarbageCollect()
		}),
	}

	var from, to, namespace string
	migrate := &cobra.Command{
		Use:   "migrate",
		Short: "Migrate the internal state of Pachyderm from one version to another.",
		Long: `Migrate the internal state of Pachyderm from one version to
another.  Note that most of the time updating Pachyderm doesn't
require a migration.  Refer to the docs for your specific version
to find out if it requires a migration.

It's highly recommended that you only run migrations when there are no
activities in your cluster, e.g. no jobs should be running.

The migration command takes the general form:

$ pachctl migrate --from <FROM_VERSION> --to <TO_VERSION>

If "--from" is not provided, pachctl will attempt to discover the current
version of the cluster.  If "--to" is not provided, pachctl will use the
version of pachctl itself.

Example:

# Migrate Pachyderm from 1.4.8 to 1.5.0
$ pachctl migrate --from 1.4.8 --to 1.5.0
`,
		Run: cmdutil.RunFixedArgs(0, func(args []string) (retErr error) {
			// If `from` is not provided, we use the cluster version.
			if from == "" {
				cfg, err := config.Read()
				if err != nil {
					log.Warningf("error loading user config from ~/.pachderm/config: %v", err)
				}
				pachdAddress := client.GetAddressFromUserMachine(cfg)
				versionClient, err := getVersionAPIClient(pachdAddress)
				if err != nil {
					return sanitizeErr(err)
				}
				ctx, cancel := context.WithTimeout(context.Background(), time.Second)
				defer cancel()
				clusterVersion, err := versionClient.GetVersion(ctx, &types.Empty{})
				if err != nil {
					return fmt.Errorf("unable to discover cluster version; please provide the --from flag.  Error: %v", err)
				}
				from = version.PrettyPrintVersionNoAdditional(clusterVersion)
			}

			// if `to` is not provided, we use the version of pachctl itself.
			if to == "" {
				to = version.PrettyPrintVersionNoAdditional(version.Version)
			}

			jobSpec := batch.Job{
				TypeMeta: unversioned.TypeMeta{
					Kind:       "Job",
					APIVersion: "batch/v1",
				},
				ObjectMeta: api.ObjectMeta{
					Name: "pach-migration",
					Labels: map[string]string{
						"suite": "pachyderm",
					},
				},
				Spec: batch.JobSpec{
					Template: api.PodTemplateSpec{
						Spec: api.PodSpec{
							Containers: []api.Container{
								{
									Name:    "migration",
									Image:   fmt.Sprintf("pachyderm/pachd:%v", version.PrettyPrintVersion(version.Version)),
									Command: []string{"/pachd", fmt.Sprintf("--migrate=%v-%v", from, to)},
								},
							},
							RestartPolicy: "OnFailure",
						},
					},
				},
			}

			tmpFile, err := ioutil.TempFile("", "")
			if err != nil {
				return err
			}
			defer os.Remove(tmpFile.Name())

			jsonEncoderHandle := &codec.JsonHandle{
				BasicHandle: codec.BasicHandle{
					EncodeOptions: codec.EncodeOptions{Canonical: true},
				},
				Indent: 2,
			}
			encoder := codec.NewEncoder(tmpFile, jsonEncoderHandle)
			jobSpec.CodecEncodeSelf(encoder)
			tmpFile.Close()

			cmd := exec.Command("kubectl", "create", "--validate=false", "-f", tmpFile.Name())
			out, err := cmd.CombinedOutput()
			fmt.Println(string(out))
			if err != nil {
				return err
			}
			fmt.Println("Successfully launched migration.  To see the progress, use `kubectl logs job/pach-migration`")
			return nil
		}),
	}
	migrate.Flags().StringVar(&from, "from", "", "The current version of the cluster.  If not specified, pachctl will attempt to discover the version of the cluster.")
	migrate.Flags().StringVar(&to, "to", "", "The version of Pachyderm to migrate to.  If not specified, pachctl will use its own version.")
	migrate.Flags().StringVar(&namespace, "namespace", "default", "The kubernetes namespace under which Pachyderm is deployed.")

	rootCmd.AddCommand(versionCmd)
	rootCmd.AddCommand(deleteAll)
	rootCmd.AddCommand(portForward)
	rootCmd.AddCommand(garbageCollect)
	rootCmd.AddCommand(migrate)
	return rootCmd, nil
}

func getVersionAPIClient(address string) (versionpb.APIClient, error) {
	clientConn, err := grpc.Dial(address, client.PachDialOptions()...)
	if err != nil {
		return nil, err
	}
	return versionpb.NewAPIClient(clientConn), nil
}

func printVersionHeader(w io.Writer) {
	fmt.Fprintf(w, "COMPONENT\tVERSION\t\n")
}

func printVersion(w io.Writer, component string, v *versionpb.Version) {
	fmt.Fprintf(w, "%s\t%s\t\n", component, version.PrettyPrintVersion(v))
}

func sanitizeErr(err error) error {
	if err == nil {
		return nil
	}

	return errors.New(grpc.ErrorDesc(err))
}
<commit_msg>Expose UI on ports 30080/30081 via port-forward<commit_after>package cmd

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"text/tabwriter"
	"time"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/apis/batch"

	"github.com/gogo/protobuf/types"
	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/pkg/config"
	"github.com/pachyderm/pachyderm/src/client/version"
	"github.com/pachyderm/pachyderm/src/client/version/versionpb"
	pfscmds "github.com/pachyderm/pachyderm/src/server/pfs/cmds"
	"github.com/pachyderm/pachyderm/src/server/pkg/cmdutil"
	deploycmds "github.com/pachyderm/pachyderm/src/server/pkg/deploy/cmds"
	"github.com/pachyderm/pachyderm/src/server/pkg/metrics"
	ppscmds "github.com/pachyderm/pachyderm/src/server/pps/cmds"

	log "github.com/Sirupsen/logrus"
	"github.com/spf13/cobra"
	"github.com/ugorji/go/codec"
	"golang.org/x/net/context"
	"golang.org/x/sync/errgroup"
	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"
)

// PachctlCmd creates a cobra.Command which can deploy pachyderm clusters and
// interact with them (it implements the pachctl binary).
func PachctlCmd() (*cobra.Command, error) {
	var verbose bool
	var noMetrics bool
	rootCmd := &cobra.Command{
		Use: os.Args[0],
		Long: `Access the Pachyderm API.

Environment variables:
  ADDRESS=<host>:<port>, the pachd server to connect to (e.g. 127.0.0.1:30650).
`,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			if !verbose {
				// Silence any grpc logs
				l := log.New()
				l.Level = log.FatalLevel
				grpclog.SetLogger(l)
			}
		},
	}
	rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Output verbose logs")
	rootCmd.PersistentFlags().BoolVarP(&noMetrics, "no-metrics", "", false, "Don't report user metrics for this command")

	pfsCmds := pfscmds.Cmds(&noMetrics)
	for _, cmd := range pfsCmds {
		rootCmd.AddCommand(cmd)
	}
	ppsCmds, err := ppscmds.Cmds(&noMetrics)
	if err != nil {
		return nil, sanitizeErr(err)
	}
	for _, cmd := range ppsCmds {
		rootCmd.AddCommand(cmd)
	}
	deployCmds := deploycmds.Cmds(&noMetrics)
	for _, cmd := range deployCmds {
		rootCmd.AddCommand(cmd)
	}

	versionCmd := &cobra.Command{
		Use:   "version",
		Short: "Return version information.",
		Long:  "Return version information.",
		Run: cmdutil.RunFixedArgs(0, func(args []string) (retErr error) {
			if !noMetrics {
				start := time.Now()
				startMetricsWait := metrics.StartReportAndFlushUserAction("Version", start)
				defer startMetricsWait()
				defer func() {
					finishMetricsWait := metrics.FinishReportAndFlushUserAction("Version", retErr, start)
					finishMetricsWait()
				}()
			}
			writer := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
			printVersionHeader(writer)
			printVersion(writer, "pachctl", version.Version)
			writer.Flush()

			cfg, err := config.Read()
			if err != nil {
				log.Warningf("error loading user config from ~/.pachderm/config: %v", err)
			}
			pachdAddress := client.GetAddressFromUserMachine(cfg)
			versionClient, err := getVersionAPIClient(pachdAddress)
			if err != nil {
				return sanitizeErr(err)
			}
			ctx, cancel := context.WithTimeout(context.Background(), time.Second)
			defer cancel()
			version, err := versionClient.GetVersion(ctx, &types.Empty{})

			if err != nil {
				buf := bytes.NewBufferString("")
				errWriter := tabwriter.NewWriter(buf, 20, 1, 3, ' ', 0)
				fmt.Fprintf(errWriter, "pachd\t(version unknown) : error connecting to pachd server at address (%v): %v\n\nplease make sure pachd is up (`kubectl get all`) and portforwarding is enabled\n", pachdAddress, sanitizeErr(err))
				errWriter.Flush()
				return errors.New(buf.String())
			}

			printVersion(writer, "pachd", version)
			return writer.Flush()
		}),
	}
	deleteAll := &cobra.Command{
		Use:   "delete-all",
		Short: "Delete everything.",
		Long: `Delete all repos, commits, files, pipelines and jobs.
This resets the cluster to its initial state.`,
		Run: cmdutil.RunFixedArgs(0, func(args []string) error {
			client, err := client.NewOnUserMachine(!noMetrics, "user")
			if err != nil {
				return sanitizeErr(err)
			}
			fmt.Printf("Are you sure you want to delete all repos, commits, files, pipelines and jobs? yN\n")
			r := bufio.NewReader(os.Stdin)
			bytes, err := r.ReadBytes('\n')
			if err != nil {
				return err
			}
			if bytes[0] == 'y' || bytes[0] == 'Y' {
				return client.DeleteAll()
			}
			return nil
		}),
	}
	var port int
	var uiPort int
	var uiWebsocketPort int
	var kubeCtlFlags string
	portForward := &cobra.Command{
		Use:   "port-forward",
		Short: "Forward a port on the local machine to pachd. This command blocks.",
		Long:  "Forward a port on the local machine to pachd. This command blocks.",
		Run: cmdutil.RunFixedArgs(0, func(args []string) error {

			var eg errgroup.Group

			eg.Go(func() error {
				stdin := strings.NewReader(fmt.Sprintf(`
pod=$(kubectl %v get pod -l app=pachd | awk '{if (NR!=1) { print $1; exit 0 }}')
kubectl %v port-forward "$pod" %d:650
`, kubeCtlFlags, kubeCtlFlags, port))
				return cmdutil.RunIO(cmdutil.IO{
					Stdin:  stdin,
					Stderr: os.Stderr,
				}, "sh")
			})

			eg.Go(func() error {
				stdin := strings.NewReader(fmt.Sprintf(`
pod=$(kubectl %v get pod -l app=dash | awk '{if (NR!=1) { print $1; exit 0 }}')
kubectl %v port-forward "$pod" %d:8080
`, kubeCtlFlags, kubeCtlFlags, uiPort))
				if err := cmdutil.RunIO(cmdutil.IO{
					Stdin: stdin,
				}, "sh"); err != nil {
					return fmt.Errorf("UI not enabled, deploy with --dashboard")
				}
				return nil
			})

			eg.Go(func() error {
				stdin := strings.NewReader(fmt.Sprintf(`
pod=$(kubectl %v get pod -l app=dash | awk '{if (NR!=1) { print $1; exit 0 }}')
kubectl %v port-forward "$pod" %d:8081
`, kubeCtlFlags, kubeCtlFlags, uiWebsocketPort))
				cmdutil.RunIO(cmdutil.IO{
					Stdin: stdin,
				}, "sh")
				return nil
			})

			fmt.Printf("Pachd port forwarded\nDash websocket port forwarded\nDash UI port forwarded, navigate to localhost:%v\nCTRL-C to exit", uiPort)
			return eg.Wait()
		}),
	}
	portForward.Flags().IntVarP(&port, "port", "p", 30650, "The local port to bind to.")
	portForward.Flags().IntVarP(&uiPort, "ui-port", "u", 30080, "The local port to bind to.")
	portForward.Flags().IntVarP(&uiWebsocketPort, "proxy-port", "x", 30081, "The local port to bind to.")
	portForward.Flags().StringVarP(&kubeCtlFlags, "kubectlflags", "k", "", "Any kubectl flags to proxy, e.g. --kubectlflags='--kubeconfig /some/path/kubeconfig'")

	garbageCollect := &cobra.Command{
		Use:   "garbage-collect",
		Short: "Garbage collect unused data.",
		Long: `Garbage collect unused data.

When a file/commit/repo is deleted, the data is not immediately removed from the underlying storage system (e.g. S3) for performance and architectural reasons.  This is similar to how when you delete a file on your computer, the file is not necessarily wiped from disk immediately.

To actually remove the data, you will need to manually invoke garbage collection.  The easiest way to do it is through "pachctl garbage-collecth".

Currently "pachctl garbage-collect" can only be started when there are no active jobs running.  You also need to ensure that there's no ongoing "put-file".  Garbage collection puts the cluster into a readonly mode where no new jobs can be created and no data can be added.
`,
		Run: cmdutil.RunFixedArgs(0, func(args []string) (retErr error) {
			client, err := client.NewOnUserMachine(!noMetrics, "user")
			if err != nil {
				return err
			}

			return client.GarbageCollect()
		}),
	}

	var from, to, namespace string
	migrate := &cobra.Command{
		Use:   "migrate",
		Short: "Migrate the internal state of Pachyderm from one version to another.",
		Long: `Migrate the internal state of Pachyderm from one version to
another.  Note that most of the time updating Pachyderm doesn't
require a migration.  Refer to the docs for your specific version
to find out if it requires a migration.

It's highly recommended that you only run migrations when there are no
activities in your cluster, e.g. no jobs should be running.

The migration command takes the general form:

$ pachctl migrate --from <FROM_VERSION> --to <TO_VERSION>

If "--from" is not provided, pachctl will attempt to discover the current
version of the cluster.  If "--to" is not provided, pachctl will use the
version of pachctl itself.

Example:

# Migrate Pachyderm from 1.4.8 to 1.5.0
$ pachctl migrate --from 1.4.8 --to 1.5.0
`,
		Run: cmdutil.RunFixedArgs(0, func(args []string) (retErr error) {
			// If `from` is not provided, we use the cluster version.
			if from == "" {
				cfg, err := config.Read()
				if err != nil {
					log.Warningf("error loading user config from ~/.pachderm/config: %v", err)
				}
				pachdAddress := client.GetAddressFromUserMachine(cfg)
				versionClient, err := getVersionAPIClient(pachdAddress)
				if err != nil {
					return sanitizeErr(err)
				}
				ctx, cancel := context.WithTimeout(context.Background(), time.Second)
				defer cancel()
				clusterVersion, err := versionClient.GetVersion(ctx, &types.Empty{})
				if err != nil {
					return fmt.Errorf("unable to discover cluster version; please provide the --from flag.  Error: %v", err)
				}
				from = version.PrettyPrintVersionNoAdditional(clusterVersion)
			}

			// if `to` is not provided, we use the version of pachctl itself.
			if to == "" {
				to = version.PrettyPrintVersionNoAdditional(version.Version)
			}

			jobSpec := batch.Job{
				TypeMeta: unversioned.TypeMeta{
					Kind:       "Job",
					APIVersion: "batch/v1",
				},
				ObjectMeta: api.ObjectMeta{
					Name: "pach-migration",
					Labels: map[string]string{
						"suite": "pachyderm",
					},
				},
				Spec: batch.JobSpec{
					Template: api.PodTemplateSpec{
						Spec: api.PodSpec{
							Containers: []api.Container{
								{
									Name:    "migration",
									Image:   fmt.Sprintf("pachyderm/pachd:%v", version.PrettyPrintVersion(version.Version)),
									Command: []string{"/pachd", fmt.Sprintf("--migrate=%v-%v", from, to)},
								},
							},
							RestartPolicy: "OnFailure",
						},
					},
				},
			}

			tmpFile, err := ioutil.TempFile("", "")
			if err != nil {
				return err
			}
			defer os.Remove(tmpFile.Name())

			jsonEncoderHandle := &codec.JsonHandle{
				BasicHandle: codec.BasicHandle{
					EncodeOptions: codec.EncodeOptions{Canonical: true},
				},
				Indent: 2,
			}
			encoder := codec.NewEncoder(tmpFile, jsonEncoderHandle)
			jobSpec.CodecEncodeSelf(encoder)
			tmpFile.Close()

			cmd := exec.Command("kubectl", "create", "--validate=false", "-f", tmpFile.Name())
			out, err := cmd.CombinedOutput()
			fmt.Println(string(out))
			if err != nil {
				return err
			}
			fmt.Println("Successfully launched migration.  To see the progress, use `kubectl logs job/pach-migration`")
			return nil
		}),
	}
	migrate.Flags().StringVar(&from, "from", "", "The current version of the cluster.  If not specified, pachctl will attempt to discover the version of the cluster.")
	migrate.Flags().StringVar(&to, "to", "", "The version of Pachyderm to migrate to.  If not specified, pachctl will use its own version.")
	migrate.Flags().StringVar(&namespace, "namespace", "default", "The kubernetes namespace under which Pachyderm is deployed.")

	rootCmd.AddCommand(versionCmd)
	rootCmd.AddCommand(deleteAll)
	rootCmd.AddCommand(portForward)
	rootCmd.AddCommand(garbageCollect)
	rootCmd.AddCommand(migrate)
	return rootCmd, nil
}

func getVersionAPIClient(address string) (versionpb.APIClient, error) {
	clientConn, err := grpc.Dial(address, client.PachDialOptions()...)
	if err != nil {
		return nil, err
	}
	return versionpb.NewAPIClient(clientConn), nil
}

func printVersionHeader(w io.Writer) {
	fmt.Fprintf(w, "COMPONENT\tVERSION\t\n")
}

func printVersion(w io.Writer, component string, v *versionpb.Version) {
	fmt.Fprintf(w, "%s\t%s\t\n", component, version.PrettyPrintVersion(v))
}

func sanitizeErr(err error) error {
	if err == nil {
		return nil
	}

	return errors.New(grpc.ErrorDesc(err))
}
<|endoftext|>


<commit_before>package sparta

import (
	"bytes"
	"fmt"
	"strings"
	"text/template"

	gocf "github.com/mweagle/go-cloudformation"
	"github.com/rs/zerolog"
)

// Utility function to marshal an interface
func marshalInterface(item interface{}) interface{} {
	if item != nil {
		return item
	}
	return nil
}

// Utility function to marshal array of strings
func marshalStringArray(vals []string) *gocf.StringListExpr {
	if len(vals) <= 0 {
		return nil
	}
	arrValues := []gocf.Stringable{}
	for i := 0; i != len(vals); i++ {
		arrValues = append(arrValues, gocf.String(vals[i]))
	}
	return gocf.StringList(arrValues...)
}

// Utility function to marshal an int
func marshalInt(intVal int64) *gocf.IntegerExpr {
	if intVal != 0 {
		return gocf.Integer(intVal)
	}
	return nil
}

// Utility function to marshal a string
func marshalString(stringVal string) *gocf.StringExpr {
	if stringVal != "" {
		return gocf.String(stringVal)
	}
	return nil
}

func marshalStringExpr(stringExpr gocf.Stringable) *gocf.StringExpr {
	if stringExpr != nil {
		return stringExpr.String()
	}
	return nil
}

// Utility function to marshal a string lsit
func marshalStringList(stringVals []string) *gocf.StringListExpr {
	if len(stringVals) != 0 {
		stringableList := make([]gocf.Stringable, len(stringVals))
		for eachIndex, eachStringVal := range stringVals {
			stringableList[eachIndex] = gocf.String(eachStringVal)
		}
		return gocf.StringList(stringableList...)
	}
	return nil
}

// Utility function to marshal a boolean
func marshalBool(boolValue bool) *gocf.BoolExpr {
	if !boolValue {
		return gocf.Bool(boolValue)
	}
	return nil
}

// resourceOutputs is responsible for returning the conditional
// set of CloudFormation outputs for a given resource type.
func resourceOutputs(resourceName string,
	resource gocf.ResourceProperties,
	logger *zerolog.Logger) ([]string, error) {

	outputProps := []string{}
	switch typedResource := resource.(type) {
	case gocf.IAMRole:
		// NOP
	case *gocf.DynamoDBTable:
		if typedResource.StreamSpecification != nil {
			outputProps = append(outputProps, "StreamArn")
		}
	case gocf.DynamoDBTable:
		if typedResource.StreamSpecification != nil {
			outputProps = append(outputProps, "StreamArn")
		}
	case gocf.KinesisStream,
		*gocf.KinesisStream:
		outputProps = append(outputProps, "Arn")
	case gocf.Route53RecordSet,
		*gocf.Route53RecordSet:
		// NOP
	case gocf.S3Bucket,
		*gocf.S3Bucket:
		outputProps = append(outputProps, "DomainName", "WebsiteURL")
	case gocf.SNSTopic,
		*gocf.SNSTopic:
		outputProps = append(outputProps, "TopicName")
	case gocf.SQSQueue,
		*gocf.SQSQueue:
		outputProps = append(outputProps, "Arn", "QueueName")
	default:
		logger.Warn().
			Str("ResourceType", fmt.Sprintf("%T", typedResource)).
			Msg("Discovery information for dependency not yet implemented")
	}
	return outputProps, nil
}

func newCloudFormationResource(resourceType string, logger *zerolog.Logger) (gocf.ResourceProperties, error) {
	resProps := gocf.NewResourceByType(resourceType)
	if nil == resProps {

		logger.Fatal().
			Str("Type", resourceType).
			Msg("Failed to create CloudFormation CustomResource!")

		return nil, fmt.Errorf("unsupported CustomResourceType: %s", resourceType)
	}
	return resProps, nil
}

type discoveryDataTemplate struct {
	ResourceID         string
	ResourceType       string
	ResourceProperties string
}

var discoveryDataForResourceDependency = `
	{
		"ResourceID" : "<< .ResourceID >>",
		"ResourceRef" : "{"Ref":"<< .ResourceID >>"}",
		"ResourceType" : "<< .ResourceType >>",
		"Properties" : {
			<< .ResourceProperties >>
		}
	}
`

func discoveryResourceInfoForDependency(cfTemplate *gocf.Template,
	logicalResourceName string,
	logger *zerolog.Logger) ([]byte, error) {

	item, ok := cfTemplate.Resources[logicalResourceName]
	if !ok {
		return nil, nil
	}
	resourceOutputs, resourceOutputsErr := resourceOutputs(logicalResourceName,
		item.Properties,
		logger)
	if resourceOutputsErr != nil {
		return nil, resourceOutputsErr
	}
	// Template data
	templateData := &discoveryDataTemplate{
		ResourceID:   logicalResourceName,
		ResourceType: item.Properties.CfnResourceType(),
	}
	quotedAttrs := make([]string, len(resourceOutputs))
	for eachIndex, eachOutput := range resourceOutputs {
		quotedAttrs[eachIndex] = fmt.Sprintf(`"%s" :"{ "Fn::GetAtt" : [ "%s", "%s" ] }"`,
			eachOutput,
			logicalResourceName,
			eachOutput)
	}
	templateData.ResourceProperties = strings.Join(quotedAttrs, ",")

	// Create the data that can be stuffed into Environment
	discoveryTemplate, discoveryTemplateErr := template.New("discoveryResourceData").
		Delims("<<", ">>").
		Parse(discoveryDataForResourceDependency)
	if nil != discoveryTemplateErr {
		return nil, discoveryTemplateErr
	}

	var templateResults bytes.Buffer
	evalResultErr := discoveryTemplate.Execute(&templateResults, templateData)
	return templateResults.Bytes(), evalResultErr
}
func safeAppendDependency(resource *gocf.Resource, dependencyName string) {
	if nil == resource.DependsOn {
		resource.DependsOn = []string{}
	}
	resource.DependsOn = append(resource.DependsOn, dependencyName)
}
func safeMetadataInsert(resource *gocf.Resource, key string, value interface{}) {
	if nil == resource.Metadata {
		resource.Metadata = make(map[string]interface{})
	}
	resource.Metadata[key] = value
}
<commit_msg>Remove obsolete function<commit_after>package sparta

import (
	"bytes"
	"fmt"
	"strings"
	"text/template"

	gocf "github.com/mweagle/go-cloudformation"
	"github.com/rs/zerolog"
)

// Utility function to marshal an interface
func marshalInterface(item interface{}) interface{} {
	if item != nil {
		return item
	}
	return nil
}

// Utility function to marshal an int
func marshalInt(intVal int64) *gocf.IntegerExpr {
	if intVal != 0 {
		return gocf.Integer(intVal)
	}
	return nil
}

// Utility function to marshal a string
func marshalString(stringVal string) *gocf.StringExpr {
	if stringVal != "" {
		return gocf.String(stringVal)
	}
	return nil
}

func marshalStringExpr(stringExpr gocf.Stringable) *gocf.StringExpr {
	if stringExpr != nil {
		return stringExpr.String()
	}
	return nil
}

// Utility function to marshal a string lsit
func marshalStringList(stringVals []string) *gocf.StringListExpr {
	if len(stringVals) != 0 {
		stringableList := make([]gocf.Stringable, len(stringVals))
		for eachIndex, eachStringVal := range stringVals {
			stringableList[eachIndex] = gocf.String(eachStringVal)
		}
		return gocf.StringList(stringableList...)
	}
	return nil
}

// Utility function to marshal a boolean
func marshalBool(boolValue bool) *gocf.BoolExpr {
	if !boolValue {
		return gocf.Bool(boolValue)
	}
	return nil
}

// resourceOutputs is responsible for returning the conditional
// set of CloudFormation outputs for a given resource type.
func resourceOutputs(resourceName string,
	resource gocf.ResourceProperties,
	logger *zerolog.Logger) ([]string, error) {

	outputProps := []string{}
	switch typedResource := resource.(type) {
	case gocf.IAMRole:
		// NOP
	case *gocf.DynamoDBTable:
		if typedResource.StreamSpecification != nil {
			outputProps = append(outputProps, "StreamArn")
		}
	case gocf.DynamoDBTable:
		if typedResource.StreamSpecification != nil {
			outputProps = append(outputProps, "StreamArn")
		}
	case gocf.KinesisStream,
		*gocf.KinesisStream:
		outputProps = append(outputProps, "Arn")
	case gocf.Route53RecordSet,
		*gocf.Route53RecordSet:
		// NOP
	case gocf.S3Bucket,
		*gocf.S3Bucket:
		outputProps = append(outputProps, "DomainName", "WebsiteURL")
	case gocf.SNSTopic,
		*gocf.SNSTopic:
		outputProps = append(outputProps, "TopicName")
	case gocf.SQSQueue,
		*gocf.SQSQueue:
		outputProps = append(outputProps, "Arn", "QueueName")
	default:
		logger.Warn().
			Str("ResourceType", fmt.Sprintf("%T", typedResource)).
			Msg("Discovery information for dependency not yet implemented")
	}
	return outputProps, nil
}

func newCloudFormationResource(resourceType string, logger *zerolog.Logger) (gocf.ResourceProperties, error) {
	resProps := gocf.NewResourceByType(resourceType)
	if nil == resProps {

		logger.Fatal().
			Str("Type", resourceType).
			Msg("Failed to create CloudFormation CustomResource!")

		return nil, fmt.Errorf("unsupported CustomResourceType: %s", resourceType)
	}
	return resProps, nil
}

type discoveryDataTemplate struct {
	ResourceID         string
	ResourceType       string
	ResourceProperties string
}

var discoveryDataForResourceDependency = `
	{
		"ResourceID" : "<< .ResourceID >>",
		"ResourceRef" : "{"Ref":"<< .ResourceID >>"}",
		"ResourceType" : "<< .ResourceType >>",
		"Properties" : {
			<< .ResourceProperties >>
		}
	}
`

func discoveryResourceInfoForDependency(cfTemplate *gocf.Template,
	logicalResourceName string,
	logger *zerolog.Logger) ([]byte, error) {

	item, ok := cfTemplate.Resources[logicalResourceName]
	if !ok {
		return nil, nil
	}
	resourceOutputs, resourceOutputsErr := resourceOutputs(logicalResourceName,
		item.Properties,
		logger)
	if resourceOutputsErr != nil {
		return nil, resourceOutputsErr
	}
	// Template data
	templateData := &discoveryDataTemplate{
		ResourceID:   logicalResourceName,
		ResourceType: item.Properties.CfnResourceType(),
	}
	quotedAttrs := make([]string, len(resourceOutputs))
	for eachIndex, eachOutput := range resourceOutputs {
		quotedAttrs[eachIndex] = fmt.Sprintf(`"%s" :"{ "Fn::GetAtt" : [ "%s", "%s" ] }"`,
			eachOutput,
			logicalResourceName,
			eachOutput)
	}
	templateData.ResourceProperties = strings.Join(quotedAttrs, ",")

	// Create the data that can be stuffed into Environment
	discoveryTemplate, discoveryTemplateErr := template.New("discoveryResourceData").
		Delims("<<", ">>").
		Parse(discoveryDataForResourceDependency)
	if nil != discoveryTemplateErr {
		return nil, discoveryTemplateErr
	}

	var templateResults bytes.Buffer
	evalResultErr := discoveryTemplate.Execute(&templateResults, templateData)
	return templateResults.Bytes(), evalResultErr
}
func safeAppendDependency(resource *gocf.Resource, dependencyName string) {
	if nil == resource.DependsOn {
		resource.DependsOn = []string{}
	}
	resource.DependsOn = append(resource.DependsOn, dependencyName)
}
func safeMetadataInsert(resource *gocf.Resource, key string, value interface{}) {
	if nil == resource.Metadata {
		resource.Metadata = make(map[string]interface{})
	}
	resource.Metadata[key] = value
}
<|endoftext|>


<commit_before>// Copyright 2016-2017 Authors of Cilium
//
// 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/ioutil"
	"path/filepath"
	"reflect"

	"github.com/cilium/cilium/common"
	"github.com/cilium/cilium/pkg/endpoint"
	"github.com/cilium/cilium/pkg/endpointmanager"
	"github.com/cilium/cilium/pkg/ipam"
	"github.com/cilium/cilium/pkg/labels"
	"github.com/cilium/cilium/pkg/logfields"
	"github.com/cilium/cilium/pkg/policy"
	"github.com/cilium/cilium/pkg/workloads/containerd"

	log "github.com/sirupsen/logrus"
)

// deleteNonFunctionalEndpoints cleans all endpoints that do not have a running
// container associated with it.
func (d *Daemon) deleteNonFunctionalEndpoints() {
	for _, ep := range endpointmanager.Endpoints {
		if !containerd.IsRunning(ep) {
			log.WithFields(log.Fields{
				logfields.EndpointID: ep.StringID(),
			}).Info("No workload could be associated with endpoint, removing endpoint")
			d.deleteEndpoint(ep)
		}
	}
}

// SyncState syncs cilium state against the containers running in the host. dir is the
// cilium's running directory. If clean is set, the endpoints that don't have its
// container in running state are deleted.
func (d *Daemon) SyncState(dir string, clean bool) error {
	restored := 0

	log.Info("Restoring old cilium endpoints...")

	dirFiles, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}
	eptsID := endpoint.FilterEPDir(dirFiles)

	possibleEPs := readEPsFromDirNames(dir, eptsID)

	if len(possibleEPs) == 0 {
		log.Info("No old endpoints found.")
		return nil
	}

	for _, ep := range possibleEPs {
		log.WithField(logfields.EndpointID, ep.ID).Debug("Restoring endpoint")

		if err := d.syncLabels(ep); err != nil {
			log.WithError(err).WithField(logfields.EndpointID, ep.ID).Warn("Unable to restore endpoint")
			continue
		}

		ep.Mutex.Lock()
		if d.conf.KeepConfig {
			ep.SetDefaultOpts(nil)
		} else {
			ep.SetDefaultOpts(d.conf.Opts)
			alwaysEnforce := policy.GetPolicyEnabled() == endpoint.AlwaysEnforce
			ep.Opts.Set(endpoint.OptionPolicy, alwaysEnforce)
		}

		endpointmanager.Insert(ep)
		restored++

		ep.Mutex.Unlock()
	}

	if clean {
		d.deleteNonFunctionalEndpoints()
	}

	endpointmanager.Mutex.Lock()
	nEndpoints := len(endpointmanager.Endpoints)
	wg := make(chan bool, nEndpoints)
	for k := range endpointmanager.Endpoints {
		ep := endpointmanager.Endpoints[k]
		go func(ep *endpoint.Endpoint, wg chan<- bool) {
			scopedLog := log.WithField(logfields.EndpointID, ep.ID)
			if err := d.allocateIPs(ep); err != nil {
				scopedLog.WithError(err).Error("Failed to re-allocate IP of endpoint. Not restoring endpoint.")
				d.deleteEndpoint(ep)
				wg <- false
				return
			}

			if buildSuccess := <-ep.Regenerate(d); !buildSuccess {
				scopedLog.Warn("Failed while regenerating endpoint")
				wg <- false
				return
			}
			ep.Mutex.RLock()
			log.WithField(logfields.IPAddr, []string{ep.IPv4.String(), ep.IPv6.String()}).Info("Restored endpoint with IPs")
			ep.Mutex.RUnlock()
			wg <- true
		}(ep, wg)
	}
	endpointmanager.Mutex.Unlock()

	restored, total := 0, 0
	if nEndpoints > 0 {
		for buildSuccess := range wg {
			if buildSuccess {
				restored++
			}
			total++
			if total >= nEndpoints {
				break
			}
		}
	}
	close(wg)

	log.WithFields(log.Fields{
		"count.restored": restored,
		"count.total":    total,
	}).Info("Found endpoints of which some were restored")

	return nil
}

func (d *Daemon) allocateIPs(ep *endpoint.Endpoint) error {
	ep.Mutex.RLock()
	defer ep.Mutex.RUnlock()
	err := ipam.AllocateIP(ep.IPv6.IP())
	if err != nil {
		// TODO if allocation failed reallocate a new IP address and setup veth
		// pair accordingly
		return fmt.Errorf("unable to reallocate IPv6 address: %s", err)
	}

	defer func(ep *endpoint.Endpoint) {
		if err != nil {
			ipam.ReleaseIP(ep.IPv6.IP())
		}
	}(ep)

	if !d.conf.IPv4Disabled {
		if ep.IPv4 != nil {
			if err = ipam.AllocateIP(ep.IPv4.IP()); err != nil {
				return fmt.Errorf("unable to reallocate IPv4 address: %s", err)
			}
		}
	}
	return nil
}

// readEPsFromDirNames returns a list of endpoints from a list of directory
// names that can possible contain an endpoint.
func readEPsFromDirNames(basePath string, eptsDirNames []string) []*endpoint.Endpoint {
	possibleEPs := []*endpoint.Endpoint{}
	for _, epID := range eptsDirNames {
		epDir := filepath.Join(basePath, epID)
		readDir := func() string {
			scopedLog := log.WithFields(log.Fields{
				logfields.EndpointID: epID,
				logfields.Path:       filepath.Join(epDir, common.CHeaderFileName),
			})
			scopedLog.Debug("Reading directory")
			epFiles, err := ioutil.ReadDir(epDir)
			if err != nil {
				scopedLog.Warn("Error while reading directory. Ignoring it...")
				return ""
			}
			cHeaderFile := common.FindEPConfigCHeader(epDir, epFiles)
			if cHeaderFile == "" {
				scopedLog.Info("File not found. Ignoring endpoint.")
				return ""
			}
			return cHeaderFile
		}
		// There's an odd issue where the first read dir doesn't work.
		cHeaderFile := readDir()
		if cHeaderFile == "" {
			cHeaderFile = readDir()
		}

		scopedLog := log.WithFields(log.Fields{
			logfields.EndpointID: epID,
			logfields.Path:       cHeaderFile,
		})
		scopedLog.Debug("Found endpoint C header file")

		strEp, err := common.GetCiliumVersionString(cHeaderFile)
		if err != nil {
			scopedLog.WithError(err).Warn("Unable to read the C header file")
			continue
		}
		ep, err := endpoint.ParseEndpoint(strEp)
		if err != nil {
			scopedLog.WithError(err).Warn("Unable to read the C header file")
			continue
		}
		possibleEPs = append(possibleEPs, ep)
	}
	return possibleEPs
}

// syncLabels syncs the labels from the labels' database for the given endpoint.
// To be used with endpoint.Mutex locked.
func (d *Daemon) syncLabels(ep *endpoint.Endpoint) error {
	if ep.SecLabel == nil {
		return fmt.Errorf("Endpoint doesn't have a security label.")
	}

	// Filter the restored labels with the new daemon's filter
	idtyLbls, _ := labels.FilterLabels(ep.SecLabel.Labels)

	ep.SecLabel.Labels = idtyLbls

	sha256sum := ep.SecLabel.Labels.SHA256Sum()
	labels, err := LookupIdentityBySHA256(sha256sum)
	if err != nil {
		return fmt.Errorf("Unable to get labels of sha256sum:%s: %+v\n", sha256sum, err)
	}

	if labels == nil {
		l, _, err := d.CreateOrUpdateIdentity(ep.SecLabel.Labels, ep.StringID())
		if err != nil {
			return fmt.Errorf("Unable to put labels %+v: %s\n", ep.SecLabel.Labels, err)
		}
		labels = l
	}

	if !reflect.DeepEqual(labels.Labels, ep.SecLabel.Labels) {
		return fmt.Errorf("The set of labels should be the same for " +
			"the endpoint being restored and the labels stored")
	}

	if labels.ID != ep.SecLabel.ID {
		log.WithFields(log.Fields{
			logfields.EndpointID:              ep.ID,
			logfields.IdentityLabels + ".old": ep.SecLabel.ID,
			logfields.IdentityLabels + ".new": labels.ID,
		}).Info("Security label ID for endpoint is different that the one stored, updating")
	}
	ep.SetIdentity(d, labels)

	return nil
}
<commit_msg>daemon: Daemon.SyncState locks Endpoint to syncLabels<commit_after>// Copyright 2016-2017 Authors of Cilium
//
// 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/ioutil"
	"path/filepath"
	"reflect"

	"github.com/cilium/cilium/common"
	"github.com/cilium/cilium/pkg/endpoint"
	"github.com/cilium/cilium/pkg/endpointmanager"
	"github.com/cilium/cilium/pkg/ipam"
	"github.com/cilium/cilium/pkg/labels"
	"github.com/cilium/cilium/pkg/logfields"
	"github.com/cilium/cilium/pkg/policy"
	"github.com/cilium/cilium/pkg/workloads/containerd"

	log "github.com/sirupsen/logrus"
)

// deleteNonFunctionalEndpoints cleans all endpoints that do not have a running
// container associated with it.
func (d *Daemon) deleteNonFunctionalEndpoints() {
	for _, ep := range endpointmanager.Endpoints {
		if !containerd.IsRunning(ep) {
			log.WithFields(log.Fields{
				logfields.EndpointID: ep.StringID(),
			}).Info("No workload could be associated with endpoint, removing endpoint")
			d.deleteEndpoint(ep)
		}
	}
}

// SyncState syncs cilium state against the containers running in the host. dir is the
// cilium's running directory. If clean is set, the endpoints that don't have its
// container in running state are deleted.
func (d *Daemon) SyncState(dir string, clean bool) error {
	restored := 0

	log.Info("Restoring old cilium endpoints...")

	dirFiles, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}
	eptsID := endpoint.FilterEPDir(dirFiles)

	possibleEPs := readEPsFromDirNames(dir, eptsID)

	if len(possibleEPs) == 0 {
		log.Info("No old endpoints found.")
		return nil
	}

	for _, ep := range possibleEPs {
		ep.Mutex.Lock()
		log.WithField(logfields.EndpointID, ep.ID).Debug("Restoring endpoint")

		if err := d.syncLabels(ep); err != nil {
			log.WithError(err).WithField(logfields.EndpointID, ep.ID).Warn("Unable to restore endpoint")
			ep.Mutex.Unlock()
			continue
		}

		if d.conf.KeepConfig {
			ep.SetDefaultOpts(nil)
		} else {
			ep.SetDefaultOpts(d.conf.Opts)
			alwaysEnforce := policy.GetPolicyEnabled() == endpoint.AlwaysEnforce
			ep.Opts.Set(endpoint.OptionPolicy, alwaysEnforce)
		}

		endpointmanager.Insert(ep)
		restored++

		ep.Mutex.Unlock()
	}

	if clean {
		d.deleteNonFunctionalEndpoints()
	}

	endpointmanager.Mutex.Lock()
	nEndpoints := len(endpointmanager.Endpoints)
	wg := make(chan bool, nEndpoints)
	for k := range endpointmanager.Endpoints {
		ep := endpointmanager.Endpoints[k]
		go func(ep *endpoint.Endpoint, wg chan<- bool) {
			scopedLog := log.WithField(logfields.EndpointID, ep.ID)
			if err := d.allocateIPs(ep); err != nil {
				scopedLog.WithError(err).Error("Failed to re-allocate IP of endpoint. Not restoring endpoint.")
				d.deleteEndpoint(ep)
				wg <- false
				return
			}

			if buildSuccess := <-ep.Regenerate(d); !buildSuccess {
				scopedLog.Warn("Failed while regenerating endpoint")
				wg <- false
				return
			}
			ep.Mutex.RLock()
			log.WithField(logfields.IPAddr, []string{ep.IPv4.String(), ep.IPv6.String()}).Info("Restored endpoint with IPs")
			ep.Mutex.RUnlock()
			wg <- true
		}(ep, wg)
	}
	endpointmanager.Mutex.Unlock()

	restored, total := 0, 0
	if nEndpoints > 0 {
		for buildSuccess := range wg {
			if buildSuccess {
				restored++
			}
			total++
			if total >= nEndpoints {
				break
			}
		}
	}
	close(wg)

	log.WithFields(log.Fields{
		"count.restored": restored,
		"count.total":    total,
	}).Info("Found endpoints of which some were restored")

	return nil
}

func (d *Daemon) allocateIPs(ep *endpoint.Endpoint) error {
	ep.Mutex.RLock()
	defer ep.Mutex.RUnlock()
	err := ipam.AllocateIP(ep.IPv6.IP())
	if err != nil {
		// TODO if allocation failed reallocate a new IP address and setup veth
		// pair accordingly
		return fmt.Errorf("unable to reallocate IPv6 address: %s", err)
	}

	defer func(ep *endpoint.Endpoint) {
		if err != nil {
			ipam.ReleaseIP(ep.IPv6.IP())
		}
	}(ep)

	if !d.conf.IPv4Disabled {
		if ep.IPv4 != nil {
			if err = ipam.AllocateIP(ep.IPv4.IP()); err != nil {
				return fmt.Errorf("unable to reallocate IPv4 address: %s", err)
			}
		}
	}
	return nil
}

// readEPsFromDirNames returns a list of endpoints from a list of directory
// names that can possible contain an endpoint.
func readEPsFromDirNames(basePath string, eptsDirNames []string) []*endpoint.Endpoint {
	possibleEPs := []*endpoint.Endpoint{}
	for _, epID := range eptsDirNames {
		epDir := filepath.Join(basePath, epID)
		readDir := func() string {
			scopedLog := log.WithFields(log.Fields{
				logfields.EndpointID: epID,
				logfields.Path:       filepath.Join(epDir, common.CHeaderFileName),
			})
			scopedLog.Debug("Reading directory")
			epFiles, err := ioutil.ReadDir(epDir)
			if err != nil {
				scopedLog.Warn("Error while reading directory. Ignoring it...")
				return ""
			}
			cHeaderFile := common.FindEPConfigCHeader(epDir, epFiles)
			if cHeaderFile == "" {
				scopedLog.Info("File not found. Ignoring endpoint.")
				return ""
			}
			return cHeaderFile
		}
		// There's an odd issue where the first read dir doesn't work.
		cHeaderFile := readDir()
		if cHeaderFile == "" {
			cHeaderFile = readDir()
		}

		scopedLog := log.WithFields(log.Fields{
			logfields.EndpointID: epID,
			logfields.Path:       cHeaderFile,
		})
		scopedLog.Debug("Found endpoint C header file")

		strEp, err := common.GetCiliumVersionString(cHeaderFile)
		if err != nil {
			scopedLog.WithError(err).Warn("Unable to read the C header file")
			continue
		}
		ep, err := endpoint.ParseEndpoint(strEp)
		if err != nil {
			scopedLog.WithError(err).Warn("Unable to read the C header file")
			continue
		}
		possibleEPs = append(possibleEPs, ep)
	}
	return possibleEPs
}

// syncLabels syncs the labels from the labels' database for the given endpoint.
// To be used with endpoint.Mutex locked.
func (d *Daemon) syncLabels(ep *endpoint.Endpoint) error {
	if ep.SecLabel == nil {
		return fmt.Errorf("Endpoint doesn't have a security label.")
	}

	// Filter the restored labels with the new daemon's filter
	idtyLbls, _ := labels.FilterLabels(ep.SecLabel.Labels)

	ep.SecLabel.Labels = idtyLbls

	sha256sum := ep.SecLabel.Labels.SHA256Sum()
	labels, err := LookupIdentityBySHA256(sha256sum)
	if err != nil {
		return fmt.Errorf("Unable to get labels of sha256sum:%s: %+v\n", sha256sum, err)
	}

	if labels == nil {
		l, _, err := d.CreateOrUpdateIdentity(ep.SecLabel.Labels, ep.StringID())
		if err != nil {
			return fmt.Errorf("Unable to put labels %+v: %s\n", ep.SecLabel.Labels, err)
		}
		labels = l
	}

	if !reflect.DeepEqual(labels.Labels, ep.SecLabel.Labels) {
		return fmt.Errorf("The set of labels should be the same for " +
			"the endpoint being restored and the labels stored")
	}

	if labels.ID != ep.SecLabel.ID {
		log.WithFields(log.Fields{
			logfields.EndpointID:              ep.ID,
			logfields.IdentityLabels + ".old": ep.SecLabel.ID,
			logfields.IdentityLabels + ".new": labels.ID,
		}).Info("Security label ID for endpoint is different that the one stored, updating")
	}
	ep.SetIdentity(d, labels)

	return nil
}
<|endoftext|>


<commit_before>package engine

import (
	"context"
	"database/sql"

	"github.com/gernest/ngorm/dialects"
	"github.com/gernest/ngorm/model"
)

// DB contains information for current db connection
type Engine struct {
	Value             interface{}
	Error             error
	RowsAffected      int64
	Parent            *Engine
	logMode           int
	SingularTable     bool
	source            string
	values            map[string]interface{}
	blockGlobalUpdate bool
	ctx               context.Context
	Dialect           dialects.Dialect

	Search    *Search
	Scope     *Scope
	StructMap *model.SafeModelStructsMap
}

func (e *Engine) AddError(err error) error {
	return nil
}

type Search struct {
	WhereConditions  []map[string]interface{}
	OrConditions     []map[string]interface{}
	NotConditions    []map[string]interface{}
	HavingConditions []map[string]interface{}
	JoinConditions   []map[string]interface{}
	InitAttrs        []interface{}
	AssignAttrs      []interface{}
	Selects          map[string]interface{}
	Omits            []string
	orders           []interface{}
	Preload          []SearchPreload
	Offset           interface{}
	Limit            interface{}
	Group            string
	TableName        string
	Raw              bool
	Unscoped         bool
	IgnoreOrderQuery bool
}

type SearchPreload struct {
	Schema     string
	Conditions []interface{}
}

type Scope struct {
	Value           interface{}
	SQL             string
	SQLVars         []interface{}
	InstanceID      string
	PrimaryKeyField *model.Field
	SkipLeft        bool
	Fields          *[]*model.Field
	SelectAttrs     *[]string
}

type DbTabler interface {
	TableName(*Engine) string
}

type Tabler interface {
	TableName() string
}

type SQLCommon interface {
	Exec(query string, args ...interface{}) (sql.Result, error)
	Prepare(query string) (*sql.Stmt, error)
	Query(query string, args ...interface{}) (*sql.Rows, error)
	QueryRow(query string, args ...interface{}) *sql.Row
}
<commit_msg>[engine] Add SQL field<commit_after>package engine

import (
	"context"
	"database/sql"

	"github.com/gernest/ngorm/dialects"
	"github.com/gernest/ngorm/model"
)

// DB contains information for current db connection
type Engine struct {
	Value             interface{}
	Error             error
	RowsAffected      int64
	Parent            *Engine
	logMode           int
	SingularTable     bool
	source            string
	values            map[string]interface{}
	blockGlobalUpdate bool
	ctx               context.Context
	Dialect           dialects.Dialect

	Search    *Search
	Scope     *Scope
	StructMap *model.SafeModelStructsMap

	SQL *SQL
}

type SQL struct {
	Query  string
	Values []interface{}
}

func (e *Engine) AddError(err error) error {
	return nil
}

type Search struct {
	WhereConditions  []map[string]interface{}
	OrConditions     []map[string]interface{}
	NotConditions    []map[string]interface{}
	HavingConditions []map[string]interface{}
	JoinConditions   []map[string]interface{}
	InitAttrs        []interface{}
	AssignAttrs      []interface{}
	Selects          map[string]interface{}
	Omits            []string
	orders           []interface{}
	Preload          []SearchPreload
	Offset           interface{}
	Limit            interface{}
	Group            string
	TableName        string
	Raw              bool
	Unscoped         bool
	IgnoreOrderQuery bool
}

type SearchPreload struct {
	Schema     string
	Conditions []interface{}
}

type Scope struct {
	Value           interface{}
	SQL             string
	SQLVars         []interface{}
	InstanceID      string
	PrimaryKeyField *model.Field
	SkipLeft        bool
	Fields          *[]*model.Field
	SelectAttrs     *[]string
}

type DbTabler interface {
	TableName(*Engine) string
}

type Tabler interface {
	TableName() string
}

type SQLCommon interface {
	Exec(query string, args ...interface{}) (sql.Result, error)
	Prepare(query string) (*sql.Stmt, error)
	Query(query string, args ...interface{}) (*sql.Rows, error)
	QueryRow(query string, args ...interface{}) *sql.Row
}
<|endoftext|>


<commit_before>// +build darwin,!arm,!arm64 linux windows
// +build !ios,!android,!js,!sdl,!headless,!vulkan

package engo

import (
	"io"
	"log"
	"os"
	"os/signal"
	"runtime"
	"syscall"
	"time"

	"github.com/EngoEngine/gl"
	"github.com/go-gl/glfw/v3.2/glfw"

	"github.com/Noofbiz/glfwMojaveFix"
)

var (
	// Window is the glfw.Window used for engo
	Window *glfw.Window
	// Gl is the current OpenGL context
	Gl *gl.Context

	cursorArrow     *glfw.Cursor
	cursorIBeam     *glfw.Cursor
	cursorCrosshair *glfw.Cursor
	cursorHand      *glfw.Cursor
	cursorHResize   *glfw.Cursor
	cursorVResize   *glfw.Cursor

	scale = float32(1)
)

func init() {
	runtime.LockOSThread()
}

// CreateWindow sets up the GLFW window and prepares the OpenGL surface for rendering
func CreateWindow(title string, width, height int, fullscreen bool, msaa int) {
	CurrentBackEnd = BackEndGLFW
	err := glfw.Init()
	if err != nil {
		log.Fatal(err)
	}

	if !opts.HeadlessMode {
		cursorArrow = glfw.CreateStandardCursor(glfw.ArrowCursor)
		cursorIBeam = glfw.CreateStandardCursor(glfw.IBeamCursor)
		cursorCrosshair = glfw.CreateStandardCursor(glfw.CrosshairCursor)
		cursorHand = glfw.CreateStandardCursor(glfw.HandCursor)
		cursorHResize = glfw.CreateStandardCursor(glfw.HResizeCursor)
		cursorVResize = glfw.CreateStandardCursor(glfw.VResizeCursor)
	}

	monitor := glfw.GetPrimaryMonitor()

	var mode *glfw.VidMode
	if monitor != nil {
		mode = monitor.GetVideoMode()
	} else {
		// Initialize default values if no monitor is found
		mode = &glfw.VidMode{
			Width:       1,
			Height:      1,
			RedBits:     8,
			GreenBits:   8,
			BlueBits:    8,
			RefreshRate: 60,
		}
	}

	gameWidth = float32(width)
	gameHeight = float32(height)

	if fullscreen {
		width = mode.Width
		height = mode.Height
		glfw.WindowHint(glfw.Decorated, 0)
	} else {
		monitor = nil
	}

	if opts.HeadlessMode {
		glfw.WindowHint(glfw.Visible, glfw.False)
	}
	if opts.NotResizable {
		glfw.WindowHint(glfw.Resizable, glfw.False)
	}

	glfw.WindowHint(glfw.ContextVersionMajor, 2)
	glfw.WindowHint(glfw.ContextVersionMinor, 1)

	glfw.WindowHint(glfw.Samples, msaa)

	if opts.HeadlessMode {
		Gl = gl.NewContext()
		return
	}

	Window, err = glfw.CreateWindow(width, height, title, monitor, nil)
	if err != nil {
		log.Fatal(err)
	}

	Window.MakeContextCurrent()

	if !fullscreen {
		Window.SetPos((mode.Width-width)/2, (mode.Height-height)/2)
	}

	SetVSync(opts.VSync)

	Gl = gl.NewContext()

	width, height = Window.GetSize()
	windowWidth, windowHeight = float32(width), float32(height)

	fw, fh := Window.GetFramebufferSize()
	canvasWidth, canvasHeight = float32(fw), float32(fh)

	if windowWidth <= canvasWidth && windowHeight <= canvasHeight {
		scale = canvasWidth / windowWidth
	}

	Window.SetFramebufferSizeCallback(func(Window *glfw.Window, w, h int) {
		Gl.Viewport(0, 0, w, h)
		width, height = Window.GetSize()
		windowWidth, windowHeight = float32(width), float32(width)

		oldCanvasW, oldCanvasH := canvasWidth, canvasHeight

		canvasWidth, canvasHeight = float32(w), float32(h)

		ResizeXOffset += oldCanvasW - canvasWidth
		ResizeYOffset += oldCanvasH - canvasHeight

		if windowWidth <= canvasWidth && windowHeight <= canvasHeight {
			scale = canvasWidth / windowWidth
		}
	})

	Window.SetCursorPosCallback(func(Window *glfw.Window, x, y float64) {
		Input.Mouse.X, Input.Mouse.Y = float32(x)/opts.GlobalScale.X, float32(y)/opts.GlobalScale.Y
		if Input.Mouse.Action != Release && Input.Mouse.Action != Press {
			Input.Mouse.Action = Move
		}
	})

	Window.SetMouseButtonCallback(func(Window *glfw.Window, b glfw.MouseButton, a glfw.Action, m glfw.ModifierKey) {
		x, y := Window.GetCursorPos()
		Input.Mouse.X, Input.Mouse.Y = float32(x)/(opts.GlobalScale.X), float32(y)/(opts.GlobalScale.Y)

		// this is only valid because we use an internal structure that is
		// 100% compatible with glfw3.h
		Input.Mouse.Button = MouseButton(b)
		Input.Mouse.Modifer = Modifier(m)

		if a == glfw.Press {
			Input.Mouse.Action = Press
		} else {
			Input.Mouse.Action = Release
		}
	})

	Window.SetScrollCallback(func(Window *glfw.Window, xoff, yoff float64) {
		Input.Mouse.ScrollX = float32(xoff)
		Input.Mouse.ScrollY = float32(yoff)
	})

	Window.SetKeyCallback(func(Window *glfw.Window, k glfw.Key, s int, a glfw.Action, m glfw.ModifierKey) {
		key := Key(k)
		if a == glfw.Press {
			Input.keys.Set(key, true)
		} else if a == glfw.Release {
			Input.keys.Set(key, false)
		}
	})

	Window.SetSizeCallback(func(w *glfw.Window, widthInt int, heightInt int) {
		message := WindowResizeMessage{
			OldWidth:  int(windowWidth),
			OldHeight: int(windowHeight),
			NewWidth:  widthInt,
			NewHeight: heightInt,
		}

		windowWidth = float32(widthInt)
		windowHeight = float32(heightInt)

		// TODO: verify these for retina displays & verify if needed here
		fw, fh := Window.GetFramebufferSize()
		canvasWidth, canvasHeight = float32(fw), float32(fh)

		if !opts.ScaleOnResize {
			gameWidth, gameHeight = float32(widthInt), float32(heightInt)
		}

		Mailbox.Dispatch(message)
	})

	Window.SetCharCallback(func(Window *glfw.Window, char rune) {
		Mailbox.Dispatch(TextMessage{char})
	})

	Window.SetCloseCallback(func(Window *glfw.Window) {
		Exit()
	})
}

// DestroyWindow handles the termination of windows
func DestroyWindow() {
	glfw.Terminate()
}

// SetTitle sets the title of the window
func SetTitle(title string) {
	if opts.HeadlessMode {
		log.Println("Title set to:", title)
	} else {
		Window.SetTitle(title)
	}
}

// RunIteration runs one iteration per frame
func RunIteration() {
	Time.Tick()

	// First check for new keypresses
	if !opts.HeadlessMode {
		Input.update()
		glfw.PollEvents()
	}

	// Then update the world and all Systems
	currentUpdater.Update(Time.Delta())

	// Lastly, forget keypresses and swap buffers
	if !opts.HeadlessMode {
		// reset values to avoid catching the same "signal" twice
		Input.Mouse.ScrollX, Input.Mouse.ScrollY = 0, 0
		Input.Mouse.Action = Neutral

		glfwMojaveFix.UpdateNSGLContext(*Window)
		Window.SwapBuffers()
	}
}

// RunPreparation is called automatically when calling Open. It should only be called once.
func RunPreparation(defaultScene Scene) {
	Time = NewClock()
	SetScene(defaultScene, false)
}

func runLoop(defaultScene Scene, headless bool) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	signal.Notify(c, syscall.SIGTERM)
	go func() {
		<-c
		closeEvent()
	}()

	RunPreparation(defaultScene)
	ticker := time.NewTicker(time.Duration(int(time.Second) / opts.FPSLimit))

	// Start tick, minimize the delta
	Time.Tick()

	for {
		select {
		case <-ticker.C:
			RunIteration()
		case <-resetLoopTicker:
			ticker.Stop()
			ticker = time.NewTicker(time.Duration(int(time.Second) / opts.FPSLimit))
		case <-closeGame:
			ticker.Stop()
			closeEvent()
			return
		}
	}
}

// CursorPos returns the current cursor position
func CursorPos() (x, y float32) {
	w, h := Window.GetCursorPos()
	return float32(w), float32(h)
}

// WindowSize gets the current window size
func WindowSize() (w, h int) {
	return Window.GetSize()
}

// WindowWidth gets the current window width
func WindowWidth() float32 {
	return windowWidth
}

// WindowHeight gets the current window height
func WindowHeight() float32 {
	return windowHeight
}

// CanvasWidth gets the width of the current OpenGL Framebuffer
func CanvasWidth() float32 {
	return canvasWidth
}

// CanvasHeight gets the height of the current OpenGL Framebuffer
func CanvasHeight() float32 {
	return canvasHeight
}

// CanvasScale gets the ratio of the canvas to the window sizes
func CanvasScale() float32 {
	return scale
}

// SetCursor sets the pointer of the mouse to the defined standard cursor
func SetCursor(c Cursor) {
	var cur *glfw.Cursor
	switch c {
	case CursorNone:
		cur = nil // just for the documentation, this isn't required
	case CursorArrow:
		cur = cursorArrow
	case CursorCrosshair:
		cur = cursorCrosshair
	case CursorHand:
		cur = cursorHand
	case CursorIBeam:
		cur = cursorIBeam
	case CursorHResize:
		cur = cursorHResize
	case CursorVResize:
		cur = cursorVResize
	}
	Window.SetCursor(cur)
}

// SetVSync sets whether or not to use VSync
func SetVSync(enabled bool) {
	opts.VSync = enabled
	if opts.VSync {
		glfw.SwapInterval(1)
	} else {
		glfw.SwapInterval(0)
	}
}

//SetCursorVisibility sets the visibility of the cursor.
//If true the cursor is visible, if false the cursor is not.
func SetCursorVisibility(visible bool) {
	if visible {
		glfw.GetCurrentContext().SetInputMode(glfw.CursorMode, glfw.CursorNormal)
	} else {
		glfw.GetCurrentContext().SetInputMode(glfw.CursorMode, glfw.CursorHidden)
	}
}

// openFile is the desktop-specific way of opening a file
func openFile(url string) (io.ReadCloser, error) {
	return os.Open(url)
}

// IsAndroidChrome tells if the browser is Chrome for android
func IsAndroidChrome() bool {
	return false
}
<commit_msg>fix width/height at fullscreen<commit_after>// +build darwin,!arm,!arm64 linux windows
// +build !ios,!android,!js,!sdl,!headless,!vulkan

package engo

import (
	"io"
	"log"
	"os"
	"os/signal"
	"runtime"
	"syscall"
	"time"

	"github.com/EngoEngine/gl"
	"github.com/go-gl/glfw/v3.2/glfw"

	"github.com/Noofbiz/glfwMojaveFix"
)

var (
	// Window is the glfw.Window used for engo
	Window *glfw.Window
	// Gl is the current OpenGL context
	Gl *gl.Context

	cursorArrow     *glfw.Cursor
	cursorIBeam     *glfw.Cursor
	cursorCrosshair *glfw.Cursor
	cursorHand      *glfw.Cursor
	cursorHResize   *glfw.Cursor
	cursorVResize   *glfw.Cursor

	scale = float32(1)
)

func init() {
	runtime.LockOSThread()
}

// CreateWindow sets up the GLFW window and prepares the OpenGL surface for rendering
func CreateWindow(title string, width, height int, fullscreen bool, msaa int) {
	CurrentBackEnd = BackEndGLFW
	err := glfw.Init()
	if err != nil {
		log.Fatal(err)
	}

	if !opts.HeadlessMode {
		cursorArrow = glfw.CreateStandardCursor(glfw.ArrowCursor)
		cursorIBeam = glfw.CreateStandardCursor(glfw.IBeamCursor)
		cursorCrosshair = glfw.CreateStandardCursor(glfw.CrosshairCursor)
		cursorHand = glfw.CreateStandardCursor(glfw.HandCursor)
		cursorHResize = glfw.CreateStandardCursor(glfw.HResizeCursor)
		cursorVResize = glfw.CreateStandardCursor(glfw.VResizeCursor)
	}

	monitor := glfw.GetPrimaryMonitor()

	var mode *glfw.VidMode
	if monitor != nil {
		mode = monitor.GetVideoMode()
	} else {
		// Initialize default values if no monitor is found
		mode = &glfw.VidMode{
			Width:       1,
			Height:      1,
			RedBits:     8,
			GreenBits:   8,
			BlueBits:    8,
			RefreshRate: 60,
		}
	}

	if fullscreen {
		width = mode.Width
		height = mode.Height
		glfw.WindowHint(glfw.Decorated, 0)
	} else {
		monitor = nil
	}

	gameWidth = float32(width)
	gameHeight = float32(height)

	if opts.HeadlessMode {
		glfw.WindowHint(glfw.Visible, glfw.False)
	}
	if opts.NotResizable {
		glfw.WindowHint(glfw.Resizable, glfw.False)
	}

	glfw.WindowHint(glfw.ContextVersionMajor, 2)
	glfw.WindowHint(glfw.ContextVersionMinor, 1)

	glfw.WindowHint(glfw.Samples, msaa)

	if opts.HeadlessMode {
		Gl = gl.NewContext()
		return
	}

	Window, err = glfw.CreateWindow(width, height, title, monitor, nil)
	if err != nil {
		log.Fatal(err)
	}

	Window.MakeContextCurrent()

	if !fullscreen {
		Window.SetPos((mode.Width-width)/2, (mode.Height-height)/2)
	}

	SetVSync(opts.VSync)

	Gl = gl.NewContext()

	width, height = Window.GetSize()
	windowWidth, windowHeight = float32(width), float32(height)

	fw, fh := Window.GetFramebufferSize()
	canvasWidth, canvasHeight = float32(fw), float32(fh)

	if windowWidth <= canvasWidth && windowHeight <= canvasHeight {
		scale = canvasWidth / windowWidth
	}

	Window.SetFramebufferSizeCallback(func(Window *glfw.Window, w, h int) {
		Gl.Viewport(0, 0, w, h)
		width, height = Window.GetSize()
		windowWidth, windowHeight = float32(width), float32(width)

		oldCanvasW, oldCanvasH := canvasWidth, canvasHeight

		canvasWidth, canvasHeight = float32(w), float32(h)

		ResizeXOffset += oldCanvasW - canvasWidth
		ResizeYOffset += oldCanvasH - canvasHeight

		if windowWidth <= canvasWidth && windowHeight <= canvasHeight {
			scale = canvasWidth / windowWidth
		}
	})

	Window.SetCursorPosCallback(func(Window *glfw.Window, x, y float64) {
		Input.Mouse.X, Input.Mouse.Y = float32(x)/opts.GlobalScale.X, float32(y)/opts.GlobalScale.Y
		if Input.Mouse.Action != Release && Input.Mouse.Action != Press {
			Input.Mouse.Action = Move
		}
	})

	Window.SetMouseButtonCallback(func(Window *glfw.Window, b glfw.MouseButton, a glfw.Action, m glfw.ModifierKey) {
		x, y := Window.GetCursorPos()
		Input.Mouse.X, Input.Mouse.Y = float32(x)/(opts.GlobalScale.X), float32(y)/(opts.GlobalScale.Y)

		// this is only valid because we use an internal structure that is
		// 100% compatible with glfw3.h
		Input.Mouse.Button = MouseButton(b)
		Input.Mouse.Modifer = Modifier(m)

		if a == glfw.Press {
			Input.Mouse.Action = Press
		} else {
			Input.Mouse.Action = Release
		}
	})

	Window.SetScrollCallback(func(Window *glfw.Window, xoff, yoff float64) {
		Input.Mouse.ScrollX = float32(xoff)
		Input.Mouse.ScrollY = float32(yoff)
	})

	Window.SetKeyCallback(func(Window *glfw.Window, k glfw.Key, s int, a glfw.Action, m glfw.ModifierKey) {
		key := Key(k)
		if a == glfw.Press {
			Input.keys.Set(key, true)
		} else if a == glfw.Release {
			Input.keys.Set(key, false)
		}
	})

	Window.SetSizeCallback(func(w *glfw.Window, widthInt int, heightInt int) {
		message := WindowResizeMessage{
			OldWidth:  int(windowWidth),
			OldHeight: int(windowHeight),
			NewWidth:  widthInt,
			NewHeight: heightInt,
		}

		windowWidth = float32(widthInt)
		windowHeight = float32(heightInt)

		// TODO: verify these for retina displays & verify if needed here
		fw, fh := Window.GetFramebufferSize()
		canvasWidth, canvasHeight = float32(fw), float32(fh)

		if !opts.ScaleOnResize {
			gameWidth, gameHeight = float32(widthInt), float32(heightInt)
		}

		Mailbox.Dispatch(message)
	})

	Window.SetCharCallback(func(Window *glfw.Window, char rune) {
		Mailbox.Dispatch(TextMessage{char})
	})

	Window.SetCloseCallback(func(Window *glfw.Window) {
		Exit()
	})
}

// DestroyWindow handles the termination of windows
func DestroyWindow() {
	glfw.Terminate()
}

// SetTitle sets the title of the window
func SetTitle(title string) {
	if opts.HeadlessMode {
		log.Println("Title set to:", title)
	} else {
		Window.SetTitle(title)
	}
}

// RunIteration runs one iteration per frame
func RunIteration() {
	Time.Tick()

	// First check for new keypresses
	if !opts.HeadlessMode {
		Input.update()
		glfw.PollEvents()
	}

	// Then update the world and all Systems
	currentUpdater.Update(Time.Delta())

	// Lastly, forget keypresses and swap buffers
	if !opts.HeadlessMode {
		// reset values to avoid catching the same "signal" twice
		Input.Mouse.ScrollX, Input.Mouse.ScrollY = 0, 0
		Input.Mouse.Action = Neutral

		glfwMojaveFix.UpdateNSGLContext(*Window)
		Window.SwapBuffers()
	}
}

// RunPreparation is called automatically when calling Open. It should only be called once.
func RunPreparation(defaultScene Scene) {
	Time = NewClock()
	SetScene(defaultScene, false)
}

func runLoop(defaultScene Scene, headless bool) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	signal.Notify(c, syscall.SIGTERM)
	go func() {
		<-c
		closeEvent()
	}()

	RunPreparation(defaultScene)
	ticker := time.NewTicker(time.Duration(int(time.Second) / opts.FPSLimit))

	// Start tick, minimize the delta
	Time.Tick()

	for {
		select {
		case <-ticker.C:
			RunIteration()
		case <-resetLoopTicker:
			ticker.Stop()
			ticker = time.NewTicker(time.Duration(int(time.Second) / opts.FPSLimit))
		case <-closeGame:
			ticker.Stop()
			closeEvent()
			return
		}
	}
}

// CursorPos returns the current cursor position
func CursorPos() (x, y float32) {
	w, h := Window.GetCursorPos()
	return float32(w), float32(h)
}

// WindowSize gets the current window size
func WindowSize() (w, h int) {
	return Window.GetSize()
}

// WindowWidth gets the current window width
func WindowWidth() float32 {
	return windowWidth
}

// WindowHeight gets the current window height
func WindowHeight() float32 {
	return windowHeight
}

// CanvasWidth gets the width of the current OpenGL Framebuffer
func CanvasWidth() float32 {
	return canvasWidth
}

// CanvasHeight gets the height of the current OpenGL Framebuffer
func CanvasHeight() float32 {
	return canvasHeight
}

// CanvasScale gets the ratio of the canvas to the window sizes
func CanvasScale() float32 {
	return scale
}

// SetCursor sets the pointer of the mouse to the defined standard cursor
func SetCursor(c Cursor) {
	var cur *glfw.Cursor
	switch c {
	case CursorNone:
		cur = nil // just for the documentation, this isn't required
	case CursorArrow:
		cur = cursorArrow
	case CursorCrosshair:
		cur = cursorCrosshair
	case CursorHand:
		cur = cursorHand
	case CursorIBeam:
		cur = cursorIBeam
	case CursorHResize:
		cur = cursorHResize
	case CursorVResize:
		cur = cursorVResize
	}
	Window.SetCursor(cur)
}

// SetVSync sets whether or not to use VSync
func SetVSync(enabled bool) {
	opts.VSync = enabled
	if opts.VSync {
		glfw.SwapInterval(1)
	} else {
		glfw.SwapInterval(0)
	}
}

//SetCursorVisibility sets the visibility of the cursor.
//If true the cursor is visible, if false the cursor is not.
func SetCursorVisibility(visible bool) {
	if visible {
		glfw.GetCurrentContext().SetInputMode(glfw.CursorMode, glfw.CursorNormal)
	} else {
		glfw.GetCurrentContext().SetInputMode(glfw.CursorMode, glfw.CursorHidden)
	}
}

// openFile is the desktop-specific way of opening a file
func openFile(url string) (io.ReadCloser, error) {
	return os.Open(url)
}

// IsAndroidChrome tells if the browser is Chrome for android
func IsAndroidChrome() bool {
	return false
}
<|endoftext|>


<commit_before>package bsw

import (
	"crypto/tls"
	"net/http"
	"net/url"
	"regexp"
)

func Headers(ip string) ([]Result, error) {
	results := make([]Result, 0, 2)
	host, _ := hostnameFromHttpLocationHeader(ip, "http")
	if host != "" {
		results = append(results, Result{Source: "Headers", IP: ip, Hostname: host})
	}
	host, _ = hostnameFromHttpLocationHeader(ip, "https")
	if host != "" {
		results = append(results, Result{Source: "Headers", IP: ip, Hostname: host})
	}
	return results, nil
}

func hostnameFromHttpLocationHeader(ip string, protocol string) (string, error) {
	req, err := http.NewRequest("GET", protocol+"://"+ip, nil)
	if err != nil {
		return "", err
	}
	tr := http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
	res, err := tr.RoundTrip(req)
	if err != nil {
		return "", err
	}
	location := res.Header["Location"]
	if location != nil {
		u, err := url.Parse(location[0])
		if err != nil {
			return "", err
		}
		host := u.Host
		if m, _ := regexp.Match("[a-zA-Z]+", []byte(host)); m == true {
			return u.Host, nil
		}
		return "", nil
	}
	return "", nil
}
<commit_msg>Fixes return of host<commit_after>package bsw

import (
	"crypto/tls"
	"net/http"
	"net/url"
	"regexp"
)

func Headers(ip string) ([]Result, error) {
	results := make([]Result, 0, 2)
	host, _ := hostnameFromHttpLocationHeader(ip, "http")
	if host != "" {
		results = append(results, Result{Source: "Headers", IP: ip, Hostname: host})
	}
	host, _ = hostnameFromHttpLocationHeader(ip, "https")
	if host != "" {
		results = append(results, Result{Source: "Headers", IP: ip, Hostname: host})
	}
	return results, nil
}

func hostnameFromHttpLocationHeader(ip string, protocol string) (string, error) {
	req, err := http.NewRequest("GET", protocol+"://"+ip, nil)
	if err != nil {
		return "", err
	}
	tr := http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
	res, err := tr.RoundTrip(req)
	if err != nil {
		return "", err
	}
	location := res.Header["Location"]
	if location != nil {
		u, err := url.Parse(location[0])
		if err != nil {
			return "", err
		}
		host := u.Host
		if m, _ := regexp.Match("[a-zA-Z]+", []byte(host)); m == true {
			return host, nil
		}
		return "", nil
	}
	return "", nil
}
<|endoftext|>


<commit_before>package etcd

import (
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"regexp"
	"strconv"

	etcd2 "github.com/coreos/go-etcd/etcd"
	"github.com/gliderlabs/registrator/bridge"
	etcd "gopkg.in/coreos/go-etcd.v0/etcd"
)

func init() {
	bridge.Register(new(Factory), "etcd")
}

type Factory struct{}

func (f *Factory) New(uri *url.URL) bridge.RegistryAdapter {
	urls := make([]string, 0)
	if uri.Host != "" {
		urls = append(urls, "http://"+uri.Host)
	} else {
		urls = append(urls, "http://127.0.0.1:4001")
	}

	res, err := http.Get(urls[0] + "/version")
	if err != nil {
		log.Fatal("etcd: error retrieving version", err)
	}

	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)

	if match, _ := regexp.Match("0\\.4\\.*", body); match == true {
		log.Println("etcd: using v0 client")
		return &EtcdAdapter{client: etcd.NewClient(urls), path: uri.Path}
	}

	return &EtcdAdapter{client2: etcd2.NewClient(urls), path: uri.Path}
}

type EtcdAdapter struct {
	client  *etcd.Client
	client2 *etcd2.Client

	path string
}

func (r *EtcdAdapter) Ping() error {
	r.syncEtcdCluster()

	var err error
	if r.client != nil {
		rr := etcd.NewRawRequest("GET", "version", nil, nil)
		_, err = r.client.SendRequest(rr)
	} else {
		rr := etcd2.NewRawRequest("GET", "version", nil, nil)
		_, err = r.client2.SendRequest(rr)
	}

	if err != nil {
		return err
	}
	return nil
}

func (r *EtcdAdapter) syncEtcdCluster() {
	var result bool
	if r.client != nil {
		result = r.client.SyncCluster()
	} else {
		result = r.client2.SyncCluster()
	}

	if !result {
		log.Println("etcd: sync cluster was unsuccessful")
	}
}

func (r *EtcdAdapter) Register(service *bridge.Service) error {
	r.syncEtcdCluster()

	path := r.path + "/" + service.Name + "/" + service.ID
	port := strconv.Itoa(service.Port)
	addr := net.JoinHostPort(service.IP, port)

	var err error
	if r.client != nil {
		_, err = r.client.Set(path, addr, uint64(service.TTL))
	} else {
		_, err = r.client2.Set(path, addr, uint64(service.TTL))
	}

	if err != nil {
		log.Println("etcd: failed to register service:", err)
	}
	return err
}

func (r *EtcdAdapter) Deregister(service *bridge.Service) error {
	r.syncEtcdCluster()

	path := r.path + "/" + service.Name + "/" + service.ID

	var err error
	if r.client != nil {
		_, err = r.client.Delete(path, false)
	} else {
		_, err = r.client2.Delete(path, false)
	}

	if err != nil {
		log.Println("etcd: failed to deregister service:", err)
	}
	return err
}

func (r *EtcdAdapter) Refresh(service *bridge.Service) error {
	return r.Register(service)
}
<commit_msg>Added etcds adapter for using etcd via tls<commit_after>package etcd

import (
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"os"

	etcd2 "github.com/coreos/go-etcd/etcd"
	"github.com/gliderlabs/registrator/bridge"
	etcd "gopkg.in/coreos/go-etcd.v0/etcd"
)

func init() {
  bridge.Register(&Factory{Scheme: "http"}, "etcd")
  bridge.Register(&Factory{Scheme: "https"}, "etcds")
}

type Factory struct{
  Scheme string
}

func (f *Factory) New(uri *url.URL) bridge.RegistryAdapter {
	urls := make([]string, 0)
	if uri.Host != "" {
		urls = append(urls, f.Scheme+"://"+uri.Host)
	} else {
		urls = append(urls, f.Scheme+"://127.0.0.1:4001")
	}

  if f.Scheme == "https" {
    cert := os.Getenv("ETCD_CERTFILE")
    key := os.Getenv("ETCD_KEYFILE")
    caCert := os.Getenv("ETCD_CAFILE")
    client, err := etcd2.NewTLSClient(urls, cert, key, caCert)
    if err != nil {
      log.Fatal("etcd: error creating tls client", err)
    }
    return &EtcdAdapter{client2: client, path: uri.Path}
  }

	res, err := http.Get(urls[0] + "/version")
	if err != nil {
		log.Fatal("etcd: error retrieving version", err)
	}

	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)

	if match, _ := regexp.Match("0\\.4\\.*", body); match == true {
		log.Println("etcd: using v0 client")
		return &EtcdAdapter{client: etcd.NewClient(urls), path: uri.Path}
	}

	return &EtcdAdapter{client2: etcd2.NewClient(urls), path: uri.Path}
}

type EtcdAdapter struct {
	client  *etcd.Client
	client2 *etcd2.Client

	path string
}

func (r *EtcdAdapter) Ping() error {
	r.syncEtcdCluster()

	var err error
	if r.client != nil {
		rr := etcd.NewRawRequest("GET", "version", nil, nil)
		_, err = r.client.SendRequest(rr)
	} else {
		rr := etcd2.NewRawRequest("GET", "version", nil, nil)
		_, err = r.client2.SendRequest(rr)
	}

	if err != nil {
		return err
	}
	return nil
}

func (r *EtcdAdapter) syncEtcdCluster() {
	var result bool
	if r.client != nil {
		result = r.client.SyncCluster()
	} else {
		result = r.client2.SyncCluster()
	}

	if !result {
		log.Println("etcd: sync cluster was unsuccessful")
	}
}

func (r *EtcdAdapter) Register(service *bridge.Service) error {
	r.syncEtcdCluster()

	path := r.path + "/" + service.Name + "/" + service.ID
	port := strconv.Itoa(service.Port)
	addr := net.JoinHostPort(service.IP, port)

	var err error
	if r.client != nil {
		_, err = r.client.Set(path, addr, uint64(service.TTL))
	} else {
		_, err = r.client2.Set(path, addr, uint64(service.TTL))
	}

	if err != nil {
		log.Println("etcd: failed to register service:", err)
	}
	return err
}

func (r *EtcdAdapter) Deregister(service *bridge.Service) error {
	r.syncEtcdCluster()

	path := r.path + "/" + service.Name + "/" + service.ID

	var err error
	if r.client != nil {
		_, err = r.client.Delete(path, false)
	} else {
		_, err = r.client2.Delete(path, false)
	}

	if err != nil {
		log.Println("etcd: failed to deregister service:", err)
	}
	return err
}

func (r *EtcdAdapter) Refresh(service *bridge.Service) error {
	return r.Register(service)
}
<|endoftext|>