<commit_before>package modes

import (
	"io/ioutil"
	"net/http"
	"testing"

	"github.com/SpectoLabs/hoverfly/core/models"
	. "github.com/onsi/gomega"
)

func Test_ReconstructResponse_ReturnsAResponseWithCorrectStatus(t *testing.T) {
	RegisterTestingT(t)

	req, _ := http.NewRequest("GET", "http://example.com", nil)

	pair := models.RequestResponsePair{
		Response: models.ResponseDetails{
			Status: 404,
		},
	}

	response := ReconstructResponse(req, pair)

	Expect(response.StatusCode).To(Equal(404))
}

func Test_ReconstructResponse_ReturnsAResponseWithBody(t *testing.T) {
	RegisterTestingT(t)

	req, _ := http.NewRequest("GET", "http://example.com", nil)

	pair := models.RequestResponsePair{
		Response: models.ResponseDetails{
			Body: "test body",
		},
	}

	response := ReconstructResponse(req, pair)

	responseBody, err := ioutil.ReadAll(response.Body)
	Expect(err).To(BeNil())

	Expect(string(responseBody)).To(Equal("test body"))
}

func Test_ReconstructResponse_AddsHeadersToResponse(t *testing.T) {
	RegisterTestingT(t)

	req, _ := http.NewRequest("GET", "http://example.com", nil)

	pair := models.RequestResponsePair{}

	headers := make(map[string][]string)
	headers["Header"] = []string{"one"}

	pair.Response.Headers = headers

	response := ReconstructResponse(req, pair)

	Expect(response.Header.Get("Header")).To(Equal(headers["Header"][0]))
}

func Test_ReconstructResponse_AddsMultipleHeaderValuesToResponse(t *testing.T) {
	RegisterTestingT(t)

	req, _ := http.NewRequest("GET", "http://example.com", nil)

	pair := models.RequestResponsePair{}

	headers := make(map[string][]string)
	headers["Header"] = []string{"one", "two", "three"}

	pair.Response.Headers = headers

	response := ReconstructResponse(req, pair)
	values, ok := response.Header["Header"]
	Expect(ok).To(BeTrue())

	Expect(len(values)).To(Equal(3))
	Expect(values[0]).To(Equal("one"))
	Expect(values[1]).To(Equal("two"))
	Expect(values[2]).To(Equal("three"))
}

func Test_ReconstructResponse_CanReturnACompleteHttpResponseWithAllFieldsFilled(t *testing.T) {
	RegisterTestingT(t)

	req, _ := http.NewRequest("GET", "http://example.com", nil)

	pair := models.RequestResponsePair{
		Response: models.ResponseDetails{
			Status: 201,
			Body:   "test body",
		},
	}

	headers := make(map[string][]string)
	headers["Header"] = []string{"header test"}
	headers["Other"] = []string{"header"}
	pair.Response.Headers = headers

	response := ReconstructResponse(req, pair)

	Expect(response.StatusCode).To(Equal(201))

	responseBody, err := ioutil.ReadAll(response.Body)
	Expect(err).To(BeNil())

	Expect(string(responseBody)).To(Equal("test body"))

	Expect(response.Header.Get("Header")).To(Equal(headers["Header"][0]))
	Expect(response.Header.Get("Other")).To(Equal(headers["Other"][0]))
}
<commit_msg>Added some tests for errorResponse()<commit_after>package modes

import (
	"errors"
	"io/ioutil"
	"net/http"
	"testing"

	"github.com/SpectoLabs/hoverfly/core/models"
	. "github.com/onsi/gomega"
)

func Test_ReconstructResponse_ReturnsAResponseWithCorrectStatus(t *testing.T) {
	RegisterTestingT(t)

	req, _ := http.NewRequest("GET", "http://example.com", nil)

	pair := models.RequestResponsePair{
		Response: models.ResponseDetails{
			Status: 404,
		},
	}

	response := ReconstructResponse(req, pair)

	Expect(response.StatusCode).To(Equal(404))
}

func Test_ReconstructResponse_ReturnsAResponseWithBody(t *testing.T) {
	RegisterTestingT(t)

	req, _ := http.NewRequest("GET", "http://example.com", nil)

	pair := models.RequestResponsePair{
		Response: models.ResponseDetails{
			Body: "test body",
		},
	}

	response := ReconstructResponse(req, pair)

	responseBody, err := ioutil.ReadAll(response.Body)
	Expect(err).To(BeNil())

	Expect(string(responseBody)).To(Equal("test body"))
}

func Test_ReconstructResponse_AddsHeadersToResponse(t *testing.T) {
	RegisterTestingT(t)

	req, _ := http.NewRequest("GET", "http://example.com", nil)

	pair := models.RequestResponsePair{}

	headers := make(map[string][]string)
	headers["Header"] = []string{"one"}

	pair.Response.Headers = headers

	response := ReconstructResponse(req, pair)

	Expect(response.Header.Get("Header")).To(Equal(headers["Header"][0]))
}

func Test_ReconstructResponse_AddsMultipleHeaderValuesToResponse(t *testing.T) {
	RegisterTestingT(t)

	req, _ := http.NewRequest("GET", "http://example.com", nil)

	pair := models.RequestResponsePair{}

	headers := make(map[string][]string)
	headers["Header"] = []string{"one", "two", "three"}

	pair.Response.Headers = headers

	response := ReconstructResponse(req, pair)
	values, ok := response.Header["Header"]
	Expect(ok).To(BeTrue())

	Expect(len(values)).To(Equal(3))
	Expect(values[0]).To(Equal("one"))
	Expect(values[1]).To(Equal("two"))
	Expect(values[2]).To(Equal("three"))
}

func Test_ReconstructResponse_CanReturnACompleteHttpResponseWithAllFieldsFilled(t *testing.T) {
	RegisterTestingT(t)

	req, _ := http.NewRequest("GET", "http://example.com", nil)

	pair := models.RequestResponsePair{
		Response: models.ResponseDetails{
			Status: 201,
			Body:   "test body",
		},
	}

	headers := make(map[string][]string)
	headers["Header"] = []string{"header test"}
	headers["Other"] = []string{"header"}
	pair.Response.Headers = headers

	response := ReconstructResponse(req, pair)

	Expect(response.StatusCode).To(Equal(201))

	responseBody, err := ioutil.ReadAll(response.Body)
	Expect(err).To(BeNil())

	Expect(string(responseBody)).To(Equal("test body"))

	Expect(response.Header.Get("Header")).To(Equal(headers["Header"][0]))
	Expect(response.Header.Get("Other")).To(Equal(headers["Other"][0]))
}

func Test_errorResponse_ShouldAlwaysBeABadGatway(t *testing.T) {
	RegisterTestingT(t)

	response := errorResponse(&http.Request{}, errors.New(""), "An error was got")

	Expect(response.StatusCode).To(Equal(http.StatusBadGateway))
}

func Test_errorResponse_ShouldAlwaysIncludeBothMessageAndErrorInResponseBody(t *testing.T) {
	RegisterTestingT(t)

	response := errorResponse(&http.Request{}, errors.New("error doing something"), "This is a test error")

	responseBody, err := ioutil.ReadAll(response.Body)
	Expect(err).To(BeNil())

	Expect(string(responseBody)).To(ContainSubstring("Hoverfly Error!"))
	Expect(string(responseBody)).To(ContainSubstring("This is a test error"))
	Expect(string(responseBody)).To(ContainSubstring("error doing something"))
}
<|endoftext|>


<commit_before>package terraform

import (
	"log"

	"github.com/hashicorp/terraform/configs"
)

// GraphNodeAttachResourceConfig is an interface that must be implemented by nodes
// that want resource configurations attached.
type GraphNodeAttachResourceConfig interface {
	GraphNodeResource

	// Sets the configuration
	AttachResourceConfig(*configs.Resource)
}

// AttachResourceConfigTransformer goes through the graph and attaches
// resource configuration structures to nodes that implement
// GraphNodeAttachManagedResourceConfig or GraphNodeAttachDataResourceConfig.
//
// The attached configuration structures are directly from the configuration.
// If they're going to be modified, a copy should be made.
type AttachResourceConfigTransformer struct {
	Config *configs.Config // Config is the root node in the config tree
}

func (t *AttachResourceConfigTransformer) Transform(g *Graph) error {

	// Go through and find GraphNodeAttachResource
	for _, v := range g.Vertices() {
		// Only care about GraphNodeAttachResource implementations
		arn, ok := v.(GraphNodeAttachResourceConfig)
		if !ok {
			continue
		}

		// Determine what we're looking for
		addr := arn.ResourceAddr()

		// Get the configuration.
		config := t.Config.DescendentForInstance(addr.Module)
		if config == nil {
			log.Printf("[TRACE] AttachResourceConfigTransformer: %s has no configuration available", addr.String())
			continue
		}

		for _, r := range config.Module.ManagedResources {
			rAddr := r.Addr()

			if rAddr != addr.Resource {
				// Not the same resource
				continue
			}

			log.Printf("[TRACE] AttachResourceConfigTransformer: attaching to %s: %#v", addr.String(), r)
			arn.AttachResourceConfig(r)
		}
		for _, r := range config.Module.DataResources {
			rAddr := r.Addr()

			if rAddr != addr.Resource {
				// Not the same resource
				continue
			}

			log.Printf("[TRACE] AttachResourceConfigTransformer: attaching to %s: %#v", addr.String(), r)
			arn.AttachResourceConfig(r)
		}
	}

	return nil
}
<commit_msg>core: Additional trace logging in attach resource config transformer<commit_after>package terraform

import (
	"log"

	"github.com/hashicorp/terraform/configs"
	"github.com/hashicorp/terraform/dag"
)

// GraphNodeAttachResourceConfig is an interface that must be implemented by nodes
// that want resource configurations attached.
type GraphNodeAttachResourceConfig interface {
	GraphNodeResource

	// Sets the configuration
	AttachResourceConfig(*configs.Resource)
}

// AttachResourceConfigTransformer goes through the graph and attaches
// resource configuration structures to nodes that implement
// GraphNodeAttachManagedResourceConfig or GraphNodeAttachDataResourceConfig.
//
// The attached configuration structures are directly from the configuration.
// If they're going to be modified, a copy should be made.
type AttachResourceConfigTransformer struct {
	Config *configs.Config // Config is the root node in the config tree
}

