<commit_before>package main

import (
	"io/ioutil"
	"net/rpc"
	"os"
	"strings"
	"time"
)

const (
	mGocodeAddr = "127.0.0.1:57952"
)

type mGocode struct {
	Bin      string
	Env      map[string]string
	Home     string
	cmd      string
	Set      map[string]string
	Complete struct {
		Fn  string
		Src string
		Pos int
	}
}

func (m *mGocode) Call() (interface{}, string) {
	e := ""
	res := M{}
	c, err := rpc.Dial("tcp", mGocodeAddr)
	if err != nil {
		if serveErr := mGocodeServe(m); serveErr != nil {
			return res, "Error starting gocode server: " + serveErr.Error()
		}

		for i := 0; err != nil && i < 5; i += 1 {
			time.Sleep(20 * time.Millisecond)
			c, err = rpc.Dial("tcp", mGocodeAddr)
		}

		if err != nil {
			return res, "Error connecting to gocode server: " + err.Error()
		}
	}
	defer c.Close()

	switch m.cmd {
	case "set":
		for k, v := range m.Set {
			mGocodeCmdSet(c, k, v)
		}
		res, e = mGocodeCmdSet(c, "\x00", "\x00")
	case "complete":
		if m.Complete.Src == "" {
			// this is here for testing, the client should always send the src
			s, _ := ioutil.ReadFile(m.Complete.Fn)
			m.Complete.Src = string(s)
		}

		pos := 0
		for i, _ := range m.Complete.Src {
			pos += 1
			if pos > m.Complete.Pos {
				pos = i
				break
			}
		}
		res, e = mGocodeCmdComplete(c, m.Complete.Fn, []byte(m.Complete.Src), pos)
	default:
		panic("Unsupported command: gocode: " + m.cmd)
	}

	return res, e
}

func init() {
	registry.Register("gocode_set", func(b *Broker) Caller {
		return &mGocode{cmd: "set"}
	})

	registry.Register("gocode_complete", func(b *Broker) Caller {
		return &mGocode{cmd: "complete"}
	})
}

func mGocodeServe(m *mGocode) error {
	argv := []string{m.Bin, "-s", "-sock", "tcp", "-addr", mGocodeAddr}
	attr := os.ProcAttr{
		Dir:   m.Home,
		Env:   envSlice(m.Env),
		Files: []*os.File{nil, nil, nil},
	}

	p, err := os.StartProcess(m.Bin, argv, &attr)
	if err == nil {
		byeDefer(func() {
			p.Kill()
		})
		go func() {
			_, err := p.Wait()
			if err != nil {
				logger.Println("gocode failed", err)
			}
		}()
	}
	return err
}

func mGocodeCmdSet(c *rpc.Client, k, v string) (res M, e string) {
	args := struct{ Arg0, Arg1 string }{k, v}
	reply := struct{ Arg0 string }{}

	if err := c.Call("RPC.RPC_set", &args, &reply); err != nil {
		e = "RPC error: " + err.Error()
	}
	options := map[string]string{}
	for _, s := range strings.Split(reply.Arg0, "\n") {
		v := strings.SplitN(strings.TrimSpace(s), " ", 2)
		if len(v) == 2 {
			options[v[0]] = v[1]
		}
	}
	res = M{"options": options}
	return
}

func mGocodeCmdComplete(c *rpc.Client, fn string, src []byte, pos int) (res M, e string) {
	args := struct {
		Arg0 []byte
		Arg1 string
		Arg2 int
	}{src, fn, pos}

	reply := struct {
		Arg0 []candidate
		Arg1 int
	}{}

	if err := c.Call("RPC.RPC_auto_complete", &args, &reply); err != nil {
		e = "RPC error: " + err.Error()
	}

	completions := []M{}
	for _, d := range reply.Arg0 {
		completions = append(completions, M{
			"class": d.Class.String(),
			"type":  d.Type,
			"name":  d.Name,
		})
	}
	res = M{"completions": completions}

	return
}
<commit_msg>* handle user settings on-the-fly * allow the client to send settings as part of all supported methods<commit_after>package main

import (
	"io/ioutil"
	"net/rpc"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"
)

const (
	mGocodeAddr = "127.0.0.1:57952"
)

var (
	mGocodeVars = struct {
		lck          sync.Mutex
		lastGopath   string
		lastBuiltins bool
	}{}
)

type mGocode struct {
	cmd      string
	Bin      string
	Env      map[string]string
	Home     string
	Dir      string
	Set      map[string]string
	Complete struct {
		Builtins bool
		Fn       string
		Src      string
		Pos      int
	}
}

func (m *mGocode) Call() (interface{}, string) {
	e := ""
	res := M{}
	c, err := rpc.Dial("tcp", mGocodeAddr)
	if err != nil {
		if serveErr := mGocodeServe(m); serveErr != nil {
			return res, "Error starting gocode server: " + serveErr.Error()
		}

		for i := 0; err != nil && i < 5; i += 1 {
			time.Sleep(20 * time.Millisecond)
			c, err = rpc.Dial("tcp", mGocodeAddr)
		}

		if err != nil {
			return res, "Error connecting to gocode server: " + err.Error()
		}
	}
	defer c.Close()

	for k, v := range m.Set {
		if _, e := mGocodeCmdSet(c, k, v); e != "" {
			logger.Print(e)
		}
	}

	switch m.cmd {
	case "set":
		res, e = mGocodeCmdSet(c, "\x00", "\x00")
	case "complete":
		if m.Complete.Src == "" {
			// this is here for testing, the client should always send the src
			s, _ := ioutil.ReadFile(m.Complete.Fn)
			m.Complete.Src = string(s)
		}

		pos := 0
		for i, _ := range m.Complete.Src {
			pos += 1
			if pos > m.Complete.Pos {
				pos = i
				break
			}
		}

		src := []byte(m.Complete.Src)
		fn := m.Complete.Fn
		if !filepath.IsAbs(fn) {
			fn = filepath.Join(orString(m.Dir, m.Home), orString(fn, "_.go"))
		}

		mGocodeVars.lck.Lock()
		defer mGocodeVars.lck.Unlock()

		if m.Complete.Builtins != mGocodeVars.lastBuiltins {
			builtins := "false"
			if m.Complete.Builtins {
				builtins = "true"
			}
			if _, e := mGocodeCmdSet(c, "propose-builtins", builtins); e != "" {
				logger.Print(e)
			} else {
				mGocodeVars.lastBuiltins = m.Complete.Builtins
			}
		}

		gopath := orString(m.Env["GOPATH"], os.Getenv("GOPATH"))
		if gopath != mGocodeVars.lastGopath {
			p := []string{}
			osArch := runtime.GOOS + "_" + runtime.GOARCH
			for _, s := range filepath.SplitList(gopath) {
				p = append(p, filepath.Join(s, "pkg", osArch))
			}
			libpath := strings.Join(p, string(filepath.ListSeparator))
			if _, e := mGocodeCmdSet(c, "lib-path", libpath); e != "" {
				logger.Print(e)
			} else {
				mGocodeVars.lastGopath = gopath
			}
		}
		res, e = mGocodeCmdComplete(c, fn, src, pos)
	default:
		panic("Unsupported command: gocode: " + m.cmd)
	}

	return res, e
}

func init() {
	registry.Register("gocode_set", func(b *Broker) Caller {
		return &mGocode{cmd: "set"}
	})

	registry.Register("gocode_complete", func(b *Broker) Caller {
		return &mGocode{cmd: "complete"}
	})
}

func mGocodeServe(m *mGocode) error {
	argv := []string{m.Bin, "-s", "-sock", "tcp", "-addr", mGocodeAddr}
	attr := os.ProcAttr{
		Dir:   m.Home,
		Env:   envSlice(m.Env),
		Files: []*os.File{nil, nil, nil},
	}

	p, err := os.StartProcess(m.Bin, argv, &attr)
	if err == nil {
		byeDefer(func() {
			p.Kill()
		})
		go func() {
			_, err := p.Wait()
			if err != nil {
				logger.Println("gocode failed", err)
			}
		}()
	}
	return err
}

func mGocodeCmdSet(c *rpc.Client, k, v string) (res M, e string) {
	args := struct{ Arg0, Arg1 string }{k, v}
	reply := struct{ Arg0 string }{}

	if err := c.Call("RPC.RPC_set", &args, &reply); err != nil {
		e = "RPC error: " + err.Error()
	}
	options := map[string]string{}
	for _, s := range strings.Split(reply.Arg0, "\n") {
		v := strings.SplitN(strings.TrimSpace(s), " ", 2)
		if len(v) == 2 {
			options[v[0]] = v[1]
		}
	}
	res = M{"options": options}
	return
}

func mGocodeCmdComplete(c *rpc.Client, fn string, src []byte, pos int) (res M, e string) {
	args := struct {
		Arg0 []byte
		Arg1 string
		Arg2 int
	}{src, fn, pos}

	reply := struct {
		Arg0 []candidate
		Arg1 int
	}{}

	if err := c.Call("RPC.RPC_auto_complete", &args, &reply); err != nil {
		e = "RPC error: " + err.Error()
	}

	completions := []M{}
	for _, d := range reply.Arg0 {
		completions = append(completions, M{
			"class": d.Class.String(),
			"type":  d.Type,
			"name":  d.Name,
		})
	}
	res = M{"completions": completions}

	return
}
<|endoftext|>


<commit_before>package proxy

import (
	"bufio"
	"crypto/tls"
	"errors"
	"io"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/fabiolb/fabio/auth"
	"github.com/fabiolb/fabio/config"
	"github.com/fabiolb/fabio/logger"
	"github.com/fabiolb/fabio/metrics"
	"github.com/fabiolb/fabio/noroute"
	"github.com/fabiolb/fabio/proxy/gzip"
	"github.com/fabiolb/fabio/route"
	"github.com/fabiolb/fabio/trace"
	"github.com/fabiolb/fabio/uuid"
)

// HTTPProxy is a dynamic reverse proxy for HTTP and HTTPS protocols.
type HTTPProxy struct {
	// Config is the proxy configuration as provided during startup.
	Config config.Proxy

	// Time returns the current time as the number of seconds since the epoch.
	// If Time is nil, time.Now is used.
	Time func() time.Time

	// Transport is the http connection pool configured with timeouts.
	// The proxy will panic if this value is nil.
	Transport http.RoundTripper

	// InsecureTransport is the http connection pool configured with
	// InsecureSkipVerify set. This is used for https proxies with
	// self-signed certs.
	InsecureTransport http.RoundTripper

	// Lookup returns a target host for the given request.
	// The proxy will panic if this value is nil.
	Lookup func(*http.Request) *route.Target

	// Requests is a timer metric which is updated for every request.
	Requests metrics.Timer

	// Noroute is a counter metric which is updated for every request
	// where Lookup() returns nil.
	Noroute metrics.Counter

	// Logger is the access logger for the requests.
	Logger logger.Logger

	// TracerCfg is the Open Tracing  configuration as provided during startup
	TracerCfg config.Tracing

	// UUID returns a unique id in uuid format.
	// If UUID is nil, uuid.NewUUID() is used.
	UUID func() string

	// Auth schemes registered with the server
	AuthSchemes map[string]auth.AuthScheme
}

