<commit_before>package google_calendar

/*
 * Copyright 2016 Albert P. Tobey <atobey@netflix.com>
 *
 * 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.
 */

// TODO: announce start / end

import (
	"fmt"
	"log"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/netflix/hal-9001/hal"
)

const Usage = `
Even when attached, this plugin will not do anything until it is fully configured
for the room. At a mininum the calendar-id needs to be set. One or all of autoreply,
announce-start, and announce-end should be set to true to make anything happen.

Setting up:

    !prefs set --room <roomid> --plugin google_calendar --key calendar-id --value <calendar link>

    autoreply: when set to true, the bot will reply with a message for any activity in the
    room during hours when an event exists on the calendar. If the event has a description
    set, that will be the text sent to the room. Otherwise a default message is generated.
    !prefs set --room <roomid> --plugin google_calendar --key autoreply --value true

    announce-(start|end): the bot will automatically announce when an event is starting or
    ending. The event's description will be included if it is not empty.
    !prefs set --room <roomid> --plugin google_calendar --key announce-start --value true
    !prefs set --room <roomid> --plugin google_calendar --key announce-end --value true

    timezone: optional, tells the bot which timezone to report dates in
    !prefs set --room <roomid> --plugin google_calendar --key timezone --value America/Los_Angeles
`

const DefaultTz = "America/Los_Angeles"
const DefaultMsg = "Calendar event: %q"

type Config struct {
	RoomId        string
	CalendarId    string
	Timezone      time.Location
	Autoreply     bool
	AnnounceStart bool
	AnnounceEnd   bool
	CalEvents     []CalEvent
	LastReply     time.Time
	mut           sync.Mutex
	configTs      time.Time
	calTs         time.Time
}

var configCache map[string]*Config
var topMut sync.Mutex

func init() {
	configCache = make(map[string]*Config)
}

func Register() {
	p := hal.Plugin{
		Name: "google_calendar",
		Func: handleEvt,
		Init: initData,
	}

	p.Register()
}

// initData primes the cache and starts the background goroutine
func initData(inst *hal.Instance) {
	topMut.Lock()
	config := Config{RoomId: inst.RoomId}
	configCache[inst.RoomId] = &config
	topMut.Unlock()

	go func() {
		// wait one minute before kicking off the background refresh
		time.Sleep(time.Minute)

		pf := hal.PeriodicFunc{
			Name:     "google_calendar-" + inst.RoomId,
			Interval: time.Minute * 10,
			Function: func() { updateCachedCalEvents(inst.RoomId) },
		}
		pf.Register()
	}()
}

// handleEvt handles events coming in from the chat system. It does not interact
// directly with the calendar API and relies on the background goroutine to populate
// the cache.
func handleEvt(evt hal.Evt) {
	if strings.HasPrefix(strings.TrimSpace(evt.Body), "!") {
		handleCommand(&evt)
		return
	}

	now := time.Now()
	config := getCachedConfig(evt.RoomId, now)
	calEvents, err := config.getCachedCalEvents(now)
	if err != nil {
		evt.Replyf("Error while getting calendar data: %s", err)
		return
	}

	for _, e := range calEvents {
		if config.Autoreply && e.Start.Before(now) && e.End.After(now) {
			lastReplyAge := now.Sub(config.LastReply)
			// TODO: track more detailed state to make squelching replies easier
			// for now: only reply once an hour
			if lastReplyAge.Hours() < 1 {
				log.Printf("not autoresponding because a message has been sent in the last hour")
				continue
			}

			if e.Description != "" {
				evt.Reply(e.Description)
			} else {
				evt.Replyf(DefaultMsg, e.Name)
			}

			config.LastReply = now
			// return // TODO: should overlapping events mean multiple messages?
		}
	}
}

func handleCommand(evt *hal.Evt) {
	argv := evt.BodyAsArgv()

	if argv[0] != "!gcal" {
		return
	}

	if len(argv) < 2 {
		evt.Replyf(Usage)
		return
	}

	now := time.Now()
	config := getCachedConfig(evt.RoomId, now)

	switch argv[1] {
	case "status":
		evt.Replyf("Calendar cache is %.f minutes old. Config cache is %.f minutes old.",
			now.Sub(config.calTs).Minutes(), now.Sub(config.configTs).Minutes())
	case "help":
		evt.Replyf(Usage)
	case "expire":
		config.expireCaches()
		evt.Replyf("config & calendar caches expired")
	case "reload":
		config.expireCaches()
		updateCachedCalEvents(evt.RoomId)
		evt.Replyf("reload complete")
	}
}

func updateCachedCalEvents(roomId string) {
	log.Printf("START: updateCachedCalEvents(%q)", roomId)
	topMut.Lock()
	defer topMut.Unlock()

	now := time.Now()

	c := configCache[roomId]

	// update the config from prefs
	c.LoadFromPrefs()

	// force-expire the cache
	c.calTs = now.Add(time.Hour * -2)

	_, err := c.getCachedCalEvents(now)
	if err != nil {
		log.Printf("FAILED: updateCachedCalEvents(%q): %s", roomId, err)
	}

	log.Printf("DONE: updateCachedCalEvents(%q) @ %s", roomId, now)
}

func getCachedConfig(roomId string, now time.Time) *Config {
	topMut.Lock()
	c := configCache[roomId]
	topMut.Unlock()

	age := now.Sub(c.configTs)

	if age.Minutes() > 10 {
		c.LoadFromPrefs()
	}

	return c
}

// getCachedEvents fetches the calendar data from the Google Calendar API,
// holding a mutex while doing so. This prevents handleEvt from firing until
// the first load of data is complete and will block the goroutines for a short
// time.
func (c *Config) getCachedCalEvents(now time.Time) ([]CalEvent, error) {
	c.mut.Lock()
	defer c.mut.Unlock()

	calAge := now.Sub(c.calTs)

	if calAge.Hours() > 1.1 {
		log.Printf("%q's calendar cache appears to be expired after %f hours", c.RoomId, calAge.Hours())
		evts, err := getEvents(c.CalendarId, now)
		if err != nil {
			log.Printf("Error encountered while fetching calendar events: %s", err)
			return nil, err
		} else {
			c.calTs = now
			c.CalEvents = evts
		}
	}

	return c.CalEvents, nil
}

func (c *Config) LoadFromPrefs() error {
	c.mut.Lock()
	defer c.mut.Unlock()

	cidpref := hal.GetPref("", "", c.RoomId, "google_calendar", "calendar-id", "")
	if cidpref.Success {
		c.CalendarId = cidpref.Value
	} else {
		return fmt.Errorf("Failed to load calendar-id preference for room %q: %s", c.RoomId, cidpref.Error)
	}

	c.Autoreply = c.loadBoolPref("autoreply")
	c.AnnounceStart = c.loadBoolPref("announce-start")
	c.AnnounceEnd = c.loadBoolPref("announce-end")

	tzpref := hal.GetPref("", "", c.RoomId, "google_calendar", "timezone", DefaultTz)
	tz, err := time.LoadLocation(tzpref.Value)
	if err != nil {
		return fmt.Errorf("Could not load timezone info for '%s': %s\n", tzpref.Value, err)
	}
	c.Timezone = *tz

	c.configTs = time.Now()

	return nil
}

func (c *Config) expireCaches() {
	c.calTs = time.Time{}
	c.configTs = time.Time{}
}

func (c *Config) loadBoolPref(key string) bool {
	pref := hal.GetPref("", "", c.RoomId, "google_calendar", key, "false")

	val, err := strconv.ParseBool(pref.Value)
	if err != nil {
		log.Printf("unable to parse boolean pref value: %s", err)
		return false
	}

	return val
}
<commit_msg>adjust periodic func startup, use the hal.GetKV for squelching<commit_after>package google_calendar

/*
 * Copyright 2016 Albert P. Tobey <atobey@netflix.com>
 *
 * 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.
 */

// TODO: announce start / end

import (
	"fmt"
	"log"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/netflix/hal-9001/hal"
)

const Usage = `
Even when attached, this plugin will not do anything until it is fully configured
for the room. At a mininum the calendar-id needs to be set. One or all of autoreply,
announce-start, and announce-end should be set to true to make anything happen.

Setting up:

    !prefs set --room <roomid> --plugin google_calendar --key calendar-id --value <calendar link>

    autoreply: when set to true, the bot will reply with a message for any activity in the
    room during hours when an event exists on the calendar. If the event has a description
    set, that will be the text sent to the room. Otherwise a default message is generated.
    !prefs set --room <roomid> --plugin google_calendar --key autoreply --value true

    announce-(start|end): the bot will automatically announce when an event is starting or
    ending. The event's description will be included if it is not empty.
    !prefs set --room <roomid> --plugin google_calendar --key announce-start --value true
    !prefs set --room <roomid> --plugin google_calendar --key announce-end --value true

    timezone: optional, tells the bot which timezone to report dates in
    !prefs set --room <roomid> --plugin google_calendar --key timezone --value America/Los_Angeles
`

const DefaultTz = "America/Los_Angeles"
const DefaultMsg = "Calendar event: %q"

type Config struct {
	RoomId        string
	CalendarId    string
	Timezone      time.Location
	Autoreply     bool
	AnnounceStart bool
	AnnounceEnd   bool
	CalEvents     []CalEvent
	LastReply     time.Time
	mut           sync.Mutex
	configTs      time.Time
	calTs         time.Time
}

var configCache map[string]*Config
var topMut sync.Mutex

func init() {
	configCache = make(map[string]*Config)
}

func Register() {
	p := hal.Plugin{
		Name: "google_calendar",
		Func: handleEvt,
		Init: initData,
	}

	p.Register()
}

// initData primes the cache and starts the background goroutine
func initData(inst *hal.Instance) {
	topMut.Lock()
	config := Config{RoomId: inst.RoomId}
	configCache[inst.RoomId] = &config
	topMut.Unlock()

	pf := hal.PeriodicFunc{
		Name:     "google_calendar-" + inst.RoomId,
		Interval: time.Minute * 10,
		Function: func() { updateCachedCalEvents(inst.RoomId) },
	}
	pf.Register()

	go func() {
		// wait one minute before kicking off the background refresh
		time.Sleep(time.Minute)

		pf.Start()
	}()
}

// handleEvt handles events coming in from the chat system. It does not interact
// directly with the calendar API and relies on the background goroutine to populate
// the cache.
func handleEvt(evt hal.Evt) {
	if strings.HasPrefix(strings.TrimSpace(evt.Body), "!") {
		handleCommand(&evt)
		return
	}

	now := time.Now()
	config := getCachedConfig(evt.RoomId, now)
	calEvents, err := config.getCachedCalEvents(now)
	if err != nil {
		evt.Replyf("Error while getting calendar data: %s", err)
		return
	}

	evtKey := "gcal-notified-" + evt.RoomId + evt.UserId

	for _, e := range calEvents {
		if config.Autoreply && e.Start.Before(now) && e.End.After(now) {
			// use the hal kv store to prevent spamming
			val, err := hal.GetKV(evtKey)
			if err == nil && val == "-" {
				// the key exists so skip it
				log.Printf("not autoresponding because %q exists in kv", evtKey)
				continue
			}

			if e.Description != "" {
				evt.Reply(e.Description)
			} else {
				evt.Replyf(DefaultMsg, e.Name)
			}

			hal.SetKV(evtKey, "-", time.Hour)

			// return // TODO: should overlapping events mean multiple messages?
		}
	}
}

func handleCommand(evt *hal.Evt) {
	argv := evt.BodyAsArgv()

	if argv[0] != "!gcal" {
		return
	}

	if len(argv) < 2 {
		evt.Replyf(Usage)
		return
	}

	now := time.Now()
	config := getCachedConfig(evt.RoomId, now)

	switch argv[1] {
	case "status":
		evt.Replyf("Calendar cache is %.f minutes old. Config cache is %.f minutes old.",
			now.Sub(config.calTs).Minutes(), now.Sub(config.configTs).Minutes())
	case "help":
		evt.Replyf(Usage)
	case "expire":
		config.expireCaches()
		evt.Replyf("config & calendar caches expired")
	case "reload":
		config.expireCaches()
		updateCachedCalEvents(evt.RoomId)
		evt.Replyf("reload complete")
	}
}

func updateCachedCalEvents(roomId string) {
	log.Printf("START: updateCachedCalEvents(%q)", roomId)
	topMut.Lock()
	defer topMut.Unlock()

	now := time.Now()

	c := configCache[roomId]

	// update the config from prefs
	c.LoadFromPrefs()

	// force-expire the cache
	c.calTs = now.Add(time.Hour * -2)

	_, err := c.getCachedCalEvents(now)
	if err != nil {
		log.Printf("FAILED: updateCachedCalEvents(%q): %s", roomId, err)
	}

	log.Printf("DONE: updateCachedCalEvents(%q) @ %s", roomId, now)
}

