<commit_before>package squircy2

//go:generate go-bindata -prefix "./" -pkg squircy2 -tags "!debug" ./views/... ./public/...

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"io"
	"log"
	"net"
	"net/http"
	"strconv"
	"sync"
	"time"

	"github.com/go-martini/martini"
	_ "github.com/jteeuwen/go-bindata"
	"github.com/tyler-sommer/squircy2/config"
	"github.com/tyler-sommer/squircy2/data"
	"github.com/tyler-sommer/squircy2/event"
	"github.com/tyler-sommer/squircy2/eventsource"
	"github.com/tyler-sommer/squircy2/irc"
	"github.com/tyler-sommer/squircy2/script"
	"github.com/tyler-sommer/squircy2/webhook"
)

type Manager struct {
	*martini.ClassicMartini

	httpListener  io.Closer
	httpsListener io.Closer
}

func NewManager(rootPath string) (manager *Manager) {
	manager = &Manager{ClassicMartini: martini.Classic()}
	manager.Map(manager)
	manager.Map(manager.Injector)

	conf := config.NewConfiguration(rootPath)
	database := data.NewDatabaseConnection(conf.RootPath)
	config.LoadConfig(database, conf)
	manager.Map(conf)

	manager.invokeAndMap(event.NewEventManager)
	manager.invokeAndMap(newEventTracer)
	manager.Invoke(configureLog)

	manager.Map(database)
	manager.invokeAndMap(script.NewScriptRepository)
	manager.Map(webhook.NewWebhookRepository(database))

	manager.Invoke(configureWeb)
	manager.invokeAndMap(newEventSource)
	manager.invokeAndMap(irc.NewIrcConnectionManager)
	manager.invokeAndMap(script.NewScriptManager)

	return
}

func (manager *Manager) ListenAndServe() {
	manager.Invoke(manager.listenAndServe)
}

func (manager *Manager) StopListenAndServe() error {
	if manager.httpListener == nil {
		return nil
	}
	defer func() {
		manager.httpListener = nil
	}()
	return manager.httpListener.Close()
}

func (manager *Manager) StopListenAndServeTLS() error {
	if manager.httpsListener == nil {
		return nil
	}
	defer func() {
		manager.httpsListener = nil
	}()
	return manager.httpsListener.Close()
}

func (manager *Manager) listenAndServe(conf *config.Configuration, l *log.Logger) {
	wg := &sync.WaitGroup{}
	wg.Add(2)
	go func() {
		defer func() {
			if err, ok := recover().(error); ok {
				l.Println("Could not start HTTP: ", err)
			}
			wg.Done()
		}()
		err := listenAndServe(manager, conf, l)
		if err != nil {
			panic(err)
		}
	}()
	go func() {
		defer func() {
			if err, ok := recover().(error); ok {
				l.Println("Could not start HTTPS: ", err)
			}
			wg.Done()
		}()
		err := listenAndServeTLS(manager, conf, l)
		if err != nil {
			panic(err)
		}
	}()
	wg.Wait()
}

func (manager *Manager) AutoConnect() {
	manager.Invoke(manager.autoConnect)
}

func (manager *Manager) autoConnect(conf *config.Configuration, ircmgr *irc.ConnectionManager) {
	if conf.AutoConnect {
		ircmgr.Connect()
	}
}

func (manager *Manager) invokeAndMap(fn interface{}) interface{} {
	res, err := manager.Invoke(fn)
	if err != nil {
		panic(err)
	}

	val := res[0].Interface()
	manager.Map(val)

	return val
}

// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by ListenAndServe and ListenAndServeTLS so
// dead TCP connections (e.g. closing laptop mid-download) eventually
// go away.
//
// Taken from stdlib net/http.
type tcpKeepAliveListener struct {
	*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	tc.SetKeepAlive(true)
	tc.SetKeepAlivePeriod(3 * time.Minute)
	return tc, nil
}

func listenAndServe(manager *Manager, conf *config.Configuration, l *log.Logger) error {
	if !conf.WebInterface {
		return errors.New("Web Interface is disabled.")
	}
	l.Println("Starting HTTP, listening at", conf.HTTPHostPort)
	s := &http.Server{Addr: conf.HTTPHostPort, Handler: manager}
	listener, err := net.Listen("tcp", conf.HTTPHostPort)
	if err != nil {
		return err
	}
	go func() {
		err := s.Serve(tcpKeepAliveListener{listener.(*net.TCPListener)})
		if err != nil {
			l.Println(err.Error())
		}
	}()
	manager.httpListener = listener.(io.Closer)
	return nil
}

func listenAndServeTLS(manager *Manager, conf *config.Configuration, l *log.Logger) error {
	if !conf.WebInterface {
		return errors.New("Web Interface is disabled.")
	}
	if !conf.HTTPS {
		return errors.New("HTTPS is disabled.")
	}
	l.Println("Starting HTTPS, listening at", conf.SSLHostPort)
	s := &http.Server{Addr: conf.SSLHostPort, Handler: manager}
	var err error
	s.TLSConfig = &tls.Config{}
	s.TLSConfig.Certificates = make([]tls.Certificate, 1)
	s.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(conf.SSLCertFile, conf.SSLCertKey)
	if err != nil {
		return err
	}
	listener, err := net.Listen("tcp", conf.SSLHostPort)
	if err != nil {
		return err
	}
	go func() {
		err := s.Serve(tls.NewListener(tcpKeepAliveListener{listener.(*net.TCPListener)}, s.TLSConfig))
		if err != nil {
			l.Println(err.Error())
		}
	}()
	manager.httpsListener = listener.(io.Closer)
	return nil
}

func newEventSource(evm event.EventManager) *eventsource.Broker {
	es := eventsource.New()

	var id int = -1
	evm.Bind(event.AllEvents, func(es *eventsource.Broker, ev event.Event) {
		id++
		d, _ := json.Marshal(ev.Data)
		es.Notify(&eventsource.Message{strconv.Itoa(id), string(ev.Type), string(d)})
	})

	return es
}

type eventTracer struct {
	limit int
	data  map[event.EventType][]map[string]interface{}
}

func newEventTracer(evm event.EventManager) *eventTracer {
	t := &eventTracer{25, make(map[event.EventType][]map[string]interface{}, 0)}
	evm.Bind(event.AllEvents, func(ev event.Event) {
		history, ok := t.data[ev.Type]
		if !ok {
			history = make([]map[string]interface{}, 0)
		}

		if len(history) >= t.limit {
			history = history[1:]
		}

		history = append(history, ev.Data)

		t.data[ev.Type] = history
	})

	return t
}

func (t *eventTracer) History(evt event.EventType) []map[string]interface{} {
	if history, ok := t.data[evt]; ok {
		return history
	}

	return nil
}
<commit_msg>HTTP/2 is now used for SSL connections again.<commit_after>package squircy2

//go:generate go-bindata -prefix "./" -pkg squircy2 -tags "!debug" ./views/... ./public/...

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"io"
	"log"
	"net"
	"net/http"
	"strconv"
	"sync"
	"time"

	"github.com/go-martini/martini"
	_ "github.com/jteeuwen/go-bindata"
	"github.com/tyler-sommer/squircy2/config"
	"github.com/tyler-sommer/squircy2/data"
	"github.com/tyler-sommer/squircy2/event"
	"github.com/tyler-sommer/squircy2/eventsource"
	"github.com/tyler-sommer/squircy2/irc"
	"github.com/tyler-sommer/squircy2/script"
	"github.com/tyler-sommer/squircy2/webhook"
)

type Manager struct {
	*martini.ClassicMartini

	httpListener  io.Closer
	httpsListener io.Closer
}

func NewManager(rootPath string) (manager *Manager) {
	manager = &Manager{ClassicMartini: martini.Classic()}
	manager.Map(manager)
	manager.Map(manager.Injector)

	conf := config.NewConfiguration(rootPath)
	database := data.NewDatabaseConnection(conf.RootPath)
	config.LoadConfig(database, conf)
	manager.Map(conf)

	manager.invokeAndMap(event.NewEventManager)
	manager.invokeAndMap(newEventTracer)
	manager.Invoke(configureLog)

	manager.Map(database)
	manager.invokeAndMap(script.NewScriptRepository)
	manager.Map(webhook.NewWebhookRepository(database))

	manager.Invoke(configureWeb)
	manager.invokeAndMap(newEventSource)
	manager.invokeAndMap(irc.NewIrcConnectionManager)
	manager.invokeAndMap(script.NewScriptManager)

	return
}

func (manager *Manager) ListenAndServe() {
	manager.Invoke(manager.listenAndServe)
}

func (manager *Manager) StopListenAndServe() error {
	if manager.httpListener == nil {
		return nil
	}
	defer func() {
		manager.httpListener = nil
	}()
	return manager.httpListener.Close()
}

func (manager *Manager) StopListenAndServeTLS() error {
	if manager.httpsListener == nil {
		return nil
	}
	defer func() {
		manager.httpsListener = nil
	}()
	return manager.httpsListener.Close()
}

func (manager *Manager) listenAndServe(conf *config.Configuration, l *log.Logger) {
	wg := &sync.WaitGroup{}
	wg.Add(2)
	go func() {
		defer func() {
			if err, ok := recover().(error); ok {
				l.Println("Could not start HTTP: ", err)
			}
			wg.Done()
		}()
		err := listenAndServe(manager, conf, l)
		if err != nil {
			panic(err)
		}
	}()
	go func() {
		defer func() {
			if err, ok := recover().(error); ok {
				l.Println("Could not start HTTPS: ", err)
			}
			wg.Done()
		}()
		err := listenAndServeTLS(manager, conf, l)
		if err != nil {
			panic(err)
		}
	}()
	wg.Wait()
}

func (manager *Manager) AutoConnect() {
	manager.Invoke(manager.autoConnect)
}

func (manager *Manager) autoConnect(conf *config.Configuration, ircmgr *irc.ConnectionManager) {
	if conf.AutoConnect {
		ircmgr.Connect()
	}
}

func (manager *Manager) invokeAndMap(fn interface{}) interface{} {
	res, err := manager.Invoke(fn)
	if err != nil {
		panic(err)
	}

	val := res[0].Interface()
	manager.Map(val)

	return val
}

// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by ListenAndServe and ListenAndServeTLS so
// dead TCP connections (e.g. closing laptop mid-download) eventually
// go away.
//
// Taken from stdlib net/http.
type tcpKeepAliveListener struct {
	*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	tc.SetKeepAlive(true)
	tc.SetKeepAlivePeriod(3 * time.Minute)
	return tc, nil
}

func listenAndServe(manager *Manager, conf *config.Configuration, l *log.Logger) error {
	if !conf.WebInterface {
		return errors.New("Web Interface is disabled.")
	}
	l.Println("Starting HTTP, listening at", conf.HTTPHostPort)
	s := &http.Server{Addr: conf.HTTPHostPort, Handler: manager}
	listener, err := net.Listen("tcp", conf.HTTPHostPort)
	if err != nil {
		return err
	}
	go func() {
		err := s.Serve(tcpKeepAliveListener{listener.(*net.TCPListener)})
		if err != nil {
			l.Println(err.Error())
		}
	}()
	manager.httpListener = listener.(io.Closer)
	return nil
}

func listenAndServeTLS(manager *Manager, conf *config.Configuration, l *log.Logger) error {
	if !conf.WebInterface {
		return errors.New("Web Interface is disabled.")
	}
	if !conf.HTTPS {
		return errors.New("HTTPS is disabled.")
	}
	l.Println("Starting HTTPS, listening at", conf.SSLHostPort)
	s := &http.Server{Addr: conf.SSLHostPort, Handler: manager}
	var err error
	s.TLSConfig = &tls.Config{}
	s.TLSConfig.NextProtos = []string{"http", "h2"}
	s.TLSConfig.Certificates = make([]tls.Certificate, 1)
	s.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(conf.SSLCertFile, conf.SSLCertKey)
	if err != nil {
		return err
	}
	listener, err := net.Listen("tcp", conf.SSLHostPort)
	if err != nil {
		return err
	}
	go func() {
		err := s.Serve(tls.NewListener(tcpKeepAliveListener{listener.(*net.TCPListener)}, s.TLSConfig))
		if err != nil {
			l.Println(err.Error())
		}
	}()
	manager.httpsListener = listener.(io.Closer)
	return nil
}

func newEventSource(evm event.EventManager) *eventsource.Broker {
	es := eventsource.New()

	var id int = -1
	evm.Bind(event.AllEvents, func(es *eventsource.Broker, ev event.Event) {
		id++
		d, _ := json.Marshal(ev.Data)
		es.Notify(&eventsource.Message{strconv.Itoa(id), string(ev.Type), string(d)})
	})

	return es
}

