<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	proto "github.com/gogo/protobuf/proto"
	"github.com/mesos/mesos-go"
	"github.com/mesos/mesos-go/encoding"
	"github.com/mesos/mesos-go/httpcli"
	"github.com/mesos/mesos-go/httpcli/stream"
	"github.com/mesos/mesos-go/scheduler"
	"github.com/mesos/mesos-go/scheduler/calls"
)

func main() {
	cfg := config{
		user:       "root",
		name:       "example",
		url:        "http://:5050/api/v1/scheduler",
		codec:      codec{Codec: &encoding.ProtobufCodec},
		timeout:    time.Second,
		checkpoint: true,
		server:     server{address: "127.0.0.1", port: 34567},
	}

	fs := flag.NewFlagSet("example-scheduler", flag.ExitOnError)
	fs.StringVar(&cfg.user, "user", cfg.user, "Framework user to register with the Mesos master")
	fs.StringVar(&cfg.name, "name", cfg.name, "Framework name to register with the Mesos master")
	fs.StringVar(&cfg.role, "role", cfg.role, "Framework role to register with the Mesos master")
	fs.Var(&cfg.codec, "codec", "Codec to encode/decode scheduler API communications [protobuf, json]")
	fs.StringVar(&cfg.url, "url", cfg.url, "Mesos scheduler API URL")
	fs.DurationVar(&cfg.timeout, "timeout", cfg.timeout, "Mesos scheduler API connection timeout")
	fs.BoolVar(&cfg.checkpoint, "checkpoint", cfg.checkpoint, "Enable/disable framework checkpointing")
	fs.StringVar(&cfg.principal, "principal", cfg.principal, "Framework principal with which to authenticate")
	fs.StringVar(&cfg.hostname, "hostname", cfg.hostname, "Framework hostname that is advertised to the master")
	fs.Var(&cfg.labels, "label", "Framework label, may be specified multiple times")
	fs.StringVar(&cfg.server.address, "server.address", cfg.server.address, "IP of artifact server")
	fs.IntVar(&cfg.server.port, "server.port", cfg.server.port, "Port of artifact server")
	fs.StringVar(&cfg.executor, "executor", cfg.executor, "Full path to executor binary")
	fs.Parse(os.Args[1:])

	if err := run(&cfg); err != nil {
		log.Fatal(err)
	}
}

func run(cfg *config) error {
	var state internalState
	state.cli = httpcli.New(
		httpcli.URL(cfg.url),
		httpcli.Codec(cfg.codec.Codec),
		httpcli.Do(httpcli.With(httpcli.Timeout(cfg.timeout))),
	)

	if cfg.executor == "" {
		panic("must specify an executor binary")
	}

	state.executor = prepareExecutorInfo(cfg.executor, cfg.server)
	state.totalTasks = 5 // TODO(jdef) parameterize this

	frameworkInfo := &mesos.FrameworkInfo{
		User:       cfg.user,
		Name:       cfg.name,
		Checkpoint: &cfg.checkpoint,
	}
	if cfg.role != "" {
		frameworkInfo.Role = &cfg.role
	}
	if cfg.principal != "" {
		frameworkInfo.Principal = &cfg.principal
	}
	if cfg.hostname != "" {
		frameworkInfo.Hostname = &cfg.hostname
	}
	if len(cfg.labels) > 0 {
		log.Println("using labels:", cfg.labels)
		frameworkInfo.Labels = &mesos.Labels{Labels: cfg.labels}
	}
	subscribe := calls.Subscribe(true, frameworkInfo)
	registrationTokens := backoffBucket(1*time.Second, 15*time.Second, nil)
	for {
		resp, opt, err := stream.Subscribe(state.cli, subscribe)
		if err == nil {
			func() {
				undo := state.cli.With(opt)
				defer state.cli.With(undo) // strip the stream options
				err = eventLoop(&state, resp.Decoder, resp)
			}()
		}
		if err != nil && err != io.EOF {
			log.Println(err)
		} else {
			log.Println("disconnected")
		}
		if state.frameworkID != "" {
			subscribe.Subscribe.FrameworkInfo.ID = &mesos.FrameworkID{Value: state.frameworkID}
		}
		<-registrationTokens
		log.Println("reconnecting..")
	}
}

// returns the framework ID received by mesos (if any); callers should check for a
// framework ID regardless of whether error != nil.
func eventLoop(state *internalState, eventDecoder encoding.Decoder, conn io.Closer) (err error) {
	defer func() {
		if conn != nil {
			conn.Close()
		}
	}()

	state.frameworkID = ""
	callOptions := scheduler.CallOptions{} // should be applied to every outgoing call

	for err == nil {
		var e scheduler.Event
		if err = eventDecoder.Invoke(&e); err != nil {
			continue
		}

		log.Printf("%+v\n", e)

		switch e.GetType() {
		case scheduler.Event_FAILURE:
			log.Println("received a FAILURE event")
			f := e.GetFailure()
			failure(f.ExecutorID, f.AgentID, f.Status)

		case scheduler.Event_OFFERS:
			log.Println("received an OFFERS event")
			resourceOffers(state, callOptions.Copy(), e.GetOffers().GetOffers())

		case scheduler.Event_UPDATE:
			statusUpdate(state, callOptions.Copy(), e.GetUpdate().GetStatus())

		case scheduler.Event_ERROR:
			// it's recommended that we abort and re-try subscribing; setting
			// err here will cause the event loop to terminate and the connection
			// will be reset.
			err = fmt.Errorf("ERROR: " + e.GetError().GetMessage())

		case scheduler.Event_SUBSCRIBED:
			log.Println("received a SUBSCRIBED event")
			if state.frameworkID == "" {
				state.frameworkID = e.GetSubscribed().GetFrameworkID().GetValue()
				if state.frameworkID == "" {
					// sanity check
					panic("mesos gave us an empty frameworkID")
				}
				callOptions = append(callOptions, calls.Framework(state.frameworkID))
			}
			// else, ignore subsequently received events like this on the same connection
		default:
			// handle unknown event
		}
	} // for
	return err
} // eventLoop

func failure(eid *mesos.ExecutorID, aid *mesos.AgentID, stat *int32) {
	if eid != nil {
		// executor failed..
		msg := "executor '" + eid.Value + "' terminated"
		if aid != nil {
			msg += "on agent '" + aid.Value + "'"
		}
		if stat != nil {
			msg += ", with status=" + strconv.Itoa(int(*stat))
		}
		log.Println(msg)
	} else if aid != nil {
		// agent failed..
		log.Println("agent '" + aid.Value + "' terminated")
	}
}

const (
	CPUS_PER_TASK = 1
	MEM_PER_TASK  = 64
)

var wantsTaskResources = func() (r mesos.Resources) {
	r.Add(
		*mesos.BuildResource().Name("cpus").Scalar(CPUS_PER_TASK).Resource,
		*mesos.BuildResource().Name("mem").Scalar(MEM_PER_TASK).Resource,
	)
	log.Println("wants-task-resources = " + r.String())
	return
}()

func resourceOffers(state *internalState, callOptions scheduler.CallOptions, offers []mesos.Offer) {
	for i := range offers {
		var (
			remaining = mesos.Resources(offers[i].Resources)
			tasks     = []mesos.TaskInfo{}
		)

		log.Println("received offer id '" + offers[i].ID.Value + "' with resources " + remaining.String())

		wantsResources := wantsTaskResources.Clone()
		if len(offers[i].ExecutorIDs) == 0 {
			wantsResources.Add(state.executor.Resources...)
		}

		for state.tasksLaunched < state.totalTasks && remaining.Flatten().ContainsAll(wantsResources) {
			state.tasksLaunched++
			taskID := state.tasksLaunched

			log.Println("launching task " + strconv.Itoa(taskID) + " using offer " + offers[i].ID.Value)

			task := mesos.TaskInfo{TaskID: mesos.TaskID{Value: strconv.Itoa(taskID)}}
			task.Name = "Task " + task.TaskID.Value
			task.AgentID = offers[i].AgentID
			task.Executor = state.executor
			task.Resources = remaining.Find(wantsTaskResources.Flatten(mesos.Role(state.role).Assign()))

			remaining.Subtract(task.Resources...)
			tasks = append(tasks, task)
		}

		// build Accept call to launch all of the tasks we've assembled
		accept := calls.Accept(
			calls.OfferWithOperations(
				offers[i].ID,
				calls.OpLaunch(tasks...),
			),
		).With(callOptions...)

		// send Accept call to mesos
		resp, err := state.cli.Do(accept)
		if err != nil {
			log.Printf("failed to launch tasks: %+v", err)
		} else {
			resp.Close() // no data for these calls
		}
	}
}

func statusUpdate(state *internalState, callOptions scheduler.CallOptions, s mesos.TaskStatus) {
	msg := "Task " + s.TaskID.Value + " is in state " + s.GetState().String()
	if m := s.GetMessage(); m != "" {
		msg += " with message '" + m + "'"
	}
	log.Println(msg)

	if uuid := s.GetUUID(); len(uuid) > 0 {
		ack := calls.Acknowledge(
			s.GetAgentID().GetValue(),
			s.TaskID.Value,
			uuid,
		).With(callOptions...)

		// send Accept call to mesos
		resp, err := state.cli.Do(ack)
		if err != nil {
			log.Println("failed to ack status update for task: %+v", err)
			return
		} else {
			resp.Close() // no data for these calls
		}
	}
	switch st := s.GetState(); st {
	case mesos.TASK_FINISHED:
		state.tasksFinished++
	case mesos.TASK_LOST, mesos.TASK_KILLED, mesos.TASK_FAILED:
		log.Println("Exiting because task " + s.GetTaskID().Value +
			" is in an unexpected state " + st.String() +
			" with reason " + s.GetReason().String() +
			" from source " + s.GetSource().String() +
			" with message '" + s.GetMessage() + "'")
		os.Exit(1)
	}

	if state.tasksFinished == state.totalTasks {
		log.Println("mission accomplished, terminating")
		os.Exit(0)
	}
}

// returns (downloadURI, basename(path))
func serveExecutorArtifact(server server, path string) (string, string) {
	serveFile := func(pattern string, filename string) {
		http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
			http.ServeFile(w, r, filename)
		})
	}

	// Create base path (http://foobar:5000/<base>)
	pathSplit := strings.Split(path, "/")
	var base string
	if len(pathSplit) > 0 {
		base = pathSplit[len(pathSplit)-1]
	} else {
		base = path
	}
	serveFile("/"+base, path)

	hostURI := fmt.Sprintf("http://%s:%d/%s", server.address, server.port, base)
	log.Println("Hosting artifact '" + path + "' at '" + hostURI + "'")

	return hostURI, base
}

var wantsExecutorResources = func() (r mesos.Resources) {
	r.Add(
		*mesos.BuildResource().Name("cpus").Scalar(0.01).Resource,
		*mesos.BuildResource().Name("mem").Scalar(64).Resource,
	)
	log.Println("wants-executor-resources = " + r.String())
	return
}()

func prepareExecutorInfo(execBinary string, server server) *mesos.ExecutorInfo {
	var (
		executorUris     = []mesos.CommandInfo_URI{}
		uri, executorCmd = serveExecutorArtifact(server, execBinary)
		executorCommand  = fmt.Sprintf("./%s", executorCmd)
	)
	executorUris = append(executorUris, mesos.CommandInfo_URI{Value: uri, Executable: proto.Bool(true)})

	go http.ListenAndServe(fmt.Sprintf("%s:%d", server.address, server.port), nil)
	log.Println("Serving executor artifacts...")

	// Create mesos scheduler driver.
	return &mesos.ExecutorInfo{
		ExecutorID: mesos.ExecutorID{Value: "default"},
		Name:       proto.String("Test Executor"),
		Source:     proto.String("foo"),
		Command: mesos.CommandInfo{
			Value: proto.String(executorCommand),
			URIs:  executorUris,
		},
		Resources: wantsExecutorResources,
	}
}

type server struct {
	address string
	port    int
}

type config struct {
	id         string
	user       string
	name       string
	role       string
	url        string
	codec      codec
	timeout    time.Duration
	checkpoint bool
	principal  string
	hostname   string
	labels     Labels
	server     server
	executor   string
}

type internalState struct {
	tasksLaunched int
	tasksFinished int
	totalTasks    int
	frameworkID   string
	role          string
	executor      *mesos.ExecutorInfo
	cli           *httpcli.Client
}
<commit_msg>parameterize number of tasks<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	proto "github.com/gogo/protobuf/proto"
	"github.com/mesos/mesos-go"
	"github.com/mesos/mesos-go/encoding"
	"github.com/mesos/mesos-go/httpcli"
	"github.com/mesos/mesos-go/httpcli/stream"
	"github.com/mesos/mesos-go/scheduler"
	"github.com/mesos/mesos-go/scheduler/calls"
)

func main() {
	cfg := config{
		user:       "root",
		name:       "example",
		url:        "http://:5050/api/v1/scheduler",
		codec:      codec{Codec: &encoding.ProtobufCodec},
		timeout:    time.Second,
		checkpoint: true,
		server:     server{address: "127.0.0.1", port: 34567},
		tasks:      5,
	}

	fs := flag.NewFlagSet("example-scheduler", flag.ExitOnError)
	cfg.addFlags(fs)
	fs.Parse(os.Args[1:])

	if err := run(&cfg); err != nil {
		log.Fatal(err)
	}
}

