<commit_before>package fake

import (
	"errors"

	"github.com/pmorie/go-open-service-broker-client/v2"
)

// NewFakeClientFunc generates a v2.CreateFunc that returns a FakeClient with
// the given FakeClientConfiguration.  It is useful for injecting the
// FakeClient in code that uses the v2.CreateFunc interface.
func NewFakeClientFunc(config FakeClientConfiguration) v2.CreateFunc {
	return func(_ *v2.ClientConfiguration) (v2.Client, error) {
		return &FakeClient{
			CatalogReaction:           config.CatalogReaction,
			ProvisionReaction:         config.ProvisionReaction,
			UpdateInstanceReaction:    config.UpdateInstanceReaction,
			DeprovisionReaction:       config.DeprovisionReaction,
			PollLastOperationReaction: config.PollLastOperationReaction,
			BindReaction:              config.BindReaction,
			UnbindReaction:            config.UnbindReaction,
		}, nil
	}
}

type FakeClientConfiguration struct {
	CatalogReaction           *CatalogReaction
	ProvisionReaction         *ProvisionReaction
	UpdateInstanceReaction    *UpdateInstanceReaction
	DeprovisionReaction       *DeprovisionReaction
	PollLastOperationReaction *PollLastOperationReaction
	BindReaction              *BindReaction
	UnbindReaction            *UnbindReaction
}

type FakeClient struct {
	CatalogReaction           *CatalogReaction
	ProvisionReaction         *ProvisionReaction
	UpdateInstanceReaction    *UpdateInstanceReaction
	DeprovisionReaction       *DeprovisionReaction
	PollLastOperationReaction *PollLastOperationReaction
	BindReaction              *BindReaction
	UnbindReaction            *UnbindReaction
	actions                   []Action
}

type Action struct {
	Type    ActionType
	Request interface{}
}

type ActionType string

const (
	GetCatalog          ActionType = "GetCatalog"
	ProvisionInstance   ActionType = "ProvisionInstance"
	UpdateInstance      ActionType = "UpdateInstance"
	DeprovisionInstance ActionType = "DeprovisionInstance"
	PollLastOperation   ActionType = "PollLastOperation"
	Bind                ActionType = "Bind"
	Unbind              ActionType = "Unbind"
)

var _ v2.Client = &FakeClient{}

func (c *FakeClient) Actions() []Action {
	return c.actions
}

func (c *FakeClient) GetCatalog() (*v2.CatalogResponse, error) {
	c.actions = append(c.actions, Action{Type: GetCatalog})

	if c.CatalogReaction != nil {
		return c.CatalogReaction.Response, c.CatalogReaction.Error
	}

	return nil, UnexpectedActionError()
}

func (c *FakeClient) ProvisionInstance(r *v2.ProvisionRequest) (*v2.ProvisionResponse, error) {
	c.actions = append(c.actions, Action{ProvisionInstance, r})

	if c.ProvisionReaction != nil {
		return c.ProvisionReaction.Response, c.ProvisionReaction.Error
	}

	return nil, UnexpectedActionError()
}

func (c *FakeClient) UpdateInstance(r *v2.UpdateInstanceRequest) (*v2.UpdateInstanceResponse, error) {
	c.actions = append(c.actions, Action{UpdateInstance, r})

	if c.UpdateInstanceReaction != nil {
		return c.UpdateInstanceReaction.Response, c.UpdateInstanceReaction.Error
	}

	return nil, UnexpectedActionError()
}

func (c *FakeClient) DeprovisionInstance(r *v2.DeprovisionRequest) (*v2.DeprovisionResponse, error) {
	c.actions = append(c.actions, Action{DeprovisionInstance, r})

	if c.DeprovisionReaction != nil {
		return c.DeprovisionReaction.Response, c.DeprovisionReaction.Error
	}

	return nil, UnexpectedActionError()
}

func (c *FakeClient) PollLastOperation(r *v2.LastOperationRequest) (*v2.LastOperationResponse, error) {
	c.actions = append(c.actions, Action{PollLastOperation, r})

	if c.PollLastOperationReaction != nil {
		return c.PollLastOperationReaction.Response, c.PollLastOperationReaction.Error
	}

	return nil, UnexpectedActionError()
}

func (c *FakeClient) Bind(r *v2.BindRequest) (*v2.BindResponse, error) {
	c.actions = append(c.actions, Action{Bind, r})

	if c.BindReaction != nil {
		return c.BindReaction.Response, c.BindReaction.Error
	}

	return nil, UnexpectedActionError()
}

func (c *FakeClient) Unbind(r *v2.UnbindRequest) (*v2.UnbindResponse, error) {
	c.actions = append(c.actions, Action{Unbind, r})

	if c.UnbindReaction != nil {
		return c.UnbindReaction.Response, c.UnbindReaction.Error
	}

	return nil, UnexpectedActionError()
}

func UnexpectedActionError() error {
	return errors.New("Unexpected action")
}

type CatalogReaction struct {
	Response *v2.CatalogResponse
	Error    error
}

type ProvisionReaction struct {
	Response *v2.ProvisionResponse
	Error    error
}

type UpdateInstanceReaction struct {
	Response *v2.UpdateInstanceResponse
	Error    error
}

type DeprovisionReaction struct {
	Response *v2.DeprovisionResponse
	Error    error
}

type PollLastOperationReaction struct {
	Response *v2.LastOperationResponse
	Error    error
}

type BindReaction struct {
	Response *v2.BindResponse
	Error    error
}

