<commit_before>package limiter

import (
	"context"
	"io"
	"os"
	"time"

	"golang.org/x/time/rate"
)

type Writer struct {
	w       io.WriteCloser
	limiter Rate
	ctx     context.Context
}

type Rate interface {
	WaitN(ctx context.Context, n int) error
}

func NewRate(bytesPerSec, burstLimit int) Rate {
	limiter := rate.NewLimiter(rate.Limit(bytesPerSec), burstLimit)
	limiter.AllowN(time.Now(), burstLimit) // spend initial burst
	return limiter
}

// NewWriter returns a writer that implements io.Writer with rate limiting.
// The limiter use a token bucket approach and limits the rate to bytesPerSec
// with a maximum burst of burstLimit.
func NewWriter(w io.WriteCloser, bytesPerSec, burstLimit int) *Writer {
	limiter := NewRate(bytesPerSec, burstLimit)

	return &Writer{
		w:       w,
		ctx:     context.Background(),
		limiter: limiter,
	}
}

// WithRate returns a Writer with the specified rate limiter.
func NewWriterWithRate(w io.WriteCloser, limiter Rate) *Writer {
	return &Writer{
		w:       w,
		ctx:     context.Background(),
		limiter: limiter,
	}
}

// Write writes bytes from p.
func (s *Writer) Write(b []byte) (int, error) {
	if s.limiter == nil {
		return s.w.Write(b)
	}

	n, err := s.w.Write(b)
	if err != nil {
		return n, err
	}

	if err := s.limiter.WaitN(s.ctx, n); err != nil {
		return n, err
	}
	return n, err
}

func (s *Writer) Sync() error {
	if f, ok := s.w.(*os.File); ok {
		return f.Sync()
	}
	return nil
}

func (s *Writer) Name() string {
	if f, ok := s.w.(*os.File); ok {
		return f.Name()
	}
	return ""
}

func (s *Writer) Close() error {
	return s.w.Close()
}
<commit_msg>fix the comments error<commit_after>package limiter

import (
	"context"
	"io"
	"os"
	"time"

	"golang.org/x/time/rate"
)

type Writer struct {
	w       io.WriteCloser
	limiter Rate
	ctx     context.Context
}

type Rate interface {
	WaitN(ctx context.Context, n int) error
}

func NewRate(bytesPerSec, burstLimit int) Rate {
	limiter := rate.NewLimiter(rate.Limit(bytesPerSec), burstLimit)
	limiter.AllowN(time.Now(), burstLimit) // spend initial burst
	return limiter
}

// NewWriter returns a writer that implements io.Writer with rate limiting.
// The limiter use a token bucket approach and limits the rate to bytesPerSec
// with a maximum burst of burstLimit.
func NewWriter(w io.WriteCloser, bytesPerSec, burstLimit int) *Writer {
	limiter := NewRate(bytesPerSec, burstLimit)

	return &Writer{
		w:       w,
		ctx:     context.Background(),
		limiter: limiter,
	}
}

// WithRate returns a Writer with the specified rate limiter.
func NewWriterWithRate(w io.WriteCloser, limiter Rate) *Writer {
	return &Writer{
		w:       w,
		ctx:     context.Background(),
		limiter: limiter,
	}
}

// Write writes bytes from b.
func (s *Writer) Write(b []byte) (int, error) {
	if s.limiter == nil {
		return s.w.Write(b)
	}

	n, err := s.w.Write(b)
	if err != nil {
		return n, err
	}

	if err := s.limiter.WaitN(s.ctx, n); err != nil {
		return n, err
	}
	return n, err
}

func (s *Writer) Sync() error {
	if f, ok := s.w.(*os.File); ok {
		return f.Sync()
	}
	return nil
}

func (s *Writer) Name() string {
	if f, ok := s.w.(*os.File); ok {
		return f.Name()
	}
	return ""
}

func (s *Writer) Close() error {
	return s.w.Close()
}
<|endoftext|>


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

package menu

import (
	"fmt"
	"sync"
	"testing"
	"time"

	"github.com/google/goterm/term"
)

type dummyEntry struct {
	mu        sync.Mutex
	label     string
	isDefault bool
	do        error
	called    bool
}

func (d *dummyEntry) Label() string {
	d.mu.Lock()
	defer d.mu.Unlock()
	return d.label
}
func (d *dummyEntry) String() string {
	d.mu.Lock()
	defer d.mu.Unlock()
	return d.String()
}

func (d *dummyEntry) Do() error {
	d.mu.Lock()
	defer d.mu.Unlock()
	d.called = true
	return d.do
}
func (d *dummyEntry) Called() bool {
	d.mu.Lock()
	defer d.mu.Unlock()
	return d.called
}
func (d *dummyEntry) IsDefault() bool {
	d.mu.Lock()
	defer d.mu.Unlock()
	return d.isDefault
}

func TestChoose(t *testing.T) {
	entry1 := &dummyEntry{label: "1"}
	entry2 := &dummyEntry{label: "2"}
	entry3 := &dummyEntry{label: "3"}

	for _, tt := range []struct {
		name      string
		entries   []Entry
		userEntry []byte
		want      Entry
	}{
		{
			name:    "just_hit_enter",
			entries: []Entry{entry1, entry2, entry3},
			// user just hits enter.
			userEntry: []byte("\r\n"),
			want:      nil,
		},
		{
			name:      "hit_nothing",
			entries:   []Entry{entry1, entry2, entry3},
			userEntry: nil,
			want:      nil,
		},
		{
			name:      "hit_1",
			entries:   []Entry{entry1, entry2, entry3},
			userEntry: []byte("1\r\n"),
			want:      entry1,
		},
		{
			name:      "hit_3",
			entries:   []Entry{entry1, entry2, entry3},
			userEntry: []byte("3\r\n"),
			want:      entry3,
		},
		{
			name:    "tentative_hit_1",
			entries: []Entry{entry1, entry2, entry3},
			// \x08 is the backspace character.
			userEntry: []byte("2\x081\r\n"),
			want:      entry1,
		},
		{
			name:      "out_of_bounds",
			entries:   []Entry{entry1, entry2, entry3},
			userEntry: []byte("4\r\n"),
			want:      nil,
		},
		{
			name:      "not_a_number",
			entries:   []Entry{entry1, entry2, entry3},
			userEntry: []byte("abc\r\n"),
			want:      nil,
		},
	} {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			pty, err := term.OpenPTY()
			if err != nil {
				t.Fatalf("%v", err)
			}
			defer pty.Close()

			chosen := make(chan Entry)
			go func() {
				chosen <- Choose(pty.Slave, tt.entries...)
			}()

			// Well this sucks.
			//
			// We have to wait until Choose has actually started trying to read, as
			// ttys are asynchronous.
			//
			// Know a better way? Halp.
			time.Sleep(1 * time.Second)

			if tt.userEntry != nil {
				if _, err := pty.Master.Write(tt.userEntry); err != nil {
					t.Fatalf("failed to write new-line: %v", err)
				}
			}

			if got := <-chosen; got != tt.want {
				t.Errorf("Choose(%#v, %#v) = %#v, want %#v", tt.userEntry, tt.entries, got, tt.want)
			}
		})
	}
}

func contains(s []string, t string) bool {
	for _, u := range s {
		if u == t {
			return true
		}
	}
	return false
}

func TestShowMenuAndBoot(t *testing.T) {
	tests := []struct {
		name      string
		entries   []*dummyEntry
		userEntry []byte

		// calledLabels are the entries for which Do was called.
		calledLabels []string
	}{
		{
			name: "default_entry",
			entries: []*dummyEntry{
				{label: "1", isDefault: true, do: errStopTestOnly},
				{label: "2", isDefault: true, do: nil},
			},
			// user just hits enter.
			userEntry:    []byte("\r\n"),
			calledLabels: []string{"1"},
		},
		{
			name: "non_default_entry_default",
			entries: []*dummyEntry{
				{label: "1", isDefault: false, do: errStopTestOnly},
				{label: "2", isDefault: true, do: errStopTestOnly},
				{label: "3", isDefault: true, do: nil},
			},
			// user just hits enter.
			userEntry:    []byte("\r\n"),
			calledLabels: []string{"2"},
		},
		{
			name: "non_default_entry_chosen_but_broken",
			entries: []*dummyEntry{
				{label: "1", isDefault: false, do: fmt.Errorf("borked")},
				{label: "2", isDefault: true, do: errStopTestOnly},
				{label: "3", isDefault: true, do: nil},
			},
			userEntry:    []byte("1\r\n"),
			calledLabels: []string{"1", "2"},
		},
		{
			name: "last_entry_works",
			entries: []*dummyEntry{
				{label: "1", isDefault: true, do: nil},
				{label: "2", isDefault: true, do: nil},
				{label: "3", isDefault: true, do: errStopTestOnly},
			},
			// user just hits enter.
			userEntry:    []byte("\r\n"),
			calledLabels: []string{"1", "2", "3"},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			pty, err := term.OpenPTY()
			if err != nil {
				t.Fatalf("%v", err)
			}
			defer pty.Close()

			var entries []Entry
			for _, e := range tt.entries {
				entries = append(entries, e)
			}

			var wg sync.WaitGroup
			wg.Add(1)
			go func() {
				ShowMenuAndBoot(pty.Slave, entries...)
				wg.Done()
			}()

			// Well this sucks.
			//
			// We have to wait until Choose has actually started trying to read, as
			// ttys are asynchronous.
			//
			// Know a better way? Halp.
			time.Sleep(1 * time.Second)

			if tt.userEntry != nil {
				if _, err := pty.Master.Write(tt.userEntry); err != nil {
					t.Fatalf("failed to write new-line: %v", err)
				}
			}

			wg.Wait()

			for _, entry := range tt.entries {
				wantCalled := contains(tt.calledLabels, entry.label)
				if wantCalled != entry.Called() {
					t.Errorf("Entry %s gotCalled %t, wantCalled %t", entry.Label(), entry.Called(), wantCalled)
				}
			}
		})
	}
}
<commit_msg>menu: fix test problem<commit_after>// Copyright 2020 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package menu

import (
	"fmt"
	"sync"
	"testing"
	"time"

	"github.com/google/goterm/term"
)

type dummyEntry struct {
	mu        sync.Mutex
	label     string
	isDefault bool
	do        error
	called    bool
}

func (d *dummyEntry) Label() string {
	d.mu.Lock()
	defer d.mu.Unlock()
	return d.label
}

func (d *dummyEntry) String() string {
	return d.Label()
}

func (d *dummyEntry) Do() error {
	d.mu.Lock()
	defer d.mu.Unlock()
	d.called = true
	return d.do
}

func (d *dummyEntry) Called() bool {
	d.mu.Lock()
	defer d.mu.Unlock()
	return d.called
}

func (d *dummyEntry) IsDefault() bool {
	d.mu.Lock()
	defer d.mu.Unlock()
	return d.isDefault
}

func TestChoose(t *testing.T) {
	entry1 := &dummyEntry{label: "1"}
	entry2 := &dummyEntry{label: "2"}
	entry3 := &dummyEntry{label: "3"}

	for _, tt := range []struct {
		name      string
		entries   []Entry
		userEntry []byte
		want      Entry
	}{
		{
			name:    "just_hit_enter",
			entries: []Entry{entry1, entry2, entry3},
			// user just hits enter.
			userEntry: []byte("\r\n"),
			want:      nil,
		},
		{
			name:      "hit_nothing",
			entries:   []Entry{entry1, entry2, entry3},
			userEntry: nil,
			want:      nil,
		},
		{
			name:      "hit_1",
			entries:   []Entry{entry1, entry2, entry3},
			userEntry: []byte("1\r\n"),
			want:      entry1,
		},
		{
			name:      "hit_3",
			entries:   []Entry{entry1, entry2, entry3},
			userEntry: []byte("3\r\n"),
			want:      entry3,
		},
		{
			name:    "tentative_hit_1",
			entries: []Entry{entry1, entry2, entry3},
			// \x08 is the backspace character.
			userEntry: []byte("2\x081\r\n"),
			want:      entry1,
		},
		{
			name:      "out_of_bounds",
			entries:   []Entry{entry1, entry2, entry3},
			userEntry: []byte("4\r\n"),
			want:      nil,
		},
		{
			name:      "not_a_number",
			entries:   []Entry{entry1, entry2, entry3},
			userEntry: []byte("abc\r\n"),
			want:      nil,
		},
	} {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			pty, err := term.OpenPTY()
			if err != nil {
				t.Fatalf("%v", err)
			}
			defer pty.Close()

			chosen := make(chan Entry)
			go func() {
				chosen <- Choose(pty.Slave, tt.entries...)
			}()

			// Well this sucks.
			//
			// We have to wait until Choose has actually started trying to read, as
			// ttys are asynchronous.
			//
			// Know a better way? Halp.
			time.Sleep(1 * time.Second)

			if tt.userEntry != nil {
				if _, err := pty.Master.Write(tt.userEntry); err != nil {
					t.Fatalf("failed to write new-line: %v", err)
				}
			}

			if got := <-chosen; got != tt.want {
				t.Errorf("Choose(%#v, %#v) = %#v, want %#v", tt.userEntry, tt.entries, got, tt.want)
			}
		})
	}
}

