<commit_before>package k8s

import (
	"time"

	"k8s.io/client-go/1.5/kubernetes"
	"k8s.io/client-go/1.5/pkg/api"
	"k8s.io/client-go/1.5/pkg/api/v1"
	"k8s.io/client-go/1.5/pkg/apis/extensions/v1beta1"
	"k8s.io/client-go/1.5/pkg/fields"
	"k8s.io/client-go/1.5/pkg/labels"
	"k8s.io/client-go/1.5/pkg/runtime"
	"k8s.io/client-go/1.5/pkg/watch"
	"k8s.io/client-go/1.5/rest"
	"k8s.io/client-go/1.5/tools/cache"
)

const resyncPeriod = time.Minute * 5

// Client is a client for the Kubernetes master.
type Client interface {
	GetIngresses(namespaces Namespaces) []*v1beta1.Ingress
	GetService(namespace, name string) (*v1.Service, bool, error)
	GetEndpoints(namespace, name string) (*v1.Endpoints, bool, error)
	WatchAll(labelSelector string, stopCh <-chan bool) (chan interface{}, error)
}

type clientImpl struct {
	ingController *cache.Controller
	svcController *cache.Controller
	epController  *cache.Controller

	ingStore cache.Store
	svcStore cache.Store
	epStore  cache.Store

	clientset *kubernetes.Clientset
}

// NewInClusterClient returns a new Kubernetes client.
// WatchAll starts the watch of the Kubernetes ressources and updates the stores.
// The stores can be accessed via the Get* functions.
func NewInClusterClient() (Client, error) {
	config, err := rest.InClusterConfig()
	if err != nil {
		return nil, err
	}
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	return &clientImpl{
		clientset: clientset,
	}, nil
}

// NewInClusterClientWithEndpoint is the same as NewInClusterClient but uses the provided endpoint URL
func NewInClusterClientWithEndpoint(endpoint string) (Client, error) {
	config, err := rest.InClusterConfig()
	if err != nil {
		return nil, err
	}

	config.Host = endpoint

	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	return &clientImpl{
		clientset: clientset,
	}, nil
}

// GetIngresses returns all ingresses in the cluster
func (c *clientImpl) GetIngresses(namespaces Namespaces) []*v1beta1.Ingress {
	ingList := c.ingStore.List()
	result := make([]*v1beta1.Ingress, 0, len(ingList))

	for _, obj := range ingList {
		ingress := obj.(*v1beta1.Ingress)
		if HasNamespace(ingress, namespaces) {
			result = append(result, ingress)
		}
	}

	return result
}

// WatchIngresses starts the watch of Kubernetes Ingresses resources and updates the corresponding store
func (c *clientImpl) WatchIngresses(labelSelector labels.Selector, stopCh <-chan struct{}) chan interface{} {
	watchCh := make(chan interface{}, 10)

	source := NewListWatchFromClient(
		c.clientset.ExtensionsClient,
		"ingresses",
		api.NamespaceAll,
		fields.Everything(),
		labelSelector)

	c.ingStore, c.ingController = cache.NewInformer(
		source,
		&v1beta1.Ingress{},
		resyncPeriod,
		newResourceEventHandlerFuncs(watchCh))
	go c.ingController.Run(stopCh)

	return watchCh
}

func newResourceEventHandlerFuncs(events chan interface{}) cache.ResourceEventHandlerFuncs {

	return cache.ResourceEventHandlerFuncs{
		AddFunc:    func(obj interface{}) { events <- obj },
		UpdateFunc: func(old, new interface{}) { events <- new },
		DeleteFunc: func(obj interface{}) { events <- obj },
	}

}

// GetService returns the named service from the named namespace
func (c *clientImpl) GetService(namespace, name string) (*v1.Service, bool, error) {
	var service *v1.Service
	item, exists, err := c.svcStore.GetByKey(namespace + "/" + name)
	if item != nil {
		service = item.(*v1.Service)
	}

	return service, exists, err
}

// WatchServices starts the watch of Kubernetes Service resources and updates the corresponding store
func (c *clientImpl) WatchServices(stopCh <-chan struct{}) chan interface{} {
	watchCh := make(chan interface{}, 10)

	source := cache.NewListWatchFromClient(
		c.clientset.CoreClient,
		"services",
		api.NamespaceAll,
		fields.Everything())

	c.svcStore, c.svcController = cache.NewInformer(
		source,
		&v1.Service{},
		resyncPeriod,
		newResourceEventHandlerFuncs(watchCh))
	go c.svcController.Run(stopCh)

	return watchCh
}

// GetEndpoints returns the named Endpoints
// Endpoints have the same name as the coresponding service
func (c *clientImpl) GetEndpoints(namespace, name string) (*v1.Endpoints, bool, error) {
	var endpoint *v1.Endpoints
	item, exists, err := c.epStore.GetByKey(namespace + "/" + name)

	if item != nil {
		endpoint = item.(*v1.Endpoints)
	}

	return endpoint, exists, err
}

// WatchEndpoints starts the watch of Kubernetes Endpoints resources and updates the corresponding store
func (c *clientImpl) WatchEndpoints(stopCh <-chan struct{}) chan interface{} {
	watchCh := make(chan interface{}, 10)

	source := cache.NewListWatchFromClient(
		c.clientset.CoreClient,
		"endpoints",
		api.NamespaceAll,
		fields.Everything())

	c.epStore, c.epController = cache.NewInformer(
		source,
		&v1.Endpoints{},
		resyncPeriod,
		newResourceEventHandlerFuncs(watchCh))
	go c.epController.Run(stopCh)

	return watchCh
}

// WatchAll returns events in the cluster and updates the stores via informer
// Filters ingresses by labelSelector
func (c *clientImpl) WatchAll(labelSelector string, stopCh <-chan bool) (chan interface{}, error) {
	watchCh := make(chan interface{}, 100)
	stopWatchCh := make(chan struct{}, 1)

	kubeLabelSelector, err := labels.Parse(labelSelector)
	if err != nil {
		return nil, err
	}

	chanIngresses := c.WatchIngresses(kubeLabelSelector, stopWatchCh)
	chanServices := c.WatchServices(stopWatchCh)
	chanEndpoints := c.WatchEndpoints(stopWatchCh)

	go func() {
		defer close(stopWatchCh)
		defer close(watchCh)

		for {
			select {
			case <-stopCh:
				return
			case event := <-chanIngresses:
				c.fireEvent(event, watchCh)
			case event := <-chanServices:
				c.fireEvent(event, watchCh)
			case event := <-chanEndpoints:
				c.fireEvent(event, watchCh)
			}
		}
	}()

	return watchCh, nil
}

// fireEvent checks if all controllers have synced before firing
// Used after startup or a reconnect
func (c *clientImpl) fireEvent(event interface{}, watchCh chan interface{}) {
	if c.ingController.HasSynced() && c.svcController.HasSynced() && c.epController.HasSynced() {
		watchCh <- event
	}
}

// HasNamespace checks if the ingress is in one of the namespaces
func HasNamespace(ingress *v1beta1.Ingress, namespaces Namespaces) bool {
	if len(namespaces) == 0 {
		return true
	}
	for _, n := range namespaces {
		if ingress.ObjectMeta.Namespace == n {
			return true
		}
	}
	return false
}

// NewListWatchFromClient creates a new ListWatch from the specified client, resource, namespace, field selector and label selector.
// Extends cache.NewListWatchFromClient to support labelSelector
func NewListWatchFromClient(c cache.Getter, resource string, namespace string, fieldSelector fields.Selector, labelSelector labels.Selector) *cache.ListWatch {
	listFunc := func(options api.ListOptions) (runtime.Object, error) {
		return c.Get().
			Namespace(namespace).
			Resource(resource).
			VersionedParams(&options, api.ParameterCodec).
			FieldsSelectorParam(fieldSelector).
			LabelsSelectorParam(labelSelector).
			Do().
			Get()
	}
	watchFunc := func(options api.ListOptions) (watch.Interface, error) {
		return c.Get().
			Prefix("watch").
			Namespace(namespace).
			Resource(resource).
			VersionedParams(&options, api.ParameterCodec).
			FieldsSelectorParam(fieldSelector).
			LabelsSelectorParam(labelSelector).
			Watch()
	}
	return &cache.ListWatch{ListFunc: listFunc, WatchFunc: watchFunc}
}
<commit_msg>kubernetes: moved doc of client to the interface<commit_after>package k8s

import (
	"time"

	"k8s.io/client-go/1.5/kubernetes"
	"k8s.io/client-go/1.5/pkg/api"
	"k8s.io/client-go/1.5/pkg/api/v1"
	"k8s.io/client-go/1.5/pkg/apis/extensions/v1beta1"
	"k8s.io/client-go/1.5/pkg/fields"
	"k8s.io/client-go/1.5/pkg/labels"
	"k8s.io/client-go/1.5/pkg/runtime"
	"k8s.io/client-go/1.5/pkg/watch"
	"k8s.io/client-go/1.5/rest"
	"k8s.io/client-go/1.5/tools/cache"
)

const resyncPeriod = time.Minute * 5

// Client is a client for the Kubernetes master.
// WatchAll starts the watch of the Kubernetes ressources and updates the stores.
// The stores can then be accessed via the Get* functions.
type Client interface {
	GetIngresses(namespaces Namespaces) []*v1beta1.Ingress
	GetService(namespace, name string) (*v1.Service, bool, error)
	GetEndpoints(namespace, name string) (*v1.Endpoints, bool, error)
	WatchAll(labelSelector string, stopCh <-chan bool) (chan interface{}, error)
}

type clientImpl struct {
	ingController *cache.Controller
	svcController *cache.Controller
	epController  *cache.Controller

	ingStore cache.Store
	svcStore cache.Store
	epStore  cache.Store

	clientset *kubernetes.Clientset
}

// NewInClusterClient returns a new Kubernetes client that expect to run inside the cluster
func NewInClusterClient() (Client, error) {
	config, err := rest.InClusterConfig()
	if err != nil {
		return nil, err
	}
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	return &clientImpl{
		clientset: clientset,
	}, nil
}

// NewInClusterClientWithEndpoint is the same as NewInClusterClient but uses the provided endpoint URL
func NewInClusterClientWithEndpoint(endpoint string) (Client, error) {
	config, err := rest.InClusterConfig()
	if err != nil {
		return nil, err
	}

	config.Host = endpoint

	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	return &clientImpl{
		clientset: clientset,
	}, nil
}

// GetIngresses returns all ingresses in the cluster
func (c *clientImpl) GetIngresses(namespaces Namespaces) []*v1beta1.Ingress {
	ingList := c.ingStore.List()
	result := make([]*v1beta1.Ingress, 0, len(ingList))

	for _, obj := range ingList {
		ingress := obj.(*v1beta1.Ingress)
		if HasNamespace(ingress, namespaces) {
			result = append(result, ingress)
		}
	}

	return result
}

