<commit_before>/*
Copyright 2016 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 cloudprovider

import (
	"bytes"
	"fmt"
	"math"
	"time"

	apiv1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/autoscaler/cluster-autoscaler/utils/errors"
	schedulercache "k8s.io/kubernetes/pkg/scheduler/cache"
)

// CloudProvider contains configuration info and functions for interacting with
// cloud provider (GCE, AWS, etc).
type CloudProvider interface {
	// Name returns name of the cloud provider.
	Name() string

	// NodeGroups returns all node groups configured for this cloud provider.
	NodeGroups() []NodeGroup

	// NodeGroupForNode returns the node group for the given node, nil if the node
	// should not be processed by cluster autoscaler, or non-nil error if such
	// occurred. Must be implemented.
	NodeGroupForNode(*apiv1.Node) (NodeGroup, error)

	// Pricing returns pricing model for this cloud provider or error if not available.
	// Implementation optional.
	Pricing() (PricingModel, errors.AutoscalerError)

	// GetAvailableMachineTypes get all machine types that can be requested from the cloud provider.
	// Implementation optional.
	GetAvailableMachineTypes() ([]string, error)

	// NewNodeGroup builds a theoretical node group based on the node definition provided. The node group is not automatically
	// created on the cloud provider side. The node group is not returned by NodeGroups() until it is created.
	// Implementation optional.
	NewNodeGroup(machineType string, labels map[string]string, systemLabels map[string]string,
		taints []apiv1.Taint, extraResources map[string]resource.Quantity) (NodeGroup, error)

	// GetResourceLimiter returns struct containing limits (max, min) for resources (cores, memory etc.).
	GetResourceLimiter() (*ResourceLimiter, error)

	// Cleanup cleans up open resources before the cloud provider is destroyed, i.e. go routines etc.
	Cleanup() error

	// Refresh is called before every main loop and can be used to dynamically update cloud provider state.
	// In particular the list of node groups returned by NodeGroups can change as a result of CloudProvider.Refresh().
	Refresh() error
}

// ErrNotImplemented is returned if a method is not implemented.
var ErrNotImplemented errors.AutoscalerError = errors.NewAutoscalerError(errors.InternalError, "Not implemented")

// ErrAlreadyExist is returned if a method is not implemented.
var ErrAlreadyExist errors.AutoscalerError = errors.NewAutoscalerError(errors.InternalError, "Already exist")

// ErrIllegalConfiguration is returned when trying to create NewNodeGroup with
// configuration that is not supported by cloudprovider.
var ErrIllegalConfiguration errors.AutoscalerError = errors.NewAutoscalerError(errors.InternalError, "Configuration not allowed by cloud provider")

// NodeGroup contains configuration info and functions to control a set
// of nodes that have the same capacity and set of labels.
type NodeGroup interface {
	// MaxSize returns maximum size of the node group.
	MaxSize() int

	// MinSize returns minimum size of the node group.
	MinSize() int

	// TargetSize returns the current target size of the node group. It is possible that the
	// number of nodes in Kubernetes is different at the moment but should be equal
	// to Size() once everything stabilizes (new nodes finish startup and registration or
	// removed nodes are deleted completely). Implementation required.
	TargetSize() (int, error)

	// IncreaseSize increases the size of the node group. To delete a node you need
	// to explicitly name it and use DeleteNode. This function should wait until
	// node group size is updated. Implementation required.
	IncreaseSize(delta int) error

	// DeleteNodes deletes nodes from this node group. Error is returned either on
	// failure or if the given node doesn't belong to this node group. This function
	// should wait until node group size is updated. Implementation required.
	DeleteNodes([]*apiv1.Node) error

	// DecreaseTargetSize decreases the target size of the node group. This function
	// doesn't permit to delete any existing node and can be used only to reduce the
	// request for new nodes that have not been yet fulfilled. Delta should be negative.
	// It is assumed that cloud provider will not delete the existing nodes when there
	// is an option to just decrease the target. Implementation required.
	DecreaseTargetSize(delta int) error

	// Id returns an unique identifier of the node group.
	Id() string

	// Debug returns a string containing all information regarding this node group.
	Debug() string

	// Nodes returns a list of all nodes that belong to this node group.
	Nodes() ([]string, error)

	// TemplateNodeInfo returns a schedulercache.NodeInfo structure of an empty
	// (as if just started) node. This will be used in scale-up simulations to
	// predict what would a new node look like if a node group was expanded. The returned
	// NodeInfo is expected to have a fully populated Node object, with all of the labels,
	// capacity and allocatable information as well as all pods that are started on
	// the node by default, using manifest (most likely only kube-proxy). Implementation optional.
	TemplateNodeInfo() (*schedulercache.NodeInfo, error)

	// Exist checks if the node group really exists on the cloud provider side. Allows to tell the
	// theoretical node group from the real one. Implementation required.
	Exist() bool

	// Create creates the node group on the cloud provider side. Implementation optional.
	Create() error

	// Delete deletes the node group on the cloud provider side.
	// This will be executed only for autoprovisioned node groups, once their size drops to 0.
	// Implementation optional.
	Delete() error

	// Autoprovisioned returns true if the node group is autoprovisioned. An autoprovisioned group
	// was created by CA and can be deleted when scaled to 0.
	Autoprovisioned() bool
}

// PricingModel contains information about the node price and how it changes in time.
type PricingModel interface {
	// NodePrice returns a price of running the given node for a given period of time.
	// All prices returned by the structure should be in the same currency.
	NodePrice(node *apiv1.Node, startTime time.Time, endTime time.Time) (float64, error)

	// PodPrice returns a theoretical minimum price of running a pod for a given
	// period of time on a perfectly matching machine.
	PodPrice(pod *apiv1.Pod, startTime time.Time, endTime time.Time) (float64, error)
}

const (
	// ResourceNameCores is string name for cores. It's used by ResourceLimiter.
	ResourceNameCores = "cpu"
	// ResourceNameMemory is string name for memory. It's used by ResourceLimiter.
	// Memory should always be provided in bytes.
	ResourceNameMemory = "memory"
)

// ResourceLimiter contains limits (max, min) for resources (cores, memory etc.).
type ResourceLimiter struct {
	minLimits map[string]int64
	maxLimits map[string]int64
}

// NewResourceLimiter creates new ResourceLimiter for map. Maps are deep copied.
func NewResourceLimiter(minLimits map[string]int64, maxLimits map[string]int64) *ResourceLimiter {
	minLimitsCopy := make(map[string]int64)
	maxLimitsCopy := make(map[string]int64)
	for key, value := range minLimits {
		minLimitsCopy[key] = value
	}
	for key, value := range maxLimits {
		maxLimitsCopy[key] = value
	}
	return &ResourceLimiter{minLimitsCopy, maxLimitsCopy}
}

// GetMin returns minimal number of resources for a given resource type.
func (r *ResourceLimiter) GetMin(resourceName string) int64 {
	result, found := r.minLimits[resourceName]
	if found {
		return result
	}
	return 0
}

// GetMax returns maximal number of resources for a given resource type.
func (r *ResourceLimiter) GetMax(resourceName string) int64 {
	result, found := r.maxLimits[resourceName]
	if found {
		return result
	}
	return math.MaxInt64
}

// GetResources returns list of all resource names for which min or max limits are defined
func (r *ResourceLimiter) GetResources() []string {
	minResources := sets.StringKeySet(r.minLimits)
	maxResources := sets.StringKeySet(r.maxLimits)
	return minResources.Union(maxResources).List()
}

func (r *ResourceLimiter) String() string {
	var buffer bytes.Buffer
	for name, maxLimit := range r.maxLimits {
		if buffer.Len() > 0 {
			buffer.WriteString(", ")
		}
		buffer.WriteString(fmt.Sprintf("{%s : %d - %d}", name, r.minLimits[name], maxLimit))
	}
	return buffer.String()
}
<commit_msg>Cleanup ResourceLimiter.String method<commit_after>/*
Copyright 2016 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 cloudprovider

import (
	"bytes"
	"fmt"
	"math"
	"time"

	apiv1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/autoscaler/cluster-autoscaler/utils/errors"
	schedulercache "k8s.io/kubernetes/pkg/scheduler/cache"
)

// CloudProvider contains configuration info and functions for interacting with
// cloud provider (GCE, AWS, etc).
type CloudProvider interface {
	// Name returns name of the cloud provider.
	Name() string

	// NodeGroups returns all node groups configured for this cloud provider.
	NodeGroups() []NodeGroup

	// NodeGroupForNode returns the node group for the given node, nil if the node
	// should not be processed by cluster autoscaler, or non-nil error if such
	// occurred. Must be implemented.
	NodeGroupForNode(*apiv1.Node) (NodeGroup, error)

	// Pricing returns pricing model for this cloud provider or error if not available.
	// Implementation optional.
	Pricing() (PricingModel, errors.AutoscalerError)

	// GetAvailableMachineTypes get all machine types that can be requested from the cloud provider.
	// Implementation optional.
	GetAvailableMachineTypes() ([]string, error)

	// NewNodeGroup builds a theoretical node group based on the node definition provided. The node group is not automatically
	// created on the cloud provider side. The node group is not returned by NodeGroups() until it is created.
	// Implementation optional.
	NewNodeGroup(machineType string, labels map[string]string, systemLabels map[string]string,
		taints []apiv1.Taint, extraResources map[string]resource.Quantity) (NodeGroup, error)

	// GetResourceLimiter returns struct containing limits (max, min) for resources (cores, memory etc.).
	GetResourceLimiter() (*ResourceLimiter, error)

	// Cleanup cleans up open resources before the cloud provider is destroyed, i.e. go routines etc.
	Cleanup() error

	// Refresh is called before every main loop and can be used to dynamically update cloud provider state.
	// In particular the list of node groups returned by NodeGroups can change as a result of CloudProvider.Refresh().
	Refresh() error
}

// ErrNotImplemented is returned if a method is not implemented.
var ErrNotImplemented errors.AutoscalerError = errors.NewAutoscalerError(errors.InternalError, "Not implemented")

// ErrAlreadyExist is returned if a method is not implemented.
var ErrAlreadyExist errors.AutoscalerError = errors.NewAutoscalerError(errors.InternalError, "Already exist")

// ErrIllegalConfiguration is returned when trying to create NewNodeGroup with
// configuration that is not supported by cloudprovider.
var ErrIllegalConfiguration errors.AutoscalerError = errors.NewAutoscalerError(errors.InternalError, "Configuration not allowed by cloud provider")

// NodeGroup contains configuration info and functions to control a set
// of nodes that have the same capacity and set of labels.
type NodeGroup interface {
	// MaxSize returns maximum size of the node group.
	MaxSize() int

	// MinSize returns minimum size of the node group.
	MinSize() int

	// TargetSize returns the current target size of the node group. It is possible that the
	// number of nodes in Kubernetes is different at the moment but should be equal
	// to Size() once everything stabilizes (new nodes finish startup and registration or
	// removed nodes are deleted completely). Implementation required.
	TargetSize() (int, error)

	// IncreaseSize increases the size of the node group. To delete a node you need
	// to explicitly name it and use DeleteNode. This function should wait until
	// node group size is updated. Implementation required.
	IncreaseSize(delta int) error

	// DeleteNodes deletes nodes from this node group. Error is returned either on
	// failure or if the given node doesn't belong to this node group. This function
	// should wait until node group size is updated. Implementation required.
	DeleteNodes([]*apiv1.Node) error

	// DecreaseTargetSize decreases the target size of the node group. This function
	// doesn't permit to delete any existing node and can be used only to reduce the
	// request for new nodes that have not been yet fulfilled. Delta should be negative.
	// It is assumed that cloud provider will not delete the existing nodes when there
	// is an option to just decrease the target. Implementation required.
	DecreaseTargetSize(delta int) error

	// Id returns an unique identifier of the node group.
	Id() string

	// Debug returns a string containing all information regarding this node group.
	Debug() string

	// Nodes returns a list of all nodes that belong to this node group.
	Nodes() ([]string, error)

	// TemplateNodeInfo returns a schedulercache.NodeInfo structure of an empty
	// (as if just started) node. This will be used in scale-up simulations to
	// predict what would a new node look like if a node group was expanded. The returned
	// NodeInfo is expected to have a fully populated Node object, with all of the labels,
	// capacity and allocatable information as well as all pods that are started on
	// the node by default, using manifest (most likely only kube-proxy). Implementation optional.
	TemplateNodeInfo() (*schedulercache.NodeInfo, error)

	// Exist checks if the node group really exists on the cloud provider side. Allows to tell the
	// theoretical node group from the real one. Implementation required.
	Exist() bool

	// Create creates the node group on the cloud provider side. Implementation optional.
	Create() error

	// Delete deletes the node group on the cloud provider side.
	// This will be executed only for autoprovisioned node groups, once their size drops to 0.
	// Implementation optional.
	Delete() error

	// Autoprovisioned returns true if the node group is autoprovisioned. An autoprovisioned group
	// was created by CA and can be deleted when scaled to 0.
	Autoprovisioned() bool
}

// PricingModel contains information about the node price and how it changes in time.
type PricingModel interface {
	// NodePrice returns a price of running the given node for a given period of time.
	// All prices returned by the structure should be in the same currency.
	NodePrice(node *apiv1.Node, startTime time.Time, endTime time.Time) (float64, error)

	// PodPrice returns a theoretical minimum price of running a pod for a given
	// period of time on a perfectly matching machine.
	PodPrice(pod *apiv1.Pod, startTime time.Time, endTime time.Time) (float64, error)
}

const (
	// ResourceNameCores is string name for cores. It's used by ResourceLimiter.
	ResourceNameCores = "cpu"
	// ResourceNameMemory is string name for memory. It's used by ResourceLimiter.
	// Memory should always be provided in bytes.
	ResourceNameMemory = "memory"
)

// ResourceLimiter contains limits (max, min) for resources (cores, memory etc.).
type ResourceLimiter struct {
	minLimits map[string]int64
	maxLimits map[string]int64
}

// NewResourceLimiter creates new ResourceLimiter for map. Maps are deep copied.
func NewResourceLimiter(minLimits map[string]int64, maxLimits map[string]int64) *ResourceLimiter {
	minLimitsCopy := make(map[string]int64)
	maxLimitsCopy := make(map[string]int64)
	for key, value := range minLimits {
		minLimitsCopy[key] = value
	}
	for key, value := range maxLimits {
		maxLimitsCopy[key] = value
	}
	return &ResourceLimiter{minLimitsCopy, maxLimitsCopy}
}

// GetMin returns minimal number of resources for a given resource type.
func (r *ResourceLimiter) GetMin(resourceName string) int64 {
	result, found := r.minLimits[resourceName]
	if found {
		return result
	}
	return 0
}

// GetMax returns maximal number of resources for a given resource type.
func (r *ResourceLimiter) GetMax(resourceName string) int64 {
	result, found := r.maxLimits[resourceName]
	if found {
		return result
	}
	return math.MaxInt64
}

// GetResources returns list of all resource names for which min or max limits are defined
func (r *ResourceLimiter) GetResources() []string {
	minResources := sets.StringKeySet(r.minLimits)
	maxResources := sets.StringKeySet(r.maxLimits)
	return minResources.Union(maxResources).List()
}

func (r *ResourceLimiter) String() string {
	var buffer bytes.Buffer
	for _, name := range r.GetResources() {
		if buffer.Len() > 0 {
			buffer.WriteString(", ")
		}
		buffer.WriteString(fmt.Sprintf("{%s : %d - %d}", name, r.GetMin(name), r.GetMax(name)))
	}
	return buffer.String()
}
<|endoftext|>


<commit_before>package octopussy

import (
	"encoding/json"
	"syscall"
	"time"

	"github.com/gorilla/websocket"
	"github.com/streadway/amqp"
)

const (
	// Time allowed to read the next pong message from the peer.
	pongWait = 60 * time.Second
	// Send pings to peer with this period. Must be less than pongWait.
	pingPeriod = (pongWait * 9) / 10
)

type handler struct {
	channel   *amqp.Channel
	messages  <-chan amqp.Delivery
	websocket *websocket.Conn
	ticker    *time.Ticker
	exchange  string
	queue     *amqp.Queue
	topics    []string
}

type step func() error

type stepper struct {
	err error
}

func (s *stepper) perform(steps ...step) {
	for _, st := range steps {
		if s.err == nil {
			s.err = st()
		}
	}
}

func newHandler(ch *amqp.Channel, ws *websocket.Conn, ex string) *handler {
	return &handler{
		channel:   ch,
		websocket: ws,
		exchange:  ex,
	}
}

func (h *handler) handle() error {
	if err := h.setUp(); err != nil {
		return err
	}
	return h.consume()
}

func (h *handler) setUp() error {
	s := &stepper{}
	s.perform(h.getTopics, h.declareExchange, h.declareQueue, h.subscribeToTopics)
	if s.err != nil {
		return s.err
	}
	h.setUpTicker()
	return h.createChannel()
}

func (h *handler) getTopics() error {
	_, r, err := h.websocket.NextReader()
	if err != nil {
		return err
	}
	if err := json.NewDecoder(r).Decode(&h.topics); err != nil {
		return err
	}
	return nil
}

func (h *handler) declareExchange() error {
	return h.channel.ExchangeDeclare(
		h.exchange, // name
		"topic",    // type
		true,       // durable
		false,      // auto-deleted
		false,      // internal
		false,      // no-wait
		nil,        // arguments
	)
}

func (h *handler) declareQueue() error {
	queue, err := h.channel.QueueDeclare(
		"",    // name
		false, // durable
		false, // delete when usused
		true,  // exclusive
		false, // no-wait
		nil,   // arguments
	)
	if err == nil {
		h.queue = &queue
	}
	return err
}

func (h *handler) subscribeToTopics() error {
	for _, topic := range h.topics {
		if err := h.subscribeToTopic(topic); err != nil {
			return err
		}
	}
	return nil
}

func (h *handler) subscribeToTopic(topic string) error {
	return h.channel.QueueBind(
		h.queue.Name, // queue name
		topic,        // routing key
		h.exchange,   // exchange
		false,        // no-wait
		nil,          // arguments
	)
}

func (h *handler) createChannel() error {
	msgChan, err := h.channel.Consume(
		h.queue.Name, // queue
		"",           // consumer
		true,         // auto-ack
		false,        // exclusive
		false,        // no-local
		false,        // no-wait
		nil,          // arguments
	)
	if err == nil {
		h.messages = msgChan
	}
	return err
}

func pongHandler(ws *websocket.Conn) func(_ string) error {
	return func(_ string) error {
		ws.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	}
}

func (h *handler) setUpTicker() {
	h.ticker = time.NewTicker(pingPeriod)
	h.websocket.SetReadDeadline(time.Now().Add(pongWait))
	h.websocket.SetPongHandler(pongHandler(h.websocket))
}

func (h *handler) consume() error {
	defer h.Close()
	for {
		if err := h.consumeOne(); err != nil {
			return handlePipeError(err)
		}
	}
	return nil
}

func (h *handler) consumeOne() error {
	select {
	case msg := <-h.messages:
		return h.websocket.WriteMessage(websocket.TextMessage, msg.Body)
	case <-h.ticker.C:
		break
	}
	ttl := time.Now().Add(5 * time.Second)
	return h.websocket.WriteControl(websocket.PingMessage, []byte{}, ttl)
}

func handlePipeError(err error) error {
	if err == syscall.EPIPE {
		return nil
	}
	return err
}

func (h *handler) Close() {
	h.channel.Close()
	h.websocket.Close()
	h.ticker.Stop()
}
<commit_msg>Delete unused queues<commit_after>package octopussy

import (
	"encoding/json"
	"syscall"
	"time"

	"github.com/gorilla/websocket"
	"github.com/streadway/amqp"
)

const (
	// Time allowed to read the next pong message from the peer.
	pongWait = 60 * time.Second
	// Send pings to peer with this period. Must be less than pongWait.
	pingPeriod = (pongWait * 9) / 10
)

type handler struct {
	channel   *amqp.Channel
	messages  <-chan amqp.Delivery
	websocket *websocket.Conn
	ticker    *time.Ticker
	exchange  string
	queue     *amqp.Queue
	topics    []string
}

type step func() error

type stepper struct {
	err error
}

func (s *stepper) perform(steps ...step) {
	for _, st := range steps {
		if s.err == nil {
			s.err = st()
		}
	}
}

func newHandler(ch *amqp.Channel, ws *websocket.Conn, ex string) *handler {
	return &handler{
		channel:   ch,
		websocket: ws,
		exchange:  ex,
	}
}

func (h *handler) handle() error {
	if err := h.setUp(); err != nil {
		return err
	}
	return h.consume()
}

func (h *handler) setUp() error {
	s := &stepper{}
	s.perform(h.getTopics, h.declareExchange, h.declareQueue, h.subscribeToTopics)
	if s.err != nil {
		return s.err
	}
	h.setUpTicker()
	return h.createChannel()
}

func (h *handler) getTopics() error {
	_, r, err := h.websocket.NextReader()
	if err != nil {
		return err
	}
	if err := json.NewDecoder(r).Decode(&h.topics); err != nil {
		return err
	}
	return nil
}

func (h *handler) declareExchange() error {
	return h.channel.ExchangeDeclare(
		h.exchange, // name
		"topic",    // type
		true,       // durable
		false,      // auto-deleted
		false,      // internal
		false,      // no-wait
		nil,        // arguments
	)
}

func (h *handler) declareQueue() error {
	queue, err := h.channel.QueueDeclare(
		"",    // name
		false, // durable
		true,  // delete when usused
		true,  // exclusive
		false, // no-wait
		nil,   // arguments
	)
	if err == nil {
		h.queue = &queue
	}
	return err
}

func (h *handler) subscribeToTopics() error {
	for _, topic := range h.topics {
		if err := h.subscribeToTopic(topic); err != nil {
			return err
		}
	}
	return nil
}

func (h *handler) subscribeToTopic(topic string) error {
	return h.channel.QueueBind(
		h.queue.Name, // queue name
		topic,        // routing key
		h.exchange,   // exchange
		false,        // no-wait
		nil,          // arguments
	)
}

func (h *handler) createChannel() error {
	msgChan, err := h.channel.Consume(
		h.queue.Name, // queue
		"",           // consumer
		true,         // auto-ack
		false,        // exclusive
		false,        // no-local
		false,        // no-wait
		nil,          // arguments
	)
	if err == nil {
		h.messages = msgChan
	}
	return err
}

func pongHandler(ws *websocket.Conn) func(_ string) error {
	return func(_ string) error {
		ws.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	}
}

func (h *handler) setUpTicker() {
	h.ticker = time.NewTicker(pingPeriod)
	h.websocket.SetReadDeadline(time.Now().Add(pongWait))
	h.websocket.SetPongHandler(pongHandler(h.websocket))
}

func (h *handler) consume() error {
	defer h.Close()
	for {
		if err := h.consumeOne(); err != nil {
			return handlePipeError(err)
		}
	}
	return nil
}

func (h *handler) consumeOne() error {
	select {
	case msg := <-h.messages:
		return h.websocket.WriteMessage(websocket.TextMessage, msg.Body)
	case <-h.ticker.C:
		break
	}
	ttl := time.Now().Add(5 * time.Second)
	return h.websocket.WriteControl(websocket.PingMessage, []byte{}, ttl)
}

func handlePipeError(err error) error {
	if err == syscall.EPIPE {
		return nil
	}
	return err
}

func (h *handler) Close() {
	h.channel.Close()
	h.websocket.Close()
	h.ticker.Stop()
}
<|endoftext|>


<commit_before>package lsp

import (
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/sourcegraph/go-lsp"

	"github.com/thought-machine/please/rules"
	"github.com/thought-machine/please/src/core"
	"github.com/thought-machine/please/src/parse/asp"
	"github.com/thought-machine/please/tools/build_langserver/lsp/astutils"
)

// definition implements 'go-to-definition' support.
// It is also used for go-to-declaration since we do not make a distinction between the two.
func (h *Handler) definition(params *lsp.TextDocumentPositionParams) ([]lsp.Location, error) {
	doc := h.doc(params.TextDocument.URI)
	ast := h.parseIfNeeded(doc)

	var locs []lsp.Location
	pos := aspPos(params.Position)
	asp.WalkAST(ast, func(expr *asp.Expression) bool {
		if !asp.WithinRange(pos, expr.Pos, expr.EndPos) {
			return false
		}

		if expr.Val.Ident != nil {
			if loc := h.findGlobal(expr.Val.Ident.Name); loc.URI != "" {
				locs = append(locs, loc)
			}
			return false
		}

		if expr.Val.String != "" {
			label := astutils.TrimStrLit(expr.Val.String)
			if loc := h.findLabel(doc.PkgName, label); loc.URI != "" {
				locs = append(locs, loc)
			}
			return false
		}

		return true
	})
	// It might also be a statement.
	asp.WalkAST(ast, func(stmt *asp.Statement) bool {
		if stmt.Ident != nil {
			endPos := stmt.Pos
			// TODO(jpoole): The AST should probably just have this information
			endPos.Column += len(stmt.Ident.Name)

			if !asp.WithinRange(pos, stmt.Pos, endPos) {
				return false
			}

			if loc := h.findGlobal(stmt.Ident.Name); loc.URI != "" {
				locs = append(locs, loc)
			}
			return false
		}
		return true
	})
	return locs, nil
}

// findLabel will attempt to parse the package containing the label to determine the position within that build
// file that that rule exists
func (h *Handler) findLabel(currentPath, label string) lsp.Location {
	l, err := core.TryParseBuildLabel(label, currentPath, "")

	// If we can't parse this as a build label, it might be a file on disk
	if err != nil {
		p := filepath.Join(h.root, currentPath, label)
		if _, err := os.Lstat(p); err == nil {
			return lsp.Location{URI: lsp.DocumentURI("file://" + p)}
		}
		return lsp.Location{}
	}

	pkg := h.state.Graph.PackageByLabel(l)
	uri := lsp.DocumentURI("file://" + pkg.Filename)
	loc := lsp.Location{URI: uri}
	doc, err := h.maybeOpenDoc(uri)
	if err != nil {
		log.Warningf("failed to open doc for completion: %v", err)
		return loc
	}
	ast := h.parseIfNeeded(doc)

	// Try and find expression function calls
	asp.WalkAST(ast, func(expr *asp.Expression) bool {
		if expr.Val != nil && expr.Val.Call != nil {
			if findName(expr.Val.Call.Arguments) == l.Name {
				loc.Range = lsp.Range{
					Start: lsp.Position{Line: expr.Pos.Line, Character: expr.Pos.Column},
					End:   lsp.Position{Line: expr.EndPos.Line, Character: expr.EndPos.Column},
				}
			}
			return false
		}
		return true
	})

	asp.WalkAST(ast, func(stmt *asp.Statement) bool {
		if stmt.Ident != nil && stmt.Ident.Action != nil && stmt.Ident.Action.Call != nil {
			if findName(stmt.Ident.Action.Call.Arguments) == l.Name {
				loc.Range = lsp.Range{
					Start: lsp.Position{Line: stmt.Pos.Line, Character: stmt.Pos.Column},
					End:   lsp.Position{Line: stmt.EndPos.Line, Character: stmt.EndPos.Column},
				}
			}
			return false
		}
		return true
	})

	return loc
}

// findName finds the name arguments to a function call. The name must be a simple string lit as we don't evaluate the
// package to deal with more complex expressions.
func findName(args []asp.CallArgument) string {
	for _, arg := range args {
		if arg.Name == "name" {
			if arg.Value.Val != nil && arg.Value.Val.String != "" {
				return astutils.TrimStrLit(arg.Value.Val.String)
			}
		}
	}
	return ""
}

// findGlobal returns the location of a global of the given name.
func (h *Handler) findGlobal(name string) lsp.Location {
	if f, present := h.builtins[name]; present {
		if f.FuncDef.IsBuiltin && !strings.Contains(f.Pos.Filename, "/") {
			// Extract the builtin to a temporary location so the user can see it.
			dir, err := os.UserCacheDir()
			if err != nil {
				log.Warning("Cannot determine user cache dir: %s", err)
				return lsp.Location{}
			} else if err := os.MkdirAll(path.Join(dir, "please"), core.DirPermissions); err != nil {
				log.Warning("Cannot create cache dir: %s", err)
				return lsp.Location{}
			}
			dest := path.Join(dir, "please", f.Pos.Filename)
			if data, err := rules.ReadAsset(f.Pos.Filename); err != nil {
				log.Warning("Failed to extract builtin rules for %s: %s", name, err)
				return lsp.Location{}
			} else if err := ioutil.WriteFile(dest, data, 0644); err != nil {
				log.Warning("Failed to extract builtin rules for %s: %s", name, err)
				return lsp.Location{}
			}
			f.Pos.Filename = dest
		}
		file := f.Pos.Filename
		if !path.IsAbs(file) {
			file = path.Join(h.root, f.Pos.Filename)
		}
		return lsp.Location{
			URI:   lsp.DocumentURI("file://" + file),
			Range: rng(f.Pos, f.EndPos),
		}
	}
	return lsp.Location{}
}
<commit_msg>Fix lsp server findLabel URI construction (#1943)<commit_after>package lsp

import (
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/sourcegraph/go-lsp"

	"github.com/thought-machine/please/rules"
	"github.com/thought-machine/please/src/core"
	"github.com/thought-machine/please/src/parse/asp"
	"github.com/thought-machine/please/tools/build_langserver/lsp/astutils"
)

// definition implements 'go-to-definition' support.
// It is also used for go-to-declaration since we do not make a distinction between the two.
func (h *Handler) definition(params *lsp.TextDocumentPositionParams) ([]lsp.Location, error) {
	doc := h.doc(params.TextDocument.URI)
	ast := h.parseIfNeeded(doc)

	var locs []lsp.Location
	pos := aspPos(params.Position)
	asp.WalkAST(ast, func(expr *asp.Expression) bool {
		if !asp.WithinRange(pos, expr.Pos, expr.EndPos) {
			return false
		}

		if expr.Val.Ident != nil {
			if loc := h.findGlobal(expr.Val.Ident.Name); loc.URI != "" {
				locs = append(locs, loc)
			}
			return false
		}

		if expr.Val.String != "" {
			label := astutils.TrimStrLit(expr.Val.String)
			if loc := h.findLabel(doc.PkgName, label); loc.URI != "" {
				locs = append(locs, loc)
			}
			return false
		}

		return true
	})
	// It might also be a statement.
	asp.WalkAST(ast, func(stmt *asp.Statement) bool {
		if stmt.Ident != nil {
			endPos := stmt.Pos
			// TODO(jpoole): The AST should probably just have this information
			endPos.Column += len(stmt.Ident.Name)

			if !asp.WithinRange(pos, stmt.Pos, endPos) {
				return false
			}

			if loc := h.findGlobal(stmt.Ident.Name); loc.URI != "" {
				locs = append(locs, loc)
			}
			return false
		}
		return true
	})
	return locs, nil
}

// findLabel will attempt to parse the package containing the label to determine the position within that build
// file that that rule exists
func (h *Handler) findLabel(currentPath, label string) lsp.Location {
	l, err := core.TryParseBuildLabel(label, currentPath, "")

	// If we can't parse this as a build label, it might be a file on disk
	if err != nil {
		p := filepath.Join(h.root, currentPath, label)
		if _, err := os.Lstat(p); err == nil {
			return lsp.Location{URI: lsp.DocumentURI("file://" + p)}
		}
		return lsp.Location{}
	}

	pkg := h.state.Graph.PackageByLabel(l)
	uri := lsp.DocumentURI("file://" + path.Join(h.root, pkg.Filename))
	loc := lsp.Location{URI: uri}
	doc, err := h.maybeOpenDoc(uri)
	if err != nil {
		log.Warningf("failed to open doc for completion: %v", err)
		return loc
	}
	ast := h.parseIfNeeded(doc)

	// Try and find expression function calls
	asp.WalkAST(ast, func(expr *asp.Expression) bool {
		if expr.Val != nil && expr.Val.Call != nil {
			if findName(expr.Val.Call.Arguments) == l.Name {
				loc.Range = lsp.Range{
					Start: lsp.Position{Line: expr.Pos.Line, Character: expr.Pos.Column},
					End:   lsp.Position{Line: expr.EndPos.Line, Character: expr.EndPos.Column},
				}
			}
			return false
		}
		return true
	})

	asp.WalkAST(ast, func(stmt *asp.Statement) bool {
		if stmt.Ident != nil && stmt.Ident.Action != nil && stmt.Ident.Action.Call != nil {
			if findName(stmt.Ident.Action.Call.Arguments) == l.Name {
				loc.Range = lsp.Range{
					Start: lsp.Position{Line: stmt.Pos.Line, Character: stmt.Pos.Column},
					End:   lsp.Position{Line: stmt.EndPos.Line, Character: stmt.EndPos.Column},
				}
			}
			return false
		}
		return true
	})

	return loc
}

// findName finds the name arguments to a function call. The name must be a simple string lit as we don't evaluate the
// package to deal with more complex expressions.
func findName(args []asp.CallArgument) string {
	for _, arg := range args {
		if arg.Name == "name" {
			if arg.Value.Val != nil && arg.Value.Val.String != "" {
				return astutils.TrimStrLit(arg.Value.Val.String)
			}
		}
	}
	return ""
}

// findGlobal returns the location of a global of the given name.
func (h *Handler) findGlobal(name string) lsp.Location {
	if f, present := h.builtins[name]; present {
		if f.FuncDef.IsBuiltin && !strings.Contains(f.Pos.Filename, "/") {
			// Extract the builtin to a temporary location so the user can see it.
			dir, err := os.UserCacheDir()
			if err != nil {
				log.Warning("Cannot determine user cache dir: %s", err)
				return lsp.Location{}
			} else if err := os.MkdirAll(path.Join(dir, "please"), core.DirPermissions); err != nil {
				log.Warning("Cannot create cache dir: %s", err)
				return lsp.Location{}
			}
			dest := path.Join(dir, "please", f.Pos.Filename)
			if data, err := rules.ReadAsset(f.Pos.Filename); err != nil {
				log.Warning("Failed to extract builtin rules for %s: %s", name, err)
				return lsp.Location{}
			} else if err := ioutil.WriteFile(dest, data, 0644); err != nil {
				log.Warning("Failed to extract builtin rules for %s: %s", name, err)
				return lsp.Location{}
			}
			f.Pos.Filename = dest
		}
		file := f.Pos.Filename
		if !path.IsAbs(file) {
			file = path.Join(h.root, f.Pos.Filename)
		}
		return lsp.Location{
			URI:   lsp.DocumentURI("file://" + file),
			Range: rng(f.Pos, f.EndPos),
		}
	}
	return lsp.Location{}
}
<|endoftext|>


<commit_before>package alertmanager

import (
	"encoding/json"
	"fmt"
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/hako/durafmt"
	"github.com/prometheus/alertmanager/types"
)

type silencesResponse struct {
	Data   []types.Silence `json:"data"`
	Status string          `json:"status"`
}

// ListSilences returns a slice of Silence and an error.
func ListSilences(logger log.Logger, alertmanagerURL string) ([]types.Silence, error) {
	resp, err := httpRetry(logger, http.MethodGet, alertmanagerURL+"/api/v1/silences")
	if err != nil {
		return nil, err
	}

	var silencesResponse silencesResponse
	dec := json.NewDecoder(resp.Body)
	defer resp.Body.Close()
	if err := dec.Decode(&silencesResponse); err != nil {
		return nil, err
	}

	silences := silencesResponse.Data
	sort.Slice(silences, func(i, j int) bool {
		return silences[i].EndsAt.After(silences[j].EndsAt)
	})

	return silences, err
}

// SilenceMessage converts a silences to a message string
func SilenceMessage(s types.Silence) string {
	var alertname, emoji, matchers, duration string

	for _, m := range s.Matchers {
		if m.Name == "alertname" {
			alertname = m.Value
		} else {
			matchers = matchers + fmt.Sprintf(` %s="%s"`, m.Name, m.Value)
		}
	}

	resolved := Resolved(s)
	if !resolved {
		emoji = " 🔕"
		duration = fmt.Sprintf(
			"*Started*: %s ago\n*Ends:* %s\n",
			durafmt.Parse(time.Since(s.StartsAt)),
			durafmt.Parse(time.Since(s.EndsAt)),
		)
	} else {
		duration = fmt.Sprintf(
			"*Ended*: %s ago\n*Duration*: %s",
			durafmt.Parse(time.Since(s.EndsAt)),
			durafmt.Parse(s.EndsAt.Sub(s.StartsAt)),
		)
	}

	return fmt.Sprintf(
		"%s%s\n```%s```\n%s\n",
		alertname, emoji,
		strings.TrimSpace(matchers),
		duration,
	)
}

// Resolved returns if a silence is reolved by EndsAt
func Resolved(s types.Silence) bool {
	if s.EndsAt.IsZero() {
		return false
	}
	return !s.EndsAt.After(time.Now())
}
<commit_msg>Fix type-o<commit_after>package alertmanager

import (
	"encoding/json"
	"fmt"
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/hako/durafmt"
	"github.com/prometheus/alertmanager/types"
)

type silencesResponse struct {
	Data   []types.Silence `json:"data"`
	Status string          `json:"status"`
}

// ListSilences returns a slice of Silence and an error.
func ListSilences(logger log.Logger, alertmanagerURL string) ([]types.Silence, error) {
	resp, err := httpRetry(logger, http.MethodGet, alertmanagerURL+"/api/v1/silences")
	if err != nil {
		return nil, err
	}

	var silencesResponse silencesResponse
	dec := json.NewDecoder(resp.Body)
	defer resp.Body.Close()
	if err := dec.Decode(&silencesResponse); err != nil {
		return nil, err
	}

	silences := silencesResponse.Data
	sort.Slice(silences, func(i, j int) bool {
		return silences[i].EndsAt.After(silences[j].EndsAt)
	})

	return silences, err
}

// SilenceMessage converts a silences to a message string
func SilenceMessage(s types.Silence) string {
	var alertname, emoji, matchers, duration string

	for _, m := range s.Matchers {
		if m.Name == "alertname" {
			alertname = m.Value
		} else {
			matchers = matchers + fmt.Sprintf(` %s="%s"`, m.Name, m.Value)
		}
	}

	resolved := Resolved(s)
	if !resolved {
		emoji = " 🔕"
		duration = fmt.Sprintf(
			"*Started*: %s ago\n*Ends:* %s\n",
			durafmt.Parse(time.Since(s.StartsAt)),
			durafmt.Parse(time.Since(s.EndsAt)),
		)
	} else {
		duration = fmt.Sprintf(
			"*Ended*: %s ago\n*Duration*: %s",
			durafmt.Parse(time.Since(s.EndsAt)),
			durafmt.Parse(s.EndsAt.Sub(s.StartsAt)),
		)
	}

	return fmt.Sprintf(
		"%s%s\n```%s```\n%s\n",
		alertname, emoji,
		strings.TrimSpace(matchers),
		duration,
	)
}

// Resolved returns if a silence is resolved by EndsAt
func Resolved(s types.Silence) bool {
	if s.EndsAt.IsZero() {
		return false
	}
	return !s.EndsAt.After(time.Now())
}
<|endoftext|>


<commit_before>package indicsoundex

import "testing"

func TestCalculate(t *testing.T) {
	// inArray := []string {"vasudeva", "kamath", "ವಾಸುದೇವ", "वासुदॆव"}
	inArray := []string{`ವಾಸುದೇವ`, `वासुदॆव`}
	// outArray := []string {"vA2C3D1A", "kA5A3000", "ವASCKDR0", "वASCKDR0" }
	outArray := []string{`ವASCKDR0`, `वASCKDR0`}

	for index, value := range inArray {
		if x, output := Calculate(value, 8), outArray[index]; x != output {
			t.Errorf("Calculate(%s) = %s was expecting %s", value, x, output)
		}
	}
}
<commit_msg>English test cases are now enabled<commit_after>package indicsoundex

import "testing"

func TestCalculate(t *testing.T) {
	inArray := []string{"vasudeva", "kamath", "ವಾಸುದೇವ", "वासुदॆव"}
	outArray := []string{"v231", "k53", "ವASCKDR0", "वASCKDR0"}

	for index, value := range inArray {
		if x, output := Calculate(value, 8), outArray[index]; x != output {
			t.Errorf("Calculate(%s) = %s was expecting %s", value, x, output)
		}
	}
}
<|endoftext|>


<commit_before>// +build !386
// +build !amd64

package glm

import (
	"math"
)

//------------------------------------------------------------------------------

func Sqrt(x float32) float32 {
	return float32(math.Sqrt(float64(x)))
}

//------------------------------------------------------------------------------
// Copyright (c) 2013 - Laurent Moussault <moussault.laurent@gmail.com>
<commit_msg>Default Floor implementation.<commit_after>// +build !386
// +build !amd64

package glm

import (
	"math"
)

//------------------------------------------------------------------------------

func Sqrt(x float32) float32 {
	return float32(math.Sqrt(float64(x)))
}

//------------------------------------------------------------------------------

func Floor(x float32) float32 {
	return float32(math.Floor(float64(x)))
}

//------------------------------------------------------------------------------
// Copyright (c) 2013 - Laurent Moussault <moussault.laurent@gmail.com>
<|endoftext|>


<commit_before>package walk
import (
	"unsafe"
	"syscall"
	"errors"
)
import . "github.com/lxn/go-winapi"

const (
	LB_ERR       = -1
	LB_ERRSPACE  = -2
)

type ListBox struct{
	WidgetBase
	maxItemTextWidth              int
	selectedIndexChangedPublisher EventPublisher
	dbClickedPublisher            EventPublisher
}

func NewListBox(parent Container)(*ListBox, error){
	//TODO: move to go-winapi/listbox
	//LBS_STANDARD := LBS_NOTIFY | LBS_SORT | WS_VSCROLL | WS_BORDER

	lb := &ListBox{}
	err := initChildWidget(
		lb,
		parent,
		"LISTBOX",
		WS_TABSTOP | WS_VISIBLE | LBS_NOTIFY | LBS_SORT | WS_VSCROLL | WS_BORDER,
		0)
	if err != nil{
		return nil, err
	}
	return lb, nil
}

func (*ListBox) origWndProcPtr() uintptr {
	return checkBoxOrigWndProcPtr
}

func (*ListBox) setOrigWndProcPtr(ptr uintptr) {
	checkBoxOrigWndProcPtr = ptr
}

func (*ListBox) LayoutFlags() LayoutFlags {
	return GrowableHorz | GrowableVert
}

func (this *ListBox) AddString(item string){
	SendMessage (this.hWnd, LB_ADDSTRING, 0,
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(item))))
}

//If this parameter is -1, the string is added to the end of the list.
func (this *ListBox) InsertString(index int, item string) error{
	if index < -1{
		return errors.New("Invalid index")
	}

	ret := SendMessage(this.hWnd, LB_INSERTSTRING, uintptr(index), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))))
	if ret == LB_ERRSPACE || ret == LB_ERR {
		return errors.New("LB_ERR or LB_ERRSPACE")
	}
	return nil
}

func (this *ListBox) DeleteString(index uint) error{
	ret := SendMessage(this.hWnd, LB_DELETESTRING, uintptr(index), 0)
	if ret == LB_ERR {
		return errors.New("LB_ERR")
	}
	return nil
}

func (this *ListBox) GetString(index uint) string{
	len := SendMessage(this.hWnd, LB_GETTEXTLEN, uintptr(index), 0)
	if len == LB_ERR{
		return ""
	}

	buf := make([]byte, len + 1)
	ret := SendMessage(this.hWnd, LB_GETTEXT, uintptr(index), uintptr(unsafe.Pointer(&buf[0])))

	if len == LB_ERR{
		return ""
	}
	return string(buf)
}

func (this *ListBox) ResetContent(){
	SendMessage(this.hWnd, LB_RESETCONTENT, 0, 0)
}

//The return value is the number of items in the list box,
//or LB_ERR (-1) if an error occurs.
func (this *ListBox) GetCount() int{
	return SendMessage(this.hWnd, LB_GETCOUNT, 0, 0)
}

func (this *ListBox) calculateMaxItemTextWidth() int {
	hdc := GetDC(this.hWnd)
	if hdc == 0 {
		newError("GetDC failed")
		return -1
	}
	defer ReleaseDC(this.hWnd, hdc)

	hFontOld := SelectObject(hdc, HGDIOBJ(this.Font().handleForDPI(0)))
	defer SelectObject(hdc, hFontOld)

	var maxWidth int

	for _, item := range this.Items {
		var s SIZE
		str := syscall.StringToUTF16(item)

		if !GetTextExtentPoint32(hdc, &str[0], int32(len(str)-1), &s) {
			newError("GetTextExtentPoint32 failed")
			return -1
		}

		maxWidth = maxi(maxWidth, int(s.CX))
	}

	return maxWidth
}

func (this *ListBox) SizeHint() Size {

	defaultSize := this.dialogBaseUnitsToPixels(Size{50, 12})

	if this.Items != nil && this.maxItemTextWidth <= 0 {
		this.maxItemTextWidth = this.calculateMaxItemTextWidth()
	}

	// FIXME: Use GetThemePartSize instead of guessing
	w := maxi(defaultSize.Width, this.maxItemTextWidth+24)
	h := defaultSize.Height + 1

	return Size{w, h}

}

func (this *ListBox) SelectedIndex() int{
	return int(SendMessage (this.hWnd, LB_GETCURSEL, 0, 0))
}

func (this *ListBox) SelectedItem() string{
	index := this.SelectedIndex()
	length := int(SendMessage(this.hWnd, LB_GETTEXTLEN, uintptr(index), 0)) + 1
	buffer := make([]uint16, length +1)
	SendMessage(this.hWnd, LB_GETTEXT, uintptr(index), uintptr(unsafe.Pointer(&buffer[0])))
	return syscall.UTF16ToString(buffer)
}

func (this *ListBox) SelectedIndexChanged() *Event{
	return this.selectedIndexChangedPublisher.Event()
}

func (this *ListBox) DBClicked() *Event{
	return this.dbClickedPublisher.Event()
}

func (this *ListBox) wndProc(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr {
	switch msg {
	case WM_COMMAND:
		switch HIWORD(uint32(wParam)) {
		case LBN_SELCHANGE:
			this.selectedIndexChangedPublisher.Publish()
		case LBN_DBLCLK:
			this.dbClickedPublisher.Publish()
		}
	}

	return this.WidgetBase.wndProc(hwnd, msg, wParam, lParam)
}<commit_msg>fix compiling error<commit_after>package walk
import (
	"unsafe"
	"syscall"
	"errors"
)
import . "github.com/lxn/go-winapi"

const (
	LB_ERR       = -1
	LB_ERRSPACE  = -2
)

type ListBox struct{
	WidgetBase
	maxItemTextWidth              int
	selectedIndexChangedPublisher EventPublisher
	dbClickedPublisher            EventPublisher
}

func NewListBox(parent Container)(*ListBox, error){
	//TODO: move to go-winapi/listbox
	//LBS_STANDARD := LBS_NOTIFY | LBS_SORT | WS_VSCROLL | WS_BORDER

	lb := &ListBox{}
	err := initChildWidget(
		lb,
		parent,
		"LISTBOX",
		WS_TABSTOP | WS_VISIBLE | LBS_NOTIFY | LBS_SORT | WS_VSCROLL | WS_BORDER,
		0)
	if err != nil{
		return nil, err
	}
	return lb, nil
}

func (*ListBox) origWndProcPtr() uintptr {
	return checkBoxOrigWndProcPtr
}

func (*ListBox) setOrigWndProcPtr(ptr uintptr) {
	checkBoxOrigWndProcPtr = ptr
}

func (*ListBox) LayoutFlags() LayoutFlags {
	return GrowableHorz | GrowableVert
}

func (this *ListBox) AddString(item string){
	SendMessage (this.hWnd, LB_ADDSTRING, 0,
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(item))))
}

//If this parameter is -1, the string is added to the end of the list.
func (this *ListBox) InsertString(index int, item string) error{
	if index < -1{
		return errors.New("Invalid index")
	}

	ret := int(SendMessage(this.hWnd, LB_INSERTSTRING, uintptr(index), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(item)))))
	if ret == LB_ERRSPACE || ret == LB_ERR {
		return errors.New("LB_ERR or LB_ERRSPACE")
	}
	return nil
}

func (this *ListBox) DeleteString(index uint) error{
	ret := int(SendMessage(this.hWnd, LB_DELETESTRING, uintptr(index), 0))
	if ret == LB_ERR {
		return errors.New("LB_ERR")
	}
	return nil
}

func (this *ListBox) GetString(index uint) string{
	len := int(SendMessage(this.hWnd, LB_GETTEXTLEN, uintptr(index), 0))
	if len == LB_ERR{
		return ""
	}

	buf := make([]byte, len + 1)
	_ = SendMessage(this.hWnd, LB_GETTEXT, uintptr(index), uintptr(unsafe.Pointer(&buf[0])))

	if len == LB_ERR{
		return ""
	}
	return syscall.UTF16ToString(buf)
}

func (this *ListBox) ResetContent(){
	SendMessage(this.hWnd, LB_RESETCONTENT, 0, 0)
}

//The return value is the number of items in the list box,
//or LB_ERR (-1) if an error occurs.
func (this *ListBox) GetCount() (uint, error){
    retPtr := SendMessage(this.hWnd, LB_GETCOUNT, 0, 0)
	ret := int(retPtr)
	if ret == LB_ERR{
		return 0, errors.New("LB_ERR")
	}
	return uint(ret), nil
}

func (this *ListBox) calculateMaxItemTextWidth() int {
	hdc := GetDC(this.hWnd)
	if hdc == 0 {
		newError("GetDC failed")
		return -1
	}
	defer ReleaseDC(this.hWnd, hdc)

	hFontOld := SelectObject(hdc, HGDIOBJ(this.Font().handleForDPI(0)))
	defer SelectObject(hdc, hFontOld)

	var maxWidth int

	count, _ := this.GetCount()
	var i uint
	for i = 0; i < count ; i++{
		item  := this.GetString(i)
		var s SIZE
		str := syscall.StringToUTF16(item)

		if !GetTextExtentPoint32(hdc, &str[0], int32(len(str)-1), &s) {
			newError("GetTextExtentPoint32 failed")
			return -1
		}

		maxWidth = maxi(maxWidth, int(s.CX))
	}

	return maxWidth
}

func (this *ListBox) SizeHint() Size {

	defaultSize := this.dialogBaseUnitsToPixels(Size{50, 12})

	if this.maxItemTextWidth <= 0 {
		this.maxItemTextWidth = this.calculateMaxItemTextWidth()
	}

	// FIXME: Use GetThemePartSize instead of guessing
	w := maxi(defaultSize.Width, this.maxItemTextWidth+24)
	h := defaultSize.Height + 1

	return Size{w, h}

}

func (this *ListBox) SelectedIndex() int{
	return int(SendMessage (this.hWnd, LB_GETCURSEL, 0, 0))
}

func (this *ListBox) SelectedItem() string{
	index := this.SelectedIndex()
	length := int(SendMessage(this.hWnd, LB_GETTEXTLEN, uintptr(index), 0)) + 1
	buffer := make([]uint16, length +1)
	SendMessage(this.hWnd, LB_GETTEXT, uintptr(index), uintptr(unsafe.Pointer(&buffer[0])))
	return syscall.UTF16ToString(buffer)
}

func (this *ListBox) SelectedIndexChanged() *Event{
	return this.selectedIndexChangedPublisher.Event()
}

func (this *ListBox) DBClicked() *Event{
	return this.dbClickedPublisher.Event()
}

func (this *ListBox) wndProc(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr {
	switch msg {
	case WM_COMMAND:
		switch HIWORD(uint32(wParam)) {
		case LBN_SELCHANGE:
			this.selectedIndexChangedPublisher.Publish()
		case LBN_DBLCLK:
			this.dbClickedPublisher.Publish()
		}
	}

	return this.WidgetBase.wndProc(hwnd, msg, wParam, lParam)
}<|endoftext|>


<commit_before>package main

import (
	"strings"
)

// HandleCommand handles input from the user
func HandleCommand(input string, view *View) {
	cmd := strings.Split(input, " ")[0]
	args := strings.Split(input, " ")[1:]
	if cmd == "set" {
		SetOption(view, args)
	}
}
<commit_msg>Add more commands<commit_after>package main

import (
	"os"
	"strings"
)

// HandleCommand handles input from the user
func HandleCommand(input string, view *View) {
	cmd := strings.Split(input, " ")[0]
	args := strings.Split(input, " ")[1:]
	switch cmd {
	case "set":
		SetOption(view, args)
	case "quit":
		if view.CanClose("Quit anyway? ") {
			screen.Fini()
			os.Exit(0)
		}
	case "save":
		view.Save()
	}
}
<|endoftext|>


<commit_before>package mvn

import (
	"errors"
	"fmt"
	gofrogcmd "github.com/jfrog/gofrog/io"
	"github.com/jfrog/jfrog-cli-go/artifactory/utils"
	"github.com/jfrog/jfrog-cli-go/utils/config"
	"github.com/jfrog/jfrog-client-go/utils/errorutils"
	"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
	"github.com/jfrog/jfrog-client-go/utils/log"
	"github.com/spf13/viper"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
)

const mavenExtractorDependencyVersion = "2.13.13"
const ClasswordConfFileName = "classworlds.conf"
const MavenHome = "M2_HOME"

type MvnCommand struct {
	goals         string
	configPath    string
	configuration *utils.BuildConfiguration
	rtDetails     *config.ArtifactoryDetails
}

func NewMvnCommand() *MvnCommand {
	return &MvnCommand{}
}

func (mc *MvnCommand) SetRtDetails(rtDetails *config.ArtifactoryDetails) *MvnCommand {
	mc.rtDetails = rtDetails
	return mc
}

func (mc *MvnCommand) SetConfiguration(configuration *utils.BuildConfiguration) *MvnCommand {
	mc.configuration = configuration
	return mc
}

func (mc *MvnCommand) SetConfigPath(configPath string) *MvnCommand {
	mc.configPath = configPath
	return mc
}

func (mc *MvnCommand) SetGoals(goals string) *MvnCommand {
	mc.goals = goals
	return mc
}

func (mc *MvnCommand) Run() error {
	log.Info("Running Mvn...")
	err := validateMavenInstallation()
	if err != nil {
		return err
	}

	var dependenciesPath string
	dependenciesPath, err = downloadDependencies()
	if err != nil {
		return err
	}

	mvnRunConfig, err := mc.createMvnRunConfig(dependenciesPath)
	if err != nil {
		return err
	}

	defer os.Remove(mvnRunConfig.buildInfoProperties)
	if err := gofrogcmd.RunCmd(mvnRunConfig); err != nil {
		return err
	}

	return nil
}

// Returns the ArtfiactoryDetails. The information returns from the config file provided.
func (mc *MvnCommand) RtDetails() (*config.ArtifactoryDetails, error) {
	// Get the rtDetails from the config file.
	var err error
	if mc.rtDetails == nil {
		vConfig, err := utils.ReadConfigFile(mc.configPath, utils.YAML)
		if err != nil {
			return nil, err
		}
		mc.rtDetails, err = utils.GetRtDetails(vConfig)
	}
	return mc.rtDetails, err
}

func (mc *MvnCommand) CommandName() string {
	return "rt_maven"
}

func validateMavenInstallation() error {
	log.Debug("Checking prerequisites.")
	mavenHome := os.Getenv(MavenHome)
	if mavenHome == "" {
		return errorutils.CheckError(errors.New(MavenHome + " environment variable is not set"))
	}
	return nil
}

func downloadDependencies() (string, error) {
	dependenciesPath, err := config.GetJfrogDependenciesPath()
	if err != nil {
		return "", err
	}
	dependenciesPath = filepath.Join(dependenciesPath, "maven", mavenExtractorDependencyVersion)

	filename := fmt.Sprintf("build-info-extractor-maven3-%s-uber.jar", mavenExtractorDependencyVersion)
	filePath := fmt.Sprintf("org/jfrog/buildinfo/build-info-extractor-maven3/%s", mavenExtractorDependencyVersion)
	downloadPath := path.Join(filePath, filename)

	err = utils.DownloadExtractorIfNeeded(downloadPath, filepath.Join(dependenciesPath, filename))
	if err != nil {
		return "", err
	}

	err = createClassworldsConfig(dependenciesPath)
	return dependenciesPath, err
}

func createClassworldsConfig(dependenciesPath string) error {
	classworldsPath := filepath.Join(dependenciesPath, ClasswordConfFileName)

	if fileutils.IsPathExists(classworldsPath, false) {
		return nil
	}
	return errorutils.CheckError(ioutil.WriteFile(classworldsPath, []byte(utils.ClassworldsConf), 0644))
}

func (mc *MvnCommand) createMvnRunConfig(dependenciesPath string) (*mvnRunConfig, error) {
	var err error
	var javaExecPath string

	javaHome := os.Getenv("JAVA_HOME")
	if javaHome != "" {
		javaExecPath = filepath.Join(javaHome, "bin", "java")
	} else {
		javaExecPath, err = exec.LookPath("java")
		if err != nil {
			return nil, errorutils.CheckError(err)
		}
	}

	mavenHome := os.Getenv("M2_HOME")
	plexusClassworlds, err := filepath.Glob(filepath.Join(mavenHome, "boot", "plexus-classworlds*"))
	if err != nil {
		return nil, errorutils.CheckError(err)
	}

	mavenOpts := os.Getenv("MAVEN_OPTS")

	if len(plexusClassworlds) != 1 {
		return nil, errorutils.CheckError(errors.New("couldn't find plexus-classworlds-x.x.x.jar in Maven installation path, please check M2_HOME environment variable"))
	}

	var currentWorkdir string
	currentWorkdir, err = os.Getwd()
	if err != nil {
		return nil, errorutils.CheckError(err)
	}

	var vConfig *viper.Viper
	vConfig, err = utils.ReadConfigFile(mc.configPath, utils.YAML)
	if err != nil {
		return nil, err
	}

	if len(mc.configuration.BuildName) > 0 && len(mc.configuration.BuildNumber) > 0 {
		vConfig.Set(utils.BUILD_NAME, mc.configuration.BuildName)
		vConfig.Set(utils.BUILD_NUMBER, mc.configuration.BuildNumber)
		err = utils.SaveBuildGeneralDetails(mc.configuration.BuildName, mc.configuration.BuildNumber)
		if err != nil {
			return nil, err
		}
	}

	buildInfoProperties, err := utils.CreateBuildInfoPropertiesFile(mc.configuration.BuildName, mc.configuration.BuildNumber, vConfig, utils.MAVEN)
	if err != nil {
		return nil, err
	}

	return &mvnRunConfig{
		java:                   javaExecPath,
		pluginDependencies:     dependenciesPath,
		plexusClassworlds:      plexusClassworlds[0],
		cleassworldsConfig:     filepath.Join(dependenciesPath, ClasswordConfFileName),
		mavenHome:              mavenHome,
		workspace:              currentWorkdir,
		goals:                  mc.goals,
		buildInfoProperties:    buildInfoProperties,
		generatedBuildInfoPath: vConfig.GetString(utils.GENERATED_BUILD_INFO),
		mavenOpts:              mavenOpts,
	}, nil
}

func (config *mvnRunConfig) GetCmd() *exec.Cmd {
	var cmd []string
	cmd = append(cmd, config.java)
	cmd = append(cmd, "-classpath", config.plexusClassworlds)
	cmd = append(cmd, "-Dmaven.home="+config.mavenHome)
	cmd = append(cmd, "-DbuildInfoConfig.propertiesFile="+config.buildInfoProperties)
	cmd = append(cmd, "-Dm3plugin.lib="+config.pluginDependencies)
	cmd = append(cmd, "-Dclassworlds.conf="+config.cleassworldsConfig)
	cmd = append(cmd, "-Dmaven.multiModuleProjectDirectory="+config.workspace)
	if config.mavenOpts != "" {
		cmd = append(cmd, strings.Split(config.mavenOpts, " ")...)
	}
	cmd = append(cmd, "org.codehaus.plexus.classworlds.launcher.Launcher")
	cmd = append(cmd, strings.Split(config.goals, " ")...)
	return exec.Command(cmd[0], cmd[1:]...)
}

func (config *mvnRunConfig) GetEnv() map[string]string {
	return map[string]string{}
}

func (config *mvnRunConfig) GetStdWriter() io.WriteCloser {
	return nil
}

func (config *mvnRunConfig) GetErrWriter() io.WriteCloser {
	return nil
}

type mvnRunConfig struct {
	java                   string
	plexusClassworlds      string
	cleassworldsConfig     string
	mavenHome              string
	pluginDependencies     string
	workspace              string
	pom                    string
	goals                  string
	buildInfoProperties    string
	generatedBuildInfoPath string
	mavenOpts              string
}
<commit_msg>Fix for - CLI claims it couldn't find plexus-classworlds-x.x.x.jar (#514)<commit_after>package mvn

import (
	"errors"
	"fmt"
	gofrogcmd "github.com/jfrog/gofrog/io"
	"github.com/jfrog/jfrog-cli-go/artifactory/utils"
	"github.com/jfrog/jfrog-cli-go/utils/config"
	"github.com/jfrog/jfrog-client-go/utils/errorutils"
	"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
	"github.com/jfrog/jfrog-client-go/utils/log"
	"github.com/spf13/viper"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
)

const mavenExtractorDependencyVersion = "2.13.13"
const classworldsConfFileName = "classworlds.conf"
const MavenHome = "M2_HOME"

type MvnCommand struct {
	goals         string
	configPath    string
	configuration *utils.BuildConfiguration
	rtDetails     *config.ArtifactoryDetails
}

func NewMvnCommand() *MvnCommand {
	return &MvnCommand{}
}

func (mc *MvnCommand) SetRtDetails(rtDetails *config.ArtifactoryDetails) *MvnCommand {
	mc.rtDetails = rtDetails
	return mc
}

func (mc *MvnCommand) SetConfiguration(configuration *utils.BuildConfiguration) *MvnCommand {
	mc.configuration = configuration
	return mc
}

func (mc *MvnCommand) SetConfigPath(configPath string) *MvnCommand {
	mc.configPath = configPath
	return mc
}

func (mc *MvnCommand) SetGoals(goals string) *MvnCommand {
	mc.goals = goals
	return mc
}

func (mc *MvnCommand) Run() error {
	log.Info("Running Mvn...")
	err := validateMavenInstallation()
	if err != nil {
		return err
	}

	var dependenciesPath string
	dependenciesPath, err = downloadDependencies()
	if err != nil {
		return err
	}

	mvnRunConfig, err := mc.createMvnRunConfig(dependenciesPath)
	if err != nil {
		return err
	}

	defer os.Remove(mvnRunConfig.buildInfoProperties)
	if err := gofrogcmd.RunCmd(mvnRunConfig); err != nil {
		return err
	}

	return nil
}

// Returns the ArtfiactoryDetails. The information returns from the config file provided.
func (mc *MvnCommand) RtDetails() (*config.ArtifactoryDetails, error) {
	// Get the rtDetails from the config file.
	var err error
	if mc.rtDetails == nil {
		vConfig, err := utils.ReadConfigFile(mc.configPath, utils.YAML)
		if err != nil {
			return nil, err
		}
		mc.rtDetails, err = utils.GetRtDetails(vConfig)
	}
	return mc.rtDetails, err
}

func (mc *MvnCommand) CommandName() string {
	return "rt_maven"
}

func validateMavenInstallation() error {
	log.Debug("Checking prerequisites.")
	mavenHome := os.Getenv(MavenHome)
	if mavenHome == "" {
		return errorutils.CheckError(errors.New(MavenHome + " environment variable is not set"))
	}
	return nil
}

func downloadDependencies() (string, error) {
	dependenciesPath, err := config.GetJfrogDependenciesPath()
	if err != nil {
		return "", err
	}
	dependenciesPath = filepath.Join(dependenciesPath, "maven", mavenExtractorDependencyVersion)

	filename := fmt.Sprintf("build-info-extractor-maven3-%s-uber.jar", mavenExtractorDependencyVersion)
	filePath := fmt.Sprintf("org/jfrog/buildinfo/build-info-extractor-maven3/%s", mavenExtractorDependencyVersion)
	downloadPath := path.Join(filePath, filename)

	err = utils.DownloadExtractorIfNeeded(downloadPath, filepath.Join(dependenciesPath, filename))
	if err != nil {
		return "", err
	}

	err = createClassworldsConfig(dependenciesPath)
	return dependenciesPath, err
}

func createClassworldsConfig(dependenciesPath string) error {
	classworldsPath := filepath.Join(dependenciesPath, classworldsConfFileName)

	if fileutils.IsPathExists(classworldsPath, false) {
		return nil
	}
	return errorutils.CheckError(ioutil.WriteFile(classworldsPath, []byte(utils.ClassworldsConf), 0644))
}

func (mc *MvnCommand) createMvnRunConfig(dependenciesPath string) (*mvnRunConfig, error) {
	var err error
	var javaExecPath string

	javaHome := os.Getenv("JAVA_HOME")
	if javaHome != "" {
		javaExecPath = filepath.Join(javaHome, "bin", "java")
	} else {
		javaExecPath, err = exec.LookPath("java")
		if err != nil {
			return nil, errorutils.CheckError(err)
		}
	}

	mavenHome := os.Getenv("M2_HOME")
	plexusClassworlds, err := filepath.Glob(filepath.Join(mavenHome, "boot", "plexus-classworlds*.jar"))
	if err != nil {
		return nil, errorutils.CheckError(err)
	}

	mavenOpts := os.Getenv("MAVEN_OPTS")

	if len(plexusClassworlds) != 1 {
		return nil, errorutils.CheckError(errors.New("couldn't find plexus-classworlds-x.x.x.jar in Maven installation path, please check M2_HOME environment variable"))
	}

	var currentWorkdir string
	currentWorkdir, err = os.Getwd()
	if err != nil {
		return nil, errorutils.CheckError(err)
	}

	var vConfig *viper.Viper
	vConfig, err = utils.ReadConfigFile(mc.configPath, utils.YAML)
	if err != nil {
		return nil, err
	}

	if len(mc.configuration.BuildName) > 0 && len(mc.configuration.BuildNumber) > 0 {
		vConfig.Set(utils.BUILD_NAME, mc.configuration.BuildName)
		vConfig.Set(utils.BUILD_NUMBER, mc.configuration.BuildNumber)
		err = utils.SaveBuildGeneralDetails(mc.configuration.BuildName, mc.configuration.BuildNumber)
		if err != nil {
			return nil, err
		}
	}

	buildInfoProperties, err := utils.CreateBuildInfoPropertiesFile(mc.configuration.BuildName, mc.configuration.BuildNumber, vConfig, utils.MAVEN)
	if err != nil {
		return nil, err
	}

	return &mvnRunConfig{
		java:                   javaExecPath,
		pluginDependencies:     dependenciesPath,
		plexusClassworlds:      plexusClassworlds[0],
		cleassworldsConfig:     filepath.Join(dependenciesPath, classworldsConfFileName),
		mavenHome:              mavenHome,
		workspace:              currentWorkdir,
		goals:                  mc.goals,
		buildInfoProperties:    buildInfoProperties,
		generatedBuildInfoPath: vConfig.GetString(utils.GENERATED_BUILD_INFO),
		mavenOpts:              mavenOpts,
	}, nil
}

func (config *mvnRunConfig) GetCmd() *exec.Cmd {
	var cmd []string
	cmd = append(cmd, config.java)
	cmd = append(cmd, "-classpath", config.plexusClassworlds)
	cmd = append(cmd, "-Dmaven.home="+config.mavenHome)
	cmd = append(cmd, "-DbuildInfoConfig.propertiesFile="+config.buildInfoProperties)
	cmd = append(cmd, "-Dm3plugin.lib="+config.pluginDependencies)
	cmd = append(cmd, "-Dclassworlds.conf="+config.cleassworldsConfig)
	cmd = append(cmd, "-Dmaven.multiModuleProjectDirectory="+config.workspace)
	if config.mavenOpts != "" {
		cmd = append(cmd, strings.Split(config.mavenOpts, " ")...)
	}
	cmd = append(cmd, "org.codehaus.plexus.classworlds.launcher.Launcher")
	cmd = append(cmd, strings.Split(config.goals, " ")...)
	return exec.Command(cmd[0], cmd[1:]...)
}

func (config *mvnRunConfig) GetEnv() map[string]string {
	return map[string]string{}
}

func (config *mvnRunConfig) GetStdWriter() io.WriteCloser {
	return nil
}

func (config *mvnRunConfig) GetErrWriter() io.WriteCloser {
	return nil
}

type mvnRunConfig struct {
	java                   string
	plexusClassworlds      string
	cleassworldsConfig     string
	mavenHome              string
	pluginDependencies     string
	workspace              string
	pom                    string
	goals                  string
	buildInfoProperties    string
	generatedBuildInfoPath string
	mavenOpts              string
}
<|endoftext|>


<commit_before>package libvirt

import (
	"encoding/xml"
	"fmt"
	"path/filepath"
	"regexp"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	libvirt "github.com/libvirt/libvirt-go"
)

func testAccCheckLibvirtVolumeExists(name string, volume *libvirt.StorageVol) resource.TestCheckFunc {
	return func(state *terraform.State) error {
		virConn := testAccProvider.Meta().(*Client).libvirt

		rs, err := getResourceFromTerraformState(name, state)
		if err != nil {
			return err
		}

		retrievedVol, err := getVolumeFromTerraformState(name, state, *virConn)
		if err != nil {
			return err
		}

		realID, err := retrievedVol.GetKey()
		if err != nil {
			return err
		}

		if realID != rs.Primary.ID {
			return fmt.Errorf("Resource ID and volume key does not match")
		}

		*volume = *retrievedVol

		return nil
	}
}

func testAccCheckLibvirtVolumeDoesNotExists(n string, volume *libvirt.StorageVol) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		virConn := testAccProvider.Meta().(*Client).libvirt

		key, err := volume.GetKey()
		if err != nil {
			return fmt.Errorf("Can't retrieve volume key: %s", err)
		}

		vol, err := virConn.LookupStorageVolByKey(key)
		if err == nil {
			vol.Free()
			return fmt.Errorf("Volume '%s' still exists", key)
		}

		return nil
	}
}

func testAccCheckLibvirtVolumeIsBackingStore(name string, volume *libvirt.StorageVol) resource.TestCheckFunc {
	return func(state *terraform.State) error {
		virConn := testAccProvider.Meta().(*Client).libvirt

		vol, err := getVolumeFromTerraformState(name, state, *virConn)
		if err != nil {
			return err
		}

		volXMLDesc, err := vol.GetXMLDesc(0)
		if err != nil {
			return fmt.Errorf("Error retrieving libvirt volume XML description: %s", err)
		}

		volumeDef := newDefVolume()
		err = xml.Unmarshal([]byte(volXMLDesc), &volumeDef)
		if err != nil {
			return fmt.Errorf("Error reading libvirt volume XML description: %s", err)
		}
		if volumeDef.BackingStore == nil {
			return fmt.Errorf("FAIL: the volume was supposed to be a backingstore, but it is not")
		}
		value := volumeDef.BackingStore.Path
		if value == "" {
			return fmt.Errorf("FAIL: the volume was supposed to be a backingstore, but it is not")
		}

		return nil
	}
}

func TestAccLibvirtVolume_Basic(t *testing.T) {
	var volume libvirt.StorageVol
	randomVolumeResource := acctest.RandString(10)
	randomVolumeName := acctest.RandString(10)
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLibvirtVolumeDestroy,
		Steps: []resource.TestStep{
			{
				Config: fmt.Sprintf(`
				resource "libvirt_volume" "%s" {
					name = "%s"
					size =  1073741824
				}`, randomVolumeResource, randomVolumeName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResource, &volume),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResource, "name", randomVolumeName),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResource, "size", "1073741824"),
				),
			},
		},
	})
}

func TestAccLibvirtVolume_BackingStoreTestByID(t *testing.T) {
	var volume libvirt.StorageVol
	var volume2 libvirt.StorageVol
	randomVolumeResource := acctest.RandString(10)
	randomVolumeName := acctest.RandString(10)
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLibvirtVolumeDestroy,
		Steps: []resource.TestStep{
			{
				Config: fmt.Sprintf(`
				resource "libvirt_volume" "%s" {
					name = "%s"
					size =  1073741824
				}
				resource "libvirt_volume" "backing-store" {
					name = "backing-store"
					base_volume_id = "${libvirt_volume.%s.id}"
			        }
				`, randomVolumeResource, randomVolumeName, randomVolumeResource),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResource, &volume),
					testAccCheckLibvirtVolumeIsBackingStore("libvirt_volume.backing-store", &volume2),
				),
			},
		},
	})
}

func TestAccLibvirtVolume_BackingStoreTestByName(t *testing.T) {
	var volume libvirt.StorageVol
	var volume2 libvirt.StorageVol
	randomVolumeResource := acctest.RandString(10)
	randomVolumeName := acctest.RandString(10)
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLibvirtVolumeDestroy,
		Steps: []resource.TestStep{
			{
				Config: fmt.Sprintf(`
					resource "libvirt_volume" "%s" {
						name = "%s"
						size =  1073741824
					}
					resource "libvirt_volume" "backing-store" {
						name = "backing-store"
						base_volume_name = "${libvirt_volume.%s.name}"
				  }	`, randomVolumeResource, randomVolumeName, randomVolumeResource),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResource, &volume),
					testAccCheckLibvirtVolumeIsBackingStore("libvirt_volume.backing-store", &volume2),
				),
			},
		},
	})
}

// The destroy function should always handle the case where the resource might already be destroyed
// (manually, for example). If the resource is already destroyed, this should not return an error.
// This allows Terraform users to manually delete resources without breaking Terraform.
// This test should fail without a proper "Exists" implementation
func TestAccLibvirtVolume_ManuallyDestroyed(t *testing.T) {
	var volume libvirt.StorageVol
	randomVolumeResource := acctest.RandString(10)
	randomVolumeName := acctest.RandString(10)
	testAccCheckLibvirtVolumeConfigBasic := fmt.Sprintf(`
	resource "libvirt_volume" "%s" {
		name = "%s"
		size =  1073741824
	}`, randomVolumeResource, randomVolumeName)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLibvirtVolumeDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccCheckLibvirtVolumeConfigBasic,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResource, &volume),
				),
			},
			{
				Config:  testAccCheckLibvirtVolumeConfigBasic,
				Destroy: true,
				PreConfig: func() {
					client := testAccProvider.Meta().(*Client)
					id, err := volume.GetKey()
					if err != nil {
						panic(err)
					}
					volumeDelete(client, id)
				},
			},
		},
	})
}

func TestAccLibvirtVolume_UniqueName(t *testing.T) {
	randomVolumeName := acctest.RandString(10)
	randomVolumeResource2 := acctest.RandString(10)
	randomVolumeResource := acctest.RandString(10)
	config := fmt.Sprintf(`
	resource "libvirt_volume" "%s" {
		name = "%s"
		size =  1073741824
	}

	resource "libvirt_volume" "%s" {
		name = "%s"
		size =  1073741824
	}
	`, randomVolumeResource, randomVolumeName, randomVolumeResource2, randomVolumeName)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLibvirtVolumeDestroy,
		Steps: []resource.TestStep{
			{
				Config:      config,
				ExpectError: regexp.MustCompile(`storage volume '` + randomVolumeName + `' (exists already|already exists)`),
			},
		},
	})
}

func TestAccLibvirtVolume_DownloadFromSource(t *testing.T) {
	var volume libvirt.StorageVol
	randomVolumeResource := acctest.RandString(10)
	randomVolumeName := acctest.RandString(10)

	fws := fileWebServer{}
	if err := fws.Start(); err != nil {
		t.Fatal(err)
	}
	defer fws.Stop()

	content := []byte("a fake image")
	url, _, err := fws.AddContent(content)
	if err != nil {
		t.Fatal(err)
	}

	config := fmt.Sprintf(`
	resource "libvirt_volume" "%s" {
		name   = "%s"
		source = "%s"
	}`, randomVolumeResource, randomVolumeName, url)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLibvirtVolumeDestroy,
		Steps: []resource.TestStep{
			{
				Config: config,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResource, &volume),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResource, "name", randomVolumeName),
				),
			},
		},
	})
}

func TestAccLibvirtVolume_DownloadFromSourceFormat(t *testing.T) {
	var volumeRaw libvirt.StorageVol
	var volumeQCOW2 libvirt.StorageVol
	randomVolumeNameRaw := acctest.RandString(10)
	randomVolumeNameQCOW := acctest.RandString(10)
	randomVolumeResourceRaw := acctest.RandString(10)
	randomVolumeResourceQCOW := acctest.RandString(10)
	qcow2Path, err := filepath.Abs("testdata/test.qcow2")
	if err != nil {
		t.Fatal(err)
	}

	rawPath, err := filepath.Abs("testdata/initrd.img")
	if err != nil {
		t.Fatal(err)
	}

	config := fmt.Sprintf(`
	resource "libvirt_volume" "%s" {
		name   = "%s"
		source = "%s"
	}
  resource "libvirt_volume" "%s" {
		name   = "%s"
		source = "%s"
	}`, randomVolumeResourceRaw, randomVolumeNameRaw, fmt.Sprintf("file://%s", rawPath), randomVolumeResourceQCOW, randomVolumeNameQCOW, fmt.Sprintf("file://%s", qcow2Path))
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLibvirtVolumeDestroy,
		Steps: []resource.TestStep{
			{
				Config: config,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResourceRaw, &volumeRaw),
					testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResourceQCOW, &volumeQCOW2),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResourceRaw, "name", randomVolumeNameRaw),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResourceRaw, "format", "raw"),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResourceQCOW, "name", randomVolumeNameQCOW),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResourceQCOW, "format", "qcow2"),
				),
			},
		},
	})
}

func TestAccLibvirtVolume_Format(t *testing.T) {
	var volume libvirt.StorageVol
	randomVolumeResource := acctest.RandString(10)
	randomVolumeName := acctest.RandString(10)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLibvirtVolumeDestroy,
		Steps: []resource.TestStep{
			{
				Config: fmt.Sprintf(`
				resource "libvirt_volume" "%s" {
					name   = "%s"
					format = "raw"
					size   =  1073741824
				}`, randomVolumeResource, randomVolumeName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResource, &volume),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResource, "name", randomVolumeName),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResource, "size", "1073741824"),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResource, "format", "raw"),
				),
			},
		},
	})
}

func TestAccLibvirtVolume_Import(t *testing.T) {
	var volume libvirt.StorageVol
	randomVolumeResource := acctest.RandString(10)
	randomVolumeName := acctest.RandString(10)
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLibvirtVolumeDestroy,
		Steps: []resource.TestStep{
			{
				Config: fmt.Sprintf(`
					resource "libvirt_volume" "%s" {
							name   = "%s"
							format = "raw"
							size   =  1073741824
					}`, randomVolumeResource, randomVolumeName),
			},
			{
				ResourceName: "libvirt_volume." + randomVolumeResource,
				ImportState:  true,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResource, &volume),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResource, "name", randomVolumeName),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResource, "size", "1073741824"),
				),
			},
		},
	})
}

func testAccCheckLibvirtVolumeDestroy(state *terraform.State) error {
	virConn := testAccProvider.Meta().(*Client).libvirt
	for _, rs := range state.RootModule().Resources {
		if rs.Type != "libvirt_volume" {
			continue
		}
		_, err := virConn.LookupStorageVolByKey(rs.Primary.ID)
		if err == nil {
			return fmt.Errorf(
				"Error waiting for volume (%s) to be destroyed: %s",
				rs.Primary.ID, err)
		}
	}
	return nil
}
<commit_msg>Cleanup backing store test<commit_after>package libvirt

import (
	"encoding/xml"
	"fmt"
	"path/filepath"
	"regexp"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	libvirt "github.com/libvirt/libvirt-go"
)

func testAccCheckLibvirtVolumeExists(name string, volume *libvirt.StorageVol) resource.TestCheckFunc {
	return func(state *terraform.State) error {
		virConn := testAccProvider.Meta().(*Client).libvirt

		rs, err := getResourceFromTerraformState(name, state)
		if err != nil {
			return err
		}

		retrievedVol, err := getVolumeFromTerraformState(name, state, *virConn)
		if err != nil {
			return err
		}

		realID, err := retrievedVol.GetKey()
		if err != nil {
			return err
		}

		if realID != rs.Primary.ID {
			return fmt.Errorf("Resource ID and volume key does not match")
		}

		*volume = *retrievedVol

		return nil
	}
}

func testAccCheckLibvirtVolumeDoesNotExists(n string, volume *libvirt.StorageVol) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		virConn := testAccProvider.Meta().(*Client).libvirt

		key, err := volume.GetKey()
		if err != nil {
			return fmt.Errorf("Can't retrieve volume key: %s", err)
		}

		vol, err := virConn.LookupStorageVolByKey(key)
		if err == nil {
			vol.Free()
			return fmt.Errorf("Volume '%s' still exists", key)
		}

		return nil
	}
}

func testAccCheckLibvirtVolumeIsBackingStore(name string, volume *libvirt.StorageVol) resource.TestCheckFunc {
	return func(state *terraform.State) error {
		virConn := testAccProvider.Meta().(*Client).libvirt

		vol, err := getVolumeFromTerraformState(name, state, *virConn)
		if err != nil {
			return err
		}

		volXMLDesc, err := vol.GetXMLDesc(0)
		if err != nil {
			return fmt.Errorf("Error retrieving libvirt volume XML description: %s", err)
		}

		volumeDef := newDefVolume()
		err = xml.Unmarshal([]byte(volXMLDesc), &volumeDef)
		if err != nil {
			return fmt.Errorf("Error reading libvirt volume XML description: %s", err)
		}
		if volumeDef.BackingStore == nil {
			return fmt.Errorf("FAIL: the volume was supposed to be a backingstore, but it is not")
		}
		value := volumeDef.BackingStore.Path
		if value == "" {
			return fmt.Errorf("FAIL: the volume was supposed to be a backingstore, but it is not")
		}

		return nil
	}
}

func TestAccLibvirtVolume_Basic(t *testing.T) {
	var volume libvirt.StorageVol
	randomVolumeResource := acctest.RandString(10)
	randomVolumeName := acctest.RandString(10)
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLibvirtVolumeDestroy,
		Steps: []resource.TestStep{
			{
				Config: fmt.Sprintf(`
				resource "libvirt_volume" "%s" {
					name = "%s"
					size =  1073741824
				}`, randomVolumeResource, randomVolumeName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResource, &volume),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResource, "name", randomVolumeName),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResource, "size", "1073741824"),
				),
			},
		},
	})
}

func TestAccLibvirtVolume_BackingStoreTestByID(t *testing.T) {
	var volume libvirt.StorageVol
	var volume2 libvirt.StorageVol
	random := acctest.RandString(10)
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLibvirtVolumeDestroy,
		Steps: []resource.TestStep{
			{
				Config: fmt.Sprintf(`
				resource "libvirt_volume" "backing-%s" {
					name = "backing-%s"
					size =  1073741824
				}
				resource "libvirt_volume" "%s" {
					name = "%s"
					base_volume_id = "${libvirt_volume.backing-%s.id}"
			        }
				`, random, random, random, random, random),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLibvirtVolumeExists("libvirt_volume.backing-" + random, &volume),
					testAccCheckLibvirtVolumeIsBackingStore("libvirt_volume." + random, &volume2),
				),
			},
		},
	})
}

func TestAccLibvirtVolume_BackingStoreTestByName(t *testing.T) {
	var volume libvirt.StorageVol
	var volume2 libvirt.StorageVol
	random := acctest.RandString(10)
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLibvirtVolumeDestroy,
		Steps: []resource.TestStep{
			{
				Config: fmt.Sprintf(`
				resource "libvirt_volume" "backing-%s" {
					name = "backing-%s"
					size =  1073741824
				}
				resource "libvirt_volume" "%s" {
					name = "%s"
                    base_volume_name = "${libvirt_volume.backing-%s.name}"
			        }
				`, random, random, random, random, random),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLibvirtVolumeExists("libvirt_volume.backing-"+random, &volume),
					testAccCheckLibvirtVolumeIsBackingStore("libvirt_volume." + random, &volume2),
				),
			},
		},
	})
}

// The destroy function should always handle the case where the resource might already be destroyed
// (manually, for example). If the resource is already destroyed, this should not return an error.
// This allows Terraform users to manually delete resources without breaking Terraform.
// This test should fail without a proper "Exists" implementation
func TestAccLibvirtVolume_ManuallyDestroyed(t *testing.T) {
	var volume libvirt.StorageVol
	randomVolumeResource := acctest.RandString(10)
	randomVolumeName := acctest.RandString(10)
	testAccCheckLibvirtVolumeConfigBasic := fmt.Sprintf(`
	resource "libvirt_volume" "%s" {
		name = "%s"
		size =  1073741824
	}`, randomVolumeResource, randomVolumeName)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLibvirtVolumeDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccCheckLibvirtVolumeConfigBasic,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResource, &volume),
				),
			},
			{
				Config:  testAccCheckLibvirtVolumeConfigBasic,
				Destroy: true,
				PreConfig: func() {
					client := testAccProvider.Meta().(*Client)
					id, err := volume.GetKey()
					if err != nil {
						panic(err)
					}
					volumeDelete(client, id)
				},
			},
		},
	})
}

func TestAccLibvirtVolume_UniqueName(t *testing.T) {
	randomVolumeName := acctest.RandString(10)
	randomVolumeResource2 := acctest.RandString(10)
	randomVolumeResource := acctest.RandString(10)
	config := fmt.Sprintf(`
	resource "libvirt_volume" "%s" {
		name = "%s"
		size =  1073741824
	}

	resource "libvirt_volume" "%s" {
		name = "%s"
		size =  1073741824
	}
	`, randomVolumeResource, randomVolumeName, randomVolumeResource2, randomVolumeName)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLibvirtVolumeDestroy,
		Steps: []resource.TestStep{
			{
				Config:      config,
				ExpectError: regexp.MustCompile(`storage volume '` + randomVolumeName + `' (exists already|already exists)`),
			},
		},
	})
}

func TestAccLibvirtVolume_DownloadFromSource(t *testing.T) {
	var volume libvirt.StorageVol
	randomVolumeResource := acctest.RandString(10)
	randomVolumeName := acctest.RandString(10)

	fws := fileWebServer{}
	if err := fws.Start(); err != nil {
		t.Fatal(err)
	}
	defer fws.Stop()

	content := []byte("a fake image")
	url, _, err := fws.AddContent(content)
	if err != nil {
		t.Fatal(err)
	}

	config := fmt.Sprintf(`
	resource "libvirt_volume" "%s" {
		name   = "%s"
		source = "%s"
	}`, randomVolumeResource, randomVolumeName, url)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLibvirtVolumeDestroy,
		Steps: []resource.TestStep{
			{
				Config: config,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResource, &volume),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResource, "name", randomVolumeName),
				),
			},
		},
	})
}

func TestAccLibvirtVolume_DownloadFromSourceFormat(t *testing.T) {
	var volumeRaw libvirt.StorageVol
	var volumeQCOW2 libvirt.StorageVol
	randomVolumeNameRaw := acctest.RandString(10)
	randomVolumeNameQCOW := acctest.RandString(10)
	randomVolumeResourceRaw := acctest.RandString(10)
	randomVolumeResourceQCOW := acctest.RandString(10)
	qcow2Path, err := filepath.Abs("testdata/test.qcow2")
	if err != nil {
		t.Fatal(err)
	}

	rawPath, err := filepath.Abs("testdata/initrd.img")
	if err != nil {
		t.Fatal(err)
	}

	config := fmt.Sprintf(`
	resource "libvirt_volume" "%s" {
		name   = "%s"
		source = "%s"
	}
  resource "libvirt_volume" "%s" {
		name   = "%s"
		source = "%s"
	}`, randomVolumeResourceRaw, randomVolumeNameRaw, fmt.Sprintf("file://%s", rawPath), randomVolumeResourceQCOW, randomVolumeNameQCOW, fmt.Sprintf("file://%s", qcow2Path))
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLibvirtVolumeDestroy,
		Steps: []resource.TestStep{
			{
				Config: config,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResourceRaw, &volumeRaw),
					testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResourceQCOW, &volumeQCOW2),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResourceRaw, "name", randomVolumeNameRaw),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResourceRaw, "format", "raw"),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResourceQCOW, "name", randomVolumeNameQCOW),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResourceQCOW, "format", "qcow2"),
				),
			},
		},
	})
}

func TestAccLibvirtVolume_Format(t *testing.T) {
	var volume libvirt.StorageVol
	randomVolumeResource := acctest.RandString(10)
	randomVolumeName := acctest.RandString(10)

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLibvirtVolumeDestroy,
		Steps: []resource.TestStep{
			{
				Config: fmt.Sprintf(`
				resource "libvirt_volume" "%s" {
					name   = "%s"
					format = "raw"
					size   =  1073741824
				}`, randomVolumeResource, randomVolumeName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResource, &volume),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResource, "name", randomVolumeName),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResource, "size", "1073741824"),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResource, "format", "raw"),
				),
			},
		},
	})
}

func TestAccLibvirtVolume_Import(t *testing.T) {
	var volume libvirt.StorageVol
	randomVolumeResource := acctest.RandString(10)
	randomVolumeName := acctest.RandString(10)
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckLibvirtVolumeDestroy,
		Steps: []resource.TestStep{
			{
				Config: fmt.Sprintf(`
					resource "libvirt_volume" "%s" {
							name   = "%s"
							format = "raw"
							size   =  1073741824
					}`, randomVolumeResource, randomVolumeName),
			},
			{
				ResourceName: "libvirt_volume." + randomVolumeResource,
				ImportState:  true,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckLibvirtVolumeExists("libvirt_volume."+randomVolumeResource, &volume),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResource, "name", randomVolumeName),
					resource.TestCheckResourceAttr(
						"libvirt_volume."+randomVolumeResource, "size", "1073741824"),
				),
			},
		},
	})
}

func testAccCheckLibvirtVolumeDestroy(state *terraform.State) error {
	virConn := testAccProvider.Meta().(*Client).libvirt
	for _, rs := range state.RootModule().Resources {
		if rs.Type != "libvirt_volume" {
			continue
		}
		_, err := virConn.LookupStorageVolByKey(rs.Primary.ID)
		if err == nil {
			return fmt.Errorf(
				"Error waiting for volume (%s) to be destroyed: %s",
				rs.Primary.ID, err)
		}
	}
	return nil
}
<|endoftext|>