<commit_before>package types

import (
	"fmt"
	"html/template"
	"reflect"
)

var stype2type = map[string]string{
	"zero":             "zero",
	"bool":             "bool",
	"string":           "string",
	"byte":             "byte",
	"rune":             "rune",
	"int":              "int",
	"int8":             "int8",
	"int16":            "int16",
	"int32":            "int32",
	"int64":            "int64",
	"uint":             "uint",
	"uint8":            "uint8",
	"uint16":           "uint16",
	"uint32":           "uint32",
	"uint64":           "uint64",
	"float32":          "float32",
	"float64":          "float64",
	"complex64":        "complex64",
	"complex128":       "complex128",
	"bools":            "[]bool",
	"strings":          "[]string",
	"bytes":            "[]byte",
	"runes":            "[]rune",
	"ints":             "[]int",
	"int8s":            "[]int8",
	"int16s":           "[]int16",
	"int32s":           "[]int32",
	"int64s":           "[]int64",
	"uints":            "[]uint",
	"uint8s":           "[]uint8",
	"uint16s":          "[]uint16",
	"uint32s":          "[]uint32",
	"uint64s":          "[]uint64",
	"float32s":         "[]float32",
	"float64s":         "[]float64",
	"complex64s":       "[]complex64",
	"complex128s":      "[]complex128",
	"string2string":    "map[string]string,",
	"string2interface": "map[string]interface{}",
	"int642interface":  "map[int64]interface{}",
	"int642string":     "map[int64]string",
	"int2string":       "map[int]string",
	"int2interface":    "map[int]interface{}",
	"uint642interface": "map[uint64]interface{}",
	"uint642string":    "map[uint64]string",
	"uint2string":      "map[uint]string",
	"uint2interface":   "map[uint]interface{}",
}

// IsZero judges whether a value is ZERO.
//
// For "", 0, 0.0, false, 0+0i, nil, and the slice, array or map that the length
// is 0, they are all ZERO. Others is not ZERO.
func IsZero(v interface{}) bool {
	ok, _ := template.IsTrue(v)
	return !ok
}

// VerifyFunc is a function to verifty whether the type of a value is the given
// type, the first argument of which is the value, and the second of which is
// the given type.
type VerifyFunc func(interface{}, string) bool

var (
	typeMap = make(map[string]VerifyFunc)
)

