<commit_before>package kubernetes

import (
	"github.com/weaveworks/scope/report"
)

// Reporter generate Reports containing Container and ContainerImage topologies
type Reporter struct {
	client Client
}

// NewReporter makes a new Reporter
func NewReporter(client Client) *Reporter {
	return &Reporter{
		client: client,
	}
}

// Name of this reporter, for metrics gathering
func (Reporter) Name() string { return "K8s" }

// Report generates a Report containing Container and ContainerImage topologies
func (r *Reporter) Report() (report.Report, error) {
	result := report.MakeReport()
	serviceTopology, services, err := r.serviceTopology()
	if err != nil {
		return result, err
	}
	podTopology, containerTopology, err := r.podTopology(services)
	if err != nil {
		return result, err
	}
	result.Service = result.Service.Merge(serviceTopology)
	result.Pod = result.Pod.Merge(podTopology)
	result.Container = result.Container.Merge(containerTopology)
	return result, nil
}

func (r *Reporter) serviceTopology() (report.Topology, []Service, error) {
	var (
		result   = report.MakeTopology()
		services = []Service{}
	)
	err := r.client.WalkServices(func(s Service) error {
		nodeID := report.MakeServiceNodeID(s.Namespace(), s.Name())
		result = result.AddNode(nodeID, s.GetNode())
		services = append(services, s)
		return nil
	})
	return result, services, err
}

func (r *Reporter) podTopology(services []Service) (report.Topology, report.Topology, error) {
	pods, containers := report.MakeTopology(), report.MakeTopology()
	err := r.client.WalkPods(func(p Pod) error {
		for _, service := range services {
			if service.Selector().Matches(p.Labels()) {
				p.AddServiceID(service.ID())
			}
		}
		nodeID := report.MakePodNodeID(p.Namespace(), p.Name())
		pods = pods.AddNode(nodeID, p.GetNode())

		container := report.MakeNodeWith(map[string]string{
			PodID:     p.ID(),
			Namespace: p.Namespace(),
		}).WithParents(report.EmptySets.Add(report.Pod, report.MakeStringSet(nodeID)))
		for _, containerID := range p.ContainerIDs() {
			containers.AddNode(report.MakeContainerNodeID(containerID), container)
		}
		return nil
	})
	return pods, containers, err
}
<commit_msg>Compile k8s selectors once (not for each pod)<commit_after>package kubernetes

import (
	"github.com/weaveworks/scope/report"
	"k8s.io/kubernetes/pkg/labels"
)

// Reporter generate Reports containing Container and ContainerImage topologies
type Reporter struct {
	client Client
}

// NewReporter makes a new Reporter
func NewReporter(client Client) *Reporter {
	return &Reporter{
		client: client,
	}
}

// Name of this reporter, for metrics gathering
func (Reporter) Name() string { return "K8s" }

// Report generates a Report containing Container and ContainerImage topologies
func (r *Reporter) Report() (report.Report, error) {
	result := report.MakeReport()
	serviceTopology, services, err := r.serviceTopology()
	if err != nil {
		return result, err
	}
	podTopology, containerTopology, err := r.podTopology(services)
	if err != nil {
		return result, err
	}
	result.Service = result.Service.Merge(serviceTopology)
	result.Pod = result.Pod.Merge(podTopology)
	result.Container = result.Container.Merge(containerTopology)
	return result, nil
}

func (r *Reporter) serviceTopology() (report.Topology, []Service, error) {
	var (
		result   = report.MakeTopology()
		services = []Service{}
	)
	err := r.client.WalkServices(func(s Service) error {
		nodeID := report.MakeServiceNodeID(s.Namespace(), s.Name())
		result = result.AddNode(nodeID, s.GetNode())
		services = append(services, s)
		return nil
	})
	return result, services, err
}

func (r *Reporter) podTopology(services []Service) (report.Topology, report.Topology, error) {
	pods, containers := report.MakeTopology(), report.MakeTopology()
	selectors := map[string]labels.Selector{}
	for _, service := range services {
		selectors[service.ID()] = service.Selector()
	}
	err := r.client.WalkPods(func(p Pod) error {
		for serviceID, selector := range selectors {
			if selector.Matches(p.Labels()) {
				p.AddServiceID(serviceID)
			}
		}
		nodeID := report.MakePodNodeID(p.Namespace(), p.Name())
		pods = pods.AddNode(nodeID, p.GetNode())

		container := report.MakeNodeWith(map[string]string{
			PodID:     p.ID(),
			Namespace: p.Namespace(),
		}).WithParents(report.EmptySets.Add(report.Pod, report.MakeStringSet(nodeID)))
		for _, containerID := range p.ContainerIDs() {
			containers.AddNode(report.MakeContainerNodeID(containerID), container)
		}
		return nil
	})
	return pods, containers, err
}
<|endoftext|>


<commit_before>package closuretable

import (
	"errors"
	"github.com/carbocation/util.git/datatypes/binarytree"
	"sort"
)

// A ClosureTable should represent every direct-line relationship, including self-to-self
type ClosureTable []Relationship

// A Relationship is the fundamental unit of the closure table. A relationship is
// defined between every entry and itselft, its parent, and any of its parent's ancestors.
type Relationship struct {
	Ancestor   int64
	Descendant int64
	Depth      int
}

// A Child is intended to be an ephemeral entity that gets validated and converted to a Relationship
type Child struct {
	Parent int64
	Child  int64
}

type EmptyTableError int

func (e EmptyTableError) Error() error {
	return errors.New("forum: The closure table is empty, so a parent cannot exist, so a child cannot be added.")
}

func ParentDoesNotExistError() error {
	return errors.New("forum: The closure table contains no record of the requested parent, so no child can be created.")
}

func EntityExistsError() error {
	return errors.New("forum: The entity that you are trying to add to the closure table already exists within it. This operation is not permitted.")
}

func New(origin int64) *ClosureTable {
	r := Relationship{Ancestor: origin, Descendant: origin, Depth: 0}
	return &ClosureTable{r}
}

// AddChild takes a Child, verifies that it is acceptable, verifies that the
// ClosureTable is suitable to accept a child, and then creates the appropriate
// Relationships within the ClosureTable to instantiate that child.
func (table *ClosureTable) AddChild(new Child) error {
	if len(*table) < 1 {
		return EmptyTableError.Error(1)
	}

	if table.EntityExists(new.Parent) != true {
		return ParentDoesNotExistError()
	}

	if table.EntityExists(new.Child) {
		return EntityExistsError()
	}

	// It checks out, create all of the consequent ancestral relationships:
	// Self
	*table = append(*table, Relationship{Ancestor: new.Child, Descendant: new.Child, Depth: 0})

	// All derived relationships, including the direct parent<->child relationship
	for _, rel := range table.GetAncestralRelationships(new.Parent) {
		*table = append(*table, Relationship{Ancestor: rel.Ancestor, Descendant: new.Child, Depth: rel.Depth + 1})
	}

	return nil
}

func (table *ClosureTable) GetAncestralRelationships(id int64) []Relationship {
	list := []Relationship{}
	for _, rel := range *table {
		if rel.Descendant == id {
			list = append(list, rel)
		}
	}

	return list
}

// EntityExists asks if an entity of a given id exists in the closure table
// Entities that exist are guaranteed to appear at least once in ancestor and
// descendant thanks to the self relationship, so the choice of which one to inspect
// is arbitrary
func (table *ClosureTable) EntityExists(id int64) bool {
	for _, r := range *table {
		if r.Descendant == id {
			return true
		}
	}

	return false
}

// Return the id of the root node of the closure table.
// This method assumes that there can only be one root node.
func (table *ClosureTable) RootNodeId() (int64, error) {
	m := map[int64]int{}
	for _, rel := range *table {
		//In go, it's valid to increment an integer in a map without first zeroing it
		m[rel.Descendant]++
	}

	trip := 0
	var result int64
	for item, count := range m {
		if count == 1 {
			result = item
			trip++
		}

		if trip > 1 {
			return int64(-1), errors.New("More than one potential root node was present in the closure table.")
		}
	}

	if trip < 1 {
		return int64(-1), errors.New("No potential root nodes were present in the closure table.")
	}

	return result, nil
}

// Takes map of entries whose keys are the same values as the IDs of the closure table entries
// Returns a well-formed *binarytree.Tree with those entries as values.
func (table *ClosureTable) TableToTree(entries map[int64]interface{}) *binarytree.Tree {
	// The strategy here is to create one tree per entry, then to iterate through them
	// and correct their parent/child/sibling pointers as we proceed.

	// ID in the map must be the element's ID from the closure table
	forest := map[int64]*binarytree.Tree{}

	// All entries now are trees
	for id, entry := range entries {
		forest[id] = binarytree.New(entry)
	}

	childparent := table.DepthOneRelationships()

	for _, rel := range childparent {
		// Add the children.
		// If there is already a child, then traverse right until you find nil
		parentTree := forest[rel.Ancestor]
		siblingMode := false

		for {
			if siblingMode {
				if parentTree.Right() == nil {
					// We found an empty slot
					parentTree.SetRight(forest[rel.Descendant])
					forest[rel.Descendant].SetParent(parentTree)
					break
				} else {
					parentTree = parentTree.Right()
				}
			} else {
				if parentTree.Left() == nil {
					// We found an empty slot
					parentTree.SetLeft(forest[rel.Descendant])
					forest[rel.Descendant].SetParent(parentTree)
					break
				} else {
					parentTree = parentTree.Left()
					siblingMode = true
				}
			}
		}
	}

	rootNodeId, err := table.RootNodeId()
	if err != nil {
		return &binarytree.Tree{}
	}

	return forest[rootNodeId]
}

// Returns a map of the ID of each node along with its maximum depth
func (table *ClosureTable) DeepestRelationships() ([]int, map[int][]Relationship) {
	tmp := map[int64]Relationship{}
	out := map[int][]Relationship{}
	discreteDepths := []int{}

	for _, rel := range *table {
		if rel.Depth > tmp[rel.Descendant].Depth {
			tmp[rel.Descendant] = rel
		}
	}

	for _, rel := range tmp {
		out[rel.Depth] = append(out[rel.Depth], rel)
	}

	for depth, _ := range out {
		discreteDepths = append(discreteDepths, depth)
	}

	sort.Ints(discreteDepths)

	return discreteDepths, out
}

// Returns a map of the ID of each node along with its immediate parent
func (table *ClosureTable) DepthOneRelationships() []Relationship {
	out := []Relationship{}

	for _, rel := range *table {
		if rel.Depth == 1 {
			out = append(out, rel)
		}
	}

	return out
}
<commit_msg>Added AddRelationship<commit_after>package closuretable

import (
	"errors"
	"github.com/carbocation/util.git/datatypes/binarytree"
	"sort"
)

// A ClosureTable should represent every direct-line relationship, including self-to-self
type ClosureTable []Relationship

// A Relationship is the fundamental unit of the closure table. A relationship is
// defined between every entry and itselft, its parent, and any of its parent's ancestors.
type Relationship struct {
	Ancestor   int64
	Descendant int64
	Depth      int
}

// A Child is intended to be an ephemeral entity that gets validated and converted to a Relationship
type Child struct {
	Parent int64
	Child  int64
}

type EmptyTableError int

func (e EmptyTableError) Error() error {
	return errors.New("forum: The closure table is empty, so a parent cannot exist, so a child cannot be added.")
}

func ParentDoesNotExistError() error {
	return errors.New("forum: The closure table contains no record of the requested parent, so no child can be created.")
}

func EntityExistsError() error {
	return errors.New("forum: The entity that you are trying to add to the closure table already exists within it. This operation is not permitted.")
}

func New(origin int64) *ClosureTable {
	r := Relationship{Ancestor: origin, Descendant: origin, Depth: 0}
	return &ClosureTable{r}
}

// AddChild takes a Child, verifies that it is acceptable, verifies that the
// ClosureTable is suitable to accept a child, and then creates the appropriate
// Relationships within the ClosureTable to instantiate that child.
func (table *ClosureTable) AddChild(new Child) error {
	if len(*table) < 1 {
		return EmptyTableError.Error(1)
	}

	if table.EntityExists(new.Parent) != true {
		return ParentDoesNotExistError()
	}

	if table.EntityExists(new.Child) {
		return EntityExistsError()
	}

	// It checks out, create all of the consequent ancestral relationships:
	// Self
	*table = append(*table, Relationship{Ancestor: new.Child, Descendant: new.Child, Depth: 0})

	// All derived relationships, including the direct parent<->child relationship
	for _, rel := range table.GetAncestralRelationships(new.Parent) {
		*table = append(*table, Relationship{Ancestor: rel.Ancestor, Descendant: new.Child, Depth: rel.Depth + 1})
	}

	return nil
}