func contains(s []string, t string) bool {
	for _, u := range s {
		if u == t {
			return true
		}
	}
	return false
}

func TestShowMenuAndBoot(t *testing.T) {
	tests := []struct {
		name      string
		entries   []*dummyEntry
		userEntry []byte

		// calledLabels are the entries for which Do was called.
		calledLabels []string
	}{
		{
			name: "default_entry",
			entries: []*dummyEntry{
				{label: "1", isDefault: true, do: errStopTestOnly},
				{label: "2", isDefault: true, do: nil},
			},
			// user just hits enter.
			userEntry:    []byte("\r\n"),
			calledLabels: []string{"1"},
		},
		{
			name: "non_default_entry_default",
			entries: []*dummyEntry{
				{label: "1", isDefault: false, do: errStopTestOnly},
				{label: "2", isDefault: true, do: errStopTestOnly},
				{label: "3", isDefault: true, do: nil},
			},
			// user just hits enter.
			userEntry:    []byte("\r\n"),
			calledLabels: []string{"2"},
		},
		{
			name: "non_default_entry_chosen_but_broken",
			entries: []*dummyEntry{
				{label: "1", isDefault: false, do: fmt.Errorf("borked")},
				{label: "2", isDefault: true, do: errStopTestOnly},
				{label: "3", isDefault: true, do: nil},
			},
			userEntry:    []byte("1\r\n"),
			calledLabels: []string{"1", "2"},
		},
		{
			name: "last_entry_works",
			entries: []*dummyEntry{
				{label: "1", isDefault: true, do: nil},
				{label: "2", isDefault: true, do: nil},
				{label: "3", isDefault: true, do: errStopTestOnly},
			},
			// user just hits enter.
			userEntry:    []byte("\r\n"),
			calledLabels: []string{"1", "2", "3"},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			pty, err := term.OpenPTY()
			if err != nil {
				t.Fatalf("%v", err)
			}
			defer pty.Close()

			var entries []Entry
			for _, e := range tt.entries {
				entries = append(entries, e)
			}

			var wg sync.WaitGroup
			wg.Add(1)
			go func() {
				ShowMenuAndBoot(pty.Slave, entries...)
				wg.Done()
			}()

			// Well this sucks.
			//
			// We have to wait until Choose has actually started trying to read, as
			// ttys are asynchronous.
			//
			// Know a better way? Halp.
			time.Sleep(1 * time.Second)

			if tt.userEntry != nil {
				if _, err := pty.Master.Write(tt.userEntry); err != nil {
					t.Fatalf("failed to write new-line: %v", err)
				}
			}

			wg.Wait()

			for _, entry := range tt.entries {
				wantCalled := contains(tt.calledLabels, entry.label)
				if wantCalled != entry.Called() {
					t.Errorf("Entry %s gotCalled %t, wantCalled %t", entry.Label(), entry.Called(), wantCalled)
				}
			}
		})
	}
}
<|endoftext|>


<commit_before>package source

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"sync"
	"time"

	"github.com/docker/docker/pkg/jsonmessage"
	docker "github.com/fsouza/go-dockerclient"
	git "github.com/sensiblecodeio/git-prep-directory"
)

type ImageSource interface {
	// Build/pull/fetch a docker image and return its name as a string
	Obtain(client *docker.Client, payload []byte) (string, error)
}

type CwdSource struct{}

func (CwdSource) Name() (string, error) {
	name, err := os.Getwd()
	if err != nil {
		return "", err
	}
	return filepath.Base(name), nil
}

func (s *CwdSource) Obtain(c *docker.Client, payload []byte) (string, error) {
	imageName, err := s.Name()
	if err != nil {
		return "", err
	}

	buildPath := "."
	err = DockerBuildDirectory(c, imageName, buildPath)
	if err != nil {
		return "", err
	}

	// Test for the presence of a 'runtime/Dockerfile' in the buildpath.
	// If it's there, then we run the image we just built, and use its
	// stdout as a build context
	if exists(filepath.Join(buildPath, "runtime", "Dockerfile")) {
		log.Printf("Generate runtime image")
		imageName, err = constructRuntime(c, imageName)
		if err != nil {
			return "", err
		}
	}

	return imageName, nil
}

type DockerPullSource struct {
	Repository, Tag string
}

var imageTag = regexp.MustCompile("^([^:]+):?(.*)$")

// DockerPullSourceFromImage creates a *DockerPullSource from an image name
// (with an optional tag)
func DockerPullSourceFromImage(image string) *DockerPullSource {
	parts := imageTag.FindStringSubmatch(image)
	if len(parts) != 2 {
		log.Panicf("imageTag regexp failed to match %q", image)
	}
	image, tag := parts[0], parts[1]
	return &DockerPullSource{image, tag}
}

func (s *DockerPullSource) Obtain(c *docker.Client, payload []byte) (string, error) {

	opts := docker.PullImageOptions{
		Repository:    s.Repository,
		Tag:           s.Tag,
		RawJSONStream: true,
	}

	// TODO(pwaller): Send the output somewhere better
	target := os.Stderr

	outputStream, errorC := PullProgressCopier(target)
	opts.OutputStream = outputStream

	// TODO(pwaller):
	// I don't use auth, just a daemon listening only on localhost,
	// so this remains unimplemented.
	var auth docker.AuthConfiguration
	err := c.PullImage(opts, auth)

	outputStream.Close()

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

	imageName := fmt.Sprintf("%s:%s", s.Repository, s.Tag)
	return imageName, <-errorC

	// c.PullImage(opts)
	// return , nil
	// return "", fmt.Errorf("not implemented: DockerPullSource.Obtain(%v, %v)", s.Repository, s.Tag)
}

type GitHostSource struct {
	Host          string
	User          string
	Repository    string
	InitialBranch string
	// Directory in which to do `docker build`.
	// Uses repository root if blank.
	ImageRoot string
}

func (s *GitHostSource) CloneURL() string {
	format := "https://%s/%s/%s"
	if HaveSSHKey() {
		format = "ssh://git@%s/%s/%s"
	}

	return fmt.Sprintf(format, s.Host, s.User, s.Repository)
}

// Return the git SHA from the given hook payload, if we have a hook payload,
// otherwise return the InitialBranch.
func (s *GitHostSource) Ref(payload []byte) (string, error) {
	if len(payload) == 0 {
		return s.InitialBranch, nil
	}

	var v struct {
		SHA string
	}

	err := json.Unmarshal(payload, &v)
	if err != nil {
		return "", err
	}

	return v.SHA, nil
}

func (s *GitHostSource) Obtain(c *docker.Client, payload []byte) (string, error) {
	// Obtain/update local mirrorformat

	ref, err := s.Ref(payload)
	if err != nil {
		return "", err
	}

	gitDir, err := filepath.Abs(filepath.Join(".", "src", s.Host, s.User, s.Repository))
	if err != nil {
		return "", err
	}

	build, err := git.PrepBuildDirectory(gitDir, s.CloneURL(), ref)
	if err != nil {
		return "", err
	}
	defer build.Cleanup()

	dockerImage := fmt.Sprintf("%s:%s", s.Repository, build.Name)
	buildPath := filepath.Join(build.Dir, s.ImageRoot)

	err = DockerBuildDirectory(c, dockerImage, buildPath)
	if err != nil {
		return "", err
	}

	// Test for the presence of a 'runtime/Dockerfile' in the buildpath.
	// If it's there, then we run the image we just built, and use its
	// stdout as a build context
	if exists(filepath.Join(buildPath, "runtime", "Dockerfile")) {
		dockerImage, err = constructRuntime(c, dockerImage)
		if err != nil {
			return "", err
		}
	}

	return dockerImage, nil
}

func constructRuntime(c *docker.Client, dockerImage string) (string, error) {
	stdout, err := DockerRun(c, dockerImage)
	if err != nil {
		return "", fmt.Errorf("run buildtime image: %v", err)
	}

	err = c.BuildImage(docker.BuildImageOptions{
		Name: dockerImage + "-runtime",
		// OutputStream is an io.Reader, but it gets typecast
		// to an io.ReadCloser and closes the body inside
		// net/http's Request type.
		// stdout is closed inside here.
		InputStream:  stdout,
		OutputStream: os.Stderr,
	})
	if err != nil {
		return "", fmt.Errorf("BuildImage -runtime: %v", err)
	}

	return dockerImage + "-runtime", nil
}

func DockerRun(c *docker.Client, imageName string) (io.ReadCloser, error) {
	cont, err := c.CreateContainer(docker.CreateContainerOptions{
		Config: &docker.Config{
			Hostname:     "generateruntimecontext",
			AttachStdout: true,
			AttachStderr: true,
			Image:        imageName,
			Labels: map[string]string{
				"orchestrator": "hanoverd",
				"purpose":      "Generate build context for runtime container",
			},
		},
	})
	if err != nil {
		log.Printf("Create container... failed: %v", err)
		return nil, err
	}

	r, w := io.Pipe()
	attached := make(chan struct{})
	detached := make(chan struct{})

	go func() {
		defer close(detached)

		err := c.AttachToContainer(docker.AttachToContainerOptions{
			Container:    cont.ID,
			OutputStream: w,
			ErrorStream:  os.Stderr,
			Logs:         true,
			Stdout:       true,
			Stderr:       true,
			Stream:       true,
			Success:      attached,
		})
		// io.Pipe hardwired to never return error here.
		_ = w.CloseWithError(err)
	}()

	select {
	case <-detached:
		// attachment failed
		log.Printf("Attachment failed")
		return nil, fmt.Errorf("Attachment failed")
	case <-attached:
		attached <- struct{}{}
	}

	err = c.StartContainer(cont.ID, &docker.HostConfig{})
	if err != nil {
		log.Printf("Start container... failed: %v", err)
		return nil, err
	}

	removeContainer := func() {
		err := c.RemoveContainer(docker.RemoveContainerOptions{
			ID:            cont.ID,
			RemoveVolumes: true,
			Force:         true,
		})
		if err != nil {
			log.Printf("Error removing intermediate container: %v", err)
		}
	}

	return struct {
		io.Reader
		io.Closer
	}{
		Reader: r,
		Closer: CloseFunc(func() error {
			defer removeContainer()

			status, err := c.WaitContainer(cont.ID)
			if err != nil {
				return err
			}
			if status != 0 {
				return fmt.Errorf("non-zero exit status: %v", err)
			}
			return nil
		}),
	}, err
}

type CloseFunc func() error

func (fn CloseFunc) Close() error { return fn() }

func exists(filename string) bool {
	_, err := os.Stat(filename)
	switch {
	case err == nil:
		return true
	default:
		log.Printf("Error checking for the existence of %q: %v", filename, err)
	case os.IsNotExist(err):
	}
	return false
}

// Returns true if $HOME/.ssh exists, false otherwise
func HaveSSHKey() bool {
	keys := []string{"id_dsa", "id_ecdsa", "id_rsa", "id_ed25519"}
	for _, filename := range keys {
		path := os.ExpandEnv(filepath.Join("$HOME/.ssh", filename))
		if _, err := os.Stat(path); err == nil {
			return true
		}
	}
	return false
}

func DockerBuildDirectory(c *docker.Client, name, path string) error {
	return c.BuildImage(docker.BuildImageOptions{
		Name:         name,
		ContextDir:   path,
		OutputStream: os.Stderr,
	})
}

func PullProgressCopier(target io.Writer) (io.WriteCloser, <-chan error) {
	reader, wrappedWriter := io.Pipe()
	errorC := make(chan error)
	go func() {
		finish := make(chan struct{})
		defer close(finish)
		defer close(errorC)

		mu := sync.Mutex{}
		lastMessage := jsonmessage.JSONMessage{}
		newMessage := false

		printMessage := func(m *jsonmessage.JSONMessage) {
			if m.ProgressMessage != "" {
				fmt.Fprintln(target, m.ID[:8], m.Status, m.ProgressMessage)
			} else if m.Progress != nil {
				fmt.Fprintln(target, m.ID[:8], m.Status, m.Progress.String())
			} else {
				m.Display(target, false)
			}
		}

		go func() {
			tick := time.Tick(1 * time.Second)
			for {
				select {
				case <-tick:
					mu.Lock()
					if newMessage {
						printMessage(&lastMessage)
						newMessage = false
					}
					mu.Unlock()

				case <-finish:
					return
				}
			}
		}()

		dec := json.NewDecoder(reader)
		for {
			tmp := jsonmessage.JSONMessage{}
			err := dec.Decode(&tmp)

			mu.Lock()
			if tmp.Error != nil || tmp.ErrorMessage != "" {
				tmp.Display(target, false)
				if tmp.Error != nil {
					errorC <- tmp.Error
				} else {
					errorC <- fmt.Errorf("%s", tmp.ErrorMessage)
				}
				return
			} else if tmp.Status != "Downloading" && tmp.Status != "Extracting" {
				printMessage(&tmp)
			} else {
				newMessage = true
				lastMessage = tmp
			}
			mu.Unlock()

			if err == io.EOF {
				return
			}
			if err != nil {
				log.Print("decode failure in", err)
				return
			}
		}
	}()
	return wrappedWriter, errorC
}
<commit_msg>Pass timeout and error writer to PrepBuildDirectory<commit_after>package source

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"sync"
	"time"

	"github.com/docker/docker/pkg/jsonmessage"
	docker "github.com/fsouza/go-dockerclient"
	git "github.com/sensiblecodeio/git-prep-directory"
)

