<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"

	"github.com/prometheus/client_golang/prometheus"
)

type collector struct {
	sources []*source
}

type source struct {
	URL       string
	Namespace string
	Subsystem string
	Labels    map[string]string
	Keys      map[string]struct {
		Skip      bool
		MapValue  map[string]float64
		MakeLabel string
		LabelKey  string
	}
}

func main() {
	sourcesJSON := os.Getenv("SOURCES")
	if sourcesJSON == "" {
		log.Print("environemnt variable SOURCES is requred")
		return
	}

	var sources []*source
	if err := json.Unmarshal([]byte(sourcesJSON), &sources); err != nil {
		log.Print(err)
		return
	}

	prometheus.MustRegister(&collector{sources})
	http.Handle("/", http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
		resp.Write([]byte(`<html><head><title>json2prom</title></head><body><h1>json2prom</h1><p><a href="/metrics">Metrics</a></p></body></html>`))
	}))
	http.Handle("/metrics", prometheus.Handler())

	addr := os.Getenv("HTTP_ADDR")
	if addr == "" {
		addr = ":8080"
	}
	fmt.Println("listening on " + addr)
	log.Print(http.ListenAndServe(addr, nil))
}

func (c *collector) Describe(descs chan<- *prometheus.Desc) {
	metrics := make(chan prometheus.Metric)
	done := make(chan struct{})
	go func() {
		for m := range metrics {
			descs <- m.Desc()
		}
		close(done)
	}()
	c.Collect(metrics)
	close(metrics)
	<-done
}

func (c *collector) Collect(metrics chan<- prometheus.Metric) {
	var wg sync.WaitGroup
	for _, s := range c.sources {
		wg.Add(1)
		go func(s *source) {
			defer wg.Done()

			resp, err := http.Get(s.URL)
			if err != nil {
				log.Print(err)
				return
			}
			defer resp.Body.Close()

			var value interface{}
			if err := json.NewDecoder(resp.Body).Decode(&value); err != nil {
				log.Print(err)
				return
			}

			var labelNames []string
			var labelValues []string
			for k, v := range s.Labels {
				labelNames = append(labelNames, k)
				labelValues = append(labelValues, v)
			}
			s.processValue(nil, labelNames, labelValues, value, metrics)
		}(s)
	}
	wg.Wait()
}

func (s *source) processValue(keys []string, labelNames, labelValues []string, value interface{}, metrics chan<- prometheus.Metric) {
	switch value := value.(type) {
	case map[string]interface{}:
		for k2, v2 := range value {
			d := s.Keys[k2]
			if d.Skip {
				continue
			}
			if d.MapValue != nil {
				s.processValue(append(keys, strings.Trim(k2, "_")), labelNames, labelValues, d.MapValue[v2.(string)], metrics)
				continue
			}
			if d.MakeLabel != "" {
				for k3, v3 := range v2.(map[string]interface{}) {
					labelValue := k3
					if d.LabelKey != "" {
						labelValue = v3.(map[string]interface{})[d.LabelKey].(string)
					}
					s.processValue(append(keys, strings.Trim(k2, "_")), append(labelNames, d.MakeLabel), append(labelValues, labelValue), v3, metrics)
				}
				continue
			}
			s.processValue(append(keys, strings.Trim(k2, "_")), labelNames, labelValues, v2, metrics)
		}
	case float64:
		if value == 0 {
			return
		}
		labels := make(prometheus.Labels)
		for i, name := range labelNames {
			labels[name] = labelValues[i]
		}
		g := prometheus.NewGauge(prometheus.GaugeOpts{
			Namespace:   s.Namespace,
			Subsystem:   s.Subsystem,
			Name:        strings.Join(keys, "_"),
			Help:        strings.Join(keys, "."),
			ConstLabels: labels,
		})
		g.Set(value)
		metrics <- g
	}
}
<commit_msg>simplified code structure<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"

	"github.com/prometheus/client_golang/prometheus"
)

type collector struct {
	sources []*source
}

type source struct {
	URL       string
	Namespace string
	Subsystem string
	Labels    map[string]string
	Keys      map[string]action
}

type action struct {
	Skip      bool
	MapValue  map[string]float64
	MakeLabel string
	LabelKey  string
}

func main() {
	sourcesJSON := os.Getenv("SOURCES")
	if sourcesJSON == "" {
		log.Print("environemnt variable SOURCES is requred")
		return
	}

	var sources []*source
	if err := json.Unmarshal([]byte(sourcesJSON), &sources); err != nil {
		log.Print(err)
		return
	}

	prometheus.MustRegister(&collector{sources})
	http.Handle("/", http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
		resp.Write([]byte(`<html><head><title>json2prom</title></head><body><h1>json2prom</h1><p><a href="/metrics">Metrics</a></p></body></html>`))
	}))
	http.Handle("/metrics", prometheus.Handler())

	addr := os.Getenv("HTTP_ADDR")
	if addr == "" {
		addr = ":8080"
	}
	fmt.Println("listening on " + addr)
	log.Print(http.ListenAndServe(addr, nil))
}

func (c *collector) Describe(descs chan<- *prometheus.Desc) {
	metrics := make(chan prometheus.Metric)
	done := make(chan struct{})
	go func() {
		for m := range metrics {
			descs <- m.Desc()
		}
		close(done)
	}()
	c.Collect(metrics)
	close(metrics)
	<-done
}

func (c *collector) Collect(metrics chan<- prometheus.Metric) {
	var wg sync.WaitGroup
	for _, s := range c.sources {
		wg.Add(1)
		go func(s *source) {
			defer wg.Done()

			resp, err := http.Get(s.URL)
			if err != nil {
				log.Print(err)
				return
			}
			defer resp.Body.Close()

			var value interface{}
			if err := json.NewDecoder(resp.Body).Decode(&value); err != nil {
				log.Print(err)
				return
			}

			var labelNames []string
			var labelValues []string
			for k, v := range s.Labels {
				labelNames = append(labelNames, k)
				labelValues = append(labelValues, v)
			}
			s.processValue(nil, labelNames, labelValues, value, s.Keys["^"], metrics)
		}(s)
	}
	wg.Wait()
}

func (s *source) processValue(keys []string, labelNames, labelValues []string, value interface{}, act action, metrics chan<- prometheus.Metric) {
	if act.Skip {
		return
	}
	if act.MapValue != nil {
		value = act.MapValue[value.(string)]
	}
	if act.MakeLabel != "" {
		for k, v := range value.(map[string]interface{}) {
			labelValue := k
			if act.LabelKey != "" {
				labelValue = v.(map[string]interface{})[act.LabelKey].(string)
			}
			s.processValue(keys, append(labelNames, act.MakeLabel), append(labelValues, labelValue), v, action{}, metrics)
		}
		return
	}

	switch value := value.(type) {
	case map[string]interface{}:
		for k, v := range value {
			s.processValue(append(keys, strings.Trim(k, "_")), labelNames, labelValues, v, s.Keys[k], metrics)
		}
	case float64:
		if value == 0 {
			return
		}
		labels := make(prometheus.Labels)
		for i, name := range labelNames {
			labels[name] = labelValues[i]
		}
		g := prometheus.NewGauge(prometheus.GaugeOpts{
			Namespace:   s.Namespace,
			Subsystem:   s.Subsystem,
			Name:        strings.Join(keys, "_"),
			Help:        strings.Join(keys, "."),
			ConstLabels: labels,
		})
		g.Set(value)
		metrics <- g
	}
}
<|endoftext|>


<commit_before>package app

import (
	"fmt"
	"strconv"

	"github.com/moncho/dry/appui"
	"github.com/moncho/dry/ui"
	termbox "github.com/nsf/termbox-go"
)

type servicesScreenEventHandler struct {
	baseEventHandler
	passingEvents bool
}

func (h *servicesScreenEventHandler) handle(event termbox.Event) {
	if h.passingEvents {
		h.eventChan <- event
		return
	}
	handled := false
	focus := true
	dry := h.dry
	switch event.Key {
	case termbox.KeyCtrlR:

		rw := appui.NewAskForConfirmation("About to remove the selected service. Do you want to proceed? y/N")
		h.passingEvents = true
		handled = true
		dry.widgetRegistry.add(rw)
		go func() {
			events := ui.EventSource{
				Events: h.eventChan,
				EventHandledCallback: func(e termbox.Event) error {
					return refreshScreen()
				},
			}
			rw.OnFocus(events)
			dry.widgetRegistry.remove(rw)
			confirmation, canceled := rw.Text()
			h.passingEvents = false
			if canceled || (confirmation != "y" && confirmation != "Y") {
				return
			}
			removeService := func(serviceID string) error {
				err := dry.dockerDaemon.ServiceRemove(serviceID)
				refreshScreen()
				return err
			}
			h.dry.state.activeWidget.OnEvent(removeService)
		}()

	case termbox.KeyCtrlS:

		rw := appui.NewAskForConfirmation("Scale service. Number of replicas?")
		h.passingEvents = true
		handled = true
		dry.widgetRegistry.add(rw)
		go func() {
			events := ui.EventSource{
				Events: h.eventChan,
				EventHandledCallback: func(e termbox.Event) error {
					return refreshScreen()
				},
			}
			rw.OnFocus(events)
			dry.widgetRegistry.remove(rw)
			replicas, canceled := rw.Text()
			h.passingEvents = false
			if canceled {
				return
			}
			scaleTo, err := strconv.Atoi(replicas)
			if err != nil || scaleTo < 0 {
				dry.appmessage(
					fmt.Sprintf("Cannot scale service, invalid number of replicas: %s", replicas))
				return
			}

			scaleService := func(serviceID string) error {
				var err error
				err = dry.dockerDaemon.ServiceScale(serviceID, uint64(scaleTo))

				if err == nil {
					dry.appmessage(fmt.Sprintf("Service %s scaled to %d replicas", serviceID, scaleTo))
				}
				refreshScreen()
				return err
			}
			h.dry.state.activeWidget.OnEvent(scaleService)
		}()

	case termbox.KeyEnter:
		showServices := func(serviceID string) error {
			h.dry.ShowServiceTasks(serviceID)
			return refreshScreen()
		}
		h.dry.state.activeWidget.OnEvent(showServices)
		handled = true
	}
	switch event.Ch {
	case 'l':
		showServiceLogs := func(serviceID string) error {
			logs, err := h.dry.ServiceLogs(serviceID)
			if err == nil {
				go appui.Stream(h.screen, logs, h.eventChan, h.closeViewChan)
				return nil
			}
			return err
		}
		//TODO show error on screen
		if err := h.dry.state.activeWidget.OnEvent(showServiceLogs); err == nil {
			handled = true
			focus = false
		}
	}
	if !handled {
		h.baseEventHandler.handle(event)
	} else {
		h.setFocus(focus)
		if h.hasFocus() {
			refreshScreen()
		}
	}
}

type serviceTaskScreenEventHandler struct {
	baseEventHandler
}

func (h *serviceTaskScreenEventHandler) handle(event termbox.Event) {

	switch event.Key {
	case termbox.KeyEsc:
		h.dry.ShowServices()
	}

	h.baseEventHandler.handle(event)

}
<commit_msg>Check for nil actionables<commit_after>package app

import (
	"fmt"
	"strconv"

	"github.com/moncho/dry/appui"
	"github.com/moncho/dry/ui"
	termbox "github.com/nsf/termbox-go"
)

type servicesScreenEventHandler struct {
	baseEventHandler
	passingEvents bool
}

