<commit_before>package dhcp4

import (
	"net"
)

type Handler interface {
	ServeDHCP(req Packet, msgType MessageType, options Options) Packet
}

// A Server defines parameters for running a DHCP server.
type Server struct {
	Handler  Handler
	ServerIP net.IP // Used to bind to interface to send broadcast packets from
}

// ListenAndServe listens on the UDP network address s.Addr and then
// calls Serve to handle requests on incoming packets.  If
// s.Addr is blank, ":67" is used.
func (s *Server) ListenAndServe() error {
	l, err := net.ListenPacket("udp4", ":67")
	if err != nil {
		return err
	}
	defer l.Close()
	return s.Serve(l, 68)
}

func (s *Server) Serve(l net.PacketConn, replyPort int) error {
	var srcAddr *net.UDPAddr
	if s.ServerIP != nil {
		srcAddr = &net.UDPAddr{IP: s.ServerIP}
	}
	buffer := make([]byte, 1500)
	for {
		n, _, err := l.ReadFrom(buffer)
		if err != nil {
			return err
		}
		if n < 240 {
			continue
		}
		p := Packet(buffer[:n])
		options := p.ParseOptions()
		msgType := options[OptionDHCPMessageType]
		if len(msgType) != 1 {
			return nil
		}

		// TODO consider more packet validity checks
		if res := s.Handler.ServeDHCP(p, MessageType(msgType[0]), options); res != nil {
			dstIP := net.IPv4(255, 255, 255, 255)
			/*if !p.Broadcast() {
				dstIP = net.IP(p.CIAddr())
			}*/
			r, err := net.DialUDP("udp", srcAddr, &net.UDPAddr{IP: dstIP, Port: replyPort})
			if err == nil {
				defer r.Close()
				r.Write(res)
			}

		}
	}
	return nil
}

// ListenAndServe listens on the UDP network address addr
// and then calls Serve with handler to handle requests
// on incoming packets.
func ListenAndServe(handler Handler) error {
	return (&Server{Handler: handler}).ListenAndServe()
}
<commit_msg>Fix/Stop multiple dials that never close<commit_after>package dhcp4

import (
	"fmt"
	"net"
)

type Handler interface {
	ServeDHCP(req Packet, msgType MessageType, options Options) Packet
}

// A Server defines parameters for running a DHCP server.
type Server struct {
	Handler  Handler
	ServerIP net.IP // Used to bind to interface to send broadcast packets from
}

// ListenAndServe listens on the UDP network address s.Addr and then
// calls Serve to handle requests on incoming packets.  If
// s.Addr is blank, ":67" is used.
func (s *Server) ListenAndServe() error {
	l, err := net.ListenPacket("udp4", ":67")
	if err != nil {
		return err
	}
	defer l.Close()
	return s.Serve(l, 68)
}

func (s *Server) Serve(l net.PacketConn, replyPort int) error {
	var srcAddr *net.UDPAddr
	if s.ServerIP != nil {
		srcAddr = &net.UDPAddr{IP: s.ServerIP}
	}
	buffer := make([]byte, 1500)
	r, err := net.DialUDP("udp", srcAddr, &net.UDPAddr{IP: net.IPv4(255, 255, 255, 255), Port: replyPort})
	if err != nil {
		return err
	}
	defer r.Close()
	for {
		n, _, err := l.ReadFrom(buffer)
		if err != nil {
			return err
		}
		if n < 240 {
			continue
		}
		p := Packet(buffer[:n])
		options := p.ParseOptions()
		msgType := options[OptionDHCPMessageType]
		if len(msgType) != 1 {
			return nil
		}
		// TODO consider more packet validity checks
		if res := s.Handler.ServeDHCP(p, MessageType(msgType[0]), options); res != nil {
			if _, e := r.Write(res); e != nil {
				fmt.Println("Write Error:", e.Error())
			}
		}
	}
	return nil
}

// ListenAndServe listens on the UDP network address addr
// and then calls Serve with handler to handle requests
// on incoming packets.
func ListenAndServe(handler Handler) error {
	return (&Server{Handler: handler}).ListenAndServe()
}
<|endoftext|>


<commit_before>package rpc2

import (
	"io"
	"log"
	"net"
	"reflect"
	"unicode"
	"unicode/utf8"

	"github.com/cenkalti/hub"
)

// Precompute the reflect type for error.  Can't use error directly
// because Typeof takes an empty interface value.  This is annoying.
var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
var typeOfClient = reflect.TypeOf((*Client)(nil))

const (
	clientConnected hub.Kind = iota
	clientDisconnected
)

type Server struct {
	handlers map[string]*handler
	eventHub *hub.Hub
}

type handler struct {
	fn        reflect.Value
	argType   reflect.Type
	replyType reflect.Type
}

type connectionEvent struct {
	Client *Client
}

type disconnectionEvent struct {
	Client *Client
}

func (connectionEvent) Kind() hub.Kind    { return clientConnected }
func (disconnectionEvent) Kind() hub.Kind { return clientDisconnected }

func NewServer() *Server {
	return &Server{
		handlers: make(map[string]*handler),
		eventHub: hub.New(),
	}
}

// Handle registers the handler function for the given method. If a handler already exists for method, Handle panics.
func (s *Server) Handle(method string, handlerFunc interface{}) {
	addHandler(s.handlers, method, handlerFunc)
}

func addHandler(handlers map[string]*handler, mname string, handlerFunc interface{}) {
	if _, ok := handlers[mname]; ok {
		panic("rpc2: multiple registrations for " + mname)
	}

	method := reflect.ValueOf(handlerFunc)
	mtype := method.Type()
	// Method needs three ins: *client, *args, *reply.
	if mtype.NumIn() != 3 {
		log.Panicln("method", mname, "has wrong number of ins:", mtype.NumIn())
	}
	// First arg must be a pointer to rpc2.Client.
	clientType := mtype.In(0)
	if clientType.Kind() != reflect.Ptr {
		log.Panicln("method", mname, "client type not a pointer:", clientType)
	}
	if clientType != typeOfClient {
		log.Panicln("method", mname, "first argument", clientType.String(), "not *rpc2.Client")
	}
	// Second arg need not be a pointer.
	argType := mtype.In(1)
	if !isExportedOrBuiltinType(argType) {
		log.Panicln(mname, "argument type not exported:", argType)
	}
	// Third arg must be a pointer.
	replyType := mtype.In(2)
	if replyType.Kind() != reflect.Ptr {
		log.Panicln("method", mname, "reply type not a pointer:", replyType)
	}
	// Reply type must be exported.
	if !isExportedOrBuiltinType(replyType) {
		log.Panicln("method", mname, "reply type not exported:", replyType)
	}
	// Method needs one out.
	if mtype.NumOut() != 1 {
		log.Panicln("method", mname, "has wrong number of outs:", mtype.NumOut())
	}
	// The return type of the method must be error.
	if returnType := mtype.Out(0); returnType != typeOfError {
		log.Panicln("method", mname, "returns", returnType.String(), "not error")
	}
	handlers[mname] = &handler{
		fn:        method,
		argType:   argType,
		replyType: replyType,
	}
}

// Is this type exported or a builtin?
func isExportedOrBuiltinType(t reflect.Type) bool {
	for t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	// PkgPath will be non-empty even for an exported type,
	// so we need to check the type name as well.
	return isExported(t.Name()) || t.PkgPath() == ""
}

// Is this an exported - upper case - name?
func isExported(name string) bool {
	rune, _ := utf8.DecodeRuneInString(name)
	return unicode.IsUpper(rune)
}

// OnConnect registers a function to run when a client connects.
func (s *Server) OnConnect(f func(*Client)) {
	s.eventHub.Subscribe(clientConnected, func(e hub.Event) {
		go f(e.(connectionEvent).Client)
	})
}

// OnDisconnect registers a function to run when a client disconnects.
func (s *Server) OnDisconnect(f func(*Client)) {
	s.eventHub.Subscribe(clientDisconnected, func(e hub.Event) {
		go f(e.(disconnectionEvent).Client)
	})
}

// Accept accepts connections on the listener and serves requests
// for each incoming connection.  Accept blocks; the caller typically
// invokes it in a go statement.
func (s *Server) Accept(lis net.Listener) {
	for {
		conn, err := lis.Accept()
		if err != nil {
			log.Fatal("rpc.Serve: accept:", err.Error())
		}
		go s.ServeConn(conn)
	}
}

// ServeConn runs the server on a single connection.
// ServeConn blocks, serving the connection until the client hangs up.
// The caller typically invokes ServeConn in a go statement.
// ServeConn uses the gob wire format (see package gob) on the
// connection.  To use an alternate codec, use ServeCodec.
func (s *Server) ServeConn(conn io.ReadWriteCloser) {
	s.ServeCodec(newGobCodec(conn))
}

// ServeCodec is like ServeConn but uses the specified codec to
// decode requests and encode responses.
func (s *Server) ServeCodec(codec Codec) {
	defer codec.Close()

	// Client also handles the incoming connections.
	c := NewClientWithCodec(codec)
	c.handlers = s.handlers

	s.eventHub.Publish(connectionEvent{c})
	c.Run()
	s.eventHub.Publish(disconnectionEvent{c})
}
<commit_msg>fixed for new hub package.<commit_after>package rpc2

import (
	"io"
	"log"
	"net"
	"reflect"
	"unicode"
	"unicode/utf8"

	"github.com/cenkalti/hub"
)

// Precompute the reflect type for error.  Can't use error directly
// because Typeof takes an empty interface value.  This is annoying.
var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
var typeOfClient = reflect.TypeOf((*Client)(nil))

const (
	clientConnected = iota
	clientDisconnected
)

type Server struct {
	handlers map[string]*handler
	eventHub *hub.Hub
}

type handler struct {
	fn        reflect.Value
	argType   reflect.Type
	replyType reflect.Type
}

type connectionEvent struct {
	Client *Client
}

type disconnectionEvent struct {
	Client *Client
}

func (connectionEvent) Kind() int    { return clientConnected }
func (disconnectionEvent) Kind() int { return clientDisconnected }

func NewServer() *Server {
	return &Server{
		handlers: make(map[string]*handler),
		eventHub: &hub.Hub{},
	}
}

// Handle registers the handler function for the given method. If a handler already exists for method, Handle panics.
func (s *Server) Handle(method string, handlerFunc interface{}) {
	addHandler(s.handlers, method, handlerFunc)
}

func addHandler(handlers map[string]*handler, mname string, handlerFunc interface{}) {
	if _, ok := handlers[mname]; ok {
		panic("rpc2: multiple registrations for " + mname)
	}

	method := reflect.ValueOf(handlerFunc)
	mtype := method.Type()
	// Method needs three ins: *client, *args, *reply.
	if mtype.NumIn() != 3 {
		log.Panicln("method", mname, "has wrong number of ins:", mtype.NumIn())
	}
	// First arg must be a pointer to rpc2.Client.
	clientType := mtype.In(0)
	if clientType.Kind() != reflect.Ptr {
		log.Panicln("method", mname, "client type not a pointer:", clientType)
	}
	if clientType != typeOfClient {
		log.Panicln("method", mname, "first argument", clientType.String(), "not *rpc2.Client")
	}
	// Second arg need not be a pointer.
	argType := mtype.In(1)
	if !isExportedOrBuiltinType(argType) {
		log.Panicln(mname, "argument type not exported:", argType)
	}
	// Third arg must be a pointer.
	replyType := mtype.In(2)
	if replyType.Kind() != reflect.Ptr {
		log.Panicln("method", mname, "reply type not a pointer:", replyType)
	}
	// Reply type must be exported.
	if !isExportedOrBuiltinType(replyType) {
		log.Panicln("method", mname, "reply type not exported:", replyType)
	}
	// Method needs one out.
	if mtype.NumOut() != 1 {
		log.Panicln("method", mname, "has wrong number of outs:", mtype.NumOut())
	}
	// The return type of the method must be error.
	if returnType := mtype.Out(0); returnType != typeOfError {
		log.Panicln("method", mname, "returns", returnType.String(), "not error")
	}
	handlers[mname] = &handler{
		fn:        method,
		argType:   argType,
		replyType: replyType,
	}
}

// Is this type exported or a builtin?
func isExportedOrBuiltinType(t reflect.Type) bool {
	for t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	// PkgPath will be non-empty even for an exported type,
	// so we need to check the type name as well.
	return isExported(t.Name()) || t.PkgPath() == ""
}

// Is this an exported - upper case - name?
func isExported(name string) bool {
	rune, _ := utf8.DecodeRuneInString(name)
	return unicode.IsUpper(rune)
}

// OnConnect registers a function to run when a client connects.
func (s *Server) OnConnect(f func(*Client)) {
	s.eventHub.Subscribe(clientConnected, func(e hub.Event) {
		go f(e.(connectionEvent).Client)
	})
}

