<commit_before>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package backend

import (
	"code.google.com/p/log4go"
	"strings"
)

type (
	// An event callback dealing with View events.
	ViewEventCallback func(v *View)
	// A ViewEvent is simply a bunch of ViewEventCallbacks.
	ViewEvent []ViewEventCallback

	// The return value returned from a QueryContextCallback.
	QueryContextReturn int

	// The context is queried when trying to figure out what action should be performed when
	// certain conditions are met.
	//
	// Context is just a string identifier, an optional comparison operator, an optional operand, and an optional
	// match_all boolean. The data of the context is optionally provided together with a key binding and the key's
	// action will only be considered if the context conditions are met.
	//
	// Exactly how these values are interpreted is up to the individual context handlers, which may be fully
	// customized by implementing the callback in a plugin.
	//
	// For instance pressing the key 'j' will have a different meaning when in a VI command mode emulation
	// and when in a VI insert mode emulation. A plugin would then define two key binding entries for 'j',
	// describe the key binding context to be able to discern which action is appropriate when 'j' is then pressed.
	QueryContextCallback func(v *View, key string, operator Op, operand interface{}, match_all bool) QueryContextReturn

	// A QueryContextEvent is simply a bunch of QueryContextCallbacks.
	QueryContextEvent []QueryContextCallback

	// A WindowEventCallback deals with Window events.
	WindowEventCallback func(w *Window)
	// A WindowEvent is simply a bunch of WindowEventCallbacks.
	WindowEvent []WindowEventCallback
)

const (
	True    QueryContextReturn = iota //< Returned when the context query matches.
	False                             //< Returned when the context query does not match.
	Unknown                           //< Returned when the QueryContextCallback does not know how to deal with the given context.
)

// Add the provided ViewEventCallback to this ViewEvent
// TODO(.): Support removing ViewEventCallbacks?
func (ve *ViewEvent) Add(cb ViewEventCallback) {
	*ve = append(*ve, cb)
}

// Trigger this ViewEvent by calling all the registered callbacks in order of registration.
func (ve ViewEvent) Call(v *View) {
	log4go.Finest("ViewEvent")
	for i := range ve {
		ve[i](v)
	}
}

// Add the provided QueryContextCallback to the QueryContextEvent.
// TODO(.): Support removing QueryContextCallbacks?
func (qe *QueryContextEvent) Add(cb QueryContextCallback) {
	*qe = append(*qe, cb)
}

// Searches for a QueryContextCallback and returns the result of the first callback being able to deal with this
// context, or Unknown if no such callback was found.
func (qe QueryContextEvent) Call(v *View, key string, operator Op, operand interface{}, match_all bool) QueryContextReturn {
	log4go.Fine("Query context: %s, %v, %v, %v", key, operator, operand, match_all)
	for i := range qe {
		r := qe[i](v, key, operator, operand, match_all)
		if r != Unknown {
			return r
		}
	}
	log4go.Fine("Unknown context: %s", key)
	return Unknown
}

// Add the provided WindowEventCallback to this WindowEvent.
// TODO(.): Support removing WindowEventCallbacks?
func (we *WindowEvent) Add(cb WindowEventCallback) {
	*we = append(*we, cb)
}

// Trigger this WindowEvent by callig all the registered callbacks in order of registration.
func (we WindowEvent) Call(w *Window) {
	log4go.Finest("WindowEvent")
	for i := range we {
		we[i](w)
	}
}

var (
	OnNew               ViewEvent //< Called when a new view is created
	OnLoad              ViewEvent //< Called when loading a view's buffer has finished
	OnActivated         ViewEvent //< Called when a view gains input focus.
	OnDeactivated       ViewEvent //< Called when a view loses input focus.
	OnClose             ViewEvent //< Called when a view has been closed.
	OnPreSave           ViewEvent //< Called just before a view's buffer is saved.
	OnPostSave          ViewEvent //< Called after a view's buffer has been saved.
	OnModified          ViewEvent //< Called when the contents of a view's underlying buffer has changed.
	OnSelectionModified ViewEvent //< Called when a view's Selection/cursor has changed.

	OnNewWindow    WindowEvent       //< Called when a new window has been created.
	OnQueryContext QueryContextEvent //< Called when context is being queried.
)

func init() {
	// Register functionality dealing with a couple of built in contexts
	OnQueryContext.Add(func(v *View, key string, operator Op, operand interface{}, match_all bool) QueryContextReturn {
		if strings.HasPrefix(key, "setting.") && operator == OpEqual {
			c, ok := v.Settings().Get(key[8:]).(bool)
			if c && ok {
				return True
			}
			return False
		} else if key == "num_selections" {
			opf, _ := operand.(float64)
			op := int(opf)

			switch operator {
			case OpEqual:
				if op == v.Sel().Len() {
					return True
				}
				return False
			case OpNotEqual:
				if op != v.Sel().Len() {
					return True
				}
				return False
			}
		}
		return Unknown
	})
}
<commit_msg>Fix typo in events Call documentation<commit_after>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package backend

import (
	"code.google.com/p/log4go"
	"strings"
)

type (
	// An event callback dealing with View events.
	ViewEventCallback func(v *View)
	// A ViewEvent is simply a bunch of ViewEventCallbacks.
	ViewEvent []ViewEventCallback

	// The return value returned from a QueryContextCallback.
	QueryContextReturn int

	// The context is queried when trying to figure out what action should be performed when
	// certain conditions are met.
	//
	// Context is just a string identifier, an optional comparison operator, an optional operand, and an optional
	// match_all boolean. The data of the context is optionally provided together with a key binding and the key's
	// action will only be considered if the context conditions are met.
	//
	// Exactly how these values are interpreted is up to the individual context handlers, which may be fully
	// customized by implementing the callback in a plugin.
	//
	// For instance pressing the key 'j' will have a different meaning when in a VI command mode emulation
	// and when in a VI insert mode emulation. A plugin would then define two key binding entries for 'j',
	// describe the key binding context to be able to discern which action is appropriate when 'j' is then pressed.
	QueryContextCallback func(v *View, key string, operator Op, operand interface{}, match_all bool) QueryContextReturn

	// A QueryContextEvent is simply a bunch of QueryContextCallbacks.
	QueryContextEvent []QueryContextCallback

	// A WindowEventCallback deals with Window events.
	WindowEventCallback func(w *Window)
	// A WindowEvent is simply a bunch of WindowEventCallbacks.
	WindowEvent []WindowEventCallback
)

const (
	True    QueryContextReturn = iota //< Returned when the context query matches.
	False                             //< Returned when the context query does not match.
	Unknown                           //< Returned when the QueryContextCallback does not know how to deal with the given context.
)

// Add the provided ViewEventCallback to this ViewEvent
// TODO(.): Support removing ViewEventCallbacks?
func (ve *ViewEvent) Add(cb ViewEventCallback) {
	*ve = append(*ve, cb)
}

// Trigger this ViewEvent by calling all the registered callbacks in order of registration.
func (ve ViewEvent) Call(v *View) {
	log4go.Finest("ViewEvent")
	for i := range ve {
		ve[i](v)
	}
}

// Add the provided QueryContextCallback to the QueryContextEvent.
// TODO(.): Support removing QueryContextCallbacks?
func (qe *QueryContextEvent) Add(cb QueryContextCallback) {
	*qe = append(*qe, cb)
}

// Searches for a QueryContextCallback and returns the result of the first callback being able to deal with this
// context, or Unknown if no such callback was found.
func (qe QueryContextEvent) Call(v *View, key string, operator Op, operand interface{}, match_all bool) QueryContextReturn {
	log4go.Fine("Query context: %s, %v, %v, %v", key, operator, operand, match_all)
	for i := range qe {
		r := qe[i](v, key, operator, operand, match_all)
		if r != Unknown {
			return r
		}
	}
	log4go.Fine("Unknown context: %s", key)
	return Unknown
}

// Add the provided WindowEventCallback to this WindowEvent.
// TODO(.): Support removing WindowEventCallbacks?
func (we *WindowEvent) Add(cb WindowEventCallback) {
	*we = append(*we, cb)
}

// Trigger this WindowEvent by calling all the registered callbacks in order of registration.
func (we WindowEvent) Call(w *Window) {
	log4go.Finest("WindowEvent")
	for i := range we {
		we[i](w)
	}
}

var (
	OnNew               ViewEvent //< Called when a new view is created
	OnLoad              ViewEvent //< Called when loading a view's buffer has finished
	OnActivated         ViewEvent //< Called when a view gains input focus.
	OnDeactivated       ViewEvent //< Called when a view loses input focus.
	OnClose             ViewEvent //< Called when a view has been closed.
	OnPreSave           ViewEvent //< Called just before a view's buffer is saved.
	OnPostSave          ViewEvent //< Called after a view's buffer has been saved.
	OnModified          ViewEvent //< Called when the contents of a view's underlying buffer has changed.
	OnSelectionModified ViewEvent //< Called when a view's Selection/cursor has changed.

	OnNewWindow    WindowEvent       //< Called when a new window has been created.
	OnQueryContext QueryContextEvent //< Called when context is being queried.
)

func init() {
	// Register functionality dealing with a couple of built in contexts
	OnQueryContext.Add(func(v *View, key string, operator Op, operand interface{}, match_all bool) QueryContextReturn {
		if strings.HasPrefix(key, "setting.") && operator == OpEqual {
			c, ok := v.Settings().Get(key[8:]).(bool)
			if c && ok {
				return True
			}
			return False
		} else if key == "num_selections" {
			opf, _ := operand.(float64)
			op := int(opf)

			switch operator {
			case OpEqual:
				if op == v.Sel().Len() {
					return True
				}
				return False
			case OpNotEqual:
				if op != v.Sel().Len() {
					return True
				}
				return False
			}
		}
		return Unknown
	})
}
<|endoftext|>


<commit_before>package aws

import (
	"time"

	"fmt"

	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/batch"
)

var defaultTimeout = 10 * time.Minute
var defaultPollingTime = 10 * time.Second

// BatchJobTask execute AWS Batch Job.
type BatchJobTask struct {
	Session        *session.Session
	SubmitJobInput *batch.SubmitJobInput
	PollingTime    time.Duration
	Timeout        time.Duration
}

// NewBatchJobTask creates a AWS Batch Job task.
func NewBatchJobTask(session *session.Session, input *batch.SubmitJobInput) *BatchJobTask {
	return &BatchJobTask{
		Session:        session,
		SubmitJobInput: input,
		PollingTime:    defaultPollingTime,
		Timeout:        defaultTimeout,
	}
}

// Execute implement Task.Execute
func (bjt *BatchJobTask) Execute() error {
	completeChan := make(chan error)

	go func() {
		b := batch.New(bjt.Session)
		submit, err := submitJob(b, bjt.SubmitJobInput)
		if err != nil {
			completeChan <- err
		}

		elapsed := 0 * time.Millisecond

		for {
			describe, err := describeJobs(b, &batch.DescribeJobsInput{Jobs: []*string{submit.JobId}})
			if err != nil {
				completeChan <- err
			}

			if len(describe.Jobs) == 0 {
				completeChan <- fmt.Errorf("cloudflow: aws batch job:%v not found", submit.JobId)
				break
			}

			job := describe.Jobs[0]
			switch *job.Status {
			case "SUCCEEDED":
				completeChan <- nil
				break
			case "FAILED":
				completeChan <- fmt.Errorf("cloudflow: aws batch job id:%v failed by reason:%v", job.JobId, job.StatusReason)
				break
			default:
				time.Sleep(bjt.PollingTime)
				elapsed += bjt.PollingTime
			}

			if elapsed >= bjt.Timeout {
				completeChan <- fmt.Errorf("cloudflow: aws batch job id:%v timed out", job.JobId)
				break
			}
		}
	}()

	return <-completeChan
}

