<commit_before>// Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package gin

import (
	"html/template"
	"net"
	"net/http"
	"os"
	"sync"

	"github.com/gin-gonic/gin/render"
)

// Version is Framework's version
const Version = "v1.1.4"

var default404Body = []byte("404 page not found")
var default405Body = []byte("405 method not allowed")
var defaultAppEngine bool

type HandlerFunc func(*Context)
type HandlersChain []HandlerFunc

// Last returns the last handler in the chain. ie. the last handler is the main own.
func (c HandlersChain) Last() HandlerFunc {
	length := len(c)
	if length > 0 {
		return c[length-1]
	}
	return nil
}

type (
	RoutesInfo []RouteInfo
	RouteInfo  struct {
		Method  string
		Path    string
		Handler string
	}

	// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
	// Create an instance of Engine, by using New() or Default()
	Engine struct {
		RouterGroup
		HTMLRender  render.HTMLRender
		allNoRoute  HandlersChain
		allNoMethod HandlersChain
		noRoute     HandlersChain
		noMethod    HandlersChain
		pool        sync.Pool
		trees       methodTrees

		// Enables automatic redirection if the current route can't be matched but a
		// handler for the path with (without) the trailing slash exists.
		// For example if /foo/ is requested but a route only exists for /foo, the
		// client is redirected to /foo with http status code 301 for GET requests
		// and 307 for all other request methods.
		RedirectTrailingSlash bool

		// If enabled, the router tries to fix the current request path, if no
		// handle is registered for it.
		// First superfluous path elements like ../ or // are removed.
		// Afterwards the router does a case-insensitive lookup of the cleaned path.
		// If a handle can be found for this route, the router makes a redirection
		// to the corrected path with status code 301 for GET requests and 307 for
		// all other request methods.
		// For example /FOO and /..//Foo could be redirected to /foo.
		// RedirectTrailingSlash is independent of this option.
		RedirectFixedPath bool

		// If enabled, the router checks if another method is allowed for the
		// current route, if the current request can not be routed.
		// If this is the case, the request is answered with 'Method Not Allowed'
		// and HTTP status code 405.
		// If no other Method is allowed, the request is delegated to the NotFound
		// handler.
		HandleMethodNotAllowed bool
		ForwardedByClientIP    bool

		// #726 #755 If enabled, it will thrust some headers starting with
		// 'X-AppEngine...' for better integration with that PaaS.
		AppEngine bool

		// If enabled, the url.RawPath will be used to find parameters.
		UseRawPath bool
		// If true, the path value will be unescaped.
		// If UseRawPath is false (by default), the UnescapePathValues effectively is true,
		// as url.Path gonna be used, which is already unescaped.
		UnescapePathValues bool
	}
)

var _ IRouter = &Engine{}

// New returns a new blank Engine instance without any middleware attached.
// By default the configuration is:
// - RedirectTrailingSlash:  true
// - RedirectFixedPath:      false
// - HandleMethodNotAllowed: false
// - ForwardedByClientIP:    true
// - UseRawPath:             false
// - UnescapePathValues:     true
func New() *Engine {
	debugPrintWARNINGNew()
	engine := &Engine{
		RouterGroup: RouterGroup{
			Handlers: nil,
			basePath: "/",
			root:     true,
		},
		RedirectTrailingSlash:  true,
		RedirectFixedPath:      false,
		HandleMethodNotAllowed: false,
		ForwardedByClientIP:    true,
		AppEngine:              defaultAppEngine,
		UseRawPath:             false,
		UnescapePathValues:     true,
		trees:                  make(methodTrees, 0, 9),
	}
	engine.RouterGroup.engine = engine
	engine.pool.New = func() interface{} {
		return engine.allocateContext()
	}
	return engine
}

// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
	engine := New()
	engine.Use(Logger(), Recovery())
	return engine
}

func (engine *Engine) allocateContext() *Context {
	return &Context{engine: engine}
}

func (engine *Engine) LoadHTMLGlob(pattern string) {
	if IsDebugging() {
		debugPrintLoadTemplate(template.Must(template.ParseGlob(pattern)))
		engine.HTMLRender = render.HTMLDebug{Glob: pattern}
	} else {
		templ := template.Must(template.ParseGlob(pattern))
		engine.SetHTMLTemplate(templ)
	}
}

func (engine *Engine) LoadHTMLFiles(files ...string) {
	if IsDebugging() {
		engine.HTMLRender = render.HTMLDebug{Files: files}
	} else {
		templ := template.Must(template.ParseFiles(files...))
		engine.SetHTMLTemplate(templ)
	}
}

func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
	if len(engine.trees) > 0 {
		debugPrintWARNINGSetHTMLTemplate()
	}
	engine.HTMLRender = render.HTMLProduction{Template: templ}
}

// NoRoute adds handlers for NoRoute. It return a 404 code by default.
func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
	engine.noRoute = handlers
	engine.rebuild404Handlers()
}

// NoMethod sets the handlers called when... TODO
func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
	engine.noMethod = handlers
	engine.rebuild405Handlers()
}

// Use attachs a global middleware to the router. ie. the middleware attached though Use() will be
// included in the handlers chain for every single request. Even 404, 405, static files...
// For example, this is the right place for a logger or error management middleware.
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
	engine.RouterGroup.Use(middleware...)
	engine.rebuild404Handlers()
	engine.rebuild405Handlers()
	return engine
}

func (engine *Engine) rebuild404Handlers() {
	engine.allNoRoute = engine.combineHandlers(engine.noRoute)
}

func (engine *Engine) rebuild405Handlers() {
	engine.allNoMethod = engine.combineHandlers(engine.noMethod)
}

func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
	assert1(path[0] == '/', "path must begin with '/'")
	assert1(len(method) > 0, "HTTP method can not be empty")
	assert1(len(handlers) > 0, "there must be at least one handler")

	debugPrintRoute(method, path, handlers)
	root := engine.trees.get(method)
	if root == nil {
		root = new(node)
		engine.trees = append(engine.trees, methodTree{method: method, root: root})
	}
	root.addRoute(path, handlers)
}

// Routes returns a slice of registered routes, including some useful information, such as:
// the http method, path and the handler name.
func (engine *Engine) Routes() (routes RoutesInfo) {
	for _, tree := range engine.trees {
		routes = iterate("", tree.method, routes, tree.root)
	}
	return routes
}

func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
	path += root.path
	if len(root.handlers) > 0 {
		routes = append(routes, RouteInfo{
			Method:  method,
			Path:    path,
			Handler: nameOfFunction(root.handlers.Last()),
		})
	}
	for _, child := range root.children {
		routes = iterate(path, method, routes, child)
	}
	return routes
}

// Run attaches the router to a http.Server and starts listening and serving HTTP requests.
// It is a shortcut for http.ListenAndServe(addr, router)
// Note: this method will block the calling goroutine indefinitely unless an error happens.
func (engine *Engine) Run(addr ...string) (err error) {
	defer func() { debugPrintError(err) }()

	address := resolveAddress(addr)
	debugPrint("Listening and serving HTTP on %s\n", address)
	err = http.ListenAndServe(address, engine)
	return
}

// RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests.
// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
// Note: this method will block the calling goroutine indefinitely unless an error happens.
func (engine *Engine) RunTLS(addr string, certFile string, keyFile string) (err error) {
	debugPrint("Listening and serving HTTPS on %s\n", addr)
	defer func() { debugPrintError(err) }()

	err = http.ListenAndServeTLS(addr, certFile, keyFile, engine)
	return
}

// RunUnix attaches the router to a http.Server and starts listening and serving HTTP requests
// through the specified unix socket (ie. a file).
// Note: this method will block the calling goroutine indefinitely unless an error happens.
func (engine *Engine) RunUnix(file string) (err error) {
	debugPrint("Listening and serving HTTP on unix:/%s", file)
	defer func() { debugPrintError(err) }()

	os.Remove(file)
	listener, err := net.Listen("unix", file)
	if err != nil {
		return
	}
	defer listener.Close()
	err = http.Serve(listener, engine)
	return
}

// Conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	c := engine.pool.Get().(*Context)
	c.writermem.reset(w)
	c.Request = req
	c.reset()

	engine.handleHTTPRequest(c)

	engine.pool.Put(c)
}

// Re-enter a context that has been rewritten.
// This can be done by setting c.Request.Path to your new target.
// Disclaimer: You can loop yourself to death with this, use wisely.
func (engine *Engine) HandleContext(c *Context) {
	c.reset()
	engine.handleHTTPRequest(c)
	engine.pool.Put(c)
}

func (engine *Engine) handleHTTPRequest(context *Context) {
	httpMethod := context.Request.Method
	var path string
	var unescape bool
	if engine.UseRawPath && len(context.Request.URL.RawPath) > 0 {
		path = context.Request.URL.RawPath
		unescape = engine.UnescapePathValues
	} else {
		path = context.Request.URL.Path
		unescape = false
	}

	// Find root of the tree for the given HTTP method
	t := engine.trees
	for i, tl := 0, len(t); i < tl; i++ {
		if t[i].method == httpMethod {
			root := t[i].root
			// Find route in tree
			handlers, params, tsr := root.getValue(path, context.Params, unescape)
			if handlers != nil {
				context.handlers = handlers
				context.Params = params
				context.Next()
				context.writermem.WriteHeaderNow()
				return

			} else if httpMethod != "CONNECT" && path != "/" {
				if tsr && engine.RedirectTrailingSlash {
					redirectTrailingSlash(context)
					return
				}
				if engine.RedirectFixedPath && redirectFixedPath(context, root, engine.RedirectFixedPath) {
					return
				}
			}
			break
		}
	}

	// TODO: unit test
	if engine.HandleMethodNotAllowed {
		for _, tree := range engine.trees {
			if tree.method != httpMethod {
				if handlers, _, _ := tree.root.getValue(path, nil, unescape); handlers != nil {
					context.handlers = engine.allNoMethod
					serveError(context, 405, default405Body)
					return
				}
			}
		}
	}
	context.handlers = engine.allNoRoute
	serveError(context, 404, default404Body)
}

var mimePlain = []string{MIMEPlain}

func serveError(c *Context, code int, defaultMessage []byte) {
	c.writermem.status = code
	c.Next()
	if !c.writermem.Written() {
		if c.writermem.Status() == code {
			c.writermem.Header()["Content-Type"] = mimePlain
			c.Writer.Write(defaultMessage)
		} else {
			c.writermem.WriteHeaderNow()
		}
	}
}

func redirectTrailingSlash(c *Context) {
	req := c.Request
	path := req.URL.Path
	code := 301 // Permanent redirect, request with GET method
	if req.Method != "GET" {
		code = 307
	}

	if len(path) > 1 && path[len(path)-1] == '/' {
		req.URL.Path = path[:len(path)-1]
	} else {
		req.URL.Path = path + "/"
	}
	debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
	http.Redirect(c.Writer, req, req.URL.String(), code)
	c.writermem.WriteHeaderNow()
}

func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool {
	req := c.Request
	path := req.URL.Path

	fixedPath, found := root.findCaseInsensitivePath(
		cleanPath(path),
		trailingSlash,
	)
	if found {
		code := 301 // Permanent redirect, request with GET method
		if req.Method != "GET" {
			code = 307
		}
		req.URL.Path = string(fixedPath)
		debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
		http.Redirect(c.Writer, req, req.URL.String(), code)
		c.writermem.WriteHeaderNow()
		return true
	}
	return false
}
<commit_msg>bump version to 1.2<commit_after>// Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package gin

import (
	"html/template"
	"net"
	"net/http"
	"os"
	"sync"

	"github.com/gin-gonic/gin/render"
)

// Version is Framework's version
const Version = "v1.2"

var default404Body = []byte("404 page not found")
var default405Body = []byte("405 method not allowed")
var defaultAppEngine bool

type HandlerFunc func(*Context)
type HandlersChain []HandlerFunc

// Last returns the last handler in the chain. ie. the last handler is the main own.
func (c HandlersChain) Last() HandlerFunc {
	length := len(c)
	if length > 0 {
		return c[length-1]
	}
	return nil
}