// OnDisconnect registers a function to run when a client disconnects.
func (s *Server) OnDisconnect(f func(*Client)) {
	s.eventHub.Subscribe(clientDisconnected, func(e hub.Event) {
		go f(e.(disconnectionEvent).Client)
	})
}

// Accept accepts connections on the listener and serves requests
// for each incoming connection.  Accept blocks; the caller typically
// invokes it in a go statement.
func (s *Server) Accept(lis net.Listener) {
	for {
		conn, err := lis.Accept()
		if err != nil {
			log.Fatal("rpc.Serve: accept:", err.Error())
		}
		go s.ServeConn(conn)
	}
}

// ServeConn runs the server on a single connection.
// ServeConn blocks, serving the connection until the client hangs up.
// The caller typically invokes ServeConn in a go statement.
// ServeConn uses the gob wire format (see package gob) on the
// connection.  To use an alternate codec, use ServeCodec.
func (s *Server) ServeConn(conn io.ReadWriteCloser) {
	s.ServeCodec(newGobCodec(conn))
}

// ServeCodec is like ServeConn but uses the specified codec to
// decode requests and encode responses.
func (s *Server) ServeCodec(codec Codec) {
	defer codec.Close()

	// Client also handles the incoming connections.
	c := NewClientWithCodec(codec)
	c.handlers = s.handlers

	s.eventHub.Publish(connectionEvent{c})
	c.Run()
	s.eventHub.Publish(disconnectionEvent{c})
}
<|endoftext|>


<commit_before>/* {{{ Copyright (c) Paul R. Tagliamonte <paultag@debian.org>, 2015
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE. }}} */

package main

import (
	"fmt"
	"io"
	"net"
	"regexp"
	"sync"
	"time"

	"github.com/golang/glog"
	"github.com/paultag/sniff/parser"

	corev1 "k8s.io/client-go/1.4/kubernetes/typed/core/v1"
	typedv1beta1 "k8s.io/client-go/1.4/kubernetes/typed/extensions/v1beta1"
	"k8s.io/client-go/1.4/pkg/api"
	_ "k8s.io/client-go/1.4/pkg/api/install"
	apiv1 "k8s.io/client-go/1.4/pkg/api/v1"
	_ "k8s.io/client-go/1.4/pkg/apis/extensions/install"
	extapiv1beta1 "k8s.io/client-go/1.4/pkg/apis/extensions/v1beta1"
	"k8s.io/client-go/1.4/pkg/fields"
	"k8s.io/client-go/1.4/pkg/util/intstr"
	"k8s.io/client-go/1.4/pkg/watch"
	"k8s.io/client-go/1.4/tools/cache"
	"k8s.io/client-go/1.4/tools/clientcmd"
)

const (
	// ingressClassKey picks a specific "class" for the Ingress. The controller
	// only processes Ingresses with this annotation either unset, or set
	// to either nginxIngressClass or the empty string.
	ingressClassKey = "kubernetes.io/ingress.class"
)

type ServerAndRegexp struct {
	Server *Server
	Regexp *regexp.Regexp
}

type Proxy struct {
	Lock       sync.RWMutex
	ServerList []ServerAndRegexp
	Default    *Server
}

func (c *Proxy) Get(host string) *Server {
	c.Lock.RLock()
	defer c.Lock.RUnlock()

	for _, tuple := range c.ServerList {
		if tuple.Regexp.MatchString(host) {
			return tuple.Server
		}
	}
	return c.Default
}

func (p *Proxy) Update(c *Config) error {
	servers := []ServerAndRegexp{}
	for i, server := range c.Servers {
		for _, hostname := range server.Names {
			var host_regexp *regexp.Regexp
			var err error
			if server.Regexp {
				host_regexp, err = regexp.Compile(hostname)
			} else {
				host_regexp, err = regexp.Compile("^" + regexp.QuoteMeta(hostname) + "$")
			}
			if err != nil {
				return fmt.Errorf("cannot update proxy due to invalid regex: %v", err)
			}
			tuple := ServerAndRegexp{&c.Servers[i], host_regexp}
			servers = append(servers, tuple)
		}
	}
	var def *Server
	for i, server := range c.Servers {
		if server.Default {
			def = &c.Servers[i]
			break
		}
	}

	p.Lock.Lock()
	defer p.Lock.Unlock()
	p.ServerList = servers
	p.Default = def

	return nil
}

func (c *Config) Serve() error {
	glog.V(1).Infof("Listening on %s:%d", c.Bind.Host, c.Bind.Port)
	listener, err := net.Listen("tcp", fmt.Sprintf(
		"%s:%d", c.Bind.Host, c.Bind.Port,
	))
	if err != nil {
		return err
	}

	proxy := Proxy{}
	err = proxy.Update(c)
	if err != nil {
		return err
	}

	if c.Kubernetes != nil {
		rules := clientcmd.NewDefaultClientConfigLoadingRules()
		if c.Kubernetes.Kubeconfig != "" {
			rules.ExplicitPath = c.Kubernetes.Kubeconfig
		}
		cmdcfg, err := rules.Load()
		if err != nil {
			return err
		}
		ccfg := clientcmd.NewDefaultClientConfig(*cmdcfg, &clientcmd.ConfigOverrides{})
		rcfg, err := ccfg.ClientConfig()
		if err != nil {
			return err
		}
		extclient := typedv1beta1.NewForConfigOrDie(rcfg)
		client := corev1.NewForConfigOrDie(rcfg)

		// trigger to update the proxy
		updateTrigger := make(chan struct{}, 1)

		// watch services
		services := NotifyingStore{
			Store: cache.NewStore(cache.MetaNamespaceKeyFunc),
			NotifyFunc: func () {
				select {
				case updateTrigger <- struct{}{}:
				default:
				}
			},
		}
		lw := cache.NewListWatchFromClient(client, "services", "", fields.Everything())
		refl := cache.NewReflector(lw, &apiv1.Service{}, &services, time.Minute)
		refl.Run()

		// wait until services are ready
		glog.V(1).Infof("Waiting for service store to be ready")
		for {
			if refl.LastSyncResourceVersion() != "" {
				break
			}
			time.Sleep(time.Millisecond * 200)
		}

		// watch ingresses
		ingresses := map[string]*extapiv1beta1.Ingress{}
		lock := sync.Mutex{}
		class := c.Kubernetes.IngressClass
		if class == "" {
			class = "sniff"
		}
		go func() {
			for {
				w, err := extclient.Ingresses("").Watch(api.ListOptions{})
				if err != nil {
					glog.Errorf("Ingress watch error: %v", err)
					// TODO: add backoff logic
					time.Sleep(time.Second)
					continue
				}
				evs := w.ResultChan()

			EventLoop:
				for ev := range evs {
					i := ev.Object.(*extapiv1beta1.Ingress)
					if i != nil && i.Annotations[ingressClassKey] != class {
						continue
					}
					switch ev.Type {
					case watch.Added, watch.Modified:
						glog.V(5).Infof("event %s for %s/%s", ev.Type, i.Namespace, i.Name)
						lock.Lock()
						ingresses[i.Namespace+"/"+i.Name] = i
						lock.Unlock()
					case watch.Deleted:
						glog.V(5).Infof("event %s for %s/%s", ev.Type, i.Namespace, i.Name)
						lock.Lock()
						delete(ingresses, i.Namespace+"/"+i.Name)
						lock.Unlock()
					case watch.Error:
						if i != nil {
							glog.V(5).Infof("event %s for %s/%s", ev.Type, i.Namespace, i.Name)
						}
						w.Stop()
						break EventLoop
					}
					select {
					case updateTrigger <- struct{}{}:
					default:
					}
				}

				// TODO: add backoff logic
				time.Sleep(time.Second)
			}
		}()

		go func() {
			for range updateTrigger {
				lock.Lock()

				serverForBackend := func(ing *extapiv1beta1.Ingress, backend *extapiv1beta1.IngressBackend) (*Server, error) {
					obj, found, err := services.GetByKey(fmt.Sprintf("%s/%s", ing.Namespace, backend.ServiceName))
					if err != nil {
						return nil, err
					}
					if !found {
						return nil, fmt.Errorf("service %s/%s not found", ing.Namespace, backend.ServiceName)
					}
					svc := obj.(*apiv1.Service)
					var port int
					if backend.ServicePort.Type == intstr.String {
						for _, p := range svc.Spec.Ports {
							if p.Name == backend.ServicePort.StrVal {
								port = int(p.Port)
								break
							}
						}
						if port == 0 {
							return nil, fmt.Errorf("port %q of service %s/%s not found", backend.ServicePort.StrVal, svc.Namespace, svc.Name)
						}
					} else {
						port = int(backend.ServicePort.IntVal)
					}
					return &Server{
						Host: svc.Spec.ClusterIP,
						// TODO: support string values:
						Port: port,
					}, nil
				}

				for _, i := range ingresses {
					c.Servers = []Server{}
					if i.Spec.Backend != nil {
						s, err := serverForBackend(i, i.Spec.Backend)
						if err != nil {
							glog.Errorf("Ingress %s/%s error with default backend, skipping: %v", i.Namespace, i.Name, err)
						} else {
							s.Default = true
							glog.V(4).Infof("Adding default backend -> %s:%d", s.Host, s.Port)
							c.Servers = append(c.Servers, *s)
						}
					}
					for _, r := range i.Spec.Rules {
						if r.HTTP == nil {
							glog.Errorf("Ingress %s/%s error with rule, skipping: http must be set", i.Namespace, i.Name)
							continue
						}
						for _, p := range r.HTTP.Paths {
							if p.Path != "" && p.Path != "/" {
								glog.Errorf("Ingress %s/%s error with rule, skipping: %v", i.Namespace, i.Name, err)
								continue
							}
							s, err := serverForBackend(i, &p.Backend)
							if err != nil {
								glog.Errorf("Ingress %s/%s error with rule %q path %q, skipping: %v", i.Namespace, i.Name, r.Host, p.Path, err)
								continue
							}
							s.Names = []string{r.Host}
							glog.V(4).Infof("Adding backend %q -> %s:%d", r.Host, s.Host, s.Port)
							c.Servers = append(c.Servers, *s)
						}
					}
				}
				lock.Unlock()

				glog.V(2).Infof("Updating proxy configuration")
				err := proxy.Update(c)
				if err != nil {
					glog.Errorf("Error updating proxy: %v", err)
					// TODO: add backoff logic
					time.Sleep(time.Second)
				} else {
					glog.V(2).Infof("Proxy configuration update done")
				}

			}
		}()
	}

	for {
		conn, err := listener.Accept()
		if err != nil {
			return err
		}
		glog.V(3).Infof(
			"%s -> %s",
			conn.RemoteAddr(),
			conn.LocalAddr(),
		)
		go proxy.Handle(conn)
	}
}

func (s *Proxy) Handle(conn net.Conn) {
	data := make([]byte, 4096)

	length, err := conn.Read(data)
	if err != nil {
		glog.Errorf("Error reading the configuration: %s", err)
	}

	var proxy *Server
	hostname, hostname_err := parser.GetHostname(data[:])
	if hostname_err == nil {
		glog.V(6).Infof("Parsed hostname: %s", hostname)

		proxy = s.Get(hostname)
		if proxy == nil {
			glog.V(4).Infof("No proxy matched %s", hostname)
			conn.Close()
			return
		}
	} else {
		glog.V(6).Info("Parsed request without hostname")

		proxy = s.Default
		if proxy == nil {
			glog.V(4).Info("No default proxy")
			conn.Close()
			return
		}
	}

	clientConn, err := net.Dial("tcp", fmt.Sprintf(
		"%s:%d", proxy.Host, proxy.Port,
	))
	if err != nil {
		glog.Warningf("Error connecting to backend: %s", err)
		conn.Close()
		return
	}
	n, err := clientConn.Write(data[:length])
	glog.V(7).Infof("Wrote %d bytes", n)
	if err != nil {
		glog.V(7).Infof("Error sending data to backend: %s", err)
		conn.Close()
		clientConn.Close()
	}
	Copycat(clientConn, conn)
}

func Copycat(client, server net.Conn) {
	defer client.Close()
	defer server.Close()

	glog.V(6).Info("Entering copy routine")

	doCopy := func(s, c net.Conn, cancel chan<- bool) {
		io.Copy(s, c)
		cancel <- true
	}

	cancel := make(chan bool, 2)

	go doCopy(server, client, cancel)
	go doCopy(client, server, cancel)

	select {
	case <-cancel:
		glog.V(6).Info("Disconnected")
		return
	}

}

// vim: foldmethod=marker
<commit_msg>Fix connection closing<commit_after>/* {{{ Copyright (c) Paul R. Tagliamonte <paultag@debian.org>, 2015
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE. }}} */

package main