func (p *HTTPProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if p.Lookup == nil {
		panic("no lookup function")
	}

	if p.Config.RequestID != "" {
		id := p.UUID
		if id == nil {
			id = uuid.NewUUID
		}
		r.Header.Set(p.Config.RequestID, id())
	}

	//Create Span
	span := trace.CreateSpan(r, p.TracerCfg.ServiceName)
	defer span.Finish()

	t := p.Lookup(r)

	if t == nil {
		status := p.Config.NoRouteStatus
		if status < 100 || status > 999 {
			status = http.StatusNotFound
		}
		w.WriteHeader(status)
		html := noroute.GetHTML()
		if html != "" {
			io.WriteString(w, html)
		}
		return
	}

	if t.AccessDeniedHTTP(r) {
		http.Error(w, "access denied", http.StatusForbidden)
		return
	}

	if !t.Authorized(r, w, p.AuthSchemes) {
		http.Error(w, "authorization failed", http.StatusUnauthorized)
		return
	}

	// build the request url since r.URL will get modified
	// by the reverse proxy and contains only the RequestURI anyway
	requestURL := &url.URL{
		Scheme:   scheme(r),
		Host:     r.Host,
		Path:     r.URL.Path,
		RawQuery: r.URL.RawQuery,
	}

	if t.RedirectCode != 0 && t.RedirectURL != nil {
		http.Redirect(w, r, t.RedirectURL.String(), t.RedirectCode)
		if t.Timer != nil {
			t.Timer.Update(0)
		}
		metrics.DefaultRegistry.GetTimer(key(t.RedirectCode)).Update(0)
		return
	}

	// build the real target url that is passed to the proxy
	targetURL := &url.URL{
		Scheme: t.URL.Scheme,
		Host:   t.URL.Host,
		Path:   r.URL.Path,
	}
	if t.URL.RawQuery == "" || r.URL.RawQuery == "" {
		targetURL.RawQuery = t.URL.RawQuery + r.URL.RawQuery
	} else {
		targetURL.RawQuery = t.URL.RawQuery + "&" + r.URL.RawQuery
	}

	if t.Host == "dst" {
		r.Host = targetURL.Host
	} else if t.Host != "" {
		r.Host = t.Host
	}

	// TODO(fs): The HasPrefix check seems redundant since the lookup function should
	// TODO(fs): have found the target based on the prefix but there may be other
	// TODO(fs): matchers which may have different rules. I'll keep this for
	// TODO(fs): a defensive approach.
	if t.StripPath != "" && strings.HasPrefix(r.URL.Path, t.StripPath) {
		targetURL.Path = targetURL.Path[len(t.StripPath):]
	}

	if err := addHeaders(r, p.Config, t.StripPath); err != nil {
		http.Error(w, "cannot parse "+r.RemoteAddr, http.StatusInternalServerError)
		return
	}

	if err := addResponseHeaders(w, r, p.Config); err != nil {
		http.Error(w, "cannot add response headers", http.StatusInternalServerError)
		return
	}

	//Add OpenTrace Headers to response
	trace.InjectHeaders(span, r)

	upgrade, accept := r.Header.Get("Upgrade"), r.Header.Get("Accept")

	tr := p.Transport
	if t.TLSSkipVerify {
		tr = p.InsecureTransport
	}

	var h http.Handler
	switch {
	case upgrade == "websocket" || upgrade == "Websocket":
		r.URL = targetURL
		if targetURL.Scheme == "https" || targetURL.Scheme == "wss" {
			h = newWSHandler(targetURL.Host, func(network, address string) (net.Conn, error) {
				return tls.Dial(network, address, tr.(*http.Transport).TLSClientConfig)
			})
		} else {
			h = newWSHandler(targetURL.Host, net.Dial)
		}

	case accept == "text/event-stream":
		// use the flush interval for SSE (server-sent events)
		// must be > 0s to be effective
		h = newHTTPProxy(targetURL, tr, p.Config.FlushInterval)

	default:
		h = newHTTPProxy(targetURL, tr, p.Config.GlobalFlushInterval)
	}

	if p.Config.GZIPContentTypes != nil {
		h = gzip.NewGzipHandler(h, p.Config.GZIPContentTypes)
	}

	timeNow := p.Time
	if timeNow == nil {
		timeNow = time.Now
	}

	start := timeNow()
	rw := &responseWriter{w: w}
	h.ServeHTTP(rw, r)
	end := timeNow()
	dur := end.Sub(start)

	if p.Requests != nil {
		p.Requests.Update(dur)
	}
	if t.Timer != nil {
		t.Timer.Update(dur)
	}
	if rw.code <= 0 {
		return
	}

	metrics.DefaultRegistry.GetTimer(key(rw.code)).Update(dur)

	// write access log
	if p.Logger != nil {
		p.Logger.Log(&logger.Event{
			Start:   start,
			End:     end,
			Request: r,
			Response: &http.Response{
				StatusCode:    rw.code,
				ContentLength: int64(rw.size),
			},
			RequestURL:      requestURL,
			UpstreamAddr:    targetURL.Host,
			UpstreamService: t.Service,
			UpstreamURL:     targetURL,
		})
	}
}

func key(code int) string {
	b := []byte("http.status.")
	b = strconv.AppendInt(b, int64(code), 10)
	return string(b)
}

// responseWriter wraps an http.ResponseWriter to capture the status code and
// the size of the response. It also implements http.Hijacker to forward
// hijacking the connection to the wrapped writer if supported.
type responseWriter struct {
	w    http.ResponseWriter
	code int
	size int
}

func (rw *responseWriter) Header() http.Header {
	return rw.w.Header()
}

func (rw *responseWriter) Write(b []byte) (int, error) {
	n, err := rw.w.Write(b)
	rw.size += n
	return n, err
}

func (rw *responseWriter) WriteHeader(statusCode int) {
	rw.w.WriteHeader(statusCode)
	rw.code = statusCode
}

var errNoHijacker = errors.New("not a hijacker")

func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	if hj, ok := rw.w.(http.Hijacker); ok {
		return hj.Hijack()
	}
	return nil, nil, errNoHijacker
}
<commit_msg>ensure absolute path after strip to maintain rfc complaince (#645)<commit_after>package proxy

import (
	"bufio"
	"crypto/tls"
	"errors"
	"io"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/fabiolb/fabio/auth"
	"github.com/fabiolb/fabio/config"
	"github.com/fabiolb/fabio/logger"
	"github.com/fabiolb/fabio/metrics"
	"github.com/fabiolb/fabio/noroute"
	"github.com/fabiolb/fabio/proxy/gzip"
	"github.com/fabiolb/fabio/route"
	"github.com/fabiolb/fabio/trace"
	"github.com/fabiolb/fabio/uuid"
)

// HTTPProxy is a dynamic reverse proxy for HTTP and HTTPS protocols.
type HTTPProxy struct {
	// Config is the proxy configuration as provided during startup.
	Config config.Proxy

	// Time returns the current time as the number of seconds since the epoch.
	// If Time is nil, time.Now is used.
	Time func() time.Time

	// Transport is the http connection pool configured with timeouts.
	// The proxy will panic if this value is nil.
	Transport http.RoundTripper

	// InsecureTransport is the http connection pool configured with
	// InsecureSkipVerify set. This is used for https proxies with
	// self-signed certs.
	InsecureTransport http.RoundTripper

	// Lookup returns a target host for the given request.
	// The proxy will panic if this value is nil.
	Lookup func(*http.Request) *route.Target

	// Requests is a timer metric which is updated for every request.
	Requests metrics.Timer

	// Noroute is a counter metric which is updated for every request
	// where Lookup() returns nil.
	Noroute metrics.Counter

	// Logger is the access logger for the requests.
	Logger logger.Logger

	// TracerCfg is the Open Tracing  configuration as provided during startup
	TracerCfg config.Tracing

	// UUID returns a unique id in uuid format.
	// If UUID is nil, uuid.NewUUID() is used.
	UUID func() string

	// Auth schemes registered with the server
	AuthSchemes map[string]auth.AuthScheme
}

func (p *HTTPProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if p.Lookup == nil {
		panic("no lookup function")
	}

	if p.Config.RequestID != "" {
		id := p.UUID
		if id == nil {
			id = uuid.NewUUID
		}
		r.Header.Set(p.Config.RequestID, id())
	}

	//Create Span
	span := trace.CreateSpan(r, p.TracerCfg.ServiceName)
	defer span.Finish()

	t := p.Lookup(r)

	if t == nil {
		status := p.Config.NoRouteStatus
		if status < 100 || status > 999 {
			status = http.StatusNotFound
		}
		w.WriteHeader(status)
		html := noroute.GetHTML()
		if html != "" {
			io.WriteString(w, html)
		}
		return
	}

	if t.AccessDeniedHTTP(r) {
		http.Error(w, "access denied", http.StatusForbidden)
		return
	}

	if !t.Authorized(r, w, p.AuthSchemes) {
		http.Error(w, "authorization failed", http.StatusUnauthorized)
		return
	}

	// build the request url since r.URL will get modified
	// by the reverse proxy and contains only the RequestURI anyway
	requestURL := &url.URL{
		Scheme:   scheme(r),
		Host:     r.Host,
		Path:     r.URL.Path,
		RawQuery: r.URL.RawQuery,
	}

	if t.RedirectCode != 0 && t.RedirectURL != nil {
		http.Redirect(w, r, t.RedirectURL.String(), t.RedirectCode)
		if t.Timer != nil {
			t.Timer.Update(0)
		}
		metrics.DefaultRegistry.GetTimer(key(t.RedirectCode)).Update(0)
		return
	}

	// build the real target url that is passed to the proxy
	targetURL := &url.URL{
		Scheme: t.URL.Scheme,
		Host:   t.URL.Host,
		Path:   r.URL.Path,
	}
	if t.URL.RawQuery == "" || r.URL.RawQuery == "" {
		targetURL.RawQuery = t.URL.RawQuery + r.URL.RawQuery
	} else {
		targetURL.RawQuery = t.URL.RawQuery + "&" + r.URL.RawQuery
	}

	if t.Host == "dst" {
		r.Host = targetURL.Host
	} else if t.Host != "" {
		r.Host = t.Host
	}

	// TODO(fs): The HasPrefix check seems redundant since the lookup function should
	// TODO(fs): have found the target based on the prefix but there may be other
	// TODO(fs): matchers which may have different rules. I'll keep this for
	// TODO(fs): a defensive approach.
	if t.StripPath != "" && strings.HasPrefix(r.URL.Path, t.StripPath) {
		targetURL.Path = targetURL.Path[len(t.StripPath):]
		// ensure absolute path after stripping to maintain compliance with
		// section 5.3 of RFC7230 (https://tools.ietf.org/html/rfc7230#section-5.3)
		if !strings.HasPrefix(targetURL.Path, "/") {
			targetURL.Path = "/" + targetURL.Path
		}
	}

	if err := addHeaders(r, p.Config, t.StripPath); err != nil {
		http.Error(w, "cannot parse "+r.RemoteAddr, http.StatusInternalServerError)
		return
	}

	if err := addResponseHeaders(w, r, p.Config); err != nil {
		http.Error(w, "cannot add response headers", http.StatusInternalServerError)
		return
	}

	//Add OpenTrace Headers to response
	trace.InjectHeaders(span, r)

	upgrade, accept := r.Header.Get("Upgrade"), r.Header.Get("Accept")

	tr := p.Transport
	if t.TLSSkipVerify {
		tr = p.InsecureTransport
	}

	var h http.Handler
	switch {
	case upgrade == "websocket" || upgrade == "Websocket":
		r.URL = targetURL
		if targetURL.Scheme == "https" || targetURL.Scheme == "wss" {
			h = newWSHandler(targetURL.Host, func(network, address string) (net.Conn, error) {
				return tls.Dial(network, address, tr.(*http.Transport).TLSClientConfig)
			})
		} else {
			h = newWSHandler(targetURL.Host, net.Dial)
		}

	case accept == "text/event-stream":
		// use the flush interval for SSE (server-sent events)
		// must be > 0s to be effective
		h = newHTTPProxy(targetURL, tr, p.Config.FlushInterval)

	default:
		h = newHTTPProxy(targetURL, tr, p.Config.GlobalFlushInterval)
	}

	if p.Config.GZIPContentTypes != nil {
		h = gzip.NewGzipHandler(h, p.Config.GZIPContentTypes)
	}

	timeNow := p.Time
	if timeNow == nil {
		timeNow = time.Now
	}

	start := timeNow()
	rw := &responseWriter{w: w}
	h.ServeHTTP(rw, r)
	end := timeNow()
	dur := end.Sub(start)

	if p.Requests != nil {
		p.Requests.Update(dur)
	}
	if t.Timer != nil {
		t.Timer.Update(dur)
	}
	if rw.code <= 0 {
		return
	}

	metrics.DefaultRegistry.GetTimer(key(rw.code)).Update(dur)

	// write access log
	if p.Logger != nil {
		p.Logger.Log(&logger.Event{
			Start:   start,
			End:     end,
			Request: r,
			Response: &http.Response{
				StatusCode:    rw.code,
				ContentLength: int64(rw.size),
			},
			RequestURL:      requestURL,
			UpstreamAddr:    targetURL.Host,
			UpstreamService: t.Service,
			UpstreamURL:     targetURL,
		})
	}
}

func key(code int) string {
	b := []byte("http.status.")
	b = strconv.AppendInt(b, int64(code), 10)
	return string(b)
}

// responseWriter wraps an http.ResponseWriter to capture the status code and
// the size of the response. It also implements http.Hijacker to forward
// hijacking the connection to the wrapped writer if supported.
type responseWriter struct {
	w    http.ResponseWriter
	code int
	size int
}

func (rw *responseWriter) Header() http.Header {
	return rw.w.Header()
}

func (rw *responseWriter) Write(b []byte) (int, error) {
	n, err := rw.w.Write(b)
	rw.size += n
	return n, err
}

func (rw *responseWriter) WriteHeader(statusCode int) {
	rw.w.WriteHeader(statusCode)
	rw.code = statusCode
}

var errNoHijacker = errors.New("not a hijacker")

func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	if hj, ok := rw.w.(http.Hijacker); ok {
		return hj.Hijack()
	}
	return nil, nil, errNoHijacker
}
<|endoftext|>


