<commit_before>/*
Copyright 2013 The Camlistore Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// This file adds the "mount" subcommand to devcam, to run cammount against the dev server.

package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"

	"camlistore.org/pkg/cmdmain"
)

type mountCmd struct {
	// start of flag vars
	altkey bool
	path   string
	port   string
	tls    bool
	debug  bool
	xterm  bool
	// end of flag vars

	env *Env
}

const mountpoint = "/tmp/cammount-dir"

func init() {
	cmdmain.RegisterCommand("mount", func(flags *flag.FlagSet) cmdmain.CommandRunner {
		cmd := &mountCmd{
			env: NewCopyEnv(),
		}
		flags.BoolVar(&cmd.altkey, "altkey", false, "Use different gpg key and password from the server's.")
		flags.BoolVar(&cmd.tls, "tls", false, "Use TLS.")
		flags.StringVar(&cmd.path, "path", "/", "Optional URL prefix path.")
		flags.StringVar(&cmd.port, "port", "3179", "Port camlistore is listening on.")
		flags.BoolVar(&cmd.debug, "debug", false, "print debugging messages.")
		flags.BoolVar(&cmd.xterm, "xterm", false, "Run an xterm in the mounted directory. Shut down when xterm ends.")
		return cmd
	})
}

func (c *mountCmd) Usage() {
	fmt.Fprintf(cmdmain.Stderr, "Usage: devcam mount [mount_opts] [<root-blobref>|<share URL>]\n")
}

func (c *mountCmd) Examples() []string {
	return []string{
		"",
		"http://localhost:3169/share/<blobref>",
	}
}

func (c *mountCmd) Describe() string {
	return "run cammount in dev mode."
}

func tryUnmount(dir string) error {
	if runtime.GOOS == "darwin" {
		return exec.Command("diskutil", "umount", "force", dir).Run()
	}
	return exec.Command("fusermount", "-u", dir).Run()
}

func (c *mountCmd) RunCommand(args []string) error {
	err := c.checkFlags(args)
	if err != nil {
		return cmdmain.UsageError(fmt.Sprint(err))
	}
	if !*noBuild {
		if err := build(filepath.Join("cmd", "cammount")); err != nil {
			return fmt.Errorf("Could not build cammount: %v", err)
		}
	}
	c.env.SetCamdevVars(c.altkey)

	tryUnmount(mountpoint)
	if err := os.Mkdir(mountpoint, 0700); err != nil && !os.IsExist(err) {
		return fmt.Errorf("Could not make mount point: %v", err)
	}

	blobserver := "http://localhost:" + c.port + c.path
	if c.tls {
		blobserver = strings.Replace(blobserver, "http://", "https://", 1)
	}

	cmdBin := filepath.Join("bin", "cammount")
	cmdArgs := []string{
		"-xterm=" + strconv.FormatBool(c.xterm),
		"-debug=" + strconv.FormatBool(c.debug),
		"-server=" + blobserver,
	}
	cmdArgs = append(cmdArgs, args...)
	cmdArgs = append(cmdArgs, mountpoint)
	fmt.Printf("Cammount running with mountpoint %v. Press 'q' <enter> or ctrl-c to shut down.\n", mountpoint)
	return runExec(cmdBin, cmdArgs, c.env)
}

func (c *mountCmd) checkFlags(args []string) error {
	if _, err := strconv.ParseInt(c.port, 0, 0); err != nil {
		return fmt.Errorf("Invalid -port value: %q", c.port)
	}
	return nil
}
<commit_msg>devcam: mount: ditch xterm flag<commit_after>/*
Copyright 2013 The Camlistore Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// This file adds the "mount" subcommand to devcam, to run cammount against the dev server.

package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"

	"camlistore.org/pkg/cmdmain"
)

type mountCmd struct {
	// start of flag vars
	altkey bool
	path   string
	port   string
	tls    bool
	debug  bool
	// end of flag vars

	env *Env
}

const mountpoint = "/tmp/cammount-dir"

func init() {
	cmdmain.RegisterCommand("mount", func(flags *flag.FlagSet) cmdmain.CommandRunner {
		cmd := &mountCmd{
			env: NewCopyEnv(),
		}
		flags.BoolVar(&cmd.altkey, "altkey", false, "Use different gpg key and password from the server's.")
		flags.BoolVar(&cmd.tls, "tls", false, "Use TLS.")
		flags.StringVar(&cmd.path, "path", "/", "Optional URL prefix path.")
		flags.StringVar(&cmd.port, "port", "3179", "Port camlistore is listening on.")
		flags.BoolVar(&cmd.debug, "debug", false, "print debugging messages.")
		return cmd
	})
}

func (c *mountCmd) Usage() {
	fmt.Fprintf(cmdmain.Stderr, "Usage: devcam mount [mount_opts] [<root-blobref>|<share URL>]\n")
}

func (c *mountCmd) Examples() []string {
	return []string{
		"",
		"http://localhost:3169/share/<blobref>",
	}
}

func (c *mountCmd) Describe() string {
	return "run cammount in dev mode."
}

func tryUnmount(dir string) error {
	if runtime.GOOS == "darwin" {
		return exec.Command("diskutil", "umount", "force", dir).Run()
	}
	return exec.Command("fusermount", "-u", dir).Run()
}

func (c *mountCmd) RunCommand(args []string) error {
	err := c.checkFlags(args)
	if err != nil {
		return cmdmain.UsageError(fmt.Sprint(err))
	}
	if !*noBuild {
		if err := build(filepath.Join("cmd", "cammount")); err != nil {
			return fmt.Errorf("Could not build cammount: %v", err)
		}
	}
	c.env.SetCamdevVars(c.altkey)

	tryUnmount(mountpoint)
	if err := os.Mkdir(mountpoint, 0700); err != nil && !os.IsExist(err) {
		return fmt.Errorf("Could not make mount point: %v", err)
	}

	blobserver := "http://localhost:" + c.port + c.path
	if c.tls {
		blobserver = strings.Replace(blobserver, "http://", "https://", 1)
	}

	cmdBin := filepath.Join("bin", "cammount")
	cmdArgs := []string{
		"-debug=" + strconv.FormatBool(c.debug),
		"-server=" + blobserver,
	}
	cmdArgs = append(cmdArgs, args...)
	cmdArgs = append(cmdArgs, mountpoint)
	fmt.Printf("Cammount running with mountpoint %v. Press 'q' <enter> or ctrl-c to shut down.\n", mountpoint)
	return runExec(cmdBin, cmdArgs, c.env)
}

func (c *mountCmd) checkFlags(args []string) error {
	if _, err := strconv.ParseInt(c.port, 0, 0); err != nil {
		return fmt.Errorf("Invalid -port value: %q", c.port)
	}
	return nil
}
<|endoftext|>


<commit_before>// Package docker provides a layer on top of Docker's API via Kite handlers.
package docker

import (
	"errors"
	"fmt"
	"io"
	"os"
	"strconv"
	"time"
	"unicode/utf8"

	"github.com/docker/docker/pkg/promise"
	"github.com/koding/klient/Godeps/_workspace/src/code.google.com/p/go-charset/charset"
	_ "github.com/koding/klient/Godeps/_workspace/src/code.google.com/p/go-charset/data"
	dockerclient "github.com/koding/klient/Godeps/_workspace/src/github.com/fsouza/go-dockerclient"
	"github.com/koding/klient/Godeps/_workspace/src/github.com/koding/kite"
)

// Docker defines the main configuration. One instance is running as one Docker
// client, so multiple instances of a Docker struct can connect to multiple
// Docker Servers.
type Docker struct {
	client *dockerclient.Client
	log    kite.Logger
}

// New connects to a Docker Deamon specified with the given URL. It can be a
// TCP address or a UNIX socket.
func New(url string, log kite.Logger) *Docker {
	// the error is returned only when the passed URL is not parsable via
	// url.Parse, so we can safely neglect it
	client, _ := dockerclient.NewVersionedClient(url, "1.16")
	return &Docker{
		client: client,
		log:    log,
	}
}

// Build builds a new container image from a public Docker path or from a
// given Dockerfile
func (d *Docker) Build(r *kite.Request) (interface{}, error) {
	return nil, errors.New("build is not implemented yet.")
}

// Create creates a new container
func (d *Docker) Create(r *kite.Request) (interface{}, error) {
	var params struct {
		// Custom Container name
		Name string

		// Image name
		Image string
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, err
	}

	if params.Image == "" {
		return nil, errors.New("missing arg: image is empty")
	}

	// generate new name if name is missing
	if params.Name == "" {
		params.Name = r.Username + "-" + strconv.FormatInt(time.Now().UTC().UnixNano(), 10)
	}

	opts := dockerclient.CreateContainerOptions{
		Name: params.Name,
		Config: &dockerclient.Config{
			Image: params.Image,
			Tty:   true,
			// AttachStdin:  true,
			// AttachStdout: true,
			// AttachStderr: true,
		},
	}

	container, err := d.client.CreateContainer(opts)
	if err != nil {
		return nil, err
	}

	return container.Name, nil
}

// Connect connects to an existing Container by spawning a new process and
// attaching to it.
func (d *Docker) Connect(r *kite.Request) (interface{}, error) {
	var params struct {
		// The ID of the container.
		ID string
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, err
	}

	if params.ID == "" {
		return nil, errors.New("missing arg: container ID is empty")
	}

	createOpts := dockerclient.CreateExecOptions{
		// Container: params.ID,
		Container: "high_torvalds",
		Tty:       true,
		Cmd:       []string{"bash"},
		// we attach to anything, it's used in the same was as with `docker
		// exec`
		AttachStdout: true,
		AttachStderr: true,
		AttachStdin:  true,
	}

	// now we create a new Exec instance. It will return us an exec ID which
	// will be used to start the created exec instance
	d.log.Info("Creating exec instance")
	ex, err := d.client.CreateExec(createOpts)
	if err != nil {
		return nil, err
	}
	fmt.Printf("ex = %+v\n", ex)

	opts := dockerclient.StartExecOptions{
		Detach:       false,
		Tty:          true,
		OutputStream: os.Stdout,
		ErrorStream:  os.Stdout,
		InputStream:  os.Stdin,
	}

	errCh := promise.Go(func() error {
		d.log.Info("starting exec instance '%s'", ex.ID)
		return d.client.StartExec(ex.ID, opts)
	})

	d.log.Info("Resizing exec instance '%s'", ex.ID)
	if err := d.client.ResizeExecTTY(ex.ID, 28, 208); err != nil {
		fmt.Printf("resize exec err %+v\n", err)
	}

	fmt.Println("waiting err from errch")
	if err := <-errCh; err != nil {
		fmt.Printf("hijack err = %+v\n", err)
		return nil, err
	}

	return true, nil
}

// Exec connects to an existing Container by spawning a new process and
// attaching to it.
func (d *Docker) Exec(r *kite.Request) (interface{}, error) {
	var params struct {
		// The ID of the container.
		ID string

		Remote       Remote
		Session      string
		SizeX, SizeY int
		Mode         string
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, err
	}

	// if params.ID == "" {
	// 	return nil, errors.New("missing arg: container ID is empty")
	// }

	d.log.Info("params %+v\n", params)

	//////////////////
	imageOpts := dockerclient.CreateContainerOptions{
		Name: "webtest",
		Config: &dockerclient.Config{
			Image:        "redis",
			Tty:          true,
			AttachStdin:  true,
			AttachStdout: true,
			AttachStderr: true,
		},
	}

	container, err := d.client.CreateContainer(imageOpts)
	if err == nil {
		// if successfull start it
		if err := d.client.StartContainer(container.ID, nil); err != nil {
			// return nil, err
			d.log.Error("starting error: %s", err)
		}
	} else {
		d.log.Error("creating error: %s", err)
	}

	//////////////////

	createOpts := dockerclient.CreateExecOptions{
		Container: container.ID,
		Tty:       true,
		Cmd:       []string{"bash"},
		// we attach to anything, it's used in the same was as with `docker
		// exec`
		AttachStdout: true,
		AttachStderr: true,
		AttachStdin:  true,
	}

	// now we create a new Exec instance. It will return us an exec ID which
	// will be used to start the created exec instance
	d.log.Info("Creating exec instance")
	ex, err := d.client.CreateExec(createOpts)
	if err != nil {
		return nil, err
	}

	outReadPipe, outWritePipe := io.Pipe()
	inReadPipe, inWritePipe := io.Pipe()

	opts := dockerclient.StartExecOptions{
		Detach:       false,
		Tty:          true,
		OutputStream: outWritePipe,
		ErrorStream:  outWritePipe,
		InputStream:  inReadPipe,
	}

	masterEncoded, err := charset.NewWriter("ISO-8859-1", inWritePipe)
	if err != nil {
		return nil, err
	}

	errCh := make(chan error)
	closeCh := make(chan bool)

	server := &Server{
		Session:         ex.ID,
		remote:          params.Remote,
		out:             outReadPipe,
		in:              inWritePipe,
		controlSequence: masterEncoded,
		closeChan:       closeCh,
		client:          d.client,
	}

	go func() {
		d.log.Info("Starting exec instance '%s'", ex.ID)
		err := d.client.StartExec(ex.ID, opts)
		errCh <- err
	}()

	// Y is  height, X is width
	err = d.client.ResizeExecTTY(ex.ID, int(params.SizeY), int(params.SizeX))
	if err != nil {
		fmt.Println("error resizing", err)
	}

	go func() {
		select {
		case err := <-errCh:
			if err != nil {
				d.log.Error("startExec error: err")
			}
		case <-closeCh:
			// TODO close hijacker's connection. We need to modify startExec
		}

	}()

	// Read the STDOUT from shell process and send to the connected client.
	go func() {
		buf := make([]byte, (1<<12)-utf8.UTFMax, 1<<12)
		for {
			n, err := server.out.Read(buf)
			for n < cap(buf)-1 {
				r, _ := utf8.DecodeLastRune(buf[:n])
				if r != utf8.RuneError {
					break
				}
				server.out.Read(buf[n : n+1])
				n++
			}

			out := string(filterInvalidUTF8(buf[:n]))
			fmt.Printf("out = %+v\n", out)
			server.remote.Output.Call(string(filterInvalidUTF8(buf[:n])))
			if err != nil {
				break
			}
		}
		d.log.Info("Breaking out of for loop")
	}()

	d.log.Info("Returning server")
	return server, nil
}

// Stop stops a running container
func (d *Docker) Stop(r *kite.Request) (interface{}, error) {
	var params struct {
		// The ID of the container.
		ID string
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, err
	}

	if params.ID == "" {
		return nil, errors.New("missing arg: container is is empty")
	}

	if err := d.client.StopContainer(params.ID, 0); err != nil {
		return nil, err
	}

	return true, nil
}

// Start starts a stopped container
func (d *Docker) Start(r *kite.Request) (interface{}, error) {
	var params struct {
		// The ID of the container.
		ID string
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, err
	}

	if params.ID == "" {
		return nil, errors.New("missing arg: container is is empty")
	}

	if err := d.client.StartContainer(params.ID, nil); err != nil {
		return nil, err
	}

	return true, nil
}

// RemoveContainer removes a container
func (d *Docker) RemoveContainer(r *kite.Request) (interface{}, error) {
	var params struct {
		// The ID of the container.
		ID string

		// A flag that indicates whether Docker should remove the volumes
		// associated to the container.
		RemoveVolumes bool

		// A flag that indicates whether Docker should remove the container
		// even if it is currently running.
		Force bool
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, err
	}

	if params.ID == "" {
		return nil, errors.New("missing arg: container is is empty")
	}

	opts := dockerclient.RemoveContainerOptions{
		ID: params.ID,
	}

	if err := d.client.RemoveContainer(opts); err != nil {
		return nil, err
	}

	return true, nil
}

// RemoveImage removes an existing image.
func (d *Docker) RemoveImage(r *kite.Request) (interface{}, error) {
	// we can remove either by name or by id
	var params struct {
		// Container name
		Name string
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, err
	}

	if params.Name == "" {
		return nil, errors.New("missing arg: name is empty")
	}

	if err := d.client.RemoveImage(params.Name); err != nil {
		return nil, err
	}

	return true, nil
}

// List lists all available containers
func (d *Docker) List(r *kite.Request) (interface{}, error) {
	filter := dockerclient.ListContainersOptions{
		All: true,
	}

	return d.client.ListContainers(filter)
}

func filterInvalidUTF8(buf []byte) []byte {
	i := 0
	j := 0
	for {
		r, l := utf8.DecodeRune(buf[i:])
		if l == 0 {
			break
		}
		if r < 0xD800 {
			if i != j {
				copy(buf[j:], buf[i:i+l])
			}
			j += l
		}
		i += l
	}
	return buf[:j]
}
<commit_msg>docker: set size when output is ready<commit_after>// Package docker provides a layer on top of Docker's API via Kite handlers.
package docker

import (
	"errors"
	"fmt"
	"io"
	"os"
	"strconv"
	"sync"
	"time"
	"unicode/utf8"

	"github.com/docker/docker/pkg/promise"
	"github.com/koding/klient/Godeps/_workspace/src/code.google.com/p/go-charset/charset"
	_ "github.com/koding/klient/Godeps/_workspace/src/code.google.com/p/go-charset/data"
	dockerclient "github.com/koding/klient/Godeps/_workspace/src/github.com/fsouza/go-dockerclient"
	"github.com/koding/klient/Godeps/_workspace/src/github.com/koding/kite"
)

// Docker defines the main configuration. One instance is running as one Docker
// client, so multiple instances of a Docker struct can connect to multiple
// Docker Servers.
type Docker struct {
	client *dockerclient.Client
	log    kite.Logger
}

// New connects to a Docker Deamon specified with the given URL. It can be a
// TCP address or a UNIX socket.
func New(url string, log kite.Logger) *Docker {
	// the error is returned only when the passed URL is not parsable via
	// url.Parse, so we can safely neglect it
	client, _ := dockerclient.NewVersionedClient(url, "1.16")
	return &Docker{
		client: client,
		log:    log,
	}
}

// Build builds a new container image from a public Docker path or from a
// given Dockerfile
func (d *Docker) Build(r *kite.Request) (interface{}, error) {
	return nil, errors.New("build is not implemented yet.")
}

// Create creates a new container
func (d *Docker) Create(r *kite.Request) (interface{}, error) {
	var params struct {
		// Custom Container name
		Name string

		// Image name
		Image string
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, err
	}

	if params.Image == "" {
		return nil, errors.New("missing arg: image is empty")
	}

	// generate new name if name is missing
	if params.Name == "" {
		params.Name = r.Username + "-" + strconv.FormatInt(time.Now().UTC().UnixNano(), 10)
	}

	opts := dockerclient.CreateContainerOptions{
		Name: params.Name,
		Config: &dockerclient.Config{
			Image: params.Image,
			Tty:   true,
			// AttachStdin:  true,
			// AttachStdout: true,
			// AttachStderr: true,
		},
	}

	container, err := d.client.CreateContainer(opts)
	if err != nil {
		return nil, err
	}

	return container.Name, nil
}

// Connect connects to an existing Container by spawning a new process and
// attaching to it.
func (d *Docker) Connect(r *kite.Request) (interface{}, error) {
	var params struct {
		// The ID of the container.
		ID string
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, err
	}

	if params.ID == "" {
		return nil, errors.New("missing arg: container ID is empty")
	}

	createOpts := dockerclient.CreateExecOptions{
		// Container: params.ID,
		Container: "high_torvalds",
		Tty:       true,
		Cmd:       []string{"bash"},
		// we attach to anything, it's used in the same was as with `docker
		// exec`
		AttachStdout: true,
		AttachStderr: true,
		AttachStdin:  true,
	}

	// now we create a new Exec instance. It will return us an exec ID which
	// will be used to start the created exec instance
	d.log.Info("Creating exec instance")
	ex, err := d.client.CreateExec(createOpts)
	if err != nil {
		return nil, err
	}
	fmt.Printf("ex = %+v\n", ex)

	opts := dockerclient.StartExecOptions{
		Detach:       false,
		Tty:          true,
		OutputStream: os.Stdout,
		ErrorStream:  os.Stdout,
		InputStream:  os.Stdin,
	}

	errCh := promise.Go(func() error {
		d.log.Info("starting exec instance '%s'", ex.ID)
		return d.client.StartExec(ex.ID, opts)
	})

	d.log.Info("Resizing exec instance '%s'", ex.ID)
	if err := d.client.ResizeExecTTY(ex.ID, 28, 208); err != nil {
		fmt.Printf("resize exec err %+v\n", err)
	}

	fmt.Println("waiting err from errch")
	if err := <-errCh; err != nil {
		fmt.Printf("hijack err = %+v\n", err)
		return nil, err
	}

	return true, nil
}

// Exec connects to an existing Container by spawning a new process and
// attaching to it.
func (d *Docker) Exec(r *kite.Request) (interface{}, error) {
	var params struct {
		// The ID of the container.
		ID string

		Remote       Remote
		Session      string
		SizeX, SizeY int
		Mode         string
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, err
	}

	// if params.ID == "" {
	// 	return nil, errors.New("missing arg: container ID is empty")
	// }

	d.log.Info("params %+v\n", params)

	//////////////////
	imageOpts := dockerclient.CreateContainerOptions{
		Name: "webtest",
		Config: &dockerclient.Config{
			Image:        "redis",
			Tty:          true,
			AttachStdin:  true,
			AttachStdout: true,
			AttachStderr: true,
		},
	}

	container, err := d.client.CreateContainer(imageOpts)
	if err == nil {
		// if successfull start it
		if err := d.client.StartContainer(container.ID, nil); err != nil {
			// return nil, err
			d.log.Error("starting error: %s", err)
		}
	} else {
		d.log.Error("creating error: %s", err)
	}

	//////////////////

	createOpts := dockerclient.CreateExecOptions{
		Container: container.ID,
		Tty:       true,
		Cmd:       []string{"bash"},
		// we attach to anything, it's used in the same was as with `docker
		// exec`
		AttachStdout: true,
		AttachStderr: true,
		AttachStdin:  true,
	}

	// now we create a new Exec instance. It will return us an exec ID which
	// will be used to start the created exec instance
	d.log.Info("Creating exec instance")
	ex, err := d.client.CreateExec(createOpts)
	if err != nil {
		return nil, err
	}

	outReadPipe, outWritePipe := io.Pipe()
	inReadPipe, inWritePipe := io.Pipe()

	opts := dockerclient.StartExecOptions{
		Detach:       false,
		Tty:          true,
		OutputStream: outWritePipe,
		ErrorStream:  outWritePipe,
		InputStream:  inReadPipe,
	}

	masterEncoded, err := charset.NewWriter("ISO-8859-1", inWritePipe)
	if err != nil {
		return nil, err
	}

	errCh := make(chan error)
	closeCh := make(chan bool)

	server := &Server{
		Session:         ex.ID,
		remote:          params.Remote,
		out:             outReadPipe,
		in:              inWritePipe,
		controlSequence: masterEncoded,
		closeChan:       closeCh,
		client:          d.client,
	}

	go func() {
		d.log.Info("Starting exec instance '%s'", ex.ID)
		err := d.client.StartExec(ex.ID, opts)
		errCh <- err
	}()

	go func() {
		select {
		case err := <-errCh:
			if err != nil {
				d.log.Error("startExec error: err")
			}
		case <-closeCh:
			// TODO close hijacker's connection. We need to modify startExec
		}

	}()

	var once sync.Once

	// Read the STDOUT from shell process and send to the connected client.
	go func() {
		buf := make([]byte, (1<<12)-utf8.UTFMax, 1<<12)
		for {
			n, err := server.out.Read(buf)
			for n < cap(buf)-1 {
				r, _ := utf8.DecodeLastRune(buf[:n])
				if r != utf8.RuneError {
					break
				}
				server.out.Read(buf[n : n+1])
				n++
			}

			// we need to set it for the first time. Because "StartExec" is
			// called in a goroutine and it's blocking there is now way we know
			// when it's ready. Therefore we set the size only once when get an
			// output. After that the client side is setting the TTY size with
			// the Server.SetSize method, that is called everytime the client
			// side sends a size command to us.
			once.Do(func() {
				// Y is  height, X is width
				err = d.client.ResizeExecTTY(ex.ID, int(params.SizeY), int(params.SizeX))
				if err != nil {
					fmt.Println("error resizing", err)
				}
			})

			out := string(filterInvalidUTF8(buf[:n]))
			fmt.Printf("out = %+v\n", out)
			server.remote.Output.Call(string(filterInvalidUTF8(buf[:n])))
			if err != nil {
				break
			}
		}
		d.log.Info("Breaking out of for loop")
	}()

	d.log.Info("Returning server")
	return server, nil
}

// Stop stops a running container
func (d *Docker) Stop(r *kite.Request) (interface{}, error) {
	var params struct {
		// The ID of the container.
		ID string
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, err
	}

	if params.ID == "" {
		return nil, errors.New("missing arg: container is is empty")
	}

	if err := d.client.StopContainer(params.ID, 0); err != nil {
		return nil, err
	}

	return true, nil
}

// Start starts a stopped container
func (d *Docker) Start(r *kite.Request) (interface{}, error) {
	var params struct {
		// The ID of the container.
		ID string
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, err
	}

	if params.ID == "" {
		return nil, errors.New("missing arg: container is is empty")
	}

	if err := d.client.StartContainer(params.ID, nil); err != nil {
		return nil, err
	}

	return true, nil
}

// RemoveContainer removes a container
func (d *Docker) RemoveContainer(r *kite.Request) (interface{}, error) {
	var params struct {
		// The ID of the container.
		ID string

		// A flag that indicates whether Docker should remove the volumes
		// associated to the container.
		RemoveVolumes bool

		// A flag that indicates whether Docker should remove the container
		// even if it is currently running.
		Force bool
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, err
	}

	if params.ID == "" {
		return nil, errors.New("missing arg: container is is empty")
	}

	opts := dockerclient.RemoveContainerOptions{
		ID: params.ID,
	}

	if err := d.client.RemoveContainer(opts); err != nil {
		return nil, err
	}

	return true, nil
}

// RemoveImage removes an existing image.
func (d *Docker) RemoveImage(r *kite.Request) (interface{}, error) {
	// we can remove either by name or by id
	var params struct {
		// Container name
		Name string
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, err
	}

	if params.Name == "" {
		return nil, errors.New("missing arg: name is empty")
	}

	if err := d.client.RemoveImage(params.Name); err != nil {
		return nil, err
	}

	return true, nil
}

// List lists all available containers
func (d *Docker) List(r *kite.Request) (interface{}, error) {
	filter := dockerclient.ListContainersOptions{
		All: true,
	}

	return d.client.ListContainers(filter)
}

func filterInvalidUTF8(buf []byte) []byte {
	i := 0
	j := 0
	for {
		r, l := utf8.DecodeRune(buf[i:])
		if l == 0 {
			break
		}
		if r < 0xD800 {
			if i != j {
				copy(buf[j:], buf[i:i+l])
			}
			j += l
		}
		i += l
	}
	return buf[:j]
}
<|endoftext|>


<commit_before>package utils

import (
	"bytes"
	"crypto/md5"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"strconv"

	e "github.com/techjanitor/pram-post/errors"
)

var nRandBytes = 32

// creates a new random session id with user id
func NewSession(userid uint) (cookieToken string, err error) {

	// convert userid to string
	uid := strconv.Itoa(int(userid))

	// Initialize cache handle
	cache := RedisCache

	// make slice for token, with user + semicolon
	token := make([]byte, nRandBytes+len(uid)+1)
	// copy key into token
	copy(token, []byte(uid))
	// add semicolon
	token[len(uid)] = ';'

	// read in random bytes
	_, err = rand.Read(token[len(uid)+1:])
	if err != nil {
		return
	}

	// goes to redis
	sum := md5.Sum(token)
	storageToken := base64.StdEncoding.EncodeToString(sum[:])

	// user hash is like user:100
	user_key := fmt.Sprintf("user:%d", userid)

	// check to see if session exists already
	result, err := cache.HGet(user_key, "session")
	if err != nil {
		return
	}

	hash_key := fmt.Sprintf("%s", result)

	// delete keys
	err = cache.Delete(user_key, hash_key)
	if err != nil {
		return
	}

	// set in user hash
	err = cache.HMSet(user_key, "session", []byte(storageToken))
	if err != nil {
		return
	}

	// set key in redis
	err = cache.SetEx(storageToken, 2592000, []byte(uid))
	if err != nil {
		return
	}

	// goes in the cookie
	cookieToken = base64.URLEncoding.EncodeToString(token)

	return

}

// validate compares provided session id to redis
func ValidateSession(key []byte) (err error) {

	// Initialize cache handle
	cache := RedisCache

	// decode key
	token, err := base64.URLEncoding.DecodeString(string(key))
	if err != nil {
		return
	}

	// get uid
	index := bytes.IndexByte(token, ';')

	// check to see if user is there
	if index < 0 {
		return e.ErrInvalidSession
	}

	// get given uid
	uid := string(token[:index])

	// hash token
	sum := md5.Sum(token)

	// base64 encode sum
	providedHash := base64.StdEncoding.EncodeToString(sum[:])

	// check for match
	result, err := cache.Get(providedHash)
	if err == ErrCacheMiss {
		return e.ErrInvalidSession
	}
	if err != nil {
		return
	}

	// check if uid matches
	if uid != string(result) {
		return e.ErrInvalidSession
	}

	return

}
<commit_msg>only one session per user<commit_after>package utils

import (
	"bytes"
	"crypto/md5"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"strconv"

	e "github.com/techjanitor/pram-post/errors"
)

var nRandBytes = 32

// creates a new random session id with user id
func NewSession(userid uint) (cookieToken string, err error) {

	// convert userid to string
	uid := strconv.Itoa(int(userid))

	// Initialize cache handle
	cache := RedisCache

	// make slice for token, with user + semicolon
	token := make([]byte, nRandBytes+len(uid)+1)
	// copy key into token
	copy(token, []byte(uid))
	// add semicolon
	token[len(uid)] = ';'

	// read in random bytes
	_, err = rand.Read(token[len(uid)+1:])
	if err != nil {
		return
	}

	// goes to redis
	sum := md5.Sum(token)
	storageToken := base64.StdEncoding.EncodeToString(sum[:])

	// user hash is like user:100
	user_key := fmt.Sprintf("user:%d", userid)

	// check to see if session exists already
	result, err := cache.HGet(user_key, "session")
	if err != ErrCacheMiss {
		return
	}

	hash_key := fmt.Sprintf("%s", result)

	// delete keys
	err = cache.Delete(user_key, hash_key)
	if err != nil {
		return
	}

	// set in user hash
	err = cache.HMSet(user_key, "session", []byte(storageToken))
	if err != nil {
		return
	}

	// set key in redis
	err = cache.SetEx(storageToken, 2592000, []byte(uid))
	if err != nil {
		return
	}

	// goes in the cookie
	cookieToken = base64.URLEncoding.EncodeToString(token)

	return

}

// validate compares provided session id to redis
func ValidateSession(key []byte) (err error) {

	// Initialize cache handle
	cache := RedisCache

	// decode key
	token, err := base64.URLEncoding.DecodeString(string(key))
	if err != nil {
		return
	}

	// get uid
	index := bytes.IndexByte(token, ';')

	// check to see if user is there
	if index < 0 {
		return e.ErrInvalidSession
	}

	// get given uid
	uid := string(token[:index])

	// hash token
	sum := md5.Sum(token)

	// base64 encode sum
	providedHash := base64.StdEncoding.EncodeToString(sum[:])

	// check for match
	result, err := cache.Get(providedHash)
	if err == ErrCacheMiss {
		return e.ErrInvalidSession
	}
	if err != nil {
		return
	}

	// check if uid matches
	if uid != string(result) {
		return e.ErrInvalidSession
	}

	return

}
<|endoftext|>


<commit_before>package installation

import (
	"errors"
	"log"
	"time"

	corev1 "k8s.io/api/core/v1"
	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"

	kubeerrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"

	kubeinformers "k8s.io/client-go/informers"

	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	"k8s.io/client-go/util/retry"
	"k8s.io/client-go/util/workqueue"

	"github.com/kyma-project/kyma/components/installer/pkg/apis/installer/v1alpha1"
	internalClientset "github.com/kyma-project/kyma/components/installer/pkg/client/clientset/versioned"
	internalscheme "github.com/kyma-project/kyma/components/installer/pkg/client/clientset/versioned/scheme"
	informers "github.com/kyma-project/kyma/components/installer/pkg/client/informers/externalversions"
	listers "github.com/kyma-project/kyma/components/installer/pkg/client/listers/installer/v1alpha1"
	"github.com/kyma-project/kyma/components/installer/pkg/conditionmanager"
	"github.com/kyma-project/kyma/components/installer/pkg/consts"
	"github.com/kyma-project/kyma/components/installer/pkg/finalizer"
	"github.com/kyma-project/kyma/components/installer/pkg/overrides"

	"github.com/kyma-project/kyma/components/installer/pkg/config"
	internalerrors "github.com/kyma-project/kyma/components/installer/pkg/errors"
	"github.com/kyma-project/kyma/components/installer/pkg/steps"
)

const (
	// SuccessSynced is used as part of the Event 'reason' when a Installation is synced
	SuccessSynced = "Synced"

	// MessageResourceSynced is the message used for an Event fired when a Installation
	// is synced successfully
	MessageResourceSynced = "Installation synced successfully"
)

// Controller .
type Controller struct {
	kubeClientset      *kubernetes.Clientset
	installationLister listers.InstallationLister
	installationSynced cache.InformerSynced
	queue              workqueue.RateLimitingInterface
	recorder           record.EventRecorder
	errorHandlers      internalerrors.ErrorHandlersInterface
	kymaSteps          *steps.InstallationSteps
	conditionManager   conditionmanager.Interface
	finalizerManager   *finalizer.Manager
	internalClientset  *internalClientset.Clientset
}

// NewController .
func NewController(kubeClientset *kubernetes.Clientset, kubeInformerFactory kubeinformers.SharedInformerFactory,
	internalInformerFactory informers.SharedInformerFactory, installationSteps *steps.InstallationSteps,
	conditionManager conditionmanager.Interface, finalizerManager *finalizer.Manager, internalClientset *internalClientset.Clientset) *Controller {

	installationInformer := internalInformerFactory.Installer().V1alpha1().Installations()

	internalscheme.AddToScheme(scheme.Scheme)

	eventBroadcaster := record.NewBroadcaster()
	eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeClientset.CoreV1().Events("")})
	recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: "kymaInstaller"})

	c := &Controller{
		kubeClientset:      kubeClientset,
		installationLister: installationInformer.Lister(),
		installationSynced: installationInformer.Informer().HasSynced,
		queue:              workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "kymaInstallerQueue"),
		recorder:           recorder,
		errorHandlers:      &internalerrors.ErrorHandlers{},
		kymaSteps:          installationSteps,
		conditionManager:   conditionManager,
		finalizerManager:   finalizerManager,
		internalClientset:  internalClientset,
	}

	installationInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: c.enqueueInstallation,
		UpdateFunc: func(old, new interface{}) {
			c.enqueueInstallation(new)
		},
	})

	return c
}

// Run .
func (c *Controller) Run(workers int, stopCh <-chan struct{}) {

	defer func() {

		log.Println("Shutting down controller...")
		c.queue.ShutDown()
	}()

	for i := 0; i < workers; i++ {
		// start workers
		go wait.Until(c.worker, time.Second, stopCh)
	}

	// wait until we receive a stop signal
	<-stopCh
}

func (c *Controller) worker() {

	// process until we're told to stop
	for c.processNextWorkItem() {
	}
}

func (c *Controller) processNextWorkItem() bool {

	key, quit := c.queue.Get()
	if quit {

		return false
	}

	defer c.queue.Done(key)

	err := c.syncHandler(key.(string))
	c.handleErr(err, key)
	return true
}

func (c *Controller) syncHandler(key string) error {

	namespace, name, err := cache.SplitMetaNamespaceKey(key)
	if err != nil {
		return err
	}

	installation, err := c.installationLister.Installations(namespace).Get(name)
	if err != nil {

		if kubeerrors.IsNotFound(err) {
			runtime.HandleError(err)
			return nil
		}

		return err
	}

	//Handle Delete
	if installation.IsBeingDeleted() {
		if installation.CanBeDeleted() {
			log.Println("Delete of Installation CR was requested, removing finalizer...")
			err := c.deleteFinalizer(installation)

			if c.errorHandlers.CheckError("Error while removing finalizer", err) {
				return err
			}

			return nil
		} else {
			log.Println("Delete of Installation CR was requested but it's status does not allow for it - ignoring the request")
		}
	}

	//TODO: Fill it with proper data and install UpdateStatus func
	installationData, err := config.NewInstallationData(installation)
	if c.errorHandlers.CheckError("Error while building installation data: ", err) {
		return err
	}

	overrideProvider, overridesErr := overrides.New(c.kubeClientset)
	if overridesErr != nil {
		return overridesErr
	}

	domainName, exists := overrides.FindOverrideStringValue(overrideProvider.Common(), "global.domainName")
	if !exists || domainName == "" {
		runtime.HandleError(errors.New("'global.domainName' override not found"))
		return nil
	}

	if installation.ShouldInstall() {

		err = c.conditionManager.InstallStart()
		if err != nil {
			return err
		}

		err = c.kymaSteps.InstallKyma(installationData, overrideProvider, consts.InstallAction)
		if err != nil {
			c.conditionManager.InstallError()

			return err
		}

		err = c.conditionManager.InstallSuccess()
		if err != nil {
			return err
		}
	} else if installation.ShouldUninstall() {
		err = c.conditionManager.UninstallStart()
		if err != nil {
			return err
		}

		err = c.kymaSteps.UninstallKyma(installationData)
		if err != nil {
			c.conditionManager.UninstallError()

			return err
		}

		err = c.conditionManager.UninstallSuccess()
		if err != nil {
			return err
		}
	} else if installation.ShouldUpdate() {

		err = c.conditionManager.UpdateStart()
		if err != nil {
			return err
		}

		err = c.kymaSteps.InstallKyma(installationData, overrideProvider, consts.UpgradeAction)
		if err != nil {
			c.conditionManager.UpdateError()

			return err
		}

		err = c.conditionManager.UpdateSuccess()
		if err != nil {
			return err
		}
	}

	c.recorder.Event(installation, corev1.EventTypeNormal, SuccessSynced, MessageResourceSynced)
	return nil
}

func (c *Controller) deleteFinalizer(installation *v1alpha1.Installation) error {
	if !c.finalizerManager.HasFinalizer(installation) {
		return nil
	}

	retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
		instObj, getErr := c.installationLister.Installations(installation.Namespace).Get(installation.Name)

		if getErr != nil {
			return getErr
		}

		installationCopy := instObj.DeepCopy()

		c.finalizerManager.RemoveFinalizer(installationCopy)
		_, updateErr := c.internalClientset.InstallerV1alpha1().Installations(installation.Namespace).Update(installationCopy)
		return updateErr
	})

	if retryErr != nil {
		return retryErr
	}

	return nil
}

func (c *Controller) handleErr(err error, key interface{}) {

	if err == nil {
		c.queue.Forget(key)
		return
	}

	if c.queue.NumRequeues(key) < 5 {

		// Re-enqueue the key rate limited. Based on the rate limiter on the
		// queue and the re-enqueue history, the key will be processed later again.
		c.queue.AddRateLimited(key)
		return
	}

	c.queue.Forget(key)

	runtime.HandleError(err)
}

func (c *Controller) enqueueInstallation(obj interface{}) {

	var key string
	var err error
	if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil {
		runtime.HandleError(err)
		return
	}
	c.queue.AddRateLimited(key)
}
<commit_msg>Remove hardcoded override from installer (#771)<commit_after>package installation

import (
	"log"
	"time"

	corev1 "k8s.io/api/core/v1"
	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"

	kubeerrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"

	kubeinformers "k8s.io/client-go/informers"

	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	"k8s.io/client-go/util/retry"
	"k8s.io/client-go/util/workqueue"

	"github.com/kyma-project/kyma/components/installer/pkg/apis/installer/v1alpha1"
	internalClientset "github.com/kyma-project/kyma/components/installer/pkg/client/clientset/versioned"
	internalscheme "github.com/kyma-project/kyma/components/installer/pkg/client/clientset/versioned/scheme"
	informers "github.com/kyma-project/kyma/components/installer/pkg/client/informers/externalversions"
	listers "github.com/kyma-project/kyma/components/installer/pkg/client/listers/installer/v1alpha1"
	"github.com/kyma-project/kyma/components/installer/pkg/conditionmanager"
	"github.com/kyma-project/kyma/components/installer/pkg/consts"
	"github.com/kyma-project/kyma/components/installer/pkg/finalizer"
	"github.com/kyma-project/kyma/components/installer/pkg/overrides"

	"github.com/kyma-project/kyma/components/installer/pkg/config"
	internalerrors "github.com/kyma-project/kyma/components/installer/pkg/errors"
	"github.com/kyma-project/kyma/components/installer/pkg/steps"
)

const (
	// SuccessSynced is used as part of the Event 'reason' when a Installation is synced
	SuccessSynced = "Synced"

	// MessageResourceSynced is the message used for an Event fired when a Installation
	// is synced successfully
	MessageResourceSynced = "Installation synced successfully"
)

// Controller .
type Controller struct {
	kubeClientset      *kubernetes.Clientset
	installationLister listers.InstallationLister
	installationSynced cache.InformerSynced
	queue              workqueue.RateLimitingInterface
	recorder           record.EventRecorder
	errorHandlers      internalerrors.ErrorHandlersInterface
	kymaSteps          *steps.InstallationSteps
	conditionManager   conditionmanager.Interface
	finalizerManager   *finalizer.Manager
	internalClientset  *internalClientset.Clientset
}

// NewController .
func NewController(kubeClientset *kubernetes.Clientset, kubeInformerFactory kubeinformers.SharedInformerFactory,
	internalInformerFactory informers.SharedInformerFactory, installationSteps *steps.InstallationSteps,
	conditionManager conditionmanager.Interface, finalizerManager *finalizer.Manager, internalClientset *internalClientset.Clientset) *Controller {

	installationInformer := internalInformerFactory.Installer().V1alpha1().Installations()

	internalscheme.AddToScheme(scheme.Scheme)

	eventBroadcaster := record.NewBroadcaster()
	eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeClientset.CoreV1().Events("")})
	recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: "kymaInstaller"})

	c := &Controller{
		kubeClientset:      kubeClientset,
		installationLister: installationInformer.Lister(),
		installationSynced: installationInformer.Informer().HasSynced,
		queue:              workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "kymaInstallerQueue"),
		recorder:           recorder,
		errorHandlers:      &internalerrors.ErrorHandlers{},
		kymaSteps:          installationSteps,
		conditionManager:   conditionManager,
		finalizerManager:   finalizerManager,
		internalClientset:  internalClientset,
	}

	installationInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: c.enqueueInstallation,
		UpdateFunc: func(old, new interface{}) {
			c.enqueueInstallation(new)
		},
	})

	return c
}

// Run .
func (c *Controller) Run(workers int, stopCh <-chan struct{}) {

	defer func() {

		log.Println("Shutting down controller...")
		c.queue.ShutDown()
	}()

	for i := 0; i < workers; i++ {
		// start workers
		go wait.Until(c.worker, time.Second, stopCh)
	}

	// wait until we receive a stop signal
	<-stopCh
}

func (c *Controller) worker() {

	// process until we're told to stop
	for c.processNextWorkItem() {
	}
}

func (c *Controller) processNextWorkItem() bool {

	key, quit := c.queue.Get()
	if quit {

		return false
	}

	defer c.queue.Done(key)

	err := c.syncHandler(key.(string))
	c.handleErr(err, key)
	return true
}

func (c *Controller) syncHandler(key string) error {

	namespace, name, err := cache.SplitMetaNamespaceKey(key)
	if err != nil {
		return err
	}

	installation, err := c.installationLister.Installations(namespace).Get(name)
	if err != nil {

		if kubeerrors.IsNotFound(err) {
			runtime.HandleError(err)
			return nil
		}

		return err
	}

	//Handle Delete
	if installation.IsBeingDeleted() {
		if installation.CanBeDeleted() {
			log.Println("Delete of Installation CR was requested, removing finalizer...")
			err := c.deleteFinalizer(installation)

			if c.errorHandlers.CheckError("Error while removing finalizer", err) {
				return err
			}

			return nil
		} else {
			log.Println("Delete of Installation CR was requested but it's status does not allow for it - ignoring the request")
		}
	}

	//TODO: Fill it with proper data and install UpdateStatus func
	installationData, err := config.NewInstallationData(installation)
	if c.errorHandlers.CheckError("Error while building installation data: ", err) {
		return err
	}

	overrideProvider, overridesErr := overrides.New(c.kubeClientset)
	if overridesErr != nil {
		return overridesErr
	}

	if installation.ShouldInstall() {

		err = c.conditionManager.InstallStart()
		if err != nil {
			return err
		}

		err = c.kymaSteps.InstallKyma(installationData, overrideProvider, consts.InstallAction)
		if err != nil {
			c.conditionManager.InstallError()

			return err
		}

		err = c.conditionManager.InstallSuccess()
		if err != nil {
			return err
		}
	} else if installation.ShouldUninstall() {
		err = c.conditionManager.UninstallStart()
		if err != nil {
			return err
		}

		err = c.kymaSteps.UninstallKyma(installationData)
		if err != nil {
			c.conditionManager.UninstallError()

			return err
		}

		err = c.conditionManager.UninstallSuccess()
		if err != nil {
			return err
		}
	} else if installation.ShouldUpdate() {

		err = c.conditionManager.UpdateStart()
		if err != nil {
			return err
		}

		err = c.kymaSteps.InstallKyma(installationData, overrideProvider, consts.UpgradeAction)
		if err != nil {
			c.conditionManager.UpdateError()

			return err
		}

		err = c.conditionManager.UpdateSuccess()
		if err != nil {
			return err
		}
	}

	c.recorder.Event(installation, corev1.EventTypeNormal, SuccessSynced, MessageResourceSynced)
	return nil
}

func (c *Controller) deleteFinalizer(installation *v1alpha1.Installation) error {
	if !c.finalizerManager.HasFinalizer(installation) {
		return nil
	}

	retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
		instObj, getErr := c.installationLister.Installations(installation.Namespace).Get(installation.Name)

		if getErr != nil {
			return getErr
		}

		installationCopy := instObj.DeepCopy()

		c.finalizerManager.RemoveFinalizer(installationCopy)
		_, updateErr := c.internalClientset.InstallerV1alpha1().Installations(installation.Namespace).Update(installationCopy)
		return updateErr
	})

	if retryErr != nil {
		return retryErr
	}

	return nil
}

func (c *Controller) handleErr(err error, key interface{}) {

	if err == nil {
		c.queue.Forget(key)
		return
	}

	if c.queue.NumRequeues(key) < 5 {

		// Re-enqueue the key rate limited. Based on the rate limiter on the
		// queue and the re-enqueue history, the key will be processed later again.
		c.queue.AddRateLimited(key)
		return
	}

	c.queue.Forget(key)

	runtime.HandleError(err)
}

func (c *Controller) enqueueInstallation(obj interface{}) {

	var key string
	var err error
	if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil {
		runtime.HandleError(err)
		return
	}
	c.queue.AddRateLimited(key)
}
<|endoftext|>


<commit_before>package overlay

import (
	"fmt"
	"net"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/docker/libkv/store"
	"github.com/docker/libnetwork/datastore"
	"github.com/docker/libnetwork/driverapi"
	"github.com/docker/libnetwork/idm"
	"github.com/docker/libnetwork/netlabel"
	"github.com/hashicorp/serf/serf"
)

const (
	networkType  = "overlay"
	vethPrefix   = "veth"
	vethLen      = 7
	vxlanIDStart = 256
	vxlanIDEnd   = 1000
	vxlanPort    = 4789
	vxlanVethMTU = 1450
)

type driver struct {
	eventCh      chan serf.Event
	notifyCh     chan ovNotify
	exitCh       chan chan struct{}
	bindAddress  string
	neighIP      string
	config       map[string]interface{}
	peerDb       peerNetworkMap
	serfInstance *serf.Serf
	networks     networkTable
	store        datastore.DataStore
	ipAllocator  *idm.Idm
	vxlanIdm     *idm.Idm
	once         sync.Once
	joinOnce     sync.Once
	sync.Mutex
}

// Init registers a new instance of overlay driver
func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
	c := driverapi.Capability{
		DataScope: datastore.GlobalScope,
	}

	d := &driver{
		networks: networkTable{},
		peerDb: peerNetworkMap{
			mp: map[string]*peerMap{},
		},
		config: config,
	}

	return dc.RegisterDriver(networkType, d, c)
}

// Fini cleans up the driver resources
func Fini(drv driverapi.Driver) {
	d := drv.(*driver)

	if d.exitCh != nil {
		waitCh := make(chan struct{})

		d.exitCh <- waitCh

		<-waitCh
	}
}

func (d *driver) configure() error {
	var err error

	if len(d.config) == 0 {
		return nil
	}

	d.once.Do(func() {
		provider, provOk := d.config[netlabel.GlobalKVProvider]
		provURL, urlOk := d.config[netlabel.GlobalKVProviderURL]

		if provOk && urlOk {
			cfg := &datastore.ScopeCfg{
				Client: datastore.ScopeClientCfg{
					Provider: provider.(string),
					Address:  provURL.(string),
				},
			}
			provConfig, confOk := d.config[netlabel.GlobalKVProviderConfig]
			if confOk {
				cfg.Client.Config = provConfig.(*store.Config)
			}
			d.store, err = datastore.NewDataStore(datastore.GlobalScope, cfg)
			if err != nil {
				err = fmt.Errorf("failed to initialize data store: %v", err)
				return
			}
		}

		d.vxlanIdm, err = idm.New(d.store, "vxlan-id", vxlanIDStart, vxlanIDEnd)
		if err != nil {
			err = fmt.Errorf("failed to initialize vxlan id manager: %v", err)
			return
		}

		d.ipAllocator, err = idm.New(d.store, "ipam-id", 1, 0xFFFF-2)
		if err != nil {
			err = fmt.Errorf("failed to initalize ipam id manager: %v", err)
			return
		}
	})

	return err
}

func (d *driver) Type() string {
	return networkType
}

func validateSelf(node string) error {
	advIP := net.ParseIP(node)
	if advIP == nil {
		return fmt.Errorf("invalid self address (%s)", node)
	}

	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return fmt.Errorf("Unable to get interface addresses %v", err)
	}
	for _, addr := range addrs {
		ip, _, err := net.ParseCIDR(addr.String())
		if err == nil && ip.Equal(advIP) {
			return nil
		}
	}
	return fmt.Errorf("Multi-Host overlay networking requires cluster-advertise(%s) to be configured with a local ip-address that is reachable within the cluster", advIP.String())
}

func (d *driver) nodeJoin(node string, self bool) {
	if self && !d.isSerfAlive() {
		if err := validateSelf(node); err != nil {
			logrus.Errorf("%s", err.Error())
		}
		d.Lock()
		d.bindAddress = node
		d.Unlock()
		err := d.serfInit()
		if err != nil {
			logrus.Errorf("initializing serf instance failed: %v", err)
			return
		}
	}

	d.Lock()
	if !self {
		d.neighIP = node
	}
	neighIP := d.neighIP
	d.Unlock()

	if d.serfInstance != nil && neighIP != "" {
		var err error
		d.joinOnce.Do(func() {
			err = d.serfJoin(neighIP)
			if err == nil {
				d.pushLocalDb()
			}
		})
		if err != nil {
			logrus.Errorf("joining serf neighbor %s failed: %v", node, err)
			d.Lock()
			d.joinOnce = sync.Once{}
			d.Unlock()
			return
		}
	}
}

func (d *driver) pushLocalEndpointEvent(action, nid, eid string) {
	if !d.isSerfAlive() {
		return
	}
	d.notifyCh <- ovNotify{
		action: "join",
		nid:    nid,
		eid:    eid,
	}
}

// DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster
func (d *driver) DiscoverNew(dType driverapi.DiscoveryType, data interface{}) error {
	if dType == driverapi.NodeDiscovery {
		nodeData, ok := data.(driverapi.NodeDiscoveryData)
		if !ok || nodeData.Address == "" {
			return fmt.Errorf("invalid discovery data")
		}
		d.nodeJoin(nodeData.Address, nodeData.Self)
	}
	return nil
}

// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster
func (d *driver) DiscoverDelete(dType driverapi.DiscoveryType, data interface{}) error {
	return nil
}
<commit_msg>Remove overlay's ipAllocator<commit_after>package overlay

import (
	"fmt"
	"net"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/docker/libkv/store"
	"github.com/docker/libnetwork/datastore"
	"github.com/docker/libnetwork/driverapi"
	"github.com/docker/libnetwork/idm"
	"github.com/docker/libnetwork/netlabel"
	"github.com/hashicorp/serf/serf"
)

const (
	networkType  = "overlay"
	vethPrefix   = "veth"
	vethLen      = 7
	vxlanIDStart = 256
	vxlanIDEnd   = 1000
	vxlanPort    = 4789
	vxlanVethMTU = 1450
)

type driver struct {
	eventCh      chan serf.Event
	notifyCh     chan ovNotify
	exitCh       chan chan struct{}
	bindAddress  string
	neighIP      string
	config       map[string]interface{}
	peerDb       peerNetworkMap
	serfInstance *serf.Serf
	networks     networkTable
	store        datastore.DataStore
	vxlanIdm     *idm.Idm
	once         sync.Once
	joinOnce     sync.Once
	sync.Mutex
}

// Init registers a new instance of overlay driver
func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
	c := driverapi.Capability{
		DataScope: datastore.GlobalScope,
	}

	d := &driver{
		networks: networkTable{},
		peerDb: peerNetworkMap{
			mp: map[string]*peerMap{},
		},
		config: config,
	}

	return dc.RegisterDriver(networkType, d, c)
}

// Fini cleans up the driver resources
func Fini(drv driverapi.Driver) {
	d := drv.(*driver)

	if d.exitCh != nil {
		waitCh := make(chan struct{})

		d.exitCh <- waitCh

		<-waitCh
	}
}

func (d *driver) configure() error {
	var err error

	if len(d.config) == 0 {
		return nil
	}

	d.once.Do(func() {
		provider, provOk := d.config[netlabel.GlobalKVProvider]
		provURL, urlOk := d.config[netlabel.GlobalKVProviderURL]

		if provOk && urlOk {
			cfg := &datastore.ScopeCfg{
				Client: datastore.ScopeClientCfg{
					Provider: provider.(string),
					Address:  provURL.(string),
				},
			}
			provConfig, confOk := d.config[netlabel.GlobalKVProviderConfig]
			if confOk {
				cfg.Client.Config = provConfig.(*store.Config)
			}
			d.store, err = datastore.NewDataStore(datastore.GlobalScope, cfg)
			if err != nil {
				err = fmt.Errorf("failed to initialize data store: %v", err)
				return
			}
		}

		d.vxlanIdm, err = idm.New(d.store, "vxlan-id", vxlanIDStart, vxlanIDEnd)
		if err != nil {
			err = fmt.Errorf("failed to initialize vxlan id manager: %v", err)
			return
		}
	})

	return err
}

func (d *driver) Type() string {
	return networkType
}

func validateSelf(node string) error {
	advIP := net.ParseIP(node)
	if advIP == nil {
		return fmt.Errorf("invalid self address (%s)", node)
	}

	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return fmt.Errorf("Unable to get interface addresses %v", err)
	}
	for _, addr := range addrs {
		ip, _, err := net.ParseCIDR(addr.String())
		if err == nil && ip.Equal(advIP) {
			return nil
		}
	}
	return fmt.Errorf("Multi-Host overlay networking requires cluster-advertise(%s) to be configured with a local ip-address that is reachable within the cluster", advIP.String())
}

func (d *driver) nodeJoin(node string, self bool) {
	if self && !d.isSerfAlive() {
		if err := validateSelf(node); err != nil {
			logrus.Errorf("%s", err.Error())
		}
		d.Lock()
		d.bindAddress = node
		d.Unlock()
		err := d.serfInit()
		if err != nil {
			logrus.Errorf("initializing serf instance failed: %v", err)
			return
		}
	}

	d.Lock()
	if !self {
		d.neighIP = node
	}
	neighIP := d.neighIP
	d.Unlock()

	if d.serfInstance != nil && neighIP != "" {
		var err error
		d.joinOnce.Do(func() {
			err = d.serfJoin(neighIP)
			if err == nil {
				d.pushLocalDb()
			}
		})
		if err != nil {
			logrus.Errorf("joining serf neighbor %s failed: %v", node, err)
			d.Lock()
			d.joinOnce = sync.Once{}
			d.Unlock()
			return
		}
	}
}

func (d *driver) pushLocalEndpointEvent(action, nid, eid string) {
	if !d.isSerfAlive() {
		return
	}
	d.notifyCh <- ovNotify{
		action: "join",
		nid:    nid,
		eid:    eid,
	}
}

// DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster
func (d *driver) DiscoverNew(dType driverapi.DiscoveryType, data interface{}) error {
	if dType == driverapi.NodeDiscovery {
		nodeData, ok := data.(driverapi.NodeDiscoveryData)
		if !ok || nodeData.Address == "" {
			return fmt.Errorf("invalid discovery data")
		}
		d.nodeJoin(nodeData.Address, nodeData.Self)
	}
	return nil
}

// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster
func (d *driver) DiscoverDelete(dType driverapi.DiscoveryType, data interface{}) error {
	return nil
}
<|endoftext|>


<commit_before>package volume

import (
	"fmt"
	"strings"
	"syscall"

	"golang.org/x/sys/windows"

	"github.com/gentlemanautomaton/volmgmt/guidconv"
	"github.com/gentlemanautomaton/volmgmt/hsync"
	"github.com/gentlemanautomaton/volmgmt/mountapi"
	"github.com/gentlemanautomaton/volmgmt/storageapi"
	"github.com/gentlemanautomaton/volmgmt/usn"
	"github.com/gentlemanautomaton/volmgmt/volumeapi"
)

/*
type Volume interface {
	Name() (string, error)
	Paths() ([]string, error)
	Handle() syscall.Handle
	Close() error
}
*/