func (h *servicesScreenEventHandler) handle(event termbox.Event) {
	if h.passingEvents {
		h.eventChan <- event
		return
	}
	handled := false
	focus := true
	dry := h.dry
	switch event.Key {
	case termbox.KeyCtrlR:

		rw := appui.NewAskForConfirmation("About to remove the selected service. Do you want to proceed? y/N")
		h.passingEvents = true
		handled = true
		dry.widgetRegistry.add(rw)
		go func() {
			events := ui.EventSource{
				Events: h.eventChan,
				EventHandledCallback: func(e termbox.Event) error {
					return refreshScreen()
				},
			}
			rw.OnFocus(events)
			dry.widgetRegistry.remove(rw)
			confirmation, canceled := rw.Text()
			h.passingEvents = false
			if canceled || (confirmation != "y" && confirmation != "Y") {
				return
			}
			removeService := func(serviceID string) error {
				err := dry.dockerDaemon.ServiceRemove(serviceID)
				refreshScreen()
				return err
			}
			h.dry.state.activeWidget.OnEvent(removeService)
		}()

	case termbox.KeyCtrlS:

		rw := appui.NewAskForConfirmation("Scale service. Number of replicas?")
		h.passingEvents = true
		handled = true
		dry.widgetRegistry.add(rw)
		go func() {
			events := ui.EventSource{
				Events: h.eventChan,
				EventHandledCallback: func(e termbox.Event) error {
					return refreshScreen()
				},
			}
			rw.OnFocus(events)
			dry.widgetRegistry.remove(rw)
			replicas, canceled := rw.Text()
			h.passingEvents = false
			if canceled {
				return
			}
			scaleTo, err := strconv.Atoi(replicas)
			if err != nil || scaleTo < 0 {
				dry.appmessage(
					fmt.Sprintf("Cannot scale service, invalid number of replicas: %s", replicas))
				return
			}

			scaleService := func(serviceID string) error {
				var err error
				err = dry.dockerDaemon.ServiceScale(serviceID, uint64(scaleTo))

				if err == nil {
					dry.appmessage(fmt.Sprintf("Service %s scaled to %d replicas", serviceID, scaleTo))
				}
				refreshScreen()
				return err
			}
			h.dry.state.activeWidget.OnEvent(scaleService)
		}()

	case termbox.KeyEnter:
		showServices := func(serviceID string) error {
			h.dry.ShowServiceTasks(serviceID)
			return refreshScreen()
		}
		h.dry.state.activeWidget.OnEvent(showServices)
		handled = true
	}
	switch event.Ch {
	case 'l':
		showServiceLogs := func(serviceID string) error {
			logs, err := h.dry.ServiceLogs(serviceID)
			if err == nil {
				go appui.Stream(h.screen, logs, h.eventChan, h.closeViewChan)
				return nil
			}
			return err
		}
		//TODO show error on screen
		if h.dry.state.activeWidget != nil {
			if err := h.dry.state.activeWidget.OnEvent(showServiceLogs); err == nil {
				handled = true
				focus = false
			}
		}
	}
	if !handled {
		h.baseEventHandler.handle(event)
	} else {
		h.setFocus(focus)
		if h.hasFocus() {
			refreshScreen()
		}
	}
}

type serviceTaskScreenEventHandler struct {
	baseEventHandler
}

func (h *serviceTaskScreenEventHandler) handle(event termbox.Event) {

	switch event.Key {
	case termbox.KeyEsc:
		h.dry.ShowServices()
	}

	h.baseEventHandler.handle(event)

}
<|endoftext|>


<commit_before>package fasthttp

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"math"
	"net"
	"reflect"
	"sync"
	"time"
	"unsafe"
)

// AppendHTMLEscape appends html-escaped s to dst and returns the extended dst.
func AppendHTMLEscape(dst []byte, s string) []byte {
	var prev int
	var sub string
	for i, n := 0, len(s); i < n; i++ {
		sub = ""
		switch s[i] {
		case '<':
			sub = "<"
		case '>':
			sub = ">"
		case '"':
			sub = """
		case '\'':
			sub = "'"
		}
		if len(sub) > 0 {
			dst = append(dst, s[prev:i]...)
			dst = append(dst, sub...)
			prev = i + 1
		}
	}
	return append(dst, s[prev:]...)
}

// AppendHTMLEscapeBytes appends html-escaped s to dst and returns
// the extended dst.
func AppendHTMLEscapeBytes(dst, s []byte) []byte {
	return AppendHTMLEscape(dst, b2s(s))
}

// AppendIPv4 appends string representation of the given ip v4 to dst
// and returns the extended dst.
func AppendIPv4(dst []byte, ip net.IP) []byte {
	ip = ip.To4()
	if ip == nil {
		return append(dst, "non-v4 ip passed to AppendIPv4"...)
	}

	dst = AppendUint(dst, int(ip[0]))
	for i := 1; i < 4; i++ {
		dst = append(dst, '.')
		dst = AppendUint(dst, int(ip[i]))
	}
	return dst
}

var errEmptyIPStr = errors.New("empty ip address string")

// ParseIPv4 parses ip address from ipStr into dst and returns the extended dst.
func ParseIPv4(dst net.IP, ipStr []byte) (net.IP, error) {
	if len(ipStr) == 0 {
		return dst, errEmptyIPStr
	}
	if len(dst) < net.IPv4len {
		dst = make([]byte, net.IPv4len)
	}
	copy(dst, net.IPv4zero)
	dst = dst.To4()
	if dst == nil {
		panic("BUG: dst must not be nil")
	}

	b := ipStr
	for i := 0; i < 3; i++ {
		n := bytes.IndexByte(b, '.')
		if n < 0 {
			return dst, fmt.Errorf("cannot find dot in ipStr %q", ipStr)
		}
		v, err := ParseUint(b[:n])
		if err != nil {
			return dst, fmt.Errorf("cannot parse ipStr %q: %s", ipStr, err)
		}
		if v > 255 {
			return dst, fmt.Errorf("cannot parse ipStr %q: ip part cannot exceed 255: parsed %d", ipStr, v)
		}
		dst[i] = byte(v)
		b = b[n+1:]
	}
	v, err := ParseUint(b)
	if err != nil {
		return dst, fmt.Errorf("cannot parse ipStr %q: %s", ipStr, err)
	}
	if v > 255 {
		return dst, fmt.Errorf("cannot parse ipStr %q: ip part cannot exceed 255: parsed %d", ipStr, v)
	}
	dst[3] = byte(v)

	return dst, nil
}

// AppendHTTPDate appends HTTP-compliant (RFC1123) representation of date
// to dst and returns the extended dst.
func AppendHTTPDate(dst []byte, date time.Time) []byte {
	dst = date.In(time.UTC).AppendFormat(dst, time.RFC1123)
	copy(dst[len(dst)-3:], strGMT)
	return dst
}

// ParseHTTPDate parses HTTP-compliant (RFC1123) date.
func ParseHTTPDate(date []byte) (time.Time, error) {
	return time.Parse(time.RFC1123, b2s(date))
}

// AppendUint appends n to dst and returns the extended dst.
func AppendUint(dst []byte, n int) []byte {
	if n < 0 {
		panic("BUG: int must be positive")
	}

	var b [20]byte
	buf := b[:]
	i := len(buf)
	var q int
	for n >= 10 {
		i--
		q = n / 10
		buf[i] = '0' + byte(n-q*10)
		n = q
	}
	i--
	buf[i] = '0' + byte(n)

	dst = append(dst, buf[i:]...)
	return dst
}

// ParseUint parses uint from buf.
func ParseUint(buf []byte) (int, error) {
	v, n, err := parseUintBuf(buf)
	if n != len(buf) {
		return -1, errUnexpectedTrailingChar
	}
	return v, err
}

var (
	errEmptyInt               = errors.New("empty integer")
	errUnexpectedFirstChar    = errors.New("unexpected first char found. Expecting 0-9")
	errUnexpectedTrailingChar = errors.New("unexpected traling char found. Expecting 0-9")
	errTooLongInt             = errors.New("too long int")
)

func parseUintBuf(b []byte) (int, int, error) {
	n := len(b)
	if n == 0 {
		return -1, 0, errEmptyInt
	}
	v := 0
	for i := 0; i < n; i++ {
		c := b[i]
		k := c - '0'
		if k > 9 {
			if i == 0 {
				return -1, i, errUnexpectedFirstChar
			}
			return v, i, nil
		}
		if i >= maxIntChars {
			return -1, i, errTooLongInt
		}
		v = 10*v + int(k)
	}
	return v, n, nil
}

var (
	errEmptyFloat           = errors.New("empty float number")
	errDuplicateFloatPoint  = errors.New("duplicate point found in float number")
	errUnexpectedFloatEnd   = errors.New("unexpected end of float number")
	errInvalidFloatExponent = errors.New("invalid float number exponent")
	errUnexpectedFloatChar  = errors.New("unexpected char found in float number")
)

// ParseUfloat parses unsigned float from buf.
func ParseUfloat(buf []byte) (float64, error) {
	if len(buf) == 0 {
		return -1, errEmptyFloat
	}
	b := buf
	var v uint64
	var offset = 1.0
	var pointFound bool
	for i, c := range b {
		if c < '0' || c > '9' {
			if c == '.' {
				if pointFound {
					return -1, errDuplicateFloatPoint
				}
				pointFound = true
				continue
			}
			if c == 'e' || c == 'E' {
				if i+1 >= len(b) {
					return -1, errUnexpectedFloatEnd
				}
				b = b[i+1:]
				minus := -1
				switch b[0] {
				case '+':
					b = b[1:]
					minus = 1
				case '-':
					b = b[1:]
				default:
					minus = 1
				}
				vv, err := ParseUint(b)
				if err != nil {
					return -1, errInvalidFloatExponent
				}
				return float64(v) * offset * math.Pow10(minus*int(vv)), nil
			}
			return -1, errUnexpectedFloatChar
		}
		v = 10*v + uint64(c-'0')
		if pointFound {
			offset /= 10
		}
	}
	return float64(v) * offset, nil
}

var (
	errEmptyHexNum    = errors.New("empty hex number")
	errTooLargeHexNum = errors.New("too large hex number")
)

func readHexInt(r *bufio.Reader) (int, error) {
	n := 0
	i := 0
	var k int
	for {
		c, err := r.ReadByte()
		if err != nil {
			if err == io.EOF && i > 0 {
				return n, nil
			}
			return -1, err
		}
		k = hexbyte2int(c)
		if k < 0 {
			if i == 0 {
				return -1, errEmptyHexNum
			}
			r.UnreadByte()
			return n, nil
		}
		if i >= maxHexIntChars {
			return -1, errTooLargeHexNum
		}
		n = (n << 4) | k
		i++
	}
}

var hexIntBufPool sync.Pool

func writeHexInt(w *bufio.Writer, n int) error {
	if n < 0 {
		panic("BUG: int must be positive")
	}

	v := hexIntBufPool.Get()
	if v == nil {
		v = make([]byte, maxHexIntChars+1)
	}
	buf := v.([]byte)
	i := len(buf) - 1
	for {
		buf[i] = int2hexbyte(n & 0xf)
		n >>= 4
		if n == 0 {
			break
		}
		i--
	}
	_, err := w.Write(buf[i:])
	hexIntBufPool.Put(v)
	return err
}

func int2hexbyte(n int) byte {
	if n < 10 {
		return '0' + byte(n)
	}
	return 'a' + byte(n) - 10
}

func hexCharUpper(c byte) byte {
	if c < 10 {
		return '0' + c
	}
	return c - 10 + 'A'
}

var hex2intTable = func() []byte {
	b := make([]byte, 255)
	for i := byte(0); i < 255; i++ {
		c := byte(0)
		if i >= '0' && i <= '9' {
			c = 1 + i - '0'
		} else if i >= 'a' && i <= 'f' {
			c = 1 + i - 'a' + 10
		} else if i >= 'A' && i <= 'F' {
			c = 1 + i - 'A' + 10
		}
		b[i] = c
	}
	return b
}()

func hexbyte2int(c byte) int {
	return int(hex2intTable[c]) - 1
}

const toLower = 'a' - 'A'

func uppercaseByte(p *byte) {
	c := *p
	if c >= 'a' && c <= 'z' {
		*p = c - toLower
	}
}

func lowercaseByte(p *byte) {
	c := *p
	if c >= 'A' && c <= 'Z' {
		*p = c + toLower
	}
}

func lowercaseBytes(b []byte) {
	for i, n := 0, len(b); i < n; i++ {
		lowercaseByte(&b[i])
	}
}

// b2s converts byte slice to a string without memory allocation.
// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
//
// Note it may break if string and/or slice header will change
// in the future go versions.
func b2s(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

// s2b converts string to a byte slice without memory allocation.
//
// Note it may break if string and/or slice header will change
// in the future go versions.
func s2b(s string) []byte {
	sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
	bh := reflect.SliceHeader{
		Data: sh.Data,
		Len:  sh.Len,
		Cap:  sh.Len,
	}
	return *(*[]byte)(unsafe.Pointer(&bh))
}

// AppendUnquotedArg appends url-decoded src to dst and returns appended dst.
//
// dst may point to src. In this case src will be overwritten.
func AppendUnquotedArg(dst, src []byte) []byte {
	return decodeArgAppend(dst, src, true)
}

// AppendQuotedArg appends url-encoded src to dst and returns appended dst.
func AppendQuotedArg(dst, src []byte) []byte {
	for _, c := range src {
		// See http://www.w3.org/TR/html5/forms.html#form-submission-algorithm
		if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' ||
			c == '*' || c == '-' || c == '.' || c == '_' {
			dst = append(dst, c)
		} else {
			dst = append(dst, '%', hexCharUpper(c>>4), hexCharUpper(c&15))
		}
	}
	return dst
}

func appendQuotedPath(dst, src []byte) []byte {
	for _, c := range src {
		if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' ||
			c == '/' || c == '.' || c == ',' || c == '=' || c == ':' || c == '&' || c == '~' || c == '-' || c == '_' {
			dst = append(dst, c)
		} else {
			dst = append(dst, '%', hexCharUpper(c>>4), hexCharUpper(c&15))
		}
	}
	return dst
}

// EqualBytesStr returns true if string(b) == s.
//
// This function has no performance benefits comparing to string(b) == s.
// It is left here for backwards compatibility only.
//
// This function is deperecated and may be deleted soon.
func EqualBytesStr(b []byte, s string) bool {
	return string(b) == s
}

// AppendBytesStr appends src to dst and returns the extended dst.
//
// This function has no performance benefits comparing to append(dst, src...).
// It is left here for backwards compatibility only.
//
// This function is deprecated and may be deleted soon.
func AppendBytesStr(dst []byte, src string) []byte {
	return append(dst, src...)
}
<commit_msg>added a fast path to AppendHTMLEscape when the string doesnt contain special chars<commit_after>package fasthttp

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"math"
	"net"
	"reflect"
	"strings"
	"sync"
	"time"
	"unsafe"
)

// AppendHTMLEscape appends html-escaped s to dst and returns the extended dst.
func AppendHTMLEscape(dst []byte, s string) []byte {
	if strings.IndexByte(s, '<') < 0 &&
		strings.IndexByte(s, '>') < 0 &&
		strings.IndexByte(s, '"') < 0 &&
		strings.IndexByte(s, '\'') < 0 {

		// fast path - nothing to escape
		return append(dst, s...)
	}

	// slow path
	var prev int
	var sub string
	for i, n := 0, len(s); i < n; i++ {
		sub = ""
		switch s[i] {
		case '<':
			sub = "<"
		case '>':
			sub = ">"
		case '"':
			sub = """
		case '\'':
			sub = "'"
		}
		if len(sub) > 0 {
			dst = append(dst, s[prev:i]...)
			dst = append(dst, sub...)
			prev = i + 1
		}
	}
	return append(dst, s[prev:]...)
}