<commit_before>package proxy

// This file was copied from https://github.com/zenazn/goji/blob/master/web/middleware/request_id.go
// at commit 8459820, decoupled, and then updated to use 1.7+ contexts using the tutorial
// at https://joeshaw.org/revisiting-context-and-http-handler-for-go-17/.

import (
	"context"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"net/http"
	"os"
	"strings"
	"sync/atomic"
)

const requestIDKey = "ec2metaproxyReqID"

var reqIDPrefix string
var reqID uint64

/*
A quick note on the statistics here: we're trying to calculate the chance that
two randomly generated base62 prefixes will collide. We use the formula from
http://en.wikipedia.org/wiki/Birthday_problem

P[m, n] \approx 1 - e^{-m^2/2n}

We ballpark an upper bound for $m$ by imagining (for whatever reason) a server
that restarts every second over 10 years, for $m = 86400 * 365 * 10 = 315360000$

For a $k$ character base-62 identifier, we have $n(k) = 62^k$

Plugging this in, we find $P[m, n(10)] \approx 5.75%$, which is good enough for
our purposes, and is surely more than anyone would ever need in practice -- a
process that is rebooted a handful of times a day for a hundred years has less
than a millionth of a percent chance of generating two colliding IDs.
*/

func init() {
	hostname, err := os.Hostname()
	if hostname == "" || err != nil {
		hostname = "localhost"
	}
	var buf [12]byte
	var b64 string
	for len(b64) < 10 {
		_, readErr := rand.Read(buf[:])
		if readErr != nil {
			panic(fmt.Sprintf("Error creating random prefix for ec2metaproxy request IDs: %+v", readErr))
		}
		b64 = base64.StdEncoding.EncodeToString(buf[:])
		b64 = strings.NewReplacer("+", "", "/", "").Replace(b64)
	}
	reqIDPrefix = fmt.Sprintf("%s/%s", hostname, b64[0:10])
}

func newContextWithRequestID(ctx context.Context, req *http.Request) context.Context {
	id := req.Header.Get("x-ec2metaproxy-id")
	if id == "" {
		id = fmt.Sprintf("%s-%06d", reqIDPrefix, atomic.AddUint64(&reqID, 1))
	}
	return context.WithValue(ctx, requestIDKey, id)
}

func requestIDFromContext(ctx context.Context) string {
	return ctx.Value(requestIDKey).(string)
}

// RequestID is a middleware that injects a request ID into the context of each
// request. A request ID is a string of the form "host.example.com/random-0001",
// where "random" is a base62 random string that uniquely identifies this go
// process, and where the last number is an atomically incremented request
// counter.
func RequestID(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := newContextWithRequestID(r.Context(), r)
		h.ServeHTTP(w, r.WithContext(ctx))
	})
}
<commit_msg>feat: add request ID to response headers<commit_after>package proxy

// This file was copied from https://github.com/zenazn/goji/blob/master/web/middleware/request_id.go
// at commit 8459820, decoupled, and then updated to use 1.7+ contexts using the tutorial
// at https://joeshaw.org/revisiting-context-and-http-handler-for-go-17/.

import (
	"context"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"net/http"
	"strings"
	"sync/atomic"
)

const (
	requestIDContextKey = "ec2metaproxyReqID"
	requestIDHeaderKey  = "X-EC2Metaproxy-ID"
)

var reqIDPrefix string
var reqID uint64

/*
A quick note on the statistics here: we're trying to calculate the chance that
two randomly generated base62 prefixes will collide. We use the formula from
http://en.wikipedia.org/wiki/Birthday_problem

P[m, n] \approx 1 - e^{-m^2/2n}

We ballpark an upper bound for $m$ by imagining (for whatever reason) a server
that restarts every second over 10 years, for $m = 86400 * 365 * 10 = 315360000$

For a $k$ character base-62 identifier, we have $n(k) = 62^k$

Plugging this in, we find $P[m, n(10)] \approx 5.75%$, which is good enough for
our purposes, and is surely more than anyone would ever need in practice -- a
process that is rebooted a handful of times a day for a hundred years has less
than a millionth of a percent chance of generating two colliding IDs.
*/

func init() {
	var buf [12]byte
	var b64 string
	for len(b64) < 10 {
		_, readErr := rand.Read(buf[:])
		if readErr != nil {
			panic(fmt.Sprintf("Error creating random prefix for ec2metaproxy request IDs: %+v", readErr))
		}
		b64 = base64.StdEncoding.EncodeToString(buf[:])
		b64 = strings.NewReplacer("+", "", "/", "").Replace(b64)
	}
	reqIDPrefix = b64[0:10]
}

func newContextWithRequestID(ctx context.Context, req *http.Request) context.Context {
	id := req.Header.Get(requestIDHeaderKey)
	if id == "" {
		id = fmt.Sprintf("%s-%06d", reqIDPrefix, atomic.AddUint64(&reqID, 1))
	}
	return context.WithValue(ctx, requestIDContextKey, id)
}

func requestIDFromContext(ctx context.Context) string {
	return ctx.Value(requestIDContextKey).(string)
}

// RequestID is a middleware that injects a request ID into the context of each
// request. A request ID is a string of the form "host.example.com/random-0001",
// where "random" is a base62 random string that uniquely identifies this go
// process, and where the last number is an atomically incremented request
// counter.
func RequestID(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := newContextWithRequestID(r.Context(), r)
		w.Header().Set(requestIDHeaderKey, requestIDFromContext(ctx))
		h.ServeHTTP(w, r.WithContext(ctx))
	})
}
<|endoftext|>


<commit_before>package proxy

import (
	"bufio"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
)

// msgReader is a helper that reads values from a PostgreSQL message.
type msgReader struct {
	reader            *bufio.Reader
	buf               [128]byte
	msgBytesRemaining int32
	err               error
}

// Err returns any error that the msgReader has experienced
func (r *msgReader) Err() error {
	return r.err
}

// fatal tells r that a Fatal error has occurred
func (r *msgReader) fatal(err error) {
	r.err = err
}

// rxMsg reads the type and size of the next message.
func (r *msgReader) rxMsg() (t byte, err error) {
	if r.err != nil {
		return 0, err
	}
	if r.msgBytesRemaining > 0 {
		io.CopyN(ioutil.Discard, r.reader, int64(r.msgBytesRemaining))
	}
	fmt.Println("1")
	b := r.buf[0:5]
	fmt.Println("2")
	_, err = io.ReadFull(r.reader, b)
	fmt.Println(err)
	if r.err != nil {
		fmt.Println(err)
	}
	fmt.Println("3")
	r.msgBytesRemaining = int32(binary.BigEndian.Uint32(b[1:])) - 4
	fmt.Println("4")
	return b[0], err
}

func (r *msgReader) readByte() byte {
	if r.err != nil {
		return 0
	}

	r.msgBytesRemaining -= 1
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return 0
	}

	b, err := r.reader.ReadByte()
	if err != nil {
		r.fatal(err)
		return 0
	}

	return b
}

func (r *msgReader) readInt16() int16 {
	if r.err != nil {
		return 0
	}

	r.msgBytesRemaining -= 2
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return 0
	}

	b := r.buf[0:2]
	_, err := io.ReadFull(r.reader, b)
	if err != nil {
		r.fatal(err)
		return 0
	}

	return int16(binary.BigEndian.Uint16(b))
}

func (r *msgReader) readInt32() int32 {
	if r.err != nil {
		return 0
	}

	r.msgBytesRemaining -= 4
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return 0
	}

	b := r.buf[0:4]
	_, err := io.ReadFull(r.reader, b)
	if err != nil {
		r.fatal(err)
		return 0
	}

	return int32(binary.BigEndian.Uint32(b))
}

