<commit_before>package servers

import (
	"github.com/miekg/dns"
	"net"
	"strings"
	"time"

	"github.com/hawkingrei/g53/cache"
	"github.com/hawkingrei/g53/utils"
	"github.com/hawkingrei/g53/servers/dnsutils"
)

// NewService creates a new service
func NewService() (s *utils.Service) {
	s = &utils.Service{TTL: -1}
	return
}

// ServiceListProvider represents the entrypoint to get containers
type ServiceListProvider interface {
	AddService(utils.Service)
	RemoveService(utils.Service) error
	//SetService(utils.Service, utils.Service) error
	GetService(utils.Service) ([]utils.Service, error)
	GetAllServices() []utils.Service
}

// DNSServer represents a DNS server
type DNSServer struct {
	config     *utils.Config
	server     *dns.Server
	mux        *dns.ServeMux
	publicDns  *cache.MsgCache
	privateDns *cache.Cache
}

// NewDNSServer create a new DNSServer
func NewDNSServer(c *utils.Config) *DNSServer {
	publicDns, _ := cache.NewMsgCache(10000)
	privateDns, _ := cache.New(100000000)
	s := &DNSServer{
		config:     c,
		publicDns:  publicDns,
		privateDns: privateDns,
	}

	logger.Debugf("Handling DNS requests for '%s'.", c.Domain.String())

	s.mux = dns.NewServeMux()
	s.mux.HandleFunc(".", s.handleRequest)
	s.server = &dns.Server{Addr: c.DnsAddr, Net: "udp", Handler: s.mux}

	return s
}

// Start starts the DNSServer
func (s *DNSServer) Start() error {
	logger.Infof("start DNS Server")
	return s.server.ListenAndServe()
}

// Stop stops the DNSServer
func (s *DNSServer) Stop() {
	s.server.Shutdown()
}

//func (s *DNSServer) SetService(originalValue utils.Service, modifyValue utils.Service) error {
//	return s.privateDns.Set(originalValue, modifyValue)
//}

// AddService adds a new container and thus new DNS records
func (s *DNSServer) AddService(service utils.Service) {
	if service.RecordType == "CNAME" || service.RecordType == "A" {
		if string(service.Aliases[len(service.Aliases)-1]) != "." {
			service.Aliases = string(service.Aliases) + "."
		}

		if service.RecordType == "CNAME" && string(service.Value[len(service.Value)-1]) != "." {
			service.Value = string(service.Value) + "."
		}

		s.privateDns.Add(service)

		logger.Debugf("Added service: '%s'.", service)
		logger.Debugf("Handling DNS requests for '%s'.", service.Aliases)
		s.mux.HandleFunc(service.Aliases+".", s.handleRequest)
		//for _, alias := range service.Aliases {
		//	logger.Debugf("Handling DNS requests for '%s'.", alias)
		//	s.mux.HandleFunc(alias+".", s.handleRequest)
		//}

	} else {
		logger.Warningf("Service '%s' ignored: No RecordType provided:", service)
	}
}

// RemoveService removes a new container and thus DNS records
func (s *DNSServer) RemoveService(service utils.Service) error {
	if err := s.privateDns.Remove(service); err != nil {
		return err
	}
	s.mux.HandleRemove(service.Aliases + ".")
	logger.Debugf("Removeed service '%s'", service)

	return nil
}

// GetService reads a service from the repository
func (s *DNSServer) GetService(service utils.Service) ([]utils.Service, error) {
	result, err := s.privateDns.Get(service)
	if err != nil {
		return *new([]utils.Service), err
	}
	return utils.BatchEntryToServer(&result), err
}

// GetAllServices reads all services from the repository

func (s *DNSServer) GetAllServices() []utils.Service {
	return s.privateDns.List()
}

func (s *DNSServer) queryDnsCache(r *dns.Msg) (*dns.Msg, error) {
	return dnsutils.QueryDnsCache(s.publicDns,r)

}

func (s *DNSServer) DNSExchange(c dns.Client, nameservers string, r *dns.Msg) (*dns.Msg, []dns.RR, error) {
	in, _, err := c.Exchange(r, nameservers)
	if err == nil {
		if len(in.Answer) != 0 {
			logger.Debugf("Cache answer")
			s.publicDns.Add(in.Answer)
		}
		return in, in.Answer, err
	}
	return new(dns.Msg), []dns.RR{}, err
}

func (s *DNSServer) handleForward(w dns.ResponseWriter, r *dns.Msg) {
	//r.SetEdns0(4096, true)
	logger.Debugf("Using DNS forwarding for '%s'", r.Question[0].Name)
	logger.Debugf("Forwarding DNS nameservers: %s", s.config.Nameservers.String())

	// Otherwise just forward the request to another server

	if result, err := s.queryDnsCache(r); err == nil {
		logger.Debugf("Hit Public Cache")
		w.WriteMsg(result)
		return
	}
	c := new(dns.Client)
	c.UDPSize = uint16(4096)
	c.Timeout = time.Duration(5) * time.Second
	// look at each Nameserver, stop on success
	for i := range s.config.Nameservers {
		logger.Debugf("Using Nameserver %s", s.config.Nameservers[i])

		in, _, err := s.DNSExchange(*c, s.config.Nameservers[i], r)
		if err == nil {
			w.WriteMsg(in)
			return
		}

		if i == (len(s.config.Nameservers) - 1) {
			logger.Noticef("DNS fowarding for '%s' failed: no more nameservers to try", err.Error())

			// Send failure reply
			m := new(dns.Msg)
			m.SetReply(r)
			m.Ns = s.createSOA()
			m.SetRcode(r, dns.RcodeRefused) // REFUSED
			w.WriteMsg(m)

		} else {
			logger.Errorf("DNS fowarding for '%s' failed: trying next Nameserver...", err.Error())
		}
	}
}

func (s *DNSServer) makeServiceCNAME(n string, service utils.Service) dns.RR {
	rr := new(dns.CNAME)
	var ttl int
	if service.TTL != -1 {
		ttl = service.TTL
	} else {
		ttl = s.config.Ttl
	}

	rr.Hdr = dns.RR_Header{
		Name:   n,
		Rrtype: dns.TypeCNAME,
		Class:  dns.ClassINET,
		Ttl:    uint32(ttl),
	}
	rr.Target = service.Value
	return rr
}

func (s *DNSServer) makeServiceA(n string, service utils.Service) dns.RR {
	rr := new(dns.A)
	var ttl int
	if service.TTL != -1 {
		ttl = service.TTL
	} else {
		ttl = s.config.Ttl
	}

	rr.Hdr = dns.RR_Header{
		Name:   n,
		Rrtype: dns.TypeA,
		Class:  dns.ClassINET,
		Ttl:    uint32(ttl),
	}
	rr.A = net.ParseIP(service.Value)
	return rr
}

//func (s *DNSServer) RecursionPrivate(service utils.Service) dns.RR {
//	result, err := s.privateDns.Get(service)//
//}

func (s *DNSServer) MakePrivateRR(query string, qtype uint16, m *dns.Msg) {
	result := s.queryServices(utils.Service{dns.TypeToString[qtype], "", 0, strings.ToLower(query)})
	for services := range result {
		for i := range services {
			var rr dns.RR
			switch qtype {
			case dns.TypeA:
				rr = s.makeServiceA(query, utils.EntryToServer(&services[i]))
			case dns.TypeCNAME:
				rr = s.makeServiceCNAME(query, utils.EntryToServer(&services[i]))
			default:
				// this query type isn't supported, but we do have
				// a record with this name. Per RFC 4074 sec. 3, we
				// immediately return an empty NOERROR reply.
				m.Ns = s.createSOA()
				m.MsgHdr.Authoritative = true
				continue
			}
			m.Answer = append(m.Answer, rr)

		}
	}
}

//handle with dns request
func (s *DNSServer) handleRequest(w dns.ResponseWriter, r *dns.Msg) {
	m := new(dns.Msg)
	m.Compress = true
	m.SetReply(r)
	m.RecursionAvailable = true

	// Send empty response for empty requests
	if len(r.Question) == 0 {
		m.Ns = s.createSOA()
		w.WriteMsg(m)
		return
	}

	// respond to SOA requests
	if r.Question[0].Qtype == dns.TypeSOA {
		m.Answer = s.createSOA()
		w.WriteMsg(m)
		return
	}

	m.Answer = make([]dns.RR, 0, 2)
	query := r.Question[0].Name

	// trim off any trailing dot
	if query[len(query)-1] != '.' {
		query = query + "."
	}
	existDomain := s.privateDns.Containkey(query)
	if existDomain {
		logger.Debugf("DNS record found for query '%s'  '%s'", query, dns.TypeToString[r.Question[0].Qtype])
		s.MakePrivateRR(query, r.Question[0].Qtype, m)
		if len(m.Answer) != 0 && m.Answer[0].Header().Rrtype == dns.TypeSOA {
			w.WriteMsg(m)
			return
		}
		if len(m.Answer) == 0 && r.Question[0].Qtype == dns.TypeA || r.Question[0].Qtype == dns.TypeA {
			logger.Debugf("DNS record found for query '%s'  '%s'", query, "CNAME")
			s.MakePrivateRR(query, dns.TypeA, m)
		}

		if len(m.Answer) != 0 && m.Answer[len(m.Answer)-1].Header().Rrtype == dns.TypeCNAME {
			tmplong := len(m.Answer)
			s.MakePrivateRR(m.Answer[len(m.Answer)-1].String(), dns.TypeCNAME, m)
			if len(m.Answer) == tmplong {
				c := new(dns.Client)
				c.UDPSize = uint16(4096)
				c.Timeout = time.Duration(5) * time.Second
				askmsg := new(dns.Msg)
				askmsg.Id = dns.Id()
				askmsg.RecursionDesired = true
				askmsg.Question = make([]dns.Question, 1)
				askmsg.Question[0] = dns.Question{m.Answer[len(m.Answer)-1].String(), dns.TypeCNAME, dns.ClassINET}
				for i := range s.config.Nameservers {
					in, _, err := s.DNSExchange(*c, s.config.Nameservers[i], r)
					if err == nil {
						for v := range in.Answer {
							m.Answer = append(m.Answer, in.Answer[v])
						}
						w.WriteMsg(m)
						return
					}
				}
			}
		}
	}

	// We didn't find a record corresponding to the query
	if len(m.Answer) == 0 {
		s.handleForward(w, r)
		return
	}
	w.WriteMsg(m)
	return
}

func (s *DNSServer) queryServices(service utils.Service) chan []utils.Entry {
	c := make(chan []utils.Entry, 10)
	go func() {
		result, err := s.privateDns.Get(service)
		if err == nil {
			logger.Debugf("get the number of records: ", len(result))
			c <- result
		} else {
			logger.Debugf(err.Error())
		}
		close(c)
	}()
	return c
}

// TTL is used from config so that not-found result responses are not cached
// for a long time. The other defaults left as is(skydns source) because they
// do not have an use case in this situation.
func (s *DNSServer) createSOA() []dns.RR {
	dom := dns.Fqdn(s.config.Domain.String() + ".")
	soa := &dns.SOA{
		Hdr: dns.RR_Header{
			Name:   dom,
			Rrtype: dns.TypeSOA,
			Class:  dns.ClassINET,
			Ttl:    uint32(s.config.Ttl)},
		Ns:      "g53." + dom,
		Mbox:    "g53.g53." + dom,
		Serial:  uint32(time.Now().Truncate(time.Hour).Unix()),
		Refresh: 28800,
		Retry:   7200,
		Expire:  604800,
		Minttl:  uint32(s.config.Ttl),
	}
	return []dns.RR{soa}
}
<commit_msg>fix: logical error<commit_after>package servers

import (
	"github.com/miekg/dns"
	"net"
	"strings"
	"time"

	"github.com/hawkingrei/g53/cache"
	"github.com/hawkingrei/g53/utils"
	"github.com/hawkingrei/g53/servers/dnsutils"
)

// NewService creates a new service
func NewService() (s *utils.Service) {
	s = &utils.Service{TTL: -1}
	return
}

// ServiceListProvider represents the entrypoint to get containers
type ServiceListProvider interface {
	AddService(utils.Service)
	RemoveService(utils.Service) error
	//SetService(utils.Service, utils.Service) error
	GetService(utils.Service) ([]utils.Service, error)
	GetAllServices() []utils.Service
}

// DNSServer represents a DNS server
type DNSServer struct {
	config     *utils.Config
	server     *dns.Server
	mux        *dns.ServeMux
	publicDns  *cache.MsgCache
	privateDns *cache.Cache
}

// NewDNSServer create a new DNSServer
func NewDNSServer(c *utils.Config) *DNSServer {
	publicDns, _ := cache.NewMsgCache(10000)
	privateDns, _ := cache.New(100000000)
	s := &DNSServer{
		config:     c,
		publicDns:  publicDns,
		privateDns: privateDns,
	}

	logger.Debugf("Handling DNS requests for '%s'.", c.Domain.String())

	s.mux = dns.NewServeMux()
	s.mux.HandleFunc(".", s.handleRequest)
	s.server = &dns.Server{Addr: c.DnsAddr, Net: "udp", Handler: s.mux}

	return s
}