// AppendHTMLEscapeBytes appends html-escaped s to dst and returns
// the extended dst.
func AppendHTMLEscapeBytes(dst, s []byte) []byte {
	return AppendHTMLEscape(dst, b2s(s))
}

// AppendIPv4 appends string representation of the given ip v4 to dst
// and returns the extended dst.
func AppendIPv4(dst []byte, ip net.IP) []byte {
	ip = ip.To4()
	if ip == nil {
		return append(dst, "non-v4 ip passed to AppendIPv4"...)
	}

	dst = AppendUint(dst, int(ip[0]))
	for i := 1; i < 4; i++ {
		dst = append(dst, '.')
		dst = AppendUint(dst, int(ip[i]))
	}
	return dst
}

var errEmptyIPStr = errors.New("empty ip address string")

// ParseIPv4 parses ip address from ipStr into dst and returns the extended dst.
func ParseIPv4(dst net.IP, ipStr []byte) (net.IP, error) {
	if len(ipStr) == 0 {
		return dst, errEmptyIPStr
	}
	if len(dst) < net.IPv4len {
		dst = make([]byte, net.IPv4len)
	}
	copy(dst, net.IPv4zero)
	dst = dst.To4()
	if dst == nil {
		panic("BUG: dst must not be nil")
	}

	b := ipStr
	for i := 0; i < 3; i++ {
		n := bytes.IndexByte(b, '.')
		if n < 0 {
			return dst, fmt.Errorf("cannot find dot in ipStr %q", ipStr)
		}
		v, err := ParseUint(b[:n])
		if err != nil {
			return dst, fmt.Errorf("cannot parse ipStr %q: %s", ipStr, err)
		}
		if v > 255 {
			return dst, fmt.Errorf("cannot parse ipStr %q: ip part cannot exceed 255: parsed %d", ipStr, v)
		}
		dst[i] = byte(v)
		b = b[n+1:]
	}
	v, err := ParseUint(b)
	if err != nil {
		return dst, fmt.Errorf("cannot parse ipStr %q: %s", ipStr, err)
	}
	if v > 255 {
		return dst, fmt.Errorf("cannot parse ipStr %q: ip part cannot exceed 255: parsed %d", ipStr, v)
	}
	dst[3] = byte(v)

	return dst, nil
}

// AppendHTTPDate appends HTTP-compliant (RFC1123) representation of date
// to dst and returns the extended dst.
func AppendHTTPDate(dst []byte, date time.Time) []byte {
	dst = date.In(time.UTC).AppendFormat(dst, time.RFC1123)
	copy(dst[len(dst)-3:], strGMT)
	return dst
}

// ParseHTTPDate parses HTTP-compliant (RFC1123) date.
func ParseHTTPDate(date []byte) (time.Time, error) {
	return time.Parse(time.RFC1123, b2s(date))
}

// AppendUint appends n to dst and returns the extended dst.
func AppendUint(dst []byte, n int) []byte {
	if n < 0 {
		panic("BUG: int must be positive")
	}

	var b [20]byte
	buf := b[:]
	i := len(buf)
	var q int
	for n >= 10 {
		i--
		q = n / 10
		buf[i] = '0' + byte(n-q*10)
		n = q
	}
	i--
	buf[i] = '0' + byte(n)

	dst = append(dst, buf[i:]...)
	return dst
}

// ParseUint parses uint from buf.
func ParseUint(buf []byte) (int, error) {
	v, n, err := parseUintBuf(buf)
	if n != len(buf) {
		return -1, errUnexpectedTrailingChar
	}
	return v, err
}

var (
	errEmptyInt               = errors.New("empty integer")
	errUnexpectedFirstChar    = errors.New("unexpected first char found. Expecting 0-9")
	errUnexpectedTrailingChar = errors.New("unexpected traling char found. Expecting 0-9")
	errTooLongInt             = errors.New("too long int")
)

func parseUintBuf(b []byte) (int, int, error) {
	n := len(b)
	if n == 0 {
		return -1, 0, errEmptyInt
	}
	v := 0
	for i := 0; i < n; i++ {
		c := b[i]
		k := c - '0'
		if k > 9 {
			if i == 0 {
				return -1, i, errUnexpectedFirstChar
			}
			return v, i, nil
		}
		if i >= maxIntChars {
			return -1, i, errTooLongInt
		}
		v = 10*v + int(k)
	}
	return v, n, nil
}

var (
	errEmptyFloat           = errors.New("empty float number")
	errDuplicateFloatPoint  = errors.New("duplicate point found in float number")
	errUnexpectedFloatEnd   = errors.New("unexpected end of float number")
	errInvalidFloatExponent = errors.New("invalid float number exponent")
	errUnexpectedFloatChar  = errors.New("unexpected char found in float number")
)

// ParseUfloat parses unsigned float from buf.
func ParseUfloat(buf []byte) (float64, error) {
	if len(buf) == 0 {
		return -1, errEmptyFloat
	}
	b := buf
	var v uint64
	var offset = 1.0
	var pointFound bool
	for i, c := range b {
		if c < '0' || c > '9' {
			if c == '.' {
				if pointFound {
					return -1, errDuplicateFloatPoint
				}
				pointFound = true
				continue
			}
			if c == 'e' || c == 'E' {
				if i+1 >= len(b) {
					return -1, errUnexpectedFloatEnd
				}
				b = b[i+1:]
				minus := -1
				switch b[0] {
				case '+':
					b = b[1:]
					minus = 1
				case '-':
					b = b[1:]
				default:
					minus = 1
				}
				vv, err := ParseUint(b)
				if err != nil {
					return -1, errInvalidFloatExponent
				}
				return float64(v) * offset * math.Pow10(minus*int(vv)), nil
			}
			return -1, errUnexpectedFloatChar
		}
		v = 10*v + uint64(c-'0')
		if pointFound {
			offset /= 10
		}
	}
	return float64(v) * offset, nil
}

var (
	errEmptyHexNum    = errors.New("empty hex number")
	errTooLargeHexNum = errors.New("too large hex number")
)

func readHexInt(r *bufio.Reader) (int, error) {
	n := 0
	i := 0
	var k int
	for {
		c, err := r.ReadByte()
		if err != nil {
			if err == io.EOF && i > 0 {
				return n, nil
			}
			return -1, err
		}
		k = hexbyte2int(c)
		if k < 0 {
			if i == 0 {
				return -1, errEmptyHexNum
			}
			r.UnreadByte()
			return n, nil
		}
		if i >= maxHexIntChars {
			return -1, errTooLargeHexNum
		}
		n = (n << 4) | k
		i++
	}
}

var hexIntBufPool sync.Pool

func writeHexInt(w *bufio.Writer, n int) error {
	if n < 0 {
		panic("BUG: int must be positive")
	}

	v := hexIntBufPool.Get()
	if v == nil {
		v = make([]byte, maxHexIntChars+1)
	}
	buf := v.([]byte)
	i := len(buf) - 1
	for {
		buf[i] = int2hexbyte(n & 0xf)
		n >>= 4
		if n == 0 {
			break
		}
		i--
	}
	_, err := w.Write(buf[i:])
	hexIntBufPool.Put(v)
	return err
}

func int2hexbyte(n int) byte {
	if n < 10 {
		return '0' + byte(n)
	}
	return 'a' + byte(n) - 10
}

func hexCharUpper(c byte) byte {
	if c < 10 {
		return '0' + c
	}
	return c - 10 + 'A'
}

var hex2intTable = func() []byte {
	b := make([]byte, 255)
	for i := byte(0); i < 255; i++ {
		c := byte(0)
		if i >= '0' && i <= '9' {
			c = 1 + i - '0'
		} else if i >= 'a' && i <= 'f' {
			c = 1 + i - 'a' + 10
		} else if i >= 'A' && i <= 'F' {
			c = 1 + i - 'A' + 10
		}
		b[i] = c
	}
	return b
}()

func hexbyte2int(c byte) int {
	return int(hex2intTable[c]) - 1
}

const toLower = 'a' - 'A'

func uppercaseByte(p *byte) {
	c := *p
	if c >= 'a' && c <= 'z' {
		*p = c - toLower
	}
}

func lowercaseByte(p *byte) {
	c := *p
	if c >= 'A' && c <= 'Z' {
		*p = c + toLower
	}
}

func lowercaseBytes(b []byte) {
	for i, n := 0, len(b); i < n; i++ {
		lowercaseByte(&b[i])
	}
}

// b2s converts byte slice to a string without memory allocation.
// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
//
// Note it may break if string and/or slice header will change
// in the future go versions.
func b2s(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

// s2b converts string to a byte slice without memory allocation.
//
// Note it may break if string and/or slice header will change
// in the future go versions.
func s2b(s string) []byte {
	sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
	bh := reflect.SliceHeader{
		Data: sh.Data,
		Len:  sh.Len,
		Cap:  sh.Len,
	}
	return *(*[]byte)(unsafe.Pointer(&bh))
}

// AppendUnquotedArg appends url-decoded src to dst and returns appended dst.
//
// dst may point to src. In this case src will be overwritten.
func AppendUnquotedArg(dst, src []byte) []byte {
	return decodeArgAppend(dst, src, true)
}

// AppendQuotedArg appends url-encoded src to dst and returns appended dst.
func AppendQuotedArg(dst, src []byte) []byte {
	for _, c := range src {
		// See http://www.w3.org/TR/html5/forms.html#form-submission-algorithm
		if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' ||
			c == '*' || c == '-' || c == '.' || c == '_' {
			dst = append(dst, c)
		} else {
			dst = append(dst, '%', hexCharUpper(c>>4), hexCharUpper(c&15))
		}
	}
	return dst
}

func appendQuotedPath(dst, src []byte) []byte {
	for _, c := range src {
		if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' ||
			c == '/' || c == '.' || c == ',' || c == '=' || c == ':' || c == '&' || c == '~' || c == '-' || c == '_' {
			dst = append(dst, c)
		} else {
			dst = append(dst, '%', hexCharUpper(c>>4), hexCharUpper(c&15))
		}
	}
	return dst
}

// EqualBytesStr returns true if string(b) == s.
//
// This function has no performance benefits comparing to string(b) == s.
// It is left here for backwards compatibility only.
//
// This function is deperecated and may be deleted soon.
func EqualBytesStr(b []byte, s string) bool {
	return string(b) == s
}

// AppendBytesStr appends src to dst and returns the extended dst.
//
// This function has no performance benefits comparing to append(dst, src...).
// It is left here for backwards compatibility only.
//
// This function is deprecated and may be deleted soon.
func AppendBytesStr(dst []byte, src string) []byte {
	return append(dst, src...)
}
<|endoftext|>


<commit_before>package main

import (
	"./cachectl"
	"flag"
	"log"
	"os"
	"os/signal"
	"regexp"
	"syscall"
	"time"
)

func scheduledPurgePages(target cachectl.SectionTarget) {

	re := regexp.MustCompile(target.Filter)
	verbose := false

	for {
		timer := time.NewTimer(time.Second * time.Duration(target.PurgeInterval))
		<-timer.C

		fi, err := os.Stat(target.Path)
		if err != nil {
			log.Println(err.Error())
			continue
		}

		if fi.IsDir() {
			err := cachectl.WalkPurgePages(target.Path, re, target.Rate, verbose)
			if err != nil {
				log.Printf("failed to walk in %s.", fi.Name())
			}
		} else {
			if !fi.Mode().IsRegular() {
				log.Printf("%s is not regular file", fi.Name())
				continue
			}

			err := cachectl.RunPurgePages(target.Path, fi.Size(), target.Rate, verbose)
			if err != nil {
				log.Printf("%s: %s", fi.Name(), err.Error())
			}
		}
	}
}

func waitSignal() int {
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan,
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT)

	var exitcode int

	s := <-sigchan

	switch s {
	case syscall.SIGHUP:
		fallthrough
	case syscall.SIGINT:
		fallthrough
	case syscall.SIGTERM:
		fallthrough
	case syscall.SIGQUIT:
		exitcode = 0
	default:
		exitcode = 1
	}

	return exitcode
}

