<commit_before>// Package javascript implements Javascript code generation from compiled VDL packages.
package javascript

// Generates the javascript source code for vdl files.  The generated output in javascript
// differs from most other languages, since we don't generate stubs. Instead generate an
// object that contains the parsed VDL structures that will be used by the Javascript code
// to valid servers.

import (
	"bytes"
	"fmt"
	"path/filepath"
	"strconv"
	"text/template"

	"veyron.io/veyron/veyron2/vdl"
	"veyron.io/veyron/veyron2/vdl/codegen"
	"veyron.io/veyron/veyron2/vdl/compile"
	"veyron.io/veyron/veyron2/vdl/vdlutil"
)

type data struct {
	Pkg            *compile.Package
	Env            *compile.Env
	GenerateImport func(string) string
	UserImports    codegen.Imports
}

// Generate takes a populated compile.Package and produces a byte slice
// containing the generated Javascript code.
func Generate(pkg *compile.Package, env *compile.Env, genImport func(string) string) []byte {
	data := data{
		Pkg:            pkg,
		Env:            env,
		GenerateImport: genImport,
		UserImports:    codegen.ImportsForFiles(pkg.Files...),
	}
	var buf bytes.Buffer
	if err := javascriptTemplate.Execute(&buf, data); err != nil {
		// We shouldn't see an error; it means our template is buggy.
		panic(fmt.Errorf("vdl: couldn't execute template: %v", err))
	}
	return buf.Bytes()
}

var javascriptTemplate *template.Template

func numOutArgs(method *compile.Method) int {
	return len(method.OutArgs) - 1
}

func bitlen(kind vdl.Kind) int {
	switch kind {
	case vdl.Float32, vdl.Complex64:
		return 32
	case vdl.Float64, vdl.Complex128:
		return 64
	}
	panic(fmt.Errorf("vdl: bitLen unhandled kind %v", kind))
}

func genMethodTags(data data, method *compile.Method) string {
	tags := method.Tags
	result := "["
	for _, tag := range tags {
		result += typedConst(data, tag) + ", "
	}
	result += "]"
	return result
}

func unTypedConst(data data, v *vdl.Value) string {
	switch v.Kind() {
	case vdl.Bool:
		if v.Bool() {
			return "true"
		} else {
			return "false"
		}
	case vdl.Byte:
		return strconv.FormatUint(uint64(v.Byte()), 10)
	case vdl.Uint16, vdl.Uint32, vdl.Uint64:
		return strconv.FormatUint(v.Uint(), 10)
	case vdl.Int16, vdl.Int32, vdl.Int64:
		return strconv.FormatInt(v.Int(), 10)
	case vdl.Float32, vdl.Float64:
		return strconv.FormatFloat(v.Float(), 'g', -1, bitlen(v.Kind()))
	case vdl.String:
		return strconv.Quote(v.RawString())
	case vdl.OneOf, vdl.Any, vdl.Nilable:
		if elem := v.Elem(); elem != nil {
			return typedConst(data, elem)
		}
		return "null"
	case vdl.Complex64, vdl.Complex128:
		return fmt.Sprintf("new Complex(%f, %f)", real(v.Complex()), imag(v.Complex()))
	case vdl.Enum:
		return strconv.FormatInt(int64(v.EnumIndex()), 10)
	case vdl.Array, vdl.List:
		s := "["
		for ix := 0; ix < v.Len(); ix++ {
			s += "\n" + typedConst(data, v.Index(ix)) + ","
		}
		return s + "\n]"
	case vdl.Set, vdl.Map:
		s := "{"
		for _, key := range v.Keys() {
			s += "\n  " + unTypedConst(data, key) + ":"
			if v.Kind() == vdl.Set {
				s += "true"
			} else {
				s += typedConst(data, v.MapIndex(key))
			}
			s += ","
			// TODO(bjornick): Deal with non-string keys.
		}
		s += "}"
		return s
	case vdl.Struct:
		s := "{"
		t := v.Type()
		for ix := 0; ix < t.NumField(); ix++ {
			s += "\n  '" + t.Field(ix).Name + "': " + typedConst(data, v.Field(ix)) + ","
		}
		return s + "\n}"

	}

	panic(fmt.Errorf("vdl: unTypedConst unhandled type %v %v", v.Kind(), v.Type()))
}

func primitiveWithOptionalName(primitive, name string) string {
	if name == "" {
		return "Types." + primitive
	}
	return "{kind: Kind." + primitive + ", name: '" + name + "'}"
}

func typeStruct(t *vdl.Type) string {
	nameField := ""
	if t.Name() != "" {
		nameField = "\n    name: '" + t.Name() + "',"
	}

	switch t.Kind() {
	case vdl.Any:
		return primitiveWithOptionalName("ANY", t.Name())
	case vdl.Bool:
		return primitiveWithOptionalName("BOOL", t.Name())
	case vdl.Byte:
		return primitiveWithOptionalName("BYTE", t.Name())
	case vdl.Uint16:
		return primitiveWithOptionalName("UINT16", t.Name())
	case vdl.Uint32:
		return primitiveWithOptionalName("UINT32", t.Name())
	case vdl.Uint64:
		return primitiveWithOptionalName("UINT64", t.Name())
	case vdl.Int16:
		return primitiveWithOptionalName("INT16", t.Name())
	case vdl.Int32:
		return primitiveWithOptionalName("INT32", t.Name())
	case vdl.Int64:
		return primitiveWithOptionalName("INT64", t.Name())
	case vdl.Float32:
		return primitiveWithOptionalName("FLOAT32", t.Name())
	case vdl.Float64:
		return primitiveWithOptionalName("FLOAT64", t.Name())
	case vdl.Complex64:
		return primitiveWithOptionalName("COMPLEX64", t.Name())
	case vdl.Complex128:
		return primitiveWithOptionalName("COMPLEX128", t.Name())
	case vdl.String:
		return primitiveWithOptionalName("STRING", t.Name())
	case vdl.Enum:
		labels := ""
		for i := 0; i < t.NumEnumLabel(); i++ {
			labels += "'" + t.EnumLabel(i) + "', "
		}
		return fmt.Sprintf(`{
    kind: Kind.ENUM,
    name: '%s',
    labels: [%s]
  }`, t.Name(), labels)
	case vdl.TypeVal:
		return "{}"

	// TODO(bjornick): Handle recursive types and de-duping of the same struct definitions over
	// and over again.
	case vdl.Array:
		return fmt.Sprintf(`{
    kind: Kind.ARRAY,%s
    elem: %s,
    len: %d
  }`, nameField, typeStruct(t.Elem()), t.Len())
	case vdl.List:
		return `{
    kind: Kind.LIST,` + nameField + `
    elem: ` + typeStruct(t.Elem()) + `
  }`
	case vdl.Set:
		return `{
    kind: Kind.SET,` + nameField + `
    key: ` + typeStruct(t.Key()) + `
  }`
	case vdl.Map:
		return fmt.Sprintf(`{
    kind: Kind.MAP,%s
    key: %s,
    elem: %s
  }`, nameField, typeStruct(t.Key()), typeStruct(t.Elem()))
	case vdl.Struct:
		fields := ""
		for i := 0; i < t.NumField(); i++ {
			f := t.Field(i)
			fields += fmt.Sprintf(`
    {
      name: '%s',
      type: %s
    },`, f.Name, typeStruct(f.Type))
		}
		return fmt.Sprintf(`{
    kind: Kind.STRUCT,
    name: '%s',
    fields: [%s
  ]}`, t.Name(), fields)
	}
	return ""
}

func qualifiedIdent(data data, name string, file *compile.File) string {
	if data.Pkg.Path == file.Package.Path {
		return name
	}
	return data.UserImports.LookupLocal(file.Package.Path) + "." + name
}

func defaultValue(data data, t *vdl.Type) string {
	if def := data.Env.FindTypeDef(t); def != nil {
		switch {
		case t == vdl.AnyType || t == vdl.TypeValType || t == compile.ErrorType:
			return "null"
		case def.File != compile.BuiltInFile:
			return "new " + qualifiedIdent(data, def.Name, def.File) + "()"
		}
	}
	switch t.Kind() {
	case vdl.Nilable:
		return "null"
	case vdl.Array, vdl.List:
		return "[]"
	case vdl.Set, vdl.Map:
		return "{}"
	case vdl.Byte, vdl.Uint16, vdl.Uint32, vdl.Uint64, vdl.Int16, vdl.Int32, vdl.Int64, vdl.Float32, vdl.Float64:
		return "0"
	case vdl.Complex64, vdl.Complex128:
		return "new Complex(0, 0)"
	case vdl.String:
		return "''"
	case vdl.Bool:
		return "false"
	}
	panic(fmt.Sprintf("Unhandled type %v", t))
}

func generateTypeStub(data data, t *compile.TypeDef) string {
	name := t.Name
	wrappedType := true
	kind := t.Type.Kind()
	// We can't always have Map be a wrapped type, since the key doesn't
	// have to be a string.
	// TODO(bjornick): Handle maps without string keys.
	switch kind {
	case vdl.Struct, vdl.Any, vdl.Enum:
		wrappedType = false
	}

	result := "function " + name + "(val) {"

	if wrappedType {
		result += "\n    this.val = val;\n"
	} else if kind == vdl.Struct {
		result += "\n    val = val || {};"
		for i := 0; i < t.Type.NumField(); i++ {
			field := t.Type.Field(i)
			result += fmt.Sprintf("\n    this.%s = val.%s || %s;", field.Name, field.Name, defaultValue(data, field.Type))
		}
		result += "\n"
	} else if kind == vdl.Enum {
		// The default value of an enum type is the first label which is mapped
		// to 0.
		result += "\n    this.val = 0;\n"
	}
	result += "  }\n"
	result += name + ".prototype._type = " + typeStruct(t.Type) + ";\n"

	result += "  types." + name + " = " + name + ";\n"

	return result
}

func typedConst(data data, v *vdl.Value) string {
	valstr := unTypedConst(data, v)
	t := v.Type()
	if def := data.Env.FindTypeDef(t); def != nil {
		if def.File != compile.BuiltInFile {
			return fmt.Sprintf("new %s(%s)", qualifiedIdent(data, def.Name, def.File), valstr)
		}
	}
	return valstr
}

func generateConstDefinition(data data, c *compile.ConstDef) string {
	return fmt.Sprintf("  %s: %s,", c.Name, typedConst(data, c.Value))
}

func importPath(data data, path string) string {
	// We need to prefix all of these paths with a ./ to tell node that the path is relative to
	// the current directory.  Sadly filepath.Join(".", foo) == foo, so we have to do it
	// explicitly.
	return "." + string(filepath.Separator) + data.GenerateImport(path)

}

func init() {
	funcMap := template.FuncMap{
		"toCamelCase":             vdlutil.ToCamelCase,
		"numOutArgs":              numOutArgs,
		"genMethodTags":           genMethodTags,
		"generateTypeStub":        generateTypeStub,
		"generateConstDefinition": generateConstDefinition,
		"importPath":              importPath,
	}
	javascriptTemplate = template.Must(template.New("genJS").Funcs(funcMap).Parse(genJS))
}

// The template that we execute against a compile.Package instance to generate our
// code.  Most of this is fairly straightforward substitution and ranges; more
// complicated logic is delegated to the helper functions above.
//
// We try to generate code that has somewhat reasonable formatting.
const genJS = `{{with $data := .}}// This file was auto-generated by the veyron vdl tool.
{{$pkg := $data.Pkg}}
{{if $data.UserImports}}{{range $imp := $data.UserImports}}
var {{$imp.Local}} = require('{{importPath $data $imp.Path}}');{{end}}{{end}}
var services = {
package: '{{$pkg.Path}}',
{{range $file := $pkg.Files}}{{range $iface := $file.Interfaces}}  {{$iface.Name}}: {
{{range $method := $iface.AllMethods}}    {{toCamelCase $method.Name}}: {
    numInArgs: {{len $method.InArgs}},
    numOutArgs: {{numOutArgs $method}},
    inputStreaming: {{if $method.InStream}}true{{else}}false{{end}},
    outputStreaming: {{if $method.OutStream}}true{{else}}false{{end}},
    tags: {{genMethodTags $data $method}}
},
{{end}}
},
{{end}}{{end}}
};

var vom = require('vom');
var Types = vom.Types;
var Kind = vom.Kind;
var Complex = vom.Complex;

var types = {};
{{range $file := $pkg.Files}}{{range $type := $file.TypeDefs}}
{{generateTypeStub $data $type}}
{{end}}{{end}}

var consts = { {{range $file := $pkg.Files}}{{range $const := $file.ConstDefs}}
{{generateConstDefinition $data $const}}{{end}}{{end}}
};
module.exports = {
  types: types,
  services: services,
  consts: consts,
};
{{end}}`
<commit_msg>vdl: Fix a bug in the generated constructors where falsy values for an any field got turned into null.<commit_after>// Package javascript implements Javascript code generation from compiled VDL packages.
package javascript

// Generates the javascript source code for vdl files.  The generated output in javascript
// differs from most other languages, since we don't generate stubs. Instead generate an
// object that contains the parsed VDL structures that will be used by the Javascript code
// to valid servers.

import (
	"bytes"
	"fmt"
	"path/filepath"
	"strconv"
	"text/template"

	"veyron.io/veyron/veyron2/vdl"
	"veyron.io/veyron/veyron2/vdl/codegen"
	"veyron.io/veyron/veyron2/vdl/compile"
	"veyron.io/veyron/veyron2/vdl/vdlutil"
)

type data struct {
	Pkg            *compile.Package
	Env            *compile.Env
	GenerateImport func(string) string
	UserImports    codegen.Imports
}

// Generate takes a populated compile.Package and produces a byte slice
// containing the generated Javascript code.
func Generate(pkg *compile.Package, env *compile.Env, genImport func(string) string) []byte {
	data := data{
		Pkg:            pkg,
		Env:            env,
		GenerateImport: genImport,
		UserImports:    codegen.ImportsForFiles(pkg.Files...),
	}
	var buf bytes.Buffer
	if err := javascriptTemplate.Execute(&buf, data); err != nil {
		// We shouldn't see an error; it means our template is buggy.
		panic(fmt.Errorf("vdl: couldn't execute template: %v", err))
	}
	return buf.Bytes()
}

