<commit_before>package boom

import (
	"context"
	"errors"
	"fmt"
	"testing"
	"time"

	"github.com/aphistic/sweet"
	junit "github.com/aphistic/sweet-junit"
	. "github.com/onsi/gomega"
)

func TestMain(m *testing.M) {
	RegisterFailHandler(sweet.GomegaFail)

	sweet.Run(m, func(s *sweet.S) {
		s.RegisterPlugin(junit.NewPlugin())

		s.AddSuite(&TaskSuite{})
		s.AddSuite(&RunnerSuite{})
		s.AddSuite(&AsyncColSuite{})
	})
}

type TaskSuite struct{}

const waitTimeout = 10 * time.Millisecond

func ExampleTask() {
	// Create a new task but don't start execution right away
	t := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		// Run the task until something requests that we stop
		<-task.Stopping()
		return NewValueResult(args, nil)
	}, "first", "second", 3)

	// Start the Task. Another way to do this in a single command is to use
	// runTask(newTaskConfig(), ) to create a new task and start it right away
	t.Start()

	// Let the task run a little bit
	time.Sleep(100 * time.Millisecond)

	// Ask the task to stop
	t.Stop()

	// Wait forever for the task to finish running and get
	// the result from it
	res, _ := t.Wait(0)

	valRes := res.(*ValueResult)

	fmt.Printf("Value: %+v\n", valRes.Value)
	fmt.Printf("Error: %+v\n", valRes.Error)

	// Output:
	// Value: [first second 3]
	// Error: <nil>
}

func (s *TaskSuite) TestStartSync(t sweet.T) {
	task := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		return NewValueResult(args[0], nil)
	}, 1)

	res, err := task.StartSync()
	Expect(err).To(BeNil())
	Expect(res).To(Equal(&ValueResult{Value: 1, Error: nil}))
}

func (s *TaskSuite) TestContext(t sweet.T) {
	var taskCtx context.Context

	ctx := context.Background()
	task := runTask(ctx, newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		taskCtx = task.Context()
		return nil
	})
	task.Wait(0)

	Expect(taskCtx).ToNot(BeNil())
}

func (s *TaskSuite) TestContextCancelStopsTask(t sweet.T) {
	ctx, cancelCtx := context.WithCancel(context.Background())
	task := newTask(ctx, newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		return nil
	})

	Expect(ctx.Done()).ToNot(BeClosed())
	Expect(task.Stopping()).ToNot(BeClosed())

	cancelCtx()

	Eventually(ctx.Done()).Should(BeClosed())
	Eventually(task.Stopping()).Should(BeClosed())
}

func (s *TaskSuite) TestStart(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		return NewValueResult(args[0], nil)
	}, 1)

	res, err := task.Wait(waitTimeout)
	Expect(err).To(BeNil())
	Expect(res).To(Equal(&ValueResult{Value: 1, Error: nil}))
}

func (s *TaskSuite) TestStartStop(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		<-task.Stopping()

		return NewValueResult(args[0], nil)
	}, 1)

	task.Stop()

	res, err := task.Wait(waitTimeout)
	Expect(err).To(BeNil())
	Expect(res).To(Equal(&ValueResult{Value: 1, Error: nil}))
}

func (s *TaskSuite) TestStartFinished(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		return NewValueResult(args[0], nil)
	}, 1)

	res, err := task.Wait(waitTimeout)
	Expect(err).To(BeNil())
	Expect(res).To(Equal(&ValueResult{Value: 1, Error: nil}))

	err = task.Start()
	Expect(err).To(Equal(ErrFinished))
}

func (s *TaskSuite) TestFinished(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		return NewValueResult(args[0], nil)
	}, 1)

	res, err := task.Wait(waitTimeout)
	Expect(err).To(BeNil())
	Expect(res).To(Equal(&ValueResult{Value: 1, Error: nil}))

	Eventually(task.Finished()).Should(BeClosed())
	Expect(task.resultChan).To(BeClosed())
}

func (s *TaskSuite) TestStarted(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		<-task.Stopping()
		return NewValueResult(args[0], nil)
	}, 1)

	Expect(task.Started()).To(BeClosed())

	task.Stop()
	task.Wait(waitTimeout)
}

func (s *TaskSuite) TestRunning(t sweet.T) {
	advance := make(chan int)
	advanced := make(chan int)
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		//c.Log("Started task")
		<-advance
		//c.Log("Setting running to true")
		task.SetRunning(true)
		advanced <- 1
		<-advance
		//c.Log("Setting running to false")
		task.SetRunning(false)
		advanced <- 1
		<-task.Stopping()

		//c.Log("Task returning")
		return NewValueResult(nil, nil)
	})

	Expect(task.Running()).ToNot(BeClosed())
	//c.Log("Advancing to running")
	advance <- 1
	<-advanced
	Expect(task.Running()).To(BeClosed())
	//c.Log("Advancing to not running")
	advance <- 1
	<-advanced
	Expect(task.Running()).ToNot(BeClosed())

	//c.Log("Stopping")
	task.Stop()
}

func (s *TaskSuite) TestWaitForRunning(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		task.SetRunning(true)
		<-task.Stopping()

		return NewValueResult(nil, nil)
	})

	err := task.WaitForRunning(1 * time.Second)
	Expect(err).To(BeNil())

	_, err = task.StopAndWait(1 * time.Second)
	Expect(err).To(BeNil())
}

func (s *TaskSuite) TestWaitForRunningTimeout(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		<-task.Stopping()
		return NewValueResult(nil, nil)
	})

	err := task.WaitForRunning(100 * time.Millisecond)
	Expect(err).To(Equal(ErrTimeout))
	_, err = task.StopAndWait(1 * time.Second)
	Expect(err).To(BeNil())
}

func (s *TaskSuite) TestWaitForRunningTaskFinished(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		return NewErrorResult(errors.New("I'm an error! - Ralph"))
	})

	err := task.WaitForRunning(100 * time.Millisecond)
	Expect(err).To(BeNil())
	res, err := task.Wait(100 * time.Millisecond)
	Expect(err).To(BeNil())
	Expect(res).To(Equal(NewErrorResult(errors.New("I'm an error! - Ralph"))))
}

func (s *TaskSuite) TestRunningSetFalseWhenFinished(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		task.SetRunning(true)
		<-task.Stopping()
		return NewValueResult(nil, nil)
	})

	err := task.WaitForRunning(100 * time.Millisecond)
	Expect(err).To(BeNil())
	_, err = task.StopAndWait(1 * time.Second)
	Expect(err).To(BeNil())
	Expect(task.Running()).ToNot(BeClosed())
}

func (s *TaskSuite) TestWaitTwice(t sweet.T) {
	task := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		task.SetRunning(true)
		<-task.Stopping()
		return NewValueResult(args[0], nil)
	}, 1)

	task.Start()

	task.WaitForRunning(0)

	task.Stop()

	res, err := task.Wait(waitTimeout)
	Expect(err).To(BeNil())
	Expect(res).To(Equal(&ValueResult{Value: 1, Error: nil}))
	Expect(task.resultChan).To(BeClosed())

	res, err = task.Wait(waitTimeout)
	Expect(err).To(BeNil())
	Expect(res).To(Equal(&ValueResult{Value: 1, Error: nil}))
	Expect(task.resultChan).To(BeClosed())
}

func (s *TaskSuite) TestWaitTimeout(t sweet.T) {
	task := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		time.Sleep(100 * time.Millisecond)
		return NewValueResult(args[0], nil)
	}, 1)

	task.Start()

	res, err := task.Wait(10 * time.Millisecond)
	Expect(err).To(Equal(ErrTimeout))
	Expect(res).To(BeNil())
	Expect(task.resultChan).ToNot(BeClosed())

	res, err = task.Wait(200 * time.Millisecond)
	Expect(err).To(BeNil())
	Expect(res).To(Equal(&ValueResult{Value: 1, Error: nil}))
	Expect(task.resultChan).To(BeClosed())
}

func (s *TaskSuite) TestDiscardBeforeRunning(t sweet.T) {
	task := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		return NewValueResult(nil, nil)
	})

	task.Discard()
	task.Start()

	Eventually(task.resultChan).Should(BeClosed())
}

func (s *TaskSuite) TestDiscardAfterRunning(t sweet.T) {
	task := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		return NewValueResult(nil, nil)
	})

	task.Start()
	task.Discard()

	Eventually(task.resultChan).Should(BeClosed())
}

func (s *TaskSuite) TestStartStarted(t sweet.T) {
	task := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		<-task.Stopping()
		return NewValueResult(nil, nil)
	})

	err := task.Start()
	Expect(err).To(BeNil())
	err = task.Start()
	Expect(err).ToNot(BeNil())
	Expect(err).To(Equal(ErrExecuting))

	task.Stop()
	task.Wait(waitTimeout)
}

func (s *TaskSuite) TestStopStopped(t sweet.T) {
	task := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		<-task.Stopping()
		return NewValueResult(nil, nil)
	})

	err := task.Stop()
	Expect(err).To(Equal(ErrNotExecuting))
}

func (s *TaskSuite) TestWaitStopped(t sweet.T) {
	task := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		<-task.Stopping()
		return NewValueResult(nil, nil)
	})

	res, err := task.Wait(waitTimeout)
	Expect(err).To(Equal(ErrNotExecuting))
	Expect(res).To(BeNil())
}

func (s *TaskSuite) TestStopAndWait(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		<-task.Stopping()
		return NewValueResult(1, nil)
	})

	Eventually(task.Started()).Should(BeClosed())

	res, err := task.StopAndWait(waitTimeout)
	Expect(err).To(BeNil())
	Expect(res).To(Equal(&ValueResult{Value: 1, Error: nil}))
}

func (s *TaskSuite) TestStopAndWaitWhileStopped(t sweet.T) {
	task := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		<-task.Stopping()
		return NewValueResult(1, nil)
	})

	res, err := task.StopAndWait(waitTimeout)
	Expect(err).To(Equal(ErrNotExecuting))
	Expect(res).To(BeNil())
}

func (s *TaskSuite) TestStopAndWaitTimeout(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		time.Sleep(25 * time.Millisecond)
		<-task.Stopping()
		return NewValueResult(1, nil)
	})

	res, err := task.StopAndWait(10 * time.Millisecond)
	Expect(err).To(Equal(ErrTimeout))
	Expect(res).To(BeNil())
}

func (s *TaskSuite) TestNilResult(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		return nil
	})

	res, err := task.Wait(waitTimeout)
	Expect(err).To(BeNil())
	Expect(res).To(BeNil())
}

func (s *TaskSuite) TestValueResultErr(t sweet.T) {
	res := NewValueResult(1234, errors.New("I'm an error - Ralph"))
	Expect(res.Err()).To(Equal(errors.New("I'm an error - Ralph")))
}

func (s *TaskSuite) TestErrorResultErr(t sweet.T) {
	res := NewErrorResult(errors.New("I'm an error - Ralph"))
	Expect(res.Err()).To(Equal(errors.New("I'm an error - Ralph")))
}
<commit_msg>Add sleeps to test to allow runtime to start up goroutines<commit_after>package boom

import (
	"context"
	"errors"
	"fmt"
	"testing"
	"time"

	"github.com/aphistic/sweet"
	junit "github.com/aphistic/sweet-junit"
	. "github.com/onsi/gomega"
)

func TestMain(m *testing.M) {
	RegisterFailHandler(sweet.GomegaFail)

	sweet.Run(m, func(s *sweet.S) {
		s.RegisterPlugin(junit.NewPlugin())

		s.AddSuite(&TaskSuite{})
		s.AddSuite(&RunnerSuite{})
		s.AddSuite(&AsyncColSuite{})
	})
}

type TaskSuite struct{}

const waitTimeout = 10 * time.Millisecond

func ExampleTask() {
	// Create a new task but don't start execution right away
	t := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		// Run the task until something requests that we stop
		<-task.Stopping()
		return NewValueResult(args, nil)
	}, "first", "second", 3)

	// Start the Task. Another way to do this in a single command is to use
	// runTask(newTaskConfig(), ) to create a new task and start it right away
	t.Start()

	// Let the task run a little bit
	time.Sleep(100 * time.Millisecond)

	// Ask the task to stop
	t.Stop()

	// Wait forever for the task to finish running and get
	// the result from it
	res, _ := t.Wait(0)

	valRes := res.(*ValueResult)

	fmt.Printf("Value: %+v\n", valRes.Value)
	fmt.Printf("Error: %+v\n", valRes.Error)

	// Output:
	// Value: [first second 3]
	// Error: <nil>
}

func (s *TaskSuite) TestStartSync(t sweet.T) {
	task := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		return NewValueResult(args[0], nil)
	}, 1)

	res, err := task.StartSync()
	Expect(err).To(BeNil())
	Expect(res).To(Equal(&ValueResult{Value: 1, Error: nil}))
}

