<commit_before>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains high level helper functions and easy entry points for the
// entire discordgo package.  These functions are being developed and are very
// experimental at this point.  They will most likely change so please use the
// low level functions if that's a problem.

// Package discordgo provides Discord binding for Go
package discordgo

import (
	"errors"
	"fmt"
	"net/http"
	"time"
)

// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
const VERSION = "0.20.2"

// ErrMFA will be risen by New when the user has 2FA.
var ErrMFA = errors.New("account has 2FA enabled")

// New creates a new Discord session and will automate some startup
// tasks if given enough information to do so.  Currently you can pass zero
// arguments and it will return an empty Discord session.
// There are 3 ways to call New:
//     With a single auth token - All requests will use the token blindly,
//         no verification of the token will be done and requests may fail.
//         IF THE TOKEN IS FOR A BOT, IT MUST BE PREFIXED WITH `BOT `
//         eg: `"Bot <token>"`
//     With an email and password - Discord will sign in with the provided
//         credentials.
//     With an email, password and auth token - Discord will verify the auth
//         token, if it is invalid it will sign in with the provided
//         credentials. This is the Discord recommended way to sign in.
//
// NOTE: While email/pass authentication is supported by DiscordGo it is
// HIGHLY DISCOURAGED by Discord. Please only use email/pass to obtain a token
// and then use that authentication token for all future connections.
// Also, doing any form of automation with a user (non Bot) account may result
// in that account being permanently banned from Discord.
func New(args ...interface{}) (s *Session, err error) {

	// Create an empty Session interface.
	s = &Session{
		State:                  NewState(),
		Ratelimiter:            NewRatelimiter(),
		StateEnabled:           true,
		Compress:               true,
		ShouldReconnectOnError: true,
		ShardID:                0,
		ShardCount:             1,
		MaxRestRetries:         3,
		Client:                 &http.Client{Timeout: (20 * time.Second)},
		UserAgent:              "DiscordBot (https://github.com/bwmarrin/discordgo, v" + VERSION + ")",
		sequence:               new(int64),
		LastHeartbeatAck:       time.Now().UTC(),
	}

	// If no arguments are passed return the empty Session interface.
	if args == nil {
		return
	}

	// Variables used below when parsing func arguments
	var auth, pass string

	// Parse passed arguments
	for _, arg := range args {

		switch v := arg.(type) {

		case []string:
			if len(v) > 3 {
				err = fmt.Errorf("too many string parameters provided")
				return
			}

			// First string is either token or username
			if len(v) > 0 {
				auth = v[0]
			}

			// If second string exists, it must be a password.
			if len(v) > 1 {
				pass = v[1]
			}

			// If third string exists, it must be an auth token.
			if len(v) > 2 {
				s.Token = v[2]
			}

		case string:
			// First string must be either auth token or username.
			// Second string must be a password.
			// Only 2 input strings are supported.

			if auth == "" {
				auth = v
			} else if pass == "" {
				pass = v
			} else if s.Token == "" {
				s.Token = v
			} else {
				err = fmt.Errorf("too many string parameters provided")
				return
			}

			//		case Config:
			// TODO: Parse configuration struct

		default:
			err = fmt.Errorf("unsupported parameter type provided")
			return
		}
	}

	// If only one string was provided, assume it is an auth token.
	// Otherwise get auth token from Discord, if a token was specified
	// Discord will verify it for free, or log the user in if it is
	// invalid.
	if pass == "" {
		s.Token = auth
	} else {
		err = s.Login(auth, pass)
		if err != nil || s.Token == "" {
			if s.MFA {
				err = ErrMFA
			} else {
				err = fmt.Errorf("Unable to fetch discord authentication token. %v", err)
			}
			return
		}
	}

	// The Session is now able to have RestAPI methods called on it.
	// It is recommended that you now call Open() so that events will trigger.

	return
}
<commit_msg>Version 0.20.3: allow resume on reconnect<commit_after>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains high level helper functions and easy entry points for the
// entire discordgo package.  These functions are being developed and are very
// experimental at this point.  They will most likely change so please use the
// low level functions if that's a problem.

// Package discordgo provides Discord binding for Go
package discordgo

import (
	"errors"
	"fmt"
	"net/http"
	"time"
)

// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
const VERSION = "0.20.3"

// ErrMFA will be risen by New when the user has 2FA.
var ErrMFA = errors.New("account has 2FA enabled")

// New creates a new Discord session and will automate some startup
// tasks if given enough information to do so.  Currently you can pass zero
// arguments and it will return an empty Discord session.
// There are 3 ways to call New:
//     With a single auth token - All requests will use the token blindly,
//         no verification of the token will be done and requests may fail.
//         IF THE TOKEN IS FOR A BOT, IT MUST BE PREFIXED WITH `BOT `
//         eg: `"Bot <token>"`
//     With an email and password - Discord will sign in with the provided
//         credentials.
//     With an email, password and auth token - Discord will verify the auth
//         token, if it is invalid it will sign in with the provided
//         credentials. This is the Discord recommended way to sign in.
//
// NOTE: While email/pass authentication is supported by DiscordGo it is
// HIGHLY DISCOURAGED by Discord. Please only use email/pass to obtain a token
// and then use that authentication token for all future connections.
// Also, doing any form of automation with a user (non Bot) account may result
// in that account being permanently banned from Discord.
func New(args ...interface{}) (s *Session, err error) {

	// Create an empty Session interface.
	s = &Session{
		State:                  NewState(),
		Ratelimiter:            NewRatelimiter(),
		StateEnabled:           true,
		Compress:               true,
		ShouldReconnectOnError: true,
		ShardID:                0,
		ShardCount:             1,
		MaxRestRetries:         3,
		Client:                 &http.Client{Timeout: (20 * time.Second)},
		UserAgent:              "DiscordBot (https://github.com/bwmarrin/discordgo, v" + VERSION + ")",
		sequence:               new(int64),
		LastHeartbeatAck:       time.Now().UTC(),
	}

	// If no arguments are passed return the empty Session interface.
	if args == nil {
		return
	}

	// Variables used below when parsing func arguments
	var auth, pass string

	// Parse passed arguments
	for _, arg := range args {

		switch v := arg.(type) {

		case []string:
			if len(v) > 3 {
				err = fmt.Errorf("too many string parameters provided")
				return
			}

			// First string is either token or username
			if len(v) > 0 {
				auth = v[0]
			}

			// If second string exists, it must be a password.
			if len(v) > 1 {
				pass = v[1]
			}

			// If third string exists, it must be an auth token.
			if len(v) > 2 {
				s.Token = v[2]
			}

		case string:
			// First string must be either auth token or username.
			// Second string must be a password.
			// Only 2 input strings are supported.

			if auth == "" {
				auth = v
			} else if pass == "" {
				pass = v
			} else if s.Token == "" {
				s.Token = v
			} else {
				err = fmt.Errorf("too many string parameters provided")
				return
			}

			//		case Config:
			// TODO: Parse configuration struct

		default:
			err = fmt.Errorf("unsupported parameter type provided")
			return
		}
	}

	// If only one string was provided, assume it is an auth token.
	// Otherwise get auth token from Discord, if a token was specified
	// Discord will verify it for free, or log the user in if it is
	// invalid.
	if pass == "" {
		s.Token = auth
	} else {
		err = s.Login(auth, pass)
		if err != nil || s.Token == "" {
			if s.MFA {
				err = ErrMFA
			} else {
				err = fmt.Errorf("Unable to fetch discord authentication token. %v", err)
			}
			return
		}
	}

	// The Session is now able to have RestAPI methods called on it.
	// It is recommended that you now call Open() so that events will trigger.

	return
}
<|endoftext|>


<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 config_test

import (
	"github.com/jacobsa/comeback/config"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
	"testing"
)

func TestParse(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type ParseTest struct {
	data string
	cfg *config.Config
	err error
}

func init() { RegisterTestSuite(&ParseTest{}) }

func (t *ParseTest) parse() {
	t.cfg, t.err = config.Parse([]byte(t.data))
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *ParseTest) TotalJunk() {
	t.data = "sdhjklfghdskjghdjkfgj"
	t.parse()

	ExpectThat(t.err, Error(HasSubstr("TODO")))
}

func (t *ParseTest) NonObject() {
	t.data = `[17, 19]`
	t.parse()

	ExpectThat(t.err, Error(HasSubstr("TODO")))
}

func (t *ParseTest) MissingTrailingBrace() {
	t.data = `
	{
		"jobs": []
	`

	t.parse()

	ExpectThat(t.err, Error(HasSubstr("TODO")))
}

func (t *ParseTest) BasePathIsNumber() {
	t.data = `
	{
		"jobs": [
			{
				"base_path": 17
			}
		]
	}
	`

	t.parse()

	ExpectThat(t.err, Error(HasSubstr("TODO")))
}

func (t *ParseTest) BasePathIsNull() {
	t.data = `
	{
		"jobs": [
			{
				"base_path": null
			}
		]
	}
	`

	t.parse()

	ExpectThat(t.err, Error(HasSubstr("TODO")))
}

func (t *ParseTest) BasePathIsObject() {
	t.data = `
	{
		"jobs": [
			{
				"base_path": {}
			}
		]
	}
	`

	t.parse()

	ExpectThat(t.err, Error(HasSubstr("TODO")))
}

func (t *ParseTest) OneExcludeDoesntCompile() {
	t.data = `
	{
		"jobs": [
			{
				"base_path": "/foo",
				"excludes": ["a"],
			},
			{
				"base_path": "/bar",
				"excludes": ["b", "(c"]
			},
			{
				"base_path": "/foo",
				"excludes": ["d"]
			}
		]
	}
	`

	t.parse()

	ExpectThat(t.err, Error(HasSubstr("TODO")))
}

func (t *ParseTest) EmptyConfig() {
	ExpectEq("TODO", "")
}

func (t *ParseTest) MissingExcludesArray() {
	ExpectEq("TODO", "")
}

func (t *ParseTest) DuplicateJobName() {
	ExpectEq("TODO", "")
}

func (t *ParseTest) StructurallyValid() {
	ExpectEq("TODO", "")
}
<commit_msg>ParseTest.EmptyConfig<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 config_test

import (
	"github.com/jacobsa/comeback/config"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
	"testing"
)

func TestParse(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type ParseTest struct {
	data string
	cfg *config.Config
	err error
}

func init() { RegisterTestSuite(&ParseTest{}) }

func (t *ParseTest) parse() {
	t.cfg, t.err = config.Parse([]byte(t.data))
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *ParseTest) TotalJunk() {
	t.data = "sdhjklfghdskjghdjkfgj"
	t.parse()

	ExpectThat(t.err, Error(HasSubstr("TODO")))
}

func (t *ParseTest) NonObject() {
	t.data = `[17, 19]`
	t.parse()

	ExpectThat(t.err, Error(HasSubstr("TODO")))
}

func (t *ParseTest) MissingTrailingBrace() {
	t.data = `
	{
		"jobs": []
	`

	t.parse()

	ExpectThat(t.err, Error(HasSubstr("TODO")))
}

func (t *ParseTest) BasePathIsNumber() {
	t.data = `
	{
		"jobs": [
			{
				"base_path": 17
			}
		]
	}
	`

	t.parse()

	ExpectThat(t.err, Error(HasSubstr("TODO")))
}

func (t *ParseTest) BasePathIsNull() {
	t.data = `
	{
		"jobs": [
			{
				"base_path": null
			}
		]
	}
	`

	t.parse()

	ExpectThat(t.err, Error(HasSubstr("TODO")))
}

func (t *ParseTest) BasePathIsObject() {
	t.data = `
	{
		"jobs": [
			{
				"base_path": {}
			}
		]
	}
	`

	t.parse()

	ExpectThat(t.err, Error(HasSubstr("TODO")))
}

func (t *ParseTest) OneExcludeDoesntCompile() {
	t.data = `
	{
		"jobs": [
			{
				"base_path": "/foo",
				"excludes": ["a"],
			},
			{
				"base_path": "/bar",
				"excludes": ["b", "(c"]
			},
			{
				"base_path": "/foo",
				"excludes": ["d"]
			}
		]
	}
	`

	t.parse()

	ExpectThat(t.err, Error(HasSubstr("TODO")))
}

func (t *ParseTest) EmptyConfig() {
	t.data = `{}`
	t.parse()

	AssertEq(nil, t.err)
	ExpectNe(nil, t.cfg.Jobs)
	ExpectEq(0, len(t.cfg.Jobs))
}

func (t *ParseTest) MissingExcludesArray() {
	ExpectEq("TODO", "")
}

func (t *ParseTest) DuplicateJobName() {
	ExpectEq("TODO", "")
}

func (t *ParseTest) StructurallyValid() {
	ExpectEq("TODO", "")
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"github.com/mitchellh/cli"
	"strings"
)

// InfoCommand is a Command implementation which retrieves info
// about the coreId from spark cloud
type InfoCommand struct {
	Ui cli.Ui
}

func (c *InfoCommand) Help() string {
	helpText := `
Usage: spark info {coreId}

  Get basic information about the given Core,
  including the custom variables and functions it has exposed.
`
	return strings.TrimSpace(helpText)
}

func (c *InfoCommand) Run(args []string) int {
	coreId := args[0]

	client, err := AuthenticatedSparkClient(true)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed connecting to spark cloud: %s", err))
		return 1
	}

	info, resp, err := client.Devices.Info(coreId)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed retrieving info: %s", err))
		c.Ui.Error(fmt.Sprintf("Response: %s", resp))
		return 1
	}

	c.Ui.Info(fmt.Sprintf("Info for %s [%s]", info.Name, info.Id))
	c.Ui.Info("Variables:")
	for varName, varType := range info.Variables {
		c.Ui.Info(fmt.Sprintf(" - %s : %s", varName, varType))
	}

	c.Ui.Info("Functions:")
	for _, funcName := range info.Functions {
		c.Ui.Info(fmt.Sprintf(" - %s", funcName))
	}
	return 0
}