type (
	RoutesInfo []RouteInfo
	RouteInfo  struct {
		Method  string
		Path    string
		Handler string
	}

	// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
	// Create an instance of Engine, by using New() or Default()
	Engine struct {
		RouterGroup
		HTMLRender  render.HTMLRender
		allNoRoute  HandlersChain
		allNoMethod HandlersChain
		noRoute     HandlersChain
		noMethod    HandlersChain
		pool        sync.Pool
		trees       methodTrees

		// Enables automatic redirection if the current route can't be matched but a
		// handler for the path with (without) the trailing slash exists.
		// For example if /foo/ is requested but a route only exists for /foo, the
		// client is redirected to /foo with http status code 301 for GET requests
		// and 307 for all other request methods.
		RedirectTrailingSlash bool

		// If enabled, the router tries to fix the current request path, if no
		// handle is registered for it.
		// First superfluous path elements like ../ or // are removed.
		// Afterwards the router does a case-insensitive lookup of the cleaned path.
		// If a handle can be found for this route, the router makes a redirection
		// to the corrected path with status code 301 for GET requests and 307 for
		// all other request methods.
		// For example /FOO and /..//Foo could be redirected to /foo.
		// RedirectTrailingSlash is independent of this option.
		RedirectFixedPath bool

		// If enabled, the router checks if another method is allowed for the
		// current route, if the current request can not be routed.
		// If this is the case, the request is answered with 'Method Not Allowed'
		// and HTTP status code 405.
		// If no other Method is allowed, the request is delegated to the NotFound
		// handler.
		HandleMethodNotAllowed bool
		ForwardedByClientIP    bool

		// #726 #755 If enabled, it will thrust some headers starting with
		// 'X-AppEngine...' for better integration with that PaaS.
		AppEngine bool

		// If enabled, the url.RawPath will be used to find parameters.
		UseRawPath bool
		// If true, the path value will be unescaped.
		// If UseRawPath is false (by default), the UnescapePathValues effectively is true,
		// as url.Path gonna be used, which is already unescaped.
		UnescapePathValues bool
	}
)

var _ IRouter = &Engine{}

// New returns a new blank Engine instance without any middleware attached.
// By default the configuration is:
// - RedirectTrailingSlash:  true
// - RedirectFixedPath:      false
// - HandleMethodNotAllowed: false
// - ForwardedByClientIP:    true
// - UseRawPath:             false
// - UnescapePathValues:     true
func New() *Engine {
	debugPrintWARNINGNew()
	engine := &Engine{
		RouterGroup: RouterGroup{
			Handlers: nil,
			basePath: "/",
			root:     true,
		},
		RedirectTrailingSlash:  true,
		RedirectFixedPath:      false,
		HandleMethodNotAllowed: false,
		ForwardedByClientIP:    true,
		AppEngine:              defaultAppEngine,
		UseRawPath:             false,
		UnescapePathValues:     true,
		trees:                  make(methodTrees, 0, 9),
	}
	engine.RouterGroup.engine = engine
	engine.pool.New = func() interface{} {
		return engine.allocateContext()
	}
	return engine
}

// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
	engine := New()
	engine.Use(Logger(), Recovery())
	return engine
}

func (engine *Engine) allocateContext() *Context {
	return &Context{engine: engine}
}

func (engine *Engine) LoadHTMLGlob(pattern string) {
	if IsDebugging() {
		debugPrintLoadTemplate(template.Must(template.ParseGlob(pattern)))
		engine.HTMLRender = render.HTMLDebug{Glob: pattern}
	} else {
		templ := template.Must(template.ParseGlob(pattern))
		engine.SetHTMLTemplate(templ)
	}
}

func (engine *Engine) LoadHTMLFiles(files ...string) {
	if IsDebugging() {
		engine.HTMLRender = render.HTMLDebug{Files: files}
	} else {
		templ := template.Must(template.ParseFiles(files...))
		engine.SetHTMLTemplate(templ)
	}
}

func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
	if len(engine.trees) > 0 {
		debugPrintWARNINGSetHTMLTemplate()
	}
	engine.HTMLRender = render.HTMLProduction{Template: templ}
}

// NoRoute adds handlers for NoRoute. It return a 404 code by default.
func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
	engine.noRoute = handlers
	engine.rebuild404Handlers()
}

// NoMethod sets the handlers called when... TODO
func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
	engine.noMethod = handlers
	engine.rebuild405Handlers()
}

// Use attachs a global middleware to the router. ie. the middleware attached though Use() will be
// included in the handlers chain for every single request. Even 404, 405, static files...
// For example, this is the right place for a logger or error management middleware.
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
	engine.RouterGroup.Use(middleware...)
	engine.rebuild404Handlers()
	engine.rebuild405Handlers()
	return engine
}

func (engine *Engine) rebuild404Handlers() {
	engine.allNoRoute = engine.combineHandlers(engine.noRoute)
}

func (engine *Engine) rebuild405Handlers() {
	engine.allNoMethod = engine.combineHandlers(engine.noMethod)
}

func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
	assert1(path[0] == '/', "path must begin with '/'")
	assert1(len(method) > 0, "HTTP method can not be empty")
	assert1(len(handlers) > 0, "there must be at least one handler")

	debugPrintRoute(method, path, handlers)
	root := engine.trees.get(method)
	if root == nil {
		root = new(node)
		engine.trees = append(engine.trees, methodTree{method: method, root: root})
	}
	root.addRoute(path, handlers)
}

// Routes returns a slice of registered routes, including some useful information, such as:
// the http method, path and the handler name.
func (engine *Engine) Routes() (routes RoutesInfo) {
	for _, tree := range engine.trees {
		routes = iterate("", tree.method, routes, tree.root)
	}
	return routes
}

func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
	path += root.path
	if len(root.handlers) > 0 {
		routes = append(routes, RouteInfo{
			Method:  method,
			Path:    path,
			Handler: nameOfFunction(root.handlers.Last()),
		})
	}
	for _, child := range root.children {
		routes = iterate(path, method, routes, child)
	}
	return routes
}

// Run attaches the router to a http.Server and starts listening and serving HTTP requests.
// It is a shortcut for http.ListenAndServe(addr, router)
// Note: this method will block the calling goroutine indefinitely unless an error happens.
func (engine *Engine) Run(addr ...string) (err error) {
	defer func() { debugPrintError(err) }()

	address := resolveAddress(addr)
	debugPrint("Listening and serving HTTP on %s\n", address)
	err = http.ListenAndServe(address, engine)
	return
}

// RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests.
// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
// Note: this method will block the calling goroutine indefinitely unless an error happens.
func (engine *Engine) RunTLS(addr string, certFile string, keyFile string) (err error) {
	debugPrint("Listening and serving HTTPS on %s\n", addr)
	defer func() { debugPrintError(err) }()

	err = http.ListenAndServeTLS(addr, certFile, keyFile, engine)
	return
}

// RunUnix attaches the router to a http.Server and starts listening and serving HTTP requests
// through the specified unix socket (ie. a file).
// Note: this method will block the calling goroutine indefinitely unless an error happens.
func (engine *Engine) RunUnix(file string) (err error) {
	debugPrint("Listening and serving HTTP on unix:/%s", file)
	defer func() { debugPrintError(err) }()

	os.Remove(file)
	listener, err := net.Listen("unix", file)
	if err != nil {
		return
	}
	defer listener.Close()
	err = http.Serve(listener, engine)
	return
}

// Conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	c := engine.pool.Get().(*Context)
	c.writermem.reset(w)
	c.Request = req
	c.reset()

	engine.handleHTTPRequest(c)

	engine.pool.Put(c)
}

// Re-enter a context that has been rewritten.
// This can be done by setting c.Request.Path to your new target.
// Disclaimer: You can loop yourself to death with this, use wisely.
func (engine *Engine) HandleContext(c *Context) {
	c.reset()
	engine.handleHTTPRequest(c)
	engine.pool.Put(c)
}

func (engine *Engine) handleHTTPRequest(context *Context) {
	httpMethod := context.Request.Method
	var path string
	var unescape bool
	if engine.UseRawPath && len(context.Request.URL.RawPath) > 0 {
		path = context.Request.URL.RawPath
		unescape = engine.UnescapePathValues
	} else {
		path = context.Request.URL.Path
		unescape = false
	}

	// Find root of the tree for the given HTTP method
	t := engine.trees
	for i, tl := 0, len(t); i < tl; i++ {
		if t[i].method == httpMethod {
			root := t[i].root
			// Find route in tree
			handlers, params, tsr := root.getValue(path, context.Params, unescape)
			if handlers != nil {
				context.handlers = handlers
				context.Params = params
				context.Next()
				context.writermem.WriteHeaderNow()
				return

			} else if httpMethod != "CONNECT" && path != "/" {
				if tsr && engine.RedirectTrailingSlash {
					redirectTrailingSlash(context)
					return
				}
				if engine.RedirectFixedPath && redirectFixedPath(context, root, engine.RedirectFixedPath) {
					return
				}
			}
			break
		}
	}

	// TODO: unit test
	if engine.HandleMethodNotAllowed {
		for _, tree := range engine.trees {
			if tree.method != httpMethod {
				if handlers, _, _ := tree.root.getValue(path, nil, unescape); handlers != nil {
					context.handlers = engine.allNoMethod
					serveError(context, 405, default405Body)
					return
				}
			}
		}
	}
	context.handlers = engine.allNoRoute
	serveError(context, 404, default404Body)
}

var mimePlain = []string{MIMEPlain}

func serveError(c *Context, code int, defaultMessage []byte) {
	c.writermem.status = code
	c.Next()
	if !c.writermem.Written() {
		if c.writermem.Status() == code {
			c.writermem.Header()["Content-Type"] = mimePlain
			c.Writer.Write(defaultMessage)
		} else {
			c.writermem.WriteHeaderNow()
		}
	}
}

func redirectTrailingSlash(c *Context) {
	req := c.Request
	path := req.URL.Path
	code := 301 // Permanent redirect, request with GET method
	if req.Method != "GET" {
		code = 307
	}

	if len(path) > 1 && path[len(path)-1] == '/' {
		req.URL.Path = path[:len(path)-1]
	} else {
		req.URL.Path = path + "/"
	}
	debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
	http.Redirect(c.Writer, req, req.URL.String(), code)
	c.writermem.WriteHeaderNow()
}