func (s *TaskSuite) TestContext(t sweet.T) {
	var taskCtx context.Context

	ctx := context.Background()
	task := runTask(ctx, newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		taskCtx = task.Context()
		return nil
	})
	task.Wait(0)

	Expect(taskCtx).ToNot(BeNil())
}

func (s *TaskSuite) TestContextCancelStopsTask(t sweet.T) {
	ctx, cancelCtx := context.WithCancel(context.Background())
	task := newTask(ctx, newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		return nil
	})

	Expect(ctx.Done()).ToNot(BeClosed())
	Expect(task.Stopping()).ToNot(BeClosed())

	cancelCtx()

	Eventually(ctx.Done()).Should(BeClosed())
	Eventually(task.Stopping()).Should(BeClosed())
}

func (s *TaskSuite) TestStart(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		return NewValueResult(args[0], nil)
	}, 1)

	res, err := task.Wait(waitTimeout)
	Expect(err).To(BeNil())
	Expect(res).To(Equal(&ValueResult{Value: 1, Error: nil}))
}

func (s *TaskSuite) TestStartStop(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		<-task.Stopping()

		return NewValueResult(args[0], nil)
	}, 1)

	task.Stop()

	res, err := task.Wait(waitTimeout)
	Expect(err).To(BeNil())
	Expect(res).To(Equal(&ValueResult{Value: 1, Error: nil}))
}

func (s *TaskSuite) TestStartFinished(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		return NewValueResult(args[0], nil)
	}, 1)

	res, err := task.Wait(waitTimeout)
	Expect(err).To(BeNil())
	Expect(res).To(Equal(&ValueResult{Value: 1, Error: nil}))

	err = task.Start()
	Expect(err).To(Equal(ErrFinished))
}

func (s *TaskSuite) TestFinished(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		return NewValueResult(args[0], nil)
	}, 1)

	res, err := task.Wait(waitTimeout)
	Expect(err).To(BeNil())
	Expect(res).To(Equal(&ValueResult{Value: 1, Error: nil}))

	Eventually(task.Finished()).Should(BeClosed())
	Expect(task.resultChan).To(BeClosed())
}

func (s *TaskSuite) TestStarted(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		<-task.Stopping()
		return NewValueResult(args[0], nil)
	}, 1)

	Expect(task.Started()).To(BeClosed())

	task.Stop()
	task.Wait(waitTimeout)
}

func (s *TaskSuite) TestRunning(t sweet.T) {
	advance := make(chan int)
	advanced := make(chan int)
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		//c.Log("Started task")
		<-advance
		//c.Log("Setting running to true")
		task.SetRunning(true)
		advanced <- 1
		<-advance
		//c.Log("Setting running to false")
		task.SetRunning(false)
		advanced <- 1
		<-task.Stopping()

		//c.Log("Task returning")
		return NewValueResult(nil, nil)
	})

	Expect(task.Running()).ToNot(BeClosed())
	//c.Log("Advancing to running")
	advance <- 1
	<-advanced
	Expect(task.Running()).To(BeClosed())
	//c.Log("Advancing to not running")
	advance <- 1
	<-advanced
	Expect(task.Running()).ToNot(BeClosed())

	//c.Log("Stopping")
	task.Stop()
}

func (s *TaskSuite) TestWaitForRunning(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		task.SetRunning(true)
		<-task.Stopping()

		return NewValueResult(nil, nil)
	})

	err := task.WaitForRunning(1 * time.Second)
	Expect(err).To(BeNil())

	_, err = task.StopAndWait(1 * time.Second)
	Expect(err).To(BeNil())
}

func (s *TaskSuite) TestWaitForRunningTimeout(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		<-task.Stopping()
		return NewValueResult(nil, nil)
	})

	err := task.WaitForRunning(100 * time.Millisecond)
	Expect(err).To(Equal(ErrTimeout))
	_, err = task.StopAndWait(1 * time.Second)
	Expect(err).To(BeNil())
}

func (s *TaskSuite) TestWaitForRunningTaskFinished(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		return NewErrorResult(errors.New("I'm an error! - Ralph"))
	})

	err := task.WaitForRunning(100 * time.Millisecond)
	Expect(err).To(BeNil())
	res, err := task.Wait(100 * time.Millisecond)
	Expect(err).To(BeNil())
	Expect(res).To(Equal(NewErrorResult(errors.New("I'm an error! - Ralph"))))
}

func (s *TaskSuite) TestRunningSetFalseWhenFinished(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		task.SetRunning(true)
		<-task.Stopping()
		return NewValueResult(nil, nil)
	})

	err := task.WaitForRunning(100 * time.Millisecond)
	Expect(err).To(BeNil())
	_, err = task.StopAndWait(1 * time.Second)
	Expect(err).To(BeNil())
	Expect(task.Running()).ToNot(BeClosed())
}

func (s *TaskSuite) TestWaitTwice(t sweet.T) {
	task := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		task.SetRunning(true)
		<-task.Stopping()
		return NewValueResult(args[0], nil)
	}, 1)

	task.Start()

	task.WaitForRunning(0)

	task.Stop()

	res, err := task.Wait(waitTimeout)
	Expect(err).To(BeNil())
	Expect(res).To(Equal(&ValueResult{Value: 1, Error: nil}))
	Expect(task.resultChan).To(BeClosed())

	res, err = task.Wait(waitTimeout)
	Expect(err).To(BeNil())
	Expect(res).To(Equal(&ValueResult{Value: 1, Error: nil}))
	Expect(task.resultChan).To(BeClosed())
}

func (s *TaskSuite) TestWaitTimeout(t sweet.T) {
	task := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		time.Sleep(100 * time.Millisecond)
		return NewValueResult(args[0], nil)
	}, 1)

	task.Start()

	res, err := task.Wait(10 * time.Millisecond)
	Expect(err).To(Equal(ErrTimeout))
	Expect(res).To(BeNil())
	Expect(task.resultChan).ToNot(BeClosed())

	res, err = task.Wait(200 * time.Millisecond)
	Expect(err).To(BeNil())
	Expect(res).To(Equal(&ValueResult{Value: 1, Error: nil}))
	Expect(task.resultChan).To(BeClosed())
}

func (s *TaskSuite) TestDiscardBeforeRunning(t sweet.T) {
	task := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		return NewValueResult(nil, nil)
	})

	task.Discard()
	task.Start()

	Eventually(task.resultChan).Should(BeClosed())
}

func (s *TaskSuite) TestDiscardAfterRunning(t sweet.T) {
	task := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		return NewValueResult(nil, nil)
	})

	task.Start()

	// Yield to allow the runtime to start goroutines
	time.Sleep(time.Millisecond)

	task.Discard()

	// Yield to allow the runtime to start goroutines
	time.Sleep(time.Millisecond)

	Eventually(task.resultChan).Should(BeClosed())
}

func (s *TaskSuite) TestStartStarted(t sweet.T) {
	task := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		<-task.Stopping()
		return NewValueResult(nil, nil)
	})

	err := task.Start()
	Expect(err).To(BeNil())
	err = task.Start()
	Expect(err).ToNot(BeNil())
	Expect(err).To(Equal(ErrExecuting))

	task.Stop()
	task.Wait(waitTimeout)
}

func (s *TaskSuite) TestStopStopped(t sweet.T) {
	task := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		<-task.Stopping()
		return NewValueResult(nil, nil)
	})

	err := task.Stop()
	Expect(err).To(Equal(ErrNotExecuting))
}

func (s *TaskSuite) TestWaitStopped(t sweet.T) {
	task := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		<-task.Stopping()
		return NewValueResult(nil, nil)
	})

	res, err := task.Wait(waitTimeout)
	Expect(err).To(Equal(ErrNotExecuting))
	Expect(res).To(BeNil())
}

func (s *TaskSuite) TestStopAndWait(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		<-task.Stopping()
		return NewValueResult(1, nil)
	})

	Eventually(task.Started()).Should(BeClosed())

	res, err := task.StopAndWait(waitTimeout)
	Expect(err).To(BeNil())
	Expect(res).To(Equal(&ValueResult{Value: 1, Error: nil}))
}

func (s *TaskSuite) TestStopAndWaitWhileStopped(t sweet.T) {
	task := newTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		<-task.Stopping()
		return NewValueResult(1, nil)
	})

	res, err := task.StopAndWait(waitTimeout)
	Expect(err).To(Equal(ErrNotExecuting))
	Expect(res).To(BeNil())
}

func (s *TaskSuite) TestStopAndWaitTimeout(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		time.Sleep(25 * time.Millisecond)
		<-task.Stopping()
		return NewValueResult(1, nil)
	})

	res, err := task.StopAndWait(10 * time.Millisecond)
	Expect(err).To(Equal(ErrTimeout))
	Expect(res).To(BeNil())
}

func (s *TaskSuite) TestNilResult(t sweet.T) {
	task := runTask(context.Background(), newTaskConfig(), func(task *Task, args ...interface{}) TaskResult {
		return nil
	})

	res, err := task.Wait(waitTimeout)
	Expect(err).To(BeNil())
	Expect(res).To(BeNil())
}

func (s *TaskSuite) TestValueResultErr(t sweet.T) {
	res := NewValueResult(1234, errors.New("I'm an error - Ralph"))
	Expect(res.Err()).To(Equal(errors.New("I'm an error - Ralph")))
}

func (s *TaskSuite) TestErrorResultErr(t sweet.T) {
	res := NewErrorResult(errors.New("I'm an error - Ralph"))
	Expect(res.Err()).To(Equal(errors.New("I'm an error - Ralph")))
}
<|endoftext|>


<commit_before>package auth

import (
	"encoding/base64"
	"errors"
	"net/http"
	"strings"

	"code.google.com/p/go.crypto/bcrypt"
)

type Handler struct {
	Handler        http.Handler
	Username       string
	HashedPassword string
}

func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	username, password, err := ExtractUsernameAndPassword(r.Header.Get("Authorization"))
	if err != nil {
		w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	err = bcrypt.CompareHashAndPassword([]byte(h.HashedPassword), []byte(password))
	if username == h.Username && err == nil {
		h.Handler.ServeHTTP(w, r)
	} else {
		w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
		w.WriteHeader(http.StatusUnauthorized)
	}
}

var ErrUnparsableHeader = errors.New("unparseable Authorization header")

func ExtractUsernameAndPassword(authorizationHeader string) (string, string, error) {
	if !strings.HasPrefix(authorizationHeader, "Basic ") {
		return "", "", ErrUnparsableHeader
	}

	substring := authorizationHeader[6:]
	decodedSubstring, err := base64.StdEncoding.DecodeString(substring)
	if err != nil {
		return "", "", ErrUnparsableHeader
	}

	parts := strings.Split(string(decodedSubstring), ":")
	if len(parts) != 2 {
		return "", "", ErrUnparsableHeader
	}

	return parts[0], parts[1], nil
}
<commit_msg>extract unauthorized response<commit_after>package auth

import (
	"encoding/base64"
	"errors"
	"net/http"
	"strings"

	"code.google.com/p/go.crypto/bcrypt"
)

type Handler struct {
	Handler        http.Handler
	Username       string
	HashedPassword string
}

func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	username, password, err := ExtractUsernameAndPassword(r.Header.Get("Authorization"))
	if err != nil {
		h.unauthorized(w)
		return
	}

	err = bcrypt.CompareHashAndPassword([]byte(h.HashedPassword), []byte(password))
	if username == h.Username && err == nil {
		h.Handler.ServeHTTP(w, r)
	} else {
		h.unauthorized(w)
	}
}

func (h Handler) unauthorized(w http.ResponseWriter) {
	w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
	w.WriteHeader(http.StatusUnauthorized)
}

var ErrUnparsableHeader = errors.New("unparseable Authorization header")

func ExtractUsernameAndPassword(authorizationHeader string) (string, string, error) {
	if !strings.HasPrefix(authorizationHeader, "Basic ") {
		return "", "", ErrUnparsableHeader
	}

	substring := authorizationHeader[6:]
	decodedSubstring, err := base64.StdEncoding.DecodeString(substring)
	if err != nil {
		return "", "", ErrUnparsableHeader
	}

	parts := strings.Split(string(decodedSubstring), ":")
	if len(parts) != 2 {
		return "", "", ErrUnparsableHeader
	}

	return parts[0], parts[1], nil
}
<|endoftext|>


<commit_before>package whois

import (
	"net/http"
	"io"
	"io/ioutil"
	"net"
	"time"
)

var Timeout = 10 * time.Second

// Request represents a whois request
type Request struct {
	Query   string
	Host    string
	URL     string
	Body    string
	Timeout time.Duration
}

func NewRequest(q string) *Request {
	return &Request{Query: q, Timeout: Timeout}
}

func (req *Request) Fetch() (*Response, error) {
	if req.URL == "" {
		return req.fetchWhois()
	}
	return req.fetchHTTP()
}