// for mock testing
var submitJob = func(b *batch.Batch, input *batch.SubmitJobInput) (*batch.SubmitJobOutput, error) {
	return b.SubmitJob(input)
}
var describeJobs = func(b *batch.Batch, input *batch.DescribeJobsInput) (*batch.DescribeJobsOutput, error) {
	return b.DescribeJobs(input)
}
<commit_msg>Adjsut aws batch job polling & timeout<commit_after>package aws

import (
	"time"

	"fmt"

	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/batch"
)

var defaultTimeout = 30 * time.Minute
var defaultPollingTime = 30 * time.Second

// BatchJobTask execute AWS Batch Job.
type BatchJobTask struct {
	Session        *session.Session
	SubmitJobInput *batch.SubmitJobInput
	PollingTime    time.Duration
	Timeout        time.Duration
}

// NewBatchJobTask creates a AWS Batch Job task.
func NewBatchJobTask(session *session.Session, input *batch.SubmitJobInput) *BatchJobTask {
	return &BatchJobTask{
		Session:        session,
		SubmitJobInput: input,
		PollingTime:    defaultPollingTime,
		Timeout:        defaultTimeout,
	}
}

// Execute implement Task.Execute
func (bjt *BatchJobTask) Execute() error {
	completeChan := make(chan error)

	go func() {
		b := batch.New(bjt.Session)
		submit, err := submitJob(b, bjt.SubmitJobInput)
		if err != nil {
			completeChan <- err
		}

		elapsed := 0 * time.Millisecond

		for {
			describe, err := describeJobs(b, &batch.DescribeJobsInput{Jobs: []*string{submit.JobId}})
			if err != nil {
				completeChan <- err
			}

			if len(describe.Jobs) == 0 {
				completeChan <- fmt.Errorf("cloudflow: aws batch job:%v not found", submit.JobId)
				break
			}

			job := describe.Jobs[0]
			switch *job.Status {
			case "SUCCEEDED":
				completeChan <- nil
				break
			case "FAILED":
				completeChan <- fmt.Errorf("cloudflow: aws batch job id:%v failed by reason:%v", job.JobId, job.StatusReason)
				break
			default:
				time.Sleep(bjt.PollingTime)
				elapsed += bjt.PollingTime
			}

			if elapsed >= bjt.Timeout {
				completeChan <- fmt.Errorf("cloudflow: aws batch job id:%v timed out", job.JobId)
				break
			}
		}
	}()

	return <-completeChan
}

// for mock testing
var submitJob = func(b *batch.Batch, input *batch.SubmitJobInput) (*batch.SubmitJobOutput, error) {
	return b.SubmitJob(input)
}
var describeJobs = func(b *batch.Batch, input *batch.DescribeJobsInput) (*batch.DescribeJobsOutput, error) {
	return b.DescribeJobs(input)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"time"
	"unicode/utf8"
)

// locates this directory's parent `.git` directory and returns it, or an error
// if no parent `.git` directory could be found.
func gitPath() (string, error) {
	// start at the current directory
	cur, err := os.Getwd()
	if err != nil {
		return "", err
	}

	// clean up the path and ensure it's absolute so we can traverse all the way
	// to the root directory if necessary.
	cur, err = filepath.Abs(filepath.Clean(cur))
	if err != nil {
		return "", err
	}

	// walk our way up the directory tree, attempting to find a `.git` directory
	const gitDirectoryName = ".git"
	for cur != "/" {
		// list all this directory's children
		children, err := ioutil.ReadDir(cur)
		if err != nil {
			return "", err
		}

		// look for a `.git` directory in the children
		for _, info := range children {
			name := info.Name()

			// if we find a directory with the appropriate name, return its path
			if name == gitDirectoryName && info.IsDir() {
				return path.Join(cur, name), nil
			}
		}

		// if we failed, move up to the parent path
		cur = filepath.Dir(cur)
	}

	// if we've reached the root and haven't found a `.git` directory, return an
	// error.
	return "", fmt.Errorf("No Git directory found.")
}

// finds the current branch of the current Git repository
func gitCurrentBranch() string {
	gitPath, err := gitPath()
	if err != nil {
		return ""
	}

	// this file contains a pointer to the current branch which we can parse to
	// determine the branch name.
	headPath := path.Join(gitPath, "HEAD")

	// read the HEAD file
	data, err := ioutil.ReadFile(headPath)
	if err != nil {
		return ""
	}

	refSpec := strings.TrimSpace(string(data))

	// parse the HEAD file to get the branch name. the HEAD file contents look
	// something like: `ref: refs/heads/master`. we split into three parts, then
	// use whatever's left over as the branch name. If it doesn't split, it's
	// probably a commit hash, in which case we use the first 8 characters of it
	// as the branch name.
	refSpecParts := strings.SplitN(refSpec, "/", 3)
	branchName := ""
	if len(refSpecParts) == 3 {
		// use the last part as the branch name
		branchName = strings.TrimSpace(refSpecParts[2])
	} else if len(refSpecParts) == 1 && len(refSpec) == 40 {
		// we got a commit hash, use the first 7 characters as the branch name
		branchName = refSpec[0:7]
	} else {
		// notify that we failed
		branchName = "BAD_REF_SPEC (" + refSpec + ")"
	}

	// return the third part of our split ref spec, the branch name
	return branchName
}

// gets the current status symbols for the existing git repository as a map of
// file name to status symbol, or nil if there's no repository.
func gitCurrentStatus() map[string]string {
	out, err := exec.Command("git", "status", "--porcelain").CombinedOutput()
	if err != nil {
		return nil
	}

	// turn the output into a map of file to status string
	files := make(map[string]string)
	for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
		// trim whitespace so we can reliably split out the status/name
		line = strings.TrimSpace(line)

		// split into a (status, file) pair
		parts := strings.SplitN(line, " ", 2)
		if len(parts) == 2 {
			files[parts[1]] = parts[0]
		}
	}

	return files
}

func compressWithTruncator(s string, truncator rune, maxLen int) string {
	lenS := utf8.RuneCountInString(s)

	// if we're already short enough, bail
	if lenS <= maxLen {
		return s
	}

	// otherwise, calculate the reduction we need to fit into the max length
	reductionAmount := lenS - maxLen

	// remove the middle characters and replace them with our truncator
	middle := float64(lenS) / 2
	startIExact := middle - (float64(reductionAmount) / 2.0)
	endIExact := startIExact + float64(reductionAmount)
	startI := int(startIExact)
	endI := int(endIExact)

	// protect against overruns
	if startI < 0 {
		startI = 0
	}

	if endI >= lenS {
		endI = lenS
	}

	// construct a new string out of our old string's runes, replacing the
	// truncated ones with our truncator rune.
	truncatedS := make([]rune, 0, lenS-reductionAmount)
	truncated := false
	for i, ch := range s {
		if i < startI {
			truncatedS = append(truncatedS, ch)
		} else if !truncated {
			// add the truncator character if we haven't done so already
			truncatedS = append(truncatedS, truncator)
			truncated = true
		} else if i > endI {
			truncatedS = append(truncatedS, ch)
		}
	}

	return string(truncatedS)
}

// shortens and prettifies the given path, keeping it at or under the target
// length in runes.
func prettifyPath(p string, targetLength int) (string, error) {
	// clean up the path first
	p, err := filepath.Abs(filepath.Clean(p))
	if err != nil {
		return "", err
	}

	// if this path is in the current HOME directory, replace that dir with `~`
	homePath := os.Getenv("HOME")
	const homeTruncator = "~"
	if homePath != "" && strings.HasPrefix(p, homePath) {
		// mark that we're in the home directory for later
		p = homeTruncator + p[len(homePath):]
	}

	// save an original copy in case we can't do smart truncation well enough
	origP := p

	// determine how much we need to shorten our path to get it under the target,
	// i.e. how many characters of space we need to regain.
	neededGain := utf8.RuneCountInString(p) - targetLength

	// ALGORITHM:
	// truncate parent directories
	// * skips any leading home directory marker
	// * skips the base directory
	// * minimally truncates paths in order from longest to shortest

	const pathSeparator = string(os.PathSeparator)
	const segmentTruncator = '…'
	segments := strings.Split(p, pathSeparator)

	// inclusive/exclusive start/end indexes for the segments we'll try to
	// truncate in this pass.
	segmentsStartI := 0
	segmentsEndI := len(segments) - 1

	// truncate path segments by the minimum possible amount to try to reduce the
	// size of the overall path string.
	for i := segmentsStartI; i < segmentsEndI && neededGain > 0; i++ {
		// find the index of the longest remaining segment. linear search should be
		// fast enough for us since we'll probably never have more than 20 paths (on
		// a typical system at least, no?).
		longestI := segmentsStartI
		for j := segmentsStartI; j < segmentsEndI; j++ {
			// mark this as the longest segment if that's the case
			if len(segments[j]) > len(segments[longestI]) {
				longestI = j
			}
		}

		// operate on the longest segment
		segment := segments[longestI]
		lenSegment := utf8.RuneCountInString(segment)

		// calculate how much we can possibly gain from this segment, omitting the
		// start/end runes and one for the segment truncator.
		maxGain := lenSegment - 3

		// if we can reduce this segment...
		if maxGain > 0 {
			// reduce the segment by the smaller of the needed gain and the most we
			// can gain from this segment.
			reductionAmount := neededGain
			if reductionAmount > maxGain {
				reductionAmount = maxGain
			}

			// replace this segment with its truncated version
			segments[longestI] = compressWithTruncator(
				segment,
				segmentTruncator,
				lenSegment-reductionAmount,
			)

			// reduce the needed gain by the amount we just reduced our segment by
			neededGain -= reductionAmount
		}
	}

	// ALGORITHM:
	// * compress paths of length 3 to the first character and a truncator
	for i := segmentsStartI; i < segmentsEndI && neededGain > 0; i++ {
		segment := segments[i]
		lenSegment := utf8.RuneCountInString(segment)

		// if this segment is small enough, truncate to the first character and a
		// single truncator, saving a single character overall.
		if lenSegment == 3 {
			truncatedSegment := make([]rune, 0, 2)

			// append the first character, followed by a single truncator, then end.
			// this is a ghetto hack to easily pull out the first rune.
			for _, ch := range segment {
				truncatedSegment = append(truncatedSegment, ch, segmentTruncator)
				break
			}

			segments[i] = string(truncatedSegment)

			// reduce the needed gain by the amount we just reduced our segment by
			neededGain -= 1
		}
	}

	// ALGORITHM:
	// * compress already-compressed paths to a single character
	for i := segmentsStartI; i < segmentsEndI && neededGain > 0; i++ {
		segment := segments[i]
		lenSegment := utf8.RuneCountInString(segment)

		// if this segment is small enough and has already been truncated, truncate
		// to the first character alone.
		if lenSegment == 2 {
			lastRune, size := utf8.DecodeLastRuneInString(segment)
			if size > 0 && lastRune == segmentTruncator {
				for _, ch := range segment {
					segments[i] = string(ch)
					break
				}

				// reduce the needed gain by the single character
				neededGain -= 1
			}
		}
	}

	// ALGORITHM:
	// * if we're still out of space, just truncate the original path with a
	//   single truncator character.
	if neededGain > 0 {
		// compress the path by just truncating the original since we've lost so
		// much fidelity at this point it looks nicer this way. otherwise, the
		// result can become littered with random truncators.
		p = compressWithTruncator(origP, segmentTruncator, targetLength)
	} else {
		// put the path back together now that we're done modifying it by segment
		p = path.Join(segments...)
	}

	return p, nil
}

