<commit_before>package main

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"time"
)

func apiLogHandler(event string) {
	fmt.Fprintln(os.Stderr, event, "-", time.Now())
}
func VersionHandler(logger func(string)) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		logger("Version")
		var jsonResp struct {
			Text string `json:"version"`
		}
		jsonResp.Text = Version
		message, err := json.Marshal(jsonResp)
		if err != nil {
			logger(fmt.Sprintf("%s", err))
		}
		w.Write(message)
	})
}

func DoorStatusHandler(doorStatus func(int) (string, error), logger func(string), statusPin int) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		var jsonResp struct {
			Text string `json:"door_status"`
		}

		status, err := doorStatus(statusPin)
		if err != nil {
			errMessage := fmt.Sprintf("%s", err)
			logger(errMessage)
			jsonResp.Text = errMessage
			w.WriteHeader(422)
		}

		jsonResp.Text = status
		message, err := json.Marshal(jsonResp)
		if err != nil {
			logger(fmt.Sprintf("%s", err))
		}
		w.Write(message)
	})
}

func RelayHandle(toggleSwitch func(int, int) error, logger func(string), pinNumber int, sleepTimeout int) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logger("TOGGLE DOOR")
		err := toggleSwitch(pinNumber, sleepTimeout)
		if err != nil {
			errMessage := "Could not write to pin"
			logger(errMessage)
			w.WriteHeader(500)
			return
		}

		var resp struct {
			Status string `json:"status"`
		}
		resp.Status = "signal received"
		message, err := json.Marshal(resp)
		if err != nil {
			logger(fmt.Sprintf("%s", err))
		}
		w.Write(message)
	})
}

func AuthenticatedHandler(f http.HandlerFunc) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		signature := req.Header.Get("signature")
		timestamp := req.Header.Get("timestamp")
		decodedSignature, err := base64.URLEncoding.DecodeString(signature)
		if err != nil {
			w.WriteHeader(http.StatusForbidden)
			return
		}

		verified := VerifySignature([]byte(timestamp), decodedSignature)
		if verified {
			// Verify time
			i, err := strconv.ParseInt(timestamp, 10, 64)
			if err != nil {
				w.WriteHeader(http.StatusForbidden)
				return
			}

			_, err = VerifyTime(i)
			if err != nil {
				w.WriteHeader(http.StatusForbidden)
				return
			}
		} else {
			w.WriteHeader(http.StatusForbidden)
			return
		}

		f(w, req)
	})
}
<commit_msg>Add better error message for doorStatus<commit_after>package main

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"time"
)

func apiLogHandler(event string) {
	fmt.Fprintln(os.Stderr, event, "-", time.Now())
}
func VersionHandler(logger func(string)) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		logger("Version")
		var jsonResp struct {
			Text string `json:"version"`
		}
		jsonResp.Text = Version
		message, err := json.Marshal(jsonResp)
		if err != nil {
			logger(fmt.Sprintf("%s", err))
		}
		w.Write(message)
	})
}

func DoorStatusHandler(doorStatus func(int) (string, error), logger func(string), statusPin int) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		var jsonResp struct {
			Text string `json:"door_status"`
		}

		status, err := doorStatus(statusPin)
		if err != nil {
			errMessage := fmt.Sprintf("Could not read pin '%d' on Raspberry Pi", statusPin)
			logger(errMessage)
			jsonResp.Text = errMessage
			w.WriteHeader(422)
		}

		jsonResp.Text = status
		message, err := json.Marshal(jsonResp)
		if err != nil {
			logger(fmt.Sprintf("%s", err))
		}
		w.Write(message)
	})
}

func RelayHandle(toggleSwitch func(int, int) error, logger func(string), pinNumber int, sleepTimeout int) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logger("TOGGLE DOOR")
		err := toggleSwitch(pinNumber, sleepTimeout)
		if err != nil {
			errMessage := "Could not write to pin"
			logger(errMessage)
			w.WriteHeader(500)
			return
		}

		var resp struct {
			Status string `json:"status"`
		}
		resp.Status = "signal received"
		message, err := json.Marshal(resp)
		if err != nil {
			logger(fmt.Sprintf("%s", err))
		}
		w.Write(message)
	})
}

func AuthenticatedHandler(f http.HandlerFunc) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		signature := req.Header.Get("signature")
		timestamp := req.Header.Get("timestamp")
		decodedSignature, err := base64.URLEncoding.DecodeString(signature)
		if err != nil {
			w.WriteHeader(http.StatusForbidden)
			return
		}

		verified := VerifySignature([]byte(timestamp), decodedSignature)
		if verified {
			// Verify time
			i, err := strconv.ParseInt(timestamp, 10, 64)
			if err != nil {
				w.WriteHeader(http.StatusForbidden)
				return
			}

			_, err = VerifyTime(i)
			if err != nil {
				w.WriteHeader(http.StatusForbidden)
				return
			}
		} else {
			w.WriteHeader(http.StatusForbidden)
			return
		}

		f(w, req)
	})
}
<|endoftext|>


<commit_before>// Copyright 2013 Xing Xing <mikespook@gmail.com>.
// All rights reserved.
// Use of this source code is governed by a commercial
// license that can be found in the LICENSE file.

package main

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/mikespook/golib/idgen"
	"github.com/mikespook/golib/iptpool"
	"github.com/mikespook/golib/log"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"path"
)

var (
	ErrAccessDeny = errors.New("Access Deny")
)

type httpServer struct {
	conn       net.Listener
	srv        *http.Server
	iptPool    *iptpool.IptPool
	secret     string
	scriptPath string
	idgen      idgen.IdGen
}

func NewHook(addr, scriptPath, secret string) (srv *httpServer) {
	srv = &httpServer{
		srv:        &http.Server{Addr: addr},
		iptPool:    iptpool.NewIptPool(NewLuaIpt),
		scriptPath: scriptPath,
		secret:     secret,
		idgen:      idgen.NewObjectId(),
	}
	return
}

func (s *httpServer) SetTLS(certFile, keyFile string) (err error) {
	s.srv.TLSConfig = &tls.Config{}
	s.srv.TLSConfig.NextProtos = []string{"http/1.1"}
	s.srv.TLSConfig.Certificates = make([]tls.Certificate, 1)
	s.srv.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	return
}

func (s *httpServer) Serve() (err error) {
	s.conn, err = net.Listen("tcp", s.srv.Addr)
	if err != nil {
		return
	}
	if s.srv.TLSConfig != nil {
		s.conn = tls.NewListener(s.conn, s.srv.TLSConfig)
	}
	s.iptPool.OnCreate = func(ipt iptpool.ScriptIpt) error {
		ipt.Init(s.scriptPath)
		ipt.Bind("Call", s.call)
		return nil
	}
	http.HandleFunc("/", s.handler)
	return s.srv.Serve(s.conn)
}

func (s *httpServer) Close() error {
	errstr := ""
	emap := s.iptPool.Free()
	if n := len(emap); n > 0 {
		for k, err := range emap {
			errstr = fmt.Sprintf("%s[%s]: %s\n", errstr, k, err)
		}
	}
	s.conn.Close()
	if errstr != "" {
		return errors.New(errstr)
	}
	return nil
}

func (s *httpServer) verify(p url.Values) bool {
	if (s.secret == "") {
		return true;
	}
	return s.secret == p.Get("secret")
}

func (s *httpServer) handler(w http.ResponseWriter, r *http.Request) {
	u, err := url.Parse(r.RequestURI)
	if err != nil {
		log.Errorf("[%s] %s \"%s\"", r.RemoteAddr, r.RequestURI, err)
		http.Error(w, err.Error(), 500)
		return
	}
	p := u.Query()
	if s.verify(p) { // verify secret token
		log.Errorf("[%s] %s \"%s\"", r.RemoteAddr, r.RequestURI, ErrAccessDeny)
		http.Error(w, err.Error(), 403)
		return
	}
	p.Del("secret")
	params := make(Params)
	params.AddValues(p)
	if r.Method == "POST" {
		data, err := ioutil.ReadAll(r.Body)
		if err != nil {
			log.Errorf("[%s] %s \"%s\"", r.RemoteAddr, r.RequestURI, err)
			http.Error(w, err.Error(), 500)
			return
		}
		defer r.Body.Close()
		if err := params.AddJSON(data); err != nil {
			log.Errorf("[%s] %s \"%s\"", r.RemoteAddr, r.RequestURI, err)
			http.Error(w, err.Error(), 500)
			return
		}
	}
	name := path.Base(u.Path)
	id := s.idgen.Id().(string)
	go func() {
		if err := s.call(id, name, params); err != nil {
			log.Errorf("[%s] %s \"%s\"", r.RemoteAddr,
				r.RequestURI, err.Error())
			return
		}
		log.Messagef("[%s] %s \"Success\"", r.RemoteAddr,
			r.RequestURI)

	}()
	if _, err := w.Write([]byte(id)); err != nil {
		log.Errorf("[%s] %s %s \"%s\"", r.RemoteAddr,
			r.RequestURI, id, err)
	}
}

func (s *httpServer) call(id, name string, params Params) (err error) {
	ipt := s.iptPool.Get()
	defer s.iptPool.Put(ipt)
	ipt.Bind("Id", id)
	return ipt.Exec(name, params)
}
<commit_msg>only GET and POST methods permitted<commit_after>// Copyright 2013 Xing Xing <mikespook@gmail.com>.
// All rights reserved.
// Use of this source code is governed by a commercial
// license that can be found in the LICENSE file.

package main

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/mikespook/golib/idgen"
	"github.com/mikespook/golib/iptpool"
	"github.com/mikespook/golib/log"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"path"
)

var (
	ErrAccessDeny = errors.New("Access Deny")
	ErrMethodNotAllowed = errors.New("Method Not Allowed")
)

type httpServer struct {
	conn       net.Listener
	srv        *http.Server
	iptPool    *iptpool.IptPool
	secret     string
	scriptPath string
	idgen      idgen.IdGen
}

func NewHook(addr, scriptPath, secret string) (srv *httpServer) {
	srv = &httpServer{
		srv:        &http.Server{Addr: addr},
		iptPool:    iptpool.NewIptPool(NewLuaIpt),
		scriptPath: scriptPath,
		secret:     secret,
		idgen:      idgen.NewObjectId(),
	}
	return
}

func (s *httpServer) SetTLS(certFile, keyFile string) (err error) {
	s.srv.TLSConfig = &tls.Config{}
	s.srv.TLSConfig.NextProtos = []string{"http/1.1"}
	s.srv.TLSConfig.Certificates = make([]tls.Certificate, 1)
	s.srv.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	return
}

func (s *httpServer) Serve() (err error) {
	s.conn, err = net.Listen("tcp", s.srv.Addr)
	if err != nil {
		return
	}
	if s.srv.TLSConfig != nil {
		s.conn = tls.NewListener(s.conn, s.srv.TLSConfig)
	}
	s.iptPool.OnCreate = func(ipt iptpool.ScriptIpt) error {
		ipt.Init(s.scriptPath)
		ipt.Bind("Call", s.call)
		return nil
	}
	http.HandleFunc("/", s.handler)
	return s.srv.Serve(s.conn)
}

func (s *httpServer) Close() error {
	errstr := ""
	emap := s.iptPool.Free()
	if n := len(emap); n > 0 {
		for k, err := range emap {
			errstr = fmt.Sprintf("%s[%s]: %s\n", errstr, k, err)
		}
	}
	s.conn.Close()
	if errstr != "" {
		return errors.New(errstr)
	}
	return nil
}

func (s *httpServer) verify(p url.Values) bool {
	if (s.secret == "") {
		return true;
	}
	return s.secret == p.Get("secret")
}

func (s *httpServer) handler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
		case "GET":
			fallthrough
		case "POST":
		default:
			log.Errorf("[%s] %s \"%s %s\"", r.RemoteAddr, r.RequestURI, ErrMethodNotAllowed, r.Method)
			http.Error(w, ErrMethodNotAllowed.Error(), 405)
			return
	}
	u, err := url.Parse(r.RequestURI)
	if err != nil {
		log.Errorf("[%s] %s \"%s\"", r.RemoteAddr, r.RequestURI, err)
		http.Error(w, err.Error(), 500)
		return
	}
	p := u.Query()
	if ! s.verify(p) { // verify secret token
		log.Errorf("[%s] %s \"%s\"", r.RemoteAddr, r.RequestURI, ErrAccessDeny)
		http.Error(w, err.Error(), 403)
		return
	}
	p.Del("secret")
	params := make(Params)
	params.AddValues(p)
	if r.Method == "POST" {
		data, err := ioutil.ReadAll(r.Body)
		if err != nil {
			log.Errorf("[%s] %s \"%s\"", r.RemoteAddr, r.RequestURI, err)
			http.Error(w, err.Error(), 500)
			return
		}
		defer r.Body.Close()
		if err := params.AddJSON(data); err != nil {
			log.Errorf("[%s] %s \"%s\"", r.RemoteAddr, r.RequestURI, err)
			http.Error(w, err.Error(), 500)
			return
		}
	}
	name := path.Base(u.Path)
	id := s.idgen.Id().(string)
	go func() {
		if err := s.call(id, name, params); err != nil {
			log.Errorf("[%s] %s \"%s\"", r.RemoteAddr,
				r.RequestURI, err.Error())
			return
		}
		log.Messagef("[%s] %s \"Success\"", r.RemoteAddr,
			r.RequestURI)

	}()
	if _, err := w.Write([]byte(id)); err != nil {
		log.Errorf("[%s] %s %s \"%s\"", r.RemoteAddr,
			r.RequestURI, id, err)
	}
}