func (t *AttachResourceConfigTransformer) Transform(g *Graph) error {

	// Go through and find GraphNodeAttachResource
	for _, v := range g.Vertices() {
		// Only care about GraphNodeAttachResource implementations
		arn, ok := v.(GraphNodeAttachResourceConfig)
		if !ok {
			continue
		}

		// Determine what we're looking for
		addr := arn.ResourceAddr()

		// Get the configuration.
		config := t.Config.DescendentForInstance(addr.Module)
		if config == nil {
			log.Printf("[TRACE] AttachResourceConfigTransformer: %q (%T) has no configuration available", dag.VertexName(v), v)
			continue
		}

		for _, r := range config.Module.ManagedResources {
			rAddr := r.Addr()

			if rAddr != addr.Resource {
				// Not the same resource
				continue
			}

			log.Printf("[TRACE] AttachResourceConfigTransformer: attaching to %q (%T) config from %s", dag.VertexName(v), v, r.DeclRange)
			arn.AttachResourceConfig(r)
		}
		for _, r := range config.Module.DataResources {
			rAddr := r.Addr()

			if rAddr != addr.Resource {
				// Not the same resource
				continue
			}

			log.Printf("[TRACE] AttachResourceConfigTransformer: attaching to %q (%T) config from %#v", dag.VertexName(v), v, r.DeclRange)
			arn.AttachResourceConfig(r)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package registry

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"path"
	"time"

	"github.com/coreos/coreinit/job"
	"github.com/coreos/coreinit/machine"
	"github.com/coreos/go-etcd/etcd"
)

const (
	keyPrefix      = "/coreos.com/coreinit/"
	lockPrefix     = "/locks/"
	machinePrefix  = "/machines/"
	schedulePrefix = "/schedule/"
	statePrefix    = "/state/"
)

type Registry struct {
	Etcd *etcd.Client
}

func New() (registry *Registry) {
	etcdC := etcd.NewClient(nil)
	registry = &Registry{etcdC}
	return registry
}

// Describe the list of all known Machines
func (r *Registry) GetActiveMachines() map[string]machine.Machine {
	key := path.Join(keyPrefix, machinePrefix)
	resp, err := r.Etcd.Get(key, false)

	// Assume the error was KeyNotFound and return an empty data structure
	if err != nil {
		return make(map[string]machine.Machine, 0)
	}

	machines := make(map[string]machine.Machine, len(resp.Kvs))
	for _, kv := range resp.Kvs {
		_, bootId := path.Split(kv.Key)
		machine := machine.New(bootId)

		// This is a hacky way of telling if a Machine is reporting state
		addrs := r.GetMachineAddrs(machine)
		if len(addrs) > 0 {
			machines[machine.BootId] = *machine
		}
	}

	return machines
}

func (r *Registry) GetMachineAddrs(m *machine.Machine) []machine.IPAddress {
	key := path.Join(keyPrefix, machinePrefix, m.BootId, "addrs")
	resp, err :=r.Etcd.Get(key, false)

	addrs := make([]machine.IPAddress, 0)

	// Assume this is KeyNotFound and return an empty data structure
	if err != nil {
		return addrs
	}

	//TODO: Handle the error generated by unmarshal
	unmarshal(resp.Value, &addrs)

	return addrs
}

func (r *Registry) SetMachineAddrs(machine *machine.Machine, addrs []machine.IPAddress, ttl time.Duration) {
	//TODO: Handle the error generated by marshal
	json, _ := marshal(addrs)
	key := path.Join(keyPrefix, machinePrefix, machine.BootId, "addrs")
	r.Etcd.Set(key, json, uint64(ttl.Seconds()))
}

// Private helper method that takes a path to an Etcd directory and returns
// all of the items as job.Job objects.
func (r *Registry) getJobsAtPath(key string) map[string]job.Job {
	resp, err := r.Etcd.Get(key, false)

	// Assume the error was KeyNotFound and return an empty data structure
	if err != nil {
		return make(map[string]job.Job, 0)
	}

	jobs := make(map[string]job.Job, len(resp.Kvs))
	for _, kv := range resp.Kvs {
		name := path.Base(kv.Key)

		var payload job.JobPayload
		err := unmarshal(kv.Value, &payload)

		if err == nil {
			job := job.NewJob(name, nil, &payload)
			jobs[job.Name] = *job
		} else {
			log.Print(err)
		}
	}

	return jobs
}

// Describe the list of jobs that have not yet been scheduled to a Machine
func (r *Registry) GetGlobalJobs() map[string]job.Job {
	key := path.Join(keyPrefix, schedulePrefix)
	return r.getJobsAtPath(key)
}

// Describe the list of jobs a given Machine is scheduled to run
func (r *Registry) GetMachineJobs(machine *machine.Machine) map[string]job.Job {
	key := path.Join(keyPrefix, machinePrefix, machine.BootId, schedulePrefix)
	return r.getJobsAtPath(key)
}

func (r *Registry) GetJobState(j *job.Job) *job.JobState {
	key := path.Join(keyPrefix, statePrefix, j.Name)
	resp, err := r.Etcd.Get(key, false)

	// Assume the error was KeyNotFound and return an empty data structure
	if err != nil {
		return nil
	}

	var state job.JobState
	//TODO: Handle the error generated by unmarshal
	unmarshal(resp.Value, &state)
	return &state
}

func (r *Registry) ScheduleJob(job *job.Job, machine *machine.Machine) {
	key := path.Join(keyPrefix, machinePrefix, machine.BootId, schedulePrefix, job.Name)
	//TODO: Handle the error generated by unmarshal
	json, _ := marshal(job.Payload)
	r.Etcd.Set(key, json, 0)
}

// StartJob adds a job to the schedule to be started.
func (r *Registry) StartJob(job *job.Job) {
	key := path.Join(keyPrefix, schedulePrefix, job.Name)
	//TODO: Handle the error generated by unmarshal
	json, _ := marshal(job.Payload)
	r.Etcd.Set(key, json, 0)
}

// StopJob removes the job from the global and machine schedule.
func (r *Registry) StopJob(job *job.Job) {
	key := path.Join(keyPrefix, schedulePrefix, job.Name)
	r.Etcd.Delete(key)

	state := r.GetJobState(job)

	if state != nil {
		key := path.Join(keyPrefix, machinePrefix, state.Machine.BootId, schedulePrefix, job.Name)
		r.Etcd.Delete(key)
	}
}

// Persist the changes in a provided Machine's Job to Etcd with the provided TTL
func (r *Registry) UpdateJob(job *job.Job, ttl uint64) {
	key := path.Join(keyPrefix, statePrefix, job.Name)
	//TODO: Handle the error generated by marshal
	json, _ := marshal(job.State)
	r.Etcd.Set(key, json, ttl)
}

// Attempt to acquire a lock in Etcd on an arbitrary string. Returns true if
// successful, otherwise false.
func (r *Registry) AcquireLock(name string, context string, ttl time.Duration) bool {
	key := path.Join(keyPrefix, lockPrefix, name)
	_, err := r.Etcd.Create(key, context, uint64(ttl.Seconds()))
	return err == nil
}

func marshal(obj interface{}) (string, error) {
	encoded, err := json.Marshal(obj)
	if err != nil {
		return "", errors.New("Unable to JSON-serialize object")
	} else {
		return string(encoded), nil
	}
}

func unmarshal(val string, obj interface{}) (error) {
	err := json.Unmarshal([]byte(val), &obj)
	if err == nil {
		return nil
	} else {
		return errors.New("Unable to JSON-deserialize object")
	}
}
<commit_msg>refactor(registry): Expand json serialization error reporting<commit_after>package registry

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"path"
	"time"

	"github.com/coreos/coreinit/job"
	"github.com/coreos/coreinit/machine"
	"github.com/coreos/go-etcd/etcd"
)

const (
	keyPrefix      = "/coreos.com/coreinit/"
	lockPrefix     = "/locks/"
	machinePrefix  = "/machines/"
	schedulePrefix = "/schedule/"
	statePrefix    = "/state/"
)

type Registry struct {
	Etcd *etcd.Client
}

func New() (registry *Registry) {
	etcdC := etcd.NewClient(nil)
	registry = &Registry{etcdC}
	return registry
}

// Describe the list of all known Machines
func (r *Registry) GetActiveMachines() map[string]machine.Machine {
	key := path.Join(keyPrefix, machinePrefix)
	resp, err := r.Etcd.Get(key, false)

	// Assume the error was KeyNotFound and return an empty data structure
	if err != nil {
		return make(map[string]machine.Machine, 0)
	}

	machines := make(map[string]machine.Machine, len(resp.Kvs))
	for _, kv := range resp.Kvs {
		_, bootId := path.Split(kv.Key)
		machine := machine.New(bootId)

		// This is a hacky way of telling if a Machine is reporting state
		addrs := r.GetMachineAddrs(machine)
		if len(addrs) > 0 {
			machines[machine.BootId] = *machine
		}
	}

	return machines
}

func (r *Registry) GetMachineAddrs(m *machine.Machine) []machine.IPAddress {
	key := path.Join(keyPrefix, machinePrefix, m.BootId, "addrs")
	resp, err :=r.Etcd.Get(key, false)

	addrs := make([]machine.IPAddress, 0)

	// Assume this is KeyNotFound and return an empty data structure
	if err != nil {
		return addrs
	}

	//TODO: Handle the error generated by unmarshal
	unmarshal(resp.Value, &addrs)

	return addrs
}

func (r *Registry) SetMachineAddrs(machine *machine.Machine, addrs []machine.IPAddress, ttl time.Duration) {
	//TODO: Handle the error generated by marshal
	json, _ := marshal(addrs)
	key := path.Join(keyPrefix, machinePrefix, machine.BootId, "addrs")
	r.Etcd.Set(key, json, uint64(ttl.Seconds()))
}

// Private helper method that takes a path to an Etcd directory and returns
// all of the items as job.Job objects.
func (r *Registry) getJobsAtPath(key string) map[string]job.Job {
	resp, err := r.Etcd.Get(key, false)

	// Assume the error was KeyNotFound and return an empty data structure
	if err != nil {
		return make(map[string]job.Job, 0)
	}

	jobs := make(map[string]job.Job, len(resp.Kvs))
	for _, kv := range resp.Kvs {
		name := path.Base(kv.Key)

		var payload job.JobPayload
		err := unmarshal(kv.Value, &payload)

		if err == nil {
			job := job.NewJob(name, nil, &payload)
			jobs[job.Name] = *job
		} else {
			log.Print(err)
		}
	}

	return jobs
}

// Describe the list of jobs that have not yet been scheduled to a Machine
func (r *Registry) GetGlobalJobs() map[string]job.Job {
	key := path.Join(keyPrefix, schedulePrefix)
	return r.getJobsAtPath(key)
}

// Describe the list of jobs a given Machine is scheduled to run
func (r *Registry) GetMachineJobs(machine *machine.Machine) map[string]job.Job {
	key := path.Join(keyPrefix, machinePrefix, machine.BootId, schedulePrefix)
	return r.getJobsAtPath(key)
}

func (r *Registry) GetJobState(j *job.Job) *job.JobState {
	key := path.Join(keyPrefix, statePrefix, j.Name)
	resp, err := r.Etcd.Get(key, false)

	// Assume the error was KeyNotFound and return an empty data structure
	if err != nil {
		return nil
	}

	var state job.JobState
	//TODO: Handle the error generated by unmarshal
	unmarshal(resp.Value, &state)
	return &state
}

func (r *Registry) ScheduleJob(job *job.Job, machine *machine.Machine) {
	key := path.Join(keyPrefix, machinePrefix, machine.BootId, schedulePrefix, job.Name)
	//TODO: Handle the error generated by unmarshal
	json, _ := marshal(job.Payload)
	r.Etcd.Set(key, json, 0)
}

// StartJob adds a job to the schedule to be started.
func (r *Registry) StartJob(job *job.Job) {
	key := path.Join(keyPrefix, schedulePrefix, job.Name)
	//TODO: Handle the error generated by unmarshal
	json, _ := marshal(job.Payload)
	r.Etcd.Set(key, json, 0)
}

// StopJob removes the job from the global and machine schedule.
func (r *Registry) StopJob(job *job.Job) {
	key := path.Join(keyPrefix, schedulePrefix, job.Name)
	r.Etcd.Delete(key)

	state := r.GetJobState(job)

	if state != nil {
		key := path.Join(keyPrefix, machinePrefix, state.Machine.BootId, schedulePrefix, job.Name)
		r.Etcd.Delete(key)
	}
}

// Persist the changes in a provided Machine's Job to Etcd with the provided TTL
func (r *Registry) UpdateJob(job *job.Job, ttl uint64) {
	key := path.Join(keyPrefix, statePrefix, job.Name)
	//TODO: Handle the error generated by marshal
	json, _ := marshal(job.State)
	r.Etcd.Set(key, json, ttl)
}

// Attempt to acquire a lock in Etcd on an arbitrary string. Returns true if
// successful, otherwise false.
func (r *Registry) AcquireLock(name string, context string, ttl time.Duration) bool {
	key := path.Join(keyPrefix, lockPrefix, name)
	_, err := r.Etcd.Create(key, context, uint64(ttl.Seconds()))
	return err == nil
}

func marshal(obj interface{}) (string, error) {
	encoded, err := json.Marshal(obj)
	if err == nil {
		return string(encoded), nil
	} else {
		return "", errors.New(fmt.Sprintf("Unable to JSON-serialize object: %s", err))
	}
}

func unmarshal(val string, obj interface{}) (error) {
	err := json.Unmarshal([]byte(val), &obj)
	if err == nil {
		return nil
	} else {
		return errors.New(fmt.Sprintf("Unable to JSON-deserialize object: %s", err))
	}
}
<|endoftext|>


<commit_before>package apptail

import (
	"encoding/json"
	"fmt"
	"github.com/ActiveState/log"
	"github.com/ActiveState/tail"
	"logyard"
	"logyard/util/pubsub"
	"path/filepath"
	"unicode/utf8"
)

// AppInstance is the NATS message sent by dea/stager to notify of new
// instances.
type AppInstance struct {
	AppID    int
	AppName  string
	AppGroup string `json:"group"`
	Type     string
	Index    int
	LogFiles []string
}

// AppLogMessage is a struct corresponding to an entry in the app log stream.
type AppLogMessage struct {
	Text          string
	LogFilename   string
	UnixTime      int64
	HumanTime     string
	Source        string // example: app, staging, stackato.dea, stackato.stager
	InstanceIndex int
	AppID         int
	AppName       string
	AppGroup      string
	NodeID        string // Host (DEA,stager) IP of this app instance
}

// Publish publishes an AppLogMessage to logyard after sanity checks.
func (line *AppLogMessage) Publish(pub *pubsub.Publisher, allowInvalidJson bool) error {
	// JSON must be a UTF-8 encoded string.
	if !utf8.ValidString(line.Text) {
		line.Text = string([]rune(line.Text))
	}

	data, err := json.Marshal(line)
	if err != nil {
		if allowInvalidJson {
			log.Errorf("Cannot encode %+v into JSON -- %s. Skipping this message", line, err)
		} else {
			return fmt.Errorf("Failed to encode app log record to JSON: ", err)
		}
	}
	key := fmt.Sprintf("apptail.%d", line.AppID)
	pub.MustPublish(key, string(data))
	return nil
}

// AppInstanceStarted is a function to be invoked when dea/stager
// starts an application instance.
func AppInstanceStarted(instance *AppInstance, nodeid string) {
	log.Infof("New app instance was started: %+v", instance)

	for _, filename := range instance.LogFiles {
		go func(filename string) {
			pub := logyard.Broker.NewPublisherMust()
			defer pub.Stop()

			tail, err := tail.TailFile(filename, tail.Config{
				MaxLineSize: GetConfig().MaxRecordSize,
				MustExist:   true,
				Follow:      true,
				Location:    -1,
				ReOpen:      false,
				Poll:        true})
			if err != nil {
				log.Errorf("Cannot tail file (%s); %s", filename, err)
				return
			}
			for line := range tail.Lines {
				// JSON must be a valid UTF-8 string
				if !utf8.ValidString(line.Text) {
					line.Text = string([]rune(line.Text))
				}
				err := (&AppLogMessage{
					Text:          line.Text,
					LogFilename:   filepath.Base(filename),
					UnixTime:      line.Time.Unix(),
					HumanTime:     ToHerokuTime(line.Time),
					Source:        instance.Type,
					InstanceIndex: instance.Index,
					AppID:         instance.AppID,
					AppName:       instance.AppName,
					AppGroup:      instance.AppGroup,
					NodeID:        nodeid,
				}).Publish(pub, false)
				if err != nil {
					log.Fatal(err)
				}
			}
			err = tail.Wait()
			if err != nil {
				log.Error(err)
			}
		}(filename)
	}
}
<commit_msg>accept arbitrary number of log files from the DEA (cherry picked from commit ac172f7ac826c71976520433dc21e5b5e73a8b31)<commit_after>package apptail

import (
	"encoding/json"
	"fmt"
	"github.com/ActiveState/log"
	"github.com/ActiveState/tail"
	"logyard"
	"logyard/util/pubsub"
	"unicode/utf8"
)

// AppInstance is the NATS message sent by dea/stager to notify of new
// instances.
type AppInstance struct {
	AppID    int
	AppName  string
	AppGroup string `json:"group"`
	Type     string
	Index    int
	LogFiles map[string]string
}

// AppLogMessage is a struct corresponding to an entry in the app log stream.
type AppLogMessage struct {
	Text          string
	LogFilename   string
	UnixTime      int64
	HumanTime     string
	Source        string // example: app, staging, stackato.dea, stackato.stager
	InstanceIndex int
	AppID         int
	AppName       string
	AppGroup      string
	NodeID        string // Host (DEA,stager) IP of this app instance
}

// Publish publishes an AppLogMessage to logyard after sanity checks.
func (line *AppLogMessage) Publish(pub *pubsub.Publisher, allowInvalidJson bool) error {
	// JSON must be a UTF-8 encoded string.
	if !utf8.ValidString(line.Text) {
		line.Text = string([]rune(line.Text))
	}

	data, err := json.Marshal(line)
	if err != nil {
		if allowInvalidJson {
			log.Errorf("Cannot encode %+v into JSON -- %s. Skipping this message", line, err)
		} else {
			return fmt.Errorf("Failed to encode app log record to JSON: ", err)
		}
	}
	key := fmt.Sprintf("apptail.%d", line.AppID)
	pub.MustPublish(key, string(data))
	return nil
}

// AppInstanceStarted is a function to be invoked when dea/stager
// starts an application instance.
func AppInstanceStarted(instance *AppInstance, nodeid string) {
	log.Infof("New app instance was started: %+v", instance)

	for name, filename := range instance.LogFiles {
		go func(filename string) {
			pub := logyard.Broker.NewPublisherMust()
			defer pub.Stop()

			tail, err := tail.TailFile(filename, tail.Config{
				MaxLineSize: GetConfig().MaxRecordSize,
				MustExist:   true,
				Follow:      true,
				Location:    -1,
				ReOpen:      false,
				Poll:        true})
			if err != nil {
				log.Errorf("Cannot tail file (%s); %s", filename, err)
				return
			}
			for line := range tail.Lines {
				// JSON must be a valid UTF-8 string
				if !utf8.ValidString(line.Text) {
					line.Text = string([]rune(line.Text))
				}
				err := (&AppLogMessage{
					Text:          line.Text,
					LogFilename:   name,
					UnixTime:      line.Time.Unix(),
					HumanTime:     ToHerokuTime(line.Time),
					Source:        instance.Type,
					InstanceIndex: instance.Index,
					AppID:         instance.AppID,
					AppName:       instance.AppName,
					AppGroup:      instance.AppGroup,
					NodeID:        nodeid,
				}).Publish(pub, false)
				if err != nil {
					log.Fatal(err)
				}
			}
			err = tail.Wait()
			if err != nil {
				log.Error(err)
			}
		}(filename)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Eleme Inc. All rights reserved.

package metricdb

import (
	"github.com/eleme/banshee/models"
	"github.com/eleme/banshee/util"
	"github.com/eleme/banshee/util/assert"
	"os"
	"reflect"
	"testing"
)

func TestOpen(t *testing.T) {
	fileName := "db-testing"
	db, err := Open(fileName)
	assert.Ok(t, err == nil)
	assert.Ok(t, util.IsFileExist(fileName))
	db.Close()
	os.RemoveAll(fileName)
}

func TestPut(t *testing.T) {
	// Open db.
	fileName := "db-testing"
	db, _ := Open(fileName)
	defer os.RemoveAll(fileName)
	defer db.Close()
	// Put.
	m := &models.Metric{
		Name:    "foo",
		Stamp:   1452758773,
		Value:   3.14,
		Score:   0.1892,
		Average: 3.133,
	}
	err := db.Put(m)
	assert.Ok(t, err == nil)
	// Must in db
	key := encodeKey(m)
	value, err := db.db.Get(key, nil)
	assert.Ok(t, err == nil)
	m1 := &models.Metric{
		Name:  m.Name,
		Stamp: m.Stamp,
	}
	err = decodeValue(value, m1)
	assert.Ok(t, err == nil)
	assert.Ok(t, reflect.DeepEqual(m, m1))
}

func TestGet(t *testing.T) {
	// Open db.
	fileName := "db-testing"
	db, _ := Open(fileName)
	defer os.RemoveAll(fileName)
	defer db.Close()
	// Nothing.
	ms, err := db.Get("foo", 0, 1452758773)
	assert.Ok(t, err == nil)
	assert.Ok(t, len(ms) == 0)
	// Put some.
	db.Put(&models.Metric{Name: "foo", Stamp: 1452758723})
	db.Put(&models.Metric{Name: "foo", Stamp: 1452758733, Value: 1.89, Score: 1.12, Average: 1.72})
	db.Put(&models.Metric{Name: "foo", Stamp: 1452758743})
	db.Put(&models.Metric{Name: "foo", Stamp: 1452758753})
	// Get again.
	ms, err = db.Get("foo", 1452758733, 1452758753)
	assert.Ok(t, err == nil)
	assert.Ok(t, len(ms) == 2)
	// Test the value.
	m := ms[0]
	assert.Ok(t, m.Value == 1.89 && m.Score == 1.12)
}

func TestDelete(t *testing.T) {
	// Open db.
	fileName := "db-testing"
	db, _ := Open(fileName)
	defer os.RemoveAll(fileName)
	defer db.Close()
	// Nothing.
	err := db.Delete("foo", 0, 1452758773)
	assert.Ok(t, err == nil)
	// Put some.
	db.Put(&models.Metric{Name: "foo", Stamp: 1452758723})
	db.Put(&models.Metric{Name: "foo", Stamp: 1452758733})
	db.Put(&models.Metric{Name: "foo", Stamp: 1452758743})
	db.Put(&models.Metric{Name: "foo", Stamp: 1452758753})
	// Delete again.
	err = db.Delete("foo", 1452758733, 1452758753)
	assert.Ok(t, err == nil)
	// Get
	ms, err := db.Get("foo", 1452758733, 1452758753)
	assert.Ok(t, len(ms) == 0)
}
<commit_msg>Test metricdb delete with fuller cases<commit_after>// Copyright 2015 Eleme Inc. All rights reserved.

package metricdb

import (
	"github.com/eleme/banshee/models"
	"github.com/eleme/banshee/util"
	"github.com/eleme/banshee/util/assert"
	"os"
	"reflect"
	"testing"
)

func TestOpen(t *testing.T) {
	fileName := "db-testing"
	db, err := Open(fileName)
	assert.Ok(t, err == nil)
	assert.Ok(t, util.IsFileExist(fileName))
	db.Close()
	os.RemoveAll(fileName)
}

func TestPut(t *testing.T) {
	// Open db.
	fileName := "db-testing"
	db, _ := Open(fileName)
	defer os.RemoveAll(fileName)
	defer db.Close()
	// Put.
	m := &models.Metric{
		Name:    "foo",
		Stamp:   1452758773,
		Value:   3.14,
		Score:   0.1892,
		Average: 3.133,
	}
	err := db.Put(m)
	assert.Ok(t, err == nil)
	// Must in db
	key := encodeKey(m)
	value, err := db.db.Get(key, nil)
	assert.Ok(t, err == nil)
	m1 := &models.Metric{
		Name:  m.Name,
		Stamp: m.Stamp,
	}
	err = decodeValue(value, m1)
	assert.Ok(t, err == nil)
	assert.Ok(t, reflect.DeepEqual(m, m1))
}

func TestGet(t *testing.T) {
	// Open db.
	fileName := "db-testing"
	db, _ := Open(fileName)
	defer os.RemoveAll(fileName)
	defer db.Close()
	// Nothing.
	ms, err := db.Get("foo", 0, 1452758773)
	assert.Ok(t, err == nil)
	assert.Ok(t, len(ms) == 0)
	// Put some.
	db.Put(&models.Metric{Name: "foo", Stamp: 1452758723})
	db.Put(&models.Metric{Name: "foo", Stamp: 1452758733, Value: 1.89, Score: 1.12, Average: 1.72})
	db.Put(&models.Metric{Name: "foo", Stamp: 1452758743})
	db.Put(&models.Metric{Name: "foo", Stamp: 1452758753})
	// Get again.
	ms, err = db.Get("foo", 1452758733, 1452758753)
	assert.Ok(t, err == nil)
	assert.Ok(t, len(ms) == 2)
	// Test the value.
	m := ms[0]
	assert.Ok(t, m.Value == 1.89 && m.Score == 1.12)
}

func TestDelete(t *testing.T) {
	// Open db.
	fileName := "db-testing"
	db, _ := Open(fileName)
	defer os.RemoveAll(fileName)
	defer db.Close()
	// Nothing.
	err := db.Delete("foo", 0, 1452758773)
	assert.Ok(t, err == nil)
	// Put some.
	db.Put(&models.Metric{Name: "foo", Stamp: 1452758723})
	db.Put(&models.Metric{Name: "foo", Stamp: 1452758733})
	db.Put(&models.Metric{Name: "foo", Stamp: 1452758743})
	db.Put(&models.Metric{Name: "foo", Stamp: 1452758753})
	// Delete again
	err = db.Delete("foo", 1452758733, 1452758753)
	assert.Ok(t, err == nil)
	// Get
	ms, err := db.Get("foo", 1452758723, 1452758763)
	assert.Ok(t, len(ms) == 2)
	assert.Ok(t, ms[0].Stamp == 1452758723)
	assert.Ok(t, ms[1].Stamp == 1452758753)
}
<|endoftext|>


<commit_before>/*
Package kiiroo manages the Kiiroo haptics protocol.

Kiiroo protocol is really wonkey so don't expect anything magical from it.

Protocol format is:

	{<time>:<value>,<time>:<value>,...}

	time : x.xx event time in sec
	value: 0-4 position/intensity of event (but not for the Launch)

Using the Kirroo protocol, the positions sends to the Launch are always 5% and
95% (alternating.) Commands send to the Launch are interrupted/canceled by new
ones, giving only the illusion of precision.

The value is used to determine if the Launch should move. The rate at which
events are received determines the speed parameter of the movement command
issued to the Launch.

Speed algorithm

Move with "default" speed of 50% when it has been >=2sec since last event.

Move at the "slowest" speed of 20% when is has been >=1sec since last event.

Raw speed value between events is 100 - (hundredth sec + 10%) as %. Eg:

	0.20 sec = 100-22 = 78%
	0.50 sec = 100-55 = 45%

If raw speed is bigger then previous speed, then increase speed with 1/6 of the
difference. Eg:

	Previous 50 and Raw 78 = 50+78/6 = 63%

If raw speed is smaller then previous speed, then decrease speed with 1/2 of
the difference. Eg:

	Previous 63 and Raw 45 = 63-45/2 = 41%

Speed can not go below 20%.

Movement algorithm

Stop moving if no signal has been received in 150ms.

Move only when value is different from previous event.

When events are coming in faster then 150ms, send command at 150ms intervals
using last calculated speed. Reset the limiter when the receive window >150ms.

The speed is still calculated when the limiter is active, but the last send
speed is used until the limiter is stopped.

*/
package kiiroo
<commit_msg>kiiroo-format: Fix wording in documentation<commit_after>/*
Package kiiroo manages the Kiiroo haptics protocol.

Kiiroo protocol is really wonkey so don't expect anything magical from it.

Protocol format is:

	{<time>:<value>,<time>:<value>,...}

	time : x.xx event time in sec
	value: 0-4 position/intensity of event (but not for the Launch)

Using the Kirroo protocol, the positions sends to the Launch are always 5% and
95% (alternating.) Commands send to the Launch are interrupted/canceled by new
ones, giving only the illusion of precision.

The value is used to determine if the Launch should move. The rate at which
events are received determines the speed parameter of the movement command
issued to the Launch.

Speed algorithm

Move with "default" speed of 50% when it has been >=2sec since last event.

Move at the "slowest" speed of 20% when is has been >=1sec since last event.

Raw speed value between events is 100 - (hundredth sec + 10%) as %. Eg:

	0.20 sec = 100-22 = 78%
	0.50 sec = 100-55 = 45%

If raw speed is bigger then previous speed, then increase speed with 1/6 of the
raw speed. Eg:

	Previous 50 and Raw 78 = 50+78/6 = 63%

If raw speed is smaller then previous speed, then decrease speed with 1/2 of
the raw speed. Eg:

	Previous 63 and Raw 45 = 63-45/2 = 41%

Speed can not go below 20%.

Movement algorithm

Stop moving if no signal has been received in 150ms.

Move only when value is different from previous event.

When events are coming in faster then 150ms, send command at 150ms intervals
using last calculated speed. Reset the limiter when the receive window >150ms.

The speed is still calculated when the limiter is active, but the last send
speed is used until the limiter is stopped.

*/
package kiiroo
<|endoftext|>


<commit_before>package local

import "os/exec"

type container struct {
	name string
}

// create creates a lxc container with ubuntu template by default.
func (c *container) create() error {
	cmd := exec.Command("sudo", "lxc-create", "-t", "ubuntu", "-n", c.name)
	return cmd.Run()
}

// start starts a lxc container.
func (c *container) start() error {
	cmd := exec.Command("sudo", "lxc-start", "--daemon", "-n", c.name)
	return cmd.Run()
}
<commit_msg>provision/local: added docs for container.<commit_after>package local

import "os/exec"

// container represents an lxc container with the given name.
type container struct {
	name string
}

// create creates a lxc container with ubuntu template by default.
func (c *container) create() error {
	cmd := exec.Command("sudo", "lxc-create", "-t", "ubuntu", "-n", c.name)
	return cmd.Run()
}

// start starts a lxc container.
func (c *container) start() error {
	cmd := exec.Command("sudo", "lxc-start", "--daemon", "-n", c.name)
	return cmd.Run()
}
<|endoftext|>


<commit_before>package local

import (
	"github.com/globocom/tsuru/log"
	"os/exec"
)

// container represents an lxc container with the given name.
type container struct {
	name string
}

// runCmd executes commands and log the given stdout and stderror.
func runCmd(cmd string, args ...string) error {
	command := exec.Command(cmd, args...)
	out, err := command.CombinedOutput()
	log.Printf("running the cmd: %s with the args: %s", cmd, args)
	log.Print(out)
	return err
}

// create creates a lxc container with ubuntu template by default.
func (c *container) create() error {
	return runCmd("sudo", "lxc-create", "-t", "ubuntu", "-n", c.name)
}

// start starts a lxc container.
func (c *container) start() error {
	return runCmd("sudo", "lxc-start", "--daemon", "-n", c.name)
}

// stop stops a lxc container.
func (c *container) stop() error {
	return runCmd("sudo", "lxc-stop", "-n", c.name)
}

// destroy destory a lxc container.
func (c *container) destroy() error {
	return runCmd("sudo", "lxc-destroy", "-n", c.name)
}
<commit_msg>provision/local: converting bytes do string.<commit_after>package local

import (
	"github.com/globocom/tsuru/log"
	"os/exec"
)

// container represents an lxc container with the given name.
type container struct {
	name string
}

// runCmd executes commands and log the given stdout and stderror.
func runCmd(cmd string, args ...string) error {
	command := exec.Command(cmd, args...)
	out, err := command.CombinedOutput()
	log.Printf("running the cmd: %s with the args: %s", cmd, args)
	log.Print(string(out))
	return err
}

// create creates a lxc container with ubuntu template by default.
func (c *container) create() error {
	return runCmd("sudo", "lxc-create", "-t", "ubuntu", "-n", c.name)
}

// start starts a lxc container.
func (c *container) start() error {
	return runCmd("sudo", "lxc-start", "--daemon", "-n", c.name)
}

// stop stops a lxc container.
func (c *container) stop() error {
	return runCmd("sudo", "lxc-stop", "-n", c.name)
}

// destroy destory a lxc container.
func (c *container) destroy() error {
	return runCmd("sudo", "lxc-destroy", "-n", c.name)
}
<|endoftext|>


<commit_before>// Copyright 2014 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package provision provides interfaces that need to be satisfied in order to
// implement a new provisioner on tsuru.
package provision

import (
	"fmt"
	"github.com/globocom/tsuru/action"
	"github.com/globocom/tsuru/app/bind"
	"github.com/globocom/tsuru/cmd"
	"io"
)

type Status string

func (s Status) String() string {
	return string(s)
}

const (
	// building - is while the unit is been provisioned,
	// it occurs during a deploy.
	StatusBuilding = Status("building")
	// error - when an error occurs caused by the application code.
	StatusError = Status("error")
	// is when an error occurs caused by tsuru internal problems.
	StatusDown = Status("down")
	// is when the app process is up but it is not binded to the
	// right host ("0.0.0.0") and right port ($PORT).
	// If your process is a worker its state will be unreachable.
	StatusUnreachable = Status("unreachable")
	// Is when the app process is up and binded to the right
	// host ("0.0.0.0") and right port ($PORT).
	StatusStarted = Status("started")
	// stopped - is when the Docker container is stopped
	StatusStopped = Status("stopped")
)

// Unit represents a provision unit. Can be a machine, container or anything
// IP-addressable.
type Unit struct {
	Name       string
	AppName    string
	Type       string
	InstanceId string
	Machine    int
	Ip         string
	Status     Status
}

// Named is something that has a name, providing the GetName method.
type Named interface {
	GetName() string
}

// AppUnit represents a unit in an app.
type AppUnit interface {
	Named
	GetMachine() int
	GetStatus() Status
	GetIp() string
	GetInstanceId() string
	Available() bool
}

// App represents a tsuru app.
//
// It contains only relevant information for provisioning.
type App interface {
	Named
	// Log should be used to log messages in the app.
	Log(message, source string) error

	// GetPlatform returns the platform (type) of the app. It is equivalent
	// to the Unit `Type` field.
	GetPlatform() string

	// GetDeploy returns the deploys that an app has.
	GetDeploys() uint

	ProvisionedUnits() []AppUnit
	RemoveUnit(id string) error

	// Run executes the command in app units. Commands executed with this
	// method should have access to environment variables defined in the
	// app.
	Run(cmd string, w io.Writer, once bool) error

	Restart(io.Writer) error

	SerializeEnvVars() error

	Envs() map[string]bind.EnvVar

	// Ready marks the app as ready for deployment.
	Ready() error
}

type CNameManager interface {
	SetCName(app App, cname string) error
	UnsetCName(app App, cname string) error
}

// Provisioner is the basic interface of this package.
//
// Any tsuru provisioner must implement this interface in order to provision
// tsuru apps.
//
// Tsuru comes with a default provisioner: juju. One can add other provisioners
// by satisfying this interface and registering it using the function Register.
type Provisioner interface {
	// Deploy updates the code of the app in units to match the given
	// version, logging progress in the given writer.
	Deploy(app App, version string, w io.Writer) error

	// Provision is called when tsuru is creating the app.
	Provision(App) error

	// Destroy is called when tsuru is destroying the app.
	Destroy(App) error

	// AddUnits adds units to an app. The first parameter is the app, the
	// second is the number of units to add.
	//
	// It returns a slice containing all added units
	AddUnits(App, uint) ([]Unit, error)

	// RemoveUnit removes a unit from the app. It receives the app and the name
	// of the unit to be removed.
	RemoveUnit(App, string) error

	// ExecuteCommand runs a command in all units of the app.
	ExecuteCommand(stdout, stderr io.Writer, app App, cmd string, args ...string) error

	// ExecuteCommandOnce runs a command in one unit of the app.
	ExecuteCommandOnce(stdout, stderr io.Writer, app App, cmd string, args ...string) error

	Restart(App) error
	Stop(App) error

	// Start start the app units.
	Start(App) error

	// CollectStatus returns information about all provisioned units. It's used
	// by tsuru collector when updating the status of apps in the database.
	CollectStatus() ([]Unit, error)

	// Addr returns the address for an app.
	//
	// Tsuru will use this method to get the IP (althought it might not be
	// an actual IP, collector calls it "IP") of the app from the
	// provisioner.
	Addr(App) (string, error)

	// InstallDeps installs the dependencies required for the application
	// to run and writes the log in the received writer.
	InstallDeps(app App, w io.Writer) error

	// Swap change the router between two apps.
	Swap(App, App) error

	// DeployPipeline returns actions that should be executed on deploy.
	DeployPipeline() *action.Pipeline
}

// Commandable is a provisioner that provides commands to extend the tsr
// command line interface.
type Commandable interface {
	Commands() []cmd.Command
}

var provisioners = make(map[string]Provisioner)

// Register registers a new provisioner in the Provisioner registry.
func Register(name string, p Provisioner) {
	provisioners[name] = p
}

// Get gets the named provisioner from the registry.
func Get(name string) (Provisioner, error) {
	p, ok := provisioners[name]
	if !ok {
		return nil, fmt.Errorf("Unknown provisioner: %q.", name)
	}
	return p, nil
}

// Registry returns the list of registered provisioners.
func Registry() []Provisioner {
	registry := make([]Provisioner, 0, len(provisioners))
	for _, p := range provisioners {
		registry = append(registry, p)
	}
	return registry
}

type Error struct {
	Reason string
	Err    error
}

func (e *Error) Error() string {
	var err string
	if e.Err != nil {
		err = e.Err.Error() + ": " + e.Reason
	} else {
		err = e.Reason
	}
	return err
}
<commit_msg>Added GetMemory and GetSwap to interface<commit_after>// Copyright 2014 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package provision provides interfaces that need to be satisfied in order to
// implement a new provisioner on tsuru.
package provision

import (
	"fmt"
	"github.com/globocom/tsuru/action"
	"github.com/globocom/tsuru/app/bind"
	"github.com/globocom/tsuru/cmd"
	"io"
)

type Status string

func (s Status) String() string {
	return string(s)
}

const (
	// building - is while the unit is been provisioned,
	// it occurs during a deploy.
	StatusBuilding = Status("building")
	// error - when an error occurs caused by the application code.
	StatusError = Status("error")
	// is when an error occurs caused by tsuru internal problems.
	StatusDown = Status("down")
	// is when the app process is up but it is not binded to the
	// right host ("0.0.0.0") and right port ($PORT).
	// If your process is a worker its state will be unreachable.
	StatusUnreachable = Status("unreachable")
	// Is when the app process is up and binded to the right
	// host ("0.0.0.0") and right port ($PORT).
	StatusStarted = Status("started")
	// stopped - is when the Docker container is stopped
	StatusStopped = Status("stopped")
)

// Unit represents a provision unit. Can be a machine, container or anything
// IP-addressable.
type Unit struct {
	Name       string
	AppName    string
	Type       string
	InstanceId string
	Machine    int
	Ip         string
	Status     Status
}

// Named is something that has a name, providing the GetName method.
type Named interface {
	GetName() string
}

// AppUnit represents a unit in an app.
type AppUnit interface {
	Named
	GetMachine() int
	GetStatus() Status
	GetIp() string
	GetInstanceId() string
	Available() bool
}

// App represents a tsuru app.
//
// It contains only relevant information for provisioning.
type App interface {
	Named
	// Log should be used to log messages in the app.
	Log(message, source string) error

	// GetPlatform returns the platform (type) of the app. It is equivalent
	// to the Unit `Type` field.
	GetPlatform() string

	// GetDeploy returns the deploys that an app has.
	GetDeploys() uint

	ProvisionedUnits() []AppUnit
	RemoveUnit(id string) error

	// Run executes the command in app units. Commands executed with this
	// method should have access to environment variables defined in the
	// app.
	Run(cmd string, w io.Writer, once bool) error

	Restart(io.Writer) error

	SerializeEnvVars() error

	Envs() map[string]bind.EnvVar

	// Ready marks the app as ready for deployment.
	Ready() error

	GetMemory() int
	GetSwap() int
}

type CNameManager interface {
	SetCName(app App, cname string) error
	UnsetCName(app App, cname string) error
}

// Provisioner is the basic interface of this package.
//
// Any tsuru provisioner must implement this interface in order to provision
// tsuru apps.
//
// Tsuru comes with a default provisioner: juju. One can add other provisioners
// by satisfying this interface and registering it using the function Register.
type Provisioner interface {
	// Deploy updates the code of the app in units to match the given
	// version, logging progress in the given writer.
	Deploy(app App, version string, w io.Writer) error

	// Provision is called when tsuru is creating the app.
	Provision(App) error

	// Destroy is called when tsuru is destroying the app.
	Destroy(App) error

	// AddUnits adds units to an app. The first parameter is the app, the
	// second is the number of units to add.
	//
	// It returns a slice containing all added units
	AddUnits(App, uint) ([]Unit, error)

	// RemoveUnit removes a unit from the app. It receives the app and the name
	// of the unit to be removed.
	RemoveUnit(App, string) error

	// ExecuteCommand runs a command in all units of the app.
	ExecuteCommand(stdout, stderr io.Writer, app App, cmd string, args ...string) error

	// ExecuteCommandOnce runs a command in one unit of the app.
	ExecuteCommandOnce(stdout, stderr io.Writer, app App, cmd string, args ...string) error

	Restart(App) error
	Stop(App) error

	// Start start the app units.
	Start(App) error

	// CollectStatus returns information about all provisioned units. It's used
	// by tsuru collector when updating the status of apps in the database.
	CollectStatus() ([]Unit, error)

	// Addr returns the address for an app.
	//
	// Tsuru will use this method to get the IP (althought it might not be
	// an actual IP, collector calls it "IP") of the app from the
	// provisioner.
	Addr(App) (string, error)

	// InstallDeps installs the dependencies required for the application
	// to run and writes the log in the received writer.
	InstallDeps(app App, w io.Writer) error

	// Swap change the router between two apps.
	Swap(App, App) error

	// DeployPipeline returns actions that should be executed on deploy.
	DeployPipeline() *action.Pipeline
}

// Commandable is a provisioner that provides commands to extend the tsr
// command line interface.
type Commandable interface {
	Commands() []cmd.Command
}

var provisioners = make(map[string]Provisioner)

// Register registers a new provisioner in the Provisioner registry.
func Register(name string, p Provisioner) {
	provisioners[name] = p
}

// Get gets the named provisioner from the registry.
func Get(name string) (Provisioner, error) {
	p, ok := provisioners[name]
	if !ok {
		return nil, fmt.Errorf("Unknown provisioner: %q.", name)
	}
	return p, nil
}

// Registry returns the list of registered provisioners.
func Registry() []Provisioner {
	registry := make([]Provisioner, 0, len(provisioners))
	for _, p := range provisioners {
		registry = append(registry, p)
	}
	return registry
}

type Error struct {
	Reason string
	Err    error
}

func (e *Error) Error() string {
	var err string
	if e.Err != nil {
		err = e.Err.Error() + ": " + e.Reason
	} else {
		err = e.Reason
	}
	return err
}
<|endoftext|>


<commit_before>// Copyright 2016 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package provision provides interfaces that need to be satisfied in order to
// implement a new provisioner on tsuru.
package provision

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

	"github.com/tsuru/tsuru/app/bind"
	"github.com/tsuru/tsuru/quota"
)

var (
	ErrInvalidStatus = errors.New("invalid status")
	ErrEmptyApp      = errors.New("no units for this app")
)

type UnitNotFoundError struct {
	ID string
}

func (e *UnitNotFoundError) Error() string {
	return fmt.Sprintf("unit %q not found", e.ID)
}

type InvalidProcessError struct {
	Msg string
}

func (e InvalidProcessError) Error() string {
	return fmt.Sprintf("process error: %s", e.Msg)
}

// Status represents the status of a unit in tsuru.
type Status string

func (s Status) String() string {
	return string(s)
}

func ParseStatus(status string) (Status, error) {
	switch status {
	case "created":
		return StatusCreated, nil
	case "building":
		return StatusBuilding, nil
	case "error":
		return StatusError, nil
	case "started":
		return StatusStarted, nil
	case "starting":
		return StatusStarting, nil
	case "stopped":
		return StatusStopped, nil
	case "asleep":
		return StatusAsleep, nil
	}
	return Status(""), ErrInvalidStatus
}

//     Flow:
//                                    +----------------------------------------------+
//                                    |                                              |
//                                    |            Start                             |
//     +----------+                   |                      +---------+             |
//     | Building |                   +---------------------+| Stopped |             |
//     +----------+                   |                      +---------+             |
//           ^                        |                           ^                  |
//           |                        |                           |                  |
//      deploy unit                   |                         Stop                 |
//           |                        |                           |                  |
//           +                        v       RegisterUnit        +                  +
//      +---------+  app unit   +----------+  SetUnitStatus  +---------+  Sleep  +--------+
//      | Created | +---------> | Starting | +-------------> | Started |+------->| Asleep |
//      +---------+             +----------+                 +---------+         +--------+
//                                    +                         ^ +
//                                    |                         | |
//                              SetUnitStatus                   | |
//                                    |                         | |
//                                    v                         | |
//                                +-------+     SetUnitStatus   | |
//                                | Error | +-------------------+ |
//                                +-------+ <---------------------+
const (
	// StatusCreated is the initial status of a unit in the database,
	// it should transition shortly to a more specific status
	StatusCreated = Status("created")

	// StatusBuilding is the status for units being provisioned by the
	// provisioner, like in the deployment.
	StatusBuilding = Status("building")

	// StatusError is the status for units that failed to start, because of
	// an application error.
	StatusError = Status("error")

	// StatusStarting is set when the container is started in docker.
	StatusStarting = Status("starting")

	// StatusStarted is for cases where the unit is up and running, and bound
	// to the proper status, it's set by RegisterUnit and SetUnitStatus.
	StatusStarted = Status("started")

	// StatusStopped is for cases where the unit has been stopped.
	StatusStopped = Status("stopped")

	// StatusAsleep is for cases where the unit has been asleep.
	StatusAsleep = Status("asleep")
)

// Unit represents a provision unit. Can be a machine, container or anything
// IP-addressable.
type Unit struct {
	ID          string
	Name        string
	AppName     string
	ProcessName string
	Type        string
	Ip          string
	Status      Status
	Address     *url.URL
}

// GetName returns the name of the unit.
func (u *Unit) GetID() string {
	return u.ID
}

// GetIp returns the Unit.IP.
func (u *Unit) GetIp() string {
	return u.Ip
}

// Available returns true if the unit is available. It will return true
// whenever the unit itself is available, even when the application process is
// not.
func (u *Unit) Available() bool {
	return u.Status == StatusStarted ||
		u.Status == StatusStarting ||
		u.Status == StatusError
}

// Named is something that has a name, providing the GetName method.
type Named interface {
	GetName() string
}

// App represents a tsuru app.
//
// It contains only relevant information for provisioning.
type App interface {
	Named

	BindUnit(*Unit) error
	UnbindUnit(*Unit) error

	// Log should be used to log messages in the app.
	Log(message, source, unit string) error

	// GetPlatform returns the platform (type) of the app. It is equivalent
	// to the Unit `Type` field.
	GetPlatform() string

	// GetDeploy returns the deploys that an app has.
	GetDeploys() uint

	Units() ([]Unit, error)

	// Run executes the command in app units. Commands executed with this
	// method should have access to environment variables defined in the
	// app.
	Run(cmd string, w io.Writer, once bool) error

	Envs() map[string]bind.EnvVar

	GetMemory() int64
	GetSwap() int64
	GetCpuShare() int

	SetUpdatePlatform(bool) error
	GetUpdatePlatform() bool

	GetRouter() (string, error)

	GetPool() string

	GetTeamOwner() string

	GetTeamsName() []string

	GetQuota() quota.Quota
	SetQuotaInUse(int) error

	GetCname() []string

	GetIp() string
}

// CNameManager represents a provisioner that supports cname on applications.
type CNameManager interface {
	SetCName(app App, cname string) error
	UnsetCName(app App, cname string) error
}

// ShellOptions is the set of options that can be used when calling the method
// Shell in the provisioner.
type ShellOptions struct {
	App    App
	Conn   io.ReadWriteCloser
	Width  int
	Height int
	Unit   string
	Term   string
}

// ArchiveDeployer is a provisioner that can deploy archives.
type ArchiveDeployer interface {
	ArchiveDeploy(app App, archiveURL string, w io.Writer) (string, error)
}

// UploadDeployer is a provisioner that can deploy the application from an
// uploaded file.
type UploadDeployer interface {
	UploadDeploy(app App, file io.ReadCloser, build bool, w io.Writer) (string, error)
}

// ImageDeployer is a provisioner that can deploy the application from a
// previously generated image.
type ImageDeployer interface {
	ImageDeploy(app App, image string, w io.Writer) (string, error)
}

// Provisioner is the basic interface of this package.
//
// Any tsuru provisioner must implement this interface in order to provision
// tsuru apps.
type Provisioner interface {
	// Provision is called when tsuru is creating the app.
	Provision(App) error

	// Destroy is called when tsuru is destroying the app.
	Destroy(App) error

	// AddUnits adds units to an app. The first parameter is the app, the
	// second is the number of units to be added.
	//
	// It returns a slice containing all added units
	AddUnits(App, uint, string, io.Writer) ([]Unit, error)

	// RemoveUnits "undoes" AddUnits, removing the given number of units
	// from the app.
	RemoveUnits(App, uint, string, io.Writer) error

	// SetUnitStatus changes the status of a unit.
	SetUnitStatus(Unit, Status) error

	// ExecuteCommand runs a command in all units of the app.
	ExecuteCommand(stdout, stderr io.Writer, app App, cmd string, args ...string) error

	// ExecuteCommandOnce runs a command in one unit of the app.
	ExecuteCommandOnce(stdout, stderr io.Writer, app App, cmd string, args ...string) error

	// Restart restarts the units of the application, with an optional
	// string parameter represeting the name of the process to start. When
	// the process is empty, Restart will restart all units of the
	// application.
	Restart(App, string, io.Writer) error

	// Start starts the units of the application, with an optional string
	// parameter representing the name of the process to start. When the
	// process is empty, Start will start all units of the application.
	Start(App, string) error

	// Stop stops the units of the application, with an optional string
	// parameter representing the name of the process to start. When the
	// process is empty, Stop will stop all units of the application.
	Stop(App, string) error

	// Sleep puts the units of the application to sleep, with an optional string
	// parameter representing the name of the process to sleep. When the
	// process is empty, Sleep will put all units of the application to sleep.
	Sleep(App, string) error

	// Addr returns the address for an app.
	//
	// tsuru will use this method to get the IP (although it might not be
	// an actual IP, collector calls it "IP") of the app from the
	// provisioner.
	Addr(App) (string, error)

	// Swap change the router between two apps.
	Swap(App, App) error

	// Units returns information about units by App.
	Units(App) ([]Unit, error)

	// RoutableUnits returns information about routable units by App.
	RoutableUnits(App) ([]Unit, error)

	// Register a unit after the container has been created or restarted.
	RegisterUnit(Unit, map[string]interface{}) error

	// Open a remote shel in one of the units in the application.
	Shell(ShellOptions) error

	// Returns list of valid image names for app, these can be used for
	// rollback.
	ValidAppImages(string) ([]string, error)

	// Returns the metric backend environs for the app.
	MetricEnvs(App) map[string]string

	// Rollback a deploy
	Rollback(App, string, io.Writer) (string, error)

	FilterAppsByUnitStatus([]App, []string) ([]App, error)
}

type MessageProvisioner interface {
	StartupMessage() (string, error)
}

// InitializableProvisioner is a provisioner that provides an initialization
// method that should be called when the app is started
type InitializableProvisioner interface {
	Initialize() error
}

// Provisioners can implement this interface to optionaly disable logs for a
// given app.
type OptionalLogsProvisioner interface {
	// Checks if logs are enabled for given app.
	LogsEnabled(App) (bool, string, error)
}

// PlatformOptions is the set of options provided to PlatformAdd and
// PlatformUpdate, in the ExtensibleProvisioner.
type PlatformOptions struct {
	Name   string
	Args   map[string]string
	Input  io.Reader
	Output io.Writer
}

// ExtensibleProvisioner is a provisioner where administrators can manage
// platforms (automatically adding, removing and updating platforms).
type ExtensibleProvisioner interface {
	PlatformAdd(PlatformOptions) error
	PlatformUpdate(PlatformOptions) error
	PlatformRemove(name string) error
}

var provisioners = make(map[string]Provisioner)

// Register registers a new provisioner in the Provisioner registry.
func Register(name string, p Provisioner) {
	provisioners[name] = p
}

// Get gets the named provisioner from the registry.
func Get(name string) (Provisioner, error) {
	p, ok := provisioners[name]
	if !ok {
		return nil, fmt.Errorf("unknown provisioner: %q", name)
	}
	return p, nil
}

// Registry returns the list of registered provisioners.
func Registry() []Provisioner {
	registry := make([]Provisioner, 0, len(provisioners))
	for _, p := range provisioners {
		registry = append(registry, p)
	}
	return registry
}

// Error represents a provisioning error. It encapsulates further errors.
type Error struct {
	Reason string
	Err    error
}

// Error is the string representation of a provisioning error.
func (e *Error) Error() string {
	var err string
	if e.Err != nil {
		err = e.Err.Error() + ": " + e.Reason
	} else {
		err = e.Reason
	}
	return err
}

type TsuruYamlRestartHooks struct {
	Before []string
	After  []string
}

type TsuruYamlHooks struct {
	Restart TsuruYamlRestartHooks
	Build   []string
}

type TsuruYamlHealthcheck struct {
	Path            string
	Method          string
	Status          int
	Match           string
	AllowedFailures int `json:"allowed_failures" bson:"allowed_failures"`
}

type TsuruYamlData struct {
	Hooks       TsuruYamlHooks
	Healthcheck TsuruYamlHealthcheck
}
<commit_msg>provision: add GetLock to App interface<commit_after>// Copyright 2016 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package provision provides interfaces that need to be satisfied in order to
// implement a new provisioner on tsuru.
package provision

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/url"

	"github.com/tsuru/tsuru/app/bind"
	"github.com/tsuru/tsuru/quota"
)

var (
	ErrInvalidStatus = errors.New("invalid status")
	ErrEmptyApp      = errors.New("no units for this app")
)

type UnitNotFoundError struct {
	ID string
}

func (e *UnitNotFoundError) Error() string {
	return fmt.Sprintf("unit %q not found", e.ID)
}

type InvalidProcessError struct {
	Msg string
}

func (e InvalidProcessError) Error() string {
	return fmt.Sprintf("process error: %s", e.Msg)
}

// Status represents the status of a unit in tsuru.
type Status string

func (s Status) String() string {
	return string(s)
}

func ParseStatus(status string) (Status, error) {
	switch status {
	case "created":
		return StatusCreated, nil
	case "building":
		return StatusBuilding, nil
	case "error":
		return StatusError, nil
	case "started":
		return StatusStarted, nil
	case "starting":
		return StatusStarting, nil
	case "stopped":
		return StatusStopped, nil
	case "asleep":
		return StatusAsleep, nil
	}
	return Status(""), ErrInvalidStatus
}

//     Flow:
//                                    +----------------------------------------------+
//                                    |                                              |
//                                    |            Start                             |
//     +----------+                   |                      +---------+             |
//     | Building |                   +---------------------+| Stopped |             |
//     +----------+                   |                      +---------+             |
//           ^                        |                           ^                  |
//           |                        |                           |                  |
//      deploy unit                   |                         Stop                 |
//           |                        |                           |                  |
//           +                        v       RegisterUnit        +                  +
//      +---------+  app unit   +----------+  SetUnitStatus  +---------+  Sleep  +--------+
//      | Created | +---------> | Starting | +-------------> | Started |+------->| Asleep |
//      +---------+             +----------+                 +---------+         +--------+
//                                    +                         ^ +
//                                    |                         | |
//                              SetUnitStatus                   | |
//                                    |                         | |
//                                    v                         | |
//                                +-------+     SetUnitStatus   | |
//                                | Error | +-------------------+ |
//                                +-------+ <---------------------+
const (
	// StatusCreated is the initial status of a unit in the database,
	// it should transition shortly to a more specific status
	StatusCreated = Status("created")

	// StatusBuilding is the status for units being provisioned by the
	// provisioner, like in the deployment.
	StatusBuilding = Status("building")

	// StatusError is the status for units that failed to start, because of
	// an application error.
	StatusError = Status("error")

	// StatusStarting is set when the container is started in docker.
	StatusStarting = Status("starting")

	// StatusStarted is for cases where the unit is up and running, and bound
	// to the proper status, it's set by RegisterUnit and SetUnitStatus.
	StatusStarted = Status("started")

	// StatusStopped is for cases where the unit has been stopped.
	StatusStopped = Status("stopped")

	// StatusAsleep is for cases where the unit has been asleep.
	StatusAsleep = Status("asleep")
)

// Unit represents a provision unit. Can be a machine, container or anything
// IP-addressable.
type Unit struct {
	ID          string
	Name        string
	AppName     string
	ProcessName string
	Type        string
	Ip          string
	Status      Status
	Address     *url.URL
}

// GetName returns the name of the unit.
func (u *Unit) GetID() string {
	return u.ID
}

// GetIp returns the Unit.IP.
func (u *Unit) GetIp() string {
	return u.Ip
}

// Available returns true if the unit is available. It will return true
// whenever the unit itself is available, even when the application process is
// not.
func (u *Unit) Available() bool {
	return u.Status == StatusStarted ||
		u.Status == StatusStarting ||
		u.Status == StatusError
}

// Named is something that has a name, providing the GetName method.
type Named interface {
	GetName() string
}

// App represents a tsuru app.
//
// It contains only relevant information for provisioning.
type App interface {
	Named

	BindUnit(*Unit) error
	UnbindUnit(*Unit) error

	// Log should be used to log messages in the app.
	Log(message, source, unit string) error

	// GetPlatform returns the platform (type) of the app. It is equivalent
	// to the Unit `Type` field.
	GetPlatform() string

	// GetDeploy returns the deploys that an app has.
	GetDeploys() uint

	Units() ([]Unit, error)

	// Run executes the command in app units. Commands executed with this
	// method should have access to environment variables defined in the
	// app.
	Run(cmd string, w io.Writer, once bool) error

	Envs() map[string]bind.EnvVar

	GetMemory() int64
	GetSwap() int64
	GetCpuShare() int

	SetUpdatePlatform(bool) error
	GetUpdatePlatform() bool

	GetRouter() (string, error)

	GetPool() string

	GetTeamOwner() string

	GetTeamsName() []string

	GetQuota() quota.Quota
	SetQuotaInUse(int) error

	GetCname() []string

	GetIp() string

	GetLock() json.Marshaler
}

// CNameManager represents a provisioner that supports cname on applications.
type CNameManager interface {
	SetCName(app App, cname string) error
	UnsetCName(app App, cname string) error
}

// ShellOptions is the set of options that can be used when calling the method
// Shell in the provisioner.
type ShellOptions struct {
	App    App
	Conn   io.ReadWriteCloser
	Width  int
	Height int
	Unit   string
	Term   string
}

// ArchiveDeployer is a provisioner that can deploy archives.
type ArchiveDeployer interface {
	ArchiveDeploy(app App, archiveURL string, w io.Writer) (string, error)
}

// UploadDeployer is a provisioner that can deploy the application from an
// uploaded file.
type UploadDeployer interface {
	UploadDeploy(app App, file io.ReadCloser, build bool, w io.Writer) (string, error)
}

// ImageDeployer is a provisioner that can deploy the application from a
// previously generated image.
type ImageDeployer interface {
	ImageDeploy(app App, image string, w io.Writer) (string, error)
}

// Provisioner is the basic interface of this package.
//
// Any tsuru provisioner must implement this interface in order to provision
// tsuru apps.
type Provisioner interface {
	// Provision is called when tsuru is creating the app.
	Provision(App) error

	// Destroy is called when tsuru is destroying the app.
	Destroy(App) error

	// AddUnits adds units to an app. The first parameter is the app, the
	// second is the number of units to be added.
	//
	// It returns a slice containing all added units
	AddUnits(App, uint, string, io.Writer) ([]Unit, error)

	// RemoveUnits "undoes" AddUnits, removing the given number of units
	// from the app.
	RemoveUnits(App, uint, string, io.Writer) error

	// SetUnitStatus changes the status of a unit.
	SetUnitStatus(Unit, Status) error

	// ExecuteCommand runs a command in all units of the app.
	ExecuteCommand(stdout, stderr io.Writer, app App, cmd string, args ...string) error

	// ExecuteCommandOnce runs a command in one unit of the app.
	ExecuteCommandOnce(stdout, stderr io.Writer, app App, cmd string, args ...string) error

	// Restart restarts the units of the application, with an optional
	// string parameter represeting the name of the process to start. When
	// the process is empty, Restart will restart all units of the
	// application.
	Restart(App, string, io.Writer) error

	// Start starts the units of the application, with an optional string
	// parameter representing the name of the process to start. When the
	// process is empty, Start will start all units of the application.
	Start(App, string) error

	// Stop stops the units of the application, with an optional string
	// parameter representing the name of the process to start. When the
	// process is empty, Stop will stop all units of the application.
	Stop(App, string) error

	// Sleep puts the units of the application to sleep, with an optional string
	// parameter representing the name of the process to sleep. When the
	// process is empty, Sleep will put all units of the application to sleep.
	Sleep(App, string) error

	// Addr returns the address for an app.
	//
	// tsuru will use this method to get the IP (although it might not be
	// an actual IP, collector calls it "IP") of the app from the
	// provisioner.
	Addr(App) (string, error)

	// Swap change the router between two apps.
	Swap(App, App) error

	// Units returns information about units by App.
	Units(App) ([]Unit, error)

	// RoutableUnits returns information about routable units by App.
	RoutableUnits(App) ([]Unit, error)

	// Register a unit after the container has been created or restarted.
	RegisterUnit(Unit, map[string]interface{}) error

	// Open a remote shel in one of the units in the application.
	Shell(ShellOptions) error

	// Returns list of valid image names for app, these can be used for
	// rollback.
	ValidAppImages(string) ([]string, error)

	// Returns the metric backend environs for the app.
	MetricEnvs(App) map[string]string

	// Rollback a deploy
	Rollback(App, string, io.Writer) (string, error)

	FilterAppsByUnitStatus([]App, []string) ([]App, error)
}

type MessageProvisioner interface {
	StartupMessage() (string, error)
}

// InitializableProvisioner is a provisioner that provides an initialization
// method that should be called when the app is started
type InitializableProvisioner interface {
	Initialize() error
}

// Provisioners can implement this interface to optionaly disable logs for a
// given app.
type OptionalLogsProvisioner interface {
	// Checks if logs are enabled for given app.
	LogsEnabled(App) (bool, string, error)
}

// PlatformOptions is the set of options provided to PlatformAdd and
// PlatformUpdate, in the ExtensibleProvisioner.
type PlatformOptions struct {
	Name   string
	Args   map[string]string
	Input  io.Reader
	Output io.Writer
}

// ExtensibleProvisioner is a provisioner where administrators can manage
// platforms (automatically adding, removing and updating platforms).
type ExtensibleProvisioner interface {
	PlatformAdd(PlatformOptions) error
	PlatformUpdate(PlatformOptions) error
	PlatformRemove(name string) error
}

var provisioners = make(map[string]Provisioner)

// Register registers a new provisioner in the Provisioner registry.
func Register(name string, p Provisioner) {
	provisioners[name] = p
}

// Get gets the named provisioner from the registry.
func Get(name string) (Provisioner, error) {
	p, ok := provisioners[name]
	if !ok {
		return nil, fmt.Errorf("unknown provisioner: %q", name)
	}
	return p, nil
}

// Registry returns the list of registered provisioners.
func Registry() []Provisioner {
	registry := make([]Provisioner, 0, len(provisioners))
	for _, p := range provisioners {
		registry = append(registry, p)
	}
	return registry
}

// Error represents a provisioning error. It encapsulates further errors.
type Error struct {
	Reason string
	Err    error
}

// Error is the string representation of a provisioning error.
func (e *Error) Error() string {
	var err string
	if e.Err != nil {
		err = e.Err.Error() + ": " + e.Reason
	} else {
		err = e.Reason
	}
	return err
}

type TsuruYamlRestartHooks struct {
	Before []string
	After  []string
}

type TsuruYamlHooks struct {
	Restart TsuruYamlRestartHooks
	Build   []string
}

type TsuruYamlHealthcheck struct {
	Path            string
	Method          string
	Status          int
	Match           string
	AllowedFailures int `json:"allowed_failures" bson:"allowed_failures"`
}

type TsuruYamlData struct {
	Hooks       TsuruYamlHooks
	Healthcheck TsuruYamlHealthcheck
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net"
	"sync"
	"time"

	"github.com/alienth/fastlyctl/util"
)

type hitMap struct {
	sync.RWMutex
	m map[string]*ipRate
}

// getMap returns a copy of the hitmap. This is used to prevent long lock times on the hitMap
func (hits *hitMap) getMap() map[string]*ipRate {
	newMap := make(map[string]*ipRate)
	hits.RLock()
	for ip, ipr := range hits.m {
		newMap[ip] = ipr
	}
	hits.RUnlock()
	return newMap
}

func (hits *hitMap) expireRecords() {
	for {
		hitMapCopy := hits.getMap()
		for ip, ipr := range hitMapCopy {
			if time.Now().After(ipr.Expire) {
				if err := ipr.RemoveLimit(); err != nil {
					fmt.Println(err)
				} else {
					hits.Lock()
					delete(hits.m, ip)
					hits.Unlock()
					ipr.cleanSharedBuckets()
				}
			}
		}
		hitMapCopy = nil
		time.Sleep(time.Duration(60) * time.Second)
	}
}

func (hits *hitMap) expireLimits() {
	for {
		hitMapCopy := hits.getMap()
		for _, ipr := range hitMapCopy {
			if time.Now().After(ipr.LimitExpire) {
				if err := ipr.RemoveLimit(); err != nil {
					fmt.Println(err)
				}
			}
		}
		time.Sleep(time.Duration(15) * time.Second)
	}
}

// Fetches down remote ACLs and populates local hitMap with previously stored data.
func (hits *hitMap) importIPRates(serviceDomains ServiceDomains) error {
	aclEntries := make([]*util.ACLEntry, 0)
	for service, _ := range serviceDomains {
		acl, err := util.NewACL(client, service.Name, aclName)
		if err != nil {
			return err
		}

		entries, err := acl.ListEntries()
		if err != nil {
			return err
		}

		for _, e := range entries {
			entry := &util.ACLEntry{Client: client, ID: e.ID, ACLID: e.ACLID, ServiceID: service.ID, IP: e.IP, Comment: e.Comment, Subnet: e.Subnet, Negated: e.Negated}
			aclEntries = append(aclEntries, entry)
		}
	}
	for _, entry := range aclEntries {
		var ipr *ipRate
		var found bool
		hits.Lock()
		if ipr, found = hits.m[entry.IP]; !found {
			ip := net.ParseIP(entry.IP)
			ipr = ipLists.getRate(&ip)
			if ip == nil {
				return fmt.Errorf("Unable to parse IP %s in ACL.")
			}
			hits.m[ip.String()] = ipr
		}
		hits.Unlock()

		var placeholder ipRate
		err := json.Unmarshal([]byte(entry.Comment), &placeholder)
		if err != nil {
			// We may not have created an entry, so ignore entries with
			// comments that we don't recognize.
			break
		}
		if ipr.LastHit.Before(placeholder.LastHit) {
			json.Unmarshal([]byte(entry.Comment), &ipr)
		}
		ipr.limited = true
		ipr.entries = append(ipr.entries, entry)
	}

	return nil
}
<commit_msg>Fix early loop break when a bad comment is found.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net"
	"sync"
	"time"

	"github.com/alienth/fastlyctl/util"
)