func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool {
	req := c.Request
	path := req.URL.Path

	fixedPath, found := root.findCaseInsensitivePath(
		cleanPath(path),
		trailingSlash,
	)
	if found {
		code := 301 // Permanent redirect, request with GET method
		if req.Method != "GET" {
			code = 307
		}
		req.URL.Path = string(fixedPath)
		debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
		http.Redirect(c.Writer, req, req.URL.String(), code)
		c.writermem.WriteHeaderNow()
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package main

/**
 * Git Repo Information
 */

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	ui "github.com/gizak/termui"
	walk "github.com/karrick/godirwalk"
)

////////////////////////////////////////////
// Utility: Git Repo Info
////////////////////////////////////////////

const GitRepoStatusUpdateInterval = 10 * time.Second

type RepoStatusField struct {
	OutputCharacter   rune
	OutputColorString string
}

// Key is the git status rune (what shows up in `git status -sb`)
var RepoStatusFieldDefinitionsOrderedKeys = []rune{'M', 'A', 'D', 'R', 'C', 'U', '?', '!'}
var RepoStatusFieldDefinitions = map[rune]RepoStatusField{
	// modified
	'M': RepoStatusField{OutputCharacter: 'M', OutputColorString: "fg-green"},
	// added
	'A': RepoStatusField{OutputCharacter: '+', OutputColorString: "fg-green,fg-bold"},
	// deleted
	'D': RepoStatusField{OutputCharacter: '-', OutputColorString: "fg-red,fg-bold"},
	// renamed
	'R': RepoStatusField{OutputCharacter: 'R', OutputColorString: "fg-yellow,fg-bold"},
	// copied
	'C': RepoStatusField{OutputCharacter: 'C', OutputColorString: "fg-blue,fg-bold"},
	// updated
	'U': RepoStatusField{OutputCharacter: 'U', OutputColorString: "fg-magenta,fg-bold"},
	// untracked
	'?': RepoStatusField{OutputCharacter: '?', OutputColorString: "fg-red"},
	// ignored
	'!': RepoStatusField{OutputCharacter: '!', OutputColorString: "fg-cyan"},
}

type RepoInfo struct {
	Name         string
	FullPath     string
	HomePath     string
	BranchStatus string
	Status       string
	lastUpdated  *time.Time
}

func NewRepoInfo(fullPath string) RepoInfo {
	if strings.HasSuffix(fullPath, ".git") || strings.HasSuffix(fullPath, ".git/") {
		// This is the path to the .git folder, so go up a level
		fullPath = normalizePath(filepath.Join(fullPath, ".."))
	}

	// Repo name
	name := filepath.Base(fullPath)

	// Normalize path with home directory (if possible)
	homePath := fullPath

	if strings.HasPrefix(fullPath, HOME) {
		relative, relErr := filepath.Rel(HOME, fullPath)

		if relErr == nil {
			homePath = filepath.Join("~", relative)
		} else {
			log.Printf("Error getting relative: %v", relErr)
		}
	} else if strings.HasPrefix(fullPath, CANONHOME) {
		relative, relErr := filepath.Rel(CANONHOME, fullPath)

		if relErr == nil {
			homePath = filepath.Join("~", relative)
		} else {
			log.Printf("Error getting relative: %v", relErr)
		}
	}

	// Load repo status
	branches := "my branches"
	status := "my status"

	// Build it
	r := RepoInfo{
		Name:         name,
		FullPath:     fullPath,
		HomePath:     homePath,
		BranchStatus: branches,
		Status:       status,
	}

	r.update()

	return r
}

func (w *RepoInfo) update() {
	if shouldUpdate(w) {
		// TODO: Make this not run a command to get this data
		// Go do a git status in that folder
		output, exitCode, err := execAndGetOutput("git", &w.FullPath, "-c", "color.status=never", "-c", "color.ui=never", "status", "-sb")

		if err != nil {
			log.Printf("Failed to get git output for repo %v (%v): %v", w.Name, w.FullPath, err)
		} else if exitCode != 0 {
			log.Printf("Bad exit code getting git output for repo %v (%v): %v", w.Name, w.FullPath, exitCode)
		} else {
			// Parse out the output
			lines := strings.Split(output, "\n")

			// Branch is first line
			branchLine := lines[0][3:]
			branchName := strings.Split(branchLine, " ")[0]
			if strings.Contains(branchName, "...") {
				branchName = strings.Split(branchName, "...")[0]
			}

			branchState := ""
			if strings.Contains(branchLine, "[") {
				branchState = "[" + strings.Split(branchLine, "[")[1]
			}

			nameColor := "fg-cyan"

			if branchName == "master" || branchName == "mainline" {
				nameColor = "fg-green"
			}

			w.BranchStatus = fmt.Sprintf("[%v](%s)", branchName, nameColor)

			if len(branchState) > 0 {
				w.BranchStatus += fmt.Sprintf(" [%v](fg-magenta)", branchState)
			}

			// Status for files follows, let's aggregate
			status := make(map[rune]int, len(RepoStatusFieldDefinitions))
			for field, _ := range RepoStatusFieldDefinitions {
				status[field] = 0
			}

			for _, l := range lines[1:] {
				l = strings.TrimSpace(l)

				if len(l) < 2 {
					continue
				}

				// Grab first two characters
				statchars := l[:2]

				for key := range status {
					if strings.ContainsRune(statchars, key) {
						status[key]++
					}
				}
			}

			w.Status = buildColoredStatusStringFromMap(status)
		}
	}
}

func (w *RepoInfo) getUpdateInterval() time.Duration {
	return GitRepoStatusUpdateInterval
}

func (w *RepoInfo) getLastUpdated() *time.Time {
	return w.lastUpdated
}

func (w *RepoInfo) setLastUpdated(t time.Time) {
	w.lastUpdated = &t
}

type BySortOrder []*ui.Gauge

func (a BySortOrder) Len() int           { return len(a) }
func (a BySortOrder) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a BySortOrder) Less(i, j int) bool { return a[i].BorderLabel < a[j].BorderLabel }

func buildColoredStatusStringFromMap(status map[rune]int) string {
	retval := ""

	for _, key := range RepoStatusFieldDefinitionsOrderedKeys {
		count := status[key]

		if count > 0 {
			if retval != "" {
				retval += " "
			}

			retval += fmt.Sprintf("[%c:%d](%s)", RepoStatusFieldDefinitions[key].OutputCharacter, count, RepoStatusFieldDefinitions[key].OutputColorString)
		}
	}

	return retval
}

////////////////////////////////////////////
// Utility: Git Repo List
////////////////////////////////////////////

const GitRepoListUpdateInterval = 30 * time.Second

var HOME = os.ExpandEnv("$HOME")
var CANONHOME = normalizePath(HOME)

type CachedGitRepoList struct {
	repoSearch  map[string]int
	Repos       []RepoInfo
	lastUpdated *time.Time
}

func (w *CachedGitRepoList) getUpdateInterval() time.Duration {
	return GitRepoListUpdateInterval
}

func (w *CachedGitRepoList) getLastUpdated() *time.Time {
	return w.lastUpdated
}

func (w *CachedGitRepoList) setLastUpdated(t time.Time) {
	w.lastUpdated = &t
}

func (w *CachedGitRepoList) update() {
	if shouldUpdate(w) {
		repos := getGitRepositories(w.repoSearch)

		w.Repos = make([]RepoInfo, 0)

		for _, repo := range repos {
			repoInfo := NewRepoInfo(repo)

			w.Repos = append(w.Repos, repoInfo)
		}
	}

	// Update status for all the repos as well
	for _, r := range w.Repos {
		r.update()
	}
}

func NewCachedGitRepoList(search map[string]int) *CachedGitRepoList {
	// Build it
	w := &CachedGitRepoList{
		repoSearch: search,
		Repos:      make([]RepoInfo, 0),
	}

	w.update()

	return w
}

var cachedGitRepos = NewCachedGitRepoList(GetGitRepoSearchPaths())

// Walks the search directories to look for git folders
// search is a map of directory roots to depths
func getGitRepositories(search map[string]int) []string {
	var retval = make([]string, 0)

	for path, depth := range search {
		gitRepos := getGitRepositoriesForPath(path, depth)

		retval = append(retval, gitRepos...)
	}

	// Sort
	sort.Strings(retval)

	// Uniquify
	// w is where non-matching elements should be written
	// last is the last element we wrote
	// r is the current read pointer
	w := 1
	last := 0
	for r := 1; r < len(retval); r++ {
		// If they're the same, skip it
		if retval[r] == retval[last] {
			continue
		}

		// They're different, write it to the array
		retval[w] = retval[r]

		// Save last pointer
		last = w

		// Advance
		w++
	}

	retval = retval[:w] // slice it to just what we wrote

	return retval
}

func getGitRepositoriesForPath(root string, maxDepth int) []string {
	var retval = walkTreeLookingForGit(root, nil, 0, maxDepth)

	return retval
}

func walkTreeLookingForGit(path string, de *walk.Dirent, curDepth int, maxDepth int) []string {
	// Do we keep going?
	if curDepth <= maxDepth {
		// de is nil the first time through
		if de != nil {
			gitPath := checkAndResolveGitFolder(path, de)

			if gitPath != nil {
				// Got it!
				return []string{*gitPath}
			}
		}

		// Get children
		retval := make([]string, 0)

		kids, err := walk.ReadDirents(path, nil)

		if err != nil {
			log.Printf("Failed to traverse into children of '%v': %v", path, err)
		} else {
			for _, kidDE := range kids {
				if kidDE.IsDir() {
					results := walkTreeLookingForGit(filepath.Join(path, kidDE.Name()), kidDE, curDepth+1, maxDepth)

					retval = append(retval, results...)
				}
			}
		}

		return retval
	} else {
		return []string{}
	}
}

// Returns nil if not a git folder
// Returns a resolved pathname if is a git folder
func checkAndResolveGitFolder(osPathname string, de *walk.Dirent) *string {
	// check name
	if !de.IsDir() {
		return nil
	}

	if de.Name() != ".git" {
		return nil
	}

	path := normalizePath(osPathname)
	return &path
}

////////////////////////////////////////////
// Widget: Git Repos
////////////////////////////////////////////

const MinimumRepoNameWidth = 26
const MinimumRepoBranchesWidth = 37

type GitRepoWidget struct {
	widget      *ui.Table
	lastUpdated *time.Time
}

func NewGitRepoWidget() *GitRepoWidget {
	// Create base element
	e := ui.NewTable()
	e.Border = true
	e.BorderLabel = "Git Repos"
	e.Separator = false

	// Create widget
	w := &GitRepoWidget{
		widget: e,
	}

	w.update()
	w.resize()

	return w
}

func (w *GitRepoWidget) getGridWidget() ui.GridBufferer {
	return w.widget
}

func (w *GitRepoWidget) update() {
	w.widget.Rows = [][]string{}
	w.widget.Height = 2

	// Load repos
	cachedGitRepos.update()

	maxRepoWidth := 0

	for _, repo := range cachedGitRepos.Repos {
		// Figure out max length
		if len(repo.HomePath) > maxRepoWidth {
			maxRepoWidth = len(repo.HomePath)
		}
	}

	if maxRepoWidth < MinimumRepoNameWidth {
		maxRepoWidth = MinimumRepoNameWidth
	}

	for _, repo := range cachedGitRepos.Repos {
		// Make the name all fancy
		pathPad := maxRepoWidth - len(repo.Name)
		path := filepath.Dir(repo.HomePath)

		name := fmt.Sprintf("[%*v%c](fg-cyan)[%v](fg-cyan,fg-bold)", pathPad, path, os.PathSeparator, repo.Name)

		line := []string{name, repo.BranchStatus, repo.Status}

		w.widget.Rows = append(w.widget.Rows, line)
		w.widget.Height++
	}
}

func (w *GitRepoWidget) resize() {
	// Do nothing
}
<commit_msg>Try to be more robust against new/removed git repositories.<commit_after>package main

/**
 * Git Repo Information
 */

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	ui "github.com/gizak/termui"
	walk "github.com/karrick/godirwalk"
)

////////////////////////////////////////////
// Utility: Git Repo Info
////////////////////////////////////////////

const GitRepoStatusUpdateInterval = 10 * time.Second

type RepoStatusField struct {
	OutputCharacter   rune
	OutputColorString string
}

// Key is the git status rune (what shows up in `git status -sb`)
var RepoStatusFieldDefinitionsOrderedKeys = []rune{'M', 'A', 'D', 'R', 'C', 'U', '?', '!'}
var RepoStatusFieldDefinitions = map[rune]RepoStatusField{
	// modified
	'M': RepoStatusField{OutputCharacter: 'M', OutputColorString: "fg-green"},
	// added
	'A': RepoStatusField{OutputCharacter: '+', OutputColorString: "fg-green,fg-bold"},
	// deleted
	'D': RepoStatusField{OutputCharacter: '-', OutputColorString: "fg-red,fg-bold"},
	// renamed
	'R': RepoStatusField{OutputCharacter: 'R', OutputColorString: "fg-yellow,fg-bold"},
	// copied
	'C': RepoStatusField{OutputCharacter: 'C', OutputColorString: "fg-blue,fg-bold"},
	// updated
	'U': RepoStatusField{OutputCharacter: 'U', OutputColorString: "fg-magenta,fg-bold"},
	// untracked
	'?': RepoStatusField{OutputCharacter: '?', OutputColorString: "fg-red"},
	// ignored
	'!': RepoStatusField{OutputCharacter: '!', OutputColorString: "fg-cyan"},
}

type RepoInfo struct {
	Name         string
	FullPath     string
	HomePath     string
	BranchStatus string
	Status       string
	lastUpdated  *time.Time
}