func main() {

	// Parse flags
	version := flag.Bool("v", false, "show version")
	confPath := flag.String("c", "", "configuration file for cachectld")
	flag.Parse()

	if *version {
		cachectl.PrintVersion(cachectl.Cachectld)
		os.Exit(0)
	}

	var confCachectld cachectl.ConfToml
	err := cachectl.LoadConf(*confPath, &confCachectld)
	if err != nil {
		log.Fatal(err.Error())
	}

	err = cachectl.ValidateConf(&confCachectld)
	if err != nil {
		log.Fatal(err.Error())
	}

	for _, target := range confCachectld.Targets {
		go scheduledPurgePages(target)
	}

	code := waitSignal()

	os.Exit(code)
}
<commit_msg>cachectld: refactored.<commit_after>package main

import (
	"./cachectl"
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"regexp"
	"syscall"
	"time"
)

func purgePages(target *cachectl.SectionTarget, re *regexp.Regexp) error {
	fi, err := os.Stat(target.Path)
	if err != nil {
		return err
	}

	verbose := false

	if fi.IsDir() {
		err := cachectl.WalkPurgePages(target.Path, re, target.Rate, verbose)
		if err != nil {
			return fmt.Errorf("failed to walk in %s.", fi.Name())
		}
	} else {
		if !fi.Mode().IsRegular() {
			return fmt.Errorf("%s is not regular file", fi.Name())
		}

		err := cachectl.RunPurgePages(target.Path, fi.Size(), target.Rate, verbose)
		if err != nil {
			return fmt.Errorf("%s: %s", fi.Name(), err.Error())
		}
	}

	return nil
}

func scheduledPurgePages(target *cachectl.SectionTarget) {

	re := regexp.MustCompile(target.Filter)

	for {
		timer := time.NewTimer(time.Second * time.Duration(target.PurgeInterval))
		<-timer.C

		err := purgePages(target, re)
		if err != nil {
			log.Println(err.Error())
		}
	}
}

func waitSignal() int {
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan,
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT)

	var exitcode int

	s := <-sigchan

	switch s {
	case syscall.SIGHUP:
		fallthrough
	case syscall.SIGINT:
		fallthrough
	case syscall.SIGTERM:
		fallthrough
	case syscall.SIGQUIT:
		exitcode = 0
	default:
		exitcode = 1
	}

	return exitcode
}

func main() {

	// Parse flags
	version := flag.Bool("v", false, "show version")
	confPath := flag.String("c", "", "configuration file for cachectld")
	flag.Parse()

	if *version {
		cachectl.PrintVersion(cachectl.Cachectld)
		os.Exit(0)
	}

	var confCachectld cachectl.ConfToml
	err := cachectl.LoadConf(*confPath, &confCachectld)
	if err != nil {
		log.Fatal(err.Error())
	}

	err = cachectl.ValidateConf(&confCachectld)
	if err != nil {
		log.Fatal(err.Error())
	}

	for _, target := range confCachectld.Targets {
		go scheduledPurgePages(target)
	}

	code := waitSignal()

	os.Exit(code)
}
<|endoftext|>


<commit_before>// author: Jacky Boen

package main

import (
	"fmt"
	"github.com/veandco/go-sdl2/sdl"
	"os"
)

var winTitle string = "Go-SDL2 Render"
var winWidth, winHeight int = 800, 600

func run() int {
	var window *sdl.Window
	var renderer *sdl.Renderer
	var points []sdl.Point
	var rect sdl.Rect
	var rects []sdl.Rect

	window, err := sdl.CreateWindow(winTitle, sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED,
		winWidth, winHeight, sdl.WINDOW_SHOWN)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to create window: %s\n", err)
		return 1
	}
	defer window.Destroy()

	sdl.CallQueue <- func() {
        renderer, err = sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED)
        if err != nil {
            fmt.Fprint(os.Stderr, "Failed to create renderer: %s\n", err)
            os.Exit(2)
        }
        renderer.Clear()
        println("queue: A")
    }
    defer renderer.Destroy()

    go func() {
        println("goroutine: A")
    }()

    sdl.CallQueue <- func() {
        renderer.SetDrawColor(255, 255, 255, 255)
        renderer.DrawPoint(150, 300)
        println("queue: B")
    }

    go func() {
        println("goroutine: B")
    }()

    sdl.CallQueue <- func() {
        renderer.SetDrawColor(0, 0, 255, 255)
        renderer.DrawLine(0, 0, 200, 200)
        println("queue: C")
    }

    go func() {
        println("goroutine: C")
    }()

    sdl.CallQueue <- func() {
        points = []sdl.Point{{0, 0}, {100, 300}, {100, 300}, {200, 0}}
        renderer.SetDrawColor(255, 255, 0, 255)
        renderer.DrawLines(points)
        println("queue: D")
    }

    go func() {
        println("goroutine: D")
    }()

    sdl.CallQueue <- func() {
        rect = sdl.Rect{300, 0, 200, 200}
        renderer.SetDrawColor(255, 0, 0, 255)
        renderer.DrawRect(&rect)
        println("queue: E")
    }

    go func() {
        println("goroutine: E")
    }()

    sdl.CallQueue <- func() {
        rects = []sdl.Rect{{400, 400, 100, 100}, {550, 350, 200, 200}}
        renderer.SetDrawColor(0, 255, 255, 255)
        renderer.DrawRects(rects)
        println("queue: F")
    }

    go func() {
        println("goroutine: F")
    }()

    sdl.CallQueue <- func() {
        rect = sdl.Rect{250, 250, 200, 200}
        renderer.SetDrawColor(0, 255, 0, 255)
        renderer.FillRect(&rect)
        println("queue: G")
    }

    go func() {
        println("goroutine: G")
    }()

    sdl.CallQueue <- func() {
        rects = []sdl.Rect{{500, 300, 100, 100}, {200, 300, 200, 200}}
        renderer.SetDrawColor(255, 0, 255, 255)
        renderer.FillRects(rects)
        println("queue: H")
    }

    go func() {
        println("goroutine: H")
    }()

    sdl.CallQueue <- func() {
        renderer.Present()
        println("queue: I")
    }

    go func() {
        println("goroutine: I")
    }()

    sdl.Delay(2000)

    return 0
}

func main() {
	os.Exit(run())
}
<commit_msg>examples: render_queue: fixed possible race condition<commit_after>// author: Jacky Boen

package main

import (
	"fmt"
	"github.com/veandco/go-sdl2/sdl"
	"os"
)

var winTitle string = "Go-SDL2 Render"
var winWidth, winHeight int = 800, 600

func run() int {
	var window *sdl.Window
	var renderer *sdl.Renderer
	var points []sdl.Point
	var rect sdl.Rect
	var rects []sdl.Rect

	window, err := sdl.CreateWindow(winTitle, sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED,
		winWidth, winHeight, sdl.WINDOW_SHOWN)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to create window: %s\n", err)
		return 1
	}
	defer window.Destroy()

	renderer, err = sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED)
	if err != nil {
		fmt.Fprint(os.Stderr, "Failed to create renderer: %s\n", err)
		os.Exit(2)
	}
	renderer.Clear()
	defer renderer.Destroy()

	go func() {
		println("goroutine: A")
	}()

	sdl.CallQueue <- func() {
		renderer.SetDrawColor(255, 255, 255, 255)
		renderer.DrawPoint(150, 300)
		println("queue: A")
	}

	go func() {
		println("goroutine: B")
	}()

	sdl.CallQueue <- func() {
		renderer.SetDrawColor(0, 0, 255, 255)
		renderer.DrawLine(0, 0, 200, 200)
		println("queue: B")
	}

	go func() {
		println("goroutine: C")
	}()

	sdl.CallQueue <- func() {
		points = []sdl.Point{{0, 0}, {100, 300}, {100, 300}, {200, 0}}
		renderer.SetDrawColor(255, 255, 0, 255)
		renderer.DrawLines(points)
		println("queue: C")
	}

	go func() {
		println("goroutine: D")
	}()

	sdl.CallQueue <- func() {
		rect = sdl.Rect{300, 0, 200, 200}
		renderer.SetDrawColor(255, 0, 0, 255)
		renderer.DrawRect(&rect)
		println("queue: D")
	}

	go func() {
		println("goroutine: E")
	}()

	sdl.CallQueue <- func() {
		rects = []sdl.Rect{{400, 400, 100, 100}, {550, 350, 200, 200}}
		renderer.SetDrawColor(0, 255, 255, 255)
		renderer.DrawRects(rects)
		println("queue: E")
	}

	go func() {
		println("goroutine: F")
	}()

	sdl.CallQueue <- func() {
		rect = sdl.Rect{250, 250, 200, 200}
		renderer.SetDrawColor(0, 255, 0, 255)
		renderer.FillRect(&rect)
		println("queue: F")
	}

	go func() {
		println("goroutine: G")
	}()

	sdl.CallQueue <- func() {
		rects = []sdl.Rect{{500, 300, 100, 100}, {200, 300, 200, 200}}
		renderer.SetDrawColor(255, 0, 255, 255)
		renderer.FillRects(rects)
		println("queue: G")
	}

	go func() {
		println("goroutine: H")
	}()

	sdl.CallQueue <- func() {
		renderer.Present()
		println("queue: H")
	}

	sdl.Delay(2000)

	return 0
}

func main() {
	os.Exit(run())
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/HewlettPackard/oneview-golang/ov"
	"os"
	"path/filepath"
)

func main() {

	var (
		ClientOV    *ov.OVClient
		name_to_get = "<volume attachment name>"
	)

	ovc := ClientOV.NewOVClient(
		os.Getenv("ONEVIEW_OV_USER"),
		os.Getenv("ONEVIEW_OV_PASSWORD"),
		os.Getenv("ONEVIEW_OV_DOMAIN"),
		os.Getenv("ONEVIEW_OV_ENDPOINT"),
		false,
		1800,
		"*")

	// Get All the attachments present
	fmt.Println("\nGetting all the storage attachments present in the system: \n")
	sort := "name:desc"
	attachment_list, err := ovc.GetStorageAttachments("", sort, "", "")
	if err != nil {
		fmt.Println("Error Getting the storage attachments ", err)
	}
	for i := 0; i < len(attachment_list.Members); i++ {
		fmt.Println(attachment_list.Members[i].URI)
	}
	id := filepath.Base(string(attachment_list.Members[0].URI))

	// Get volume attachment by id
	fmt.Println("\nGetting details of volume attachment with name: ", id)
	volAttach_by_id, _ := ovc.GetStorageAttachmentById(id)
	fmt.Println(volAttach_by_id)
	// Get volume attachment by name
	fmt.Println("\nGetting details of volume attachment with name: ", name_to_get)
	volAttach_by_name, _ := ovc.GetStorageAttachmentByName(name_to_get)
	fmt.Println(volAttach_by_name)
}
<commit_msg>Added error handling in example<commit_after>package main

import (
	"fmt"
	"github.com/HewlettPackard/oneview-golang/ov"
	"os"
	"path/filepath"
)