func getCachedConfig(roomId string, now time.Time) *Config {
	topMut.Lock()
	c := configCache[roomId]
	topMut.Unlock()

	age := now.Sub(c.configTs)

	if age.Minutes() > 10 {
		c.LoadFromPrefs()
	}

	return c
}

// getCachedEvents fetches the calendar data from the Google Calendar API,
// holding a mutex while doing so. This prevents handleEvt from firing until
// the first load of data is complete and will block the goroutines for a short
// time.
func (c *Config) getCachedCalEvents(now time.Time) ([]CalEvent, error) {
	c.mut.Lock()
	defer c.mut.Unlock()

	calAge := now.Sub(c.calTs)

	if calAge.Hours() > 1.1 {
		log.Printf("%q's calendar cache appears to be expired after %f hours", c.RoomId, calAge.Hours())
		evts, err := getEvents(c.CalendarId, now)
		if err != nil {
			log.Printf("Error encountered while fetching calendar events: %s", err)
			return nil, err
		} else {
			c.calTs = now
			c.CalEvents = evts
		}
	}

	return c.CalEvents, nil
}

func (c *Config) LoadFromPrefs() error {
	c.mut.Lock()
	defer c.mut.Unlock()

	cidpref := hal.GetPref("", "", c.RoomId, "google_calendar", "calendar-id", "")
	if cidpref.Success {
		c.CalendarId = cidpref.Value
	} else {
		return fmt.Errorf("Failed to load calendar-id preference for room %q: %s", c.RoomId, cidpref.Error)
	}

	c.Autoreply = c.loadBoolPref("autoreply")
	c.AnnounceStart = c.loadBoolPref("announce-start")
	c.AnnounceEnd = c.loadBoolPref("announce-end")

	tzpref := hal.GetPref("", "", c.RoomId, "google_calendar", "timezone", DefaultTz)
	tz, err := time.LoadLocation(tzpref.Value)
	if err != nil {
		return fmt.Errorf("Could not load timezone info for '%s': %s\n", tzpref.Value, err)
	}
	c.Timezone = *tz

	c.configTs = time.Now()

	return nil
}

func (c *Config) expireCaches() {
	c.calTs = time.Time{}
	c.configTs = time.Time{}
}

func (c *Config) loadBoolPref(key string) bool {
	pref := hal.GetPref("", "", c.RoomId, "google_calendar", key, "false")

	val, err := strconv.ParseBool(pref.Value)
	if err != nil {
		log.Printf("unable to parse boolean pref value: %s", err)
		return false
	}

	return val
}
<|endoftext|>


<commit_before>package kamino

import (
	"fmt"
	"net/url"
	"os/exec"

	"github.com/modcloth/go-fileutils"
)

type clone struct {
	*Genome
	workdir string
}

func (creator *clone) cachePath() string {
	return fmt.Sprintf("%s/%s/%s", creator.workdir, creator.Account, creator.Repo)
}

func (creator *clone) cloneCacheIfAvailable() (string, error) {
	if err := creator.updateToRef(creator.cachePath()); err != nil {
		return creator.cloneNoCache()
	}

	return creator.cachePath(), nil
}

func (creator *clone) cloneForceCache() (string, error) {
	if err := creator.updateToRef(creator.cachePath()); err != nil {
		return "", err
	}

	return creator.cachePath(), nil
}

func (creator *clone) cloneCreateCache() (string, error) {
	if err := creator.cloneRepo(creator.cachePath()); err != nil {
		return "", err
	}

	return creator.cachePath(), nil
}

func (creator *clone) cloneNoCache() (string, error) {
	uuid, err := nextUUID()
	if err != nil {
		return "", err
	}

	clonePath := fmt.Sprintf("%s/%s/%s", creator.workdir, creator.Account, uuid)

	if err = creator.cloneRepo(clonePath); err != nil {
		return "", err
	}

	return clonePath, nil
}

func (creator *clone) cloneRepo(dest string) error {
	repoURL := &url.URL{
		Scheme: "https",
		Host:   "github.com",
		Path:   fmt.Sprintf("%s/%s", creator.Account, creator.Repo),
	}

	if creator.APIToken != "" {
		repoURL.User = url.User(creator.APIToken)
	}

	var cloneCmd *exec.Cmd

	if creator.Depth == "" {
		cloneCmd = exec.Command(
			"git", "clone",
			"--quiet",
			repoURL.String(),
			dest,
		)
	} else {
		cloneCmd = exec.Command(
			"git", "clone",
			"--quiet",
			"--depth", creator.Depth,
			repoURL.String(),
			dest,
		)
	}

	if err := cloneCmd.Run(); err != nil {
		return err
	}

	git, err := fileutils.Which("git")
	if err != nil {
		return err
	}

	checkoutCmd := &exec.Cmd{
		Path: git,
		Dir:  dest,
		Args: []string{"git", "checkout", "--force", "--quiet", creator.Ref},
	}

	if err := checkoutCmd.Run(); err != nil {
		fmt.Printf("GOT HERE, err = %q, dest = %q\n", err, dest)
		return err
	}

	return nil
}

