<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ast

import "fmt"

// A Visitor's Visit method is invoked for each node encountered by Walk.
// If Visit returns true, Walk is invoked for each of the node's children.
//
type Visitor interface {
	Visit(node interface{}) bool;
}

func walkIdent(v Visitor, x *Ident) {
	if x != nil {
		Walk(v, x);
	}
}

func walkCommentGroup(v Visitor, g *CommentGroup) {
	if g != nil {
		Walk(v, g);
	}
}

func walkFieldList(v Visitor, list []*Field) {
	for _, x := range list {
		Walk(v, x);
	}
}

func walkIdentList(v Visitor, list []*Ident) {
	for _, x := range list {
		walk(v, x);
	}
}

func walkExprList(v Visitor, list []Expr) {
	for _, x := range list {
		walk(v, x);
	}
}

func walkStmtList(v Visitor, list []Stmt) {
	for _, s := range list {
		walk(v, s);
	}
}

func walkBlockStmt(v Visitor, b *BlockStmt) {
	if b != nil {
		Walk(v, b);
	}
}

func walk(v Visitor, n Node) {
	if n != nil {
		Walk(v, n);
	}
}

// Walk recursively traverses an AST invokes v.Visit(n) for each
// node n encountered (starting with node). If v.Visit(n) returns
// true, Walk is invoked for each of the children of n.
//
func Walk(v Visitor, node interface{}) {
	if !v.Visit(node) {
		return;
	}

	// walk children
	switch n := node.(type) {
	// Comments and fields
	case *CommentGroup:
		for _, c := range n.List {
			Walk(v, c);
		}

	case *Field:
		walkCommentGroup(v, n.Doc);
		walkIdentList(v, n.Names);
		walk(v, n.Type);
		for _, x := range n.Tag {
			Walk(v, x);
		}
		walkCommentGroup(v, n.Comment);

	// Expressions
	case *StringList:
		for _, x := range n.Strings {
			Walk(v, x);
		}

	case *FuncLit:
		walk(v, n.Type);
		walkBlockStmt(v, n.Body);

	case *CompositeLit:
		walk(v, n.Type);
		walkExprList(v, n.Elts);

	case *ParenExpr:
		walk(v, n.X);

	case *SelectorExpr:
		walk(v, n.X);
		if n.Sel != nil {
			Walk(v, n.Sel);
		}

	case *IndexExpr:
		walk(v, n.X);
		walk(v, n.Index);
		walk(v, n.End);

	case *TypeAssertExpr:
		walk(v, n.X);
		walk(v, n.Type);

	case *CallExpr:
		walk(v, n.Fun);
		walkExprList(v, n.Args);

	case *StarExpr:
		walk(v, n.X);

	case *UnaryExpr:
		walk(v, n.X);

	case *BinaryExpr:
		walk(v, n.X);
		walk(v, n.Y);

	case *KeyValueExpr:
		walk(v, n.Key);
		walk(v, n.Value);

	// Types
	case *ArrayType:
		walk(v, n.Len);
		walk(v, n.Elt);

	case *StructType:
		walkFieldList(v, n.Fields);

	case *FuncType:
		walkFieldList(v, n.Params);
		walkFieldList(v, n.Results);

	case *InterfaceType:
		walkFieldList(v, n.Methods);

	case *MapType:
		walk(v, n.Key);
		walk(v, n.Value);

	case *ChanType:
		walk(v, n.Value);

	// Statements
	case *DeclStmt:
		walk(v, n.Decl);

	case *LabeledStmt:
		walkIdent(v, n.Label);
		walk(v, n.Stmt);

	case *ExprStmt:
		walk(v, n.X);

	case *IncDecStmt:
		walk(v, n.X);

	case *AssignStmt:
		walkExprList(v, n.Lhs);
		walkExprList(v, n.Rhs);

	case *GoStmt:
		if n.Call != nil {
			Walk(v, n.Call);
		}

	case *DeferStmt:
		if n.Call != nil {
			Walk(v, n.Call);
		}

	case *ReturnStmt:
		walkExprList(v, n.Results);

	case *BranchStmt:
		walkIdent(v, n.Label);

	case *BlockStmt:
		walkStmtList(v, n.List);

	case *IfStmt:
		walk(v, n.Init);
		walk(v, n.Cond);
		walkBlockStmt(v, n.Body);
		walk(v, n.Else);

	case *CaseClause:
		walkExprList(v, n.Values);
		walkStmtList(v, n.Body);

	case *SwitchStmt:
		walk(v, n.Init);
		walk(v, n.Tag);
		walkBlockStmt(v, n.Body);

	case *TypeCaseClause:
		walkExprList(v, n.Types);
		walkStmtList(v, n.Body);

	case *TypeSwitchStmt:
		walk(v, n.Init);
		walk(v, n.Assign);
		walkBlockStmt(v, n.Body);

	case *CommClause:
		walk(v, n.Lhs);
		walk(v, n.Rhs);
		walkStmtList(v, n.Body);

	case *SelectStmt:
		walkBlockStmt(v, n.Body);

	case *ForStmt:
		walk(v, n.Init);
		walk(v, n.Cond);
		walk(v, n.Post);
		walkBlockStmt(v, n.Body);

	case *RangeStmt:
		walk(v, n.Key);
		walk(v, n.Value);
		walk(v, n.X);
		walkBlockStmt(v, n.Body);

	// Declarations
	case *ImportSpec:
		walkCommentGroup(v, n.Doc);
		walkIdent(v, n.Name);
		for _, x := range n.Path {
			Walk(v, x);
		}
		walkCommentGroup(v, n.Comment);

	case *ValueSpec:
		walkCommentGroup(v, n.Doc);
		walkIdentList(v, n.Names);
		walk(v, n.Type);
		walkExprList(v, n.Values);
		walkCommentGroup(v, n.Comment);

	case *TypeSpec:
		walkCommentGroup(v, n.Doc);
		walkIdent(v, n.Name);
		walk(v, n.Type);
		walkCommentGroup(v, n.Comment);

	case *GenDecl:
		walkCommentGroup(v, n.Doc);
		for _, s := range n.Specs {
			Walk(v, s);
		}

	case *FuncDecl:
		walkCommentGroup(v, n.Doc);
		if n.Recv != nil {
			Walk(v, n.Recv);
		}
		walkIdent(v, n.Name);
		walk(v, n.Type);
		walkBlockStmt(v, n.Body);

	// Files and packages
	case *File:
		walkCommentGroup(v, n.Doc);
		walkIdent(v, n.Name);
		for _, d := range n.Decls {
			walk(v, d);
		}
		walkCommentGroup(v, n.Comments);

	case *Package:
		for _, f := range n.Files {
			Walk(v, f);
		}

	default:
		fmt.Printf("ast.Walk: unexpected type %T", n);
		panic();
	}
}
<commit_msg>minor changes to walk<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ast

import "fmt"

// A Visitor's Visit method is invoked for each node encountered by Walk.
// If Visit returns true, Walk is invoked for each of the node's children.
//
type Visitor interface {
	Visit(node interface{}) bool;
}

func walkIdent(v Visitor, x *Ident) {
	if x != nil {
		Walk(v, x);
	}
}

func walkCommentGroup(v Visitor, g *CommentGroup) {
	if g != nil {
		Walk(v, g);
	}
}

func walkFieldList(v Visitor, list []*Field) {
	for _, x := range list {
		Walk(v, x);
	}
}

func walkIdentList(v Visitor, list []*Ident) {
	for _, x := range list {
		Walk(v, x);
	}
}

func walkExprList(v Visitor, list []Expr) {
	for _, x := range list {
		Walk(v, x);
	}
}

func walkStmtList(v Visitor, list []Stmt) {
	for _, s := range list {
		Walk(v, s);
	}
}

func walkBlockStmt(v Visitor, b *BlockStmt) {
	if b != nil {
		Walk(v, b);
	}
}

// Walk traverses an AST in depth-first order and invokes v.Visit(n) for each
// non-nil node n encountered, starting with node. If v.Visit(n) returns true,
// Walk visits each of the children of n.
//
func Walk(v Visitor, node interface{}) {
	if node != nil && !v.Visit(node) {
		return;
	}

	// walk children
	switch n := node.(type) {
	// Comments and fields
	case *CommentGroup:
		for _, c := range n.List {
			Walk(v, c);
		}

	case *Field:
		walkCommentGroup(v, n.Doc);
		walkIdentList(v, n.Names);
		Walk(v, n.Type);
		for _, x := range n.Tag {
			Walk(v, x);
		}
		walkCommentGroup(v, n.Comment);

	// Expressions
	case *StringList:
		for _, x := range n.Strings {
			Walk(v, x);
		}

	case *FuncLit:
		Walk(v, n.Type);
		walkBlockStmt(v, n.Body);

	case *CompositeLit:
		Walk(v, n.Type);
		walkExprList(v, n.Elts);

	case *ParenExpr:
		Walk(v, n.X);

	case *SelectorExpr:
		Walk(v, n.X);
		if n.Sel != nil {
			Walk(v, n.Sel);
		}

	case *IndexExpr:
		Walk(v, n.X);
		Walk(v, n.Index);
		Walk(v, n.End);

	case *TypeAssertExpr:
		Walk(v, n.X);
		Walk(v, n.Type);

	case *CallExpr:
		Walk(v, n.Fun);
		walkExprList(v, n.Args);

	case *StarExpr:
		Walk(v, n.X);

	case *UnaryExpr:
		Walk(v, n.X);

	case *BinaryExpr:
		Walk(v, n.X);
		Walk(v, n.Y);

	case *KeyValueExpr:
		Walk(v, n.Key);
		Walk(v, n.Value);

	// Types
	case *ArrayType:
		Walk(v, n.Len);
		Walk(v, n.Elt);

	case *StructType:
		walkFieldList(v, n.Fields);

	case *FuncType:
		walkFieldList(v, n.Params);
		walkFieldList(v, n.Results);

	case *InterfaceType:
		walkFieldList(v, n.Methods);

	case *MapType:
		Walk(v, n.Key);
		Walk(v, n.Value);

	case *ChanType:
		Walk(v, n.Value);

	// Statements
	case *DeclStmt:
		Walk(v, n.Decl);

	case *LabeledStmt:
		walkIdent(v, n.Label);
		Walk(v, n.Stmt);

	case *ExprStmt:
		Walk(v, n.X);

	case *IncDecStmt:
		Walk(v, n.X);

	case *AssignStmt:
		walkExprList(v, n.Lhs);
		walkExprList(v, n.Rhs);

	case *GoStmt:
		if n.Call != nil {
			Walk(v, n.Call);
		}

	case *DeferStmt:
		if n.Call != nil {
			Walk(v, n.Call);
		}

	case *ReturnStmt:
		walkExprList(v, n.Results);

	case *BranchStmt:
		walkIdent(v, n.Label);

	case *BlockStmt:
		walkStmtList(v, n.List);

	case *IfStmt:
		Walk(v, n.Init);
		Walk(v, n.Cond);
		walkBlockStmt(v, n.Body);
		Walk(v, n.Else);

	case *CaseClause:
		walkExprList(v, n.Values);
		walkStmtList(v, n.Body);

	case *SwitchStmt:
		Walk(v, n.Init);
		Walk(v, n.Tag);
		walkBlockStmt(v, n.Body);

	case *TypeCaseClause:
		walkExprList(v, n.Types);
		walkStmtList(v, n.Body);

	case *TypeSwitchStmt:
		Walk(v, n.Init);
		Walk(v, n.Assign);
		walkBlockStmt(v, n.Body);

	case *CommClause:
		Walk(v, n.Lhs);
		Walk(v, n.Rhs);
		walkStmtList(v, n.Body);

	case *SelectStmt:
		walkBlockStmt(v, n.Body);

	case *ForStmt:
		Walk(v, n.Init);
		Walk(v, n.Cond);
		Walk(v, n.Post);
		walkBlockStmt(v, n.Body);

	case *RangeStmt:
		Walk(v, n.Key);
		Walk(v, n.Value);
		Walk(v, n.X);
		walkBlockStmt(v, n.Body);

	// Declarations
	case *ImportSpec:
		walkCommentGroup(v, n.Doc);
		walkIdent(v, n.Name);
		for _, x := range n.Path {
			Walk(v, x);
		}
		walkCommentGroup(v, n.Comment);

	case *ValueSpec:
		walkCommentGroup(v, n.Doc);
		walkIdentList(v, n.Names);
		Walk(v, n.Type);
		walkExprList(v, n.Values);
		walkCommentGroup(v, n.Comment);

	case *TypeSpec:
		walkCommentGroup(v, n.Doc);
		walkIdent(v, n.Name);
		Walk(v, n.Type);
		walkCommentGroup(v, n.Comment);

	case *GenDecl:
		walkCommentGroup(v, n.Doc);
		for _, s := range n.Specs {
			Walk(v, s);
		}

	case *FuncDecl:
		walkCommentGroup(v, n.Doc);
		if n.Recv != nil {
			Walk(v, n.Recv);
		}
		walkIdent(v, n.Name);
		Walk(v, n.Type);
		walkBlockStmt(v, n.Body);

	// Files and packages
	case *File:
		walkCommentGroup(v, n.Doc);
		walkIdent(v, n.Name);
		for _, d := range n.Decls {
			Walk(v, d);
		}
		walkCommentGroup(v, n.Comments);

	case *Package:
		for _, f := range n.Files {
			Walk(v, f);
		}

	default:
		fmt.Printf("ast.Walk: unexpected type %T", n);
		panic();
	}
}
<|endoftext|>


