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

// Read Eval Print Loop for CLI
package cli

import (
	"fmt"
	"io"
	"os"
	"os/user"
	"path/filepath"

	"github.com/go-python/gpython/py"
	"github.com/go-python/gpython/repl"
	"github.com/peterh/liner"
)

const HistoryFileName = ".gpyhistory"

// homeDirectory finds the home directory or returns ""
func homeDirectory() string {
	usr, err := user.Current()
	if err == nil {
		return usr.HomeDir
	}
	// Fall back to reading $HOME - work around user.Current() not
	// working for cross compiled binaries on OSX.
	// https://github.com/golang/go/issues/6376
	return os.Getenv("HOME")
}

// Holds state for readline services
type readline struct {
	*liner.State
	repl        *repl.REPL
	historyFile string
	module      *py.Module
	prompt      string
}

// newReadline creates a new instance of readline
func newReadline(repl *repl.REPL) *readline {
	rl := &readline{
		State: liner.NewLiner(),
		repl:  repl,
	}
	home := homeDirectory()
	if home != "" {
		rl.historyFile = filepath.Join(home, HistoryFileName)
	}
	rl.SetTabCompletionStyle(liner.TabPrints)
	rl.SetWordCompleter(rl.Completer)
	return rl
}

// readHistory reads the history into the term
func (rl *readline) ReadHistory() error {
	f, err := os.Open(rl.historyFile)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = rl.State.ReadHistory(f)
	if err != nil {
		return err
	}
	return nil
}