func (creator *clone) updateToRef(dest string) error {
	/*
		workflow as follows:
			git reset --hard
			git clean -df
			git fetch
			git checkout -f <ref>
			git symbolic-ref HEAD && git pull --rebase
	*/
	git, err := fileutils.Which("git")
	if err != nil {
		return err
	}

	cmds := []*exec.Cmd{
		&exec.Cmd{
			Path: git,
			Dir:  dest,
			Args: []string{"git", "reset", "--hard", "--quiet"},
		},
		&exec.Cmd{
			Path: git,
			Dir:  dest,
			Args: []string{"git", "clean", "-d", "--force", "--quiet"},
		},
		&exec.Cmd{
			Path: git,
			Dir:  dest,
			Args: []string{"git", "fetch", "--prune", "--quiet"},
		},
		&exec.Cmd{
			Path: git,
			Dir:  dest,
			Args: []string{"git", "checkout", "--force", "--quiet", creator.Ref},
		},
	}

	for _, cmd := range cmds {
		if err := cmd.Run(); err != nil {
			return err
		}
	}

	detectBranch := &exec.Cmd{
		Path: git,
		Dir:  dest,
		Args: []string{"git", "symbolic-ref", "--quiet", "HEAD"},
	}

	// no error => we are on a proper branch (as opposed to a detached HEAD)
	if err := detectBranch.Run(); err == nil {
		pullRebase := &exec.Cmd{
			Path: git,
			Dir:  dest,
			Args: []string{"git", "pull", "--rebase", "--quiet"},
		}

		if err = pullRebase.Run(); err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>Removing `git reset --hard`, since it is redundant<commit_after>package kamino

import (
	"fmt"
	"net/url"
	"os/exec"

	"github.com/modcloth/go-fileutils"
)

type clone struct {
	*Genome
	workdir string
}

func (creator *clone) cachePath() string {
	return fmt.Sprintf("%s/%s/%s", creator.workdir, creator.Account, creator.Repo)
}

func (creator *clone) cloneCacheIfAvailable() (string, error) {
	if err := creator.updateToRef(creator.cachePath()); err != nil {
		return creator.cloneNoCache()
	}

	return creator.cachePath(), nil
}

func (creator *clone) cloneForceCache() (string, error) {
	if err := creator.updateToRef(creator.cachePath()); err != nil {
		return "", err
	}

	return creator.cachePath(), nil
}

func (creator *clone) cloneCreateCache() (string, error) {
	if err := creator.cloneRepo(creator.cachePath()); err != nil {
		return "", err
	}

	return creator.cachePath(), nil
}

func (creator *clone) cloneNoCache() (string, error) {
	uuid, err := nextUUID()
	if err != nil {
		return "", err
	}

	clonePath := fmt.Sprintf("%s/%s/%s", creator.workdir, creator.Account, uuid)

	if err = creator.cloneRepo(clonePath); err != nil {
		return "", err
	}

	return clonePath, nil
}

func (creator *clone) cloneRepo(dest string) error {
	repoURL := &url.URL{
		Scheme: "https",
		Host:   "github.com",
		Path:   fmt.Sprintf("%s/%s", creator.Account, creator.Repo),
	}

	if creator.APIToken != "" {
		repoURL.User = url.User(creator.APIToken)
	}

	var cloneCmd *exec.Cmd

	if creator.Depth == "" {
		cloneCmd = exec.Command(
			"git", "clone",
			"--quiet",
			repoURL.String(),
			dest,
		)
	} else {
		cloneCmd = exec.Command(
			"git", "clone",
			"--quiet",
			"--depth", creator.Depth,
			repoURL.String(),
			dest,
		)
	}

	if err := cloneCmd.Run(); err != nil {
		return err
	}

	git, err := fileutils.Which("git")
	if err != nil {
		return err
	}

	checkoutCmd := &exec.Cmd{
		Path: git,
		Dir:  dest,
		Args: []string{"git", "checkout", "--force", "--quiet", creator.Ref},
	}

	if err := checkoutCmd.Run(); err != nil {
		fmt.Printf("GOT HERE, err = %q, dest = %q\n", err, dest)
		return err
	}

	return nil
}

func (creator *clone) updateToRef(dest string) error {
	/*
		workflow as follows:
			git reset --hard
			git clean -df
			git fetch
			git checkout -f <ref>
			git symbolic-ref HEAD && git pull --rebase
	*/
	git, err := fileutils.Which("git")
	if err != nil {
		return err
	}

	cmds := []*exec.Cmd{
		&exec.Cmd{
			Path: git,
			Dir:  dest,
			Args: []string{"git", "clean", "-d", "--force", "--quiet"},
		},
		&exec.Cmd{
			Path: git,
			Dir:  dest,
			Args: []string{"git", "fetch", "--prune", "--quiet"},
		},
		&exec.Cmd{
			Path: git,
			Dir:  dest,
			Args: []string{"git", "checkout", "--force", "--quiet", creator.Ref},
		},
	}

	for _, cmd := range cmds {
		if err := cmd.Run(); err != nil {
			return err
		}
	}

	detectBranch := &exec.Cmd{
		Path: git,
		Dir:  dest,
		Args: []string{"git", "symbolic-ref", "--quiet", "HEAD"},
	}

	// no error => we are on a proper branch (as opposed to a detached HEAD)
	if err := detectBranch.Run(); err == nil {
		pullRebase := &exec.Cmd{
			Path: git,
			Dir:  dest,
			Args: []string{"git", "pull", "--rebase", "--quiet"},
		}

		if err = pullRebase.Run(); err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
// Use of this source code is governed by a MIT-license.
// See http://olivere.mit-license.org/license.txt for details.

package elastic

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

	"github.com/olivere/elastic/uritemplates"
)

// ClusterStatsService is documented at http://www.elasticsearch.org/guide/en/elasticsearch/reference/1.4/cluster-stats.html.
type ClusterStatsService struct {
	client       *Client
	pretty       bool
	nodeId       []string
	flatSettings *bool
	human        *bool
}

// NewClusterStatsService creates a new ClusterStatsService.
func NewClusterStatsService(client *Client) *ClusterStatsService {
	return &ClusterStatsService{
		client: client,
		nodeId: make([]string, 0),
	}
}

// NodeId is documented as: A comma-separated list of node IDs or names to limit the returned information; use `_local` to return information from the node you're connecting to, leave empty to get information from all nodes.
func (s *ClusterStatsService) NodeId(nodeId []string) *ClusterStatsService {
	s.nodeId = nodeId
	return s
}

// FlatSettings is documented as: Return settings in flat format (default: false).
func (s *ClusterStatsService) FlatSettings(flatSettings bool) *ClusterStatsService {
	s.flatSettings = &flatSettings
	return s
}

// Human is documented as: Whether to return time and byte values in human-readable format..
func (s *ClusterStatsService) Human(human bool) *ClusterStatsService {
	s.human = &human
	return s
}

// Pretty indicates that the JSON response be indented and human readable.
func (s *ClusterStatsService) Pretty(pretty bool) *ClusterStatsService {
	s.pretty = pretty
	return s
}

// buildURL builds the URL for the operation.
func (s *ClusterStatsService) buildURL() (string, url.Values, error) {
	// Build URL
	var err error
	var path string

	if len(s.nodeId) > 0 {
		path, err = uritemplates.Expand("/_cluster/stats/nodes/{node_id}", map[string]string{
			"node_id": strings.Join(s.nodeId, ","),
		})
		if err != nil {
			return "", url.Values{}, err
		}
	} else {
		path, err = uritemplates.Expand("/_cluster/stats", map[string]string{})
		if err != nil {
			return "", url.Values{}, err
		}
	}

	// Add query string parameters
	params := url.Values{}
	if s.pretty {
		params.Set("pretty", "1")
	}
	if s.flatSettings != nil {
		params.Set("flat_settings", fmt.Sprintf("%v", *s.flatSettings))
	}
	if s.human != nil {
		params.Set("human", fmt.Sprintf("%v", *s.human))
	}
	return path, params, nil
}

// Validate checks if the operation is valid.
func (s *ClusterStatsService) Validate() error {
	return nil
}

// Do executes the operation.
func (s *ClusterStatsService) Do() (*ClusterStatsResponse, error) {
	// Check pre-conditions
	if err := s.Validate(); err != nil {
		return nil, err
	}

	// Get URL for request
	path, params, err := s.buildURL()
	if err != nil {
		return nil, err
	}

	// Get HTTP response
	res, err := s.client.PerformRequest("GET", path, params, nil)
	if err != nil {
		return nil, err
	}

	// Return operation response
	ret := new(ClusterStatsResponse)
	if err := json.Unmarshal(res.Body, ret); err != nil {
		return nil, err
	}
	return ret, nil
}

// ClusterStatsResponse is the response of ClusterStatsService.Do.
type ClusterStatsResponse struct {
	Timestamp   int64                `json:"timestamp"`
	ClusterName string               `json:"cluster_name"`
	ClusterUUID string               `json:"uuid"`
	Status      string               `json:"status"`
	Indices     *ClusterStatsIndices `json:"indices"`
	Nodes       *ClusterStatsNodes   `json:"nodes"`
}

type ClusterStatsIndices struct {
	Count       int                             `json:"count"`
	Shards      *ClusterStatsIndicesShards      `json:"shards"`
	Docs        *ClusterStatsIndicesDocs        `json:"shards"`
	Store       *ClusterStatsIndicesStore       `json:"store"`
	FieldData   *ClusterStatsIndicesFieldData   `json:"fielddata"`
	FilterCache *ClusterStatsIndicesFilterCache `json:"filter_cache"`
	IdCache     *ClusterStatsIndicesIdCache     `json:"id_cache"`
	Completion  *ClusterStatsIndicesCompletion  `json:"completion"`
	Segments    *ClusterStatsIndicesSegments    `json:"segments"`
	Percolate   *ClusterStatsIndicesPercolate   `json:"percolate"`
}

type ClusterStatsIndicesShards struct {
	Total       int                             `json:"total"`
	Primaries   int                             `json:"primaries"`
	Replication float64                         `json:"replication"`
	Index       *ClusterStatsIndicesShardsIndex `json:"index"`
}

type ClusterStatsIndicesShardsIndex struct {
	Shards      *ClusterStatsIndicesShardsIndexIntMinMax     `json:"shards"`
	Primaries   *ClusterStatsIndicesShardsIndexIntMinMax     `json:"primaries"`
	Replication *ClusterStatsIndicesShardsIndexFloat64MinMax `json:"replication"`
}

type ClusterStatsIndicesShardsIndexIntMinMax struct {
	Min int     `json:"min"`
	Max int     `json:"max"`
	Avg float64 `json:"avg"`
}

type ClusterStatsIndicesShardsIndexFloat64MinMax struct {
	Min float64 `json:"min"`
	Max float64 `json:"max"`
	Avg float64 `json:"avg"`
}

type ClusterStatsIndicesDocs struct {
	Count   int `json:"count"`
	Deleted int `json:"deleted"`
}

type ClusterStatsIndicesStore struct {
	Size                 string `json:"size"` // e.g. "5.3gb"
	SizeInBytes          int64  `json:"size_in_bytes"`
	ThrottleTime         string `json:"throttle_time"` // e.g. "0s"
	ThrottleTimeInMillis int64  `json:"throttle_time_in_millis"`
}

type ClusterStatsIndicesFieldData struct {
	MemorySize        string `json:"memory_size"` // e.g. "61.3kb"
	MemorySizeInBytes int64  `json:"memory_size_in_bytes"`
	Evictions         int64  `json:"evictions"`
	Fields            map[string]struct {
		MemorySize        string `json:"memory_size"` // e.g. "61.3kb"
		MemorySizeInBytes int64  `json:"memory_size_in_bytes"`
	} `json:"fields"`
}

type ClusterStatsIndicesFilterCache struct {
	MemorySize        string `json:"memory_size"` // e.g. "61.3kb"
	MemorySizeInBytes int64  `json:"memory_size_in_bytes"`
	Evictions         int64  `json:"evictions"`
}

type ClusterStatsIndicesIdCache struct {
	MemorySize        string `json:"memory_size"` // e.g. "61.3kb"
	MemorySizeInBytes int64  `json:"memory_size_in_bytes"`
}

type ClusterStatsIndicesCompletion struct {
	Size        string `json:"size"` // e.g. "61.3kb"
	SizeInBytes int64  `json:"size_in_bytes"`
	Fields      map[string]struct {
		Size        string `json:"size"` // e.g. "61.3kb"
		SizeInBytes int64  `json:"size_in_bytes"`
	} `json:"fields"`
}

type ClusterStatsIndicesSegments struct {
	Count                       int64  `json:"count"`
	Memory                      string `json:"memory"` // e.g. "61.3kb"
	MemoryInBytes               int64  `json:"memory_in_bytes"`
	IndexWriterMemory           string `json:"index_writer_memory"` // e.g. "61.3kb"
	IndexWriterMemoryInBytes    int64  `json:"index_writer_memory_in_bytes"`
	IndexWriterMaxMemory        string `json:"index_writer_max_memory"` // e.g. "61.3kb"
	IndexWriterMaxMemoryInBytes int64  `json:"index_writer_max_memory_in_bytes"`
	VersionMapMemory            string `json:"version_map_memory"` // e.g. "61.3kb"
	VersionMapMemoryInBytes     int64  `json:"version_map_memory_in_bytes"`
	FixedBitSet                 string `json:"fixed_bit_set"` // e.g. "61.3kb"
	FixedBitSetInBytes          int64  `json:"fixed_bit_set_memory_in_bytes"`
}

type ClusterStatsIndicesPercolate struct {
	Total int64 `json:"total"`
	// TODO(oe) The JSON tag here is wrong as of ES 1.5.2 it seems
	Time              string `json:"get_time"` // e.g. "1s"
	TimeInBytes       int64  `json:"time_in_millis"`
	Current           int64  `json:"current"`
	MemorySize        string `json:"memory_size"` // e.g. "61.3kb"
	MemorySizeInBytes int64  `json:"memory_sitze_in_bytes"`
	Queries           int64  `json:"queries"`
}

// ---

type ClusterStatsNodes struct {
	Count    *ClusterStatsNodesCounts       `json:"counts"`
	Versions []string                       `json:"versions"`
	OS       *ClusterStatsNodesOsStats      `json:"os"`
	Process  *ClusterStatsNodesProcessStats `json:"process"`
	JVM      *ClusterStatsNodesJvmStats     `json:"jvm"`
	FS       *ClusterStatsNodesFsStats      `json:"fs"`
	Plugins  []*ClusterStatsNodesPlugin     `json:"plugins"`
}

type ClusterStatsNodesCounts struct {
	Total      int `json:"total"`
	MasterOnly int `json:"master_only"`
	DataOnly   int `json:"data_only"`
	MasterData int `json:"master_data"`
	Client     int `json:"client"`
}

type ClusterStatsNodesOsStats struct {
	AvailableProcessors int                            `json:"available_processors"`
	Mem                 *ClusterStatsNodesOsStatsMem   `json:"mem"`
	CPU                 []*ClusterStatsNodesOsStatsCPU `json:"cpu"`
}

type ClusterStatsNodesOsStatsMem struct {
	Total        string `json:"total"` // e.g. "16gb"
	TotalInBytes int64  `json:"total_in_bytes"`
}

type ClusterStatsNodesOsStatsCPU struct {
	Vendor           string `json:"vendor"`
	Model            string `json:"model"`
	MHz              int    `json:"mhz"`
	TotalCores       int    `json:"total_cores"`
	TotalSockets     int    `json:"total_sockets"`
	CoresPerSocket   int    `json:"cores_per_socket"`
	CacheSize        string `json:"cache_size"` // e.g. "256b"
	CacheSizeInBytes int64  `json:"cache_size_in_bytes"`
	Count            int    `json:"count"`
}

type ClusterStatsNodesProcessStats struct {
	CPU                 *ClusterStatsNodesProcessStatsCPU                 `json:"cpu"`
	OpenFileDescriptors *ClusterStatsNodesProcessStatsOpenFileDescriptors `json:"open_file_descriptors"`
}

type ClusterStatsNodesProcessStatsCPU struct {
	Percent float64 `json:"percent"`
}

type ClusterStatsNodesProcessStatsOpenFileDescriptors struct {
	Min int64 `json:"min"`
	Max int64 `json:"max"`
	Avg int64 `json:"avg"`
}

type ClusterStatsNodesJvmStats struct {
	MaxUptime         string                              `json:"max_uptime"` // e.g. "5h"
	MaxUptimeInMillis int64                               `json:"max_uptime_in_millis"`
	Versions          []*ClusterStatsNodesJvmStatsVersion `json:"versions"`
	Mem               *ClusterStatsNodesJvmStatsMem       `json:"mem"`
	Threads           int64                               `json:"threads"`
}

type ClusterStatsNodesJvmStatsVersion struct {
	Version   string `json:"version"`    // e.g. "1.8.0_45"
	VMName    string `json:"vm_name"`    // e.g. "Java HotSpot(TM) 64-Bit Server VM"
	VMVersion string `json:"vm_version"` // e.g. "25.45-b02"
	VMVendor  string `json:"vm_vendor"`  // e.g. "Oracle Corporation"
	Count     int    `json:"count"`
}

type ClusterStatsNodesJvmStatsMem struct {
	HeapUsed        string `json:"heap_used"`
	HeapUsedInBytes int64  `json:"heap_used_in_bytes"`
	HeapMax         string `json:"heap_max"`
	HeapMaxInBytes  int64  `json:"heap_max_in_bytes"`
}

type ClusterStatsNodesFsStats struct {
	Path                 string `json:"path"`
	Mount                string `json:"mount"`
	Dev                  string `json:"dev"`
	Total                string `json:"total"` // e.g. "930.7gb"`
	TotalInBytes         int64  `json:"total_in_bytes"`
	Free                 string `json:"free"` // e.g. "930.7gb"`
	FreeInBytes          int64  `json:"free_in_bytes"`
	Available            string `json:"available"` // e.g. "930.7gb"`
	AvailableInBytes     int64  `json:"available_in_bytes"`
	DiskReads            int64  `json:"disk_reads"`
	DiskWrites           int64  `json:"disk_writes"`
	DiskIOOp             int64  `json:"disk_io_op"`
	DiskReadSize         string `json:"disk_read_size"` // e.g. "0b"`
	DiskReadSizeInBytes  int64  `json:"disk_read_size_in_bytes"`
	DiskWriteSize        string `json:"disk_write_size"` // e.g. "0b"`
	DiskWriteSizeInBytes int64  `json:"disk_write_size_in_bytes"`
	DiskIOSize           string `json:"disk_io_size"` // e.g. "0b"`
	DiskIOSizeInBytes    int64  `json:"disk_io_size_in_bytes"`
	DiskQueue            string `json:"disk_queue"`
	DiskServiceTime      string `json:"disk_service_time"`
}

type ClusterStatsNodesPlugin struct {
	Name        string `json:"name"`
	Version     string `json:"version"`
	Description string `json:"description"`
	URL         string `json:"url"`
	JVM         bool   `json:"jvm"`
	Site        bool   `json:"site"`
}
<commit_msg>Fix wrong JSON tag<commit_after>// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
// Use of this source code is governed by a MIT-license.
// See http://olivere.mit-license.org/license.txt for details.

package elastic

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

	"github.com/olivere/elastic/uritemplates"
)

// ClusterStatsService is documented at http://www.elasticsearch.org/guide/en/elasticsearch/reference/1.4/cluster-stats.html.
type ClusterStatsService struct {
	client       *Client
	pretty       bool
	nodeId       []string
	flatSettings *bool
	human        *bool
}

// NewClusterStatsService creates a new ClusterStatsService.
func NewClusterStatsService(client *Client) *ClusterStatsService {
	return &ClusterStatsService{
		client: client,
		nodeId: make([]string, 0),
	}
}

// NodeId is documented as: A comma-separated list of node IDs or names to limit the returned information; use `_local` to return information from the node you're connecting to, leave empty to get information from all nodes.
func (s *ClusterStatsService) NodeId(nodeId []string) *ClusterStatsService {
	s.nodeId = nodeId
	return s
}

// FlatSettings is documented as: Return settings in flat format (default: false).
func (s *ClusterStatsService) FlatSettings(flatSettings bool) *ClusterStatsService {
	s.flatSettings = &flatSettings
	return s
}

// Human is documented as: Whether to return time and byte values in human-readable format..
func (s *ClusterStatsService) Human(human bool) *ClusterStatsService {
	s.human = &human
	return s
}

// Pretty indicates that the JSON response be indented and human readable.
func (s *ClusterStatsService) Pretty(pretty bool) *ClusterStatsService {
	s.pretty = pretty
	return s
}

// buildURL builds the URL for the operation.
func (s *ClusterStatsService) buildURL() (string, url.Values, error) {
	// Build URL
	var err error
	var path string

	if len(s.nodeId) > 0 {
		path, err = uritemplates.Expand("/_cluster/stats/nodes/{node_id}", map[string]string{
			"node_id": strings.Join(s.nodeId, ","),
		})
		if err != nil {
			return "", url.Values{}, err
		}
	} else {
		path, err = uritemplates.Expand("/_cluster/stats", map[string]string{})
		if err != nil {
			return "", url.Values{}, err
		}
	}

	// Add query string parameters
	params := url.Values{}
	if s.pretty {
		params.Set("pretty", "1")
	}
	if s.flatSettings != nil {
		params.Set("flat_settings", fmt.Sprintf("%v", *s.flatSettings))
	}
	if s.human != nil {
		params.Set("human", fmt.Sprintf("%v", *s.human))
	}
	return path, params, nil
}

// Validate checks if the operation is valid.
func (s *ClusterStatsService) Validate() error {
	return nil
}

// Do executes the operation.
func (s *ClusterStatsService) Do() (*ClusterStatsResponse, error) {
	// Check pre-conditions
	if err := s.Validate(); err != nil {
		return nil, err
	}

	// Get URL for request
	path, params, err := s.buildURL()
	if err != nil {
		return nil, err
	}

	// Get HTTP response
	res, err := s.client.PerformRequest("GET", path, params, nil)
	if err != nil {
		return nil, err
	}

	// Return operation response
	ret := new(ClusterStatsResponse)
	if err := json.Unmarshal(res.Body, ret); err != nil {
		return nil, err
	}
	return ret, nil
}

// ClusterStatsResponse is the response of ClusterStatsService.Do.
type ClusterStatsResponse struct {
	Timestamp   int64                `json:"timestamp"`
	ClusterName string               `json:"cluster_name"`
	ClusterUUID string               `json:"uuid"`
	Status      string               `json:"status"`
	Indices     *ClusterStatsIndices `json:"indices"`
	Nodes       *ClusterStatsNodes   `json:"nodes"`
}

type ClusterStatsIndices struct {
	Count       int                             `json:"count"`
	Shards      *ClusterStatsIndicesShards      `json:"shards"`
	Docs        *ClusterStatsIndicesDocs        `json:"docs"`
	Store       *ClusterStatsIndicesStore       `json:"store"`
	FieldData   *ClusterStatsIndicesFieldData   `json:"fielddata"`
	FilterCache *ClusterStatsIndicesFilterCache `json:"filter_cache"`
	IdCache     *ClusterStatsIndicesIdCache     `json:"id_cache"`
	Completion  *ClusterStatsIndicesCompletion  `json:"completion"`
	Segments    *ClusterStatsIndicesSegments    `json:"segments"`
	Percolate   *ClusterStatsIndicesPercolate   `json:"percolate"`
}

type ClusterStatsIndicesShards struct {
	Total       int                             `json:"total"`
	Primaries   int                             `json:"primaries"`
	Replication float64                         `json:"replication"`
	Index       *ClusterStatsIndicesShardsIndex `json:"index"`
}

type ClusterStatsIndicesShardsIndex struct {
	Shards      *ClusterStatsIndicesShardsIndexIntMinMax     `json:"shards"`
	Primaries   *ClusterStatsIndicesShardsIndexIntMinMax     `json:"primaries"`
	Replication *ClusterStatsIndicesShardsIndexFloat64MinMax `json:"replication"`
}

type ClusterStatsIndicesShardsIndexIntMinMax struct {
	Min int     `json:"min"`
	Max int     `json:"max"`
	Avg float64 `json:"avg"`
}

type ClusterStatsIndicesShardsIndexFloat64MinMax struct {
	Min float64 `json:"min"`
	Max float64 `json:"max"`
	Avg float64 `json:"avg"`
}

type ClusterStatsIndicesDocs struct {
	Count   int `json:"count"`
	Deleted int `json:"deleted"`
}

type ClusterStatsIndicesStore struct {
	Size                 string `json:"size"` // e.g. "5.3gb"
	SizeInBytes          int64  `json:"size_in_bytes"`
	ThrottleTime         string `json:"throttle_time"` // e.g. "0s"
	ThrottleTimeInMillis int64  `json:"throttle_time_in_millis"`
}

type ClusterStatsIndicesFieldData struct {
	MemorySize        string `json:"memory_size"` // e.g. "61.3kb"
	MemorySizeInBytes int64  `json:"memory_size_in_bytes"`
	Evictions         int64  `json:"evictions"`
	Fields            map[string]struct {
		MemorySize        string `json:"memory_size"` // e.g. "61.3kb"
		MemorySizeInBytes int64  `json:"memory_size_in_bytes"`
	} `json:"fields"`
}

type ClusterStatsIndicesFilterCache struct {
	MemorySize        string `json:"memory_size"` // e.g. "61.3kb"
	MemorySizeInBytes int64  `json:"memory_size_in_bytes"`
	Evictions         int64  `json:"evictions"`
}

type ClusterStatsIndicesIdCache struct {
	MemorySize        string `json:"memory_size"` // e.g. "61.3kb"
	MemorySizeInBytes int64  `json:"memory_size_in_bytes"`
}

type ClusterStatsIndicesCompletion struct {
	Size        string `json:"size"` // e.g. "61.3kb"
	SizeInBytes int64  `json:"size_in_bytes"`
	Fields      map[string]struct {
		Size        string `json:"size"` // e.g. "61.3kb"
		SizeInBytes int64  `json:"size_in_bytes"`
	} `json:"fields"`
}

type ClusterStatsIndicesSegments struct {
	Count                       int64  `json:"count"`
	Memory                      string `json:"memory"` // e.g. "61.3kb"
	MemoryInBytes               int64  `json:"memory_in_bytes"`
	IndexWriterMemory           string `json:"index_writer_memory"` // e.g. "61.3kb"
	IndexWriterMemoryInBytes    int64  `json:"index_writer_memory_in_bytes"`
	IndexWriterMaxMemory        string `json:"index_writer_max_memory"` // e.g. "61.3kb"
	IndexWriterMaxMemoryInBytes int64  `json:"index_writer_max_memory_in_bytes"`
	VersionMapMemory            string `json:"version_map_memory"` // e.g. "61.3kb"
	VersionMapMemoryInBytes     int64  `json:"version_map_memory_in_bytes"`
	FixedBitSet                 string `json:"fixed_bit_set"` // e.g. "61.3kb"
	FixedBitSetInBytes          int64  `json:"fixed_bit_set_memory_in_bytes"`
}

type ClusterStatsIndicesPercolate struct {
	Total int64 `json:"total"`
	// TODO(oe) The JSON tag here is wrong as of ES 1.5.2 it seems
	Time              string `json:"get_time"` // e.g. "1s"
	TimeInBytes       int64  `json:"time_in_millis"`
	Current           int64  `json:"current"`
	MemorySize        string `json:"memory_size"` // e.g. "61.3kb"
	MemorySizeInBytes int64  `json:"memory_sitze_in_bytes"`
	Queries           int64  `json:"queries"`
}

// ---

type ClusterStatsNodes struct {
	Count    *ClusterStatsNodesCounts       `json:"counts"`
	Versions []string                       `json:"versions"`
	OS       *ClusterStatsNodesOsStats      `json:"os"`
	Process  *ClusterStatsNodesProcessStats `json:"process"`
	JVM      *ClusterStatsNodesJvmStats     `json:"jvm"`
	FS       *ClusterStatsNodesFsStats      `json:"fs"`
	Plugins  []*ClusterStatsNodesPlugin     `json:"plugins"`
}

type ClusterStatsNodesCounts struct {
	Total      int `json:"total"`
	MasterOnly int `json:"master_only"`
	DataOnly   int `json:"data_only"`
	MasterData int `json:"master_data"`
	Client     int `json:"client"`
}

type ClusterStatsNodesOsStats struct {
	AvailableProcessors int                            `json:"available_processors"`
	Mem                 *ClusterStatsNodesOsStatsMem   `json:"mem"`
	CPU                 []*ClusterStatsNodesOsStatsCPU `json:"cpu"`
}

type ClusterStatsNodesOsStatsMem struct {
	Total        string `json:"total"` // e.g. "16gb"
	TotalInBytes int64  `json:"total_in_bytes"`
}

type ClusterStatsNodesOsStatsCPU struct {
	Vendor           string `json:"vendor"`
	Model            string `json:"model"`
	MHz              int    `json:"mhz"`
	TotalCores       int    `json:"total_cores"`
	TotalSockets     int    `json:"total_sockets"`
	CoresPerSocket   int    `json:"cores_per_socket"`
	CacheSize        string `json:"cache_size"` // e.g. "256b"
	CacheSizeInBytes int64  `json:"cache_size_in_bytes"`
	Count            int    `json:"count"`
}

type ClusterStatsNodesProcessStats struct {
	CPU                 *ClusterStatsNodesProcessStatsCPU                 `json:"cpu"`
	OpenFileDescriptors *ClusterStatsNodesProcessStatsOpenFileDescriptors `json:"open_file_descriptors"`
}

type ClusterStatsNodesProcessStatsCPU struct {
	Percent float64 `json:"percent"`
}

type ClusterStatsNodesProcessStatsOpenFileDescriptors struct {
	Min int64 `json:"min"`
	Max int64 `json:"max"`
	Avg int64 `json:"avg"`
}

type ClusterStatsNodesJvmStats struct {
	MaxUptime         string                              `json:"max_uptime"` // e.g. "5h"
	MaxUptimeInMillis int64                               `json:"max_uptime_in_millis"`
	Versions          []*ClusterStatsNodesJvmStatsVersion `json:"versions"`
	Mem               *ClusterStatsNodesJvmStatsMem       `json:"mem"`
	Threads           int64                               `json:"threads"`
}

type ClusterStatsNodesJvmStatsVersion struct {
	Version   string `json:"version"`    // e.g. "1.8.0_45"
	VMName    string `json:"vm_name"`    // e.g. "Java HotSpot(TM) 64-Bit Server VM"
	VMVersion string `json:"vm_version"` // e.g. "25.45-b02"
	VMVendor  string `json:"vm_vendor"`  // e.g. "Oracle Corporation"
	Count     int    `json:"count"`
}

type ClusterStatsNodesJvmStatsMem struct {
	HeapUsed        string `json:"heap_used"`
	HeapUsedInBytes int64  `json:"heap_used_in_bytes"`
	HeapMax         string `json:"heap_max"`
	HeapMaxInBytes  int64  `json:"heap_max_in_bytes"`
}

type ClusterStatsNodesFsStats struct {
	Path                 string `json:"path"`
	Mount                string `json:"mount"`
	Dev                  string `json:"dev"`
	Total                string `json:"total"` // e.g. "930.7gb"`
	TotalInBytes         int64  `json:"total_in_bytes"`
	Free                 string `json:"free"` // e.g. "930.7gb"`
	FreeInBytes          int64  `json:"free_in_bytes"`
	Available            string `json:"available"` // e.g. "930.7gb"`
	AvailableInBytes     int64  `json:"available_in_bytes"`
	DiskReads            int64  `json:"disk_reads"`
	DiskWrites           int64  `json:"disk_writes"`
	DiskIOOp             int64  `json:"disk_io_op"`
	DiskReadSize         string `json:"disk_read_size"` // e.g. "0b"`
	DiskReadSizeInBytes  int64  `json:"disk_read_size_in_bytes"`
	DiskWriteSize        string `json:"disk_write_size"` // e.g. "0b"`
	DiskWriteSizeInBytes int64  `json:"disk_write_size_in_bytes"`
	DiskIOSize           string `json:"disk_io_size"` // e.g. "0b"`
	DiskIOSizeInBytes    int64  `json:"disk_io_size_in_bytes"`
	DiskQueue            string `json:"disk_queue"`
	DiskServiceTime      string `json:"disk_service_time"`
}

type ClusterStatsNodesPlugin struct {
	Name        string `json:"name"`
	Version     string `json:"version"`
	Description string `json:"description"`
	URL         string `json:"url"`
	JVM         bool   `json:"jvm"`
	Site        bool   `json:"site"`
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"

	"github.com/apex/apex/function"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/lambda"
	"github.com/tj/docopt"
)

var version = "0.0.1"

const usage = `
  Usage:
    apex deploy [-C path]
    apex invoke [-C path] [-v]
    apex zip [-C path]
    apex -h | --help
    apex --version

  Options:
    -C, --chdir path   Working directory
    -h, --help         Output help information
    -v, --verbose      Output verbose logs
    -V, --version      Output version

  Examples:
    Deploy a function in the current directory
    $ apex deploy

    Invoke a function in the current directory
    $ apex invoke < request.json

    Deploy a function in a different directory
    $ apex deploy -C functions/hello-world

    Output zip of a function in the current directory
    $ apex zip > /tmp/out.zip
`

func main() {
	args, err := docopt.Parse(usage, nil, true, version, false)
	if err != nil {
		log.Fatalf("error: %s", err)
	}

	fn := &function.Function{
		Service: lambda.New(session.New(aws.NewConfig())),
		Path:    ".",
	}

	if dir, ok := args["--chdir"].(string); ok {
		if err := os.Chdir(dir); err != nil {
			log.Fatalf("error: %s", err)
		}
	}

	if err := fn.Open(); err != nil {
		log.Fatalf("error: %s", err)
	}

	switch {
	case args["deploy"].(bool):
		deploy(fn)
	case args["invoke"].(bool):
		invoke(fn, args["--verbose"].(bool))
	case args["zip"].(bool):
		zip(fn)
	}
}

// invoke reads request json from stdin and outputs the responses.
func invoke(fn *function.Function, verbose bool) {
	dec := json.NewDecoder(os.Stdin)

	for {
		var v struct {
			Event   interface{}
			Context interface{}
		}

		err := dec.Decode(&v)

		if err == io.EOF {
			break
		}

		if err != nil {
			log.Fatalf("error: %s", err)
		}

		reply, logs, err := fn.Request(v.Event, v.Context)
		if err != nil {
			log.Fatalf("error: %s", err)
		}

		if verbose {
			io.Copy(os.Stderr, logs)
			fmt.Fprintf(os.Stderr, "\n")
		}

		io.Copy(os.Stdout, reply)
		fmt.Fprintf(os.Stdout, "\n")
	}
}

// deploy creates or updates the function.
func deploy(fn *function.Function) {
	if err := fn.Deploy(); err != nil && err != function.ErrUnchanged {
		log.Fatalf("error: %s", err)
	}
}

// zip outputs the generated archive to stdout.
func zip(fn *function.Function) {
	zip, err := fn.Zip()
	if err != nil {
		log.Fatalf("error: %s", err)
	}

	_, err = io.Copy(os.Stdout, zip)
	if err != nil {
		log.Fatalf("error: %s", err)
	}
}
<commit_msg>remove invoke stderr newline<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"

	"github.com/apex/apex/function"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/lambda"
	"github.com/tj/docopt"
)

var version = "0.0.1"

const usage = `
  Usage:
    apex deploy [-C path]
    apex invoke [-C path] [-v]
    apex zip [-C path]
    apex -h | --help
    apex --version

  Options:
    -C, --chdir path   Working directory
    -h, --help         Output help information
    -v, --verbose      Output verbose logs
    -V, --version      Output version

  Examples:
    Deploy a function in the current directory
    $ apex deploy

    Invoke a function in the current directory
    $ apex invoke < request.json

    Deploy a function in a different directory
    $ apex deploy -C functions/hello-world

    Output zip of a function in the current directory
    $ apex zip > /tmp/out.zip
`

func main() {
	args, err := docopt.Parse(usage, nil, true, version, false)
	if err != nil {
		log.Fatalf("error: %s", err)
	}

	fn := &function.Function{
		Service: lambda.New(session.New(aws.NewConfig())),
		Path:    ".",
	}

	if dir, ok := args["--chdir"].(string); ok {
		if err := os.Chdir(dir); err != nil {
			log.Fatalf("error: %s", err)
		}
	}

	if err := fn.Open(); err != nil {
		log.Fatalf("error: %s", err)
	}

	switch {
	case args["deploy"].(bool):
		deploy(fn)
	case args["invoke"].(bool):
		invoke(fn, args["--verbose"].(bool))
	case args["zip"].(bool):
		zip(fn)
	}
}

// invoke reads request json from stdin and outputs the responses.
func invoke(fn *function.Function, verbose bool) {
	dec := json.NewDecoder(os.Stdin)

	for {
		var v struct {
			Event   interface{}
			Context interface{}
		}

		err := dec.Decode(&v)

		if err == io.EOF {
			break
		}

		if err != nil {
			log.Fatalf("error: %s", err)
		}

		reply, logs, err := fn.Request(v.Event, v.Context)
		if err != nil {
			log.Fatalf("error: %s", err)
		}

		if verbose {
			io.Copy(os.Stderr, logs)
		}

		io.Copy(os.Stdout, reply)
		fmt.Fprintf(os.Stdout, "\n")
	}
}

// deploy creates or updates the function.
func deploy(fn *function.Function) {
	if err := fn.Deploy(); err != nil && err != function.ErrUnchanged {
		log.Fatalf("error: %s", err)
	}
}

// zip outputs the generated archive to stdout.
func zip(fn *function.Function) {
	zip, err := fn.Zip()
	if err != nil {
		log.Fatalf("error: %s", err)
	}

	_, err = io.Copy(os.Stdout, zip)
	if err != nil {
		log.Fatalf("error: %s", err)
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"os"
	"path"
	"strings"
	"text/tabwriter"

	"github.com/exercism/cli/api"
	"github.com/exercism/cli/config"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var (
	viperUserConfig *viper.Viper
	viperAPIConfig  *viper.Viper
)

// configureCmd configures the command-line client with user-specific settings.
var configureCmd = &cobra.Command{
	Use:     "configure",
	Aliases: []string{"c"},
	Short:   "Configure the command-line client.",
	Long: `Configure the command-line client to customize it to your needs.

This lets you set up the CLI to talk to the API on your behalf,
and tells the CLI about your setup so it puts things in the right
places.

You can also override certain default settings to suit your preferences.
`,
	RunE: func(cmd *cobra.Command, args []string) error {
		usrCfg := config.NewEmptyUserConfig()
		err := usrCfg.Load(viperUserConfig)
		if err != nil {
			return err
		}
		usrCfg.Normalize()
		if usrCfg.Workspace == "" {
			dirName := strings.Replace(path.Base(BinaryName), ".exe", "", 1)
			defaultWorkspace := path.Join(usrCfg.Home, dirName)
			_, err := os.Stat(defaultWorkspace)
			// Sorry about the double negative.
			if !os.IsNotExist(err) {
				defaultWorkspace = fmt.Sprintf("%s-1", defaultWorkspace)
			}
			usrCfg.Workspace = defaultWorkspace
		}

		apiCfg := config.NewEmptyAPIConfig()
		err = apiCfg.Load(viperAPIConfig)
		if err != nil {
			return err
		}
		apiCfg.SetDefaults()

		show, err := cmd.Flags().GetBool("show")
		if err != nil {
			return err
		}
		if show {
			defer printCurrentConfig()
		}

		tokenFlag := cmd.Flags().Lookup("token")
		if tokenFlag.Changed {
			skipAuth, _ := cmd.Flags().GetBool("skip-auth")
			err = api.ValidateToken(usrCfg.Token, skipAuth)
			if err != nil {
				return err
			}
		}

		err = usrCfg.Write()
		if err != nil {
			return err
		}

		return apiCfg.Write()
	},
}

func printCurrentConfig() {
	usrCfg, err := config.NewUserConfig()
	if err != nil {
		return
	}
	apiCfg, err := config.NewAPIConfig()
	if err != nil {
		return
	}
	w := tabwriter.NewWriter(Out, 0, 0, 2, ' ', 0)
	defer w.Flush()

	fmt.Fprintln(w, fmt.Sprintf("Config dir:\t%s", config.Dir()))
	fmt.Fprintln(w, fmt.Sprintf("-t, --token\t%s", usrCfg.Token))
	fmt.Fprintln(w, fmt.Sprintf("-w, --workspace\t%s", usrCfg.Workspace))
	fmt.Fprintln(w, fmt.Sprintf("-a, --api\t%s", apiCfg.BaseURL))
	fmt.Fprintln(w, "")
}

func initConfigureCmd() {
	configureCmd.Flags().StringP("token", "t", "", "authentication token used to connect to the site")
	configureCmd.Flags().StringP("workspace", "w", "", "directory for exercism exercises")
	configureCmd.Flags().StringP("api", "a", "", "API base url")
	configureCmd.Flags().BoolP("show", "s", false, "show the current configuration")
	configureCmd.Flags().BoolP("skip-auth", "", false, "skip online token authorization check")

	viperUserConfig = viper.New()
	viperUserConfig.BindPFlag("token", configureCmd.Flags().Lookup("token"))
	viperUserConfig.BindPFlag("workspace", configureCmd.Flags().Lookup("workspace"))

	viperAPIConfig = viper.New()
	viperAPIConfig.BindPFlag("baseurl", configureCmd.Flags().Lookup("api"))
}

func init() {
	RootCmd.AddCommand(configureCmd)

	initConfigureCmd()
}
<commit_msg>Handle existing-token and no-token cases on configure without token flag<commit_after>package cmd

import (
	"fmt"
	"os"
	"path"
	"strings"
	"text/tabwriter"

	"github.com/exercism/cli/api"
	"github.com/exercism/cli/config"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var (
	viperUserConfig *viper.Viper
	viperAPIConfig  *viper.Viper
)

// configureCmd configures the command-line client with user-specific settings.
var configureCmd = &cobra.Command{
	Use:     "configure",
	Aliases: []string{"c"},
	Short:   "Configure the command-line client.",
	Long: `Configure the command-line client to customize it to your needs.

This lets you set up the CLI to talk to the API on your behalf,
and tells the CLI about your setup so it puts things in the right
places.

You can also override certain default settings to suit your preferences.
`,
	RunE: func(cmd *cobra.Command, args []string) error {
		usrCfg := config.NewEmptyUserConfig()
		err := usrCfg.Load(viperUserConfig)
		if err != nil {
			return err
		}
		usrCfg.Normalize()
		if usrCfg.Workspace == "" {
			dirName := strings.Replace(path.Base(BinaryName), ".exe", "", 1)
			defaultWorkspace := path.Join(usrCfg.Home, dirName)
			_, err := os.Stat(defaultWorkspace)
			// Sorry about the double negative.
			if !os.IsNotExist(err) {
				defaultWorkspace = fmt.Sprintf("%s-1", defaultWorkspace)
			}
			usrCfg.Workspace = defaultWorkspace
		}

		apiCfg := config.NewEmptyAPIConfig()
		err = apiCfg.Load(viperAPIConfig)
		if err != nil {
			return err
		}
		apiCfg.SetDefaults()

		show, err := cmd.Flags().GetBool("show")
		if err != nil {
			return err
		}
		if show {
			defer printCurrentConfig()
		}

		switch {
		case cmd.Flags().Lookup("token").Changed:
			// User set new token
			skipAuth, _ := cmd.Flags().GetBool("skip-auth")
			err = api.ValidateToken(usrCfg.Token, skipAuth)
			if err != nil {
				return err
			}
		case usrCfg.Token == "":
			fmt.Fprintln(Out, "There is no token configured, please set it using --token.")
		default:
			// Validate existing token
			skipAuth, _ := cmd.Flags().GetBool("skip-auth")
			err = api.ValidateToken(usrCfg.Token, skipAuth)
			if err != nil {
				if !show {
					defer printCurrentConfig()
				}
				fmt.Fprintln(Out, err)
			}
		}

		err = usrCfg.Write()
		if err != nil {
			return err
		}

		return apiCfg.Write()
	},
}

func printCurrentConfig() {
	usrCfg, err := config.NewUserConfig()
	if err != nil {
		return
	}
	apiCfg, err := config.NewAPIConfig()
	if err != nil {
		return
	}
	w := tabwriter.NewWriter(Out, 0, 0, 2, ' ', 0)
	defer w.Flush()

	fmt.Fprintln(w, "")
	fmt.Fprintln(w, fmt.Sprintf("Config dir:\t%s", config.Dir()))
	fmt.Fprintln(w, fmt.Sprintf("-t, --token\t%s", usrCfg.Token))
	fmt.Fprintln(w, fmt.Sprintf("-w, --workspace\t%s", usrCfg.Workspace))
	fmt.Fprintln(w, fmt.Sprintf("-a, --api\t%s", apiCfg.BaseURL))
	fmt.Fprintln(w, "")
}

func initConfigureCmd() {
	configureCmd.Flags().StringP("token", "t", "", "authentication token used to connect to the site")
	configureCmd.Flags().StringP("workspace", "w", "", "directory for exercism exercises")
	configureCmd.Flags().StringP("api", "a", "", "API base url")
	configureCmd.Flags().BoolP("show", "s", false, "show the current configuration")
	configureCmd.Flags().BoolP("skip-auth", "", false, "skip online token authorization check")

	viperUserConfig = viper.New()
	viperUserConfig.BindPFlag("token", configureCmd.Flags().Lookup("token"))
	viperUserConfig.BindPFlag("workspace", configureCmd.Flags().Lookup("workspace"))

	viperAPIConfig = viper.New()
	viperAPIConfig.BindPFlag("baseurl", configureCmd.Flags().Lookup("api"))
}

func init() {
	RootCmd.AddCommand(configureCmd)

	initConfigureCmd()
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"fmt"
	"net/rpc"
	"os"
	"path/filepath"

	"launchpad.net/gnuflag"

	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/names"
	"launchpad.net/juju-core/utils/exec"
	"launchpad.net/juju-core/utils/fslock"
	"launchpad.net/juju-core/worker/uniter"
)

var (
	AgentDir = "/var/lib/juju/agents"
	LockDir  = "/var/lib/juju/locks"
)

type RunCommand struct {
	cmd.CommandBase
	unit      string
	commands  string
	showHelp  bool
	noContext bool
}

const runCommandDoc = `
Run the specified commands in the hook context for the unit.

unit-name can be either the unit tag:
 i.e.  unit-ubuntu-0
or the unit id:
 i.e.  ubuntu/0

If --no-context is specified, the <unit-name> positional
argument is not needed.

The commands are executed with '/bin/bash -s', and the output returned.
`

// Info returns usage information for the command.
func (c *RunCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "juju-run",
		Args:    "<unit-name> <commands>",
		Purpose: "run commands in a unit's hook context",
		Doc:     runCommandDoc,
	}
}

func (c *RunCommand) SetFlags(f *gnuflag.FlagSet) {
	f.BoolVar(&c.showHelp, "h", false, "show help on juju-run")
	f.BoolVar(&c.showHelp, "help", false, "")
	f.BoolVar(&c.noContext, "no-context", false, "do not run the command in a unit context")
}

func (c *RunCommand) Init(args []string) error {
	// make sure we aren't in an existing hook context
	if contextId, err := getenv("JUJU_CONTEXT_ID"); err == nil && contextId != "" {
		return fmt.Errorf("juju-run cannot be called from within a hook, have context %q", contextId)
	}
	if !c.noContext {
		if len(args) < 1 {
			return fmt.Errorf("missing unit-name")
		}
		c.unit, args = args[0], args[1:]
		// If the command line param is a unit id (like service/2) we need to
		// change it to the unit tag as that is the format of the agent directory
		// on disk (unit-service-2).
		if names.IsUnit(c.unit) {
			c.unit = names.UnitTag(c.unit)
		}
	}
	if len(args) < 1 {
		return fmt.Errorf("missing commands")
	}
	c.commands, args = args[0], args[1:]
	return cmd.CheckEmpty(args)
}

func (c *RunCommand) Run(ctx *cmd.Context) error {
	if c.showHelp {
		return gnuflag.ErrHelp
	}

	var result *exec.ExecResponse
	var err error
	if c.noContext {
		result, err = c.executeNoContext()
	} else {
		result, err = c.executeInUnitContext()
	}
	if err != nil {
		return err
	}

	ctx.Stdout.Write(result.Stdout)
	ctx.Stderr.Write(result.Stderr)
	return cmd.NewRcPassthroughError(result.Code)
}

func (c *RunCommand) executeInUnitContext() (*exec.ExecResponse, error) {
	unitDir := filepath.Join(AgentDir, c.unit)
	logger.Debugf("looking for unit dir %s", unitDir)
	// make sure the unit exists
	_, err := os.Stat(unitDir)
	if os.IsNotExist(err) {
		return nil, fmt.Errorf("unit %q not found on this machine", c.unit)
	} else if err != nil {
		return nil, err
	}

	socketPath := filepath.Join(unitDir, uniter.RunListenerFile)
	// make sure the socket exists
	client, err := rpc.Dial(uniter.RunListenerNetType, socketPath)
	if err != nil {
		return nil, err
	}
	defer client.Close()

	var result exec.ExecResponse
	err = client.Call(uniter.JujuRunEndpoint, c.commands, &result)
	return &result, err
}

func getLock() (*fslock.Lock, error) {
	return fslock.NewLock(LockDir, "uniter-hook-execution")
}

func (c *RunCommand) executeNoContext() (*exec.ExecResponse, error) {
	// Acquire the uniter hook execution lock to make sure we don't
	// stomp on each other.
	lock, err := getLock()
	if err != nil {
		return nil, err
	}
	lock.Lock("juju-run")
	defer lock.Unlock()

	runCmd := `[ -f "/home/ubuntu/.juju-proxy" ] && . "/home/ubuntu/.juju-proxy"` + "\n" + c.commands

	return exec.RunCommands(
		exec.RunParams{
			Commands: runCmd,
		})
}
<commit_msg>Check the err response from the fslock Lock method.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"fmt"
	"net/rpc"
	"os"
	"path/filepath"

	"launchpad.net/gnuflag"

	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/names"
	"launchpad.net/juju-core/utils/exec"
	"launchpad.net/juju-core/utils/fslock"
	"launchpad.net/juju-core/worker/uniter"
)

var (
	AgentDir = "/var/lib/juju/agents"
	LockDir  = "/var/lib/juju/locks"
)

type RunCommand struct {
	cmd.CommandBase
	unit      string
	commands  string
	showHelp  bool
	noContext bool
}

const runCommandDoc = `
Run the specified commands in the hook context for the unit.

unit-name can be either the unit tag:
 i.e.  unit-ubuntu-0
or the unit id:
 i.e.  ubuntu/0

If --no-context is specified, the <unit-name> positional
argument is not needed.

The commands are executed with '/bin/bash -s', and the output returned.
`

// Info returns usage information for the command.
func (c *RunCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "juju-run",
		Args:    "<unit-name> <commands>",
		Purpose: "run commands in a unit's hook context",
		Doc:     runCommandDoc,
	}
}

func (c *RunCommand) SetFlags(f *gnuflag.FlagSet) {
	f.BoolVar(&c.showHelp, "h", false, "show help on juju-run")
	f.BoolVar(&c.showHelp, "help", false, "")
	f.BoolVar(&c.noContext, "no-context", false, "do not run the command in a unit context")
}

func (c *RunCommand) Init(args []string) error {
	// make sure we aren't in an existing hook context
	if contextId, err := getenv("JUJU_CONTEXT_ID"); err == nil && contextId != "" {
		return fmt.Errorf("juju-run cannot be called from within a hook, have context %q", contextId)
	}
	if !c.noContext {
		if len(args) < 1 {
			return fmt.Errorf("missing unit-name")
		}
		c.unit, args = args[0], args[1:]
		// If the command line param is a unit id (like service/2) we need to
		// change it to the unit tag as that is the format of the agent directory
		// on disk (unit-service-2).
		if names.IsUnit(c.unit) {
			c.unit = names.UnitTag(c.unit)
		}
	}
	if len(args) < 1 {
		return fmt.Errorf("missing commands")
	}
	c.commands, args = args[0], args[1:]
	return cmd.CheckEmpty(args)
}

func (c *RunCommand) Run(ctx *cmd.Context) error {
	if c.showHelp {
		return gnuflag.ErrHelp
	}

	var result *exec.ExecResponse
	var err error
	if c.noContext {
		result, err = c.executeNoContext()
	} else {
		result, err = c.executeInUnitContext()
	}
	if err != nil {
		return err
	}

	ctx.Stdout.Write(result.Stdout)
	ctx.Stderr.Write(result.Stderr)
	return cmd.NewRcPassthroughError(result.Code)
}

func (c *RunCommand) executeInUnitContext() (*exec.ExecResponse, error) {
	unitDir := filepath.Join(AgentDir, c.unit)
	logger.Debugf("looking for unit dir %s", unitDir)
	// make sure the unit exists
	_, err := os.Stat(unitDir)
	if os.IsNotExist(err) {
		return nil, fmt.Errorf("unit %q not found on this machine", c.unit)
	} else if err != nil {
		return nil, err
	}

	socketPath := filepath.Join(unitDir, uniter.RunListenerFile)
	// make sure the socket exists
	client, err := rpc.Dial(uniter.RunListenerNetType, socketPath)
	if err != nil {
		return nil, err
	}
	defer client.Close()

	var result exec.ExecResponse
	err = client.Call(uniter.JujuRunEndpoint, c.commands, &result)
	return &result, err
}

func getLock() (*fslock.Lock, error) {
	return fslock.NewLock(LockDir, "uniter-hook-execution")
}

func (c *RunCommand) executeNoContext() (*exec.ExecResponse, error) {
	// Acquire the uniter hook execution lock to make sure we don't
	// stomp on each other.
	lock, err := getLock()
	if err != nil {
		return nil, err
	}
	err = lock.Lock("juju-run")
	if err != nil {
		return nil, err
	}
	defer lock.Unlock()

	runCmd := `[ -f "/home/ubuntu/.juju-proxy" ] && . "/home/ubuntu/.juju-proxy"` + "\n" + c.commands

	return exec.RunCommands(
		exec.RunParams{
			Commands: runCmd,
		})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"

	"github.com/robdimsdale/wundergo"
)

const (
	WL_CLIENT_ID    = "WL_CLIENT_ID"
	WL_ACCESS_TOKEN = "WL_ACCESS_TOKEN"
)

func main() {
	accessToken := os.Getenv(WL_ACCESS_TOKEN)
	clientID := os.Getenv(WL_CLIENT_ID)

	if accessToken == "" {
		log.Fatal("Access Token must be provided")
	}

	if clientID == "" {
		log.Fatal("Client ID must be provided")
	}

	client := wundergo.NewOauthClient(accessToken, clientID)

	user, err := client.User()
	if err != nil {
		log.Printf("Error getting user: %s\n", err.Error())
	}
	fmt.Printf("%+v\n", user)
}
<commit_msg>Main method creates and deletes new list.<commit_after>package main

import (
	"fmt"
	"log"
	"os"

	"github.com/robdimsdale/wundergo"
)

const (
	WL_CLIENT_ID    = "WL_CLIENT_ID"
	WL_ACCESS_TOKEN = "WL_ACCESS_TOKEN"
)

func main() {
	accessToken := os.Getenv(WL_ACCESS_TOKEN)
	clientID := os.Getenv(WL_CLIENT_ID)

	if accessToken == "" {
		log.Fatal("Access Token must be provided")
	}

	if clientID == "" {
		log.Fatal("Client ID must be provided")
	}

	client := wundergo.NewOauthClient(accessToken, clientID)

	log.Printf("User info\n")
	user, err := client.User()
	if err != nil {
		log.Printf("Error getting user: %s\n", err.Error())
	}
	fmt.Printf("%+v\n", user)
	fmt.Printf("----------------------------------\n")

	log.Printf("Creating new list\n")
	newList, err := client.CreateList("newListTitle")
	if err != nil {
		log.Printf("Error creating list: %s\n", err.Error())
	}
	fmt.Printf("%+v\n", newList)
	fmt.Printf("----------------------------------\n")

	log.Printf("Getting lists\n")
	lists, err := client.Lists()
	if err != nil {
		log.Printf("Error getting lists: %s\n", err.Error())
	}
	fmt.Printf("%+v\n", lists)
	fmt.Printf("----------------------------------\n")

	log.Printf("Deleting new list\n")
	err = client.DeleteList(newList)
	if err != nil {
		log.Printf("Error deleting list: %s\n", err.Error())
	}
	fmt.Printf("----------------------------------\n")

	log.Printf("Getting lists\n")
	lists, err = client.Lists()
	if err != nil {
		log.Printf("Error getting lists: %s\n", err.Error())
	}
	fmt.Printf("%+v\n", lists)
	fmt.Printf("----------------------------------\n")
}
<|endoftext|>


<commit_before>// mark command line tool. available at https://github.com/a8m/mark
package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"

	"github.com/a8m/mark"
)