func (s *httpServer) call(id, name string, params Params) (err error) {
	ipt := s.iptPool.Get()
	defer s.iptPool.Put(ipt)
	ipt.Bind("Id", id)
	return ipt.Exec(name, params)
}
<|endoftext|>


<commit_before>// Package operator is the primary package of the Operator Admin API
package operator

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"runtime"

	"github.com/bakins/go-metrics-middleware"
	"github.com/bakins/net-http-recover"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/justinas/alice"
	"github.com/mistifyio/mistify-operator-admin/metrics"
)

type (
	// HTTPResponse is a wrapper for http.ResponseWriter which provides access
	// to several convenience methods
	HTTPResponse struct {
		http.ResponseWriter
	}

	// HTTPError contains information for http error responses
	HTTPError struct {
		Message string   `json:"message"`
		Code    int      `json:"code"`
		Stack   []string `json:"stack"`
	}
)

// Run starts the server
func Run(port uint) error {
	router := mux.NewRouter()
	router.StrictSlash(true)

	m, _ := metrics.GetObject(nil)
	mw := mmw.New(m)

	// Common middleware applied to every request
	commonMiddleware := alice.New(
		func(h http.Handler) http.Handler {
			return handlers.CombinedLoggingHandler(os.Stdout, h)
		},
		handlers.CompressHandler,
		func(h http.Handler) http.Handler {
			return recovery.Handler(os.Stderr, h, true)
		},
		mw.HandlerWrapper("foo"),
	)

	// NOTE: Due to weirdness with PrefixPath and StrictSlash, can't just pass
	// a prefixed subrouter to the register functions and have the base path
	// work cleanly. The register functions need to add a base path handler to
	// the main router before setting subhandlers on either main or subrouter

	// Register the various routes
	RegisterPermissionRoutes("/permissions", router)
	RegisterNetworkRoutes("/networks", router)
	RegisterIPRangeRoutes("/ipranges", router)
	RegisterHypervisorRoutes("/hypervisors", router)
	RegisterProjectRoutes("/projects", router)
	RegisterUserRoutes("/users", router)
	RegisterFlavorRoutes("/flavors", router)
	RegisterConfigRoutes("/config", router)

	server := &http.Server{
		Addr:           fmt.Sprintf(":%d", port),
		Handler:        commonMiddleware.Then(router),
		MaxHeaderBytes: 1 << 20,
	}
	return server.ListenAndServe()
}

// JSON writes appropriate headers and JSON body to the http response
func (hr *HTTPResponse) JSON(code int, obj interface{}) {
	hr.Header().Set("Content-Type", "application/json")
	hr.WriteHeader(code)
	encoder := json.NewEncoder(hr)
	if err := encoder.Encode(obj); err != nil {
		hr.JSONError(http.StatusInternalServerError, err)
	}
}

// JSONError prepares an HTTPError with a stack trace and writes it with
// HTTPResponse.JSON
func (hr *HTTPResponse) JSONError(code int, err error) {
	httpError := &HTTPError{
		Message: err.Error(),
		Code:    code,
		Stack:   make([]string, 0, 4),
	}
	for i := 1; ; i++ { //
		pc, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}
		// Print this much at least.  If we can't find the source, it won't show.
		httpError.Stack = append(httpError.Stack, fmt.Sprintf("%s:%d (0x%x)", file, line, pc))
	}
	hr.JSON(code, httpError)
}

// JSONMsg is a convenience method to write a JSON response with just a message
// string
func (hr *HTTPResponse) JSONMsg(code int, msg string) {
	msgObj := map[string]string{
		"message": msg,
	}
	hr.JSON(code, msgObj)
}
<commit_msg>Log-fatal this error for now (work around in a later version)<commit_after>// Package operator is the primary package of the Operator Admin API
package operator

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"runtime"

	"github.com/bakins/go-metrics-middleware"
	"github.com/bakins/net-http-recover"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/justinas/alice"
	"github.com/mistifyio/mistify-operator-admin/metrics"
)

type (
	// HTTPResponse is a wrapper for http.ResponseWriter which provides access
	// to several convenience methods
	HTTPResponse struct {
		http.ResponseWriter
	}

	// HTTPError contains information for http error responses
	HTTPError struct {
		Message string   `json:"message"`
		Code    int      `json:"code"`
		Stack   []string `json:"stack"`
	}
)

// Run starts the server
func Run(port uint) error {
	router := mux.NewRouter()
	router.StrictSlash(true)

	m, err := metrics.GetObject(nil)
	if err != nil {
		log.Fatal(err)
	}
	mw := mmw.New(m)

	// Common middleware applied to every request
	commonMiddleware := alice.New(
		func(h http.Handler) http.Handler {
			return handlers.CombinedLoggingHandler(os.Stdout, h)
		},
		handlers.CompressHandler,
		func(h http.Handler) http.Handler {
			return recovery.Handler(os.Stderr, h, true)
		},
		mw.HandlerWrapper("foo"),
	)

	// NOTE: Due to weirdness with PrefixPath and StrictSlash, can't just pass
	// a prefixed subrouter to the register functions and have the base path
	// work cleanly. The register functions need to add a base path handler to
	// the main router before setting subhandlers on either main or subrouter

	// Register the various routes
	RegisterPermissionRoutes("/permissions", router)
	RegisterNetworkRoutes("/networks", router)
	RegisterIPRangeRoutes("/ipranges", router)
	RegisterHypervisorRoutes("/hypervisors", router)
	RegisterProjectRoutes("/projects", router)
	RegisterUserRoutes("/users", router)
	RegisterFlavorRoutes("/flavors", router)
	RegisterConfigRoutes("/config", router)

	server := &http.Server{
		Addr:           fmt.Sprintf(":%d", port),
		Handler:        commonMiddleware.Then(router),
		MaxHeaderBytes: 1 << 20,
	}
	return server.ListenAndServe()
}

// JSON writes appropriate headers and JSON body to the http response
func (hr *HTTPResponse) JSON(code int, obj interface{}) {
	hr.Header().Set("Content-Type", "application/json")
	hr.WriteHeader(code)
	encoder := json.NewEncoder(hr)
	if err := encoder.Encode(obj); err != nil {
		hr.JSONError(http.StatusInternalServerError, err)
	}
}

// JSONError prepares an HTTPError with a stack trace and writes it with
// HTTPResponse.JSON
func (hr *HTTPResponse) JSONError(code int, err error) {
	httpError := &HTTPError{
		Message: err.Error(),
		Code:    code,
		Stack:   make([]string, 0, 4),
	}
	for i := 1; ; i++ { //
		pc, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}
		// Print this much at least.  If we can't find the source, it won't show.
		httpError.Stack = append(httpError.Stack, fmt.Sprintf("%s:%d (0x%x)", file, line, pc))
	}
	hr.JSON(code, httpError)
}

// JSONMsg is a convenience method to write a JSON response with just a message
// string
func (hr *HTTPResponse) JSONMsg(code int, msg string) {
	msgObj := map[string]string{
		"message": msg,
	}
	hr.JSON(code, msgObj)
}
<|endoftext|>


<commit_before>/*
	Package hwio implements a simple Arduino-like interface for controlling
	hardware I/O, with configurable backends depending on the device.
*/
package hwio

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

type BitShiftOrder byte

const (
	LSBFIRST BitShiftOrder = iota
	MSBFIRST
)

// Reference to driver we're using
var driver HardwareDriver

// Retrieved from the driver, this is the map of the hardware pins supported by
// the driver and their capabilities
var definedPins HardwarePinMap

// A private type for associating a pin's definition with the current IO mode
// and any other dynamic properties of the pin.
type assignedPin struct {
	pin    Pin    // pin being assigned
	module Module // module that has assigned this pin
	// pinIOMode PinIOMode // mode that was assigned to this pin
}

// A map of pin numbers to the assigned dynamic properties of the pin. This is
// set by PinMode when errorChecking is on, and can be used by other functions
// to determine if the request is valid given the assigned properties of the pin.
var assignedPins map[Pin]*assignedPin

// If set to true, functions should test that their constraints are met.
// e.g. test that the pin is capable of doing what is asked. This can be set
// with SetErrorChecking(). Setting to false bypasses checks for performance.
// By default turned on, which is a better default for beginners.
var errorChecking bool = true

// init() attempts to determine from the environment what the driver is. The
// intent is that the consumer of the library would not generally have to worry
// about it, it would just work. If it cannot determine the driver, it doesn't
// set the driver to anything.
func init() {
	assignedPins = make(map[Pin]*assignedPin)
	determineDriver()
}

func fileExists(name string) bool {
	_, err := os.Stat(name)
	if err != nil {
		return false
	}

	return true
}

// Work out the driver from environment if we can. If we have any problems,
// don't generate an error, just return with the driver not set.
// @todo use reflection to determine all implementors of the driver interface, and
// @todo   call a method on the interface to self-detect. init and
// @todo   constructor of drivers should do no setup in this case, esp of hardware
func determineDriver() {
	uname, e := exec.Command("uname", "-a").Output()
	if e != nil {
		return
	}

	s := string(uname)
	if strings.Contains(s, "beaglebone") {
		if fileExists("/sys/kernel/debug/omap_mux") {
			panic("BeagleBone devices with kernels prior to 3.7 are no longer supported. The old driver is available in the 'legacy' branch.")
		} else {
			SetDriver(new(BeagleBoneBlackDriver))
		}
	} else if strings.Contains(s, "raspberrypi") || strings.Contains(s, "adafruit") {
		// @todo test for pre-3.7 kernels
		SetDriver(new(RaspberryPiDTDriver))
	} else {
		// file, e := os.Open("/etc/rpi-issue") // test for existence (only)
		// if e == nil {
		// 	file.Close()
		// 	SetDriver(new(RaspberryPiDriver))
		// } else {
		fmt.Printf("Unable to select a suitable driver for this board.\n%s\n", s)
		// }
	}
}

// Check if the driver is assigned. If not, return an error to indicate that,
// otherwise return no error.
func assertDriver() error {
	if driver == nil {
		return errors.New("hwio has no configured driver")
	}
	return nil
}

// Set the driver. Also calls Init on the driver, and loads the capabilities
// of the device.
func SetDriver(d HardwareDriver) {
	driver = d
	e := driver.Init()
	if e != nil {
		fmt.Printf("Could not initialise driver: %s", e)
	}
	definedPins = driver.PinMap()
}

// Retrieve the current hardware driver.
func GetDriver() HardwareDriver {
	return driver
}

// Returns a map of the hardware pins. This will only work once the driver is
// set.
func GetDefinedPins() HardwarePinMap {
	return definedPins
}

// Returns a Pin given a canonical name for the pin.
// e.g. to get the pin number of P8.13 on a beaglebone,
//     pin := hwio.GetPin("P8.13")
// Order of search is:
// - search hwRefs in the pin map in order.
// This function should not generally be relied on for performance. For max speed, call this
// for each pin you use once on init, and use the returned Pin values thereafter.
// Search is case sensitive at the moment
// @todo GetPin: consider making it case-insensitive on name
// @todo GetPin: consider allowing an int or int as string to identify logical pin directly
func GetPin(pinName string) (Pin, error) {
	pl := strings.ToLower(pinName)
	for pin, pinDef := range definedPins {
		for _, name := range pinDef.names {
			if strings.ToLower(name) == pl {
				return pin, nil
			}
		}
	}

	return Pin(0), fmt.Errorf("Could not find a pin called %s", pinName)
}

// Shortcut for calling GetPin and then PinMode.
func GetPinWithMode(cname string, mode PinIOMode) (pin Pin, e error) {
	p, e := GetPin(cname)
	if e != nil {
		return
	}

	e = PinMode(p, mode)
	return p, e
}

// Set error checking. This should be called before pin assignments.
func SetErrorChecking(check bool) {
	errorChecking = check
}

// Helper function to get GPIO module
func GetGPIOModule() (GPIOModule, error) {
	m, e := GetModule("gpio")
	if e != nil {
		return nil, e
	}

	if m == nil {
		return nil, errors.New("Driver does not support GPIO")
	}

	return m.(GPIOModule), nil
}

// Given an internal pin number, return the canonical name for the pin, as defined by the driver. If the pin
// is not to the driver, return "".
func PinName(pin Pin) string {
	p := definedPins[pin]
	if p == nil {
		return ""
	}
	return p.names[0]
}

// Set the mode of a pin. Analogous to Arduino pin mode.
func PinMode(pin Pin, mode PinIOMode) error {
	gpio, e := GetGPIOModule()
	if e != nil {
		return e
	}

	return gpio.PinMode(pin, mode)
}

// Assign a pin to a module. This is typically called by modules when they allocate pins. If the pin is already assigned,
// an error is generated. ethod is public in case it is needed to hack around default driver settings.
func AssignPin(pin Pin, module Module) error {
	if a := assignedPins[pin]; a != nil {
		return fmt.Errorf("Pin %d is already assigned to module %s", pin, a.module.GetName())
	}
	assignedPins[pin] = &assignedPin{pin, module}
	return nil
}

// Assign a set of pins. Method is public in case it is needed to hack around default driver settings.
func AssignPins(pins PinList, module Module) error {
	for _, pin := range pins {
		e := AssignPin(pin, module)
		if e != nil {
			return e
		}
	}
	return nil
}

