func CombineGuestOSFeaturesBeta(features1 []*computeBeta.GuestOsFeature,
	features2 ...string) []*computeBeta.GuestOsFeature {
	featureSet := map[string]bool{}
	for _, feature := range features2 {
		featureSet[feature] = true
	}
	for _, feature := range features1 {
		featureSet[feature.Type] = true
	}
	ret := make([]*computeBeta.GuestOsFeature, 0)
	for feature := range featureSet {
		ret = append(ret, &computeBeta.GuestOsFeature{
			Type: feature,
		})
	}
	sort.Slice(ret, func(i, j int) bool {
		return ret[i].Type < ret[j].Type
	})
	return ret
}
CombineGuestOSFeaturesBeta merges two slices of Beta Guest OS features and
returns a new slice instance. Duplicates are removed.
package daisy
import (
	"context"
	"math/rand"
	"os"
	"os/user"
	"reflect"
	"regexp"
	"sort"
	"strings"
	"time"
	computeBeta "google.golang.org/api/compute/v0.beta"
	"google.golang.org/api/compute/v1"
)
var (
	varPattern = regexp.MustCompile(`\$\{.*\}`)
)
func getUser() string {
	if cu, err := user.Current(); err == nil {
		return cu.Username
	}
	if hn, err := os.Hostname(); err == nil {
		return hn
	}
	return "unknown"
}
func NamedSubexp(re *regexp.Regexp, s string) map[string]string {
	match := re.FindStringSubmatch(s)
	if match == nil {
		return nil
	}
	result := make(map[string]string)
	l := len(match)
	for i, name := range re.SubexpNames() {
		if i == 0 || name == "" {
			continue
		}
		result[name] = ""
		if i < l {
			result[name] = match[i]
		}
	}
	return result
}
func filter(ss []string, s string) []string {
	result := []string{}
	for _, element := range ss {
		if element != s {
			result = append(result, element)
		}
	}
	return result
}
func minInt(x int, ys ...int) int {
	for _, y := range ys {
		if y < x {
			x = y
		}
	}
	return x
}
func randString(n int) string {
	gen := rand.New(rand.NewSource(time.Now().UnixNano()))
	letters := "bdghjlmnpqrstvwxyz0123456789"
	b := make([]byte, n)
	for i := range b {
		b[i] = letters[gen.Int63()%int64(len(letters))]
	}
	return string(b)
}
func strIn(s string, ss []string) bool {
	for _, x := range ss {
		if s == x {
			return true
		}
	}
	return false
}
func strLitPtr(s string) *string {
	return &s
}
func strOr(s string, ss ...string) string {
	ss = append([]string{s}, ss...)
	for _, st := range ss {
		if st != "" {
			return st
		}
	}
	return ""
}
func substitute(v reflect.Value, replacer *strings.Replacer) {
	traverseData(v, func(val reflect.Value) DError {
		switch val.Interface().(type) {
		case string:
			val.SetString(replacer.Replace(val.String()))
		}
		return nil
	}, func(v reflect.Value) traverseAction {
		_, ok := v.Interface().(*Workflow)
		if ok {
			return prune
		}
		return continueTraversal
	})
}
func hasVariableDeclaration(s string) bool {
	return varPattern.MatchString(s)
}
func getRegionFromZone(z string) string {
	if z != "" {
		return z[:len(z)-2]
	}
	return ""
}
func (w *Workflow) substituteSourceVars(ctx context.Context, v reflect.Value) DError {
	return traverseData(v, func(val reflect.Value) DError {
		switch val.Interface().(type) {
		case string:
			if matches := sourceVarRgx.FindAllStringSubmatch(val.String(), -1); matches != nil {
				futureVal := val.String()
				for _, match := range matches {
					if len(match) < 2 || !w.sourceExists(match[1]) {
						return Errf("source not found for expansion: %s", match[0])
					}
					sv, err := w.sourceContent(ctx, match[1])
					if err != nil {
						return Errf("error reading source content for %s: %v", match[1], err)
					}
					futureVal = strings.Replace(futureVal, match[0], sv, -1)
					if len(futureVal) > 1024*256 {
						return Errf("Expanded string for '%s' is too large", val.String())
					}
				}
				val.SetString(futureVal)
			}
		}
		return nil
	})
}
type traverseAction uint
const (
	continueTraversal traverseAction = iota
	prune
)
func traverseData(v reflect.Value, f func(reflect.Value) DError, actions ...func(reflect.Value) traverseAction) DError {
	if !v.CanSet() {
		return nil
	}
	for _, action := range actions {
		switch action(v) {
		case prune:
			return nil
		}
	}
	switch v.Kind() {
	case reflect.Chan, reflect.Func:
		return nil
	case reflect.Interface, reflect.Ptr, reflect.UnsafePointer:
		if v.IsNil() {
			return nil
		}
		return traverseData(v.Elem(), f, actions...)
	}
	switch v.Kind() {
	case reflect.Array, reflect.Slice:
		for i := 0; i < v.Len(); i++ {
			if err := traverseData(v.Index(i), f, actions...); err != nil {
				return err
			}
		}
	case reflect.Map:
		kvs := v.MapKeys()
		for _, kv := range kvs {
			vv := v.MapIndex(kv)
			newKv := reflect.New(kv.Type()).Elem()
			newKv.Set(kv)
			newVv := reflect.New(vv.Type()).Elem()
			newVv.Set(vv)
			if err := traverseData(newKv, f, actions...); err != nil {
				return err
			}
			if err := traverseData(newVv, f, actions...); err != nil {
				return err
			}
			v.SetMapIndex(kv, reflect.Value{})
			v.SetMapIndex(newKv, newVv)
		}
	case reflect.Struct:
		for i := 0; i < v.NumField(); i++ {
			if err := traverseData(v.Field(i), f, actions...); err != nil {
				return err
			}
		}
	default:
		return f(v)
	}
	return nil
}
func xor(x, y bool) bool {
	return x != y
}
func CombineGuestOSFeatures(features1 []*compute.GuestOsFeature,
	features2 ...string) []*compute.GuestOsFeature {
	featureSet := map[string]bool{}
	for _, feature := range features2 {
		featureSet[feature] = true
	}
	for _, feature := range features1 {
		featureSet[feature.Type] = true
	}
	ret := make([]*compute.GuestOsFeature, 0)
	for feature := range featureSet {
		ret = append(ret, &compute.GuestOsFeature{
			Type: feature,
		})
	}
	sort.Slice(ret, func(i, j int) bool {
		return ret[i].Type < ret[j].Type
	})
	return ret
}