func (r *msgReader) readInt64() int64 {
	if r.err != nil {
		return 0
	}

	r.msgBytesRemaining -= 8
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return 0
	}

	b := r.buf[0:8]
	_, err := io.ReadFull(r.reader, b)
	if err != nil {
		r.fatal(err)
		return 0
	}

	return int64(binary.BigEndian.Uint64(b))
}

func (r *msgReader) readOid() Oid {
	return Oid(r.readInt32())
}

// readCString reads a null terminated string
func (r *msgReader) readCString() string {
	if r.err != nil {
		return ""
	}

	b, err := r.reader.ReadBytes(0)
	if err != nil {
		r.fatal(err)
		return ""
	}

	r.msgBytesRemaining -= int32(len(b))
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return ""
	}

	return string(b[0 : len(b)-1])
}

// readString reads count bytes and returns as string
func (r *msgReader) readString(count int32) string {
	if r.err != nil {
		return ""
	}

	r.msgBytesRemaining -= count
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return ""
	}

	var b []byte
	if count <= int32(len(r.buf)) {
		b = r.buf[0:int(count)]
	} else {
		b = make([]byte, int(count))
	}

	_, err := io.ReadFull(r.reader, b)
	if err != nil {
		r.fatal(err)
		return ""
	}

	return string(b)
}

// readBytes reads count bytes and returns as []byte
func (r *msgReader) readBytes(count int32) []byte {
	if r.err != nil {
		return nil
	}

	r.msgBytesRemaining -= count
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return nil
	}

	b := make([]byte, int(count))

	_, err := io.ReadFull(r.reader, b)
	if err != nil {
		r.fatal(err)
		return nil
	}

	return b
}
<commit_msg>Update<commit_after>package proxy

import (
	"bufio"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
)

// msgReader is a helper that reads values from a PostgreSQL message.
type msgReader struct {
	reader            *bufio.Reader
	buf               [128]byte
	msgBytesRemaining int32
	err               error
}

// Err returns any error that the msgReader has experienced
func (r *msgReader) Err() error {
	return r.err
}

// fatal tells r that a Fatal error has occurred
func (r *msgReader) fatal(err error) {
	r.err = err
}

// rxMsg reads the type and size of the next message.
func (r *msgReader) rxMsg() (t byte, err error) {
	if r.err != nil {
		return 0, err
	}
	if r.msgBytesRemaining > 0 {
		io.CopyN(ioutil.Discard, r.reader, int64(r.msgBytesRemaining))
	}
	fmt.Println("1")
	b := r.buf[0:5]
	fmt.Println("2")
	fmt.Printf("%#v\n", b)
	_, err = io.ReadFull(r.reader, b)
	fmt.Println(err)
	if r.err != nil {
		fmt.Println(err)
	}
	fmt.Println("3")
	r.msgBytesRemaining = int32(binary.BigEndian.Uint32(b[1:])) - 4
	fmt.Println("4")
	return b[0], err
}

func (r *msgReader) readByte() byte {
	if r.err != nil {
		return 0
	}

	r.msgBytesRemaining -= 1
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return 0
	}

	b, err := r.reader.ReadByte()
	if err != nil {
		r.fatal(err)
		return 0
	}

	return b
}

func (r *msgReader) readInt16() int16 {
	if r.err != nil {
		return 0
	}

	r.msgBytesRemaining -= 2
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return 0
	}

	b := r.buf[0:2]
	_, err := io.ReadFull(r.reader, b)
	if err != nil {
		r.fatal(err)
		return 0
	}

	return int16(binary.BigEndian.Uint16(b))
}

func (r *msgReader) readInt32() int32 {
	if r.err != nil {
		return 0
	}

	r.msgBytesRemaining -= 4
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return 0
	}

	b := r.buf[0:4]
	_, err := io.ReadFull(r.reader, b)
	if err != nil {
		r.fatal(err)
		return 0
	}

	return int32(binary.BigEndian.Uint32(b))
}

func (r *msgReader) readInt64() int64 {
	if r.err != nil {
		return 0
	}

	r.msgBytesRemaining -= 8
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return 0
	}

	b := r.buf[0:8]
	_, err := io.ReadFull(r.reader, b)
	if err != nil {
		r.fatal(err)
		return 0
	}

	return int64(binary.BigEndian.Uint64(b))
}

func (r *msgReader) readOid() Oid {
	return Oid(r.readInt32())
}

// readCString reads a null terminated string
func (r *msgReader) readCString() string {
	if r.err != nil {
		return ""
	}

	b, err := r.reader.ReadBytes(0)
	if err != nil {
		r.fatal(err)
		return ""
	}

	r.msgBytesRemaining -= int32(len(b))
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return ""
	}

	return string(b[0 : len(b)-1])
}

// readString reads count bytes and returns as string
func (r *msgReader) readString(count int32) string {
	if r.err != nil {
		return ""
	}

	r.msgBytesRemaining -= count
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return ""
	}

	var b []byte
	if count <= int32(len(r.buf)) {
		b = r.buf[0:int(count)]
	} else {
		b = make([]byte, int(count))
	}

	_, err := io.ReadFull(r.reader, b)
	if err != nil {
		r.fatal(err)
		return ""
	}

	return string(b)
}

// readBytes reads count bytes and returns as []byte
func (r *msgReader) readBytes(count int32) []byte {
	if r.err != nil {
		return nil
	}

	r.msgBytesRemaining -= count
	if r.msgBytesRemaining < 0 {
		r.fatal(errors.New("read past end of message"))
		return nil
	}

	b := make([]byte, int(count))

	_, err := io.ReadFull(r.reader, b)
	if err != nil {
		r.fatal(err)
		return nil
	}

	return b
}
<|endoftext|>


<commit_before>package web

import (
	"encoding/json"
	"sync"

	"time"

	"github.com/Sirupsen/logrus"
	"github.com/go-iris2/iris2"
	"github.com/go-iris2/iris2/adaptors/websocket"
	"github.com/rikvdh/ci/lib/builder"
	"github.com/rikvdh/ci/models"
)

type BuildList struct {
	Running []models.Job `json:"running"`
	Queue   []models.Job `json:"queue"`
}

func getBuildList() []byte {
	var msg BuildList

	models.Handle().Where("status = ?", models.StatusBusy).Order("start DESC").Find(&msg.Running)
	models.Handle().Where("status = ?", models.StatusNew).Order("start DESC").Find(&msg.Queue)

	data, _ := json.Marshal(msg)
	return data
}

type jsonData struct {
	Action          string `json:"action"`
	ID              int    `json:"id,omitempty"`
	CurrentPosition int64  `json:"current_position,omitempty"`
	Data            string `json:"data,omitempty"`
}

func startWs(app *iris2.Framework) {
	ws := websocket.New(websocket.Config{
		Endpoint:         "/ws",
		ClientSourcePath: "/ws.js",
	})

	app.Adapt(ws)

	var mu sync.RWMutex

	conns := make(map[string]websocket.Connection)

	go func() {
		ch := builder.GetEventChannel()
		for {
			<-ch

			data := getBuildList()
			logrus.Infof("Emit new build-list via websocket to %d connections", len(conns))
			mu.RLock()
			for _, con := range conns {
				con.EmitMessage(data)
			}
			mu.RUnlock()
		}
	}()

	ws.OnConnection(func(c websocket.Connection) {
		ctx := c.Context()
		if ctx.Session().GetString("authenticated") != "true" {
			go func() {
				// its a hack, but for some reason disconnecting here fails..
				time.Sleep(time.Second * 2)
				c.Disconnect()
			}()
			return
		}
		mu.Lock()
		conns[c.ID()] = c
		mu.Unlock()

		c.EmitMessage(getBuildList())

		c.OnMessage(func(d []byte) {
			var req jsonData
			json.Unmarshal(d, &req)
			switch req.Action {
			case "build":
				item := models.Branch{}
				models.Handle().Preload("Build").Where("id = ?", req.ID).First(&item)
				if item.ID > 0 {
					models.ScheduleJob(item.Build.ID, item.ID, item.LastReference)
				} else {
					logrus.Warnf("error, branch not found: %v", req)
				}
			case "logpos":
				item := models.Job{}
				models.Handle().First(&item, req.ID)
				if item.ID > 0 {
					buf := builder.GetLogFromPos(&item, req.CurrentPosition)
					rep := jsonData{
						Action:          req.Action,
						ID:              req.ID,
						Data:            buf,
						CurrentPosition: int64(len(buf)) + req.CurrentPosition,
					}
					rb, _ := json.Marshal(rep)
					c.EmitMessage(rb)
				} else {
					logrus.Warnf("logreader: %v", req)
				}
			}
		})

		c.OnDisconnect(func() {
			mu.Lock()
			delete(conns, c.ID())
			mu.Unlock()
		})
	})
}
<commit_msg>Minor cleanup of buildList updater<commit_after>package web

import (
	"encoding/json"
	"sync"

	"time"

	"github.com/Sirupsen/logrus"
	"github.com/go-iris2/iris2"
	"github.com/go-iris2/iris2/adaptors/websocket"
	"github.com/rikvdh/ci/lib/builder"
	"github.com/rikvdh/ci/models"
)

type BuildList struct {
	Running []models.Job `json:"running"`
	Queue   []models.Job `json:"queue"`
}

func getBuildList() []byte {
	var msg BuildList

	models.Handle().Where("status = ?", models.StatusBusy).Order("start DESC").Find(&msg.Running)
	models.Handle().Where("status = ?", models.StatusNew).Order("start DESC").Find(&msg.Queue)

	data, _ := json.Marshal(msg)
	return data
}

type jsonData struct {
	Action          string `json:"action"`
	ID              int    `json:"id,omitempty"`
	CurrentPosition int64  `json:"current_position,omitempty"`
	Data            string `json:"data,omitempty"`
}

func startWs(app *iris2.Framework) {
	ws := websocket.New(websocket.Config{
		Endpoint:         "/ws",
		ClientSourcePath: "/ws.js",
	})

	app.Adapt(ws)

	var mu sync.RWMutex

	conns := make(map[string]websocket.Connection)

	go func() {
		for range builder.GetEventChannel() {
			data := getBuildList()
			logrus.Infof("Emit new build-list via websocket to %d connections", len(conns))
			mu.RLock()
			for _, con := range conns {
				con.EmitMessage(data)
			}
			mu.RUnlock()
		}
	}()

	ws.OnConnection(func(c websocket.Connection) {
		ctx := c.Context()
		if ctx.Session().GetString("authenticated") != "true" {
			go func() {
				// its a hack, but for some reason disconnecting here fails..
				time.Sleep(time.Second * 2)
				c.Disconnect()
			}()
			return
		}
		mu.Lock()
		conns[c.ID()] = c
		mu.Unlock()

		c.EmitMessage(getBuildList())

		c.OnMessage(func(d []byte) {
			var req jsonData
			json.Unmarshal(d, &req)
			switch req.Action {
			case "build":
				item := models.Branch{}
				models.Handle().Preload("Build").Where("id = ?", req.ID).First(&item)
				if item.ID > 0 {
					models.ScheduleJob(item.Build.ID, item.ID, item.LastReference)
				} else {
					logrus.Warnf("error, branch not found: %v", req)
				}
			case "logpos":
				item := models.Job{}
				models.Handle().First(&item, req.ID)
				if item.ID > 0 {
					buf := builder.GetLogFromPos(&item, req.CurrentPosition)
					rep := jsonData{
						Action:          req.Action,
						ID:              req.ID,
						Data:            buf,
						CurrentPosition: int64(len(buf)) + req.CurrentPosition,
					}
					rb, _ := json.Marshal(rep)
					c.EmitMessage(rb)
				} else {
					logrus.Warnf("logreader: %v", req)
				}
			}
		})

		c.OnDisconnect(func() {
			mu.Lock()
			delete(conns, c.ID())
			mu.Unlock()
		})
	})
}
<|endoftext|>