// Volume represents a storage volume. It must be created with a call to New.
type Volume struct {
	h          *hsync.Handle
	devnum     storageapi.DeviceNumber
	descriptor storageapi.DeviceDescriptor
}

// New returns a volume representing the volume of the given path, which must be in one of the
// following formats:
//
//  \\.\X:
//  \\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\
//  \\.\PhysicalDrive0
//
// The returned volume will wrap a system handle and will consume system
// resources until the volume is closed. It is the caller's responsibility to
// close the volume when finished with it.
func New(path string) (*Volume, error) {
	const (
		// access = 0
		// access = syscall.GENERIC_READ | syscall.GENERIC_WRITE

		access = syscall.GENERIC_READ
		mode   = syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE
	)

	h, err := volumeapi.Handle(path, access, mode)
	if err != nil {
		return nil, err
	}

	v := &Volume{
		h: hsync.New(h),
	}

	err = v.init()
	if err != nil {
		v.Close()
		return nil, err
	}

	return v, nil
}

func (v *Volume) init() error {
	var err error

	// Query and store the volume's device number
	v.devnum, err = storageapi.GetDeviceNumber(v.h.Handle())
	if err != nil {
		return err
	}

	// Query and store the volume's device descriptor
	v.descriptor, err = storageapi.QueryDeviceDescriptor(v.h.Handle())
	if err != nil {
		return err
	}

	return nil
}