func (c *InfoCommand) Synopsis() string {
	return "Displays basic information about the given Core"
}
<commit_msg>Validating arguments passed to info command<commit_after>package command

import (
	"fmt"
	"github.com/mitchellh/cli"
	"strings"
)

// InfoCommand is a Command implementation which retrieves info
// about the coreId from spark cloud
type InfoCommand struct {
	Ui cli.Ui
}

func (c *InfoCommand) Help() string {
	helpText := `
Usage: spark info {coreId}

  Get basic information about the given Core,
  including the custom variables and functions it has exposed.
`
	return strings.TrimSpace(helpText)
}

func (c *InfoCommand) Run(args []string) int {
	if len(args) != 1 {
		c.Ui.Error("A coreId must be specified.")
		c.Ui.Error("")
		c.Ui.Error(c.Help())
		return 1
	}

	coreId := args[0]

	client, err := AuthenticatedSparkClient(true)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed connecting to spark cloud: %s", err))
		return 1
	}

	info, resp, err := client.Devices.Info(coreId)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed retrieving info: %s", err))
		c.Ui.Error(fmt.Sprintf("Response: %s", resp))
		return 1
	}

	c.Ui.Info(fmt.Sprintf("Info for %s [%s]", info.Name, info.Id))
	c.Ui.Info("Variables:")
	for varName, varType := range info.Variables {
		c.Ui.Info(fmt.Sprintf(" - %s : %s", varName, varType))
	}

	c.Ui.Info("Functions:")
	for _, funcName := range info.Functions {
		c.Ui.Info(fmt.Sprintf(" - %s", funcName))
	}
	return 0
}

func (c *InfoCommand) Synopsis() string {
	return "Displays basic information about the given Core"
}
<|endoftext|>


<commit_before>package command

import (
	"encoding/json"
	"fmt"

	"github.com/jessevdk/go-flags"
	"github.com/ondevice/ondevice/api"
	"github.com/ondevice/ondevice/logg"
)

type listCommand struct {
	BaseCommand
}

// ListOpts -- commandline arguments for `ondevice list`
var ListOpts struct {
	Properties bool   `short:"p" long:"props" description:"Include properties in the JSON output"`
	JSON       bool   `short:"j" long:"json" description:"JSON output, on object/device per line"`
	State      string `long:"state" description:"Filter output by device state, one of online/offline"`
}

func (l listCommand) run(args []string) int {
	// parse args
	opts := ListOpts
	if _, err := flags.ParseArgs(&opts, args); err != nil {
		logg.Fatal(err)
	}

	devices, err := api.ListDevices(opts.State, opts.Properties)
	if err != nil {
		logg.Fatal(err)
	}

	if opts.JSON {
		l.printJSON(devices)
	} else {
		l.print(devices)
	}

	return 0
}

func (l listCommand) print(devices []api.Device) {
	// find the maximum lengths for each column
	titles := []string{"ID", "State", "IP", "Version", "Name"}
	widths := []int{2, 5, 2, 7, 4}
	for _, dev := range devices {
		cols := _getColumns(dev)
		for j, col := range cols {
			width := len(col)
			if width > widths[j] {
				widths[j] = width
			}
		}
	}

	_printColumns(widths, titles)

	for _, dev := range devices {
		_printColumns(widths, _getColumns(dev))
	}
}

func (l listCommand) printJSON(devs []api.Device) {
	for _, dev := range devs {
		out, err := json.Marshal(dev)
		if err != nil {
			logg.Fatal("JSON serialization failed: ", err)
		}
		fmt.Println(string(out))
	}
}

func _getColumns(dev api.Device) []string {
	return []string{dev.ID, dev.State, dev.IP, dev.Version, dev.Name}
}

func _printColumns(widths []int, cols []string) {
	if len(widths) != len(cols) {
		logg.Fatal("mismatch between cols and widths count", cols, widths)
	}

	for i, width := range widths {
		_printValue(width, cols[i])
		fmt.Print(" ")
	}
	fmt.Println("")
}

func _printValue(width int, val string) {
	if len(val) > width {
		logg.Fatal("width < len(val) !")
	}
	fmt.Print(val)
	for i := len(val); i < width; i++ {
		fmt.Print(" ")
	}
}

// ListCommand -- implements `ondevice list`
var ListCommand = listCommand{
	BaseCommand: BaseCommand{
		Arguments: "[--json] [--props] [--status=<online/offline>]",
		ShortHelp: "List your devices",
		RunFn:     nil, // we're implementing our own run() method
		LongHelp: `$ ondevice list

List your devices

Options:
--json
  output JSON, one line/object per device
--props
  include properties (only affects JSON output)
--state <online/offline>
  limit to devices that are on/offline

Example:

  $ ondevice list
  ID                State   IP             Version         Name
  demo.7t91ta   offline                ondevice v0.4.3
  demo.fbqh2p   offline 192.168.1.23   ondevice v0.3.9
  demo.q5dkpm   online  127.0.0.1      ondevice v0.4.2
  demo.thm7br   offline 10.0.0.127     ondevice v0.4.3

  $ ondevice list --json --props
  {"id":"demo.7t91ta",state":"offline","stateTs":1490197318991,"version":"ondevice v0.4.3"}
  {"id":"demo.fbqh2p","ip":"192.168.1.23","state":"offline","stateTs":1485721709598,"version":"ondevice v0.3.9"}
  {"id":"demo.q5dkpm","ip":"127.0.0.1","state":"offline","stateTs":1487068641353,"version":"ondevice v0.4.2","props":{"test":"1234"}}
  {"id":"demo.thm7br","ip":"10.0.0.127","state":"offline","stateTs":1490963689912,"version":"ondevice v0.4.3"}

Keep in mind that JSON fields may be missing or null
`,
	},
}
<commit_msg>`ondevice list`: added simple filter expression support<commit_after>package command

import (
	"encoding/json"
	"fmt"

	"github.com/jessevdk/go-flags"
	"github.com/ondevice/ondevice/api"
	"github.com/ondevice/ondevice/config"
	"github.com/ondevice/ondevice/logg"
	"github.com/ondevice/ondevice/property"
)

type listCommand struct {
	BaseCommand
}

// ListOpts -- commandline arguments for `ondevice list`
var ListOpts struct {
	Properties bool   `short:"p" long:"props" description:"Include properties in the JSON output"`
	JSON       bool   `short:"j" long:"json" description:"JSON output, on object/device per line"`
	State      string `long:"state" description:"Filter output by device state, one of online/offline"`
	User       string `long:"user" description:"List devices of another user"`
	PrintIDs   bool   `long:"print-ids" description:"Print only "`
}

func (l listCommand) run(args []string) int {
	// parse args
	opts := ListOpts
	var filters []string
	var err error
	if filters, err = flags.ParseArgs(&opts, args); err != nil {
		logg.Fatal(err)
	}

	// check output flags
	if opts.JSON && opts.PrintIDs {
		logg.Fatal("Specified conflicting output modes (--json and --print-ids)")
	}

	// --user
	var auths []api.Authentication
	if opts.User != "" {
		var user, pwd string
		if user, pwd, err = config.GetClientUserAuth(opts.User); err == nil {
			auths = append(auths, api.CreateAuth(user, pwd))
		} else {
			logg.Errorf("Can't find client auth for user '%s'", opts.User)
			return 1
		}
	}

	// imply --props if filters have been specified
	if len(filters) > 0 {
		opts.Properties = true
	}

	allDevices, err := api.ListDevices(opts.State, opts.Properties, auths...)
	if err != nil {
		logg.Fatal(err)
	}

	var devices = make([]api.Device, 0, len(allDevices))
	for _, dev := range allDevices {
		if ok, err := _matches(dev, filters); err != nil {
			logg.Fatal(err)
		} else if ok {
			devices = append(devices, dev)
		}
	}

	if opts.JSON {
		l.printJSON(devices)
	} else if opts.PrintIDs {
		for _, dev := range devices {
			if _, err = fmt.Println(dev.ID); err != nil {
				logg.Error("print failed: ", err)
				break
			}
		}
	} else {
		l.print(devices)
	}

	return 0
}

func (l listCommand) print(devices []api.Device) {
	// find the maximum lengths for each column
	titles := []string{"ID", "State", "IP", "Version", "Name"}
	widths := []int{2, 5, 2, 7, 4}
	for _, dev := range devices {
		cols := _getColumns(dev)
		for j, col := range cols {
			width := len(col)
			if width > widths[j] {
				widths[j] = width
			}
		}
	}

	_printColumns(widths, titles)

	for _, dev := range devices {
		_printColumns(widths, _getColumns(dev))
	}
}

func (l listCommand) printJSON(devs []api.Device) {
	for _, dev := range devs {
		out, err := json.Marshal(dev)
		if err != nil {
			logg.Fatal("JSON serialization failed: ", err)
		}
		fmt.Println(string(out))
	}
}

func _getColumns(dev api.Device) []string {
	return []string{dev.ID, dev.State, dev.IP, dev.Version, dev.Name}
}

// returns true if the device matches the given --with(out) flags
func _matches(dev api.Device, filters []string) (bool, error) {
	for _, f := range filters {
		if ok, err := property.Matches(dev, f); err != nil {
			return false, err
		} else if !ok {
			return false, nil
		}
	}

	return true, nil
}

func _printColumns(widths []int, cols []string) {
	if len(widths) != len(cols) {
		logg.Fatal("mismatch between cols and widths count", cols, widths)
	}

	for i, width := range widths {
		_printValue(width, cols[i])
		fmt.Print(" ")
	}
	fmt.Println("")
}

func _printValue(width int, val string) {
	if len(val) > width {
		logg.Fatal("width < len(val) !")
	}
	fmt.Print(val)
	for i := len(val); i < width; i++ {
		fmt.Print(" ")
	}
}

// ListCommand -- implements `ondevice list`
var ListCommand = listCommand{
	BaseCommand: BaseCommand{
		Arguments: "[--json] [--props] [--status=<online/offline>] [--print-ids] [filters...]",
		ShortHelp: "List your devices",
		RunFn:     nil, // we're implementing our own run() method
		LongHelp: `$ ondevice list [--options] [filters...]

List your devices

Options:
--json
  output JSON, one line/object per device
--props
  include properties (only affects JSON output)
--state <online/offline>
  limit to devices that are on/offline
--print-ids
	Print one devId per line (instead of tabular or JSON output)
	Can be used as input to

Filters:

With filters you can limit the output based on device properties.

Filters have the format:
  propertyName[operator[value]]
e.g.:
  somePackageVersion<2.3.4
  foo=

All comparisons are string-based (e.g. "234" is less than "99")
Missing properties are treated like the empty string ("arch=" lists devices with
empty or missing 'arch' property).

The following comparison operators are supported (comma-separated):
"=,==,!=,<,<<,<=,>,>>,>="

The one-character operators ("=,<,>") each have a two-character alias ("==,<<,>>"
respectively). These are provided to fix ambiguities (if the value to c start with "")
Note that you might have to escape '>' and '<' to shell redirection

Example:
  $ ondevice list
  ID            State   IP             Version         Name
  demo.7t91ta   offline                ondevice v0.4.3
  demo.fbqh2p   offline 192.168.1.23   ondevice v0.3.9
  demo.q5dkpm   online  127.0.0.1      ondevice v0.4.2
  demo.thm7br   offline 10.0.0.127     ondevice v0.4.3 My Raspberry PI

Example: Filters
  $ ondevice list 'fooVersion<2.3.4' 'foo=' --print-ids
	demo.7t91ta
  demo.fbqh2p
	demo.thm7br

	Only lists devices with the "fooVersion" property less than "2.3.4" (simple
	string comparison, so "2.3.4" < "2.34.5") and without the "foo" property
	(unset is equivalent to "")

Example: JSON output
  $ ondevice list --json --props
  {"id":"demo.7t91ta",state":"offline","stateTs":1490197318991,"version":"ondevice v0.4.3"}
  {"id":"demo.fbqh2p","ip":"192.168.1.23","state":"offline","stateTs":1485721709598,"version":"ondevice v0.3.9"}
  {"id":"demo.q5dkpm","ip":"127.0.0.1","state":"offline","stateTs":1487068641353,"version":"ondevice v0.4.2","props":{"test":"1234"}}
  {"id":"demo.thm7br","ip":"10.0.0.127","state":"offline", "": "My Raspberry PI","stateTs":1490963689912,"version":"ondevice v0.4.3"}

  Note that JSON fields may be missing or null

`,
	},
}
<|endoftext|>


<commit_before>package command

import (
	"bufio"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/hashicorp/vault/api"
	"github.com/hashicorp/vault/command/token"
	"github.com/mitchellh/cli"
)