func init() {
	RegisterVerifyFunc("zero", verifyBasicBuiltinType)
	RegisterVerifyFunc("bool", verifyBasicBuiltinType)
	RegisterVerifyFunc("string", verifyBasicBuiltinType)
	RegisterVerifyFunc("byte", verifyBasicBuiltinType)
	RegisterVerifyFunc("rune", verifyBasicBuiltinType)
	RegisterVerifyFunc("int", verifyBasicBuiltinType)
	RegisterVerifyFunc("int8", verifyBasicBuiltinType)
	RegisterVerifyFunc("int16", verifyBasicBuiltinType)
	RegisterVerifyFunc("int32", verifyBasicBuiltinType)
	RegisterVerifyFunc("int64", verifyBasicBuiltinType)
	RegisterVerifyFunc("uint", verifyBasicBuiltinType)
	RegisterVerifyFunc("uint8", verifyBasicBuiltinType)
	RegisterVerifyFunc("uint16", verifyBasicBuiltinType)
	RegisterVerifyFunc("uint32", verifyBasicBuiltinType)
	RegisterVerifyFunc("uint64", verifyBasicBuiltinType)
	RegisterVerifyFunc("float32", verifyBasicBuiltinType)
	RegisterVerifyFunc("float64", verifyBasicBuiltinType)
	RegisterVerifyFunc("complex64", verifyBasicBuiltinType)
	RegisterVerifyFunc("complex128", verifyBasicBuiltinType)

	RegisterVerifyFunc("bools", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("strings", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("bytes", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("runes", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("ints", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("int8s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("int16s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("int32s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("int64s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("uints", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("uint8s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("uint16s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("uint32s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("uint64s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("float32s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("float64s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("complex64s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("complex128s", verifyBasicBuiltinTypeList)

	RegisterVerifyFunc("string2string", verifyMapType)
	RegisterVerifyFunc("string2interface", verifyMapType)
	RegisterVerifyFunc("int642interface", verifyMapType)
	RegisterVerifyFunc("int642string", verifyMapType)
	RegisterVerifyFunc("int2string", verifyMapType)
	RegisterVerifyFunc("int2interface", verifyMapType)
}

// RegisterVerifyFunc registers a type verification function.
func RegisterVerifyFunc(t string, f VerifyFunc) error {
	if _, ok := typeMap[t]; ok {
		return fmt.Errorf("has registered the type of %s", t)
	}
	typeMap[t] = f
	return nil
}

func verifyBasicBuiltinType(v interface{}, t string) (ok bool) {
	switch t {
	case "zero":
		return IsZero(v)
	case "bool":
		_, ok = v.(bool)
	case "string":
		_, ok = v.(string)
	case "int":
		_, ok = v.(int)
	case "int8":
		_, ok = v.(int8)
	case "int16":
		_, ok = v.(int16)
	case "int32", "rune":
		_, ok = v.(int32)
	case "int64":
		_, ok = v.(int64)
	case "uint":
		_, ok = v.(uint)
	case "uint8", "byte":
		_, ok = v.(uint8)
	case "uint16":
		_, ok = v.(uint16)
	case "uint32":
		_, ok = v.(uint32)
	case "uint64":
		_, ok = v.(uint64)
	case "float32":
		_, ok = v.(float32)
	case "float64":
		_, ok = v.(float64)
	case "complex64":
		_, ok = v.(complex64)
	case "complex128":
		_, ok = v.(complex128)
	}
	return
}

func verifyBasicBuiltinTypeList(v interface{}, t string) (ok bool) {
	switch t {
	case "bools":
		_, ok = v.([]bool)
	case "strings":
		_, ok = v.([]string)
	case "ints":
		_, ok = v.([]int)
	case "int8s":
		_, ok = v.([]int8)
	case "int16s":
		_, ok = v.([]int16)
	case "int32s", "rune":
		_, ok = v.([]int32)
	case "int64s":
		_, ok = v.([]int64)
	case "uints":
		_, ok = v.([]uint)
	case "uint8s", "bytes":
		_, ok = v.([]uint8)
	case "uint16s":
		_, ok = v.([]uint16)
	case "uint32s":
		_, ok = v.([]uint32)
	case "uint64s":
		_, ok = v.([]uint64)
	case "float32s":
		_, ok = v.([]float32)
	case "float64s":
		_, ok = v.([]float64)
	case "complex64s":
		_, ok = v.([]complex64)
	case "complex128s":
		_, ok = v.([]complex128)
	}
	return
}

func verifyMapType(v interface{}, t string) (ok bool) {
	switch t {
	case "string2string":
		_, ok = v.(map[string]string)
	case "string2interface":
		_, ok = v.(map[string]interface{})
	case "int642interface":
		_, ok = v.(map[int64]interface{})
	case "int642string":
		_, ok = v.(map[int64]string)
	case "int2string":
		_, ok = v.(map[int]string)
	case "int2interface":
		_, ok = v.(map[int]interface{})
	case "uint642interface":
		_, ok = v.(map[uint64]interface{})
	case "uint642string":
		_, ok = v.(map[uint64]string)
	case "uint2string":
		_, ok = v.(map[uint]string)
	case "uint2interface":
		_, ok = v.(map[uint]interface{})
	}
	return
}

// VerifyType verifies whether the type of v is t.
//
// The supported types are below:
//
//     t(string)           Go Type / Function Call
//     -------------------------------------------
//     "zero"              IsZero(v)
//     "bool"              bool
//     "string"            string
//     "byte"              byte
//     "rune"              rune
//     "int"               int
//     "int8"              int8
//     "int16"             int16
//     "int32"             int32
//     "int64"             int64
//     "uint"              uint
//     "uint8"             uint8
//     "uint16"            uint16
//     "uint32"            uint32
//     "uint64"            uint64
//     "float32"           float32
//     "float64"           float64
//     "complex64"         complex64
//     "complex128"        complex128
//     "bools"             []bool
//     "strings"           []string
//     "bytes"             []byte
//     "runes"             []rune
//     "ints"              []int
//     "int8s"             []int8
//     "int16s"            []int16
//     "int32s"            []int32
//     "int64s"            []int64
//     "uints"             []uint
//     "uint8s"            []uint8
//     "uint16s"           []uint16
//     "uint32s"           []uint32
//     "uint64s"           []uint64
//     "float32s"          []float32
//     "float64s"          []float64
//     "complex64s"        []complex64
//     "complex128s"       []complex128
//     "string2string"     map[string]string,
//     "string2interface"  map[string]interface{}
//     "int642interface"   map[int64]interface{}
//     "int642string"      map[int64]string
//     "int2string"        map[int]string
//     "int2interface"     map[int]interface{}
//     "uint642interface"  map[uint64]interface{}
//     "uint642string"     map[uint64]string
//     "uint2string"       map[uint]string
//     "uint2interface"    map[uint]interface{}
//
// Notice: You can add the new type verification by RegisterVerifyFunc it.
func VerifyType(v interface{}, t string) bool {
	if f, ok := typeMap[t]; ok {
		return f(v, t)
	}
	panic(fmt.Errorf("Not support to verify the type of %s", t))
}

// VerifyMapValueType verifies whether the type of the value of the key
// in the map is the given type.
//
// Return false if m is not the map type or the map does not have the key.
//
// Notice: the type of the key of the map type must be string, or return false.
func VerifyMapValueType(m interface{}, k, t string) (ok bool) {
	// Optimize the types of map[string]interface{}, map[string]string,
	// and map[string]int.
	switch _m := m.(type) {
	case map[string]interface{}:
		if v, ok := _m[k]; ok {
			return VerifyType(v, t)
		}
		return
	case map[string]string:
		if v, ok := _m[k]; ok {
			return VerifyType(v, t)
		}
		return
	case map[string]int:
		if v, ok := _m[k]; ok {
			return VerifyType(v, t)
		}
		return
	}

	v := reflect.ValueOf(m)
	if v.Kind() != reflect.Map || v.Type().Key().Kind() != reflect.String {
		return
	}

	for _, _v := range v.MapKeys() {
		if _v.String() == k {
			return VerifyType(v.MapIndex(_v).Interface(), t)
		}
	}
	return
}

// VerifySliceValueType verifies whether the type of the ith value of the slice
// is the given type.
//
// Return false if the ith value of slice does not exist, that's, i>=len(slice).
// Return false if slice is not a slice or array type, too.
func VerifySliceValueType(slice interface{}, i int, t string) (ok bool) {
	// Optimize the types of []interface, []string, and []int.
	switch s := slice.(type) {
	case []interface{}:
		if i < len(s) {
			return VerifyType(s[i], t)
		}
		return
	case []string:
		if i < len(s) {
			return VerifyType(s[i], t)
		}
		return
	case []int:
		if i < len(s) {
			return VerifyType(s[i], t)
		}
		return
	}

	v := reflect.ValueOf(slice)
	kind := v.Kind()
	if kind != reflect.Slice && kind != reflect.Array && i >= v.Len() {
		return
	}
	return VerifyType(v.Index(i), t)
}
<commit_msg>Fix a bug about type name error<commit_after>package types

import (
	"fmt"
	"html/template"
	"reflect"
)

var stype2type = map[string]string{
	"zero":             "zero",
	"bool":             "bool",
	"string":           "string",
	"byte":             "byte",
	"rune":             "rune",
	"int":              "int",
	"int8":             "int8",
	"int16":            "int16",
	"int32":            "int32",
	"int64":            "int64",
	"uint":             "uint",
	"uint8":            "uint8",
	"uint16":           "uint16",
	"uint32":           "uint32",
	"uint64":           "uint64",
	"float32":          "float32",
	"float64":          "float64",
	"complex64":        "complex64",
	"complex128":       "complex128",
	"bools":            "[]bool",
	"strings":          "[]string",
	"bytes":            "[]byte",
	"runes":            "[]rune",
	"ints":             "[]int",
	"int8s":            "[]int8",
	"int16s":           "[]int16",
	"int32s":           "[]int32",
	"int64s":           "[]int64",
	"uints":            "[]uint",
	"uint8s":           "[]uint8",
	"uint16s":          "[]uint16",
	"uint32s":          "[]uint32",
	"uint64s":          "[]uint64",
	"float32s":         "[]float32",
	"float64s":         "[]float64",
	"complex64s":       "[]complex64",
	"complex128s":      "[]complex128",
	"string2string":    "map[string]string",
	"string2interface": "map[string]interface{}",
	"int642interface":  "map[int64]interface{}",
	"int642string":     "map[int64]string",
	"int2string":       "map[int]string",
	"int2interface":    "map[int]interface{}",
	"uint642interface": "map[uint64]interface{}",
	"uint642string":    "map[uint64]string",
	"uint2string":      "map[uint]string",
	"uint2interface":   "map[uint]interface{}",
}

// IsZero judges whether a value is ZERO.
//
// For "", 0, 0.0, false, 0+0i, nil, and the slice, array or map that the length
// is 0, they are all ZERO. Others is not ZERO.
func IsZero(v interface{}) bool {
	ok, _ := template.IsTrue(v)
	return !ok
}

// VerifyFunc is a function to verifty whether the type of a value is the given
// type, the first argument of which is the value, and the second of which is
// the given type.
type VerifyFunc func(interface{}, string) bool

var (
	typeMap = make(map[string]VerifyFunc)
)

func init() {
	RegisterVerifyFunc("zero", verifyBasicBuiltinType)
	RegisterVerifyFunc("bool", verifyBasicBuiltinType)
	RegisterVerifyFunc("string", verifyBasicBuiltinType)
	RegisterVerifyFunc("byte", verifyBasicBuiltinType)
	RegisterVerifyFunc("rune", verifyBasicBuiltinType)
	RegisterVerifyFunc("int", verifyBasicBuiltinType)
	RegisterVerifyFunc("int8", verifyBasicBuiltinType)
	RegisterVerifyFunc("int16", verifyBasicBuiltinType)
	RegisterVerifyFunc("int32", verifyBasicBuiltinType)
	RegisterVerifyFunc("int64", verifyBasicBuiltinType)
	RegisterVerifyFunc("uint", verifyBasicBuiltinType)
	RegisterVerifyFunc("uint8", verifyBasicBuiltinType)
	RegisterVerifyFunc("uint16", verifyBasicBuiltinType)
	RegisterVerifyFunc("uint32", verifyBasicBuiltinType)
	RegisterVerifyFunc("uint64", verifyBasicBuiltinType)
	RegisterVerifyFunc("float32", verifyBasicBuiltinType)
	RegisterVerifyFunc("float64", verifyBasicBuiltinType)
	RegisterVerifyFunc("complex64", verifyBasicBuiltinType)
	RegisterVerifyFunc("complex128", verifyBasicBuiltinType)

	RegisterVerifyFunc("bools", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("strings", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("bytes", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("runes", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("ints", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("int8s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("int16s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("int32s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("int64s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("uints", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("uint8s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("uint16s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("uint32s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("uint64s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("float32s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("float64s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("complex64s", verifyBasicBuiltinTypeList)
	RegisterVerifyFunc("complex128s", verifyBasicBuiltinTypeList)

	RegisterVerifyFunc("string2string", verifyMapType)
	RegisterVerifyFunc("string2interface", verifyMapType)
	RegisterVerifyFunc("int642interface", verifyMapType)
	RegisterVerifyFunc("int642string", verifyMapType)
	RegisterVerifyFunc("int2string", verifyMapType)
	RegisterVerifyFunc("int2interface", verifyMapType)
}

// RegisterVerifyFunc registers a type verification function.
func RegisterVerifyFunc(t string, f VerifyFunc) error {
	if _, ok := typeMap[t]; ok {
		return fmt.Errorf("has registered the type of %s", t)
	}
	typeMap[t] = f
	return nil
}

func verifyBasicBuiltinType(v interface{}, t string) (ok bool) {
	switch t {
	case "zero":
		return IsZero(v)
	case "bool":
		_, ok = v.(bool)
	case "string":
		_, ok = v.(string)
	case "int":
		_, ok = v.(int)
	case "int8":
		_, ok = v.(int8)
	case "int16":
		_, ok = v.(int16)
	case "int32", "rune":
		_, ok = v.(int32)
	case "int64":
		_, ok = v.(int64)
	case "uint":
		_, ok = v.(uint)
	case "uint8", "byte":
		_, ok = v.(uint8)
	case "uint16":
		_, ok = v.(uint16)
	case "uint32":
		_, ok = v.(uint32)
	case "uint64":
		_, ok = v.(uint64)
	case "float32":
		_, ok = v.(float32)
	case "float64":
		_, ok = v.(float64)
	case "complex64":
		_, ok = v.(complex64)
	case "complex128":
		_, ok = v.(complex128)
	}
	return
}

func verifyBasicBuiltinTypeList(v interface{}, t string) (ok bool) {
	switch t {
	case "bools":
		_, ok = v.([]bool)
	case "strings":
		_, ok = v.([]string)
	case "ints":
		_, ok = v.([]int)
	case "int8s":
		_, ok = v.([]int8)
	case "int16s":
		_, ok = v.([]int16)
	case "int32s", "rune":
		_, ok = v.([]int32)
	case "int64s":
		_, ok = v.([]int64)
	case "uints":
		_, ok = v.([]uint)
	case "uint8s", "bytes":
		_, ok = v.([]uint8)
	case "uint16s":
		_, ok = v.([]uint16)
	case "uint32s":
		_, ok = v.([]uint32)
	case "uint64s":
		_, ok = v.([]uint64)
	case "float32s":
		_, ok = v.([]float32)
	case "float64s":
		_, ok = v.([]float64)
	case "complex64s":
		_, ok = v.([]complex64)
	case "complex128s":
		_, ok = v.([]complex128)
	}
	return
}

func verifyMapType(v interface{}, t string) (ok bool) {
	switch t {
	case "string2string":
		_, ok = v.(map[string]string)
	case "string2interface":
		_, ok = v.(map[string]interface{})
	case "int642interface":
		_, ok = v.(map[int64]interface{})
	case "int642string":
		_, ok = v.(map[int64]string)
	case "int2string":
		_, ok = v.(map[int]string)
	case "int2interface":
		_, ok = v.(map[int]interface{})
	case "uint642interface":
		_, ok = v.(map[uint64]interface{})
	case "uint642string":
		_, ok = v.(map[uint64]string)
	case "uint2string":
		_, ok = v.(map[uint]string)
	case "uint2interface":
		_, ok = v.(map[uint]interface{})
	}
	return
}

// VerifyType verifies whether the type of v is t.
//
// The supported types are below:
//
//     t(string)           Go Type / Function Call
//     -------------------------------------------
//     "zero"              IsZero(v)
//     "bool"              bool
//     "string"            string
//     "byte"              byte
//     "rune"              rune
//     "int"               int
//     "int8"              int8
//     "int16"             int16
//     "int32"             int32
//     "int64"             int64
//     "uint"              uint
//     "uint8"             uint8
//     "uint16"            uint16
//     "uint32"            uint32
//     "uint64"            uint64
//     "float32"           float32
//     "float64"           float64
//     "complex64"         complex64
//     "complex128"        complex128
//     "bools"             []bool
//     "strings"           []string
//     "bytes"             []byte
//     "runes"             []rune
//     "ints"              []int
//     "int8s"             []int8
//     "int16s"            []int16
//     "int32s"            []int32
//     "int64s"            []int64
//     "uints"             []uint
//     "uint8s"            []uint8
//     "uint16s"           []uint16
//     "uint32s"           []uint32
//     "uint64s"           []uint64
//     "float32s"          []float32
//     "float64s"          []float64
//     "complex64s"        []complex64
//     "complex128s"       []complex128
//     "string2string"     map[string]string,
//     "string2interface"  map[string]interface{}
//     "int642interface"   map[int64]interface{}
//     "int642string"      map[int64]string
//     "int2string"        map[int]string
//     "int2interface"     map[int]interface{}
//     "uint642interface"  map[uint64]interface{}
//     "uint642string"     map[uint64]string
//     "uint2string"       map[uint]string
//     "uint2interface"    map[uint]interface{}
//
// Notice: You can add the new type verification by RegisterVerifyFunc it.
func VerifyType(v interface{}, t string) bool {
	if f, ok := typeMap[t]; ok {
		return f(v, t)
	}
	panic(fmt.Errorf("Not support to verify the type of %s", t))
}

// VerifyMapValueType verifies whether the type of the value of the key
// in the map is the given type.
//
// Return false if m is not the map type or the map does not have the key.
//
// Notice: the type of the key of the map type must be string, or return false.
func VerifyMapValueType(m interface{}, k, t string) (ok bool) {
	// Optimize the types of map[string]interface{}, map[string]string,
	// and map[string]int.
	switch _m := m.(type) {
	case map[string]interface{}:
		if v, ok := _m[k]; ok {
			return VerifyType(v, t)
		}
		return
	case map[string]string:
		if v, ok := _m[k]; ok {
			return VerifyType(v, t)
		}
		return
	case map[string]int:
		if v, ok := _m[k]; ok {
			return VerifyType(v, t)
		}
		return
	}

	v := reflect.ValueOf(m)
	if v.Kind() != reflect.Map || v.Type().Key().Kind() != reflect.String {
		return
	}

	for _, _v := range v.MapKeys() {
		if _v.String() == k {
			return VerifyType(v.MapIndex(_v).Interface(), t)
		}
	}
	return
}

// VerifySliceValueType verifies whether the type of the ith value of the slice
// is the given type.
//
// Return false if the ith value of slice does not exist, that's, i>=len(slice).
// Return false if slice is not a slice or array type, too.
func VerifySliceValueType(slice interface{}, i int, t string) (ok bool) {
	// Optimize the types of []interface, []string, and []int.
	switch s := slice.(type) {
	case []interface{}:
		if i < len(s) {
			return VerifyType(s[i], t)
		}
		return
	case []string:
		if i < len(s) {
			return VerifyType(s[i], t)
		}
		return
	case []int:
		if i < len(s) {
			return VerifyType(s[i], t)
		}
		return
	}

	v := reflect.ValueOf(slice)
	kind := v.Kind()
	if kind != reflect.Slice && kind != reflect.Array && i >= v.Len() {
		return
	}
	return VerifyType(v.Index(i), t)
}
<|endoftext|>


<commit_before>package iso

import (
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"testing"

	"github.com/hashicorp/packer/packer"
)

func testConfig() map[string]interface{} {
	return map[string]interface{}{
		"iso_checksum":      "foo",
		"iso_checksum_type": "md5",
		"iso_url":           "http://www.packer.io",
		"shutdown_command":  "foo",
		"ssh_username":      "foo",

		packer.BuildNameConfigKey: "foo",
	}
}

func TestBuilder_ImplementsBuilder(t *testing.T) {
	var raw interface{}
	raw = &Builder{}
	if _, ok := raw.(packer.Builder); !ok {
		t.Error("Builder must implement builder.")
	}
}

func TestBuilderPrepare_Defaults(t *testing.T) {
	var b Builder
	config := testConfig()
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	if b.config.DiskName != "disk" {
		t.Errorf("bad disk name: %s", b.config.DiskName)
	}

	if b.config.OutputDir != "output-foo" {
		t.Errorf("bad output dir: %s", b.config.OutputDir)
	}

	if b.config.Version != "9" {
		t.Errorf("bad Version: %s", b.config.Version)
	}

	if b.config.VMName != "packer-foo" {
		t.Errorf("bad vm name: %s", b.config.VMName)
	}
}

func TestBuilderPrepare_DiskSize(t *testing.T) {
	var b Builder
	config := testConfig()

	delete(config, "disk_size")
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("bad err: %s", err)
	}

	if b.config.DiskSize != 40000 {
		t.Fatalf("bad size: %d", b.config.DiskSize)
	}

	config["disk_size"] = 60000
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	if b.config.DiskSize != 60000 {
		t.Fatalf("bad size: %d", b.config.DiskSize)
	}
}

func TestBuilderPrepare_FloppyFiles(t *testing.T) {
	var b Builder
	config := testConfig()

	delete(config, "floppy_files")
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("bad err: %s", err)
	}

	if len(b.config.FloppyFiles) != 0 {
		t.Fatalf("bad: %#v", b.config.FloppyFiles)
	}

	floppies_path := "../../../common/test-fixtures/floppies"
	config["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)}
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	expected := []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)}
	if !reflect.DeepEqual(b.config.FloppyFiles, expected) {
		t.Fatalf("bad: %#v", b.config.FloppyFiles)
	}
}

func TestBuilderPrepare_InvalidFloppies(t *testing.T) {
	var b Builder
	config := testConfig()
	config["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"}
	b = Builder{}
	_, errs := b.Prepare(config)
	if errs == nil {
		t.Fatalf("Nonexistent floppies should trigger multierror")
	}

	if len(errs.(*packer.MultiError).Errors) != 2 {
		t.Fatalf("Multierror should work and report 2 errors")
	}
}

func TestBuilderPrepare_RemoteType(t *testing.T) {
	var b Builder
	config := testConfig()

	config["format"] = "ovf"
	config["remote_host"] = "foobar.example.com"
	// Bad
	config["remote_type"] = "foobar"
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	config["remote_host"] = ""
	config["remote_type"] = ""
	// Bad
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Good
	config["remote_type"] = "esx5"
	config["remote_host"] = "foobar.example.com"
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestBuilderPrepare_Format(t *testing.T) {
	var b Builder
	config := testConfig()

	// Bad
	config["format"] = "foobar"
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	goodFormats := []string{"ova", "ovf", "vmx"}

	for _, format := range goodFormats {
		// Good
		config["format"] = format
		b = Builder{}
		warns, err = b.Prepare(config)
		if len(warns) > 0 {
			t.Fatalf("bad: %#v", warns)
		}
		if err != nil {
			t.Fatalf("should not have error: %s", err)
		}
	}
}

func TestBuilderPrepare_InvalidKey(t *testing.T) {
	var b Builder
	config := testConfig()

	// Add a random key
	config["i_should_not_be_valid"] = true
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}
}

func TestBuilderPrepare_OutputDir(t *testing.T) {
	var b Builder
	config := testConfig()

	// Test with existing dir
	dir, err := ioutil.TempDir("", "packer")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.RemoveAll(dir)

	config["output_directory"] = dir
	b = Builder{}
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Test with a good one
	config["output_directory"] = "i-hope-i-dont-exist"
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestBuilderPrepare_ToolsUploadPath(t *testing.T) {
	var b Builder
	config := testConfig()

	// Test a default
	delete(config, "tools_upload_path")
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	if b.config.ToolsUploadPath == "" {
		t.Fatalf("bad value: %s", b.config.ToolsUploadPath)
	}

	// Test with a bad value
	config["tools_upload_path"] = "{{{nope}"
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Test with a good one
	config["tools_upload_path"] = "hey"
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestBuilderPrepare_VMXTemplatePath(t *testing.T) {
	var b Builder
	config := testConfig()

	// Test bad
	config["vmx_template_path"] = "/i/dont/exist/forreal"
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Test good
	tf, err := ioutil.TempFile("", "packer")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.Remove(tf.Name())
	defer tf.Close()

	if _, err := tf.Write([]byte("HELLO!")); err != nil {
		t.Fatalf("err: %s", err)
	}

	config["vmx_template_path"] = tf.Name()
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	// Bad template
	tf2, err := ioutil.TempFile("", "packer")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.Remove(tf2.Name())
	defer tf2.Close()

	if _, err := tf2.Write([]byte("{{foo}")); err != nil {
		t.Fatalf("err: %s", err)
	}

	config["vmx_template_path"] = tf2.Name()
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}
}

func TestBuilderPrepare_VNCPort(t *testing.T) {
	var b Builder
	config := testConfig()

	// Bad
	config["vnc_port_min"] = 1000
	config["vnc_port_max"] = 500
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Bad
	config["vnc_port_min"] = -500
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Good
	config["vnc_port_min"] = 500
	config["vnc_port_max"] = 1000
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestBuilderPrepare_CommConfig(t *testing.T) {
	// Test Winrm
	{
		config := testConfig()
		config["communicator"] = "winrm"
		config["winrm_username"] = "username"
		config["winrm_password"] = "password"
		config["winrm_host"] = "1.2.3.4"

		var b Builder
		warns, err := b.Prepare(config)
		if len(warns) > 0 {
			t.Fatalf("bad: %#v", warns)
		}
		if err != nil {
			t.Fatalf("should not have error: %s", err)
		}

		if b.config.CommConfig.WinRMUser != "username" {
			t.Errorf("bad winrm_username: %s", b.config.CommConfig.WinRMUser)
		}
		if b.config.CommConfig.WinRMPassword != "password" {
			t.Errorf("bad winrm_password: %s", b.config.CommConfig.WinRMPassword)
		}
		if host := b.config.CommConfig.Host(); host != "1.2.3.4" {
			t.Errorf("bad host: %s", host)
		}
	}

	// Test SSH
	{
		config := testConfig()
		config["communicator"] = "ssh"
		config["ssh_username"] = "username"
		config["ssh_password"] = "password"
		config["ssh_host"] = "1.2.3.4"

		var b Builder
		warns, err := b.Prepare(config)
		if len(warns) > 0 {
			t.Fatalf("bad: %#v", warns)
		}
		if err != nil {
			t.Fatalf("should not have error: %s", err)
		}

		if b.config.CommConfig.SSHUsername != "username" {
			t.Errorf("bad ssh_username: %s", b.config.CommConfig.SSHUsername)
		}
		if b.config.CommConfig.SSHPassword != "password" {
			t.Errorf("bad ssh_password: %s", b.config.CommConfig.SSHPassword)
		}
		if host := b.config.CommConfig.Host(); host != "1.2.3.4" {
			t.Errorf("bad host: %s", host)
		}
	}
}
<commit_msg>Fix existing tests as they were not doing what they should have been<commit_after>package iso

import (
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"testing"

	"github.com/hashicorp/packer/packer"
)

func testConfig() map[string]interface{} {
	return map[string]interface{}{
		"iso_checksum":      "foo",
		"iso_checksum_type": "md5",
		"iso_url":           "http://www.packer.io",
		"shutdown_command":  "foo",
		"ssh_username":      "foo",

		packer.BuildNameConfigKey: "foo",
	}
}

func TestBuilder_ImplementsBuilder(t *testing.T) {
	var raw interface{}
	raw = &Builder{}
	if _, ok := raw.(packer.Builder); !ok {
		t.Error("Builder must implement builder.")
	}
}

func TestBuilderPrepare_Defaults(t *testing.T) {
	var b Builder
	config := testConfig()
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	if b.config.DiskName != "disk" {
		t.Errorf("bad disk name: %s", b.config.DiskName)
	}

	if b.config.OutputDir != "output-foo" {
		t.Errorf("bad output dir: %s", b.config.OutputDir)
	}

	if b.config.Version != "9" {
		t.Errorf("bad Version: %s", b.config.Version)
	}

	if b.config.VMName != "packer-foo" {
		t.Errorf("bad vm name: %s", b.config.VMName)
	}
}

func TestBuilderPrepare_DiskSize(t *testing.T) {
	var b Builder
	config := testConfig()

	delete(config, "disk_size")
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("bad err: %s", err)
	}

	if b.config.DiskSize != 40000 {
		t.Fatalf("bad size: %d", b.config.DiskSize)
	}

	config["disk_size"] = 60000
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	if b.config.DiskSize != 60000 {
		t.Fatalf("bad size: %d", b.config.DiskSize)
	}
}

func TestBuilderPrepare_FloppyFiles(t *testing.T) {
	var b Builder
	config := testConfig()

	delete(config, "floppy_files")
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("bad err: %s", err)
	}

	if len(b.config.FloppyFiles) != 0 {
		t.Fatalf("bad: %#v", b.config.FloppyFiles)
	}

	floppies_path := "../../../common/test-fixtures/floppies"
	config["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)}
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	expected := []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)}
	if !reflect.DeepEqual(b.config.FloppyFiles, expected) {
		t.Fatalf("bad: %#v", b.config.FloppyFiles)
	}
}

func TestBuilderPrepare_InvalidFloppies(t *testing.T) {
	var b Builder
	config := testConfig()
	config["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"}
	b = Builder{}
	_, errs := b.Prepare(config)
	if errs == nil {
		t.Fatalf("Nonexistent floppies should trigger multierror")
	}

	if len(errs.(*packer.MultiError).Errors) != 2 {
		t.Fatalf("Multierror should work and report 2 errors")
	}
}

func TestBuilderPrepare_RemoteType(t *testing.T) {
	var b Builder
	config := testConfig()

	config["format"] = "ovf"
	config["remote_host"] = "foobar.example.com"
	config["remote_password"] = "supersecret"
	// Bad
	config["remote_type"] = "foobar"
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	config["remote_type"] = "esx5"
	// Bad
	config["remote_host"] = ""
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Good
	config["remote_type"] = "esx5"
	config["remote_host"] = "foobar.example.com"
	config["remote_password"] = "supersecret"
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestBuilderPrepare_Format(t *testing.T) {
	var b Builder
	config := testConfig()

	// Bad
	config["format"] = "foobar"
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	goodFormats := []string{"ova", "ovf", "vmx"}

	for _, format := range goodFormats {
		// Good
		config["format"] = format
		b = Builder{}
		warns, err = b.Prepare(config)
		if len(warns) > 0 {
			t.Fatalf("bad: %#v", warns)
		}
		if err != nil {
			t.Fatalf("should not have error: %s", err)
		}
	}
}

func TestBuilderPrepare_InvalidKey(t *testing.T) {
	var b Builder
	config := testConfig()

	// Add a random key
	config["i_should_not_be_valid"] = true
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}
}

func TestBuilderPrepare_OutputDir(t *testing.T) {
	var b Builder
	config := testConfig()

	// Test with existing dir
	dir, err := ioutil.TempDir("", "packer")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.RemoveAll(dir)

	config["output_directory"] = dir
	b = Builder{}
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Test with a good one
	config["output_directory"] = "i-hope-i-dont-exist"
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestBuilderPrepare_ToolsUploadPath(t *testing.T) {
	var b Builder
	config := testConfig()

	// Test a default
	delete(config, "tools_upload_path")
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	if b.config.ToolsUploadPath == "" {
		t.Fatalf("bad value: %s", b.config.ToolsUploadPath)
	}

	// Test with a bad value
	config["tools_upload_path"] = "{{{nope}"
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Test with a good one
	config["tools_upload_path"] = "hey"
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestBuilderPrepare_VMXTemplatePath(t *testing.T) {
	var b Builder
	config := testConfig()

	// Test bad
	config["vmx_template_path"] = "/i/dont/exist/forreal"
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Test good
	tf, err := ioutil.TempFile("", "packer")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.Remove(tf.Name())
	defer tf.Close()

	if _, err := tf.Write([]byte("HELLO!")); err != nil {
		t.Fatalf("err: %s", err)
	}

	config["vmx_template_path"] = tf.Name()
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	// Bad template
	tf2, err := ioutil.TempFile("", "packer")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	defer os.Remove(tf2.Name())
	defer tf2.Close()

	if _, err := tf2.Write([]byte("{{foo}")); err != nil {
		t.Fatalf("err: %s", err)
	}

	config["vmx_template_path"] = tf2.Name()
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}
}

func TestBuilderPrepare_VNCPort(t *testing.T) {
	var b Builder
	config := testConfig()

	// Bad
	config["vnc_port_min"] = 1000
	config["vnc_port_max"] = 500
	warns, err := b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Bad
	config["vnc_port_min"] = -500
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err == nil {
		t.Fatal("should have error")
	}

	// Good
	config["vnc_port_min"] = 500
	config["vnc_port_max"] = 1000
	b = Builder{}
	warns, err = b.Prepare(config)
	if len(warns) > 0 {
		t.Fatalf("bad: %#v", warns)
	}
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestBuilderPrepare_CommConfig(t *testing.T) {
	// Test Winrm
	{
		config := testConfig()
		config["communicator"] = "winrm"
		config["winrm_username"] = "username"
		config["winrm_password"] = "password"
		config["winrm_host"] = "1.2.3.4"

		var b Builder
		warns, err := b.Prepare(config)
		if len(warns) > 0 {
			t.Fatalf("bad: %#v", warns)
		}
		if err != nil {
			t.Fatalf("should not have error: %s", err)
		}

		if b.config.CommConfig.WinRMUser != "username" {
			t.Errorf("bad winrm_username: %s", b.config.CommConfig.WinRMUser)
		}
		if b.config.CommConfig.WinRMPassword != "password" {
			t.Errorf("bad winrm_password: %s", b.config.CommConfig.WinRMPassword)
		}
		if host := b.config.CommConfig.Host(); host != "1.2.3.4" {
			t.Errorf("bad host: %s", host)
		}
	}

	// Test SSH
	{
		config := testConfig()
		config["communicator"] = "ssh"
		config["ssh_username"] = "username"
		config["ssh_password"] = "password"
		config["ssh_host"] = "1.2.3.4"

		var b Builder
		warns, err := b.Prepare(config)
		if len(warns) > 0 {
			t.Fatalf("bad: %#v", warns)
		}
		if err != nil {
			t.Fatalf("should not have error: %s", err)
		}

		if b.config.CommConfig.SSHUsername != "username" {
			t.Errorf("bad ssh_username: %s", b.config.CommConfig.SSHUsername)
		}
		if b.config.CommConfig.SSHPassword != "password" {
			t.Errorf("bad ssh_password: %s", b.config.CommConfig.SSHPassword)
		}
		if host := b.config.CommConfig.Host(); host != "1.2.3.4" {
			t.Errorf("bad host: %s", host)
		}
	}
}
<|endoftext|>


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

// +build go1.3

package lxdclient

import (
	"os"
	"path"
	"path/filepath"

	"github.com/juju/errors"
	"github.com/lxc/lxd"
)

// The LXD repo does not expose any consts for these values.
// TODO(ericsnow) Expose these consts in the LXD repo?
const (
	// see https://github.com/lxc/lxd/blob/master/config.go
	configDefaultFile = "config.yml"
	// see https://github.com/lxc/lxd/blob/master/client.go (readMyCert)
	configCertFile = "client.crt"
	configKeyFile  = "client.key"
)

// Config contains the config values used for a connection to the LXD API.
type Config struct {
	// Namespace identifies the namespace to associate with containers
	// and other resources with which the client interacts. If may be
	// blank.
	Namespace string

	// Dirname identifies where the client will find config files.
	// default: "$HOME/.config/lxc"
	Dirname string

	// Remote identifies the remote server to which the client should
	// connect. For the default "remote" use Local.
	Remote Remote
}

// JujuConfigDir returns the full path to Juju LXC config directory.
func JujuConfigDir(namespace string) string {
	return filepath.Join(os.ExpandEnv(lxd.ConfigDir), namespace)
}

// WithDefaults updates a copy of the config with default values
// where needed.
func (cfg Config) WithDefaults() (Config, error) {
	// We leave a blank namespace alone.
	// Also, note that cfg is a value receiver, so it is an implicit copy.

	if cfg.Dirname == "" {
		// TODO(ericsnow) Switch to filepath as soon as LXD does.
		dirname := path.Dir(lxd.ConfigPath("DUMMY"))
		cfg.Dirname = path.Clean(dirname)
	}

	var err error
	cfg.Remote, err = cfg.Remote.WithDefaults()
	if err != nil {
		return cfg, errors.Trace(err)
	}

	return cfg, nil
}

// Validate checks the client's fields for invalid values.
func (cfg Config) Validate() error {
	// TODO(ericsnow) Check cfg.Namespace (if provided)?

	// TODO(ericsnow) Check cfg.Dirname (if provided)?

	// TODO(ericsnow) Check cfg.Filename (if provided)?

	if err := cfg.Remote.Validate(); err != nil {
		return errors.Trace(err)
	}

	return nil
}

// Write writes all the various files for this config.
func (cfg Config) Write() error {
	if err := cfg.Validate(); err != nil {
		return errors.Trace(err)
	}

	origConfigDir := updateLXDVars(cfg.Dirname)
	defer updateLXDVars(origConfigDir)

	if err := cfg.write(); err != nil {
		return errors.Trace(err)
	}

	return nil
}

// UsingTCPRemote converts the config into a "non-local" version. An
// already non-local remote is left alone.
//
// For a "local" remote (see Local), the remote is changed to a one
// with the host set to the IP address of the local lxcbr0 bridge
// interface. The LXD server is also set up for remote access, exposing
// the TCP port and adding a certificate for remote access.
func (cfg Config) UsingTCPRemote() (Config, error) {
	// Note that cfg is a value receiver, so it is an implicit copy.

	if !cfg.Remote.isLocal() {
		return cfg, nil
	}

	remote, err := cfg.Remote.UsingTCP()
	if err != nil {
		return cfg, errors.Trace(err)
	}

	// Update the server config and authorized certs.
	if err := prepareRemote(cfg, *remote.Cert); err != nil {
		return cfg, errors.Trace(err)
	}

	cfg.Remote = remote
	return cfg, nil
}

func prepareRemote(cfg Config, newCert Cert) error {
	client, err := Connect(cfg)
	if err != nil {
		return errors.Trace(err)
	}

	if err := client.SetConfig("core.https_address", "[::]"); err != nil {
		return errors.Trace(err)
	}

	if err := client.AddCert(newCert); err != nil {
		return errors.Trace(err)
	}

	return nil
}

func updateLXDVars(dirname string) string {
	// Change the hard-coded config dir that the raw client uses.
	// TODO(ericsnow) This is exactly what happens in the lxc CLI for
	// the LXD_CONF env var. Once the raw client accepts a path to the
	// config dir we can drop this line.
	// See:
	//   https://github.com/lxc/lxd/blob/master/lxc/main.go
	//   https://github.com/lxc/lxd/issues/1196
	origConfigDir := lxd.ConfigDir
	lxd.ConfigDir = dirname

	return origConfigDir
}

func initializeConfigDir(cfg Config) error {
	logger.Debugf("initializing config dir %q", cfg.Dirname)

	if err := os.MkdirAll(cfg.Dirname, 0775); err != nil {
		return errors.Trace(err)
	}

	// Force the default config to get written. on
	// default config from memory if there isn't a config file on disk.
	// So we load that and then explicitly save it to disk with a call
	// to SaveConfig().
	config, err := lxd.LoadConfig()
	if err != nil {
		return errors.Trace(err)
	}

	origConfigDir := updateLXDVars(cfg.Dirname)
	defer updateLXDVars(origConfigDir)

	if err := lxd.SaveConfig(config); err != nil {
		return errors.Trace(err)
	}

	return nil
}

func (cfg Config) write() error {
	// Ensure the initial config is set up.
	if err := initializeConfigDir(cfg); err != nil {
		return errors.Trace(err)
	}

	// Update config.yml, if necessary.
	if err := cfg.writeConfigFile(); err != nil {
		return errors.Trace(err)
	}

	// Write the cert file and key file, if applicable.
	var cert Cert
	if cfg.Remote.Cert != nil {
		cert = *cfg.Remote.Cert
	}
	if err := cert.Validate(); err != nil {
		logger.Debugf("not writing invalid/empty certificate")
	} else {
		if err := cfg.writeCertPEM(cert); err != nil {
			return errors.Trace(err)
		}
		if err := cfg.writeKeyPEM(cert); err != nil {
			return errors.Trace(err)
		}
	}

	return nil
}

func (cfg Config) writeConfigFile() error {
	filename := cfg.resolve(configDefaultFile)
	logger.Debugf("writing config file %q", filename)

	// TODO(ericsnow) Cache the low-level config in Config?
	rawCfg, err := lxd.LoadConfig()
	if err != nil {
		return errors.Trace(err)
	}

	if !cfg.Remote.isLocal() {
		// Ensure the remote is set correctly.
		remote := cfg.Remote.Name
		delete(rawCfg.Remotes, remote)
		addr := cfg.Remote.Host
		if err := addServer(rawCfg, remote, addr); err != nil {
			return errors.Trace(err)
		}
	}

	// Write out the updated config, if changed.
	// TODO(ericsnow) Check if changed.
	if err := lxd.SaveConfig(rawCfg); err != nil {
		return errors.Trace(err)
	}

	return nil
}

func (cfg Config) writeCertPEM(cert Cert) error {
	filename := cfg.resolve(configCertFile)
	logger.Debugf("writing cert PEM file %q", filename)

	file, err := os.Create(filename)
	if err != nil {
		return errors.Trace(err)
	}
	defer file.Close()

	if err := cert.WriteCertPEM(file); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func (cfg Config) writeKeyPEM(cert Cert) error {
	filename := cfg.resolve(configKeyFile)
	logger.Debugf("writing key PEM file %q", filename)

	file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return errors.Trace(err)
	}
	defer file.Close()

	if err := cert.WriteKeyPEM(file); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func (cfg Config) resolve(file string) string {
	return filepath.Join(cfg.Dirname, file)
}
<commit_msg>removing unneeded updateLXDVars<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// +build go1.3

package lxdclient

import (
	"os"
	"path"
	"path/filepath"

	"github.com/juju/errors"
	"github.com/lxc/lxd"
)

// The LXD repo does not expose any consts for these values.
// TODO(ericsnow) Expose these consts in the LXD repo?
const (
	// see https://github.com/lxc/lxd/blob/master/config.go
	configDefaultFile = "config.yml"
	// see https://github.com/lxc/lxd/blob/master/client.go (readMyCert)
	configCertFile = "client.crt"
	configKeyFile  = "client.key"
)

// Config contains the config values used for a connection to the LXD API.
type Config struct {
	// Namespace identifies the namespace to associate with containers
	// and other resources with which the client interacts. If may be
	// blank.
	Namespace string

	// Dirname identifies where the client will find config files.
	// default: "$HOME/.config/lxc"
	Dirname string

	// Remote identifies the remote server to which the client should
	// connect. For the default "remote" use Local.
	Remote Remote
}

// JujuConfigDir returns the full path to Juju LXC config directory.
func JujuConfigDir(namespace string) string {
	return filepath.Join(os.ExpandEnv(lxd.ConfigDir), namespace)
}

// WithDefaults updates a copy of the config with default values
// where needed.
func (cfg Config) WithDefaults() (Config, error) {
	// We leave a blank namespace alone.
	// Also, note that cfg is a value receiver, so it is an implicit copy.

	if cfg.Dirname == "" {
		// TODO(ericsnow) Switch to filepath as soon as LXD does.
		dirname := path.Dir(lxd.ConfigPath("DUMMY"))
		cfg.Dirname = path.Clean(dirname)
	}

	var err error
	cfg.Remote, err = cfg.Remote.WithDefaults()
	if err != nil {
		return cfg, errors.Trace(err)
	}

	return cfg, nil
}

// Validate checks the client's fields for invalid values.
func (cfg Config) Validate() error {
	// TODO(ericsnow) Check cfg.Namespace (if provided)?

	// TODO(ericsnow) Check cfg.Dirname (if provided)?

	// TODO(ericsnow) Check cfg.Filename (if provided)?

	if err := cfg.Remote.Validate(); err != nil {
		return errors.Trace(err)
	}

	return nil
}

// Write writes all the various files for this config.
func (cfg Config) Write() error {
	if err := cfg.Validate(); err != nil {
		return errors.Trace(err)
	}

	origConfigDir := updateLXDVars(cfg.Dirname)
	defer updateLXDVars(origConfigDir)

	if err := cfg.write(); err != nil {
		return errors.Trace(err)
	}

	return nil
}

// UsingTCPRemote converts the config into a "non-local" version. An
// already non-local remote is left alone.
//
// For a "local" remote (see Local), the remote is changed to a one
// with the host set to the IP address of the local lxcbr0 bridge
// interface. The LXD server is also set up for remote access, exposing
// the TCP port and adding a certificate for remote access.
func (cfg Config) UsingTCPRemote() (Config, error) {
	// Note that cfg is a value receiver, so it is an implicit copy.

	if !cfg.Remote.isLocal() {
		return cfg, nil
	}

	remote, err := cfg.Remote.UsingTCP()
	if err != nil {
		return cfg, errors.Trace(err)
	}

	// Update the server config and authorized certs.
	if err := prepareRemote(cfg, *remote.Cert); err != nil {
		return cfg, errors.Trace(err)
	}

	cfg.Remote = remote
	return cfg, nil
}

func prepareRemote(cfg Config, newCert Cert) error {
	client, err := Connect(cfg)
	if err != nil {
		return errors.Trace(err)
	}

	if err := client.SetConfig("core.https_address", "[::]"); err != nil {
		return errors.Trace(err)
	}

	if err := client.AddCert(newCert); err != nil {
		return errors.Trace(err)
	}

	return nil
}

func updateLXDVars(dirname string) string {
	// Change the hard-coded config dir that the raw client uses.
	// TODO(ericsnow) This is exactly what happens in the lxc CLI for
	// the LXD_CONF env var. Once the raw client accepts a path to the
	// config dir we can drop this line.
	// See:
	//   https://github.com/lxc/lxd/blob/master/lxc/main.go
	//   https://github.com/lxc/lxd/issues/1196
	origConfigDir := lxd.ConfigDir
	lxd.ConfigDir = dirname

	return origConfigDir
}

func initializeConfigDir(cfg Config) error {
	logger.Debugf("initializing config dir %q", cfg.Dirname)

	if err := os.MkdirAll(cfg.Dirname, 0775); err != nil {
		return errors.Trace(err)
	}

	// Force the default config to get written. on
	// default config from memory if there isn't a config file on disk.
	// So we load that and then explicitly save it to disk with a call
	// to SaveConfig().
	config, err := lxd.LoadConfig()
	if err != nil {
		return errors.Trace(err)
	}

	if err := lxd.SaveConfig(config); err != nil {
		return errors.Trace(err)
	}

	return nil
}

func (cfg Config) write() error {
	// Ensure the initial config is set up.
	if err := initializeConfigDir(cfg); err != nil {
		return errors.Trace(err)
	}

	// Update config.yml, if necessary.
	if err := cfg.writeConfigFile(); err != nil {
		return errors.Trace(err)
	}

	// Write the cert file and key file, if applicable.
	var cert Cert
	if cfg.Remote.Cert != nil {
		cert = *cfg.Remote.Cert
	}
	if err := cert.Validate(); err != nil {
		logger.Debugf("not writing invalid/empty certificate")
	} else {
		if err := cfg.writeCertPEM(cert); err != nil {
			return errors.Trace(err)
		}
		if err := cfg.writeKeyPEM(cert); err != nil {
			return errors.Trace(err)
		}
	}

	return nil
}

func (cfg Config) writeConfigFile() error {
	filename := cfg.resolve(configDefaultFile)
	logger.Debugf("writing config file %q", filename)

	// TODO(ericsnow) Cache the low-level config in Config?
	rawCfg, err := lxd.LoadConfig()
	if err != nil {
		return errors.Trace(err)
	}

	if !cfg.Remote.isLocal() {
		// Ensure the remote is set correctly.
		remote := cfg.Remote.Name
		delete(rawCfg.Remotes, remote)
		addr := cfg.Remote.Host
		if err := addServer(rawCfg, remote, addr); err != nil {
			return errors.Trace(err)
		}
	}

	// Write out the updated config, if changed.
	// TODO(ericsnow) Check if changed.
	if err := lxd.SaveConfig(rawCfg); err != nil {
		return errors.Trace(err)
	}

	return nil
}

func (cfg Config) writeCertPEM(cert Cert) error {
	filename := cfg.resolve(configCertFile)
	logger.Debugf("writing cert PEM file %q", filename)

	file, err := os.Create(filename)
	if err != nil {
		return errors.Trace(err)
	}
	defer file.Close()

	if err := cert.WriteCertPEM(file); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func (cfg Config) writeKeyPEM(cert Cert) error {
	filename := cfg.resolve(configKeyFile)
	logger.Debugf("writing key PEM file %q", filename)

	file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return errors.Trace(err)
	}
	defer file.Close()

	if err := cert.WriteKeyPEM(file); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func (cfg Config) resolve(file string) string {
	return filepath.Join(cfg.Dirname, file)
}
<|endoftext|>


<commit_before>package bootstrap

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/coreos/ignition/config/util"
	igntypes "github.com/coreos/ignition/config/v2_2/types"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"

	"github.com/openshift/installer/data"
	"github.com/openshift/installer/pkg/asset"
	"github.com/openshift/installer/pkg/asset/ignition"
	"github.com/openshift/installer/pkg/asset/installconfig"
	"github.com/openshift/installer/pkg/asset/kubeconfig"
	"github.com/openshift/installer/pkg/asset/manifests"
	"github.com/openshift/installer/pkg/asset/tls"
	"github.com/openshift/installer/pkg/types"
)

const (
	rootDir              = "/opt/openshift"
	bootstrapIgnFilename = "bootstrap.ign"
	etcdCertSignerImage  = "quay.io/coreos/kube-etcd-signer-server:678cc8e6841e2121ebfdb6e2db568fce290b67d6"
	etcdctlImage         = "quay.io/coreos/etcd:v3.2.14"
	ignitionUser         = "core"
)

var (
	defaultReleaseImage = "registry.svc.ci.openshift.org/openshift/origin-release:v4.0"
)

// bootstrapTemplateData is the data to use to replace values in bootstrap
// template files.
type bootstrapTemplateData struct {
	EtcdCertSignerImage string
	EtcdCluster         string
	EtcdctlImage        string
	PullSecret          string
	ReleaseImage        string
}

// Bootstrap is an asset that generates the ignition config for bootstrap nodes.
type Bootstrap struct {
	Config *igntypes.Config
	File   *asset.File
}

var _ asset.WritableAsset = (*Bootstrap)(nil)

// Dependencies returns the assets on which the Bootstrap asset depends.
func (a *Bootstrap) Dependencies() []asset.Asset {
	return []asset.Asset{
		&installconfig.InstallConfig{},
		&tls.RootCA{},
		&tls.EtcdCA{},
		&tls.KubeCA{},
		&tls.AggregatorCA{},
		&tls.ServiceServingCA{},
		&tls.EtcdClientCertKey{},
		&tls.APIServerCertKey{},
		&tls.APIServerProxyCertKey{},
		&tls.AdminCertKey{},
		&tls.KubeletCertKey{},
		&tls.MCSCertKey{},
		&tls.ServiceAccountKeyPair{},
		&kubeconfig.Admin{},
		&kubeconfig.Kubelet{},
		&manifests.Manifests{},
		&manifests.Openshift{},
	}
}

// Generate generates the ignition config for the Bootstrap asset.
func (a *Bootstrap) Generate(dependencies asset.Parents) error {
	installConfig := &installconfig.InstallConfig{}
	dependencies.Get(installConfig)

	templateData, err := a.getTemplateData(installConfig.Config)
	if err != nil {
		return errors.Wrap(err, "failed to get bootstrap templates")
	}

	a.Config = &igntypes.Config{
		Ignition: igntypes.Ignition{
			Version: igntypes.MaxVersion.String(),
		},
	}

	err = a.addStorageFiles("/", "bootstrap/files", templateData)
	if err != nil {
		return err
	}
	err = a.addSystemdUnits("bootstrap/systemd/units", templateData)
	if err != nil {
		return err
	}
	a.addParentFiles(dependencies)

	a.Config.Passwd.Users = append(
		a.Config.Passwd.Users,
		igntypes.PasswdUser{Name: "core", SSHAuthorizedKeys: []igntypes.SSHAuthorizedKey{igntypes.SSHAuthorizedKey(installConfig.Config.SSHKey)}},
	)

	data, err := json.Marshal(a.Config)
	if err != nil {
		return errors.Wrap(err, "failed to Marshal Ignition config")
	}
	a.File = &asset.File{
		Filename: bootstrapIgnFilename,
		Data:     data,
	}

	return nil
}

// Name returns the human-friendly name of the asset.
func (a *Bootstrap) Name() string {
	return "Bootstrap Ignition Config"
}

// Files returns the files generated by the asset.
func (a *Bootstrap) Files() []*asset.File {
	if a.File != nil {
		return []*asset.File{a.File}
	}
	return []*asset.File{}
}

// getTemplateData returns the data to use to execute bootstrap templates.
func (a *Bootstrap) getTemplateData(installConfig *types.InstallConfig) (*bootstrapTemplateData, error) {
	etcdEndpoints := make([]string, installConfig.MasterCount())
	for i := range etcdEndpoints {
		etcdEndpoints[i] = fmt.Sprintf("https://%s-etcd-%d.%s:2379", installConfig.ObjectMeta.Name, i, installConfig.BaseDomain)
	}

	releaseImage := defaultReleaseImage
	if ri, ok := os.LookupEnv("OPENSHIFT_INSTALL_RELEASE_IMAGE_OVERRIDE"); ok && ri != "" {
		logrus.Warn("Found override for ReleaseImage. Please be warned, this is not advised")
		releaseImage = ri
	}

	return &bootstrapTemplateData{
		EtcdCertSignerImage: etcdCertSignerImage,
		EtcdctlImage:        etcdctlImage,
		PullSecret:          installConfig.PullSecret,
		ReleaseImage:        releaseImage,
		EtcdCluster:         strings.Join(etcdEndpoints, ","),
	}, nil
}

func (a *Bootstrap) addStorageFiles(base string, uri string, templateData *bootstrapTemplateData) (err error) {
	file, err := data.Assets.Open(uri)
	if err != nil {
		return err
	}
	defer file.Close()

	info, err := file.Stat()
	if err != nil {
		return err
	}

	if info.IsDir() {
		children, err := file.Readdir(0)
		if err != nil {
			return err
		}
		file.Close()

		for _, childInfo := range children {
			name := childInfo.Name()
			err = a.addStorageFiles(path.Join(base, name), path.Join(uri, name), templateData)
			if err != nil {
				return err
			}
		}
		return nil
	}

	name := info.Name()
	_, data, err := readFile(name, file, templateData)
	if err != nil {
		return err
	}

	filename := path.Base(uri)

	var mode int
	appendToFile := false
	if path.Base(path.Dir(uri)) == "bin" {
		mode = 0555
	} else if filename == "motd" {
		mode = 0644
		appendToFile = true
	} else {
		mode = 0600
	}
	ign := ignition.FileFromBytes(strings.TrimSuffix(base, ".template"), mode, data)
	if filename == ".bash_history" {
		ign.User = &igntypes.NodeUser{Name: ignitionUser}
		ign.Group = &igntypes.NodeGroup{Name: ignitionUser}
	}
	ign.Append = appendToFile
	a.Config.Storage.Files = append(a.Config.Storage.Files, ign)

	return nil
}

func (a *Bootstrap) addSystemdUnits(uri string, templateData *bootstrapTemplateData) (err error) {
	enabled := map[string]bool{
		"progress.service": true,
		"kubelet.service":  true,
	}

	directory, err := data.Assets.Open(uri)
	if err != nil {
		return err
	}
	defer directory.Close()

	children, err := directory.Readdir(0)
	if err != nil {
		return err
	}

	for _, childInfo := range children {
		name := childInfo.Name()
		file, err := data.Assets.Open(path.Join(uri, name))
		if err != nil {
			return err
		}
		defer file.Close()

		name, data, err := readFile(name, file, templateData)
		if err != nil {
			return err
		}

		unit := igntypes.Unit{Name: name, Contents: string(data)}
		if _, ok := enabled[name]; ok {
			unit.Enabled = util.BoolToPtr(true)
		}
		a.Config.Systemd.Units = append(a.Config.Systemd.Units, unit)
	}

	return nil
}

// Read data from the string reader, and, if the name ends with
// '.template', strip that extension from the name and render the
// template.
func readFile(name string, reader io.Reader, templateData interface{}) (finalName string, data []byte, err error) {
	data, err = ioutil.ReadAll(reader)
	if err != nil {
		return name, []byte{}, err
	}

	if filepath.Ext(name) == ".template" {
		name = strings.TrimSuffix(name, ".template")
		tmpl := template.New(name)
		tmpl, err := tmpl.Parse(string(data))
		if err != nil {
			return name, data, err
		}
		stringData := applyTemplateData(tmpl, templateData)
		data = []byte(stringData)
	}

	return name, data, nil
}

func (a *Bootstrap) addParentFiles(dependencies asset.Parents) {
	mfsts := &manifests.Manifests{}
	openshiftManifests := &manifests.Openshift{}
	dependencies.Get(mfsts, openshiftManifests)

	a.Config.Storage.Files = append(
		a.Config.Storage.Files,
		ignition.FilesFromAsset(rootDir, 0644, mfsts)...,
	)
	a.Config.Storage.Files = append(
		a.Config.Storage.Files,
		ignition.FilesFromAsset(rootDir, 0644, openshiftManifests)...,
	)

	for _, asset := range []asset.WritableAsset{
		&kubeconfig.Admin{},
		&kubeconfig.Kubelet{},
		&tls.RootCA{},
		&tls.KubeCA{},
		&tls.AggregatorCA{},
		&tls.ServiceServingCA{},
		&tls.EtcdCA{},
		&tls.EtcdClientCertKey{},
		&tls.APIServerCertKey{},
		&tls.APIServerProxyCertKey{},
		&tls.AdminCertKey{},
		&tls.KubeletCertKey{},
		&tls.MCSCertKey{},
		&tls.ServiceAccountKeyPair{},
	} {
		dependencies.Get(asset)
		a.Config.Storage.Files = append(a.Config.Storage.Files, ignition.FilesFromAsset(rootDir, 0600, asset)...)
	}
}

func applyTemplateData(template *template.Template, templateData interface{}) string {
	buf := &bytes.Buffer{}
	if err := template.Execute(buf, templateData); err != nil {
		panic(err)
	}
	return buf.String()
}

// Load returns the bootstrap ignition from disk.
func (a *Bootstrap) Load(f asset.FileFetcher) (found bool, err error) {
	file, err := f.FetchByName(bootstrapIgnFilename)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		return false, err
	}

	config := &igntypes.Config{}
	if err := json.Unmarshal(file.Data, config); err != nil {
		return false, errors.Wrapf(err, "failed to unmarshal")
	}

	a.File, a.Config = file, config
	return true, nil
}
<commit_msg>asset/ignition: allow systemd drop-ins to be used<commit_after>package bootstrap

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/coreos/ignition/config/util"
	igntypes "github.com/coreos/ignition/config/v2_2/types"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"

	"github.com/openshift/installer/data"
	"github.com/openshift/installer/pkg/asset"
	"github.com/openshift/installer/pkg/asset/ignition"
	"github.com/openshift/installer/pkg/asset/installconfig"
	"github.com/openshift/installer/pkg/asset/kubeconfig"
	"github.com/openshift/installer/pkg/asset/manifests"
	"github.com/openshift/installer/pkg/asset/tls"
	"github.com/openshift/installer/pkg/types"
)

const (
	rootDir              = "/opt/openshift"
	bootstrapIgnFilename = "bootstrap.ign"
	etcdCertSignerImage  = "quay.io/coreos/kube-etcd-signer-server:678cc8e6841e2121ebfdb6e2db568fce290b67d6"
	etcdctlImage         = "quay.io/coreos/etcd:v3.2.14"
	ignitionUser         = "core"
)

var (
	defaultReleaseImage = "registry.svc.ci.openshift.org/openshift/origin-release:v4.0"
)

// bootstrapTemplateData is the data to use to replace values in bootstrap
// template files.
type bootstrapTemplateData struct {
	EtcdCertSignerImage string
	EtcdCluster         string
	EtcdctlImage        string
	PullSecret          string
	ReleaseImage        string
}

// Bootstrap is an asset that generates the ignition config for bootstrap nodes.
type Bootstrap struct {
	Config *igntypes.Config
	File   *asset.File
}

var _ asset.WritableAsset = (*Bootstrap)(nil)

// Dependencies returns the assets on which the Bootstrap asset depends.
func (a *Bootstrap) Dependencies() []asset.Asset {
	return []asset.Asset{
		&installconfig.InstallConfig{},
		&tls.RootCA{},
		&tls.EtcdCA{},
		&tls.KubeCA{},
		&tls.AggregatorCA{},
		&tls.ServiceServingCA{},
		&tls.EtcdClientCertKey{},
		&tls.APIServerCertKey{},
		&tls.APIServerProxyCertKey{},
		&tls.AdminCertKey{},
		&tls.KubeletCertKey{},
		&tls.MCSCertKey{},
		&tls.ServiceAccountKeyPair{},
		&kubeconfig.Admin{},
		&kubeconfig.Kubelet{},
		&manifests.Manifests{},
		&manifests.Openshift{},
	}
}

// Generate generates the ignition config for the Bootstrap asset.
func (a *Bootstrap) Generate(dependencies asset.Parents) error {
	installConfig := &installconfig.InstallConfig{}
	dependencies.Get(installConfig)

	templateData, err := a.getTemplateData(installConfig.Config)
	if err != nil {
		return errors.Wrap(err, "failed to get bootstrap templates")
	}

	a.Config = &igntypes.Config{
		Ignition: igntypes.Ignition{
			Version: igntypes.MaxVersion.String(),
		},
	}

	err = a.addStorageFiles("/", "bootstrap/files", templateData)
	if err != nil {
		return err
	}
	err = a.addSystemdUnits("bootstrap/systemd/units", templateData)
	if err != nil {
		return err
	}
	a.addParentFiles(dependencies)

	a.Config.Passwd.Users = append(
		a.Config.Passwd.Users,
		igntypes.PasswdUser{Name: "core", SSHAuthorizedKeys: []igntypes.SSHAuthorizedKey{igntypes.SSHAuthorizedKey(installConfig.Config.SSHKey)}},
	)

	data, err := json.Marshal(a.Config)
	if err != nil {
		return errors.Wrap(err, "failed to Marshal Ignition config")
	}
	a.File = &asset.File{
		Filename: bootstrapIgnFilename,
		Data:     data,
	}

	return nil
}

// Name returns the human-friendly name of the asset.
func (a *Bootstrap) Name() string {
	return "Bootstrap Ignition Config"
}

// Files returns the files generated by the asset.
func (a *Bootstrap) Files() []*asset.File {
	if a.File != nil {
		return []*asset.File{a.File}
	}
	return []*asset.File{}
}

// getTemplateData returns the data to use to execute bootstrap templates.
func (a *Bootstrap) getTemplateData(installConfig *types.InstallConfig) (*bootstrapTemplateData, error) {
	etcdEndpoints := make([]string, installConfig.MasterCount())
	for i := range etcdEndpoints {
		etcdEndpoints[i] = fmt.Sprintf("https://%s-etcd-%d.%s:2379", installConfig.ObjectMeta.Name, i, installConfig.BaseDomain)
	}

	releaseImage := defaultReleaseImage
	if ri, ok := os.LookupEnv("OPENSHIFT_INSTALL_RELEASE_IMAGE_OVERRIDE"); ok && ri != "" {
		logrus.Warn("Found override for ReleaseImage. Please be warned, this is not advised")
		releaseImage = ri
	}

	return &bootstrapTemplateData{
		EtcdCertSignerImage: etcdCertSignerImage,
		EtcdctlImage:        etcdctlImage,
		PullSecret:          installConfig.PullSecret,
		ReleaseImage:        releaseImage,
		EtcdCluster:         strings.Join(etcdEndpoints, ","),
	}, nil
}

func (a *Bootstrap) addStorageFiles(base string, uri string, templateData *bootstrapTemplateData) (err error) {
	file, err := data.Assets.Open(uri)
	if err != nil {
		return err
	}
	defer file.Close()

	info, err := file.Stat()
	if err != nil {
		return err
	}

	if info.IsDir() {
		children, err := file.Readdir(0)
		if err != nil {
			return err
		}
		if err = file.Close(); err != nil {
			return err
		}

		for _, childInfo := range children {
			name := childInfo.Name()
			err = a.addStorageFiles(path.Join(base, name), path.Join(uri, name), templateData)
			if err != nil {
				return err
			}
		}
		return nil
	}

	name := info.Name()
	_, data, err := readFile(name, file, templateData)
	if err != nil {
		return err
	}

	filename := path.Base(uri)

	var mode int
	appendToFile := false
	if path.Base(path.Dir(uri)) == "bin" {
		mode = 0555
	} else if filename == "motd" {
		mode = 0644
		appendToFile = true
	} else {
		mode = 0600
	}
	ign := ignition.FileFromBytes(strings.TrimSuffix(base, ".template"), mode, data)
	if filename == ".bash_history" {
		ign.User = &igntypes.NodeUser{Name: ignitionUser}
		ign.Group = &igntypes.NodeGroup{Name: ignitionUser}
	}
	ign.Append = appendToFile
	a.Config.Storage.Files = append(a.Config.Storage.Files, ign)

	return nil
}

func (a *Bootstrap) addSystemdUnits(uri string, templateData *bootstrapTemplateData) (err error) {
	enabled := map[string]bool{
		"progress.service": true,
		"kubelet.service":  true,
	}

	directory, err := data.Assets.Open(uri)
	if err != nil {
		return err
	}
	defer directory.Close()

	children, err := directory.Readdir(0)
	if err != nil {
		return err
	}

	for _, childInfo := range children {
		dir := path.Join(uri, childInfo.Name())
		file, err := data.Assets.Open(dir)
		if err != nil {
			return err
		}
		defer file.Close()

		info, err := file.Stat()
		if err != nil {
			return err
		}

		if info.IsDir() {
			if dir := info.Name(); !strings.HasSuffix(dir, ".d") {
				logrus.Tracef("Ignoring internal asset directory %q while looking for systemd drop-ins", dir)
				continue
			}

			children, err := file.Readdir(0)
			if err != nil {
				return err
			}
			if err = file.Close(); err != nil {
				return err
			}

			dropins := []igntypes.SystemdDropin{}
			for _, childInfo := range children {
				file, err := data.Assets.Open(path.Join(dir, childInfo.Name()))
				if err != nil {
					return err
				}
				defer file.Close()

				childName, contents, err := readFile(childInfo.Name(), file, templateData)
				if err != nil {
					return err
				}

				dropins = append(dropins, igntypes.SystemdDropin{
					Name:     childName,
					Contents: string(contents),
				})
			}

			name := strings.TrimSuffix(childInfo.Name(), ".d")
			unit := igntypes.Unit{
				Name:    name,
				Dropins: dropins,
			}
			if _, ok := enabled[name]; ok {
				unit.Enabled = util.BoolToPtr(true)
			}
			a.Config.Systemd.Units = append(a.Config.Systemd.Units, unit)
		} else {
			name, contents, err := readFile(childInfo.Name(), file, templateData)
			if err != nil {
				return err
			}

			unit := igntypes.Unit{
				Name:     name,
				Contents: string(contents),
			}
			if _, ok := enabled[name]; ok {
				unit.Enabled = util.BoolToPtr(true)
			}
			a.Config.Systemd.Units = append(a.Config.Systemd.Units, unit)
		}
	}

	return nil
}

// Read data from the string reader, and, if the name ends with
// '.template', strip that extension from the name and render the
// template.
func readFile(name string, reader io.Reader, templateData interface{}) (finalName string, data []byte, err error) {
	data, err = ioutil.ReadAll(reader)
	if err != nil {
		return name, []byte{}, err
	}

	if filepath.Ext(name) == ".template" {
		name = strings.TrimSuffix(name, ".template")
		tmpl := template.New(name)
		tmpl, err := tmpl.Parse(string(data))
		if err != nil {
			return name, data, err
		}
		stringData := applyTemplateData(tmpl, templateData)
		data = []byte(stringData)
	}

	return name, data, nil
}

func (a *Bootstrap) addParentFiles(dependencies asset.Parents) {
	mfsts := &manifests.Manifests{}
	openshiftManifests := &manifests.Openshift{}
	dependencies.Get(mfsts, openshiftManifests)

	a.Config.Storage.Files = append(
		a.Config.Storage.Files,
		ignition.FilesFromAsset(rootDir, 0644, mfsts)...,
	)
	a.Config.Storage.Files = append(
		a.Config.Storage.Files,
		ignition.FilesFromAsset(rootDir, 0644, openshiftManifests)...,
	)

	for _, asset := range []asset.WritableAsset{
		&kubeconfig.Admin{},
		&kubeconfig.Kubelet{},
		&tls.RootCA{},
		&tls.KubeCA{},
		&tls.AggregatorCA{},
		&tls.ServiceServingCA{},
		&tls.EtcdCA{},
		&tls.EtcdClientCertKey{},
		&tls.APIServerCertKey{},
		&tls.APIServerProxyCertKey{},
		&tls.AdminCertKey{},
		&tls.KubeletCertKey{},
		&tls.MCSCertKey{},
		&tls.ServiceAccountKeyPair{},
	} {
		dependencies.Get(asset)
		a.Config.Storage.Files = append(a.Config.Storage.Files, ignition.FilesFromAsset(rootDir, 0600, asset)...)
	}
}

func applyTemplateData(template *template.Template, templateData interface{}) string {
	buf := &bytes.Buffer{}
	if err := template.Execute(buf, templateData); err != nil {
		panic(err)
	}
	return buf.String()
}

// Load returns the bootstrap ignition from disk.
func (a *Bootstrap) Load(f asset.FileFetcher) (found bool, err error) {
	file, err := f.FetchByName(bootstrapIgnFilename)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		return false, err
	}

	config := &igntypes.Config{}
	if err := json.Unmarshal(file.Data, config); err != nil {
		return false, errors.Wrapf(err, "failed to unmarshal")
	}

	a.File, a.Config = file, config
	return true, nil
}
<|endoftext|>


<commit_before>package management

import (
	"fmt"
	"os"
	"reflect"
	"strings"

	v32 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	v3 "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3"
	"github.com/rancher/rancher/pkg/types/config"
	"github.com/sirupsen/logrus"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	Amazonec2driver    = "amazonec2"
	Azuredriver        = "azure"
	DigitalOceandriver = "digitalocean"
	ExoscaleDriver     = "exoscale"
	HarvesterDriver    = "harvester"
	Linodedriver       = "linode"
	OCIDriver          = "oci"
	OTCDriver          = "otc"
	OpenstackDriver    = "openstack"
	PacketDriver       = "packet"
	PhoenixNAPDriver   = "pnap"
	RackspaceDriver    = "rackspace"
	SoftLayerDriver    = "softlayer"
	Vmwaredriver       = "vmwarevsphere"
	GoogleDriver       = "google"
)

var DriverData = map[string]map[string][]string{
	Amazonec2driver:    {"publicCredentialFields": []string{"accessKey"}, "privateCredentialFields": []string{"secretKey"}},
	Azuredriver:        {"publicCredentialFields": []string{"clientId", "subscriptionId", "tenantId", "environment"}, "privateCredentialFields": []string{"clientSecret"}, "optionalCredentialFields": []string{"tenantId"}},
	DigitalOceandriver: {"privateCredentialFields": []string{"accessToken"}},
	ExoscaleDriver:     {"privateCredentialFields": []string{"apiSecretKey"}},
	HarvesterDriver:    {"publicCredentialFields": []string{"clusterType", "clusterId"}, "privateCredentialFields": []string{"kubeconfigContent"}, "optionalCredentialFields": []string{"clusterId"}},
	Linodedriver:       {"privateCredentialFields": []string{"token"}, "passwordFields": []string{"rootPass"}},
	OCIDriver:          {"publicCredentialFields": []string{"tenancyId", "userId", "fingerprint"}, "privateCredentialFields": []string{"privateKeyContents"}, "passwordFields": []string{"privateKeyPassphrase"}},
	OTCDriver:          {"privateCredentialFields": []string{"accessKeySecret"}},
	OpenstackDriver:    {"privateCredentialFields": []string{"password"}},
	PacketDriver:       {"privateCredentialFields": []string{"apiKey"}},
	PhoenixNAPDriver:   {"publicCredentialFields": []string{"clientIdentifier"}, "privateCredentialFields": []string{"clientSecret"}},
	RackspaceDriver:    {"privateCredentialFields": []string{"apiKey"}},
	SoftLayerDriver:    {"privateCredentialFields": []string{"apiKey"}},
	Vmwaredriver:       {"publicCredentialFields": []string{"username", "vcenter", "vcenterPort"}, "privateCredentialFields": []string{"password"}},
	GoogleDriver:       {"privateCredentialFields": []string{"authEncodedJson"}},
}

var driverDefaults = map[string]map[string]string{
	HarvesterDriver: {"clusterType": "imported"},
	Vmwaredriver:    {"vcenterPort": "443"},
}

type machineDriverCompare struct {
	builtin            bool
	addCloudCredential bool
	url                string
	uiURL              string
	checksum           string
	name               string
	whitelist          []string
	annotations        map[string]string
}

func addMachineDrivers(management *config.ManagementContext) error {
	if err := addMachineDriver("pinganyunecs", "https://drivers.rancher.cn/node-driver-pinganyun/0.3.0/docker-machine-driver-pinganyunecs-linux.tgz", "https://drivers.rancher.cn/node-driver-pinganyun/0.3.0/component.js", "f84ccec11c2c1970d76d30150916933efe8ca49fe4c422c8954fc37f71273bb5", []string{"drivers.rancher.cn"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver("aliyunecs", "https://drivers.rancher.cn/node-driver-aliyun/1.0.4/docker-machine-driver-aliyunecs.tgz", "", "5990d40d71c421a85563df9caf069466f300cd75723effe4581751b0de9a6a0e", []string{"ecs.aliyuncs.com"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(Amazonec2driver, "local://", "", "",
		[]string{"iam.amazonaws.com", "iam.us-gov.amazonaws.com", "iam.%.amazonaws.com.cn", "ec2.%.amazonaws.com", "ec2.%.amazonaws.com.cn", "eks.%.amazonaws.com", "eks.%.amazonaws.com.cn", "kms.%.amazonaws.com", "kms.%.amazonaws.com.cn"},
		true, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(Azuredriver, "local://", "", "", nil, true, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver("cloudca", "https://github.com/cloud-ca/docker-machine-driver-cloudca/files/2446837/docker-machine-driver-cloudca_v2.0.0_linux-amd64.zip", "https://objects-east.cloud.ca/v1/5ef827605f884961b94881e928e7a250/ui-driver-cloudca/v2.1.2/component.js", "2a55efd6d62d5f7fd27ce877d49596f4", []string{"objects-east.cloud.ca"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver("cloudscale", "https://github.com/cloudscale-ch/docker-machine-driver-cloudscale/releases/download/v1.2.0/docker-machine-driver-cloudscale_v1.2.0_linux_amd64.tar.gz", "https://objects.rma.cloudscale.ch/cloudscale-rancher-v2-ui-driver/component.js", "e33fbd6c2f87b1c470bcb653cc8aa50baf914a9d641a2f18f86a07c398cfb544", []string{"objects.rma.cloudscale.ch"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(DigitalOceandriver, "local://", "", "", []string{"api.digitalocean.com"}, true, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(ExoscaleDriver, "local://", "", "", []string{"api.exoscale.ch"}, false, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(GoogleDriver, "local://", "", "", nil, false, true, true, management); err != nil {
		return err
	}
	if err := addMachineDriver(HarvesterDriver, "https://releases.rancher.com/harvester-node-driver/v0.2.0/docker-machine-driver-harvester-amd64.tar.gz", "", "41a7f9c310faaa2511e6d27a93c0fbf415391c282b020c2ae0cf77465eae4818", []string{"releases.rancher.com"}, false, false, false, management); err != nil {
		return err
	}
	linodeBuiltin := true
	if dl := os.Getenv("CATTLE_DEV_MODE"); dl != "" {
		linodeBuiltin = false
	}
	if err := addMachineDriver(Linodedriver, "https://github.com/linode/docker-machine-driver-linode/releases/download/v0.1.8/docker-machine-driver-linode_linux-amd64.zip", "/assets/rancher-ui-driver-linode/component.js", "b31b6a504c59ee758d2dda83029fe4a85b3f5601e22dfa58700a5e6c8f450dc7", []string{"api.linode.com"}, linodeBuiltin, linodeBuiltin, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(OCIDriver, "https://github.com/rancher-plugins/rancher-machine-driver-oci/releases/download/v1.1.0/docker-machine-driver-oci-linux", "", "c19b109050e56a451f93c3390cc9d015da985a19d21bd0def858430be3324fd2", []string{"*.oraclecloud.com"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(OpenstackDriver, "local://", "", "", nil, false, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(OTCDriver, "https://github.com/rancher-plugins/docker-machine-driver-otc/releases/download/v2019.5.7/docker-machine-driver-otc", "", "3f793ebb0ebd9477b9166ec542f77e25", nil, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(PacketDriver, "https://github.com/packethost/docker-machine-driver-packet/releases/download/v0.2.2/docker-machine-driver-packet_linux-amd64.zip", "https://packethost.github.io/ui-driver-packet/1.0.2/component.js", "e03c6bc9406c811e03e9bc2c39f43e6cc8c02d1615bd0e0b8ee1b38be6fe201c", []string{"api.packet.net", "packethost.github.io"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(PhoenixNAPDriver, "https://github.com/phoenixnap/docker-machine-driver-pnap/releases/download/v0.1.0/docker-machine-driver-pnap_0.1.0_linux_amd64.zip", "", "5f25a7fbcaca0710b7290216464ca8433fa3d683b59d5e4e674bef2d0a3ff6c7", []string{"api.securedservers.com", "api.phoenixnap.com"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(RackspaceDriver, "local://", "", "", nil, false, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(SoftLayerDriver, "local://", "", "", nil, false, true, false, management); err != nil {
		return err
	}
	return addMachineDriver(Vmwaredriver, "local://", "", "", nil, true, true, false, management)
}

func addMachineDriver(name, url, uiURL, checksum string, whitelist []string, active, builtin, addCloudCredential bool, management *config.ManagementContext) error {
	lister := management.Management.NodeDrivers("").Controller().Lister()
	cli := management.Management.NodeDrivers("")
	m, _ := lister.Get("", name)
	// annotations can have keys cred and password, values []string to be considered as a part of cloud credential
	annotations := map[string]string{}
	if m != nil {
		for k, v := range m.Annotations {
			annotations[k] = v
		}
	}
	for key, fields := range DriverData[name] {
		annotations[key] = strings.Join(fields, ",")
	}
	defaults := []string{}
	for key, val := range driverDefaults[name] {
		defaults = append(defaults, fmt.Sprintf("%s:%s", key, val))
	}
	if len(defaults) > 0 {
		annotations["defaults"] = strings.Join(defaults, ",")
	}
	if m != nil {
		old := machineDriverCompare{
			builtin:            m.Spec.Builtin,
			addCloudCredential: m.Spec.AddCloudCredential,
			url:                m.Spec.URL,
			uiURL:              m.Spec.UIURL,
			checksum:           m.Spec.Checksum,
			name:               m.Spec.DisplayName,
			whitelist:          m.Spec.WhitelistDomains,
			annotations:        m.Annotations,
		}
		new := machineDriverCompare{
			builtin:            builtin,
			addCloudCredential: addCloudCredential,
			url:                url,
			uiURL:              uiURL,
			checksum:           checksum,
			name:               name,
			whitelist:          whitelist,
			annotations:        annotations,
		}
		if !reflect.DeepEqual(new, old) {
			logrus.Infof("Updating node driver %v", name)
			m.Spec.Builtin = builtin
			m.Spec.AddCloudCredential = addCloudCredential
			m.Spec.URL = url
			m.Spec.UIURL = uiURL
			m.Spec.Checksum = checksum
			m.Spec.DisplayName = name
			m.Spec.WhitelistDomains = whitelist
			m.Annotations = annotations
			_, err := cli.Update(m)
			return err
		}
		return nil
	}

	logrus.Infof("Creating node driver %v", name)
	_, err := cli.Create(&v3.NodeDriver{
		ObjectMeta: v1.ObjectMeta{
			Name:        name,
			Annotations: annotations,
		},
		Spec: v32.NodeDriverSpec{
			Active:             active,
			Builtin:            builtin,
			AddCloudCredential: addCloudCredential,
			URL:                url,
			UIURL:              uiURL,
			DisplayName:        name,
			Checksum:           checksum,
			WhitelistDomains:   whitelist,
		},
	})

	return err
}
<commit_msg>Bump harvester node driver to 0.3.0<commit_after>package management

import (
	"fmt"
	"os"
	"reflect"
	"strings"

	v32 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	v3 "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3"
	"github.com/rancher/rancher/pkg/types/config"
	"github.com/sirupsen/logrus"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	Amazonec2driver    = "amazonec2"
	Azuredriver        = "azure"
	DigitalOceandriver = "digitalocean"
	ExoscaleDriver     = "exoscale"
	HarvesterDriver    = "harvester"
	Linodedriver       = "linode"
	OCIDriver          = "oci"
	OTCDriver          = "otc"
	OpenstackDriver    = "openstack"
	PacketDriver       = "packet"
	PhoenixNAPDriver   = "pnap"
	RackspaceDriver    = "rackspace"
	SoftLayerDriver    = "softlayer"
	Vmwaredriver       = "vmwarevsphere"
	GoogleDriver       = "google"
)

var DriverData = map[string]map[string][]string{
	Amazonec2driver:    {"publicCredentialFields": []string{"accessKey"}, "privateCredentialFields": []string{"secretKey"}},
	Azuredriver:        {"publicCredentialFields": []string{"clientId", "subscriptionId", "tenantId", "environment"}, "privateCredentialFields": []string{"clientSecret"}, "optionalCredentialFields": []string{"tenantId"}},
	DigitalOceandriver: {"privateCredentialFields": []string{"accessToken"}},
	ExoscaleDriver:     {"privateCredentialFields": []string{"apiSecretKey"}},
	HarvesterDriver:    {"publicCredentialFields": []string{"clusterType", "clusterId"}, "privateCredentialFields": []string{"kubeconfigContent"}, "optionalCredentialFields": []string{"clusterId"}},
	Linodedriver:       {"privateCredentialFields": []string{"token"}, "passwordFields": []string{"rootPass"}},
	OCIDriver:          {"publicCredentialFields": []string{"tenancyId", "userId", "fingerprint"}, "privateCredentialFields": []string{"privateKeyContents"}, "passwordFields": []string{"privateKeyPassphrase"}},
	OTCDriver:          {"privateCredentialFields": []string{"accessKeySecret"}},
	OpenstackDriver:    {"privateCredentialFields": []string{"password"}},
	PacketDriver:       {"privateCredentialFields": []string{"apiKey"}},
	PhoenixNAPDriver:   {"publicCredentialFields": []string{"clientIdentifier"}, "privateCredentialFields": []string{"clientSecret"}},
	RackspaceDriver:    {"privateCredentialFields": []string{"apiKey"}},
	SoftLayerDriver:    {"privateCredentialFields": []string{"apiKey"}},
	Vmwaredriver:       {"publicCredentialFields": []string{"username", "vcenter", "vcenterPort"}, "privateCredentialFields": []string{"password"}},
	GoogleDriver:       {"privateCredentialFields": []string{"authEncodedJson"}},
}

var driverDefaults = map[string]map[string]string{
	HarvesterDriver: {"clusterType": "imported"},
	Vmwaredriver:    {"vcenterPort": "443"},
}

type machineDriverCompare struct {
	builtin            bool
	addCloudCredential bool
	url                string
	uiURL              string
	checksum           string
	name               string
	whitelist          []string
	annotations        map[string]string
}

func addMachineDrivers(management *config.ManagementContext) error {
	if err := addMachineDriver("pinganyunecs", "https://drivers.rancher.cn/node-driver-pinganyun/0.3.0/docker-machine-driver-pinganyunecs-linux.tgz", "https://drivers.rancher.cn/node-driver-pinganyun/0.3.0/component.js", "f84ccec11c2c1970d76d30150916933efe8ca49fe4c422c8954fc37f71273bb5", []string{"drivers.rancher.cn"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver("aliyunecs", "https://drivers.rancher.cn/node-driver-aliyun/1.0.4/docker-machine-driver-aliyunecs.tgz", "", "5990d40d71c421a85563df9caf069466f300cd75723effe4581751b0de9a6a0e", []string{"ecs.aliyuncs.com"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(Amazonec2driver, "local://", "", "",
		[]string{"iam.amazonaws.com", "iam.us-gov.amazonaws.com", "iam.%.amazonaws.com.cn", "ec2.%.amazonaws.com", "ec2.%.amazonaws.com.cn", "eks.%.amazonaws.com", "eks.%.amazonaws.com.cn", "kms.%.amazonaws.com", "kms.%.amazonaws.com.cn"},
		true, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(Azuredriver, "local://", "", "", nil, true, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver("cloudca", "https://github.com/cloud-ca/docker-machine-driver-cloudca/files/2446837/docker-machine-driver-cloudca_v2.0.0_linux-amd64.zip", "https://objects-east.cloud.ca/v1/5ef827605f884961b94881e928e7a250/ui-driver-cloudca/v2.1.2/component.js", "2a55efd6d62d5f7fd27ce877d49596f4", []string{"objects-east.cloud.ca"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver("cloudscale", "https://github.com/cloudscale-ch/docker-machine-driver-cloudscale/releases/download/v1.2.0/docker-machine-driver-cloudscale_v1.2.0_linux_amd64.tar.gz", "https://objects.rma.cloudscale.ch/cloudscale-rancher-v2-ui-driver/component.js", "e33fbd6c2f87b1c470bcb653cc8aa50baf914a9d641a2f18f86a07c398cfb544", []string{"objects.rma.cloudscale.ch"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(DigitalOceandriver, "local://", "", "", []string{"api.digitalocean.com"}, true, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(ExoscaleDriver, "local://", "", "", []string{"api.exoscale.ch"}, false, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(GoogleDriver, "local://", "", "", nil, false, true, true, management); err != nil {
		return err
	}
	if err := addMachineDriver(HarvesterDriver, "https://releases.rancher.com/harvester-node-driver/v0.3.0/docker-machine-driver-harvester-amd64.tar.gz", "", "08321a666cd664039e318bd1b3c7085f8bda3ef9b3c1e046796dfe1c45c91cf9", []string{"releases.rancher.com"}, false, false, false, management); err != nil {
		return err
	}
	linodeBuiltin := true
	if dl := os.Getenv("CATTLE_DEV_MODE"); dl != "" {
		linodeBuiltin = false
	}
	if err := addMachineDriver(Linodedriver, "https://github.com/linode/docker-machine-driver-linode/releases/download/v0.1.8/docker-machine-driver-linode_linux-amd64.zip", "/assets/rancher-ui-driver-linode/component.js", "b31b6a504c59ee758d2dda83029fe4a85b3f5601e22dfa58700a5e6c8f450dc7", []string{"api.linode.com"}, linodeBuiltin, linodeBuiltin, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(OCIDriver, "https://github.com/rancher-plugins/rancher-machine-driver-oci/releases/download/v1.1.0/docker-machine-driver-oci-linux", "", "c19b109050e56a451f93c3390cc9d015da985a19d21bd0def858430be3324fd2", []string{"*.oraclecloud.com"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(OpenstackDriver, "local://", "", "", nil, false, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(OTCDriver, "https://github.com/rancher-plugins/docker-machine-driver-otc/releases/download/v2019.5.7/docker-machine-driver-otc", "", "3f793ebb0ebd9477b9166ec542f77e25", nil, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(PacketDriver, "https://github.com/packethost/docker-machine-driver-packet/releases/download/v0.2.2/docker-machine-driver-packet_linux-amd64.zip", "https://packethost.github.io/ui-driver-packet/1.0.2/component.js", "e03c6bc9406c811e03e9bc2c39f43e6cc8c02d1615bd0e0b8ee1b38be6fe201c", []string{"api.packet.net", "packethost.github.io"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(PhoenixNAPDriver, "https://github.com/phoenixnap/docker-machine-driver-pnap/releases/download/v0.1.0/docker-machine-driver-pnap_0.1.0_linux_amd64.zip", "", "5f25a7fbcaca0710b7290216464ca8433fa3d683b59d5e4e674bef2d0a3ff6c7", []string{"api.securedservers.com", "api.phoenixnap.com"}, false, false, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(RackspaceDriver, "local://", "", "", nil, false, true, false, management); err != nil {
		return err
	}
	if err := addMachineDriver(SoftLayerDriver, "local://", "", "", nil, false, true, false, management); err != nil {
		return err
	}
	return addMachineDriver(Vmwaredriver, "local://", "", "", nil, true, true, false, management)
}

func addMachineDriver(name, url, uiURL, checksum string, whitelist []string, active, builtin, addCloudCredential bool, management *config.ManagementContext) error {
	lister := management.Management.NodeDrivers("").Controller().Lister()
	cli := management.Management.NodeDrivers("")
	m, _ := lister.Get("", name)
	// annotations can have keys cred and password, values []string to be considered as a part of cloud credential
	annotations := map[string]string{}
	if m != nil {
		for k, v := range m.Annotations {
			annotations[k] = v
		}
	}
	for key, fields := range DriverData[name] {
		annotations[key] = strings.Join(fields, ",")
	}
	defaults := []string{}
	for key, val := range driverDefaults[name] {
		defaults = append(defaults, fmt.Sprintf("%s:%s", key, val))
	}
	if len(defaults) > 0 {
		annotations["defaults"] = strings.Join(defaults, ",")
	}
	if m != nil {
		old := machineDriverCompare{
			builtin:            m.Spec.Builtin,
			addCloudCredential: m.Spec.AddCloudCredential,
			url:                m.Spec.URL,
			uiURL:              m.Spec.UIURL,
			checksum:           m.Spec.Checksum,
			name:               m.Spec.DisplayName,
			whitelist:          m.Spec.WhitelistDomains,
			annotations:        m.Annotations,
		}
		new := machineDriverCompare{
			builtin:            builtin,
			addCloudCredential: addCloudCredential,
			url:                url,
			uiURL:              uiURL,
			checksum:           checksum,
			name:               name,
			whitelist:          whitelist,
			annotations:        annotations,
		}
		if !reflect.DeepEqual(new, old) {
			logrus.Infof("Updating node driver %v", name)
			m.Spec.Builtin = builtin
			m.Spec.AddCloudCredential = addCloudCredential
			m.Spec.URL = url
			m.Spec.UIURL = uiURL
			m.Spec.Checksum = checksum
			m.Spec.DisplayName = name
			m.Spec.WhitelistDomains = whitelist
			m.Annotations = annotations
			_, err := cli.Update(m)
			return err
		}
		return nil
	}

	logrus.Infof("Creating node driver %v", name)
	_, err := cli.Create(&v3.NodeDriver{
		ObjectMeta: v1.ObjectMeta{
			Name:        name,
			Annotations: annotations,
		},
		Spec: v32.NodeDriverSpec{
			Active:             active,
			Builtin:            builtin,
			AddCloudCredential: addCloudCredential,
			URL:                url,
			UIURL:              uiURL,
			DisplayName:        name,
			Checksum:           checksum,
			WhitelistDomains:   whitelist,
		},
	})

	return err
}
<|endoftext|>