type ImageSource interface {
	// Build/pull/fetch a docker image and return its name as a string
	Obtain(client *docker.Client, payload []byte) (string, error)
}

type CwdSource struct{}

func (CwdSource) Name() (string, error) {
	name, err := os.Getwd()
	if err != nil {
		return "", err
	}
	return filepath.Base(name), nil
}

func (s *CwdSource) Obtain(c *docker.Client, payload []byte) (string, error) {
	imageName, err := s.Name()
	if err != nil {
		return "", err
	}

	buildPath := "."
	err = DockerBuildDirectory(c, imageName, buildPath)
	if err != nil {
		return "", err
	}

	// Test for the presence of a 'runtime/Dockerfile' in the buildpath.
	// If it's there, then we run the image we just built, and use its
	// stdout as a build context
	if exists(filepath.Join(buildPath, "runtime", "Dockerfile")) {
		log.Printf("Generate runtime image")
		imageName, err = constructRuntime(c, imageName)
		if err != nil {
			return "", err
		}
	}

	return imageName, nil
}

type DockerPullSource struct {
	Repository, Tag string
}

var imageTag = regexp.MustCompile("^([^:]+):?(.*)$")

// DockerPullSourceFromImage creates a *DockerPullSource from an image name
// (with an optional tag)
func DockerPullSourceFromImage(image string) *DockerPullSource {
	parts := imageTag.FindStringSubmatch(image)
	if len(parts) != 2 {
		log.Panicf("imageTag regexp failed to match %q", image)
	}
	image, tag := parts[0], parts[1]
	return &DockerPullSource{image, tag}
}

func (s *DockerPullSource) Obtain(c *docker.Client, payload []byte) (string, error) {

	opts := docker.PullImageOptions{
		Repository:    s.Repository,
		Tag:           s.Tag,
		RawJSONStream: true,
	}

	// TODO(pwaller): Send the output somewhere better
	target := os.Stderr

	outputStream, errorC := PullProgressCopier(target)
	opts.OutputStream = outputStream

	// TODO(pwaller):
	// I don't use auth, just a daemon listening only on localhost,
	// so this remains unimplemented.
	var auth docker.AuthConfiguration
	err := c.PullImage(opts, auth)

	outputStream.Close()

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

	imageName := fmt.Sprintf("%s:%s", s.Repository, s.Tag)
	return imageName, <-errorC

	// c.PullImage(opts)
	// return , nil
	// return "", fmt.Errorf("not implemented: DockerPullSource.Obtain(%v, %v)", s.Repository, s.Tag)
}

type GitHostSource struct {
	Host          string
	User          string
	Repository    string
	InitialBranch string
	// Directory in which to do `docker build`.
	// Uses repository root if blank.
	ImageRoot string
}

func (s *GitHostSource) CloneURL() string {
	format := "https://%s/%s/%s"
	if HaveSSHKey() {
		format = "ssh://git@%s/%s/%s"
	}

	return fmt.Sprintf(format, s.Host, s.User, s.Repository)
}

// Return the git SHA from the given hook payload, if we have a hook payload,
// otherwise return the InitialBranch.
func (s *GitHostSource) Ref(payload []byte) (string, error) {
	if len(payload) == 0 {
		return s.InitialBranch, nil
	}

	var v struct {
		SHA string
	}

	err := json.Unmarshal(payload, &v)
	if err != nil {
		return "", err
	}

	return v.SHA, nil
}

func (s *GitHostSource) Obtain(c *docker.Client, payload []byte) (string, error) {
	// Obtain/update local mirrorformat

	ref, err := s.Ref(payload)
	if err != nil {
		return "", err
	}

	gitDir, err := filepath.Abs(filepath.Join(".", "src", s.Host, s.User, s.Repository))
	if err != nil {
		return "", err
	}

	build, err := git.PrepBuildDirectory(gitDir, s.CloneURL(), ref, 10*time.Minute, os.Stderr)
	if err != nil {
		return "", err
	}
	defer build.Cleanup()

	dockerImage := fmt.Sprintf("%s:%s", s.Repository, build.Name)
	buildPath := filepath.Join(build.Dir, s.ImageRoot)

	err = DockerBuildDirectory(c, dockerImage, buildPath)
	if err != nil {
		return "", err
	}

	// Test for the presence of a 'runtime/Dockerfile' in the buildpath.
	// If it's there, then we run the image we just built, and use its
	// stdout as a build context
	if exists(filepath.Join(buildPath, "runtime", "Dockerfile")) {
		dockerImage, err = constructRuntime(c, dockerImage)
		if err != nil {
			return "", err
		}
	}

	return dockerImage, nil
}

func constructRuntime(c *docker.Client, dockerImage string) (string, error) {
	stdout, err := DockerRun(c, dockerImage)
	if err != nil {
		return "", fmt.Errorf("run buildtime image: %v", err)
	}

	err = c.BuildImage(docker.BuildImageOptions{
		Name: dockerImage + "-runtime",
		// OutputStream is an io.Reader, but it gets typecast
		// to an io.ReadCloser and closes the body inside
		// net/http's Request type.
		// stdout is closed inside here.
		InputStream:  stdout,
		OutputStream: os.Stderr,
	})
	if err != nil {
		return "", fmt.Errorf("BuildImage -runtime: %v", err)
	}

	return dockerImage + "-runtime", nil
}

func DockerRun(c *docker.Client, imageName string) (io.ReadCloser, error) {
	cont, err := c.CreateContainer(docker.CreateContainerOptions{
		Config: &docker.Config{
			Hostname:     "generateruntimecontext",
			AttachStdout: true,
			AttachStderr: true,
			Image:        imageName,
			Labels: map[string]string{
				"orchestrator": "hanoverd",
				"purpose":      "Generate build context for runtime container",
			},
		},
	})
	if err != nil {
		log.Printf("Create container... failed: %v", err)
		return nil, err
	}

	r, w := io.Pipe()
	attached := make(chan struct{})
	detached := make(chan struct{})

	go func() {
		defer close(detached)

		err := c.AttachToContainer(docker.AttachToContainerOptions{
			Container:    cont.ID,
			OutputStream: w,
			ErrorStream:  os.Stderr,
			Logs:         true,
			Stdout:       true,
			Stderr:       true,
			Stream:       true,
			Success:      attached,
		})
		// io.Pipe hardwired to never return error here.
		_ = w.CloseWithError(err)
	}()

	select {
	case <-detached:
		// attachment failed
		log.Printf("Attachment failed")
		return nil, fmt.Errorf("Attachment failed")
	case <-attached:
		attached <- struct{}{}
	}

	err = c.StartContainer(cont.ID, &docker.HostConfig{})
	if err != nil {
		log.Printf("Start container... failed: %v", err)
		return nil, err
	}

	removeContainer := func() {
		err := c.RemoveContainer(docker.RemoveContainerOptions{
			ID:            cont.ID,
			RemoveVolumes: true,
			Force:         true,
		})
		if err != nil {
			log.Printf("Error removing intermediate container: %v", err)
		}
	}

	return struct {
		io.Reader
		io.Closer
	}{
		Reader: r,
		Closer: CloseFunc(func() error {
			defer removeContainer()

			status, err := c.WaitContainer(cont.ID)
			if err != nil {
				return err
			}
			if status != 0 {
				return fmt.Errorf("non-zero exit status: %v", err)
			}
			return nil
		}),
	}, err
}

type CloseFunc func() error

func (fn CloseFunc) Close() error { return fn() }

func exists(filename string) bool {
	_, err := os.Stat(filename)
	switch {
	case err == nil:
		return true
	default:
		log.Printf("Error checking for the existence of %q: %v", filename, err)
	case os.IsNotExist(err):
	}
	return false
}

// Returns true if $HOME/.ssh exists, false otherwise
func HaveSSHKey() bool {
	keys := []string{"id_dsa", "id_ecdsa", "id_rsa", "id_ed25519"}
	for _, filename := range keys {
		path := os.ExpandEnv(filepath.Join("$HOME/.ssh", filename))
		if _, err := os.Stat(path); err == nil {
			return true
		}
	}
	return false
}

func DockerBuildDirectory(c *docker.Client, name, path string) error {
	return c.BuildImage(docker.BuildImageOptions{
		Name:         name,
		ContextDir:   path,
		OutputStream: os.Stderr,
	})
}

func PullProgressCopier(target io.Writer) (io.WriteCloser, <-chan error) {
	reader, wrappedWriter := io.Pipe()
	errorC := make(chan error)
	go func() {
		finish := make(chan struct{})
		defer close(finish)
		defer close(errorC)

		mu := sync.Mutex{}
		lastMessage := jsonmessage.JSONMessage{}
		newMessage := false

		printMessage := func(m *jsonmessage.JSONMessage) {
			if m.ProgressMessage != "" {
				fmt.Fprintln(target, m.ID[:8], m.Status, m.ProgressMessage)
			} else if m.Progress != nil {
				fmt.Fprintln(target, m.ID[:8], m.Status, m.Progress.String())
			} else {
				m.Display(target, false)
			}
		}

		go func() {
			tick := time.Tick(1 * time.Second)
			for {
				select {
				case <-tick:
					mu.Lock()
					if newMessage {
						printMessage(&lastMessage)
						newMessage = false
					}
					mu.Unlock()

				case <-finish:
					return
				}
			}
		}()

		dec := json.NewDecoder(reader)
		for {
			tmp := jsonmessage.JSONMessage{}
			err := dec.Decode(&tmp)

			mu.Lock()
			if tmp.Error != nil || tmp.ErrorMessage != "" {
				tmp.Display(target, false)
				if tmp.Error != nil {
					errorC <- tmp.Error
				} else {
					errorC <- fmt.Errorf("%s", tmp.ErrorMessage)
				}
				return
			} else if tmp.Status != "Downloading" && tmp.Status != "Extracting" {
				printMessage(&tmp)
			} else {
				newMessage = true
				lastMessage = tmp
			}
			mu.Unlock()

			if err == io.EOF {
				return
			}
			if err != nil {
				log.Print("decode failure in", err)
				return
			}
		}
	}()
	return wrappedWriter, errorC
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"testing"
	"net/url"
)

func TestParseUrlQuery(t *testing.T) {
	request := "http://127.0.0.1:3000/announce?info_hash=QtA%C0%81%8D%C5GV%02%150%5D%2B%91%80a%BB%02%9A&peer_id=-lt0D20-s%081%8ER%D7%C9%15X%DB%DD%D2&key=602bcd6f&compact=1&port=6963&uploaded=0&downloaded=0&left=5448254&event=started"

	result := decodeQueryURL(request)
	if result["uploaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["uploaded"])
	}
	if result["port"][0] != "6963" {
		t.Fatalf("Expected 0, got %s", result["port"])
	}
	if result["downloaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["downloaded"])
	}
	if result["compact"][0] != "1" {
		t.Fatalf("Expected 0, got %s", result["compact"])
	}

}

func TestParseTorrentGetRequest(t *testing.T) {
	request := "http://127.0.0.1:3000/announce?info_hash=QtA%C0%81%8D%C5GV%02%150%5D%2B%91%80a%BB%02%9A&peer_id=-lt0D20-s%081%8ER%D7%C9%15X%DB%DD%D2&key=602bcd6f&compact=1&port=6963&uploaded=0&downloaded=0&left=5448254&event=started"

	result := decodeQueryURL(request)
	fmt.Println(result)
	if result["uploaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["uploaded"])
	}
	if result["port"][0] != "6963" {
		t.Fatalf("Expected 0, got %s", result["port"])
	}
	if result["downloaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["downloaded"])
	}
	if result["compact"][0] != "1" {
		t.Fatalf("Expected 0, got %s", result["compact"])
	}
}

func TestParseInfoHash(t *testing.T) {
	expectedResult := "4925623525306625313825326325633425313825396325383925316325396559732563382566346725376225623359253137"
	result := ParseInfoHash("I%b5%0f%18%2c%c4%18%9c%89%1c%9eYs%c8%f4g%7b%b3Y%17")

	if result != expectedResult {
		t.Fatalf("Expected %s, got %s", expectedResult, result)
	}
}

func TestGetIntFailEmptyKey(t *testing.T) {
	u, _ := url.Parse("http://google.com/")
	urlValues := u.Query()
	key := "testInt"

	expectedResult := 50
	result, err := GetInt(urlValues, key)
	if err != nil {
		t.Fatalf("Failed to GetInt() with %v", err)
	}

	if result == expectedResult {
		t.Fatalf("Expected %s, got %s", expectedResult, result)
	}
}

func TestGetInt(t *testing.T) {
	u, _ := url.Parse("http://google.com/?testInt=50")
	urlValues := u.Query()
	key := "testInt"

	expectedResult := 50
	result, err := GetInt(urlValues, key)
	if err != nil {
		t.Fatalf("Failed to GetInt() with %v", err)
	}

	if result != expectedResult || err != nil {
		t.Fatalf("Expected %s, got %s", expectedResult, result)
	}
}