<commit_before>package main

import (
	bigv "bigv.io/client/lib"
	"fmt"
	"strconv"
	"unicode/utf8"
)

// DiscSpecError represents an error during parse.
type DiscSpecError struct {
	Position  int
	Character rune
}

func (e *DiscSpecError) Error() string {
	return fmt.Sprintf("Disc spec error: Unexpected %s at %d.")
}

// ParseDiscSpec takes a disc spec and returns a slice of Discs (from bigv.io/client/lib)
func ParseDiscSpec(spec string, trace bool) ([]*bigv.Disc, error) {
	// parser!
	pos := 0

	discs := make([]*bigv.Disc, 0, 4)

	const (
		_either = 0
		_grade  = 1
		_size   = 2
	)

	curDisc := 0
	curGrade := ""
	curSize := ""

	expecting := _either

	// parser!!
	for true {
		if pos >= len(spec) {
			break
		}
		c, w := utf8.DecodeRuneInString(spec[pos:])
		if c == ' ' {
			pos += w
			continue
		}
		switch expecting {
		case _grade:
			if c >= 'a' && c <= 'z' {
				curGrade += spec[pos : pos+w]
				pos += w
			} else if c == ':' {
				expecting = _size
				pos += w
			} else {
				return nil, &DiscSpecError{pos, c}
			}
		case _size:
			if c >= '0' && c <= '9' {
				curSize += spec[pos : pos+w]
				pos += w
			} else if c == ',' {
				size, err := strconv.ParseInt(curSize, 10, 32)
				if err != nil {
					panic(err)
				}
				discs = append(discs, &bigv.Disc{
					StorageGrade: curGrade,
					Size:         int(size) * 1024,
				})

				curGrade = ""
				curSize = ""
				curDisc++
				expecting = _either
				pos += w
			} else {
				return nil, &DiscSpecError{pos, c}
			}
		default: // _expecting
			if c >= 'a' && c <= 'z' {
				expecting = _grade
			} else if c >= '0' && c <= '9' {
				expecting = _size
			} else {
				return nil, &DiscSpecError{pos, c}
			}
		}
	}
	size, err := strconv.ParseInt(curSize, 10, 32)
	if err != nil {
		panic(err)
	}
	discs = append(discs, &bigv.Disc{
		StorageGrade: curGrade,
		Size:         int(size) * 1024,
	})
	return discs, nil
}
<commit_msg>Remove panics from ParseDiscSpec<commit_after>package main

import (
	bigv "bigv.io/client/lib"
	"fmt"
	"strconv"
	"unicode/utf8"
)

// DiscSpecError represents an error during parse.
type DiscSpecError struct {
	Position  int
	Character rune
}

func (e *DiscSpecError) Error() string {
	return fmt.Sprintf("Disc spec error: Unexpected %s at %d.")
}

// ParseDiscSpec takes a disc spec and returns a slice of Discs (from bigv.io/client/lib)
func ParseDiscSpec(spec string, trace bool) ([]*bigv.Disc, error) {
	// parser!
	pos := 0

	discs := make([]*bigv.Disc, 0, 4)

	const (
		_either = 0
		_grade  = 1
		_size   = 2
	)

	curDisc := 0
	curGrade := ""
	curSize := ""

	expecting := _either

	// parser!!
	for true {
		if pos >= len(spec) {
			break
		}
		c, w := utf8.DecodeRuneInString(spec[pos:])
		if c == ' ' {
			pos += w
			continue
		}
		switch expecting {
		case _grade:
			if c >= 'a' && c <= 'z' {
				curGrade += spec[pos : pos+w]
				pos += w
			} else if c == ':' {
				expecting = _size
				pos += w
			} else {
				return nil, &DiscSpecError{pos, c}
			}
		case _size:
			if c >= '0' && c <= '9' {
				curSize += spec[pos : pos+w]
				pos += w
			} else if c == ',' {
				size, err := strconv.ParseInt(curSize, 10, 32)
				if err != nil {
					// this should logically be impossible - curSize should be a string solely containing characters from 0-9
					return nil, &DiscSpecError{Position: pos - len(curSize)}
				}
				discs = append(discs, &bigv.Disc{
					StorageGrade: curGrade,
					Size:         int(size) * 1024,
				})

				curGrade = ""
				curSize = ""
				curDisc++
				expecting = _either
				pos += w
			} else {
				return nil, &DiscSpecError{pos, c}
			}
		default: // _expecting
			if c >= 'a' && c <= 'z' {
				expecting = _grade
			} else if c >= '0' && c <= '9' {
				expecting = _size
			} else {
				return nil, &DiscSpecError{pos, c}
			}
		}
	}
	size, err := strconv.ParseInt(curSize, 10, 32)
	if err != nil {
		// this should logically be impossible - curSize should be a string solely containing characters from 0-9
		return nil, &DiscSpecError{Position: pos - len(curSize)}
	}
	discs = append(discs, &bigv.Disc{
		StorageGrade: curGrade,
		Size:         int(size) * 1024,
	})
	return discs, nil
}
<|endoftext|>


<commit_before>package lib

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/BytemarkHosting/bytemark-client/lib/brain"
)

func labelDiscs(discs []brain.Disc, offset ...int) {
	realOffset := 0
	if len(offset) >= 1 {
		realOffset = offset[0]
	}
	for i := range discs {
		if discs[i].Label == "" {
			discs[i].Label = fmt.Sprintf("disc-%d", realOffset+i+1)
		}
	}

}

// CreateDisc creates the given Disc and attaches it to the given virtual machine.
func (c *bytemarkClient) CreateDisc(name *VirtualMachineName, disc brain.Disc) (err error) {
	err = c.validateVirtualMachineName(name)
	if err != nil {
		return
	}
	vm, err := c.GetVirtualMachine(name)
	if err != nil {
		return
	}
	discs := []brain.Disc{disc}

	labelDiscs(discs, vm.GetDiscLabelOffset())

	r, err := c.BuildRequest("POST", BrainEndpoint, "/accounts/%s/groups/%s/virtual_machines/%s/discs", name.Account, name.Group, name.VirtualMachine)
	if err != nil {
		return
	}

	js, err := json.Marshal(discs[0])
	if err != nil {
		return
	}

	_, _, err = r.Run(bytes.NewBuffer(js), nil)
	return

}

// DeleteDisc removes the specified disc from the given virtual machine
func (c *bytemarkClient) DeleteDisc(vm *VirtualMachineName, discLabelOrID string) (err error) {
	err = c.validateVirtualMachineName(vm)
	if err != nil {
		return
	}
	r, err := c.BuildRequest("DELETE", BrainEndpoint, "/accounts/%s/groups/%s/virtual_machines/%s/discs/%s?purge=true", vm.Account, vm.Group, vm.VirtualMachine, discLabelOrID)

	if err != nil {
		return
	}

	_, _, err = r.Run(nil, nil)

	return
}

// ResizeDisc resizes the specified disc to the given size in megabytes
func (c *bytemarkClient) ResizeDisc(vm *VirtualMachineName, discLabelOrID string, sizeMB int) (err error) {
	err = c.validateVirtualMachineName(vm)
	if err != nil {
		return err
	}
	r, err := c.BuildRequest("PUT", BrainEndpoint, "/accounts/%s/groups/%s/virtual_machines/%s/discs/%s", vm.Account, vm.Group, vm.VirtualMachine, discLabelOrID)
	if err != nil {
		return
	}

	// TODO(telyn): marshal json instead of sprintf
	disc := fmt.Sprintf(`{"size":%d}`, sizeMB)

	_, _, err = r.Run(bytes.NewBufferString(disc), nil)
	return err
}

// SetDiscIopsLimit resizes the specified disc to the given size in megabytes
func (c *bytemarkClient) SetDiscIopsLimit(vm *VirtualMachineName, discLabelOrID string, iopsLimit int) (err error) {
	err = c.validateVirtualMachineName(vm)
	if err != nil {
		return err
	}
	r, err := c.BuildRequest("PUT", BrainEndpoint, "/accounts/%s/groups/%s/virtual_machines/%s/discs/%s", vm.Account, vm.Group, vm.VirtualMachine, discLabelOrID)
	if err != nil {
		return
	}

	data := map[string]int{
		"iops_limit": iopsLimit,
	}

	jsonData, err := json.Marshal(data)
	if err != nil {
		return err
	}

	_, _, err = r.Run(bytes.NewBuffer(jsonData), nil)
	return err
}

// GetDisc returns the specified disc from the given virtual machine.
func (c *bytemarkClient) GetDisc(vm *VirtualMachineName, discLabelOrID string) (disc *brain.Disc, err error) {
	disc = new(brain.Disc)
	err = c.validateVirtualMachineName(vm)
	if err != nil {
		return
	}
	r, err := c.BuildRequest("GET", BrainEndpoint, "/accounts/%s/groups/%s/virtual_machines/%s/discs/%s", vm.Account, vm.Group, vm.VirtualMachine, discLabelOrID)

	if err != nil {
		return
	}

	_, _, err = r.Run(nil, disc)
	return
}
<commit_msg>Fix comment!<commit_after>package lib

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/BytemarkHosting/bytemark-client/lib/brain"
)

func labelDiscs(discs []brain.Disc, offset ...int) {
	realOffset := 0
	if len(offset) >= 1 {
		realOffset = offset[0]
	}
	for i := range discs {
		if discs[i].Label == "" {
			discs[i].Label = fmt.Sprintf("disc-%d", realOffset+i+1)
		}
	}

}

// CreateDisc creates the given Disc and attaches it to the given virtual machine.
func (c *bytemarkClient) CreateDisc(name *VirtualMachineName, disc brain.Disc) (err error) {
	err = c.validateVirtualMachineName(name)
	if err != nil {
		return
	}
	vm, err := c.GetVirtualMachine(name)
	if err != nil {
		return
	}
	discs := []brain.Disc{disc}

	labelDiscs(discs, vm.GetDiscLabelOffset())

	r, err := c.BuildRequest("POST", BrainEndpoint, "/accounts/%s/groups/%s/virtual_machines/%s/discs", name.Account, name.Group, name.VirtualMachine)
	if err != nil {
		return
	}

	js, err := json.Marshal(discs[0])
	if err != nil {
		return
	}

	_, _, err = r.Run(bytes.NewBuffer(js), nil)
	return

}

// DeleteDisc removes the specified disc from the given virtual machine
func (c *bytemarkClient) DeleteDisc(vm *VirtualMachineName, discLabelOrID string) (err error) {
	err = c.validateVirtualMachineName(vm)
	if err != nil {
		return
	}
	r, err := c.BuildRequest("DELETE", BrainEndpoint, "/accounts/%s/groups/%s/virtual_machines/%s/discs/%s?purge=true", vm.Account, vm.Group, vm.VirtualMachine, discLabelOrID)

	if err != nil {
		return
	}

	_, _, err = r.Run(nil, nil)

	return
}

// ResizeDisc resizes the specified disc to the given size in megabytes
func (c *bytemarkClient) ResizeDisc(vm *VirtualMachineName, discLabelOrID string, sizeMB int) (err error) {
	err = c.validateVirtualMachineName(vm)
	if err != nil {
		return err
	}
	r, err := c.BuildRequest("PUT", BrainEndpoint, "/accounts/%s/groups/%s/virtual_machines/%s/discs/%s", vm.Account, vm.Group, vm.VirtualMachine, discLabelOrID)
	if err != nil {
		return
	}

	// TODO(telyn): marshal json instead of sprintf
	disc := fmt.Sprintf(`{"size":%d}`, sizeMB)

	_, _, err = r.Run(bytes.NewBufferString(disc), nil)
	return err
}