// Allows you to add relationships manually
// Note that this is unsafe, because it relies on you to get all relationships
// right, instead of building intermediary relationships for you
func (table *ClosureTable) AddRelationship(r Relationship) error {
	if len(*table) < 1 {
		return EmptyTableError.Error(1)
	}

	*table = append(*table, r)

	return nil
}

func (table *ClosureTable) GetAncestralRelationships(id int64) []Relationship {
	list := []Relationship{}
	for _, rel := range *table {
		if rel.Descendant == id {
			list = append(list, rel)
		}
	}

	return list
}

// EntityExists asks if an entity of a given id exists in the closure table
// Entities that exist are guaranteed to appear at least once in ancestor and
// descendant thanks to the self relationship, so the choice of which one to inspect
// is arbitrary
func (table *ClosureTable) EntityExists(id int64) bool {
	for _, r := range *table {
		if r.Descendant == id {
			return true
		}
	}

	return false
}

// Return the id of the root node of the closure table.
// This method assumes that there can only be one root node.
func (table *ClosureTable) RootNodeId() (int64, error) {
	m := map[int64]int{}
	for _, rel := range *table {
		//In go, it's valid to increment an integer in a map without first zeroing it
		m[rel.Descendant]++
	}

	trip := 0
	var result int64
	for item, count := range m {
		if count == 1 {
			result = item
			trip++
		}

		if trip > 1 {
			return int64(-1), errors.New("More than one potential root node was present in the closure table.")
		}
	}

	if trip < 1 {
		return int64(-1), errors.New("No potential root nodes were present in the closure table.")
	}

	return result, nil
}

// Takes map of entries whose keys are the same values as the IDs of the closure table entries
// Returns a well-formed *binarytree.Tree with those entries as values.
func (table *ClosureTable) TableToTree(entries map[int64]interface{}) *binarytree.Tree {
	// The strategy here is to create one tree per entry, then to iterate through them
	// and correct their parent/child/sibling pointers as we proceed.

	// ID in the map must be the element's ID from the closure table
	// (specifically, the element's map ID must be the same as its Descendant value)
	forest := map[int64]*binarytree.Tree{}

	// All entries now are trees
	for id, entry := range entries {
		forest[id] = binarytree.New(entry)
	}

	childparent := table.DepthOneRelationships()

	for _, rel := range childparent {
		// Add the children.
		// If there is already a child, then traverse right until you find nil
		parentTree := forest[rel.Ancestor]
		siblingMode := false

		for {
			if siblingMode {
				if parentTree.Right() == nil {
					// We found an empty slot
					parentTree.SetRight(forest[rel.Descendant])
					forest[rel.Descendant].SetParent(parentTree)
					break
				} else {
					parentTree = parentTree.Right()
				}
			} else {
				if parentTree.Left() == nil {
					// We found an empty slot
					parentTree.SetLeft(forest[rel.Descendant])
					forest[rel.Descendant].SetParent(parentTree)
					break
				} else {
					parentTree = parentTree.Left()
					siblingMode = true
				}
			}
		}
	}

	rootNodeId, err := table.RootNodeId()
	if err != nil {
		return &binarytree.Tree{}
	}

	return forest[rootNodeId]
}

// Returns a map of the ID of each node along with its maximum depth
func (table *ClosureTable) DeepestRelationships() ([]int, map[int][]Relationship) {
	tmp := map[int64]Relationship{}
	out := map[int][]Relationship{}
	discreteDepths := []int{}

	for _, rel := range *table {
		if rel.Depth > tmp[rel.Descendant].Depth {
			tmp[rel.Descendant] = rel
		}
	}

	for _, rel := range tmp {
		out[rel.Depth] = append(out[rel.Depth], rel)
	}

	for depth, _ := range out {
		discreteDepths = append(discreteDepths, depth)
	}

	sort.Ints(discreteDepths)

	return discreteDepths, out
}

// Returns a map of the ID of each node along with its immediate parent
func (table *ClosureTable) DepthOneRelationships() []Relationship {
	out := []Relationship{}

	for _, rel := range *table {
		if rel.Depth == 1 {
			out = append(out, rel)
		}
	}

	return out
}
<|endoftext|>


<commit_before>package eventstore

import (
	"encoding/json"
	"errors"
	"net/http"
	"net/url"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/TeaMeow/KitSvc/shared/eventutil"
	"github.com/jetbasrawi/go.geteventstore"
	"github.com/parnurzeal/gorequest"
)

var (
	AllConnected = false
	SentTotal    = 0
	RecvTotal    = 0
	QueueTotal   = 0
)

type eventstore struct {
	*goes.Client
	isConnected bool
	queue       []event
}

type event struct {
	stream string
	data   interface{}
	meta   interface{}
}

// NewClient creates a new event store client.
func NewClient(url string, esURL string, username string, password string, e *eventutil.Engine, replayed chan<- bool, deployed <-chan bool) *eventstore {
	// Ping the Event Store to make sure it's alive.
	if err := pingStore(esURL); err != nil {
		logrus.Fatalln(err)
	}
	// Create the client to Event Store.
	client, err := goes.NewClient(nil, esURL)
	if err != nil {
		logrus.Errorln(err)
		logrus.Fatalln("Cannot create the client of Event Store.")
	}
	// Set the username, password
	client.SetBasicAuth(username, password)

	es := &eventstore{
		Client:      client,
		isConnected: true,
	}
	AllConnected = true

	// Capturing the events when the router was ready in the goroutine.
	go es.capture(url, e, replayed, deployed)

	// Pushing the events in the local queue to Event Store.
	go es.push(esURL)

	return es
}

// pingStore pings the eventstore with backoff to ensure
// a connection can be established before we proceed with the
// eventstore setup and migration.
func pingStore(url string) error {
	for i := 0; i < 30; i++ {
		_, err := http.Get(url)
		if err == nil {
			return nil
		}
		logrus.Infof("Cannot connect to Event Store, retry in 1 second.")
		time.Sleep(time.Second)
	}
	return errors.New("Cannot connect to Event Store.")
}

// sendToRouter sends the received event data to the self router.
func sendToRouter(method string, url string, json []byte) {
	// Send the request via the HTTP client.
	resp, _, err := gorequest.
		New().
		CustomMethod(method, url).
		Send(string(json)).
		End()
	if err != nil {
		// DO NOT FATA DIE
		logrus.Errorln(err)
		logrus.Fatalln("Error occurred while sending the event to self router.")
	}
	if resp.StatusCode != 200 {
		logrus.Infoln("The event has been recevied by the router, but the status code wasn't 200.")
	}
}

// isEmptyEvent returns true when the event body is empty.
func isEmptyEvent(json []byte) bool {
	return string(json) == "{}"
}

//
func (es *eventstore) capture(localUrl string, e *eventutil.Engine, replayed chan<- bool, deployed <-chan bool) {
	//
	<-deployed

	playedStreams := 0
	totalStreams := len(e.Listeners)

	// Each of the listener.
	for _, l := range e.Listeners {
		r := es.NewStreamReader(l.Stream)

		go func(l eventutil.Listener) {
			replayedStream := false
			startTime := time.Now()
			totalEvents := 0

			// Read the next event.
			for r.Next() {

				// Error occurred.
				if r.Err() != nil {
					switch r.Err().(type) {

					// Continue if there's no more event.
					case *goes.ErrNoMoreEvents:
						// We've replayed all the events in the stream since there're no more events can read,
						// so we mark the stream as `replayed`, we'll continue to the next step once all the streams were replayed.
						if !replayedStream {
							playedStreams++
							replayedStream = true

							// Show the statistic of the replayed stream.
							logrus.Infof("The `%s` events were all replayed in %.2f seconds, there's a total of %d events were in the stream.",
								l.Stream,
								time.Since(startTime).Seconds(),
								totalEvents,
							)
							// Set the `played` as true once we have replayed all the event streams.
							if playedStreams >= totalStreams {
								close(replayed)
							}
						}

						// When there are no more events in the stream, set LongPoll.
						// The server will wait for 15 seconds in this case or until
						// events become available on the stream.
						r.LongPoll(15)

					// Create an empty event if the stream hasn't been created.
					case *goes.ErrNotFound:
						writer := es.NewStreamWriter(l.Stream)

						err := writer.Append(nil, goes.NewEvent("", "", map[string]string{}, map[string]string{}))
						if err != nil {
							logrus.Errorln(err)
							logrus.Fatalln("Error occurred while creating an empty stream.")
						}
						continue

						// Sleep for few seconds and try again if the Event Store was not connected.
					case *url.Error, *goes.ErrTemporarilyUnavailable:
						es.isConnected = false
						AllConnected = false
						<-time.After(time.Duration(3) * time.Second)

						// Other errors.
					default:
						logrus.Warningln(r.Err())
						logrus.Warningln("Error occurred while reading the incoming event.")
						continue
					}

					// We received the event, and we're going to make a http request,
					// abd send it to out own Gin router.
				} else {
					// Get the event body.
					json, err := r.EventResponse().Event.Data.(*json.RawMessage).MarshalJSON()
					if err != nil {
						logrus.Warningln(err)
						logrus.Warningf("Cannot parse the event data, the `%s` event has been skipped.", l.Stream)
						continue
					}
					// Skip the empty json event,
					// because that might be the one which we used to create the new stream.
					if isEmptyEvent(json) {
						//logrus.Infof("Received the empty `%s` event.", l.Stream)
						continue
					}

					//
					totalEvents++
					RecvTotal++

					// Send the received data to the router,
					// so we can process the event in the router.
					go sendToRouter(l.Method, localUrl+l.Path, json)

					continue
				}
			}
		}(l)
	}
}

func (es *eventstore) push(esURL string) {
	for {
		// Check the queue every second.
		<-time.After(time.Millisecond * 10)

		//calculate send rateRATE

		// Ping the Event Store to see if it's back online or not.
		if !es.isConnected {
			if err := pingStore(esURL); err == nil {
				es.isConnected = true
				AllConnected = true

				logrus.Infof("Event Store is back online, there are %d unsent events that will begin to send.", len(es.queue))
			}
			continue
		}

		// Skip if there's nothing in the queue.
		if len(es.queue) == 0 {
			continue
		}

		// A downward loop for the queue.
		for i := len(es.queue) - 1; i >= 0; i-- {
			e := es.queue[i]
			//
			<-time.After(time.Millisecond * 2)
			// create the stream write.
			writer := es.NewStreamWriter(e.stream)
			// Append the event in the stream.
			err := writer.Append(nil, goes.NewEvent("", "", e.data, e.meta))
			if err != nil {
				// ERR HANDLE
				continue
			}
			//
			QueueTotal--
			// Remove the event from the queue since it has been sent.
			es.queue = append(es.queue[:i], es.queue[i+1:]...)
		}
	}
}

func (es *eventstore) send(stream string, data interface{}, meta interface{}) {
	// Prepend to keep the order, because the push queue is a downward loop.
	es.queue = append([]event{event{stream, data, meta}}, es.queue...)
	//
	SentTotal++
	//
	QueueTotal++
	//
	if !es.isConnected {
		logrus.Warningf("The `%s` event will be sent when the Event Store is back online. (Queue length: %d)", stream, len(es.queue))
	}
}
<commit_msg>Cleaned eventstore, use new logger<commit_after>package eventstore

import (
	"encoding/json"
	"errors"
	"net/http"
	"net/url"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/TeaMeow/KitSvc/module/log"
	"github.com/TeaMeow/KitSvc/shared/eventutil"
	"github.com/jetbasrawi/go.geteventstore"
	"github.com/parnurzeal/gorequest"
)

var (
	// AllConnected returns true when the event stores were all connected.
	AllConnected = false
	// SentTotal returns the total of the sent event.
	SentTotal = 0
	// RecvTotal returns the total of the received event.
	RecvTotal = 0
	// QueueTotal returns the total of the event are still in the queue.
	QueueTotal = 0
)

type eventstore struct {
	*goes.Client
	isConnected bool
	queue       []event
}

// event represents an event data.
type event struct {
	stream string
	data   interface{}
	meta   interface{}
}