// Start starts the DNSServer
func (s *DNSServer) Start() error {
	logger.Infof("start DNS Server")
	return s.server.ListenAndServe()
}

// Stop stops the DNSServer
func (s *DNSServer) Stop() {
	s.server.Shutdown()
}

//func (s *DNSServer) SetService(originalValue utils.Service, modifyValue utils.Service) error {
//	return s.privateDns.Set(originalValue, modifyValue)
//}

// AddService adds a new container and thus new DNS records
func (s *DNSServer) AddService(service utils.Service) {
	if service.RecordType == "CNAME" || service.RecordType == "A" {
		if string(service.Aliases[len(service.Aliases)-1]) != "." {
			service.Aliases = string(service.Aliases) + "."
		}

		if service.RecordType == "CNAME" && string(service.Value[len(service.Value)-1]) != "." {
			service.Value = string(service.Value) + "."
		}

		s.privateDns.Add(service)

		logger.Debugf("Added service: '%s'.", service)
		logger.Debugf("Handling DNS requests for '%s'.", service.Aliases)
		s.mux.HandleFunc(service.Aliases+".", s.handleRequest)
		//for _, alias := range service.Aliases {
		//	logger.Debugf("Handling DNS requests for '%s'.", alias)
		//	s.mux.HandleFunc(alias+".", s.handleRequest)
		//}

	} else {
		logger.Warningf("Service '%s' ignored: No RecordType provided:", service)
	}
}

// RemoveService removes a new container and thus DNS records
func (s *DNSServer) RemoveService(service utils.Service) error {
	if err := s.privateDns.Remove(service); err != nil {
		return err
	}
	s.mux.HandleRemove(service.Aliases + ".")
	logger.Debugf("Removeed service '%s'", service)

	return nil
}

// GetService reads a service from the repository
func (s *DNSServer) GetService(service utils.Service) ([]utils.Service, error) {
	result, err := s.privateDns.Get(service)
	if err != nil {
		return *new([]utils.Service), err
	}
	return utils.BatchEntryToServer(&result), err
}

// GetAllServices reads all services from the repository

func (s *DNSServer) GetAllServices() []utils.Service {
	return s.privateDns.List()
}

func (s *DNSServer) queryDnsCache(r *dns.Msg) (*dns.Msg, error) {
	return dnsutils.QueryDnsCache(s.publicDns,r)

}

func (s *DNSServer) DNSExchange(c dns.Client, nameservers string, r *dns.Msg) (*dns.Msg, []dns.RR, error) {
	in, _, err := c.Exchange(r, nameservers)
	if err == nil {
		if len(in.Answer) != 0 {
			logger.Debugf("Cache answer")
			s.publicDns.Add(in.Answer)
		}
		return in, in.Answer, err
	}
	return new(dns.Msg), []dns.RR{}, err
}

func (s *DNSServer) handleForward(w dns.ResponseWriter, r *dns.Msg) {
	//r.SetEdns0(4096, true)
	logger.Debugf("Using DNS forwarding for '%s'", r.Question[0].Name)
	logger.Debugf("Forwarding DNS nameservers: %s", s.config.Nameservers.String())

	// Otherwise just forward the request to another server

	if result, err := s.queryDnsCache(r); err == nil {
		logger.Debugf("Hit Public Cache")
		w.WriteMsg(result)
		return
	}
	c := new(dns.Client)
	c.UDPSize = uint16(4096)
	c.Timeout = time.Duration(5) * time.Second
	// look at each Nameserver, stop on success
	for i := range s.config.Nameservers {
		logger.Debugf("Using Nameserver %s", s.config.Nameservers[i])

		in, _, err := s.DNSExchange(*c, s.config.Nameservers[i], r)
		if err == nil {
			w.WriteMsg(in)
			return
		}

		if i == (len(s.config.Nameservers) - 1) {
			logger.Noticef("DNS fowarding for '%s' failed: no more nameservers to try", err.Error())

			// Send failure reply
			m := new(dns.Msg)
			m.SetReply(r)
			m.Ns = s.createSOA()
			m.SetRcode(r, dns.RcodeRefused) // REFUSED
			w.WriteMsg(m)

		} else {
			logger.Errorf("DNS fowarding for '%s' failed: trying next Nameserver...", err.Error())
		}
	}
}

func (s *DNSServer) makeServiceCNAME(n string, service utils.Service) dns.RR {
	rr := new(dns.CNAME)
	var ttl int
	if service.TTL != -1 {
		ttl = service.TTL
	} else {
		ttl = s.config.Ttl
	}

	rr.Hdr = dns.RR_Header{
		Name:   n,
		Rrtype: dns.TypeCNAME,
		Class:  dns.ClassINET,
		Ttl:    uint32(ttl),
	}
	rr.Target = service.Value
	return rr
}

func (s *DNSServer) makeServiceA(n string, service utils.Service) dns.RR {
	rr := new(dns.A)
	var ttl int
	if service.TTL != -1 {
		ttl = service.TTL
	} else {
		ttl = s.config.Ttl
	}

	rr.Hdr = dns.RR_Header{
		Name:   n,
		Rrtype: dns.TypeA,
		Class:  dns.ClassINET,
		Ttl:    uint32(ttl),
	}
	rr.A = net.ParseIP(service.Value)
	return rr
}

//func (s *DNSServer) RecursionPrivate(service utils.Service) dns.RR {
//	result, err := s.privateDns.Get(service)//
//}

func (s *DNSServer) MakePrivateRR(query string, qtype uint16, m *dns.Msg) {
	result := s.queryServices(utils.Service{dns.TypeToString[qtype], "", 0, strings.ToLower(query)})
	for services := range result {
		for i := range services {
			var rr dns.RR
			switch qtype {
			case dns.TypeA:
				rr = s.makeServiceA(query, utils.EntryToServer(&services[i]))
			case dns.TypeCNAME:
				rr = s.makeServiceCNAME(query, utils.EntryToServer(&services[i]))
			default:
				// this query type isn't supported, but we do have
				// a record with this name. Per RFC 4074 sec. 3, we
				// immediately return an empty NOERROR reply.
				m.Ns = s.createSOA()
				m.MsgHdr.Authoritative = true
				continue
			}
			m.Answer = append(m.Answer, rr)

		}
	}
}

//handle with dns request
func (s *DNSServer) handleRequest(w dns.ResponseWriter, r *dns.Msg) {
	m := new(dns.Msg)
	m.Compress = true
	m.SetReply(r)
	m.RecursionAvailable = true

	// Send empty response for empty requests
	if len(r.Question) == 0 {
		m.Ns = s.createSOA()
		w.WriteMsg(m)
		return
	}

	// respond to SOA requests
	if r.Question[0].Qtype == dns.TypeSOA {
		m.Answer = s.createSOA()
		w.WriteMsg(m)
		return
	}

	m.Answer = make([]dns.RR, 0, 2)
	query := r.Question[0].Name

	// trim off any trailing dot
	if query[len(query)-1] != '.' {
		query = query + "."
	}
	existDomain := s.privateDns.Containkey(query)
	if existDomain {
		logger.Debugf("DNS record found for query '%s'  '%s'", query, dns.TypeToString[r.Question[0].Qtype])
		s.MakePrivateRR(query, r.Question[0].Qtype, m)
		if len(m.Answer) != 0 && m.Answer[0].Header().Rrtype == dns.TypeSOA {
			w.WriteMsg(m)
			return
		}
		if len(m.Answer) == 0 && (r.Question[0].Qtype == dns.TypeA || r.Question[0].Qtype == dns.TypeAAAA) {
			logger.Debugf("DNS record found for query '%s'  '%s'", query, "CNAME")
			s.MakePrivateRR(query, dns.TypeA, m)
		}

		if len(m.Answer) != 0 && m.Answer[len(m.Answer)-1].Header().Rrtype == dns.TypeCNAME {
			tmplong := len(m.Answer)
			s.MakePrivateRR(m.Answer[len(m.Answer)-1].String(), dns.TypeCNAME, m)
			if len(m.Answer) == tmplong {
				c := new(dns.Client)
				c.UDPSize = uint16(4096)
				c.Timeout = time.Duration(5) * time.Second
				askmsg := new(dns.Msg)
				askmsg.Id = dns.Id()
				askmsg.RecursionDesired = true
				askmsg.Question = make([]dns.Question, 1)
				askmsg.Question[0] = dns.Question{m.Answer[len(m.Answer)-1].String(), dns.TypeCNAME, dns.ClassINET}
				for i := range s.config.Nameservers {
					in, _, err := s.DNSExchange(*c, s.config.Nameservers[i], r)
					if err == nil {
						for v := range in.Answer {
							m.Answer = append(m.Answer, in.Answer[v])
						}
						w.WriteMsg(m)
						return
					}
				}
			}
		}
	}

	// We didn't find a record corresponding to the query
	if len(m.Answer) == 0 {
		s.handleForward(w, r)
		return
	}
	w.WriteMsg(m)
	return
}

func (s *DNSServer) queryServices(service utils.Service) chan []utils.Entry {
	c := make(chan []utils.Entry, 10)
	go func() {
		result, err := s.privateDns.Get(service)
		if err == nil {
			logger.Debugf("get the number of records: ", len(result))
			c <- result
		} else {
			logger.Debugf(err.Error())
		}
		close(c)
	}()
	return c
}

// TTL is used from config so that not-found result responses are not cached
// for a long time. The other defaults left as is(skydns source) because they
// do not have an use case in this situation.
func (s *DNSServer) createSOA() []dns.RR {
	dom := dns.Fqdn(s.config.Domain.String() + ".")
	soa := &dns.SOA{
		Hdr: dns.RR_Header{
			Name:   dom,
			Rrtype: dns.TypeSOA,
			Class:  dns.ClassINET,
			Ttl:    uint32(s.config.Ttl)},
		Ns:      "g53." + dom,
		Mbox:    "g53.g53." + dom,
		Serial:  uint32(time.Now().Truncate(time.Hour).Unix()),
		Refresh: 28800,
		Retry:   7200,
		Expire:  604800,
		Minttl:  uint32(s.config.Ttl),
	}
	return []dns.RR{soa}
}
<|endoftext|>


<commit_before>package service

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"

	"github.com/intel-data/app-launching-service-broker/utils"
)

// CFClient object
type CFClient struct {
	config *ServiceConfig
}

// NewCFClient creates a new isntance of CFClient
func NewCFClient(c *ServiceConfig) *CFClient {
	return &CFClient{
		config: c,
	}
}

func (c *CFClient) initialize() (*utils.CommandLogger, error) {
	log.Println("initializing...")

	// yep, this is a royal hack, should get this from the env somehow
	pushID := genRandomString(8)
	appDir, err := ioutil.TempDir(c.config.CFEnv.TempDir, pushID)
	if err != nil {
		log.Fatalf("err creating a temp dir: %v", err)
		return nil, err
	}

	// api
	cf := utils.NewCommandLogger("cf")

	// TODO: remove the skip API validation part once real cert deployed
	cf.WithArgs("api", c.config.APIEndpoint, "--skip-ssl-validation").
		WithEnv("CF_HOME", appDir).Exec()
	if cf.Err() != nil {
		log.Fatalf("err cmd: %v", cf)
		return cf, cf.Err()
	}

	// auth
	cf.WithArgs("auth", c.config.APIUser, c.config.APIPassword).Exec()
	if cf.Err() != nil {
		log.Fatalf("err cmd: %v", cf)
		return cf, cf.Err()
	}

	return cf, nil
}

func (c *CFClient) provision(ctx *CFServiceContext) error {
	log.Printf("provisioning service: %v", ctx)

	// initialize
	cf, err := c.initialize()
	if err != nil {
		log.Fatalf("err initializing command: %v", err)
		return err
	}

	// target
	cf.WithArgs("target", "-o", ctx.OrgName, "-s", ctx.SpaceName).Exec()
	if cf.Err() != nil {
		log.Fatalf("err cmd: %v", cf)
		return cf.Err()
	}

	// push
	cf.WithArgs("push", ctx.AppName, "-p", c.config.AppSource, "--no-start").Exec()
	if cf.Err() != nil {
		log.Printf("push error: %s", cf.Err())
		c.deprovision(ctx)
		return cf.Err()
	}

	// TODO: Add cleanup of dependencies
	for i, dep := range c.config.Dependencies {
		depName := dep.Name + "-" + ctx.AppName
		cf.WithArgs("create-service", dep.Name, dep.Plan, depName).Exec()
		if cf.Err() != nil {
			log.Printf("err on dependency[%d]: %s - %v", i, depName, cf)
			return cf.Err()
		}

		// bind
		cf.WithArgs("bind-service", ctx.AppName, depName).Exec()
		if cf.Err() != nil {
			log.Printf("err on bind[%d]: %s > %s - %v", i, ctx.AppName, depName, cf)
			return cf.Err()
		}

		//TODO: check if we need to restage the app after binding
	}

	// start
	cf.WithArgs("start", ctx.AppName).Exec()
	if cf.Err() != nil {
		log.Printf("err cmd: %v", cf)
		c.deprovision(ctx)
		return cf.Err()
	}

	return nil
}