var (
	input     = flag.String("i", "", "")
	output    = flag.String("o", "", "")
	smarty    = flag.Bool("smartypants", false, "")
	fractions = flag.Bool("fractions", false, "")
)

var usage = `Usage: mark [options...] <input>

Options:
  -i  Specify file input, otherwise use last argument as input file.
      If no input file is specified, read from stdin.
  -o  Specify file output. If none is specified, write to stdout.

  -smartypants  Use "smart" typograhic punctuation for things like
                quotes and dashes.
  -fractions    Traslate fraction like to suitable HTML elements
`

func main() {
	flag.Usage = func() {
		fmt.Fprint(os.Stderr, fmt.Sprintf(usage))
	}
	flag.Parse()
	// read
	var reader *bufio.Reader
	if *input != "" {
		file, err := os.Open(*input)
		if err != nil {
			usageAndExit(fmt.Sprintf("Error to open file input: %s.", *input))
		}
		defer file.Close()
		reader = bufio.NewReader(file)
	} else {
		if stat, err := os.Stdin.Stat(); err == nil && stat.Size() > 0 {
			reader = bufio.NewReader(os.Stdin)
		} else {
			usageAndExit("")
		}
	}
	// collect data
	var data string
	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				break
			}
			usageAndExit("failed to reading input.")
		}
		data += line
	}
	// write
	var (
		err error
		file *os.File = os.Stdout
	)
	if *output != "" {
		if file, err = os.Create(*output); err != nil {
			usageAndExit("error to create the wanted output file.")
		}
	}
	// mark rendering
	opts := mark.DefaultOptions()
	opts.Smartypants = *smarty
	opts.Fractions = *fractions
	m := mark.New(data, opts)
	if _, err := file.WriteString(m.Render()); err != nil {
		usageAndExit(fmt.Sprintf("error writing output to: %s.", file.Name()))
	}
}