func NewRepoInfo(fullPath string) RepoInfo {
	if strings.HasSuffix(fullPath, ".git") || strings.HasSuffix(fullPath, ".git/") {
		// This is the path to the .git folder, so go up a level
		fullPath = normalizePath(filepath.Join(fullPath, ".."))
	}

	// Repo name
	name := filepath.Base(fullPath)

	// Normalize path with home directory (if possible)
	homePath := fullPath

	if strings.HasPrefix(fullPath, HOME) {
		relative, relErr := filepath.Rel(HOME, fullPath)

		if relErr == nil {
			homePath = filepath.Join("~", relative)
		} else {
			log.Printf("Error getting relative: %v", relErr)
		}
	} else if strings.HasPrefix(fullPath, CANONHOME) {
		relative, relErr := filepath.Rel(CANONHOME, fullPath)

		if relErr == nil {
			homePath = filepath.Join("~", relative)
		} else {
			log.Printf("Error getting relative: %v", relErr)
		}
	}

	// Load repo status
	branches := "my branches"
	status := "my status"

	// Build it
	r := RepoInfo{
		Name:         name,
		FullPath:     fullPath,
		HomePath:     homePath,
		BranchStatus: branches,
		Status:       status,
	}

	r.update()

	return r
}

func (w *RepoInfo) update() {
	if shouldUpdate(w) {
		// TODO: Make this not run a command to get this data
		// Go do a git status in that folder
		output, exitCode, err := execAndGetOutput("git", &w.FullPath, "-c", "color.status=never", "-c", "color.ui=never", "status", "-sb")

		if err != nil {
			log.Printf("Failed to get git output for repo %v (%v): %v", w.Name, w.FullPath, err)
		} else if exitCode != 0 {
			log.Printf("Bad exit code getting git output for repo %v (%v): %v", w.Name, w.FullPath, exitCode)
		} else {
			// Parse out the output
			lines := strings.Split(output, "\n")

			// Branch is first line
			branchLine := lines[0][3:]
			branchName := strings.Split(branchLine, " ")[0]
			if strings.Contains(branchName, "...") {
				branchName = strings.Split(branchName, "...")[0]
			}

			branchState := ""
			if strings.Contains(branchLine, "[") {
				branchState = "[" + strings.Split(branchLine, "[")[1]
			}

			nameColor := "fg-cyan"

			if branchName == "master" || branchName == "mainline" {
				nameColor = "fg-green"
			}

			w.BranchStatus = fmt.Sprintf("[%v](%s)", branchName, nameColor)

			if len(branchState) > 0 {
				w.BranchStatus += fmt.Sprintf(" [%v](fg-magenta)", branchState)
			}

			// Status for files follows, let's aggregate
			status := make(map[rune]int, len(RepoStatusFieldDefinitions))
			for field, _ := range RepoStatusFieldDefinitions {
				status[field] = 0
			}

			for _, l := range lines[1:] {
				l = strings.TrimSpace(l)

				if len(l) < 2 {
					continue
				}

				// Grab first two characters
				statchars := l[:2]

				for key := range status {
					if strings.ContainsRune(statchars, key) {
						status[key]++
					}
				}
			}

			w.Status = buildColoredStatusStringFromMap(status)
		}
	}
}

func (w *RepoInfo) getUpdateInterval() time.Duration {
	return GitRepoStatusUpdateInterval
}

func (w *RepoInfo) getLastUpdated() *time.Time {
	return w.lastUpdated
}

func (w *RepoInfo) setLastUpdated(t time.Time) {
	w.lastUpdated = &t
}

type BySortOrder []*ui.Gauge

func (a BySortOrder) Len() int           { return len(a) }
func (a BySortOrder) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a BySortOrder) Less(i, j int) bool { return a[i].BorderLabel < a[j].BorderLabel }

func buildColoredStatusStringFromMap(status map[rune]int) string {
	retval := ""

	for _, key := range RepoStatusFieldDefinitionsOrderedKeys {
		count := status[key]

		if count > 0 {
			if retval != "" {
				retval += " "
			}

			retval += fmt.Sprintf("[%c:%d](%s)", RepoStatusFieldDefinitions[key].OutputCharacter, count, RepoStatusFieldDefinitions[key].OutputColorString)
		}
	}

	return retval
}

////////////////////////////////////////////
// Utility: Git Repo List
////////////////////////////////////////////

const GitRepoListUpdateInterval = 30 * time.Second

var HOME = os.ExpandEnv("$HOME")
var CANONHOME = normalizePath(HOME)

type CachedGitRepoList struct {
	repoSearch  map[string]int
	Repos       []RepoInfo
	lastUpdated *time.Time
}

func (w *CachedGitRepoList) getUpdateInterval() time.Duration {
	return GitRepoListUpdateInterval
}

func (w *CachedGitRepoList) getLastUpdated() *time.Time {
	return w.lastUpdated
}

func (w *CachedGitRepoList) setLastUpdated(t time.Time) {
	w.lastUpdated = &t
}

func (w *CachedGitRepoList) update() {
	if shouldUpdate(w) {
		repoPaths := getGitRepositories(w.repoSearch)

		repos := make([]RepoInfo, 0)

		for _, repo := range repoPaths {
			repoInfo := NewRepoInfo(repo)

			repos = append(repos, repoInfo)
		}

		w.Repos = repos
	}

	// Update status for all the repos as well
	for _, r := range w.Repos {
		r.update()
	}
}

func NewCachedGitRepoList(search map[string]int) *CachedGitRepoList {
	// Build it
	w := &CachedGitRepoList{
		repoSearch: search,
		Repos:      make([]RepoInfo, 0),
	}

	w.update()

	return w
}

var cachedGitRepos = NewCachedGitRepoList(GetGitRepoSearchPaths())

// Walks the search directories to look for git folders
// search is a map of directory roots to depths
func getGitRepositories(search map[string]int) []string {
	var retval = make([]string, 0)

	for path, depth := range search {
		gitRepos := getGitRepositoriesForPath(path, depth)

		retval = append(retval, gitRepos...)
	}

	// Sort
	sort.Strings(retval)

	// Uniquify
	// w is where non-matching elements should be written
	// last is the last element we wrote
	// r is the current read pointer
	w := 1
	last := 0
	for r := 1; r < len(retval); r++ {
		// If they're the same, skip it
		if retval[r] == retval[last] {
			continue
		}

		// They're different, write it to the array
		retval[w] = retval[r]

		// Save last pointer
		last = w

		// Advance
		w++
	}

	retval = retval[:w] // slice it to just what we wrote

	return retval
}

func getGitRepositoriesForPath(root string, maxDepth int) []string {
	var retval = walkTreeLookingForGit(root, nil, 0, maxDepth)

	return retval
}

func walkTreeLookingForGit(path string, de *walk.Dirent, curDepth int, maxDepth int) []string {
	// Do we keep going?
	if curDepth <= maxDepth {
		// de is nil the first time through
		if de != nil {
			gitPath := checkAndResolveGitFolder(path, de)

			if gitPath != nil {
				// Got it!
				return []string{*gitPath}
			}
		}

		// Get children
		retval := make([]string, 0)

		kids, err := walk.ReadDirents(path, nil)

		if err != nil {
			log.Printf("Failed to traverse into children of '%v': %v", path, err)
		} else {
			for _, kidDE := range kids {
				if kidDE.IsDir() {
					results := walkTreeLookingForGit(filepath.Join(path, kidDE.Name()), kidDE, curDepth+1, maxDepth)

					retval = append(retval, results...)
				}
			}
		}

		return retval
	} else {
		return []string{}
	}
}

// Returns nil if not a git folder
// Returns a resolved pathname if is a git folder
func checkAndResolveGitFolder(osPathname string, de *walk.Dirent) *string {
	// check name
	if !de.IsDir() {
		return nil
	}

	if de.Name() != ".git" {
		return nil
	}

	path := normalizePath(osPathname)
	return &path
}

////////////////////////////////////////////
// Widget: Git Repos
////////////////////////////////////////////

const MinimumRepoNameWidth = 26
const MinimumRepoBranchesWidth = 37

type GitRepoWidget struct {
	widget      *ui.Table
	lastUpdated *time.Time
}

func NewGitRepoWidget() *GitRepoWidget {
	// Create base element
	e := ui.NewTable()
	e.Border = true
	e.BorderLabel = "Git Repos"
	e.Separator = false

	// Create widget
	w := &GitRepoWidget{
		widget: e,
	}

	w.update()
	w.resize()

	return w
}

func (w *GitRepoWidget) getGridWidget() ui.GridBufferer {
	return w.widget
}

func (w *GitRepoWidget) update() {
	rows := [][]string{}
	height := 2

	// Load repos
	cachedGitRepos.update()

	maxRepoWidth := 0

	for _, repo := range cachedGitRepos.Repos {
		// Figure out max length
		if len(repo.HomePath) > maxRepoWidth {
			maxRepoWidth = len(repo.HomePath)
		}
	}

	if maxRepoWidth < MinimumRepoNameWidth {
		maxRepoWidth = MinimumRepoNameWidth
	}

	for _, repo := range cachedGitRepos.Repos {
		// Make the name all fancy
		pathPad := maxRepoWidth - len(repo.Name)
		path := filepath.Dir(repo.HomePath)

		name := fmt.Sprintf("[%*v%c](fg-cyan)[%v](fg-cyan,fg-bold)", pathPad, path, os.PathSeparator, repo.Name)

		line := []string{name, repo.BranchStatus, repo.Status}

		rows = append(rows, line)
		height++
	}

	w.widget.Rows = rows
	w.widget.Height = height

}

func (w *GitRepoWidget) resize() {
	// Do nothing
}
<|endoftext|>


<commit_before>package main

import (
	"os"
    "fmt"
    "net/http"
    "io/ioutil"
    "strconv"
    "strings"
    "errors"
	"github.com/codegangsta/cli"
    mp "github.com/mackerelio/go-mackerel-plugin"
)

// metric value structure
var graphdef map[string](mp.Graphs) = map[string](mp.Graphs){
    "apache2.workers": mp.Graphs{
        Label: "Apache Workers",
        Unit: "integer",
        Metrics: [](mp.Metrics){
            mp.Metrics{ Name: "busy_workers", Label: "Busy Workers", Diff: false },
            mp.Metrics{ Name: "idle_workers", Label: "Idle Workers", Diff: false },
        },
    },
    "apache2.bytes": mp.Graphs{
        Label: "Apache Bytes",
        Unit: "integer",
        Metrics: [](mp.Metrics){
            mp.Metrics{ Name: "bytes_sent", Label: "Bytes Sent", Diff: false },
        },
    },
    "apache2.cpu": mp.Graphs{
        Label: "Apache CPU Load",
        Unit: "float",
        Metrics: [](mp.Metrics){
            mp.Metrics{ Name: "cpu_load", Label: "CPU Load", Diff: false },
        },
    },
    "apache2.req": mp.Graphs{
        Label: "Apache Requests",
        Unit: "integer",
        Metrics: [](mp.Metrics){
            mp.Metrics{ Name: "requests", Label: "Requests", Diff: false },
        },
    },
    "apache2.scoreboard": mp.Graphs{
        Label: "Apache Scoreboard",
        Unit: "integer",
        Metrics: [](mp.Metrics){
            mp.Metrics{ Name: "score-_", Label: "Waiting for connection", Diff: false },
            mp.Metrics{ Name: "score-S", Label: "Starting up", Diff: false },
            mp.Metrics{ Name: "score-R", Label: "Reading request", Diff: false },
            mp.Metrics{ Name: "scpre-W", Label: "Sending reply", Diff: false },
            mp.Metrics{ Name: "score-K", Label: "Keepalive", Diff: false },
            mp.Metrics{ Name: "score-D", Label: "DNS lookup", Diff: false },
            mp.Metrics{ Name: "score-C", Label: "Closing connection", Diff: false },
            mp.Metrics{ Name: "score-L", Label: "Logging", Diff: false },
            mp.Metrics{ Name: "score-G", Label: "Gracefully finishing", Diff: false },
            mp.Metrics{ Name: "score-I", Label: "Idle cleanup", Diff: false },
            mp.Metrics{ Name: "score-.", Label: "Open slot", Diff: false },
        },
    },
}