func run(cfg *config) error {
	var state internalState
	state.cli = httpcli.New(
		httpcli.URL(cfg.url),
		httpcli.Codec(cfg.codec.Codec),
		httpcli.Do(httpcli.With(httpcli.Timeout(cfg.timeout))),
	)

	if cfg.executor == "" {
		panic("must specify an executor binary")
	}

	state.executor = prepareExecutorInfo(cfg.executor, cfg.server)
	state.totalTasks = cfg.tasks

	frameworkInfo := &mesos.FrameworkInfo{
		User:       cfg.user,
		Name:       cfg.name,
		Checkpoint: &cfg.checkpoint,
	}
	if cfg.role != "" {
		frameworkInfo.Role = &cfg.role
	}
	if cfg.principal != "" {
		frameworkInfo.Principal = &cfg.principal
	}
	if cfg.hostname != "" {
		frameworkInfo.Hostname = &cfg.hostname
	}
	if len(cfg.labels) > 0 {
		log.Println("using labels:", cfg.labels)
		frameworkInfo.Labels = &mesos.Labels{Labels: cfg.labels}
	}
	subscribe := calls.Subscribe(true, frameworkInfo)
	registrationTokens := backoffBucket(1*time.Second, 15*time.Second, nil)
	for {
		resp, opt, err := stream.Subscribe(state.cli, subscribe)
		if err == nil {
			func() {
				undo := state.cli.With(opt)
				defer state.cli.With(undo) // strip the stream options
				err = eventLoop(&state, resp.Decoder, resp)
			}()
		}
		if err != nil && err != io.EOF {
			log.Println(err)
		} else {
			log.Println("disconnected")
		}
		if state.frameworkID != "" {
			subscribe.Subscribe.FrameworkInfo.ID = &mesos.FrameworkID{Value: state.frameworkID}
		}
		<-registrationTokens
		log.Println("reconnecting..")
	}
}

// returns the framework ID received by mesos (if any); callers should check for a
// framework ID regardless of whether error != nil.
func eventLoop(state *internalState, eventDecoder encoding.Decoder, conn io.Closer) (err error) {
	defer func() {
		if conn != nil {
			conn.Close()
		}
	}()

	state.frameworkID = ""
	callOptions := scheduler.CallOptions{} // should be applied to every outgoing call

	for err == nil {
		var e scheduler.Event
		if err = eventDecoder.Invoke(&e); err != nil {
			continue
		}

		log.Printf("%+v\n", e)

		switch e.GetType() {
		case scheduler.Event_FAILURE:
			log.Println("received a FAILURE event")
			f := e.GetFailure()
			failure(f.ExecutorID, f.AgentID, f.Status)

		case scheduler.Event_OFFERS:
			log.Println("received an OFFERS event")
			resourceOffers(state, callOptions.Copy(), e.GetOffers().GetOffers())

		case scheduler.Event_UPDATE:
			statusUpdate(state, callOptions.Copy(), e.GetUpdate().GetStatus())

		case scheduler.Event_ERROR:
			// it's recommended that we abort and re-try subscribing; setting
			// err here will cause the event loop to terminate and the connection
			// will be reset.
			err = fmt.Errorf("ERROR: " + e.GetError().GetMessage())

		case scheduler.Event_SUBSCRIBED:
			log.Println("received a SUBSCRIBED event")
			if state.frameworkID == "" {
				state.frameworkID = e.GetSubscribed().GetFrameworkID().GetValue()
				if state.frameworkID == "" {
					// sanity check
					panic("mesos gave us an empty frameworkID")
				}
				callOptions = append(callOptions, calls.Framework(state.frameworkID))
			}
			// else, ignore subsequently received events like this on the same connection
		default:
			// handle unknown event
		}
	} // for
	return err
} // eventLoop

func failure(eid *mesos.ExecutorID, aid *mesos.AgentID, stat *int32) {
	if eid != nil {
		// executor failed..
		msg := "executor '" + eid.Value + "' terminated"
		if aid != nil {
			msg += "on agent '" + aid.Value + "'"
		}
		if stat != nil {
			msg += ", with status=" + strconv.Itoa(int(*stat))
		}
		log.Println(msg)
	} else if aid != nil {
		// agent failed..
		log.Println("agent '" + aid.Value + "' terminated")
	}
}

const (
	CPUS_PER_TASK = 1
	MEM_PER_TASK  = 64
)

var wantsTaskResources = func() (r mesos.Resources) {
	r.Add(
		*mesos.BuildResource().Name("cpus").Scalar(CPUS_PER_TASK).Resource,
		*mesos.BuildResource().Name("mem").Scalar(MEM_PER_TASK).Resource,
	)
	log.Println("wants-task-resources = " + r.String())
	return
}()

func resourceOffers(state *internalState, callOptions scheduler.CallOptions, offers []mesos.Offer) {
	for i := range offers {
		var (
			remaining = mesos.Resources(offers[i].Resources)
			tasks     = []mesos.TaskInfo{}
		)

		log.Println("received offer id '" + offers[i].ID.Value + "' with resources " + remaining.String())

		wantsResources := wantsTaskResources.Clone()
		if len(offers[i].ExecutorIDs) == 0 {
			wantsResources.Add(state.executor.Resources...)
		}

		for state.tasksLaunched < state.totalTasks && remaining.Flatten().ContainsAll(wantsResources) {
			state.tasksLaunched++
			taskID := state.tasksLaunched

			log.Println("launching task " + strconv.Itoa(taskID) + " using offer " + offers[i].ID.Value)

			task := mesos.TaskInfo{TaskID: mesos.TaskID{Value: strconv.Itoa(taskID)}}
			task.Name = "Task " + task.TaskID.Value
			task.AgentID = offers[i].AgentID
			task.Executor = state.executor
			task.Resources = remaining.Find(wantsTaskResources.Flatten(mesos.Role(state.role).Assign()))

			remaining.Subtract(task.Resources...)
			tasks = append(tasks, task)
		}

		// build Accept call to launch all of the tasks we've assembled
		accept := calls.Accept(
			calls.OfferWithOperations(
				offers[i].ID,
				calls.OpLaunch(tasks...),
			),
		).With(callOptions...)

		// send Accept call to mesos
		resp, err := state.cli.Do(accept)
		if err != nil {
			log.Printf("failed to launch tasks: %+v", err)
		} else {
			resp.Close() // no data for these calls
		}
	}
}

func statusUpdate(state *internalState, callOptions scheduler.CallOptions, s mesos.TaskStatus) {
	msg := "Task " + s.TaskID.Value + " is in state " + s.GetState().String()
	if m := s.GetMessage(); m != "" {
		msg += " with message '" + m + "'"
	}
	log.Println(msg)

	if uuid := s.GetUUID(); len(uuid) > 0 {
		ack := calls.Acknowledge(
			s.GetAgentID().GetValue(),
			s.TaskID.Value,
			uuid,
		).With(callOptions...)

		// send Accept call to mesos
		resp, err := state.cli.Do(ack)
		if err != nil {
			log.Println("failed to ack status update for task: %+v", err)
			return
		} else {
			resp.Close() // no data for these calls
		}
	}
	switch st := s.GetState(); st {
	case mesos.TASK_FINISHED:
		state.tasksFinished++
	case mesos.TASK_LOST, mesos.TASK_KILLED, mesos.TASK_FAILED:
		log.Println("Exiting because task " + s.GetTaskID().Value +
			" is in an unexpected state " + st.String() +
			" with reason " + s.GetReason().String() +
			" from source " + s.GetSource().String() +
			" with message '" + s.GetMessage() + "'")
		os.Exit(1)
	}

	if state.tasksFinished == state.totalTasks {
		log.Println("mission accomplished, terminating")
		os.Exit(0)
	}
}

// returns (downloadURI, basename(path))
func serveExecutorArtifact(server server, path string) (string, string) {
	serveFile := func(pattern string, filename string) {
		http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
			http.ServeFile(w, r, filename)
		})
	}

	// Create base path (http://foobar:5000/<base>)
	pathSplit := strings.Split(path, "/")
	var base string
	if len(pathSplit) > 0 {
		base = pathSplit[len(pathSplit)-1]
	} else {
		base = path
	}
	serveFile("/"+base, path)

	hostURI := fmt.Sprintf("http://%s:%d/%s", server.address, server.port, base)
	log.Println("Hosting artifact '" + path + "' at '" + hostURI + "'")

	return hostURI, base
}

var wantsExecutorResources = func() (r mesos.Resources) {
	r.Add(
		*mesos.BuildResource().Name("cpus").Scalar(0.01).Resource,
		*mesos.BuildResource().Name("mem").Scalar(64).Resource,
	)
	log.Println("wants-executor-resources = " + r.String())
	return
}()

func prepareExecutorInfo(execBinary string, server server) *mesos.ExecutorInfo {
	var (
		executorUris     = []mesos.CommandInfo_URI{}
		uri, executorCmd = serveExecutorArtifact(server, execBinary)
		executorCommand  = fmt.Sprintf("./%s", executorCmd)
	)
	executorUris = append(executorUris, mesos.CommandInfo_URI{Value: uri, Executable: proto.Bool(true)})

	go http.ListenAndServe(fmt.Sprintf("%s:%d", server.address, server.port), nil)
	log.Println("Serving executor artifacts...")

	// Create mesos scheduler driver.
	return &mesos.ExecutorInfo{
		ExecutorID: mesos.ExecutorID{Value: "default"},
		Name:       proto.String("Test Executor"),
		Source:     proto.String("foo"),
		Command: mesos.CommandInfo{
			Value: proto.String(executorCommand),
			URIs:  executorUris,
		},
		Resources: wantsExecutorResources,
	}
}

type server struct {
	address string
	port    int
}

type config struct {
	id         string
	user       string
	name       string
	role       string
	url        string
	codec      codec
	timeout    time.Duration
	checkpoint bool
	principal  string
	hostname   string
	labels     Labels
	server     server
	executor   string
	tasks      int
}

func (cfg *config) addFlags(fs *flag.FlagSet) {
	fs.StringVar(&cfg.user, "user", cfg.user, "Framework user to register with the Mesos master")
	fs.StringVar(&cfg.name, "name", cfg.name, "Framework name to register with the Mesos master")
	fs.StringVar(&cfg.role, "role", cfg.role, "Framework role to register with the Mesos master")
	fs.Var(&cfg.codec, "codec", "Codec to encode/decode scheduler API communications [protobuf, json]")
	fs.StringVar(&cfg.url, "url", cfg.url, "Mesos scheduler API URL")
	fs.DurationVar(&cfg.timeout, "timeout", cfg.timeout, "Mesos scheduler API connection timeout")
	fs.BoolVar(&cfg.checkpoint, "checkpoint", cfg.checkpoint, "Enable/disable framework checkpointing")
	fs.StringVar(&cfg.principal, "principal", cfg.principal, "Framework principal with which to authenticate")
	fs.StringVar(&cfg.hostname, "hostname", cfg.hostname, "Framework hostname that is advertised to the master")
	fs.Var(&cfg.labels, "label", "Framework label, may be specified multiple times")
	fs.StringVar(&cfg.server.address, "server.address", cfg.server.address, "IP of artifact server")
	fs.IntVar(&cfg.server.port, "server.port", cfg.server.port, "Port of artifact server")
	fs.StringVar(&cfg.executor, "executor", cfg.executor, "Full path to executor binary")
	fs.IntVar(&cfg.tasks, "tasks", cfg.tasks, "Number of tasks to spawn")
}

type internalState struct {
	tasksLaunched int
	tasksFinished int
	totalTasks    int
	frameworkID   string
	role          string
	executor      *mesos.ExecutorInfo
	cli           *httpcli.Client
}
<|endoftext|>


<commit_before>package main

import (
	"archive/tar"
	"compress/gzip"
	"errors"
	"fmt"
	"github.com/Symantec/Dominator/imageserver/client"
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/filesystem/scanner"
	"github.com/Symantec/Dominator/lib/filesystem/untar"
	"github.com/Symantec/Dominator/lib/filter"
	"github.com/Symantec/Dominator/lib/hash"
	"github.com/Symantec/Dominator/lib/image"
	objectclient "github.com/Symantec/Dominator/lib/objectserver/client"
	"github.com/Symantec/Dominator/lib/srpc"
	"io"
	"os"
	"strings"
	"time"
)

func addImagefileSubcommand(args []string) {
	imageSClient, objectClient := getClients()
	err := addImagefile(imageSClient, objectClient, args[0], args[1], args[2],
		args[3])
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error adding image: \"%s\"\t%s\n", args[0], err)
		os.Exit(1)
	}
	os.Exit(0)
}

func addImagefile(imageSClient *srpc.Client,
	objectClient *objectclient.ObjectClient,
	name, imageFilename, filterFilename, triggersFilename string) error {
	imageExists, err := client.CheckImage(imageSClient, name)
	if err != nil {
		return errors.New("error checking for image existance: " + err.Error())
	}
	if imageExists {
		return errors.New("image exists")
	}
	newImage := new(image.Image)
	if err := loadImageFiles(newImage, objectClient, filterFilename,
		triggersFilename); err != nil {
		return err
	}
	newImage.FileSystem, err = buildImage(imageSClient, newImage.Filter,
		imageFilename)
	if err != nil {
		return errors.New("error building image: " + err.Error())
	}
	if err := spliceComputedFiles(newImage.FileSystem); err != nil {
		return err
	}
	return addImage(imageSClient, name, newImage)
}