// writeHistory writes the history from the term
func (rl *readline) WriteHistory() error {
	f, err := os.OpenFile(rl.historyFile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = rl.State.WriteHistory(f)
	if err != nil {
		return err
	}
	return nil
}

// Close the readline and write history
func (rl *readline) Close() error {
	err := rl.State.Close()
	if err != nil {
		return err
	}
	if rl.historyFile != "" {
		err := rl.WriteHistory()
		if err != nil {
			return err
		}
	}
	return nil
}

// Completer takes the currently edited line with the cursor
// position and returns the completion candidates for the partial word
// to be completed. If the line is "Hello, wo!!!" and the cursor is
// before the first '!', ("Hello, wo!!!", 9) is passed to the
// completer which may returns ("Hello, ", {"world", "Word"}, "!!!")
// to have "Hello, world!!!".
func (rl *readline) Completer(line string, pos int) (head string, completions []string, tail string) {
	return rl.repl.Completer(line, pos)
}

// SetPrompt sets the current terminal prompt
func (rl *readline) SetPrompt(prompt string) {
	rl.prompt = prompt
}

// Print prints the output
func (rl *readline) Print(out string) {
	_, _ = os.Stdout.WriteString(out + "\n")
}

// RunREPL starts the REPL loop
func RunREPL() {
	repl := repl.New()
	rl := newReadline(repl)
	repl.SetUI(rl)
	defer rl.Close()
	err := rl.ReadHistory()
	if err != nil {
		fmt.Printf("Failed to open history: %v\n", err)
	}

	for {
		line, err := rl.Prompt(rl.prompt)
		if err != nil {
			if err == io.EOF {
				fmt.Printf("\n")
				break
			}
			fmt.Printf("Problem reading line: %v\n", err)
			continue
		}
		if line != "" {
			rl.AppendHistory(line)
		}
		rl.repl.Run(line)
	}
}
<commit_msg>fix: ~/.gpyhistory: no such file or directory<commit_after>// Copyright 2018 The go-python Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Read Eval Print Loop for CLI
package cli

import (
	"fmt"
	"io"
	"os"
	"os/user"
	"path/filepath"

	"github.com/go-python/gpython/py"
	"github.com/go-python/gpython/repl"
	"github.com/peterh/liner"
)

const HistoryFileName = ".gpyhistory"

// homeDirectory finds the home directory or returns ""
func homeDirectory() string {
	usr, err := user.Current()
	if err == nil {
		return usr.HomeDir
	}
	// Fall back to reading $HOME - work around user.Current() not
	// working for cross compiled binaries on OSX.
	// https://github.com/golang/go/issues/6376
	return os.Getenv("HOME")
}

// Holds state for readline services
type readline struct {
	*liner.State
	repl        *repl.REPL
	historyFile string
	module      *py.Module
	prompt      string
}

// newReadline creates a new instance of readline
func newReadline(repl *repl.REPL) *readline {
	rl := &readline{
		State: liner.NewLiner(),
		repl:  repl,
	}
	home := homeDirectory()
	if home != "" {
		rl.historyFile = filepath.Join(home, HistoryFileName)
	}
	rl.SetTabCompletionStyle(liner.TabPrints)
	rl.SetWordCompleter(rl.Completer)
	return rl
}

// readHistory reads the history into the term
func (rl *readline) ReadHistory() error {
	f, err := os.Open(rl.historyFile)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = rl.State.ReadHistory(f)
	if err != nil {
		return err
	}
	return nil
}

// writeHistory writes the history from the term
func (rl *readline) WriteHistory() error {
	f, err := os.OpenFile(rl.historyFile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = rl.State.WriteHistory(f)
	if err != nil {
		return err
	}
	return nil
}

// Close the readline and write history
func (rl *readline) Close() error {
	err := rl.State.Close()
	if err != nil {
		return err
	}
	if rl.historyFile != "" {
		err := rl.WriteHistory()
		if err != nil {
			return err
		}
	}
	return nil
}

// Completer takes the currently edited line with the cursor
// position and returns the completion candidates for the partial word
// to be completed. If the line is "Hello, wo!!!" and the cursor is
// before the first '!', ("Hello, wo!!!", 9) is passed to the
// completer which may returns ("Hello, ", {"world", "Word"}, "!!!")
// to have "Hello, world!!!".
func (rl *readline) Completer(line string, pos int) (head string, completions []string, tail string) {
	return rl.repl.Completer(line, pos)
}

// SetPrompt sets the current terminal prompt
func (rl *readline) SetPrompt(prompt string) {
	rl.prompt = prompt
}

// Print prints the output
func (rl *readline) Print(out string) {
	_, _ = os.Stdout.WriteString(out + "\n")
}

// RunREPL starts the REPL loop
func RunREPL() {
	repl := repl.New()
	rl := newReadline(repl)
	repl.SetUI(rl)
	defer rl.Close()
	err := rl.ReadHistory()
	if err != nil {
		if !os.IsNotExist(err) {
			fmt.Printf("Failed to open history: %v\n", err)
		}
	}

	for {
		line, err := rl.Prompt(rl.prompt)
		if err != nil {
			if err == io.EOF {
				fmt.Printf("\n")
				break
			}
			fmt.Printf("Problem reading line: %v\n", err)
			continue
		}
		if line != "" {
			rl.AppendHistory(line)
		}
		rl.repl.Run(line)
	}
}
<|endoftext|>


<commit_before>package repo

import (
	"encoding/json"
	"errors"
	log "github.com/sirupsen/logrus"
	"github.com/xshellinc/tools/lib/help"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// DeviceImage contains url, title, username and password which are used after flashing
type DeviceImage struct {
	URL   string `json:"URL"`
	Alias string `json:"Alias,omitempty"`
	Title string `json:"Title,omitempty"`
	User  string `json:"User,omitempty"`
	Pass  string `json:"Pass,omitempty"`
}

// DeviceMapping is a collection of device, it sub-types and sets of images for these devices
type DeviceMapping struct {
	Name   string           `json:"Name"`
	Alias  string           `json:"Alias,omitempty"`
	Sub    []*DeviceMapping `json:"Sub,omitempty"`
	Images []DeviceImage    `json:"Images,omitempty"`
	Type   string

	dir   string
	Image DeviceImage
}

// deviceCollection is a starting point of the collection of images
type DeviceCollection struct {
	Devices []DeviceMapping `json:"Devices"`
	Version string          `json:"Version,omitempty"`
}

// Images repository URL
const imagesRepo = "https://cdn.isaax.io/iotit/mapping.json"

var path string
var dm *DeviceCollection

func init() {
	path = filepath.Join(baseDir, "mapping.json")
}

// GetSubsNames returns array of Names within a `Sub`
func (d *DeviceMapping) GetSubsNames() []string {
	r := make([]string, len(d.Sub))
	for i, o := range d.Sub {
		r[i] = o.Name
	}

	return r
}

// GetImageTitles returns array of image titles
func (d *DeviceMapping) GetImageTitles() []string {
	r := make([]string, len(d.Images))
	for i, o := range d.Images {
		r[i] = o.Title
	}

	return r
}

// Dir - returns directory of a local repo `.iotit/images/{name}`
func (d *DeviceMapping) Dir() string {
	return filepath.Join(ImageDir, d.dir)
}

// FindImage - searches image in the repo
func (d *DeviceMapping) FindImage(image string) error {
	search := strings.ToLower(image)
	for _, obj := range d.Images {
		if strings.ToLower(obj.Title) == search || obj.Alias == search {
			d.Image = obj
			return nil
		}
	}

	return errors.New(image + " unknown image")
}

// findDevice searches device in the repo
func (d *DeviceCollection) findDevice(device string) (*DeviceMapping, error) {
	search := strings.ToLower(device)
	for _, obj := range d.Devices {
		obj.dir = obj.Name
		obj.Type = obj.Name
		if strings.ToLower(obj.Name) == search || obj.Alias == search {
			fillEmptyImages(&obj)
			return &obj, nil
		}
		if len(obj.Sub) > 0 {
			for _, sub := range obj.Sub {
				sub.dir = obj.Name
				sub.Type = obj.Name
				if strings.ToLower(sub.Name) == search || sub.Alias == search {
					if len(sub.Images) == 0 {
						sub.Images = obj.Images
					}
					return sub, nil
				}
			}
		}
	}

	return nil, errors.New(device + " device is not supported")
}

func (d *DeviceCollection) getDevices() []string {
	devices := []string{}
	for _, obj := range d.Devices {
		devices = append(devices, obj.Name)
	}
	return devices
}

// DownloadDevicesRepository downloads new mapping.json from the cloud
func DownloadDevicesRepository() error {
	log.Info("Downloading new mapping.json...")
	if err := help.DownloadFile(path, imagesRepo); err != nil {
		log.Error(err)
		return err
	}
	// update file modification date so on the next run we don't try to download it again
	if err := os.Chtimes(path, time.Now(), time.Now()); err != nil {
		log.Error(err)
		return err
	}

	return nil
}

// CheckDevicesRepository checks mapping.json for updates
func CheckDevicesRepository() {
	log.Info("Checking for mapping.json updates...")
	if info, err := os.Stat(path); os.IsNotExist(err) || time.Now().Sub(info.ModTime()).Hours() >= 24 {
		DownloadDevicesRepository()
	}
}

// SetPath of the mapping.json file
func SetPath(p string) {
	path = p
}

// GetAllRepos returns all repo Names
func GetAllRepos() ([]string, error) {
	if dm == nil {
		if err := initDeviceCollection(); err != nil {
			return nil, err
		}
	}

	str := make([]string, 0)

	for _, d := range dm.Devices {
		str = append(str, d.Name)
	}

	return str, nil
}

// GetRepo returns devices collection
func GetRepo() *DeviceCollection {
	if dm == nil {
		if err := initDeviceCollection(); err != nil {
			return &DeviceCollection{}
		}
	}
	return dm
}

// GetDeviceRepo returns a devices repo. It checks the existence of mapping.json first then proceeds to the default variable
func GetDeviceRepo(device string) (*DeviceMapping, error) {
	if dm == nil {
		if err := initDeviceCollection(); err != nil {
			return nil, err
		}
	}

	return dm.findDevice(device)
}

func GetDevices() []string {
	if dm == nil {
		if err := initDeviceCollection(); err != nil {
			return []string{}
		}
	}
	return dm.getDevices()
}

// initDeviceCollection initializes deviceCollection from file, alternatively from the internal constant
func initDeviceCollection() error {
	dm = &DeviceCollection{}

	d, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(d, dm); err != nil {
		return err
	}

	return nil
}

// GenMappingFile generates mapping.json file
func GenMappingFile() error {
	DownloadDevicesRepository()
	return nil
}

// fillEmptyImages updates substructures' empty image arrays with the parent one
func fillEmptyImages(m *DeviceMapping) {
	if len(m.Images) != 0 {
		for _, dm := range m.Sub {
			dm.dir = m.Name
			if len(dm.Images) == 0 {
				dm.Images = m.Images
			}

			fillEmptyImages(dm)
		}
	}
}
<commit_msg>Update artifact repository<commit_after>package repo

import (
	"encoding/json"
	"errors"
	log "github.com/sirupsen/logrus"
	"github.com/xshellinc/tools/lib/help"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// DeviceImage contains url, title, username and password which are used after flashing
type DeviceImage struct {
	URL   string `json:"URL"`
	Alias string `json:"Alias,omitempty"`
	Title string `json:"Title,omitempty"`
	User  string `json:"User,omitempty"`
	Pass  string `json:"Pass,omitempty"`
}

// DeviceMapping is a collection of device, it sub-types and sets of images for these devices
type DeviceMapping struct {
	Name   string           `json:"Name"`
	Alias  string           `json:"Alias,omitempty"`
	Sub    []*DeviceMapping `json:"Sub,omitempty"`
	Images []DeviceImage    `json:"Images,omitempty"`
	Type   string

	dir   string
	Image DeviceImage
}

// deviceCollection is a starting point of the collection of images
type DeviceCollection struct {
	Devices []DeviceMapping `json:"Devices"`
	Version string          `json:"Version,omitempty"`
}

// Images repository URL
const imagesRepo = "https://isaaxartifacts.blob.core.windows.net/iotit/mapping.json"

var path string
var dm *DeviceCollection

func init() {
	path = filepath.Join(baseDir, "mapping.json")
}

// GetSubsNames returns array of Names within a `Sub`
func (d *DeviceMapping) GetSubsNames() []string {
	r := make([]string, len(d.Sub))
	for i, o := range d.Sub {
		r[i] = o.Name
	}

	return r
}

// GetImageTitles returns array of image titles
func (d *DeviceMapping) GetImageTitles() []string {
	r := make([]string, len(d.Images))
	for i, o := range d.Images {
		r[i] = o.Title
	}

	return r
}

// Dir - returns directory of a local repo `.iotit/images/{name}`
func (d *DeviceMapping) Dir() string {
	return filepath.Join(ImageDir, d.dir)
}

// FindImage - searches image in the repo
func (d *DeviceMapping) FindImage(image string) error {
	search := strings.ToLower(image)
	for _, obj := range d.Images {
		if strings.ToLower(obj.Title) == search || obj.Alias == search {
			d.Image = obj
			return nil
		}
	}

	return errors.New(image + " unknown image")
}

// findDevice searches device in the repo
func (d *DeviceCollection) findDevice(device string) (*DeviceMapping, error) {
	search := strings.ToLower(device)
	for _, obj := range d.Devices {
		obj.dir = obj.Name
		obj.Type = obj.Name
		if strings.ToLower(obj.Name) == search || obj.Alias == search {
			fillEmptyImages(&obj)
			return &obj, nil
		}
		if len(obj.Sub) > 0 {
			for _, sub := range obj.Sub {
				sub.dir = obj.Name
				sub.Type = obj.Name
				if strings.ToLower(sub.Name) == search || sub.Alias == search {
					if len(sub.Images) == 0 {
						sub.Images = obj.Images
					}
					return sub, nil
				}
			}
		}
	}

	return nil, errors.New(device + " device is not supported")
}

func (d *DeviceCollection) getDevices() []string {
	devices := []string{}
	for _, obj := range d.Devices {
		devices = append(devices, obj.Name)
	}
	return devices
}

// DownloadDevicesRepository downloads new mapping.json from the cloud
func DownloadDevicesRepository() error {
	log.Info("Downloading new mapping.json...")
	if err := help.DownloadFile(path, imagesRepo); err != nil {
		log.Error(err)
		return err
	}
	// update file modification date so on the next run we don't try to download it again
	if err := os.Chtimes(path, time.Now(), time.Now()); err != nil {
		log.Error(err)
		return err
	}

	return nil
}

// CheckDevicesRepository checks mapping.json for updates
func CheckDevicesRepository() {
	log.Info("Checking for mapping.json updates...")
	if info, err := os.Stat(path); os.IsNotExist(err) || time.Now().Sub(info.ModTime()).Hours() >= 24 {
		DownloadDevicesRepository()
	}
}

// SetPath of the mapping.json file
func SetPath(p string) {
	path = p
}

// GetAllRepos returns all repo Names
func GetAllRepos() ([]string, error) {
	if dm == nil {
		if err := initDeviceCollection(); err != nil {
			return nil, err
		}
	}

	str := make([]string, 0)

	for _, d := range dm.Devices {
		str = append(str, d.Name)
	}

	return str, nil
}

// GetRepo returns devices collection
func GetRepo() *DeviceCollection {
	if dm == nil {
		if err := initDeviceCollection(); err != nil {
			return &DeviceCollection{}
		}
	}
	return dm
}

// GetDeviceRepo returns a devices repo. It checks the existence of mapping.json first then proceeds to the default variable
func GetDeviceRepo(device string) (*DeviceMapping, error) {
	if dm == nil {
		if err := initDeviceCollection(); err != nil {
			return nil, err
		}
	}

	return dm.findDevice(device)
}

func GetDevices() []string {
	if dm == nil {
		if err := initDeviceCollection(); err != nil {
			return []string{}
		}
	}
	return dm.getDevices()
}

// initDeviceCollection initializes deviceCollection from file, alternatively from the internal constant
func initDeviceCollection() error {
	dm = &DeviceCollection{}

	d, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(d, dm); err != nil {
		return err
	}

	return nil
}

// GenMappingFile generates mapping.json file
func GenMappingFile() error {
	DownloadDevicesRepository()
	return nil
}

// fillEmptyImages updates substructures' empty image arrays with the parent one
func fillEmptyImages(m *DeviceMapping) {
	if len(m.Images) != 0 {
		for _, dm := range m.Sub {
			dm.dir = m.Name
			if len(dm.Images) == 0 {
				dm.Images = m.Images
			}

			fillEmptyImages(dm)
		}
	}
}
<|endoftext|>


<commit_before>package report

import (
	"fmt"
	"sort"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/weaveworks/common/mtime"
)

// MaxTableRows sets the limit on the table size to render
// TODO: this won't be needed once we send reports incrementally
const (
	MaxTableRows           = 20
	TableEntryKeySeparator = "___"
	TruncationCountPrefix  = "table_truncation_count_"
	MulticolumnTableType   = "multicolumn-table"
	PropertyListType       = "property-list"
)

// WithTableTruncationInformation appends table truncation info to the node, returning the new node.
func (node Node) WithTableTruncationInformation(prefix string, totalRowsCount int) Node {
	if totalRowsCount > MaxTableRows {
		truncationCount := fmt.Sprintf("%d", totalRowsCount-MaxTableRows)
		node = node.WithLatest(TruncationCountPrefix+prefix, mtime.Now(), truncationCount)
	}
	return node
}

// AddPrefixMulticolumnTable appends arbitrary rows to the Node, returning a new node.
func (node Node) AddPrefixMulticolumnTable(prefix string, rows []Row) Node {
	addedRowsCount := 0
	for _, row := range rows {
		if addedRowsCount >= MaxTableRows {
			break
		}
		// Add all the row values as separate entries
		for columnID, value := range row.Entries {
			key := strings.Join([]string{row.ID, columnID}, TableEntryKeySeparator)
			node = node.WithLatest(prefix+key, mtime.Now(), value)
		}
		addedRowsCount++
	}
	return node.WithTableTruncationInformation(prefix, len(rows))
}

// AddPrefixPropertyList appends arbitrary key-value pairs to the Node, returning a new node.
func (node Node) AddPrefixPropertyList(prefix string, propertyList map[string]string) Node {
	addedPropertiesCount := 0
	for label, value := range propertyList {
		if addedPropertiesCount >= MaxTableRows {
			break
		}
		node = node.WithLatest(prefix+label, mtime.Now(), value)
		addedPropertiesCount++
	}
	return node.WithTableTruncationInformation(prefix, len(propertyList))
}

// WithoutPrefix returns the string with trimmed prefix and a
// boolean information of whether that prefix was really there.
// NOTE: Consider moving this function to utilities.
func WithoutPrefix(s string, prefix string) (string, bool) {
	return strings.TrimPrefix(s, prefix), len(prefix) > 0 && strings.HasPrefix(s, prefix)
}

// ExtractMulticolumnTable returns the rows to build a multicolumn table from this node
func (node Node) ExtractMulticolumnTable(template TableTemplate) (rows []Row) {
	rowsMapByID := map[string]Row{}

	// Itearate through the whole of our map to extract all the values with the key
	// with the given prefix. Since multicolumn tables don't support fixed rows (yet),
	// all the table values will be stored under the table prefix.
	// NOTE: It would be nice to optimize this part by only iterating through the keys
	// with the given prefix. If it is possible to traverse the keys in the Latest map
	// in a sorted order, then having LowerBoundEntry(key) and UpperBoundEntry(key)
	// methods should be enough to implement ForEachWithPrefix(prefix) straightforwardly.
	node.Latest.ForEach(func(key string, _ time.Time, value string) {
		if keyWithoutPrefix, ok := WithoutPrefix(key, template.Prefix); ok {
			ids := strings.Split(keyWithoutPrefix, TableEntryKeySeparator)
			rowID, columnID := ids[0], ids[1]
			// If the row with the given ID doesn't yet exist, we create an empty one.
			if _, ok := rowsMapByID[rowID]; !ok {
				rowsMapByID[rowID] = Row{
					ID:      rowID,
					Entries: map[string]string{},
				}
			}
			// At this point, the row with that ID always exists, so we just update the value.
			rowsMapByID[rowID].Entries[columnID] = value
		}
	})

	// Gather a list of rows.
	rows = make([]Row, 0, len(rowsMapByID))
	for _, row := range rowsMapByID {
		rows = append(rows, row)
	}

	// Return the rows sorted by ID.
	sort.Sort(rowsByID(rows))
	return rows
}

// ExtractPropertyList returns the rows to build a property list from this node
func (node Node) ExtractPropertyList(template TableTemplate) (rows []Row) {
	valuesMapByLabel := map[string]string{}

	// Itearate through the whole of our map to extract all the values with the key
	// with the given prefix as well as the keys corresponding to the fixed table rows.
	node.Latest.ForEach(func(key string, _ time.Time, value string) {
		if label, ok := template.FixedRows[key]; ok {
			valuesMapByLabel[label] = value
		} else if label, ok := WithoutPrefix(key, template.Prefix); ok {
			valuesMapByLabel[label] = value
		}
	})

	// Gather a label-value formatted list of rows.
	rows = make([]Row, 0, len(valuesMapByLabel))
	for label, value := range valuesMapByLabel {
		rows = append(rows, Row{
			ID: "label_" + label,
			Entries: map[string]string{
				"label": label,
				"value": value,
			},
		})
	}

	// Return the rows sorted by ID.
	sort.Sort(rowsByID(rows))
	return rows
}

// ExtractTable returns the rows to build either a property list or a generic table from this node
func (node Node) ExtractTable(template TableTemplate) (rows []Row, truncationCount int) {
	switch template.Type {
	case MulticolumnTableType:
		rows = node.ExtractMulticolumnTable(template)
	default: // By default assume it's a property list (for backward compatibility).
		rows = node.ExtractPropertyList(template)
	}

	truncationCount = 0
	if str, ok := node.Latest.Lookup(TruncationCountPrefix + template.Prefix); ok {
		if n, err := fmt.Sscanf(str, "%d", &truncationCount); n != 1 || err != nil {
			log.Warn("Unexpected truncation count format %q", str)
		}
	}

	return rows, truncationCount
}

// Column is the type for multi-column tables in the UI.
type Column struct {
	ID       string `json:"id"`
	Label    string `json:"label"`
	DataType string `json:"dataType"`
}

// Row is the type that holds the table data for the UI. Entries map from column ID to cell value.
type Row struct {
	ID      string            `json:"id"`
	Entries map[string]string `json:"entries"`
}

type rowsByID []Row

func (t rowsByID) Len() int           { return len(t) }
func (t rowsByID) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
func (t rowsByID) Less(i, j int) bool { return t[i].ID < t[j].ID }

// Copy returns a copy of the Row.
func (r Row) Copy() Row {
	entriesCopy := make(map[string]string, len(r.Entries))
	for key, value := range r.Entries {
		entriesCopy[key] = value
	}
	r.Entries = entriesCopy
	return r
}

// Table is the type for a table in the UI.
type Table struct {
	ID              string   `json:"id"`
	Label           string   `json:"label"`
	Type            string   `json:"type"`
	Columns         []Column `json:"columns"`
	Rows            []Row    `json:"rows"`
	TruncationCount int      `json:"truncationCount,omitempty"`
}

type tablesByID []Table

func (t tablesByID) Len() int           { return len(t) }
func (t tablesByID) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
func (t tablesByID) Less(i, j int) bool { return t[i].ID < t[j].ID }

// Copy returns a copy of the Table.
func (t Table) Copy() Table {
	result := Table{
		ID:      t.ID,
		Label:   t.Label,
		Type:    t.Type,
		Columns: make([]Column, 0, len(t.Columns)),
		Rows:    make([]Row, 0, len(t.Rows)),
	}
	for _, column := range t.Columns {
		result.Columns = append(result.Columns, column)
	}
	for _, row := range t.Rows {
		result.Rows = append(result.Rows, row)
	}
	return result
}

// TableTemplate describes how to render a table for the UI.
type TableTemplate struct {
	ID      string   `json:"id"`
	Label   string   `json:"label"`
	Prefix  string   `json:"prefix"`
	Type    string   `json:"type"`
	Columns []Column `json:"columns"`
	// FixedRows indicates what predetermined rows to render each entry is
	// indexed by the key to extract the row value is mapped to the row
	// label
	FixedRows map[string]string `json:"fixedRows"`
}

// Copy returns a value-copy of the TableTemplate
func (t TableTemplate) Copy() TableTemplate {
	fixedRowsCopy := make(map[string]string, len(t.FixedRows))
	for key, value := range t.FixedRows {
		fixedRowsCopy[key] = value
	}
	t.FixedRows = fixedRowsCopy
	return t
}

// Merge other into t, returning a fresh copy.  Does fieldwise max -
// whilst this isn't particularly meaningful, at least it idempotent,
// commutativite and associative.
func (t TableTemplate) Merge(other TableTemplate) TableTemplate {
	max := func(s1, s2 string) string {
		if s1 > s2 {
			return s1
		}
		return s2
	}

	// NOTE: Consider actually merging the columns and fixed rows.
	fixedRows := t.FixedRows
	if len(other.FixedRows) > len(fixedRows) {
		fixedRows = other.FixedRows
	}
	columns := t.Columns
	if len(other.Columns) > len(columns) {
		columns = other.Columns
	}

	// TODO: Refactor the merging logic, as mixing the types now might result in
	// invalid tables. Maybe we should return an error if the types are different?
	return TableTemplate{
		ID:        max(t.ID, other.ID),
		Label:     max(t.Label, other.Label),
		Prefix:    max(t.Prefix, other.Prefix),
		Type:      max(t.Type, other.Type),
		Columns:   columns,
		FixedRows: fixedRows,
	}
}

// TableTemplates is a mergeable set of TableTemplate
type TableTemplates map[string]TableTemplate

// Tables renders the TableTemplates for a given node.
func (t TableTemplates) Tables(node Node) []Table {
	var result []Table
	for _, template := range t {
		rows, truncationCount := node.ExtractTable(template)
		// Extract the type from the template; default to
		// property list for backwards-compatibility.
		tableType := template.Type
		if tableType == "" {
			tableType = PropertyListType
		}
		result = append(result, Table{
			ID:              template.ID,
			Label:           template.Label,
			Columns:         template.Columns,
			Type:            tableType,
			Rows:            rows,
			TruncationCount: truncationCount,
		})
	}
	sort.Sort(tablesByID(result))
	return result
}

// Copy returns a value copy of the TableTemplates
func (t TableTemplates) Copy() TableTemplates {
	if t == nil {
		return nil
	}
	result := TableTemplates{}
	for k, v := range t {
		result[k] = v.Copy()
	}
	return result
}

// Merge merges two sets of TableTemplates
func (t TableTemplates) Merge(other TableTemplates) TableTemplates {
	if t == nil && other == nil {
		return nil
	}
	result := make(TableTemplates, len(t))
	for k, v := range t {
		result[k] = v
	}
	for k, v := range other {
		if existing, ok := result[k]; ok {
			result[k] = v.Merge(existing)
		} else {
			result[k] = v
		}
	}
	return result
}
<commit_msg>make Node.WithTableTruncationInformation private<commit_after>package report

import (
	"fmt"
	"sort"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/weaveworks/common/mtime"
)

// MaxTableRows sets the limit on the table size to render
// TODO: this won't be needed once we send reports incrementally
const (
	MaxTableRows           = 20
	TableEntryKeySeparator = "___"
	TruncationCountPrefix  = "table_truncation_count_"
	MulticolumnTableType   = "multicolumn-table"
	PropertyListType       = "property-list"
)

// withTableTruncationInformation appends table truncation info to the node, returning the new node.
func (node Node) withTableTruncationInformation(prefix string, totalRowsCount int) Node {
	if totalRowsCount > MaxTableRows {
		truncationCount := fmt.Sprintf("%d", totalRowsCount-MaxTableRows)
		node = node.WithLatest(TruncationCountPrefix+prefix, mtime.Now(), truncationCount)
	}
	return node
}

// AddPrefixMulticolumnTable appends arbitrary rows to the Node, returning a new node.
func (node Node) AddPrefixMulticolumnTable(prefix string, rows []Row) Node {
	addedRowsCount := 0
	for _, row := range rows {
		if addedRowsCount >= MaxTableRows {
			break
		}
		// Add all the row values as separate entries
		for columnID, value := range row.Entries {
			key := strings.Join([]string{row.ID, columnID}, TableEntryKeySeparator)
			node = node.WithLatest(prefix+key, mtime.Now(), value)
		}
		addedRowsCount++
	}
	return node.withTableTruncationInformation(prefix, len(rows))
}

// AddPrefixPropertyList appends arbitrary key-value pairs to the Node, returning a new node.
func (node Node) AddPrefixPropertyList(prefix string, propertyList map[string]string) Node {
	addedPropertiesCount := 0
	for label, value := range propertyList {
		if addedPropertiesCount >= MaxTableRows {
			break
		}
		node = node.WithLatest(prefix+label, mtime.Now(), value)
		addedPropertiesCount++
	}
	return node.withTableTruncationInformation(prefix, len(propertyList))
}

// WithoutPrefix returns the string with trimmed prefix and a
// boolean information of whether that prefix was really there.
// NOTE: Consider moving this function to utilities.
func WithoutPrefix(s string, prefix string) (string, bool) {
	return strings.TrimPrefix(s, prefix), len(prefix) > 0 && strings.HasPrefix(s, prefix)
}

// ExtractMulticolumnTable returns the rows to build a multicolumn table from this node
func (node Node) ExtractMulticolumnTable(template TableTemplate) (rows []Row) {
	rowsMapByID := map[string]Row{}

	// Itearate through the whole of our map to extract all the values with the key
	// with the given prefix. Since multicolumn tables don't support fixed rows (yet),
	// all the table values will be stored under the table prefix.
	// NOTE: It would be nice to optimize this part by only iterating through the keys
	// with the given prefix. If it is possible to traverse the keys in the Latest map
	// in a sorted order, then having LowerBoundEntry(key) and UpperBoundEntry(key)
	// methods should be enough to implement ForEachWithPrefix(prefix) straightforwardly.
	node.Latest.ForEach(func(key string, _ time.Time, value string) {
		if keyWithoutPrefix, ok := WithoutPrefix(key, template.Prefix); ok {
			ids := strings.Split(keyWithoutPrefix, TableEntryKeySeparator)
			rowID, columnID := ids[0], ids[1]
			// If the row with the given ID doesn't yet exist, we create an empty one.
			if _, ok := rowsMapByID[rowID]; !ok {
				rowsMapByID[rowID] = Row{
					ID:      rowID,
					Entries: map[string]string{},
				}
			}
			// At this point, the row with that ID always exists, so we just update the value.
			rowsMapByID[rowID].Entries[columnID] = value
		}
	})

	// Gather a list of rows.
	rows = make([]Row, 0, len(rowsMapByID))
	for _, row := range rowsMapByID {
		rows = append(rows, row)
	}

	// Return the rows sorted by ID.
	sort.Sort(rowsByID(rows))
	return rows
}

// ExtractPropertyList returns the rows to build a property list from this node
func (node Node) ExtractPropertyList(template TableTemplate) (rows []Row) {
	valuesMapByLabel := map[string]string{}

	// Itearate through the whole of our map to extract all the values with the key
	// with the given prefix as well as the keys corresponding to the fixed table rows.
	node.Latest.ForEach(func(key string, _ time.Time, value string) {
		if label, ok := template.FixedRows[key]; ok {
			valuesMapByLabel[label] = value
		} else if label, ok := WithoutPrefix(key, template.Prefix); ok {
			valuesMapByLabel[label] = value
		}
	})

	// Gather a label-value formatted list of rows.
	rows = make([]Row, 0, len(valuesMapByLabel))
	for label, value := range valuesMapByLabel {
		rows = append(rows, Row{
			ID: "label_" + label,
			Entries: map[string]string{
				"label": label,
				"value": value,
			},
		})
	}

	// Return the rows sorted by ID.
	sort.Sort(rowsByID(rows))
	return rows
}

// ExtractTable returns the rows to build either a property list or a generic table from this node
func (node Node) ExtractTable(template TableTemplate) (rows []Row, truncationCount int) {
	switch template.Type {
	case MulticolumnTableType:
		rows = node.ExtractMulticolumnTable(template)
	default: // By default assume it's a property list (for backward compatibility).
		rows = node.ExtractPropertyList(template)
	}

	truncationCount = 0
	if str, ok := node.Latest.Lookup(TruncationCountPrefix + template.Prefix); ok {
		if n, err := fmt.Sscanf(str, "%d", &truncationCount); n != 1 || err != nil {
			log.Warn("Unexpected truncation count format %q", str)
		}
	}

	return rows, truncationCount
}

// Column is the type for multi-column tables in the UI.
type Column struct {
	ID       string `json:"id"`
	Label    string `json:"label"`
	DataType string `json:"dataType"`
}

// Row is the type that holds the table data for the UI. Entries map from column ID to cell value.
type Row struct {
	ID      string            `json:"id"`
	Entries map[string]string `json:"entries"`
}

type rowsByID []Row

func (t rowsByID) Len() int           { return len(t) }
func (t rowsByID) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
func (t rowsByID) Less(i, j int) bool { return t[i].ID < t[j].ID }

// Copy returns a copy of the Row.
func (r Row) Copy() Row {
	entriesCopy := make(map[string]string, len(r.Entries))
	for key, value := range r.Entries {
		entriesCopy[key] = value
	}
	r.Entries = entriesCopy
	return r
}

// Table is the type for a table in the UI.
type Table struct {
	ID              string   `json:"id"`
	Label           string   `json:"label"`
	Type            string   `json:"type"`
	Columns         []Column `json:"columns"`
	Rows            []Row    `json:"rows"`
	TruncationCount int      `json:"truncationCount,omitempty"`
}

type tablesByID []Table

func (t tablesByID) Len() int           { return len(t) }
func (t tablesByID) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
func (t tablesByID) Less(i, j int) bool { return t[i].ID < t[j].ID }

// Copy returns a copy of the Table.
func (t Table) Copy() Table {
	result := Table{
		ID:      t.ID,
		Label:   t.Label,
		Type:    t.Type,
		Columns: make([]Column, 0, len(t.Columns)),
		Rows:    make([]Row, 0, len(t.Rows)),
	}
	for _, column := range t.Columns {
		result.Columns = append(result.Columns, column)
	}
	for _, row := range t.Rows {
		result.Rows = append(result.Rows, row)
	}
	return result
}

// TableTemplate describes how to render a table for the UI.
type TableTemplate struct {
	ID      string   `json:"id"`
	Label   string   `json:"label"`
	Prefix  string   `json:"prefix"`
	Type    string   `json:"type"`
	Columns []Column `json:"columns"`
	// FixedRows indicates what predetermined rows to render each entry is
	// indexed by the key to extract the row value is mapped to the row
	// label
	FixedRows map[string]string `json:"fixedRows"`
}

// Copy returns a value-copy of the TableTemplate
func (t TableTemplate) Copy() TableTemplate {
	fixedRowsCopy := make(map[string]string, len(t.FixedRows))
	for key, value := range t.FixedRows {
		fixedRowsCopy[key] = value
	}
	t.FixedRows = fixedRowsCopy
	return t
}

// Merge other into t, returning a fresh copy.  Does fieldwise max -
// whilst this isn't particularly meaningful, at least it idempotent,
// commutativite and associative.
func (t TableTemplate) Merge(other TableTemplate) TableTemplate {
	max := func(s1, s2 string) string {
		if s1 > s2 {
			return s1
		}
		return s2
	}

	// NOTE: Consider actually merging the columns and fixed rows.
	fixedRows := t.FixedRows
	if len(other.FixedRows) > len(fixedRows) {
		fixedRows = other.FixedRows
	}
	columns := t.Columns
	if len(other.Columns) > len(columns) {
		columns = other.Columns
	}

	// TODO: Refactor the merging logic, as mixing the types now might result in
	// invalid tables. Maybe we should return an error if the types are different?
	return TableTemplate{
		ID:        max(t.ID, other.ID),
		Label:     max(t.Label, other.Label),
		Prefix:    max(t.Prefix, other.Prefix),
		Type:      max(t.Type, other.Type),
		Columns:   columns,
		FixedRows: fixedRows,
	}
}

// TableTemplates is a mergeable set of TableTemplate
type TableTemplates map[string]TableTemplate

// Tables renders the TableTemplates for a given node.
func (t TableTemplates) Tables(node Node) []Table {
	var result []Table
	for _, template := range t {
		rows, truncationCount := node.ExtractTable(template)
		// Extract the type from the template; default to
		// property list for backwards-compatibility.
		tableType := template.Type
		if tableType == "" {
			tableType = PropertyListType
		}
		result = append(result, Table{
			ID:              template.ID,
			Label:           template.Label,
			Columns:         template.Columns,
			Type:            tableType,
			Rows:            rows,
			TruncationCount: truncationCount,
		})
	}
	sort.Sort(tablesByID(result))
	return result
}

// Copy returns a value copy of the TableTemplates
func (t TableTemplates) Copy() TableTemplates {
	if t == nil {
		return nil
	}
	result := TableTemplates{}
	for k, v := range t {
		result[k] = v.Copy()
	}
	return result
}

// Merge merges two sets of TableTemplates
func (t TableTemplates) Merge(other TableTemplates) TableTemplates {
	if t == nil && other == nil {
		return nil
	}
	result := make(TableTemplates, len(t))
	for k, v := range t {
		result[k] = v
	}
	for k, v := range other {
		if existing, ok := result[k]; ok {
			result[k] = v.Merge(existing)
		} else {
			result[k] = v
		}
	}
	return result
}
<|endoftext|>


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

// stdlibsymbol compares data from the symbol_history table with
// the stdlib API data at
// https://go.googlesource.com/go/+/refs/heads/master/api.
package main

import (
	"context"
	"fmt"

	"golang.org/x/pkgsite/cmd/internal/cmdconfig"
	"golang.org/x/pkgsite/internal/config"
	"golang.org/x/pkgsite/internal/log"
)

func main() {
	ctx := context.Background()
	cfg, err := config.Init(ctx)
	log.SetLevel("error")
	if err != nil {
		log.Fatal(ctx, err)
	}
	db, err := cmdconfig.OpenDB(ctx, cfg, false)
	if err != nil {
		log.Fatal(ctx, err)
	}

	pkgToErrors, err := db.CompareStdLib(ctx)
	if err != nil {
		log.Fatal(ctx, err)
	}
	for path, errs := range pkgToErrors {
		fmt.Printf("----- %s -----\n", path)
		for _, e := range errs {
			fmt.Print(e)
		}
	}
}
<commit_msg>cmd/internal/testing/stdlibsymbol: add stdlib versions page test<commit_after>// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// stdlibsymbol compares database information with
// the stdlib API data at
// https://go.googlesource.com/go/+/refs/heads/master/api.
package main

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"

	"golang.org/x/pkgsite/cmd/internal/cmdconfig"
	"golang.org/x/pkgsite/internal"
	"golang.org/x/pkgsite/internal/config"
	"golang.org/x/pkgsite/internal/frontend"
	"golang.org/x/pkgsite/internal/log"
	"golang.org/x/pkgsite/internal/stdlib"
	"golang.org/x/pkgsite/internal/symbol"
)

var (
	path = flag.String("path", "", "path to compare against dataset")
)

func main() {
	flag.Parse()

	log.SetLevel("error")
	ctx := context.Background()
	cfg, err := config.Init(ctx)
	if err != nil {
		log.Fatal(ctx, err)
	}
	if err := os.Setenv("GO_DISCOVERY_SERVE_STATS", "true"); err != nil {
		log.Fatal(ctx, err)
	}
	if *path != "" {
		if err := compareVersionsPage(*path); err != nil {
			log.Fatal(ctx, err)
		}
		return
	}
	if err := compareDB(ctx, cfg); err != nil {
		log.Fatal(ctx, err)
	}
}

func compareDB(ctx context.Context, cfg *config.Config) error {
	db, err := cmdconfig.OpenDB(ctx, cfg, false)
	if err != nil {
		return err
	}
	pkgToErrors, err := db.CompareStdLib(ctx)
	if err != nil {
		return err
	}
	for path, errs := range pkgToErrors {
		fmt.Printf("----- %s -----\n", path)
		for _, e := range errs {
			fmt.Print(e)
		}
	}
	return nil
}

func compareVersionsPage(path string) error {
	url := fmt.Sprintf("http://localhost:8080/%s?tab=versions&m=json", path)
	r, err := http.Get(url)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	var vd frontend.VersionsDetails
	if err := json.Unmarshal(body, &vd); err != nil {
		return fmt.Errorf("json.Unmarshal: %v", err)
	}

	versionToNameToSymbol := map[string]map[string]*internal.UnitSymbol{}
	for _, vl := range vd.ThisModule {
		for _, vs := range vl.Versions {
			v := stdlib.VersionForTag(vs.Version)
			versionToNameToSymbol[v] = map[string]*internal.UnitSymbol{}
			for _, s := range vs.Symbols {
				if s.New {
					versionToNameToSymbol[v][s.Name] = unitSymbol(s)
				}
				for _, c := range s.Children {
					versionToNameToSymbol[v][c.Name] = unitSymbol(c)
				}
			}
		}
	}
	apiVersions, err := symbol.ParsePackageAPIInfo()
	if err != nil {
		return err
	}
	errs := symbol.CompareStdLib(path, apiVersions[path], versionToNameToSymbol)
	for _, e := range errs {
		fmt.Print(e)
	}
	return nil
}

func unitSymbol(s *frontend.Symbol) *internal.UnitSymbol {
	us := &internal.UnitSymbol{Name: s.Name}
	if len(s.Builds) == 0 {
		us.AddBuildContext(internal.BuildContextAll)
	}
	for _, b := range s.Builds {
		parts := strings.SplitN(b, "/", 2)
		var build internal.BuildContext
		switch parts[0] {
		case "linux":
			build = internal.BuildContextLinux
		case "darwin":
			build = internal.BuildContextDarwin
		case "windows":
			build = internal.BuildContextWindows
		case "js":
			build = internal.BuildContextJS
		}
		if us.SupportsBuild(build) {
			fmt.Printf("duplicate build context for %q: %v\n", s.Name, build)
		}
		us.AddBuildContext(build)
	}
	return us
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"log"

	"github.com/sboyettedh/whiplash"
)

func pingHandler(args [][]byte) ([]byte, error) {
	req := &whiplash.ClientUpdate{}
	json.Unmarshal(args[0], req)
	if svc, ok := svcs[req.Svc.Name]; !ok {
		log.Printf("adding svc %v", req.Svc.Name)
		// add service to svcs, upds
		svcs[req.Svc.Name] = req.Svc
		upds[req.Svc.Name] = map[string]int64{"ping": req.Time}
		// and to svcmap
		if _, ok := svcmap[req.Svc.Host]; !ok {
			log.Printf("adding host %v", req.Svc.Host)
			svcmap[req.Svc.Host] = []string{}
		}
		svcmap[req.Svc.Host] = append(svcmap[req.Svc.Host], req.Svc.Name)
	} else {
		log.Printf("got update from %v", svc.Name)
		// TODO make version change an Event, once events are implemented
		svc.Version = req.Svc.Version
		svc.Reporting = req.Svc.Reporting
		upds[req.Svc.Name]["ping"] = req.Time
	}
	return []byte("ok"), nil
}
<commit_msg>pingHandler updated to handle updates :)<commit_after>package main

import (
	"encoding/json"
	"log"

	"github.com/sboyettedh/whiplash"
)

func pingHandler(args [][]byte) ([]byte, error) {
	req := &whiplash.ClientUpdate{}
	json.Unmarshal(args[0], req)
	if svc, ok := svcs[req.Svc.Name]; !ok {
		log.Println("adding svc", req.Svc.Name)
		// add service to svcs, upds
		svcs[req.Svc.Name] = req.Svc
		upds[req.Svc.Name] = map[string]int64{"ping": req.Time}
		// and to svcmap
		if _, ok := svcmap[req.Svc.Host]; !ok {
			log.Println("adding host", req.Svc.Host)
			svcmap[req.Svc.Host] = []string{}
		}
		svcmap[req.Svc.Host] = append(svcmap[req.Svc.Host], req.Svc.Name)
	} else {
		log.Println("updating", svc.Name)
		// TODO make version change an Event, once events are implemented
		svc.Version = req.Svc.Version
		svc.Reporting = req.Svc.Reporting
		upds[req.Svc.Name]["ping"] = req.Time
	}
	return []byte("ok"), nil
}
<|endoftext|>


<commit_before>package hue

import (
	"fmt"
)

type Configuration struct {
	// Read and Write
	Name           string              `json:"name,omitempty"`
	ProxyAddress   string              `json:"proxyaddress,omitempty"`
	ProxyPort      *uint16             `json:"proxyport,omitempty"`
	IpAddress      string              `json:"ipaddress,omitempty"`
	Netmask        string              `json:"netmask,omitempty"`
	Gateway        string              `json:"gateway,omitempty"`
	Dhcp           *bool               `json:"dhcp,omitempty"`
	PortalServices *bool               `json:"portalservices,omitempty"`
	LinkButton     *bool               `json:"linkbutton,omitempty"`
	SoftwareUpdate *SoftwareUpdateInfo `json:"swupdate,omitempty"`

	// Read only
	Utc             string                       `json:"utc,omitempty"`
	Whitelist       map[string]map[string]string `json:"whitelist,omitempty"`
	SoftwareVersion string                       `json:"swversion,omitempty"`
	Mac             string                       `json:"mac,omitempty"`
}

type SoftwareUpdateInfo struct {
	UpdateState *uint  `json:"updatestate,omitempty"`
	Url         string `json:"url,omitempty"`
	Text        string `json:"text,omitempty"`
	Notify      *bool  `json:"notify,omitempty"`
}

func (u *User) GetConfiguration() (*Configuration, error) {
	url := fmt.Sprintf("/api/%s/config", u.Username)

	var configuration *Configuration
	err := u.Bridge.client.Get(url, &configuration)
	if err != nil {
		return nil, err
	}

	return configuration, nil
}

// TODO Configuration Api Methods:
// Add struct for and Whitelist
// Provide strong type for times, addresses, etc
// func (u *User) DeleteUser(*User) error
// func (u *User) UpdateConfiguration(*Configuration) error
// func (u *User) GetDataStore() (DataStore, error)
//

// type DataStore struct {
// 	Lights []Light
// 	Groups []Group
// 	Schedules []Schedule
// 	Config Configuration
// }
<commit_msg>updated todo list for configuration apis<commit_after>package hue

import (
	"fmt"
)

type Configuration struct {
	// Read and Write
	Name           string              `json:"name,omitempty"`
	ProxyAddress   string              `json:"proxyaddress,omitempty"`
	ProxyPort      *uint16             `json:"proxyport,omitempty"`
	IpAddress      string              `json:"ipaddress,omitempty"`
	Netmask        string              `json:"netmask,omitempty"`
	Gateway        string              `json:"gateway,omitempty"`
	Dhcp           *bool               `json:"dhcp,omitempty"`
	PortalServices *bool               `json:"portalservices,omitempty"`
	LinkButton     *bool               `json:"linkbutton,omitempty"`
	SoftwareUpdate *SoftwareUpdateInfo `json:"swupdate,omitempty"`

	// Read only
	Utc             string                       `json:"utc,omitempty"`
	Whitelist       map[string]map[string]string `json:"whitelist,omitempty"`
	SoftwareVersion string                       `json:"swversion,omitempty"`
	Mac             string                       `json:"mac,omitempty"`
}

type SoftwareUpdateInfo struct {
	UpdateState *uint  `json:"updatestate,omitempty"`
	Url         string `json:"url,omitempty"`
	Text        string `json:"text,omitempty"`
	Notify      *bool  `json:"notify,omitempty"`
}

func (u *User) GetConfiguration() (*Configuration, error) {
	url := fmt.Sprintf("/api/%s/config", u.Username)

	var configuration *Configuration
	err := u.Bridge.client.Get(url, &configuration)
	if err != nil {
		return nil, err
	}

	return configuration, nil
}

// TODO Configuration Api Methods:
// func (u *User) DeleteUser(*User) error
// func (u *User) UpdateConfiguration(*Configuration) error
// func (u *User) GetDataStore() (DataStore, error)
<|endoftext|>


<commit_before>package consul

import (
	"fmt"
	"log"
	"net/url"
	"strings"

	"github.com/gliderlabs/registrator/bridge"
	consulapi "github.com/hashicorp/consul/api"
)

const DefaultInterval = "10s"

func init() {
	bridge.Register(new(Factory), "consul")
}

func (r *ConsulAdapter) interpolateService(script string, service *bridge.Service) string {
	withIp := strings.Replace(script, "$SERVICE_IP", service.Origin.HostIP, -1)
	withPort := strings.Replace(withIp, "$SERVICE_PORT", service.Origin.HostPort, -1)
	return withPort
}

type Factory struct{}

func (f *Factory) New(uri *url.URL) bridge.RegistryAdapter {
	config := consulapi.DefaultConfig()
	if uri.Host != "" {
		config.Address = uri.Host
	}
	servicePrefix := uri.Query()["prefix"]

	client, err := consulapi.NewClient(config)
	if err != nil {
		log.Fatal("consul: ", uri.Scheme)
	}
	if(len(servicePrefix)>0) {
		return &ConsulAdapter{client: client, servicePrefix: servicePrefix[0]}
	} else {
		return &ConsulAdapter{client: client}

	}
}

type ConsulAdapter struct {
	client        *consulapi.Client
	servicePrefix string
}

// Ping will try to connect to consul by attempting to retrieve the current leader.
func (r *ConsulAdapter) Ping() error {
	status := r.client.Status()
	leader, err := status.Leader()
	if err != nil {
		return err
	}
	log.Println("consul: current leader ", leader)

	return nil
}

func (r *ConsulAdapter) Register(service *bridge.Service) error {
	registration := new(consulapi.AgentServiceRegistration)
	registration.ID = service.ID
	registration.Name = service.Name
	registration.Port = service.Port
	registration.Tags = service.Tags
	registration.Address = service.IP
	registration.Check = r.buildCheck(service)
	if r.servicePrefix != "" {
		kv := r.client.KV()
		for k, v := range service.Attrs {
			pair := &consulapi.KVPair{Key: r.servicePrefix + "/attributes/" + service.ID + "/" + k, Value: []byte(v)}
			_, err := kv.Put(pair, nil)
			if err != nil {
				panic(err)
			}
		}
		if(service.Origin.ContainerID!="") {
			pair := &consulapi.KVPair{Key: r.servicePrefix + "/container/" + service.Origin.ContainerID, Value: []byte(service.ID)}
//			pair := &consulapi.KVPair{Key: r.servicePrefix + "/container/" + service.ID + "/" + service.Origin.ContainerID, Value: []byte(service.Origin.ContainerID)}
			_, err := kv.Put(pair, nil)
			if err != nil {
				panic(err)
			}
		}
	}
	return r.client.Agent().ServiceRegister(registration)
}

func (r *ConsulAdapter) buildCheck(service *bridge.Service) *consulapi.AgentServiceCheck {
	check := new(consulapi.AgentServiceCheck)
	if path := service.Attrs["check_http"]; path != "" {
		check.HTTP = fmt.Sprintf("http://%s:%d%s", service.IP, service.Port, path)
		if timeout := service.Attrs["check_timeout"]; timeout != "" {
			check.Timeout = timeout
		}
	} else if cmd := service.Attrs["check_cmd"]; cmd != "" {
		check.Script = fmt.Sprintf("check-cmd %s %s %s", service.Origin.ContainerID[:12], service.Origin.ExposedPort, cmd)
	} else if script := service.Attrs["check_script"]; script != "" {
		check.Script = r.interpolateService(script, service)
	} else if ttl := service.Attrs["check_ttl"]; ttl != "" {
		check.TTL = ttl
	} else {
		return nil
	}
	if check.Script != "" || check.HTTP != "" {
		if interval := service.Attrs["check_interval"]; interval != "" {
			check.Interval = interval
		} else {
			check.Interval = DefaultInterval
		}
	}
	return check
}

func (r *ConsulAdapter) Deregister(service *bridge.Service) error {
	//	pair := &consulapi.KVPair{Key: "service_attribute" + "/" + service.Name + "/" + k, Value: []byte(v)}
	success := r.client.Agent().ServiceDeregister(service.ID)
	r.client.KV().DeleteTree("service_attribute"+"/attributes/"+service.ID, nil)
	return success
}

func (r *ConsulAdapter) Refresh(service *bridge.Service) error {
	return nil
}
<commit_msg>Remove leaked fix<commit_after>package consul

import (
	"fmt"
	"log"
	"net/url"
	"strings"

	"github.com/gliderlabs/registrator/bridge"
	consulapi "github.com/hashicorp/consul/api"
)

const DefaultInterval = "10s"

func init() {
	bridge.Register(new(Factory), "consul")
}

func (r *ConsulAdapter) interpolateService(script string, service *bridge.Service) string {
	withIp := strings.Replace(script, "$SERVICE_IP", service.Origin.HostIP, -1)
	withPort := strings.Replace(withIp, "$SERVICE_PORT", service.Origin.HostPort, -1)
	return withPort
}

type Factory struct{}

func (f *Factory) New(uri *url.URL) bridge.RegistryAdapter {
	config := consulapi.DefaultConfig()
	if uri.Host != "" {
		config.Address = uri.Host
	}
	servicePrefix := uri.Query()["prefix"]

	client, err := consulapi.NewClient(config)
	if err != nil {
		log.Fatal("consul: ", uri.Scheme)
	}
	if(len(servicePrefix)>0) {
		return &ConsulAdapter{client: client, servicePrefix: servicePrefix[0]}
	} else {
		return &ConsulAdapter{client: client}

	}
}

type ConsulAdapter struct {
	client        *consulapi.Client
	servicePrefix string
}

// Ping will try to connect to consul by attempting to retrieve the current leader.
func (r *ConsulAdapter) Ping() error {
	status := r.client.Status()
	leader, err := status.Leader()
	if err != nil {
		return err
	}
	log.Println("consul: current leader ", leader)

	return nil
}

func (r *ConsulAdapter) Register(service *bridge.Service) error {
	registration := new(consulapi.AgentServiceRegistration)
	registration.ID = service.ID
	registration.Name = service.Name
	registration.Port = service.Port
	registration.Tags = service.Tags
	registration.Address = service.IP
	registration.Check = r.buildCheck(service)
	if r.servicePrefix != "" {
		kv := r.client.KV()
		for k, v := range service.Attrs {
                        pair := &consulapi.KVPair{Key: r.servicePrefix + "/" + service.ID + "/" + k, Value: []byte(v)}
			_, err := kv.Put(pair, nil)
			if err != nil {
				panic(err)
			}
		}
	}
	return r.client.Agent().ServiceRegister(registration)
}

func (r *ConsulAdapter) buildCheck(service *bridge.Service) *consulapi.AgentServiceCheck {
	check := new(consulapi.AgentServiceCheck)
	if path := service.Attrs["check_http"]; path != "" {
		check.HTTP = fmt.Sprintf("http://%s:%d%s", service.IP, service.Port, path)
		if timeout := service.Attrs["check_timeout"]; timeout != "" {
			check.Timeout = timeout
		}
	} else if cmd := service.Attrs["check_cmd"]; cmd != "" {
		check.Script = fmt.Sprintf("check-cmd %s %s %s", service.Origin.ContainerID[:12], service.Origin.ExposedPort, cmd)
	} else if script := service.Attrs["check_script"]; script != "" {
		check.Script = r.interpolateService(script, service)
	} else if ttl := service.Attrs["check_ttl"]; ttl != "" {
		check.TTL = ttl
	} else {
		return nil
	}
	if check.Script != "" || check.HTTP != "" {
		if interval := service.Attrs["check_interval"]; interval != "" {
			check.Interval = interval
		} else {
			check.Interval = DefaultInterval
		}
	}
	return check
}

func (r *ConsulAdapter) Deregister(service *bridge.Service) error {
	//	pair := &consulapi.KVPair{Key: "service_attribute" + "/" + service.Name + "/" + k, Value: []byte(v)}
	success := r.client.Agent().ServiceDeregister(service.ID)
	r.client.KV().DeleteTree("service_attribute"+"/attributes/"+service.ID, nil)
	return success
}

func (r *ConsulAdapter) Refresh(service *bridge.Service) error {
	return nil
}
<|endoftext|>


<commit_before>package consul

import (
	"log"

	"github.com/hashicorp/consul/api"
)

var (
	client *api.Client

	// The name of the service in Consul
	ServiceName string = "unkown-service"

	// The name of the proxy-path
	Proxy string = "unkown"
)

// Init Consul client
func init() {
	var err error
	client, err = api.NewClient(api.DefaultConfig())
	if err != nil {
		log.Panicf("cannot init consul client: %v\n", err)
	}
}

func unregisterProxy() {
	log.Println("deregister service...")
	if err := client.Agent().ServiceDeregister(ServiceName); err != nil {
		log.Printf("cannot deregister proxy: %v\n", err)
	}
}

func registerProxy() error {
	log.Printf("Registering service '%s'\n", ServiceName)
	check := &api.AgentServiceCheck{
		HTTP:     "http://localhost:8080/health",
		Interval: "1s",
		Timeout:  "100ms",
	}
	return client.Agent().ServiceRegister(&api.AgentServiceRegistration{
		Name:  ServiceName,
		Port:  8080,
		Check: check,
		Tags:  []string{"proxy://" + Proxy},
	})
}
<commit_msg>updated methodnames<commit_after>package consul

import (
	"log"

	"github.com/hashicorp/consul/api"
)

var (
	client *api.Client

	// The name of the service in Consul
	ServiceName string = "unkown-service"

	// The name of the proxy-path
	Proxy string = "proxy://unkown"
)

// Init Consul client
func init() {
	var err error
	client, err = api.NewClient(api.DefaultConfig())
	if err != nil {
		log.Panicf("cannot init consul client: %v\n", err)
	}
}

func Unregister() {
	log.Println("deregister service...")
	if err := client.Agent().ServiceDeregister(ServiceName); err != nil {
		log.Printf("cannot deregister proxy: %v\n", err)
	}
}

func Register() error {
	log.Printf("Registering service '%s'\n", ServiceName)
	check := &api.AgentServiceCheck{
		HTTP:     "http://localhost:8080/health",
		Interval: "1s",
		Timeout:  "100ms",
	}
	return client.Agent().ServiceRegister(&api.AgentServiceRegistration{
		Name:  ServiceName,
		Port:  8080,
		Check: check,
		Tags:  []string{Proxy},
	})
}
<|endoftext|>


<commit_before>package client

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"text/template"

	"github.com/hyperhq/hyperd/client/api"
	"github.com/hyperhq/runv/lib/term"

	"github.com/docker/docker/cliconfig"
	"github.com/docker/docker/pkg/homedir"
)

type HyperClient struct {
	client        api.APIInterface
	in            io.ReadCloser
	out           io.Writer
	err           io.Writer
	inFd          uintptr
	outFd         uintptr
	isTerminalIn  bool
	isTerminalOut bool
	configFile    *cliconfig.ConfigFile
}

func NewHyperClient(proto, addr string, tlsConfig *tls.Config) *HyperClient {
	var (
		inFd          uintptr
		outFd         uintptr
		isTerminalIn  = false
		isTerminalOut = false
	)

	clifile, err := cliconfig.Load(filepath.Join(homedir.Get(), ".docker"))
	if err != nil {
		fmt.Fprintf(os.Stdout, "WARNING: Error loading config file %v\n", err)
	}

	inFd, isTerminalIn = term.GetFdInfo(os.Stdin)
	outFd, isTerminalOut = term.GetFdInfo(os.Stdout)

	return &HyperClient{
		client:        api.NewClient(proto, addr, tlsConfig),
		in:            os.Stdin,
		out:           os.Stdout,
		err:           os.Stdout,
		inFd:          inFd,
		outFd:         outFd,
		isTerminalIn:  isTerminalIn,
		isTerminalOut: isTerminalOut,
		configFile:    clifile,
	}
}

var funcMap = template.FuncMap{
	"json": func(v interface{}) string {
		a, _ := json.Marshal(v)
		return string(a)
	},
}

func (cli *HyperClient) getMethod(args ...string) (func(...string) error, bool) {
	camelArgs := make([]string, len(args))
	for i, s := range args {
		if len(s) == 0 {
			return nil, false
		}
		camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
	}
	methodName := "HyperCmd" + strings.Join(camelArgs, "")
	method := reflect.ValueOf(cli).MethodByName(methodName)
	if !method.IsValid() {
		return nil, false
	}
	return method.Interface().(func(...string) error), true
}

// Cmd executes the specified command.
func (cli *HyperClient) Cmd(args ...string) error {
	if len(args) > 1 {
		method, exists := cli.getMethod(args[:2]...)
		if exists {
			return method(args[2:]...)
		}
	}
	if len(args) > 0 {
		method, exists := cli.getMethod(args[0])
		if !exists {
			fmt.Printf("%s: '%s' is not a %s command. See '%s --help'.\n", os.Args[0], args[0], os.Args[0], os.Args[0])
			os.Exit(1)
		}
		return method(args[1:]...)
	}
	return cli.HyperCmdHelp()
}
<commit_msg>use os.Stderr for HyperClient.err<commit_after>package client

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"text/template"

	"github.com/hyperhq/hyperd/client/api"
	"github.com/hyperhq/runv/lib/term"

	"github.com/docker/docker/cliconfig"
	"github.com/docker/docker/pkg/homedir"
)

