<commit_before>package shellwords

import (
	"os"
	"reflect"
	"testing"
)

var testcases = []struct {
	line     string
	expected []string
}{
	{`var --bar=baz`, []string{`var`, `--bar=baz`}},
	{`var --bar="baz"`, []string{`var`, `--bar=baz`}},
	{`var "--bar=baz"`, []string{`var`, `--bar=baz`}},
	{`var "--bar='baz'"`, []string{`var`, `--bar='baz'`}},
	{"var --bar=`baz`", []string{`var`, "--bar=`baz`"}},
	{`var "--bar=\"baz'"`, []string{`var`, `--bar="baz'`}},
	{`var "--bar baz"`, []string{`var`, `--bar baz`}},
	{`var --"bar baz"`, []string{`var`, `--bar baz`}},
	{`var  --"bar baz"`, []string{`var`, `--bar baz`}},
}

func TestSimple(t *testing.T) {
	for _, testcase := range testcases {
		args, err := Parse(testcase.line)
		if err != nil {
			t.Fatalf(err.Error())
		}
		if !reflect.DeepEqual(args, testcase.expected) {
			t.Fatalf("Expected %v, but %v:", testcase.expected, args)
		}
	}
}

func TestError(t *testing.T) {
	_, err := Parse("foo '")
	if err == nil {
		t.Fatalf("Should be an error")
	}
	_, err = Parse(`foo "`)
	if err == nil {
		t.Fatalf("Should be an error")
	}

	_, err = Parse("foo `")
	if err == nil {
		t.Fatalf("Should be an error")
	}
}

func TestBacktick(t *testing.T) {
	goversion, err := shellRun("go version")
	if err != nil {
		t.Fatalf(err.Error())
	}

	parser := NewParser()
	parser.ParseBacktick = true
	args, err := parser.Parse("echo `go version`")
	if err != nil {
		t.Fatalf(err.Error())
	}
	expected := []string{"echo", goversion}
	if !reflect.DeepEqual(args, expected) {
		t.Fatalf("Expected %v, but %v:", expected, args)
	}
}

func TestEnv(t *testing.T) {
	os.Setenv("FOO", "bar")

	parser := NewParser()
	parser.ParseEnv = true
	args, err := parser.Parse("echo $FOO")
	if err != nil {
		t.Fatalf(err.Error())
	}
	expected := []string{"echo", "bar"}
	if !reflect.DeepEqual(args, expected) {
		t.Fatalf("Expected %v, but %v:", expected, args)
	}
}

func TestNoEnv(t *testing.T) {
	parser := NewParser()
	parser.ParseEnv = true
	args, err := parser.Parse("echo $BAR")
	if err != nil {
		t.Fatalf(err.Error())
	}
	expected := []string{"echo", ""}
	if !reflect.DeepEqual(args, expected) {
		t.Fatalf("Expected %v, but %v:", expected, args)
	}
}

func TestDupEnv(t *testing.T) {
	os.Setenv("FOO", "bar")
	os.Setenv("FOO_BAR", "baz")

	parser := NewParser()
	parser.ParseEnv = true
	args, err := parser.Parse("echo $$FOO$")
	if err != nil {
		t.Fatalf(err.Error())
	}
	expected := []string{"echo", "$bar$"}
	if !reflect.DeepEqual(args, expected) {
		t.Fatalf("Expected %v, but %v:", expected, args)
	}

	args, err = parser.Parse("echo $${FOO_BAR}$")
	if err != nil {
		t.Fatalf(err.Error())
	}
	expected = []string{"echo", "$baz$"}
	if !reflect.DeepEqual(args, expected) {
		t.Fatalf("Expected %v, but %v:", expected, args)
	}
}

<commit_msg>Shell error<commit_after>package shellwords

import (
	"os"
	"reflect"
	"testing"
)

var testcases = []struct {
	line     string
	expected []string
}{
	{`var --bar=baz`, []string{`var`, `--bar=baz`}},
	{`var --bar="baz"`, []string{`var`, `--bar=baz`}},
	{`var "--bar=baz"`, []string{`var`, `--bar=baz`}},
	{`var "--bar='baz'"`, []string{`var`, `--bar='baz'`}},
	{"var --bar=`baz`", []string{`var`, "--bar=`baz`"}},
	{`var "--bar=\"baz'"`, []string{`var`, `--bar="baz'`}},
	{`var "--bar baz"`, []string{`var`, `--bar baz`}},
	{`var --"bar baz"`, []string{`var`, `--bar baz`}},
	{`var  --"bar baz"`, []string{`var`, `--bar baz`}},
}

func TestSimple(t *testing.T) {
	for _, testcase := range testcases {
		args, err := Parse(testcase.line)
		if err != nil {
			t.Fatalf(err.Error())
		}
		if !reflect.DeepEqual(args, testcase.expected) {
			t.Fatalf("Expected %v, but %v:", testcase.expected, args)
		}
	}
}

func TestError(t *testing.T) {
	_, err := Parse("foo '")
	if err == nil {
		t.Fatalf("Should be an error")
	}
	_, err = Parse(`foo "`)
	if err == nil {
		t.Fatalf("Should be an error")
	}

	_, err = Parse("foo `")
	if err == nil {
		t.Fatalf("Should be an error")
	}
}

func TestBacktick(t *testing.T) {
	goversion, err := shellRun("go version")
	if err != nil {
		t.Fatalf(err.Error())
	}

	parser := NewParser()
	parser.ParseBacktick = true
	args, err := parser.Parse("echo `go version`")
	if err != nil {
		t.Fatalf(err.Error())
	}
	expected := []string{"echo", goversion}
	if !reflect.DeepEqual(args, expected) {
		t.Fatalf("Expected %v, but %v:", expected, args)
	}
}

func TestBacktickError(t *testing.T) {
	parser := NewParser()
	parser.ParseBacktick = true
	_, err := parser.Parse("echo `go Version`")
	if err == nil {
		t.Fatalf("Should be an error")
	}
}

func TestEnv(t *testing.T) {
	os.Setenv("FOO", "bar")

	parser := NewParser()
	parser.ParseEnv = true
	args, err := parser.Parse("echo $FOO")
	if err != nil {
		t.Fatalf(err.Error())
	}
	expected := []string{"echo", "bar"}
	if !reflect.DeepEqual(args, expected) {
		t.Fatalf("Expected %v, but %v:", expected, args)
	}
}

func TestNoEnv(t *testing.T) {
	parser := NewParser()
	parser.ParseEnv = true
	args, err := parser.Parse("echo $BAR")
	if err != nil {
		t.Fatalf(err.Error())
	}
	expected := []string{"echo", ""}
	if !reflect.DeepEqual(args, expected) {
		t.Fatalf("Expected %v, but %v:", expected, args)
	}
}

func TestDupEnv(t *testing.T) {
	os.Setenv("FOO", "bar")
	os.Setenv("FOO_BAR", "baz")

	parser := NewParser()
	parser.ParseEnv = true
	args, err := parser.Parse("echo $$FOO$")
	if err != nil {
		t.Fatalf(err.Error())
	}
	expected := []string{"echo", "$bar$"}
	if !reflect.DeepEqual(args, expected) {
		t.Fatalf("Expected %v, but %v:", expected, args)
	}

	args, err = parser.Parse("echo $${FOO_BAR}$")
	if err != nil {
		t.Fatalf(err.Error())
	}
	expected = []string{"echo", "$baz$"}
	if !reflect.DeepEqual(args, expected) {
		t.Fatalf("Expected %v, but %v:", expected, args)
	}
}

<|endoftext|>


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

// Package font defines an interface for font faces, for drawing text on an
// image.
//
// Other packages provide font face implementations. For example, a truetype
// package would provide one based on .ttf font files.
package font

// TODO: move this from golang.org/x/exp to golang.org/x/image ??

import (
	"image"
	"image/draw"
	"io"

	"golang.org/x/image/math/fixed"
)

// TODO: who is responsible for caches (glyph images, glyph indices, kerns)?
// The Drawer or the Face?

// Face is a font face. Its glyphs are often derived from a font file, such as
// "Comic_Sans_MS.ttf", but a face has a specific size, style, weight and
// hinting. For example, the 12pt and 18pt versions of Comic Sans are two
// different faces, even if derived from the same font file.
//
// A Face is not safe for concurrent use by multiple goroutines, as its methods
// may re-use implementation-specific caches and mask image buffers.
//
// To create a Face, look to other packages that implement specific font file
// formats.
type Face interface {
	io.Closer

	// Glyph returns the draw.DrawMask parameters (dr, mask, maskp) to draw r's
	// glyph at the sub-pixel destination location dot. It also returns the new
	// dot after adding the glyph's advance width. It returns !ok if the face
	// does not contain a glyph for r.
	//
	// The contents of the mask image returned by one Glyph call may change
	// after the next Glyph call. Callers that want to cache the mask must make
	// a copy.
	Glyph(dot fixed.Point26_6, r rune) (
		newDot fixed.Point26_6, dr image.Rectangle, mask image.Image, maskp image.Point, ok bool)

	// Kern returns the horizontal adjustment for the kerning pair (r0, r1). A
	// positive kern means to move the glyphs further apart.
	Kern(r0, r1 rune) fixed.Int26_6

	// TODO: per-font and per-glyph Metrics.
	// TODO: ColoredGlyph for various emoji?
	// TODO: Ligatures? Shaping?
}

// TODO: Drawer.Layout or Drawer.Measure methods to measure text without
// drawing?

// Drawer draws text on a destination image.
//
// A Drawer is not safe for concurrent use by multiple goroutines, since its
// Face is not.
type Drawer struct {
	// Dst is the destination image.
	Dst draw.Image
	// Src is the source image.
	Src image.Image
	// Face provides the glyph mask images.
	Face Face
	// Dot is the baseline location to draw the next glyph. The majority of the
	// affected pixels will be above and to the right of the dot, but some may
	// be below or to the left. For example, drawing a 'j' in an italic face
	// may affect pixels below and to the left of the dot.
	Dot fixed.Point26_6

	// TODO: Clip image.Image?
	// TODO: SrcP image.Point for Src images other than *image.Uniform? How
	// does it get updated during DrawString?
}

// TODO: should DrawString return the last rune drawn, so the next DrawString
// call can kern beforehand? Or should that be the responsibility of the caller
// if they really want to do that, since they have to explicitly shift d.Dot
// anyway?
//
// In general, we'd have a DrawBytes([]byte) and DrawRuneReader(io.RuneReader)
// and the last case can't assume that you can rewind the stream.
//
// TODO: how does this work with line breaking: drawing text up until a
// vertical line? Should DrawString return the number of runes drawn?

// DrawString draws s at the dot and advances the dot's location.
func (d *Drawer) DrawString(s string) {
	var prevC rune
	for i, c := range s {
		if i != 0 {
			d.Dot.X += d.Face.Kern(prevC, c)
		}
		newDot, dr, mask, maskp, ok := d.Face.Glyph(d.Dot, c)
		if !ok {
			// TODO: is falling back on the U+FFFD glyph the responsibility of
			// the Drawer or the Face?
			continue
		}
		draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over)
		d.Dot, prevC = newDot, c
	}
}
<commit_msg>shiny/font: add Hinting, Stretch, Style and Weight option types.<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package font defines an interface for font faces, for drawing text on an
// image.
//
// Other packages provide font face implementations. For example, a truetype
// package would provide one based on .ttf font files.
package font

// TODO: move this from golang.org/x/exp to golang.org/x/image ??

import (
	"image"
	"image/draw"
	"io"

	"golang.org/x/image/math/fixed"
)

// TODO: who is responsible for caches (glyph images, glyph indices, kerns)?
// The Drawer or the Face?

// Face is a font face. Its glyphs are often derived from a font file, such as
// "Comic_Sans_MS.ttf", but a face has a specific size, style, weight and
// hinting. For example, the 12pt and 18pt versions of Comic Sans are two
// different faces, even if derived from the same font file.
//
// A Face is not safe for concurrent use by multiple goroutines, as its methods
// may re-use implementation-specific caches and mask image buffers.
//
// To create a Face, look to other packages that implement specific font file
// formats.
type Face interface {
	io.Closer

	// Glyph returns the draw.DrawMask parameters (dr, mask, maskp) to draw r's
	// glyph at the sub-pixel destination location dot. It also returns the new
	// dot after adding the glyph's advance width. It returns !ok if the face
	// does not contain a glyph for r.
	//
	// The contents of the mask image returned by one Glyph call may change
	// after the next Glyph call. Callers that want to cache the mask must make
	// a copy.
	Glyph(dot fixed.Point26_6, r rune) (
		newDot fixed.Point26_6, dr image.Rectangle, mask image.Image, maskp image.Point, ok bool)

	// Kern returns the horizontal adjustment for the kerning pair (r0, r1). A
	// positive kern means to move the glyphs further apart.
	Kern(r0, r1 rune) fixed.Int26_6

	// TODO: per-font and per-glyph Metrics.
	// TODO: ColoredGlyph for various emoji?
	// TODO: Ligatures? Shaping?
}

