<commit_before>package service

import (
	"github.com/boivie/lovebeat-go/alert"
	"github.com/boivie/lovebeat-go/backend"
	"github.com/op/go-logging"
	"math"
	"regexp"
	"sort"
	"time"
)

const (
	MAX_UNPROCESSED_PACKETS = 1000
	EXPIRY_INTERVAL         = 1

	// Number of samples (diffs) we require to be able to
	// properly calculate an "auto" timeout
	AUTO_MIN_SAMPLES = 5
)

var (
	log          = logging.MustGetLogger("lovebeat")
	EMPTY_REGEXP = regexp.MustCompile("^$")
)

type Services struct {
	be                   backend.Backend
	alerters             []alert.Alerter
	services             map[string]*Service
	views                map[string]*View
	beatCmdChan          chan string
	upsertServiceCmdChan chan *upsertServiceCmd
	deleteServiceCmdChan chan string
	deleteViewCmdChan    chan string
	upsertViewCmdChan    chan *upsertViewCmd
	getServicesChan      chan *getServicesCmd
	getServiceChan       chan *getServiceCmd
	getViewsChan         chan *getViewsCmd
	getViewChan          chan *getViewCmd
}

type Service struct {
	data backend.StoredService
}

type View struct {
	svcs *Services
	data backend.StoredView
	ree  *regexp.Regexp
}

func now() int64 { return time.Now().Unix() }

func (s *Service) getExpiry(timeout int64) int64 {
	if timeout <= 0 {
		return 0
	}
	return s.data.LastBeat + timeout
}

func (s *Service) name() string { return s.data.Name }
func (v *View) name() string    { return v.data.Name }

func calcTimeout(values []int64) int64 {
	diffs := calcDiffs(values)
	if len(diffs) < AUTO_MIN_SAMPLES {
		log.Debug("AUTO-TIMEOUT: Not enough samples to calculate")
		return TIMEOUT_AUTO
	}

	ret := int64(math.Ceil(float64(median(diffs)) * 1.5))
	log.Debug("AUTO-TIMEOUT: vale calculated as %d", ret)
	return ret
}

func calcDiffs(values []int64) []int64 {
	var p []int64
	for i := 1; i < len(values); i++ {
		if values[i-1] != 0 && values[i] != 0 {
			p = append(p, values[i]-values[i-1])
		}
	}
	return p
}

type int64arr []int64

func (a int64arr) Len() int           { return len(a) }
func (a int64arr) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a int64arr) Less(i, j int) bool { return a[i] < a[j] }

func median(numbers []int64) int64 {
	sort.Sort(int64arr(numbers))
	middle := len(numbers) / 2
	result := numbers[middle]
	if len(numbers)%2 == 0 {
		result = (result + numbers[middle-1]) / 2
	}
	return result
}

// Called before saving - to update internal states
func (s *Service) update(ts int64) {
	s.data.State = s.stateAt(ts)

	if s.data.WarningTimeout == TIMEOUT_AUTO {
		s.data.WarningTimeout = calcTimeout(s.data.PreviousBeats)
	}
	if s.data.ErrorTimeout == TIMEOUT_AUTO {
		s.data.ErrorTimeout = calcTimeout(s.data.PreviousBeats)
	}
	s.data.LastUpdated = ts
}

func (s *Service) stateAt(ts int64) string {
	var state = backend.STATE_OK
	var warningExpiry = s.getExpiry(s.data.WarningTimeout)
	var errorExpiry = s.getExpiry(s.data.ErrorTimeout)
	if warningExpiry > 0 && ts >= warningExpiry {
		state = backend.STATE_WARNING
	}
	if errorExpiry > 0 && ts >= errorExpiry {
		state = backend.STATE_ERROR
	}
	return state
}

func (s *Service) registerBeat(ts int64) {
	s.data.LastBeat = ts
	s.data.PreviousBeats = append(s.data.PreviousBeats[1:], ts)
}

func (s *Service) save(be backend.Backend, ref *Service, ts int64) {
	if s.data.State != ref.data.State {
		log.Info("SERVICE '%s', state %s -> %s",
			s.name(), ref.data.State, s.data.State)
	}
	if s.data.WarningTimeout != ref.data.WarningTimeout {
		log.Info("SERVICE '%s', warn %d -> %d",
			s.name(), ref.data.WarningTimeout,
			s.data.WarningTimeout)
	}
	if s.data.ErrorTimeout != ref.data.ErrorTimeout {
		log.Info("SERVICE '%s', err %d -> %d",
			s.name(), ref.data.ErrorTimeout,
			s.data.ErrorTimeout)
	}
	be.SaveService(&s.data)
}

func (v *View) update(ts int64) {
	v.data.State = backend.STATE_OK
	v.data.LastUpdated = ts
	for _, s := range v.svcs.services {
		if v.ree.Match([]byte(s.name())) {
			if s.data.State == backend.STATE_WARNING && v.data.State == backend.STATE_OK {
				v.data.State = backend.STATE_WARNING
			} else if s.data.State == backend.STATE_ERROR {
				v.data.State = backend.STATE_ERROR
			}
		}
	}
}

func (v *View) contains(serviceName string) bool {
	return v.ree.Match([]byte(serviceName))
}

func (v *View) save(be backend.Backend, ref *View, ts int64) {
	if v.data.State != ref.data.State {
		if ref.data.State == backend.STATE_OK {
			v.data.IncidentNbr += 1
		}
	}
	be.SaveView(&v.data)
}

func (v *View) sendAlerts(ref *View, ts int64) {
	if v.data.State != ref.data.State {
		log.Info("VIEW '%s', %d: state %s -> %s",
			v.name(), v.data.IncidentNbr, ref.data.State,
			v.data.State)

		var services = make([]backend.StoredService, 0, 10)
		for _, s := range v.svcs.services {
			if (s.data.State == backend.STATE_WARNING ||
				s.data.State == backend.STATE_ERROR) &&
				v.contains(s.name()) {
				services = append(services, s.data)
				if len(services) == 10 {
					break
				}
			}
		}

		for _, a := range v.svcs.alerters {
			a.Notify(ref.data, v.data, services)
		}
	}
}

func (svcs *Services) updateViews(ts int64, serviceName string) {
	for _, view := range svcs.views {
		if view.ree.Match([]byte(serviceName)) {
			var ref = *view
			view.update(ts)
			view.save(svcs.be, &ref, ts)
			view.sendAlerts(&ref, ts)
		}
	}
}

func (svcs *Services) getService(name string) *Service {
	var s, ok = svcs.services[name]
	if !ok {
		log.Debug("Asked for unknown service %s", name)
		s = &Service{
			data: backend.StoredService{
				Name:           name,
				LastValue:      -1,
				LastBeat:       -1,
				PreviousBeats:  make([]int64, backend.PREVIOUS_BEATS_COUNT),
				LastUpdated:    -1,
				WarningTimeout: -1,
				ErrorTimeout:   -1,
				State:          backend.STATE_PAUSED,
			},
		}
		svcs.services[name] = s
	}
	return s
}

func (svcs *Services) getView(name string) *View {
	var s, ok = svcs.views[name]
	if !ok {
		log.Debug("Asked for unknown view %s", name)
		s = &View{
			svcs: svcs,
			data: backend.StoredView{
				Name:        name,
				State:       backend.STATE_OK,
				LastUpdated: -1,
				Regexp:      "^$",
			},
			ree: EMPTY_REGEXP}
		svcs.views[name] = s
	}
	return s
}

func (svcs *Services) createView(name string, expr string, alertMail string,
	webhooks string, ts int64) {
	var ree, err = regexp.Compile(expr)
	if err != nil {
		log.Error("Invalid regexp: %s", err)
		return
	}

	var view = svcs.getView(name)
	var ref = *view
	view.data.Regexp = expr
	view.ree = ree
	view.data.AlertMail = alertMail
	view.data.Webhooks = webhooks
	view.update(ts)
	view.save(svcs.be, &ref, ts)

	log.Info("VIEW '%s' created or updated.", name)
}

func (svcs *Services) Monitor() {
	period := time.Duration(EXPIRY_INTERVAL) * time.Second
	ticker := time.NewTicker(period)
	for {
		select {
		case <-ticker.C:
			var ts = now()
			for _, s := range svcs.services {
				if s.data.State == backend.STATE_PAUSED || s.data.State == s.stateAt(ts) {
					continue
				}
				var ref = *s
				s.update(ts)
				s.save(svcs.be, &ref, ts)
				svcs.updateViews(ts, s.name())
			}
		case c := <-svcs.upsertViewCmdChan:
			log.Debug("Create or update view %s", c.View)
			svcs.createView(c.View, c.Regexp, c.AlertMail,
				c.Webhooks, now())
		case c := <-svcs.deleteViewCmdChan:
			log.Debug("Delete view %s", c)
			delete(svcs.views, c)
			svcs.be.DeleteView(c)
		case c := <-svcs.getServicesChan:
			var ret []backend.StoredService
			var view, ok = svcs.views[c.View]
			if ok {
				for _, s := range svcs.services {
					if view.contains(s.name()) {
						ret = append(ret, s.data)
					}
				}
			}
			c.Reply <- ret
		case c := <-svcs.getServiceChan:
			var ret = svcs.services[c.Name]
			c.Reply <- ret.data
		case c := <-svcs.getViewsChan:
			var ret []backend.StoredView
			for _, v := range svcs.views {
				ret = append(ret, v.data)
			}
			c.Reply <- ret
		case c := <-svcs.getViewChan:
			var ret = svcs.views[c.Name]
			c.Reply <- ret.data
		case c := <-svcs.beatCmdChan:
			var ts = now()
			var s = svcs.getService(c)
			var ref = *s
			s.registerBeat(ts)
			log.Debug("Beat from %s", s.name())
			s.update(ts)
			s.save(svcs.be, &ref, ts)
			svcs.updateViews(ts, s.name())
		case c := <-svcs.deleteServiceCmdChan:
			var ts = now()
			var s = svcs.getService(c)
			delete(svcs.services, s.name())
			svcs.be.DeleteService(s.name())
			svcs.updateViews(ts, s.name())
		case c := <-svcs.upsertServiceCmdChan:
			var ts = now()
			var s = svcs.getService(c.Service)
			var ref = *s
			// Don't re-calculate 'auto' if we already have values
			if c.WarningTimeout == TIMEOUT_AUTO &&
				s.data.WarningTimeout == -1 {
				s.data.WarningTimeout = TIMEOUT_AUTO
				s.data.PreviousBeats = make([]int64, backend.PREVIOUS_BEATS_COUNT)
			} else if c.WarningTimeout == TIMEOUT_CLEAR {
				s.data.WarningTimeout = TIMEOUT_CLEAR
			} else if c.WarningTimeout > 0 {
				s.data.WarningTimeout = c.WarningTimeout
			}
			if c.ErrorTimeout == TIMEOUT_AUTO &&
				s.data.ErrorTimeout == -1 {
				s.data.ErrorTimeout = TIMEOUT_AUTO
				s.data.PreviousBeats = make([]int64, backend.PREVIOUS_BEATS_COUNT)
			} else if c.ErrorTimeout == TIMEOUT_CLEAR {
				s.data.ErrorTimeout = TIMEOUT_CLEAR
			} else if c.ErrorTimeout > 0 {
				s.data.ErrorTimeout = c.ErrorTimeout
			}
			s.update(ts)
			s.save(svcs.be, &ref, ts)
			svcs.updateViews(ts, s.name())
		}
	}
}

func NewServices(beiface backend.Backend, alerters []alert.Alerter) *Services {
	svcs := new(Services)
	svcs.be = beiface
	svcs.alerters = alerters
	svcs.beatCmdChan = make(chan string, MAX_UNPROCESSED_PACKETS)
	svcs.deleteServiceCmdChan = make(chan string, 5)
	svcs.upsertServiceCmdChan = make(chan *upsertServiceCmd, 5)
	svcs.deleteViewCmdChan = make(chan string, 5)
	svcs.upsertViewCmdChan = make(chan *upsertViewCmd, 5)
	svcs.getServicesChan = make(chan *getServicesCmd, 5)
	svcs.getServiceChan = make(chan *getServiceCmd, 5)
	svcs.getViewsChan = make(chan *getViewsCmd, 5)
	svcs.getViewChan = make(chan *getViewCmd, 5)
	svcs.services = make(map[string]*Service)
	svcs.views = make(map[string]*View)

	for _, s := range svcs.be.LoadServices() {
		var svc = &Service{data: *s}
		if svc.data.PreviousBeats == nil || len(svc.data.PreviousBeats) != backend.PREVIOUS_BEATS_COUNT {
			svc.data.PreviousBeats = make([]int64, backend.PREVIOUS_BEATS_COUNT)
		}
		svcs.services[s.Name] = svc
	}

	for _, v := range svcs.be.LoadViews() {
		var ree, _ = regexp.Compile(v.Regexp)
		svcs.views[v.Name] = &View{svcs: svcs, data: *v, ree: ree}
	}

	return svcs
}
<commit_msg>Refactoring (7/10)<commit_after>package service