// Unassign a pin. Method is public in case it is needed to hack around default driver settings.
func UnassignPin(pin Pin) error {
	delete(assignedPins, pin)
	return nil
}

// Unassign a set of pins. Method is public in case it is needed to hack around default driver settings.
func UnassignPins(pins PinList) (er error) {
	er = nil

	for _, pin := range pins {
		e := UnassignPin(pin)
		if e != nil {
			er = e
		}
	}

	return
}

// Write a value to a digital pin
func DigitalWrite(pin Pin, value int) (e error) {
	gpio, e := GetGPIOModule()
	if e != nil {
		return e
	}

	return gpio.DigitalWrite(pin, value)
}

// Read a value from a digital pin
func DigitalRead(pin Pin) (result int, e error) {
	// @todo consider memoizing
	gpio, e := GetGPIOModule()
	if e != nil {
		return 0, e
	}

	return gpio.DigitalRead(pin)
}

// Helper function to get GPIO module
func GetAnalogModule() (AnalogModule, error) {
	m, e := GetModule("analog")
	if e != nil {
		return nil, e
	}

	if m == nil {
		return nil, errors.New("Driver does not support analog")
	}

	return m.(AnalogModule), nil
}

// Read an analog value from a pin. The range of values is hardware driver dependent.
func AnalogRead(pin Pin) (int, error) {
	analog, e := GetAnalogModule()
	if e != nil {
		return 0, e
	}

	return analog.AnalogRead(pin)
}

// Delay execution by the specified number of milliseconds. This is a helper
// function for similarity with Arduino. It is implemented using standard go
// time package.
func Delay(duration int) {
	time.Sleep(time.Duration(duration) * time.Millisecond)
}

// Delay execution by the specified number of microseconds. This is a helper
// function for similarity with Arduino. It is implemented using standard go
// time package
func DelayMicroseconds(duration int) {
	time.Sleep(time.Duration(duration) * time.Microsecond)
}

// @todo DebugPinMap: sort
func DebugPinMap() {
	fmt.Println("HardwarePinMap:")
	for key, val := range definedPins {
		fmt.Printf("Pin %d: %s\n", key, val.String())
	}
	fmt.Printf("\n")
}

// The approximate mapping of Arduino shiftOut, this shifts a byte out on the
// data pin, pulsing the clock pin high and then low.
func ShiftOut(dataPin Pin, clockPin Pin, value uint, order BitShiftOrder) error {
	return ShiftOutSize(dataPin, clockPin, value, order, 8)
}

// More generic version of ShiftOut which shifts out n of data from value. The
// value shifted out is always the lowest n bits of the value, but 'order'
// determines whether the msb or lsb from that value are shifted first
func ShiftOutSize(dataPin Pin, clockPin Pin, value uint, order BitShiftOrder, n uint) error {
	bit := uint(0)
	v := value
	mask := uint(1) << (n - 1)
	for i := uint(0); i < n; i++ {
		// get the next bit
		if order == LSBFIRST {
			bit = v & 1
			v = v >> 1
		} else {
			bit = v & mask
			if bit != 0 {
				bit = 1
			}
			v = v << 1
		}
		// write to data pin
		e := DigitalWrite(dataPin, int(bit))
		if e != nil {
			return e
		}
		// pulse clock high and then low
		e = DigitalWrite(clockPin, HIGH)
		if e != nil {
			return e
		}
		DigitalWrite(clockPin, LOW)
	}
	return nil
}

// Given an integer and a list of GPIO pins (that must have been set up as outputs), write the integer across
// the pins. The number of bits is determined by the length of the pins. The most-significant output pin is first.
// Bits are written MSB first.
// Maximum number of bits that can be shifted is 32.
// Note that the bits are not written out instantaneously, although very quickly. If you need instantaneous changing of
// all pins, you need to consider an output buffer.
func WriteUIntToPins(value uint32, pins []Pin) error {
	if len(pins) > 31 {
		return errors.New("WriteUIntToPins only supports up to 32 bits")
	}

	bit := uint32(0)
	v := value
	mask := uint32(1) << uint32((len(pins) - 1))
	for i := uint32(0); i < uint32(len(pins)); i++ {
		bit = v & mask
		if bit != 0 {
			bit = 1
		}
		v = v << 1
		// write to data pin
		//		fmt.Printf("Writing %s to pin %s\n", bit, pins[i])
		e := DigitalWrite(pins[i], int(bit))
		if e != nil {
			return e
		}
	}
	return nil
}

// Write a string to a file and close it again.
func WriteStringToFile(filename string, value string) error {
	//	fmt.Printf("writing %s to file %s\n", value, filename)
	f, e := os.OpenFile(filename, os.O_WRONLY|os.O_TRUNC, 0666)
	if e != nil {
		return e
	}
	defer f.Close()

	_, e = f.WriteString(value)
	return e
}

// Given a glob pattern, return the full path of the first matching file
func findFirstMatchingFile(glob string) (string, error) {
	matches, e := filepath.Glob(glob)
	if e != nil {
		return "", e
	}

	if len(matches) >= 1 {
		return matches[0], nil
	}
	return "", nil
}

func Map(value int, fromLow int, fromHigh int, toLow int, toHigh int) int {
	return (value-fromLow)*(toHigh-toLow)/(fromHigh-fromLow) + toLow
}

// Given a high and low byte, combine to form a single 16-bit value
func UInt16FromUInt8(highByte byte, lowByte byte) uint16 {
	return uint16(uint16(highByte)<<8) | uint16(lowByte)
}

func ReverseBytes16(value uint16) uint16 {
	// @todo implement ReverseBytes16()
	return 0
}

func ReverseBytes32(value uint32) uint32 {
	// @todo implement ReverseBytes32()
	return 0
}

// Get a module by name. If driver is not set, it will return an error. If the driver does not support that module,
//
func GetModule(name string) (Module, error) {
	driver := GetDriver()
	if driver == nil {
		return nil, errors.New("GetModule: Driver is not set")
	}

	modules := driver.GetModules()
	return modules[name], nil
}

// This is the interface that hardware drivers implement.
type HardwareDriver interface {
	// Initialise the driver after creation
	Init() (e error)

	// Return a module by name, or nil if undefined. The module names can be different between types of boards.
	GetModules() map[string]Module

	// Return the pin map for the driver, listing all supported pins and their capabilities
	PinMap() (pinMap HardwarePinMap)

	// Close the driver before destruction
	Close()
}
<commit_msg>helper function Led for simple turning on/off of on-board LEDs<commit_after>/*
	Package hwio implements a simple Arduino-like interface for controlling
	hardware I/O, with configurable backends depending on the device.
*/
package hwio

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

type BitShiftOrder byte

const (
	LSBFIRST BitShiftOrder = iota
	MSBFIRST
)

// Reference to driver we're using
var driver HardwareDriver

// Retrieved from the driver, this is the map of the hardware pins supported by
// the driver and their capabilities
var definedPins HardwarePinMap

// A private type for associating a pin's definition with the current IO mode
// and any other dynamic properties of the pin.
type assignedPin struct {
	pin    Pin    // pin being assigned
	module Module // module that has assigned this pin
	// pinIOMode PinIOMode // mode that was assigned to this pin
}

// A map of pin numbers to the assigned dynamic properties of the pin. This is
// set by PinMode when errorChecking is on, and can be used by other functions
// to determine if the request is valid given the assigned properties of the pin.
var assignedPins map[Pin]*assignedPin

// If set to true, functions should test that their constraints are met.
// e.g. test that the pin is capable of doing what is asked. This can be set
// with SetErrorChecking(). Setting to false bypasses checks for performance.
// By default turned on, which is a better default for beginners.
var errorChecking bool = true

// init() attempts to determine from the environment what the driver is. The
// intent is that the consumer of the library would not generally have to worry
// about it, it would just work. If it cannot determine the driver, it doesn't
// set the driver to anything.
func init() {
	assignedPins = make(map[Pin]*assignedPin)
	determineDriver()
}

func fileExists(name string) bool {
	_, err := os.Stat(name)
	if err != nil {
		return false
	}

	return true
}

// Work out the driver from environment if we can. If we have any problems,
// don't generate an error, just return with the driver not set.
// @todo use reflection to determine all implementors of the driver interface, and
// @todo   call a method on the interface to self-detect. init and
// @todo   constructor of drivers should do no setup in this case, esp of hardware
func determineDriver() {
	uname, e := exec.Command("uname", "-a").Output()
	if e != nil {
		return
	}

	s := string(uname)
	if strings.Contains(s, "beaglebone") {
		if fileExists("/sys/kernel/debug/omap_mux") {
			panic("BeagleBone devices with kernels prior to 3.7 are no longer supported. The old driver is available in the 'legacy' branch.")
		} else {
			SetDriver(new(BeagleBoneBlackDriver))
		}
	} else if strings.Contains(s, "raspberrypi") || strings.Contains(s, "adafruit") {
		// @todo test for pre-3.7 kernels
		SetDriver(new(RaspberryPiDTDriver))
	} else {
		// file, e := os.Open("/etc/rpi-issue") // test for existence (only)
		// if e == nil {
		// 	file.Close()
		// 	SetDriver(new(RaspberryPiDriver))
		// } else {
		fmt.Printf("Unable to select a suitable driver for this board.\n%s\n", s)
		// }
	}
}

// Check if the driver is assigned. If not, return an error to indicate that,
// otherwise return no error.
func assertDriver() error {
	if driver == nil {
		return errors.New("hwio has no configured driver")
	}
	return nil
}

// Set the driver. Also calls Init on the driver, and loads the capabilities
// of the device.
func SetDriver(d HardwareDriver) {
	driver = d
	e := driver.Init()
	if e != nil {
		fmt.Printf("Could not initialise driver: %s", e)
	}
	definedPins = driver.PinMap()
}

// Retrieve the current hardware driver.
func GetDriver() HardwareDriver {
	return driver
}

// Returns a map of the hardware pins. This will only work once the driver is
// set.
func GetDefinedPins() HardwarePinMap {
	return definedPins
}

// Returns a Pin given a canonical name for the pin.
// e.g. to get the pin number of P8.13 on a beaglebone,
//     pin := hwio.GetPin("P8.13")
// Order of search is:
// - search hwRefs in the pin map in order.
// This function should not generally be relied on for performance. For max speed, call this
// for each pin you use once on init, and use the returned Pin values thereafter.
// Search is case sensitive at the moment
// @todo GetPin: consider making it case-insensitive on name
// @todo GetPin: consider allowing an int or int as string to identify logical pin directly
func GetPin(pinName string) (Pin, error) {
	pl := strings.ToLower(pinName)
	for pin, pinDef := range definedPins {
		for _, name := range pinDef.names {
			if strings.ToLower(name) == pl {
				return pin, nil
			}
		}
	}

	return Pin(0), fmt.Errorf("Could not find a pin called %s", pinName)
}

// Shortcut for calling GetPin and then PinMode.
func GetPinWithMode(cname string, mode PinIOMode) (pin Pin, e error) {
	p, e := GetPin(cname)
	if e != nil {
		return
	}

	e = PinMode(p, mode)
	return p, e
}

// Set error checking. This should be called before pin assignments.
func SetErrorChecking(check bool) {
	errorChecking = check
}

// Helper function to get GPIO module
func GetGPIOModule() (GPIOModule, error) {
	m, e := GetModule("gpio")
	if e != nil {
		return nil, e
	}

	if m == nil {
		return nil, errors.New("Driver does not support GPIO")
	}

	return m.(GPIOModule), nil
}

// Given an internal pin number, return the canonical name for the pin, as defined by the driver. If the pin
// is not to the driver, return "".
func PinName(pin Pin) string {
	p := definedPins[pin]
	if p == nil {
		return ""
	}
	return p.names[0]
}

// Set the mode of a pin. Analogous to Arduino pin mode.
func PinMode(pin Pin, mode PinIOMode) error {
	gpio, e := GetGPIOModule()
	if e != nil {
		return e
	}

	return gpio.PinMode(pin, mode)
}

// Assign a pin to a module. This is typically called by modules when they allocate pins. If the pin is already assigned,
// an error is generated. ethod is public in case it is needed to hack around default driver settings.
func AssignPin(pin Pin, module Module) error {
	if a := assignedPins[pin]; a != nil {
		return fmt.Errorf("Pin %d is already assigned to module %s", pin, a.module.GetName())
	}
	assignedPins[pin] = &assignedPin{pin, module}
	return nil
}

// Assign a set of pins. Method is public in case it is needed to hack around default driver settings.
func AssignPins(pins PinList, module Module) error {
	for _, pin := range pins {
		e := AssignPin(pin, module)
		if e != nil {
			return e
		}
	}
	return nil
}

// Unassign a pin. Method is public in case it is needed to hack around default driver settings.
func UnassignPin(pin Pin) error {
	delete(assignedPins, pin)
	return nil
}

// Unassign a set of pins. Method is public in case it is needed to hack around default driver settings.
func UnassignPins(pins PinList) (er error) {
	er = nil

	for _, pin := range pins {
		e := UnassignPin(pin)
		if e != nil {
			er = e
		}
	}

	return
}

// Write a value to a digital pin
func DigitalWrite(pin Pin, value int) (e error) {
	gpio, e := GetGPIOModule()
	if e != nil {
		return e
	}

	return gpio.DigitalWrite(pin, value)
}