// NewClient creates a new event store client.
func NewClient(url string, esURL string, username string, password string, e *eventutil.Engine, replayed chan<- bool, deployed <-chan bool) *eventstore {
	// Ping the Event Store to make sure it's alive.
	if err := pingStore(esURL); err != nil {
		logrus.Fatalln(err)
	}
	// Create the client to Event Store.
	client, err := goes.NewClient(nil, esURL)
	if err != nil {
		log.Logger.WithFields(logrus.Fields{
			"err":    err,
			"remote": esURL,
		}).Fatal("Cannot create the client for Event Store.")
	}
	client.SetBasicAuth(username, password)

	es := &eventstore{
		Client:      client,
		isConnected: true,
	}
	AllConnected = true

	// Capturing the events when the router was ready in the goroutine.
	go es.capture(url, e, replayed, deployed)
	// Pushing the events which are in the local queue to Event Store.
	go es.push(esURL)

	return es
}

// pingStore pings the eventstore with backoff to ensure
// a connection can be established before we proceed with the
// eventstore setup and migration.
func pingStore(url string) error {
	for i := 0; i < 30; i++ {
		_, err := http.Get(url)
		if err == nil {
			return nil
		}
		log.Logger.Info("Cannot connect to Event Store, retry in 1 second.")
		time.Sleep(time.Second)
	}
	return errors.New("Cannot connect to Event Store.")
}

// sendToRouter sends the received event data to the self router.
func sendToRouter(method string, url string, json []byte) {
	// Send the request via the HTTP client.
	resp, _, err := gorequest.
		New().
		CustomMethod(method, url).
		Send(string(json)).
		End()
	if err != nil {
		log.Logger.WithFields(logrus.Fields{
			"err":    err,
			"method": method,
			"url":    url,
			"body":   json,
		}).Error("Error occurred while sending the event to self router.")
	}
	if resp.StatusCode != 200 {
		log.Logger.WithFields(logrus.Fields{
			"status": resp.StatusCode,
			"method": method,
			"url":    url,
			"body":   json,
		}).Info("The event has been recevied by the router, but the status code wasn't 200.")
	}
}

// isEmptyEvent returns true when the event body is empty.
func isEmptyEvent(json []byte) bool {
	return string(json) == "{}"
}

// capture the incoming events.
func (es *eventstore) capture(localURL string, e *eventutil.Engine, replayed chan<- bool, deployed <-chan bool) {
	<-deployed

	playedStreams := 0
	totalStreams := len(e.Listeners)

	// Each of the listener.
	for _, l := range e.Listeners {
		r := es.NewStreamReader(l.Stream)

		go func(l eventutil.Listener) {
			replayedStream := false
			startTime := time.Now()
			totalEvents := 0

			// Read the next event.
			for r.Next() {

				// Error occurred.
				if r.Err() != nil {
					switch r.Err().(type) {

					// Continue if there's no more event.
					case *goes.ErrNoMoreEvents:
						// We've replayed all the events in the stream since there're no more events can read,
						// so we mark the stream as `replayed`, we'll continue to the next step once all the streams were replayed.
						if !replayedStream {
							// Mark the stream has been replayed.
							playedStreams++
							replayedStream = true
							// Show the statistic of the replayed stream.
							log.Logger.WithFields(logrus.Fields{
								"stream": l.Stream,
								"wasted": time.Since(startTime).Seconds(),
								"amount": totalEvents,
							}).Info("The event stream has been replayed successfully.")
							// Set the `played` as true once we have replayed all the event streams.
							if playedStreams >= totalStreams {
								close(replayed)
							}
						}
						// When there are no more events in the stream, set LongPoll.
						// The server will wait for 15 seconds in this case or until
						// events become available on the stream.
						r.LongPoll(15)

					// Create an empty event if the stream hasn't been created.
					case *goes.ErrNotFound:
						writer := es.NewStreamWriter(l.Stream)

						err := writer.Append(nil, goes.NewEvent("", "", map[string]string{}, map[string]string{}))
						if err != nil {
							log.Logger.WithFields(logrus.Fields{
								"stream": l.Stream,
								"err":    err,
							}).Fatal("Error occurred while creating an empty stream.")
						}
						continue

						// Sleep for few seconds and try again if the Event Store was not connected.
					case *url.Error, *goes.ErrTemporarilyUnavailable:
						es.isConnected = false
						AllConnected = false
						<-time.After(time.Duration(3) * time.Second)

						// Other errors.
					default:
						log.Logger.WithFields(logrus.Fields{
							"stream": l.Stream,
							"err":    r.Err(),
						}).Error("Error occurred while reading the incoming event.")
						continue
					}

					// We received the event, and we're going to make a http request,
					// abd send it to out own Gin router.
				} else {
					// Get the event body.
					json, err := r.EventResponse().Event.Data.(*json.RawMessage).MarshalJSON()
					if err != nil {
						log.Logger.WithFields(logrus.Fields{
							"stream": l.Stream,
							"err":    err,
						}).Error("Cannot parse the event data, the event has been skipped.")
						continue
					}
					// Skip the empty json event,
					// because that might be the one which we used to create the new stream.
					if isEmptyEvent(json) {
						continue
					}
					// Counters.
					totalEvents++
					RecvTotal++
					// Send the received data to the router,
					// so we can process the event in the router.
					go sendToRouter(l.Method, localURL+l.Path, json)

					continue
				}
			}
		}(l)
	}
}

// push the events which are in the queue to the event store.
func (es *eventstore) push(esURL string) {
	for {
		<-time.After(time.Millisecond * 10)

		// Ping the Event Store to see if it's back online or not.
		if !es.isConnected {
			if err := pingStore(esURL); err == nil {
				es.isConnected = true
				AllConnected = true
				log.Logger.WithFields(logrus.Fields{
					"unsent": len(es.queue),
				}).Info("Event Store is back online, the unsent events that will begin to send.")
			}
			continue
		}

		// Skip if there's nothing in the queue.
		if len(es.queue) == 0 {
			continue
		}

		// A downward loop for the queue.
		for i := len(es.queue) - 1; i >= 0; i-- {
			e := es.queue[i]
			// Wait a little bit for another event.
			<-time.After(time.Millisecond * 2)
			// create the stream write.
			writer := es.NewStreamWriter(e.stream)
			// Append the event in the stream.
			err := writer.Append(nil, goes.NewEvent("", "", e.data, e.meta))
			if err != nil {
				log.Logger.WithFields(logrus.Fields{
					"stream": e.stream,
					"meta":   e.meta,
				}).Error("Error occurred while pushing the event to the stream of Event Store.")
				continue
			}
			QueueTotal--
			// Remove the event from the queue since it has been sent.
			es.queue = append(es.queue[:i], es.queue[i+1:]...)
		}
	}
}

// send the event to the specified stream.
func (es *eventstore) send(stream string, data interface{}, meta interface{}) {
	// Prepend the event to the queue to keep the order,
	// because the event queue is a downward loop.
	es.queue = append([]event{event{stream, data, meta}}, es.queue...)

	// Counters.
	SentTotal++
	QueueTotal++

	if !es.isConnected {
		log.Logger.WithFields(logrus.Fields{
			"stream": stream,
			"unsent": len(es.queue),
		}).Warning("Event will be sent when the Event Store is back online.")
	}
}
<|endoftext|>


<commit_before>package libvirt

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

	"github.com/davecgh/go-spew/spew"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	libvirt "github.com/libvirt/libvirt-go"
	"github.com/libvirt/libvirt-go-xml"
)

// deprecated, now defaults to not use it, but we warn the user
const skipQemuAgentEnvVar = "TF_SKIP_QEMU_AGENT"

// if explicitly enabled
const useQemuAgentEnvVar = "TF_USE_QEMU_AGENT"

const domWaitLeaseStillWaiting = "waiting-addresses"
const domWaitLeaseDone = "all-addresses-obtained"

var errDomainInvalidState = errors.New("invalid state for domain")

func domainWaitForLeases(domain *libvirt.Domain, waitForLeases []*libvirtxml.DomainInterface,
	timeout time.Duration, rd *schema.ResourceData) error {
	waitFunc := func() (interface{}, string, error) {

		state, err := domainGetState(*domain)
		if err != nil {
			return false, "", err
		}

		for _, fatalState := range []string{"crashed", "shutoff", "shutdown", "pmsuspended"} {
			if state == fatalState {
				return false, "", errDomainInvalidState
			}
		}

		if state != "running" {
			return false, domWaitLeaseStillWaiting, nil
		}

		// check we have IPs for all the interfaces we are waiting for
		for _, iface := range waitForLeases {
			found, ignore, err := domainIfaceHasAddress(*domain, *iface, rd)
			if err != nil {
				return false, "", err
			}
			if ignore {
				log.Printf("[DEBUG] we don't care about the IP address for %+v", iface)
				continue
			}
			if !found {
				log.Printf("[DEBUG] IP address not found for iface=%+v: will try in a while", strings.ToUpper(iface.MAC.Address))
				return false, domWaitLeaseStillWaiting, nil
			}
		}

		log.Printf("[DEBUG] all the %d IP addresses obtained for the domain", len(waitForLeases))
		return true, domWaitLeaseDone, nil
	}

	stateConf := &resource.StateChangeConf{
		Pending:    []string{domWaitLeaseStillWaiting},
		Target:     []string{domWaitLeaseDone},
		Refresh:    waitFunc,
		Timeout:    timeout,
		MinTimeout: 10 * time.Second,
		Delay:      5 * time.Second,
	}

	_, err := stateConf.WaitForState()
	log.Print("[DEBUG] wait-for-leases was successful")
	return err
}

func domainIfaceHasAddress(domain libvirt.Domain, iface libvirtxml.DomainInterface, rd *schema.ResourceData) (found bool, ignore bool, err error) {

	mac := strings.ToUpper(iface.MAC.Address)
	if mac == "" {
		log.Printf("[DEBUG] Can't wait without a MAC address: ignoring interface %+v.\n", iface)
		// we can't get the ip without a mac address
		return false, true, nil
	}

	log.Printf("[DEBUG] waiting for network address for iface=%s\n", mac)
	ifacesWithAddr, err := domainGetIfacesInfo(domain, rd)
	if err != nil {
		return false, false, fmt.Errorf("Error retrieving interface addresses: %s", err)
	}
	log.Printf("[DEBUG] ifaces with addresses: %+v\n", ifacesWithAddr)

	for _, ifaceWithAddr := range ifacesWithAddr {
		if mac == strings.ToUpper(ifaceWithAddr.Hwaddr) {
			log.Printf("[DEBUG] found IPs for MAC=%+v: %+v\n", mac, ifaceWithAddr.Addrs)
			return true, false, nil
		}
	}

	log.Printf("[DEBUG] %+v doesn't have IP address(es) yet...\n", mac)
	return false, false, nil
}

func domainGetState(domain libvirt.Domain) (string, error) {
	state, _, err := domain.GetState()
	if err != nil {
		return "", err
	}

	var stateStr string

	switch state {
	case libvirt.DOMAIN_NOSTATE:
		stateStr = "nostate"
	case libvirt.DOMAIN_RUNNING:
		stateStr = "running"
	case libvirt.DOMAIN_BLOCKED:
		stateStr = "blocked"
	case libvirt.DOMAIN_PAUSED:
		stateStr = "paused"
	case libvirt.DOMAIN_SHUTDOWN:
		stateStr = "shutdown"
	case libvirt.DOMAIN_CRASHED:
		stateStr = "crashed"
	case libvirt.DOMAIN_PMSUSPENDED:
		stateStr = "pmsuspended"
	case libvirt.DOMAIN_SHUTOFF:
		stateStr = "shutoff"
	default:
		stateStr = fmt.Sprintf("unknown: %v", state)
	}

	return stateStr, nil
}

func domainIsRunning(domain libvirt.Domain) (bool, error) {
	state, _, err := domain.GetState()
	if err != nil {
		return false, fmt.Errorf("Couldn't get state of domain: %s", err)
	}

	return state == libvirt.DOMAIN_RUNNING, nil
}