type UnbindReaction struct {
	Response *v2.UnbindResponse
	Error    error
}
<commit_msg>Add NewFakeClient method (#34)<commit_after>package fake

import (
	"errors"

	"github.com/pmorie/go-open-service-broker-client/v2"
)

// NewFakeClientFunc returns a v2.CreateFunc that returns a FakeClient with
// the given FakeClientConfiguration.  It is useful for injecting the
// FakeClient in code that uses the v2.CreateFunc interface.
func NewFakeClientFunc(config FakeClientConfiguration) v2.CreateFunc {
	return func(_ *v2.ClientConfiguration) (v2.Client, error) {
		return NewFakeClient(config), nil
	}
}

// ReturnFakeClientFunc returns a v2.CreateFunc that returns the given
// FakeClient.
func ReturnFakeClientFunc(c *FakeClient) v2.CreateFunc {
	return func(_ *v2.ClientConfiguration) (v2.Client, error) {
		return c, nil
	}
}

// NewFakeClient returns a new fake Client with the given
// FakeClientConfiguration.
func NewFakeClient(config FakeClientConfiguration) *FakeClient {
	return &FakeClient{
		CatalogReaction:           config.CatalogReaction,
		ProvisionReaction:         config.ProvisionReaction,
		UpdateInstanceReaction:    config.UpdateInstanceReaction,
		DeprovisionReaction:       config.DeprovisionReaction,
		PollLastOperationReaction: config.PollLastOperationReaction,
		BindReaction:              config.BindReaction,
		UnbindReaction:            config.UnbindReaction,
	}
}

// FakeClientConfiguration models the configuration of a FakeClient.
type FakeClientConfiguration struct {
	CatalogReaction           *CatalogReaction
	ProvisionReaction         *ProvisionReaction
	UpdateInstanceReaction    *UpdateInstanceReaction
	DeprovisionReaction       *DeprovisionReaction
	PollLastOperationReaction *PollLastOperationReaction
	BindReaction              *BindReaction
	UnbindReaction            *UnbindReaction
}

// FakeClient is a fake implementation of the v2.Client interface. It records
// the actions that are taken on it and runs the appropriate reaction to those
// actions. If an action for which there is no reaction specified occurs, it
// returns an error.
type FakeClient struct {
	CatalogReaction           *CatalogReaction
	ProvisionReaction         *ProvisionReaction
	UpdateInstanceReaction    *UpdateInstanceReaction
	DeprovisionReaction       *DeprovisionReaction
	PollLastOperationReaction *PollLastOperationReaction
	BindReaction              *BindReaction
	UnbindReaction            *UnbindReaction
	actions                   []Action
}

// Action is a record of a method call on the FakeClient.
type Action struct {
	Type    ActionType
	Request interface{}
}

// ActionType is a typedef over the set of actions that can be taken on a
// FakeClient.
type ActionType string

const (
	GetCatalog          ActionType = "GetCatalog"
	ProvisionInstance   ActionType = "ProvisionInstance"
	UpdateInstance      ActionType = "UpdateInstance"
	DeprovisionInstance ActionType = "DeprovisionInstance"
	PollLastOperation   ActionType = "PollLastOperation"
	Bind                ActionType = "Bind"
	Unbind              ActionType = "Unbind"
)

var _ v2.Client = &FakeClient{}

func (c *FakeClient) Actions() []Action {
	return c.actions
}

func (c *FakeClient) GetCatalog() (*v2.CatalogResponse, error) {
	c.actions = append(c.actions, Action{Type: GetCatalog})

	if c.CatalogReaction != nil {
		return c.CatalogReaction.Response, c.CatalogReaction.Error
	}

	return nil, UnexpectedActionError()
}

func (c *FakeClient) ProvisionInstance(r *v2.ProvisionRequest) (*v2.ProvisionResponse, error) {
	c.actions = append(c.actions, Action{ProvisionInstance, r})

	if c.ProvisionReaction != nil {
		return c.ProvisionReaction.Response, c.ProvisionReaction.Error
	}

	return nil, UnexpectedActionError()
}

func (c *FakeClient) UpdateInstance(r *v2.UpdateInstanceRequest) (*v2.UpdateInstanceResponse, error) {
	c.actions = append(c.actions, Action{UpdateInstance, r})

	if c.UpdateInstanceReaction != nil {
		return c.UpdateInstanceReaction.Response, c.UpdateInstanceReaction.Error
	}

	return nil, UnexpectedActionError()
}

func (c *FakeClient) DeprovisionInstance(r *v2.DeprovisionRequest) (*v2.DeprovisionResponse, error) {
	c.actions = append(c.actions, Action{DeprovisionInstance, r})

	if c.DeprovisionReaction != nil {
		return c.DeprovisionReaction.Response, c.DeprovisionReaction.Error
	}

	return nil, UnexpectedActionError()
}

func (c *FakeClient) PollLastOperation(r *v2.LastOperationRequest) (*v2.LastOperationResponse, error) {
	c.actions = append(c.actions, Action{PollLastOperation, r})

	if c.PollLastOperationReaction != nil {
		return c.PollLastOperationReaction.Response, c.PollLastOperationReaction.Error
	}

	return nil, UnexpectedActionError()
}

func (c *FakeClient) Bind(r *v2.BindRequest) (*v2.BindResponse, error) {
	c.actions = append(c.actions, Action{Bind, r})

	if c.BindReaction != nil {
		return c.BindReaction.Response, c.BindReaction.Error
	}

	return nil, UnexpectedActionError()
}

func (c *FakeClient) Unbind(r *v2.UnbindRequest) (*v2.UnbindResponse, error) {
	c.actions = append(c.actions, Action{Unbind, r})

	if c.UnbindReaction != nil {
		return c.UnbindReaction.Response, c.UnbindReaction.Error
	}

	return nil, UnexpectedActionError()
}

func UnexpectedActionError() error {
	return errors.New("Unexpected action")
}

type CatalogReaction struct {
	Response *v2.CatalogResponse
	Error    error
}

type ProvisionReaction struct {
	Response *v2.ProvisionResponse
	Error    error
}

type UpdateInstanceReaction struct {
	Response *v2.UpdateInstanceResponse
	Error    error
}

type DeprovisionReaction struct {
	Response *v2.DeprovisionResponse
	Error    error
}

type PollLastOperationReaction struct {
	Response *v2.LastOperationResponse
	Error    error
}

type BindReaction struct {
	Response *v2.BindResponse
	Error    error
}

type UnbindReaction struct {
	Response *v2.UnbindResponse
	Error    error
}
<|endoftext|>


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

package util

import (
	"github.com/timob/javabind"
	"github.com/timob/swt"
)

type UIRunner struct {
	uiFuncChan chan func()
	uiDone     chan byte
	cbFuncChan chan func()
	jniThread  *javabind.AttachedThread
	display    *swt.WidgetsDisplay
	awake		bool
	check		*javabind.CheckEnv
}

func NewUIRunner(d *swt.WidgetsDisplay) *UIRunner {
	return &UIRunner{make(chan func(), 10), make(chan byte), make(chan func(), 1000), javabind.NewAttachedThread(), d, false, nil}
}

func (u *UIRunner) Run(f func()) {
	if u.check != nil && u.check.SameEnv() {
		f()
	} else {
		u.uiFuncChan <- f
		u.jniThread.Run(u.display.Wake)
		<-u.uiDone
	}
}

func (u *UIRunner) ExecUiFuncs() {
	if u.check == nil {
		u.check = javabind.NewCheckEnv()
	}
	select {
	case f := <-u.uiFuncChan:
		f()
		u.uiDone <- 1
	case f := <-u.cbFuncChan:
		f()
	A:
		for {
			select {
			case f := <-u.cbFuncChan:
				f()
			default:
				break A
			}
		}
	default:
	}
}

func (u *UIRunner) CallBackRun(f func()) {
	u.cbFuncChan <- f
}
<commit_msg>improve util.uirunner<commit_after>// Copyright 2015 Tim O'Brien. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package util

import (
	"github.com/timob/javabind"
	"github.com/timob/swt"
)

type UIRunner struct {
	uiFuncChan chan func()
	uiDone     chan byte
	cbFuncChan chan func()
	jniThread  *javabind.AttachedThread
	display    *swt.WidgetsDisplay
	awake		bool
	check		*javabind.CheckEnv
}

func NewUIRunner(d *swt.WidgetsDisplay) *UIRunner {
	return &UIRunner{make(chan func(), 10), make(chan byte), make(chan func(), 1000), javabind.NewAttachedThread(), d, false, nil}
}

func (u *UIRunner) Run(f func()) {
	if u.check != nil && u.check.SameEnv() {
		f()
	} else {
		u.uiFuncChan <- f
		u.jniThread.Run(u.display.Wake)
		<-u.uiDone
	}
}

func (u *UIRunner) ExecUiFuncs() {
	if u.check == nil {
		u.check = javabind.NewCheckEnv()
	}
	for {
		select {
		case f := <-u.uiFuncChan:
			f()
			u.uiDone <- 1
		default:
			goto finish
		}
	}
	finish:
}
<|endoftext|>


<commit_before>package etcd

import (
	"context"
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/coreos/etcd/client"
)

type Decoder interface {
	Decode(string, interface{}) error
	DecodeWithContext(string, interface{}, context.Context) error
	SkipMissing(bool)
}

type decoder struct {
	client client.KeysAPI
}

func NewDecoder(client client.KeysAPI) Decoder {
	return &decoder{
		client: client,
	}
}

// Deprecated, use ",omitempty" tag
func (d *decoder) SkipMissing(skip bool) {
	d.skipMissing = skip
}

func (d *decoder) Decode(path string, v interface{}) error {
	return d.DecodeWithContext(path, v, context.Background())
}

func (d *decoder) DecodeWithContext(path string, v interface{}, ctx context.Context) error {
	value := reflect.ValueOf(v)
	if value.Kind() != reflect.Ptr {
		return errors.New("destination has to be a pointer")
	}

	return d.decode(path, value.Elem(), ctx)
}

func (d *decoder) decode(path string, value reflect.Value, ctx context.Context) error {
	if value.Kind() == reflect.Ptr {
		value = value.Elem()
	}

	switch value.Kind() {
	case reflect.Func:
		return errors.New("can't decode func: not implemented")

	case reflect.Chan:
		return errors.New("can't decode channel: not implemented")

	case reflect.Interface:
		v := reflect.New(value.Elem().Type()).Elem()
		d.decode(path, v, ctx)
		value.Set(v)

	case reflect.Struct:
		if err := d.decodeStruct(path, value, ctx); err != nil {
			return err
		}

	case reflect.Map:
		if err := d.decodeMap(path, value, ctx); err != nil {
			return err
		}

	case reflect.Slice, reflect.Array:
		if err := d.decodeSlice(path, value, ctx); err != nil {
			return err
		}

	default:
		node, err := d.getNode(path, ctx)
		if node == nil {
			return err
		}

		return decodePrimitive(node.Value, value)
	}

	return nil
}

func (d *decoder) decodeSlice(path string, value reflect.Value, ctx context.Context) error {
	node, err := d.getNode(path, ctx)
	if node == nil {
		return err
	}

	if !node.Dir {
		return errors.New(fmt.Sprintf("%s is not a dir", path))
	}

	if value.IsNil() {
		value.Set(reflect.MakeSlice(value.Type(), value.Len(), value.Cap()))
	}

	for _, node := range node.Nodes {
		sliceValue := reflect.New(value.Type().Elem()).Elem()
		if err := d.decode(node.Key, sliceValue, ctx); err != nil {
			return err
		}
		value.Set(reflect.Append(value, sliceValue))
	}

	return nil
}

func (d *decoder) decodeMap(path string, value reflect.Value, ctx context.Context) error {
	node, err := d.getNode(path, ctx)
	if node == nil {
		return err
	}

	if !node.Dir {
		return errors.New(fmt.Sprintf("%s is not a dir", path))
	}

	if value.IsNil() {
		value.Set(reflect.MakeMap(value.Type()))
	}

	for _, node := range node.Nodes {
		mapValue := reflect.New(value.Type().Elem()).Elem()
		if err := d.decode(node.Key, mapValue, ctx); err != nil {
			return err
		}

		mapKey := reflect.New(value.Type().Key()).Elem()
		p := strings.Split(node.Key, "/")
		mapKey.SetString(p[len(p)-1])
		value.SetMapIndex(mapKey, mapValue)
	}

	return nil
}

func (d *decoder) decodeStruct(path string, value reflect.Value, ctx context.Context) error {
	for i := 0; i < value.NumField(); i++ {
		typeField := value.Type().Field(i)
		name := typeField.Name
		tag := typeField.Tag.Get("etcd")
		if tag != "-" {
			params := strings.Split(tag, ",")
			if len(params) > 0 && params[0] != "" {
				name = params[0]
			}
			if err := d.decode(fmt.Sprintf("%s/%s", path, name), value.Field(i), ctx); err != nil {
				if !canOmitEmpty(err, params) {
					return err
				}
			}
		}
	}

	return nil
}

func (d *decoder) getNode(path string, ctx context.Context) (*client.Node, error) {
	op, ok := ctx.Value("options").(*client.GetOptions)
	if !ok {
		op = &client.GetOptions{}
	}

	r, err := d.client.Get(ctx, path, op)
	if err != nil {
		return nil, err
	}
	return r.Node, nil
}

func decodePrimitive(nodeValue string, value reflect.Value) error {
	switch value.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		switch value.Interface().(type) {
		case time.Duration:
			v, err := time.ParseDuration(nodeValue)
			if err != nil {
				return err
			}
			value.SetInt(int64(v))

		default:
			v, err := strconv.ParseInt(nodeValue, 10, 64)
			if err != nil {
				return err
			}
			value.SetInt(v)
		}

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		v, err := strconv.ParseUint(nodeValue, 10, 64)
		if err != nil {
			return err
		}
		value.SetUint(v)

	case reflect.Float32, reflect.Float64:
		v, err := strconv.ParseFloat(nodeValue, 64)
		if err != nil {
			return err
		}
		value.SetFloat(v)

	case reflect.Bool:
		v, err := strconv.ParseBool(nodeValue)
		if err != nil {
			return err
		}
		value.SetBool(v)
	case reflect.String:
		value.SetString(nodeValue)
	default:
		return errors.New(fmt.Sprintf("can't decode value of type %s", value.Type()))
	}

	return nil
}

func canOmitEmpty(err error, params []string) bool {
	if len(params) < 2 {
		return false
	}

	if e, ok := err.(client.Error); ok && params[1] == "omitempty" && e.Code == client.ErrorCodeKeyNotFound {
		return true
	}

	return false
}
<commit_msg>Temporarily return SkipMissing method, but log it with deprecated message<commit_after>package etcd

import (
	"context"
	"errors"
	"fmt"
	"log"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/coreos/etcd/client"
)

type Decoder interface {
	Decode(string, interface{}) error
	DecodeWithContext(string, interface{}, context.Context) error
	SkipMissing(bool)
}

type decoder struct {
	client      client.KeysAPI
	skipMissing bool
}

func NewDecoder(client client.KeysAPI) Decoder {
	return &decoder{
		client: client,
	}
}

// Deprecated, use ",omitempty" tag
func (d *decoder) SkipMissing(skip bool) {
	log.Println("SkipMissing method is deprecated, use \",omitempty\" tag instead")
	d.skipMissing = skip
}

func (d *decoder) Decode(path string, v interface{}) error {
	return d.DecodeWithContext(path, v, context.Background())
}

func (d *decoder) DecodeWithContext(path string, v interface{}, ctx context.Context) error {
	value := reflect.ValueOf(v)
	if value.Kind() != reflect.Ptr {
		return errors.New("destination has to be a pointer")
	}

	return d.decode(path, value.Elem(), ctx)
}

func (d *decoder) decode(path string, value reflect.Value, ctx context.Context) error {
	if value.Kind() == reflect.Ptr {
		value = value.Elem()
	}

	switch value.Kind() {
	case reflect.Func:
		return errors.New("can't decode func: not implemented")

	case reflect.Chan:
		return errors.New("can't decode channel: not implemented")

	case reflect.Interface:
		v := reflect.New(value.Elem().Type()).Elem()
		d.decode(path, v, ctx)
		value.Set(v)

	case reflect.Struct:
		if err := d.decodeStruct(path, value, ctx); err != nil {
			return err
		}

	case reflect.Map:
		if err := d.decodeMap(path, value, ctx); err != nil {
			return err
		}

	case reflect.Slice, reflect.Array:
		if err := d.decodeSlice(path, value, ctx); err != nil {
			return err
		}

	default:
		node, err := d.getNode(path, ctx)
		if node == nil {
			return err
		}

		return decodePrimitive(node.Value, value)
	}

	return nil
}

func (d *decoder) decodeSlice(path string, value reflect.Value, ctx context.Context) error {
	node, err := d.getNode(path, ctx)
	if node == nil {
		return err
	}

	if !node.Dir {
		return errors.New(fmt.Sprintf("%s is not a dir", path))
	}

	if value.IsNil() {
		value.Set(reflect.MakeSlice(value.Type(), value.Len(), value.Cap()))
	}

	for _, node := range node.Nodes {
		sliceValue := reflect.New(value.Type().Elem()).Elem()
		if err := d.decode(node.Key, sliceValue, ctx); err != nil {
			return err
		}
		value.Set(reflect.Append(value, sliceValue))
	}

	return nil
}

func (d *decoder) decodeMap(path string, value reflect.Value, ctx context.Context) error {
	node, err := d.getNode(path, ctx)
	if node == nil {
		return err
	}

	if !node.Dir {
		return errors.New(fmt.Sprintf("%s is not a dir", path))
	}

	if value.IsNil() {
		value.Set(reflect.MakeMap(value.Type()))
	}

	for _, node := range node.Nodes {
		mapValue := reflect.New(value.Type().Elem()).Elem()
		if err := d.decode(node.Key, mapValue, ctx); err != nil {
			return err
		}

		mapKey := reflect.New(value.Type().Key()).Elem()
		p := strings.Split(node.Key, "/")
		mapKey.SetString(p[len(p)-1])
		value.SetMapIndex(mapKey, mapValue)
	}

	return nil
}

func (d *decoder) decodeStruct(path string, value reflect.Value, ctx context.Context) error {
	for i := 0; i < value.NumField(); i++ {
		typeField := value.Type().Field(i)
		name := typeField.Name
		tag := typeField.Tag.Get("etcd")
		if tag != "-" {
			params := strings.Split(tag, ",")
			if len(params) > 0 && params[0] != "" {
				name = params[0]
			}
			if err := d.decode(fmt.Sprintf("%s/%s", path, name), value.Field(i), ctx); err != nil {
				if !canOmitEmpty(err, params) {
					return err
				}
			}
		}
	}

	return nil
}

func (d *decoder) getNode(path string, ctx context.Context) (*client.Node, error) {
	op, ok := ctx.Value("options").(*client.GetOptions)
	if !ok {
		op = &client.GetOptions{}
	}

	r, err := d.client.Get(ctx, path, op)
	if err != nil {
		if canSkipMissing(err, d.skipMissing) {
			return nil, nil
		}
		return nil, err
	}
	return r.Node, nil
}