var javascriptTemplate *template.Template

func numOutArgs(method *compile.Method) int {
	return len(method.OutArgs) - 1
}

func bitlen(kind vdl.Kind) int {
	switch kind {
	case vdl.Float32, vdl.Complex64:
		return 32
	case vdl.Float64, vdl.Complex128:
		return 64
	}
	panic(fmt.Errorf("vdl: bitLen unhandled kind %v", kind))
}

func genMethodTags(data data, method *compile.Method) string {
	tags := method.Tags
	result := "["
	for _, tag := range tags {
		result += typedConst(data, tag) + ", "
	}
	result += "]"
	return result
}

func unTypedConst(data data, v *vdl.Value) string {
	switch v.Kind() {
	case vdl.Bool:
		if v.Bool() {
			return "true"
		} else {
			return "false"
		}
	case vdl.Byte:
		return strconv.FormatUint(uint64(v.Byte()), 10)
	case vdl.Uint16, vdl.Uint32, vdl.Uint64:
		return strconv.FormatUint(v.Uint(), 10)
	case vdl.Int16, vdl.Int32, vdl.Int64:
		return strconv.FormatInt(v.Int(), 10)
	case vdl.Float32, vdl.Float64:
		return strconv.FormatFloat(v.Float(), 'g', -1, bitlen(v.Kind()))
	case vdl.String:
		return strconv.Quote(v.RawString())
	case vdl.OneOf, vdl.Any, vdl.Nilable:
		if elem := v.Elem(); elem != nil {
			return typedConst(data, elem)
		}
		return "null"
	case vdl.Complex64, vdl.Complex128:
		return fmt.Sprintf("new Complex(%f, %f)", real(v.Complex()), imag(v.Complex()))
	case vdl.Enum:
		return strconv.FormatInt(int64(v.EnumIndex()), 10)
	case vdl.Array, vdl.List:
		s := "["
		for ix := 0; ix < v.Len(); ix++ {
			s += "\n" + typedConst(data, v.Index(ix)) + ","
		}
		return s + "\n]"
	case vdl.Set, vdl.Map:
		s := "{"
		for _, key := range v.Keys() {
			s += "\n  " + unTypedConst(data, key) + ":"
			if v.Kind() == vdl.Set {
				s += "true"
			} else {
				s += typedConst(data, v.MapIndex(key))
			}
			s += ","
			// TODO(bjornick): Deal with non-string keys.
		}
		s += "}"
		return s
	case vdl.Struct:
		s := "{"
		t := v.Type()
		for ix := 0; ix < t.NumField(); ix++ {
			s += "\n  '" + t.Field(ix).Name + "': " + typedConst(data, v.Field(ix)) + ","
		}
		return s + "\n}"

	}

	panic(fmt.Errorf("vdl: unTypedConst unhandled type %v %v", v.Kind(), v.Type()))
}

func primitiveWithOptionalName(primitive, name string) string {
	if name == "" {
		return "Types." + primitive
	}
	return "{kind: Kind." + primitive + ", name: '" + name + "'}"
}

func typeStruct(t *vdl.Type) string {
	nameField := ""
	if t.Name() != "" {
		nameField = "\n    name: '" + t.Name() + "',"
	}

	switch t.Kind() {
	case vdl.Any:
		return primitiveWithOptionalName("ANY", t.Name())
	case vdl.Bool:
		return primitiveWithOptionalName("BOOL", t.Name())
	case vdl.Byte:
		return primitiveWithOptionalName("BYTE", t.Name())
	case vdl.Uint16:
		return primitiveWithOptionalName("UINT16", t.Name())
	case vdl.Uint32:
		return primitiveWithOptionalName("UINT32", t.Name())
	case vdl.Uint64:
		return primitiveWithOptionalName("UINT64", t.Name())
	case vdl.Int16:
		return primitiveWithOptionalName("INT16", t.Name())
	case vdl.Int32:
		return primitiveWithOptionalName("INT32", t.Name())
	case vdl.Int64:
		return primitiveWithOptionalName("INT64", t.Name())
	case vdl.Float32:
		return primitiveWithOptionalName("FLOAT32", t.Name())
	case vdl.Float64:
		return primitiveWithOptionalName("FLOAT64", t.Name())
	case vdl.Complex64:
		return primitiveWithOptionalName("COMPLEX64", t.Name())
	case vdl.Complex128:
		return primitiveWithOptionalName("COMPLEX128", t.Name())
	case vdl.String:
		return primitiveWithOptionalName("STRING", t.Name())
	case vdl.Enum:
		labels := ""
		for i := 0; i < t.NumEnumLabel(); i++ {
			labels += "'" + t.EnumLabel(i) + "', "
		}
		return fmt.Sprintf(`{
    kind: Kind.ENUM,
    name: '%s',
    labels: [%s]
  }`, t.Name(), labels)
	case vdl.TypeVal:
		return "{}"

	// TODO(bjornick): Handle recursive types and de-duping of the same struct definitions over
	// and over again.
	case vdl.Array:
		return fmt.Sprintf(`{
    kind: Kind.ARRAY,%s
    elem: %s,
    len: %d
  }`, nameField, typeStruct(t.Elem()), t.Len())
	case vdl.List:
		return `{
    kind: Kind.LIST,` + nameField + `
    elem: ` + typeStruct(t.Elem()) + `
  }`
	case vdl.Set:
		return `{
    kind: Kind.SET,` + nameField + `
    key: ` + typeStruct(t.Key()) + `
  }`
	case vdl.Map:
		return fmt.Sprintf(`{
    kind: Kind.MAP,%s
    key: %s,
    elem: %s
  }`, nameField, typeStruct(t.Key()), typeStruct(t.Elem()))
	case vdl.Struct:
		fields := ""
		for i := 0; i < t.NumField(); i++ {
			f := t.Field(i)
			fields += fmt.Sprintf(`
    {
      name: '%s',
      type: %s
    },`, f.Name, typeStruct(f.Type))
		}
		return fmt.Sprintf(`{
    kind: Kind.STRUCT,
    name: '%s',
    fields: [%s
  ]}`, t.Name(), fields)
	}
	return ""
}

func qualifiedIdent(data data, name string, file *compile.File) string {
	if data.Pkg.Path == file.Package.Path {
		return name
	}
	return data.UserImports.LookupLocal(file.Package.Path) + "." + name
}

func defaultValue(data data, t *vdl.Type) string {
	if def := data.Env.FindTypeDef(t); def != nil {
		switch {
		case t == vdl.AnyType || t == vdl.TypeValType || t == compile.ErrorType:
			return "null"
		case def.File != compile.BuiltInFile:
			return "new " + qualifiedIdent(data, def.Name, def.File) + "()"
		}
	}
	switch t.Kind() {
	case vdl.Nilable:
		return "null"
	case vdl.Array, vdl.List:
		return "[]"
	case vdl.Set, vdl.Map:
		return "{}"
	case vdl.Byte, vdl.Uint16, vdl.Uint32, vdl.Uint64, vdl.Int16, vdl.Int32, vdl.Int64, vdl.Float32, vdl.Float64:
		return "0"
	case vdl.Complex64, vdl.Complex128:
		return "new Complex(0, 0)"
	case vdl.String:
		return "''"
	case vdl.Bool:
		return "false"
	}
	panic(fmt.Sprintf("Unhandled type %v", t))
}

func generateTypeStub(data data, t *compile.TypeDef) string {
	name := t.Name
	wrappedType := true
	kind := t.Type.Kind()
	// We can't always have Map be a wrapped type, since the key doesn't
	// have to be a string.
	// TODO(bjornick): Handle maps without string keys.
	switch kind {
	case vdl.Struct, vdl.Any, vdl.Enum:
		wrappedType = false
	}

	result := "function " + name + "(val) {"

	if wrappedType {
		result += "\n    this.val = val;\n"
	} else if kind == vdl.Struct {
		result += "\n    val = val || {};"
		for i := 0; i < t.Type.NumField(); i++ {
			field := t.Type.Field(i)
			result += fmt.Sprintf(`
    if (val.hasOwnProperty('%s')) {
      this.%s = val.%s;
    } else {
      this.%s = %s;
    }`, field.Name, field.Name, field.Name, field.Name, defaultValue(data, field.Type))
		}
		result += "\n"
	} else if kind == vdl.Enum {
		// The default value of an enum type is the first label which is mapped
		// to 0.
		result += "\n    this.val = 0;\n"
	}
	result += "  }\n"
	result += name + ".prototype._type = " + typeStruct(t.Type) + ";\n"

	result += "  types." + name + " = " + name + ";\n"

	return result
}

func typedConst(data data, v *vdl.Value) string {
	valstr := unTypedConst(data, v)
	t := v.Type()
	if def := data.Env.FindTypeDef(t); def != nil {
		if def.File != compile.BuiltInFile {
			return fmt.Sprintf("new %s(%s)", qualifiedIdent(data, def.Name, def.File), valstr)
		}
	}
	return valstr
}

func generateConstDefinition(data data, c *compile.ConstDef) string {
	return fmt.Sprintf("  %s: %s,", c.Name, typedConst(data, c.Value))
}

func importPath(data data, path string) string {
	// We need to prefix all of these paths with a ./ to tell node that the path is relative to
	// the current directory.  Sadly filepath.Join(".", foo) == foo, so we have to do it
	// explicitly.
	return "." + string(filepath.Separator) + data.GenerateImport(path)

}

func init() {
	funcMap := template.FuncMap{
		"toCamelCase":             vdlutil.ToCamelCase,
		"numOutArgs":              numOutArgs,
		"genMethodTags":           genMethodTags,
		"generateTypeStub":        generateTypeStub,
		"generateConstDefinition": generateConstDefinition,
		"importPath":              importPath,
	}
	javascriptTemplate = template.Must(template.New("genJS").Funcs(funcMap).Parse(genJS))
}

// The template that we execute against a compile.Package instance to generate our
// code.  Most of this is fairly straightforward substitution and ranges; more
// complicated logic is delegated to the helper functions above.
//
// We try to generate code that has somewhat reasonable formatting.
const genJS = `{{with $data := .}}// This file was auto-generated by the veyron vdl tool.
{{$pkg := $data.Pkg}}
{{if $data.UserImports}}{{range $imp := $data.UserImports}}
var {{$imp.Local}} = require('{{importPath $data $imp.Path}}');{{end}}{{end}}
var services = {
package: '{{$pkg.Path}}',
{{range $file := $pkg.Files}}{{range $iface := $file.Interfaces}}  {{$iface.Name}}: {
{{range $method := $iface.AllMethods}}    {{toCamelCase $method.Name}}: {
    numInArgs: {{len $method.InArgs}},
    numOutArgs: {{numOutArgs $method}},
    inputStreaming: {{if $method.InStream}}true{{else}}false{{end}},
    outputStreaming: {{if $method.OutStream}}true{{else}}false{{end}},
    tags: {{genMethodTags $data $method}}
},
{{end}}
},
{{end}}{{end}}
};

var vom = require('vom');
var Types = vom.Types;
var Kind = vom.Kind;
var Complex = vom.Complex;

var types = {};
{{range $file := $pkg.Files}}{{range $type := $file.TypeDefs}}
{{generateTypeStub $data $type}}
{{end}}{{end}}

var consts = { {{range $file := $pkg.Files}}{{range $const := $file.ConstDefs}}
{{generateConstDefinition $data $const}}{{end}}{{end}}
};
module.exports = {
  types: types,
  services: services,
  consts: consts,
};
{{end}}`
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"fmt"
	"log"
	"sort"
	"time"

	"github.com/UniversityRadioYork/2016-site/utils"
)

// weekFromVars extracts the year, and week strings from vars.
func weekFromVars(vars map[string]string) (string, string, error) {
	y, ok := vars["year"]
	if !ok {
		return "", "", errors.New("no year provided")
	}
	w, ok := vars["week"]
	if !ok {
		return "", "", errors.New("no week provided")
	}

	return y, w, nil
}

//
// Week schedule algorithm
// TODO(CaptainHayashi): move?
//

// WeekScheduleCell represents one cell in the week schedule.
type WeekScheduleCell struct {
	// Number of rows this cell spans.
	// If 0, this is a continuation from a cell further up.
	RowSpan uint

	// Pointer to the timeslot in this cell, if any.
	// Will be nil if 'RowSpan' is 0.
	Item *ScheduleItem

	// Hour stores which hour (row) the cell is in
	Hour int

	// Minute stores the minute for this row
	Minute int
}

// WeekScheduleRow represents one row in the week schedule.
type WeekScheduleRow struct {
	// The hour of the row (0..23).
	Hour int
	// The minute of the show (0..59).
	Minute int
	// The cells inside this row.
	Cells []WeekScheduleCell
}

// addCell adds a cell with rowspan s and item i to the row r.
func (r *WeekScheduleRow) addCell(s uint, i *ScheduleItem) {
	r.Cells = append(r.Cells, WeekScheduleCell{RowSpan: s, Item: i})
}

// straddlesDay checks whether a show's start and finish cross over the boundary of a URY day.
func straddlesDay(s *ScheduleItem) bool {
	dayBoundary := utils.StartHour
	adjustedStartDay := s.Start.Add(time.Hour * time.Duration(-dayBoundary))
	adjustedEndDay := s.Finish.Add(time.Hour * time.Duration(-dayBoundary))
	straddle := adjustedEndDay.Day() != adjustedStartDay.Day()
	if straddle {
		fmt.Println(s.Name)
	}
	return straddle
}