func usageAndExit(msg string) {
	if msg != "" {
		fmt.Fprintf(os.Stderr, msg)
		fmt.Fprintf(os.Stderr, "\n\n")
	}
	flag.Usage()
	fmt.Fprintf(os.Stderr, "\n")
	os.Exit(1)
}<commit_msg>fix: test if something being pipe to stdin<commit_after>// mark command line tool. available at https://github.com/a8m/mark
package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"

	"github.com/a8m/mark"
)

var (
	input     = flag.String("i", "", "")
	output    = flag.String("o", "", "")
	smarty    = flag.Bool("smartypants", false, "")
	fractions = flag.Bool("fractions", false, "")
)

var usage = `Usage: mark [options...] <input>

Options:
  -i  Specify file input, otherwise use last argument as input file.
      If no input file is specified, read from stdin.
  -o  Specify file output. If none is specified, write to stdout.

  -smartypants  Use "smart" typograhic punctuation for things like
                quotes and dashes.
  -fractions    Traslate fraction like to suitable HTML elements
`

func main() {
	flag.Usage = func() {
		fmt.Fprint(os.Stderr, fmt.Sprintf(usage))
	}
	flag.Parse()
	// read
	var reader *bufio.Reader
	if *input != "" {
		file, err := os.Open(*input)
		if err != nil {
			usageAndExit(fmt.Sprintf("Error to open file input: %s.", *input))
		}
		defer file.Close()
		reader = bufio.NewReader(file)
	} else {
		stat, err := os.Stdin.Stat()
		if err != nil || (stat.Mode()&os.ModeCharDevice) != 0 {
			usageAndExit("")
		}
		reader = bufio.NewReader(os.Stdin)
	}
	// collect data
	var data string
	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				break
			}
			usageAndExit("failed to reading input.")
		}
		data += line
	}
	// write
	var (
		err  error
		file = os.Stdout
	)
	if *output != "" {
		if file, err = os.Create(*output); err != nil {
			usageAndExit("error to create the wanted output file.")
		}
	}
	// mark rendering
	opts := mark.DefaultOptions()
	opts.Smartypants = *smarty
	opts.Fractions = *fractions
	m := mark.New(data, opts)
	if _, err := file.WriteString(m.Render()); err != nil {
		usageAndExit(fmt.Sprintf("error writing output to: %s.", file.Name()))
	}
}