func decodePrimitive(nodeValue string, value reflect.Value) error {
	switch value.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		switch value.Interface().(type) {
		case time.Duration:
			v, err := time.ParseDuration(nodeValue)
			if err != nil {
				return err
			}
			value.SetInt(int64(v))

		default:
			v, err := strconv.ParseInt(nodeValue, 10, 64)
			if err != nil {
				return err
			}
			value.SetInt(v)
		}

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		v, err := strconv.ParseUint(nodeValue, 10, 64)
		if err != nil {
			return err
		}
		value.SetUint(v)

	case reflect.Float32, reflect.Float64:
		v, err := strconv.ParseFloat(nodeValue, 64)
		if err != nil {
			return err
		}
		value.SetFloat(v)

	case reflect.Bool:
		v, err := strconv.ParseBool(nodeValue)
		if err != nil {
			return err
		}
		value.SetBool(v)
	case reflect.String:
		value.SetString(nodeValue)
	default:
		return errors.New(fmt.Sprintf("can't decode value of type %s", value.Type()))
	}

	return nil
}

func canOmitEmpty(err error, params []string) bool {
	if len(params) < 2 {
		return false
	}

	if e, ok := err.(client.Error); ok && params[1] == "omitempty" && e.Code == client.ErrorCodeKeyNotFound {
		return true
	}

	return false
}

func canSkipMissing(err error, skipFlag bool) bool {
	if e, ok := err.(client.Error); ok && skipFlag && e.Code == client.ErrorCodeKeyNotFound {
		return true
	}

	return false
}
<|endoftext|>


<commit_before>package ethpub

import (
	"encoding/hex"
	"github.com/ethereum/eth-go/ethchain"
	"github.com/ethereum/eth-go/ethutil"
)

// Block interface exposed to QML
type PBlock struct {
	Number int    `json:"number"`
	Hash   string `json:"hash"`
}

// Creates a new QML Block from a chain block
func NewPBlock(block *ethchain.Block) *PBlock {
	info := block.BlockInfo()
	hash := hex.EncodeToString(block.Hash())

	return &PBlock{Number: int(info.Number), Hash: hash}
}

type PTx struct {
	Value, Hash, Address string
	Contract             bool
}

func NewPTx(tx *ethchain.Transaction) *PTx {
	hash := hex.EncodeToString(tx.Hash())
	sender := hex.EncodeToString(tx.Recipient)
	isContract := len(tx.Data) > 0

	return &PTx{Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: sender, Contract: isContract}
}

type PKey struct {
	Address    string
	PrivateKey string
	PublicKey  string
}

func NewPKey(key *ethchain.KeyPair) *PKey {
	return &PKey{ethutil.Hex(key.Address()), ethutil.Hex(key.PrivateKey), ethutil.Hex(key.PublicKey)}
}

type PReceipt struct {
	CreatedContract bool   `json:"createdContract"`
	Address         string `json:"address"`
	Hash            string `json:"hash"`
	Sender          string `json:"sender"`
}

func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) *PReceipt {
	return &PReceipt{
		contractCreation,
		ethutil.Hex(creationAddress),
		ethutil.Hex(hash),
		ethutil.Hex(address),
	}
}

/*
type PKeyRing struct {
	Keys []interface{}
}

func NewPKeyRing(keys []interface{}) *PKeyRing {
	return &PKeyRing{Keys: keys}
}
*/

type PStateObject struct {
	object *ethchain.StateObject
}

func NewPStateObject(object *ethchain.StateObject) *PStateObject {
	return &PStateObject{object: object}
}

func (c *PStateObject) GetStorage(address string) string {
	// Because somehow, even if you return nil to QML it
	// still has some magical object so we can't rely on
	// undefined or null at the QML side
	if c.object != nil {
		val := c.object.GetMem(ethutil.Big("0x" + address))

		return val.BigInt().String()
	}

	return ""
}

func (c *PStateObject) Value() string {
	if c.object != nil {
		return c.object.Amount.String()
	}

	return ""
}

func (c *PStateObject) Address() string {
	if c.object != nil {
		return ethutil.Hex(c.object.Address())
	}

	return ""
}
<commit_msg>Added more JSON niceties to types<commit_after>package ethpub

import (
	"encoding/hex"
	"github.com/ethereum/eth-go/ethchain"
	"github.com/ethereum/eth-go/ethutil"
)

// Block interface exposed to QML
type PBlock struct {
	Number int    `json:"number"`
	Hash   string `json:"hash"`
}

// Creates a new QML Block from a chain block
func NewPBlock(block *ethchain.Block) *PBlock {
	info := block.BlockInfo()
	hash := hex.EncodeToString(block.Hash())

	return &PBlock{Number: int(info.Number), Hash: hash}
}

type PTx struct {
	Value, Hash, Address string
	Contract             bool
}

func NewPTx(tx *ethchain.Transaction) *PTx {
	hash := hex.EncodeToString(tx.Hash())
	sender := hex.EncodeToString(tx.Recipient)
	isContract := len(tx.Data) > 0

	return &PTx{Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: sender, Contract: isContract}
}

type PKey struct {
	Address    string `json:"address"`
	PrivateKey string `json:"privateKey"`
	PublicKey  string `json:"publicKey"`
}

func NewPKey(key *ethchain.KeyPair) *PKey {
	return &PKey{ethutil.Hex(key.Address()), ethutil.Hex(key.PrivateKey), ethutil.Hex(key.PublicKey)}
}

type PReceipt struct {
	CreatedContract bool   `json:"createdContract"`
	Address         string `json:"address"`
	Hash            string `json:"hash"`
	Sender          string `json:"sender"`
}

func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) *PReceipt {
	return &PReceipt{
		contractCreation,
		ethutil.Hex(creationAddress),
		ethutil.Hex(hash),
		ethutil.Hex(address),
	}
}

/*
type PKeyRing struct {
	Keys []interface{}
}

func NewPKeyRing(keys []interface{}) *PKeyRing {
	return &PKeyRing{Keys: keys}
}
*/

type PStateObject struct {
	object *ethchain.StateObject
}

func NewPStateObject(object *ethchain.StateObject) *PStateObject {
	return &PStateObject{object: object}
}

func (c *PStateObject) GetStorage(address string) string {
	// Because somehow, even if you return nil to QML it
	// still has some magical object so we can't rely on
	// undefined or null at the QML side
	if c.object != nil {
		val := c.object.GetMem(ethutil.Big("0x" + address))

		return val.BigInt().String()
	}

	return ""
}

func (c *PStateObject) Value() string {
	if c.object != nil {
		return c.object.Amount.String()
	}

	return ""
}

func (c *PStateObject) Address() string {
	if c.object != nil {
		return ethutil.Hex(c.object.Address())
	}

	return ""
}
<|endoftext|>


<commit_before>package ethpub

import (
	"encoding/json"
	"fmt"
	"github.com/ethereum/eth-go/ethchain"
	"github.com/ethereum/eth-go/ethcrypto"
	"github.com/ethereum/eth-go/ethutil"
	"strings"
)

// Peer interface exposed to QML

type PPeer struct {
	ref          *ethchain.Peer
	Inbound      bool   `json:"isInbound"`
	LastSend     int64  `json:"lastSend"`
	LastPong     int64  `json:"lastPong"`
	Ip           string `json:"ip"`
	Port         int    `json:"port"`
	Version      string `json:"version"`
	LastResponse string `json:"lastResponse"`
	Latency      string `json:"latency"`
}

func NewPPeer(peer ethchain.Peer) *PPeer {
	if peer == nil {
		return nil
	}

	// TODO: There must be something build in to do this?
	var ip []string
	for _, i := range peer.Host() {
		ip = append(ip, fmt.Sprintf("%d", i))
	}
	ipAddress := strings.Join(ip, ".")

	return &PPeer{ref: &peer, Inbound: peer.Inbound(), LastSend: peer.LastSend().Unix(), LastPong: peer.LastPong(), Version: peer.Version(), Ip: ipAddress, Port: int(peer.Port()), Latency: peer.PingTime()}
}

// Block interface exposed to QML
type PBlock struct {
	ref          *ethchain.Block
	Number       int    `json:"number"`
	Hash         string `json:"hash"`
	Transactions string `json:"transactions"`
	Time         int64  `json:"time"`
	Coinbase     string `json:"coinbase"`
	GasLimit     string `json:"gasLimit"`
	GasUsed      string `json:"gasUsed"`
}

// Creates a new QML Block from a chain block
func NewPBlock(block *ethchain.Block) *PBlock {
	if block == nil {
		return nil
	}

	var ptxs []PTx
	for _, tx := range block.Transactions() {
		ptxs = append(ptxs, *NewPTx(tx))
	}

	txJson, err := json.Marshal(ptxs)
	if err != nil {
		return nil
	}

	return &PBlock{ref: block, Number: int(block.Number.Uint64()), GasUsed: block.GasUsed.String(), GasLimit: block.GasLimit.String(), Hash: ethutil.Bytes2Hex(block.Hash()), Transactions: string(txJson), Time: block.Time, Coinbase: ethutil.Bytes2Hex(block.Coinbase)}
}

func (self *PBlock) ToString() string {
	if self.ref != nil {
		return self.ref.String()
	}

	return ""
}

func (self *PBlock) GetTransaction(hash string) *PTx {
	tx := self.ref.GetTransaction(ethutil.Hex2Bytes(hash))
	if tx == nil {
		return nil
	}

	return NewPTx(tx)
}

type PTx struct {
	ref *ethchain.Transaction

	Value           string `json:"value"`
	Gas             string `json:"gas"`
	GasPrice        string `json:"gasPrice"`
	Hash            string `json:"hash"`
	Address         string `json:"address"`
	Sender          string `json:"sender"`
	RawData         string `json:"rawData"`
	Data            string `json:"data"`
	Contract        bool   `json:"isContract"`
	CreatesContract bool   `json:"createsContract"`
	Confirmations   int    `json:"confirmations"`
}

func NewPTx(tx *ethchain.Transaction) *PTx {
	hash := ethutil.Bytes2Hex(tx.Hash())
	receiver := ethutil.Bytes2Hex(tx.Recipient)
	if receiver == "0000000000000000000000000000000000000000" {
		receiver = ethutil.Bytes2Hex(tx.CreationAddress())
	}
	sender := ethutil.Bytes2Hex(tx.Sender())
	createsContract := tx.CreatesContract()

	var data string
	if tx.CreatesContract() {
		data = strings.Join(ethchain.Disassemble(tx.Data), "\n")
	} else {
		data = ethutil.Bytes2Hex(tx.Data)
	}

	return &PTx{ref: tx, Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: receiver, Contract: tx.CreatesContract(), Gas: tx.Gas.String(), GasPrice: tx.GasPrice.String(), Data: data, Sender: sender, CreatesContract: createsContract, RawData: ethutil.Bytes2Hex(tx.Data)}
}

func (self *PTx) ToString() string {
	return self.ref.String()
}

type PKey struct {
	Address    string `json:"address"`
	PrivateKey string `json:"privateKey"`
	PublicKey  string `json:"publicKey"`
}

func NewPKey(key *ethcrypto.KeyPair) *PKey {
	return &PKey{ethutil.Bytes2Hex(key.Address()), ethutil.Bytes2Hex(key.PrivateKey), ethutil.Bytes2Hex(key.PublicKey)}
}

type PReceipt struct {
	CreatedContract bool   `json:"createdContract"`
	Address         string `json:"address"`
	Hash            string `json:"hash"`
	Sender          string `json:"sender"`
}

func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) *PReceipt {
	return &PReceipt{
		contractCreation,
		ethutil.Bytes2Hex(creationAddress),
		ethutil.Bytes2Hex(hash),
		ethutil.Bytes2Hex(address),
	}
}

type PStateObject struct {
	object *ethchain.StateObject
}

func NewPStateObject(object *ethchain.StateObject) *PStateObject {
	return &PStateObject{object: object}
}

func (c *PStateObject) GetStorage(address string) string {
	// Because somehow, even if you return nil to QML it
	// still has some magical object so we can't rely on
	// undefined or null at the QML side
	if c.object != nil {
		val := c.object.GetStorage(ethutil.Big("0x" + address))

		return val.BigInt().String()
	}

	return ""
}