// SetDiscIopsLimit sets the IOPS limit of the specified disc
func (c *bytemarkClient) SetDiscIopsLimit(vm *VirtualMachineName, discLabelOrID string, iopsLimit int) (err error) {
	err = c.validateVirtualMachineName(vm)
	if err != nil {
		return err
	}
	r, err := c.BuildRequest("PUT", BrainEndpoint, "/accounts/%s/groups/%s/virtual_machines/%s/discs/%s", vm.Account, vm.Group, vm.VirtualMachine, discLabelOrID)
	if err != nil {
		return
	}

	data := map[string]int{
		"iops_limit": iopsLimit,
	}

	jsonData, err := json.Marshal(data)
	if err != nil {
		return err
	}

	_, _, err = r.Run(bytes.NewBuffer(jsonData), nil)
	return err
}

// GetDisc returns the specified disc from the given virtual machine.
func (c *bytemarkClient) GetDisc(vm *VirtualMachineName, discLabelOrID string) (disc *brain.Disc, err error) {
	disc = new(brain.Disc)
	err = c.validateVirtualMachineName(vm)
	if err != nil {
		return
	}
	r, err := c.BuildRequest("GET", BrainEndpoint, "/accounts/%s/groups/%s/virtual_machines/%s/discs/%s", vm.Account, vm.Group, vm.VirtualMachine, discLabelOrID)

	if err != nil {
		return
	}

	_, _, err = r.Run(nil, disc)
	return
}
<|endoftext|>


<commit_before>package instructions

import "jvmgo/rtda"

// Shift left int
type ishl struct {NoOperandsInstruction}
func (self *ishl) execute(thread *rtda.Thread) {
    stack := thread.CurrentFrame().OperandStack()
    v1 := stack.PopInt()
    v2 := uint32(stack.PopInt()) & 0x1f
    result := v1 << v2
    stack.PushInt(result)
}

// Arithmetic shift right int
type ishr struct {NoOperandsInstruction}
func (self *ishr) execute(thread *rtda.Thread) {
    stack := thread.CurrentFrame().OperandStack()
    v1 := stack.PopInt()
    v2 := uint32(stack.PopInt()) & 0x1f
    result := v1 >> v2 // todo
    stack.PushInt(result)
}

// Arithmetic shift right int
type iushr struct {NoOperandsInstruction}
func (self *iushr) execute(thread *rtda.Thread) {
    stack := thread.CurrentFrame().OperandStack()
    v1 := stack.PopInt()
    v2 := uint32(stack.PopInt()) & 0x1f
    result := v1 >> v2
    stack.PushInt(result)
}
<commit_msg>code refactor<commit_after>package instructions

import "jvmgo/rtda"

// Shift left int
type ishl struct {NoOperandsInstruction}
func (self *ishl) execute(thread *rtda.Thread) {
    stack := thread.CurrentFrame().OperandStack()
    v1 := stack.PopInt()
    v2 := stack.PopInt()
    s := uint32(v2) & 0x1f
    result := v1 << s
    stack.PushInt(result)
}

// Arithmetic shift right int
type ishr struct {NoOperandsInstruction}
func (self *ishr) execute(thread *rtda.Thread) {
    stack := thread.CurrentFrame().OperandStack()
    v1 := stack.PopInt()
    v2 := stack.PopInt()
    s := uint32(v2) & 0x1f
    result := v1 >> s // todo
    stack.PushInt(result)
}

// Logical shift right int
type iushr struct {NoOperandsInstruction}
func (self *iushr) execute(thread *rtda.Thread) {
    stack := thread.CurrentFrame().OperandStack()
    v1 := stack.PopInt()
    v2 := stack.PopInt()
    s := uint32(v2) & 0x1f
    result := v1 >> s
    stack.PushInt(result)
}
<|endoftext|>


<commit_before>package jsonschema

import (
	"testing"

	. "launchpad.net/gocheck"
)

func Test(t *testing.T) { TestingT(t) }

type propertySuite struct{}

var _ = Suite(&propertySuite{})

type ExampleJSONBasic struct {
	Bool       bool    `json:",omitempty"`
	Integer    int     `json:",omitempty"`
	Integer8   int8    `json:",omitempty"`
	Integer16  int16   `json:",omitempty"`
	Integer32  int32   `json:",omitempty"`
	Integer64  int64   `json:",omitempty"`
	UInteger   uint    `json:",omitempty"`
	UInteger8  uint8   `json:",omitempty"`
	UInteger16 uint16  `json:",omitempty"`
	UInteger32 uint32  `json:",omitempty"`
	UInteger64 uint64  `json:",omitempty"`
	String     string  `json:",omitempty"`
	Bytes      []byte  `json:",omitempty"`
	Float32    float32 `json:",omitempty"`
	Float64    float64
	Interface  interface{}
}

func (self *propertySuite) TestLoad(c *C) {
	j := &Document{}
	j.Read(&ExampleJSONBasic{})

	c.Assert(*j, DeepEquals, Document{
		Schema: "http://json-schema.org/schema#",
		property: property{
			Type:     "object",
			Required: []string{"Float64", "Interface"},
			Properties: map[string]*property{
				"Bool":       &property{Type: "bool"},
				"Integer":    &property{Type: "integer"},
				"Integer8":   &property{Type: "integer"},
				"Integer16":  &property{Type: "integer"},
				"Integer32":  &property{Type: "integer"},
				"Integer64":  &property{Type: "integer"},
				"UInteger":   &property{Type: "integer"},
				"UInteger8":  &property{Type: "integer"},
				"UInteger16": &property{Type: "integer"},
				"UInteger32": &property{Type: "integer"},
				"UInteger64": &property{Type: "integer"},
				"String":     &property{Type: "string"},
				"Bytes":      &property{Type: "string"},
				"Float32":    &property{Type: "number"},
				"Float64":    &property{Type: "number"},
				"Interface":  &property{},
			},
		},
	})
}

type ExampleJSONBasicWithTag struct {
	Bool bool `json:"test"`
}

func (self *propertySuite) TestLoadWithTag(c *C) {
	j := &Document{}
	j.Read(&ExampleJSONBasicWithTag{})

	c.Assert(*j, DeepEquals, Document{
		Schema: "http://json-schema.org/schema#",
		property: property{
			Type:     "object",
			Required: []string{"test"},
			Properties: map[string]*property{
				"test": &property{Type: "bool"},
			},
		},
	})
}

type ExampleJSONBasicSlices struct {
	Slice            []string      `json:",foo,omitempty"`
	SliceOfInterface []interface{} `json:",foo"`
}

func (self *propertySuite) TestLoadSliceAndContains(c *C) {
	j := &Document{}
	j.Read(&ExampleJSONBasicSlices{})

	c.Assert(*j, DeepEquals, Document{
		Schema: "http://json-schema.org/schema#",
		property: property{
			Type: "object",
			Properties: map[string]*property{
				"Slice": &property{
					Type:  "array",
					Items: &item{Type: "string"},
				},
				"SliceOfInterface": &property{
					Type: "array",
				},
			},

			Required: []string{"SliceOfInterface"},
		},
	})
}

type ExampleJSONNestedStruct struct {
	Struct struct {
		Foo string
	}
}

func (self *propertySuite) TestLoadNested(c *C) {
	j := &Document{}
	j.Read(&ExampleJSONNestedStruct{})

	c.Assert(*j, DeepEquals, Document{
		Schema: "http://json-schema.org/schema#",
		property: property{
			Type: "object",
			Properties: map[string]*property{
				"Struct": &property{
					Type: "object",
					Properties: map[string]*property{
						"Foo": &property{Type: "string"},
					},
					Required: []string{"Foo"},
				},
			},
			Required: []string{"Struct"},
		},
	})
}

type ExampleJSONBasicMaps struct {
	Maps           map[string]string `json:",omitempty"`
	MapOfInterface map[string]interface{}
}

func (self *propertySuite) TestLoadMap(c *C) {
	j := &Document{}
	j.Read(&ExampleJSONBasicMaps{})

	c.Assert(*j, DeepEquals, Document{
		Schema: "http://json-schema.org/schema#",
		property: property{
			Type: "object",
			Properties: map[string]*property{
				"Maps": &property{
					Type: "object",
					Properties: map[string]*property{
						".*": &property{Type: "string"},
					},
					AdditionalProperties: false,
				},
				"MapOfInterface": &property{
					Type:                 "object",
					AdditionalProperties: true,
				},
			},
			Required: []string{"MapOfInterface"},
		},
	})
}

func (self *propertySuite) TestLoadNonStruct(c *C) {
	j := &Document{}
	j.Read([]string{})

	c.Assert(*j, DeepEquals, Document{
		Schema: "http://json-schema.org/schema#",
		property: property{
			Type:  "array",
			Items: &item{Type: "string"},
		},
	})
}

func (self *propertySuite) TestString(c *C) {
	j := &Document{}
	j.Read(true)

	expected := "{\n" +
		"    \"$schema\": \"http://json-schema.org/schema#\",\n" +
		"    \"type\": \"bool\"\n" +
		"}"

	c.Assert(j.String(), Equals, expected)
}

func (self *propertySuite) TestMarshal(c *C) {
	j := &Document{}
	j.Read(10)

	expected := "{\n" +
		"    \"$schema\": \"http://json-schema.org/schema#\",\n" +
		"    \"type\": \"integer\"\n" +
		"}"

	json, err := j.Marshal()
	c.Assert(err, IsNil)
	c.Assert(string(json), Equals, expected)
}
<commit_msg>gocheck relocated to github.com<commit_after>package jsonschema

