<commit_before>package activity

import (
	"fmt"
	"reflect"

	"github.com/aws/aws-sdk-go/service/swf"
)

type ActivityHandlerFunc func(activityTask *swf.PollForActivityTaskOutput, input interface{}) (interface{}, error)

type ActivityHandler struct {
	Activity    string
	HandlerFunc ActivityHandlerFunc
	Input       interface{}
}

type CoordinatedActivityHandlerStartFunc func(*swf.PollForActivityTaskOutput, interface{}) (interface{}, error)

type CoordinatedActivityHandlerTickFunc func(*swf.PollForActivityTaskOutput, interface{}) (bool, interface{}, error)

type CoordinatedActivityHandlerCancelFunc func(*swf.PollForActivityTaskOutput, interface{}) error

type CoordinatedActivityHandlerFinishFunc func(*swf.PollForActivityTaskOutput, interface{}) error

type CoordinatedActivityHandler struct {
	// Start is called when a new activity is ready to be handled.
	Start CoordinatedActivityHandlerStartFunc

	// Tick is called regularly to process a running activity.
	// Tick that returns false, nil, nil just expresses that the job is still running.
	// Tick that returns false, &SomeStruct{}, nil will express that the job is still running and also send an 'ActivityUpdated' signal back to the FSM with SomeStruct{} as the Input.
	// Tick that returns true, &SomeStruct{}, nil, expresses that the job/activity is done and send SomeStruct{} back as the result. as well as stops heartbeating.
	// Tick that returns true, nil, nil, expresses that the job is done and send no result back, as well as stops heartbeating.
	// Tick that returns false, nil, err expresses that the job/activity failed and sends back err as the reason. as well as stops heartbeating.
	Tick CoordinatedActivityHandlerTickFunc

	// Cancel is called when a running activity receives a request to cancel
	// via heartbeat update.
	Cancel CoordinatedActivityHandlerCancelFunc

	// Finish is called at the end of handling every activity.
	// It is called no matter the outcome, eg if Start fails,
	// Tick decides to stop continuing, or the activity is canceled.
	Finish CoordinatedActivityHandlerFinishFunc

	Input    interface{}
	Activity string
}

func NewActivityHandler(activity string, handler interface{}) *ActivityHandler {
	input := inputType(handler, 1)
	output := outputType(handler, 0)
	newType := input
	if input.Kind() == reflect.Ptr {
		newType = input.Elem()
	}
	typeCheck(handler, []string{"*swf.PollForActivityTaskOutput", input.String()}, []string{output, "error"})
	return &ActivityHandler{
		Activity:    activity,
		HandlerFunc: marshalledFunc{reflect.ValueOf(handler)}.activityHandlerFunc,
		Input:       reflect.New(newType).Elem().Interface(),
	}
}

func NewCoordinatedActivityHandler(activity string, start interface{}, tick interface{}, cancel interface{}, finish interface{}) *CoordinatedActivityHandler {
	input := inputType(tick, 1)
	newType := input
	if input.Kind() == reflect.Ptr {
		newType = input.Elem()
	}
	output := outputType(tick, 1)

	typeCheck(start, []string{"*swf.PollForActivityTaskOutput", input.String()}, []string{output, "error"})
	typeCheck(tick, []string{"*swf.PollForActivityTaskOutput", input.String()}, []string{"bool", output, "error"})
	typeCheck(cancel, []string{"*swf.PollForActivityTaskOutput", input.String()}, []string{"error"})
	typeCheck(finish, []string{"*swf.PollForActivityTaskOutput", input.String()}, []string{"error"})

	return &CoordinatedActivityHandler{
		Activity: activity,
		Start:    marshalledFunc{reflect.ValueOf(start)}.handleCoordinatedActivityStart,
		Tick:     marshalledFunc{reflect.ValueOf(tick)}.handleCoordinatedActivityTick,
		Cancel:   marshalledFunc{reflect.ValueOf(cancel)}.handleCoordinatedActivityCancel,
		Finish:   marshalledFunc{reflect.ValueOf(finish)}.handleCoordinatedActivityFinish,
		Input:    reflect.New(newType).Elem().Interface(),
	}

}

func (a *ActivityHandler) ZeroInput() interface{} {
	return reflect.New(reflect.TypeOf(a.Input)).Interface()
}

type marshalledFunc struct {
	v reflect.Value
}

func (m marshalledFunc) activityHandlerFunc(task *swf.PollForActivityTaskOutput, input interface{}) (interface{}, error) {
	ret := m.v.Call([]reflect.Value{reflect.ValueOf(task), reflect.ValueOf(input)})
	return outputValue(ret[0]), errorValue(ret[1])
}

func (m marshalledFunc) longRunningActivityHandlerFunc(task *swf.PollForActivityTaskOutput, input interface{}) {
	m.v.Call([]reflect.Value{reflect.ValueOf(task), reflect.ValueOf(input)})
}

func (m marshalledFunc) handleCoordinatedActivityStart(task *swf.PollForActivityTaskOutput, input interface{}) (interface{}, error) {
	ret := m.v.Call([]reflect.Value{reflect.ValueOf(task), reflect.ValueOf(input)})
	return outputValue(ret[0]), errorValue(ret[1])
}

func (m marshalledFunc) handleCoordinatedActivityTick(task *swf.PollForActivityTaskOutput, input interface{}) (bool, interface{}, error) {
	ret := m.v.Call([]reflect.Value{reflect.ValueOf(task), reflect.ValueOf(input)})
	return outputValue(ret[0]).(bool), outputValue(ret[1]), errorValue(ret[2])
}

func (m marshalledFunc) handleCoordinatedActivityCancel(task *swf.PollForActivityTaskOutput, input interface{}) error {
	ret := m.v.Call([]reflect.Value{reflect.ValueOf(task), reflect.ValueOf(input)})
	return errorValue(ret[0])
}

func (m marshalledFunc) handleCoordinatedActivityFinish(task *swf.PollForActivityTaskOutput, input interface{}) error {
	ret := m.v.Call([]reflect.Value{reflect.ValueOf(task), reflect.ValueOf(input)})
	return errorValue(ret[0])
}

func outputValue(v reflect.Value) interface{} {
	switch v.Kind() {
	case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
		if v.IsNil() {
			return nil
		} else {
			return v.Interface()
		}
	default:
		return v.Interface()
	}
}

func errorValue(v reflect.Value) error {
	if v.IsNil() {
		return nil
	}
	return v.Interface().(error)
}

func inputType(handler interface{}, idx int) reflect.Type {
	t := reflect.TypeOf(handler)
	if reflect.Func != t.Kind() {
		panic(fmt.Sprintf("kind was %v, not Func", t.Kind()))
	}
	return t.In(idx)
}

func outputType(handler interface{}, idx int) string {
	t := reflect.TypeOf(handler)
	if reflect.Func != t.Kind() {
		panic(fmt.Sprintf("kind was %v, not Func", t.Kind()))
	}
	return t.Out(idx).String()
}

func typeCheck(typedFunc interface{}, in []string, out []string) {
	t := reflect.TypeOf(typedFunc)
	if reflect.Func != t.Kind() {
		panic(fmt.Sprintf("kind was %v, not Func", t.Kind()))
	}
	if len(in) != t.NumIn() {
		panic(fmt.Sprintf(
			"input arity was %v, not %v",
			t.NumIn(), len(in),
		))
	}

	for i, rt := range in {
		if rt != t.In(i).String() {
			panic(fmt.Sprintf(
				"type of argument %v was %v, not %v",
				i, t.In(i), rt,
			))
		}
	}

	if len(out) != t.NumOut() {
		panic(fmt.Sprintf(
			"number of return values was %v, not %v",
			t.NumOut(), len(out),
		))
	}

	for i, rt := range out {
		if rt != t.Out(i).String() {
			panic(fmt.Sprintf(
				"type of return value %v was %v, not %v",
				i, t.Out(i), rt,
			))
		}
	}
}
<commit_msg>Update Tick documentation to match the expected behavior<commit_after>package activity

import (
	"fmt"
	"reflect"

	"github.com/aws/aws-sdk-go/service/swf"
)

type ActivityHandlerFunc func(activityTask *swf.PollForActivityTaskOutput, input interface{}) (interface{}, error)

type ActivityHandler struct {
	Activity    string
	HandlerFunc ActivityHandlerFunc
	Input       interface{}
}

type CoordinatedActivityHandlerStartFunc func(*swf.PollForActivityTaskOutput, interface{}) (interface{}, error)

type CoordinatedActivityHandlerTickFunc func(*swf.PollForActivityTaskOutput, interface{}) (bool, interface{}, error)

type CoordinatedActivityHandlerCancelFunc func(*swf.PollForActivityTaskOutput, interface{}) error

type CoordinatedActivityHandlerFinishFunc func(*swf.PollForActivityTaskOutput, interface{}) error

type CoordinatedActivityHandler struct {
	// Start is called when a new activity is ready to be handled.
	Start CoordinatedActivityHandlerStartFunc

	// Tick is called regularly to process a running activity.
	// Tick that returns true, nil, nil just expresses that the job is still running.
	// Tick that returns true, &SomeStruct{}, nil will express that the job is still running and also send an 'ActivityUpdated' signal back to the FSM with SomeStruct{} as the Input.
	// Tick that returns false, &SomeStruct{}, nil, expresses that the job/activity is done and send SomeStruct{} back as the result. as well as stops heartbeating.
	// Tick that returns false, nil, nil, expresses that the job is done and send no result back, as well as stops heartbeating.
	// Tick that returns false, nil, err expresses that the job/activity failed and sends back err as the reason. as well as stops heartbeating.
	Tick CoordinatedActivityHandlerTickFunc

	// Cancel is called when a running activity receives a request to cancel
	// via heartbeat update.
	Cancel CoordinatedActivityHandlerCancelFunc

	// Finish is called at the end of handling every activity.
	// It is called no matter the outcome, eg if Start fails,
	// Tick decides to stop continuing, or the activity is canceled.
	Finish CoordinatedActivityHandlerFinishFunc

	Input    interface{}
	Activity string
}

func NewActivityHandler(activity string, handler interface{}) *ActivityHandler {
	input := inputType(handler, 1)
	output := outputType(handler, 0)
	newType := input
	if input.Kind() == reflect.Ptr {
		newType = input.Elem()
	}
	typeCheck(handler, []string{"*swf.PollForActivityTaskOutput", input.String()}, []string{output, "error"})
	return &ActivityHandler{
		Activity:    activity,
		HandlerFunc: marshalledFunc{reflect.ValueOf(handler)}.activityHandlerFunc,
		Input:       reflect.New(newType).Elem().Interface(),
	}
}

func NewCoordinatedActivityHandler(activity string, start interface{}, tick interface{}, cancel interface{}, finish interface{}) *CoordinatedActivityHandler {
	input := inputType(tick, 1)
	newType := input
	if input.Kind() == reflect.Ptr {
		newType = input.Elem()
	}
	output := outputType(tick, 1)

	typeCheck(start, []string{"*swf.PollForActivityTaskOutput", input.String()}, []string{output, "error"})
	typeCheck(tick, []string{"*swf.PollForActivityTaskOutput", input.String()}, []string{"bool", output, "error"})
	typeCheck(cancel, []string{"*swf.PollForActivityTaskOutput", input.String()}, []string{"error"})
	typeCheck(finish, []string{"*swf.PollForActivityTaskOutput", input.String()}, []string{"error"})

	return &CoordinatedActivityHandler{
		Activity: activity,
		Start:    marshalledFunc{reflect.ValueOf(start)}.handleCoordinatedActivityStart,
		Tick:     marshalledFunc{reflect.ValueOf(tick)}.handleCoordinatedActivityTick,
		Cancel:   marshalledFunc{reflect.ValueOf(cancel)}.handleCoordinatedActivityCancel,
		Finish:   marshalledFunc{reflect.ValueOf(finish)}.handleCoordinatedActivityFinish,
		Input:    reflect.New(newType).Elem().Interface(),
	}

}

func (a *ActivityHandler) ZeroInput() interface{} {
	return reflect.New(reflect.TypeOf(a.Input)).Interface()
}