func addImage(imageSClient *srpc.Client, name string, img *image.Image) error {
	if *expiresIn > 0 {
		img.ExpiresAt = time.Now().Add(*expiresIn)
	}
	if err := img.Verify(); err != nil {
		return err
	}
	if err := img.VerifyRequiredPaths(requiredPaths); err != nil {
		return err
	}
	if err := client.AddImage(imageSClient, name, img); err != nil {
		return errors.New("remote error: " + err.Error())
	}
	return nil
}

type hasher struct {
	objQ *objectclient.ObjectAdderQueue
}

func (h *hasher) Hash(reader io.Reader, length uint64) (
	hash.Hash, error) {
	hash, err := h.objQ.Add(reader, length)
	if err != nil {
		return hash, errors.New("error sending image data: " + err.Error())
	}
	return hash, nil
}

func buildImage(imageSClient *srpc.Client, filter *filter.Filter,
	imageFilename string) (*filesystem.FileSystem, error) {
	fi, err := os.Lstat(imageFilename)
	if err != nil {
		return nil, err
	}
	var h hasher
	h.objQ, err = objectclient.NewObjectAdderQueue(imageSClient)
	if err != nil {
		return nil, err
	}
	var fs *filesystem.FileSystem
	if fi.IsDir() {
		sfs, err := scanner.ScanFileSystem(imageFilename, nil, filter, nil, &h,
			nil)
		if err != nil {
			h.objQ.Close()
			return nil, err
		}
		fs = &sfs.FileSystem
	} else {
		imageFile, err := os.Open(imageFilename)
		if err != nil {
			h.objQ.Close()
			return nil, errors.New("error opening image file: " + err.Error())
		}
		defer imageFile.Close()
		var imageReader io.Reader
		if strings.HasSuffix(imageFilename, ".tar") {
			imageReader = imageFile
		} else if strings.HasSuffix(imageFilename, ".tar.gz") ||
			strings.HasSuffix(imageFilename, ".tgz") {
			gzipReader, err := gzip.NewReader(imageFile)
			if err != nil {
				h.objQ.Close()
				return nil, errors.New(
					"error creating gzip reader: " + err.Error())
			}
			defer gzipReader.Close()
			imageReader = gzipReader
		} else {
			h.objQ.Close()
			return nil, errors.New("unrecognised image type")
		}
		tarReader := tar.NewReader(imageReader)
		fs, err = untar.Decode(tarReader, &h, filter)
		if err != nil {
			h.objQ.Close()
			return nil, errors.New("error building image: " + err.Error())
		}
	}
	if err != nil {
		h.objQ.Close()
		return nil, err
	}
	err = h.objQ.Close()
	if err != nil {
		return nil, err
	}
	return fs, nil
}
<commit_msg>Minor refactor of buildImage() in cmd/imagetool.<commit_after>package main

import (
	"archive/tar"
	"compress/gzip"
	"errors"
	"fmt"
	"github.com/Symantec/Dominator/imageserver/client"
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/filesystem/scanner"
	"github.com/Symantec/Dominator/lib/filesystem/untar"
	"github.com/Symantec/Dominator/lib/filter"
	"github.com/Symantec/Dominator/lib/hash"
	"github.com/Symantec/Dominator/lib/image"
	objectclient "github.com/Symantec/Dominator/lib/objectserver/client"
	"github.com/Symantec/Dominator/lib/srpc"
	"io"
	"os"
	"strings"
	"time"
)

func addImagefileSubcommand(args []string) {
	imageSClient, objectClient := getClients()
	err := addImagefile(imageSClient, objectClient, args[0], args[1], args[2],
		args[3])
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error adding image: \"%s\"\t%s\n", args[0], err)
		os.Exit(1)
	}
	os.Exit(0)
}

func addImagefile(imageSClient *srpc.Client,
	objectClient *objectclient.ObjectClient,
	name, imageFilename, filterFilename, triggersFilename string) error {
	imageExists, err := client.CheckImage(imageSClient, name)
	if err != nil {
		return errors.New("error checking for image existance: " + err.Error())
	}
	if imageExists {
		return errors.New("image exists")
	}
	newImage := new(image.Image)
	if err := loadImageFiles(newImage, objectClient, filterFilename,
		triggersFilename); err != nil {
		return err
	}
	newImage.FileSystem, err = buildImage(imageSClient, newImage.Filter,
		imageFilename)
	if err != nil {
		return errors.New("error building image: " + err.Error())
	}
	if err := spliceComputedFiles(newImage.FileSystem); err != nil {
		return err
	}
	return addImage(imageSClient, name, newImage)
}

func addImage(imageSClient *srpc.Client, name string, img *image.Image) error {
	if *expiresIn > 0 {
		img.ExpiresAt = time.Now().Add(*expiresIn)
	}
	if err := img.Verify(); err != nil {
		return err
	}
	if err := img.VerifyRequiredPaths(requiredPaths); err != nil {
		return err
	}
	if err := client.AddImage(imageSClient, name, img); err != nil {
		return errors.New("remote error: " + err.Error())
	}
	return nil
}

type hasher struct {
	objQ *objectclient.ObjectAdderQueue
}

func (h *hasher) Hash(reader io.Reader, length uint64) (
	hash.Hash, error) {
	hash, err := h.objQ.Add(reader, length)
	if err != nil {
		return hash, errors.New("error sending image data: " + err.Error())
	}
	return hash, nil
}

func buildImage(imageSClient *srpc.Client, filter *filter.Filter,
	imageFilename string) (*filesystem.FileSystem, error) {
	var h hasher
	var err error
	h.objQ, err = objectclient.NewObjectAdderQueue(imageSClient)
	if err != nil {
		return nil, err
	}
	fs, err := buildImageWithHasher(imageSClient, filter, imageFilename, &h)
	if err != nil {
		h.objQ.Close()
		return nil, err
	}
	err = h.objQ.Close()
	if err != nil {
		return nil, err
	}
	return fs, nil
}

func buildImageWithHasher(imageSClient *srpc.Client, filter *filter.Filter,
	imageFilename string, h *hasher) (*filesystem.FileSystem, error) {
	fi, err := os.Lstat(imageFilename)
	if err != nil {
		return nil, err
	}
	var fs *filesystem.FileSystem
	if fi.IsDir() {
		sfs, err := scanner.ScanFileSystem(imageFilename, nil, filter, nil, h,
			nil)
		if err != nil {
			return nil, err
		}
		fs = &sfs.FileSystem
	} else {
		imageFile, err := os.Open(imageFilename)
		if err != nil {
			return nil, errors.New("error opening image file: " + err.Error())
		}
		defer imageFile.Close()
		var imageReader io.Reader
		if strings.HasSuffix(imageFilename, ".tar") {
			imageReader = imageFile
		} else if strings.HasSuffix(imageFilename, ".tar.gz") ||
			strings.HasSuffix(imageFilename, ".tgz") {
			gzipReader, err := gzip.NewReader(imageFile)
			if err != nil {
				return nil, errors.New(
					"error creating gzip reader: " + err.Error())
			}
			defer gzipReader.Close()
			imageReader = gzipReader
		} else {
			return nil, errors.New("unrecognised image type")
		}
		tarReader := tar.NewReader(imageReader)
		fs, err = untar.Decode(tarReader, h, filter)
		if err != nil {
			return nil, errors.New("error building image: " + err.Error())
		}
	}
	return fs, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/mindjiver/gopherstack"
	"os"
)

func main() {

	apiurl := os.Getenv("CLOUDSTACK_API_URL")
	if len(apiurl) == 0 {
		fmt.Println("Needed environment variable CLOUDSTACK_API_URL not found, exiting")
		os.Exit(1)
	}
	apikey := os.Getenv("CLOUDSTACK_API_KEY")
	if len(apikey) == 0 {
		fmt.Println("Needed environment variable CLOUDSTACK_API_KEY not found, exiting")
		os.Exit(1)
	}
	secret := os.Getenv("CLOUDSTACK_SECRET")
	if len(secret) == 0 {
		fmt.Println("Needed environment variable CLOUDSTACK_SECRET not found, exiting")
		os.Exit(1)
	}

	// Always validate any SSL certificates in the chain
	insecureskipverify := false
	cs := gopherstack.CloudStackClient{}.New(apiurl, apikey, secret, insecureskipverify)

	vmid := "19d2acfb-e281-4a13-8d62-e04ab501271d"
	response, err := cs.ListVirtualMachines(vmid)
	if err != nil {
		fmt.Errorf("Error listing virtual machine: %s", err)
		os.Exit(1)
	}

	if len(response.Listvirtualmachinesresponse.Virtualmachine) > 0 {
		ip := response.Listvirtualmachinesresponse.Virtualmachine[0].Nic[0].Ipaddress
		state := response.Listvirtualmachinesresponse.Virtualmachine[0].State
		fmt.Printf("%s has IP : %s and state : %s\n", vmid, ip, state)
	} else {
		fmt.Printf("No VM with UUID: %s found\n", vmid)
	}

}
<commit_msg>Update for new API naming<commit_after>package main

import (
	"fmt"
	"github.com/mindjiver/gopherstack"
	"os"
)

func main() {

	apiurl := os.Getenv("CLOUDSTACK_API_URL")
	if len(apiurl) == 0 {
		fmt.Println("Needed environment variable CLOUDSTACK_API_URL not found, exiting")
		os.Exit(1)
	}
	apikey := os.Getenv("CLOUDSTACK_API_KEY")
	if len(apikey) == 0 {
		fmt.Println("Needed environment variable CLOUDSTACK_API_KEY not found, exiting")
		os.Exit(1)
	}
	secret := os.Getenv("CLOUDSTACK_SECRET")
	if len(secret) == 0 {
		fmt.Println("Needed environment variable CLOUDSTACK_SECRET not found, exiting")
		os.Exit(1)
	}

	// Always validate any SSL certificates in the chain
	insecureskipverify := false
	cs := gopherstack.CloudstackClient{}.New(apiurl, apikey, secret, insecureskipverify)

	vmid := "19d2acfb-e281-4a13-8d62-e04ab501271d"
	response, err := cs.ListVirtualMachines(vmid)
	if err != nil {
		fmt.Errorf("Error listing virtual machine: %s", err)
		os.Exit(1)
	}

	if len(response.Listvirtualmachinesresponse.Virtualmachine) > 0 {
		ip := response.Listvirtualmachinesresponse.Virtualmachine[0].Nic[0].Ipaddress
		state := response.Listvirtualmachinesresponse.Virtualmachine[0].State
		fmt.Printf("%s has IP : %s and state : %s\n", vmid, ip, state)
	} else {
		fmt.Printf("No VM with UUID: %s found\n", vmid)
	}

}
<|endoftext|>


<commit_before>// Copyright 2019-2020 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Synopsis:
//     ipmidump [-option]
//
// Description:
//
// Options:
//     -chassis : Print chassis power status.
//     -sel     : Print SEL information.
//     -lan     : Print IP information.
//     -device  : Print device information.
//     -raw     : Send raw command and print response.
//     -help    : Print help message.
package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strconv"
	"time"

	"github.com/u-root/u-root/pkg/ipmi"
)

const cmd = "ipmidump [options] "

var (
	flagChassis = flag.Bool("chassis", false, "print chassis power status")
	flagSEL     = flag.Bool("sel", false, "print SEL information")
	flagLan     = flag.Bool("lan", false, "Print IP address")
	flagRaw     = flag.Bool("raw", false, "Send IPMI raw command")
	flagHelp    = flag.Bool("help", false, "print help message")
	flagDev     = flag.Bool("device", false, "print device information")
)

func itob(i int) bool { return i != 0 }

func init() {
	defUsage := flag.Usage
	flag.Usage = func() {
		os.Args[0] = cmd
		defUsage()
	}
}

func main() {
	flag.Parse()

	if *flagHelp {
		flag.Usage()
		os.Exit(1)
	}

	if *flagChassis {
		chassisInfo()
	}

	if *flagSEL {
		selInfo()
	}

	if *flagLan {
		lanConfig()
	}

	if *flagDev {
		deviceID()
	}

	if *flagRaw {
		sendRawCmd(flag.Args())
	}
}