type hitMap struct {
	sync.RWMutex
	m map[string]*ipRate
}

// getMap returns a copy of the hitmap. This is used to prevent long lock times on the hitMap
func (hits *hitMap) getMap() map[string]*ipRate {
	newMap := make(map[string]*ipRate)
	hits.RLock()
	for ip, ipr := range hits.m {
		newMap[ip] = ipr
	}
	hits.RUnlock()
	return newMap
}

func (hits *hitMap) expireRecords() {
	for {
		hitMapCopy := hits.getMap()
		for ip, ipr := range hitMapCopy {
			if time.Now().After(ipr.Expire) {
				if err := ipr.RemoveLimit(); err != nil {
					fmt.Println(err)
				} else {
					hits.Lock()
					delete(hits.m, ip)
					hits.Unlock()
					ipr.cleanSharedBuckets()
				}
			}
		}
		hitMapCopy = nil
		time.Sleep(time.Duration(60) * time.Second)
	}
}

func (hits *hitMap) expireLimits() {
	for {
		hitMapCopy := hits.getMap()
		for _, ipr := range hitMapCopy {
			if time.Now().After(ipr.LimitExpire) {
				if err := ipr.RemoveLimit(); err != nil {
					fmt.Println(err)
				}
			}
		}
		time.Sleep(time.Duration(15) * time.Second)
	}
}