type marshalledFunc struct {
	v reflect.Value
}

func (m marshalledFunc) activityHandlerFunc(task *swf.PollForActivityTaskOutput, input interface{}) (interface{}, error) {
	ret := m.v.Call([]reflect.Value{reflect.ValueOf(task), reflect.ValueOf(input)})
	return outputValue(ret[0]), errorValue(ret[1])
}

func (m marshalledFunc) longRunningActivityHandlerFunc(task *swf.PollForActivityTaskOutput, input interface{}) {
	m.v.Call([]reflect.Value{reflect.ValueOf(task), reflect.ValueOf(input)})
}

func (m marshalledFunc) handleCoordinatedActivityStart(task *swf.PollForActivityTaskOutput, input interface{}) (interface{}, error) {
	ret := m.v.Call([]reflect.Value{reflect.ValueOf(task), reflect.ValueOf(input)})
	return outputValue(ret[0]), errorValue(ret[1])
}

func (m marshalledFunc) handleCoordinatedActivityTick(task *swf.PollForActivityTaskOutput, input interface{}) (bool, interface{}, error) {
	ret := m.v.Call([]reflect.Value{reflect.ValueOf(task), reflect.ValueOf(input)})
	return outputValue(ret[0]).(bool), outputValue(ret[1]), errorValue(ret[2])
}

func (m marshalledFunc) handleCoordinatedActivityCancel(task *swf.PollForActivityTaskOutput, input interface{}) error {
	ret := m.v.Call([]reflect.Value{reflect.ValueOf(task), reflect.ValueOf(input)})
	return errorValue(ret[0])
}

func (m marshalledFunc) handleCoordinatedActivityFinish(task *swf.PollForActivityTaskOutput, input interface{}) error {
	ret := m.v.Call([]reflect.Value{reflect.ValueOf(task), reflect.ValueOf(input)})
	return errorValue(ret[0])
}

func outputValue(v reflect.Value) interface{} {
	switch v.Kind() {
	case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
		if v.IsNil() {
			return nil
		} else {
			return v.Interface()
		}
	default:
		return v.Interface()
	}
}

func errorValue(v reflect.Value) error {
	if v.IsNil() {
		return nil
	}
	return v.Interface().(error)
}

func inputType(handler interface{}, idx int) reflect.Type {
	t := reflect.TypeOf(handler)
	if reflect.Func != t.Kind() {
		panic(fmt.Sprintf("kind was %v, not Func", t.Kind()))
	}
	return t.In(idx)
}

func outputType(handler interface{}, idx int) string {
	t := reflect.TypeOf(handler)
	if reflect.Func != t.Kind() {
		panic(fmt.Sprintf("kind was %v, not Func", t.Kind()))
	}
	return t.Out(idx).String()
}

func typeCheck(typedFunc interface{}, in []string, out []string) {
	t := reflect.TypeOf(typedFunc)
	if reflect.Func != t.Kind() {
		panic(fmt.Sprintf("kind was %v, not Func", t.Kind()))
	}
	if len(in) != t.NumIn() {
		panic(fmt.Sprintf(
			"input arity was %v, not %v",
			t.NumIn(), len(in),
		))
	}

	for i, rt := range in {
		if rt != t.In(i).String() {
			panic(fmt.Sprintf(
				"type of argument %v was %v, not %v",
				i, t.In(i), rt,
			))
		}
	}

	if len(out) != t.NumOut() {
		panic(fmt.Sprintf(
			"number of return values was %v, not %v",
			t.NumOut(), len(out),
		))
	}

	for i, rt := range out {
		if rt != t.Out(i).String() {
			panic(fmt.Sprintf(
				"type of return value %v was %v, not %v",
				i, t.Out(i), rt,
			))
		}
	}
}
<|endoftext|>


<commit_before>package elk

import (
	"errors"
	"io"
	"log"
	"log/syslog"
	"net"
	"os"
	"strings"
	"time"

	"github.com/delectable/logspout/router"
)

func init() {
	router.AdapterFactories.Register(NewElkAdapter, "elk")
}

func getopt(name, dfault string) string {
	value := os.Getenv(name)
	if value == "" {
		value = dfault
	}
	return value
}

func NewElkAdapter(route *router.Route) (router.LogAdapter, error) {
	transport, found := router.AdapterTransports.Lookup(route.AdapterTransport("udp"))
	if !found {
		return nil, errors.New("unable to find adapter: " + route.Adapter)
	}
	conn, err := transport.Dial(route.Address, route.Options)
	if err != nil {
		return nil, err
	}

	return &ElkAdapter{
		route: route,
		conn:  conn,
	}, nil
}

type ElkAdapter struct {
	conn  net.Conn
	route *router.Route
}

func (a *ElkAdapter) Stream(logstream chan *router.Message) {
	for message := range logstream {
		io.WriteString(a.conn, message.Data)
		// err := a.tmpl.Execute(a.conn, &ElkMessage{message, a})
		// if err != nil {
		// 	log.Println("syslog:", err)
		// 	a.route.Close()
		// 	return
		// }
	}
}

type ElkMessage struct {
	*router.Message
	adapter *ElkAdapter
}

func (m *ElkMessage) Priority() syslog.Priority {
	switch m.Message.Source {
	case "stdout":
		return syslog.LOG_USER | syslog.LOG_INFO
	case "stderr":
		return syslog.LOG_USER | syslog.LOG_ERR
	default:
		return syslog.LOG_DAEMON | syslog.LOG_INFO
	}
}

func (m *ElkMessage) Hostname() string {
	h, _ := os.Hostname()
	return h
}

func (m *ElkMessage) CleanData() string {
	return strings.Replace(m.Data, "\n", " ", -1)
}

func (m ElkMessage) TestStr() string {
	return "test_string"
}

func (m *ElkMessage) LocalAddr() string {
	return m.adapter.conn.LocalAddr().String()
}

func (m *ElkMessage) Timestamp() string {
	return m.Message.Time.Format(time.RFC3339)
}

func (m *ElkMessage) ContainerName() string {
	return m.Message.Container.Name[1:]
}
<commit_msg>Testing ELK Adapter<commit_after>package elk

import (
	"errors"
	"io"
	// "log"
	"log/syslog"
	"net"
	"os"
	"strings"
	"time"

	"github.com/delectable/logspout/router"
)

func init() {
	router.AdapterFactories.Register(NewElkAdapter, "elk")
}

func getopt(name, dfault string) string {
	value := os.Getenv(name)
	if value == "" {
		value = dfault
	}
	return value
}

func NewElkAdapter(route *router.Route) (router.LogAdapter, error) {
	transport, found := router.AdapterTransports.Lookup(route.AdapterTransport("udp"))
	if !found {
		return nil, errors.New("unable to find adapter: " + route.Adapter)
	}
	conn, err := transport.Dial(route.Address, route.Options)
	if err != nil {
		return nil, err
	}

	return &ElkAdapter{
		route: route,
		conn:  conn,
	}, nil
}

type ElkAdapter struct {
	conn  net.Conn
	route *router.Route
}

func (a *ElkAdapter) Stream(logstream chan *router.Message) {
	for message := range logstream {
		io.WriteString(a.conn, message.Data)
		// err := a.tmpl.Execute(a.conn, &ElkMessage{message, a})
		// if err != nil {
		// 	log.Println("syslog:", err)
		// 	a.route.Close()
		// 	return
		// }
	}
}

type ElkMessage struct {
	*router.Message
	adapter *ElkAdapter
}

func (m *ElkMessage) Priority() syslog.Priority {
	switch m.Message.Source {
	case "stdout":
		return syslog.LOG_USER | syslog.LOG_INFO
	case "stderr":
		return syslog.LOG_USER | syslog.LOG_ERR
	default:
		return syslog.LOG_DAEMON | syslog.LOG_INFO
	}
}

func (m *ElkMessage) Hostname() string {
	h, _ := os.Hostname()
	return h
}

func (m *ElkMessage) CleanData() string {
	return strings.Replace(m.Data, "\n", " ", -1)
}

func (m ElkMessage) TestStr() string {
	return "test_string"
}

func (m *ElkMessage) LocalAddr() string {
	return m.adapter.conn.LocalAddr().String()
}

func (m *ElkMessage) Timestamp() string {
	return m.Message.Time.Format(time.RFC3339)
}

func (m *ElkMessage) ContainerName() string {
	return m.Message.Container.Name[1:]
}
<|endoftext|>


<commit_before>package ahrs

import (
	"math"
)

const (
	K = 0.9	// Reversion constant
)

type SimpleState struct {
	State
	rollGPS, pitchGPS, headingGPS float64 // Deg
	roll, pitch, heading          float64 // Deg
	w1, w2, gs                    float64 // Kts
	tr                            float64 // Rad/s
}

func (s *SimpleState) init(m *Measurement) {
	s.T = m.T
	if m.WValid {
		s.gs = math.Hypot(m.W1, m.W2)
		s.w1 = m.W1
		s.w2 = m.W2
	} else {
		s.gs = 0
		s.w1 = 0
		s.w2 = 0
	}

	s.tr = 0
	s.rollGPS = 0
	s.pitchGPS = 0
	if s.gs > 10 {
		s.headingGPS = math.Atan2(m.W1, m.W2) / Deg
	} else {
		s.headingGPS = 90
	}

	s.roll = s.rollGPS
	s.pitch = s.pitchGPS
	s.heading = s.headingGPS
}

func InitializeSimple(m *Measurement) (s *SimpleState) {
	s = new(SimpleState)
	s.init(m)
	return
}

func (s *SimpleState) Compute(m *Measurement) {
	dt := m.T - s.T
	if dt < 1e-6 {
		return
	}
	if dt > 10 {
		s.init(m)
		return
	}

	if m.WValid {
		s.gs = math.Hypot(m.W1, m.W2)
	}

	if m.WValid && s.gs > 10 {
		s.tr = 0.9*s.tr + 0.1*(m.W2 * (m.W1 - s.w1) - m.W1 * (m.W2 - s.w2)) / (s.gs * s.gs)/ dt
		s.rollGPS = math.Atan(s.gs*s.tr/G) / Deg
		s.pitchGPS = 0 //TODO westphae: can we do better here?
		s.headingGPS = math.Atan2(m.W1, m.W2) / Deg
		s.w1 = m.W1
		s.w2 = m.W2
	} else {
		s.tr = 0
		s.rollGPS = s.roll
		s.pitchGPS = s.pitch
		s.headingGPS = s.heading
		s.w1 = 0
		s.w2 = 0
	}

	dr := m.B1*dt
	dp := (-math.Cos(s.rollGPS*Deg)*m.B2 + math.Sin(s.rollGPS*Deg)*m.B3)*dt
	dh := (-math.Sin(s.rollGPS*Deg)*m.B2 - math.Cos(s.rollGPS*Deg)*m.B3)*dt

	if (s.pitch - s.pitchGPS)*dp > 0 {
		dp *= K
	}
	if (s.roll - s.rollGPS)*dr > 0 {
		dr *= K
	}
	ddh := s.heading - s.headingGPS
	if ddh > 180 {
		ddh -= 360
	} else if ddh < -180 {
		ddh += 360
	}
	if ddh*dh > 0 {
		dh *= K
	}

	s.pitch += dp
	s.roll += dr
	s.heading += dh

	// Regularize
	for s.pitch > 180 {
		s.pitch -= 360
	}
	for s.pitch <= -180 {
		s.pitch += 360
	}
	if s.pitch > 90 {
		s.pitch = 180 - s.pitch
		s.roll -= 180
		s.heading += 180
	}
	if s.pitch < -90 {
		s.pitch = -180 - s.pitch
		s.roll -= 180
		s.heading += 180
	}

	for s.roll > 180 {
		s.roll -= 360
	}
	for s.roll < -180 {
		s.roll += 360
	}

	for s.heading >= 360 {
		s.heading -= 360
	}
	for s.heading < 0 {
		s.heading += 360
	}

	s.E0, s.E1, s.E2, s.E3 = ToQuaternion(s.roll*Deg, s.pitch*Deg, s.heading*Deg)
	s.T = m.T
}