// given a string, returns a hex color based on its contents
func colorHash(input string) int {
	// turn the user/host combination into a color, then use that color as the
	// foreground color of the `@` symbol, to help distinguish between terminals
	// running on different hosts.
	md5Hash := md5.New()
	io.WriteString(md5Hash, input)
	sum := md5Hash.Sum(nil)

	// use the first three bytes as an RGB color, then convert to HSL so we can
	// easily keep the color in a nice range. then convert back to RGB, then back
	// to hex so we can display it!
	r := int(sum[0])
	g := int(sum[1])
	b := int(sum[2])

	h, s, l := rgbToHSL(r, g, b)

	// scale our lightness to keep it readable against a dark background
	minLightness := 0.3
	maxLightness := 0.85
	l = (l * (maxLightness - minLightness)) + minLightness

	r, g, b = hslToRGB(h, s, l)
	return rgbToHex(r, g, b)
}

// returns the user/hostname of the system with a specifically-colored `@`
func userAndHost() string {
	// never mind the error, just use whatever came back
	host, _ := os.Hostname()
	user := os.Getenv("USER")

	c := colorHash(user + host)

	return trueColored("[", c) + user + trueColored("@", c) + host + trueColored("]", c)
}

func currentTime() string {
	return fmt.Sprintf("%d", time.Now().Unix())
}

// print the status line!
func main() {
	cwd, _ := os.Getwd()
	prettyPath, _ := prettifyPath(cwd, 60)
	branch := gitCurrentBranch()

	// pick a color for the branch depending on status output
	branchColor := COLOR_GREEN
	statuses := gitCurrentStatus()
	if statuses != nil && len(statuses) > 0 {
		hasUntracked := false
		hasModified := false

		for _, status := range statuses {
			// true if we have untracked or added files
			hasUntracked = hasUntracked || strings.ContainsAny(status, "A?")

			// true if we have modified, renamed, deleted, or unstaged files
			hasModified = hasModified || strings.ContainsAny(status, "MRDU")
		}

		if hasUntracked && !hasModified {
			branchColor = COLOR_YELLOW
		} else if hasModified {
			branchColor = COLOR_RED
		}
	}

	fmt.Printf("┌╼ %s %s %s %s\n└╼ \n",
		colored(currentTime(), COLOR_MAGENTA),
		userAndHost(),
		colored(prettyPath, COLOR_BLUE),
		colored(branch, branchColor))
}
<commit_msg>Remove brackets around username<commit_after>package main

import (
	"crypto/md5"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"time"
	"unicode/utf8"
)

// locates this directory's parent `.git` directory and returns it, or an error
// if no parent `.git` directory could be found.
func gitPath() (string, error) {
	// start at the current directory
	cur, err := os.Getwd()
	if err != nil {
		return "", err
	}

	// clean up the path and ensure it's absolute so we can traverse all the way
	// to the root directory if necessary.
	cur, err = filepath.Abs(filepath.Clean(cur))
	if err != nil {
		return "", err
	}

	// walk our way up the directory tree, attempting to find a `.git` directory
	const gitDirectoryName = ".git"
	for cur != "/" {
		// list all this directory's children
		children, err := ioutil.ReadDir(cur)
		if err != nil {
			return "", err
		}

		// look for a `.git` directory in the children
		for _, info := range children {
			name := info.Name()

			// if we find a directory with the appropriate name, return its path
			if name == gitDirectoryName && info.IsDir() {
				return path.Join(cur, name), nil
			}
		}

		// if we failed, move up to the parent path
		cur = filepath.Dir(cur)
	}

	// if we've reached the root and haven't found a `.git` directory, return an
	// error.
	return "", fmt.Errorf("No Git directory found.")
}

// finds the current branch of the current Git repository
func gitCurrentBranch() string {
	gitPath, err := gitPath()
	if err != nil {
		return ""
	}

	// this file contains a pointer to the current branch which we can parse to
	// determine the branch name.
	headPath := path.Join(gitPath, "HEAD")

	// read the HEAD file
	data, err := ioutil.ReadFile(headPath)
	if err != nil {
		return ""
	}

	refSpec := strings.TrimSpace(string(data))

	// parse the HEAD file to get the branch name. the HEAD file contents look
	// something like: `ref: refs/heads/master`. we split into three parts, then
	// use whatever's left over as the branch name. If it doesn't split, it's
	// probably a commit hash, in which case we use the first 8 characters of it
	// as the branch name.
	refSpecParts := strings.SplitN(refSpec, "/", 3)
	branchName := ""
	if len(refSpecParts) == 3 {
		// use the last part as the branch name
		branchName = strings.TrimSpace(refSpecParts[2])
	} else if len(refSpecParts) == 1 && len(refSpec) == 40 {
		// we got a commit hash, use the first 7 characters as the branch name
		branchName = refSpec[0:7]
	} else {
		// notify that we failed
		branchName = "BAD_REF_SPEC (" + refSpec + ")"
	}

	// return the third part of our split ref spec, the branch name
	return branchName
}

// gets the current status symbols for the existing git repository as a map of
// file name to status symbol, or nil if there's no repository.
func gitCurrentStatus() map[string]string {
	out, err := exec.Command("git", "status", "--porcelain").CombinedOutput()
	if err != nil {
		return nil
	}

	// turn the output into a map of file to status string
	files := make(map[string]string)
	for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
		// trim whitespace so we can reliably split out the status/name
		line = strings.TrimSpace(line)

		// split into a (status, file) pair
		parts := strings.SplitN(line, " ", 2)
		if len(parts) == 2 {
			files[parts[1]] = parts[0]
		}
	}

	return files
}

func compressWithTruncator(s string, truncator rune, maxLen int) string {
	lenS := utf8.RuneCountInString(s)

	// if we're already short enough, bail
	if lenS <= maxLen {
		return s
	}

	// otherwise, calculate the reduction we need to fit into the max length
	reductionAmount := lenS - maxLen

	// remove the middle characters and replace them with our truncator
	middle := float64(lenS) / 2
	startIExact := middle - (float64(reductionAmount) / 2.0)
	endIExact := startIExact + float64(reductionAmount)
	startI := int(startIExact)
	endI := int(endIExact)

	// protect against overruns
	if startI < 0 {
		startI = 0
	}

	if endI >= lenS {
		endI = lenS
	}

	// construct a new string out of our old string's runes, replacing the
	// truncated ones with our truncator rune.
	truncatedS := make([]rune, 0, lenS-reductionAmount)
	truncated := false
	for i, ch := range s {
		if i < startI {
			truncatedS = append(truncatedS, ch)
		} else if !truncated {
			// add the truncator character if we haven't done so already
			truncatedS = append(truncatedS, truncator)
			truncated = true
		} else if i > endI {
			truncatedS = append(truncatedS, ch)
		}
	}

	return string(truncatedS)
}

// shortens and prettifies the given path, keeping it at or under the target
// length in runes.
func prettifyPath(p string, targetLength int) (string, error) {
	// clean up the path first
	p, err := filepath.Abs(filepath.Clean(p))
	if err != nil {
		return "", err
	}

	// if this path is in the current HOME directory, replace that dir with `~`
	homePath := os.Getenv("HOME")
	const homeTruncator = "~"
	if homePath != "" && strings.HasPrefix(p, homePath) {
		// mark that we're in the home directory for later
		p = homeTruncator + p[len(homePath):]
	}

	// save an original copy in case we can't do smart truncation well enough
	origP := p

	// determine how much we need to shorten our path to get it under the target,
	// i.e. how many characters of space we need to regain.
	neededGain := utf8.RuneCountInString(p) - targetLength

	// ALGORITHM:
	// truncate parent directories
	// * skips any leading home directory marker
	// * skips the base directory
	// * minimally truncates paths in order from longest to shortest

	const pathSeparator = string(os.PathSeparator)
	const segmentTruncator = '…'
	segments := strings.Split(p, pathSeparator)

	// inclusive/exclusive start/end indexes for the segments we'll try to
	// truncate in this pass.
	segmentsStartI := 0
	segmentsEndI := len(segments) - 1

	// truncate path segments by the minimum possible amount to try to reduce the
	// size of the overall path string.
	for i := segmentsStartI; i < segmentsEndI && neededGain > 0; i++ {
		// find the index of the longest remaining segment. linear search should be
		// fast enough for us since we'll probably never have more than 20 paths (on
		// a typical system at least, no?).
		longestI := segmentsStartI
		for j := segmentsStartI; j < segmentsEndI; j++ {
			// mark this as the longest segment if that's the case
			if len(segments[j]) > len(segments[longestI]) {
				longestI = j
			}
		}

		// operate on the longest segment
		segment := segments[longestI]
		lenSegment := utf8.RuneCountInString(segment)

		// calculate how much we can possibly gain from this segment, omitting the
		// start/end runes and one for the segment truncator.
		maxGain := lenSegment - 3

		// if we can reduce this segment...
		if maxGain > 0 {
			// reduce the segment by the smaller of the needed gain and the most we
			// can gain from this segment.
			reductionAmount := neededGain
			if reductionAmount > maxGain {
				reductionAmount = maxGain
			}

			// replace this segment with its truncated version
			segments[longestI] = compressWithTruncator(
				segment,
				segmentTruncator,
				lenSegment-reductionAmount,
			)

			// reduce the needed gain by the amount we just reduced our segment by
			neededGain -= reductionAmount
		}
	}

	// ALGORITHM:
	// * compress paths of length 3 to the first character and a truncator
	for i := segmentsStartI; i < segmentsEndI && neededGain > 0; i++ {
		segment := segments[i]
		lenSegment := utf8.RuneCountInString(segment)

		// if this segment is small enough, truncate to the first character and a
		// single truncator, saving a single character overall.
		if lenSegment == 3 {
			truncatedSegment := make([]rune, 0, 2)

			// append the first character, followed by a single truncator, then end.
			// this is a ghetto hack to easily pull out the first rune.
			for _, ch := range segment {
				truncatedSegment = append(truncatedSegment, ch, segmentTruncator)
				break
			}

			segments[i] = string(truncatedSegment)

			// reduce the needed gain by the amount we just reduced our segment by
			neededGain -= 1
		}
	}

	// ALGORITHM:
	// * compress already-compressed paths to a single character
	for i := segmentsStartI; i < segmentsEndI && neededGain > 0; i++ {
		segment := segments[i]
		lenSegment := utf8.RuneCountInString(segment)

		// if this segment is small enough and has already been truncated, truncate
		// to the first character alone.
		if lenSegment == 2 {
			lastRune, size := utf8.DecodeLastRuneInString(segment)
			if size > 0 && lastRune == segmentTruncator {
				for _, ch := range segment {
					segments[i] = string(ch)
					break
				}

				// reduce the needed gain by the single character
				neededGain -= 1
			}
		}
	}

	// ALGORITHM:
	// * if we're still out of space, just truncate the original path with a
	//   single truncator character.
	if neededGain > 0 {
		// compress the path by just truncating the original since we've lost so
		// much fidelity at this point it looks nicer this way. otherwise, the
		// result can become littered with random truncators.
		p = compressWithTruncator(origP, segmentTruncator, targetLength)
	} else {
		// put the path back together now that we're done modifying it by segment
		p = path.Join(segments...)
	}

	return p, nil
}