// TODO: Drawer.Layout or Drawer.Measure methods to measure text without
// drawing?

// Drawer draws text on a destination image.
//
// A Drawer is not safe for concurrent use by multiple goroutines, since its
// Face is not.
type Drawer struct {
	// Dst is the destination image.
	Dst draw.Image
	// Src is the source image.
	Src image.Image
	// Face provides the glyph mask images.
	Face Face
	// Dot is the baseline location to draw the next glyph. The majority of the
	// affected pixels will be above and to the right of the dot, but some may
	// be below or to the left. For example, drawing a 'j' in an italic face
	// may affect pixels below and to the left of the dot.
	Dot fixed.Point26_6

	// TODO: Clip image.Image?
	// TODO: SrcP image.Point for Src images other than *image.Uniform? How
	// does it get updated during DrawString?
}

// TODO: should DrawString return the last rune drawn, so the next DrawString
// call can kern beforehand? Or should that be the responsibility of the caller
// if they really want to do that, since they have to explicitly shift d.Dot
// anyway?
//
// In general, we'd have a DrawBytes([]byte) and DrawRuneReader(io.RuneReader)
// and the last case can't assume that you can rewind the stream.
//
// TODO: how does this work with line breaking: drawing text up until a
// vertical line? Should DrawString return the number of runes drawn?

// DrawString draws s at the dot and advances the dot's location.
func (d *Drawer) DrawString(s string) {
	var prevC rune
	for i, c := range s {
		if i != 0 {
			d.Dot.X += d.Face.Kern(prevC, c)
		}
		newDot, dr, mask, maskp, ok := d.Face.Glyph(d.Dot, c)
		if !ok {
			// TODO: is falling back on the U+FFFD glyph the responsibility of
			// the Drawer or the Face?
			continue
		}
		draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over)
		d.Dot, prevC = newDot, c
	}
}

// Hinting selects how to quantize a vector font's glyph nodes.
//
// Not all fonts support hinting.
type Hinting int

const (
	HintingNone Hinting = iota
	HintingVertical
	HintingFull
)

// Stretch selects a normal, condensed, or expanded face.
//
// Not all fonts support stretches.
type Stretch int

const (
	StretchUltraCondensed Stretch = -4
	StretchExtraCondensed Stretch = -3
	StretchCondensed      Stretch = -2
	StretchSemiCondensed  Stretch = -1
	StretchNormal         Stretch = +0
	StretchSemiExpanded   Stretch = +1
	StretchExpanded       Stretch = +2
	StretchExtraExpanded  Stretch = +3
	StretchUltraExpanded  Stretch = +4
)

// Style selects a normal, italic, or oblique face.
//
// Not all fonts support styles.
type Style int

const (
	StyleNormal Style = iota
	StyleItalic
	StyleOblique
)

// Weight selects a normal, light or bold face.
//
// Not all fonts support weights.
type Weight int

const (
	WeightThin       Weight = 100
	WeightExtraLight Weight = 200
	WeightLight      Weight = 300
	WeightNormal     Weight = 400
	WeightMedium     Weight = 500
	WeightSemiBold   Weight = 600
	WeightBold       Weight = 700
	WeightExtraBold  Weight = 800
	WeightBlack      Weight = 900
)
<|endoftext|>


<commit_before>package main

import "github.com/hopkinsth/lambda-phage/Godeps/_workspace/src/gopkg.in/yaml.v2"
import "github.com/hopkinsth/lambda-phage/Godeps/_workspace/src/github.com/spf13/cobra"
import "github.com/hopkinsth/lambda-phage/Godeps/_workspace/src/github.com/peterh/liner"
import "github.com/hopkinsth/lambda-phage/Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/iam"
import "github.com/hopkinsth/lambda-phage/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws"
import "github.com/hopkinsth/lambda-phage/Godeps/_workspace/src/github.com/tj/go-debug"
import "strconv"
import "strings"
import "fmt"
import "io/ioutil"
import "os"

func init() {
	initCmd := &cobra.Command{
		Use:   "init",
		Short: "initializes a config for your function",
		Run:   initPhage,
	}

	cmds = append(cmds, initCmd)
}

type prompt struct {
	text           string
	def            string
	required       bool
	stringStore    **string
	stringSetStore *[]*string
	intStore       **int64
	funcStore      func(string)
	completer      func(string) []string
}

func newPrompt() *prompt {
	return new(prompt)
}

func (p *prompt) withCompleter(f liner.Completer) *prompt {
	p.completer = f
	return p
}

func (p *prompt) isRequired() *prompt {
	p.required = true
	return p
}

func (p *prompt) setDef(d string) *prompt {
	p.def = d
	return p
}

func (p *prompt) setText(t string) *prompt {
	p.text = t
	return p
}

func (p *prompt) withString(s **string) *prompt {
	p.stringStore = s
	return p
}

func (p *prompt) withStringSet(s *[]*string) *prompt {
	p.stringSetStore = s
	return p
}

func (p *prompt) withInt(s **int64) *prompt {
	p.intStore = s
	return p
}

func (p *prompt) withFunc(s func(string)) *prompt {
	p.funcStore = s
	return p
}

// helps you build a config file
func initPhage(c *cobra.Command, _ []string) {
	l := liner.NewLiner()
	l.SetCtrlCAborts(true)
	fmt.Println(`
		HELLO AND WELCOME

		This command will help you set up your code for deployment to lambda!
		Please answer the prompts as they appear below:
	`)

	//reqMsg := "Sorry, that field is required. Try again."

	// set this callback we can use to call all the stuff
	var realCompleter liner.Completer
	l.SetCompleter(func(line string) []string {
		if realCompleter != nil {
			return realCompleter(line)
		}
		return nil
	})

	cfg := new(Config)
	cfg.IamRole = new(IamRole)
	cfg.Location = new(Location)
	prompts := getPrompts(cfg)

	for _, cPrompt := range prompts {
		p := cPrompt
		text := p.text
		if p.def != "" {
			text += " [" + p.def + "]"
		}

		text += ": "

		realCompleter = nil
		if p.completer != nil {
			realCompleter = p.completer
		}

		if s, err := l.Prompt(text); err == nil {
			input := s
			hasInput := input != ""
			if p.stringStore != nil {
				if hasInput {
					*p.stringStore = &input
				} else {
					*p.stringStore = &p.def
				}

			} else if p.stringSetStore != nil {
				var splitMe string
				if hasInput {
					splitMe = input
				} else {
					splitMe = p.def
				}

				spl := strings.Split(splitMe, ",")
				pspl := make([]*string, len(spl))
				for i, v := range spl {
					// we need to set the value
					// in a variable local to this block
					// because the pointed-to value in
					// `v` will change on the next
					// loop iteration
					realVal := v
					pspl[i] = &realVal
				}

				*p.stringSetStore = pspl

			} else if p.intStore != nil {
				var tParse string
				if hasInput {
					tParse = input
				} else {
					tParse = p.def
				}

				i, _ := strconv.ParseInt(tParse, 10, 64)
				*p.intStore = &i
			} else if p.funcStore != nil {
				// just call the function, man
				p.funcStore(s)
			}
		} else if err == liner.ErrPromptAborted {
			fmt.Println("Aborted")
			return
		} else {
			fmt.Println("Error reading line: ", err)
			return
		}
	}

	l.Close()

	d, err := yaml.Marshal(cfg)
	if err != nil {
		panic(err)
	}

	err = ioutil.WriteFile("l-p.yml", d, os.FileMode(0644))
	if err != nil {
		panic(err)
	}
}

// returns all the prompts needed for the `init` command
func getPrompts(cfg *Config) []*prompt {
	wd, _ := os.Getwd()
	st, _ := os.Stat(wd)

	iamRoles, roleMap := getIamRoles()

	return []*prompt{
		newPrompt().
			withString(&cfg.Name).
			isRequired().
			setText("Enter a project name").
			setDef(st.Name()),
		newPrompt().
			withString(&cfg.Description).
			setText("Enter a project description if you'd like").
			setDef(""),
		newPrompt().
			withString(&cfg.Archive).
			setText("Enter a archive name if you'd like").
			setDef(st.Name() + ".zip"),
		newPrompt().
			withString(&cfg.Runtime).
			isRequired().
			setText("What runtime are you using: nodejs, java8, or python 2.7?").
			setDef("nodejs"),
		newPrompt().
			withString(&cfg.EntryPoint).
			isRequired().
			setText("Enter an entry point or handler name").
			setDef("index.handler"),
		newPrompt().
			withInt(&cfg.MemorySize).
			setText("Enter memory size").
			setDef("128"),
		newPrompt().
			withInt(&cfg.Timeout).
			setText("Enter timeout").
			setDef("5"),
		newPrompt().
			withStringSet(&cfg.Regions).
			setText("Enter AWS regions where this function will run").
			setDef("us-east-1"),
		newPrompt().
			withFunc(
			func(s string) {
				// if this looks like an ARN,
				// we'll assume it is... for now
				if strings.Index(s, "arn:aws:iam::") == 0 {
					cfg.IamRole.Arn = &s
				} else {
					// check to see if this name is inside the role map
					if arn, ok := roleMap[s]; ok {
						cfg.IamRole.Arn = arn
					} else {
						// if not in role map, set the name
						cfg.IamRole.Name = &s
					}
				}
			},
		).
			isRequired().
			setText("Enter IAM role name").
			setDef("").
			withCompleter(
			func(l string) []string {
				c := make([]string, 0)
				for _, role := range iamRoles {
					if strings.HasPrefix(*role.Name, l) {
						c = append(c, *role.Name)
					}
				}

				return c
			},
		),
	}
}

// pulls all the IAM roles from your account
func getIamRoles() ([]*IamRole, map[string]*string) {
	debug := debug.Debug("core.getIamRoles")
	i := iam.New(nil)
	r, err := i.ListRoles(&iam.ListRolesInput{
		// try loading up to 1000 roles now
		MaxItems: aws.Int64(1000),
	})

	if err != nil {
		debug("getting IAM roles failed! maybe you don't have permission to do that?")
		return []*IamRole{}, map[string]*string{}
	}

	roles := make([]*IamRole, len(r.Roles))
	roleMap := make(map[string]*string)
	for i, r := range r.Roles {
		roles[i] = &IamRole{
			Arn:  r.Arn,
			Name: r.RoleName,
		}
		roleMap[*r.RoleName] = r.Arn
	}

	return roles, roleMap
}
<commit_msg>add completion for runtime<commit_after>package main

import "github.com/hopkinsth/lambda-phage/Godeps/_workspace/src/gopkg.in/yaml.v2"
import "github.com/hopkinsth/lambda-phage/Godeps/_workspace/src/github.com/spf13/cobra"
import "github.com/hopkinsth/lambda-phage/Godeps/_workspace/src/github.com/peterh/liner"
import "github.com/hopkinsth/lambda-phage/Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/iam"
import "github.com/hopkinsth/lambda-phage/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws"
import "github.com/hopkinsth/lambda-phage/Godeps/_workspace/src/github.com/tj/go-debug"
import "strconv"
import "strings"
import "fmt"
import "io/ioutil"
import "os"

func init() {
	initCmd := &cobra.Command{
		Use:   "init",
		Short: "initializes a config for your function",
		Run:   initPhage,
	}

	cmds = append(cmds, initCmd)
}

type prompt struct {
	text           string
	def            string
	required       bool
	stringStore    **string
	stringSetStore *[]*string
	intStore       **int64
	funcStore      func(string)
	completer      func(string) []string
}

func newPrompt() *prompt {
	return new(prompt)
}

func (p *prompt) withCompleter(f liner.Completer) *prompt {
	p.completer = f
	return p
}

func (p *prompt) isRequired() *prompt {
	p.required = true
	return p
}

func (p *prompt) setDef(d string) *prompt {
	p.def = d
	return p
}

func (p *prompt) setText(t string) *prompt {
	p.text = t
	return p
}

func (p *prompt) withString(s **string) *prompt {
	p.stringStore = s
	return p
}

func (p *prompt) withStringSet(s *[]*string) *prompt {
	p.stringSetStore = s
	return p
}

func (p *prompt) withInt(s **int64) *prompt {
	p.intStore = s
	return p
}

func (p *prompt) withFunc(s func(string)) *prompt {
	p.funcStore = s
	return p
}