<commit_before>//
// SPDX-License-Identifier: BSD-3-Clause
//

package redfish

import (
	"encoding/json"

	"github.com/stmcginnis/gofish/common"
)

// RAIDType is
type RAIDType string

const (

	// RAID0RAIDType A placement policy where consecutive logical blocks of
	// data are uniformly distributed across a set of independent storage
	// devices without offering any form of redundancy. This is commonly
	// referred to as data striping. This form of RAID will encounter data
	// loss with the failure of any storage device in the set.
	RAID0RAIDType RAIDType = "RAID0"
	// RAID1RAIDType A placement policy where each logical block of data is
	// stored on more than one independent storage device. This is commonly
	// referred to as mirroring. Data stored using this form of RAID is able
	// to survive a single storage device failure without data loss.
	RAID1RAIDType RAIDType = "RAID1"
	// RAID3RAIDType A placement policy using parity-based protection where
	// logical bytes of data are uniformly distributed across a set of
	// independent storage devices and where the parity is stored on a
	// dedicated independent storage device. Data stored using this form of
	// RAID is able to survive a single storage device failure without data
	// loss. If the storage devices use rotating media, they are assumed to
	// be rotationally synchronized, and the data stripe size should be no
	// larger than the exported block size.
	RAID3RAIDType RAIDType = "RAID3"
	// RAID4RAIDType A placement policy using parity-based protection where
	// logical blocks of data are uniformly distributed across a set of
	// independent storage devices and where the parity is stored on a
	// dedicated independent storage device. Data stored using this form of
	// RAID is able to survive a single storage device failure without data
	// loss.
	RAID4RAIDType RAIDType = "RAID4"
	// RAID5RAIDType A placement policy using parity-based protection for
	// storing stripes of 'n' logical blocks of data and one logical block of
	// parity across a set of 'n+1' independent storage devices where the
	// parity and data blocks are interleaved across the storage devices.
	// Data stored using this form of RAID is able to survive a single
	// storage device failure without data loss.
	RAID5RAIDType RAIDType = "RAID5"
	// RAID6RAIDType A placement policy using parity-based protection for
	// storing stripes of 'n' logical blocks of data and two logical blocks
	// of independent parity across a set of 'n+2' independent storage
	// devices where the parity and data blocks are interleaved across the
	// storage devices. Data stored using this form of RAID is able to
	// survive any two independent storage device failures without data loss.
	RAID6RAIDType RAIDType = "RAID6"
	// RAID10RAIDType A placement policy that creates a striped device (RAID
	// 0) over a set of mirrored devices (RAID 1). This is commonly referred
	// to as RAID 1/0. Data stored using this form of RAID is able to survive
	// storage device failures in each RAID 1 set without data loss.
	RAID10RAIDType RAIDType = "RAID10"
	// RAID01RAIDType A data placement policy that creates a mirrored device
	// (RAID 1) over a set of striped devices (RAID 0). This is commonly
	// referred to as RAID 0+1 or RAID 0/1. Data stored using this form of
	// RAID is able to survive a single RAID 0 data set failure without data
	// loss.
	RAID01RAIDType RAIDType = "RAID01"
	// RAID6TPRAIDType A placement policy that uses parity-based protection
	// for storing stripes of 'n' logical blocks of data and three logical
	// blocks of independent parity across a set of 'n+3' independent storage
	// devices where the parity and data blocks are interleaved across the
	// storage devices. This is commonly referred to as Triple Parity RAID.
	// Data stored using this form of RAID is able to survive any three
	// independent storage device failures without data loss.
	RAID6TPRAIDType RAIDType = "RAID6TP"
	// RAID1ERAIDType A placement policy that uses a form of mirroring
	// implemented over a set of independent storage devices where logical
	// blocks are duplicated on a pair of independent storage devices so that
	// data is uniformly distributed across the storage devices. This is
	// commonly referred to as RAID 1 Enhanced. Data stored using this form
	// of RAID is able to survive a single storage device failure without
	// data loss.
	RAID1ERAIDType RAIDType = "RAID1E"
	// RAID50RAIDType A placement policy that uses a RAID 0 stripe set over
	// two or more RAID 5 sets of independent storage devices. Data stored
	// using this form of RAID is able to survive a single storage device
	// failure within each RAID 5 set without data loss.
	RAID50RAIDType RAIDType = "RAID50"
	// RAID60RAIDType A placement policy that uses a RAID 0 stripe set over
	// two or more RAID 6 sets of independent storage devices. Data stored
	// using this form of RAID is able to survive two device failures within
	// each RAID 6 set without data loss.
	RAID60RAIDType RAIDType = "RAID60"
	// RAID00RAIDType A placement policy that creates a RAID 0 stripe set
	// over two or more RAID 0 sets. This is commonly referred to as RAID
	// 0+0. This form of data layout is not fault tolerant; if any storage
	// device fails there will be data loss.
	RAID00RAIDType RAIDType = "RAID00"
	// RAID10ERAIDType A placement policy that uses a RAID 0 stripe set over
	// two or more RAID 10 sets. This is commonly referred to as Enhanced
	// RAID 10. Data stored using this form of RAID is able to survive a
	// single device failure within each nested RAID 1 set without data loss.
	RAID10ERAIDType RAIDType = "RAID10E"
	// RAID1TripleRAIDType A placement policy where each logical block of
	// data is mirrored three times across a set of three independent storage
	// devices. This is commonly referred to as three-way mirroring. This
	// form of RAID can survive two device failures without data loss.
	RAID1TripleRAIDType RAIDType = "RAID1Triple"
	// RAID10TripleRAIDType A placement policy that uses a striped device
	// (RAID 0) over a set of triple mirrored devices (RAID 1Triple). This
	// form of RAID can survive up to two failures in each triple mirror set
	// without data loss.
	RAID10TripleRAIDType RAIDType = "RAID10Triple"
)

// EncryptionTypes is the type of encryption used by the volume.
type EncryptionTypes string

const (
	// NativeDriveEncryptionEncryptionTypes indicates the volume is is utilizing the
	// native drive encryption capabilities of the drive hardware.
	NativeDriveEncryptionEncryptionTypes EncryptionTypes = "NativeDriveEncryption"
	// ControllerAssistedEncryptionTypes indicates the volume is is being encrypted by the
	// storage controller entity.
	ControllerAssistedEncryptionTypes EncryptionTypes = "ControllerAssisted"
	// SoftwareAssistedEncryptionTypes indicates the volume is is being encrypted by
	// software running on the system or the operating system.
	SoftwareAssistedEncryptionTypes EncryptionTypes = "SoftwareAssisted"
)

// VolumeType is the type of volume.
type VolumeType string

const (
	// RawDeviceVolumeType indicates the volume is is a raw physical device without any
	// RAID or other virtualization applied.
	RawDeviceVolumeType VolumeType = "RawDevice"
	// NonRedundantVolumeType indicates the volume is is a non-redundant storage device.
	NonRedundantVolumeType VolumeType = "NonRedundant"
	// MirroredVolumeType indicates the volume is is a mirrored device.
	MirroredVolumeType VolumeType = "Mirrored"
	// StripedWithParityVolumeType indicates the volume is is a device which uses parity
	// to retain redundant information.
	StripedWithParityVolumeType VolumeType = "StripedWithParity"
	// SpannedMirrorsVolumeType indicates the volume is is a spanned set of mirrored
	// devices.
	SpannedMirrorsVolumeType VolumeType = "SpannedMirrors"
	// SpannedStripesWithParityVolumeType indicates the volume is is a spanned set of
	// devices which uses parity to retain redundant information.
	SpannedStripesWithParityVolumeType VolumeType = "SpannedStripesWithParity"
)

// Volume is used to represent a volume, virtual disk, logical disk, LUN,
// or other logical storage for a Redfish implementation.
type Volume struct {
	common.Entity

	// ODataContext is the odata context.
	ODataContext string `json:"@odata.context"`
	// ODataType is the odata type.
	ODataType string `json:"@odata.type"`
	// Description provides a description of this resource.
	Description string
	// Status is
	Status common.Status
	// CapacityBytes shall contain the size in bytes of the associated volume.
	CapacityBytes int
	// VolumeType shall contain the type of the associated Volume.
	VolumeType VolumeType
	// Encrypted shall contain a boolean indicator if the Volume is currently
	// utilizing encryption or not.
	Encrypted bool
	// EncryptionTypes is used by this Volume.
	EncryptionTypes []EncryptionTypes
	// Identifiers shall contain a list of all known durable names for the
	// associated volume.
	Identifiers []common.Identifier
	// BlockSizeBytes shall contain size of the smallest addressable unit of the
	// associated volume.
	BlockSizeBytes int
	// Operations shall contain a list of all currently running on the Volume.
	Operations []common.Operations
	// OptimumIOSizeBytes shall contain the optimum IO size to use when
	// performing IO on this volume. For logical disks, this is the stripe size.
	// For physical disks, this describes the physical sector size.
	OptimumIOSizeBytes int
	// DrivesCount is the number of associated drives.
	DrivesCount int
	// drives contains references to associated drives.
	drives []string
}

// Volumes is used to represent the volumes information related to a Storage
type Volumes struct {
	common.Entity
	// OperationApplyTimeSupport contains, among other things, the types
	// of apply times the client is allowed request when performing a Create,
	// Delete, or Action operation.
	OperationApplyTimeSupport common.OperationApplyTimeSupport `json:"@Redfish.OperationApplyTimeSupport"`
}

// UnmarshalJSON unmarshals a Volume object from the raw JSON.
func (volume *Volume) UnmarshalJSON(b []byte) error {
	type temp Volume
	type links struct {
		DriveCount int `json:"Drives@odata.count"`
		Drives     common.Links
	}
	var t struct {
		temp
		Links links
	}

	err := json.Unmarshal(b, &t)
	if err != nil {
		return err
	}

	*volume = Volume(t.temp)

	// Extract the links to other entities for later
	volume.DrivesCount = t.DrivesCount
	volume.drives = t.Links.Drives.ToStrings()

	return nil
}

// GetVolume will get a Volume instance from the service.
func GetVolume(c common.Client, uri string) (*Volume, error) {
	resp, err := c.Get(uri)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var volume Volume
	err = json.NewDecoder(resp.Body).Decode(&volume)
	if err != nil {
		return nil, err
	}

	volume.SetClient(c)
	return &volume, nil
}