func (s *SimpleState) Valid() (ok bool) {
	return true
}

func (s *SimpleState) CalcRollPitchHeading() (roll float64, pitch float64, heading float64) {
	roll, pitch, heading = s.roll, s.pitch, s.heading
	return
}

func (s *SimpleState) CalcRollPitchHeadingUncertainty() (droll float64, dpitch float64, dheading float64) {
	return
}
<commit_msg>Complete the initialization of simple ahrs to avoid bad initial vals<commit_after>package ahrs

import (
	"math"
)

const (
	K = 0.9	// Reversion constant
)

type SimpleState struct {
	State
	rollGPS, pitchGPS, headingGPS float64 // Deg
	roll, pitch, heading          float64 // Deg
	w1, w2, gs                    float64 // Kts
	tr                            float64 // Rad/s
}

func (s *SimpleState) init(m *Measurement) {
	s.T = m.T
	if m.WValid {
		s.gs = math.Hypot(m.W1, m.W2)
		s.w1 = m.W1
		s.w2 = m.W2
	} else {
		s.gs = 0
		s.w1 = 0
		s.w2 = 0
	}

	s.tr = 0
	s.rollGPS = 0
	s.pitchGPS = 0
	if s.gs > 10 {
		s.headingGPS = math.Atan2(m.W1, m.W2) / Deg
	} else {
		s.headingGPS = 90
	}

	s.roll = s.rollGPS
	s.pitch = s.pitchGPS
	s.heading = s.headingGPS

	s.E0, s.E1, s.E2, s.E3 = ToQuaternion(s.roll*Deg, s.pitch*Deg, s.heading*Deg)
}

func InitializeSimple(m *Measurement) (s *SimpleState) {
	s = new(SimpleState)
	s.init(m)
	return
}

func (s *SimpleState) Compute(m *Measurement) {
	dt := m.T - s.T
	if dt < 1e-6 {
		return
	}
	if dt > 10 {
		s.init(m)
		return
	}

	if m.WValid {
		s.gs = math.Hypot(m.W1, m.W2)
	}

	if m.WValid && s.gs > 10 {
		s.tr = 0.9*s.tr + 0.1*(m.W2 * (m.W1 - s.w1) - m.W1 * (m.W2 - s.w2)) / (s.gs * s.gs)/ dt
		s.rollGPS = math.Atan(s.gs*s.tr/G) / Deg
		s.pitchGPS = 0 //TODO westphae: can we do better here?
		s.headingGPS = math.Atan2(m.W1, m.W2) / Deg
		s.w1 = m.W1
		s.w2 = m.W2
	} else {
		s.tr = 0
		s.rollGPS = s.roll
		s.pitchGPS = s.pitch
		s.headingGPS = s.heading
		s.w1 = 0
		s.w2 = 0
	}

	dr := m.B1*dt
	dp := (-math.Cos(s.rollGPS*Deg)*m.B2 + math.Sin(s.rollGPS*Deg)*m.B3)*dt
	dh := (-math.Sin(s.rollGPS*Deg)*m.B2 - math.Cos(s.rollGPS*Deg)*m.B3)*dt

	if (s.pitch - s.pitchGPS)*dp > 0 {
		dp *= K
	}
	if (s.roll - s.rollGPS)*dr > 0 {
		dr *= K
	}
	ddh := s.heading - s.headingGPS
	if ddh > 180 {
		ddh -= 360
	} else if ddh < -180 {
		ddh += 360
	}
	if ddh*dh > 0 {
		dh *= K
	}

	s.pitch += dp
	s.roll += dr
	s.heading += dh

	// Regularize
	for s.pitch > 180 {
		s.pitch -= 360
	}
	for s.pitch <= -180 {
		s.pitch += 360
	}
	if s.pitch > 90 {
		s.pitch = 180 - s.pitch
		s.roll -= 180
		s.heading += 180
	}
	if s.pitch < -90 {
		s.pitch = -180 - s.pitch
		s.roll -= 180
		s.heading += 180
	}

	for s.roll > 180 {
		s.roll -= 360
	}
	for s.roll < -180 {
		s.roll += 360
	}

	for s.heading >= 360 {
		s.heading -= 360
	}
	for s.heading < 0 {
		s.heading += 360
	}

	s.E0, s.E1, s.E2, s.E3 = ToQuaternion(s.roll*Deg, s.pitch*Deg, s.heading*Deg)
	s.T = m.T
}

func (s *SimpleState) Valid() (ok bool) {
	return true
}

func (s *SimpleState) CalcRollPitchHeading() (roll float64, pitch float64, heading float64) {
	roll, pitch, heading = s.roll, s.pitch, s.heading
	return
}

func (s *SimpleState) CalcRollPitchHeadingUncertainty() (droll float64, dpitch float64, dheading float64) {
	return
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"github.com/Syleron/PulseHA/src/netUtils"
	"github.com/Syleron/PulseHA/src/utils"
	log "github.com/Sirupsen/logrus"
	"strconv"
)

/**
 * Generate a new group in memory.
 *
 * @return string - group name
 * @return error
 */
func GroupNew() (string, error) {
	gconf.Lock()
	defer gconf.Unlock()
	if gconf.ClusterCheck() {
		groupName := GenGroupName()
		gconf.Groups[groupName] = []string{}
		return groupName, nil
	} else {
		return "", errors.New("groups can only be created in a configured cluster")
	}
}

/**
 * Remove group from memory
 *
 * @return error
 */
func GroupDelete(groupName string) error {
	gconf.Lock()
	defer gconf.Unlock()
	if GroupExist(groupName) {
		if !NodeAssignedToInterface(groupName) {
			delete(gconf.Groups, groupName)
			return nil
		}
		return errors.New("group has network interface assignments. Please remove them and try again")
	} else {
		return errors.New("unable to delete group that doesn't exist")
	}
}

/**
 * Clear out all local Groups
 */
func GroupClearLocal() {
	gconf.Lock()
	defer gconf.Unlock()
	gconf.Groups = map[string][]string{}
}

/**
 * Add floating IP to group
 *
 * @return error
 */
func GroupIpAdd(groupName string, ips []string) error {
	gconf.Lock()
	defer gconf.Unlock()
	if !GroupExist(groupName) {
		return errors.New("group does not exist")
	}
	for _, ip := range ips {
		if err := utils.ValidIPAddress(ip); err == nil {
			if len(gconf.Groups[groupName]) > 0 {
				if exists, _ := GroupIPExist(groupName, ip); !exists {
					gconf.Groups[groupName] = append(gconf.Groups[groupName], ip)
				} else {
					return errors.New(ip + " already exists in group " + groupName + ".. skipping.")
				}
			} else {
				gconf.Groups[groupName] = append(gconf.Groups[groupName], ip)
			}
		} else {
			return err
		}
	}
	return nil
}

/**
 * Remove floating IP from group
 *
 * @return error
 */
func GroupIpRemove(groupName string, ips []string) error {
	gconf.Lock()
	defer gconf.Unlock()
	if !GroupExist(groupName) {
		return errors.New("group does not exist")
	}
	for _, ip := range ips {
		if len(gconf.Groups[groupName]) > 0 {
			if exists, i := GroupIPExist(groupName, ip); exists {
				gconf.Groups[groupName] = append(gconf.Groups[groupName][:i], gconf.Groups[groupName][i+1:]...)
			} else {
				log.Warning(ip + " does not exist in group " + groupName + ".. skipping.")
			}
		}
	}
	return nil
}

/**
 * Assign a group to a node's interface
 *
 * @return error
 */
func GroupAssign(groupName, node, iface string) error {
	gconf.Lock()
	defer gconf.Unlock()
	if !GroupExist(groupName) {
		return errors.New("IP group does not exist")
	}
	if netUtils.InterfaceExist(iface) {
		if exists, _ := NodeInterfaceGroupExists(node, iface, groupName); !exists {
			gconf.Nodes[node].IPGroups[iface] = append(gconf.Nodes[node].IPGroups[iface], groupName)
		} else {
			log.Warning(groupName + " already exists in node " + node + ".. skipping.")
		}
		return nil
	}
	return errors.New("interface does not exist")
}

/**
 * Unassign a group from a node's interface
 *
 * @return error
 */
func GroupUnassign(groupName, node, iface string) error {
	gconf.Lock()
	defer gconf.Unlock()
	if !GroupExist(groupName) {
		return errors.New("IP group does not exist")
	}
	if netUtils.InterfaceExist(iface) {
		if exists, i := NodeInterfaceGroupExists(node, iface, groupName); exists {
			gconf.Nodes[node].IPGroups[iface] = append(gconf.Nodes[node].IPGroups[iface][:i], gconf.Nodes[node].IPGroups[iface][i+1:]...)
		} else {
			log.Warning(groupName + " does not exist in node " + node + ".. skipping.")
		}
		return nil
	}
	return errors.New("interface does not exist")
}

/**
 * Generates an available IP floating group name.
 */
func GenGroupName() string {
	config := gconf.GetConfig()
	totalGroups := len(config.Groups)
	for i := 1; i <= totalGroups; i++ {
		newName := "group" + strconv.Itoa(i)
		if _, ok := config.Groups[newName]; !ok {
			return newName
		}
	}
	return "group" + strconv.Itoa(totalGroups+1)
}

/**
 * Checks to see if a floating IP group already exists
 */
func GroupExist(name string) bool {
	config := gconf.GetConfig()
	if _, ok := config.Groups[name]; ok {
		return true
	}
	return false
}

/**
 * Checks to see if a floating IP already exists inside of a floating ip group
 * Returns bool - exists/not & int - slice index
 */
func GroupIPExist(name string, ip string) (bool, int) {
	config := gconf.GetConfig()
	for index, cip := range config.Groups[name] {
		if ip == cip {
			return true, index
		}
	}
	return false, -1
}

/**
 * function to get the nodes and interfaces that relate to the specified node
 */
func getGroupNodes(group string) ([]string, []string) {
	var hosts []string
	var interfaces []string
	var found = false
	config := gconf.GetConfig()
	for name, node := range config.Nodes {
		for iface, groupNameSlice := range node.IPGroups {
			for _, groupName := range groupNameSlice {
				if group == groupName {
					hosts = append(hosts, name)
					interfaces = append(interfaces, iface)
					found = true
				}
			}
		}
	}
	if found {
		return hosts, interfaces
	}
	return nil, nil
}

/**
 * Make a group of IPs active
 */
func makeGroupActive(iface string, groupName string) {
	//log.Infof("Bringing up IPs on Interface: %s, Group: %s", iface, groupName)
	// gconf.Reload()
	configCopy := gconf.GetConfig()
	bringUpIPs(iface, configCopy.Groups[groupName])
	//garp?
}

func makeGroupPassive(iface string, groupName string) {
	//log.Infof("Bringing down IPs on Interface: %s, Group: %s", iface, groupName)
	// gconf.Reload()
	configCopy := gconf.GetConfig()
	bringDownIPs(iface, configCopy.Groups[groupName])
	//garp?
}
<commit_msg>assign/unassign groups bring up/down floating IPs<commit_after>package main

import (
	"errors"
	"github.com/Syleron/PulseHA/src/netUtils"
	"github.com/Syleron/PulseHA/src/utils"
	log "github.com/Sirupsen/logrus"
	"strconv"
)

/**
 * Generate a new group in memory.
 *
 * @return string - group name
 * @return error
 */
func GroupNew() (string, error) {
	gconf.Lock()
	defer gconf.Unlock()
	if gconf.ClusterCheck() {
		groupName := GenGroupName()
		gconf.Groups[groupName] = []string{}
		return groupName, nil
	} else {
		return "", errors.New("groups can only be created in a configured cluster")
	}
}

/**
 * Remove group from memory
 *
 * @return error
 */
func GroupDelete(groupName string) error {
	gconf.Lock()
	defer gconf.Unlock()
	if GroupExist(groupName) {
		if !NodeAssignedToInterface(groupName) {
			delete(gconf.Groups, groupName)
			return nil
		}
		return errors.New("group has network interface assignments. Please remove them and try again")
	} else {
		return errors.New("unable to delete group that doesn't exist")
	}
}