// helps you build a config file
func initPhage(c *cobra.Command, _ []string) {
	l := liner.NewLiner()
	defer l.Close()
	l.SetCtrlCAborts(true)
	fmt.Println(`
		HELLO AND WELCOME

		This command will help you set up your code for deployment to lambda!
		Please answer the prompts as they appear below:
	`)

	//reqMsg := "Sorry, that field is required. Try again."

	// set this callback we can use to call all the stuff
	var realCompleter liner.Completer
	l.SetCompleter(func(line string) []string {
		if realCompleter != nil {
			return realCompleter(line)
		}
		return nil
	})

	cfg := new(Config)
	cfg.IamRole = new(IamRole)
	cfg.Location = new(Location)
	prompts := getPrompts(cfg)

	for _, cPrompt := range prompts {
		p := cPrompt
		text := p.text
		if p.def != "" {
			text += " [" + p.def + "]"
		}

		text += ": "

		realCompleter = nil
		if p.completer != nil {
			realCompleter = p.completer
		}

		if s, err := l.Prompt(text); err == nil {
			input := s
			hasInput := input != ""
			if p.stringStore != nil {
				if hasInput {
					*p.stringStore = &input
				} else {
					*p.stringStore = &p.def
				}

			} else if p.stringSetStore != nil {
				var splitMe string
				if hasInput {
					splitMe = input
				} else {
					splitMe = p.def
				}

				spl := strings.Split(splitMe, ",")
				pspl := make([]*string, len(spl))
				for i, v := range spl {
					// we need to set the value
					// in a variable local to this block
					// because the pointed-to value in
					// `v` will change on the next
					// loop iteration
					realVal := v
					pspl[i] = &realVal
				}

				*p.stringSetStore = pspl

			} else if p.intStore != nil {
				var tParse string
				if hasInput {
					tParse = input
				} else {
					tParse = p.def
				}

				i, _ := strconv.ParseInt(tParse, 10, 64)
				*p.intStore = &i
			} else if p.funcStore != nil {
				// just call the function, man
				p.funcStore(s)
			}
		} else if err == liner.ErrPromptAborted {
			fmt.Println("Aborted")
			return
		} else {
			fmt.Println("Error reading line: ", err)
			return
		}
	}

	d, err := yaml.Marshal(cfg)
	if err != nil {
		panic(err)
	}

	err = ioutil.WriteFile("l-p.yml", d, os.FileMode(0644))
	if err != nil {
		panic(err)
	}
}

// returns all the prompts needed for the `init` command
func getPrompts(cfg *Config) []*prompt {
	wd, _ := os.Getwd()
	st, _ := os.Stat(wd)

	iamRoles, roleMap := getIamRoles()

	return []*prompt{
		newPrompt().
			withString(&cfg.Name).
			isRequired().
			setText("Enter a project name").
			setDef(st.Name()),
		newPrompt().
			withString(&cfg.Description).
			setText("Enter a project description if you'd like").
			setDef(""),
		newPrompt().
			withString(&cfg.Archive).
			setText("Enter a archive name if you'd like").
			setDef(st.Name() + ".zip"),
		newPrompt().
			withString(&cfg.Runtime).
			isRequired().
			setText("What runtime are you using: nodejs, java8, or python 2.7?").
			setDef("nodejs").
			withCompleter(
			func(l string) []string {
				// there can only be one
				r := make([]string, 1)
				if len(l) == 0 {
					r[0] = ""
				} else {
					switch string(l[0]) {
					case "n":
						r[0] = "nodejs"
					case "j":
						r[0] = "java8"
					case "p":
						r[0] = "python2.7"
					}
				}

				return r
			},
		),
		newPrompt().
			withString(&cfg.EntryPoint).
			isRequired().
			setText("Enter an entry point or handler name").
			setDef("index.handler"),
		newPrompt().
			withInt(&cfg.MemorySize).
			setText("Enter memory size").
			setDef("128"),
		newPrompt().
			withInt(&cfg.Timeout).
			setText("Enter timeout").
			setDef("5"),
		newPrompt().
			withStringSet(&cfg.Regions).
			setText("Enter AWS regions where this function will run").
			setDef("us-east-1"),
		newPrompt().
			withFunc(
			func(s string) {
				// if this looks like an ARN,
				// we'll assume it is... for now
				if strings.Index(s, "arn:aws:iam::") == 0 {
					cfg.IamRole.Arn = &s
				} else {
					// check to see if this name is inside the role map
					if arn, ok := roleMap[s]; ok {
						cfg.IamRole.Arn = arn
					} else {
						// if not in role map, set the name
						cfg.IamRole.Name = &s
					}
				}
			},
		).
			isRequired().
			setText("Enter IAM role name").
			setDef("").
			withCompleter(
			func(l string) []string {
				c := make([]string, 0)
				for _, role := range iamRoles {
					if strings.HasPrefix(*role.Name, l) {
						c = append(c, *role.Name)
					}
				}

				return c
			},
		),
	}
}

// pulls all the IAM roles from your account
func getIamRoles() ([]*IamRole, map[string]*string) {
	debug := debug.Debug("core.getIamRoles")
	i := iam.New(nil)
	r, err := i.ListRoles(&iam.ListRolesInput{
		// try loading up to 1000 roles now
		MaxItems: aws.Int64(1000),
	})

	if err != nil {
		debug("getting IAM roles failed! maybe you don't have permission to do that?")
		return []*IamRole{}, map[string]*string{}
	}

	roles := make([]*IamRole, len(r.Roles))
	roleMap := make(map[string]*string)
	for i, r := range r.Roles {
		roles[i] = &IamRole{
			Arn:  r.Arn,
			Name: r.RoleName,
		}
		roleMap[*r.RoleName] = r.Arn
	}

	return roles, roleMap
}
<|endoftext|>


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

// Package pargzip contains a parallel gzip writer implementation.  By
// compressing each chunk of data in parallel, all the CPUs on the
// machine can be used, at a slight loss of compression efficiency.
// In addition, this implementation can use the system gzip binary as
// a child process, which is faster than Go's native implementation.
package pargzip

import (
	"bufio"
	"bytes"
	"compress/gzip"
	"io"
	"os/exec"
	"runtime"
	"strings"
	"sync"
)

// A Writer is an io.WriteCloser.
// Writes to a Writer are compressed and written to w.
//
// Any exported fields may only be mutated before the first call to
// Write.
type Writer struct {
	// UseSystemGzip controls whether the system gzip binary is
	// used. The default from NewWriter is true.
	UseSystemGzip bool

	// ChunkSize is the number of bytes to gzip at once.
	// The default from NewWriter is 1MB.
	ChunkSize int

	// Parallel is the number of chunks to compress in parallel.
	// The default from NewWriter is runtime.NumCPU().
	Parallel int

	w  io.Writer
	bw *bufio.Writer

	allWritten chan struct{} // when writing goroutine ends

	sem    chan bool        // semaphore bounding compressions in flight
	chunkc chan *writeChunk // closed on Close

	mu     sync.Mutex // guards following
	closed bool
	err    error // sticky write error
}

type writeChunk struct {
	zw *Writer
	p  string // uncompressed

	donec chan struct{} // closed on completion

	// one of following is set:
	z   []byte // compressed
	err error  // exec error
}

// compress runs the gzip child process.
// It runs in its own goroutine.
func (c *writeChunk) compress() (err error) {
	defer func() {
		if err != nil {
			c.err = err
		}
		close(c.donec)
		<-c.zw.sem
	}()
	var zbuf bytes.Buffer
	if c.zw.UseSystemGzip {
		cmd := exec.Command("gzip")
		cmd.Stdin = strings.NewReader(c.p)
		cmd.Stdout = &zbuf
		if err := cmd.Run(); err != nil {
			return err
		}
	} else {
		zw := gzip.NewWriter(&zbuf)
		if _, err := io.Copy(zw, strings.NewReader(c.p)); err != nil {
			return err
		}
		if err := zw.Close(); err != nil {
			return err
		}
	}
	c.z = zbuf.Bytes()
	return nil
}

// NewWriter returns a new Writer.
// Writes to the returned writer are compressed and written to w.
//
// It is the caller's responsibility to call Close on the WriteCloser
// when done. Writes may be buffered and not flushed until Close.
//
// Any fields on Writer may only be modified before the first call to
// Write.
func NewWriter(w io.Writer) *Writer {
	return &Writer{
		w:          w,
		allWritten: make(chan struct{}),

		UseSystemGzip: true,
		ChunkSize:     1 << 20,
		Parallel:      runtime.NumCPU(),
	}
}

func (w *Writer) didInit() bool { return w.bw != nil }

func (w *Writer) init() {
	w.bw = bufio.NewWriterSize(newChunkWriter{w}, w.ChunkSize)
	w.chunkc = make(chan *writeChunk, w.Parallel+1)
	w.sem = make(chan bool, w.Parallel)
	go func() {
		defer close(w.allWritten)
		for c := range w.chunkc {
			if err := w.writeCompressedChunk(c); err != nil {
				return
			}
		}
	}()
}

func (w *Writer) startChunk(p []byte) {
	w.sem <- true // block until we can begin
	c := &writeChunk{
		zw:    w,
		p:     string(p), // string, since the bufio.Writer owns the slice
		donec: make(chan struct{}),
	}
	go c.compress() // receives from w.sem
	w.chunkc <- c
}

func (w *Writer) writeCompressedChunk(c *writeChunk) (err error) {
	defer func() {
		if err != nil {
			w.mu.Lock()
			defer w.mu.Unlock()
			if w.err == nil {
				w.err = err
			}
		}
	}()
	<-c.donec
	if c.err != nil {
		return c.err
	}
	_, err = w.w.Write(c.z)
	return
}

func (w *Writer) Write(p []byte) (n int, err error) {
	if !w.didInit() {
		w.init()
	}
	return w.bw.Write(p)
}

func (w *Writer) Close() error {
	w.mu.Lock()
	err, wasClosed := w.err, w.closed
	w.closed = true
	w.mu.Unlock()
	if wasClosed {
		return nil
	}
	if !w.didInit() {
		return nil
	}
	if err != nil {
		return err
	}

	w.bw.Flush()
	close(w.chunkc)
	<-w.allWritten // wait for writing goroutine to end

	w.mu.Lock()
	err = w.err
	w.mu.Unlock()
	return err
}

// newChunkWriter gets large chunks to compress and write to zw.
type newChunkWriter struct {
	zw *Writer
}

func (cw newChunkWriter) Write(p []byte) (n int, err error) {
	n = len(p)
	max := cw.zw.ChunkSize
	for len(p) > 0 {
		chunk := p
		if len(chunk) > max {
			chunk = chunk[:max]
		}
		p = p[len(chunk):]
		cw.zw.startChunk(chunk)
	}
	return
}
<commit_msg>pargzip: fix blocked goroutine on write error<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package pargzip contains a parallel gzip writer implementation.  By
// compressing each chunk of data in parallel, all the CPUs on the
// machine can be used, at a slight loss of compression efficiency.
// In addition, this implementation can use the system gzip binary as
// a child process, which is faster than Go's native implementation.
package pargzip

import (
	"bufio"
	"bytes"
	"compress/gzip"
	"io"
	"os/exec"
	"runtime"
	"strings"
	"sync"
)

// A Writer is an io.WriteCloser.
// Writes to a Writer are compressed and written to w.
//
// Any exported fields may only be mutated before the first call to
// Write.
type Writer struct {
	// UseSystemGzip controls whether the system gzip binary is
	// used. The default from NewWriter is true.
	UseSystemGzip bool

	// ChunkSize is the number of bytes to gzip at once.
	// The default from NewWriter is 1MB.
	ChunkSize int

	// Parallel is the number of chunks to compress in parallel.
	// The default from NewWriter is runtime.NumCPU().
	Parallel int

	w  io.Writer
	bw *bufio.Writer

	allWritten  chan struct{} // when writing goroutine ends
	wasWriteErr chan struct{} // closed after 'err' set

	sem    chan bool        // semaphore bounding compressions in flight
	chunkc chan *writeChunk // closed on Close

	mu     sync.Mutex // guards following
	closed bool
	err    error // sticky write error
}

type writeChunk struct {
	zw *Writer
	p  string // uncompressed

	donec chan struct{} // closed on completion

	// one of following is set:
	z   []byte // compressed
	err error  // exec error
}

// compress runs the gzip child process.
// It runs in its own goroutine.
func (c *writeChunk) compress() (err error) {
	defer func() {
		if err != nil {
			c.err = err
		}
		close(c.donec)
		<-c.zw.sem
	}()
	var zbuf bytes.Buffer
	if c.zw.UseSystemGzip {
		cmd := exec.Command("gzip")
		cmd.Stdin = strings.NewReader(c.p)
		cmd.Stdout = &zbuf
		if err := cmd.Run(); err != nil {
			return err
		}
	} else {
		zw := gzip.NewWriter(&zbuf)
		if _, err := io.Copy(zw, strings.NewReader(c.p)); err != nil {
			return err
		}
		if err := zw.Close(); err != nil {
			return err
		}
	}
	c.z = zbuf.Bytes()
	return nil
}

// NewWriter returns a new Writer.
// Writes to the returned writer are compressed and written to w.
//
// It is the caller's responsibility to call Close on the WriteCloser
// when done. Writes may be buffered and not flushed until Close.
//
// Any fields on Writer may only be modified before the first call to
// Write.
func NewWriter(w io.Writer) *Writer {
	return &Writer{
		w:           w,
		allWritten:  make(chan struct{}),
		wasWriteErr: make(chan struct{}),

		UseSystemGzip: true,
		ChunkSize:     1 << 20,
		Parallel:      runtime.NumCPU(),
	}
}