func chassisInfo() {
	allow := map[bool]string{true: "allowed", false: "not allowed"}
	act := map[bool]string{true: "active", false: "inactive"}
	state := map[bool]string{true: "true", false: "false"}

	policy := map[int]string{
		0x0: "always-off",
		0x1: "previous",
		0x2: "always-on",
		0x3: "unknown",
	}

	event := map[int]string{
		0x10: "IPMI command",
		0x08: "power fault",
		0x04: "power interlock",
		0x02: "power overload",
		0x01: "AC failed",
		0x00: "none",
	}

	ipmi, err := ipmi.Open(0)
	if err != nil {
		fmt.Printf("Failed to open ipmi device: %v\n", err)
	}
	defer ipmi.Close()

	if status, err := ipmi.GetChassisStatus(); err != nil {
		fmt.Printf("Failed to get chassis power status: %v\n", err)
	} else {
		// Current power status
		data := int(status.CurrentPowerState)
		fmt.Println("Chassis power status")
		fmt.Println("Power Restore Policy:", policy[(data>>5)&0x03])
		fmt.Println("Power Control Fault :", state[itob(data&0x10)])
		fmt.Println("Power Fault         :", state[itob(data&0x08)])
		fmt.Println("Power Interlock     :", act[itob(data&0x04)])
		fmt.Println("Power Overload      :", state[itob(data&0x02)])
		fmt.Printf("Power Status        : ")
		if (data & 0x01) != 0 {
			fmt.Println("on")
		} else {
			fmt.Println("off")
		}

		// Last power event
		data = int(status.LastPowerEvent)
		fmt.Println("Last Power Event    :", event[data&0x1F])

		// Misc. chassis state
		data = int(status.MiscChassisState)
		fmt.Println("Misc. chassis state")
		fmt.Println("Cooling/Fan Fault   :", state[itob(data&0x08)])
		fmt.Println("Drive Fault         :", state[itob(data&0x04)])
		fmt.Println("Front Panel Lockout :", act[itob(data&0x02)])
		fmt.Println("Chass Intrusion     :", act[itob(data&0x01)])

		// Front panel button (optional)
		data = int(status.FrontPanelButton)
		if status.FrontPanelButton != 0 {
			fmt.Println("Front Panel Button")
			fmt.Println("Standby Button Disable    :", allow[itob(data&0x80)])
			fmt.Println("Diagnostic Buttton Disable:", allow[itob(data&0x40)])
			fmt.Println("Reset Button Disable      :", allow[itob(data&0x20)])
			fmt.Println("Power-off Button Disable  :", allow[itob(data&0x10)])

			fmt.Println("Standby Button            :", state[itob(data&0x08)])
			fmt.Println("Diagnostic Buttton        :", state[itob(data&0x04)])
			fmt.Println("Reset Button              :", state[itob(data&0x02)])
			fmt.Println("Power-off Button          :", state[itob(data&0x01)])
		} else {
			fmt.Println("Front Panel Button  : none")
		}
	}
}

func selInfo() {
	support := map[bool]string{true: "supported", false: "unsupported"}

	ipmi, err := ipmi.Open(0)
	if err != nil {
		fmt.Printf("Failed to open ipmi device: %v\n", err)
	}
	defer ipmi.Close()

	if info, err := ipmi.GetSELInfo(); err != nil {
		fmt.Printf("Failed to get SEL information: %v\n", err)
	} else {
		fmt.Println("SEL information")

		switch info.Version {
		case 0x51:
			fallthrough
		case 0x02:
			fmt.Printf("Version        : %d.%d (1.5, 2.0 compliant)\n", info.Version&0x0F, info.Version>>4)
		default:
			fmt.Println("Version        : unknown")
		}

		fmt.Println("Entries        :", info.Entries)
		fmt.Printf("Free Space     : %d bytes\n", info.FreeSpace)

		// Most recent addition/erase timestamp
		fmt.Printf("Last Add Time  : ")
		if info.LastAddTime != 0xFFFFFFFF {
			fmt.Println(time.Unix(int64(info.LastAddTime), 0))
		} else {
			fmt.Println("not available")
		}

		fmt.Printf("Last Del Time  : ")
		if info.LastDelTime != 0xFFFFFFFF {
			fmt.Println(time.Unix(int64(info.LastDelTime), 0))
		} else {
			fmt.Println("not available")
		}

		// Operation Support
		fmt.Printf("Overflow       : ")
		if (info.OpSupport & 0x80) != 0 {
			fmt.Println("true")
		} else {
			fmt.Println("false")
		}

		data := int(info.OpSupport)
		if (data & 0x0F) != 0 {
			fmt.Println("Supported cmds")
			fmt.Println("Delete         :", support[itob(data&0x08)])
			fmt.Println("Partial Add    :", support[itob(data&0x04)])
			fmt.Println("Reserve        :", support[itob(data&0x02)])
			fmt.Println("Get Alloc Info :", support[itob(data&0x01)])
		} else {
			fmt.Println("Supported cmds : none")
		}
	}
}

func lanConfig() {
	const (
		setInProgress byte = iota
		_
		_
		IPAddress
		IPAddressSrc
		MACAddress
	)

	setInProgressStr := []string{
		"Set Complete", "Set In Progress", "Commit Write", "Reserved",
	}

	IPAddressSrcStr := []string{
		"Unspecified", "Static Address", "DHCP Address", "BIOS Assigned Address",
	}

	ipmi, err := ipmi.Open(0)
	if err != nil {
		log.Fatal(err)
	}
	defer ipmi.Close()

	// data 1	completion code
	// data 2	parameter revision, 0x11
	// data 3:N	data

	// set in progress
	if buf, err := ipmi.GetLanConfig(1, setInProgress); err != nil {
		fmt.Printf("Failed to get LAN config: %v\n", err)
	} else {
		fmt.Printf("Set In Progress   : ")
		if int(buf[2]) < len(setInProgressStr) {
			fmt.Println(setInProgressStr[buf[2]])
		} else {
			fmt.Println("Unknown")
			fmt.Printf("%v\n", buf)
		}
	}

	// ip address source
	if buf, err := ipmi.GetLanConfig(1, IPAddressSrc); err != nil {
		fmt.Printf("Failed to get LAN config: %v\n", err)
	} else {
		fmt.Printf("IP Address Source : ")
		if int(buf[2]) < len(IPAddressSrcStr) {
			fmt.Println(IPAddressSrcStr[buf[2]])
		} else {
			fmt.Println("Other")
			fmt.Printf("%v\n", buf)
		}
	}

	// ip address
	if buf, err := ipmi.GetLanConfig(1, IPAddress); err != nil {
		fmt.Printf("Failed to get LAN config: %v\n", err)
	} else {
		fmt.Printf("IP Address        : ")
		if len(buf) == 6 {
			fmt.Printf("%d.%d.%d.%d\n", buf[2], buf[3], buf[4], buf[5])
		} else {
			fmt.Printf("Unknown\n")
		}
	}

	// MAC address
	if buf, err := ipmi.GetLanConfig(1, MACAddress); err != nil {
		fmt.Printf("Failed to get LAN config: %v\n", err)
	} else {
		fmt.Printf("MAC Address       : ")
		if len(buf) == 8 {
			fmt.Printf("%02x:%02x:%02x:%02x:%02x:%02x\n", buf[2], buf[3], buf[4], buf[5], buf[6], buf[7])
		} else {
			fmt.Printf("Unknown\n")
		}
	}
}

func deviceID() {
	status := map[byte]string{
		0x80: "yes",
		0x00: "no",
	}

	adtlDevSupport := []string{
		"Sensor Device",         /* bit 0 */
		"SDR Repository Device", /* bit 1 */
		"SEL Device",            /* bit 2 */
		"FRU Inventory Device",  /* bit 3 */
		"IPMB Event Receiver",   /* bit 4 */
		"IPMB Event Generator",  /* bit 5 */
		"Bridge",                /* bit	6 */
		"Chassis Device",        /* bit 7 */
	}

	ipmi, err := ipmi.Open(0)
	if err != nil {
		fmt.Printf("Failed to open ipmi device: %v\n", err)
	}
	defer ipmi.Close()

	if info, err := ipmi.GetDeviceID(); err != nil {
		fmt.Printf("Failed to get device ID information: %v\n", err)
	} else {
		fmt.Println("Device ID information")
		fmt.Printf("%-26s: %d\n", "Device ID", info.DeviceID)
		fmt.Printf("%-26s: %d\n", "Device Revision", (info.DeviceRevision & 0x0F))
		fmt.Printf("%-26s: %d.%02x\n", "Firmware Revision",
			(info.FwRev1 & 0x3F), info.FwRev2)

		spec := uint8(info.IpmiVersion)
		fmt.Printf("%-26s: %x.%x\n", "IPMI Version", spec&0x0F, (spec&0xF0)>>4)

		var mid uint32
		mid = uint32(info.ManufacturerID[2]) << 16
		mid |= uint32(info.ManufacturerID[1]) << 8
		mid |= uint32(info.ManufacturerID[0])

		fmt.Printf("%-26s: %d (0x%04X)\n", "Manufacturer ID", mid, mid)

		var pid uint16
		pid = uint16(info.ProductID[1]) << 8
		pid |= uint16(info.ProductID[0])

		fmt.Printf("%-26s: %d (0x%04X)\n", "Product ID", pid, pid)

		// bit 7 == 0 indicates normal operation
		fmt.Printf("%-26s: %s\n", "Device Available", status[(^info.FwRev1&0x80)])
		fmt.Printf("%-26s: %s\n", "Provides Device SDRs",
			status[(info.DeviceRevision&0x80)])

		fmt.Printf("%-26s:\n", "Additional Device Support")
		for i := 0; i < 8; i++ {
			if (info.AdtlDeviceSupport & (1 << i)) != 0 {
				fmt.Printf("    %s\n", adtlDevSupport[i])
			}
		}

		// This field is optional, 4 bytes.
		fmt.Printf("%-26s:\n", "Aux Firmware Rev Info")
		for _, val := range info.AuxFwRev {
			fmt.Printf("    0x%02x\n", val)
		}
	}
}

func sendRawCmd(cmds []string) {
	ipmi, err := ipmi.Open(0)
	if err != nil {
		log.Fatal(err)
	}
	defer ipmi.Close()

	data := make([]byte, 0)

	for _, cmd := range cmds {
		val, err := strconv.ParseInt(cmd, 0, 16)
		if err != nil {
			fmt.Printf("Invalid syntax: \"%s\"\n", cmd)
			return
		}
		data = append(data, byte(val))
	}

	if buf, err := ipmi.RawCmd(data); err != nil {
		fmt.Printf("Unable to send RAW command: %v\n", err)
	} else {
		for i, x := range buf {
			fmt.Printf("| 0x%-2x ", x)
			if i%8 == 7 || i == len(buf)-1 {
				fmt.Printf("|\n")
			}
		}
	}
}
<commit_msg>cmds/exp/ipmidump: use correct number of bits in ParseUint<commit_after>// Copyright 2019-2020 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Synopsis:
//     ipmidump [-option]
//
// Description:
//
// Options:
//     -chassis : Print chassis power status.
//     -sel     : Print SEL information.
//     -lan     : Print IP information.
//     -device  : Print device information.
//     -raw     : Send raw command and print response.
//     -help    : Print help message.
package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strconv"
	"time"

	"github.com/u-root/u-root/pkg/ipmi"
)

const cmd = "ipmidump [options] "

var (
	flagChassis = flag.Bool("chassis", false, "print chassis power status")
	flagSEL     = flag.Bool("sel", false, "print SEL information")
	flagLan     = flag.Bool("lan", false, "Print IP address")
	flagRaw     = flag.Bool("raw", false, "Send IPMI raw command")
	flagHelp    = flag.Bool("help", false, "print help message")
	flagDev     = flag.Bool("device", false, "print device information")
)

func itob(i int) bool { return i != 0 }

func init() {
	defUsage := flag.Usage
	flag.Usage = func() {
		os.Args[0] = cmd
		defUsage()
	}
}

func main() {
	flag.Parse()

	if *flagHelp {
		flag.Usage()
		os.Exit(1)
	}

	if *flagChassis {
		chassisInfo()
	}

	if *flagSEL {
		selInfo()
	}

	if *flagLan {
		lanConfig()
	}

	if *flagDev {
		deviceID()
	}

	if *flagRaw {
		sendRawCmd(flag.Args())
	}
}

func chassisInfo() {
	allow := map[bool]string{true: "allowed", false: "not allowed"}
	act := map[bool]string{true: "active", false: "inactive"}
	state := map[bool]string{true: "true", false: "false"}

	policy := map[int]string{
		0x0: "always-off",
		0x1: "previous",
		0x2: "always-on",
		0x3: "unknown",
	}

	event := map[int]string{
		0x10: "IPMI command",
		0x08: "power fault",
		0x04: "power interlock",
		0x02: "power overload",
		0x01: "AC failed",
		0x00: "none",
	}

	ipmi, err := ipmi.Open(0)
	if err != nil {
		fmt.Printf("Failed to open ipmi device: %v\n", err)
	}
	defer ipmi.Close()

	if status, err := ipmi.GetChassisStatus(); err != nil {
		fmt.Printf("Failed to get chassis power status: %v\n", err)
	} else {
		// Current power status
		data := int(status.CurrentPowerState)
		fmt.Println("Chassis power status")
		fmt.Println("Power Restore Policy:", policy[(data>>5)&0x03])
		fmt.Println("Power Control Fault :", state[itob(data&0x10)])
		fmt.Println("Power Fault         :", state[itob(data&0x08)])
		fmt.Println("Power Interlock     :", act[itob(data&0x04)])
		fmt.Println("Power Overload      :", state[itob(data&0x02)])
		fmt.Printf("Power Status        : ")
		if (data & 0x01) != 0 {
			fmt.Println("on")
		} else {
			fmt.Println("off")
		}

		// Last power event
		data = int(status.LastPowerEvent)
		fmt.Println("Last Power Event    :", event[data&0x1F])

		// Misc. chassis state
		data = int(status.MiscChassisState)
		fmt.Println("Misc. chassis state")
		fmt.Println("Cooling/Fan Fault   :", state[itob(data&0x08)])
		fmt.Println("Drive Fault         :", state[itob(data&0x04)])
		fmt.Println("Front Panel Lockout :", act[itob(data&0x02)])
		fmt.Println("Chass Intrusion     :", act[itob(data&0x01)])

		// Front panel button (optional)
		data = int(status.FrontPanelButton)
		if status.FrontPanelButton != 0 {
			fmt.Println("Front Panel Button")
			fmt.Println("Standby Button Disable    :", allow[itob(data&0x80)])
			fmt.Println("Diagnostic Buttton Disable:", allow[itob(data&0x40)])
			fmt.Println("Reset Button Disable      :", allow[itob(data&0x20)])
			fmt.Println("Power-off Button Disable  :", allow[itob(data&0x10)])

			fmt.Println("Standby Button            :", state[itob(data&0x08)])
			fmt.Println("Diagnostic Buttton        :", state[itob(data&0x04)])
			fmt.Println("Reset Button              :", state[itob(data&0x02)])
			fmt.Println("Power-off Button          :", state[itob(data&0x01)])
		} else {
			fmt.Println("Front Panel Button  : none")
		}
	}
}