// WatchIngresses starts the watch of Kubernetes Ingresses resources and updates the corresponding store
func (c *clientImpl) WatchIngresses(labelSelector labels.Selector, stopCh <-chan struct{}) chan interface{} {
	watchCh := make(chan interface{}, 10)

	source := NewListWatchFromClient(
		c.clientset.ExtensionsClient,
		"ingresses",
		api.NamespaceAll,
		fields.Everything(),
		labelSelector)

	c.ingStore, c.ingController = cache.NewInformer(
		source,
		&v1beta1.Ingress{},
		resyncPeriod,
		newResourceEventHandlerFuncs(watchCh))
	go c.ingController.Run(stopCh)

	return watchCh
}

func newResourceEventHandlerFuncs(events chan interface{}) cache.ResourceEventHandlerFuncs {

	return cache.ResourceEventHandlerFuncs{
		AddFunc:    func(obj interface{}) { events <- obj },
		UpdateFunc: func(old, new interface{}) { events <- new },
		DeleteFunc: func(obj interface{}) { events <- obj },
	}

}

// GetService returns the named service from the named namespace
func (c *clientImpl) GetService(namespace, name string) (*v1.Service, bool, error) {
	var service *v1.Service
	item, exists, err := c.svcStore.GetByKey(namespace + "/" + name)
	if item != nil {
		service = item.(*v1.Service)
	}

	return service, exists, err
}

// WatchServices starts the watch of Kubernetes Service resources and updates the corresponding store
func (c *clientImpl) WatchServices(stopCh <-chan struct{}) chan interface{} {
	watchCh := make(chan interface{}, 10)

	source := cache.NewListWatchFromClient(
		c.clientset.CoreClient,
		"services",
		api.NamespaceAll,
		fields.Everything())

	c.svcStore, c.svcController = cache.NewInformer(
		source,
		&v1.Service{},
		resyncPeriod,
		newResourceEventHandlerFuncs(watchCh))
	go c.svcController.Run(stopCh)

	return watchCh
}

// GetEndpoints returns the named Endpoints
// Endpoints have the same name as the coresponding service
func (c *clientImpl) GetEndpoints(namespace, name string) (*v1.Endpoints, bool, error) {
	var endpoint *v1.Endpoints
	item, exists, err := c.epStore.GetByKey(namespace + "/" + name)

	if item != nil {
		endpoint = item.(*v1.Endpoints)
	}

	return endpoint, exists, err
}

// WatchEndpoints starts the watch of Kubernetes Endpoints resources and updates the corresponding store
func (c *clientImpl) WatchEndpoints(stopCh <-chan struct{}) chan interface{} {
	watchCh := make(chan interface{}, 10)

	source := cache.NewListWatchFromClient(
		c.clientset.CoreClient,
		"endpoints",
		api.NamespaceAll,
		fields.Everything())

	c.epStore, c.epController = cache.NewInformer(
		source,
		&v1.Endpoints{},
		resyncPeriod,
		newResourceEventHandlerFuncs(watchCh))
	go c.epController.Run(stopCh)

	return watchCh
}

// WatchAll returns events in the cluster and updates the stores via informer
// Filters ingresses by labelSelector
func (c *clientImpl) WatchAll(labelSelector string, stopCh <-chan bool) (chan interface{}, error) {
	watchCh := make(chan interface{}, 100)
	stopWatchCh := make(chan struct{}, 1)

	kubeLabelSelector, err := labels.Parse(labelSelector)
	if err != nil {
		return nil, err
	}

	chanIngresses := c.WatchIngresses(kubeLabelSelector, stopWatchCh)
	chanServices := c.WatchServices(stopWatchCh)
	chanEndpoints := c.WatchEndpoints(stopWatchCh)

	go func() {
		defer close(stopWatchCh)
		defer close(watchCh)

		for {
			select {
			case <-stopCh:
				return
			case event := <-chanIngresses:
				c.fireEvent(event, watchCh)
			case event := <-chanServices:
				c.fireEvent(event, watchCh)
			case event := <-chanEndpoints:
				c.fireEvent(event, watchCh)
			}
		}
	}()

	return watchCh, nil
}

// fireEvent checks if all controllers have synced before firing
// Used after startup or a reconnect
func (c *clientImpl) fireEvent(event interface{}, watchCh chan interface{}) {
	if c.ingController.HasSynced() && c.svcController.HasSynced() && c.epController.HasSynced() {
		watchCh <- event
	}
}

// HasNamespace checks if the ingress is in one of the namespaces
func HasNamespace(ingress *v1beta1.Ingress, namespaces Namespaces) bool {
	if len(namespaces) == 0 {
		return true
	}
	for _, n := range namespaces {
		if ingress.ObjectMeta.Namespace == n {
			return true
		}
	}
	return false
}

// NewListWatchFromClient creates a new ListWatch from the specified client, resource, namespace, field selector and label selector.
// Extends cache.NewListWatchFromClient to support labelSelector
func NewListWatchFromClient(c cache.Getter, resource string, namespace string, fieldSelector fields.Selector, labelSelector labels.Selector) *cache.ListWatch {
	listFunc := func(options api.ListOptions) (runtime.Object, error) {
		return c.Get().
			Namespace(namespace).
			Resource(resource).
			VersionedParams(&options, api.ParameterCodec).
			FieldsSelectorParam(fieldSelector).
			LabelsSelectorParam(labelSelector).
			Do().
			Get()
	}
	watchFunc := func(options api.ListOptions) (watch.Interface, error) {
		return c.Get().
			Prefix("watch").
			Namespace(namespace).
			Resource(resource).
			VersionedParams(&options, api.ParameterCodec).
			FieldsSelectorParam(fieldSelector).
			LabelsSelectorParam(labelSelector).
			Watch()
	}
	return &cache.ListWatch{ListFunc: listFunc, WatchFunc: watchFunc}
}
<|endoftext|>


<commit_before>package responses

// APISuccess represents an error that can be sent in an error response.
type APISuccess struct {
	// Status represents the HTTP status code
	Status int `json:"200"`
	// Message is the error message that may be displayed to end users
	Message string `json:"ok"`
	// Data specifies the additional response information
	Data struct{} `json:"data,omitempty"`
}
<commit_msg>api_responses<commit_after>package responses

import "encoding/json"

// APISuccess represents an error that can be sent in an error response.
type APISuccess struct {
	// Status represents the HTTP status code
	Status int `json:"status"`
	// Message is the error message that may be displayed to end users
	Message string `json:"message"`
	// Data specifies the additional response information
	Data struct{} `json:"data,omitempty"`
}

func (s *APISuccess) MarshalJSON() ([]byte, error) {
	return json.Marshal(&APISuccess{
		Status:       200,
		Message:     "ok",
	})
}<|endoftext|>


<commit_before>package proxmox

import (
	"encoding/json"
	"errors"
	"io"
	"log"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type ConfigQemu struct {
	Name         string  `json:"name"`
	Description  string  `json:"desc"`
	Memory       int     `json:"memory"`
	DiskSize     float64 `json:"diskGB"`
	Storage      string  `json:"storage"`
	QemuOs       string  `json:"os"`
	QemuCores    int     `json:"cores"`
	QemuSockets  int     `json:"sockets"`
	QemuIso      string  `json:"iso"`
	QemuNicModel string  `json:"nic"`
	QemuBrige    string  `json:"bridge"`
	QemuVlanTag  int     `json:"vlan"`
	FullClone    *int    `json:"fullclone"`
}

func (config ConfigQemu) CreateVm(vmr *VmRef, client *Client) (err error) {
	vmr.SetVmType("qemu")
	network := config.QemuNicModel + ",bridge=" + config.QemuBrige
	if config.QemuVlanTag > 0 {
		network = network + ",tag=" + strconv.Itoa(config.QemuVlanTag)
	}

	params := map[string]string{
		"vmid":        strconv.Itoa(vmr.vmId),
		"name":        config.Name,
		"ide2":        config.QemuIso + ",media=cdrom",
		"ostype":      config.QemuOs,
		"virtio0":     config.Storage + ":" + strconv.FormatFloat(config.DiskSize, 'f', -1, 64),
		"sockets":     strconv.Itoa(config.QemuSockets),
		"cores":       strconv.Itoa(config.QemuCores),
		"cpu":         "host",
		"memory":      strconv.Itoa(config.Memory),
		"net0":        network,
		"description": config.Description,
	}

	_, err = client.CreateQemuVm(vmr.node, params)
	return
}

/*

CloneVm
Example: Request

nodes/proxmox1-xx/qemu/1012/clone

newid:145
name:tf-clone1
target:proxmox1-xx
full:1
storage:xxx

*/
func (config ConfigQemu) CloneVm(sourceVmr *VmRef, vmr *VmRef, client *Client) (err error) {
	vmr.SetVmType("qemu")
	fullclone := "1"
	if config.FullClone != nil {
		fullclone = strconv.Itoa(*config.FullClone)
	}
	params := map[string]string{
		"newid":   strconv.Itoa(vmr.vmId),
		"target":  vmr.node,
		"name":    config.Name,
		"storage": config.Storage,
		"full":    fullclone,
	}
	_, err = client.CloneQemuVm(sourceVmr, params)
	if err != nil {
		return
	}
	return config.UpdateConfig(vmr, client)
}

func (config ConfigQemu) UpdateConfig(vmr *VmRef, client *Client) (err error) {
	network := config.QemuNicModel + ",bridge=" + config.QemuBrige
	if config.QemuVlanTag > 0 {
		network = network + ",tag=" + strconv.Itoa(config.QemuVlanTag)
	}
	configParams := map[string]string{
		"sockets":     strconv.Itoa(config.QemuSockets),
		"cores":       strconv.Itoa(config.QemuCores),
		"memory":      strconv.Itoa(config.Memory),
		"net0":        network,
		"description": config.Description,
	}
	_, err = client.SetVmConfig(vmr, configParams)
	return err
}

func NewConfigQemuFromJson(io io.Reader) (config *ConfigQemu, err error) {
	config = &ConfigQemu{QemuVlanTag: -1}
	err = json.NewDecoder(io).Decode(config)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}
	log.Println(config)
	return
}

var rxStorage = regexp.MustCompile("(.*?):.*?,size=(\\d+)G")
var rxIso = regexp.MustCompile("(.*?),media")
var rxNetwork = regexp.MustCompile("(.*?)=.*?,bridge=([^,]+)(?:,tag=)?(.*)")