func main() {

	var (
		ClientOV    *ov.OVClient
		name_to_get = "<volume attachment name>"
	)

	ovc := ClientOV.NewOVClient(
		os.Getenv("ONEVIEW_OV_USER"),
		os.Getenv("ONEVIEW_OV_PASSWORD"),
		os.Getenv("ONEVIEW_OV_DOMAIN"),
		os.Getenv("ONEVIEW_OV_ENDPOINT"),
		false,
		1800,
		"*")

	// Get All the attachments present
	fmt.Println("\nGetting all the storage attachments present in the system: \n")
	sort := "name:desc"
	attachment_list, err := ovc.GetStorageAttachments("", sort, "", "")
	if err != nil {
		fmt.Println("Error Getting the storage attachments ", err)
	}
	for i := 0; i < len(attachment_list.Members); i++ {
		fmt.Println(attachment_list.Members[i].URI)
	}
	id := filepath.Base(string(attachment_list.Members[0].URI))

	// Get volume attachment by id
	fmt.Println("\nGetting details of volume attachment with id: ", id)
	volAttach_by_id, err_id := ovc.GetStorageAttachmentById(id)
	if err_id != nil {
		fmt.Println("Error Getting the storage attachments ", err_id)
	}

	fmt.Println(volAttach_by_id)
	// Get volume attachment by name
	fmt.Println("\nGetting details of volume attachment with name: ", name_to_get)
	volAttach_by_name, err_name := ovc.GetStorageAttachmentByName(name_to_get)
	if err_name != nil {
		fmt.Println("Error Getting the storage attachments ", err_name)
	}

	fmt.Println(volAttach_by_name)
}
<|endoftext|>


<commit_before>package arn

import (
	"errors"
	"os"
	"os/exec"
	"path"
	"sort"
	"strings"

	"github.com/aerogo/nano"
	"github.com/animenotifier/arn/autocorrect"
	"github.com/fatih/color"
)

// SoundTrack ...
type SoundTrack struct {
	ID        string           `json:"id"`
	Title     string           `json:"title" editable:"true"`
	Media     []*ExternalMedia `json:"media" editable:"true"`
	Tags      []string         `json:"tags" editable:"true" tooltip:"

anime:ID to connect it with anime

opening for openings

ending for endings

cover for covers

remix for remixes

"`
	IsDraft   bool             `json:"isDraft" editable:"true"`
	File      string           `json:"file"`
	Created   string           `json:"created"`
	CreatedBy string           `json:"createdBy"`
	Edited    string           `json:"edited"`
	EditedBy  string           `json:"editedBy"`
	LikeableImplementation
}

// Link returns the permalink for the track.
func (track *SoundTrack) Link() string {
	return "/soundtrack/" + track.ID
}

// MediaByService ...
func (track *SoundTrack) MediaByService(service string) []*ExternalMedia {
	filtered := []*ExternalMedia{}

	for _, media := range track.Media {
		if media.Service == service {
			filtered = append(filtered, media)
		}
	}

	return filtered
}

// HasTag returns true if it contains the given tag.
func (track *SoundTrack) HasTag(search string) bool {
	for _, tag := range track.Tags {
		if tag == search {
			return true
		}
	}

	return false
}

// Anime fetches all tagged anime of the sound track.
func (track *SoundTrack) Anime() []*Anime {
	var animeList []*Anime

	for _, tag := range track.Tags {
		if strings.HasPrefix(tag, "anime:") {
			animeID := strings.TrimPrefix(tag, "anime:")
			anime, err := GetAnime(animeID)

			if err != nil {
				color.Red("Error fetching anime: %v", err)
				continue
			}

			animeList = append(animeList, anime)
		}
	}

	return animeList
}

// Beatmaps returns all osu beatmap IDs of the sound track.
func (track *SoundTrack) Beatmaps() []string {
	var beatmaps []string

	for _, tag := range track.Tags {
		if strings.HasPrefix(tag, "osu-beatmap:") {
			osuID := strings.TrimPrefix(tag, "osu-beatmap:")
			beatmaps = append(beatmaps, osuID)
		}
	}

	return beatmaps
}

// MainAnime ...
func (track *SoundTrack) MainAnime() *Anime {
	allAnime := track.Anime()

	if len(allAnime) == 0 {
		return nil
	}

	return allAnime[0]
}

// Creator returns the user who created this track.
func (track *SoundTrack) Creator() *User {
	user, _ := GetUser(track.CreatedBy)
	return user
}

// EditedByUser returns the user who edited this track last.
func (track *SoundTrack) EditedByUser() *User {
	user, _ := GetUser(track.EditedBy)
	return user
}

// OnLike is called when the soundtrack receives a like.
func (track *SoundTrack) OnLike(likedBy *User) {
	if likedBy.ID == track.CreatedBy {
		return
	}

	go func() {
		track.Creator().SendNotification(&PushNotification{
			Title:   likedBy.Nick + " liked your soundtrack " + track.Title,
			Message: likedBy.Nick + " liked your soundtrack " + track.Title + ".",
			Icon:    "https:" + likedBy.AvatarLink("large"),
			Link:    "https://notify.moe" + likedBy.Link(),
			Type:    NotificationTypeLike,
		})
	}()
}

// Publish ...
func (track *SoundTrack) Publish() error {
	// No draft
	if !track.IsDraft {
		return errors.New("Not a draft")
	}

	// No media added
	if len(track.Media) == 0 {
		return errors.New("No media specified (at least 1 media source is required)")
	}

	animeFound := false

	for _, tag := range track.Tags {
		tag = autocorrect.FixTag(tag)

		if strings.HasPrefix(tag, "anime:") {
			animeID := strings.TrimPrefix(tag, "anime:")
			_, err := GetAnime(animeID)

			if err != nil {
				return errors.New("Invalid anime ID")
			}

			animeFound = true
		}
	}

	// No anime found
	if !animeFound {
		return errors.New("Need to specify at least one anime")
	}

	// No tags
	if len(track.Tags) < 1 {
		return errors.New("Need to specify at least one tag")
	}

	draftIndex, err := GetDraftIndex(track.CreatedBy)

	if err != nil {
		return err
	}

	if draftIndex.SoundTrackID == "" {
		return errors.New("Soundtrack draft doesn't exist in the user draft index")
	}

	track.IsDraft = false
	draftIndex.SoundTrackID = ""
	draftIndex.Save()
	return nil
}

// Unpublish ...
func (track *SoundTrack) Unpublish() error {
	draftIndex, err := GetDraftIndex(track.CreatedBy)

	if err != nil {
		return err
	}

	if draftIndex.SoundTrackID != "" {
		return errors.New("You still have an unfinished draft")
	}

	track.IsDraft = true
	draftIndex.SoundTrackID = track.ID
	draftIndex.Save()
	return nil
}

// Download downloads the track.
func (track *SoundTrack) Download() error {
	youtubeVideos := track.MediaByService("Youtube")

	if len(youtubeVideos) == 0 {
		return errors.New("No Youtube ID")
	}

	youtubeID := youtubeVideos[0].ServiceID

	// Check for existing file
	if track.File != "" {
		stat, err := os.Stat(track.File)

		if err != nil && !stat.IsDir() && stat.Size() > 0 {
			return errors.New("Already downloaded")
		}
	}

	audioDirectory := path.Join(Root, "audio")
	baseName := track.ID + "|" + youtubeID
	filePath := path.Join(audioDirectory, baseName)

	cmd := exec.Command("youtube-dl", "--extract-audio", "--audio-quality", "0", "--output", filePath+".%(ext)s", youtubeID)
	err := cmd.Start()

	if err != nil {
		return err
	}

	err = cmd.Wait()

	if err != nil {
		return err
	}

	fullPath := FindFileWithExtension(baseName, audioDirectory, []string{
		".opus",
		".webm",
		".ogg",
		".m4a",
		".mp3",
		".flac",
		".wav",
	})

	extension := path.Ext(fullPath)
	track.File = baseName + extension

	return nil
}

// String implements the default string serialization.
func (track *SoundTrack) String() string {
	return track.Title
}

// SortSoundTracksLatestFirst ...
func SortSoundTracksLatestFirst(tracks []*SoundTrack) {
	sort.Slice(tracks, func(i, j int) bool {
		return tracks[i].Created > tracks[j].Created
	})
}

// SortSoundTracksPopularFirst ...
func SortSoundTracksPopularFirst(tracks []*SoundTrack) {
	sort.Slice(tracks, func(i, j int) bool {
		aLikes := len(tracks[i].Likes)
		bLikes := len(tracks[j].Likes)

		if aLikes == bLikes {
			return tracks[i].Created > tracks[j].Created
		}

		return aLikes > bLikes
	})
}

// GetSoundTrack ...
func GetSoundTrack(id string) (*SoundTrack, error) {
	track, err := DB.Get("SoundTrack", id)

	if err != nil {
		return nil, err
	}

	return track.(*SoundTrack), nil
}

// StreamSoundTracks returns a stream of all soundtracks.
func StreamSoundTracks() chan *SoundTrack {
	channel := make(chan *SoundTrack, nano.ChannelBufferSize)

	go func() {
		for obj := range DB.All("SoundTrack") {
			channel <- obj.(*SoundTrack)
		}

		close(channel)
	}()

	return channel
}

// AllSoundTracks ...
func AllSoundTracks() ([]*SoundTrack, error) {
	var all []*SoundTrack

	for obj := range StreamSoundTracks() {
		all = append(all, obj)
	}

	return all, nil
}

// FilterSoundTracks filters all soundtracks by a custom function.
func FilterSoundTracks(filter func(*SoundTrack) bool) []*SoundTrack {
	var filtered []*SoundTrack

	for obj := range StreamSoundTracks() {
		if filter(obj) {
			filtered = append(filtered, obj)
		}
	}

	return filtered
}
<commit_msg>Fixed os.Stat call in soundtracks<commit_after>package arn

import (
	"errors"
	"os"
	"os/exec"
	"path"
	"sort"
	"strings"

	"github.com/aerogo/nano"
	"github.com/animenotifier/arn/autocorrect"
	"github.com/fatih/color"
)

// SoundTrack ...
type SoundTrack struct {
	ID        string           `json:"id"`
	Title     string           `json:"title" editable:"true"`
	Media     []*ExternalMedia `json:"media" editable:"true"`
	Tags      []string         `json:"tags" editable:"true" tooltip:"

anime:ID to connect it with anime

opening for openings

ending for endings

cover for covers

remix for remixes

"`
	IsDraft   bool             `json:"isDraft" editable:"true"`
	File      string           `json:"file"`
	Created   string           `json:"created"`
	CreatedBy string           `json:"createdBy"`
	Edited    string           `json:"edited"`
	EditedBy  string           `json:"editedBy"`
	LikeableImplementation
}

// Link returns the permalink for the track.
func (track *SoundTrack) Link() string {
	return "/soundtrack/" + track.ID
}

// MediaByService ...
func (track *SoundTrack) MediaByService(service string) []*ExternalMedia {
	filtered := []*ExternalMedia{}

	for _, media := range track.Media {
		if media.Service == service {
			filtered = append(filtered, media)
		}
	}

	return filtered
}

// HasTag returns true if it contains the given tag.
func (track *SoundTrack) HasTag(search string) bool {
	for _, tag := range track.Tags {
		if tag == search {
			return true
		}
	}

	return false
}

// Anime fetches all tagged anime of the sound track.
func (track *SoundTrack) Anime() []*Anime {
	var animeList []*Anime

	for _, tag := range track.Tags {
		if strings.HasPrefix(tag, "anime:") {
			animeID := strings.TrimPrefix(tag, "anime:")
			anime, err := GetAnime(animeID)

			if err != nil {
				color.Red("Error fetching anime: %v", err)
				continue
			}

			animeList = append(animeList, anime)
		}
	}

	return animeList
}

// Beatmaps returns all osu beatmap IDs of the sound track.
func (track *SoundTrack) Beatmaps() []string {
	var beatmaps []string

	for _, tag := range track.Tags {
		if strings.HasPrefix(tag, "osu-beatmap:") {
			osuID := strings.TrimPrefix(tag, "osu-beatmap:")
			beatmaps = append(beatmaps, osuID)
		}
	}

	return beatmaps
}

// MainAnime ...
func (track *SoundTrack) MainAnime() *Anime {
	allAnime := track.Anime()

	if len(allAnime) == 0 {
		return nil
	}

	return allAnime[0]
}

// Creator returns the user who created this track.
func (track *SoundTrack) Creator() *User {
	user, _ := GetUser(track.CreatedBy)
	return user
}

// EditedByUser returns the user who edited this track last.
func (track *SoundTrack) EditedByUser() *User {
	user, _ := GetUser(track.EditedBy)
	return user
}

// OnLike is called when the soundtrack receives a like.
func (track *SoundTrack) OnLike(likedBy *User) {
	if likedBy.ID == track.CreatedBy {
		return
	}

	go func() {
		track.Creator().SendNotification(&PushNotification{
			Title:   likedBy.Nick + " liked your soundtrack " + track.Title,
			Message: likedBy.Nick + " liked your soundtrack " + track.Title + ".",
			Icon:    "https:" + likedBy.AvatarLink("large"),
			Link:    "https://notify.moe" + likedBy.Link(),
			Type:    NotificationTypeLike,
		})
	}()
}