func (w *Writer) didInit() bool { return w.bw != nil }

func (w *Writer) init() {
	w.bw = bufio.NewWriterSize(newChunkWriter{w}, w.ChunkSize)
	w.chunkc = make(chan *writeChunk, w.Parallel+1)
	w.sem = make(chan bool, w.Parallel)
	go func() {
		defer close(w.allWritten)
		for c := range w.chunkc {
			if err := w.writeCompressedChunk(c); err != nil {
				close(w.wasWriteErr)
				return
			}
		}
	}()
}

func (w *Writer) startChunk(p []byte) {
	w.sem <- true // block until we can begin
	c := &writeChunk{
		zw:    w,
		p:     string(p), // string, since the bufio.Writer owns the slice
		donec: make(chan struct{}),
	}
	go c.compress() // receives from w.sem
	select {
	case w.chunkc <- c:
	case <-w.wasWriteErr:
		// Discard chunks that come after any chunk that failed
		// to write.
	}
}

func (w *Writer) writeCompressedChunk(c *writeChunk) (err error) {
	defer func() {
		if err != nil {
			w.mu.Lock()
			defer w.mu.Unlock()
			if w.err == nil {
				w.err = err
			}
		}
	}()
	<-c.donec
	if c.err != nil {
		return c.err
	}
	_, err = w.w.Write(c.z)
	return
}

func (w *Writer) Write(p []byte) (n int, err error) {
	if !w.didInit() {
		w.init()
	}
	return w.bw.Write(p)
}

func (w *Writer) Close() error {
	w.mu.Lock()
	err, wasClosed := w.err, w.closed
	w.closed = true
	w.mu.Unlock()
	if wasClosed {
		return nil
	}
	if !w.didInit() {
		return nil
	}
	if err != nil {
		return err
	}

	w.bw.Flush()
	close(w.chunkc)
	<-w.allWritten // wait for writing goroutine to end

	w.mu.Lock()
	err = w.err
	w.mu.Unlock()
	return err
}

// newChunkWriter gets large chunks to compress and write to zw.
type newChunkWriter struct {
	zw *Writer
}

func (cw newChunkWriter) Write(p []byte) (n int, err error) {
	n = len(p)
	max := cw.zw.ChunkSize
	for len(p) > 0 {
		chunk := p
		if len(chunk) > max {
			chunk = chunk[:max]
		}
		p = p[len(chunk):]
		cw.zw.startChunk(chunk)
	}
	return
}
<|endoftext|>


<commit_before>// A `pass` based credential helper. Passwords are stored as arguments to pass
// of the form: "$PASS_FOLDER/base64-url(serverURL)/username". We base64-url
// encode the serverURL, because under the hood pass uses files and folders, so
// /s will get translated into additional folders.
package pass

import (
	"encoding/base64"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"strings"
	"sync"

	"github.com/docker/docker-credential-helpers/credentials"
)

const PASS_FOLDER = "docker-credential-helpers"

// Pass handles secrets using Linux secret-service as a store.
type Pass struct{}

// Ideally these would be stored as members of Pass, but since all of Pass's
// methods have value receivers, not pointer receivers, and changing that is
// backwards incompatible, we assume that all Pass instances share the same configuration

// initializationMutex is held while initializing so that only one 'pass'
// round-tripping is done to check pass is functioning.
var initializationMutex sync.Mutex
var passInitialized bool

func (p Pass) checkInitialized() error {
	initializationMutex.Lock()
	defer initializationMutex.Unlock()
	if passInitialized {
		return nil
	}
	// In principle, we could just run `pass init`. However, pass has a bug
	// where if gpg fails, it doesn't always exit 1. Additionally, pass
	// uses gpg2, but gpg is the default, which may be confusing. So let's
	// just explictily check that pass actually can store and retreive a
	// password.
	password := "pass is initialized"
	name := path.Join(getPassDir(), "docker-pass-initialized-check")

	_, err := p.runPassHelper(password, "insert", "-f", "-m", name)
	if err != nil {
		return fmt.Errorf("error initializing pass: %v", err)
	}

	stored, err := p.runPassHelper("", "show", name)
	if err != nil {
		return fmt.Errorf("error fetching password during initialization: %v", err)
	}
	if stored != password {
		return fmt.Errorf("error round-tripping password during initialization: %q != %q", password, stored)
	}
	passInitialized = true
	return nil
}

func (p Pass) runPass(stdinContent string, args ...string) (string, error) {
	if err := p.checkInitialized(); err != nil {
		return "", err
	}
	return p.runPassHelper(stdinContent, args...)
}

func (p Pass) runPassHelper(stdinContent string, args ...string) (string, error) {
	cmd := exec.Command("pass", args...)
	cmd.Stdin = strings.NewReader(stdin)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	err := cmd.Run()
	if err != nil {
		return "", fmt.Errorf("%s: %s", err, stderr.String())
	}

	return stdout.String(), nil
}

// Add adds new credentials to the keychain.
func (h Pass) Add(creds *credentials.Credentials) error {
	if creds == nil {
		return errors.New("missing credentials")
	}

	encoded := base64.URLEncoding.EncodeToString([]byte(creds.ServerURL))

	_, err := h.runPass(creds.Secret, "insert", "-f", "-m", path.Join(PASS_FOLDER, encoded, creds.Username))
	return err
}

// Delete removes credentials from the store.
func (h Pass) Delete(serverURL string) error {
	if serverURL == "" {
		return errors.New("missing server url")
	}

	encoded := base64.URLEncoding.EncodeToString([]byte(serverURL))
	_, err := h.runPass("", "rm", "-rf", path.Join(PASS_FOLDER, encoded))
	return err
}

func getPassDir() string {
	passDir := "$HOME/.password-store"
	if envDir := os.Getenv("PASSWORD_STORE_DIR"); envDir != "" {
		passDir = envDir
	}
	return os.ExpandEnv(passDir)
}

// listPassDir lists all the contents of a directory in the password store.
// Pass uses fancy unicode to emit stuff to stdout, so rather than try
// and parse this, let's just look at the directory structure instead.
func listPassDir(args ...string) ([]os.FileInfo, error) {
	passDir := getPassDir()
	p := path.Join(append([]string{passDir, PASS_FOLDER}, args...)...)
	contents, err := ioutil.ReadDir(p)
	if err != nil {
		if os.IsNotExist(err) {
			return []os.FileInfo{}, nil
		}

		return nil, err
	}

	return contents, nil
}

// Get returns the username and secret to use for a given registry server URL.
func (h Pass) Get(serverURL string) (string, string, error) {
	if serverURL == "" {
		return "", "", errors.New("missing server url")
	}

	encoded := base64.URLEncoding.EncodeToString([]byte(serverURL))

	if _, err := os.Stat(path.Join(getPassDir(), PASS_FOLDER, encoded)); err != nil {
		if os.IsNotExist(err) {
			return "", "", nil
		}

		return "", "", err
	}

	usernames, err := listPassDir(encoded)
	if err != nil {
		return "", "", err
	}

	if len(usernames) < 1 {
		return "", "", fmt.Errorf("no usernames for %s", serverURL)
	}

	actual := strings.TrimSuffix(usernames[0].Name(), ".gpg")
	secret, err := h.runPass("", "show", path.Join(PASS_FOLDER, encoded, actual))
	return actual, secret, err
}

// List returns the stored URLs and corresponding usernames for a given credentials label
func (h Pass) List() (map[string]string, error) {
	servers, err := listPassDir()
	if err != nil {
		return nil, err
	}

	resp := map[string]string{}

	for _, server := range servers {
		if !server.IsDir() {
			continue
		}

		serverURL, err := base64.URLEncoding.DecodeString(server.Name())
		if err != nil {
			return nil, err
		}

		usernames, err := listPassDir(server.Name())
		if err != nil {
			return nil, err
		}

		if len(usernames) < 1 {
			return nil, fmt.Errorf("no usernames for %s", serverURL)
		}

		resp[string(serverURL)] = strings.TrimSuffix(usernames[0].Name(), ".gpg")
	}

	return resp, nil
}
<commit_msg>pass: add IsInitialized helper<commit_after>// A `pass` based credential helper. Passwords are stored as arguments to pass
// of the form: "$PASS_FOLDER/base64-url(serverURL)/username". We base64-url
// encode the serverURL, because under the hood pass uses files and folders, so
// /s will get translated into additional folders.
package pass

import (
	"bytes"
	"encoding/base64"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"strings"
	"sync"

	"github.com/docker/docker-credential-helpers/credentials"
)

const PASS_FOLDER = "docker-credential-helpers"

// Pass handles secrets using Linux secret-service as a store.
type Pass struct{}

// Ideally these would be stored as members of Pass, but since all of Pass's
// methods have value receivers, not pointer receivers, and changing that is
// backwards incompatible, we assume that all Pass instances share the same configuration

// initializationMutex is held while initializing so that only one 'pass'
// round-tripping is done to check pass is functioning.
var initializationMutex sync.Mutex
var passInitialized bool

// CheckInitialized checks whether the password helper can be used. It
// internally caches and so may be safely called multiple times with no impact
// on performance, though the first call may take longer.
func (p Pass) CheckInitialized() bool {
	return p.checkInitialized() == nil
}

func (p Pass) checkInitialized() error {
	initializationMutex.Lock()
	defer initializationMutex.Unlock()
	if passInitialized {
		return nil
	}
	// In principle, we could just run `pass init`. However, pass has a bug
	// where if gpg fails, it doesn't always exit 1. Additionally, pass
	// uses gpg2, but gpg is the default, which may be confusing. So let's
	// just explictily check that pass actually can store and retreive a
	// password.
	password := "pass is initialized"
	name := path.Join(getPassDir(), "docker-pass-initialized-check")

	_, err := p.runPassHelper(password, "insert", "-f", "-m", name)
	if err != nil {
		return fmt.Errorf("error initializing pass: %v", err)
	}

	stored, err := p.runPassHelper("", "show", name)
	if err != nil {
		return fmt.Errorf("error fetching password during initialization: %v", err)
	}
	if stored != password {
		return fmt.Errorf("error round-tripping password during initialization: %q != %q", password, stored)
	}
	passInitialized = true
	return nil
}

func (p Pass) runPass(stdinContent string, args ...string) (string, error) {
	if err := p.checkInitialized(); err != nil {
		return "", err
	}
	return p.runPassHelper(stdinContent, args...)
}

func (p Pass) runPassHelper(stdinContent string, args ...string) (string, error) {
	var stdout, stderr bytes.Buffer
	cmd := exec.Command("pass", args...)
	cmd.Stdin = strings.NewReader(stdinContent)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	err := cmd.Run()
	if err != nil {
		return "", fmt.Errorf("%s: %s", err, stderr.String())
	}

	return stdout.String(), nil
}

// Add adds new credentials to the keychain.
func (h Pass) Add(creds *credentials.Credentials) error {
	if creds == nil {
		return errors.New("missing credentials")
	}

	encoded := base64.URLEncoding.EncodeToString([]byte(creds.ServerURL))

	_, err := h.runPass(creds.Secret, "insert", "-f", "-m", path.Join(PASS_FOLDER, encoded, creds.Username))
	return err
}

// Delete removes credentials from the store.
func (h Pass) Delete(serverURL string) error {
	if serverURL == "" {
		return errors.New("missing server url")
	}

	encoded := base64.URLEncoding.EncodeToString([]byte(serverURL))
	_, err := h.runPass("", "rm", "-rf", path.Join(PASS_FOLDER, encoded))
	return err
}

func getPassDir() string {
	passDir := "$HOME/.password-store"
	if envDir := os.Getenv("PASSWORD_STORE_DIR"); envDir != "" {
		passDir = envDir
	}
	return os.ExpandEnv(passDir)
}

// listPassDir lists all the contents of a directory in the password store.
// Pass uses fancy unicode to emit stuff to stdout, so rather than try
// and parse this, let's just look at the directory structure instead.
func listPassDir(args ...string) ([]os.FileInfo, error) {
	passDir := getPassDir()
	p := path.Join(append([]string{passDir, PASS_FOLDER}, args...)...)
	contents, err := ioutil.ReadDir(p)
	if err != nil {
		if os.IsNotExist(err) {
			return []os.FileInfo{}, nil
		}

		return nil, err
	}

	return contents, nil
}