// EnvVaultAddress can be used to set the address of Vault
const EnvVaultAddress = "VAULT_ADDR"

// FlagSetFlags is an enum to define what flags are present in the
// default FlagSet returned by Meta.FlagSet.
type FlagSetFlags uint

const (
	FlagSetNone    FlagSetFlags = 0
	FlagSetServer  FlagSetFlags = 1 << iota
	FlagSetDefault              = FlagSetServer
)

// Meta contains the meta-options and functionality that nearly every
// Vault command inherits.
type Meta struct {
	ClientToken string
	Ui          cli.Ui

	// The things below can be set, but aren't common
	ForceAddress string  // Address to force for API clients
	ForceConfig  *Config // Force a config, don't load from disk

	// These are set by the command line flags.
	flagAddress  string
	flagCACert   string
	flagCAPath   string
	flagInsecure bool

	// These are internal and shouldn't be modified or access by anyone
	// except Meta.
	config *Config
}

// Client returns the API client to a Vault server given the configured
// flag settings for this command.
func (m *Meta) Client() (*api.Client, error) {
	config := api.DefaultConfig()
	if v := os.Getenv(EnvVaultAddress); v != "" {
		config.Address = v
	}
	if m.flagAddress != "" {
		config.Address = m.flagAddress
	}
	if m.ForceAddress != "" {
		config.Address = m.ForceAddress
	}

	// If we need custom TLS configuration, then set it
	if m.flagCACert != "" || m.flagCAPath != "" || m.flagInsecure {
		var certPool *x509.CertPool
		var err error
		if m.flagCACert != "" {
			certPool, err = m.loadCACert(m.flagCACert)
		} else if m.flagCAPath != "" {
			certPool, err = m.loadCAPath(m.flagCAPath)
		}
		if err != nil {
			return nil, fmt.Errorf("Error setting up CA path: %s", err)
		}

		tlsConfig := &tls.Config{
			InsecureSkipVerify: m.flagInsecure,
			MinVersion:         tls.VersionTLS12,
			RootCAs:            certPool,
		}

		client := *http.DefaultClient
		client.Transport = &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			Dial: (&net.Dialer{
				Timeout:   30 * time.Second,
				KeepAlive: 30 * time.Second,
			}).Dial,
			TLSClientConfig:     tlsConfig,
			TLSHandshakeTimeout: 10 * time.Second,
		}

		config.HttpClient = &client
	}

	// Build the client
	client, err := api.NewClient(config)
	if err != nil {
		return nil, err
	}

	// If we have a token directly, then set that
	token := m.ClientToken

	// If we don't have a token, check the token helper
	if token == "" {
		// If we have a token, then set that
		tokenHelper, err := m.TokenHelper()
		if err != nil {
			return nil, err
		}
		token, err = tokenHelper.Get()
		if err != nil {
			return nil, err
		}
	}

	// Set the token
	if token != "" {
		client.SetToken(token)
	}

	return client, nil
}

// Config loads the configuration and returns it. If the configuration
// is already loaded, it is returned.
func (m *Meta) Config() (*Config, error) {
	if m.config != nil {
		return m.config, nil
	}
	if m.ForceConfig != nil {
		return m.ForceConfig, nil
	}

	var err error
	m.config, err = LoadConfig("")
	if err != nil {
		return nil, err
	}

	return m.config, nil
}

// FlagSet returns a FlagSet with the common flags that every
// command implements. The exact behavior of FlagSet can be configured
// using the flags as the second parameter, for example to disable
// server settings on the commands that don't talk to a server.
func (m *Meta) FlagSet(n string, fs FlagSetFlags) *flag.FlagSet {
	f := flag.NewFlagSet(n, flag.ContinueOnError)

	// FlagSetServer tells us to enable the settings for selecting
	// the server information.
	if fs&FlagSetServer != 0 {
		f.StringVar(&m.flagAddress, "address", "", "")
		f.StringVar(&m.flagCACert, "ca-cert", "", "")
		f.StringVar(&m.flagCAPath, "ca-path", "", "")
		f.BoolVar(&m.flagInsecure, "insecure", false, "")
	}

	// Create an io.Writer that writes to our Ui properly for errors.
	// This is kind of a hack, but it does the job. Basically: create
	// a pipe, use a scanner to break it into lines, and output each line
	// to the UI. Do this forever.
	errR, errW := io.Pipe()
	errScanner := bufio.NewScanner(errR)
	go func() {
		for errScanner.Scan() {
			m.Ui.Error(errScanner.Text())
		}
	}()
	f.SetOutput(errW)

	return f
}

// TokenHelper returns the token helper that is configured for Vault.
func (m *Meta) TokenHelper() (*token.Helper, error) {
	config, err := m.Config()
	if err != nil {
		return nil, err
	}

	path := config.TokenHelper
	if path == "" {
		path = "disk"
	}

	path = token.HelperPath(path)
	return &token.Helper{Path: path}, nil
}

func (m *Meta) loadCACert(path string) (*x509.CertPool, error) {
	certs, err := m.loadCertFromPEM(path)
	if err != nil {
		return nil, fmt.Errorf("Error loading %s: %s", path, err)
	}

	result := x509.NewCertPool()
	for _, cert := range certs {
		result.AddCert(cert)
	}

	return result, nil
}

func (m *Meta) loadCAPath(path string) (*x509.CertPool, error) {
	result := x509.NewCertPool()
	fn := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		certs, err := m.loadCertFromPEM(path)
		if err != nil {
			return fmt.Errorf("Error loading %s: %s", path, err)
		}

		for _, cert := range certs {
			result.AddCert(cert)
		}
		return nil
	}

	return result, filepath.Walk(path, fn)
}

func (m *Meta) loadCertFromPEM(path string) ([]*x509.Certificate, error) {
	pemCerts, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	certs := make([]*x509.Certificate, 0, 5)
	for len(pemCerts) > 0 {
		var block *pem.Block
		block, pemCerts = pem.Decode(pemCerts)
		if block == nil {
			break
		}
		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
			continue
		}

		cert, err := x509.ParseCertificate(block.Bytes)
		if err != nil {
			return nil, err
		}

		certs = append(certs, cert)
	}

	return certs, nil
}
<commit_msg>command/meta: don't read token file if token is already set [GH-162]<commit_after>package command

import (
	"bufio"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/hashicorp/vault/api"
	"github.com/hashicorp/vault/command/token"
	"github.com/mitchellh/cli"
)

// EnvVaultAddress can be used to set the address of Vault
const EnvVaultAddress = "VAULT_ADDR"

// FlagSetFlags is an enum to define what flags are present in the
// default FlagSet returned by Meta.FlagSet.
type FlagSetFlags uint

const (
	FlagSetNone    FlagSetFlags = 0
	FlagSetServer  FlagSetFlags = 1 << iota
	FlagSetDefault              = FlagSetServer
)

// Meta contains the meta-options and functionality that nearly every
// Vault command inherits.
type Meta struct {
	ClientToken string
	Ui          cli.Ui

	// The things below can be set, but aren't common
	ForceAddress string  // Address to force for API clients
	ForceConfig  *Config // Force a config, don't load from disk

	// These are set by the command line flags.
	flagAddress  string
	flagCACert   string
	flagCAPath   string
	flagInsecure bool

	// These are internal and shouldn't be modified or access by anyone
	// except Meta.
	config *Config
}

// Client returns the API client to a Vault server given the configured
// flag settings for this command.
func (m *Meta) Client() (*api.Client, error) {
	config := api.DefaultConfig()
	if v := os.Getenv(EnvVaultAddress); v != "" {
		config.Address = v
	}
	if m.flagAddress != "" {
		config.Address = m.flagAddress
	}
	if m.ForceAddress != "" {
		config.Address = m.ForceAddress
	}

	// If we need custom TLS configuration, then set it
	if m.flagCACert != "" || m.flagCAPath != "" || m.flagInsecure {
		var certPool *x509.CertPool
		var err error
		if m.flagCACert != "" {
			certPool, err = m.loadCACert(m.flagCACert)
		} else if m.flagCAPath != "" {
			certPool, err = m.loadCAPath(m.flagCAPath)
		}
		if err != nil {
			return nil, fmt.Errorf("Error setting up CA path: %s", err)
		}

		tlsConfig := &tls.Config{
			InsecureSkipVerify: m.flagInsecure,
			MinVersion:         tls.VersionTLS12,
			RootCAs:            certPool,
		}

		client := *http.DefaultClient
		client.Transport = &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			Dial: (&net.Dialer{
				Timeout:   30 * time.Second,
				KeepAlive: 30 * time.Second,
			}).Dial,
			TLSClientConfig:     tlsConfig,
			TLSHandshakeTimeout: 10 * time.Second,
		}

		config.HttpClient = &client
	}

	// Build the client
	client, err := api.NewClient(config)
	if err != nil {
		return nil, err
	}

	// If we have a token directly, then set that
	token := m.ClientToken

	// Try to set the token to what is already stored
	if token == "" {
		token = client.Token()
	}

	// If we don't have a token, check the token helper
	if token == "" {
		// If we have a token, then set that
		tokenHelper, err := m.TokenHelper()
		if err != nil {
			return nil, err
		}
		token, err = tokenHelper.Get()
		if err != nil {
			return nil, err
		}
	}

	// Set the token
	if token != "" {
		client.SetToken(token)
	}

	return client, nil
}

// Config loads the configuration and returns it. If the configuration
// is already loaded, it is returned.
func (m *Meta) Config() (*Config, error) {
	if m.config != nil {
		return m.config, nil
	}
	if m.ForceConfig != nil {
		return m.ForceConfig, nil
	}

	var err error
	m.config, err = LoadConfig("")
	if err != nil {
		return nil, err
	}

	return m.config, nil
}

// FlagSet returns a FlagSet with the common flags that every
// command implements. The exact behavior of FlagSet can be configured
// using the flags as the second parameter, for example to disable
// server settings on the commands that don't talk to a server.
func (m *Meta) FlagSet(n string, fs FlagSetFlags) *flag.FlagSet {
	f := flag.NewFlagSet(n, flag.ContinueOnError)

	// FlagSetServer tells us to enable the settings for selecting
	// the server information.
	if fs&FlagSetServer != 0 {
		f.StringVar(&m.flagAddress, "address", "", "")
		f.StringVar(&m.flagCACert, "ca-cert", "", "")
		f.StringVar(&m.flagCAPath, "ca-path", "", "")
		f.BoolVar(&m.flagInsecure, "insecure", false, "")
	}

	// Create an io.Writer that writes to our Ui properly for errors.
	// This is kind of a hack, but it does the job. Basically: create
	// a pipe, use a scanner to break it into lines, and output each line
	// to the UI. Do this forever.
	errR, errW := io.Pipe()
	errScanner := bufio.NewScanner(errR)
	go func() {
		for errScanner.Scan() {
			m.Ui.Error(errScanner.Text())
		}
	}()
	f.SetOutput(errW)

	return f
}

// TokenHelper returns the token helper that is configured for Vault.
func (m *Meta) TokenHelper() (*token.Helper, error) {
	config, err := m.Config()
	if err != nil {
		return nil, err
	}

	path := config.TokenHelper
	if path == "" {
		path = "disk"
	}

	path = token.HelperPath(path)
	return &token.Helper{Path: path}, nil
}

func (m *Meta) loadCACert(path string) (*x509.CertPool, error) {
	certs, err := m.loadCertFromPEM(path)
	if err != nil {
		return nil, fmt.Errorf("Error loading %s: %s", path, err)
	}

	result := x509.NewCertPool()
	for _, cert := range certs {
		result.AddCert(cert)
	}

	return result, nil
}

func (m *Meta) loadCAPath(path string) (*x509.CertPool, error) {
	result := x509.NewCertPool()
	fn := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		certs, err := m.loadCertFromPEM(path)
		if err != nil {
			return fmt.Errorf("Error loading %s: %s", path, err)
		}

		for _, cert := range certs {
			result.AddCert(cert)
		}
		return nil
	}

	return result, filepath.Walk(path, fn)
}

func (m *Meta) loadCertFromPEM(path string) ([]*x509.Certificate, error) {
	pemCerts, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	certs := make([]*x509.Certificate, 0, 5)
	for len(pemCerts) > 0 {
		var block *pem.Block
		block, pemCerts = pem.Decode(pemCerts)
		if block == nil {
			break
		}
		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
			continue
		}

		cert, err := x509.ParseCertificate(block.Bytes)
		if err != nil {
			return nil, err
		}

		certs = append(certs, cert)
	}

	return certs, nil
}
<|endoftext|>


<commit_before>package command

import (
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"regexp"
	"strconv"
	"strings"
)

const (
	XKCDLatestUrl = "http://xkcd.com/info.0.json"
	XKCDNumberUrl = "http://xkcd.com/%d/info.0.json"
)

var numRegexp = regexp.MustCompile(`(^\d+$)`)

type XKCDResult struct {
	Num        int    `json:"num"`
	Day        string `json:"day"`
	Month      string `json:"month"`
	Year       string `json:"year"`
	Img        string `json:"img"`
	Link       string `json:"link"`
	SafeTitle  string `json:"safe_title"`
	Title      string `json:"title"`
	Alt        string `json:"alt"`
	Transcript string `json:"transcript"`
	News       string `json:"news"`
}

type XKCDCommand struct {
	pattern *regexp.Regexp
}