// given a string, returns a hex color based on its contents
func colorHash(input string) int {
	// turn the user/host combination into a color, then use that color as the
	// foreground color of the `@` symbol, to help distinguish between terminals
	// running on different hosts.
	md5Hash := md5.New()
	io.WriteString(md5Hash, input)
	sum := md5Hash.Sum(nil)

	// use the first three bytes as an RGB color, then convert to HSL so we can
	// easily keep the color in a nice range. then convert back to RGB, then back
	// to hex so we can display it!
	r := int(sum[0])
	g := int(sum[1])
	b := int(sum[2])

	h, s, l := rgbToHSL(r, g, b)

	// scale our lightness to keep it readable against a dark background
	minLightness := 0.3
	maxLightness := 0.85
	l = (l * (maxLightness - minLightness)) + minLightness

	r, g, b = hslToRGB(h, s, l)
	return rgbToHex(r, g, b)
}

// returns the user/hostname of the system with a specifically-colored `@`
func userAndHost() string {
	// never mind the error, just use whatever came back
	host, _ := os.Hostname()
	user := os.Getenv("USER")

	c := colorHash(user + host)

	return user + trueColored("@", c) + host
}

func currentTime() string {
	return fmt.Sprintf("%d", time.Now().Unix())
}

// print the status line!
func main() {
	cwd, _ := os.Getwd()
	prettyPath, _ := prettifyPath(cwd, 60)
	branch := gitCurrentBranch()

	// pick a color for the branch depending on status output
	branchColor := COLOR_GREEN
	statuses := gitCurrentStatus()
	if statuses != nil && len(statuses) > 0 {
		hasUntracked := false
		hasModified := false

		for _, status := range statuses {
			// true if we have untracked or added files
			hasUntracked = hasUntracked || strings.ContainsAny(status, "A?")

			// true if we have modified, renamed, deleted, or unstaged files
			hasModified = hasModified || strings.ContainsAny(status, "MRDU")
		}

		if hasUntracked && !hasModified {
			branchColor = COLOR_YELLOW
		} else if hasModified {
			branchColor = COLOR_RED
		}
	}

	fmt.Printf("┌╼ %s %s %s %s\n└╼ \n",
		colored(currentTime(), COLOR_MAGENTA),
		userAndHost(),
		colored(prettyPath, COLOR_BLUE),
		colored(branch, branchColor))
}
<|endoftext|>


<commit_before>package pixelgl

// Doer is an interface for manipulating OpenGL state.
//
// OpenGL is a state machine. Every object can 'enter' it's state and 'leave' it's state. For example,
// you can bind a buffer and unbind a buffer, bind a texture and unbind it, use shader and unuse it, and so on.
//
// This interface provides a clever and flexible way to do it. A typical workflow of an OpenGL object is that
// you enter (load, bind) that object's state, then do something with it, and then leave the state. That 'something'
// in between, let's call it sub (as in subroutine).
//
// The recommended way to implement a Doer is to wrap another Doer (vertex array wraps texture and so on), let's call
// it parent. Then the Do method will look like this:
//
//   func (o *MyObject) Do(sub func(Context)) {
//       o.parent.Do(func(ctx Context) {
//	     // enter the object's state
//           sub(ctx)
//           // leave the object's state
//       })
//   }
//
// It might seem difficult to grasp this kind of recursion at first, but it's really simple. What it's basically saying
// is: "Hey parent, enter your state, then let me enter mine, then I'll do whatever I'm supposed to do in the middle.
// After that I'll leave my state and please leave your state too parent."
//
// Also notice, that the functions are passing a Context around. This context contains the most important state variables.
// Usually, you just pass it as you received it. If you want to pass a changed context to your child (e.g. your a shader),
// use ctx.With* methods.
//
// If possible and makes sense, Do method should be reentrant.
type Doer interface {
	Do(sub func(Context))
}

// Context takes state from one object to another. OpenGL is a state machine, so we have to approach it like that.
// However, global variables are evil, so we have Context, that transfers important OpenGL state from one object to another.
//
// This type does *not* represent an OpenGL context in the OpenGL terminology.
type Context struct {
	shader *Shader
}

// Shader returns the current shader.
func (c Context) Shader() *Shader {
	return c.shader
}

// WithShader returns a copy of this context with the specified shader.
func (c Context) WithShader(s *Shader) Context {
	return Context{
		shader: s,
	}
}

// ContextHolder is a root Doer with no parent. It simply forwards a context to a child.
type ContextHolder struct {
	Context Context
}

// Do calls sub and passes it the held context.
func (ch *ContextHolder) Do(sub func(ctx Context)) {
	sub(ch.Context)
}
<commit_msg>fix compile error<commit_after>package pixelgl

// Doer is an interface for manipulating OpenGL state.
//
// OpenGL is a state machine. Every object can 'enter' it's state and 'leave' it's state. For example,
// you can bind a buffer and unbind a buffer, bind a texture and unbind it, use shader and unuse it, and so on.
//
// This interface provides a clever and flexible way to do it. A typical workflow of an OpenGL object is that
// you enter (load, bind) that object's state, then do something with it, and then leave the state. That 'something'
// in between, let's call it sub (as in subroutine).
//
// The recommended way to implement a Doer is to wrap another Doer (vertex array wraps texture and so on), let's call
// it parent. Then the Do method will look like this:
//
//   func (o *MyObject) Do(sub func(Context)) {
//       o.parent.Do(func(ctx Context) {
//	     // enter the object's state
//           sub(ctx)
//           // leave the object's state
//       })
//   }
//
// It might seem difficult to grasp this kind of recursion at first, but it's really simple. What it's basically saying
// is: "Hey parent, enter your state, then let me enter mine, then I'll do whatever I'm supposed to do in the middle.
// After that I'll leave my state and please leave your state too parent."
//
// Also notice, that the functions are passing a Context around. This context contains the most important state variables.
// Usually, you just pass it as you received it. If you want to pass a changed context to your child (e.g. your a shader),
// use ctx.With* methods.
//
// If possible and makes sense, Do method should be reentrant.
type Doer interface {
	Do(sub func(Context))
}

// Context takes state from one object to another. OpenGL is a state machine, so we have to approach it like that.
// However, global variables are evil, so we have Context, that transfers important OpenGL state from one object to another.
//
// This type does *not* represent an OpenGL context in the OpenGL terminology.
type Context struct {
	shader *Shader
}

// Shader returns the current shader.
func (c Context) Shader() *Shader {
	return c.shader
}

// WithShader returns a copy of this context with the specified shader.
func (c Context) WithShader(s *Shader) Context {
	return Context{
		shader: s,
	}
}

// ContextHolder is a root Doer with no parent. It simply forwards a context to a child.
type ContextHolder struct {
	Context Context
}

// Do calls sub and passes it the held context.
func (ch *ContextHolder) Do(sub func(ctx Context)) {
	sub(ch.Context)
}

type noOpDoer struct{}

func (noOpDoer) Do(sub func(ctx Context)) {
	sub(Context{})
}

// NoOpDoer is a Doer that just passes an empty context to the caller of Do.
var NoOpDoer = noOpDoer{}
<|endoftext|>


<commit_before>// Package closer ensures a clean exit for your Go app.
//
// The aim of this package is to provide an universal way to catch the event of application’s exit
// and perform some actions before it’s too late. Closer doesn’t care about the way application
// tries to exit, i.e. was that a panic or just a signal from the OS, it calls the provided methods
// for cleanup and that’s the whole point.
//
// Exit codes
//
// All errors and panics will be logged if the logging option of `closer.Checked` was set true,
// also the exit code (for `os.Exit`) will be determined accordingly:
//
//   Event         | Default exit code
//   ------------- | -------------
//   error = nil   | 0 (success)
//   error != nil  | 1 (failure)
//   panic         | 1 (failure)
//
package closer

import (
	"fmt"
	"log"
	"os"
	"os/signal"
	"runtime"
	"sync"
	"syscall"
)

var (
	// DebugSignalSet is a predefined list of signals to watch for. Usually
	// these signals will terminate the app without executing the code in defer blocks.
	DebugSignalSet = []os.Signal{
		syscall.SIGINT,
		syscall.SIGHUP,
		syscall.SIGTERM,
	}
	// DefaultSignalSet will have syscall.SIGABRT that should be
	// opted out if user wants to debug the stacktrace.
	DefaultSignalSet = append(DebugSignalSet, syscall.SIGABRT)
)

var (
	// ExitCodeOK is a successfull exit code.
	ExitCodeOK = 0
	// ExitCodeErr is a failure exit code.
	ExitCodeErr = 1
	// ExitSignals is the active list of signals to watch for.
	ExitSignals = DefaultSignalSet
)

// Config should be used with Init function to override the defaults.
type Config struct {
	ExitCodeOK  int
	ExitCodeErr int
	ExitSignals []os.Signal
}

var c = newCloser()

type closer struct {
	codeOK     int
	codeErr    int
	signals    []os.Signal
	sem        sync.Mutex
	cleanups   []func()
	errChan    chan struct{}
	doneChan   chan struct{}
	signalChan chan os.Signal
	closeChan  chan struct{}
	holdChan   chan struct{}
	//
	cancelWaitChan chan struct{}
}

func newCloser() *closer {
	c := &closer{
		codeOK:  ExitCodeOK,
		codeErr: ExitCodeErr,
		signals: ExitSignals,
		//
		errChan:    make(chan struct{}),
		doneChan:   make(chan struct{}),
		signalChan: make(chan os.Signal, 1),
		closeChan:  make(chan struct{}),
		holdChan:   make(chan struct{}),
		//
		cancelWaitChan: make(chan struct{}),
	}

	signal.Notify(c.signalChan, c.signals...)

	// start waiting
	go c.wait()
	return c
}

func (c *closer) wait() {
	exitCode := c.codeOK

	// wait for a close request
	select {
	case <-c.cancelWaitChan:
		return
	case <-c.signalChan:
	case <-c.closeChan:
		break
	case <-c.errChan:
		exitCode = c.codeErr
	}

	// ensure we'll exit
	defer os.Exit(exitCode)

	c.sem.Lock()
	defer c.sem.Unlock()
	for _, fn := range c.cleanups {
		fn()
	}
	// done!
	close(c.doneChan)
}

// Close sends a close request.
// The app will be terminated by OS as soon as the first close request will be handled by closer, this
// function will return no sooner. The exit code will always be 0 (success).
func Close() {
	// check if there was a panic
	if x := recover(); x != nil {
		var (
			offset int = 3
			pc     uintptr
			ok     bool
		)
		log.Printf("run time panic: %v", x)
		for offset < 32 {
			pc, _, _, ok = runtime.Caller(offset)
			if !ok {
				// close with an error
				close(c.errChan)
				<-c.doneChan
				return
			}
			frame := newStackFrame(pc)
			fmt.Print(frame.String())
			offset++
		}
		// close with an error
		close(c.errChan)
		<-c.doneChan
	}
	// normal close
	close(c.closeChan)
	<-c.doneChan
}

// Fatalln works the same as log.Fatalln but respects the closer's logic.
func Fatalln(v ...interface{}) {
	out := log.New(os.Stderr, "", log.Flags())
	out.Output(2, fmt.Sprintln(v...))
	close(c.errChan)
	<-c.doneChan
}

// Fatalf works the same as log.Fatalf but respects the closer's logic.
func Fatalf(format string, v ...interface{}) {
	out := log.New(os.Stderr, "", log.Flags())
	out.Output(2, fmt.Sprintf(format, v...))
	close(c.errChan)
	<-c.doneChan
}

func (c *closer) closeErr() {
	close(c.errChan)
	<-c.doneChan
}

// Init allows user to override the defaults (a set of OS signals to watch for, for example).
func Init(cfg Config) {
	c.sem.Lock()
	signal.Stop(c.signalChan)
	close(c.cancelWaitChan)
	c.codeOK = cfg.ExitCodeOK
	c.codeErr = cfg.ExitCodeErr
	c.signals = cfg.ExitSignals
	signal.Notify(c.signalChan, c.signals...)
	go c.wait()
	c.sem.Unlock()
}