func domainGetIfacesInfo(domain libvirt.Domain, rd *schema.ResourceData) ([]libvirt.DomainInterface, error) {
	qemuAgentEnabled := rd.Get("qemu_agent").(bool)
	if qemuAgentEnabled {
		// get all the interfaces using the qemu-agent, this includes also
		// interfaces that are not attached to networks managed by libvirt
		// (eg. bridges, macvtap,...)
		log.Print("[DEBUG] fetching networking interfaces using qemu-agent")
		interfaces := qemuAgentGetInterfacesInfo(&domain, true)
		if len(interfaces) > 0 {
			// the agent will always return all the interfaces, both the
			// ones managed by libvirt and the ones attached to bridge interfaces
			// or macvtap. Hence it has the highest priority
			return interfaces, nil
		}
	} else {
		log.Printf("[DEBUG] qemu-agent is not used")
	}
	var interfaces []libvirt.DomainInterface

	// get all the interfaces attached to libvirt networks
	log.Print("[DEBUG] no interfaces could be obtained with qemu-agent: falling back to the libvirt API")

	domainRunningNow, err := domainIsRunning(domain)
	if err != nil {
		return interfaces, err
	}
	if !domainRunningNow {
		log.Print("[DEBUG] no interfaces could be obtained with libvirt API: domain not running")
		return interfaces, nil
	}

	interfaces, err = domain.ListAllInterfaceAddresses(libvirt.DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE)
	if err != nil {
		switch err.(type) {
		default:
			return interfaces, fmt.Errorf("Error retrieving interface addresses: %s", err)
		case libvirt.Error:
			virErr := err.(libvirt.Error)
			if virErr.Code != libvirt.ERR_OPERATION_INVALID || virErr.Domain != libvirt.FROM_QEMU {
				return interfaces, fmt.Errorf("Error retrieving interface addresses: %s", err)
			}
		}
	}
	log.Printf("[DEBUG] Interfaces info obtained with libvirt API:\n%s\n", spew.Sdump(interfaces))

	return interfaces, nil
}
<commit_msg>Do not try contacting qemu-agent if domain is not running<commit_after>package libvirt

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

	"github.com/davecgh/go-spew/spew"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	libvirt "github.com/libvirt/libvirt-go"
	"github.com/libvirt/libvirt-go-xml"
)

// deprecated, now defaults to not use it, but we warn the user
const skipQemuAgentEnvVar = "TF_SKIP_QEMU_AGENT"

// if explicitly enabled
const useQemuAgentEnvVar = "TF_USE_QEMU_AGENT"

const domWaitLeaseStillWaiting = "waiting-addresses"
const domWaitLeaseDone = "all-addresses-obtained"

var errDomainInvalidState = errors.New("invalid state for domain")

func domainWaitForLeases(domain *libvirt.Domain, waitForLeases []*libvirtxml.DomainInterface,
	timeout time.Duration, rd *schema.ResourceData) error {
	waitFunc := func() (interface{}, string, error) {

		state, err := domainGetState(*domain)
		if err != nil {
			return false, "", err
		}

		for _, fatalState := range []string{"crashed", "shutoff", "shutdown", "pmsuspended"} {
			if state == fatalState {
				return false, "", errDomainInvalidState
			}
		}

		if state != "running" {
			return false, domWaitLeaseStillWaiting, nil
		}

		// check we have IPs for all the interfaces we are waiting for
		for _, iface := range waitForLeases {
			found, ignore, err := domainIfaceHasAddress(*domain, *iface, rd)
			if err != nil {
				return false, "", err
			}
			if ignore {
				log.Printf("[DEBUG] we don't care about the IP address for %+v", iface)
				continue
			}
			if !found {
				log.Printf("[DEBUG] IP address not found for iface=%+v: will try in a while", strings.ToUpper(iface.MAC.Address))
				return false, domWaitLeaseStillWaiting, nil
			}
		}

		log.Printf("[DEBUG] all the %d IP addresses obtained for the domain", len(waitForLeases))
		return true, domWaitLeaseDone, nil
	}

	stateConf := &resource.StateChangeConf{
		Pending:    []string{domWaitLeaseStillWaiting},
		Target:     []string{domWaitLeaseDone},
		Refresh:    waitFunc,
		Timeout:    timeout,
		MinTimeout: 10 * time.Second,
		Delay:      5 * time.Second,
	}

	_, err := stateConf.WaitForState()
	log.Print("[DEBUG] wait-for-leases was successful")
	return err
}

func domainIfaceHasAddress(domain libvirt.Domain, iface libvirtxml.DomainInterface, rd *schema.ResourceData) (found bool, ignore bool, err error) {

	mac := strings.ToUpper(iface.MAC.Address)
	if mac == "" {
		log.Printf("[DEBUG] Can't wait without a MAC address: ignoring interface %+v.\n", iface)
		// we can't get the ip without a mac address
		return false, true, nil
	}

	log.Printf("[DEBUG] waiting for network address for iface=%s\n", mac)
	ifacesWithAddr, err := domainGetIfacesInfo(domain, rd)
	if err != nil {
		return false, false, fmt.Errorf("Error retrieving interface addresses: %s", err)
	}
	log.Printf("[DEBUG] ifaces with addresses: %+v\n", ifacesWithAddr)

	for _, ifaceWithAddr := range ifacesWithAddr {
		if mac == strings.ToUpper(ifaceWithAddr.Hwaddr) {
			log.Printf("[DEBUG] found IPs for MAC=%+v: %+v\n", mac, ifaceWithAddr.Addrs)
			return true, false, nil
		}
	}

	log.Printf("[DEBUG] %+v doesn't have IP address(es) yet...\n", mac)
	return false, false, nil
}

func domainGetState(domain libvirt.Domain) (string, error) {
	state, _, err := domain.GetState()
	if err != nil {
		return "", err
	}

	var stateStr string

	switch state {
	case libvirt.DOMAIN_NOSTATE:
		stateStr = "nostate"
	case libvirt.DOMAIN_RUNNING:
		stateStr = "running"
	case libvirt.DOMAIN_BLOCKED:
		stateStr = "blocked"
	case libvirt.DOMAIN_PAUSED:
		stateStr = "paused"
	case libvirt.DOMAIN_SHUTDOWN:
		stateStr = "shutdown"
	case libvirt.DOMAIN_CRASHED:
		stateStr = "crashed"
	case libvirt.DOMAIN_PMSUSPENDED:
		stateStr = "pmsuspended"
	case libvirt.DOMAIN_SHUTOFF:
		stateStr = "shutoff"
	default:
		stateStr = fmt.Sprintf("unknown: %v", state)
	}

	return stateStr, nil
}

func domainIsRunning(domain libvirt.Domain) (bool, error) {
	state, _, err := domain.GetState()
	if err != nil {
		return false, fmt.Errorf("Couldn't get state of domain: %s", err)
	}

	return state == libvirt.DOMAIN_RUNNING, nil
}

func domainGetIfacesInfo(domain libvirt.Domain, rd *schema.ResourceData) ([]libvirt.DomainInterface, error) {
	domainRunningNow, err := domainIsRunning(domain)
	if err != nil {
		return []libvirt.DomainInterface{}, err
	}
	if !domainRunningNow {
		log.Print("[DEBUG] no interfaces could be obtained: domain not running")
		return []libvirt.DomainInterface{}, nil
	}

	qemuAgentEnabled := rd.Get("qemu_agent").(bool)
	if qemuAgentEnabled {
		// get all the interfaces using the qemu-agent, this includes also
		// interfaces that are not attached to networks managed by libvirt
		// (eg. bridges, macvtap,...)
		log.Print("[DEBUG] fetching networking interfaces using qemu-agent")
		interfaces := qemuAgentGetInterfacesInfo(&domain, true)
		if len(interfaces) > 0 {
			// the agent will always return all the interfaces, both the
			// ones managed by libvirt and the ones attached to bridge interfaces
			// or macvtap. Hence it has the highest priority
			return interfaces, nil
		}
	} else {
		log.Printf("[DEBUG] qemu-agent is not used")
	}
	var interfaces []libvirt.DomainInterface

	// get all the interfaces attached to libvirt networks
	log.Print("[DEBUG] no interfaces could be obtained with qemu-agent: falling back to the libvirt API")

	interfaces, err = domain.ListAllInterfaceAddresses(libvirt.DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE)
	if err != nil {
		switch err.(type) {
		default:
			return interfaces, fmt.Errorf("Error retrieving interface addresses: %s", err)
		case libvirt.Error:
			virErr := err.(libvirt.Error)
			if virErr.Code != libvirt.ERR_OPERATION_INVALID || virErr.Domain != libvirt.FROM_QEMU {
				return interfaces, fmt.Errorf("Error retrieving interface addresses: %s", err)
			}
		}
	}
	log.Printf("[DEBUG] Interfaces info obtained with libvirt API:\n%s\n", spew.Sdump(interfaces))

	return interfaces, nil
}
<|endoftext|>


<commit_before>/*
	Copyright 2015-2016 the original author or authors.

	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at

		http://www.apache.org/licenses/LICENSE-2.0

	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
 */
package models

import (
	"bufio"
	"flag"
	"io"
	"os"
	"strings"
	"fmt"
	"github.com/qiniu/log"
)

type Licenses struct {
	License *[]License `yaml:"license,flow" xml:"license,omitempty"`
}

type Developers struct {
	Developer *[]Developer `yaml:"developer,flow" xml:"developer,omitempty"`
}

type Contributors struct {
	Contributor *[]Contributor `yaml:"contributor,flow" xml:"contributor,omitempty"`
}

type MailingLists struct {
	MailingList []MailingList `yaml:"mailingList,flow" xml:"mailingList,omitempty"`
}

type Modules struct {
	Module *[]string `yaml:"module,flow" xml:"module,omitempty"`
}

type Dependencies struct {
	Dependency *[]Dependency `yaml:"dependency,flow" xml:"dependency,omitempty"`
}

type Repositories struct {
	Repository *[]Repository `yaml:"repository,flow" xml:"repository,omitempty"`
}

type PluginRepositories struct {
	PluginRepository *[]Repository `yaml:"pluginRepository,flow" xml:"pluginRepository,omitempty"`
}

type Reports struct {
	Project *[]project `yaml:"project,flow" xml:"project,omitempty"`
	Report  *[]string  `yaml:"report,flow" xml:"report,omitempty"`
}

type Profiles struct {
	Profile *[]Profile `yaml:"profile,flow" xml:"profile,omitempty"`
}

type Roles struct {
	Role *[]string `yaml:"role,flow" xml:"role,omitempty"`
}

type Notifiers struct {
	Notifier *[]Notifier `yaml:"notifier,flow" xml:"notifier,omitempty"`
}

type Resources struct {
	Resource *[]Resource `yaml:"resource,flow" xml:"resource,omitempty"`
}

type TestResources struct {
	TestResource *[]Resource `yaml:"testResource,flow" xml:"testResource,omitempty"`
}

type Filters struct {
	Filter *[]string `yaml:"filter,flow" xml:"filter,omitempty"`
}

type Plugins struct {
	Plugin *[]Plugin `yaml:"plugin,flow" xml:"plugin,omitempty"`
}

type Executions struct {
	Execution *[]PluginExecution `yaml:"execution,flow" xml:"execution,omitempty"`
}

type Goals struct {
	Project *[]project `yaml:"project,flow" xml:"project,omitempty"`
	Goal    *[]string  `yaml:"goal,flow" xml:"goal,omitempty"`
}

type Exclusions struct {
	Exclusion *[]Exclusion `yaml:"exclusion,flow" xml:"exclusion,omitempty"`
}

type Includes struct {
	Include *[]string `yaml:"include,flow" xml:"include,omitempty"`
}

type Excludes struct {
	Exclude *[]string `yaml:"exclude,flow" xml:"exclude,omitempty"`
}

type ReportPlugins struct {
	Plugin *[]ReportPlugin `yaml:"plugin,flow" xml:"plugin,omitempty"`
}

type ReportSets struct {
	ReportSet *[]ReportSet `yaml:"reportSet,flow" xml:"reportSet,omitempty"`
}

type Extensions struct {
	Extension *[]Extension `yaml:"extension,flow" xml:"extension,omitempty"`
}

type OtherArchives struct {
	OtherArchive *[]string `yaml:"otherArchive,flow" xml:"otherArchive,omitempty"`
}

type Argument struct {
	SystemVersion *bool

	New          *bool
	NewWebapp    *bool
	NewScheduler *bool

	Path *string
	Yaml *string

	Parent *Parent

	GroupId    *string
	ArtifactId *string
	Version    *string

	/* Default Webapp or Scheduler General Only */
	Javadoc    *bool
	Source     *bool
	Checkstyle *bool
	Findbugs   *bool
	License    *bool
	Pmd        *bool

	Port *string

}