func XKCD() XKCDCommand {
	return XKCDCommand{regexp.MustCompile(`(?i)xkcd\s*(.*)`)}
}

func (c XKCDCommand) Pattern() *regexp.Regexp {
	return c.pattern
}

func (c XKCDCommand) Run(query string) []string {
	q := strings.Trim(query, " ")
	current := loadComic(XKCDLatestUrl)
	comic := current

	if q == "random" {
		comic = loadComic(fmt.Sprintf(XKCDNumberUrl, rand.Intn(current.Num)))
	} else if match := numRegexp.FindStringSubmatch(q); len(match) > 1 {
		if number, err := strconv.Atoi(match[1]); err == nil {
			if number == 404 {
				return []string{"smart ass :)"}
			}

			comic = loadComic(fmt.Sprintf(XKCDNumberUrl, number))
		}
	}

	return []string{
		comic.Img,
		comic.Title,
		comic.Alt,
	}
}

func loadComic(url string) *XKCDResult {
	if body, err := NewHTTPClient(url).Get(); err == nil {
		var result XKCDResult
		json.Unmarshal(body, &result)

		return &result
	} else {
		log.Println("ERROR:", err)
	}
	return nil
}
<commit_msg>Optimize XKCD command.<commit_after>package command

import (
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"regexp"
	"strconv"
	"strings"
)

const (
	XKCDLatestUrl = "http://xkcd.com/info.0.json"
	XKCDNumberUrl = "http://xkcd.com/%d/info.0.json"
)

var numRegexp = regexp.MustCompile(`(^\d+$)`)

type XKCDResult struct {
	Num        int    `json:"num"`
	Day        string `json:"day"`
	Month      string `json:"month"`
	Year       string `json:"year"`
	Img        string `json:"img"`
	Link       string `json:"link"`
	SafeTitle  string `json:"safe_title"`
	Title      string `json:"title"`
	Alt        string `json:"alt"`
	Transcript string `json:"transcript"`
	News       string `json:"news"`
}

type XKCDCommand struct {
	pattern *regexp.Regexp
}

func XKCD() XKCDCommand {
	return XKCDCommand{regexp.MustCompile(`(?i)xkcd\s*(.*)`)}
}

func (c XKCDCommand) Pattern() *regexp.Regexp {
	return c.pattern
}

func (c XKCDCommand) Run(query string) []string {
	q := strings.Trim(query, " ")

	if match := numRegexp.FindStringSubmatch(q); len(match) > 1 {
		if number, err := strconv.Atoi(match[1]); err == nil {
			if number == 404 {
				return []string{"smart ass :)"}
			}

			return render(loadComic(fmt.Sprintf(XKCDNumberUrl, number)))
		}
	}

	current := loadComic(XKCDLatestUrl)
	comic := current

	if q == "random" {
		comic = loadComic(fmt.Sprintf(XKCDNumberUrl, rand.Intn(current.Num)))
	}

	return render(comic)
}

func loadComic(url string) *XKCDResult {
	if body, err := NewHTTPClient(url).Get(); err == nil {
		var result XKCDResult
		json.Unmarshal(body, &result)

		return &result
	} else {
		log.Println("ERROR:", err)
	}
	return nil
}

func render(comic *XKCDResult) []string {
	return []string{
		comic.Img,
		comic.Title,
		comic.Alt,
	}
}
<|endoftext|>


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

package dot

import (
	"fmt"

	"golang.org/x/tools/container/intsets"

	"gonum.org/v1/gonum/graph"
	"gonum.org/v1/gonum/graph/formats/dot"
	"gonum.org/v1/gonum/graph/formats/dot/ast"
)

// Builder is a graph that can have user-defined nodes and edges added.
type Builder interface {
	graph.Graph
	graph.Builder
	// NewNode adds a new node with a unique node ID to the graph.
	NewNode() graph.Node
	// NewEdge adds a new edge from the source to the destination node to the
	// graph, or returns the existing edge if already present.
	NewEdge(from, to graph.Node) graph.Edge
}

// UnmarshalerAttr is the interface implemented by objects that can unmarshal a
// DOT attribute description of themselves.
type UnmarshalerAttr interface {
	// UnmarshalDOTAttr decodes a single DOT attribute.
	UnmarshalDOTAttr(attr Attribute) error
}

// Unmarshal parses the Graphviz DOT-encoded data and stores the result in dst.
func Unmarshal(data []byte, dst Builder) error {
	file, err := dot.ParseBytes(data)
	if err != nil {
		return err
	}
	if len(file.Graphs) != 1 {
		return fmt.Errorf("invalid number of graphs; expected 1, got %d", len(file.Graphs))
	}
	return copyGraph(dst, file.Graphs[0])
}

// copyGraph copies the nodes and edges from the Graphviz AST source graph to
// the destination graph. Edge direction is maintained if present.
func copyGraph(dst Builder, src *ast.Graph) (err error) {
	defer func() {
		switch e := recover().(type) {
		case nil:
		case error:
			err = e
		default:
			panic(e)
		}
	}()
	gen := &generator{
		directed: src.Directed,
		ids:      make(map[string]graph.Node),
	}
	for _, stmt := range src.Stmts {
		gen.addStmt(dst, stmt)
	}
	return err
}

// A generator keeps track of the information required for generating a gonum
// graph from a dot AST graph.
type generator struct {
	// Directed graph.
	directed bool
	// Map from dot AST node ID to gonum node.
	ids map[string]graph.Node
	// Nodes processed within the context of a subgraph, that is to be used as a
	// vertex of an edge.
	subNodes []graph.Node
	// Stack of start indices into the subgraph node slice. The top element
	// corresponds to the start index of the active (or inner-most) subgraph.
	subStart []int
}

// node returns the gonum node corresponding to the given dot AST node ID,
// generating a new such node if none exist.
func (gen *generator) node(dst Builder, id string) graph.Node {
	if n, ok := gen.ids[id]; ok {
		return n
	}
	n := dst.NewNode()
	gen.ids[id] = n
	// Check if within the context of a subgraph, that is to be used as a vertex
	// of an edge.
	if gen.isInSubgraph() {
		// Append node processed within the context of a subgraph, that is to be
		// used as a vertex of an edge
		gen.appendSubgraphNode(n)
	}
	return n
}

// addStmt adds the given statement to the graph.
func (gen *generator) addStmt(dst Builder, stmt ast.Stmt) {
	switch stmt := stmt.(type) {
	case *ast.NodeStmt:
		n := gen.node(dst, stmt.Node.ID)
		if n, ok := n.(UnmarshalerAttr); ok {
			for _, attr := range stmt.Attrs {
				a := Attribute{
					Key:   attr.Key,
					Value: attr.Val,
				}
				if err := n.UnmarshalDOTAttr(a); err != nil {
					panic(fmt.Errorf("unable to unmarshal node DOT attribute (%s=%s)", a.Key, a.Value))
				}
			}
		}
	case *ast.EdgeStmt:
		gen.addEdgeStmt(dst, stmt)
	case *ast.AttrStmt:
		// ignore.
	case *ast.Attr:
		// ignore.
	case *ast.Subgraph:
		for _, stmt := range stmt.Stmts {
			gen.addStmt(dst, stmt)
		}
	default:
		panic(fmt.Sprintf("unknown statement type %T", stmt))
	}
}

// addEdgeStmt adds the given edge statement to the graph.
func (gen *generator) addEdgeStmt(dst Builder, e *ast.EdgeStmt) {
	fs := gen.addVertex(dst, e.From)
	ts := gen.addEdge(dst, e.To)
	for _, f := range fs {
		for _, t := range ts {
			edge := dst.NewEdge(f, t)
			if edge, ok := edge.(UnmarshalerAttr); ok {
				for _, attr := range e.Attrs {
					a := Attribute{
						Key:   attr.Key,
						Value: attr.Val,
					}
					if err := edge.UnmarshalDOTAttr(a); err != nil {
						panic(fmt.Errorf("unable to unmarshal edge DOT attribute (%s=%s)", a.Key, a.Value))
					}
				}
			}
		}
	}
}

// addVertex adds the given vertex to the graph, and returns its set of nodes.
func (gen *generator) addVertex(dst Builder, v ast.Vertex) []graph.Node {
	switch v := v.(type) {
	case *ast.Node:
		n := gen.node(dst, v.ID)
		return []graph.Node{n}
	case *ast.Subgraph:
		gen.pushSubgraph()
		for _, stmt := range v.Stmts {
			gen.addStmt(dst, stmt)
		}
		return gen.popSubgraph()
	default:
		panic(fmt.Sprintf("unknown vertex type %T", v))
	}
}

// addEdge adds the given edge to the graph, and returns its set of nodes.
func (gen *generator) addEdge(dst Builder, to *ast.Edge) []graph.Node {
	if !gen.directed && to.Directed {
		panic(fmt.Errorf("directed edge to %v in undirected graph", to.Vertex))
	}
	fs := gen.addVertex(dst, to.Vertex)
	if to.To != nil {
		ts := gen.addEdge(dst, to.To)
		for _, f := range fs {
			for _, t := range ts {
				dst.NewEdge(f, t)
			}
		}
	}
	return fs
}

// pushSubgraph pushes the node start index of the active subgraph onto the
// stack.
func (gen *generator) pushSubgraph() {
	gen.subStart = append(gen.subStart, len(gen.subNodes))
}

// popSubgraph pops the node start index of the active subgraph from the stack,
// and returns the nodes processed since.
func (gen *generator) popSubgraph() []graph.Node {
	// Get nodes processed since the subgraph became active.
	start := gen.subStart[len(gen.subStart)-1]
	// TODO: Figure out a better way to store subgraph nodes, so that duplicates
	// may not occur.
	nodes := unique(gen.subNodes[start:])
	// Remove subgraph from stack.
	gen.subStart = gen.subStart[:len(gen.subStart)-1]
	if len(gen.subStart) == 0 {
		// Remove subgraph nodes when the bottom-most subgraph has been processed.
		gen.subNodes = gen.subNodes[:0]
	}
	return nodes
}

// unique returns the set of unique nodes contained within ns.
func unique(ns []graph.Node) []graph.Node {
	var nodes []graph.Node
	var set intsets.Sparse
	for _, n := range ns {
		id := n.ID()
		if set.Has(id) {
			// skip duplicate node
			continue
		}
		set.Insert(id)
		nodes = append(nodes, n)
	}
	return nodes
}

// isInSubgraph reports whether the active context is within a subgraph, that is
// to be used as a vertex of an edge.
func (gen *generator) isInSubgraph() bool {
	return len(gen.subStart) > 0
}

// appendSubgraphNode appends the given node to the slice of nodes processed
// within the context of a subgraph.
func (gen *generator) appendSubgraphNode(n graph.Node) {
	gen.subNodes = append(gen.subNodes, n)
}
<commit_msg>graph/endcoding/dot: tidy up decode code/docs<commit_after>// Copyright ©2017 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package dot

import (
	"fmt"

	"golang.org/x/tools/container/intsets"

	"gonum.org/v1/gonum/graph"
	"gonum.org/v1/gonum/graph/formats/dot"
	"gonum.org/v1/gonum/graph/formats/dot/ast"
)

// Builder is a graph that can have user-defined nodes and edges added.
type Builder interface {
	graph.Graph
	graph.Builder
	// NewNode adds a new node with a unique node ID to the graph.
	NewNode() graph.Node
	// NewEdge adds a new edge from the source to the destination node to the
	// graph, or returns the existing edge if already present.
	NewEdge(from, to graph.Node) graph.Edge
}

// UnmarshalerAttr is implemented by types that can unmarshal a DOT
// attribute description of themselves.
type UnmarshalerAttr interface {
	// UnmarshalDOTAttr decodes a single DOT attribute.
	UnmarshalDOTAttr(attr Attribute) error
}

// Unmarshal parses the Graphviz DOT-encoded data and stores the result in dst.
func Unmarshal(data []byte, dst Builder) error {
	file, err := dot.ParseBytes(data)
	if err != nil {
		return err
	}
	if len(file.Graphs) != 1 {
		return fmt.Errorf("invalid number of graphs; expected 1, got %d", len(file.Graphs))
	}
	return copyGraph(dst, file.Graphs[0])
}

// copyGraph copies the nodes and edges from the Graphviz AST source graph to
// the destination graph. Edge direction is maintained if present.
func copyGraph(dst Builder, src *ast.Graph) (err error) {
	defer func() {
		switch e := recover().(type) {
		case nil:
		case error:
			err = e
		default:
			panic(e)
		}
	}()
	gen := &generator{
		directed: src.Directed,
		ids:      make(map[string]graph.Node),
	}
	for _, stmt := range src.Stmts {
		gen.addStmt(dst, stmt)
	}
	return err
}

// A generator keeps track of the information required for generating a gonum
// graph from a dot AST graph.
type generator struct {
	// Directed graph.
	directed bool
	// Map from dot AST node ID to gonum node.
	ids map[string]graph.Node
	// Nodes processed within the context of a subgraph, that is to be used as a
	// vertex of an edge.
	subNodes []graph.Node
	// Stack of start indices into the subgraph node slice. The top element
	// corresponds to the start index of the active (or inner-most) subgraph.
	subStart []int
}