// calcScheduleBoundaries gets the offsets of the earliest and latest visible schedule hours.
// It returns these as top and bot respectively.
func calcScheduleBoundaries(items []*ScheduleItem, scheduleStart time.Time) (top, bot utils.StartOffset, err error) {
	if len(items) == 0 {
		err = errors.New("calculateScheduleBoundaries: no schedule")
		return
	}

	// These are the boundaries for culling, and are expanded upwards when we find shows that start earlier or finish later than the last-set boundary.
	// Initially they are set to one past their worst case to make the updating logic easier.
	// Since we assert we have a schedule, these values _will_ change.
	top = utils.StartOffset(23)
	bot = utils.StartOffset(0)

	for _, s := range items {
		// Any show that isn't a sustainer affects the culling boundaries.
		if s.IsSustainer() {
			continue
		}

		if straddlesDay(s) {
			if scheduleStart.After(s.Start) {
				//This is the first item on the schedule and straddles the week, so we only set the top of the schedule
				//top = utils.StartOffset(0)
				//Temporarily disabled as this slot doesn't show up on the schedule
			} else if s.Finish.After(scheduleStart.AddDate(0, 0, 7)) {
				//This is the last item on the schedule and straddles the week, so we only set the bottom of the schedule
				bot = utils.StartOffset(23)
			} else {
				// An item that straddles the day crosses over from the end of a day to the start of the day.
				// This means that we saturate the culling boundaries.
				// As an optimisation we don't need to consider any other show.
				return utils.StartOffset(0), utils.StartOffset(23), nil
			}
		}

		// Otherwise, if its start/finish as offsets from start time are outside the current boundaries, update them.
		var ctop utils.StartOffset
		if ctop, err = utils.HourToStartOffset(s.Start.Hour()); err != nil {
			return
		}
		if ctop < top {
			top = ctop
		}

		var cbot utils.StartOffset
		if cbot, err = utils.HourToStartOffset(s.Finish.Hour()); err != nil {
			return
		}
		// cbot is the offset of the hour in which the item finishes.
		// This is _one past_ the last row the item occupies if the item ends cleanly at :00:00.
		if s.Finish.Minute() == 0 && s.Finish.Second() == 0 && s.Finish.Nanosecond() == 0 {
			cbot--
		}

		if bot < cbot {
			bot = cbot
		}
	}

	return
}

// rowDecision is an internal type recording information about which rows to display in the week schedule.
// It records, for one hour, the minute rows (00, 30, etc) that are switched 'on' for that row.
type rowDecision map[int]struct{}

// visible checks if the hour represented by row decision r is to be shown on the schedule.
func (r rowDecision) visible() bool {
	// Each visible row has its on-the-hour row set.
	_, visible := r[0]
	return visible
}

// mark adds a mark for the given minute to row decision r.
func (r rowDecision) mark(minute int) {
	r[minute] = struct{}{}
}

// toRow converts row decision r to a slice of schedule rows for the given hour.
func (r rowDecision) toRows(hour int) []WeekScheduleRow {
	minutes := make([]int, len(r))
	j := 0
	for k := range r {
		minutes[j] = k
		j++
	}
	sort.Ints(minutes)

	rows := make([]WeekScheduleRow, len(minutes))
	for j, m := range minutes {
		rows[j] = WeekScheduleRow{Hour: hour, Minute: m, Cells: []WeekScheduleCell{}}
	}
	return rows
}

// initRowDecisions creates 24 rowDecisions, from schedule start to schedule end.
// Each is marked as visble or invisible depending on the offsets top and bot.
func initRowDecisions(top, bot utils.StartOffset) ([]rowDecision, error) {
	// Make sure the offsets are valid.
	if !top.Valid() || !bot.Valid() {
		return nil, fmt.Errorf("initRowDecisions: row boundaries %d to %d are invalid", int(top), int(bot))
	}

	rows := make([]rowDecision, 24)

	// Go through each hour, culling ones before the boundaries, and adding on-the-hour minute marks to the others.
	// Boundaries are inclusive, so cull only things outside of them.
	for i := utils.StartOffset(0); i < utils.StartOffset(24); i++ {
		h, err := i.ToHour()
		if err != nil {
			return nil, err
		}

		rows[h] = rowDecision{}
		if top <= i && i <= bot {
			// This has the effect of making the row visible.
			rows[h].mark(0)
		}
	}

	return rows, nil
}

// addItemsToRowDecisions populates the row decision list rows with minute marks from schedule items not starting on the hour.
func addItemsToRowDecisions(rows []rowDecision, items []*ScheduleItem) {
	for _, item := range items {
		h := item.Start.Hour()
		if rows[h].visible() {
			rows[h].mark(item.Start.Minute())
		}
	}
}

// rowDecisionsToRows generates rows based on the per-hourly row decisions in rdecs.
func rowDecisionsToRows(rdecs []rowDecision) ([]WeekScheduleRow, error) {
	rows := []WeekScheduleRow{}

	for i := utils.StartOffset(0); i < utils.StartOffset(24); i++ {
		h, err := i.ToHour()
		if err != nil {
			return nil, err
		}

		if rdecs[h].visible() {
			rows = append(rows, rdecs[h].toRows(h)...)
		}
	}

	return rows, nil
}

// initScheduleRows takes a schedule and determines which rows should be displayed.
func initScheduleRows(items []*ScheduleItem, startTime time.Time) ([]WeekScheduleRow, error) {
	top, bot, err := calcScheduleBoundaries(items, startTime)
	if err != nil {
		return nil, err
	}

	rdecs, err := initRowDecisions(top, bot)
	if err != nil {
		return nil, err
	}
	addItemsToRowDecisions(rdecs, items)

	return rowDecisionsToRows(rdecs)
}

// populateRows fills schedule rows with timeslots.
// It takes the list of schedule start times on the days the schedule spans,
// the slice of rows to populate, and the schedule items to add.
func populateRows(days []time.Time, rows []WeekScheduleRow, items []*ScheduleItem) {
	currentItem := 0

	for d, day := range days {
		// We use this to find out when we've gone over midnight
		lastHour := -1
		// And this to find out where the current show started
		thisShowIndex := -1

		// Now, go through all the rows for this day.
		// We have to be careful to make sure we tick over day if we go past midnight.
		for i := range rows {
			if rows[i].Hour < lastHour {
				day = day.AddDate(0, 0, 1)
			}
			lastHour = rows[i].Hour

			rowTime := time.Date(day.Year(), day.Month(), day.Day(), rows[i].Hour, rows[i].Minute, 0, 0, time.Local)

			// Seek forwards if the current show has finished.
			for !items[currentItem].Finish.After(rowTime) {
				currentItem++
				thisShowIndex = -1
			}

			// If this is not the first time we've seen this slot,
			// update the rowspan in the first instance's cell and
			// put in a placeholder.
			if thisShowIndex != -1 {
				rows[thisShowIndex].Cells[d].RowSpan++
				rows[i].addCell(0, nil)
			} else {
				thisShowIndex = i
				rows[i].addCell(1, items[currentItem])
			}
		}
	}
}

// WeekSchedule is the type of week schedules.
type WeekSchedule struct {
	// Dates enumerates the dates this week schedule covers.
	Dates []time.Time
	// Table is the actual week table.
	// If there is no schedule for the given week, this will be nil.
	Table []WeekScheduleCol
}

// hasShows asks whether a schedule slice contains any non-sustainer shows.
// It assumes the slice has been filled with sustainer.
func hasShows(schedule []*ScheduleItem) bool {
	// This shouldn't happen, but if it does, this is the right thing to do.
	if len(schedule) == 0 {
		return false
	}

	// We know that, if a slice is filled but has no non-sustainer, then
	// the slice will contain only one sustainer item.  So, eliminate the
	// other cases.
	if 1 < len(schedule) || !schedule[0].IsSustainer() {
		return true
	}

	return false
}

// Flippin that table

// WeekScheduleCol represents one day in the week schedule.
type WeekScheduleCol struct {
	// The day of the show.
	Day time.Time
	// The cells inside this row.
	Cells []WeekScheduleCell
}

// addCell adds a cell with rowspan s and item i to the column c.
func (c *WeekScheduleCol) addCell(s uint, i *ScheduleItem, h int, m int) {
	c.Cells = append(c.Cells, WeekScheduleCell{RowSpan: s, Item: i, Hour: h, Minute: m})
}

// tableFilp flips the schedule table such that it becomes a list of days which have a list
// of shows on that day.
func tableFilp(rows []WeekScheduleRow, dates []time.Time) []WeekScheduleCol {
	days := make([]WeekScheduleCol, 7)
	for i := range days {
		days[i].Day = dates[i]
	}
	for _, row := range rows {
		for i, cell := range row.Cells {
			days[i].addCell(cell.RowSpan, cell.Item, row.Hour, row.Minute)
		}
	}
	return days
}

// tabulateWeekSchedule creates a schedule table from the given schedule slice.
func tabulateWeekSchedule(start, finish time.Time, schedule []*ScheduleItem) (*WeekSchedule, error) {
	days := []time.Time{}
	for d := start; d.Before(finish); d = d.AddDate(0, 0, 1) {
		days = append(days, d)
	}

	if !hasShows(schedule) {
		return &WeekSchedule{
			Dates: days,
			Table: nil,
		}, nil
	}

	rows, err := initScheduleRows(schedule, start)
	if err != nil {
		log.Println(err)
		return nil, err
	}

	populateRows(days, rows, schedule)

	table := tableFilp(rows, days)

	sch := WeekSchedule{
		Dates: days,
		Table: table,
	}

	return &sch, nil
}
<commit_msg>remove print statement<commit_after>package models

import (
	"errors"
	"fmt"
	"log"
	"sort"
	"time"

	"github.com/UniversityRadioYork/2016-site/utils"
)

// weekFromVars extracts the year, and week strings from vars.
func weekFromVars(vars map[string]string) (string, string, error) {
	y, ok := vars["year"]
	if !ok {
		return "", "", errors.New("no year provided")
	}
	w, ok := vars["week"]
	if !ok {
		return "", "", errors.New("no week provided")
	}

	return y, w, nil
}

//
// Week schedule algorithm
// TODO(CaptainHayashi): move?
//

// WeekScheduleCell represents one cell in the week schedule.
type WeekScheduleCell struct {
	// Number of rows this cell spans.
	// If 0, this is a continuation from a cell further up.
	RowSpan uint

	// Pointer to the timeslot in this cell, if any.
	// Will be nil if 'RowSpan' is 0.
	Item *ScheduleItem

	// Hour stores which hour (row) the cell is in
	Hour int

	// Minute stores the minute for this row
	Minute int
}

// WeekScheduleRow represents one row in the week schedule.
type WeekScheduleRow struct {
	// The hour of the row (0..23).
	Hour int
	// The minute of the show (0..59).
	Minute int
	// The cells inside this row.
	Cells []WeekScheduleCell
}

// addCell adds a cell with rowspan s and item i to the row r.
func (r *WeekScheduleRow) addCell(s uint, i *ScheduleItem) {
	r.Cells = append(r.Cells, WeekScheduleCell{RowSpan: s, Item: i})
}

// straddlesDay checks whether a show's start and finish cross over the boundary of a URY day.
func straddlesDay(s *ScheduleItem) bool {
	dayBoundary := utils.StartHour
	adjustedStartDay := s.Start.Add(time.Hour * time.Duration(-dayBoundary))
	adjustedEndDay := s.Finish.Add(time.Hour * time.Duration(-dayBoundary))
	straddle := adjustedEndDay.Day() != adjustedStartDay.Day()
	return straddle
}

// calcScheduleBoundaries gets the offsets of the earliest and latest visible schedule hours.
// It returns these as top and bot respectively.
func calcScheduleBoundaries(items []*ScheduleItem, scheduleStart time.Time) (top, bot utils.StartOffset, err error) {
	if len(items) == 0 {
		err = errors.New("calculateScheduleBoundaries: no schedule")
		return
	}

	// These are the boundaries for culling, and are expanded upwards when we find shows that start earlier or finish later than the last-set boundary.
	// Initially they are set to one past their worst case to make the updating logic easier.
	// Since we assert we have a schedule, these values _will_ change.
	top = utils.StartOffset(23)
	bot = utils.StartOffset(0)

	for _, s := range items {
		// Any show that isn't a sustainer affects the culling boundaries.
		if s.IsSustainer() {
			continue
		}

		if straddlesDay(s) {
			if scheduleStart.After(s.Start) {
				//This is the first item on the schedule and straddles the week, so we only set the top of the schedule
				//top = utils.StartOffset(0)
				//Temporarily disabled as this slot doesn't show up on the schedule
			} else if s.Finish.After(scheduleStart.AddDate(0, 0, 7)) {
				//This is the last item on the schedule and straddles the week, so we only set the bottom of the schedule
				bot = utils.StartOffset(23)
			} else {
				// An item that straddles the day crosses over from the end of a day to the start of the day.
				// This means that we saturate the culling boundaries.
				// As an optimisation we don't need to consider any other show.
				return utils.StartOffset(0), utils.StartOffset(23), nil
			}
		}

		// Otherwise, if its start/finish as offsets from start time are outside the current boundaries, update them.
		var ctop utils.StartOffset
		if ctop, err = utils.HourToStartOffset(s.Start.Hour()); err != nil {
			return
		}
		if ctop < top {
			top = ctop
		}

		var cbot utils.StartOffset
		if cbot, err = utils.HourToStartOffset(s.Finish.Hour()); err != nil {
			return
		}
		// cbot is the offset of the hour in which the item finishes.
		// This is _one past_ the last row the item occupies if the item ends cleanly at :00:00.
		if s.Finish.Minute() == 0 && s.Finish.Second() == 0 && s.Finish.Nanosecond() == 0 {
			cbot--
		}

		if bot < cbot {
			bot = cbot
		}
	}

	return
}

// rowDecision is an internal type recording information about which rows to display in the week schedule.
// It records, for one hour, the minute rows (00, 30, etc) that are switched 'on' for that row.
type rowDecision map[int]struct{}

// visible checks if the hour represented by row decision r is to be shown on the schedule.
func (r rowDecision) visible() bool {
	// Each visible row has its on-the-hour row set.
	_, visible := r[0]
	return visible
}

// mark adds a mark for the given minute to row decision r.
func (r rowDecision) mark(minute int) {
	r[minute] = struct{}{}
}

// toRow converts row decision r to a slice of schedule rows for the given hour.
func (r rowDecision) toRows(hour int) []WeekScheduleRow {
	minutes := make([]int, len(r))
	j := 0
	for k := range r {
		minutes[j] = k
		j++
	}
	sort.Ints(minutes)

	rows := make([]WeekScheduleRow, len(minutes))
	for j, m := range minutes {
		rows[j] = WeekScheduleRow{Hour: hour, Minute: m, Cells: []WeekScheduleCell{}}
	}
	return rows
}