// Read a value from a digital pin
func DigitalRead(pin Pin) (result int, e error) {
	// @todo consider memoizing
	gpio, e := GetGPIOModule()
	if e != nil {
		return 0, e
	}

	return gpio.DigitalRead(pin)
}

// Helper function to get GPIO module
func GetAnalogModule() (AnalogModule, error) {
	m, e := GetModule("analog")
	if e != nil {
		return nil, e
	}

	if m == nil {
		return nil, errors.New("Driver does not support analog")
	}

	return m.(AnalogModule), nil
}

// Read an analog value from a pin. The range of values is hardware driver dependent.
func AnalogRead(pin Pin) (int, error) {
	analog, e := GetAnalogModule()
	if e != nil {
		return 0, e
	}

	return analog.AnalogRead(pin)
}

// Helper to turn an on-board LED on or off. Uses LED module
func Led(name string, on bool) error {
	m, e := GetModule("leds")
	if e != nil {
		return e
	}

	leds := m.(LEDModule)
	led, e := leds.GetLED(name)
	if e != nil {
		return e
	}

	e = led.SetTrigger("none")
	if e != nil {
		return e
	}

	return led.SetOn(on)
}

// Delay execution by the specified number of milliseconds. This is a helper
// function for similarity with Arduino. It is implemented using standard go
// time package.
func Delay(duration int) {
	time.Sleep(time.Duration(duration) * time.Millisecond)
}

// Delay execution by the specified number of microseconds. This is a helper
// function for similarity with Arduino. It is implemented using standard go
// time package
func DelayMicroseconds(duration int) {
	time.Sleep(time.Duration(duration) * time.Microsecond)
}

// @todo DebugPinMap: sort
func DebugPinMap() {
	fmt.Println("HardwarePinMap:")
	for key, val := range definedPins {
		fmt.Printf("Pin %d: %s\n", key, val.String())
	}
	fmt.Printf("\n")
}

// The approximate mapping of Arduino shiftOut, this shifts a byte out on the
// data pin, pulsing the clock pin high and then low.
func ShiftOut(dataPin Pin, clockPin Pin, value uint, order BitShiftOrder) error {
	return ShiftOutSize(dataPin, clockPin, value, order, 8)
}

// More generic version of ShiftOut which shifts out n of data from value. The
// value shifted out is always the lowest n bits of the value, but 'order'
// determines whether the msb or lsb from that value are shifted first
func ShiftOutSize(dataPin Pin, clockPin Pin, value uint, order BitShiftOrder, n uint) error {
	bit := uint(0)
	v := value
	mask := uint(1) << (n - 1)
	for i := uint(0); i < n; i++ {
		// get the next bit
		if order == LSBFIRST {
			bit = v & 1
			v = v >> 1
		} else {
			bit = v & mask
			if bit != 0 {
				bit = 1
			}
			v = v << 1
		}
		// write to data pin
		e := DigitalWrite(dataPin, int(bit))
		if e != nil {
			return e
		}
		// pulse clock high and then low
		e = DigitalWrite(clockPin, HIGH)
		if e != nil {
			return e
		}
		DigitalWrite(clockPin, LOW)
	}
	return nil
}

// Given an integer and a list of GPIO pins (that must have been set up as outputs), write the integer across
// the pins. The number of bits is determined by the length of the pins. The most-significant output pin is first.
// Bits are written MSB first.
// Maximum number of bits that can be shifted is 32.
// Note that the bits are not written out instantaneously, although very quickly. If you need instantaneous changing of
// all pins, you need to consider an output buffer.
func WriteUIntToPins(value uint32, pins []Pin) error {
	if len(pins) > 31 {
		return errors.New("WriteUIntToPins only supports up to 32 bits")
	}

	bit := uint32(0)
	v := value
	mask := uint32(1) << uint32((len(pins) - 1))
	for i := uint32(0); i < uint32(len(pins)); i++ {
		bit = v & mask
		if bit != 0 {
			bit = 1
		}
		v = v << 1
		// write to data pin
		//		fmt.Printf("Writing %s to pin %s\n", bit, pins[i])
		e := DigitalWrite(pins[i], int(bit))
		if e != nil {
			return e
		}
	}
	return nil
}

// Write a string to a file and close it again.
func WriteStringToFile(filename string, value string) error {
	//	fmt.Printf("writing %s to file %s\n", value, filename)
	f, e := os.OpenFile(filename, os.O_WRONLY|os.O_TRUNC, 0666)
	if e != nil {
		return e
	}
	defer f.Close()

	_, e = f.WriteString(value)
	return e
}

// Given a glob pattern, return the full path of the first matching file
func findFirstMatchingFile(glob string) (string, error) {
	matches, e := filepath.Glob(glob)
	if e != nil {
		return "", e
	}

	if len(matches) >= 1 {
		return matches[0], nil
	}
	return "", nil
}

func Map(value int, fromLow int, fromHigh int, toLow int, toHigh int) int {
	return (value-fromLow)*(toHigh-toLow)/(fromHigh-fromLow) + toLow
}

// Given a high and low byte, combine to form a single 16-bit value
func UInt16FromUInt8(highByte byte, lowByte byte) uint16 {
	return uint16(uint16(highByte)<<8) | uint16(lowByte)
}

func ReverseBytes16(value uint16) uint16 {
	// @todo implement ReverseBytes16()
	return 0
}

func ReverseBytes32(value uint32) uint32 {
	// @todo implement ReverseBytes32()
	return 0
}

// Get a module by name. If driver is not set, it will return an error. If the driver does not support that module,
//
func GetModule(name string) (Module, error) {
	driver := GetDriver()
	if driver == nil {
		return nil, errors.New("GetModule: Driver is not set")
	}

	modules := driver.GetModules()
	return modules[name], nil
}

// This is the interface that hardware drivers implement.
type HardwareDriver interface {
	// Initialise the driver after creation
	Init() (e error)

	// Return a module by name, or nil if undefined. The module names can be different between types of boards.
	GetModules() map[string]Module

	// Return the pin map for the driver, listing all supported pins and their capabilities
	PinMap() (pinMap HardwarePinMap)

	// Close the driver before destruction
	Close()
}
<|endoftext|>


<commit_before>package godless

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"io/ioutil"
	"net/http"

	ipfs "github.com/ipfs/go-ipfs-api"
	"github.com/pkg/errors"
)

type IPFSPath string

func (path IPFSPath) Path() string {
	return string(path)
}

func castIPFSPath(addr RemoteStoreAddress) IPFSPath {
	path, ok := addr.(IPFSPath)

	if !ok {
		panic("addr was not IPFSPath")
	}

	return path
}

type IPFSPeer struct {
	Url    string
	Client *http.Client
	Shell  *ipfs.Shell
}

type IPFSRecord struct {
	Namespace Namespace
}

type IPFSIndex struct {
	Index map[string][]IPFSPath
}

func (index IPFSIndex) remoteIndex() RemoteNamespaceIndex {
	out := RemoteNamespaceIndex{}

	for indexKey, paths := range index.Index {
		addrs := make([]RemoteStoreAddress, len(paths))
		for i, p := range paths {
			addrs[i] = p
		}
		out.Index[indexKey] = addrs
	}

	return out
}

func makeIpfsIndex(index RemoteNamespaceIndex) IPFSIndex {
	out := IPFSIndex{}

	for indexKey, addrs := range index.Index {
		paths := make([]IPFSPath, len(addrs))
		for i, a := range addrs {
			paths[i] = castIPFSPath(a)
		}
		out.Index[indexKey] = paths
	}

	return out
}

func MakeIPFSPeer(url string) RemoteStore {
	peer := &IPFSPeer{
		Url:    url,
		Client: defaultHttpClient(),
	}

	return peer
}

func (peer *IPFSPeer) Connect() error {
	peer.Shell = ipfs.NewShellWithClient(peer.Url, peer.Client)

	if !peer.Shell.IsUp() {
		return fmt.Errorf("IPFSPeer is not up at '%v'", peer.Url)
	}

	return nil
}

func (peer *IPFSPeer) Disconnect() error {
	// Nothing to do.
	return nil
}

func (peer *IPFSPeer) AddIndex(index RemoteNamespaceIndex) (RemoteStoreAddress, error) {
	chunk := makeIpfsIndex(index)

	path, err := peer.add(&chunk)

	if err != nil {
		return nil, errors.Wrap(err, "IPFSPeer.AddIndex failed")
	}

	return path, nil
}

func (peer *IPFSPeer) CatIndex(addr RemoteStoreAddress) (RemoteNamespaceIndex, error) {
	path := castIPFSPath(addr)

	chunk := IPFSIndex{}
	caterr := peer.cat(path, &chunk)

	if caterr != nil {
		return EMPTY_INDEX, errors.Wrap(caterr, "IPFSPeer.CatNamespace failed")
	}

	index := chunk.remoteIndex()
	return index, nil
}

func (peer *IPFSPeer) AddNamespace(record RemoteNamespaceRecord) (RemoteStoreAddress, error) {
	chunk := IPFSRecord{
		Namespace: record.Namespace,
	}

	path, err := peer.add(&chunk)

	if err != nil {
		return nil, errors.Wrap(err, "IPFSPeer.AddNamespace failed")
	}

	return path, nil
}

func (peer *IPFSPeer) CatNamespace(addr RemoteStoreAddress) (RemoteNamespaceRecord, error) {
	path := castIPFSPath(addr)

	chunk := IPFSRecord{}
	caterr := peer.cat(path, &chunk)

	if caterr != nil {
		return EMPTY_RECORD, errors.Wrap(caterr, "IPFSPeer.CatNamespace failed")
	}

	record := RemoteNamespaceRecord{Namespace: chunk.Namespace}
	return record, nil
}

func (peer *IPFSPeer) add(chunk interface{}) (IPFSPath, error) {
	buff := bytes.Buffer{}
	enc := gob.NewEncoder(&buff)
	err := enc.Encode(chunk)

	if err != nil {
		return "", errors.Wrap(err, "Error encoding Gob")
	}

	path, sherr := peer.Shell.Add(&buff)

	if sherr != nil {
		return "", errors.Wrap(err, "IPFSPeer.add failed")
	}

	return IPFSPath(path), nil
}

func (peer *IPFSPeer) cat(path IPFSPath, out interface{}) error {
	reader, err := peer.Shell.Cat(string(path))

	if err != nil {
		return errors.Wrap(err, "IPFSPeer.cat failed")
	}

	defer reader.Close()

	dec := gob.NewDecoder(reader)
	err = dec.Decode(out)

	if err != nil {
		return errors.Wrap(err, "failed to decode gob")
	}

	// According to IPFS binding docs we must drain the reader.
	remainder, drainerr := ioutil.ReadAll(reader)

	if drainerr != nil {
		logwarn("error draining reader: %v", drainerr)
	}

	if len(remainder) != 0 {
		logwarn("remaining bits after gob: %v", remainder)
	}

	return nil
}
<commit_msg>Fix defects in new IPFSPeer implementation<commit_after>package godless

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"io/ioutil"
	"net/http"

	ipfs "github.com/ipfs/go-ipfs-api"
	"github.com/pkg/errors"
)

type IPFSPath string

func (path IPFSPath) Path() string {
	return string(path)
}

func castIPFSPath(addr RemoteStoreAddress) IPFSPath {
	path, ok := addr.(IPFSPath)

	if !ok {
		panic("addr was not IPFSPath")
	}

	return path
}

type IPFSPeer struct {
	Url    string
	Client *http.Client
	Shell  *ipfs.Shell
}

type IPFSRecord struct {
	Namespace Namespace
}

type IPFSIndex struct {
	Index map[string][]IPFSPath
}

func (index IPFSIndex) remoteIndex() RemoteNamespaceIndex {
	out := RemoteNamespaceIndex{
		Index: map[string][]RemoteStoreAddress{},
	}

	for indexKey, paths := range index.Index {
		addrs := make([]RemoteStoreAddress, len(paths))
		for i, p := range paths {
			addrs[i] = p
		}
		out.Index[indexKey] = addrs
	}

	return out
}

func makeIpfsIndex(index RemoteNamespaceIndex) IPFSIndex {
	out := IPFSIndex{
		Index: map[string][]IPFSPath{},
	}

	for indexKey, addrs := range index.Index {
		paths := make([]IPFSPath, len(addrs))
		for i, a := range addrs {
			paths[i] = castIPFSPath(a)
		}
		out.Index[indexKey] = paths
	}

	return out
}

func MakeIPFSPeer(url string) RemoteStore {
	peer := &IPFSPeer{
		Url:    url,
		Client: defaultHttpClient(),
	}

	return peer
}

func (peer *IPFSPeer) Connect() error {
	peer.Shell = ipfs.NewShellWithClient(peer.Url, peer.Client)

	if !peer.Shell.IsUp() {
		return fmt.Errorf("IPFSPeer is not up at '%v'", peer.Url)
	}

	return nil
}

func (peer *IPFSPeer) Disconnect() error {
	// Nothing to do.
	return nil
}

func (peer *IPFSPeer) AddIndex(index RemoteNamespaceIndex) (RemoteStoreAddress, error) {
	chunk := makeIpfsIndex(index)

	path, err := peer.add(&chunk)

	if err != nil {
		return nil, errors.Wrap(err, "IPFSPeer.AddIndex failed")
	}

	return path, nil
}