import (
	"testing"

	. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

type propertySuite struct{}

var _ = Suite(&propertySuite{})

type ExampleJSONBasic struct {
	Bool       bool    `json:",omitempty"`
	Integer    int     `json:",omitempty"`
	Integer8   int8    `json:",omitempty"`
	Integer16  int16   `json:",omitempty"`
	Integer32  int32   `json:",omitempty"`
	Integer64  int64   `json:",omitempty"`
	UInteger   uint    `json:",omitempty"`
	UInteger8  uint8   `json:",omitempty"`
	UInteger16 uint16  `json:",omitempty"`
	UInteger32 uint32  `json:",omitempty"`
	UInteger64 uint64  `json:",omitempty"`
	String     string  `json:",omitempty"`
	Bytes      []byte  `json:",omitempty"`
	Float32    float32 `json:",omitempty"`
	Float64    float64
	Interface  interface{}
}

func (self *propertySuite) TestLoad(c *C) {
	j := &Document{}
	j.Read(&ExampleJSONBasic{})

	c.Assert(*j, DeepEquals, Document{
		Schema: "http://json-schema.org/schema#",
		property: property{
			Type:     "object",
			Required: []string{"Float64", "Interface"},
			Properties: map[string]*property{
				"Bool":       &property{Type: "bool"},
				"Integer":    &property{Type: "integer"},
				"Integer8":   &property{Type: "integer"},
				"Integer16":  &property{Type: "integer"},
				"Integer32":  &property{Type: "integer"},
				"Integer64":  &property{Type: "integer"},
				"UInteger":   &property{Type: "integer"},
				"UInteger8":  &property{Type: "integer"},
				"UInteger16": &property{Type: "integer"},
				"UInteger32": &property{Type: "integer"},
				"UInteger64": &property{Type: "integer"},
				"String":     &property{Type: "string"},
				"Bytes":      &property{Type: "string"},
				"Float32":    &property{Type: "number"},
				"Float64":    &property{Type: "number"},
				"Interface":  &property{},
			},
		},
	})
}

type ExampleJSONBasicWithTag struct {
	Bool bool `json:"test"`
}

func (self *propertySuite) TestLoadWithTag(c *C) {
	j := &Document{}
	j.Read(&ExampleJSONBasicWithTag{})

	c.Assert(*j, DeepEquals, Document{
		Schema: "http://json-schema.org/schema#",
		property: property{
			Type:     "object",
			Required: []string{"test"},
			Properties: map[string]*property{
				"test": &property{Type: "bool"},
			},
		},
	})
}

type ExampleJSONBasicSlices struct {
	Slice            []string      `json:",foo,omitempty"`
	SliceOfInterface []interface{} `json:",foo"`
}

func (self *propertySuite) TestLoadSliceAndContains(c *C) {
	j := &Document{}
	j.Read(&ExampleJSONBasicSlices{})

	c.Assert(*j, DeepEquals, Document{
		Schema: "http://json-schema.org/schema#",
		property: property{
			Type: "object",
			Properties: map[string]*property{
				"Slice": &property{
					Type:  "array",
					Items: &item{Type: "string"},
				},
				"SliceOfInterface": &property{
					Type: "array",
				},
			},

			Required: []string{"SliceOfInterface"},
		},
	})
}

type ExampleJSONNestedStruct struct {
	Struct struct {
		Foo string
	}
}

func (self *propertySuite) TestLoadNested(c *C) {
	j := &Document{}
	j.Read(&ExampleJSONNestedStruct{})

	c.Assert(*j, DeepEquals, Document{
		Schema: "http://json-schema.org/schema#",
		property: property{
			Type: "object",
			Properties: map[string]*property{
				"Struct": &property{
					Type: "object",
					Properties: map[string]*property{
						"Foo": &property{Type: "string"},
					},
					Required: []string{"Foo"},
				},
			},
			Required: []string{"Struct"},
		},
	})
}

type ExampleJSONBasicMaps struct {
	Maps           map[string]string `json:",omitempty"`
	MapOfInterface map[string]interface{}
}

func (self *propertySuite) TestLoadMap(c *C) {
	j := &Document{}
	j.Read(&ExampleJSONBasicMaps{})

	c.Assert(*j, DeepEquals, Document{
		Schema: "http://json-schema.org/schema#",
		property: property{
			Type: "object",
			Properties: map[string]*property{
				"Maps": &property{
					Type: "object",
					Properties: map[string]*property{
						".*": &property{Type: "string"},
					},
					AdditionalProperties: false,
				},
				"MapOfInterface": &property{
					Type:                 "object",
					AdditionalProperties: true,
				},
			},
			Required: []string{"MapOfInterface"},
		},
	})
}

func (self *propertySuite) TestLoadNonStruct(c *C) {
	j := &Document{}
	j.Read([]string{})

	c.Assert(*j, DeepEquals, Document{
		Schema: "http://json-schema.org/schema#",
		property: property{
			Type:  "array",
			Items: &item{Type: "string"},
		},
	})
}

func (self *propertySuite) TestString(c *C) {
	j := &Document{}
	j.Read(true)

	expected := "{\n" +
		"    \"$schema\": \"http://json-schema.org/schema#\",\n" +
		"    \"type\": \"bool\"\n" +
		"}"

	c.Assert(j.String(), Equals, expected)
}

func (self *propertySuite) TestMarshal(c *C) {
	j := &Document{}
	j.Read(10)

	expected := "{\n" +
		"    \"$schema\": \"http://json-schema.org/schema#\",\n" +
		"    \"type\": \"integer\"\n" +
		"}"

	json, err := j.Marshal()
	c.Assert(err, IsNil)
	c.Assert(string(json), Equals, expected)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/gophergala2016/goad/lambda/sqsadaptor"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"
)

func main() {
	address := os.Args[1]
	concurrencycount, err := strconv.Atoi(os.Args[2])
	if err != nil {
		fmt.Printf("ERROR %s\n", err)
		return
	}
	requestcount, err := strconv.Atoi(os.Args[3])
	sqsurl := os.Args[4]
	if err != nil {
		fmt.Printf("ERROR %s\n", err)
		return
	}
	client := &http.Client{}
	fmt.Printf("Will spawn %d workers each making %d requests to %s\n", concurrencycount, requestcount, address)
	runLoadTest(client, sqsurl, address, requestcount, concurrencycount)
}

func runLoadTest(client *http.Client, sqsurl string, url string, requestcount int, concurrencycount int) {
	sqsAdaptor := sqsadaptor.NewDummyAdaptor(sqsurl)

	totalRequests := requestcount * concurrencycount
	ch := make(chan sqsadaptor.Result, totalRequests)
	var wg sync.WaitGroup
	for i := 0; i < concurrencycount; i++ {
		wg.Add(1)
		go fetch(client, url, requestcount, ch, &wg)
	}
	fmt.Println("Waiting for results…")

	completedRequests := 0
	for completedRequests < totalRequests {
		r := <-ch
		completedRequests++
		if completedRequests%10 == 0 {
			fmt.Printf("\r%.2f%% done (%d requests out of %d)", (float64(completedRequests)/float64(totalRequests))*100.0, completedRequests, totalRequests)
		}
		sqsAdaptor.SendResult(r)
	}
	wg.Wait()
	fmt.Printf("\nYay🎈\n")

}

func fetch(client *http.Client, address string, requestcount int, ch chan sqsadaptor.Result, wg *sync.WaitGroup) {
	defer wg.Done()
	fmt.Printf("Fetching %s %d times\n", address, requestcount)
	for i := 0; i < requestcount; i++ {
		start := time.Now()
		req, err := http.NewRequest("GET", address, nil)
		req.Header.Add("User-Agent", "GOAD/0.1")

		response, err := client.Do(req)
		if err != nil {
			fmt.Printf("ERROR %s\n", err)
			return
		}
		_, err = ioutil.ReadAll(response.Body)
		if err != nil {
			fmt.Printf("ERROR %s\n", err)
			return
		}
		elapsed := time.Since(start)
		result := sqsadaptor.Result{
			"2016-01-01 10:00:00",
			"example.com",
			"Fetch",
			"928429348",
			200,
			elapsed.Nanoseconds(),
			2398,
			"Finished",
		}
		ch <- result
	}

}
<commit_msg>Put some real data into the result<commit_after>package main

import (
	"fmt"
	"github.com/gophergala2016/goad/lambda/sqsadaptor"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"
)

func main() {
	address := os.Args[1]
	concurrencycount, err := strconv.Atoi(os.Args[2])
	if err != nil {
		fmt.Printf("ERROR %s\n", err)
		return
	}
	requestcount, err := strconv.Atoi(os.Args[3])
	sqsurl := os.Args[4]
	if err != nil {
		fmt.Printf("ERROR %s\n", err)
		return
	}
	client := &http.Client{}
	fmt.Printf("Will spawn %d workers each making %d requests to %s\n", concurrencycount, requestcount, address)
	runLoadTest(client, sqsurl, address, requestcount, concurrencycount)
}

func runLoadTest(client *http.Client, sqsurl string, url string, requestcount int, concurrencycount int) {
	sqsAdaptor := sqsadaptor.NewDummyAdaptor(sqsurl)

	totalRequests := requestcount * concurrencycount
	ch := make(chan sqsadaptor.Result, totalRequests)
	var wg sync.WaitGroup
	for i := 0; i < concurrencycount; i++ {
		wg.Add(1)
		go fetch(client, url, requestcount, ch, &wg)
	}
	fmt.Println("Waiting for results…")

	completedRequests := 0
	for completedRequests < totalRequests {
		r := <-ch
		completedRequests++
		if completedRequests%10 == 0 {
			fmt.Printf("\r%.2f%% done (%d requests out of %d)", (float64(completedRequests)/float64(totalRequests))*100.0, completedRequests, totalRequests)
		}
		sqsAdaptor.SendResult(r)
	}
	wg.Wait()
	fmt.Printf("\nYay🎈\n")

}

func fetch(client *http.Client, address string, requestcount int, ch chan sqsadaptor.Result, wg *sync.WaitGroup) {
	defer wg.Done()
	fmt.Printf("Fetching %s %d times\n", address, requestcount)
	for i := 0; i < requestcount; i++ {
		start := time.Now()
		req, err := http.NewRequest("GET", address, nil)
		req.Header.Add("User-Agent", "GOAD/0.1")

		response, err := client.Do(req)
		if err != nil {
			fmt.Printf("ERROR %s\n", err)
			return
		}
		buf, err := ioutil.ReadAll(response.Body)
		if err != nil {
			fmt.Printf("ERROR %s\n", err)
			return
		}
		elapsed := time.Since(start)
		result := sqsadaptor.Result{
			start.Format(time.RFC3339),
			req.URL.Host,
			"Fetch",
			"4711",
			response.StatusCode,
			elapsed.Nanoseconds(),
			len(buf),
			"Finished",
		}
		ch <- result
	}

}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/tls"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/goadapp/goad/helpers"
	"github.com/goadapp/goad/queue"
)

func main() {

	var (
		address          string
		sqsurl           string
		concurrencycount int
		maxRequestCount  int
		timeout          string
		frequency        string
		awsregion        string
		queueRegion      string
		requestMethod    string
		requestBody      string
		requestHeaders   helpers.StringsliceFlag
	)

	flag.StringVar(&address, "u", "", "URL to load test (required)")
	flag.StringVar(&requestMethod, "m", "GET", "HTTP method")
	flag.StringVar(&requestBody, "b", "", "HTTP request body")
	flag.StringVar(&awsregion, "r", "", "AWS region to run in")
	flag.StringVar(&queueRegion, "q", "", "Queue region")
	flag.StringVar(&sqsurl, "s", "", "sqsUrl")
	flag.StringVar(&timeout, "t", "15s", "request timeout in seconds")
	flag.StringVar(&frequency, "f", "15s", "Reporting frequency in seconds")

	flag.IntVar(&concurrencycount, "c", 10, "number of concurrent requests")
	flag.IntVar(&maxRequestCount, "n", 1000, "number of total requests to make")

	flag.Var(&requestHeaders, "H", "List of headers")
	flag.Parse()

	clientTimeout, _ := time.ParseDuration(timeout)
	fmt.Printf("Using a timeout of %s\n", clientTimeout)
	reportingFrequency, _ := time.ParseDuration(frequency)
	fmt.Printf("Using a reporting frequency of %s\n", reportingFrequency)

	// InsecureSkipVerify so that sites with self signed certs can be tested
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}
	client.Timeout = clientTimeout

	fmt.Printf("Will spawn %d workers making %d requests to %s\n", concurrencycount, maxRequestCount, address)
	runLoadTest(client, sqsurl, address, maxRequestCount, concurrencycount, awsregion, reportingFrequency, queueRegion, requestMethod, requestBody, requestHeaders)
}

type RequestResult struct {
	Time             int64  `json:"time"`
	Host             string `json:"host"`
	Type             string `json:"type"`
	Status           int    `json:"status"`
	ElapsedFirstByte int64  `json:"elapsed-first-byte"`
	ElapsedLastByte  int64  `json:"elapsed-last-byte"`
	Elapsed          int64  `json:"elapsed"`
	Bytes            int    `json:"bytes"`
	Timeout          bool   `json:"timeout"`
	ConnectionError  bool   `json:"connection-error"`
	State            string `json:"state"`
}