// initRowDecisions creates 24 rowDecisions, from schedule start to schedule end.
// Each is marked as visble or invisible depending on the offsets top and bot.
func initRowDecisions(top, bot utils.StartOffset) ([]rowDecision, error) {
	// Make sure the offsets are valid.
	if !top.Valid() || !bot.Valid() {
		return nil, fmt.Errorf("initRowDecisions: row boundaries %d to %d are invalid", int(top), int(bot))
	}

	rows := make([]rowDecision, 24)

	// Go through each hour, culling ones before the boundaries, and adding on-the-hour minute marks to the others.
	// Boundaries are inclusive, so cull only things outside of them.
	for i := utils.StartOffset(0); i < utils.StartOffset(24); i++ {
		h, err := i.ToHour()
		if err != nil {
			return nil, err
		}

		rows[h] = rowDecision{}
		if top <= i && i <= bot {
			// This has the effect of making the row visible.
			rows[h].mark(0)
		}
	}

	return rows, nil
}

// addItemsToRowDecisions populates the row decision list rows with minute marks from schedule items not starting on the hour.
func addItemsToRowDecisions(rows []rowDecision, items []*ScheduleItem) {
	for _, item := range items {
		h := item.Start.Hour()
		if rows[h].visible() {
			rows[h].mark(item.Start.Minute())
		}
	}
}

// rowDecisionsToRows generates rows based on the per-hourly row decisions in rdecs.
func rowDecisionsToRows(rdecs []rowDecision) ([]WeekScheduleRow, error) {
	rows := []WeekScheduleRow{}

	for i := utils.StartOffset(0); i < utils.StartOffset(24); i++ {
		h, err := i.ToHour()
		if err != nil {
			return nil, err
		}

		if rdecs[h].visible() {
			rows = append(rows, rdecs[h].toRows(h)...)
		}
	}

	return rows, nil
}

// initScheduleRows takes a schedule and determines which rows should be displayed.
func initScheduleRows(items []*ScheduleItem, startTime time.Time) ([]WeekScheduleRow, error) {
	top, bot, err := calcScheduleBoundaries(items, startTime)
	if err != nil {
		return nil, err
	}

	rdecs, err := initRowDecisions(top, bot)
	if err != nil {
		return nil, err
	}
	addItemsToRowDecisions(rdecs, items)

	return rowDecisionsToRows(rdecs)
}

// populateRows fills schedule rows with timeslots.
// It takes the list of schedule start times on the days the schedule spans,
// the slice of rows to populate, and the schedule items to add.
func populateRows(days []time.Time, rows []WeekScheduleRow, items []*ScheduleItem) {
	currentItem := 0

	for d, day := range days {
		// We use this to find out when we've gone over midnight
		lastHour := -1
		// And this to find out where the current show started
		thisShowIndex := -1

		// Now, go through all the rows for this day.
		// We have to be careful to make sure we tick over day if we go past midnight.
		for i := range rows {
			if rows[i].Hour < lastHour {
				day = day.AddDate(0, 0, 1)
			}
			lastHour = rows[i].Hour

			rowTime := time.Date(day.Year(), day.Month(), day.Day(), rows[i].Hour, rows[i].Minute, 0, 0, time.Local)

			// Seek forwards if the current show has finished.
			for !items[currentItem].Finish.After(rowTime) {
				currentItem++
				thisShowIndex = -1
			}

			// If this is not the first time we've seen this slot,
			// update the rowspan in the first instance's cell and
			// put in a placeholder.
			if thisShowIndex != -1 {
				rows[thisShowIndex].Cells[d].RowSpan++
				rows[i].addCell(0, nil)
			} else {
				thisShowIndex = i
				rows[i].addCell(1, items[currentItem])
			}
		}
	}
}

// WeekSchedule is the type of week schedules.
type WeekSchedule struct {
	// Dates enumerates the dates this week schedule covers.
	Dates []time.Time
	// Table is the actual week table.
	// If there is no schedule for the given week, this will be nil.
	Table []WeekScheduleCol
}

// hasShows asks whether a schedule slice contains any non-sustainer shows.
// It assumes the slice has been filled with sustainer.
func hasShows(schedule []*ScheduleItem) bool {
	// This shouldn't happen, but if it does, this is the right thing to do.
	if len(schedule) == 0 {
		return false
	}

	// We know that, if a slice is filled but has no non-sustainer, then
	// the slice will contain only one sustainer item.  So, eliminate the
	// other cases.
	if 1 < len(schedule) || !schedule[0].IsSustainer() {
		return true
	}

	return false
}

// Flippin that table

// WeekScheduleCol represents one day in the week schedule.
type WeekScheduleCol struct {
	// The day of the show.
	Day time.Time
	// The cells inside this row.
	Cells []WeekScheduleCell
}

// addCell adds a cell with rowspan s and item i to the column c.
func (c *WeekScheduleCol) addCell(s uint, i *ScheduleItem, h int, m int) {
	c.Cells = append(c.Cells, WeekScheduleCell{RowSpan: s, Item: i, Hour: h, Minute: m})
}

// tableFilp flips the schedule table such that it becomes a list of days which have a list
// of shows on that day.
func tableFilp(rows []WeekScheduleRow, dates []time.Time) []WeekScheduleCol {
	days := make([]WeekScheduleCol, 7)
	for i := range days {
		days[i].Day = dates[i]
	}
	for _, row := range rows {
		for i, cell := range row.Cells {
			days[i].addCell(cell.RowSpan, cell.Item, row.Hour, row.Minute)
		}
	}
	return days
}

// tabulateWeekSchedule creates a schedule table from the given schedule slice.
func tabulateWeekSchedule(start, finish time.Time, schedule []*ScheduleItem) (*WeekSchedule, error) {
	days := []time.Time{}
	for d := start; d.Before(finish); d = d.AddDate(0, 0, 1) {
		days = append(days, d)
	}

	if !hasShows(schedule) {
		return &WeekSchedule{
			Dates: days,
			Table: nil,
		}, nil
	}

	rows, err := initScheduleRows(schedule, start)
	if err != nil {
		log.Println(err)
		return nil, err
	}

	populateRows(days, rows, schedule)

	table := tableFilp(rows, days)

	sch := WeekSchedule{
		Dates: days,
		Table: table,
	}

	return &sch, nil
}
<|endoftext|>


<commit_before>package overlay

import (
	"fmt"
	"net"
	"sync"
	"syscall"

	"github.com/Sirupsen/logrus"
)

const ovPeerTable = "overlay_peer_table"

type peerKey struct {
	peerIP  net.IP
	peerMac net.HardwareAddr
}

type peerEntry struct {
	eid        string
	vtep       net.IP
	peerIPMask net.IPMask
	inSandbox  bool
	isLocal    bool
}

type peerMap struct {
	mp map[string]peerEntry
	sync.Mutex
}

type peerNetworkMap struct {
	mp map[string]*peerMap
	sync.Mutex
}

func (pKey peerKey) String() string {
	return fmt.Sprintf("%s %s", pKey.peerIP, pKey.peerMac)
}

func (pKey *peerKey) Scan(state fmt.ScanState, verb rune) error {
	ipB, err := state.Token(true, nil)
	if err != nil {
		return err
	}

	pKey.peerIP = net.ParseIP(string(ipB))

	macB, err := state.Token(true, nil)
	if err != nil {
		return err
	}

	pKey.peerMac, err = net.ParseMAC(string(macB))
	if err != nil {
		return err
	}

	return nil
}

var peerDbWg sync.WaitGroup

func (d *driver) peerDbWalk(f func(string, *peerKey, *peerEntry) bool) error {
	d.peerDb.Lock()
	nids := []string{}
	for nid := range d.peerDb.mp {
		nids = append(nids, nid)
	}
	d.peerDb.Unlock()

	for _, nid := range nids {
		d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
			return f(nid, pKey, pEntry)
		})
	}
	return nil
}

func (d *driver) peerDbNetworkWalk(nid string, f func(*peerKey, *peerEntry) bool) error {
	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	d.peerDb.Unlock()

	if !ok {
		return nil
	}

	mp := map[string]peerEntry{}

	pMap.Lock()
	for pKeyStr, pEntry := range pMap.mp {
		mp[pKeyStr] = pEntry
	}
	pMap.Unlock()

	for pKeyStr, pEntry := range mp {
		var pKey peerKey
		if _, err := fmt.Sscan(pKeyStr, &pKey); err != nil {
			logrus.Warnf("Peer key scan on network %s failed: %v", nid, err)
		}
		if f(&pKey, &pEntry) {
			return nil
		}
	}

	return nil
}

func (d *driver) peerDbSearch(nid string, peerIP net.IP) (net.HardwareAddr, net.IPMask, net.IP, error) {
	var (
		peerMac    net.HardwareAddr
		vtep       net.IP
		peerIPMask net.IPMask
		found      bool
	)

	err := d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
		if pKey.peerIP.Equal(peerIP) {
			peerMac = pKey.peerMac
			peerIPMask = pEntry.peerIPMask
			vtep = pEntry.vtep
			found = true
			return found
		}

		return found
	})

	if err != nil {
		return nil, nil, nil, fmt.Errorf("peerdb search for peer ip %q failed: %v", peerIP, err)
	}

	if !found {
		return nil, nil, nil, fmt.Errorf("peer ip %q not found in peerdb", peerIP)
	}

	return peerMac, peerIPMask, vtep, nil
}

func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, isLocal bool) {

	peerDbWg.Wait()

	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	if !ok {
		d.peerDb.mp[nid] = &peerMap{
			mp: make(map[string]peerEntry),
		}

		pMap = d.peerDb.mp[nid]
	}
	d.peerDb.Unlock()

	pKey := peerKey{
		peerIP:  peerIP,
		peerMac: peerMac,
	}

	pEntry := peerEntry{
		eid:        eid,
		vtep:       vtep,
		peerIPMask: peerIPMask,
		isLocal:    isLocal,
	}

	pMap.Lock()
	pMap.mp[pKey.String()] = pEntry
	pMap.Unlock()
}

func (d *driver) peerDbDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP) peerEntry {
	peerDbWg.Wait()

	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	if !ok {
		d.peerDb.Unlock()
		return peerEntry{}
	}
	d.peerDb.Unlock()

	pKey := peerKey{
		peerIP:  peerIP,
		peerMac: peerMac,
	}

	pMap.Lock()

	pEntry, ok := pMap.mp[pKey.String()]
	if ok {
		// Mismatched endpoint ID(possibly outdated). Do not
		// delete peerdb
		if pEntry.eid != eid {
			pMap.Unlock()
			return pEntry
		}
	}

	delete(pMap.mp, pKey.String())
	pMap.Unlock()

	return pEntry
}

func (d *driver) peerDbUpdateSandbox(nid string) {
	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	if !ok {
		d.peerDb.Unlock()
		return
	}
	d.peerDb.Unlock()

	peerDbWg.Add(1)

	var peerOps []func()
	pMap.Lock()
	for pKeyStr, pEntry := range pMap.mp {
		var pKey peerKey
		if _, err := fmt.Sscan(pKeyStr, &pKey); err != nil {
			fmt.Printf("peer key scan failed: %v", err)
		}

		if pEntry.isLocal {
			continue
		}

		// Go captures variables by reference. The pEntry could be
		// pointing to the same memory location for every iteration. Make
		// a copy of pEntry before capturing it in the following closure.
		entry := pEntry
		op := func() {
			if err := d.peerAdd(nid, entry.eid, pKey.peerIP, entry.peerIPMask,
				pKey.peerMac, entry.vtep,
				false, false, false); err != nil {
				fmt.Printf("peerdbupdate in sandbox failed for ip %s and mac %s: %v",
					pKey.peerIP, pKey.peerMac, err)
			}
		}

		peerOps = append(peerOps, op)
	}
	pMap.Unlock()

	for _, op := range peerOps {
		op()
	}

	peerDbWg.Done()
}

func (d *driver) peerAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, updateDb, l2Miss, l3Miss bool) error {

	if err := validateID(nid, eid); err != nil {
		return err
	}

	if updateDb {
		d.peerDbAdd(nid, eid, peerIP, peerIPMask, peerMac, vtep, false)
	}

	n := d.network(nid)
	if n == nil {
		return nil
	}

	sbox := n.sandbox()
	if sbox == nil {
		return nil
	}

	IP := &net.IPNet{
		IP:   peerIP,
		Mask: peerIPMask,
	}

	s := n.getSubnetforIP(IP)
	if s == nil {
		return fmt.Errorf("couldn't find the subnet %q in network %q", IP.String(), n.id)
	}

	if err := n.obtainVxlanID(s); err != nil {
		return fmt.Errorf("couldn't get vxlan id for %q: %v", s.subnetIP.String(), err)
	}

	if err := n.joinSubnetSandbox(s, false); err != nil {
		return fmt.Errorf("subnet sandbox join failed for %q: %v", s.subnetIP.String(), err)
	}

	if err := d.checkEncryption(nid, vtep, n.vxlanID(s), false, true); err != nil {
		logrus.Warn(err)
	}

	// Add neighbor entry for the peer IP
	if err := sbox.AddNeighbor(peerIP, peerMac, l3Miss, sbox.NeighborOptions().LinkName(s.vxlanName)); err != nil {
		return fmt.Errorf("could not add neighbor entry into the sandbox: %v", err)
	}

	// Add fdb entry to the bridge for the peer mac
	if err := sbox.AddNeighbor(vtep, peerMac, l2Miss, sbox.NeighborOptions().LinkName(s.vxlanName),
		sbox.NeighborOptions().Family(syscall.AF_BRIDGE)); err != nil {
		return fmt.Errorf("could not add fdb entry into the sandbox: %v", err)
	}

	return nil
}