func (req *Request) fetchWhois() (*Response, error) {
	res := &Response{Request: req, FetchedAt: time.Now()}

	c, err := net.DialTimeout("tcp", req.Host+":43", req.Timeout)
	if err != nil {
		return res, err
	}
	defer c.Close()
	c.SetDeadline(time.Now().Add(req.Timeout))
	if _, err = io.WriteString(c, req.Body); err != nil {
		return res, err
	}
	if res.Body, err = ioutil.ReadAll(c); err != nil {
		return res, err
	}

	return res, nil
}

func (req *Request) fetchHTTP() (*Response, error) {
	res := &Response{Request: req, FetchedAt: time.Now()}

	hres, err := http.Get(req.URL)
	if err != nil {
		return res, err
	}
	defer hres.Body.Close()
	if res.Body, err = ioutil.ReadAll(hres.Body); err != nil {
		return res, err
	}

	return res, nil
}
<commit_msg>fetchHTTP -> fetchURL<commit_after>package whois

import (
	"net/http"
	"io"
	"io/ioutil"
	"net"
	"time"
)

var Timeout = 10 * time.Second

// Request represents a whois request
type Request struct {
	Query   string
	Host    string
	URL     string
	Body    string
	Timeout time.Duration
}

func NewRequest(q string) *Request {
	return &Request{Query: q, Timeout: Timeout}
}

func (req *Request) Fetch() (*Response, error) {
	if req.URL != "" {
		return req.fetchURL()
	}
	return req.fetchWhois()
}

func (req *Request) fetchWhois() (*Response, error) {
	res := &Response{Request: req, FetchedAt: time.Now()}

	c, err := net.DialTimeout("tcp", req.Host+":43", req.Timeout)
	if err != nil {
		return res, err
	}
	defer c.Close()
	c.SetDeadline(time.Now().Add(req.Timeout))
	if _, err = io.WriteString(c, req.Body); err != nil {
		return res, err
	}
	if res.Body, err = ioutil.ReadAll(c); err != nil {
		return res, err
	}

	return res, nil
}

func (req *Request) fetchURL() (*Response, error) {
	res := &Response{Request: req, FetchedAt: time.Now()}

	hres, err := http.Get(req.URL)
	if err != nil {
		return res, err
	}
	defer hres.Body.Close()
	if res.Body, err = ioutil.ReadAll(hres.Body); err != nil {
		return res, err
	}

	return res, nil
}
<|endoftext|>


<commit_before>package sftp

import (
	"context"
	"io"
	"os"
	"path"
	"path/filepath"
	"sync"
	"syscall"

	"github.com/pkg/errors"
)

// MaxFilelist is the max number of files to return in a readdir batch.
var MaxFilelist int64 = 100

// Request contains the data and state for the incoming service request.
type Request struct {
	// Get, Put, Setstat, Stat, Rename, Remove
	// Rmdir, Mkdir, List, Readlink, Symlink
	Method   string
	Filepath string
	Flags    uint32
	Attrs    []byte // convert to sub-struct
	Target   string // for renames and sym-links
	// reader/writer/readdir from handlers
	state state
	// context lasts duration of request
	ctx       context.Context
	cancelCtx context.CancelFunc
}

type state struct {
	*sync.RWMutex
	writerAt io.WriterAt
	readerAt io.ReaderAt
	listerAt ListerAt
	lsoffset int64
}

// New Request initialized based on packet data
func requestFromPacket(ctx context.Context, pkt hasPath) *Request {
	method := requestMethod(pkt)
	request := NewRequest(method, pkt.getPath())
	request.ctx, request.cancelCtx = context.WithCancel(ctx)

	switch p := pkt.(type) {
	case *sshFxpOpenPacket:
		request.Flags = p.Pflags
	case *sshFxpSetstatPacket:
		request.Flags = p.Flags
		request.Attrs = p.Attrs.([]byte)
	case *sshFxpRenamePacket:
		request.Target = cleanPath(p.Newpath)
	case *sshFxpSymlinkPacket:
		request.Target = cleanPath(p.Linkpath)
	}
	return request
}

// NewRequest creates a new Request object.
func NewRequest(method, path string) *Request {
	return &Request{Method: method, Filepath: cleanPath(path),
		state: state{RWMutex: new(sync.RWMutex)}}
}

// shallow copy of existing request
func (r *Request) copy() *Request {
	r.state.Lock()
	defer r.state.Unlock()
	r2 := new(Request)
	*r2 = *r
	return r2
}

// Context returns the request's context. To change the context,
// use WithContext.
//
// The returned context is always non-nil; it defaults to the
// background context.
//
// For incoming server requests, the context is canceled when the
// request is complete or the client's connection closes.
func (r *Request) Context() context.Context {
	if r.ctx != nil {
		return r.ctx
	}
	return context.Background()
}

// WithContext returns a copy of r with its context changed to ctx.
// The provided ctx must be non-nil.
func (r *Request) WithContext(ctx context.Context) *Request {
	if ctx == nil {
		panic("nil context")
	}
	r2 := r.copy()
	r2.ctx = ctx
	r2.cancelCtx = nil
	return r2
}

// Returns current offset for file list
func (r *Request) lsNext() int64 {
	r.state.RLock()
	defer r.state.RUnlock()
	return r.state.lsoffset
}

// Increases next offset
func (r *Request) lsInc(offset int64) {
	r.state.Lock()
	defer r.state.Unlock()
	r.state.lsoffset = r.state.lsoffset + offset
}

// manage file read/write state
func (r *Request) setWriterState(wa io.WriterAt) {
	r.state.Lock()
	defer r.state.Unlock()
	r.state.writerAt = wa
}
func (r *Request) setReaderState(ra io.ReaderAt) {
	r.state.Lock()
	defer r.state.Unlock()
	r.state.readerAt = ra
}
func (r *Request) setListerState(la ListerAt) {
	r.state.Lock()
	defer r.state.Unlock()
	r.state.listerAt = la
}

func (r *Request) getWriter() io.WriterAt {
	r.state.RLock()
	defer r.state.RUnlock()
	return r.state.writerAt
}

func (r *Request) getReader() io.ReaderAt {
	r.state.RLock()
	defer r.state.RUnlock()
	return r.state.readerAt
}

func (r *Request) getLister() ListerAt {
	r.state.RLock()
	defer r.state.RUnlock()
	return r.state.listerAt
}

// Close reader/writer if possible
func (r *Request) close() error {
	rd := r.getReader()
	if c, ok := rd.(io.Closer); ok {
		return c.Close()
	}
	wt := r.getWriter()
	if c, ok := wt.(io.Closer); ok {
		return c.Close()
	}
	if r.cancelCtx != nil {
		r.cancelCtx()
	}
	return nil
}

// called from worker to handle packet/request
func (r *Request) call(handlers Handlers, pkt requestPacket) responsePacket {
	switch r.Method {
	case "Get":
		return fileget(handlers.FileGet, r, pkt)
	case "Put", "Open":
		return fileput(handlers.FilePut, r, pkt)
	case "Setstat", "Rename", "Rmdir", "Mkdir", "Symlink", "Remove":
		return filecmd(handlers.FileCmd, r, pkt)
	case "List", "Stat", "Readlink":
		return filelist(handlers.FileList, r, pkt)
	default:
		return statusFromError(pkt,
			errors.Errorf("unexpected method: %s", r.Method))
	}
}

// file data for additional read/write packets
func packetData(p requestPacket) (data []byte, offset int64, length uint32) {
	switch p := p.(type) {
	case *sshFxpReadPacket:
		length = p.Len
		offset = int64(p.Offset)
	case *sshFxpWritePacket:
		data = p.Data
		length = p.Length
		offset = int64(p.Offset)
	}
	return
}

// wrap FileReader handler
func fileget(h FileReader, r *Request, pkt requestPacket) responsePacket {
	var err error
	reader := r.getReader()
	if reader == nil {
		reader, err = h.Fileread(r)
		if err != nil {
			return statusFromError(pkt, err)
		}
		r.setReaderState(reader)
	}

	_, offset, length := packetData(pkt)
	data := make([]byte, clamp(length, maxTxPacket))
	n, err := reader.ReadAt(data, offset)
	// only return EOF erro if no data left to read
	if err != nil && (err != io.EOF || n == 0) {
		return statusFromError(pkt, err)
	}
	return &sshFxpDataPacket{
		ID:     pkt.id(),
		Length: uint32(n),
		Data:   data[:n],
	}
}

// wrap FileWriter handler
func fileput(h FileWriter, r *Request, pkt requestPacket) responsePacket {
	var err error
	writer := r.getWriter()
	if writer == nil {
		writer, err = h.Filewrite(r)
		if err != nil {
			return statusFromError(pkt, err)
		}
		r.setWriterState(writer)
	}

	data, offset, _ := packetData(pkt)
	_, err = writer.WriteAt(data, offset)
	return statusFromError(pkt, err)
}

// wrap FileCmder handler
func filecmd(h FileCmder, r *Request, pkt requestPacket) responsePacket {

	switch p := pkt.(type) {
	case *sshFxpFsetstatPacket:
		r.Flags = p.Flags
		r.Attrs = p.Attrs.([]byte)
	}
	err := h.Filecmd(r)
	return statusFromError(pkt, err)
}

// wrap FileLister handler
func filelist(h FileLister, r *Request, pkt requestPacket) responsePacket {
	var err error
	lister := r.getLister()
	if lister == nil {
		lister, err = h.Filelist(r)
		if err != nil {
			return statusFromError(pkt, err)
		}
		r.setListerState(lister)
	}

	offset := r.lsNext()
	finfo := make([]os.FileInfo, MaxFilelist)
	n, err := lister.ListAt(finfo, offset)
	r.lsInc(int64(n))
	// ignore EOF as we only return it when there are no results
	finfo = finfo[:n] // avoid need for nil tests below

	switch r.Method {
	case "List":
		if err != nil && err != io.EOF {
			return statusFromError(pkt, err)
		}
		if err == io.EOF && n == 0 {
			return statusFromError(pkt, io.EOF)
		}
		dirname := filepath.ToSlash(path.Base(r.Filepath))
		ret := &sshFxpNamePacket{ID: pkt.id()}

		for _, fi := range finfo {
			ret.NameAttrs = append(ret.NameAttrs, sshFxpNameAttr{
				Name:     fi.Name(),
				LongName: runLs(dirname, fi),
				Attrs:    []interface{}{fi},
			})
		}
		return ret
	case "Stat":
		if err != nil && err != io.EOF {
			return statusFromError(pkt, err)
		}
		if n == 0 {
			err = &os.PathError{Op: "stat", Path: r.Filepath,
				Err: syscall.ENOENT}
			return statusFromError(pkt, err)
		}
		return &sshFxpStatResponse{
			ID:   pkt.id(),
			info: finfo[0],
		}
	case "Readlink":
		if err != nil && err != io.EOF {
			return statusFromError(pkt, err)
		}
		if n == 0 {
			err = &os.PathError{Op: "readlink", Path: r.Filepath,
				Err: syscall.ENOENT}
			return statusFromError(pkt, err)
		}
		filename := finfo[0].Name()
		return &sshFxpNamePacket{
			ID: pkt.id(),
			NameAttrs: []sshFxpNameAttr{{
				Name:     filename,
				LongName: filename,
				Attrs:    emptyFileStat,
			}},
		}
	default:
		err = errors.Errorf("unexpected method: %s", r.Method)
		return statusFromError(pkt, err)
	}
}

// init attributes of request object from packet data
func requestMethod(p requestPacket) (method string) {
	switch p.(type) {
	case *sshFxpReadPacket:
		method = "Get"
	case *sshFxpWritePacket:
		method = "Put"
	case *sshFxpReaddirPacket:
		method = "List"
	case *sshFxpOpenPacket, *sshFxpOpendirPacket:
		method = "Open"
	case *sshFxpSetstatPacket, *sshFxpFsetstatPacket:
		method = "Setstat"
	case *sshFxpRenamePacket:
		method = "Rename"
	case *sshFxpSymlinkPacket:
		method = "Symlink"
	case *sshFxpRemovePacket:
		method = "Remove"
	case *sshFxpStatPacket, *sshFxpLstatPacket, *sshFxpFstatPacket:
		method = "Stat"
	case *sshFxpRmdirPacket:
		method = "Rmdir"
	case *sshFxpReadlinkPacket:
		method = "Readlink"
	case *sshFxpMkdirPacket:
		method = "Mkdir"
	}
	return method
}
<commit_msg>Be sure to cancel context when Close is called.<commit_after>package sftp

import (
	"context"
	"io"
	"os"
	"path"
	"path/filepath"
	"sync"
	"syscall"

	"github.com/pkg/errors"
)

// MaxFilelist is the max number of files to return in a readdir batch.
var MaxFilelist int64 = 100

// Request contains the data and state for the incoming service request.
type Request struct {
	// Get, Put, Setstat, Stat, Rename, Remove
	// Rmdir, Mkdir, List, Readlink, Symlink
	Method   string
	Filepath string
	Flags    uint32
	Attrs    []byte // convert to sub-struct
	Target   string // for renames and sym-links
	// reader/writer/readdir from handlers
	state state
	// context lasts duration of request
	ctx       context.Context
	cancelCtx context.CancelFunc
}