func usageAndExit(msg string) {
	if msg != "" {
		fmt.Fprintf(os.Stderr, msg)
		fmt.Fprintf(os.Stderr, "\n\n")
	}
	flag.Usage()
	fmt.Fprintf(os.Stderr, "\n")
	os.Exit(1)
}
<|endoftext|>


<commit_before>package main

import (
    "flag"
    "fmt"
    "net/http"
    "os"
    "path/filepath"
    "strings"
    "io/ioutil"
    "io"
    "golang.org/x/crypto/openpgp"
    "golang.org/x/crypto/openpgp/packet"
    "path"
    v "github.com/lukin0110/push/version"
)

const Url string = "https://push.kiwi/"
const UsageString string =
`Usage: push [OPTIONS] file...
       push [--help | --version]

Share a file from the command line. It returns an unique url to share.

Options:

 -e, --email        Share files via email
 -p, --passphrase   Protect files with a password
 -h, --help         Print usage
 -v, --version      Print version information and quit

Examples:

$ push ./nginx.conf
$ push --email dude@example.com ./nginx.conf
`

func UploadFile(url string, file string, email string) (string, error) {
    f, err := os.Open(file)
    if (err != nil) {
        return "", err
    }
    defer f.Close()

    req, err := http.NewRequest("PUT", url, f)
    if (err != nil) {
        return "", err
    }

    if email != "" {
        req.Header.Set("x-email", email)
    }
    fileStat, err1 := f.Stat()
    if err1 == nil {
        req.ContentLength = fileStat.Size()
    }

    //tr := &http.Transport{
    //    TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    //}
    //client := &http.Client{Transport: tr}
    client := &http.Client{}

    res, err := client.Do(req)
    if (err != nil) {
        return "", err
    } else {
        defer res.Body.Close()
    }

    if(res.StatusCode != http.StatusOK) {
        bs, _ := ioutil.ReadAll(res.Body)
        return "", fmt.Errorf("bad status: %s, %s", res.Status, strings.Replace(string(bs), "\n", "", -1))
    } else {
        bs, err1 := ioutil.ReadAll(res.Body)
        if (err1 != nil) {
            return "", err
        }
        return strings.Replace(string(bs), "\n", "", -1), nil
    }
}