// Fetches down remote ACLs and populates local hitMap with previously stored data.
func (hits *hitMap) importIPRates(serviceDomains ServiceDomains) error {
	aclEntries := make([]*util.ACLEntry, 0)
	for service, _ := range serviceDomains {
		acl, err := util.NewACL(client, service.Name, aclName)
		if err != nil {
			return err
		}

		entries, err := acl.ListEntries()
		if err != nil {
			return err
		}

		for _, e := range entries {
			entry := &util.ACLEntry{Client: client, ID: e.ID, ACLID: e.ACLID, ServiceID: service.ID, IP: e.IP, Comment: e.Comment, Subnet: e.Subnet, Negated: e.Negated}
			aclEntries = append(aclEntries, entry)
		}
	}
	for _, entry := range aclEntries {
		var ipr *ipRate
		var found bool
		hits.Lock()
		if ipr, found = hits.m[entry.IP]; !found {
			ip := net.ParseIP(entry.IP)
			ipr = ipLists.getRate(&ip)
			if ip == nil {
				return fmt.Errorf("Unable to parse IP %s in ACL.")
			}
			hits.m[ip.String()] = ipr
		}
		hits.Unlock()

		var placeholder ipRate
		err := json.Unmarshal([]byte(entry.Comment), &placeholder)
		if err != nil {
			// We may not have created an entry, so ignore entries with
			// comments that we don't recognize.
			fmt.Printf("Found unrecognized ACL comment for IP %s on service %s. Ignoring.\ncomment:\n%s\nError:\n%s\n", ipr.ip.String(), entry.ServiceID, entry.Comment, err)
			continue
		}
		if ipr.LastHit.Before(placeholder.LastHit) {
			json.Unmarshal([]byte(entry.Comment), &ipr)
		}
		ipr.limited = true
		ipr.entries = append(ipr.entries, entry)
	}

	return nil
}
<|endoftext|>