type state struct {
	*sync.RWMutex
	writerAt io.WriterAt
	readerAt io.ReaderAt
	listerAt ListerAt
	lsoffset int64
}

// New Request initialized based on packet data
func requestFromPacket(ctx context.Context, pkt hasPath) *Request {
	method := requestMethod(pkt)
	request := NewRequest(method, pkt.getPath())
	request.ctx, request.cancelCtx = context.WithCancel(ctx)

	switch p := pkt.(type) {
	case *sshFxpOpenPacket:
		request.Flags = p.Pflags
	case *sshFxpSetstatPacket:
		request.Flags = p.Flags
		request.Attrs = p.Attrs.([]byte)
	case *sshFxpRenamePacket:
		request.Target = cleanPath(p.Newpath)
	case *sshFxpSymlinkPacket:
		request.Target = cleanPath(p.Linkpath)
	}
	return request
}

// NewRequest creates a new Request object.
func NewRequest(method, path string) *Request {
	return &Request{Method: method, Filepath: cleanPath(path),
		state: state{RWMutex: new(sync.RWMutex)}}
}

// shallow copy of existing request
func (r *Request) copy() *Request {
	r.state.Lock()
	defer r.state.Unlock()
	r2 := new(Request)
	*r2 = *r
	return r2
}

// Context returns the request's context. To change the context,
// use WithContext.
//
// The returned context is always non-nil; it defaults to the
// background context.
//
// For incoming server requests, the context is canceled when the
// request is complete or the client's connection closes.
func (r *Request) Context() context.Context {
	if r.ctx != nil {
		return r.ctx
	}
	return context.Background()
}

// WithContext returns a copy of r with its context changed to ctx.
// The provided ctx must be non-nil.
func (r *Request) WithContext(ctx context.Context) *Request {
	if ctx == nil {
		panic("nil context")
	}
	r2 := r.copy()
	r2.ctx = ctx
	r2.cancelCtx = nil
	return r2
}

// Returns current offset for file list
func (r *Request) lsNext() int64 {
	r.state.RLock()
	defer r.state.RUnlock()
	return r.state.lsoffset
}

// Increases next offset
func (r *Request) lsInc(offset int64) {
	r.state.Lock()
	defer r.state.Unlock()
	r.state.lsoffset = r.state.lsoffset + offset
}

// manage file read/write state
func (r *Request) setWriterState(wa io.WriterAt) {
	r.state.Lock()
	defer r.state.Unlock()
	r.state.writerAt = wa
}
func (r *Request) setReaderState(ra io.ReaderAt) {
	r.state.Lock()
	defer r.state.Unlock()
	r.state.readerAt = ra
}
func (r *Request) setListerState(la ListerAt) {
	r.state.Lock()
	defer r.state.Unlock()
	r.state.listerAt = la
}

func (r *Request) getWriter() io.WriterAt {
	r.state.RLock()
	defer r.state.RUnlock()
	return r.state.writerAt
}

func (r *Request) getReader() io.ReaderAt {
	r.state.RLock()
	defer r.state.RUnlock()
	return r.state.readerAt
}

func (r *Request) getLister() ListerAt {
	r.state.RLock()
	defer r.state.RUnlock()
	return r.state.listerAt
}

// Close reader/writer if possible
func (r *Request) close() error {
	defer func() {
		if r.cancelCtx != nil {
			r.cancelCtx()
		}
	}()
	rd := r.getReader()
	if c, ok := rd.(io.Closer); ok {
		return c.Close()
	}
	wt := r.getWriter()
	if c, ok := wt.(io.Closer); ok {
		return c.Close()
	}
	return nil
}

// called from worker to handle packet/request
func (r *Request) call(handlers Handlers, pkt requestPacket) responsePacket {
	switch r.Method {
	case "Get":
		return fileget(handlers.FileGet, r, pkt)
	case "Put", "Open":
		return fileput(handlers.FilePut, r, pkt)
	case "Setstat", "Rename", "Rmdir", "Mkdir", "Symlink", "Remove":
		return filecmd(handlers.FileCmd, r, pkt)
	case "List", "Stat", "Readlink":
		return filelist(handlers.FileList, r, pkt)
	default:
		return statusFromError(pkt,
			errors.Errorf("unexpected method: %s", r.Method))
	}
}

// file data for additional read/write packets
func packetData(p requestPacket) (data []byte, offset int64, length uint32) {
	switch p := p.(type) {
	case *sshFxpReadPacket:
		length = p.Len
		offset = int64(p.Offset)
	case *sshFxpWritePacket:
		data = p.Data
		length = p.Length
		offset = int64(p.Offset)
	}
	return
}

// wrap FileReader handler
func fileget(h FileReader, r *Request, pkt requestPacket) responsePacket {
	var err error
	reader := r.getReader()
	if reader == nil {
		reader, err = h.Fileread(r)
		if err != nil {
			return statusFromError(pkt, err)
		}
		r.setReaderState(reader)
	}

	_, offset, length := packetData(pkt)
	data := make([]byte, clamp(length, maxTxPacket))
	n, err := reader.ReadAt(data, offset)
	// only return EOF erro if no data left to read
	if err != nil && (err != io.EOF || n == 0) {
		return statusFromError(pkt, err)
	}
	return &sshFxpDataPacket{
		ID:     pkt.id(),
		Length: uint32(n),
		Data:   data[:n],
	}
}

// wrap FileWriter handler
func fileput(h FileWriter, r *Request, pkt requestPacket) responsePacket {
	var err error
	writer := r.getWriter()
	if writer == nil {
		writer, err = h.Filewrite(r)
		if err != nil {
			return statusFromError(pkt, err)
		}
		r.setWriterState(writer)
	}

	data, offset, _ := packetData(pkt)
	_, err = writer.WriteAt(data, offset)
	return statusFromError(pkt, err)
}

// wrap FileCmder handler
func filecmd(h FileCmder, r *Request, pkt requestPacket) responsePacket {

	switch p := pkt.(type) {
	case *sshFxpFsetstatPacket:
		r.Flags = p.Flags
		r.Attrs = p.Attrs.([]byte)
	}
	err := h.Filecmd(r)
	return statusFromError(pkt, err)
}

// wrap FileLister handler
func filelist(h FileLister, r *Request, pkt requestPacket) responsePacket {
	var err error
	lister := r.getLister()
	if lister == nil {
		lister, err = h.Filelist(r)
		if err != nil {
			return statusFromError(pkt, err)
		}
		r.setListerState(lister)
	}

	offset := r.lsNext()
	finfo := make([]os.FileInfo, MaxFilelist)
	n, err := lister.ListAt(finfo, offset)
	r.lsInc(int64(n))
	// ignore EOF as we only return it when there are no results
	finfo = finfo[:n] // avoid need for nil tests below

	switch r.Method {
	case "List":
		if err != nil && err != io.EOF {
			return statusFromError(pkt, err)
		}
		if err == io.EOF && n == 0 {
			return statusFromError(pkt, io.EOF)
		}
		dirname := filepath.ToSlash(path.Base(r.Filepath))
		ret := &sshFxpNamePacket{ID: pkt.id()}

		for _, fi := range finfo {
			ret.NameAttrs = append(ret.NameAttrs, sshFxpNameAttr{
				Name:     fi.Name(),
				LongName: runLs(dirname, fi),
				Attrs:    []interface{}{fi},
			})
		}
		return ret
	case "Stat":
		if err != nil && err != io.EOF {
			return statusFromError(pkt, err)
		}
		if n == 0 {
			err = &os.PathError{Op: "stat", Path: r.Filepath,
				Err: syscall.ENOENT}
			return statusFromError(pkt, err)
		}
		return &sshFxpStatResponse{
			ID:   pkt.id(),
			info: finfo[0],
		}
	case "Readlink":
		if err != nil && err != io.EOF {
			return statusFromError(pkt, err)
		}
		if n == 0 {
			err = &os.PathError{Op: "readlink", Path: r.Filepath,
				Err: syscall.ENOENT}
			return statusFromError(pkt, err)
		}
		filename := finfo[0].Name()
		return &sshFxpNamePacket{
			ID: pkt.id(),
			NameAttrs: []sshFxpNameAttr{{
				Name:     filename,
				LongName: filename,
				Attrs:    emptyFileStat,
			}},
		}
	default:
		err = errors.Errorf("unexpected method: %s", r.Method)
		return statusFromError(pkt, err)
	}
}

// init attributes of request object from packet data
func requestMethod(p requestPacket) (method string) {
	switch p.(type) {
	case *sshFxpReadPacket:
		method = "Get"
	case *sshFxpWritePacket:
		method = "Put"
	case *sshFxpReaddirPacket:
		method = "List"
	case *sshFxpOpenPacket, *sshFxpOpendirPacket:
		method = "Open"
	case *sshFxpSetstatPacket, *sshFxpFsetstatPacket:
		method = "Setstat"
	case *sshFxpRenamePacket:
		method = "Rename"
	case *sshFxpSymlinkPacket:
		method = "Symlink"
	case *sshFxpRemovePacket:
		method = "Remove"
	case *sshFxpStatPacket, *sshFxpLstatPacket, *sshFxpFstatPacket:
		method = "Stat"
	case *sshFxpRmdirPacket:
		method = "Rmdir"
	case *sshFxpReadlinkPacket:
		method = "Readlink"
	case *sshFxpMkdirPacket:
		method = "Mkdir"
	}
	return method
}
<|endoftext|>


<commit_before>// Copyright 2015 The Cockroach 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. See the AUTHORS file
// for names of contributors.
//
// Author: Peter Mattis (peter@cockroachlabs.com)

package cli

import (
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/cockroachdb/cockroach/security"
	"github.com/cockroachdb/cockroach/server"
)

type cliTest struct {
	*server.TestServer
}

func newCLITest() cliTest {
	// Reset the client context for each test. We don't reset the
	// pointer (because they are tied into the flags), but instead
	// overwrite the existing struct's values.
	*Context = *server.NewContext()

	osExit = func(int) {}
	osStderr = os.Stdout

	s := &server.TestServer{}
	if err := s.Start(); err != nil {
		log.Fatalf("Could not start server: %v", err)
	}

	return cliTest{TestServer: s}
}

func (c cliTest) Run(line string) {
	a := strings.Fields(line)

	var args []string
	args = append(args, a[0])
	args = append(args, fmt.Sprintf("--addr=%s", c.ServingAddr()))
	// Always load test certs.
	args = append(args, fmt.Sprintf("--certs=%s", security.EmbeddedCertsDir))
	args = append(args, a[1:]...)

	fmt.Fprintf(os.Stderr, "%s\n", args)
	fmt.Println(line)
	if err := Run(args); err != nil {
		fmt.Println(err)
	}
}

func ExampleBasic() {
	c := newCLITest()

	c.Run("kv put a 1 b 2")
	c.Run("kv scan")
	c.Run("kv del a")
	c.Run("kv get a")
	c.Run("kv get b")
	c.Run("kv inc c 1")
	c.Run("kv inc c 10")
	c.Run("kv inc c 100")
	c.Run("kv scan")
	c.Run("kv inc c b")
	c.Run("quit")

	// Output:
	// kv put a 1 b 2
	// kv scan
	// "a"	"1"
	// "b"	"2"
	// kv del a
	// kv get a
	// "a" not found
	// kv get b
	// "2"
	// kv inc c 1
	// 1
	// kv inc c 10
	// 11
	// kv inc c 100
	// 111
	// kv scan
	// "b"	"2"
	// "c"	"\x00\x00\x00\x00\x00\x00\x00o"
	// kv inc c b
	// invalid increment: b: strconv.ParseInt: parsing "b": invalid syntax
	// quit
	// node drained and shutdown: ok
}

func ExampleQuoted() {
	c := newCLITest()

	c.Run(`kv put a\x00 日本語`)                                  // UTF-8 input text
	c.Run(`kv put a\x01 \u65e5\u672c\u8a9e`)                   // explicit Unicode code points
	c.Run(`kv put a\x02 \U000065e5\U0000672c\U00008a9e`)       // explicit Unicode code points
	c.Run(`kv put a\x03 \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e`) // explicit UTF-8 bytes
	c.Run(`kv scan`)
	c.Run(`kv get a\x00`)
	c.Run(`kv del a\x00`)
	c.Run(`kv inc 1\x01`)
	c.Run(`kv get 1\x01`)
	c.Run("quit")

	// Output:
	// kv put a\x00 日本語
	// kv put a\x01 \u65e5\u672c\u8a9e
	// kv put a\x02 \U000065e5\U0000672c\U00008a9e
	// kv put a\x03 \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e
	// kv scan
	// "a\x00"	"日本語"
	// "a\x01"	"日本語"
	// "a\x02"	"日本語"
	// "a\x03"	"日本語"
	// kv get a\x00
	// "日本語"
	// kv del a\x00
	// kv inc 1\x01
	// 1
	// kv get 1\x01
	// "\x00\x00\x00\x00\x00\x00\x00\x01"
	// quit
	// node drained and shutdown: ok
}

