<commit_before>package jason

import (
	"encoding/json"
	"errors"
	"io"
)

// Value represents an arbitrary JSON value.
// It may contain a bool, number, string, object, array or null.
type Value struct {
	data   interface{}
	exists bool // Used to separate nil and non-existing values
}

// Object represents an object JSON value.
// The underlying golang map can be accessed with Map().

type Object struct {
	Value
	m     map[string]*Value // The formatted map with typed values
	valid bool
}

// Returns the golang map.
// Needed when iterating through the values of the object.
func (j *Object) Map() map[string]*Value {
	return j.m
}

// Create a new Value from a io.reader.
// Useful for parsing the body of a net/http response.
// Returns an error if something went wrong.
// Example: NewFromReader(res.Body)
func NewValueFromReader(reader io.Reader) (*Value, error) {
	j := new(Value)
	d := json.NewDecoder(reader)
	err := d.Decode(&j.data)
	return j, err
}

// Create a new Value from bytes
// Returns an error if the bytes couldn't be parsed.
func NewValueFromBytes(b []byte) (*Value, error) {
	j := new(Value)
	err := json.Unmarshal(b, &j.data)
	return j, err
}

// Create a new Value from a string
// Returns an error if the string couldn't be parsed.
func NewValueFromString(s string) (*Value, error) {
	b := []byte(s)
	return NewValueFromBytes(b)
}

// Marshal into bytes
func (j *Value) Marshal() ([]byte, error) {
	return json.Marshal(j.data)
}

// Private Get
func (j *Value) get(key string) (*Value, error) {

	// Assume this is an object
	obj := j.object()

	// Only continue if it really is an object
	if obj.valid {
		child, ok := obj.Map()[key]
		if ok {
			return child, nil
		}
	}

	return nil, errors.New("could not get")

}

// Private get path
func (j *Value) getPath(keys []string) (*Value, error) {
	current := j
	var err error
	for _, key := range keys {
		current, err = current.get(key)

		if err != nil {
			return nil, err
		}
	}
	return current, nil
}

// Get Value at key path. Returns a Value or error.
// Example: Get("address", "street")
func (j *Value) Get(keys ...string) (*Value, error) {
	return j.getPath(keys)
}

// Get Object at key path. Returns an Object, or error.
func (v *Value) GetObject(keys ...string) (*Object, error) {
	child, err := v.getPath(keys)

	if err != nil {
		return nil, err
	} else {

		obj, err := child.AsObject()

		if err != nil {
			return nil, err
		} else {
			return obj, nil
		}

	}

	return nil, nil
}

// Get string at key path. Returns a string primitive, or error.
func (v *Value) GetString(keys ...string) (string, error) {
	child, err := v.getPath(keys)

	if err != nil {
		return "", err
	} else {

		return child.AsString()

	}

	return "", nil
}

// Get number at key path. Returns a float64, or error.
func (v *Value) GetNumber(keys ...string) (float64, error) {
	child, err := v.getPath(keys)

	if err != nil {
		return 0, err
	} else {

		n, err := child.AsNumber()

		if err != nil {
			return 0, err
		} else {
			return n, nil
		}

	}

	return 0, nil
}

// Get number at key path. Returns a bool, or error.
func (v *Value) GetBoolean(keys ...string) (bool, error) {
	child, err := v.getPath(keys)

	if err != nil {
		return false, err
	}

	return child.AsBoolean()
}

// Get array at key path. Returns an array of Values, or error.
func (v *Value) GetArray(keys ...string) ([]*Value, error) {
	child, err := v.getPath(keys)

	if err != nil {
		return nil, err
	} else {

		return child.AsArray()

	}

	return nil, nil
}

// Returns an error if the value is not actually null
func (j *Value) AsNull() error {
	var valid bool

	// Check the type of this data
	switch j.data.(type) {
	case nil:
		valid = j.exists // Valid only if j also exists, since other values could possibly also be nil
		break
	}

	if valid {
		return nil
	}

	return errors.New("is not null")

}

// Returns the current Value as an array of Values.
// Returns an error if the current Value is not an Array.
func (j *Value) AsArray() ([]*Value, error) {
	var valid bool

	// Check the type of this data
	switch j.data.(type) {
	case []interface{}:
		valid = true
		break
	}

	// Unsure if this is a good way to use slices, it's probably not
	var slice []*Value

	if valid {

		for _, element := range j.data.([]interface{}) {
			child := Value{element, true}
			slice = append(slice, &child)
		}

		return slice, nil
	}

	return slice, errors.New("Not an array")

}

// Returns the current Value as a float64.
// Returns an error if the current Value is not a json number.
func (j *Value) AsNumber() (float64, error) {
	var valid bool

	// Check the type of this data
	switch j.data.(type) {
	case float64:
		valid = true
		break
	}

	if valid {
		return j.data.(float64), nil
	}

	return 0, errors.New("not a number")
}

// Returns the current Value as a bool.
// Returns an error if the current Value is not a json boolean.
func (j *Value) AsBoolean() (bool, error) {
	var valid bool

	// Check the type of this data
	switch j.data.(type) {
	case bool:
		valid = true
		break
	}

	if valid {
		return j.data.(bool), nil
	}

	return false, errors.New("no bool")
}

// Private object
func (j *Value) object() *Object {

	var valid bool

	// Check the type of this data
	switch j.data.(type) {
	case map[string]interface{}:
		valid = true
		break
	}

	obj := new(Object)
	obj.valid = valid

	m := make(map[string]*Value)

	if valid {
		//obj.Map = j.data.(map[string]interface{})

		for key, element := range j.data.(map[string]interface{}) {
			m[key] = &Value{element, true}

		}
	}

	obj.data = j.data
	obj.m = m

	return obj
}

// Returns the current data as objects with string keys and Value values.
// Returns an error if the current Value is not a json object
func (j *Value) AsObject() (*Object, error) {
	obj := j.object()

	var err error

	if !obj.valid {
		err = errors.New("Is not an object")
	}

	return obj, err
}

// Returns the current data as a golang string
// Returns an error if the current Value is not a json string
func (j *Value) AsString() (string, error) {
	var valid bool

	// Check the type of this data
	switch j.data.(type) {
	case string:
		valid = true
		break
	}

	if valid {
		return j.data.(string), nil
	}

	return "", errors.New("not a string")
}

// The method named String() is used by golang's log method for logging
// It returns the Value as object
func (j *Value) String() string {
	f, err := json.Marshal(j.data)
	if err != nil {
		return err.Error()
	} else {
		return string(f)
	}
}
<commit_msg>Update documentation<commit_after>package jason

import (
	"encoding/json"
	"errors"
	"io"
)

// Value represents an arbitrary JSON value.
// It may contain a bool, number, string, object, array or null.
type Value struct {
	data   interface{}
	exists bool // Used to separate nil and non-existing values
}

// Object represents an object JSON object.
// It inherets from Value but with an additional method to access
// a map representation of it's content. It's useful when iterating.
type Object struct {
	Value
	m     map[string]*Value
	valid bool
}

// Returns the golang map.
// Needed when iterating through the values of the object.
func (j *Object) Map() map[string]*Value {
	return j.m
}

// Creates a new value from an io.reader.
// Returns an error if the reader does not contain valid json.
// Useful for parsing the body of a net/http response.
// Example: NewFromReader(res.Body)
func NewValueFromReader(reader io.Reader) (*Value, error) {
	j := new(Value)
	d := json.NewDecoder(reader)
	err := d.Decode(&j.data)
	return j, err
}

// Creates a new value from bytes.
// Returns an error if the bytes are not valid json.
func NewValueFromBytes(b []byte) (*Value, error) {
	j := new(Value)
	err := json.Unmarshal(b, &j.data)
	return j, err
}

// Creates a new value from a string.
// Returns an error if the string is not valid json.
func NewValueFromString(s string) (*Value, error) {
	b := []byte(s)
	return NewValueFromBytes(b)
}

// Marshal into bytes.
func (j *Value) Marshal() ([]byte, error) {
	return json.Marshal(j.data)
}

// Private Get
func (j *Value) get(key string) (*Value, error) {

	// Assume this is an object
	obj := j.object()

	// Only continue if it really is an object
	if obj.valid {
		child, ok := obj.Map()[key]
		if ok {
			return child, nil
		}
	}

	return nil, errors.New("could not get")

}

// Private get path
func (j *Value) getPath(keys []string) (*Value, error) {
	current := j
	var err error
	for _, key := range keys {
		current, err = current.get(key)

		if err != nil {
			return nil, err
		}
	}
	return current, nil
}

// Gets the value at key path.
// Returns error if the value does not exist.
// Example: Get("address", "street")
func (j *Value) Get(keys ...string) (*Value, error) {
	return j.getPath(keys)
}

// Gets the value at key path and attempts to typecast the value into an object.
// Returns error if the value is not a json object.
func (v *Value) GetObject(keys ...string) (*Object, error) {
	child, err := v.getPath(keys)

	if err != nil {
		return nil, err
	} else {

		obj, err := child.AsObject()

		if err != nil {
			return nil, err
		} else {
			return obj, nil
		}

	}

	return nil, nil
}

// Gets the value at key path and attempts to typecast the value into a string.
// Returns error if the value is not a json string.
func (v *Value) GetString(keys ...string) (string, error) {
	child, err := v.getPath(keys)

	if err != nil {
		return "", err
	} else {
		return child.AsString()
	}

	return "", nil
}

// Gets the value at key path and attempts to typecast the value into null.
// Returns error if the value is not json null.
func (v *Value) GetNull(keys ...string) error {
	child, err := v.getPath(keys)

	if err != nil {
		return err
	}

	return child.AsNull()
}

// Gets the value at key path and attempts to typecast the value into a float64.
// Returns error if the value is not a json number.
func (v *Value) GetNumber(keys ...string) (float64, error) {
	child, err := v.getPath(keys)

	if err != nil {
		return 0, err
	} else {

		n, err := child.AsNumber()

		if err != nil {
			return 0, err
		} else {
			return n, nil
		}
	}

	return 0, nil
}

// Gets the value at key path and attempts to typecast the value into a bool.
// Returns error if the value is not a json boolean.
func (v *Value) GetBoolean(keys ...string) (bool, error) {
	child, err := v.getPath(keys)

	if err != nil {
		return false, err
	}

	return child.AsBoolean()
}

// Gets the value at key path and attempts to typecast the value into an array.
// Returns error if the value is not a json array.
func (v *Value) GetArray(keys ...string) ([]*Value, error) {
	child, err := v.getPath(keys)

	if err != nil {
		return nil, err
	} else {

		return child.AsArray()

	}

	return nil, nil
}

// Returns an error if the value is not actually null
func (j *Value) AsNull() error {
	var valid bool

	// Check the type of this data
	switch j.data.(type) {
	case nil:
		valid = j.exists // Valid only if j also exists, since other values could possibly also be nil
		break
	}

	if valid {
		return nil
	}

	return errors.New("is not null")

}