// Bind will register the cleanup function that will be called when closer will get a close request.
// All the callbacks will be called in the reverse order they were bound, that's similar to how `defer` works.
func Bind(cleanup func()) {
	c.sem.Lock()
	// store in the reverse order
	s := make([]func(), 0, 1+len(c.cleanups))
	s = append(s, cleanup)
	c.cleanups = append(s, c.cleanups...)
	c.sem.Unlock()
}

// Checked runs the target function and checks for panics and errors it may yield. In case of panic or error, closer
// will terminate the app with an error code, but either case it will call all the bound callbacks beforehand.
// One can use this instead of `defer` if you need to care about errors and panics that always may happen.
// This function optionally can emit log messages via standard `log` package.
func Checked(target func() error, logging bool) {
	defer func() {
		// check if there was a panic
		if x := recover(); x != nil {
			if logging {
				log.Printf("run time panic: %v", x)
			}
			// close with an error
			c.closeErr()
		}
	}()
	if err := target(); err != nil {
		if logging {
			log.Println("error:", err)
		}
		// close with an error
		c.closeErr()
	}
}

// Hold is a helper that may be used to hold the main from returning,
// until the closer will do a proper exit via `os.Exit`.
func Hold() {
	<-c.holdChan
}
<commit_msg>Close once.<commit_after>// Package closer ensures a clean exit for your Go app.
//
// The aim of this package is to provide an universal way to catch the event of application’s exit
// and perform some actions before it’s too late. Closer doesn’t care about the way application
// tries to exit, i.e. was that a panic or just a signal from the OS, it calls the provided methods
// for cleanup and that’s the whole point.
//
// Exit codes
//
// All errors and panics will be logged if the logging option of `closer.Checked` was set true,
// also the exit code (for `os.Exit`) will be determined accordingly:
//
//   Event         | Default exit code
//   ------------- | -------------
//   error = nil   | 0 (success)
//   error != nil  | 1 (failure)
//   panic         | 1 (failure)
//
package closer

import (
	"fmt"
	"log"
	"os"
	"os/signal"
	"runtime"
	"sync"
	"syscall"
)

var (
	// DebugSignalSet is a predefined list of signals to watch for. Usually
	// these signals will terminate the app without executing the code in defer blocks.
	DebugSignalSet = []os.Signal{
		syscall.SIGINT,
		syscall.SIGHUP,
		syscall.SIGTERM,
	}
	// DefaultSignalSet will have syscall.SIGABRT that should be
	// opted out if user wants to debug the stacktrace.
	DefaultSignalSet = append(DebugSignalSet, syscall.SIGABRT)
)

var (
	// ExitCodeOK is a successfull exit code.
	ExitCodeOK = 0
	// ExitCodeErr is a failure exit code.
	ExitCodeErr = 1
	// ExitSignals is the active list of signals to watch for.
	ExitSignals = DefaultSignalSet
)

// Config should be used with Init function to override the defaults.
type Config struct {
	ExitCodeOK  int
	ExitCodeErr int
	ExitSignals []os.Signal
}

var c = newCloser()

type closer struct {
	codeOK     int
	codeErr    int
	signals    []os.Signal
	sem        sync.Mutex
	closeOnce  sync.Once
	cleanups   []func()
	errChan    chan struct{}
	doneChan   chan struct{}
	signalChan chan os.Signal
	closeChan  chan struct{}
	holdChan   chan struct{}
	//
	cancelWaitChan chan struct{}
}

func newCloser() *closer {
	c := &closer{
		codeOK:  ExitCodeOK,
		codeErr: ExitCodeErr,
		signals: ExitSignals,
		//
		errChan:    make(chan struct{}),
		doneChan:   make(chan struct{}),
		signalChan: make(chan os.Signal, 1),
		closeChan:  make(chan struct{}),
		holdChan:   make(chan struct{}),
		//
		cancelWaitChan: make(chan struct{}),
	}

	signal.Notify(c.signalChan, c.signals...)

	// start waiting
	go c.wait()
	return c
}

func (c *closer) wait() {
	exitCode := c.codeOK

	// wait for a close request
	select {
	case <-c.cancelWaitChan:
		return
	case <-c.signalChan:
	case <-c.closeChan:
		break
	case <-c.errChan:
		exitCode = c.codeErr
	}

	// ensure we'll exit
	defer os.Exit(exitCode)

	c.sem.Lock()
	defer c.sem.Unlock()
	for _, fn := range c.cleanups {
		fn()
	}
	// done!
	close(c.doneChan)
}

// Close sends a close request.
// The app will be terminated by OS as soon as the first close request will be handled by closer, this
// function will return no sooner. The exit code will always be 0 (success).
func Close() {
	// check if there was a panic
	if x := recover(); x != nil {
		var (
			offset int = 3
			pc     uintptr
			ok     bool
		)
		log.Printf("run time panic: %v", x)
		for offset < 32 {
			pc, _, _, ok = runtime.Caller(offset)
			if !ok {
				// close with an error
				c.closeErr()
				return
			}
			frame := newStackFrame(pc)
			fmt.Print(frame.String())
			offset++
		}
		// close with an error
		c.closeErr()
	}
	// normal close
	c.closeOnce.Do(func() {
		close(c.closeChan)
	})
	<-c.doneChan
}

// Fatalln works the same as log.Fatalln but respects the closer's logic.
func Fatalln(v ...interface{}) {
	out := log.New(os.Stderr, "", log.Flags())
	out.Output(2, fmt.Sprintln(v...))
	c.closeErr()
}

// Fatalf works the same as log.Fatalf but respects the closer's logic.
func Fatalf(format string, v ...interface{}) {
	out := log.New(os.Stderr, "", log.Flags())
	out.Output(2, fmt.Sprintf(format, v...))
	c.closeErr()
}

func (c *closer) closeErr() {
	c.closeOnce.Do(func() {
		close(c.errChan)
	})
	<-c.doneChan
}

// Init allows user to override the defaults (a set of OS signals to watch for, for example).
func Init(cfg Config) {
	c.sem.Lock()
	signal.Stop(c.signalChan)
	close(c.cancelWaitChan)
	c.codeOK = cfg.ExitCodeOK
	c.codeErr = cfg.ExitCodeErr
	c.signals = cfg.ExitSignals
	signal.Notify(c.signalChan, c.signals...)
	go c.wait()
	c.sem.Unlock()
}

// Bind will register the cleanup function that will be called when closer will get a close request.
// All the callbacks will be called in the reverse order they were bound, that's similar to how `defer` works.
func Bind(cleanup func()) {
	c.sem.Lock()
	// store in the reverse order
	s := make([]func(), 0, 1+len(c.cleanups))
	s = append(s, cleanup)
	c.cleanups = append(s, c.cleanups...)
	c.sem.Unlock()
}

// Checked runs the target function and checks for panics and errors it may yield. In case of panic or error, closer
// will terminate the app with an error code, but either case it will call all the bound callbacks beforehand.
// One can use this instead of `defer` if you need to care about errors and panics that always may happen.
// This function optionally can emit log messages via standard `log` package.
func Checked(target func() error, logging bool) {
	defer func() {
		// check if there was a panic
		if x := recover(); x != nil {
			if logging {
				log.Printf("run time panic: %v", x)
			}
			// close with an error
			c.closeErr()
		}
	}()
	if err := target(); err != nil {
		if logging {
			log.Println("error:", err)
		}
		// close with an error
		c.closeErr()
	}
}

// Hold is a helper that may be used to hold the main from returning,
// until the closer will do a proper exit via `os.Exit`.
func Hold() {
	<-c.holdChan
}
<|endoftext|>


<commit_before>package backup

import (
	"testing"
	. "github.com/bborbe/assert"
	"github.com/bborbe/backup/host"
	"github.com/bborbe/backup/rootdir"
)

func TestImplementsBackup(t *testing.T) {
	backup := ByName(host.ByName(rootdir.New("/rootdir"), "hostname"), "backupname")
	var expected *Backup
	err := AssertThat(backup, Implements(expected))
	if err != nil {
		t.Fatal(err)
	}
}
<commit_msg>test backup<commit_after>package backup

import (
	"testing"
	. "github.com/bborbe/assert"
	"github.com/bborbe/backup/host"
	"github.com/bborbe/backup/rootdir"
)

func TestImplementsBackup(t *testing.T) {
	backup := ByName(host.ByName(rootdir.New("/rootdir"), "hostname"), "backupname")
	var expected *Backup
	err := AssertThat(backup, Implements(expected))
	if err != nil {
		t.Fatal(err)
	}
}

func TestName(t *testing.T) {
	backup := ByName(host.ByName(rootdir.New("/rootdir"), "hostname"), "backupname")
	err := AssertThat(backup.Name(), Is("backupname"))
	if err != nil {
		t.Fatal(err)
	}
}

func TestPath(t *testing.T) {
	backup := ByName(host.ByName(rootdir.New("/rootdir"), "hostname"), "backupname")
	err := AssertThat(backup.Path(), Is("/rootdir/hostname/backupname"))
	if err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package api

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"

	"github.com/grafana/grafana/pkg/api/cloudwatch"
	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/metrics"
	"github.com/grafana/grafana/pkg/middleware"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/setting"
	"github.com/grafana/grafana/pkg/util"
)

var (
	dataproxyLogger log.Logger = log.New("data-proxy-log")
)

func NewReverseProxy(ds *m.DataSource, proxyPath string, targetUrl *url.URL) *httputil.ReverseProxy {
	director := func(req *http.Request) {
		req.URL.Scheme = targetUrl.Scheme
		req.URL.Host = targetUrl.Host
		req.Host = targetUrl.Host

		reqQueryVals := req.URL.Query()

		if ds.Type == m.DS_INFLUXDB_08 {
			req.URL.Path = util.JoinUrlFragments(targetUrl.Path, "db/"+ds.Database+"/"+proxyPath)
			reqQueryVals.Add("u", ds.User)
			reqQueryVals.Add("p", ds.Password)
			req.URL.RawQuery = reqQueryVals.Encode()
		} else if ds.Type == m.DS_INFLUXDB {
			req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath)
			req.URL.RawQuery = reqQueryVals.Encode()
			if !ds.BasicAuth {
				req.Header.Del("Authorization")
				req.Header.Add("Authorization", util.GetBasicAuthHeader(ds.User, ds.Password))
			}
		} else {
			req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath)
		}

		if ds.BasicAuth {
			req.Header.Del("Authorization")
			req.Header.Add("Authorization", util.GetBasicAuthHeader(ds.BasicAuthUser, ds.BasicAuthPassword))
		}

		dsAuth := req.Header.Get("X-DS-Authorization")
		if len(dsAuth) > 0 {
			req.Header.Del("X-DS-Authorization")
			req.Header.Del("Authorization")
			req.Header.Add("Authorization", dsAuth)
		}

		// clear cookie headers
		req.Header.Del("Cookie")
		req.Header.Del("Set-Cookie")
	}

	return &httputil.ReverseProxy{Director: director, FlushInterval: time.Millisecond * 200}
}

func getDatasource(id int64, orgId int64) (*m.DataSource, error) {
	query := m.GetDataSourceByIdQuery{Id: id, OrgId: orgId}
	if err := bus.Dispatch(&query); err != nil {
		return nil, err
	}

	return query.Result, nil
}