// Publish ...
func (track *SoundTrack) Publish() error {
	// No draft
	if !track.IsDraft {
		return errors.New("Not a draft")
	}

	// No media added
	if len(track.Media) == 0 {
		return errors.New("No media specified (at least 1 media source is required)")
	}

	animeFound := false

	for _, tag := range track.Tags {
		tag = autocorrect.FixTag(tag)

		if strings.HasPrefix(tag, "anime:") {
			animeID := strings.TrimPrefix(tag, "anime:")
			_, err := GetAnime(animeID)

			if err != nil {
				return errors.New("Invalid anime ID")
			}

			animeFound = true
		}
	}

	// No anime found
	if !animeFound {
		return errors.New("Need to specify at least one anime")
	}

	// No tags
	if len(track.Tags) < 1 {
		return errors.New("Need to specify at least one tag")
	}

	draftIndex, err := GetDraftIndex(track.CreatedBy)

	if err != nil {
		return err
	}

	if draftIndex.SoundTrackID == "" {
		return errors.New("Soundtrack draft doesn't exist in the user draft index")
	}

	track.IsDraft = false
	draftIndex.SoundTrackID = ""
	draftIndex.Save()
	return nil
}

// Unpublish ...
func (track *SoundTrack) Unpublish() error {
	draftIndex, err := GetDraftIndex(track.CreatedBy)

	if err != nil {
		return err
	}

	if draftIndex.SoundTrackID != "" {
		return errors.New("You still have an unfinished draft")
	}

	track.IsDraft = true
	draftIndex.SoundTrackID = track.ID
	draftIndex.Save()
	return nil
}

// Download downloads the track.
func (track *SoundTrack) Download() error {
	youtubeVideos := track.MediaByService("Youtube")

	if len(youtubeVideos) == 0 {
		return errors.New("No Youtube ID")
	}

	youtubeID := youtubeVideos[0].ServiceID

	// Check for existing file
	if track.File != "" {
		stat, err := os.Stat(track.File)

		if err == nil && !stat.IsDir() && stat.Size() > 0 {
			return errors.New("Already downloaded")
		}
	}

	audioDirectory := path.Join(Root, "audio")
	baseName := track.ID + "|" + youtubeID
	filePath := path.Join(audioDirectory, baseName)

	cmd := exec.Command("youtube-dl", "--extract-audio", "--audio-quality", "0", "--output", filePath+".%(ext)s", youtubeID)
	err := cmd.Start()

	if err != nil {
		return err
	}

	err = cmd.Wait()

	if err != nil {
		return err
	}

	fullPath := FindFileWithExtension(baseName, audioDirectory, []string{
		".opus",
		".webm",
		".ogg",
		".m4a",
		".mp3",
		".flac",
		".wav",
	})

	extension := path.Ext(fullPath)
	track.File = baseName + extension

	return nil
}

// String implements the default string serialization.
func (track *SoundTrack) String() string {
	return track.Title
}

// SortSoundTracksLatestFirst ...
func SortSoundTracksLatestFirst(tracks []*SoundTrack) {
	sort.Slice(tracks, func(i, j int) bool {
		return tracks[i].Created > tracks[j].Created
	})
}

// SortSoundTracksPopularFirst ...
func SortSoundTracksPopularFirst(tracks []*SoundTrack) {
	sort.Slice(tracks, func(i, j int) bool {
		aLikes := len(tracks[i].Likes)
		bLikes := len(tracks[j].Likes)

		if aLikes == bLikes {
			return tracks[i].Created > tracks[j].Created
		}

		return aLikes > bLikes
	})
}

// GetSoundTrack ...
func GetSoundTrack(id string) (*SoundTrack, error) {
	track, err := DB.Get("SoundTrack", id)

	if err != nil {
		return nil, err
	}

	return track.(*SoundTrack), nil
}

// StreamSoundTracks returns a stream of all soundtracks.
func StreamSoundTracks() chan *SoundTrack {
	channel := make(chan *SoundTrack, nano.ChannelBufferSize)

	go func() {
		for obj := range DB.All("SoundTrack") {
			channel <- obj.(*SoundTrack)
		}

		close(channel)
	}()

	return channel
}

// AllSoundTracks ...
func AllSoundTracks() ([]*SoundTrack, error) {
	var all []*SoundTrack

	for obj := range StreamSoundTracks() {
		all = append(all, obj)
	}

	return all, nil
}

// FilterSoundTracks filters all soundtracks by a custom function.
func FilterSoundTracks(filter func(*SoundTrack) bool) []*SoundTrack {
	var filtered []*SoundTrack

	for obj := range StreamSoundTracks() {
		if filter(obj) {
			filtered = append(filtered, obj)
		}
	}

	return filtered
}
<|endoftext|>


<commit_before>package api

import (
	"context"
	"net/http"
	"regexp"
	"strings"

	"github.com/go-gorp/gorp"
	"github.com/gorilla/mux"

	"github.com/ovh/cds/engine/api/cache"
	"github.com/ovh/cds/engine/api/event"
	"github.com/ovh/cds/engine/api/group"
	"github.com/ovh/cds/engine/api/keys"
	"github.com/ovh/cds/engine/api/permission"
	"github.com/ovh/cds/engine/api/project"
	"github.com/ovh/cds/engine/api/workflow"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
)

func (api *API) getProjectsHandler() Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		withApplications := FormBool(r, "application")
		withWorkflows := FormBool(r, "workflow")
		filterByRepo := r.FormValue("repo")

		opts := []project.LoadOptionFunc{}
		if withApplications {
			opts = append(opts, project.LoadOptions.WithApplications)
		}

		if withWorkflows {
			opts = append(opts, project.LoadOptions.WithWorkflows)
		}

		if filterByRepo == "" {
			projects, err := project.LoadAll(api.mustDB(), api.Cache, getUser(ctx), opts...)
			if err != nil {
				return sdk.WrapError(err, "getProjectsHandler")
			}
			return WriteJSON(w, projects, http.StatusOK)
		}

		var filterByRepoFunc = func(db gorp.SqlExecutor, store cache.Store, p *sdk.Project, u *sdk.User) error {
			//Filter the applications by repo
			apps := []sdk.Application{}
			for i := range p.Applications {
				if p.Applications[i].RepositoryFullname == filterByRepo {
					apps = append(apps, p.Applications[i])
				}
			}
			p.Applications = apps
			ws := []sdk.Workflow{}
			//Filter the workflow by applications
			for i := range p.Workflows {
				w, err := workflow.LoadByID(db, store, p.Workflows[i].ID, u, workflow.LoadOptions{})
				if err != nil {
					return err
				}

				wapps := w.GetApplications()
				//Checks the workflow use one of the applications
			wapps:
				for _, a := range wapps {
					for _, b := range apps {
						if a.Name == b.Name {
							ws = append(ws, p.Workflows[i])
							break wapps
						}
					}
				}
			}
			p.Workflows = ws
			return nil
		}
		opts = append(opts, &filterByRepoFunc)

		projects, err := project.LoadAllByRepo(api.mustDB(), api.Cache, getUser(ctx), filterByRepo, opts...)
		if err != nil {
			return sdk.WrapError(err, "getProjectsHandler")
		}
		return WriteJSON(w, projects, http.StatusOK)
	}
}

func (api *API) updateProjectHandler() Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// Get project name in URL
		vars := mux.Vars(r)
		key := vars["permProjectKey"]

		proj := &sdk.Project{}
		if err := UnmarshalBody(r, proj); err != nil {
			return sdk.WrapError(err, "updateProject> Unmarshall error")
		}

		if proj.Name == "" {
			return sdk.WrapError(sdk.ErrInvalidProjectName, "updateProject> Project name must no be empty")
		}

		// Check Request
		if key != proj.Key {
			return sdk.WrapError(sdk.ErrWrongRequest, "updateProject> bad Project key %s/%s ", key, proj.Key)
		}

		// Check is project exist
		p, errProj := project.Load(api.mustDB(), api.Cache, key, getUser(ctx))
		if errProj != nil {
			return sdk.WrapError(errProj, "updateProject> Cannot load project from db")
		}
		// Update in DB is made given the primary key
		proj.ID = p.ID
		if errUp := project.Update(api.mustDB(), api.Cache, proj, getUser(ctx)); errUp != nil {
			return sdk.WrapError(errUp, "updateProject> Cannot update project %s", key)
		}
		event.PublishUpdateProject(proj, p, getUser(ctx))
		return WriteJSON(w, proj, http.StatusOK)
	}
}

func (api *API) getProjectHandler() Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// Get project name in URL
		vars := mux.Vars(r)
		key := vars["permProjectKey"]

		withVariables := FormBool(r, "withVariables")
		withApplications := FormBool(r, "withApplications")
		withApplicationNames := FormBool(r, "withApplicationNames")
		withApplicationPipelines := FormBool(r, "withApplicationPipelines")
		withPipelines := FormBool(r, "withPipelines")
		withPipelineNames := FormBool(r, "withPipelineNames")
		withEnvironments := FormBool(r, "withEnvironments")
		withGroups := FormBool(r, "withGroups")
		withPermission := FormBool(r, "withPermission")
		withKeys := FormBool(r, "withKeys")
		withWorkflows := FormBool(r, "withWorkflows")
		withWorkflowNames := FormBool(r, "withWorkflowNames")
		withPlatforms := FormBool(r, "withPlatforms")

		opts := []project.LoadOptionFunc{}
		if withVariables {
			opts = append(opts, project.LoadOptions.WithVariables)
		}
		if withApplications {
			opts = append(opts, project.LoadOptions.WithApplications)
		}
		if withApplicationNames {
			opts = append(opts, project.LoadOptions.WithApplicationNames)
		}
		if withApplicationPipelines {
			opts = append(opts, project.LoadOptions.WithApplicationPipelines)
		}
		if withPipelines {
			opts = append(opts, project.LoadOptions.WithPipelines)
		}
		if withPipelineNames {
			opts = append(opts, project.LoadOptions.WithPipelineNames)
		}
		if withEnvironments {
			opts = append(opts, project.LoadOptions.WithEnvironments)
		}
		if withGroups {
			opts = append(opts, project.LoadOptions.WithGroups)
		}
		if withPermission {
			opts = append(opts, project.LoadOptions.WithPermission)
		}
		if withKeys {
			opts = append(opts, project.LoadOptions.WithKeys)
		}
		if withWorkflows {
			opts = append(opts, project.LoadOptions.WithWorkflows)
		}
		if withWorkflowNames {
			opts = append(opts, project.LoadOptions.WithWorkflowNames)
		}
		if withPlatforms {
			opts = append(opts, project.LoadOptions.WithPlatforms)
		}

		p, errProj := project.Load(api.mustDB(), api.Cache, key, getUser(ctx), opts...)
		if errProj != nil {
			return sdk.WrapError(errProj, "getProjectHandler (%s)", key)
		}

		return WriteJSON(w, p, http.StatusOK)
	}
}