type HyperClient struct {
	client        api.APIInterface
	in            io.ReadCloser
	out           io.Writer
	err           io.Writer
	inFd          uintptr
	outFd         uintptr
	isTerminalIn  bool
	isTerminalOut bool
	configFile    *cliconfig.ConfigFile
}

func NewHyperClient(proto, addr string, tlsConfig *tls.Config) *HyperClient {
	var (
		inFd          uintptr
		outFd         uintptr
		isTerminalIn  = false
		isTerminalOut = false
	)

	clifile, err := cliconfig.Load(filepath.Join(homedir.Get(), ".docker"))
	if err != nil {
		fmt.Fprintf(os.Stdout, "WARNING: Error loading config file %v\n", err)
	}

	inFd, isTerminalIn = term.GetFdInfo(os.Stdin)
	outFd, isTerminalOut = term.GetFdInfo(os.Stdout)

	return &HyperClient{
		client:        api.NewClient(proto, addr, tlsConfig),
		in:            os.Stdin,
		out:           os.Stdout,
		err:           os.Stderr,
		inFd:          inFd,
		outFd:         outFd,
		isTerminalIn:  isTerminalIn,
		isTerminalOut: isTerminalOut,
		configFile:    clifile,
	}
}

var funcMap = template.FuncMap{
	"json": func(v interface{}) string {
		a, _ := json.Marshal(v)
		return string(a)
	},
}