/**
 * Clear out all local Groups
 */
func GroupClearLocal() {
	gconf.Lock()
	defer gconf.Unlock()
	gconf.Groups = map[string][]string{}
}

/**
 * Add floating IP to group
 *
 * @return error
 */
func GroupIpAdd(groupName string, ips []string) error {
	gconf.Lock()
	defer gconf.Unlock()
	if !GroupExist(groupName) {
		return errors.New("group does not exist")
	}
	for _, ip := range ips {
		if err := utils.ValidIPAddress(ip); err == nil {
			if len(gconf.Groups[groupName]) > 0 {
				if exists, _ := GroupIPExist(groupName, ip); !exists {
					gconf.Groups[groupName] = append(gconf.Groups[groupName], ip)
				} else {
					return errors.New(ip + " already exists in group " + groupName + ".. skipping.")
				}
			} else {
				gconf.Groups[groupName] = append(gconf.Groups[groupName], ip)
			}
		} else {
			return err
		}
	}
	return nil
}

/**
 * Remove floating IP from group
 *
 * @return error
 */
func GroupIpRemove(groupName string, ips []string) error {
	gconf.Lock()
	defer gconf.Unlock()
	if !GroupExist(groupName) {
		return errors.New("group does not exist")
	}
	for _, ip := range ips {
		if len(gconf.Groups[groupName]) > 0 {
			if exists, i := GroupIPExist(groupName, ip); exists {
				gconf.Groups[groupName] = append(gconf.Groups[groupName][:i], gconf.Groups[groupName][i+1:]...)
			} else {
				log.Warning(ip + " does not exist in group " + groupName + ".. skipping.")
			}
		}
	}
	return nil
}

/**
 * Assign a group to a node's interface
 *
 * @return error
 */
func GroupAssign(groupName, node, iface string) error {
	gconf.Lock()
	defer gconf.Unlock()
	if !GroupExist(groupName) {
		return errors.New("IP group does not exist")
	}
	if netUtils.InterfaceExist(iface) {
		if exists, _ := NodeInterfaceGroupExists(node, iface, groupName); !exists {
			// Add the group
			gconf.Nodes[node].IPGroups[iface] = append(gconf.Nodes[node].IPGroups[iface], groupName)
			// make the group active
			makeGroupActive(iface, groupName)
		} else {
			log.Warning(groupName + " already exists in node " + node + ".. skipping.")
		}
		return nil
	}
	return errors.New("interface does not exist")
}

/**
 * Unassign a group from a node's interface
 *
 * @return error
 */
func GroupUnassign(groupName, node, iface string) error {
	gconf.Lock()
	defer gconf.Unlock()
	if !GroupExist(groupName) {
		return errors.New("IP group does not exist")
	}
	if netUtils.InterfaceExist(iface) {
		if exists, i := NodeInterfaceGroupExists(node, iface, groupName); exists {
			// make the group passive before removing it
			makeGroupPassive(iface, groupName)
			// Remove it
			gconf.Nodes[node].IPGroups[iface] = append(gconf.Nodes[node].IPGroups[iface][:i], gconf.Nodes[node].IPGroups[iface][i+1:]...)
		} else {
			log.Warning(groupName + " does not exist in node " + node + ".. skipping.")
		}
		return nil
	}
	return errors.New("interface does not exist")
}

/**
 * Generates an available IP floating group name.
 */
func GenGroupName() string {
	config := gconf.GetConfig()
	totalGroups := len(config.Groups)
	for i := 1; i <= totalGroups; i++ {
		newName := "group" + strconv.Itoa(i)
		if _, ok := config.Groups[newName]; !ok {
			return newName
		}
	}
	return "group" + strconv.Itoa(totalGroups+1)
}

/**
 * Checks to see if a floating IP group already exists
 */
func GroupExist(name string) bool {
	config := gconf.GetConfig()
	if _, ok := config.Groups[name]; ok {
		return true
	}
	return false
}

/**
 * Checks to see if a floating IP already exists inside of a floating ip group
 * Returns bool - exists/not & int - slice index
 */
func GroupIPExist(name string, ip string) (bool, int) {
	config := gconf.GetConfig()
	for index, cip := range config.Groups[name] {
		if ip == cip {
			return true, index
		}
	}
	return false, -1
}

/**
 * function to get the nodes and interfaces that relate to the specified node
 */
func getGroupNodes(group string) ([]string, []string) {
	var hosts []string
	var interfaces []string
	var found = false
	config := gconf.GetConfig()
	for name, node := range config.Nodes {
		for iface, groupNameSlice := range node.IPGroups {
			for _, groupName := range groupNameSlice {
				if group == groupName {
					hosts = append(hosts, name)
					interfaces = append(interfaces, iface)
					found = true
				}
			}
		}
	}
	if found {
		return hosts, interfaces
	}
	return nil, nil
}

/**
 * Make a group of IPs active
 */
func makeGroupActive(iface string, groupName string) {
	//log.Infof("Bringing up IPs on Interface: %s, Group: %s", iface, groupName)
	// gconf.Reload()
	configCopy := gconf.GetConfig()
	bringUpIPs(iface, configCopy.Groups[groupName])
	//garp?
}

func makeGroupPassive(iface string, groupName string) {
	//log.Infof("Bringing down IPs on Interface: %s, Group: %s", iface, groupName)
	// gconf.Reload()
	configCopy := gconf.GetConfig()
	bringDownIPs(iface, configCopy.Groups[groupName])
	//garp?
}
<|endoftext|>


<commit_before>package id3

import (
	"fmt"
	"os"
	"strconv"
)

const (
	header_length = 10
	version_begin = 6
	version_end   = 9
	count_limit   = 5
	full_limit    = 40
)

var (
	header []byte
)

type frame struct {
	name string
	data string
	size int
	init byte
}

type Id3V2 struct {
	Items []frame
}

func NewV2() *Id3V2 {
	i := new(Id3V2)

	return i
}

func (i *Id3V2) Parse(f string) {
	b := fileToBuffer(f, header_length, 0)
	if getString(b[0:tag_start]) != "ID3" {
		return
	}

	file, _ := os.Open(f)
	file.Seek(header_length, 0)

	// version information after the identifier
	major_version, _ := strconv.Atoi(fmt.Sprintf("%d", b[tag_start]))
	minor_version, _ := strconv.Atoi(fmt.Sprintf("%d", b[tag_start+1]))

	// we shouldn't need to use the flag at all

	// final four are the size
	size := int(b[version_begin]<<21 | b[version_begin+1]<<14 | b[version_end-1]<<7 | b[version_end])
	_, _, _ = major_version, minor_version, size
	// the annoying thing is, I don't see any of these
	//  really being an issue or actually being used
	//  at all

	// in testing, some frames have broken names
	//  this loop is to attempt to skip and find further
	//  valid names
	count := 0
	for c := 0; c < full_limit; c++ {
		fr := getFrame(file)

		if fr.IsValid() {
			i.Items = append(i.Items, *fr)
		} else {
			count += 1
			if count > count_limit {
				break
			}
		}
	}
}

func (f *frame) Name() string {
	return f.name
}

func (f *frame) Content() string {
	return f.data
}

func (f *frame) Length() string {
	return strconv.Itoa(f.size)
}

func (f *frame) Explain() string {
	switch f.name {
	case "COMM":
		return "Comments"
	case "PRIV":
		return "Private Frame"
	case "TALB":
		return "Album Name"
	case "TCOM":
		return "Composer"
	case "TCON":
		return "Content Type"
	case "TCOP":
		return "Copyright"
	case "TDAT":
		return "Date"
	case "TENC":
		return "Encoded by"
	case "TIT2":
		return "Title"
	case "TLAN":
		return "Language"
	case "TOPE":
		return "Original Artist"
	case "TPE1":
		return "Lead Performer"
	case "TPUB":
		return "Publisher"
	case "TRCK":
		return "Track Number"
	case "TSSE":
		return "Encoding Settings"
	case "TXXX":
		return "User Text"
	case "TYER":
		return "Year"
	case "WXXX":
		return "Provided URL"
	default:
		return "Unknown"
	}
}

func (f *frame) IsValid() bool {
	if len(f.name) != 4 || f.size < 1 {
		if len(f.name) > 0 {
			fmt.Printf("Invalid name, found [%s]\n", f.name)
		} else if len(f.data) > 0 {
			fmt.Printf("Data with no name, [%s]\n", f.data)
		}

		return false
	}

	return true
}

func getFrame(f *os.File) *frame {
	fr := new(frame)

	tag := make([]byte, 4)
	size := make([]byte, 4)
	padding := make([]byte, 2)

	f.Read(tag)
	fr.init = tag[0]
	fr.name = getString(tag)

	f.Read(size)
	fr.size = int(size[0]<<21 | size[1]<<14 | size[2]<<7 | size[3])

	frame_content := make([]byte, fr.size)

	f.Read(padding)
	f.Read(frame_content)

	if len(frame_content) > 1 && frame_content[0] == 0 {
		fr.data = getString(frame_content[1:])
		fr.size = fr.size - 1
	} else if len(frame_content) == 1 && frame_content[0] == 0 {
		fr.data = ""
		fr.size = 0
	} else {
		fr.data = getString(frame_content)
	}

	return fr
}
<commit_msg>Adding more frame types<commit_after>package id3

import (
	"fmt"
	"os"
	"strconv"
)

const (
	header_length = 10
	version_begin = 6
	version_end   = 9
	count_limit   = 5
	full_limit    = 40
)

var (
	header []byte
)

type frame struct {
	name string
	data string
	size int
	init byte
}

type Id3V2 struct {
	Items []frame
}

func NewV2() *Id3V2 {
	i := new(Id3V2)

	return i
}

func (i *Id3V2) Parse(f string) {
	b := fileToBuffer(f, header_length, 0)
	if getString(b[0:tag_start]) != "ID3" {
		return
	}

	file, _ := os.Open(f)
	file.Seek(header_length, 0)

	// version information after the identifier
	major_version, _ := strconv.Atoi(fmt.Sprintf("%d", b[tag_start]))
	minor_version, _ := strconv.Atoi(fmt.Sprintf("%d", b[tag_start+1]))

	// we shouldn't need to use the flag at all

	// final four are the size
	size := int(b[version_begin]<<21 | b[version_begin+1]<<14 | b[version_end-1]<<7 | b[version_end])
	_, _, _ = major_version, minor_version, size
	// the annoying thing is, I don't see any of these
	//  really being an issue or actually being used
	//  at all

	// in testing, some frames have broken names
	//  this loop is to attempt to skip and find further
	//  valid names
	count := 0
	for c := 0; c < full_limit; c++ {
		fr := getFrame(file)

		if fr.IsValid() {
			i.Items = append(i.Items, *fr)
		} else {
			count += 1
			if count > count_limit {
				break
			}
		}
	}
}

func (f *frame) Name() string {
	return f.name
}

func (f *frame) Content() string {
	return f.data
}

func (f *frame) Length() string {
	return strconv.Itoa(f.size)
}

func (f *frame) Explain() string {
	switch f.name {
	case "COMM":
		return "Comments"
	case "PRIV":
		return "Private Frame"
	case "TALB":
		return "Album Name"
	case "TBPM":
		return "Beats Per Minute"
	case "TCOM":
		return "Composer"
	case "TCON":
		return "Content Type"
	case "TCOP":
		return "Copyright"
	case "TDAT":
		return "Date"
	case "TENC":
		return "Encoded by"
	case "TEXT":
		return "Lyricist"
	case "TFLT":
		return "File Type"
	case "TIME":
		return "Time"
	case "TIT2":
		return "Title"
	case "TLAN":
		return "Language"
	case "TLEN":
		return "Length"
	case "TOAL":
		return "Original Album"
	case "TOLY":
		return "Original Lyricist"
	case "TOPE":
		return "Original Artist"
	case "TORY":
		return "Original Release Year"
	case "TPE1":
		return "Lead Performer"
	case "TPUB":
		return "Publisher"
	case "TRCK":
		return "Track Number"
	case "TSSE":
		return "Encoding Settings"
	case "TXXX":
		return "User Text"
	case "TYER":
		return "Year"
	case "WXXX":
		return "Provided URL"
	default:
		return "Unknown"
	}
}

