<commit_before>package ddcloud

import (
	"fmt"
	"log"
	"regexp"

	"github.com/DimensionDataResearch/go-dd-cloud-compute/compute"
)

const (
	serverImageTypeOS       = "os"
	serverImageTypeCustomer = "customer"
	serverImageTypeAuto     = "auto"
)

func isUUID(str string) (bool, error) {
	return regexp.Match(`[A-Fa-f0-9]{8}(-[A-Fa-f0-9]{4}){3}-[A-Fa-f0-9]{12}`, []byte(str))
}

func resolveServerImage(imageNameOrID string, imageType string, dataCenterID string, apiClient *compute.Client) (resolvedImage compute.Image, err error) {
	isID, err := isUUID(imageNameOrID)
	if err != nil {
		return
	}

	switch imageType {
	case serverImageTypeOS:
		if isID {
			resolvedImage, err = lookupOSImageByID(imageNameOrID, apiClient)
			if err != nil {
				return
			}

			if resolvedImage == nil {
				err = fmt.Errorf("Cannot find an OS image with Id '%s' in datacenter '%s'",
					imageNameOrID,
					dataCenterID,
				)
			}
		} else {
			resolvedImage, err = lookupOSImageByName(imageNameOrID, dataCenterID, apiClient)
			if err != nil {
				return
			}

			if resolvedImage == nil {
				err = fmt.Errorf("Cannot find an OS image named '%s' in datacenter '%s'",
					imageNameOrID,
					dataCenterID,
				)
			}
		}
	case serverImageTypeCustomer:
		if isID {
			resolvedImage, err = lookupCustomerImageByID(imageNameOrID, apiClient)
			if err != nil {
				return
			}

			if resolvedImage == nil {
				err = fmt.Errorf("Cannot find a customer image with Id '%s' in datacenter '%s'",
					imageNameOrID,
					dataCenterID,
				)
			}
		} else {
			resolvedImage, err = lookupCustomerImageByName(imageNameOrID, dataCenterID, apiClient)
			if err != nil {
				return
			}

			if resolvedImage == nil {
				err = fmt.Errorf("Cannot find a customer image named '%s' in datacenter '%s'",
					imageNameOrID,
					dataCenterID,
				)
			}
		}
	case serverImageTypeAuto:
		if isID {
			resolvedImage, err = lookupOSImageByID(imageNameOrID, apiClient)
			if err != nil {
				return
			}

			// Fall back to customer image, if required.
			if resolvedImage == nil {
				resolvedImage, err = lookupCustomerImageByID(imageNameOrID, apiClient)
				if err != nil {
					return
				}
			}

			if resolvedImage == nil {
				err = fmt.Errorf("Cannot find an OS or customer image with Id '%s' in datacenter '%s'",
					imageNameOrID,
					dataCenterID,
				)
			}
		} else {
			resolvedImage, err = lookupOSImageByName(imageNameOrID, dataCenterID, apiClient)
			if err != nil {
				return
			}

			// Fall back to customer image, if required.
			if resolvedImage == nil {
				resolvedImage, err = lookupCustomerImageByName(imageNameOrID, dataCenterID, apiClient)
				if err != nil {
					return
				}
			}

			if resolvedImage == nil {
				err = fmt.Errorf("Cannot find an OS or customer image named '%s' in datacenter '%s'",
					imageNameOrID,
					dataCenterID,
				)
			}
		}
	default:
		err = fmt.Errorf("Invalid image type '%s'", imageType)

		return
	}

	return
}

func lookupOSImageByID(imageID string, apiClient *compute.Client) (compute.Image, error) {
	log.Printf("Looking up OS image '%s' by Id...", imageID)

	return apiClient.GetOSImage(imageID)
}

func lookupOSImageByName(imageName string, dataCenterID string, apiClient *compute.Client) (compute.Image, error) {
	log.Printf("Looking up OS image '%s' by name in datacenter '%s'...", imageName, dataCenterID)

	return apiClient.FindOSImage(imageName, dataCenterID)
}

func lookupCustomerImageByID(imageID string, apiClient *compute.Client) (compute.Image, error) {
	log.Printf("Looking up customer image '%s' by Id...", imageID)

	return apiClient.GetCustomerImage(imageID)
}

func lookupCustomerImageByName(imageName string, dataCenterID string, apiClient *compute.Client) (compute.Image, error) {
	log.Printf("Looking up customer image '%s' by name in datacenter '%s'...", imageName, dataCenterID)

	return apiClient.FindCustomerImage(imageName, dataCenterID)
}
<commit_msg>Fix crash when deploying a customer image using image_type 'auto' (#66).<commit_after>package ddcloud

import (
	"fmt"
	"log"
	"regexp"

	"github.com/DimensionDataResearch/go-dd-cloud-compute/compute"
)

const (
	serverImageTypeOS       = "os"
	serverImageTypeCustomer = "customer"
	serverImageTypeAuto     = "auto"
)

func isUUID(str string) (bool, error) {
	return regexp.Match(`[A-Fa-f0-9]{8}(-[A-Fa-f0-9]{4}){3}-[A-Fa-f0-9]{12}`, []byte(str))
}

func resolveServerImage(imageNameOrID string, imageType string, dataCenterID string, apiClient *compute.Client) (resolvedImage compute.Image, err error) {
	isID, err := isUUID(imageNameOrID)
	if err != nil {
		return
	}

	var (
		osImage       *compute.OSImage
		customerImage *compute.CustomerImage
	)
	switch imageType {
	case serverImageTypeOS:
		if isID {
			osImage, err = lookupOSImageByID(imageNameOrID, apiClient)
			if err != nil {
				return
			}
			if osImage != nil {
				resolvedImage = osImage

				return
			}

			err = fmt.Errorf("Cannot find an OS image with Id '%s' in datacenter '%s'",
				imageNameOrID,
				dataCenterID,
			)
		} else {
			osImage, err = lookupOSImageByName(imageNameOrID, dataCenterID, apiClient)
			if err != nil {
				return
			}
			if osImage != nil {
				resolvedImage = osImage

				return
			}

			err = fmt.Errorf("Cannot find an OS image named '%s' in datacenter '%s'",
				imageNameOrID,
				dataCenterID,
			)
		}
	case serverImageTypeCustomer:
		if isID {
			customerImage, err = lookupCustomerImageByID(imageNameOrID, apiClient)
			if err != nil {
				return
			}
			if customerImage != nil {
				resolvedImage = customerImage

				return
			}

			err = fmt.Errorf("Cannot find a customer image with Id '%s' in datacenter '%s'",
				imageNameOrID,
				dataCenterID,
			)
		} else {
			customerImage, err = lookupCustomerImageByName(imageNameOrID, dataCenterID, apiClient)
			if err != nil {
				return
			}
			if customerImage != nil {
				resolvedImage = customerImage

				return
			}

			err = fmt.Errorf("Cannot find a customer image named '%s' in datacenter '%s'",
				imageNameOrID,
				dataCenterID,
			)
		}
	case serverImageTypeAuto:
		if isID {
			osImage, err = lookupOSImageByID(imageNameOrID, apiClient)
			if err != nil {
				return
			}
			if osImage != nil {
				resolvedImage = osImage

				return
			}

			// Fall back to customer image, if required.
			customerImage, err = lookupCustomerImageByID(imageNameOrID, apiClient)
			if err != nil {
				return
			}
			if customerImage != nil {
				resolvedImage = customerImage

				return
			}

			err = fmt.Errorf("Cannot find an OS or customer image with Id '%s' in datacenter '%s'",
				imageNameOrID,
				dataCenterID,
			)
		} else {
			osImage, err = lookupOSImageByName(imageNameOrID, dataCenterID, apiClient)
			if err != nil {
				return
			}
			if osImage != nil {
				resolvedImage = osImage

				return
			}

			// Fall back to customer image, if required.
			customerImage, err = lookupCustomerImageByName(imageNameOrID, dataCenterID, apiClient)
			if err != nil {
				return
			}
			if customerImage != nil {
				resolvedImage = customerImage

				return
			}

			err = fmt.Errorf("Cannot find an OS or customer image named '%s' in datacenter '%s'",
				imageNameOrID,
				dataCenterID,
			)
		}
	default:
		err = fmt.Errorf("Invalid image type '%s'", imageType)

		return
	}

	return
}

func lookupOSImageByID(imageID string, apiClient *compute.Client) (*compute.OSImage, error) {
	log.Printf("Looking up OS image '%s' by Id...", imageID)

	return apiClient.GetOSImage(imageID)
}

func lookupOSImageByName(imageName string, dataCenterID string, apiClient *compute.Client) (*compute.OSImage, error) {
	log.Printf("Looking up OS image '%s' by name in datacenter '%s'...", imageName, dataCenterID)

	return apiClient.FindOSImage(imageName, dataCenterID)
}

func lookupCustomerImageByID(imageID string, apiClient *compute.Client) (*compute.CustomerImage, error) {
	log.Printf("Looking up customer image '%s' by Id...", imageID)

	return apiClient.GetCustomerImage(imageID)
}

func lookupCustomerImageByName(imageName string, dataCenterID string, apiClient *compute.Client) (*compute.CustomerImage, error) {
	log.Printf("Looking up customer image '%s' by name in datacenter '%s'...", imageName, dataCenterID)

	return apiClient.FindCustomerImage(imageName, dataCenterID)
}
<|endoftext|>


<commit_before>// Binary genauth generates a C++ implementation and serialization from the Go
// version.
package main

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"strings"
)

type FieldType int

const (
	IdentType FieldType = iota
	StarType
	ArrayType
)

type Field struct {
	Name string
	Type FieldType
	TypeName string
}

type TypeVisitor struct {
	ConcreteTypes map[string][]Field
	InterfaceTypes map[string]bool
}

type FieldVisitor struct {
	ConcreteTypes map[string][]Field
	InterfaceTypes map[string]bool
	Name string
}

func (tv *TypeVisitor) Visit(n ast.Node) ast.Visitor {
	ts, ok := n.(*ast.TypeSpec)
	if !ok {
		return tv
	}

	name := ts.Name.Name
	fmt.Printf("Type %s", name)
	switch ts.Type.(type) {
	case *ast.StructType:
		fmt.Println(" is a struct")
		if _, ok := tv.ConcreteTypes[name]; !ok {
			tv.ConcreteTypes[name] = make([]Field, 0)
		}

		return &FieldVisitor{
			ConcreteTypes: tv.ConcreteTypes,
			InterfaceTypes: tv.InterfaceTypes,
			Name: name,
		}
	case *ast.ArrayType:
		at := ts.Type.(*ast.ArrayType)
		elt, ok := at.Elt.(*ast.Ident)
		if ok {
			tv.ConcreteTypes[name] = []Field{Field{"elt", ArrayType, elt.Name}}
			fmt.Printf(" is an array of type []%s\n", elt.Name)
		}
	case *ast.Ident:
		id := ts.Type.(*ast.Ident)
		fmt.Printf(" is a %s\n", id.Name)
		tv.ConcreteTypes[name] = []Field{Field{"value", IdentType, id.Name}}
	case *ast.InterfaceType:
		fmt.Println(" is an interface")
		tv.InterfaceTypes[name] = true
	default:
		fmt.Println(" is not a struct, an array, or an identifier")
	}

	return tv
}

func (fv *FieldVisitor) Visit(n ast.Node) ast.Visitor {
	f, ok := n.(*ast.Field)
	if !ok {
		return fv
	}

	if len(f.Names) == 0 {
		return nil
	}

	name := strings.ToLower(f.Names[0].Name)
	st := fv.ConcreteTypes
	switch f.Type.(type) {
	case *ast.Ident:
		ident := f.Type.(*ast.Ident)
		st[fv.Name] = append(st[fv.Name], Field{name, IdentType, ident.Name})
		fmt.Printf("\t\t%s %s\n", name, ident.Name)
	case *ast.StarExpr:
		star := f.Type.(*ast.StarExpr)
		ident, ok := star.X.(*ast.Ident)
		if ok {
			st[fv.Name] = append(st[fv.Name], Field{name, StarType, ident.Name})
			fmt.Printf("\t\t%s *%s\n", name, ident.Name)
		}
	case *ast.ArrayType:
		atype := f.Type.(*ast.ArrayType)
		elt, ok := atype.Elt.(*ast.Ident)
		if ok {
			st[fv.Name] = append(st[fv.Name], Field{name, ArrayType, elt.Name})
			fmt.Printf("\t\t%s []%s\n", name, elt.Name)
		}
	default:
		fmt.Printf("\t\tField %s is not an identifier or an Array\n", name)
		return fv
	}

	return &TypeVisitor{
		ConcreteTypes: fv.ConcreteTypes,
		InterfaceTypes: fv.InterfaceTypes,
	}
}

type FuncReceiverWalker struct {
	types map[string]bool
	name string
}

func (fw *FuncReceiverWalker) Visit(n ast.Node) ast.Visitor {
	fd, ok := n.(*ast.FuncDecl)
	if !ok {
		return fw
	}

	if fd.Name.Name != fw.name {
		return fw
	}

	// Record the name of the type of the first receiver.
	if len(fd.Recv.List) == 0 {
		return fw
	}

	field := fd.Recv.List[0]
	ident, ok := field.Type.(*ast.Ident)
	if !ok {
		return fw
	}

	fw.types[ident.Name] = true
	return fw
}

type Constant struct {
	Name string
	Value int
}

type ConstantVisitor struct {
	Constants []Constant
}