// node returns the gonum node corresponding to the given dot AST node ID,
// generating a new such node if none exist.
func (gen *generator) node(dst Builder, id string) graph.Node {
	if n, ok := gen.ids[id]; ok {
		return n
	}
	n := dst.NewNode()
	gen.ids[id] = n
	// Check if within the context of a subgraph, that is to be used as a vertex
	// of an edge.
	if gen.isInSubgraph() {
		// Append node processed within the context of a subgraph, that is to be
		// used as a vertex of an edge
		gen.appendSubgraphNode(n)
	}
	return n
}

// addStmt adds the given statement to the graph.
func (gen *generator) addStmt(dst Builder, stmt ast.Stmt) {
	switch stmt := stmt.(type) {
	case *ast.NodeStmt:
		n, ok := gen.node(dst, stmt.Node.ID).(UnmarshalerAttr)
		if !ok {
			return
		}
		for _, attr := range stmt.Attrs {
			a := Attribute{
				Key:   attr.Key,
				Value: attr.Val,
			}
			if err := n.UnmarshalDOTAttr(a); err != nil {
				panic(fmt.Errorf("unable to unmarshal node DOT attribute (%s=%s)", a.Key, a.Value))
			}
		}
	case *ast.EdgeStmt:
		gen.addEdgeStmt(dst, stmt)
	case *ast.AttrStmt:
		// ignore.
	case *ast.Attr:
		// ignore.
	case *ast.Subgraph:
		for _, stmt := range stmt.Stmts {
			gen.addStmt(dst, stmt)
		}
	default:
		panic(fmt.Sprintf("unknown statement type %T", stmt))
	}
}

// addEdgeStmt adds the given edge statement to the graph.
func (gen *generator) addEdgeStmt(dst Builder, e *ast.EdgeStmt) {
	fs := gen.addVertex(dst, e.From)
	ts := gen.addEdge(dst, e.To)
	for _, f := range fs {
		for _, t := range ts {
			edge, ok := dst.NewEdge(f, t).(UnmarshalerAttr)
			if !ok {
				continue
			}
			for _, attr := range e.Attrs {
				a := Attribute{
					Key:   attr.Key,
					Value: attr.Val,
				}
				if err := edge.UnmarshalDOTAttr(a); err != nil {
					panic(fmt.Errorf("unable to unmarshal edge DOT attribute (%s=%s)", a.Key, a.Value))
				}
			}
		}
	}
}

// addVertex adds the given vertex to the graph, and returns its set of nodes.
func (gen *generator) addVertex(dst Builder, v ast.Vertex) []graph.Node {
	switch v := v.(type) {
	case *ast.Node:
		n := gen.node(dst, v.ID)
		return []graph.Node{n}
	case *ast.Subgraph:
		gen.pushSubgraph()
		for _, stmt := range v.Stmts {
			gen.addStmt(dst, stmt)
		}
		return gen.popSubgraph()
	default:
		panic(fmt.Sprintf("unknown vertex type %T", v))
	}
}

// addEdge adds the given edge to the graph, and returns its set of nodes.
func (gen *generator) addEdge(dst Builder, to *ast.Edge) []graph.Node {
	if !gen.directed && to.Directed {
		panic(fmt.Errorf("directed edge to %v in undirected graph", to.Vertex))
	}
	fs := gen.addVertex(dst, to.Vertex)
	if to.To != nil {
		ts := gen.addEdge(dst, to.To)
		for _, f := range fs {
			for _, t := range ts {
				dst.NewEdge(f, t)
			}
		}
	}
	return fs
}

// pushSubgraph pushes the node start index of the active subgraph onto the
// stack.
func (gen *generator) pushSubgraph() {
	gen.subStart = append(gen.subStart, len(gen.subNodes))
}

// popSubgraph pops the node start index of the active subgraph from the stack,
// and returns the nodes processed since.
func (gen *generator) popSubgraph() []graph.Node {
	// Get nodes processed since the subgraph became active.
	start := gen.subStart[len(gen.subStart)-1]
	// TODO: Figure out a better way to store subgraph nodes, so that duplicates
	// may not occur.
	nodes := unique(gen.subNodes[start:])
	// Remove subgraph from stack.
	gen.subStart = gen.subStart[:len(gen.subStart)-1]
	if len(gen.subStart) == 0 {
		// Remove subgraph nodes when the bottom-most subgraph has been processed.
		gen.subNodes = gen.subNodes[:0]
	}
	return nodes
}

// unique returns the set of unique nodes contained within ns.
func unique(ns []graph.Node) []graph.Node {
	var nodes []graph.Node
	var set intsets.Sparse
	for _, n := range ns {
		id := n.ID()
		if set.Has(id) {
			// skip duplicate node
			continue
		}
		set.Insert(id)
		nodes = append(nodes, n)
	}
	return nodes
}

// isInSubgraph reports whether the active context is within a subgraph, that is
// to be used as a vertex of an edge.
func (gen *generator) isInSubgraph() bool {
	return len(gen.subStart) > 0
}

// appendSubgraphNode appends the given node to the slice of nodes processed
// within the context of a subgraph.
func (gen *generator) appendSubgraphNode(n graph.Node) {
	gen.subNodes = append(gen.subNodes, n)
}
<|endoftext|>


<commit_before>package commands

import "github.com/concourse/fly/rc"

type FlyCommand struct {
	Help HelpCommand `command:"help" description:"Print this help message"`

	Target  rc.TargetName  `short:"t" long:"target" description:"Concourse target name"`
	Targets TargetsCommand `command:"targets" alias:"ts" description:"List saved targets"`

	Version func() `short:"v" long:"version" description:"Print the version of Fly and exit"`

	Login  LoginCommand  `command:"login" alias:"l" description:"Authenticate with the target"`
	Logout LogoutCommand `command:"logout" alias:"lo" description:"Release authentication with the target"`
	Sync   SyncCommand   `command:"sync"  alias:"s" description:"Download and replace the current fly from the target"`

	SetTeam SetTeamCommand `command:"set-team"  alias:"st" description:"Create or modify a team to have the given credentials"`

	Checklist ChecklistCommand `command:"checklist" alias:"cl" description:"Print a Checkfile of the given pipeline"`

	Execute ExecuteCommand `command:"execute" alias:"e" description:"Execute a one-off build using local bits"`
	Watch   WatchCommand   `command:"watch"   alias:"w" description:"Stream a build's output"`

	Containers ContainersCommand `command:"containers" alias:"cs" description:"Print the active containers"`
	Hijack     HijackCommand     `command:"hijack"     alias:"intercept" alias:"i" description:"Execute a command in a container"`

	PauseJob   PauseJobCommand   `command:"pause-job" alias:"pj" description:"Pause a job"`
	UnpauseJob UnpauseJobCommand `command:"unpause-job" alias:"uj" description:"Unpause a job"`

	Pipelines       PipelinesCommand       `command:"pipelines"        alias:"ps" description:"List the configured pipelines"`
	DestroyPipeline DestroyPipelineCommand `command:"destroy-pipeline" alias:"dp" description:"Destroy a pipeline"`
	GetPipeline     GetPipelineCommand     `command:"get-pipeline"     alias:"gp" description:"Get a pipeline's current configuration"`
	SetPipeline     SetPipelineCommand     `command:"set-pipeline"     alias:"sp" description:"Create or update a pipeline's configuration"`
	PausePipeline   PausePipelineCommand   `command:"pause-pipeline"   alias:"pp" description:"Pause a pipeline"`
	UnpausePipeline UnpausePipelineCommand `command:"unpause-pipeline" alias:"up" description:"Un-pause a pipeline"`
	ExposePipeline  ExposePipelineCommand  `command:"expose-pipeline"  alias:"ep" description:"Make a pipeline publicly viewable"`
	HidePipeline    HidePipelineCommand    `command:"hide-pipeline"    alias:"hp" description:"Hide a pipeline from the public"`
	RenamePipeline  RenamePipelineCommand  `command:"rename-pipeline"  alias:"rp" description:"Rename a pipeline"`

	CheckResource   CheckResourceCommand   `command:"check-resource"    alias:"cr" description:"Check a resource"`
	PauseResource   PauseResourceCommand   `command:"pause-resource"    alias:"pr" description:"Pause a resource"`
	UnpauseResource UnpauseResourceCommand `command:"unpause-resource"  alias:"ur" description:"Unpause a resource"`

	Builds     BuildsCommand     `command:"builds"      alias:"bs" description:"List builds data"`
	AbortBuild AbortBuildCommand `command:"abort-build" alias:"ab" description:"Abort a build"`

	TriggerJob TriggerJobCommand `command:"trigger-job" alias:"tj" description:"Start a job in a pipeline"`

	Volumes VolumesCommand `command:"volumes" alias:"vs" description:"List the active volumes"`
	Workers WorkersCommand `command:"workers" alias:"ws" description:"List the registered workers"`
}

var Fly FlyCommand
<commit_msg>updated logout alias<commit_after>package commands

import "github.com/concourse/fly/rc"

type FlyCommand struct {
	Help HelpCommand `command:"help" description:"Print this help message"`

	Target  rc.TargetName  `short:"t" long:"target" description:"Concourse target name"`
	Targets TargetsCommand `command:"targets" alias:"ts" description:"List saved targets"`

	Version func() `short:"v" long:"version" description:"Print the version of Fly and exit"`

	Login  LoginCommand  `command:"login" alias:"l" description:"Authenticate with the target"`
	Logout LogoutCommand `command:"logout" alias:"o" description:"Release authentication with the target"`
	Sync   SyncCommand   `command:"sync"  alias:"s" description:"Download and replace the current fly from the target"`

	SetTeam SetTeamCommand `command:"set-team"  alias:"st" description:"Create or modify a team to have the given credentials"`

	Checklist ChecklistCommand `command:"checklist" alias:"cl" description:"Print a Checkfile of the given pipeline"`

	Execute ExecuteCommand `command:"execute" alias:"e" description:"Execute a one-off build using local bits"`
	Watch   WatchCommand   `command:"watch"   alias:"w" description:"Stream a build's output"`

	Containers ContainersCommand `command:"containers" alias:"cs" description:"Print the active containers"`
	Hijack     HijackCommand     `command:"hijack"     alias:"intercept" alias:"i" description:"Execute a command in a container"`

	PauseJob   PauseJobCommand   `command:"pause-job" alias:"pj" description:"Pause a job"`
	UnpauseJob UnpauseJobCommand `command:"unpause-job" alias:"uj" description:"Unpause a job"`

	Pipelines       PipelinesCommand       `command:"pipelines"        alias:"ps" description:"List the configured pipelines"`
	DestroyPipeline DestroyPipelineCommand `command:"destroy-pipeline" alias:"dp" description:"Destroy a pipeline"`
	GetPipeline     GetPipelineCommand     `command:"get-pipeline"     alias:"gp" description:"Get a pipeline's current configuration"`
	SetPipeline     SetPipelineCommand     `command:"set-pipeline"     alias:"sp" description:"Create or update a pipeline's configuration"`
	PausePipeline   PausePipelineCommand   `command:"pause-pipeline"   alias:"pp" description:"Pause a pipeline"`
	UnpausePipeline UnpausePipelineCommand `command:"unpause-pipeline" alias:"up" description:"Un-pause a pipeline"`
	ExposePipeline  ExposePipelineCommand  `command:"expose-pipeline"  alias:"ep" description:"Make a pipeline publicly viewable"`
	HidePipeline    HidePipelineCommand    `command:"hide-pipeline"    alias:"hp" description:"Hide a pipeline from the public"`
	RenamePipeline  RenamePipelineCommand  `command:"rename-pipeline"  alias:"rp" description:"Rename a pipeline"`

	CheckResource   CheckResourceCommand   `command:"check-resource"    alias:"cr" description:"Check a resource"`
	PauseResource   PauseResourceCommand   `command:"pause-resource"    alias:"pr" description:"Pause a resource"`
	UnpauseResource UnpauseResourceCommand `command:"unpause-resource"  alias:"ur" description:"Unpause a resource"`

	Builds     BuildsCommand     `command:"builds"      alias:"bs" description:"List builds data"`
	AbortBuild AbortBuildCommand `command:"abort-build" alias:"ab" description:"Abort a build"`

	TriggerJob TriggerJobCommand `command:"trigger-job" alias:"tj" description:"Start a job in a pipeline"`

	Volumes VolumesCommand `command:"volumes" alias:"vs" description:"List the active volumes"`
	Workers WorkersCommand `command:"workers" alias:"ws" description:"List the registered workers"`
}

var Fly FlyCommand
<|endoftext|>


<commit_before>package meep_test

import (
	"os"
	"runtime"
	"testing"

	"."
	"./fixtures"
)

var cwd, _ = os.Getwd()

var use14fnnames bool

func init() {
	gover := runtime.Version()
	// I have truely minimal desire to parse this "well".
	// If it's not recognized, we'll assume it's new.
	if gover[0:4] != "go1." {
		return
	}
	switch gover[5] {
	case '0', '1', '2', '3', '4':
		use14fnnames = true
	}
}

