<commit_before>package s3api

import (
	"bytes"
	"context"
	"encoding/base64"
	"encoding/xml"
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/util"
	"github.com/gorilla/mux"
	"net/http"
	"net/url"
	"time"
)

type mimeType string

const (
	mimeNone mimeType = ""
	mimeJSON mimeType = "application/json"
	mimeXML  mimeType = "application/xml"
)

func setCommonHeaders(w http.ResponseWriter) {
	w.Header().Set("x-amz-request-id", fmt.Sprintf("%d", time.Now().UnixNano()))
	w.Header().Set("Accept-Ranges", "bytes")
}

// Encodes the response headers into XML format.
func encodeResponse(response interface{}) []byte {
	var bytesBuffer bytes.Buffer
	bytesBuffer.WriteString(xml.Header)
	e := xml.NewEncoder(&bytesBuffer)
	e.Encode(response)
	return bytesBuffer.Bytes()
}

func newContext(r *http.Request, api string) context.Context {
	vars := mux.Vars(r)
	return context.WithValue(context.Background(), "bucket", vars["bucket"])
}

func (s3a *S3ApiServer) withFilerClient(fn func(filer_pb.SeaweedFilerClient) error) error {

	grpcConnection, err := util.GrpcDial(s3a.option.FilerGrpcAddress)
	if err != nil {
		return fmt.Errorf("fail to dial %s: %v", s3a.option.FilerGrpcAddress, err)
	}
	defer grpcConnection.Close()

	client := filer_pb.NewSeaweedFilerClient(grpcConnection)

	return fn(client)
}

// If none of the http routes match respond with MethodNotAllowed
func notFoundHandler(w http.ResponseWriter, r *http.Request) {
	writeErrorResponse(w, ErrMethodNotAllowed, r.URL)
}

func writeErrorResponse(w http.ResponseWriter, errorCode ErrorCode, reqURL *url.URL) {
	apiError := getAPIError(errorCode)
	errorResponse := getRESTErrorResponse(apiError, reqURL.Path)
	encodedErrorResponse := encodeResponse(errorResponse)
	writeResponse(w, apiError.HTTPStatusCode, encodedErrorResponse, mimeXML)
}

func getRESTErrorResponse(err APIError, resource string) RESTErrorResponse {
	return RESTErrorResponse{
		Code:      err.Code,
		Message:   err.Description,
		Resource:  resource,
		RequestID: fmt.Sprintf("%d", time.Now().UnixNano()),
	}
}

func writeResponse(w http.ResponseWriter, statusCode int, response []byte, mType mimeType) {
	setCommonHeaders(w)
	if mType != mimeNone {
		w.Header().Set("Content-Type", string(mType))
	}
	w.WriteHeader(statusCode)
	if response != nil {
		w.Write(response)
		w.(http.Flusher).Flush()
	}
}

func writeSuccessResponseXML(w http.ResponseWriter, response []byte) {
	writeResponse(w, http.StatusOK, response, mimeXML)
}

func writeSuccessResponseEmpty(w http.ResponseWriter) {
	writeResponse(w, http.StatusOK, nil, mimeNone)
}

func validateContentMd5(h http.Header) ([]byte, error) {
	md5B64, ok := h["Content-Md5"]
	if ok {
		if md5B64[0] == "" {
			return nil, fmt.Errorf("Content-Md5 header set to empty value")
		}
		return base64.StdEncoding.DecodeString(md5B64[0])
	}
	return []byte{}, nil
}
<commit_msg>unused method<commit_after>package s3api

import (
	"bytes"
	"context"
	"encoding/base64"
	"encoding/xml"
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/util"
	"github.com/gorilla/mux"
	"net/http"
	"net/url"
	"time"
)

type mimeType string

const (
	mimeNone mimeType = ""
	mimeJSON mimeType = "application/json"
	mimeXML  mimeType = "application/xml"
)

func setCommonHeaders(w http.ResponseWriter) {
	w.Header().Set("x-amz-request-id", fmt.Sprintf("%d", time.Now().UnixNano()))
	w.Header().Set("Accept-Ranges", "bytes")
}

// Encodes the response headers into XML format.
func encodeResponse(response interface{}) []byte {
	var bytesBuffer bytes.Buffer
	bytesBuffer.WriteString(xml.Header)
	e := xml.NewEncoder(&bytesBuffer)
	e.Encode(response)
	return bytesBuffer.Bytes()
}

func (s3a *S3ApiServer) withFilerClient(fn func(filer_pb.SeaweedFilerClient) error) error {

	grpcConnection, err := util.GrpcDial(s3a.option.FilerGrpcAddress)
	if err != nil {
		return fmt.Errorf("fail to dial %s: %v", s3a.option.FilerGrpcAddress, err)
	}
	defer grpcConnection.Close()

	client := filer_pb.NewSeaweedFilerClient(grpcConnection)

	return fn(client)
}

// If none of the http routes match respond with MethodNotAllowed
func notFoundHandler(w http.ResponseWriter, r *http.Request) {
	writeErrorResponse(w, ErrMethodNotAllowed, r.URL)
}

func writeErrorResponse(w http.ResponseWriter, errorCode ErrorCode, reqURL *url.URL) {
	apiError := getAPIError(errorCode)
	errorResponse := getRESTErrorResponse(apiError, reqURL.Path)
	encodedErrorResponse := encodeResponse(errorResponse)
	writeResponse(w, apiError.HTTPStatusCode, encodedErrorResponse, mimeXML)
}

func getRESTErrorResponse(err APIError, resource string) RESTErrorResponse {
	return RESTErrorResponse{
		Code:      err.Code,
		Message:   err.Description,
		Resource:  resource,
		RequestID: fmt.Sprintf("%d", time.Now().UnixNano()),
	}
}

func writeResponse(w http.ResponseWriter, statusCode int, response []byte, mType mimeType) {
	setCommonHeaders(w)
	if mType != mimeNone {
		w.Header().Set("Content-Type", string(mType))
	}
	w.WriteHeader(statusCode)
	if response != nil {
		w.Write(response)
		w.(http.Flusher).Flush()
	}
}

func writeSuccessResponseXML(w http.ResponseWriter, response []byte) {
	writeResponse(w, http.StatusOK, response, mimeXML)
}

func writeSuccessResponseEmpty(w http.ResponseWriter) {
	writeResponse(w, http.StatusOK, nil, mimeNone)
}

func validateContentMd5(h http.Header) ([]byte, error) {
	md5B64, ok := h["Content-Md5"]
	if ok {
		if md5B64[0] == "" {
			return nil, fmt.Errorf("Content-Md5 header set to empty value")
		}
		return base64.StdEncoding.DecodeString(md5B64[0])
	}
	return []byte{}, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package mux

import (
	"net/http"
)

// 错误处理函数。
// msg为输出的错误信息，可能是任意类型的数据，一般为从recover()返回的数据。
type RecoverFunc func(w http.ResponseWriter, msg interface{})

// ErrorHandlerFunc的默认实现。
// 为一个简单的500错误信息。不会输出msg参数的内容。
func defaultRecoverFunc(w http.ResponseWriter, msg interface{}) {
	http.Error(w, http.StatusText(500), 500)
}

// 捕获并处理panic信息。
type recovery struct {
	handler     http.Handler
	recoverFunc RecoverFunc
}

// 声明一个错误处理的handler，h参数中发生的panic将被截获并处理，不会再向上级反映。
// 当h参数为空时，直接panic。
// rf参数用于指定处理panic信息的函数，其原型为RecoverFunc，
// 当将rf指定为nil时，将使用默认的处理函数，仅仅向客户端输出500的错误信息，没有具体内容。
func NewRecovery(h http.Handler, rf RecoverFunc) *recovery {
	if h == nil {
		panic("NewRecovery:参数h不能为空")
	}

	if rf == nil {
		rf = defaultRecoverFunc
	}

	return &recovery{
		handler:     h,
		recoverFunc: rf,
	}
}

// implement net/http.Handler.ServeHTTP(...)
func (r *recovery) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			r.recoverFunc(w, err)
		}
	}()

	r.handler.ServeHTTP(w, req)
}
<commit_msg>改变NewRecovery在传递错误参数时的默认行为<commit_after>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package mux

import (
	"fmt"
	"net/http"
	"os"
)

// 错误处理函数。
// msg为输出的错误信息，可能是任意类型的数据，一般为从recover()返回的数据。
type RecoverFunc func(w http.ResponseWriter, msg interface{})

// ErrorHandlerFunc的默认实现。
// 为一个简单的500错误信息。不会输出msg参数的内容。
func defaultRecoverFunc(w http.ResponseWriter, msg interface{}) {
	http.Error(w, http.StatusText(500), 500)
}

// 捕获并处理panic信息。
type recovery struct {
	handler     http.Handler
	recoverFunc RecoverFunc
}

// 声明一个错误处理的handler，h参数中发生的panic将被截获并处理，不会再向上级反映。
//
// 当h参数为空时，将直接退出程序。
// rf参数用于指定处理panic信息的函数，其原型为RecoverFunc，
// 当将rf指定为nil时，将使用默认的处理函数，仅仅向客户端输出500的错误信息，没有具体内容。
//
// Recovery应该处在所有http.Handler的最外层，用于处理所有没有被处理的panic。
func NewRecovery(h http.Handler, rf RecoverFunc) *recovery {
	if h == nil {
		fmt.Println("NewRecovery:参数h不能为空")
		os.Exit(2)
	}

	if rf == nil {
		rf = defaultRecoverFunc
	}

	return &recovery{
		handler:     h,
		recoverFunc: rf,
	}
}

// implement net/http.Handler.ServeHTTP(...)
func (r *recovery) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			r.recoverFunc(w, err)
		}
	}()

	r.handler.ServeHTTP(w, req)
}
<|endoftext|>


<commit_before>package negroni

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"runtime"
	"runtime/debug"
	"text/template"
)

const (
	// NoPrintStackBodyString is the body content returned when HTTP stack printing is suppressed
	NoPrintStackBodyString = "500 Internal Server Error"

	panicText = "PANIC: %s\n%s"
	panicHTML = `
<title>PANIC: {{.RecoveredPanic}}</title>

Negroni - PANIC

{{.RequestDescription}}

	Runtime error: {{.RecoveredPanic}}

{{ if .Stack }}

Runtime Stack

{{.StackAsString}}

{{ end }}

`
	nilRequestMessage = "Request is nil"
)

var panicHTMLTemplate = template.Must(template.New("PanicPage").Parse(panicHTML))

// PanicInformation contains all
// elements for printing stack informations.
type PanicInformation struct {
	RecoveredPanic interface{}
	Stack          []byte
	Request        *http.Request
}

// StackAsString returns a printable version of the stack
func (p *PanicInformation) StackAsString() string {
	return string(p.Stack)
}

// RequestDescription returns a printable description of the url
func (p *PanicInformation) RequestDescription() string {

	if p.Request == nil {
		return nilRequestMessage
	}

	var queryOutput string
	if p.Request.URL.RawQuery != "" {
		queryOutput = "?" + p.Request.URL.RawQuery
	}
	return fmt.Sprintf("%s %s%s", p.Request.Method, p.Request.URL.Path, queryOutput)
}

// PanicFormatter is an interface on object can implement
// to be able to output the stack trace
type PanicFormatter interface {
	// FormatPanicError output the stack for a given answer/response.
	// In case the the middleware should not output the stack trace,
	// the field `Stack` of the passed `PanicInformation` instance equals `[]byte{}`.
	FormatPanicError(rw http.ResponseWriter, r *http.Request, infos *PanicInformation)
}

// TextPanicFormatter output the stack
// as simple text on os.Stdout. If no `Content-Type` is set,
// it will output the data as `text/plain; charset=utf-8`.
// Otherwise, the origin `Content-Type` is kept.
type TextPanicFormatter struct{}

func (t *TextPanicFormatter) FormatPanicError(rw http.ResponseWriter, r *http.Request, infos *PanicInformation) {
	if rw.Header().Get("Content-Type") == "" {
		rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
	}
	fmt.Fprintf(rw, panicText, infos.RecoveredPanic, infos.Stack)
}

// HTMLPanicFormatter output the stack inside
// an HTML page. This has been largely inspired by
// https://github.com/go-martini/martini/pull/156/commits.
type HTMLPanicFormatter struct{}

func (t *HTMLPanicFormatter) FormatPanicError(rw http.ResponseWriter, r *http.Request, infos *PanicInformation) {
	if rw.Header().Get("Content-Type") == "" {
		rw.Header().Set("Content-Type", "text/html; charset=utf-8")
	}
	panicHTMLTemplate.Execute(rw, infos)
}