func (tv *ConstantVisitor) Visit(n ast.Node) ast.Visitor {
	vs, ok := n.(*ast.ValueSpec)
	if !ok {
		return tv
	}

	if len(vs.Names) == 0 {
		return tv
	}

	ident := vs.Names[0]
	if ident.Name == "_" {
		return tv
	}

	if ident.Obj == nil || ident.Obj.Data == nil || ident.Obj.Kind != ast.Con {
		return tv
	}

	value, ok := ident.Obj.Data.(int)
	if !ok {
		return tv
	}

	// Turn the constant name into a C++ constant name.
	name := "k" + strings.Title(ident.Name)

	tv.Constants = append(tv.Constants, Constant{name, value})
	return tv
}

func main() {
	output := os.Stdout

	tset := token.NewFileSet()
	tf, err := parser.ParseFile(tset, "../../tao/auth/binary.go", nil, 0)
	if err != nil {
		fmt.Println(err)
		return
	}

	//ast.Print(tset, tf)

	constantVisitor := &ConstantVisitor{
		Constants: make([]Constant, 0),
	}

	ast.Walk(constantVisitor, tf)

	fmt.Fprintf(output, "The constants are as follows: %v\n", constantVisitor.Constants)

	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "../../tao/auth/ast.go", nil, 0)
	if err != nil {
		fmt.Println(err)
		return
	}

	tv := &TypeVisitor{
		ConcreteTypes: make(map[string][]Field),
		InterfaceTypes: make(map[string]bool),
	}
	ast.Walk(tv, f)

	fmt.Printf("The full set of types is %+v\n", tv)

	formWalker := &FuncReceiverWalker{
		types: make(map[string]bool),
		name: "isForm",
	}
	ast.Walk(formWalker, f)

	fmt.Printf("The following types are Forms: %v\n", formWalker.types)

	termWalker := &FuncReceiverWalker{
		types: make(map[string]bool),
		name: "isTerm",
	}
	ast.Walk(termWalker, f)

	fmt.Printf("The following types are Terms: %v\n", termWalker.types)

	primitives := map[string]bool{
		"string": true,
		"bytes": true,
		"int": true,
		"int64": true,
		"bool": true,
	}

	includes := `
#include <memory>
#include <vector>

#include <google/protobuf/io/coded_stream.h>
`
	logicElt := `
class LogicElement {
 public:
  virtual bool Marshal(google::protobuf::io::CodedOutputStream* output) = 0;
};`

	form := "class Form : public LogicElement {};\n"
	term := "class Term : public LogicElement {};\n"
	constructor := "  %s(google::protobuf::io::CodedInputStream* input);"
	marshal := "  bool Marshal(google::protobuf::io::CodedOutputStream* output)"

	fmt.Fprintln(output, includes)
	fmt.Fprintln(output, logicElt, "\n")
	fmt.Fprintln(output, form)
	fmt.Fprintln(output, term)

	fmt.Fprintln(output, "enum class BinaryTags {")
	for i, constant := range constantVisitor.Constants {
		fmt.Fprintf(output, "  %s = %d", constant.Name, constant.Value)
		if i < len(constantVisitor.Constants) - 1 {
			fmt.Fprint(output, ",")
		}
		fmt.Fprintln(output)
	}
	fmt.Fprintf(output, "};\n\n")

	for name, fields := range tv.ConcreteTypes {
		fmt.Fprintf(output, "class %s", name)
		if _, isForm := formWalker.types[name]; isForm {
			fmt.Fprintf(output, ": public Form")
		} else if _, isTerm := termWalker.types[name]; isTerm {
			fmt.Fprintf(output, ": public Term")
		}

		fmt.Fprintf(output, " {\n")
		fmt.Fprintf(output, " public:\n")

		fmt.Fprintf(output, constructor, name)
		fmt.Fprintln(output)

		fmt.Fprint(output, marshal)
		fmt.Fprintf(output, " override;\n");

		if len(fields) > 0 {
			fmt.Fprintf(output, " private:\n");
		}

		for _, info := range fields {
			typeName := info.TypeName

			if primitives[typeName] {
				if info.Type == StarType {
					typeName = typeName + "*"
				}
				fmt.Fprintf(output, "  %s %s_;\n", typeName, info.Name)
				continue
			}

			switch info.Type {
			case IdentType, StarType:
				fmt.Fprintf(output, "  std::unique_ptr<%s> %s_;\n", info.TypeName, info.Name)
			case ArrayType:
				if info.TypeName == "byte" {
					fmt.Fprintf(output, "  string %s_;\n", info.Name)
					continue
				}

				typeName := info.TypeName
				if tv.InterfaceTypes[typeName] {
					typeName = "std::unique_ptr<" + typeName + ">"
				}
				fmt.Fprintf(output, "  std::vector<%s> %ss_;\n", typeName, info.Name)
			}
		}

		fmt.Fprintf(output, "};\n\n")
	}

	// Generate the class declarations with inheritance that puts the Marshal method on everything. Everything should have a constructor based on the protobuf streams.
	//
	// Then generate the marshalling/demarshalling code.

	// The following line prints the full (immense) AST.
	//ast.Print(fset, f)
}
<commit_msg>Move header generation into its own function<commit_after>// Binary genauth generates a C++ implementation and serialization from the Go
// version.
package main

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"strings"
)

var primitives = map[string]bool{
	"string": true,
	"bytes": true,
	"int": true,
	"int64": true,
	"bool": true,
}

type FieldType int

const (
	IdentType FieldType = iota
	StarType
	ArrayType
)

type Field struct {
	Name string
	Type FieldType
	TypeName string
}

type TypeVisitor struct {
	ConcreteTypes map[string][]Field
	InterfaceTypes map[string]bool
}

type FieldVisitor struct {
	ConcreteTypes map[string][]Field
	InterfaceTypes map[string]bool
	Name string
}

func (tv *TypeVisitor) Visit(n ast.Node) ast.Visitor {
	ts, ok := n.(*ast.TypeSpec)
	if !ok {
		return tv
	}

	name := ts.Name.Name
	switch ts.Type.(type) {
	case *ast.StructType:
		if _, ok := tv.ConcreteTypes[name]; !ok {
			tv.ConcreteTypes[name] = make([]Field, 0)
		}

		return &FieldVisitor{
			ConcreteTypes: tv.ConcreteTypes,
			InterfaceTypes: tv.InterfaceTypes,
			Name: name,
		}
	case *ast.ArrayType:
		at := ts.Type.(*ast.ArrayType)
		elt, ok := at.Elt.(*ast.Ident)
		if ok {
			tv.ConcreteTypes[name] = []Field{Field{"elt", ArrayType, elt.Name}}
		}
	case *ast.Ident:
		id := ts.Type.(*ast.Ident)
		tv.ConcreteTypes[name] = []Field{Field{"value", IdentType, id.Name}}
	case *ast.InterfaceType:
		tv.InterfaceTypes[name] = true
	}

	return tv
}

func (fv *FieldVisitor) Visit(n ast.Node) ast.Visitor {
	f, ok := n.(*ast.Field)
	if !ok {
		return fv
	}

	if len(f.Names) == 0 {
		return nil
	}

	name := strings.ToLower(f.Names[0].Name)
	st := fv.ConcreteTypes
	switch f.Type.(type) {
	case *ast.Ident:
		ident := f.Type.(*ast.Ident)
		st[fv.Name] = append(st[fv.Name], Field{name, IdentType, ident.Name})
	case *ast.StarExpr:
		star := f.Type.(*ast.StarExpr)
		ident, ok := star.X.(*ast.Ident)
		if ok {
			st[fv.Name] = append(st[fv.Name], Field{name, StarType, ident.Name})
		}
	case *ast.ArrayType:
		atype := f.Type.(*ast.ArrayType)
		elt, ok := atype.Elt.(*ast.Ident)
		if ok {
			st[fv.Name] = append(st[fv.Name], Field{name, ArrayType, elt.Name})
		}
	default:
		return fv
	}

	return &TypeVisitor{
		ConcreteTypes: fv.ConcreteTypes,
		InterfaceTypes: fv.InterfaceTypes,
	}
}

type FuncReceiverWalker struct {
	types map[string]bool
	name string
}

func (fw *FuncReceiverWalker) Visit(n ast.Node) ast.Visitor {
	fd, ok := n.(*ast.FuncDecl)
	if !ok {
		return fw
	}

	if fd.Name.Name != fw.name {
		return fw
	}

	// Record the name of the type of the first receiver.
	if len(fd.Recv.List) == 0 {
		return fw
	}

	field := fd.Recv.List[0]
	ident, ok := field.Type.(*ast.Ident)
	if !ok {
		return fw
	}

	fw.types[ident.Name] = true
	return fw
}

type Constant struct {
	Name string
	Value int
}

type ConstantVisitor struct {
	Constants []Constant
}

func (tv *ConstantVisitor) Visit(n ast.Node) ast.Visitor {
	vs, ok := n.(*ast.ValueSpec)
	if !ok {
		return tv
	}

	if len(vs.Names) == 0 {
		return tv
	}

	ident := vs.Names[0]
	if ident.Name == "_" {
		return tv
	}

	if ident.Obj == nil || ident.Obj.Data == nil || ident.Obj.Kind != ast.Con {
		return tv
	}

	value, ok := ident.Obj.Data.(int)
	if !ok {
		return tv
	}

	// Turn the constant name into a C++ constant name.
	name := "k" + strings.Title(ident.Name)

	tv.Constants = append(tv.Constants, Constant{name, value})
	return tv
}

func writeHeader(constants []Constant, types map[string][]Field, interfaces map[string]bool, formTypes map[string]bool, termTypes map[string]bool) []string {
	header := []string{
		"#include <memory>",
		"#include <vector>",
		"",
		"#include <google/protobuf/io/coded_stream.h>",
		"",
		"class LogicElement {",
		" public:",
		"  virtual bool Marshal(google::protobuf::io::CodedOutputStream* output) = 0;",
		"};",
		"",
		"class Form : public LogicElement {};",
		"class Term : public LogicElement {};",
		"",
	}

	constructor := "  %s(google::protobuf::io::CodedInputStream* input);"
	marshal := "  bool Marshal(google::protobuf::io::CodedOutputStream* output)"

	header = append(header, "enum class BinaryTags {")
	for i, constant := range constants {
		value := fmt.Sprintf("  %s = %d", constant.Name, constant.Value)
		if i < len(constants) - 1 {
			value += ","
		}
		header = append(header, value)
	}
	header = append(header, "};", "")

	for name, fields := range types {
		class := fmt.Sprintf("class %s", name)
		if _, isForm := formTypes[name]; isForm {
			class += ": public Form"
		} else if _, isTerm := termTypes[name]; isTerm {
			class += ": public Term"
		}

		class += " {"
		header = append(header, class, " public:")

		header = append(header, fmt.Sprintf(constructor, name))
		header = append(header, fmt.Sprintf("%s override;", marshal))

		if len(fields) > 0 {
			header = append(header, " private:")
		}

		for _, info := range fields {
			typeName := info.TypeName

			if primitives[typeName] {
				if info.Type == StarType {
					typeName = typeName + "*"
				}
				header = append(header, fmt.Sprintf("  %s %s_;", typeName, info.Name))
				continue
			}

			switch info.Type {
			case IdentType, StarType:
				header = append(header, fmt.Sprintf("  std::unique_ptr<%s> %s_;", info.TypeName, info.Name))
			case ArrayType:
				if info.TypeName == "byte" {
					header = append(header, fmt.Sprintf("  string %s_;", info.Name))
					continue
				}

				typeName := info.TypeName
				if interfaces[typeName] {
					typeName = "std::unique_ptr<" + typeName + ">"
				}
				header = append(header, fmt.Sprintf("  std::vector<%s> %ss_;", typeName, info.Name))
			}
		}

		header = append(header, "};", "")
	}

	return header
}

func main() {
	output := os.Stdout

	tset := token.NewFileSet()
	tf, err := parser.ParseFile(tset, "../../tao/auth/binary.go", nil, 0)
	if err != nil {
		fmt.Println(err)
		return
	}

	//ast.Print(tset, tf)

	constantVisitor := &ConstantVisitor{
		Constants: make([]Constant, 0),
	}

	ast.Walk(constantVisitor, tf)

	//fmt.Fprintf(output, "The constants are as follows: %v\n", constantVisitor.Constants)

	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "../../tao/auth/ast.go", nil, 0)
	if err != nil {
		fmt.Println(err)
		return
	}

	tv := &TypeVisitor{
		ConcreteTypes: make(map[string][]Field),
		InterfaceTypes: make(map[string]bool),
	}
	ast.Walk(tv, f)

	//fmt.Printf("The full set of types is %+v\n", tv)

	formWalker := &FuncReceiverWalker{
		types: make(map[string]bool),
		name: "isForm",
	}
	ast.Walk(formWalker, f)

	//fmt.Printf("The following types are Forms: %v\n", formWalker.types)

	termWalker := &FuncReceiverWalker{
		types: make(map[string]bool),
		name: "isTerm",
	}
	ast.Walk(termWalker, f)

	//fmt.Printf("The following types are Terms: %v\n", termWalker.types)

	header := writeHeader(constantVisitor.Constants, tv.ConcreteTypes, tv.InterfaceTypes, formWalker.types, termWalker.types)
	for _, line := range header {
		fmt.Fprintln(output, line)
	}

	// Generate the class declarations with inheritance that puts the Marshal method on everything. Everything should have a constructor based on the protobuf streams.
	//
	// Then generate the marshalling/demarshalling code.

	// The following line prints the full (immense) AST.
	//ast.Print(fset, f)
}
<|endoftext|>


<commit_before>package gonx

import (
	"bufio"
	"io"
	"sync"
)