func NewConfigQemuFromApi(vmr *VmRef, client *Client) (config *ConfigQemu, err error) {
	vmConfig, err := client.GetVmConfig(vmr)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}

	// vmConfig Sample: map[ cpu:host
	// net0:virtio=62:DF:XX:XX:XX:XX,bridge=vmbr0
	// ide2:local:iso/xxx-xx.iso,media=cdrom memory:2048
	// smbios1:uuid=8b3bf833-aad8-4545-xxx-xxxxxxx digest:aa6ce5xxxxx1b9ce33e4aaeff564d4 sockets:1
	// name:terraform-ubuntu1404-template bootdisk:virtio0
	// virtio0:ProxmoxxxxISCSI:vm-1014-disk-2,size=4G
	// description:Base image
	// cores:2 ostype:l26

	fullclone := 1
	if vmConfig["fullclone"] != nil {
		fullclone = int(vmConfig["fullclone"].(float64))
	}
	config = &ConfigQemu{
		Name:        vmConfig["name"].(string),
		Description: strings.TrimSpace(vmConfig["description"].(string)),
		QemuOs:      vmConfig["ostype"].(string),
		Memory:      int(vmConfig["memory"].(float64)),
		QemuCores:   int(vmConfig["cores"].(float64)),
		QemuSockets: int(vmConfig["sockets"].(float64)),
		QemuVlanTag: -1,
		FullClone:   &fullclone,
	}

	storageMatch := rxStorage.FindStringSubmatch(vmConfig["virtio0"].(string))
	config.Storage = storageMatch[1]
	config.DiskSize, _ = strconv.ParseFloat(storageMatch[2], 64)

	if vmConfig["ide2"] != nil {
		isoMatch := rxIso.FindStringSubmatch(vmConfig["ide2"].(string))
		config.QemuIso = isoMatch[1]
	}

	netMatch := rxNetwork.FindStringSubmatch(vmConfig["net0"].(string))
	config.QemuNicModel = netMatch[1]
	config.QemuBrige = netMatch[2]
	if netMatch[3] != "" {
		config.QemuVlanTag, _ = strconv.Atoi(netMatch[3])
	}

	return
}

// Useful waiting for ISO install to complete
func WaitForShutdown(vmr *VmRef, client *Client) (err error) {
	for ii := 0; ii < 100; ii++ {
		vmState, err := client.GetVmState(vmr)
		if err != nil {
			log.Print("Wait error:")
			log.Println(err)
		} else if vmState["status"] == "stopped" {
			return nil
		}
		time.Sleep(5 * time.Second)
	}
	return errors.New("Not shutdown within wait time")
}

// This is because proxmox create/config API won't let us make usernet devices
func SshForwardUsernet(vmr *VmRef, client *Client) (sshPort string, err error) {
	vmState, err := client.GetVmState(vmr)
	if err != nil {
		return "", err
	}
	if vmState["status"] == "stopped" {
		return "", errors.New("VM must be running first")
	}
	sshPort = strconv.Itoa(vmr.VmId() + 22000)
	_, err = client.MonitorCmd(vmr, "netdev_add user,id=net1,hostfwd=tcp::"+sshPort+"-:22")
	if err != nil {
		return "", err
	}
	_, err = client.MonitorCmd(vmr, "device_add virtio-net-pci,id=net1,netdev=net1,addr=0x13")
	if err != nil {
		return "", err
	}
	return
}

// device_del net1
// netdev_del net1
func RemoveSshForwardUsernet(vmr *VmRef, client *Client) (err error) {
	vmState, err := client.GetVmState(vmr)
	if err != nil {
		return err
	}
	if vmState["status"] == "stopped" {
		return errors.New("VM must be running first")
	}
	_, err = client.MonitorCmd(vmr, "device_del net1")
	if err != nil {
		return err
	}
	_, err = client.MonitorCmd(vmr, "netdev_del net1")
	if err != nil {
		return err
	}
	return nil
}

func MaxVmId(client *Client) (max int, err error) {
	resp, err := client.GetVmList()
	vms := resp["data"].([]interface{})
	max = 0
	for vmii := range vms {
		vm := vms[vmii].(map[string]interface{})
		vmid := int(vm["vmid"].(float64))
		if vmid > max {
			max = vmid
		}
	}
	return
}

func SendKeysString(vmr *VmRef, client *Client, keys string) (err error) {
	vmState, err := client.GetVmState(vmr)
	if err != nil {
		return err
	}
	if vmState["status"] == "stopped" {
		return errors.New("VM must be running first")
	}
	for _, r := range keys {
		c := string(r)
		lower := strings.ToLower(c)
		if c != lower {
			c = "shift-" + lower
		} else {
			switch c {
			case "!":
				c = "shift-1"
			case "@":
				c = "shift-2"
			case "#":
				c = "shift-3"
			case "$":
				c = "shift-4"
			case "%%":
				c = "shift-5"
			case "^":
				c = "shift-6"
			case "&":
				c = "shift-7"
			case "*":
				c = "shift-8"
			case "(":
				c = "shift-9"
			case ")":
				c = "shift-0"
			case "_":
				c = "shift-minus"
			case "+":
				c = "shift-equal"
			case " ":
				c = "spc"
			case "/":
				c = "slash"
			case "\\":
				c = "backslash"
			case ",":
				c = "comma"
			case "-":
				c = "minus"
			case "=":
				c = "equal"
			case ".":
				c = "dot"
			case "?":
				c = "shift-slash"
			}
		}
		_, err = client.MonitorCmd(vmr, "sendkey "+c)
		if err != nil {
			return err
		}
		time.Sleep(100)
	}
	return nil
}
<commit_msg>return more informative error messages on config read errors<commit_after>package proxmox

import (
	"encoding/json"
	"errors"
	"io"
	"log"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type ConfigQemu struct {
	Name         string  `json:"name"`
	Description  string  `json:"desc"`
	Memory       int     `json:"memory"`
	DiskSize     float64 `json:"diskGB"`
	Storage      string  `json:"storage"`
	QemuOs       string  `json:"os"`
	QemuCores    int     `json:"cores"`
	QemuSockets  int     `json:"sockets"`
	QemuIso      string  `json:"iso"`
	QemuNicModel string  `json:"nic"`
	QemuBrige    string  `json:"bridge"`
	QemuVlanTag  int     `json:"vlan"`
	FullClone    *int    `json:"fullclone"`
}

func (config ConfigQemu) CreateVm(vmr *VmRef, client *Client) (err error) {
	vmr.SetVmType("qemu")
	network := config.QemuNicModel + ",bridge=" + config.QemuBrige
	if config.QemuVlanTag > 0 {
		network = network + ",tag=" + strconv.Itoa(config.QemuVlanTag)
	}

	params := map[string]string{
		"vmid":        strconv.Itoa(vmr.vmId),
		"name":        config.Name,
		"ide2":        config.QemuIso + ",media=cdrom",
		"ostype":      config.QemuOs,
		"virtio0":     config.Storage + ":" + strconv.FormatFloat(config.DiskSize, 'f', -1, 64),
		"sockets":     strconv.Itoa(config.QemuSockets),
		"cores":       strconv.Itoa(config.QemuCores),
		"cpu":         "host",
		"memory":      strconv.Itoa(config.Memory),
		"net0":        network,
		"description": config.Description,
	}

	_, err = client.CreateQemuVm(vmr.node, params)
	return
}

/*

CloneVm
Example: Request

nodes/proxmox1-xx/qemu/1012/clone

newid:145
name:tf-clone1
target:proxmox1-xx
full:1
storage:xxx

*/
func (config ConfigQemu) CloneVm(sourceVmr *VmRef, vmr *VmRef, client *Client) (err error) {
	vmr.SetVmType("qemu")
	fullclone := "1"
	if config.FullClone != nil {
		fullclone = strconv.Itoa(*config.FullClone)
	}
	params := map[string]string{
		"newid":   strconv.Itoa(vmr.vmId),
		"target":  vmr.node,
		"name":    config.Name,
		"storage": config.Storage,
		"full":    fullclone,
	}
	_, err = client.CloneQemuVm(sourceVmr, params)
	if err != nil {
		return
	}
	return config.UpdateConfig(vmr, client)
}

func (config ConfigQemu) UpdateConfig(vmr *VmRef, client *Client) (err error) {
	network := config.QemuNicModel + ",bridge=" + config.QemuBrige
	if config.QemuVlanTag > 0 {
		network = network + ",tag=" + strconv.Itoa(config.QemuVlanTag)
	}
	configParams := map[string]string{
		"sockets":     strconv.Itoa(config.QemuSockets),
		"cores":       strconv.Itoa(config.QemuCores),
		"memory":      strconv.Itoa(config.Memory),
		"net0":        network,
		"description": config.Description,
	}
	_, err = client.SetVmConfig(vmr, configParams)
	return err
}

func NewConfigQemuFromJson(io io.Reader) (config *ConfigQemu, err error) {
	config = &ConfigQemu{QemuVlanTag: -1}
	err = json.NewDecoder(io).Decode(config)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}
	log.Println(config)
	return
}

var rxStorage = regexp.MustCompile("(.*?):.*?,size=(\\d+)G")
var rxIso = regexp.MustCompile("(.*?),media")
var rxNetwork = regexp.MustCompile("(.*?)=.*?,bridge=([^,]+)(?:,tag=)?(.*)")

func NewConfigQemuFromApi(vmr *VmRef, client *Client) (config *ConfigQemu, err error) {
	vmConfig, err := client.GetVmConfig(vmr)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}

	// vmConfig Sample: map[ cpu:host
	// net0:virtio=62:DF:XX:XX:XX:XX,bridge=vmbr0
	// ide2:local:iso/xxx-xx.iso,media=cdrom memory:2048
	// smbios1:uuid=8b3bf833-aad8-4545-xxx-xxxxxxx digest:aa6ce5xxxxx1b9ce33e4aaeff564d4 sockets:1
	// name:terraform-ubuntu1404-template bootdisk:virtio0
	// virtio0:ProxmoxxxxISCSI:vm-1014-disk-2,size=4G
	// description:Base image
	// cores:2 ostype:l26

	fullclone := 1
	if vmConfig["fullclone"] != nil {
		fullclone = int(vmConfig["fullclone"].(float64))
	}
	config = &ConfigQemu{
		Name:        vmConfig["name"].(string),
		Description: strings.TrimSpace(vmConfig["description"].(string)),
		QemuOs:      vmConfig["ostype"].(string),
		Memory:      int(vmConfig["memory"].(float64)),
		QemuCores:   int(vmConfig["cores"].(float64)),
		QemuSockets: int(vmConfig["sockets"].(float64)),
		QemuVlanTag: -1,
		FullClone:   &fullclone,
	}

	if vmConfig["virtio0"] == nil {
		return nil, errors.New("virtio0 (required) not found in current config")
	}

	storageMatch := rxStorage.FindStringSubmatch(vmConfig["virtio0"].(string))
	config.Storage = storageMatch[1]
	config.DiskSize, _ = strconv.ParseFloat(storageMatch[2], 64)

	if vmConfig["ide2"] != nil {
		isoMatch := rxIso.FindStringSubmatch(vmConfig["ide2"].(string))
		config.QemuIso = isoMatch[1]
	}

	if vmConfig["net0"] == nil {
		return nil, errors.New("net0 (required) not found in current config")
	}

	netMatch := rxNetwork.FindStringSubmatch(vmConfig["net0"].(string))
	config.QemuNicModel = netMatch[1]
	config.QemuBrige = netMatch[2]
	if netMatch[3] != "" {
		config.QemuVlanTag, _ = strconv.Atoi(netMatch[3])
	}

	return
}