// Get returns the username and secret to use for a given registry server URL.
func (h Pass) Get(serverURL string) (string, string, error) {
	if serverURL == "" {
		return "", "", errors.New("missing server url")
	}

	encoded := base64.URLEncoding.EncodeToString([]byte(serverURL))

	if _, err := os.Stat(path.Join(getPassDir(), PASS_FOLDER, encoded)); err != nil {
		if os.IsNotExist(err) {
			return "", "", nil
		}

		return "", "", err
	}

	usernames, err := listPassDir(encoded)
	if err != nil {
		return "", "", err
	}

	if len(usernames) < 1 {
		return "", "", fmt.Errorf("no usernames for %s", serverURL)
	}

	actual := strings.TrimSuffix(usernames[0].Name(), ".gpg")
	secret, err := h.runPass("", "show", path.Join(PASS_FOLDER, encoded, actual))
	return actual, secret, err
}

// List returns the stored URLs and corresponding usernames for a given credentials label
func (h Pass) List() (map[string]string, error) {
	servers, err := listPassDir()
	if err != nil {
		return nil, err
	}

	resp := map[string]string{}

	for _, server := range servers {
		if !server.IsDir() {
			continue
		}

		serverURL, err := base64.URLEncoding.DecodeString(server.Name())
		if err != nil {
			return nil, err
		}

		usernames, err := listPassDir(server.Name())
		if err != nil {
			return nil, err
		}

		if len(usernames) < 1 {
			return nil, fmt.Errorf("no usernames for %s", serverURL)
		}

		resp[string(serverURL)] = strings.TrimSuffix(usernames[0].Name(), ".gpg")
	}

	return resp, nil
}
<|endoftext|>


<commit_before>package collector

import (
	"bufio"
	"context"
	"io"
	"strings"
	"time"

	"github.com/bcicen/ctop/models"
	api "github.com/fsouza/go-dockerclient"
)

type DockerLogs struct {
	id     string
	client *api.Client
	done   chan bool
}

func NewDockerLogs(id string, client *api.Client) *DockerLogs {
	return &DockerLogs{
		id:     id,
		client: client,
		done:   make(chan bool),
	}
}

func (l *DockerLogs) Stream() chan models.Log {
	r, w := io.Pipe()
	logCh := make(chan models.Log)
	ctx, cancel := context.WithCancel(context.Background())

	opts := api.LogsOptions{
		Context:      ctx,
		Container:    l.id,
		OutputStream: w,
		ErrorStream:  w,
		Stdout:       true,
		Stderr:       true,
		Tail:         "10",
		Follow:       true,
		Timestamps:   true,
	}

	// read io pipe into channel
	go func() {
		scanner := bufio.NewScanner(r)
		for scanner.Scan() {
			parts := strings.Split(scanner.Text(), " ")
			ts := l.parseTime(parts[0])
			logCh <- models.Log{Timestamp: ts, Message: strings.Join(parts[1:], " ")}
		}
	}()

	// connect to container log stream
	go func() {
		err := l.client.Logs(opts)
		if err != nil {
			log.Errorf("error reading container logs: %s", err)
		}
		log.Infof("log reader stopped for container: %s", l.id)
	}()

	go func() {
		<-l.done
		cancel()
	}()

	log.Infof("log reader started for container: %s", l.id)
	return logCh
}

func (l *DockerLogs) Stop() { l.done <- true }

func (l *DockerLogs) parseTime(s string) time.Time {
	ts, err := time.Parse("2006-01-02T15:04:05.000000000Z", s)
	if err != nil {
		log.Errorf("failed to parse container log: %s", err)
		ts = time.Now()
	}
	return ts
}
<commit_msg>use raw log stream in docker log collector<commit_after>package collector

import (
	"bufio"
	"context"
	"io"
	"strings"
	"time"

	"github.com/bcicen/ctop/models"
	api "github.com/fsouza/go-dockerclient"
)

type DockerLogs struct {
	id     string
	client *api.Client
	done   chan bool
}

func NewDockerLogs(id string, client *api.Client) *DockerLogs {
	return &DockerLogs{
		id:     id,
		client: client,
		done:   make(chan bool),
	}
}

func (l *DockerLogs) Stream() chan models.Log {
	r, w := io.Pipe()
	logCh := make(chan models.Log)
	ctx, cancel := context.WithCancel(context.Background())

	opts := api.LogsOptions{
		Context:      ctx,
		Container:    l.id,
		OutputStream: w,
		//ErrorStream:  w,
		Stdout:      true,
		Stderr:      true,
		Tail:        "20",
		Follow:      true,
		Timestamps:  true,
		RawTerminal: true,
	}

	// read io pipe into channel
	go func() {
		scanner := bufio.NewScanner(r)
		for scanner.Scan() {
			parts := strings.Split(scanner.Text(), " ")
			ts := l.parseTime(parts[0])
			logCh <- models.Log{Timestamp: ts, Message: strings.Join(parts[1:], " ")}
		}
	}()

	// connect to container log stream
	go func() {
		err := l.client.Logs(opts)
		if err != nil {
			log.Errorf("error reading container logs: %s", err)
		}
		log.Infof("log reader stopped for container: %s", l.id)
	}()

	go func() {
		<-l.done
		cancel()
	}()

	log.Infof("log reader started for container: %s", l.id)
	return logCh
}

func (l *DockerLogs) Stop() { l.done <- true }

func (l *DockerLogs) parseTime(s string) time.Time {
	ts, err := time.Parse("2006-01-02T15:04:05.000000000Z", s)
	if err == nil {
		return ts
	}

	ts, err2 := time.Parse("2006-01-02T15:04:05.000000000Z", l.stripPfx(s))
	if err2 == nil {
		return ts
	}

	log.Errorf("failed to parse container log: %s", err)
	log.Errorf("failed to parse container log2: %s", err2)
	return time.Now()
}

// attempt to strip message header prefix from a given raw docker log string
func (l *DockerLogs) stripPfx(s string) string {
	b := []byte(s)
	if len(b) > 8 {
		return string(b[8:])
	}
	return s
}
<|endoftext|>


<commit_before>// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gvar_test

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"math"
	"testing"
	"time"

	"github.com/gogf/gf/frame/g"

	"github.com/gogf/gf/container/gvar"
	"github.com/gogf/gf/test/gtest"
)

func Test_Set(t *testing.T) {
	gtest.Case(t, func() {
		objOne := gvar.New("old", true)
		objOneOld, _ := objOne.Set("new").(string)
		gtest.Assert(objOneOld, "old")

		objTwo := gvar.New("old", false)
		objTwoOld, _ := objTwo.Set("new").(string)
		gtest.Assert(objTwoOld, "old")
	})
}

func Test_Val(t *testing.T) {
	gtest.Case(t, func() {
		objOne := gvar.New(1, true)
		objOneOld, _ := objOne.Val().(int)
		gtest.Assert(objOneOld, 1)

		objTwo := gvar.New(1, false)
		objTwoOld, _ := objTwo.Val().(int)
		gtest.Assert(objTwoOld, 1)
	})
}
func Test_Interface(t *testing.T) {
	gtest.Case(t, func() {
		objOne := gvar.New(1, true)
		objOneOld, _ := objOne.Interface().(int)
		gtest.Assert(objOneOld, 1)

		objTwo := gvar.New(1, false)
		objTwoOld, _ := objTwo.Interface().(int)
		gtest.Assert(objTwoOld, 1)
	})
}
func Test_IsNil(t *testing.T) {
	gtest.Case(t, func() {
		objOne := gvar.New(nil, true)
		gtest.Assert(objOne.IsNil(), true)

		objTwo := gvar.New("noNil", false)
		gtest.Assert(objTwo.IsNil(), false)

	})
}

func Test_Bytes(t *testing.T) {
	gtest.Case(t, func() {
		x := int32(1)
		bytesBuffer := bytes.NewBuffer([]byte{})
		binary.Write(bytesBuffer, binary.BigEndian, x)

		objOne := gvar.New(bytesBuffer.Bytes(), true)

		bBuf := bytes.NewBuffer(objOne.Bytes())
		var y int32
		binary.Read(bBuf, binary.BigEndian, &y)

		gtest.Assert(x, y)

	})
}

func Test_String(t *testing.T) {
	gtest.Case(t, func() {
		var str string = "hello"
		objOne := gvar.New(str, true)
		gtest.Assert(objOne.String(), str)

	})
}
func Test_Bool(t *testing.T) {
	gtest.Case(t, func() {
		var ok bool = true
		objOne := gvar.New(ok, true)
		gtest.Assert(objOne.Bool(), ok)

		ok = false
		objTwo := gvar.New(ok, true)
		gtest.Assert(objTwo.Bool(), ok)

	})
}

func Test_Int(t *testing.T) {
	gtest.Case(t, func() {
		var num int = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Int(), num)

	})
}

func Test_Int8(t *testing.T) {
	gtest.Case(t, func() {
		var num int8 = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Int8(), num)

	})
}

func Test_Int16(t *testing.T) {
	gtest.Case(t, func() {
		var num int16 = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Int16(), num)

	})
}

func Test_Int32(t *testing.T) {
	gtest.Case(t, func() {
		var num int32 = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Int32(), num)

	})
}

func Test_Int64(t *testing.T) {
	gtest.Case(t, func() {
		var num int64 = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Int64(), num)

	})
}

func Test_Uint(t *testing.T) {
	gtest.Case(t, func() {
		var num uint = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Uint(), num)

	})
}

func Test_Uint8(t *testing.T) {
	gtest.Case(t, func() {
		var num uint8 = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Uint8(), num)

	})
}

func Test_Uint16(t *testing.T) {
	gtest.Case(t, func() {
		var num uint16 = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Uint16(), num)

	})
}

func Test_Uint32(t *testing.T) {
	gtest.Case(t, func() {
		var num uint32 = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Uint32(), num)

	})
}

func Test_Uint64(t *testing.T) {
	gtest.Case(t, func() {
		var num uint64 = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Uint64(), num)

	})
}
func Test_Float32(t *testing.T) {
	gtest.Case(t, func() {
		var num float32 = 1.1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Float32(), num)

	})
}

func Test_Float64(t *testing.T) {
	gtest.Case(t, func() {
		var num float64 = 1.1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Float64(), num)

	})
}

func Test_Ints(t *testing.T) {
	gtest.Case(t, func() {
		var arr = []int{1, 2, 3, 4, 5}
		objOne := gvar.New(arr, true)
		gtest.Assert(objOne.Ints()[0], arr[0])
	})
}
func Test_Floats(t *testing.T) {
	gtest.Case(t, func() {
		var arr = []float64{1, 2, 3, 4, 5}
		objOne := gvar.New(arr, true)
		gtest.Assert(objOne.Floats()[0], arr[0])
	})
}
func Test_Strings(t *testing.T) {
	gtest.Case(t, func() {
		var arr = []string{"hello", "world"}
		objOne := gvar.New(arr, true)
		gtest.Assert(objOne.Strings()[0], arr[0])
	})
}

func Test_Interfaces(t *testing.T) {
	gtest.Case(t, func() {
		var arr = []int{1, 2, 3, 4, 5}
		objOne := gvar.New(arr, true)
		gtest.Assert(objOne.Interfaces(), arr)
	})
}

func Test_Slice(t *testing.T) {
	gtest.Case(t, func() {
		var arr = []int{1, 2, 3, 4, 5}
		objOne := gvar.New(arr, true)
		gtest.Assert(objOne.Slice(), arr)
	})
}

func Test_Array(t *testing.T) {
	gtest.Case(t, func() {
		var arr = []int{1, 2, 3, 4, 5}
		objOne := gvar.New(arr, false)
		gtest.Assert(objOne.Array(), arr)
	})
}

func Test_Vars(t *testing.T) {
	gtest.Case(t, func() {
		var arr = []int{1, 2, 3, 4, 5}
		objOne := gvar.New(arr, false)
		gtest.Assert(len(objOne.Vars()), 5)
		gtest.Assert(objOne.Vars()[0].Int(), 1)
		gtest.Assert(objOne.Vars()[4].Int(), 5)
	})
}

func Test_Time(t *testing.T) {
	gtest.Case(t, func() {
		var timeUnix int64 = 1556242660
		objOne := gvar.New(timeUnix, true)
		gtest.Assert(objOne.Time().Unix(), timeUnix)
	})
}

func Test_GTime(t *testing.T) {
	gtest.Case(t, func() {
		var timeUnix int64 = 1556242660
		objOne := gvar.New(timeUnix, true)
		gtest.Assert(objOne.GTime().Unix(), timeUnix)
	})
}

func Test_Duration(t *testing.T) {
	gtest.Case(t, func() {
		var timeUnix int64 = 1556242660
		objOne := gvar.New(timeUnix, true)
		gtest.Assert(objOne.Duration(), time.Duration(timeUnix))
	})
}

func Test_Map(t *testing.T) {
	gtest.Case(t, func() {
		m := g.Map{
			"k1": "v1",
			"k2": "v2",
		}
		objOne := gvar.New(m, true)
		gtest.Assert(objOne.Map()["k1"], m["k1"])
		gtest.Assert(objOne.Map()["k2"], m["k2"])
	})
}