import (
	"fmt"
	"io"
	"net"
	"regexp"
	"sync"
	"time"

	"github.com/golang/glog"
	"github.com/paultag/sniff/parser"

	corev1 "k8s.io/client-go/1.4/kubernetes/typed/core/v1"
	typedv1beta1 "k8s.io/client-go/1.4/kubernetes/typed/extensions/v1beta1"
	"k8s.io/client-go/1.4/pkg/api"
	_ "k8s.io/client-go/1.4/pkg/api/install"
	apiv1 "k8s.io/client-go/1.4/pkg/api/v1"
	_ "k8s.io/client-go/1.4/pkg/apis/extensions/install"
	extapiv1beta1 "k8s.io/client-go/1.4/pkg/apis/extensions/v1beta1"
	"k8s.io/client-go/1.4/pkg/fields"
	"k8s.io/client-go/1.4/pkg/util/intstr"
	"k8s.io/client-go/1.4/pkg/watch"
	"k8s.io/client-go/1.4/tools/cache"
	"k8s.io/client-go/1.4/tools/clientcmd"
)

const (
	// ingressClassKey picks a specific "class" for the Ingress. The controller
	// only processes Ingresses with this annotation either unset, or set
	// to either nginxIngressClass or the empty string.
	ingressClassKey = "kubernetes.io/ingress.class"
)

type ServerAndRegexp struct {
	Server *Server
	Regexp *regexp.Regexp
}

type Proxy struct {
	Lock       sync.RWMutex
	ServerList []ServerAndRegexp
	Default    *Server
}

func (c *Proxy) Get(host string) *Server {
	c.Lock.RLock()
	defer c.Lock.RUnlock()

	for _, tuple := range c.ServerList {
		if tuple.Regexp.MatchString(host) {
			return tuple.Server
		}
	}
	return c.Default
}

func (p *Proxy) Update(c *Config) error {
	servers := []ServerAndRegexp{}
	for i, server := range c.Servers {
		for _, hostname := range server.Names {
			var host_regexp *regexp.Regexp
			var err error
			if server.Regexp {
				host_regexp, err = regexp.Compile(hostname)
			} else {
				host_regexp, err = regexp.Compile("^" + regexp.QuoteMeta(hostname) + "$")
			}
			if err != nil {
				return fmt.Errorf("cannot update proxy due to invalid regex: %v", err)
			}
			tuple := ServerAndRegexp{&c.Servers[i], host_regexp}
			servers = append(servers, tuple)
		}
	}
	var def *Server
	for i, server := range c.Servers {
		if server.Default {
			def = &c.Servers[i]
			break
		}
	}

	p.Lock.Lock()
	defer p.Lock.Unlock()
	p.ServerList = servers
	p.Default = def

	return nil
}

func (c *Config) Serve() error {
	glog.V(1).Infof("Listening on %s:%d", c.Bind.Host, c.Bind.Port)
	listener, err := net.Listen("tcp", fmt.Sprintf(
		"%s:%d", c.Bind.Host, c.Bind.Port,
	))
	if err != nil {
		return err
	}

	proxy := Proxy{}
	err = proxy.Update(c)
	if err != nil {
		return err
	}

	if c.Kubernetes != nil {
		rules := clientcmd.NewDefaultClientConfigLoadingRules()
		if c.Kubernetes.Kubeconfig != "" {
			rules.ExplicitPath = c.Kubernetes.Kubeconfig
		}
		cmdcfg, err := rules.Load()
		if err != nil {
			return err
		}
		ccfg := clientcmd.NewDefaultClientConfig(*cmdcfg, &clientcmd.ConfigOverrides{})
		rcfg, err := ccfg.ClientConfig()
		if err != nil {
			return err
		}
		extclient := typedv1beta1.NewForConfigOrDie(rcfg)
		client := corev1.NewForConfigOrDie(rcfg)

		// trigger to update the proxy
		updateTrigger := make(chan struct{}, 1)

		// watch services
		services := NotifyingStore{
			Store: cache.NewStore(cache.MetaNamespaceKeyFunc),
			NotifyFunc: func () {
				select {
				case updateTrigger <- struct{}{}:
				default:
				}
			},
		}
		lw := cache.NewListWatchFromClient(client, "services", "", fields.Everything())
		refl := cache.NewReflector(lw, &apiv1.Service{}, &services, time.Minute)
		refl.Run()

		// wait until services are ready
		glog.V(1).Infof("Waiting for service store to be ready")
		for {
			if refl.LastSyncResourceVersion() != "" {
				break
			}
			time.Sleep(time.Millisecond * 200)
		}

		// watch ingresses
		ingresses := map[string]*extapiv1beta1.Ingress{}
		lock := sync.Mutex{}
		class := c.Kubernetes.IngressClass
		if class == "" {
			class = "sniff"
		}
		go func() {
			for {
				w, err := extclient.Ingresses("").Watch(api.ListOptions{})
				if err != nil {
					glog.Errorf("Ingress watch error: %v", err)
					// TODO: add backoff logic
					time.Sleep(time.Second)
					continue
				}
				evs := w.ResultChan()

			EventLoop:
				for ev := range evs {
					i := ev.Object.(*extapiv1beta1.Ingress)
					if i != nil && i.Annotations[ingressClassKey] != class {
						continue
					}
					switch ev.Type {
					case watch.Added, watch.Modified:
						glog.V(5).Infof("event %s for %s/%s", ev.Type, i.Namespace, i.Name)
						lock.Lock()
						ingresses[i.Namespace+"/"+i.Name] = i
						lock.Unlock()
					case watch.Deleted:
						glog.V(5).Infof("event %s for %s/%s", ev.Type, i.Namespace, i.Name)
						lock.Lock()
						delete(ingresses, i.Namespace+"/"+i.Name)
						lock.Unlock()
					case watch.Error:
						if i != nil {
							glog.V(5).Infof("event %s for %s/%s", ev.Type, i.Namespace, i.Name)
						}
						w.Stop()
						break EventLoop
					}
					select {
					case updateTrigger <- struct{}{}:
					default:
					}
				}

				// TODO: add backoff logic
				time.Sleep(time.Second)
			}
		}()

		go func() {
			for range updateTrigger {
				lock.Lock()

				serverForBackend := func(ing *extapiv1beta1.Ingress, backend *extapiv1beta1.IngressBackend) (*Server, error) {
					obj, found, err := services.GetByKey(fmt.Sprintf("%s/%s", ing.Namespace, backend.ServiceName))
					if err != nil {
						return nil, err
					}
					if !found {
						return nil, fmt.Errorf("service %s/%s not found", ing.Namespace, backend.ServiceName)
					}
					svc := obj.(*apiv1.Service)
					var port int
					if backend.ServicePort.Type == intstr.String {
						for _, p := range svc.Spec.Ports {
							if p.Name == backend.ServicePort.StrVal {
								port = int(p.Port)
								break
							}
						}
						if port == 0 {
							return nil, fmt.Errorf("port %q of service %s/%s not found", backend.ServicePort.StrVal, svc.Namespace, svc.Name)
						}
					} else {
						port = int(backend.ServicePort.IntVal)
					}
					return &Server{
						Host: svc.Spec.ClusterIP,
						// TODO: support string values:
						Port: port,
					}, nil
				}

				for _, i := range ingresses {
					c.Servers = []Server{}
					if i.Spec.Backend != nil {
						s, err := serverForBackend(i, i.Spec.Backend)
						if err != nil {
							glog.Errorf("Ingress %s/%s error with default backend, skipping: %v", i.Namespace, i.Name, err)
						} else {
							s.Default = true
							glog.V(4).Infof("Adding default backend -> %s:%d", s.Host, s.Port)
							c.Servers = append(c.Servers, *s)
						}
					}
					for _, r := range i.Spec.Rules {
						if r.HTTP == nil {
							glog.Errorf("Ingress %s/%s error with rule, skipping: http must be set", i.Namespace, i.Name)
							continue
						}
						for _, p := range r.HTTP.Paths {
							if p.Path != "" && p.Path != "/" {
								glog.Errorf("Ingress %s/%s error with rule, skipping: %v", i.Namespace, i.Name, err)
								continue
							}
							s, err := serverForBackend(i, &p.Backend)
							if err != nil {
								glog.Errorf("Ingress %s/%s error with rule %q path %q, skipping: %v", i.Namespace, i.Name, r.Host, p.Path, err)
								continue
							}
							s.Names = []string{r.Host}
							glog.V(4).Infof("Adding backend %q -> %s:%d", r.Host, s.Host, s.Port)
							c.Servers = append(c.Servers, *s)
						}
					}
				}
				lock.Unlock()

				glog.V(2).Infof("Updating proxy configuration")
				err := proxy.Update(c)
				if err != nil {
					glog.Errorf("Error updating proxy: %v", err)
					// TODO: add backoff logic
					time.Sleep(time.Second)
				} else {
					glog.V(2).Infof("Proxy configuration update done")
				}

			}
		}()
	}

	for {
		conn, err := listener.Accept()
		if err != nil {
			return err
		}
		glog.V(3).Infof(
			"%s -> %s",
			conn.RemoteAddr(),
			conn.LocalAddr(),
		)
		go proxy.Handle(conn)
	}
}

func (s *Proxy) Handle(conn net.Conn) {
	defer conn.Close()
	data := make([]byte, 4096)

	length, err := conn.Read(data)
	if err != nil {
		glog.V(4).Infof("Error reading the first 4k of the connection: %s", err)
		return
	}

	var proxy *Server
	hostname, hostname_err := parser.GetHostname(data[:])
	if hostname_err == nil {
		glog.V(6).Infof("Parsed hostname: %s", hostname)

		proxy = s.Get(hostname)
		if proxy == nil {
			glog.V(4).Infof("No proxy matched %s", hostname)
			return
		}
	} else {
		glog.V(6).Info("Parsed request without hostname")

		proxy = s.Default
		if proxy == nil {
			glog.V(4).Info("No default proxy")
			return
		}
	}

	clientConn, err := net.Dial("tcp", fmt.Sprintf(
		"%s:%d", proxy.Host, proxy.Port,
	))
	if err != nil {
		glog.Warningf("Error connecting to backend: %s", err)
		return
	}
	defer clientConn.Close()
	n, err := clientConn.Write(data[:length])
	glog.V(7).Infof("Wrote %d bytes", n)
	if err != nil {
		glog.V(7).Infof("Error sending data to backend: %s", err)
		clientConn.Close()
	}
	Copycat(clientConn, conn)
}

func Copycat(client, server net.Conn) {
	glog.V(6).Info("Entering copy routine")

	doCopy := func(s, c net.Conn, cancel chan<- bool) {
		io.Copy(s, c)
		cancel <- true
	}

	cancel := make(chan bool, 2)

	go doCopy(server, client, cancel)
	go doCopy(client, server, cancel)

	select {
	case <-cancel:
		glog.V(6).Info("Disconnected")
		return
	}

}

// vim: foldmethod=marker
<|endoftext|>


<commit_before>// +build windows

package helpers

var RootFolder = "C:/"
<commit_msg>test fix for windows: retrieve current root folder via os.Getwd<commit_after>// +build windows

package helpers

import (
	"fmt"
	"os"
)

var RootFolder = retrieveRootFolder()