func TestStacksStraightforward(t *testing.T) {
	var result meep.Stack
	fn := func() {
		result = *(meep.CaptureStack())
	}
	fixtures.WheeOne(fn)
	expect := []struct {
		n   int
		str string
	}{
		{0, cwd + "/stackinfo_test.go:16: meep_test.func·001"},                  // right here, where we call `CaptureStack`
		{1, cwd + "/fixtures/stack1.go:9: fixtures.wheeTwo"},                    // should be in the body of the func
		{2, cwd + "/fixtures/stack1.go:5: fixtures.WheeOne"},                    // should be in the body of the func
		{3, cwd + "/stackinfo_test.go:18: meep_test.TestStacksStraightforward"}, // right here, where we call `fixtures.*`
		// No need to get overly precise about line numbers in the stdlib:
		//{4, "/usr/local/go/src/testing/testing.go:447: testing.tRunner"},
		//{5, "/usr/local/go/src/runtime/asm_amd64.s:2232: runtime.goexit"},
	}
	expectMax := len(expect) + 2
	for _, tr := range expect {
		str := result.Frames[tr.n].String()
		if str != tr.str {
			t.Errorf("Stack[%d] should be %q, was %q", tr.n, tr.str, str)
		}
	}
	for i, fr := range result.Frames {
		if i < expectMax {
			continue
		}
		t.Errorf("Stack[%d] was expected to be empty, was %q", i, fr.String())
	}
}

func TestStacksPlusDeferral(t *testing.T) {
	var result meep.Stack
	fn := func() {
		result = *(meep.CaptureStack())
	}
	fixtures.WheeTree(fn)
	expect := []struct {
		n   int
		str string
	}{
		// note the total lack of 'wheeTwo'; it's called, but already returned before the defer path is hit, so of course it's absent here.
		{0, cwd + "/stackinfo_test.go:49: meep_test.func·002"},               // right here, where we call `CaptureStack`
		{1, cwd + "/fixtures/stack1.go:19: fixtures.wheedee"},                // should be in the body of the func (natch, the declare location -- the defer location never shows up; that's not a new func)
		{2, cwd + "/fixtures/stack1.go:16: fixtures.WheeTree"},               // golang considers 'defer' to run on the last line of the parent func.  even if that's "}\n".
		{3, cwd + "/stackinfo_test.go:51: meep_test.TestStacksPlusDeferral"}, // right here, where we call `fixtures.*`
		// No need to get overly precise about line numbers in the stdlib:
		//{4, "/usr/local/go/src/testing/testing.go:447: testing.tRunner"},
		//{5, "/usr/local/go/src/runtime/asm_amd64.s:2232: runtime.goexit"},
	}
	expectMax := len(expect) + 2
	for _, tr := range expect {
		str := result.Frames[tr.n].String()
		if str != tr.str {
			t.Errorf("Stack[%d] should be %q, was %q", tr.n, tr.str, str)
		}
	}
	for i, fr := range result.Frames {
		if i < expectMax {
			continue
		}
		t.Errorf("Stack[%d] was expected to be empty, was %q", i, fr.String())
	}
}

func TestStacksPanickingInDefersOhMy(t *testing.T) {
	var result meep.Stack
	fixtures.BeesBuzz(func() {
		result = *(meep.CaptureStack())
	})
	expect := []struct {
		n      int
		file   string
		line   int
		func14 string
		func15 string
	}{
		// note the total lack of reference to where "recover" is called.  (That happened after the stack capture... not that that really matters;
		//   if you flip the recover before the BeesBuzz defer'd func's call to our thunk, this thing on line 9 just moves to 10, that's it -- there's no other flow change.)
		{0, cwd + "/stackinfo_test.go", 99, "meep_test.func·003", "meep_test.TestStacksPanickingInDefersOhMy.func1"}, // right here, where we call `CaptureStack` in our thunk
		{1, cwd + "/fixtures/stack2.go", 9, "fixtures.func·002", "fixtures.BeesBuzz.func1"},                          // the line in the deferred function that called our thunk
		// No need to get overly precise about line numbers in the stdlib",
		//{2, "/usr/local/go/src/runtime/asm_amd64.s", 401, "", "runtime.call16"}, // if this isn't a single line on some platforms... uff.
		//{3, "/usr/local/go/src/runtime/panic.go", 387, "", "runtime.gopanic"},  // it might be reasonable to detect these and elide everything following from `runtime.*`.
		{4, cwd + "/fixtures/stack2.go", 22, "", "fixtures.buzzkill"},                         // the line that panicked!
		{5, cwd + "/fixtures/stack2.go", 19, "", "fixtures.beesWuz"},                          // the trailing `}` of `beesWuz`, because we left it via defer
		{6, cwd + "/fixtures/stack2.go", 14, "", "fixtures.BeesBuzz"},                         // the body line the calls down to `beesWuz`
		{7, cwd + "/stackinfo_test.go", 100, "", "meep_test.TestStacksPanickingInDefersOhMy"}, // obtw!  when we split the `fixtures.*()` *invocation* across lines, this becomes the last one!
		// No need to get overly precise about line numbers in the stdlib",
		//{8, "/usr/local/go/src/testing/testing.go", 447, "", "testing.tRunner"},
		//{9, "/usr/local/go/src/runtime/asm_amd64.s", 2232, "", "runtime.goexit"},
	}
	// If no exceptions were specified, the old funcname is the same as the new
	for _, ex := range expect {
		if ex.func14 == "" {
			ex.func14 = ex.func15
		}
	}

	expectMax := len(expect) + 4
	for _, tr := range expect {
		file, line, fnname := result.Frames[tr.n].Where()
		if file != tr.file {
			t.Errorf("Stack[%d] file should be %q, was %q", tr.n, tr.file, file)
		}
		if line != tr.line {
			t.Errorf("Stack[%d] line should be %d, was %d", tr.n, tr.line, line)
		}
		expectedFnname := tr.func15
		if use14fnnames {
			expectedFnname = tr.func14
		}
		if fnname != expectedFnname {
			t.Errorf("Stack[%d] func name should be %q, was %q", tr.n, expectedFnname, fnname)
		}
	}
	for i, fr := range result.Frames {
		if i < expectMax {
			continue
		}
		t.Errorf("Stack[%d] was expected to be empty, was %q", i, fr.String())
	}
}
<commit_msg>Convert the rest of stack tests and update all those line numbers.  Should be good now.<commit_after>package meep_test

import (
	"os"
	"runtime"
	"testing"

	"."
	"./fixtures"
)

var cwd, _ = os.Getwd()

var use14fnnames bool

func init() {
	gover := runtime.Version()
	// I have truely minimal desire to parse this "well".
	// If it's not recognized, we'll assume it's new.
	if gover[0:4] != "go1." {
		return
	}
	switch gover[5] {
	case '0', '1', '2', '3', '4':
		use14fnnames = true
	}
}

type stackFrameExpectation struct {
	n      int
	file   string
	line   int
	func14 string
	func15 string
}

func TestStacksStraightforward(t *testing.T) {
	var result meep.Stack
	fn := func() {
		result = *(meep.CaptureStack())
	}
	fixtures.WheeOne(fn)
	expects := []stackFrameExpectation{
		{0, cwd + "/stackinfo_test.go", 40, "meep_test.func·001", "meep_test.TestStacksStraightforward.func1"}, // right here, where we call `CaptureStack`
		{1, cwd + "/fixtures/stack1.go", 9, "", "fixtures.wheeTwo"},                                            // should be in the body of the func
		{2, cwd + "/fixtures/stack1.go", 5, "", "fixtures.WheeOne"},                                            // should be in the body of the func
		{3, cwd + "/stackinfo_test.go", 42, "", "meep_test.TestStacksStraightforward"},                         // right here, where we call `fixtures.*`
		// No need to get overly precise about line numbers in the stdlib:
		//{4, "/usr/local/go/src/testing/testing.go", 447, "", "testing.tRunner"},
		//{5, "/usr/local/go/src/runtime/asm_amd64.s", 2232, "", "runtime.goexit"},
	}
	expectLen := len(expects) + 2
	assertStack(t, result, expects, expectLen)
}

func TestStacksPlusDeferral(t *testing.T) {
	var result meep.Stack
	fn := func() {
		result = *(meep.CaptureStack())
	}
	fixtures.WheeTree(fn)
	expects := []stackFrameExpectation{
		// note the total lack of 'wheeTwo'; it's called, but already returned before the defer path is hit, so of course it's absent here.
		{0, cwd + "/stackinfo_test.go", 59, "meep_test.func·002", "meep_test.TestStacksPlusDeferral.func1"}, // right here, where we call `CaptureStack`
		{1, cwd + "/fixtures/stack1.go", 19, "", "fixtures.wheedee"},                                        // should be in the body of the func (natch, the declare location -- the defer location never shows up; that's not a new func)
		{2, cwd + "/fixtures/stack1.go", 16, "", "fixtures.WheeTree"},                                       // golang considers 'defer' to run on the last line of the parent func.  even if that's "}\n".
		{3, cwd + "/stackinfo_test.go", 61, "", "meep_test.TestStacksPlusDeferral"},                         // right here, where we call `fixtures.*`
		// No need to get overly precise about line numbers in the stdlib:
		//{4, "/usr/local/go/src/testing/testing.go", 447, "", "testing.tRunner"},
		//{5, "/usr/local/go/src/runtime/asm_amd64.s", 2232, "", "runtime.goexit"},
	}
	expectLen := len(expects) + 2
	assertStack(t, result, expects, expectLen)
}

func TestStacksPanickingInDefersOhMy(t *testing.T) {
	var result meep.Stack
	fixtures.BeesBuzz(func() {
		result = *(meep.CaptureStack())
	})
	expects := []stackFrameExpectation{
		// note the total lack of reference to where "recover" is called.  (That happened after the stack capture... not that that really matters;
		//   if you flip the recover before the BeesBuzz defer'd func's call to our thunk, this thing on line 9 just moves to 10, that's it -- there's no other flow change.)
		{0, cwd + "/stackinfo_test.go", 79, "meep_test.func·003", "meep_test.TestStacksPanickingInDefersOhMy.func1"}, // right here, where we call `CaptureStack` in our thunk
		{1, cwd + "/fixtures/stack2.go", 9, "fixtures.func·002", "fixtures.BeesBuzz.func1"},                          // the line in the deferred function that called our thunk
		// No need to get overly precise about line numbers in the stdlib:
		//{2, "/usr/local/go/src/runtime/asm_amd64.s", 401, "", "runtime.call16"}, // if this isn't a single line on some platforms... uff.
		//{3, "/usr/local/go/src/runtime/panic.go", 387, "", "runtime.gopanic"},  // it might be reasonable to detect these and elide everything following from `runtime.*`.
		{4, cwd + "/fixtures/stack2.go", 22, "", "fixtures.buzzkill"},                        // the line that panicked!
		{5, cwd + "/fixtures/stack2.go", 19, "", "fixtures.beesWuz"},                         // the trailing `}` of `beesWuz`, because we left it via defer
		{6, cwd + "/fixtures/stack2.go", 14, "", "fixtures.BeesBuzz"},                        // the body line the calls down to `beesWuz`
		{7, cwd + "/stackinfo_test.go", 80, "", "meep_test.TestStacksPanickingInDefersOhMy"}, // obtw!  when we split the `fixtures.*()` *invocation* across lines, this becomes the last one!
		// No need to get overly precise about line numbers in the stdlib",
		//{8, "/usr/local/go/src/testing/testing.go", 447, "", "testing.tRunner"},
		//{9, "/usr/local/go/src/runtime/asm_amd64.s", 2232, "", "runtime.goexit"},
	}
	expectLen := len(expects) + 4
	assertStack(t, result, expects, expectLen)
}

func assertStack(t *testing.T, result meep.Stack, expects []stackFrameExpectation, expectLen int) {
	// Some quick cleanup on the expectations:
	// If no exceptions were specified, the old 1.4 funcname is expected to be same as the new
	for _, ex := range expects {
		if ex.func14 == "" {
			ex.func14 = ex.func15
		}
	}

	// Assertions!
	for _, tr := range expects {
		file, line, fnname := result.Frames[tr.n].Where()
		if file != tr.file {
			t.Errorf("Stack[%d] file should be %q, was %q", tr.n, tr.file, file)
		}
		if line != tr.line {
			t.Errorf("Stack[%d] line should be %d, was %d", tr.n, tr.line, line)
		}
		expectedFnname := tr.func15
		if use14fnnames {
			expectedFnname = tr.func14
		}
		if fnname != expectedFnname {
			t.Errorf("Stack[%d] func name should be %q, was %q", tr.n, expectedFnname, fnname)
		}
	}
	for i, fr := range result.Frames {
		if i < expectLen {
			continue
		}
		t.Errorf("Stack[%d] was expected to be empty, was %q", i, fr.String())
	}
}
<|endoftext|>


<commit_before>package dna

import (
	"github.com/craiglowe/gonomics/common"
)

type Base byte

const (
	A   Base = 0
	C   Base = 1
	G   Base = 2
	T   Base = 3
	N   Base = 4
	a   Base = 5
	c   Base = 6
	g   Base = 7
	t   Base = 8
	n   Base = 9
	Gap Base = 10
)

func ToUpper(b Base) Base {
	if b == Gap {
		return b
	} else if b > N {
		return b - 5
	} else {
		return b
	}
}

func ToLower(b Base) Base {
	if b == Gap {
		return b
	} else if b < a {
		return b + 5
	} else {
		return b
	}
}

// start is closed, end is open, both are zero-based
func RangeToUpper(bases []Base, start int, end int) {
	for i := start; i < end; i++ {
		bases[i] = ToUpper(bases[i])
	}
}