func ExampleInsecure() {
	c := cliTest{}
	c.TestServer = &server.TestServer{}
	c.Ctx = server.NewTestContext()
	c.Ctx.Insecure = true
	if err := c.Start(); err != nil {
		log.Fatalf("Could not start server: %v", err)
	}

	c.Run("kv --insecure put a 1 b 2")
	c.Run("kv --insecure scan")
	c.Run("quit --insecure")

	// Output:
	// kv --insecure put a 1 b 2
	// kv --insecure scan
	// "a"	"1"
	// "b"	"2"
	// quit --insecure
	// node drained and shutdown: ok
}

func ExampleSplitMergeRanges() {
	c := newCLITest()

	c.Run("kv put a 1 b 2 c 3 d 4")
	c.Run("kv scan")
	c.Run("range split c")
	c.Run("range ls")
	c.Run("kv scan")
	c.Run("range merge b")
	c.Run("range ls")
	c.Run("kv scan")
	c.Run("quit")

	// Output:
	// kv put a 1 b 2 c 3 d 4
	// kv scan
	// "a"	"1"
	// "b"	"2"
	// "c"	"3"
	// "d"	"4"
	// range split c
	// range ls
	// ""-"c" [1]
	// 	0: node-id=1 store-id=1
	// "c"-"\xff\xff" [2]
	// 	0: node-id=1 store-id=1
	// kv scan
	// "a"	"1"
	// "b"	"2"
	// "c"	"3"
	// "d"	"4"
	// range merge b
	// range ls
	// ""-"\xff\xff" [1]
	// 	0: node-id=1 store-id=1
	// kv scan
	// "a"	"1"
	// "b"	"2"
	// "c"	"3"
	// "d"	"4"
	// quit
	// node drained and shutdown: ok
}

func ExampleGlogFlags() {
	c := newCLITest()

	c.Run("kv --alsologtostderr=false scan")
	c.Run("kv --log-backtrace-at=foo.go:1 scan")
	c.Run("kv --log-dir='' scan")
	c.Run("kv --logtostderr=true scan")
	c.Run("kv --verbosity=0 scan")
	c.Run("kv --vmodule=foo=1 scan")

	// Output:
	// kv --alsologtostderr=false scan
	// kv --log-backtrace-at=foo.go:1 scan
	// kv --log-dir='' scan
	// kv --logtostderr=true scan
	// kv --verbosity=0 scan
	// kv --vmodule=foo=1 scan
}
<commit_msg>fix server/cli Example names<commit_after>// Copyright 2015 The Cockroach 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. See the AUTHORS file
// for names of contributors.
//
// Author: Peter Mattis (peter@cockroachlabs.com)

package cli

import (
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/cockroachdb/cockroach/security"
	"github.com/cockroachdb/cockroach/server"
)

type cliTest struct {
	*server.TestServer
}

func newCLITest() cliTest {
	// Reset the client context for each test. We don't reset the
	// pointer (because they are tied into the flags), but instead
	// overwrite the existing struct's values.
	*Context = *server.NewContext()

	osExit = func(int) {}
	osStderr = os.Stdout

	s := &server.TestServer{}
	if err := s.Start(); err != nil {
		log.Fatalf("Could not start server: %v", err)
	}

	return cliTest{TestServer: s}
}

func (c cliTest) Run(line string) {
	a := strings.Fields(line)

	var args []string
	args = append(args, a[0])
	args = append(args, fmt.Sprintf("--addr=%s", c.ServingAddr()))
	// Always load test certs.
	args = append(args, fmt.Sprintf("--certs=%s", security.EmbeddedCertsDir))
	args = append(args, a[1:]...)

	fmt.Fprintf(os.Stderr, "%s\n", args)
	fmt.Println(line)
	if err := Run(args); err != nil {
		fmt.Println(err)
	}
}

func Example_basic() {
	c := newCLITest()

	c.Run("kv put a 1 b 2")
	c.Run("kv scan")
	c.Run("kv del a")
	c.Run("kv get a")
	c.Run("kv get b")
	c.Run("kv inc c 1")
	c.Run("kv inc c 10")
	c.Run("kv inc c 100")
	c.Run("kv scan")
	c.Run("kv inc c b")
	c.Run("quit")

	// Output:
	// kv put a 1 b 2
	// kv scan
	// "a"	"1"
	// "b"	"2"
	// kv del a
	// kv get a
	// "a" not found
	// kv get b
	// "2"
	// kv inc c 1
	// 1
	// kv inc c 10
	// 11
	// kv inc c 100
	// 111
	// kv scan
	// "b"	"2"
	// "c"	"\x00\x00\x00\x00\x00\x00\x00o"
	// kv inc c b
	// invalid increment: b: strconv.ParseInt: parsing "b": invalid syntax
	// quit
	// node drained and shutdown: ok
}

func Example_quoted() {
	c := newCLITest()

	c.Run(`kv put a\x00 日本語`)                                  // UTF-8 input text
	c.Run(`kv put a\x01 \u65e5\u672c\u8a9e`)                   // explicit Unicode code points
	c.Run(`kv put a\x02 \U000065e5\U0000672c\U00008a9e`)       // explicit Unicode code points
	c.Run(`kv put a\x03 \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e`) // explicit UTF-8 bytes
	c.Run(`kv scan`)
	c.Run(`kv get a\x00`)
	c.Run(`kv del a\x00`)
	c.Run(`kv inc 1\x01`)
	c.Run(`kv get 1\x01`)
	c.Run("quit")

	// Output:
	// kv put a\x00 日本語
	// kv put a\x01 \u65e5\u672c\u8a9e
	// kv put a\x02 \U000065e5\U0000672c\U00008a9e
	// kv put a\x03 \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e
	// kv scan
	// "a\x00"	"日本語"
	// "a\x01"	"日本語"
	// "a\x02"	"日本語"
	// "a\x03"	"日本語"
	// kv get a\x00
	// "日本語"
	// kv del a\x00
	// kv inc 1\x01
	// 1
	// kv get 1\x01
	// "\x00\x00\x00\x00\x00\x00\x00\x01"
	// quit
	// node drained and shutdown: ok
}

func Example_insecure() {
	c := cliTest{}
	c.TestServer = &server.TestServer{}
	c.Ctx = server.NewTestContext()
	c.Ctx.Insecure = true
	if err := c.Start(); err != nil {
		log.Fatalf("Could not start server: %v", err)
	}

	c.Run("kv --insecure put a 1 b 2")
	c.Run("kv --insecure scan")
	c.Run("quit --insecure")

	// Output:
	// kv --insecure put a 1 b 2
	// kv --insecure scan
	// "a"	"1"
	// "b"	"2"
	// quit --insecure
	// node drained and shutdown: ok
}

func Example_ranges() {
	c := newCLITest()

	c.Run("kv put a 1 b 2 c 3 d 4")
	c.Run("kv scan")
	c.Run("range split c")
	c.Run("range ls")
	c.Run("kv scan")
	c.Run("range merge b")
	c.Run("range ls")
	c.Run("kv scan")
	c.Run("quit")

	// Output:
	// kv put a 1 b 2 c 3 d 4
	// kv scan
	// "a"	"1"
	// "b"	"2"
	// "c"	"3"
	// "d"	"4"
	// range split c
	// range ls
	// ""-"c" [1]
	// 	0: node-id=1 store-id=1
	// "c"-"\xff\xff" [2]
	// 	0: node-id=1 store-id=1
	// kv scan
	// "a"	"1"
	// "b"	"2"
	// "c"	"3"
	// "d"	"4"
	// range merge b
	// range ls
	// ""-"\xff\xff" [1]
	// 	0: node-id=1 store-id=1
	// kv scan
	// "a"	"1"
	// "b"	"2"
	// "c"	"3"
	// "d"	"4"
	// quit
	// node drained and shutdown: ok
}

func Example_logging() {
	c := newCLITest()

	c.Run("kv --alsologtostderr=false scan")
	c.Run("kv --log-backtrace-at=foo.go:1 scan")
	c.Run("kv --log-dir='' scan")
	c.Run("kv --logtostderr=true scan")
	c.Run("kv --verbosity=0 scan")
	c.Run("kv --vmodule=foo=1 scan")

	// Output:
	// kv --alsologtostderr=false scan
	// kv --log-backtrace-at=foo.go:1 scan
	// kv --log-dir='' scan
	// kv --logtostderr=true scan
	// kv --verbosity=0 scan
	// kv --vmodule=foo=1 scan
}
<|endoftext|>


<commit_before>package fosite

import (
	"net/url"
	"time"

	"github.com/pborman/uuid"
)

// Request is an implementation of Requester
type Request struct {
	ID            string     `json:"id" gorethink:"id"`
	RequestedAt   time.Time  `json:"requestedAt" gorethink:"requestedAt"`
	Client        Client     `json:"client" gorethink:"client"`
	Scopes        Arguments  `json:"scopes" gorethink:"scopes"`
	GrantedScopes Arguments  `json:"grantedScopes" gorethink:"grantedScopes"`
	Form          url.Values `json:"form" gorethink:"form"`
	Session       Session    `json:"session" gorethink:"session"`
}

func NewRequest() *Request {
	return &Request{
		Client:        &DefaultClient{},
		Scopes:        Arguments{},
		GrantedScopes: Arguments{},
		Form:          url.Values{},
		RequestedAt:   time.Now(),
	}
}

func (a *Request) GetID() string {
	if a.ID == "" {
		a.ID = uuid.New()
	}
	return a.ID
}

func (a *Request) GetRequestForm() url.Values {
	return a.Form
}

func (a *Request) GetRequestedAt() time.Time {
	return a.RequestedAt
}

func (a *Request) GetClient() Client {
	return a.Client
}

func (a *Request) GetRequestedScopes() Arguments {
	return a.Scopes
}

func (a *Request) SetRequestedScopes(s Arguments) {
	for _, scope := range s {
		a.AppendRequestedScope(scope)
	}
}

func (a *Request) AppendRequestedScope(scope string) {
	for _, has := range a.Scopes {
		if scope == has {
			return
		}
	}
	a.Scopes = append(a.Scopes, scope)
}

func (a *Request) GetGrantedScopes() Arguments {
	return a.GrantedScopes
}

func (a *Request) GrantScope(scope string) {
	for _, has := range a.GrantedScopes {
		if scope == has {
			return
		}
	}
	a.GrantedScopes = append(a.GrantedScopes, scope)
}

func (a *Request) SetSession(session Session) {
	a.Session = session
}

func (a *Request) GetSession() Session {
	return a.Session
}