func retrieveRootFolder() string {
	cwd, _ := os.Getwd();

	return fmt.Sprintf("%s:/", cwd[0:1])
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 Sam Kumar, Michael Andersen, and the University
 * of California, Berkeley.
 *
 * This file is part of Mr. Plotter (the Multi-Resolution Plotter).
 *
 * Mr. Plotter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Mr. Plotter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Mr. Plotter.  If not, see <http://www.gnu.org/licenses/>.
 */

package csvquery

import (
	"context"
	"encoding/csv"
	"errors"
	"fmt"
	"math"
	"time"

	btrdb "gopkg.in/btrdb.v4"
)

const (
	// AlignedWindowsQuery specifies that an Aligned Windows query (i.e.
	// Statistical Values query) should be made.
	AlignedWindowsQuery = iota

	// WindowsQuery specifies that a Windows query should be made.
	WindowsQuery

	// RawQuery specifies that a Raw Values query should be made.
	RawQuery
)

// CSVQuery stores the parameters for a CSV query.
type CSVQuery struct {
	// QueryType should be one of AlignedWindowsQuery, WindowsQuery, or
	// RawQuery. It specifies what data should be in the CSV file for each
	// stream.
	QueryType int

	// StartTime is the start time for the query, in nanoseconds.
	StartTime int64

	// EndTime is the end time for the query, in nanoseconds.
	EndTime int64

	// WindowSize specifies the size of the window, in nanoseconds, for a
	// Windows query. It is ignored for Aligned Windows queries and Raw Values
	// queries.
	WindowSize uint64

	// Depth specifies the point width exponent for AlignedWindows queries, and
	// the maximum depth for Windows queries. It is ignored for Raw Values
	// queries.
	Depth uint8

	// Streams is a slice of streams to query.
	Streams []*btrdb.Stream

	// Versions is a slice of version numbers to query for each stream.
	// Defaults to 0 (most recent version) for all streams if nil.
	Versions []uint64

	// Labels contains the name to use for each stream in the output CSV file.
	Labels []string
}

// streambuffer is a buffer for an array of pending requests, allowing the user
// to individually manipulate the response channels in a type-independent way.
type streambuffer interface {
	getTime(i int) int64
	isOpen(i int) bool
	readPoint(i int) (bool, error)
	writePoint(i int, row []string)
	writeEmptyPoint(i int, row []string)
	getHeaderRow(labels []string) []string
}

type stabufentry struct {
	stac chan btrdb.StatPoint
	verc chan uint64
	errc chan error
	pt   btrdb.StatPoint
	open bool
}

type stabuffer []stabufentry

func (sb stabuffer) getTime(i int) int64 {
	return sb[i].pt.Time
}

func (sb stabuffer) isOpen(i int) bool {
	return sb[i].open
}

func (sb stabuffer) readPoint(i int) (bool, error) {
	sb[i].pt, sb[i].open = <-sb[i].stac
	if !sb[i].open {
		err := <-sb[i].errc
		return sb[i].open, err
	}
	return sb[i].open, nil
}

func (sb stabuffer) writePoint(i int, row []string) {
	offset := 2 + (i << 2)
	row[offset+0] = fmt.Sprintf("%f", sb[i].pt.Min)
	row[offset+1] = fmt.Sprintf("%f", sb[i].pt.Mean)
	row[offset+2] = fmt.Sprintf("%f", sb[i].pt.Max)
	row[offset+3] = fmt.Sprintf("%d", sb[i].pt.Count)
}

func (sb stabuffer) writeEmptyPoint(i int, row []string) {
	offset := 2 + (i << 2)
	row[offset+0] = ""
	row[offset+1] = ""
	row[offset+2] = ""
	row[offset+3] = ""
}

func (sb stabuffer) getHeaderRow(labels []string) []string {
	numcols := 2 + (len(sb) << 2)
	row := make([]string, numcols, numcols)
	row[0] = "Timestamp (ns)"
	row[1] = "Date/Time"
	for i, label := range labels {
		offset := 2 + (i << 2)
		row[offset+0] = fmt.Sprintf("%s (Min)", label)
		row[offset+1] = fmt.Sprintf("%s (Mean)", label)
		row[offset+2] = fmt.Sprintf("%s (Max)", label)
		row[offset+3] = fmt.Sprintf("%s (Count)", label)
	}
	return row
}

type rawbufentry struct {
	rawc chan btrdb.RawPoint
	verc chan uint64
	errc chan error
	pt   btrdb.RawPoint
	open bool
}

type rawbuffer []rawbufentry

func (rb rawbuffer) getTime(i int) int64 {
	return rb[i].pt.Time
}

func (rb rawbuffer) isOpen(i int) bool {
	return rb[i].open
}

func (rb rawbuffer) readPoint(i int) (bool, error) {
	rb[i].pt, rb[i].open = <-rb[i].rawc
	if !rb[i].open {
		err := <-rb[i].errc
		return rb[i].open, err
	}
	return rb[i].open, nil
}

func (rb rawbuffer) writePoint(i int, row []string) {
	offset := 2 + i
	row[offset] = fmt.Sprintf("%f", rb[i].pt.Value)
}

func (rb rawbuffer) writeEmptyPoint(i int, row []string) {
	offset := 2 + i
	row[offset] = ""
}

func (rb rawbuffer) getHeaderRow(labels []string) []string {
	numcols := 2 + len(rb)
	row := make([]string, numcols, numcols)
	row[0] = "Timestamp (ns)"
	row[1] = "Date/Time"
	for i, label := range labels {
		offset := 2 + i
		row[offset+0] = label
	}
	return row
}

// MakeCSVQuery performs a CSV query, and outputs the result using the provided
// CSV writer.
func MakeCSVQuery(ctx context.Context, b *btrdb.BTrDB, q *CSVQuery, w *csv.Writer) error {
	var numstreams = len(q.Streams)
	if numstreams != len(q.Labels) {
		return fmt.Errorf("Got %d streams but %d labels", len(q.Streams), len(q.Labels))
	}

	var versions = q.Versions
	if versions == nil {
		versions = make([]uint64, numstreams, numstreams)
	}

	switch q.QueryType {
	case AlignedWindowsQuery:
		var sq stabuffer = make([]stabufentry, numstreams, numstreams)
		for i, s := range q.Streams {
			sq[i].stac, sq[i].verc, sq[i].errc = s.AlignedWindows(ctx, q.StartTime, q.EndTime, q.Depth, versions[i])
		}
		return createCSV(sq, q, w, true)
	case WindowsQuery:
		var sq stabuffer = make([]stabufentry, numstreams, numstreams)
		for i, s := range q.Streams {
			sq[i].stac, sq[i].verc, sq[i].errc = s.Windows(ctx, q.StartTime, q.EndTime, q.WindowSize, q.Depth, versions[i])
		}
		return createCSV(sq, q, w, true)
	case RawQuery:
		var sq rawbuffer = make([]rawbufentry, numstreams, numstreams)
		for i, s := range q.Streams {
			sq[i].rawc, sq[i].verc, sq[i].errc = s.RawValues(ctx, q.StartTime, q.EndTime, versions[i])
		}
		return createCSV(sq, q, w, false)
	default:
		return errors.New("Invalid query type")
	}
}

func createCSV(buf streambuffer, q *CSVQuery, w *csv.Writer, statistical bool) error {
	// Buffer for the row of the CSV that we are writing
	var row = buf.getHeaderRow(q.Labels)

	// Write the header row
	var err = w.Write(row)
	if err != nil {
		return err
	}

	var open bool
	var numopen = len(q.Streams)
	for i := range q.Streams {
		open, err = buf.readPoint(i)
		if !open {
			numopen--
			if err != nil {
				return err
			}
		}
	}

	for {
		// Compute the time of the next row
		var earliest int64 = math.MaxInt64
		for i := range q.Streams {
			if buf.isOpen(i) && buf.getTime(i) < earliest {
				earliest = buf.getTime(i)
			}
		}

		// Compute the next row
		row[0] = fmt.Sprintf("%d", earliest)
		row[1] = time.Unix(0, earliest).Format(time.RFC3339Nano)
		for i := range q.Streams {
			if !buf.isOpen(i) {
				continue
			} else if buf.getTime(i) == earliest {
				buf.writePoint(i, row)

				// We consumed this point, so fetch the next point
				open, err = buf.readPoint(i)
				if !open {
					numopen--
					if err != nil {
						return err
					}
				}
			} else {
				buf.writeEmptyPoint(i, row)
			}
		}

		if numopen == 0 {
			break
		}

		// Emit the row
		err = w.Write(row)
		if err != nil {
			return nil
		}
	}

	return nil
}
<commit_msg>Improve CSV generation code<commit_after>/*
 * Copyright (C) 2017 Sam Kumar, Michael Andersen, and the University
 * of California, Berkeley.
 *
 * This file is part of Mr. Plotter (the Multi-Resolution Plotter).
 *
 * Mr. Plotter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Mr. Plotter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Mr. Plotter.  If not, see <http://www.gnu.org/licenses/>.
 */

package csvquery

import (
	"context"
	"encoding/csv"
	"errors"
	"fmt"
	"math"
	"time"

	btrdb "gopkg.in/btrdb.v4"
)

const (
	// AlignedWindowsQuery specifies that an Aligned Windows query (i.e.
	// Statistical Values query) should be made.
	AlignedWindowsQuery = iota

	// WindowsQuery specifies that a Windows query should be made.
	WindowsQuery

	// RawQuery specifies that a Raw Values query should be made.
	RawQuery
)

// CSVQuery stores the parameters for a CSV query.
type CSVQuery struct {
	// QueryType should be one of AlignedWindowsQuery, WindowsQuery, or
	// RawQuery. It specifies what data should be in the CSV file for each
	// stream.
	QueryType int

	// StartTime is the start time for the query, in nanoseconds.
	StartTime int64

	// EndTime is the end time for the query, in nanoseconds.
	EndTime int64

	// WindowSize specifies the size of the window, in nanoseconds, for a
	// Windows query. It is ignored for Aligned Windows queries and Raw Values
	// queries.
	WindowSize uint64

	// Depth specifies the point width exponent for AlignedWindows queries, and
	// the maximum depth for Windows queries. It is ignored for Raw Values
	// queries.
	Depth uint8

	// Streams is a slice of streams to query.
	Streams []*btrdb.Stream

	// Versions is a slice of version numbers to query for each stream.
	// Defaults to 0 (most recent version) for all streams if nil.
	Versions []uint64

	// Labels contains the name to use for each stream in the output CSV file.
	Labels []string

	// IncludeVersions specifies whether the version number of each stream
	// should be included in the CSV header.
	IncludeVersions bool
}

func setTimeHeaders(row []string) {
	row[0] = "Timestamp (ns)"
	row[1] = "Human-Readable Time (UTC)"
}

// streambuffer is a buffer for an array of pending requests, allowing the user
// to individually manipulate the response channels in a type-independent way.
type streambuffer interface {
	getTime(i int) int64
	isOpen(i int) bool
	readPoint(i int) (bool, error)
	writePoint(i int, row []string)
	writeEmptyPoint(i int, row []string)
	getHeaderRow(labels []string, includeVersions bool) []string
}

type stabufentry struct {
	stac chan btrdb.StatPoint
	verc chan uint64
	errc chan error
	pt   btrdb.StatPoint
	open bool
}

// stabuffer satisfies the streambuffer interface using statistical points
type stabuffer []stabufentry

func (sb stabuffer) getTime(i int) int64 {
	return sb[i].pt.Time
}

func (sb stabuffer) isOpen(i int) bool {
	return sb[i].open
}

func (sb stabuffer) readPoint(i int) (bool, error) {
	sb[i].pt, sb[i].open = <-sb[i].stac
	if !sb[i].open {
		err := <-sb[i].errc
		return sb[i].open, err
	}
	return sb[i].open, nil
}

func (sb stabuffer) writePoint(i int, row []string) {
	offset := 2 + (i << 2)
	row[offset+0] = fmt.Sprintf("%f", sb[i].pt.Min)
	row[offset+1] = fmt.Sprintf("%f", sb[i].pt.Mean)
	row[offset+2] = fmt.Sprintf("%f", sb[i].pt.Max)
	row[offset+3] = fmt.Sprintf("%d", sb[i].pt.Count)
}

func (sb stabuffer) writeEmptyPoint(i int, row []string) {
	offset := 2 + (i << 2)
	row[offset+0] = ""
	row[offset+1] = ""
	row[offset+2] = ""
	row[offset+3] = ""
}

func (sb stabuffer) getHeaderRow(labels []string, includeVersions bool) []string {
	numcols := 2 + (len(sb) << 2)
	row := make([]string, numcols, numcols)
	setTimeHeaders(row)
	versionStr := ""
	for i, label := range labels {
		offset := 2 + (i << 2)
		if includeVersions {
			versionStr = fmt.Sprintf(", ver. %d", <-sb[i].verc)
		}
		row[offset+0] = fmt.Sprintf("%s%s (Min)", label, versionStr)
		row[offset+1] = fmt.Sprintf("%s%s (Mean)", label, versionStr)
		row[offset+2] = fmt.Sprintf("%s%s (Max)", label, versionStr)
		row[offset+3] = fmt.Sprintf("%s%s (Count)", label, versionStr)
	}
	return row
}

type rawbufentry struct {
	rawc chan btrdb.RawPoint
	verc chan uint64
	errc chan error
	pt   btrdb.RawPoint
	open bool
}

// rawbuffer satisfies the streambuffer interface using raw points
type rawbuffer []rawbufentry

func (rb rawbuffer) getTime(i int) int64 {
	return rb[i].pt.Time
}

func (rb rawbuffer) isOpen(i int) bool {
	return rb[i].open
}

func (rb rawbuffer) readPoint(i int) (bool, error) {
	rb[i].pt, rb[i].open = <-rb[i].rawc
	if !rb[i].open {
		err := <-rb[i].errc
		return rb[i].open, err
	}
	return rb[i].open, nil
}

func (rb rawbuffer) writePoint(i int, row []string) {
	offset := 2 + i
	row[offset] = fmt.Sprintf("%f", rb[i].pt.Value)
}

func (rb rawbuffer) writeEmptyPoint(i int, row []string) {
	offset := 2 + i
	row[offset] = ""
}

func (rb rawbuffer) getHeaderRow(labels []string, includeVersions bool) []string {
	numcols := 2 + len(rb)
	row := make([]string, numcols, numcols)
	setTimeHeaders(row)
	versionStr := ""
	for i, label := range labels {
		offset := 2 + i
		if includeVersions {
			versionStr = fmt.Sprintf(", ver. %d", <-rb[i].verc)
		}
		row[offset+0] = fmt.Sprintf("%s%s", label, versionStr)
	}
	return row
}

// MakeCSVQuery performs a CSV query, and outputs the result using the provided
// CSV writer.
func MakeCSVQuery(ctx context.Context, b *btrdb.BTrDB, q *CSVQuery, w *csv.Writer) error {
	var numstreams = len(q.Streams)
	if numstreams != len(q.Labels) {
		return fmt.Errorf("Got %d streams but %d labels", len(q.Streams), len(q.Labels))
	}

	var versions = q.Versions
	if versions == nil {
		versions = make([]uint64, numstreams, numstreams)
	}

	switch q.QueryType {
	case AlignedWindowsQuery:
		var sq stabuffer = make([]stabufentry, numstreams, numstreams)
		for i, s := range q.Streams {
			sq[i].stac, sq[i].verc, sq[i].errc = s.AlignedWindows(ctx, q.StartTime, q.EndTime, q.Depth, versions[i])
		}
		return createCSV(sq, q, w, true)
	case WindowsQuery:
		var sq stabuffer = make([]stabufentry, numstreams, numstreams)
		for i, s := range q.Streams {
			sq[i].stac, sq[i].verc, sq[i].errc = s.Windows(ctx, q.StartTime, q.EndTime, q.WindowSize, q.Depth, versions[i])
		}
		return createCSV(sq, q, w, true)
	case RawQuery:
		var sq rawbuffer = make([]rawbufentry, numstreams, numstreams)
		for i, s := range q.Streams {
			sq[i].rawc, sq[i].verc, sq[i].errc = s.RawValues(ctx, q.StartTime, q.EndTime, versions[i])
		}
		return createCSV(sq, q, w, false)
	default:
		return errors.New("Invalid query type")
	}
}

func createCSV(buf streambuffer, q *CSVQuery, w *csv.Writer, statistical bool) error {
	// Buffer for the row of the CSV that we are writing
	var row = buf.getHeaderRow(q.Labels, q.IncludeVersions)

	// Write the header row
	var err = w.Write(row)
	if err != nil {
		return err
	}

	var open bool
	var numopen = len(q.Streams)
	for i := range q.Streams {
		open, err = buf.readPoint(i)
		if !open {
			numopen--
			if err != nil {
				return err
			}
		}
	}

	for numopen != 0 {
		// Compute the time of the next row
		var earliest int64 = math.MaxInt64
		for i := range q.Streams {
			if buf.isOpen(i) && buf.getTime(i) < earliest {
				earliest = buf.getTime(i)
			}
		}

		// Compute the next row
		row[0] = fmt.Sprintf("%d", earliest)
		row[1] = time.Unix(0, earliest).Format("2006-01-02 15:04:05.000000000")
		for i := range q.Streams {
			if !buf.isOpen(i) {
				continue
			} else if buf.getTime(i) == earliest {
				buf.writePoint(i, row)

				// We consumed this point, so fetch the next point
				open, err = buf.readPoint(i)
				if !open {
					numopen--
					if err != nil {
						return err
					}
				}
			} else {
				buf.writeEmptyPoint(i, row)
			}
		}

		// Emit the row
		err = w.Write(row)
		if err != nil {
			return nil
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package ics

import (
	"errors"
	"fmt"
	"time"
)

type Calendar struct {
	name         string
	description  string
	version      float64
	timezone     time.Location
	events       []Event
	eventsByDate map[string][]*Event
	eventByID    map[string]*Event
}

func NewCalendar() *Calendar {
	c := new(Calendar)
	// c.events = make([]Event)
	c.eventsByDate = make(map[string][]*Event)
	c.eventByID = make(map[string]*Event)
	return c
}

func (c *Calendar) SetName(n string) *Calendar {
	c.name = n
	return c
}

func (c *Calendar) GetName() string {
	return ""
}

func (c *Calendar) SetDesc(desc string) *Calendar {
	c.description = desc
	return c
}

func (c *Calendar) GetDesc() string {
	return ""
}

func (c *Calendar) SetVersion(ver float64) *Calendar {
	c.version = ver
	return c
}

func (c *Calendar) GetVersion() float64 {
	return c.version
}

func (c *Calendar) SetTimezone(tz time.Location) *Calendar {
	c.timezone = tz
	return c
}

func (c *Calendar) GetTimezone() time.Location {
	return c.timezone
}

//  add event to the calendar
func (c *Calendar) SetEvent(event Event) (*Calendar, error) {
	//  lock so that the events array doesn't change its size from other goruote
	mutex.Lock()

	// reference to the calendar
	if event.GetCalendar() == nil || event.GetCalendar() != c {
		event.SetCalendar(c)
	}
	// add the event to the main array with events
	c.events = append(c.events, event)

	// pointer to the added event in the main array
	eventPtr := &c.events[len(c.events)-1]

	// calculate the start day of the event
	eventStartTime := event.GetStart()
	tz := c.GetTimezone()
	eventDate := time.Date(eventStartTime.Year(), eventStartTime.Month(), eventStartTime.Day(), 0, 0, 0, 0, &tz)

	// faster search by date
	c.eventsByDate[eventDate.Format(YmdHis)] = append(c.eventsByDate[eventDate.Format(YmdHis)], eventPtr)
	// faster search by id
	c.eventByID[event.GetID()] = eventPtr

	mutex.Unlock()
	return c, nil
}

//  get event by id
func (c *Calendar) GetEventByID(eventID string) (*Event, error) {
	event, ok := c.eventByID[eventID]
	if ok {
		return event, nil
	}
	return nil, errors.New(fmt.Sprintf("There is no event with id %s", eventID))
}

//  get all events in the calendar
func (c *Calendar) GetEvents() []Event {
	return c.events
}

//  get all events in the calendar ordered by date
func (c *Calendar) GetEventsByDates() map[string][]*Event {
	return c.eventsByDate
}

// get all events for specified date
func (c *Calendar) GetEventsByDate(dateTime time.Time) ([]*Event, error) {
	tz := c.GetTimezone()
	day := time.Date(dateTime.Year(), dateTime.Month(), dateTime.Day(), 0, 0, 0, 0, &tz)
	events, ok := c.eventsByDate[day.Format(YmdHis)]
	if ok {
		return events, nil
	}
	return nil, errors.New(fmt.Sprintf("There are no events for the day %s", day.Format(YmdHis)))
}
func (c *Calendar) String() string {
	eventsCount := len(c.GetEvents())
	name := c.GetName()
	desc := c.GetDesc()
	return fmt.Sprintf("Calendar %s about %s has %d events", name, desc, eventsCount)
}
<commit_msg>Fixed geters for name and descr<commit_after>package ics

import (
	"errors"
	"fmt"
	"time"
)

type Calendar struct {
	name         string
	description  string
	version      float64
	timezone     time.Location
	events       []Event
	eventsByDate map[string][]*Event
	eventByID    map[string]*Event
}

func NewCalendar() *Calendar {
	c := new(Calendar)
	// c.events = make([]Event)
	c.eventsByDate = make(map[string][]*Event)
	c.eventByID = make(map[string]*Event)
	return c
}

func (c *Calendar) SetName(n string) *Calendar {
	c.name = n
	return c
}

func (c *Calendar) GetName() string {
	return c.name
}

func (c *Calendar) SetDesc(desc string) *Calendar {
	c.description = desc
	return c
}

func (c *Calendar) GetDesc() string {
	return c.description
}

func (c *Calendar) SetVersion(ver float64) *Calendar {
	c.version = ver
	return c
}

func (c *Calendar) GetVersion() float64 {
	return c.version
}

func (c *Calendar) SetTimezone(tz time.Location) *Calendar {
	c.timezone = tz
	return c
}

func (c *Calendar) GetTimezone() time.Location {
	return c.timezone
}

//  add event to the calendar
func (c *Calendar) SetEvent(event Event) (*Calendar, error) {
	//  lock so that the events array doesn't change its size from other goruote
	mutex.Lock()

	// reference to the calendar
	if event.GetCalendar() == nil || event.GetCalendar() != c {
		event.SetCalendar(c)
	}
	// add the event to the main array with events
	c.events = append(c.events, event)

	// pointer to the added event in the main array
	eventPtr := &c.events[len(c.events)-1]

	// calculate the start day of the event
	eventStartTime := event.GetStart()
	tz := c.GetTimezone()
	eventDate := time.Date(eventStartTime.Year(), eventStartTime.Month(), eventStartTime.Day(), 0, 0, 0, 0, &tz)

	// faster search by date
	c.eventsByDate[eventDate.Format(YmdHis)] = append(c.eventsByDate[eventDate.Format(YmdHis)], eventPtr)
	// faster search by id
	c.eventByID[event.GetID()] = eventPtr

	mutex.Unlock()
	return c, nil
}

//  get event by id
func (c *Calendar) GetEventByID(eventID string) (*Event, error) {
	event, ok := c.eventByID[eventID]
	if ok {
		return event, nil
	}
	return nil, errors.New(fmt.Sprintf("There is no event with id %s", eventID))
}

//  get all events in the calendar
func (c *Calendar) GetEvents() []Event {
	return c.events
}

//  get all events in the calendar ordered by date
func (c *Calendar) GetEventsByDates() map[string][]*Event {
	return c.eventsByDate
}

// get all events for specified date
func (c *Calendar) GetEventsByDate(dateTime time.Time) ([]*Event, error) {
	tz := c.GetTimezone()
	day := time.Date(dateTime.Year(), dateTime.Month(), dateTime.Day(), 0, 0, 0, 0, &tz)
	events, ok := c.eventsByDate[day.Format(YmdHis)]
	if ok {
		return events, nil
	}
	return nil, errors.New(fmt.Sprintf("There are no events for the day %s", day.Format(YmdHis)))
}
func (c *Calendar) String() string {
	eventsCount := len(c.GetEvents())
	name := c.GetName()
	desc := c.GetDesc()
	return fmt.Sprintf("Calendar %s about %s has %d events", name, desc, eventsCount)
}
<|endoftext|>


<commit_before>package api

// CertificateTypeClient indicates a client certificate type.
const CertificateTypeClient = "client"

// CertificateTypeServer indicates a server certificate type.
const CertificateTypeServer = "server"

// CertificateTypeUnknown indicates an unknown certificate type.
const CertificateTypeUnknown = "unknown"

// CertificatesPost represents the fields of a new LXD certificate
//
// swagger:model
type CertificatesPost struct {
	CertificatePut `yaml:",inline"`

	// The certificate itself, as PEM encoded X509
	// Example: X509 PEM certificate
	Certificate string `json:"certificate" yaml:"certificate"`

	// Server trust password (used to add an untrusted client)
	// Example: blah
	Password string `json:"password" yaml:"password"`
}

// CertificatePut represents the modifiable fields of a LXD certificate
//
// swagger:model
//
// API extension: certificate_update
type CertificatePut struct {
	// Name associated with the certificate
	// Example: castiana
	Name string `json:"name" yaml:"name"`

	// Usage type for the certificate (only client currently)
	// Example: client
	Type string `json:"type" yaml:"type"`

	// Whether to limit the certificate to listed projects
	// Example: true
	//
	// API extension: certificate_project
	Restricted bool `json:"restricted" yaml:"restricted"`

	// List of allowed projects (applies when restricted)
	// Example: ["default", "foo", "bar"]
	//
	// API extension: certificate_project
	Projects []string `json:"projects" yaml:"projects"`
}

// Certificate represents a LXD certificate
//
// swagger:model
type Certificate struct {
	CertificatePut `yaml:",inline"`

	// The certificate itself, as PEM encoded X509
	// Read only: true
	// Example: X509 PEM certificate
	Certificate string `json:"certificate" yaml:"certificate"`

	// SHA256 fingerprint of the certificate
	// Read only: true
	// Example: fd200419b271f1dc2a5591b693cc5774b7f234e1ff8c6b78ad703b6888fe2b69
	Fingerprint string `json:"fingerprint" yaml:"fingerprint"`
}

// Writable converts a full Certificate struct into a CertificatePut struct (filters read-only fields)
func (cert *Certificate) Writable() CertificatePut {
	return cert.CertificatePut
}
<commit_msg>shared/api: Add CertificateTypeMetrics<commit_after>package api

// CertificateTypeClient indicates a client certificate type.
const CertificateTypeClient = "client"

// CertificateTypeServer indicates a server certificate type.
const CertificateTypeServer = "server"

// CertificateTypeMetrics indicates a metrics certificate type.
const CertificateTypeMetrics = "metrics"

// CertificateTypeUnknown indicates an unknown certificate type.
const CertificateTypeUnknown = "unknown"

// CertificatesPost represents the fields of a new LXD certificate
//
// swagger:model
type CertificatesPost struct {
	CertificatePut `yaml:",inline"`

	// The certificate itself, as PEM encoded X509
	// Example: X509 PEM certificate
	Certificate string `json:"certificate" yaml:"certificate"`

	// Server trust password (used to add an untrusted client)
	// Example: blah
	Password string `json:"password" yaml:"password"`
}

// CertificatePut represents the modifiable fields of a LXD certificate
//
// swagger:model
//
// API extension: certificate_update
type CertificatePut struct {
	// Name associated with the certificate
	// Example: castiana
	Name string `json:"name" yaml:"name"`

	// Usage type for the certificate (only client currently)
	// Example: client
	Type string `json:"type" yaml:"type"`

	// Whether to limit the certificate to listed projects
	// Example: true
	//
	// API extension: certificate_project
	Restricted bool `json:"restricted" yaml:"restricted"`

	// List of allowed projects (applies when restricted)
	// Example: ["default", "foo", "bar"]
	//
	// API extension: certificate_project
	Projects []string `json:"projects" yaml:"projects"`
}

// Certificate represents a LXD certificate
//
// swagger:model
type Certificate struct {
	CertificatePut `yaml:",inline"`

	// The certificate itself, as PEM encoded X509
	// Read only: true
	// Example: X509 PEM certificate
	Certificate string `json:"certificate" yaml:"certificate"`

	// SHA256 fingerprint of the certificate
	// Read only: true
	// Example: fd200419b271f1dc2a5591b693cc5774b7f234e1ff8c6b78ad703b6888fe2b69
	Fingerprint string `json:"fingerprint" yaml:"fingerprint"`
}

// Writable converts a full Certificate struct into a CertificatePut struct (filters read-only fields)
func (cert *Certificate) Writable() CertificatePut {
	return cert.CertificatePut
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"github.com/jeffpierce/cassabon/api"
	"github.com/jeffpierce/cassabon/config"
	"github.com/jeffpierce/cassabon/datastore"
	"github.com/jeffpierce/cassabon/listener"
	"github.com/jeffpierce/cassabon/logging"
)

func main() {

	// The name of the YAML configuration file.
	var confFile string

	// Get options provided on the command line.
	flag.StringVar(&confFile, "conf", "config/cassabon.yaml", "Location of YAML configuration file.")
	flag.Parse()

	// Read the configuration file from disk.
	// This will PANIC ON ERRORS, because the logger is not yet available.
	if err := config.ReadConfigurationFile(confFile); err != nil {
		panic(err)
	}
	// Populate the global config with values used only once.
	config.LoadStartupValues()

	// Set up logging.
	sev, errLogLevel := logging.TextToSeverity(config.G.Log.Loglevel)
	if config.G.Log.Logdir != "" {
		logDir, _ := filepath.Abs(config.G.Log.Logdir)
		config.G.Log.System = logging.NewLogger("system", filepath.Join(logDir, "cassabon.system.log"), sev)
		config.G.Log.Carbon = logging.NewLogger("carbon", filepath.Join(logDir, "cassabon.carbon.log"), sev)
		config.G.Log.API = logging.NewLogger("api", filepath.Join(logDir, "cassabon.api.log"), logging.Unclassified)
	} else {
		config.G.Log.System = logging.NewLogger("system", "", sev)
		config.G.Log.Carbon = logging.NewLogger("carbon", "", sev)
		config.G.Log.API = logging.NewLogger("api", "", logging.Unclassified)
	}
	defer config.G.Log.System.Close()
	defer config.G.Log.Carbon.Close()
	defer config.G.Log.API.Close()

	// Announce the application startup in the logs.
	config.G.Log.System.LogInfo("Startup in progress")
	if errLogLevel != nil {
		config.G.Log.System.LogWarn("Bad command line argument: %v", errLogLevel)
	}

	// Now that we have a logger to report warnings, populate the remainder of the global config.
	config.G.Log.System.LogInfo("Reading configuration file %s", confFile)
	config.LoadRefreshableValues()
	config.LoadRollups()

	// Set up reload and termination signal handlers.
	var sighup = make(chan os.Signal, 1)
	signal.Notify(sighup, syscall.SIGHUP)
	var sigterm = make(chan os.Signal, 1)
	signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM)

	// Set up stats reporting.
	if config.G.Statsd.Host != "" {
		if err := logging.Statsd.Open(config.G.Statsd.Host, config.G.Statsd.Port, "cassabon"); err != nil {
			config.G.Log.System.LogError("Not reporting to statsd: %v", err)
		} else {
			config.G.Log.System.LogInfo("Reporting to statsd at %s:%s", config.G.Statsd.Host, config.G.Statsd.Port)
		}
	} else {
		logging.Statsd.Open("", "", "cassabon")
		config.G.Log.System.LogInfo("Not reporting to statsd: specify host or IP to enable")
	}
	defer logging.Statsd.Close()

	// Create all the inter-process communication channels.
	config.G.OnPeerChange = make(chan struct{}, 1)
	config.G.OnPeerChangeReq = make(chan struct{}, 1)
	config.G.OnPeerChangeRsp = make(chan struct{}, 1)
	config.G.OnExit = make(chan struct{}, 1)
	config.G.Channels.DataStore = make(chan config.CarbonMetric, config.G.Channels.DataStoreChanLen)
	config.G.Channels.IndexStore = make(chan config.CarbonMetric, config.G.Channels.IndexStoreChanLen)
	config.G.Channels.Gopher = make(chan config.IndexQuery, config.G.Channels.GopherChanLen)

	// Create and initialize the internal modules.
	storeManager := new(datastore.StoreManager)
	statIndexer := new(datastore.MetricsIndexer)
	statGopher := new(datastore.StatPathGopher)
	carbonListener := new(listener.CarbonPlaintextListener)
	storeManager.Init()
	statIndexer.Init()
	statGopher.Init()
	carbonListener.Init()

	// Repeat until terminated by SIGINT/SIGTERM.
	configIsStale := false
	repeat := true
	for repeat {

		// Perform initialization that is repeated on every SIGHUP.
		config.G.OnReload1 = make(chan struct{}, 1)
		config.G.OnReload2 = make(chan struct{}, 1)

		// Re-read the configuration to get any updated values.
		if configIsStale {
			config.G.Log.System.LogInfo("Reading configuration file %s", confFile)
			if err := config.ReadConfigurationFile(confFile); err != nil {
				config.G.Log.System.LogError("Unable to load configuration: %v", err)
			} else {
				config.LoadRefreshableValues()
				sev, _ := logging.TextToSeverity(config.G.Log.Loglevel)
				config.G.Log.System.SetLogLevel(sev)
			}
		}

		// Start the internal modules, Carbon listener last.
		storeManager.Start()
		statIndexer.Start()
		statGopher.Start()
		carbonListener.Start()

		// Start Cassabon Web API
		api := new(api.CassabonAPI)
		api.Start()
		config.G.Log.System.LogInfo("Initialization complete")

		// Wait for receipt of a recognized signal.
		select {

		case <-config.G.OnPeerChange:
			config.G.Log.System.LogInfo("Received OnPeerChange")
			api.Stop()                  // Notify API to stop
			close(config.G.OnReload1)   // Notify all externally-listening goroutines to exit
			config.G.OnReload1WG.Wait() // Wait for them to exit
			close(config.G.OnReload2)   // Notify all reloadable goroutines to exit
			config.G.OnReload2WG.Wait() // Wait for them to exit

		case <-sighup:
			config.G.Log.System.LogInfo("Received SIGHUP")
			configIsStale = true
			api.Stop()                  // Notify API to stop
			close(config.G.OnReload1)   // Notify all externally-listening goroutines to exit
			config.G.OnReload1WG.Wait() // Wait for them to exit
			close(config.G.OnReload2)   // Notify all reloadable goroutines to exit
			config.G.OnReload2WG.Wait() // Wait for them to exit
			logging.Reopen()

		case <-sigterm:
			config.G.Log.System.LogInfo("Received SIGINT/SIGTERM, preparing to terminate")
			api.Stop()                  // Notify API to stop
			close(config.G.OnReload1)   // Notify all externally-listening goroutines to exit
			config.G.OnReload1WG.Wait() // Wait for them to exit
			close(config.G.OnReload2)   // Notify all reloadable goroutines to exit
			config.G.OnReload2WG.Wait() // Wait for them to exit
			close(config.G.OnExit)      // Notify all persistent goroutines to exit
			config.G.OnExitWG.Wait()    // Wait for them to exit
			repeat = false

		}
	}

	// Final cleanup.
	config.G.Log.System.LogInfo("Termination complete")
}
<commit_msg>Recover cleanly from panics with a message to stderr<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"github.com/jeffpierce/cassabon/api"
	"github.com/jeffpierce/cassabon/config"
	"github.com/jeffpierce/cassabon/datastore"
	"github.com/jeffpierce/cassabon/listener"
	"github.com/jeffpierce/cassabon/logging"
)

func main() {

	// Recover cleanly from panics with a message to stderr.
	defer func() {
		if err := recover(); err != nil {
			fmt.Fprintf(os.Stderr, "ABORT: %v\n", err)
		}
	}()

	// The name of the YAML configuration file.
	var confFile string

	// Get options provided on the command line.
	flag.StringVar(&confFile, "conf", "config/cassabon.yaml", "Location of YAML configuration file.")
	flag.Parse()

	// Read the configuration file from disk.
	if err := config.ReadConfigurationFile(confFile); err != nil {
		panic(fmt.Errorf("Unable to load configuration: %v", err))
	}
	// Populate the global config with values used only once.
	config.LoadStartupValues()

	// Set up logging.
	sev, errLogLevel := logging.TextToSeverity(config.G.Log.Loglevel)
	if config.G.Log.Logdir != "" {
		logDir, _ := filepath.Abs(config.G.Log.Logdir)
		config.G.Log.System = logging.NewLogger("system", filepath.Join(logDir, "cassabon.system.log"), sev)
		config.G.Log.Carbon = logging.NewLogger("carbon", filepath.Join(logDir, "cassabon.carbon.log"), sev)
		config.G.Log.API = logging.NewLogger("api", filepath.Join(logDir, "cassabon.api.log"), logging.Unclassified)
	} else {
		config.G.Log.System = logging.NewLogger("system", "", sev)
		config.G.Log.Carbon = logging.NewLogger("carbon", "", sev)
		config.G.Log.API = logging.NewLogger("api", "", logging.Unclassified)
	}
	defer config.G.Log.System.Close()
	defer config.G.Log.Carbon.Close()
	defer config.G.Log.API.Close()

	// Announce the application startup in the logs.
	config.G.Log.System.LogInfo("Startup in progress")
	if errLogLevel != nil {
		config.G.Log.System.LogWarn("Bad command line argument: %v", errLogLevel)
	}

	// Now that we have a logger to report warnings, populate the remainder of the global config.
	config.G.Log.System.LogInfo("Reading configuration file %s", confFile)
	config.LoadRefreshableValues()
	config.LoadRollups()

	// Set up reload and termination signal handlers.
	var sighup = make(chan os.Signal, 1)
	signal.Notify(sighup, syscall.SIGHUP)
	var sigterm = make(chan os.Signal, 1)
	signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM)

	// Set up stats reporting.
	if config.G.Statsd.Host != "" {
		if err := logging.Statsd.Open(config.G.Statsd.Host, config.G.Statsd.Port, "cassabon"); err != nil {
			config.G.Log.System.LogError("Not reporting to statsd: %v", err)
		} else {
			config.G.Log.System.LogInfo("Reporting to statsd at %s:%s", config.G.Statsd.Host, config.G.Statsd.Port)
		}
	} else {
		logging.Statsd.Open("", "", "cassabon")
		config.G.Log.System.LogInfo("Not reporting to statsd: specify host or IP to enable")
	}
	defer logging.Statsd.Close()

	// Create all the inter-process communication channels.
	config.G.OnPeerChange = make(chan struct{}, 1)
	config.G.OnPeerChangeReq = make(chan struct{}, 1)
	config.G.OnPeerChangeRsp = make(chan struct{}, 1)
	config.G.OnExit = make(chan struct{}, 1)
	config.G.Channels.DataStore = make(chan config.CarbonMetric, config.G.Channels.DataStoreChanLen)
	config.G.Channels.IndexStore = make(chan config.CarbonMetric, config.G.Channels.IndexStoreChanLen)
	config.G.Channels.Gopher = make(chan config.IndexQuery, config.G.Channels.GopherChanLen)

	// Create and initialize the internal modules.
	storeManager := new(datastore.StoreManager)
	statIndexer := new(datastore.MetricsIndexer)
	statGopher := new(datastore.StatPathGopher)
	carbonListener := new(listener.CarbonPlaintextListener)
	storeManager.Init()
	statIndexer.Init()
	statGopher.Init()
	carbonListener.Init()

	// Repeat until terminated by SIGINT/SIGTERM.
	configIsStale := false
	repeat := true
	for repeat {

		// Perform initialization that is repeated on every SIGHUP.
		config.G.OnReload1 = make(chan struct{}, 1)
		config.G.OnReload2 = make(chan struct{}, 1)

		// Re-read the configuration to get any updated values.
		if configIsStale {
			config.G.Log.System.LogInfo("Reading configuration file %s", confFile)
			if err := config.ReadConfigurationFile(confFile); err != nil {
				config.G.Log.System.LogError("Unable to load configuration: %v", err)
			} else {
				config.LoadRefreshableValues()
				sev, _ := logging.TextToSeverity(config.G.Log.Loglevel)
				config.G.Log.System.SetLogLevel(sev)
			}
		}

		// Start the internal modules, Carbon listener last.
		storeManager.Start()
		statIndexer.Start()
		statGopher.Start()
		carbonListener.Start()

		// Start Cassabon Web API
		api := new(api.CassabonAPI)
		api.Start()
		config.G.Log.System.LogInfo("Initialization complete")

		// Wait for receipt of a recognized signal.
		select {

		case <-config.G.OnPeerChange:
			config.G.Log.System.LogInfo("Received OnPeerChange")
			api.Stop()                  // Notify API to stop
			close(config.G.OnReload1)   // Notify all externally-listening goroutines to exit
			config.G.OnReload1WG.Wait() // Wait for them to exit
			close(config.G.OnReload2)   // Notify all reloadable goroutines to exit
			config.G.OnReload2WG.Wait() // Wait for them to exit

		case <-sighup:
			config.G.Log.System.LogInfo("Received SIGHUP")
			configIsStale = true
			api.Stop()                  // Notify API to stop
			close(config.G.OnReload1)   // Notify all externally-listening goroutines to exit
			config.G.OnReload1WG.Wait() // Wait for them to exit
			close(config.G.OnReload2)   // Notify all reloadable goroutines to exit
			config.G.OnReload2WG.Wait() // Wait for them to exit
			logging.Reopen()

		case <-sigterm:
			config.G.Log.System.LogInfo("Received SIGINT/SIGTERM, preparing to terminate")
			api.Stop()                  // Notify API to stop
			close(config.G.OnReload1)   // Notify all externally-listening goroutines to exit
			config.G.OnReload1WG.Wait() // Wait for them to exit
			close(config.G.OnReload2)   // Notify all reloadable goroutines to exit
			config.G.OnReload2WG.Wait() // Wait for them to exit
			close(config.G.OnExit)      // Notify all persistent goroutines to exit
			config.G.OnExitWG.Wait()    // Wait for them to exit
			repeat = false

		}
	}

	// Final cleanup.
	config.G.Log.System.LogInfo("Termination complete")
}
<|endoftext|>


<commit_before>package errors

import (
	. "launchpad.net/gocheck"
)

// TESTS ==========================================================================================

func (s *S) TestNew(c *C) {

	err := New("this is %s", "nice!")
	c.Assert(err, NotNil)
	c.Assert(err.Error(), Equals, "this is nice!")
}
<commit_msg>minor formatting & style changes<commit_after>package errors

import (
	. "101loops/bdd"
)

var _ = Describe("Utility", func() {

	It("return image url", func() {
		err := New("this is %s", "nice!")
		Check(err, IsNil)
		Check(err.Error(), Equals, "this is nice!")
	})
})
<|endoftext|>


<commit_before>// Copyright 2018 The Ebiten 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 audio

import (
	"io"
	"io/ioutil"
	"sync"

	"github.com/hajimehoshi/oto/v2"
)

type (
	dummyContext struct{}
	dummyPlayer  struct {
		r       io.Reader
		playing bool
		volume  float64
		m       sync.Mutex
	}
)

func (c *dummyContext) NewPlayer(r io.Reader) oto.Player {
	return &dummyPlayer{
		r:      r,
		volume: 1,
	}
}

func (c *dummyContext) MaxBufferSize() int {
	return 48000 * channelNum * bitDepthInBytes / 4
}

func (c *dummyContext) Suspend() error {
	return nil
}

func (c *dummyContext) Resume() error {
	return nil
}

func (p *dummyPlayer) Pause() {
	p.m.Lock()
	p.playing = false
	p.m.Unlock()
}

func (p *dummyPlayer) Play() {
	p.m.Lock()
	p.playing = true
	p.m.Unlock()
	go func() {
		if _, err := ioutil.ReadAll(p.r); err != nil {
			panic(err)
		}
		p.m.Lock()
		p.playing = false
		p.m.Unlock()
	}()
}

func (p *dummyPlayer) IsPlaying() bool {
	p.m.Lock()
	defer p.m.Unlock()
	return p.playing
}

func (p *dummyPlayer) Reset() {
	p.m.Lock()
	defer p.m.Unlock()
	p.playing = false
}

func (p *dummyPlayer) Volume() float64 {
	return p.volume
}

func (p *dummyPlayer) SetVolume(volume float64) {
	p.volume = volume
}

func (p *dummyPlayer) UnplayedBufferSize() int {
	return 0
}

func (p *dummyPlayer) Err() error {
	return nil
}

func (p *dummyPlayer) Close() error {
	p.m.Lock()
	defer p.m.Unlock()
	p.playing = false
	return nil
}

func init() {
	driverForTesting = &dummyContext{}
}

type dummyHook struct {
	updates []func() error
}

func (h *dummyHook) OnSuspendAudio(f func() error) {
}

func (h *dummyHook) OnResumeAudio(f func() error) {
}

func (h *dummyHook) AppendHookOnBeforeUpdate(f func() error) {
	h.updates = append(h.updates, f)
}

func init() {
	hookForTesting = &dummyHook{}
}

func UpdateForTesting() error {
	for _, f := range hookForTesting.(*dummyHook).updates {
		if err := f(); err != nil {
			return err
		}
	}
	return nil
}

func PlayersNumForTesting() int {
	c := CurrentContext()
	c.m.Lock()
	n := len(c.players)
	c.m.Unlock()
	return n
}

func ResetContextForTesting() {
	theContext = nil
}
<commit_msg>audio: Bug fix: Compile error<commit_after>// Copyright 2018 The Ebiten 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 audio

import (
	"io"
	"io/ioutil"
	"sync"

	"github.com/hajimehoshi/oto/v2"
)

type (
	dummyContext struct{}
	dummyPlayer  struct {
		r       io.Reader
		playing bool
		volume  float64
		m       sync.Mutex
	}
)

func (c *dummyContext) NewPlayer(r io.Reader) oto.Player {
	return &dummyPlayer{
		r:      r,
		volume: 1,
	}
}

func (c *dummyContext) MaxBufferSize() int {
	return 48000 * channelNum * bitDepthInBytes / 4
}

func (c *dummyContext) Suspend() error {
	return nil
}

func (c *dummyContext) Resume() error {
	return nil
}

func (c *dummyContext) Err() error {
	return nil
}

func (p *dummyPlayer) Pause() {
	p.m.Lock()
	p.playing = false
	p.m.Unlock()
}

func (p *dummyPlayer) Play() {
	p.m.Lock()
	p.playing = true
	p.m.Unlock()
	go func() {
		if _, err := ioutil.ReadAll(p.r); err != nil {
			panic(err)
		}
		p.m.Lock()
		p.playing = false
		p.m.Unlock()
	}()
}

func (p *dummyPlayer) IsPlaying() bool {
	p.m.Lock()
	defer p.m.Unlock()
	return p.playing
}

func (p *dummyPlayer) Reset() {
	p.m.Lock()
	defer p.m.Unlock()
	p.playing = false
}

func (p *dummyPlayer) Volume() float64 {
	return p.volume
}

func (p *dummyPlayer) SetVolume(volume float64) {
	p.volume = volume
}

func (p *dummyPlayer) UnplayedBufferSize() int {
	return 0
}

func (p *dummyPlayer) Err() error {
	return nil
}

func (p *dummyPlayer) Close() error {
	p.m.Lock()
	defer p.m.Unlock()
	p.playing = false
	return nil
}

func init() {
	driverForTesting = &dummyContext{}
}

type dummyHook struct {
	updates []func() error
}

func (h *dummyHook) OnSuspendAudio(f func() error) {
}

func (h *dummyHook) OnResumeAudio(f func() error) {
}

func (h *dummyHook) AppendHookOnBeforeUpdate(f func() error) {
	h.updates = append(h.updates, f)
}

func init() {
	hookForTesting = &dummyHook{}
}

func UpdateForTesting() error {
	for _, f := range hookForTesting.(*dummyHook).updates {
		if err := f(); err != nil {
			return err
		}
	}
	return nil
}

func PlayersNumForTesting() int {
	c := CurrentContext()
	c.m.Lock()
	n := len(c.players)
	c.m.Unlock()
	return n
}

func ResetContextForTesting() {
	theContext = nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gcs

import (
	"crypto/md5"
	"fmt"
	"io"
)

// A request to create an object, accepted by Bucket.CreateObject.
type CreateObjectRequest struct {
	// The name with which to create the object. This field must be set.
	//
	// Object names must:
	//
	// *  be non-empty.
	// *  be no longer than 1024 bytes.
	// *  be valid UTF-8.
	// *  not contain the code point U+000A (line feed).
	// *  not contain the code point U+000D (carriage return).
	//
	// See here for authoritative documentation:
	//     https://cloud.google.com/storage/docs/bucket-naming#objectnames
	Name string

	// Optional information with which to create the object. See here for more
	// information:
	//
	//     https://cloud.google.com/storage/docs/json_api/v1/objects#resource
	//
	ContentType     string
	ContentLanguage string
	ContentEncoding string
	CacheControl    string
	Metadata        map[string]string

	// A reader from which to obtain the contents of the object. Must be non-nil.
	Contents io.Reader

	// If non-nil, the object will not be created if the checksum of the received
	// contents does not match the supplied value.
	CRC32C *uint32

	// If non-nil, the object will not be created if the MD5 sum of the received
	// contents does not match the supplied value.
	MD5 *[md5.Size]byte

	// If non-nil, the object will be created/overwritten only if the current
	// generation for the object name is equal to the given value. Zero means the
	// object does not exist.
	GenerationPrecondition *int64
}

// A request to copy an object to a new name, preserving all metadata.
type CopyObjectRequest struct {
	SrcName string
	DstName string

	// The generation of the source object to copy, or zero for the latest
	// generation.
	SrcGeneration int64
}

// The maximum number of sources that a ComposeObjectsRequest may contain.
//
// Cf. https://cloud.google.com/storage/docs/composite-objects#_Compose
const MaxSourcesPerComposeRequest = 32

// The maximum number of components that a composite object may have. The sum
// of the component counts of the sources in a ComposeObjectsRequest must be no
// more than this value.
//
// Cf. https://cloud.google.com/storage/docs/composite-objects#_Count
const MaxComponentCount = 1024

// A request to compose one or more objects into a single composite object.
type ComposeObjectsRequest struct {
	// The name of the destination composite object.
	DstName string

	// If non-nil, the destination object will be created/overwritten only if the
	// current generation for its name is equal to the given value. Zero means
	// the object does not exist.
	//
	// Make sure to see the notes on MaxSourcesPerComposeRequest and
	// MaxComponentCount.
	DstGenerationPrecondition *int64

	// The source objects from which to compose. This must be non-empty.
	Sources []ComposeSource

	// Optional information with which to create the object. See here for more
	// information:
	//
	//     https://cloud.google.com/storage/docs/json_api/v1/objects#resource
	//
	Metadata map[string]string
}

type ComposeSource struct {
	// The name of the source object.
	Name string

	// The generation of the source object to compose from. Zero means the latest
	// generation.
	Generation int64
}

// A [start, limit) range of bytes within an object.
//
// Semantics:
//
//  *  If Limit is less than or equal to Start, the range is treated as empty.
//
//  *  If Limit is greater than the length of the object, the range is
//     implicitly truncated.
//
type ByteRange struct {
	Start uint64
	Limit uint64
}

func (br ByteRange) String() string {
	return fmt.Sprintf("[%d, %d)", br.Start, br.Limit)
}

// A request to read the contents of an object at a particular generation.
type ReadObjectRequest struct {
	// The name of the object to read.
	Name string

	// The generation of the object to read. Zero means the latest generation.
	Generation int64

	// If present, limit the contents returned to a range within the object.
	Range *ByteRange
}

type StatObjectRequest struct {
	// The name of the object in question.
	Name string
}

type ListObjectsRequest struct {
	// List only objects whose names begin with this prefix.
	Prefix string

	// Collapse results based on a delimiter.
	//
	// If non-empty, enable the following behavior. For each run of one or more
	// objects whose names are of the form:
	//
	//     <Prefix><S><Delimiter><...>
	//
	// where <S> is a string that doesn't itself contain Delimiter and <...> is
	// anything, return a single Collaped entry in the listing consisting of
	//
	//     <Prefix><S><Delimiter>
	//
	// instead of one Object record per object. If a collapsed entry consists of
	// a large number of objects, this may be more efficient.
	Delimiter string

	// Used to continue a listing where a previous one left off. See
	// Listing.ContinuationToken for more information.
	ContinuationToken string

	// The maximum number of objects and collapsed runs to return. Fewer than
	// this number may actually be returned. If this is zero, a sensible default
	// is used.
	MaxResults int
}

// A set of objects and delimter-based collapsed runs returned by a call to
// ListObjects. See also ListObjectsRequest.
type Listing struct {
	// Records for objects matching the listing criteria.
	//
	// Guaranteed to be strictly increasing under a lexicographical comparison on
	// (name, generation) pairs.
	Objects []*Object

	// Collapsed entries for runs of names sharing a prefix followed by a
	// delimiter. See notes on ListObjectsRequest.Delimiter.
	//
	// Guaranteed to be strictly increasing.
	CollapsedRuns []string

	// A continuation token, for fetching more results.
	//
	// If non-empty, this listing does not represent the full set of matching
	// objects in the bucket. Call ListObjects again with the request's
	// ContinuationToken field set to this value to continue where you left off.
	//
	// Guarantees, for replies R1 and R2, with R2 continuing from R1:
	//
	//  *  All of R1's object names are strictly less than all object names and
	//     collapsed runs in R2.
	//
	//  *  All of R1's collapsed runs are strictly less than all object names and
	//     prefixes in R2.
	//
	// (Cf. Google-internal bug 19286144)
	//
	// Note that there is no guarantee of atomicity of listings. Objects written
	// and deleted concurrently with a single or multiple listing requests may or
	// may not be returned.
	ContinuationToken string
}

// A request to update the metadata of an object, accepted by
// Bucket.UpdateObject.
type UpdateObjectRequest struct {
	// The name of the object to update. Must be specified.
	Name string

	// String fields in the object to update (or not). The semantics are as
	// follows, for a given field F:
	//
	//  *  If F is set to nil, the corresponding GCS object field is untouched.
	//
	//  *  If *F is the empty string, then the corresponding GCS object field is
	//     removed.
	//
	//  *  Otherwise, the corresponding GCS object field is set to *F.
	//
	//  *  There is no facility for setting a GCS object field to the empty
	//     string, since many of the fields do not actually allow that as a legal
	//     value.
	//
	// Note that the GCS object's content type field cannot be removed.
	ContentType     *string
	ContentEncoding *string
	ContentLanguage *string
	CacheControl    *string

	// User-provided metadata updates. Keys that are not mentioned are untouched.
	// Keys whose values are nil are deleted, and others are updated to the
	// supplied string. There is no facility for completely removing user
	// metadata.
	Metadata map[string]*string
}

// A request to delete an object by name. Non-existence is not treated as an
// error.
type DeleteObjectRequest struct {
	// The name of the object to delete. Must be specified.
	Name string

	// The generation of the object to delete. Zero means the latest generation.
	Generation int64
}
<commit_msg>Added UpdateObjectRequest.Generation.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gcs

import (
	"crypto/md5"
	"fmt"
	"io"
)

// A request to create an object, accepted by Bucket.CreateObject.
type CreateObjectRequest struct {
	// The name with which to create the object. This field must be set.
	//
	// Object names must:
	//
	// *  be non-empty.
	// *  be no longer than 1024 bytes.
	// *  be valid UTF-8.
	// *  not contain the code point U+000A (line feed).
	// *  not contain the code point U+000D (carriage return).
	//
	// See here for authoritative documentation:
	//     https://cloud.google.com/storage/docs/bucket-naming#objectnames
	Name string

	// Optional information with which to create the object. See here for more
	// information:
	//
	//     https://cloud.google.com/storage/docs/json_api/v1/objects#resource
	//
	ContentType     string
	ContentLanguage string
	ContentEncoding string
	CacheControl    string
	Metadata        map[string]string

	// A reader from which to obtain the contents of the object. Must be non-nil.
	Contents io.Reader

	// If non-nil, the object will not be created if the checksum of the received
	// contents does not match the supplied value.
	CRC32C *uint32

	// If non-nil, the object will not be created if the MD5 sum of the received
	// contents does not match the supplied value.
	MD5 *[md5.Size]byte

	// If non-nil, the object will be created/overwritten only if the current
	// generation for the object name is equal to the given value. Zero means the
	// object does not exist.
	GenerationPrecondition *int64
}

// A request to copy an object to a new name, preserving all metadata.
type CopyObjectRequest struct {
	SrcName string
	DstName string

	// The generation of the source object to copy, or zero for the latest
	// generation.
	SrcGeneration int64
}

// The maximum number of sources that a ComposeObjectsRequest may contain.
//
// Cf. https://cloud.google.com/storage/docs/composite-objects#_Compose
const MaxSourcesPerComposeRequest = 32

// The maximum number of components that a composite object may have. The sum
// of the component counts of the sources in a ComposeObjectsRequest must be no
// more than this value.
//
// Cf. https://cloud.google.com/storage/docs/composite-objects#_Count
const MaxComponentCount = 1024

// A request to compose one or more objects into a single composite object.
type ComposeObjectsRequest struct {
	// The name of the destination composite object.
	DstName string

	// If non-nil, the destination object will be created/overwritten only if the
	// current generation for its name is equal to the given value. Zero means
	// the object does not exist.
	//
	// Make sure to see the notes on MaxSourcesPerComposeRequest and
	// MaxComponentCount.
	DstGenerationPrecondition *int64

	// The source objects from which to compose. This must be non-empty.
	Sources []ComposeSource

	// Optional information with which to create the object. See here for more
	// information:
	//
	//     https://cloud.google.com/storage/docs/json_api/v1/objects#resource
	//
	Metadata map[string]string
}

type ComposeSource struct {
	// The name of the source object.
	Name string

	// The generation of the source object to compose from. Zero means the latest
	// generation.
	Generation int64
}

// A [start, limit) range of bytes within an object.
//
// Semantics:
//
//  *  If Limit is less than or equal to Start, the range is treated as empty.
//
//  *  If Limit is greater than the length of the object, the range is
//     implicitly truncated.
//
type ByteRange struct {
	Start uint64
	Limit uint64
}

func (br ByteRange) String() string {
	return fmt.Sprintf("[%d, %d)", br.Start, br.Limit)
}

// A request to read the contents of an object at a particular generation.
type ReadObjectRequest struct {
	// The name of the object to read.
	Name string

	// The generation of the object to read. Zero means the latest generation.
	Generation int64

	// If present, limit the contents returned to a range within the object.
	Range *ByteRange
}

type StatObjectRequest struct {
	// The name of the object in question.
	Name string
}

type ListObjectsRequest struct {
	// List only objects whose names begin with this prefix.
	Prefix string

	// Collapse results based on a delimiter.
	//
	// If non-empty, enable the following behavior. For each run of one or more
	// objects whose names are of the form:
	//
	//     <Prefix><S><Delimiter><...>
	//
	// where <S> is a string that doesn't itself contain Delimiter and <...> is
	// anything, return a single Collaped entry in the listing consisting of
	//
	//     <Prefix><S><Delimiter>
	//
	// instead of one Object record per object. If a collapsed entry consists of
	// a large number of objects, this may be more efficient.
	Delimiter string

	// Used to continue a listing where a previous one left off. See
	// Listing.ContinuationToken for more information.
	ContinuationToken string

	// The maximum number of objects and collapsed runs to return. Fewer than
	// this number may actually be returned. If this is zero, a sensible default
	// is used.
	MaxResults int
}

// A set of objects and delimter-based collapsed runs returned by a call to
// ListObjects. See also ListObjectsRequest.
type Listing struct {
	// Records for objects matching the listing criteria.
	//
	// Guaranteed to be strictly increasing under a lexicographical comparison on
	// (name, generation) pairs.
	Objects []*Object

	// Collapsed entries for runs of names sharing a prefix followed by a
	// delimiter. See notes on ListObjectsRequest.Delimiter.
	//
	// Guaranteed to be strictly increasing.
	CollapsedRuns []string

	// A continuation token, for fetching more results.
	//
	// If non-empty, this listing does not represent the full set of matching
	// objects in the bucket. Call ListObjects again with the request's
	// ContinuationToken field set to this value to continue where you left off.
	//
	// Guarantees, for replies R1 and R2, with R2 continuing from R1:
	//
	//  *  All of R1's object names are strictly less than all object names and
	//     collapsed runs in R2.
	//
	//  *  All of R1's collapsed runs are strictly less than all object names and
	//     prefixes in R2.
	//
	// (Cf. Google-internal bug 19286144)
	//
	// Note that there is no guarantee of atomicity of listings. Objects written
	// and deleted concurrently with a single or multiple listing requests may or
	// may not be returned.
	ContinuationToken string
}

// A request to update the metadata of an object, accepted by
// Bucket.UpdateObject.
type UpdateObjectRequest struct {
	// The name of the object to update. Must be specified.
	Name string

	// The generation of the object to update. Zero means the latest generation.
	Generation int64

	// String fields in the object to update (or not). The semantics are as
	// follows, for a given field F:
	//
	//  *  If F is set to nil, the corresponding GCS object field is untouched.
	//
	//  *  If *F is the empty string, then the corresponding GCS object field is
	//     removed.
	//
	//  *  Otherwise, the corresponding GCS object field is set to *F.
	//
	//  *  There is no facility for setting a GCS object field to the empty
	//     string, since many of the fields do not actually allow that as a legal
	//     value.
	//
	// Note that the GCS object's content type field cannot be removed.
	ContentType     *string
	ContentEncoding *string
	ContentLanguage *string
	CacheControl    *string

	// User-provided metadata updates. Keys that are not mentioned are untouched.
	// Keys whose values are nil are deleted, and others are updated to the
	// supplied string. There is no facility for completely removing user
	// metadata.
	Metadata map[string]*string
}

// A request to delete an object by name. Non-existence is not treated as an
// error.
type DeleteObjectRequest struct {
	// The name of the object to delete. Must be specified.
	Name string

	// The generation of the object to delete. Zero means the latest generation.
	Generation int64
}
<|endoftext|>


<commit_before>package gen

import (
	"github.com/gernest/blackfriday"
	"github.com/robertkrimen/otto"
)

const (
	fileNameKey    = "name"
	fileMetaKey    = "meta"
	fileContentKey = "contents"
)

// markdown plugin for the otto runtime. This exposes one method exec, which
// accepts a File object as an argument. It renders the Contents of the file to
// markdown.
//
// A render method is provided just in case you want to render a piece of text
// as markdown.
func markdown() Export {
	e := make(Export)
	e.Set("exec", func(call otto.FunctionCall) otto.Value {
		v, err := call.Argument(0).Export()
		if err != nil {
			panicOtto(err.Error())
		}
		o, ok := v.(map[string]interface{})
		if !ok {
			panicOtto("markdown: wrong ragument type, expected a file object")
		}
		if c, ok := o[fileContentKey]; ok {
			if cv, cok := c.(string); cok {
				if cv != "" {
					o[fileContentKey] = blackfriday.MarkdownCommon([]byte(cv))
				}
			}
		}
		ov, _ := call.Otto.ToValue(o)
		return ov
	})
	return e
}
<commit_msg>gen: Add HTML property to markdown module<commit_after>package gen

import (
	"github.com/gernest/blackfriday"
	"github.com/robertkrimen/otto"
)

const (
	fileNameKey    = "name"
	fileMetaKey    = "meta"
	fileContentKey = "contents"
)

// markdown plugin for the otto runtime. This exposes one method exec, which
// accepts a File object as an argument. It renders the Contents of the file to
// markdown.
//
// A HTML method is provided just in case you want to render a piece of text
// as markdown.
func markdown() Export {
	e := make(Export)
	e.Set("exec", func(call otto.FunctionCall) otto.Value {
		v, err := call.Argument(0).Export()
		if err != nil {
			panicOtto(err.Error())
		}
		o, ok := v.(map[string]interface{})
		if !ok {
			panicOtto("markdown: wrong ragument type, expected a file object")
		}
		if c, ok := o[fileContentKey]; ok {
			if cv, cok := c.(string); cok {
				if cv != "" {
					o[fileContentKey] = blackfriday.MarkdownCommon([]byte(cv))
				}
			}
		}
		ov, _ := call.Otto.ToValue(o)
		return ov
	})
	e.Set("HTML", func(call otto.FunctionCall) otto.Value {
		v, err := call.Argument(0).ToString()
		if err != nil {
			panicOtto(err.Error())
		}
		v = string(blackfriday.MarkdownCommon([]byte(v)))
		ov, _ := call.Otto.ToValue(v)
		return ov
	})
	return e
}
<|endoftext|>