func (d *driver) peerDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, updateDb bool) error {

	if err := validateID(nid, eid); err != nil {
		return err
	}

	var pEntry peerEntry
	if updateDb {
		pEntry = d.peerDbDelete(nid, eid, peerIP, peerIPMask, peerMac, vtep)
	}

	n := d.network(nid)
	if n == nil {
		return nil
	}

	sbox := n.sandbox()
	if sbox == nil {
		return nil
	}

	// Delete fdb entry to the bridge for the peer mac only if the
	// entry existed in local peerdb. If it is a stale delete
	// request, still call DeleteNeighbor but only to cleanup any
	// leftover sandbox neighbor cache and not actually delete the
	// kernel state.
	if (eid == pEntry.eid && vtep.Equal(pEntry.vtep)) ||
		(eid != pEntry.eid && !vtep.Equal(pEntry.vtep)) {
		if err := sbox.DeleteNeighbor(vtep, peerMac,
			eid == pEntry.eid && vtep.Equal(pEntry.vtep)); err != nil {
			return fmt.Errorf("could not delete fdb entry into the sandbox: %v", err)
		}
	}

	// Delete neighbor entry for the peer IP
	if eid == pEntry.eid {
		if err := sbox.DeleteNeighbor(peerIP, peerMac, true); err != nil {
			return fmt.Errorf("could not delete neighbor entry into the sandbox: %v", err)
		}
	}

	if err := d.checkEncryption(nid, vtep, 0, false, false); err != nil {
		logrus.Warn(err)
	}

	return nil
}

func (d *driver) pushLocalDb() {
	d.peerDbWalk(func(nid string, pKey *peerKey, pEntry *peerEntry) bool {
		if pEntry.isLocal {
			d.pushLocalEndpointEvent("join", nid, pEntry.eid)
		}
		return false
	})
}

func (d *driver) peerDBUpdateSelf() {
	d.peerDbWalk(func(nid string, pkey *peerKey, pEntry *peerEntry) bool {
		if pEntry.isLocal {
			pEntry.vtep = net.ParseIP(d.advertiseAddress)
		}
		return false
	})
}
<commit_msg>Removed printfs<commit_after>package overlay

import (
	"fmt"
	"net"
	"sync"
	"syscall"

	"github.com/Sirupsen/logrus"
)

const ovPeerTable = "overlay_peer_table"

type peerKey struct {
	peerIP  net.IP
	peerMac net.HardwareAddr
}

type peerEntry struct {
	eid        string
	vtep       net.IP
	peerIPMask net.IPMask
	inSandbox  bool
	isLocal    bool
}

type peerMap struct {
	mp map[string]peerEntry
	sync.Mutex
}

type peerNetworkMap struct {
	mp map[string]*peerMap
	sync.Mutex
}

func (pKey peerKey) String() string {
	return fmt.Sprintf("%s %s", pKey.peerIP, pKey.peerMac)
}

func (pKey *peerKey) Scan(state fmt.ScanState, verb rune) error {
	ipB, err := state.Token(true, nil)
	if err != nil {
		return err
	}

	pKey.peerIP = net.ParseIP(string(ipB))

	macB, err := state.Token(true, nil)
	if err != nil {
		return err
	}

	pKey.peerMac, err = net.ParseMAC(string(macB))
	if err != nil {
		return err
	}

	return nil
}

var peerDbWg sync.WaitGroup

func (d *driver) peerDbWalk(f func(string, *peerKey, *peerEntry) bool) error {
	d.peerDb.Lock()
	nids := []string{}
	for nid := range d.peerDb.mp {
		nids = append(nids, nid)
	}
	d.peerDb.Unlock()

	for _, nid := range nids {
		d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
			return f(nid, pKey, pEntry)
		})
	}
	return nil
}

func (d *driver) peerDbNetworkWalk(nid string, f func(*peerKey, *peerEntry) bool) error {
	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	d.peerDb.Unlock()

	if !ok {
		return nil
	}

	mp := map[string]peerEntry{}

	pMap.Lock()
	for pKeyStr, pEntry := range pMap.mp {
		mp[pKeyStr] = pEntry
	}
	pMap.Unlock()

	for pKeyStr, pEntry := range mp {
		var pKey peerKey
		if _, err := fmt.Sscan(pKeyStr, &pKey); err != nil {
			logrus.Warnf("Peer key scan on network %s failed: %v", nid, err)
		}
		if f(&pKey, &pEntry) {
			return nil
		}
	}

	return nil
}

func (d *driver) peerDbSearch(nid string, peerIP net.IP) (net.HardwareAddr, net.IPMask, net.IP, error) {
	var (
		peerMac    net.HardwareAddr
		vtep       net.IP
		peerIPMask net.IPMask
		found      bool
	)

	err := d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
		if pKey.peerIP.Equal(peerIP) {
			peerMac = pKey.peerMac
			peerIPMask = pEntry.peerIPMask
			vtep = pEntry.vtep
			found = true
			return found
		}

		return found
	})

	if err != nil {
		return nil, nil, nil, fmt.Errorf("peerdb search for peer ip %q failed: %v", peerIP, err)
	}

	if !found {
		return nil, nil, nil, fmt.Errorf("peer ip %q not found in peerdb", peerIP)
	}

	return peerMac, peerIPMask, vtep, nil
}

func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, isLocal bool) {

	peerDbWg.Wait()

	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	if !ok {
		d.peerDb.mp[nid] = &peerMap{
			mp: make(map[string]peerEntry),
		}

		pMap = d.peerDb.mp[nid]
	}
	d.peerDb.Unlock()

	pKey := peerKey{
		peerIP:  peerIP,
		peerMac: peerMac,
	}

	pEntry := peerEntry{
		eid:        eid,
		vtep:       vtep,
		peerIPMask: peerIPMask,
		isLocal:    isLocal,
	}

	pMap.Lock()
	pMap.mp[pKey.String()] = pEntry
	pMap.Unlock()
}

func (d *driver) peerDbDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP) peerEntry {
	peerDbWg.Wait()

	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	if !ok {
		d.peerDb.Unlock()
		return peerEntry{}
	}
	d.peerDb.Unlock()

	pKey := peerKey{
		peerIP:  peerIP,
		peerMac: peerMac,
	}

	pMap.Lock()

	pEntry, ok := pMap.mp[pKey.String()]
	if ok {
		// Mismatched endpoint ID(possibly outdated). Do not
		// delete peerdb
		if pEntry.eid != eid {
			pMap.Unlock()
			return pEntry
		}
	}

	delete(pMap.mp, pKey.String())
	pMap.Unlock()

	return pEntry
}

func (d *driver) peerDbUpdateSandbox(nid string) {
	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	if !ok {
		d.peerDb.Unlock()
		return
	}
	d.peerDb.Unlock()

	peerDbWg.Add(1)

	var peerOps []func()
	pMap.Lock()
	for pKeyStr, pEntry := range pMap.mp {
		var pKey peerKey
		if _, err := fmt.Sscan(pKeyStr, &pKey); err != nil {
			logrus.Errorf("peer key scan failed: %v", err)
		}

		if pEntry.isLocal {
			continue
		}

		// Go captures variables by reference. The pEntry could be
		// pointing to the same memory location for every iteration. Make
		// a copy of pEntry before capturing it in the following closure.
		entry := pEntry
		op := func() {
			if err := d.peerAdd(nid, entry.eid, pKey.peerIP, entry.peerIPMask,
				pKey.peerMac, entry.vtep,
				false, false, false); err != nil {
				logrus.Errorf("peerdbupdate in sandbox failed for ip %s and mac %s: %v",
					pKey.peerIP, pKey.peerMac, err)
			}
		}

		peerOps = append(peerOps, op)
	}
	pMap.Unlock()

	for _, op := range peerOps {
		op()
	}

	peerDbWg.Done()
}

func (d *driver) peerAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, updateDb, l2Miss, l3Miss bool) error {

	if err := validateID(nid, eid); err != nil {
		return err
	}

	if updateDb {
		d.peerDbAdd(nid, eid, peerIP, peerIPMask, peerMac, vtep, false)
	}

	n := d.network(nid)
	if n == nil {
		return nil
	}

	sbox := n.sandbox()
	if sbox == nil {
		return nil
	}

	IP := &net.IPNet{
		IP:   peerIP,
		Mask: peerIPMask,
	}

	s := n.getSubnetforIP(IP)
	if s == nil {
		return fmt.Errorf("couldn't find the subnet %q in network %q", IP.String(), n.id)
	}

	if err := n.obtainVxlanID(s); err != nil {
		return fmt.Errorf("couldn't get vxlan id for %q: %v", s.subnetIP.String(), err)
	}

	if err := n.joinSubnetSandbox(s, false); err != nil {
		return fmt.Errorf("subnet sandbox join failed for %q: %v", s.subnetIP.String(), err)
	}

	if err := d.checkEncryption(nid, vtep, n.vxlanID(s), false, true); err != nil {
		logrus.Warn(err)
	}

	// Add neighbor entry for the peer IP
	if err := sbox.AddNeighbor(peerIP, peerMac, l3Miss, sbox.NeighborOptions().LinkName(s.vxlanName)); err != nil {
		return fmt.Errorf("could not add neighbor entry into the sandbox: %v", err)
	}

	// Add fdb entry to the bridge for the peer mac
	if err := sbox.AddNeighbor(vtep, peerMac, l2Miss, sbox.NeighborOptions().LinkName(s.vxlanName),
		sbox.NeighborOptions().Family(syscall.AF_BRIDGE)); err != nil {
		return fmt.Errorf("could not add fdb entry into the sandbox: %v", err)
	}

	return nil
}

func (d *driver) peerDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, updateDb bool) error {

	if err := validateID(nid, eid); err != nil {
		return err
	}

	var pEntry peerEntry
	if updateDb {
		pEntry = d.peerDbDelete(nid, eid, peerIP, peerIPMask, peerMac, vtep)
	}

	n := d.network(nid)
	if n == nil {
		return nil
	}

	sbox := n.sandbox()
	if sbox == nil {
		return nil
	}

	// Delete fdb entry to the bridge for the peer mac only if the
	// entry existed in local peerdb. If it is a stale delete
	// request, still call DeleteNeighbor but only to cleanup any
	// leftover sandbox neighbor cache and not actually delete the
	// kernel state.
	if (eid == pEntry.eid && vtep.Equal(pEntry.vtep)) ||
		(eid != pEntry.eid && !vtep.Equal(pEntry.vtep)) {
		if err := sbox.DeleteNeighbor(vtep, peerMac,
			eid == pEntry.eid && vtep.Equal(pEntry.vtep)); err != nil {
			return fmt.Errorf("could not delete fdb entry into the sandbox: %v", err)
		}
	}

	// Delete neighbor entry for the peer IP
	if eid == pEntry.eid {
		if err := sbox.DeleteNeighbor(peerIP, peerMac, true); err != nil {
			return fmt.Errorf("could not delete neighbor entry into the sandbox: %v", err)
		}
	}

	if err := d.checkEncryption(nid, vtep, 0, false, false); err != nil {
		logrus.Warn(err)
	}

	return nil
}

func (d *driver) pushLocalDb() {
	d.peerDbWalk(func(nid string, pKey *peerKey, pEntry *peerEntry) bool {
		if pEntry.isLocal {
			d.pushLocalEndpointEvent("join", nid, pEntry.eid)
		}
		return false
	})
}

func (d *driver) peerDBUpdateSelf() {
	d.peerDbWalk(func(nid string, pkey *peerKey, pEntry *peerEntry) bool {
		if pEntry.isLocal {
			pEntry.vtep = net.ParseIP(d.advertiseAddress)
		}
		return false
	})
}
<|endoftext|>


<commit_before>package main

// This file contains various demo applications of the go-mapnik package

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	"github.com/fawick/go-mapnik/mapnik"
	"github.com/fawick/go-mapnik/maptiles"
)

// Render a simple map of europe to a PNG file
func SimpleExample() {
	m := mapnik.NewMap(1600, 1200)
	defer m.Free()
	m.Load("sampledata/stylesheet.xml")
	fmt.Println(m.SRS())
	// Perform a projection that is only neccessary because stylesheet.xml
	// is using EPSG:3857 rather than WGS84
	p := m.Projection()
	ll := p.Forward(mapnik.Coord{0, 35})  // 0 degrees longitude, 35 degrees north
	ur := p.Forward(mapnik.Coord{16, 70}) // 16 degrees east, 70 degrees north
	m.ZoomToMinMax(ll.X, ll.Y, ur.X, ur.Y)
	blob, err := m.RenderToMemoryPng()
	if err != nil {
		fmt.Println(err)
		return
	}
	ioutil.WriteFile("mapnik.png", blob, 0644)
}

// This function resembles the OSM python script 'generate_tiles.py'
// The original script is found here:
// http://svn.openstreetmap.org/applications/rendering/mapnik/generate_tiles.py
func GenerateOSMTiles() {
	g := maptiles.Generator{}

	// Modify this number according to your machine!
	g.Threads = 4

	home := os.Getenv("HOME")
	g.MapFile = os.Getenv("MAPNIK_MAP_FILE")
	if g.MapFile == "" {
		g.MapFile = home + "/svn.openstreetmap.org/applications/rendering/mapnik/osm-local.xml"
	}
	g.TileDir = os.Getenv("MAPNIK_TILE_DIR")
	if g.TileDir == "" {
		g.TileDir = home + "/osm/tiles"
	}

	g.Run(mapnik.Coord{-180, -90}, mapnik.Coord{180, 90}, 0, 6, "World")
	g.Run(mapnik.Coord{0, 35.0}, mapnik.Coord{16, 70}, 1, 11, "Europe")
}

// Serve a single stylesheet via HTTP. Open view_tileserver.html in your browser
// to see the results.
// The created tiles are cached in an sqlite database (MBTiles 1.2 conform) so
// successive access a tile is much faster.
func TileserverWithCaching() {
	cache := "gomapnikcache.sqlite"
	os.Remove(cache)
	t := maptiles.NewTileServer(cache)
	t.AddMapnikLayer("default", "sampledata/stylesheet.xml")
	http.ListenAndServe(":8080", t)
}

// Before uncommenting the GenerateOSMTiles call make sure you have
// the neccessary OSM sources. Consult OSM wiki for details.
func main() {
	SimpleExample()
	//GenerateOSMTiles()
	TileserverWithCaching()
}
<commit_msg>tile server logging<commit_after>package main

// This file contains various demo applications of the go-mapnik package

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	"github.com/sjsafranek/go-mapnik/mapnik"
	"github.com/sjsafranek/go-mapnik/maptiles"
)