func (c *CFClient) deprovision(ctx *CFServiceContext) error {
	log.Printf("deprovision service: %v", ctx)

	// initialize
	cf, err := c.initialize()
	if err != nil {
		log.Fatalf("err initializing command: %v", err)
		return err
	}

	// target
	cf.WithArgs("target", "-o", ctx.OrgName, "-s", ctx.SpaceName).Exec()
	if cf.Err() != nil {
		log.Fatalf("err cmd: %v", cf)
		return cf.Err()
	}

	// delete app
	cf.WithArgs("delete", ctx.AppName, "-f").Exec()
	if cf.Err() != nil {
		log.Printf("err cmd: %v", cf)
		return cf.Err()
	}

	// TODO: Does the service have to unbined first
	//       or deleting app will take care of it
	for i, dep := range c.config.Dependencies {
		depName := dep.Name + "-" + ctx.AppName
		cf.WithArgs("delete-service", depName, "-f").Exec()
		if cf.Err() != nil {
			log.Printf("err on dependency delete[%d]: %s - %v", i, depName, cf)
		}
	}

	return nil
}

func (c *CFClient) queryAPI(query string) (string, error) {
	log.Printf("running query: %s", query)
	cf, err := c.initialize()
	if err != nil {
		log.Fatalf("err initializing command: %v", err)
		return "", err
	}
	cf.WithArgs("curl", query).Exec()
	return cf.Out(), cf.Err()
}

func (c *CFClient) getContextFromSpaceOrg(instanceID, spaceGUID, orgGUID string) (*CFServiceContext, error) {
	log.Printf("getting service context for ID %s in org %s space %s", instanceID, orgGUID, spaceGUID)

	t := NewCFServiceContext(instanceID)

	space, err := c.getSpace(spaceGUID)
	if err != nil {
		log.Printf("error getting space: %v", err)
		return nil, err
	}
	t.SpaceName = space.Name

	org, err := c.getOrg(orgGUID)
	if err != nil {
		log.Printf("error getting org: %v", err)
		return nil, err
	}
	t.OrgName = org.Name

	return t, nil

}

func (c *CFClient) getContextFromServiceInstanceID(instanceID string) (*CFServiceContext, error) {
	log.Printf("getting service context for: %s", instanceID)

	t := &CFServiceContext{}
	t.InstanceID = instanceID

	srv, err := c.getService(instanceID)
	if err != nil {
		log.Printf("error getting service: %v", err)
		return nil, err
	}
	t.AppName = fmt.Sprintf("%s-%s", Config.ServiceName, instanceID)

	space, err := c.getSpace(srv.SpaceGUID)
	if err != nil {
		log.Printf("error getting space: %v", err)
		return nil, err
	}
	t.SpaceName = space.Name

	org, err := c.getOrg(space.OrgGUID)
	if err != nil {
		log.Printf("error getting org: %v", err)
		return nil, err
	}
	t.OrgName = org.Name

	return t, nil

}

func (c *CFClient) getService(instanceID string) (*cfApp, error) {
	log.Printf("getting service info for: %s", instanceID)
	query := fmt.Sprintf("/v2/service_instances/%s", instanceID)
	resp, err := c.queryAPI(query)
	if err != nil {
		return nil, errors.New("query error")
	}

	// cf-client.go:150: running query: /v2/service_instances/26576e51...
	// cf-client.go:26: initializing...
	// {
	//    "code": 60004,
	//    "description": "The service instance could not be found: 26576e51-8a47-46e3-bd6e-5908287e9935",
	//    "error_code": "CF-ServiceInstanceNotFound"
	// }
	//
	// TODO: map results to a CFError struct to see if an error was returned.
	// FIXME: looks like service instance object doesn't exist when "cf create-service" called
	// TODO: perhaps a background worker to rename service instances later?

	t := &cfAppResource{}
	log.Println(string(resp))
	err2 := json.Unmarshal([]byte(resp), &t)
	if err2 != nil {
		log.Fatalf("err unmarshaling: %v - %v", err2, resp)
		return nil, errors.New("invalid JSON")
	}
	log.Printf("service output: %v", t)
	t.Entity.GUID = t.Meta.GUID
	return &t.Entity, nil
}

func (c *CFClient) getOrg(orgID string) (*cfApp, error) {
	log.Printf("getting org info for: %s", orgID)
	query := fmt.Sprintf("/v2/organizations/%s", orgID)
	resp, err := c.queryAPI(query)
	if err != nil {
		return nil, errors.New("query error")
	}
	log.Println(string(resp))
	t := &cfAppResource{}
	err2 := json.Unmarshal([]byte(resp), &t)
	if err2 != nil {
		log.Fatalf("err unmarshaling: %v - %v", err2, resp)
		return nil, errors.New("invalid JSON")
	}
	log.Printf("org output: %v", t)
	t.Entity.GUID = t.Meta.GUID
	return &t.Entity, nil
}

func (c *CFClient) getSpace(spaceID string) (*cfSpace, error) {
	log.Printf("getting space info for: %s", spaceID)
	query := fmt.Sprintf("/v2/spaces/%s", spaceID)
	resp, err := c.queryAPI(query)
	if err != nil {
		return nil, errors.New("query error")
	}
	log.Println(string(resp))
	t := &cfSpaceResource{}
	err2 := json.Unmarshal([]byte(resp), &t)
	if err2 != nil {
		log.Fatalf("err unmarshaling: %v - %v", err2, resp)
		return nil, errors.New("invalid JSON")
	}
	log.Printf("space output: %v", t)
	t.Entity.GUID = t.Meta.GUID
	return &t.Entity, nil
}

func (c *CFClient) getApp(appID string) (*cfApp, error) {
	log.Printf("getting app info for: %s", appID)
	query := fmt.Sprintf("/v2/apps/%s", appID)
	resp, err := c.queryAPI(query)
	if err != nil {
		return nil, errors.New("query error")
	}
	log.Println(string(resp))
	t := &cfAppResource{}
	err2 := json.Unmarshal([]byte(resp), &t)
	if err2 != nil {
		log.Fatalf("err unmarshaling: %v - %v", err2, resp)
		return nil, errors.New("invalid JSON")
	}
	log.Printf("app output: %v", t)
	t.Entity.GUID = t.Meta.GUID
	return &t.Entity, nil
}

func (c *CFClient) getBinding(bindingID string) (*CFBindingResponse, error) {
	log.Printf("getting service binding for: %s", bindingID)
	query := fmt.Sprintf("/v2/service_bindings/%s", bindingID)
	resp, err := c.queryAPI(query)
	if err != nil {
		return nil, errors.New("query error")
	}
	log.Println(string(resp))
	t := &cfBindingResource{}
	err2 := json.Unmarshal([]byte(resp), &t)
	if err2 != nil {
		log.Fatalf("err unmarshaling: %v - %v", err2, resp)
		return nil, errors.New("invalid JSON")
	}
	log.Printf("service binding output: %v", t)
	t.Entity.GUID = t.Meta.GUID
	return &t.Entity, nil
}

func (c *CFClient) getApps() (*cfAppsResponse, error) {
	log.Println("getting apps...")
	query := "/v2/apps?results-per-page=100"
	resp, err := c.queryAPI(query)
	if err != nil {
		return nil, errors.New("query error")
	}
	log.Println(string(resp))
	t := &cfAppsResponse{}
	err2 := json.Unmarshal([]byte(resp), &t)
	if err2 != nil {
		log.Fatalf("err unmarshaling: %v - %v", err2, resp)
		return nil, errors.New("invalid JSON")
	}
	log.Printf("apps output: %v", t)
	return t, nil
}

func (c *CFClient) getServices() (*cfAppsResponse, error) {
	log.Println("getting services...")
	query := "/v2/service_instances?results-per-page=100"
	resp, err := c.queryAPI(query)
	if err != nil {
		return nil, errors.New("query error")
	}
	log.Println(string(resp))
	t := &cfAppsResponse{}
	err2 := json.Unmarshal([]byte(resp), &t)
	if err2 != nil {
		log.Fatalf("err unmarshaling: %v - %v", err2, resp)
		return nil, errors.New("invalid JSON")
	}
	log.Printf("services output: %v", t)
	return t, nil
}
<commit_msg>remove comment - initial start performs staging<commit_after>package service

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"

	"github.com/intel-data/app-launching-service-broker/utils"
)

// CFClient object
type CFClient struct {
	config *ServiceConfig
}

// NewCFClient creates a new isntance of CFClient
func NewCFClient(c *ServiceConfig) *CFClient {
	return &CFClient{
		config: c,
	}
}

func (c *CFClient) initialize() (*utils.CommandLogger, error) {
	log.Println("initializing...")

	// yep, this is a royal hack, should get this from the env somehow
	pushID := genRandomString(8)
	appDir, err := ioutil.TempDir(c.config.CFEnv.TempDir, pushID)
	if err != nil {
		log.Fatalf("err creating a temp dir: %v", err)
		return nil, err
	}

	// api
	cf := utils.NewCommandLogger("cf")

	// TODO: remove the skip API validation part once real cert deployed
	cf.WithArgs("api", c.config.APIEndpoint, "--skip-ssl-validation").
		WithEnv("CF_HOME", appDir).Exec()
	if cf.Err() != nil {
		log.Fatalf("err cmd: %v", cf)
		return cf, cf.Err()
	}

	// auth
	cf.WithArgs("auth", c.config.APIUser, c.config.APIPassword).Exec()
	if cf.Err() != nil {
		log.Fatalf("err cmd: %v", cf)
		return cf, cf.Err()
	}

	return cf, nil
}

func (c *CFClient) provision(ctx *CFServiceContext) error {
	log.Printf("provisioning service: %v", ctx)

	// initialize
	cf, err := c.initialize()
	if err != nil {
		log.Fatalf("err initializing command: %v", err)
		return err
	}

	// target
	cf.WithArgs("target", "-o", ctx.OrgName, "-s", ctx.SpaceName).Exec()
	if cf.Err() != nil {
		log.Fatalf("err cmd: %v", cf)
		return cf.Err()
	}

	// push
	cf.WithArgs("push", ctx.AppName, "-p", c.config.AppSource, "--no-start").Exec()
	if cf.Err() != nil {
		log.Printf("push error: %s", cf.Err())
		c.deprovision(ctx)
		return cf.Err()
	}

	// TODO: Add cleanup of dependencies
	for i, dep := range c.config.Dependencies {
		depName := dep.Name + "-" + ctx.AppName
		cf.WithArgs("create-service", dep.Name, dep.Plan, depName).Exec()
		if cf.Err() != nil {
			log.Printf("err on dependency[%d]: %s - %v", i, depName, cf)
			return cf.Err()
		}

		// bind
		cf.WithArgs("bind-service", ctx.AppName, depName).Exec()
		if cf.Err() != nil {
			log.Printf("err on bind[%d]: %s > %s - %v", i, ctx.AppName, depName, cf)
			return cf.Err()
		}
	}

	// start
	cf.WithArgs("start", ctx.AppName).Exec()
	if cf.Err() != nil {
		log.Printf("err cmd: %v", cf)
		c.deprovision(ctx)
		return cf.Err()
	}

	return nil
}

func (c *CFClient) deprovision(ctx *CFServiceContext) error {
	log.Printf("deprovision service: %v", ctx)

	// initialize
	cf, err := c.initialize()
	if err != nil {
		log.Fatalf("err initializing command: %v", err)
		return err
	}

	// target
	cf.WithArgs("target", "-o", ctx.OrgName, "-s", ctx.SpaceName).Exec()
	if cf.Err() != nil {
		log.Fatalf("err cmd: %v", cf)
		return cf.Err()
	}

	// delete app
	cf.WithArgs("delete", ctx.AppName, "-f").Exec()
	if cf.Err() != nil {
		log.Printf("err cmd: %v", cf)
		return cf.Err()
	}

	// TODO: Does the service have to unbined first
	//       or deleting app will take care of it
	for i, dep := range c.config.Dependencies {
		depName := dep.Name + "-" + ctx.AppName
		cf.WithArgs("delete-service", depName, "-f").Exec()
		if cf.Err() != nil {
			log.Printf("err on dependency delete[%d]: %s - %v", i, depName, cf)
		}
	}

	return nil
}

func (c *CFClient) queryAPI(query string) (string, error) {
	log.Printf("running query: %s", query)
	cf, err := c.initialize()
	if err != nil {
		log.Fatalf("err initializing command: %v", err)
		return "", err
	}
	cf.WithArgs("curl", query).Exec()
	return cf.Out(), cf.Err()
}

func (c *CFClient) getContextFromSpaceOrg(instanceID, spaceGUID, orgGUID string) (*CFServiceContext, error) {
	log.Printf("getting service context for ID %s in org %s space %s", instanceID, orgGUID, spaceGUID)

	t := NewCFServiceContext(instanceID)

	space, err := c.getSpace(spaceGUID)
	if err != nil {
		log.Printf("error getting space: %v", err)
		return nil, err
	}
	t.SpaceName = space.Name

	org, err := c.getOrg(orgGUID)
	if err != nil {
		log.Printf("error getting org: %v", err)
		return nil, err
	}
	t.OrgName = org.Name

	return t, nil

}