func (f *frame) IsValid() bool {
	if len(f.name) != 4 || f.size < 1 {
		if len(f.name) > 0 {
			fmt.Printf("Invalid name, found [%s]\n", f.name)
		} else if len(f.data) > 0 {
			fmt.Printf("Data with no name, [%s]\n", f.data)
		}

		return false
	}

	return true
}

func getFrame(f *os.File) *frame {
	fr := new(frame)

	tag := make([]byte, 4)
	size := make([]byte, 4)
	padding := make([]byte, 2)

	f.Read(tag)
	fr.init = tag[0]
	fr.name = getString(tag)

	f.Read(size)
	fr.size = int(size[0]<<21 | size[1]<<14 | size[2]<<7 | size[3])

	frame_content := make([]byte, fr.size)

	f.Read(padding)
	f.Read(frame_content)

	if len(frame_content) > 1 && frame_content[0] == 0 {
		fr.data = getString(frame_content[1:])
		fr.size = fr.size - 1
	} else if len(frame_content) == 1 && frame_content[0] == 0 {
		fr.data = ""
		fr.size = 0
	} else {
		fr.data = getString(frame_content)
	}

	return fr
}
<|endoftext|>


<commit_before>// ClawIO - Scalable Distributed High-Performance Synchronisation and Sharing Service
//
// Copyright (C) 2015  Hugo González Labrador <clawio@hugo.labkode.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. See file COPYNG.

// Package apiserver contains the functions to create the HTTP/HTTPS API server
package apiserver

import (
	"fmt"
	"github.com/clawio/clawiod/Godeps/_workspace/src/github.com/gorilla/handlers"
	"github.com/clawio/clawiod/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/clawio/clawiod/pkg/config"
	"io"

	"net/http"
	"time"

	apidisp "github.com/clawio/clawiod/pkg/api/dispatcher"
	authdisp "github.com/clawio/clawiod/pkg/auth/dispatcher"
	storagedisp "github.com/clawio/clawiod/pkg/storage/dispatcher"

	"github.com/clawio/clawiod/pkg/logger"

	"github.com/clawio/clawiod/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid"
	"github.com/clawio/clawiod/Godeps/_workspace/src/github.com/tylerb/graceful"
)

// APIServer is the interface that api servers must implement.
type APIServer interface {
	Start() error
	StopChan() <-chan struct{}
	Stop()
}

type apiServer struct {
	cfg       *config.Config
	logWriter io.Writer
	reqWriter io.Writer

	apidisp apidisp.Dispatcher
	adisp   authdisp.Dispatcher
	sdisp   storagedisp.Dispatcher

	srv *graceful.Server
}

// New returns a new APIServer
func New(cfg *config.Config, w io.Writer, rw io.Writer, apidisp apidisp.Dispatcher, adisp authdisp.Dispatcher, sdisp storagedisp.Dispatcher) APIServer {
	srv := &graceful.Server{
		NoSignalHandling: true,
		Timeout:          10 * time.Second,
		Server: &http.Server{
			Addr: fmt.Sprintf(":%d", cfg.GetDirectives().Port),
		},
	}
	return &apiServer{cfg: cfg, logWriter: w, reqWriter: rw, apidisp: apidisp, adisp: adisp, sdisp: sdisp, srv: srv}
}

func (s *apiServer) Start() error {
	s.srv.Server.Handler = s.handleRequest()
	if s.cfg.GetDirectives().TLSEnabled == true {
		return s.srv.ListenAndServeTLS(s.cfg.GetDirectives().TLSCertificate, s.cfg.GetDirectives().TLSCertificatePrivateKey)
	}
	return s.srv.ListenAndServe()
}
func (s *apiServer) StopChan() <-chan struct{} {
	return s.srv.StopChan()
}
func (s *apiServer) Stop() {
	s.srv.Stop(10 * time.Second)
}

func (s *apiServer) handleRequest() http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		/******************************************
		 ** 1. Create logger for request    *******
		 ******************************************/
		log := logger.New(s.logWriter, uuid.New())
		log.Infof("Request started: %+v", map[string]interface{}{"URL": r.RequestURI})
		defer func() {
			log.Info("Request finished")
		}()

		rootCtx := context.Background()
		ctx := context.WithValue(rootCtx, "log", log)
		ctx = context.WithValue(ctx, "adisp", s.adisp)
		ctx = context.WithValue(ctx, "sdisp", s.sdisp)
		ctx = context.WithValue(ctx, "cfg", s.cfg)

		s.apidisp.HandleRequest(ctx, w, r)

	}

	if s.cfg.GetDirectives().LogRequests == true {
		return handlers.CombinedLoggingHandler(s.reqWriter, http.HandlerFunc(fn))
	}
	return http.HandlerFunc(fn)
}
<commit_msg>Catch panics and return 500<commit_after>// ClawIO - Scalable Distributed High-Performance Synchronisation and Sharing Service
//
// Copyright (C) 2015  Hugo González Labrador <clawio@hugo.labkode.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. See file COPYNG.

// Package apiserver contains the functions to create the HTTP/HTTPS API server
package apiserver

import (
	"errors"
	"fmt"
	"github.com/clawio/clawiod/Godeps/_workspace/src/github.com/gorilla/handlers"
	"github.com/clawio/clawiod/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/clawio/clawiod/pkg/config"
	"io"

	"net/http"
	"time"

	apidisp "github.com/clawio/clawiod/pkg/api/dispatcher"
	authdisp "github.com/clawio/clawiod/pkg/auth/dispatcher"
	storagedisp "github.com/clawio/clawiod/pkg/storage/dispatcher"

	"github.com/clawio/clawiod/pkg/logger"

	"github.com/clawio/clawiod/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid"
	"github.com/clawio/clawiod/Godeps/_workspace/src/github.com/tylerb/graceful"
)

// APIServer is the interface that api servers must implement.
type APIServer interface {
	Start() error
	StopChan() <-chan struct{}
	Stop()
}

type apiServer struct {
	cfg       *config.Config
	logWriter io.Writer
	reqWriter io.Writer

	apidisp apidisp.Dispatcher
	adisp   authdisp.Dispatcher
	sdisp   storagedisp.Dispatcher

	srv *graceful.Server
}

// New returns a new APIServer
func New(cfg *config.Config, w io.Writer, rw io.Writer, apidisp apidisp.Dispatcher, adisp authdisp.Dispatcher, sdisp storagedisp.Dispatcher) APIServer {
	srv := &graceful.Server{
		NoSignalHandling: true,
		Timeout:          10 * time.Second,
		Server: &http.Server{
			Addr: fmt.Sprintf(":%d", cfg.GetDirectives().Port),
		},
	}
	return &apiServer{cfg: cfg, logWriter: w, reqWriter: rw, apidisp: apidisp, adisp: adisp, sdisp: sdisp, srv: srv}
}

func (s *apiServer) Start() error {
	s.srv.Server.Handler = s.handleRequest()
	if s.cfg.GetDirectives().TLSEnabled == true {
		return s.srv.ListenAndServeTLS(s.cfg.GetDirectives().TLSCertificate, s.cfg.GetDirectives().TLSCertificatePrivateKey)
	}
	return s.srv.ListenAndServe()
}
func (s *apiServer) StopChan() <-chan struct{} {
	return s.srv.StopChan()
}
func (s *apiServer) Stop() {
	s.srv.Stop(10 * time.Second)
}

func (s *apiServer) handleRequest() http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		/******************************************
		 ** 1. Create logger for request    *******
		 ******************************************/
		log := logger.New(s.logWriter, uuid.New())
		log.Infof("Request started: %+v", map[string]interface{}{"URL": r.RequestURI})
		defer func() {
			log.Info("Request finished")

			// Catch panic and return 500
			var err error
			r := recover()
			if r != nil {
				switch t := r.(type) {
				case string:
					err = errors.New(t)
				case error:
					err = t
				default:
					err = errors.New("Unknown error")
				}
				log.Fatal(err.Error())
				http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
				return
			}
		}()

		rootCtx := context.Background()
		ctx := context.WithValue(rootCtx, "log", log)
		ctx = context.WithValue(ctx, "adisp", s.adisp)
		ctx = context.WithValue(ctx, "sdisp", s.sdisp)
		ctx = context.WithValue(ctx, "cfg", s.cfg)

		s.apidisp.HandleRequest(ctx, w, r)

	}

	if s.cfg.GetDirectives().LogRequests == true {
		return handlers.CombinedLoggingHandler(s.reqWriter, http.HandlerFunc(fn))
	}
	return http.HandlerFunc(fn)
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

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 apiserver

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"runtime/debug"
	"strings"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/golang/glog"
)

// RESTStorage is a generic interface for RESTful storage services
type RESTStorage interface {
	List(labels.Selector) (interface{}, error)
	Get(id string) (interface{}, error)
	Delete(id string) (<-chan interface{}, error)
	Extract(body []byte) (interface{}, error)
	Create(interface{}) (<-chan interface{}, error)
	Update(interface{}) (<-chan interface{}, error)
}

// WorkFunc is used to perform any time consuming work for an api call, after
// the input has been validated. Pass one of these to MakeAsync to create an
// appropriate return value for the Update, Delete, and Create methods.
type WorkFunc func() (result interface{}, err error)

// MakeAsync takes a function and executes it, delivering the result in the way required
// by RESTStorage's Update, Delete, and Create methods.
func MakeAsync(fn WorkFunc) <-chan interface{} {
	channel := make(chan interface{})
	go func() {
		defer util.HandleCrash()
		obj, err := fn()
		if err != nil {
			channel <- &api.Status{
				Status:  api.StatusFailure,
				Details: err.Error(),
			}
		} else {
			channel <- obj
		}
		// 'close' is used to signal that no further values will
		// be written to the channel. Not strictly necessary, but
		// also won't hurt.
		close(channel)
	}()
	return channel
}

// ApiServer is an HTTPHandler that delegates to RESTStorage objects.
// It handles URLs of the form:
// ${prefix}/${storage_key}[/${object_name}]
// Where 'prefix' is an arbitrary string, and 'storage_key' points to a RESTStorage object stored in storage.
//
// TODO: consider migrating this to go-restful which is a more full-featured version of the same thing.
type ApiServer struct {
	prefix  string
	storage map[string]RESTStorage
	ops     *Operations
}

// New creates a new ApiServer object.
// 'storage' contains a map of handlers.
// 'prefix' is the hosting path prefix.
func New(storage map[string]RESTStorage, prefix string) *ApiServer {
	return &ApiServer{
		storage: storage,
		prefix:  prefix,
		ops:     NewOperations(),
	}
}

func (server *ApiServer) handleIndex(w http.ResponseWriter) {
	w.WriteHeader(http.StatusOK)
	// TODO: serve this out of a file?
	data := "Welcome to Kubernetes"
	fmt.Fprint(w, data)
}

// HTTP Handler interface
func (server *ApiServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if x := recover(); x != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprint(w, "apiserver panic. Look in log for details.")
			glog.Infof("ApiServer panic'd on %v %v: %#v\n%s\n", req.Method, req.RequestURI, x, debug.Stack())
		}
	}()
	logger := MakeLogged(req, w)
	w = logger
	defer logger.Log()
	url, err := url.ParseRequestURI(req.RequestURI)
	if err != nil {
		server.error(err, w)
		return
	}
	if url.Path == "/index.html" || url.Path == "/" || url.Path == "" {
		server.handleIndex(w)
		return
	}
	if !strings.HasPrefix(url.Path, server.prefix) {
		server.notFound(req, w)
		return
	}
	requestParts := strings.Split(url.Path[len(server.prefix):], "/")[1:]
	if len(requestParts) < 1 {
		server.notFound(req, w)
		return
	}
	if requestParts[0] == "operations" {
		server.handleOperationRequest(requestParts[1:], w, req)
		return
	}
	storage := server.storage[requestParts[0]]
	if storage == nil {
		logger.Addf("'%v' has no storage object", requestParts[0])
		server.notFound(req, w)
		return
	} else {
		server.handleREST(requestParts, url, req, w, storage)
	}
}