func (c *PStateObject) Value() string {
	if c.object != nil {
		return c.object.Amount.String()
	}

	return ""
}

func (c *PStateObject) Address() string {
	if c.object != nil {
		return ethutil.Bytes2Hex(c.object.Address())
	}

	return ""
}

func (c *PStateObject) Nonce() int {
	if c.object != nil {
		return int(c.object.Nonce)
	}

	return 0
}

func (c *PStateObject) Root() string {
	if c.object != nil {
		return ethutil.Bytes2Hex(ethutil.NewValue(c.object.State().Root()).Bytes())
	}

	return "<err>"
}

func (c *PStateObject) IsContract() bool {
	if c.object != nil {
		return len(c.object.Script()) > 0
	}

	return false
}

type KeyVal struct {
	Key   string
	Value string
}

func (c *PStateObject) StateKeyVal(asJson bool) interface{} {
	var values []KeyVal
	if c.object != nil {
		c.object.State().EachStorage(func(name string, value *ethutil.Value) {
			values = append(values, KeyVal{name, ethutil.Bytes2Hex(value.Bytes())})
		})
	}

	if asJson {
		valuesJson, err := json.Marshal(values)
		if err != nil {
			return nil
		}
		fmt.Println(string(valuesJson))
		return string(valuesJson)
	}

	return values
}

func (c *PStateObject) Script() string {
	if c.object != nil {
		return strings.Join(ethchain.Disassemble(c.object.Script()), " ")
	}

	return ""
}

func (c *PStateObject) HexScript() string {
	if c.object != nil {
		return ethutil.Bytes2Hex(c.object.Script())
	}

	return ""
}

type PStorageState struct {
	StateAddress string
	Address      string
	Value        string
}

func NewPStorageState(storageObject *ethchain.StorageState) *PStorageState {
	return &PStorageState{ethutil.Bytes2Hex(storageObject.StateAddress), ethutil.Bytes2Hex(storageObject.Address), storageObject.Value.String()}
}
<commit_msg>Added each callback for storage<commit_after>package ethpub

import (
	"encoding/json"
	"fmt"
	"github.com/ethereum/eth-go/ethchain"
	"github.com/ethereum/eth-go/ethcrypto"
	"github.com/ethereum/eth-go/ethtrie"
	"github.com/ethereum/eth-go/ethutil"
	"strings"
)

// Peer interface exposed to QML

type PPeer struct {
	ref          *ethchain.Peer
	Inbound      bool   `json:"isInbound"`
	LastSend     int64  `json:"lastSend"`
	LastPong     int64  `json:"lastPong"`
	Ip           string `json:"ip"`
	Port         int    `json:"port"`
	Version      string `json:"version"`
	LastResponse string `json:"lastResponse"`
	Latency      string `json:"latency"`
}

func NewPPeer(peer ethchain.Peer) *PPeer {
	if peer == nil {
		return nil
	}

	// TODO: There must be something build in to do this?
	var ip []string
	for _, i := range peer.Host() {
		ip = append(ip, fmt.Sprintf("%d", i))
	}
	ipAddress := strings.Join(ip, ".")

	return &PPeer{ref: &peer, Inbound: peer.Inbound(), LastSend: peer.LastSend().Unix(), LastPong: peer.LastPong(), Version: peer.Version(), Ip: ipAddress, Port: int(peer.Port()), Latency: peer.PingTime()}
}

// Block interface exposed to QML
type PBlock struct {
	ref          *ethchain.Block
	Number       int    `json:"number"`
	Hash         string `json:"hash"`
	Transactions string `json:"transactions"`
	Time         int64  `json:"time"`
	Coinbase     string `json:"coinbase"`
	GasLimit     string `json:"gasLimit"`
	GasUsed      string `json:"gasUsed"`
}

// Creates a new QML Block from a chain block
func NewPBlock(block *ethchain.Block) *PBlock {
	if block == nil {
		return nil
	}

	var ptxs []PTx
	for _, tx := range block.Transactions() {
		ptxs = append(ptxs, *NewPTx(tx))
	}

	txJson, err := json.Marshal(ptxs)
	if err != nil {
		return nil
	}

	return &PBlock{ref: block, Number: int(block.Number.Uint64()), GasUsed: block.GasUsed.String(), GasLimit: block.GasLimit.String(), Hash: ethutil.Bytes2Hex(block.Hash()), Transactions: string(txJson), Time: block.Time, Coinbase: ethutil.Bytes2Hex(block.Coinbase)}
}

func (self *PBlock) ToString() string {
	if self.ref != nil {
		return self.ref.String()
	}

	return ""
}

func (self *PBlock) GetTransaction(hash string) *PTx {
	tx := self.ref.GetTransaction(ethutil.Hex2Bytes(hash))
	if tx == nil {
		return nil
	}

	return NewPTx(tx)
}

type PTx struct {
	ref *ethchain.Transaction

	Value           string `json:"value"`
	Gas             string `json:"gas"`
	GasPrice        string `json:"gasPrice"`
	Hash            string `json:"hash"`
	Address         string `json:"address"`
	Sender          string `json:"sender"`
	RawData         string `json:"rawData"`
	Data            string `json:"data"`
	Contract        bool   `json:"isContract"`
	CreatesContract bool   `json:"createsContract"`
	Confirmations   int    `json:"confirmations"`
}

func NewPTx(tx *ethchain.Transaction) *PTx {
	hash := ethutil.Bytes2Hex(tx.Hash())
	receiver := ethutil.Bytes2Hex(tx.Recipient)
	if receiver == "0000000000000000000000000000000000000000" {
		receiver = ethutil.Bytes2Hex(tx.CreationAddress())
	}
	sender := ethutil.Bytes2Hex(tx.Sender())
	createsContract := tx.CreatesContract()

	var data string
	if tx.CreatesContract() {
		data = strings.Join(ethchain.Disassemble(tx.Data), "\n")
	} else {
		data = ethutil.Bytes2Hex(tx.Data)
	}

	return &PTx{ref: tx, Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: receiver, Contract: tx.CreatesContract(), Gas: tx.Gas.String(), GasPrice: tx.GasPrice.String(), Data: data, Sender: sender, CreatesContract: createsContract, RawData: ethutil.Bytes2Hex(tx.Data)}
}

func (self *PTx) ToString() string {
	return self.ref.String()
}

type PKey struct {
	Address    string `json:"address"`
	PrivateKey string `json:"privateKey"`
	PublicKey  string `json:"publicKey"`
}

func NewPKey(key *ethcrypto.KeyPair) *PKey {
	return &PKey{ethutil.Bytes2Hex(key.Address()), ethutil.Bytes2Hex(key.PrivateKey), ethutil.Bytes2Hex(key.PublicKey)}
}

type PReceipt struct {
	CreatedContract bool   `json:"createdContract"`
	Address         string `json:"address"`
	Hash            string `json:"hash"`
	Sender          string `json:"sender"`
}

func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) *PReceipt {
	return &PReceipt{
		contractCreation,
		ethutil.Bytes2Hex(creationAddress),
		ethutil.Bytes2Hex(hash),
		ethutil.Bytes2Hex(address),
	}
}

type PStateObject struct {
	object *ethchain.StateObject
}

func NewPStateObject(object *ethchain.StateObject) *PStateObject {
	return &PStateObject{object: object}
}

func (c *PStateObject) GetStorage(address string) string {
	// Because somehow, even if you return nil to QML it
	// still has some magical object so we can't rely on
	// undefined or null at the QML side
	if c.object != nil {
		val := c.object.GetStorage(ethutil.Big("0x" + address))

		return val.BigInt().String()
	}

	return ""
}

func (c *PStateObject) Value() string {
	if c.object != nil {
		return c.object.Amount.String()
	}

	return ""
}

func (c *PStateObject) Address() string {
	if c.object != nil {
		return ethutil.Bytes2Hex(c.object.Address())
	}

	return ""
}

func (c *PStateObject) Nonce() int {
	if c.object != nil {
		return int(c.object.Nonce)
	}

	return 0
}

func (c *PStateObject) Root() string {
	if c.object != nil {
		return ethutil.Bytes2Hex(ethutil.NewValue(c.object.State().Root()).Bytes())
	}

	return "<err>"
}

func (c *PStateObject) IsContract() bool {
	if c.object != nil {
		return len(c.object.Script()) > 0
	}

	return false
}

func (self *PStateObject) EachStorage(cb ethtrie.EachCallback) {
	self.object.State().EachStorage(cb)
}

type KeyVal struct {
	Key   string
	Value string
}

func (c *PStateObject) StateKeyVal(asJson bool) interface{} {
	var values []KeyVal
	if c.object != nil {
		c.object.State().EachStorage(func(name string, value *ethutil.Value) {
			values = append(values, KeyVal{name, ethutil.Bytes2Hex(value.Bytes())})
		})
	}

	if asJson {
		valuesJson, err := json.Marshal(values)
		if err != nil {
			return nil
		}
		fmt.Println(string(valuesJson))
		return string(valuesJson)
	}

	return values
}

func (c *PStateObject) Script() string {
	if c.object != nil {
		return strings.Join(ethchain.Disassemble(c.object.Script()), " ")
	}

	return ""
}

func (c *PStateObject) HexScript() string {
	if c.object != nil {
		return ethutil.Bytes2Hex(c.object.Script())
	}

	return ""
}

type PStorageState struct {
	StateAddress string
	Address      string
	Value        string
}

func NewPStorageState(storageObject *ethchain.StorageState) *PStorageState {
	return &PStorageState{ethutil.Bytes2Hex(storageObject.StateAddress), ethutil.Bytes2Hex(storageObject.Address), storageObject.Value.String()}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Fission 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 kubewatcher

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"strings"

	"k8s.io/client-go/1.5/kubernetes"
	"k8s.io/client-go/1.5/pkg/api"
	"k8s.io/client-go/1.5/pkg/runtime"
	"k8s.io/client-go/1.5/pkg/watch"

	"github.com/platform9/fission"
)

type requestType int

const (
	SYNC requestType = iota
)

type (
	watchSubscription struct {
		fission.Watch
		kubeWatch watch.Interface
	}

	kubeWatcherRequest struct {
		requestType
		watches         []fission.Watch
		responseChannel chan *kubeWatcherResponse
	}
	kubeWatcherResponse struct {
		error
	}

	KubeWatcher struct {
		watches          map[string]watchSubscription
		kubernetesClient *kubernetes.Clientset
		poster           *Poster
		requestChannel   chan *kubeWatcherRequest
	}
)

func MakeKubeWatcher(kubernetesClient *kubernetes.Clientset, poster *Poster) *KubeWatcher {
	kw := &KubeWatcher{
		watches:          make(map[string]watchSubscription),
		kubernetesClient: kubernetesClient,
		poster:           poster,
		requestChannel:   make(chan *kubeWatcherRequest),
	}
	go kw.svc()
	return kw
}

func (kw *KubeWatcher) Sync(watches []fission.Watch) error {
	req := &kubeWatcherRequest{
		requestType:     SYNC,
		watches:         watches,
		responseChannel: make(chan *kubeWatcherResponse),
	}
	kw.requestChannel <- req
	resp := <-req.responseChannel
	return resp.error
}

func (kw *KubeWatcher) svc() {
	for {
		req := <-kw.requestChannel
		switch req.requestType {
		case SYNC:
			newWatchUids := make(map[string]bool)
			for _, w := range req.watches {
				newWatchUids[w.Metadata.Uid] = true
			}
			// Remove old watches
			for uid, ws := range kw.watches {
				if _, ok := newWatchUids[uid]; !ok {
					kw.removeWatch(&ws.Watch)
				}
			}
			// Add new watches
			for _, w := range req.watches {
				if _, ok := kw.watches[w.Metadata.Uid]; !ok {
					kw.addWatch(&w)
				}
			}
			req.responseChannel <- &kubeWatcherResponse{error: nil}
		}
	}
}

// lifted from kubernetes/pkg/kubectl/resource_printer.go
func printKubernetesObject(obj runtime.Object, w io.Writer) error {
	switch obj := obj.(type) {
	case *runtime.Unknown:
		var buf bytes.Buffer
		err := json.Indent(&buf, obj.Raw, "", "    ")
		if err != nil {
			return err
		}
		buf.WriteRune('\n')
		_, err = buf.WriteTo(w)
		return err
	}

	data, err := json.MarshalIndent(obj, "", "    ")
	if err != nil {
		return err
	}
	data = append(data, '\n')
	_, err = w.Write(data)
	return err
}