func (c *CFClient) getContextFromServiceInstanceID(instanceID string) (*CFServiceContext, error) {
	log.Printf("getting service context for: %s", instanceID)

	t := &CFServiceContext{}
	t.InstanceID = instanceID

	srv, err := c.getService(instanceID)
	if err != nil {
		log.Printf("error getting service: %v", err)
		return nil, err
	}
	t.AppName = fmt.Sprintf("%s-%s", Config.ServiceName, instanceID)

	space, err := c.getSpace(srv.SpaceGUID)
	if err != nil {
		log.Printf("error getting space: %v", err)
		return nil, err
	}
	t.SpaceName = space.Name

	org, err := c.getOrg(space.OrgGUID)
	if err != nil {
		log.Printf("error getting org: %v", err)
		return nil, err
	}
	t.OrgName = org.Name

	return t, nil

}

func (c *CFClient) getService(instanceID string) (*cfApp, error) {
	log.Printf("getting service info for: %s", instanceID)
	query := fmt.Sprintf("/v2/service_instances/%s", instanceID)
	resp, err := c.queryAPI(query)
	if err != nil {
		return nil, errors.New("query error")
	}

	// cf-client.go:150: running query: /v2/service_instances/26576e51...
	// cf-client.go:26: initializing...
	// {
	//    "code": 60004,
	//    "description": "The service instance could not be found: 26576e51-8a47-46e3-bd6e-5908287e9935",
	//    "error_code": "CF-ServiceInstanceNotFound"
	// }
	//
	// TODO: map results to a CFError struct to see if an error was returned.
	// FIXME: looks like service instance object doesn't exist when "cf create-service" called
	// TODO: perhaps a background worker to rename service instances later?

	t := &cfAppResource{}
	log.Println(string(resp))
	err2 := json.Unmarshal([]byte(resp), &t)
	if err2 != nil {
		log.Fatalf("err unmarshaling: %v - %v", err2, resp)
		return nil, errors.New("invalid JSON")
	}
	log.Printf("service output: %v", t)
	t.Entity.GUID = t.Meta.GUID
	return &t.Entity, nil
}

func (c *CFClient) getOrg(orgID string) (*cfApp, error) {
	log.Printf("getting org info for: %s", orgID)
	query := fmt.Sprintf("/v2/organizations/%s", orgID)
	resp, err := c.queryAPI(query)
	if err != nil {
		return nil, errors.New("query error")
	}
	log.Println(string(resp))
	t := &cfAppResource{}
	err2 := json.Unmarshal([]byte(resp), &t)
	if err2 != nil {
		log.Fatalf("err unmarshaling: %v - %v", err2, resp)
		return nil, errors.New("invalid JSON")
	}
	log.Printf("org output: %v", t)
	t.Entity.GUID = t.Meta.GUID
	return &t.Entity, nil
}

func (c *CFClient) getSpace(spaceID string) (*cfSpace, error) {
	log.Printf("getting space info for: %s", spaceID)
	query := fmt.Sprintf("/v2/spaces/%s", spaceID)
	resp, err := c.queryAPI(query)
	if err != nil {
		return nil, errors.New("query error")
	}
	log.Println(string(resp))
	t := &cfSpaceResource{}
	err2 := json.Unmarshal([]byte(resp), &t)
	if err2 != nil {
		log.Fatalf("err unmarshaling: %v - %v", err2, resp)
		return nil, errors.New("invalid JSON")
	}
	log.Printf("space output: %v", t)
	t.Entity.GUID = t.Meta.GUID
	return &t.Entity, nil
}

func (c *CFClient) getApp(appID string) (*cfApp, error) {
	log.Printf("getting app info for: %s", appID)
	query := fmt.Sprintf("/v2/apps/%s", appID)
	resp, err := c.queryAPI(query)
	if err != nil {
		return nil, errors.New("query error")
	}
	log.Println(string(resp))
	t := &cfAppResource{}
	err2 := json.Unmarshal([]byte(resp), &t)
	if err2 != nil {
		log.Fatalf("err unmarshaling: %v - %v", err2, resp)
		return nil, errors.New("invalid JSON")
	}
	log.Printf("app output: %v", t)
	t.Entity.GUID = t.Meta.GUID
	return &t.Entity, nil
}

func (c *CFClient) getBinding(bindingID string) (*CFBindingResponse, error) {
	log.Printf("getting service binding for: %s", bindingID)
	query := fmt.Sprintf("/v2/service_bindings/%s", bindingID)
	resp, err := c.queryAPI(query)
	if err != nil {
		return nil, errors.New("query error")
	}
	log.Println(string(resp))
	t := &cfBindingResource{}
	err2 := json.Unmarshal([]byte(resp), &t)
	if err2 != nil {
		log.Fatalf("err unmarshaling: %v - %v", err2, resp)
		return nil, errors.New("invalid JSON")
	}
	log.Printf("service binding output: %v", t)
	t.Entity.GUID = t.Meta.GUID
	return &t.Entity, nil
}

func (c *CFClient) getApps() (*cfAppsResponse, error) {
	log.Println("getting apps...")
	query := "/v2/apps?results-per-page=100"
	resp, err := c.queryAPI(query)
	if err != nil {
		return nil, errors.New("query error")
	}
	log.Println(string(resp))
	t := &cfAppsResponse{}
	err2 := json.Unmarshal([]byte(resp), &t)
	if err2 != nil {
		log.Fatalf("err unmarshaling: %v - %v", err2, resp)
		return nil, errors.New("invalid JSON")
	}
	log.Printf("apps output: %v", t)
	return t, nil
}

func (c *CFClient) getServices() (*cfAppsResponse, error) {
	log.Println("getting services...")
	query := "/v2/service_instances?results-per-page=100"
	resp, err := c.queryAPI(query)
	if err != nil {
		return nil, errors.New("query error")
	}
	log.Println(string(resp))
	t := &cfAppsResponse{}
	err2 := json.Unmarshal([]byte(resp), &t)
	if err2 != nil {
		log.Fatalf("err unmarshaling: %v - %v", err2, resp)
		return nil, errors.New("invalid JSON")
	}
	log.Printf("services output: %v", t)
	return t, nil
}
<|endoftext|>


<commit_before>package models

import (
	"github.com/netlify/gotrue/storage/namespace"
	"database/sql"
	"time"

	"github.com/gobuffalo/pop"
	"github.com/netlify/gotrue/conf"
	"github.com/netlify/gotrue/storage"
	"github.com/pkg/errors"
	"github.com/gobuffalo/uuid"
)

const baseConfigKey = ""

type Instance struct {
	ID uuid.UUID `json:"id" db:"id"`
	// Netlify UUID
	UUID uuid.UUID `json:"uuid,omitempty" db:"uuid"`

	BaseConfig *conf.Configuration `json:"config" db:"raw_base_config"`

	CreatedAt time.Time `json:"created_at" db:"created_at"`
	UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}

func (i *Instance) TableName() string {
	tableName := "instances"

	if namespace.GetNamespace() != "" {
		return namespace.GetNamespace() + "_" + tableName
	}

	return tableName
}

// Config loads the the base configuration values with defaults.
func (i *Instance) Config() (*conf.Configuration, error) {
	if i.BaseConfig == nil {
		return nil, errors.New("no configuration data available")
	}

	baseConf := &conf.Configuration{}
	*baseConf = *i.BaseConfig
	baseConf.ApplyDefaults()

	return baseConf, nil
}

// UpdateConfig updates the base config
func (i *Instance) UpdateConfig(tx *storage.Connection, config *conf.Configuration) error {
	i.BaseConfig = config
	return tx.UpdateOnly(i, "raw_base_config")
}

// GetInstance finds an instance by ID
func GetInstance(tx *storage.Connection, instanceID uuid.UUID) (*Instance, error) {
	instance := Instance{}
	if err := tx.Find(&instance, instanceID); err != nil {
		if errors.Cause(err) == sql.ErrNoRows {
			return nil, InstanceNotFoundError{}
		}
		return nil, errors.Wrap(err, "error finding instance")
	}
	return &instance, nil
}

func GetInstanceByUUID(tx *storage.Connection, uuid uuid.UUID) (*Instance, error) {
	instance := Instance{}
	if err := tx.Where("uuid = ?", uuid).First(&instance); err != nil {
		if errors.Cause(err) == sql.ErrNoRows {
			return nil, InstanceNotFoundError{}
		}
		return nil, errors.Wrap(err, "error finding instance")
	}
	return &instance, nil
}