// Label returns the label of the volume.
func (v *Volume) Label() (string, error) {
	label, _, _, _, _, err := volumeapi.GetVolumeInformationByHandle(v.h.Handle())
	return label, err
}

// Name returns the volume GUID name.
//
// BUG: This currently only works for device drivers that supply a stable GUID.
func (v *Volume) Name() (string, error) {
	guid, err := v.GUID()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf(`\\?\Volume%s\`, strings.ToLower(guidconv.Format(&guid))), nil
}

// GUID returns a GUID for the volume that is supplied by the mount manager.
//
// If the underlying device driver supplies a stable GUID, the mount manager
// will use and return that value.
//
// BUG: This currently only works for device drivers that supply a stable GUID.
func (v *Volume) GUID() (guid windows.GUID, err error) {
	guid, err = v.StableGUID()
	if err == nil {
		return
	}
	// TODO: Query the mount manager
	return
}

// StableGUID returns a stable GUID for the volume that is supplied by its
// device driver.
//
// Not all device drivers are capable of supplying a stable GUID. If this
// device doesn't supply one an error will be returned. In such cases the
// mount manager will generate a GUID for the volume, which can be accessed
// via v.GUID().
func (v *Volume) StableGUID() (guid windows.GUID, err error) {
	guid, err = mountapi.QueryStableGUID(v.h.Handle())
	if err != nil {
		err = fmt.Errorf("unable to retrieve stable GUID: %v", err)
	}
	return
}

// DeviceNumber returns the physical device number of the volume.
func (v *Volume) DeviceNumber() uint32 {
	return v.devnum.DeviceNumber
}

// PartitionNumber returns the partition number of the volume.
func (v *Volume) PartitionNumber() int32 {
	return v.devnum.PartitionNumber
}

// DeviceType returns the type of device represented by the volume.
func (v *Volume) DeviceType() uint16 {
	return v.devnum.DeviceType
}

// BusType returns the hardware bus type of the volume.
func (v *Volume) BusType() uint32 {
	return v.descriptor.BusType
}

// RemovableMedia returns true if the volume is located on removable media.
func (v *Volume) RemovableMedia() bool {
	return v.descriptor.RemovableMedia
}

// VendorID returns the hardware vendor ID of the volume.
func (v *Volume) VendorID() string {
	return v.descriptor.VendorID
}

// ProductID returns the hardware product ID of the volume.
func (v *Volume) ProductID() string {
	return v.descriptor.ProductID
}

// ProductRevision returns the hardware product revision of the volume.
func (v *Volume) ProductRevision() string {
	return v.descriptor.ProductRevision
}

// SerialNumber returns the hardware serial number of the volume.
func (v *Volume) SerialNumber() string {
	return v.descriptor.SerialNumber
}

// DeviceID returns the device ID of the volume.
func (v *Volume) DeviceID() ([]byte, error) {
	return mountapi.QueryUniqueID(v.h.Handle())
}

// DevicePath returns an NT namespace device path for the volume.
func (v *Volume) DevicePath() (string, error) {
	return mountapi.QueryDeviceName(v.h.Handle())
}

// Paths returns all of the volume's mount points.
func (v *Volume) Paths() ([]string, error) {
	// TODO: Consider using IOCTL_MOUNTMGR_QUERY_POINTS instead
	name, err := v.Name()
	if err != nil {
		return nil, err
	}

	return volumeapi.GetVolumePathNamesForVolumeName(name)
}

// Handle returns the system handle of the volume.
func (v *Volume) Handle() syscall.Handle {
	return v.h.Handle()
}

// Journal returns a change journal accessor for the volume.
func (v *Volume) Journal() *usn.Journal {
	return usn.NewJournalWithHandle(v.h.Clone())
}

// Close releases any resources consumed by the volume.
func (v *Volume) Close() error {
	return v.h.Close()
}
<commit_msg>volume: Added MFT() method to access the Master File Table for a volume<commit_after>package volume

import (
	"fmt"
	"strings"
	"syscall"

	"golang.org/x/sys/windows"

	"github.com/gentlemanautomaton/volmgmt/guidconv"
	"github.com/gentlemanautomaton/volmgmt/hsync"
	"github.com/gentlemanautomaton/volmgmt/mountapi"
	"github.com/gentlemanautomaton/volmgmt/storageapi"
	"github.com/gentlemanautomaton/volmgmt/usn"
	"github.com/gentlemanautomaton/volmgmt/volumeapi"
)

/*
type Volume interface {
	Name() (string, error)
	Paths() ([]string, error)
	Handle() syscall.Handle
	Close() error
}
*/

// Volume represents a storage volume. It must be created with a call to New.
type Volume struct {
	h          *hsync.Handle
	devnum     storageapi.DeviceNumber
	descriptor storageapi.DeviceDescriptor
}

// New returns a volume representing the volume of the given path, which must be in one of the
// following formats:
//
//  \\.\X:
//  \\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\
//  \\.\PhysicalDrive0
//
// The returned volume will wrap a system handle and will consume system
// resources until the volume is closed. It is the caller's responsibility to
// close the volume when finished with it.
func New(path string) (*Volume, error) {
	const (
		// access = 0
		// access = syscall.GENERIC_READ | syscall.GENERIC_WRITE

		access = syscall.GENERIC_READ
		mode   = syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE
	)

	h, err := volumeapi.Handle(path, access, mode)
	if err != nil {
		return nil, err
	}

	v := &Volume{
		h: hsync.New(h),
	}

	err = v.init()
	if err != nil {
		v.Close()
		return nil, err
	}

	return v, nil
}

func (v *Volume) init() error {
	var err error

	// Query and store the volume's device number
	v.devnum, err = storageapi.GetDeviceNumber(v.h.Handle())
	if err != nil {
		return err
	}

	// Query and store the volume's device descriptor
	v.descriptor, err = storageapi.QueryDeviceDescriptor(v.h.Handle())
	if err != nil {
		return err
	}

	return nil
}

// Label returns the label of the volume.
func (v *Volume) Label() (string, error) {
	label, _, _, _, _, err := volumeapi.GetVolumeInformationByHandle(v.h.Handle())
	return label, err
}

// Name returns the volume GUID name.
//
// BUG: This currently only works for device drivers that supply a stable GUID.
func (v *Volume) Name() (string, error) {
	guid, err := v.GUID()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf(`\\?\Volume%s\`, strings.ToLower(guidconv.Format(&guid))), nil
}

// GUID returns a GUID for the volume that is supplied by the mount manager.
//
// If the underlying device driver supplies a stable GUID, the mount manager
// will use and return that value.
//
// BUG: This currently only works for device drivers that supply a stable GUID.
func (v *Volume) GUID() (guid windows.GUID, err error) {
	guid, err = v.StableGUID()
	if err == nil {
		return
	}
	// TODO: Query the mount manager
	return
}

// StableGUID returns a stable GUID for the volume that is supplied by its
// device driver.
//
// Not all device drivers are capable of supplying a stable GUID. If this
// device doesn't supply one an error will be returned. In such cases the
// mount manager will generate a GUID for the volume, which can be accessed
// via v.GUID().
func (v *Volume) StableGUID() (guid windows.GUID, err error) {
	guid, err = mountapi.QueryStableGUID(v.h.Handle())
	if err != nil {
		err = fmt.Errorf("unable to retrieve stable GUID: %v", err)
	}
	return
}

// DeviceNumber returns the physical device number of the volume.
func (v *Volume) DeviceNumber() uint32 {
	return v.devnum.DeviceNumber
}

// PartitionNumber returns the partition number of the volume.
func (v *Volume) PartitionNumber() int32 {
	return v.devnum.PartitionNumber
}

// DeviceType returns the type of device represented by the volume.
func (v *Volume) DeviceType() uint16 {
	return v.devnum.DeviceType
}

// BusType returns the hardware bus type of the volume.
func (v *Volume) BusType() uint32 {
	return v.descriptor.BusType
}

// RemovableMedia returns true if the volume is located on removable media.
func (v *Volume) RemovableMedia() bool {
	return v.descriptor.RemovableMedia
}

// VendorID returns the hardware vendor ID of the volume.
func (v *Volume) VendorID() string {
	return v.descriptor.VendorID
}

// ProductID returns the hardware product ID of the volume.
func (v *Volume) ProductID() string {
	return v.descriptor.ProductID
}

// ProductRevision returns the hardware product revision of the volume.
func (v *Volume) ProductRevision() string {
	return v.descriptor.ProductRevision
}

// SerialNumber returns the hardware serial number of the volume.
func (v *Volume) SerialNumber() string {
	return v.descriptor.SerialNumber
}

// DeviceID returns the device ID of the volume.
func (v *Volume) DeviceID() ([]byte, error) {
	return mountapi.QueryUniqueID(v.h.Handle())
}

// DevicePath returns an NT namespace device path for the volume.
func (v *Volume) DevicePath() (string, error) {
	return mountapi.QueryDeviceName(v.h.Handle())
}

// Paths returns all of the volume's mount points.
func (v *Volume) Paths() ([]string, error) {
	// TODO: Consider using IOCTL_MOUNTMGR_QUERY_POINTS instead
	name, err := v.Name()
	if err != nil {
		return nil, err
	}

	return volumeapi.GetVolumePathNamesForVolumeName(name)
}

// Handle returns the system handle of the volume.
func (v *Volume) Handle() syscall.Handle {
	return v.h.Handle()
}

// Journal returns a change journal accessor for the volume.
func (v *Volume) Journal() *usn.Journal {
	return usn.NewJournalWithHandle(v.h.Clone())
}

// MFT returns an MFT for the volume.
func (v *Volume) MFT() *usn.MFT {
	return usn.NewMFTWithHandle(v.h.Clone())
}

// Close releases any resources consumed by the volume.
func (v *Volume) Close() error {
	return v.h.Close()
}
<|endoftext|>


<commit_before>/* Copyright 2017 Google 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

    https://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.
*/

// Binary gnmi_set performs a set request against a gNMI target with the specified config file.
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"
	"time"

	log "github.com/golang/glog"
	"golang.org/x/net/context"
	"google.golang.org/grpc"

	"github.com/google/gnxi/utils"
	"github.com/google/gnxi/utils/credentials"
	"github.com/google/gnxi/utils/xpath"

	pb "github.com/openconfig/gnmi/proto/gnmi"
)

type arrayFlags []string

func (i *arrayFlags) String() string {
	return "my string representation"
}

func (i *arrayFlags) Set(value string) error {
	*i = append(*i, value)
	return nil
}

var (
	deleteOpt  arrayFlags
	replaceOpt arrayFlags
	updateOpt  arrayFlags
	targetAddr = flag.String("target_addr", "localhost:10161", "The target address in the format of host:port")
	targetName = flag.String("target_name", "hostname.com", "The target name use to verify the hostname returned by TLS handshake")
	timeOut    = flag.Duration("time_out", 10*time.Second, "Timeout for the Get request, 10 seconds by default")
)

func buildPbUpdateList(pathValuePairs []string) []*pb.Update {
	var pbUpdateList []*pb.Update
	for _, item := range pathValuePairs {
		pathValuePair := strings.SplitN(item, ":", 2)
		// TODO (leguo): check if any path attribute contains ':'
		if len(pathValuePair) != 2 || len(pathValuePair[1]) == 0 {
			log.Exitf("invalid path-value pair: %v", item)
		}
		pbPath, err := xpath.ToGNMIPath(pathValuePair[0])
		if err != nil {
			log.Exitf("error in parsing xpath %q to gnmi path", pathValuePair[0])
		}
		var pbVal *pb.TypedValue
		if pathValuePair[1][0] == '@' {
			jsonFile := pathValuePair[1][1:]
			jsonConfig, err := ioutil.ReadFile(jsonFile)
			if err != nil {
				log.Exitf("cannot read data from file %v", jsonFile)
			}
			pbVal = &pb.TypedValue{
				Value: &pb.TypedValue_JsonIetfVal{
					JsonIetfVal: jsonConfig,
				},
			}
		} else {
			if strVal, err := strconv.Unquote(pathValuePair[1]); err == nil {
				pbVal = &pb.TypedValue{
					Value: &pb.TypedValue_StringVal{
						StringVal: strVal,
					},
				}
			} else {
				if intVal, err := strconv.ParseInt(pathValuePair[1], 10, 64); err == nil {
					pbVal = &pb.TypedValue{
						Value: &pb.TypedValue_IntVal{
							IntVal: intVal,
						},
					}
				} else if floatVal, err := strconv.ParseFloat(pathValuePair[1], 32); err == nil {
					pbVal = &pb.TypedValue{
						Value: &pb.TypedValue_FloatVal{
							FloatVal: float32(floatVal),
						},
					}
				} else if boolVal, err := strconv.ParseBool(pathValuePair[1]); err == nil {
					pbVal = &pb.TypedValue{
						Value: &pb.TypedValue_BoolVal{
							BoolVal: boolVal,
						},
					}
				} else {
					pbVal = &pb.TypedValue{
						Value: &pb.TypedValue_StringVal{
							StringVal: pathValuePair[1],
						},
					}
				}
			}
		}
		pbUpdateList = append(pbUpdateList, &pb.Update{Path: pbPath, Val: pbVal})
	}
	return pbUpdateList
}

func main() {
	flag.Var(&deleteOpt, "delete", "xpath to be deleted.")
	flag.Var(&replaceOpt, "replace", "xpath:value pair to be replaced. Value can be numeric, boolean, string, or IETF JSON file (. starts with '@').")
	flag.Var(&updateOpt, "update", "xpath:value pair to be updated. Value can be numeric, boolean, string, or IETF JSON file (. starts with '@').")
	flag.Parse()

	opts := credentials.ClientCredentials(*targetName)
	conn, err := grpc.Dial(*targetAddr, opts...)
	if err != nil {
		log.Exitf("Dialing to %q failed: %v", *targetAddr, err)
	}
	defer conn.Close()

	var deleteList []*pb.Path
	for _, xPath := range deleteOpt {
		pbPath, err := xpath.ToGNMIPath(xPath)
		if err != nil {
			log.Exitf("error in parsing xpath %q to gnmi path", xPath)
		}
		deleteList = append(deleteList, pbPath)
	}
	replaceList := buildPbUpdateList(replaceOpt)
	updateList := buildPbUpdateList(updateOpt)

	setRequest := &pb.SetRequest{
		Delete:  deleteList,
		Replace: replaceList,
		Update:  updateList,
	}

	fmt.Println("== getRequest:")
	utils.PrintProto(setRequest)

	cli := pb.NewGNMIClient(conn)
	setResponse, err := cli.Set(context.Background(), setRequest)
	if err != nil {
		log.Exitf("Set failed: %v", err)
	}

	fmt.Println("== getResponse:")
	utils.PrintProto(setResponse)
}
<commit_msg>fix typo in gnmi set command<commit_after>/* Copyright 2017 Google 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

    https://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.
*/

// Binary gnmi_set performs a set request against a gNMI target with the specified config file.
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"
	"time"

	log "github.com/golang/glog"
	"golang.org/x/net/context"
	"google.golang.org/grpc"

	"github.com/google/gnxi/utils"
	"github.com/google/gnxi/utils/credentials"
	"github.com/google/gnxi/utils/xpath"

	pb "github.com/openconfig/gnmi/proto/gnmi"
)

type arrayFlags []string

func (i *arrayFlags) String() string {
	return "my string representation"
}

func (i *arrayFlags) Set(value string) error {
	*i = append(*i, value)
	return nil
}

var (
	deleteOpt  arrayFlags
	replaceOpt arrayFlags
	updateOpt  arrayFlags
	targetAddr = flag.String("target_addr", "localhost:10161", "The target address in the format of host:port")
	targetName = flag.String("target_name", "hostname.com", "The target name use to verify the hostname returned by TLS handshake")
	timeOut    = flag.Duration("time_out", 10*time.Second, "Timeout for the Get request, 10 seconds by default")
)

func buildPbUpdateList(pathValuePairs []string) []*pb.Update {
	var pbUpdateList []*pb.Update
	for _, item := range pathValuePairs {
		pathValuePair := strings.SplitN(item, ":", 2)
		// TODO (leguo): check if any path attribute contains ':'
		if len(pathValuePair) != 2 || len(pathValuePair[1]) == 0 {
			log.Exitf("invalid path-value pair: %v", item)
		}
		pbPath, err := xpath.ToGNMIPath(pathValuePair[0])
		if err != nil {
			log.Exitf("error in parsing xpath %q to gnmi path", pathValuePair[0])
		}
		var pbVal *pb.TypedValue
		if pathValuePair[1][0] == '@' {
			jsonFile := pathValuePair[1][1:]
			jsonConfig, err := ioutil.ReadFile(jsonFile)
			if err != nil {
				log.Exitf("cannot read data from file %v", jsonFile)
			}
			pbVal = &pb.TypedValue{
				Value: &pb.TypedValue_JsonIetfVal{
					JsonIetfVal: jsonConfig,
				},
			}
		} else {
			if strVal, err := strconv.Unquote(pathValuePair[1]); err == nil {
				pbVal = &pb.TypedValue{
					Value: &pb.TypedValue_StringVal{
						StringVal: strVal,
					},
				}
			} else {
				if intVal, err := strconv.ParseInt(pathValuePair[1], 10, 64); err == nil {
					pbVal = &pb.TypedValue{
						Value: &pb.TypedValue_IntVal{
							IntVal: intVal,
						},
					}
				} else if floatVal, err := strconv.ParseFloat(pathValuePair[1], 32); err == nil {
					pbVal = &pb.TypedValue{
						Value: &pb.TypedValue_FloatVal{
							FloatVal: float32(floatVal),
						},
					}
				} else if boolVal, err := strconv.ParseBool(pathValuePair[1]); err == nil {
					pbVal = &pb.TypedValue{
						Value: &pb.TypedValue_BoolVal{
							BoolVal: boolVal,
						},
					}
				} else {
					pbVal = &pb.TypedValue{
						Value: &pb.TypedValue_StringVal{
							StringVal: pathValuePair[1],
						},
					}
				}
			}
		}
		pbUpdateList = append(pbUpdateList, &pb.Update{Path: pbPath, Val: pbVal})
	}
	return pbUpdateList
}

func main() {
	flag.Var(&deleteOpt, "delete", "xpath to be deleted.")
	flag.Var(&replaceOpt, "replace", "xpath:value pair to be replaced. Value can be numeric, boolean, string, or IETF JSON file (. starts with '@').")
	flag.Var(&updateOpt, "update", "xpath:value pair to be updated. Value can be numeric, boolean, string, or IETF JSON file (. starts with '@').")
	flag.Parse()

	opts := credentials.ClientCredentials(*targetName)
	conn, err := grpc.Dial(*targetAddr, opts...)
	if err != nil {
		log.Exitf("Dialing to %q failed: %v", *targetAddr, err)
	}
	defer conn.Close()

	var deleteList []*pb.Path
	for _, xPath := range deleteOpt {
		pbPath, err := xpath.ToGNMIPath(xPath)
		if err != nil {
			log.Exitf("error in parsing xpath %q to gnmi path", xPath)
		}
		deleteList = append(deleteList, pbPath)
	}
	replaceList := buildPbUpdateList(replaceOpt)
	updateList := buildPbUpdateList(updateOpt)

	setRequest := &pb.SetRequest{
		Delete:  deleteList,
		Replace: replaceList,
		Update:  updateList,
	}

	fmt.Println("== setRequest:")
	utils.PrintProto(setRequest)

	cli := pb.NewGNMIClient(conn)
	setResponse, err := cli.Set(context.Background(), setRequest)
	if err != nil {
		log.Exitf("Set failed: %v", err)
	}

	fmt.Println("== getResponse:")
	utils.PrintProto(setResponse)
}
<|endoftext|>


<commit_before>package libkb

import (
	"fmt"
	"strings"
)

func (u *User) IdentifyKey(is IdentifyState) error {
	// first, check to see if the eldest is a pgp key
	fokid := u.GetEldestFOKID()
	if fokid.Fp != nil {
		displayKey(fokid, is)
		return nil
	}

	// then, check entire key family
	ids := u.GetComputedKeyFamily().PGPKeyFOKIDs()
	for _, fokid := range ids {
		displayKey(&fokid, is)
	}

	return nil
}

func displayKey(fokid *FOKID, is IdentifyState) {
	var diff TrackDiff
	if mt := is.track; mt != nil {
		diff = mt.ComputeKeyDiff(fokid)
		is.res.KeyDiff = diff
	}

	is.GetUI().DisplayKey(fokid.Export(), ExportTrackDiff(diff))
}

type IdentifyArg struct {
	Me *User // The user who's doing the tracking
	Ui IdentifyUI
}

func (i IdentifyArg) MeSet() bool {
	return i.Me != nil
}

type IdentifyOutcome struct {
	Error       error
	KeyDiff     TrackDiff
	Deleted     []TrackDiffDeleted
	ProofChecks []*LinkCheckResult
	Warnings    []Warning
	TrackUsed   *TrackLookup
	TrackEqual  bool // Whether the track statement was equal to what we saw
	MeSet       bool // whether me was set at the time
}

func (i IdentifyOutcome) NumDeleted() int {
	return len(i.Deleted)
}

func (i IdentifyOutcome) NumProofFailures() int {
	nfails := 0
	for _, c := range i.ProofChecks {
		if c.err != nil {
			nfails++
		}
	}
	return nfails
}

func (i IdentifyOutcome) NumProofSuccesses() int {
	nsucc := 0
	for _, c := range i.ProofChecks {
		if c.err == nil {
			nsucc++
		}
	}
	return nsucc
}

func (i IdentifyOutcome) NumTrackFailures() int {
	ntf := 0
	check := func(d TrackDiff) bool {
		return d != nil && d.BreaksTracking()
	}
	for _, c := range i.ProofChecks {
		if check(c.diff) || check(c.remoteDiff) {
			ntf++
		}
	}
	if check(i.KeyDiff) {
		ntf++
	}
	return ntf
}

func (i IdentifyOutcome) NumTrackChanges() int {
	ntc := 0
	check := func(d TrackDiff) bool {
		return d != nil && !d.IsSameAsTracked()
	}
	for _, c := range i.ProofChecks {
		if check(c.diff) || check(c.remoteDiff) {
			ntc++
		}
	}
	return ntc
}

func (i IdentifyOutcome) GetErrorAndWarnings(strict bool) (err error, warnings Warnings) {

	if i.Error != nil {
		err = i.Error
		return
	}

	probs := make([]string, 0, 0)

	softErr := func(s string) {
		if strict {
			probs = append(probs, s)
		} else {
			warnings.Push(StringWarning(s))
		}
	}

	for _, deleted := range i.Deleted {
		softErr(deleted.ToDisplayString())
	}

	if nfails := i.NumProofFailures(); nfails > 0 {
		p := fmt.Sprintf("PROBLEM: %d proof%s failed remote checks", nfails, GiveMeAnS(nfails))
		softErr(p)
	}

	if ntf := i.NumTrackFailures(); ntf > 0 {
		probs = append(probs,
			fmt.Sprintf("%d track component%s failed",
				ntf, GiveMeAnS(ntf)))
	}

	if len(probs) > 0 {
		err = fmt.Errorf("%s", strings.Join(probs, ";"))
	}

	return
}

func (i IdentifyOutcome) GetError() error {
	e, _ := i.GetErrorAndWarnings(true)
	return e
}

func (i IdentifyOutcome) GetErrorLax() (error, Warnings) {
	return i.GetErrorAndWarnings(true)
}

func NewIdentifyOutcome(m bool) *IdentifyOutcome {
	return &IdentifyOutcome{
		MeSet:       m,
		Warnings:    make([]Warning, 0, 0),
		ProofChecks: make([]*LinkCheckResult, 0, 1),
	}
}

type IdentifyState struct {
	arg   *IdentifyArg
	res   *IdentifyOutcome
	u     *User
	track *TrackLookup
}

func (s IdentifyState) GetUI() IdentifyUI {
	return s.arg.Ui
}

func NewIdentifyState(arg *IdentifyArg, res *IdentifyOutcome, u *User) IdentifyState {
	return IdentifyState{arg, res, u, nil}
}

func (s *IdentifyState) ComputeDeletedProofs() {
	if s.track == nil {
		return
	}
	found := s.u.IdTable.MakeTrackSet()
	tracked := s.track.set

	// These are the proofs that we previously tracked that we
	// didn't observe in the current profile
	diff := (*tracked).Subtract(*found)

	for _, e := range diff {
		// If the proofs in the difference are for GOOD proofs,
		// the we have a problem.  Mark the proof as "DELETED"
		if e.GetProofState() == PROOF_STATE_OK {
			s.res.Deleted = append(s.res.Deleted, TrackDiffDeleted{e})
		}
	}
}

func (s *IdentifyState) InitResultList() {
	idt := s.u.IdTable
	l := len(idt.activeProofs)
	s.res.ProofChecks = make([]*LinkCheckResult, l)
	for i, p := range idt.activeProofs {
		s.res.ProofChecks[i] = &LinkCheckResult{link: p, trackedProofState: PROOF_STATE_NONE, position: i}
	}
}

func (s *IdentifyState) ComputeTrackDiffs() {
	if s.track != nil {
		G.Log.Debug("| with tracking %v", s.track.set)
		for _, c := range s.res.ProofChecks {
			c.diff = c.link.ComputeTrackDiff(s.track)
			c.trackedProofState = s.track.GetProofState(c.link)
		}
	}
}

func (u *User) _identify(arg IdentifyArg) (res *IdentifyOutcome) {
	res = NewIdentifyOutcome(arg.MeSet())
	is := NewIdentifyState(&arg, res, u)

	if arg.Me == nil {
		// noop
	} else if tlink, err := arg.Me.GetTrackingStatementFor(u.name, u.id); err != nil {
		res.Error = err
		return
	} else if tlink != nil {
		is.track = NewTrackLookup(tlink)
		res.TrackUsed = is.track
	}

	is.GetUI().ReportLastTrack(ExportTrackSummary(is.track))

	G.Log.Debug("+ Identify(%s)", u.name)

	if res.Error = u.IdentifyKey(is); res.Error != nil {
		return
	}

	is.InitResultList()
	is.ComputeTrackDiffs()
	is.ComputeDeletedProofs()

	is.GetUI().LaunchNetworkChecks(res.ExportToUncheckedIdentity(), u.Export())
	u.IdTable.Identify(is)

	G.Log.Debug("- Identify(%s)", u.name)
	return
}

func (u *User) Identify(arg IdentifyArg) (outcome *IdentifyOutcome, ti TrackInstructions, err error) {
	arg.Ui.Start()
	outcome = u._identify(arg)
	tmp, err := arg.Ui.FinishAndPrompt(outcome.Export())
	fpr := ImportFinishAndPromptRes(tmp)
	return outcome, fpr, err
}

func (u *User) IdentifySimple(me *User, ui IdentifyUI) (*IdentifyOutcome, error) {
	outcome, _, err := u.Identify(IdentifyArg{
		Me: me,
		Ui: ui,
	})
	return outcome, err
}
<commit_msg>Closed #239<commit_after>package libkb

import (
	"fmt"
	"strings"
)

func (u *User) IdentifyKey(is IdentifyState) error {
	// first, check to see if the eldest is a pgp key
	fokid := u.GetEldestFOKID()
	if fokid.Fp != nil {
		displayKey(fokid, is)
		return nil
	}

	// then, check entire key family
	if u.GetComputedKeyFamily() == nil {
		return nil
	}
	ids := u.GetComputedKeyFamily().PGPKeyFOKIDs()
	for _, fokid := range ids {
		displayKey(&fokid, is)
	}

	return nil
}

func displayKey(fokid *FOKID, is IdentifyState) {
	var diff TrackDiff
	if mt := is.track; mt != nil {
		diff = mt.ComputeKeyDiff(fokid)
		is.res.KeyDiff = diff
	}

	is.GetUI().DisplayKey(fokid.Export(), ExportTrackDiff(diff))
}

type IdentifyArg struct {
	Me *User // The user who's doing the tracking
	Ui IdentifyUI
}

func (i IdentifyArg) MeSet() bool {
	return i.Me != nil
}

type IdentifyOutcome struct {
	Error       error
	KeyDiff     TrackDiff
	Deleted     []TrackDiffDeleted
	ProofChecks []*LinkCheckResult
	Warnings    []Warning
	TrackUsed   *TrackLookup
	TrackEqual  bool // Whether the track statement was equal to what we saw
	MeSet       bool // whether me was set at the time
}

func (i IdentifyOutcome) NumDeleted() int {
	return len(i.Deleted)
}

func (i IdentifyOutcome) NumProofFailures() int {
	nfails := 0
	for _, c := range i.ProofChecks {
		if c.err != nil {
			nfails++
		}
	}
	return nfails
}

func (i IdentifyOutcome) NumProofSuccesses() int {
	nsucc := 0
	for _, c := range i.ProofChecks {
		if c.err == nil {
			nsucc++
		}
	}
	return nsucc
}

func (i IdentifyOutcome) NumTrackFailures() int {
	ntf := 0
	check := func(d TrackDiff) bool {
		return d != nil && d.BreaksTracking()
	}
	for _, c := range i.ProofChecks {
		if check(c.diff) || check(c.remoteDiff) {
			ntf++
		}
	}
	if check(i.KeyDiff) {
		ntf++
	}
	return ntf
}

func (i IdentifyOutcome) NumTrackChanges() int {
	ntc := 0
	check := func(d TrackDiff) bool {
		return d != nil && !d.IsSameAsTracked()
	}
	for _, c := range i.ProofChecks {
		if check(c.diff) || check(c.remoteDiff) {
			ntc++
		}
	}
	return ntc
}

func (i IdentifyOutcome) GetErrorAndWarnings(strict bool) (err error, warnings Warnings) {

	if i.Error != nil {
		err = i.Error
		return
	}

	probs := make([]string, 0, 0)

	softErr := func(s string) {
		if strict {
			probs = append(probs, s)
		} else {
			warnings.Push(StringWarning(s))
		}
	}

	for _, deleted := range i.Deleted {
		softErr(deleted.ToDisplayString())
	}

	if nfails := i.NumProofFailures(); nfails > 0 {
		p := fmt.Sprintf("PROBLEM: %d proof%s failed remote checks", nfails, GiveMeAnS(nfails))
		softErr(p)
	}

	if ntf := i.NumTrackFailures(); ntf > 0 {
		probs = append(probs,
			fmt.Sprintf("%d track component%s failed",
				ntf, GiveMeAnS(ntf)))
	}

	if len(probs) > 0 {
		err = fmt.Errorf("%s", strings.Join(probs, ";"))
	}

	return
}

func (i IdentifyOutcome) GetError() error {
	e, _ := i.GetErrorAndWarnings(true)
	return e
}

func (i IdentifyOutcome) GetErrorLax() (error, Warnings) {
	return i.GetErrorAndWarnings(true)
}

func NewIdentifyOutcome(m bool) *IdentifyOutcome {
	return &IdentifyOutcome{
		MeSet:       m,
		Warnings:    make([]Warning, 0, 0),
		ProofChecks: make([]*LinkCheckResult, 0, 1),
	}
}

type IdentifyState struct {
	arg   *IdentifyArg
	res   *IdentifyOutcome
	u     *User
	track *TrackLookup
}

func (s IdentifyState) GetUI() IdentifyUI {
	return s.arg.Ui
}

func NewIdentifyState(arg *IdentifyArg, res *IdentifyOutcome, u *User) IdentifyState {
	return IdentifyState{arg, res, u, nil}
}

func (s *IdentifyState) ComputeDeletedProofs() {
	if s.track == nil {
		return
	}
	found := s.u.IdTable.MakeTrackSet()
	tracked := s.track.set

	// These are the proofs that we previously tracked that we
	// didn't observe in the current profile
	diff := (*tracked).Subtract(*found)

	for _, e := range diff {
		// If the proofs in the difference are for GOOD proofs,
		// the we have a problem.  Mark the proof as "DELETED"
		if e.GetProofState() == PROOF_STATE_OK {
			s.res.Deleted = append(s.res.Deleted, TrackDiffDeleted{e})
		}
	}
}

func (s *IdentifyState) InitResultList() {
	idt := s.u.IdTable
	l := len(idt.activeProofs)
	s.res.ProofChecks = make([]*LinkCheckResult, l)
	for i, p := range idt.activeProofs {
		s.res.ProofChecks[i] = &LinkCheckResult{link: p, trackedProofState: PROOF_STATE_NONE, position: i}
	}
}

func (s *IdentifyState) ComputeTrackDiffs() {
	if s.track != nil {
		G.Log.Debug("| with tracking %v", s.track.set)
		for _, c := range s.res.ProofChecks {
			c.diff = c.link.ComputeTrackDiff(s.track)
			c.trackedProofState = s.track.GetProofState(c.link)
		}
	}
}

func (u *User) _identify(arg IdentifyArg) (res *IdentifyOutcome) {
	res = NewIdentifyOutcome(arg.MeSet())
	is := NewIdentifyState(&arg, res, u)

	if arg.Me == nil {
		// noop
	} else if tlink, err := arg.Me.GetTrackingStatementFor(u.name, u.id); err != nil {
		res.Error = err
		return
	} else if tlink != nil {
		is.track = NewTrackLookup(tlink)
		res.TrackUsed = is.track
	}

	is.GetUI().ReportLastTrack(ExportTrackSummary(is.track))

	G.Log.Debug("+ Identify(%s)", u.name)

	if res.Error = u.IdentifyKey(is); res.Error != nil {
		return
	}

	is.InitResultList()
	is.ComputeTrackDiffs()
	is.ComputeDeletedProofs()

	is.GetUI().LaunchNetworkChecks(res.ExportToUncheckedIdentity(), u.Export())
	u.IdTable.Identify(is)

	G.Log.Debug("- Identify(%s)", u.name)
	return
}

func (u *User) Identify(arg IdentifyArg) (outcome *IdentifyOutcome, ti TrackInstructions, err error) {
	arg.Ui.Start()
	outcome = u._identify(arg)
	tmp, err := arg.Ui.FinishAndPrompt(outcome.Export())
	fpr := ImportFinishAndPromptRes(tmp)
	return outcome, fpr, err
}

func (u *User) IdentifySimple(me *User, ui IdentifyUI) (*IdentifyOutcome, error) {
	outcome, _, err := u.Identify(IdentifyArg{
		Me: me,
		Ui: ui,
	})
	return outcome, err
}
<|endoftext|>