func (a *Request) Merge(request Requester) {
	for _, scope := range request.GetRequestedScopes() {
		a.AppendRequestedScope(scope)
	}
	for _, scope := range request.GetGrantedScopes() {
		a.GrantScope(scope)
	}
	a.RequestedAt = request.GetRequestedAt()
	a.Client = request.GetClient()
	a.Session = request.GetSession()

	for k, v := range request.GetRequestForm() {
		a.Form[k] = v
	}
}
<commit_msg>request: fix SetRequestedScopes (#139)<commit_after>package fosite

import (
	"net/url"
	"time"

	"github.com/pborman/uuid"
)

// Request is an implementation of Requester
type Request struct {
	ID            string     `json:"id" gorethink:"id"`
	RequestedAt   time.Time  `json:"requestedAt" gorethink:"requestedAt"`
	Client        Client     `json:"client" gorethink:"client"`
	Scopes        Arguments  `json:"scopes" gorethink:"scopes"`
	GrantedScopes Arguments  `json:"grantedScopes" gorethink:"grantedScopes"`
	Form          url.Values `json:"form" gorethink:"form"`
	Session       Session    `json:"session" gorethink:"session"`
}

func NewRequest() *Request {
	return &Request{
		Client:        &DefaultClient{},
		Scopes:        Arguments{},
		GrantedScopes: Arguments{},
		Form:          url.Values{},
		RequestedAt:   time.Now(),
	}
}

func (a *Request) GetID() string {
	if a.ID == "" {
		a.ID = uuid.New()
	}
	return a.ID
}

func (a *Request) GetRequestForm() url.Values {
	return a.Form
}

func (a *Request) GetRequestedAt() time.Time {
	return a.RequestedAt
}

func (a *Request) GetClient() Client {
	return a.Client
}

func (a *Request) GetRequestedScopes() Arguments {
	return a.Scopes
}

func (a *Request) SetRequestedScopes(s Arguments) {
	a.Scopes = nil
	for _, scope := range s {
		a.AppendRequestedScope(scope)
	}
}

func (a *Request) AppendRequestedScope(scope string) {
	for _, has := range a.Scopes {
		if scope == has {
			return
		}
	}
	a.Scopes = append(a.Scopes, scope)
}

func (a *Request) GetGrantedScopes() Arguments {
	return a.GrantedScopes
}

func (a *Request) GrantScope(scope string) {
	for _, has := range a.GrantedScopes {
		if scope == has {
			return
		}
	}
	a.GrantedScopes = append(a.GrantedScopes, scope)
}

func (a *Request) SetSession(session Session) {
	a.Session = session
}

func (a *Request) GetSession() Session {
	return a.Session
}

func (a *Request) Merge(request Requester) {
	for _, scope := range request.GetRequestedScopes() {
		a.AppendRequestedScope(scope)
	}
	for _, scope := range request.GetGrantedScopes() {
		a.GrantScope(scope)
	}
	a.RequestedAt = request.GetRequestedAt()
	a.Client = request.GetClient()
	a.Session = request.GetSession()

	for k, v := range request.GetRequestForm() {
		a.Form[k] = v
	}
}
<|endoftext|>


<commit_before>// Package fallback provides an enhanced degree of redundancy to HTTP requests
// by introducing a Chain of Responsibility, consisting of a series of fallback
// HTTP requests, to augment an initial HTTP request. Should the initial HTTP
// request fail, the next fallback HTTP request in the chain will execute. Any
// number of fallback HTTP requests can be chained sequentially. Redundancy is
// achieved by executing each fallback HTTP request in a recursive manner until
// one of the requests succeeds, or all requests fail.
package fallback

import (
	"bytes"
	"encoding/json"
	"errors"
	"net/http"
)

// Connecter represents the Handler abstraction in the Chain of Responsibility
// that consists of a series of fallback HTTP requests. It provides a contract
// which defines the prerequisites that implementations must adhere to in order
// to take part in the chain.
type Connecter interface {
	GetName() string

	CreateHTTPRequest(method, path string, body []byte,
		headers map[string]string) (*http.Request, error)

	ExecuteHTTPRequest(method, path string, body []byte,
		headers map[string]string) (int, error)
}

// Connection represents a Concrete Handler implementation in the Chain of
// Responsibility that consists of a series of fallback HTTP requests.
// Consuming clients can utilise this class directly, as per the examples
// provided, or provide custom implementations derived from Connecter.
type Connection struct {
	// The name used to describe the Connection.
	Name string
	// The Host URI segment excluding other segments such as query string.
	Host string
	// A custom struct that represents a deserialised object returned as result
	// of a successful HTTP request.
	Output      interface{}
	CustomError interface{}
	Fallback    Connecter
}

// NewConnection returns a new Connection instance based on the supplied
// metadata pertaining to Connection.
func NewConnection(name, host string, output interface{},
	customError interface{}, fallback Connecter) *Connection {

	return &Connection{name, host, output, customError, fallback}
}

// GetName returns the Connection name.
func (connection Connection) GetName() string {

	return connection.Name
}

func (connection Connection) CreateHTTPRequest(method, path string,
	body []byte, headers map[string]string) (*http.Request, error) {

	var request *http.Request
	var err error

	if body == nil {
		request, err = http.NewRequest(method, connection.Host+"/"+path, nil)
	} else {
		request, err = http.NewRequest(method, connection.Host+"/"+path, bytes.NewBuffer(body))
	}

	if err != nil {
		return nil, err
	}

	for key := range headers {
		request.Header.Add(key, headers[key])
	}
	return request, nil
}

func (connection Connection) ExecuteHTTPRequest(method, path string,
	body []byte, headers map[string]string) (int, error) {

	client := &http.Client{}

	request, err := connection.CreateHTTPRequest(method, path, body, headers)
	if err != nil {
		if connection.Fallback != nil {
			statusCode, err :=
				connection.Fallback.ExecuteHTTPRequest(method, path, body, headers)
			if statusCode < 200 || statusCode > 299 {
				return statusCode, err
			}
			return statusCode, nil
		}
		return 400, err // This error will occur if the URI is malformed or otherwise invalid.
	}

	resp, err := client.Do(request)
	if err != nil {
		if connection.Fallback != nil {
			statusCode, err :=
				connection.Fallback.ExecuteHTTPRequest(method, path, body, headers)
			if statusCode < 200 || statusCode > 299 {
				return statusCode, err
			}
			return statusCode, nil
		}
		return 503, err // This error will occur if the URI is unreachable.
	}
	defer resp.Body.Close()

	if resp.StatusCode == 404 {
		if connection.Fallback != nil {
			statusCode, err :=
				connection.Fallback.ExecuteHTTPRequest(method, path, body, headers)
			if statusCode < 200 || statusCode > 299 {
				return statusCode, err
			}
			return statusCode, nil
		}
		return 404, nil
	} else if resp.StatusCode < 200 || resp.StatusCode > 299 {
		if connection.Fallback != nil {
			statusCode, err :=
				connection.Fallback.ExecuteHTTPRequest(method, path, body, headers)
			if statusCode < 200 || statusCode > 299 {
				return statusCode, err
			}
			return statusCode, nil
		}

		dec := json.NewDecoder(resp.Body)

		err := dec.Decode(connection.CustomError)
		if err != nil {
			return resp.StatusCode, errors.New("Unable to parse custom error.")
		}
		return resp.StatusCode, nil
	} else {
		dec := json.NewDecoder(resp.Body)

		err := dec.Decode(connection.Output)
		if err != nil {
			return resp.StatusCode, errors.New("Unable to parse custom error.")
		}
		return resp.StatusCode, nil
	}
}
<commit_msg>Adjusted struct property documentation alignment<commit_after>// Package fallback provides an enhanced degree of redundancy to HTTP requests
// by introducing a Chain of Responsibility, consisting of a series of fallback
// HTTP requests, to augment an initial HTTP request. Should the initial HTTP
// request fail, the next fallback HTTP request in the chain will execute. Any
// number of fallback HTTP requests can be chained sequentially. Redundancy is
// achieved by executing each fallback HTTP request in a recursive manner until
// one of the requests succeeds, or all requests fail.
package fallback

import (
	"bytes"
	"encoding/json"
	"errors"
	"net/http"
)

// Connecter represents the Handler abstraction in the Chain of Responsibility
// that consists of a series of fallback HTTP requests. It provides a contract
// which defines the prerequisites that implementations must adhere to in order
// to take part in the chain.
type Connecter interface {
	GetName() string

	CreateHTTPRequest(method, path string, body []byte,
		headers map[string]string) (*http.Request, error)

	ExecuteHTTPRequest(method, path string, body []byte,
		headers map[string]string) (int, error)
}

// Connection represents a Concrete Handler implementation in the Chain of
// Responsibility that consists of a series of fallback HTTP requests.
// Consuming clients can utilise this class directly, as per the examples
// provided, or provide custom implementations derived from Connecter.
type Connection struct {
	Name   string      // The name used to describe the Connection.
	Host   string      // The Host URI segment excluding other segments such as query string.
	Output interface{} // A custom struct that represents a deserialised object returned as result
	// of a successful HTTP request.
	CustomError interface{}
	Fallback    Connecter
}

// NewConnection returns a new Connection instance based on the supplied
// metadata pertaining to Connection.
func NewConnection(name, host string, output interface{},
	customError interface{}, fallback Connecter) *Connection {

	return &Connection{name, host, output, customError, fallback}
}

// GetName returns the Connection name.
func (connection Connection) GetName() string {

	return connection.Name
}

func (connection Connection) CreateHTTPRequest(method, path string,
	body []byte, headers map[string]string) (*http.Request, error) {

	var request *http.Request
	var err error

	if body == nil {
		request, err = http.NewRequest(method, connection.Host+"/"+path, nil)
	} else {
		request, err = http.NewRequest(method, connection.Host+"/"+path, bytes.NewBuffer(body))
	}

	if err != nil {
		return nil, err
	}

	for key := range headers {
		request.Header.Add(key, headers[key])
	}
	return request, nil
}

func (connection Connection) ExecuteHTTPRequest(method, path string,
	body []byte, headers map[string]string) (int, error) {

	client := &http.Client{}

	request, err := connection.CreateHTTPRequest(method, path, body, headers)
	if err != nil {
		if connection.Fallback != nil {
			statusCode, err :=
				connection.Fallback.ExecuteHTTPRequest(method, path, body, headers)
			if statusCode < 200 || statusCode > 299 {
				return statusCode, err
			}
			return statusCode, nil
		}
		return 400, err // This error will occur if the URI is malformed or otherwise invalid.
	}

	resp, err := client.Do(request)
	if err != nil {
		if connection.Fallback != nil {
			statusCode, err :=
				connection.Fallback.ExecuteHTTPRequest(method, path, body, headers)
			if statusCode < 200 || statusCode > 299 {
				return statusCode, err
			}
			return statusCode, nil
		}
		return 503, err // This error will occur if the URI is unreachable.
	}
	defer resp.Body.Close()

	if resp.StatusCode == 404 {
		if connection.Fallback != nil {
			statusCode, err :=
				connection.Fallback.ExecuteHTTPRequest(method, path, body, headers)
			if statusCode < 200 || statusCode > 299 {
				return statusCode, err
			}
			return statusCode, nil
		}
		return 404, nil
	} else if resp.StatusCode < 200 || resp.StatusCode > 299 {
		if connection.Fallback != nil {
			statusCode, err :=
				connection.Fallback.ExecuteHTTPRequest(method, path, body, headers)
			if statusCode < 200 || statusCode > 299 {
				return statusCode, err
			}
			return statusCode, nil
		}

		dec := json.NewDecoder(resp.Body)

		err := dec.Decode(connection.CustomError)
		if err != nil {
			return resp.StatusCode, errors.New("Unable to parse custom error.")
		}
		return resp.StatusCode, nil
	} else {
		dec := json.NewDecoder(resp.Body)

		err := dec.Decode(connection.Output)
		if err != nil {
			return resp.StatusCode, errors.New("Unable to parse custom error.")
		}
		return resp.StatusCode, nil
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Jacob Taylor jacob@ablox.io
// License: Apache2 - http://www.apache.org/licenses/LICENSE-2.0
package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/goji/httpauth"
	"log"
	"net"
	"net/http"
	"os"
	"strconv"
	"sync"
)

type ReplicatServer struct {
	Cluster       string
	Name          string
	Address       string
	CurrentState  DirTreeMap
	PreviousState DirTreeMap
	Lock          sync.Mutex
	storage       StorageTracker
}

var serverMap = make(map[string]ReplicatServer)
var serverMapLock sync.RWMutex

func bootstrapAndServe() {
	http.Handle("/event/", httpauth.SimpleBasicAuth("replicat", "isthecat")(http.HandlerFunc(eventHandler)))
	http.Handle("/tree/", httpauth.SimpleBasicAuth("replicat", "isthecat")(http.HandlerFunc(folderTreeHandler)))
	http.Handle("/config/", httpauth.SimpleBasicAuth("replicat", "isthecat")(http.HandlerFunc(configHandler)))

	lsnr, err := net.Listen("tcp4", ":0")
	if err != nil {
		fmt.Println("Error listening:", err)
		os.Exit(1)
	}
	fmt.Println("Listening on:", lsnr.Addr().String())

	logOnlyHandler := LogOnlyChangeHandler{}
	tracker := FilesystemTracker{}
	tracker.init(globalSettings.Directory)
	var c ChangeHandler
	c = &logOnlyHandler
	tracker.watchDirectory(&c)

	serverMap[globalSettings.Name] = ReplicatServer{Name: globalSettings.Name, Address: "127.0.0.1:" + strconv.Itoa(lsnr.Addr().(*net.TCPAddr).Port), storage: &tracker}

	go func(lsnr net.Listener) {
		err = http.Serve(lsnr, nil)
		if err != nil {
			panic(err)
		}
	}(lsnr)

	fmt.Println("about to send config to server")
	go sendConfigToServer(lsnr.Addr())
	fmt.Printf("config sent to server with address: %s\n", lsnr.Addr())
}

func sendConfigToServer(addr net.Addr) {
	url := "http://" + globalSettings.BootstrapAddress + "/config/"
	fmt.Printf("Manager location: %s\n", url)

	jsonStr, _ := json.Marshal(serverMap[globalSettings.Name])
	fmt.Printf("jsonStr: %s\n", jsonStr)

	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
	req.Header.Set("Content-Type", "application/json")

	data := []byte(globalSettings.ManagerCredentials)
	authHash := base64.StdEncoding.EncodeToString(data)
	req.Header.Add("Authorization", "Basic "+authHash)

	client := &http.Client{}
	_, err = client.Do(req)
	if err != nil {
		panic(err)
	}
}

func configHandler(_ http.ResponseWriter, r *http.Request) {
	log.Println("configHandler called on bootstrap")
	switch r.Method {
	case "POST":
		serverMapLock.Lock()
		defer serverMapLock.Unlock()

		//todo move this to a channel to ensure ordering. It should always be safe to grab the latest one only.
		decoder := json.NewDecoder(r.Body)
		var newServerMap map[string]ReplicatServer //:= make(map[string]ReplicatServer)
		err := decoder.Decode(&newServerMap)
		if err != nil {
			fmt.Println(err)
		}
		log.Printf("configHandler serverMap read from webcat to: %v\n", newServerMap)

		// find any nodes that have been deleted
		for name, serverData := range serverMap {
			newServerData, exists := newServerMap[name]
			if !exists {
				fmt.Printf("No longer found config for: %s deleting\n", name)
				delete(serverMap, name)
				continue
			}

			if serverData.Address != newServerData.Address || serverData.Name != newServerData.Name || serverData.Cluster != newServerData.Cluster {
				fmt.Printf("Server data is radically changed. Replacing.\nold: %v\nnew: %v\n", serverData, newServerData)
				serverMap[name] = newServerData
				fmt.Println("Server data replaced with new server data")
			} else {
				fmt.Printf("Server data has not radically changed. ignoring.\nold: %v\nnew: %v\n", serverData, newServerData)
			}
		}

		// find any new nodes
		for name, newServerData := range newServerMap {
			_, exists := serverMap[name]
			if !exists {
				fmt.Printf("New server configuration for %s: %v\n", name, newServerData)

				// If this server map is for ourselves, build a list of folder if needed and notify others
				if name == globalSettings.Name {
					listOfFileInfo, err := scanDirectoryContents()
					if err != nil {
						log.Fatal(err)
					}
					newServerData.CurrentState = listOfFileInfo
					// Tell all of our friends that we exist and our current state for them to compare against.
					go func(tree DirTreeMap) {
						sendFolderTree(tree)
					}(listOfFileInfo)
				}

				fmt.Printf("New server configuration provided. Copying: %s\n", name)
				serverMap[name] = newServerData
			}
		}
	}
}
<commit_msg>adding comment<commit_after>// Copyright 2016 Jacob Taylor jacob@ablox.io
// License: Apache2 - http://www.apache.org/licenses/LICENSE-2.0
package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/goji/httpauth"
	"log"
	"net"
	"net/http"
	"os"
	"strconv"
	"sync"
)

// ReplicatServer is a structure that contains the definition of the servers in a cluster. Each node has a name and this
// node (as determined by globalSettings.name at the moment) also has a StorageTracker interface.
type ReplicatServer struct {
	Cluster       string
	Name          string
	Address       string
	CurrentState  DirTreeMap
	PreviousState DirTreeMap
	Lock          sync.Mutex
	storage       StorageTracker
}

var serverMap = make(map[string]ReplicatServer)
var serverMapLock sync.RWMutex

func bootstrapAndServe() {
	http.Handle("/event/", httpauth.SimpleBasicAuth("replicat", "isthecat")(http.HandlerFunc(eventHandler)))
	http.Handle("/tree/", httpauth.SimpleBasicAuth("replicat", "isthecat")(http.HandlerFunc(folderTreeHandler)))
	http.Handle("/config/", httpauth.SimpleBasicAuth("replicat", "isthecat")(http.HandlerFunc(configHandler)))

	lsnr, err := net.Listen("tcp4", ":0")
	if err != nil {
		fmt.Println("Error listening:", err)
		os.Exit(1)
	}
	fmt.Println("Listening on:", lsnr.Addr().String())

	logOnlyHandler := LogOnlyChangeHandler{}
	tracker := FilesystemTracker{}
	tracker.init(globalSettings.Directory)
	var c ChangeHandler
	c = &logOnlyHandler
	tracker.watchDirectory(&c)

	serverMap[globalSettings.Name] = ReplicatServer{Name: globalSettings.Name, Address: "127.0.0.1:" + strconv.Itoa(lsnr.Addr().(*net.TCPAddr).Port), storage: &tracker}

	go func(lsnr net.Listener) {
		err = http.Serve(lsnr, nil)
		if err != nil {
			panic(err)
		}
	}(lsnr)

	fmt.Println("about to send config to server")
	go sendConfigToServer(lsnr.Addr())
	fmt.Printf("config sent to server with address: %s\n", lsnr.Addr())
}

func sendConfigToServer(addr net.Addr) {
	url := "http://" + globalSettings.BootstrapAddress + "/config/"
	fmt.Printf("Manager location: %s\n", url)

	jsonStr, _ := json.Marshal(serverMap[globalSettings.Name])
	fmt.Printf("jsonStr: %s\n", jsonStr)

	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
	req.Header.Set("Content-Type", "application/json")

	data := []byte(globalSettings.ManagerCredentials)
	authHash := base64.StdEncoding.EncodeToString(data)
	req.Header.Add("Authorization", "Basic "+authHash)

	client := &http.Client{}
	_, err = client.Do(req)
	if err != nil {
		panic(err)
	}
}

func configHandler(_ http.ResponseWriter, r *http.Request) {
	log.Println("configHandler called on bootstrap")
	switch r.Method {
	case "POST":
		serverMapLock.Lock()
		defer serverMapLock.Unlock()

		//todo move this to a channel to ensure ordering. It should always be safe to grab the latest one only.
		decoder := json.NewDecoder(r.Body)
		var newServerMap map[string]ReplicatServer //:= make(map[string]ReplicatServer)
		err := decoder.Decode(&newServerMap)
		if err != nil {
			fmt.Println(err)
		}
		log.Printf("configHandler serverMap read from webcat to: %v\n", newServerMap)

		// find any nodes that have been deleted
		for name, serverData := range serverMap {
			newServerData, exists := newServerMap[name]
			if !exists {
				fmt.Printf("No longer found config for: %s deleting\n", name)
				delete(serverMap, name)
				continue
			}

			if serverData.Address != newServerData.Address || serverData.Name != newServerData.Name || serverData.Cluster != newServerData.Cluster {
				fmt.Printf("Server data is radically changed. Replacing.\nold: %v\nnew: %v\n", serverData, newServerData)
				serverMap[name] = newServerData
				fmt.Println("Server data replaced with new server data")
			} else {
				fmt.Printf("Server data has not radically changed. ignoring.\nold: %v\nnew: %v\n", serverData, newServerData)
			}
		}

		// find any new nodes
		for name, newServerData := range newServerMap {
			_, exists := serverMap[name]
			if !exists {
				fmt.Printf("New server configuration for %s: %v\n", name, newServerData)

				// If this server map is for ourselves, build a list of folder if needed and notify others
				if name == globalSettings.Name {
					listOfFileInfo, err := scanDirectoryContents()
					if err != nil {
						log.Fatal(err)
					}
					newServerData.CurrentState = listOfFileInfo
					// Tell all of our friends that we exist and our current state for them to compare against.
					go func(tree DirTreeMap) {
						sendFolderTree(tree)
					}(listOfFileInfo)
				}

				fmt.Printf("New server configuration provided. Copying: %s\n", name)
				serverMap[name] = newServerData
			}
		}
	}
}
<|endoftext|>


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

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"text/tabwriter"

	"github.com/coreos/rocket/pkg/keystore"
)