<commit_msg>Update server_test.go<commit_after>package server

import (
	"fmt"
	"testing"
	"net/url"
)

func TestParseUrlQuery(t *testing.T) {
	request := "http://127.0.0.1:3000/announce?info_hash=QtA%C0%81%8D%C5GV%02%150%5D%2B%91%80a%BB%02%9A&peer_id=-lt0D20-s%081%8ER%D7%C9%15X%DB%DD%D2&key=602bcd6f&compact=1&port=6963&uploaded=0&downloaded=0&left=5448254&event=started"

	result := decodeQueryURL(request)
	if result["uploaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["uploaded"])
	}
	if result["port"][0] != "6963" {
		t.Fatalf("Expected 0, got %s", result["port"])
	}
	if result["downloaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["downloaded"])
	}
	if result["compact"][0] != "1" {
		t.Fatalf("Expected 0, got %s", result["compact"])
	}

}

func TestParseTorrentGetRequest(t *testing.T) {
	request := "http://127.0.0.1:3000/announce?info_hash=QtA%C0%81%8D%C5GV%02%150%5D%2B%91%80a%BB%02%9A&peer_id=-lt0D20-s%081%8ER%D7%C9%15X%DB%DD%D2&key=602bcd6f&compact=1&port=6963&uploaded=0&downloaded=0&left=5448254&event=started"

	result := decodeQueryURL(request)
	fmt.Println(result)
	if result["uploaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["uploaded"])
	}
	if result["port"][0] != "6963" {
		t.Fatalf("Expected 0, got %s", result["port"])
	}
	if result["downloaded"][0] != "0" {
		t.Fatalf("Expected 0, got %s", result["downloaded"])
	}
	if result["compact"][0] != "1" {
		t.Fatalf("Expected 0, got %s", result["compact"])
	}
}

func TestParseInfoHash(t *testing.T) {
	expectedResult := "4925623525306625313825326325633425313825396325383925316325396559732563382566346725376225623359253137"
	result := ParseInfoHash("I%b5%0f%18%2c%c4%18%9c%89%1c%9eYs%c8%f4g%7b%b3Y%17")

	if result != expectedResult {
		t.Fatalf("Expected %s, got %s", expectedResult, result)
	}
}

func TestGetIntFailEmptyKey(t *testing.T) {
	u, _ := url.Parse("http://google.com/")
	urlValues := u.Query()
	key := "testInt"

	expectedResult := int64(50)
	result, err := GetInt(urlValues, key)
	if err != nil {
		t.Fatalf("Failed to GetInt() with %v", err)
	}

	if result == expectedResult {
		t.Fatalf("Expected %s, got %s", expectedResult, result)
	}
}

func TestGetInt(t *testing.T) {
	u, _ := url.Parse("http://google.com/?testInt=50")
	urlValues := u.Query()
	key := "testInt"

	expectedResult := int64(50)
	result, err := GetInt(urlValues, key)
	if err != nil {
		t.Fatalf("Failed to GetInt() with %v", err)
	}

	if result != expectedResult || err != nil {
		t.Fatalf("Expected %s, got %s", expectedResult, result)
	}
}

<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

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

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

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

package diff

import (
	"bytes"
	"encoding/json"
	"fmt"
	"reflect"
	"sort"
	"strings"
	"text/tabwriter"

	"github.com/davecgh/go-spew/spew"

	"k8s.io/apimachinery/pkg/util/validation/field"
)

// StringDiff diffs a and b and returns a human readable diff.
func StringDiff(a, b string) string {
	ba := []byte(a)
	bb := []byte(b)
	out := []byte{}
	i := 0
	for ; i < len(ba) && i < len(bb); i++ {
		if ba[i] != bb[i] {
			break
		}
		out = append(out, ba[i])
	}
	out = append(out, []byte("\n\nA: ")...)
	out = append(out, ba[i:]...)
	out = append(out, []byte("\n\nB: ")...)
	out = append(out, bb[i:]...)
	out = append(out, []byte("\n\n")...)
	return string(out)
}

// ObjectDiff writes the two objects out as JSON and prints out the identical part of
// the objects followed by the remaining part of 'a' and finally the remaining part of 'b'.
// For debugging tests.
func ObjectDiff(a, b interface{}) string {
	ab, err := json.Marshal(a)
	if err != nil {
		panic(fmt.Sprintf("a: %v", err))
	}
	bb, err := json.Marshal(b)
	if err != nil {
		panic(fmt.Sprintf("b: %v", err))
	}
	return StringDiff(string(ab), string(bb))
}

// ObjectGoPrintDiff is like ObjectDiff, but uses go-spew to print the objects,
// which shows absolutely everything by recursing into every single pointer
// (go's %#v formatters OTOH stop at a certain point). This is needed when you
// can't figure out why reflect.DeepEqual is returning false and nothing is
// showing you differences. This will.
func ObjectGoPrintDiff(a, b interface{}) string {
	s := spew.ConfigState{DisableMethods: true}
	return StringDiff(
		s.Sprintf("%#v", a),
		s.Sprintf("%#v", b),
	)
}

func ObjectReflectDiff(a, b interface{}) string {
	vA, vB := reflect.ValueOf(a), reflect.ValueOf(b)
	if vA.Type() != vB.Type() {
		return fmt.Sprintf("type A %T and type B %T do not match", a, b)
	}
	diffs := objectReflectDiff(field.NewPath("object"), vA, vB)
	if len(diffs) == 0 {
		return "<no diffs>"
	}
	out := []string{""}
	for _, d := range diffs {
		out = append(out,
			fmt.Sprintf("%s:", d.path),
			limit(fmt.Sprintf("  a: %#v", d.a), 80),
			limit(fmt.Sprintf("  b: %#v", d.b), 80),
		)
	}
	return strings.Join(out, "\n")
}

func limit(s string, max int) string {
	if len(s) > max {
		return s[:max]
	}
	return s
}

func public(s string) bool {
	if len(s) == 0 {
		return false
	}
	return s[:1] == strings.ToUpper(s[:1])
}

type diff struct {
	path *field.Path
	a, b interface{}
}

type orderedDiffs []diff

func (d orderedDiffs) Len() int      { return len(d) }
func (d orderedDiffs) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
func (d orderedDiffs) Less(i, j int) bool {
	a, b := d[i].path.String(), d[j].path.String()
	if a < b {
		return true
	}
	return false
}

func objectReflectDiff(path *field.Path, a, b reflect.Value) []diff {
	switch a.Type().Kind() {
	case reflect.Struct:
		var changes []diff
		for i := 0; i < a.Type().NumField(); i++ {
			if !public(a.Type().Field(i).Name) {
				if reflect.DeepEqual(a.Interface(), b.Interface()) {
					continue
				}
				return []diff{{path: path, a: fmt.Sprintf("%#v", a), b: fmt.Sprintf("%#v", b)}}
			}
			if sub := objectReflectDiff(path.Child(a.Type().Field(i).Name), a.Field(i), b.Field(i)); len(sub) > 0 {
				changes = append(changes, sub...)
			} else {
				if !reflect.DeepEqual(a.Field(i).Interface(), b.Field(i).Interface()) {
					changes = append(changes, diff{path: path, a: a.Field(i).Interface(), b: b.Field(i).Interface()})
				}
			}
		}
		return changes
	case reflect.Ptr, reflect.Interface:
		if a.IsNil() || b.IsNil() {
			switch {
			case a.IsNil() && b.IsNil():
				return nil
			case a.IsNil():
				return []diff{{path: path, a: nil, b: b.Interface()}}
			default:
				return []diff{{path: path, a: a.Interface(), b: nil}}
			}
		}
		return objectReflectDiff(path, a.Elem(), b.Elem())
	case reflect.Chan:
		if !reflect.DeepEqual(a.Interface(), b.Interface()) {
			return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
		}
		return nil
	case reflect.Slice:
		lA, lB := a.Len(), b.Len()
		l := lA
		if lB < lA {
			l = lB
		}
		if lA == lB && lA == 0 {
			if a.IsNil() != b.IsNil() {
				return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
			}
			return nil
		}
		for i := 0; i < l; i++ {
			if !reflect.DeepEqual(a.Index(i), b.Index(i)) {
				return objectReflectDiff(path.Index(i), a.Index(i), b.Index(i))
			}
		}
		var diffs []diff
		for i := l; i < lA; i++ {
			diffs = append(diffs, diff{path: path.Index(i), a: a.Index(i), b: nil})
		}
		for i := l; i < lB; i++ {
			diffs = append(diffs, diff{path: path.Index(i), a: nil, b: b.Index(i)})
		}
		if len(diffs) == 0 {
			diffs = append(diffs, diff{path: path, a: a, b: b})
		}
		return diffs
	case reflect.Map:
		if reflect.DeepEqual(a.Interface(), b.Interface()) {
			return nil
		}
		aKeys := make(map[interface{}]interface{})
		for _, key := range a.MapKeys() {
			aKeys[key.Interface()] = a.MapIndex(key).Interface()
		}
		var missing []diff
		for _, key := range b.MapKeys() {
			if _, ok := aKeys[key.Interface()]; ok {
				delete(aKeys, key.Interface())
				if reflect.DeepEqual(a.MapIndex(key).Interface(), b.MapIndex(key).Interface()) {
					continue
				}
				missing = append(missing, objectReflectDiff(path.Key(fmt.Sprintf("%s", key.Interface())), a.MapIndex(key), b.MapIndex(key))...)
				continue
			}
			missing = append(missing, diff{path: path.Key(fmt.Sprintf("%s", key.Interface())), a: nil, b: b.MapIndex(key).Interface()})
		}
		for key, value := range aKeys {
			missing = append(missing, diff{path: path.Key(fmt.Sprintf("%s", key)), a: value, b: nil})
		}
		if len(missing) == 0 {
			missing = append(missing, diff{path: path, a: a.Interface(), b: b.Interface()})
		}
		sort.Sort(orderedDiffs(missing))
		return missing
	default:
		if reflect.DeepEqual(a.Interface(), b.Interface()) {
			return nil
		}
		if !a.CanInterface() {
			return []diff{{path: path, a: fmt.Sprintf("%#v", a), b: fmt.Sprintf("%#v", b)}}
		}
		return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
	}
}

// ObjectGoPrintSideBySide prints a and b as textual dumps side by side,
// enabling easy visual scanning for mismatches.
func ObjectGoPrintSideBySide(a, b interface{}) string {
	s := spew.ConfigState{
		Indent: " ",
		// Extra deep spew.
		DisableMethods: true,
	}
	sA := s.Sdump(a)
	sB := s.Sdump(b)

	linesA := strings.Split(sA, "\n")
	linesB := strings.Split(sB, "\n")
	width := 0
	for _, s := range linesA {
		l := len(s)
		if l > width {
			width = l
		}
	}
	for _, s := range linesB {
		l := len(s)
		if l > width {
			width = l
		}
	}
	buf := &bytes.Buffer{}
	w := tabwriter.NewWriter(buf, width, 0, 1, ' ', 0)
	max := len(linesA)
	if len(linesB) > max {
		max = len(linesB)
	}
	for i := 0; i < max; i++ {
		var a, b string
		if i < len(linesA) {
			a = linesA[i]
		}
		if i < len(linesB) {
			b = linesB[i]
		}
		fmt.Fprintf(w, "%s\t%s\n", a, b)
	}
	w.Flush()
	return buf.String()
}
<commit_msg>Don't exit early in diff.ObjectReflectDiff on slices<commit_after>/*
Copyright 2014 The Kubernetes Authors.

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

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

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

package diff

import (
	"bytes"
	"encoding/json"
	"fmt"
	"reflect"
	"sort"
	"strings"
	"text/tabwriter"

	"github.com/davecgh/go-spew/spew"

	"k8s.io/apimachinery/pkg/util/validation/field"
)

// StringDiff diffs a and b and returns a human readable diff.
func StringDiff(a, b string) string {
	ba := []byte(a)
	bb := []byte(b)
	out := []byte{}
	i := 0
	for ; i < len(ba) && i < len(bb); i++ {
		if ba[i] != bb[i] {
			break
		}
		out = append(out, ba[i])
	}
	out = append(out, []byte("\n\nA: ")...)
	out = append(out, ba[i:]...)
	out = append(out, []byte("\n\nB: ")...)
	out = append(out, bb[i:]...)
	out = append(out, []byte("\n\n")...)
	return string(out)
}

// ObjectDiff writes the two objects out as JSON and prints out the identical part of
// the objects followed by the remaining part of 'a' and finally the remaining part of 'b'.
// For debugging tests.
func ObjectDiff(a, b interface{}) string {
	ab, err := json.Marshal(a)
	if err != nil {
		panic(fmt.Sprintf("a: %v", err))
	}
	bb, err := json.Marshal(b)
	if err != nil {
		panic(fmt.Sprintf("b: %v", err))
	}
	return StringDiff(string(ab), string(bb))
}