func selInfo() {
	support := map[bool]string{true: "supported", false: "unsupported"}

	ipmi, err := ipmi.Open(0)
	if err != nil {
		fmt.Printf("Failed to open ipmi device: %v\n", err)
	}
	defer ipmi.Close()

	if info, err := ipmi.GetSELInfo(); err != nil {
		fmt.Printf("Failed to get SEL information: %v\n", err)
	} else {
		fmt.Println("SEL information")

		switch info.Version {
		case 0x51:
			fallthrough
		case 0x02:
			fmt.Printf("Version        : %d.%d (1.5, 2.0 compliant)\n", info.Version&0x0F, info.Version>>4)
		default:
			fmt.Println("Version        : unknown")
		}

		fmt.Println("Entries        :", info.Entries)
		fmt.Printf("Free Space     : %d bytes\n", info.FreeSpace)

		// Most recent addition/erase timestamp
		fmt.Printf("Last Add Time  : ")
		if info.LastAddTime != 0xFFFFFFFF {
			fmt.Println(time.Unix(int64(info.LastAddTime), 0))
		} else {
			fmt.Println("not available")
		}

		fmt.Printf("Last Del Time  : ")
		if info.LastDelTime != 0xFFFFFFFF {
			fmt.Println(time.Unix(int64(info.LastDelTime), 0))
		} else {
			fmt.Println("not available")
		}

		// Operation Support
		fmt.Printf("Overflow       : ")
		if (info.OpSupport & 0x80) != 0 {
			fmt.Println("true")
		} else {
			fmt.Println("false")
		}

		data := int(info.OpSupport)
		if (data & 0x0F) != 0 {
			fmt.Println("Supported cmds")
			fmt.Println("Delete         :", support[itob(data&0x08)])
			fmt.Println("Partial Add    :", support[itob(data&0x04)])
			fmt.Println("Reserve        :", support[itob(data&0x02)])
			fmt.Println("Get Alloc Info :", support[itob(data&0x01)])
		} else {
			fmt.Println("Supported cmds : none")
		}
	}
}

func lanConfig() {
	const (
		setInProgress byte = iota
		_
		_
		IPAddress
		IPAddressSrc
		MACAddress
	)

	setInProgressStr := []string{
		"Set Complete", "Set In Progress", "Commit Write", "Reserved",
	}

	IPAddressSrcStr := []string{
		"Unspecified", "Static Address", "DHCP Address", "BIOS Assigned Address",
	}

	ipmi, err := ipmi.Open(0)
	if err != nil {
		log.Fatal(err)
	}
	defer ipmi.Close()

	// data 1	completion code
	// data 2	parameter revision, 0x11
	// data 3:N	data

	// set in progress
	if buf, err := ipmi.GetLanConfig(1, setInProgress); err != nil {
		fmt.Printf("Failed to get LAN config: %v\n", err)
	} else {
		fmt.Printf("Set In Progress   : ")
		if int(buf[2]) < len(setInProgressStr) {
			fmt.Println(setInProgressStr[buf[2]])
		} else {
			fmt.Println("Unknown")
			fmt.Printf("%v\n", buf)
		}
	}

	// ip address source
	if buf, err := ipmi.GetLanConfig(1, IPAddressSrc); err != nil {
		fmt.Printf("Failed to get LAN config: %v\n", err)
	} else {
		fmt.Printf("IP Address Source : ")
		if int(buf[2]) < len(IPAddressSrcStr) {
			fmt.Println(IPAddressSrcStr[buf[2]])
		} else {
			fmt.Println("Other")
			fmt.Printf("%v\n", buf)
		}
	}

	// ip address
	if buf, err := ipmi.GetLanConfig(1, IPAddress); err != nil {
		fmt.Printf("Failed to get LAN config: %v\n", err)
	} else {
		fmt.Printf("IP Address        : ")
		if len(buf) == 6 {
			fmt.Printf("%d.%d.%d.%d\n", buf[2], buf[3], buf[4], buf[5])
		} else {
			fmt.Printf("Unknown\n")
		}
	}

	// MAC address
	if buf, err := ipmi.GetLanConfig(1, MACAddress); err != nil {
		fmt.Printf("Failed to get LAN config: %v\n", err)
	} else {
		fmt.Printf("MAC Address       : ")
		if len(buf) == 8 {
			fmt.Printf("%02x:%02x:%02x:%02x:%02x:%02x\n", buf[2], buf[3], buf[4], buf[5], buf[6], buf[7])
		} else {
			fmt.Printf("Unknown\n")
		}
	}
}

func deviceID() {
	status := map[byte]string{
		0x80: "yes",
		0x00: "no",
	}

	adtlDevSupport := []string{
		"Sensor Device",         /* bit 0 */
		"SDR Repository Device", /* bit 1 */
		"SEL Device",            /* bit 2 */
		"FRU Inventory Device",  /* bit 3 */
		"IPMB Event Receiver",   /* bit 4 */
		"IPMB Event Generator",  /* bit 5 */
		"Bridge",                /* bit	6 */
		"Chassis Device",        /* bit 7 */
	}

	ipmi, err := ipmi.Open(0)
	if err != nil {
		fmt.Printf("Failed to open ipmi device: %v\n", err)
	}
	defer ipmi.Close()

	if info, err := ipmi.GetDeviceID(); err != nil {
		fmt.Printf("Failed to get device ID information: %v\n", err)
	} else {
		fmt.Println("Device ID information")
		fmt.Printf("%-26s: %d\n", "Device ID", info.DeviceID)
		fmt.Printf("%-26s: %d\n", "Device Revision", (info.DeviceRevision & 0x0F))
		fmt.Printf("%-26s: %d.%02x\n", "Firmware Revision",
			(info.FwRev1 & 0x3F), info.FwRev2)

		spec := uint8(info.IpmiVersion)
		fmt.Printf("%-26s: %x.%x\n", "IPMI Version", spec&0x0F, (spec&0xF0)>>4)

		var mid uint32
		mid = uint32(info.ManufacturerID[2]) << 16
		mid |= uint32(info.ManufacturerID[1]) << 8
		mid |= uint32(info.ManufacturerID[0])

		fmt.Printf("%-26s: %d (0x%04X)\n", "Manufacturer ID", mid, mid)

		var pid uint16
		pid = uint16(info.ProductID[1]) << 8
		pid |= uint16(info.ProductID[0])

		fmt.Printf("%-26s: %d (0x%04X)\n", "Product ID", pid, pid)

		// bit 7 == 0 indicates normal operation
		fmt.Printf("%-26s: %s\n", "Device Available", status[(^info.FwRev1&0x80)])
		fmt.Printf("%-26s: %s\n", "Provides Device SDRs",
			status[(info.DeviceRevision&0x80)])

		fmt.Printf("%-26s:\n", "Additional Device Support")
		for i := 0; i < 8; i++ {
			if (info.AdtlDeviceSupport & (1 << i)) != 0 {
				fmt.Printf("    %s\n", adtlDevSupport[i])
			}
		}

		// This field is optional, 4 bytes.
		fmt.Printf("%-26s:\n", "Aux Firmware Rev Info")
		for _, val := range info.AuxFwRev {
			fmt.Printf("    0x%02x\n", val)
		}
	}
}

func sendRawCmd(cmds []string) {
	ipmi, err := ipmi.Open(0)
	if err != nil {
		log.Fatal(err)
	}
	defer ipmi.Close()

	data := make([]byte, 0)

	for _, cmd := range cmds {
		val, err := strconv.ParseInt(cmd, 0, 8)
		if err != nil {
			fmt.Printf("Invalid syntax: \"%s\"\n", cmd)
			return
		}
		data = append(data, byte(val))
	}

	if buf, err := ipmi.RawCmd(data); err != nil {
		fmt.Printf("Unable to send RAW command: %v\n", err)
	} else {
		for i, x := range buf {
			fmt.Printf("| 0x%-2x ", x)
			if i%8 == 7 || i == len(buf)-1 {
				fmt.Printf("|\n")
			}
		}
	}
}
<|endoftext|>


<commit_before>package grayt

import "testing"

func TestAlignedBoxIntersection(t *testing.T) {
	_ = "breakpoint"

	box := AlignedBox(
		Vect(0.9, 1.0, -0.9),
		Vect(0.1, 0.999, -0.1),
	)[0]
	r := ray{
		Vector{0.18325558497391392, 0.999012404240046, -0.9999999999999999},
		Vector{-0.5720297174078126, 0.007828964018125696, 0.8201955313976911},
	}
	intersection, hit := box.intersect(r)
	if !hit {
		t.Error("should have hit")
	}
	if intersection.distance != 0.12192214681978378 {
		t.Errorf("wrong distance")
	}
	if intersection.unitNormal != Vect(0, 0, -1) {
		t.Errorf("wrong normal")
	}
}
<commit_msg>Add benchmark for triangle intersection (~18ns)<commit_after>package grayt

import "testing"

func TestAlignedBoxIntersection(t *testing.T) {
	_ = "breakpoint"

	box := AlignedBox(
		Vect(0.9, 1.0, -0.9),
		Vect(0.1, 0.999, -0.1),
	)[0]
	r := ray{
		Vector{0.18325558497391392, 0.999012404240046, -0.9999999999999999},
		Vector{-0.5720297174078126, 0.007828964018125696, 0.8201955313976911},
	}
	intersection, hit := box.intersect(r)
	if !hit {
		t.Error("should have hit")
	}
	if intersection.distance != 0.12192214681978378 {
		t.Errorf("wrong distance")
	}
	if intersection.unitNormal != Vect(0, 0, -1) {
		t.Errorf("wrong normal")
	}
}

func BenchmarkTriangleIntersect(b *testing.B) {

	t := newTriangle(
		Vect(+0, 0, +0),
		Vect(-1, 1, -1),
		Vect(+1, 1, -1),
	)
	r := ray{start: Vect(0, 0.5, 5), dir: Vect(0, 0, -1)}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		t.intersect(r)
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/gen/autoscaling"
)

func resourceAwsAutoscalingGroup() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsAutoscalingGroupCreate,
		Read:   resourceAwsAutoscalingGroupRead,
		Update: resourceAwsAutoscalingGroupUpdate,
		Delete: resourceAwsAutoscalingGroupDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"launch_configuration": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			"desired_capacity": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},

			"min_size": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
			},

			"max_size": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
			},

			"default_cooldown": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"force_delete": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"health_check_grace_period": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"health_check_type": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"availability_zones": &schema.Schema{
				Type:     schema.TypeSet,
				Required: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},

			"load_balancers": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},

			"vpc_zone_identifier": &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))
				},
			},

			"termination_policies": &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 resourceAwsAutoscalingGroupCreate(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	var autoScalingGroupOpts autoscaling.CreateAutoScalingGroupType
	autoScalingGroupOpts.AutoScalingGroupName = aws.String(d.Get("name").(string))
	autoScalingGroupOpts.HealthCheckType = aws.String(d.Get("health_check_type").(string))
	autoScalingGroupOpts.LaunchConfigurationName = aws.String(d.Get("launch_configuration").(string))
	autoScalingGroupOpts.MinSize = aws.Integer(d.Get("min_size").(int))
	autoScalingGroupOpts.MaxSize = aws.Integer(d.Get("max_size").(int))
	autoScalingGroupOpts.AvailabilityZones = expandStringList(
		d.Get("availability_zones").(*schema.Set).List())

	if v, ok := d.GetOk("default_cooldown"); ok {
		autoScalingGroupOpts.DefaultCooldown = aws.Integer(v.(int))
	}

	if v, ok := d.GetOk("desired_capacity"); ok {
		autoScalingGroupOpts.DesiredCapacity = aws.Integer(v.(int))
	}

	if v, ok := d.GetOk("health_check_grace_period"); ok {
		autoScalingGroupOpts.HealthCheckGracePeriod = aws.Integer(v.(int))
	}

	if v, ok := d.GetOk("load_balancers"); ok && v.(*schema.Set).Len() > 0 {
		autoScalingGroupOpts.LoadBalancerNames = expandStringList(
			v.(*schema.Set).List())
	}

	if v, ok := d.GetOk("vpc_zone_identifier"); ok && v.(*schema.Set).Len() > 0 {
		exp := expandStringList(v.(*schema.Set).List())
		autoScalingGroupOpts.VPCZoneIdentifier = aws.String(strings.Join(exp, ","))
	}

	if v, ok := d.GetOk("termination_policies"); ok && v.(*schema.Set).Len() > 0 {
		autoScalingGroupOpts.TerminationPolicies = expandStringList(
			v.(*schema.Set).List())
	}

	log.Printf("[DEBUG] AutoScaling Group create configuration: %#v", autoScalingGroupOpts)
	err := autoscalingconn.CreateAutoScalingGroup(&autoScalingGroupOpts)
	if err != nil {
		return fmt.Errorf("Error creating Autoscaling Group: %s", err)
	}

	d.SetId(d.Get("name").(string))
	log.Printf("[INFO] AutoScaling Group ID: %s", d.Id())

	return resourceAwsAutoscalingGroupRead(d, meta)
}