// for fetching metrics
type Apache2Plugin struct {
    Host string
    Port uint16
    Path string
    Tempfile string
}

// Graph definition
func ( c Apache2Plugin ) GraphDefinition() map[string](mp.Graphs) {
    return graphdef
}

// main function
func doMain( c *cli.Context ) {

    var apache2 Apache2Plugin

    apache2.Host = c.String( "http_host" )
    apache2.Port = uint16( c.Int( "http_port" ) )
    apache2.Path = c.String( "status_page" )
    apache2.Tempfile = c.String( "tempfile" )

    helper := mp.NewMackerelPlugin( apache2 )

    if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
        helper.OutputDefinitions()
    } else {
        helper.OutputValues()
    }
}

// fetch metrics
func ( c Apache2Plugin ) FetchMetrics() ( map[string]float64, error ){
    data, err := getApache2Metrics( c.Host, c.Port, c.Path )
    if err != nil {
        return nil, err
    }

    stat := make(map[string]float64)
    err2 := parseApache2Status( data, &stat )
    if err2 != nil {
        return nil, err2
    }

    return stat, nil
}

// parsing metrics from server-status?auto
func parseApache2Status( str string, p *map[string]float64 )( error ) {
    Params := map[string]string{
        "Total Accesses": "requests",
        "Total kBytes": "bytes_sent",
        "CPULoad": "cpu_load",
        "BusyWorkers": "busy_workers",
        "IdleWorkers": "idle_workers" }

    for _, line := range strings.Split( str, "\n" ){
        record := strings.Split( line, ":" )
        _, assert := Params[ record[0] ];
        if !assert {
            continue
        }
        var err_parse error
        (*p)[ Params[ record[0] ] ], err_parse = strconv.ParseFloat( strings.Trim( record[1], " " ), 64 )
        if err_parse != nil {
            return err_parse
        }
    }

    if len(*p) == 0 {
        return errors.New( "Status data not found." )
    }

    return nil
}

// Getting apache2 status from server-status module data.
func getApache2Metrics( host string, port uint16, path string )( string, error ){
    uri := "http://" + host + ":" + strconv.FormatUint( uint64( port ), 10 ) + path
    resp, err := http.Get( uri )
    if err != nil {
        return "", err
    }
    if resp.StatusCode != http.StatusOK {
        return "", errors.New( fmt.Sprintf( "HTTP status error: %d", resp.StatusCode ) )
    }
    body, err := ioutil.ReadAll( resp.Body )
    resp.Body.Close()
    if err != nil {
        return "", err
    }
    return string( body[:] ), nil
}

// main
func main() {
	app := cli.NewApp()
	app.Name = "apache2_metrics"
	app.Version = Version
	app.Usage = "Get metrics from apache2."
	app.Author = "Yuichiro Saito"
	app.Email = "saito@heartbeats.jp"
	app.Flags = Flags
    app.Action = doMain

	app.Run(os.Args)
}
<commit_msg>Replace tab to 4 spaces.<commit_after>package main

import (
    "os"
    "fmt"
    "net/http"
    "io/ioutil"
    "strconv"
    "strings"
    "errors"
    "github.com/codegangsta/cli"
    mp "github.com/mackerelio/go-mackerel-plugin"
)

// metric value structure
var graphdef map[string](mp.Graphs) = map[string](mp.Graphs){
    "apache2.workers": mp.Graphs{
        Label: "Apache Workers",
        Unit: "integer",
        Metrics: [](mp.Metrics){
            mp.Metrics{ Name: "busy_workers", Label: "Busy Workers", Diff: false },
            mp.Metrics{ Name: "idle_workers", Label: "Idle Workers", Diff: false },
        },
    },
    "apache2.bytes": mp.Graphs{
        Label: "Apache Bytes",
        Unit: "integer",
        Metrics: [](mp.Metrics){
            mp.Metrics{ Name: "bytes_sent", Label: "Bytes Sent", Diff: false },
        },
    },
    "apache2.cpu": mp.Graphs{
        Label: "Apache CPU Load",
        Unit: "float",
        Metrics: [](mp.Metrics){
            mp.Metrics{ Name: "cpu_load", Label: "CPU Load", Diff: false },
        },
    },
    "apache2.req": mp.Graphs{
        Label: "Apache Requests",
        Unit: "integer",
        Metrics: [](mp.Metrics){
            mp.Metrics{ Name: "requests", Label: "Requests", Diff: false },
        },
    },
    "apache2.scoreboard": mp.Graphs{
        Label: "Apache Scoreboard",
        Unit: "integer",
        Metrics: [](mp.Metrics){
            mp.Metrics{ Name: "score-_", Label: "Waiting for connection", Diff: false },
            mp.Metrics{ Name: "score-S", Label: "Starting up", Diff: false },
            mp.Metrics{ Name: "score-R", Label: "Reading request", Diff: false },
            mp.Metrics{ Name: "scpre-W", Label: "Sending reply", Diff: false },
            mp.Metrics{ Name: "score-K", Label: "Keepalive", Diff: false },
            mp.Metrics{ Name: "score-D", Label: "DNS lookup", Diff: false },
            mp.Metrics{ Name: "score-C", Label: "Closing connection", Diff: false },
            mp.Metrics{ Name: "score-L", Label: "Logging", Diff: false },
            mp.Metrics{ Name: "score-G", Label: "Gracefully finishing", Diff: false },
            mp.Metrics{ Name: "score-I", Label: "Idle cleanup", Diff: false },
            mp.Metrics{ Name: "score-.", Label: "Open slot", Diff: false },
        },
    },
}

// for fetching metrics
type Apache2Plugin struct {
    Host string
    Port uint16
    Path string
    Tempfile string
}

// Graph definition
func ( c Apache2Plugin ) GraphDefinition() map[string](mp.Graphs) {
    return graphdef
}

// main function
func doMain( c *cli.Context ) {

    var apache2 Apache2Plugin

    apache2.Host = c.String( "http_host" )
    apache2.Port = uint16( c.Int( "http_port" ) )
    apache2.Path = c.String( "status_page" )
    apache2.Tempfile = c.String( "tempfile" )

    helper := mp.NewMackerelPlugin( apache2 )

    if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
        helper.OutputDefinitions()
    } else {
        helper.OutputValues()
    }
}

// fetch metrics
func ( c Apache2Plugin ) FetchMetrics() ( map[string]float64, error ){
    data, err := getApache2Metrics( c.Host, c.Port, c.Path )
    if err != nil {
        return nil, err
    }

    stat := make(map[string]float64)
    err2 := parseApache2Status( data, &stat )
    if err2 != nil {
        return nil, err2
    }

    return stat, nil
}

// parsing metrics from server-status?auto
func parseApache2Status( str string, p *map[string]float64 )( error ) {
    Params := map[string]string{
        "Total Accesses": "requests",
        "Total kBytes": "bytes_sent",
        "CPULoad": "cpu_load",
        "BusyWorkers": "busy_workers",
        "IdleWorkers": "idle_workers" }

    for _, line := range strings.Split( str, "\n" ){
        record := strings.Split( line, ":" )
        _, assert := Params[ record[0] ];
        if !assert {
            continue
        }
        var err_parse error
        (*p)[ Params[ record[0] ] ], err_parse = strconv.ParseFloat( strings.Trim( record[1], " " ), 64 )
        if err_parse != nil {
            return err_parse
        }
    }

    if len(*p) == 0 {
        return errors.New( "Status data not found." )
    }

    return nil
}

// Getting apache2 status from server-status module data.
func getApache2Metrics( host string, port uint16, path string )( string, error ){
    uri := "http://" + host + ":" + strconv.FormatUint( uint64( port ), 10 ) + path
    resp, err := http.Get( uri )
    if err != nil {
        return "", err
    }
    if resp.StatusCode != http.StatusOK {
        return "", errors.New( fmt.Sprintf( "HTTP status error: %d", resp.StatusCode ) )
    }
    body, err := ioutil.ReadAll( resp.Body )
    resp.Body.Close()
    if err != nil {
        return "", err
    }
    return string( body[:] ), nil
}

// main
func main() {
    app := cli.NewApp()
    app.Name = "apache2_metrics"
    app.Version = Version
    app.Usage = "Get metrics from apache2."
    app.Author = "Yuichiro Saito"
    app.Email = "saito@heartbeats.jp"
    app.Flags = Flags
    app.Action = doMain

    app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os/exec"
	"regexp"
	"strings"
)

func gitAddSubmodule(repoDir, remoteURI, targetPath string) ([]byte, error) {
	cmd := exec.Command("git", "submodule", "add", "-f", remoteURI, targetPath)
	cmd.Dir = repoDir
	return cmd.CombinedOutput()
}

func gitCheckoutCommit(repoDir, commitHash string) ([]byte, error) {
	cmd := exec.Command("git", "checkout", commitHash)
	cmd.Dir = repoDir
	return cmd.CombinedOutput()
}

var remoteExtractRegexp = regexp.MustCompile(`^([^\s]+)\s+([^\s]+) \(fetch\)`)

func gitGetRemoteURI(repoDir string, allowLocal bool) (string, error) {
	cmd := exec.Command("git", "remote", "-v")
	cmd.Dir = repoDir
	output, err := cmd.Output()
	if err != nil {
		return "", err
	}
	matches := remoteExtractRegexp.FindStringSubmatch(string(output))
	if matches == nil {
		if allowLocal {
			// @TODO : Maybe. Vendoring local repo doesn't actually sound like a good idea, gotta see if there is
			// some real usecases
			panic("Getting local repo URI : not implemented yet")
		}
		err = fmt.Errorf("Could not extract remote URL from %q", repoDir)
		return "", err
	}
	return matches[2], nil
}

func gitGetCurrentCommitHash(repoDir string) (string, error) {
	cmd := exec.Command("git", "rev-parse", "--verify", "HEAD")
	cmd.Dir = repoDir

	output, err := cmd.Output()
	if err != nil {
		return "", err
	}
	return strings.Trim(string(output), "\n"), nil
}
<commit_msg>Check for git executable at initialization<commit_after>package main

import (
	"fmt"
	"os/exec"
	"regexp"
	"strings"
)

var gitCommand string

func init() {
	var err error
	gitCommand, err = exec.LookPath("git")
	if err != nil {
		panic(err)
	}
}

func gitClone(targetPath, remoteURI string) ([]byte, error) {
	cmd := exec.Command(gitCommand, "clone", remoteURI, targetPath)
	return cmd.CombinedOutput()
}

func gitAddSubmodule(repoDir, remoteURI, targetPath string) ([]byte, error) {
	cmd := exec.Command(gitCommand, "submodule", "add", "-f", remoteURI, targetPath)
	cmd.Dir = repoDir
	return cmd.CombinedOutput()
}

func gitCheckoutCommit(repoDir, commitHash string) ([]byte, error) {
	cmd := exec.Command(gitCommand, "checkout", commitHash)
	cmd.Dir = repoDir
	return cmd.CombinedOutput()
}

var remoteExtractRegexp = regexp.MustCompile(`^([^\s]+)\s+([^\s]+) \(fetch\)`)

func gitGetRemoteURI(repoDir string, allowLocal bool) (string, error) {
	cmd := exec.Command(gitCommand, "remote", "-v")
	cmd.Dir = repoDir
	output, err := cmd.Output()
	if err != nil {
		return "", err
	}
	matches := remoteExtractRegexp.FindStringSubmatch(string(output))
	if matches == nil {
		if allowLocal {
			// @TODO : Maybe. Vendoring local repo doesn't actually sound like a good idea, gotta see if there is
			// some real usecases
			panic("Getting local repo URI : not implemented yet")
		}
		err = fmt.Errorf("Could not extract remote URL from %q", repoDir)
		return "", err
	}
	return matches[2], nil
}

func gitGetCurrentCommitHash(repoDir string) (string, error) {
	cmd := exec.Command(gitCommand, "rev-parse", "--verify", "HEAD")
	cmd.Dir = repoDir

	output, err := cmd.Output()
	if err != nil {
		return "", err
	}
	return strings.Trim(string(output), "\n"), nil
}
<|endoftext|>