// ListReferencedVolumes gets the collection of Volumes from a provided reference.
func ListReferencedVolumes(c common.Client, link string) ([]*Volume, error) {
	var result []*Volume
	if link == "" {
		return result, nil
	}

	links, err := common.GetCollection(c, link)
	if err != nil {
		return result, err
	}

	for _, volumeLink := range links.ItemLinks {
		volume, err := GetVolume(c, volumeLink)
		if err != nil {
			return result, err
		}
		result = append(result, volume)
	}

	return result, nil
}

// Drives references the Drives that this volume is associated with.
func (volume *Volume) Drives() ([]*Drive, error) {
	var result []*Drive

	for _, driveLink := range volume.drives {
		drive, err := GetDrive(volume.Client, driveLink)
		if err != nil {
			return result, err
		}
		result = append(result, drive)
	}

	return result, nil
}

// AllowedVolumesUpdateApplyTimes returns the set of allowed apply times to request when setting the volumes values
func AllowedVolumesUpdateApplyTimes(c common.Client, link string) ([]common.OperationApplyTime, error) {
	resp, err := c.Get(link)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var volumes Volumes

	err = json.NewDecoder(resp.Body).Decode(&volumes)
	if err != nil {
		return nil, err
	}

	var applyTimes []common.OperationApplyTime
	for _, v := range volumes.OperationApplyTimeSupport.SupportedValues {
		applyTimes = append(applyTimes, v)
	}
	return applyTimes, nil
}
<commit_msg>Removed Volumes struct<commit_after>//
// SPDX-License-Identifier: BSD-3-Clause
//

package redfish

import (
	"encoding/json"

	"github.com/stmcginnis/gofish/common"
)

// RAIDType is
type RAIDType string

const (

	// RAID0RAIDType A placement policy where consecutive logical blocks of
	// data are uniformly distributed across a set of independent storage
	// devices without offering any form of redundancy. This is commonly
	// referred to as data striping. This form of RAID will encounter data
	// loss with the failure of any storage device in the set.
	RAID0RAIDType RAIDType = "RAID0"
	// RAID1RAIDType A placement policy where each logical block of data is
	// stored on more than one independent storage device. This is commonly
	// referred to as mirroring. Data stored using this form of RAID is able
	// to survive a single storage device failure without data loss.
	RAID1RAIDType RAIDType = "RAID1"
	// RAID3RAIDType A placement policy using parity-based protection where
	// logical bytes of data are uniformly distributed across a set of
	// independent storage devices and where the parity is stored on a
	// dedicated independent storage device. Data stored using this form of
	// RAID is able to survive a single storage device failure without data
	// loss. If the storage devices use rotating media, they are assumed to
	// be rotationally synchronized, and the data stripe size should be no
	// larger than the exported block size.
	RAID3RAIDType RAIDType = "RAID3"
	// RAID4RAIDType A placement policy using parity-based protection where
	// logical blocks of data are uniformly distributed across a set of
	// independent storage devices and where the parity is stored on a
	// dedicated independent storage device. Data stored using this form of
	// RAID is able to survive a single storage device failure without data
	// loss.
	RAID4RAIDType RAIDType = "RAID4"
	// RAID5RAIDType A placement policy using parity-based protection for
	// storing stripes of 'n' logical blocks of data and one logical block of
	// parity across a set of 'n+1' independent storage devices where the
	// parity and data blocks are interleaved across the storage devices.
	// Data stored using this form of RAID is able to survive a single
	// storage device failure without data loss.
	RAID5RAIDType RAIDType = "RAID5"
	// RAID6RAIDType A placement policy using parity-based protection for
	// storing stripes of 'n' logical blocks of data and two logical blocks
	// of independent parity across a set of 'n+2' independent storage
	// devices where the parity and data blocks are interleaved across the
	// storage devices. Data stored using this form of RAID is able to
	// survive any two independent storage device failures without data loss.
	RAID6RAIDType RAIDType = "RAID6"
	// RAID10RAIDType A placement policy that creates a striped device (RAID
	// 0) over a set of mirrored devices (RAID 1). This is commonly referred
	// to as RAID 1/0. Data stored using this form of RAID is able to survive
	// storage device failures in each RAID 1 set without data loss.
	RAID10RAIDType RAIDType = "RAID10"
	// RAID01RAIDType A data placement policy that creates a mirrored device
	// (RAID 1) over a set of striped devices (RAID 0). This is commonly
	// referred to as RAID 0+1 or RAID 0/1. Data stored using this form of
	// RAID is able to survive a single RAID 0 data set failure without data
	// loss.
	RAID01RAIDType RAIDType = "RAID01"
	// RAID6TPRAIDType A placement policy that uses parity-based protection
	// for storing stripes of 'n' logical blocks of data and three logical
	// blocks of independent parity across a set of 'n+3' independent storage
	// devices where the parity and data blocks are interleaved across the
	// storage devices. This is commonly referred to as Triple Parity RAID.
	// Data stored using this form of RAID is able to survive any three
	// independent storage device failures without data loss.
	RAID6TPRAIDType RAIDType = "RAID6TP"
	// RAID1ERAIDType A placement policy that uses a form of mirroring
	// implemented over a set of independent storage devices where logical
	// blocks are duplicated on a pair of independent storage devices so that
	// data is uniformly distributed across the storage devices. This is
	// commonly referred to as RAID 1 Enhanced. Data stored using this form
	// of RAID is able to survive a single storage device failure without
	// data loss.
	RAID1ERAIDType RAIDType = "RAID1E"
	// RAID50RAIDType A placement policy that uses a RAID 0 stripe set over
	// two or more RAID 5 sets of independent storage devices. Data stored
	// using this form of RAID is able to survive a single storage device
	// failure within each RAID 5 set without data loss.
	RAID50RAIDType RAIDType = "RAID50"
	// RAID60RAIDType A placement policy that uses a RAID 0 stripe set over
	// two or more RAID 6 sets of independent storage devices. Data stored
	// using this form of RAID is able to survive two device failures within
	// each RAID 6 set without data loss.
	RAID60RAIDType RAIDType = "RAID60"
	// RAID00RAIDType A placement policy that creates a RAID 0 stripe set
	// over two or more RAID 0 sets. This is commonly referred to as RAID
	// 0+0. This form of data layout is not fault tolerant; if any storage
	// device fails there will be data loss.
	RAID00RAIDType RAIDType = "RAID00"
	// RAID10ERAIDType A placement policy that uses a RAID 0 stripe set over
	// two or more RAID 10 sets. This is commonly referred to as Enhanced
	// RAID 10. Data stored using this form of RAID is able to survive a
	// single device failure within each nested RAID 1 set without data loss.
	RAID10ERAIDType RAIDType = "RAID10E"
	// RAID1TripleRAIDType A placement policy where each logical block of
	// data is mirrored three times across a set of three independent storage
	// devices. This is commonly referred to as three-way mirroring. This
	// form of RAID can survive two device failures without data loss.
	RAID1TripleRAIDType RAIDType = "RAID1Triple"
	// RAID10TripleRAIDType A placement policy that uses a striped device
	// (RAID 0) over a set of triple mirrored devices (RAID 1Triple). This
	// form of RAID can survive up to two failures in each triple mirror set
	// without data loss.
	RAID10TripleRAIDType RAIDType = "RAID10Triple"
)

// EncryptionTypes is the type of encryption used by the volume.
type EncryptionTypes string

const (
	// NativeDriveEncryptionEncryptionTypes indicates the volume is is utilizing the
	// native drive encryption capabilities of the drive hardware.
	NativeDriveEncryptionEncryptionTypes EncryptionTypes = "NativeDriveEncryption"
	// ControllerAssistedEncryptionTypes indicates the volume is is being encrypted by the
	// storage controller entity.
	ControllerAssistedEncryptionTypes EncryptionTypes = "ControllerAssisted"
	// SoftwareAssistedEncryptionTypes indicates the volume is is being encrypted by
	// software running on the system or the operating system.
	SoftwareAssistedEncryptionTypes EncryptionTypes = "SoftwareAssisted"
)

// VolumeType is the type of volume.
type VolumeType string

const (
	// RawDeviceVolumeType indicates the volume is is a raw physical device without any
	// RAID or other virtualization applied.
	RawDeviceVolumeType VolumeType = "RawDevice"
	// NonRedundantVolumeType indicates the volume is is a non-redundant storage device.
	NonRedundantVolumeType VolumeType = "NonRedundant"
	// MirroredVolumeType indicates the volume is is a mirrored device.
	MirroredVolumeType VolumeType = "Mirrored"
	// StripedWithParityVolumeType indicates the volume is is a device which uses parity
	// to retain redundant information.
	StripedWithParityVolumeType VolumeType = "StripedWithParity"
	// SpannedMirrorsVolumeType indicates the volume is is a spanned set of mirrored
	// devices.
	SpannedMirrorsVolumeType VolumeType = "SpannedMirrors"
	// SpannedStripesWithParityVolumeType indicates the volume is is a spanned set of
	// devices which uses parity to retain redundant information.
	SpannedStripesWithParityVolumeType VolumeType = "SpannedStripesWithParity"
)

// Volume is used to represent a volume, virtual disk, logical disk, LUN,
// or other logical storage for a Redfish implementation.
type Volume struct {
	common.Entity

	// ODataContext is the odata context.
	ODataContext string `json:"@odata.context"`
	// ODataType is the odata type.
	ODataType string `json:"@odata.type"`
	// Description provides a description of this resource.
	Description string
	// Status is
	Status common.Status
	// CapacityBytes shall contain the size in bytes of the associated volume.
	CapacityBytes int
	// VolumeType shall contain the type of the associated Volume.
	VolumeType VolumeType
	// Encrypted shall contain a boolean indicator if the Volume is currently
	// utilizing encryption or not.
	Encrypted bool
	// EncryptionTypes is used by this Volume.
	EncryptionTypes []EncryptionTypes
	// Identifiers shall contain a list of all known durable names for the
	// associated volume.
	Identifiers []common.Identifier
	// BlockSizeBytes shall contain size of the smallest addressable unit of the
	// associated volume.
	BlockSizeBytes int
	// Operations shall contain a list of all currently running on the Volume.
	Operations []common.Operations
	// OptimumIOSizeBytes shall contain the optimum IO size to use when
	// performing IO on this volume. For logical disks, this is the stripe size.
	// For physical disks, this describes the physical sector size.
	OptimumIOSizeBytes int
	// DrivesCount is the number of associated drives.
	DrivesCount int
	// drives contains references to associated drives.
	drives []string
}

// UnmarshalJSON unmarshals a Volume object from the raw JSON.
func (volume *Volume) UnmarshalJSON(b []byte) error {
	type temp Volume
	type links struct {
		DriveCount int `json:"Drives@odata.count"`
		Drives     common.Links
	}
	var t struct {
		temp
		Links links
	}

	err := json.Unmarshal(b, &t)
	if err != nil {
		return err
	}

	*volume = Volume(t.temp)

	// Extract the links to other entities for later
	volume.DrivesCount = t.DrivesCount
	volume.drives = t.Links.Drives.ToStrings()

	return nil
}