const (
	cliName        = "rkt"
	cliDescription = "rocket, the application container runner"

	defaultDataDir = "/var/lib/rkt"
)

var (
	globalFlagset = flag.NewFlagSet(cliName, flag.ExitOnError)
	tabOut        *tabwriter.Writer
	commands      []*Command // Commands should register themselves by appending
	globalFlags   = struct {
		Dir                string
		Debug              bool
		Help               bool
		InsecureSkipVerify bool
	}{}
)

func init() {
	globalFlagset.BoolVar(&globalFlags.Help, "help", false, "Print usage information and exit")
	globalFlagset.BoolVar(&globalFlags.Debug, "debug", false, "Print out more debug information to stderr")
	globalFlagset.StringVar(&globalFlags.Dir, "dir", defaultDataDir, "rocket data directory")
	globalFlagset.BoolVar(&globalFlags.InsecureSkipVerify, "insecure-skip-verify", false, "skip image or key verification")
}

type Command struct {
	Name        string       // Name of the Command and the string to use to invoke it
	Summary     string       // One-sentence summary of what the Command does
	Usage       string       // Usage options/arguments
	Description string       // Detailed description of command
	Flags       flag.FlagSet // Set of flags associated with this command

	Run func(args []string) int // Run a command with the given arguments, return exit status

}

func init() {
	tabOut = new(tabwriter.Writer)
	tabOut.Init(os.Stdout, 0, 8, 1, '\t', 0)
}

func main() {
	// parse global arguments
	globalFlagset.Parse(os.Args[1:])
	args := globalFlagset.Args()
	if len(args) < 1 || globalFlags.Help {
		args = []string{"help"}
	}

	var cmd *Command

	// determine which Command should be run
	for _, c := range commands {
		if c.Name == args[0] {
			cmd = c
			if err := c.Flags.Parse(args[1:]); err != nil {
				stderr("%v", err)
				os.Exit(2)
			}
			break
		}
	}

	if cmd == nil {
		stderr("%v: unknown subcommand: %q", cliName, args[0])
		stderr("Run '%v help' for usage.", cliName)
		os.Exit(2)
	}

	if globalFlags.Debug {
		log.SetOutput(os.Stderr)
	}

	os.Exit(cmd.Run(cmd.Flags.Args()))
}

func stderr(format string, a ...interface{}) {
	out := fmt.Sprintf(format, a...)
	fmt.Fprintln(os.Stderr, strings.TrimSuffix(out, "\n"))
}

func stdout(format string, a ...interface{}) {
	out := fmt.Sprintf(format, a...)
	fmt.Fprintln(os.Stderr, strings.TrimSuffix(out, "\n"))
}

func getAllFlags() (flags []*flag.Flag) {
	return getFlags(globalFlagset)
}

func getFlags(flagset *flag.FlagSet) (flags []*flag.Flag) {
	flags = make([]*flag.Flag, 0)
	flagset.VisitAll(func(f *flag.Flag) {
		flags = append(flags, f)
	})
	return
}

func containersDir() string {
	return filepath.Join(globalFlags.Dir, "containers")
}

func garbageDir() string {
	return filepath.Join(globalFlags.Dir, "garbage")
}

func getKeystore() *keystore.Keystore {
	if globalFlags.InsecureSkipVerify {
		return nil
	}
	return keystore.New(nil)
}
<commit_msg>rkt/rkt.go<commit_after>// Copyright 2014 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"text/tabwriter"

	"github.com/coreos/rocket/pkg/keystore"
)

const (
	cliName        = "rkt"
	cliDescription = "rocket, the application container runner"

	defaultDataDir = "/var/lib/rkt"
)

var (
	globalFlagset = flag.NewFlagSet(cliName, flag.ExitOnError)
	tabOut        *tabwriter.Writer
	commands      []*Command // Commands should register themselves by appending
	globalFlags   = struct {
		Dir                string
		Debug              bool
		Help               bool
		InsecureSkipVerify bool
	}{}
)

func init() {
	globalFlagset.BoolVar(&globalFlags.Help, "help", false, "Print usage information and exit")
	globalFlagset.BoolVar(&globalFlags.Debug, "debug", false, "Print out more debug information to stderr")
	globalFlagset.StringVar(&globalFlags.Dir, "dir", defaultDataDir, "rocket data directory")
	globalFlagset.BoolVar(&globalFlags.InsecureSkipVerify, "insecure-skip-verify", false, "skip image or key verification")
}

type Command struct {
	Name        string       // Name of the Command and the string to use to invoke it
	Summary     string       // One-sentence summary of what the Command does
	Usage       string       // Usage options/arguments
	Description string       // Detailed description of command
	Flags       flag.FlagSet // Set of flags associated with this command

	Run func(args []string) int // Run a command with the given arguments, return exit status

}

func init() {
	tabOut = new(tabwriter.Writer)
	tabOut.Init(os.Stdout, 0, 8, 1, '\t', 0)
}

func main() {
	// parse global arguments
	globalFlagset.Parse(os.Args[1:])
	args := globalFlagset.Args()
	if len(args) < 1 || globalFlags.Help {
		args = []string{"help"}
	}

	var cmd *Command

	// determine which Command should be run
	for _, c := range commands {
		if c.Name == args[0] {
			cmd = c
			if err := c.Flags.Parse(args[1:]); err != nil {
				stderr("%v", err)
				os.Exit(2)
			}
			break
		}
	}

	if cmd == nil {
		stderr("%v: unknown subcommand: %q", cliName, args[0])
		stderr("Run '%v help' for usage.", cliName)
		os.Exit(2)
	}

	if globalFlags.Debug {
		log.SetOutput(os.Stderr)
	}

	os.Exit(cmd.Run(cmd.Flags.Args()))
}

func stderr(format string, a ...interface{}) {
	out := fmt.Sprintf(format, a...)
	fmt.Fprintln(os.Stderr, strings.TrimSuffix(out, "\n"))
}

func stdout(format string, a ...interface{}) {
	out := fmt.Sprintf(format, a...)
	fmt.Fprintln(os.Stdout, strings.TrimSuffix(out, "\n"))
}

func getAllFlags() (flags []*flag.Flag) {
	return getFlags(globalFlagset)
}

func getFlags(flagset *flag.FlagSet) (flags []*flag.Flag) {
	flags = make([]*flag.Flag, 0)
	flagset.VisitAll(func(f *flag.Flag) {
		flags = append(flags, f)
	})
	return
}