func ProxyDataSourceRequest(c *middleware.Context) {
	c.TimeRequest(metrics.M_DataSource_ProxyReq_Timer)

	ds, err := getDatasource(c.ParamsInt64(":id"), c.OrgId)

	if err != nil {
		c.JsonApiErr(500, "Unable to load datasource meta data", err)
		return
	}

	if ds.Type == m.DS_CLOUDWATCH {
		cloudwatch.HandleRequest(c, ds)
		return
	}

	if ds.Type == m.DS_INFLUXDB {
		if c.Query("db") != ds.Database {
			c.JsonApiErr(403, "Datasource is not configured to allow this database", nil)
			return
		}
	}

	targetUrl, _ := url.Parse(ds.Url)
	if len(setting.DataProxyWhiteList) > 0 {
		if _, exists := setting.DataProxyWhiteList[targetUrl.Host]; !exists {
			c.JsonApiErr(403, "Data proxy hostname and ip are not included in whitelist", nil)
			return
		}
	}

	proxyPath := c.Params("*")

	if ds.Type == m.DS_ES {
		if c.Req.Request.Method == "DELETE" {
			c.JsonApiErr(403, "Deletes not allowed on proxied Elasticsearch datasource", nil)
			return
		}
		if c.Req.Request.Method == "PUT" {
			c.JsonApiErr(403, "Puts not allowed on proxied Elasticsearch datasource", nil)
			return
		}
		if c.Req.Request.Method == "POST" && proxyPath != "_msearch" {
			c.JsonApiErr(403, "Posts not allowed on proxied Elasticsearch datasource except on /_msearch", nil)
			return
		}
	}

	proxy := NewReverseProxy(ds, proxyPath, targetUrl)
	proxy.Transport, err = ds.GetHttpTransport()
	if err != nil {
		c.JsonApiErr(400, "Unable to load TLS certificate", err)
		return
	}

	logProxyRequest(ds.Type, c)
	proxy.ServeHTTP(c.Resp, c.Req.Request)
	c.Resp.Header().Del("Set-Cookie")
}

func logProxyRequest(dataSourceType string, c *middleware.Context) {
	if !setting.DataProxyLogging {
		return
	}

	var body string
	if c.Req.Request.Body != nil {
		buffer, err := ioutil.ReadAll(c.Req.Request.Body)
		if err == nil {
			c.Req.Request.Body = ioutil.NopCloser(bytes.NewBuffer(buffer))
			body = string(buffer)
		}
	}

	dataproxyLogger.Info("Proxying incoming request",
		"userid", c.UserId,
		"orgid", c.OrgId,
		"username", c.Login,
		"datasource", dataSourceType,
		"uri", c.Req.RequestURI,
		"method", c.Req.Request.Method,
		"body", body)
}
<commit_msg>improve security of Prometheus datasource<commit_after>package api

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"

	"github.com/grafana/grafana/pkg/api/cloudwatch"
	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/metrics"
	"github.com/grafana/grafana/pkg/middleware"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/setting"
	"github.com/grafana/grafana/pkg/util"
)

var (
	dataproxyLogger log.Logger = log.New("data-proxy-log")
)

func NewReverseProxy(ds *m.DataSource, proxyPath string, targetUrl *url.URL) *httputil.ReverseProxy {
	director := func(req *http.Request) {
		req.URL.Scheme = targetUrl.Scheme
		req.URL.Host = targetUrl.Host
		req.Host = targetUrl.Host

		reqQueryVals := req.URL.Query()

		if ds.Type == m.DS_INFLUXDB_08 {
			req.URL.Path = util.JoinUrlFragments(targetUrl.Path, "db/"+ds.Database+"/"+proxyPath)
			reqQueryVals.Add("u", ds.User)
			reqQueryVals.Add("p", ds.Password)
			req.URL.RawQuery = reqQueryVals.Encode()
		} else if ds.Type == m.DS_INFLUXDB {
			req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath)
			req.URL.RawQuery = reqQueryVals.Encode()
			if !ds.BasicAuth {
				req.Header.Del("Authorization")
				req.Header.Add("Authorization", util.GetBasicAuthHeader(ds.User, ds.Password))
			}
		} else {
			req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath)
		}

		if ds.BasicAuth {
			req.Header.Del("Authorization")
			req.Header.Add("Authorization", util.GetBasicAuthHeader(ds.BasicAuthUser, ds.BasicAuthPassword))
		}

		dsAuth := req.Header.Get("X-DS-Authorization")
		if len(dsAuth) > 0 {
			req.Header.Del("X-DS-Authorization")
			req.Header.Del("Authorization")
			req.Header.Add("Authorization", dsAuth)
		}

		// clear cookie headers
		req.Header.Del("Cookie")
		req.Header.Del("Set-Cookie")
	}

	return &httputil.ReverseProxy{Director: director, FlushInterval: time.Millisecond * 200}
}

func getDatasource(id int64, orgId int64) (*m.DataSource, error) {
	query := m.GetDataSourceByIdQuery{Id: id, OrgId: orgId}
	if err := bus.Dispatch(&query); err != nil {
		return nil, err
	}

	return query.Result, nil
}

func ProxyDataSourceRequest(c *middleware.Context) {
	c.TimeRequest(metrics.M_DataSource_ProxyReq_Timer)

	ds, err := getDatasource(c.ParamsInt64(":id"), c.OrgId)

	if err != nil {
		c.JsonApiErr(500, "Unable to load datasource meta data", err)
		return
	}

	if ds.Type == m.DS_CLOUDWATCH {
		cloudwatch.HandleRequest(c, ds)
		return
	}

	if ds.Type == m.DS_INFLUXDB {
		if c.Query("db") != ds.Database {
			c.JsonApiErr(403, "Datasource is not configured to allow this database", nil)
			return
		}
	}

	targetUrl, _ := url.Parse(ds.Url)
	if len(setting.DataProxyWhiteList) > 0 {
		if _, exists := setting.DataProxyWhiteList[targetUrl.Host]; !exists {
			c.JsonApiErr(403, "Data proxy hostname and ip are not included in whitelist", nil)
			return
		}
	}

	proxyPath := c.Params("*")

	if ds.Type == m.DS_PROMETHEUS {
		if !(c.Req.Request.Method == "GET" && strings.Index(proxyPath, "api/") == 0) {
			c.JsonApiErr(403, "GET is only allowed on proxied Prometheus datasource", nil)
			return
		}
	}

	if ds.Type == m.DS_ES {
		if c.Req.Request.Method == "DELETE" {
			c.JsonApiErr(403, "Deletes not allowed on proxied Elasticsearch datasource", nil)
			return
		}
		if c.Req.Request.Method == "PUT" {
			c.JsonApiErr(403, "Puts not allowed on proxied Elasticsearch datasource", nil)
			return
		}
		if c.Req.Request.Method == "POST" && proxyPath != "_msearch" {
			c.JsonApiErr(403, "Posts not allowed on proxied Elasticsearch datasource except on /_msearch", nil)
			return
		}
	}

	proxy := NewReverseProxy(ds, proxyPath, targetUrl)
	proxy.Transport, err = ds.GetHttpTransport()
	if err != nil {
		c.JsonApiErr(400, "Unable to load TLS certificate", err)
		return
	}

	logProxyRequest(ds.Type, c)
	proxy.ServeHTTP(c.Resp, c.Req.Request)
	c.Resp.Header().Del("Set-Cookie")
}

func logProxyRequest(dataSourceType string, c *middleware.Context) {
	if !setting.DataProxyLogging {
		return
	}

	var body string
	if c.Req.Request.Body != nil {
		buffer, err := ioutil.ReadAll(c.Req.Request.Body)
		if err == nil {
			c.Req.Request.Body = ioutil.NopCloser(bytes.NewBuffer(buffer))
			body = string(buffer)
		}
	}

	dataproxyLogger.Info("Proxying incoming request",
		"userid", c.UserId,
		"orgid", c.OrgId,
		"username", c.Login,
		"datasource", dataSourceType,
		"uri", c.Req.RequestURI,
		"method", c.Req.Request.Method,
		"body", body)
}
<|endoftext|>


<commit_before>// Package link implements parsing and serialization of Link header values as
// defined in RFC 5988.
package link

import (
	"bytes"
	"errors"
	"sort"
	"unicode"
)

type Link struct {
	URI    string
	Rel    string
	Params map[string]string
}

// Format serializes a slice of Links into a header value. It does not currently
// implement RFC 2231 handling of non-ASCII character encoding and language
// information.
func Format(links []Link) string {
	buf := &bytes.Buffer{}
	for i, link := range links {
		if i > 0 {
			buf.Write([]byte(", "))
		}
		buf.WriteByte('<')
		buf.WriteString(link.URI)
		buf.WriteByte('>')

		writeParam(buf, "rel", link.Rel)

		keys := make([]string, 0, len(link.Params))
		for k, _ := range link.Params {
			keys = append(keys, k)
		}
		sort.Strings(keys)

		for _, k := range keys {
			writeParam(buf, k, link.Params[k])
		}
	}

	return buf.String()
}

func writeParam(buf *bytes.Buffer, key, value string) {
	buf.Write([]byte("; "))
	buf.WriteString(key)
	buf.Write([]byte(`="`))
	buf.WriteString(value)
	buf.WriteByte('"')
}

// Parse parses a Link header value into a slice of Links. It does not currently
// implement RFC 2231 handling of non-ASCII character encoding and language
// information.
func Parse(l string) ([]Link, error) {
	v := []byte(l)
	v = bytes.TrimSpace(v)
	if len(v) == 0 {
		return nil, nil
	}

	links := make([]Link, 0, 1)
	for len(v) > 0 {
		if v[0] != '<' {
			return nil, errors.New("link: does not start with <")
		}
		lend := bytes.IndexByte(v, '>')
		if lend == -1 {
			return nil, errors.New("link: does not contain ending >")
		}

		params := make(map[string]string)
		link := Link{URI: string(v[1:lend]), Params: params}
		links = append(links, link)

		// trim off parsed url
		v = v[lend+1:]
		if len(v) == 0 {
			break
		}
		v = bytes.TrimLeftFunc(v, unicode.IsSpace)

		for len(v) > 0 {
			if v[0] != ';' && v[0] != ',' {
				return nil, errors.New(`link: expected ";" or "'", got "` + string(v[0:1]) + `"`)
			}
			var next bool
			if v[0] == ',' {
				next = true
			}
			v = bytes.TrimLeftFunc(v[1:], unicode.IsSpace)
			if next || len(v) == 0 {
				break
			}
			var key, value []byte
			key, value, v = consumeParam(v)
			if key == nil || value == nil {
				return nil, errors.New("link: malformed param")
			}
			if k := string(key); k == "rel" {
				if links[len(links)-1].Rel == "" {
					links[len(links)-1].Rel = string(value)
				}
			} else {
				params[k] = string(value)
			}
			v = bytes.TrimLeftFunc(v, unicode.IsSpace)
		}
	}

	return links, nil
}

func isTokenChar(r rune) bool {
	return r > 0x20 && r < 0x7f && r != '"' && r != ',' && r != '=' && r != ';'
}

func isNotTokenChar(r rune) bool { return !isTokenChar(r) }

func consumeToken(v []byte) (token, rest []byte) {
	notPos := bytes.IndexFunc(v, isNotTokenChar)
	if notPos == -1 {
		return v, nil
	}
	if notPos == 0 {
		return nil, v
	}
	return v[0:notPos], v[notPos:]
}

func consumeValue(v []byte) (value, rest []byte) {
	if v[0] != '"' {
		return nil, v
	}

	rest = v[1:]
	buffer := &bytes.Buffer{}
	var nextIsLiteral bool
	for idx, r := range string(rest) {
		switch {
		case nextIsLiteral:
			buffer.WriteRune(r)
			nextIsLiteral = false
		case r == '"':
			return buffer.Bytes(), rest[idx+1:]
		case r == '\\':
			nextIsLiteral = true
		case r != '\r' && r != '\n':
			buffer.WriteRune(r)
		default:
			return nil, v
		}
	}
	return nil, v
}