// Recovery is a Negroni middleware that recovers from any panics and writes a 500 if there was one.
type Recovery struct {
	Logger           ALogger
	PrintStack       bool
	LogStack         bool
	PanicHandlerFunc func(*PanicInformation)
	StackAll         bool
	StackSize        int
	Formatter        PanicFormatter

	// Deprecated: Use PanicHandlerFunc instead to receive panic
	// error with additional information (see PanicInformation)
	ErrorHandlerFunc func(interface{})
}

// NewRecovery returns a new instance of Recovery
func NewRecovery() *Recovery {
	return &Recovery{
		Logger:     log.New(os.Stdout, "[negroni] ", 0),
		PrintStack: true,
		LogStack:   true,
		StackAll:   false,
		StackSize:  1024 * 8,
		Formatter:  &TextPanicFormatter{},
	}
}

func (rec *Recovery) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	defer func() {
		if err := recover(); err != nil {
			rw.WriteHeader(http.StatusInternalServerError)

			stack := make([]byte, rec.StackSize)
			stack = stack[:runtime.Stack(stack, rec.StackAll)]
			infos := &PanicInformation{RecoveredPanic: err, Request: r}

			// PrintStack will write stack trace info to the ResponseWriter if set to true!
			// If set to false it will respond with the standard response documented here https://httpstat.us/500
			if rec.PrintStack {
				infos.Stack = stack
				if rec.Formatter != nil {
					rec.Formatter.FormatPanicError(rw, r, infos)
				}
			} else {
				if rw.Header().Get("Content-Type") == "" {
					rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
				}
				fmt.Fprint(rw, NoPrintStackBodyString)
			}

			if rec.LogStack {
				rec.Logger.Printf(panicText, err, stack)
			}

			if rec.ErrorHandlerFunc != nil {
				func() {
					defer func() {
						if err := recover(); err != nil {
							rec.Logger.Printf("provided ErrorHandlerFunc panic'd: %s, trace:\n%s", err, debug.Stack())
							rec.Logger.Printf("%s\n", debug.Stack())
						}
					}()
					rec.ErrorHandlerFunc(err)
				}()
			}
			if rec.PanicHandlerFunc != nil {
				func() {
					defer func() {
						if err := recover(); err != nil {
							rec.Logger.Printf("provided PanicHandlerFunc panic'd: %s, trace:\n%s", err, debug.Stack())
							rec.Logger.Printf("%s\n", debug.Stack())
						}
					}()
					rec.PanicHandlerFunc(infos)
				}()
			}
		}
	}()

	next(rw, r)
}
<commit_msg>Set stack in recovery handler even if not printing it<commit_after>package negroni

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"runtime"
	"runtime/debug"
	"text/template"
)

const (
	// NoPrintStackBodyString is the body content returned when HTTP stack printing is suppressed
	NoPrintStackBodyString = "500 Internal Server Error"

	panicText = "PANIC: %s\n%s"
	panicHTML = `
<title>PANIC: {{.RecoveredPanic}}</title>

Negroni - PANIC

{{.RequestDescription}}

	Runtime error: {{.RecoveredPanic}}

{{ if .Stack }}

Runtime Stack

{{.StackAsString}}

{{ end }}

`
	nilRequestMessage = "Request is nil"
)

var panicHTMLTemplate = template.Must(template.New("PanicPage").Parse(panicHTML))

// PanicInformation contains all
// elements for printing stack informations.
type PanicInformation struct {
	RecoveredPanic interface{}
	Stack          []byte
	Request        *http.Request
}

// StackAsString returns a printable version of the stack
func (p *PanicInformation) StackAsString() string {
	return string(p.Stack)
}

// RequestDescription returns a printable description of the url
func (p *PanicInformation) RequestDescription() string {

	if p.Request == nil {
		return nilRequestMessage
	}

	var queryOutput string
	if p.Request.URL.RawQuery != "" {
		queryOutput = "?" + p.Request.URL.RawQuery
	}
	return fmt.Sprintf("%s %s%s", p.Request.Method, p.Request.URL.Path, queryOutput)
}

// PanicFormatter is an interface on object can implement
// to be able to output the stack trace
type PanicFormatter interface {
	// FormatPanicError output the stack for a given answer/response.
	// In case the the middleware should not output the stack trace,
	// the field `Stack` of the passed `PanicInformation` instance equals `[]byte{}`.
	FormatPanicError(rw http.ResponseWriter, r *http.Request, infos *PanicInformation)
}

// TextPanicFormatter output the stack
// as simple text on os.Stdout. If no `Content-Type` is set,
// it will output the data as `text/plain; charset=utf-8`.
// Otherwise, the origin `Content-Type` is kept.
type TextPanicFormatter struct{}

func (t *TextPanicFormatter) FormatPanicError(rw http.ResponseWriter, r *http.Request, infos *PanicInformation) {
	if rw.Header().Get("Content-Type") == "" {
		rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
	}
	fmt.Fprintf(rw, panicText, infos.RecoveredPanic, infos.Stack)
}

// HTMLPanicFormatter output the stack inside
// an HTML page. This has been largely inspired by
// https://github.com/go-martini/martini/pull/156/commits.
type HTMLPanicFormatter struct{}

func (t *HTMLPanicFormatter) FormatPanicError(rw http.ResponseWriter, r *http.Request, infos *PanicInformation) {
	if rw.Header().Get("Content-Type") == "" {
		rw.Header().Set("Content-Type", "text/html; charset=utf-8")
	}
	panicHTMLTemplate.Execute(rw, infos)
}

// Recovery is a Negroni middleware that recovers from any panics and writes a 500 if there was one.
type Recovery struct {
	Logger           ALogger
	PrintStack       bool
	LogStack         bool
	PanicHandlerFunc func(*PanicInformation)
	StackAll         bool
	StackSize        int
	Formatter        PanicFormatter

	// Deprecated: Use PanicHandlerFunc instead to receive panic
	// error with additional information (see PanicInformation)
	ErrorHandlerFunc func(interface{})
}

// NewRecovery returns a new instance of Recovery
func NewRecovery() *Recovery {
	return &Recovery{
		Logger:     log.New(os.Stdout, "[negroni] ", 0),
		PrintStack: true,
		LogStack:   true,
		StackAll:   false,
		StackSize:  1024 * 8,
		Formatter:  &TextPanicFormatter{},
	}
}

func (rec *Recovery) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	defer func() {
		if err := recover(); err != nil {
			rw.WriteHeader(http.StatusInternalServerError)

			infos := &PanicInformation{
				RecoveredPanic: err,
				Request:        r,
				Stack:          make([]byte, rec.StackSize),
			}
			infos.Stack = infos.Stack[:runtime.Stack(infos.Stack, rec.StackAll)]

			// PrintStack will write stack trace info to the ResponseWriter if set to true!
			// If set to false it will respond with the standard response documented here https://httpstat.us/500
			if rec.PrintStack && rec.Formatter != nil {
				rec.Formatter.FormatPanicError(rw, r, infos)
			} else {
				if rw.Header().Get("Content-Type") == "" {
					rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
				}
				fmt.Fprint(rw, NoPrintStackBodyString)
			}

			if rec.LogStack {
				rec.Logger.Printf(panicText, err, infos.Stack)
			}

			if rec.ErrorHandlerFunc != nil {
				func() {
					defer func() {
						if err := recover(); err != nil {
							rec.Logger.Printf("provided ErrorHandlerFunc panic'd: %s, trace:\n%s", err, debug.Stack())
							rec.Logger.Printf("%s\n", debug.Stack())
						}
					}()
					rec.ErrorHandlerFunc(err)
				}()
			}
			if rec.PanicHandlerFunc != nil {
				func() {
					defer func() {
						if err := recover(); err != nil {
							rec.Logger.Printf("provided PanicHandlerFunc panic'd: %s, trace:\n%s", err, debug.Stack())
							rec.Logger.Printf("%s\n", debug.Stack())
						}
					}()
					rec.PanicHandlerFunc(infos)
				}()
			}
		}
	}()

	next(rw, r)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/tutumcloud/go-tutum/tutum"
	"log"
	"syscall"
	"net/http"
	"time"
	"fmt"
	"strconv"
	"strings"
	"os"
)

func main_hide() {
	err := redeployService("tuerauf-prod", "tuerauf-haproxy")
	if err != nil {
		log.Fatal(err)
		syscall.Exit(1);
	}
}

func redeployService(serviceNameToRedeploy string, haproxyServiceName string) error {
	// tutum User, ApiKey should be set in environment TUTUM_USER, TUTUM_APIKEY

	serviceList, err := tutum.ListServices()
	if err != nil {
		return err
	}

	redeployedServiceUuid, err := getServiceUuid(serviceNameToRedeploy, serviceList.Objects)
	if err != nil {
		return err
	}
	haproxyServiceUuid, err := getServiceUuid(haproxyServiceName, serviceList.Objects)
	if err != nil {
		return err
	}
	haproxyContainerUuid, err := getServicesFirstContainerUuid(haproxyServiceUuid)
	if err != nil {
		return err
	}

	redeployedService, err := tutum.GetService(redeployedServiceUuid)
	if err != nil {
		return err
	}

	log.Printf("redeploying service %s (%s) via haproxy %s (%s)",
		redeployedService.Name, redeployedServiceUuid, haproxyServiceName, haproxyServiceUuid)

	for i := range redeployedService.Containers {
		err = doRedeployContainer(redeployedService.Containers[i], redeployedService, haproxyContainerUuid)
		if err != nil {
			return err
		}
		time.Sleep(25 * time.Second)
	}

	// future improvement: do final check via haproxy

	log.Printf("successfully redeployed service %s (%s) via haproxy %s (%s)",
		redeployedService.Name, redeployedServiceUuid, haproxyServiceName, haproxyServiceUuid)

	return nil
}

func getServiceUuid(serviceName string, serviceArray []tutum.Service) (string, error) {

	for i := range serviceArray {
		var service = serviceArray[i];
		if service.Name == serviceName {
			return service.Uuid, nil
		}
	}
	return "", fmt.Errorf("Service %s not found", serviceName)
}

func getServicesFirstContainerUuid(serviceUuid string) (string, error) {
	service, err := tutum.GetService(serviceUuid)
	if err != nil {
		return "", err
	}
	return getUuidFromUri(service.Containers[0]), nil // ??? URI or Uuid?
}

func getUuidFromUri(uri string) string {
	return uri; // no conversion seems necessary
}

func getEnvFromContainer(envName string, container tutum.Container) (string, error) {
	for i := range container.Container_envvars {
		env := container.Container_envvars[i];
		if env.Key == envName {
			return env.Value, nil
		}
	}
	return "", fmt.Errorf("Env %s not found in Container %s", envName, container.Name)
}

func doRedeployContainer(containerUri string, parentService tutum.Service, haproxyContainerUuid string) error {
	container, err := tutum.GetContainer(getUuidFromUri(containerUri))
	if err != nil {
		return err
	}
	httpCheckDef, err := getEnvFromContainer("HTTP_CHECK", container)
	if err != nil {
		return err
	}
	parts := strings.Split(httpCheckDef, " ")
	if len(parts) < 2 || parts[0] != "GET" {
		return fmt.Errorf("container %s (%s) has bad HTTP_CHECK '%s' (not implemented)",
			container.Name, containerUri, httpCheckDef)
	}
	httpCheckUri := parts[1];

	log.Printf("redeploying container %s (%s)", container.Name, container.Uuid)

	err = container.Redeploy(tutum.ReuseVolumesOption{Reuse: true})
	if err != nil {
		return err
	}

	newContainer, err := waitForContainerToTerminateAndReappear(container, parentService)
	if err != nil {
		return err
	}

	err = waitForCheckOk(httpCheckUri, newContainer, 10*60)
	if err != nil {
		return err
	}
	log.Printf("container %s (%s) checked ok", newContainer.Name, newContainer.Uuid)
	return nil
}

func waitForCheckOk(httpCheckUri string, container tutum.Container, timeoutSeconds int64) error {
	log.Printf("waitForCheckOk")
	startedAt := getTimestampSeconds()

	hostIp := os.Getenv("SEQREDEPLOY_HOSTIP")
	if hostIp == "" {
		hostIp = container.Private_ip
	}
	tcpPort := container.Container_ports[0].Outer_port
	fullUrl := "http://" + hostIp + ":" + strconv.Itoa(tcpPort) + httpCheckUri

	for {
		log.Printf("container %s (%s): calling %s", container.Name, container.Uuid, fullUrl)
		response, err := http.Get(fullUrl)
		if err != nil {
			log.Print(err)
		} else {
			if response.StatusCode >= 200 && response.StatusCode < 300 {
				return nil
			}
			if getTimestampSeconds() - startedAt >= timeoutSeconds {
				return fmt.Errorf("container %s (%s): timeout calling %s", container.Name, container.Uuid, fullUrl)
			}
		}
		time.Sleep(5 * time.Second)
	}
}