func runLoadTest(client *http.Client, sqsurl string, url string, totalRequests int, concurrencycount int, awsregion string, reportingFrequency time.Duration, queueRegion string, requestMethod string, requestBody string, requestHeaders []string) {
	awsConfig := aws.NewConfig().WithRegion(queueRegion)
	sqsAdaptor := queue.NewSQSAdaptor(awsConfig, sqsurl)
	//sqsAdaptor := queue.NewDummyAdaptor(sqsurl)
	jobs := make(chan struct{}, totalRequests)
	ch := make(chan RequestResult, totalRequests)
	var wg sync.WaitGroup
	loadTestStartTime := time.Now()
	var requestsSoFar int
	for i := 0; i < totalRequests; i++ {
		jobs <- struct{}{}
	}
	close(jobs)
	fmt.Print("Spawning workers…")
	for i := 0; i < concurrencycount; i++ {
		wg.Add(1)
		go fetch(loadTestStartTime, client, url, totalRequests, jobs, ch, &wg, awsregion, requestMethod, requestBody, requestHeaders)
		fmt.Print(".")
	}
	fmt.Println(" done.\nWaiting for results…")

	ticker := time.NewTicker(reportingFrequency)
	quit := make(chan struct{})
	quitting := false

	for requestsSoFar < totalRequests && !quitting {
		i := 0

		var timeToFirstTotal int64
		var requestTimeTotal int64
		totBytesRead := 0
		statuses := make(map[string]int)
		var firstRequestTime int64
		var lastRequestTime int64
		var slowest int64
		var fastest int64
		var totalTimedOut int
		var totalConnectionError int

		resetStats := false
		for requestsSoFar < totalRequests && !quitting && !resetStats {
			aggregate := false
			select {
			case r := <-ch:
				i++
				requestsSoFar++
				if requestsSoFar%10 == 0 || requestsSoFar == totalRequests {
					fmt.Printf("\r%.2f%% done (%d requests out of %d)", (float64(requestsSoFar)/float64(totalRequests))*100.0, requestsSoFar, totalRequests)
				}
				if firstRequestTime == 0 {
					firstRequestTime = r.Time
				}

				lastRequestTime = r.Time

				if r.Timeout {
					totalTimedOut++
					continue
				}
				if r.ConnectionError {
					totalConnectionError++
					continue
				}

				if r.ElapsedLastByte > slowest {
					slowest = r.ElapsedLastByte
				}
				if fastest == 0 {
					fastest = r.ElapsedLastByte
				} else {
					if r.ElapsedLastByte < fastest {
						fastest = r.ElapsedLastByte
					}
				}

				timeToFirstTotal += r.ElapsedFirstByte
				totBytesRead += r.Bytes
				statusStr := strconv.Itoa(r.Status)
				_, ok := statuses[statusStr]
				if !ok {
					statuses[statusStr] = 1
				} else {
					statuses[statusStr]++
				}
				requestTimeTotal += r.Elapsed
				if requestsSoFar == totalRequests {
					quitting = true
				}
			case <-ticker.C:
				if i == 0 {
					continue
				}
				aggregate = true
			case <-quit:
				ticker.Stop()
				quitting = true
			}
			if aggregate || quitting {
				durationNanoSeconds := lastRequestTime - firstRequestTime
				durationSeconds := float32(durationNanoSeconds) / float32(1000000000)
				var reqPerSec float32
				var kbPerSec float32
				if durationSeconds > 0 {
					reqPerSec = float32(i) / durationSeconds
					kbPerSec = (float32(totBytesRead) / durationSeconds) / 1024.0
				} else {
					reqPerSec = 0
					kbPerSec = 0
				}

				fatalError := ""
				if (totalTimedOut + totalConnectionError) > i/2 {
					fatalError = "Over 50% of requests failed, aborting"
					quitting = true
				}
				aggData := queue.AggData{
					i,
					totalTimedOut,
					totalConnectionError,
					timeToFirstTotal / int64(i),
					totBytesRead,
					statuses,
					requestTimeTotal / int64(i),
					reqPerSec,
					kbPerSec,
					slowest,
					fastest,
					awsregion,
					fatalError,
				}
				sqsAdaptor.SendResult(aggData)
				resetStats = true
			}
		}
	}
	fmt.Printf("\nYay🎈  - %d requests completed\n", requestsSoFar)

}

func fetch(loadTestStartTime time.Time, client *http.Client, address string, requestcount int, jobs <-chan struct{}, ch chan RequestResult, wg *sync.WaitGroup, awsregion string, requestMethod string, requestBody string, requestHeaders []string) {
	defer wg.Done()
	for _ = range jobs {
		start := time.Now()
		req, err := http.NewRequest(requestMethod, address, bytes.NewBufferString(requestBody))
		if err != nil {
			fmt.Println("Error creating the HTTP request:", err)
			return
		}
		req.Header.Add("Accept-Encoding", "gzip")
		for _, v := range requestHeaders {
			header := strings.Split(v, ":")
			if strings.ToLower(strings.Trim(header[0], " ")) == "host" {
				req.Host = strings.Trim(header[1], " ")
			} else {
				req.Header.Add(strings.Trim(header[0], " "), strings.Trim(header[1], " "))
			}
		}

		if req.Header.Get("User-Agent") == "" {
			req.Header.Add("User-Agent", "Mozilla/5.0 (compatible; Goad/1.0; +https://goad.io)")
		}

		response, err := client.Do(req)
		var status string
		var elapsedFirstByte time.Duration
		var elapsedLastByte time.Duration
		var elapsed time.Duration
		var statusCode int
		var bytesRead int
		buf := []byte(" ")
		timedOut := false
		connectionError := false
		isRedirect := err != nil && strings.Contains(err.Error(), "redirect")
		if err != nil && !isRedirect {
			status = fmt.Sprintf("ERROR: %s\n", err)
			switch err := err.(type) {
			case *url.Error:
				if err, ok := err.Err.(net.Error); ok && err.Timeout() {
					timedOut = true
				}
			case net.Error:
				if err.Timeout() {
					timedOut = true
				}
			}

			if !timedOut {
				connectionError = true
			}
		} else {
			statusCode = response.StatusCode
			elapsedFirstByte = time.Since(start)
			if !isRedirect {
				_, err = response.Body.Read(buf)
				firstByteRead := true
				if err != nil {
					status = fmt.Sprintf("reading first byte failed: %s\n", err)
					firstByteRead = false
				}
				body, err := ioutil.ReadAll(response.Body)
				if firstByteRead {
					bytesRead = len(body) + 1
				}
				elapsedLastByte = time.Since(start)
				if err != nil {
					// todo: detect timeout here as well
					status = fmt.Sprintf("reading response body failed: %s\n", err)
					connectionError = true
				} else {
					status = "Success"
				}
			} else {
				status = "Redirect"
			}
			response.Body.Close()

			elapsed = time.Since(start)
		}
		//fmt.Printf("Request end: %d, elapsed: %d\n", time.Now().Sub(loadTestStartTime).Nanoseconds(), elapsed.Nanoseconds())
		result := RequestResult{
			start.Sub(loadTestStartTime).Nanoseconds(),
			req.URL.Host,
			req.Method,
			statusCode,
			elapsed.Nanoseconds(),
			elapsedFirstByte.Nanoseconds(),
			elapsedLastByte.Nanoseconds(),
			bytesRead,
			timedOut,
			connectionError,
			status,
		}
		ch <- result
	}
}
<commit_msg>this, according to main(lambda.go).RequestResult fields order, is pretty wrong<commit_after>package main

import (
	"bytes"
	"crypto/tls"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/goadapp/goad/helpers"
	"github.com/goadapp/goad/queue"
)

func main() {

	var (
		address          string
		sqsurl           string
		concurrencycount int
		maxRequestCount  int
		timeout          string
		frequency        string
		awsregion        string
		queueRegion      string
		requestMethod    string
		requestBody      string
		requestHeaders   helpers.StringsliceFlag
	)

	flag.StringVar(&address, "u", "", "URL to load test (required)")
	flag.StringVar(&requestMethod, "m", "GET", "HTTP method")
	flag.StringVar(&requestBody, "b", "", "HTTP request body")
	flag.StringVar(&awsregion, "r", "", "AWS region to run in")
	flag.StringVar(&queueRegion, "q", "", "Queue region")
	flag.StringVar(&sqsurl, "s", "", "sqsUrl")
	flag.StringVar(&timeout, "t", "15s", "request timeout in seconds")
	flag.StringVar(&frequency, "f", "15s", "Reporting frequency in seconds")

	flag.IntVar(&concurrencycount, "c", 10, "number of concurrent requests")
	flag.IntVar(&maxRequestCount, "n", 1000, "number of total requests to make")

	flag.Var(&requestHeaders, "H", "List of headers")
	flag.Parse()

	clientTimeout, _ := time.ParseDuration(timeout)
	fmt.Printf("Using a timeout of %s\n", clientTimeout)
	reportingFrequency, _ := time.ParseDuration(frequency)
	fmt.Printf("Using a reporting frequency of %s\n", reportingFrequency)

	// InsecureSkipVerify so that sites with self signed certs can be tested
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}
	client.Timeout = clientTimeout

	fmt.Printf("Will spawn %d workers making %d requests to %s\n", concurrencycount, maxRequestCount, address)
	runLoadTest(client, sqsurl, address, maxRequestCount, concurrencycount, awsregion, reportingFrequency, queueRegion, requestMethod, requestBody, requestHeaders)
}

type RequestResult struct {
	Time             int64  `json:"time"`
	Host             string `json:"host"`
	Type             string `json:"type"`
	Status           int    `json:"status"`
	ElapsedFirstByte int64  `json:"elapsed-first-byte"`
	ElapsedLastByte  int64  `json:"elapsed-last-byte"`
	Elapsed          int64  `json:"elapsed"`
	Bytes            int    `json:"bytes"`
	Timeout          bool   `json:"timeout"`
	ConnectionError  bool   `json:"connection-error"`
	State            string `json:"state"`
}

func runLoadTest(client *http.Client, sqsurl string, url string, totalRequests int, concurrencycount int, awsregion string, reportingFrequency time.Duration, queueRegion string, requestMethod string, requestBody string, requestHeaders []string) {
	awsConfig := aws.NewConfig().WithRegion(queueRegion)
	sqsAdaptor := queue.NewSQSAdaptor(awsConfig, sqsurl)
	//sqsAdaptor := queue.NewDummyAdaptor(sqsurl)
	jobs := make(chan struct{}, totalRequests)
	ch := make(chan RequestResult, totalRequests)
	var wg sync.WaitGroup
	loadTestStartTime := time.Now()
	var requestsSoFar int
	for i := 0; i < totalRequests; i++ {
		jobs <- struct{}{}
	}
	close(jobs)
	fmt.Print("Spawning workers…")
	for i := 0; i < concurrencycount; i++ {
		wg.Add(1)
		go fetch(loadTestStartTime, client, url, totalRequests, jobs, ch, &wg, awsregion, requestMethod, requestBody, requestHeaders)
		fmt.Print(".")
	}
	fmt.Println(" done.\nWaiting for results…")

	ticker := time.NewTicker(reportingFrequency)
	quit := make(chan struct{})
	quitting := false

	for requestsSoFar < totalRequests && !quitting {
		i := 0

		var timeToFirstTotal int64
		var requestTimeTotal int64
		totBytesRead := 0
		statuses := make(map[string]int)
		var firstRequestTime int64
		var lastRequestTime int64
		var slowest int64
		var fastest int64
		var totalTimedOut int
		var totalConnectionError int

		resetStats := false
		for requestsSoFar < totalRequests && !quitting && !resetStats {
			aggregate := false
			select {
			case r := <-ch:
				i++
				requestsSoFar++
				if requestsSoFar%10 == 0 || requestsSoFar == totalRequests {
					fmt.Printf("\r%.2f%% done (%d requests out of %d)", (float64(requestsSoFar)/float64(totalRequests))*100.0, requestsSoFar, totalRequests)
				}
				if firstRequestTime == 0 {
					firstRequestTime = r.Time
				}

				lastRequestTime = r.Time

				if r.Timeout {
					totalTimedOut++
					continue
				}
				if r.ConnectionError {
					totalConnectionError++
					continue
				}

				if r.ElapsedLastByte > slowest {
					slowest = r.ElapsedLastByte
				}
				if fastest == 0 {
					fastest = r.ElapsedLastByte
				} else {
					if r.ElapsedLastByte < fastest {
						fastest = r.ElapsedLastByte
					}
				}

				timeToFirstTotal += r.ElapsedFirstByte
				totBytesRead += r.Bytes
				statusStr := strconv.Itoa(r.Status)
				_, ok := statuses[statusStr]
				if !ok {
					statuses[statusStr] = 1
				} else {
					statuses[statusStr]++
				}
				requestTimeTotal += r.Elapsed
				if requestsSoFar == totalRequests {
					quitting = true
				}
			case <-ticker.C:
				if i == 0 {
					continue
				}
				aggregate = true
			case <-quit:
				ticker.Stop()
				quitting = true
			}
			if aggregate || quitting {
				durationNanoSeconds := lastRequestTime - firstRequestTime
				durationSeconds := float32(durationNanoSeconds) / float32(1000000000)
				var reqPerSec float32
				var kbPerSec float32
				if durationSeconds > 0 {
					reqPerSec = float32(i) / durationSeconds
					kbPerSec = (float32(totBytesRead) / durationSeconds) / 1024.0
				} else {
					reqPerSec = 0
					kbPerSec = 0
				}

				fatalError := ""
				if (totalTimedOut + totalConnectionError) > i/2 {
					fatalError = "Over 50% of requests failed, aborting"
					quitting = true
				}
				aggData := queue.AggData{
					i,
					totalTimedOut,
					totalConnectionError,
					timeToFirstTotal / int64(i),
					totBytesRead,
					statuses,
					requestTimeTotal / int64(i),
					reqPerSec,
					kbPerSec,
					slowest,
					fastest,
					awsregion,
					fatalError,
				}
				sqsAdaptor.SendResult(aggData)
				resetStats = true
			}
		}
	}
	fmt.Printf("\nYay🎈  - %d requests completed\n", requestsSoFar)

}