// Render a simple map of europe to a PNG file
func SimpleExample() {
	m := mapnik.NewMap(1600, 1200)
	defer m.Free()
	m.Load("sampledata/stylesheet.xml")
	fmt.Println(m.SRS())
	// Perform a projection that is only neccessary because stylesheet.xml
	// is using EPSG:3857 rather than WGS84
	p := m.Projection()
	ll := p.Forward(mapnik.Coord{0, 35})  // 0 degrees longitude, 35 degrees north
	ur := p.Forward(mapnik.Coord{16, 70}) // 16 degrees east, 70 degrees north
	m.ZoomToMinMax(ll.X, ll.Y, ur.X, ur.Y)
	blob, err := m.RenderToMemoryPng()
	if err != nil {
		fmt.Println(err)
		return
	}
	ioutil.WriteFile("mapnik.png", blob, 0644)
}

// This function resembles the OSM python script 'generate_tiles.py'
// The original script is found here:
// http://svn.openstreetmap.org/applications/rendering/mapnik/generate_tiles.py
func GenerateOSMTiles() {
	g := maptiles.Generator{}

	// Modify this number according to your machine!
	g.Threads = 4

	home := os.Getenv("HOME")
	g.MapFile = os.Getenv("MAPNIK_MAP_FILE")
	if g.MapFile == "" {
		g.MapFile = home + "/svn.openstreetmap.org/applications/rendering/mapnik/osm-local.xml"
	}
	g.TileDir = os.Getenv("MAPNIK_TILE_DIR")
	if g.TileDir == "" {
		g.TileDir = home + "/osm/tiles"
	}

	g.Run(mapnik.Coord{-180, -90}, mapnik.Coord{180, 90}, 0, 6, "World")
	g.Run(mapnik.Coord{0, 35.0}, mapnik.Coord{16, 70}, 1, 11, "Europe")
}

// Serve a single stylesheet via HTTP. Open view_tileserver.html in your browser
// to see the results.
// The created tiles are cached in an sqlite database (MBTiles 1.2 conform) so
// successive access a tile is much faster.
func TileserverWithCaching() {
	cache := "gomapnikcache.sqlite"
	os.Remove(cache)
	t := maptiles.NewTileServer(cache)
	t.AddMapnikLayer("default", "sampledata/stylesheet.xml")
	http.ListenAndServe(":8080", t)
}

// Before uncommenting the GenerateOSMTiles call make sure you have
// the neccessary OSM sources. Consult OSM wiki for details.
func main() {
	SimpleExample()
	//GenerateOSMTiles()
	TileserverWithCaching()
}
<|endoftext|>


<commit_before>package conn

import (
	"context"
	"fmt"
	"math/rand"
	"strings"
	"time"

	addrutil "github.com/libp2p/go-addr-util"
	ci "github.com/libp2p/go-libp2p-crypto"
	iconn "github.com/libp2p/go-libp2p-interface-conn"
	lgbl "github.com/libp2p/go-libp2p-loggables"
	peer "github.com/libp2p/go-libp2p-peer"
	transport "github.com/libp2p/go-libp2p-transport"
	ma "github.com/multiformats/go-multiaddr"
	manet "github.com/multiformats/go-multiaddr-net"
	msmux "github.com/whyrusleeping/go-multistream"
)

type WrapFunc func(transport.Conn) transport.Conn

// Dialer is an object that can open connections. We could have a "convenience"
// Dial function as before, but it would have many arguments, as dialing is
// no longer simple (need a peerstore, a local peer, a context, a network, etc)
type Dialer struct {
	// LocalPeer is the identity of the local Peer.
	LocalPeer peer.ID

	// LocalAddrs is a set of local addresses to use.
	//LocalAddrs []ma.Multiaddr

	// Dialers are the sub-dialers usable by this dialer
	// selected in order based on the address being dialed
	Dialers []transport.Dialer

	// PrivateKey used to initialize a secure connection.
	// Warning: if PrivateKey is nil, connection will not be secured.
	PrivateKey ci.PrivKey

	// Wrapper to wrap the raw connection (optional)
	Wrapper WrapFunc

	fallback transport.Dialer
}

func NewDialer(p peer.ID, pk ci.PrivKey, wrap WrapFunc) *Dialer {
	return &Dialer{
		LocalPeer:  p,
		PrivateKey: pk,
		Wrapper:    wrap,
		fallback:   new(transport.FallbackDialer),
	}
}

// String returns the string rep of d.
func (d *Dialer) String() string {
	return fmt.Sprintf("<Dialer %s ...>", d.LocalPeer)
}

// Dial connects to a peer over a particular address
// Ensures raddr is part of peer.Addresses()
// Example: d.DialAddr(ctx, peer.Addresses()[0], peer)
func (d *Dialer) Dial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (iconn.Conn, error) {
	logdial := lgbl.Dial("conn", d.LocalPeer, remote, nil, raddr)
	logdial["encrypted"] = (d.PrivateKey != nil) // log wether this will be an encrypted dial or not.
	defer log.EventBegin(ctx, "connDial", logdial).Done()

	var connOut iconn.Conn
	var errOut error
	done := make(chan struct{})

	// do it async to ensure we respect don contexteone
	go func() {
		defer func() {
			select {
			case done <- struct{}{}:
			case <-ctx.Done():
			}
		}()

		maconn, err := d.rawConnDial(ctx, raddr, remote)
		if err != nil {
			errOut = err
			return
		}

		if d.Wrapper != nil {
			maconn = d.Wrapper(maconn)
		}

		cryptoProtoChoice := SecioTag
		if !iconn.EncryptConnections || d.PrivateKey == nil {
			cryptoProtoChoice = NoEncryptionTag
		}

		maconn.SetReadDeadline(time.Now().Add(NegotiateReadTimeout))

		err = msmux.SelectProtoOrFail(cryptoProtoChoice, maconn)
		if err != nil {
			errOut = err
			return
		}

		maconn.SetReadDeadline(time.Time{})

		c, err := newSingleConn(ctx, d.LocalPeer, remote, maconn)
		if err != nil {
			maconn.Close()
			errOut = err
			return
		}

		if d.PrivateKey == nil || !iconn.EncryptConnections {
			log.Warning("dialer %s dialing INSECURELY %s at %s!", d, remote, raddr)
			connOut = c
			return
		}

		c2, err := newSecureConn(ctx, d.PrivateKey, c)
		if err != nil {
			errOut = err
			c.Close()
			return
		}

		connOut = c2
	}()

	select {
	case <-ctx.Done():
		logdial["error"] = ctx.Err()
		logdial["dial"] = "failure"
		return nil, ctx.Err()
	case <-done:
		// whew, finished.
	}

	if errOut != nil {
		logdial["error"] = errOut
		logdial["dial"] = "failure"
		return nil, errOut
	}

	logdial["dial"] = "success"
	return connOut, nil
}

func (d *Dialer) AddDialer(pd transport.Dialer) {
	d.Dialers = append(d.Dialers, pd)
}

// returns dialer that can dial the given address
func (d *Dialer) subDialerForAddr(raddr ma.Multiaddr) transport.Dialer {
	for _, pd := range d.Dialers {
		if pd.Matches(raddr) {
			return pd
		}
	}

	if d.fallback.Matches(raddr) {
		return d.fallback
	}

	return nil
}

// rawConnDial dials the underlying net.Conn + manet.Conns
func (d *Dialer) rawConnDial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (transport.Conn, error) {
	if strings.HasPrefix(raddr.String(), "/ip4/0.0.0.0") {
		log.Event(ctx, "connDialZeroAddr", lgbl.Dial("conn", d.LocalPeer, remote, nil, raddr))
		return nil, fmt.Errorf("Attempted to connect to zero address: %s", raddr)
	}

	sd := d.subDialerForAddr(raddr)
	if sd == nil {
		return nil, fmt.Errorf("no dialer for %s", raddr)
	}

	return sd.DialContext(ctx, raddr)
}

func pickLocalAddr(laddrs []ma.Multiaddr, raddr ma.Multiaddr) (laddr ma.Multiaddr) {
	if len(laddrs) < 1 {
		return nil
	}

	// make sure that we ONLY use local addrs that match the remote addr.
	laddrs = manet.AddrMatch(raddr, laddrs)
	if len(laddrs) < 1 {
		return nil
	}

	// make sure that we ONLY use local addrs that CAN dial the remote addr.
	// filter out all the local addrs that aren't capable
	raddrIPLayer := ma.Split(raddr)[0]
	raddrIsLoopback := manet.IsIPLoopback(raddrIPLayer)
	raddrIsLinkLocal := manet.IsIP6LinkLocal(raddrIPLayer)
	laddrs = addrutil.FilterAddrs(laddrs, func(a ma.Multiaddr) bool {
		laddrIPLayer := ma.Split(a)[0]
		laddrIsLoopback := manet.IsIPLoopback(laddrIPLayer)
		laddrIsLinkLocal := manet.IsIP6LinkLocal(laddrIPLayer)
		if laddrIsLoopback { // our loopback addrs can only dial loopbacks.
			return raddrIsLoopback
		}
		if laddrIsLinkLocal {
			return raddrIsLinkLocal // out linklocal addrs can only dial link locals.
		}
		return true
	})

	// TODO pick with a good heuristic
	// we use a random one for now to prevent bad addresses from making nodes unreachable
	// with a random selection, multiple tries may work.
	return laddrs[rand.Intn(len(laddrs))]
}

// MultiaddrProtocolsMatch returns whether two multiaddrs match in protocol stacks.
func MultiaddrProtocolsMatch(a, b ma.Multiaddr) bool {
	ap := a.Protocols()
	bp := b.Protocols()

	if len(ap) != len(bp) {
		return false
	}

	for i, api := range ap {
		if api.Code != bp[i].Code {
			return false
		}
	}

	return true
}

// MultiaddrNetMatch returns the first Multiaddr found to match  network.
func MultiaddrNetMatch(tgt ma.Multiaddr, srcs []ma.Multiaddr) ma.Multiaddr {
	for _, a := range srcs {
		if MultiaddrProtocolsMatch(tgt, a) {
			return a
		}
	}
	return nil
}
<commit_msg>make errors actually print in logs<commit_after>package conn

import (
	"context"
	"fmt"
	"math/rand"
	"strings"
	"time"

	addrutil "github.com/libp2p/go-addr-util"
	ci "github.com/libp2p/go-libp2p-crypto"
	iconn "github.com/libp2p/go-libp2p-interface-conn"
	lgbl "github.com/libp2p/go-libp2p-loggables"
	peer "github.com/libp2p/go-libp2p-peer"
	transport "github.com/libp2p/go-libp2p-transport"
	ma "github.com/multiformats/go-multiaddr"
	manet "github.com/multiformats/go-multiaddr-net"
	msmux "github.com/whyrusleeping/go-multistream"
)

type WrapFunc func(transport.Conn) transport.Conn

// Dialer is an object that can open connections. We could have a "convenience"
// Dial function as before, but it would have many arguments, as dialing is
// no longer simple (need a peerstore, a local peer, a context, a network, etc)
type Dialer struct {
	// LocalPeer is the identity of the local Peer.
	LocalPeer peer.ID

	// LocalAddrs is a set of local addresses to use.
	//LocalAddrs []ma.Multiaddr

	// Dialers are the sub-dialers usable by this dialer
	// selected in order based on the address being dialed
	Dialers []transport.Dialer

	// PrivateKey used to initialize a secure connection.
	// Warning: if PrivateKey is nil, connection will not be secured.
	PrivateKey ci.PrivKey

	// Wrapper to wrap the raw connection (optional)
	Wrapper WrapFunc

	fallback transport.Dialer
}

func NewDialer(p peer.ID, pk ci.PrivKey, wrap WrapFunc) *Dialer {
	return &Dialer{
		LocalPeer:  p,
		PrivateKey: pk,
		Wrapper:    wrap,
		fallback:   new(transport.FallbackDialer),
	}
}

// String returns the string rep of d.
func (d *Dialer) String() string {
	return fmt.Sprintf("<Dialer %s ...>", d.LocalPeer)
}

// Dial connects to a peer over a particular address
// Ensures raddr is part of peer.Addresses()
// Example: d.DialAddr(ctx, peer.Addresses()[0], peer)
func (d *Dialer) Dial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (iconn.Conn, error) {
	logdial := lgbl.Dial("conn", d.LocalPeer, remote, nil, raddr)
	logdial["encrypted"] = (d.PrivateKey != nil) // log wether this will be an encrypted dial or not.
	defer log.EventBegin(ctx, "connDial", logdial).Done()

	var connOut iconn.Conn
	var errOut error
	done := make(chan struct{})

	// do it async to ensure we respect don contexteone
	go func() {
		defer func() {
			select {
			case done <- struct{}{}:
			case <-ctx.Done():
			}
		}()

		maconn, err := d.rawConnDial(ctx, raddr, remote)
		if err != nil {
			errOut = err
			return
		}

		if d.Wrapper != nil {
			maconn = d.Wrapper(maconn)
		}

		cryptoProtoChoice := SecioTag
		if !iconn.EncryptConnections || d.PrivateKey == nil {
			cryptoProtoChoice = NoEncryptionTag
		}

		maconn.SetReadDeadline(time.Now().Add(NegotiateReadTimeout))

		err = msmux.SelectProtoOrFail(cryptoProtoChoice, maconn)
		if err != nil {
			errOut = err
			return
		}

		maconn.SetReadDeadline(time.Time{})

		c, err := newSingleConn(ctx, d.LocalPeer, remote, maconn)
		if err != nil {
			maconn.Close()
			errOut = err
			return
		}

		if d.PrivateKey == nil || !iconn.EncryptConnections {
			log.Warning("dialer %s dialing INSECURELY %s at %s!", d, remote, raddr)
			connOut = c
			return
		}

		c2, err := newSecureConn(ctx, d.PrivateKey, c)
		if err != nil {
			errOut = err
			c.Close()
			return
		}

		connOut = c2
	}()

	select {
	case <-ctx.Done():
		logdial["error"] = ctx.Err().Error()
		logdial["dial"] = "failure"
		return nil, ctx.Err()
	case <-done:
		// whew, finished.
	}

	if errOut != nil {
		logdial["error"] = errOut.Error()
		logdial["dial"] = "failure"
		return nil, errOut
	}

	logdial["dial"] = "success"
	return connOut, nil
}