func getTimestampSeconds() int64 {
	return time.Now().UnixNano() / int64(time.Second)
}

func waitForContainerToTerminateAndReappear(container tutum.Container,
											parentService tutum.Service) (tutum.Container, error) {
	log.Printf("waitForContainerToTerminateAndReappear")
	eventChan := make(chan tutum.Event)
	errChan := make(chan error)

	go tutum.TutumEvents(eventChan, errChan)

	didTerminate := false
	var newContainer tutum.Container
	var err error

	for {
		select {
		case event := <-eventChan:
			// log.Println(event)
			if event.Type == "container" &&
				strings.Contains(event.Resource_uri, container.Uuid) &&
				event.State == "Terminated"	{

				log.Printf("got container Terminated event on my container")
				didTerminate = true
			}
			if event.Type == "container" &&
				parentsContain(event.Parents, parentService.Uuid) &&
				event.State == "Running" &&
				didTerminate == true {

				newContainer, err = tutum.GetContainer(getUuidFromUri(event.Resource_uri))
				if err != nil {
					return newContainer, err
				}
				log.Printf("got container Running event on new container (%s)", newContainer.Uuid)
				return newContainer, nil
			}
		case err := <-errChan:
			return newContainer, err
		}
	}
}

func parentsContain(parents []string, lookedFor string) bool {
	for i := range parents {
		if strings.Contains(parents[i], lookedFor) {
			return true
		}
	}
	return false
}

// func hashArraySearchForObjectContainingKeyValue(hashArray )
<commit_msg>Use container.Inner_port if SEQREDEPLOY_HOSTIP is empty<commit_after>package main

import (
	"github.com/tutumcloud/go-tutum/tutum"
	"log"
	"syscall"
	"net/http"
	"time"
	"fmt"
	"strconv"
	"strings"
	"os"
)

func main_hide() {
	err := redeployService("tuerauf-prod", "tuerauf-haproxy")
	if err != nil {
		log.Fatal(err)
		syscall.Exit(1);
	}
}

func redeployService(serviceNameToRedeploy string, haproxyServiceName string) error {
	// tutum User, ApiKey should be set in environment TUTUM_USER, TUTUM_APIKEY

	serviceList, err := tutum.ListServices()
	if err != nil {
		return err
	}

	redeployedServiceUuid, err := getServiceUuid(serviceNameToRedeploy, serviceList.Objects)
	if err != nil {
		return err
	}
	haproxyServiceUuid, err := getServiceUuid(haproxyServiceName, serviceList.Objects)
	if err != nil {
		return err
	}
	haproxyContainerUuid, err := getServicesFirstContainerUuid(haproxyServiceUuid)
	if err != nil {
		return err
	}

	redeployedService, err := tutum.GetService(redeployedServiceUuid)
	if err != nil {
		return err
	}

	log.Printf("redeploying service %s (%s) via haproxy %s (%s)",
		redeployedService.Name, redeployedServiceUuid, haproxyServiceName, haproxyServiceUuid)

	for i := range redeployedService.Containers {
		err = doRedeployContainer(redeployedService.Containers[i], redeployedService, haproxyContainerUuid)
		if err != nil {
			return err
		}
		time.Sleep(25 * time.Second)
	}

	// future improvement: do final check via haproxy

	log.Printf("successfully redeployed service %s (%s) via haproxy %s (%s)",
		redeployedService.Name, redeployedServiceUuid, haproxyServiceName, haproxyServiceUuid)

	return nil
}

func getServiceUuid(serviceName string, serviceArray []tutum.Service) (string, error) {

	for i := range serviceArray {
		var service = serviceArray[i];
		if service.Name == serviceName {
			return service.Uuid, nil
		}
	}
	return "", fmt.Errorf("Service %s not found", serviceName)
}

func getServicesFirstContainerUuid(serviceUuid string) (string, error) {
	service, err := tutum.GetService(serviceUuid)
	if err != nil {
		return "", err
	}
	return getUuidFromUri(service.Containers[0]), nil // ??? URI or Uuid?
}

func getUuidFromUri(uri string) string {
	return uri; // no conversion seems necessary
}

func getEnvFromContainer(envName string, container tutum.Container) (string, error) {
	for i := range container.Container_envvars {
		env := container.Container_envvars[i];
		if env.Key == envName {
			return env.Value, nil
		}
	}
	return "", fmt.Errorf("Env %s not found in Container %s", envName, container.Name)
}

func doRedeployContainer(containerUri string, parentService tutum.Service, haproxyContainerUuid string) error {
	container, err := tutum.GetContainer(getUuidFromUri(containerUri))
	if err != nil {
		return err
	}
	httpCheckDef, err := getEnvFromContainer("HTTP_CHECK", container)
	if err != nil {
		return err
	}
	parts := strings.Split(httpCheckDef, " ")
	if len(parts) < 2 || parts[0] != "GET" {
		return fmt.Errorf("container %s (%s) has bad HTTP_CHECK '%s' (not implemented)",
			container.Name, containerUri, httpCheckDef)
	}
	httpCheckUri := parts[1];

	log.Printf("redeploying container %s (%s)", container.Name, container.Uuid)

	err = container.Redeploy(tutum.ReuseVolumesOption{Reuse: true})
	if err != nil {
		return err
	}

	newContainer, err := waitForContainerToTerminateAndReappear(container, parentService)
	if err != nil {
		return err
	}

	err = waitForCheckOk(httpCheckUri, newContainer, 10*60)
	if err != nil {
		return err
	}
	log.Printf("container %s (%s) checked ok", newContainer.Name, newContainer.Uuid)
	return nil
}

func waitForCheckOk(httpCheckUri string, container tutum.Container, timeoutSeconds int64) error {
	log.Printf("waitForCheckOk")
	startedAt := getTimestampSeconds()

	tcpPort := container.Container_ports[0].Outer_port
	hostIp := os.Getenv("SEQREDEPLOY_HOSTIP")
	if hostIp == "" {
		hostIp = container.Private_ip
		tcpPort = container.Container_ports[0].Inner_port
	}
	fullUrl := "http://" + hostIp + ":" + strconv.Itoa(tcpPort) + httpCheckUri

	for {
		log.Printf("container %s (%s): calling %s", container.Name, container.Uuid, fullUrl)
		response, err := http.Get(fullUrl)
		if err != nil {
			log.Print(err)
		} else {
			if response.StatusCode >= 200 && response.StatusCode < 300 {
				return nil
			}
			if getTimestampSeconds() - startedAt >= timeoutSeconds {
				return fmt.Errorf("container %s (%s): timeout calling %s", container.Name, container.Uuid, fullUrl)
			}
		}
		time.Sleep(5 * time.Second)
	}
}

func getTimestampSeconds() int64 {
	return time.Now().UnixNano() / int64(time.Second)
}

func waitForContainerToTerminateAndReappear(container tutum.Container,
											parentService tutum.Service) (tutum.Container, error) {
	log.Printf("waitForContainerToTerminateAndReappear")
	eventChan := make(chan tutum.Event)
	errChan := make(chan error)

	go tutum.TutumEvents(eventChan, errChan)

	didTerminate := false
	var newContainer tutum.Container
	var err error

	for {
		select {
		case event := <-eventChan:
			// log.Println(event)
			if event.Type == "container" &&
				strings.Contains(event.Resource_uri, container.Uuid) &&
				event.State == "Terminated"	{

				log.Printf("got container Terminated event on my container")
				didTerminate = true
			}
			if event.Type == "container" &&
				parentsContain(event.Parents, parentService.Uuid) &&
				event.State == "Running" &&
				didTerminate == true {

				newContainer, err = tutum.GetContainer(getUuidFromUri(event.Resource_uri))
				if err != nil {
					return newContainer, err
				}
				log.Printf("got container Running event on new container (%s)", newContainer.Uuid)
				return newContainer, nil
			}
		case err := <-errChan:
			return newContainer, err
		}
	}
}

func parentsContain(parents []string, lookedFor string) bool {
	for i := range parents {
		if strings.Contains(parents[i], lookedFor) {
			return true
		}
	}
	return false
}

// func hashArraySearchForObjectContainingKeyValue(hashArray )
<|endoftext|>


<commit_before>/*
    MP3Cat is a fast command line utility for concatenating MP3 files
    without re-encoding. It supports both constant bit rate (CBR) and
    variable bit rate (VBR) files.
*/
package main

import (
    "fmt"
    "io"
    "os"
    "path/filepath"
    "golang.org/x/crypto/ssh/terminal"
    "github.com/dmulholland/mp3lib"
    "github.com/dmulholland/clio/go/clio"
)

const version = "2.4.1"

var helptext = fmt.Sprintf(`
Usage: %s [FLAGS] [OPTIONS] ARGUMENTS

  Concatenates MP3 files without re-encoding. Supports both constant bit rate
  (CBR) and variable bit rate (VBR) files. Strips ID3 tags and garbage data
  from the output.

Arguments:
  <files>           List of input files to merge.

Options:
  -o, --out <file>  Output filename. Defaults to 'output.mp3'.

Flags:
  -f, --force       Overwrite an existing output file.
      --help        Display this help text and exit.
  -t, --tag         Copy the ID3 tag from the first input file.
  -v, --verbose     Report progress.
      --version     Display the application's version number and exit.
`, filepath.Base(os.Args[0]))

func main() {

    // Parse the command line arguments.
    parser := clio.NewParser(helptext, version)
    parser.AddFlag("force f")
    parser.AddFlag("verbose v")
    parser.AddFlag("debug d")
    parser.AddFlag("tag t")
    parser.AddStr("out o", "output.mp3")
    parser.Parse()

    // Make sure the user has supplied a list of input files.
    if !parser.HasArgs() {
        fmt.Fprintln(
            os.Stderr,
            "Error: you must supply a list of files to merge.")
        os.Exit(1)
    }

    // Set debug mode if the user supplied a --debug flag.
    if parser.GetFlag("debug") {
        mp3lib.DebugMode = true
    }

    // Merge the input files.
    merge(
        parser.GetStr("out"),
        parser.GetArgs(),
        parser.GetFlag("force"),
        parser.GetFlag("verbose"),
        parser.GetFlag("tag"))
}