func Test_Struct(t *testing.T) {
	gtest.Case(t, func() {
		type StTest struct {
			Test int
		}

		Kv := make(map[string]int, 1)
		Kv["Test"] = 100

		testObj := &StTest{}

		objOne := gvar.New(Kv, true)

		objOne.Struct(testObj)

		gtest.Assert(testObj.Test, Kv["Test"])
	})
}

func Test_Json(t *testing.T) {
	// Marshal
	gtest.Case(t, func() {
		s := "i love gf"
		v := gvar.New(s)
		b1, err1 := json.Marshal(v)
		b2, err2 := json.Marshal(s)
		gtest.Assert(err1, err2)
		gtest.Assert(b1, b2)
	})

	gtest.Case(t, func() {
		s := math.MaxInt64
		v := gvar.New(s)
		b1, err1 := json.Marshal(v)
		b2, err2 := json.Marshal(s)
		gtest.Assert(err1, err2)
		gtest.Assert(b1, b2)
	})

	// Unmarshal
	gtest.Case(t, func() {
		s := "i love gf"
		v := gvar.New(nil)
		b, err := json.Marshal(s)
		gtest.Assert(err, nil)

		err = json.Unmarshal(b, v)
		gtest.Assert(err, nil)
		gtest.Assert(v.String(), s)
	})

	gtest.Case(t, func() {
		var v gvar.Var
		s := "i love gf"
		b, err := json.Marshal(s)
		gtest.Assert(err, nil)

		err = json.Unmarshal(b, &v)
		gtest.Assert(err, nil)
		gtest.Assert(v.String(), s)
	})
}
<commit_msg>fix issue in unit test case for gvar<commit_after>// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gvar_test

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"math"
	"testing"
	"time"

	"github.com/gogf/gf/frame/g"

	"github.com/gogf/gf/container/gvar"
	"github.com/gogf/gf/test/gtest"
)

func Test_Set(t *testing.T) {
	gtest.Case(t, func() {
		objOne := gvar.New("old", true)
		objOneOld, _ := objOne.Set("new").(string)
		gtest.Assert(objOneOld, "old")

		objTwo := gvar.New("old", false)
		objTwoOld, _ := objTwo.Set("new").(string)
		gtest.Assert(objTwoOld, "old")
	})
}

func Test_Val(t *testing.T) {
	gtest.Case(t, func() {
		objOne := gvar.New(1, true)
		objOneOld, _ := objOne.Val().(int)
		gtest.Assert(objOneOld, 1)

		objTwo := gvar.New(1, false)
		objTwoOld, _ := objTwo.Val().(int)
		gtest.Assert(objTwoOld, 1)
	})
}
func Test_Interface(t *testing.T) {
	gtest.Case(t, func() {
		objOne := gvar.New(1, true)
		objOneOld, _ := objOne.Interface().(int)
		gtest.Assert(objOneOld, 1)

		objTwo := gvar.New(1, false)
		objTwoOld, _ := objTwo.Interface().(int)
		gtest.Assert(objTwoOld, 1)
	})
}
func Test_IsNil(t *testing.T) {
	gtest.Case(t, func() {
		objOne := gvar.New(nil, true)
		gtest.Assert(objOne.IsNil(), true)

		objTwo := gvar.New("noNil", false)
		gtest.Assert(objTwo.IsNil(), false)

	})
}

func Test_Bytes(t *testing.T) {
	gtest.Case(t, func() {
		x := int32(1)
		bytesBuffer := bytes.NewBuffer([]byte{})
		binary.Write(bytesBuffer, binary.BigEndian, x)

		objOne := gvar.New(bytesBuffer.Bytes(), true)

		bBuf := bytes.NewBuffer(objOne.Bytes())
		var y int32
		binary.Read(bBuf, binary.BigEndian, &y)

		gtest.Assert(x, y)

	})
}

func Test_String(t *testing.T) {
	gtest.Case(t, func() {
		var str string = "hello"
		objOne := gvar.New(str, true)
		gtest.Assert(objOne.String(), str)

	})
}
func Test_Bool(t *testing.T) {
	gtest.Case(t, func() {
		var ok bool = true
		objOne := gvar.New(ok, true)
		gtest.Assert(objOne.Bool(), ok)

		ok = false
		objTwo := gvar.New(ok, true)
		gtest.Assert(objTwo.Bool(), ok)

	})
}

func Test_Int(t *testing.T) {
	gtest.Case(t, func() {
		var num int = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Int(), num)

	})
}

func Test_Int8(t *testing.T) {
	gtest.Case(t, func() {
		var num int8 = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Int8(), num)

	})
}

func Test_Int16(t *testing.T) {
	gtest.Case(t, func() {
		var num int16 = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Int16(), num)

	})
}

func Test_Int32(t *testing.T) {
	gtest.Case(t, func() {
		var num int32 = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Int32(), num)

	})
}

func Test_Int64(t *testing.T) {
	gtest.Case(t, func() {
		var num int64 = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Int64(), num)

	})
}

func Test_Uint(t *testing.T) {
	gtest.Case(t, func() {
		var num uint = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Uint(), num)

	})
}

func Test_Uint8(t *testing.T) {
	gtest.Case(t, func() {
		var num uint8 = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Uint8(), num)

	})
}

func Test_Uint16(t *testing.T) {
	gtest.Case(t, func() {
		var num uint16 = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Uint16(), num)

	})
}

func Test_Uint32(t *testing.T) {
	gtest.Case(t, func() {
		var num uint32 = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Uint32(), num)

	})
}

func Test_Uint64(t *testing.T) {
	gtest.Case(t, func() {
		var num uint64 = 1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Uint64(), num)

	})
}
func Test_Float32(t *testing.T) {
	gtest.Case(t, func() {
		var num float32 = 1.1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Float32(), num)

	})
}

func Test_Float64(t *testing.T) {
	gtest.Case(t, func() {
		var num float64 = 1.1
		objOne := gvar.New(num, true)
		gtest.Assert(objOne.Float64(), num)

	})
}

func Test_Ints(t *testing.T) {
	gtest.Case(t, func() {
		var arr = []int{1, 2, 3, 4, 5}
		objOne := gvar.New(arr, true)
		gtest.Assert(objOne.Ints()[0], arr[0])
	})
}
func Test_Floats(t *testing.T) {
	gtest.Case(t, func() {
		var arr = []float64{1, 2, 3, 4, 5}
		objOne := gvar.New(arr, true)
		gtest.Assert(objOne.Floats()[0], arr[0])
	})
}
func Test_Strings(t *testing.T) {
	gtest.Case(t, func() {
		var arr = []string{"hello", "world"}
		objOne := gvar.New(arr, true)
		gtest.Assert(objOne.Strings()[0], arr[0])
	})
}

func Test_Interfaces(t *testing.T) {
	gtest.Case(t, func() {
		var arr = []int{1, 2, 3, 4, 5}
		objOne := gvar.New(arr, true)
		gtest.Assert(objOne.Interfaces(), arr)
	})
}

func Test_Slice(t *testing.T) {
	gtest.Case(t, func() {
		var arr = []int{1, 2, 3, 4, 5}
		objOne := gvar.New(arr, true)
		gtest.Assert(objOne.Slice(), arr)
	})
}

func Test_Array(t *testing.T) {
	gtest.Case(t, func() {
		var arr = []int{1, 2, 3, 4, 5}
		objOne := gvar.New(arr, false)
		gtest.Assert(objOne.Array(), arr)
	})
}

func Test_Vars(t *testing.T) {
	gtest.Case(t, func() {
		var arr = []int{1, 2, 3, 4, 5}
		objOne := gvar.New(arr, false)
		gtest.Assert(len(objOne.Vars()), 5)
		gtest.Assert(objOne.Vars()[0].Int(), 1)
		gtest.Assert(objOne.Vars()[4].Int(), 5)
	})
}

func Test_Time(t *testing.T) {
	gtest.Case(t, func() {
		var timeUnix int64 = 1556242660
		objOne := gvar.New(timeUnix, true)
		gtest.Assert(objOne.Time().Unix(), timeUnix)
	})
}

func Test_GTime(t *testing.T) {
	gtest.Case(t, func() {
		var timeUnix int64 = 1556242660
		objOne := gvar.New(timeUnix, true)
		gtest.Assert(objOne.GTime().Unix(), timeUnix)
	})
}

func Test_Duration(t *testing.T) {
	gtest.Case(t, func() {
		var timeUnix int64 = 1556242660
		objOne := gvar.New(timeUnix, true)
		gtest.Assert(objOne.Duration(), time.Duration(timeUnix))
	})
}

func Test_Map(t *testing.T) {
	gtest.Case(t, func() {
		m := g.Map{
			"k1": "v1",
			"k2": "v2",
		}
		objOne := gvar.New(m, true)
		gtest.Assert(objOne.Map()["k1"], m["k1"])
		gtest.Assert(objOne.Map()["k2"], m["k2"])
	})
}

func Test_Struct(t *testing.T) {
	gtest.Case(t, func() {
		type StTest struct {
			Test int
		}

		Kv := make(map[string]int, 1)
		Kv["Test"] = 100

		testObj := &StTest{}

		objOne := gvar.New(Kv, true)

		objOne.Struct(testObj)

		gtest.Assert(testObj.Test, Kv["Test"])
	})
}

func Test_Json(t *testing.T) {
	// Marshal
	gtest.Case(t, func() {
		s := "i love gf"
		v := gvar.New(s)
		b1, err1 := json.Marshal(v)
		b2, err2 := json.Marshal(s)
		gtest.Assert(err1, err2)
		gtest.Assert(b1, b2)
	})

	gtest.Case(t, func() {
		s := int64(math.MaxInt64)
		v := gvar.New(s)
		b1, err1 := json.Marshal(v)
		b2, err2 := json.Marshal(s)
		gtest.Assert(err1, err2)
		gtest.Assert(b1, b2)
	})

	// Unmarshal
	gtest.Case(t, func() {
		s := "i love gf"
		v := gvar.New(nil)
		b, err := json.Marshal(s)
		gtest.Assert(err, nil)

		err = json.Unmarshal(b, v)
		gtest.Assert(err, nil)
		gtest.Assert(v.String(), s)
	})

	gtest.Case(t, func() {
		var v gvar.Var
		s := "i love gf"
		b, err := json.Marshal(s)
		gtest.Assert(err, nil)

		err = json.Unmarshal(b, &v)
		gtest.Assert(err, nil)
		gtest.Assert(v.String(), s)
	})
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.

package photon

import (
	"bytes"
	"encoding/json"
)

// Contains functionality for networks API.
type NetworksAPI struct {
	client *Client
}

// Options used for GetAll API
type NetworkGetOptions struct {
	Name string `urlParam:"name"`
}

var networkUrl string = "/networks"

// Creates a network.
func (api *NetworksAPI) Create(networkSpec *NetworkCreateSpec) (task *Task, err error) {
	body, err := json.Marshal(networkSpec)
	if err != nil {
		return
	}
	res, err := api.client.restClient.Post(
		api.client.Endpoint+networkUrl,
		"application/json",
		bytes.NewBuffer(body),
		api.client.options.TokenOptions.AccessToken)
	if err != nil {
		return
	}
	defer res.Body.Close()
	task, err = getTask(getError(res))
	return
}

// Deletes a network with specified ID.
func (api *NetworksAPI) Delete(id string) (task *Task, err error) {
	res, err := api.client.restClient.Delete(api.client.Endpoint+networkUrl+"/"+id, api.client.options.TokenOptions.AccessToken)
	if err != nil {
		return
	}
	defer res.Body.Close()
	task, err = getTask(getError(res))
	return
}

// Gets a network with the specified ID.
func (api *NetworksAPI) Get(id string) (network *Network, err error) {
	res, err := api.client.restClient.Get(api.client.Endpoint+networkUrl+"/"+id, api.client.options.TokenOptions.AccessToken)
	if err != nil {
		return
	}
	defer res.Body.Close()
	res, err = getError(res)
	if err != nil {
		return
	}
	var result Network
	err = json.NewDecoder(res.Body).Decode(&result)
	return &result, nil
}

// Returns all networks
func (api *NetworksAPI) GetAll(options *NetworkGetOptions) (result *Networks, err error) {
	uri := api.client.Endpoint + networkUrl
	if options != nil {
		uri += getQueryString(options)
	}
	res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)

	result = &Networks{}
	err = json.Unmarshal(res, result)
	return
}

// Sets default network.
func (api *NetworksAPI) SetDefault(id string) (task *Task, err error) {
	res, err := api.client.restClient.Post(
		api.client.Endpoint+networkUrl+"/"+id+"/set_default",
		"application/json",
		nil,
		api.client.options.TokenOptions.AccessToken)
	if err != nil {
		return
	}
	defer res.Body.Close()
	task, err = getTask(getError(res))
	return
}
<commit_msg>network: pass empty JSON instead of nil to setDefault API<commit_after>// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.

package photon

import (
	"bytes"
	"encoding/json"
)

// Contains functionality for networks API.
type NetworksAPI struct {
	client *Client
}