func handleError(err error) {
	//fmt.Fprintln(os.Stderr, err)
}

// Iterate over given file and map each it's line into Entry record using parser.
// Results will be written into output Entries channel.
func EntryMap(file io.Reader, parser *Parser, output chan Entry) {
	var wg sync.WaitGroup

	// Iterate over log file lines and spawn new mapper goroutine
	// to parse it into given format
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		wg.Add(1)
		go func(line string) {
			defer wg.Done()
			entry, err := parser.ParseString(line)
			if err == nil {
				output <- entry
			} else {
				handleError(err)
			}
		}(scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		handleError(err)
	}
	// Wait until all files will be read and all lines will be
	// parsed and wrote to the Entries channel
	wg.Wait()
}
<commit_msg>control mappers spawn with semaphore, fix memory leak<commit_after>package gonx

import (
	"bufio"
	"io"
	"sync"
)

func handleError(err error) {
	//fmt.Fprintln(os.Stderr, err)
}

// Iterate over given file and map each it's line into Entry record using parser.
// Results will be written into output Entries channel.
func EntryMap(file io.Reader, parser *Parser, output chan Entry) {
	// Input file lines. This channel is unbuffered to publish
	// next line to handle only when previous is taken by mapper.
	var lines = make(chan string)

	// Host thread to spawn new mappers
	var quit = make(chan int)
	go func(topLoad int) {
		// Create semafore channel with capacity equal to the output channel
		// capacity. Use it to control mapper goroutines spawn.
		var sem = make(chan bool, topLoad)
		for i := 0; i < topLoad; i++ {
			// Ready to go!
			sem <- true
		}

		var wg sync.WaitGroup
		for {
			// Wait until semaphore becomes available and run a mapper
			if !<-sem {
				// Stop the host loop if false received from semaphore
				break
			}
			wg.Add(1)
			go func() {
				defer wg.Done()
				// Take next file line to map. Check is channel closed.
				line, ok := <-lines
				// Return immediately if lines channel is closed
				if !ok {
					// Send false to semaphore channel to indicate that job's done
					sem <- false
					return
				}
				entry, err := parser.ParseString(line)
				if err == nil {
					// Write result Entry to the output channel. This will
					// block goroutine runtime until channel is free to
					// accept new item.
					output <- entry
				} else {
					handleError(err)
				}
				// Increment semaphore to allow new mapper workers to spawn
				sem <- true
			}()
		}
		// Wait for all mappers to complete, then send a quit signal
		wg.Wait()
		quit <- 1
	}(cap(output))

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		// Read next line from the file and feed mapper routines.
		lines <- scanner.Text()
	}
	close(lines)

	if err := scanner.Err(); err != nil {
		handleError(err)
	}

	<-quit
}
<|endoftext|>


<commit_before>package imports

import (
	"context"
	"strings"
	"testing"

	assert "github.com/stretchr/testify/require"
	"go.skia.org/infra/go/testutils"
	"go.skia.org/infra/go/util"
)

func TestNoPackageImportsTesting(t *testing.T) {
	testutils.LargeTest(t)

	// Assert that no package imports "testing".
	importers, err := FindImporters(context.Background(), "testing")
	assert.NoError(t, err)
	assert.Emptyf(t, importers, "No non-test packages should import \"testing\" but the following do:\n%s", strings.Join(importers, "\n"))
}

// Any package named testutil(s) and the packages in the
// following list are allowed to import testify
var testifyExceptions = []string{
	"go.skia.org/infra/go/deepequal",
	"go.skia.org/infra/go/mockhttpclient",
	"go.skia.org/infra/golden/go/mocks",
}

var unwantedTestifyPackages = []string{
	"github.com/stretchr/testify/require",
	"github.com/stretchr/testify/assert",
}

// TODO(kjlubick): this list should be empty.
// items should not be added to this list, only removed.
var legacyTestifyPackages = []string{
	"go.skia.org/infra/task_driver/go/db",
	"go.skia.org/infra/task_driver/go/td",
	"go.skia.org/infra/task_scheduler/go/db",
	"go.skia.org/infra/task_scheduler/go/isolate_cache",
	"go.skia.org/infra/task_scheduler/go/tryjobs",
}

func TestNoPackageImportsTestify(t *testing.T) {
	testutils.LargeTest(t)

	for _, p := range unwantedTestifyPackages {

		// Assert that no package has this import
		importers, err := FindImporters(context.Background(), p)
		assert.NoError(t, err)
		badImports := []string{}
		for _, i := range importers {
			if !strings.HasSuffix(i, "testutil") && !strings.HasSuffix(i, "testutils") && !util.In(i, testifyExceptions) && !util.In(i, legacyTestifyPackages) {
				badImports = append(badImports, i)
			}
		}
		assert.Emptyf(t, badImports, "No non-test or non-testutil packages should import %q but the following do:\n%s", p, strings.Join(badImports, "\n"))
	}
}
<commit_msg>use superior error message in imports test<commit_after>package imports

import (
	"context"
	"strings"
	"testing"

	assert "github.com/stretchr/testify/require"
	"go.skia.org/infra/go/testutils"
	"go.skia.org/infra/go/util"
)

func TestNoPackageImportsTesting(t *testing.T) {
	testutils.LargeTest(t)

	// Assert that no package imports "testing".
	importers, err := FindImporters(context.Background(), "testing")
	assert.NoError(t, err)
	assert.Emptyf(t, importers, "No non-test packages should import \"testing\" but the following do:\n%s", strings.Join(importers, "\n"))
}

// Any package named testutil(s) and the packages in the
// following list are allowed to import testify
var testifyExceptions = []string{
	"go.skia.org/infra/go/deepequal",
	"go.skia.org/infra/go/mockhttpclient",
	"go.skia.org/infra/golden/go/mocks",
}

var unwantedTestifyPackages = []string{
	"github.com/stretchr/testify/require",
	"github.com/stretchr/testify/assert",
}

// TODO(kjlubick): this list should be empty.
// items should not be added to this list, only removed.
var legacyTestifyPackages = []string{
	"go.skia.org/infra/task_driver/go/db",
	"go.skia.org/infra/task_driver/go/td",
	"go.skia.org/infra/task_scheduler/go/db",
	"go.skia.org/infra/task_scheduler/go/isolate_cache",
	"go.skia.org/infra/task_scheduler/go/tryjobs",
}

func TestNoPackageImportsTestify(t *testing.T) {
	testutils.LargeTest(t)

	for _, p := range unwantedTestifyPackages {

		// Assert that no package has this import
		importers, err := FindImporters(context.Background(), p)
		assert.NoError(t, err)
		badImports := []string{}
		for _, i := range importers {
			if !strings.HasSuffix(i, "testutil") && !strings.HasSuffix(i, "testutils") && !util.In(i, testifyExceptions) && !util.In(i, legacyTestifyPackages) {
				badImports = append(badImports, i)
			}
		}
		assert.Emptyf(t, badImports, "Only test or testutil packages should import %q. Violators:\n%s", p, strings.Join(badImports, "\n"))
	}
}
<|endoftext|>


<commit_before>package libkb

import (
	// "fmt"
	"encoding/json"
	"reflect"
	"sort"
	"testing"
	"time"

	jsonw "github.com/keybase/go-jsonw"

	testvectors "github.com/keybase/keybase-test-vectors/go"
)

// Returns a map from error name strings to sets of Go error types. If a test
// returns any error type in the corresponding set, it's a pass. (The reason
// the types aren't one-to-one here is that implementation differences between
// the Go and JS sigchains make that more trouble than it's worth.)
func getErrorTypesMap() map[string]map[reflect.Type]bool {
	return map[string]map[reflect.Type]bool{
		"CTIME_MISMATCH": map[reflect.Type]bool{
			reflect.TypeOf(CtimeMismatchError{}): true,
		},
		"EXPIRED_SIBKEY": map[reflect.Type]bool{
			reflect.TypeOf(KeyExpiredError{}): true,
		},
		"FINGERPRINT_MISMATCH": map[reflect.Type]bool{
			reflect.TypeOf(ChainLinkFingerprintMismatchError{}): true,
		},
		"INVALID_SIBKEY": map[reflect.Type]bool{
			reflect.TypeOf(KeyRevokedError{}): true,
		},
		"KEY_OWNERSHIP": map[reflect.Type]bool{
			reflect.TypeOf(KeyFamilyError{}): true,
		},
		"KID_MISMATCH": map[reflect.Type]bool{
			reflect.TypeOf(ChainLinkKIDMismatchError{}): true,
		},
		"NONEXISTENT_KID": map[reflect.Type]bool{
			reflect.TypeOf(KeyFamilyError{}): true,
		},
		"NOT_LATEST_SUBCHAIN": map[reflect.Type]bool{
			reflect.TypeOf(NotLatestSubchainError{}): true,
		},
		"REVERSE_SIG_VERIFY_FAILED": map[reflect.Type]bool{
			reflect.TypeOf(ReverseSigError{}): true,
		},
		"VERIFY_FAILED": map[reflect.Type]bool{
			reflect.TypeOf(BadSigError{}): true,
		},
		"WRONG_UID": map[reflect.Type]bool{
			reflect.TypeOf(UIDMismatchError{}): true,
		},
		"WRONG_USERNAME": map[reflect.Type]bool{
			reflect.TypeOf(BadUsernameError{}): true,
		},
		"WRONG_SEQNO": map[reflect.Type]bool{
			reflect.TypeOf(ChainLinkWrongSeqnoError{}): true,
		},
		"WRONG_PREV": map[reflect.Type]bool{
			reflect.TypeOf(ChainLinkPrevHashMismatchError{}): true,
		},
	}
}

// One of the test cases from the JSON list of all tests.
type TestCase struct {
	Input   string `json:"input"`
	Len     int    `json:"len"`
	Sibkeys int    `json:"sibkeys"`
	Subkeys int    `json:"subkeys"`
	ErrType string `json:"err_type"`
	Eldest  string `json:"eldest"`
}

// The JSON list of all test cases.
type TestList struct {
	Tests      map[string]TestCase `json:"tests"`
	ErrorTypes []string            `json:"error_types"`
}

// The input data for a single test. Each tests has its own input JSON file.
type TestInput struct {
	// We omit the "chain" member here, because we need it in blob form.
	Username  string            `json:"username"`
	UID       string            `json:"uid"`
	Keys      []string          `json:"keys"`
	LabelKids map[string]string `json:"label_kids"`
	LabelSigs map[string]string `json:"label_sigs"`
}

func TestAllChains(t *testing.T) {
	tc := SetupTest(t, "test_all_chains")
	defer tc.Cleanup()

	var testList TestList
	json.Unmarshal([]byte(testvectors.ChainTests), &testList)
	// Always do the tests in alphabetical order.
	testNames := []string{}
	for name := range testList.Tests {
		testNames = append(testNames, name)
	}
	sort.Strings(testNames)
	for _, name := range testNames {
		testCase := testList.Tests[name]
		G.Log.Info("starting sigchain test case %s (%s)", name, testCase.Input)
		doChainTest(t, testCase)
	}
}

func doChainTest(t *testing.T, testCase TestCase) {
	inputJSON, exists := testvectors.ChainTestInputs[testCase.Input]
	if !exists {
		t.Fatal("missing test input: " + testCase.Input)
	}
	// Unmarshal test input in two ways: once for the structured data and once
	// for the chain link blobs.
	var input TestInput
	err := json.Unmarshal([]byte(inputJSON), &input)
	if err != nil {
		t.Fatal(err)
	}
	inputBlob, err := jsonw.Unmarshal([]byte(inputJSON))
	if err != nil {
		t.Fatal(err)
	}
	uid, err := UIDFromHex(input.UID)
	if err != nil {
		t.Fatal(err)
	}
	sigchain := SigChain{username: input.Username, uid: uid}
	chainLen, err := inputBlob.AtKey("chain").Len()
	if err != nil {
		t.Fatal(err)
	}
	for i := 0; i < chainLen; i++ {
		linkBlob := inputBlob.AtKey("chain").AtIndex(i)
		link, err := ImportLinkFromServer(&sigchain, linkBlob, uid)
		if err != nil {
			t.Fatal(err)
		}
		sigchain.chainLinks = append(sigchain.chainLinks, link)
	}

	// Get the eldest key. This is assumed to be the first key in the list of
	// bundles, unless the "eldest" field is given in the test description, in
	// which case the eldest key is specified by name.
	var eldestKID KID
	if testCase.Eldest == "" {
		eldestKey, err := ParseGenericKey(input.Keys[0])
		if err != nil {
			t.Fatal(err)
		}
		eldestKID = eldestKey.GetKid()
	} else {
		eldestKIDStr, found := input.LabelKids[testCase.Eldest]
		if !found {
			t.Fatalf("No KID found for label %s", testCase.Eldest)
		}
		eldestKID, err = ImportKID(eldestKIDStr)
		if err != nil {
			t.Fatal(err)
		}
	}

	// Parse all the bundles.
	keyFamily, err := createKeyFamily(input.Keys)
	if err != nil {
		t.Fatal(err)
	}

	// Run the actual sigchain verification. This is most of the code that's
	// actually being tested.
	ckf := ComputedKeyFamily{kf: keyFamily}
	sigchainErr := sigchain.VerifyChain()
	if sigchainErr == nil {
		_, sigchainErr = sigchain.VerifySigsAndComputeKeys(&eldestKID, &ckf)
	}

	// Some tests expect an error. If we get one, make sure it's the right
	// type.
	if testCase.ErrType != "" {
		if sigchainErr == nil {
			t.Fatalf("Expected %s error from VerifySigsAndComputeKeys. No error returned.", testCase.ErrType)
		}
		foundType := reflect.TypeOf(sigchainErr)
		expectedTypes := getErrorTypesMap()[testCase.ErrType]
		if expectedTypes == nil || len(expectedTypes) == 0 {
			msg := "No Go error types defined for expected failure %s.\n" +
				"This could be because of new test cases in github.com/keybase/keybase-test-vectors.\n" +
				"Go error returned: %s"
			t.Fatalf(msg, testCase.ErrType, foundType)
		}
		if expectedTypes[foundType] {
			// Success! We found the error we expected. This test is done.
			G.Log.Debug("EXPECTED error encountered", sigchainErr)
			return
		}

		// Got an error, but one of the wrong type. Tests with error names
		// that are missing from the map (maybe because we add new test
		// cases in the future) will also hit this branch.
		t.Fatalf("Wrong error type encountered. Expected %#v (%s), got %s: %s",
			expectedTypes, testCase.ErrType, foundType, sigchainErr)

	}

	// Tests that expected an error terminated above. Tests that get here
	// should succeed without errors.
	if sigchainErr != nil {
		t.Fatal(err)
	}

	// Check the expected results: total unrevoked links, sibkeys, and subkeys.
	unrevokedCount := 0
	idtable, err := NewIdentityTable(FOKID{Kid: eldestKID}, &sigchain, nil)
	if err != nil {
		t.Fatal(err)
	}
	for _, link := range idtable.links {
		if !link.IsRevoked() {
			unrevokedCount++
		}
	}
	if unrevokedCount != testCase.Len {
		t.Fatalf("Expected %d unrevoked links, but found %d.", testCase.Len, unrevokedCount)
	}
	// Don't use the current time to get keys, because that will cause test
	// failures 5 years from now :-D
	testTime := getCurrentTimeForTest(sigchain, keyFamily)
	numSibkeys := len(ckf.GetAllActiveSibkeysAtTime(testTime))
	if numSibkeys != testCase.Sibkeys {
		t.Fatalf("Expected %d sibkeys, got %d", testCase.Sibkeys, numSibkeys)
	}
	numSubkeys := len(ckf.GetAllActiveSubkeysAtTime(testTime))
	if numSubkeys != testCase.Subkeys {
		t.Fatalf("Expected %d subkeys, got %d", testCase.Subkeys, numSubkeys)
	}

	// Success!
}