func (kw *KubeWatcher) createKubernetesWatch(w *fission.Watch) (watch.Interface, error) {
	var wi watch.Interface
	var err error

	listOptions := api.ListOptions{} // TODO populate labelselector and fieldselector

	// TODO handle the full list of types
	switch strings.ToUpper(w.ObjType) {
	case "POD":
		wi, err = kw.kubernetesClient.Core().Pods(w.Namespace).Watch(listOptions)
	case "SERVICE":
		wi, err = kw.kubernetesClient.Core().Services(w.Namespace).Watch(listOptions)
	default:
		msg := fmt.Sprintf("Error: unknown obj type '%v'", w.ObjType)
		log.Println(msg)
		err = errors.New(msg)
	}
	return wi, err
}

func (kw *KubeWatcher) addWatch(w *fission.Watch) error {
	log.Printf("Adding watch %v: %v", w.Metadata.Name, w.Function.Name)
	wi, err := kw.createKubernetesWatch(w)
	if err != nil {
		return err
	}
	ws := &watchSubscription{
		Watch:     *w,
		kubeWatch: wi,
	}
	kw.watches[w.Metadata.Uid] = *ws
	go ws.eventDispatchLoop(kw.poster)
	return nil
}

func (kw *KubeWatcher) removeWatch(w *fission.Watch) error {
	log.Printf("Removing watch %v: %v", w.Metadata.Name, w.Function.Name)
	ws, ok := kw.watches[w.Metadata.Uid]
	if !ok {
		return fission.MakeError(fission.ErrorNotFound,
			fmt.Sprintf("watch doesn't exist: %v", w.Metadata))
	}
	ws.kubeWatch.Stop()
	delete(kw.watches, w.Metadata.Uid)
	return nil
}