func fetch(loadTestStartTime time.Time, client *http.Client, address string, requestcount int, jobs <-chan struct{}, ch chan RequestResult, wg *sync.WaitGroup, awsregion string, requestMethod string, requestBody string, requestHeaders []string) {
	defer wg.Done()
	for _ = range jobs {
		start := time.Now()
		req, err := http.NewRequest(requestMethod, address, bytes.NewBufferString(requestBody))
		if err != nil {
			fmt.Println("Error creating the HTTP request:", err)
			return
		}
		req.Header.Add("Accept-Encoding", "gzip")
		for _, v := range requestHeaders {
			header := strings.Split(v, ":")
			if strings.ToLower(strings.Trim(header[0], " ")) == "host" {
				req.Host = strings.Trim(header[1], " ")
			} else {
				req.Header.Add(strings.Trim(header[0], " "), strings.Trim(header[1], " "))
			}
		}

		if req.Header.Get("User-Agent") == "" {
			req.Header.Add("User-Agent", "Mozilla/5.0 (compatible; Goad/1.0; +https://goad.io)")
		}

		response, err := client.Do(req)
		var status string
		var elapsedFirstByte time.Duration
		var elapsedLastByte time.Duration
		var elapsed time.Duration
		var statusCode int
		var bytesRead int
		buf := []byte(" ")
		timedOut := false
		connectionError := false
		isRedirect := err != nil && strings.Contains(err.Error(), "redirect")
		if err != nil && !isRedirect {
			status = fmt.Sprintf("ERROR: %s\n", err)
			switch err := err.(type) {
			case *url.Error:
				if err, ok := err.Err.(net.Error); ok && err.Timeout() {
					timedOut = true
				}
			case net.Error:
				if err.Timeout() {
					timedOut = true
				}
			}

			if !timedOut {
				connectionError = true
			}
		} else {
			statusCode = response.StatusCode
			elapsedFirstByte = time.Since(start)
			if !isRedirect {
				_, err = response.Body.Read(buf)
				firstByteRead := true
				if err != nil {
					status = fmt.Sprintf("reading first byte failed: %s\n", err)
					firstByteRead = false
				}
				body, err := ioutil.ReadAll(response.Body)
				if firstByteRead {
					bytesRead = len(body) + 1
				}
				elapsedLastByte = time.Since(start)
				if err != nil {
					// todo: detect timeout here as well
					status = fmt.Sprintf("reading response body failed: %s\n", err)
					connectionError = true
				} else {
					status = "Success"
				}
			} else {
				status = "Redirect"
			}
			response.Body.Close()

			elapsed = time.Since(start)
		}
		//fmt.Printf("Request end: %d, elapsed: %d\n", time.Now().Sub(loadTestStartTime).Nanoseconds(), elapsed.Nanoseconds())
		result := RequestResult{
			start.Sub(loadTestStartTime).Nanoseconds(),
			req.URL.Host,
			req.Method,
			statusCode,
			elapsedFirstByte.Nanoseconds(),
			elapsedLastByte.Nanoseconds(),
			elapsed.Nanoseconds(),
			bytesRead,
			timedOut,
			connectionError,
			status,
		}
		ch <- result
	}
}
<|endoftext|>


<commit_before>package event

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"golang.org/x/net/context"
)

type DemoEventListener struct {
	Pipe chan bool
}

func (d *DemoEventListener) Write(e *Event) error {
	return nil
}

func (d *DemoEventListener) InterestIn(e *Event) bool {
	return true
}

func (d *DemoEventListener) Key() string {
	return "demo"
}

func TestInit(t *testing.T) {
	assert.Nil(t, Instance())
	Init()
	assert.NotNil(t, Instance())
}

func TestInitOnce(t *testing.T) {
	x := Instance()
	Init()
	assert.NotNil(t, Instance())
	Init()
	assert.Equal(t, x, Instance())
}

func TestStart(t *testing.T) {
	c, cfun := context.WithCancel(context.Background())
	done := make(chan bool)
	go func() {
		err := Start(c)
		assert.NotNil(t, err)
		assert.Contains(t, err.Error(), "context canceled")
		done <- true
	}()

	time.Sleep(time.Second * 1)
	cfun()
	<-done
}

func TestAddListener(t *testing.T) {
	x := Instance()
	assert.NotNil(t, x)
	listener := &DemoEventListener{}
	AddListener(listener)
}

func TestRemoveListener(t *testing.T) {
	x := Instance()
	assert.NotNil(t, x)
	listener := &DemoEventListener{}
	RemoveListener(listener)
}

func TestStop(t *testing.T) {
	c, _ := context.WithCancel(context.Background())
	done := make(chan bool)
	go func() {
		err := Start(c)
		assert.NotNil(t, err)
		assert.Contains(t, err.Error(), "bye")
		done <- true
	}()

	time.Sleep(time.Second * 1)
	Stop()
	<-done
}

func TestDumpEvent(t *testing.T) {
	c, _ := context.WithCancel(context.Background())
	done := make(chan bool)
	go func() {
		err := Start(c)
		assert.NotNil(t, err)
		assert.Contains(t, err.Error(), "bye")
		done <- true
	}()

	time.Sleep(time.Second * 1)
	listener := &DemoEventListener{}
	AddListener(listener)

	go func() {
		res := <-listener.Pipe
		assert.True(t, res)
	}()
	e := Event{}
	WriteEvent(&e)
	Stop()
	<-done
}
<commit_msg>event bus test failed, remove statement Init<commit_after>package event

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"golang.org/x/net/context"
)

type DemoEventListener struct {
	Pipe chan bool
}

func (d *DemoEventListener) Write(e *Event) error {
	return nil
}

func (d *DemoEventListener) InterestIn(e *Event) bool {
	return true
}

func (d *DemoEventListener) Key() string {
	return "demo"
}

func TestStart(t *testing.T) {
	c, cfun := context.WithCancel(context.Background())
	done := make(chan bool)
	go func() {
		err := Start(c)
		assert.NotNil(t, err)
		assert.Contains(t, err.Error(), "context canceled")
		done <- true
	}()

	time.Sleep(time.Second * 1)
	cfun()
	<-done
}

func TestAddListener(t *testing.T) {
	x := Instance()
	assert.NotNil(t, x)
	listener := &DemoEventListener{}
	AddListener(listener)
}

func TestRemoveListener(t *testing.T) {
	x := Instance()
	assert.NotNil(t, x)
	listener := &DemoEventListener{}
	RemoveListener(listener)
}

func TestStop(t *testing.T) {
	c, _ := context.WithCancel(context.Background())
	done := make(chan bool)
	go func() {
		err := Start(c)
		assert.NotNil(t, err)
		assert.Contains(t, err.Error(), "bye")
		done <- true
	}()

	time.Sleep(time.Second * 1)
	Stop()
	<-done
}

func TestDumpEvent(t *testing.T) {
	c, _ := context.WithCancel(context.Background())
	done := make(chan bool)
	go func() {
		err := Start(c)
		assert.NotNil(t, err)
		assert.Contains(t, err.Error(), "bye")
		done <- true
	}()

	time.Sleep(time.Second * 1)
	listener := &DemoEventListener{}
	AddListener(listener)

	go func() {
		res := <-listener.Pipe
		assert.True(t, res)
	}()
	e := Event{}
	WriteEvent(&e)
	Stop()
	<-done
}
<|endoftext|>


<commit_before>package main