func createKeyFamily(bundles []string) (*KeyFamily, error) {
	allKeys := jsonw.NewArray(len(bundles))
	for i, bundle := range bundles {
		err := allKeys.SetIndex(i, jsonw.NewString(bundle))
		if err != nil {
			return nil, err
		}
	}
	publicKeys := jsonw.NewDictionary()
	publicKeys.SetKey("all_bundles", allKeys)
	return ParseKeyFamily(publicKeys)
}

func getCurrentTimeForTest(sigChain SigChain, keyFamily *KeyFamily) time.Time {
	// Pick a test time that's the latest ctime of all links and PGP keys.
	var t time.Time
	for _, link := range sigChain.chainLinks {
		linkCTime := time.Unix(link.unpacked.ctime, 0)
		if linkCTime.After(t) {
			t = linkCTime
		}
	}
	for _, pgp := range keyFamily.pgps {
		keyCTime := pgp.PrimaryKey.CreationTime
		if keyCTime.After(t) {
			t = keyCTime
		}
	}
	return t
}
<commit_msg>gofmt -s<commit_after>package libkb

import (
	// "fmt"
	"encoding/json"
	"reflect"
	"sort"
	"testing"
	"time"

	jsonw "github.com/keybase/go-jsonw"

	testvectors "github.com/keybase/keybase-test-vectors/go"
)

// Returns a map from error name strings to sets of Go error types. If a test
// returns any error type in the corresponding set, it's a pass. (The reason
// the types aren't one-to-one here is that implementation differences between
// the Go and JS sigchains make that more trouble than it's worth.)
func getErrorTypesMap() map[string]map[reflect.Type]bool {
	return map[string]map[reflect.Type]bool{
		"CTIME_MISMATCH": {
			reflect.TypeOf(CtimeMismatchError{}): true,
		},
		"EXPIRED_SIBKEY": {
			reflect.TypeOf(KeyExpiredError{}): true,
		},
		"FINGERPRINT_MISMATCH": {
			reflect.TypeOf(ChainLinkFingerprintMismatchError{}): true,
		},
		"INVALID_SIBKEY": {
			reflect.TypeOf(KeyRevokedError{}): true,
		},
		"KEY_OWNERSHIP": {
			reflect.TypeOf(KeyFamilyError{}): true,
		},
		"KID_MISMATCH": {
			reflect.TypeOf(ChainLinkKIDMismatchError{}): true,
		},
		"NONEXISTENT_KID": {
			reflect.TypeOf(KeyFamilyError{}): true,
		},
		"NOT_LATEST_SUBCHAIN": {
			reflect.TypeOf(NotLatestSubchainError{}): true,
		},
		"REVERSE_SIG_VERIFY_FAILED": {
			reflect.TypeOf(ReverseSigError{}): true,
		},
		"VERIFY_FAILED": {
			reflect.TypeOf(BadSigError{}): true,
		},
		"WRONG_UID": {
			reflect.TypeOf(UIDMismatchError{}): true,
		},
		"WRONG_USERNAME": {
			reflect.TypeOf(BadUsernameError{}): true,
		},
		"WRONG_SEQNO": {
			reflect.TypeOf(ChainLinkWrongSeqnoError{}): true,
		},
		"WRONG_PREV": {
			reflect.TypeOf(ChainLinkPrevHashMismatchError{}): true,
		},
	}
}

// One of the test cases from the JSON list of all tests.
type TestCase struct {
	Input   string `json:"input"`
	Len     int    `json:"len"`
	Sibkeys int    `json:"sibkeys"`
	Subkeys int    `json:"subkeys"`
	ErrType string `json:"err_type"`
	Eldest  string `json:"eldest"`
}

// The JSON list of all test cases.
type TestList struct {
	Tests      map[string]TestCase `json:"tests"`
	ErrorTypes []string            `json:"error_types"`
}

// The input data for a single test. Each tests has its own input JSON file.
type TestInput struct {
	// We omit the "chain" member here, because we need it in blob form.
	Username  string            `json:"username"`
	UID       string            `json:"uid"`
	Keys      []string          `json:"keys"`
	LabelKids map[string]string `json:"label_kids"`
	LabelSigs map[string]string `json:"label_sigs"`
}

func TestAllChains(t *testing.T) {
	tc := SetupTest(t, "test_all_chains")
	defer tc.Cleanup()

	var testList TestList
	json.Unmarshal([]byte(testvectors.ChainTests), &testList)
	// Always do the tests in alphabetical order.
	testNames := []string{}
	for name := range testList.Tests {
		testNames = append(testNames, name)
	}
	sort.Strings(testNames)
	for _, name := range testNames {
		testCase := testList.Tests[name]
		G.Log.Info("starting sigchain test case %s (%s)", name, testCase.Input)
		doChainTest(t, testCase)
	}
}

func doChainTest(t *testing.T, testCase TestCase) {
	inputJSON, exists := testvectors.ChainTestInputs[testCase.Input]
	if !exists {
		t.Fatal("missing test input: " + testCase.Input)
	}
	// Unmarshal test input in two ways: once for the structured data and once
	// for the chain link blobs.
	var input TestInput
	err := json.Unmarshal([]byte(inputJSON), &input)
	if err != nil {
		t.Fatal(err)
	}
	inputBlob, err := jsonw.Unmarshal([]byte(inputJSON))
	if err != nil {
		t.Fatal(err)
	}
	uid, err := UIDFromHex(input.UID)
	if err != nil {
		t.Fatal(err)
	}
	sigchain := SigChain{username: input.Username, uid: uid}
	chainLen, err := inputBlob.AtKey("chain").Len()
	if err != nil {
		t.Fatal(err)
	}
	for i := 0; i < chainLen; i++ {
		linkBlob := inputBlob.AtKey("chain").AtIndex(i)
		link, err := ImportLinkFromServer(&sigchain, linkBlob, uid)
		if err != nil {
			t.Fatal(err)
		}
		sigchain.chainLinks = append(sigchain.chainLinks, link)
	}

	// Get the eldest key. This is assumed to be the first key in the list of
	// bundles, unless the "eldest" field is given in the test description, in
	// which case the eldest key is specified by name.
	var eldestKID KID
	if testCase.Eldest == "" {
		eldestKey, err := ParseGenericKey(input.Keys[0])
		if err != nil {
			t.Fatal(err)
		}
		eldestKID = eldestKey.GetKid()
	} else {
		eldestKIDStr, found := input.LabelKids[testCase.Eldest]
		if !found {
			t.Fatalf("No KID found for label %s", testCase.Eldest)
		}
		eldestKID, err = ImportKID(eldestKIDStr)
		if err != nil {
			t.Fatal(err)
		}
	}

	// Parse all the bundles.
	keyFamily, err := createKeyFamily(input.Keys)
	if err != nil {
		t.Fatal(err)
	}

	// Run the actual sigchain verification. This is most of the code that's
	// actually being tested.
	ckf := ComputedKeyFamily{kf: keyFamily}
	sigchainErr := sigchain.VerifyChain()
	if sigchainErr == nil {
		_, sigchainErr = sigchain.VerifySigsAndComputeKeys(&eldestKID, &ckf)
	}

	// Some tests expect an error. If we get one, make sure it's the right
	// type.
	if testCase.ErrType != "" {
		if sigchainErr == nil {
			t.Fatalf("Expected %s error from VerifySigsAndComputeKeys. No error returned.", testCase.ErrType)
		}
		foundType := reflect.TypeOf(sigchainErr)
		expectedTypes := getErrorTypesMap()[testCase.ErrType]
		if expectedTypes == nil || len(expectedTypes) == 0 {
			msg := "No Go error types defined for expected failure %s.\n" +
				"This could be because of new test cases in github.com/keybase/keybase-test-vectors.\n" +
				"Go error returned: %s"
			t.Fatalf(msg, testCase.ErrType, foundType)
		}
		if expectedTypes[foundType] {
			// Success! We found the error we expected. This test is done.
			G.Log.Debug("EXPECTED error encountered", sigchainErr)
			return
		}

		// Got an error, but one of the wrong type. Tests with error names
		// that are missing from the map (maybe because we add new test
		// cases in the future) will also hit this branch.
		t.Fatalf("Wrong error type encountered. Expected %#v (%s), got %s: %s",
			expectedTypes, testCase.ErrType, foundType, sigchainErr)

	}

	// Tests that expected an error terminated above. Tests that get here
	// should succeed without errors.
	if sigchainErr != nil {
		t.Fatal(err)
	}

	// Check the expected results: total unrevoked links, sibkeys, and subkeys.
	unrevokedCount := 0
	idtable, err := NewIdentityTable(FOKID{Kid: eldestKID}, &sigchain, nil)
	if err != nil {
		t.Fatal(err)
	}
	for _, link := range idtable.links {
		if !link.IsRevoked() {
			unrevokedCount++
		}
	}
	if unrevokedCount != testCase.Len {
		t.Fatalf("Expected %d unrevoked links, but found %d.", testCase.Len, unrevokedCount)
	}
	// Don't use the current time to get keys, because that will cause test
	// failures 5 years from now :-D
	testTime := getCurrentTimeForTest(sigchain, keyFamily)
	numSibkeys := len(ckf.GetAllActiveSibkeysAtTime(testTime))
	if numSibkeys != testCase.Sibkeys {
		t.Fatalf("Expected %d sibkeys, got %d", testCase.Sibkeys, numSibkeys)
	}
	numSubkeys := len(ckf.GetAllActiveSubkeysAtTime(testTime))
	if numSubkeys != testCase.Subkeys {
		t.Fatalf("Expected %d subkeys, got %d", testCase.Subkeys, numSubkeys)
	}

	// Success!
}

func createKeyFamily(bundles []string) (*KeyFamily, error) {
	allKeys := jsonw.NewArray(len(bundles))
	for i, bundle := range bundles {
		err := allKeys.SetIndex(i, jsonw.NewString(bundle))
		if err != nil {
			return nil, err
		}
	}
	publicKeys := jsonw.NewDictionary()
	publicKeys.SetKey("all_bundles", allKeys)
	return ParseKeyFamily(publicKeys)
}

func getCurrentTimeForTest(sigChain SigChain, keyFamily *KeyFamily) time.Time {
	// Pick a test time that's the latest ctime of all links and PGP keys.
	var t time.Time
	for _, link := range sigChain.chainLinks {
		linkCTime := time.Unix(link.unpacked.ctime, 0)
		if linkCTime.After(t) {
			t = linkCTime
		}
	}
	for _, pgp := range keyFamily.pgps {
		keyCTime := pgp.PrimaryKey.CreationTime
		if keyCTime.After(t) {
			t = keyCTime
		}
	}
	return t
}
<|endoftext|>