func (d *Dialer) AddDialer(pd transport.Dialer) {
	d.Dialers = append(d.Dialers, pd)
}

// returns dialer that can dial the given address
func (d *Dialer) subDialerForAddr(raddr ma.Multiaddr) transport.Dialer {
	for _, pd := range d.Dialers {
		if pd.Matches(raddr) {
			return pd
		}
	}

	if d.fallback.Matches(raddr) {
		return d.fallback
	}

	return nil
}

// rawConnDial dials the underlying net.Conn + manet.Conns
func (d *Dialer) rawConnDial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (transport.Conn, error) {
	if strings.HasPrefix(raddr.String(), "/ip4/0.0.0.0") {
		log.Event(ctx, "connDialZeroAddr", lgbl.Dial("conn", d.LocalPeer, remote, nil, raddr))
		return nil, fmt.Errorf("Attempted to connect to zero address: %s", raddr)
	}

	sd := d.subDialerForAddr(raddr)
	if sd == nil {
		return nil, fmt.Errorf("no dialer for %s", raddr)
	}

	return sd.DialContext(ctx, raddr)
}

func pickLocalAddr(laddrs []ma.Multiaddr, raddr ma.Multiaddr) (laddr ma.Multiaddr) {
	if len(laddrs) < 1 {
		return nil
	}

	// make sure that we ONLY use local addrs that match the remote addr.
	laddrs = manet.AddrMatch(raddr, laddrs)
	if len(laddrs) < 1 {
		return nil
	}

	// make sure that we ONLY use local addrs that CAN dial the remote addr.
	// filter out all the local addrs that aren't capable
	raddrIPLayer := ma.Split(raddr)[0]
	raddrIsLoopback := manet.IsIPLoopback(raddrIPLayer)
	raddrIsLinkLocal := manet.IsIP6LinkLocal(raddrIPLayer)
	laddrs = addrutil.FilterAddrs(laddrs, func(a ma.Multiaddr) bool {
		laddrIPLayer := ma.Split(a)[0]
		laddrIsLoopback := manet.IsIPLoopback(laddrIPLayer)
		laddrIsLinkLocal := manet.IsIP6LinkLocal(laddrIPLayer)
		if laddrIsLoopback { // our loopback addrs can only dial loopbacks.
			return raddrIsLoopback
		}
		if laddrIsLinkLocal {
			return raddrIsLinkLocal // out linklocal addrs can only dial link locals.
		}
		return true
	})

	// TODO pick with a good heuristic
	// we use a random one for now to prevent bad addresses from making nodes unreachable
	// with a random selection, multiple tries may work.
	return laddrs[rand.Intn(len(laddrs))]
}

// MultiaddrProtocolsMatch returns whether two multiaddrs match in protocol stacks.
func MultiaddrProtocolsMatch(a, b ma.Multiaddr) bool {
	ap := a.Protocols()
	bp := b.Protocols()

	if len(ap) != len(bp) {
		return false
	}

	for i, api := range ap {
		if api.Code != bp[i].Code {
			return false
		}
	}

	return true
}

// MultiaddrNetMatch returns the first Multiaddr found to match  network.
func MultiaddrNetMatch(tgt ma.Multiaddr, srcs []ma.Multiaddr) ma.Multiaddr {
	for _, a := range srcs {
		if MultiaddrProtocolsMatch(tgt, a) {
			return a
		}
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Doctl Authors All rights reserved.
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 doctl

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/blang/semver"
	"github.com/digitalocean/doctl/pkg/runner"
	"github.com/digitalocean/doctl/pkg/ssh"
	"github.com/digitalocean/godo"
	"github.com/spf13/viper"
	"golang.org/x/oauth2"
)

const (
	// LatestReleaseURL is the latest release URL endpoint.
	LatestReleaseURL = "https://api.github.com/repos/digitalocean/doctl/releases/latest"
)

// Version is the version info for doit.
type Version struct {
	Major, Minor, Patch int
	Name, Build, Label  string
}

var (
	// Build, Major, Minor, Patch and Label are set at build time
	Build, Major, Minor, Patch, Label string

	// DoitVersion is doctl's version.
	DoitVersion Version

	// TraceHTTP traces http connections.
	TraceHTTP bool
)

func init() {
	if Build != "" {
		DoitVersion.Build = Build
	}
	if Major != "" {
		i, _ := strconv.Atoi(Major)
		DoitVersion.Major = i
	}
	if Minor != "" {
		i, _ := strconv.Atoi(Minor)
		DoitVersion.Minor = i
	}
	if Patch != "" {
		i, _ := strconv.Atoi(Patch)
		DoitVersion.Patch = i
	}
	if Label == "" {
		DoitVersion.Label = "dev"
	} else {
		DoitVersion.Label = Label
	}
}

func (v Version) String() string {
	var buffer bytes.Buffer
	buffer.WriteString(fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch))
	if v.Label != "" {
		buffer.WriteString("-" + v.Label)
	}

	return buffer.String()
}

// Complete is the complete version for doit.
func (v Version) Complete(lv LatestVersioner) string {
	var buffer bytes.Buffer
	buffer.WriteString(fmt.Sprintf("doctl version %s", v.String()))

	if v.Build != "" {
		buffer.WriteString(fmt.Sprintf("\nGit commit hash: %s", v.Build))
	}

	if tagName, err := lv.LatestVersion(); err == nil {
		v0, err1 := semver.Make(tagName)
		v1, err2 := semver.Make(v.String())

		if len(v0.Build) == 0 {
			v0, err1 = semver.Make(tagName + "-release")
		}

		if err1 == nil && err2 == nil && v0.GT(v1) {
			buffer.WriteString(fmt.Sprintf("\nrelease %s is available, check it out! ", tagName))
		}
	}

	return buffer.String()
}

// LatestVersioner an interface for detecting the latest version.
type LatestVersioner interface {
	LatestVersion() (string, error)
}

// GithubLatestVersioner retrieves the latest version from Github.
type GithubLatestVersioner struct{}

var _ LatestVersioner = &GithubLatestVersioner{}

// LatestVersion retrieves the latest version from Github or returns
// an error.
func (glv *GithubLatestVersioner) LatestVersion() (string, error) {
	u := LatestReleaseURL
	res, err := http.Get(u)
	if err != nil {
		return "", err
	}

	defer res.Body.Close()

	var m map[string]interface{}
	if err = json.NewDecoder(res.Body).Decode(&m); err != nil {
		return "", err
	}

	tn, ok := m["tag_name"]
	if !ok {
		return "", errors.New("could not find tag name in response")
	}

	tagName := tn.(string)
	return strings.TrimPrefix(tagName, "v"), nil
}

// Config is an interface that represent doit's config.
type Config interface {
	GetGodoClient(trace bool, accessToken string) (*godo.Client, error)
	SSH(user, host, keyPath string, port int, opts ssh.Options) runner.Runner
	Set(ns, key string, val interface{})
	IsSet(key string) bool
	GetString(ns, key string) (string, error)
	GetBool(ns, key string) (bool, error)
	GetBoolPtr(ns, key string) (*bool, error)
	GetInt(ns, key string) (int, error)
	GetIntPtr(ns, key string) (*int, error)
	GetStringSlice(ns, key string) ([]string, error)
}

// LiveConfig is an implementation of Config for live values.
type LiveConfig struct {
	cliArgs map[string]bool
}

var _ Config = &LiveConfig{}

// GetGodoClient returns a GodoClient.
func (c *LiveConfig) GetGodoClient(trace bool, accessToken string) (*godo.Client, error) {
	if accessToken == "" {
		return nil, fmt.Errorf("access token is required. (hint: run 'doctl auth init')")
	}

	tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: accessToken})
	oauthClient := oauth2.NewClient(context.Background(), tokenSource)

	if trace {
		r := newRecorder(oauthClient.Transport)

		go func() {
			for {
				select {
				case msg := <-r.req:
					log.Println("->", strconv.Quote(msg))
				case msg := <-r.resp:
					log.Println("<-", strconv.Quote(msg))
				}
			}
		}()

		oauthClient.Transport = r
	}

	args := []godo.ClientOpt{godo.SetUserAgent(userAgent())}

	apiURL := viper.GetString("api-url")
	if apiURL != "" {
		args = append(args, godo.SetBaseURL(apiURL))
	}

	return godo.New(oauthClient, args...)
}

func userAgent() string {
	return "doctl/" + DoitVersion.String()
}

// SSH creates a ssh connection to a host.
func (c *LiveConfig) SSH(user, host, keyPath string, port int, opts ssh.Options) runner.Runner {
	return &ssh.Runner{
		User:            user,
		Host:            host,
		KeyPath:         keyPath,
		Port:            port,
		AgentForwarding: opts[ArgsSSHAgentForwarding].(bool),
		Command:         opts[ArgSSHCommand].(string),
	}
}

// Set sets a config key.
func (c *LiveConfig) Set(ns, key string, val interface{}) {
	nskey := fmt.Sprintf("%s-%s", ns, key)
	viper.Set(nskey, val)
}

func (c *LiveConfig) IsSet(key string) bool {
	r := regexp.MustCompile("\b*--([a-z-_]+)")
	matches := r.FindAllStringSubmatch(strings.Join(os.Args, " "), -1)
	if len(matches) == 0 {
		return false
	}

	if len(c.cliArgs) == 0 {
		args := make(map[string]bool)
		for _, match := range matches {
			args[match[1]] = true
		}
		c.cliArgs = args
	}

	return c.cliArgs[key]
}

// GetString returns a config value as a string.
func (c *LiveConfig) GetString(ns, key string) (string, error) {
	nskey := fmt.Sprintf("%s.%s", ns, key)

	isRequired := viper.GetBool(fmt.Sprintf("required.%s", nskey))
	str := viper.GetString(nskey)

	if isRequired && strings.TrimSpace(str) == "" {
		return "", NewMissingArgsErr(nskey)
	}

	return str, nil
}

// GetBool returns a config value as a bool.
func (c *LiveConfig) GetBool(ns, key string) (bool, error) {
	nskey := fmt.Sprintf("%s.%s", ns, key)

	return viper.GetBool(nskey), nil
}

// GetBoolPtr returns a config value as a bool pointer.
func (c *LiveConfig) GetBoolPtr(ns, key string) (*bool, error) {
	if !c.IsSet(key) {
		return nil, nil
	}

	nskey := fmt.Sprintf("%s.%s", ns, key)
	val := viper.GetBool(nskey)

	return &val, nil
}

// GetInt returns a config value as an int.
func (c *LiveConfig) GetInt(ns, key string) (int, error) {
	nskey := fmt.Sprintf("%s.%s", ns, key)

	isRequired := viper.GetBool(fmt.Sprintf("required.%s", nskey))
	val := viper.GetInt(nskey)

	if isRequired && val == 0 {
		return 0, NewMissingArgsErr(nskey)
	}

	return val, nil
}

// GetIntPtr returns a config value as an int pointer.
func (c *LiveConfig) GetIntPtr(ns, key string) (*int, error) {
	nskey := fmt.Sprintf("%s.%s", ns, key)
	isRequired := viper.GetBool(fmt.Sprintf("required.%s", nskey))
	isSet := c.IsSet(key)

	if !isSet {
		if isRequired {
			return nil, NewMissingArgsErr(nskey)
		}
		return nil, nil
	}

	val := viper.GetInt(nskey)
	return &val, nil
}

// GetStringSlice returns a config value as a string slice.
func (c *LiveConfig) GetStringSlice(ns, key string) ([]string, error) {
	nskey := fmt.Sprintf("%s.%s", ns, key)

	isRequired := viper.GetBool(fmt.Sprintf("required.%s", nskey))
	val := viper.GetStringSlice(nskey)
	if isRequired && emptyStringSlice(val) {
		return nil, NewMissingArgsErr(nskey)
	}

	out := []string{}
	for _, item := range viper.GetStringSlice(nskey) {
		item = strings.TrimPrefix(item, "[")
		item = strings.TrimSuffix(item, "]")

		list := strings.Split(item, ",")
		for _, str := range list {
			if str == "" {
				continue
			}

			out = append(out, str)
		}
	}

	return out, nil
}

// TestConfig is an implementation of Config for testing.
type TestConfig struct {
	SSHFn    func(user, host, keyPath string, port int, opts ssh.Options) runner.Runner
	v        *viper.Viper
	IsSetMap map[string]bool
}

var _ Config = &TestConfig{}

// NewTestConfig creates a new, ready-to-use instance of a TestConfig.
func NewTestConfig() *TestConfig {
	return &TestConfig{
		SSHFn: func(u, h, kp string, p int, opts ssh.Options) runner.Runner {
			return &MockRunner{}
		},
		v:        viper.New(),
		IsSetMap: make(map[string]bool),
	}
}

// GetGodoClient mocks a GetGodoClient call. The returned godo client will
// be nil.
func (c *TestConfig) GetGodoClient(trace bool, accessToken string) (*godo.Client, error) {
	return &godo.Client{}, nil
}

// SSH returns a mock SSH runner.
func (c *TestConfig) SSH(user, host, keyPath string, port int, opts ssh.Options) runner.Runner {
	return c.SSHFn(user, host, keyPath, port, opts)
}

// Set sets a config key.
func (c *TestConfig) Set(ns, key string, val interface{}) {
	nskey := fmt.Sprintf("%s-%s", ns, key)
	c.v.Set(nskey, val)
	c.IsSetMap[key] = true
}

// IsSet returns true if the given key is set on the config.
func (c *TestConfig) IsSet(key string) bool {
	return c.IsSetMap[key]
}

// GetString returns the string value for the key in the given namespace. Because
// this is a mock implementation, and error will never be returned.
func (c *TestConfig) GetString(ns, key string) (string, error) {
	nskey := fmt.Sprintf("%s-%s", ns, key)
	return c.v.GetString(nskey), nil
}

// GetInt returns the int value for the key in the given namespace. Because
// this is a mock implementation, and error will never be returned.
func (c *TestConfig) GetInt(ns, key string) (int, error) {
	nskey := fmt.Sprintf("%s-%s", ns, key)
	return c.v.GetInt(nskey), nil
}