func (this *Argument) Parse() {
	this.SystemVersion = flag.Bool("v", false, "NanoGo版本")

	this.New = flag.Bool("new", false, "新建项目")
	this.NewWebapp = flag.Bool("web", false, "新建基于NanoFramework的Web项目")
	this.NewScheduler = flag.Bool("scheduler", false, "新建基于NanoFramework的任务调度项目")

	this.Path = flag.String("path", pwd(), "创建项目路径,默认使用当前路径")
	this.Yaml = flag.String("yaml", "", "Yaml配置文件路径")

	parent := flag.String("parent", "org.nanoframework:super:6", "Maven顶级POM依赖, 格式: groupId:artifactId:version")
	resp := flag.String("resp", "", "Maven项目资源定义, 格式: groupId:artifactId:version, version为可选项, 默认使用0.0.1")

	this.Javadoc = flag.Bool("no-doc", false, "移除插件: maven-javadoc-plugin")
	this.Source = flag.Bool("no-src", false, "移除插件: maven-source-plugin")
	this.Checkstyle = flag.Bool("no-chk", false, "移除插件: maven-checkstyle-plugin")
	this.Findbugs = flag.Bool("no-fb", false, "移除插件: findbugs-maven-plugin")
	this.License = flag.Bool("no-license", false, "移除插件: license-maven-plugin")
	this.Pmd = flag.Bool("no-pmd", false, "移除插件: maven-pmd-plugin")

	this.Port = flag.String("port", "7000", "项目默认端口")

	flag.Parse()

	if !strings.HasSuffix(*this.Path, "/") {
		path := *this.Path
		path += "/"
		this.Path = &path
	}

	if *parent != "" {
		parents := strings.Split(*parent, ":")
		parentsLen := len(parents)
		if parentsLen < 3 {
			fmt.Println("无效的Maven顶级POM资源定义(-parent), 格式: groupId:artifactId:version")
			os.Exit(1)
		}

		p := Parent{}
		p.GroupId = parents[0]
		p.ArtifactId = parents[1]
		p.Version = parents[2]
		this.Parent = &p
	}

	defaultVersion := "0.0.1-SNAPSHOT"
	if *resp != "" {
		resps := strings.Split(*resp, ":")
		respsLen := len(resps)
		if respsLen < 2 {
			fmt.Println("无效的Maven项目资源定义(-resp), 格式: groupId:artifactId:version")
			os.Exit(1)
		} else {
			this.GroupId = &resps[0]
			this.ArtifactId = &resps[1]
			if respsLen > 2 {
				version := resps[2]
				if !strings.HasSuffix(version, "-SNAPSHOT") {
					version += "-SNAPSHOT"
				}

				this.Version = &version
			} else {
				this.Version = &defaultVersion
			}
		}
	} else {
		empty := ""
		this.GroupId = &empty
		this.ArtifactId = &empty
		this.Version = &defaultVersion
	}
}

func (this *Argument) Validation() bool {
	if *this.GroupId == "" || *this.ArtifactId == "" {
		return false
	}

	return true
}

func (this *Argument) ExistYaml() bool {
	var yamlPath string
	if *this.Yaml == "" {
		yamlPath = pwd() + "/nanogo.yml"
	} else {
		yamlPath = *this.Yaml
	}

	if file, err := os.Open(yamlPath); err != nil && os.IsNotExist(err) {
		return false
	} else {
		defer file.Close()
		return true
	}
}

func (this *Argument) LoadYaml() (pom *string) {
	var yamlPath string
	if *this.Yaml == "" {
		yamlPath = pwd() + "/nanogo.yml"
	} else {
		yamlPath = *this.Yaml
	}

	if file, err := os.Open(yamlPath); err != nil && os.IsNotExist(err) {
		log.Fatalf("文件操作异常: ", err)
	} else {
		defer file.Close()
		reader := bufio.NewReader(file)

		data := ""
		for {
			if d, err := reader.ReadString('\n'); err == io.EOF {
				break
			} else {
				data += d
			}
		}

		pom = &data
	}

	return
}

func pwd() (path string) {
	if p, err := os.Getwd(); err != nil {
		log.Fatalf("获取当前路径异常: ", err)
	} else {
		path = p
	}

	return
}
<commit_msg>修改parent参数默认值: org.nanoframework:super:7<commit_after>/*
	Copyright 2015-2016 the original author or authors.

	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at

		http://www.apache.org/licenses/LICENSE-2.0

	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
 */
package models

import (
	"bufio"
	"flag"
	"io"
	"os"
	"strings"
	"fmt"
	"github.com/qiniu/log"
)

type Licenses struct {
	License *[]License `yaml:"license,flow" xml:"license,omitempty"`
}

type Developers struct {
	Developer *[]Developer `yaml:"developer,flow" xml:"developer,omitempty"`
}

type Contributors struct {
	Contributor *[]Contributor `yaml:"contributor,flow" xml:"contributor,omitempty"`
}

type MailingLists struct {
	MailingList []MailingList `yaml:"mailingList,flow" xml:"mailingList,omitempty"`
}

type Modules struct {
	Module *[]string `yaml:"module,flow" xml:"module,omitempty"`
}

type Dependencies struct {
	Dependency *[]Dependency `yaml:"dependency,flow" xml:"dependency,omitempty"`
}

type Repositories struct {
	Repository *[]Repository `yaml:"repository,flow" xml:"repository,omitempty"`
}

type PluginRepositories struct {
	PluginRepository *[]Repository `yaml:"pluginRepository,flow" xml:"pluginRepository,omitempty"`
}

type Reports struct {
	Project *[]project `yaml:"project,flow" xml:"project,omitempty"`
	Report  *[]string  `yaml:"report,flow" xml:"report,omitempty"`
}

type Profiles struct {
	Profile *[]Profile `yaml:"profile,flow" xml:"profile,omitempty"`
}

type Roles struct {
	Role *[]string `yaml:"role,flow" xml:"role,omitempty"`
}

type Notifiers struct {
	Notifier *[]Notifier `yaml:"notifier,flow" xml:"notifier,omitempty"`
}

type Resources struct {
	Resource *[]Resource `yaml:"resource,flow" xml:"resource,omitempty"`
}

type TestResources struct {
	TestResource *[]Resource `yaml:"testResource,flow" xml:"testResource,omitempty"`
}

type Filters struct {
	Filter *[]string `yaml:"filter,flow" xml:"filter,omitempty"`
}

type Plugins struct {
	Plugin *[]Plugin `yaml:"plugin,flow" xml:"plugin,omitempty"`
}

type Executions struct {
	Execution *[]PluginExecution `yaml:"execution,flow" xml:"execution,omitempty"`
}

type Goals struct {
	Project *[]project `yaml:"project,flow" xml:"project,omitempty"`
	Goal    *[]string  `yaml:"goal,flow" xml:"goal,omitempty"`
}

type Exclusions struct {
	Exclusion *[]Exclusion `yaml:"exclusion,flow" xml:"exclusion,omitempty"`
}

type Includes struct {
	Include *[]string `yaml:"include,flow" xml:"include,omitempty"`
}

type Excludes struct {
	Exclude *[]string `yaml:"exclude,flow" xml:"exclude,omitempty"`
}

type ReportPlugins struct {
	Plugin *[]ReportPlugin `yaml:"plugin,flow" xml:"plugin,omitempty"`
}

type ReportSets struct {
	ReportSet *[]ReportSet `yaml:"reportSet,flow" xml:"reportSet,omitempty"`
}

type Extensions struct {
	Extension *[]Extension `yaml:"extension,flow" xml:"extension,omitempty"`
}

type OtherArchives struct {
	OtherArchive *[]string `yaml:"otherArchive,flow" xml:"otherArchive,omitempty"`
}

type Argument struct {
	SystemVersion *bool

	New          *bool
	NewWebapp    *bool
	NewScheduler *bool

	Path *string
	Yaml *string

	Parent *Parent

	GroupId    *string
	ArtifactId *string
	Version    *string

	/* Default Webapp or Scheduler General Only */
	Javadoc    *bool
	Source     *bool
	Checkstyle *bool
	Findbugs   *bool
	License    *bool
	Pmd        *bool

	Port *string

}

func (this *Argument) Parse() {
	this.SystemVersion = flag.Bool("v", false, "NanoGo版本")

	this.New = flag.Bool("new", false, "新建项目")
	this.NewWebapp = flag.Bool("web", false, "新建基于NanoFramework的Web项目")
	this.NewScheduler = flag.Bool("scheduler", false, "新建基于NanoFramework的任务调度项目")

	this.Path = flag.String("path", pwd(), "创建项目路径,默认使用当前路径")
	this.Yaml = flag.String("yaml", "", "Yaml配置文件路径")

	parent := flag.String("parent", "org.nanoframework:super:7", "Maven顶级POM依赖, 格式: groupId:artifactId:version")
	resp := flag.String("resp", "", "Maven项目资源定义, 格式: groupId:artifactId:version, version为可选项, 默认使用0.0.1")

	this.Javadoc = flag.Bool("no-doc", false, "移除插件: maven-javadoc-plugin")
	this.Source = flag.Bool("no-src", false, "移除插件: maven-source-plugin")
	this.Checkstyle = flag.Bool("no-chk", false, "移除插件: maven-checkstyle-plugin")
	this.Findbugs = flag.Bool("no-fb", false, "移除插件: findbugs-maven-plugin")
	this.License = flag.Bool("no-license", false, "移除插件: license-maven-plugin")
	this.Pmd = flag.Bool("no-pmd", false, "移除插件: maven-pmd-plugin")

	this.Port = flag.String("port", "7000", "项目默认端口")

	flag.Parse()

	if !strings.HasSuffix(*this.Path, "/") {
		path := *this.Path
		path += "/"
		this.Path = &path
	}

	if *parent != "" {
		parents := strings.Split(*parent, ":")
		parentsLen := len(parents)
		if parentsLen < 3 {
			fmt.Println("无效的Maven顶级POM资源定义(-parent), 格式: groupId:artifactId:version")
			os.Exit(1)
		}

		p := Parent{}
		p.GroupId = parents[0]
		p.ArtifactId = parents[1]
		p.Version = parents[2]
		this.Parent = &p
	}

	defaultVersion := "0.0.1-SNAPSHOT"
	if *resp != "" {
		resps := strings.Split(*resp, ":")
		respsLen := len(resps)
		if respsLen < 2 {
			fmt.Println("无效的Maven项目资源定义(-resp), 格式: groupId:artifactId:version")
			os.Exit(1)
		} else {
			this.GroupId = &resps[0]
			this.ArtifactId = &resps[1]
			if respsLen > 2 {
				version := resps[2]
				if !strings.HasSuffix(version, "-SNAPSHOT") {
					version += "-SNAPSHOT"
				}

				this.Version = &version
			} else {
				this.Version = &defaultVersion
			}
		}
	} else {
		empty := ""
		this.GroupId = &empty
		this.ArtifactId = &empty
		this.Version = &defaultVersion
	}
}

func (this *Argument) Validation() bool {
	if *this.GroupId == "" || *this.ArtifactId == "" {
		return false
	}

	return true
}

func (this *Argument) ExistYaml() bool {
	var yamlPath string
	if *this.Yaml == "" {
		yamlPath = pwd() + "/nanogo.yml"
	} else {
		yamlPath = *this.Yaml
	}

	if file, err := os.Open(yamlPath); err != nil && os.IsNotExist(err) {
		return false
	} else {
		defer file.Close()
		return true
	}
}

func (this *Argument) LoadYaml() (pom *string) {
	var yamlPath string
	if *this.Yaml == "" {
		yamlPath = pwd() + "/nanogo.yml"
	} else {
		yamlPath = *this.Yaml
	}

	if file, err := os.Open(yamlPath); err != nil && os.IsNotExist(err) {
		log.Fatalf("文件操作异常: ", err)
	} else {
		defer file.Close()
		reader := bufio.NewReader(file)

		data := ""
		for {
			if d, err := reader.ReadString('\n'); err == io.EOF {
				break
			} else {
				data += d
			}
		}

		pom = &data
	}

	return
}

func pwd() (path string) {
	if p, err := os.Getwd(); err != nil {
		log.Fatalf("获取当前路径异常: ", err)
	} else {
		path = p
	}

	return
}
<|endoftext|>


<commit_before>package plugin

import (
	"zvr/server"
	"fmt"
	"zvr/utils"
	"strings"
)