import (
	"github.com/boivie/lovebeat-go/alert"
	"github.com/boivie/lovebeat-go/backend"
	"github.com/op/go-logging"
	"math"
	"regexp"
	"sort"
	"time"
)

const (
	MAX_UNPROCESSED_PACKETS = 1000
	EXPIRY_INTERVAL         = 1

	// Number of samples (diffs) we require to be able to
	// properly calculate an "auto" timeout
	AUTO_MIN_SAMPLES = 5
)

var (
	log          = logging.MustGetLogger("lovebeat")
	EMPTY_REGEXP = regexp.MustCompile("^$")
)

type Services struct {
	be                   backend.Backend
	alerters             []alert.Alerter
	services             map[string]*Service
	views                map[string]*View
	beatCmdChan          chan string
	upsertServiceCmdChan chan *upsertServiceCmd
	deleteServiceCmdChan chan string
	deleteViewCmdChan    chan string
	upsertViewCmdChan    chan *upsertViewCmd
	getServicesChan      chan *getServicesCmd
	getServiceChan       chan *getServiceCmd
	getViewsChan         chan *getViewsCmd
	getViewChan          chan *getViewCmd
}

type Service struct {
	data backend.StoredService
}

type View struct {
	svcs *Services
	data backend.StoredView
	ree  *regexp.Regexp
}

func now() int64 { return time.Now().Unix() }

func (s *Service) getExpiry(timeout int64) int64 {
	if timeout <= 0 {
		return 0
	}
	return s.data.LastBeat + timeout
}

func (s *Service) name() string { return s.data.Name }
func (v *View) name() string    { return v.data.Name }

func calcTimeout(values []int64) int64 {
	diffs := calcDiffs(values)
	if len(diffs) < AUTO_MIN_SAMPLES {
		log.Debug("AUTO-TIMEOUT: Not enough samples to calculate")
		return TIMEOUT_AUTO
	}

	ret := int64(math.Ceil(float64(median(diffs)) * 1.5))
	log.Debug("AUTO-TIMEOUT: vale calculated as %d", ret)
	return ret
}

func calcDiffs(values []int64) []int64 {
	var p []int64
	for i := 1; i < len(values); i++ {
		if values[i-1] != 0 && values[i] != 0 {
			p = append(p, values[i]-values[i-1])
		}
	}
	return p
}

type int64arr []int64

func (a int64arr) Len() int           { return len(a) }
func (a int64arr) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a int64arr) Less(i, j int) bool { return a[i] < a[j] }

func median(numbers []int64) int64 {
	sort.Sort(int64arr(numbers))
	middle := len(numbers) / 2
	result := numbers[middle]
	if len(numbers)%2 == 0 {
		result = (result + numbers[middle-1]) / 2
	}
	return result
}

// Called before saving - to update internal states
func (s *Service) update(ts int64) {
	s.data.State = s.stateAt(ts)

	if s.data.WarningTimeout == TIMEOUT_AUTO {
		s.data.WarningTimeout = calcTimeout(s.data.PreviousBeats)
	}
	if s.data.ErrorTimeout == TIMEOUT_AUTO {
		s.data.ErrorTimeout = calcTimeout(s.data.PreviousBeats)
	}
	s.data.LastUpdated = ts
}

func (s *Service) stateAt(ts int64) string {
	var state = backend.STATE_OK
	var warningExpiry = s.getExpiry(s.data.WarningTimeout)
	var errorExpiry = s.getExpiry(s.data.ErrorTimeout)
	if warningExpiry > 0 && ts >= warningExpiry {
		state = backend.STATE_WARNING
	}
	if errorExpiry > 0 && ts >= errorExpiry {
		state = backend.STATE_ERROR
	}
	return state
}

func (s *Service) registerBeat(ts int64) {
	s.data.LastBeat = ts
	s.data.PreviousBeats = append(s.data.PreviousBeats[1:], ts)
}

func (s *Service) save(be backend.Backend, ref *Service, ts int64) {
	if s.data.State != ref.data.State {
		log.Info("SERVICE '%s', state %s -> %s",
			s.name(), ref.data.State, s.data.State)
	}
	if s.data.WarningTimeout != ref.data.WarningTimeout {
		log.Info("SERVICE '%s', warn %d -> %d",
			s.name(), ref.data.WarningTimeout,
			s.data.WarningTimeout)
	}
	if s.data.ErrorTimeout != ref.data.ErrorTimeout {
		log.Info("SERVICE '%s', err %d -> %d",
			s.name(), ref.data.ErrorTimeout,
			s.data.ErrorTimeout)
	}
	be.SaveService(&s.data)
}

func (v *View) update(ts int64) {
	v.data.State = backend.STATE_OK
	v.data.LastUpdated = ts
	for _, s := range v.svcs.services {
		if v.contains(s.name()) {
			if s.data.State == backend.STATE_WARNING && v.data.State == backend.STATE_OK {
				v.data.State = backend.STATE_WARNING
			} else if s.data.State == backend.STATE_ERROR {
				v.data.State = backend.STATE_ERROR
			}
		}
	}
}

func (v *View) contains(serviceName string) bool {
	return v.ree.Match([]byte(serviceName))
}

func (v *View) save(be backend.Backend, ref *View, ts int64) {
	if v.data.State != ref.data.State {
		if ref.data.State == backend.STATE_OK {
			v.data.IncidentNbr += 1
		}
	}
	be.SaveView(&v.data)
}

func (v *View) sendAlerts(ref *View, ts int64) {
	if v.data.State != ref.data.State {
		log.Info("VIEW '%s', %d: state %s -> %s",
			v.name(), v.data.IncidentNbr, ref.data.State,
			v.data.State)

		var services = make([]backend.StoredService, 0, 10)
		for _, s := range v.svcs.services {
			if (s.data.State == backend.STATE_WARNING ||
				s.data.State == backend.STATE_ERROR) &&
				v.contains(s.name()) {
				services = append(services, s.data)
				if len(services) == 10 {
					break
				}
			}
		}

		for _, a := range v.svcs.alerters {
			a.Notify(ref.data, v.data, services)
		}
	}
}

func (svcs *Services) updateViews(ts int64, serviceName string) {
	for _, view := range svcs.views {
		if view.contains(serviceName) {
			var ref = *view
			view.update(ts)
			view.save(svcs.be, &ref, ts)
			view.sendAlerts(&ref, ts)
		}
	}
}

func (svcs *Services) getService(name string) *Service {
	var s, ok = svcs.services[name]
	if !ok {
		log.Debug("Asked for unknown service %s", name)
		s = &Service{
			data: backend.StoredService{
				Name:           name,
				LastValue:      -1,
				LastBeat:       -1,
				PreviousBeats:  make([]int64, backend.PREVIOUS_BEATS_COUNT),
				LastUpdated:    -1,
				WarningTimeout: -1,
				ErrorTimeout:   -1,
				State:          backend.STATE_PAUSED,
			},
		}
		svcs.services[name] = s
	}
	return s
}

func (svcs *Services) getView(name string) *View {
	var s, ok = svcs.views[name]
	if !ok {
		log.Debug("Asked for unknown view %s", name)
		s = &View{
			svcs: svcs,
			data: backend.StoredView{
				Name:        name,
				State:       backend.STATE_OK,
				LastUpdated: -1,
				Regexp:      "^$",
			},
			ree: EMPTY_REGEXP}
		svcs.views[name] = s
	}
	return s
}

func (svcs *Services) createView(name string, expr string, alertMail string,
	webhooks string, ts int64) {
	var ree, err = regexp.Compile(expr)
	if err != nil {
		log.Error("Invalid regexp: %s", err)
		return
	}

	var view = svcs.getView(name)
	var ref = *view
	view.data.Regexp = expr
	view.ree = ree
	view.data.AlertMail = alertMail
	view.data.Webhooks = webhooks
	view.update(ts)
	view.save(svcs.be, &ref, ts)

	log.Info("VIEW '%s' created or updated.", name)
}

func (svcs *Services) Monitor() {
	period := time.Duration(EXPIRY_INTERVAL) * time.Second
	ticker := time.NewTicker(period)
	for {
		select {
		case <-ticker.C:
			var ts = now()
			for _, s := range svcs.services {
				if s.data.State == backend.STATE_PAUSED || s.data.State == s.stateAt(ts) {
					continue
				}
				var ref = *s
				s.update(ts)
				s.save(svcs.be, &ref, ts)
				svcs.updateViews(ts, s.name())
			}
		case c := <-svcs.upsertViewCmdChan:
			log.Debug("Create or update view %s", c.View)
			svcs.createView(c.View, c.Regexp, c.AlertMail,
				c.Webhooks, now())
		case c := <-svcs.deleteViewCmdChan:
			log.Debug("Delete view %s", c)
			delete(svcs.views, c)
			svcs.be.DeleteView(c)
		case c := <-svcs.getServicesChan:
			var ret []backend.StoredService
			var view, ok = svcs.views[c.View]
			if ok {
				for _, s := range svcs.services {
					if view.contains(s.name()) {
						ret = append(ret, s.data)
					}
				}
			}
			c.Reply <- ret
		case c := <-svcs.getServiceChan:
			var ret = svcs.services[c.Name]
			c.Reply <- ret.data
		case c := <-svcs.getViewsChan:
			var ret []backend.StoredView
			for _, v := range svcs.views {
				ret = append(ret, v.data)
			}
			c.Reply <- ret
		case c := <-svcs.getViewChan:
			var ret = svcs.views[c.Name]
			c.Reply <- ret.data
		case c := <-svcs.beatCmdChan:
			var ts = now()
			var s = svcs.getService(c)
			var ref = *s
			s.registerBeat(ts)
			log.Debug("Beat from %s", s.name())
			s.update(ts)
			s.save(svcs.be, &ref, ts)
			svcs.updateViews(ts, s.name())
		case c := <-svcs.deleteServiceCmdChan:
			var ts = now()
			var s = svcs.getService(c)
			delete(svcs.services, s.name())
			svcs.be.DeleteService(s.name())
			svcs.updateViews(ts, s.name())
		case c := <-svcs.upsertServiceCmdChan:
			var ts = now()
			var s = svcs.getService(c.Service)
			var ref = *s
			// Don't re-calculate 'auto' if we already have values
			if c.WarningTimeout == TIMEOUT_AUTO &&
				s.data.WarningTimeout == -1 {
				s.data.WarningTimeout = TIMEOUT_AUTO
				s.data.PreviousBeats = make([]int64, backend.PREVIOUS_BEATS_COUNT)
			} else if c.WarningTimeout == TIMEOUT_CLEAR {
				s.data.WarningTimeout = TIMEOUT_CLEAR
			} else if c.WarningTimeout > 0 {
				s.data.WarningTimeout = c.WarningTimeout
			}
			if c.ErrorTimeout == TIMEOUT_AUTO &&
				s.data.ErrorTimeout == -1 {
				s.data.ErrorTimeout = TIMEOUT_AUTO
				s.data.PreviousBeats = make([]int64, backend.PREVIOUS_BEATS_COUNT)
			} else if c.ErrorTimeout == TIMEOUT_CLEAR {
				s.data.ErrorTimeout = TIMEOUT_CLEAR
			} else if c.ErrorTimeout > 0 {
				s.data.ErrorTimeout = c.ErrorTimeout
			}
			s.update(ts)
			s.save(svcs.be, &ref, ts)
			svcs.updateViews(ts, s.name())
		}
	}
}

func NewServices(beiface backend.Backend, alerters []alert.Alerter) *Services {
	svcs := new(Services)
	svcs.be = beiface
	svcs.alerters = alerters
	svcs.beatCmdChan = make(chan string, MAX_UNPROCESSED_PACKETS)
	svcs.deleteServiceCmdChan = make(chan string, 5)
	svcs.upsertServiceCmdChan = make(chan *upsertServiceCmd, 5)
	svcs.deleteViewCmdChan = make(chan string, 5)
	svcs.upsertViewCmdChan = make(chan *upsertViewCmd, 5)
	svcs.getServicesChan = make(chan *getServicesCmd, 5)
	svcs.getServiceChan = make(chan *getServiceCmd, 5)
	svcs.getViewsChan = make(chan *getViewsCmd, 5)
	svcs.getViewChan = make(chan *getViewCmd, 5)
	svcs.services = make(map[string]*Service)
	svcs.views = make(map[string]*View)

	for _, s := range svcs.be.LoadServices() {
		var svc = &Service{data: *s}
		if svc.data.PreviousBeats == nil || len(svc.data.PreviousBeats) != backend.PREVIOUS_BEATS_COUNT {
			svc.data.PreviousBeats = make([]int64, backend.PREVIOUS_BEATS_COUNT)
		}
		svcs.services[s.Name] = svc
	}

	for _, v := range svcs.be.LoadViews() {
		var ree, _ = regexp.Compile(v.Regexp)
		svcs.views[v.Name] = &View{svcs: svcs, data: *v, ree: ree}
	}

	return svcs
}
<|endoftext|>