<commit_before>package mpredis

import (
	"flag"
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/fzzy/radix/redis"
	mp "github.com/mackerelio/go-mackerel-plugin-helper"
	"github.com/mackerelio/golib/logging"
)

var logger = logging.GetLogger("metrics.plugin.redis")

// RedisPlugin mackerel plugin for Redis
type RedisPlugin struct {
	Host     string
	Port     string
	Password string
	Socket   string
	Prefix   string
	Timeout  int
	Tempfile string
}

func authenticateByPassword(c *redis.Client, password string) error {
	if r := c.Cmd("AUTH", password); r.Err != nil {
		logger.Errorf("Failed to authenticate. %s", r.Err)
		return r.Err
	}
	return nil
}

func fetchPercentageOfMemory(c *redis.Client, stat map[string]interface{}) error {
	r := c.Cmd("CONFIG", "GET", "maxmemory")
	if r.Err != nil {
		logger.Errorf("Failed to run `CONFIG GET maxmemory` command. %s", r.Err)
		return r.Err
	}

	res, err := r.Hash()
	if err != nil {
		logger.Errorf("Failed to fetch maxmemory. %s", err)
		return err
	}

	maxsize, err := strconv.ParseFloat(res["maxmemory"], 64)
	if err != nil {
		logger.Errorf("Failed to parse maxmemory. %s", err)
		return err
	}

	if maxsize == 0.0 {
		stat["percentage_of_memory"] = 0.0
	} else {
		stat["percentage_of_memory"] = 100.0 * stat["used_memory"].(float64) / maxsize
	}

	return nil
}

func fetchPercentageOfClients(c *redis.Client, stat map[string]interface{}) error {
	r := c.Cmd("CONFIG", "GET", "maxclients")
	if r.Err != nil {
		logger.Errorf("Failed to run `CONFIG GET maxclients` command. %s", r.Err)
		return r.Err
	}

	res, err := r.Hash()
	if err != nil {
		logger.Errorf("Failed to fetch maxclients. %s", err)
		return err
	}

	maxsize, err := strconv.ParseFloat(res["maxclients"], 64)
	if err != nil {
		logger.Errorf("Failed to parse maxclients. %s", err)
		return err
	}

	stat["percentage_of_clients"] = 100.0 * stat["connected_clients"].(float64) / maxsize

	return nil
}

func calculateCapacity(c *redis.Client, stat map[string]interface{}) error {
	if err := fetchPercentageOfMemory(c, stat); err != nil {
		return err
	}
	return fetchPercentageOfClients(c, stat)
}

// MetricKeyPrefix interface for PluginWithPrefix
func (m RedisPlugin) MetricKeyPrefix() string {
	if m.Prefix == "" {
		m.Prefix = "redis"
	}
	return m.Prefix
}

// FetchMetrics interface for mackerelplugin
func (m RedisPlugin) FetchMetrics() (map[string]interface{}, error) {
	network := "tcp"
	target := fmt.Sprintf("%s:%s", m.Host, m.Port)
	if m.Socket != "" {
		target = m.Socket
		network = "unix"
	}
	c, err := redis.DialTimeout(network, target, time.Duration(m.Timeout)*time.Second)
	if err != nil {
		logger.Errorf("Failed to connect redis. %s", err)
		return nil, err
	}
	defer c.Close()

	if m.Password != "" {
		if err = authenticateByPassword(c, m.Password); err != nil {
			return nil, err
		}
	}

	r := c.Cmd("info")
	if r.Err != nil {
		logger.Errorf("Failed to run info command. %s", r.Err)
		return nil, r.Err
	}
	str, err := r.Str()
	if err != nil {
		logger.Errorf("Failed to fetch information. %s", err)
		return nil, err
	}

	stat := make(map[string]interface{})

	keysStat := 0.0
	expiresStat := 0.0

	for _, line := range strings.Split(str, "\r\n") {
		if line == "" {
			continue
		}
		if re, _ := regexp.MatchString("^#", line); re {
			continue
		}

		record := strings.SplitN(line, ":", 2)
		if len(record) < 2 {
			continue
		}
		key, value := record[0], record[1]

		if re, _ := regexp.MatchString("^slave", key); re {
			kv := strings.SplitN(value, ",", 5)
			_, _, _, _, lag := kv[0], kv[1], kv[2], kv[3], kv[4]
			lagKv := strings.SplitN(lag, "=", 2)
			lagFv, err := strconv.ParseFloat(lagKv[1], 64)
			if err != nil {
				logger.Warningf("Failed to parse slaves. %s", err)
			}
			stat[fmt.Sprintf("%s_lag", key)] = lagFv
			continue
		}

		if re, _ := regexp.MatchString("^db", key); re {
			kv := strings.SplitN(value, ",", 3)
			keys, expires := kv[0], kv[1]

			keysKv := strings.SplitN(keys, "=", 2)
			keysFv, err := strconv.ParseFloat(keysKv[1], 64)
			if err != nil {
				logger.Warningf("Failed to parse db keys. %s", err)
			}
			keysStat += keysFv

			expiresKv := strings.SplitN(expires, "=", 2)
			expiresFv, err := strconv.ParseFloat(expiresKv[1], 64)
			if err != nil {
				logger.Warningf("Failed to parse db expires. %s", err)
			}
			expiresStat += expiresFv

			continue
		}

		stat[key], err = strconv.ParseFloat(value, 64)
		if err != nil {
			continue
		}
	}

	stat["keys"] = keysStat
	stat["expires"] = expiresStat

	if _, ok := stat["keys"]; !ok {
		stat["keys"] = 0
	}
	if _, ok := stat["expires"]; !ok {
		stat["expires"] = 0
	}

	if _, ok := stat["expired_keys"]; ok {
		stat["expired"] = stat["expired_keys"]
	} else {
		stat["expired"] = 0.0
	}

	if err := calculateCapacity(c, stat); err != nil {
		logger.Infof("Failed to calculate capacity. (The cause may be that AWS Elasticache Redis has no `CONFIG` command.) Skip these metrics. %s", err)
	}

	return stat, nil
}

// GraphDefinition interface for mackerelplugin
func (m RedisPlugin) GraphDefinition() map[string]mp.Graphs {
	labelPrefix := strings.Title(m.Prefix)

	var graphdef = map[string]mp.Graphs{
		"queries": {
			Label: (labelPrefix + " Queries"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "total_commands_processed", Label: "Queries", Diff: true},
			},
		},
		"connections": {
			Label: (labelPrefix + " Connections"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "total_connections_received", Label: "Connections", Diff: true, Stacked: true},
				{Name: "rejected_connections", Label: "Rejected Connections", Diff: true, Stacked: true},
			},
		},
		"clients": {
			Label: (labelPrefix + " Clients"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "connected_clients", Label: "Connected Clients", Diff: false, Stacked: true},
				{Name: "blocked_clients", Label: "Blocked Clients", Diff: false, Stacked: true},
				{Name: "connected_slaves", Label: "Connected Slaves", Diff: false, Stacked: true},
			},
		},
		"keys": {
			Label: (labelPrefix + " Keys"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "keys", Label: "Keys", Diff: false},
				{Name: "expires", Label: "Keys with expiration", Diff: false},
				{Name: "expired", Label: "Expired Keys", Diff: false},
			},
		},
		"keyspace": {
			Label: (labelPrefix + " Keyspace"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "keyspace_hits", Label: "Keyspace Hits", Diff: true},
				{Name: "keyspace_misses", Label: "Keyspace Missed", Diff: true},
			},
		},
		"memory": {
			Label: (labelPrefix + " Memory"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "used_memory", Label: "Used Memory", Diff: false},
				{Name: "used_memory_rss", Label: "Used Memory RSS", Diff: false},
				{Name: "used_memory_peak", Label: "Used Memory Peak", Diff: false},
				{Name: "used_memory_lua", Label: "Used Memory Lua engine", Diff: false},
			},
		},
		"capacity": {
			Label: (labelPrefix + " Capacity"),
			Unit:  "percentage",
			Metrics: []mp.Metrics{
				{Name: "percentage_of_memory", Label: "Percentage of memory", Diff: false},
				{Name: "percentage_of_clients", Label: "Percentage of clients", Diff: false},
			},
		},
	}

	network := "tcp"
	target := fmt.Sprintf("%s:%s", m.Host, m.Port)
	if m.Socket != "" {
		target = m.Socket
		network = "unix"
	}

	c, err := redis.DialTimeout(network, target, time.Duration(m.Timeout)*time.Second)
	if err != nil {
		logger.Errorf("Failed to connect redis. %s", err)
		return nil
	}
	defer c.Close()

	if m.Password != "" {
		if err = authenticateByPassword(c, m.Password); err != nil {
			return nil
		}
	}

	r := c.Cmd("info")
	if r.Err != nil {
		logger.Errorf("Failed to run info command. %s", r.Err)
		return nil
	}
	str, err := r.Str()
	if err != nil {
		logger.Errorf("Failed to fetch information. %s", err)
		return nil
	}

	var metricsLag []mp.Metrics
	for _, line := range strings.Split(str, "\r\n") {
		if line == "" {
			continue
		}

		record := strings.SplitN(line, ":", 2)
		if len(record) < 2 {
			continue
		}
		key, _ := record[0], record[1]

		if re, _ := regexp.MatchString("^slave\\d+", key); re {
			metricsLag = append(metricsLag, mp.Metrics{Name: fmt.Sprintf("%s_lag", key), Label: fmt.Sprintf("Replication lag to %s", key), Diff: false})
		}
	}

	graphdef["lag"] = mp.Graphs{
		Label:   (labelPrefix + " Slave Lag"),
		Unit:    "seconds",
		Metrics: metricsLag,
	}

	return graphdef
}

// Do the plugin
func Do() {
	optHost := flag.String("host", "localhost", "Hostname")
	optPort := flag.String("port", "6379", "Port")
	optPassowrd := flag.String("password", "", "Password")
	optSocket := flag.String("socket", "", "Server socket (overrides host and port)")
	optPrefix := flag.String("metric-key-prefix", "redis", "Metric key prefix")
	optTimeout := flag.Int("timeout", 5, "Timeout")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	redis := RedisPlugin{
		Timeout: *optTimeout,
		Prefix:  *optPrefix,
	}
	if *optSocket != "" {
		redis.Socket = *optSocket
	} else {
		redis.Host = *optHost
		redis.Port = *optPort
		redis.Password = *optPassowrd
	}
	helper := mp.NewMackerelPlugin(redis)
	helper.Tempfile = *optTempfile

	helper.Run()
}
<commit_msg>Add slave offset delay metrics<commit_after>package mpredis

import (
	"flag"
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/fzzy/radix/redis"
	mp "github.com/mackerelio/go-mackerel-plugin-helper"
	"github.com/mackerelio/golib/logging"
)

var logger = logging.GetLogger("metrics.plugin.redis")

// RedisPlugin mackerel plugin for Redis
type RedisPlugin struct {
	Host     string
	Port     string
	Password string
	Socket   string
	Prefix   string
	Timeout  int
	Tempfile string
}

func authenticateByPassword(c *redis.Client, password string) error {
	if r := c.Cmd("AUTH", password); r.Err != nil {
		logger.Errorf("Failed to authenticate. %s", r.Err)
		return r.Err
	}
	return nil
}

func fetchPercentageOfMemory(c *redis.Client, stat map[string]interface{}) error {
	r := c.Cmd("CONFIG", "GET", "maxmemory")
	if r.Err != nil {
		logger.Errorf("Failed to run `CONFIG GET maxmemory` command. %s", r.Err)
		return r.Err
	}

	res, err := r.Hash()
	if err != nil {
		logger.Errorf("Failed to fetch maxmemory. %s", err)
		return err
	}

	maxsize, err := strconv.ParseFloat(res["maxmemory"], 64)
	if err != nil {
		logger.Errorf("Failed to parse maxmemory. %s", err)
		return err
	}

	if maxsize == 0.0 {
		stat["percentage_of_memory"] = 0.0
	} else {
		stat["percentage_of_memory"] = 100.0 * stat["used_memory"].(float64) / maxsize
	}

	return nil
}