<commit_before>/*
   Copyright 2015 Shlomi Noach, courtesy Booking.com

   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 process

import (
	"github.com/github/orchestrator/go/config"
	"github.com/github/orchestrator/go/db"
	"github.com/github/orchestrator/go/raft"
	"github.com/openark/golib/log"
	"github.com/openark/golib/sqlutils"
)

// AttemptElection tries to grab leadership (become active node)
func AttemptElection() (bool, error) {
	{
		sqlResult, err := db.ExecOrchestrator(`
		insert ignore into active_node (
				anchor, hostname, token, first_seen_active, last_seen_active
			) values (
				1, ?, ?, now(), now()
			)
		`,
			ThisHostname, ProcessToken.Hash,
		)
		if err != nil {
			return false, log.Errore(err)
		}
		rows, err := sqlResult.RowsAffected()
		if err != nil {
			return false, log.Errore(err)
		}
		if rows > 0 {
			// We managed to insert a row
			return true, nil
		}
	}
	{
		// takeover from a node that has been inactive
		sqlResult, err := db.ExecOrchestrator(`
			update active_node set
				hostname = ?,
				token = ?,
				first_seen_active=now(),
				last_seen_active=now()
			where
				anchor = 1
			  and last_seen_active < (now() - interval ? second)
		`,
			ThisHostname, ProcessToken.Hash, config.ActiveNodeExpireSeconds,
		)
		if err != nil {
			return false, log.Errore(err)
		}
		rows, err := sqlResult.RowsAffected()
		if err != nil {
			return false, log.Errore(err)
		}
		if rows > 0 {
			// We managed to update a row: overtaking a previous leader
			return true, nil
		}
	}
	{
		// Update last_seen_active is this very node is already the active node
		sqlResult, err := db.ExecOrchestrator(`
			update active_node set
				last_seen_active=now()
			where
				anchor = 1
				and hostname = ?
				and token = ?
		`,
			ThisHostname, ProcessToken.Hash,
		)
		if err != nil {
			return false, log.Errore(err)
		}
		rows, err := sqlResult.RowsAffected()
		if err != nil {
			return false, log.Errore(err)
		}
		if rows > 0 {
			// Reaffirmed our own leadership
			return true, nil
		}
	}
	return false, nil
}

// GrabElection forcibly grabs leadership. Use with care!!
func GrabElection() error {
	if orcraft.IsRaftEnabled() {
		return nil
	}
	_, err := db.ExecOrchestrator(`
			replace into active_node (
					anchor, hostname, token, first_seen_active, last_seen_active
				) values (
					1, ?, ?, now(), now()
				)
			`,
		ThisHostname, ProcessToken.Hash,
	)
	return log.Errore(err)
}

// Reelect clears the way for re-elections. Active node is immediately demoted.
func Reelect() error {
	_, err := db.ExecOrchestrator(`delete from active_node where anchor = 1`)
	return log.Errore(err)
}

// ElectedNode returns the details of the elected node, as well as answering the question "is this process the elected one"?
func ElectedNode() (node NodeHealth, isElected bool, err error) {
	query := `
		select
			hostname,
			token,
			first_seen_active,
			last_seen_Active
		from
			active_node
		where
			anchor = 1
		`
	err = db.QueryOrchestratorRowsMap(query, func(m sqlutils.RowMap) error {
		node.Hostname = m.GetString("hostname")
		node.Token = m.GetString("token")
		node.FirstSeenActive = m.GetString("first_seen_active")
		node.LastSeenActive = m.GetString("last_seen_active")

		return nil
	})

	isElected = (node.Hostname == ThisHostname && node.Token == ProcessToken.Hash)
	return node, isElected, log.Errore(err)
}
<commit_msg>disabling Reelect on raft<commit_after>/*
   Copyright 2015 Shlomi Noach, courtesy Booking.com

   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 process

import (
	"github.com/github/orchestrator/go/config"
	"github.com/github/orchestrator/go/db"
	"github.com/github/orchestrator/go/raft"
	"github.com/openark/golib/log"
	"github.com/openark/golib/sqlutils"
)

// AttemptElection tries to grab leadership (become active node)
func AttemptElection() (bool, error) {
	{
		sqlResult, err := db.ExecOrchestrator(`
		insert ignore into active_node (
				anchor, hostname, token, first_seen_active, last_seen_active
			) values (
				1, ?, ?, now(), now()
			)
		`,
			ThisHostname, ProcessToken.Hash,
		)
		if err != nil {
			return false, log.Errore(err)
		}
		rows, err := sqlResult.RowsAffected()
		if err != nil {
			return false, log.Errore(err)
		}
		if rows > 0 {
			// We managed to insert a row
			return true, nil
		}
	}
	{
		// takeover from a node that has been inactive
		sqlResult, err := db.ExecOrchestrator(`
			update active_node set
				hostname = ?,
				token = ?,
				first_seen_active=now(),
				last_seen_active=now()
			where
				anchor = 1
			  and last_seen_active < (now() - interval ? second)
		`,
			ThisHostname, ProcessToken.Hash, config.ActiveNodeExpireSeconds,
		)
		if err != nil {
			return false, log.Errore(err)
		}
		rows, err := sqlResult.RowsAffected()
		if err != nil {
			return false, log.Errore(err)
		}
		if rows > 0 {
			// We managed to update a row: overtaking a previous leader
			return true, nil
		}
	}
	{
		// Update last_seen_active is this very node is already the active node
		sqlResult, err := db.ExecOrchestrator(`
			update active_node set
				last_seen_active=now()
			where
				anchor = 1
				and hostname = ?
				and token = ?
		`,
			ThisHostname, ProcessToken.Hash,
		)
		if err != nil {
			return false, log.Errore(err)
		}
		rows, err := sqlResult.RowsAffected()
		if err != nil {
			return false, log.Errore(err)
		}
		if rows > 0 {
			// Reaffirmed our own leadership
			return true, nil
		}
	}
	return false, nil
}

// GrabElection forcibly grabs leadership. Use with care!!
func GrabElection() error {
	if orcraft.IsRaftEnabled() {
		return log.Errorf("Cannot GrabElection on raft setup")
	}
	_, err := db.ExecOrchestrator(`
			replace into active_node (
					anchor, hostname, token, first_seen_active, last_seen_active
				) values (
					1, ?, ?, now(), now()
				)
			`,
		ThisHostname, ProcessToken.Hash,
	)
	return log.Errore(err)
}

// Reelect clears the way for re-elections. Active node is immediately demoted.
func Reelect() error {
	if orcraft.IsRaftEnabled() {
		return log.Errorf("Cannot Reelect on raft setup")
	}
	_, err := db.ExecOrchestrator(`delete from active_node where anchor = 1`)
	return log.Errore(err)
}

// ElectedNode returns the details of the elected node, as well as answering the question "is this process the elected one"?
func ElectedNode() (node NodeHealth, isElected bool, err error) {
	query := `
		select
			hostname,
			token,
			first_seen_active,
			last_seen_Active
		from
			active_node
		where
			anchor = 1
		`
	err = db.QueryOrchestratorRowsMap(query, func(m sqlutils.RowMap) error {
		node.Hostname = m.GetString("hostname")
		node.Token = m.GetString("token")
		node.FirstSeenActive = m.GetString("first_seen_active")
		node.LastSeenActive = m.GetString("last_seen_active")

		return nil
	})

	isElected = (node.Hostname == ThisHostname && node.Token == ProcessToken.Hash)
	return node, isElected, log.Errore(err)
}
<|endoftext|>


<commit_before>// Copyright 2013, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package worker

import (
	"flag"
	"fmt"
	"math/rand"
	"time"

	"github.com/youtube/vitess/go/vt/servenv"
	"github.com/youtube/vitess/go/vt/topo"
	"github.com/youtube/vitess/go/vt/wrangler"
	"golang.org/x/net/context"
)

var (
	minHealthyEndPoints = flag.Int("min_healthy_rdonly_endpoints", 2, "minimum number of healthy rdonly endpoints required for checker")
	// The intent of this timeout is to wait for the healthcheck to automatically return rdonly instances which have been taken out by previous *Clone or *Diff runs.
	// Therefore, the default for this variable must be higher than -health_check_interval.
	waitForHealthyEndPointsTimeout = flag.Duration("wait_for_healthy_rdonly_endpoints_timeout", 60*time.Second, "maximum time to wait if less than --min_healthy_rdonly_endpoints are available")
)

// FindHealthyRdonlyEndPoint returns a random healthy endpoint.
// Since we don't want to use them all, we require at least
// minHealthyEndPoints servers to be healthy.
// May block up to -wait_for_healthy_rdonly_endpoints_timeout.
func FindHealthyRdonlyEndPoint(ctx context.Context, wr *wrangler.Wrangler, cell, keyspace, shard string) (topo.TabletAlias, error) {
	waitDeadline := time.Now().Add(*waitForHealthyEndPointsTimeout)
	var healthyEndpoints []topo.EndPoint
	for {
		newCtx, _ := context.WithTimeout(ctx, waitDeadline.Sub(time.Now()))
		endPoints, _, err := wr.TopoServer().GetEndPoints(newCtx, cell, keyspace, shard, topo.TYPE_RDONLY)
		if err != nil {
			if err == topo.ErrNoNode {
				// If the node doesn't exist, count that as 0 available rdonly instances.
				endPoints = &topo.EndPoints{}
			} else {
				return topo.TabletAlias{}, fmt.Errorf("GetEndPoints(%v,%v,%v,rdonly) failed: %v", cell, keyspace, shard, err)
			}
		}
		healthyEndpoints = make([]topo.EndPoint, 0, len(endPoints.Entries))
		for _, entry := range endPoints.Entries {
			if len(entry.Health) == 0 {
				healthyEndpoints = append(healthyEndpoints, entry)
			}
		}
		if len(healthyEndpoints) < *minHealthyEndPoints {
			if time.Now().After(waitDeadline) {
				return topo.TabletAlias{}, fmt.Errorf("Not enough endpoints to chose from in (%v,%v/%v), have %v healthy ones, need at least %v", cell, keyspace, shard, len(healthyEndpoints), *minHealthyEndPoints)
			}
			wr.Logger().Infof("Waiting for enough endpoints to become available. available: %v required: %v Waiting up to %.1f more seconds.", len(healthyEndpoints), *minHealthyEndPoints, waitDeadline.Sub(time.Now()).Seconds())
			// Block for 1 second because 2 seconds is the -health_check_interval in integration tests.
			time.Sleep(1 * time.Second)
			continue
		}
		break
	}

	// random server in the list is what we want
	index := rand.Intn(len(healthyEndpoints))
	return topo.TabletAlias{
		Cell: cell,
		Uid:  healthyEndpoints[index].Uid,
	}, nil
}

// FindWorkerTablet will:
// - find a rdonly instance in the keyspace / shard
// - mark it as worker
// - tag it with our worker process
func FindWorkerTablet(ctx context.Context, wr *wrangler.Wrangler, cleaner *wrangler.Cleaner, cell, keyspace, shard string) (topo.TabletAlias, error) {
	tabletAlias, err := FindHealthyRdonlyEndPoint(ctx, wr, cell, keyspace, shard)
	if err != nil {
		return topo.TabletAlias{}, err
	}

	// We add the tag before calling ChangeSlaveType, so the destination
	// vttablet reloads the worker URL when it reloads the tablet.
	ourURL := servenv.ListeningURL.String()
	wr.Logger().Infof("Adding tag[worker]=%v to tablet %v", ourURL, tabletAlias)
	if err := wr.TopoServer().UpdateTabletFields(ctx, tabletAlias, func(tablet *topo.Tablet) error {
		if tablet.Tags == nil {
			tablet.Tags = make(map[string]string)
		}
		tablet.Tags["worker"] = ourURL
		return nil
	}); err != nil {
		return topo.TabletAlias{}, err
	}
	// we remove the tag *before* calling ChangeSlaveType back, so
	// we need to record this tag change after the change slave
	// type change in the cleaner.
	defer wrangler.RecordTabletTagAction(cleaner, tabletAlias, "worker", "")

	wr.Logger().Infof("Changing tablet %v to 'checker'", tabletAlias)
	shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout)
	err = wr.ChangeType(shortCtx, tabletAlias, topo.TYPE_WORKER, false /*force*/)
	cancel()
	if err != nil {
		return topo.TabletAlias{}, err
	}

	// Record a clean-up action to take the tablet back to rdonly.
	// We will alter this one later on and let the tablet go back to
	// 'spare' if we have stopped replication for too long on it.
	wrangler.RecordChangeSlaveTypeAction(cleaner, tabletAlias, topo.TYPE_RDONLY)
	return tabletAlias, nil
}

func init() {
	rand.Seed(time.Now().UnixNano())
}
<commit_msg>vtworker: Implement busy wait in FindHealthyRdonlyEndPoint() using the correct Go idiom.<commit_after>// Copyright 2013, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package worker

import (
	"flag"
	"fmt"
	"math/rand"
	"time"

	"github.com/youtube/vitess/go/vt/servenv"
	"github.com/youtube/vitess/go/vt/topo"
	"github.com/youtube/vitess/go/vt/wrangler"
	"golang.org/x/net/context"
)

var (
	minHealthyEndPoints = flag.Int("min_healthy_rdonly_endpoints", 2, "minimum number of healthy rdonly endpoints required for checker")
	// The intent of this timeout is to wait for the healthcheck to automatically return rdonly instances which have been taken out by previous *Clone or *Diff runs.
	// Therefore, the default for this variable must be higher than -health_check_interval.
	waitForHealthyEndPointsTimeout = flag.Duration("wait_for_healthy_rdonly_endpoints_timeout", 60*time.Second, "maximum time to wait if less than --min_healthy_rdonly_endpoints are available")
)