func containersDir() string {
	return filepath.Join(globalFlags.Dir, "containers")
}

func garbageDir() string {
	return filepath.Join(globalFlags.Dir, "garbage")
}

func getKeystore() *keystore.Keystore {
	if globalFlags.InsecureSkipVerify {
		return nil
	}
	return keystore.New(nil)
}
<|endoftext|>


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

package main

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/fatih/color"
	"github.com/minio/cli"
	"github.com/minio/mc/pkg/console"
	"github.com/minio/minio-xl/pkg/probe"
)

// rm specific flags.
var (
	rmFlags = []cli.Flag{
		cli.BoolFlag{
			Name:  "help, h",
			Usage: "Help of rm.",
		},
		cli.BoolFlag{
			Name:  "recursive, r",
			Usage: "Remove recursively.",
		},
		cli.BoolFlag{
			Name:  "force",
			Usage: "Force a dangerous remove operation.",
		},
		cli.BoolFlag{
			Name:  "incomplete, I",
			Usage: "Remove an incomplete upload(s).",
		},
		cli.BoolFlag{
			Name:  "fake",
			Usage: "Perform a fake remove operation.",
		},
	}
)

// remove a file or folder.
var rmCmd = cli.Command{
	Name:   "rm",
	Usage:  "Remove file or bucket [WARNING: Use with care].",
	Action: mainRm,
	Flags:  append(rmFlags, globalFlags...),
	CustomHelpTemplate: `NAME:
   mc {{.Name}} - {{.Usage}}

USAGE:
   mc {{.Name}} [FLAGS] TARGET [TARGET ...]

FLAGS:
  {{range .Flags}}{{.}}
  {{end}}
EXAMPLES:
   1. Remove a file.
      $ mc {{.Name}} 1999/old-backup.tgz

   2. Remove contents of a folder, excluding its sub-folders.
     $ mc {{.Name}} --force s3/jazz-songs/louis/

   3. Remove contents of a folder recursively.
     $ mc {{.Name}} --force --recursive s3/jazz-songs/louis/

   4. Remove all matching objects with this prefix.
     $ mc {{.Name}} --force s3/ogg/gunmetal

   5. Drop an incomplete upload of an object.
      $ mc {{.Name}} --incomplete s3/jazz-songs/louis/file01.mp3

   6. Drop all incomplete uploads recursively matching this prefix.
      $ mc {{.Name}} --incomplete --force --recursive s3/jazz-songs/louis/
`,
}

// Structured message depending on the type of console.
type rmMessage struct {
	Status string `json:"status"`
	URL    string `json:"url"`
}

// Colorized message for console printing.
func (r rmMessage) String() string {
	return console.Colorize("Remove", fmt.Sprintf("Removed ‘%s’.", r.URL))
}

// JSON'ified message for scripting.
func (r rmMessage) JSON() string {
	msgBytes, e := json.Marshal(r)
	fatalIf(probe.NewError(e), "Unable to marshal into JSON.")
	return string(msgBytes)
}

// Validate command line arguments.
func checkRmSyntax(ctx *cli.Context) {
	// Set command flags from context.
	isForce := ctx.Bool("force")
	isRecursive := ctx.Bool("recursive")
	isIncomplete := ctx.Bool("incomplete")

	if !ctx.Args().Present() {
		exitCode := 1
		cli.ShowCommandHelpAndExit(ctx, "rm", exitCode)
	}

	if !isRecursive && !isIncomplete {
		for _, url := range ctx.Args() {
			if _, _, err := url2Stat(url); err != nil {
				fatalIf(err.Trace(url), "Unable to stat.")
			}
		}
	}

	if isRecursive && !isForce {
		fatalIf(errDummy().Trace(),
			"Recursive removal requires --force option. Please review carefully before performing this *DANGEROUS* operation.")
	}
}

// Remove a single object.
func rm(url string, isIncomplete, isFake bool) *probe.Error {
	clnt, err := newClient(url)
	if err != nil {
		return err.Trace(url)
	}

	if isFake { // It is a fake remove. Return success.
		return nil
	}

	if err = clnt.Remove(isIncomplete); err != nil {
		return err.Trace(url)
	}

	return nil
}

// Remove all objects recursively.
func rmAll(url string, isRecursive, isIncomplete, isFake bool) {
	// Initialize new client.
	clnt, err := newClient(url)
	if err != nil {
		errorIf(err.Trace(url), "Invalid URL ‘"+url+"’.")
		return // End of journey.
	}

	/* Disable recursion and only list this folder's contents. We
	perform manual depth-first recursion ourself here. */
	nonRecursive := false
	for entry := range clnt.List(nonRecursive, isIncomplete) {
		if entry.Err != nil {
			errorIf(entry.Err.Trace(url), "Unable to list ‘"+url+"’.")
			return // End of journey.
		}

		if entry.Type.IsDir() && isRecursive {
			// Add separator at the end to remove all its contents.
			url := entry.URL
			url.Path = strings.TrimSuffix(entry.URL.Path, string(entry.URL.Separator)) + string(entry.URL.Separator)

			// Recursively remove contents of this directory.
			rmAll(url.String(), isRecursive, isIncomplete, isFake)
		}

		// Regular type.
		if err = rm(entry.URL.String(), isIncomplete, isFake); err != nil {
			errorIf(err.Trace(entry.URL.String()), "Unable to remove ‘"+entry.URL.String()+"’.")
			continue
		}
		printMsg(rmMessage{Status: "success", URL: entry.URL.String()})
	}
}

// main for rm command.
func mainRm(ctx *cli.Context) {
	// Set global flags from context.
	setGlobalsFromContext(ctx)

	// check 'rm' cli arguments.
	checkRmSyntax(ctx)

	// rm specific flags.
	isForce := ctx.Bool("force")
	isIncomplete := ctx.Bool("incomplete")
	isRecursive := ctx.Bool("recursive")
	isFake := ctx.Bool("fake")

	// Set color.
	console.SetColor("Remove", color.New(color.FgGreen, color.Bold))

	// Support multiple targets.
	for _, url := range ctx.Args() {
		if isRecursive && isForce {
			rmAll(url, isRecursive, isIncomplete, isFake)
		} else {
			if err := rm(url, isIncomplete, isFake); err != nil {
				errorIf(err.Trace(url), "Unable to remove ‘"+url+"’.")
				continue
			}
			printMsg(rmMessage{Status: "success", URL: url})
		}
	}
}
<commit_msg>rm: Fix alias URL handling in rm.<commit_after>/*
 * Minio Client (C) 2014, 2015 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"encoding/json"
	"fmt"
	"path/filepath"
	"strings"

	"github.com/fatih/color"
	"github.com/minio/cli"
	"github.com/minio/mc/pkg/console"
	"github.com/minio/minio-xl/pkg/probe"
)

// rm specific flags.
var (
	rmFlags = []cli.Flag{
		cli.BoolFlag{
			Name:  "help, h",
			Usage: "Help of rm.",
		},
		cli.BoolFlag{
			Name:  "recursive, r",
			Usage: "Remove recursively.",
		},
		cli.BoolFlag{
			Name:  "force",
			Usage: "Force a dangerous remove operation.",
		},
		cli.BoolFlag{
			Name:  "incomplete, I",
			Usage: "Remove an incomplete upload(s).",
		},
		cli.BoolFlag{
			Name:  "fake",
			Usage: "Perform a fake remove operation.",
		},
	}
)

// remove a file or folder.
var rmCmd = cli.Command{
	Name:   "rm",
	Usage:  "Remove file or bucket [WARNING: Use with care].",
	Action: mainRm,
	Flags:  append(rmFlags, globalFlags...),
	CustomHelpTemplate: `NAME:
   mc {{.Name}} - {{.Usage}}

USAGE:
   mc {{.Name}} [FLAGS] TARGET [TARGET ...]

FLAGS:
  {{range .Flags}}{{.}}
  {{end}}
EXAMPLES:
   1. Remove a file.
      $ mc {{.Name}} 1999/old-backup.tgz

   2. Remove contents of a folder, excluding its sub-folders.
     $ mc {{.Name}} --force s3/jazz-songs/louis/

   3. Remove contents of a folder recursively.
     $ mc {{.Name}} --force --recursive s3/jazz-songs/louis/

   4. Remove all matching objects with this prefix.
     $ mc {{.Name}} --force s3/ogg/gunmetal

   5. Drop an incomplete upload of an object.
      $ mc {{.Name}} --incomplete s3/jazz-songs/louis/file01.mp3

   6. Drop all incomplete uploads recursively matching this prefix.
      $ mc {{.Name}} --incomplete --force --recursive s3/jazz-songs/louis/
`,
}

// Structured message depending on the type of console.
type rmMessage struct {
	Status string `json:"status"`
	URL    string `json:"url"`
}

// Colorized message for console printing.
func (r rmMessage) String() string {
	return console.Colorize("Remove", fmt.Sprintf("Removed ‘%s’.", r.URL))
}

// JSON'ified message for scripting.
func (r rmMessage) JSON() string {
	msgBytes, e := json.Marshal(r)
	fatalIf(probe.NewError(e), "Unable to marshal into JSON.")
	return string(msgBytes)
}

// Validate command line arguments.
func checkRmSyntax(ctx *cli.Context) {
	// Set command flags from context.
	isForce := ctx.Bool("force")
	isRecursive := ctx.Bool("recursive")
	isIncomplete := ctx.Bool("incomplete")

	if !ctx.Args().Present() {
		exitCode := 1
		cli.ShowCommandHelpAndExit(ctx, "rm", exitCode)
	}

	if !isRecursive && !isIncomplete {
		for _, url := range ctx.Args() {
			if _, _, err := url2Stat(url); err != nil {
				fatalIf(err.Trace(url), "Unable to stat.")
			}
		}
	}

	if isRecursive && !isForce {
		fatalIf(errDummy().Trace(),
			"Recursive removal requires --force option. Please review carefully before performing this *DANGEROUS* operation.")
	}
}

// Remove a single object.
func rm(targetAlias, targetURL string, isIncomplete, isFake bool) *probe.Error {
	clnt, err := newClientFromAlias(targetAlias, targetURL)
	if err != nil {
		return err.Trace(targetURL)
	}

	if isFake { // It is a fake remove. Return success.
		return nil
	}

	if err = clnt.Remove(isIncomplete); err != nil {
		return err.Trace(targetURL)
	}

	return nil
}

// Remove all objects recursively.
func rmAll(targetAlias, targetURL string, isRecursive, isIncomplete, isFake bool) {
	// Initialize new client.
	clnt, err := newClientFromAlias(targetAlias, targetURL)
	if err != nil {
		errorIf(err.Trace(targetURL), "Invalid URL ‘"+targetURL+"’.")
		return // End of journey.
	}

	/* Disable recursion and only list this folder's contents. We
	perform manual depth-first recursion ourself here. */
	nonRecursive := false
	for entry := range clnt.List(nonRecursive, isIncomplete) {
		if entry.Err != nil {
			errorIf(entry.Err.Trace(targetURL), "Unable to list ‘"+targetURL+"’.")
			return // End of journey.
		}

		if entry.Type.IsDir() && isRecursive {
			// Add separator at the end to remove all its contents.
			url := entry.URL
			url.Path = strings.TrimSuffix(entry.URL.Path, string(entry.URL.Separator)) + string(entry.URL.Separator)

			// Recursively remove contents of this directory.
			rmAll(targetAlias, url.String(), isRecursive, isIncomplete, isFake)
		}

		// Regular type.
		if err = rm(targetAlias, entry.URL.String(), isIncomplete, isFake); err != nil {
			errorIf(err.Trace(entry.URL.String()), "Unable to remove ‘"+entry.URL.String()+"’.")
			continue
		}
		// Construct user facing message and path.
		entryPath := filepath.Join(targetAlias, entry.URL.Path)
		printMsg(rmMessage{Status: "success", URL: entryPath})
	}
}

// main for rm command.
func mainRm(ctx *cli.Context) {
	// Set global flags from context.
	setGlobalsFromContext(ctx)

	// check 'rm' cli arguments.
	checkRmSyntax(ctx)

	// rm specific flags.
	isForce := ctx.Bool("force")
	isIncomplete := ctx.Bool("incomplete")
	isRecursive := ctx.Bool("recursive")
	isFake := ctx.Bool("fake")

	// Set color.
	console.SetColor("Remove", color.New(color.FgGreen, color.Bold))

	// Support multiple targets.
	for _, url := range ctx.Args() {
		targetAlias, targetURL, _ := mustExpandAlias(url)
		if isRecursive && isForce {
			rmAll(targetAlias, targetURL, isRecursive, isIncomplete, isFake)
		} else {
			if err := rm(targetAlias, targetURL, isIncomplete, isFake); err != nil {
				errorIf(err.Trace(url), "Unable to remove ‘"+url+"’.")
				continue
			}
			printMsg(rmMessage{Status: "success", URL: url})
		}
	}
}
<|endoftext|>