func consumeParam(v []byte) (param, value, rest []byte) {
	param, rest = consumeToken(v)
	param = bytes.ToLower(param)
	if param == nil {
		return nil, nil, v
	}

	rest = bytes.TrimLeftFunc(rest, unicode.IsSpace)
	if len(rest) == 0 || rest[0] != '=' {
		return nil, nil, v
	}
	rest = rest[1:] // consume equals sign
	rest = bytes.TrimLeftFunc(rest, unicode.IsSpace)
	if len(rest) == 0 {
		return nil, nil, v
	}
	if rest[0] != '"' {
		value, rest = consumeToken(rest)
	} else {
		value, rest = consumeValue(rest)
	}
	if value == nil {
		return nil, nil, v
	}
	return param, value, rest
}
<commit_msg>Simplify range<commit_after>// Package link implements parsing and serialization of Link header values as
// defined in RFC 5988.
package link

import (
	"bytes"
	"errors"
	"sort"
	"unicode"
)

type Link struct {
	URI    string
	Rel    string
	Params map[string]string
}

// Format serializes a slice of Links into a header value. It does not currently
// implement RFC 2231 handling of non-ASCII character encoding and language
// information.
func Format(links []Link) string {
	buf := &bytes.Buffer{}
	for i, link := range links {
		if i > 0 {
			buf.Write([]byte(", "))
		}
		buf.WriteByte('<')
		buf.WriteString(link.URI)
		buf.WriteByte('>')

		writeParam(buf, "rel", link.Rel)

		keys := make([]string, 0, len(link.Params))
		for k := range link.Params {
			keys = append(keys, k)
		}
		sort.Strings(keys)

		for _, k := range keys {
			writeParam(buf, k, link.Params[k])
		}
	}

	return buf.String()
}

func writeParam(buf *bytes.Buffer, key, value string) {
	buf.Write([]byte("; "))
	buf.WriteString(key)
	buf.Write([]byte(`="`))
	buf.WriteString(value)
	buf.WriteByte('"')
}

// Parse parses a Link header value into a slice of Links. It does not currently
// implement RFC 2231 handling of non-ASCII character encoding and language
// information.
func Parse(l string) ([]Link, error) {
	v := []byte(l)
	v = bytes.TrimSpace(v)
	if len(v) == 0 {
		return nil, nil
	}

	links := make([]Link, 0, 1)
	for len(v) > 0 {
		if v[0] != '<' {
			return nil, errors.New("link: does not start with <")
		}
		lend := bytes.IndexByte(v, '>')
		if lend == -1 {
			return nil, errors.New("link: does not contain ending >")
		}

		params := make(map[string]string)
		link := Link{URI: string(v[1:lend]), Params: params}
		links = append(links, link)

		// trim off parsed url
		v = v[lend+1:]
		if len(v) == 0 {
			break
		}
		v = bytes.TrimLeftFunc(v, unicode.IsSpace)

		for len(v) > 0 {
			if v[0] != ';' && v[0] != ',' {
				return nil, errors.New(`link: expected ";" or "'", got "` + string(v[0:1]) + `"`)
			}
			var next bool
			if v[0] == ',' {
				next = true
			}
			v = bytes.TrimLeftFunc(v[1:], unicode.IsSpace)
			if next || len(v) == 0 {
				break
			}
			var key, value []byte
			key, value, v = consumeParam(v)
			if key == nil || value == nil {
				return nil, errors.New("link: malformed param")
			}
			if k := string(key); k == "rel" {
				if links[len(links)-1].Rel == "" {
					links[len(links)-1].Rel = string(value)
				}
			} else {
				params[k] = string(value)
			}
			v = bytes.TrimLeftFunc(v, unicode.IsSpace)
		}
	}

	return links, nil
}

func isTokenChar(r rune) bool {
	return r > 0x20 && r < 0x7f && r != '"' && r != ',' && r != '=' && r != ';'
}

func isNotTokenChar(r rune) bool { return !isTokenChar(r) }

func consumeToken(v []byte) (token, rest []byte) {
	notPos := bytes.IndexFunc(v, isNotTokenChar)
	if notPos == -1 {
		return v, nil
	}
	if notPos == 0 {
		return nil, v
	}
	return v[0:notPos], v[notPos:]
}

func consumeValue(v []byte) (value, rest []byte) {
	if v[0] != '"' {
		return nil, v
	}

	rest = v[1:]
	buffer := &bytes.Buffer{}
	var nextIsLiteral bool
	for idx, r := range string(rest) {
		switch {
		case nextIsLiteral:
			buffer.WriteRune(r)
			nextIsLiteral = false
		case r == '"':
			return buffer.Bytes(), rest[idx+1:]
		case r == '\\':
			nextIsLiteral = true
		case r != '\r' && r != '\n':
			buffer.WriteRune(r)
		default:
			return nil, v
		}
	}
	return nil, v
}

func consumeParam(v []byte) (param, value, rest []byte) {
	param, rest = consumeToken(v)
	param = bytes.ToLower(param)
	if param == nil {
		return nil, nil, v
	}

	rest = bytes.TrimLeftFunc(rest, unicode.IsSpace)
	if len(rest) == 0 || rest[0] != '=' {
		return nil, nil, v
	}
	rest = rest[1:] // consume equals sign
	rest = bytes.TrimLeftFunc(rest, unicode.IsSpace)
	if len(rest) == 0 {
		return nil, nil, v
	}
	if rest[0] != '"' {
		value, rest = consumeToken(rest)
	} else {
		value, rest = consumeValue(rest)
	}
	if value == nil {
		return nil, nil, v
	}
	return param, value, rest
}
<|endoftext|>


<commit_before>// Copyright © 2017 Aidan Steele <aidan.steele@glassechidna.com.au>
//
// 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 cmd

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/fatih/color"
	"github.com/glassechidna/stackit/pkg/stackit"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"io/ioutil"
	"os"
	"strings"
)

// up --stack-name stackit-test --template sample.yml --param-value DockerImage=nginx --param-value Cluster=app-cluster-Cluster-1C2I18JXK9QNM --tag MyTag=Cool

var paramValues []string
var previousParamValues []string
var tags []string
var notificationArns []string

func printOrExit(tailEvent stackit.TailStackEvent, printer stackit.TailPrinter) {
	if tailEvent.StackitError != nil {
		if awsErr, ok := tailEvent.StackitError.(awserr.Error); ok {
			color.New(color.FgRed).Fprintf(os.Stderr, "%s: %s\n", awsErr.Code(), awsErr.Message())
		} else {
			color.New(color.FgRed).Fprintln(os.Stderr, tailEvent.StackitError.Error())
		}
		os.Exit(1)
	}

	printer.PrintTailEvent(tailEvent)
}

var upCmd = &cobra.Command{
	Use:   "up",
	Short: "Bring stack up to date",
	Run: func(cmd *cobra.Command, args []string) {
		region := viper.GetString("region")
		profile := viper.GetString("profile")
		stackName := viper.GetString("stack-name")

		serviceRole := viper.GetString("service-role")
		stackPolicy := viper.GetString("stack-policy")
		template := viper.GetString("template")
		previousTemplate := viper.GetBool("previous-template")
		//noDestroy := viper.GetBool("no-destroy")
		//cancelOnExit := !viper.GetBool("no-cancel-on-exit")

		showTimestamps := !viper.GetBool("no-timestamps")
		showColor := !viper.GetBool("no-color")
		printer := stackit.NewTailPrinterWithOptions(showTimestamps, showColor)

		parsed := parseCLIInput(
			serviceRole,
			stackPolicy,
			template,
			paramValues,
			previousParamValues,
			tags,
			notificationArns,
			previousTemplate)

		events := make(chan stackit.TailStackEvent)

		sess := awsSession(profile, region)
		api := cloudformation.New(sess)
		sit := stackit.NewStackit(api, stackName)

		go func() {
			err := sit.EnsureStackReady(events)
			if err == nil {
				sit.Up(parsed, events)
			}
		}()

		for tailEvent := range events {
			printOrExit(tailEvent, printer)
		}

		if success, _ := sit.IsSuccessfulState(); !success {
			os.Exit(1)
		}

		sit.PrintOutputs()
	},
}

func keyvalSliceToMap(slice []string) map[string]string {
	theMap := map[string]string{}

	for _, paramPair := range slice {
		parts := strings.SplitN(paramPair, "=", 2)
		name, value := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])
		theMap[name] = value
	}

	return theMap
}

func parseCLIInput(
	serviceRole,
	stackPolicy,
	template string,
	cliParamValues,
	previousParamValues,
	tags,
	notificationArns []string,
	previousTemplate bool) stackit.StackitUpInput {
	input := stackit.StackitUpInput{
		PopulateMissing: true,
	}

	if len(serviceRole) > 0 {
		input.RoleARN = serviceRole
	}

	if len(stackPolicy) > 0 {
		policyBody, err := ioutil.ReadFile(stackPolicy)
		if err != nil {

		} else {
			input.StackPolicyBody = string(policyBody)
		}
	}

	if len(template) > 0 {
		templateBody, err := ioutil.ReadFile(template)
		if err != nil {

		} else {
			input.TemplateBody = string(templateBody)
		}
	}

	input.PreviousTemplate = previousTemplate

	paramMap := keyvalSliceToMap(viper.GetStringSlice("parameters"))
	for key, val := range keyvalSliceToMap(cliParamValues) {
		paramMap[key] = val
	}

	params := []*cloudformation.Parameter{}
	for name, value := range paramMap {
		params = append(params, &cloudformation.Parameter{
			ParameterKey:   aws.String(name),
			ParameterValue: aws.String(value),
		})
	}

	for _, param := range previousParamValues {
		params = append(params, &cloudformation.Parameter{
			ParameterKey:     aws.String(param),
			UsePreviousValue: aws.Bool(true),
		})
	}

	input.Parameters = params
	input.NotificationARNs = notificationArns

	if len(tags) > 0 {
		input.Tags = keyvalSliceToMap(tags)
	}

	return input
}

func awsSession(profile, region string) *session.Session {
	sessOpts := session.Options{
		SharedConfigState:       session.SharedConfigEnable,
		AssumeRoleTokenProvider: stscreds.StdinTokenProvider,
	}

	if len(profile) > 0 {
		sessOpts.Profile = profile
	}

	if len(os.Getenv("STACKIT_AWS_VERBOSE")) > 0 {
		sessOpts.Config.LogLevel = aws.LogLevel(aws.LogDebugWithHTTPBody)
	}

	sess, _ := session.NewSessionWithOptions(sessOpts)
	config := aws.NewConfig()

	if len(region) > 0 {
		config.Region = aws.String(region)
		sess.Config = config
	}

	return sess
}