// Options used for GetAll API
type NetworkGetOptions struct {
	Name string `urlParam:"name"`
}

var networkUrl string = "/networks"

// Creates a network.
func (api *NetworksAPI) Create(networkSpec *NetworkCreateSpec) (task *Task, err error) {
	body, err := json.Marshal(networkSpec)
	if err != nil {
		return
	}
	res, err := api.client.restClient.Post(
		api.client.Endpoint+networkUrl,
		"application/json",
		bytes.NewBuffer(body),
		api.client.options.TokenOptions.AccessToken)
	if err != nil {
		return
	}
	defer res.Body.Close()
	task, err = getTask(getError(res))
	return
}

// Deletes a network with specified ID.
func (api *NetworksAPI) Delete(id string) (task *Task, err error) {
	res, err := api.client.restClient.Delete(api.client.Endpoint+networkUrl+"/"+id, api.client.options.TokenOptions.AccessToken)
	if err != nil {
		return
	}
	defer res.Body.Close()
	task, err = getTask(getError(res))
	return
}

// Gets a network with the specified ID.
func (api *NetworksAPI) Get(id string) (network *Network, err error) {
	res, err := api.client.restClient.Get(api.client.Endpoint+networkUrl+"/"+id, api.client.options.TokenOptions.AccessToken)
	if err != nil {
		return
	}
	defer res.Body.Close()
	res, err = getError(res)
	if err != nil {
		return
	}
	var result Network
	err = json.NewDecoder(res.Body).Decode(&result)
	return &result, nil
}

// Returns all networks
func (api *NetworksAPI) GetAll(options *NetworkGetOptions) (result *Networks, err error) {
	uri := api.client.Endpoint + networkUrl
	if options != nil {
		uri += getQueryString(options)
	}
	res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)

	result = &Networks{}
	err = json.Unmarshal(res, result)
	return
}

// Sets default network.
func (api *NetworksAPI) SetDefault(id string) (task *Task, err error) {
	res, err := api.client.restClient.Post(
		api.client.Endpoint+networkUrl+"/"+id+"/set_default",
		"application/json",
		bytes.NewBuffer([]byte("")),
		api.client.options.TokenOptions.AccessToken)
	if err != nil {
		return
	}
	defer res.Body.Close()
	task, err = getTask(getError(res))
	return
}
<|endoftext|>


<commit_before>// Copyright 2017 Kumina, https://kumina.nl/
// 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 (
	"bufio"
	"fmt"
	"os"
	"io"
	"log"
	"net/http"
	"path"
	"regexp"
	"strconv"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	client_model "github.com/prometheus/client_model/go"
	"github.com/prometheus/common/expfmt"
	"github.com/prometheus/common/version"
	"github.com/tomasen/fcgi_client"
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	phpfpmSocketPathLabel = "socket_path"
	phpfpmScriptPathLabel = "script_path"

	phpfpmUpDesc = prometheus.NewDesc(
		prometheus.BuildFQName("php", "fpm", "up"),
		"Whether scraping PHP-FPM's metrics was successful.",
		[]string{phpfpmSocketPathLabel}, nil)

	phpfpmAcceptedConnections = prometheus.NewDesc(
		prometheus.BuildFQName("php", "fpm", "accepted_connections_total"),
		"Number of request accepted by the pool.",
		[]string{phpfpmSocketPathLabel}, nil)

	phpfpmStartTime = prometheus.NewDesc(
		prometheus.BuildFQName("php", "fpm", "start_time_seconds"),
		"Unix time when FPM has started or reloaded.",
		[]string{phpfpmSocketPathLabel}, nil)

	phpfpmGauges = map[string]*prometheus.Desc{
		"listen queue": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "listen_queue"),
			"Number of request in the queue of pending connections.",
			[]string{phpfpmSocketPathLabel}, nil),
		"max listen queue": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "max_listen_queue"),
			"Maximum number of requests in the queue of pending connections since FPM has started.",
			[]string{phpfpmSocketPathLabel}, nil),
		"listen queue len": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "listen_queue_length"),
			"The size of the socket queue of pending connections.",
			[]string{phpfpmSocketPathLabel}, nil),
		"idle processes": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "idle_processes"),
			"Number of idle processes.",
			[]string{phpfpmSocketPathLabel}, nil),
		"active processes": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "active_processes"),
			"Number of active processes.",
			[]string{phpfpmSocketPathLabel}, nil),
		"max active processes": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "max_active_processes"),
			"Maximum number of active processes since FPM has started.",
			[]string{phpfpmSocketPathLabel}, nil),
		"max children reached": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "max_children_reached"),
			"Number of times, the process limit has been reached.",
			[]string{phpfpmSocketPathLabel}, nil),
		"slow requests": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "slow_requests"),
			"Enable php-fpm slow-log before you consider this. If this value is non-zero you may have slow php processes.",
			[]string{phpfpmSocketPathLabel}, nil),
	}
)

func CollectStatusFromReader(reader io.Reader, socketPath string, ch chan<- prometheus.Metric) error {
	scanner := bufio.NewScanner(reader)
	re := regexp.MustCompile("^(.*): +(.*)$")

	// Scrape the interesting values:
	for scanner.Scan() {
		fields := re.FindStringSubmatch(scanner.Text())
		if fields == nil {
			return fmt.Errorf("Failed to parse %s", scanner.Text())
		}

		if gauge, ok := phpfpmGauges[fields[1]]; ok {
			f, err := strconv.ParseFloat(fields[2], 64)
			if err != nil {
				return err
			}
			ch <- prometheus.MustNewConstMetric(
				gauge,
				prometheus.GaugeValue,
				f,
				socketPath)
		} else if fields[1] == "accepted conn" {
			f, err := strconv.ParseFloat(fields[2], 64)
			if err != nil {
				return err
			}
			ch <- prometheus.MustNewConstMetric(
				phpfpmAcceptedConnections,
				prometheus.CounterValue,
				f,
				socketPath)
		} else if fields[1] == "start time" {
			location, err := time.LoadLocation("Local")
			if err != nil {
				return err
			}
			since, err := time.ParseInLocation("02/Jan/2006:15:04:05 -0700", fields[2], location)
			if err != nil {
				return err
			}
			f := float64(since.Unix())
			ch <- prometheus.MustNewConstMetric(
				phpfpmStartTime,
				prometheus.GaugeValue,
				f,
				socketPath)
		}
	}
	return nil
}

func CollectStatusFromSocket(path string, statusPath string, ch chan<- prometheus.Metric) error {

	env := make(map[string]string)
	env["SCRIPT_FILENAME"] = statusPath
	env["SCRIPT_NAME"] = statusPath
	env["REQUEST_METHOD"] = "GET"

	fcgi, err := fcgiclient.Dial("unix", path)
	if err != nil {
		return err
	}
	defer fcgi.Close()

	resp, err := fcgi.Get(env)
	if err != nil {
		return err
	}

	return CollectStatusFromReader(resp.Body, path, ch)
}

func CollectMetricsFromScript(socketPaths []string, scriptPaths []string) ([]*client_model.MetricFamily, error) {
	var result []*client_model.MetricFamily

	for _, socketPath := range socketPaths {

		for _, scriptPath := range scriptPaths {
			fcgi, err := fcgiclient.Dial("unix", socketPath)
			if err != nil {
				return result, err
			}
			defer fcgi.Close()

			env := make(map[string]string)
			env["DOCUMENT_ROOT"] = path.Dir(scriptPath)
			env["SCRIPT_FILENAME"] = scriptPath
			env["SCRIPT_NAME"] = path.Base(scriptPath)
			env["REQUEST_METHOD"] = "GET"

			resp, err := fcgi.Get(env)
			if err != nil {
				return result, err
			}

			var parser expfmt.TextParser
			metricFamilies, err := parser.TextToMetricFamilies(resp.Body)
			if err != nil {
				return result, err
			}

			for _, metricFamily := range metricFamilies {
				for _, metric := range metricFamily.Metric {
					socketPathCopy := socketPath
					scriptPathCopy := scriptPath
					metric.Label = append(
						metric.Label,
						&client_model.LabelPair{
							Name:  &phpfpmSocketPathLabel,
							Value: &socketPathCopy,
						},
						&client_model.LabelPair{
							Name:  &phpfpmScriptPathLabel,
							Value: &scriptPathCopy,
						})
				}
				result = append(result, metricFamily)
			}
		}
	}
	return result, nil
}

type PhpfpmExporter struct {
	socketPaths []string
	statusPath  string
}

func NewPhpfpmExporter(socketPaths []string, statusPath string) (*PhpfpmExporter, error) {
	return &PhpfpmExporter{
		socketPaths: socketPaths,
		statusPath:  statusPath,
	}, nil
}

func (e *PhpfpmExporter) Describe(ch chan<- *prometheus.Desc) {
	ch <- phpfpmUpDesc
	ch <- phpfpmAcceptedConnections
	ch <- phpfpmStartTime
	for _, desc := range phpfpmGauges {
		ch <- desc
	}
}

func (e *PhpfpmExporter) Collect(ch chan<- prometheus.Metric) {

	for _, socketPath := range e.socketPaths {
		err := CollectStatusFromSocket(socketPath, e.statusPath, ch)
		if err == nil {
			ch <- prometheus.MustNewConstMetric(
				phpfpmUpDesc,
				prometheus.GaugeValue,
				1.0,
				socketPath)
		} else {
			log.Printf("Failed to scrape socket: %s", err)
			ch <- prometheus.MustNewConstMetric(
				phpfpmUpDesc,
				prometheus.GaugeValue,
				0.0,
				socketPath)
		}
	}
}

func main() {
	var (
		listenAddress        = kingpin.Flag("web.listen-address", "Address to listen on for web interface and telemetry.").Default(":9253").String()
		metricsPath          = kingpin.Flag("web.telemetry-path", "Path under which to expose metrics.").Default("/metrics").String()
		socketPaths          = kingpin.Flag("phpfpm.socket-paths", "Paths of the PHP-FPM sockets.").Strings()
		statusPath           = kingpin.Flag("phpfpm.status-path", "Path which has been configured in PHP-FPM to show status page.").Default("/status").String()
		showVersion           = kingpin.Flag("version", "Print version information.").Bool()
		scriptCollectorPaths = kingpin.Flag("phpfpm.script-collector-paths", "Paths of the PHP file whose output needs to be collected.").Strings()
	)

	kingpin.Parse()

	if *showVersion {
		fmt.Println(version.Print("phpfpm_exporter"))
		os.Exit(0)
	}

	exporter, err := NewPhpfpmExporter(*socketPaths, *statusPath)
	if err != nil {
		panic(err)
	}
	prometheus.MustRegister(exporter)

	if len(*scriptCollectorPaths) != 0 {
		prometheus.DefaultGatherer = prometheus.Gatherers{
			prometheus.DefaultGatherer,
			prometheus.GathererFunc(func() ([]*client_model.MetricFamily, error) {
				return CollectMetricsFromScript(*socketPaths, *scriptCollectorPaths)
			}),
		}
	}

	log.Println("Starting phpfpm_exporter", version.Info())
	log.Println("Build context", version.BuildContext())
	log.Printf("Starting Server: %s", *listenAddress)

	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`

			<title>PHP-FPM Exporter</title>

PHP-FPM Exporter

<a href='` + *metricsPath + `'>Metrics

			`))
	})
	log.Fatal(http.ListenAndServe(*listenAddress, nil))
}
<commit_msg>Go fmt<commit_after>// Copyright 2017 Kumina, https://kumina.nl/
// 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 (
	"bufio"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"regexp"
	"strconv"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	client_model "github.com/prometheus/client_model/go"
	"github.com/prometheus/common/expfmt"
	"github.com/prometheus/common/version"
	"github.com/tomasen/fcgi_client"
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	phpfpmSocketPathLabel = "socket_path"
	phpfpmScriptPathLabel = "script_path"

	phpfpmUpDesc = prometheus.NewDesc(
		prometheus.BuildFQName("php", "fpm", "up"),
		"Whether scraping PHP-FPM's metrics was successful.",
		[]string{phpfpmSocketPathLabel}, nil)

	phpfpmAcceptedConnections = prometheus.NewDesc(
		prometheus.BuildFQName("php", "fpm", "accepted_connections_total"),
		"Number of request accepted by the pool.",
		[]string{phpfpmSocketPathLabel}, nil)

	phpfpmStartTime = prometheus.NewDesc(
		prometheus.BuildFQName("php", "fpm", "start_time_seconds"),
		"Unix time when FPM has started or reloaded.",
		[]string{phpfpmSocketPathLabel}, nil)

	phpfpmGauges = map[string]*prometheus.Desc{
		"listen queue": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "listen_queue"),
			"Number of request in the queue of pending connections.",
			[]string{phpfpmSocketPathLabel}, nil),
		"max listen queue": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "max_listen_queue"),
			"Maximum number of requests in the queue of pending connections since FPM has started.",
			[]string{phpfpmSocketPathLabel}, nil),
		"listen queue len": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "listen_queue_length"),
			"The size of the socket queue of pending connections.",
			[]string{phpfpmSocketPathLabel}, nil),
		"idle processes": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "idle_processes"),
			"Number of idle processes.",
			[]string{phpfpmSocketPathLabel}, nil),
		"active processes": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "active_processes"),
			"Number of active processes.",
			[]string{phpfpmSocketPathLabel}, nil),
		"max active processes": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "max_active_processes"),
			"Maximum number of active processes since FPM has started.",
			[]string{phpfpmSocketPathLabel}, nil),
		"max children reached": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "max_children_reached"),
			"Number of times, the process limit has been reached.",
			[]string{phpfpmSocketPathLabel}, nil),
		"slow requests": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "slow_requests"),
			"Enable php-fpm slow-log before you consider this. If this value is non-zero you may have slow php processes.",
			[]string{phpfpmSocketPathLabel}, nil),
	}
)