// GetVolume will get a Volume instance from the service.
func GetVolume(c common.Client, uri string) (*Volume, error) {
	resp, err := c.Get(uri)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var volume Volume
	err = json.NewDecoder(resp.Body).Decode(&volume)
	if err != nil {
		return nil, err
	}

	volume.SetClient(c)
	return &volume, nil
}

// ListReferencedVolumes gets the collection of Volumes from a provided reference.
func ListReferencedVolumes(c common.Client, link string) ([]*Volume, error) {
	var result []*Volume
	if link == "" {
		return result, nil
	}

	links, err := common.GetCollection(c, link)
	if err != nil {
		return result, err
	}

	for _, volumeLink := range links.ItemLinks {
		volume, err := GetVolume(c, volumeLink)
		if err != nil {
			return result, err
		}
		result = append(result, volume)
	}

	return result, nil
}

// Drives references the Drives that this volume is associated with.
func (volume *Volume) Drives() ([]*Drive, error) {
	var result []*Drive

	for _, driveLink := range volume.drives {
		drive, err := GetDrive(volume.Client, driveLink)
		if err != nil {
			return result, err
		}
		result = append(result, drive)
	}

	return result, nil
}

// AllowedVolumesUpdateApplyTimes returns the set of allowed apply times to request when setting the volumes values
func AllowedVolumesUpdateApplyTimes(c common.Client, link string) ([]common.OperationApplyTime, error) {
	resp, err := c.Get(link)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var temp struct {
		OperationApplyTimeSupport common.OperationApplyTimeSupport `json:"@Redfish.OperationApplyTimeSupport"`
	}

	err = json.NewDecoder(resp.Body).Decode(&temp)
	if err != nil {
		return nil, err
	}

	var applyTimes []common.OperationApplyTime
	for _, v := range temp.OperationApplyTimeSupport.SupportedValues {
		applyTimes = append(applyTimes, v)
	}
	return applyTimes, nil
}
<|endoftext|>


<commit_before>// Copyright 2013 Chris McGee <sirnewton_01@yahoo.ca>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gdblib

import ()

type ExecRunParms struct {
	ThreadGroup  string
	AllInferiors bool
}

func (gdb *GDB) ExecRun(parms ExecRunParms) error {
	descriptor := cmdDescr{}

	descriptor.cmd = "-exec-run"
	if parms.AllInferiors {
		descriptor.cmd = descriptor.cmd + " --all"
	} else if parms.ThreadGroup != "" {
		descriptor.cmd = descriptor.cmd + " --thread-group " + parms.ThreadGroup
	}

	descriptor.response = make(chan cmdResultRecord)
	gdb.input <- descriptor

	result := <-descriptor.response
	err := parseResult(result, nil)

	return err
}

type ExecInterruptParms struct {
	ThreadGroup  string
	AllInferiors bool
}

func (gdb *GDB) ExecInterrupt(parms ExecInterruptParms) error {
	descriptor := cmdDescr{}

	descriptor.cmd = "-exec-interrupt"
	if parms.AllInferiors {
		descriptor.cmd = descriptor.cmd + " --all"
	} else if parms.ThreadGroup != "" {
		descriptor.cmd = descriptor.cmd + " --thread-group " + parms.ThreadGroup
	}

	descriptor.response = make(chan cmdResultRecord)
	gdb.input <- descriptor
	result := <-descriptor.response
	err := parseResult(result, nil)

	return err
}

type ExecNextParms struct {
	Reverse bool
}

func (gdb *GDB) ExecNext(parms ExecNextParms) error {
	descriptor := cmdDescr{}

	descriptor.cmd = "-exec-next"
	if parms.Reverse {
		descriptor.cmd = descriptor.cmd + " --reverse"
	}

	descriptor.response = make(chan cmdResultRecord)
	gdb.input <- descriptor
	result := <-descriptor.response
	err := parseResult(result, nil)

	return err
}

type ExecStepParms struct {
	Reverse bool
}

func (gdb *GDB) ExecStep(parms ExecStepParms) error {
	descriptor := cmdDescr{}

	descriptor.cmd = "-exec-step"
	if parms.Reverse {
		descriptor.cmd = descriptor.cmd + " --reverse"
	}

	descriptor.response = make(chan cmdResultRecord)
	gdb.input <- descriptor
	result := <-descriptor.response
	err := parseResult(result, nil)

	return err
}

type ExecContinueParms struct {
	Reverse      bool
	ThreadGroup  string
	AllInferiors bool
}

func (gdb *GDB) ExecContinue(parms ExecContinueParms) error {
	descriptor := cmdDescr{}

	descriptor.cmd = "-exec-continue"

	if parms.Reverse {
		descriptor.cmd = descriptor.cmd + " --reverse"
	}
	if parms.AllInferiors {
		descriptor.cmd = descriptor.cmd + " --all"
	} else if parms.ThreadGroup != "" {
		descriptor.cmd = descriptor.cmd + " --thread-group " + parms.ThreadGroup
	}
	descriptor.response = make(chan cmdResultRecord)
	gdb.input <- descriptor
	result := <-descriptor.response
	err := parseResult(result, nil)

	return err
}
<commit_msg>Add exec arguments capability.<commit_after>// Copyright 2013 Chris McGee <sirnewton_01@yahoo.ca>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gdblib

import ()

type ExecRunParms struct {
	ThreadGroup  string
	AllInferiors bool
}

func (gdb *GDB) ExecRun(parms ExecRunParms) error {
	descriptor := cmdDescr{}

	descriptor.cmd = "-exec-run"
	if parms.AllInferiors {
		descriptor.cmd = descriptor.cmd + " --all"
	} else if parms.ThreadGroup != "" {
		descriptor.cmd = descriptor.cmd + " --thread-group " + parms.ThreadGroup
	}

	descriptor.response = make(chan cmdResultRecord)
	gdb.input <- descriptor

	result := <-descriptor.response
	err := parseResult(result, nil)

	return err
}

type ExecArgsParms struct {
	Args  string
}

func (gdb *GDB) ExecArgs(parms ExecArgsParms) error {
	descriptor := cmdDescr{}

	descriptor.cmd = "-exec-arguments"
	descriptor.cmd = descriptor.cmd + " " + parms.Args

	descriptor.response = make(chan cmdResultRecord)
	gdb.input <- descriptor

	result := <-descriptor.response
	err := parseResult(result, nil)

	return err
}

type ExecInterruptParms struct {
	ThreadGroup  string
	AllInferiors bool
}

func (gdb *GDB) ExecInterrupt(parms ExecInterruptParms) error {
	descriptor := cmdDescr{}

	descriptor.cmd = "-exec-interrupt"
	if parms.AllInferiors {
		descriptor.cmd = descriptor.cmd + " --all"
	} else if parms.ThreadGroup != "" {
		descriptor.cmd = descriptor.cmd + " --thread-group " + parms.ThreadGroup
	}

	descriptor.response = make(chan cmdResultRecord)
	gdb.input <- descriptor
	result := <-descriptor.response
	err := parseResult(result, nil)

	return err
}

type ExecNextParms struct {
	Reverse bool
}

func (gdb *GDB) ExecNext(parms ExecNextParms) error {
	descriptor := cmdDescr{}

	descriptor.cmd = "-exec-next"
	if parms.Reverse {
		descriptor.cmd = descriptor.cmd + " --reverse"
	}

	descriptor.response = make(chan cmdResultRecord)
	gdb.input <- descriptor
	result := <-descriptor.response
	err := parseResult(result, nil)

	return err
}

type ExecStepParms struct {
	Reverse bool
}

func (gdb *GDB) ExecStep(parms ExecStepParms) error {
	descriptor := cmdDescr{}

	descriptor.cmd = "-exec-step"
	if parms.Reverse {
		descriptor.cmd = descriptor.cmd + " --reverse"
	}

	descriptor.response = make(chan cmdResultRecord)
	gdb.input <- descriptor
	result := <-descriptor.response
	err := parseResult(result, nil)

	return err
}

type ExecContinueParms struct {
	Reverse      bool
	ThreadGroup  string
	AllInferiors bool
}

func (gdb *GDB) ExecContinue(parms ExecContinueParms) error {
	descriptor := cmdDescr{}

	descriptor.cmd = "-exec-continue"

	if parms.Reverse {
		descriptor.cmd = descriptor.cmd + " --reverse"
	}
	if parms.AllInferiors {
		descriptor.cmd = descriptor.cmd + " --all"
	} else if parms.ThreadGroup != "" {
		descriptor.cmd = descriptor.cmd + " --thread-group " + parms.ThreadGroup
	}
	descriptor.response = make(chan cmdResultRecord)
	gdb.input <- descriptor
	result := <-descriptor.response
	err := parseResult(result, nil)

	return err
}
<|endoftext|>


<commit_before>package gode

import (
	"os/exec"
	"path/filepath"
)

// RunScript runs a given script in node
// Returns an *os/exec.Cmd instance
func (c *Client) RunScript(script string) *exec.Cmd {
	nodePath, err := filepath.Rel(c.RootPath, c.nodePath())
	if err != nil {
		panic(err)
	}
	cmd := exec.Command(nodePath, "-e", script)
	cmd.Dir = c.RootPath
	return cmd
}
<commit_msg>added DebugScript()<commit_after>package gode

import (
	"fmt"
	"io/ioutil"
	"os/exec"
	"path/filepath"
)

// RunScript runs a given script in node
// Returns an *os/exec.Cmd instance
func (c *Client) RunScript(script string) *exec.Cmd {
	nodePath, err := filepath.Rel(c.RootPath, c.nodePath())
	if err != nil {
		panic(err)
	}
	cmd := exec.Command(nodePath, "-e", script)
	cmd.Dir = c.RootPath
	return cmd
}

// DebugScript is the same as RunScript except it launches with node-inspector
func (c *Client) DebugScript(script string) (*exec.Cmd, error) {
	scriptPath := filepath.Join(c.RootPath, "debug.js")
	if err := ioutil.WriteFile(scriptPath, []byte(script), 0644); err != nil {
		panic(err)
	}
	path := filepath.Join(c.RootPath, "node_modules", ".bin", "node-debug")
	if _, err := exec.LookPath(path); err != nil {
		fmt.Print("Installing node-inspector... ")
		c.InstallPackage("node-inspector")
		fmt.Println("done")
	}
	cmd := exec.Command(path, "debug.js")
	cmd.Dir = c.RootPath
	return cmd, nil
}
<|endoftext|>


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

// Docs can currently be found at https://github.com/docker/docker/blob/master/docs/sources/reference/api/docker_remote_api_v1.15.md#exec-create

package docker

import (
	"encoding/json"
	"io"
	"net/http"
	"net/url"
	"strconv"
)

// CreateExecOptions specify parameters to the CreateExecContainer function.
//
// TODO: Add link to docs once Docker 1.3 is out
type CreateExecOptions struct {
	AttachStdin  bool     `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty"`
	AttachStdout bool     `json:"AttachStdout,omitempty" yaml:"AttachStdout,omitempty"`
	AttachStderr bool     `json:"AttachStderr,omitempty" yaml:"AttachStderr,omitempty"`
	Tty          bool     `json:"Tty,omitempty" yaml:"Tty,omitempty"`
	Cmd          []string `json:"Cmd,omitempty" yaml:"Cmd,omitempty"`
	Container    string   `json:"Container,omitempty" yaml:"Container,omitempty"`
}