const (
	VR_CREATE_EIP = "/createeip"
	VR_REMOVE_EIP = "/removeeip"
	VR_SYNC_EIP = "/synceip"
)

type eipInfo struct {
	VipIp string `json:"vipIp"`
	PrivateMac string `json:"privateMac"`
	GuestIp string `json:"guestIp"`
	SnatInboundTraffic bool `json:"snatInboundTraffic"`
}

type setEipCmd struct {
	Eip eipInfo `json:"eip"`
}

type removeEipCmd struct {
	Eip eipInfo `json:"eip"`
}

type syncEipCmd struct {
	Eips []eipInfo `json:"eips"`
}

var EIP_SNAT_START_RULE_NUM = 5000

func makeEipDescription(info eipInfo) string {
	return fmt.Sprintf("EIP-%v-%v-%v", info.VipIp, info.GuestIp, info.PrivateMac)
}

func setEip(tree *server.VyosConfigTree, eip eipInfo) {
	des := makeEipDescription(eip)
	nicname, err := utils.GetNicNameByIp(eip.VipIp); utils.PanicOnError(err)

	if r := tree.FindSnatRuleDescription(des); r == nil {
		tree.SetSnat(
			fmt.Sprintf("description %v", des),
			fmt.Sprintf("outbound-interface any"),
			fmt.Sprintf("source address %v", eip.GuestIp),
			fmt.Sprintf("translation address %v", eip.VipIp),
		)
	}

	if r := tree.FindDnatRuleDescription(des); r == nil {
		tree.SetDnat(
			fmt.Sprintf("description %v", des),
			fmt.Sprintf("inbound-interface any"),
			fmt.Sprintf("destination address %v", eip.VipIp),
			fmt.Sprintf("translation address %v", eip.GuestIp),
		)
	}

	if r := tree.FindFirewallRuleByDescription(nicname, "in", des); r == nil {
		tree.SetFirewallOnInterface(nicname, "in",
			fmt.Sprintf("description %v", des),
			fmt.Sprintf("destination address %v", eip.GuestIp),
			"state new enable",
			"state established enable",
			"state related enable",
			"action accept",
		)

		tree.AttachFirewallToInterface(nicname, "in")
	}

	prinicname, err := utils.GetNicNameByMac(eip.PrivateMac); utils.PanicOnError(err)
	if r := tree.FindFirewallRuleByDescription(prinicname, "in", des); r == nil {
		tree.SetFirewallOnInterface(prinicname, "in",
			fmt.Sprintf("description %v", des),
			fmt.Sprintf("source address %v", eip.GuestIp),
			"state new enable",
			"state established enable",
			"state related enable",
			"action accept",
		)

		tree.AttachFirewallToInterface(prinicname, "in")
	}
}

func deleteEip(tree *server.VyosConfigTree, eip eipInfo) {
	des := makeEipDescription(eip)
	nicname, err := utils.GetNicNameByIp(eip.VipIp); utils.PanicOnError(err)

	if r := tree.FindSnatRuleDescription(des); r != nil {
		r.Delete()
	}

	if r := tree.FindDnatRuleDescription(des); r != nil {
		r.Delete()
	}

	if r := tree.FindFirewallRuleByDescription(nicname, "in", des); r != nil {
		r.Delete()
	}

	prinicname, err := utils.GetNicNameByMac(eip.PrivateMac); utils.PanicOnError(err)
	if r := tree.FindFirewallRuleByDescription(prinicname, "in", des); r != nil {
		r.Delete()
	}
}

func createEip(ctx *server.CommandContext) interface{} {
	cmd := &setEipCmd{}
	ctx.GetCommand(cmd)
	eip := cmd.Eip

	tree := server.NewParserFromShowConfiguration().Tree
	setEip(tree, eip)
	tree.Apply(false)

	return nil
}

func removeEip(ctx *server.CommandContext) interface{} {
	cmd := &removeEipCmd{}
	ctx.GetCommand(cmd)
	eip := cmd.Eip

	tree := server.NewParserFromShowConfiguration().Tree
	deleteEip(tree, eip)
	tree.Apply(false)

	return nil
}

func syncEip(ctx *server.CommandContext) interface{} {
	cmd := &syncEipCmd{}
	ctx.GetCommand(cmd)

	tree := server.NewParserFromShowConfiguration().Tree

	// delete all EIP related rules
	if rs := tree.Get("nat destination rule"); rs != nil {
		for _, r := range rs.Children() {
			if d := r.Get("description"); d != nil && strings.HasPrefix(d.Value(), "EIP") {
				r.Delete()
			}
		}
	}

	if rs := tree.Getf("nat source rule"); rs != nil {
		for _, r := range rs.Children() {
			if d := r.Get("description"); d != nil && strings.HasPrefix(d.Value(), "EIP") {
				r.Delete()
			}
		}
	}

	if rs := tree.Getf("firewall name"); rs != nil {
		for _, r := range rs.Children() {
			if rss := r.Get("rule"); rss != nil {
				for _, rr := range rss.Children() {
					if d := rr.Get("description"); d != nil && strings.HasPrefix(d.Value(), "EIP") {
						rr.Delete()
					}
				}
			}
		}
	}

	for _, eip := range cmd.Eips {
		setEip(tree, eip)
	}

	tree.Apply(false)

	return nil
}

func EipEntryPoint() {
	server.RegisterAsyncCommandHandler(VR_CREATE_EIP, server.VyosLock(createEip))
	server.RegisterAsyncCommandHandler(VR_REMOVE_EIP, server.VyosLock(removeEip))
	server.RegisterAsyncCommandHandler(VR_SYNC_EIP, server.VyosLock(syncEip))
}
<commit_msg>Only do snat at proper nic<commit_after>package plugin

import (
	"zvr/server"
	"fmt"
	"zvr/utils"
	"strings"
)

const (
	VR_CREATE_EIP = "/createeip"
	VR_REMOVE_EIP = "/removeeip"
	VR_SYNC_EIP = "/synceip"
)

type eipInfo struct {
	VipIp string `json:"vipIp"`
	PrivateMac string `json:"privateMac"`
	GuestIp string `json:"guestIp"`
	SnatInboundTraffic bool `json:"snatInboundTraffic"`
}

type setEipCmd struct {
	Eip eipInfo `json:"eip"`
}

type removeEipCmd struct {
	Eip eipInfo `json:"eip"`
}

type syncEipCmd struct {
	Eips []eipInfo `json:"eips"`
}

var EIP_SNAT_START_RULE_NUM = 5000

func makeEipDescription(info eipInfo) string {
	return fmt.Sprintf("EIP-%v-%v-%v", info.VipIp, info.GuestIp, info.PrivateMac)
}

func setEip(tree *server.VyosConfigTree, eip eipInfo) {
	des := makeEipDescription(eip)
	nicname, err := utils.GetNicNameByIp(eip.VipIp); utils.PanicOnError(err)
	prinicname, err := utils.GetNicNameByMac(eip.PrivateMac); utils.PanicOnError(err)

	if r := tree.FindSnatRuleDescription(des); r == nil {
		tree.SetSnat(
			fmt.Sprintf("description %v", des),
			fmt.Sprintf("outbound-interface %v", prinicname),
			fmt.Sprintf("source address %v", eip.GuestIp),
			fmt.Sprintf("translation address %v", eip.VipIp),
		)
	}

	if r := tree.FindDnatRuleDescription(des); r == nil {
		tree.SetDnat(
			fmt.Sprintf("description %v", des),
			fmt.Sprintf("inbound-interface any"),
			fmt.Sprintf("destination address %v", eip.VipIp),
			fmt.Sprintf("translation address %v", eip.GuestIp),
		)
	}

	if r := tree.FindFirewallRuleByDescription(nicname, "in", des); r == nil {
		tree.SetFirewallOnInterface(nicname, "in",
			fmt.Sprintf("description %v", des),
			fmt.Sprintf("destination address %v", eip.GuestIp),
			"state new enable",
			"state established enable",
			"state related enable",
			"action accept",
		)

		tree.AttachFirewallToInterface(nicname, "in")
	}

	if r := tree.FindFirewallRuleByDescription(prinicname, "in", des); r == nil {
		tree.SetFirewallOnInterface(prinicname, "in",
			fmt.Sprintf("description %v", des),
			fmt.Sprintf("source address %v", eip.GuestIp),
			"state new enable",
			"state established enable",
			"state related enable",
			"action accept",
		)

		tree.AttachFirewallToInterface(prinicname, "in")
	}
}

func deleteEip(tree *server.VyosConfigTree, eip eipInfo) {
	des := makeEipDescription(eip)
	nicname, err := utils.GetNicNameByIp(eip.VipIp); utils.PanicOnError(err)

	if r := tree.FindSnatRuleDescription(des); r != nil {
		r.Delete()
	}

	if r := tree.FindDnatRuleDescription(des); r != nil {
		r.Delete()
	}

	if r := tree.FindFirewallRuleByDescription(nicname, "in", des); r != nil {
		r.Delete()
	}

	prinicname, err := utils.GetNicNameByMac(eip.PrivateMac); utils.PanicOnError(err)
	if r := tree.FindFirewallRuleByDescription(prinicname, "in", des); r != nil {
		r.Delete()
	}
}

func createEip(ctx *server.CommandContext) interface{} {
	cmd := &setEipCmd{}
	ctx.GetCommand(cmd)
	eip := cmd.Eip

	tree := server.NewParserFromShowConfiguration().Tree
	setEip(tree, eip)
	tree.Apply(false)

	return nil
}

func removeEip(ctx *server.CommandContext) interface{} {
	cmd := &removeEipCmd{}
	ctx.GetCommand(cmd)
	eip := cmd.Eip

	tree := server.NewParserFromShowConfiguration().Tree
	deleteEip(tree, eip)
	tree.Apply(false)

	return nil
}

func syncEip(ctx *server.CommandContext) interface{} {
	cmd := &syncEipCmd{}
	ctx.GetCommand(cmd)

	tree := server.NewParserFromShowConfiguration().Tree

	// delete all EIP related rules
	if rs := tree.Get("nat destination rule"); rs != nil {
		for _, r := range rs.Children() {
			if d := r.Get("description"); d != nil && strings.HasPrefix(d.Value(), "EIP") {
				r.Delete()
			}
		}
	}

	if rs := tree.Getf("nat source rule"); rs != nil {
		for _, r := range rs.Children() {
			if d := r.Get("description"); d != nil && strings.HasPrefix(d.Value(), "EIP") {
				r.Delete()
			}
		}
	}

	if rs := tree.Getf("firewall name"); rs != nil {
		for _, r := range rs.Children() {
			if rss := r.Get("rule"); rss != nil {
				for _, rr := range rss.Children() {
					if d := rr.Get("description"); d != nil && strings.HasPrefix(d.Value(), "EIP") {
						rr.Delete()
					}
				}
			}
		}
	}

	for _, eip := range cmd.Eips {
		setEip(tree, eip)
	}

	tree.Apply(false)

	return nil
}

func EipEntryPoint() {
	server.RegisterAsyncCommandHandler(VR_CREATE_EIP, server.VyosLock(createEip))
	server.RegisterAsyncCommandHandler(VR_REMOVE_EIP, server.VyosLock(removeEip))
	server.RegisterAsyncCommandHandler(VR_SYNC_EIP, server.VyosLock(syncEip))
}
<|endoftext|>


<commit_before>package deliveryservice

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import (
	"errors"
	"fmt"
	"regexp"
	"strings"

	"github.com/apache/incubator-trafficcontrol/lib/go-log"
	"github.com/apache/incubator-trafficcontrol/lib/go-tc"
	"github.com/asaskevich/govalidator"
	validation "github.com/go-ozzo/ozzo-validation"
	"github.com/go-ozzo/ozzo-validation/is"

	"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/auth"
	"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
	"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/tovalidate"
	"github.com/jmoiron/sqlx"
	"github.com/lib/pq"
)

//we need a type alias to define functions on
type TODeliveryService tc.DeliveryServiceNullable

//the refType is passed into the handlers where a copy of its type is used to decode the json.
var refType = TODeliveryService(tc.DeliveryServiceNullable{})

func GetRefType() *TODeliveryService {
	return &refType
}

//Implementation of the Identifier, Validator interface functions
func (ds *TODeliveryService) GetID() int {
	return ds.ID
}

func (ds *TODeliveryService) GetAuditName() string {
	xmlId := *ds.XMLID
	return xmlId
}