func flagBool(nameLong string, nameShort string, value bool, usage string) *bool {
    result := flag.Bool(nameLong, value, usage)
    flag.BoolVar(result, nameShort, value, usage)
    return result
}

func flagString(nameLong string, nameShort string, value string, usage string) *string {
    result := flag.String(nameLong, value, usage)
    flag.StringVar(result, nameShort, value, usage)
    return result
}

func encrypt(fullPath string, passPhrase string) (resultFile *os.File, err error) {
    filename := path.Base(fullPath)
    stats, err := os.Stat(fullPath)
    if err != nil {
        return
    }
    hints := &openpgp.FileHints{IsBinary: true, FileName: filename, ModTime: stats.ModTime()}

    var config *packet.Config = &packet.Config{
        DefaultCompressionAlgo: packet.CompressionNone,
        CompressionConfig: &packet.CompressionConfig{packet.DefaultCompression},
    }

    sourceFile, err := os.Open(fullPath)
    if err != nil {
        return
    }
    defer sourceFile.Close()

    resultFile, err = ioutil.TempFile(filepath.Dir(fullPath), ".pushKiwiPGP")
    if err != nil {
        return
    }
    defer resultFile.Close()

    plaintext, err := openpgp.SymmetricallyEncrypt(resultFile, []byte(passPhrase), hints, config)
    if err != nil {
        return
    }
    defer plaintext.Close()

    // Copy original contents the the writer created previously
    _, err = io.Copy(plaintext, sourceFile)

    return
}

// Main function for the push command. Parses a few flag (optional) options and takes a few file, or more files, as
// command line arguments. All passed and existing files will be pushed, they will all return a shareable url.
func main() {
    // runtime.GOMAXPROCS(runtime.NumCPU() * 2)
    // Overwrite the default help message
    flag.Usage = func() {
    	fmt.Fprintln(os.Stderr, "See 'push --help'.")
    }

    email := flagString("email", "e", "", "Share files via email")
    passPhrase := flagString("passphrase", "p", "", "Protect files with a password")
    help := flagBool("help", "h", false, "Print usage")
    version := flagBool("version", "v", false, "Print version information and quit")
    flag.Parse()

    if *help {
        fmt.Println(UsageString)
        os.Exit(0)
    } else if *version {
        fmt.Println(v.Full())
        os.Exit(0)
    }

    if len(flag.Args()) == 0 {
        fmt.Println(UsageString)
        os.Exit(0)
    }

    var toRemove = make([]string, len(flag.Args()))

    for index, v := range flag.Args() {
        var err error
        fullPath, err := filepath.Abs(v)

        if _, err1 := os.Stat(fullPath); err1 == nil {
            var uploadPath string
            var filename string = filepath.Base(fullPath)

            if *passPhrase != "" {
                var file *os.File
                file, err = encrypt(fullPath, *passPhrase)
                uploadPath, _ = filepath.Abs(file.Name())
                filename += ".gpg"
                toRemove[index] = uploadPath

            } else {
                uploadPath = fullPath
            }

            result, err := UploadFile(Url + filename, uploadPath, *email)
            //fmt.Printf("Uploading: %s, %s\n", uploadPath, *email); result := filename

            if err == nil {
                fmt.Println(result)
            } else {
                fmt.Println(err)
            }
        } else {
            err = fmt.Errorf("Doesn't exist: %s\n", fullPath)
        }

        if err != nil {
            fmt.Print(err)
        }
    }

    // Cleanup temporary encrypted files
    for _, v := range toRemove {
        if v != "" {
            os.Remove(v)
        }
    }
}
<commit_msg>shadow fix<commit_after>package main

import (
    "flag"
    "fmt"
    "net/http"
    "os"
    "path/filepath"
    "strings"
    "io/ioutil"
    "io"
    "golang.org/x/crypto/openpgp"
    "golang.org/x/crypto/openpgp/packet"
    "path"
    v "github.com/lukin0110/push/version"
)

const Url string = "https://push.kiwi/"
const UsageString string =
`Usage: push [OPTIONS] file...
       push [--help | --version]

Share a file from the command line. It returns an unique url to share.

Options:

 -e, --email        Share files via email
 -p, --passphrase   Protect files with a password
 -h, --help         Print usage
 -v, --version      Print version information and quit

Examples:

$ push ./nginx.conf
$ push --email dude@example.com ./nginx.conf
`

func UploadFile(url string, file string, email string) (string, error) {
    f, err := os.Open(file)
    if (err != nil) {
        return "", err
    }
    defer f.Close()

    req, err := http.NewRequest("PUT", url, f)
    if (err != nil) {
        return "", err
    }

    if email != "" {
        req.Header.Set("x-email", email)
    }
    fileStat, err1 := f.Stat()
    if err1 == nil {
        req.ContentLength = fileStat.Size()
    }

    //tr := &http.Transport{
    //    TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    //}
    //client := &http.Client{Transport: tr}
    client := &http.Client{}

    res, err := client.Do(req)
    if (err != nil) {
        return "", err
    } else {
        defer res.Body.Close()
    }

    if(res.StatusCode != http.StatusOK) {
        bs, _ := ioutil.ReadAll(res.Body)
        return "", fmt.Errorf("bad status: %s, %s", res.Status, strings.Replace(string(bs), "\n", "", -1))
    } else {
        bs, err1 := ioutil.ReadAll(res.Body)
        if (err1 != nil) {
            return "", err
        }
        return strings.Replace(string(bs), "\n", "", -1), nil
    }
}

func flagBool(nameLong string, nameShort string, value bool, usage string) *bool {
    result := flag.Bool(nameLong, value, usage)
    flag.BoolVar(result, nameShort, value, usage)
    return result
}

func flagString(nameLong string, nameShort string, value string, usage string) *string {
    result := flag.String(nameLong, value, usage)
    flag.StringVar(result, nameShort, value, usage)
    return result
}

func encrypt(fullPath string, passPhrase string) (resultFile *os.File, err error) {
    filename := path.Base(fullPath)
    stats, err := os.Stat(fullPath)
    if err != nil {
        return
    }
    hints := &openpgp.FileHints{IsBinary: true, FileName: filename, ModTime: stats.ModTime()}

    var config *packet.Config = &packet.Config{
        DefaultCompressionAlgo: packet.CompressionNone,
        CompressionConfig: &packet.CompressionConfig{packet.DefaultCompression},
    }

    sourceFile, err := os.Open(fullPath)
    if err != nil {
        return
    }
    defer sourceFile.Close()

    resultFile, err = ioutil.TempFile(filepath.Dir(fullPath), ".pushKiwiPGP")
    if err != nil {
        return
    }
    defer resultFile.Close()

    plaintext, err := openpgp.SymmetricallyEncrypt(resultFile, []byte(passPhrase), hints, config)
    if err != nil {
        return
    }
    defer plaintext.Close()

    // Copy original contents the the writer created previously
    _, err = io.Copy(plaintext, sourceFile)

    return
}