// start is closed, end is open, both are zero-based
func RangeToLower(bases []Base, start int, end int) {
	for i := start; i < end; i++ {
		bases[i] = ToLower(bases[i])
	}
}

func AllToUpper(bases []Base) {
	RangeToUpper(bases, 0, len(bases))
}

func AllToLower(bases []Base) {
	RangeToLower(bases, 0, len(bases))
}

func compareBases(alpha Base, beta Base, ignoreCase bool) int {
	if ignoreCase {
		alpha = ToUpper(alpha)
		beta = ToUpper(beta)
	}
	if alpha < beta {
		return -1
	} else if alpha > beta {
		return 1
	} else {
		return 0
	}
}

func compareSeqs(alpha []Base, beta []Base, ignoreCase bool) int {
	var res int
	stop := common.Min(len(alpha), len(beta))
	for i := 0; i < stop; i++ {
		res = compareBases(alpha[i], beta[i], ignoreCase)
		if res != 0 {
			return res
		}
	}
	if len(alpha) < len(beta) {
		return -1
	} else if len(alpha) > len(beta) {
		return 1
	} else {
		return 0
	}
}

func CompareSeqsIgnoreCase(alpha []Base, beta []Base) int {
	return compareSeqs(alpha, beta, true)
}

func CompareSeqsCaseSensitive(alpha []Base, beta []Base) int {
	return compareSeqs(alpha, beta, false)
}
<commit_msg>added TODO to make lowercase bases visible outside package<commit_after>package dna

import (
	"github.com/craiglowe/gonomics/common"
)

type Base byte

// TODO: change these names so that all variables can be seen
// from outside.  Maybe BaseA, Basea, etc
const (
	A   Base = 0
	C   Base = 1
	G   Base = 2
	T   Base = 3
	N   Base = 4
	a   Base = 5
	c   Base = 6
	g   Base = 7
	t   Base = 8
	n   Base = 9
	Gap Base = 10
)

func ToUpper(b Base) Base {
	if b == Gap {
		return b
	} else if b > N {
		return b - 5
	} else {
		return b
	}
}

func ToLower(b Base) Base {
	if b == Gap {
		return b
	} else if b < a {
		return b + 5
	} else {
		return b
	}
}

// start is closed, end is open, both are zero-based
func RangeToUpper(bases []Base, start int, end int) {
	for i := start; i < end; i++ {
		bases[i] = ToUpper(bases[i])
	}
}

// start is closed, end is open, both are zero-based
func RangeToLower(bases []Base, start int, end int) {
	for i := start; i < end; i++ {
		bases[i] = ToLower(bases[i])
	}
}

func AllToUpper(bases []Base) {
	RangeToUpper(bases, 0, len(bases))
}

func AllToLower(bases []Base) {
	RangeToLower(bases, 0, len(bases))
}

func compareBases(alpha Base, beta Base, ignoreCase bool) int {
	if ignoreCase {
		alpha = ToUpper(alpha)
		beta = ToUpper(beta)
	}
	if alpha < beta {
		return -1
	} else if alpha > beta {
		return 1
	} else {
		return 0
	}
}

func compareSeqs(alpha []Base, beta []Base, ignoreCase bool) int {
	var res int
	stop := common.Min(len(alpha), len(beta))
	for i := 0; i < stop; i++ {
		res = compareBases(alpha[i], beta[i], ignoreCase)
		if res != 0 {
			return res
		}
	}
	if len(alpha) < len(beta) {
		return -1
	} else if len(alpha) > len(beta) {
		return 1
	} else {
		return 0
	}
}

func CompareSeqsIgnoreCase(alpha []Base, beta []Base) int {
	return compareSeqs(alpha, beta, true)
}

func CompareSeqsCaseSensitive(alpha []Base, beta []Base) int {
	return compareSeqs(alpha, beta, false)
}
<|endoftext|>


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

package state

import (
	"github.com/juju/errors"
	"github.com/juju/names"

	"github.com/juju/juju/workload"
)

// TODO(ericsnow) Track juju-level status in the status collection.

type EnvPayloads interface {
	// ListAll builds the list of registered payloads in the env and returns it.
	ListAll() ([]workload.Payload, error)
}

// TODO(ericsnow) Use a more generic component registration mechanism?

type newEnvPayloadsFunc func(Persistence, func() ([]string, error), func(string) ([]names.UnitTag, error)) (EnvPayloads, error)

var (
	newEnvPayloads newEnvPayloadsFunc
)

// SetPayloadComponent registers the functions that provide the state
// functionality related to payloads.
func SetPayloadsComponent(upFunc newEnvPayloadsFunc) {
	newEnvPayloads = upFunc
}

// EnvPayloads exposes interaction with payloads in state.
func (st *State) EnvPayloads() (EnvPayloads, error) {
	if newEnvPayloads == nil {
		return nil, errors.Errorf("payloads not supported")
	}

	persist := st.newPersistence()
	envPayloads, err := newEnvPayloads(persist, st.allMachineNames, st.unitTagsFor)
	if err != nil {
		return nil, errors.Trace(err)
	}

	return envPayloads, nil
}

func (st *State) allMachineNames() ([]string, error) {
	ms, err := st.AllMachines()
	if err != nil {
		return nil, errors.Trace(err)
	}
	var names []string
	for _, m := range ms {
		names = append(names, m.Id())
	}
	return names, nil
}

func (st *State) unitTagsFor(machine string) ([]names.UnitTag, error) {
	us, err := st.UnitsFor(machine)
	if err != nil {
		return nil, errors.Trace(err)
	}
	var tags []names.UnitTag
	for _, u := range us {
		tags = append(tags, u.UnitTag())
	}
	return tags, nil
}
<commit_msg>Name some return args.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package state

import (
	"github.com/juju/errors"
	"github.com/juju/names"

	"github.com/juju/juju/workload"
)

// TODO(ericsnow) Track juju-level status in the status collection.

type EnvPayloads interface {
	// ListAll builds the list of registered payloads in the env and returns it.
	ListAll() ([]workload.Payload, error)
}

// TODO(ericsnow) Use a more generic component registration mechanism?

type newEnvPayloadsFunc func(_ Persistence, machineNames func() ([]string, error), machineUnits func(string) ([]names.UnitTag, error)) (EnvPayloads, error)

var (
	newEnvPayloads newEnvPayloadsFunc
)

// SetPayloadComponent registers the functions that provide the state
// functionality related to payloads.
func SetPayloadsComponent(upFunc newEnvPayloadsFunc) {
	newEnvPayloads = upFunc
}

// EnvPayloads exposes interaction with payloads in state.
func (st *State) EnvPayloads() (EnvPayloads, error) {
	if newEnvPayloads == nil {
		return nil, errors.Errorf("payloads not supported")
	}

	persist := st.newPersistence()
	envPayloads, err := newEnvPayloads(persist, st.allMachineNames, st.unitTagsFor)
	if err != nil {
		return nil, errors.Trace(err)
	}

	return envPayloads, nil
}

func (st *State) allMachineNames() ([]string, error) {
	ms, err := st.AllMachines()
	if err != nil {
		return nil, errors.Trace(err)
	}
	var names []string
	for _, m := range ms {
		names = append(names, m.Id())
	}
	return names, nil
}

func (st *State) unitTagsFor(machine string) ([]names.UnitTag, error) {
	us, err := st.UnitsFor(machine)
	if err != nil {
		return nil, errors.Trace(err)
	}
	var tags []names.UnitTag
	for _, u := range us {
		tags = append(tags, u.UnitTag())
	}
	return tags, nil
}
<|endoftext|>


<commit_before>package dns

import (
	"fmt"
	"strings"
)

// Record containing information regarding DNS record
type Record struct {
	Dnszone  string
	Name     string
	Type     string
	Value    string
	TTL      float64
	ID       string
	NewValue string
	NewTTL   float64
}

// ReadRecords returns all DNS records matching query
func (c *Client) ReadRecords(rec Record) ([]Record, error) {
	// powershell script template to read record from DNS
	const tmplpscript = `
Get-DnsServerResourceRecord -ZoneName {{.Dnszone}}{{ if .Name }} -Name {{.Name}}{{end}} | ?{($_.RecordType -eq 'A' -or $_.RecordType -eq 'CNAME') -and $_.HostName -eq '{{ .Name }}'} | select DistinguishedName, HostName, RecordData, RecordType, TimeToLive | ConvertTo-Json
`

	pscript, err := tmplExec(rec, tmplpscript)
	if err != nil {
		return []Record{}, fmt.Errorf("Creating template: %v", err)
	}
	output, err := c.ExecutePowerShellScript(pscript)
	if err != nil {
		return []Record{}, fmt.Errorf("Running PowerShell script: %v", err)
	}
	if output.stdout == "" {
		return []Record{}, fmt.Errorf("No Record found: %v", rec.Name)
	}
	output.stdout = makeResponseArray(output.stdout)
	resp, err := unmarshalResponse(output.stdout)
	if err != nil {
		return []Record{}, fmt.Errorf("Unmarshalling response: %v", err)
	}
	return *convertResponse(resp, rec), nil
}

// ReadRecord performs DNS Record lookup from server
func (c *Client) ReadRecord(rec Record) (Record, error) {
	// powershell script template to read record from DNS
	const tmplpscript = `
Get-DnsServerResourceRecord -ZoneName {{.Dnszone}}{{ if .Name }} -Name {{.Name}}{{end}} | ?{($_.RecordType -eq 'A' -or $_.RecordType -eq 'CNAME') -and $_.HostName -eq '{{ .Name }}'} | select DistinguishedName, HostName, RecordData, RecordType, TimeToLive | ConvertTo-Json
`

	pscript, err := tmplExec(rec, tmplpscript)
	if err != nil {
		return Record{}, fmt.Errorf("Creating template: %v", err)
	}
	output, err := c.ExecutePowerShellScript(pscript)
	if err != nil {
		return Record{}, fmt.Errorf("Running PowerShell script: %v", err)
	}
	if output.stdout == "" {
		return Record{}, fmt.Errorf("No Record found: %v", rec.Name)
	}
	output.stdout = makeResponseArray(output.stdout)

	resp, err := unmarshalResponse(output.stdout)
	if err != nil {
		return Record{}, fmt.Errorf("Unmarshalling response: %v", err)
	}
	for _, v := range *convertResponse(resp, rec) {
		if v.Value == rec.Value {
			return v, nil
		}
	}
	return Record{}, fmt.Errorf("Record not found: %s", rec.Name)
}

// ReadRecordfromID retrieves specifc DNS record based on record ID
func (c *Client) ReadRecordfromID(recID string) (Record, error) {
	id := strings.Split(recID, "|")
	if len(id) != 3 {
		return Record{}, fmt.Errorf("ID is incorrect")
	}
	rec := Record{
		Dnszone: id[0],
		Name:    id[1],
		Value:   id[2],
	}
	result, err := c.ReadRecords(rec)
	if err != nil {
		return Record{}, fmt.Errorf("Reading record: %v", err)
	}
	for i, v := range result {
		if v.ID == recID {
			return result[i], nil
		}
	}
	return Record{}, fmt.Errorf("Record not found: %v", recID)
}

// CreateRecord creates new DNS records on server
func (c *Client) CreateRecord(rec Record) ([]Record, error) {
	const tmplscriptA = `
Add-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Name {{ .Name }} -A -IPv4Address {{ .Value }} -TimeToLive (New-TimeSpan -Seconds {{ .TTL }})
`
	const tmplscriptCname = `
Add-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Name {{ .Name }} -CName -HostNameAlias {{ .Value }} -TimeToLive (New-TimeSpan -Seconds {{ .TTL }})
`
	var (
		pscript string
		err     error
	)

	if c.RecordExist(rec) {
		return []Record{}, fmt.Errorf("Record already exists: %v", rec)
	}
	rec.ID = fmt.Sprintf("%s|%s|%s", rec.Dnszone, rec.Name, rec.Value)
	switch rec.Type {
	case "A":
		pscript, err = tmplExec(rec, tmplscriptA)
		if err != nil {
			return []Record{}, fmt.Errorf("Creating template: %v", err)
		}
	case "CNAME":
		pscript, err = tmplExec(rec, tmplscriptCname)
		if err != nil {
			return []Record{}, fmt.Errorf("Creating template: %v", err)
		}
	}
	_, err = c.ExecutePowerShellScript(pscript)
	if err != nil {
		return []Record{}, fmt.Errorf("Executing PowerShell script: %v", err)
	}
	record, err := c.ReadRecordfromID(rec.ID)
	if err != nil {
		return []Record{}, fmt.Errorf("Reading record: %v", err)
	}

	var result []Record
	result = append(result, record)

	return result, nil
}

// DeleteRecord deletes DNS record specified
func (c *Client) DeleteRecord(rec Record) error {
	const tmplscriptA string = `
(Get-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Name {{ .Name }}) | ?{$_.HostName -eq '{{ .Name }}' -and $_.RecordData.IPv4Address -match '{{ .Value }}'} | Remove-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Force
`
	const tmplscriptCname string = `
(Get-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Name {{ .Name }}) | ?{$_.HostName -eq '{{ .Name }}' -and $_.RecordData.HostNameAlias -match '{{ .Value }}'} | Remove-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Force
`
	var (
		pscript string
		err     error
	)

	if !c.RecordExist(rec) {
		return fmt.Errorf("Record not found: %v", rec)
	}

	switch rec.Type {
	case "A":
		pscript, err = tmplExec(rec, tmplscriptA)
		if err != nil {
			return fmt.Errorf("Creating template: %v", err)
		}
	case "CNAME":
		pscript, err = tmplExec(rec, tmplscriptCname)
		if err != nil {
			return fmt.Errorf("Creating template: %v", err)
		}
	}

	_, err = c.ExecutePowerShellScript(pscript)
	if err != nil {
		return fmt.Errorf("Executing PowerShell script: %v", err)
	}

	return nil
}