// Useful waiting for ISO install to complete
func WaitForShutdown(vmr *VmRef, client *Client) (err error) {
	for ii := 0; ii < 100; ii++ {
		vmState, err := client.GetVmState(vmr)
		if err != nil {
			log.Print("Wait error:")
			log.Println(err)
		} else if vmState["status"] == "stopped" {
			return nil
		}
		time.Sleep(5 * time.Second)
	}
	return errors.New("Not shutdown within wait time")
}

// This is because proxmox create/config API won't let us make usernet devices
func SshForwardUsernet(vmr *VmRef, client *Client) (sshPort string, err error) {
	vmState, err := client.GetVmState(vmr)
	if err != nil {
		return "", err
	}
	if vmState["status"] == "stopped" {
		return "", errors.New("VM must be running first")
	}
	sshPort = strconv.Itoa(vmr.VmId() + 22000)
	_, err = client.MonitorCmd(vmr, "netdev_add user,id=net1,hostfwd=tcp::"+sshPort+"-:22")
	if err != nil {
		return "", err
	}
	_, err = client.MonitorCmd(vmr, "device_add virtio-net-pci,id=net1,netdev=net1,addr=0x13")
	if err != nil {
		return "", err
	}
	return
}

// device_del net1
// netdev_del net1
func RemoveSshForwardUsernet(vmr *VmRef, client *Client) (err error) {
	vmState, err := client.GetVmState(vmr)
	if err != nil {
		return err
	}
	if vmState["status"] == "stopped" {
		return errors.New("VM must be running first")
	}
	_, err = client.MonitorCmd(vmr, "device_del net1")
	if err != nil {
		return err
	}
	_, err = client.MonitorCmd(vmr, "netdev_del net1")
	if err != nil {
		return err
	}
	return nil
}

func MaxVmId(client *Client) (max int, err error) {
	resp, err := client.GetVmList()
	vms := resp["data"].([]interface{})
	max = 0
	for vmii := range vms {
		vm := vms[vmii].(map[string]interface{})
		vmid := int(vm["vmid"].(float64))
		if vmid > max {
			max = vmid
		}
	}
	return
}

func SendKeysString(vmr *VmRef, client *Client, keys string) (err error) {
	vmState, err := client.GetVmState(vmr)
	if err != nil {
		return err
	}
	if vmState["status"] == "stopped" {
		return errors.New("VM must be running first")
	}
	for _, r := range keys {
		c := string(r)
		lower := strings.ToLower(c)
		if c != lower {
			c = "shift-" + lower
		} else {
			switch c {
			case "!":
				c = "shift-1"
			case "@":
				c = "shift-2"
			case "#":
				c = "shift-3"
			case "$":
				c = "shift-4"
			case "%%":
				c = "shift-5"
			case "^":
				c = "shift-6"
			case "&":
				c = "shift-7"
			case "*":
				c = "shift-8"
			case "(":
				c = "shift-9"
			case ")":
				c = "shift-0"
			case "_":
				c = "shift-minus"
			case "+":
				c = "shift-equal"
			case " ":
				c = "spc"
			case "/":
				c = "slash"
			case "\\":
				c = "backslash"
			case ",":
				c = "comma"
			case "-":
				c = "minus"
			case "=":
				c = "equal"
			case ".":
				c = "dot"
			case "?":
				c = "shift-slash"
			}
		}
		_, err = client.MonitorCmd(vmr, "sendkey "+c)
		if err != nil {
			return err
		}
		time.Sleep(100)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/viant/endly"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/storage"
	"log"
	"path"
)

//main generate file with static content from meta, workflow, req folders so that they can be compiled into final binary
func main() {
	genDirectory := toolbox.CallerDirectory(3)

	parent := string(genDirectory[:len(genDirectory)-4])

	mappings := []*storage.StorageMapping{
		{
			SourceURL:      toolbox.FileSchema + path.Join(parent, "shared/meta"),
			DestinationURI: path.Join(endly.Namespace, "meta"),
			TargetFile:     path.Join(parent, "static", "meta.go"),
			TargetPackage:  "static",
		},
		{
			SourceURL:      toolbox.FileSchema + path.Join(parent, "shared/workflow"),
			DestinationURI: path.Join(endly.Namespace, "workflow"),
			TargetFile:     path.Join(parent, "static", "workflow.go"),
			TargetPackage:  "static",
		},
		{
			SourceURL:      toolbox.FileSchema + path.Join(parent, "shared/req"),
			DestinationURI: path.Join(endly.Namespace, "req"),
			TargetFile:     path.Join(parent, "static", "req.go"),
			TargetPackage:  "static",
		},
		{
			SourceURL:      toolbox.FileSchema + path.Join(parent, "Version"),
			DestinationURI: path.Join(endly.Namespace, "Version"),
			TargetFile:     path.Join(parent, "static", "version.go"),
			TargetPackage:  "static",
		},
	}
	err := storage.GenerateStorageCode(mappings...)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>moved to shared<commit_after><|endoftext|>


<commit_before>package ohyeah

import (
	"fmt"
	"log"
	"testing"
)

func TestConst(t *testing.T) {
	f := ConstantGen(44)

	for i := 0; i < 100; i++ {
		if v := f(); v != 44 {
			t.Errorf("Expected generator to produce 44, but got %v", v)
		}
	}
}

func TestCycle(t *testing.T) {
	a := ConstantGen(44)
	b := ConstantGen("hello")
	f := CycleGen(a, b)

	for i := 0; i < 100; i++ {
		if v1 := f(); v1 != 44 {
			t.Errorf("Expected generator to produce 44, but got %v", v1)
		}

		if v2 := f(); v2 != "hello" {
			t.Errorf(`Expected generator to produce "hello", but got %v`, v2)
		}
	}
}

func TestRandom(t *testing.T) {
	f := RandomFunc(373)

	numEqual := 0
	lastValue := int64(0)

	// Count the number of random numbers that are
	// equal to the previous number. Check to see if that
	// is less than 10% of the bunch. This is an extremely weak
	// randomness check.

	for i := 0; i < 100; i++ {
		value := f()
		if value == lastValue {
			numEqual++
		}
	}

	if numEqual > 10 {
		t.Errorf("More than 10% of the random numbers are equal to the previous number!")
	}
}

func TestPatternedStrings(t *testing.T) {
	r := RandomFunc(99)
	var f Generator
	f = PatternedStringGen(r, "foo")

	for i := 0; i < 100; i++ {
		value := f()
		expected := fmt.Sprintf("foo%d", i+1)
		if value != expected {
			t.Errorf("Expected value %v not == to actual %v", expected, value)
		}
	}
}

func XXTestStrings(t *testing.T) {
	r := RandomFunc(99)
	var f Generator
	f = StringGen(r)

	for i := 0; i < 100; i++ {
		log.Println(f())
	}
}

func XXTestValues(t *testing.T) {
	r := RandomFunc(99)

	intF := IntGen(r)
	sF := StringGen(r)

	f := RandomGen(r, intF, sF, sF)

	for i := 0; i < 100; i++ {
		log.Println(f())
	}
}

func XXTestArray(t *testing.T) {
	r := RandomFunc(99)

	intF := IntGen(r)
	f := ArrayGen(r, intF, 1)

	for i := 0; i < 100; i++ {
		log.Println(f())
	}
}

func XXTestMap(t *testing.T) {
	r := RandomFunc(99)

	kf := PatternedStringGen(r, "key")
	vf := IntGen(r)

	f := MapGen(r, kf, vf, 5)

	for i := 0; i < 100; i++ {
		log.Println(f())
	}
}

func XXTestElement(t *testing.T) {
	r := RandomFunc(99)

	a := []interface{}{"russ", "olsen", "1234", "hello"}

	f := ElementGen(r, a)

	for i := 0; i < 100; i++ {
		log.Println(f())
	}
}

func XXTestBool(t *testing.T) {
	r := RandomFunc(99)
	f := BoolGen(r)

	for i := 0; i < 100; i++ {
		log.Println(f())
	}
}

func TestCrazy(t *testing.T) {
	r := RandomFunc(99)

	//a := []interface{}{"russ", "olsen", "1234", "hello"}

	z := MapGen(r, PatternedStringGen(r, "key"), ArrayGen(r, IntGen(r), 10), 25)

	for i := 0; i < 100; i++ {
		log.Println(z())
	}
}
<commit_msg>Fix PatternedStringGen tests<commit_after>package ohyeah

import (
	"fmt"
	"log"
	"testing"
)

func TestConst(t *testing.T) {
	f := ConstantGen(44)

	for i := 0; i < 100; i++ {
		if v := f(); v != 44 {
			t.Errorf("Expected generator to produce 44, but got %v", v)
		}
	}
}

func TestCycle(t *testing.T) {
	a := ConstantGen(44)
	b := ConstantGen("hello")
	f := CycleGen(a, b)

	for i := 0; i < 100; i++ {
		if v1 := f(); v1 != 44 {
			t.Errorf("Expected generator to produce 44, but got %v", v1)
		}

		if v2 := f(); v2 != "hello" {
			t.Errorf(`Expected generator to produce "hello", but got %v`, v2)
		}
	}
}

func TestRandom(t *testing.T) {
	f := RandomFunc(373)

	numEqual := 0
	lastValue := int64(0)

	// Count the number of random numbers that are
	// equal to the previous number. Check to see if that
	// is less than 10% of the bunch. This is an extremely weak
	// randomness check.

	for i := 0; i < 100; i++ {
		value := f()
		if value == lastValue {
			numEqual++
		}
	}

	if numEqual > 10 {
		t.Errorf("More than 10% of the random numbers are equal to the previous number!")
	}
}

func TestPatternedStrings(t *testing.T) {
	var f Generator
	f = PatternedStringGen("foo")

	for i := 0; i < 100; i++ {
		value := f()
		expected := fmt.Sprintf("foo%d", i+1)
		if value != expected {
			t.Errorf("Expected value %v not == to actual %v", expected, value)
		}
	}
}

func XXTestStrings(t *testing.T) {
	r := RandomFunc(99)
	var f Generator
	f = StringGen(r)

	for i := 0; i < 100; i++ {
		log.Println(f())
	}
}

func XXTestValues(t *testing.T) {
	r := RandomFunc(99)

	intF := IntGen(r)
	sF := StringGen(r)

	f := RandomGen(r, intF, sF, sF)

	for i := 0; i < 100; i++ {
		log.Println(f())
	}
}

func XXTestArray(t *testing.T) {
	r := RandomFunc(99)

	intF := IntGen(r)
	f := ArrayGen(r, intF, 1)

	for i := 0; i < 100; i++ {
		log.Println(f())
	}
}

func XXTestMap(t *testing.T) {
	r := RandomFunc(99)

	kf := PatternedStringGen("key")
	vf := IntGen(r)

	f := MapGen(r, kf, vf, 5)

	for i := 0; i < 100; i++ {
		log.Println(f())
	}
}

func XXTestElement(t *testing.T) {
	r := RandomFunc(99)

	a := []interface{}{"russ", "olsen", "1234", "hello"}

	f := ElementGen(r, a)

	for i := 0; i < 100; i++ {
		log.Println(f())
	}
}

func XXTestBool(t *testing.T) {
	r := RandomFunc(99)
	f := BoolGen(r)

	for i := 0; i < 100; i++ {
		log.Println(f())
	}
}

func XXTestCrazy(t *testing.T) {
	r := RandomFunc(99)
	z := MapGen(r, PatternedStringGen("key"), ArrayGen(r, IntGen(r), 10), 25)

	for i := 0; i < 100; i++ {
		log.Println(z())
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/service/elasticache"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsElasticacheCluster() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsElasticacheClusterCreate,
		Read:   resourceAwsElasticacheClusterRead,
		Delete: resourceAwsElasticacheClusterDelete,

		Schema: map[string]*schema.Schema{
			"cluster_id": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"engine": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"node_type": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"num_cache_nodes": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: true,
			},
			"parameter_group_name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
			"port": &schema.Schema{
				Type:     schema.TypeInt,
				Default:  11211,
				Optional: true,
				ForceNew: true,
			},
			"engine_version": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
			"subnet_group_name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
			"security_group_names": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},
			"security_group_ids": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},
		},
	}
}

func resourceAwsElasticacheClusterCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).elasticacheconn

	clusterId := d.Get("cluster_id").(string)
	nodeType := d.Get("node_type").(string)           // e.g) cache.m1.small
	numNodes := int64(d.Get("num_cache_nodes").(int)) // 2
	engine := d.Get("engine").(string)                // memcached
	engineVersion := d.Get("engine_version").(string) // 1.4.14
	port := int64(d.Get("port").(int))                // 11211
	subnetGroupName := d.Get("subnet_group_name").(string)
	securityNameSet := d.Get("security_group_names").(*schema.Set)
	securityIdSet := d.Get("security_group_ids").(*schema.Set)
	paramGroupName := d.Get("parameter_group_name").(string) // default.memcached1.4

	securityNames := expandStringList(securityNameSet.List())
	securityIds := expandStringList(securityIdSet.List())

	req := &elasticache.CreateCacheClusterInput{
		CacheClusterID:          aws.String(clusterId),
		CacheNodeType:           aws.String(nodeType),
		NumCacheNodes:           aws.Long(numNodes),
		Engine:                  aws.String(engine),
		EngineVersion:           aws.String(engineVersion),
		Port:                    aws.Long(port),
		CacheSubnetGroupName:    aws.String(subnetGroupName),
		CacheSecurityGroupNames: securityNames,
		SecurityGroupIDs:        securityIds,
		CacheParameterGroupName: aws.String(paramGroupName),
	}

	_, err := conn.CreateCacheCluster(req)
	if err != nil {
		return fmt.Errorf("Error creating Elasticache: %s", err)
	}

	pending := []string{"creating"}
	stateConf := &resource.StateChangeConf{
		Pending:    pending,
		Target:     "available",
		Refresh:    CacheClusterStateRefreshFunc(conn, d.Id(), "available", pending),
		Timeout:    10 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	log.Printf("[DEBUG] Waiting for state to become available: %v", d.Id())
	_, sterr := stateConf.WaitForState()
	if sterr != nil {
		return fmt.Errorf("Error waiting for elasticache (%s) to be created: %s", d.Id(), sterr)
	}

	d.SetId(clusterId)

	return nil
}

func resourceAwsElasticacheClusterRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).elasticacheconn
	req := &elasticache.DescribeCacheClustersInput{
		CacheClusterID: aws.String(d.Id()),
	}

	res, err := conn.DescribeCacheClusters(req)
	if err != nil {
		return err
	}

	if len(res.CacheClusters) == 1 {
		c := res.CacheClusters[0]
		d.Set("cluster_id", c.CacheClusterID)
		d.Set("node_type", c.CacheNodeType)
		d.Set("num_cache_nodes", c.NumCacheNodes)
		d.Set("engine", c.Engine)
		d.Set("engine_version", c.EngineVersion)
		if c.ConfigurationEndpoint != nil {
			d.Set("port", c.ConfigurationEndpoint.Port)
		}
		d.Set("subnet_group_name", c.CacheSubnetGroupName)
		d.Set("security_group_names", c.CacheSecurityGroups)
		d.Set("security_group_ids", c.SecurityGroups)
		d.Set("parameter_group_name", c.CacheParameterGroup)
	}

	return nil
}

func resourceAwsElasticacheClusterDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).elasticacheconn

	req := &elasticache.DeleteCacheClusterInput{
		CacheClusterID: aws.String(d.Id()),
	}
	_, err := conn.DeleteCacheCluster(req)
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] Waiting for deletion: %v", d.Id())
	stateConf := &resource.StateChangeConf{
		Pending:    []string{"creating", "available", "deleting", "incompatible-parameters", "incompatible-network", "restore-failed"},
		Target:     "",
		Refresh:    CacheClusterStateRefreshFunc(conn, d.Id(), "", []string{}),
		Timeout:    10 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	_, sterr := stateConf.WaitForState()
	if sterr != nil {
		return fmt.Errorf("Error waiting for elasticache (%s) to delete: %s", d.Id(), sterr)
	}

	d.SetId("")

	return nil
}

func CacheClusterStateRefreshFunc(conn *elasticache.ElastiCache, clusterID, givenState string, pending []string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		resp, err := conn.DescribeCacheClusters(&elasticache.DescribeCacheClustersInput{
			CacheClusterID: aws.String(clusterID),
		})
		if err != nil {
			apierr := err.(aws.APIError)
			log.Printf("[DEBUG] message: %v, code: %v", apierr.Message, apierr.Code)
			if apierr.Message == fmt.Sprintf("CacheCluster not found: %v", clusterID) {
				log.Printf("[DEBUG] Detect deletion")
				return nil, "", nil
			}

			log.Printf("[ERROR] CacheClusterStateRefreshFunc: %s", err)
			return nil, "", err
		}

		c := resp.CacheClusters[0]
		log.Printf("[DEBUG] status: %v", *c.CacheClusterStatus)

		// return the current state if it's in the pending array
		for _, p := range pending {
			s := *c.CacheClusterStatus
			if p == s {
				log.Printf("[DEBUG] Return with status: %v", *c.CacheClusterStatus)
				return c, p, nil
			}
		}

		// return given state if it's not in pending
		if givenState != "" {
			return c, givenState, nil
		}
		log.Printf("[DEBUG] current status: %v", *c.CacheClusterStatus)
		return c, *c.CacheClusterStatus, nil
	}
}
<commit_msg>provider/aws: elasticache_cluster engine_version is computed<commit_after>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/service/elasticache"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsElasticacheCluster() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsElasticacheClusterCreate,
		Read:   resourceAwsElasticacheClusterRead,
		Delete: resourceAwsElasticacheClusterDelete,

		Schema: map[string]*schema.Schema{
			"cluster_id": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"engine": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"node_type": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"num_cache_nodes": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: true,
			},
			"parameter_group_name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
			"port": &schema.Schema{
				Type:     schema.TypeInt,
				Default:  11211,
				Optional: true,
				ForceNew: true,
			},
			"engine_version": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"subnet_group_name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},
			"security_group_names": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},
			"security_group_ids": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},
		},
	}
}

func resourceAwsElasticacheClusterCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).elasticacheconn

	clusterId := d.Get("cluster_id").(string)
	nodeType := d.Get("node_type").(string)           // e.g) cache.m1.small
	numNodes := int64(d.Get("num_cache_nodes").(int)) // 2
	engine := d.Get("engine").(string)                // memcached
	engineVersion := d.Get("engine_version").(string) // 1.4.14
	port := int64(d.Get("port").(int))                // 11211
	subnetGroupName := d.Get("subnet_group_name").(string)
	securityNameSet := d.Get("security_group_names").(*schema.Set)
	securityIdSet := d.Get("security_group_ids").(*schema.Set)
	paramGroupName := d.Get("parameter_group_name").(string) // default.memcached1.4

	securityNames := expandStringList(securityNameSet.List())
	securityIds := expandStringList(securityIdSet.List())

	req := &elasticache.CreateCacheClusterInput{
		CacheClusterID:          aws.String(clusterId),
		CacheNodeType:           aws.String(nodeType),
		NumCacheNodes:           aws.Long(numNodes),
		Engine:                  aws.String(engine),
		EngineVersion:           aws.String(engineVersion),
		Port:                    aws.Long(port),
		CacheSubnetGroupName:    aws.String(subnetGroupName),
		CacheSecurityGroupNames: securityNames,
		SecurityGroupIDs:        securityIds,
		CacheParameterGroupName: aws.String(paramGroupName),
	}

	_, err := conn.CreateCacheCluster(req)
	if err != nil {
		return fmt.Errorf("Error creating Elasticache: %s", err)
	}

	pending := []string{"creating"}
	stateConf := &resource.StateChangeConf{
		Pending:    pending,
		Target:     "available",
		Refresh:    CacheClusterStateRefreshFunc(conn, d.Id(), "available", pending),
		Timeout:    10 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	log.Printf("[DEBUG] Waiting for state to become available: %v", d.Id())
	_, sterr := stateConf.WaitForState()
	if sterr != nil {
		return fmt.Errorf("Error waiting for elasticache (%s) to be created: %s", d.Id(), sterr)
	}

	d.SetId(clusterId)

	return nil
}

func resourceAwsElasticacheClusterRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).elasticacheconn
	req := &elasticache.DescribeCacheClustersInput{
		CacheClusterID: aws.String(d.Id()),
	}

	res, err := conn.DescribeCacheClusters(req)
	if err != nil {
		return err
	}

	if len(res.CacheClusters) == 1 {
		c := res.CacheClusters[0]
		d.Set("cluster_id", c.CacheClusterID)
		d.Set("node_type", c.CacheNodeType)
		d.Set("num_cache_nodes", c.NumCacheNodes)
		d.Set("engine", c.Engine)
		d.Set("engine_version", c.EngineVersion)
		if c.ConfigurationEndpoint != nil {
			d.Set("port", c.ConfigurationEndpoint.Port)
		}
		d.Set("subnet_group_name", c.CacheSubnetGroupName)
		d.Set("security_group_names", c.CacheSecurityGroups)
		d.Set("security_group_ids", c.SecurityGroups)
		d.Set("parameter_group_name", c.CacheParameterGroup)
	}

	return nil
}

func resourceAwsElasticacheClusterDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).elasticacheconn

	req := &elasticache.DeleteCacheClusterInput{
		CacheClusterID: aws.String(d.Id()),
	}
	_, err := conn.DeleteCacheCluster(req)
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] Waiting for deletion: %v", d.Id())
	stateConf := &resource.StateChangeConf{
		Pending:    []string{"creating", "available", "deleting", "incompatible-parameters", "incompatible-network", "restore-failed"},
		Target:     "",
		Refresh:    CacheClusterStateRefreshFunc(conn, d.Id(), "", []string{}),
		Timeout:    10 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	_, sterr := stateConf.WaitForState()
	if sterr != nil {
		return fmt.Errorf("Error waiting for elasticache (%s) to delete: %s", d.Id(), sterr)
	}

	d.SetId("")

	return nil
}

func CacheClusterStateRefreshFunc(conn *elasticache.ElastiCache, clusterID, givenState string, pending []string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		resp, err := conn.DescribeCacheClusters(&elasticache.DescribeCacheClustersInput{
			CacheClusterID: aws.String(clusterID),
		})
		if err != nil {
			apierr := err.(aws.APIError)
			log.Printf("[DEBUG] message: %v, code: %v", apierr.Message, apierr.Code)
			if apierr.Message == fmt.Sprintf("CacheCluster not found: %v", clusterID) {
				log.Printf("[DEBUG] Detect deletion")
				return nil, "", nil
			}

			log.Printf("[ERROR] CacheClusterStateRefreshFunc: %s", err)
			return nil, "", err
		}

		c := resp.CacheClusters[0]
		log.Printf("[DEBUG] status: %v", *c.CacheClusterStatus)

		// return the current state if it's in the pending array
		for _, p := range pending {
			s := *c.CacheClusterStatus
			if p == s {
				log.Printf("[DEBUG] Return with status: %v", *c.CacheClusterStatus)
				return c, p, nil
			}
		}

		// return given state if it's not in pending
		if givenState != "" {
			return c, givenState, nil
		}
		log.Printf("[DEBUG] current status: %v", *c.CacheClusterStatus)
		return c, *c.CacheClusterStatus, nil
	}
}
<|endoftext|>


<commit_before>package anaconda

import (
	"net/url"
	"strconv"
)

type Cursor struct {
	Previous_cursor     int64
	Previous_cursor_str string

	Ids []int64

	Next_cursor     int64
	Next_cursor_str string
}

type UserCursor struct {
	Previous_cursor     int64
	Previous_cursor_str string
	Next_cursor         int64
	Next_cursor_str     string
	Users               []User
}

type Friendship struct {
	Name        string
	Id_str      string
	Id          int64
	Connections []string
	Screen_name string
}

type FollowersPage struct {
	Followers []User
	Error     error
}

//GetFriendshipsNoRetweets s a collection of user_ids that the currently authenticated user does not want to receive retweets from.
//It does not currently support the stringify_ids parameter
func (a TwitterApi) GetFriendshipsNoRetweets() (ids []int64, err error) {
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friendships/no_retweets/ids.json", nil, &ids, _GET, response_ch}
	return ids, (<-response_ch).err
}

func (a TwitterApi) GetFollowersIds(v url.Values) (c Cursor, err error) {
	err = a.apiGet(BaseUrl+"/followers/ids.json", v, &c)
	return
}

func (a TwitterApi) GetFriendsIds(v url.Values) (c Cursor, err error) {
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friends/ids.json", v, &c, _GET, response_ch}
	return c, (<-response_ch).err
}

func (a TwitterApi) GetFriendshipsLookup(v url.Values) (friendships []Friendship, err error) {
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friendships/lookup.json", v, &friendships, _GET, response_ch}
	return friendships, (<-response_ch).err
}

func (a TwitterApi) GetFriendshipsIncoming(v url.Values) (c Cursor, err error) {
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friendships/incoming.json", v, &c, _GET, response_ch}
	return c, (<-response_ch).err
}

func (a TwitterApi) GetFriendshipsOutgoing(v url.Values) (c Cursor, err error) {
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friendships/outgoing.json", v, &c, _GET, response_ch}
	return c, (<-response_ch).err
}

func (a TwitterApi) GetFollowersList(v url.Values) (c UserCursor, err error) {
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/followers/list.json", v, &c, _GET, response_ch}
	return c, (<-response_ch).err
}

func (a TwitterApi) GetFriendsList(v url.Values) (c UserCursor, err error) {
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friends/list.json", v, &c, _GET, response_ch}
	return c, (<-response_ch).err
}

// Like GetFollowersList, but returns a channel instead of a cursor and pre-fetches the remaining results
// This channel is closed once all values have been fetched
func (a TwitterApi) GetFollowersListAll(v url.Values) (result chan FollowersPage) {

	result = make(chan FollowersPage)

	if v == nil {
		v = url.Values{}
	}
	go func(a TwitterApi, v url.Values, result chan FollowersPage) {
		// Cursor defaults to the first page ("-1")
		next_cursor := "-1"
		for {
			v.Set("cursor", next_cursor)
			c, err := a.GetFollowersList(v)

			// throttledQuery() handles all rate-limiting errors
			// if GetFollowersList() returns an error, it must be a different kind of error

			result <- FollowersPage{c.Users, err}

			next_cursor = c.Next_cursor_str
			if next_cursor == "0" {
				close(result)
				break
			}
		}
	}(a, v, result)
	return result
}

func (a TwitterApi) GetFollowersUser(id int64, v url.Values) (c Cursor, err error) {
	v = cleanValues(v)
	v.Set("user_id", strconv.FormatInt(id, 10))
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/followers/ids.json", v, &c, _GET, response_ch}
	return c, (<-response_ch).err
}

// Like GetFriendsIds, but returns a channel instead of a cursor and pre-fetches the remaining results
// This channel is closed once all values have been fetched
func (a TwitterApi) GetFriendsIdsAll(v url.Values) (c Cursor, err error) {
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friends/ids.json", v, &c, _GET, response_ch}
	return c, (<-response_ch).err
}

func (a TwitterApi) GetFriendsUser(id int64, v url.Values) (c Cursor, err error) {
	v = cleanValues(v)
	v.Set("user_id", strconv.FormatInt(id, 10))
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friends/ids.json", v, &c, _GET, response_ch}
	return c, (<-response_ch).err
}

// FollowUserId follows the user with the specified userId.
// This implements the /friendships/create endpoint, though the function name
// uses the terminology 'follow' as this is most consistent with colloquial Twitter terminology.
func (a TwitterApi) FollowUserId(userId int64, v url.Values) (user User, err error) {
	v = cleanValues(v)
	v.Set("user_id", strconv.FormatInt(userId, 10))
	return a.postFriendshipsCreateImpl(v)
}

// FollowUserId follows the user with the specified screenname (username).
// This implements the /friendships/create endpoint, though the function name
// uses the terminology 'follow' as this is most consistent with colloquial Twitter terminology.
func (a TwitterApi) FollowUser(screenName string) (user User, err error) {
	v := url.Values{}
	v.Set("screen_name", screenName)
	return a.postFriendshipsCreateImpl(v)
}

func (a TwitterApi) postFriendshipsCreateImpl(v url.Values) (user User, err error) {
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friendships/create.json", v, &user, _POST, response_ch}
	return user, (<-response_ch).err
}

// UnfollowUserId unfollows the user with the specified userId.
// This implements the /friendships/destroy endpoint, though the function name
// uses the terminology 'unfollow' as this is most consistent with colloquial Twitter terminology.
func (a TwitterApi) UnfollowUserId(userId int64) (u User, err error) {
	v := url.Values{}
	v.Set("user_id", strconv.FormatInt(userId, 10))
	// Set other values before calling this method:
	// page, count, include_entities
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friendships/destroy.json", v, &u, _POST, response_ch}
	return u, (<-response_ch).err
}

// UnfollowUser unfollows the user with the specified screenname (username)
// This implements the /friendships/destroy endpoint, though the function name
// uses the terminology 'unfollow' as this is most consistent with colloquial Twitter terminology.
func (a TwitterApi) UnfollowUser(screenname string) (u User, err error) {
	v := url.Values{}
	v.Set("screen_name", screenname)
	// Set other values before calling this method:
	// page, count, include_entities
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friendships/destroy.json", v, &u, _POST, response_ch}
	return u, (<-response_ch).err
}
<commit_msg>Fix GetFriendsIdsAll<commit_after>package anaconda

import (
	"net/url"
	"strconv"
)

type Cursor struct {
	Previous_cursor     int64
	Previous_cursor_str string

	Ids []int64

	Next_cursor     int64
	Next_cursor_str string
}

type UserCursor struct {
	Previous_cursor     int64
	Previous_cursor_str string
	Next_cursor         int64
	Next_cursor_str     string
	Users               []User
}

type FriendsIdsCursor struct {
	Previous_cursor     int64
	Previous_cursor_str string
	Next_cursor         int64
	Next_cursor_str     string
	Ids                 []int64
}

type FriendsIdsPage struct {
	Ids   []int64
	Error error
}

type Friendship struct {
	Name        string
	Id_str      string
	Id          int64
	Connections []string
	Screen_name string
}

type FollowersPage struct {
	Followers []User
	Error     error
}

//GetFriendshipsNoRetweets s a collection of user_ids that the currently authenticated user does not want to receive retweets from.
//It does not currently support the stringify_ids parameter
func (a TwitterApi) GetFriendshipsNoRetweets() (ids []int64, err error) {
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friendships/no_retweets/ids.json", nil, &ids, _GET, response_ch}
	return ids, (<-response_ch).err
}

func (a TwitterApi) GetFollowersIds(v url.Values) (c Cursor, err error) {
	err = a.apiGet(BaseUrl+"/followers/ids.json", v, &c)
	return
}

func (a TwitterApi) GetFriendsIds(v url.Values) (c Cursor, err error) {
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friends/ids.json", v, &c, _GET, response_ch}
	return c, (<-response_ch).err
}

func (a TwitterApi) GetFriendshipsLookup(v url.Values) (friendships []Friendship, err error) {
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friendships/lookup.json", v, &friendships, _GET, response_ch}
	return friendships, (<-response_ch).err
}

func (a TwitterApi) GetFriendshipsIncoming(v url.Values) (c Cursor, err error) {
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friendships/incoming.json", v, &c, _GET, response_ch}
	return c, (<-response_ch).err
}

func (a TwitterApi) GetFriendshipsOutgoing(v url.Values) (c Cursor, err error) {
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friendships/outgoing.json", v, &c, _GET, response_ch}
	return c, (<-response_ch).err
}

func (a TwitterApi) GetFollowersList(v url.Values) (c UserCursor, err error) {
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/followers/list.json", v, &c, _GET, response_ch}
	return c, (<-response_ch).err
}

func (a TwitterApi) GetFriendsList(v url.Values) (c UserCursor, err error) {
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friends/list.json", v, &c, _GET, response_ch}
	return c, (<-response_ch).err
}