<commit_before>package system

import (
	"testing"
)

// Test_JoinLeaderNode tests a join operation between a leader and a new node.
func Test_JoinLeaderNode(t *testing.T) {
	leader := mustNewLeaderNode()
	defer leader.Deprovision()

	node := mustNewNode(false)
	defer node.Deprovision()
	if err := node.Join(leader); err != nil {
		t.Fatalf("node failed to join leader: %s", err.Error())
	}
	_, err := node.WaitForLeader()
	if err != nil {
		t.Fatalf("failed waiting for leader: %s", err.Error())
	}
}

// Test_MultiNodeCluster tests formation of a 3-node cluster, and its operation.
func Test_MultiNodeCluster(t *testing.T) {
	node1 := mustNewLeaderNode()
	defer node1.Deprovision()

	node2 := mustNewNode(false)
	defer node2.Deprovision()
	if err := node2.Join(node1); err != nil {
		t.Fatalf("node failed to join leader: %s", err.Error())
	}
	_, err := node2.WaitForLeader()
	if err != nil {
		t.Fatalf("failed waiting for leader: %s", err.Error())
	}

	// Get the new leader, in case it changed.
	c := Cluster{node1, node2}
	leader, err := c.Leader()
	if err != nil {
		t.Fatalf("failed to find cluster leader: %s", err.Error())
	}

	node3 := mustNewNode(false)
	defer node3.Deprovision()
	if err := node3.Join(leader); err != nil {
		t.Fatalf("node failed to join leader: %s", err.Error())
	}
	_, err = node3.WaitForLeader()
	if err != nil {
		t.Fatalf("failed waiting for leader: %s", err.Error())
	}

	// Get the new leader, in case it changed.
	c = Cluster{node1, node2, node3}
	leader, err = c.Leader()
	if err != nil {
		t.Fatalf("failed to find cluster leader: %s", err.Error())
	}

	// Run queries against cluster.
	tests := []struct {
		stmt     string
		expected string
		execute  bool
	}{
		{
			stmt:     `CREATE TABLE foo (id integer not null primary key, name text)`,
			expected: `{"results":[{}]}`,
			execute:  true,
		},
		{
			stmt:     `INSERT INTO foo(name) VALUES("fiona")`,
			expected: `{"results":[{"last_insert_id":1,"rows_affected":1}]}`,
			execute:  true,
		},
		{
			stmt:     `SELECT * FROM foo`,
			expected: `{"results":[{"columns":["id","name"],"types":["integer","text"],"values":[[1,"fiona"]]}]}`,
			execute:  false,
		},
	}

	for i, tt := range tests {
		var r string
		var err error
		if tt.execute {
			r, err = leader.Execute(tt.stmt)
		} else {
			r, err = leader.Query(tt.stmt)
		}
		if err != nil {
			t.Fatalf(`test %d failed "%s": %s`, i, tt.stmt, err.Error())
		}
		if r != tt.expected {
			t.Fatalf(`test %d received wrong result "%s" got: %s exp: %s`, i, tt.stmt, r, tt.expected)
		}
	}

	// Kill the leader and wait for the new leader.
	leader.Deprovision()
	c.RemoveNode(leader)
	leader, err = c.WaitForNewLeader(leader)
	if err != nil {
		t.Fatalf("failed to find new cluster leader after killing leader: %s", err.Error())
	}

	// Run queries against 2-node cluster.
	tests = []struct {
		stmt     string
		expected string
		execute  bool
	}{
		{
			stmt:     `CREATE TABLE foo (id integer not null primary key, name text)`,
			expected: `{"results":[{"error":"table foo already exists"}]}`,
			execute:  true,
		},
		{
			stmt:     `INSERT INTO foo(name) VALUES("sinead")`,
			expected: `{"results":[{"last_insert_id":2,"rows_affected":1}]}`,
			execute:  true,
		},
		{
			stmt:     `SELECT * FROM foo`,
			expected: `{"results":[{"columns":["id","name"],"types":["integer","text"],"values":[[1,"fiona"],[2,"sinead"]]}]}`,
			execute:  false,
		},
	}

	for i, tt := range tests {
		var r string
		var err error
		if tt.execute {
			r, err = leader.Execute(tt.stmt)
		} else {
			r, err = leader.Query(tt.stmt)
		}
		if err != nil {
			t.Fatalf(`test %d failed "%s": %s`, i, tt.stmt, err.Error())
		}
		if r != tt.expected {
			t.Fatalf(`test %d received wrong result "%s" got: %s exp: %s`, i, tt.stmt, r, tt.expected)
		}
	}
}
<commit_msg>Improve test comment<commit_after>package system