// UpdateRecord updates an existing DNS record
func (c *Client) UpdateRecord(rec Record, newValue string, newTTL float64) (Record, error) {
	const tmplscriptA string = `
$old = Get-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Name {{ .Name }} | ?{$_.HostName -eq '{{ .Name }}' -and $_.RecordData.IPv4Address -eq '{{ .Value }}'}
$new = Get-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Name {{ .Name }} | ?{$_.HostName -eq '{{ .Name }}' -and $_.RecordData.IPv4Address -eq '{{ .Value }}'}
{{ if .NewValue -}}
$new.RecordData.IPv4Address = [System.Net.IPAddress]::Parse('{{ .NewValue }}')
{{ end -}}
{{ if ne .NewTTL 0.0 -}}
$new.TimeToLive = New-Timespan -Seconds {{ .NewTTL }}
{{ end -}}
Set-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -NewInputObject $new -OldInputObject $old
`
	const tmplscriptCname string = `
$old = Get-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Name {{ .Name }} | ?{$_.HostName -eq '{{ .Name }}' -and $_.RecordData.HostNameAlias -eq '{{ .Value }}'}
$new = Get-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Name {{ .Name }} | ?{$_.HostName -eq '{{ .Name }}' -and $_.RecordData.HostNameAlias -eq '{{ .Value }}'}
{{ if .NewValue -}}
$new.RecordData.HostNameAlias = '{{ .NewValue }}'
{{ end -}}
{{ if ne .NewTTL 0 -}}
$new.TimeToLive = New-Timespan -Seconds {{ .NewTTL }}
{{ end -}}
Set-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -NewInputObject $new -OldInputObject $old
`
	var (
		pscript string
		err     error
	)

	if !c.RecordExist(rec) {
		return Record{}, fmt.Errorf("Record not found: %v", rec.Name)
	}
	rec, err = c.ReadRecord(rec)
	if err != nil {
		return Record{}, fmt.Errorf("Reading record: %v", err)
	}
	rec.NewValue = newValue
	rec.NewTTL = newTTL
	switch rec.Type {
	case "A":
		pscript, err = tmplExec(rec, tmplscriptA)
		if err != nil {
			return Record{}, fmt.Errorf("Createing template: %v", err)
		}
	case "CNAME":
		pscript, err = tmplExec(rec, tmplscriptCname)
		if err != nil {
			return Record{}, fmt.Errorf("Createing template: %v", err)
		}
	}

	_, err = c.ExecutePowerShellScript(pscript)
	if err != nil {
		return Record{}, fmt.Errorf("Excuting PowerShell script: %v", err)
	}
	if rec.NewValue != "" {
		rec.Value = rec.NewValue
	}

	rec, err = c.ReadRecord(rec)
	if err != nil {
		return Record{}, fmt.Errorf("Reading updated record: %v", err)
	}

	return rec, nil
}

// RecordExist returns if record exists or not
func (c *Client) RecordExist(rec Record) bool {
	records, _ := c.ReadRecords(rec)
	if len(records) > 0 {
		for _, v := range records {
			if v.Value == rec.Value {
				return true
			}
		}
	}
	return false
}
<commit_msg>Fixing type in update script for CNAME<commit_after>package dns

import (
	"fmt"
	"strings"
)

// Record containing information regarding DNS record
type Record struct {
	Dnszone  string
	Name     string
	Type     string
	Value    string
	TTL      float64
	ID       string
	NewValue string
	NewTTL   float64
}

// ReadRecords returns all DNS records matching query
func (c *Client) ReadRecords(rec Record) ([]Record, error) {
	// powershell script template to read record from DNS
	const tmplpscript = `
Get-DnsServerResourceRecord -ZoneName {{.Dnszone}}{{ if .Name }} -Name {{.Name}}{{end}} | ?{($_.RecordType -eq 'A' -or $_.RecordType -eq 'CNAME') -and $_.HostName -eq '{{ .Name }}'} | select DistinguishedName, HostName, RecordData, RecordType, TimeToLive | ConvertTo-Json
`

	pscript, err := tmplExec(rec, tmplpscript)
	if err != nil {
		return []Record{}, fmt.Errorf("Creating template: %v", err)
	}
	output, err := c.ExecutePowerShellScript(pscript)
	if err != nil {
		return []Record{}, fmt.Errorf("Running PowerShell script: %v", err)
	}
	if output.stdout == "" {
		return []Record{}, fmt.Errorf("No Record found: %v", rec.Name)
	}
	output.stdout = makeResponseArray(output.stdout)
	resp, err := unmarshalResponse(output.stdout)
	if err != nil {
		return []Record{}, fmt.Errorf("Unmarshalling response: %v", err)
	}
	return *convertResponse(resp, rec), nil
}

// ReadRecord performs DNS Record lookup from server
func (c *Client) ReadRecord(rec Record) (Record, error) {
	// powershell script template to read record from DNS
	const tmplpscript = `
Get-DnsServerResourceRecord -ZoneName {{.Dnszone}}{{ if .Name }} -Name {{.Name}}{{end}} | ?{($_.RecordType -eq 'A' -or $_.RecordType -eq 'CNAME') -and $_.HostName -eq '{{ .Name }}'} | select DistinguishedName, HostName, RecordData, RecordType, TimeToLive | ConvertTo-Json
`

	pscript, err := tmplExec(rec, tmplpscript)
	if err != nil {
		return Record{}, fmt.Errorf("Creating template: %v", err)
	}
	output, err := c.ExecutePowerShellScript(pscript)
	if err != nil {
		return Record{}, fmt.Errorf("Running PowerShell script: %v", err)
	}
	if output.stdout == "" {
		return Record{}, fmt.Errorf("No Record found: %v", rec.Name)
	}
	output.stdout = makeResponseArray(output.stdout)

	resp, err := unmarshalResponse(output.stdout)
	if err != nil {
		return Record{}, fmt.Errorf("Unmarshalling response: %v", err)
	}
	for _, v := range *convertResponse(resp, rec) {
		if v.Value == rec.Value {
			return v, nil
		}
	}
	return Record{}, fmt.Errorf("Record not found: %s", rec.Name)
}

// ReadRecordfromID retrieves specifc DNS record based on record ID
func (c *Client) ReadRecordfromID(recID string) (Record, error) {
	id := strings.Split(recID, "|")
	if len(id) != 3 {
		return Record{}, fmt.Errorf("ID is incorrect")
	}
	rec := Record{
		Dnszone: id[0],
		Name:    id[1],
		Value:   id[2],
	}
	result, err := c.ReadRecords(rec)
	if err != nil {
		return Record{}, fmt.Errorf("Reading record: %v", err)
	}
	for i, v := range result {
		if v.ID == recID {
			return result[i], nil
		}
	}
	return Record{}, fmt.Errorf("Record not found: %v", recID)
}

// CreateRecord creates new DNS records on server
func (c *Client) CreateRecord(rec Record) ([]Record, error) {
	const tmplscriptA = `
Add-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Name {{ .Name }} -A -IPv4Address {{ .Value }} -TimeToLive (New-TimeSpan -Seconds {{ .TTL }})
`
	const tmplscriptCname = `
Add-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Name {{ .Name }} -CName -HostNameAlias {{ .Value }} -TimeToLive (New-TimeSpan -Seconds {{ .TTL }})
`
	var (
		pscript string
		err     error
	)

	if c.RecordExist(rec) {
		return []Record{}, fmt.Errorf("Record already exists: %v", rec)
	}
	rec.ID = fmt.Sprintf("%s|%s|%s", rec.Dnszone, rec.Name, rec.Value)
	switch rec.Type {
	case "A":
		pscript, err = tmplExec(rec, tmplscriptA)
		if err != nil {
			return []Record{}, fmt.Errorf("Creating template: %v", err)
		}
	case "CNAME":
		pscript, err = tmplExec(rec, tmplscriptCname)
		if err != nil {
			return []Record{}, fmt.Errorf("Creating template: %v", err)
		}
	}
	_, err = c.ExecutePowerShellScript(pscript)
	if err != nil {
		return []Record{}, fmt.Errorf("Executing PowerShell script: %v", err)
	}
	record, err := c.ReadRecordfromID(rec.ID)
	if err != nil {
		return []Record{}, fmt.Errorf("Reading record: %v", err)
	}

	var result []Record
	result = append(result, record)

	return result, nil
}

// DeleteRecord deletes DNS record specified
func (c *Client) DeleteRecord(rec Record) error {
	const tmplscriptA string = `
(Get-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Name {{ .Name }}) | ?{$_.HostName -eq '{{ .Name }}' -and $_.RecordData.IPv4Address -match '{{ .Value }}'} | Remove-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Force
`
	const tmplscriptCname string = `
(Get-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Name {{ .Name }}) | ?{$_.HostName -eq '{{ .Name }}' -and $_.RecordData.HostNameAlias -match '{{ .Value }}'} | Remove-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Force
`
	var (
		pscript string
		err     error
	)

	if !c.RecordExist(rec) {
		return fmt.Errorf("Record not found: %v", rec)
	}

	switch rec.Type {
	case "A":
		pscript, err = tmplExec(rec, tmplscriptA)
		if err != nil {
			return fmt.Errorf("Creating template: %v", err)
		}
	case "CNAME":
		pscript, err = tmplExec(rec, tmplscriptCname)
		if err != nil {
			return fmt.Errorf("Creating template: %v", err)
		}
	}

	_, err = c.ExecutePowerShellScript(pscript)
	if err != nil {
		return fmt.Errorf("Executing PowerShell script: %v", err)
	}

	return nil
}

// UpdateRecord updates an existing DNS record
func (c *Client) UpdateRecord(rec Record, newValue string, newTTL float64) (Record, error) {
	const tmplscriptA string = `
$old = Get-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Name {{ .Name }} | ?{$_.HostName -eq '{{ .Name }}' -and $_.RecordData.IPv4Address -eq '{{ .Value }}'}
$new = Get-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Name {{ .Name }} | ?{$_.HostName -eq '{{ .Name }}' -and $_.RecordData.IPv4Address -eq '{{ .Value }}'}
{{ if .NewValue -}}
$new.RecordData.IPv4Address = [System.Net.IPAddress]::Parse('{{ .NewValue }}')
{{ end -}}
{{ if ne .NewTTL 0.0 -}}
$new.TimeToLive = New-Timespan -Seconds {{ .NewTTL }}
{{ end -}}
Set-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -NewInputObject $new -OldInputObject $old
`
	const tmplscriptCname string = `
$old = Get-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Name {{ .Name }} | ?{$_.HostName -eq '{{ .Name }}' -and $_.RecordData.HostNameAlias -eq '{{ .Value }}'}
$new = Get-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -Name {{ .Name }} | ?{$_.HostName -eq '{{ .Name }}' -and $_.RecordData.HostNameAlias -eq '{{ .Value }}'}
{{ if .NewValue -}}
$new.RecordData.HostNameAlias = '{{ .NewValue }}'
{{ end -}}
{{ if ne .NewTTL 0.0 -}}
$new.TimeToLive = New-Timespan -Seconds {{ .NewTTL }}
{{ end -}}
Set-DnsServerResourceRecord -ZoneName {{ .Dnszone }} -NewInputObject $new -OldInputObject $old
`
	var (
		pscript string
		err     error
	)

	if !c.RecordExist(rec) {
		return Record{}, fmt.Errorf("Record not found: %v", rec.Name)
	}
	rec, err = c.ReadRecord(rec)
	if err != nil {
		return Record{}, fmt.Errorf("Reading record: %v", err)
	}
	rec.NewValue = newValue
	rec.NewTTL = newTTL
	switch rec.Type {
	case "A":
		pscript, err = tmplExec(rec, tmplscriptA)
		if err != nil {
			return Record{}, fmt.Errorf("Createing template: %v", err)
		}
	case "CNAME":
		pscript, err = tmplExec(rec, tmplscriptCname)
		if err != nil {
			return Record{}, fmt.Errorf("Createing template: %v", err)
		}
	}

	_, err = c.ExecutePowerShellScript(pscript)
	if err != nil {
		return Record{}, fmt.Errorf("Excuting PowerShell script: %v", err)
	}
	if rec.NewValue != "" {
		rec.Value = rec.NewValue
	}

	rec, err = c.ReadRecord(rec)
	if err != nil {
		return Record{}, fmt.Errorf("Reading updated record: %v", err)
	}

	return rec, nil
}

// RecordExist returns if record exists or not
func (c *Client) RecordExist(rec Record) bool {
	var records []Record

	if rec.ID != "" {
		resp, err := c.ReadRecordfromID(rec.ID)
		if err != nil {
			return false
		}
		records = append(records, resp)
	} else {
		records, _ = c.ReadRecords(rec)
	}

	if len(records) > 0 {
		for _, v := range records {
			if v.Value == rec.Value {
				return true
			}
		}
	}
	return false
}
<|endoftext|>