func (server *ApiServer) notFound(req *http.Request, w http.ResponseWriter) {
	w.WriteHeader(http.StatusNotFound)
	fmt.Fprintf(w, "Not Found: %#v", req)
}

func (server *ApiServer) write(statusCode int, object interface{}, w http.ResponseWriter) {
	w.WriteHeader(statusCode)
	output, err := api.Encode(object)
	if err != nil {
		server.error(err, w)
		return
	}
	w.Write(output)
}

func (server *ApiServer) error(err error, w http.ResponseWriter) {
	w.WriteHeader(500)
	fmt.Fprintf(w, "Internal Error: %#v", err)
}

func (server *ApiServer) readBody(req *http.Request) ([]byte, error) {
	defer req.Body.Close()
	body, err := ioutil.ReadAll(req.Body)
	return body, err
}

// finishReq finishes up a request, waiting until the operation finishes or, after a timeout, creating an
// Operation to recieve the result and returning its ID down the writer.
func (server *ApiServer) finishReq(out <-chan interface{}, sync bool, timeout time.Duration, w http.ResponseWriter) {
	op := server.ops.NewOperation(out)
	if sync {
		op.WaitFor(timeout)
	}
	obj, complete := op.StatusOrResult()
	if complete {
		server.write(http.StatusOK, obj, w)
	} else {
		server.write(http.StatusAccepted, obj, w)
	}
}

func parseTimeout(str string) time.Duration {
	if str != "" {
		timeout, err := time.ParseDuration(str)
		if err == nil {
			return timeout
		}
		glog.Errorf("Failed to parse: %#v '%s'", err, str)
	}
	return 30 * time.Second
}

// handleREST is the main dispatcher for the server.  It switches on the HTTP method, and then
// on path length, according to the following table:
//   Method     Path          Action
//   GET        /foo          list
//   GET        /foo/bar      get 'bar'
//   POST       /foo          create
//   PUT        /foo/bar      update 'bar'
//   DELETE     /foo/bar      delete 'bar'
// Returns 404 if the method/pattern doesn't match one of these entries
// The server accepts several query parameters:
//    sync=[false|true] Synchronous request (only applies to create, update, delete operations)
//    timeout=<duration> Timeout for synchronous requests, only applies if sync=true
//    labels= Used for filtering list operations
func (server *ApiServer) handleREST(parts []string, requestUrl *url.URL, req *http.Request, w http.ResponseWriter, storage RESTStorage) {
	sync := requestUrl.Query().Get("sync") == "true"
	timeout := parseTimeout(requestUrl.Query().Get("timeout"))
	switch req.Method {
	case "GET":
		switch len(parts) {
		case 1:
			selector, err := labels.ParseSelector(requestUrl.Query().Get("labels"))
			if err != nil {
				server.error(err, w)
				return
			}
			list, err := storage.List(selector)
			if err != nil {
				server.error(err, w)
				return
			}
			server.write(http.StatusOK, list, w)
		case 2:
			item, err := storage.Get(parts[1])
			if err != nil {
				server.error(err, w)
				return
			}
			if item == nil {
				server.notFound(req, w)
				return
			}
			server.write(http.StatusOK, item, w)
		default:
			server.notFound(req, w)
		}
	case "POST":
		if len(parts) != 1 {
			server.notFound(req, w)
			return
		}
		body, err := server.readBody(req)
		if err != nil {
			server.error(err, w)
			return
		}
		obj, err := storage.Extract(body)
		if err != nil {
			server.error(err, w)
			return
		}
		out, err := storage.Create(obj)
		if err != nil {
			server.error(err, w)
			return
		}
		server.finishReq(out, sync, timeout, w)
	case "DELETE":
		if len(parts) != 2 {
			server.notFound(req, w)
			return
		}
		out, err := storage.Delete(parts[1])
		if err != nil {
			server.error(err, w)
			return
		}
		server.finishReq(out, sync, timeout, w)
	case "PUT":
		if len(parts) != 2 {
			server.notFound(req, w)
			return
		}
		body, err := server.readBody(req)
		if err != nil {
			server.error(err, w)
		}
		obj, err := storage.Extract(body)
		if err != nil {
			server.error(err, w)
			return
		}
		out, err := storage.Update(obj)
		if err != nil {
			server.error(err, w)
			return
		}
		server.finishReq(out, sync, timeout, w)
	default:
		server.notFound(req, w)
	}
}

func (server *ApiServer) handleOperationRequest(parts []string, w http.ResponseWriter, req *http.Request) {
	if req.Method != "GET" {
		server.notFound(req, w)
	}
	if len(parts) == 0 {
		// List outstanding operations.
		list := server.ops.List()
		server.write(http.StatusOK, list, w)
		return
	}

	op := server.ops.Get(parts[0])
	if op == nil {
		server.notFound(req, w)
	}

	obj, complete := op.StatusOrResult()
	if complete {
		server.write(http.StatusOK, obj, w)
	} else {
		server.write(http.StatusAccepted, obj, w)
	}
}
<commit_msg>Add a FileServer for master's logs<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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 apiserver

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"runtime/debug"
	"strings"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/golang/glog"
)

// RESTStorage is a generic interface for RESTful storage services
type RESTStorage interface {
	List(labels.Selector) (interface{}, error)
	Get(id string) (interface{}, error)
	Delete(id string) (<-chan interface{}, error)
	Extract(body []byte) (interface{}, error)
	Create(interface{}) (<-chan interface{}, error)
	Update(interface{}) (<-chan interface{}, error)
}

// WorkFunc is used to perform any time consuming work for an api call, after
// the input has been validated. Pass one of these to MakeAsync to create an
// appropriate return value for the Update, Delete, and Create methods.
type WorkFunc func() (result interface{}, err error)

// MakeAsync takes a function and executes it, delivering the result in the way required
// by RESTStorage's Update, Delete, and Create methods.
func MakeAsync(fn WorkFunc) <-chan interface{} {
	channel := make(chan interface{})
	go func() {
		defer util.HandleCrash()
		obj, err := fn()
		if err != nil {
			channel <- &api.Status{
				Status:  api.StatusFailure,
				Details: err.Error(),
			}
		} else {
			channel <- obj
		}
		// 'close' is used to signal that no further values will
		// be written to the channel. Not strictly necessary, but
		// also won't hurt.
		close(channel)
	}()
	return channel
}

// ApiServer is an HTTPHandler that delegates to RESTStorage objects.
// It handles URLs of the form:
// ${prefix}/${storage_key}[/${object_name}]
// Where 'prefix' is an arbitrary string, and 'storage_key' points to a RESTStorage object stored in storage.
//
// TODO: consider migrating this to go-restful which is a more full-featured version of the same thing.
type ApiServer struct {
	prefix    string
	storage   map[string]RESTStorage
	ops       *Operations
	logserver http.Handler
}

// New creates a new ApiServer object.
// 'storage' contains a map of handlers.
// 'prefix' is the hosting path prefix.
func New(storage map[string]RESTStorage, prefix string) *ApiServer {
	return &ApiServer{
		storage:   storage,
		prefix:    prefix,
		ops:       NewOperations(),
		logserver: http.StripPrefix("/logs/", http.FileServer(http.Dir("/var/log/"))),
	}
}

func (server *ApiServer) handleIndex(w http.ResponseWriter) {
	w.WriteHeader(http.StatusOK)
	// TODO: serve this out of a file?
	data := "Welcome to Kubernetes"
	fmt.Fprint(w, data)
}

// HTTP Handler interface
func (server *ApiServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if x := recover(); x != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprint(w, "apiserver panic. Look in log for details.")
			glog.Infof("ApiServer panic'd on %v %v: %#v\n%s\n", req.Method, req.RequestURI, x, debug.Stack())
		}
	}()
	logger := MakeLogged(req, w)
	w = logger
	defer logger.Log()
	url, err := url.ParseRequestURI(req.RequestURI)
	if err != nil {
		server.error(err, w)
		return
	}
	if url.Path == "/index.html" || url.Path == "/" || url.Path == "" {
		server.handleIndex(w)
		return
	}
	if strings.HasPrefix(url.Path, "/logs/") {
		server.logserver.ServeHTTP(w, req)
		return
	}
	if !strings.HasPrefix(url.Path, server.prefix) {
		server.notFound(req, w)
		return
	}
	requestParts := strings.Split(url.Path[len(server.prefix):], "/")[1:]
	if len(requestParts) < 1 {
		server.notFound(req, w)
		return
	}
	if requestParts[0] == "operations" {
		server.handleOperationRequest(requestParts[1:], w, req)
		return
	}
	storage := server.storage[requestParts[0]]
	if storage == nil {
		logger.Addf("'%v' has no storage object", requestParts[0])
		server.notFound(req, w)
		return
	} else {
		server.handleREST(requestParts, url, req, w, storage)
	}
}

func (server *ApiServer) notFound(req *http.Request, w http.ResponseWriter) {
	w.WriteHeader(http.StatusNotFound)
	fmt.Fprintf(w, "Not Found: %#v", req)
}

func (server *ApiServer) write(statusCode int, object interface{}, w http.ResponseWriter) {
	w.WriteHeader(statusCode)
	output, err := api.Encode(object)
	if err != nil {
		server.error(err, w)
		return
	}
	w.Write(output)
}

func (server *ApiServer) error(err error, w http.ResponseWriter) {
	w.WriteHeader(500)
	fmt.Fprintf(w, "Internal Error: %#v", err)
}

func (server *ApiServer) readBody(req *http.Request) ([]byte, error) {
	defer req.Body.Close()
	body, err := ioutil.ReadAll(req.Body)
	return body, err
}

// finishReq finishes up a request, waiting until the operation finishes or, after a timeout, creating an
// Operation to recieve the result and returning its ID down the writer.
func (server *ApiServer) finishReq(out <-chan interface{}, sync bool, timeout time.Duration, w http.ResponseWriter) {
	op := server.ops.NewOperation(out)
	if sync {
		op.WaitFor(timeout)
	}
	obj, complete := op.StatusOrResult()
	if complete {
		server.write(http.StatusOK, obj, w)
	} else {
		server.write(http.StatusAccepted, obj, w)
	}
}

func parseTimeout(str string) time.Duration {
	if str != "" {
		timeout, err := time.ParseDuration(str)
		if err == nil {
			return timeout
		}
		glog.Errorf("Failed to parse: %#v '%s'", err, str)
	}
	return 30 * time.Second
}

// handleREST is the main dispatcher for the server.  It switches on the HTTP method, and then
// on path length, according to the following table:
//   Method     Path          Action
//   GET        /foo          list
//   GET        /foo/bar      get 'bar'
//   POST       /foo          create
//   PUT        /foo/bar      update 'bar'
//   DELETE     /foo/bar      delete 'bar'
// Returns 404 if the method/pattern doesn't match one of these entries
// The server accepts several query parameters:
//    sync=[false|true] Synchronous request (only applies to create, update, delete operations)
//    timeout=<duration> Timeout for synchronous requests, only applies if sync=true
//    labels= Used for filtering list operations
func (server *ApiServer) handleREST(parts []string, requestUrl *url.URL, req *http.Request, w http.ResponseWriter, storage RESTStorage) {
	sync := requestUrl.Query().Get("sync") == "true"
	timeout := parseTimeout(requestUrl.Query().Get("timeout"))
	switch req.Method {
	case "GET":
		switch len(parts) {
		case 1:
			selector, err := labels.ParseSelector(requestUrl.Query().Get("labels"))
			if err != nil {
				server.error(err, w)
				return
			}
			list, err := storage.List(selector)
			if err != nil {
				server.error(err, w)
				return
			}
			server.write(http.StatusOK, list, w)
		case 2:
			item, err := storage.Get(parts[1])
			if err != nil {
				server.error(err, w)
				return
			}
			if item == nil {
				server.notFound(req, w)
				return
			}
			server.write(http.StatusOK, item, w)
		default:
			server.notFound(req, w)
		}
	case "POST":
		if len(parts) != 1 {
			server.notFound(req, w)
			return
		}
		body, err := server.readBody(req)
		if err != nil {
			server.error(err, w)
			return
		}
		obj, err := storage.Extract(body)
		if err != nil {
			server.error(err, w)
			return
		}
		out, err := storage.Create(obj)
		if err != nil {
			server.error(err, w)
			return
		}
		server.finishReq(out, sync, timeout, w)
	case "DELETE":
		if len(parts) != 2 {
			server.notFound(req, w)
			return
		}
		out, err := storage.Delete(parts[1])
		if err != nil {
			server.error(err, w)
			return
		}
		server.finishReq(out, sync, timeout, w)
	case "PUT":
		if len(parts) != 2 {
			server.notFound(req, w)
			return
		}
		body, err := server.readBody(req)
		if err != nil {
			server.error(err, w)
		}
		obj, err := storage.Extract(body)
		if err != nil {
			server.error(err, w)
			return
		}
		out, err := storage.Update(obj)
		if err != nil {
			server.error(err, w)
			return
		}
		server.finishReq(out, sync, timeout, w)
	default:
		server.notFound(req, w)
	}
}