func (api *API) addProjectHandler() Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		//Unmarshal data
		p := &sdk.Project{}
		if err := UnmarshalBody(r, p); err != nil {
			return sdk.WrapError(err, "addProjectHandler> Unable to unmarshal body")
		}

		// check projectKey pattern
		if rgxp := regexp.MustCompile(sdk.ProjectKeyPattern); !rgxp.MatchString(p.Key) {
			return sdk.WrapError(sdk.ErrInvalidProjectKey, "addProjectHandler> Project key %s do not respect pattern %s")
		}

		//check project Name
		if p.Name == "" {
			return sdk.WrapError(sdk.ErrInvalidProjectName, "addProjectHandler> Project name must no be empty")
		}

		// Check that project does not already exists
		exist, errExist := project.Exist(api.mustDB(), p.Key)
		if errExist != nil {
			return sdk.WrapError(errExist, "addProjectHandler>  Cannot check if project %s exist", p.Key)
		}

		if exist {
			return sdk.WrapError(sdk.ErrConflict, "addProjectHandler> Project %s already exists", p.Key)
		}

		var groupAttached bool
		for i := range p.ProjectGroups {
			groupPermission := &p.ProjectGroups[i]
			if strings.TrimSpace(groupPermission.Group.Name) == "" {
				continue
			}
			// the default group could not be selected on ui 'Project Add'
			if !group.IsDefaultGroupID(groupPermission.Group.ID) {
				groupAttached = true
			}
		}
		if !groupAttached {
			// check if new auto group does not already exists
			if _, errl := group.LoadGroup(api.mustDB(), p.Name); errl != nil {
				if errl == sdk.ErrGroupNotFound {
					// group name does not exists, add it on project
					permG := sdk.GroupPermission{
						Group:      sdk.Group{Name: strings.Replace(p.Name, " ", "", -1)},
						Permission: permission.PermissionReadWriteExecute,
					}
					p.ProjectGroups = append(p.ProjectGroups, permG)
				} else {
					return sdk.WrapError(errl, "addProjectHandler> Cannot check if group already exists")
				}
			} else {
				return sdk.WrapError(sdk.ErrGroupPresent, "addProjectHandler> Group %s already exists", p.Name)
			}
		}

		//Create a project within a transaction
		tx, errBegin := api.mustDB().Begin()
		defer tx.Rollback()
		if errBegin != nil {
			return sdk.WrapError(errBegin, "addProjectHandler> Cannot start tx")
		}

		if err := project.Insert(tx, api.Cache, p, getUser(ctx)); err != nil {
			return sdk.WrapError(err, "addProjectHandler> Cannot insert project")
		}

		// Add group
		for i := range p.ProjectGroups {
			groupPermission := &p.ProjectGroups[i]

			// Insert group
			groupID, new, errGroup := group.AddGroup(tx, &groupPermission.Group)
			if groupID == 0 {
				return errGroup
			}
			groupPermission.Group.ID = groupID

			if group.IsDefaultGroupID(groupID) {
				groupPermission.Permission = permission.PermissionRead
			}

			// Add group on project
			if err := group.InsertGroupInProject(tx, p.ID, groupPermission.Group.ID, groupPermission.Permission); err != nil {
				return sdk.WrapError(err, "addProjectHandler> Cannot add group %s in project %s", groupPermission.Group.Name, p.Name)
			}

			// Add user in group
			if new {
				if err := group.InsertUserInGroup(tx, groupPermission.Group.ID, getUser(ctx).ID, true); err != nil {
					return sdk.WrapError(err, "addProjectHandler> Cannot add user %s in group %s", getUser(ctx).Username, groupPermission.Group.Name)
				}
			}
		}

		for _, v := range p.Variable {
			if errVar := project.InsertVariable(tx, p, &v, getUser(ctx)); errVar != nil {
				return sdk.WrapError(errVar, "addProjectHandler> Cannot add variable %s in project %s", v.Name, p.Name)
			}
		}

		for _, k := range p.Keys {
			k.ProjectID = p.ID
			switch k.Type {
			case sdk.KeyTypeSSH:
				keyTemp, errK := keys.GenerateSSHKey(k.Name)
				if errK != nil {
					return sdk.WrapError(errK, "addProjectHandler> Cannot generate ssh key for project %s", p.Name)
				}
				k.Key = keyTemp
			case sdk.KeyTypePGP:
				keyTemp, errK := keys.GeneratePGPKeyPair(k.Name)
				if errK != nil {
					return sdk.WrapError(errK, "addProjectHandler> Cannot generate pgp key for project %s", p.Name)
				}
				k.Key = keyTemp
			}
			if errK := project.InsertKey(tx, &k); errK != nil {
				return sdk.WrapError(errK, "addProjectHandler> Cannot add key %s in project %s", k.Name)
			}
		}

		if err := tx.Commit(); err != nil {
			return sdk.WrapError(err, "addProjectHandler> Cannot commit transaction")
		}

		event.PublishAddProject(p, getUser(ctx))

		return WriteJSON(w, p, http.StatusCreated)
	}
}

func (api *API) deleteProjectHandler() Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// Get project name in URL
		vars := mux.Vars(r)
		key := vars["permProjectKey"]

		p, errProj := project.Load(api.mustDB(), api.Cache, key, getUser(ctx), project.LoadOptions.WithPipelines, project.LoadOptions.WithApplications)
		if errProj != nil {
			if errProj != sdk.ErrNoProject {
				return sdk.WrapError(errProj, "deleteProject> load project '%s' from db", key)
			}
			return sdk.WrapError(errProj, "deleteProject> cannot load project %s", key)
		}

		if len(p.Pipelines) > 0 {
			return sdk.WrapError(sdk.ErrProjectHasPipeline, "deleteProject> Project '%s' still used by %d pipelines", key, len(p.Pipelines))
		}

		if len(p.Applications) > 0 {
			return sdk.WrapError(sdk.ErrProjectHasApplication, "deleteProject> Project '%s' still used by %d applications", key, len(p.Applications))
		}

		tx, errBegin := api.mustDB().Begin()
		if errBegin != nil {
			return sdk.WrapError(errBegin, "deleteProject> Cannot start transaction")
		}
		defer tx.Rollback()

		if err := project.Delete(tx, api.Cache, p.Key); err != nil {
			return sdk.WrapError(err, "deleteProject> cannot delete project %s", key)
		}
		if err := tx.Commit(); err != nil {
			return sdk.WrapError(err, "deleteProject> Cannot commit transaction")
		}

		event.PublishDeleteProject(p, getUser(ctx))

		log.Info("Project %s deleted.", p.Name)

		return nil
	}
}
<commit_msg>fix(api): do not erase vcs server datas (#2434)<commit_after>package api

import (
	"context"
	"net/http"
	"regexp"
	"strings"

	"github.com/go-gorp/gorp"
	"github.com/gorilla/mux"

	"github.com/ovh/cds/engine/api/cache"
	"github.com/ovh/cds/engine/api/event"
	"github.com/ovh/cds/engine/api/group"
	"github.com/ovh/cds/engine/api/keys"
	"github.com/ovh/cds/engine/api/permission"
	"github.com/ovh/cds/engine/api/project"
	"github.com/ovh/cds/engine/api/workflow"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
)

func (api *API) getProjectsHandler() Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		withApplications := FormBool(r, "application")
		withWorkflows := FormBool(r, "workflow")
		filterByRepo := r.FormValue("repo")

		opts := []project.LoadOptionFunc{}
		if withApplications {
			opts = append(opts, project.LoadOptions.WithApplications)
		}

		if withWorkflows {
			opts = append(opts, project.LoadOptions.WithWorkflows)
		}

		if filterByRepo == "" {
			projects, err := project.LoadAll(api.mustDB(), api.Cache, getUser(ctx), opts...)
			if err != nil {
				return sdk.WrapError(err, "getProjectsHandler")
			}
			return WriteJSON(w, projects, http.StatusOK)
		}

		var filterByRepoFunc = func(db gorp.SqlExecutor, store cache.Store, p *sdk.Project, u *sdk.User) error {
			//Filter the applications by repo
			apps := []sdk.Application{}
			for i := range p.Applications {
				if p.Applications[i].RepositoryFullname == filterByRepo {
					apps = append(apps, p.Applications[i])
				}
			}
			p.Applications = apps
			ws := []sdk.Workflow{}
			//Filter the workflow by applications
			for i := range p.Workflows {
				w, err := workflow.LoadByID(db, store, p.Workflows[i].ID, u, workflow.LoadOptions{})
				if err != nil {
					return err
				}

				wapps := w.GetApplications()
				//Checks the workflow use one of the applications
			wapps:
				for _, a := range wapps {
					for _, b := range apps {
						if a.Name == b.Name {
							ws = append(ws, p.Workflows[i])
							break wapps
						}
					}
				}
			}
			p.Workflows = ws
			return nil
		}
		opts = append(opts, &filterByRepoFunc)

		projects, err := project.LoadAllByRepo(api.mustDB(), api.Cache, getUser(ctx), filterByRepo, opts...)
		if err != nil {
			return sdk.WrapError(err, "getProjectsHandler")
		}
		return WriteJSON(w, projects, http.StatusOK)
	}
}

func (api *API) updateProjectHandler() Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// Get project name in URL
		vars := mux.Vars(r)
		key := vars["permProjectKey"]

		proj := &sdk.Project{}
		if err := UnmarshalBody(r, proj); err != nil {
			return sdk.WrapError(err, "updateProject> Unmarshall error")
		}

		if proj.Name == "" {
			return sdk.WrapError(sdk.ErrInvalidProjectName, "updateProject> Project name must no be empty")
		}

		// Check Request
		if key != proj.Key {
			return sdk.WrapError(sdk.ErrWrongRequest, "updateProject> bad Project key %s/%s ", key, proj.Key)
		}

		// Check is project exist
		p, errProj := project.Load(api.mustDB(), api.Cache, key, getUser(ctx))
		if errProj != nil {
			return sdk.WrapError(errProj, "updateProject> Cannot load project from db")
		}
		// Update in DB is made given the primary key
		proj.ID = p.ID
		proj.VCSServers = p.VCSServers
		if errUp := project.Update(api.mustDB(), api.Cache, proj, getUser(ctx)); errUp != nil {
			return sdk.WrapError(errUp, "updateProject> Cannot update project %s", key)
		}
		event.PublishUpdateProject(proj, p, getUser(ctx))
		return WriteJSON(w, proj, http.StatusOK)
	}
}

func (api *API) getProjectHandler() Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// Get project name in URL
		vars := mux.Vars(r)
		key := vars["permProjectKey"]

		withVariables := FormBool(r, "withVariables")
		withApplications := FormBool(r, "withApplications")
		withApplicationNames := FormBool(r, "withApplicationNames")
		withApplicationPipelines := FormBool(r, "withApplicationPipelines")
		withPipelines := FormBool(r, "withPipelines")
		withPipelineNames := FormBool(r, "withPipelineNames")
		withEnvironments := FormBool(r, "withEnvironments")
		withGroups := FormBool(r, "withGroups")
		withPermission := FormBool(r, "withPermission")
		withKeys := FormBool(r, "withKeys")
		withWorkflows := FormBool(r, "withWorkflows")
		withWorkflowNames := FormBool(r, "withWorkflowNames")
		withPlatforms := FormBool(r, "withPlatforms")

		opts := []project.LoadOptionFunc{}
		if withVariables {
			opts = append(opts, project.LoadOptions.WithVariables)
		}
		if withApplications {
			opts = append(opts, project.LoadOptions.WithApplications)
		}
		if withApplicationNames {
			opts = append(opts, project.LoadOptions.WithApplicationNames)
		}
		if withApplicationPipelines {
			opts = append(opts, project.LoadOptions.WithApplicationPipelines)
		}
		if withPipelines {
			opts = append(opts, project.LoadOptions.WithPipelines)
		}
		if withPipelineNames {
			opts = append(opts, project.LoadOptions.WithPipelineNames)
		}
		if withEnvironments {
			opts = append(opts, project.LoadOptions.WithEnvironments)
		}
		if withGroups {
			opts = append(opts, project.LoadOptions.WithGroups)
		}
		if withPermission {
			opts = append(opts, project.LoadOptions.WithPermission)
		}
		if withKeys {
			opts = append(opts, project.LoadOptions.WithKeys)
		}
		if withWorkflows {
			opts = append(opts, project.LoadOptions.WithWorkflows)
		}
		if withWorkflowNames {
			opts = append(opts, project.LoadOptions.WithWorkflowNames)
		}
		if withPlatforms {
			opts = append(opts, project.LoadOptions.WithPlatforms)
		}

		p, errProj := project.Load(api.mustDB(), api.Cache, key, getUser(ctx), opts...)
		if errProj != nil {
			return sdk.WrapError(errProj, "getProjectHandler (%s)", key)
		}

		return WriteJSON(w, p, http.StatusOK)
	}
}