func (ws *watchSubscription) eventDispatchLoop(poster *Poster) {
	log.Println("Listening to watch ", ws.Watch.Metadata.Name)
	for {
		ev, more := <-ws.kubeWatch.ResultChan()
		if !more {
			log.Println("Watch stopped", ws.Watch.Metadata.Name)
			return
		}

		var buf bytes.Buffer
		err := printKubernetesObject(ev.Object, &buf)
		if err != nil {
			log.Println("Failed to serialize object: %v", err)
		}
		poster.Post(string(ev.Type), ws.Watch.ObjType, ws.Watch.Url, &buf)
	}
}
<commit_msg>Add an atomic bool to track if a watch is stopped<commit_after>/*
Copyright 2016 The Fission 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 kubewatcher

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"strings"
	"sync/atomic"

	"k8s.io/client-go/1.5/kubernetes"
	"k8s.io/client-go/1.5/pkg/api"
	"k8s.io/client-go/1.5/pkg/runtime"
	"k8s.io/client-go/1.5/pkg/watch"

	"github.com/platform9/fission"
)

type requestType int

const (
	SYNC requestType = iota
)

type (
	watchSubscription struct {
		fission.Watch
		kubeWatch watch.Interface
		stopped   *int32
	}

	kubeWatcherRequest struct {
		requestType
		watches         []fission.Watch
		responseChannel chan *kubeWatcherResponse
	}
	kubeWatcherResponse struct {
		error
	}

	KubeWatcher struct {
		watches          map[string]watchSubscription
		kubernetesClient *kubernetes.Clientset
		poster           *Poster
		requestChannel   chan *kubeWatcherRequest
	}
)

func MakeKubeWatcher(kubernetesClient *kubernetes.Clientset, poster *Poster) *KubeWatcher {
	kw := &KubeWatcher{
		watches:          make(map[string]watchSubscription),
		kubernetesClient: kubernetesClient,
		poster:           poster,
		requestChannel:   make(chan *kubeWatcherRequest),
	}
	go kw.svc()
	return kw
}

func (kw *KubeWatcher) Sync(watches []fission.Watch) error {
	req := &kubeWatcherRequest{
		requestType:     SYNC,
		watches:         watches,
		responseChannel: make(chan *kubeWatcherResponse),
	}
	kw.requestChannel <- req
	resp := <-req.responseChannel
	return resp.error
}

func (kw *KubeWatcher) svc() {
	for {
		req := <-kw.requestChannel
		switch req.requestType {
		case SYNC:
			newWatchUids := make(map[string]bool)
			for _, w := range req.watches {
				newWatchUids[w.Metadata.Uid] = true
			}
			// Remove old watches
			for uid, ws := range kw.watches {
				if _, ok := newWatchUids[uid]; !ok {
					kw.removeWatch(&ws.Watch)
				}
			}
			// Add new watches
			for _, w := range req.watches {
				if _, ok := kw.watches[w.Metadata.Uid]; !ok {
					kw.addWatch(&w)
				}
			}
			req.responseChannel <- &kubeWatcherResponse{error: nil}
		}
	}
}

// lifted from kubernetes/pkg/kubectl/resource_printer.go
func printKubernetesObject(obj runtime.Object, w io.Writer) error {
	switch obj := obj.(type) {
	case *runtime.Unknown:
		var buf bytes.Buffer
		err := json.Indent(&buf, obj.Raw, "", "    ")
		if err != nil {
			return err
		}
		buf.WriteRune('\n')
		_, err = buf.WriteTo(w)
		return err
	}

	data, err := json.MarshalIndent(obj, "", "    ")
	if err != nil {
		return err
	}
	data = append(data, '\n')
	_, err = w.Write(data)
	return err
}

func (kw *KubeWatcher) createKubernetesWatch(w *fission.Watch) (watch.Interface, error) {
	var wi watch.Interface
	var err error

	listOptions := api.ListOptions{} // TODO populate labelselector and fieldselector

	// TODO handle the full list of types
	switch strings.ToUpper(w.ObjType) {
	case "POD":
		wi, err = kw.kubernetesClient.Core().Pods(w.Namespace).Watch(listOptions)
	case "SERVICE":
		wi, err = kw.kubernetesClient.Core().Services(w.Namespace).Watch(listOptions)
	default:
		msg := fmt.Sprintf("Error: unknown obj type '%v'", w.ObjType)
		log.Println(msg)
		err = errors.New(msg)
	}
	return wi, err
}

func (kw *KubeWatcher) addWatch(w *fission.Watch) error {
	log.Printf("Adding watch %v: %v", w.Metadata.Name, w.Function.Name)
	wi, err := kw.createKubernetesWatch(w)
	if err != nil {
		return err
	}
	var stopped int32 = 0
	ws := &watchSubscription{
		Watch:     *w,
		kubeWatch: wi,
		stopped:   &stopped,
	}
	kw.watches[w.Metadata.Uid] = *ws
	go ws.eventDispatchLoop(kw.poster)
	return nil
}

func (kw *KubeWatcher) removeWatch(w *fission.Watch) error {
	log.Printf("Removing watch %v: %v", w.Metadata.Name, w.Function.Name)
	ws, ok := kw.watches[w.Metadata.Uid]
	if !ok {
		return fission.MakeError(fission.ErrorNotFound,
			fmt.Sprintf("watch doesn't exist: %v", w.Metadata))
	}
	delete(kw.watches, w.Metadata.Uid)
	atomic.StoreInt32(ws.stopped, 1)
	ws.kubeWatch.Stop()
	return nil
}

func (ws *watchSubscription) eventDispatchLoop(poster *Poster) {
	log.Println("Listening to watch ", ws.Watch.Metadata.Name)
	for {
		ev, more := <-ws.kubeWatch.ResultChan()
		if !more {
			log.Println("Watch stopped", ws.Watch.Metadata.Name)
			break
		}

		var buf bytes.Buffer
		err := printKubernetesObject(ev.Object, &buf)
		if err != nil {
			log.Println("Failed to serialize object: %v", err)
		}
		poster.Post(string(ev.Type), ws.Watch.ObjType, ws.Watch.Url, &buf)
	}
	if !atomic.LoadInt32(ws.stopped) {
		// TODO re-watch.  What about resource version?
		log.Panicf("Watch channel closed unexpectedly")
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 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 integration

import (
	"fmt"
	"sync"
	"testing"
	"time"

	apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	"k8s.io/apiextensions-apiserver/test/integration/fixtures"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/dynamic"
)

func TestChangeCRD(t *testing.T) {
	tearDown, config, _, err := fixtures.StartDefaultServer(t)
	if err != nil {
		t.Fatal(err)
	}
	defer tearDown()
	config.QPS = 1000
	config.Burst = 1000
	apiExtensionsClient, err := clientset.NewForConfig(config)
	if err != nil {
		t.Fatal(err)
	}
	dynamicClient, err := dynamic.NewForConfig(config)
	if err != nil {
		t.Fatal(err)
	}

	noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
	noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionsClient, dynamicClient)
	if err != nil {
		t.Fatal(err)
	}

	ns := "default"
	noxuNamespacedResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, "v1beta1")

	stopChan := make(chan struct{})

	wg := &sync.WaitGroup{}

	// Set up loop to modify CRD in the background
	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			select {
			case <-stopChan:
				return
			default:
			}

			noxuDefinitionToUpdate, err := apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(noxuDefinition.Name, metav1.GetOptions{})
			if err != nil {
				t.Fatal(err)
			}
			if len(noxuDefinitionToUpdate.Spec.Versions) == 1 {
				v2 := noxuDefinitionToUpdate.Spec.Versions[0]
				v2.Name = "v2"
				v2.Served = true
				v2.Storage = false
				noxuDefinitionToUpdate.Spec.Versions = append(noxuDefinitionToUpdate.Spec.Versions, v2)
			} else {
				noxuDefinitionToUpdate.Spec.Versions = noxuDefinitionToUpdate.Spec.Versions[0:1]
			}
			if _, err := apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(noxuDefinitionToUpdate); err != nil && !apierrors.IsConflict(err) {
				t.Fatal(err)
			}
			time.Sleep(10 * time.Millisecond)
		}
	}()

	// Set up 100 loops creating and reading and watching custom resources
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			noxuInstanceToCreate := fixtures.NewNoxuInstance(ns, fmt.Sprintf("foo-%d", i))
			if _, err := noxuNamespacedResourceClient.Create(noxuInstanceToCreate, metav1.CreateOptions{}); err != nil {
				t.Fatal(err)
			}
			for {
				select {
				case <-stopChan:
					return
				default:
					if _, err := noxuNamespacedResourceClient.Get(noxuInstanceToCreate.GetName(), metav1.GetOptions{}); err != nil {
						t.Fatal(err)
					}
				}
				time.Sleep(10 * time.Millisecond)
			}
		}(i)

		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			for {
				select {
				case <-stopChan:
					return
				default:
					w, err := noxuNamespacedResourceClient.Watch(metav1.ListOptions{})
					if err != nil {
						t.Fatalf("unexpected error establishing watch: %v", err)
					}
					for event := range w.ResultChan() {
						switch event.Type {
						case watch.Added, watch.Modified, watch.Deleted:
							// all expected
						default:
							t.Errorf("unexpected watch event: %#v", event)
						}
					}
				}
				time.Sleep(10 * time.Millisecond)
			}
		}(i)
	}

	// Let all the established get request loops soak
	time.Sleep(5 * time.Second)

	// Tear down
	close(stopChan)

	// Let loops drain
	drained := make(chan struct{})
	go func() {
		defer close(drained)
		wg.Wait()
	}()

	select {
	case <-drained:
	case <-time.After(wait.ForeverTestTimeout):
		t.Error("timed out waiting for clients to complete")
	}
}
<commit_msg>Deflake TestChangeCRD<commit_after>/*
Copyright 2019 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 integration

import (
	"fmt"
	"sync"
	"testing"
	"time"

	apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	"k8s.io/apiextensions-apiserver/test/integration/fixtures"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/dynamic"
)

func TestChangeCRD(t *testing.T) {
	tearDown, config, _, err := fixtures.StartDefaultServer(t)
	if err != nil {
		t.Fatal(err)
	}
	defer tearDown()
	config.QPS = 1000
	config.Burst = 1000
	apiExtensionsClient, err := clientset.NewForConfig(config)
	if err != nil {
		t.Fatal(err)
	}
	dynamicClient, err := dynamic.NewForConfig(config)
	if err != nil {
		t.Fatal(err)
	}

	noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
	noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionsClient, dynamicClient)
	if err != nil {
		t.Fatal(err)
	}

	ns := "default"
	noxuNamespacedResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, "v1beta1")

	stopChan := make(chan struct{})

	wg := &sync.WaitGroup{}

	// Set up loop to modify CRD in the background
	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			select {
			case <-stopChan:
				return
			default:
			}

			noxuDefinitionToUpdate, err := apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(noxuDefinition.Name, metav1.GetOptions{})
			if err != nil {
				t.Fatal(err)
			}
			if len(noxuDefinitionToUpdate.Spec.Versions) == 1 {
				v2 := noxuDefinitionToUpdate.Spec.Versions[0]
				v2.Name = "v2"
				v2.Served = true
				v2.Storage = false
				noxuDefinitionToUpdate.Spec.Versions = append(noxuDefinitionToUpdate.Spec.Versions, v2)
			} else {
				noxuDefinitionToUpdate.Spec.Versions = noxuDefinitionToUpdate.Spec.Versions[0:1]
			}
			if _, err := apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(noxuDefinitionToUpdate); err != nil && !apierrors.IsConflict(err) {
				t.Fatal(err)
			}
			time.Sleep(10 * time.Millisecond)
		}
	}()

	// Set up 10 loops creating and reading and watching custom resources
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			noxuInstanceToCreate := fixtures.NewNoxuInstance(ns, fmt.Sprintf("foo-%d", i))
			if _, err := noxuNamespacedResourceClient.Create(noxuInstanceToCreate, metav1.CreateOptions{}); err != nil {
				t.Fatal(err)
			}
			for {
				select {
				case <-stopChan:
					return
				default:
					if _, err := noxuNamespacedResourceClient.Get(noxuInstanceToCreate.GetName(), metav1.GetOptions{}); err != nil {
						t.Fatal(err)
					}
				}
				time.Sleep(10 * time.Millisecond)
			}
		}(i)

		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			for {
				select {
				case <-stopChan:
					return
				default:
					w, err := noxuNamespacedResourceClient.Watch(metav1.ListOptions{})
					if err != nil {
						t.Fatalf("unexpected error establishing watch: %v", err)
					}
					for event := range w.ResultChan() {
						switch event.Type {
						case watch.Added, watch.Modified, watch.Deleted:
							// all expected
						default:
							t.Errorf("unexpected watch event: %#v", event)
						}
					}
				}
				time.Sleep(10 * time.Millisecond)
			}
		}(i)
	}

	// Let all the established get request loops soak
	time.Sleep(5 * time.Second)

	// Tear down
	close(stopChan)

	// Let loops drain
	drained := make(chan struct{})
	go func() {
		defer close(drained)
		wg.Wait()
	}()

	select {
	case <-drained:
	case <-time.After(wait.ForeverTestTimeout):
		t.Error("timed out waiting for clients to complete")
	}
}
<|endoftext|>


<commit_before>package scraper

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"strings"
	"sync"
)

// ResourceOrigin represents data originally used to create this resource
type ResourceOrigin struct {
	// URL represents the initial URL received by input
	URL string
	// IP represents the initial IP address received by input
	IP string
	// Host represents the original requested hostname for the resource
	Host string
}

// Response represents the data for the HTTP-based request, closely matching
// http.Response
type Response struct {
	// Host represents the resulting host derived by the original returned
	// resource
	Host string

	// Remote represents if the resulting resource is remote to the original domain
	Remote bool

	// Code represents the numeric HTTP based status code
	Code int

	// Body represents a string implementation of the byte array returned by
	// http.Response. Only used for primary requests, ignored for Resource structs.
	Body string

	// Scheme represents the end scheme used to fetch the page. For example, https
	Scheme string

	// ContentLength represents the number of bytes in the body of the response
	ContentLength int64

	// TLS represents the SSL/TLS handshake/session if the resource was loaded over
	// SSL.
	TLS *tls.ConnectionState
}

// Resource represents a single entity of many within a given crawl. These should
// only be of type css, js, jpg, png, etc (static resources).
type Resource struct {
	// request represents what we were provided before the request
	request ResourceOrigin

	// Response represents the end result/data/status/etc.
	Response Response

	// URL represents the resulting static URL derived by the original result page
	URL string

	// Error represents any errors that may have occurred when fetching the resource
	Error error

	// Time represents the time it took to complete the request
	Time *TimerResult
}

// fetchResource fetches a singular resource from a page, returning a *Resource struct.
// As we don't care much about the body of the resource, that can safely be ignored. We
// must still close the body object, however.
func (c *Crawler) fetchResource(rsrc *Resource) {
	var err error

	defer resourcePool.Done()

	// calculate the time it takes to fetch the request
	resp, err := c.Get(rsrc.request.URL)

	if err != nil {
		rsrc.Error = err
		return
	}

	if resp.Body != nil {
		resp.Body.Close()
	}

	rsrc.request.Host, err = getHost(rsrc.request.URL)
	if err != nil {
		rsrc.Error = err
		return
	}

	rsrc.Response.Host = resp.Request.Host
	rsrc.URL = resp.URL
	rsrc.Response.Code = resp.StatusCode
	rsrc.Response.Scheme = resp.Request.URL.Scheme
	rsrc.Response.ContentLength = resp.ContentLength
	rsrc.Response.TLS = resp.TLS
	rsrc.Time = resp.Time

	if rsrc.Response.Host != rsrc.request.Host {
		rsrc.Response.Remote = true
	}

	c.Log.Printf("fetched %s in %dms with status %d", rsrc.URL, rsrc.Time.Milli, rsrc.Response.Code)

	return
}

// Results -- struct returned by Crawl() to represent the entire crawl process
type Results struct {
	// Inherit the Resource struct
	Resource

	// Slice of Resource structs containing the needed resources for the given URL
	Resources []*Resource

	// ResourceTime shows how long it took to fetch all resources
	ResourceTime *TimerResult

	// TotalTime represents the time it took to crawl the site
	TotalTime *TimerResult
}

func (r *Results) String() string {
	if r.Resources != nil && r.ResourceTime != nil && r.TotalTime != nil {
		return fmt.Sprintf("<url(%s) == %d, resources(%d), resourceTime(%dms), totalTime(%dms), err(%s)>", r.URL, r.Response.Code, len(r.Resources), r.ResourceTime.Milli, r.TotalTime.Milli, r.Error)
	}

	return fmt.Sprintf("<url(%s), ip(%s), err(%s)>", r.request.URL, r.request.IP, r.Error)
}

var resourcePool sync.WaitGroup

// FetchURL manages the fetching of the main resource, as well as all child resources,
// providing a Results struct containing the entire crawl data needed
func (c *Crawler) FetchURL(URL string) (res *Results) {
	res = &Results{}
	crawlTimer := NewTimer()

	var err error

	res.request.URL = URL
	res.request.Host, err = getHost(URL)
	if err != nil {
		res.Error = err
		return
	}
	res.request.IP = c.ipmap[res.request.Host]

	// actually fetch the request
	resp, err := c.Get(URL)

	defer func() {
		crawlTimer.End()
		res.TotalTime = crawlTimer.Result
	}()

	if err != nil {
		res.Error = err
		return
	}

	defer resp.Body.Close()

	res.Response.Host = resp.Request.Host
	res.URL = resp.URL
	res.Response.Code = resp.StatusCode
	res.Response.Scheme = resp.Request.URL.Scheme
	res.Response.ContentLength = resp.ContentLength
	res.Response.TLS = resp.TLS
	res.Time = resp.Time

	if res.Response.Host != res.request.Host {
		res.Response.Remote = true
	}

	buf, _ := ioutil.ReadAll(resp.Body)
	b := ioutil.NopCloser(bytes.NewReader(buf))
	defer b.Close()

	bbytes, err := ioutil.ReadAll(bytes.NewBuffer(buf))
	if err == nil && len(bbytes) != 0 {
		res.Response.Body = string(bbytes[:])
	}

	urls := getSrc(b, resp.Request)

	c.Log.Printf("fetched %s in %dms with status %d", res.URL, res.Time.Milli, res.Response.Code)

	resourceTime := NewTimer()

	defer func() {
		resourceTime.End()
		res.ResourceTime = resourceTime.Result
	}()

	for i := range urls {
		resourcePool.Add(1)

		rsrc := &Resource{request: ResourceOrigin{URL: urls[i]}}
		res.Resources = append(res.Resources, rsrc)
		go c.fetchResource(res.Resources[i])
	}

	resourcePool.Wait()

	return
}

// Domain represents a url we need to fetch, including the items needed to
// fetch said url. E.g: host, port, ip, scheme, path, etc.
type Domain struct {
	URL *url.URL
	IP  string
}

// Crawler is the higher level struct which wraps the entire threaded crawl process
type Crawler struct {
	Log     *log.Logger
	Domains []*Domain
	Results []*Results
	ipmap   map[string]string
}

// Crawl represents the higher level functionality of scraper. Crawl should
// concurrently request the needed resources for a list of domains, allowing
// the bypass of DNS lookups where necessary.
func (c *Crawler) Crawl() {
	var results []*Results
	var wg sync.WaitGroup
	timer := NewTimer()

	// strip all common duplicate domain/ip pairs
	stripDups(&c.Domains)

	c.ipmap = make(map[string]string)
	var withwww string
	for i := range c.Domains {
		c.ipmap[c.Domains[i].URL.Host] = c.Domains[i].IP
		c.ipmap[strings.TrimPrefix(c.Domains[i].URL.Host, "www.")] = c.Domains[i].IP // no www. directive

		withwww = "www." + strings.TrimPrefix(c.Domains[i].URL.Host, "www.")
		c.ipmap[withwww] = c.Domains[i].IP // www. directive
	}

	// loop through all supplied urls and send them to a worker to be fetched
	for _, domain := range c.Domains {
		wg.Add(1)

		go func(domain *Domain) {
			defer wg.Done()

			result := c.FetchURL(domain.URL.String())
			results = append(results, result)

			if result.Error != nil {
				c.Log.Printf("error scanning %s (error: %s)", domain.URL.String(), result.Error)
			} else {
				c.Log.Printf("finished scanning %s (%dms)", domain.URL.String(), result.TotalTime.Milli)
			}
		}(domain)
	}

	// wait for all workers to complete their tasks
	wg.Wait()
	timer.End()

	c.Log.Printf("finished scanning %d urls in %d seconds", len(results), timer.Result.Seconds)

	// give some extra details
	var resSuccess, resError int
	for i := range results {
		if results[i].Error != nil {
			resError++
			continue
		}

		resSuccess++
	}

	c.Log.Printf("%d successful, %d errored", resSuccess, resError)

	c.Results = results

	return
}

// GetResults gets the potential results of a given requested url/ip
func (c *Crawler) GetResults(URL, IP string) *Results {
	for i := range c.Results {
		if c.Results[i].request.URL == URL && c.Results[i].request.IP == IP {
			return c.Results[i]
		}
	}

	return nil
}
<commit_msg>because speedz<commit_after>package scraper

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"strings"
	"sync"
)

// ResourceOrigin represents data originally used to create this resource
type ResourceOrigin struct {
	// URL represents the initial URL received by input
	URL string
	// IP represents the initial IP address received by input
	IP string
	// Host represents the original requested hostname for the resource
	Host string
}

// Response represents the data for the HTTP-based request, closely matching
// http.Response
type Response struct {
	// Host represents the resulting host derived by the original returned
	// resource
	Host string

	// Remote represents if the resulting resource is remote to the original domain
	Remote bool

	// Code represents the numeric HTTP based status code
	Code int

	// Body represents a string implementation of the byte array returned by
	// http.Response. Only used for primary requests, ignored for Resource structs.
	Body string

	// Scheme represents the end scheme used to fetch the page. For example, https
	Scheme string

	// ContentLength represents the number of bytes in the body of the response
	ContentLength int64

	// TLS represents the SSL/TLS handshake/session if the resource was loaded over
	// SSL.
	TLS *tls.ConnectionState
}

// Resource represents a single entity of many within a given crawl. These should
// only be of type css, js, jpg, png, etc (static resources).
type Resource struct {
	// request represents what we were provided before the request
	request ResourceOrigin

	// Response represents the end result/data/status/etc.
	Response Response

	// URL represents the resulting static URL derived by the original result page
	URL string

	// Error represents any errors that may have occurred when fetching the resource
	Error error

	// Time represents the time it took to complete the request
	Time *TimerResult
}

// fetchResource fetches a singular resource from a page, returning a *Resource struct.
// As we don't care much about the body of the resource, that can safely be ignored. We
// must still close the body object, however.
func (c *Crawler) fetchResource(rsrc *Resource) {
	var err error

	defer resourcePool.Done()

	// calculate the time it takes to fetch the request
	resp, err := c.Get(rsrc.request.URL)

	if err != nil {
		rsrc.Error = err
		return
	}

	if resp.Body != nil {
		resp.Body.Close()
	}

	rsrc.request.Host, err = getHost(rsrc.request.URL)
	if err != nil {
		rsrc.Error = err
		return
	}

	rsrc.Response.Host = resp.Request.Host
	rsrc.URL = resp.URL
	rsrc.Response.Code = resp.StatusCode
	rsrc.Response.Scheme = resp.Request.URL.Scheme
	rsrc.Response.ContentLength = resp.ContentLength
	rsrc.Response.TLS = resp.TLS
	rsrc.Time = resp.Time

	if rsrc.Response.Host != rsrc.request.Host {
		rsrc.Response.Remote = true
	}

	c.Log.Printf("fetched %s in %dms with status %d", rsrc.URL, rsrc.Time.Milli, rsrc.Response.Code)

	return
}

// Results -- struct returned by Crawl() to represent the entire crawl process
type Results struct {
	// Inherit the Resource struct
	Resource

	// Slice of Resource structs containing the needed resources for the given URL
	Resources []*Resource

	// ResourceTime shows how long it took to fetch all resources
	ResourceTime *TimerResult

	// TotalTime represents the time it took to crawl the site
	TotalTime *TimerResult
}

func (r *Results) String() string {
	if r.Resources != nil && r.ResourceTime != nil && r.TotalTime != nil {
		return fmt.Sprintf("<url(%s) == %d, resources(%d), resourceTime(%dms), totalTime(%dms), err(%s)>", r.URL, r.Response.Code, len(r.Resources), r.ResourceTime.Milli, r.TotalTime.Milli, r.Error)
	}

	return fmt.Sprintf("<url(%s), ip(%s), err(%s)>", r.request.URL, r.request.IP, r.Error)
}

var resourcePool sync.WaitGroup

// FetchURL manages the fetching of the main resource, as well as all child resources,
// providing a Results struct containing the entire crawl data needed
func (c *Crawler) FetchURL(URL string) (res *Results) {
	res = &Results{}
	crawlTimer := NewTimer()

	var err error

	res.request.URL = URL
	res.request.Host, err = getHost(URL)
	if err != nil {
		res.Error = err
		return
	}
	res.request.IP = c.ipmap[res.request.Host]

	// actually fetch the request
	resp, err := c.Get(URL)

	defer func() {
		crawlTimer.End()
		res.TotalTime = crawlTimer.Result
	}()

	if err != nil {
		res.Error = err
		return
	}

	defer resp.Body.Close()

	res.Response.Host = resp.Request.Host
	res.URL = resp.URL
	res.Response.Code = resp.StatusCode
	res.Response.Scheme = resp.Request.URL.Scheme
	res.Response.ContentLength = resp.ContentLength
	res.Response.TLS = resp.TLS
	res.Time = resp.Time

	if res.Response.Host != res.request.Host {
		res.Response.Remote = true
	}

	buf, _ := ioutil.ReadAll(resp.Body)
	b := ioutil.NopCloser(bytes.NewReader(buf))
	defer b.Close()

	bbytes, err := ioutil.ReadAll(bytes.NewBuffer(buf))
	if err == nil && len(bbytes) != 0 {
		res.Response.Body = string(bbytes[:])
	}

	urls := getSrc(b, resp.Request)

	c.Log.Printf("fetched %s in %dms with status %d", res.URL, res.Time.Milli, res.Response.Code)

	resourceTime := NewTimer()

	defer func() {
		resourceTime.End()
		res.ResourceTime = resourceTime.Result
	}()

	for i := range urls {
		resourcePool.Add(1)

		rsrc := &Resource{request: ResourceOrigin{URL: urls[i]}}
		res.Resources = append(res.Resources, rsrc)
		go c.fetchResource(res.Resources[i])
	}

	resourcePool.Wait()

	return
}

// Domain represents a url we need to fetch, including the items needed to
// fetch said url. E.g: host, port, ip, scheme, path, etc.
type Domain struct {
	URL *url.URL
	IP  string
}

// Crawler is the higher level struct which wraps the entire threaded crawl process
type Crawler struct {
	Log     *log.Logger
	Domains []*Domain
	Results []*Results
	ipmap   map[string]string
}

// Crawl represents the higher level functionality of scraper. Crawl should
// concurrently request the needed resources for a list of domains, allowing
// the bypass of DNS lookups where necessary.
func (c *Crawler) Crawl() {
	var results []*Results
	var wg sync.WaitGroup
	timer := NewTimer()

	// strip all common duplicate domain/ip pairs
	stripDups(&c.Domains)

	c.ipmap = make(map[string]string)
	var dom string
	for i := range c.Domains {
		c.ipmap[c.Domains[i].URL.Host] = c.Domains[i].IP
		dom = strings.TrimPrefix(c.Domains[i].URL.Host, "www.")
		c.ipmap[dom] = c.Domains[i].IP        // no www. directive
		c.ipmap["www."+dom] = c.Domains[i].IP // www. directive
	}

	// loop through all supplied urls and send them to a worker to be fetched
	for _, domain := range c.Domains {
		wg.Add(1)

		go func(domain *Domain) {
			defer wg.Done()

			result := c.FetchURL(domain.URL.String())
			results = append(results, result)

			if result.Error != nil {
				c.Log.Printf("error scanning %s (error: %s)", domain.URL.String(), result.Error)
			} else {
				c.Log.Printf("finished scanning %s (%dms)", domain.URL.String(), result.TotalTime.Milli)
			}
		}(domain)
	}

	// wait for all workers to complete their tasks
	wg.Wait()
	timer.End()

	c.Log.Printf("finished scanning %d urls in %d seconds", len(results), timer.Result.Seconds)

	// give some extra details
	var resSuccess, resError int
	for i := range results {
		if results[i].Error != nil {
			resError++
			continue
		}

		resSuccess++
	}

	c.Log.Printf("%d successful, %d errored", resSuccess, resError)

	c.Results = results

	return
}

// GetResults gets the potential results of a given requested url/ip
func (c *Crawler) GetResults(URL, IP string) *Results {
	for i := range c.Results {
		if c.Results[i].request.URL == URL && c.Results[i].request.IP == IP {
			return c.Results[i]
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package forms

import (
	"encoding/json"
	"fmt"

	"github.com/Jeffail/gabs"
	"github.com/bep/gr"
	"github.com/bep/gr/el"
	"github.com/bep/gr/evt"
	"github.com/murdinc/awsmDashboard/helpers"
)

type SnapshotClassForm struct {
	*gr.This
}

// Implements the StateInitializer interface
func (s SnapshotClassForm) GetInitialState() gr.State {
	return gr.State{"querying": true, "queryingOpts": true, "queryingVolumes": true, "error": "", "success": "",
		"rotate":    false,
		"propagate": false,
		"step":      1,
	}
}

// Implements the ComponentWillMount interface
func (s SnapshotClassForm) ComponentWillMount() {
	var class map[string]interface{}

	if s.Props().Interface("class") != nil {
		classJson := s.Props().Interface("class").([]byte)
		json.Unmarshal(classJson, &class)
	}

	s.SetState(class)
	s.SetState(gr.State{"queryingOpts": true, "queryingVolumes": true})

	// Get our options for the form
	go func() {
		endpoint := "//localhost:8081/api/classes/" + s.Props().String("apiType") + "/options"
		resp, err := helpers.GetAPI(endpoint)
		if !s.IsMounted() {
			return
		}
		if err != nil {
			s.SetState(gr.State{"queryingOpts": false, "error": fmt.Sprintf("Error while querying endpoint: %s", endpoint)})
			return
		}

		s.SetState(gr.State{"classOptionsResp": resp, "queryingOpts": false})
	}()

	// Get our existing instances for the form
	go func() {
		endpoint := "//localhost:8081/api/assets/volumes"
		resp, err := helpers.GetAPI(endpoint)
		if !s.IsMounted() {
			return
		}
		if err != nil {
			s.SetState(gr.State{"queryingVolumes": false, "error": fmt.Sprintf("Error while querying endpoint: %s", endpoint)})
			return
		}

		s.SetState(gr.State{"volumeOptionsResp": resp, "queryingVolumes": false})
	}()
}

func (s SnapshotClassForm) Render() gr.Component {

	state := s.State()
	props := s.Props()

	// Form placeholder
	response := el.Div()

	// Print any alerts
	helpers.ErrorElem(state.String("error")).Modify(response)
	helpers.SuccessElem(state.String("success")).Modify(response)

	if state.Int("step") == 1 {
		if state.Bool("queryingOpts") || state.Bool("queryingVolumes") {
			gr.Text("Loading...").Modify(response)
		} else {
			s.BuildClassForm(props.String("className"), state.Interface("classOptionsResp"), state.Interface("volumeOptionsResp")).Modify(response)
		}

	} else if state.Int("step") == 2 {

		if state.Bool("querying") {
			gr.Text("Saving...").Modify(response)
		} else {

			buttons := el.Div(
				gr.CSS("btn-toolbar"),
			)

			// Back
			el.Button(
				evt.Click(s.backButton).PreventDefault(),
				gr.CSS("btn", "btn-secondary"),
				gr.Text("Back"),
			).Modify(buttons)

			// Done
			el.Button(
				evt.Click(s.doneButton).PreventDefault(),
				gr.CSS("btn", "btn-primary"),
				gr.Text("Done"),
			).Modify(buttons)

			buttons.Modify(response)
		}

	}

	return response
}

func (s SnapshotClassForm) BuildClassForm(className string, optionsResp interface{}, volumeResp interface{}) *gr.Element {

	state := s.State()
	props := s.Props()

	var classOptions map[string][]string
	jsonParsed, _ := gabs.ParseJSON(optionsResp.([]byte))
	classOptionsJson := jsonParsed.S("classOptions").Bytes()
	json.Unmarshal(classOptionsJson, &classOptions)

	volumeJsonParsed, _ := gabs.ParseJSON(volumeResp.([]byte))
	volumeOptionsSlice, _ := volumeJsonParsed.S("assets").Children()

	var volumes []string
	volumesMeta := make(map[string]string)
	for _, volumeOption := range volumeOptionsSlice {
		volume := volumeOption.S("volumeID").Data().(string)
		if volume != "" {
			volumes = append(volumes, volume)
			volumesMeta[volume] = volumeOption.S("sizeHuman").Data().(string) + " " + volumeOption.S("name").Data().(string) + " " + volumeOption.S("attachment").Data().(string)
		}
	}

	classEdit := el.Div(
		el.Header3(gr.Text(className)),
		el.HorizontalRule(),
	)

	classEditForm := el.Form()

	TextField("Description", "description", state.String("description"), s.storeValue).Modify(classEditForm)

	Checkbox("Rotate", "rotate", state.Bool("rotate"), s.storeValue).Modify(classEditForm)
	if state.Bool("rotate") {
		NumberField("Retain", "retain", state.Int("retain"), s.storeValue).Modify(classEditForm)
	}
	Checkbox("Propagate", "propagate", state.Bool("propagate"), s.storeValue).Modify(classEditForm)
	if state.Bool("propagate") {
		SelectMultiple("Propagate Regions", "propagateRegions", classOptions["regions"], state.Interface("propagateRegions"), s.storeSelect).Modify(classEditForm)
	}

	SelectOneMeta("Volume", "volume", volumes, volumesMeta, state.Interface("volume"), s.storeSelect).Modify(classEditForm)

	classEditForm.Modify(classEdit)

	buttons := el.Div(
		gr.CSS("btn-toolbar"),
	)

	// Back
	el.Button(
		evt.Click(s.backButton).PreventDefault(),
		gr.CSS("btn", "btn-secondary"),
		gr.Text("Back"),
	).Modify(buttons)

	// Save
	el.Button(
		evt.Click(s.saveButton).PreventDefault(),
		gr.CSS("btn", "btn-primary"),
		gr.Text("Save"),
	).Modify(buttons)

	// Delete
	if props.Interface("hasDelete") != nil && props.Bool("hasDelete") {
		el.Button(
			evt.Click(s.deleteButton).PreventDefault(),
			gr.CSS("btn", "btn-danger", "pull-right"),
			gr.Text("Delete"),
		).Modify(buttons)
	}

	buttons.Modify(classEdit)

	return classEdit

}

func (s SnapshotClassForm) backButton(*gr.Event) {
	s.SetState(gr.State{"success": ""})
	s.Props().Call("backButton")
}

func (s SnapshotClassForm) doneButton(*gr.Event) {
	s.SetState(gr.State{"success": ""})
	s.Props().Call("hideAllModals")
}

func (s SnapshotClassForm) saveButton(*gr.Event) {
	s.SetState(gr.State{"querying": true, "step": 2})

	cfg := make(map[string]interface{})
	for key, _ := range s.State() {
		cfg[key] = s.State().Interface(key)
	}

	go func() {
		endpoint := "//localhost:8081/api/classes/" + s.Props().String("apiType") + "/name/" + s.Props().String("className")

		_, err := helpers.PutAPI(endpoint, cfg)
		if !s.IsMounted() {
			return
		}

		if err != nil {
			s.SetState(gr.State{"querying": false, "error": fmt.Sprintf("Error while querying endpoint: %s", endpoint), "step": 1})
			return
		}

		s.SetState(gr.State{"querying": false, "success": "Class was saved", "error": ""})
	}()

}

func (s SnapshotClassForm) deleteButton(*gr.Event) {
	s.SetState(gr.State{"querying": true})

	go func() {
		endpoint := "//localhost:8081/api/classes/" + s.Props().String("apiType") + "/name/" + s.Props().String("className")

		_, err := helpers.DeleteAPI(endpoint)
		if !s.IsMounted() {
			return
		}

		if err != nil {
			s.SetState(gr.State{"querying": false, "error": fmt.Sprintf("Error while querying endpoint: %s", endpoint)})
			return
		}

		s.SetState(gr.State{"querying": false, "success": "Class was deleted", "error": "", "step": 2})
	}()
}

func (s SnapshotClassForm) storeValue(event *gr.Event) {
	id := event.Target().Get("id").String()
	inputType := event.Target().Get("type").String()

	switch inputType {

	case "checkbox":
		s.SetState(gr.State{id: event.Target().Get("checked").Bool()})

	case "number":
		s.SetState(gr.State{id: event.TargetValue().Int()})

	default: // text, at least
		s.SetState(gr.State{id: event.TargetValue()})

	}
}

func (s SnapshotClassForm) storeSelect(id string, val interface{}) {
	switch value := val.(type) {

	case map[string]interface{}:
		// single
		s.SetState(gr.State{id: value["value"]})

	case []interface{}:
		// multi
		var vals []string
		options := len(value)
		for i := 0; i < options; i++ {
			vals = append(vals, value[i].(map[string]interface{})["value"].(string))
		}
		s.SetState(gr.State{id: vals})

	default:
		s.SetState(gr.State{id: val})

	}
}
<commit_msg>add description field<commit_after>package forms

import (
	"encoding/json"
	"fmt"

	"github.com/Jeffail/gabs"
	"github.com/bep/gr"
	"github.com/bep/gr/el"
	"github.com/bep/gr/evt"
	"github.com/murdinc/awsmDashboard/helpers"
)

type SnapshotClassForm struct {
	*gr.This
}

// Implements the StateInitializer interface
func (s SnapshotClassForm) GetInitialState() gr.State {
	return gr.State{"querying": true, "queryingOpts": true, "queryingVolumes": true, "error": "", "success": "",
		"description":      "",
		"rotate":           false,
		"propagate":        false,
		"propagateRegions": []string{},
		"step":             1,
		"volume":           "",
	}
}

// Implements the ComponentWillMount interface
func (s SnapshotClassForm) ComponentWillMount() {
	var class map[string]interface{}

	if s.Props().Interface("class") != nil {
		classJson := s.Props().Interface("class").([]byte)
		json.Unmarshal(classJson, &class)
	}

	s.SetState(class)
	s.SetState(gr.State{"queryingOpts": true, "queryingVolumes": true})

	// Get our options for the form
	go func() {
		endpoint := "//localhost:8081/api/classes/" + s.Props().String("apiType") + "/options"
		resp, err := helpers.GetAPI(endpoint)
		if !s.IsMounted() {
			return
		}
		if err != nil {
			s.SetState(gr.State{"queryingOpts": false, "error": fmt.Sprintf("Error while querying endpoint: %s", endpoint)})
			return
		}

		s.SetState(gr.State{"classOptionsResp": resp, "queryingOpts": false})
	}()

	// Get our existing instances for the form
	go func() {
		endpoint := "//localhost:8081/api/assets/volumes"
		resp, err := helpers.GetAPI(endpoint)
		if !s.IsMounted() {
			return
		}
		if err != nil {
			s.SetState(gr.State{"queryingVolumes": false, "error": fmt.Sprintf("Error while querying endpoint: %s", endpoint)})
			return
		}

		s.SetState(gr.State{"volumeOptionsResp": resp, "queryingVolumes": false})
	}()
}

func (s SnapshotClassForm) Render() gr.Component {

	state := s.State()
	props := s.Props()

	// Form placeholder
	response := el.Div()

	// Print any alerts
	helpers.ErrorElem(state.String("error")).Modify(response)
	helpers.SuccessElem(state.String("success")).Modify(response)

	if state.Int("step") == 1 {
		if state.Bool("queryingOpts") || state.Bool("queryingVolumes") {
			gr.Text("Loading...").Modify(response)
		} else {
			s.BuildClassForm(props.String("className"), state.Interface("classOptionsResp"), state.Interface("volumeOptionsResp")).Modify(response)
		}

	} else if state.Int("step") == 2 {

		if state.Bool("querying") {
			gr.Text("Saving...").Modify(response)
		} else {

			buttons := el.Div(
				gr.CSS("btn-toolbar"),
			)

			// Back
			el.Button(
				evt.Click(s.backButton).PreventDefault(),
				gr.CSS("btn", "btn-secondary"),
				gr.Text("Back"),
			).Modify(buttons)

			// Done
			el.Button(
				evt.Click(s.doneButton).PreventDefault(),
				gr.CSS("btn", "btn-primary"),
				gr.Text("Done"),
			).Modify(buttons)

			buttons.Modify(response)
		}

	}

	return response
}

func (s SnapshotClassForm) BuildClassForm(className string, optionsResp interface{}, volumeResp interface{}) *gr.Element {

	state := s.State()
	props := s.Props()

	var classOptions map[string][]string
	jsonParsed, _ := gabs.ParseJSON(optionsResp.([]byte))
	classOptionsJson := jsonParsed.S("classOptions").Bytes()
	json.Unmarshal(classOptionsJson, &classOptions)

	volumeJsonParsed, _ := gabs.ParseJSON(volumeResp.([]byte))
	volumeOptionsSlice, _ := volumeJsonParsed.S("assets").Children()

	var volumes []string
	volumesMeta := make(map[string]string)
	for _, volumeOption := range volumeOptionsSlice {
		volume := volumeOption.S("volumeID").Data().(string)
		if volume != "" {
			volumes = append(volumes, volume)
			volumesMeta[volume] = volumeOption.S("sizeHuman").Data().(string) + " " + volumeOption.S("name").Data().(string) + " " + volumeOption.S("attachment").Data().(string)
		}
	}

	classEdit := el.Div(
		el.Header3(gr.Text(className)),
		el.HorizontalRule(),
	)

	classEditForm := el.Form()

	TextField("Description", "description", state.String("description"), s.storeValue).Modify(classEditForm)

	Checkbox("Rotate", "rotate", state.Bool("rotate"), s.storeValue).Modify(classEditForm)
	if state.Bool("rotate") {
		NumberField("Retain", "retain", state.Int("retain"), s.storeValue).Modify(classEditForm)
	}
	Checkbox("Propagate", "propagate", state.Bool("propagate"), s.storeValue).Modify(classEditForm)
	if state.Bool("propagate") {
		SelectMultiple("Propagate Regions", "propagateRegions", classOptions["regions"], state.Interface("propagateRegions"), s.storeSelect).Modify(classEditForm)
	}

	SelectOneMeta("Volume", "volume", volumes, volumesMeta, state.Interface("volume"), s.storeSelect).Modify(classEditForm)

	classEditForm.Modify(classEdit)

	buttons := el.Div(
		gr.CSS("btn-toolbar"),
	)

	// Back
	el.Button(
		evt.Click(s.backButton).PreventDefault(),
		gr.CSS("btn", "btn-secondary"),
		gr.Text("Back"),
	).Modify(buttons)

	// Save
	el.Button(
		evt.Click(s.saveButton).PreventDefault(),
		gr.CSS("btn", "btn-primary"),
		gr.Text("Save"),
	).Modify(buttons)

	// Delete
	if props.Interface("hasDelete") != nil && props.Bool("hasDelete") {
		el.Button(
			evt.Click(s.deleteButton).PreventDefault(),
			gr.CSS("btn", "btn-danger", "pull-right"),
			gr.Text("Delete"),
		).Modify(buttons)
	}

	buttons.Modify(classEdit)

	return classEdit

}

func (s SnapshotClassForm) backButton(*gr.Event) {
	s.SetState(gr.State{"success": ""})
	s.Props().Call("backButton")
}

func (s SnapshotClassForm) doneButton(*gr.Event) {
	s.SetState(gr.State{"success": ""})
	s.Props().Call("hideAllModals")
}

func (s SnapshotClassForm) saveButton(*gr.Event) {
	s.SetState(gr.State{"querying": true, "step": 2})

	cfg := make(map[string]interface{})
	for key, _ := range s.State() {
		cfg[key] = s.State().Interface(key)
	}

	go func() {
		endpoint := "//localhost:8081/api/classes/" + s.Props().String("apiType") + "/name/" + s.Props().String("className")

		_, err := helpers.PutAPI(endpoint, cfg)
		if !s.IsMounted() {
			return
		}

		if err != nil {
			s.SetState(gr.State{"querying": false, "error": fmt.Sprintf("Error while querying endpoint: %s", endpoint), "step": 1})
			return
		}

		s.SetState(gr.State{"querying": false, "success": "Class was saved", "error": ""})
	}()

}

func (s SnapshotClassForm) deleteButton(*gr.Event) {
	s.SetState(gr.State{"querying": true})

	go func() {
		endpoint := "//localhost:8081/api/classes/" + s.Props().String("apiType") + "/name/" + s.Props().String("className")

		_, err := helpers.DeleteAPI(endpoint)
		if !s.IsMounted() {
			return
		}

		if err != nil {
			s.SetState(gr.State{"querying": false, "error": fmt.Sprintf("Error while querying endpoint: %s", endpoint)})
			return
		}

		s.SetState(gr.State{"querying": false, "success": "Class was deleted", "error": "", "step": 2})
	}()
}

func (s SnapshotClassForm) storeValue(event *gr.Event) {
	id := event.Target().Get("id").String()
	inputType := event.Target().Get("type").String()

	switch inputType {

	case "checkbox":
		s.SetState(gr.State{id: event.Target().Get("checked").Bool()})

	case "number":
		s.SetState(gr.State{id: event.TargetValue().Int()})

	default: // text, at least
		s.SetState(gr.State{id: event.TargetValue()})

	}
}

func (s SnapshotClassForm) storeSelect(id string, val interface{}) {
	switch value := val.(type) {

	case map[string]interface{}:
		// single
		s.SetState(gr.State{id: value["value"]})

	case []interface{}:
		// multi
		var vals []string
		options := len(value)
		for i := 0; i < options; i++ {
			vals = append(vals, value[i].(map[string]interface{})["value"].(string))
		}
		s.SetState(gr.State{id: vals})

	default:
		s.SetState(gr.State{id: val})

	}
}
<|endoftext|>