// StartExecOptions specify parameters to the StartExecContainer function.
//
// TODO: Add link to docs once Docker 1.3 is out
type StartExecOptions struct {
	Detach bool `json:"Detach,omitempty" yaml:"Detach,omitempty"`

	Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty"`

	InputStream  io.Reader `qs:"-"`
	OutputStream io.Writer `qs:"-"`
	ErrorStream  io.Writer `qs:"-"`

	// Use raw terminal? Usually true when the container contains a TTY.
	RawTerminal bool `qs:"-"`

	// If set, after a successful connect, a sentinel will be sent and then the
	// client will block on receive before continuing.
	//
	// It must be an unbuffered channel. Using a buffered channel can lead
	// to unexpected behavior.
	Success chan struct{} `json:"-"`
}

type Exec struct {
	Id string `json:"Id,omitempty" yaml:"Id,omitempty"`
}

// CreateExec sets up an exec instance in a running container `id`, returning the exec
// instance, or an error in case of failure.
//
// TODO: Add link to docs once Docker 1.3 is out
func (c *Client) CreateExec(opts CreateExecOptions) (*Exec, error) {
	path := "/containers/" + opts.Container + "/exec"
	body, status, err := c.do("POST", path, opts)
	if status == http.StatusNotFound {
		return nil, &NoSuchContainer{ID: opts.Container}
	}
	if err != nil {
		return nil, err
	}
	var exec Exec
	err = json.Unmarshal(body, &exec)
	if err != nil {
		return nil, err
	}

	return &exec, nil
}

// Starts a previously set up exec instance id. If opts.Detach is true, it returns
// after starting the exec command. Otherwise, it sets up an interactive session
// with the exec command.
//
// TODO: Add link to docs once Docker 1.3 is out
func (c *Client) StartExec(id string, opts StartExecOptions) error {
	if id == "" {
		return &NoSuchExec{ID: id}
	}

	path := "/exec/" + id + "/start"

	if opts.Detach {
		_, status, err := c.do("POST", path, opts)
		if status == http.StatusNotFound {
			return &NoSuchExec{ID: id}
		}
		if err != nil {
			return err
		}
		return nil
	}

	return c.hijack("POST", path, opts.Success, opts.RawTerminal, opts.InputStream, opts.ErrorStream, opts.OutputStream, opts)
}

// Resizes the tty session used by the exec command id. This API is valid only
// if Tty was specified as part of creating and starting the exec command.
//
// TODO: Add link to docs once Docker 1.3 is out
func (c *Client) ResizeExecTTY(id string, height, width int) error {
	params := make(url.Values)
	params.Set("h", strconv.Itoa(height))
	params.Set("w", strconv.Itoa(width))
	_, _, err := c.do("POST", "/exec/"+id+"/resize?"+params.Encode(), nil)
	return err
}

// NoSuchExec is the error returned when a given exec instance does not exist.
type NoSuchExec struct {
	ID string
}

func (err *NoSuchExec) Error() string {
	return "No such exec instance: " + err.ID
}
<commit_msg>exec: Minor cleanup on path building<commit_after>// Copyright 2014 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Docs can currently be found at https://github.com/docker/docker/blob/master/docs/sources/reference/api/docker_remote_api_v1.15.md#exec-create

package docker

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strconv"
)

// CreateExecOptions specify parameters to the CreateExecContainer function.
//
// TODO: Add link to docs once Docker 1.3 is out
type CreateExecOptions struct {
	AttachStdin  bool     `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty"`
	AttachStdout bool     `json:"AttachStdout,omitempty" yaml:"AttachStdout,omitempty"`
	AttachStderr bool     `json:"AttachStderr,omitempty" yaml:"AttachStderr,omitempty"`
	Tty          bool     `json:"Tty,omitempty" yaml:"Tty,omitempty"`
	Cmd          []string `json:"Cmd,omitempty" yaml:"Cmd,omitempty"`
	Container    string   `json:"Container,omitempty" yaml:"Container,omitempty"`
}

// StartExecOptions specify parameters to the StartExecContainer function.
//
// TODO: Add link to docs once Docker 1.3 is out
type StartExecOptions struct {
	Detach bool `json:"Detach,omitempty" yaml:"Detach,omitempty"`

	Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty"`

	InputStream  io.Reader `qs:"-"`
	OutputStream io.Writer `qs:"-"`
	ErrorStream  io.Writer `qs:"-"`

	// Use raw terminal? Usually true when the container contains a TTY.
	RawTerminal bool `qs:"-"`

	// If set, after a successful connect, a sentinel will be sent and then the
	// client will block on receive before continuing.
	//
	// It must be an unbuffered channel. Using a buffered channel can lead
	// to unexpected behavior.
	Success chan struct{} `json:"-"`
}

type Exec struct {
	Id string `json:"Id,omitempty" yaml:"Id,omitempty"`
}

// CreateExec sets up an exec instance in a running container `id`, returning the exec
// instance, or an error in case of failure.
//
// TODO: Add link to docs once Docker 1.3 is out
func (c *Client) CreateExec(opts CreateExecOptions) (*Exec, error) {
	path := fmt.Sprintf("/containers/%s/exec", opts.Container)
	body, status, err := c.do("POST", path, opts)
	if status == http.StatusNotFound {
		return nil, &NoSuchContainer{ID: opts.Container}
	}
	if err != nil {
		return nil, err
	}
	var exec Exec
	err = json.Unmarshal(body, &exec)
	if err != nil {
		return nil, err
	}

	return &exec, nil
}

// Starts a previously set up exec instance id. If opts.Detach is true, it returns
// after starting the exec command. Otherwise, it sets up an interactive session
// with the exec command.
//
// TODO: Add link to docs once Docker 1.3 is out
func (c *Client) StartExec(id string, opts StartExecOptions) error {
	if id == "" {
		return &NoSuchExec{ID: id}
	}

	path := fmt.Sprintf("/exec/%s/start", id)

	if opts.Detach {
		_, status, err := c.do("POST", path, opts)
		if status == http.StatusNotFound {
			return &NoSuchExec{ID: id}
		}
		if err != nil {
			return err
		}
		return nil
	}

	return c.hijack("POST", path, opts.Success, opts.RawTerminal, opts.InputStream, opts.ErrorStream, opts.OutputStream, opts)
}

// Resizes the tty session used by the exec command id. This API is valid only
// if Tty was specified as part of creating and starting the exec command.
//
// TODO: Add link to docs once Docker 1.3 is out
func (c *Client) ResizeExecTTY(id string, height, width int) error {
	params := make(url.Values)
	params.Set("h", strconv.Itoa(height))
	params.Set("w", strconv.Itoa(width))
	_, _, err := c.do("POST", "/exec/"+id+"/resize?"+params.Encode(), nil)
	return err
}

// NoSuchExec is the error returned when a given exec instance does not exist.
type NoSuchExec struct {
	ID string
}

func (err *NoSuchExec) Error() string {
	return "No such exec instance: " + err.ID
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"io"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/hashicorp/go-multierror"
)

type HookExec struct {
	Owner string
	Repo  string
	Event string
	Data  io.ReadSeeker
}

func (h *HookExec) GetPathExecs() ([]string, error) {
	path := filepath.Join(".", h.Owner, h.Repo, h.Event)

	files := []string{}

	fs, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			return files, nil
		}

		return files, err
	}

	if fs.IsDir() {
		d, err := os.Open(path)
		defer d.Close()
		if err != nil {
			return files, err
		}

		fi, err := d.Readdir(-1)
		if err != nil {
			return files, err
		}

		for _, fi := range fi {
			if isExecFile(fi) {
				// fmt.Println(fi.Name(), fi.Size(), "bytes")
				files = append(files, filepath.Join(path, fi.Name()))
			}
		}

	} else if isExecFile(fs) {
		// fmt.Println(fs.Name(), fs.Size(), "bytes")
		files = append(files, filepath.Join(path, fs.Name()))
	} else {
		return files, errors.New("bad file mumbo jumbo")
	}

	return files, nil
}

func (h *HookExec) Exec() error {
	files, err := h.GetPathExecs()
	if err != nil {
		return err
	}

	var result error

	for _, f := range files {
		cmd := exec.Command(f)

		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		stdin, err := cmd.StdinPipe()
		if err != nil {
			multierror.Append(result, err)
			continue
		}
		defer stdin.Close()

		// io.Copy( cmd.StdinPipe
		err = cmd.Start()
		if err != nil {
			multierror.Append(result, err)
			continue
		}

		h.Data.Seek(0, 0)
		io.Copy(stdin, h.Data)
		stdin.Close()

		err = cmd.Wait()
		if err != nil {
			multierror.Append(result, err)
			continue
		}
	}

	return result
}

// todo: base this on OS
func isExecFile(fi os.FileInfo) bool {
	return fi.Mode().IsRegular() && fi.Mode()|0111 == fi.Mode()
}
<commit_msg>Adds comments where missing<commit_after>package main

import (
	"errors"
	"io"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/hashicorp/go-multierror"
)

// HookExec represents a call to a hook
type HookExec struct {
	Owner string
	Repo  string
	Event string
	Data  io.ReadSeeker
}

// GetPathExecs fetches the executable filenames for the given path
func (h *HookExec) GetPathExecs() ([]string, error) {
	path := filepath.Join(".", h.Owner, h.Repo, h.Event)

	files := []string{}

	fs, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			return files, nil
		}

		return files, err
	}

	if fs.IsDir() {
		d, err := os.Open(path)
		defer d.Close()
		if err != nil {
			return files, err
		}

		fi, err := d.Readdir(-1)
		if err != nil {
			return files, err
		}

		for _, fi := range fi {
			if isExecFile(fi) {
				// fmt.Println(fi.Name(), fi.Size(), "bytes")
				files = append(files, filepath.Join(path, fi.Name()))
			}
		}

	} else if isExecFile(fs) {
		// fmt.Println(fs.Name(), fs.Size(), "bytes")
		files = append(files, filepath.Join(path, fs.Name()))
	} else {
		return files, errors.New("bad file mumbo jumbo")
	}

	return files, nil
}

// Exec triggers the execution of all scripts associated with the given Hook
func (h *HookExec) Exec() error {
	files, err := h.GetPathExecs()
	if err != nil {
		return err
	}

	var result error

	for _, f := range files {
		cmd := exec.Command(f)

		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		stdin, err := cmd.StdinPipe()
		if err != nil {
			multierror.Append(result, err)
			continue
		}
		defer stdin.Close()

		// io.Copy( cmd.StdinPipe
		err = cmd.Start()
		if err != nil {
			multierror.Append(result, err)
			continue
		}

		h.Data.Seek(0, 0)
		io.Copy(stdin, h.Data)
		stdin.Close()

		err = cmd.Wait()
		if err != nil {
			multierror.Append(result, err)
			continue
		}
	}

	return result
}

// todo: base this on OS
func isExecFile(fi os.FileInfo) bool {
	return fi.Mode().IsRegular() && fi.Mode()|0111 == fi.Mode()
}
<|endoftext|>