<commit_before>// Copyright The OpenTelemetry 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 service // import "go.opentelemetry.io/collector/service"

import (
	"context"
	"fmt"

	"go.opentelemetry.io/otel/metric/nonrecording"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
	"go.opentelemetry.io/otel/trace"
	"go.uber.org/multierr"

	"go.opentelemetry.io/collector/component"
	"go.opentelemetry.io/collector/service/internal"
	"go.opentelemetry.io/collector/service/internal/extensions"
	"go.opentelemetry.io/collector/service/internal/pipelines"
	"go.opentelemetry.io/collector/service/internal/telemetrylogs"
)

// service represents the implementation of a component.Host.
type service struct {
	buildInfo component.BuildInfo
	config    *Config
	telemetry component.TelemetrySettings
	host      *serviceHost
}

func newService(set *settings) (*service, error) {
	srv := &service{
		buildInfo: set.BuildInfo,
		config:    set.Config,
		telemetry: component.TelemetrySettings{
			TracerProvider: trace.NewNoopTracerProvider(),
			MeterProvider:  nonrecording.NewNoopMeterProvider(),
			MetricsLevel:   set.Config.Telemetry.Metrics.Level,
		},
		host: &serviceHost{
			factories:         set.Factories,
			buildInfo:         set.BuildInfo,
			asyncErrorChannel: set.AsyncErrorChannel,
		},
	}

	srv.telemetry.TracerProvider = sdktrace.NewTracerProvider(
		// needed for supporting the zpages extension
		sdktrace.WithSampler(internal.AlwaysRecord()),
	)

	var err error
	if srv.telemetry.Logger, err = telemetrylogs.NewLogger(set.Config.Service.Telemetry.Logs, set.LoggingOptions); err != nil {
		return nil, fmt.Errorf("failed to get logger: %w", err)
	}

	if srv.host.builtExtensions, err = extensions.Build(context.Background(), srv.telemetry, srv.buildInfo, srv.config.Extensions, srv.config.Service.Extensions, srv.host.factories.Extensions); err != nil {
		return nil, fmt.Errorf("cannot build extensions: %w", err)
	}

	if srv.host.pipelines, err = pipelines.Build(context.Background(), srv.telemetry, srv.buildInfo, srv.config, srv.host.factories); err != nil {
		return nil, fmt.Errorf("cannot build pipelines: %w", err)
	}

	return srv, nil
}

func (srv *service) Start(ctx context.Context) error {
	if err := srv.host.builtExtensions.StartAll(ctx, srv.host); err != nil {
		return fmt.Errorf("failed to start extensions: %w", err)
	}

	if err := srv.host.pipelines.StartAll(ctx, srv.host); err != nil {
		return fmt.Errorf("cannot start pipelines: %w", err)
	}

	return srv.host.builtExtensions.NotifyPipelineReady()
}