func CollectStatusFromReader(reader io.Reader, socketPath string, ch chan<- prometheus.Metric) error {
	scanner := bufio.NewScanner(reader)
	re := regexp.MustCompile("^(.*): +(.*)$")

	// Scrape the interesting values:
	for scanner.Scan() {
		fields := re.FindStringSubmatch(scanner.Text())
		if fields == nil {
			return fmt.Errorf("Failed to parse %s", scanner.Text())
		}

		if gauge, ok := phpfpmGauges[fields[1]]; ok {
			f, err := strconv.ParseFloat(fields[2], 64)
			if err != nil {
				return err
			}
			ch <- prometheus.MustNewConstMetric(
				gauge,
				prometheus.GaugeValue,
				f,
				socketPath)
		} else if fields[1] == "accepted conn" {
			f, err := strconv.ParseFloat(fields[2], 64)
			if err != nil {
				return err
			}
			ch <- prometheus.MustNewConstMetric(
				phpfpmAcceptedConnections,
				prometheus.CounterValue,
				f,
				socketPath)
		} else if fields[1] == "start time" {
			location, err := time.LoadLocation("Local")
			if err != nil {
				return err
			}
			since, err := time.ParseInLocation("02/Jan/2006:15:04:05 -0700", fields[2], location)
			if err != nil {
				return err
			}
			f := float64(since.Unix())
			ch <- prometheus.MustNewConstMetric(
				phpfpmStartTime,
				prometheus.GaugeValue,
				f,
				socketPath)
		}
	}
	return nil
}

func CollectStatusFromSocket(path string, statusPath string, ch chan<- prometheus.Metric) error {

	env := make(map[string]string)
	env["SCRIPT_FILENAME"] = statusPath
	env["SCRIPT_NAME"] = statusPath
	env["REQUEST_METHOD"] = "GET"

	fcgi, err := fcgiclient.Dial("unix", path)
	if err != nil {
		return err
	}
	defer fcgi.Close()

	resp, err := fcgi.Get(env)
	if err != nil {
		return err
	}

	return CollectStatusFromReader(resp.Body, path, ch)
}

func CollectMetricsFromScript(socketPaths []string, scriptPaths []string) ([]*client_model.MetricFamily, error) {
	var result []*client_model.MetricFamily

	for _, socketPath := range socketPaths {

		for _, scriptPath := range scriptPaths {
			fcgi, err := fcgiclient.Dial("unix", socketPath)
			if err != nil {
				return result, err
			}
			defer fcgi.Close()

			env := make(map[string]string)
			env["DOCUMENT_ROOT"] = path.Dir(scriptPath)
			env["SCRIPT_FILENAME"] = scriptPath
			env["SCRIPT_NAME"] = path.Base(scriptPath)
			env["REQUEST_METHOD"] = "GET"

			resp, err := fcgi.Get(env)
			if err != nil {
				return result, err
			}

			var parser expfmt.TextParser
			metricFamilies, err := parser.TextToMetricFamilies(resp.Body)
			if err != nil {
				return result, err
			}

			for _, metricFamily := range metricFamilies {
				for _, metric := range metricFamily.Metric {
					socketPathCopy := socketPath
					scriptPathCopy := scriptPath
					metric.Label = append(
						metric.Label,
						&client_model.LabelPair{
							Name:  &phpfpmSocketPathLabel,
							Value: &socketPathCopy,
						},
						&client_model.LabelPair{
							Name:  &phpfpmScriptPathLabel,
							Value: &scriptPathCopy,
						})
				}
				result = append(result, metricFamily)
			}
		}
	}
	return result, nil
}

type PhpfpmExporter struct {
	socketPaths []string
	statusPath  string
}

func NewPhpfpmExporter(socketPaths []string, statusPath string) (*PhpfpmExporter, error) {
	return &PhpfpmExporter{
		socketPaths: socketPaths,
		statusPath:  statusPath,
	}, nil
}

func (e *PhpfpmExporter) Describe(ch chan<- *prometheus.Desc) {
	ch <- phpfpmUpDesc
	ch <- phpfpmAcceptedConnections
	ch <- phpfpmStartTime
	for _, desc := range phpfpmGauges {
		ch <- desc
	}
}

func (e *PhpfpmExporter) Collect(ch chan<- prometheus.Metric) {

	for _, socketPath := range e.socketPaths {
		err := CollectStatusFromSocket(socketPath, e.statusPath, ch)
		if err == nil {
			ch <- prometheus.MustNewConstMetric(
				phpfpmUpDesc,
				prometheus.GaugeValue,
				1.0,
				socketPath)
		} else {
			log.Printf("Failed to scrape socket: %s", err)
			ch <- prometheus.MustNewConstMetric(
				phpfpmUpDesc,
				prometheus.GaugeValue,
				0.0,
				socketPath)
		}
	}
}

func main() {
	var (
		listenAddress        = kingpin.Flag("web.listen-address", "Address to listen on for web interface and telemetry.").Default(":9253").String()
		metricsPath          = kingpin.Flag("web.telemetry-path", "Path under which to expose metrics.").Default("/metrics").String()
		socketPaths          = kingpin.Flag("phpfpm.socket-paths", "Paths of the PHP-FPM sockets.").Strings()
		statusPath           = kingpin.Flag("phpfpm.status-path", "Path which has been configured in PHP-FPM to show status page.").Default("/status").String()
		showVersion          = kingpin.Flag("version", "Print version information.").Bool()
		scriptCollectorPaths = kingpin.Flag("phpfpm.script-collector-paths", "Paths of the PHP file whose output needs to be collected.").Strings()
	)

	kingpin.Parse()

	if *showVersion {
		fmt.Println(version.Print("phpfpm_exporter"))
		os.Exit(0)
	}

	exporter, err := NewPhpfpmExporter(*socketPaths, *statusPath)
	if err != nil {
		panic(err)
	}
	prometheus.MustRegister(exporter)

	if len(*scriptCollectorPaths) != 0 {
		prometheus.DefaultGatherer = prometheus.Gatherers{
			prometheus.DefaultGatherer,
			prometheus.GathererFunc(func() ([]*client_model.MetricFamily, error) {
				return CollectMetricsFromScript(*socketPaths, *scriptCollectorPaths)
			}),
		}
	}

	log.Println("Starting phpfpm_exporter", version.Info())
	log.Println("Build context", version.BuildContext())
	log.Printf("Starting Server: %s", *listenAddress)

	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`

			<title>PHP-FPM Exporter</title>

PHP-FPM Exporter

<a href='` + *metricsPath + `'>Metrics

			`))
	})
	log.Fatal(http.ListenAndServe(*listenAddress, nil))
}
<|endoftext|>


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

package backup

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
)

const (
	backupTmpDir         = "tmp"
	backupFilePerm       = 0600
	backupFilenameSuffix = "etcd.backup"
)

// ensure fileBackend satisfies backend interface.
var _ backend = &fileBackend{}

type fileBackend struct {
	dir string
}

func (fb *fileBackend) save(version string, snapRev int64, rc io.ReadCloser) error {
	filename := makeBackupName(version, snapRev)
	tmpfile, err := os.OpenFile(filepath.Join(fb.dir, backupTmpDir, filename), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, backupFilePerm)
	if err != nil {
		return fmt.Errorf("failed to create snapshot tempfile: %v", err)
	}
	n, err := io.Copy(tmpfile, rc)
	if err != nil {
		tmpfile.Close()
		os.Remove(tmpfile.Name())
		return fmt.Errorf("failed to save snapshot: %v", err)
	}
	tmpfile.Close()

	nextSnapshotName := filepath.Join(fb.dir, filename)
	err = os.Rename(tmpfile.Name(), nextSnapshotName)
	if err != nil {
		os.Remove(tmpfile.Name())
		return fmt.Errorf("rename snapshot from %s to %s failed: %v", tmpfile.Name(), nextSnapshotName, err)
	}
	log.Printf("saved snapshot %s (size: %d) successfully", nextSnapshotName, n)
	return nil
}

func (fb *fileBackend) getLatest() (string, io.ReadCloser, error) {
	files, err := ioutil.ReadDir(fb.dir)
	if err != nil {
		return "", nil, fmt.Errorf("failed to list dir (%s): error (%v)", fb.dir, err)
	}

	var names []string
	for _, f := range files {
		names = append(names, f.Name())
	}

	fn := getLatestBackupName(names)
	if fn == "" {
		return "", nil, nil
	}
	f, err := os.Open(path.Join(fb.dir, fn))
	return fn, f, err
}

func (fb *fileBackend) purge(maxBackupFiles int) {
	files, err := ioutil.ReadDir(fb.dir)
	if err != nil {
		return
	}

	var names []string
	for _, f := range files {
		names = append(names, f.Name())
	}

	bnames := filterAndSortBackups(names)
	if len(bnames) < maxBackupFiles {
		return
	}
	for i := 0; i < len(bnames)-maxBackupFiles; i++ {
		err := os.Remove(path.Join(fb.dir, bnames[i]))
		if err != nil {
			log.Printf("failed to remove backup file: %s", bnames[i])
		} else {
			log.Printf("removed backup file: %s", bnames[i])
		}
	}
}
<commit_msg>backup: fsync before close<commit_after>// Copyright 2016 The etcd-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package backup

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"

	"github.com/Sirupsen/logrus"
)

const (
	backupTmpDir         = "tmp"
	backupFilePerm       = 0600
	backupFilenameSuffix = "etcd.backup"
)

// ensure fileBackend satisfies backend interface.
var _ backend = &fileBackend{}

type fileBackend struct {
	dir string
}

func (fb *fileBackend) save(version string, snapRev int64, rc io.ReadCloser) error {
	filename := makeBackupName(version, snapRev)
	tmpfile, err := os.OpenFile(filepath.Join(fb.dir, backupTmpDir, filename), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, backupFilePerm)
	if err != nil {
		return fmt.Errorf("failed to create snapshot tempfile: %v", err)
	}
	n, err := io.Copy(tmpfile, rc)
	if err != nil {
		tmpfile.Close()
		os.Remove(tmpfile.Name())
		return fmt.Errorf("failed to save snapshot: %v", err)
	}
	err = tmpfile.Sync()
	if err != nil {
		logrus.Warningf("filebackend: failed to sync backup file %s (%v)", filename, err)
	}
	tmpfile.Close()

	nextSnapshotName := filepath.Join(fb.dir, filename)
	err = os.Rename(tmpfile.Name(), nextSnapshotName)
	if err != nil {
		os.Remove(tmpfile.Name())
		return fmt.Errorf("rename snapshot from %s to %s failed: %v", tmpfile.Name(), nextSnapshotName, err)
	}

	logrus.Infof("saved snapshot %s (size: %d) successfully", nextSnapshotName, n)
	return nil
}

func (fb *fileBackend) getLatest() (string, io.ReadCloser, error) {
	files, err := ioutil.ReadDir(fb.dir)
	if err != nil {
		return "", nil, fmt.Errorf("failed to list dir (%s): error (%v)", fb.dir, err)
	}

	var names []string
	for _, f := range files {
		names = append(names, f.Name())
	}

	fn := getLatestBackupName(names)
	if fn == "" {
		return "", nil, nil
	}
	f, err := os.Open(path.Join(fb.dir, fn))
	return fn, f, err
}

func (fb *fileBackend) purge(maxBackupFiles int) {
	files, err := ioutil.ReadDir(fb.dir)
	if err != nil {
		return
	}

	var names []string
	for _, f := range files {
		names = append(names, f.Name())
	}

	bnames := filterAndSortBackups(names)
	if len(bnames) < maxBackupFiles {
		return
	}
	for i := 0; i < len(bnames)-maxBackupFiles; i++ {
		err := os.Remove(path.Join(fb.dir, bnames[i]))
		if err != nil {
			log.Printf("failed to remove backup file: %s", bnames[i])
		} else {
			log.Printf("removed backup file: %s", bnames[i])
		}
	}
}
<|endoftext|>