// Create a new file at the specified output path containing the merged
// contents of the list of input files.
func merge(outpath string, inpaths []string, force, verbose, tag bool) {

    var totalFrames uint32
    var totalBytes uint32
    var totalFiles int
    var firstBitRate int
    var isVBR bool

    // Only overwrite an existing file if the --force flag has been used.
    if _, err := os.Stat(outpath); err == nil {
        if !force {
            fmt.Fprintf(
                os.Stderr,
                "Error: the file '%v' already exists.\n", outpath)
            os.Exit(1)
        }
    }

    // If the list of input files includes the output file we'll end up in an
    // infinite loop.
    for _, filepath := range inpaths {
        if filepath == outpath {
            fmt.Fprintln(
                os.Stderr,
                "Error: the list of input files includes the output file.")
            os.Exit(1)
        }
    }

    // Create the output file.
    outfile, err := os.Create(outpath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    if verbose {
        line()
    }

    // Loop over the input files and append their MP3 frames to the output
    // file.
    for _, inpath := range inpaths {

        if verbose {
            fmt.Println("+", inpath)
        }

        infile, err := os.Open(inpath)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        isFirstFrame := true

        for {

            // Read the next frame from the input file.
            frame := mp3lib.NextFrame(infile)
            if frame == nil {
                break
            }

            // Skip the first frame if it's a VBR header.
            if isFirstFrame {
                isFirstFrame = false
                if mp3lib.IsXingHeader(frame) || mp3lib.IsVbriHeader(frame) {
                    continue
                }
            }

            // If we detect more than one bitrate we'll need to add a VBR
            // header to the output file.
            if firstBitRate == 0 {
                firstBitRate = frame.BitRate
            } else if frame.BitRate != firstBitRate {
                isVBR = true
            }

            // Write the frame to the output file.
            _, err := outfile.Write(frame.RawBytes)
            if err != nil {
                fmt.Fprintln(os.Stderr, err)
                os.Exit(1)
            }

            totalFrames += 1
            totalBytes += uint32(len(frame.RawBytes))
        }

        infile.Close()
        totalFiles += 1
    }

    outfile.Close()
    if verbose {
        line()
    }

    // If we detected multiple bitrates, prepend a VBR header to the file.
    if isVBR {
        if verbose {
            fmt.Println("• Multiple bitrates detected. Adding VBR header.")
        }
        addXingHeader(outpath, totalFrames, totalBytes)
    }

    // Copy the ID3v2 tag from the first input file if requested. Order of
    // operations is important here. The ID3 tag must be the first item in
    // the file - in particular, it must come *before* any VBR header.
    if tag {
        if verbose {
            fmt.Println("• Adding ID3 tag.")
        }
        addID3v2Tag(outpath, inpaths[0])
    }

    // Print a count of the number of files merged.
    if verbose {
        fmt.Printf("• %v files merged.\n", totalFiles)
        line()
    }
}

// Prepend an Xing VBR header to the specified MP3 file.
func addXingHeader(filepath string, totalFrames, totalBytes uint32) {

    outputFile, err := os.Create(filepath + ".mp3cat.tmp")
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    inputFile, err := os.Open(filepath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    templateFrame := mp3lib.NextFrame(inputFile)
    inputFile.Seek(0, 0)

    xingHeader := mp3lib.NewXingHeader(templateFrame, totalFrames, totalBytes)

    _, err = outputFile.Write(xingHeader.RawBytes)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    _, err = io.Copy(outputFile, inputFile)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    outputFile.Close()
    inputFile.Close()

    err = os.Remove(filepath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    err = os.Rename(filepath + ".mp3cat.tmp", filepath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}

// Prepend an ID3v2 tag to the MP3 file at mp3Path, copying from tagPath.
func addID3v2Tag(mp3Path, tagPath string) {

    tagFile, err := os.Open(tagPath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    id3tag := mp3lib.NextID3v2Tag(tagFile)
    tagFile.Close()

    if id3tag != nil {
        outputFile, err := os.Create(mp3Path + ".mp3cat.tmp")
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        inputFile, err := os.Open(mp3Path)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        _, err = outputFile.Write(id3tag.RawBytes)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        _, err = io.Copy(outputFile, inputFile)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        outputFile.Close()
        inputFile.Close()

        err = os.Remove(mp3Path)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        err = os.Rename(mp3Path + ".mp3cat.tmp", mp3Path)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
    }
}

// Print a line to stdout if we're running in a terminal.
func line() {
    if terminal.IsTerminal(int(os.Stdout.Fd())) {
        width, _, err := terminal.GetSize(int(os.Stdout.Fd()))
        if err == nil {
            for i := 0; i < width; i++ {
                fmt.Print("─")
            }
            fmt.Println()
        }
    }
}
<commit_msg>Add support for a --dir <path> option<commit_after>/*
    MP3Cat is a fast command line utility for concatenating MP3 files
    without re-encoding. It supports both constant bit rate (CBR) and
    variable bit rate (VBR) files.
*/
package main

import (
    "fmt"
    "io"
    "os"
    "path"
    "path/filepath"
    "golang.org/x/crypto/ssh/terminal"
    "github.com/dmulholland/mp3lib"
    "github.com/dmulholland/clio/go/clio"
)

const version = "2.5.0.dev"

var helptext = fmt.Sprintf(`
Usage: %s [FLAGS] [OPTIONS] [ARGUMENTS]

  This tool concatenates MP3 files without re-encoding. It supports both
  constant bit rate (CBR) and variable bit rate (VBR) MP3 files. It also
  strips ID3 tags and garbage data from the output.

  Files to be merged can be specified as a list of filenames:

    $ mp3cat one.mp3 two.mp3 three.mp3

  Alternatively, an entire directory of files can be merged:

    $ mp3cat --dir /path/to/directory/

Arguments:
  [files]             List of input files to merge.

Options:
  -d, --dir <path>    Directory of files to merge.
  -o, --out <path>    Output filename. Defaults to 'output.mp3'.

Flags:
  -f, --force         Overwrite an existing output file.
      --help          Display this help text and exit.
  -t, --tag           Copy the ID3 tag from the first input file.
  -v, --verbose       Report progress.
      --version       Display the application's version number and exit.
`, filepath.Base(os.Args[0]))

func main() {

    // Parse the command line arguments.
    parser := clio.NewParser(helptext, version)
    parser.AddFlag("force f")
    parser.AddFlag("verbose v")
    parser.AddFlag("debug")
    parser.AddFlag("tag t")
    parser.AddStr("out o", "output.mp3")
    parser.AddStr("dir d", "")
    parser.Parse()

    // Make sure we have files to merge.
    var files []string
    if parser.GetStr("dir") != "" {
        globs, err := filepath.Glob(path.Join(parser.GetStr("dir"), "*.mp3"))
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
        if globs == nil || len(globs) == 0 {
            fmt.Fprintln(os.Stderr, "Error: no files found.")
            os.Exit(1)
        }
        files = globs
    } else if parser.HasArgs() {
        files = parser.GetArgs()
    } else {
        fmt.Fprintln(os.Stderr, "Error: you must specify files to merge.")
        os.Exit(1)
    }

    // Set debug mode if the user supplied a --debug flag.
    if parser.GetFlag("debug") {
        mp3lib.DebugMode = true
    }

    // Merge the input files.
    merge(
        parser.GetStr("out"),
        files,
        parser.GetFlag("force"),
        parser.GetFlag("verbose"),
        parser.GetFlag("tag"))
}

// Create a new file at the specified output path containing the merged
// contents of the list of input files.
func merge(outpath string, inpaths []string, force, verbose, tag bool) {

    var totalFrames uint32
    var totalBytes uint32
    var totalFiles int
    var firstBitRate int
    var isVBR bool

    // Only overwrite an existing file if the --force flag has been used.
    if _, err := os.Stat(outpath); err == nil {
        if !force {
            fmt.Fprintf(
                os.Stderr,
                "Error: the file '%v' already exists.\n", outpath)
            os.Exit(1)
        }
    }

    // If the list of input files includes the output file we'll end up in an
    // infinite loop.
    for _, filepath := range inpaths {
        if filepath == outpath {
            fmt.Fprintln(
                os.Stderr,
                "Error: the list of input files includes the output file.")
            os.Exit(1)
        }
    }

    // Create the output file.
    outfile, err := os.Create(outpath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    if verbose {
        line()
    }

    // Loop over the input files and append their MP3 frames to the output
    // file.
    for _, inpath := range inpaths {

        if verbose {
            fmt.Println("+", inpath)
        }

        infile, err := os.Open(inpath)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        isFirstFrame := true

        for {

            // Read the next frame from the input file.
            frame := mp3lib.NextFrame(infile)
            if frame == nil {
                break
            }

            // Skip the first frame if it's a VBR header.
            if isFirstFrame {
                isFirstFrame = false
                if mp3lib.IsXingHeader(frame) || mp3lib.IsVbriHeader(frame) {
                    continue
                }
            }

            // If we detect more than one bitrate we'll need to add a VBR
            // header to the output file.
            if firstBitRate == 0 {
                firstBitRate = frame.BitRate
            } else if frame.BitRate != firstBitRate {
                isVBR = true
            }

            // Write the frame to the output file.
            _, err := outfile.Write(frame.RawBytes)
            if err != nil {
                fmt.Fprintln(os.Stderr, err)
                os.Exit(1)
            }

            totalFrames += 1
            totalBytes += uint32(len(frame.RawBytes))
        }

        infile.Close()
        totalFiles += 1
    }

    outfile.Close()
    if verbose {
        line()
    }

    // If we detected multiple bitrates, prepend a VBR header to the file.
    if isVBR {
        if verbose {
            fmt.Println("• Multiple bitrates detected. Adding VBR header.")
        }
        addXingHeader(outpath, totalFrames, totalBytes)
    }

    // Copy the ID3v2 tag from the first input file if requested. Order of
    // operations is important here. The ID3 tag must be the first item in
    // the file - in particular, it must come *before* any VBR header.
    if tag {
        if verbose {
            fmt.Println("• Adding ID3 tag.")
        }
        addID3v2Tag(outpath, inpaths[0])
    }

    // Print a count of the number of files merged.
    if verbose {
        fmt.Printf("• %v files merged.\n", totalFiles)
        line()
    }
}

// Prepend an Xing VBR header to the specified MP3 file.
func addXingHeader(filepath string, totalFrames, totalBytes uint32) {

    outputFile, err := os.Create(filepath + ".mp3cat.tmp")
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    inputFile, err := os.Open(filepath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    templateFrame := mp3lib.NextFrame(inputFile)
    inputFile.Seek(0, 0)

    xingHeader := mp3lib.NewXingHeader(templateFrame, totalFrames, totalBytes)

    _, err = outputFile.Write(xingHeader.RawBytes)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    _, err = io.Copy(outputFile, inputFile)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    outputFile.Close()
    inputFile.Close()

    err = os.Remove(filepath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    err = os.Rename(filepath + ".mp3cat.tmp", filepath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}

// Prepend an ID3v2 tag to the MP3 file at mp3Path, copying from tagPath.
func addID3v2Tag(mp3Path, tagPath string) {

    tagFile, err := os.Open(tagPath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    id3tag := mp3lib.NextID3v2Tag(tagFile)
    tagFile.Close()

    if id3tag != nil {
        outputFile, err := os.Create(mp3Path + ".mp3cat.tmp")
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        inputFile, err := os.Open(mp3Path)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        _, err = outputFile.Write(id3tag.RawBytes)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        _, err = io.Copy(outputFile, inputFile)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        outputFile.Close()
        inputFile.Close()

        err = os.Remove(mp3Path)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        err = os.Rename(mp3Path + ".mp3cat.tmp", mp3Path)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
    }
}

// Print a line to stdout if we're running in a terminal.
func line() {
    if terminal.IsTerminal(int(os.Stdout.Fd())) {
        width, _, err := terminal.GetSize(int(os.Stdout.Fd()))
        if err == nil {
            for i := 0; i < width; i++ {
                fmt.Print("─")
            }
            fmt.Println()
        }
    }
}
<|endoftext|>


<commit_before>package flag

import (
	"go/ast"
	"reflect"
	"strings"
	"unicode"
)

type register struct {
}

var defaultReguster register

func (r register) addIndexes(indexes map[string]int, keys []string, index int) {
	for _, key := range keys {
		indexes[key] = index
	}
}

func (r register) findDuplicates(parent, set *FlagSet, names []string) []string {
	var duplicates []string
	for _, name := range names {
		if set.isFlagOrSubset(name) || (parent != nil && parent.isFlagOrSubset(name)) {
			duplicates = append(duplicates, name)
			continue
		}
		for i := range set.subsets {
			if set.subsets[i].isFlagOrSubset(name) {
				duplicates = append(duplicates, name)
				break
			}
		}
	}
	return duplicates
}

const (
	flagNameSeparatorForSplit = ","
	flagNameSeparatorForJoin  = ", "
)

func (r register) joinFlagNames(names []string) string {
	return strings.Join(names, flagNameSeparatorForJoin)
}

func (r register) cleanFlagNames(names string) ([]string, string) {
	ns := splitAndTrimSpace(names, flagNameSeparatorForSplit)
	return ns, r.joinFlagNames(ns)
}

func (r register) cleanFlag(flag *Flag) {
	if flag.ValSep == "" {
		flag.ValSep = ","
	}
	r.updateFlagDesc(flag, flag.Desc)
	r.updateFlagVersion(flag, flag.Version)
}

func (r register) registerFlag(parent, set *FlagSet, flag Flag) error {
	refval := reflect.ValueOf(flag.Ptr)
	if refval.Kind() != reflect.Ptr {
		return newErrorf(errNonPointer, "illegal flag pointer: %s", flag.Names)
	}
	if typeName(flag.Ptr) == "" {
		return newErrorf(errInvalidType, "unsupported flag type: %s", flag.Names)
	}
	if flag.Default != nil {
		var compatible bool

		refdef := reflect.ValueOf(flag.Default)
		if isSlicePtr(flag.Ptr) {
			compatible = refdef.Kind() == reflect.Slice
			compatible = compatible && isKindCompatible(sliceElemKind(refval.Elem()), sliceElemKind(refdef))
		} else {
			compatible = isKindCompatible(refval.Elem().Kind(), refdef.Kind())
		}
		if !compatible {
			return newErrorf(errInvalidType, "incompatible default value type: %s", flag.Names)
		}
	}
	if flag.Selects != nil {
		var err error
		flag.Selects, err = parseSelectsValue(flag.Ptr, flag.Selects)
		if err != nil {
			return newErrorf(errInvalidSelects, "%s: %s", flag.Names, err.Error())
		}
	}

	ns, names := r.cleanFlagNames(flag.Names)
	if duplicates := r.findDuplicates(parent, set, ns); len(duplicates) > 0 {
		return newErrorf(errDuplicateFlagRegister, "duplicate flags with parent/self/childs: %v", duplicates)
	}

	flag.Names = names
	r.cleanFlag(&flag)

	set.flags = append(set.flags, flag)
	r.addIndexes(set.flagIndexes, ns, len(set.flags)-1)
	return nil
}

func (r register) checkSubsetValid(flag *Flag) error {
	if flag.Names == "" {
		return newErrorf(errInvalidNames, "subset names should not be empty")
	}
	return nil
}

func (r register) registerSet(parent, set *FlagSet, flag Flag) (*FlagSet, error) {
	var ns []string

	ns, flag.Names = r.cleanFlagNames(flag.Names)
	err := r.checkSubsetValid(&flag)
	if err != nil {
		return nil, err
	}

	if duplicates := r.findDuplicates(parent, set, ns); len(duplicates) > 0 {
		return nil, newErrorf(errDuplicateFlagRegister, "duplicate subset name: %v", duplicates)
	}

	child := newFlagSet(flag)
	child.self.Default = false
	child.errorHandling = set.errorHandling

	set.subsets = append(set.subsets, *child)
	r.addIndexes(set.subsetIndexes, ns, len(set.subsets)-1)
	return &set.subsets[len(set.subsets)-1], nil
}

func (r register) registerStructure(parent, set *FlagSet, st interface{}) error {
	const (
		tagNames     = "names"
		tagArglist   = "arglist"
		tagUsage     = "usage"
		tagDesc      = "desc"
		tagVersion   = "version"
		tagImportant = "important"

		tagEnv      = "env"
		tagValsep   = "valsep"
		tagDefault  = "default"
		tagSelects  = "selects"
		tagExpand   = "expand"
		tagArgs     = "args"
		tagShowType = "showType"

		fieldSubsetEnable = "Enable"
		fieldArgs         = "Args"
	)

	refval := reflect.ValueOf(st)
	if refval.Kind() != reflect.Ptr || refval.Elem().Kind() != reflect.Struct {
		return newErrorf(errNonPointer, "not pointer of structure")
	}
	refval = refval.Elem()
	reftyp := refval.Type()
	numfield := refval.NumField()
	for i := 0; i < numfield; i++ {
		fieldType := reftyp.Field(i)
		if !ast.IsExported(fieldType.Name) {
			continue
		}

		fieldVal := refval.Field(i)

		args := fieldType.Tag.Get(tagArgs)
		isArgs, err := parseBool(args, "false")
		if err != nil {
			return newErrorf(errInvalidValue, "non-bool tag args value: %s.%s %s", set.self.Names, fieldType.Name, args)
		}
		if fieldType.Name == fieldArgs || isArgs {
			if set.self.ArgsPtr != nil {
				return newErrorf(errDuplicateFlagRegister, "duplicate args field: %s", set.self.Names)
			}
			if _, ok := fieldVal.Interface().([]string); !ok {
				return newErrorf(errInvalidType, "invalid %s:Args field type, expect []string", set.self.Names)
			}
			set.self.ArgsPtr = fieldVal.Addr().Interface().(*[]string)
			continue
		}

		ptr := fieldVal.Addr().Interface()
		if fieldType.Name == fieldSubsetEnable {
			if fieldType.Type.Kind() != reflect.Bool {
				return newErrorf(errInvalidType, "illegal type of field '%s', expect bool", fieldSubsetEnable)
			}
			if set.self.Ptr == nil {
				set.self.Ptr = ptr
			}
			continue
		}

		var (
			names     = fieldType.Tag.Get(tagNames)
			usage     = fieldType.Tag.Get(tagUsage)
			desc      = fieldType.Tag.Get(tagDesc)
			version   = fieldType.Tag.Get(tagVersion)
			arglist   = fieldType.Tag.Get(tagArglist)
			important = fieldType.Tag.Get(tagImportant)
		)
		if names == "-" {
			continue
		}
		_, ok := ptr.(NoFlag)
		if ok {
			continue
		}

		importantVal, err := parseBool(important, "false")
		if err != nil {
			return newErrorf(errInvalidValue, "invalid tag import value: %s.%s %s", set.self.Names, fieldType.Name, important)
		}
		if fieldVal.Kind() != reflect.Struct {
			var (
				env      = fieldType.Tag.Get(tagEnv)
				def      = fieldType.Tag.Get(tagDefault)
				valsep   = fieldType.Tag.Get(tagValsep)
				selects  = fieldType.Tag.Get(tagSelects)
				showType = fieldType.Tag.Get(tagShowType)
			)
			if names == "" {
				names = "-" + unexportedName(fieldType.Name)
			}
			if valsep == "" {
				valsep = ","
			}
			if typeName(ptr) == "" {
				continue
			}
			defVal, err := parseDefault(def, valsep, ptr)
			if err != nil {
				return err
			}
			selectsVal, err := parseSelectsString(selects, valsep, ptr)
			if err != nil {
				return err
			}
			showTypeVal, err := parseBool(showType, "false")
			if err != nil {
				return newErrorf(errInvalidValue, "invalid tag showType value: %s.%s %s", set.self.Names, fieldType.Name, showType)
			}
			err = r.registerFlag(parent, set, Flag{
				Names:     names,
				Arglist:   arglist,
				Usage:     usage,
				Desc:      desc,
				Version:   version,
				Important: importantVal,
				ShowType:  showTypeVal,

				Ptr:     ptr,
				Env:     env,
				ValSep:  valsep,
				Default: defVal,
				Selects: selectsVal,
			})
			if err != nil {
				return err
			}
		} else {
			expand := fieldType.Tag.Get(tagExpand)
			if names == "" {
				names = unexportedName(fieldType.Name)
			}
			expandVal, err := parseBool(expand, "false")
			if err != nil {
				return newErrorf(errInvalidValue, "parse expand value %s as bool failed", expand)
			}
			child, err := r.registerSet(parent, set, Flag{
				Names:     names,
				Arglist:   arglist,
				Usage:     usage,
				Desc:      desc,
				Version:   version,
				Important: importantVal,
				Expand:    expandVal,
			})
			if err != nil {
				return err
			}
			err = r.registerStructure(set, child, fieldVal.Addr().Interface())
			if err != nil {
				return err
			}
		}
	}
	if md, ok := st.(Metadata); ok {
		for children, meta := range md.Metadata() {
			err := r.updateMeta(set, children, meta)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func (r register) registerBoolFlags(parent, set *FlagSet, names []string, usage string) (bool, error) {
	if len(names) == 0 {
		return false, nil
	}
	if duplicates := r.findDuplicates(parent, set, names); len(duplicates) > 0 {
		return false, nil
	}
	var value bool
	err := r.registerFlag(parent, set, Flag{
		Ptr:   &value,
		Names: strings.Join(names, ","),
		Usage: usage,
	})
	return err == nil, err
}

func (r register) registerHelpFlags(parent, set *FlagSet) error {
	registered, err := r.registerBoolFlags(parent, set, []string{"-h", "--help"}, "show help")
	if err == nil && registered {
		_, err = r.registerBoolFlags(parent, set, []string{"-v", "--verbose"}, "show verbose help")
	}
	return err
}

func (r register) boolFlagVal(set *FlagSet, flag string) (val, has bool) {
	index, has := set.flagIndexes[flag]
	if !has {
		return false, false
	}
	return *set.flags[index].Ptr.(*bool), true
}

func (r register) helpFlagValues(set *FlagSet) (show, verbose bool) {
	var has bool
	show, has = r.boolFlagVal(set, "-h")
	if show {
		if has {
			verbose, _ = r.boolFlagVal(set, "-v")
		}
	}
	return
}

func (r register) prefixSpaceCount(s string) int {
	var c int
	for _, r := range s {
		if unicode.IsSpace(r) {
			c++
		} else {
			break
		}
	}
	return c
}

func (r register) splitLines(line string) []string {
	var (
		lines    = strings.Split(line, "\n")
		begin    = -1
		end      = -1
		minSpace = -1
	)
	for i := range lines {
		if strings.TrimSpace(lines[i]) != "" {
			if begin < 0 {
				begin = i
			}
			end = i + 1
			c := r.prefixSpaceCount(lines[i])
			if minSpace > c || minSpace < 0 {
				minSpace = c
			}
		}
	}
	if begin < 0 {
		return nil
	}
	lines = lines[begin:end]
	for i := range lines {
		line := lines[i]
		if len(line) >= minSpace {
			line = line[minSpace:]
		}
		lines[i] = line
	}
	return lines
}

func (r register) updateFlagDesc(flag *Flag, desc string) {
	flag.Desc = desc
	flag.descLines = r.splitLines(flag.Desc)
}

func (r register) updateFlagVersion(flag *Flag, version string) {
	flag.Version = version
	flag.versionLines = r.splitLines(flag.Version)
}

func (r register) searchChildrenFlag(set *FlagSet, children string) (*Flag, *FlagSet, error) {
	var (
		currSet  = set
		currFlag *Flag

		sections = splitAndTrimSpace(children, flagNameSeparatorForSplit)
		last     = len(sections) - 1
	)
	for i, sec := range sections {
		index, has := currSet.subsetIndexes[sec]
		if has {
			currSet = &currSet.subsets[index]
			continue
		}
		if i != last {
			return nil, nil, newErrorf(errFlagNotFound, "subset %s is not found", sec)
		}
		index, has = currSet.flagIndexes[sec]
		if !has {
			return nil, nil, newErrorf(errFlagNotFound, "subset or flag %s is not found", sec)
		}
		currFlag = &currSet.flags[index]
	}
	if currFlag == nil {
		currFlag = &currSet.self
	}
	return currFlag, currSet, nil
}

func (r register) updateMeta(set *FlagSet, children string, meta Flag) error {
	flag, subset, err := r.searchChildrenFlag(set, children)
	if err != nil {
		return err
	}
	if meta.Desc != "" {
		flag.Desc = meta.Desc
	}
	if subset != nil && meta.Version != "" {
		flag.Version = meta.Version
	}
	if meta.Arglist != "" {
		flag.Arglist = meta.Arglist
	}
	if meta.Usage != "" {
		flag.Usage = meta.Usage
	}
	r.cleanFlag(flag)
	return nil
}

func (r register) findSubset(set *FlagSet, children string) (*FlagSet, error) {
	_, subset, err := r.searchChildrenFlag(set, children)
	return subset, err
}
<commit_msg>no subset, no '-verbose'.<commit_after>package flag

import (
	"go/ast"
	"reflect"
	"strings"
	"unicode"
)

type register struct {
}

var defaultReguster register

func (r register) addIndexes(indexes map[string]int, keys []string, index int) {
	for _, key := range keys {
		indexes[key] = index
	}
}

func (r register) findDuplicates(parent, set *FlagSet, names []string) []string {
	var duplicates []string
	for _, name := range names {
		if set.isFlagOrSubset(name) || (parent != nil && parent.isFlagOrSubset(name)) {
			duplicates = append(duplicates, name)
			continue
		}
		for i := range set.subsets {
			if set.subsets[i].isFlagOrSubset(name) {
				duplicates = append(duplicates, name)
				break
			}
		}
	}
	return duplicates
}

const (
	flagNameSeparatorForSplit = ","
	flagNameSeparatorForJoin  = ", "
)

func (r register) joinFlagNames(names []string) string {
	return strings.Join(names, flagNameSeparatorForJoin)
}

func (r register) cleanFlagNames(names string) ([]string, string) {
	ns := splitAndTrimSpace(names, flagNameSeparatorForSplit)
	return ns, r.joinFlagNames(ns)
}

func (r register) cleanFlag(flag *Flag) {
	if flag.ValSep == "" {
		flag.ValSep = ","
	}
	r.updateFlagDesc(flag, flag.Desc)
	r.updateFlagVersion(flag, flag.Version)
}

func (r register) registerFlag(parent, set *FlagSet, flag Flag) error {
	refval := reflect.ValueOf(flag.Ptr)
	if refval.Kind() != reflect.Ptr {
		return newErrorf(errNonPointer, "illegal flag pointer: %s", flag.Names)
	}
	if typeName(flag.Ptr) == "" {
		return newErrorf(errInvalidType, "unsupported flag type: %s", flag.Names)
	}
	if flag.Default != nil {
		var compatible bool

		refdef := reflect.ValueOf(flag.Default)
		if isSlicePtr(flag.Ptr) {
			compatible = refdef.Kind() == reflect.Slice
			compatible = compatible && isKindCompatible(sliceElemKind(refval.Elem()), sliceElemKind(refdef))
		} else {
			compatible = isKindCompatible(refval.Elem().Kind(), refdef.Kind())
		}
		if !compatible {
			return newErrorf(errInvalidType, "incompatible default value type: %s", flag.Names)
		}
	}
	if flag.Selects != nil {
		var err error
		flag.Selects, err = parseSelectsValue(flag.Ptr, flag.Selects)
		if err != nil {
			return newErrorf(errInvalidSelects, "%s: %s", flag.Names, err.Error())
		}
	}

	ns, names := r.cleanFlagNames(flag.Names)
	if duplicates := r.findDuplicates(parent, set, ns); len(duplicates) > 0 {
		return newErrorf(errDuplicateFlagRegister, "duplicate flags with parent/self/childs: %v", duplicates)
	}

	flag.Names = names
	r.cleanFlag(&flag)

	set.flags = append(set.flags, flag)
	r.addIndexes(set.flagIndexes, ns, len(set.flags)-1)
	return nil
}

func (r register) checkSubsetValid(flag *Flag) error {
	if flag.Names == "" {
		return newErrorf(errInvalidNames, "subset names should not be empty")
	}
	return nil
}

func (r register) registerSet(parent, set *FlagSet, flag Flag) (*FlagSet, error) {
	var ns []string

	ns, flag.Names = r.cleanFlagNames(flag.Names)
	err := r.checkSubsetValid(&flag)
	if err != nil {
		return nil, err
	}

	if duplicates := r.findDuplicates(parent, set, ns); len(duplicates) > 0 {
		return nil, newErrorf(errDuplicateFlagRegister, "duplicate subset name: %v", duplicates)
	}

	child := newFlagSet(flag)
	child.self.Default = false
	child.errorHandling = set.errorHandling

	set.subsets = append(set.subsets, *child)
	r.addIndexes(set.subsetIndexes, ns, len(set.subsets)-1)
	return &set.subsets[len(set.subsets)-1], nil
}

func (r register) registerStructure(parent, set *FlagSet, st interface{}) error {
	const (
		tagNames     = "names"
		tagArglist   = "arglist"
		tagUsage     = "usage"
		tagDesc      = "desc"
		tagVersion   = "version"
		tagImportant = "important"

		tagEnv      = "env"
		tagValsep   = "valsep"
		tagDefault  = "default"
		tagSelects  = "selects"
		tagExpand   = "expand"
		tagArgs     = "args"
		tagShowType = "showType"

		fieldSubsetEnable = "Enable"
		fieldArgs         = "Args"
	)

	refval := reflect.ValueOf(st)
	if refval.Kind() != reflect.Ptr || refval.Elem().Kind() != reflect.Struct {
		return newErrorf(errNonPointer, "not pointer of structure")
	}
	refval = refval.Elem()
	reftyp := refval.Type()
	numfield := refval.NumField()
	for i := 0; i < numfield; i++ {
		fieldType := reftyp.Field(i)
		if !ast.IsExported(fieldType.Name) {
			continue
		}

		fieldVal := refval.Field(i)

		args := fieldType.Tag.Get(tagArgs)
		isArgs, err := parseBool(args, "false")
		if err != nil {
			return newErrorf(errInvalidValue, "non-bool tag args value: %s.%s %s", set.self.Names, fieldType.Name, args)
		}
		if fieldType.Name == fieldArgs || isArgs {
			if set.self.ArgsPtr != nil {
				return newErrorf(errDuplicateFlagRegister, "duplicate args field: %s", set.self.Names)
			}
			if _, ok := fieldVal.Interface().([]string); !ok {
				return newErrorf(errInvalidType, "invalid %s:Args field type, expect []string", set.self.Names)
			}
			set.self.ArgsPtr = fieldVal.Addr().Interface().(*[]string)
			continue
		}

		ptr := fieldVal.Addr().Interface()
		if fieldType.Name == fieldSubsetEnable {
			if fieldType.Type.Kind() != reflect.Bool {
				return newErrorf(errInvalidType, "illegal type of field '%s', expect bool", fieldSubsetEnable)
			}
			if set.self.Ptr == nil {
				set.self.Ptr = ptr
			}
			continue
		}

		var (
			names     = fieldType.Tag.Get(tagNames)
			usage     = fieldType.Tag.Get(tagUsage)
			desc      = fieldType.Tag.Get(tagDesc)
			version   = fieldType.Tag.Get(tagVersion)
			arglist   = fieldType.Tag.Get(tagArglist)
			important = fieldType.Tag.Get(tagImportant)
		)
		if names == "-" {
			continue
		}
		_, ok := ptr.(NoFlag)
		if ok {
			continue
		}

		importantVal, err := parseBool(important, "false")
		if err != nil {
			return newErrorf(errInvalidValue, "invalid tag import value: %s.%s %s", set.self.Names, fieldType.Name, important)
		}
		if fieldVal.Kind() != reflect.Struct {
			var (
				env      = fieldType.Tag.Get(tagEnv)
				def      = fieldType.Tag.Get(tagDefault)
				valsep   = fieldType.Tag.Get(tagValsep)
				selects  = fieldType.Tag.Get(tagSelects)
				showType = fieldType.Tag.Get(tagShowType)
			)
			if names == "" {
				names = "-" + unexportedName(fieldType.Name)
			}
			if valsep == "" {
				valsep = ","
			}
			if typeName(ptr) == "" {
				continue
			}
			defVal, err := parseDefault(def, valsep, ptr)
			if err != nil {
				return err
			}
			selectsVal, err := parseSelectsString(selects, valsep, ptr)
			if err != nil {
				return err
			}
			showTypeVal, err := parseBool(showType, "false")
			if err != nil {
				return newErrorf(errInvalidValue, "invalid tag showType value: %s.%s %s", set.self.Names, fieldType.Name, showType)
			}
			err = r.registerFlag(parent, set, Flag{
				Names:     names,
				Arglist:   arglist,
				Usage:     usage,
				Desc:      desc,
				Version:   version,
				Important: importantVal,
				ShowType:  showTypeVal,

				Ptr:     ptr,
				Env:     env,
				ValSep:  valsep,
				Default: defVal,
				Selects: selectsVal,
			})
			if err != nil {
				return err
			}
		} else {
			expand := fieldType.Tag.Get(tagExpand)
			if names == "" {
				names = unexportedName(fieldType.Name)
			}
			expandVal, err := parseBool(expand, "false")
			if err != nil {
				return newErrorf(errInvalidValue, "parse expand value %s as bool failed", expand)
			}
			child, err := r.registerSet(parent, set, Flag{
				Names:     names,
				Arglist:   arglist,
				Usage:     usage,
				Desc:      desc,
				Version:   version,
				Important: importantVal,
				Expand:    expandVal,
			})
			if err != nil {
				return err
			}
			err = r.registerStructure(set, child, fieldVal.Addr().Interface())
			if err != nil {
				return err
			}
		}
	}
	if md, ok := st.(Metadata); ok {
		for children, meta := range md.Metadata() {
			err := r.updateMeta(set, children, meta)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func (r register) registerBoolFlags(parent, set *FlagSet, names []string, usage string) (bool, error) {
	if len(names) == 0 {
		return false, nil
	}
	if duplicates := r.findDuplicates(parent, set, names); len(duplicates) > 0 {
		return false, nil
	}
	var value bool
	err := r.registerFlag(parent, set, Flag{
		Ptr:   &value,
		Names: strings.Join(names, ","),
		Usage: usage,
	})
	return err == nil, err
}

func (r register) registerHelpFlags(parent, set *FlagSet) error {
	registered, err := r.registerBoolFlags(parent, set, []string{"-h", "--help"}, "show help")
	if err == nil && registered && len(set.subsets) > 0 {
		_, err = r.registerBoolFlags(parent, set, []string{"-v", "--verbose"}, "show verbose help")
	}
	return err
}

func (r register) boolFlagVal(set *FlagSet, flag string) (val, has bool) {
	index, has := set.flagIndexes[flag]
	if !has {
		return false, false
	}
	return *set.flags[index].Ptr.(*bool), true
}

func (r register) helpFlagValues(set *FlagSet) (show, verbose bool) {
	var has bool
	show, has = r.boolFlagVal(set, "-h")
	if show {
		if has {
			verbose, _ = r.boolFlagVal(set, "-v")
		}
	}
	return
}

func (r register) prefixSpaceCount(s string) int {
	var c int
	for _, r := range s {
		if unicode.IsSpace(r) {
			c++
		} else {
			break
		}
	}
	return c
}

func (r register) splitLines(line string) []string {
	var (
		lines    = strings.Split(line, "\n")
		begin    = -1
		end      = -1
		minSpace = -1
	)
	for i := range lines {
		if strings.TrimSpace(lines[i]) != "" {
			if begin < 0 {
				begin = i
			}
			end = i + 1
			c := r.prefixSpaceCount(lines[i])
			if minSpace > c || minSpace < 0 {
				minSpace = c
			}
		}
	}
	if begin < 0 {
		return nil
	}
	lines = lines[begin:end]
	for i := range lines {
		line := lines[i]
		if len(line) >= minSpace {
			line = line[minSpace:]
		}
		lines[i] = line
	}
	return lines
}

func (r register) updateFlagDesc(flag *Flag, desc string) {
	flag.Desc = desc
	flag.descLines = r.splitLines(flag.Desc)
}

func (r register) updateFlagVersion(flag *Flag, version string) {
	flag.Version = version
	flag.versionLines = r.splitLines(flag.Version)
}

func (r register) searchChildrenFlag(set *FlagSet, children string) (*Flag, *FlagSet, error) {
	var (
		currSet  = set
		currFlag *Flag

		sections = splitAndTrimSpace(children, flagNameSeparatorForSplit)
		last     = len(sections) - 1
	)
	for i, sec := range sections {
		index, has := currSet.subsetIndexes[sec]
		if has {
			currSet = &currSet.subsets[index]
			continue
		}
		if i != last {
			return nil, nil, newErrorf(errFlagNotFound, "subset %s is not found", sec)
		}
		index, has = currSet.flagIndexes[sec]
		if !has {
			return nil, nil, newErrorf(errFlagNotFound, "subset or flag %s is not found", sec)
		}
		currFlag = &currSet.flags[index]
	}
	if currFlag == nil {
		currFlag = &currSet.self
	}
	return currFlag, currSet, nil
}

func (r register) updateMeta(set *FlagSet, children string, meta Flag) error {
	flag, subset, err := r.searchChildrenFlag(set, children)
	if err != nil {
		return err
	}
	if meta.Desc != "" {
		flag.Desc = meta.Desc
	}
	if subset != nil && meta.Version != "" {
		flag.Version = meta.Version
	}
	if meta.Arglist != "" {
		flag.Arglist = meta.Arglist
	}
	if meta.Usage != "" {
		flag.Usage = meta.Usage
	}
	r.cleanFlag(flag)
	return nil
}

func (r register) findSubset(set *FlagSet, children string) (*FlagSet, error) {
	_, subset, err := r.searchChildrenFlag(set, children)
	return subset, err
}
<|endoftext|>


<commit_before>package builder

import "reflect"

var registry = make(map[reflect.Type]reflect.Type)

// RegisterType maps the given builderType to a structType.
// This mapping affects the type of slices returned by Get and is required for
// GetStruct to work.
//
// Returns a Value containing an empty instance of the registered builderType.
//
// RegisterType will panic if builderType's underlying type is not Builder or
// if structType's Kind is not Struct.
func RegisterType(builderType reflect.Type, structType reflect.Type) *reflect.Value {
	structType.NumField() // Panic if structType is not a struct
	registry[builderType] = structType
	emptyValue := emptyBuilderValue.Convert(builderType)
	return &emptyValue
}

// Register wraps RegisterType, taking instances instead of Types.
//
// Returns an empty instance of the registered builder type which can be used
// as the initial value for builder expressions. See example.
func Register(builderProto interface{}, structProto interface{}) interface{} {
	empty := RegisterType(
		reflect.TypeOf(builderProto),
		reflect.TypeOf(structProto),
	).Interface()
	return empty
}

func getBuilderStructType(builderType reflect.Type) *reflect.Type {
	structType, ok := registry[builderType]
	if !ok {
		return nil
	}
	return &structType
}

func newBuilderStruct(builderType reflect.Type) *reflect.Value {
	structType := getBuilderStructType(builderType)
	if structType == nil {
		return nil
	}
	newStruct := reflect.New(*structType).Elem()
	return &newStruct
}
<commit_msg>Wrap registry map access in RWMutex<commit_after>package builder

import (
	"reflect"
	"sync"
)

var (
	registry = make(map[reflect.Type]reflect.Type)
	registryMux sync.RWMutex
)

// RegisterType maps the given builderType to a structType.
// This mapping affects the type of slices returned by Get and is required for
// GetStruct to work.
//
// Returns a Value containing an empty instance of the registered builderType.
//
// RegisterType will panic if builderType's underlying type is not Builder or
// if structType's Kind is not Struct.
func RegisterType(builderType reflect.Type, structType reflect.Type) *reflect.Value {
	registryMux.Lock()
	defer registryMux.Unlock()
	structType.NumField() // Panic if structType is not a struct
	registry[builderType] = structType
	emptyValue := emptyBuilderValue.Convert(builderType)
	return &emptyValue
}

// Register wraps RegisterType, taking instances instead of Types.
//
// Returns an empty instance of the registered builder type which can be used
// as the initial value for builder expressions. See example.
func Register(builderProto, structProto interface{}) interface{} {
	empty := RegisterType(
		reflect.TypeOf(builderProto),
		reflect.TypeOf(structProto),
	).Interface()
	return empty
}

func getBuilderStructType(builderType reflect.Type) *reflect.Type {
	registryMux.RLock()
	defer registryMux.RUnlock()
	structType, ok := registry[builderType]
	if !ok {
		return nil
	}
	return &structType
}

func newBuilderStruct(builderType reflect.Type) *reflect.Value {
	structType := getBuilderStructType(builderType)
	if structType == nil {
		return nil
	}
	newStruct := reflect.New(*structType).Elem()
	return &newStruct
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"io/ioutil"
	"strings"
	"time"
)

var serverId string

func validateServerId(s string) error {
	invalid := errors.New("Invalid server id: " + s)
	switch {
	case len(s) == 0:
		return invalid
	case s[0] == '/' || s[0] == '@':
		return invalid
	}
	return nil
}

func initServerId() error {
	bytes, err := ioutil.ReadFile(*guidFile)
	if err == nil {
		serverId = strings.TrimSpace(string(bytes))
	} else {
		serverId = time.Now().UTC().Format(time.RFC3339Nano)
		err = ioutil.WriteFile(*guidFile,
			[]byte(serverId), 0666)
	}
	return err
}
<commit_msg>Allow users to override the node name.<commit_after>package main

import (
	"errors"
	"flag"
	"io/ioutil"
	"strings"
	"time"
)

var serverId string

func init() {
	flag.StringVar(&serverId, "nodeID", "",
		"Node ID (defaults to what's stored in guid file or arbitrary)")
}

func validateServerId(s string) error {
	invalid := errors.New("Invalid server id: " + s)
	switch {
	case len(s) == 0:
		return invalid
	case s[0] == '/' || s[0] == '@':
		return invalid
	}
	return nil
}

func initServerId() error {
	var err error
	var bytes []byte
	if serverId == "" {
		bytes, err = ioutil.ReadFile(*guidFile)
	}
	if len(bytes) > 0 && err == nil {
		serverId = strings.TrimSpace(string(bytes))
	} else {
		serverId = time.Now().UTC().Format(time.RFC3339Nano)
		err = ioutil.WriteFile(*guidFile,
			[]byte(serverId), 0666)
	}
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/phonyphonecall/turnip"
	"github.com/tylerharter/open-lambda/worker/container"
	"github.com/tylerharter/open-lambda/worker/handler"
)

type Server struct {
	manager  container.ContainerManager
	handlers *handler.HandlerSet

	// config options
	registry_host string
	registry_port string
	docker_host   string

	lambdaTimer *turnip.Turnip
}

type httpErr struct {
	msg  string
	code int
}

func newHttpErr(msg string, code int) *httpErr {
	return &httpErr{msg: msg, code: code}
}

func NewServer(
	registry_host string,
	registry_port string,
	docker_host string) (*Server, error) {

	// registry
	if registry_host == "" {
		registry_host = "localhost"
		log.Printf("Using '%v' for registry_host", registry_host)
	}

	if registry_port == "" {
		registry_port = "5000"
		log.Printf("Using '%v' for registry_port", registry_port)
	}

	// daemon
	cm := container.NewDockerManager(registry_host, registry_port)
	if docker_host == "" {
		endpoint := cm.Client().Endpoint()
		local := "unix://"
		nonLocal := "https://"
		if strings.HasPrefix(endpoint, local) {
			docker_host = "localhost"
		} else if strings.HasPrefix(endpoint, nonLocal) {
			start := strings.Index(endpoint, nonLocal) + len([]rune(nonLocal))
			end := strings.LastIndex(endpoint, ":")
			docker_host = endpoint[start:end]
		} else {
			return nil, fmt.Errorf("please specify a docker host!")
		}
		log.Printf("Using '%v' for docker_host", docker_host)
	}

	// create server
	opts := handler.HandlerSetOpts{
		Cm:  cm,
		Lru: handler.NewHandlerLRU(100), // TODO(tyler)
	}
	server := &Server{
		registry_host: registry_host,
		registry_port: registry_port,
		docker_host:   docker_host,
		manager:       cm,
		handlers:      handler.NewHandlerSet(opts),
		lambdaTimer:   turnip.NewTurnip(),
	}

	return server, nil
}

func (s *Server) Manager() container.ContainerManager {
	return s.manager
}

func (s *Server) ForwardToContainer(handler *handler.Handler, r *http.Request, input []byte) ([]byte, *http.Response, *httpErr) {
	port, err := handler.RunStart()
	if err != nil {
		return nil, nil, newHttpErr(
			err.Error(),
			http.StatusInternalServerError)
	}
	defer handler.RunFinish()

	// forward request to container.  r and w are the server
	// request and response respectively.  r2 and w2 are the
	// container request and response respectively.
	host := fmt.Sprintf("%s:%s", s.docker_host, port)
	url := fmt.Sprintf("http://%s%s", host, r.URL.Path)
	// log.Printf("proxying request to %s\n", url)

	// TODO(tyler): some sort of smarter backoff.  Or, a better
	// way to detect a started container.
	max_tries := 10
	for tries := 1; ; tries++ {
		r2, err := http.NewRequest(r.Method, url, bytes.NewReader(input))
		if err != nil {
			return nil, nil, newHttpErr(
				err.Error(),
				http.StatusInternalServerError)
		}

		r2.Header.Set("Content-Type", r.Header.Get("Content-Type"))
		client := &http.Client{}
		w2, err := client.Do(r2)
		if err != nil {
			log.Printf("request to container failed with %v\n", err)
			if tries == max_tries {
				return nil, nil, newHttpErr(
					err.Error(),
					http.StatusInternalServerError)
			}
			log.Printf("retry request\n")
			time.Sleep(time.Duration(tries*10) * time.Millisecond)
			continue
		}

		defer w2.Body.Close()
		wbody, err := ioutil.ReadAll(w2.Body)
		if err != nil {
			return nil, nil, newHttpErr(
				err.Error(),
				http.StatusInternalServerError)
		}
		return wbody, w2, nil
	}
}

func (s *Server) RunLambdaErr(w http.ResponseWriter, r *http.Request) *httpErr {
	// components represent runLambda[0]/<name_of_container>[1]/<extra_things>...
	// ergo we want [1] for name of container
	urlParts := getUrlComponents(r)
	if len(urlParts) < 2 {
		return newHttpErr(
			"Name of image to run required",
			http.StatusBadRequest)
	}
	img := urlParts[1]
	i := strings.Index(img, "?")
	if i >= 0 {
		img = img[:i-1]
	}

	// read request
	rbody := []byte{}
	if r.Body != nil {
		defer r.Body.Close()
		var err error
		rbody, err = ioutil.ReadAll(r.Body)
		if err != nil {
			return newHttpErr(
				err.Error(),
				http.StatusInternalServerError)
		}
	}

	// forward to container
	handler := s.handlers.Get(img)
	wbody, w2, err := s.ForwardToContainer(handler, r, rbody)
	if err != nil {
		return err
	}

	// write response
	// TODO(tyler): origins should be configurable
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods",
		"GET, PUT, POST, DELETE, OPTIONS")
	w.Header().Set("Access-Control-Allow-Headers",
		"Content-Type, Content-Range, Content-Disposition, Content-Description")

	w.WriteHeader(w2.StatusCode)

	if _, err := w.Write(wbody); err != nil {
		return newHttpErr(
			err.Error(),
			http.StatusInternalServerError)
	}

	return nil
}

// RunLambda expects POST requests like this:
//
// curl -X POST localhost:8080/runLambda/<lambda-name> -d '{}'
func (s *Server) RunLambda(w http.ResponseWriter, r *http.Request) {
	s.lambdaTimer.Start()
	if err := s.RunLambdaErr(w, r); err != nil {
		log.Printf("could not handle request: %s\n", err.msg)
		http.Error(w, err.msg, err.code)
	}
	s.lambdaTimer.Stop()
}

func (s *Server) Dump() {
	log.Printf("============ Server Stats ===========\n")
	log.Printf("\tlambda: \t%fms\n", s.lambdaTimer.AverageMs())
	log.Printf("=====================================\n")
}

// Parses request URL into its "/" delimated components
func getUrlComponents(r *http.Request) []string {
	path := r.URL.Path

	// trim prefix
	if strings.HasPrefix(path, "/") {
		path = path[1:]
	}

	// trim trailing "/"
	if strings.HasSuffix(path, "/") {
		path = path[:len(path)-1]
	}

	components := strings.Split(path, "/")
	return components
}

func main() {
	if len(os.Args) < 3 {
		log.Fatalf("usage: %s <registry hostname> <registry port>\n", os.Args[0])
	}

	docker_host, ok := os.LookupEnv("OL_DOCKER_HOST")
	if !ok {
		docker_host = ""
	}
	server, err := NewServer(os.Args[1], os.Args[2], docker_host)
	if err != nil {
		log.Fatal(err)
	}

	http.HandleFunc("/runLambda/", server.RunLambda)
	log.Fatal(http.ListenAndServe(":8080", nil))
}
<commit_msg>added more useful error message for when handler script fails<commit_after>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"time"
	"errors"

	"github.com/phonyphonecall/turnip"
	"github.com/tylerharter/open-lambda/worker/container"
	"github.com/tylerharter/open-lambda/worker/handler"
)

type Server struct {
	manager  container.ContainerManager
	handlers *handler.HandlerSet

	// config options
	registry_host string
	registry_port string
	docker_host   string

	lambdaTimer *turnip.Turnip
}

type httpErr struct {
	msg  string
	code int
}

func newHttpErr(msg string, code int) *httpErr {
	return &httpErr{msg: msg, code: code}
}

func NewServer(
	registry_host string,
	registry_port string,
	docker_host string) (*Server, error) {

	// registry
	if registry_host == "" {
		registry_host = "localhost"
		log.Printf("Using '%v' for registry_host", registry_host)
	}

	if registry_port == "" {
		registry_port = "5000"
		log.Printf("Using '%v' for registry_port", registry_port)
	}

	// daemon
	cm := container.NewDockerManager(registry_host, registry_port)
	if docker_host == "" {
		endpoint := cm.Client().Endpoint()
		local := "unix://"
		nonLocal := "https://"
		if strings.HasPrefix(endpoint, local) {
			docker_host = "localhost"
		} else if strings.HasPrefix(endpoint, nonLocal) {
			start := strings.Index(endpoint, nonLocal) + len([]rune(nonLocal))
			end := strings.LastIndex(endpoint, ":")
			docker_host = endpoint[start:end]
		} else {
			return nil, fmt.Errorf("please specify a docker host!")
		}
		log.Printf("Using '%v' for docker_host", docker_host)
	}

	// create server
	opts := handler.HandlerSetOpts{
		Cm:  cm,
		Lru: handler.NewHandlerLRU(100), // TODO(tyler)
	}
	server := &Server{
		registry_host: registry_host,
		registry_port: registry_port,
		docker_host:   docker_host,
		manager:       cm,
		handlers:      handler.NewHandlerSet(opts),
		lambdaTimer:   turnip.NewTurnip(),
	}

	return server, nil
}

func (s *Server) Manager() container.ContainerManager {
	return s.manager
}

func (s *Server) ForwardToContainer(handler *handler.Handler, r *http.Request, input []byte) ([]byte, *http.Response, *httpErr) {
	port, err := handler.RunStart()
	if err != nil {
		if strings.Contains(err.Error(), "is not paused") {
			err = errors.New("Initial execution of lambda handler failed")
		}
		return nil, nil, newHttpErr(
			err.Error(),
			http.StatusInternalServerError)
	}
	defer handler.RunFinish()

	// forward request to container.  r and w are the server
	// request and response respectively.  r2 and w2 are the
	// container request and response respectively.
	host := fmt.Sprintf("%s:%s", s.docker_host, port)
	url := fmt.Sprintf("http://%s%s", host, r.URL.Path)
	// log.Printf("proxying request to %s\n", url)

	// TODO(tyler): some sort of smarter backoff.  Or, a better
	// way to detect a started container.
	max_tries := 10
	for tries := 1; ; tries++ {
		r2, err := http.NewRequest(r.Method, url, bytes.NewReader(input))
		if err != nil {
			return nil, nil, newHttpErr(
				err.Error(),
				http.StatusInternalServerError)
		}

		r2.Header.Set("Content-Type", r.Header.Get("Content-Type"))
		client := &http.Client{}
		w2, err := client.Do(r2)
		if err != nil {
			log.Printf("request to container failed with %v\n", err)
			if tries == max_tries {
				return nil, nil, newHttpErr(
					err.Error(),
					http.StatusInternalServerError)
			}
			log.Printf("retry request\n")
			time.Sleep(time.Duration(tries*10) * time.Millisecond)
			continue
		}

		defer w2.Body.Close()
		wbody, err := ioutil.ReadAll(w2.Body)
		if err != nil {
			return nil, nil, newHttpErr(
				err.Error(),
				http.StatusInternalServerError)
		}
		return wbody, w2, nil
	}
}

func (s *Server) RunLambdaErr(w http.ResponseWriter, r *http.Request) *httpErr {
	// components represent runLambda[0]/<name_of_container>[1]/<extra_things>...
	// ergo we want [1] for name of container
	urlParts := getUrlComponents(r)
	if len(urlParts) < 2 {
		return newHttpErr(
			"Name of image to run required",
			http.StatusBadRequest)
	}
	img := urlParts[1]
	i := strings.Index(img, "?")
	if i >= 0 {
		img = img[:i-1]
	}

	// read request
	rbody := []byte{}
	if r.Body != nil {
		defer r.Body.Close()
		var err error
		rbody, err = ioutil.ReadAll(r.Body)
		if err != nil {
			return newHttpErr(
				err.Error(),
				http.StatusInternalServerError)
		}
	}

	// forward to container
	handler := s.handlers.Get(img)
	wbody, w2, err := s.ForwardToContainer(handler, r, rbody)
	if err != nil {
		return err
	}

	// write response
	// TODO(tyler): origins should be configurable
	// w.Header().Set("Access-Control-Allow-Origin", "*")
	// w.Header().Set("Access-Control-Allow-Methods",
	//	"GET, PUT, POST, DELETE, OPTIONS")
	//w.Header().Set("Access-Control-Allow-Headers",
	//	"Content-Type, Content-Range, Content-Disposition, Content-Description")

	w.WriteHeader(w2.StatusCode)

	if _, err := w.Write(wbody); err != nil {
		return newHttpErr(
			err.Error(),
			http.StatusInternalServerError)
	}

	return nil
}

// RunLambda expects POST requests like this:
//
// curl -X POST localhost:8080/runLambda/<lambda-name> -d '{}'
func (s *Server) RunLambda(w http.ResponseWriter, r *http.Request) {
	// write response headers
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods",
		"GET, PUT, POST, DELETE, OPTIONS")
	w.Header().Set("Access-Control-Allow-Headers",
		"Content-Type, Content-Range, Content-Disposition, Content-Description, X-Requested-With")

	//if r.Method == "options" {
	//	w.WriteHeader(200)
	//} else {
	s.lambdaTimer.Start()
	if err := s.RunLambdaErr(w, r); err != nil {
		log.Printf("could not handle request: %s\n", err.msg)
		http.Error(w, err.msg, err.code)
	}
	s.lambdaTimer.Stop()

	//}

}

func (s *Server) Dump() {
	log.Printf("============ Server Stats ===========\n")
	log.Printf("\tlambda: \t%fms\n", s.lambdaTimer.AverageMs())
	log.Printf("=====================================\n")
}

// Parses request URL into its "/" delimated components
func getUrlComponents(r *http.Request) []string {
	path := r.URL.Path

	// trim prefix
	if strings.HasPrefix(path, "/") {
		path = path[1:]
	}

	// trim trailing "/"
	if strings.HasSuffix(path, "/") {
		path = path[:len(path)-1]
	}

	components := strings.Split(path, "/")
	return components
}

func main() {
	if len(os.Args) < 3 {
		log.Fatalf("usage: %s <registry hostname> <registry port>\n", os.Args[0])
	}

	docker_host, ok := os.LookupEnv("OL_DOCKER_HOST")
	if !ok {
		docker_host = ""
	}
	server, err := NewServer(os.Args[1], os.Args[2], docker_host)
	if err != nil {
		log.Fatal(err)
	}

	http.HandleFunc("/runLambda/", server.RunLambda)
	log.Fatal(http.ListenAndServe(":8080", nil))
}
<|endoftext|>


<commit_before>// +build linux

package workercmd

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"

	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/localip"
	concourseCmd "github.com/concourse/concourse/cmd"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/grouper"
)

// This prepares the Guardian runtime using the gdn binary.
// The gdn binary exposes Guardian's functionality via a Garden server.
func (cmd *WorkerCommand) guardianRunner(logger lager.Logger) (ifrit.Runner, error) {
	depotDir := filepath.Join(cmd.WorkDir.Path(), "depot")

	// must be readable by other users so unprivileged containers can run their
	// own `initc' process
	err := os.MkdirAll(depotDir, 0755)
	if err != nil {
		return nil, err
	}

	members := grouper.Members{}

	gdnConfigFlag := []string{}

	if cmd.Guardian.Config.Path() != "" {
		gdnConfigFlag = append(gdnConfigFlag, "--config", cmd.Guardian.Config.Path())
	}

	gdnServerFlags := []string{
		"--bind-ip", cmd.BindIP.IP.String(),
		"--bind-port", fmt.Sprintf("%d", cmd.BindPort),

		"--depot", depotDir,
		"--properties-path", filepath.Join(cmd.WorkDir.Path(), "garden-properties.json"),

		"--time-format", "rfc3339",

		// disable graph and grootfs setup; all images passed to Concourse
		// containers are raw://
		"--no-image-plugin",
	}

	gdnServerFlags = append(gdnServerFlags, detectGuardianFlags(logger)...)

	if cmd.Guardian.DNS.Enable {
		dnsProxyRunner, err := cmd.dnsProxyRunner(logger.Session("dns-proxy"))
		if err != nil {
			return nil, err
		}

		lip, err := localip.LocalIP()
		if err != nil {
			return nil, err
		}

		members = append(members, grouper.Member{
			Name: "dns-proxy",
			Runner: concourseCmd.NewLoggingRunner(
				logger.Session("dns-proxy-runner"),
				dnsProxyRunner,
			),
		})

		gdnServerFlags = append(gdnServerFlags, "--dns-server", lip)

		// must permit access to host network in order for DNS proxy address to be
		// reachable
		gdnServerFlags = append(gdnServerFlags, "--allow-host-access")
	}

	gdnArgs := append(gdnConfigFlag, append([]string{"server"}, gdnServerFlags...)...)

	bin := "gdn"
	if cmd.Guardian.Bin != "" {
		bin = cmd.Guardian.Bin
	}

	gdnCmd := exec.Command(bin, gdnArgs...)
	gdnCmd.Stdout = os.Stdout
	gdnCmd.Stderr = os.Stderr
	gdnCmd.SysProcAttr = &syscall.SysProcAttr{
		Pdeathsig: syscall.SIGKILL,
	}

	members = append(members, grouper.Member{
		Name: "gdn",
		Runner: concourseCmd.NewLoggingRunner(
			logger.Session("gdn-runner"),
			CmdRunner{gdnCmd},
		),
	})

	return grouper.NewParallel(os.Interrupt, members), nil
}

func detectGuardianFlags(logger lager.Logger) []string {
	env := os.Environ()

	flags := []string{}
	for _, e := range env {
		spl := strings.SplitN(e, "=", 2)
		if len(spl) != 2 {
			logger.Info("bogus-env", lager.Data{"env": spl})
			continue
		}

		name := spl[0]
		val := spl[1]

		if !strings.HasPrefix(name, guardianEnvPrefix) {
			continue
		}

		strip := strings.Replace(name, guardianEnvPrefix, "", 1)
		flag := flagify(strip)

		logger.Info("forwarding-garden-env-var", lager.Data{
			"env":  name,
			"flag": flag,
		})

		vals := strings.Split(val, ",")

		for _, v := range vals {
			flags = append(flags, "--"+flag, v)
		}

		// clear out env (as twentythousandtonnesofcrudeoil does)
		_ = os.Unsetenv(name)
	}

	return flags
}

func flagify(env string) string {
	return strings.Replace(strings.ToLower(env), "_", "-", -1)
}
<commit_msg>worker: add default value for network-pool<commit_after>// +build linux

package workercmd

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"

	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/localip"
	concourseCmd "github.com/concourse/concourse/cmd"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/grouper"
)

// This prepares the Guardian runtime using the gdn binary.
// The gdn binary exposes Guardian's functionality via a Garden server.
func (cmd *WorkerCommand) guardianRunner(logger lager.Logger) (ifrit.Runner, error) {
	depotDir := filepath.Join(cmd.WorkDir.Path(), "depot")

	// must be readable by other users so unprivileged containers can run their
	// own `initc' process
	err := os.MkdirAll(depotDir, 0755)
	if err != nil {
		return nil, err
	}

	members := grouper.Members{}

	gdnConfigFlag := []string{}

	if cmd.Guardian.Config.Path() != "" {
		gdnConfigFlag = append(gdnConfigFlag, "--config", cmd.Guardian.Config.Path())
	}

	gdnServerFlags := []string{
		"--bind-ip", cmd.BindIP.IP.String(),
		"--bind-port", fmt.Sprintf("%d", cmd.BindPort),

		"--depot", depotDir,
		"--properties-path", filepath.Join(cmd.WorkDir.Path(), "garden-properties.json"),

		"--time-format", "rfc3339",

		// disable graph and grootfs setup; all images passed to Concourse
		// containers are raw://
		"--no-image-plugin",
	}

	gdnServerFlags = append(gdnServerFlags, detectGuardianFlags(logger)...)

	networkPoolSet := false
	for _, gdnServerFlag := range gdnServerFlags {
		if gdnServerFlag == "--network-pool" {
			networkPoolSet = true
			break
		}
	}
	if !networkPoolSet {
		// If network-pool is unset Guardian defaults to 10.80.0.0/22 which allows 1024 addresses implicitly limiting a
		// Guardian worker to 250 containers (4 addresses per container). This is true even if max-containers is set to
		// a higher value.
		// We set network-pool to 10.80.0.0/16 to increase the allowable addresses significantly ensuring the container
		// count is only limited by the explicit max-containers flag.
		gdnServerFlags = append(gdnServerFlags, "--network-pool", "10.80.0.0/16")
	}

	if cmd.Guardian.DNS.Enable {
		dnsProxyRunner, err := cmd.dnsProxyRunner(logger.Session("dns-proxy"))
		if err != nil {
			return nil, err
		}

		lip, err := localip.LocalIP()
		if err != nil {
			return nil, err
		}

		members = append(members, grouper.Member{
			Name: "dns-proxy",
			Runner: concourseCmd.NewLoggingRunner(
				logger.Session("dns-proxy-runner"),
				dnsProxyRunner,
			),
		})

		gdnServerFlags = append(gdnServerFlags, "--dns-server", lip)

		// must permit access to host network in order for DNS proxy address to be
		// reachable
		gdnServerFlags = append(gdnServerFlags, "--allow-host-access")
	}

	gdnArgs := append(gdnConfigFlag, append([]string{"server"}, gdnServerFlags...)...)

	bin := "gdn"
	if cmd.Guardian.Bin != "" {
		bin = cmd.Guardian.Bin
	}

	gdnCmd := exec.Command(bin, gdnArgs...)
	gdnCmd.Stdout = os.Stdout
	gdnCmd.Stderr = os.Stderr
	gdnCmd.SysProcAttr = &syscall.SysProcAttr{
		Pdeathsig: syscall.SIGKILL,
	}

	members = append(members, grouper.Member{
		Name: "gdn",
		Runner: concourseCmd.NewLoggingRunner(
			logger.Session("gdn-runner"),
			CmdRunner{gdnCmd},
		),
	})

	return grouper.NewParallel(os.Interrupt, members), nil
}

func detectGuardianFlags(logger lager.Logger) []string {
	env := os.Environ()

	flags := []string{}
	for _, e := range env {
		spl := strings.SplitN(e, "=", 2)
		if len(spl) != 2 {
			logger.Info("bogus-env", lager.Data{"env": spl})
			continue
		}

		name := spl[0]
		val := spl[1]

		if !strings.HasPrefix(name, guardianEnvPrefix) {
			continue
		}

		strip := strings.Replace(name, guardianEnvPrefix, "", 1)
		flag := flagify(strip)

		logger.Info("forwarding-garden-env-var", lager.Data{
			"env":  name,
			"flag": flag,
		})

		vals := strings.Split(val, ",")

		for _, v := range vals {
			flags = append(flags, "--"+flag, v)
		}

		// clear out env (as twentythousandtonnesofcrudeoil does)
		_ = os.Unsetenv(name)
	}

	return flags
}

func flagify(env string) string {
	return strings.Replace(strings.ToLower(env), "_", "-", -1)
}
<|endoftext|>