func init() {
	RootCmd.AddCommand(upCmd)

	upCmd.PersistentFlags().String("service-role", "", "")
	upCmd.PersistentFlags().String("stack-policy", "", "")
	upCmd.PersistentFlags().String("template", "", "")
	upCmd.PersistentFlags().StringArrayVar(&paramValues, "param-value", []string{}, "")
	upCmd.PersistentFlags().StringArrayVar(&previousParamValues, "previous-param-value", []string{}, "")
	upCmd.PersistentFlags().StringArrayVar(&tags, "tag", []string{}, "")
	upCmd.PersistentFlags().StringArrayVar(&notificationArns, "notification-arn", []string{}, "")
	upCmd.PersistentFlags().Bool("previous-template", false, "")
	upCmd.PersistentFlags().Bool("no-destroy", false, "")
	upCmd.PersistentFlags().Bool("no-cancel-on-exit", false, "")
	upCmd.PersistentFlags().Bool("no-timestamps", false, "")
	upCmd.PersistentFlags().Bool("no-color", false, "")

	viper.BindPFlags(upCmd.PersistentFlags())
}
<commit_msg>Bug fix: don’t break when region passed in by command line<commit_after>// Copyright © 2017 Aidan Steele <aidan.steele@glassechidna.com.au>
//
// 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 cmd

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/fatih/color"
	"github.com/glassechidna/stackit/pkg/stackit"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"io/ioutil"
	"os"
	"strings"
)

// up --stack-name stackit-test --template sample.yml --param-value DockerImage=nginx --param-value Cluster=app-cluster-Cluster-1C2I18JXK9QNM --tag MyTag=Cool

var paramValues []string
var previousParamValues []string
var tags []string
var notificationArns []string

func printOrExit(tailEvent stackit.TailStackEvent, printer stackit.TailPrinter) {
	if tailEvent.StackitError != nil {
		if awsErr, ok := tailEvent.StackitError.(awserr.Error); ok {
			color.New(color.FgRed).Fprintf(os.Stderr, "%s: %s\n", awsErr.Code(), awsErr.Message())
		} else {
			color.New(color.FgRed).Fprintln(os.Stderr, tailEvent.StackitError.Error())
		}
		os.Exit(1)
	}

	printer.PrintTailEvent(tailEvent)
}

var upCmd = &cobra.Command{
	Use:   "up",
	Short: "Bring stack up to date",
	Run: func(cmd *cobra.Command, args []string) {
		region := viper.GetString("region")
		profile := viper.GetString("profile")
		stackName := viper.GetString("stack-name")

		serviceRole := viper.GetString("service-role")
		stackPolicy := viper.GetString("stack-policy")
		template := viper.GetString("template")
		previousTemplate := viper.GetBool("previous-template")
		//noDestroy := viper.GetBool("no-destroy")
		//cancelOnExit := !viper.GetBool("no-cancel-on-exit")

		showTimestamps := !viper.GetBool("no-timestamps")
		showColor := !viper.GetBool("no-color")
		printer := stackit.NewTailPrinterWithOptions(showTimestamps, showColor)

		parsed := parseCLIInput(
			serviceRole,
			stackPolicy,
			template,
			paramValues,
			previousParamValues,
			tags,
			notificationArns,
			previousTemplate)

		events := make(chan stackit.TailStackEvent)

		sess := awsSession(profile, region)
		api := cloudformation.New(sess)
		sit := stackit.NewStackit(api, stackName)

		go func() {
			err := sit.EnsureStackReady(events)
			if err == nil {
				sit.Up(parsed, events)
			}
		}()

		for tailEvent := range events {
			printOrExit(tailEvent, printer)
		}

		if success, _ := sit.IsSuccessfulState(); !success {
			os.Exit(1)
		}

		sit.PrintOutputs()
	},
}

func keyvalSliceToMap(slice []string) map[string]string {
	theMap := map[string]string{}

	for _, paramPair := range slice {
		parts := strings.SplitN(paramPair, "=", 2)
		name, value := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])
		theMap[name] = value
	}

	return theMap
}

func parseCLIInput(
	serviceRole,
	stackPolicy,
	template string,
	cliParamValues,
	previousParamValues,
	tags,
	notificationArns []string,
	previousTemplate bool) stackit.StackitUpInput {
	input := stackit.StackitUpInput{
		PopulateMissing: true,
	}

	if len(serviceRole) > 0 {
		input.RoleARN = serviceRole
	}

	if len(stackPolicy) > 0 {
		policyBody, err := ioutil.ReadFile(stackPolicy)
		if err != nil {

		} else {
			input.StackPolicyBody = string(policyBody)
		}
	}

	if len(template) > 0 {
		templateBody, err := ioutil.ReadFile(template)
		if err != nil {

		} else {
			input.TemplateBody = string(templateBody)
		}
	}

	input.PreviousTemplate = previousTemplate

	paramMap := keyvalSliceToMap(viper.GetStringSlice("parameters"))
	for key, val := range keyvalSliceToMap(cliParamValues) {
		paramMap[key] = val
	}

	params := []*cloudformation.Parameter{}
	for name, value := range paramMap {
		params = append(params, &cloudformation.Parameter{
			ParameterKey:   aws.String(name),
			ParameterValue: aws.String(value),
		})
	}

	for _, param := range previousParamValues {
		params = append(params, &cloudformation.Parameter{
			ParameterKey:     aws.String(param),
			UsePreviousValue: aws.Bool(true),
		})
	}

	input.Parameters = params
	input.NotificationARNs = notificationArns

	if len(tags) > 0 {
		input.Tags = keyvalSliceToMap(tags)
	}

	return input
}

func awsSession(profile, region string) *session.Session {
	sessOpts := session.Options{
		SharedConfigState:       session.SharedConfigEnable,
		AssumeRoleTokenProvider: stscreds.StdinTokenProvider,
	}

	if len(profile) > 0 {
		sessOpts.Profile = profile
	}

	if len(region) > 0 {
		sessOpts.Config.Region = aws.String(region)
	}

	if len(os.Getenv("STACKIT_AWS_VERBOSE")) > 0 {
		sessOpts.Config.LogLevel = aws.LogLevel(aws.LogDebugWithHTTPBody)
	}

	sess, _ := session.NewSessionWithOptions(sessOpts)
	return sess
}

func init() {
	RootCmd.AddCommand(upCmd)

	upCmd.PersistentFlags().String("service-role", "", "")
	upCmd.PersistentFlags().String("stack-policy", "", "")
	upCmd.PersistentFlags().String("template", "", "")
	upCmd.PersistentFlags().StringArrayVar(&paramValues, "param-value", []string{}, "")
	upCmd.PersistentFlags().StringArrayVar(&previousParamValues, "previous-param-value", []string{}, "")
	upCmd.PersistentFlags().StringArrayVar(&tags, "tag", []string{}, "")
	upCmd.PersistentFlags().StringArrayVar(&notificationArns, "notification-arn", []string{}, "")
	upCmd.PersistentFlags().Bool("previous-template", false, "")
	upCmd.PersistentFlags().Bool("no-destroy", false, "")
	upCmd.PersistentFlags().Bool("no-cancel-on-exit", false, "")
	upCmd.PersistentFlags().Bool("no-timestamps", false, "")
	upCmd.PersistentFlags().Bool("no-color", false, "")

	viper.BindPFlags(upCmd.PersistentFlags())
}
<|endoftext|>


<commit_before>package api

import (
	"net/http"
	"net/http/httputil"
	"net/url"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/middleware"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/util"
)

func NewReverseProxy(ds *m.DataSource, proxyPath string) *httputil.ReverseProxy {
	target, _ := url.Parse(ds.Url)

	director := func(req *http.Request) {
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host

		reqQueryVals := req.URL.Query()

		if ds.Type == m.DS_INFLUXDB {
			req.URL.Path = util.JoinUrlFragments(target.Path, "db/"+ds.Database+"/"+proxyPath)
			reqQueryVals.Add("u", ds.User)
			reqQueryVals.Add("p", ds.Password)
			req.URL.RawQuery = reqQueryVals.Encode()
		} else {
			req.URL.Path = util.JoinUrlFragments(target.Path, proxyPath)
		}
	}

	return &httputil.ReverseProxy{Director: director}
}

// TODO: need to cache datasources
func ProxyDataSourceRequest(c *middleware.Context) {
	id := c.ParamsInt64(":id")

	query := m.GetDataSourceByIdQuery{
		Id:        id,
		AccountId: c.AccountId,
	}

	err := bus.Dispatch(&query)
	if err != nil {
		c.JsonApiErr(500, "Unable to load datasource meta data", err)
	}

	proxyPath := c.Params("*")
	proxy := NewReverseProxy(&query.Result, proxyPath)
	proxy.ServeHTTP(c.RW(), c.Req.Request)
}
<commit_msg>Fixed req.Host in datasource proxy, Fixes #1478<commit_after>package api

import (
	"net/http"
	"net/http/httputil"
	"net/url"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/middleware"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/util"
)

func NewReverseProxy(ds *m.DataSource, proxyPath string) *httputil.ReverseProxy {
	target, _ := url.Parse(ds.Url)

	director := func(req *http.Request) {
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		req.Host = target.Host

		reqQueryVals := req.URL.Query()

		if ds.Type == m.DS_INFLUXDB {
			req.URL.Path = util.JoinUrlFragments(target.Path, "db/"+ds.Database+"/"+proxyPath)
			reqQueryVals.Add("u", ds.User)
			reqQueryVals.Add("p", ds.Password)
			req.URL.RawQuery = reqQueryVals.Encode()
		} else {
			req.URL.Path = util.JoinUrlFragments(target.Path, proxyPath)
		}
	}

	return &httputil.ReverseProxy{Director: director}
}

// TODO: need to cache datasources
func ProxyDataSourceRequest(c *middleware.Context) {
	id := c.ParamsInt64(":id")

	query := m.GetDataSourceByIdQuery{
		Id:        id,
		AccountId: c.AccountId,
	}

	err := bus.Dispatch(&query)
	if err != nil {
		c.JsonApiErr(500, "Unable to load datasource meta data", err)
	}

	proxyPath := c.Params("*")
	proxy := NewReverseProxy(&query.Result, proxyPath)
	proxy.ServeHTTP(c.RW(), c.Req.Request)
}
<|endoftext|>


<commit_before>package async

import (
  "container/list"
  // "fmt"
  "sync"
)

/*
  Used to contain the Routine functions to be processed
*/
type List struct {
  *list.List

  Wait sync.WaitGroup
}

/*
  Create a new list
*/
func New() *List {
  return &List{
    List: list.New(),
  }
}

/*
  Add a Routine function to the current list
*/
func (l *List) Add(routine Routine) (*List, *list.Element) {
  element := l.PushBack(routine)
  return l, element
}

/*
  Add multiple Routine functions to the current list
*/
func (l *List) Multiple(routines ...Routine) (*List, []*list.Element) {
  var (
    elements = make([]*list.Element, 0)
  )

  for i := 0; i < len(routines); i++ {
    _, e := l.Add(routines[i])
    elements = append(elements, e)
  }

  return l, elements
}

/*
  Remove an element from the current list
*/
func (l *List) Remove(element *list.Element) (*List, Routine) {
  routine := l.List.Remove(element).(Routine)
  return l, routine
}
<commit_msg>Removed comment in list.go<commit_after>package async

import (
  "container/list"
  "sync"
)

/*
  Used to contain the Routine functions to be processed
*/
type List struct {
  *list.List

  Wait sync.WaitGroup
}

/*
  Create a new list
*/
func New() *List {
  return &List{
    List: list.New(),
  }
}

/*
  Add a Routine function to the current list
*/
func (l *List) Add(routine Routine) (*List, *list.Element) {
  element := l.PushBack(routine)
  return l, element
}

/*
  Add multiple Routine functions to the current list
*/
func (l *List) Multiple(routines ...Routine) (*List, []*list.Element) {
  var (
    elements = make([]*list.Element, 0)
  )

  for i := 0; i < len(routines); i++ {
    _, e := l.Add(routines[i])
    elements = append(elements, e)
  }

  return l, elements
}

/*
  Remove an element from the current list
*/
func (l *List) Remove(element *list.Element) (*List, Routine) {
  routine := l.List.Remove(element).(Routine)
  return l, routine
}
<|endoftext|>