func (peer *IPFSPeer) CatIndex(addr RemoteStoreAddress) (RemoteNamespaceIndex, error) {
	path := castIPFSPath(addr)

	chunk := IPFSIndex{}
	caterr := peer.cat(path, &chunk)

	if caterr != nil {
		return EMPTY_INDEX, errors.Wrap(caterr, "IPFSPeer.CatNamespace failed")
	}

	index := chunk.remoteIndex()
	return index, nil
}

func (peer *IPFSPeer) AddNamespace(record RemoteNamespaceRecord) (RemoteStoreAddress, error) {
	chunk := IPFSRecord{
		Namespace: record.Namespace,
	}

	path, err := peer.add(&chunk)

	if err != nil {
		return nil, errors.Wrap(err, "IPFSPeer.AddNamespace failed")
	}

	return path, nil
}

func (peer *IPFSPeer) CatNamespace(addr RemoteStoreAddress) (RemoteNamespaceRecord, error) {
	path := castIPFSPath(addr)

	chunk := IPFSRecord{}
	caterr := peer.cat(path, &chunk)

	if caterr != nil {
		return EMPTY_RECORD, errors.Wrap(caterr, "IPFSPeer.CatNamespace failed")
	}

	record := RemoteNamespaceRecord{Namespace: chunk.Namespace}
	return record, nil
}

func (peer *IPFSPeer) add(chunk interface{}) (IPFSPath, error) {
	buff := bytes.Buffer{}
	enc := gob.NewEncoder(&buff)
	err := enc.Encode(chunk)

	if err != nil {
		return "", errors.Wrap(err, "Error encoding Gob")
	}

	path, sherr := peer.Shell.Add(&buff)

	if sherr != nil {
		return "", errors.Wrap(err, "IPFSPeer.add failed")
	}

	return IPFSPath(path), nil
}

func (peer *IPFSPeer) cat(path IPFSPath, out interface{}) error {
	reader, err := peer.Shell.Cat(string(path))

	if err != nil {
		return errors.Wrap(err, "IPFSPeer.cat failed")
	}

	defer reader.Close()

	dec := gob.NewDecoder(reader)
	err = dec.Decode(out)

	if err != nil {
		return errors.Wrap(err, "failed to decode gob")
	}

	// According to IPFS binding docs we must drain the reader.
	remainder, drainerr := ioutil.ReadAll(reader)

	if drainerr != nil {
		logwarn("error draining reader: %v", drainerr)
	}

	if len(remainder) != 0 {
		logwarn("remaining bits after gob: %v", remainder)
	}

	return nil
}
<|endoftext|>


<commit_before>package pingdumb_test

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"time"

	"github.com/alphagov/paas-cf/tools/metrics/fakes"
	. "github.com/alphagov/paas-cf/tools/metrics/pingdumb"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Pingdumb", func() {
	var resolvers []*net.Resolver
	var server *http.Server

	BeforeSuite(func() {
		resolvers = []*net.Resolver{
			&net.Resolver{
				Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
					dialer := &net.Dialer{}
					return dialer.DialContext(ctx, network, "127.0.0.1:8053")
				},
			},
		}

		records := map[string][]string{
			"local.test.": []string{
				"127.0.0.1",
				"0.0.0.0",
			},
		}

		go func() {
			defer GinkgoRecover()
			Expect(fakes.ListenAndServeDNS(":8053", records)).To(Succeed())
		}()

		server = fakes.ListenAndServeHTTP(":8080")
	})

	AfterSuite(func() {
		fakes.ShutdownDNS()
		server.Close()
	})

	It("produces a report", func() {
		config := ReportConfig{
			Target:    "http://local.test:8080/",
			Resolvers: resolvers,
			Timeout:   1 * time.Second,
		}
		r, err := GetReport(config)
		Expect(err).NotTo(HaveOccurred())
		Expect(len(r.Checks)).To(BeNumerically(">", 1))

		By("not translating HTTP error codes into failures")
		for _, check := range r.Checks {
			Expect(check.Err()).NotTo(HaveOccurred())
		}
		Expect(len(r.Failures())).To(Equal(0))
		Expect(r.OK()).To(BeTrue())

		By("including status codes")
		for _, check := range r.Checks {
			Expect(check.Response).NotTo(BeNil())
			Expect(check.Response.StatusCode).To(Equal(418))
		}

		By("checking multiple IPs")
		uniqueIPs := map[string]bool{}
		for _, check := range r.Checks {
			uniqueIPs[check.Addr] = true
		}
		Expect(len(uniqueIPs)).To(Equal(len(r.Checks)),
			fmt.Sprintf("Only got %d of %d expected unique IPs", len(uniqueIPs), len(r.Checks)))

		By("including request timestamps")
		for _, check := range r.Checks {
			Expect(check.Start).To(BeTemporally("~", time.Now(), 1*time.Hour))
			Expect(check.Stop).To(BeTemporally("~", time.Now(), 1*time.Hour))
			Expect(check.Stop).To(BeTemporally(">", check.Start))
		}
	})

	It("produces an error when it can't resolve the target", func() {
		config := ReportConfig{
			Target:  "https://this.domain.is.invalid/",
			Timeout: 1 * time.Second,
		}
		_, err := GetReport(config)
		_, ok := err.(*net.DNSError)
		Expect(ok).To(BeTrue(), "expected net.DNSError")
	})

	It("returns failures on hanging connections", func() {
		config := ReportConfig{
			Target:    "http://local.test:8080/?hang=true",
			Timeout:   100 * time.Millisecond,
			Resolvers: resolvers,
		}
		r, err := GetReport(config)
		Expect(err).ToNot(HaveOccurred())
		Expect(len(r.Failures())).To(Equal(2))
		Expect(r.OK()).To(BeFalse())
		for _, check := range r.Checks {
			Expect(check.Err()).To(HaveOccurred())
			Expect(check.Err().Error()).To(ContainSubstring("context deadline exceeded"))
		}
	})
})
<commit_msg>Keep trying until GetReport() is successful.<commit_after>package pingdumb_test

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"time"

	"github.com/alphagov/paas-cf/tools/metrics/fakes"
	. "github.com/alphagov/paas-cf/tools/metrics/pingdumb"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Pingdumb", func() {
	var resolvers []*net.Resolver
	var server *http.Server

	BeforeSuite(func() {
		resolvers = []*net.Resolver{
			&net.Resolver{
				Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
					dialer := &net.Dialer{}
					return dialer.DialContext(ctx, network, "127.0.0.1:8053")
				},
			},
		}

		records := map[string][]string{
			"local.test.": []string{
				"127.0.0.1",
				"0.0.0.0",
			},
		}

		go func() {
			defer GinkgoRecover()
			Expect(fakes.ListenAndServeDNS(":8053", records)).To(Succeed())
		}()

		server = fakes.ListenAndServeHTTP(":8080")
	})

	AfterSuite(func() {
		fakes.ShutdownDNS()
		server.Close()
	})

	It("produces a report", func() {
		config := ReportConfig{
			Target:    "http://local.test:8080/",
			Resolvers: resolvers,
			Timeout:   1 * time.Second,
		}
		var (
			r   *Report
			err error
		)
		Eventually(func() error {
			r, err = GetReport(config)
			return err
		}).Should(Succeed())
		Expect(len(r.Checks)).To(BeNumerically(">", 1))

		By("not translating HTTP error codes into failures")
		for _, check := range r.Checks {
			Expect(check.Err()).NotTo(HaveOccurred())
		}
		Expect(len(r.Failures())).To(Equal(0))
		Expect(r.OK()).To(BeTrue())

		By("including status codes")
		for _, check := range r.Checks {
			Expect(check.Response).NotTo(BeNil())
			Expect(check.Response.StatusCode).To(Equal(418))
		}

		By("checking multiple IPs")
		uniqueIPs := map[string]bool{}
		for _, check := range r.Checks {
			uniqueIPs[check.Addr] = true
		}
		Expect(len(uniqueIPs)).To(Equal(len(r.Checks)),
			fmt.Sprintf("Only got %d of %d expected unique IPs", len(uniqueIPs), len(r.Checks)))

		By("including request timestamps")
		for _, check := range r.Checks {
			Expect(check.Start).To(BeTemporally("~", time.Now(), 1*time.Hour))
			Expect(check.Stop).To(BeTemporally("~", time.Now(), 1*time.Hour))
			Expect(check.Stop).To(BeTemporally(">", check.Start))
		}
	})

	It("produces an error when it can't resolve the target", func() {
		config := ReportConfig{
			Target:  "https://this.domain.is.invalid/",
			Timeout: 1 * time.Second,
		}
		_, err := GetReport(config)
		_, ok := err.(*net.DNSError)
		Expect(ok).To(BeTrue(), "expected net.DNSError")
	})

	It("returns failures on hanging connections", func() {
		config := ReportConfig{
			Target:    "http://local.test:8080/?hang=true",
			Timeout:   100 * time.Millisecond,
			Resolvers: resolvers,
		}
		r, err := GetReport(config)
		Expect(err).ToNot(HaveOccurred())
		Expect(len(r.Failures())).To(Equal(2))
		Expect(r.OK()).To(BeFalse())
		for _, check := range r.Checks {
			Expect(check.Err()).To(HaveOccurred())
			Expect(check.Err().Error()).To(ContainSubstring("context deadline exceeded"))
		}
	})
})
<|endoftext|>


<commit_before>package catalog

import (
	"bytes"
	"encoding/json"
	"fmt"
	"time"

	log "github.com/sirupsen/logrus"
	v1beta1ext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/runtime"
	k8sjson "k8s.io/apimachinery/pkg/runtime/serializer/json"
	"k8s.io/client-go/tools/cache"

	csvv1alpha1 "github.com/coreos-inc/alm/apis/clusterserviceversion/v1alpha1"
	v1alpha1csv "github.com/coreos-inc/alm/apis/clusterserviceversion/v1alpha1"
	"github.com/coreos-inc/alm/apis/installplan/v1alpha1"
	catlib "github.com/coreos-inc/alm/catalog"
	"github.com/coreos-inc/alm/client"
	"github.com/coreos-inc/alm/queueinformer"
)

const crdKind = "CustomResourceDefinition"

// Operator represents a Kubernetes operator that executes InstallPlans by
// resolving dependencies in a catalog.
type Operator struct {
	*queueinformer.Operator
	ipClient  client.InstallPlanInterface
	csvClient client.ClusterServiceVersionInterface
	sources   []catlib.Source
}

// NewOperator creates a new Catalog Operator.
func NewOperator(kubeconfigPath string, wakeupInterval time.Duration, sources []catlib.Source, namespaces ...string) (*Operator, error) {
	if namespaces == nil {
		namespaces = []string{metav1.NamespaceAll}
	}

	// Create an instance of an InstallPlan client.
	ipClient, err := client.NewInstallPlanClient(kubeconfigPath)
	if err != nil {
		return nil, err
	}

	// Create an instance of a CSV client.
	csvClient, err := client.NewClusterServiceVersionClient(kubeconfigPath)
	if err != nil {
		return nil, err
	}

	// Create a watch for each namespace.
	ipWatchers := []*cache.ListWatch{}
	for _, namespace := range namespaces {
		ipWatchers = append(ipWatchers, cache.NewListWatchFromClient(
			ipClient,
			"installplan-v1s",
			namespace,
			fields.Everything(),
		))
	}

	// Create an informer for each watch.
	sharedIndexInformers := []cache.SharedIndexInformer{}
	for _, ipWatcher := range ipWatchers {
		sharedIndexInformers = append(sharedIndexInformers, cache.NewSharedIndexInformer(
			ipWatcher,
			&v1alpha1.InstallPlan{},
			wakeupInterval,
			cache.Indexers{},
		))
	}

	// Create a new queueinformer-based operator.
	queueOperator, err := queueinformer.NewOperator(kubeconfigPath)
	if err != nil {
		return nil, err
	}

	op := &Operator{
		queueOperator,
		ipClient,
		csvClient,
		sources,
	}

	ipQueueInformers := queueinformer.New(
		"installplans",
		sharedIndexInformers,
		op.syncInstallPlans,
		nil,
	)

	for _, opVerQueueInformer := range ipQueueInformers {
		op.RegisterQueueInformer(opVerQueueInformer)
	}

	return op, nil
}

func (o *Operator) syncInstallPlans(obj interface{}) error {
	plan, ok := obj.(*v1alpha1.InstallPlan)
	if !ok {
		log.Debugf("wrong type: %#v", obj)
		return fmt.Errorf("casting InstallPlan failed")
	}

	log.Infof("syncing InstallPlan: %s", plan.SelfLink)

	if err := o.transitionInstallPlanState(plan); err != nil {
		return err
	}

	_, err := o.ipClient.UpdateInstallPlan(plan)
	return err
}

func (o *Operator) transitionInstallPlanState(plan *v1alpha1.InstallPlan) error {
	switch plan.Status.Phase {
	case v1alpha1.InstallPlanPhasePlanning:
		for _, source := range o.sources {
			if err := createInstallPlan(source, plan); err != nil {
				return err
			}
		}
	case v1alpha1.InstallPlanPhaseInstalling:
		if err := o.installInstallPlan(plan); err != nil {
			return err
		}
	}
	return nil
}