func (srv *service) Shutdown(ctx context.Context) error {
	// Accumulate errors and proceed with shutting down remaining components.
	var errs error

	if err := srv.host.builtExtensions.NotifyPipelineNotReady(); err != nil {
		errs = multierr.Append(errs, fmt.Errorf("failed to notify that pipeline is not ready: %w", err))
	}

	if err := srv.host.pipelines.ShutdownAll(ctx); err != nil {
		errs = multierr.Append(errs, fmt.Errorf("failed to shutdown pipelines: %w", err))
	}

	if err := srv.host.builtExtensions.ShutdownAll(ctx); err != nil {
		errs = multierr.Append(errs, fmt.Errorf("failed to shutdown extensions: %w", err))
	}

	// TODO: Shutdown TracerProvider, MeterProvider, and Sync Logger.
	return errs
}
<commit_msg>nit: simplify TraceProvider initialization in one step (#5570)<commit_after>// Copyright The OpenTelemetry 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 service // import "go.opentelemetry.io/collector/service"

import (
	"context"
	"fmt"

	"go.opentelemetry.io/otel/metric/nonrecording"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
	"go.uber.org/multierr"

	"go.opentelemetry.io/collector/component"
	"go.opentelemetry.io/collector/service/internal"
	"go.opentelemetry.io/collector/service/internal/extensions"
	"go.opentelemetry.io/collector/service/internal/pipelines"
	"go.opentelemetry.io/collector/service/internal/telemetrylogs"
)

// service represents the implementation of a component.Host.
type service struct {
	buildInfo component.BuildInfo
	config    *Config
	telemetry component.TelemetrySettings
	host      *serviceHost
}

func newService(set *settings) (*service, error) {
	srv := &service{
		buildInfo: set.BuildInfo,
		config:    set.Config,
		telemetry: component.TelemetrySettings{
			TracerProvider: sdktrace.NewTracerProvider(
				// needed for supporting the zpages extension
				sdktrace.WithSampler(internal.AlwaysRecord()),
			),
			MeterProvider: nonrecording.NewNoopMeterProvider(),
			MetricsLevel:  set.Config.Telemetry.Metrics.Level,
		},
		host: &serviceHost{
			factories:         set.Factories,
			buildInfo:         set.BuildInfo,
			asyncErrorChannel: set.AsyncErrorChannel,
		},
	}

	var err error
	if srv.telemetry.Logger, err = telemetrylogs.NewLogger(set.Config.Service.Telemetry.Logs, set.LoggingOptions); err != nil {
		return nil, fmt.Errorf("failed to get logger: %w", err)
	}

	if srv.host.builtExtensions, err = extensions.Build(context.Background(), srv.telemetry, srv.buildInfo, srv.config.Extensions, srv.config.Service.Extensions, srv.host.factories.Extensions); err != nil {
		return nil, fmt.Errorf("cannot build extensions: %w", err)
	}

	if srv.host.pipelines, err = pipelines.Build(context.Background(), srv.telemetry, srv.buildInfo, srv.config, srv.host.factories); err != nil {
		return nil, fmt.Errorf("cannot build pipelines: %w", err)
	}

	return srv, nil
}

func (srv *service) Start(ctx context.Context) error {
	if err := srv.host.builtExtensions.StartAll(ctx, srv.host); err != nil {
		return fmt.Errorf("failed to start extensions: %w", err)
	}

	if err := srv.host.pipelines.StartAll(ctx, srv.host); err != nil {
		return fmt.Errorf("cannot start pipelines: %w", err)
	}

	return srv.host.builtExtensions.NotifyPipelineReady()
}

func (srv *service) Shutdown(ctx context.Context) error {
	// Accumulate errors and proceed with shutting down remaining components.
	var errs error

	if err := srv.host.builtExtensions.NotifyPipelineNotReady(); err != nil {
		errs = multierr.Append(errs, fmt.Errorf("failed to notify that pipeline is not ready: %w", err))
	}

	if err := srv.host.pipelines.ShutdownAll(ctx); err != nil {
		errs = multierr.Append(errs, fmt.Errorf("failed to shutdown pipelines: %w", err))
	}

	if err := srv.host.builtExtensions.ShutdownAll(ctx); err != nil {
		errs = multierr.Append(errs, fmt.Errorf("failed to shutdown extensions: %w", err))
	}

	// TODO: Shutdown TracerProvider, MeterProvider, and Sync Logger.
	return errs
}
<|endoftext|>


<commit_before>package service

import (
	"code.google.com/p/go-uuid/uuid"
	"fmt"
	"github.com/ninjasphere/app-presets/model"

	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/config"
	"github.com/ninjasphere/go-ninja/logger"
	nmodel "github.com/ninjasphere/go-ninja/model"
	"github.com/ninjasphere/go-ninja/rpc"
	"strings"
	"time"
)

type Connection interface {
	ExportService(service interface{}, topic string, ann *nmodel.ServiceAnnouncement) (*rpc.ExportedService, error)
	GetServiceClient(serviceTopic string) *ninja.ServiceClient
}

type PresetsService struct {
	Model       *model.Presets
	Save        func(*model.Presets)
	Conn        Connection
	Log         *logger.Logger
	initialized bool
}

func (ps *PresetsService) Init() error {
	if ps.Log == nil {
		return fmt.Errorf("illegal state: no logger")
	}
	if ps.Model == nil {
		return fmt.Errorf("illegal state: Model is nil")
	}
	if ps.Save == nil {
		return fmt.Errorf("illegal state: Save is nil")
	}
	if ps.Conn == nil {
		return fmt.Errorf("illegal state: Conn is nil")
	}

	var err error
	siteID := config.MustString("siteId")
	topic := fmt.Sprintf("$site/%s/service/%s", siteID, "presets")
	announcement := &nmodel.ServiceAnnouncement{
		Schema: "http://schema.ninjablocks.com/service/presets",
	}
	if _, err = ps.Conn.ExportService(ps, topic, announcement); err != nil {
		return err
	}

	ps.initialized = true
	return nil
}

func (ps *PresetsService) Destroy() error {
	ps.initialized = false
	return nil
}

func (ps *PresetsService) checkInit() {
	if ps.Log == nil {
		ps.Log = logger.GetLogger("com.ninja.app-presets")
	}
	if !ps.initialized {
		ps.Log.Fatalf("illegal state: the service is not initialized")
	}
}

// see: http://schema.ninjablocks.com/service/presets#fetchScenes
func (ps *PresetsService) FetchScenes(scope string) (*[]*model.Scene, error) {
	ps.checkInit()
	collect := make([]*model.Scene, 0, 0)
	for _, m := range ps.Model.Scenes {
		if m.Scope == scope {
			collect = append(collect, m)
		}
	}
	return &collect, nil
}

// see: http://schema.ninjablocks.com/service/presets#fetchScene
func (ps *PresetsService) FetchScene(id string) (*model.Scene, error) {
	ps.checkInit()
	for _, m := range ps.Model.Scenes {
		if m.ID == id {
			return m, nil
		}
	}
	return nil, fmt.Errorf("No such scene: %s", id)
}

const defaultTimeout = 10 * time.Second

// see: http://schema.ninjablocks.com/service/presets#fetchScenePrototype
func (ps *PresetsService) FetchScenePrototype(scope string) (*model.Scene, error) {
	ps.checkInit()

	location := ""
	roomOffset := strings.Index(scope, "room/")
	if roomOffset >= 0 {
		location = scope[roomOffset:]
	}
	thingClient := ps.Conn.GetServiceClient("$home/services/ThingModel")
	things := make([]*nmodel.Thing, 0)
	keptThings := make([]*nmodel.Thing, 0, len(things))
	thingClient.Call("fetchAll", nil, &things, defaultTimeout)

	for _, t := range things {
		if !t.Promoted ||
			(location != "" && (t.Location == nil || *t.Location != location)) {
			continue
		}
		keptThings = append(keptThings, t)
	}
	result := &model.Scene{
		Scope:  scope,
		Things: make([]model.ThingState, 0, len(keptThings)),
	}
	for _, t := range keptThings {
		if t.Device == nil || t.Device.Channels == nil {
			continue
		}
		thingState := model.ThingState{
			ID:       t.ID,
			Channels: make([]model.ChannelState, 0, len(*t.Device.Channels)),
		}
		for _, c := range *t.Device.Channels {
			channelState := model.ChannelState{
				ID: c.ID,
			}
			// TODO: implement a filter to exclude channels that whose cannot be restored.
			thingState.Channels = append(thingState.Channels, channelState)
		}
		if len(thingState.Channels) > 0 {
			result.Things = append(result.Things, thingState)
		}
	}
	return result, nil
}

// see: http://schema.ninjablocks.com/service/presets#storeScene
func (ps *PresetsService) StoreScene(model *model.Scene) (*model.Scene, error) {
	ps.checkInit()
	var found int

	if model.Scope == "" {
		return nil, fmt.Errorf("illegal argument: model.Scope is empty")
	}

	if model.ID == "prototype" {
		return nil, fmt.Errorf("illegal argument: model.ID == \"prototype\"")
	}

	for i, m := range ps.Model.Scenes {
		if model.ID == "" {
			if m.Scope == model.Scope && m.Slot == model.Slot {
				found = i
			}
		} else {
			if m.ID == model.ID {
				found = i
			}
		}
	}

	if model.ID == "" {
		model.ID = uuid.NewUUID().String()
	}

	if found >= len(ps.Model.Scenes) {
		ps.Model.Scenes = append(ps.Model.Scenes, model)
	} else {
		ps.Model.Scenes[found] = model
	}
	ps.Save(ps.Model)
	return model, nil
}

// see: http://schema.ninjablocks.com/service/presets#applyScene
func (ps *PresetsService) ApplyScene(id string) error {
	ps.checkInit()
	return fmt.Errorf("unimplemented function: ApplyScene")
}
<commit_msg>Inject copied state into channel.<commit_after>package service

import (
	"code.google.com/p/go-uuid/uuid"
	"fmt"
	"github.com/ninjasphere/app-presets/model"

	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/config"
	"github.com/ninjasphere/go-ninja/logger"
	nmodel "github.com/ninjasphere/go-ninja/model"
	"github.com/ninjasphere/go-ninja/rpc"
	"strings"
	"time"
)

type Connection interface {
	ExportService(service interface{}, topic string, ann *nmodel.ServiceAnnouncement) (*rpc.ExportedService, error)
	GetServiceClient(serviceTopic string) *ninja.ServiceClient
}

type PresetsService struct {
	Model       *model.Presets
	Save        func(*model.Presets)
	Conn        Connection
	Log         *logger.Logger
	initialized bool
}

func (ps *PresetsService) Init() error {
	if ps.Log == nil {
		return fmt.Errorf("illegal state: no logger")
	}
	if ps.Model == nil {
		return fmt.Errorf("illegal state: Model is nil")
	}
	if ps.Save == nil {
		return fmt.Errorf("illegal state: Save is nil")
	}
	if ps.Conn == nil {
		return fmt.Errorf("illegal state: Conn is nil")
	}

	var err error
	siteID := config.MustString("siteId")
	topic := fmt.Sprintf("$site/%s/service/%s", siteID, "presets")
	announcement := &nmodel.ServiceAnnouncement{
		Schema: "http://schema.ninjablocks.com/service/presets",
	}
	if _, err = ps.Conn.ExportService(ps, topic, announcement); err != nil {
		return err
	}

	ps.initialized = true
	return nil
}

func (ps *PresetsService) Destroy() error {
	ps.initialized = false
	return nil
}

func (ps *PresetsService) checkInit() {
	if ps.Log == nil {
		ps.Log = logger.GetLogger("com.ninja.app-presets")
	}
	if !ps.initialized {
		ps.Log.Fatalf("illegal state: the service is not initialized")
	}
}

// see: http://schema.ninjablocks.com/service/presets#fetchScenes
func (ps *PresetsService) FetchScenes(scope string) (*[]*model.Scene, error) {
	ps.checkInit()
	collect := make([]*model.Scene, 0, 0)
	for _, m := range ps.Model.Scenes {
		if m.Scope == scope {
			collect = append(collect, m)
		}
	}
	return &collect, nil
}

// see: http://schema.ninjablocks.com/service/presets#fetchScene
func (ps *PresetsService) FetchScene(id string) (*model.Scene, error) {
	ps.checkInit()
	for _, m := range ps.Model.Scenes {
		if m.ID == id {
			return m, nil
		}
	}
	return nil, fmt.Errorf("No such scene: %s", id)
}

const defaultTimeout = 10 * time.Second

func copyState(ch *nmodel.Channel) interface{} {
	if ch.LastState != nil {
		if state := ch.LastState.(map[string]interface{}), ok; ok {
			if payload, ok := state["payload"]; ok {
				return payload
			}
		}
	}
	return nil
}

// see: http://schema.ninjablocks.com/service/presets#fetchScenePrototype
func (ps *PresetsService) FetchScenePrototype(scope string) (*model.Scene, error) {
	ps.checkInit()

	location := ""
	roomOffset := strings.Index(scope, "room/")
	if roomOffset >= 0 {
		location = scope[roomOffset:]
	}
	thingClient := ps.Conn.GetServiceClient("$home/services/ThingModel")
	things := make([]*nmodel.Thing, 0)
	keptThings := make([]*nmodel.Thing, 0, len(things))
	thingClient.Call("fetchAll", nil, &things, defaultTimeout)

	for _, t := range things {
		if !t.Promoted ||
			(location != "" && (t.Location == nil || *t.Location != location)) {
			continue
		}
		keptThings = append(keptThings, t)
	}
	result := &model.Scene{
		Scope:  scope,
		Things: make([]model.ThingState, 0, len(keptThings)),
	}
	for _, t := range keptThings {
		if t.Device == nil || t.Device.Channels == nil {
			continue
		}
		thingState := model.ThingState{
			ID:       t.ID,
			Channels: make([]model.ChannelState, 0, len(*t.Device.Channels)),
		}
		for _, c := range *t.Device.Channels {
			channelState := model.ChannelState{
				ID:    c.ID,
				State: copyState(c),
			}
			// TODO: implement a filter to exclude channels that whose cannot be restored.
			thingState.Channels = append(thingState.Channels, channelState)
		}
		if len(thingState.Channels) > 0 {
			result.Things = append(result.Things, thingState)
		}
	}
	return result, nil
}

// see: http://schema.ninjablocks.com/service/presets#storeScene
func (ps *PresetsService) StoreScene(model *model.Scene) (*model.Scene, error) {
	ps.checkInit()
	var found int

	if model.Scope == "" {
		return nil, fmt.Errorf("illegal argument: model.Scope is empty")
	}

	if model.ID == "prototype" {
		return nil, fmt.Errorf("illegal argument: model.ID == \"prototype\"")
	}

	for i, m := range ps.Model.Scenes {
		if model.ID == "" {
			if m.Scope == model.Scope && m.Slot == model.Slot {
				found = i
			}
		} else {
			if m.ID == model.ID {
				found = i
			}
		}
	}

	if model.ID == "" {
		model.ID = uuid.NewUUID().String()
	}

	if found >= len(ps.Model.Scenes) {
		ps.Model.Scenes = append(ps.Model.Scenes, model)
	} else {
		ps.Model.Scenes[found] = model
	}
	ps.Save(ps.Model)
	return model, nil
}

// see: http://schema.ninjablocks.com/service/presets#applyScene
func (ps *PresetsService) ApplyScene(id string) error {
	ps.checkInit()
	return fmt.Errorf("unimplemented function: ApplyScene")
}
<|endoftext|>


<commit_before>package service

import (
	"fmt"
	"io/ioutil"
	"regexp"
	"strings"

	"github.com/juju/utils/exec"

	"github.com/juju/juju/service/common"
	"github.com/juju/juju/service/upstart"
	"github.com/juju/juju/service/windows"
	"github.com/juju/juju/version"
)

var _ Service = (*upstart.Service)(nil)
var _ Service = (*windows.Service)(nil)

// Service represents a service running on the current system
type Service interface {
	// Installed will return a boolean value that denotes
	// whether or not the service is installed
	Installed() bool
	// Exists returns whether the service configuration exists in the
	// init directory with the same content that this Service would have
	// if installed.
	Exists() bool
	// Running returns a boolean value that denotes
	// whether or not the service is running
	Running() bool
	// Start will try to start the service
	Start() error
	// Stop will try to stop the service
	Stop() error
	// StopAndRemove will stop the service and remove it
	StopAndRemove() error
	// Remove will remove the service
	Remove() error
	// Install installs a service
	Install() error
	// Config adds a config to the service, overwritting the current one
	UpdateConfig(conf common.Conf)
}

// NewService returns an interface to a service apropriate
// for the current system
func NewService(name string, conf common.Conf) Service {
	switch version.Current.OS {
	case version.Windows:
		svc := windows.NewService(name, conf)
		return svc
	default:
		return upstart.NewService(name, conf)
	}
}

func windowsListServices() ([]string, error) {
	com := exec.RunParams{
		Commands: `(Get-Service).Name`,
	}
	out, err := exec.RunCommands(com)
	if err != nil {
		return nil, err
	}
	if out.Code != 0 {
		return nil, fmt.Errorf("Error running %s: %s", com.Commands, string(out.Stderr))
	}
	return strings.Fields(string(out.Stdout)), nil
}

var servicesRe = regexp.MustCompile("^([a-zA-Z0-9-_:]+)\\.conf$")

func upstartListServices(initDir string) ([]string, error) {
	var services []string
	fis, err := ioutil.ReadDir(initDir)
	if err != nil {
		return nil, err
	}
	for _, fi := range fis {
		if groups := servicesRe.FindStringSubmatch(fi.Name()); len(groups) > 0 {
			services = append(services, groups[1])
		}
	}
	return services, nil
}

// ListServices lists all installed services on the running system
func ListServices(initDir string) ([]string, error) {
	switch version.Current.OS {
	case version.Ubuntu:
		return upstartListServices(initDir)
	case version.Windows:
		return windowsListServices()
	default:
		return upstartListServices(initDir)
	}
}
<commit_msg>Clean up service.go.<commit_after>package service

import (
	"fmt"
	"io/ioutil"
	"regexp"
	"strings"

	"github.com/juju/utils/exec"

	"github.com/juju/juju/service/common"
	"github.com/juju/juju/service/upstart"
	"github.com/juju/juju/service/windows"
	"github.com/juju/juju/version"
)

var _ Service = (*upstart.Service)(nil)
var _ Service = (*windows.Service)(nil)

// Service represents a service running on the current system
type Service interface {
	// TODO(ericsnow) Add Name and Conf.

	// Installed will return a boolean value that denotes
	// whether or not the service is installed
	Installed() bool

	// Exists returns whether the service configuration exists in the
	// init directory with the same content that this Service would have
	// if installed.
	Exists() bool

	// Running returns a boolean value that denotes
	// whether or not the service is running
	Running() bool

	// Start will try to start the service
	Start() error

	// Stop will try to stop the service
	Stop() error

	// StopAndRemove will stop the service and remove it
	StopAndRemove() error

	// Remove will remove the service
	Remove() error

	// Install installs a service
	Install() error

	// Config adds a config to the service, overwritting the current one
	UpdateConfig(conf common.Conf)
}

// TODO(ericsnow) NewService -> DiscoverService.

// TODO(ericsnow) Return an error from NewService.

// NewService returns an interface to a service apropriate
// for the current system
func NewService(name string, conf common.Conf) Service {
	switch version.Current.OS {
	case version.Windows:
		svc := windows.NewService(name, conf)
		return svc
	default:
		return upstart.NewService(name, conf)
	}
}

// TODO(ericsnow) Move windowsListServices to service/windows.ListServices.

func windowsListServices() ([]string, error) {
	com := exec.RunParams{
		Commands: `(Get-Service).Name`,
	}
	out, err := exec.RunCommands(com)
	if err != nil {
		return nil, err
	}
	if out.Code != 0 {
		return nil, fmt.Errorf("Error running %s: %s", com.Commands, string(out.Stderr))
	}
	return strings.Fields(string(out.Stdout)), nil
}

// TODO(ericsnow) Move upstartListServices to service/upstart.ListServices.

var servicesRe = regexp.MustCompile("^([a-zA-Z0-9-_:]+)\\.conf$")

func upstartListServices(initDir string) ([]string, error) {
	var services []string
	fis, err := ioutil.ReadDir(initDir)
	if err != nil {
		return nil, err
	}
	for _, fi := range fis {
		if groups := servicesRe.FindStringSubmatch(fi.Name()); len(groups) > 0 {
			services = append(services, groups[1])
		}
	}
	return services, nil
}

// ListServices lists all installed services on the running system
func ListServices(initDir string) ([]string, error) {
	switch version.Current.OS {
	case version.Ubuntu:
		return upstartListServices(initDir)
	case version.Windows:
		return windowsListServices()
	default:
		return upstartListServices(initDir)
	}
}
<|endoftext|>


<commit_before>package servicelocator

import (
	"errors"
	"github.com/spf13/viper"
	"reflect"
	"regexp"
)

var dependencyRegexp *regexp.Regexp

func init() {
	dependencyRegexp = regexp.MustCompile("%(\\w+)%")
}

type iternalConfig struct {
	Arguments   []interface{}
	Constructor string
}

type iternalConfigMap map[string]iternalConfig

type ServiceLocator struct {
	configLoader *viper.Viper
	constructors map[string]reflect.Value
	services     map[string]interface{}
	config       iternalConfigMap
}

func New(fileName string, configType string) *ServiceLocator {
	configLoader := viper.New()
	configLoader.SetConfigType(configType)
	configLoader.SetConfigName(fileName)

	return &ServiceLocator{
		configLoader: configLoader,
		constructors: make(map[string]reflect.Value),
		services:     make(map[string]interface{}),
	}
}

func (sl *ServiceLocator) AddConfigPath(path string) {
	sl.configLoader.AddConfigPath(path)
}

func (sl *ServiceLocator) Set(name string, constructor interface{}) error {
	_, foundService := sl.services[name]
	_, foundConstructor := sl.constructors[name]
	if foundService || foundConstructor {
		return errors.New("service already exists: " + name)
	}

	constructorType := reflect.TypeOf(constructor)

	if constructorType.Kind() != reflect.Func {
		sl.services[name] = constructor

		return nil
	}

	if numOut := constructorType.NumOut(); (numOut > 2) || (numOut == 0) {
		return errors.New("invalid count result elements: " + string(numOut) + " in constructor: " + name)
	} else if (numOut == 2) && constructorType.Out(1).Kind() != reflect.Interface {
		return errors.New("last result element must be error type in constructor:" + name)
	}

	sl.constructors[name] = reflect.ValueOf(constructor)

	return nil
}

func (sl *ServiceLocator) Get(name string) (service interface{}, err error) {
	if service, found := sl.services[name]; found {
		return service, nil
	}

	defer func() {
		if exception := recover(); exception != nil {
			service = nil
			err = exception.(error)
		}
	}()

	serviceConfig := sl.getConfigForService(name)

	constructor, found := sl.constructors[serviceConfig.Constructor]
	if !found {
		return nil, errors.New("constructor not found for service: " + name)
	}

	var result []reflect.Value

	if len(serviceConfig.Arguments) == 0 {
		result = constructor.Call([]reflect.Value{})
	} else {
		result = constructor.Call(sl.prepareArguments(serviceConfig.Arguments))
	}

	switch len(result) {
	case 1:
		return result[0].Interface().(interface{}), nil
	case 2:
		if result[1].Interface() != nil {
			panic(result[1].Interface().(error))
		}

		return result[0].Interface().(interface{}), nil
	}

	panic(errors.New("invalid constructor: " + name))
}

func (sl *ServiceLocator) getConfig() iternalConfigMap {
	if sl.config != nil {
		return sl.config
	}

	if err := sl.configLoader.ReadInConfig(); err != nil {
		panic(err)
	}

	config := make(iternalConfigMap)

	if err := sl.configLoader.Marshal(&config); err != nil {
		panic(err)
	}

	sl.config = config

	return sl.config
}

func (sl *ServiceLocator) getConfigForService(name string) *iternalConfig {
	config := sl.getConfig()

	if result, found := config[name]; found {
		return &result
	}

	panic(errors.New("service: " + name + " not registered"))
}

func (sl *ServiceLocator) prepareArguments(arguments []interface{}) []reflect.Value {
	result := make([]reflect.Value, len(arguments))

	for i, argument := range arguments {
		value := reflect.ValueOf(argument)

		if value.Kind() == reflect.String {
			matches := dependencyRegexp.FindStringSubmatch(argument.(string))

			if len(matches) > 0 {
				if interfaceValue, err := sl.Get(matches[1]); err != nil {
					panic(err)
				} else {
					value = reflect.ValueOf(interfaceValue)
				}
			}
		}

		result[i] = value
	}

	return result
}
<commit_msg>fix errors<commit_after>package servicelocator

import (
	"errors"
	"github.com/spf13/viper"
	"reflect"
	"regexp"
)

var dependencyRegexp *regexp.Regexp

func init() {
	dependencyRegexp = regexp.MustCompile("%(\\w+)%")
}

type iternalConfig struct {
	Arguments   []interface{}
	Constructor string
}

type iternalConfigMap map[string]iternalConfig

type ServiceLocator struct {
	configLoader *viper.Viper
	constructors map[string]reflect.Value
	services     map[string]interface{}
	config       iternalConfigMap
}

func New(fileName string, configType string) *ServiceLocator {
	configLoader := viper.New()
	configLoader.SetConfigType(configType)
	configLoader.SetConfigName(fileName)

	return &ServiceLocator{
		configLoader: configLoader,
		constructors: make(map[string]reflect.Value),
		services:     make(map[string]interface{}),
	}
}

func (sl *ServiceLocator) AddConfigPath(path string) {
	sl.configLoader.AddConfigPath(path)
}

func (sl *ServiceLocator) Set(name string, constructor interface{}) error {
	_, foundService := sl.services[name]
	_, foundConstructor := sl.constructors[name]
	if foundService || foundConstructor {
		return errors.New("service already exists: " + name)
	}

	constructorType := reflect.TypeOf(constructor)

	if constructorType.Kind() != reflect.Func {
		sl.services[name] = constructor

		return nil
	}

	if numOut := constructorType.NumOut(); (numOut > 2) || (numOut == 0) {
		return errors.New("invalid count result elements: " + string(numOut) + " in constructor: " + name)
	} else if (numOut == 2) && constructorType.Out(1).Kind() != reflect.Interface {
		return errors.New("last result element must be error type in constructor:" + name)
	}

	sl.constructors[name] = reflect.ValueOf(constructor)

	return nil
}

func (sl *ServiceLocator) Get(name string) (service interface{}, err error) {
	if service, found := sl.services[name]; found {
		return service, nil
	}

	defer func() {
		if exception := recover(); exception != nil {
			service = nil
			err = exception.(error)
		}
	}()

	serviceConfig := sl.getConfigForService(name)

	constructor, found := sl.constructors[serviceConfig.Constructor]
	if !found {
		panic(errors.New("constructor not found for service: " + name))
	}

	var result []reflect.Value

	if len(serviceConfig.Arguments) == 0 {
		result = constructor.Call([]reflect.Value{})
	} else {
		result = constructor.Call(sl.prepareArguments(serviceConfig.Arguments))
	}

	switch len(result) {
	case 1:
		return result[0].Interface(), nil
	case 2:
		if result[1].Interface() != nil {
			panic(result[1].Interface().(error))
		}

		return result[0].Interface(), nil
	}

	panic(errors.New("invalid constructor: " + name))
}

func (sl *ServiceLocator) getConfig() iternalConfigMap {
	if sl.config != nil {
		return sl.config
	}

	if err := sl.configLoader.ReadInConfig(); err != nil {
		panic(err)
	}

	config := make(iternalConfigMap)

	if err := sl.configLoader.Marshal(&config); err != nil {
		panic(err)
	}

	sl.config = config

	return sl.config
}

func (sl *ServiceLocator) getConfigForService(name string) *iternalConfig {
	config := sl.getConfig()

	if result, found := config[name]; found {
		return &result
	}

	panic(errors.New("service: " + name + " not registered"))
}

func (sl *ServiceLocator) prepareArguments(arguments []interface{}) []reflect.Value {
	result := make([]reflect.Value, len(arguments))

	for i, argument := range arguments {
		value := reflect.ValueOf(argument)

		if value.Kind() == reflect.String {
			matches := dependencyRegexp.FindStringSubmatch(argument.(string))

			if len(matches) > 0 {
				if interfaceValue, err := sl.Get(matches[1]); err != nil {
					panic(err)
				} else {
					value = reflect.ValueOf(interfaceValue)
				}
			}
		}

		result[i] = value
	}

	return result
}
<|endoftext|>


<commit_before>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package backend

import (
	//	"bytes"
	"code.google.com/p/log4go"
	"fmt"
	//	"github.com/quarnster/parser"
	//	"io/ioutil"
	//	"lime/backend/loaders"
	. "github.com/quarnster/util/text"
	"lime/backend/parser"
	"lime/backend/render"
	"lime/backend/textmate"
	. "lime/backend/util"
	"reflect"
	"runtime/debug"
	//	"sort"
	"strings"
	"sync"
)

type (
	// A View provides a view into a specific underlying buffer
	// with its own set of selections, settings, viewport, etc.
	// Multiple Views can share the same underlying data buffer.
	View struct {
		HasSettings
		HasId
		name        string
		window      *Window
		buffer      Buffer
		selection   RegionSet
		undoStack   UndoStack
		scratch     bool
		overwrite   bool
		cursyntax   string
		syntax      parser.SyntaxHighlighter
		regions     render.ViewRegionMap
		editstack   []*Edit
		lock        sync.Mutex
		modified    bool
		reparseChan chan parseReq
	}
	parseReq struct {
		forced bool
	}

	// The Edit object is an internal type passed as an argument
	// to a TextCommand. All text operations need to be associated
	// with a valid Edit object.
	Edit struct {
		invalid    bool
		composite  CompositeAction
		savedSel   RegionSet
		savedCount int
		command    string
		args       Args
		v          *View
		bypassUndo bool
	}
)

func newView(w *Window) *View {
	ret := &View{window: w, regions: make(render.ViewRegionMap)}
	ret.Settings().AddOnChange("lime.view.syntax", func() {
		ret.lock.Lock()
		defer ret.lock.Unlock()
		syn, _ := ret.Settings().Get("syntax", "").(string)
		if syn != ret.cursyntax {
			ret.cursyntax = syn
			defer ret.reparse(true)
		}
	})

	ret.reparseChan = make(chan parseReq, 32)
	go ret.parsethread()
	ret.Settings().Set("is_widget", false)
	return ret
}

func newEdit(v *View) *Edit {
	ret := &Edit{
		v:          v,
		savedCount: v.buffer.ChangeCount(),
	}
	for _, r := range v.Sel().Regions() {
		ret.savedSel.Add(r)
	}
	return ret
}

func (e *Edit) String() string {
	return fmt.Sprintf("%s: %v, %v, %v", e.command, e.args, e.bypassUndo, e.composite)
}

func (e *Edit) Apply() {
	e.composite.Apply()
}

func (e *Edit) Undo() {
	e.composite.Undo()
	e.v.Sel().Clear()
	for _, r := range e.savedSel.Regions() {
		e.v.Sel().Add(r)
	}
}

func (v *View) setBuffer(b Buffer) error {
	if v.buffer != nil {
		return fmt.Errorf("There is already a buffer set")
	}
	v.buffer = b
	// TODO(q): Dynamically load the correct syntax file
	b.AddCallback(func(_ Buffer, position, delta int) {
		v.flush(position, delta)
	})
	return nil
}

func (v *View) flush(position, delta int) {
	func() {
		v.lock.Lock()
		defer v.lock.Unlock()

		e := Prof.Enter("view.flush")
		defer e.Exit()
		v.selection.Adjust(position, delta)
		if v.syntax != nil {
			v.syntax.Adjust(position, delta)
		}
		for k, v2 := range v.regions {
			v2.Regions.Adjust(position, delta)
			v.regions[k] = v2
		}
	}()
	OnModified.Call(v)
	v.reparse(false)
}

func (v *View) parsethread() {
	pc := 0
	lastParse := -1
	doparse := func() {
		p := Prof.Enter("syntax.parse")
		defer p.Exit()
		defer func() {
			if r := recover(); r != nil {
				log4go.Error("Panic in parse thread: %v\n%s", r, string(debug.Stack()))
				if pc > 0 {
					panic(r)
				}
				pc++
			}
		}()
		b := v.Buffer()
		b.Lock()
		sub := b.Substr(Region{0, b.Size()})
		b.Unlock()
		source, _ := v.Settings().Get("syntax", "").(string)
		if len(source) == 0 {
			return
		}
		// TODO
		pr, err := textmate.NewLanguageParser(source, sub)
		if err != nil {
			log4go.Error("Couldn't parse: %v", err)
			return
		}
		syn, err := parser.NewSyntaxHighlighter(pr)
		if err != nil {
			log4go.Error("Couldn't create syntaxhighlighter: %v", err)
			return
		}
		v.lock.Lock()
		defer v.lock.Unlock()
		// Only set if it isn't invalid already, otherwise the
		// current syntax highlighting will be more accurate
		// as it will have had incremental adjustments done to it
		if v.buffer.ChangeCount() != lastParse {
			return
		}
		v.syntax = syn
		for k := range v.regions {
			if strings.HasPrefix(k, "lime.syntax") {
				delete(v.regions, k)
			}
		}
		for k, v2 := range syn.Flatten() {
			if v2.Regions.HasNonEmpty() {
				v.regions[k] = v2
			}
		}
	}
	for pr := range v.reparseChan {
		if cc := v.buffer.ChangeCount(); lastParse != cc || pr.forced {
			lastParse = cc
			doparse()
			v.Settings().Set("lime.syntax.updated", lastParse)
		}
	}
}

func (v *View) reparse(forced bool) {
	if len(v.reparseChan) < cap(v.reparseChan) || forced {
		v.reparseChan <- parseReq{forced}
	}
}

func (v *View) ScopeName(point int) string {
	v.lock.Lock()
	defer v.lock.Unlock()
	if v.syntax != nil {
		return v.syntax.ScopeName(point)
	}
	return ""
}

func (v *View) ExtractScope(point int) Region {
	v.lock.Lock()
	defer v.lock.Unlock()
	if v.syntax != nil {
		return v.syntax.ScopeExtent(point)
	}
	return Region{}
}

func (v *View) ScoreSelector(point int, selector string) int {
	if sn := v.syntax.ScopeName(point); len(sn) > 0 {
		return 1 + strings.Index(sn, selector)
	}
	return 0
}

func (v *View) Sel() *RegionSet {
	return &v.selection
}

func (v *View) Window() *Window {
	return v.window
}

func (v *View) Buffer() Buffer {
	return v.buffer
}

func (v *View) Insert(edit *Edit, point int, value string) int {
	if t, ok := v.Settings().Get("translate_tabs_to_spaces", false).(bool); ok && t && strings.Contains(value, "\t") {
		tab_size, ok := v.Settings().Get("tab_size", 4).(int)
		if !ok {
			tab_size = 4
		}

		lines := strings.Split(value, "\n")
		for i, li := range lines {
			for {
				idx := strings.Index(li, "\t")
				if idx == -1 {
					break
				}
				ai := idx
				if i == 0 {
					_, col := v.buffer.RowCol(point)
					ai = col + 1
				}
				add := 1 + ((ai + (tab_size - 1)) &^ (tab_size - 1))
				spaces := ""
				for j := ai; j < add; j++ {
					spaces += " "
				}
				li = li[:idx] + spaces + li[idx+1:]
			}
			lines[i] = li
		}
		value = strings.Join(lines, "\n")
	}
	edit.composite.AddExec(NewInsertAction(v.buffer, point, value))
	return len(value)
}

func (v *View) Erase(edit *Edit, r Region) {
	edit.composite.AddExec(NewEraseAction(v.buffer, r))
}

func (v *View) Replace(edit *Edit, r Region, value string) {
	edit.composite.AddExec(NewReplaceAction(v.buffer, r, value))
}

func (v *View) BeginEdit() *Edit {
	e := newEdit(v)
	v.editstack = append(v.editstack, e)
	return e
}

func (v *View) EndEdit(e *Edit) {
	if e.invalid {
		log4go.Fine("This edit has already been invalidated: %v, %v", e, v.editstack)
		return
	}
	i := len(v.editstack) - 1
	for i := len(v.editstack) - 1; i >= 0; i-- {
		if v.editstack[i] == e {
			break
		}
	}
	if i == -1 {
		log4go.Error("This edit isn't even in the stack... where did it come from? %v, %v", e, v.editstack)
		return
	}

	var selmod bool

	if l := len(v.editstack) - 1; i != l {
		log4go.Error("This edit wasn't last in the stack... %d !=  %d: %v, %v", i, l, e, v.editstack)
	}
	for j := len(v.editstack) - 1; j >= i; j-- {
		ce := v.editstack[j]
		ce.invalid = true
		is := reflect.DeepEqual(*v.Sel(), ce.savedSel)
		ib := v.buffer.ChangeCount() == ce.savedCount
		eq := (is && ib && ce.composite.Len() == 0)
		if !eq && is {
			selmod = true
		}
		if v.scratch || ce.bypassUndo || eq {
			continue
		}
		if i == 0 || j != i {
			// Presume someone forgot to add it in the j != i case
			v.undoStack.Add(e)
		} else {
			// This edit belongs to another edit
			v.editstack[i-1].composite.Add(ce)
		}
	}
	v.editstack = v.editstack[:i]
	if selmod {
		OnSelectionModified.Call(v)
	}
}

func (v *View) SetScratch(s bool) {
	v.scratch = s
}

func (v *View) IsScratch() bool {
	return v.scratch
}

func (v *View) OverwriteStatus() bool {
	return v.overwrite
}

func (v *View) SetOverwriteStatus(s bool) {
	v.overwrite = s
}

func (v *View) CommandHistory(idx int, modifying_only bool) (name string, args Args, count int) {
	// TODO: merge history when possible
	if i := v.undoStack.index(idx, modifying_only); i != -1 {
		e := v.undoStack.actions[i]
		return e.command, e.args, 1
	}
	return "", nil, 0
}

func (v *View) runCommand(cmd TextCommand, name string) error {
	e := v.BeginEdit()
	e.command = name
	//	e.args = args
	e.bypassUndo = cmd.BypassUndo()

	defer func() {
		v.EndEdit(e)
		if r := recover(); r != nil {
			log4go.Error("Paniced while running text command %s %v: %v\n%s", name, cmd, r, string(debug.Stack()))
		}
	}()
	p := Prof.Enter("view.cmd." + name)
	defer p.Exit()
	return cmd.Run(v, e)
}

func (v *View) AddRegions(key string, regions []Region, scope, icon string, flags render.ViewRegionFlags) {
	vr := render.ViewRegions{Scope: scope, Icon: icon, Flags: flags}
	vr.Regions.AddAll(regions)

	v.lock.Lock()
	defer v.lock.Unlock()
	v.regions[key] = vr
}

func (v *View) GetRegions(key string) (ret []Region) {
	v.lock.Lock()
	defer v.lock.Unlock()
	vr := v.regions[key]
	rs := vr.Regions.Regions()
	ret = make([]Region, len(rs))
	copy(ret, rs)
	return
}

func (v *View) EraseRegions(key string) {
	v.lock.Lock()
	defer v.lock.Unlock()
	delete(v.regions, key)
}

func (v *View) UndoStack() *UndoStack {
	return &v.undoStack
}

func (v *View) Transform(scheme render.ColourScheme, viewport Region) render.Recipe {
	v.lock.Lock()
	defer v.lock.Unlock()
	if v.syntax == nil {
		return nil
	}
	rr := make(render.ViewRegionMap)
	for k, v := range v.regions {
		rr[k] = v.Clone()
	}
	rs := render.ViewRegions{Flags: render.SELECTION}
	rs.Regions.AddAll(v.selection.Regions())
	rr["lime.selection"] = rs
	return render.Transform(scheme, rr, viewport)
}
<commit_msg>Modify EndEdit to use a more descriptive var name<commit_after>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package backend

import (
	//	"bytes"
	"code.google.com/p/log4go"
	"fmt"
	//	"github.com/quarnster/parser"
	//	"io/ioutil"
	//	"lime/backend/loaders"
	. "github.com/quarnster/util/text"
	"lime/backend/parser"
	"lime/backend/render"
	"lime/backend/textmate"
	. "lime/backend/util"
	"reflect"
	"runtime/debug"
	//	"sort"
	"strings"
	"sync"
)

type (
	// A View provides a view into a specific underlying buffer
	// with its own set of selections, settings, viewport, etc.
	// Multiple Views can share the same underlying data buffer.
	View struct {
		HasSettings
		HasId
		name        string
		window      *Window
		buffer      Buffer
		selection   RegionSet
		undoStack   UndoStack
		scratch     bool
		overwrite   bool
		cursyntax   string
		syntax      parser.SyntaxHighlighter
		regions     render.ViewRegionMap
		editstack   []*Edit
		lock        sync.Mutex
		modified    bool
		reparseChan chan parseReq
	}
	parseReq struct {
		forced bool
	}

	// The Edit object is an internal type passed as an argument
	// to a TextCommand. All text operations need to be associated
	// with a valid Edit object.
	Edit struct {
		invalid    bool
		composite  CompositeAction
		savedSel   RegionSet
		savedCount int
		command    string
		args       Args
		v          *View
		bypassUndo bool
	}
)

func newView(w *Window) *View {
	ret := &View{window: w, regions: make(render.ViewRegionMap)}
	ret.Settings().AddOnChange("lime.view.syntax", func() {
		ret.lock.Lock()
		defer ret.lock.Unlock()
		syn, _ := ret.Settings().Get("syntax", "").(string)
		if syn != ret.cursyntax {
			ret.cursyntax = syn
			defer ret.reparse(true)
		}
	})

	ret.reparseChan = make(chan parseReq, 32)
	go ret.parsethread()
	ret.Settings().Set("is_widget", false)
	return ret
}

func newEdit(v *View) *Edit {
	ret := &Edit{
		v:          v,
		savedCount: v.buffer.ChangeCount(),
	}
	for _, r := range v.Sel().Regions() {
		ret.savedSel.Add(r)
	}
	return ret
}

func (e *Edit) String() string {
	return fmt.Sprintf("%s: %v, %v, %v", e.command, e.args, e.bypassUndo, e.composite)
}

func (e *Edit) Apply() {
	e.composite.Apply()
}

func (e *Edit) Undo() {
	e.composite.Undo()
	e.v.Sel().Clear()
	for _, r := range e.savedSel.Regions() {
		e.v.Sel().Add(r)
	}
}

func (v *View) setBuffer(b Buffer) error {
	if v.buffer != nil {
		return fmt.Errorf("There is already a buffer set")
	}
	v.buffer = b
	// TODO(q): Dynamically load the correct syntax file
	b.AddCallback(func(_ Buffer, position, delta int) {
		v.flush(position, delta)
	})
	return nil
}

func (v *View) flush(position, delta int) {
	func() {
		v.lock.Lock()
		defer v.lock.Unlock()

		e := Prof.Enter("view.flush")
		defer e.Exit()
		v.selection.Adjust(position, delta)
		if v.syntax != nil {
			v.syntax.Adjust(position, delta)
		}
		for k, v2 := range v.regions {
			v2.Regions.Adjust(position, delta)
			v.regions[k] = v2
		}
	}()
	OnModified.Call(v)
	v.reparse(false)
}

func (v *View) parsethread() {
	pc := 0
	lastParse := -1
	doparse := func() {
		p := Prof.Enter("syntax.parse")
		defer p.Exit()
		defer func() {
			if r := recover(); r != nil {
				log4go.Error("Panic in parse thread: %v\n%s", r, string(debug.Stack()))
				if pc > 0 {
					panic(r)
				}
				pc++
			}
		}()
		b := v.Buffer()
		b.Lock()
		sub := b.Substr(Region{0, b.Size()})
		b.Unlock()
		source, _ := v.Settings().Get("syntax", "").(string)
		if len(source) == 0 {
			return
		}
		// TODO
		pr, err := textmate.NewLanguageParser(source, sub)
		if err != nil {
			log4go.Error("Couldn't parse: %v", err)
			return
		}
		syn, err := parser.NewSyntaxHighlighter(pr)
		if err != nil {
			log4go.Error("Couldn't create syntaxhighlighter: %v", err)
			return
		}
		v.lock.Lock()
		defer v.lock.Unlock()
		// Only set if it isn't invalid already, otherwise the
		// current syntax highlighting will be more accurate
		// as it will have had incremental adjustments done to it
		if v.buffer.ChangeCount() != lastParse {
			return
		}
		v.syntax = syn
		for k := range v.regions {
			if strings.HasPrefix(k, "lime.syntax") {
				delete(v.regions, k)
			}
		}
		for k, v2 := range syn.Flatten() {
			if v2.Regions.HasNonEmpty() {
				v.regions[k] = v2
			}
		}
	}
	for pr := range v.reparseChan {
		if cc := v.buffer.ChangeCount(); lastParse != cc || pr.forced {
			lastParse = cc
			doparse()
			v.Settings().Set("lime.syntax.updated", lastParse)
		}
	}
}

func (v *View) reparse(forced bool) {
	if len(v.reparseChan) < cap(v.reparseChan) || forced {
		v.reparseChan <- parseReq{forced}
	}
}

func (v *View) ScopeName(point int) string {
	v.lock.Lock()
	defer v.lock.Unlock()
	if v.syntax != nil {
		return v.syntax.ScopeName(point)
	}
	return ""
}

func (v *View) ExtractScope(point int) Region {
	v.lock.Lock()
	defer v.lock.Unlock()
	if v.syntax != nil {
		return v.syntax.ScopeExtent(point)
	}
	return Region{}
}

func (v *View) ScoreSelector(point int, selector string) int {
	if sn := v.syntax.ScopeName(point); len(sn) > 0 {
		return 1 + strings.Index(sn, selector)
	}
	return 0
}

func (v *View) Sel() *RegionSet {
	return &v.selection
}

func (v *View) Window() *Window {
	return v.window
}

func (v *View) Buffer() Buffer {
	return v.buffer
}

func (v *View) Insert(edit *Edit, point int, value string) int {
	if t, ok := v.Settings().Get("translate_tabs_to_spaces", false).(bool); ok && t && strings.Contains(value, "\t") {
		tab_size, ok := v.Settings().Get("tab_size", 4).(int)
		if !ok {
			tab_size = 4
		}

		lines := strings.Split(value, "\n")
		for i, li := range lines {
			for {
				idx := strings.Index(li, "\t")
				if idx == -1 {
					break
				}
				ai := idx
				if i == 0 {
					_, col := v.buffer.RowCol(point)
					ai = col + 1
				}
				add := 1 + ((ai + (tab_size - 1)) &^ (tab_size - 1))
				spaces := ""
				for j := ai; j < add; j++ {
					spaces += " "
				}
				li = li[:idx] + spaces + li[idx+1:]
			}
			lines[i] = li
		}
		value = strings.Join(lines, "\n")
	}
	edit.composite.AddExec(NewInsertAction(v.buffer, point, value))
	return len(value)
}

func (v *View) Erase(edit *Edit, r Region) {
	edit.composite.AddExec(NewEraseAction(v.buffer, r))
}

func (v *View) Replace(edit *Edit, r Region, value string) {
	edit.composite.AddExec(NewReplaceAction(v.buffer, r, value))
}

func (v *View) BeginEdit() *Edit {
	e := newEdit(v)
	v.editstack = append(v.editstack, e)
	return e
}

func (v *View) EndEdit(edit *Edit) {
	if edit.invalid {
		log4go.Fine("This edit has already been invalidated: %v, %v", edit, v.editstack)
		return
	}
	i := len(v.editstack) - 1
	for i := len(v.editstack) - 1; i >= 0; i-- {
		if v.editstack[i] == edit {
			break
		}
	}
	if i == -1 {
		log4go.Error("This edit isn't even in the stack... where did it come from? %v, %v", edit, v.editstack)
		return
	}

	var selmod bool

	if l := len(v.editstack) - 1; i != l {
		log4go.Error("This edit wasn't last in the stack... %d !=  %d: %v, %v", i, l, edit, v.editstack)
	}
	for j := len(v.editstack) - 1; j >= i; j-- {
		ce := v.editstack[j]
		ce.invalid = true
		is := reflect.DeepEqual(*v.Sel(), ce.savedSel)
		ib := v.buffer.ChangeCount() == ce.savedCount
		eq := (is && ib && ce.composite.Len() == 0)
		if !eq && is {
			selmod = true
		}
		if v.scratch || ce.bypassUndo || eq {
			continue
		}
		if i == 0 || j != i {
			// Presume someone forgot to add it in the j != i case
			v.undoStack.Add(edit)
		} else {
			// This edit belongs to another edit
			v.editstack[i-1].composite.Add(ce)
		}
	}
	v.editstack = v.editstack[:i]
	if selmod {
		OnSelectionModified.Call(v)
	}
}

func (v *View) SetScratch(s bool) {
	v.scratch = s
}

func (v *View) IsScratch() bool {
	return v.scratch
}

func (v *View) OverwriteStatus() bool {
	return v.overwrite
}

func (v *View) SetOverwriteStatus(s bool) {
	v.overwrite = s
}

func (v *View) CommandHistory(idx int, modifying_only bool) (name string, args Args, count int) {
	// TODO: merge history when possible
	if i := v.undoStack.index(idx, modifying_only); i != -1 {
		e := v.undoStack.actions[i]
		return e.command, e.args, 1
	}
	return "", nil, 0
}

func (v *View) runCommand(cmd TextCommand, name string) error {
	e := v.BeginEdit()
	e.command = name
	//	e.args = args
	e.bypassUndo = cmd.BypassUndo()

	defer func() {
		v.EndEdit(e)
		if r := recover(); r != nil {
			log4go.Error("Paniced while running text command %s %v: %v\n%s", name, cmd, r, string(debug.Stack()))
		}
	}()
	p := Prof.Enter("view.cmd." + name)
	defer p.Exit()
	return cmd.Run(v, e)
}

func (v *View) AddRegions(key string, regions []Region, scope, icon string, flags render.ViewRegionFlags) {
	vr := render.ViewRegions{Scope: scope, Icon: icon, Flags: flags}
	vr.Regions.AddAll(regions)

	v.lock.Lock()
	defer v.lock.Unlock()
	v.regions[key] = vr
}

func (v *View) GetRegions(key string) (ret []Region) {
	v.lock.Lock()
	defer v.lock.Unlock()
	vr := v.regions[key]
	rs := vr.Regions.Regions()
	ret = make([]Region, len(rs))
	copy(ret, rs)
	return
}

func (v *View) EraseRegions(key string) {
	v.lock.Lock()
	defer v.lock.Unlock()
	delete(v.regions, key)
}

func (v *View) UndoStack() *UndoStack {
	return &v.undoStack
}

func (v *View) Transform(scheme render.ColourScheme, viewport Region) render.Recipe {
	v.lock.Lock()
	defer v.lock.Unlock()
	if v.syntax == nil {
		return nil
	}
	rr := make(render.ViewRegionMap)
	for k, v := range v.regions {
		rr[k] = v.Clone()
	}
	rs := render.ViewRegions{Flags: render.SELECTION}
	rs.Regions.AddAll(v.selection.Regions())
	rr["lime.selection"] = rs
	return render.Transform(scheme, rr, viewport)
}
<|endoftext|>


<commit_before>package coreos

import "github.com/bcrusu/kcm/util"

type CloudConfigParams struct {
	Hostname      string
	DNSName       string
	IsMaster      bool
	SSHPublicKey  string
	Network       util.NetworkInfo
	ClusterDomain string
	DNSServiceIP  string
}

const cloudConfigTemplate = `#cloud-config

hostname: {{ .Hostname }}

ssh_authorized_keys:
  - {{ .SSHPublicKey }}

write_files:
  - path: /etc/systemd/journald.conf
    permissions: 0644
    content: |
      [Journal]
      SystemMaxUse=50M
      RuntimeMaxUse=50M

coreos:
{{ if .IsMaster }}
  etcd2:
    advertise-client-urls: http://0.0.0.0:2379
    listen-client-urls: http://0.0.0.0:2379
    listen-peer-urls: http://0.0.0.0:2380
    initial-cluster-state: new
    initial-cluster: {{ .Hostname }}=http://0.0.0.0:2380
    initial-advertise-peer-urls: http://0.0.0.0:2380
{{ end }}

  units:
{{ if .IsMaster }}
    - name: etcd2.service
      command: start
      drop-ins:
        - name: 10-override-name.conf
          content: |
            [Service]
            Environment=ETCD_NAME=%H
{{ end }}

    - name: cbr0.netdev
      command: start
      content: |
        [NetDev]
        Kind=bridge
        Name=cbr0
    - name: dhcp.network
      command: start
      content: |
        [Match]
        Name=eth0
        [Network]
        DHCP=yes
        SendHostname=true

    - name: docker.service
      command: start
      drop-ins:
        - name: 50-opts.conf
          content: |
            [Service]
            Environment='DOCKER_OPTS=--bridge=cbr0 --iptables=false --ip-masq=false'
            Environment='DOCKER_NOFILE=1000000'
    - name: docker-tcp.socket
      command: start
      enable: yes
      content: |
        [Unit]
        Description=Docker Socket for the API
        [Socket]
        ListenStream=2375
        BindIPv6Only=both
        Service=docker.service
        [Install]
        WantedBy=sockets.target

    - name: opt-kubernetes.mount
      command: start
      content: |
        [Unit]
        ConditionVirtualization=|vm
        [Mount]
        What=k8sConfig
        Where=/opt/kubernetes
        Options=ro,trans=virtio,version=9p2000.L
        Type=9p
    - name: opt-kubernetes-bin.mount
      command: start
      content: |
        [Unit]
        ConditionVirtualization=|vm
        After=opt-kubernetes.mount
        Requires=opt-kubernetes.mount
        [Mount]
        What=k8sBin
        Where=/opt/kubernetes/bin
        Options=ro,trans=virtio,version=9p2000.L
        Type=9p
    - name: opt-kubernetes-manifests.mount
      command: start
      content: |
        [Unit]
        ConditionVirtualization=|vm
        After=opt-kubernetes.mount
        Requires=opt-kubernetes.mount
        [Mount]
        What=k8sConfigManifests
        Where=/opt/kubernetes/manifests
        Options=ro,trans=virtio,version=9p2000.L
        Type=9p
    - name: opt-kubernetes-kubeconfig.mount
      command: start
      content: |
        [Unit]
        ConditionVirtualization=|vm
        After=opt-kubernetes.mount
        Requires=opt-kubernetes.mount
        [Mount]
        What=k8sConfigKubeconfig
        Where=/opt/kubernetes/kubeconfig
        Options=ro,trans=virtio,version=9p2000.L
        Type=9p
    - name: opt-kubernetes-addons.mount
      command: start
      content: |
        [Unit]
        ConditionVirtualization=|vm
        After=opt-kubernetes.mount
        Requires=opt-kubernetes.mount
        [Mount]
        What=k8sConfigAddons
        Where=/opt/kubernetes/addons
        Options=ro,trans=virtio,version=9p2000.L
        Type=9p
    - name: opt-cni-bin.mount
      command: start
      content: |
        [Unit]
        ConditionVirtualization=|vm
        [Mount]
        What=cniBin
        Where=/opt/cni/bin
        Options=ro,trans=virtio,version=9p2000.L
        Type=9p
    - name: opt-scripts.mount
      command: start
      content: |
        [Unit]
        ConditionVirtualization=|vm
        [Mount]
        What=scripts
        Where=/opt/scripts
        Options=ro,trans=virtio,version=9p2000.L
        Type=9p
    - name: allVmMounts.target
      command: start
      content: |
        [Unit]
        After=opt-kubernetes.mount opt-kubernetes-bin.mount opt-kubernetes-manifests.mount opt-kubernetes-kubeconfig.mount opt-kubernetes-addons.mount opt-cni-bin.mount opt-scripts.mount
        Requires=opt-kubernetes.mount opt-kubernetes-bin.mount opt-kubernetes-manifests.mount opt-kubernetes-kubeconfig.mount opt-kubernetes-addons.mount opt-cni-bin.mount opt-scripts.mount

    - name: kubelet.service
      command: start
      content: |
        [Unit]
        After=allVmMounts.target docker.service load-k8s-images.service
        ConditionFileIsExecutable=/opt/kubernetes/bin/kubelet
        Description=Kubernetes Kubelet Server
        Documentation=https://github.com/kubernetes/kubernetes
        Requires=allVmMounts.target docker.service load-k8s-images.service

        [Service]
        Restart=always
        RestartSec=2
        StartLimitInterval=0
        KillMode=process
        Environment="PATH=/opt/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
        ExecStart=/opt/kubernetes/bin/kubelet \
        --address=0.0.0.0 \
        --hostname-override={{ .DNSName }} \
        --cluster-dns={{ .DNSServiceIP }} \
        --cluster-domain={{ .ClusterDomain }} \
        --kubeconfig=/opt/kubernetes/kubeconfig/kubelet \
        --require-kubeconfig=true \
        --anonymous-auth=false \
        --register-node=true \
        --node-labels='kubernetes.io/role={{ Role }},node-role.kubernetes.io/{{ Role }}=' \
        --network-plugin=cni \
        --cni-bin-dir=/opt/cni/bin \
        --cni-conf-dir=/etc/cni/net.d \
        --allow-privileged=true \
        --pod-manifest-path=/opt/kubernetes/manifests \
        --tls-cert-file=/opt/kubernetes/certs/tls-server.pem \
        --tls-private-key-file=/opt/kubernetes/certs/tls-server-key.pem \
        --client-ca-file=/opt/kubernetes/certs/ca.pem {{ if .IsMaster }}\
        --register-with-taints='node-role.kubernetes.io/master=:NoSchedule'{{ end }}

        [Install]
        WantedBy=multi-user.target

    - name: load-k8s-images.service
      command: start
      content: |
        [Unit]
        Description=Load Kubernetes images to Docker
        After=opt-kubernetes-bin.mount docker.service
        Requires=opt-kubernetes-bin.mount docker.service

        [Service]
        WorkingDirectory=/opt/kubernetes/bin
        Type=simple
        KillMode=process
{{ if .IsMaster }}
        ExecStart=/bin/bash -c 'docker load -i kube-apiserver.tar && docker load -i kube-controller-manager.tar && docker load -i kube-scheduler.tar && docker load -i kube-proxy.tar'
{{ else }}
        ExecStart=/usr/bin/docker load -i kube-proxy.tar
{{ end }}

{{ if .IsMaster }}
    - name: apply-k8s-addons.service
      command: start
      content: |
        [Unit]
        Description=Apply Kubernetes addons
        After=kubelet.service
        Requires=kubelet.service

        [Service]
        Type=simple
        KillMode=process
        ExecStart=/opt/scripts/load_addons

        [Install]
        WantedBy=multi-user.target
{{ end }}

    - name: install-socat.service
      command: start
      content: |
        [Unit]
        Description=Install socat binary
        After=opt-scripts.mount
        Requires=opt-scripts.mount

        [Service]
        Type=simple
        KillMode=process
        ExecStart=/opt/scripts/install_socat

        [Install]
        WantedBy=multi-user.target
`
<commit_msg>load missing br_netfilter kernel module<commit_after>package coreos

import "github.com/bcrusu/kcm/util"

type CloudConfigParams struct {
	Hostname      string
	DNSName       string
	IsMaster      bool
	SSHPublicKey  string
	Network       util.NetworkInfo
	ClusterDomain string
	DNSServiceIP  string
}

const cloudConfigTemplate = `#cloud-config

hostname: {{ .Hostname }}

ssh_authorized_keys:
  - {{ .SSHPublicKey }}

write_files:
  - path: /etc/systemd/journald.conf
    permissions: 0644
    content: |
      [Journal]
      SystemMaxUse=50M
      RuntimeMaxUse=50M

coreos:
{{ if .IsMaster }}
  etcd2:
    advertise-client-urls: http://0.0.0.0:2379
    listen-client-urls: http://0.0.0.0:2379
    listen-peer-urls: http://0.0.0.0:2380
    initial-cluster-state: new
    initial-cluster: {{ .Hostname }}=http://0.0.0.0:2380
    initial-advertise-peer-urls: http://0.0.0.0:2380
{{ end }}

  units:
{{ if .IsMaster }}
    - name: etcd2.service
      command: start
      drop-ins:
        - name: 10-override-name.conf
          content: |
            [Service]
            Environment=ETCD_NAME=%H
{{ end }}

    - name: cbr0.netdev
      command: start
      content: |
        [NetDev]
        Kind=bridge
        Name=cbr0
    - name: dhcp.network
      command: start
      content: |
        [Match]
        Name=eth0
        [Network]
        DHCP=yes
        SendHostname=true

    - name: docker.service
      command: start
      drop-ins:
        - name: 50-opts.conf
          content: |
            [Service]
            Environment='DOCKER_OPTS=--bridge=cbr0 --iptables=false --ip-masq=false'
            Environment='DOCKER_NOFILE=1000000'
    - name: docker-tcp.socket
      command: start
      enable: yes
      content: |
        [Unit]
        Description=Docker Socket for the API
        [Socket]
        ListenStream=2375
        BindIPv6Only=both
        Service=docker.service
        [Install]
        WantedBy=sockets.target

    - name: load-kernel-modules.service
      command: start
      content: |
        [Unit]
        Description=Load kernel modules needed by k8s

        [Service]
        Type=simple
        KillMode=process
        ExecStart=/usr/sbin/modprobe br_netfilter

        [Install]
        WantedBy=multi-user.target

    - name: opt-kubernetes.mount
      command: start
      content: |
        [Unit]
        ConditionVirtualization=|vm
        [Mount]
        What=k8sConfig
        Where=/opt/kubernetes
        Options=ro,trans=virtio,version=9p2000.L
        Type=9p
    - name: opt-kubernetes-bin.mount
      command: start
      content: |
        [Unit]
        ConditionVirtualization=|vm
        After=opt-kubernetes.mount
        Requires=opt-kubernetes.mount
        [Mount]
        What=k8sBin
        Where=/opt/kubernetes/bin
        Options=ro,trans=virtio,version=9p2000.L
        Type=9p
    - name: opt-kubernetes-manifests.mount
      command: start
      content: |
        [Unit]
        ConditionVirtualization=|vm
        After=opt-kubernetes.mount
        Requires=opt-kubernetes.mount
        [Mount]
        What=k8sConfigManifests
        Where=/opt/kubernetes/manifests
        Options=ro,trans=virtio,version=9p2000.L
        Type=9p
    - name: opt-kubernetes-kubeconfig.mount
      command: start
      content: |
        [Unit]
        ConditionVirtualization=|vm
        After=opt-kubernetes.mount
        Requires=opt-kubernetes.mount
        [Mount]
        What=k8sConfigKubeconfig
        Where=/opt/kubernetes/kubeconfig
        Options=ro,trans=virtio,version=9p2000.L
        Type=9p
    - name: opt-kubernetes-addons.mount
      command: start
      content: |
        [Unit]
        ConditionVirtualization=|vm
        After=opt-kubernetes.mount
        Requires=opt-kubernetes.mount
        [Mount]
        What=k8sConfigAddons
        Where=/opt/kubernetes/addons
        Options=ro,trans=virtio,version=9p2000.L
        Type=9p
    - name: opt-cni-bin.mount
      command: start
      content: |
        [Unit]
        ConditionVirtualization=|vm
        [Mount]
        What=cniBin
        Where=/opt/cni/bin
        Options=ro,trans=virtio,version=9p2000.L
        Type=9p
    - name: opt-scripts.mount
      command: start
      content: |
        [Unit]
        ConditionVirtualization=|vm
        [Mount]
        What=scripts
        Where=/opt/scripts
        Options=ro,trans=virtio,version=9p2000.L
        Type=9p
    - name: allVmMounts.target
      command: start
      content: |
        [Unit]
        After=opt-kubernetes.mount opt-kubernetes-bin.mount opt-kubernetes-manifests.mount opt-kubernetes-kubeconfig.mount opt-kubernetes-addons.mount opt-cni-bin.mount opt-scripts.mount
        Requires=opt-kubernetes.mount opt-kubernetes-bin.mount opt-kubernetes-manifests.mount opt-kubernetes-kubeconfig.mount opt-kubernetes-addons.mount opt-cni-bin.mount opt-scripts.mount

    - name: kubelet.service
      command: start
      content: |
        [Unit]
        After=allVmMounts.target docker.service load-k8s-images.service
        ConditionFileIsExecutable=/opt/kubernetes/bin/kubelet
        Description=Kubernetes Kubelet Server
        Documentation=https://github.com/kubernetes/kubernetes
        Requires=allVmMounts.target docker.service load-k8s-images.service

        [Service]
        Restart=always
        RestartSec=2
        StartLimitInterval=0
        KillMode=process
        Environment="PATH=/opt/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
        ExecStart=/opt/kubernetes/bin/kubelet \
        --address=0.0.0.0 \
        --hostname-override={{ .DNSName }} \
        --cluster-dns={{ .DNSServiceIP }} \
        --cluster-domain={{ .ClusterDomain }} \
        --kubeconfig=/opt/kubernetes/kubeconfig/kubelet \
        --require-kubeconfig=true \
        --anonymous-auth=false \
        --register-node=true \
        --node-labels='kubernetes.io/role={{ Role }},node-role.kubernetes.io/{{ Role }}=' \
        --network-plugin=cni \
        --cni-bin-dir=/opt/cni/bin \
        --cni-conf-dir=/etc/cni/net.d \
        --allow-privileged=true \
        --pod-manifest-path=/opt/kubernetes/manifests \
        --tls-cert-file=/opt/kubernetes/certs/tls-server.pem \
        --tls-private-key-file=/opt/kubernetes/certs/tls-server-key.pem \
        --client-ca-file=/opt/kubernetes/certs/ca.pem {{ if .IsMaster }}\
        --register-with-taints='node-role.kubernetes.io/master=:NoSchedule'{{ end }}

        [Install]
        WantedBy=multi-user.target

    - name: load-k8s-images.service
      command: start
      content: |
        [Unit]
        Description=Load Kubernetes images to Docker
        After=opt-kubernetes-bin.mount docker.service
        Requires=opt-kubernetes-bin.mount docker.service

        [Service]
        WorkingDirectory=/opt/kubernetes/bin
        Type=simple
        KillMode=process
{{ if .IsMaster }}
        ExecStart=/bin/bash -c 'docker load -i kube-apiserver.tar && docker load -i kube-controller-manager.tar && docker load -i kube-scheduler.tar && docker load -i kube-proxy.tar'
{{ else }}
        ExecStart=/usr/bin/docker load -i kube-proxy.tar
{{ end }}

{{ if .IsMaster }}
    - name: apply-k8s-addons.service
      command: start
      content: |
        [Unit]
        Description=Apply Kubernetes addons
        After=kubelet.service
        Requires=kubelet.service

        [Service]
        Type=simple
        KillMode=process
        ExecStart=/opt/scripts/load_addons

        [Install]
        WantedBy=multi-user.target
{{ end }}

    - name: install-socat.service
      command: start
      content: |
        [Unit]
        Description=Install socat binary
        After=opt-scripts.mount
        Requires=opt-scripts.mount

        [Service]
        Type=simple
        KillMode=process
        ExecStart=/opt/scripts/install_socat

        [Install]
        WantedBy=multi-user.target
`
<|endoftext|>


<commit_before>package internal

import (
	"encoding/json"
	"io/ioutil"
	"os"

	"github.com/sirupsen/logrus"
)

// AuthJSON -- marshals/unmarshals the contents of the auth.json file
type AuthJSON struct {
	Client       AuthEntry
	Device       AuthEntry
	ExtraClients []AuthEntry `json:",omitempty"`
}

// ReadAuth -- Read auth.json from the given file path
//
// for now, this will migrate the old-style ondevice.conf auth info into the new auth.json
func ReadAuth(path string) (AuthJSON, error) {
	var file *os.File
	var err error
	var rc AuthJSON

	if file, err = os.Open(path); err != nil {
		logrus.WithError(err).WithField("path", path).Error("failed to open auth.json")
		return rc, err
	}

	var stat os.FileInfo
	if stat, err = file.Stat(); err != nil {
		logrus.WithError(err).WithField("path", path).Error("failed to check auth.json file mode")
		return rc, err
	}

	if stat.Mode() != 0o600 {
		// Note: this may fail on certain platforms - TODO add special cases as they happen
		logrus.WithField("path", path).Fatalf("auth.json isn't supposed to be accessible by other users (mode: %o)", stat.Mode())
	}

	var data []byte
	if data, err = ioutil.ReadAll(file); err != nil {
		logrus.WithError(err).WithField("path", path).Error("failed to read auth.json")
		return rc, err
	}

	if err = json.Unmarshal(data, &rc); err != nil {
		logrus.WithError(err).WithField("path", path).Error("failed to parse auth.json")
		return rc, err
	}

	// legacy overrides (probably will be deleted in v1.0)
	if user, key := os.Getenv("ONDEVICE_USER"), os.Getenv("ONDEVICE_AUTH"); user != "" || key != "" {
		logrus.Warning("ONDEVICE_USER/ONDEVICE_AUTH overrides present. these will become obsolete soon")
		rc.Client = AuthEntry{
			UserField: os.Getenv("ONDEVICE_USER"),
			KeyField:  os.Getenv("ONDEVICE_AUTH"),
		}
		rc.Device = AuthEntry{
			UserField: os.Getenv("ONDEVICE_USER"),
			KeyField:  os.Getenv("ONDEVICE_AUTH"),
			DeviceKey: rc.Device.DeviceKey,
		}
	}

	return rc, nil
}
<commit_msg>internal.AuthJSON: added .Write(), storing path in AuthJSON when calling ReadAuth()<commit_after>package internal

import (
	"encoding/json"
	"io/ioutil"
	"os"

	"github.com/sirupsen/logrus"
)

// AuthJSON -- marshals/unmarshals the contents of the auth.json file
type AuthJSON struct {
	Client       AuthEntry
	Device       AuthEntry
	ExtraClients []AuthEntry `json:",omitempty"`

	path string
}

func (j AuthJSON) Write() error {
	var data, err = json.Marshal(j)
	if err != nil {
		logrus.WithError(err).Error("failed to marshal auth.json data")
		return err
	}

	return WriteFile(data, j.path, 0o600)
}

// ReadAuth -- Read auth.json from the given file path
//
// for now, this will migrate the old-style ondevice.conf auth info into the new auth.json
func ReadAuth(path string) (AuthJSON, error) {
	var file *os.File
	var err error
	var rc = AuthJSON{
		path: path,
	}

	if file, err = os.Open(path); err != nil {
		logrus.WithError(err).WithField("path", path).Error("failed to open auth.json")
		return rc, err
	}

	var stat os.FileInfo
	if stat, err = file.Stat(); err != nil {
		logrus.WithError(err).WithField("path", path).Error("failed to check auth.json file mode")
		return rc, err
	}

	if stat.Mode() != 0o600 {
		// Note: this may fail on certain platforms - TODO add special cases as they happen
		logrus.WithField("path", path).Fatalf("auth.json isn't supposed to be accessible by other users (mode: %o)", stat.Mode())
	}

	var data []byte
	if data, err = ioutil.ReadAll(file); err != nil {
		logrus.WithError(err).WithField("path", path).Error("failed to read auth.json")
		return rc, err
	}

	if err = json.Unmarshal(data, &rc); err != nil {
		logrus.WithError(err).WithField("path", path).Error("failed to parse auth.json")
		return rc, err
	}

	// legacy overrides (probably will be deleted in v1.0)
	if user, key := os.Getenv("ONDEVICE_USER"), os.Getenv("ONDEVICE_AUTH"); user != "" || key != "" {
		logrus.Warning("ONDEVICE_USER/ONDEVICE_AUTH overrides present. these will become obsolete soon")
		rc.Client = AuthEntry{
			UserField: os.Getenv("ONDEVICE_USER"),
			KeyField:  os.Getenv("ONDEVICE_AUTH"),
		}
		rc.Device = AuthEntry{
			UserField: os.Getenv("ONDEVICE_USER"),
			KeyField:  os.Getenv("ONDEVICE_AUTH"),
			DeviceKey: rc.Device.DeviceKey,
		}
	}

	return rc, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package status_test

import (
	stdtesting "testing"

	"github.com/juju/juju/testing"
)

func TestPackage(t *stdtesting.T) {
	testing.MgoTestPackage(t)
}
<commit_msg>Ensure the status tests run.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package status

import (
	stdtesting "testing"

	"github.com/juju/juju/testing"
)

func TestPackage(t *stdtesting.T) {
	testing.MgoTestPackage(t)
}
<|endoftext|>