import (
	"strings"
	"sync"
	"time"

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

const (
	exporterNamespace = "varnish"
)

// prometheusExporter

type prometheusExporter struct {
	sync.RWMutex

	up      prometheus.Gauge
	version prometheus.Gauge
}

func NewPrometheusExporter() *prometheusExporter {
	return &prometheusExporter{
		// @todo varnishstat never fails, even if varnish is not running.
		// Figure our a reliable way to detect if varnish is running and use that for up.
		up: prometheus.NewGauge(prometheus.GaugeOpts{
			Namespace: exporterNamespace,
			Name:      "up",
			Help:      "Was the last scrape of varnish successful.",
		}),
	}
}

func (pe *prometheusExporter) Initialize() error {
	pe.version = prometheus.NewGauge(prometheus.GaugeOpts{
		Namespace:   exporterNamespace,
		Name:        "version",
		Help:        "Varnish version information",
		ConstLabels: VarnishVersion.Labels(),
	})
	pe.version.Set(1)
	return nil
}

// Implements prometheus.Collector
func (pe *prometheusExporter) Describe(ch chan<- *prometheus.Desc) {
	start := time.Now()

	ch <- pe.up.Desc()
	ch <- pe.version.Desc()

	if StartParams.Verbose {
		logInfo("prometheus.Collector.Describe  %s", time.Now().Sub(start))
	}
}

// Implements prometheus.Collector
func (pe *prometheusExporter) Collect(ch chan<- prometheus.Metric) {
	start := time.Now()

	pe.Lock()
	defer pe.Unlock()

	if _, err := scrapeVarnish(ch); err == nil {
		pe.up.Set(1)
	} else {
		logError(err.Error())
		pe.up.Set(0)
	}

	ch <- pe.up
	ch <- pe.version

	if StartParams.Verbose {
		logInfo("prometheus.Collector.Collect   %s", time.Now().Sub(start))
	}
}

// utils

type group struct {
	name     string
	prefixes []string
}

var (
	groups = []group{
		group{name: "backend", prefixes: []string{
			"vbe.",
		}},
		group{name: "mempool", prefixes: []string{
			"mempool.",
		}},
		group{name: "lck", prefixes: []string{
			"lck.",
		}},
		group{name: "sma", prefixes: []string{
			"sma.",
		}},
		group{name: "mgt", prefixes: []string{
			"mgt.",
		}},
		group{name: "main", prefixes: []string{
			"main.",
		}},
	}
)

type grouping struct {
	newPrefix string
	prefix    string
	total     string
	desc      string
	labelKey  string
}

var (
	fqGroupPrefixes = []*grouping{
		&grouping{
			prefix: "main_fetch",
			total:  "main_s_fetch",
			desc:   "Number of fetches",
		},
		&grouping{
			newPrefix: "main_sessions",
			prefix:    "main_sess",
			total:     "main_s_sess",
			desc:      "Number of sessions",
		},
		&grouping{
			newPrefix: "main_worker_threads",
			prefix:    "main_n_wrk",
			total:     "main_n_wrk",
			desc:      "Number of worker threads",
		},
		&grouping{
			newPrefix: "main_bans",
			prefix:    "main_n_ban",
			total:     "main_n_ban",
			desc:      "Number of bans",
			labelKey:  "action",
		},
	}
	fqNames = map[string]string{
		"varnish_lck_colls":   "varnish_lock_collisions",
		"varnish_lck_creat":   "varnish_lock_created",
		"varnish_lck_destroy": "varnish_lock_destroyed",
		"varnish_lck_locks":   "varnish_lock_operations",
	}
	fqIdentifiers = map[string]string{
		"varnish_lock_collisions": "target",
		"varnish_lock_created":    "target",
		"varnish_lock_destroyed":  "target",
		"varnish_lock_operations": "target",
		"varnish_sma_c_bytes":     "type",
		"varnish_sma_c_fail":      "type",
		"varnish_sma_c_freed":     "type",
		"varnish_sma_c_req":       "type",
		"varnish_sma_g_alloc":     "type",
		"varnish_sma_g_bytes":     "type",
		"varnish_sma_g_space":     "type",
	}
)

// https://prometheus.io/docs/practices/naming/
func computePrometheusInfo(vName, vGroup, vIdentifier, vDescription string) (name, description string, labelKeys, labelValues []string) {
	// name and description
	{
		fq := strings.ToLower(vName)
		// Remove unique identifiers from name to group similar metrics by labeling
		if len(vIdentifier) > 0 {
			fq = strings.Replace(fq, "."+strings.ToLower(vIdentifier), "", -1)
		}
		// Make sure our group is prefixed only once
		fq = prometheusTrimGroupPrefix(fq)
		// Build fq name
		name = exporterNamespace + "_" + vGroup + "_" + strings.Replace(fq, ".", "_", -1)
		if swapName := fqNames[name]; len(swapName) > 0 {
			name = swapName
		}
		description = vDescription
	}
	// labels: can alter final name and description
	{
		if len(vIdentifier) > 0 {
			if isVBE := startsWith(vName, "VBE.", caseSensitive); isVBE {
				// @todo this is quick and dirty, do regexp?
				if VarnishVersion.Major == 4 {
					// <uuid>.<name>
					if len(vIdentifier) > 37 && vIdentifier[8] == '-' && vIdentifier[36] == '.' {
						labelKeys, labelValues = append(labelKeys, "server"), append(labelValues, vIdentifier[0:36])
						labelKeys, labelValues = append(labelKeys, "backend"), append(labelValues, vIdentifier[37:])
					}
				} else if VarnishVersion.Major == 3 {
					// <name>(<ip>,<something>,<port>)
					iStart, iEnd := strings.Index(vIdentifier, "("), strings.Index(vIdentifier, ")")
					if iStart > 0 && iEnd > 1 && iStart < iEnd {
						labelKeys, labelValues = append(labelKeys, "server"), append(labelValues, vIdentifier[iStart+1:iEnd])
						labelKeys, labelValues = append(labelKeys, "backend"), append(labelValues, vIdentifier[0:iStart])
					}
				}
			}
			if len(labelKeys) == 0 {
				labelKey := fqIdentifiers[name]
				if len(labelKey) == 0 {
					labelKey = "id"
				}
				labelKeys, labelValues = append(labelKeys, labelKey), append(labelValues, vIdentifier)
			}
		}

		// create groupings by moving part of the fq name as a label and optional total
		for _, grouping := range fqGroupPrefixes {
			fqTotal := exporterNamespace + "_" + grouping.total
			fqPrefix := exporterNamespace + "_" + grouping.prefix
			fqNewName := fqPrefix
			if len(grouping.newPrefix) > 0 {
				fqNewName = exporterNamespace + "_" + grouping.newPrefix
			}
			if name == fqTotal {
				// @note total should not be a label as it breaks aggregation
				name, description = fqNewName+"_total", grouping.desc
				break
			} else if len(name) > len(fqPrefix)+1 && strings.HasPrefix(name, fqPrefix+"_") {
				labelKey := "type"
				if len(grouping.labelKey) > 0 {
					labelKey = grouping.labelKey
				}
				labelKeys, labelValues = append(labelKeys, labelKey), append(labelValues, name[len(fqPrefix)+1:])
				name, description = fqNewName, grouping.desc
				break
			}
		}
	}
	return name, description, labelKeys, labelValues
}

func prometheusTrimGroupPrefix(name string) string {
	nameLower := strings.ToLower(name)
	for _, group := range groups {
		for _, prefix := range group.prefixes {
			if startsWith(nameLower, prefix, caseSensitive) {
				return name[len(prefix):]
			}
		}
	}
	return name
}

// Always returns at least one main label
func prometheusGroup(vName string) string {
	vNameLower := strings.ToLower(vName)
	for _, group := range groups {
		if startsWithAny(vNameLower, group.prefixes, caseSensitive) {
			return group.name
		}
	}
	return "main"
}
<commit_msg>Backend identifier also seems to have changed in 4.1 not in 4.0<commit_after>package main

import (
	"strings"
	"sync"
	"time"

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

const (
	exporterNamespace = "varnish"
)

// prometheusExporter

type prometheusExporter struct {
	sync.RWMutex

	up      prometheus.Gauge
	version prometheus.Gauge
}

func NewPrometheusExporter() *prometheusExporter {
	return &prometheusExporter{
		// @todo varnishstat never fails, even if varnish is not running.
		// Figure our a reliable way to detect if varnish is running and use that for up.
		up: prometheus.NewGauge(prometheus.GaugeOpts{
			Namespace: exporterNamespace,
			Name:      "up",
			Help:      "Was the last scrape of varnish successful.",
		}),
	}
}

func (pe *prometheusExporter) Initialize() error {
	pe.version = prometheus.NewGauge(prometheus.GaugeOpts{
		Namespace:   exporterNamespace,
		Name:        "version",
		Help:        "Varnish version information",
		ConstLabels: VarnishVersion.Labels(),
	})
	pe.version.Set(1)
	return nil
}

// Implements prometheus.Collector
func (pe *prometheusExporter) Describe(ch chan<- *prometheus.Desc) {
	start := time.Now()

	ch <- pe.up.Desc()
	ch <- pe.version.Desc()

	if StartParams.Verbose {
		logInfo("prometheus.Collector.Describe  %s", time.Now().Sub(start))
	}
}

// Implements prometheus.Collector
func (pe *prometheusExporter) Collect(ch chan<- prometheus.Metric) {
	start := time.Now()

	pe.Lock()
	defer pe.Unlock()

	if _, err := scrapeVarnish(ch); err == nil {
		pe.up.Set(1)
	} else {
		logError(err.Error())
		pe.up.Set(0)
	}

	ch <- pe.up
	ch <- pe.version

	if StartParams.Verbose {
		logInfo("prometheus.Collector.Collect   %s", time.Now().Sub(start))
	}
}

// utils

type group struct {
	name     string
	prefixes []string
}

var (
	groups = []group{
		group{name: "backend", prefixes: []string{
			"vbe.",
		}},
		group{name: "mempool", prefixes: []string{
			"mempool.",
		}},
		group{name: "lck", prefixes: []string{
			"lck.",
		}},
		group{name: "sma", prefixes: []string{
			"sma.",
		}},
		group{name: "mgt", prefixes: []string{
			"mgt.",
		}},
		group{name: "main", prefixes: []string{
			"main.",
		}},
	}
)

type grouping struct {
	newPrefix string
	prefix    string
	total     string
	desc      string
	labelKey  string
}

var (
	fqGroupPrefixes = []*grouping{
		&grouping{
			prefix: "main_fetch",
			total:  "main_s_fetch",
			desc:   "Number of fetches",
		},
		&grouping{
			newPrefix: "main_sessions",
			prefix:    "main_sess",
			total:     "main_s_sess",
			desc:      "Number of sessions",
		},
		&grouping{
			newPrefix: "main_worker_threads",
			prefix:    "main_n_wrk",
			total:     "main_n_wrk",
			desc:      "Number of worker threads",
		},
		&grouping{
			newPrefix: "main_bans",
			prefix:    "main_n_ban",
			total:     "main_n_ban",
			desc:      "Number of bans",
			labelKey:  "action",
		},
	}
	fqNames = map[string]string{
		"varnish_lck_colls":   "varnish_lock_collisions",
		"varnish_lck_creat":   "varnish_lock_created",
		"varnish_lck_destroy": "varnish_lock_destroyed",
		"varnish_lck_locks":   "varnish_lock_operations",
	}
	fqIdentifiers = map[string]string{
		"varnish_lock_collisions": "target",
		"varnish_lock_created":    "target",
		"varnish_lock_destroyed":  "target",
		"varnish_lock_operations": "target",
		"varnish_sma_c_bytes":     "type",
		"varnish_sma_c_fail":      "type",
		"varnish_sma_c_freed":     "type",
		"varnish_sma_c_req":       "type",
		"varnish_sma_g_alloc":     "type",
		"varnish_sma_g_bytes":     "type",
		"varnish_sma_g_space":     "type",
	}
)

// https://prometheus.io/docs/practices/naming/
func computePrometheusInfo(vName, vGroup, vIdentifier, vDescription string) (name, description string, labelKeys, labelValues []string) {
	// name and description
	{
		fq := strings.ToLower(vName)
		// Remove unique identifiers from name to group similar metrics by labeling
		if len(vIdentifier) > 0 {
			fq = strings.Replace(fq, "."+strings.ToLower(vIdentifier), "", -1)
		}
		// Make sure our group is prefixed only once
		fq = prometheusTrimGroupPrefix(fq)
		// Build fq name
		name = exporterNamespace + "_" + vGroup + "_" + strings.Replace(fq, ".", "_", -1)
		if swapName := fqNames[name]; len(swapName) > 0 {
			name = swapName
		}
		description = vDescription
	}
	// labels: can alter final name and description
	{
		if len(vIdentifier) > 0 {
			if isVBE := startsWith(vName, "VBE.", caseSensitive); isVBE {
				// @todo this is quick and dirty, do regexp?
				if VarnishVersion.Major == 4 && VarnishVersion.Minor >= 1 {
					// <uuid>.<name>
					if len(vIdentifier) > 37 && vIdentifier[8] == '-' && vIdentifier[36] == '.' {
						labelKeys, labelValues = append(labelKeys, "server"), append(labelValues, vIdentifier[0:36])
						labelKeys, labelValues = append(labelKeys, "backend"), append(labelValues, vIdentifier[37:])
					}
				} else {
					// <name>(<ip>,<something>,<port>)
					iStart, iEnd := strings.Index(vIdentifier, "("), strings.Index(vIdentifier, ")")
					if iStart > 0 && iEnd > 1 && iStart < iEnd {
						labelKeys, labelValues = append(labelKeys, "server"), append(labelValues, vIdentifier[iStart+1:iEnd])
						labelKeys, labelValues = append(labelKeys, "backend"), append(labelValues, vIdentifier[0:iStart])
					}
				}
			}
			if len(labelKeys) == 0 {
				labelKey := fqIdentifiers[name]
				if len(labelKey) == 0 {
					labelKey = "id"
				}
				labelKeys, labelValues = append(labelKeys, labelKey), append(labelValues, vIdentifier)
			}
		}

		// create groupings by moving part of the fq name as a label and optional total
		for _, grouping := range fqGroupPrefixes {
			fqTotal := exporterNamespace + "_" + grouping.total
			fqPrefix := exporterNamespace + "_" + grouping.prefix
			fqNewName := fqPrefix
			if len(grouping.newPrefix) > 0 {
				fqNewName = exporterNamespace + "_" + grouping.newPrefix
			}
			if name == fqTotal {
				// @note total should not be a label as it breaks aggregation
				name, description = fqNewName+"_total", grouping.desc
				break
			} else if len(name) > len(fqPrefix)+1 && strings.HasPrefix(name, fqPrefix+"_") {
				labelKey := "type"
				if len(grouping.labelKey) > 0 {
					labelKey = grouping.labelKey
				}
				labelKeys, labelValues = append(labelKeys, labelKey), append(labelValues, name[len(fqPrefix)+1:])
				name, description = fqNewName, grouping.desc
				break
			}
		}
	}
	return name, description, labelKeys, labelValues
}

func prometheusTrimGroupPrefix(name string) string {
	nameLower := strings.ToLower(name)
	for _, group := range groups {
		for _, prefix := range group.prefixes {
			if startsWith(nameLower, prefix, caseSensitive) {
				return name[len(prefix):]
			}
		}
	}
	return name
}

// Always returns at least one main label
func prometheusGroup(vName string) string {
	vNameLower := strings.ToLower(vName)
	for _, group := range groups {
		if startsWithAny(vNameLower, group.prefixes, caseSensitive) {
			return group.name
		}
	}
	return "main"
}
<|endoftext|>


<commit_before>/*
 * Minio Client (C) 2015 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/fatih/color"
	"github.com/minio/cli"
	"github.com/minio/mc/pkg/client"
	"github.com/minio/mc/pkg/console"
	"github.com/minio/minio-xl/pkg/probe"
)

// Compute differences between two files or folders.
var diffCmd = cli.Command{
	Name:        "diff",
	Usage:       "Compute differences between two folders.",
	Description: "Diff only lists missing objects or objects with size differences. It *DOES NOT* compare contents. i.e. Objects of same name and size, but differ in contents are not noticed.",
	Action:      mainDiff,
	CustomHelpTemplate: `NAME:
   mc {{.Name}} - {{.Usage}}

DESCRIPTION:
   {{.Description}}

USAGE:
   mc {{.Name}} [FLAGS] FIRST SECOND

FLAGS:
  {{range .Flags}}{{.}}
  {{end}}
EXAMPLES:
   1. Compare a local folder with a folder on Amazon S3 cloud storage.
      $ mc {{.Name}} ~/Photos https://s3.amazonaws.com/MyBucket/Photos

   2. Compare two different folders on a local filesystem.
      $ mc {{.Name}} ~/Photos /Media/Backup/Photos
`,
}

func checkDiffSyntax(ctx *cli.Context) {
	if len(ctx.Args()) != 2 {
		cli.ShowCommandHelpAndExit(ctx, "diff", 1) // last argument is exit code
	}
	for _, arg := range ctx.Args() {
		if strings.TrimSpace(arg) == "" {
			fatalIf(errInvalidArgument().Trace(), "Unable to validate empty argument.")
		}
	}
}

// diffMessage json container for diff messages
type diffMessage struct {
	FirstURL  string       `json:"first"`
	SecondURL string       `json:"second"`
	Diff      string       `json:"diff"`
	Error     *probe.Error `json:"error,omitempty"`
}

// String colorized diff message
func (d diffMessage) String() string {
	msg := ""
	switch d.Diff {
	case "only-in-first":
		msg = console.Colorize("DiffMessage",
			"‘"+d.FirstURL+"’"+" and "+"‘"+d.SecondURL+"’") + console.Colorize("DiffOnlyInFirst", " - only in first.")
	case "type":
		msg = console.Colorize("DiffMessage",
			"‘"+d.FirstURL+"’"+" and "+"‘"+d.SecondURL+"’") + console.Colorize("DiffType", " - differ in type.")
	case "size":
		msg = console.Colorize("DiffMessage",
			"‘"+d.FirstURL+"’"+" and "+"‘"+d.SecondURL+"’") + console.Colorize("DiffSize", " - differ in size.")
	default:
		fatalIf(errDummy().Trace(),
			"Unhandled difference between ‘"+d.FirstURL+"’ and ‘"+d.SecondURL+"’.")
	}
	return msg

}

// JSON jsonified diff message
func (d diffMessage) JSON() string {
	diffJSONBytes, err := json.Marshal(d)
	fatalIf(probe.NewError(err),
		"Unable to marshal diff message ‘"+d.FirstURL+"’, ‘"+d.SecondURL+"’ and ‘"+d.Diff+"’.")
	return string(diffJSONBytes)
}

// doDiffMain runs the diff.
func doDiffMain(firstURL, secondURL string) <-chan diffMessage {
	ch := make(chan diffMessage, 10000)
	go func() {
		defer close(ch)
		// source and targets are always directories
		sourceSeparator := string(client.NewURL(firstURL).Separator)
		if !strings.HasSuffix(firstURL, sourceSeparator) {
			firstURL = firstURL + sourceSeparator
		}
		targetSeparator := string(client.NewURL(secondURL).Separator)
		if !strings.HasSuffix(secondURL, targetSeparator) {
			secondURL = secondURL + targetSeparator
		}

		firstClient, err := url2Client(firstURL)
		if err != nil {
			ch <- diffMessage{Error: err.Trace(firstURL)}
			return
		}
		difference, err := objectDifferenceFactory(secondURL)
		if err != nil {
			ch <- diffMessage{Error: err.Trace(secondURL)}
			return
		}
		isRecursive := true
		for sourceContent := range firstClient.List(isRecursive, false) {
			if sourceContent.Err != nil {
				ch <- diffMessage{Error: sourceContent.Err.Trace()}
				continue
			}
			if sourceContent.Type.IsDir() {
				continue
			}
			suffix := strings.TrimPrefix(sourceContent.URL.String(), firstURL)
			differ, err := difference(suffix, sourceContent.Type, sourceContent.Size)
			if err != nil {
				ch <- diffMessage{Error: err.Trace()}
				continue
			}
			if differ == differNone {
				continue
			}
			ch <- diffMessage{
				FirstURL:  sourceContent.URL.String(),
				SecondURL: urlJoinPath(secondURL, suffix),
				Diff:      differ,
			}
		}
	}()
	return ch
}

// mainDiff main for 'diff'.
func mainDiff(ctx *cli.Context) {
	checkDiffSyntax(ctx)

	// Additional command specific theme customization.
	console.SetColor("DiffMessage", color.New(color.FgGreen, color.Bold))
	console.SetColor("DiffOnlyInFirst", color.New(color.FgRed, color.Bold))
	console.SetColor("DiffType", color.New(color.FgYellow, color.Bold))
	console.SetColor("DiffSize", color.New(color.FgMagenta, color.Bold))

	config := mustGetMcConfig()
	firstArg := ctx.Args().First()
	secondArg := ctx.Args().Last()

	firstURL := getAliasURL(firstArg, config.Aliases)
	secondURL := getAliasURL(secondArg, config.Aliases)

	_, firstContent, err := url2Stat(firstURL)
	if err != nil {
		fatalIf(err.Trace(), fmt.Sprintf("Unable to stat '%s'.", firstURL))
	}
	if !firstContent.Type.IsDir() {
		fatalIf(errInvalidArgument().Trace(), fmt.Sprintf("‘%s’ is not a folder.", firstURL))
	}
	_, secondContent, err := url2Stat(secondURL)
	if err != nil {
		fatalIf(err.Trace(), fmt.Sprintf("Unable to stat '%s'.", secondURL))
	}
	if !secondContent.Type.IsDir() {
		fatalIf(errInvalidArgument().Trace(), fmt.Sprintf("‘%s’ is not a folder.", secondURL))
	}

	for diff := range doDiffMain(firstURL, secondURL) {
		if diff.Error != nil {
			// Print in new line and adjust to top so that we don't print over the ongoing scan bar
			if !globalQuietFlag && !globalJSONFlag {
				console.Eraseline()
			}
		}
		fatalIf(diff.Error.Trace(firstURL, secondURL), "Failed to diff ‘"+firstURL+"’ and ‘"+secondURL+"’.")
		printMsg(diff)
	}
	// Print in new line and adjust to top so that we don't print over the ongoing scan bar
	if !globalQuietFlag && !globalJSONFlag {
		console.Eraseline()
	}
}
<commit_msg>implement -h flag<commit_after>/*
 * Minio Client (C) 2015 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/fatih/color"
	"github.com/minio/cli"
	"github.com/minio/mc/pkg/client"
	"github.com/minio/mc/pkg/console"
	"github.com/minio/minio-xl/pkg/probe"
)

// diff specific flags.
var (
	diffFlagHelp = cli.BoolFlag{
		Name:  "help, h",
		Usage: "Help of diff.",
	}
)

// Compute differences between two files or folders.
var diffCmd = cli.Command{
	Name:        "diff",
	Usage:       "Compute differences between two folders.",
	Description: "Diff only lists missing objects or objects with size differences. It *DOES NOT* compare contents. i.e. Objects of same name and size, but differ in contents are not noticed.",
	Action:      mainDiff,
	Flags:       []cli.Flag{diffFlagHelp},
	CustomHelpTemplate: `NAME:
   mc {{.Name}} - {{.Usage}}

USAGE:
   mc {{.Name}} [FLAGS] FIRST SECOND

FLAGS:
  {{range .Flags}}{{.}}
  {{end}}
DESCRIPTION:
   {{.Description}}

EXAMPLES:
   1. Compare a local folder with a folder on Amazon S3 cloud storage.
      $ mc {{.Name}} ~/Photos https://s3.amazonaws.com/MyBucket/Photos

   2. Compare two different folders on a local filesystem.
      $ mc {{.Name}} ~/Photos /Media/Backup/Photos
`,
}

func checkDiffSyntax(ctx *cli.Context) {
	if len(ctx.Args()) != 2 {
		cli.ShowCommandHelpAndExit(ctx, "diff", 1) // last argument is exit code
	}
	for _, arg := range ctx.Args() {
		if strings.TrimSpace(arg) == "" {
			fatalIf(errInvalidArgument().Trace(), "Unable to validate empty argument.")
		}
	}
}

// diffMessage json container for diff messages
type diffMessage struct {
	FirstURL  string       `json:"first"`
	SecondURL string       `json:"second"`
	Diff      string       `json:"diff"`
	Error     *probe.Error `json:"error,omitempty"`
}

// String colorized diff message
func (d diffMessage) String() string {
	msg := ""
	switch d.Diff {
	case "only-in-first":
		msg = console.Colorize("DiffMessage",
			"‘"+d.FirstURL+"’"+" and "+"‘"+d.SecondURL+"’") + console.Colorize("DiffOnlyInFirst", " - only in first.")
	case "type":
		msg = console.Colorize("DiffMessage",
			"‘"+d.FirstURL+"’"+" and "+"‘"+d.SecondURL+"’") + console.Colorize("DiffType", " - differ in type.")
	case "size":
		msg = console.Colorize("DiffMessage",
			"‘"+d.FirstURL+"’"+" and "+"‘"+d.SecondURL+"’") + console.Colorize("DiffSize", " - differ in size.")
	default:
		fatalIf(errDummy().Trace(),
			"Unhandled difference between ‘"+d.FirstURL+"’ and ‘"+d.SecondURL+"’.")
	}
	return msg

}

// JSON jsonified diff message
func (d diffMessage) JSON() string {
	diffJSONBytes, err := json.Marshal(d)
	fatalIf(probe.NewError(err),
		"Unable to marshal diff message ‘"+d.FirstURL+"’, ‘"+d.SecondURL+"’ and ‘"+d.Diff+"’.")
	return string(diffJSONBytes)
}

// doDiffMain runs the diff.
func doDiffMain(firstURL, secondURL string) <-chan diffMessage {
	ch := make(chan diffMessage, 10000)
	go func() {
		defer close(ch)
		// source and targets are always directories
		sourceSeparator := string(client.NewURL(firstURL).Separator)
		if !strings.HasSuffix(firstURL, sourceSeparator) {
			firstURL = firstURL + sourceSeparator
		}
		targetSeparator := string(client.NewURL(secondURL).Separator)
		if !strings.HasSuffix(secondURL, targetSeparator) {
			secondURL = secondURL + targetSeparator
		}

		firstClient, err := url2Client(firstURL)
		if err != nil {
			ch <- diffMessage{Error: err.Trace(firstURL)}
			return
		}
		difference, err := objectDifferenceFactory(secondURL)
		if err != nil {
			ch <- diffMessage{Error: err.Trace(secondURL)}
			return
		}
		isRecursive := true
		for sourceContent := range firstClient.List(isRecursive, false) {
			if sourceContent.Err != nil {
				ch <- diffMessage{Error: sourceContent.Err.Trace()}
				continue
			}
			if sourceContent.Type.IsDir() {
				continue
			}
			suffix := strings.TrimPrefix(sourceContent.URL.String(), firstURL)
			differ, err := difference(suffix, sourceContent.Type, sourceContent.Size)
			if err != nil {
				ch <- diffMessage{Error: err.Trace()}
				continue
			}
			if differ == differNone {
				continue
			}
			ch <- diffMessage{
				FirstURL:  sourceContent.URL.String(),
				SecondURL: urlJoinPath(secondURL, suffix),
				Diff:      differ,
			}
		}
	}()
	return ch
}

// mainDiff main for 'diff'.
func mainDiff(ctx *cli.Context) {
	checkDiffSyntax(ctx)

	// Additional command specific theme customization.
	console.SetColor("DiffMessage", color.New(color.FgGreen, color.Bold))
	console.SetColor("DiffOnlyInFirst", color.New(color.FgRed, color.Bold))
	console.SetColor("DiffType", color.New(color.FgYellow, color.Bold))
	console.SetColor("DiffSize", color.New(color.FgMagenta, color.Bold))

	config := mustGetMcConfig()
	firstArg := ctx.Args().First()
	secondArg := ctx.Args().Last()

	firstURL := getAliasURL(firstArg, config.Aliases)
	secondURL := getAliasURL(secondArg, config.Aliases)

	_, firstContent, err := url2Stat(firstURL)
	if err != nil {
		fatalIf(err.Trace(), fmt.Sprintf("Unable to stat '%s'.", firstURL))
	}
	if !firstContent.Type.IsDir() {
		fatalIf(errInvalidArgument().Trace(), fmt.Sprintf("‘%s’ is not a folder.", firstURL))
	}
	_, secondContent, err := url2Stat(secondURL)
	if err != nil {
		fatalIf(err.Trace(), fmt.Sprintf("Unable to stat '%s'.", secondURL))
	}
	if !secondContent.Type.IsDir() {
		fatalIf(errInvalidArgument().Trace(), fmt.Sprintf("‘%s’ is not a folder.", secondURL))
	}

	for diff := range doDiffMain(firstURL, secondURL) {
		if diff.Error != nil {
			// Print in new line and adjust to top so that we don't print over the ongoing scan bar
			if !globalQuietFlag && !globalJSONFlag {
				console.Eraseline()
			}
		}
		fatalIf(diff.Error.Trace(firstURL, secondURL), "Failed to diff ‘"+firstURL+"’ and ‘"+secondURL+"’.")
		printMsg(diff)
	}
	// Print in new line and adjust to top so that we don't print over the ongoing scan bar
	if !globalQuietFlag && !globalJSONFlag {
		console.Eraseline()
	}
}
<|endoftext|>