// FindHealthyRdonlyEndPoint returns a random healthy endpoint.
// Since we don't want to use them all, we require at least
// minHealthyEndPoints servers to be healthy.
// May block up to -wait_for_healthy_rdonly_endpoints_timeout.
func FindHealthyRdonlyEndPoint(ctx context.Context, wr *wrangler.Wrangler, cell, keyspace, shard string) (topo.TabletAlias, error) {
	newCtx, _ := context.WithTimeout(ctx, *waitForHealthyEndPointsTimeout)
	busyWaitTimer := time.NewTimer(0 * time.Second)
	var healthyEndpoints []topo.EndPoint
	for {
		select {
		case <-newCtx.Done():
			return topo.TabletAlias{}, fmt.Errorf("Not enough endpoints to choose from in (%v,%v/%v), have %v healthy ones, need at least %v Context Error: %v", cell, keyspace, shard, len(healthyEndpoints), *minHealthyEndPoints, newCtx.Err())
		case <-busyWaitTimer.C:
		}

		endPoints, _, err := wr.TopoServer().GetEndPoints(newCtx, cell, keyspace, shard, topo.TYPE_RDONLY)
		if err != nil {
			if err == topo.ErrNoNode {
				// If the node doesn't exist, count that as 0 available rdonly instances.
				endPoints = &topo.EndPoints{}
			} else {
				return topo.TabletAlias{}, fmt.Errorf("GetEndPoints(%v,%v,%v,rdonly) failed: %v", cell, keyspace, shard, err)
			}
		}
		healthyEndpoints = make([]topo.EndPoint, 0, len(endPoints.Entries))
		for _, entry := range endPoints.Entries {
			if len(entry.Health) == 0 {
				healthyEndpoints = append(healthyEndpoints, entry)
			}
		}
		if len(healthyEndpoints) < *minHealthyEndPoints {
			deadlineForLog, _ := newCtx.Deadline()
			wr.Logger().Infof("Waiting for enough endpoints to become available. available: %v required: %v Waiting up to %.1f more seconds.", len(healthyEndpoints), *minHealthyEndPoints, deadlineForLog.Sub(time.Now()).Seconds())
			// Block for 1 second because 2 seconds is the -health_check_interval in integration tests.
			busyWaitTimer.Reset(1 * time.Second)
		} else {
			break
		}
	}

	// random server in the list is what we want
	index := rand.Intn(len(healthyEndpoints))
	return topo.TabletAlias{
		Cell: cell,
		Uid:  healthyEndpoints[index].Uid,
	}, nil
}

// FindWorkerTablet will:
// - find a rdonly instance in the keyspace / shard
// - mark it as worker
// - tag it with our worker process
func FindWorkerTablet(ctx context.Context, wr *wrangler.Wrangler, cleaner *wrangler.Cleaner, cell, keyspace, shard string) (topo.TabletAlias, error) {
	tabletAlias, err := FindHealthyRdonlyEndPoint(ctx, wr, cell, keyspace, shard)
	if err != nil {
		return topo.TabletAlias{}, err
	}

	// We add the tag before calling ChangeSlaveType, so the destination
	// vttablet reloads the worker URL when it reloads the tablet.
	ourURL := servenv.ListeningURL.String()
	wr.Logger().Infof("Adding tag[worker]=%v to tablet %v", ourURL, tabletAlias)
	if err := wr.TopoServer().UpdateTabletFields(ctx, tabletAlias, func(tablet *topo.Tablet) error {
		if tablet.Tags == nil {
			tablet.Tags = make(map[string]string)
		}
		tablet.Tags["worker"] = ourURL
		return nil
	}); err != nil {
		return topo.TabletAlias{}, err
	}
	// we remove the tag *before* calling ChangeSlaveType back, so
	// we need to record this tag change after the change slave
	// type change in the cleaner.
	defer wrangler.RecordTabletTagAction(cleaner, tabletAlias, "worker", "")

	wr.Logger().Infof("Changing tablet %v to 'checker'", tabletAlias)
	shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout)
	err = wr.ChangeType(shortCtx, tabletAlias, topo.TYPE_WORKER, false /*force*/)
	cancel()
	if err != nil {
		return topo.TabletAlias{}, err
	}

	// Record a clean-up action to take the tablet back to rdonly.
	// We will alter this one later on and let the tablet go back to
	// 'spare' if we have stopped replication for too long on it.
	wrangler.RecordChangeSlaveTypeAction(cleaner, tabletAlias, topo.TYPE_RDONLY)
	return tabletAlias, nil
}

func init() {
	rand.Seed(time.Now().UnixNano())
}
<|endoftext|>


<commit_before>package v2

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func defaultDeprovisionRequest() *DeprovisionRequest {
	return &DeprovisionRequest{
		InstanceID: testInstanceID,
		ServiceID:  testServiceID,
		PlanID:     testPlanID,
	}
}

func defaultAsyncDeprovisionRequest() *DeprovisionRequest {
	r := defaultDeprovisionRequest()
	r.AcceptsIncomplete = true
	return r
}

const successDeprovisionResponseBody = `{}`

func successDeprovisionResponse() *DeprovisionResponse {
	return &DeprovisionResponse{}
}

const successAsyncDeprovisionResponseBody = `{
  "operation": "test-operation-key"
}`

func successDeprovisionResponseAsync() *DeprovisionResponse {
	r := successDeprovisionResponse()
	r.Async = true
	r.OperationKey = &testOperation
	return r
}

func TestDeprovisionInstance(t *testing.T) {
	cases := []struct {
		name               string
		request            *DeprovisionRequest
		httpChecks         httpChecks
		httpReaction       httpReaction
		expectedResponse   *DeprovisionResponse
		expectedErrMessage string
		expectedErr        error
	}{
		{
			name:    "success - ok",
			request: defaultDeprovisionRequest(),
			httpReaction: httpReaction{
				status: http.StatusOK,
				body:   successDeprovisionResponseBody,
			},
			expectedResponse: successDeprovisionResponse(),
		},
		{
			name:    "success - gone",
			request: defaultDeprovisionRequest(),
			httpReaction: httpReaction{
				status: http.StatusGone,
				body:   successDeprovisionResponseBody,
			},
			expectedResponse: successDeprovisionResponse(),
		},
		{
			name:    "success - async",
			request: defaultDeprovisionRequest(),
			httpReaction: httpReaction{
				status: http.StatusAccepted,
				body:   successAsyncDeprovisionResponseBody,
			},
			expectedResponse: successDeprovisionResponseAsync(),
		},
		{
			name:    "http error",
			request: defaultDeprovisionRequest(),
			httpReaction: httpReaction{
				err: fmt.Errorf("http error"),
			},
			expectedErrMessage: "http error",
		},
		{
			name:    "200 with malformed response",
			request: defaultDeprovisionRequest(),
			httpReaction: httpReaction{
				status: http.StatusOK,
				body:   malformedResponse,
			},
			expectedResponse: successDeprovisionResponse(),
		},
		{
			name:    "500 with malformed response",
			request: defaultDeprovisionRequest(),
			httpReaction: httpReaction{
				status: http.StatusInternalServerError,
				body:   malformedResponse,
			},
			expectedErrMessage: "unexpected end of JSON input",
		},
		{
			name:    "500 with conventional failure response",
			request: defaultDeprovisionRequest(),
			httpReaction: httpReaction{
				status: http.StatusInternalServerError,
				body:   conventionalFailureResponseBody,
			},
			expectedErr: testHttpStatusCodeError(),
		},
	}

	for _, tc := range cases {
		if tc.httpChecks.URL == "" {
			tc.httpChecks.URL = "/v2/service_instances/test-instance-id"
		}

		if tc.httpChecks.body == "" {
			tc.httpChecks.body = "{}"
		}

		doDeprovisionInstanceTest(t, tc.name, tc.request, tc.httpChecks, tc.httpReaction, tc.expectedResponse, tc.expectedErrMessage, tc.expectedErr)
	}
}