type eventTracer struct {
	limit int
	data  map[event.EventType][]map[string]interface{}
}

func newEventTracer(evm event.EventManager) *eventTracer {
	t := &eventTracer{25, make(map[event.EventType][]map[string]interface{}, 0)}
	evm.Bind(event.AllEvents, func(ev event.Event) {
		history, ok := t.data[ev.Type]
		if !ok {
			history = make([]map[string]interface{}, 0)
		}

		if len(history) >= t.limit {
			history = history[1:]
		}

		history = append(history, ev.Data)

		t.data[ev.Type] = history
	})

	return t
}

func (t *eventTracer) History(evt event.EventType) []map[string]interface{} {
	if history, ok := t.data[evt]; ok {
		return history
	}

	return nil
}
<|endoftext|>


<commit_before>/*
package manhole provides a means to dynamically interact with registered Go
objects in a live process, using a Lua shell.

Inspiration is mainly from Twisted's library of the same name:
https://twistedmatrix.com/documents/current/api/twisted.conch.manhole.html

Example usage:

    package main

    import (
      "github.com/jtolds/go-manhole"
    )

    type MyType struct{ x int }

    func (m *MyType) Set(x int) { m.x = x }
    func (m *MyType) Get() int  { return m.x }

    func main() {
      manhole.RegisterType("MyType", MyType{})
      panic(manhole.ListenAndServe(2222))
    }

After running the above program, you can now connect via telnet or netcat
to localhost:2222, and run the following interaction:

    > x = MyType()
    > print(x:Get())
    0
    > x:Set(5)
    > print(x:Get())
    5

*/
package manhole

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

	"github.com/jtolds/gopher-luar"
	"github.com/yuin/gopher-lua"
)

var (
	reserved = map[string]bool{"quit": true, "print": true, "repr": true}
)

// Manhole is essentially a registry of Go values to expose via a remote shell.
type Manhole struct {
	mtx           sync.Mutex
	registrations map[string]func(*lua.LState)
}

func (m *Manhole) register(name string, val interface{},
	mapper func(l *lua.LState, val interface{}) lua.LValue) error {
	m.mtx.Lock()
	defer m.mtx.Unlock()
	if m.registrations == nil {
		m.registrations = map[string]func(*lua.LState){}
	}

	_, exists := m.registrations[name]
	if exists || reserved[name] {
		return fmt.Errorf("Registration %#v already exists", name)
	}
	m.registrations[name] = func(l *lua.LState) {
		l.SetGlobal(name, mapper(l, val))
	}
	return nil
}

// RegisterType registers the type with example value `example` using the
// global name `name`.
// Example:
//   m.RegisterType("MyType", MyType{})
// Applies to all future manhole sessions, but not already started ones.
func (m *Manhole) RegisterType(name string, example interface{}) error {
	return m.register(name, example, luar.NewType)
}

// RegisterVal registers the value `value` using the global name `name`.
// Example:
//   m.RegisterVal("x", x)
// Applies to all future manhole sessions, but not already started ones.
func (m *Manhole) RegisterVal(name string, value interface{}) error {
	return m.register(name, value, luar.New)
}

// Unregister removes the previously-registered global name `name` from all
// future manhole sessions.
func (m *Manhole) Unregister(name string) {
	m.mtx.Lock()
	defer m.mtx.Unlock()
	if m.registrations == nil {
		return
	}
	delete(m.registrations, name)
}

// Interact takes input from `in` and returns output to `out`. It runs until
// there is an error, or the user runs `quit()`. In the case of the input
// returning io.EOF or the user entering `quit()`, no error will be returned.
func (m *Manhole) Interact(in io.Reader, out io.Writer) error {
	l := lua.NewState(lua.Options{
		Stdout: out, Stderr: out, Stdin: in,
		SkipOpenLibs: true})
	defer l.Close()

	m.mtx.Lock()
	registrations := make([]func(l *lua.LState), 0, len(m.registrations))
	for _, reg := range m.registrations {
		registrations = append(registrations, reg)
	}
	m.mtx.Unlock()
	for _, reg := range registrations {
		reg(l)
	}

	eof := false
	l.SetGlobal("quit", luar.New(l, func() { eof = true }))
	l.SetGlobal("print", luar.New(l, func(vals ...interface{}) {
		fmt.Fprintln(out, vals...)
	}))
	l.SetGlobal("repr", luar.New(l, func(vals ...interface{}) {
		for i, val := range vals {
			fmt.Fprintf(out, "%#v", val)
			if i > 0 {
				fmt.Fprintf(out, " ")
			}
		}
		fmt.Fprintln(out)
	}))

	stdin := bufio.NewReader(in)
	for !eof {
		_, err := fmt.Fprintf(out, "> ")
		if err != nil {
			return err
		}
		line, err := stdin.ReadString('\n')
		eof = err == io.EOF
		if err != nil && !eof {
			return err
		}
		err = l.DoString(line)
		if err != nil {
			_, err = fmt.Fprintf(out, "%v\n", err)
			if err != nil {
				return err
			}
			continue
		}
	}
	return nil
}

// ListenAndServe listens on localhost with the given port. It calls Serve
// with an appropriate listener
func (m *Manhole) ListenAndServe(port int) error {
	l, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
	if err != nil {
		return err
	}
	return m.Serve(l)
}

// Serve accepts incoming connections and calls Interact with both sides of
// incoming client connections. Careful, it's probably a security mistake to
// use a listener that can accept connections from anywhere.
func (m *Manhole) Serve(l net.Listener) error {
	defer l.Close()
	var delay time.Duration
	for {
		conn, err := l.Accept()
		if err != nil {
			if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
				if delay == 0 {
					delay = 5 * time.Millisecond
				} else {
					delay *= 2
				}
				if delay > time.Second {
					delay = time.Second
				}
				time.Sleep(delay)
				continue
			}
			return err
		}
		delay = 0
		go func() {
			defer conn.Close()
			m.Interact(conn, conn)
		}()
	}
}

var (
	Default = &Manhole{}

	RegisterType   = Default.RegisterType
	RegisterVal    = Default.RegisterVal
	Unregister     = Default.Unregister
	Interact       = Default.Interact
	Serve          = Default.Serve
	ListenAndServe = Default.ListenAndServe
)
<commit_msg>update import paths<commit_after>/*
package manhole provides a means to dynamically interact with registered Go
objects in a live process, using a Lua shell.

Inspiration is mainly from Twisted's library of the same name:
https://twistedmatrix.com/documents/current/api/twisted.conch.manhole.html

Example usage:

    package main

    import (
      "github.com/jtolds/go-manhole"
    )

    type MyType struct{ x int }

    func (m *MyType) Set(x int) { m.x = x }
    func (m *MyType) Get() int  { return m.x }

    func main() {
      manhole.RegisterType("MyType", MyType{})
      panic(manhole.ListenAndServe(2222))
    }

After running the above program, you can now connect via telnet or netcat
to localhost:2222, and run the following interaction:

    > x = MyType()
    > print(x:Get())
    0
    > x:Set(5)
    > print(x:Get())
    5

*/
package manhole

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

	"github.com/layeh/gopher-luar"  // pending https://github.com/layeh/gopher-luar/pull/4
	"github.com/yuin/gopher-lua"  // pending https://github.com/yuin/gopher-lua/pull/53
)

var (
	reserved = map[string]bool{"quit": true, "print": true, "repr": true}
)

// Manhole is essentially a registry of Go values to expose via a remote shell.
type Manhole struct {
	mtx           sync.Mutex
	registrations map[string]func(*lua.LState)
}

func (m *Manhole) register(name string, val interface{},
	mapper func(l *lua.LState, val interface{}) lua.LValue) error {
	m.mtx.Lock()
	defer m.mtx.Unlock()
	if m.registrations == nil {
		m.registrations = map[string]func(*lua.LState){}
	}

	_, exists := m.registrations[name]
	if exists || reserved[name] {
		return fmt.Errorf("Registration %#v already exists", name)
	}
	m.registrations[name] = func(l *lua.LState) {
		l.SetGlobal(name, mapper(l, val))
	}
	return nil
}

// RegisterType registers the type with example value `example` using the
// global name `name`.
// Example:
//   m.RegisterType("MyType", MyType{})
// Applies to all future manhole sessions, but not already started ones.
func (m *Manhole) RegisterType(name string, example interface{}) error {
	return m.register(name, example, luar.NewType)
}

// RegisterVal registers the value `value` using the global name `name`.
// Example:
//   m.RegisterVal("x", x)
// Applies to all future manhole sessions, but not already started ones.
func (m *Manhole) RegisterVal(name string, value interface{}) error {
	return m.register(name, value, luar.New)
}

// Unregister removes the previously-registered global name `name` from all
// future manhole sessions.
func (m *Manhole) Unregister(name string) {
	m.mtx.Lock()
	defer m.mtx.Unlock()
	if m.registrations == nil {
		return
	}
	delete(m.registrations, name)
}

// Interact takes input from `in` and returns output to `out`. It runs until
// there is an error, or the user runs `quit()`. In the case of the input
// returning io.EOF or the user entering `quit()`, no error will be returned.
func (m *Manhole) Interact(in io.Reader, out io.Writer) error {
	l := lua.NewState(lua.Options{
		Stdout: out, Stderr: out, Stdin: in,
		SkipOpenLibs: true})
	defer l.Close()

	m.mtx.Lock()
	registrations := make([]func(l *lua.LState), 0, len(m.registrations))
	for _, reg := range m.registrations {
		registrations = append(registrations, reg)
	}
	m.mtx.Unlock()
	for _, reg := range registrations {
		reg(l)
	}

	eof := false
	l.SetGlobal("quit", luar.New(l, func() { eof = true }))
	l.SetGlobal("print", luar.New(l, func(vals ...interface{}) {
		fmt.Fprintln(out, vals...)
	}))
	l.SetGlobal("repr", luar.New(l, func(vals ...interface{}) {
		for i, val := range vals {
			fmt.Fprintf(out, "%#v", val)
			if i > 0 {
				fmt.Fprintf(out, " ")
			}
		}
		fmt.Fprintln(out)
	}))

	stdin := bufio.NewReader(in)
	for !eof {
		_, err := fmt.Fprintf(out, "> ")
		if err != nil {
			return err
		}
		line, err := stdin.ReadString('\n')
		eof = err == io.EOF
		if err != nil && !eof {
			return err
		}
		err = l.DoString(line)
		if err != nil {
			_, err = fmt.Fprintf(out, "%v\n", err)
			if err != nil {
				return err
			}
			continue
		}
	}
	return nil
}

// ListenAndServe listens on localhost with the given port. It calls Serve
// with an appropriate listener
func (m *Manhole) ListenAndServe(port int) error {
	l, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
	if err != nil {
		return err
	}
	return m.Serve(l)
}

// Serve accepts incoming connections and calls Interact with both sides of
// incoming client connections. Careful, it's probably a security mistake to
// use a listener that can accept connections from anywhere.
func (m *Manhole) Serve(l net.Listener) error {
	defer l.Close()
	var delay time.Duration
	for {
		conn, err := l.Accept()
		if err != nil {
			if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
				if delay == 0 {
					delay = 5 * time.Millisecond
				} else {
					delay *= 2
				}
				if delay > time.Second {
					delay = time.Second
				}
				time.Sleep(delay)
				continue
			}
			return err
		}
		delay = 0
		go func() {
			defer conn.Close()
			m.Interact(conn, conn)
		}()
	}
}

var (
	Default = &Manhole{}

	RegisterType   = Default.RegisterType
	RegisterVal    = Default.RegisterVal
	Unregister     = Default.Unregister
	Interact       = Default.Interact
	Serve          = Default.Serve
	ListenAndServe = Default.ListenAndServe
)
<|endoftext|>


<commit_before>// Copyright (c) 2021 Timo Savola. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package container

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"syscall"

	"gate.computer/gate/internal/container/child"
	"gate.computer/gate/internal/container/common"
	runtimeerrors "gate.computer/gate/internal/error/runtime"
	config "gate.computer/gate/runtime/container"
	"golang.org/x/sys/unix"
)

const magicKey = "TNGREHAGVZRPBAGNVARE"

var magicValue = runtime.Version()