// Main function for the push command. Parses a few flag (optional) options and takes a few file, or more files, as
// command line arguments. All passed and existing files will be pushed, they will all return a shareable url.
func main() {
    // runtime.GOMAXPROCS(runtime.NumCPU() * 2)
    // Overwrite the default help message
    flag.Usage = func() {
    	fmt.Fprintln(os.Stderr, "See 'push --help'.")
    }

    email := flagString("email", "e", "", "Share files via email")
    passPhrase := flagString("passphrase", "p", "", "Protect files with a password")
    help := flagBool("help", "h", false, "Print usage")
    version := flagBool("version", "v", false, "Print version information and quit")
    flag.Parse()

    if *help {
        fmt.Println(UsageString)
        os.Exit(0)
    } else if *version {
        fmt.Println(v.Full())
        os.Exit(0)
    }

    if len(flag.Args()) == 0 {
        fmt.Println(UsageString)
        os.Exit(0)
    }

    var toRemove = make([]string, len(flag.Args()))

    for index, v := range flag.Args() {
        var err error
        fullPath, err := filepath.Abs(v)

        if _, err1 := os.Stat(fullPath); err1 == nil {
            var uploadPath string
            var filename string = filepath.Base(fullPath)

            if *passPhrase != "" {
                var file *os.File
                file, err = encrypt(fullPath, *passPhrase)
                uploadPath, _ = filepath.Abs(file.Name())
                filename += ".gpg"
                toRemove[index] = uploadPath

            } else {
                uploadPath = fullPath
            }

            result, err := UploadFile(Url + filename, uploadPath, *email)
            //fmt.Printf("Uploading: %s, %s\n", uploadPath, *email); result := filename

            if err == nil {
                fmt.Println(result)
            } else {
                fmt.Println(err)
            }
        } else {
            err = fmt.Errorf("Doesn't exist: %s\n", fullPath)
        }

        if err != nil {
            fmt.Print(err)
        }
    }

    // Cleanup temporary encrypted files
    for _, fp := range toRemove {
        if fp != "" {
            os.Remove(fp)
        }
    }
}
<|endoftext|>


<commit_before>// Copyright ©2014 Dan Kortschak. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package ct provides terminal text coloring and decoration support.
package ct

import (
	"bytes"
	"fmt"
	"reflect"
)

// Color is a basic color list selector. Not all colors are available on all platforms.
type Color int

const (
	Black Color = iota
	Red
	Green
	Yellow
	Blue
	Magenta
	Cyan
	LightGray
	Gray
	BoldRed
	BoldGreen
	BoldYellow
	BoldBlue
	BoldMagenta
	BoldCyan
	White
)

// Fg returns a foreground color Mode based on the provided Color.
func Fg(c Color) Mode { return colorSet | Mode(c)&colorMask }

// Bg returns a Mode background color based on the provided Color.
func Bg(c Color) Mode { return (colorSet | Mode(c)&colorMask) << colorWidth }

// XTermFg returns an XTerm foreground color Mode based on the provided XTerm color.
func XTermFg(c byte) Mode { return (xTermColorSet | Mode(c)) << (2 * colorWidth) }

// XTermBg returns an XTerm background color Mode based on the provided XTerm color.
func XTermBg(c byte) Mode { return (xTermColorSet | Mode(c)) << (2*colorWidth + xTermColorWidth) }

const (
	colorWidth = 5
	colorSet   = 1 << (colorWidth - 1)
	colorMask  = colorSet - 1

	xTermColorWidth = 9
	xTermColorSet   = 1 << (xTermColorWidth - 1)
	xTermColorMask  = xTermColorSet - 1

	colorBits = 2*colorWidth + 2*xTermColorWidth // Fg + Bg + XtermFg + XtermBg
)

// Mode specifies terminal rendering modes via its Render method. Modes should be bitwise or'd together.
// Not all modes are available on all platforms.
//
// A Mode is essentially a bit field of colors and decoration flags. The layout of a Mode is as follows:
//
//  bits 0-3    - color of foreground
//  bit 4       - set foreground color
//  bits 5-7    - color of background
//  bit 8       - set background color
//  bits 9-16   - XTerm color of foreground
//  bit 17      - set XTerm foreground color
//  bits 18-25  - XTerm color of background
//  bit 26      - set XTerm background color
//  bits 27-    - text decoration flags
//  highest bit - do not reset terminal after rendering
//
// XTerm colors take priority over normal terminal colors if supported by the platform.
type Mode uint64

const (
	Reset Mode = 1 << (iota + colorBits)
	Bold
	Faint
	Italic
	Underline
	BlinkSlow
	BlinkRapid
	Negative
	Conceal
	CrossedOut
	NoResetAfter Mode = 1 << 63
)

// This set of constants must be kept in sync with the Mode constants above.
const (
	reset = iota
	bold
	faint
	italic
	underline
	blinkSlow
	blinkRapid
	negative
	conceal
	crossedOut
)

// Render returns a fmt.Formatter that will apply the mode to the
// printed values of the parameters.
func (m Mode) Render(v ...interface{}) fmt.Formatter {
	return text{Mode: m, v: v}
}

type text struct {
	Mode
	v []interface{}
}

func doesString(v interface{}) bool {
	switch v.(type) {
	case string:
		return true
	case fmt.Stringer:
		return true
	case fmt.GoStringer:
		return true
	case fmt.Formatter:
		return true
	default:
		return reflect.ValueOf(v).Kind() == reflect.String
	}
}

// Format allows text to satisfy the fmt.Formatter interface. The format
// behaviour is the same as for fmt.Print.
func (t text) Format(fs fmt.State, c rune) {
	t.Mode.set(fs)

	w, wOk := fs.Width()
	p, pOk := fs.Precision()
	var (
		b          bytes.Buffer
		prevString bool
	)
	b.WriteByte('%')
	for _, f := range "+-# 0" {
		if fs.Flag(int(f)) {
			b.WriteRune(f)
		}
	}
	if wOk {
		fmt.Fprint(&b, w)
	}
	if pOk {
		b.WriteByte('.')
		fmt.Fprint(&b, p)
	}
	b.WriteRune(c)
	format := b.String()

	for _, v := range t.v {
		isString := v != nil && doesString(v)
		if isString && prevString {
			fs.Write([]byte{' '})
		}
		prevString = isString
		fmt.Fprintf(fs, format, v)
	}

	if t.Mode&NoResetAfter == 0 {
		t.reset(fs)
	}
}
<commit_msg>Don't do work unless it's required by the Mode<commit_after>// Copyright ©2014 Dan Kortschak. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package ct provides terminal text coloring and decoration support.
package ct

import (
	"bytes"
	"fmt"
	"reflect"
)

// Color is a basic color list selector. Not all colors are available on all platforms.
type Color int

const (
	Black Color = iota
	Red
	Green
	Yellow
	Blue
	Magenta
	Cyan
	LightGray
	Gray
	BoldRed
	BoldGreen
	BoldYellow
	BoldBlue
	BoldMagenta
	BoldCyan
	White
)

// Fg returns a foreground color Mode based on the provided Color.
func Fg(c Color) Mode { return colorSet | Mode(c)&colorMask }

// Bg returns a Mode background color based on the provided Color.
func Bg(c Color) Mode { return (colorSet | Mode(c)&colorMask) << colorWidth }

// XTermFg returns an XTerm foreground color Mode based on the provided XTerm color.
func XTermFg(c byte) Mode { return (xTermColorSet | Mode(c)) << (2 * colorWidth) }

// XTermBg returns an XTerm background color Mode based on the provided XTerm color.
func XTermBg(c byte) Mode { return (xTermColorSet | Mode(c)) << (2*colorWidth + xTermColorWidth) }

const (
	colorWidth = 5
	colorSet   = 1 << (colorWidth - 1)
	colorMask  = colorSet - 1

	xTermColorWidth = 9
	xTermColorSet   = 1 << (xTermColorWidth - 1)
	xTermColorMask  = xTermColorSet - 1

	colorBits = 2*colorWidth + 2*xTermColorWidth // Fg + Bg + XtermFg + XtermBg
)

// Mode specifies terminal rendering modes via its Render method. Modes should be bitwise or'd together.
// Not all modes are available on all platforms.
//
// A Mode is essentially a bit field of colors and decoration flags. The layout of a Mode is as follows:
//
//  bits 0-3    - color of foreground
//  bit 4       - set foreground color
//  bits 5-7    - color of background
//  bit 8       - set background color
//  bits 9-16   - XTerm color of foreground
//  bit 17      - set XTerm foreground color
//  bits 18-25  - XTerm color of background
//  bit 26      - set XTerm background color
//  bits 27-    - text decoration flags
//  highest bit - do not reset terminal after rendering
//
// XTerm colors take priority over normal terminal colors if supported by the platform.
type Mode uint64

const (
	Reset Mode = 1 << (iota + colorBits)
	Bold
	Faint
	Italic
	Underline
	BlinkSlow
	BlinkRapid
	Negative
	Conceal
	CrossedOut
	NoResetAfter Mode = 1 << 63
)

// This set of constants must be kept in sync with the Mode constants above.
const (
	reset = iota
	bold
	faint
	italic
	underline
	blinkSlow
	blinkRapid
	negative
	conceal
	crossedOut
)

// Render returns a fmt.Formatter that will apply the mode to the
// printed values of the parameters.
func (m Mode) Render(v ...interface{}) fmt.Formatter {
	return text{Mode: m, v: v}
}

type text struct {
	Mode
	v []interface{}
}

func doesString(v interface{}) bool {
	switch v.(type) {
	case string:
		return true
	case fmt.Stringer:
		return true
	case fmt.GoStringer:
		return true
	case fmt.Formatter:
		return true
	default:
		return reflect.ValueOf(v).Kind() == reflect.String
	}
}

// Format allows text to satisfy the fmt.Formatter interface. The format
// behaviour is the same as for fmt.Print.
func (t text) Format(fs fmt.State, c rune) {
	if t.Mode&^NoResetAfter != 0 {
		t.Mode.set(fs)
	}

	w, wOk := fs.Width()
	p, pOk := fs.Precision()
	var (
		b          bytes.Buffer
		prevString bool
	)
	b.WriteByte('%')
	for _, f := range "+-# 0" {
		if fs.Flag(int(f)) {
			b.WriteRune(f)
		}
	}
	if wOk {
		fmt.Fprint(&b, w)
	}
	if pOk {
		b.WriteByte('.')
		fmt.Fprint(&b, p)
	}
	b.WriteRune(c)
	format := b.String()

	for _, v := range t.v {
		isString := v != nil && doesString(v)
		if isString && prevString {
			fs.Write([]byte{' '})
		}
		prevString = isString
		fmt.Fprintf(fs, format, v)
	}

	if t.Mode&^NoResetAfter != 0 && t.Mode&NoResetAfter == 0 {
		t.reset(fs)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/csv"
	"io"
	"os"
	"strconv"
)

const recordSize = 18

const (
	nameField    = 3
	latField     = 4
	longField    = 5
	countryField = 8
	cityField    = 9
	icaoField    = 12
	iataField    = 13
)

func readAirports(file string) (database, error) {
	f, err := os.Open(file)
	defer f.Close()

	if err != nil {
		return nil, err
	}

	db := make(map[string]*airport)
	reader := csv.NewReader(f)
	reader.FieldsPerRecord = recordSize
	header := true

	for {
		record, err := reader.Read()
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, err
		}

		if header {
			header = false
			continue
		}

		apt := new(airport)
		apt.Name = record[nameField]
		apt.Country = record[countryField]
		apt.City = record[cityField]
		apt.Icao = record[icaoField]
		apt.Iata = record[iataField]

		lat, err := strconv.ParseFloat(record[latField], 64)
		if err != nil {
			return nil, err
		}
		apt.Lat = lat

		long, err := strconv.ParseFloat(record[longField], 64)
		if err != nil {
			return nil, err
		}
		apt.Long = long

		db[apt.Iata] = apt
	}

	return db, nil
}
<commit_msg>Use correct city field<commit_after>package main

import (
	"encoding/csv"
	"io"
	"os"
	"strconv"
)

const recordSize = 18

const (
	nameField    = 3
	latField     = 4
	longField    = 5
	countryField = 8
	cityField    = 10
	icaoField    = 12
	iataField    = 13
)

func readAirports(file string) (database, error) {
	f, err := os.Open(file)
	defer f.Close()

	if err != nil {
		return nil, err
	}

	db := make(map[string]*airport)
	reader := csv.NewReader(f)
	reader.FieldsPerRecord = recordSize
	header := true

	for {
		record, err := reader.Read()
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, err
		}

		if header {
			header = false
			continue
		}

		apt := new(airport)
		apt.Name = record[nameField]
		apt.Country = record[countryField]
		apt.City = record[cityField]
		apt.Icao = record[icaoField]
		apt.Iata = record[iataField]

		lat, err := strconv.ParseFloat(record[latField], 64)
		if err != nil {
			return nil, err
		}
		apt.Lat = lat

		long, err := strconv.ParseFloat(record[longField], 64)
		if err != nil {
			return nil, err
		}
		apt.Long = long

		db[apt.Iata] = apt
	}

	return db, nil
}
<|endoftext|>