func doDeprovisionInstanceTest(
	t *testing.T,
	name string,
	request *DeprovisionRequest,
	httpChecks httpChecks,
	httpReaction httpReaction,
	expectedResponse *DeprovisionResponse,
	expectedErrMessage string,
	expectedErr error,
) {
	klient := &client{
		Name:          "test client",
		Verbose:       true,
		URL:           "https://example.com",
		doRequestFunc: doHTTP(t, name, httpChecks, httpReaction),
	}

	response, err := klient.DeprovisionInstance(request)
	if err != nil && expectedErrMessage == "" && expectedErr == nil {
		t.Errorf("%v: error getting catalog: %v", name, err)
		return
	} else if err != nil && expectedErrMessage != "" && expectedErrMessage != err.Error() {
		t.Errorf("%v: unexpected error message: expected %v, got %v", name, expectedErrMessage, err)
		return
	} else if err != nil && expectedErr != nil && !reflect.DeepEqual(expectedErr, err) {
		t.Errorf("%v: unexpected error: expected %+v, got %v", name, expectedErr, err)
		return
	}

	if e, a := expectedResponse, response; !reflect.DeepEqual(e, a) {
		t.Errorf("%v: unexpected diff in catalog response; expected %+v, got %+v", name, e, a)
		return
	}

}
<commit_msg>Improve unit test coverage in deprovision_test.go (#18)<commit_after>package v2

import (
	"fmt"
	"net/http"
	"reflect"
	"testing"
)

func defaultDeprovisionRequest() *DeprovisionRequest {
	return &DeprovisionRequest{
		InstanceID: testInstanceID,
		ServiceID:  testServiceID,
		PlanID:     testPlanID,
	}
}

func defaultAsyncDeprovisionRequest() *DeprovisionRequest {
	r := defaultDeprovisionRequest()
	r.AcceptsIncomplete = true
	return r
}

const successDeprovisionResponseBody = `{}`

func successDeprovisionResponse() *DeprovisionResponse {
	return &DeprovisionResponse{}
}

const successAsyncDeprovisionResponseBody = `{
  "operation": "test-operation-key"
}`

func successDeprovisionResponseAsync() *DeprovisionResponse {
	r := successDeprovisionResponse()
	r.Async = true
	r.OperationKey = &testOperation
	return r
}

func TestDeprovisionInstance(t *testing.T) {
	cases := []struct {
		name               string
		request            *DeprovisionRequest
		httpChecks         httpChecks
		httpReaction       httpReaction
		expectedResponse   *DeprovisionResponse
		expectedErrMessage string
		expectedErr        error
	}{
		{
			name:    "success - ok",
			request: defaultDeprovisionRequest(),
			httpReaction: httpReaction{
				status: http.StatusOK,
				body:   successDeprovisionResponseBody,
			},
			expectedResponse: successDeprovisionResponse(),
		},
		{
			name:    "success - gone",
			request: defaultDeprovisionRequest(),
			httpReaction: httpReaction{
				status: http.StatusGone,
				body:   successDeprovisionResponseBody,
			},
			expectedResponse: successDeprovisionResponse(),
		},
		{
			name:    "success - async",
			request: defaultAsyncDeprovisionRequest(),
			httpReaction: httpReaction{
				status: http.StatusAccepted,
				body:   successAsyncDeprovisionResponseBody,
			},
			expectedResponse: successDeprovisionResponseAsync(),
		},
		{
			name:    "accepted with malformed response",
			request: defaultAsyncDeprovisionRequest(),
			httpReaction: httpReaction{
				status: http.StatusAccepted,
				body:   malformedResponse,
			},
			expectedErrMessage: "unexpected end of JSON input",
		},
		{
			name:    "http error",
			request: defaultDeprovisionRequest(),
			httpReaction: httpReaction{
				err: fmt.Errorf("http error"),
			},
			expectedErrMessage: "http error",
		},
		{
			name:    "200 with malformed response",
			request: defaultDeprovisionRequest(),
			httpReaction: httpReaction{
				status: http.StatusOK,
				body:   malformedResponse,
			},
			expectedResponse: successDeprovisionResponse(),
		},
		{
			name:    "500 with malformed response",
			request: defaultDeprovisionRequest(),
			httpReaction: httpReaction{
				status: http.StatusInternalServerError,
				body:   malformedResponse,
			},
			expectedErrMessage: "unexpected end of JSON input",
		},
		{
			name:    "500 with conventional failure response",
			request: defaultDeprovisionRequest(),
			httpReaction: httpReaction{
				status: http.StatusInternalServerError,
				body:   conventionalFailureResponseBody,
			},
			expectedErr: testHttpStatusCodeError(),
		},
	}

	for _, tc := range cases {
		if tc.httpChecks.URL == "" {
			tc.httpChecks.URL = "/v2/service_instances/test-instance-id"
		}

		if tc.httpChecks.body == "" {
			tc.httpChecks.body = "{}"
		}

		doDeprovisionInstanceTest(t, tc.name, tc.request, tc.httpChecks, tc.httpReaction, tc.expectedResponse, tc.expectedErrMessage, tc.expectedErr)
	}
}

func doDeprovisionInstanceTest(
	t *testing.T,
	name string,
	request *DeprovisionRequest,
	httpChecks httpChecks,
	httpReaction httpReaction,
	expectedResponse *DeprovisionResponse,
	expectedErrMessage string,
	expectedErr error,
) {
	klient := &client{
		Name:          "test client",
		Verbose:       true,
		URL:           "https://example.com",
		doRequestFunc: doHTTP(t, name, httpChecks, httpReaction),
	}

	response, err := klient.DeprovisionInstance(request)
	if err != nil && expectedErrMessage == "" && expectedErr == nil {
		t.Errorf("%v: error getting catalog: %v", name, err)
		return
	} else if err != nil && expectedErrMessage != "" && expectedErrMessage != err.Error() {
		t.Errorf("%v: unexpected error message: expected %v, got %v", name, expectedErrMessage, err)
		return
	} else if err != nil && expectedErr != nil && !reflect.DeepEqual(expectedErr, err) {
		t.Errorf("%v: unexpected error: expected %+v, got %v", name, expectedErr, err)
		return
	}

	if e, a := expectedResponse, response; !reflect.DeepEqual(e, a) {
		t.Errorf("%v: unexpected diff in catalog response; expected %+v, got %+v", name, e, a)
		return
	}

}
<|endoftext|>


<commit_before>package main

import "github.com/Sirupsen/logrus"

func pluginHelp(result Result) {
	logrus.Infof("pluginHelp: request %d", result.UpdateID)

	err := sendMessage(result.Message.Chat.ID, result.Message.MessageID, `This bot have multiple uses, currently the following commands are available:

/g [keyword] - Search Google
/img [keyword] - Search for a random image
/gif [keyword] - Search for a random gif
/movie [keyword] - Search for movie info
/series [keyword] - Search for tv series info
/artist [keyword] - Search for movie/tv series artist info

More commands will be added soon, check back often for new stuff!

Results are retrieved from multiple sources, including:
* Google - http://google.com/
* Flickr - http://flickr.com/
* Giphy - http://giphy.com/
* OMDb - http://omdbapi.com/
* TMDb - http://themoviedb.org/

`, true)
	if err != nil {
		logrus.Error("pluginHelp:", err)
		return
	}
}
<commit_msg>shorten help message<commit_after>package main

import "github.com/Sirupsen/logrus"

func pluginHelp(result Result) {
	logrus.Infof("pluginHelp: request %d", result.UpdateID)

	err := sendMessage(result.Message.Chat.ID, result.Message.MessageID, `This bot have multiple uses, currently the following commands are available:

/g [keyword] - Search Google
/img [keyword] - Search for a random image
/gif [keyword] - Search for a random gif
/movie [keyword] - Search for movie info
/series [keyword] - Search for tv series info
/artist [keyword] - Search for movie/tv series artist info

More commands will be added soon, check back often for new stuff!

Results are retrieved from multiple sources, including: Google, Flickr, Giphy, OMDb and TMDb.`, true)
	if err != nil {
		logrus.Error("pluginHelp:", err)
		return
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"strings"
	"testing"
)

func TestPublish(t *testing.T) {
	repository := os.Getenv("PLUGIN_REPOSITORY")
	username := os.Getenv("PLUGIN_USERNAME")
	password := os.Getenv("PLUGIN_PASSWORD")
	plugin := Plugin{
		Repository:    repository,
		Username:      username,
		Password:      password,
		SetupFile:     "testdata/setup.py",
		Distributions: strings.Split(os.Getenv("PLUGIN_DISTRIBUTIONS"), " "),
		SkipBuild:     false,
	}
	err := plugin.Exec()
	if err != nil {
		t.Error(err)
	}
}

// TestUpload checks if a distutils upload command can be properly
// generated and formatted.
func TestUpload(t *testing.T) {
	testdata := []struct {
		distributions []string
		exp           []string
	}{
		{
			[]string{},
			[]string{"python3", "testdata/setup.py", "sdist"},
		},
		{
			[]string{"sdist", "bdist_wheel"},
			[]string{"python3", "testdata/setup.py", "sdist", "bdist_wheel"},
		},
	}
	for i, data := range testdata {
		p := Plugin{Distributions: data.distributions, SetupFile: "testdata/setup.py"}
		c := p.buildCommand()
		if len(c.Args) != len(data.exp) {
			t.Errorf("Case %d: Expected %d, got %d", i, len(data.exp), len(c.Args))
		}
		for i := range c.Args {
			if c.Args[i] != data.exp[i] {
				t.Errorf("Case %d: Expected %s, got %s", i, strings.Join(data.exp, " "), strings.Join(c.Args, " "))
			}
		}
	}
}
<commit_msg>add flag to test case<commit_after>package main

import (
	"os"
	"strings"
	"testing"
)

func TestPublish(t *testing.T) {
	repository := os.Getenv("PLUGIN_REPOSITORY")
	username := os.Getenv("PLUGIN_USERNAME")
	password := os.Getenv("PLUGIN_PASSWORD")
	plugin := Plugin{
		Repository:    repository,
		Username:      username,
		Password:      password,
		SetupFile:     "testdata/setup.py",
		Distributions: strings.Split(os.Getenv("PLUGIN_DISTRIBUTIONS"), " "),
		SkipBuild:     false,
		DistDir:       "dist/",
	}
	err := plugin.Exec()
	if err != nil {
		t.Error(err)
	}
}

// TestUpload checks if a distutils upload command can be properly
// generated and formatted.
func TestUpload(t *testing.T) {
	testdata := []struct {
		distributions []string
		exp           []string
	}{
		{
			[]string{},
			[]string{"python3", "testdata/setup.py", "sdist"},
		},
		{
			[]string{"sdist", "bdist_wheel"},
			[]string{"python3", "testdata/setup.py", "sdist", "bdist_wheel"},
		},
	}
	for i, data := range testdata {
		p := Plugin{Distributions: data.distributions, SetupFile: "testdata/setup.py"}
		c := p.buildCommand()
		if len(c.Args) != len(data.exp) {
			t.Errorf("Case %d: Expected %d, got %d", i, len(data.exp), len(c.Args))
		}
		for i := range c.Args {
			if c.Args[i] != data.exp[i] {
				t.Errorf("Case %d: Expected %s, got %s", i, strings.Join(data.exp, " "), strings.Join(c.Args, " "))
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"testing"
)

func TestInitialiseKubeconfig(t *testing.T) {

	cmd := make([]string, 2)
	cmd[0] = "install"
	cmd[1] = "--debug"

	plugin := Plugin{
		Config: Config{
			APIServer:      "http://myapiserver",
			Token:          "secret-token",
			ServiceAccount: "default-account",
			HelmCommand:    cmd,
			Namespace:      "default",
			SkipTLSVerify:  true,
		},
	}

	configfile := "config3.test"
	initialiseKubeconfig(&plugin.Config, "kubeconfig", configfile)
	data, err := ioutil.ReadFile(configfile)
	if err != nil {
		t.Errorf("Error reading file %v", err)
	}
	kubeConfigStr := string(data)

	if !strings.Contains(kubeConfigStr, "secret-token") {
		t.Errorf("Kubeconfig doesn't render token")
	}
	if !strings.Contains(kubeConfigStr, "http://myapiserver") {
		t.Errorf("Kubeconfig doesn't render APIServer")
	}
	if !strings.Contains(kubeConfigStr, "default-account") {
		t.Errorf("Kubeconfig doesn't render serviceaccount")
	}

}

func TestGetHelmCommand(t *testing.T) {
	os.Setenv("DRONE_BUILD_EVENT", "push")
	plugin := &Plugin{
		Config: Config{
			APIServer:     "http://myapiserver",
			Token:         "secret-token",
			HelmCommand:   nil,
			Namespace:     "default",
			SkipTLSVerify: true,
			Debug:         true,
			DryRun:        true,
			Chart:         "./chart/test",
			Version:       "1.2.3",
			Release:       "test-release",
			Values:        `"image.tag=v.0.1.0,nameOverride=my-over-app"`,
			Wait:          true,
			ReuseValues:   true,
			Timeout:       "500",
			Force:         true,
		},
	}
	setHelmCommand(plugin)
	res := strings.Join(plugin.Config.HelmCommand[:], " ")
	expected := "upgrade --install test-release ./chart/test --version 1.2.3 --set image.tag=v.0.1.0,nameOverride=my-over-app --namespace default --dry-run --debug --wait --reuse-values --timeout 500 --force"
	if res != expected {
		t.Errorf("Result is %s and we expected %s", res, expected)
	}
}

func TestResolveSecrets(t *testing.T) {

	testEnvs := []struct {
		prefix  string
		tag     string
		api     string
		token   string
		account string
	}{
		{prefix: "PROD", tag: "v0.1.1", api: "http://apiserver", token: "12345", account: "helm"},
		{prefix: "STAGING", tag: "12345678", api: "http://apiserver", token: "12345", account: "helm"},
	}
	for _, env := range testEnvs {
		envMap := map[string]string{
			"TAG":              env.tag,
			"API_SERVER":       env.api,
			"KUBERNETES_TOKEN": env.token,
			"SERVICE_ACCOUNT":  env.account,
		}

		for envKey, envValue := range envMap {
			os.Setenv(fmt.Sprintf("%s_%s", env.prefix, envKey), envValue)
		}

		plugin := &Plugin{
			Config: Config{
				HelmCommand:   nil,
				Namespace:     "default",
				SkipTLSVerify: true,
				Debug:         true,
				DryRun:        true,
				Chart:         "./chart/test",
				Release:       "test-release",
				Prefix:        env.prefix,
				Values:        "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}",
			},
		}

		resolveSecrets(plugin)
		// test that the subsitution works
		fmt.Println(plugin.Config.Values)
		if !strings.Contains(plugin.Config.Values, env.tag) {
			t.Errorf("env var ${TAG} not resolved %s", env.tag)
		}
		if strings.Contains(plugin.Config.Values, "${TAG}") {
			t.Errorf("env var ${TAG} not resolved %s", env.tag)
		}

		if plugin.Config.APIServer != env.api {
			t.Errorf("env var ${API_SERVER} not resolved %s", env.api)
		}
		if plugin.Config.Token != env.token {
			t.Errorf("env var ${KUBERNETES_TOKEN} not resolved %s", env.token)
		}
		if plugin.Config.ServiceAccount != env.account {
			t.Errorf("env var ${SERVICE_ACCOUNT} not resolved %s", env.account)
		}

		// clean up
		for envKey, _ := range envMap {
			os.Unsetenv(fmt.Sprintf("%s_%s", env.prefix, envKey))
		}
	}
}

func TestDetHelmRepoAdd(t *testing.T) {
	plugin := &Plugin{
		Config: Config{
			HelmCommand:   nil,
			Namespace:     "default",
			SkipTLSVerify: true,
			Debug:         true,
			DryRun:        true,
			Chart:         "./chart/test",
			Release:       "test-release",
			Prefix:        "MY",
			Values:        "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}",
			ClientOnly:    true,
			HelmRepos: []string{
				`"r1=http://r1.example.com"`, //handle quoted strings
				`r2=http://r2.example.com`,   //and unquoted strings
			},
		},
	}
	expected := []string{
		"repo add r1 http://r1.example.com",
		"repo add r2 http://r2.example.com",
	}

	for i, r := range plugin.Config.HelmRepos {
		repos, err := doHelmRepoAdd(r)
		if err != nil {
			t.Error(err)
		}
		result := strings.Join(repos, " ")
		if expected[i] != result {
			t.Errorf("Helm cannot add remote repositories - expected %q - got %q",
				expected[i],
				result,
			)
		}
	}
}

func TestHelmAddRepositoryError(t *testing.T) {
	_, err := doHelmRepoAdd("drone-helm=bad://drone-helm.example.com:443/stable")
	if err == nil {
		t.Errorf("Expect to see error when repo URL is invalid")
	}
}

func TestGetEnvVars(t *testing.T) {

	testText := "this should be ${TAG} now"
	result := getEnvVars(testText)
	if len(result) == 0 {
		t.Error("No envvar was found")
	}
	envvar := result[0]
	if !strings.Contains(envvar[2], "TAG") {
		t.Errorf("envvar not found in %s", testText)
	}
}

func TestReplaceEnvvars(t *testing.T) {
	tag := "tagged"
	os.Setenv("MY_TAG", tag)
	prefix := "MY"
	testText := "this should be ${TAG} now ${TAG}"
	result := getEnvVars(testText)
	resolved := replaceEnvvars(result, prefix, testText)
	if !strings.Contains(resolved, tag) {
		t.Errorf("EnvVar MY_TAG no replaced by %s  -- %s \n", tag, resolved)
	}
}

func TestSetHelmHelp(t *testing.T) {
	plugin := &Plugin{
		Config: Config{
			HelmCommand:   nil,
			Namespace:     "default",
			SkipTLSVerify: true,
			Debug:         true,
			DryRun:        true,
			Chart:         "./chart/test",
			Release:       "test-release",
			Prefix:        "MY",
			Values:        "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}",
		},
	}
	setHelmHelp(plugin)
	if plugin.Config.HelmCommand == nil {
		t.Error("Helm help is not displayed")
	}
}

func TestDetHelmInit(t *testing.T) {
	plugin := &Plugin{
		Config: Config{
			HelmCommand:   nil,
			Namespace:     "default",
			SkipTLSVerify: true,
			Debug:         true,
			DryRun:        true,
			Chart:         "./chart/test",
			Release:       "test-release",
			Prefix:        "MY",
			Values:        "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}",
			TillerNs:      "system-test",
		},
	}
	init := doHelmInit(plugin)
	result := strings.Join(init, " ")
	expected := "init --tiller-namespace " + plugin.Config.TillerNs

	if expected != result {
		t.Error("Tiller not installed in proper namespace")
	}
}

func TestDetHelmInitClient(t *testing.T) {
	plugin := &Plugin{
		Config: Config{
			HelmCommand:   nil,
			Namespace:     "default",
			SkipTLSVerify: true,
			Debug:         true,
			DryRun:        true,
			Chart:         "./chart/test",
			Release:       "test-release",
			Prefix:        "MY",
			Values:        "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}",
			ClientOnly:    true,
		},
	}
	init := doHelmInit(plugin)
	result := strings.Join(init, " ")
	expected := "init "
	if plugin.Config.ClientOnly {
		expected = expected + "--client-only"
	}

	if expected != result {
		t.Error("Helm cannot init in client only")
	}
}

func TestDetHelmInitUpgrade(t *testing.T) {
	plugin := &Plugin{
		Config: Config{
			HelmCommand:   nil,
			Namespace:     "default",
			SkipTLSVerify: true,
			Debug:         true,
			DryRun:        true,
			Chart:         "./chart/test",
			Release:       "test-release",
			Prefix:        "MY",
			Values:        "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}",
			Upgrade:       true,
		},
	}
	init := doHelmInit(plugin)
	result := strings.Join(init, " ")
	expected := "init "
	if plugin.Config.Upgrade {
		expected = expected + "--upgrade"
	}

	if expected != result {
		t.Error("Helm cannot init in client only")
	}
}

func TestDetHelmInitCanary(t *testing.T) {
	plugin := &Plugin{
		Config: Config{
			HelmCommand:   nil,
			Namespace:     "default",
			SkipTLSVerify: true,
			Debug:         true,
			DryRun:        true,
			Chart:         "./chart/test",
			Release:       "test-release",
			Prefix:        "MY",
			Values:        "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}",
			CanaryImage:   true,
		},
	}
	init := doHelmInit(plugin)
	result := strings.Join(init, " ")
	expected := "init "
	if plugin.Config.CanaryImage {
		expected = expected + "--canary-image"
	}

	if expected != result {
		t.Error("Helm cannot init in client only")
	}
}
func TestResolveSecretsFallback(t *testing.T) {
	tag := "v0.1.1"
	api := "http://apiserver"
	os.Setenv("MY_TAG", tag)
	os.Setenv("MY_API_SERVER", api)
	os.Setenv("MY_TOKEN", "12345")
	os.Setenv("NOTTOKEN", "99999")

	plugin := &Plugin{
		Config: Config{
			HelmCommand:   nil,
			Namespace:     "default",
			SkipTLSVerify: true,
			Debug:         true,
			DryRun:        true,
			Chart:         "./chart/test",
			Release:       "test-release",
			Prefix:        "MY",
			Values:        "image.tag=$TAG,api=${API_SERVER},nottoken=${NOTTOKEN},nameOverride=my-over-app,second.tag=${TAG}",
		},
	}

	resolveSecrets(plugin)
	// test that the subsitution works
	if !strings.Contains(plugin.Config.Values, tag) {
		t.Errorf("env var ${TAG} not resolved %s", tag)
	}
	if strings.Contains(plugin.Config.Values, "${TAG}") {
		t.Errorf("env var ${TAG} not resolved %s", tag)
	}

	if plugin.Config.APIServer != api {
		t.Errorf("env var ${API_SERVER} not resolved %s", api)
	}
	if !strings.Contains(plugin.Config.Values, "99999") {
		t.Errorf("envar ${NOTTOKEN} has not been resolved to 99999, not using prefix")
	}
}
<commit_msg>fix replaceEnvvars test and clear build<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"testing"
)

func TestInitialiseKubeconfig(t *testing.T) {

	cmd := make([]string, 2)
	cmd[0] = "install"
	cmd[1] = "--debug"

	plugin := Plugin{
		Config: Config{
			APIServer:      "http://myapiserver",
			Token:          "secret-token",
			ServiceAccount: "default-account",
			HelmCommand:    cmd,
			Namespace:      "default",
			SkipTLSVerify:  true,
		},
	}

	configfile := "config3.test"
	initialiseKubeconfig(&plugin.Config, "kubeconfig", configfile)
	data, err := ioutil.ReadFile(configfile)
	if err != nil {
		t.Errorf("Error reading file %v", err)
	}
	kubeConfigStr := string(data)

	if !strings.Contains(kubeConfigStr, "secret-token") {
		t.Errorf("Kubeconfig doesn't render token")
	}
	if !strings.Contains(kubeConfigStr, "http://myapiserver") {
		t.Errorf("Kubeconfig doesn't render APIServer")
	}
	if !strings.Contains(kubeConfigStr, "default-account") {
		t.Errorf("Kubeconfig doesn't render serviceaccount")
	}

}

func TestGetHelmCommand(t *testing.T) {
	os.Setenv("DRONE_BUILD_EVENT", "push")
	plugin := &Plugin{
		Config: Config{
			APIServer:     "http://myapiserver",
			Token:         "secret-token",
			HelmCommand:   nil,
			Namespace:     "default",
			SkipTLSVerify: true,
			Debug:         true,
			DryRun:        true,
			Chart:         "./chart/test",
			Version:       "1.2.3",
			Release:       "test-release",
			Values:        `"image.tag=v.0.1.0,nameOverride=my-over-app"`,
			Wait:          true,
			ReuseValues:   true,
			Timeout:       "500",
			Force:         true,
		},
	}
	setHelmCommand(plugin)
	res := strings.Join(plugin.Config.HelmCommand[:], " ")
	expected := "upgrade --install test-release ./chart/test --version 1.2.3 --set image.tag=v.0.1.0,nameOverride=my-over-app --namespace default --dry-run --debug --wait --reuse-values --timeout 500 --force"
	if res != expected {
		t.Errorf("Result is %s and we expected %s", res, expected)
	}
}

func TestResolveSecrets(t *testing.T) {

	testEnvs := []struct {
		prefix  string
		tag     string
		api     string
		token   string
		account string
	}{
		{prefix: "PROD", tag: "v0.1.1", api: "http://apiserver", token: "12345", account: "helm"},
		{prefix: "STAGING", tag: "12345678", api: "http://apiserver", token: "12345", account: "helm"},
	}
	for _, env := range testEnvs {
		envMap := map[string]string{
			"TAG":              env.tag,
			"API_SERVER":       env.api,
			"KUBERNETES_TOKEN": env.token,
			"SERVICE_ACCOUNT":  env.account,
		}

		for envKey, envValue := range envMap {
			os.Setenv(fmt.Sprintf("%s_%s", env.prefix, envKey), envValue)
		}

		plugin := &Plugin{
			Config: Config{
				HelmCommand:   nil,
				Namespace:     "default",
				SkipTLSVerify: true,
				Debug:         true,
				DryRun:        true,
				Chart:         "./chart/test",
				Release:       "test-release",
				Prefix:        env.prefix,
				Values:        "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}",
			},
		}

		resolveSecrets(plugin)
		// test that the subsitution works
		fmt.Println(plugin.Config.Values)
		if !strings.Contains(plugin.Config.Values, env.tag) {
			t.Errorf("env var ${TAG} not resolved %s", env.tag)
		}
		if strings.Contains(plugin.Config.Values, "${TAG}") {
			t.Errorf("env var ${TAG} not resolved %s", env.tag)
		}

		if plugin.Config.APIServer != env.api {
			t.Errorf("env var ${API_SERVER} not resolved %s", env.api)
		}
		if plugin.Config.Token != env.token {
			t.Errorf("env var ${KUBERNETES_TOKEN} not resolved %s", env.token)
		}
		if plugin.Config.ServiceAccount != env.account {
			t.Errorf("env var ${SERVICE_ACCOUNT} not resolved %s", env.account)
		}

		// clean up
		for envKey, _ := range envMap {
			os.Unsetenv(fmt.Sprintf("%s_%s", env.prefix, envKey))
		}
	}
}

func TestDetHelmRepoAdd(t *testing.T) {
	plugin := &Plugin{
		Config: Config{
			HelmCommand:   nil,
			Namespace:     "default",
			SkipTLSVerify: true,
			Debug:         true,
			DryRun:        true,
			Chart:         "./chart/test",
			Release:       "test-release",
			Prefix:        "MY",
			Values:        "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}",
			ClientOnly:    true,
			HelmRepos: []string{
				`"r1=http://r1.example.com"`, //handle quoted strings
				`r2=http://r2.example.com`,   //and unquoted strings
			},
		},
	}
	expected := []string{
		"repo add r1 http://r1.example.com",
		"repo add r2 http://r2.example.com",
	}

	for i, r := range plugin.Config.HelmRepos {
		repos, err := doHelmRepoAdd(r)
		if err != nil {
			t.Error(err)
		}
		result := strings.Join(repos, " ")
		if expected[i] != result {
			t.Errorf("Helm cannot add remote repositories - expected %q - got %q",
				expected[i],
				result,
			)
		}
	}
}

func TestHelmAddRepositoryError(t *testing.T) {
	_, err := doHelmRepoAdd("drone-helm=bad://drone-helm.example.com:443/stable")
	if err == nil {
		t.Errorf("Expect to see error when repo URL is invalid")
	}
}

func TestGetEnvVars(t *testing.T) {

	testText := "this should be ${TAG} now"
	result := getEnvVars(testText)
	if len(result) == 0 {
		t.Error("No envvar was found")
	}
	envvar := result[0]
	if !strings.Contains(envvar[2], "TAG") {
		t.Errorf("envvar not found in %s", testText)
	}
}

func TestReplaceEnvvars(t *testing.T) {
	tag := "tagged"
	os.Setenv("MY_TAG", tag)
	prefix := "MY"
	testText := "this should be ${TAG} now ${TAG}"
	result := getEnvVars(testText)
	resolved := replaceEnvvars(result, prefix, testText, false)
	if !strings.Contains(resolved, tag) {
		t.Errorf("EnvVar MY_TAG no replaced by %s  -- %s \n", tag, resolved)
	}
}

func TestSetHelmHelp(t *testing.T) {
	plugin := &Plugin{
		Config: Config{
			HelmCommand:   nil,
			Namespace:     "default",
			SkipTLSVerify: true,
			Debug:         true,
			DryRun:        true,
			Chart:         "./chart/test",
			Release:       "test-release",
			Prefix:        "MY",
			Values:        "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}",
		},
	}
	setHelmHelp(plugin)
	if plugin.Config.HelmCommand == nil {
		t.Error("Helm help is not displayed")
	}
}

func TestDetHelmInit(t *testing.T) {
	plugin := &Plugin{
		Config: Config{
			HelmCommand:   nil,
			Namespace:     "default",
			SkipTLSVerify: true,
			Debug:         true,
			DryRun:        true,
			Chart:         "./chart/test",
			Release:       "test-release",
			Prefix:        "MY",
			Values:        "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}",
			TillerNs:      "system-test",
		},
	}
	init := doHelmInit(plugin)
	result := strings.Join(init, " ")
	expected := "init --tiller-namespace " + plugin.Config.TillerNs

	if expected != result {
		t.Error("Tiller not installed in proper namespace")
	}
}

func TestDetHelmInitClient(t *testing.T) {
	plugin := &Plugin{
		Config: Config{
			HelmCommand:   nil,
			Namespace:     "default",
			SkipTLSVerify: true,
			Debug:         true,
			DryRun:        true,
			Chart:         "./chart/test",
			Release:       "test-release",
			Prefix:        "MY",
			Values:        "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}",
			ClientOnly:    true,
		},
	}
	init := doHelmInit(plugin)
	result := strings.Join(init, " ")
	expected := "init "
	if plugin.Config.ClientOnly {
		expected = expected + "--client-only"
	}

	if expected != result {
		t.Error("Helm cannot init in client only")
	}
}

func TestDetHelmInitUpgrade(t *testing.T) {
	plugin := &Plugin{
		Config: Config{
			HelmCommand:   nil,
			Namespace:     "default",
			SkipTLSVerify: true,
			Debug:         true,
			DryRun:        true,
			Chart:         "./chart/test",
			Release:       "test-release",
			Prefix:        "MY",
			Values:        "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}",
			Upgrade:       true,
		},
	}
	init := doHelmInit(plugin)
	result := strings.Join(init, " ")
	expected := "init "
	if plugin.Config.Upgrade {
		expected = expected + "--upgrade"
	}

	if expected != result {
		t.Error("Helm cannot init in client only")
	}
}

func TestDetHelmInitCanary(t *testing.T) {
	plugin := &Plugin{
		Config: Config{
			HelmCommand:   nil,
			Namespace:     "default",
			SkipTLSVerify: true,
			Debug:         true,
			DryRun:        true,
			Chart:         "./chart/test",
			Release:       "test-release",
			Prefix:        "MY",
			Values:        "image.tag=$TAG,api=${API_SERVER},nameOverride=my-over-app,second.tag=${TAG}",
			CanaryImage:   true,
		},
	}
	init := doHelmInit(plugin)
	result := strings.Join(init, " ")
	expected := "init "
	if plugin.Config.CanaryImage {
		expected = expected + "--canary-image"
	}

	if expected != result {
		t.Error("Helm cannot init in client only")
	}
}
func TestResolveSecretsFallback(t *testing.T) {
	tag := "v0.1.1"
	api := "http://apiserver"
	os.Setenv("MY_TAG", tag)
	os.Setenv("MY_API_SERVER", api)
	os.Setenv("MY_TOKEN", "12345")
	os.Setenv("NOTTOKEN", "99999")

	plugin := &Plugin{
		Config: Config{
			HelmCommand:   nil,
			Namespace:     "default",
			SkipTLSVerify: true,
			Debug:         true,
			DryRun:        true,
			Chart:         "./chart/test",
			Release:       "test-release",
			Prefix:        "MY",
			Values:        "image.tag=$TAG,api=${API_SERVER},nottoken=${NOTTOKEN},nameOverride=my-over-app,second.tag=${TAG}",
		},
	}

	resolveSecrets(plugin)
	// test that the subsitution works
	if !strings.Contains(plugin.Config.Values, tag) {
		t.Errorf("env var ${TAG} not resolved %s", tag)
	}
	if strings.Contains(plugin.Config.Values, "${TAG}") {
		t.Errorf("env var ${TAG} not resolved %s", tag)
	}

	if plugin.Config.APIServer != api {
		t.Errorf("env var ${API_SERVER} not resolved %s", api)
	}
	if !strings.Contains(plugin.Config.Values, "99999") {
		t.Errorf("envar ${NOTTOKEN} has not been resolved to 99999, not using prefix")
	}
}
<|endoftext|>