// GetIntPtr returns the int value for the key in the given namespace. Because
// this is a mock implementation, and error will never be returned.
func (c *TestConfig) GetIntPtr(ns, key string) (*int, error) {
	nskey := fmt.Sprintf("%s-%s", ns, key)
	if !c.v.IsSet(nskey) {
		return nil, nil
	}
	val := c.v.GetInt(nskey)
	return &val, nil
}

// GetStringSlice returns the string slice value for the key in the given
// namespace. Because this is a mock implementation, and error will never be
// returned.
func (c *TestConfig) GetStringSlice(ns, key string) ([]string, error) {
	nskey := fmt.Sprintf("%s-%s", ns, key)
	return c.v.GetStringSlice(nskey), nil
}

// GetBool returns the bool value for the key in the given namespace. Because
// this is a mock implementation, and error will never be returned.
func (c *TestConfig) GetBool(ns, key string) (bool, error) {
	nskey := fmt.Sprintf("%s-%s", ns, key)
	return c.v.GetBool(nskey), nil
}

// GetBoolPtr returns the bool value for the key in the given namespace. Because
// this is a mock implementation, and error will never be returned.
func (c *TestConfig) GetBoolPtr(ns, key string) (*bool, error) {
	nskey := fmt.Sprintf("%s-%s", ns, key)
	if !c.v.IsSet(nskey) {
		return nil, nil
	}
	val := c.v.GetBool(nskey)
	return &val, nil
}

// This is needed because an empty StringSlice flag returns `["[]"]`
func emptyStringSlice(s []string) bool {
	return len(s) == 1 && s[0] == "[]"
}

// CommandName returns the name by which doctl was invoked
func CommandName() string {
	name, ok := os.LookupEnv("SNAP_NAME")
	if !ok {
		return os.Args[0]
	}
	return name
}
<commit_msg>refactor global viper setter/getters<commit_after>/*
Copyright 2018 The Doctl Authors All rights reserved.
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 doctl

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/blang/semver"
	"github.com/digitalocean/doctl/pkg/runner"
	"github.com/digitalocean/doctl/pkg/ssh"
	"github.com/digitalocean/godo"
	"github.com/spf13/viper"
	"golang.org/x/oauth2"
)

const (
	// LatestReleaseURL is the latest release URL endpoint.
	LatestReleaseURL = "https://api.github.com/repos/digitalocean/doctl/releases/latest"
)

// Version is the version info for doit.
type Version struct {
	Major, Minor, Patch int
	Name, Build, Label  string
}

var (
	// Build, Major, Minor, Patch and Label are set at build time
	Build, Major, Minor, Patch, Label string

	// DoitVersion is doctl's version.
	DoitVersion Version

	// TraceHTTP traces http connections.
	TraceHTTP bool
)

func init() {
	if Build != "" {
		DoitVersion.Build = Build
	}
	if Major != "" {
		i, _ := strconv.Atoi(Major)
		DoitVersion.Major = i
	}
	if Minor != "" {
		i, _ := strconv.Atoi(Minor)
		DoitVersion.Minor = i
	}
	if Patch != "" {
		i, _ := strconv.Atoi(Patch)
		DoitVersion.Patch = i
	}
	if Label == "" {
		DoitVersion.Label = "dev"
	} else {
		DoitVersion.Label = Label
	}
}

func (v Version) String() string {
	var buffer bytes.Buffer
	buffer.WriteString(fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch))
	if v.Label != "" {
		buffer.WriteString("-" + v.Label)
	}

	return buffer.String()
}

// Complete is the complete version for doit.
func (v Version) Complete(lv LatestVersioner) string {
	var buffer bytes.Buffer
	buffer.WriteString(fmt.Sprintf("doctl version %s", v.String()))

	if v.Build != "" {
		buffer.WriteString(fmt.Sprintf("\nGit commit hash: %s", v.Build))
	}

	if tagName, err := lv.LatestVersion(); err == nil {
		v0, err1 := semver.Make(tagName)
		v1, err2 := semver.Make(v.String())

		if len(v0.Build) == 0 {
			v0, err1 = semver.Make(tagName + "-release")
		}

		if err1 == nil && err2 == nil && v0.GT(v1) {
			buffer.WriteString(fmt.Sprintf("\nrelease %s is available, check it out! ", tagName))
		}
	}

	return buffer.String()
}

// LatestVersioner an interface for detecting the latest version.
type LatestVersioner interface {
	LatestVersion() (string, error)
}

// GithubLatestVersioner retrieves the latest version from Github.
type GithubLatestVersioner struct{}

var _ LatestVersioner = &GithubLatestVersioner{}

// LatestVersion retrieves the latest version from Github or returns
// an error.
func (glv *GithubLatestVersioner) LatestVersion() (string, error) {
	u := LatestReleaseURL
	res, err := http.Get(u)
	if err != nil {
		return "", err
	}

	defer res.Body.Close()

	var m map[string]interface{}
	if err = json.NewDecoder(res.Body).Decode(&m); err != nil {
		return "", err
	}

	tn, ok := m["tag_name"]
	if !ok {
		return "", errors.New("could not find tag name in response")
	}

	tagName := tn.(string)
	return strings.TrimPrefix(tagName, "v"), nil
}

// Config is an interface that represent doit's config.
type Config interface {
	GetGodoClient(trace bool, accessToken string) (*godo.Client, error)
	SSH(user, host, keyPath string, port int, opts ssh.Options) runner.Runner
	Set(ns, key string, val interface{})
	IsSet(key string) bool
	GetString(ns, key string) (string, error)
	GetBool(ns, key string) (bool, error)
	GetBoolPtr(ns, key string) (*bool, error)
	GetInt(ns, key string) (int, error)
	GetIntPtr(ns, key string) (*int, error)
	GetStringSlice(ns, key string) ([]string, error)
}

// LiveConfig is an implementation of Config for live values.
type LiveConfig struct {
	cliArgs map[string]bool
}

var _ Config = &LiveConfig{}

// GetGodoClient returns a GodoClient.
func (c *LiveConfig) GetGodoClient(trace bool, accessToken string) (*godo.Client, error) {
	if accessToken == "" {
		return nil, fmt.Errorf("access token is required. (hint: run 'doctl auth init')")
	}

	tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: accessToken})
	oauthClient := oauth2.NewClient(context.Background(), tokenSource)

	if trace {
		r := newRecorder(oauthClient.Transport)

		go func() {
			for {
				select {
				case msg := <-r.req:
					log.Println("->", strconv.Quote(msg))
				case msg := <-r.resp:
					log.Println("<-", strconv.Quote(msg))
				}
			}
		}()

		oauthClient.Transport = r
	}

	args := []godo.ClientOpt{godo.SetUserAgent(userAgent())}

	apiURL := viper.GetString("api-url")
	if apiURL != "" {
		args = append(args, godo.SetBaseURL(apiURL))
	}

	return godo.New(oauthClient, args...)
}

func userAgent() string {
	return "doctl/" + DoitVersion.String()
}

// SSH creates a ssh connection to a host.
func (c *LiveConfig) SSH(user, host, keyPath string, port int, opts ssh.Options) runner.Runner {
	return &ssh.Runner{
		User:            user,
		Host:            host,
		KeyPath:         keyPath,
		Port:            port,
		AgentForwarding: opts[ArgsSSHAgentForwarding].(bool),
		Command:         opts[ArgSSHCommand].(string),
	}
}

// Set sets a config key.
func (c *LiveConfig) Set(ns, key string, val interface{}) {
	viper.Set(nskey(ns, key), val)
}

func (c *LiveConfig) IsSet(key string) bool {
	matches := regexp.MustCompile("\b*--([a-z-_]+)").FindAllStringSubmatch(strings.Join(os.Args, " "), -1)
	if len(matches) == 0 {
		return false
	}

	if len(c.cliArgs) == 0 {
		args := make(map[string]bool)
		for _, match := range matches {
			args[match[1]] = true
		}
		c.cliArgs = args
	}
	return c.cliArgs[key]
}

// GetString returns a config value as a string.
func (c *LiveConfig) GetString(ns, key string) (string, error) {
	nskey := nskey(ns, key)
	str := viper.GetString(nskey)

	if isRequired(nskey) && strings.TrimSpace(str) == "" {
		return "", NewMissingArgsErr(nskey)
	}
	return str, nil
}

// GetBool returns a config value as a bool.
func (c *LiveConfig) GetBool(ns, key string) (bool, error) {
	return viper.GetBool(nskey(ns, key)), nil
}

// GetBoolPtr returns a config value as a bool pointer.
func (c *LiveConfig) GetBoolPtr(ns, key string) (*bool, error) {
	if !c.IsSet(key) {
		return nil, nil
	}
	val := viper.GetBool(nskey(ns, key))
	return &val, nil
}

// GetInt returns a config value as an int.
func (c *LiveConfig) GetInt(ns, key string) (int, error) {
	nskey := nskey(ns, key)
	val := viper.GetInt(nskey)

	if isRequired(nskey) && val == 0 {
		return 0, NewMissingArgsErr(nskey)
	}
	return val, nil
}

// GetIntPtr returns a config value as an int pointer.
func (c *LiveConfig) GetIntPtr(ns, key string) (*int, error) {
	nskey := nskey(ns, key)

	if !c.IsSet(key) {
		if isRequired(nskey) {
			return nil, NewMissingArgsErr(nskey)
		}
		return nil, nil
	}
	val := viper.GetInt(nskey)
	return &val, nil
}

// GetStringSlice returns a config value as a string slice.
func (c *LiveConfig) GetStringSlice(ns, key string) ([]string, error) {
	nskey := nskey(ns, key)
	val := viper.GetStringSlice(nskey)

	if isRequired(nskey) && emptyStringSlice(val) {
		return nil, NewMissingArgsErr(nskey)
	}

	out := []string{}
	for _, item := range viper.GetStringSlice(nskey) {
		item = strings.TrimPrefix(item, "[")
		item = strings.TrimSuffix(item, "]")

		list := strings.Split(item, ",")
		for _, str := range list {
			if str == "" {
				continue
			}
			out = append(out, str)
		}
	}
	return out, nil
}

func nskey(ns, key string) string {
	return fmt.Sprintf("%s.%s", ns, key)
}

func isRequired(key string) bool {
	return viper.GetBool(fmt.Sprintf("required.%s", key))
}

// TestConfig is an implementation of Config for testing.
type TestConfig struct {
	SSHFn    func(user, host, keyPath string, port int, opts ssh.Options) runner.Runner
	v        *viper.Viper
	IsSetMap map[string]bool
}

var _ Config = &TestConfig{}

// NewTestConfig creates a new, ready-to-use instance of a TestConfig.
func NewTestConfig() *TestConfig {
	return &TestConfig{
		SSHFn: func(u, h, kp string, p int, opts ssh.Options) runner.Runner {
			return &MockRunner{}
		},
		v:        viper.New(),
		IsSetMap: make(map[string]bool),
	}
}

// GetGodoClient mocks a GetGodoClient call. The returned godo client will
// be nil.
func (c *TestConfig) GetGodoClient(trace bool, accessToken string) (*godo.Client, error) {
	return &godo.Client{}, nil
}

// SSH returns a mock SSH runner.
func (c *TestConfig) SSH(user, host, keyPath string, port int, opts ssh.Options) runner.Runner {
	return c.SSHFn(user, host, keyPath, port, opts)
}

// Set sets a config key.
func (c *TestConfig) Set(ns, key string, val interface{}) {
	nskey := fmt.Sprintf("%s-%s", ns, key)
	c.v.Set(nskey, val)
	c.IsSetMap[key] = true
}

// IsSet returns true if the given key is set on the config.
func (c *TestConfig) IsSet(key string) bool {
	return c.IsSetMap[key]
}

// GetString returns the string value for the key in the given namespace. Because
// this is a mock implementation, and error will never be returned.
func (c *TestConfig) GetString(ns, key string) (string, error) {
	nskey := fmt.Sprintf("%s-%s", ns, key)
	return c.v.GetString(nskey), nil
}

// GetInt returns the int value for the key in the given namespace. Because
// this is a mock implementation, and error will never be returned.
func (c *TestConfig) GetInt(ns, key string) (int, error) {
	nskey := fmt.Sprintf("%s-%s", ns, key)
	return c.v.GetInt(nskey), nil
}

// GetIntPtr returns the int value for the key in the given namespace. Because
// this is a mock implementation, and error will never be returned.
func (c *TestConfig) GetIntPtr(ns, key string) (*int, error) {
	nskey := fmt.Sprintf("%s-%s", ns, key)
	if !c.v.IsSet(nskey) {
		return nil, nil
	}
	val := c.v.GetInt(nskey)
	return &val, nil
}

// GetStringSlice returns the string slice value for the key in the given
// namespace. Because this is a mock implementation, and error will never be
// returned.
func (c *TestConfig) GetStringSlice(ns, key string) ([]string, error) {
	nskey := fmt.Sprintf("%s-%s", ns, key)
	return c.v.GetStringSlice(nskey), nil
}

// GetBool returns the bool value for the key in the given namespace. Because
// this is a mock implementation, and error will never be returned.
func (c *TestConfig) GetBool(ns, key string) (bool, error) {
	nskey := fmt.Sprintf("%s-%s", ns, key)
	return c.v.GetBool(nskey), nil
}

// GetBoolPtr returns the bool value for the key in the given namespace. Because
// this is a mock implementation, and error will never be returned.
func (c *TestConfig) GetBoolPtr(ns, key string) (*bool, error) {
	nskey := fmt.Sprintf("%s-%s", ns, key)
	if !c.v.IsSet(nskey) {
		return nil, nil
	}
	val := c.v.GetBool(nskey)
	return &val, nil
}

// This is needed because an empty StringSlice flag returns `["[]"]`
func emptyStringSlice(s []string) bool {
	return len(s) == 1 && s[0] == "[]"
}

// CommandName returns the name by which doctl was invoked
func CommandName() string {
	name, ok := os.LookupEnv("SNAP_NAME")
	if !ok {
		return os.Args[0]
	}
	return name
}
<|endoftext|>