func (ds *TODeliveryService) GetType() string {
	return "ds"
}

func (ds *TODeliveryService) SetID(i int) {
	ds.ID = i
}

func (ds *TODeliveryService) Validate(db *sqlx.DB) []error {

	noSpaces := validation.Match(regexp.MustCompile("^\\S*$"))
	noSpaces.Error("cannot contain spaces")

	noPeriods := validation.Match(regexp.MustCompile("^[^\\.]*$"))
	noPeriods.Error("cannot contain periods")

	var typeName string
	var err error
	if db != nil && ds.TypeID != nil {
		typeID := *ds.TypeID
		typeName, err = getTypeName(db, typeID)
		if err != nil {
			return []error{err}
		}
	}

	DNSRegexType := "^DNS.*$"
	HTTPRegexType := "^HTTP.*$"
	SteeringRegexType := "^STEERING.*$"
	// Custom Examples:
	// Just add isCIDR as a parameter to Validate()
	// isCIDR := validation.NewStringRule(govalidator.IsCIDR, "must be a valid CIDR address")
	isHost := validation.NewStringRule(govalidator.IsHost, "must be a valid hostname")
	errs := validation.Errors{
		"active": validation.Validate(ds.Active, validation.NotNil),
		"cdnId":  validation.Validate(ds.CDNID, validation.NotNil),
		"displayName": validation.Validate(ds.DisplayName,
			validation.Required),
		"dnsBypassIp":  validation.Validate(ds.DNSBypassIP, is.IP),
		"dnsBypassIp6": validation.Validate(ds.DNSBypassIP6, is.IPv6),
		"dscp":         validation.Validate(ds.DSCP, validation.NotNil),
		"geoLimit":     validation.Validate(ds.GeoLimit, validation.NotNil),
		"geoProvider":  validation.Validate(ds.GeoProvider, validation.NotNil),
		"infoUrl":      validation.Validate(ds.InfoURL, is.URL),
		"initialDispersion": validation.Validate(ds.InitialDispersion,
			validation.By(tovalidate.GreaterThanZero),
			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
		"ipv6RoutingEnabled": validation.Validate(ds.IPV6RoutingEnabled,
			validation.By(requiredIfMatchesTypeName([]string{SteeringRegexType, DNSRegexType, HTTPRegexType}, typeName))),
		"logsEnabled": validation.Validate(ds.LogsEnabled, validation.NotNil),
		"missLat": validation.Validate(ds.MissLat,
			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
		"missLong": validation.Validate(ds.MissLong,
			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
		"multiSiteOrigin": validation.Validate(ds.MultiSiteOrigin,
			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
		"orgServerFqdn": validation.Validate(ds.OrgServerFQDN,
			is.URL,
			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
		"protocol": validation.Validate(ds.Protocol,
			validation.By(requiredIfMatchesTypeName([]string{SteeringRegexType, DNSRegexType, HTTPRegexType}, typeName))),
		"qstringIgnore": validation.Validate(ds.QStringIgnore,
			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
		"rangeRequestHandling": validation.Validate(ds.RangeRequestHandling,
			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
		"regionalGeoBlocking": validation.Validate(ds.RegionalGeoBlocking,
			validation.NotNil),
		"routingName": validation.Validate(ds.RoutingName,
			isHost,
			noPeriods,
			validation.Length(1, 48)),
		"typeId": validation.Validate(ds.TypeID,
			validation.NotNil,
			validation.By(tovalidate.GreaterThanZero)),
		"xmlId": validation.Validate(ds.XMLID,
			validation.Required,
			noSpaces,
			validation.Length(1, 48)),
	}
	return tovalidate.ToErrors(errs)
}

func requiredIfMatchesTypeName(patterns []string, typeName string) func(interface{}) error {
	return func(value interface{}) error {

		pattern := strings.Join(patterns, "|")
		var err error
		var match bool
		if typeName != "" {
			match, err = regexp.MatchString(pattern, typeName)
			if match {
				return fmt.Errorf("is required if type is '%s'", typeName)
			}
		}
		return err
	}
}

// TODO: drichardson - refactor to the types.go once implemented.
func getTypeName(db *sqlx.DB, typeID int) (string, error) {

	query := `SELECT name from type where id=$1`

	var rows *sqlx.Rows
	var err error

	rows, err = db.Queryx(query, typeID)
	if err != nil {
		return "", err
	}
	defer rows.Close()

	typeResults := []tc.Type{}
	for rows.Next() {
		var s tc.Type
		if err = rows.StructScan(&s); err != nil {
			return "", fmt.Errorf("getting Type: %v", err)
		}
		typeResults = append(typeResults, s)
	}

	typeName := typeResults[0].Name
	return typeName, err
}

//The TODeliveryService implementation of the Updater interface
//all implementations of Updater should use transactions and return the proper errorType
//ParsePQUniqueConstraintError is used to determine if a delivery service with conflicting values exists
//if so, it will return an errorType of DataConflict and the type should be appended to the
//generic error message returned
func (ds *TODeliveryService) Update(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
	tx, err := db.Beginx()
	defer func() {
		if tx == nil {
			return
		}
		if err != nil {
			tx.Rollback()
			return
		}
		tx.Commit()
	}()

	if err != nil {
		log.Error.Printf("could not begin transaction: %v", err)
		return tc.DBError, tc.SystemError
	}
	log.Debugf("about to run exec query: %s with ds: %++v", updateDSQuery(), ds)
	resultRows, err := tx.NamedQuery(updateDSQuery(), ds)
	if err != nil {
		if err, ok := err.(*pq.Error); ok {
			err, eType := dbhelpers.ParsePQUniqueConstraintError(err)
			if eType == tc.DataConflictError {
				return errors.New("a delivery service with " + err.Error()), eType
			}
			return err, eType
		} else {
			log.Errorf("received error: %++v from update execution", err)
			return tc.DBError, tc.SystemError
		}
	}
	var lastUpdated tc.Time
	rowsAffected := 0
	for resultRows.Next() {
		rowsAffected++
		if err := resultRows.Scan(&lastUpdated); err != nil {
			log.Error.Printf("could not scan lastUpdated from insert: %s\n", err)
			return tc.DBError, tc.SystemError
		}
	}
	log.Debugf("lastUpdated: %++v", lastUpdated)
	ds.LastUpdated = lastUpdated
	if rowsAffected != 1 {
		if rowsAffected < 1 {
			return errors.New("no delivery service found with this id"), tc.DataMissingError
		} else {
			return fmt.Errorf("this update affected too many rows: %d", rowsAffected), tc.SystemError
		}
	}
	return nil, tc.NoError
}

//The TODeliveryService implementation of the Inserter interface
//all implementations of Inserter should use transactions and return the proper errorType
//ParsePQUniqueConstraintError is used to determine if a ds with conflicting values exists
//if so, it will return an errorType of DataConflict and the type should be appended to the
//generic error message returned
//The insert sql returns the id and lastUpdated values of the newly inserted ds and have
//to be added to the struct
func (ds *TODeliveryService) Insert(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
	tx, err := db.Beginx()
	defer func() {
		if tx == nil {
			return
		}
		if err != nil {
			tx.Rollback()
			return
		}
		tx.Commit()
	}()

	if err != nil {
		log.Error.Printf("could not begin transaction: %v", err)
		return tc.DBError, tc.SystemError
	}
	fmt.Printf("ds ---> %v\n", ds)
	resultRows, err := tx.NamedQuery(insertDSQuery(), ds)
	if err != nil {
		if pqerr, ok := err.(*pq.Error); ok {
			err, eType := dbhelpers.ParsePQUniqueConstraintError(pqerr)
			return errors.New("a delivery service with " + err.Error()), eType
		} else {
			log.Errorf("received non pq error: %++v from create execution", err)
			return tc.DBError, tc.SystemError
		}
	}
	var id int
	var lastUpdated tc.Time
	rowsAffected := 0
	for resultRows.Next() {
		rowsAffected++
		if err := resultRows.Scan(&id, &lastUpdated); err != nil {
			log.Error.Printf("could not scan id from insert: %s\n", err)
			return tc.DBError, tc.SystemError
		}
	}
	if rowsAffected == 0 {
		err = errors.New("no delivery service was inserted, no id was returned")
		log.Errorln(err)
		return tc.DBError, tc.SystemError
	} else if rowsAffected > 1 {
		err = errors.New("too many ids returned from delivery service insert")
		log.Errorln(err)
		return tc.DBError, tc.SystemError
	}
	ds.SetID(id)
	ds.LastUpdated = lastUpdated
	return nil, tc.NoError
}

//The DeliveryService implementation of the Deleter interface
//all implementations of Deleter should use transactions and return the proper errorType
func (ds *TODeliveryService) Delete(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
	tx, err := db.Beginx()
	defer func() {
		if tx == nil {
			return
		}
		if err != nil {
			tx.Rollback()
			return
		}
		tx.Commit()
	}()

	if err != nil {
		log.Error.Printf("could not begin transaction: %v", err)
		return tc.DBError, tc.SystemError
	}
	log.Debugf("about to run exec query: %s with Delivery Service: %++v", deleteDSQuery(), ds)
	result, err := tx.NamedExec(deleteDSQuery(), ds)
	if err != nil {
		log.Errorf("received error: %++v from delete execution", err)
		return tc.DBError, tc.SystemError
	}
	rowsAffected, err := result.RowsAffected()
	if err != nil {
		return tc.DBError, tc.SystemError
	}
	if rowsAffected != 1 {
		if rowsAffected < 1 {
			return errors.New("no delivery service with that id found"), tc.DataMissingError
		} else {
			return fmt.Errorf("this create affected too many rows: %d", rowsAffected), tc.SystemError
		}
	}
	return nil, tc.NoError
}

//TODO: drichardson - plumb these out!
func updateDSQuery() string {
	query := `UPDATE
cdn SET
dnssec_enabled=:dnssec_enabled,
domain_name=:domain_name,
name=:name
WHERE id=:id RETURNING last_updated`
	return query
}

//TODO: drichardson - plumb these out!
func insertDSQuery() string {
	query := `INSERT INTO deliveryservice (
dnssec_enabled,
domain_name,
name) VALUES (
:dnssec_enabled,
:domain_name,
:name) RETURNING id,last_updated`
	return query
}

func deleteDSQuery() string {
	query := `DELETE FROM deliveryservice
WHERE id=:id`
	return query
}
<commit_msg>added nil check for XMLID<commit_after>package deliveryservice

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import (
	"errors"
	"fmt"
	"regexp"
	"strings"

	"github.com/apache/incubator-trafficcontrol/lib/go-log"
	"github.com/apache/incubator-trafficcontrol/lib/go-tc"
	"github.com/asaskevich/govalidator"
	validation "github.com/go-ozzo/ozzo-validation"
	"github.com/go-ozzo/ozzo-validation/is"

	"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/auth"
	"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
	"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/tovalidate"
	"github.com/jmoiron/sqlx"
	"github.com/lib/pq"
)

//we need a type alias to define functions on
type TODeliveryService tc.DeliveryServiceNullable

//the refType is passed into the handlers where a copy of its type is used to decode the json.
var refType = TODeliveryService(tc.DeliveryServiceNullable{})

func GetRefType() *TODeliveryService {
	return &refType
}

//Implementation of the Identifier, Validator interface functions
func (ds *TODeliveryService) GetID() int {
	return ds.ID
}

func (ds *TODeliveryService) GetAuditName() string {
	if ds.XMLID != nil {
		return *ds.XMLID
	}
	return ""
}

func (ds *TODeliveryService) GetType() string {
	return "ds"
}

func (ds *TODeliveryService) SetID(i int) {
	ds.ID = i
}

func (ds *TODeliveryService) Validate(db *sqlx.DB) []error {

	noSpaces := validation.Match(regexp.MustCompile("^\\S*$"))
	noSpaces.Error("cannot contain spaces")

	noPeriods := validation.Match(regexp.MustCompile("^[^\\.]*$"))
	noPeriods.Error("cannot contain periods")

	var typeName string
	var err error
	if db != nil && ds.TypeID != nil {
		typeID := *ds.TypeID
		typeName, err = getTypeName(db, typeID)
		if err != nil {
			return []error{err}
		}
	}

	DNSRegexType := "^DNS.*$"
	HTTPRegexType := "^HTTP.*$"
	SteeringRegexType := "^STEERING.*$"
	// Custom Examples:
	// Just add isCIDR as a parameter to Validate()
	// isCIDR := validation.NewStringRule(govalidator.IsCIDR, "must be a valid CIDR address")
	isHost := validation.NewStringRule(govalidator.IsHost, "must be a valid hostname")
	errs := validation.Errors{
		"active": validation.Validate(ds.Active, validation.NotNil),
		"cdnId":  validation.Validate(ds.CDNID, validation.NotNil),
		"displayName": validation.Validate(ds.DisplayName,
			validation.Required),
		"dnsBypassIp":  validation.Validate(ds.DNSBypassIP, is.IP),
		"dnsBypassIp6": validation.Validate(ds.DNSBypassIP6, is.IPv6),
		"dscp":         validation.Validate(ds.DSCP, validation.NotNil),
		"geoLimit":     validation.Validate(ds.GeoLimit, validation.NotNil),
		"geoProvider":  validation.Validate(ds.GeoProvider, validation.NotNil),
		"infoUrl":      validation.Validate(ds.InfoURL, is.URL),
		"initialDispersion": validation.Validate(ds.InitialDispersion,
			validation.By(tovalidate.GreaterThanZero),
			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
		"ipv6RoutingEnabled": validation.Validate(ds.IPV6RoutingEnabled,
			validation.By(requiredIfMatchesTypeName([]string{SteeringRegexType, DNSRegexType, HTTPRegexType}, typeName))),
		"logsEnabled": validation.Validate(ds.LogsEnabled, validation.NotNil),
		"missLat": validation.Validate(ds.MissLat,
			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
		"missLong": validation.Validate(ds.MissLong,
			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
		"multiSiteOrigin": validation.Validate(ds.MultiSiteOrigin,
			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
		"orgServerFqdn": validation.Validate(ds.OrgServerFQDN,
			is.URL,
			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
		"protocol": validation.Validate(ds.Protocol,
			validation.By(requiredIfMatchesTypeName([]string{SteeringRegexType, DNSRegexType, HTTPRegexType}, typeName))),
		"qstringIgnore": validation.Validate(ds.QStringIgnore,
			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
		"rangeRequestHandling": validation.Validate(ds.RangeRequestHandling,
			validation.By(requiredIfMatchesTypeName([]string{DNSRegexType, HTTPRegexType}, typeName))),
		"regionalGeoBlocking": validation.Validate(ds.RegionalGeoBlocking,
			validation.NotNil),
		"routingName": validation.Validate(ds.RoutingName,
			isHost,
			noPeriods,
			validation.Length(1, 48)),
		"typeId": validation.Validate(ds.TypeID,
			validation.NotNil,
			validation.By(tovalidate.GreaterThanZero)),
		"xmlId": validation.Validate(ds.XMLID,
			validation.Required,
			noSpaces,
			validation.Length(1, 48)),
	}
	return tovalidate.ToErrors(errs)
}

func requiredIfMatchesTypeName(patterns []string, typeName string) func(interface{}) error {
	return func(value interface{}) error {

		pattern := strings.Join(patterns, "|")
		var err error
		var match bool
		if typeName != "" {
			match, err = regexp.MatchString(pattern, typeName)
			if match {
				return fmt.Errorf("is required if type is '%s'", typeName)
			}
		}
		return err
	}
}

// TODO: drichardson - refactor to the types.go once implemented.
func getTypeName(db *sqlx.DB, typeID int) (string, error) {

	query := `SELECT name from type where id=$1`

	var rows *sqlx.Rows
	var err error

	rows, err = db.Queryx(query, typeID)
	if err != nil {
		return "", err
	}
	defer rows.Close()

	typeResults := []tc.Type{}
	for rows.Next() {
		var s tc.Type
		if err = rows.StructScan(&s); err != nil {
			return "", fmt.Errorf("getting Type: %v", err)
		}
		typeResults = append(typeResults, s)
	}

	typeName := typeResults[0].Name
	return typeName, err
}

//The TODeliveryService implementation of the Updater interface
//all implementations of Updater should use transactions and return the proper errorType
//ParsePQUniqueConstraintError is used to determine if a delivery service with conflicting values exists
//if so, it will return an errorType of DataConflict and the type should be appended to the
//generic error message returned
func (ds *TODeliveryService) Update(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
	tx, err := db.Beginx()
	defer func() {
		if tx == nil {
			return
		}
		if err != nil {
			tx.Rollback()
			return
		}
		tx.Commit()
	}()

	if err != nil {
		log.Error.Printf("could not begin transaction: %v", err)
		return tc.DBError, tc.SystemError
	}
	log.Debugf("about to run exec query: %s with ds: %++v", updateDSQuery(), ds)
	resultRows, err := tx.NamedQuery(updateDSQuery(), ds)
	if err != nil {
		if err, ok := err.(*pq.Error); ok {
			err, eType := dbhelpers.ParsePQUniqueConstraintError(err)
			if eType == tc.DataConflictError {
				return errors.New("a delivery service with " + err.Error()), eType
			}
			return err, eType
		} else {
			log.Errorf("received error: %++v from update execution", err)
			return tc.DBError, tc.SystemError
		}
	}
	var lastUpdated tc.Time
	rowsAffected := 0
	for resultRows.Next() {
		rowsAffected++
		if err := resultRows.Scan(&lastUpdated); err != nil {
			log.Error.Printf("could not scan lastUpdated from insert: %s\n", err)
			return tc.DBError, tc.SystemError
		}
	}
	log.Debugf("lastUpdated: %++v", lastUpdated)
	ds.LastUpdated = lastUpdated
	if rowsAffected != 1 {
		if rowsAffected < 1 {
			return errors.New("no delivery service found with this id"), tc.DataMissingError
		} else {
			return fmt.Errorf("this update affected too many rows: %d", rowsAffected), tc.SystemError
		}
	}
	return nil, tc.NoError
}

//The TODeliveryService implementation of the Inserter interface
//all implementations of Inserter should use transactions and return the proper errorType
//ParsePQUniqueConstraintError is used to determine if a ds with conflicting values exists
//if so, it will return an errorType of DataConflict and the type should be appended to the
//generic error message returned
//The insert sql returns the id and lastUpdated values of the newly inserted ds and have
//to be added to the struct
func (ds *TODeliveryService) Insert(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
	tx, err := db.Beginx()
	defer func() {
		if tx == nil {
			return
		}
		if err != nil {
			tx.Rollback()
			return
		}
		tx.Commit()
	}()

	if err != nil {
		log.Error.Printf("could not begin transaction: %v", err)
		return tc.DBError, tc.SystemError
	}
	fmt.Printf("ds ---> %v\n", ds)
	resultRows, err := tx.NamedQuery(insertDSQuery(), ds)
	if err != nil {
		if pqerr, ok := err.(*pq.Error); ok {
			err, eType := dbhelpers.ParsePQUniqueConstraintError(pqerr)
			return errors.New("a delivery service with " + err.Error()), eType
		} else {
			log.Errorf("received non pq error: %++v from create execution", err)
			return tc.DBError, tc.SystemError
		}
	}
	var id int
	var lastUpdated tc.Time
	rowsAffected := 0
	for resultRows.Next() {
		rowsAffected++
		if err := resultRows.Scan(&id, &lastUpdated); err != nil {
			log.Error.Printf("could not scan id from insert: %s\n", err)
			return tc.DBError, tc.SystemError
		}
	}
	if rowsAffected == 0 {
		err = errors.New("no delivery service was inserted, no id was returned")
		log.Errorln(err)
		return tc.DBError, tc.SystemError
	} else if rowsAffected > 1 {
		err = errors.New("too many ids returned from delivery service insert")
		log.Errorln(err)
		return tc.DBError, tc.SystemError
	}
	ds.SetID(id)
	ds.LastUpdated = lastUpdated
	return nil, tc.NoError
}

//The DeliveryService implementation of the Deleter interface
//all implementations of Deleter should use transactions and return the proper errorType
func (ds *TODeliveryService) Delete(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
	tx, err := db.Beginx()
	defer func() {
		if tx == nil {
			return
		}
		if err != nil {
			tx.Rollback()
			return
		}
		tx.Commit()
	}()

	if err != nil {
		log.Error.Printf("could not begin transaction: %v", err)
		return tc.DBError, tc.SystemError
	}
	log.Debugf("about to run exec query: %s with Delivery Service: %++v", deleteDSQuery(), ds)
	result, err := tx.NamedExec(deleteDSQuery(), ds)
	if err != nil {
		log.Errorf("received error: %++v from delete execution", err)
		return tc.DBError, tc.SystemError
	}
	rowsAffected, err := result.RowsAffected()
	if err != nil {
		return tc.DBError, tc.SystemError
	}
	if rowsAffected != 1 {
		if rowsAffected < 1 {
			return errors.New("no delivery service with that id found"), tc.DataMissingError
		} else {
			return fmt.Errorf("this create affected too many rows: %d", rowsAffected), tc.SystemError
		}
	}
	return nil, tc.NoError
}

//TODO: drichardson - plumb these out!
func updateDSQuery() string {
	query := `UPDATE
cdn SET
dnssec_enabled=:dnssec_enabled,
domain_name=:domain_name,
name=:name
WHERE id=:id RETURNING last_updated`
	return query
}

//TODO: drichardson - plumb these out!
func insertDSQuery() string {
	query := `INSERT INTO deliveryservice (
dnssec_enabled,
domain_name,
name) VALUES (
:dnssec_enabled,
:domain_name,
:name) RETURNING id,last_updated`
	return query
}

func deleteDSQuery() string {
	query := `DELETE FROM deliveryservice
WHERE id=:id`
	return query
}
<|endoftext|>


<commit_before>package main

import (
	"image"
	"image/color"
	"image/gif"
	"io"
	"math"
	"math/rand"
	"os"
)

var palette = []color.Color{color.White, color.Black}

const (
	whiteIndex = 0 // First color in palette
	blackIndex = 1 // Second color in palette
)

func main() {
	lissajous(os.Stdout)
}

func lissajous(out io.Writer) {
	const (
		cycles  = 5     // Number of complete x oscillator revolutions
		res     = 0.001 // Angular resolution
		size    = 100   // Image canvas covers [-size..+size]
		nframes = 64    // Number of animation frames
		delay   = 8     // Delay between frames in 10ms units
	)
	freq := rand.Float64() * 3.0 // Relative frequency of y oscillator
	anim := gif.GIF{LoopCount: nframes}
	phase := 0.0 // Phase difference
	for i := 0; i < nframes; i++ {
		rect := image.Rect(0, 0, 2*size+1, 2*size+1)
		img := image.NewPaletted(rect, palette)
		for t := 0.0; t < cycles*2*math.Pi; t += res {
			x := math.Sin(t)
			y := math.Sin(t*freq + phase)
			img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5), blackIndex)
		}
		phase += 0.1
		anim.Delay = append(anim.Delay, delay)
		anim.Image = append(anim.Image, img)
	}
	gif.EncodeAll(out, &anim)
}
<commit_msg>Added comment<commit_after>package main

import (
	"image"
	"image/color"
	"image/gif"
	"io"
	"math"
	"math/rand"
	"os"
)

// @desc lissajous generates a gif image based on the values of variables provided

var palette = []color.Color{color.White, color.Black}

const (
	whiteIndex = 0 // First color in palette
	blackIndex = 1 // Second color in palette
)

func main() {
	lissajous(os.Stdout)
}

func lissajous(out io.Writer) {
	const (
		cycles  = 5     // Number of complete x oscillator revolutions
		res     = 0.001 // Angular resolution
		size    = 100   // Image canvas covers [-size..+size]
		nframes = 64    // Number of animation frames
		delay   = 8     // Delay between frames in 10ms units
	)
	freq := rand.Float64() * 3.0 // Relative frequency of y oscillator
	anim := gif.GIF{LoopCount: nframes}
	phase := 0.0 // Phase difference
	for i := 0; i < nframes; i++ {
		rect := image.Rect(0, 0, 2*size+1, 2*size+1)
		img := image.NewPaletted(rect, palette)
		for t := 0.0; t < cycles*2*math.Pi; t += res {
			x := math.Sin(t)
			y := math.Sin(t*freq + phase)
			img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5), blackIndex)
		}
		phase += 0.1
		anim.Delay = append(anim.Delay, delay)
		anim.Image = append(anim.Image, img)
	}
	gif.EncodeAll(out, &anim)
}
<|endoftext|>