func fetchPercentageOfClients(c *redis.Client, stat map[string]interface{}) error {
	r := c.Cmd("CONFIG", "GET", "maxclients")
	if r.Err != nil {
		logger.Errorf("Failed to run `CONFIG GET maxclients` command. %s", r.Err)
		return r.Err
	}

	res, err := r.Hash()
	if err != nil {
		logger.Errorf("Failed to fetch maxclients. %s", err)
		return err
	}

	maxsize, err := strconv.ParseFloat(res["maxclients"], 64)
	if err != nil {
		logger.Errorf("Failed to parse maxclients. %s", err)
		return err
	}

	stat["percentage_of_clients"] = 100.0 * stat["connected_clients"].(float64) / maxsize

	return nil
}

func calculateCapacity(c *redis.Client, stat map[string]interface{}) error {
	if err := fetchPercentageOfMemory(c, stat); err != nil {
		return err
	}
	return fetchPercentageOfClients(c, stat)
}

// MetricKeyPrefix interface for PluginWithPrefix
func (m RedisPlugin) MetricKeyPrefix() string {
	if m.Prefix == "" {
		m.Prefix = "redis"
	}
	return m.Prefix
}

// FetchMetrics interface for mackerelplugin
func (m RedisPlugin) FetchMetrics() (map[string]interface{}, error) {
	network := "tcp"
	target := fmt.Sprintf("%s:%s", m.Host, m.Port)
	if m.Socket != "" {
		target = m.Socket
		network = "unix"
	}
	c, err := redis.DialTimeout(network, target, time.Duration(m.Timeout)*time.Second)
	if err != nil {
		logger.Errorf("Failed to connect redis. %s", err)
		return nil, err
	}
	defer c.Close()

	if m.Password != "" {
		if err = authenticateByPassword(c, m.Password); err != nil {
			return nil, err
		}
	}

	r := c.Cmd("info")
	if r.Err != nil {
		logger.Errorf("Failed to run info command. %s", r.Err)
		return nil, r.Err
	}
	str, err := r.Str()
	if err != nil {
		logger.Errorf("Failed to fetch information. %s", err)
		return nil, err
	}

	stat := make(map[string]interface{})

	keysStat := 0.0
	expiresStat := 0.0
	var slaves []string

	for _, line := range strings.Split(str, "\r\n") {
		if line == "" {
			continue
		}
		if re, _ := regexp.MatchString("^#", line); re {
			continue
		}

		record := strings.SplitN(line, ":", 2)
		if len(record) < 2 {
			continue
		}
		key, value := record[0], record[1]

		if re, _ := regexp.MatchString("^slave", key); re {
			slaves = append(slaves, key)
			kv := strings.SplitN(value, ",", 5)
			_, _, _, offset, lag := kv[0], kv[1], kv[2], kv[3], kv[4]
			offsetKv := strings.SplitN(offset, "=", 2)
			offsetFv, err := strconv.ParseFloat(offsetKv[1], 64)
			if err != nil {
				logger.Warningf("Failed to parse slaves. %s", err)
			}
			stat[fmt.Sprintf("%s_offset_delay", key)] = offsetFv
			lagKv := strings.SplitN(lag, "=", 2)
			lagFv, err := strconv.ParseFloat(lagKv[1], 64)
			if err != nil {
				logger.Warningf("Failed to parse slaves. %s", err)
			}
			stat[fmt.Sprintf("%s_lag", key)] = lagFv
			continue
		}

		if re, _ := regexp.MatchString("^db", key); re {
			kv := strings.SplitN(value, ",", 3)
			keys, expires := kv[0], kv[1]

			keysKv := strings.SplitN(keys, "=", 2)
			keysFv, err := strconv.ParseFloat(keysKv[1], 64)
			if err != nil {
				logger.Warningf("Failed to parse db keys. %s", err)
			}
			keysStat += keysFv

			expiresKv := strings.SplitN(expires, "=", 2)
			expiresFv, err := strconv.ParseFloat(expiresKv[1], 64)
			if err != nil {
				logger.Warningf("Failed to parse db expires. %s", err)
			}
			expiresStat += expiresFv

			continue
		}

		stat[key], err = strconv.ParseFloat(value, 64)
		if err != nil {
			continue
		}
	}

	stat["keys"] = keysStat
	stat["expires"] = expiresStat

	if _, ok := stat["keys"]; !ok {
		stat["keys"] = 0
	}
	if _, ok := stat["expires"]; !ok {
		stat["expires"] = 0
	}

	if _, ok := stat["expired_keys"]; ok {
		stat["expired"] = stat["expired_keys"]
	} else {
		stat["expired"] = 0.0
	}

	if err := calculateCapacity(c, stat); err != nil {
		logger.Infof("Failed to calculate capacity. (The cause may be that AWS Elasticache Redis has no `CONFIG` command.) Skip these metrics. %s", err)
	}

	for _, slave := range slaves {
		stat[fmt.Sprintf("%s_offset_delay", slave)] = stat["master_repl_offset"].(float64) - stat[fmt.Sprintf("%s_offset_delay", slave)].(float64)
	}

	return stat, nil
}

// GraphDefinition interface for mackerelplugin
func (m RedisPlugin) GraphDefinition() map[string]mp.Graphs {
	labelPrefix := strings.Title(m.Prefix)

	var graphdef = map[string]mp.Graphs{
		"queries": {
			Label: (labelPrefix + " Queries"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "total_commands_processed", Label: "Queries", Diff: true},
			},
		},
		"connections": {
			Label: (labelPrefix + " Connections"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "total_connections_received", Label: "Connections", Diff: true, Stacked: true},
				{Name: "rejected_connections", Label: "Rejected Connections", Diff: true, Stacked: true},
			},
		},
		"clients": {
			Label: (labelPrefix + " Clients"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "connected_clients", Label: "Connected Clients", Diff: false, Stacked: true},
				{Name: "blocked_clients", Label: "Blocked Clients", Diff: false, Stacked: true},
				{Name: "connected_slaves", Label: "Connected Slaves", Diff: false, Stacked: true},
			},
		},
		"keys": {
			Label: (labelPrefix + " Keys"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "keys", Label: "Keys", Diff: false},
				{Name: "expires", Label: "Keys with expiration", Diff: false},
				{Name: "expired", Label: "Expired Keys", Diff: false},
			},
		},
		"keyspace": {
			Label: (labelPrefix + " Keyspace"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "keyspace_hits", Label: "Keyspace Hits", Diff: true},
				{Name: "keyspace_misses", Label: "Keyspace Missed", Diff: true},
			},
		},
		"memory": {
			Label: (labelPrefix + " Memory"),
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "used_memory", Label: "Used Memory", Diff: false},
				{Name: "used_memory_rss", Label: "Used Memory RSS", Diff: false},
				{Name: "used_memory_peak", Label: "Used Memory Peak", Diff: false},
				{Name: "used_memory_lua", Label: "Used Memory Lua engine", Diff: false},
			},
		},
		"capacity": {
			Label: (labelPrefix + " Capacity"),
			Unit:  "percentage",
			Metrics: []mp.Metrics{
				{Name: "percentage_of_memory", Label: "Percentage of memory", Diff: false},
				{Name: "percentage_of_clients", Label: "Percentage of clients", Diff: false},
			},
		},
	}

	network := "tcp"
	target := fmt.Sprintf("%s:%s", m.Host, m.Port)
	if m.Socket != "" {
		target = m.Socket
		network = "unix"
	}

	c, err := redis.DialTimeout(network, target, time.Duration(m.Timeout)*time.Second)
	if err != nil {
		logger.Errorf("Failed to connect redis. %s", err)
		return nil
	}
	defer c.Close()

	if m.Password != "" {
		if err = authenticateByPassword(c, m.Password); err != nil {
			return nil
		}
	}

	r := c.Cmd("info")
	if r.Err != nil {
		logger.Errorf("Failed to run info command. %s", r.Err)
		return nil
	}
	str, err := r.Str()
	if err != nil {
		logger.Errorf("Failed to fetch information. %s", err)
		return nil
	}

	var metricsLag []mp.Metrics
	var metricsOffsetDelay []mp.Metrics
	for _, line := range strings.Split(str, "\r\n") {
		if line == "" {
			continue
		}

		record := strings.SplitN(line, ":", 2)
		if len(record) < 2 {
			continue
		}
		key, _ := record[0], record[1]

		if re, _ := regexp.MatchString("^slave\\d+", key); re {
			metricsLag = append(metricsLag, mp.Metrics{Name: fmt.Sprintf("%s_lag", key), Label: fmt.Sprintf("Replication lag to %s", key), Diff: false})
			metricsOffsetDelay = append(metricsOffsetDelay, mp.Metrics{Name: fmt.Sprintf("%s_offset_delay", key), Label: fmt.Sprintf("Offset delay to %s", key), Diff: false})
		}
	}

	graphdef["lag"] = mp.Graphs{
		Label:   (labelPrefix + " Slave Lag"),
		Unit:    "seconds",
		Metrics: metricsLag,
	}
	graphdef["offset_delay"] = mp.Graphs{
		Label:   (labelPrefix + " Slave Offset Delay"),
		Unit:    "count",
		Metrics: metricsOffsetDelay,
	}

	return graphdef
}

// Do the plugin
func Do() {
	optHost := flag.String("host", "localhost", "Hostname")
	optPort := flag.String("port", "6379", "Port")
	optPassowrd := flag.String("password", "", "Password")
	optSocket := flag.String("socket", "", "Server socket (overrides host and port)")
	optPrefix := flag.String("metric-key-prefix", "redis", "Metric key prefix")
	optTimeout := flag.Int("timeout", 5, "Timeout")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	redis := RedisPlugin{
		Timeout: *optTimeout,
		Prefix:  *optPrefix,
	}
	if *optSocket != "" {
		redis.Socket = *optSocket
	} else {
		redis.Host = *optHost
		redis.Port = *optPort
		redis.Password = *optPassowrd
	}
	helper := mp.NewMackerelPlugin(redis)
	helper.Tempfile = *optTempfile

	helper.Run()
}
<|endoftext|>


<commit_before>package sdl

import "unsafe"

// SWYUVTexture is the software implementation of the YUV texture support.
type SWYUVTexture struct {
	Format       uint32
	TargetFormat uint32
	W            int
	H            int
	Pixels       *uint8
	ColorTab     *int
	Rgb2Pix      *uint32
	Display1X    unsafe.Pointer
	Display2x    unsafe.Pointer
	Pitches      [3]uint16
	Planes       *[3]uint8
	Stretch      *Surface
	Display      *Surface
}
<commit_msg>sdl: Remove yuv_sw_c.go<commit_after><|endoftext|>


<commit_before>package sdr

import "math"

type goertzel struct {
	coeff    float32
	cos, sin float32
	q1, q2   float32
}

type Goertzel struct {
	freq []*goertzel
	mag  []float32
	cplx []complex64
}

func NewGoertzel(targetFreqs []int, sampleRate, blockSize int) *Goertzel {
	freq := make([]*goertzel, len(targetFreqs))
	for i, f := range targetFreqs {
		k := int(0.5 + float64(blockSize*f)/float64(sampleRate))
		w := 2.0 * math.Pi * float64(k) / float64(blockSize)

		// norm := float64(f) / float64(sampleRate)
		// w := 2.0 * math.Pi * norm

		sin := float32(math.Sin(w))
		cos := float32(math.Cos(w))
		freq[i] = &goertzel{
			coeff: 2.0 * cos,
			cos:   cos,
			sin:   sin,
		}
	}
	return &Goertzel{
		freq: freq,
		mag:  make([]float32, len(targetFreqs)),
		cplx: make([]complex64, len(targetFreqs)),
	}
}

func (g *Goertzel) Reset() {
	for _, freq := range g.freq {
		freq.q1 = 0.0
		freq.q2 = 0.0
	}
}

func (g *Goertzel) Feed(samples []float32) {
	for _, samp := range samples {
		for _, freq := range g.freq {
			q0 := freq.coeff*freq.q1 - freq.q2 + samp
			freq.q2 = freq.q1
			freq.q1 = q0
		}
	}
}