func resourceAwsAutoscalingGroupRead(d *schema.ResourceData, meta interface{}) error {
	g, err := getAwsAutoscalingGroup(d, meta)
	if err != nil {
		return err
	}
	if g == nil {
		return nil
	}

	d.Set("availability_zones", g.AvailabilityZones)
	d.Set("default_cooldown", *g.DefaultCooldown)
	d.Set("desired_capacity", *g.DesiredCapacity)
	d.Set("health_check_grace_period", *g.HealthCheckGracePeriod)
	d.Set("health_check_type", *g.HealthCheckType)
	d.Set("launch_configuration", *g.LaunchConfigurationName)
	d.Set("load_balancers", g.LoadBalancerNames)
	d.Set("min_size", *g.MinSize)
	d.Set("max_size", *g.MaxSize)
	d.Set("name", *g.AutoScalingGroupName)
	d.Set("vpc_zone_identifier", strings.Split(*g.VPCZoneIdentifier, ","))
	d.Set("termination_policies", g.TerminationPolicies)

	return nil
}

func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	opts := autoscaling.UpdateAutoScalingGroupType{
		AutoScalingGroupName: aws.String(d.Id()),
	}

	if d.HasChange("desired_capacity") {
		opts.DesiredCapacity = aws.Integer(d.Get("desired_capacity").(int))
	}

	if d.HasChange("launch_configuration") {
		opts.LaunchConfigurationName = aws.String(d.Get("launch_configuration").(string))
	}

	if d.HasChange("min_size") {
		opts.MinSize = aws.Integer(d.Get("min_size").(int))
	}

	if d.HasChange("max_size") {
		opts.MaxSize = aws.Integer(d.Get("max_size").(int))
	}

	log.Printf("[DEBUG] AutoScaling Group update configuration: %#v", opts)
	err := autoscalingconn.UpdateAutoScalingGroup(&opts)
	if err != nil {
		d.Partial(true)
		return fmt.Errorf("Error updating Autoscaling group: %s", err)
	}

	return resourceAwsAutoscalingGroupRead(d, meta)
}

func resourceAwsAutoscalingGroupDelete(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	// Read the autoscaling group first. If it doesn't exist, we're done.
	// We need the group in order to check if there are instances attached.
	// If so, we need to remove those first.
	g, err := getAwsAutoscalingGroup(d, meta)
	if err != nil {
		return err
	}
	if g == nil {
		return nil
	}
	if len(g.Instances) > 0 || *g.DesiredCapacity > 0 {
		if err := resourceAwsAutoscalingGroupDrain(d, meta); err != nil {
			return err
		}
	}

	log.Printf("[DEBUG] AutoScaling Group destroy: %v", d.Id())
	deleteopts := autoscaling.DeleteAutoScalingGroupType{AutoScalingGroupName: aws.String(d.Id())}

	// You can force an autoscaling group to delete
	// even if it's in the process of scaling a resource.
	// Normally, you would set the min-size and max-size to 0,0
	// and then delete the group. This bypasses that and leaves
	// resources potentially dangling.
	if d.Get("force_delete").(bool) {
		deleteopts.ForceDelete = aws.Boolean(true)
	}

	if err := autoscalingconn.DeleteAutoScalingGroup(&deleteopts); err != nil {
		autoscalingerr, ok := err.(aws.APIError)
		if ok && autoscalingerr.Code == "InvalidGroup.NotFound" {
			return nil
		}
		return err
	}

	return nil
}

func getAwsAutoscalingGroup(
	d *schema.ResourceData,
	meta interface{}) (*autoscaling.AutoScalingGroup, error) {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	describeOpts := autoscaling.AutoScalingGroupNamesType{
		AutoScalingGroupNames: []string{d.Id()},
	}

	log.Printf("[DEBUG] AutoScaling Group describe configuration: %#v", describeOpts)
	describeGroups, err := autoscalingconn.DescribeAutoScalingGroups(&describeOpts)
	if err != nil {
		autoscalingerr, ok := err.(aws.APIError)
		if ok && autoscalingerr.Code == "InvalidGroup.NotFound" {
			d.SetId("")
			return nil, nil
		}

		return nil, fmt.Errorf("Error retrieving AutoScaling groups: %s", err)
	}

	// Search for the autoscaling group
	for idx, asc := range describeGroups.AutoScalingGroups {
		if *asc.AutoScalingGroupName == d.Id() {
			return &describeGroups.AutoScalingGroups[idx], nil
		}
	}

	// ASG not found
	d.SetId("")
	return nil, nil
}

func resourceAwsAutoscalingGroupDrain(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	// First, set the capacity to zero so the group will drain
	log.Printf("[DEBUG] Reducing autoscaling group capacity to zero")
	opts := autoscaling.UpdateAutoScalingGroupType{
		AutoScalingGroupName: aws.String(d.Id()),
		DesiredCapacity:      aws.Integer(0),
		MinSize:              aws.Integer(0),
		MaxSize:              aws.Integer(0),
	}
	if err := autoscalingconn.UpdateAutoScalingGroup(&opts); err != nil {
		return fmt.Errorf("Error setting capacity to zero to drain: %s", err)
	}

	// Next, wait for the autoscale group to drain
	log.Printf("[DEBUG] Waiting for group to have zero instances")
	return resource.Retry(10*time.Minute, func() error {
		g, err := getAwsAutoscalingGroup(d, meta)
		if err != nil {
			return resource.RetryError{Err: err}
		}
		if g == nil {
			return nil
		}

		if len(g.Instances) == 0 {
			return nil
		}

		return fmt.Errorf("group still has %d instances", len(g.Instances))
	})
}
<commit_msg>Prevent empty string to be used as default health_check_type<commit_after>package aws

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/gen/autoscaling"
)

func resourceAwsAutoscalingGroup() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsAutoscalingGroupCreate,
		Read:   resourceAwsAutoscalingGroupRead,
		Update: resourceAwsAutoscalingGroupUpdate,
		Delete: resourceAwsAutoscalingGroupDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"launch_configuration": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			"desired_capacity": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},

			"min_size": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
			},

			"max_size": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
			},

			"default_cooldown": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"force_delete": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"health_check_grace_period": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"health_check_type": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"availability_zones": &schema.Schema{
				Type:     schema.TypeSet,
				Required: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},

			"load_balancers": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},

			"vpc_zone_identifier": &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))
				},
			},

			"termination_policies": &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 resourceAwsAutoscalingGroupCreate(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	var autoScalingGroupOpts autoscaling.CreateAutoScalingGroupType
	autoScalingGroupOpts.AutoScalingGroupName = aws.String(d.Get("name").(string))
	autoScalingGroupOpts.LaunchConfigurationName = aws.String(d.Get("launch_configuration").(string))
	autoScalingGroupOpts.MinSize = aws.Integer(d.Get("min_size").(int))
	autoScalingGroupOpts.MaxSize = aws.Integer(d.Get("max_size").(int))
	autoScalingGroupOpts.AvailabilityZones = expandStringList(
		d.Get("availability_zones").(*schema.Set).List())

	if v, ok := d.GetOk("default_cooldown"); ok {
		autoScalingGroupOpts.DefaultCooldown = aws.Integer(v.(int))
	}

	if v, ok := d.GetOk("health_check"); ok && v.(string) != "" {
		autoScalingGroupOpts.HealthCheckType = aws.String(v.(string))
	}

	if v, ok := d.GetOk("desired_capacity"); ok {
		autoScalingGroupOpts.DesiredCapacity = aws.Integer(v.(int))
	}

	if v, ok := d.GetOk("health_check_grace_period"); ok {
		autoScalingGroupOpts.HealthCheckGracePeriod = aws.Integer(v.(int))
	}

	if v, ok := d.GetOk("load_balancers"); ok && v.(*schema.Set).Len() > 0 {
		autoScalingGroupOpts.LoadBalancerNames = expandStringList(
			v.(*schema.Set).List())
	}

	if v, ok := d.GetOk("vpc_zone_identifier"); ok && v.(*schema.Set).Len() > 0 {
		exp := expandStringList(v.(*schema.Set).List())
		autoScalingGroupOpts.VPCZoneIdentifier = aws.String(strings.Join(exp, ","))
	}

	if v, ok := d.GetOk("termination_policies"); ok && v.(*schema.Set).Len() > 0 {
		autoScalingGroupOpts.TerminationPolicies = expandStringList(
			v.(*schema.Set).List())
	}

	log.Printf("[DEBUG] AutoScaling Group create configuration: %#v", autoScalingGroupOpts)
	err := autoscalingconn.CreateAutoScalingGroup(&autoScalingGroupOpts)
	if err != nil {
		return fmt.Errorf("Error creating Autoscaling Group: %s", err)
	}

	d.SetId(d.Get("name").(string))
	log.Printf("[INFO] AutoScaling Group ID: %s", d.Id())

	return resourceAwsAutoscalingGroupRead(d, meta)
}

func resourceAwsAutoscalingGroupRead(d *schema.ResourceData, meta interface{}) error {
	g, err := getAwsAutoscalingGroup(d, meta)
	if err != nil {
		return err
	}
	if g == nil {
		return nil
	}

	d.Set("availability_zones", g.AvailabilityZones)
	d.Set("default_cooldown", *g.DefaultCooldown)
	d.Set("desired_capacity", *g.DesiredCapacity)
	d.Set("health_check_grace_period", *g.HealthCheckGracePeriod)
	d.Set("health_check_type", *g.HealthCheckType)
	d.Set("launch_configuration", *g.LaunchConfigurationName)
	d.Set("load_balancers", g.LoadBalancerNames)
	d.Set("min_size", *g.MinSize)
	d.Set("max_size", *g.MaxSize)
	d.Set("name", *g.AutoScalingGroupName)
	d.Set("vpc_zone_identifier", strings.Split(*g.VPCZoneIdentifier, ","))
	d.Set("termination_policies", g.TerminationPolicies)

	return nil
}

func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	opts := autoscaling.UpdateAutoScalingGroupType{
		AutoScalingGroupName: aws.String(d.Id()),
	}

	if d.HasChange("desired_capacity") {
		opts.DesiredCapacity = aws.Integer(d.Get("desired_capacity").(int))
	}

	if d.HasChange("launch_configuration") {
		opts.LaunchConfigurationName = aws.String(d.Get("launch_configuration").(string))
	}

	if d.HasChange("min_size") {
		opts.MinSize = aws.Integer(d.Get("min_size").(int))
	}

	if d.HasChange("max_size") {
		opts.MaxSize = aws.Integer(d.Get("max_size").(int))
	}

	log.Printf("[DEBUG] AutoScaling Group update configuration: %#v", opts)
	err := autoscalingconn.UpdateAutoScalingGroup(&opts)
	if err != nil {
		d.Partial(true)
		return fmt.Errorf("Error updating Autoscaling group: %s", err)
	}

	return resourceAwsAutoscalingGroupRead(d, meta)
}

func resourceAwsAutoscalingGroupDelete(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	// Read the autoscaling group first. If it doesn't exist, we're done.
	// We need the group in order to check if there are instances attached.
	// If so, we need to remove those first.
	g, err := getAwsAutoscalingGroup(d, meta)
	if err != nil {
		return err
	}
	if g == nil {
		return nil
	}
	if len(g.Instances) > 0 || *g.DesiredCapacity > 0 {
		if err := resourceAwsAutoscalingGroupDrain(d, meta); err != nil {
			return err
		}
	}

	log.Printf("[DEBUG] AutoScaling Group destroy: %v", d.Id())
	deleteopts := autoscaling.DeleteAutoScalingGroupType{AutoScalingGroupName: aws.String(d.Id())}

	// You can force an autoscaling group to delete
	// even if it's in the process of scaling a resource.
	// Normally, you would set the min-size and max-size to 0,0
	// and then delete the group. This bypasses that and leaves
	// resources potentially dangling.
	if d.Get("force_delete").(bool) {
		deleteopts.ForceDelete = aws.Boolean(true)
	}

	if err := autoscalingconn.DeleteAutoScalingGroup(&deleteopts); err != nil {
		autoscalingerr, ok := err.(aws.APIError)
		if ok && autoscalingerr.Code == "InvalidGroup.NotFound" {
			return nil
		}
		return err
	}

	return nil
}