// ObjectGoPrintDiff is like ObjectDiff, but uses go-spew to print the objects,
// which shows absolutely everything by recursing into every single pointer
// (go's %#v formatters OTOH stop at a certain point). This is needed when you
// can't figure out why reflect.DeepEqual is returning false and nothing is
// showing you differences. This will.
func ObjectGoPrintDiff(a, b interface{}) string {
	s := spew.ConfigState{DisableMethods: true}
	return StringDiff(
		s.Sprintf("%#v", a),
		s.Sprintf("%#v", b),
	)
}

func ObjectReflectDiff(a, b interface{}) string {
	vA, vB := reflect.ValueOf(a), reflect.ValueOf(b)
	if vA.Type() != vB.Type() {
		return fmt.Sprintf("type A %T and type B %T do not match", a, b)
	}
	diffs := objectReflectDiff(field.NewPath("object"), vA, vB)
	if len(diffs) == 0 {
		return "<no diffs>"
	}
	out := []string{""}
	for _, d := range diffs {
		out = append(out,
			fmt.Sprintf("%s:", d.path),
			limit(fmt.Sprintf("  a: %#v", d.a), 80),
			limit(fmt.Sprintf("  b: %#v", d.b), 80),
		)
	}
	return strings.Join(out, "\n")
}

func limit(s string, max int) string {
	if len(s) > max {
		return s[:max]
	}
	return s
}

func public(s string) bool {
	if len(s) == 0 {
		return false
	}
	return s[:1] == strings.ToUpper(s[:1])
}

type diff struct {
	path *field.Path
	a, b interface{}
}

type orderedDiffs []diff

func (d orderedDiffs) Len() int      { return len(d) }
func (d orderedDiffs) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
func (d orderedDiffs) Less(i, j int) bool {
	a, b := d[i].path.String(), d[j].path.String()
	if a < b {
		return true
	}
	return false
}

func objectReflectDiff(path *field.Path, a, b reflect.Value) []diff {
	switch a.Type().Kind() {
	case reflect.Struct:
		var changes []diff
		for i := 0; i < a.Type().NumField(); i++ {
			if !public(a.Type().Field(i).Name) {
				if reflect.DeepEqual(a.Interface(), b.Interface()) {
					continue
				}
				return []diff{{path: path, a: fmt.Sprintf("%#v", a), b: fmt.Sprintf("%#v", b)}}
			}
			if sub := objectReflectDiff(path.Child(a.Type().Field(i).Name), a.Field(i), b.Field(i)); len(sub) > 0 {
				changes = append(changes, sub...)
			}
		}
		return changes
	case reflect.Ptr, reflect.Interface:
		if a.IsNil() || b.IsNil() {
			switch {
			case a.IsNil() && b.IsNil():
				return nil
			case a.IsNil():
				return []diff{{path: path, a: nil, b: b.Interface()}}
			default:
				return []diff{{path: path, a: a.Interface(), b: nil}}
			}
		}
		return objectReflectDiff(path, a.Elem(), b.Elem())
	case reflect.Chan:
		if !reflect.DeepEqual(a.Interface(), b.Interface()) {
			return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
		}
		return nil
	case reflect.Slice:
		lA, lB := a.Len(), b.Len()
		l := lA
		if lB < lA {
			l = lB
		}
		if lA == lB && lA == 0 {
			if a.IsNil() != b.IsNil() {
				return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
			}
			return nil
		}
		var diffs []diff
		for i := 0; i < l; i++ {
			if !reflect.DeepEqual(a.Index(i), b.Index(i)) {
				diffs = append(diffs, objectReflectDiff(path.Index(i), a.Index(i), b.Index(i))...)
			}
		}
		for i := l; i < lA; i++ {
			diffs = append(diffs, diff{path: path.Index(i), a: a.Index(i), b: nil})
		}
		for i := l; i < lB; i++ {
			diffs = append(diffs, diff{path: path.Index(i), a: nil, b: b.Index(i)})
		}
		return diffs
	case reflect.Map:
		if reflect.DeepEqual(a.Interface(), b.Interface()) {
			return nil
		}
		aKeys := make(map[interface{}]interface{})
		for _, key := range a.MapKeys() {
			aKeys[key.Interface()] = a.MapIndex(key).Interface()
		}
		var missing []diff
		for _, key := range b.MapKeys() {
			if _, ok := aKeys[key.Interface()]; ok {
				delete(aKeys, key.Interface())
				if reflect.DeepEqual(a.MapIndex(key).Interface(), b.MapIndex(key).Interface()) {
					continue
				}
				missing = append(missing, objectReflectDiff(path.Key(fmt.Sprintf("%s", key.Interface())), a.MapIndex(key), b.MapIndex(key))...)
				continue
			}
			missing = append(missing, diff{path: path.Key(fmt.Sprintf("%s", key.Interface())), a: nil, b: b.MapIndex(key).Interface()})
		}
		for key, value := range aKeys {
			missing = append(missing, diff{path: path.Key(fmt.Sprintf("%s", key)), a: value, b: nil})
		}
		if len(missing) == 0 {
			missing = append(missing, diff{path: path, a: a.Interface(), b: b.Interface()})
		}
		sort.Sort(orderedDiffs(missing))
		return missing
	default:
		if reflect.DeepEqual(a.Interface(), b.Interface()) {
			return nil
		}
		if !a.CanInterface() {
			return []diff{{path: path, a: fmt.Sprintf("%#v", a), b: fmt.Sprintf("%#v", b)}}
		}
		return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
	}
}