// Like GetFollowersList, but returns a channel instead of a cursor and pre-fetches the remaining results
// This channel is closed once all values have been fetched
func (a TwitterApi) GetFollowersListAll(v url.Values) (result chan FollowersPage) {

	result = make(chan FollowersPage)

	if v == nil {
		v = url.Values{}
	}
	go func(a TwitterApi, v url.Values, result chan FollowersPage) {
		// Cursor defaults to the first page ("-1")
		next_cursor := "-1"
		for {
			v.Set("cursor", next_cursor)
			c, err := a.GetFollowersList(v)

			// throttledQuery() handles all rate-limiting errors
			// if GetFollowersList() returns an error, it must be a different kind of error

			result <- FollowersPage{c.Users, err}

			next_cursor = c.Next_cursor_str
			if next_cursor == "0" {
				close(result)
				break
			}
		}
	}(a, v, result)
	return result
}

func (a TwitterApi) GetFollowersUser(id int64, v url.Values) (c Cursor, err error) {
	v = cleanValues(v)
	v.Set("user_id", strconv.FormatInt(id, 10))
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/followers/ids.json", v, &c, _GET, response_ch}
	return c, (<-response_ch).err
}

// Like GetFriendsIds, but returns a channel instead of a cursor and pre-fetches the remaining results
// This channel is closed once all values have been fetched
func (a TwitterApi) GetFriendsIdsAll(v url.Values) (result chan FriendsIdsPage) {

	result = make(chan FriendsIdsPage)

	if v == nil {
		v = url.Values{}
	}
	go func(a TwitterApi, v url.Values, result chan FriendsIdsPage) {
		// Cursor defaults to the first page ("-1")
		next_cursor := "-1"
		for {
			v.Set("cursor", next_cursor)
			c, err := a.GetFriendsIds(v)

			// throttledQuery() handles all rate-limiting errors
			// if GetFollowersList() returns an error, it must be a different kind of error

			result <- FriendsIdsPage{c.Ids, err}

			next_cursor = c.Next_cursor_str
			if next_cursor == "0" {
				close(result)
				break
			}
		}
	}(a, v, result)
	return result
}

func (a TwitterApi) GetFriendsUser(id int64, v url.Values) (c Cursor, err error) {
	v = cleanValues(v)
	v.Set("user_id", strconv.FormatInt(id, 10))
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friends/ids.json", v, &c, _GET, response_ch}
	return c, (<-response_ch).err
}

// FollowUserId follows the user with the specified userId.
// This implements the /friendships/create endpoint, though the function name
// uses the terminology 'follow' as this is most consistent with colloquial Twitter terminology.
func (a TwitterApi) FollowUserId(userId int64, v url.Values) (user User, err error) {
	v = cleanValues(v)
	v.Set("user_id", strconv.FormatInt(userId, 10))
	return a.postFriendshipsCreateImpl(v)
}

// FollowUserId follows the user with the specified screenname (username).
// This implements the /friendships/create endpoint, though the function name
// uses the terminology 'follow' as this is most consistent with colloquial Twitter terminology.
func (a TwitterApi) FollowUser(screenName string) (user User, err error) {
	v := url.Values{}
	v.Set("screen_name", screenName)
	return a.postFriendshipsCreateImpl(v)
}

func (a TwitterApi) postFriendshipsCreateImpl(v url.Values) (user User, err error) {
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friendships/create.json", v, &user, _POST, response_ch}
	return user, (<-response_ch).err
}

// UnfollowUserId unfollows the user with the specified userId.
// This implements the /friendships/destroy endpoint, though the function name
// uses the terminology 'unfollow' as this is most consistent with colloquial Twitter terminology.
func (a TwitterApi) UnfollowUserId(userId int64) (u User, err error) {
	v := url.Values{}
	v.Set("user_id", strconv.FormatInt(userId, 10))
	// Set other values before calling this method:
	// page, count, include_entities
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friendships/destroy.json", v, &u, _POST, response_ch}
	return u, (<-response_ch).err
}

// UnfollowUser unfollows the user with the specified screenname (username)
// This implements the /friendships/destroy endpoint, though the function name
// uses the terminology 'unfollow' as this is most consistent with colloquial Twitter terminology.
func (a TwitterApi) UnfollowUser(screenname string) (u User, err error) {
	v := url.Values{}
	v.Set("screen_name", screenname)
	// Set other values before calling this method:
	// page, count, include_entities
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/friendships/destroy.json", v, &u, _POST, response_ch}
	return u, (<-response_ch).err
}
<|endoftext|>


<commit_before>// A Naive Bayesian Classifier
// Jake Brukhman <jbrukh@gmail.com>

//
// BAYESIAN CLASSIFICATION REFRESHER: suppose you have a set
// of classes (e.g. categories) C := {C_1, ..., C_n}, and a
// document D consisting of words D := {W_1, ..., W_k}.
// We wish to ascertain the probability that the document
// belongs to some class C_j given some set of training data
// associating documents and classes.
//
// By Bayes Theorem, we have that
//
//    P(C_j|D) = P(D|C_j)*P(C_j)/P(D).
//
// The LHS is the probability that the document belongs to class
// C_j given the document itself (by which is meant, in practice,
// the word frequencies occurring in this document), and our program
// will calculate this probability for each j and spit out the
// most likely class for this document.
//
// P(C_j) is referred to as the "prior" probability, or the
// probability that a document belongs to C_j in general, without
// seeing the document first. P(D|C_j) is the probability of seeing
// such a document, given that it belongs to C_j. Here, by assuming
// that words appear independently in documents (this being the
// "naive" assumption), we can estimate
//
//    P(D|C_j) ~= P(W_1|C_j)*...*P(W_k|C_j)
//
// where P(W_i|C_j) is the probability of seeing the given word
// in a document of the given class. Finally, P(D) can be seen as
// merely a scaling factor and is not strictly relevant to
// classificiation, unless you want to normalize the resulting
// scores and actually see probabilities. In this case, note that
//
//    P(D) = SUM_j(P(D|C_j)*P(C_j))
//
// One practical issue with performing these calculations is the
// possibility of float64 underflow when calculating P(D|C_j), as
// individual word probabilities can be arbitrarily small, and
// a document can have an arbitrarily large number of them. A
// typical method for dealing with this case is to transform the
// probability to the log domain and perform additions instead
// of multiplications:
//
//   log P(C_j|D) ~ log(P(C_j)) + SUM_i(log P(W_i|C_j))
//
// where i = 1, ..., k. Note that by doing this, we are discarding
// the scaling factor P(D) and our scores are no longer
// probabilities.
//
package bayesian

import (
    "math"
    "gob"
    "os"
)

// defaultProb is the tiny non-zero probability that a word
// we have not seen before appears in the class.
const defaultProb = 0.00000000001

// This type defines a set of classes that the classifier will
// filter: C = {C_1, ..., C_n}. You should define your classes
// as a set of constants, for example as follows:
//
//    const (
//        Good Class = "Good"
//        Bad Class = "Bad
//    )
//
type Class string

// Classifier implements the Naive Bayesian Classifier.
type Classifier struct {
    Classes []Class
    datas map[Class]*classData
}

// serializableClassifier represents an container for
// classifier objects whose fields are modifiable by
// reflection and are therefore writeable.
type serializableClassifier struct {
    Classes []Class
    Datas map[Class]*classData
}

// classData holds the frequency data for words in a
// particular class. In the future, we may replace this
// structure with a trie-like structure for more
// efficient storage.
type classData struct {
    Freqs map[string]int
    Total int
}

// newClassData creates a new empty class data node.
func newClassData() *classData {
    return &classData{
        Freqs: make(map[string]int),
    }
}

// P(W|Cj) -- the probability of seeing a particular word
// in a document of this class.
func (d *classData) getWordProb(word string) float64 {
    value, ok := d.Freqs[word]
    if !ok {
        return defaultProb
    }
    return float64(value)/float64(d.Total)
}

// P(D|C_j) -- the probability of seeing this set of words
// in a document of this class.
//
// Note that words should not be empty, and this method of
// calulation is prone to underflow if there are many words
// and their individual probabilties are small.
func (d *classData) getWordsProb(words []string) (prob float64) {
    prob = 1
    for _, word := range words {
        prob *= d.getWordProb(word)
    }
    return
}

// New creates a new Classifier. The classes the provided
// should be at least 2 in number and unique from each other.
func NewClassifier(classes ...Class) (inst *Classifier) {
    if len(classes) < 2 {
        panic("provide at least two classes")
    }
    inst = &Classifier{
            classes,
            make(map[Class]*classData),
    }
    for _, class := range classes {
        inst.datas[class] = newClassData()
    }
    return
}

func NewClassifierFromFile(name string) (c *Classifier, err os.Error) {
    file, err := os.Open(name)
    if err != nil {
        return nil, err
    }
    dec := gob.NewDecoder(file)
    w := new(serializableClassifier)
    err = dec.Decode(w)

    return &Classifier{w.Classes, w.Datas}, err
}

// getPriors returns the prior probabilities for the
// classes provided -- P(C_i). There is a way to
// smooth priors, currently not implemented here.
func (c *Classifier) getPriors() (priors []float64) {
    n := len(c.Classes)
    priors = make([]float64, n, n)
    sum := 0
    for index, class := range c.Classes {
        total := c.datas[class].Total;
        priors[index] = float64(total)
        sum += total
    }
    if sum != 0 {
        for i := 0; i < n; i++ {
            priors[i] /= float64(sum)
        }
    }
    return
}

// Learn will train the classifier on the provided data.
func (c *Classifier) Learn(words []string, which Class) {
    data := c.datas[which]
    for _, word := range words {
        data.Freqs[word]++
        data.Total++
    }
}

// Scores will produce an array of scores that correspond
// to its opinion on the document in question, and whether it
// belongs to the given class. The order of the scores
// in the return values follows the order of the inital array
// of Class objects parameterized to the NewClassifier() function.
// If no training data has been provided, c will return
// a 0 array.
//
// The value of the score is proportional to the likelihood,
// even if the score is negative, so that the score with the
// greatest value corresponds to the most likely class.
//
// Additionally, c function will return the index of the
// maximum probability. The value of c number is given by
// scores[inx]. The class of that corresponds to c number
// is classifier.Classes[inx]. If more than one of the
// returned probabilities has the maximum values, then
// strict is false.
func (c *Classifier) Scores(words []string) (scores []float64, inx int, strict bool) {
    n := len(c.Classes)
    scores = make([]float64, n, n)
    priors := c.getPriors()

    // calculate the score for each class
    for index, class := range c.Classes {
        data := c.datas[class]
        // c is the sum of the logarithms
        // as outlined in the refresher
        score := math.Log(priors[index])
        for _, word := range words {
            score += math.Log(data.getWordProb(word))
        }
        scores[index] = score
    }
    inx, strict = findMax(scores)
    return scores, inx, strict
}