func createInstallPlan(source catlib.Source, installPlan *v1alpha1.InstallPlan) error {
	steps := installPlan.Status.Plan
	names := installPlan.Spec.ClusterServiceVersionNames

	crdSerializer := k8sjson.NewYAMLSerializer(k8sjson.DefaultMetaFactory, scheme.Scheme, scheme.Scheme)
	scheme := runtime.NewScheme()
	if err := v1alpha1csv.AddToScheme(scheme); err != nil {
		return err
	}
	csvSerializer := k8sjson.NewYAMLSerializer(k8sjson.DefaultMetaFactory, scheme, scheme)

	for len(names) > 0 {
		// looping here like this because we are adding names to the list from dependencies
		name := names[0]
		names = names[1:]
		csv, err := source.FindLatestCSVByServiceName(name)
		if err != nil {
			return err
		}

		for _, crdDescription := range csv.Spec.CustomResourceDefinitions.GetAllCrds() {
			crd, err := source.FindCRDByName(crdDescription.Name)
			if err != nil {
				return err
			}

			if checkIfOwned(*csv, crd.OwnerReferences) {
				var manifest bytes.Buffer
				if err := crdSerializer.Encode(crd, &manifest); err != nil {
					return err
				}
				step := v1alpha1.Step{
					Resolving: name,
					Resource: v1alpha1.StepResource{
						Group:    crd.Spec.Group,
						Version:  crd.Spec.Version,
						Kind:     crd.Kind,
						Name:     crd.Name,
						Manifest: manifest.String(),
					},
					Status: v1alpha1.StepStatusUnknown,
				}
				steps = append(steps, step)
			} else {
				csvForCRD, err := source.FindLatestCSVForCRD(crdDescription.Name)
				if err != nil {
					return err
				}
				names = append(names, csvForCRD.Name)
			}

		}

		var manifestCSV bytes.Buffer
		if err := csvSerializer.Encode(csv, &manifestCSV); err != nil {
			return err
		}
		stepCSV := v1alpha1.Step{
			Resolving: name,
			Resource: v1alpha1.StepResource{
				Group:    csv.GroupVersionKind().Group,
				Version:  csv.GroupVersionKind().Group,
				Kind:     csv.Kind,
				Name:     csv.Name,
				Manifest: manifestCSV.String(),
			},
			Status: v1alpha1.StepStatusUnknown,
		}
		steps = append(steps, stepCSV)
	}
	installPlan.Status.Plan = steps
	installPlan.Status.Phase = v1alpha1.InstallPlanPhaseInstalling
	return nil
}

func checkIfOwned(csv v1alpha1csv.ClusterServiceVersion, ownerRefs []metav1.OwnerReference) bool {
	for _, ownerRef := range ownerRefs {
		if csv.Name != "" && csv.Name == ownerRef.Name && csv.Kind != "" && csv.Kind == ownerRef.Kind {
			return true
		}
	}
	return false
}

func (o *Operator) installInstallPlan(plan *v1alpha1.InstallPlan) error {
	if plan.Status.Phase != v1alpha1.InstallPlanPhaseInstalling {
		panic("attempted to install a plan that wasn't in the installing phase")
	}

	for i, step := range plan.Status.Plan {
		switch step.Status {
		case v1alpha1.StepStatusPresent, v1alpha1.StepStatusCreated:
			continue
		case v1alpha1.StepStatusUnknown, v1alpha1.StepStatusNotPresent:
			switch step.Resource.Kind {
			case crdKind:
				// Marshal the manifest into a CRD instance.
				var crd v1beta1ext.CustomResourceDefinition
				err := json.Unmarshal([]byte(step.Resource.Manifest), &crd)
				if err != nil {
					return err
				}

				// Attempt to create the CRD.
				err = o.OpClient.CreateCustomResourceDefinitionKind(&crd)
				if errors.IsAlreadyExists(err) {
					// If it already existed, mark the step as Present.
					plan.Status.Plan[i].Status = v1alpha1.StepStatusPresent
					continue
				} else if err != nil {
					return err
				} else {
					// If it no error occured, mark the step as Created.
					plan.Status.Plan[i].Status = v1alpha1.StepStatusCreated
					continue
				}

			case csvv1alpha1.ClusterServiceVersionKind:
				// Marshal the manifest into a CRD instance.
				var csv csvv1alpha1.ClusterServiceVersion
				err := json.Unmarshal([]byte(step.Resource.Manifest), &csv)
				if err != nil {
					return err
				}

				// Attempt to create the CSV.
				err = o.csvClient.CreateCSV(&csv)
				if errors.IsAlreadyExists(err) {
					// If it already existed, mark the step as Present.
					plan.Status.Plan[i].Status = v1alpha1.StepStatusPresent
				} else if err != nil {
					return err
				} else {
					// If it no error occured, mark the step as Created.
					plan.Status.Plan[i].Status = v1alpha1.StepStatusCreated
				}

			default:
				return v1alpha1.ErrInvalidInstallPlan
			}

		default:
			return v1alpha1.ErrInvalidInstallPlan
		}
	}

	// Loop over one final time to check and see if everything is good.
	for _, step := range plan.Status.Plan {
		switch step.Status {
		case v1alpha1.StepStatusCreated, v1alpha1.StepStatusPresent:
		default:
			return nil
		}
	}
	plan.Status.Phase = v1alpha1.InstallPlanPhaseComplete
	return nil
}
<commit_msg>operators/catalog: add multi-source TODO<commit_after>package catalog

import (
	"bytes"
	"encoding/json"
	"fmt"
	"time"

	log "github.com/sirupsen/logrus"
	v1beta1ext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/runtime"
	k8sjson "k8s.io/apimachinery/pkg/runtime/serializer/json"
	"k8s.io/client-go/tools/cache"

	csvv1alpha1 "github.com/coreos-inc/alm/apis/clusterserviceversion/v1alpha1"
	v1alpha1csv "github.com/coreos-inc/alm/apis/clusterserviceversion/v1alpha1"
	"github.com/coreos-inc/alm/apis/installplan/v1alpha1"
	catlib "github.com/coreos-inc/alm/catalog"
	"github.com/coreos-inc/alm/client"
	"github.com/coreos-inc/alm/queueinformer"
)

const crdKind = "CustomResourceDefinition"

// Operator represents a Kubernetes operator that executes InstallPlans by
// resolving dependencies in a catalog.
type Operator struct {
	*queueinformer.Operator
	ipClient  client.InstallPlanInterface
	csvClient client.ClusterServiceVersionInterface
	sources   []catlib.Source
}

// NewOperator creates a new Catalog Operator.
func NewOperator(kubeconfigPath string, wakeupInterval time.Duration, sources []catlib.Source, namespaces ...string) (*Operator, error) {
	if namespaces == nil {
		namespaces = []string{metav1.NamespaceAll}
	}

	// Create an instance of an InstallPlan client.
	ipClient, err := client.NewInstallPlanClient(kubeconfigPath)
	if err != nil {
		return nil, err
	}

	// Create an instance of a CSV client.
	csvClient, err := client.NewClusterServiceVersionClient(kubeconfigPath)
	if err != nil {
		return nil, err
	}

	// Create a watch for each namespace.
	ipWatchers := []*cache.ListWatch{}
	for _, namespace := range namespaces {
		ipWatchers = append(ipWatchers, cache.NewListWatchFromClient(
			ipClient,
			"installplan-v1s",
			namespace,
			fields.Everything(),
		))
	}

	// Create an informer for each watch.
	sharedIndexInformers := []cache.SharedIndexInformer{}
	for _, ipWatcher := range ipWatchers {
		sharedIndexInformers = append(sharedIndexInformers, cache.NewSharedIndexInformer(
			ipWatcher,
			&v1alpha1.InstallPlan{},
			wakeupInterval,
			cache.Indexers{},
		))
	}

	// Create a new queueinformer-based operator.
	queueOperator, err := queueinformer.NewOperator(kubeconfigPath)
	if err != nil {
		return nil, err
	}

	op := &Operator{
		queueOperator,
		ipClient,
		csvClient,
		sources,
	}

	ipQueueInformers := queueinformer.New(
		"installplans",
		sharedIndexInformers,
		op.syncInstallPlans,
		nil,
	)

	for _, opVerQueueInformer := range ipQueueInformers {
		op.RegisterQueueInformer(opVerQueueInformer)
	}

	return op, nil
}

func (o *Operator) syncInstallPlans(obj interface{}) error {
	plan, ok := obj.(*v1alpha1.InstallPlan)
	if !ok {
		log.Debugf("wrong type: %#v", obj)
		return fmt.Errorf("casting InstallPlan failed")
	}

	log.Infof("syncing InstallPlan: %s", plan.SelfLink)

	if err := o.transitionInstallPlanState(plan); err != nil {
		return err
	}

	_, err := o.ipClient.UpdateInstallPlan(plan)
	return err
}

func (o *Operator) transitionInstallPlanState(plan *v1alpha1.InstallPlan) error {
	switch plan.Status.Phase {
	case v1alpha1.InstallPlanPhasePlanning:
		for _, source := range o.sources {
			err := createInstallPlan(source, plan)
			// Intentionally return after the first source only.
			// TODO(jzelinskie): update to check all sources.
			return err
		}
	case v1alpha1.InstallPlanPhaseInstalling:
		if err := o.installInstallPlan(plan); err != nil {
			return err
		}
	}
	return nil
}

func createInstallPlan(source catlib.Source, installPlan *v1alpha1.InstallPlan) error {
	steps := installPlan.Status.Plan
	names := installPlan.Spec.ClusterServiceVersionNames

	crdSerializer := k8sjson.NewYAMLSerializer(k8sjson.DefaultMetaFactory, scheme.Scheme, scheme.Scheme)
	scheme := runtime.NewScheme()
	if err := v1alpha1csv.AddToScheme(scheme); err != nil {
		return err
	}
	csvSerializer := k8sjson.NewYAMLSerializer(k8sjson.DefaultMetaFactory, scheme, scheme)

	for len(names) > 0 {
		// looping here like this because we are adding names to the list from dependencies
		name := names[0]
		names = names[1:]
		csv, err := source.FindLatestCSVByServiceName(name)
		if err != nil {
			return err
		}

		for _, crdDescription := range csv.Spec.CustomResourceDefinitions.GetAllCrds() {
			crd, err := source.FindCRDByName(crdDescription.Name)
			if err != nil {
				return err
			}

			if checkIfOwned(*csv, crd.OwnerReferences) {
				var manifest bytes.Buffer
				if err := crdSerializer.Encode(crd, &manifest); err != nil {
					return err
				}
				step := v1alpha1.Step{
					Resolving: name,
					Resource: v1alpha1.StepResource{
						Group:    crd.Spec.Group,
						Version:  crd.Spec.Version,
						Kind:     crd.Kind,
						Name:     crd.Name,
						Manifest: manifest.String(),
					},
					Status: v1alpha1.StepStatusUnknown,
				}
				steps = append(steps, step)
			} else {
				csvForCRD, err := source.FindLatestCSVForCRD(crdDescription.Name)
				if err != nil {
					return err
				}
				names = append(names, csvForCRD.Name)
			}

		}

		var manifestCSV bytes.Buffer
		if err := csvSerializer.Encode(csv, &manifestCSV); err != nil {
			return err
		}
		stepCSV := v1alpha1.Step{
			Resolving: name,
			Resource: v1alpha1.StepResource{
				Group:    csv.GroupVersionKind().Group,
				Version:  csv.GroupVersionKind().Group,
				Kind:     csv.Kind,
				Name:     csv.Name,
				Manifest: manifestCSV.String(),
			},
			Status: v1alpha1.StepStatusUnknown,
		}
		steps = append(steps, stepCSV)
	}
	installPlan.Status.Plan = steps
	installPlan.Status.Phase = v1alpha1.InstallPlanPhaseInstalling
	return nil
}

func checkIfOwned(csv v1alpha1csv.ClusterServiceVersion, ownerRefs []metav1.OwnerReference) bool {
	for _, ownerRef := range ownerRefs {
		if csv.Name != "" && csv.Name == ownerRef.Name && csv.Kind != "" && csv.Kind == ownerRef.Kind {
			return true
		}
	}
	return false
}