func (cli *HyperClient) getMethod(args ...string) (func(...string) error, bool) {
	camelArgs := make([]string, len(args))
	for i, s := range args {
		if len(s) == 0 {
			return nil, false
		}
		camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
	}
	methodName := "HyperCmd" + strings.Join(camelArgs, "")
	method := reflect.ValueOf(cli).MethodByName(methodName)
	if !method.IsValid() {
		return nil, false
	}
	return method.Interface().(func(...string) error), true
}

// Cmd executes the specified command.
func (cli *HyperClient) Cmd(args ...string) error {
	if len(args) > 1 {
		method, exists := cli.getMethod(args[:2]...)
		if exists {
			return method(args[2:]...)
		}
	}
	if len(args) > 0 {
		method, exists := cli.getMethod(args[0])
		if !exists {
			fmt.Printf("%s: '%s' is not a %s command. See '%s --help'.\n", os.Args[0], args[0], os.Args[0], os.Args[0])
			os.Exit(1)
		}
		return method(args[1:]...)
	}
	return cli.HyperCmdHelp()
}
<|endoftext|>


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

package core

import (
	"fmt"

	"github.com/google/blueprint"
)

type defaults struct {
	library
}

func (m *defaults) supportedVariants() []tgtType {
	return []tgtType{tgtTypeHost, tgtTypeTarget}
}