import (
	"testing"
)

// Test_JoinLeaderNode tests a join operation between a leader and a new node.
func Test_JoinLeaderNode(t *testing.T) {
	leader := mustNewLeaderNode()
	defer leader.Deprovision()

	node := mustNewNode(false)
	defer node.Deprovision()
	if err := node.Join(leader); err != nil {
		t.Fatalf("node failed to join leader: %s", err.Error())
	}
	_, err := node.WaitForLeader()
	if err != nil {
		t.Fatalf("failed waiting for leader: %s", err.Error())
	}
}

// Test_MultiNodeCluster tests formation of a 3-node cluster, and its operation.
func Test_MultiNodeCluster(t *testing.T) {
	node1 := mustNewLeaderNode()
	defer node1.Deprovision()

	node2 := mustNewNode(false)
	defer node2.Deprovision()
	if err := node2.Join(node1); err != nil {
		t.Fatalf("node failed to join leader: %s", err.Error())
	}
	_, err := node2.WaitForLeader()
	if err != nil {
		t.Fatalf("failed waiting for leader: %s", err.Error())
	}

	// Get the new leader, in case it changed.
	c := Cluster{node1, node2}
	leader, err := c.Leader()
	if err != nil {
		t.Fatalf("failed to find cluster leader: %s", err.Error())
	}

	node3 := mustNewNode(false)
	defer node3.Deprovision()
	if err := node3.Join(leader); err != nil {
		t.Fatalf("node failed to join leader: %s", err.Error())
	}
	_, err = node3.WaitForLeader()
	if err != nil {
		t.Fatalf("failed waiting for leader: %s", err.Error())
	}

	// Get the new leader, in case it changed.
	c = Cluster{node1, node2, node3}
	leader, err = c.Leader()
	if err != nil {
		t.Fatalf("failed to find cluster leader: %s", err.Error())
	}

	// Run queries against cluster.
	tests := []struct {
		stmt     string
		expected string
		execute  bool
	}{
		{
			stmt:     `CREATE TABLE foo (id integer not null primary key, name text)`,
			expected: `{"results":[{}]}`,
			execute:  true,
		},
		{
			stmt:     `INSERT INTO foo(name) VALUES("fiona")`,
			expected: `{"results":[{"last_insert_id":1,"rows_affected":1}]}`,
			execute:  true,
		},
		{
			stmt:     `SELECT * FROM foo`,
			expected: `{"results":[{"columns":["id","name"],"types":["integer","text"],"values":[[1,"fiona"]]}]}`,
			execute:  false,
		},
	}

	for i, tt := range tests {
		var r string
		var err error
		if tt.execute {
			r, err = leader.Execute(tt.stmt)
		} else {
			r, err = leader.Query(tt.stmt)
		}
		if err != nil {
			t.Fatalf(`test %d failed "%s": %s`, i, tt.stmt, err.Error())
		}
		if r != tt.expected {
			t.Fatalf(`test %d received wrong result "%s" got: %s exp: %s`, i, tt.stmt, r, tt.expected)
		}
	}

	// Kill the leader and wait for the new leader.
	leader.Deprovision()
	c.RemoveNode(leader)
	leader, err = c.WaitForNewLeader(leader)
	if err != nil {
		t.Fatalf("failed to find new cluster leader after killing leader: %s", err.Error())
	}

	// Run queries against the now 2-node cluster.
	tests = []struct {
		stmt     string
		expected string
		execute  bool
	}{
		{
			stmt:     `CREATE TABLE foo (id integer not null primary key, name text)`,
			expected: `{"results":[{"error":"table foo already exists"}]}`,
			execute:  true,
		},
		{
			stmt:     `INSERT INTO foo(name) VALUES("sinead")`,
			expected: `{"results":[{"last_insert_id":2,"rows_affected":1}]}`,
			execute:  true,
		},
		{
			stmt:     `SELECT * FROM foo`,
			expected: `{"results":[{"columns":["id","name"],"types":["integer","text"],"values":[[1,"fiona"],[2,"sinead"]]}]}`,
			execute:  false,
		},
	}

	for i, tt := range tests {
		var r string
		var err error
		if tt.execute {
			r, err = leader.Execute(tt.stmt)
		} else {
			r, err = leader.Query(tt.stmt)
		}
		if err != nil {
			t.Fatalf(`test %d failed "%s": %s`, i, tt.stmt, err.Error())
		}
		if r != tt.expected {
			t.Fatalf(`test %d received wrong result "%s" got: %s exp: %s`, i, tt.stmt, r, tt.expected)
		}
	}
}
<|endoftext|>