func DeleteInstance(conn *storage.Connection, instance *Instance) error {
	return conn.Transaction(func(tx *storage.Connection) error {
		delModels := map[string]*pop.Model{
			"user":          &pop.Model{Value: User{}},
			"refresh token": &pop.Model{Value: RefreshToken{}},
		}

		for name, dm := range delModels {
			if err := tx.RawQuery("DELETE FROM "+dm.TableName()+" WHERE instance_id = ?", instance.ID).Exec(); err != nil {
				return errors.Wrapf(err, "Error deleting %s records", name)
			}
		}

		return errors.Wrap(tx.Destroy(instance), "Error deleting instance record")
	})
}
<commit_msg>Fix wrong tablename when deleting instance (#220)<commit_after>package models

import (
	"github.com/netlify/gotrue/storage/namespace"
	"database/sql"
	"time"

	"github.com/gobuffalo/pop"
	"github.com/netlify/gotrue/conf"
	"github.com/netlify/gotrue/storage"
	"github.com/pkg/errors"
	"github.com/gobuffalo/uuid"
)

const baseConfigKey = ""

type Instance struct {
	ID uuid.UUID `json:"id" db:"id"`
	// Netlify UUID
	UUID uuid.UUID `json:"uuid,omitempty" db:"uuid"`

	BaseConfig *conf.Configuration `json:"config" db:"raw_base_config"`

	CreatedAt time.Time `json:"created_at" db:"created_at"`
	UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}

func (i *Instance) TableName() string {
	tableName := "instances"

	if namespace.GetNamespace() != "" {
		return namespace.GetNamespace() + "_" + tableName
	}

	return tableName
}

// Config loads the the base configuration values with defaults.
func (i *Instance) Config() (*conf.Configuration, error) {
	if i.BaseConfig == nil {
		return nil, errors.New("no configuration data available")
	}

	baseConf := &conf.Configuration{}
	*baseConf = *i.BaseConfig
	baseConf.ApplyDefaults()

	return baseConf, nil
}

// UpdateConfig updates the base config
func (i *Instance) UpdateConfig(tx *storage.Connection, config *conf.Configuration) error {
	i.BaseConfig = config
	return tx.UpdateOnly(i, "raw_base_config")
}

// GetInstance finds an instance by ID
func GetInstance(tx *storage.Connection, instanceID uuid.UUID) (*Instance, error) {
	instance := Instance{}
	if err := tx.Find(&instance, instanceID); err != nil {
		if errors.Cause(err) == sql.ErrNoRows {
			return nil, InstanceNotFoundError{}
		}
		return nil, errors.Wrap(err, "error finding instance")
	}
	return &instance, nil
}

func GetInstanceByUUID(tx *storage.Connection, uuid uuid.UUID) (*Instance, error) {
	instance := Instance{}
	if err := tx.Where("uuid = ?", uuid).First(&instance); err != nil {
		if errors.Cause(err) == sql.ErrNoRows {
			return nil, InstanceNotFoundError{}
		}
		return nil, errors.Wrap(err, "error finding instance")
	}
	return &instance, nil
}

func DeleteInstance(conn *storage.Connection, instance *Instance) error {
	return conn.Transaction(func(tx *storage.Connection) error {
		delModels := map[string]*pop.Model{
			"user":          &pop.Model{Value: &User{}},
			"refresh token": &pop.Model{Value: &RefreshToken{}},
		}

		for name, dm := range delModels {
			if err := tx.RawQuery("DELETE FROM "+dm.TableName()+" WHERE instance_id = ?", instance.ID).Exec(); err != nil {
				return errors.Wrapf(err, "Error deleting %s records", name)
			}
		}

		return errors.Wrap(tx.Destroy(instance), "Error deleting instance record")
	})
}
<|endoftext|>


<commit_before>package conn

import (
	"errors"
	"fmt"
	"net/textproto"
	"regexp"
	"strings"

	"github.com/jordwest/imap-server/mailstore"
	"github.com/jordwest/imap-server/types"
	"github.com/jordwest/imap-server/util"
)

const (
	fetchArgUID    int = 0
	fetchArgRange  int = 1
	fetchArgParams int = 2
)

var registeredFetchParams []fetchParamDefinition
var peekRE *regexp.Regexp

// ErrUnrecognisedParameter indicates that the parameter requested in a FETCH
// command is unrecognised or not implemented in this IMAP server
var ErrUnrecognisedParameter = errors.New("Unrecognised Parameter")

type fetchParamDefinition struct {
	re      *regexp.Regexp
	handler func([]string, *Conn, mailstore.Message, bool) string
}

func cmdFetch(args commandArgs, c *Conn) {
	// Fetch the messages

	seqSet, err := types.InterpretSequenceSet(args.Arg(fetchArgRange))
	if err != nil {
		c.writeResponse(args.ID(), "NO "+err.Error())
		return
	}

	searchByUID := strings.ToUpper(args.Arg(fetchArgUID)) == "UID "

	var msgs []mailstore.Message
	if searchByUID {
		msgs = c.SelectedMailbox.MessageSetByUID(seqSet)
	} else {
		msgs = c.SelectedMailbox.MessageSetBySequenceNumber(seqSet)
	}

	fetchParamString := args.Arg(fetchArgParams)
	if searchByUID && !strings.Contains(fetchParamString, "UID") {
		fetchParamString += " UID"
	}

	for _, msg := range msgs {
		fetchParams, err := fetch(fetchParamString, c, msg)
		if err != nil {
			if err == ErrUnrecognisedParameter {
				c.writeResponse(args.ID(), "BAD Unrecognised Parameter")
				return
			}

			c.writeResponse(args.ID(), "BAD")
			return
		}

		if c.mailboxWritable == ReadWrite {
			msg = msg.RemoveFlags(types.FlagRecent)
			msg, err = msg.Save()
			if err != nil {
				// TODO: this error is not fatal, but should still be logged
			}
		}

		fullReply := fmt.Sprintf("%d FETCH (%s)",
			msg.SequenceNumber(),
			fetchParams)

		c.writeResponse("", fullReply)
	}

	if searchByUID {
		c.writeResponse(args.ID(), "OK UID FETCH Completed")
	} else {
		c.writeResponse(args.ID(), "OK FETCH Completed")
	}
}

// Fetch requested params from a given message
// eg fetch("UID BODY[TEXT] RFC822.SIZE", c, message)
func fetch(params string, c *Conn, m mailstore.Message) (string, error) {
	paramList := util.SplitParams(params)

	// Prepare the list of responses
	responseParams := make([]string, 0, len(paramList))

	for _, param := range paramList {
		paramResponse, err := fetchParam(param, c, m)
		if err != nil {
			return "", err
		}
		responseParams = append(responseParams, paramResponse)
	}
	return strings.Join(responseParams, " "), nil
}

// Match a single fetch parameter and return the data
func fetchParam(param string, c *Conn, m mailstore.Message) (string, error) {
	peek := false
	if peekRE.MatchString(param) {
		peek = true
	}
	// Search through the parameter list until a parameter handler is found
	for _, element := range registeredFetchParams {
		if element.re.MatchString(param) {
			return element.handler(element.re.FindStringSubmatch(param), c, m, peek), nil
		}
	}
	return "", ErrUnrecognisedParameter
}

// Register all supported fetch parameters
func init() {
	peekRE = regexp.MustCompile("\\.PEEK")
	registeredFetchParams = make([]fetchParamDefinition, 0)
	registerFetchParam("UID", fetchUID)
	registerFetchParam("FLAGS", fetchFlags)
	registerFetchParam("RFC822\\.SIZE", fetchRfcSize)
	registerFetchParam("INTERNALDATE", fetchInternalDate)
	registerFetchParam("BODY(?:\\.PEEK)?\\[HEADER\\]", fetchHeaders)
	registerFetchParam("BODY(?:\\.PEEK)?"+
		"\\[HEADER\\.FIELDS \\(([A-z\\s-]+)\\)\\]", fetchHeaderSpecificFields)
	registerFetchParam("BODY(?:\\.PEEK)?\\[TEXT\\]", fetchBody)
	registerFetchParam("BODY(?:\\.PEEK)?\\[\\]", fetchFullText)
}

func registerFetchParam(regex string, handler func([]string, *Conn, mailstore.Message, bool) string) {
	newParam := fetchParamDefinition{
		re:      regexp.MustCompile(regex),
		handler: handler,
	}
	registeredFetchParams = append(registeredFetchParams, newParam)
}

// Fetch the UID of the mail message
func fetchUID(args []string, c *Conn, m mailstore.Message, peekOnly bool) string {
	return fmt.Sprintf("UID %d", m.UID())
}

func fetchFlags(args []string, c *Conn, m mailstore.Message, peekOnly bool) string {
	flags := append(m.Flags().Strings(), m.Keywords()...)
	flagList := strings.Join(flags, " ")
	return fmt.Sprintf("FLAGS (%s)", flagList)
}

func fetchRfcSize(args []string, c *Conn, m mailstore.Message, peekOnly bool) string {
	return fmt.Sprintf("RFC822.SIZE %d", m.Size())
}

func fetchInternalDate(args []string, c *Conn, m mailstore.Message, peekOnly bool) string {
	dateStr := m.InternalDate().Format(util.InternalDate)
	return fmt.Sprintf("INTERNALDATE \"%s\"", dateStr)
}

func fetchHeaders(args []string, c *Conn, m mailstore.Message, peekOnly bool) string {
	hdr := fmt.Sprintf("%s\r\n", util.MIMEHeaderToString(m.Header()))
	hdrLen := len(hdr)

	peekStr := ""
	if peekOnly {
		peekStr = ".PEEK"
	}

	return fmt.Sprintf("BODY%s[HEADER] {%d}\r\n%s", peekStr, hdrLen, hdr)
}

func fetchHeaderSpecificFields(args []string, c *Conn, m mailstore.Message, peekOnly bool) string {
	if !peekOnly {
		fmt.Printf("TODO: Peek not requested, mark all as non-recent\n")
	}
	fields := strings.Split(args[1], " ")
	hdrs := m.Header()
	requestedHeaders := make(textproto.MIMEHeader)
	replyFieldList := make([]string, len(fields))
	for i, key := range fields {
		replyFieldList[i] = "\"" + key + "\""
		// If the key exists in the headers, copy it over
		v := hdrs.Get(key)
		if v != "" {
			requestedHeaders.Add(key, v)
		}
	}
	hdr := util.MIMEHeaderToString(requestedHeaders)
	hdrLen := len(hdr)

	return fmt.Sprintf("BODY[HEADER.FIELDS (%s)] {%d}\r\n%s",
		strings.Join(replyFieldList, " "),
		hdrLen,
		hdr)

}

func fetchBody(args []string, c *Conn, m mailstore.Message, peekOnly bool) string {
	body := fmt.Sprintf("%s\r\n", m.Body())
	bodyLen := len(body)

	return fmt.Sprintf("BODY[TEXT] {%d}\r\n%s",
		bodyLen, body)
}

func fetchFullText(args []string, c *Conn, m mailstore.Message, peekOnly bool) string {
	mail := fmt.Sprintf("%s\r\n%s\r\n", util.MIMEHeaderToString(m.Header()), m.Body())
	mailLen := len(mail)

	return fmt.Sprintf("BODY[] {%d}\r\n%s",
		mailLen, mail)
}
<commit_msg>`FETCH` gives error response if mailbox is not selected<commit_after>package conn

import (
	"errors"
	"fmt"
	"net/textproto"
	"regexp"
	"strings"

	"github.com/jordwest/imap-server/mailstore"
	"github.com/jordwest/imap-server/types"
	"github.com/jordwest/imap-server/util"
)

const (
	fetchArgUID    int = 0
	fetchArgRange  int = 1
	fetchArgParams int = 2
)

var registeredFetchParams []fetchParamDefinition
var peekRE *regexp.Regexp

// ErrUnrecognisedParameter indicates that the parameter requested in a FETCH
// command is unrecognised or not implemented in this IMAP server
var ErrUnrecognisedParameter = errors.New("Unrecognised Parameter")

type fetchParamDefinition struct {
	re      *regexp.Regexp
	handler func([]string, *Conn, mailstore.Message, bool) string
}

func cmdFetch(args commandArgs, c *Conn) {
	if !c.assertSelected(args.ID(), ReadOnly) {
		return
	}

	// Fetch the messages
	seqSet, err := types.InterpretSequenceSet(args.Arg(fetchArgRange))
	if err != nil {
		c.writeResponse(args.ID(), "NO "+err.Error())
		return
	}

	searchByUID := strings.ToUpper(args.Arg(fetchArgUID)) == "UID "

	var msgs []mailstore.Message
	if searchByUID {
		msgs = c.SelectedMailbox.MessageSetByUID(seqSet)
	} else {
		msgs = c.SelectedMailbox.MessageSetBySequenceNumber(seqSet)
	}

	fetchParamString := args.Arg(fetchArgParams)
	if searchByUID && !strings.Contains(fetchParamString, "UID") {
		fetchParamString += " UID"
	}

	for _, msg := range msgs {
		fetchParams, err := fetch(fetchParamString, c, msg)
		if err != nil {
			if err == ErrUnrecognisedParameter {
				c.writeResponse(args.ID(), "BAD Unrecognised Parameter")
				return
			}

			c.writeResponse(args.ID(), "BAD")
			return
		}

		if c.mailboxWritable == ReadWrite {
			msg = msg.RemoveFlags(types.FlagRecent)
			msg, err = msg.Save()
			if err != nil {
				// TODO: this error is not fatal, but should still be logged
			}
		}

		fullReply := fmt.Sprintf("%d FETCH (%s)",
			msg.SequenceNumber(),
			fetchParams)

		c.writeResponse("", fullReply)
	}

	if searchByUID {
		c.writeResponse(args.ID(), "OK UID FETCH Completed")
	} else {
		c.writeResponse(args.ID(), "OK FETCH Completed")
	}
}

// Fetch requested params from a given message
// eg fetch("UID BODY[TEXT] RFC822.SIZE", c, message)
func fetch(params string, c *Conn, m mailstore.Message) (string, error) {
	paramList := util.SplitParams(params)

	// Prepare the list of responses
	responseParams := make([]string, 0, len(paramList))

	for _, param := range paramList {
		paramResponse, err := fetchParam(param, c, m)
		if err != nil {
			return "", err
		}
		responseParams = append(responseParams, paramResponse)
	}
	return strings.Join(responseParams, " "), nil
}

// Match a single fetch parameter and return the data
func fetchParam(param string, c *Conn, m mailstore.Message) (string, error) {
	peek := false
	if peekRE.MatchString(param) {
		peek = true
	}
	// Search through the parameter list until a parameter handler is found
	for _, element := range registeredFetchParams {
		if element.re.MatchString(param) {
			return element.handler(element.re.FindStringSubmatch(param), c, m, peek), nil
		}
	}
	return "", ErrUnrecognisedParameter
}

// Register all supported fetch parameters
func init() {
	peekRE = regexp.MustCompile("\\.PEEK")
	registeredFetchParams = make([]fetchParamDefinition, 0)
	registerFetchParam("UID", fetchUID)
	registerFetchParam("FLAGS", fetchFlags)
	registerFetchParam("RFC822\\.SIZE", fetchRfcSize)
	registerFetchParam("INTERNALDATE", fetchInternalDate)
	registerFetchParam("BODY(?:\\.PEEK)?\\[HEADER\\]", fetchHeaders)
	registerFetchParam("BODY(?:\\.PEEK)?"+
		"\\[HEADER\\.FIELDS \\(([A-z\\s-]+)\\)\\]", fetchHeaderSpecificFields)
	registerFetchParam("BODY(?:\\.PEEK)?\\[TEXT\\]", fetchBody)
	registerFetchParam("BODY(?:\\.PEEK)?\\[\\]", fetchFullText)
}

func registerFetchParam(regex string, handler func([]string, *Conn, mailstore.Message, bool) string) {
	newParam := fetchParamDefinition{
		re:      regexp.MustCompile(regex),
		handler: handler,
	}
	registeredFetchParams = append(registeredFetchParams, newParam)
}

// Fetch the UID of the mail message
func fetchUID(args []string, c *Conn, m mailstore.Message, peekOnly bool) string {
	return fmt.Sprintf("UID %d", m.UID())
}

func fetchFlags(args []string, c *Conn, m mailstore.Message, peekOnly bool) string {
	flags := append(m.Flags().Strings(), m.Keywords()...)
	flagList := strings.Join(flags, " ")
	return fmt.Sprintf("FLAGS (%s)", flagList)
}

func fetchRfcSize(args []string, c *Conn, m mailstore.Message, peekOnly bool) string {
	return fmt.Sprintf("RFC822.SIZE %d", m.Size())
}

func fetchInternalDate(args []string, c *Conn, m mailstore.Message, peekOnly bool) string {
	dateStr := m.InternalDate().Format(util.InternalDate)
	return fmt.Sprintf("INTERNALDATE \"%s\"", dateStr)
}

func fetchHeaders(args []string, c *Conn, m mailstore.Message, peekOnly bool) string {
	hdr := fmt.Sprintf("%s\r\n", util.MIMEHeaderToString(m.Header()))
	hdrLen := len(hdr)

	peekStr := ""
	if peekOnly {
		peekStr = ".PEEK"
	}

	return fmt.Sprintf("BODY%s[HEADER] {%d}\r\n%s", peekStr, hdrLen, hdr)
}

func fetchHeaderSpecificFields(args []string, c *Conn, m mailstore.Message, peekOnly bool) string {
	if !peekOnly {
		fmt.Printf("TODO: Peek not requested, mark all as non-recent\n")
	}
	fields := strings.Split(args[1], " ")
	hdrs := m.Header()
	requestedHeaders := make(textproto.MIMEHeader)
	replyFieldList := make([]string, len(fields))
	for i, key := range fields {
		replyFieldList[i] = "\"" + key + "\""
		// If the key exists in the headers, copy it over
		v := hdrs.Get(key)
		if v != "" {
			requestedHeaders.Add(key, v)
		}
	}
	hdr := util.MIMEHeaderToString(requestedHeaders)
	hdrLen := len(hdr)

	return fmt.Sprintf("BODY[HEADER.FIELDS (%s)] {%d}\r\n%s",
		strings.Join(replyFieldList, " "),
		hdrLen,
		hdr)

}

func fetchBody(args []string, c *Conn, m mailstore.Message, peekOnly bool) string {
	body := fmt.Sprintf("%s\r\n", m.Body())
	bodyLen := len(body)

	return fmt.Sprintf("BODY[TEXT] {%d}\r\n%s",
		bodyLen, body)
}

func fetchFullText(args []string, c *Conn, m mailstore.Message, peekOnly bool) string {
	mail := fmt.Sprintf("%s\r\n%s\r\n", util.MIMEHeaderToString(m.Header()), m.Body())
	mailLen := len(mail)

	return fmt.Sprintf("BODY[] {%d}\r\n%s",
		mailLen, mail)
}
<|endoftext|>


<commit_before>package consensus

import (
	"math"
	"math/big"
)

// A Currency represents a number of siacoins or siafunds. Internally, a
// Currency value is unbounded; however, Currency values sent over the wire
// protocol are subject to a maximum size of 255 bytes (approximately 10^614).
// Unlike the math/big library, whose methods modify their receiver, all
// arithmetic Currency methods return a new value. This is necessary to
// preserve the immutability of types containing Currency fields.
type Currency struct {
	i big.Int
}

func NewCurrency(b *big.Int) (c Currency) {
	c.i = *b
	return
}

func NewCurrency64(x uint64) (c Currency) {
	c.i.SetUint64(x)
	return
}

func (c Currency) Big() *big.Int {
	return new(big.Int).Set(&c.i)
}

func (c Currency) Cmp(y Currency) int {
	return c.i.Cmp(&y.i)
}

func (c Currency) IsZero() bool {
	return c.i.Sign() == 0
}

func (c Currency) Add(x Currency) (y Currency) {
	y.i.Add(&c.i, &x.i)
	return
}

func (c Currency) Sub(x Currency) (y Currency) {
	y.i.Sub(&c.i, &x.i)
	return
}

func (c Currency) Mul(x Currency) (y Currency) {
	y.i.Mul(&c.i, &x.i)
	return
}

func (c Currency) Div(x Currency) (y Currency) {
	y.i.Div(&c.i, &x.i)
	return
}

func (c Currency) MulFloat(x float64) (y Currency) {
	yRat := new(big.Rat).Mul(
		new(big.Rat).SetInt(&c.i),
		new(big.Rat).SetFloat64(x),
	)
	y.i.Div(yRat.Num(), yRat.Denom())
	return
}

func (c Currency) Sqrt() (y Currency) {
	f, _ := new(big.Rat).SetInt(&c.i).Float64()
	sqrt := new(big.Rat).SetFloat64(math.Sqrt(f))
	y.i.Div(sqrt.Num(), sqrt.Denom())
	return
}

// RoundDown returns the largest multiple of n <= c.
func (c Currency) RoundDown(n uint64) (y Currency) {
	diff := new(big.Int).Mod(&c.i, new(big.Int).SetUint64(n))
	y.i.Sub(&c.i, diff)
	return
}

// MarshalSia implements the encoding.SiaMarshaler interface. It returns the
// byte-slice representation of the Currency's internal big.Int, prepended
// with a single byte indicating the length of the slice. This implies a
// maximum encodable value of 2^(255 * 8), or approximately 10^614.
//
// Note that as the bytes of the big.Int correspond to the absolute value of
// the integer, there is no way to marshal a negative Currency.
func (c Currency) MarshalSia() []byte {
	b := c.i.Bytes()
	return append(
		[]byte{byte(len(b))},
		b...,
	)
}

// UnmarshalSia implements the encoding.SiaUnmarshaler interface. See
// MarshalSia for a description of how Currency values are marshalled.
func (c *Currency) UnmarshalSia(b []byte) int {
	var n int
	n, b = int(b[0]), b[1:]
	c.i.SetBytes(b[:n])
	return 1 + n
}

func (c Currency) MarshalJSON() ([]byte, error) {
	return c.i.MarshalJSON()
}

func (c *Currency) UnmarshalJSON(b []byte) error {
	return c.i.UnmarshalJSON(b)
}
<commit_msg>add docstrings to Currency methods<commit_after>package consensus

import (
	"math"
	"math/big"
)

// A Currency represents a number of siacoins or siafunds. Internally, a
// Currency value is unbounded; however, Currency values sent over the wire
// protocol are subject to a maximum size of 255 bytes (approximately 10^614).
// Unlike the math/big library, whose methods modify their receiver, all
// arithmetic Currency methods return a new value. This is necessary to
// preserve the immutability of types containing Currency fields.
type Currency struct {
	i big.Int
}

// NewCurrency creates a Currency value from a big.Int.
func NewCurrency(b *big.Int) (c Currency) {
	c.i = *b
	return
}

// NewCurrency64 creates a Currency value from a uint64.
func NewCurrency64(x uint64) (c Currency) {
	c.i.SetUint64(x)
	return
}

// Big returns the value of c as a *big.Int. Importantly, it does not provide
// access to the c's internal big.Int object, only a copy. This is in
// accordance with the immutability constraint described above.
func (c Currency) Big() *big.Int {
	return new(big.Int).Set(&c.i)
}

// Cmp compares two Currency values. The return value follows the convention
// of the math/big package.
func (c Currency) Cmp(y Currency) int {
	return c.i.Cmp(&y.i)
}

// IsZero returns true if c's value is 0.
func (c Currency) IsZero() bool {
	return c.i.Sign() == 0
}

// Add returns a new Currency value y = c + x.
func (c Currency) Add(x Currency) (y Currency) {
	y.i.Add(&c.i, &x.i)
	return
}

// Sub returns a new Currency value y = c - x.
func (c Currency) Sub(x Currency) (y Currency) {
	y.i.Sub(&c.i, &x.i)
	return
}

// Mult returns a new Currency value y = c * x.
func (c Currency) Mul(x Currency) (y Currency) {
	y.i.Mul(&c.i, &x.i)
	return
}

// Div returns a new Currency value y = c / x.
func (c Currency) Div(x Currency) (y Currency) {
	y.i.Div(&c.i, &x.i)
	return
}

// MulFloat returns a new Currency value y = c * x, where x is a float64.
func (c Currency) MulFloat(x float64) (y Currency) {
	yRat := new(big.Rat).Mul(
		new(big.Rat).SetInt(&c.i),
		new(big.Rat).SetFloat64(x),
	)
	y.i.Div(yRat.Num(), yRat.Denom())
	return
}

// Sqrt returns a new Currency value y = sqrt(c)
func (c Currency) Sqrt() (y Currency) {
	f, _ := new(big.Rat).SetInt(&c.i).Float64()
	sqrt := new(big.Rat).SetFloat64(math.Sqrt(f))
	y.i.Div(sqrt.Num(), sqrt.Denom())
	return
}

// RoundDown returns the largest multiple of n <= c.
func (c Currency) RoundDown(n uint64) (y Currency) {
	diff := new(big.Int).Mod(&c.i, new(big.Int).SetUint64(n))
	y.i.Sub(&c.i, diff)
	return
}

// MarshalSia implements the encoding.SiaMarshaler interface. It returns the
// byte-slice representation of the Currency's internal big.Int, prepended
// with a single byte indicating the length of the slice. This implies a
// maximum encodable value of 2^(255 * 8), or approximately 10^614.
//
// Note that as the bytes of the big.Int correspond to the absolute value of
// the integer, there is no way to marshal a negative Currency.
func (c Currency) MarshalSia() []byte {
	b := c.i.Bytes()
	return append(
		[]byte{byte(len(b))},
		b...,
	)
}

// UnmarshalSia implements the encoding.SiaUnmarshaler interface. See
// MarshalSia for a description of how Currency values are marshalled.
func (c *Currency) UnmarshalSia(b []byte) int {
	var n int
	n, b = int(b[0]), b[1:]
	c.i.SetBytes(b[:n])
	return 1 + n
}

// MarshalJSON implements the json.Marshaler interface.
func (c Currency) MarshalJSON() ([]byte, error) {
	return c.i.MarshalJSON()
}

// UnmarshalJSON implements the json.Unmarshaler interface.
func (c *Currency) UnmarshalJSON(b []byte) error {
	return c.i.UnmarshalJSON(b)
}
<|endoftext|>


<commit_before>package debugstream

import (
	"bytes"
	"context"
	"testing"
	"time"

	"github.com/oakmound/oak/v3/alg/intgeom"
	"github.com/oakmound/oak/v3/event"
	"github.com/oakmound/oak/v3/render"
	"github.com/oakmound/oak/v3/window"
)

type fakeWindow struct {
	window.Window

	skipCalls       int
	quitCalls       int
	fullscreenCalls int

	moveWindow func(x, y, w, h int)
}

func (f *fakeWindow) NextScene() {
	f.skipCalls++
}

func (f *fakeWindow) Quit() {
	f.quitCalls++
}

func (f *fakeWindow) EventHandler() event.Handler {
	return event.NewBus()
}

func (f *fakeWindow) SetFullScreen(bool) error {
	f.fullscreenCalls++
	return nil
}

func (f *fakeWindow) MoveWindow(x, y, w, h int) error {
	f.moveWindow(x, y, w, h)
	return nil
}

func (f *fakeWindow) Width() int {
	return 1
}

func (f *fakeWindow) Height() int {
	return 1
}

func (f *fakeWindow) Viewport() intgeom.Point2 {
	return intgeom.Point2{}
}

func TestSkipCommands(t *testing.T) {
	sc := NewScopedCommands()
	fw := &fakeWindow{}
	sc.AddDefaultsForScope(1, fw)

	in := bytes.NewBufferString("skip-scene\n1 skip-scene\nscope 1\nskip-scene")
	out := new(bytes.Buffer)

	sc.AttachToStream(context.Background(), in, out)

	time.Sleep(50 * time.Millisecond)

	expected := `assumed scope 1
`

	got := out.String()
	if got != expected {
		t.Fatal("got:\n" + got + "\nexpected:\n" + expected)
	}
	if fw.skipCalls != 3 {
		t.Fatal("expected 3 skips, got:", fw.skipCalls)
	}
}

func TestQuitCommands(t *testing.T) {
	sc := NewScopedCommands()
	fw := &fakeWindow{}
	sc.AddDefaultsForScope(1, fw)

	in := bytes.NewBufferString("quit\n")
	out := new(bytes.Buffer)

	sc.AttachToStream(context.Background(), in, out)

	time.Sleep(50 * time.Millisecond)

	expected := ``

	got := out.String()
	if got != expected {
		t.Fatal("got:\n" + got + "\nexpected:\n" + expected)
	}
	if fw.quitCalls != 1 {
		t.Fatal("expected 1 quit, got:", fw.quitCalls)
	}
}

func TestFadeCommands(t *testing.T) {
	sc := NewScopedCommands()
	fw := &fakeWindow{}
	sc.AddDefaultsForScope(1, fw)

	render.UpdateDebugMap("test-r", render.EmptyRenderable())

	in := bytes.NewBufferString("fade test-r\nfade test-r 200\nfade\nfade bad-r")
	out := new(bytes.Buffer)

	sc.AttachToStream(context.Background(), in, out)

	time.Sleep(50 * time.Millisecond)

	expected := "Must supply at least 1 arguments\nCould not fade input bad-r\nPossible inputs are 'test-r'\n"

	got := out.String()
	if got != expected {
		t.Fatal("got:\n" + got + "\nexpected:\n" + expected)
	}
}

func TestMouseCommands(t *testing.T) {
	sc := NewScopedCommands()
	fw := &fakeWindow{}
	sc.AddDefaultsForScope(1, fw)

	in := bytes.NewBufferString("mouse-details\n")
	out := new(bytes.Buffer)

	sc.AttachToStream(context.Background(), in, out)

	time.Sleep(50 * time.Millisecond)

	expected := ``

	got := out.String()
	if got != expected {
		t.Fatal("got:\n" + got + "\nexpected:\n" + expected)
	}
}

func TestFullScreen(t *testing.T) {
	sc := NewScopedCommands()
	fw := &fakeWindow{}
	sc.AddDefaultsForScope(1, fw)

	in := bytes.NewBufferString("fullscreen\nfullscreen off")
	out := new(bytes.Buffer)

	sc.AttachToStream(context.Background(), in, out)

	time.Sleep(50 * time.Millisecond)

	expected := ``

	got := out.String()
	if got != expected {
		t.Fatal("got:\n" + got + "\nexpected:\n" + expected)
	}
	if fw.fullscreenCalls != 2 {
		t.Fatal("expected 2 fullscreens, got:", fw.fullscreenCalls)
	}
}

func TestMoveWindow(t *testing.T) {
	sc := NewScopedCommands()
	fw := &fakeWindow{}
	sc.AddDefaultsForScope(1, fw)

	fw.moveWindow = func(x, y, w, h int) {
		if x != 1 {
			t.Fatal("x was not 1:", x)
		}
		if y != 2 {
			t.Fatal("y was not 2:", y)
		}
		if w != 3 {
			t.Fatal("w was not 3:", w)
		}
		if h != 4 {
			t.Fatal("h was not 4:", h)
		}
	}

	in := bytes.NewBufferString("move 1 2 3 4\nmove")
	out := new(bytes.Buffer)

	sc.AttachToStream(context.Background(), in, out)

	time.Sleep(50 * time.Millisecond)

	expected := `Must supply at least 2 coordinates`

	got := out.String()
	if got != expected {
		t.Fatal("got:\n" + got + "\nexpected:\n" + expected)
	}
}
<commit_msg>debugstream: extend delay in tests<commit_after>package debugstream

import (
	"bytes"
	"context"
	"testing"
	"time"

	"github.com/oakmound/oak/v3/alg/intgeom"
	"github.com/oakmound/oak/v3/event"
	"github.com/oakmound/oak/v3/render"
	"github.com/oakmound/oak/v3/window"
)

type fakeWindow struct {
	window.Window

	skipCalls       int
	quitCalls       int
	fullscreenCalls int

	moveWindow func(x, y, w, h int)
}

func (f *fakeWindow) NextScene() {
	f.skipCalls++
}

func (f *fakeWindow) Quit() {
	f.quitCalls++
}

func (f *fakeWindow) EventHandler() event.Handler {
	return event.NewBus()
}

func (f *fakeWindow) SetFullScreen(bool) error {
	f.fullscreenCalls++
	return nil
}

func (f *fakeWindow) MoveWindow(x, y, w, h int) error {
	f.moveWindow(x, y, w, h)
	return nil
}

func (f *fakeWindow) Width() int {
	return 1
}

func (f *fakeWindow) Height() int {
	return 1
}

func (f *fakeWindow) Viewport() intgeom.Point2 {
	return intgeom.Point2{}
}

func TestSkipCommands(t *testing.T) {
	sc := NewScopedCommands()
	fw := &fakeWindow{}
	sc.AddDefaultsForScope(1, fw)

	in := bytes.NewBufferString("skip-scene\n1 skip-scene\nscope 1\nskip-scene")
	out := new(bytes.Buffer)

	sc.AttachToStream(context.Background(), in, out)

	time.Sleep(100 * time.Millisecond)

	expected := `assumed scope 1
`

	got := out.String()
	if got != expected {
		t.Fatal("got:\n" + got + "\nexpected:\n" + expected)
	}
	if fw.skipCalls != 3 {
		t.Fatal("expected 3 skips, got:", fw.skipCalls)
	}
}

func TestQuitCommands(t *testing.T) {
	sc := NewScopedCommands()
	fw := &fakeWindow{}
	sc.AddDefaultsForScope(1, fw)

	in := bytes.NewBufferString("quit\n")
	out := new(bytes.Buffer)

	sc.AttachToStream(context.Background(), in, out)

	time.Sleep(100 * time.Millisecond)

	expected := ``

	got := out.String()
	if got != expected {
		t.Fatal("got:\n" + got + "\nexpected:\n" + expected)
	}
	if fw.quitCalls != 1 {
		t.Fatal("expected 1 quit, got:", fw.quitCalls)
	}
}

func TestFadeCommands(t *testing.T) {
	sc := NewScopedCommands()
	fw := &fakeWindow{}
	sc.AddDefaultsForScope(1, fw)

	render.UpdateDebugMap("test-r", render.EmptyRenderable())

	in := bytes.NewBufferString("fade test-r\nfade test-r 200\nfade\nfade bad-r")
	out := new(bytes.Buffer)

	sc.AttachToStream(context.Background(), in, out)

	time.Sleep(100 * time.Millisecond)

	expected := "Must supply at least 1 arguments\nCould not fade input bad-r\nPossible inputs are 'test-r'\n"

	got := out.String()
	if got != expected {
		t.Fatal("got:\n" + got + "\nexpected:\n" + expected)
	}
}

func TestMouseCommands(t *testing.T) {
	sc := NewScopedCommands()
	fw := &fakeWindow{}
	sc.AddDefaultsForScope(1, fw)

	in := bytes.NewBufferString("mouse-details\n")
	out := new(bytes.Buffer)

	sc.AttachToStream(context.Background(), in, out)

	time.Sleep(100 * time.Millisecond)

	expected := ``

	got := out.String()
	if got != expected {
		t.Fatal("got:\n" + got + "\nexpected:\n" + expected)
	}
}

func TestFullScreen(t *testing.T) {
	sc := NewScopedCommands()
	fw := &fakeWindow{}
	sc.AddDefaultsForScope(1, fw)

	in := bytes.NewBufferString("fullscreen\nfullscreen off")
	out := new(bytes.Buffer)

	sc.AttachToStream(context.Background(), in, out)

	time.Sleep(100 * time.Millisecond)

	expected := ``

	got := out.String()
	if got != expected {
		t.Fatal("got:\n" + got + "\nexpected:\n" + expected)
	}
	if fw.fullscreenCalls != 2 {
		t.Fatal("expected 2 fullscreens, got:", fw.fullscreenCalls)
	}
}

func TestMoveWindow(t *testing.T) {
	sc := NewScopedCommands()
	fw := &fakeWindow{}
	sc.AddDefaultsForScope(1, fw)

	fw.moveWindow = func(x, y, w, h int) {
		if x != 1 {
			t.Fatal("x was not 1:", x)
		}
		if y != 2 {
			t.Fatal("y was not 2:", y)
		}
		if w != 3 {
			t.Fatal("w was not 3:", w)
		}
		if h != 4 {
			t.Fatal("h was not 4:", h)
		}
	}

	in := bytes.NewBufferString("move 1 2 3 4\nmove")
	out := new(bytes.Buffer)

	sc.AttachToStream(context.Background(), in, out)

	time.Sleep(100 * time.Millisecond)

	expected := `Must supply at least 2 coordinates`

	got := out.String()
	if got != expected {
		t.Fatal("got:\n" + got + "\nexpected:\n" + expected)
	}
}
<|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 (
	"errors"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"unicode"

	"contrib.go.opencensus.io/exporter/prometheus"
	"github.com/google/uuid"
	"go.opencensus.io/stats/view"
	otelprometheus "go.opentelemetry.io/otel/exporters/prometheus"
	"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
	controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
	processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
	selector "go.opentelemetry.io/otel/sdk/metric/selector/simple"
	"go.uber.org/zap"

	"go.opentelemetry.io/collector/config/configtelemetry"
	"go.opentelemetry.io/collector/internal/obsreportconfig"
	"go.opentelemetry.io/collector/processor/batchprocessor"
	semconv "go.opentelemetry.io/collector/semconv/v1.5.0"
	"go.opentelemetry.io/collector/service/featuregate"
	"go.opentelemetry.io/collector/service/internal/telemetry"
)

// collectorTelemetry is collector's own telemetry.
var collectorTelemetry collectorTelemetryExporter = newColTelemetry(featuregate.GetRegistry())

// AddCollectorVersionTag indicates if the collector version tag should be added to all telemetry metrics
const AddCollectorVersionTag = true

const (
	zapKeyTelemetryAddress = "address"
	zapKeyTelemetryLevel   = "level"

	// useOtelForInternalMetricsfeatureGateID is the feature gate ID that controls whether the collector uses open
	// telemetry for internal metrics.
	useOtelForInternalMetricsfeatureGateID = "telemetry.useOtelForInternalMetrics"
)

type collectorTelemetryExporter interface {
	init(svc *service) error
	shutdown() error
}

type colTelemetry struct {
	registry   *featuregate.Registry
	views      []*view.View
	server     *http.Server
	doInitOnce sync.Once
}

func newColTelemetry(registry *featuregate.Registry) *colTelemetry {
	registry.MustRegister(featuregate.Gate{
		ID:          useOtelForInternalMetricsfeatureGateID,
		Description: "controls whether the collector to uses open telemetry for internal metrics",
		Enabled:     false,
	})
	return &colTelemetry{registry: registry}
}

func (tel *colTelemetry) init(svc *service) error {
	var err error
	tel.doInitOnce.Do(
		func() {
			err = tel.initOnce(svc)
		},
	)
	if err != nil {
		return fmt.Errorf("failed to initialize telemetry: %w", err)
	}
	return nil
}

func (tel *colTelemetry) initOnce(svc *service) error {
	telemetryConf := svc.config.Telemetry

	if telemetryConf.Metrics.Level == configtelemetry.LevelNone || telemetryConf.Metrics.Address == "" {
		svc.telemetry.Logger.Info(
			"Skipping telemetry setup.",
			zap.String(zapKeyTelemetryAddress, telemetryConf.Metrics.Address),
			zap.String(zapKeyTelemetryLevel, telemetryConf.Metrics.Level.String()),
		)
		return nil
	}

	svc.telemetry.Logger.Info("Setting up own telemetry...")

	// Construct telemetry attributes from resource attributes.
	telAttrs := map[string]string{}
	for k, v := range telemetryConf.Resource {
		// nil value indicates that the attribute should not be included in the telemetry.
		if v != nil {
			telAttrs[k] = *v
		}
	}

	if _, ok := telemetryConf.Resource[semconv.AttributeServiceInstanceID]; !ok {
		// AttributeServiceInstanceID is not specified in the config. Auto-generate one.
		instanceUUID, _ := uuid.NewRandom()
		instanceID := instanceUUID.String()
		telAttrs[semconv.AttributeServiceInstanceID] = instanceID
	}

	if AddCollectorVersionTag {
		if _, ok := telemetryConf.Resource[semconv.AttributeServiceVersion]; !ok {
			// AttributeServiceVersion is not specified in the config. Use the actual
			// build version.
			telAttrs[semconv.AttributeServiceVersion] = col.set.BuildInfo.Version
		}
	}

	var pe http.Handler
	if tel.registry.IsEnabled(useOtelForInternalMetricsfeatureGateID) {
		otelHandler, err := tel.initOpenTelemetry(svc)
		if err != nil {
			return err
		}
		pe = otelHandler
	} else {
		ocHandler, err := tel.initOpenCensus(svc, telAttrs)
		if err != nil {
			return err
		}
		pe = ocHandler
	}

	svc.telemetry.Logger.Info(
		"Serving Prometheus metrics",
		zap.String(zapKeyTelemetryAddress, telemetryConf.Metrics.Address),
		zap.String(zapKeyTelemetryLevel, telemetryConf.Metrics.Level.String()),
	)

	mux := http.NewServeMux()
	mux.Handle("/metrics", pe)

	tel.server = &http.Server{
		Addr:    telemetryConf.Metrics.Address,
		Handler: mux,
	}

	go func() {
		if serveErr := tel.server.ListenAndServe(); serveErr != nil && !errors.Is(serveErr, http.ErrServerClosed) {
			svc.host.asyncErrorChannel <- serveErr
		}
	}()

	return nil
}

func (tel *colTelemetry) initOpenCensus(svc *service, telAttrs map[string]string) (http.Handler, error) {
	telemetryConf := svc.config.Telemetry
	processMetricsViews, err := telemetry.NewProcessMetricsViews(getBallastSize(svc.host))
	if err != nil {
		return nil, err
	}

	var views []*view.View
	obsMetrics := obsreportconfig.Configure(telemetryConf.Metrics.Level)
	views = append(views, batchprocessor.MetricViews()...)
	views = append(views, obsMetrics.Views...)
	views = append(views, processMetricsViews.Views()...)

	tel.views = views
	if err = view.Register(views...); err != nil {
		return nil, err
	}

	processMetricsViews.StartCollection()

	// Until we can use a generic metrics exporter, default to Prometheus.
	opts := prometheus.Options{
		Namespace: "otelcol",
	}

	opts.ConstLabels = make(map[string]string)

	for k, v := range telAttrs {
		opts.ConstLabels[sanitizePrometheusKey(k)] = v
	}

	pe, err := prometheus.NewExporter(opts)
	if err != nil {
		return nil, err
	}

	view.RegisterExporter(pe)
	return pe, nil
}

func (tel *colTelemetry) initOpenTelemetry(svc *service) (http.Handler, error) {
	config := otelprometheus.Config{}
	c := controller.New(
		processor.NewFactory(
			selector.NewWithHistogramDistribution(
				histogram.WithExplicitBoundaries(config.DefaultHistogramBoundaries),
			),
			aggregation.CumulativeTemporalitySelector(),
			processor.WithMemory(true),
		),
	)

	pe, err := otelprometheus.New(config, c)
	if err != nil {
		return nil, err
	}

	svc.telemetry.MeterProvider = pe.MeterProvider()
	return pe, err
}

func (tel *colTelemetry) shutdown() error {
	view.Unregister(tel.views...)

	if tel.server != nil {
		return tel.server.Close()
	}

	return nil
}

func sanitizePrometheusKey(str string) string {
	runeFilterMap := func(r rune) rune {
		if unicode.IsDigit(r) || unicode.IsLetter(r) || r == '_' {
			return r
		}
		return '_'
	}
	return strings.Map(runeFilterMap, str)
}
<commit_msg>Fix merge race condition between PRs (#5452)<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 (
	"errors"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"unicode"

	"contrib.go.opencensus.io/exporter/prometheus"
	"github.com/google/uuid"
	"go.opencensus.io/stats/view"
	otelprometheus "go.opentelemetry.io/otel/exporters/prometheus"
	"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
	controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
	"go.opentelemetry.io/otel/sdk/metric/export/aggregation"
	processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
	selector "go.opentelemetry.io/otel/sdk/metric/selector/simple"
	"go.uber.org/zap"

	"go.opentelemetry.io/collector/config/configtelemetry"
	"go.opentelemetry.io/collector/internal/obsreportconfig"
	"go.opentelemetry.io/collector/processor/batchprocessor"
	semconv "go.opentelemetry.io/collector/semconv/v1.5.0"
	"go.opentelemetry.io/collector/service/featuregate"
	"go.opentelemetry.io/collector/service/internal/telemetry"
)

// collectorTelemetry is collector's own telemetry.
var collectorTelemetry collectorTelemetryExporter = newColTelemetry(featuregate.GetRegistry())

// AddCollectorVersionTag indicates if the collector version tag should be added to all telemetry metrics
const AddCollectorVersionTag = true

const (
	zapKeyTelemetryAddress = "address"
	zapKeyTelemetryLevel   = "level"

	// useOtelForInternalMetricsfeatureGateID is the feature gate ID that controls whether the collector uses open
	// telemetry for internal metrics.
	useOtelForInternalMetricsfeatureGateID = "telemetry.useOtelForInternalMetrics"
)

type collectorTelemetryExporter interface {
	init(svc *service) error
	shutdown() error
}

type colTelemetry struct {
	registry   *featuregate.Registry
	views      []*view.View
	server     *http.Server
	doInitOnce sync.Once
}

func newColTelemetry(registry *featuregate.Registry) *colTelemetry {
	registry.MustRegister(featuregate.Gate{
		ID:          useOtelForInternalMetricsfeatureGateID,
		Description: "controls whether the collector to uses open telemetry for internal metrics",
		Enabled:     false,
	})
	return &colTelemetry{registry: registry}
}

func (tel *colTelemetry) init(svc *service) error {
	var err error
	tel.doInitOnce.Do(
		func() {
			err = tel.initOnce(svc)
		},
	)
	if err != nil {
		return fmt.Errorf("failed to initialize telemetry: %w", err)
	}
	return nil
}

func (tel *colTelemetry) initOnce(svc *service) error {
	telemetryConf := svc.config.Telemetry

	if telemetryConf.Metrics.Level == configtelemetry.LevelNone || telemetryConf.Metrics.Address == "" {
		svc.telemetry.Logger.Info(
			"Skipping telemetry setup.",
			zap.String(zapKeyTelemetryAddress, telemetryConf.Metrics.Address),
			zap.String(zapKeyTelemetryLevel, telemetryConf.Metrics.Level.String()),
		)
		return nil
	}

	svc.telemetry.Logger.Info("Setting up own telemetry...")

	// Construct telemetry attributes from resource attributes.
	telAttrs := map[string]string{}
	for k, v := range telemetryConf.Resource {
		// nil value indicates that the attribute should not be included in the telemetry.
		if v != nil {
			telAttrs[k] = *v
		}
	}

	if _, ok := telemetryConf.Resource[semconv.AttributeServiceInstanceID]; !ok {
		// AttributeServiceInstanceID is not specified in the config. Auto-generate one.
		instanceUUID, _ := uuid.NewRandom()
		instanceID := instanceUUID.String()
		telAttrs[semconv.AttributeServiceInstanceID] = instanceID
	}

	if AddCollectorVersionTag {
		if _, ok := telemetryConf.Resource[semconv.AttributeServiceVersion]; !ok {
			// AttributeServiceVersion is not specified in the config. Use the actual
			// build version.
			telAttrs[semconv.AttributeServiceVersion] = svc.buildInfo.Version
		}
	}

	var pe http.Handler
	if tel.registry.IsEnabled(useOtelForInternalMetricsfeatureGateID) {
		otelHandler, err := tel.initOpenTelemetry(svc)
		if err != nil {
			return err
		}
		pe = otelHandler
	} else {
		ocHandler, err := tel.initOpenCensus(svc, telAttrs)
		if err != nil {
			return err
		}
		pe = ocHandler
	}

	svc.telemetry.Logger.Info(
		"Serving Prometheus metrics",
		zap.String(zapKeyTelemetryAddress, telemetryConf.Metrics.Address),
		zap.String(zapKeyTelemetryLevel, telemetryConf.Metrics.Level.String()),
	)

	mux := http.NewServeMux()
	mux.Handle("/metrics", pe)

	tel.server = &http.Server{
		Addr:    telemetryConf.Metrics.Address,
		Handler: mux,
	}

	go func() {
		if serveErr := tel.server.ListenAndServe(); serveErr != nil && !errors.Is(serveErr, http.ErrServerClosed) {
			svc.host.asyncErrorChannel <- serveErr
		}
	}()

	return nil
}

func (tel *colTelemetry) initOpenCensus(svc *service, telAttrs map[string]string) (http.Handler, error) {
	telemetryConf := svc.config.Telemetry
	processMetricsViews, err := telemetry.NewProcessMetricsViews(getBallastSize(svc.host))
	if err != nil {
		return nil, err
	}

	var views []*view.View
	obsMetrics := obsreportconfig.Configure(telemetryConf.Metrics.Level)
	views = append(views, batchprocessor.MetricViews()...)
	views = append(views, obsMetrics.Views...)
	views = append(views, processMetricsViews.Views()...)

	tel.views = views
	if err = view.Register(views...); err != nil {
		return nil, err
	}

	processMetricsViews.StartCollection()

	// Until we can use a generic metrics exporter, default to Prometheus.
	opts := prometheus.Options{
		Namespace: "otelcol",
	}

	opts.ConstLabels = make(map[string]string)

	for k, v := range telAttrs {
		opts.ConstLabels[sanitizePrometheusKey(k)] = v
	}

	pe, err := prometheus.NewExporter(opts)
	if err != nil {
		return nil, err
	}

	view.RegisterExporter(pe)
	return pe, nil
}

func (tel *colTelemetry) initOpenTelemetry(svc *service) (http.Handler, error) {
	config := otelprometheus.Config{}
	c := controller.New(
		processor.NewFactory(
			selector.NewWithHistogramDistribution(
				histogram.WithExplicitBoundaries(config.DefaultHistogramBoundaries),
			),
			aggregation.CumulativeTemporalitySelector(),
			processor.WithMemory(true),
		),
	)

	pe, err := otelprometheus.New(config, c)
	if err != nil {
		return nil, err
	}

	svc.telemetry.MeterProvider = pe.MeterProvider()
	return pe, err
}

func (tel *colTelemetry) shutdown() error {
	view.Unregister(tel.views...)

	if tel.server != nil {
		return tel.server.Close()
	}

	return nil
}

func sanitizePrometheusKey(str string) string {
	runeFilterMap := func(r rune) rune {
		if unicode.IsDigit(r) || unicode.IsLetter(r) || r == '_' {
			return r
		}
		return '_'
	}
	return strings.Map(runeFilterMap, str)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"

	"github.com/ninjasphere/go-ninja/channels"
	"github.com/ninjasphere/go-zigbee/gateway"
)

type PowerChannel struct {
	Channel
	channel *channels.PowerChannel
}

func (c *PowerChannel) init() error {
	log.Debugf("Initialising power channel of device %d", *c.device.deviceInfo.IeeeAddress)

	clusterID := ClusterIDPower
	instantaneousDemandAttributeID := uint32(0x0400)
	minReportInterval := uint32(1)
	maxReportInterval := uint32(120)
	reportableChange := uint32(1)

	request := &gateway.GwSetAttributeReportingReq{
		DstAddress: &gateway.GwAddressStructT{
			AddressType: gateway.GwAddressTypeT_UNICAST.Enum(),
			IeeeAddr:    c.device.deviceInfo.IeeeAddress,
			EndpointId:  c.endpoint.EndpointId,
		},
		ClusterId: &clusterID,
		AttributeReportList: []*gateway.GwAttributeReportT{{
			AttributeId:       &instantaneousDemandAttributeID,
			AttributeType:     gateway.GwZclAttributeDataTypesT_ZCL_DATATYPE_INT24.Enum(),
			MinReportInterval: &minReportInterval,
			MaxReportInterval: &maxReportInterval,
			ReportableChange:  &reportableChange,
		}},
	}

	response := &gateway.GwSetAttributeReportingRspInd{}
	err := c.device.driver.gatewayConn.SendAsyncCommand(request, response, 20*time.Second)
	if err != nil {
		log.Errorf("Error enabling power reporting: %s", err)
	} else if response.Status.String() != "STATUS_SUCCESS" {
		log.Errorf("Failed to enable power reporting. status: %s", response.Status.String())
	}

	c.channel = channels.NewPowerChannel(c)
	err = c.device.driver.Conn.ExportChannel(c.device, c.channel, c.ID)
	if err != nil {
		log.Fatalf("Failed to announce power channel: %s", err)
	}

	go func() {
		for {
			log.Debugf("Polling for power")
			err := c.fetchState()
			if err != nil {
				log.Errorf("Failed to poll for power level %s", err)
			}
			time.Sleep(10 * time.Second)
		}
	}()

	return nil
}

func (c *PowerChannel) fetchState() error {

	request := &gateway.DevGetPowerReq{
		DstAddress: &gateway.GwAddressStructT{
			AddressType: gateway.GwAddressTypeT_UNICAST.Enum(),
			IeeeAddr:    c.device.deviceInfo.IeeeAddress,
		},
	}

	response := &gateway.DevGetPowerRspInd{}
	err := c.device.driver.gatewayConn.SendAsyncCommand(request, response, 10*time.Second)
	if err != nil {
		return fmt.Errorf("Error getting power level : %s", err)
	}
	if response.Status.String() != "STATUS_SUCCESS" {
		return fmt.Errorf("Failed to get power level. status: %s", response.Status.String())
	}

	log.Debugf("Got power value %d", *response.PowerValue)

	c.channel.SendState((float64)(*response.PowerValue))

	return nil
}
<commit_msg>Add assertions to help debug bugsnags of this kind https://bugsnag.com/ninja-blocks-1/driver-go-zigbee/errors/54ba09080eb4aaa7d97190d1<commit_after>package main

import (
	"fmt"
	"time"

	"github.com/ninjasphere/go-ninja/channels"
	"github.com/ninjasphere/go-zigbee/gateway"
)

type PowerChannel struct {
	Channel
	channel *channels.PowerChannel
}

func (c *PowerChannel) init() error {
	log.Debugf("Initialising power channel of device %d", *c.device.deviceInfo.IeeeAddress)

	clusterID := ClusterIDPower
	instantaneousDemandAttributeID := uint32(0x0400)
	minReportInterval := uint32(1)
	maxReportInterval := uint32(120)
	reportableChange := uint32(1)

	request := &gateway.GwSetAttributeReportingReq{
		DstAddress: &gateway.GwAddressStructT{
			AddressType: gateway.GwAddressTypeT_UNICAST.Enum(),
			IeeeAddr:    c.device.deviceInfo.IeeeAddress,
			EndpointId:  c.endpoint.EndpointId,
		},
		ClusterId: &clusterID,
		AttributeReportList: []*gateway.GwAttributeReportT{{
			AttributeId:       &instantaneousDemandAttributeID,
			AttributeType:     gateway.GwZclAttributeDataTypesT_ZCL_DATATYPE_INT24.Enum(),
			MinReportInterval: &minReportInterval,
			MaxReportInterval: &maxReportInterval,
			ReportableChange:  &reportableChange,
		}},
	}

	response := &gateway.GwSetAttributeReportingRspInd{}
	err := c.device.driver.gatewayConn.SendAsyncCommand(request, response, 20*time.Second)
	if err != nil {
		log.Errorf("Error enabling power reporting: %s", err)
	} else if response.Status.String() != "STATUS_SUCCESS" {
		log.Errorf("Failed to enable power reporting. status: %s", response.Status.String())
	}

	c.channel = channels.NewPowerChannel(c)
	err = c.device.driver.Conn.ExportChannel(c.device, c.channel, c.ID)
	if err != nil {
		log.Fatalf("Failed to announce power channel: %s", err)
	}

	go func() {
		for {
			log.Debugf("Polling for power")
			err := c.fetchState()
			if err != nil {
				log.Errorf("Failed to poll for power level %s", err)
			}
			time.Sleep(10 * time.Second)
		}
	}()

	return nil
}

func (c *PowerChannel) fetchState() error {

	request := &gateway.DevGetPowerReq{
		DstAddress: &gateway.GwAddressStructT{
			AddressType: gateway.GwAddressTypeT_UNICAST.Enum(),
			IeeeAddr:    c.device.deviceInfo.IeeeAddress,
		},
	}

	response := &gateway.DevGetPowerRspInd{}
	if c.device.driver == nil {
		log.Fatalf("assertion failed: c.device.driver != nil")
	}
	if c.device.driver.gatewayConn == nil {
		log.Fatalf("assertion failed: c.device.driver.gatewayConn != nil")
	}
	err := c.device.driver.gatewayConn.SendAsyncCommand(request, response, 10*time.Second)
	if err != nil {
		return fmt.Errorf("Error getting power level : %s", err)
	}
	if response.Status.String() != "STATUS_SUCCESS" {
		return fmt.Errorf("Failed to get power level. status: %s", response.Status.String())
	}

	log.Debugf("Got power value %d", *response.PowerValue)

	c.channel.SendState((float64)(*response.PowerValue))

	return nil
}
<|endoftext|>