func Start(controlSocket *os.File, c *config.Config, cred *NamespaceCreds) (*exec.Cmd, error) {
	executorBin, err := openExecutorBinary(c)
	if err != nil {
		return nil, err
	}
	defer executorBin.Close()

	loaderBin, err := openLoaderBinary(c)
	if err != nil {
		return nil, err
	}
	defer loaderBin.Close()

	cgroupDir, err := openDefaultCgroup(&c.Cgroup)
	if err != nil {
		return nil, err
	}
	defer cgroupDir.Close()

	cmd := &exec.Cmd{
		Path:   "/proc/self/exe", // Intercepted by the init() function below.
		Args:   []string{common.ContainerName},
		Env:    append(append([]string{}, os.Environ()...), magicKey+"="+magicValue),
		Stderr: os.Stderr,
		ExtraFiles: []*os.File{
			controlSocket,
			loaderBin,
			executorBin,
			cgroupDir,
		},
		SysProcAttr: &syscall.SysProcAttr{
			Pdeathsig: unix.SIGKILL,
		},
	}

	if c.Namespace.Disabled {
		cmd.Args = append(cmd.Args, common.ArgNamespaceDisabled)
	} else {
		if c.Namespace.SingleUID {
			cmd.Args = append(cmd.Args, common.ArgSingleUID)
		}
		setupNamespace(cmd.SysProcAttr, &c.Namespace, cred)
	}

	stdin, err := cmd.StdinPipe()
	if err != nil {
		return nil, err
	}
	defer func() {
		if stdin != nil {
			stdin.Close()
		}
	}()

	kill := true

	if err := cmd.Start(); err != nil {
		return nil, err
	}
	defer func() {
		if kill {
			cmd.Process.Kill()
			cmd.Wait()
		}
	}()

	if err := configureExecutorCgroup(cmd.Process.Pid, &c.Cgroup); err != nil {
		return nil, err
	}

	if common.Sandbox {
		procOOMScoreAdj := fmt.Sprintf("/proc/%d/oom_score_adj", cmd.Process.Pid)
		if err := os.WriteFile(procOOMScoreAdj, []byte("1000"), 0); err != nil {
			return nil, err
		}

		if !c.Namespace.Disabled && !c.Namespace.SingleUID && isNewidmap(&c.Namespace) {
			if err := configureUserNamespace(cmd.Process.Pid, &c.Namespace, cred); err != nil {
				return nil, err
			}
		}
	}

	err = stdin.Close()
	stdin = nil
	if err != nil {
		return nil, err
	}

	kill = false
	return cmd, nil
}

func init() {
	// Intercept the self-execution.
	if len(os.Args) > 0 && os.Args[0] == common.ContainerName && os.Getenv(magicKey) == magicValue {
		os.Unsetenv(magicKey)
		child.Exec()
	}
}

// Wait for process to exit.  It is requested to exit via the executor API; the
// done channel just tells us if it was expected or not.
func Wait(cmd *exec.Cmd, done <-chan struct{}) error {
	err := cmd.Wait()

	if status, ok := err.(*exec.ExitError); ok && status.Exited() {
		switch code := status.Sys().(syscall.WaitStatus).ExitStatus(); code {
		case 0:
			err = nil

		case 1:
			err = errors.New("(message should have been written to stderr)")

		default:
			err = runtimeerrors.ExecutorError(code)
		}
	}

	select {
	case <-done:
		return err

	default:
		if err == nil {
			err = errors.New("(no error code)")
		}
		return fmt.Errorf("container terminated unexpectedly: %w", err)
	}
}
<commit_msg>runtime: require explicit namespace disabling without sandbox<commit_after>// Copyright (c) 2021 Timo Savola. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package container

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"syscall"

	"gate.computer/gate/internal/container/child"
	"gate.computer/gate/internal/container/common"
	runtimeerrors "gate.computer/gate/internal/error/runtime"
	config "gate.computer/gate/runtime/container"
	"golang.org/x/sys/unix"
)

const magicKey = "TNGREHAGVZRPBAGNVARE"

var magicValue = runtime.Version()

func Start(controlSocket *os.File, c *config.Config, cred *NamespaceCreds) (*exec.Cmd, error) {
	executorBin, err := openExecutorBinary(c)
	if err != nil {
		return nil, err
	}
	defer executorBin.Close()

	loaderBin, err := openLoaderBinary(c)
	if err != nil {
		return nil, err
	}
	defer loaderBin.Close()

	cgroupDir, err := openDefaultCgroup(&c.Cgroup)
	if err != nil {
		return nil, err
	}
	defer cgroupDir.Close()

	cmd := &exec.Cmd{
		Path:   "/proc/self/exe", // Intercepted by the init() function below.
		Args:   []string{common.ContainerName},
		Env:    append(append([]string{}, os.Environ()...), magicKey+"="+magicValue),
		Stderr: os.Stderr,
		ExtraFiles: []*os.File{
			controlSocket,
			loaderBin,
			executorBin,
			cgroupDir,
		},
		SysProcAttr: &syscall.SysProcAttr{
			Pdeathsig: unix.SIGKILL,
		},
	}

	if c.Namespace.Disabled {
		cmd.Args = append(cmd.Args, common.ArgNamespaceDisabled)
	} else {
		if !common.Sandbox {
			return nil, errors.New("container namespace must be disabled when sandbox is disabled")
		}
		if c.Namespace.SingleUID {
			cmd.Args = append(cmd.Args, common.ArgSingleUID)
		}
		setupNamespace(cmd.SysProcAttr, &c.Namespace, cred)
	}

	stdin, err := cmd.StdinPipe()
	if err != nil {
		return nil, err
	}
	defer func() {
		if stdin != nil {
			stdin.Close()
		}
	}()

	kill := true

	if err := cmd.Start(); err != nil {
		return nil, err
	}
	defer func() {
		if kill {
			cmd.Process.Kill()
			cmd.Wait()
		}
	}()

	if err := configureExecutorCgroup(cmd.Process.Pid, &c.Cgroup); err != nil {
		return nil, err
	}

	if common.Sandbox {
		procOOMScoreAdj := fmt.Sprintf("/proc/%d/oom_score_adj", cmd.Process.Pid)
		if err := os.WriteFile(procOOMScoreAdj, []byte("1000"), 0); err != nil {
			return nil, err
		}

		if !c.Namespace.Disabled && !c.Namespace.SingleUID && isNewidmap(&c.Namespace) {
			if err := configureUserNamespace(cmd.Process.Pid, &c.Namespace, cred); err != nil {
				return nil, err
			}
		}
	}

	err = stdin.Close()
	stdin = nil
	if err != nil {
		return nil, err
	}

	kill = false
	return cmd, nil
}

func init() {
	// Intercept the self-execution.
	if len(os.Args) > 0 && os.Args[0] == common.ContainerName && os.Getenv(magicKey) == magicValue {
		os.Unsetenv(magicKey)
		child.Exec()
	}
}