<commit_before>package captainslog

import (
	"testing"
	"time"
)

type testCase struct {
	input  []byte
	result bool
}

func TestRangeTransformerTTL(t *testing.T) {
	input := []byte("<4>2016-03-08T14:59:36.293816+00:00 host.example.com kernel: [15803005.789011] ------------[ cut here ]------------\n")

	transformer := NewTagRangeTransformer(
		NewTagMatcher("kernel:"),
		NewContentContainsMatcher("[ cut here ]"),
		NewContentContainsMatcher("[ end trace"),
		NewTagArrayMutator("tags", "trace"),
		1, 1)

	original := NewSyslogMsg()
	err := Unmarshal(input, &original)
	if err != nil {
		t.Error(err)
	}

	if want, got := 0, len(transformer.trackingDB); want != got {
		t.Errorf("want '%v', got '%v'", want, got)
	}

	_, err = transformer.Transform(original)
	if err != nil {
		t.Error(err)
	}

	if want, got := 1, len(transformer.trackingDB); want != got {
		t.Errorf("want '%v', got '%v'", want, got)
	}

	time.Sleep(2 * time.Second)

	if want, got := 0, len(transformer.trackingDB); want != got {
		t.Errorf("want '%v', got '%v'", want, got)
	}
}

func TestTagRangeTransformerTransform(t *testing.T) {
	cases := []testCase{
		testCase{
			input:  []byte("<4>2016-03-08T14:59:36.293816+00:00 host.example.com kernel: [15803005.789010] this line is not part of the trace\n"),
			result: false,
		},
		testCase{
			input:  []byte("<4>2016-03-08T14:59:36.293816+00:00 host.example.com kernel: [15803005.789011] ------------[ cut here ]------------\n"),
			result: true,
		},
		testCase{
			input:  []byte("<4>2016-03-08T14:59:36.293816+00:00 host.example.com kernel: [15803005.789013] this  line should be part of the trace\n"),
			result: true,
		},
		testCase{
			input:  []byte("<4>2016-03-08T14:59:36.293816+00:00 host.example.com kernel: [15803005.789015] this line should also be part of the trace\n"),
			result: true,
		},
		testCase{
			input:  []byte("<4>2016-03-08T14:59:36.293918+00:00 host.example.com kernel: [15803005.789433] ---[ end trace 999999999 ]---\n"),
			result: true,
		},
		testCase{
			input:  []byte("<4>2016-03-08T14:59:36.293816+00:00 host.example.com kernel: [15803005.789010] this line is not part of the trace\n"),
			result: false,
		},
	}

	transformer := NewTagRangeTransformer(
		NewTagMatcher("kernel:"),
		NewContentContainsMatcher("[ cut here ]"),
		NewContentContainsMatcher("[ end trace"),
		NewTagArrayMutator("tags", "trace"),
		60, 30)

	for i, v := range cases {
		original := NewSyslogMsg()
		err := Unmarshal(v.input, &original)
		if err != nil {
			t.Error(err)
		}

		mutated, err := transformer.Transform(original)
		if err != nil {
			t.Error(err)
		}

		_, hasTagsKey := mutated.JSONValues["tags"]

		if want, got := v.result, hasTagsKey; want != got {
			t.Errorf("case %d: want '%v', got '%v'", i, want, got)
		}
	}
}