func getAwsAutoscalingGroup(
	d *schema.ResourceData,
	meta interface{}) (*autoscaling.AutoScalingGroup, error) {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	describeOpts := autoscaling.AutoScalingGroupNamesType{
		AutoScalingGroupNames: []string{d.Id()},
	}

	log.Printf("[DEBUG] AutoScaling Group describe configuration: %#v", describeOpts)
	describeGroups, err := autoscalingconn.DescribeAutoScalingGroups(&describeOpts)
	if err != nil {
		autoscalingerr, ok := err.(aws.APIError)
		if ok && autoscalingerr.Code == "InvalidGroup.NotFound" {
			d.SetId("")
			return nil, nil
		}

		return nil, fmt.Errorf("Error retrieving AutoScaling groups: %s", err)
	}

	// Search for the autoscaling group
	for idx, asc := range describeGroups.AutoScalingGroups {
		if *asc.AutoScalingGroupName == d.Id() {
			return &describeGroups.AutoScalingGroups[idx], nil
		}
	}

	// ASG not found
	d.SetId("")
	return nil, nil
}

func resourceAwsAutoscalingGroupDrain(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	// First, set the capacity to zero so the group will drain
	log.Printf("[DEBUG] Reducing autoscaling group capacity to zero")
	opts := autoscaling.UpdateAutoScalingGroupType{
		AutoScalingGroupName: aws.String(d.Id()),
		DesiredCapacity:      aws.Integer(0),
		MinSize:              aws.Integer(0),
		MaxSize:              aws.Integer(0),
	}
	if err := autoscalingconn.UpdateAutoScalingGroup(&opts); err != nil {
		return fmt.Errorf("Error setting capacity to zero to drain: %s", err)
	}

	// Next, wait for the autoscale group to drain
	log.Printf("[DEBUG] Waiting for group to have zero instances")
	return resource.Retry(10*time.Minute, func() error {
		g, err := getAwsAutoscalingGroup(d, meta)
		if err != nil {
			return resource.RetryError{Err: err}
		}
		if g == nil {
			return nil
		}

		if len(g.Instances) == 0 {
			return nil
		}

		return fmt.Errorf("group still has %d instances", len(g.Instances))
	})
}
<|endoftext|>


<commit_before>package fake_gordon

import (
	"io/ioutil"
	"os"
	"sync"

	"code.google.com/p/gogoprotobuf/proto"
	"github.com/cloudfoundry-incubator/gordon"
	"github.com/cloudfoundry-incubator/gordon/warden"
	"github.com/nu7hatch/gouuid"
)

type FakeGordon struct {
	Connected    bool
	ConnectError error

	createdHandles    []string
	createdProperties []map[string]string
	CreateError       error

	stoppedHandles []string
	StopError      error

	destroyedHandles []string
	DestroyError     error

	SpawnError error

	LinkError error

	NetInError error

	memoryLimits     []Limit
	limitMemoryError error

	GetMemoryLimitError error

	diskLimits     []DiskLimit
	limitDiskError error

	GetDiskLimitError error

	ListError error

	infoError    error
	infoResponse *warden.InfoResponse

	AttachError error

	scriptsThatRan              []*RunningScript
	runCallbacks                map[*RunningScript]RunCallback
	runReturnProcessID          uint32
	runReturnProcessPayloadChan <-chan *warden.ProcessPayload
	runReturnError              error

	copiedIn        []*CopiedIn
	copyInCallbacks map[*CopiedIn]CopyInCallback
	copyInError     error

	copiedOut                     []*CopiedOut
	copyOutCallbacks              map[*CopiedOut]CopyOutCallback
	fileContentToProvideOnCopyOut []byte
	copyOutError                  error

	lock *sync.RWMutex
}

type RunCallback func() (uint32, <-chan *warden.ProcessPayload, error)

type CopyInCallback func(CopiedIn) error
type CopyOutCallback func(CopiedOut) error

type RunningScript struct {
	Handle         string
	Script         string
	ResourceLimits gordon.ResourceLimits
}

type CopiedIn struct {
	Handle string
	Src    string
	Dst    string
}

type CopiedOut struct {
	Handle string
	Src    string
	Dst    string
	Owner  string
}

type Limit struct {
	Handle string
	Limit  uint64
}

type DiskLimit struct {
	Handle string
	Limits gordon.DiskLimits
}

func New() *FakeGordon {
	f := &FakeGordon{}
	f.Reset()
	return f
}

func (f *FakeGordon) Reset() {
	f.lock = &sync.RWMutex{}
	f.Connected = false
	f.ConnectError = nil

	f.createdHandles = []string{}
	f.createdProperties = []map[string]string{}
	f.CreateError = nil

	f.stoppedHandles = []string{}
	f.StopError = nil

	f.destroyedHandles = []string{}
	f.DestroyError = nil

	f.SpawnError = nil
	f.LinkError = nil
	f.NetInError = nil
	f.GetMemoryLimitError = nil
	f.GetDiskLimitError = nil
	f.ListError = nil
	f.AttachError = nil

	f.infoError = nil

	f.limitMemoryError = nil
	f.limitDiskError = nil
	f.memoryLimits = []Limit{}
	f.diskLimits = []DiskLimit{}

	f.scriptsThatRan = make([]*RunningScript, 0)
	f.runCallbacks = make(map[*RunningScript]RunCallback)
	f.runReturnProcessID = 0
	f.runReturnError = nil

	f.copyInError = nil
	f.copyOutError = nil
	f.copiedIn = []*CopiedIn{}
	f.copiedOut = []*CopiedOut{}
	f.copyInCallbacks = make(map[*CopiedIn]CopyInCallback)
	f.copyOutCallbacks = make(map[*CopiedOut]CopyOutCallback)
	f.fileContentToProvideOnCopyOut = []byte{}
}

func (f *FakeGordon) Connect() error {
	f.lock.Lock()
	defer f.lock.Unlock()
	f.Connected = true
	return f.ConnectError
}

func (f *FakeGordon) Create(properties map[string]string) (*warden.CreateResponse, error) {
	f.lock.Lock()
	defer f.lock.Unlock()
	if f.CreateError != nil {
		return nil, f.CreateError
	}

	handleUuid, _ := uuid.NewV4()
	handle := handleUuid.String()[:11]

	f.createdHandles = append(f.createdHandles, handle)
	f.createdProperties = append(f.createdProperties, properties)

	return &warden.CreateResponse{
		Handle: proto.String(handle),
	}, nil
}

func (f *FakeGordon) CreatedHandles() []string {
	f.lock.Lock()
	defer f.lock.Unlock()

	return f.createdHandles
}

func (f *FakeGordon) CreatedProperties() []map[string]string {
	f.lock.Lock()
	defer f.lock.Unlock()

	return f.createdProperties
}

func (f *FakeGordon) Stop(handle string, background, kill bool) (*warden.StopResponse, error) {
	f.lock.Lock()
	defer f.lock.Unlock()
	if f.StopError != nil {
		return nil, f.StopError
	}

	f.stoppedHandles = append(f.stoppedHandles, handle)

	return &warden.StopResponse{}, nil
}

func (f *FakeGordon) StoppedHandles() []string {
	f.lock.Lock()
	defer f.lock.Unlock()

	return f.stoppedHandles
}

func (f *FakeGordon) Destroy(handle string) (*warden.DestroyResponse, error) {
	f.lock.Lock()
	defer f.lock.Unlock()
	if f.DestroyError != nil {
		return nil, f.DestroyError
	}

	f.destroyedHandles = append(f.destroyedHandles, handle)

	return &warden.DestroyResponse{}, nil
}

func (f *FakeGordon) DestroyedHandles() []string {
	f.lock.Lock()
	defer f.lock.Unlock()

	return f.destroyedHandles
}

func (f *FakeGordon) NetIn(handle string) (*warden.NetInResponse, error) {
	panic("NOOP!")
	return nil, f.NetInError
}

func (f *FakeGordon) MemoryLimits() []Limit {
	f.lock.Lock()
	defer f.lock.Unlock()

	return f.memoryLimits
}

func (f *FakeGordon) SetLimitMemoryError(err error) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.limitMemoryError = err
}

func (f *FakeGordon) LimitMemory(handle string, limit uint64) (*warden.LimitMemoryResponse, error) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.memoryLimits = append(f.memoryLimits, Limit{
		Handle: handle,
		Limit:  limit,
	})

	return nil, f.limitMemoryError
}

func (f *FakeGordon) GetMemoryLimit(handle string) (uint64, error) {
	panic("NOOP!")
	return 0, f.GetMemoryLimitError
}

func (f *FakeGordon) DiskLimits() []DiskLimit {
	f.lock.Lock()
	defer f.lock.Unlock()

	return f.diskLimits
}

func (f *FakeGordon) SetLimitDiskError(err error) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.limitDiskError = err
}

func (f *FakeGordon) LimitDisk(handle string, limits gordon.DiskLimits) (*warden.LimitDiskResponse, error) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.diskLimits = append(f.diskLimits, DiskLimit{
		Handle: handle,
		Limits: limits,
	})

	return nil, f.limitDiskError
}

func (f *FakeGordon) GetDiskLimit(handle string) (uint64, error) {
	panic("NOOP!")
	return 0, f.GetDiskLimitError
}

func (f *FakeGordon) List(filterProperties map[string]string) (*warden.ListResponse, error) {
	panic("NOOP!")
	return nil, f.ListError
}

func (f *FakeGordon) Info(handle string) (*warden.InfoResponse, error) {
	f.lock.Lock()
	defer f.lock.Unlock()

	return f.infoResponse, f.infoError
}

func (f *FakeGordon) SetInfoError(err error) {
	f.lock.Lock()
	defer f.lock.Unlock()
	f.infoError = err
}

func (f *FakeGordon) SetInfoResponse(response *warden.InfoResponse) {
	f.lock.Lock()
	defer f.lock.Unlock()
	f.infoResponse = response
}

func (f *FakeGordon) CopyIn(handle, src, dst string) (*warden.CopyInResponse, error) {
	f.lock.RLock()
	err := f.copyInError
	f.lock.RUnlock()

	if err != nil {
		return nil, err
	}

	copiedIn := CopiedIn{
		Handle: handle,
		Src:    src,
		Dst:    dst,
	}

	f.lock.Lock()

	f.copiedIn = append(f.copiedIn, &copiedIn)

	f.lock.Unlock()

	for ci, cb := range f.copyInCallbacks {
		if (ci.Handle == "" || ci.Handle == handle) && ci.Src == src {
			err := cb(copiedIn)
			if err != nil {
				return nil, err
			}
		}
	}

	return &warden.CopyInResponse{}, nil
}

func (f *FakeGordon) ThingsCopiedIn() []*CopiedIn {
	f.lock.Lock()
	defer f.lock.Unlock()

	return f.copiedIn
}

func (f *FakeGordon) SetCopyInErr(err error) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.copyInError = err
}

func (f *FakeGordon) CopyOut(handle, src, dst, owner string) (*warden.CopyOutResponse, error) {
	f.lock.RLock()
	err := f.copyOutError
	f.lock.RUnlock()

	if err != nil {
		return nil, err
	}

	copiedOut := CopiedOut{
		Handle: handle,
		Src:    src,
		Dst:    dst,
		Owner:  owner,
	}

	f.lock.Lock()

	f.copiedOut = append(f.copiedOut, &copiedOut)

	if len(f.fileContentToProvideOnCopyOut) > 0 {
		err := ioutil.WriteFile(dst, f.fileContentToProvideOnCopyOut, os.ModePerm)
		if err != nil {
			panic(err)
		}
	}

	f.lock.Unlock()

	for co, cb := range f.copyOutCallbacks {
		if (co.Handle == "" || co.Handle == handle) && co.Src == src {
			err := cb(copiedOut)
			if err != nil {
				return nil, err
			}
		}
	}

	return &warden.CopyOutResponse{}, nil
}

func (f *FakeGordon) ThingsCopiedOut() []*CopiedOut {
	f.lock.Lock()
	defer f.lock.Unlock()

	return f.copiedOut
}

func (f *FakeGordon) SetCopyOutErr(err error) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.copyOutError = err
}

func (f *FakeGordon) SetCopyOutFileContent(data []byte) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.fileContentToProvideOnCopyOut = data
}

func (f *FakeGordon) Attach(handle string, jobID uint32) (<-chan *warden.ProcessPayload, error) {
	panic("NOOP!")
	return nil, f.AttachError
}

func (f *FakeGordon) ScriptsThatRan() []*RunningScript {
	f.lock.Lock()
	defer f.lock.Unlock()
	return f.scriptsThatRan
}

func (f *FakeGordon) SetRunReturnValues(processID uint32, processPayloadChan <-chan *warden.ProcessPayload, err error) {
	f.lock.Lock()
	defer f.lock.Unlock()
	f.runReturnProcessID = processID
	f.runReturnProcessPayloadChan = processPayloadChan
	f.runReturnError = err
}

func (f *FakeGordon) WhenRunning(handle string, script string, resourceLimits gordon.ResourceLimits, callback RunCallback) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.runCallbacks[&RunningScript{handle, script, resourceLimits}] = callback
}