// Attempts to typecast the current value into an array.
// Returns error if the current value is not a json array.
func (j *Value) AsArray() ([]*Value, error) {
	var valid bool

	// Check the type of this data
	switch j.data.(type) {
	case []interface{}:
		valid = true
		break
	}

	// Unsure if this is a good way to use slices, it's probably not
	var slice []*Value

	if valid {

		for _, element := range j.data.([]interface{}) {
			child := Value{element, true}
			slice = append(slice, &child)
		}

		return slice, nil
	}

	return slice, errors.New("Not an array")

}

// Attempts to typecast the current value into a float64.
// Returns error if the current value is not a json number.
func (j *Value) AsNumber() (float64, error) {
	var valid bool

	// Check the type of this data
	switch j.data.(type) {
	case float64:
		valid = true
		break
	}

	if valid {
		return j.data.(float64), nil
	}

	return 0, errors.New("not a number")
}

// Attempts to typecast the current value into a bool.
// Returns error if the current value is not a json boolean.
func (j *Value) AsBoolean() (bool, error) {
	var valid bool

	// Check the type of this data
	switch j.data.(type) {
	case bool:
		valid = true
		break
	}

	if valid {
		return j.data.(bool), nil
	}

	return false, errors.New("no bool")
}

// Private object
func (j *Value) object() *Object {

	var valid bool

	// Check the type of this data
	switch j.data.(type) {
	case map[string]interface{}:
		valid = true
		break
	}

	obj := new(Object)
	obj.valid = valid

	m := make(map[string]*Value)

	if valid {
		//obj.Map = j.data.(map[string]interface{})

		for key, element := range j.data.(map[string]interface{}) {
			m[key] = &Value{element, true}

		}
	}

	obj.data = j.data
	obj.m = m

	return obj
}

// Attempts to typecast the current value into an object.
// Returns error if the current value is not a json object.
func (j *Value) AsObject() (*Object, error) {
	obj := j.object()

	var err error

	if !obj.valid {
		err = errors.New("Is not an object")
	}

	return obj, err
}

// Attempts to typecast the current value into a string.
// Returns error if the current value is not a json string
func (j *Value) AsString() (string, error) {
	var valid bool

	// Check the type of this data
	switch j.data.(type) {
	case string:
		valid = true
		break
	}

	if valid {
		return j.data.(string), nil
	}

	return "", errors.New("not a string")
}

// Returns the value a json formatted string.
// Note: The method named String() is used by golang's log method for logging.
func (j *Value) String() string {
	f, err := json.Marshal(j.data)
	if err != nil {
		return err.Error()
	} else {
		return string(f)
	}
}
<|endoftext|>


<commit_before>// Package js provides functions for interacting with native JavaScript APIs. Calls to these functions are treated specially by GopherJS and translated directly to their JavaScript syntax.
//
// Type conversions between Go types and JavaScript types are performed automatically according to the table below. Types not listed are passed through. The second column denotes the types that are used when converting to `interface{}`.
//
//  | Go types                       | Go interface type              | JavaScript type |
//  | ------------------------------ | ------------------------------ | --------------- |
//  | bool                           | bool                           | Boolean         |
//  | int?, uint?, float?            | float64                        | Number          |
//  | string                         | string                         | String          |
//  | [?]int8                        | []int8                         | Int8Array       |
//  | [?]int16                       | []int16                        | Int16Array      |
//  | [?]int32, [?]int               | []int                          | Int32Array      |
//  | [?]uint8                       | []uint8                        | Int8Array       |
//  | [?]uint16                      | []uint16                       | Int16Array      |
//  | [?]uint32, [?]uint, [?]uintptr | []uint                         | Int32Array      |
//  | [?]float32                     | []float32                      | Float32Array    |
//  | [?]float64                     | []float64                      | Float64Array    |
//  | all other slices and arrays    | []interface{}                  | Array           |
//  | maps                           | map[string]interface{}         | Object          |
//  | functions                      | func(...interface{}) js.Object | Function        |
//  | time.Time                      | time.Time                      | Date            |
package js

// Object is a container for a native JavaScript object. Calls to its methods are treated specially by GopherJS and translated directly to their JavaScript syntax.
type Object interface {

	// Get returns the object's property with the given name.
	Get(name string) Object

	// Set assigns the value to the object's property with the given name.
	Set(name string, value interface{})

	// Delete removes the object's property with the given name.
	Delete(name string)

	// Get returns the object's "length" property, converted to int.
	Length() int

	// Index returns the i'th element of an array.
	Index(i int) Object

	// SetIndex sets the i'th element of an array.
	SetIndex(i int, value interface{})

	// Call calls the object's method with the given name.
	Call(name string, args ...interface{}) Object

	// Invoce calls the object itself. This will fail if it is not a function.
	Invoke(args ...interface{}) Object

	// New creates a new instance of this type object. This will fail if it not a function (constructor).
	New(args ...interface{}) Object

	// Bool returns the object converted to bool according to JavaScript type conversions.
	Bool() bool

	// Str returns the object converted to string according to JavaScript type conversions. Does not implement fmt.Stringer on purpose.
	Str() string

	// Int returns the object converted to int according to JavaScript type conversions (parseInt).
	Int() int

	// Int returns the object converted to int64 according to JavaScript type conversions (parseInt).
	Int64() int64

	// Int returns the object converted to uint64 according to JavaScript type conversions (parseInt).
	Uint64() uint64

	// Float returns the object converted to float64 according to JavaScript type conversions (parseFloat).
	Float() float64

	// Interface returns the object converted to interface{}. See GopherJS' README for details.
	Interface() interface{}

	// Unsafe returns the object as an uintptr, which can be converted via unsafe.Pointer. Not intended for public use.
	Unsafe() uintptr

	// IsUndefined returns true if the object is the JavaScript value "undefine".
	IsUndefined() bool

	// IsNull returns true if the object is the JavaScript value "null".
	IsNull() bool
}

// Error encapsulates JavaScript errors. Those are turned into a Go panic and may be rescued, giving an *Error that holds the JavaScript error object.
type Error struct {
	Object
}

// Error returns the message of the encapsulated JavaScript error object.
func (err *Error) Error() string {
	return "JavaScript error: " + err.Get("message").Str()
}

// Global gives JavaScript's global object ("window" for browsers and "GLOBAL" for Node.js). Set this to a mock for testing with pure Go.
var Global Object

// This gives the value of JavaScript's "this" keyword. It can be used when passing Go functions to JavaScript as callbacks. Set this to a mock for testing with pure Go.
var This Object

// Arguments gives the value of JavaScript's "arguments" keyword. It can be used when passing Go functions to JavaScript as callbacks. Set this to a mock for testing with pure Go.
var Arguments []Object

// Returns the internal JavaScript object that represents i. Not intended for public use.
func InternalObject(i interface{}) Object {
	return nil
}

func init() {
	// avoid dead code elimination of Error
	e := Error{}
	_ = e
}
<commit_msg>Fix comments<commit_after>// Package js provides functions for interacting with native JavaScript APIs. Calls to these functions are treated specially by GopherJS and translated directly to their JavaScript syntax.
//
// Type conversions between Go types and JavaScript types are performed automatically according to the table below. Types not listed are passed through. The second column denotes the types that are used when converting to `interface{}`.
//
//  | Go types                       | Go interface type              | JavaScript type |
//  | ------------------------------ | ------------------------------ | --------------- |
//  | bool                           | bool                           | Boolean         |
//  | int?, uint?, float?            | float64                        | Number          |
//  | string                         | string                         | String          |
//  | [?]int8                        | []int8                         | Int8Array       |
//  | [?]int16                       | []int16                        | Int16Array      |
//  | [?]int32, [?]int               | []int                          | Int32Array      |
//  | [?]uint8                       | []uint8                        | Int8Array       |
//  | [?]uint16                      | []uint16                       | Int16Array      |
//  | [?]uint32, [?]uint, [?]uintptr | []uint                         | Int32Array      |
//  | [?]float32                     | []float32                      | Float32Array    |
//  | [?]float64                     | []float64                      | Float64Array    |
//  | all other slices and arrays    | []interface{}                  | Array           |
//  | maps                           | map[string]interface{}         | Object          |
//  | functions                      | func(...interface{}) js.Object | Function        |
//  | time.Time                      | time.Time                      | Date            |
package js

// Object is a container for a native JavaScript object. Calls to its methods are treated specially by GopherJS and translated directly to their JavaScript syntax.
type Object interface {

	// Get returns the object's property with the given name.
	Get(name string) Object

	// Set assigns the value to the object's property with the given name.
	Set(name string, value interface{})

	// Delete removes the object's property with the given name.
	Delete(name string)

	// Get returns the object's "length" property, converted to int.
	Length() int

	// Index returns the i'th element of an array.
	Index(i int) Object

	// SetIndex sets the i'th element of an array.
	SetIndex(i int, value interface{})

	// Call calls the object's method with the given name.
	Call(name string, args ...interface{}) Object

	// Invoce calls the object itself. This will fail if it is not a function.
	Invoke(args ...interface{}) Object

	// New creates a new instance of this type object. This will fail if it not a function (constructor).
	New(args ...interface{}) Object

	// Bool returns the object converted to bool according to JavaScript type conversions.
	Bool() bool

	// Str returns the object converted to string according to JavaScript type conversions. Does not implement fmt.Stringer on purpose.
	Str() string

	// Int returns the object converted to int according to JavaScript type conversions (parseInt).
	Int() int

	// Int64 returns the object converted to int64 according to JavaScript type conversions (parseInt).
	Int64() int64

	// Uint64 returns the object converted to uint64 according to JavaScript type conversions (parseInt).
	Uint64() uint64

	// Float returns the object converted to float64 according to JavaScript type conversions (parseFloat).
	Float() float64

	// Interface returns the object converted to interface{}. See GopherJS' README for details.
	Interface() interface{}

	// Unsafe returns the object as an uintptr, which can be converted via unsafe.Pointer. Not intended for public use.
	Unsafe() uintptr

	// IsUndefined returns true if the object is the JavaScript value "undefine".
	IsUndefined() bool

	// IsNull returns true if the object is the JavaScript value "null".
	IsNull() bool
}

// Error encapsulates JavaScript errors. Those are turned into a Go panic and may be rescued, giving an *Error that holds the JavaScript error object.
type Error struct {
	Object
}

// Error returns the message of the encapsulated JavaScript error object.
func (err *Error) Error() string {
	return "JavaScript error: " + err.Get("message").Str()
}

// Global gives JavaScript's global object ("window" for browsers and "GLOBAL" for Node.js). Set this to a mock for testing with pure Go.
var Global Object

// This gives the value of JavaScript's "this" keyword. It can be used when passing Go functions to JavaScript as callbacks. Set this to a mock for testing with pure Go.
var This Object

// Arguments gives the value of JavaScript's "arguments" keyword. It can be used when passing Go functions to JavaScript as callbacks. Set this to a mock for testing with pure Go.
var Arguments []Object

// Returns the internal JavaScript object that represents i. Not intended for public use.
func InternalObject(i interface{}) Object {
	return nil
}

func init() {
	// avoid dead code elimination of Error
	e := Error{}
	_ = e
}
<|endoftext|>


<commit_before>package junos