// Probabilities works the same as Score, but delivers
// actual probabilities as discussed above. Note that float64
// underflow is possible if the word list contains too
// many doc that have probabilities very close to 0.
func (c *Classifier) Probabilities(doc []string) (scores []float64, inx int, strict bool) {
    n := len(c.Classes)
    scores = make([]float64, n, n)
    priors := c.getPriors()
    sum := float64(0)
    // calculate the score for each class
    for index, class := range c.Classes {
        data := c.datas[class]
        // c is the sum of the logarithms
        // as outlined in the refresher
        score := priors[index]
        for _, word := range doc {
            score *= data.getWordProb(word)
        }
        scores[index] = score
        sum += score
    }
    for i := 0; i < n; i++ {
        scores[i] /= sum
    }
    inx, strict = findMax(scores)
    return scores, inx, strict
}

// WordFrequencies returns a matrix of word frequencies that currently
// exist in the classifier for each class state for the given input
// words. In other words, if you obtain the frequencies
//
//    freqs := c.WordFrequencies(/* ... array of j words ... */)
//
// then the expression freq[i][j] represents the frequency of the j-th
// word within the i-th class.
func (c *Classifier) WordFrequencies(words []string) (freqMatrix [][]float64) {
    n, l := len(c.Classes), len(words)
    freqMatrix = make([][]float64, n)
    for i, _ := range freqMatrix {
        arr := make([]float64, l)
        data := c.datas[c.Classes[i]]
        for j, _ := range arr {
            arr[j] = data.getWordProb(words[j])
        }
        freqMatrix[i] = arr
    }
    return
}

// Serialize this classifier to a file.
func (c *Classifier) WriteToFile(name string) (err os.Error) {
    file, err := os.OpenFile(name, os.O_WRONLY | os.O_CREATE, 0655)
    if err != nil {
        return err
    }
    enc := gob.NewEncoder(file)
    err = enc.Encode(&serializableClassifier{c.Classes, c.datas})
    return
}

// findMax finds the maximum of a set of scores; if the
// maximum is strict -- that is, it is the single unique
// maximum from the set -- then strict has return value
// true. Otherwise it is false.
func findMax(scores []float64) (inx int, strict bool) {
    inx = 0
    strict = true
    for i := 1; i < len(scores); i++ {
        if scores[inx] < scores[i] {
            inx = i
            strict = true
        } else if scores[inx] == scores[i] {
            strict = false
        }
    }
    return
}
<commit_msg>Added comments.<commit_after>// A Naive Bayesian Classifier
// Jake Brukhman <jbrukh@gmail.com>

//
// BAYESIAN CLASSIFICATION REFRESHER: suppose you have a set
// of classes (e.g. categories) C := {C_1, ..., C_n}, and a
// document D consisting of words D := {W_1, ..., W_k}.
// We wish to ascertain the probability that the document
// belongs to some class C_j given some set of training data
// associating documents and classes.
//
// By Bayes Theorem, we have that
//
//    P(C_j|D) = P(D|C_j)*P(C_j)/P(D).
//
// The LHS is the probability that the document belongs to class
// C_j given the document itself (by which is meant, in practice,
// the word frequencies occurring in this document), and our program
// will calculate this probability for each j and spit out the
// most likely class for this document.
//
// P(C_j) is referred to as the "prior" probability, or the
// probability that a document belongs to C_j in general, without
// seeing the document first. P(D|C_j) is the probability of seeing
// such a document, given that it belongs to C_j. Here, by assuming
// that words appear independently in documents (this being the
// "naive" assumption), we can estimate
//
//    P(D|C_j) ~= P(W_1|C_j)*...*P(W_k|C_j)
//
// where P(W_i|C_j) is the probability of seeing the given word
// in a document of the given class. Finally, P(D) can be seen as
// merely a scaling factor and is not strictly relevant to
// classificiation, unless you want to normalize the resulting
// scores and actually see probabilities. In this case, note that
//
//    P(D) = SUM_j(P(D|C_j)*P(C_j))
//
// One practical issue with performing these calculations is the
// possibility of float64 underflow when calculating P(D|C_j), as
// individual word probabilities can be arbitrarily small, and
// a document can have an arbitrarily large number of them. A
// typical method for dealing with this case is to transform the
// probability to the log domain and perform additions instead
// of multiplications:
//
//   log P(C_j|D) ~ log(P(C_j)) + SUM_i(log P(W_i|C_j))
//
// where i = 1, ..., k. Note that by doing this, we are discarding
// the scaling factor P(D) and our scores are no longer
// probabilities.
//
package bayesian

import (
    "math"
    "gob"
    "os"
)

// defaultProb is the tiny non-zero probability that a word
// we have not seen before appears in the class.
const defaultProb = 0.00000000001

// This type defines a set of classes that the classifier will
// filter: C = {C_1, ..., C_n}. You should define your classes
// as a set of constants, for example as follows:
//
//    const (
//        Good Class = "Good"
//        Bad Class = "Bad
//    )
//
type Class string

// Classifier implements the Naive Bayesian Classifier.
type Classifier struct {
    Classes []Class
    datas map[Class]*classData
}

// serializableClassifier represents an container for
// classifier objects whose fields are modifiable by
// reflection and are therefore writeable.
type serializableClassifier struct {
    Classes []Class
    Datas map[Class]*classData
}

// classData holds the frequency data for words in a
// particular class. In the future, we may replace this
// structure with a trie-like structure for more
// efficient storage.
type classData struct {
    Freqs map[string]int
    Total int
}

// newClassData creates a new empty class data node.
func newClassData() *classData {
    return &classData{
        Freqs: make(map[string]int),
    }
}

// P(W|Cj) -- the probability of seeing a particular word
// in a document of this class.
func (d *classData) getWordProb(word string) float64 {
    value, ok := d.Freqs[word]
    if !ok {
        return defaultProb
    }
    return float64(value)/float64(d.Total)
}

// P(D|C_j) -- the probability of seeing this set of words
// in a document of this class.
//
// Note that words should not be empty, and this method of
// calulation is prone to underflow if there are many words
// and their individual probabilties are small.
func (d *classData) getWordsProb(words []string) (prob float64) {
    prob = 1
    for _, word := range words {
        prob *= d.getWordProb(word)
    }
    return
}

// New creates a new Classifier. The classes the provided
// should be at least 2 in number and unique from each other.
func NewClassifier(classes ...Class) (inst *Classifier) {
    if len(classes) < 2 {
        panic("provide at least two classes")
    }
    inst = &Classifier{
            classes,
            make(map[Class]*classData),
    }
    for _, class := range classes {
        inst.datas[class] = newClassData()
    }
    return
}

// NewClassifierFromFile loads an existing classifier from
// disk. The classifier was previously saved with a call
// to WriteToFile().
func NewClassifierFromFile(name string) (c *Classifier, err os.Error) {
    file, err := os.Open(name)
    if err != nil {
        return nil, err
    }
    dec := gob.NewDecoder(file)
    w := new(serializableClassifier)
    err = dec.Decode(w)

    return &Classifier{w.Classes, w.Datas}, err
}

// getPriors returns the prior probabilities for the
// classes provided -- P(C_i). There is a way to
// smooth priors, currently not implemented here.
func (c *Classifier) getPriors() (priors []float64) {
    n := len(c.Classes)
    priors = make([]float64, n, n)
    sum := 0
    for index, class := range c.Classes {
        total := c.datas[class].Total;
        priors[index] = float64(total)
        sum += total
    }
    if sum != 0 {
        for i := 0; i < n; i++ {
            priors[i] /= float64(sum)
        }
    }
    return
}

// Learn will train the classifier on the provided data.
func (c *Classifier) Learn(words []string, which Class) {
    data := c.datas[which]
    for _, word := range words {
        data.Freqs[word]++
        data.Total++
    }
}

// Scores will produce an array of scores that correspond
// to its opinion on the document in question, and whether it
// belongs to the given class. The order of the scores
// in the return values follows the order of the inital array
// of Class objects parameterized to the NewClassifier() function.
// If no training data has been provided, c will return
// a 0 array.
//
// The value of the score is proportional to the likelihood,
// even if the score is negative, so that the score with the
// greatest value corresponds to the most likely class.
//
// Additionally, c function will return the index of the
// maximum probability. The value of c number is given by
// scores[inx]. The class of that corresponds to c number
// is classifier.Classes[inx]. If more than one of the
// returned probabilities has the maximum values, then
// strict is false.
func (c *Classifier) Scores(words []string) (scores []float64, inx int, strict bool) {
    n := len(c.Classes)
    scores = make([]float64, n, n)
    priors := c.getPriors()

    // calculate the score for each class
    for index, class := range c.Classes {
        data := c.datas[class]
        // c is the sum of the logarithms
        // as outlined in the refresher
        score := math.Log(priors[index])
        for _, word := range words {
            score += math.Log(data.getWordProb(word))
        }
        scores[index] = score
    }
    inx, strict = findMax(scores)
    return scores, inx, strict
}

// Probabilities works the same as Score, but delivers
// actual probabilities as discussed above. Note that float64
// underflow is possible if the word list contains too
// many doc that have probabilities very close to 0.
func (c *Classifier) Probabilities(doc []string) (scores []float64, inx int, strict bool) {
    n := len(c.Classes)
    scores = make([]float64, n, n)
    priors := c.getPriors()
    sum := float64(0)
    // calculate the score for each class
    for index, class := range c.Classes {
        data := c.datas[class]
        // c is the sum of the logarithms
        // as outlined in the refresher
        score := priors[index]
        for _, word := range doc {
            score *= data.getWordProb(word)
        }
        scores[index] = score
        sum += score
    }
    for i := 0; i < n; i++ {
        scores[i] /= sum
    }
    inx, strict = findMax(scores)
    return scores, inx, strict
}

// WordFrequencies returns a matrix of word frequencies that currently
// exist in the classifier for each class state for the given input
// words. In other words, if you obtain the frequencies
//
//    freqs := c.WordFrequencies(/* ... array of j words ... */)
//
// then the expression freq[i][j] represents the frequency of the j-th
// word within the i-th class.
func (c *Classifier) WordFrequencies(words []string) (freqMatrix [][]float64) {
    n, l := len(c.Classes), len(words)
    freqMatrix = make([][]float64, n)
    for i, _ := range freqMatrix {
        arr := make([]float64, l)
        data := c.datas[c.Classes[i]]
        for j, _ := range arr {
            arr[j] = data.getWordProb(words[j])
        }
        freqMatrix[i] = arr
    }
    return
}

// Serialize this classifier to a file.
func (c *Classifier) WriteToFile(name string) (err os.Error) {
    file, err := os.OpenFile(name, os.O_WRONLY | os.O_CREATE, 0655)
    if err != nil {
        return err
    }
    enc := gob.NewEncoder(file)
    err = enc.Encode(&serializableClassifier{c.Classes, c.datas})
    return
}

// findMax finds the maximum of a set of scores; if the
// maximum is strict -- that is, it is the single unique
// maximum from the set -- then strict has return value
// true. Otherwise it is false.
func findMax(scores []float64) (inx int, strict bool) {
    inx = 0
    strict = true
    for i := 1; i < len(scores); i++ {
        if scores[inx] < scores[i] {
            inx = i
            strict = true
        } else if scores[inx] == scores[i] {
            strict = false
        }
    }
    return
}
<|endoftext|>