func (api *API) addProjectHandler() Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		//Unmarshal data
		p := &sdk.Project{}
		if err := UnmarshalBody(r, p); err != nil {
			return sdk.WrapError(err, "addProjectHandler> Unable to unmarshal body")
		}

		// check projectKey pattern
		if rgxp := regexp.MustCompile(sdk.ProjectKeyPattern); !rgxp.MatchString(p.Key) {
			return sdk.WrapError(sdk.ErrInvalidProjectKey, "addProjectHandler> Project key %s do not respect pattern %s")
		}

		//check project Name
		if p.Name == "" {
			return sdk.WrapError(sdk.ErrInvalidProjectName, "addProjectHandler> Project name must no be empty")
		}

		// Check that project does not already exists
		exist, errExist := project.Exist(api.mustDB(), p.Key)
		if errExist != nil {
			return sdk.WrapError(errExist, "addProjectHandler>  Cannot check if project %s exist", p.Key)
		}

		if exist {
			return sdk.WrapError(sdk.ErrConflict, "addProjectHandler> Project %s already exists", p.Key)
		}

		var groupAttached bool
		for i := range p.ProjectGroups {
			groupPermission := &p.ProjectGroups[i]
			if strings.TrimSpace(groupPermission.Group.Name) == "" {
				continue
			}
			// the default group could not be selected on ui 'Project Add'
			if !group.IsDefaultGroupID(groupPermission.Group.ID) {
				groupAttached = true
			}
		}
		if !groupAttached {
			// check if new auto group does not already exists
			if _, errl := group.LoadGroup(api.mustDB(), p.Name); errl != nil {
				if errl == sdk.ErrGroupNotFound {
					// group name does not exists, add it on project
					permG := sdk.GroupPermission{
						Group:      sdk.Group{Name: strings.Replace(p.Name, " ", "", -1)},
						Permission: permission.PermissionReadWriteExecute,
					}
					p.ProjectGroups = append(p.ProjectGroups, permG)
				} else {
					return sdk.WrapError(errl, "addProjectHandler> Cannot check if group already exists")
				}
			} else {
				return sdk.WrapError(sdk.ErrGroupPresent, "addProjectHandler> Group %s already exists", p.Name)
			}
		}

		//Create a project within a transaction
		tx, errBegin := api.mustDB().Begin()
		defer tx.Rollback()
		if errBegin != nil {
			return sdk.WrapError(errBegin, "addProjectHandler> Cannot start tx")
		}

		if err := project.Insert(tx, api.Cache, p, getUser(ctx)); err != nil {
			return sdk.WrapError(err, "addProjectHandler> Cannot insert project")
		}

		// Add group
		for i := range p.ProjectGroups {
			groupPermission := &p.ProjectGroups[i]

			// Insert group
			groupID, new, errGroup := group.AddGroup(tx, &groupPermission.Group)
			if groupID == 0 {
				return errGroup
			}
			groupPermission.Group.ID = groupID

			if group.IsDefaultGroupID(groupID) {
				groupPermission.Permission = permission.PermissionRead
			}

			// Add group on project
			if err := group.InsertGroupInProject(tx, p.ID, groupPermission.Group.ID, groupPermission.Permission); err != nil {
				return sdk.WrapError(err, "addProjectHandler> Cannot add group %s in project %s", groupPermission.Group.Name, p.Name)
			}

			// Add user in group
			if new {
				if err := group.InsertUserInGroup(tx, groupPermission.Group.ID, getUser(ctx).ID, true); err != nil {
					return sdk.WrapError(err, "addProjectHandler> Cannot add user %s in group %s", getUser(ctx).Username, groupPermission.Group.Name)
				}
			}
		}

		for _, v := range p.Variable {
			if errVar := project.InsertVariable(tx, p, &v, getUser(ctx)); errVar != nil {
				return sdk.WrapError(errVar, "addProjectHandler> Cannot add variable %s in project %s", v.Name, p.Name)
			}
		}

		for _, k := range p.Keys {
			k.ProjectID = p.ID
			switch k.Type {
			case sdk.KeyTypeSSH:
				keyTemp, errK := keys.GenerateSSHKey(k.Name)
				if errK != nil {
					return sdk.WrapError(errK, "addProjectHandler> Cannot generate ssh key for project %s", p.Name)
				}
				k.Key = keyTemp
			case sdk.KeyTypePGP:
				keyTemp, errK := keys.GeneratePGPKeyPair(k.Name)
				if errK != nil {
					return sdk.WrapError(errK, "addProjectHandler> Cannot generate pgp key for project %s", p.Name)
				}
				k.Key = keyTemp
			}
			if errK := project.InsertKey(tx, &k); errK != nil {
				return sdk.WrapError(errK, "addProjectHandler> Cannot add key %s in project %s", k.Name)
			}
		}

		if err := tx.Commit(); err != nil {
			return sdk.WrapError(err, "addProjectHandler> Cannot commit transaction")
		}

		event.PublishAddProject(p, getUser(ctx))

		return WriteJSON(w, p, http.StatusCreated)
	}
}

func (api *API) deleteProjectHandler() Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// Get project name in URL
		vars := mux.Vars(r)
		key := vars["permProjectKey"]

		p, errProj := project.Load(api.mustDB(), api.Cache, key, getUser(ctx), project.LoadOptions.WithPipelines, project.LoadOptions.WithApplications)
		if errProj != nil {
			if errProj != sdk.ErrNoProject {
				return sdk.WrapError(errProj, "deleteProject> load project '%s' from db", key)
			}
			return sdk.WrapError(errProj, "deleteProject> cannot load project %s", key)
		}

		if len(p.Pipelines) > 0 {
			return sdk.WrapError(sdk.ErrProjectHasPipeline, "deleteProject> Project '%s' still used by %d pipelines", key, len(p.Pipelines))
		}

		if len(p.Applications) > 0 {
			return sdk.WrapError(sdk.ErrProjectHasApplication, "deleteProject> Project '%s' still used by %d applications", key, len(p.Applications))
		}

		tx, errBegin := api.mustDB().Begin()
		if errBegin != nil {
			return sdk.WrapError(errBegin, "deleteProject> Cannot start transaction")
		}
		defer tx.Rollback()

		if err := project.Delete(tx, api.Cache, p.Key); err != nil {
			return sdk.WrapError(err, "deleteProject> cannot delete project %s", key)
		}
		if err := tx.Commit(); err != nil {
			return sdk.WrapError(err, "deleteProject> Cannot commit transaction")
		}

		event.PublishDeleteProject(p, getUser(ctx))

		log.Info("Project %s deleted.", p.Name)

		return nil
	}
}
<|endoftext|>


<commit_before>package engine

import (
	"archive/tar"
	"bytes"
	"fmt"
	"io"

	docker "github.com/fsouza/go-dockerclient"
)

// An Image is the result of a compile, a reference to a docker container
// containing the uploaded and compiled source code. It can be used to spawn
// multiple execution containers (internally this works via a shared read-only
// volume).
//
// Originally we did this by commiting a new docker image containing the
// compiled source, but that docker commit step takes over 1s on average, so now
// we use the shared volume approach.
type Image struct {
	ID     string
	lang   *Language
	client *docker.Client
}

func (image *Image) Run(opts *RunOptions) (result *ExecutionResult, err error) {
	filePath := fmt.Sprintf("/src/%s", image.lang.Filename)
	container, err := createContainer(image.client, docker.CreateContainerOptions{
		Config: &docker.Config{
			Image:           image.lang.DockerImage,
			Cmd:             []string{filePath},
			OpenStdin:       true,
			StdinOnce:       true,
			NetworkDisabled: true,
		},
		HostConfig: &docker.HostConfig{
			VolumesFrom: []string{image.ID + ":ro"},
		},
	})

	if err == nil {
		defer func() {
			go container.Remove()
		}()
		result = &ExecutionResult{}
		result, err = container.execute("runtime", &executionOptions{
			timeout:         opts.Timeout,
			stdin:           opts.Stdin,
			stdout:          opts.Stdout,
			stderr:          opts.Stderr,
			maxOutputSize:   opts.MaxOutputSize,
			apparmorProfile: image.lang.ApparmorProfile,
		})
	}

	return
}

// Remove will delete this image from docker, make sure this gets called so we
// don't end up keeping images around.
func (image *Image) Remove() {
	image.client.RemoveContainer(docker.RemoveContainerOptions{
		ID:            image.ID,
		RemoveVolumes: true,
		Force:         true,
	})
}

// Compile sets up a new Container and gets ready to execute the given source code.
// It's important to Remove the Container to clean up resources.
func (lang *Language) Compile(timeout int64, source string) (image *Image, result *ExecutionResult, err error) {
	filePath := fmt.Sprintf("/src/%s", lang.Filename)

	container, err := createContainer(lang.client, docker.CreateContainerOptions{
		Config: &docker.Config{
			Image:           lang.DockerImage,
			Cmd:             []string{"--build", filePath},
			OpenStdin:       true,
			StdinOnce:       true,
			NetworkDisabled: true,
			Volumes: map[string]struct{}{
				"/src": struct{}{},
			},
		},
	})

	if err == nil {
		err = lang.client.UploadToContainer(container.id, docker.UploadToContainerOptions{
			InputStream: lang.tarSource(source, filePath),
			Path:        "/",
		})
	}

	result = &ExecutionResult{}

	if err == nil && lang.compileStep {
		var stdout, stderr bytes.Buffer
		result, err = container.execute("compilation", &executionOptions{
			timeout:         timeout,
			stdout:          &stdout,
			stderr:          &stderr,
			maxOutputSize:   64 * 1024,
			apparmorProfile: lang.CompilerProfile,
		})
		result.Stdout = stdout.String()
		result.Stderr = stderr.String()
	}

	if err == nil && result.ExitCode == 0 {
		image = &Image{
			ID:     container.id,
			lang:   lang,
			client: lang.client,
		}
	}

	if image == nil {
		lang.client.RemoveContainer(docker.RemoveContainerOptions{ID: container.id, Force: true})
	}

	return
}

func (lang *Language) tarSource(source, filePath string) io.Reader {
	result := &bytes.Buffer{}
	writer := tar.NewWriter(result)
	writer.WriteHeader(&tar.Header{
		Name:     "/src",
		Mode:     0777,
		Typeflag: tar.TypeDir,
	})
	writer.WriteHeader(&tar.Header{
		Name: filePath,
		Mode: 0444,
		Size: int64(len(source)),
	})
	writer.Write([]byte(source))
	writer.Close()
	return result
}
<commit_msg>fix nil dereference error on createContainer failure<commit_after>package engine

import (
	"archive/tar"
	"bytes"
	"fmt"
	"io"

	docker "github.com/fsouza/go-dockerclient"
)

// An Image is the result of a compile, a reference to a docker container
// containing the uploaded and compiled source code. It can be used to spawn
// multiple execution containers (internally this works via a shared read-only
// volume).
//
// Originally we did this by commiting a new docker image containing the
// compiled source, but that docker commit step takes over 1s on average, so now
// we use the shared volume approach.
type Image struct {
	ID     string
	lang   *Language
	client *docker.Client
}

func (image *Image) Run(opts *RunOptions) (result *ExecutionResult, err error) {
	filePath := fmt.Sprintf("/src/%s", image.lang.Filename)
	container, err := createContainer(image.client, docker.CreateContainerOptions{
		Config: &docker.Config{
			Image:           image.lang.DockerImage,
			Cmd:             []string{filePath},
			OpenStdin:       true,
			StdinOnce:       true,
			NetworkDisabled: true,
		},
		HostConfig: &docker.HostConfig{
			VolumesFrom: []string{image.ID + ":ro"},
		},
	})

	if err == nil {
		defer func() {
			go container.Remove()
		}()
		result = &ExecutionResult{}
		result, err = container.execute("runtime", &executionOptions{
			timeout:         opts.Timeout,
			stdin:           opts.Stdin,
			stdout:          opts.Stdout,
			stderr:          opts.Stderr,
			maxOutputSize:   opts.MaxOutputSize,
			apparmorProfile: image.lang.ApparmorProfile,
		})
	}

	return
}

// Remove will delete this image from docker, make sure this gets called so we
// don't end up keeping images around.
func (image *Image) Remove() {
	image.client.RemoveContainer(docker.RemoveContainerOptions{
		ID:            image.ID,
		RemoveVolumes: true,
		Force:         true,
	})
}

// Compile sets up a new Container and gets ready to execute the given source code.
// It's important to Remove the Container to clean up resources.
func (lang *Language) Compile(timeout int64, source string) (image *Image, result *ExecutionResult, err error) {
	filePath := fmt.Sprintf("/src/%s", lang.Filename)

	container, err := createContainer(lang.client, docker.CreateContainerOptions{
		Config: &docker.Config{
			Image:           lang.DockerImage,
			Cmd:             []string{"--build", filePath},
			OpenStdin:       true,
			StdinOnce:       true,
			NetworkDisabled: true,
			Volumes: map[string]struct{}{
				"/src": struct{}{},
			},
		},
	})

	if err == nil {
		err = lang.client.UploadToContainer(container.id, docker.UploadToContainerOptions{
			InputStream: lang.tarSource(source, filePath),
			Path:        "/",
		})
	}

	result = &ExecutionResult{}

	if err == nil && lang.compileStep {
		var stdout, stderr bytes.Buffer
		result, err = container.execute("compilation", &executionOptions{
			timeout:         timeout,
			stdout:          &stdout,
			stderr:          &stderr,
			maxOutputSize:   64 * 1024,
			apparmorProfile: lang.CompilerProfile,
		})
		result.Stdout = stdout.String()
		result.Stderr = stderr.String()
	}

	if err == nil && result.ExitCode == 0 {
		image = &Image{
			ID:     container.id,
			lang:   lang,
			client: lang.client,
		}
	}

	if container != nil && image == nil {
		lang.client.RemoveContainer(docker.RemoveContainerOptions{ID: container.id, Force: true})
	}

	return
}

func (lang *Language) tarSource(source, filePath string) io.Reader {
	result := &bytes.Buffer{}
	writer := tar.NewWriter(result)
	writer.WriteHeader(&tar.Header{
		Name:     "/src",
		Mode:     0777,
		Typeflag: tar.TypeDir,
	})
	writer.WriteHeader(&tar.Header{
		Name: filePath,
		Mode: 0444,
		Size: int64(len(source)),
	})
	writer.Write([]byte(source))
	writer.Close()
	return result
}
<|endoftext|>