func (g *Goertzel) Magnitude() []float32 {
	for i, freq := range g.freq {
		g.mag[i] = freq.q1*freq.q1 + freq.q2*freq.q2 - freq.q1*freq.q2*freq.coeff
	}
	return g.mag
}

func (g *Goertzel) IQ() []complex64 {
	for i, freq := range g.freq {
		g.cplx[i] = complex(freq.q1-freq.q2*freq.cos, freq.q2*freq.sin)
	}
	return g.cplx
}
<commit_msg>Rename Goertzel.IQ to Complex<commit_after>package sdr

import "math"

type goertzel struct {
	coeff    float32
	cos, sin float32
	q1, q2   float32
}

type Goertzel struct {
	freq []*goertzel
	mag  []float32
	cplx []complex64
}

func NewGoertzel(targetFreqs []int, sampleRate, blockSize int) *Goertzel {
	freq := make([]*goertzel, len(targetFreqs))
	for i, f := range targetFreqs {
		k := int(0.5 + float64(blockSize*f)/float64(sampleRate))
		w := 2.0 * math.Pi * float64(k) / float64(blockSize)

		// norm := float64(f) / float64(sampleRate)
		// w := 2.0 * math.Pi * norm

		sin := float32(math.Sin(w))
		cos := float32(math.Cos(w))
		freq[i] = &goertzel{
			coeff: 2.0 * cos,
			cos:   cos,
			sin:   sin,
		}
	}
	return &Goertzel{
		freq: freq,
		mag:  make([]float32, len(targetFreqs)),
		cplx: make([]complex64, len(targetFreqs)),
	}
}

func (g *Goertzel) Reset() {
	for _, freq := range g.freq {
		freq.q1 = 0.0
		freq.q2 = 0.0
	}
}

func (g *Goertzel) Feed(samples []float32) {
	for _, samp := range samples {
		for _, freq := range g.freq {
			q0 := freq.coeff*freq.q1 - freq.q2 + samp
			freq.q2 = freq.q1
			freq.q1 = q0
		}
	}
}

func (g *Goertzel) Magnitude() []float32 {
	for i, freq := range g.freq {
		g.mag[i] = freq.q1*freq.q1 + freq.q2*freq.q2 - freq.q1*freq.q2*freq.coeff
	}
	return g.mag
}

func (g *Goertzel) Complex() []complex64 {
	for i, freq := range g.freq {
		g.cplx[i] = complex(freq.q1-freq.q2*freq.cos, freq.q2*freq.sin)
	}
	return g.cplx
}
<|endoftext|>


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

// +build go1.3

package lxdclient

import (
	"github.com/juju/errors"
	lxdshared "github.com/lxc/lxd/shared"
)

const (
	// remoteLocalName is a specific remote name in the default LXD config.
	// See https://github.com/lxc/lxd/blob/master/config.go:DefaultRemotes.
	remoteLocalName  = "local"
	remoteIDForLocal = remoteLocalName
)

// Local is LXD's default "remote". Essentially it is an unencrypted,
// unauthenticated connection to localhost over a unix socket.
// However it does require users to be in the lxd group.
var Local = Remote{
	Name: remoteLocalName,
	Host: "", // If Host is empty we will translate it into the local Unix socket
	// No certificates are used when connecting to the Unix socket
	Protocol:      LXDProtocol,
	Cert:          nil,
	ServerPEMCert: "",
}

type Protocol string

const (
	LXDProtocol           Protocol = "lxd"
	SimplestreamsProtocol Protocol = "simplestreams"
)

var CloudImagesRemote = Remote{
	Name:          "cloud-images.ubuntu.com",
	Host:          "https://cloud-images.ubuntu.com/releases",
	Protocol:      SimplestreamsProtocol,
	Cert:          nil,
	ServerPEMCert: "",
}

var generateCertificate = lxdshared.GenerateMemCert
var DefaultImageSources = []Remote{CloudImagesRemote}

// Remote describes a LXD "remote" server for a client. In
// particular it holds the information needed for the client
// to connect to the remote.
type Remote struct {
	// Name is a label for this remote.
	Name string

	// Host identifies the host to which the client should connect.
	// An empty string is interpreted as:
	//   "localhost over a unix socket (unencrypted)".
	Host string

	// Protocol indicates whether this Remote is accessed via the normal
	// "LXD" protocol, or whether it is a Simplestreams source. The value
	// is only useful for Remotes that are image sources
	Protocol Protocol

	// Cert holds the TLS certificate data for the client to use.
	Cert *Cert

	// ServerPEMCert is the certificate to be supplied as the acceptable
	// server certificate when connecting to the remote.
	ServerPEMCert string
}

// isLocal determines if the remote is the implicit "local" remote,
// an unencrypted, unauthenticated unix socket to a locally running LXD.
func (r Remote) isLocal() bool {
	return r.Host == Local.Host
}

// ID identifies the remote to the raw LXD client code. For the
// non-local case this is Remote.Name. For the local case it is the
// remote name that LXD special-cases for the local unix socket.
func (r Remote) ID() string {
	if r.isLocal() {
		return remoteIDForLocal
	}
	return r.Name
}

// WithDefaults updates a copy of the remote with default values
// where needed.
func (r Remote) WithDefaults() (Remote, error) {
	// Note that r is a value receiver, so it is an implicit copy.

	if r.isLocal() {
		return r.withLocalDefaults(), nil
	}

	if r.Protocol == "" {
		r.Protocol = LXDProtocol
	}

	if r.Cert == nil {
		certPEM, keyPEM, err := generateCertificate()
		if err != nil {
			return r, errors.Trace(err)
		}
		cert := NewCert(certPEM, keyPEM)
		r.Cert = &cert
	}

	cert, err := r.Cert.WithDefaults()
	if err != nil {
		return r, errors.Trace(err)
	}
	r.Cert = &cert

	return r, nil
}

func (r Remote) withLocalDefaults() Remote {
	if r.Name == "" {
		r.Name = remoteLocalName
	}
	if r.Protocol == "" {
		r.Protocol = LXDProtocol
	}

	return r
}

// Validate checks the Remote fields for invalid values.
func (r Remote) Validate() error {
	if r.Name == "" {
		return errors.NotValidf("remote missing name,")
	}

	if r.isLocal() {
		if err := r.validateLocal(); err != nil {
			return errors.Trace(err)
		}
		return nil
	}

	if r.Protocol == "" {
		return errors.NotValidf("missing Protocol")
	}
	if r.Protocol != LXDProtocol && r.Protocol != SimplestreamsProtocol {
		return errors.NotValidf("unknown Protocol %q", r.Protocol)
	}

	// r.Cert is allowed to be nil for Public remotes
	if r.Cert != nil {
		if err := r.Cert.Validate(); err != nil {
			return errors.Trace(err)
		}
	}

	return nil
}

func (r Remote) validateLocal() error {
	if r.Cert != nil {
		return errors.NotValidf("hostless remote with cert")
	}
	if r.Protocol != LXDProtocol {
		return errors.NotValidf("localhost always talks LXD protocol not: %s", r.Protocol)
	}

	return nil
}
<commit_msg>lxd: fall back to the daily remote if nothing is in releases<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// +build go1.3

package lxdclient

import (
	"github.com/juju/errors"
	lxdshared "github.com/lxc/lxd/shared"
)

const (
	// remoteLocalName is a specific remote name in the default LXD config.
	// See https://github.com/lxc/lxd/blob/master/config.go:DefaultRemotes.
	remoteLocalName  = "local"
	remoteIDForLocal = remoteLocalName
)

// Local is LXD's default "remote". Essentially it is an unencrypted,
// unauthenticated connection to localhost over a unix socket.
// However it does require users to be in the lxd group.
var Local = Remote{
	Name: remoteLocalName,
	Host: "", // If Host is empty we will translate it into the local Unix socket
	// No certificates are used when connecting to the Unix socket
	Protocol:      LXDProtocol,
	Cert:          nil,
	ServerPEMCert: "",
}

type Protocol string

const (
	LXDProtocol           Protocol = "lxd"
	SimplestreamsProtocol Protocol = "simplestreams"
)

var CloudImagesRemote = Remote{
	Name:          "cloud-images.ubuntu.com",
	Host:          "https://cloud-images.ubuntu.com/releases",
	Protocol:      SimplestreamsProtocol,
	Cert:          nil,
	ServerPEMCert: "",
}

var CloudImagesDailyRemote = Remote{
	Name:		"cloud-images.ubuntu.com",
	Host:		"https://cloud-images.ubuntu.com/daily",
	Protocol:	SimplestreamsProtocol,
	Cert:		nil,
	ServerPEMCert:  "",
}

var generateCertificate = lxdshared.GenerateMemCert
var DefaultImageSources = []Remote{CloudImagesRemote, CloudImagesDailyRemote}

// Remote describes a LXD "remote" server for a client. In
// particular it holds the information needed for the client
// to connect to the remote.
type Remote struct {
	// Name is a label for this remote.
	Name string

	// Host identifies the host to which the client should connect.
	// An empty string is interpreted as:
	//   "localhost over a unix socket (unencrypted)".
	Host string

	// Protocol indicates whether this Remote is accessed via the normal
	// "LXD" protocol, or whether it is a Simplestreams source. The value
	// is only useful for Remotes that are image sources
	Protocol Protocol

	// Cert holds the TLS certificate data for the client to use.
	Cert *Cert

	// ServerPEMCert is the certificate to be supplied as the acceptable
	// server certificate when connecting to the remote.
	ServerPEMCert string
}

// isLocal determines if the remote is the implicit "local" remote,
// an unencrypted, unauthenticated unix socket to a locally running LXD.
func (r Remote) isLocal() bool {
	return r.Host == Local.Host
}

// ID identifies the remote to the raw LXD client code. For the
// non-local case this is Remote.Name. For the local case it is the
// remote name that LXD special-cases for the local unix socket.
func (r Remote) ID() string {
	if r.isLocal() {
		return remoteIDForLocal
	}
	return r.Name
}

// WithDefaults updates a copy of the remote with default values
// where needed.
func (r Remote) WithDefaults() (Remote, error) {
	// Note that r is a value receiver, so it is an implicit copy.

	if r.isLocal() {
		return r.withLocalDefaults(), nil
	}

	if r.Protocol == "" {
		r.Protocol = LXDProtocol
	}

	if r.Cert == nil {
		certPEM, keyPEM, err := generateCertificate()
		if err != nil {
			return r, errors.Trace(err)
		}
		cert := NewCert(certPEM, keyPEM)
		r.Cert = &cert
	}

	cert, err := r.Cert.WithDefaults()
	if err != nil {
		return r, errors.Trace(err)
	}
	r.Cert = &cert

	return r, nil
}

func (r Remote) withLocalDefaults() Remote {
	if r.Name == "" {
		r.Name = remoteLocalName
	}
	if r.Protocol == "" {
		r.Protocol = LXDProtocol
	}

	return r
}

// Validate checks the Remote fields for invalid values.
func (r Remote) Validate() error {
	if r.Name == "" {
		return errors.NotValidf("remote missing name,")
	}

	if r.isLocal() {
		if err := r.validateLocal(); err != nil {
			return errors.Trace(err)
		}
		return nil
	}

	if r.Protocol == "" {
		return errors.NotValidf("missing Protocol")
	}
	if r.Protocol != LXDProtocol && r.Protocol != SimplestreamsProtocol {
		return errors.NotValidf("unknown Protocol %q", r.Protocol)
	}

	// r.Cert is allowed to be nil for Public remotes
	if r.Cert != nil {
		if err := r.Cert.Validate(); err != nil {
			return errors.Trace(err)
		}
	}

	return nil
}

func (r Remote) validateLocal() error {
	if r.Cert != nil {
		return errors.NotValidf("hostless remote with cert")
	}
	if r.Protocol != LXDProtocol {
		return errors.NotValidf("localhost always talks LXD protocol not: %s", r.Protocol)
	}

	return nil
}
<|endoftext|>