func (server *ApiServer) handleOperationRequest(parts []string, w http.ResponseWriter, req *http.Request) {
	if req.Method != "GET" {
		server.notFound(req, w)
	}
	if len(parts) == 0 {
		// List outstanding operations.
		list := server.ops.List()
		server.write(http.StatusOK, list, w)
		return
	}

	op := server.ops.Get(parts[0])
	if op == nil {
		server.notFound(req, w)
	}

	obj, complete := op.StatusOrResult()
	if complete {
		server.write(http.StatusOK, obj, w)
	} else {
		server.write(http.StatusAccepted, obj, w)
	}
}
<|endoftext|>


<commit_before>/*-
* Copyright (c) 2016-2019, F5 Networks, Inc.
*
* 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 crmanager

import (
	"fmt"
	"time"

	cisapiv1 "github.com/F5Networks/k8s-bigip-ctlr/config/apis/cis/v1"
	cisinfv1 "github.com/F5Networks/k8s-bigip-ctlr/config/client/informers/externalversions/cis/v1"
	log "github.com/F5Networks/k8s-bigip-ctlr/pkg/vlogger"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/tools/cache"
)

// start the VirtualServer informer
func (crInfr *CRInformer) start() {
	log.Infof("Starting VirtualServer Informer")
	if crInfr.vsInformer != nil {
		go crInfr.vsInformer.Run(crInfr.stopCh)
	}
	log.Infof("Starting TLSProfile Informer")
	if crInfr.tsInformer != nil {
		go crInfr.tsInformer.Run(crInfr.stopCh)
	}
	if crInfr.svcInformer != nil {
		go crInfr.svcInformer.Run(crInfr.stopCh)
	}
	if crInfr.epsInformer != nil {
		go crInfr.epsInformer.Run(crInfr.stopCh)
	}
}

func (crInfr *CRInformer) stop() {
	close(crInfr.stopCh)
}

func (crMgr *CRManager) watchingAllNamespaces() bool {
	if 0 == len(crMgr.crInformers) {
		// Not watching any namespaces.
		return false
	}
	_, watchingAll := crMgr.crInformers[""]
	return watchingAll
}

func (crMgr *CRManager) addNamespacedInformer(
	namespace string,
) error {
	if crMgr.watchingAllNamespaces() {
		return fmt.Errorf(
			"Cannot add additional namespaces when already watching all.")
	}
	if len(crMgr.crInformers) > 0 && "" == namespace {
		return fmt.Errorf(
			"Cannot watch all namespaces when already watching specific ones.")
	}
	var crInf *CRInformer
	var found bool
	if crInf, found = crMgr.crInformers[namespace]; found {
		return nil
	}
	crInf = crMgr.newInformer(namespace)
	crMgr.addEventHandlers(crInf)
	crMgr.crInformers[namespace] = crInf
	return nil
}

func (crMgr *CRManager) newInformer(
	namespace string,
) *CRInformer {
	log.Debugf("Creating Informers for Namespace %v", namespace)
	crOptions := func(options *metav1.ListOptions) {
		options.LabelSelector = crMgr.resourceSelector.String()
	}
	everything := func(options *metav1.ListOptions) {
		options.LabelSelector = ""
	}
	resyncPeriod := 0 * time.Second
	restClientv1 := crMgr.kubeClient.CoreV1().RESTClient()

	crInf := &CRInformer{
		namespace: namespace,
		stopCh:    make(chan struct{}),
		vsInformer: cisinfv1.NewFilteredVirtualServerInformer(
			crMgr.kubeCRClient,
			namespace,
			resyncPeriod,
			cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
			crOptions,
		),
		tsInformer: cisinfv1.NewFilteredTLSProfileInformer(
			crMgr.kubeCRClient,
			namespace,
			resyncPeriod,
			cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
			crOptions,
		),
		svcInformer: cache.NewSharedIndexInformer(
			cache.NewFilteredListWatchFromClient(
				restClientv1,
				"services",
				namespace,
				everything,
			),
			&corev1.Service{},
			resyncPeriod,
			cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
		),
		epsInformer: cache.NewSharedIndexInformer(
			cache.NewFilteredListWatchFromClient(
				restClientv1,
				"endpoints",
				namespace,
				everything,
			),
			&corev1.Endpoints{},
			resyncPeriod,
			cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
		),
	}

	return crInf
}

func (crMgr *CRManager) addEventHandlers(crInf *CRInformer) {

	crInf.vsInformer.AddEventHandler(
		&cache.ResourceEventHandlerFuncs{
			AddFunc:    func(obj interface{}) { crMgr.enqueueVirtualServer(obj) },
			UpdateFunc: func(old, cur interface{}) { crMgr.enqueueVirtualServer(cur) },
			DeleteFunc: func(obj interface{}) { crMgr.enqueueDeletedVirtualServer(obj) },
		},
	)

	crInf.tsInformer.AddEventHandler(
		&cache.ResourceEventHandlerFuncs{
			// AddFunc:    func(obj interface{}) { crMgr.enqueueTLSServer(obj) },
			UpdateFunc: func(old, cur interface{}) { crMgr.enqueueTLSServer(cur) },
			// DeleteFunc: func(obj interface{}) { crMgr.enqueueTLSServer(obj) },
		},
	)

	crInf.svcInformer.AddEventHandler(
		&cache.ResourceEventHandlerFuncs{
			// Ignore AddFunc for service as we dont bother about services until they are
			// mapped to VirtualServer. Any new service added and mapped to a VirtualServer
			// will be handled in the VirtualServer Informer AddFunc.
			// AddFunc:    func(obj interface{}) { crMgr.enqueueService(obj) },
			UpdateFunc: func(obj, cur interface{}) { crMgr.enqueueService(cur) },
			DeleteFunc: func(obj interface{}) { crMgr.enqueueService(obj) },
		},
	)

	crInf.epsInformer.AddEventHandler(
		&cache.ResourceEventHandlerFuncs{
			// Ignore AddFunc for endpoint as we dont bother about endpoints until they are
			// mapped to VirtualServer. Any new endpoint added and mapped to a Service
			// will be handled in the Service Informer AddFunc.
			// AddFunc:    func(obj interface{}) { crMgr.enqueueEndpoints(obj) },
			UpdateFunc: func(obj, cur interface{}) { crMgr.enqueueEndpoints(cur) },
			DeleteFunc: func(obj interface{}) { crMgr.enqueueEndpoints(obj) },
		},
	)
}

func (crMgr *CRManager) getNamespaceInformer(
	namespace string,
) (*CRInformer, bool) {
	if crMgr.watchingAllNamespaces() {
		namespace = ""
	}
	crInf, found := crMgr.crInformers[namespace]
	return crInf, found
}

func (crMgr *CRManager) enqueueVirtualServer(obj interface{}) {
	vs := obj.(*cisapiv1.VirtualServer)
	log.Infof("Enqueueing VirtualServer: %v", vs)
	key := &rqKey{
		namespace: vs.ObjectMeta.Namespace,
		kind:      VirtualServer,
		rscName:   vs.ObjectMeta.Name,
		rsc:       obj,
	}

	crMgr.rscQueue.Add(key)
}

func (crMgr *CRManager) enqueueDeletedVirtualServer(obj interface{}) {
	vs := obj.(*cisapiv1.VirtualServer)
	log.Infof("Enqueueing VirtualServer: %v", vs)
	key := &rqKey{
		namespace: vs.ObjectMeta.Namespace,
		kind:      VirtualServer,
		rscName:   vs.ObjectMeta.Name,
		rsc:       obj,
		rscDelete: true,
	}

	crMgr.rscQueue.Add(key)
}

func (crMgr *CRManager) enqueueTLSServer(obj interface{}) {
	tls := obj.(*cisapiv1.TLSProfile)
	log.Infof("Enqueueing TLSProfile: %v", tls)
	key := &rqKey{
		namespace: tls.ObjectMeta.Namespace,
		kind:      TLSProfile,
		rscName:   tls.ObjectMeta.Name,
		rsc:       obj,
	}

	crMgr.rscQueue.Add(key)
}

func (crMgr *CRManager) enqueueService(obj interface{}) {
	svc := obj.(*corev1.Service)
	log.Debugf("Enqueueing Service: %v", svc)
	key := &rqKey{
		namespace: svc.ObjectMeta.Namespace,
		kind:      Service,
		rscName:   svc.ObjectMeta.Name,
		rsc:       obj,
	}

	crMgr.rscQueue.Add(key)
}

func (crMgr *CRManager) enqueueEndpoints(obj interface{}) {
	eps := obj.(*corev1.Endpoints)
	log.Debugf("Enqueueing Endpoints: %v", eps)
	key := &rqKey{
		namespace: eps.ObjectMeta.Namespace,
		kind:      Endpoints,
		rscName:   eps.ObjectMeta.Name,
		rsc:       obj,
	}

	crMgr.rscQueue.Add(key)
}
<commit_msg>Fix for Modification of CRD VirtualAddress Spec<commit_after>/*-
* Copyright (c) 2016-2019, F5 Networks, Inc.
*
* 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 crmanager

import (
	"fmt"
	"time"

	cisapiv1 "github.com/F5Networks/k8s-bigip-ctlr/config/apis/cis/v1"
	cisinfv1 "github.com/F5Networks/k8s-bigip-ctlr/config/client/informers/externalversions/cis/v1"
	log "github.com/F5Networks/k8s-bigip-ctlr/pkg/vlogger"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/tools/cache"
)

// start the VirtualServer informer
func (crInfr *CRInformer) start() {
	log.Infof("Starting VirtualServer Informer")
	if crInfr.vsInformer != nil {
		go crInfr.vsInformer.Run(crInfr.stopCh)
	}
	log.Infof("Starting TLSProfile Informer")
	if crInfr.tsInformer != nil {
		go crInfr.tsInformer.Run(crInfr.stopCh)
	}
	if crInfr.svcInformer != nil {
		go crInfr.svcInformer.Run(crInfr.stopCh)
	}
	if crInfr.epsInformer != nil {
		go crInfr.epsInformer.Run(crInfr.stopCh)
	}
}

func (crInfr *CRInformer) stop() {
	close(crInfr.stopCh)
}

func (crMgr *CRManager) watchingAllNamespaces() bool {
	if 0 == len(crMgr.crInformers) {
		// Not watching any namespaces.
		return false
	}
	_, watchingAll := crMgr.crInformers[""]
	return watchingAll
}

func (crMgr *CRManager) addNamespacedInformer(
	namespace string,
) error {
	if crMgr.watchingAllNamespaces() {
		return fmt.Errorf(
			"Cannot add additional namespaces when already watching all.")
	}
	if len(crMgr.crInformers) > 0 && "" == namespace {
		return fmt.Errorf(
			"Cannot watch all namespaces when already watching specific ones.")
	}
	var crInf *CRInformer
	var found bool
	if crInf, found = crMgr.crInformers[namespace]; found {
		return nil
	}
	crInf = crMgr.newInformer(namespace)
	crMgr.addEventHandlers(crInf)
	crMgr.crInformers[namespace] = crInf
	return nil
}

func (crMgr *CRManager) newInformer(
	namespace string,
) *CRInformer {
	log.Debugf("Creating Informers for Namespace %v", namespace)
	crOptions := func(options *metav1.ListOptions) {
		options.LabelSelector = crMgr.resourceSelector.String()
	}
	everything := func(options *metav1.ListOptions) {
		options.LabelSelector = ""
	}
	resyncPeriod := 0 * time.Second
	restClientv1 := crMgr.kubeClient.CoreV1().RESTClient()

	crInf := &CRInformer{
		namespace: namespace,
		stopCh:    make(chan struct{}),
		vsInformer: cisinfv1.NewFilteredVirtualServerInformer(
			crMgr.kubeCRClient,
			namespace,
			resyncPeriod,
			cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
			crOptions,
		),
		tsInformer: cisinfv1.NewFilteredTLSProfileInformer(
			crMgr.kubeCRClient,
			namespace,
			resyncPeriod,
			cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
			crOptions,
		),
		svcInformer: cache.NewSharedIndexInformer(
			cache.NewFilteredListWatchFromClient(
				restClientv1,
				"services",
				namespace,
				everything,
			),
			&corev1.Service{},
			resyncPeriod,
			cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
		),
		epsInformer: cache.NewSharedIndexInformer(
			cache.NewFilteredListWatchFromClient(
				restClientv1,
				"endpoints",
				namespace,
				everything,
			),
			&corev1.Endpoints{},
			resyncPeriod,
			cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
		),
	}

	return crInf
}

func (crMgr *CRManager) addEventHandlers(crInf *CRInformer) {

	crInf.vsInformer.AddEventHandler(
		&cache.ResourceEventHandlerFuncs{
			AddFunc:    func(obj interface{}) { crMgr.enqueueVirtualServer(obj) },
			UpdateFunc: func(old, cur interface{}) { crMgr.enqueueUpdatedVirtualServer(old, cur) },
			DeleteFunc: func(obj interface{}) { crMgr.enqueueDeletedVirtualServer(obj) },
		},
	)

	crInf.tsInformer.AddEventHandler(
		&cache.ResourceEventHandlerFuncs{
			// AddFunc:    func(obj interface{}) { crMgr.enqueueTLSServer(obj) },
			UpdateFunc: func(old, cur interface{}) { crMgr.enqueueTLSServer(cur) },
			// DeleteFunc: func(obj interface{}) { crMgr.enqueueTLSServer(obj) },
		},
	)

	crInf.svcInformer.AddEventHandler(
		&cache.ResourceEventHandlerFuncs{
			// Ignore AddFunc for service as we dont bother about services until they are
			// mapped to VirtualServer. Any new service added and mapped to a VirtualServer
			// will be handled in the VirtualServer Informer AddFunc.
			// AddFunc:    func(obj interface{}) { crMgr.enqueueService(obj) },
			UpdateFunc: func(obj, cur interface{}) { crMgr.enqueueService(cur) },
			DeleteFunc: func(obj interface{}) { crMgr.enqueueService(obj) },
		},
	)

	crInf.epsInformer.AddEventHandler(
		&cache.ResourceEventHandlerFuncs{
			// Ignore AddFunc for endpoint as we dont bother about endpoints until they are
			// mapped to VirtualServer. Any new endpoint added and mapped to a Service
			// will be handled in the Service Informer AddFunc.
			// AddFunc:    func(obj interface{}) { crMgr.enqueueEndpoints(obj) },
			UpdateFunc: func(obj, cur interface{}) { crMgr.enqueueEndpoints(cur) },
			DeleteFunc: func(obj interface{}) { crMgr.enqueueEndpoints(obj) },
		},
	)
}

func (crMgr *CRManager) getNamespaceInformer(
	namespace string,
) (*CRInformer, bool) {
	if crMgr.watchingAllNamespaces() {
		namespace = ""
	}
	crInf, found := crMgr.crInformers[namespace]
	return crInf, found
}

func (crMgr *CRManager) enqueueVirtualServer(obj interface{}) {
	vs := obj.(*cisapiv1.VirtualServer)
	log.Infof("Enqueueing VirtualServer: %v", vs)
	key := &rqKey{
		namespace: vs.ObjectMeta.Namespace,
		kind:      VirtualServer,
		rscName:   vs.ObjectMeta.Name,
		rsc:       obj,
	}

	crMgr.rscQueue.Add(key)
}

func (crMgr *CRManager) enqueueUpdatedVirtualServer(oldObj, newObj interface{}) {
	oldVS := oldObj.(*cisapiv1.VirtualServer)
	newVS := newObj.(*cisapiv1.VirtualServer)

	if oldVS.Spec.VirtualServerAddress != newVS.Spec.VirtualServerAddress {
		log.Infof("Enqueueing VirtualServer: %v", oldVS)
		key := &rqKey{
			namespace: oldVS.ObjectMeta.Namespace,
			kind:      VirtualServer,
			rscName:   oldVS.ObjectMeta.Name,
			rsc:       oldObj,
			rscDelete: true,
		}
		crMgr.rscQueue.Add(key)
	}

	log.Infof("Enqueueing VirtualServer: %v", newVS)
	key := &rqKey{
		namespace: newVS.ObjectMeta.Namespace,
		kind:      VirtualServer,
		rscName:   newVS.ObjectMeta.Name,
		rsc:       newObj,
	}

	crMgr.rscQueue.Add(key)
}

func (crMgr *CRManager) enqueueDeletedVirtualServer(obj interface{}) {
	vs := obj.(*cisapiv1.VirtualServer)
	log.Infof("Enqueueing VirtualServer: %v", vs)
	key := &rqKey{
		namespace: vs.ObjectMeta.Namespace,
		kind:      VirtualServer,
		rscName:   vs.ObjectMeta.Name,
		rsc:       obj,
		rscDelete: true,
	}

	crMgr.rscQueue.Add(key)
}

func (crMgr *CRManager) enqueueTLSServer(obj interface{}) {
	tls := obj.(*cisapiv1.TLSProfile)
	log.Infof("Enqueueing TLSProfile: %v", tls)
	key := &rqKey{
		namespace: tls.ObjectMeta.Namespace,
		kind:      TLSProfile,
		rscName:   tls.ObjectMeta.Name,
		rsc:       obj,
	}

	crMgr.rscQueue.Add(key)
}

func (crMgr *CRManager) enqueueService(obj interface{}) {
	svc := obj.(*corev1.Service)
	log.Debugf("Enqueueing Service: %v", svc)
	key := &rqKey{
		namespace: svc.ObjectMeta.Namespace,
		kind:      Service,
		rscName:   svc.ObjectMeta.Name,
		rsc:       obj,
	}

	crMgr.rscQueue.Add(key)
}

func (crMgr *CRManager) enqueueEndpoints(obj interface{}) {
	eps := obj.(*corev1.Endpoints)
	log.Debugf("Enqueueing Endpoints: %v", eps)
	key := &rqKey{
		namespace: eps.ObjectMeta.Namespace,
		kind:      Endpoints,
		rscName:   eps.ObjectMeta.Name,
		rsc:       obj,
	}

	crMgr.rscQueue.Add(key)
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"io"
	"os"

	"github.com/apprenda/kismatic-platform/pkg/inspector/rule"
	"github.com/spf13/cobra"
)

// NewCmdRules returns the "rules" command
func NewCmdRules(out io.Writer) *cobra.Command {
	var file string
	cmd := &cobra.Command{
		Use:   "rules",
		Short: "manipualte the inspector's rules",
		RunE: func(cmd *cobra.Command, args []string) error {
			return cmd.Help()
		},
	}
	cmd.Flags().StringVarP(&file, "file", "f", "inspector-rules.yaml", "file where inspector rules are to be written")
	cmd.AddCommand(NewCmdDumpRules(out, file))
	return cmd
}

// NewCmdDumpRules returns the "dump" command
func NewCmdDumpRules(out io.Writer, file string) *cobra.Command {
	var overwrite bool
	cmd := &cobra.Command{
		Use:   "dump",
		Short: "dump the inspector rules to a file",
		RunE: func(cmd *cobra.Command, args []string) error {
			if _, err := os.Stat(file); err == nil && !overwrite {
				return fmt.Errorf("%q already exists. Use --overwrite to overwrite it", file)
			}
			f, err := os.Create(file)
			if err != nil {
				return fmt.Errorf("error creating %q: %v", file, err)
			}
			if err := rule.DumpDefaultRules(f); err != nil {
				return fmt.Errorf("error dumping rules: %v", err)
			}
			return nil
		},
	}
	cmd.Flags().BoolVar(&overwrite, "overwrite", false, "overwrite the destination file if it exists")
	return cmd
}

// validates rules, printing error messages to the console
func validateRules(out io.Writer, rules []rule.Rule) bool {
	allOK := true
	for i, r := range rules {
		errs := r.Validate()
		if len(errs) > 0 {
			allOK = false
			fmt.Fprintf(out, "%s (Rule #%d):\n", r.GetRuleMeta().Kind, i+1)
			for _, e := range errs {
				fmt.Fprintf(out, "- %v\n", e)
			}
			fmt.Fprintln(out, "")
		}
	}
	return allOK
}
<commit_msg>KIS-13: Add command for validating rules<commit_after>package cmd

import (
	"fmt"
	"io"
	"os"

	"github.com/apprenda/kismatic-platform/pkg/inspector/rule"
	"github.com/spf13/cobra"
)

// NewCmdRules returns the "rules" command
func NewCmdRules(out io.Writer) *cobra.Command {
	var file string
	cmd := &cobra.Command{
		Use:   "rules",
		Short: "manipualte the inspector's rules",
		RunE: func(cmd *cobra.Command, args []string) error {
			return cmd.Help()
		},
	}
	cmd.Flags().StringVarP(&file, "file", "f", "inspector-rules.yaml", "file where inspector rules are to be written")
	cmd.AddCommand(NewCmdDumpRules(out, file))
	cmd.AddCommand(NewCmdValidateRules(out, file))
	return cmd
}

// NewCmdDumpRules returns the "dump" command
func NewCmdDumpRules(out io.Writer, file string) *cobra.Command {
	var overwrite bool
	cmd := &cobra.Command{
		Use:   "dump",
		Short: "dump the inspector rules to a file",
		RunE: func(cmd *cobra.Command, args []string) error {
			if _, err := os.Stat(file); err == nil && !overwrite {
				return fmt.Errorf("%q already exists. Use --overwrite to overwrite it", file)
			}
			f, err := os.Create(file)
			if err != nil {
				return fmt.Errorf("error creating %q: %v", file, err)
			}
			if err := rule.DumpDefaultRules(f); err != nil {
				return fmt.Errorf("error dumping rules: %v", err)
			}
			return nil
		},
	}
	cmd.Flags().BoolVar(&overwrite, "overwrite", false, "overwrite the destination file if it exists")
	return cmd
}

func NewCmdValidateRules(out io.Writer, file string) *cobra.Command {
	cmd := &cobra.Command{
		Use:   "validate",
		Short: "validate the inspector rules",
		RunE: func(cmd *cobra.Command, args []string) error {
			if _, err := os.Stat(file); os.IsNotExist(err) {
				return fmt.Errorf("%q does not exist", file)
			}
			rules, err := rule.ReadFromFile(file)
			if err != nil {
				return err
			}
			if !validateRules(out, rules) {
				return fmt.Errorf("invalid rules found in %q", file)
			}
			fmt.Fprintf(out, "Rules are valid\n")
			return nil
		},
	}
	return cmd
}

// validates rules, printing error messages to the console
func validateRules(out io.Writer, rules []rule.Rule) bool {
	allOK := true
	for i, r := range rules {
		errs := r.Validate()
		if len(errs) > 0 {
			allOK = false
			fmt.Fprintf(out, "%s (Rule #%d):\n", r.GetRuleMeta().Kind, i+1)
			for _, e := range errs {
				fmt.Fprintf(out, "- %v\n", e)
			}
			fmt.Fprintln(out, "")
		}
	}
	return allOK
}
<|endoftext|>