import (
	"encoding/xml"
	"errors"
	"fmt"
	"github.com/Juniper/go-netconf/netconf"
	"io/ioutil"
	"log"
	"strings"
)

// Junos holds the connection information to our Junos device.
type Junos struct {
	*netconf.Session
}

// rollbackXML parses our rollback diff configuration.
type rollbackXML struct {
	XMLName xml.Name `xml:"rollback-information"`
	Config  string   `xml:"configuration-information>configuration-output"`
}

// CommandXML parses our operational command responses.
type commandXML struct {
	Config string `xml:",innerxml"`
}

type software struct {
	RES []routingEngine `xml:"multi-routing-engine-item"`
}

type routingEngine struct {
	Name    string `xml:"re-name"`
	Model   string `xml:"software-information>product-model"`
	Type    string `xml:"software-information>package-information>name"`
	Version string `xml:"software-information>package-information>comment"`
}

// NewSession establishes a new connection to a Junos device that we will use
// to run our commands against.
func NewSession(host, user, password string) *Junos {
	s, err := netconf.DialSSH(host, netconf.SSHConfigPassword(user, password))
	if err != nil {
		log.Fatal(err)
	}

	return &Junos{
		s,
	}
}

// Commit commits the configuration.
func (j *Junos) Commit() error {
	reply, err := j.Exec(rpcCommand["commit"])
	if err != nil {
		return err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// Lock locks the candidate configuration.
func (j *Junos) Lock() error {
	reply, err := j.Exec(rpcCommand["lock"])
	if err != nil {
		return err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// Unlock unlocks the candidate configuration.
func (j *Junos) Unlock() error {
	resp, err := j.Exec(rpcCommand["unlock"])
	if err != nil {
		return err
	}

	if resp.Ok == false {
		for _, m := range resp.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// LoadConfig loads a given configuration file locally or from
// an FTP or HTTP server. Format is either "set" "text" or "xml."
func (j *Junos) LoadConfig(path, format string) error {
	var command string
	switch format {
	case "set":
		if strings.Contains(path, "tp://") {
			command = fmt.Sprintf(rpcCommand["load-config-url-set"], path)
		} else {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			command = fmt.Sprintf(rpcCommand["load-config-local-set"], string(data))
		}
	case "text":
		if strings.Contains(path, "tp://") {
			command = fmt.Sprintf(rpcCommand["load-config-url-text"], path)
		} else {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			command = fmt.Sprintf(rpcCommand["load-config-local-text"], string(data))
		}
	case "xml":
		if strings.Contains(path, "tp://") {
			command = fmt.Sprintf(rpcCommand["load-config-url-xml"], path)
		} else {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			command = fmt.Sprintf(rpcCommand["load-config-local-xml"], string(data))
		}
	}

	reply, err := j.Exec(command)
	if err != nil {
		return err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// RollbackConfig loads and commits the configuration of a given rollback or rescue state.
func (j *Junos) RollbackConfig(option interface{}) error {
	var command string
	switch option.(type) {
	case int:
		command = fmt.Sprintf(rpcCommand["rollback-config"], option)
	case string:
        if option == "rescue" {
            command = fmt.Sprintf(rpcCommand["rescue-config"])
        }
	}

	reply, err := j.Exec(command)
	if err != nil {
		return err
	}

	err = j.Commit()
	if err != nil {
		return err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// RollbackDiff compares the current active configuration to a given rollback configuration.
func (j *Junos) RollbackDiff(compare int) (string, error) {
	rb := &rollbackXML{}
	command := fmt.Sprintf(rpcCommand["get-rollback-information-compare"], compare)
	reply, err := j.Exec(command)
	if err != nil {
		return "", err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return "", errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), rb)
	if err != nil {
		return "", err
	}

	return rb.Config, nil
}

// Command runs any operational mode command, such as "show" or "request."
// Format is either "text" or "xml".
func (j *Junos) Command(cmd, format string) (string, error) {
	c := &commandXML{}
	var command string

	switch format {
	case "xml":
		command = fmt.Sprintf(rpcCommand["command-xml"], cmd)
	default:
		command = fmt.Sprintf(rpcCommand["command"], cmd)
	}
	reply, err := j.Exec(command)
	if err != nil {
		return "", err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return "", errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &c)
	if err != nil {
		return "", err
	}

	if c.Config == "" {
		return "No output available.", nil
	}

	return c.Config, nil
}

// Close disconnects our session to the device.
func (j *Junos) Close() {
	j.Transport.Close()
}

// Software displays basic information about the device, such as software, hardware, etc.
func (j *Junos) Software() (*software, error) {
	data := &software{}
	reply, err := j.Exec(rpcCommand["software"])
	if err != nil {
		return nil, err
	}

	if reply.Ok == false {
		for _, m := range reply.Errors {
			return nil, errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &data)
	if err != nil {
		return nil, err
	}

	return data, nil
}
<commit_msg>Added more commit functions, and cleaned up error checking<commit_after>package junos

import (
	"encoding/xml"
	"errors"
	"fmt"
	"github.com/Juniper/go-netconf/netconf"
	"io/ioutil"
	"log"
	"strings"
)

// Junos holds the connection information to our Junos device.
type Junos struct {
	*netconf.Session
}

// CommandXML parses our operational command responses.
type commandXML struct {
	Config string `xml:",innerxml"`
}

type commitError struct {
	Path    string `xml:"error-path"`
	Element string `xml:"error-info>bad-element"`
	Message string `xml:"error-message"`
}

type commitResults struct {
	XMLName xml.Name      `xml:"commit-results"`
	Errors  []commitError `xml:"rpc-error"`
}

// rollbackXML parses our rollback diff configuration.
type rollbackXML struct {
	XMLName xml.Name `xml:"rollback-information"`
	Config  string   `xml:"configuration-information>configuration-output"`
}

type routingEngine struct {
	Name    string `xml:"re-name"`
	Model   string `xml:"software-information>product-model"`
	Type    string `xml:"software-information>package-information>name"`
	Version string `xml:"software-information>package-information>comment"`
}

type software struct {
	RES []routingEngine `xml:"multi-routing-engine-item"`
}

// Close disconnects our session to the device.
func (j *Junos) Close() {
	j.Transport.Close()
}

// Commit commits the configuration.
func (j *Junos) Commit() error {
	errs := &commitResults{}
	reply, err := j.Exec(rpcCommand["commit"])
	if err != nil {
		return err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &errs)
	if err != nil {
		return err
	}

	if errs.Errors != nil {
		for _, m := range errs.Errors {
			message := fmt.Sprintf("[%s]\n    %s\nError: %s", strings.Trim(m.Path, "[\r\n]"), strings.Trim(m.Element, "[\r\n]"), strings.Trim(m.Message, "[\r\n]"))
			return errors.New(message)
		}
	}

	return nil
}

// CommitAt commits the configuration at the specified <time>.
func (j *Junos) CommitAt(time string) error {
	errs := &commitResults{}
	command := fmt.Sprintf(rpcCommand["commit-at"], time)
	reply, err := j.Exec(command)
	if err != nil {
		return err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &errs)
	if err != nil {
		return err
	}

	if errs.Errors != nil {
		for _, m := range errs.Errors {
			message := fmt.Sprintf("[%s]\n    %s\nError: %s", strings.Trim(m.Path, "[\r\n]"), strings.Trim(m.Element, "[\r\n]"), strings.Trim(m.Message, "[\r\n]"))
			return errors.New(message)
		}
	}

	return nil
}

// CommitCheck checks the configuration for syntax errors.
func (j *Junos) CommitCheck() error {
	errs := &commitResults{}
	reply, err := j.Exec(rpcCommand["commit-check"])
	if err != nil {
		return err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &errs)
	if err != nil {
		return err
	}

	if errs.Errors != nil {
		for _, m := range errs.Errors {
			message := fmt.Sprintf("[%s]\n    %s\nError: %s", strings.Trim(m.Path, "[\r\n]"), strings.Trim(m.Element, "[\r\n]"), strings.Trim(m.Message, "[\r\n]"))
			return errors.New(message)
		}
	}

	return nil
}

// CommitConfirm rolls back the configuration after <delay> minutes.
func (j *Junos) CommitConfirm(delay int) error {
	errs := &commitResults{}
	command := fmt.Sprintf(rpcCommand["commit-confirm"], delay)
	reply, err := j.Exec(command)
	if err != nil {
		return err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &errs)
	if err != nil {
		return err
	}

	if errs.Errors != nil {
		for _, m := range errs.Errors {
			message := fmt.Sprintf("[%s]\n    %s\nError: %s", strings.Trim(m.Path, "[\r\n]"), strings.Trim(m.Element, "[\r\n]"), strings.Trim(m.Message, "[\r\n]"))
			return errors.New(message)
		}
	}

	return nil
}

// Command runs any operational mode command, such as "show" or "request."
// Format is either "text" or "xml".
func (j *Junos) Command(cmd, format string) (string, error) {
	c := &commandXML{}
	var command string

	switch format {
	case "xml":
		command = fmt.Sprintf(rpcCommand["command-xml"], cmd)
	default:
		command = fmt.Sprintf(rpcCommand["command"], cmd)
	}
	reply, err := j.Exec(command)
	if err != nil {
		return "", err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return "", errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &c)
	if err != nil {
		return "", err
	}

	if c.Config == "" {
		return "No output available.", nil
	}

	return c.Config, nil
}

// LoadConfig loads a given configuration file locally or from
// an FTP or HTTP server. Format is either "set" "text" or "xml."
func (j *Junos) LoadConfig(path, format string, commit bool) error {
	var command string
	switch format {
	case "set":
		if strings.Contains(path, "tp://") {
			command = fmt.Sprintf(rpcCommand["load-config-url-set"], path)
		} else {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			command = fmt.Sprintf(rpcCommand["load-config-local-set"], string(data))
		}
	case "text":
		if strings.Contains(path, "tp://") {
			command = fmt.Sprintf(rpcCommand["load-config-url-text"], path)
		} else {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			command = fmt.Sprintf(rpcCommand["load-config-local-text"], string(data))
		}
	case "xml":
		if strings.Contains(path, "tp://") {
			command = fmt.Sprintf(rpcCommand["load-config-url-xml"], path)
		} else {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			command = fmt.Sprintf(rpcCommand["load-config-local-xml"], string(data))
		}
	}

	reply, err := j.Exec(command)
	if err != nil {
		return err
	}

	if commit {
		err = j.Commit()
		if err != nil {
			return err
		}
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// Lock locks the candidate configuration.
func (j *Junos) Lock() error {
	reply, err := j.Exec(rpcCommand["lock"])
	if err != nil {
		return err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// NewSession establishes a new connection to a Junos device that we will use
// to run our commands against.
func NewSession(host, user, password string) *Junos {
	s, err := netconf.DialSSH(host, netconf.SSHConfigPassword(user, password))
	if err != nil {
		log.Fatal(err)
	}

	return &Junos{
		s,
	}
}

// RollbackConfig loads and commits the configuration of a given rollback or rescue state.
func (j *Junos) RollbackConfig(option interface{}) error {
	var command string
	switch option.(type) {
	case int:
		command = fmt.Sprintf(rpcCommand["rollback-config"], option)
	case string:
		if option == "rescue" {
			command = fmt.Sprintf(rpcCommand["rescue-config"])
		}
	}

	reply, err := j.Exec(command)
	if err != nil {
		return err
	}

	err = j.Commit()
	if err != nil {
		return err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// RollbackDiff compares the current active configuration to a given rollback configuration.
func (j *Junos) RollbackDiff(compare int) (string, error) {
	rb := &rollbackXML{}
	command := fmt.Sprintf(rpcCommand["get-rollback-information-compare"], compare)
	reply, err := j.Exec(command)
	if err != nil {
		return "", err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return "", errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), rb)
	if err != nil {
		return "", err
	}

	return rb.Config, nil
}

// Software displays basic information about the device, such as software, hardware, etc.
func (j *Junos) Software() (*software, error) {
	data := &software{}
	reply, err := j.Exec(rpcCommand["software"])
	if err != nil {
		return nil, err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return nil, errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &data)
	if err != nil {
		return nil, err
	}

	return data, nil
}

// Unlock unlocks the candidate configuration.
func (j *Junos) Unlock() error {
	resp, err := j.Exec(rpcCommand["unlock"])
	if err != nil {
		return err
	}

	if resp.Ok == false {
		for _, m := range resp.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package kopsa

import (
    "os"
    "io"
)

const mega = 1000000
var bufferSize = 10 * mega

func copyReaderWriter(dst io.Writer, src io.Reader) (int64, error) {
    buffer := make([]byte, bufferSize)
    return io.CopyBuffer(dst, src, buffer)
}

// If err == nil, file returned is OPEN
func appendFiles(dst string, srcs []string) (int64, error) {
    var (
        totalBytes  int64
        err         error
        destination *os.File
    )

    destination, err = os.Create(dst)
    if err != nil {
        return totalBytes, err
    }
    defer destination.Close()

    for _, src := range srcs {
        f, err := os.Open(src)
        if err != nil {
            return totalBytes, err
        }

        n, err := copyReaderWriter(destination, f)
        totalBytes = totalBytes + n
        if err != nil {
            f.Close()
            return totalBytes, err
        }

        if err = f.Close(); err != nil {
            return totalBytes, err
        }
    }

    return totalBytes, nil
}

// Does not close the source file
func copyFile(dst string, source *os.File) (int64, error) {
    var (
        totalBytes  int64
        destination *os.File
        err          error
    )

    destination, err = os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0660)
    if err != nil {
        destination.Close()
        os.Remove(dst)
        return totalBytes, err
    }

    totalBytes, err = copyReaderWriter(destination, source)
    if err != nil {
        destination.Close()
        os.Remove(dst)
        return totalBytes, err
    }

    if destination.Sync() != nil {
        destination.Close()
        os.Remove(dst)
        return totalBytes, err
    }

    err = destination.Close()
    if err != nil {
        os.Remove(dst)
        return totalBytes, err
    }

    return totalBytes, nil
}

func SetBufferSize(size int) {
    bufferSize = size
}

func Copy(dst string, srcs ...string) (int64, error) {
    var (
        err         error
        totalBytes  int64
        source      *os.File // This file must be closed inside this function
    )

    defer source.Close()

    if len(srcs) > 1 {
        totalBytes, err = appendFiles(dst, srcs)
        if err != nil {
            return totalBytes, err
        }
    } else {
        source, err = os.Open(srcs[0])
        if err != nil {
            return totalBytes, err
        }
        defer source.Close()

        n, err := copyFile(dst, source)
        totalBytes = totalBytes + n
        if err != nil {
            return totalBytes, err
        }

        err = source.Close()
        if err != nil {
            return totalBytes, err
        }
    }

    return totalBytes, err
}
<commit_msg>Use absolute file paths<commit_after>package kopsa

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

const mega = 1000000
var bufferSize = 10 * mega

func copyReaderWriter(dst io.Writer, src io.Reader) (int64, error) {
    buffer := make([]byte, bufferSize)
    return io.CopyBuffer(dst, src, buffer)
}

// If err == nil, file returned is OPEN
func appendFiles(dst string, srcs []string) (int64, error) {
    var (
        totalBytes  int64
        err         error
        destination *os.File
    )

    destination, err = os.Create(dst)
    if err != nil {
        return totalBytes, err
    }
    defer destination.Close()

    for _, src := range srcs {
        src, err = filepath.Abs(src)
        if err != nil {
            return totalBytes, err
        }

        sfi, err := os.Stat(src)
        if err != nil {
            return totalBytes, err
        }

        if !(sfi.Mode().IsRegular()) {
            return totalBytes, fmt.Errorf("non-regular source file %s (%q)", sfi.Name(), sfi.Mode().String())
        }

        f, err := os.Open(src)
        if err != nil {
            return totalBytes, err
        }

        n, err := copyReaderWriter(destination, f)
        totalBytes = totalBytes + n
        if err != nil {
            f.Close()
            return totalBytes, err
        }

        if err = f.Close(); err != nil {
            return totalBytes, err
        }
    }

    return totalBytes, nil
}

// Does not close the source file
func copyFile(dst string, source *os.File) (int64, error) {
    var (
        totalBytes  int64
        destination *os.File
        err          error
    )

    destination, err = os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0660)
    if err != nil {
        destination.Close()
        os.Remove(dst)
        return totalBytes, err
    }

    totalBytes, err = copyReaderWriter(destination, source)
    if err != nil {
        destination.Close()
        os.Remove(dst)
        return totalBytes, err
    }

    if destination.Sync() != nil {
        destination.Close()
        os.Remove(dst)
        return totalBytes, err
    }

    err = destination.Close()
    if err != nil {
        os.Remove(dst)
        return totalBytes, err
    }

    return totalBytes, nil
}

func SetBufferSize(size int) {
    bufferSize = size
}

func Copy(dst string, srcs ...string) (int64, error) {
    var (
        err         error
        totalBytes  int64
        source      *os.File // This file must be closed inside this function
    )

    defer source.Close()

    dst, err = filepath.Abs(dst)
    if err != nil {
        return totalBytes, err
    }

    if len(srcs) > 1 {
        totalBytes, err = appendFiles(dst, srcs)
        if err != nil {
            return totalBytes, err
        }
    } else {
        src, err := filepath.Abs(srcs[0])
        if err != nil {
            return totalBytes, err
        }

        source, err = os.Open(src)
        if err != nil {
            return totalBytes, err
        }
        defer source.Close()

        n, err := copyFile(dst, source)
        totalBytes = totalBytes + n
        if err != nil {
            return totalBytes, err
        }

        err = source.Close()
        if err != nil {
            return totalBytes, err
        }
    }

    return totalBytes, err
}
<|endoftext|>


<commit_before>package ladon

import (
	"github.com/pkg/errors"
)

// Ladon is an implementation of Warden.
type Ladon struct {
	Manager Manager
	Matcher matcher
}

func (l *Ladon) matcher() matcher {
	if l.Matcher == nil {
		l.Matcher = DefaultMatcher
	}
	return l.Matcher
}

// IsAllowed returns nil if subject s has permission p on resource r with context c or an error otherwise.
func (l *Ladon) IsAllowed(r *Request) (err error) {
	policies, err := l.Manager.FindRequestCandidates(r)
	if err != nil {
		return err
	}

	// Although the manager is responsible of matching the policies, it might decide to just scan for
	// subjects, it might return all policies, or it might have a different pattern matching than Golang.
	// Thus, we need to make sure that we actually matched the right policies.
	return l.doPoliciesAllow(r, policies)
}

func (l *Ladon) doPoliciesAllow(r *Request, policies []Policy) (err error) {
	var allowed = false

	// Iterate through all policies
	for _, p := range policies {
		// Does the action match with one of the policies?
		// This is the first check because usually actions are a superset of get|update|delete|set
		// and thus match faster.
		if pm, err := l.matcher().Matches(p, p.GetActions(), r.Action); err != nil {
			return errors.WithStack(err)
		} else if !pm {
			// no, continue to next policy
			continue
		}

		// Does the subject match with one of the policies?
		// There are usually less subjects than resources which is why this is checked
		// before checking for resources.
		if sm, err := l.matcher().Matches(p, p.GetSubjects(), r.Subject); err != nil {
			return err
		} else if !sm {
			// no, continue to next policy
			continue
		}

		// Does the resource match with one of the policies?
		if rm, err := l.matcher().Matches(p, p.GetResources(), r.Resource); err != nil {
			return errors.WithStack(err)
		} else if !rm {
			// no, continue to next policy
			continue
		}

		// Are the policies conditions met?
		// This is checked first because it usually has a small complexity.
		if !l.passesConditions(p, r) {
			// no, continue to next policy
			continue
		}

		// Is the policies effect deny? If yes, this overrides all allow policies -> access denied.
		if !p.AllowAccess() {
			return errors.WithStack(ErrRequestForcefullyDenied)
		}
		allowed = true
	}

	if !allowed {
		return errors.WithStack(ErrRequestDenied)
	}

	return nil
}

func (l *Ladon) passesConditions(p Policy, r *Request) bool {
	for key, condition := range p.GetConditions() {
		if pass := condition.Fulfills(r.Context[key], r); !pass {
			return false
		}
	}
	return true
}
<commit_msg>Expose doPoliciesAllow (#77)<commit_after>package ladon

import (
	"github.com/pkg/errors"
)

// Ladon is an implementation of Warden.
type Ladon struct {
	Manager Manager
	Matcher matcher
}

func (l *Ladon) matcher() matcher {
	if l.Matcher == nil {
		l.Matcher = DefaultMatcher
	}
	return l.Matcher
}

// IsAllowed returns nil if subject s has permission p on resource r with context c or an error otherwise.
func (l *Ladon) IsAllowed(r *Request) (err error) {
	policies, err := l.Manager.FindRequestCandidates(r)
	if err != nil {
		return err
	}

	// Although the manager is responsible of matching the policies, it might decide to just scan for
	// subjects, it might return all policies, or it might have a different pattern matching than Golang.
	// Thus, we need to make sure that we actually matched the right policies.
	return l.DoPoliciesAllow(r, policies)
}

// DoPoliciesAllow returns nil if subject s has permission p on resource r with context c for a given policy list or an error otherwise.
// The IsAllowed interface should be preferred since it uses the manager directly. This is a lower level interface for when you don't want to use the ladon manager.
func (l *Ladon) DoPoliciesAllow(r *Request, policies []Policy) (err error) {
	var allowed = false

	// Iterate through all policies
	for _, p := range policies {
		// Does the action match with one of the policies?
		// This is the first check because usually actions are a superset of get|update|delete|set
		// and thus match faster.
		if pm, err := l.matcher().Matches(p, p.GetActions(), r.Action); err != nil {
			return errors.WithStack(err)
		} else if !pm {
			// no, continue to next policy
			continue
		}

		// Does the subject match with one of the policies?
		// There are usually less subjects than resources which is why this is checked
		// before checking for resources.
		if sm, err := l.matcher().Matches(p, p.GetSubjects(), r.Subject); err != nil {
			return err
		} else if !sm {
			// no, continue to next policy
			continue
		}

		// Does the resource match with one of the policies?
		if rm, err := l.matcher().Matches(p, p.GetResources(), r.Resource); err != nil {
			return errors.WithStack(err)
		} else if !rm {
			// no, continue to next policy
			continue
		}

		// Are the policies conditions met?
		// This is checked first because it usually has a small complexity.
		if !l.passesConditions(p, r) {
			// no, continue to next policy
			continue
		}

		// Is the policies effect deny? If yes, this overrides all allow policies -> access denied.
		if !p.AllowAccess() {
			return errors.WithStack(ErrRequestForcefullyDenied)
		}
		allowed = true
	}

	if !allowed {
		return errors.WithStack(ErrRequestDenied)
	}

	return nil
}

func (l *Ladon) passesConditions(p Policy, r *Request) bool {
	for key, condition := range p.GetConditions() {
		if pass := condition.Fulfills(r.Context[key], r); !pass {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package remotecommand

import (
	"fmt"
	"io"
	"net/http"
	"net/url"

	"github.com/golang/glog"

	"k8s.io/apimachinery/pkg/util/httpstream"
	"k8s.io/apimachinery/pkg/util/remotecommand"
	restclient "k8s.io/client-go/rest"
	"k8s.io/client-go/transport"
	spdy "k8s.io/client-go/transport/spdy"
)

// StreamOptions holds information pertaining to the current streaming session: supported stream
// protocols, input/output streams, if the client is requesting a TTY, and a terminal size queue to
// support terminal resizing.
type StreamOptions struct {
	Stdin             io.Reader
	Stdout            io.Writer
	Stderr            io.Writer
	Tty               bool
	TerminalSizeQueue TerminalSizeQueue
}

// Executor is an interface for transporting shell-style streams.
type Executor interface {
	// Stream initiates the transport of the standard shell streams. It will transport any
	// non-nil stream to a remote system, and return an error if a problem occurs. If tty
	// is set, the stderr stream is not used (raw TTY manages stdout and stderr over the
	// stdout stream).
	Stream(options StreamOptions) error
}

type streamCreator interface {
	CreateStream(headers http.Header) (httpstream.Stream, error)
}

type streamProtocolHandler interface {
	stream(conn streamCreator) error
}

// streamExecutor handles transporting standard shell streams over an httpstream connection.
type streamExecutor struct {
	upgrader  spdy.Upgrader
	transport http.RoundTripper

	method    string
	url       *url.URL
	protocols []string
}

// NewSPDYExecutor connects to the provided server and upgrades the connection to
// multiplexed bidirectional streams.
func NewSPDYExecutor(config *restclient.Config, method string, url *url.URL) (Executor, error) {
	return NewSPDYExecutorForProtocols(
		config, method, url,
		remotecommand.StreamProtocolV4Name,
		remotecommand.StreamProtocolV3Name,
		remotecommand.StreamProtocolV2Name,
		remotecommand.StreamProtocolV1Name,
	)
}

// NewSPDYExecutorForProtocols connects to the provided server and upgrades the connection to
// multiplexed bidirectional streams using only the provided protocols. Exposed for testing, most
// callers should use NewSPDYExecutor.
func NewSPDYExecutorForProtocols(config *restclient.Config, method string, url *url.URL, protocols ...string) (Executor, error) {
	wrapper, upgradeRoundTripper, err := spdy.RoundTripperFor(config)
	if err != nil {
		return nil, err
	}
	wrapper = transport.DebugWrappers(wrapper)
	return &streamExecutor{
		upgrader:  upgradeRoundTripper,
		transport: wrapper,
		method:    method,
		url:       url,
		protocols: protocols,
	}, nil
}

// Stream opens a protocol streamer to the server and streams until a client closes
// the connection or the server disconnects.
func (e *streamExecutor) Stream(options StreamOptions) error {
	req, err := http.NewRequest(e.method, e.url.String(), nil)
	if err != nil {
		return fmt.Errorf("error creating request: %v", err)
	}

	conn, protocol, err := spdy.Negotiate(
		e.upgrader,
		&http.Client{Transport: e.transport},
		req,
		e.protocols...,
	)
	if err != nil {
		return err
	}
	defer conn.Close()

	var streamer streamProtocolHandler

	switch protocol {
	case remotecommand.StreamProtocolV4Name:
		streamer = newStreamProtocolV4(options)
	case remotecommand.StreamProtocolV3Name:
		streamer = newStreamProtocolV3(options)
	case remotecommand.StreamProtocolV2Name:
		streamer = newStreamProtocolV2(options)
	case "":
		glog.V(4).Infof("The server did not negotiate a streaming protocol version. Falling back to %s", remotecommand.StreamProtocolV1Name)
		fallthrough
	case remotecommand.StreamProtocolV1Name:
		streamer = newStreamProtocolV1(options)
	}

	return streamer.stream(conn)
}
<commit_msg>UPSTREAM: 55974: Allow constructing spdy executor from existing transports<commit_after>/*
Copyright 2015 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package remotecommand

import (
	"fmt"
	"io"
	"net/http"
	"net/url"

	"github.com/golang/glog"

	"k8s.io/apimachinery/pkg/util/httpstream"
	"k8s.io/apimachinery/pkg/util/remotecommand"
	restclient "k8s.io/client-go/rest"
	spdy "k8s.io/client-go/transport/spdy"
)

// StreamOptions holds information pertaining to the current streaming session: supported stream
// protocols, input/output streams, if the client is requesting a TTY, and a terminal size queue to
// support terminal resizing.
type StreamOptions struct {
	Stdin             io.Reader
	Stdout            io.Writer
	Stderr            io.Writer
	Tty               bool
	TerminalSizeQueue TerminalSizeQueue
}

// Executor is an interface for transporting shell-style streams.
type Executor interface {
	// Stream initiates the transport of the standard shell streams. It will transport any
	// non-nil stream to a remote system, and return an error if a problem occurs. If tty
	// is set, the stderr stream is not used (raw TTY manages stdout and stderr over the
	// stdout stream).
	Stream(options StreamOptions) error
}

type streamCreator interface {
	CreateStream(headers http.Header) (httpstream.Stream, error)
}

type streamProtocolHandler interface {
	stream(conn streamCreator) error
}

// streamExecutor handles transporting standard shell streams over an httpstream connection.
type streamExecutor struct {
	upgrader  spdy.Upgrader
	transport http.RoundTripper

	method    string
	url       *url.URL
	protocols []string
}

// NewSPDYExecutor connects to the provided server and upgrades the connection to
// multiplexed bidirectional streams.
func NewSPDYExecutor(config *restclient.Config, method string, url *url.URL) (Executor, error) {
	wrapper, upgradeRoundTripper, err := spdy.RoundTripperFor(config)
	if err != nil {
		return nil, err
	}
	return NewSPDYExecutorForTransports(wrapper, upgradeRoundTripper, method, url)
}

// NewSPDYExecutorForTransports connects to the provided server using the given transport,
// upgrades the response using the given upgrader to multiplexed bidirectional streams.
func NewSPDYExecutorForTransports(transport http.RoundTripper, upgrader spdy.Upgrader, method string, url *url.URL) (Executor, error) {
	return NewSPDYExecutorForProtocols(
		transport, upgrader, method, url,
		remotecommand.StreamProtocolV4Name,
		remotecommand.StreamProtocolV3Name,
		remotecommand.StreamProtocolV2Name,
		remotecommand.StreamProtocolV1Name,
	)
}

// NewSPDYExecutorForProtocols connects to the provided server and upgrades the connection to
// multiplexed bidirectional streams using only the provided protocols. Exposed for testing, most
// callers should use NewSPDYExecutor or NewSPDYExecutorForTransports.
func NewSPDYExecutorForProtocols(transport http.RoundTripper, upgrader spdy.Upgrader, method string, url *url.URL, protocols ...string) (Executor, error) {
	return &streamExecutor{
		upgrader:  upgrader,
		transport: transport,
		method:    method,
		url:       url,
		protocols: protocols,
	}, nil
}

// Stream opens a protocol streamer to the server and streams until a client closes
// the connection or the server disconnects.
func (e *streamExecutor) Stream(options StreamOptions) error {
	req, err := http.NewRequest(e.method, e.url.String(), nil)
	if err != nil {
		return fmt.Errorf("error creating request: %v", err)
	}

	conn, protocol, err := spdy.Negotiate(
		e.upgrader,
		&http.Client{Transport: e.transport},
		req,
		e.protocols...,
	)
	if err != nil {
		return err
	}
	defer conn.Close()

	var streamer streamProtocolHandler

	switch protocol {
	case remotecommand.StreamProtocolV4Name:
		streamer = newStreamProtocolV4(options)
	case remotecommand.StreamProtocolV3Name:
		streamer = newStreamProtocolV3(options)
	case remotecommand.StreamProtocolV2Name:
		streamer = newStreamProtocolV2(options)
	case "":
		glog.V(4).Infof("The server did not negotiate a streaming protocol version. Falling back to %s", remotecommand.StreamProtocolV1Name)
		fallthrough
	case remotecommand.StreamProtocolV1Name:
		streamer = newStreamProtocolV1(options)
	}

	return streamer.stream(conn)
}
<|endoftext|>


<commit_before>package assert

import (
	"io"
	"os"
)

type location struct {
	Test     string
	FileName string
	Line     int
}

type errorLogger interface {
	Log(location *location, message string)
}

func newLogger() errorLogger {
	return &errorLoggerImpl{os.Stderr}
}

type errorLoggerImpl struct {
	writer io.Writer
}

func (logger *errorLoggerImpl) Log(location *location, message string) {
}
<commit_msg>left todo for implementer<commit_after>package assert

import (
	"io"
	"os"
)

type location struct {
	Test     string
	FileName string
	Line     int
}

type errorLogger interface {
	Log(location *location, message string)
}

func newLogger() errorLogger {
	return &errorLoggerImpl{os.Stderr}
}

type errorLoggerImpl struct {
	writer io.Writer
}

func (logger *errorLoggerImpl) Log(location *location, message string) {
	// TODO: log using io.WriteString(logger.writer, ...)
}
<|endoftext|>


<commit_before>package cli_test

import (
	"context"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/datawire/ambassador/pkg/dtest"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"

	"github.com/datawire/telepresence2/pkg/client"
	"github.com/datawire/telepresence2/pkg/client/cli"
	"github.com/datawire/telepresence2/pkg/version"
)

var testVersion = "v0.1.2-test"
var namespace = fmt.Sprintf("telepresence-%d", os.Getpid())
var proxyOnMatch = regexp.MustCompile(`Proxy:\s+ON`)

var _ = Describe("Telepresence", func() {
	Context("With no daemon running", func() {
		It("Returns version", func() {
			stdout, stderr := telepresence("--version")
			Expect(stderr).To(BeEmpty())
			Expect(stdout).To(Equal(fmt.Sprintf("Client %s", client.DisplayVersion())))
		})
		It("Returns valid status", func() {
			out, _ := telepresence("--status")
			Expect(out).To(ContainSubstring("The telepresence daemon has not been started"))
		})
	})

	Context("With bad KUBECONFIG", func() {
		It("Reports config error and exits", func() {
			kubeConfig := os.Getenv("KUBECONFIG")
			defer os.Setenv("KUBECONFIG", kubeConfig)
			os.Setenv("KUBECONFIG", "/dev/null")
			stdout, stderr := telepresence()
			Expect(stderr).To(ContainSubstring("kubectl config current-context"))
			Expect(stdout).To(ContainSubstring("Launching Telepresence Daemon"))
			Expect(stdout).To(ContainSubstring("Daemon quitting"))
		})
	})

	Context("With bad context", func() {
		It("Reports connect error and exits", func() {
			stdout, stderr := telepresence("--context", "not-likely-to-exist")
			Expect(stderr).To(ContainSubstring(`"not-likely-to-exist" does not exist`))
			Expect(stdout).To(ContainSubstring("Launching Telepresence Daemon"))
			Expect(stdout).To(ContainSubstring("Daemon quitting"))
		})
	})

	Context("When started with a command", func() {
		It("Connects, executes the command, and then exits", func() {
			stdout, stderr := telepresence("--namespace", namespace, "--", client.GetExe(), "--status")
			Expect(stderr).To(BeEmpty())
			Expect(stdout).To(ContainSubstring("Launching Telepresence Daemon"))
			Expect(stdout).To(ContainSubstring("Connected to context"))
			Expect(stdout).To(ContainSubstring("Context:"))
			Expect(stdout).To(MatchRegexp(proxyOnMatch.String()))
			Expect(stdout).To(ContainSubstring("Daemon quitting"))
		})
	})

	Context("When started in the background", func() {
		itCount := int32(0)
		itTotal := int32(0) // To simulate AfterAll. Add one for each added It() test
		BeforeEach(func() {
			// This is a bit annoying, but ginkgo does not provide a context scoped "BeforeAll"
			// Will be fixed in ginkgo 2.0
			if atomic.CompareAndSwapInt32(&itCount, 0, 1) {
				stdout, stderr := telepresence("--namespace", namespace, "--no-wait")
				Expect(stderr).To(BeEmpty())
				Expect(stdout).To(ContainSubstring("Connected to context"))
			} else {
				atomic.AddInt32(&itCount, 1)
			}
		})

		AfterEach(func() {
			// This is a bit annoying, but ginkgo does not provide a context scoped "AfterAll"
			// Will be fixed in ginkgo 2.0
			if atomic.CompareAndSwapInt32(&itCount, itTotal, 0) {
				stdout, stderr := telepresence("--quit")
				Expect(stderr).To(BeEmpty())
				Expect(stdout).To(ContainSubstring("quitting"))
			}
		})

		It("Reports version from daemon", func() {
			stdout, stderr := telepresence("--version")
			Expect(stderr).To(BeEmpty())
			vs := client.DisplayVersion()
			Expect(stdout).To(ContainSubstring(fmt.Sprintf("Client %s", vs)))
			Expect(stdout).To(ContainSubstring(fmt.Sprintf("Daemon %s", vs)))
		})
		itTotal++

		It("Reports status as connected", func() {
			stdout, stderr := telepresence("--status")
			Expect(stderr).To(BeEmpty())
			Expect(stdout).To(ContainSubstring("Context:"))
		})
		itTotal++

		It("Proxies outbound traffic", func() {
			echoReady := make(chan error)
			go func() {
				echoReady <- applyEchoService()
			}()

			// Give outbound interceptor 15 seconds to kick in.
			proxy := false
			for i := 0; i < 30; i++ {
				stdout, stderr := telepresence("--status")
				Expect(stderr).To(BeEmpty())
				if proxy = proxyOnMatch.MatchString(stdout); proxy {
					break
				}
				time.Sleep(500 * time.Millisecond)
			}
			Expect(proxy).To(BeTrue(), "Timeout waiting for network overrides to establish")

			err := <-echoReady
			Expect(err).NotTo(HaveOccurred())

			out, err := output("curl", "-s", "echo-easy")
			Expect(err).NotTo(HaveOccurred())
			Expect(out).To(ContainSubstring("Request served by echo-easy-"))
		})
		itTotal++

		It("Proxies inbound traffic with --intercept", func() {
			stdout, stderr := telepresence("--intercept", "echo-easy", "--port", "9000", "--no-wait")
			Expect(stderr).To(BeEmpty())
			Expect(stdout).To(ContainSubstring("Using deployment echo-easy"))
			srv := &http.Server{Addr: ":9000"}

			defer func() {
				err := srv.Shutdown(context.Background())
				Expect(err).ToNot(HaveOccurred())
				stdout, stderr = telepresence("--remove", "echo-easy")
				Expect(stderr).To(BeEmpty())
				Expect(stdout).To(BeEmpty())
			}()

			go func() {
				http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
					fmt.Fprintf(w, "hello from intercept at %s", r.URL.Path)
				})
				err := srv.ListenAndServe()
				Expect(err).To(Equal(http.ErrServerClosed))
			}()

			var err error
			for retry := 0; retry < 100; retry++ {
				stdout, err = output("curl", "-s", "echo-easy")
				if err == nil && !strings.Contains(stdout, "served by echo-easy-") {
					break
				}
				// Inbound proxy hasn't kicked in yet
				time.Sleep(50 * time.Millisecond)
			}
			Expect(err).ToNot(HaveOccurred())
			Expect(stdout).To(Equal("hello from intercept at /"))
		})
		itTotal++
	})
})

var _ = BeforeSuite(func() {
	version.Version = testVersion

	wg := sync.WaitGroup{}
	wg.Add(1)
	go func() {
		defer GinkgoRecover()
		defer wg.Done()
		executable, err := buildExecutable(testVersion)
		Expect(err).NotTo(HaveOccurred())
		client.SetExe(executable)
	}()

	_ = os.Remove(client.ConnectorSocketName)
	err := run("sudo", "true")
	Expect(err).ToNot(HaveOccurred(), "acquire privileges")

	registry := dtest.DockerRegistry()
	os.Setenv("KO_DOCKER_REPO", registry)
	os.Setenv("TELEPRESENCE_REGISTRY", registry)

	wg.Add(1)
	go func() {
		defer GinkgoRecover()
		defer wg.Done()
		err := publishManager(testVersion)
		Expect(err).NotTo(HaveOccurred())
	}()

	wg.Add(1)
	go func() {
		defer GinkgoRecover()
		defer wg.Done()

		kubeconfig := dtest.Kubeconfig()
		os.Setenv("DTEST_KUBECONFIG", kubeconfig)
		os.Setenv("KUBECONFIG", kubeconfig)
		err = run("kubectl", "create", "namespace", namespace)
		Expect(err).NotTo(HaveOccurred())
	}()
	wg.Wait()
})

var _ = AfterSuite(func() {
	_ = run("kubectl", "delete", "namespace", namespace)
})

func applyEchoService() error {
	err := run("ko", "apply", "--namespace", namespace, "-f", "k8s/echo-easy.yaml")
	if err != nil {
		return err
	}
	for i := 0; i < 30; i++ {
		time.Sleep(time.Second)
		err = run(
			"kubectl", "--namespace", namespace, "run", "curl-from-cluster", "--rm", "-it",
			"--image=pstauffer/curl", "--restart=Never", "--",
			"curl", "--silent", "--output", "/dev/null",
			"http://echo-easy."+namespace,
		)
		if err == nil {
			return nil
		}
	}
	return errors.New("timed out waiting for echo-easy service")
}

func run(args ...string) error {
	return client.RunError(exec.Command(args[0], args[1:]...).Run())
}

func output(args ...string) (string, error) {
	out, err := exec.Command(args[0], args[1:]...).Output()
	return string(out), client.RunError(err)
}

func publishManager(testVersion string) error {
	cmd := exec.Command("ko", "publish", "--local", "./cmd/traffic")
	cmd.Env = append(os.Environ(),
		fmt.Sprintf(`GOFLAGS=-ldflags=-X=github.com/datawire/telepresence2/pkg/version.Version=%s`,
			testVersion))
	out, err := cmd.Output()
	if err != nil {
		return client.RunError(err)
	}
	imageName := strings.TrimSpace(string(out))
	tag := fmt.Sprintf("%s/tel2:%s", dtest.DockerRegistry(), testVersion)
	err = run("docker", "tag", imageName, tag)
	if err != nil {
		return err
	}
	return run("docker", "push", tag)
}

func buildExecutable(testVersion string) (string, error) {
	executable := filepath.Join("build-output", "bin", "/telepresence")
	return executable, run("go", "build", "-ldflags",
		fmt.Sprintf("-X=github.com/datawire/telepresence2/pkg/version.Version=%s", testVersion),
		"-o", executable, "./cmd/telepresence")
}

func getCommand(args ...string) *cobra.Command {
	cmd := cli.Command()
	cmd.SetArgs(args)
	flags := cmd.Flags()

	// Circumvent test flag conflict explained here https://golang.org/doc/go1.13#testing
	flag.Visit(func(f *flag.Flag) {
		flags.AddGoFlag(f)
	})
	cmd.SetOut(new(strings.Builder))
	cmd.SetErr(new(strings.Builder))
	cmd.SilenceErrors = true
	return cmd
}

func trimmed(f func() io.Writer) string {
	if out, ok := f().(*strings.Builder); ok {
		return strings.TrimSpace(out.String())
	}
	return ""
}

// telepresence executes the CLI command in-process
func telepresence(args ...string) (string, string) {
	cmd := getCommand(args...)
	err := cmd.Execute()
	if err != nil {
		fmt.Fprintln(cmd.ErrOrStderr(), err.Error())
	}
	return trimmed(cmd.OutOrStdout), trimmed(cmd.ErrOrStderr)
}
<commit_msg>Enhance intercept test so that it tests multiple parallel intercepts<commit_after>package cli_test

import (
	"context"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/datawire/ambassador/pkg/dtest"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/spf13/cobra"

	"github.com/datawire/telepresence2/pkg/client"
	"github.com/datawire/telepresence2/pkg/client/cli"
	"github.com/datawire/telepresence2/pkg/version"
)

var testVersion = "v0.1.2-test"
var namespace = fmt.Sprintf("telepresence-%d", os.Getpid())
var proxyOnMatch = regexp.MustCompile(`Proxy:\s+ON`)

// serviceCount is the number of interceptable services that gets installed
// in the cluster and later intercepted
const serviceCount = 8

var _ = Describe("Telepresence", func() {
	Context("With no daemon running", func() {
		It("Returns version", func() {
			stdout, stderr := telepresence("--version")
			Expect(stderr).To(BeEmpty())
			Expect(stdout).To(Equal(fmt.Sprintf("Client %s", client.DisplayVersion())))
		})
		It("Returns valid status", func() {
			out, _ := telepresence("--status")
			Expect(out).To(ContainSubstring("The telepresence daemon has not been started"))
		})
	})

	Context("With bad KUBECONFIG", func() {
		It("Reports config error and exits", func() {
			kubeConfig := os.Getenv("KUBECONFIG")
			defer os.Setenv("KUBECONFIG", kubeConfig)
			os.Setenv("KUBECONFIG", "/dev/null")
			stdout, stderr := telepresence()
			Expect(stderr).To(ContainSubstring("kubectl config current-context"))
			Expect(stdout).To(ContainSubstring("Launching Telepresence Daemon"))
			Expect(stdout).To(ContainSubstring("Daemon quitting"))
		})
	})

	Context("With bad context", func() {
		It("Reports connect error and exits", func() {
			stdout, stderr := telepresence("--context", "not-likely-to-exist")
			Expect(stderr).To(ContainSubstring(`"not-likely-to-exist" does not exist`))
			Expect(stdout).To(ContainSubstring("Launching Telepresence Daemon"))
			Expect(stdout).To(ContainSubstring("Daemon quitting"))
		})
	})

	Context("When started with a command", func() {
		It("Connects, executes the command, and then exits", func() {
			stdout, stderr := telepresence("--namespace", namespace, "--", client.GetExe(), "--status")
			Expect(stderr).To(BeEmpty())
			Expect(stdout).To(ContainSubstring("Launching Telepresence Daemon"))
			Expect(stdout).To(ContainSubstring("Connected to context"))
			Expect(stdout).To(ContainSubstring("Context:"))
			Expect(stdout).To(MatchRegexp(proxyOnMatch.String()))
			Expect(stdout).To(ContainSubstring("Daemon quitting"))
		})
	})

	Context("When started in the background", func() {
		itCount := int32(0)
		itTotal := int32(0) // To simulate AfterAll. Add one for each added It() test
		BeforeEach(func() {
			// This is a bit annoying, but ginkgo does not provide a context scoped "BeforeAll"
			// Will be fixed in ginkgo 2.0
			if atomic.CompareAndSwapInt32(&itCount, 0, 1) {
				stdout, stderr := telepresence("--namespace", namespace, "--no-wait")
				Expect(stderr).To(BeEmpty())
				Expect(stdout).To(ContainSubstring("Connected to context"))
			} else {
				atomic.AddInt32(&itCount, 1)
			}
		})

		AfterEach(func() {
			// This is a bit annoying, but ginkgo does not provide a context scoped "AfterAll"
			// Will be fixed in ginkgo 2.0
			if atomic.CompareAndSwapInt32(&itCount, itTotal, 0) {
				stdout, stderr := telepresence("--quit")
				Expect(stderr).To(BeEmpty())
				Expect(stdout).To(ContainSubstring("quitting"))
			}
		})

		It("Reports version from daemon", func() {
			stdout, stderr := telepresence("--version")
			Expect(stderr).To(BeEmpty())
			vs := client.DisplayVersion()
			Expect(stdout).To(ContainSubstring(fmt.Sprintf("Client %s", vs)))
			Expect(stdout).To(ContainSubstring(fmt.Sprintf("Daemon %s", vs)))
		})
		itTotal++

		It("Reports status as connected", func() {
			stdout, stderr := telepresence("--status")
			Expect(stderr).To(BeEmpty())
			Expect(stdout).To(ContainSubstring("Context:"))
		})
		itTotal++

		It("Proxies outbound traffic", func() {
			// Give outbound interceptor 15 seconds to kick in.
			proxy := false
			for i := 0; i < 30; i++ {
				stdout, stderr := telepresence("--status")
				Expect(stderr).To(BeEmpty())
				if proxy = proxyOnMatch.MatchString(stdout); proxy {
					break
				}
				time.Sleep(500 * time.Millisecond)
			}
			Expect(proxy).To(BeTrue(), "Timeout waiting for network overrides to establish")

			for i := 0; i < serviceCount; i++ {
				svc := fmt.Sprintf("hello-%d", i)
				out, err := output("curl", "-s", svc)
				Expect(err).NotTo(HaveOccurred())
				Expect(out).To(ContainSubstring(fmt.Sprintf("Request served by %s-", svc)))
			}
		})
		itTotal++

		It("Proxies inbound traffic with --intercept", func() {
			for i := 0; i < serviceCount; i++ {
				svc := fmt.Sprintf("hello-%d", i)
				port := strconv.Itoa(9000 + i)
				stdout, stderr := telepresence("--intercept", svc, "--port", port, "--no-wait")
				Expect(stderr).To(BeEmpty())
				Expect(stdout).To(ContainSubstring("Using deployment " + svc))
				srv := &http.Server{Addr: ":" + port, Handler: http.NewServeMux()}

				defer func() {
					err := srv.Shutdown(context.Background())
					Expect(err).ToNot(HaveOccurred())
					stdout, stderr = telepresence("--remove", svc)
					Expect(stderr).To(BeEmpty())
					Expect(stdout).To(BeEmpty())
				}()

				go func() {
					srv.Handler.(*http.ServeMux).HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
						fmt.Fprintf(w, "%s from intercept at %s", svc, r.URL.Path)
					})
					err := srv.ListenAndServe()
					Expect(err).To(Equal(http.ErrServerClosed))
				}()
			}

			for i := 0; i < serviceCount; i++ {
				svc := fmt.Sprintf("hello-%d", i)
				var err error
				var stdout string
				for retry := 0; retry < 100; retry++ {
					stdout, err = output("curl", "-s", svc)
					if err == nil && !strings.Contains(stdout, fmt.Sprintf("served by %s-", svc)) {
						break
					}
					// Inbound proxy hasn't kicked in yet
					time.Sleep(50 * time.Millisecond)
				}
				Expect(err).ToNot(HaveOccurred())
				Expect(stdout).To(Equal(fmt.Sprintf("%s from intercept at /", svc)))
			}
		})
		itTotal++
	})
})

var _ = BeforeSuite(func() {
	version.Version = testVersion

	wg := sync.WaitGroup{}
	wg.Add(1)
	go func() {
		defer GinkgoRecover()
		defer wg.Done()
		executable, err := buildExecutable(testVersion)
		Expect(err).NotTo(HaveOccurred())
		client.SetExe(executable)
	}()

	_ = os.Remove(client.ConnectorSocketName)
	err := run("sudo", "true")
	Expect(err).ToNot(HaveOccurred(), "acquire privileges")

	registry := dtest.DockerRegistry()
	os.Setenv("KO_DOCKER_REPO", registry)
	os.Setenv("TELEPRESENCE_REGISTRY", registry)

	wg.Add(1)
	go func() {
		defer GinkgoRecover()
		defer wg.Done()
		err := publishManager(testVersion)
		Expect(err).NotTo(HaveOccurred())
	}()

	wg.Add(1)
	go func() {
		defer GinkgoRecover()
		defer wg.Done()

		kubeconfig := dtest.Kubeconfig()
		os.Setenv("DTEST_KUBECONFIG", kubeconfig)
		os.Setenv("KUBECONFIG", kubeconfig)
		err = run("kubectl", "create", "namespace", namespace)
		Expect(err).NotTo(HaveOccurred())
	}()
	wg.Wait()

	wg.Add(serviceCount)
	for i := 0; i < serviceCount; i++ {
		i := i
		go func() {
			defer GinkgoRecover()
			defer wg.Done()
			err = applyEchoService(fmt.Sprintf("hello-%d", i))
			Expect(err).NotTo(HaveOccurred())
		}()
	}
	wg.Wait()
})

var _ = AfterSuite(func() {
	_ = run("kubectl", "delete", "namespace", namespace)
})

func applyEchoService(name string) error {
	err := run("kubectl", "--namespace", namespace, "create", "deploy", name, "--image", "jmalloc/echo-server:0.1.0")
	if err != nil {
		return fmt.Errorf("failed to create deployment %s: %s", name, err)
	}
	err = run("kubectl", "--namespace", namespace, "expose", "deploy", name, "--port", "80", "--target-port", "8080")
	if err != nil {
		return fmt.Errorf("failed to expose deployment %s: %s", name, err)
	}
	for i := 0; i < 30; i++ {
		time.Sleep(time.Second)
		err = run(
			"kubectl", "--namespace", namespace, "run", "curl-from-cluster", "--rm", "-it",
			"--image=pstauffer/curl", "--restart=Never", "--",
			"curl", "--silent", "--output", "/dev/null",
			fmt.Sprintf("http://%s.%s", name, namespace),
		)
		if err == nil {
			return nil
		}
	}
	return fmt.Errorf("timed out waiting for %s service", name)
}

func run(args ...string) error {
	return client.RunError(exec.Command(args[0], args[1:]...).Run())
}

func output(args ...string) (string, error) {
	out, err := exec.Command(args[0], args[1:]...).Output()
	return string(out), client.RunError(err)
}

func publishManager(testVersion string) error {
	cmd := exec.Command("ko", "publish", "--local", "./cmd/traffic")
	cmd.Env = append(os.Environ(),
		fmt.Sprintf(`GOFLAGS=-ldflags=-X=github.com/datawire/telepresence2/pkg/version.Version=%s`,
			testVersion))
	out, err := cmd.Output()
	if err != nil {
		return client.RunError(err)
	}
	imageName := strings.TrimSpace(string(out))
	tag := fmt.Sprintf("%s/tel2:%s", dtest.DockerRegistry(), testVersion)
	err = run("docker", "tag", imageName, tag)
	if err != nil {
		return err
	}
	return run("docker", "push", tag)
}

func buildExecutable(testVersion string) (string, error) {
	executable := filepath.Join("build-output", "bin", "/telepresence")
	return executable, run("go", "build", "-ldflags",
		fmt.Sprintf("-X=github.com/datawire/telepresence2/pkg/version.Version=%s", testVersion),
		"-o", executable, "./cmd/telepresence")
}

func getCommand(args ...string) *cobra.Command {
	cmd := cli.Command()
	cmd.SetArgs(args)
	flags := cmd.Flags()

	// Circumvent test flag conflict explained here https://golang.org/doc/go1.13#testing
	flag.Visit(func(f *flag.Flag) {
		flags.AddGoFlag(f)
	})
	cmd.SetOut(new(strings.Builder))
	cmd.SetErr(new(strings.Builder))
	cmd.SilenceErrors = true
	return cmd
}

func trimmed(f func() io.Writer) string {
	if out, ok := f().(*strings.Builder); ok {
		return strings.TrimSpace(out.String())
	}
	return ""
}

// telepresence executes the CLI command in-process
func telepresence(args ...string) (string, string) {
	cmd := getCommand(args...)
	err := cmd.Execute()
	if err != nil {
		fmt.Fprintln(cmd.ErrOrStderr(), err.Error())
	}
	return trimmed(cmd.OutOrStdout), trimmed(cmd.ErrOrStderr)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package custom_metrics

import (
	"fmt"

	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime/schema"
	serializer "k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/util/flowcontrol"
	"k8s.io/metrics/pkg/apis/custom_metrics/v1beta1"
)

type customMetricsClient struct {
	client rest.Interface
	mapper meta.RESTMapper
}

func New(client rest.Interface) CustomMetricsClient {
	return &customMetricsClient{
		client: client,
	}
}

func NewForConfig(c *rest.Config) (CustomMetricsClient, error) {
	configShallowCopy := *c
	if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
		configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
	}
	configShallowCopy.APIPath = "/apis"
	if configShallowCopy.UserAgent == "" {
		configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent()
	}
	configShallowCopy.GroupVersion = &v1beta1.SchemeGroupVersion
	configShallowCopy.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}

	client, err := rest.RESTClientFor(&configShallowCopy)
	if err != nil {
		return nil, err
	}

	return New(client), nil
}

func NewForConfigOrDie(c *rest.Config) CustomMetricsClient {
	client, err := NewForConfig(c)
	if err != nil {
		panic(err)
	}
	return client
}

// NewForMapper constructs the client with a RESTMapper, which allows more
// accurate translation from GroupVersionKind to GroupVersionResource.
func NewForMapper(client rest.Interface, mapper meta.RESTMapper) CustomMetricsClient {
	return &customMetricsClient{
		client: client,
		mapper: mapper,
	}
}

func (c *customMetricsClient) RootScopedMetrics() MetricsInterface {
	return &rootScopedMetrics{c}
}

func (c *customMetricsClient) NamespacedMetrics(namespace string) MetricsInterface {
	return &namespacedMetrics{
		client:    c,
		namespace: namespace,
	}
}

func (c *customMetricsClient) qualResourceForKind(groupKind schema.GroupKind) (string, error) {
	if c.mapper == nil {
		// the version doesn't matter
		gvk := groupKind.WithVersion("")
		gvr, _ := meta.UnsafeGuessKindToResource(gvk)
		gr := gvr.GroupResource()
		return gr.String(), nil
	}

	// use the mapper if it's available
	mapping, err := c.mapper.RESTMapping(groupKind)
	if err != nil {
		return "", fmt.Errorf("unable to map kind %s to resource: %v", groupKind.String(), err)
	}

	groupResource := schema.GroupResource{
		Group:    mapping.GroupVersionKind.Group,
		Resource: mapping.Resource,
	}
	return groupResource.String(), nil
}

type rootScopedMetrics struct {
	client *customMetricsClient
}

func (m *rootScopedMetrics) getForNamespace(namespace string, metricName string) (*v1beta1.MetricValue, error) {
	res := &v1beta1.MetricValueList{}
	err := m.client.client.Get().
		Resource("metrics").
		Namespace(namespace).
		Name(metricName).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	if len(res.Items) != 1 {
		return nil, fmt.Errorf("the custom metrics API server returned %v results when we asked for exactly one", len(res.Items))
	}

	return &res.Items[0], nil
}

func (m *rootScopedMetrics) GetForObject(groupKind schema.GroupKind, name string, metricName string) (*v1beta1.MetricValue, error) {
	// handle namespace separately
	if groupKind.Kind == "Namespace" && groupKind.Group == "" {
		return m.getForNamespace(name, metricName)
	}

	resourceName, err := m.client.qualResourceForKind(groupKind)
	if err != nil {
		return nil, err
	}

	res := &v1beta1.MetricValueList{}
	err = m.client.client.Get().
		Resource(resourceName).
		Name(name).
		SubResource(metricName).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	if len(res.Items) != 1 {
		return nil, fmt.Errorf("the custom metrics API server returned %v results when we asked for exactly one", len(res.Items))
	}

	return &res.Items[0], nil
}

func (m *rootScopedMetrics) GetForObjects(groupKind schema.GroupKind, selector labels.Selector, metricName string) (*v1beta1.MetricValueList, error) {
	// we can't wildcard-fetch for namespaces
	if groupKind.Kind == "Namespace" && groupKind.Group == "" {
		return nil, fmt.Errorf("cannot fetch metrics for multiple namespaces at once")
	}

	resourceName, err := m.client.qualResourceForKind(groupKind)
	if err != nil {
		return nil, err
	}

	res := &v1beta1.MetricValueList{}
	err = m.client.client.Get().
		Resource(resourceName).
		Name(v1beta1.AllObjects).
		SubResource(metricName).
		VersionedParams(&metav1.ListOptions{
			LabelSelector: selector.String(),
		}, metav1.ParameterCodec).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	return res, nil
}

type namespacedMetrics struct {
	client    *customMetricsClient
	namespace string
}

func (m *namespacedMetrics) GetForObject(groupKind schema.GroupKind, name string, metricName string) (*v1beta1.MetricValue, error) {
	resourceName, err := m.client.qualResourceForKind(groupKind)
	if err != nil {
		return nil, err
	}

	res := &v1beta1.MetricValueList{}
	err = m.client.client.Get().
		Resource(resourceName).
		Namespace(m.namespace).
		Name(name).
		SubResource(metricName).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	if len(res.Items) != 1 {
		return nil, fmt.Errorf("the custom metrics API server returned %v results when we asked for exactly one", len(res.Items))
	}

	return &res.Items[0], nil
}

func (m *namespacedMetrics) GetForObjects(groupKind schema.GroupKind, selector labels.Selector, metricName string) (*v1beta1.MetricValueList, error) {
	resourceName, err := m.client.qualResourceForKind(groupKind)
	if err != nil {
		return nil, err
	}

	res := &v1beta1.MetricValueList{}
	err = m.client.client.Get().
		Resource(resourceName).
		Namespace(m.namespace).
		Name(v1beta1.AllObjects).
		SubResource(metricName).
		VersionedParams(&metav1.ListOptions{
			LabelSelector: selector.String(),
		}, metav1.ParameterCodec).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	return res, nil
}
<commit_msg>update restmapping to indicate fully qualified resource<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package custom_metrics

import (
	"fmt"

	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime/schema"
	serializer "k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/util/flowcontrol"
	"k8s.io/metrics/pkg/apis/custom_metrics/v1beta1"
)

type customMetricsClient struct {
	client rest.Interface
	mapper meta.RESTMapper
}

func New(client rest.Interface) CustomMetricsClient {
	return &customMetricsClient{
		client: client,
	}
}

func NewForConfig(c *rest.Config) (CustomMetricsClient, error) {
	configShallowCopy := *c
	if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
		configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
	}
	configShallowCopy.APIPath = "/apis"
	if configShallowCopy.UserAgent == "" {
		configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent()
	}
	configShallowCopy.GroupVersion = &v1beta1.SchemeGroupVersion
	configShallowCopy.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}

	client, err := rest.RESTClientFor(&configShallowCopy)
	if err != nil {
		return nil, err
	}

	return New(client), nil
}

func NewForConfigOrDie(c *rest.Config) CustomMetricsClient {
	client, err := NewForConfig(c)
	if err != nil {
		panic(err)
	}
	return client
}

// NewForMapper constructs the client with a RESTMapper, which allows more
// accurate translation from GroupVersionKind to GroupVersionResource.
func NewForMapper(client rest.Interface, mapper meta.RESTMapper) CustomMetricsClient {
	return &customMetricsClient{
		client: client,
		mapper: mapper,
	}
}

func (c *customMetricsClient) RootScopedMetrics() MetricsInterface {
	return &rootScopedMetrics{c}
}

func (c *customMetricsClient) NamespacedMetrics(namespace string) MetricsInterface {
	return &namespacedMetrics{
		client:    c,
		namespace: namespace,
	}
}

func (c *customMetricsClient) qualResourceForKind(groupKind schema.GroupKind) (string, error) {
	if c.mapper == nil {
		// the version doesn't matter
		gvk := groupKind.WithVersion("")
		gvr, _ := meta.UnsafeGuessKindToResource(gvk)
		gr := gvr.GroupResource()
		return gr.String(), nil
	}

	// use the mapper if it's available
	mapping, err := c.mapper.RESTMapping(groupKind)
	if err != nil {
		return "", fmt.Errorf("unable to map kind %s to resource: %v", groupKind.String(), err)
	}

	gr := mapping.Resource.GroupResource()
	return gr.String(), nil
}

type rootScopedMetrics struct {
	client *customMetricsClient
}

func (m *rootScopedMetrics) getForNamespace(namespace string, metricName string) (*v1beta1.MetricValue, error) {
	res := &v1beta1.MetricValueList{}
	err := m.client.client.Get().
		Resource("metrics").
		Namespace(namespace).
		Name(metricName).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	if len(res.Items) != 1 {
		return nil, fmt.Errorf("the custom metrics API server returned %v results when we asked for exactly one", len(res.Items))
	}

	return &res.Items[0], nil
}

func (m *rootScopedMetrics) GetForObject(groupKind schema.GroupKind, name string, metricName string) (*v1beta1.MetricValue, error) {
	// handle namespace separately
	if groupKind.Kind == "Namespace" && groupKind.Group == "" {
		return m.getForNamespace(name, metricName)
	}

	resourceName, err := m.client.qualResourceForKind(groupKind)
	if err != nil {
		return nil, err
	}

	res := &v1beta1.MetricValueList{}
	err = m.client.client.Get().
		Resource(resourceName).
		Name(name).
		SubResource(metricName).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	if len(res.Items) != 1 {
		return nil, fmt.Errorf("the custom metrics API server returned %v results when we asked for exactly one", len(res.Items))
	}

	return &res.Items[0], nil
}

func (m *rootScopedMetrics) GetForObjects(groupKind schema.GroupKind, selector labels.Selector, metricName string) (*v1beta1.MetricValueList, error) {
	// we can't wildcard-fetch for namespaces
	if groupKind.Kind == "Namespace" && groupKind.Group == "" {
		return nil, fmt.Errorf("cannot fetch metrics for multiple namespaces at once")
	}

	resourceName, err := m.client.qualResourceForKind(groupKind)
	if err != nil {
		return nil, err
	}

	res := &v1beta1.MetricValueList{}
	err = m.client.client.Get().
		Resource(resourceName).
		Name(v1beta1.AllObjects).
		SubResource(metricName).
		VersionedParams(&metav1.ListOptions{
			LabelSelector: selector.String(),
		}, metav1.ParameterCodec).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	return res, nil
}

type namespacedMetrics struct {
	client    *customMetricsClient
	namespace string
}

func (m *namespacedMetrics) GetForObject(groupKind schema.GroupKind, name string, metricName string) (*v1beta1.MetricValue, error) {
	resourceName, err := m.client.qualResourceForKind(groupKind)
	if err != nil {
		return nil, err
	}

	res := &v1beta1.MetricValueList{}
	err = m.client.client.Get().
		Resource(resourceName).
		Namespace(m.namespace).
		Name(name).
		SubResource(metricName).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	if len(res.Items) != 1 {
		return nil, fmt.Errorf("the custom metrics API server returned %v results when we asked for exactly one", len(res.Items))
	}

	return &res.Items[0], nil
}

func (m *namespacedMetrics) GetForObjects(groupKind schema.GroupKind, selector labels.Selector, metricName string) (*v1beta1.MetricValueList, error) {
	resourceName, err := m.client.qualResourceForKind(groupKind)
	if err != nil {
		return nil, err
	}

	res := &v1beta1.MetricValueList{}
	err = m.client.client.Get().
		Resource(resourceName).
		Namespace(m.namespace).
		Name(v1beta1.AllObjects).
		SubResource(metricName).
		VersionedParams(&metav1.ListOptions{
			LabelSelector: selector.String(),
		}, metav1.ParameterCodec).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	return res, nil
}
<|endoftext|>