// Wait for process to exit.  It is requested to exit via the executor API; the
// done channel just tells us if it was expected or not.
func Wait(cmd *exec.Cmd, done <-chan struct{}) error {
	err := cmd.Wait()

	if status, ok := err.(*exec.ExitError); ok && status.Exited() {
		switch code := status.Sys().(syscall.WaitStatus).ExitStatus(); code {
		case 0:
			err = nil

		case 1:
			err = errors.New("(message should have been written to stderr)")

		default:
			err = runtimeerrors.ExecutorError(code)
		}
	}

	select {
	case <-done:
		return err

	default:
		if err == nil {
			err = errors.New("(no error code)")
		}
		return fmt.Errorf("container terminated unexpectedly: %w", err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Martin Planer. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package felix

import "testing"

func TestNop(t *testing.T) {
	// Dummy test for CI
}
<commit_msg>Removed dummy test file<commit_after><|endoftext|>


<commit_before>package manta

import (
	"strconv"
)

func decodeHandle(r *Reader, f *dt_field) interface{} {
	// So far these seem to occupy 32 bits but the value is made up only
	// out of what's present in the first 21 bits. In source 1, these only
	// occupied 21 bits of space.
	value := r.readBits(21) // a uint32
	r.seekBits(11)          // skip the rest of the 32 bits
	return value
}

func decodeByte(r *Reader, f *dt_field) interface{} {
	return r.readBits(8)
}

func decodeShort(r *Reader, f *dt_field) interface{} {
	return r.readBits(16)
}

func decodeUnsigned(r *Reader, f *dt_field) interface{} {
	return r.readVarUint64()
}

func decodeSigned(r *Reader, f *dt_field) interface{} {
	return r.readVarInt32()
}

func decodeSigned64(r *Reader, f *dt_field) interface{} {
	return r.readVarInt64()
}

func decodeBoolean(r *Reader, f *dt_field) interface{} {
	return r.readBoolean()
}

func decodeFloat(r *Reader, f *dt_field) interface{} {
	_debugf(
		"Bitcount: %v, Low: %v, High: %v, Flags: %v, Encoder: %v",
		saveReturnInt32(f.BitCount),
		saveReturnFloat32(f.LowValue, "nil"),
		saveReturnFloat32(f.HighValue, "nil"),
		strconv.FormatInt(int64(saveReturnInt32(f.Flags)), 2),
		f.Encoder,
	)

	// Parse specific encoders
	switch f.Encoder {
	case "coord":
		return r.readCoord()
	}

	var BitCount int
	var Low float32
	var High float32

	if f.BitCount != nil {
		BitCount = int(*f.BitCount)
	} else {
		// Maybe treated as no scale or something?
		return r.readVarUint32()
	}

	if f.LowValue != nil {
		Low = *f.LowValue
	} else {
		Low = 0.0
	}

	if f.HighValue != nil {
		High = *f.HighValue
	} else {
		High = 1.0
	}

	if f.Flags != nil {
		// Read raw float
		if *f.Flags&0x100 != 0 {
			return r.readBits(BitCount)
		}

		// read low value if empty
		if *f.Flags&0x10 != 0 && r.readBoolean() {
			return f.LowValue
		}

		// read high value if empty
		if *f.Flags&0x20 != 0 && r.readBoolean() {
			return f.HighValue
		}
	}

	dividend := r.readBits(BitCount)
	divisor := (1 << uint32(BitCount)) - 1
	return Low + (float32(dividend)/float32(divisor))*(High-Low)
}

func decodeString(r *Reader, f *dt_field) interface{} {
	return r.readString()
}

func decodeVector(r *Reader, f *dt_field) interface{} {
	size := r.readVarUint32()

	if size > 0 {
		_panicf("Ive been called")
	}

	return 0
}

func decodeClass(r *Reader, f *dt_field) interface{} {
	return r.readVarUint32()
}

func decodeQuantized(r *Reader, f *dt_field) interface{} {
	// Lets do this for now
	return decodeFloat(r, f)
}

func decodeFVector(r *Reader, f *dt_field) interface{} {
	// Parse specific encoders
	switch f.Encoder {
	case "normal":
		return r.read3BitNormal()
	}

	return []float32{decodeFloat(r, f).(float32), decodeFloat(r, f).(float32), decodeFloat(r, f).(float32)}
}

func decodeNop(r *Reader, f *dt_field) interface{} {
	return 0
}

func decodePointer(r *Reader, f *dt_field) interface{} {
	// Seems to be encoded as a single bit, not sure what to make of it
	if !r.readBoolean() {
		_panicf("Figure out how this works")
	}

	return 0
}

func decodeQAngle(r *Reader, f *dt_field) interface{} {
	if f.Flags != nil {
		// There is a flag check against 0x20 in the disasembly
		_debugf("Angle flags: %v", *f.Flags)
	}

	ret := [3]float32{0.0, 0.0, 0.0}

	rX := r.readBoolean()
	rY := r.readBoolean()
	rZ := r.readBoolean()

	if rX {
		ret[0] = r.readCoord()
	}

	if rY {
		ret[1] = r.readCoord()
	}

	if rZ {
		ret[2] = r.readCoord()
	}

	return ret
}

func decodeComponent(r *Reader, f *dt_field) interface{} {
	_debugf(
		"Bitcount: %v, Low: %v, High: %v, Flags: %v",
		saveReturnInt32(f.BitCount),
		saveReturnFloat32(f.LowValue, "nil"),
		saveReturnFloat32(f.HighValue, "nil"),
		strconv.FormatInt(int64(saveReturnInt32(f.Flags)), 2),
	)

	return r.readBits(1)
}

func decodeHSequence(r *Reader, f *dt_field) interface{} {
	// wrong, just testing
	return r.readBits(1)
}
<commit_msg>Getting CDOTAPlayer to work<commit_after>package manta

import (
	"strconv"
)

func decodeHandle(r *Reader, f *dt_field) interface{} {
	// So far these seem to occupy 32 bits but the value is made up only
	// out of what's present in the first 21 bits. In source 1, these only
	// occupied 21 bits of space.
	value := r.readBits(21) // a uint32
	r.seekBits(11)          // skip the rest of the 32 bits
	return value
}

func decodeByte(r *Reader, f *dt_field) interface{} {
	return r.readBits(8)
}

func decodeShort(r *Reader, f *dt_field) interface{} {
	return r.readBits(16)
}

func decodeUnsigned(r *Reader, f *dt_field) interface{} {
	return r.readVarUint64()
}

func decodeSigned(r *Reader, f *dt_field) interface{} {
	return r.readVarInt32()
}

func decodeSigned64(r *Reader, f *dt_field) interface{} {
	return r.readVarInt64()
}

func decodeBoolean(r *Reader, f *dt_field) interface{} {
	return r.readBoolean()
}

func decodeFloat(r *Reader, f *dt_field) interface{} {
	_debugf(
		"Bitcount: %v, Low: %v, High: %v, Flags: %v, Encoder: %v",
		saveReturnInt32(f.BitCount),
		saveReturnFloat32(f.LowValue, "nil"),
		saveReturnFloat32(f.HighValue, "nil"),
		strconv.FormatInt(int64(saveReturnInt32(f.Flags)), 2),
		f.Encoder,
	)

	// Parse specific encoders
	switch f.Encoder {
	case "coord":
		return r.readCoord()
	}

	var BitCount int
	var Low float32
	var High float32

	if f.BitCount != nil {
		BitCount = int(*f.BitCount)
	} else {
		// Maybe treated as no scale or something?
		return r.readVarUint32()
	}

	if f.LowValue != nil {
		Low = *f.LowValue
	} else {
		Low = 0.0
	}

	if f.HighValue != nil {
		High = *f.HighValue
	} else {
		High = 1.0
	}

	if f.Flags != nil {
		// Skip this case
		if *f.Flags&0x4 != 0 && f.LowValue == nil {
			// This doesn't fell right
			return r.readBits(2)
		}

		// Read raw float
		if *f.Flags&0x100 != 0 {
			return r.readBits(BitCount)
		}

		// read low value if empty
		if *f.Flags&0x10 != 0 && r.readBoolean() {
			return f.LowValue
		}

		// read high value if empty
		if *f.Flags&0x20 != 0 && r.readBoolean() {
			return f.HighValue
		}
	}

	dividend := r.readBits(BitCount)
	divisor := (1 << uint32(BitCount)) - 1
	return Low + (float32(dividend)/float32(divisor))*(High-Low)
}

func decodeString(r *Reader, f *dt_field) interface{} {
	return r.readString()
}

func decodeVector(r *Reader, f *dt_field) interface{} {
	size := r.readVarUint32()

	if size > 0 {
		_panicf("Ive been called, %v", size)
	}

	return 0
}

func decodeClass(r *Reader, f *dt_field) interface{} {
	return r.readVarUint32()
}

func decodeQuantized(r *Reader, f *dt_field) interface{} {
	// Lets do this for now
	return decodeFloat(r, f)
}

func decodeFVector(r *Reader, f *dt_field) interface{} {
	// Parse specific encoders
	switch f.Encoder {
	case "normal":
		return r.read3BitNormal()
	}

	return []float32{decodeFloat(r, f).(float32), decodeFloat(r, f).(float32), decodeFloat(r, f).(float32)}
}

func decodeNop(r *Reader, f *dt_field) interface{} {
	return 0
}

func decodePointer(r *Reader, f *dt_field) interface{} {
	// Seems to be encoded as a single bit, not sure what to make of it
	if !r.readBoolean() {
		_panicf("Figure out how this works")
	}

	return 0
}

func decodeQAngle(r *Reader, f *dt_field) interface{} {
	if f.Flags != nil {
		// There is a flag check against 0x20 in the disasembly
		_debugf("Angle flags: %v", *f.Flags)
	}

	ret := [3]float32{0.0, 0.0, 0.0}

	rX := r.readBoolean()
	rY := r.readBoolean()
	rZ := r.readBoolean()

	if rX {
		ret[0] = r.readCoord()
	}

	if rY {
		ret[1] = r.readCoord()
	}

	if rZ {
		ret[2] = r.readCoord()
	}

	return ret
}

func decodeComponent(r *Reader, f *dt_field) interface{} {
	_debugf(
		"Bitcount: %v, Low: %v, High: %v, Flags: %v",
		saveReturnInt32(f.BitCount),
		saveReturnFloat32(f.LowValue, "nil"),
		saveReturnFloat32(f.HighValue, "nil"),
		strconv.FormatInt(int64(saveReturnInt32(f.Flags)), 2),
	)

	return r.readBits(1)
}

func decodeHSequence(r *Reader, f *dt_field) interface{} {
	// wrong, just testing
	return r.readBits(1)
}
<|endoftext|>


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

package gce

import (
	"net/http"
	"net/mail"
	"time"

	"code.google.com/p/goauth2/oauth"
	"code.google.com/p/goauth2/oauth/jwt"
	"code.google.com/p/google-api-go-client/compute/v1"
	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/utils"

	"github.com/juju/juju/environs"
	"github.com/juju/juju/environs/config"
	"github.com/juju/juju/network"
	"github.com/juju/juju/provider/common"
)

const (
	driverScopes = "https://www.googleapis.com/auth/compute " +
		"https://www.googleapis.com/auth/devstorage.full_control"

	tokenURL = "https://accounts.google.com/o/oauth2/token"

	authURL = "https://accounts.google.com/o/oauth2/auth"

	partialMachineType = "zones/%s/machineTypes/%s"

	storageScratch    = "SCRATCH"
	storagePersistent = "PERSISTENT"

	statusDone         = "DONE"
	statusDown         = "DOWN"
	statusPending      = "PENDING"
	statusProvisioning = "PROVISIONING"
	statusRunning      = "RUNNING"
	statusStaging      = "STAGING"
	statusStopped      = "STOPPED"
	statusStopping     = "STOPPING"
	statusTerminated   = "TERMINATED"
	statusUp           = "UP"

	operationTimeout = 60 // seconds

	// minDiskSize is the minimum/default size (in megabytes) for GCE disks.
	//  TODO(ericsnow) Is there a minimum? What is the default?
	minDiskSize uint64 = 0
)

var (
	logger = loggo.GetLogger("juju.provider.gce")

	errNotImplemented = errors.NotImplementedf("gce provider")

	operationAttempts = utils.AttemptStrategy{
		Total: operationTimeout * time.Second,
		Delay: 10 * time.Second,
	}

	signedImageDataOnly = false
)

type gceAuth struct {
	clientID    string
	clientEmail string
	privateKey  []byte
}

func (ga gceAuth) validate() error {
	if ga.clientID == "" {
		return &config.InvalidConfigValue{Key: osEnvClientID}
	}
	if ga.clientEmail == "" {
		return &config.InvalidConfigValue{Key: osEnvClientEmail}
	} else if _, err := mail.ParseAddress(ga.clientEmail); err != nil {
		err = errors.Trace(err)
		return &config.InvalidConfigValue{osEnvClientEmail, ga.clientEmail, err}
	}
	if len(ga.privateKey) == 0 {
		return &config.InvalidConfigValue{Key: osEnvPrivateKey}
	}
	return nil
}

func (ga gceAuth) newTransport() (*oauth.Transport, error) {
	token, err := newToken(ga, driverScopes)
	if err != nil {
		return nil, errors.Trace(err)
	}

	transport := oauth.Transport{
		Config: &oauth.Config{
			ClientId: ga.clientID,
			Scope:    driverScopes,
			TokenURL: tokenURL,
			AuthURL:  authURL,
		},
		Token: token,
	}
	return &transport, nil
}

var newToken = func(auth gceAuth, scopes string) (*oauth.Token, error) {
	jtok := jwt.NewToken(auth.clientEmail, scopes, auth.privateKey)
	jtok.ClaimSet.Aud = tokenURL

	token, err := jtok.Assert(&http.Client{})
	if err != nil {
		msg := "retrieving auth token for %s"
		return nil, errors.Annotatef(err, msg, auth.clientEmail)
	}
	return token, nil
}

func (ga *gceAuth) newConnection() (*compute.Service, error) {
	transport, err := ga.newTransport()
	if err != nil {
		return nil, errors.Trace(err)
	}
	service, err := newService(transport)
	return service, errors.Trace(err)
}

var newService = func(transport *oauth.Transport) (*compute.Service, error) {
	return compute.New(transport.Client())
}

type gceConnection struct {
	*compute.Service

	region    string
	projectID string
}

func (gce *gceConnection) validate() error {
	if gce.projectID == "" {
		return &config.InvalidConfigValue{Key: osEnvProjectID}
	}
	return nil
}

func (gce *gceConnection) connect(auth gceAuth) error {
	if gce.Service != nil {
		return errors.New("connect() failed (already connected)")
	}

	service, err := auth.newConnection()
	if err != nil {
		return errors.Trace(err)
	}

	gce.Service = service
	return nil
}

func (gce *gceConnection) verifyCredentials() error {
	call := gce.Projects.Get(gce.projectID)
	if _, err := call.Do(); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func (gce *gceConnection) waitOperation(operation *compute.Operation) error {
	opID := operation.ClientOperationId

	for a := operationAttempts.Start(); a.Next(); {
		var err error
		if operation.Status == statusDone {
			return nil
		}
		// TODO(ericsnow) Should projectID be an arg?
		call := gce.GlobalOperations.Get(gce.projectID, opID)
		operation, err = call.Do()
		if err != nil {
			return errors.Annotate(err, "waiting for operation to complete")
		}
	}
	if operation.Status == statusDone {
		return nil
	}

	msg := "timed out after %d seconds waiting for GCE operation to finish"
	return errors.Errorf(msg, operationTimeout)
}

func (gce *gceConnection) instance(zone, id string) (*compute.Instance, error) {
	call := gce.Instances.Get(gce.projectID, zone, id)
	inst, err := call.Do()
	return inst, errors.Trace(err)
}

func (gce *gceConnection) newInstance(inst *compute.Instance, machineType string, zones []string) error {
	for _, zoneName := range zones {
		inst.MachineType = resolveMachineType(zoneName, machineType)
		call := gce.Instances.Insert(
			gce.projectID,
			zoneName,
			inst,
		)
		operation, err := call.Do()
		if err != nil {
			// XXX Handle zone-is-full error.
			return errors.Annotate(err, "sending new instance request")
		}
		if err := gce.waitOperation(operation); err != nil {
			// TODO(ericsnow) Handle zone-is-full error here?
			return errors.Trace(err)
		}

		// Get the instance here.
		// TODO(ericsnow) Do we really need to get it?
		updated, err := gce.instance(zoneName, inst.Name)
		if err != nil {
			return errors.Trace(err)
		}
		*inst = *updated
		// Success!
		return nil
	}
	return errors.Errorf("not able to provision in any zone")
}

func (gce *gceConnection) instances(env environs.Environ) ([]*compute.Instance, error) {
	// env won't be nil.
	prefix := common.MachineFullName(env, "")

	// TODO(ericsnow) MaxResults arg defaults to 500... (call.MaxResults()).
	call := gce.Instances.AggregatedList(gce.projectID)
	call = call.Filter("name eq " + prefix + ".*")
	// TODO(ericsnow) If we can use multiple filters, filter on status here.
	raw, err := call.Do()
	if err != nil {
		return nil, errors.Trace(err)
	}

	var results []*compute.Instance
	for _, item := range raw.Items {
		for _, inst := range item.Instances {
			results = append(results, inst)
		}
	}
	return results, nil
}

func (gce *gceConnection) availabilityZones(region string) ([]*compute.Zone, error) {
	//TODO(wwtizel3) support paging requests if we receive a truncated result.
	call := gce.Zones.List(gce.projectID)
	if region != "" {
		call = call.Filter("name eq " + region + "-")
	}
	raw, err := call.Do()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return raw.Items, nil
}

func (gce *gceConnection) removeInstance(id, zone string) error {
	call := gce.Instances.Delete(gce.projectID, zone, id)
	operation, err := call.Do()
	if err != nil {
		return errors.Trace(err)
	}
	if err := gce.waitOperation(operation); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func (gce *gceConnection) removeInstances(env environs.Environ, ids ...string) error {
	if len(ids) == 0 {
		return nil
	}

	instances, err := gce.instances(env)
	if err != nil {
		return errors.Annotatef(err, "while removing instances %v", ids)
	}

	var failed []string
	for _, instID := range ids {
		for _, inst := range instances {
			if inst.Name == instID {
				if err := gce.removeInstance(instID, inst.Zone); err != nil {
					failed = append(failed, instID)
					logger.Errorf("while removing instance %q: %v", instID, err)
				}
				break
			}
		}
	}
	if len(failed) != 0 {
		return errors.Errorf("some instance removals failed: %v", failed)
	}
	return nil
}

func (gce *gceConnection) firewall(machineId string) (*compute.Firewall, error) {
	call := gce.Firewalls.Get(gce.projectID, machineId)
	firewall, err := call.Do()
	if err != nil {
		return nil, errors.Annotate(err, "while getting firewall from GCE")
	}
	return firewall, nil
}

func (gce *gceConnection) setFirewall(machineId string, firewall *compute.Firewall) error {
	var err error
	var operation *compute.Operation
	if firewall == nil {
		call := gce.Firewalls.Delete(gce.projectID, machineId)
		operation, err = call.Do()
		if err != nil {
			return errors.Trace(err)
		}
	} else if machineId == "" {
		call := gce.Firewalls.Insert(gce.projectID, firewall)
		operation, err = call.Do()
		if err != nil {
			return errors.Trace(err)
		}
	} else {
		call := gce.Firewalls.Update(gce.projectID, machineId, firewall)
		operation, err = call.Do()
		if err != nil {
			return errors.Trace(err)
		}
	}
	if err := gce.waitOperation(operation); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func filterInstances(instances []*compute.Instance, statuses ...string) []*compute.Instance {
	// TODO(ericsnow) Filter in-place?
	// TODO(ericsnow) Also filter metadata (or tags)? While highly
	// unlikely (due to our choice of instance ID), it is possible that
	// the filter in gce.instances() results in a false positive.
	// An additional filter on the metadata would address that
	// possibility.
	var results []*compute.Instance
	for _, inst := range instances {
		if !checkInstStatus(inst, statuses...) {
			continue
		}
		results = append(results, inst)
	}
	return results
}

func checkInstStatus(inst *compute.Instance, statuses ...string) bool {
	for _, status := range statuses {
		if inst.Status == status {
			return true
		}
	}
	return false
}

func diskSpec(sizeReq *uint64, image string, boot bool) (*compute.AttachedDisk, uint64) {
	size := minDiskSize
	if sizeReq != nil && *sizeReq >= minDiskSize {
		size = *sizeReq
	}

	// TODO(ericsnow) what happens if there is not attached disk?
	disk := compute.AttachedDisk{
		// TODO(ericsnow) Set other fields too?
		Type: "SCRATCH", // Could be "PERSISTENT".
		Boot: boot,
		Mode: "READ_WRITE", // not needed?
		InitializeParams: &compute.AttachedDiskInitializeParams{
			// DiskName (defaults to instance name)
			DiskSizeGb: int64(common.MiBToGiB(size)),
			// DiskType (???)
			SourceImage: image, // needed?
		},
		// Interface (???)
		// DeviceName (persistent disk only)
		// Source (persistent disk only)
	}
	return &disk, size
}

// firewallSpec expands a port range set in to compute.FirewallAllowed
// and returns a compute.Firewall for the machineId.
func firewallSpec(machineId string, ps network.PortSet) *compute.Firewall {
	firewall := compute.Firewall{
		// Allowed is set below.
		// Description is not set.
		Name: machineId,
		// TODO(ericsnow) Does Network need to be set?
		// Network: "",
		// SourceRanges is not set.
		// SourceTags is not set.
		// TargetTags is not set.
	}

	for _, protocol := range ps.Protocols() {
		allowed := compute.FirewallAllowed{
			IPProtocol: protocol,
			Ports:      ps.PortStrings(protocol),
		}
		firewall.Allowed = append(firewall.Allowed, &allowed)
	}
	return &firewall
}
<commit_msg>provider/gce: machineId -> name.<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package gce

import (
	"net/http"
	"net/mail"
	"time"

	"code.google.com/p/goauth2/oauth"
	"code.google.com/p/goauth2/oauth/jwt"
	"code.google.com/p/google-api-go-client/compute/v1"
	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/utils"

	"github.com/juju/juju/environs"
	"github.com/juju/juju/environs/config"
	"github.com/juju/juju/network"
	"github.com/juju/juju/provider/common"
)

const (
	driverScopes = "https://www.googleapis.com/auth/compute " +
		"https://www.googleapis.com/auth/devstorage.full_control"

	tokenURL = "https://accounts.google.com/o/oauth2/token"

	authURL = "https://accounts.google.com/o/oauth2/auth"

	partialMachineType = "zones/%s/machineTypes/%s"

	storageScratch    = "SCRATCH"
	storagePersistent = "PERSISTENT"

	statusDone         = "DONE"
	statusDown         = "DOWN"
	statusPending      = "PENDING"
	statusProvisioning = "PROVISIONING"
	statusRunning      = "RUNNING"
	statusStaging      = "STAGING"
	statusStopped      = "STOPPED"
	statusStopping     = "STOPPING"
	statusTerminated   = "TERMINATED"
	statusUp           = "UP"

	operationTimeout = 60 // seconds

	// minDiskSize is the minimum/default size (in megabytes) for GCE disks.
	//  TODO(ericsnow) Is there a minimum? What is the default?
	minDiskSize uint64 = 0
)

var (
	logger = loggo.GetLogger("juju.provider.gce")

	errNotImplemented = errors.NotImplementedf("gce provider")

	operationAttempts = utils.AttemptStrategy{
		Total: operationTimeout * time.Second,
		Delay: 10 * time.Second,
	}

	signedImageDataOnly = false
)

type gceAuth struct {
	clientID    string
	clientEmail string
	privateKey  []byte
}

func (ga gceAuth) validate() error {
	if ga.clientID == "" {
		return &config.InvalidConfigValue{Key: osEnvClientID}
	}
	if ga.clientEmail == "" {
		return &config.InvalidConfigValue{Key: osEnvClientEmail}
	} else if _, err := mail.ParseAddress(ga.clientEmail); err != nil {
		err = errors.Trace(err)
		return &config.InvalidConfigValue{osEnvClientEmail, ga.clientEmail, err}
	}
	if len(ga.privateKey) == 0 {
		return &config.InvalidConfigValue{Key: osEnvPrivateKey}
	}
	return nil
}

func (ga gceAuth) newTransport() (*oauth.Transport, error) {
	token, err := newToken(ga, driverScopes)
	if err != nil {
		return nil, errors.Trace(err)
	}

	transport := oauth.Transport{
		Config: &oauth.Config{
			ClientId: ga.clientID,
			Scope:    driverScopes,
			TokenURL: tokenURL,
			AuthURL:  authURL,
		},
		Token: token,
	}
	return &transport, nil
}

var newToken = func(auth gceAuth, scopes string) (*oauth.Token, error) {
	jtok := jwt.NewToken(auth.clientEmail, scopes, auth.privateKey)
	jtok.ClaimSet.Aud = tokenURL

	token, err := jtok.Assert(&http.Client{})
	if err != nil {
		msg := "retrieving auth token for %s"
		return nil, errors.Annotatef(err, msg, auth.clientEmail)
	}
	return token, nil
}

func (ga *gceAuth) newConnection() (*compute.Service, error) {
	transport, err := ga.newTransport()
	if err != nil {
		return nil, errors.Trace(err)
	}
	service, err := newService(transport)
	return service, errors.Trace(err)
}

var newService = func(transport *oauth.Transport) (*compute.Service, error) {
	return compute.New(transport.Client())
}

type gceConnection struct {
	*compute.Service

	region    string
	projectID string
}

func (gce *gceConnection) validate() error {
	if gce.projectID == "" {
		return &config.InvalidConfigValue{Key: osEnvProjectID}
	}
	return nil
}

func (gce *gceConnection) connect(auth gceAuth) error {
	if gce.Service != nil {
		return errors.New("connect() failed (already connected)")
	}

	service, err := auth.newConnection()
	if err != nil {
		return errors.Trace(err)
	}

	gce.Service = service
	return nil
}

func (gce *gceConnection) verifyCredentials() error {
	call := gce.Projects.Get(gce.projectID)
	if _, err := call.Do(); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func (gce *gceConnection) waitOperation(operation *compute.Operation) error {
	opID := operation.ClientOperationId

	for a := operationAttempts.Start(); a.Next(); {
		var err error
		if operation.Status == statusDone {
			return nil
		}
		// TODO(ericsnow) Should projectID be an arg?
		call := gce.GlobalOperations.Get(gce.projectID, opID)
		operation, err = call.Do()
		if err != nil {
			return errors.Annotate(err, "waiting for operation to complete")
		}
	}
	if operation.Status == statusDone {
		return nil
	}

	msg := "timed out after %d seconds waiting for GCE operation to finish"
	return errors.Errorf(msg, operationTimeout)
}

func (gce *gceConnection) instance(zone, id string) (*compute.Instance, error) {
	call := gce.Instances.Get(gce.projectID, zone, id)
	inst, err := call.Do()
	return inst, errors.Trace(err)
}

func (gce *gceConnection) newInstance(inst *compute.Instance, machineType string, zones []string) error {
	for _, zoneName := range zones {
		inst.MachineType = resolveMachineType(zoneName, machineType)
		call := gce.Instances.Insert(
			gce.projectID,
			zoneName,
			inst,
		)
		operation, err := call.Do()
		if err != nil {
			// XXX Handle zone-is-full error.
			return errors.Annotate(err, "sending new instance request")
		}
		if err := gce.waitOperation(operation); err != nil {
			// TODO(ericsnow) Handle zone-is-full error here?
			return errors.Trace(err)
		}

		// Get the instance here.
		// TODO(ericsnow) Do we really need to get it?
		updated, err := gce.instance(zoneName, inst.Name)
		if err != nil {
			return errors.Trace(err)
		}
		*inst = *updated
		// Success!
		return nil
	}
	return errors.Errorf("not able to provision in any zone")
}

func (gce *gceConnection) instances(env environs.Environ) ([]*compute.Instance, error) {
	// env won't be nil.
	prefix := common.MachineFullName(env, "")

	// TODO(ericsnow) MaxResults arg defaults to 500... (call.MaxResults()).
	call := gce.Instances.AggregatedList(gce.projectID)
	call = call.Filter("name eq " + prefix + ".*")
	// TODO(ericsnow) If we can use multiple filters, filter on status here.
	raw, err := call.Do()
	if err != nil {
		return nil, errors.Trace(err)
	}

	var results []*compute.Instance
	for _, item := range raw.Items {
		for _, inst := range item.Instances {
			results = append(results, inst)
		}
	}
	return results, nil
}

func (gce *gceConnection) availabilityZones(region string) ([]*compute.Zone, error) {
	//TODO(wwtizel3) support paging requests if we receive a truncated result.
	call := gce.Zones.List(gce.projectID)
	if region != "" {
		call = call.Filter("name eq " + region + "-")
	}
	raw, err := call.Do()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return raw.Items, nil
}

func (gce *gceConnection) removeInstance(id, zone string) error {
	call := gce.Instances.Delete(gce.projectID, zone, id)
	operation, err := call.Do()
	if err != nil {
		return errors.Trace(err)
	}
	if err := gce.waitOperation(operation); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func (gce *gceConnection) removeInstances(env environs.Environ, ids ...string) error {
	if len(ids) == 0 {
		return nil
	}

	instances, err := gce.instances(env)
	if err != nil {
		return errors.Annotatef(err, "while removing instances %v", ids)
	}

	var failed []string
	for _, instID := range ids {
		for _, inst := range instances {
			if inst.Name == instID {
				if err := gce.removeInstance(instID, inst.Zone); err != nil {
					failed = append(failed, instID)
					logger.Errorf("while removing instance %q: %v", instID, err)
				}
				break
			}
		}
	}
	if len(failed) != 0 {
		return errors.Errorf("some instance removals failed: %v", failed)
	}
	return nil
}

func (gce *gceConnection) firewall(name string) (*compute.Firewall, error) {
	call := gce.Firewalls.Get(gce.projectID, name)
	firewall, err := call.Do()
	if err != nil {
		return nil, errors.Annotate(err, "while getting firewall from GCE")
	}
	return firewall, nil
}

func (gce *gceConnection) setFirewall(name string, firewall *compute.Firewall) error {
	var err error
	var operation *compute.Operation
	if firewall == nil {
		call := gce.Firewalls.Delete(gce.projectID, name)
		operation, err = call.Do()
		if err != nil {
			return errors.Trace(err)
		}
	} else if name == "" {
		call := gce.Firewalls.Insert(gce.projectID, firewall)
		operation, err = call.Do()
		if err != nil {
			return errors.Trace(err)
		}
	} else {
		call := gce.Firewalls.Update(gce.projectID, name, firewall)
		operation, err = call.Do()
		if err != nil {
			return errors.Trace(err)
		}
	}
	if err := gce.waitOperation(operation); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func filterInstances(instances []*compute.Instance, statuses ...string) []*compute.Instance {
	// TODO(ericsnow) Filter in-place?
	// TODO(ericsnow) Also filter metadata (or tags)? While highly
	// unlikely (due to our choice of instance ID), it is possible that
	// the filter in gce.instances() results in a false positive.
	// An additional filter on the metadata would address that
	// possibility.
	var results []*compute.Instance
	for _, inst := range instances {
		if !checkInstStatus(inst, statuses...) {
			continue
		}
		results = append(results, inst)
	}
	return results
}

func checkInstStatus(inst *compute.Instance, statuses ...string) bool {
	for _, status := range statuses {
		if inst.Status == status {
			return true
		}
	}
	return false
}

func diskSpec(sizeReq *uint64, image string, boot bool) (*compute.AttachedDisk, uint64) {
	size := minDiskSize
	if sizeReq != nil && *sizeReq >= minDiskSize {
		size = *sizeReq
	}

	// TODO(ericsnow) what happens if there is not attached disk?
	disk := compute.AttachedDisk{
		// TODO(ericsnow) Set other fields too?
		Type: "SCRATCH", // Could be "PERSISTENT".
		Boot: boot,
		Mode: "READ_WRITE", // not needed?
		InitializeParams: &compute.AttachedDiskInitializeParams{
			// DiskName (defaults to instance name)
			DiskSizeGb: int64(common.MiBToGiB(size)),
			// DiskType (???)
			SourceImage: image, // needed?
		},
		// Interface (???)
		// DeviceName (persistent disk only)
		// Source (persistent disk only)
	}
	return &disk, size
}

// firewallSpec expands a port range set in to compute.FirewallAllowed
// and returns a compute.Firewall for the provided name.
func firewallSpec(name string, ps network.PortSet) *compute.Firewall {
	firewall := compute.Firewall{
		// Allowed is set below.
		// Description is not set.
		Name: name,
		// TODO(ericsnow) Does Network need to be set?
		// Network: "",
		// SourceRanges is not set.
		// SourceTags is not set.
		// TargetTags is not set.
	}

	for _, protocol := range ps.Protocols() {
		allowed := compute.FirewallAllowed{
			IPProtocol: protocol,
			Ports:      ps.PortStrings(protocol),
		}
		firewall.Allowed = append(firewall.Allowed, &allowed)
	}
	return &firewall
}
<|endoftext|>


<commit_before>package authprovider

import (
	"context"
	"crypto/ed25519"
	"crypto/hmac"
	"crypto/sha256"
	"fmt"
	"io"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	authutil "github.com/containerd/containerd/remotes/docker/auth"
	remoteserrors "github.com/containerd/containerd/remotes/errors"
	"github.com/docker/cli/cli/config"
	"github.com/docker/cli/cli/config/configfile"
	"github.com/docker/cli/cli/config/types"
	"github.com/moby/buildkit/session"
	"github.com/moby/buildkit/session/auth"
	"github.com/moby/buildkit/util/progress/progresswriter"
	"github.com/pkg/errors"
	"golang.org/x/crypto/nacl/sign"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

const defaultExpiration = 60

func NewDockerAuthProvider(stderr io.Writer) session.Attachable {
	return &authProvider{
		authConfigCache: map[string]*types.AuthConfig{},
		config:          config.LoadDefaultConfigFile(stderr),
		seeds:           &tokenSeeds{dir: config.Dir()},
		loggerCache:     map[string]struct{}{},
	}
}

type authProvider struct {
	authConfigCache map[string]*types.AuthConfig
	config          *configfile.ConfigFile
	seeds           *tokenSeeds
	logger          progresswriter.Logger
	loggerCache     map[string]struct{}

	// The need for this mutex is not well understood.
	// Without it, the docker cli on OS X hangs when
	// reading credentials from docker-credential-osxkeychain.
	// See issue https://github.com/docker/cli/issues/1862
	mu sync.Mutex
}

func (ap *authProvider) SetLogger(l progresswriter.Logger) {
	ap.mu.Lock()
	ap.logger = l
	ap.mu.Unlock()
}

func (ap *authProvider) Register(server *grpc.Server) {
	auth.RegisterAuthServer(server, ap)
}

func (ap *authProvider) FetchToken(ctx context.Context, req *auth.FetchTokenRequest) (rr *auth.FetchTokenResponse, err error) {
	ac, err := ap.getAuthConfig(req.Host)
	if err != nil {
		return nil, err
	}

	// check for statically configured bearer token
	if ac.RegistryToken != "" {
		return toTokenResponse(ac.RegistryToken, time.Time{}, 0), nil
	}

	creds, err := ap.credentials(req.Host)
	if err != nil {
		return nil, err
	}

	to := authutil.TokenOptions{
		Realm:    req.Realm,
		Service:  req.Service,
		Scopes:   req.Scopes,
		Username: creds.Username,
		Secret:   creds.Secret,
	}

	if creds.Secret != "" {
		done := func(progresswriter.SubLogger) error {
			return err
		}
		defer func() {
			err = errors.Wrap(err, "failed to fetch oauth token")
		}()
		ap.mu.Lock()
		name := fmt.Sprintf("[auth] %v token for %s", strings.Join(trimScopePrefix(req.Scopes), " "), req.Host)
		if _, ok := ap.loggerCache[name]; !ok {
			progresswriter.Wrap(name, ap.logger, done)
		}
		ap.mu.Unlock()
		// credential information is provided, use oauth POST endpoint
		resp, err := authutil.FetchTokenWithOAuth(ctx, http.DefaultClient, nil, "buildkit-client", to)
		if err != nil {
			var errStatus remoteserrors.ErrUnexpectedStatus
			if errors.As(err, &errStatus) {
				// Registries without support for POST may return 404 for POST /v2/token.
				// As of September 2017, GCR is known to return 404.
				// As of February 2018, JFrog Artifactory is known to return 401.
				if (errStatus.StatusCode == 405 && to.Username != "") || errStatus.StatusCode == 404 || errStatus.StatusCode == 401 {
					resp, err := authutil.FetchToken(ctx, http.DefaultClient, nil, to)
					if err != nil {
						return nil, err
					}
					return toTokenResponse(resp.Token, resp.IssuedAt, resp.ExpiresIn), nil
				}
			}
			return nil, err
		}
		return toTokenResponse(resp.AccessToken, resp.IssuedAt, resp.ExpiresIn), nil
	}
	// do request anonymously
	resp, err := authutil.FetchToken(ctx, http.DefaultClient, nil, to)
	if err != nil {
		return nil, errors.Wrap(err, "failed to fetch anonymous token")
	}
	return toTokenResponse(resp.Token, resp.IssuedAt, resp.ExpiresIn), nil
}

func (ap *authProvider) credentials(host string) (*auth.CredentialsResponse, error) {
	ac, err := ap.getAuthConfig(host)
	if err != nil {
		return nil, err
	}
	res := &auth.CredentialsResponse{}
	if ac.IdentityToken != "" {
		res.Secret = ac.IdentityToken
	} else {
		res.Username = ac.Username
		res.Secret = ac.Password
	}
	return res, nil
}

func (ap *authProvider) Credentials(ctx context.Context, req *auth.CredentialsRequest) (*auth.CredentialsResponse, error) {
	resp, err := ap.credentials(req.Host)
	if err != nil || resp.Secret != "" {
		ap.mu.Lock()
		defer ap.mu.Unlock()
		_, ok := ap.loggerCache[req.Host]
		ap.loggerCache[req.Host] = struct{}{}
		if !ok {
			return resp, progresswriter.Wrap(fmt.Sprintf("[auth] sharing credentials for %s", req.Host), ap.logger, func(progresswriter.SubLogger) error {
				return err
			})
		}
	}
	return resp, err
}

func (ap *authProvider) GetTokenAuthority(ctx context.Context, req *auth.GetTokenAuthorityRequest) (*auth.GetTokenAuthorityResponse, error) {
	key, err := ap.getAuthorityKey(req.Host, req.Salt)
	if err != nil {
		return nil, err
	}

	return &auth.GetTokenAuthorityResponse{PublicKey: key[32:]}, nil
}

func (ap *authProvider) VerifyTokenAuthority(ctx context.Context, req *auth.VerifyTokenAuthorityRequest) (*auth.VerifyTokenAuthorityResponse, error) {
	key, err := ap.getAuthorityKey(req.Host, req.Salt)
	if err != nil {
		return nil, err
	}

	priv := new([64]byte)
	copy((*priv)[:], key)

	return &auth.VerifyTokenAuthorityResponse{Signed: sign.Sign(nil, req.Payload, priv)}, nil
}

func (ap *authProvider) getAuthConfig(host string) (*types.AuthConfig, error) {
	if _, exists := ap.authConfigCache[host]; !exists {
		ap.mu.Lock()
		defer ap.mu.Unlock()
		if host == "registry-1.docker.io" {
			host = "https://index.docker.io/v1/"
		}
		ac, err := ap.config.GetAuthConfig(host)
		if err != nil {
			return nil, err
		}
		ap.authConfigCache[host] = &ac
	}

	return ap.authConfigCache[host], nil
}

func (ap *authProvider) getAuthorityKey(host string, salt []byte) (ed25519.PrivateKey, error) {
	if v, err := strconv.ParseBool(os.Getenv("BUILDKIT_NO_CLIENT_TOKEN")); err == nil && v {
		return nil, status.Errorf(codes.Unavailable, "client side tokens disabled")
	}

	creds, err := ap.credentials(host)
	if err != nil {
		return nil, err
	}
	seed, err := ap.seeds.getSeed(host)
	if err != nil {
		return nil, err
	}

	mac := hmac.New(sha256.New, salt)
	if creds.Secret != "" {
		mac.Write(seed)
	}

	sum := mac.Sum(nil)

	return ed25519.NewKeyFromSeed(sum[:ed25519.SeedSize]), nil
}

func toTokenResponse(token string, issuedAt time.Time, expires int) *auth.FetchTokenResponse {
	if expires == 0 {
		expires = defaultExpiration
	}
	resp := &auth.FetchTokenResponse{
		Token:     token,
		ExpiresIn: int64(expires),
	}
	if !issuedAt.IsZero() {
		resp.IssuedAt = issuedAt.Unix()
	}
	return resp
}

func trimScopePrefix(scopes []string) []string {
	out := make([]string, len(scopes))
	for i, s := range scopes {
		out[i] = strings.TrimPrefix(s, "repository:")
	}
	return out
}
<commit_msg>authprovider: synchronize all authConfigCache access<commit_after>package authprovider

import (
	"context"
	"crypto/ed25519"
	"crypto/hmac"
	"crypto/sha256"
	"fmt"
	"io"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	authutil "github.com/containerd/containerd/remotes/docker/auth"
	remoteserrors "github.com/containerd/containerd/remotes/errors"
	"github.com/docker/cli/cli/config"
	"github.com/docker/cli/cli/config/configfile"
	"github.com/docker/cli/cli/config/types"
	"github.com/moby/buildkit/session"
	"github.com/moby/buildkit/session/auth"
	"github.com/moby/buildkit/util/progress/progresswriter"
	"github.com/pkg/errors"
	"golang.org/x/crypto/nacl/sign"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

const defaultExpiration = 60

func NewDockerAuthProvider(stderr io.Writer) session.Attachable {
	return &authProvider{
		authConfigCache: map[string]*types.AuthConfig{},
		config:          config.LoadDefaultConfigFile(stderr),
		seeds:           &tokenSeeds{dir: config.Dir()},
		loggerCache:     map[string]struct{}{},
	}
}

type authProvider struct {
	authConfigCache map[string]*types.AuthConfig
	config          *configfile.ConfigFile
	seeds           *tokenSeeds
	logger          progresswriter.Logger
	loggerCache     map[string]struct{}

	// The need for this mutex is not well understood.
	// Without it, the docker cli on OS X hangs when
	// reading credentials from docker-credential-osxkeychain.
	// See issue https://github.com/docker/cli/issues/1862
	mu sync.Mutex
}

func (ap *authProvider) SetLogger(l progresswriter.Logger) {
	ap.mu.Lock()
	ap.logger = l
	ap.mu.Unlock()
}

func (ap *authProvider) Register(server *grpc.Server) {
	auth.RegisterAuthServer(server, ap)
}

func (ap *authProvider) FetchToken(ctx context.Context, req *auth.FetchTokenRequest) (rr *auth.FetchTokenResponse, err error) {
	ac, err := ap.getAuthConfig(req.Host)
	if err != nil {
		return nil, err
	}

	// check for statically configured bearer token
	if ac.RegistryToken != "" {
		return toTokenResponse(ac.RegistryToken, time.Time{}, 0), nil
	}

	creds, err := ap.credentials(req.Host)
	if err != nil {
		return nil, err
	}

	to := authutil.TokenOptions{
		Realm:    req.Realm,
		Service:  req.Service,
		Scopes:   req.Scopes,
		Username: creds.Username,
		Secret:   creds.Secret,
	}

	if creds.Secret != "" {
		done := func(progresswriter.SubLogger) error {
			return err
		}
		defer func() {
			err = errors.Wrap(err, "failed to fetch oauth token")
		}()
		ap.mu.Lock()
		name := fmt.Sprintf("[auth] %v token for %s", strings.Join(trimScopePrefix(req.Scopes), " "), req.Host)
		if _, ok := ap.loggerCache[name]; !ok {
			progresswriter.Wrap(name, ap.logger, done)
		}
		ap.mu.Unlock()
		// credential information is provided, use oauth POST endpoint
		resp, err := authutil.FetchTokenWithOAuth(ctx, http.DefaultClient, nil, "buildkit-client", to)
		if err != nil {
			var errStatus remoteserrors.ErrUnexpectedStatus
			if errors.As(err, &errStatus) {
				// Registries without support for POST may return 404 for POST /v2/token.
				// As of September 2017, GCR is known to return 404.
				// As of February 2018, JFrog Artifactory is known to return 401.
				if (errStatus.StatusCode == 405 && to.Username != "") || errStatus.StatusCode == 404 || errStatus.StatusCode == 401 {
					resp, err := authutil.FetchToken(ctx, http.DefaultClient, nil, to)
					if err != nil {
						return nil, err
					}
					return toTokenResponse(resp.Token, resp.IssuedAt, resp.ExpiresIn), nil
				}
			}
			return nil, err
		}
		return toTokenResponse(resp.AccessToken, resp.IssuedAt, resp.ExpiresIn), nil
	}
	// do request anonymously
	resp, err := authutil.FetchToken(ctx, http.DefaultClient, nil, to)
	if err != nil {
		return nil, errors.Wrap(err, "failed to fetch anonymous token")
	}
	return toTokenResponse(resp.Token, resp.IssuedAt, resp.ExpiresIn), nil
}

func (ap *authProvider) credentials(host string) (*auth.CredentialsResponse, error) {
	ac, err := ap.getAuthConfig(host)
	if err != nil {
		return nil, err
	}
	res := &auth.CredentialsResponse{}
	if ac.IdentityToken != "" {
		res.Secret = ac.IdentityToken
	} else {
		res.Username = ac.Username
		res.Secret = ac.Password
	}
	return res, nil
}

func (ap *authProvider) Credentials(ctx context.Context, req *auth.CredentialsRequest) (*auth.CredentialsResponse, error) {
	resp, err := ap.credentials(req.Host)
	if err != nil || resp.Secret != "" {
		ap.mu.Lock()
		defer ap.mu.Unlock()
		_, ok := ap.loggerCache[req.Host]
		ap.loggerCache[req.Host] = struct{}{}
		if !ok {
			return resp, progresswriter.Wrap(fmt.Sprintf("[auth] sharing credentials for %s", req.Host), ap.logger, func(progresswriter.SubLogger) error {
				return err
			})
		}
	}
	return resp, err
}

func (ap *authProvider) GetTokenAuthority(ctx context.Context, req *auth.GetTokenAuthorityRequest) (*auth.GetTokenAuthorityResponse, error) {
	key, err := ap.getAuthorityKey(req.Host, req.Salt)
	if err != nil {
		return nil, err
	}

	return &auth.GetTokenAuthorityResponse{PublicKey: key[32:]}, nil
}

func (ap *authProvider) VerifyTokenAuthority(ctx context.Context, req *auth.VerifyTokenAuthorityRequest) (*auth.VerifyTokenAuthorityResponse, error) {
	key, err := ap.getAuthorityKey(req.Host, req.Salt)
	if err != nil {
		return nil, err
	}

	priv := new([64]byte)
	copy((*priv)[:], key)

	return &auth.VerifyTokenAuthorityResponse{Signed: sign.Sign(nil, req.Payload, priv)}, nil
}

func (ap *authProvider) getAuthConfig(host string) (*types.AuthConfig, error) {
	ap.mu.Lock()
	defer ap.mu.Unlock()
	if _, exists := ap.authConfigCache[host]; !exists {
		if host == "registry-1.docker.io" {
			host = "https://index.docker.io/v1/"
		}
		ac, err := ap.config.GetAuthConfig(host)
		if err != nil {
			return nil, err
		}
		ap.authConfigCache[host] = &ac
	}

	return ap.authConfigCache[host], nil
}

func (ap *authProvider) getAuthorityKey(host string, salt []byte) (ed25519.PrivateKey, error) {
	if v, err := strconv.ParseBool(os.Getenv("BUILDKIT_NO_CLIENT_TOKEN")); err == nil && v {
		return nil, status.Errorf(codes.Unavailable, "client side tokens disabled")
	}

	creds, err := ap.credentials(host)
	if err != nil {
		return nil, err
	}
	seed, err := ap.seeds.getSeed(host)
	if err != nil {
		return nil, err
	}

	mac := hmac.New(sha256.New, salt)
	if creds.Secret != "" {
		mac.Write(seed)
	}

	sum := mac.Sum(nil)

	return ed25519.NewKeyFromSeed(sum[:ed25519.SeedSize]), nil
}

func toTokenResponse(token string, issuedAt time.Time, expires int) *auth.FetchTokenResponse {
	if expires == 0 {
		expires = defaultExpiration
	}
	resp := &auth.FetchTokenResponse{
		Token:     token,
		ExpiresIn: int64(expires),
	}
	if !issuedAt.IsZero() {
		resp.IssuedAt = issuedAt.Unix()
	}
	return resp
}

func trimScopePrefix(scopes []string) []string {
	out := make([]string, len(scopes))
	for i, s := range scopes {
		out[i] = strings.TrimPrefix(s, "repository:")
	}
	return out
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"os"

	"github.com/bluepeppers/collectd-docker/collector"
	"github.com/fsouza/go-dockerclient"
	"path"
)

func main() {
	e := flag.String("endpoint", "unix:///var/run/docker.sock", "docker endpoint")
	c := flag.String("cert", "", "cert path for tls")
	h := flag.String("host", "", "host to report")
	i := flag.Int("interval", 1, "interval to report")
	flag.Parse()

	if *h == "" {
		flag.PrintDefaults()
		os.Exit(1)
	}

	var client *docker.Client
	var err error

	if *c != "" {
		client, err = docker.NewTLSClient(*e, path.Join(*c, "cert.pem"), path.Join(*c, "key.pem"), path.Join(*c, "ca.pem"))
	} else {
		client, err = docker.NewClient(*e)
	}

	if err != nil {
		log.Fatal(err)
	}

	writer := collector.NewCollectdWriter(*h, os.Stdout)

	collector := collector.NewCollector(client, writer, *i)

	err = collector.Run(5)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Relative<commit_after>package main

import (
	"flag"
	"log"
	"os"

	collector "../.."
	"github.com/fsouza/go-dockerclient"
	"path"
)

func main() {
	e := flag.String("endpoint", "unix:///var/run/docker.sock", "docker endpoint")
	c := flag.String("cert", "", "cert path for tls")
	h := flag.String("host", "", "host to report")
	i := flag.Int("interval", 1, "interval to report")
	flag.Parse()

	if *h == "" {
		flag.PrintDefaults()
		os.Exit(1)
	}

	var client *docker.Client
	var err error

	if *c != "" {
		client, err = docker.NewTLSClient(*e, path.Join(*c, "cert.pem"), path.Join(*c, "key.pem"), path.Join(*c, "ca.pem"))
	} else {
		client, err = docker.NewClient(*e)
	}

	if err != nil {
		log.Fatal(err)
	}

	writer := collector.NewCollectdWriter(*h, os.Stdout)

	collector := collector.NewCollector(client, writer, *i)

	err = collector.Run(5)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package gopdf

import (
	"fmt"
	"io"
	"math"
)

type cacheContentImage struct {
	radianAngle      float64
	verticalFlip     bool
	horizontalFlip   bool
	index            int
	x                float64
	y                float64
	pageHeight       float64
	rect             Rect
	crop             *CropOptions
	extGStateIndexes []int
}

func (c *cacheContentImage) write(w io.Writer, protection *PDFProtection) error {
	width := c.rect.W
	height := c.rect.H

	contentStream := "q\n"

	for _, extGStateIndex := range c.extGStateIndexes {
		contentStream += fmt.Sprintf("/GS%d gs\n", extGStateIndex)
	}

	if c.horizontalFlip || c.verticalFlip {
		fh := "1"
		if c.horizontalFlip {
			fh = "-1"
		}

		fv := "1"
		if c.verticalFlip {
			fv = "-1"
		}

		contentStream += fmt.Sprintf("%s 0 0 %s 0 0 cm\n", fh, fv)
	}

	contentStream += "q "

	if c.crop != nil {
		clippingX := c.x
		if c.horizontalFlip {
			clippingX = -clippingX - c.crop.Width
		}

		clippingY := c.pageHeight - (c.y + c.crop.Height)
		if c.verticalFlip {
			clippingY = -clippingY - c.crop.Height
		}

		startX := c.x - c.crop.X
		if c.horizontalFlip {
			startX = -startX - width
		}

		startY := c.pageHeight - (c.y - c.crop.Y + c.rect.H)
		if c.verticalFlip {
			startY = -startY - height
		}

		contentStream += fmt.Sprintf("%0.2f %0.2f %0.2f %0.2f re W* n\n", clippingX, clippingY, c.crop.Width, c.crop.Height)
		contentStream += fmt.Sprintf("%0.2f 0 0 %0.2f %0.2f %0.2f cm\n", width, height, startX, startY)
	} else {
		x := c.x
		y := c.pageHeight - (c.y + height)

		if c.horizontalFlip {
			x = -x - width
		}

		if c.verticalFlip {
			y = -y - height
		}

		contentStream += fmt.Sprintf("%0.2f 0 0 %0.2f %0.2f %0.2f cm\n", width, height, x, y)
	}

	if c.radianAngle != 0 {
		cos := math.Cos(c.radianAngle)
		sin := math.Sin(c.radianAngle)

		contentStream += fmt.Sprintf("%.5f %.5f %.5f %.5f 1 0 cm\n", cos, sin, -sin, cos)
	}

	contentStream += fmt.Sprintf("/I%d Do Q\n", c.index+1)

	contentStream += "Q\n"

	if _, err := io.WriteString(w, contentStream); err != nil {
		return err
	}

	return nil
}
<commit_msg>set translate x,y<commit_after>package gopdf

import (
	"fmt"
	"io"
	"math"
)

type cacheContentImage struct {
	radianAngle      float64
	verticalFlip     bool
	horizontalFlip   bool
	index            int
	x                float64
	y                float64
	pageHeight       float64
	rect             Rect
	crop             *CropOptions
	extGStateIndexes []int
}

func (c *cacheContentImage) write(w io.Writer, protection *PDFProtection) error {
	width := c.rect.W
	height := c.rect.H

	contentStream := "q\n"

	for _, extGStateIndex := range c.extGStateIndexes {
		contentStream += fmt.Sprintf("/GS%d gs\n", extGStateIndex)
	}

	if c.horizontalFlip || c.verticalFlip {
		fh := "1"
		if c.horizontalFlip {
			fh = "-1"
		}

		fv := "1"
		if c.verticalFlip {
			fv = "-1"
		}

		contentStream += fmt.Sprintf("%s 0 0 %s 0 0 cm\n", fh, fv)
	}

	contentStream += "q "

	if c.crop != nil {
		clippingX := c.x
		if c.horizontalFlip {
			clippingX = -clippingX - c.crop.Width
		}

		clippingY := c.pageHeight - (c.y + c.crop.Height)
		if c.verticalFlip {
			clippingY = -clippingY - c.crop.Height
		}

		startX := c.x - c.crop.X
		if c.horizontalFlip {
			startX = -startX - width
		}

		startY := c.pageHeight - (c.y - c.crop.Y + c.rect.H)
		if c.verticalFlip {
			startY = -startY - height
		}

		contentStream += fmt.Sprintf("%0.2f %0.2f %0.2f %0.2f re W* n\n", clippingX, clippingY, c.crop.Width, c.crop.Height)
	} else {
		x := c.x
		y := c.pageHeight - (c.y + height)

		if c.horizontalFlip {
			x = -x - width
		}

		if c.verticalFlip {
			y = -y - height
		}

		contentStream += fmt.Sprintf("%0.2f 0 0 %0.2f %0.2f %0.2f cm\n", width, height, x, y)
	}

	if c.radianAngle != 0 {
		cos := math.Cos(c.radianAngle)
		sin := math.Sin(c.radianAngle)

		degreeAngle := int(math.Round(math.Abs(c.radianAngle / math.Pi * 180)))
		if degreeAngle > 360 {
			degreeAngle = degreeAngle % 360
		}

		translateX := 0
		translateY := 0

		if 180 == degreeAngle || degreeAngle == 360 {
			translateX, translateY = 1, 1
		} else if degreeAngle > 90 {
			translateX, translateY = 1, 0
		} else {
			translateX, translateY = 0, 1
		}

		contentStream += fmt.Sprintf("%.5f %.5f %.5f %.5f %d %d cm\n", cos, sin, -sin, cos, translateX, translateY)
	}

	contentStream += fmt.Sprintf("/I%d Do Q\n", c.index+1)

	contentStream += "Q\n"

	if _, err := io.WriteString(w, contentStream); err != nil {
		return err
	}

	return nil
}

const float64EqualityThreshold = 1e-9

func almostEqual(a, b float64) bool {
	return math.Abs(a-b) <= float64EqualityThreshold
}
<|endoftext|>


<commit_before>package gokhipu

// Bank represents the schema of bank defined by khipu
type Bank struct {
	BankID    string `json:"bank_id"`
	Name      string `json:"name"`
	Message   string `json:"message"`
	MinAmount string `json:"min_amount"`
	Type      string `json:"type"`
	Parent    string `json:"parent"`
}
<commit_msg>Complete json tags<commit_after>package gokhipu

// Bank represents the schema of bank defined by khipu
type Bank struct {
	BankID    string `json:"bank_id,omitempty"`
	Name      string `json:"name,omitempty"`
	Message   string `json:"message,omitempty"`
	MinAmount string `json:"min_amount,omitempty"`
	Type      string `json:"type,omitempty"`
	Parent    string `json:"parent,omitempty"`
}
<|endoftext|>


<commit_before>package agent

import (
	"bytes"
	"io"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/hashicorp/consul/consul/structs"
)

const (
	// maxQueryTime is used to bound the limit of a blocking query
	maxQueryTime = 600 * time.Second
)

// EventFire is used to fire a new event
func (s *HTTPServer) EventFire(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
	// Mandate a PUT request
	if req.Method != "PUT" {
		resp.WriteHeader(405)
		return nil, nil
	}

	// Get the datacenter
	var dc string
	s.parseDC(req, &dc)

	event := &UserEvent{}
	event.Name = strings.TrimPrefix(req.URL.Path, "/v1/event/fire/")
	if event.Name == "" {
		resp.WriteHeader(400)
		resp.Write([]byte("Missing name"))
		return nil, nil
	}

	// Get the ACL token
	var token string
	s.parseToken(req, &token)

	// Get the filters
	if filt := req.URL.Query().Get("node"); filt != "" {
		event.NodeFilter = filt
	}
	if filt := req.URL.Query().Get("service"); filt != "" {
		event.ServiceFilter = filt
	}
	if filt := req.URL.Query().Get("tag"); filt != "" {
		event.TagFilter = filt
	}

	// Get the payload
	if req.ContentLength > 0 {
		var buf bytes.Buffer
		if _, err := io.Copy(&buf, req.Body); err != nil {
			return nil, err
		}
		event.Payload = buf.Bytes()
	}

	// Try to fire the event
	if err := s.agent.UserEvent(dc, token, event); err != nil {
		if strings.Contains(err.Error(), permissionDenied) {
			resp.WriteHeader(403)
			resp.Write([]byte(permissionDenied))
			return nil, nil
		}
		resp.WriteHeader(500)
		return nil, err
	}

	// Return the event
	return event, nil
}

// EventList is used to retrieve the recent list of events
func (s *HTTPServer) EventList(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
	// Parse the query options, since we simulate a blocking query
	var b structs.QueryOptions
	if parseWait(resp, req, &b) {
		return nil, nil
	}

	// Fetch the ACL token, if any.
	var token string
	s.parseToken(req, &token)
	acl, err := s.agent.resolveToken(token)
	if err != nil {
		return nil, err
	}

	// Look for a name filter
	var nameFilter string
	if filt := req.URL.Query().Get("name"); filt != "" {
		nameFilter = filt
	}

	// Lots of this logic is borrowed from consul/rpc.go:blockingRPC
	// However we cannot use that directly since this code has some
	// slight semantics differences...
	var timeout <-chan time.Time
	var notifyCh chan struct{}

	// Fast path non-blocking
	if b.MinQueryIndex == 0 {
		goto RUN_QUERY
	}

	// Restrict the max query time
	if b.MaxQueryTime > maxQueryTime {
		b.MaxQueryTime = maxQueryTime
	}

	// Ensure a time limit is set if we have an index
	if b.MinQueryIndex > 0 && b.MaxQueryTime == 0 {
		b.MaxQueryTime = maxQueryTime
	}

	// Setup a query timeout
	if b.MaxQueryTime > 0 {
		timeout = time.After(b.MaxQueryTime)
	}

	// Setup a notification channel for changes
SETUP_NOTIFY:
	if b.MinQueryIndex > 0 {
		notifyCh = make(chan struct{}, 1)
		s.agent.eventNotify.Wait(notifyCh)
	}

RUN_QUERY:
	// Get the recent events
	events := s.agent.UserEvents()

	// Filter the events using the ACL, if present
	if acl != nil {
		for i := 0; i < len(events); i++ {
			name := events[i].Name
			if acl.EventRead(name) {
				continue
			}
			s.agent.logger.Printf("[DEBUG] agent: dropping event %q from result due to ACLs", name)
			events = append(events[:i], events[i+1:]...)
			i--
		}
	}

	// Filter the events if requested
	if nameFilter != "" {
		for i := 0; i < len(events); i++ {
			if events[i].Name != nameFilter {
				events = append(events[:i], events[i+1:]...)
				i--
			}
		}
	}

	// Determine the index
	var index uint64
	if len(events) == 0 {
		// Return a non-zero index to prevent a hot query loop. This
		// can be caused by a watch for example when there is no matching
		// events.
		index = 1
	} else {
		last := events[len(events)-1]
		index = uuidToUint64(last.ID)
	}
	setIndex(resp, index)

	// Check for exact match on the query value. Because
	// the index value is not monotonic, we just ensure it is
	// not an exact match.
	if index > 0 && index == b.MinQueryIndex {
		select {
		case <-notifyCh:
			goto SETUP_NOTIFY
		case <-timeout:
		}
	}
	return events, nil
}

// uuidToUint64 is a bit of a hack to generate a 64bit Consul index.
// In effect, we take our random UUID, convert it to a 128 bit number,
// then XOR the high-order and low-order 64bit's together to get the
// output. This lets us generate an index which can be used to simulate
// the blocking behavior of other catalog endpoints.
func uuidToUint64(uuid string) uint64 {
	lower := uuid[0:8] + uuid[9:13] + uuid[14:18]
	upper := uuid[19:23] + uuid[24:36]
	lowVal, err := strconv.ParseUint(lower, 16, 64)
	if err != nil {
		panic("Failed to convert " + lower)
	}
	highVal, err := strconv.ParseUint(upper, 16, 64)
	if err != nil {
		panic("Failed to convert " + upper)
	}
	return lowVal ^ highVal
}
<commit_msg>Updates a comment to point to new blockingQuery function.<commit_after>package agent

import (
	"bytes"
	"io"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/hashicorp/consul/consul/structs"
)

const (
	// maxQueryTime is used to bound the limit of a blocking query
	maxQueryTime = 600 * time.Second
)

// EventFire is used to fire a new event
func (s *HTTPServer) EventFire(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
	// Mandate a PUT request
	if req.Method != "PUT" {
		resp.WriteHeader(405)
		return nil, nil
	}

	// Get the datacenter
	var dc string
	s.parseDC(req, &dc)

	event := &UserEvent{}
	event.Name = strings.TrimPrefix(req.URL.Path, "/v1/event/fire/")
	if event.Name == "" {
		resp.WriteHeader(400)
		resp.Write([]byte("Missing name"))
		return nil, nil
	}

	// Get the ACL token
	var token string
	s.parseToken(req, &token)

	// Get the filters
	if filt := req.URL.Query().Get("node"); filt != "" {
		event.NodeFilter = filt
	}
	if filt := req.URL.Query().Get("service"); filt != "" {
		event.ServiceFilter = filt
	}
	if filt := req.URL.Query().Get("tag"); filt != "" {
		event.TagFilter = filt
	}

	// Get the payload
	if req.ContentLength > 0 {
		var buf bytes.Buffer
		if _, err := io.Copy(&buf, req.Body); err != nil {
			return nil, err
		}
		event.Payload = buf.Bytes()
	}

	// Try to fire the event
	if err := s.agent.UserEvent(dc, token, event); err != nil {
		if strings.Contains(err.Error(), permissionDenied) {
			resp.WriteHeader(403)
			resp.Write([]byte(permissionDenied))
			return nil, nil
		}
		resp.WriteHeader(500)
		return nil, err
	}

	// Return the event
	return event, nil
}

// EventList is used to retrieve the recent list of events
func (s *HTTPServer) EventList(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
	// Parse the query options, since we simulate a blocking query
	var b structs.QueryOptions
	if parseWait(resp, req, &b) {
		return nil, nil
	}

	// Fetch the ACL token, if any.
	var token string
	s.parseToken(req, &token)
	acl, err := s.agent.resolveToken(token)
	if err != nil {
		return nil, err
	}

	// Look for a name filter
	var nameFilter string
	if filt := req.URL.Query().Get("name"); filt != "" {
		nameFilter = filt
	}

	// Lots of this logic is borrowed from consul/rpc.go:blockingQuery
	// However we cannot use that directly since this code has some
	// slight semantics differences...
	var timeout <-chan time.Time
	var notifyCh chan struct{}

	// Fast path non-blocking
	if b.MinQueryIndex == 0 {
		goto RUN_QUERY
	}

	// Restrict the max query time
	if b.MaxQueryTime > maxQueryTime {
		b.MaxQueryTime = maxQueryTime
	}

	// Ensure a time limit is set if we have an index
	if b.MinQueryIndex > 0 && b.MaxQueryTime == 0 {
		b.MaxQueryTime = maxQueryTime
	}

	// Setup a query timeout
	if b.MaxQueryTime > 0 {
		timeout = time.After(b.MaxQueryTime)
	}

	// Setup a notification channel for changes
SETUP_NOTIFY:
	if b.MinQueryIndex > 0 {
		notifyCh = make(chan struct{}, 1)
		s.agent.eventNotify.Wait(notifyCh)
	}

RUN_QUERY:
	// Get the recent events
	events := s.agent.UserEvents()

	// Filter the events using the ACL, if present
	if acl != nil {
		for i := 0; i < len(events); i++ {
			name := events[i].Name
			if acl.EventRead(name) {
				continue
			}
			s.agent.logger.Printf("[DEBUG] agent: dropping event %q from result due to ACLs", name)
			events = append(events[:i], events[i+1:]...)
			i--
		}
	}

	// Filter the events if requested
	if nameFilter != "" {
		for i := 0; i < len(events); i++ {
			if events[i].Name != nameFilter {
				events = append(events[:i], events[i+1:]...)
				i--
			}
		}
	}

	// Determine the index
	var index uint64
	if len(events) == 0 {
		// Return a non-zero index to prevent a hot query loop. This
		// can be caused by a watch for example when there is no matching
		// events.
		index = 1
	} else {
		last := events[len(events)-1]
		index = uuidToUint64(last.ID)
	}
	setIndex(resp, index)

	// Check for exact match on the query value. Because
	// the index value is not monotonic, we just ensure it is
	// not an exact match.
	if index > 0 && index == b.MinQueryIndex {
		select {
		case <-notifyCh:
			goto SETUP_NOTIFY
		case <-timeout:
		}
	}
	return events, nil
}

// uuidToUint64 is a bit of a hack to generate a 64bit Consul index.
// In effect, we take our random UUID, convert it to a 128 bit number,
// then XOR the high-order and low-order 64bit's together to get the
// output. This lets us generate an index which can be used to simulate
// the blocking behavior of other catalog endpoints.
func uuidToUint64(uuid string) uint64 {
	lower := uuid[0:8] + uuid[9:13] + uuid[14:18]
	upper := uuid[19:23] + uuid[24:36]
	lowVal, err := strconv.ParseUint(lower, 16, 64)
	if err != nil {
		panic("Failed to convert " + lower)
	}
	highVal, err := strconv.ParseUint(upper, 16, 64)
	if err != nil {
		panic("Failed to convert " + upper)
	}
	return lowVal ^ highVal
}
<|endoftext|>