// ObjectGoPrintSideBySide prints a and b as textual dumps side by side,
// enabling easy visual scanning for mismatches.
func ObjectGoPrintSideBySide(a, b interface{}) string {
	s := spew.ConfigState{
		Indent: " ",
		// Extra deep spew.
		DisableMethods: true,
	}
	sA := s.Sdump(a)
	sB := s.Sdump(b)

	linesA := strings.Split(sA, "\n")
	linesB := strings.Split(sB, "\n")
	width := 0
	for _, s := range linesA {
		l := len(s)
		if l > width {
			width = l
		}
	}
	for _, s := range linesB {
		l := len(s)
		if l > width {
			width = l
		}
	}
	buf := &bytes.Buffer{}
	w := tabwriter.NewWriter(buf, width, 0, 1, ' ', 0)
	max := len(linesA)
	if len(linesB) > max {
		max = len(linesB)
	}
	for i := 0; i < max; i++ {
		var a, b string
		if i < len(linesA) {
			a = linesA[i]
		}
		if i < len(linesB) {
			b = linesB[i]
		}
		fmt.Fprintf(w, "%s\t%s\n", a, b)
	}
	w.Flush()
	return buf.String()
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors All rights reserved.

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 lock

import (
	"io/ioutil"
	"os"
	"time"

	"github.com/golang/glog"
	"github.com/juju/fslock"
	"github.com/pkg/errors"
	"k8s.io/minikube/pkg/util/retry"
)

// WriteWithLock decorates ioutil.WriteFile with a file lock and retry
func WriteFile(filename string, data []byte, perm os.FileMode) error {
	lock := fslock.New(filename)

	getLock := func() error {
		lockErr := lock.TryLock()
		if lockErr != nil {
			glog.Infof("temproary error : %v", lockErr.Error())
			return errors.Wrapf(lockErr, "falied to acquire file lock for %s > ", filename)
		}
		return nil
	}
	err := retry.Expo(getLock, 500*time.Millisecond, 13*time.Second)
	if err != nil {
		return errors.Wrapf(err, "acquiring file lock for %s", filename)
	}

	if err := ioutil.WriteFile(filename, data, perm); err != nil {
		return errors.Wrapf(err, "error writing file %s", filename)
	}

	// release the lock
	err = lock.Unlock()
	if err != nil {
		return errors.Wrapf(err, "error releasing lock for file: %s", filename)
	}
	return nil
}
<commit_msg>defer release the lock<commit_after>/*
Copyright 2019 The Kubernetes Authors All rights reserved.

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 lock

import (
	"io/ioutil"
	"os"
	"time"

	"github.com/golang/glog"
	"github.com/juju/fslock"
	"github.com/pkg/errors"
	"k8s.io/minikube/pkg/util/retry"
)

// WriteWithLock decorates ioutil.WriteFile with a file lock and retry
func WriteFile(filename string, data []byte, perm os.FileMode) (err error) {
	lock := fslock.New(filename)
	glog.Infof("attempting to write to file %q with filemode %v", filename, perm)

	getLock := func() error {
		lockErr := lock.TryLock()
		if lockErr != nil {
			glog.Warningf("temporary error : %v", lockErr.Error())
			return errors.Wrapf(lockErr, "falied to acquire lock for %s > ", filename)
		}
		return nil
	}

	defer func() { // release the lock
		err = lock.Unlock()
		if err != nil {
			err = errors.Wrapf(err, "error releasing lock for file: %s", filename)
		}
	}()

	err = retry.Expo(getLock, 500*time.Millisecond, 13*time.Second)
	if err != nil {
		return errors.Wrapf(err, "error acquiring lock for %s", filename)
	}

	if err = ioutil.WriteFile(filename, data, perm); err != nil {
		return errors.Wrapf(err, "error writing file %s", filename)
	}

	return err
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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 wait

import (
	"errors"
	"math/rand"
	"time"

	"k8s.io/kubernetes/pkg/util/runtime"
)

// For any test of the style:
//   ...
//   <- time.After(timeout):
//      t.Errorf("Timed out")
// The value for timeout should effectively be "forever." Obviously we don't want our tests to truly lock up forever, but 30s
// is long enough that it is effectively forever for the things that can slow down a run on a heavily contended machine
// (GC, seeks, etc), but not so long as to make a developer ctrl-c a test run if they do happen to break that test.
var ForeverTestTimeout = time.Second * 30

// NeverStop may be passed to Until to make it never stop.
var NeverStop <-chan struct{} = make(chan struct{})

// Forever is syntactic sugar on top of Until
func Forever(f func(), period time.Duration) {
	Until(f, period, NeverStop)
}

// Until loops until stop channel is closed, running f every period.
// Catches any panics, and keeps going. f may not be invoked if
// stop channel is already closed. Pass NeverStop to Until if you
// don't want it stop.
func Until(f func(), period time.Duration, stopCh <-chan struct{}) {
	select {
	case <-stopCh:
		return
	default:
	}

	for {
		func() {
			defer runtime.HandleCrash()
			f()
		}()
		select {
		case <-stopCh:
			return
		case <-time.After(period):
		}
	}
}

// Jitter returns a time.Duration between duration and duration + maxFactor * duration,
// to allow clients to avoid converging on periodic behavior.  If maxFactor is 0.0, a
// suggested default value will be chosen.
func Jitter(duration time.Duration, maxFactor float64) time.Duration {
	if maxFactor <= 0.0 {
		maxFactor = 1.0
	}
	wait := duration + time.Duration(rand.Float64()*maxFactor*float64(duration))
	return wait
}

// ErrWaitTimeout is returned when the condition exited without success
var ErrWaitTimeout = errors.New("timed out waiting for the condition")

// ConditionFunc returns true if the condition is satisfied, or an error
// if the loop should be aborted.
type ConditionFunc func() (done bool, err error)

// Backoff is parameters applied to a Backoff function.
type Backoff struct {
	Duration time.Duration
	Factor   float64
	Jitter   float64
	Steps    int
}

// ExponentialBackoff repeats a condition check up to steps times, increasing the wait
// by multipling the previous duration by factor. If jitter is greater than zero,
// a random amount of each duration is added (between duration and duration*(1+jitter)).
// If the condition never returns true, ErrWaitTimeout is returned. All other errors
// terminate immediately.
func ExponentialBackoff(backoff Backoff, condition ConditionFunc) error {
	duration := backoff.Duration
	for i := 0; i < backoff.Steps; i++ {
		if i != 0 {
			adjusted := duration
			if backoff.Jitter > 0.0 {
				adjusted = Jitter(duration, backoff.Jitter)
			}
			time.Sleep(adjusted)
			duration = time.Duration(float64(duration) * backoff.Factor)
		}
		if ok, err := condition(); err != nil || ok {
			return err
		}
	}
	return ErrWaitTimeout
}

// Poll tries a condition func until it returns true, an error, or the timeout
// is reached. condition will always be invoked at least once but some intervals
// may be missed if the condition takes too long or the time window is too short.
// If you want to Poll something forever, see PollInfinite.
// Poll always waits the interval before the first check of the condition.
func Poll(interval, timeout time.Duration, condition ConditionFunc) error {
	return pollInternal(poller(interval, timeout), condition)
}

func pollInternal(wait WaitFunc, condition ConditionFunc) error {
	done := make(chan struct{})
	defer close(done)
	return WaitFor(wait, condition, done)
}

func PollImmediate(interval, timeout time.Duration, condition ConditionFunc) error {
	return pollImmediateInternal(poller(interval, timeout), condition)
}

func pollImmediateInternal(wait WaitFunc, condition ConditionFunc) error {
	done, err := condition()
	if err != nil {
		return err
	}
	if done {
		return nil
	}
	return pollInternal(wait, condition)
}

// PollInfinite polls forever.
func PollInfinite(interval time.Duration, condition ConditionFunc) error {
	done := make(chan struct{})
	defer close(done)
	return WaitFor(poller(interval, 0), condition, done)
}

// WaitFunc creates a channel that receives an item every time a test
// should be executed and is closed when the last test should be invoked.
type WaitFunc func(done <-chan struct{}) <-chan struct{}

// WaitFor gets a channel from wait(), and then invokes fn once for every value
// placed on the channel and once more when the channel is closed.  If fn
// returns an error the loop ends and that error is returned, and if fn returns
// true the loop ends and nil is returned. ErrWaitTimeout will be returned if
// the channel is closed without fn ever returning true.
func WaitFor(wait WaitFunc, fn ConditionFunc, done <-chan struct{}) error {
	c := wait(done)
	for {
		_, open := <-c
		ok, err := fn()
		if err != nil {
			return err
		}
		if ok {
			return nil
		}
		if !open {
			break
		}
	}
	return ErrWaitTimeout
}

// poller returns a WaitFunc that will send to the channel every
// interval until timeout has elapsed and then close the channel.
// Over very short intervals you may receive no ticks before
// the channel is closed.  If timeout is 0, the channel
// will never be closed.
func poller(interval, timeout time.Duration) WaitFunc {
	return WaitFunc(func(done <-chan struct{}) <-chan struct{} {
		ch := make(chan struct{})

		go func() {
			defer close(ch)

			tick := time.NewTicker(interval)
			defer tick.Stop()

			var after <-chan time.Time
			if timeout != 0 {
				// time.After is more convenient, but it
				// potentially leaves timers around much longer
				// than necessary if we exit early.
				timer := time.NewTimer(timeout)
				after = timer.C
				defer timer.Stop()
			}

			for {
				select {
				case <-tick.C:
					// If the consumer isn't ready for this signal drop it and
					// check the other channels.
					select {
					case ch <- struct{}{}:
					default:
					}
				case <-after:
					return
				case <-done:
					return
				}
			}
		}()

		return ch
	})
}
<commit_msg>jitter period in each run of Until<commit_after>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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 wait

import (
	"errors"
	"math/rand"
	"time"

	"k8s.io/kubernetes/pkg/util/runtime"
)

// For any test of the style:
//   ...
//   <- time.After(timeout):
//      t.Errorf("Timed out")
// The value for timeout should effectively be "forever." Obviously we don't want our tests to truly lock up forever, but 30s
// is long enough that it is effectively forever for the things that can slow down a run on a heavily contended machine
// (GC, seeks, etc), but not so long as to make a developer ctrl-c a test run if they do happen to break that test.
var ForeverTestTimeout = time.Second * 30

// NeverStop may be passed to Until to make it never stop.
var NeverStop <-chan struct{} = make(chan struct{})

// Forever is syntactic sugar on top of Until
func Forever(f func(), period time.Duration) {
	Until(f, period, NeverStop)
}

// Until loops until stop channel is closed, running f every period.
// Until is syntactic sugar on top of JitterUntil with zero jitter factor
func Until(f func(), period time.Duration, stopCh <-chan struct{}) {
	JitterUntil(f, period, 0.0, stopCh)
}

// JitterUntil loops until stop channel is closed, running f every period.
// If jitterFactor is positive, the period is jittered before every run of f.
// If jitterFactor is not positive, the period is unchanged.
// Catches any panics, and keeps going. f may not be invoked if
// stop channel is already closed. Pass NeverStop to Until if you
// don't want it stop.
func JitterUntil(f func(), period time.Duration, jitterFactor float64, stopCh <-chan struct{}) {
	select {
	case <-stopCh:
		return
	default:
	}

	for {
		func() {
			defer runtime.HandleCrash()
			f()
		}()

		jitteredPeriod := period
		if jitterFactor > 0.0 {
			jitteredPeriod = Jitter(period, jitterFactor)
		}

		select {
		case <-stopCh:
			return
		case <-time.After(jitteredPeriod):
		}
	}
}

// Jitter returns a time.Duration between duration and duration + maxFactor * duration,
// to allow clients to avoid converging on periodic behavior.  If maxFactor is 0.0, a
// suggested default value will be chosen.
func Jitter(duration time.Duration, maxFactor float64) time.Duration {
	if maxFactor <= 0.0 {
		maxFactor = 1.0
	}
	wait := duration + time.Duration(rand.Float64()*maxFactor*float64(duration))
	return wait
}

// ErrWaitTimeout is returned when the condition exited without success
var ErrWaitTimeout = errors.New("timed out waiting for the condition")

// ConditionFunc returns true if the condition is satisfied, or an error
// if the loop should be aborted.
type ConditionFunc func() (done bool, err error)

// Backoff is parameters applied to a Backoff function.
type Backoff struct {
	Duration time.Duration
	Factor   float64
	Jitter   float64
	Steps    int
}

// ExponentialBackoff repeats a condition check up to steps times, increasing the wait
// by multipling the previous duration by factor. If jitter is greater than zero,
// a random amount of each duration is added (between duration and duration*(1+jitter)).
// If the condition never returns true, ErrWaitTimeout is returned. All other errors
// terminate immediately.
func ExponentialBackoff(backoff Backoff, condition ConditionFunc) error {
	duration := backoff.Duration
	for i := 0; i < backoff.Steps; i++ {
		if i != 0 {
			adjusted := duration
			if backoff.Jitter > 0.0 {
				adjusted = Jitter(duration, backoff.Jitter)
			}
			time.Sleep(adjusted)
			duration = time.Duration(float64(duration) * backoff.Factor)
		}
		if ok, err := condition(); err != nil || ok {
			return err
		}
	}
	return ErrWaitTimeout
}

// Poll tries a condition func until it returns true, an error, or the timeout
// is reached. condition will always be invoked at least once but some intervals
// may be missed if the condition takes too long or the time window is too short.
// If you want to Poll something forever, see PollInfinite.
// Poll always waits the interval before the first check of the condition.
func Poll(interval, timeout time.Duration, condition ConditionFunc) error {
	return pollInternal(poller(interval, timeout), condition)
}

func pollInternal(wait WaitFunc, condition ConditionFunc) error {
	done := make(chan struct{})
	defer close(done)
	return WaitFor(wait, condition, done)
}

func PollImmediate(interval, timeout time.Duration, condition ConditionFunc) error {
	return pollImmediateInternal(poller(interval, timeout), condition)
}

func pollImmediateInternal(wait WaitFunc, condition ConditionFunc) error {
	done, err := condition()
	if err != nil {
		return err
	}
	if done {
		return nil
	}
	return pollInternal(wait, condition)
}

// PollInfinite polls forever.
func PollInfinite(interval time.Duration, condition ConditionFunc) error {
	done := make(chan struct{})
	defer close(done)
	return WaitFor(poller(interval, 0), condition, done)
}

// WaitFunc creates a channel that receives an item every time a test
// should be executed and is closed when the last test should be invoked.
type WaitFunc func(done <-chan struct{}) <-chan struct{}

// WaitFor gets a channel from wait(), and then invokes fn once for every value
// placed on the channel and once more when the channel is closed.  If fn
// returns an error the loop ends and that error is returned, and if fn returns
// true the loop ends and nil is returned. ErrWaitTimeout will be returned if
// the channel is closed without fn ever returning true.
func WaitFor(wait WaitFunc, fn ConditionFunc, done <-chan struct{}) error {
	c := wait(done)
	for {
		_, open := <-c
		ok, err := fn()
		if err != nil {
			return err
		}
		if ok {
			return nil
		}
		if !open {
			break
		}
	}
	return ErrWaitTimeout
}

// poller returns a WaitFunc that will send to the channel every
// interval until timeout has elapsed and then close the channel.
// Over very short intervals you may receive no ticks before
// the channel is closed.  If timeout is 0, the channel
// will never be closed.
func poller(interval, timeout time.Duration) WaitFunc {
	return WaitFunc(func(done <-chan struct{}) <-chan struct{} {
		ch := make(chan struct{})

		go func() {
			defer close(ch)

			tick := time.NewTicker(interval)
			defer tick.Stop()

			var after <-chan time.Time
			if timeout != 0 {
				// time.After is more convenient, but it
				// potentially leaves timers around much longer
				// than necessary if we exit early.
				timer := time.NewTimer(timeout)
				after = timer.C
				defer timer.Stop()
			}

			for {
				select {
				case <-tick.C:
					// If the consumer isn't ready for this signal drop it and
					// check the other channels.
					select {
					case ch <- struct{}{}:
					default:
					}
				case <-after:
					return
				case <-done:
					return
				}
			}
		}()

		return ch
	})
}
<|endoftext|>


<commit_before>package utils

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"strings"

	"github.com/containernetworking/cni/libcni"
	"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
	"k8s.io/client-go/kubernetes"
)

const (
	podCIDRAnnotation = "kube-router.io/pod-cidr"
)

// GetPodCidrFromCniSpec gets pod CIDR allocated to the node from CNI spec file and returns it
func GetPodCidrFromCniSpec(cniConfFilePath string) (net.IPNet, error) {
	var podCidr net.IPNet
	var err error
	var ipamConfig *allocator.IPAMConfig

	if strings.HasSuffix(cniConfFilePath, ".conflist") {
		var confList *libcni.NetworkConfigList
		confList, err = libcni.ConfListFromFile(cniConfFilePath)
		if err != nil {
			return net.IPNet{}, fmt.Errorf("Failed to load CNI config list file: %s", err.Error())
		}
		for _, conf := range confList.Plugins {
			if conf.Network.IPAM.Type != "" {
				ipamConfig, _, err = allocator.LoadIPAMConfig(conf.Bytes, "")
				if err != nil {
					return net.IPNet{}, fmt.Errorf("Failed to get IPAM details from the CNI conf file: %s", err.Error())
				}
				break
			}
		}
	} else {
		netconfig, err := libcni.ConfFromFile(cniConfFilePath)
		if err != nil {
			return net.IPNet{}, fmt.Errorf("Failed to load CNI conf file: %s", err.Error())
		}
		ipamConfig, _, err = allocator.LoadIPAMConfig(netconfig.Bytes, "")
		if err != nil {
			// TODO: Handle this error properly in controllers, if no subnet is specified
			if err.Error() != "no IP ranges specified" {
				return net.IPNet{}, fmt.Errorf("Failed to get IPAM details from the CNI conf file: %s", err.Error())
			}
			return net.IPNet{}, nil
		}
	}
	// TODO: Support multiple subnet definitions in CNI conf
	if len(ipamConfig.Ranges) > 0 {
		for _, rangeset := range ipamConfig.Ranges {
			for _, item := range rangeset {
				if item.Subnet.IP != nil {
					podCidr = net.IPNet(item.Subnet)
					break
				}
			}
		}
	}
	return podCidr, nil
}

// InsertPodCidrInCniSpec inserts the pod CIDR allocated to the node by kubernetes controlller manager
// and stored it in the CNI specification
func InsertPodCidrInCniSpec(cniConfFilePath string, cidr string) error {
	file, err := ioutil.ReadFile(cniConfFilePath)
	if err != nil {
		return fmt.Errorf("Failed to load CNI conf file: %s", err.Error())
	}
	var config interface{}
	if strings.HasSuffix(cniConfFilePath, ".conflist") {
		err = json.Unmarshal(file, &config)
		if err != nil {
			return fmt.Errorf("Failed to parse JSON from CNI conf file: %s", err.Error())
		}
		updatedCidr := false
		configMap := config.(map[string]interface{})
		for key := range configMap {
			if key != "plugins" {
				continue
			}
			// .conflist file has array of plug-in config. Find the one with ipam key
			// and insert the CIDR for the node
			pluginConfigs := configMap["plugins"].([]interface{})
			for _, pluginConfig := range pluginConfigs {
				pluginConfigMap := pluginConfig.(map[string]interface{})
				if val, ok := pluginConfigMap["ipam"]; ok {
					valObj := val.(map[string]interface{})
					valObj["subnet"] = cidr
					updatedCidr = true
					break
				}
			}
		}

		if !updatedCidr {
			return fmt.Errorf("failed to insert subnet cidr into CNI conf file: %s as CNI file is invalid", cniConfFilePath)
		}

	} else {
		err = json.Unmarshal(file, &config)
		if err != nil {
			return fmt.Errorf("Failed to parse JSON from CNI conf file: %s", err.Error())
		}
		pluginConfig := config.(map[string]interface{})
		pluginConfig["ipam"].(map[string]interface{})["subnet"] = cidr
	}
	configJSON, _ := json.Marshal(config)
	err = ioutil.WriteFile(cniConfFilePath, configJSON, 0644)
	if err != nil {
		return fmt.Errorf("Failed to insert subnet cidr into CNI conf file: %s", err.Error())
	}
	return nil
}

// GetPodCidrFromNodeSpec reads the pod CIDR allocated to the node from API node object and returns it
func GetPodCidrFromNodeSpec(clientset kubernetes.Interface, hostnameOverride string) (string, error) {
	node, err := GetNodeObject(clientset, hostnameOverride)
	if err != nil {
		return "", fmt.Errorf("Failed to get pod CIDR allocated for the node due to: " + err.Error())
	}

	if cidr, ok := node.Annotations[podCIDRAnnotation]; ok {
		_, _, err = net.ParseCIDR(cidr)
		if err != nil {
			return "", fmt.Errorf("error parsing pod CIDR in node annotation: %v", err)
		}

		return cidr, nil
	}

	if node.Spec.PodCIDR == "" {
		return "", fmt.Errorf("node.Spec.PodCIDR not set for node: %v", node.Name)
	}

	return node.Spec.PodCIDR, nil
}
<commit_msg>dont return error if there are no CIDR details configured in cni conflist<commit_after>package utils

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"strings"

	"github.com/containernetworking/cni/libcni"
	"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
	"k8s.io/client-go/kubernetes"
)

const (
	podCIDRAnnotation = "kube-router.io/pod-cidr"
)

// GetPodCidrFromCniSpec gets pod CIDR allocated to the node from CNI spec file and returns it
func GetPodCidrFromCniSpec(cniConfFilePath string) (net.IPNet, error) {
	var podCidr = net.IPNet{}
	var err error
	var ipamConfig *allocator.IPAMConfig

	if strings.HasSuffix(cniConfFilePath, ".conflist") {
		var confList *libcni.NetworkConfigList
		confList, err = libcni.ConfListFromFile(cniConfFilePath)
		if err != nil {
			return net.IPNet{}, fmt.Errorf("Failed to load CNI config list file: %s", err.Error())
		}
		for _, conf := range confList.Plugins {
			if conf.Network.IPAM.Type != "" {
				ipamConfig, _, err = allocator.LoadIPAMConfig(conf.Bytes, "")
				if err != nil {
					if err.Error() != "no IP ranges specified" {
						return net.IPNet{}, fmt.Errorf("Failed to get IPAM details from the CNI conf file: %s", err.Error())
					}
				}
				break
			}
		}
	} else {
		netconfig, err := libcni.ConfFromFile(cniConfFilePath)
		if err != nil {
			return net.IPNet{}, fmt.Errorf("Failed to load CNI conf file: %s", err.Error())
		}
		ipamConfig, _, err = allocator.LoadIPAMConfig(netconfig.Bytes, "")
		if err != nil {
			// TODO: Handle this error properly in controllers, if no subnet is specified
			if err.Error() != "no IP ranges specified" {
				return net.IPNet{}, fmt.Errorf("Failed to get IPAM details from the CNI conf file: %s", err.Error())
			}
			return net.IPNet{}, nil
		}
	}
	// TODO: Support multiple subnet definitions in CNI conf
	if ipamConfig != nil && len(ipamConfig.Ranges) > 0 {
		for _, rangeset := range ipamConfig.Ranges {
			for _, item := range rangeset {
				if item.Subnet.IP != nil {
					podCidr = net.IPNet(item.Subnet)
					break
				}
			}
		}
	}
	return podCidr, nil
}

// InsertPodCidrInCniSpec inserts the pod CIDR allocated to the node by kubernetes controlller manager
// and stored it in the CNI specification
func InsertPodCidrInCniSpec(cniConfFilePath string, cidr string) error {
	file, err := ioutil.ReadFile(cniConfFilePath)
	if err != nil {
		return fmt.Errorf("Failed to load CNI conf file: %s", err.Error())
	}
	var config interface{}
	if strings.HasSuffix(cniConfFilePath, ".conflist") {
		err = json.Unmarshal(file, &config)
		if err != nil {
			return fmt.Errorf("Failed to parse JSON from CNI conf file: %s", err.Error())
		}
		updatedCidr := false
		configMap := config.(map[string]interface{})
		for key := range configMap {
			if key != "plugins" {
				continue
			}
			// .conflist file has array of plug-in config. Find the one with ipam key
			// and insert the CIDR for the node
			pluginConfigs := configMap["plugins"].([]interface{})
			for _, pluginConfig := range pluginConfigs {
				pluginConfigMap := pluginConfig.(map[string]interface{})
				if val, ok := pluginConfigMap["ipam"]; ok {
					valObj := val.(map[string]interface{})
					valObj["subnet"] = cidr
					updatedCidr = true
					break
				}
			}
		}

		if !updatedCidr {
			return fmt.Errorf("failed to insert subnet cidr into CNI conf file: %s as CNI file is invalid", cniConfFilePath)
		}

	} else {
		err = json.Unmarshal(file, &config)
		if err != nil {
			return fmt.Errorf("Failed to parse JSON from CNI conf file: %s", err.Error())
		}
		pluginConfig := config.(map[string]interface{})
		pluginConfig["ipam"].(map[string]interface{})["subnet"] = cidr
	}
	configJSON, _ := json.Marshal(config)
	err = ioutil.WriteFile(cniConfFilePath, configJSON, 0644)
	if err != nil {
		return fmt.Errorf("Failed to insert subnet cidr into CNI conf file: %s", err.Error())
	}
	return nil
}

// GetPodCidrFromNodeSpec reads the pod CIDR allocated to the node from API node object and returns it
func GetPodCidrFromNodeSpec(clientset kubernetes.Interface, hostnameOverride string) (string, error) {
	node, err := GetNodeObject(clientset, hostnameOverride)
	if err != nil {
		return "", fmt.Errorf("Failed to get pod CIDR allocated for the node due to: " + err.Error())
	}

	if cidr, ok := node.Annotations[podCIDRAnnotation]; ok {
		_, _, err = net.ParseCIDR(cidr)
		if err != nil {
			return "", fmt.Errorf("error parsing pod CIDR in node annotation: %v", err)
		}

		return cidr, nil
	}

	if node.Spec.PodCIDR == "" {
		return "", fmt.Errorf("node.Spec.PodCIDR not set for node: %v", node.Name)
	}

	return node.Spec.PodCIDR, nil
}
<|endoftext|>


<commit_before>package instancecommands

import (
	"fmt"
	"os"

	"github.com/codegangsta/cli"
	"github.com/fatih/structs"
	"github.com/jrperritt/rack/util"
	"github.com/rackspace/gophercloud"
	osServers "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
)

func idOrName(c *cli.Context, client *gophercloud.ServiceClient) string {
	var err error
	var serverID string
	if c.IsSet("id") {
		serverID = c.String("id")
	} else if c.IsSet("name") {
		serverName := c.String("name")
		serverID, err = osServers.IDFromName(client, serverName)
		if err != nil {
			fmt.Printf("Error converting server name [%s] to ID: %s\n", serverName, err)
			os.Exit(1)
		}
	} else {
		util.PrintError(c, util.ErrMissingFlag{
			Msg: "One of either --id or --name must be provided.",
		})
	}
	return serverID
}

var idAndNameFlags = []cli.Flag{
	cli.StringFlag{
		Name:  "id",
		Usage: "[optional; required if 'name' is not provided] The ID of the server to update",
	},
	cli.StringFlag{
		Name:  "name",
		Usage: "[optional; required if 'id' is not provided] The name of the server to update",
	},
}

var idOrNameUsage = "[--id <serverID> | --name <serverName>]"

func serverSingle(rawServer interface{}) map[string]interface{} {
	server, ok := rawServer.(*osServers.Server)
	if !ok {
		return nil
	}
	m := structs.Map(rawServer)
	m["Public IPv4"] = server.AccessIPv4
	m["Public IPv6"] = server.AccessIPv6
	m["Private IPv4"] = ""
	ips, ok := server.Addresses["private"].([]interface{})
	if ok || len(ips) > 0 {
		priv, ok := ips[0].(map[string]interface{})
		if ok {
			m["Private IPv4"] = priv["addr"]
		}
	}
	m["Flavor"] = server.Flavor["id"]
	m["Image"] = server.Image["id"]
	return m
}
<commit_msg>generic usage msg for id and name flags<commit_after>package instancecommands

import (
	"fmt"
	"os"

	"github.com/codegangsta/cli"
	"github.com/fatih/structs"
	"github.com/jrperritt/rack/util"
	"github.com/rackspace/gophercloud"
	osServers "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
)

func idOrName(c *cli.Context, client *gophercloud.ServiceClient) string {
	var err error
	var serverID string
	if c.IsSet("id") {
		serverID = c.String("id")
	} else if c.IsSet("name") {
		serverName := c.String("name")
		serverID, err = osServers.IDFromName(client, serverName)
		if err != nil {
			fmt.Printf("Error converting server name [%s] to ID: %s\n", serverName, err)
			os.Exit(1)
		}
	} else {
		util.PrintError(c, util.ErrMissingFlag{
			Msg: "One of either --id or --name must be provided.",
		})
	}
	return serverID
}

var idAndNameFlags = []cli.Flag{
	cli.StringFlag{
		Name:  "id",
		Usage: "[optional; required if 'name' is not provided] The ID of the resource",
	},
	cli.StringFlag{
		Name:  "name",
		Usage: "[optional; required if 'id' is not provided] The name of the resource",
	},
}

var idOrNameUsage = "[--id <serverID> | --name <serverName>]"

func serverSingle(rawServer interface{}) map[string]interface{} {
	server, ok := rawServer.(*osServers.Server)
	if !ok {
		return nil
	}
	m := structs.Map(rawServer)
	m["Public IPv4"] = server.AccessIPv4
	m["Public IPv6"] = server.AccessIPv6
	m["Private IPv4"] = ""
	ips, ok := server.Addresses["private"].([]interface{})
	if ok || len(ips) > 0 {
		priv, ok := ips[0].(map[string]interface{})
		if ok {
			m["Private IPv4"] = priv["addr"]
		}
	}
	m["Flavor"] = server.Flavor["id"]
	m["Image"] = server.Image["id"]
	return m
}
<|endoftext|>


<commit_before>package api

// StorageVolumeSnapshotsPost represents the fields available for a new LXD storage volume snapshot
//
// API extension: storage_api_volume_snapshots
type StorageVolumeSnapshotsPost struct {
	Name string `json:"name" yaml:"name"`
}

// StorageVolumeSnapshotPost represents the fields required to rename/move a LXD storage volume snapshot
//
// API extension: storage_api_volume_snapshots
type StorageVolumeSnapshotPost struct {
	Name string `json:"name" yaml:"name"`
}

// StorageVolumeSnapshot represents a LXD storage volume snapshot
//
// API extension: storage_api_volume_snapshots
type StorageVolumeSnapshot struct {
	Name        string            `json:"name" yaml:"name"`
	Config      map[string]string `json:"config" yaml:"config"`
	Description string            `json:"description" yaml:"description"`
}

// StorageVolumeSnapshotPut represents the modifiable fields of a LXD storage volume
//
// API extension: storage_api_volume_snapshots
type StorageVolumeSnapshotPut struct {
	Description string `json:"description" yaml:"description"`
}
<commit_msg>shared/api: Add expiry fields to StorageVolumeSnapshot*<commit_after>package api

import "time"

// StorageVolumeSnapshotsPost represents the fields available for a new LXD storage volume snapshot
//
// API extension: storage_api_volume_snapshots
type StorageVolumeSnapshotsPost struct {
	Name string `json:"name" yaml:"name"`

	// API extension: custom_volume_snapshot_expiry
	ExpiresAt *time.Time `json:"expires_at" yaml:"expires_at"`
}

// StorageVolumeSnapshotPost represents the fields required to rename/move a LXD storage volume snapshot
//
// API extension: storage_api_volume_snapshots
type StorageVolumeSnapshotPost struct {
	Name string `json:"name" yaml:"name"`
}

// StorageVolumeSnapshot represents a LXD storage volume snapshot
//
// API extension: storage_api_volume_snapshots
type StorageVolumeSnapshot struct {
	StorageVolumeSnapshotPut `json:",inline" yaml:",inline"`

	Name   string            `json:"name" yaml:"name"`
	Config map[string]string `json:"config" yaml:"config"`
}

// StorageVolumeSnapshotPut represents the modifiable fields of a LXD storage volume
//
// API extension: storage_api_volume_snapshots
type StorageVolumeSnapshotPut struct {
	Description string `json:"description" yaml:"description"`

	// API extension: custom_volume_snapshot_expiry
	ExpiresAt *time.Time `json:"expires_at" yaml:"expires_at"`
}
<|endoftext|>


<commit_before>package pkg

func fn() bool { return true }
func fn1() bool {
	x := true
	if x { // want `should use 'return x'`
		return true
	}
	return false
}

func fn2() bool {
	x := true
	if !x {
		return true
	}
	if x {
		return true
	}
	return false
}

func fn3() int {
	var x bool
	if x {
		return 1
	}
	return 2
}

func fn4() bool { return true }

func fn5() bool {
	if fn() { // want `should use 'return !fn\(\)'`
		return false
	}
	return true
}

func fn6() bool {
	if fn3() != fn3() { // want `should use 'return fn3\(\) != fn3\(\)'`
		return true
	}
	return false
}

func fn7() bool {
	if 1 > 2 { // want `should use 'return 1 > 2'`
		return true
	}
	return false
}

func fn8() bool {
	if fn() || fn() {
		return true
	}
	return false
}

func fn9(x int) bool {
	if x > 0 {
		return true
	}
	return true
}

func fn10(x int) bool {
	if x > 0 { // want `should use 'return x <= 0'`
		return false
	}
	return true
}

func fn11(x bool) bool {
	if x { // want `should use 'return !x'`
		return false
	}
	return true
}

func fn12() bool {
	var x []bool
	if x[0] { // want `should use 'return !x\[0\]'`
		return false
	}
	return true
}

func fn13(a, b int) bool {
	if a != b { // want `should use 'return a == b' instead of 'if a != b`
		return false
	}
	return true
}
<commit_msg>simple: add test case for correct negation of >=<commit_after>package pkg

func fn() bool { return true }
func fn1() bool {
	x := true
	if x { // want `should use 'return x'`
		return true
	}
	return false
}

func fn2() bool {
	x := true
	if !x {
		return true
	}
	if x {
		return true
	}
	return false
}

func fn3() int {
	var x bool
	if x {
		return 1
	}
	return 2
}

func fn4() bool { return true }

func fn5() bool {
	if fn() { // want `should use 'return !fn\(\)'`
		return false
	}
	return true
}

func fn6() bool {
	if fn3() != fn3() { // want `should use 'return fn3\(\) != fn3\(\)'`
		return true
	}
	return false
}

func fn7() bool {
	if 1 > 2 { // want `should use 'return 1 > 2'`
		return true
	}
	return false
}

func fn8() bool {
	if fn() || fn() {
		return true
	}
	return false
}

func fn9(x int) bool {
	if x > 0 {
		return true
	}
	return true
}

func fn10(x int) bool {
	if x > 0 { // want `should use 'return x <= 0'`
		return false
	}
	return true
}

func fn11(x bool) bool {
	if x { // want `should use 'return !x'`
		return false
	}
	return true
}

func fn12() bool {
	var x []bool
	if x[0] { // want `should use 'return !x\[0\]'`
		return false
	}
	return true
}

func fn13(a, b int) bool {
	if a != b { // want `should use 'return a == b' instead of 'if a != b`
		return false
	}
	return true
}

func fn14(a, b int) bool {
	if a >= b { // want `should use 'return a < b' instead of 'if a >= b`
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package service

import (
	"context"
	"net/http"

	"github.com/jackc/pgx/v4"
	"github.com/rs/zerolog"
	"golang.org/x/oauth2"

	"github.com/gilcrest/diy-go-api/datastore/appstore"
	"github.com/gilcrest/diy-go-api/datastore/userstore"
	"github.com/gilcrest/diy-go-api/domain/app"
	"github.com/gilcrest/diy-go-api/domain/audit"
	"github.com/gilcrest/diy-go-api/domain/auth"
	"github.com/gilcrest/diy-go-api/domain/errs"
	"github.com/gilcrest/diy-go-api/domain/org"
	"github.com/gilcrest/diy-go-api/domain/secure"
	"github.com/gilcrest/diy-go-api/domain/user"
	"github.com/gilcrest/diy-go-api/gateway/authgateway"
)

// Authorizer determines if an app/user (as part of an Audit) is
// authorized for the route in the request
type Authorizer interface {
	Authorize(lgr zerolog.Logger, r *http.Request, sub audit.Audit) error
}

// MiddlewareService holds methods used by server middleware handlers
type MiddlewareService struct {
	Datastorer                 Datastorer
	GoogleOauth2TokenConverter GoogleOauth2TokenConverter
	Authorizer                 Authorizer
	EncryptionKey              *[32]byte
}

// FindAppByAPIKey finds an app given its External ID and determines
// if the given API key is a valid key for it. It is used as part of
// app authentication
func (s MiddlewareService) FindAppByAPIKey(ctx context.Context, realm, appExtlID, key string) (app.App, error) {

	var (
		kr  []appstore.FindAppAPIKeysByAppExtlIDRow
		err error
	)

	// retrieve the list of encrypted API keys from the database
	kr, err = appstore.New(s.Datastorer.Pool()).FindAppAPIKeysByAppExtlID(ctx, appExtlID)
	if err != nil {
		return app.App{}, errs.E(errs.Unauthenticated, errs.Realm(realm), err)
	}

	var (
		a   app.App
		ak  app.APIKey
		aks []app.APIKey
	)

	// for each row, decrypt the API key using the encryption key,
	// initialize an app.APIKey and set to a slice of API keys.
	for i, row := range kr {
		if i == 0 { // only need to fill the app struct on first iteration
			var extl secure.Identifier
			extl, err = secure.ParseIdentifier(row.OrgExtlID)
			if err != nil {
				return app.App{}, err
			}
			a.ID = row.AppID
			a.ExternalID = extl
			a.Org = org.Org{
				ID:          row.OrgID,
				ExternalID:  extl,
				Name:        row.OrgName,
				Description: row.OrgDescription,
			}
			a.Name = row.AppName
			a.Description = row.AppDescription
		}
		ak, err = app.NewAPIKeyFromCipher(row.ApiKey, s.EncryptionKey)
		if err != nil {
			return app.App{}, err
		}
		ak.SetDeactivationDate(row.DeactvDate)
		aks = append(aks, ak)
	}
	a.APIKeys = aks

	// ValidKey determines if any of the keys attached to the app
	// match the input key and are still valid.
	err = a.ValidKey(realm, key)
	if err != nil {
		return app.App{}, err
	}

	return a, nil
}

// GoogleOauth2TokenConverter converts an oauth2.Token to an authgateway.Userinfo struct
type GoogleOauth2TokenConverter interface {
	Convert(ctx context.Context, realm string, token oauth2.Token) (authgateway.ProviderUserInfo, error)
}

// FindUserParams is parameters for finding a User
type FindUserParams struct {
	Realm          string
	App            app.App
	Provider       auth.Provider
	Token          oauth2.Token
	RetrieveFromDB bool
}

// FindUserByOauth2Token retrieves a users' identity from a Provider
// and then retrieves the associated registered user from the datastore
func (s MiddlewareService) FindUserByOauth2Token(ctx context.Context, params FindUserParams) (user.User, error) {
	var (
		uInfo authgateway.ProviderUserInfo
		err   error
	)

	if params.Provider == auth.Invalid {
		return user.User{}, errs.E(errs.Unauthenticated, errs.Realm(params.Realm), "provider not recognized")
	}

	if params.Provider == auth.Apple {
		return user.User{}, errs.E(errs.Unauthenticated, errs.Realm(params.Realm), "apple authentication not yet implemented")
	}

	if params.Provider == auth.Google {
		uInfo, err = s.GoogleOauth2TokenConverter.Convert(ctx, params.Realm, params.Token)
		if err != nil {
			return user.User{}, err
		}
	}

	findUserByUsernameParams := userstore.FindUserByUsernameParams{
		Username: uInfo.Username,
		OrgID:    params.App.Org.ID,
	}

	if params.RetrieveFromDB {
		var findUserByUsernameRow userstore.FindUserByUsernameRow
		findUserByUsernameRow, err = userstore.New(s.Datastorer.Pool()).FindUserByUsername(ctx, findUserByUsernameParams)
		if err != nil {
			if err == pgx.ErrNoRows {
				return user.User{}, errs.E(errs.Unauthenticated, errs.Realm(params.Realm), "No user registered in database")
			}
			return user.User{}, errs.E(errs.Unauthenticated, errs.Realm(params.Realm), err)
		}

		return hydrateUserFromDB(findUserByUsernameRow), nil
	}

	return hydrateUserFromProviderUserInfo(params, uInfo), nil
}

// Authorize determines if an app/user (as part of an Audit) is
// authorized for the route in the request
func (s MiddlewareService) Authorize(lgr zerolog.Logger, r *http.Request, sub audit.Audit) error {
	return s.Authorizer.Authorize(lgr, r, sub)
}
<commit_msg>fix for new function name<commit_after>package service

import (
	"context"
	"net/http"

	"github.com/jackc/pgx/v4"
	"github.com/rs/zerolog"
	"golang.org/x/oauth2"

	"github.com/gilcrest/diy-go-api/datastore/appstore"
	"github.com/gilcrest/diy-go-api/datastore/userstore"
	"github.com/gilcrest/diy-go-api/domain/app"
	"github.com/gilcrest/diy-go-api/domain/audit"
	"github.com/gilcrest/diy-go-api/domain/auth"
	"github.com/gilcrest/diy-go-api/domain/errs"
	"github.com/gilcrest/diy-go-api/domain/org"
	"github.com/gilcrest/diy-go-api/domain/secure"
	"github.com/gilcrest/diy-go-api/domain/user"
	"github.com/gilcrest/diy-go-api/gateway/authgateway"
)

// Authorizer determines if an app/user (as part of an Audit) is
// authorized for the route in the request
type Authorizer interface {
	Authorize(lgr zerolog.Logger, r *http.Request, sub audit.Audit) error
}

// MiddlewareService holds methods used by server middleware handlers
type MiddlewareService struct {
	Datastorer                 Datastorer
	GoogleOauth2TokenConverter GoogleOauth2TokenConverter
	Authorizer                 Authorizer
	EncryptionKey              *[32]byte
}

// FindAppByAPIKey finds an app given its External ID and determines
// if the given API key is a valid key for it. It is used as part of
// app authentication
func (s MiddlewareService) FindAppByAPIKey(ctx context.Context, realm, appExtlID, key string) (app.App, error) {

	var (
		kr  []appstore.FindAppAPIKeysByAppExtlIDRow
		err error
	)

	// retrieve the list of encrypted API keys from the database
	kr, err = appstore.New(s.Datastorer.Pool()).FindAppAPIKeysByAppExtlID(ctx, appExtlID)
	if err != nil {
		return app.App{}, errs.E(errs.Unauthenticated, errs.Realm(realm), err)
	}

	var (
		a   app.App
		ak  app.APIKey
		aks []app.APIKey
	)

	// for each row, decrypt the API key using the encryption key,
	// initialize an app.APIKey and set to a slice of API keys.
	for i, row := range kr {
		if i == 0 { // only need to fill the app struct on first iteration
			var extl secure.Identifier
			extl, err = secure.ParseIdentifier(row.OrgExtlID)
			if err != nil {
				return app.App{}, err
			}
			a.ID = row.AppID
			a.ExternalID = extl
			a.Org = org.Org{
				ID:          row.OrgID,
				ExternalID:  extl,
				Name:        row.OrgName,
				Description: row.OrgDescription,
			}
			a.Name = row.AppName
			a.Description = row.AppDescription
		}
		ak, err = app.NewAPIKeyFromCipher(row.ApiKey, s.EncryptionKey)
		if err != nil {
			return app.App{}, err
		}
		ak.SetDeactivationDate(row.DeactvDate)
		aks = append(aks, ak)
	}
	a.APIKeys = aks

	// ValidKey determines if any of the keys attached to the app
	// match the input key and are still valid.
	err = a.ValidKey(realm, key)
	if err != nil {
		return app.App{}, err
	}

	return a, nil
}

// GoogleOauth2TokenConverter converts an oauth2.Token to an authgateway.Userinfo struct
type GoogleOauth2TokenConverter interface {
	Convert(ctx context.Context, realm string, token oauth2.Token) (authgateway.ProviderUserInfo, error)
}

// FindUserParams is parameters for finding a User
type FindUserParams struct {
	Realm          string
	App            app.App
	Provider       auth.Provider
	Token          oauth2.Token
	RetrieveFromDB bool
}

// FindUserByOauth2Token retrieves a users' identity from a Provider
// and then retrieves the associated registered user from the datastore
func (s MiddlewareService) FindUserByOauth2Token(ctx context.Context, params FindUserParams) (user.User, error) {
	var (
		uInfo authgateway.ProviderUserInfo
		err   error
	)

	if params.Provider == auth.Invalid {
		return user.User{}, errs.E(errs.Unauthenticated, errs.Realm(params.Realm), "provider not recognized")
	}

	if params.Provider == auth.Apple {
		return user.User{}, errs.E(errs.Unauthenticated, errs.Realm(params.Realm), "apple authentication not yet implemented")
	}

	if params.Provider == auth.Google {
		uInfo, err = s.GoogleOauth2TokenConverter.Convert(ctx, params.Realm, params.Token)
		if err != nil {
			return user.User{}, err
		}
	}

	findUserByUsernameParams := userstore.FindUserByUsernameParams{
		Username: uInfo.Username,
		OrgID:    params.App.Org.ID,
	}

	if params.RetrieveFromDB {
		var findUserByUsernameRow userstore.FindUserByUsernameRow
		findUserByUsernameRow, err = userstore.New(s.Datastorer.Pool()).FindUserByUsername(ctx, findUserByUsernameParams)
		if err != nil {
			if err == pgx.ErrNoRows {
				return user.User{}, errs.E(errs.Unauthenticated, errs.Realm(params.Realm), "No user registered in database")
			}
			return user.User{}, errs.E(errs.Unauthenticated, errs.Realm(params.Realm), err)
		}

		return hydrateUserFromUsernameRow(findUserByUsernameRow), nil
	}

	return hydrateUserFromProviderUserInfo(params, uInfo), nil
}

// Authorize determines if an app/user (as part of an Audit) is
// authorized for the route in the request
func (s MiddlewareService) Authorize(lgr zerolog.Logger, r *http.Request, sub audit.Audit) error {
	return s.Authorizer.Authorize(lgr, r, sub)
}
<|endoftext|>