func (o *Operator) installInstallPlan(plan *v1alpha1.InstallPlan) error {
	if plan.Status.Phase != v1alpha1.InstallPlanPhaseInstalling {
		panic("attempted to install a plan that wasn't in the installing phase")
	}

	for i, step := range plan.Status.Plan {
		switch step.Status {
		case v1alpha1.StepStatusPresent, v1alpha1.StepStatusCreated:
			continue
		case v1alpha1.StepStatusUnknown, v1alpha1.StepStatusNotPresent:
			switch step.Resource.Kind {
			case crdKind:
				// Marshal the manifest into a CRD instance.
				var crd v1beta1ext.CustomResourceDefinition
				err := json.Unmarshal([]byte(step.Resource.Manifest), &crd)
				if err != nil {
					return err
				}

				// Attempt to create the CRD.
				err = o.OpClient.CreateCustomResourceDefinitionKind(&crd)
				if errors.IsAlreadyExists(err) {
					// If it already existed, mark the step as Present.
					plan.Status.Plan[i].Status = v1alpha1.StepStatusPresent
					continue
				} else if err != nil {
					return err
				} else {
					// If it no error occured, mark the step as Created.
					plan.Status.Plan[i].Status = v1alpha1.StepStatusCreated
					continue
				}

			case csvv1alpha1.ClusterServiceVersionKind:
				// Marshal the manifest into a CRD instance.
				var csv csvv1alpha1.ClusterServiceVersion
				err := json.Unmarshal([]byte(step.Resource.Manifest), &csv)
				if err != nil {
					return err
				}

				// Attempt to create the CSV.
				err = o.csvClient.CreateCSV(&csv)
				if errors.IsAlreadyExists(err) {
					// If it already existed, mark the step as Present.
					plan.Status.Plan[i].Status = v1alpha1.StepStatusPresent
				} else if err != nil {
					return err
				} else {
					// If it no error occured, mark the step as Created.
					plan.Status.Plan[i].Status = v1alpha1.StepStatusCreated
				}

			default:
				return v1alpha1.ErrInvalidInstallPlan
			}

		default:
			return v1alpha1.ErrInvalidInstallPlan
		}
	}

	// Loop over one final time to check and see if everything is good.
	for _, step := range plan.Status.Plan {
		switch step.Status {
		case v1alpha1.StepStatusCreated, v1alpha1.StepStatusPresent:
		default:
			return nil
		}
	}
	plan.Status.Phase = v1alpha1.InstallPlanPhaseComplete
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014, Joe Tsai. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.

// Package bufpipe implements a buffered pipe.
package bufpipe

import "io"
import "sync"

// There are a number of modes of operation that BufferPipe can operate in.
//
// As such, there are 4 different (and mostly orthoganal) flags that can be
// bitwise ORed together to create the mode of operation. Thus, with 4 flags,
// there are technically 16 different possible combinations (although, some of
// them are illogical). All 16 combinations are allowed, even if no sensible
// programmer should be using them.
//
// The first flag determines the buffer's structure (linear vs. ring). In linear
// mode, a writer can only write up to the internal buffer length's worth of
// data. On the other hand, in ring mode, the buffer is treated like a circular
// buffer and allow indefinite reading and writing.
//
// The second flag determines concurrent access to the pipe (mono vs. dual).
// In mono access mode, the writer has sole access to the pipe. Only after the
// Close() method is called can a reader start reading data. In dual access
// mode, readers can read written data the moment it is ready.
//
// The third and fourth flag determines waiting protocol for reading and writing
// (polling vs. blocking). In blocking mode, a writer or reader will block until
// there is available buffer space or valid data to consume. In polling mode,
// read and write operations return immediately, possibly with an ErrShortWrite
// or ErrNoProgress error.
//
// Combining Ring with Mono is an illogical combination. Mono access dictates
// that no reader can drain the pipe until it is closed. However, the benefit
// of a Ring buffer is that it can circularly write data as a reader consumes
// the input. Ring with Mono is effectively Line with Mono.
//
// Combining Line with BlockI is an illogical combination. In Line mode, the
// amount that can be written is fixed and independent of how much is read.
// Thus, using BlockI in this case will cause the writer to block forever when
// the buffer is full.
//
// With all illogical combinations removed, there are only 8 logical
// combinations that programmers should use.
const (
	Ring   = 1 << iota // Ring buffer vs. linear buffer
	Dual               // Dual access IO vs. mono access IO
	BlockI             // Blocking input vs. polling input
	BlockO             // Blocking output vs. polling output

	// The below flags are the inverse of the ones above. They exist so to make
	// it obvious what the inverse is.
	Line  = 0 // Inverse of Ring
	Mono  = 0 // Inverse of Dual
	PollI = 0 // Inverse of BlockI
	PollO = 0 // Inverse of BlockO
)

// The most common combination of flags are predefined with convenient aliases.
const (
	LineMono  = Line | Mono | PollI | BlockO
	LineDual  = Line | Dual | PollI | BlockO
	RingPoll  = Ring | Dual | PollI | PollO
	RingBlock = Ring | Dual | BlockI | BlockO
)

type BufferPipe struct {
	buf    []byte
	mode   int
	rdCnt  int64
	wrCnt  int64
	closed bool
	mutex  sync.Mutex
	rdCond sync.Cond
	wrCond sync.Cond
}

// BufferPipe is similar in operation to io.Pipe and is intended to be the
// communication channel between producer and consumer routines. There are some
// specific use cases for BufferPipes over io.Pipe.
//
// First, in cases where a writer may need to go back a modify a portion of the
// past "written" data before allowing the reader to consume it.
// Second, for performance applications, where the cost of copying of data is
// noticeable. Thus, it would be more efficient to read/write data from/to the
// internal buffer directly.
//
// See the defined constants for more on the buffer mode of operation.
func NewBufferPipe(buf []byte, mode int) *BufferPipe {
	b := new(BufferPipe)
	b.buf = buf
	b.mode = mode
	b.rdCond.L = &b.mutex
	b.wrCond.L = &b.mutex
	return b
}

// The entire internal buffer. Be careful when touching the raw buffer.
func (b *BufferPipe) GetBuffer() []byte {
	return b.buf
}

// The BufferPipe mode of operation.
func (b *BufferPipe) GetMode() int {
	return b.mode
}

// The total number of bytes the buffer can store.
func (b *BufferPipe) Capacity() int {
	return len(b.buf)
}

// The number of valid bytes that can be read.
func (b *BufferPipe) Length() int {
	b.mutex.Lock()
	defer b.mutex.Unlock()
	return int(b.wrCnt - b.rdCnt)
}

// The internal pointer values.
func (b *BufferPipe) GetOffsets() (rdCnt, wrCnt int64) {
	return b.rdCnt, b.wrCnt
}

func (b *BufferPipe) writeWait() int {
	var rdZero int64 // Zero value
	isLine := b.mode & Ring == 0
	isBlock := b.mode & BlockI > 0

	rdCnt := &b.rdCnt
	if isLine {
		rdCnt = &rdZero // Amount read has no effect on amount available
	}
	if isBlock {
		for !b.closed && len(b.buf) == int(b.wrCnt-(*rdCnt)) {
			b.wrCond.Wait()
		}
	}
	if b.closed {
		return 0 // Closed buffer is never available
	}
	return len(b.buf) - int(b.wrCnt-(*rdCnt))
}

// Slice of available buffer that can be written to. This does not advance the
// internal write pointer.
//
// In linear buffers, the slice obtained is guaranteed to be the entire
// available writable buffer slice.
//
// In LineMono mode only, slices obtained may still be modified even after
// WriteMark() has been called and before Close(). This is valid because this
// mode blocks readers until the buffer has been closed.
//
// In ring buffers, the slice obtained may not represent all of the available
// buffer space since this method always returns contiguous pieces of memory.
// It is guaranteed to return a non-empty slice if space is available.
//
// In the RingBlock mode, this method blocks until there is available space in
// the buffer to write. Other modes do not block and will return an empty slice
// if the buffer is full.
func (b *BufferPipe) WriteSlice() ([]byte, error) {
	if b == nil {
		return nil, nil
	}
	b.mutex.Lock()
	defer b.mutex.Unlock()
	return b.writeSlice()
}

func (b *BufferPipe) writeSlice() ([]byte, error) {
	availCnt := b.writeWait() // Block until there is available buffer
	offLo := int(b.wrCnt) % len(b.buf)
	offHi := offLo + availCnt
	if offHi > len(b.buf) { // If available slice is split, take bottom
		offHi = len(b.buf)
	}
	buf := b.buf[offLo:offHi]

	// Check erorr status
	if len(buf) == 0 {
		if b.closed {
			return buf, io.ErrClosedPipe
		} else {
			return buf, io.ErrShortWrite
		}
	}
	return buf, nil
}

// Advances the write pointer.
//
// The amount that can be advanced must be non-negative and be less than the
// length of the slice returned by the previous WriteSlice(). Calls to Write()
// may not be done between these two calls. Also, another call to WriteMark()
// is invalid until WriteSlice() has been called again.
//
// If WriteMark() is being used, only one writer routine is allowed.
func (b *BufferPipe) WriteMark(cnt int) {
	if b == nil && cnt == 0 {
		return
	}
	b.mutex.Lock()
	defer b.mutex.Unlock()
	b.writeMark(cnt)
}

func (b *BufferPipe) writeMark(cnt int) {
	availCnt := b.writeWait()
	if cnt < 0 || cnt > availCnt {
		panic("invalid mark increment value")
	}
	b.wrCnt += int64(cnt)

	b.rdCond.Signal()
}

// Write data to the buffer.
//
// In linear buffers, the length of the data slice may not exceed the capacity
// of the buffer. Otherwise, an ErrShortWrite error will be returned.
//
// In ring buffers, the length of the data slice may exceed the capacity.
// The operation will block until all data has been written. If there is no
// consumer of the data, then this method may block forever.
func (b *BufferPipe) Write(data []byte) (cnt int, err error) {
	b.mutex.Lock()
	defer b.mutex.Unlock()

	for cnt < len(data) {
		buf, err := b.writeSlice()
		if err != nil {
			return cnt, err
		}

		copyCnt := copy(buf, data[cnt:])
		b.writeMark(copyCnt)
		cnt += copyCnt
	}
	return cnt, nil
}

// Continually read the contents of the reader and write them to the pipe.
func (b *BufferPipe) ReadFrom(rd io.Reader) (cnt int64, err error) {
	for {
		data, wrErr := b.WriteSlice()
		rdCnt, rdErr := rd.Read(data)
		b.WriteMark(rdCnt)
		cnt += int64(rdCnt)

		switch {
		case wrErr != nil:
			return cnt, wrErr
		case rdErr == io.EOF:
			return cnt, nil
		case rdErr != nil:
			return cnt, rdErr
		}
	}
}

func (b *BufferPipe) readWait() int {
	isBlock := b.mode & BlockO > 0
	isMono := b.mode & Dual == 0
	if isBlock {
		for !b.closed && b.rdCnt == b.wrCnt {
			b.rdCond.Wait()
		}
		for isMono && !b.closed {
			b.rdCond.Wait()
		}
	}
	if isMono && !b.closed {
		return 0
	}
	return int(b.wrCnt - b.rdCnt)
}

// Slice of valid data that can be read. This does not advance the internal
// read pointer.
//
// In linear buffers, the slice obtained is guaranteed to be the entire
// valid readable buffer slice.
//
// In ring buffers, the slice obtained may not represent all of the valid buffer
// data since this method always returns contiguous pieces of memory. It is
// guaranteed to return a non-empty slice if valid data is available.
//
// In all modes, this method blocks until there is some valid data to read.
// The LineMono mode is special in that it will block until the buffer has
// been closed. Other modes just block until some data is available.
func (b *BufferPipe) ReadSlice() ([]byte, error) {
	if b == nil {
		return nil, nil
	}
	b.mutex.Lock()
	defer b.mutex.Unlock()
	return b.readSlice()
}

func (b *BufferPipe) readSlice() ([]byte, error) {
	validCnt := b.readWait() // Block until there is valid buffer
	offLo := int(b.rdCnt) % len(b.buf)
	offHi := offLo + validCnt
	if offHi > len(b.buf) { // If valid slice is split, take bottom
		offHi = len(b.buf)
	}
	buf := b.buf[offLo:offHi] // Linear and ring buffers

	// Check erorr status.
	if len(buf) == 0 {
		if b.closed {
			return buf, io.EOF
		} else {
			return buf, io.ErrNoProgress
		}
	}
	return buf, nil
}

// Advances the read pointer.
//
// The amount that can be advanced must be non-negative and be less than the
// length of the slice returned by the previous ReadSlice(). Calls to Read() may
// not be done between these two calls. Also, another call to ReadMark() is
// invalid until ReadSlice() has been called again.
//
// If ReadMark() is being used, only one writer routine is allowed.
func (b *BufferPipe) ReadMark(cnt int) {
	if b == nil && cnt == 0 {
		return
	}
	b.mutex.Lock()
	defer b.mutex.Unlock()
	b.readMark(cnt)
}

func (b *BufferPipe) readMark(cnt int) {
	validCnt := b.readWait()
	if cnt < 0 || cnt > validCnt {
		panic("invalid mark increment value")
	}
	b.rdCnt += int64(cnt)

	b.wrCond.Signal()
}

// Read data from the buffer.
//
// In all modes, the length of the data slice may exceed the capacity of
// the buffer. The operation will block until all data has been read or until
// the EOF is hit. If there is no producer of the data, then this method may
// block forever.
func (b *BufferPipe) Read(data []byte) (cnt int, err error) {
	b.mutex.Lock()
	defer b.mutex.Unlock()

	for cnt < len(data) {
		buf, err := b.readSlice()
		if err != nil {
			return cnt, err
		}

		copyCnt := copy(data[cnt:], buf)
		b.readMark(copyCnt)
		cnt += copyCnt
	}
	return cnt, nil
}

// Continually read the contents of the pipe and write them to the writer.
func (b *BufferPipe) WriteTo(wr io.Writer) (cnt int64, err error) {
	for {
		data, rdErr := b.ReadSlice()
		wrCnt, wrErr := wr.Write(data)
		b.ReadMark(wrCnt)
		cnt += int64(wrCnt)

		switch {
		case wrErr != nil:
			return cnt, wrErr
		case rdErr == io.EOF:
			return cnt, nil
		case rdErr != nil:
			return cnt, rdErr
		}
	}
}

// Close the buffer down.
//
// All write operations have no effect after this, while all read operations
// will drain remaining data in the buffer. This operation is somewhat similar
// to how Go channels operation.
//
// Writers should close the buffer to indicate to readers to mark end-of-stream.
//
// Readers should only close the buffer in the event of unexpected termination.
// The mechanism allows readers to inform writers of consumer termination and
// prevents the producer from potentially being blocked forever.
func (b *BufferPipe) Close() error {
	b.mutex.Lock()
	defer b.mutex.Unlock()
	b.closed = true
	b.rdCond.Signal()
	b.wrCond.Signal()
	return nil
}

// Makes the buffer ready for use again.
//
// Resetting when readers or writers are still using the pipe results in
// undefined behavior.
func (b *BufferPipe) Reset() {
	b.mutex.Lock()
	defer b.mutex.Unlock()
	b.wrCnt, b.rdCnt = 0, 0
	b.closed = false
}
<commit_msg>Trivial comment change<commit_after>// Copyright 2014, Joe Tsai. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.

// Package bufpipe implements a buffered pipe.
package bufpipe

import "io"
import "sync"

// There are a number of modes of operation that BufferPipe can operate in.
//
// As such, there are 4 different (and mostly orthogonal) flags that can be
// bitwise ORed together to create the mode of operation. Thus, with 4 flags,
// there are technically 16 different possible combinations (although, some of
// them are illogical). All 16 combinations are allowed, even if no sensible
// programmer should be using them.
//
// The first flag determines the buffer's structure (linear vs. ring). In linear
// mode, a writer can only write up to the internal buffer length's worth of
// data. On the other hand, in ring mode, the buffer is treated like a circular
// buffer and allow indefinite reading and writing.
//
// The second flag determines concurrent access to the pipe (mono vs. dual).
// In mono access mode, the writer has sole access to the pipe. Only after the
// Close() method is called can a reader start reading data. In dual access
// mode, readers can read written data the moment it is ready.
//
// The third and fourth flag determines waiting protocol for reading and writing
// (polling vs. blocking). In blocking mode, a writer or reader will block until
// there is available buffer space or valid data to consume. In polling mode,
// read and write operations return immediately, possibly with an ErrShortWrite
// or ErrNoProgress error.
//
// Combining Ring with Mono is an illogical combination. Mono access dictates
// that no reader can drain the pipe until it is closed. However, the benefit
// of a Ring buffer is that it can circularly write data as a reader consumes
// the input. Ring with Mono is effectively Line with Mono.
//
// Combining Line with BlockI is an illogical combination. In Line mode, the
// amount that can be written is fixed and independent of how much is read.
// Thus, using BlockI in this case will cause the writer to block forever when
// the buffer is full.
//
// With all illogical combinations removed, there are only 8 logical
// combinations that programmers should use.
const (
	Ring   = 1 << iota // Ring buffer vs. linear buffer
	Dual               // Dual access IO vs. mono access IO
	BlockI             // Blocking input vs. polling input
	BlockO             // Blocking output vs. polling output

	// The below flags are the inverse of the ones above. They exist to make it
	// obvious what the inverse is.
	Line  = 0 // Inverse of Ring
	Mono  = 0 // Inverse of Dual
	PollI = 0 // Inverse of BlockI
	PollO = 0 // Inverse of BlockO
)

// The most common combination of flags are predefined with convenient aliases.
const (
	LineMono  = Line | Mono | PollI | BlockO
	LineDual  = Line | Dual | PollI | BlockO
	RingPoll  = Ring | Dual | PollI | PollO
	RingBlock = Ring | Dual | BlockI | BlockO
)

type BufferPipe struct {
	buf    []byte
	mode   int
	rdCnt  int64
	wrCnt  int64
	closed bool
	mutex  sync.Mutex
	rdCond sync.Cond
	wrCond sync.Cond
}

// BufferPipe is similar in operation to io.Pipe and is intended to be the
// communication channel between producer and consumer routines. There are some
// specific use cases for BufferPipes over io.Pipe.
//
// First, in cases where a writer may need to go back a modify a portion of the
// past "written" data before allowing the reader to consume it.
// Second, for performance applications, where the cost of copying of data is
// noticeable. Thus, it would be more efficient to read/write data from/to the
// internal buffer directly.
//
// See the defined constants for more on the buffer mode of operation.
func NewBufferPipe(buf []byte, mode int) *BufferPipe {
	b := new(BufferPipe)
	b.buf = buf
	b.mode = mode
	b.rdCond.L = &b.mutex
	b.wrCond.L = &b.mutex
	return b
}

// The entire internal buffer. Be careful when touching the raw buffer.
func (b *BufferPipe) GetBuffer() []byte {
	return b.buf
}

// The BufferPipe mode of operation.
func (b *BufferPipe) GetMode() int {
	return b.mode
}

// The total number of bytes the buffer can store.
func (b *BufferPipe) Capacity() int {
	return len(b.buf)
}

// The number of valid bytes that can be read.
func (b *BufferPipe) Length() int {
	b.mutex.Lock()
	defer b.mutex.Unlock()
	return int(b.wrCnt - b.rdCnt)
}

// The internal pointer values.
func (b *BufferPipe) GetOffsets() (rdCnt, wrCnt int64) {
	return b.rdCnt, b.wrCnt
}

func (b *BufferPipe) writeWait() int {
	var rdZero int64 // Zero value
	isLine := b.mode & Ring == 0
	isBlock := b.mode & BlockI > 0

	rdCnt := &b.rdCnt
	if isLine {
		rdCnt = &rdZero // Amount read has no effect on amount available
	}
	if isBlock {
		for !b.closed && len(b.buf) == int(b.wrCnt-(*rdCnt)) {
			b.wrCond.Wait()
		}
	}
	if b.closed {
		return 0 // Closed buffer is never available
	}
	return len(b.buf) - int(b.wrCnt-(*rdCnt))
}

// Slice of available buffer that can be written to. This does not advance the
// internal write pointer.
//
// In linear buffers, the slice obtained is guaranteed to be the entire
// available writable buffer slice.
//
// In LineMono mode only, slices obtained may still be modified even after
// WriteMark() has been called and before Close(). This is valid because this
// mode blocks readers until the buffer has been closed.
//
// In ring buffers, the slice obtained may not represent all of the available
// buffer space since this method always returns contiguous pieces of memory.
// It is guaranteed to return a non-empty slice if space is available.
//
// In the RingBlock mode, this method blocks until there is available space in
// the buffer to write. Other modes do not block and will return an empty slice
// if the buffer is full.
func (b *BufferPipe) WriteSlice() ([]byte, error) {
	if b == nil {
		return nil, nil
	}
	b.mutex.Lock()
	defer b.mutex.Unlock()
	return b.writeSlice()
}

func (b *BufferPipe) writeSlice() ([]byte, error) {
	availCnt := b.writeWait() // Block until there is available buffer
	offLo := int(b.wrCnt) % len(b.buf)
	offHi := offLo + availCnt
	if offHi > len(b.buf) { // If available slice is split, take bottom
		offHi = len(b.buf)
	}
	buf := b.buf[offLo:offHi]

	// Check error status
	if len(buf) == 0 {
		if b.closed {
			return buf, io.ErrClosedPipe
		} else {
			return buf, io.ErrShortWrite
		}
	}
	return buf, nil
}

// Advances the write pointer.
//
// The amount that can be advanced must be non-negative and be less than the
// length of the slice returned by the previous WriteSlice(). Calls to Write()
// may not be done between these two calls. Also, another call to WriteMark()
// is invalid until WriteSlice() has been called again.
//
// If WriteMark() is being used, only one writer routine is allowed.
func (b *BufferPipe) WriteMark(cnt int) {
	if b == nil && cnt == 0 {
		return
	}
	b.mutex.Lock()
	defer b.mutex.Unlock()
	b.writeMark(cnt)
}

func (b *BufferPipe) writeMark(cnt int) {
	availCnt := b.writeWait()
	if cnt < 0 || cnt > availCnt {
		panic("invalid mark increment value")
	}
	b.wrCnt += int64(cnt)

	b.rdCond.Signal()
}

// Write data to the buffer.
//
// In linear buffers, the length of the data slice may not exceed the capacity
// of the buffer. Otherwise, an ErrShortWrite error will be returned.
//
// In ring buffers, the length of the data slice may exceed the capacity.
// The operation will block until all data has been written. If there is no
// consumer of the data, then this method may block forever.
func (b *BufferPipe) Write(data []byte) (cnt int, err error) {
	b.mutex.Lock()
	defer b.mutex.Unlock()

	for cnt < len(data) {
		buf, err := b.writeSlice()
		if err != nil {
			return cnt, err
		}

		copyCnt := copy(buf, data[cnt:])
		b.writeMark(copyCnt)
		cnt += copyCnt
	}
	return cnt, nil
}

// Continually read the contents of the reader and write them to the pipe.
func (b *BufferPipe) ReadFrom(rd io.Reader) (cnt int64, err error) {
	for {
		data, wrErr := b.WriteSlice()
		rdCnt, rdErr := rd.Read(data)
		b.WriteMark(rdCnt)
		cnt += int64(rdCnt)

		switch {
		case wrErr != nil:
			return cnt, wrErr
		case rdErr == io.EOF:
			return cnt, nil
		case rdErr != nil:
			return cnt, rdErr
		}
	}
}

func (b *BufferPipe) readWait() int {
	isBlock := b.mode & BlockO > 0
	isMono := b.mode & Dual == 0
	if isBlock {
		for !b.closed && b.rdCnt == b.wrCnt {
			b.rdCond.Wait()
		}
		for isMono && !b.closed {
			b.rdCond.Wait()
		}
	}
	if isMono && !b.closed {
		return 0
	}
	return int(b.wrCnt - b.rdCnt)
}

// Slice of valid data that can be read. This does not advance the internal
// read pointer.
//
// In linear buffers, the slice obtained is guaranteed to be the entire
// valid readable buffer slice.
//
// In ring buffers, the slice obtained may not represent all of the valid buffer
// data since this method always returns contiguous pieces of memory. It is
// guaranteed to return a non-empty slice if valid data is available.
//
// In all modes, this method blocks until there is some valid data to read.
// The LineMono mode is special in that it will block until the buffer has
// been closed. Other modes just block until some data is available.
func (b *BufferPipe) ReadSlice() ([]byte, error) {
	if b == nil {
		return nil, nil
	}
	b.mutex.Lock()
	defer b.mutex.Unlock()
	return b.readSlice()
}

func (b *BufferPipe) readSlice() ([]byte, error) {
	validCnt := b.readWait() // Block until there is valid buffer
	offLo := int(b.rdCnt) % len(b.buf)
	offHi := offLo + validCnt
	if offHi > len(b.buf) { // If valid slice is split, take bottom
		offHi = len(b.buf)
	}
	buf := b.buf[offLo:offHi] // Linear and ring buffers

	// Check error status.
	if len(buf) == 0 {
		if b.closed {
			return buf, io.EOF
		} else {
			return buf, io.ErrNoProgress
		}
	}
	return buf, nil
}

// Advances the read pointer.
//
// The amount that can be advanced must be non-negative and be less than the
// length of the slice returned by the previous ReadSlice(). Calls to Read() may
// not be done between these two calls. Also, another call to ReadMark() is
// invalid until ReadSlice() has been called again.
//
// If ReadMark() is being used, only one writer routine is allowed.
func (b *BufferPipe) ReadMark(cnt int) {
	if b == nil && cnt == 0 {
		return
	}
	b.mutex.Lock()
	defer b.mutex.Unlock()
	b.readMark(cnt)
}

func (b *BufferPipe) readMark(cnt int) {
	validCnt := b.readWait()
	if cnt < 0 || cnt > validCnt {
		panic("invalid mark increment value")
	}
	b.rdCnt += int64(cnt)

	b.wrCond.Signal()
}

// Read data from the buffer.
//
// In all modes, the length of the data slice may exceed the capacity of
// the buffer. The operation will block until all data has been read or until
// the EOF is hit. If there is no producer of the data, then this method may
// block forever.
func (b *BufferPipe) Read(data []byte) (cnt int, err error) {
	b.mutex.Lock()
	defer b.mutex.Unlock()

	for cnt < len(data) {
		buf, err := b.readSlice()
		if err != nil {
			return cnt, err
		}

		copyCnt := copy(data[cnt:], buf)
		b.readMark(copyCnt)
		cnt += copyCnt
	}
	return cnt, nil
}

// Continually read the contents of the pipe and write them to the writer.
func (b *BufferPipe) WriteTo(wr io.Writer) (cnt int64, err error) {
	for {
		data, rdErr := b.ReadSlice()
		wrCnt, wrErr := wr.Write(data)
		b.ReadMark(wrCnt)
		cnt += int64(wrCnt)

		switch {
		case wrErr != nil:
			return cnt, wrErr
		case rdErr == io.EOF:
			return cnt, nil
		case rdErr != nil:
			return cnt, rdErr
		}
	}
}

// Close the buffer down.
//
// All write operations have no effect after this, while all read operations
// will drain remaining data in the buffer. This operation is somewhat similar
// to how Go channels operation.
//
// Writers should close the buffer to indicate to readers to mark end-of-stream.
//
// Readers should only close the buffer in the event of unexpected termination.
// The mechanism allows readers to inform writers of consumer termination and
// prevents the producer from potentially being blocked forever.
func (b *BufferPipe) Close() error {
	b.mutex.Lock()
	defer b.mutex.Unlock()
	b.closed = true
	b.rdCond.Signal()
	b.wrCond.Signal()
	return nil
}

// Makes the buffer ready for use again.
//
// Resetting when readers or writers are still using the pipe results in
// undefined behavior.
func (b *BufferPipe) Reset() {
	b.mutex.Lock()
	defer b.mutex.Unlock()
	b.wrCnt, b.rdCnt = 0, 0
	b.closed = false
}
<|endoftext|>