func (f *FakeGordon) WhenCopyingOut(copiedOut CopiedOut, callback CopyOutCallback) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.copyOutCallbacks[&copiedOut] = callback
}

func (f *FakeGordon) WhenCopyingIn(copiedIn CopiedIn, callback CopyInCallback) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.copyInCallbacks[&copiedIn] = callback
}

func (f *FakeGordon) Run(handle string, script string, resourceLimits gordon.ResourceLimits) (uint32, <-chan *warden.ProcessPayload, error) {
	f.lock.Lock()

	f.scriptsThatRan = append(f.scriptsThatRan, &RunningScript{
		Handle:         handle,
		Script:         script,
		ResourceLimits: resourceLimits,
	})

	f.lock.Unlock()

	for ro, cb := range f.runCallbacks {
		if (ro.Handle == "" || ro.Handle == handle) && ro.Script == script {
			return cb()
		}
	}

	return f.runReturnProcessID, f.runReturnProcessPayloadChan, f.runReturnError
}
<commit_msg>expand fake gordon<commit_after>package fake_gordon

import (
	"io/ioutil"
	"os"
	"sync"

	"code.google.com/p/gogoprotobuf/proto"
	"github.com/cloudfoundry-incubator/gordon"
	"github.com/cloudfoundry-incubator/gordon/warden"
	"github.com/nu7hatch/gouuid"
)

type FakeGordon struct {
	Connected    bool
	ConnectError error

	createdHandles    []string
	createdProperties map[string]map[string]string
	CreateError       error

	stoppedHandles []string
	StopError      error

	destroyedHandles []string
	DestroyError     error

	SpawnError error

	LinkError error

	NetInError error

	memoryLimits     []Limit
	limitMemoryError error

	GetMemoryLimitError error

	diskLimits     []DiskLimit
	limitDiskError error

	GetDiskLimitError error

	listCallback ListCallback

	infoError    error
	infoResponse *warden.InfoResponse

	AttachError error

	scriptsThatRan              []*RunningScript
	runCallbacks                map[*RunningScript]RunCallback
	runReturnProcessID          uint32
	runReturnProcessPayloadChan <-chan *warden.ProcessPayload
	runReturnError              error

	copiedIn        []*CopiedIn
	copyInCallbacks map[*CopiedIn]CopyInCallback
	copyInError     error

	copiedOut                     []*CopiedOut
	copyOutCallbacks              map[*CopiedOut]CopyOutCallback
	fileContentToProvideOnCopyOut []byte
	copyOutError                  error

	lock *sync.RWMutex
}

type ListCallback func(filterProperties map[string]string) (*warden.ListResponse, error)

type RunCallback func() (uint32, <-chan *warden.ProcessPayload, error)

type CopyInCallback func(CopiedIn) error
type CopyOutCallback func(CopiedOut) error

type RunningScript struct {
	Handle         string
	Script         string
	ResourceLimits gordon.ResourceLimits
}

type CopiedIn struct {
	Handle string
	Src    string
	Dst    string
}

type CopiedOut struct {
	Handle string
	Src    string
	Dst    string
	Owner  string
}

type Limit struct {
	Handle string
	Limit  uint64
}

type DiskLimit struct {
	Handle string
	Limits gordon.DiskLimits
}

func New() *FakeGordon {
	f := &FakeGordon{}
	f.Reset()
	return f
}

func (f *FakeGordon) Reset() {
	f.lock = &sync.RWMutex{}
	f.Connected = false
	f.ConnectError = nil

	f.createdHandles = []string{}
	f.createdProperties = map[string]map[string]string{}
	f.CreateError = nil

	f.stoppedHandles = []string{}
	f.StopError = nil

	f.destroyedHandles = []string{}
	f.DestroyError = nil

	f.SpawnError = nil
	f.LinkError = nil
	f.NetInError = nil
	f.GetMemoryLimitError = nil
	f.GetDiskLimitError = nil
	f.AttachError = nil

	f.infoError = nil

	f.limitMemoryError = nil
	f.limitDiskError = nil
	f.memoryLimits = []Limit{}
	f.diskLimits = []DiskLimit{}

	f.scriptsThatRan = make([]*RunningScript, 0)
	f.runCallbacks = make(map[*RunningScript]RunCallback)
	f.runReturnProcessID = 0
	f.runReturnError = nil

	f.copyInError = nil
	f.copyOutError = nil
	f.copiedIn = []*CopiedIn{}
	f.copiedOut = []*CopiedOut{}
	f.copyInCallbacks = make(map[*CopiedIn]CopyInCallback)
	f.copyOutCallbacks = make(map[*CopiedOut]CopyOutCallback)
	f.fileContentToProvideOnCopyOut = []byte{}
}

func (f *FakeGordon) Connect() error {
	f.lock.Lock()
	defer f.lock.Unlock()
	f.Connected = true
	return f.ConnectError
}

func (f *FakeGordon) Create(properties map[string]string) (*warden.CreateResponse, error) {
	f.lock.Lock()
	defer f.lock.Unlock()
	if f.CreateError != nil {
		return nil, f.CreateError
	}

	handleUuid, _ := uuid.NewV4()
	handle := handleUuid.String()[:11]

	f.createdHandles = append(f.createdHandles, handle)

	f.createdProperties[handle] = properties

	return &warden.CreateResponse{
		Handle: proto.String(handle),
	}, nil
}

func (f *FakeGordon) CreatedHandles() []string {
	f.lock.Lock()
	defer f.lock.Unlock()

	return f.createdHandles
}

func (f *FakeGordon) CreatedProperties(handle string) map[string]string {
	f.lock.Lock()
	defer f.lock.Unlock()

	return f.createdProperties[handle]
}

func (f *FakeGordon) Stop(handle string, background, kill bool) (*warden.StopResponse, error) {
	f.lock.Lock()
	defer f.lock.Unlock()
	if f.StopError != nil {
		return nil, f.StopError
	}

	f.stoppedHandles = append(f.stoppedHandles, handle)

	return &warden.StopResponse{}, nil
}

func (f *FakeGordon) StoppedHandles() []string {
	f.lock.Lock()
	defer f.lock.Unlock()

	return f.stoppedHandles
}

func (f *FakeGordon) Destroy(handle string) (*warden.DestroyResponse, error) {
	f.lock.Lock()
	defer f.lock.Unlock()
	if f.DestroyError != nil {
		return nil, f.DestroyError
	}

	f.destroyedHandles = append(f.destroyedHandles, handle)

	return &warden.DestroyResponse{}, nil
}

func (f *FakeGordon) DestroyedHandles() []string {
	f.lock.Lock()
	defer f.lock.Unlock()

	return f.destroyedHandles
}

func (f *FakeGordon) NetIn(handle string) (*warden.NetInResponse, error) {
	panic("NOOP!")
	return nil, f.NetInError
}

func (f *FakeGordon) MemoryLimits() []Limit {
	f.lock.Lock()
	defer f.lock.Unlock()

	return f.memoryLimits
}

func (f *FakeGordon) SetLimitMemoryError(err error) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.limitMemoryError = err
}

func (f *FakeGordon) LimitMemory(handle string, limit uint64) (*warden.LimitMemoryResponse, error) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.memoryLimits = append(f.memoryLimits, Limit{
		Handle: handle,
		Limit:  limit,
	})

	return nil, f.limitMemoryError
}

func (f *FakeGordon) GetMemoryLimit(handle string) (uint64, error) {
	panic("NOOP!")
	return 0, f.GetMemoryLimitError
}

func (f *FakeGordon) DiskLimits() []DiskLimit {
	f.lock.Lock()
	defer f.lock.Unlock()

	return f.diskLimits
}

func (f *FakeGordon) SetLimitDiskError(err error) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.limitDiskError = err
}

func (f *FakeGordon) LimitDisk(handle string, limits gordon.DiskLimits) (*warden.LimitDiskResponse, error) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.diskLimits = append(f.diskLimits, DiskLimit{
		Handle: handle,
		Limits: limits,
	})

	return nil, f.limitDiskError
}

func (f *FakeGordon) GetDiskLimit(handle string) (uint64, error) {
	panic("NOOP!")
	return 0, f.GetDiskLimitError
}

func (f *FakeGordon) List(filterProperties map[string]string) (*warden.ListResponse, error) {
	f.lock.RLock()
	callback := f.listCallback
	f.lock.RUnlock()

	if callback != nil {
		return callback(filterProperties)
	}

	return &warden.ListResponse{}, nil
}

func (f *FakeGordon) Info(handle string) (*warden.InfoResponse, error) {
	f.lock.Lock()
	defer f.lock.Unlock()

	return f.infoResponse, f.infoError
}

func (f *FakeGordon) SetInfoError(err error) {
	f.lock.Lock()
	defer f.lock.Unlock()
	f.infoError = err
}

func (f *FakeGordon) SetInfoResponse(response *warden.InfoResponse) {
	f.lock.Lock()
	defer f.lock.Unlock()
	f.infoResponse = response
}

func (f *FakeGordon) CopyIn(handle, src, dst string) (*warden.CopyInResponse, error) {
	f.lock.RLock()
	err := f.copyInError
	f.lock.RUnlock()

	if err != nil {
		return nil, err
	}

	copiedIn := CopiedIn{
		Handle: handle,
		Src:    src,
		Dst:    dst,
	}

	f.lock.Lock()

	f.copiedIn = append(f.copiedIn, &copiedIn)

	f.lock.Unlock()

	for ci, cb := range f.copyInCallbacks {
		if (ci.Handle == "" || ci.Handle == handle) && ci.Src == src {
			err := cb(copiedIn)
			if err != nil {
				return nil, err
			}
		}
	}

	return &warden.CopyInResponse{}, nil
}

func (f *FakeGordon) ThingsCopiedIn() []*CopiedIn {
	f.lock.Lock()
	defer f.lock.Unlock()

	return f.copiedIn
}

func (f *FakeGordon) SetCopyInErr(err error) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.copyInError = err
}

func (f *FakeGordon) CopyOut(handle, src, dst, owner string) (*warden.CopyOutResponse, error) {
	f.lock.RLock()
	err := f.copyOutError
	f.lock.RUnlock()

	if err != nil {
		return nil, err
	}

	copiedOut := CopiedOut{
		Handle: handle,
		Src:    src,
		Dst:    dst,
		Owner:  owner,
	}

	f.lock.Lock()

	f.copiedOut = append(f.copiedOut, &copiedOut)

	if len(f.fileContentToProvideOnCopyOut) > 0 {
		err := ioutil.WriteFile(dst, f.fileContentToProvideOnCopyOut, os.ModePerm)
		if err != nil {
			panic(err)
		}
	}

	f.lock.Unlock()

	for co, cb := range f.copyOutCallbacks {
		if (co.Handle == "" || co.Handle == handle) && co.Src == src {
			err := cb(copiedOut)
			if err != nil {
				return nil, err
			}
		}
	}

	return &warden.CopyOutResponse{}, nil
}

func (f *FakeGordon) ThingsCopiedOut() []*CopiedOut {
	f.lock.Lock()
	defer f.lock.Unlock()

	return f.copiedOut
}

func (f *FakeGordon) SetCopyOutErr(err error) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.copyOutError = err
}

func (f *FakeGordon) SetCopyOutFileContent(data []byte) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.fileContentToProvideOnCopyOut = data
}

func (f *FakeGordon) Attach(handle string, jobID uint32) (<-chan *warden.ProcessPayload, error) {
	panic("NOOP!")
	return nil, f.AttachError
}

func (f *FakeGordon) ScriptsThatRan() []*RunningScript {
	f.lock.Lock()
	defer f.lock.Unlock()
	return f.scriptsThatRan
}

func (f *FakeGordon) SetRunReturnValues(processID uint32, processPayloadChan <-chan *warden.ProcessPayload, err error) {
	f.lock.Lock()
	defer f.lock.Unlock()
	f.runReturnProcessID = processID
	f.runReturnProcessPayloadChan = processPayloadChan
	f.runReturnError = err
}

func (f *FakeGordon) WhenRunning(handle string, script string, resourceLimits gordon.ResourceLimits, callback RunCallback) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.runCallbacks[&RunningScript{handle, script, resourceLimits}] = callback
}

func (f *FakeGordon) WhenListing(callback ListCallback) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.listCallback = callback
}

func (f *FakeGordon) WhenCopyingOut(copiedOut CopiedOut, callback CopyOutCallback) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.copyOutCallbacks[&copiedOut] = callback
}

func (f *FakeGordon) WhenCopyingIn(copiedIn CopiedIn, callback CopyInCallback) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.copyInCallbacks[&copiedIn] = callback
}

func (f *FakeGordon) Run(handle string, script string, resourceLimits gordon.ResourceLimits) (uint32, <-chan *warden.ProcessPayload, error) {
	f.lock.Lock()

	f.scriptsThatRan = append(f.scriptsThatRan, &RunningScript{
		Handle:         handle,
		Script:         script,
		ResourceLimits: resourceLimits,
	})

	f.lock.Unlock()

	for ro, cb := range f.runCallbacks {
		if (ro.Handle == "" || ro.Handle == handle) && ro.Script == script {
			return cb()
		}
	}

	return f.runReturnProcessID, f.runReturnProcessPayloadChan, f.runReturnError
}
<|endoftext|>