func BenchmarkTagRangeTransformerTransform(b *testing.B) {
	m := []byte("<4>2016-03-08T14:59:36.293816+00:00 host.example.com kernel: [15803005.789011] ------------[ cut here ]------------\n")

	transformer := NewTagRangeTransformer(
		NewTagMatcher("kernel:"),
		NewContentContainsMatcher("[ cut here ]"),
		NewContentContainsMatcher("[ end trace"),
		NewTagArrayMutator("tags", "trace"),
		60, 30)

	for i := 0; i < b.N; i++ {
		original := NewSyslogMsg()
		err := Unmarshal(m, &original)
		if err != nil {
			panic(err)
		}

		_, err = transformer.Transform(original)
		if err != nil {
			panic(err)
		}
	}
}
<commit_msg>Problem: data race in test code<commit_after>package captainslog

import (
	"testing"
	"time"
)

type testCase struct {
	input  []byte
	result bool
}

func TestRangeTransformerTTL(t *testing.T) {
	input := []byte("<4>2016-03-08T14:59:36.293816+00:00 host.example.com kernel: [15803005.789011] ------------[ cut here ]------------\n")

	transformer := NewTagRangeTransformer(
		NewTagMatcher("kernel:"),
		NewContentContainsMatcher("[ cut here ]"),
		NewContentContainsMatcher("[ end trace"),
		NewTagArrayMutator("tags", "trace"),
		1, 1)

	original := NewSyslogMsg()
	err := Unmarshal(input, &original)
	if err != nil {
		t.Error(err)
	}

	transformer.mutex.Lock()
	if want, got := 0, len(transformer.trackingDB); want != got {
		t.Errorf("want '%v', got '%v'", want, got)
	}
	transformer.mutex.Unlock()

	_, err = transformer.Transform(original)
	if err != nil {
		t.Error(err)
	}

	transformer.mutex.Lock()
	if want, got := 1, len(transformer.trackingDB); want != got {
		t.Errorf("want '%v', got '%v'", want, got)
	}
	transformer.mutex.Unlock()

	time.Sleep(2 * time.Second)

	transformer.mutex.Lock()
	if want, got := 0, len(transformer.trackingDB); want != got {
		t.Errorf("want '%v', got '%v'", want, got)
	}
	transformer.mutex.Unlock()
}

func TestTagRangeTransformerTransform(t *testing.T) {
	cases := []testCase{
		testCase{
			input:  []byte("<4>2016-03-08T14:59:36.293816+00:00 host.example.com kernel: [15803005.789010] this line is not part of the trace\n"),
			result: false,
		},
		testCase{
			input:  []byte("<4>2016-03-08T14:59:36.293816+00:00 host.example.com kernel: [15803005.789011] ------------[ cut here ]------------\n"),
			result: true,
		},
		testCase{
			input:  []byte("<4>2016-03-08T14:59:36.293816+00:00 host.example.com kernel: [15803005.789013] this  line should be part of the trace\n"),
			result: true,
		},
		testCase{
			input:  []byte("<4>2016-03-08T14:59:36.293816+00:00 host.example.com kernel: [15803005.789015] this line should also be part of the trace\n"),
			result: true,
		},
		testCase{
			input:  []byte("<4>2016-03-08T14:59:36.293918+00:00 host.example.com kernel: [15803005.789433] ---[ end trace 999999999 ]---\n"),
			result: true,
		},
		testCase{
			input:  []byte("<4>2016-03-08T14:59:36.293816+00:00 host.example.com kernel: [15803005.789010] this line is not part of the trace\n"),
			result: false,
		},
	}

	transformer := NewTagRangeTransformer(
		NewTagMatcher("kernel:"),
		NewContentContainsMatcher("[ cut here ]"),
		NewContentContainsMatcher("[ end trace"),
		NewTagArrayMutator("tags", "trace"),
		60, 30)

	for i, v := range cases {
		original := NewSyslogMsg()
		err := Unmarshal(v.input, &original)
		if err != nil {
			t.Error(err)
		}

		mutated, err := transformer.Transform(original)
		if err != nil {
			t.Error(err)
		}

		_, hasTagsKey := mutated.JSONValues["tags"]

		if want, got := v.result, hasTagsKey; want != got {
			t.Errorf("case %d: want '%v', got '%v'", i, want, got)
		}
	}
}

func BenchmarkTagRangeTransformerTransform(b *testing.B) {
	m := []byte("<4>2016-03-08T14:59:36.293816+00:00 host.example.com kernel: [15803005.789011] ------------[ cut here ]------------\n")

	transformer := NewTagRangeTransformer(
		NewTagMatcher("kernel:"),
		NewContentContainsMatcher("[ cut here ]"),
		NewContentContainsMatcher("[ end trace"),
		NewTagArrayMutator("tags", "trace"),
		60, 30)

	for i := 0; i < b.N; i++ {
		original := NewSyslogMsg()
		err := Unmarshal(m, &original)
		if err != nil {
			panic(err)
		}

		_, err = transformer.Transform(original)
		if err != nil {
			panic(err)
		}
	}
}
<|endoftext|>