func (m *defaults) disable() {
	panic("disable() called on Default")
}

func (m *defaults) setVariant(variant tgtType) {
	m.library.setVariant(variant)
}

func (m *defaults) getSplittableProps() *SplittableProps {
	return m.library.getSplittableProps()
}

func (m *defaults) GenerateBuildActions(ctx blueprint.ModuleContext) {
}

func defaultsFactory(config *bobConfig) (blueprint.Module, []interface{}) {
	module := &defaults{}
	return module.LibraryFactory(config, module)
}

var defaultDepTag = dependencyTag{name: "default"}

// Modules implementing defaultable can refer to bob_defaults via the
// `defaults` property
type defaultable interface {
	build() *Build
	features() *Features
	defaults() []string
}

func defaultDepsMutator(mctx blueprint.BottomUpMutatorContext) {
	if l, ok := mctx.Module().(defaultable); ok {
		mctx.AddDependency(mctx.Module(), defaultDepTag, l.defaults()...)
	}
	if gsc, ok := getGenerateCommon(mctx.Module()); ok {
		if len(gsc.Properties.Flag_defaults) > 0 {
			tgt := gsc.Properties.Target
			if !(tgt == tgtTypeHost || tgt == tgtTypeTarget) {
				panic(fmt.Errorf("Module %s uses flag_defaults '%v' but has invalid target type '%s'",
					mctx.ModuleName(), gsc.Properties.Flag_defaults, tgt))
			}
			mctx.AddDependency(mctx.Module(), defaultDepTag, gsc.Properties.Flag_defaults...)
		}
	}
}
<commit_msg>Make bob_defaults an independent module type<commit_after>/*
 * Copyright 2018-2020 Arm Limited.
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package core

import (
	"fmt"

	"github.com/google/blueprint"
)

type defaults struct {
	moduleBase

	Properties struct {
		Features
		Build
		// The list of default properties that should prepended to all configuration
		Defaults []string
	}
}

func (m *defaults) supportedVariants() []tgtType {
	return []tgtType{tgtTypeHost, tgtTypeTarget}
}

func (m *defaults) disable() {
	panic("disable() called on Default")
}

func (m *defaults) setVariant(variant tgtType) {
	m.Properties.TargetType = variant
}

func (m *defaults) getSplittableProps() *SplittableProps {
	return &SplittableProps{}
}

func (m *defaults) defaults() []string {
	return m.Properties.Defaults
}

func (m *defaults) build() *Build {
	return &m.Properties.Build
}

func (m *defaults) topLevelProperties() []interface{} {
	return []interface{}{&m.Properties.Build.BuildProps}
}

func (m *defaults) features() *Features {
	return &m.Properties.Features
}

func (m *defaults) getTarget() tgtType {
	return m.Properties.TargetType
}

func (m *defaults) processPaths(ctx blueprint.BaseModuleContext, g generatorBackend) {
	m.Properties.Build.processPaths(ctx, g)
}

func (m *defaults) GenerateBuildActions(ctx blueprint.ModuleContext) {
}

func defaultsFactory(config *bobConfig) (blueprint.Module, []interface{}) {
	module := &defaults{}

	module.Properties.Features.Init(&config.Properties, BuildProps{})
	module.Properties.Build.Target.Init(&config.Properties, BuildProps{})
	module.Properties.Build.Host.Init(&config.Properties, BuildProps{})

	return module, []interface{}{&module.Properties, &module.SimpleName.Properties}
}

var defaultDepTag = dependencyTag{name: "default"}

// Modules implementing defaultable can refer to bob_defaults via the
// `defaults` property
type defaultable interface {
	build() *Build
	features() *Features
	defaults() []string
}

// Defaults use other defaults, so are themselves `defaultable`
var _ defaultable = (*defaults)(nil)

// Defaults have build properties
var _ moduleWithBuildProps = (*defaults)(nil)

// Defaults have host and target variants
var _ targetable = (*defaults)(nil)

// Defaults support conditional properties via "features"
var _ featurable = (*defaults)(nil)

// Defaults contain path fragments which need to be prefixes
var _ pathProcessor = (*defaults)(nil)

func defaultDepsMutator(mctx blueprint.BottomUpMutatorContext) {
	if l, ok := mctx.Module().(defaultable); ok {
		mctx.AddDependency(mctx.Module(), defaultDepTag, l.defaults()...)
	}
	if gsc, ok := getGenerateCommon(mctx.Module()); ok {
		if len(gsc.Properties.Flag_defaults) > 0 {
			tgt := gsc.Properties.Target
			if !(tgt == tgtTypeHost || tgt == tgtTypeTarget) {
				panic(fmt.Errorf("Module %s uses flag_defaults '%v' but has invalid target type '%s'",
					mctx.ModuleName(), gsc.Properties.Flag_defaults, tgt))
			}
			mctx.AddDependency(mctx.Module(), defaultDepTag, gsc.Properties.Flag_defaults...)
		}
	}
}
<|endoftext|>


<commit_before>package main

import "github.com/tiko-chan/bbs"
import "net/http"
import "encoding/json"
import "fmt"
import "io/ioutil"
import "bytes"
import "strings"
import "bufio"
import "os"
import "strconv"

var bbsServer = "http://localhost:8080/bbs"
var session = ""
var lastLine = ""
var verbose = true

const client_version string = "test-client 0.1"

func main() {
	testClient()
}

func testClient() {
	if len(os.Args) > 1 {
		bbsServer = os.Args[1]
	}

	fmt.Println("Running test client: " + client_version)
	fmt.Printf("Connecting to %s...\n", bbsServer)
	hello, _ := json.Marshal(&bbs.BBSCommand{"hello"})
	send(hello)

	r := bufio.NewReader(os.Stdin)
	fmt.Println("Type help for a list of commands or quit to exit.")
	fmt.Print("> ")
	for {
		line, err := r.ReadString('\n')
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		input(line)
		fmt.Print("> ")
	}
}

func input(line string) {
	fields := strings.Fields(line)
	if len(fields) == 0 {
		return
	}

	switch fields[0] {
	case ".":
		input(lastLine)
	case "help":
		fmt.Println("COMMANDS\n\tlogin username password\n\tnigol password username\n\tget topicID [filterID]\n\tboards\n\tlist [expression]\n\treply [topicID] [text]\n\tquit\n\thelp\n\t. (repeat last command)")
	case "quit":
		os.Exit(0)
	case "exit":
		os.Exit(0)
	case "login":
		args := strings.SplitN(line, " ", 3)
		if strings.Contains(args[2], " ") {
			fmt.Println("WARNING: If you have spaces in your username, you should use 'nigol' instead.")
		}
		if len(fields) != 3 {
			fmt.Println("Input error.")
			fmt.Println("usage: login username password")
		}
		doLogin(fields[1], fields[2])
		lastLine = line
	case "nigol":
		args := strings.SplitN(line, " ", 3)
		if strings.Contains(args[2], " ") {
			fmt.Println("WARNING: If you have spaces in your password, you should use 'login' instead. If you have spaces in both, you're SOL.")
		}
		if len(fields) != 3 {
			fmt.Println("Input error.")
			fmt.Println("usage: nigol password username")
		}
		doLogin(fields[2], fields[1])
		lastLine = line
	case "get":
		if len(fields) == 2 {
			doGet(fields[1], &bbs.Range{1, 50}, "", "")
		} else if len(fields) == 3 {
			doGet(fields[2], nil, fields[1], "")
		} else if len(fields) == 4 {
			lwr, _ := strconv.Atoi(fields[2])
			hrr, _ := strconv.Atoi(fields[3])
			doGet(fields[1], &bbs.Range{lwr, hrr}, "", "")
		} else {
			fmt.Println("Input error.")
			fmt.Println("usage: get topicID [lower upper filter]")
			fmt.Println("usage: get board topicID")
		}
		lastLine = line
	case "list":
		if len(fields) == 1 {
			doList("")
		} else if len(fields) == 2 {
			doList(fields[1])
		} else {
			fmt.Println("Input error.")
			fmt.Println("usage: list [expression]")
		}
		lastLine = line
	case "boards":
		doListBoards()
		lastLine = line
	case "reply":
		args := strings.SplitN(line, " ", 3)
		if len(args) < 3 {
			fmt.Println("Input error.")
			fmt.Println("usage: reply topicID text...")
		} else {
			doReply(args[1], strings.Trim(args[2], " \n"))
		}
		lastLine = line
	default:
		fmt.Println("What?")
	}
}

func doLogin(u, pw string) {
	login, _ := json.Marshal(&bbs.LoginCommand{"login", u, pw, 0})
	send(login)
}

func doList(exp string) {
	list, _ := json.Marshal(&bbs.ListCommand{"list", session, "thread", exp})
	send(list)
}

func doListBoards() {
	list, _ := json.Marshal(&bbs.ListCommand{"list", session, "board", ""})
	send(list)
}

func doGet(t string, r *bbs.Range, board string, filter string) {
	get, _ := json.Marshal(&bbs.GetCommand{"get", session, t, board, r, filter, "text"})
	send(get)
}

func doReply(id, text string) {
	reply, _ := json.Marshal(&bbs.ReplyCommand{"reply", session, id, "", text, "html"})
	send(reply)
}

func getURL(url string) string {
	resp, _ := http.Get(url)
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	return string(body)
}

func parse(js []byte) {
	if verbose {
		fmt.Println("server -> client")
		fmt.Println(string(js))
	}

	bbscmd := new(bbs.BBSCommand)
	err := json.Unmarshal(js, bbscmd)
	if err != nil {
		fmt.Println("JSON Parsing Error!! " + string(js))
		return
	}

	switch bbscmd.Command {
	case "error":
		m := bbs.ErrorMessage{}
		json.Unmarshal(js, &m)
		onError(&m)
	case "hello":
		m := bbs.HelloMessage{}
		json.Unmarshal(js, &m)
		onHello(&m)
	case "welcome":
		m := bbs.WelcomeMessage{}
		json.Unmarshal(js, &m)
		onWelcome(&m)
	case "msg":
		m := bbs.ThreadMessage{}
		json.Unmarshal(js, &m)
		onMsg(&m)
	case "ok":
		m := bbs.OKMessage{}
		json.Unmarshal(js, &m)
		fmt.Println("(OK: " + m.ReplyTo + ") " + m.Message)
	case "list":
		t := bbs.TypedMessage{}
		json.Unmarshal(js, &t)
		if t.Type == "thread" {
			m := bbs.ListMessage{}
			json.Unmarshal(js, &m)
			onList(&m)
		} else if t.Type == "board" {
			m := bbs.BoardListMessage{}
			json.Unmarshal(js, &m)
			onBoardList(&m)
		}
	}
}

func onError(msg *bbs.ErrorMessage) {
	prettyPrint("Error on: "+msg.ReplyTo, msg.Error)
}

func onHello(msg *bbs.HelloMessage) {
	prettyPrint("Connected", fmt.Sprintf("Name: %s\nDesc: %s\nOptions: %v\nVersion: %d\nServer: %s\n", msg.Name, msg.Description, msg.Options, msg.ProtocolVersion, msg.ServerVersion))
}

func onWelcome(msg *bbs.WelcomeMessage) {
	prettyPrint("Welcome "+msg.Username, "Session: "+msg.Session)
	session = msg.Session
}

func onMsg(msg *bbs.ThreadMessage) {
	fmt.Printf("Thread: %s [%d] \n  Tags: %s \n", msg.Title, len(msg.Messages), strings.Join(msg.Tags, ", "))
	if msg.Closed {
		fmt.Println("(Closed)")
	}
	for _, m := range msg.Messages {
		fmt.Printf("#%s User: %s | Date: %s | UserID: %s \n", m.ID, m.Author, m.Date, m.AuthorID)
		fmt.Println(m.Text + "\n")
	}
}

func onList(msg *bbs.ListMessage) {
	prettyPrint("Threads", msg.Query)
	for _, t := range msg.Threads {
		info := ""
		if t.Closed && t.Sticky {
			info = "(Sticky, Closed)"
		} else if t.Closed {
			info = "(Closed)"
		} else if t.Sticky {
			info = "(Sticky)"
		}
		newposts := " "
		if t.UnreadPosts > 0 {
			newposts = fmt.Sprintf(" (unread: %d) ", t.UnreadPosts)
		}
		fmt.Printf("#%s [%s] %s %s | %d posts%s| %s | %s\n", t.ID, t.Author, info, t.Title, t.PostCount, newposts, t.Date, strings.Join(t.Tags, ", "))
	}
}

func onBoardList(msg *bbs.BoardListMessage) {
	prettyPrint("Boards", msg.Query)
	for i, b := range msg.Boards {
		fmt.Printf("#%d (%s) %s\n", i, b.ID, b.Name)
		if b.Description != "" {
			fmt.Println(b.Description)
		}
	}
}

func send(js []byte) {
	if verbose {
		fmt.Println("client -> server")
		fmt.Println(string(js))
	}
	resp, err := http.Post(bbsServer, "application/json", bytes.NewReader(js))
	if err != nil {
		fmt.Printf("Could not send data. %v\n", err)
		return
	}
	body, _ := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	parse(body)
}

func prettyPrint(s1, s2 string) {
	fmt.Printf("### %s\n%s\n", s1, s2)
}
<commit_msg>catching up<commit_after>package main

import "github.com/tiko-chan/bbs"
import "net/http"
import "encoding/json"
import "fmt"
import "io/ioutil"
import "bytes"
import "strings"
import "bufio"
import "os"
import "strconv"

var bbsServer = "http://localhost:8080/bbs"
var session = ""
var lastLine = ""
var verbose = true

const client_version string = "test-client 0.1" //TODO: use this in User-Agent

func main() {
	testClient()
}

func testClient() {
	if len(os.Args) > 1 {
		bbsServer = os.Args[1]
	}

	fmt.Println("Running test client: " + client_version)
	fmt.Printf("Connecting to %s...\n", bbsServer)
	hello, _ := json.Marshal(&bbs.BBSCommand{"hello"})
	send(hello)

	r := bufio.NewReader(os.Stdin)
	fmt.Println("Type help for a list of commands or quit to exit.")
	fmt.Print("> ")
	for {
		line, err := r.ReadString('\n')
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		input(line)
		fmt.Print("> ")
	}
}

func input(line string) {
	fields := strings.Fields(line)
	if len(fields) == 0 {
		return
	}

	switch fields[0] {
	case ".":
		input(lastLine)
	case "help":
		fmt.Println("COMMANDS\n\tlogin username password\n\tnigol password username\n\tget topicID [filterID]\n\tboards\n\tlist [expression]\n\treply [topicID] [text]\n\tquit\n\thelp\n\t. (repeat last command)")
	case "quit":
		os.Exit(0)
	case "exit":
		os.Exit(0)
	case "login":
		args := strings.SplitN(line, " ", 3)
		if strings.Contains(args[2], " ") {
			fmt.Println("WARNING: If you have spaces in your username, you should use 'nigol' instead.")
		}
		if len(fields) != 3 {
			fmt.Println("Input error.")
			fmt.Println("usage: login username password")
		}
		doLogin(fields[1], fields[2])
		lastLine = line
	case "nigol":
		args := strings.SplitN(line, " ", 3)
		if strings.Contains(args[2], " ") {
			fmt.Println("WARNING: If you have spaces in your password, you should use 'login' instead. If you have spaces in both, you're SOL.")
		}
		if len(fields) != 3 {
			fmt.Println("Input error.")
			fmt.Println("usage: nigol password username")
		}
		doLogin(fields[2], fields[1])
		lastLine = line
	case "get":
		if len(fields) == 2 {
			doGet(fields[1], &bbs.Range{1, 50}, "")
		} else if len(fields) == 4 {
			lwr, _ := strconv.Atoi(fields[2])
			hrr, _ := strconv.Atoi(fields[3])
			doGet(fields[1], &bbs.Range{lwr, hrr}, "")
		} else {
			fmt.Println("Input error.")
			fmt.Println("usage: get topicID [lower upper filter]")
			fmt.Println("usage: get board topicID")
		}
		lastLine = line
	case "list":
		if len(fields) == 1 {
			doList("")
		} else if len(fields) == 2 {
			doList(fields[1])
		} else {
			fmt.Println("Input error.")
			fmt.Println("usage: list [expression]")
		}
		lastLine = line
	case "boards":
		doListBoards()
		lastLine = line
	case "reply":
		args := strings.SplitN(line, " ", 3)
		if len(args) < 3 {
			fmt.Println("Input error.")
			fmt.Println("usage: reply topicID text...")
		} else {
			doReply(args[1], strings.Trim(args[2], " \n"))
		}
		lastLine = line
	default:
		fmt.Println("What?")
	}
}

func doLogin(u, pw string) {
	login, _ := json.Marshal(&bbs.LoginCommand{"login", u, pw, 0})
	send(login)
}

func doList(exp string) {
	list, _ := json.Marshal(&bbs.ListCommand{"list", session, "thread", exp})
	send(list)
}

func doListBoards() {
	list, _ := json.Marshal(&bbs.ListCommand{"list", session, "board", ""})
	send(list)
}

func doGet(t string, r *bbs.Range, filter string) {
	get, _ := json.Marshal(&bbs.GetCommand{"get", session, t, r, filter, "text"})
	send(get)
}

func doReply(id, text string) {
	reply, _ := json.Marshal(&bbs.ReplyCommand{"reply", session, id, text, "html"})
	send(reply)
}

func getURL(url string) string {
	resp, _ := http.Get(url)
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	return string(body)
}

func parse(js []byte) {
	if verbose {
		fmt.Println("server -> client")
		fmt.Println(string(js))
	}

	bbscmd := new(bbs.BBSCommand)
	err := json.Unmarshal(js, bbscmd)
	if err != nil {
		fmt.Println("JSON Parsing Error!! " + string(js))
		return
	}

	switch bbscmd.Command {
	case "error":
		m := bbs.ErrorMessage{}
		json.Unmarshal(js, &m)
		onError(&m)
	case "hello":
		m := bbs.HelloMessage{}
		json.Unmarshal(js, &m)
		onHello(&m)
	case "welcome":
		m := bbs.WelcomeMessage{}
		json.Unmarshal(js, &m)
		onWelcome(&m)
	case "msg":
		m := bbs.ThreadMessage{}
		json.Unmarshal(js, &m)
		onMsg(&m)
	case "ok":
		m := bbs.OKMessage{}
		json.Unmarshal(js, &m)
		fmt.Println("(OK: " + m.ReplyTo + ") " + m.Result)
	case "list":
		t := bbs.TypedMessage{}
		json.Unmarshal(js, &t)
		if t.Type == "thread" {
			m := bbs.ListMessage{}
			json.Unmarshal(js, &m)
			onList(&m)
		} else if t.Type == "board" {
			m := bbs.BoardListMessage{}
			json.Unmarshal(js, &m)
			onBoardList(&m)
		}
	}
}

func onError(msg *bbs.ErrorMessage) {
	prettyPrint("Error on: "+msg.ReplyTo, msg.Error)
}

func onHello(msg *bbs.HelloMessage) {
	prettyPrint("Connected", fmt.Sprintf("Name: %s\nDesc: %s\nOptions: %v\nVersion: %d\nServer: %s\n", msg.Name, msg.Description, msg.Options, msg.ProtocolVersion, msg.ServerVersion))
}

func onWelcome(msg *bbs.WelcomeMessage) {
	prettyPrint("Welcome "+msg.Username, "Session: "+msg.Session)
	session = msg.Session
}

func onMsg(msg *bbs.ThreadMessage) {
	fmt.Printf("Thread: %s [%d] \n  Tags: %s \n", msg.Title, len(msg.Messages), strings.Join(msg.Tags, ", "))
	if msg.Closed {
		fmt.Println("(Closed)")
	}
	for _, m := range msg.Messages {
		fmt.Printf("#%s User: %s | Date: %s | UserID: %s \n", m.ID, m.Author, m.Date, m.AuthorID)
		fmt.Println(m.Text + "\n")
	}
}

func onList(msg *bbs.ListMessage) {
	prettyPrint("Threads", msg.Query)
	for _, t := range msg.Threads {
		info := ""
		if t.Closed && t.Sticky {
			info = "(Sticky, Closed)"
		} else if t.Closed {
			info = "(Closed)"
		} else if t.Sticky {
			info = "(Sticky)"
		}
		newposts := " "
		if t.UnreadPosts > 0 {
			newposts = fmt.Sprintf(" (unread: %d) ", t.UnreadPosts)
		}
		fmt.Printf("#%s [%s] %s %s | %d posts%s| %s | %s\n", t.ID, t.Author, info, t.Title, t.PostCount, newposts, t.Date, strings.Join(t.Tags, ", "))
	}
}

func onBoardList(msg *bbs.BoardListMessage) {
	prettyPrint("Boards", msg.Query)
	for i, b := range msg.Boards {
		fmt.Printf("#%d (%s) %s\n", i, b.ID, b.Name)
		if b.Description != "" {
			fmt.Println(b.Description)
		}
	}
}

func send(js []byte) {
	if verbose {
		fmt.Println("client -> server")
		fmt.Println(string(js))
	}
	resp, err := http.Post(bbsServer, "application/json", bytes.NewReader(js))
	if err != nil {
		fmt.Printf("Could not send data. %v\n", err)
		return
	}
	body, _ := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	parse(body)
}

func prettyPrint(s1, s2 string) {
	fmt.Printf("### %s\n%s\n", s1, s2)
}
<|endoftext|>


<commit_before>package client

import (
	"bytes"
	"encoding/json"
	"fmt"
	"math"
	"net/http"
	"time"

	"github.com/concourse/baggageclaim"
	"github.com/concourse/baggageclaim/volume"
	"github.com/pivotal-golang/lager"
	"github.com/tedsuo/rata"
)

type Client interface {
	baggageclaim.Client
}

type client struct {
	requestGenerator *rata.RequestGenerator
	httpClient       *http.Client
}

func New(apiURL string) Client {
	return &client{
		requestGenerator: rata.NewRequestGenerator(apiURL, baggageclaim.Routes),
		httpClient: &http.Client{
			Transport: &http.Transport{
				DisableKeepAlives: true,
			},
		},
	}
}

func (c *client) CreateVolume(logger lager.Logger, volumeSpec baggageclaim.VolumeSpec) (baggageclaim.Volume, error) {
	strategy := volumeSpec.Strategy
	if strategy == nil {
		strategy = baggageclaim.EmptyStrategy{}
	}

	buffer := &bytes.Buffer{}
	json.NewEncoder(buffer).Encode(baggageclaim.VolumeRequest{
		Strategy:     strategy.Encode(),
		TTLInSeconds: uint(math.Ceil(volumeSpec.TTL.Seconds())),
		Properties:   volumeSpec.Properties,
		Privileged:   volumeSpec.Privileged,
	})

	request, _ := c.requestGenerator.CreateRequest(baggageclaim.CreateVolume, nil, buffer)
	response, err := c.httpClient.Do(request)
	if err != nil {
		return nil, err
	}

	defer response.Body.Close()

	if response.StatusCode != 201 {
		return nil, fmt.Errorf("unexpected response code of: %d", response.StatusCode)
	}

	if header := response.Header.Get("Content-Type"); header != "application/json" {
		return nil, fmt.Errorf("unexpected content-type of: %s", header)
	}

	var volumeResponse baggageclaim.VolumeResponse
	err = json.NewDecoder(response.Body).Decode(&volumeResponse)
	if err != nil {
		return nil, err
	}

	v, initialHeartbeatSuccess := c.newVolume(logger, volumeResponse)
	if !initialHeartbeatSuccess {
		return nil, volume.ErrVolumeDoesNotExist
	}
	return v, nil
}

func (c *client) ListVolumes(logger lager.Logger, properties baggageclaim.VolumeProperties) (baggageclaim.Volumes, error) {
	if properties == nil {
		properties = baggageclaim.VolumeProperties{}
	}

	request, err := c.requestGenerator.CreateRequest(baggageclaim.ListVolumes, nil, nil)
	if err != nil {
		return nil, err
	}

	queryString := request.URL.Query()
	for key, val := range properties {
		queryString.Add(key, val)
	}

	request.URL.RawQuery = queryString.Encode()

	response, err := c.httpClient.Do(request)
	if err != nil {
		return nil, err
	}

	defer response.Body.Close()

	if response.StatusCode != 200 {
		return nil, fmt.Errorf("unexpected response code of: %d", response.StatusCode)
	}

	if header := response.Header.Get("Content-Type"); header != "application/json" {
		return nil, fmt.Errorf("unexpected content-type of: %s", header)
	}

	var volumesResponse []baggageclaim.VolumeResponse
	err = json.NewDecoder(response.Body).Decode(&volumesResponse)
	if err != nil {
		return nil, err
	}

	var volumes baggageclaim.Volumes
	for _, vr := range volumesResponse {
		v, initialHeartbeatSuccess := c.newVolume(logger, vr)
		if initialHeartbeatSuccess {
			volumes = append(volumes, v)
		}
	}

	return volumes, nil
}

func (c *client) LookupVolume(logger lager.Logger, handle string) (baggageclaim.Volume, bool, error) {

	volumeResponse, found, err := c.getVolumeResponse(handle)
	if err != nil {
		return nil, false, err
	}
	if !found {
		return nil, found, nil
	}

	v, initialHeartbeatSuccess := c.newVolume(logger, volumeResponse)
	if !initialHeartbeatSuccess {
		return nil, false, nil
	}
	return v, true, nil
}

func (c *client) getVolumeResponse(handle string) (baggageclaim.VolumeResponse, bool, error) {
	request, err := c.requestGenerator.CreateRequest(baggageclaim.GetVolume, rata.Params{
		"handle": handle,
	}, nil)
	if err != nil {
		return baggageclaim.VolumeResponse{}, false, err
	}

	response, err := c.httpClient.Do(request)
	if err != nil {
		return baggageclaim.VolumeResponse{}, false, err
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		if response.StatusCode == http.StatusNotFound {
			return baggageclaim.VolumeResponse{}, false, nil
		}
		return baggageclaim.VolumeResponse{}, false, fmt.Errorf("unexpected response code of: %d", response.StatusCode)
	}

	if header := response.Header.Get("Content-Type"); header != "application/json" {
		return baggageclaim.VolumeResponse{}, false, fmt.Errorf("unexpected content-type of: %s", header)
	}

	var volumeResponse baggageclaim.VolumeResponse
	err = json.NewDecoder(response.Body).Decode(&volumeResponse)
	if err != nil {
		return baggageclaim.VolumeResponse{}, false, err
	}

	return volumeResponse, true, nil
}

func (c *client) setTTL(handle string, ttl time.Duration) error {
	buffer := &bytes.Buffer{}
	json.NewEncoder(buffer).Encode(baggageclaim.TTLRequest{
		Value: uint(math.Ceil(ttl.Seconds())),
	})

	request, err := c.requestGenerator.CreateRequest(baggageclaim.SetTTL, rata.Params{
		"handle": handle,
	}, buffer)
	if err != nil {
		return err
	}

	request.Header.Add("Content-type", "application/json")

	response, err := c.httpClient.Do(request)
	if err != nil {
		return err
	}

	defer response.Body.Close()

	if response.StatusCode == 404 {
		return baggageclaim.ErrVolumeNotFound
	}

	if response.StatusCode != 204 {
		return fmt.Errorf("unexpected response code of: %d", response.StatusCode)
	}

	return nil
}

func (c *client) setProperty(handle string, propertyName string, propertyValue string) error {
	buffer := &bytes.Buffer{}
	json.NewEncoder(buffer).Encode(baggageclaim.PropertyRequest{
		Value: propertyValue,
	})

	request, err := c.requestGenerator.CreateRequest(baggageclaim.SetProperty, rata.Params{
		"handle":   handle,
		"property": propertyName,
	}, buffer)
	if err != nil {
		return err
	}

	response, err := c.httpClient.Do(request)
	if err != nil {
		return err
	}

	defer response.Body.Close()

	if response.StatusCode == 404 {
		return baggageclaim.ErrVolumeNotFound
	}

	if response.StatusCode != 204 {
		return fmt.Errorf("unexpected response code of: %d", response.StatusCode)
	}

	return nil
}
<commit_msg>retry connections to baggageclaim<commit_after>package client

import (
	"bytes"
	"encoding/json"
	"fmt"
	"math"
	"net/http"
	"time"

	"github.com/concourse/pester"
	"github.com/pivotal-golang/lager"
	"github.com/tedsuo/rata"

	"github.com/concourse/baggageclaim"
	"github.com/concourse/baggageclaim/volume"
)

type Client interface {
	baggageclaim.Client
}

type client struct {
	requestGenerator *rata.RequestGenerator
	httpClient       *pester.Client
}

func New(apiURL string) Client {
	httpClient := pester.New()
	httpClient.Transport = &http.Transport{
		DisableKeepAlives: true,
	}
	httpClient.MaxRetries = 12
	httpClient.Backoff = pester.ExponentialBackoff

	return &client{
		requestGenerator: rata.NewRequestGenerator(apiURL, baggageclaim.Routes),
		httpClient:       httpClient,
	}
}

func (c *client) CreateVolume(logger lager.Logger, volumeSpec baggageclaim.VolumeSpec) (baggageclaim.Volume, error) {
	strategy := volumeSpec.Strategy
	if strategy == nil {
		strategy = baggageclaim.EmptyStrategy{}
	}

	buffer := &bytes.Buffer{}
	json.NewEncoder(buffer).Encode(baggageclaim.VolumeRequest{
		Strategy:     strategy.Encode(),
		TTLInSeconds: uint(math.Ceil(volumeSpec.TTL.Seconds())),
		Properties:   volumeSpec.Properties,
		Privileged:   volumeSpec.Privileged,
	})

	request, _ := c.requestGenerator.CreateRequest(baggageclaim.CreateVolume, nil, buffer)
	response, err := c.httpClient.Do(request)
	if err != nil {
		return nil, err
	}

	defer response.Body.Close()

	if response.StatusCode != 201 {
		return nil, fmt.Errorf("unexpected response code of: %d", response.StatusCode)
	}

	if header := response.Header.Get("Content-Type"); header != "application/json" {
		return nil, fmt.Errorf("unexpected content-type of: %s", header)
	}

	var volumeResponse baggageclaim.VolumeResponse
	err = json.NewDecoder(response.Body).Decode(&volumeResponse)
	if err != nil {
		return nil, err
	}

	v, initialHeartbeatSuccess := c.newVolume(logger, volumeResponse)
	if !initialHeartbeatSuccess {
		return nil, volume.ErrVolumeDoesNotExist
	}
	return v, nil
}

func (c *client) ListVolumes(logger lager.Logger, properties baggageclaim.VolumeProperties) (baggageclaim.Volumes, error) {
	if properties == nil {
		properties = baggageclaim.VolumeProperties{}
	}

	request, err := c.requestGenerator.CreateRequest(baggageclaim.ListVolumes, nil, nil)
	if err != nil {
		return nil, err
	}

	queryString := request.URL.Query()
	for key, val := range properties {
		queryString.Add(key, val)
	}

	request.URL.RawQuery = queryString.Encode()

	response, err := c.httpClient.Do(request)
	if err != nil {
		return nil, err
	}

	defer response.Body.Close()

	if response.StatusCode != 200 {
		return nil, fmt.Errorf("unexpected response code of: %d", response.StatusCode)
	}

	if header := response.Header.Get("Content-Type"); header != "application/json" {
		return nil, fmt.Errorf("unexpected content-type of: %s", header)
	}

	var volumesResponse []baggageclaim.VolumeResponse
	err = json.NewDecoder(response.Body).Decode(&volumesResponse)
	if err != nil {
		return nil, err
	}

	var volumes baggageclaim.Volumes
	for _, vr := range volumesResponse {
		v, initialHeartbeatSuccess := c.newVolume(logger, vr)
		if initialHeartbeatSuccess {
			volumes = append(volumes, v)
		}
	}

	return volumes, nil
}

func (c *client) LookupVolume(logger lager.Logger, handle string) (baggageclaim.Volume, bool, error) {

	volumeResponse, found, err := c.getVolumeResponse(handle)
	if err != nil {
		return nil, false, err
	}
	if !found {
		return nil, found, nil
	}

	v, initialHeartbeatSuccess := c.newVolume(logger, volumeResponse)
	if !initialHeartbeatSuccess {
		return nil, false, nil
	}
	return v, true, nil
}

func (c *client) getVolumeResponse(handle string) (baggageclaim.VolumeResponse, bool, error) {
	request, err := c.requestGenerator.CreateRequest(baggageclaim.GetVolume, rata.Params{
		"handle": handle,
	}, nil)
	if err != nil {
		return baggageclaim.VolumeResponse{}, false, err
	}

	response, err := c.httpClient.Do(request)
	if err != nil {
		return baggageclaim.VolumeResponse{}, false, err
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		if response.StatusCode == http.StatusNotFound {
			return baggageclaim.VolumeResponse{}, false, nil
		}
		return baggageclaim.VolumeResponse{}, false, fmt.Errorf("unexpected response code of: %d", response.StatusCode)
	}

	if header := response.Header.Get("Content-Type"); header != "application/json" {
		return baggageclaim.VolumeResponse{}, false, fmt.Errorf("unexpected content-type of: %s", header)
	}

	var volumeResponse baggageclaim.VolumeResponse
	err = json.NewDecoder(response.Body).Decode(&volumeResponse)
	if err != nil {
		return baggageclaim.VolumeResponse{}, false, err
	}

	return volumeResponse, true, nil
}

func (c *client) setTTL(handle string, ttl time.Duration) error {
	buffer := &bytes.Buffer{}
	json.NewEncoder(buffer).Encode(baggageclaim.TTLRequest{
		Value: uint(math.Ceil(ttl.Seconds())),
	})

	request, err := c.requestGenerator.CreateRequest(baggageclaim.SetTTL, rata.Params{
		"handle": handle,
	}, buffer)
	if err != nil {
		return err
	}

	request.Header.Add("Content-type", "application/json")

	response, err := c.httpClient.Do(request)
	if err != nil {
		return err
	}

	defer response.Body.Close()

	if response.StatusCode == 404 {
		return baggageclaim.ErrVolumeNotFound
	}

	if response.StatusCode != 204 {
		return fmt.Errorf("unexpected response code of: %d", response.StatusCode)
	}

	return nil
}

func (c *client) setProperty(handle string, propertyName string, propertyValue string) error {
	buffer := &bytes.Buffer{}
	json.NewEncoder(buffer).Encode(baggageclaim.PropertyRequest{
		Value: propertyValue,
	})

	request, err := c.requestGenerator.CreateRequest(baggageclaim.SetProperty, rata.Params{
		"handle":   handle,
		"property": propertyName,
	}, buffer)
	if err != nil {
		return err
	}

	response, err := c.httpClient.Do(request)
	if err != nil {
		return err
	}

	defer response.Body.Close()

	if response.StatusCode == 404 {
		return baggageclaim.ErrVolumeNotFound
	}

	if response.StatusCode != 204 {
		return fmt.Errorf("unexpected response code of: %d", response.StatusCode)
	}

	return nil
}
<|endoftext|>