<commit_before>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	filepath "path"
	"strings"
)

const (
	LUNCHY_VERSION = "0.1.6"
)

func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

func fileCopy(src string, dst string) error {
	s, err := os.Open(src)
	if err != nil {
		return err
	}

	defer s.Close()

	d, err := os.Create(dst)
	if err != nil {
		return err
	}

	if _, err := io.Copy(d, s); err != nil {
		d.Close()
		return err
	}

	return d.Close()
}

func findPlists(path string) []string {
	output, err := exec.Command("find", path, "-name", "homebrew.*.plist", "-type", "f").Output()
	if err != nil {
		return []string{}
	}

	lines := strings.Split(strings.TrimSpace(string(output)), "\n")
	plists := []string{}

	for _, line := range lines {
		plists = append(plists, strings.Replace(filepath.Base(line), ".plist", "", 1))
	}

	return plists
}

func getPlists() []string {
	path := fmt.Sprintf("%s/Library/LaunchAgents", os.Getenv("HOME"))
	files := findPlists(path)

	return files
}

func getPlist(name string) string {
	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			return plist
		}
	}

	return ""
}

func sliceIncludes(slice []string, match string) bool {
	for _, val := range slice {
		if val == match {
			return true
		}
	}

	return false
}

func printUsage() {
	fmt.Printf("Lunchy %s, the friendly launchctl wrapper\n", LUNCHY_VERSION)
	fmt.Println("Usage: lunchy [start|stop|restart|list|status|install|show|edit|remove|scan] [options]")
}

func printList() {
	for _, file := range getPlists() {
		fmt.Println(file)
	}
}

func printStatus(args []string) {
	out, err := exec.Command("launchctl", "list").Output()

	if err != nil {
		fatal("failed to get process list")
	}

	pattern := ""

	if len(args) == 3 {
		pattern = args[2]
	}

	installed := getPlists()
	lines := strings.Split(strings.TrimSpace(string(out)), "\n")

	for _, line := range lines {
		chunks := strings.Split(line, "\t")
		clean_line := strings.Replace(line, "\t", " ", -1)

		if len(pattern) > 0 {
			if strings.Index(chunks[2], pattern) != -1 {
				if sliceIncludes(installed, chunks[2]) {
					fmt.Println(clean_line)
				}
			}
		} else {
			if sliceIncludes(installed, chunks[2]) {
				fmt.Println(clean_line)
			}
		}
	}
}

func exitWithInvalidArgs(args []string, msg string) {
	if len(args) < 3 {
		fmt.Println(msg)
		os.Exit(1)
	}
}

func startDaemons(args []string) {
	// Check if name pattern is not given and try profiles
	if len(args) == 2 {
		if profileExists() {
			startProfile()
			return
		} else {
			exitWithInvalidArgs(args, "name required")
		}
	}

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			startDaemon(plist)
		}
	}
}

func startDaemon(name string) {
	path := fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", os.Getenv("HOME"), name)
	_, err := exec.Command("launchctl", "load", path).Output()

	if err != nil {
		fmt.Println("failed to start", name)
		return
	}

	fmt.Println("started", name)
}

func stopDaemons(args []string) {
	// Check if name pattern is not given and try profiles
	if len(args) == 2 {
		if profileExists() {
			stopProfile()
			return
		} else {
			exitWithInvalidArgs(args, "name required")
		}
	}

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			stopDaemon(plist)
		}
	}
}

func stopDaemon(name string) {
	path := fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", os.Getenv("HOME"), name)
	_, err := exec.Command("launchctl", "unload", path).Output()

	if err != nil {
		fmt.Println("failed to stop", name)
		return
	}

	fmt.Println("stopped", name)
}

func restartDaemons(args []string) {
	// Check if name pattern is not given and try profiles
	if len(args) == 2 {
		if profileExists() {
			restartProfile()
			return
		} else {
			exitWithInvalidArgs(args, "name required")
		}
	}

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			stopDaemon(plist)
			startDaemon(plist)
		}
	}
}

func showPlist(args []string) {
	exitWithInvalidArgs(args, "name required")

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			printPlistContent(plist)
			return
		}
	}
}

func printPlistContent(name string) {
	path := fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", os.Getenv("HOME"), name)
	contents, err := ioutil.ReadFile(path)

	if err != nil {
		fatal("unable to read plist")
	}

	fmt.Printf(string(contents))
}

func editPlist(args []string) {
	exitWithInvalidArgs(args, "name required")

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			editPlistContent(plist)
			return
		}
	}
}

func editPlistContent(name string) {
	path := fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", os.Getenv("HOME"), name)
	editor := os.Getenv("EDITOR")

	if len(editor) == 0 {
		fatal("EDITOR environment variable is not set")
	}

	cmd := exec.Command(editor, path)

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout

	cmd.Start()
	cmd.Wait()
}

func installPlist(args []string) {
	exitWithInvalidArgs(args, "path required")

	path := args[2]

	if !fileExists(path) {
		fatal("source file does not exist")
	}

	info, _ := os.Stat(path)
	base_path := fmt.Sprintf("%s/%s", os.Getenv("HOME"), "Library/LaunchAgents")
	new_path := fmt.Sprintf("%s/%s", base_path, info.Name())

	if fileExists(new_path) && os.Remove(new_path) != nil {
		fatal("unable to delete existing plist")
	}

	if fileCopy(path, new_path) != nil {
		fatal("failed to copy file")
	}

	fmt.Println(path, "installed to", base_path)
}

func removePlist(args []string) {
	exitWithInvalidArgs(args, "name required")

	name := args[2]
	base_path := fmt.Sprintf("%s/%s", os.Getenv("HOME"), "Library/LaunchAgents")

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			path := fmt.Sprintf("%s/%s.plist", base_path, plist)

			if os.Remove(path) == nil {
				fmt.Println("removed", path)
			} else {
				fmt.Println("failed to remove", path)
			}
		}
	}
}

func scanPath(args []string) {
	path := fmt.Sprintf("%s/%s", os.Getenv("HOME"), "Library/LaunchAgents")

	if len(args) >= 3 {
		path = args[2]
	}

	// This is a handy override to find all homebrew-based lists
	if path == "homebrew" {
		path = "/usr/local/Cellar"
	}

	for _, f := range findPlists(path) {
		fmt.Println(f)
	}
}

// Get full path to lunchy profile file
func profilePath() string {
	dir, err := os.Getwd()
	if err != nil {
		return ""
	}
	return dir + "/.lunchy"
}

// Check if profile file exists
func profileExists() bool {
	return fileExists(profilePath())
}

// Get daemon names specified in lunchy profile
func readProfile() []string {
	path := profilePath()
	if path == "" {
		return []string{}
	}

	buff, err := ioutil.ReadFile(path)
	if err != nil {
		return []string{}
	}

	result := []string{}
	lines := strings.Split(strings.TrimSpace(string(buff)), "\n")

	for _, l := range lines {
		result = append(result, strings.TrimSpace(l))
	}

	return result
}

func plistsAction(names []string, action string) {
	plists := getPlists()

	for _, name := range names {
		for _, plist := range plists {
			if strings.Index(plist, name) != -1 {
				switch action {
				case "start":
					startDaemon(plist)
				case "stop":
					stopDaemon(plist)
				case "restart":
					stopDaemon(plist)
					startDaemon(plist)
				}
			}
		}
	}
}

func startProfile() {
	fmt.Println("Starting daemons in profile:", profilePath())
	plistsAction(readProfile(), "start")
}

func stopProfile() {
	fmt.Println("Stopping daemons in profile:", profilePath())
	plistsAction(readProfile(), "stop")
}

func restartProfile() {
	fmt.Println("Restarting daemons in profile:", profilePath())
	plistsAction(readProfile(), "restart")
}

func fatal(message string) {
	fmt.Println(message)
	os.Exit(1)
}

func main() {
	args := os.Args

	if len(args) == 1 {
		printUsage()
		os.Exit(1)
	}

	switch args[1] {
	default:
		printUsage()
		os.Exit(1)
	case "help":
		printUsage()
		return
	case "list", "ls":
		printList()
		return
	case "status", "ps":
		printStatus(args)
		return
	case "start":
		startDaemons(args)
		return
	case "stop":
		stopDaemons(args)
		return
	case "restart":
		restartDaemons(args)
		return
	case "show":
		showPlist(args)
		return
	case "edit":
		editPlist(args)
		return
	case "install", "add":
		installPlist(args)
		return
	case "remove", "rm":
		removePlist(args)
		return
	case "scan":
		scanPath(args)
		return
	}
}
<commit_msg>Skip comment lines in profile file<commit_after>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	filepath "path"
	"strings"
)

const (
	LUNCHY_VERSION = "0.1.6"
)

func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

func fileCopy(src string, dst string) error {
	s, err := os.Open(src)
	if err != nil {
		return err
	}

	defer s.Close()

	d, err := os.Create(dst)
	if err != nil {
		return err
	}

	if _, err := io.Copy(d, s); err != nil {
		d.Close()
		return err
	}

	return d.Close()
}

func findPlists(path string) []string {
	output, err := exec.Command("find", path, "-name", "homebrew.*.plist", "-type", "f").Output()
	if err != nil {
		return []string{}
	}

	lines := strings.Split(strings.TrimSpace(string(output)), "\n")
	plists := []string{}

	for _, line := range lines {
		plists = append(plists, strings.Replace(filepath.Base(line), ".plist", "", 1))
	}

	return plists
}

func getPlists() []string {
	path := fmt.Sprintf("%s/Library/LaunchAgents", os.Getenv("HOME"))
	files := findPlists(path)

	return files
}

func getPlist(name string) string {
	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			return plist
		}
	}

	return ""
}

func sliceIncludes(slice []string, match string) bool {
	for _, val := range slice {
		if val == match {
			return true
		}
	}

	return false
}

func printUsage() {
	fmt.Printf("Lunchy %s, the friendly launchctl wrapper\n", LUNCHY_VERSION)
	fmt.Println("Usage: lunchy [start|stop|restart|list|status|install|show|edit|remove|scan] [options]")
}

func printList() {
	for _, file := range getPlists() {
		fmt.Println(file)
	}
}

func printStatus(args []string) {
	out, err := exec.Command("launchctl", "list").Output()

	if err != nil {
		fatal("failed to get process list")
	}

	pattern := ""

	if len(args) == 3 {
		pattern = args[2]
	}

	installed := getPlists()
	lines := strings.Split(strings.TrimSpace(string(out)), "\n")

	for _, line := range lines {
		chunks := strings.Split(line, "\t")
		clean_line := strings.Replace(line, "\t", " ", -1)

		if len(pattern) > 0 {
			if strings.Index(chunks[2], pattern) != -1 {
				if sliceIncludes(installed, chunks[2]) {
					fmt.Println(clean_line)
				}
			}
		} else {
			if sliceIncludes(installed, chunks[2]) {
				fmt.Println(clean_line)
			}
		}
	}
}

func exitWithInvalidArgs(args []string, msg string) {
	if len(args) < 3 {
		fmt.Println(msg)
		os.Exit(1)
	}
}

func startDaemons(args []string) {
	// Check if name pattern is not given and try profiles
	if len(args) == 2 {
		if profileExists() {
			startProfile()
			return
		} else {
			exitWithInvalidArgs(args, "name required")
		}
	}

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			startDaemon(plist)
		}
	}
}

func startDaemon(name string) {
	path := fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", os.Getenv("HOME"), name)
	_, err := exec.Command("launchctl", "load", path).Output()

	if err != nil {
		fmt.Println("failed to start", name)
		return
	}

	fmt.Println("started", name)
}

func stopDaemons(args []string) {
	// Check if name pattern is not given and try profiles
	if len(args) == 2 {
		if profileExists() {
			stopProfile()
			return
		} else {
			exitWithInvalidArgs(args, "name required")
		}
	}

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			stopDaemon(plist)
		}
	}
}

func stopDaemon(name string) {
	path := fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", os.Getenv("HOME"), name)
	_, err := exec.Command("launchctl", "unload", path).Output()

	if err != nil {
		fmt.Println("failed to stop", name)
		return
	}

	fmt.Println("stopped", name)
}

func restartDaemons(args []string) {
	// Check if name pattern is not given and try profiles
	if len(args) == 2 {
		if profileExists() {
			restartProfile()
			return
		} else {
			exitWithInvalidArgs(args, "name required")
		}
	}

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			stopDaemon(plist)
			startDaemon(plist)
		}
	}
}

func showPlist(args []string) {
	exitWithInvalidArgs(args, "name required")

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			printPlistContent(plist)
			return
		}
	}
}

func printPlistContent(name string) {
	path := fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", os.Getenv("HOME"), name)
	contents, err := ioutil.ReadFile(path)

	if err != nil {
		fatal("unable to read plist")
	}

	fmt.Printf(string(contents))
}

func editPlist(args []string) {
	exitWithInvalidArgs(args, "name required")

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			editPlistContent(plist)
			return
		}
	}
}

func editPlistContent(name string) {
	path := fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", os.Getenv("HOME"), name)
	editor := os.Getenv("EDITOR")

	if len(editor) == 0 {
		fatal("EDITOR environment variable is not set")
	}

	cmd := exec.Command(editor, path)

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout

	cmd.Start()
	cmd.Wait()
}

func installPlist(args []string) {
	exitWithInvalidArgs(args, "path required")

	path := args[2]

	if !fileExists(path) {
		fatal("source file does not exist")
	}

	info, _ := os.Stat(path)
	base_path := fmt.Sprintf("%s/%s", os.Getenv("HOME"), "Library/LaunchAgents")
	new_path := fmt.Sprintf("%s/%s", base_path, info.Name())

	if fileExists(new_path) && os.Remove(new_path) != nil {
		fatal("unable to delete existing plist")
	}

	if fileCopy(path, new_path) != nil {
		fatal("failed to copy file")
	}

	fmt.Println(path, "installed to", base_path)
}

func removePlist(args []string) {
	exitWithInvalidArgs(args, "name required")

	name := args[2]
	base_path := fmt.Sprintf("%s/%s", os.Getenv("HOME"), "Library/LaunchAgents")

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			path := fmt.Sprintf("%s/%s.plist", base_path, plist)

			if os.Remove(path) == nil {
				fmt.Println("removed", path)
			} else {
				fmt.Println("failed to remove", path)
			}
		}
	}
}

func scanPath(args []string) {
	path := fmt.Sprintf("%s/%s", os.Getenv("HOME"), "Library/LaunchAgents")

	if len(args) >= 3 {
		path = args[2]
	}

	// This is a handy override to find all homebrew-based lists
	if path == "homebrew" {
		path = "/usr/local/Cellar"
	}

	for _, f := range findPlists(path) {
		fmt.Println(f)
	}
}

// Get full path to lunchy profile file
func profilePath() string {
	dir, err := os.Getwd()
	if err != nil {
		return ""
	}
	return dir + "/.lunchy"
}

// Check if profile file exists
func profileExists() bool {
	return fileExists(profilePath())
}

// Get daemon names specified in lunchy profile
func readProfile() []string {
	path := profilePath()
	if path == "" {
		return []string{}
	}

	buff, err := ioutil.ReadFile(path)
	if err != nil {
		return []string{}
	}

	result := []string{}
	lines := strings.Split(strings.TrimSpace(string(buff)), "\n")

	for _, l := range lines {
		line := strings.TrimSpace(l)

		// Skip comments (starts with #)
		if line[0] == 35 {
			continue
		}

		result = append(result, line)
	}

	return result
}

func plistsAction(names []string, action string) {
	plists := getPlists()

	for _, name := range names {
		for _, plist := range plists {
			if strings.Index(plist, name) != -1 {
				switch action {
				case "start":
					startDaemon(plist)
				case "stop":
					stopDaemon(plist)
				case "restart":
					stopDaemon(plist)
					startDaemon(plist)
				}
			}
		}
	}
}

func startProfile() {
	fmt.Println("Starting daemons in profile:", profilePath())
	plistsAction(readProfile(), "start")
}

func stopProfile() {
	fmt.Println("Stopping daemons in profile:", profilePath())
	plistsAction(readProfile(), "stop")
}

func restartProfile() {
	fmt.Println("Restarting daemons in profile:", profilePath())
	plistsAction(readProfile(), "restart")
}

func fatal(message string) {
	fmt.Println(message)
	os.Exit(1)
}

func main() {
	args := os.Args

	if len(args) == 1 {
		printUsage()
		os.Exit(1)
	}

	switch args[1] {
	default:
		printUsage()
		os.Exit(1)
	case "help":
		printUsage()
		return
	case "list", "ls":
		printList()
		return
	case "status", "ps":
		printStatus(args)
		return
	case "start":
		startDaemons(args)
		return
	case "stop":
		stopDaemons(args)
		return
	case "restart":
		restartDaemons(args)
		return
	case "show":
		showPlist(args)
		return
	case "edit":
		editPlist(args)
		return
	case "install", "add":
		installPlist(args)
		return
	case "remove", "rm":
		removePlist(args)
		return
	case "scan":
		scanPath(args)
		return
	}
}
<|endoftext|>


<commit_before>// Package defaults is a collection of helpers to retrieve the SDK's default
// configuration and handlers.
//
// Generally this package shouldn't be used directly, but session.Session
// instead. This package is useful when you need to reset the defaults
// of a session or service client to the SDK defaults before setting
// additional parameters.
package defaults

import (
	"fmt"
	"net"
	"net/http"
	"net/url"
	"os"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/corehandlers"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
	"github.com/aws/aws-sdk-go/aws/credentials/endpointcreds"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/aws/endpoints"
	"github.com/aws/aws-sdk-go/aws/request"
)

// A Defaults provides a collection of default values for SDK clients.
type Defaults struct {
	Config   *aws.Config
	Handlers request.Handlers
}

// Get returns the SDK's default values with Config and handlers pre-configured.
func Get() Defaults {
	cfg := Config()
	handlers := Handlers()
	cfg.Credentials = CredChain(cfg, handlers)

	return Defaults{
		Config:   cfg,
		Handlers: handlers,
	}
}

// Config returns the default configuration without credentials.
// To retrieve a config with credentials also included use
// `defaults.Get().Config` instead.
//
// Generally you shouldn't need to use this method directly, but
// is available if you need to reset the configuration of an
// existing service client or session.
func Config() *aws.Config {
	return aws.NewConfig().
		WithCredentials(credentials.AnonymousCredentials).
		WithRegion(os.Getenv("AWS_REGION")).
		WithHTTPClient(http.DefaultClient).
		WithMaxRetries(aws.UseServiceDefaultRetries).
		WithLogger(aws.NewDefaultLogger()).
		WithLogLevel(aws.LogOff).
		WithEndpointResolver(endpoints.DefaultResolver())
}

// Handlers returns the default request handlers.
//
// Generally you shouldn't need to use this method directly, but
// is available if you need to reset the request handlers of an
// existing service client or session.
func Handlers() request.Handlers {
	var handlers request.Handlers

	handlers.Validate.PushBackNamed(corehandlers.ValidateEndpointHandler)
	handlers.Validate.AfterEachFn = request.HandlerListStopOnError
	handlers.Build.PushBackNamed(corehandlers.SDKVersionUserAgentHandler)
	handlers.Build.PushBackNamed(corehandlers.AddHostExecEnvUserAgentHander)
	handlers.Build.AfterEachFn = request.HandlerListStopOnError
	handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
	handlers.Send.PushBackNamed(corehandlers.ValidateReqSigHandler)
	handlers.Send.PushBackNamed(corehandlers.SendHandler)
	handlers.AfterRetry.PushBackNamed(corehandlers.AfterRetryHandler)
	handlers.ValidateResponse.PushBackNamed(corehandlers.ValidateResponseHandler)

	return handlers
}

// CredChain returns the default credential chain.
//
// Generally you shouldn't need to use this method directly, but
// is available if you need to reset the credentials of an
// existing service client or session's Config.
func CredChain(cfg *aws.Config, handlers request.Handlers) *credentials.Credentials {
	return credentials.NewCredentials(&credentials.ChainProvider{
		VerboseErrors: aws.BoolValue(cfg.CredentialsChainVerboseErrors),
		Providers: []credentials.Provider{
			&credentials.EnvProvider{},
			&credentials.SharedCredentialsProvider{Filename: "", Profile: ""},
			RemoteCredProvider(*cfg, handlers),
		},
	})
}

const (
	httpProviderEnvVar     = "AWS_CONTAINER_CREDENTIALS_FULL_URI"
	ecsCredsProviderEnvVar = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
)

// RemoteCredProvider returns a credentials provider for the default remote
// endpoints such as EC2 or ECS Roles.
func RemoteCredProvider(cfg aws.Config, handlers request.Handlers) credentials.Provider {
	if u := os.Getenv(httpProviderEnvVar); len(u) > 0 {
		return localHTTPCredProvider(cfg, handlers, u)
	}

	if uri := os.Getenv(ecsCredsProviderEnvVar); len(uri) > 0 {
		u := fmt.Sprintf("http://169.254.170.2%s", uri)
		return httpCredProvider(cfg, handlers, u)
	}

	return ec2RoleProvider(cfg, handlers)
}

var lookupHostFn = net.LookupHost

func isLoopbackHost(host string) (bool, error) {
	ip := net.ParseIP(host)
	if ip != nil {
		return ip.IsLoopback(), nil
	}

	// Host is not an ip, perform lookup
	addrs, err := lookupHostFn(host)
	if err != nil {
		return false, err
	}
	for _, addr := range addrs {
		if !net.ParseIP(addr).IsLoopback() {
			return false, nil
		}
	}

	return true, nil
}

func localHTTPCredProvider(cfg aws.Config, handlers request.Handlers, u string) credentials.Provider {
	var errMsg string

	parsed, err := url.Parse(u)
	if err != nil {
		errMsg = fmt.Sprintf("invalid URL, %v", err)
	} else {
		host := aws.URLHostname(parsed)
		if len(host) == 0 {
			errMsg = "unable to parse host from local HTTP cred provider URL"
		} else if isLoopback, loopbackErr := isLoopbackHost(host); loopbackErr != nil {
			errMsg = fmt.Sprintf("failed to resolve host %q, %v", host, loopbackErr)
		} else if !isLoopback {
			errMsg = fmt.Sprintf("invalid endpoint host, %q, only loopback hosts are allowed.", host)
		}
	}

	if len(errMsg) > 0 {
		if cfg.Logger != nil {
			cfg.Logger.Log("Ignoring, HTTP credential provider", errMsg, err)
		}
		return credentials.ErrorProvider{
			Err:          awserr.New("CredentialsEndpointError", errMsg, err),
			ProviderName: endpointcreds.ProviderName,
		}
	}

	return httpCredProvider(cfg, handlers, u)
}

func httpCredProvider(cfg aws.Config, handlers request.Handlers, u string) credentials.Provider {
	return endpointcreds.NewProviderClient(cfg, handlers, u,
		func(p *endpointcreds.Provider) {
			p.ExpiryWindow = 5 * time.Minute
		},
	)
}

func ec2RoleProvider(cfg aws.Config, handlers request.Handlers) credentials.Provider {
	resolver := cfg.EndpointResolver
	if resolver == nil {
		resolver = endpoints.DefaultResolver()
	}

	e, _ := resolver.EndpointFor(endpoints.Ec2metadataServiceID, "")
	return &ec2rolecreds.EC2RoleProvider{
		Client:       ec2metadata.NewClient(cfg, handlers, e.URL, e.SigningRegion),
		ExpiryWindow: 5 * time.Minute,
	}
}
<commit_msg>aws/default: Add helper to get default provider chain list of credential providers<commit_after>// Package defaults is a collection of helpers to retrieve the SDK's default
// configuration and handlers.
//
// Generally this package shouldn't be used directly, but session.Session
// instead. This package is useful when you need to reset the defaults
// of a session or service client to the SDK defaults before setting
// additional parameters.
package defaults

import (
	"fmt"
	"net"
	"net/http"
	"net/url"
	"os"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/corehandlers"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
	"github.com/aws/aws-sdk-go/aws/credentials/endpointcreds"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/aws/endpoints"
	"github.com/aws/aws-sdk-go/aws/request"
)

// A Defaults provides a collection of default values for SDK clients.
type Defaults struct {
	Config   *aws.Config
	Handlers request.Handlers
}

// Get returns the SDK's default values with Config and handlers pre-configured.
func Get() Defaults {
	cfg := Config()
	handlers := Handlers()
	cfg.Credentials = CredChain(cfg, handlers)

	return Defaults{
		Config:   cfg,
		Handlers: handlers,
	}
}

// Config returns the default configuration without credentials.
// To retrieve a config with credentials also included use
// `defaults.Get().Config` instead.
//
// Generally you shouldn't need to use this method directly, but
// is available if you need to reset the configuration of an
// existing service client or session.
func Config() *aws.Config {
	return aws.NewConfig().
		WithCredentials(credentials.AnonymousCredentials).
		WithRegion(os.Getenv("AWS_REGION")).
		WithHTTPClient(http.DefaultClient).
		WithMaxRetries(aws.UseServiceDefaultRetries).
		WithLogger(aws.NewDefaultLogger()).
		WithLogLevel(aws.LogOff).
		WithEndpointResolver(endpoints.DefaultResolver())
}

// Handlers returns the default request handlers.
//
// Generally you shouldn't need to use this method directly, but
// is available if you need to reset the request handlers of an
// existing service client or session.
func Handlers() request.Handlers {
	var handlers request.Handlers

	handlers.Validate.PushBackNamed(corehandlers.ValidateEndpointHandler)
	handlers.Validate.AfterEachFn = request.HandlerListStopOnError
	handlers.Build.PushBackNamed(corehandlers.SDKVersionUserAgentHandler)
	handlers.Build.PushBackNamed(corehandlers.AddHostExecEnvUserAgentHander)
	handlers.Build.AfterEachFn = request.HandlerListStopOnError
	handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
	handlers.Send.PushBackNamed(corehandlers.ValidateReqSigHandler)
	handlers.Send.PushBackNamed(corehandlers.SendHandler)
	handlers.AfterRetry.PushBackNamed(corehandlers.AfterRetryHandler)
	handlers.ValidateResponse.PushBackNamed(corehandlers.ValidateResponseHandler)

	return handlers
}

// CredChain returns the default credential chain.
//
// Generally you shouldn't need to use this method directly, but
// is available if you need to reset the credentials of an
// existing service client or session's Config.
func CredChain(cfg *aws.Config, handlers request.Handlers) *credentials.Credentials {
	return credentials.NewCredentials(&credentials.ChainProvider{
		VerboseErrors: aws.BoolValue(cfg.CredentialsChainVerboseErrors),
		Providers:     CredProviders(cfg, handlers),
	})
}

// CredProviders returns the slice of providers used in
// the default credential chain.
//
// For applications that need to use some other provider (for example use
// different  environment variables for legacy reasons) but still fall back
// on the default chain of providers. This allows that default chaint to be
// automatically updated
func CredProviders(cfg *aws.Config, handlers request.Handlers) []credentials.Provider {
	return []credentials.Provider{
		&credentials.EnvProvider{},
		&credentials.SharedCredentialsProvider{Filename: "", Profile: ""},
		RemoteCredProvider(*cfg, handlers),
	}
}

const (
	httpProviderEnvVar     = "AWS_CONTAINER_CREDENTIALS_FULL_URI"
	ecsCredsProviderEnvVar = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
)

// RemoteCredProvider returns a credentials provider for the default remote
// endpoints such as EC2 or ECS Roles.
func RemoteCredProvider(cfg aws.Config, handlers request.Handlers) credentials.Provider {
	if u := os.Getenv(httpProviderEnvVar); len(u) > 0 {
		return localHTTPCredProvider(cfg, handlers, u)
	}

	if uri := os.Getenv(ecsCredsProviderEnvVar); len(uri) > 0 {
		u := fmt.Sprintf("http://169.254.170.2%s", uri)
		return httpCredProvider(cfg, handlers, u)
	}

	return ec2RoleProvider(cfg, handlers)
}

var lookupHostFn = net.LookupHost

func isLoopbackHost(host string) (bool, error) {
	ip := net.ParseIP(host)
	if ip != nil {
		return ip.IsLoopback(), nil
	}

	// Host is not an ip, perform lookup
	addrs, err := lookupHostFn(host)
	if err != nil {
		return false, err
	}
	for _, addr := range addrs {
		if !net.ParseIP(addr).IsLoopback() {
			return false, nil
		}
	}

	return true, nil
}

func localHTTPCredProvider(cfg aws.Config, handlers request.Handlers, u string) credentials.Provider {
	var errMsg string

	parsed, err := url.Parse(u)
	if err != nil {
		errMsg = fmt.Sprintf("invalid URL, %v", err)
	} else {
		host := aws.URLHostname(parsed)
		if len(host) == 0 {
			errMsg = "unable to parse host from local HTTP cred provider URL"
		} else if isLoopback, loopbackErr := isLoopbackHost(host); loopbackErr != nil {
			errMsg = fmt.Sprintf("failed to resolve host %q, %v", host, loopbackErr)
		} else if !isLoopback {
			errMsg = fmt.Sprintf("invalid endpoint host, %q, only loopback hosts are allowed.", host)
		}
	}

	if len(errMsg) > 0 {
		if cfg.Logger != nil {
			cfg.Logger.Log("Ignoring, HTTP credential provider", errMsg, err)
		}
		return credentials.ErrorProvider{
			Err:          awserr.New("CredentialsEndpointError", errMsg, err),
			ProviderName: endpointcreds.ProviderName,
		}
	}

	return httpCredProvider(cfg, handlers, u)
}

func httpCredProvider(cfg aws.Config, handlers request.Handlers, u string) credentials.Provider {
	return endpointcreds.NewProviderClient(cfg, handlers, u,
		func(p *endpointcreds.Provider) {
			p.ExpiryWindow = 5 * time.Minute
		},
	)
}

func ec2RoleProvider(cfg aws.Config, handlers request.Handlers) credentials.Provider {
	resolver := cfg.EndpointResolver
	if resolver == nil {
		resolver = endpoints.DefaultResolver()
	}

	e, _ := resolver.EndpointFor(endpoints.Ec2metadataServiceID, "")
	return &ec2rolecreds.EC2RoleProvider{
		Client:       ec2metadata.NewClient(cfg, handlers, e.URL, e.SigningRegion),
		ExpiryWindow: 5 * time.Minute,
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017-2018 Alibaba Group Holding Limited
 */
package openapi

import (
	"fmt"
	"github.com/aliyun/aliyun-cli/cli"
	"github.com/aliyun/aliyun-cli/i18n"
	"github.com/aliyun/aliyun-cli/meta"
	"github.com/aliyun/aliyun-cli/resource"
	"io"
	"strings"
	"text/tabwriter"
)

type Library struct {
	lang        string
	builtinRepo *meta.Repository
	extraRepo   *meta.Repository
	writer      io.Writer
}

func NewLibrary(w io.Writer, lang string) *Library {
	return &Library{
		builtinRepo: meta.LoadRepository(resource.NewReader()),
		extraRepo:   nil,
		lang:        lang,
		writer:      w,
	}
}

func (a *Library) GetProduct(productCode string) (meta.Product, bool) {
	return a.builtinRepo.GetProduct(productCode)
}

func (a *Library) GetApi(productCode string, version string, apiName string) (meta.Api, bool) {
	return a.builtinRepo.GetApi(productCode, version, apiName)
}

func (a *Library) GetProducts() []meta.Product {
	return a.builtinRepo.Products
}

func (a *Library) PrintProducts() {
	w := tabwriter.NewWriter(a.writer, 8, 0, 1, ' ', 0)
	cli.PrintfWithColor(w, cli.ColorOff,"\nProducts:\n")
	for _, product := range a.builtinRepo.Products {
		cli.PrintfWithColor(w, cli.Cyan,"  %s\t%s\n", strings.ToLower(product.Code), product.Name[i18n.GetLanguage()])
	}
	w.Flush()
}

func (a *Library) printProduct(product meta.Product) {
	cli.Printf(a.writer, "  %s(%s)\t%s\t%s\n", product.Code, product.Version, product.Name["zh"],
		product.GetDocumentLink("zh"))
}

func (a *Library) PrintProductUsage(productCode string, withApi bool) error {
	product, ok := a.GetProduct(productCode)
	if !ok {
		return &InvalidProductError{Code: productCode, library: a}
	}

	if product.ApiStyle == "rpc" {
		cli.Printf(a.writer, "\nUsage:\n  aliyun %s <ApiName> --parameter1 value1 --parameter2 value2 ...\n", product.Code)
	} else {
		withApi = false
		cli.Printf(a.writer, "\nUsage:\n  aliyun %s [GET|PUT|POST|DELETE] <PathPattern> --body \"...\" \n", product.Code)
	}

	cli.Printf(a.writer, "\nProduct: %s (%s)\n", product.Code, product.Name[i18n.GetLanguage()])
	cli.Printf(a.writer, "Version: %s \n", product.Version)
	cli.Printf(a.writer, "Link: %s\n", product.GetDocumentLink(i18n.GetLanguage()))

	if withApi {
		cli.PrintfWithColor(a.writer, cli.ColorOff,"\nAvailable Api List: \n")
		for _, apiName := range product.ApiNames {
			cli.PrintfWithColor(a.writer, cli.Green,"  %s\n", apiName)
		}
		// TODO some ApiName is too long, two column not seems good
		//w := tabwriter.NewWriter(cli.GetOutputWriter(), 8, 0, 1, ' ', 0)
		//for i := 0; i < len(product.ApiNames); i += 2 {
		//	name1 := product.ApiNames[i]
		//	name2 := ""
		//	if i + 1 < len(product.ApiNames) {
		//		name2 = product.ApiNames[i + 1]
		//	}
		//	fmt.Fprintf(w, "  %s\t%s\n", name1, name2)
		//}
		//w.Flush()
	}

	cli.Printf(a.writer, "\nRun `aliyun %s <ApiName> --help` to get more information about api\n", product.GetLowerCode())
	return nil
}

func (a *Library) PrintApiUsage(productCode string, apiName string) error {
	product, ok := a.builtinRepo.GetProduct(productCode)
	if !ok {
		return &InvalidProductError{Code: productCode, library: a}
	}
	api, ok := a.builtinRepo.GetApi(productCode, product.Version, apiName)
	if !ok {
		return &InvalidApiError{Name: apiName, product: &product}
	}

	cli.Printf(a.writer, "\nProduct: %s (%s)\n", product.Code, product.Name[i18n.GetLanguage()])
	// cli.Printf("Api: %s %s\n", api.Name, api.Description[i18n.GetLanguage()])
	cli.Printf(a.writer, "Link:    %s\n", api.GetDocumentLink())
	cli.Printf(a.writer, "\nParameters:\n")

	w := tabwriter.NewWriter(a.writer, 8, 0, 1, ' ', 0)
	printParameters(w, api.Parameters, "")
	w.Flush()

	return nil
}

func printParameters(w io.Writer, params []meta.Parameter, prefix string) {
	for _, param := range params {
		if param.Hidden {
			continue
		}
		if len(param.SubParameters) > 0 {
			printParameters(w, param.SubParameters, param.Name+".n.")
			//for _, sp := range param.SubParameters {
			//	fmt.Fprintf(w,"  --%s.n.%s\t%s\t%s\n", param.Name, sp.Name, sp.Type, required(sp.Required))
			//}
		} else if param.Type == "RepeatList" {
			fmt.Fprintf(w, "  --%s%s.n\t%s\t%s\t%s\n", prefix, param.Name, param.Type, required(param.Required), getDescription(param.Description))
		} else {
			fmt.Fprintf(w, "  --%s%s\t%s\t%s\t%s\n", prefix, param.Name, param.Type, required(param.Required), getDescription(param.Description))
		}
	}
}

func required(r bool) string {
	if r {
		return "Required"
	} else {
		return "Optional"
	}
}

func getDescription(d map[string]string) string {
	return ""
	// TODO: description too long, need optimize for display
	//if d == nil {
	//	return ""
	//}
	//if v, ok := d[i18n.GetLanguage()]; ok {
	//	return v
	//} else {
	//	return ""
	//}
}

//
//func (a *Helper) printCompactList() {
//	for _, s := range compactList {
//		product, _ := c.products.GetProduct(s)
//		c.PrintProduct(product)
//	}
//	cli.Printf("  ... ")
//}
<commit_msg>Updated wording in help message<commit_after>/*
 * Copyright (C) 2017-2018 Alibaba Group Holding Limited
 */
package openapi

import (
	"fmt"
	"github.com/aliyun/aliyun-cli/cli"
	"github.com/aliyun/aliyun-cli/i18n"
	"github.com/aliyun/aliyun-cli/meta"
	"github.com/aliyun/aliyun-cli/resource"
	"io"
	"strings"
	"text/tabwriter"
)

type Library struct {
	lang        string
	builtinRepo *meta.Repository
	extraRepo   *meta.Repository
	writer      io.Writer
}

func NewLibrary(w io.Writer, lang string) *Library {
	return &Library{
		builtinRepo: meta.LoadRepository(resource.NewReader()),
		extraRepo:   nil,
		lang:        lang,
		writer:      w,
	}
}

func (a *Library) GetProduct(productCode string) (meta.Product, bool) {
	return a.builtinRepo.GetProduct(productCode)
}

func (a *Library) GetApi(productCode string, version string, apiName string) (meta.Api, bool) {
	return a.builtinRepo.GetApi(productCode, version, apiName)
}

func (a *Library) GetProducts() []meta.Product {
	return a.builtinRepo.Products
}

func (a *Library) PrintProducts() {
	w := tabwriter.NewWriter(a.writer, 8, 0, 1, ' ', 0)
	cli.PrintfWithColor(w, cli.ColorOff,"\nProducts:\n")
	for _, product := range a.builtinRepo.Products {
		cli.PrintfWithColor(w, cli.Cyan,"  %s\t%s\n", strings.ToLower(product.Code), product.Name[i18n.GetLanguage()])
	}
	w.Flush()
}

func (a *Library) printProduct(product meta.Product) {
	cli.Printf(a.writer, "  %s(%s)\t%s\t%s\n", product.Code, product.Version, product.Name["zh"],
		product.GetDocumentLink("zh"))
}

func (a *Library) PrintProductUsage(productCode string, withApi bool) error {
	product, ok := a.GetProduct(productCode)
	if !ok {
		return &InvalidProductError{Code: productCode, library: a}
	}

	if product.ApiStyle == "rpc" {
		cli.Printf(a.writer, "\nUsage:\n  aliyun %s <ApiName> --parameter1 value1 --parameter2 value2 ...\n", product.Code)
	} else {
		withApi = false
		cli.Printf(a.writer, "\nUsage:\n  aliyun %s [GET|PUT|POST|DELETE] <PathPattern> --body \"...\" \n", product.Code)
	}

	cli.Printf(a.writer, "\nProduct: %s (%s)\n", product.Code, product.Name[i18n.GetLanguage()])
	cli.Printf(a.writer, "Version: %s \n", product.Version)
	cli.Printf(a.writer, "Link: %s\n", product.GetDocumentLink(i18n.GetLanguage()))

	if withApi {
		cli.PrintfWithColor(a.writer, cli.ColorOff,"\nAvailable Api List: \n")
		for _, apiName := range product.ApiNames {
			cli.PrintfWithColor(a.writer, cli.Green,"  %s\n", apiName)
		}
		// TODO some ApiName is too long, two column not seems good
		//w := tabwriter.NewWriter(cli.GetOutputWriter(), 8, 0, 1, ' ', 0)
		//for i := 0; i < len(product.ApiNames); i += 2 {
		//	name1 := product.ApiNames[i]
		//	name2 := ""
		//	if i + 1 < len(product.ApiNames) {
		//		name2 = product.ApiNames[i + 1]
		//	}
		//	fmt.Fprintf(w, "  %s\t%s\n", name1, name2)
		//}
		//w.Flush()
	}

	cli.Printf(a.writer, "\nRun `aliyun %s <ApiName> --help` to get more information about this API\n", product.GetLowerCode())
	return nil
}

func (a *Library) PrintApiUsage(productCode string, apiName string) error {
	product, ok := a.builtinRepo.GetProduct(productCode)
	if !ok {
		return &InvalidProductError{Code: productCode, library: a}
	}
	api, ok := a.builtinRepo.GetApi(productCode, product.Version, apiName)
	if !ok {
		return &InvalidApiError{Name: apiName, product: &product}
	}

	cli.Printf(a.writer, "\nProduct: %s (%s)\n", product.Code, product.Name[i18n.GetLanguage()])
	// cli.Printf("Api: %s %s\n", api.Name, api.Description[i18n.GetLanguage()])
	cli.Printf(a.writer, "Link:    %s\n", api.GetDocumentLink())
	cli.Printf(a.writer, "\nParameters:\n")

	w := tabwriter.NewWriter(a.writer, 8, 0, 1, ' ', 0)
	printParameters(w, api.Parameters, "")
	w.Flush()

	return nil
}

func printParameters(w io.Writer, params []meta.Parameter, prefix string) {
	for _, param := range params {
		if param.Hidden {
			continue
		}
		if len(param.SubParameters) > 0 {
			printParameters(w, param.SubParameters, param.Name+".n.")
			//for _, sp := range param.SubParameters {
			//	fmt.Fprintf(w,"  --%s.n.%s\t%s\t%s\n", param.Name, sp.Name, sp.Type, required(sp.Required))
			//}
		} else if param.Type == "RepeatList" {
			fmt.Fprintf(w, "  --%s%s.n\t%s\t%s\t%s\n", prefix, param.Name, param.Type, required(param.Required), getDescription(param.Description))
		} else {
			fmt.Fprintf(w, "  --%s%s\t%s\t%s\t%s\n", prefix, param.Name, param.Type, required(param.Required), getDescription(param.Description))
		}
	}
}

func required(r bool) string {
	if r {
		return "Required"
	} else {
		return "Optional"
	}
}

func getDescription(d map[string]string) string {
	return ""
	// TODO: description too long, need optimize for display
	//if d == nil {
	//	return ""
	//}
	//if v, ok := d[i18n.GetLanguage()]; ok {
	//	return v
	//} else {
	//	return ""
	//}
}

//
//func (a *Helper) printCompactList() {
//	for _, s := range compactList {
//		product, _ := c.products.GetProduct(s)
//		c.PrintProduct(product)
//	}
//	cli.Printf("  ... ")
//}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/ghodss/yaml"
	. "github.com/smartystreets/goconvey/convey"
)

// TODO: add tests that use erroneous config.
// Will likely require injecting custom logger and intercepting error msgs.

// See https://elithrar.github.io/article/testing-http-handlers-go/ for comments.
func TestIndexHandler(t *testing.T) {
	Convey("Given app is set up with default config", t, func() {
		c, err := loadTestYaml()
		So(err, ShouldBeNil)
		context := &context{config: c}
		appHandler := &ctxWrapper{context, IndexHandler}
		handler := http.Handler(appHandler)
		Convey("When we GET http://g/z", func() {
			req, err := http.NewRequest("GET", "/z", nil)
			So(err, ShouldBeNil)
			req.Host = "g"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://github.com/issmirnov/zap", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://github.com/issmirnov/zap")
			})
		})
		Convey("When we GET http://g/z/", func() {
			req, err := http.NewRequest("GET", "/z/", nil)
			So(err, ShouldBeNil)
			req.Host = "g"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://github.com/issmirnov/zap/", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://github.com/issmirnov/zap/")
			})
		})
		Convey("When we GET http://g/z/very/deep/path", func() {
			req, err := http.NewRequest("GET", "/z/very/deep/path", nil)
			So(err, ShouldBeNil)
			req.Host = "g"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://github.com/issmirnov/zap/very/deep/path", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://github.com/issmirnov/zap/very/deep/path")
			})
		})
		Convey("When we GET http://g/z/very/deep/path/", func() {
			req, err := http.NewRequest("GET", "/z/very/deep/path/", nil)
			So(err, ShouldBeNil)
			req.Host = "g"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://github.com/issmirnov/zap/very/deep/path/", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://github.com/issmirnov/zap/very/deep/path/")
			})
		})
		Convey("When we GET http://g/", func() {
			req, err := http.NewRequest("GET", "/", nil)
			So(err, ShouldBeNil)
			req.Host = "g"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://github.com/", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://github.com/")
			})
		})
		Convey("When we GET http://fake/path", func() {
			req, err := http.NewRequest("GET", "/path", nil)
			So(err, ShouldBeNil)
			req.Host = "fake"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 404", func() {
				So(rr.Code, ShouldEqual, http.StatusNotFound)
			})
		})
		Convey("When we GET http://g/s/", func() {
			req, err := http.NewRequest("GET", "/s/", nil)
			So(err, ShouldBeNil)
			req.Host = "g"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://github.com/search?q=", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://github.com/search?q=")
			})
		})
		Convey("When we GET http://g/s/foo", func() {
			req, err := http.NewRequest("GET", "/s/foo", nil)
			So(err, ShouldBeNil)
			req.Host = "g"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://github.com/search?q=foo", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://github.com/search?q=foo")
			})
		})
		Convey("When we GET http://g/s", func() {
			req, err := http.NewRequest("GET", "/s", nil)
			So(err, ShouldBeNil)
			req.Host = "g"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://github.com/search?q=", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://github.com/search?q=")
			})
		})
		Convey("When we GET http://z/ with ssl_off", func() {
			req, err := http.NewRequest("GET", "/", nil)
			So(err, ShouldBeNil)
			req.Host = "z"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to http://zero.com/", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "http://zero.com/")
			})
		})
		Convey("When we GET http://zz/ with ssl_off: no ", func() {
			req, err := http.NewRequest("GET", "/", nil)
			So(err, ShouldBeNil)
			req.Host = "zz"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://zero.ssl.on.com", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://zero.ssl.on.com/")
			})
		})

		Convey("When we GET http://l/a with ssl_off ", func() {
			req, err := http.NewRequest("GET", "/a", nil)
			So(err, ShouldBeNil)
			req.Host = "l"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to http://localhost:8080", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "http://localhost:8080")
			})
		})

		Convey("When we GET http://l/a/ with ssl_off ", func() {
			req, err := http.NewRequest("GET", "/a/", nil)
			So(err, ShouldBeNil)
			req.Host = "l"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to http://localhost:8080/", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "http://localhost:8080/")
			})
		})
		Convey("When we GET http://l/a/s with ssl_off", func() {
			req, err := http.NewRequest("GET", "/a/s", nil)
			So(err, ShouldBeNil)
			req.Host = "l"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to http://localhost:8080/service", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "http://localhost:8080/service")
			})
		})
		Convey("When we GET http://l/a/s/ with ssl_off", func() {
			req, err := http.NewRequest("GET", "/a/s/", nil)
			So(err, ShouldBeNil)
			req.Host = "l"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to http://localhost:8080/service/", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "http://localhost:8080/service/")
			})
		})

	})
}

// BenchmarkIndexHandler tests request processing geed when context is preloaded.
// Run with go test -run=BenchmarkIndexHandler -bench=. // results: 500000x	2555 ns/op
func BenchmarkIndexHandler(b *testing.B) {
	c, _ := loadTestYaml()
	context := &context{config: c}
	appHandler := &ctxWrapper{context, IndexHandler}
	handler := http.Handler(appHandler)
	req, _ := http.NewRequest("GET", "/z", nil)
	req.Host = "g"
	rr := httptest.NewRecorder()
	for n := 0; n < b.N; n++ {
		handler.ServeHTTP(rr, req)
	}
}

func TestHealthCheckHandler(t *testing.T) {
	Convey("When we GET /healthz", t, func() {
		req, err := http.NewRequest("GET", "/healthz", nil)
		So(err, ShouldBeNil)
		req.Host = "sd"

		// We create a ResponseWriter (which satisfies http.ResponseWriter) to record the response.
		rr := httptest.NewRecorder()
		handler := http.HandlerFunc(HealthHandler)
		handler.ServeHTTP(rr, req)

		Convey("We should get a 200", func() {
			So(rr.Code, ShouldEqual, http.StatusOK)
			So(rr.Body.String(), ShouldEqual, "OK")
		})
	})
}

func TestVarzHandler(t *testing.T) {
	Convey("Given app is set up with default config", t, func() {
		c, err := loadTestYaml()
		So(err, ShouldBeNil)
		context := &context{config: c}

		appHandler := &ctxWrapper{context, VarsHandler}
		handler := http.Handler(appHandler)
		Convey("When we GET /varz", func() {
			req, err := http.NewRequest("GET", "/varz", nil)
			So(err, ShouldBeNil)
			req.Host = "sd"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("We should get a 200", func() {
				So(rr.Code, ShouldEqual, http.StatusOK)
			})
			Convey("It should be valid json", func() {
				_, err := yaml.YAMLToJSON(rr.Body.Bytes())
				So(err, ShouldBeNil)
			})
			Convey("It should equal the config file", func() {
				conf, err := yaml.YAMLToJSON(c.Bytes())
				So(err, ShouldBeNil) // sanity check.

				resp, err := yaml.YAMLToJSON(rr.Body.Bytes())
				So(err, ShouldBeNil) // sanity check.

				// We get a nicely formatted response, but when we feed it into YAMLToJSON it collapses our nice
				// newlines. As a result, directly comparing the byte arrays here is a nogo. Therefore, we cheat
				// and utilize the separately tested jsonPrettyPrint to idempotently indent the JSON and compare that.
				// This does not work: "So(resp, ShouldEqual, []byte(jsonPrettyPrint(string(conf))))"
				So(jsonPrettyPrint(string(resp)), ShouldEqual, jsonPrettyPrint(string(conf)))
			})
		})
	})
}
<commit_msg>Add test for "X-Forwarded-Host" (#23)<commit_after>package main

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/ghodss/yaml"
	. "github.com/smartystreets/goconvey/convey"
)

// TODO: add tests that use erroneous config.
// Will likely require injecting custom logger and intercepting error msgs.

// See https://elithrar.github.io/article/testing-http-handlers-go/ for comments.
func TestIndexHandler(t *testing.T) {
	Convey("Given app is set up with default config", t, func() {
		c, err := loadTestYaml()
		So(err, ShouldBeNil)
		context := &context{config: c}
		appHandler := &ctxWrapper{context, IndexHandler}
		handler := http.Handler(appHandler)
		Convey("When we GET http://g/z", func() {
			req, err := http.NewRequest("GET", "/z", nil)
			So(err, ShouldBeNil)
			req.Host = "g"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://github.com/issmirnov/zap", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://github.com/issmirnov/zap")
			})
		})
		Convey("When we GET http://g/z/", func() {
			req, err := http.NewRequest("GET", "/z/", nil)
			So(err, ShouldBeNil)
			req.Host = "g"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://github.com/issmirnov/zap/", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://github.com/issmirnov/zap/")
			})
		})
		Convey("When we GET http://g/z/ with 'X-Forwarded-Host' set", func() {
			req, err := http.NewRequest("GET", "/z/", nil)
			So(err, ShouldBeNil)
			req.Header = map[string][]string{"X-Forwarded-Host": {"g"}}

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://github.com/issmirnov/zap/", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://github.com/issmirnov/zap/")
			})
		})
		Convey("When we GET http://g/z/very/deep/path", func() {
			req, err := http.NewRequest("GET", "/z/very/deep/path", nil)
			So(err, ShouldBeNil)
			req.Host = "g"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://github.com/issmirnov/zap/very/deep/path", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://github.com/issmirnov/zap/very/deep/path")
			})
		})
		Convey("When we GET http://g/z/very/deep/path/", func() {
			req, err := http.NewRequest("GET", "/z/very/deep/path/", nil)
			So(err, ShouldBeNil)
			req.Host = "g"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://github.com/issmirnov/zap/very/deep/path/", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://github.com/issmirnov/zap/very/deep/path/")
			})
		})
		Convey("When we GET http://g/", func() {
			req, err := http.NewRequest("GET", "/", nil)
			So(err, ShouldBeNil)
			req.Host = "g"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://github.com/", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://github.com/")
			})
		})
		Convey("When we GET http://fake/path", func() {
			req, err := http.NewRequest("GET", "/path", nil)
			So(err, ShouldBeNil)
			req.Host = "fake"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 404", func() {
				So(rr.Code, ShouldEqual, http.StatusNotFound)
			})
		})
		Convey("When we GET http://g/s/", func() {
			req, err := http.NewRequest("GET", "/s/", nil)
			So(err, ShouldBeNil)
			req.Host = "g"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://github.com/search?q=", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://github.com/search?q=")
			})
		})
		Convey("When we GET http://g/s/foo", func() {
			req, err := http.NewRequest("GET", "/s/foo", nil)
			So(err, ShouldBeNil)
			req.Host = "g"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://github.com/search?q=foo", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://github.com/search?q=foo")
			})
		})
		Convey("When we GET http://g/s", func() {
			req, err := http.NewRequest("GET", "/s", nil)
			So(err, ShouldBeNil)
			req.Host = "g"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://github.com/search?q=", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://github.com/search?q=")
			})
		})
		Convey("When we GET http://z/ with ssl_off", func() {
			req, err := http.NewRequest("GET", "/", nil)
			So(err, ShouldBeNil)
			req.Host = "z"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to http://zero.com/", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "http://zero.com/")
			})
		})
		Convey("When we GET http://zz/ with ssl_off: no ", func() {
			req, err := http.NewRequest("GET", "/", nil)
			So(err, ShouldBeNil)
			req.Host = "zz"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to https://zero.ssl.on.com", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "https://zero.ssl.on.com/")
			})
		})

		Convey("When we GET http://l/a with ssl_off ", func() {
			req, err := http.NewRequest("GET", "/a", nil)
			So(err, ShouldBeNil)
			req.Host = "l"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to http://localhost:8080", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "http://localhost:8080")
			})
		})

		Convey("When we GET http://l/a/ with ssl_off ", func() {
			req, err := http.NewRequest("GET", "/a/", nil)
			So(err, ShouldBeNil)
			req.Host = "l"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to http://localhost:8080/", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "http://localhost:8080/")
			})
		})
		Convey("When we GET http://l/a/s with ssl_off", func() {
			req, err := http.NewRequest("GET", "/a/s", nil)
			So(err, ShouldBeNil)
			req.Host = "l"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to http://localhost:8080/service", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "http://localhost:8080/service")
			})
		})
		Convey("When we GET http://l/a/s/ with ssl_off", func() {
			req, err := http.NewRequest("GET", "/a/s/", nil)
			So(err, ShouldBeNil)
			req.Host = "l"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("The result should be a 302 to http://localhost:8080/service/", func() {
				So(rr.Code, ShouldEqual, http.StatusFound)
				So(rr.Header().Get("Location"), ShouldEqual, "http://localhost:8080/service/")
			})
		})

	})
}

// BenchmarkIndexHandler tests request processing geed when context is preloaded.
// Run with go test -run=BenchmarkIndexHandler -bench=. // results: 500000x	2555 ns/op
func BenchmarkIndexHandler(b *testing.B) {
	c, _ := loadTestYaml()
	context := &context{config: c}
	appHandler := &ctxWrapper{context, IndexHandler}
	handler := http.Handler(appHandler)
	req, _ := http.NewRequest("GET", "/z", nil)
	req.Host = "g"
	rr := httptest.NewRecorder()
	for n := 0; n < b.N; n++ {
		handler.ServeHTTP(rr, req)
	}
}

func TestHealthCheckHandler(t *testing.T) {
	Convey("When we GET /healthz", t, func() {
		req, err := http.NewRequest("GET", "/healthz", nil)
		So(err, ShouldBeNil)
		req.Host = "sd"

		// We create a ResponseWriter (which satisfies http.ResponseWriter) to record the response.
		rr := httptest.NewRecorder()
		handler := http.HandlerFunc(HealthHandler)
		handler.ServeHTTP(rr, req)

		Convey("We should get a 200", func() {
			So(rr.Code, ShouldEqual, http.StatusOK)
			So(rr.Body.String(), ShouldEqual, "OK")
		})
	})
}

func TestVarzHandler(t *testing.T) {
	Convey("Given app is set up with default config", t, func() {
		c, err := loadTestYaml()
		So(err, ShouldBeNil)
		context := &context{config: c}

		appHandler := &ctxWrapper{context, VarsHandler}
		handler := http.Handler(appHandler)
		Convey("When we GET /varz", func() {
			req, err := http.NewRequest("GET", "/varz", nil)
			So(err, ShouldBeNil)
			req.Host = "sd"

			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			Convey("We should get a 200", func() {
				So(rr.Code, ShouldEqual, http.StatusOK)
			})
			Convey("It should be valid json", func() {
				_, err := yaml.YAMLToJSON(rr.Body.Bytes())
				So(err, ShouldBeNil)
			})
			Convey("It should equal the config file", func() {
				conf, err := yaml.YAMLToJSON(c.Bytes())
				So(err, ShouldBeNil) // sanity check.

				resp, err := yaml.YAMLToJSON(rr.Body.Bytes())
				So(err, ShouldBeNil) // sanity check.

				// We get a nicely formatted response, but when we feed it into YAMLToJSON it collapses our nice
				// newlines. As a result, directly comparing the byte arrays here is a nogo. Therefore, we cheat
				// and utilize the separately tested jsonPrettyPrint to idempotently indent the JSON and compare that.
				// This does not work: "So(resp, ShouldEqual, []byte(jsonPrettyPrint(string(conf))))"
				So(jsonPrettyPrint(string(resp)), ShouldEqual, jsonPrettyPrint(string(conf)))
			})
		})
	})
}
<|endoftext|>


<commit_before>package main

const (
	HTML_TMPL = `
<!doctype html>

<title>Slides</title>

{{data}}

`
	STYLESHEET = `
* { border: 0; margin: 0; padding: 0; box-sizing: border-box; }
body { background-color: #ffffea; }
#slides {
	display: flex;
	flex-flow: row nowrap;
}
p {
	flex: none;
	width: 100vw;
	height: 100vh;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 24pt;
	font-family: monospace;
	line-height: 1.7;
	text-align: left;
	padding: 1em;
}

.slide {
	display: none;
}
`
	JAVASCRIPT = `
var slideIndex = 0;
showSlides(slideIndex);

function plusSlides(n) {
  showSlides(slideIndex += n);
}

function currentSlide(n) {
  showSlides(slideIndex = n);
}

function showSlides(n) {
  var i;
  var slides = document.getElementsByTagName("p");
//  var dots = document.getElementsByClassName("dot");
  if (n > slides.length-1) {slideIndex = 0}
  if (n < 0) {slideIndex = slides.length-1}
  for (i = 0; i < slides.length; i++) {
      slides[i].style.display = "none";
      slides[i].onclick = function (){
        plusSlides(1);
      };
  }
//  for (i = 0; i < dots.length; i++) {
//      dots[i].className = dots[i].className.replace(" active", "");
//  }
  console.log(slideIndex);
  slides[slideIndex].style.display = "flex";
//  dots[slideIndex].className += " active";
}

window.onkeydown = function (e) {
  var e=window.event || e;
  // left, up, right, down: 37 38 39 40
  switch (e.keyCode) {
    case 37:
    case 38:
      plusSlides(-1);
      break;
    case 39:
    case 40:
      plusSlides(1);
      break;
  }
};
`
)
<commit_msg>Do not wrap around when outside begin/end.<commit_after>package main

const (
	HTML_TMPL = `
<!doctype html>

<title>Slides</title>

{{data}}

`
	STYLESHEET = `
* { border: 0; margin: 0; padding: 0; box-sizing: border-box; }
body { background-color: #ffffea; }
#slides {
	display: flex;
	flex-flow: row nowrap;
}
p {
	flex: none;
	width: 100vw;
	height: 100vh;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 24pt;
	font-family: monospace;
	line-height: 1.7;
	text-align: left;
	padding: 1em;
}

.slide {
	display: none;
}
`
	JAVASCRIPT = `
var slideIndex = 0;
showSlides(slideIndex);

function plusSlides(n) {
  showSlides(slideIndex += n);
}

function currentSlide(n) {
  showSlides(slideIndex = n);
}

function showSlides(n) {
  var i;
  var slides = document.getElementsByTagName("p");
//  var dots = document.getElementsByClassName("dot");
  if (n > slides.length-1) {slideIndex = slides.length-1}
  if (n < 0) {slideIndex = 0}
  for (i = 0; i < slides.length; i++) {
      slides[i].style.display = "none";
      slides[i].onclick = function (){
        plusSlides(1);
      };
  }
//  for (i = 0; i < dots.length; i++) {
//      dots[i].className = dots[i].className.replace(" active", "");
//  }
  console.log(slideIndex);
  slides[slideIndex].style.display = "flex";
//  dots[slideIndex].className += " active";
}

window.onkeydown = function (e) {
  var e=window.event || e;
  // left, up, right, down: 37 38 39 40
  switch (e.keyCode) {
    case 37:
    case 38:
      plusSlides(-1);
      break;
    case 39:
    case 40:
      plusSlides(1);
      break;
  }
};
`
)
<|endoftext|>


<commit_before>// The options package includes some default option parsers for the
// requests package.
package options

import (
	"errors"
	"reflect"
)

// Required is an option func that ensures a non-nil value was passed
// along in the request.  It does not ensure that the value is
// non-empty.
func Required(orig, value interface{}, optionValue string) (interface{}, error) {
	if optionValue == "true" {
		if value == nil {
			return nil, errors.New("Required value has nil input")
		}
	}
	return value, nil
}

// Default is an option func that sets a default value for a field.
// If the value doesn't exist in the request (or is nil), the provided
// default will be used instead.
func Default(orig, value interface{}, optionValue string) (interface{}, error) {
	if value == nil {
		if optionValue != "" {
			// This is a string type, but we'll leave it up to the
			// unmarshal process (or the Receiver's Receive method) to
			// convert it.
			return optionValue, nil
		}
	}
	return value, nil
}

// changeReceiver is just a clone of requests.ChangeReceiver, since we can't
// import requests in this package.
type changeReceiver interface {
	Receive(interface{}) (bool, error)
}

// receiver is a clone of requests.Receiver, similar to changeReceiver.
type receiver interface {
	Receive(interface{}) error
}

func isPtrOrInter(t reflect.Type) bool {
	return t.Kind() == reflect.Ptr || t.Kind() == reflect.Interface
}

// zeroOrEqual checks whether orig is either the zero value of its type or
// loosely equal to value.  The main utility here is that zeroOrEqual(4.0, 4)
// will return true, as will zeroOrEqual(new(int64), float32(0)).
func zeroOrEqual(orig, value interface{}) bool {
	origVal := reflect.ValueOf(orig)
	origType := origVal.Type()
	if orig == reflect.Zero(origType) {
		return true
	}
	compareValue := reflect.ValueOf(value)
	for !compareValue.Type().ConvertibleTo(origType) && isPtrOrInter(origType) {
		if origVal.IsNil() {
			if origVal.Kind() != reflect.Ptr {
				// Can't initialize
				return false
			}
			origVal.Set(reflect.New(origType.Elem()))
		}
		origVal = origVal.Elem()
		origType = origType.Elem()
	}
	if compareValue.Type().ConvertibleTo(origType) {
		compareValue = compareValue.Convert(origType)
	}
	return origVal.Interface() == compareValue.Interface()
}

// Immutable is an option func that ensures that a value is not
// modified after being set.  It will return an error if orig is
// non-empty and does not match the new value from the request.
func Immutable(orig, value interface{}, optionValue string) (interface{}, error) {
	immutableErr := errors.New("Value is immutable once set")
	if optionValue == "true" {
		if _, ok := orig.(receiver); ok {
			return nil, errors.New("Receiver types cannot be immutable.  " +
				"See ChangeReceiver for a supported alternative.")
		}
		if !zeroOrEqual(orig, value) {
			if changeReceiver, ok := orig.(changeReceiver); ok {
				changed, err := changeReceiver.Receive(value)
				if err != nil {
					return nil, err
				}
				if changed {
					return nil, immutableErr
				}
				return value, nil
			}
			return nil, immutableErr
		}
	}
	return value, nil
}
<commit_msg>Don't call Set on unaddressable values<commit_after>// The options package includes some default option parsers for the
// requests package.
package options

import (
	"errors"
	"reflect"
)

// Required is an option func that ensures a non-nil value was passed
// along in the request.  It does not ensure that the value is
// non-empty.
func Required(orig, value interface{}, optionValue string) (interface{}, error) {
	if optionValue == "true" {
		if value == nil {
			return nil, errors.New("Required value has nil input")
		}
	}
	return value, nil
}

// Default is an option func that sets a default value for a field.
// If the value doesn't exist in the request (or is nil), the provided
// default will be used instead.
func Default(orig, value interface{}, optionValue string) (interface{}, error) {
	if value == nil {
		if optionValue != "" {
			// This is a string type, but we'll leave it up to the
			// unmarshal process (or the Receiver's Receive method) to
			// convert it.
			return optionValue, nil
		}
	}
	return value, nil
}

// changeReceiver is just a clone of requests.ChangeReceiver, since we can't
// import requests in this package.
type changeReceiver interface {
	Receive(interface{}) (bool, error)
}

// receiver is a clone of requests.Receiver, similar to changeReceiver.
type receiver interface {
	Receive(interface{}) error
}

func isPtrOrInter(t reflect.Type) bool {
	return t.Kind() == reflect.Ptr || t.Kind() == reflect.Interface
}

// zeroOrEqual checks whether orig is either the zero value of its type or
// loosely equal to value.  The main utility here is that zeroOrEqual(4.0, 4)
// will return true, as will zeroOrEqual(new(int64), float32(0)).
func zeroOrEqual(orig, value interface{}) bool {
	origType := reflect.TypeOf(orig)
	origVal := reflect.New(origType).Elem()
	origVal.Set(reflect.ValueOf(orig))
	if orig == reflect.Zero(origType) {
		return true
	}
	compareValue := reflect.ValueOf(value)
	for !compareValue.Type().ConvertibleTo(origType) && isPtrOrInter(origType) {
		if origVal.IsNil() {
			if origVal.Kind() != reflect.Ptr {
				// Can't initialize
				return false
			}
			origVal.Set(reflect.New(origType.Elem()))
		}
		origVal = origVal.Elem()
		origType = origType.Elem()
	}
	if compareValue.Type().ConvertibleTo(origType) {
		compareValue = compareValue.Convert(origType)
	}
	return origVal.Interface() == compareValue.Interface()
}

// Immutable is an option func that ensures that a value is not
// modified after being set.  It will return an error if orig is
// non-empty and does not match the new value from the request.
func Immutable(orig, value interface{}, optionValue string) (interface{}, error) {
	immutableErr := errors.New("Value is immutable once set")
	if optionValue == "true" {
		if _, ok := orig.(receiver); ok {
			return nil, errors.New("Receiver types cannot be immutable.  " +
				"See ChangeReceiver for a supported alternative.")
		}
		if !zeroOrEqual(orig, value) {
			if changeReceiver, ok := orig.(changeReceiver); ok {
				changed, err := changeReceiver.Receive(value)
				if err != nil {
					return nil, err
				}
				if changed {
					return nil, immutableErr
				}
				return value, nil
			}
			return nil, immutableErr
		}
	}
	return value, nil
}
<|endoftext|>


<commit_before>/*
 * This file is part of the libvirt-go-xml project
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * Copyright (C) 2016 Red Hat, Inc.
 *
 */

package libvirtxml

import (
	"encoding/xml"
)

type CapsHostCPUTopology struct {
	Sockets int `xml:"sockets,attr"`
	Cores   int `xml:"cores,attr"`
	Threads int `xml:"threads,attr"`
}

type CapsHostCPUFeatureFlag struct {
	Name string `xml:"name,attr"`
}

type CapsHostCPUPageSize struct {
	Size int    `xml:"size,attr"`
	Unit string `xml:"unit,attr"`
}

type CapsHostCPUMicrocode struct {
	Version int `xml:"version,attr"`
}

type CapsHostCPU struct {
	XMLName      xml.Name                 `xml:"cpu"`
	Arch         string                   `xml:"arch,omitempty"`
	Model        string                   `xml:"model,omitempty"`
	Vendor       string                   `xml:"vendor,omitempty"`
	Topology     *CapsHostCPUTopology     `xml:"topology"`
	FeatureFlags []CapsHostCPUFeatureFlag `xml:"feature"`
	Features     *CapsHostCPUFeatures     `xml:"features"`
	PageSizes    []CapsHostCPUPageSize    `xml:"pages"`
	Microcode    *CapsHostCPUMicrocode    `xml:"microcode"`
}

type CapsHostCPUFeature struct {
}

type CapsHostCPUFeatures struct {
	PAE    *CapsHostCPUFeature `xml:"pae"`
	NonPAE *CapsHostCPUFeature `xml:"nonpae"`
	SVM    *CapsHostCPUFeature `xml:"svm"`
	VMX    *CapsHostCPUFeature `xml:"vmx"`
}

type CapsHostNUMAMemory struct {
	Size uint64 `xml:",chardata"`
	Unit string `xml:"unit,attr"`
}

type CapsHostNUMAPageInfo struct {
	Size  int    `xml:"size,attr"`
	Unit  string `xml:"unit,attr"`
	Count uint64 `xml:",chardata"`
}

type CapsHostNUMACPU struct {
	ID       int    `xml:"id,attr"`
	SocketID *int   `xml:"socket_id,attr"`
	CoreID   *int   `xml:"core_id,attr"`
	Siblings string `xml:"siblings,attr,omitempty"`
}

type CapsHostNUMASibling struct {
	ID    int `xml:"id,attr"`
	Value int `xml:"value,attr"`
}

type CapsHostNUMACell struct {
	ID        int                    `xml:"id,attr"`
	Memory    *CapsHostNUMAMemory    `xml:"memory"`
	PageInfo  []CapsHostNUMAPageInfo `xml:"pages"`
	Distances *CapsHostNUMADistances `xml:"distances"`
	CPUS      *CapsHostNUMACPUs      `xml:"cpus"`
}

type CapsHostNUMADistances struct {
	Siblings []CapsHostNUMASibling `xml:"sibling"`
}

type CapsHostNUMACPUs struct {
	Num  uint              `xml:"num,attr,omitempty"`
	CPUs []CapsHostNUMACPU `xml:"cpu"`
}

type CapsHostNUMATopology struct {
	Cells *CapsHostNUMACells `xml:"cells"`
}

type CapsHostNUMACells struct {
	Num   uint               `xml:"num,attr,omitempty"`
	Cells []CapsHostNUMACell `xml:"cell"`
}

type CapsHostSecModelLabel struct {
	Type  string `xml:"type,attr"`
	Value string `xml:",chardata"`
}

type CapsHostSecModel struct {
	Name   string                  `xml:"model"`
	DOI    string                  `xml:"doi"`
	Labels []CapsHostSecModelLabel `xml:"baselabel"`
}

type CapsHostMigrationFeatures struct {
	Live          *CapsHostMigrationLive          `xml:"live"`
	URITransports *CapsHostMigrationURITransports `xml:"uri_transports"`
}

type CapsHostMigrationLive struct {
}

type CapsHostMigrationURITransports struct {
	URI []string `xml:"uri_transport"`
}

type CapsHost struct {
	UUID              string                     `xml:"uuid,omitempty"`
	CPU               *CapsHostCPU               `xml:"cpu"`
	PowerManagement   *CapsHostPowerManagement   `xml:"power_management"`
	IOMMU             *CapsHostIOMMU             `xml:"iommu"`
	MigrationFeatures *CapsHostMigrationFeatures `xml:"migration_features"`
	NUMA              *CapsHostNUMATopology      `xml:"topology"`
	Cache             *CapsHostCache             `xml:"cache"`
	MemoryBandwidth   *CapsHostMemoryBandwidth   `xml:"memory_bandwidth"`
	SecModel          []CapsHostSecModel         `xml:"secmodel"`
}

type CapsHostPowerManagement struct {
	SuspendMem    *CapsHostPowerManagementMode `xml:"suspend_mem"`
	SuspendDisk   *CapsHostPowerManagementMode `xml:"suspend_disk"`
	SuspendHybrid *CapsHostPowerManagementMode `xml:"suspend_hybrid"`
}

type CapsHostPowerManagementMode struct {
}

type CapsHostIOMMU struct {
	Support string `xml:"support,attr"`
}

type CapsHostCache struct {
	Banks []CapsHostCacheBank `xml:"bank"`
}

type CapsHostCacheBank struct {
	ID      uint                   `xml:"id,attr"`
	Level   uint                   `xml:"level,attr"`
	Type    string                 `xml:"type,attr"`
	Size    uint                   `xml:"size,attr"`
	Unit    string                 `xml:"unit,attr"`
	CPUs    string                 `xml:"cpus,attr"`
	Control []CapsHostCacheControl `xml:"control"`
}

type CapsHostCacheControl struct {
	Granularity uint   `xml:"granularity,attr"`
	Min         uint   `xml:"min,attr,omitempty"`
	Unit        string `xml:"unit,attr"`
	Type        string `xml:"type,attr"`
	MaxAllows   uint   `xml:"maxAllocs,attr"`
}

type CapsHostMemoryBandwidth struct {
	Nodes []CapsHostMemoryBandwidthNode `xml:"node"`
}

type CapsHostMemoryBandwidthNode struct {
	ID      uint                                `xml:"id,attr"`
	CPUs    string                              `xml:"cpus,attr"`
	Control *CapsHostMemoryBandwidthNodeControl `xml:"control"`
}

type CapsHostMemoryBandwidthNodeControl struct {
	Granularity uint `xml:"granularity,attr"`
	Min         uint `xml:"min,attr"`
	MaxAllocs   uint `xml:"maxAllocs,attr"`
}

type CapsGuestMachine struct {
	Name      string `xml:",chardata"`
	MaxCPUs   int    `xml:"maxCpus,attr,omitempty"`
	Canonical string `xml:"canonical,attr,omitempty"`
}

type CapsGuestDomain struct {
	Type     string             `xml:"type,attr"`
	Emulator string             `xml:"emulator,omitempty"`
	Machines []CapsGuestMachine `xml:"machine"`
}

type CapsGuestArch struct {
	Name     string             `xml:"name,attr"`
	WordSize string             `xml:"wordsize"`
	Emulator string             `xml:"emulator"`
	Loader   string             `xml:"loader,omitempty"`
	Machines []CapsGuestMachine `xml:"machine"`
	Domains  []CapsGuestDomain  `xml:"domain"`
}

type CapsGuestFeatureCPUSelection struct {
}

type CapsGuestFeatureDeviceBoot struct {
}

type CapsGuestFeaturePAE struct {
}

type CapsGuestFeatureNonPAE struct {
}

type CapsGuestFeatureDiskSnapshot struct {
	Default string `xml:"default,attr,omitempty"`
	Toggle  string `xml:"toggle,attr,omitempty"`
}

type CapsGuestFeatureAPIC struct {
	Default string `xml:"default,attr,omitempty"`
	Toggle  string `xml:"toggle,attr,omitempty"`
}

type CapsGuestFeatureACPI struct {
	Default string `xml:"default,attr,omitempty"`
	Toggle  string `xml:"toggle,attr,omitempty"`
}

type CapsGuestFeatureIA64BE struct {
}

type CapsGuestFeatures struct {
	CPUSelection *CapsGuestFeatureCPUSelection `xml:"cpuselection"`
	DeviceBoot   *CapsGuestFeatureDeviceBoot   `xml:"deviceboot"`
	DiskSnapshot *CapsGuestFeatureDiskSnapshot `xml:"disksnapshot"`
	PAE          *CapsGuestFeaturePAE          `xml:"pae"`
	NonPAE       *CapsGuestFeatureNonPAE       `xml:"nonpae"`
	APIC         *CapsGuestFeatureAPIC         `xml:"apic"`
	ACPI         *CapsGuestFeatureACPI         `xml:"acpi"`
	IA64BE       *CapsGuestFeatureIA64BE       `xml:"ia64_be"`
}

type CapsGuest struct {
	OSType   string             `xml:"os_type"`
	Arch     CapsGuestArch      `xml:"arch"`
	Features *CapsGuestFeatures `xml:"features"`
}

type Caps struct {
	XMLName xml.Name    `xml:"capabilities"`
	Host    CapsHost    `xml:"host"`
	Guests  []CapsGuest `xml:"guest"`
}

func (c *CapsHostCPU) Unmarshal(doc string) error {
	return xml.Unmarshal([]byte(doc), c)
}

func (c *CapsHostCPU) Marshal() (string, error) {
	doc, err := xml.MarshalIndent(c, "", "  ")
	if err != nil {
		return "", err
	}
	return string(doc), nil
}

func (c *Caps) Unmarshal(doc string) error {
	return xml.Unmarshal([]byte(doc), c)
}

func (c *Caps) Marshal() (string, error) {
	doc, err := xml.MarshalIndent(c, "", "  ")
	if err != nil {
		return "", err
	}
	return string(doc), nil
}
<commit_msg>Add support for resource monitor capabilities<commit_after>/*
 * This file is part of the libvirt-go-xml project
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * Copyright (C) 2016 Red Hat, Inc.
 *
 */

package libvirtxml

import (
	"encoding/xml"
)

type CapsHostCPUTopology struct {
	Sockets int `xml:"sockets,attr"`
	Cores   int `xml:"cores,attr"`
	Threads int `xml:"threads,attr"`
}

type CapsHostCPUFeatureFlag struct {
	Name string `xml:"name,attr"`
}

type CapsHostCPUPageSize struct {
	Size int    `xml:"size,attr"`
	Unit string `xml:"unit,attr"`
}

type CapsHostCPUMicrocode struct {
	Version int `xml:"version,attr"`
}

type CapsHostCPU struct {
	XMLName      xml.Name                 `xml:"cpu"`
	Arch         string                   `xml:"arch,omitempty"`
	Model        string                   `xml:"model,omitempty"`
	Vendor       string                   `xml:"vendor,omitempty"`
	Topology     *CapsHostCPUTopology     `xml:"topology"`
	FeatureFlags []CapsHostCPUFeatureFlag `xml:"feature"`
	Features     *CapsHostCPUFeatures     `xml:"features"`
	PageSizes    []CapsHostCPUPageSize    `xml:"pages"`
	Microcode    *CapsHostCPUMicrocode    `xml:"microcode"`
}

type CapsHostCPUFeature struct {
}

type CapsHostCPUFeatures struct {
	PAE    *CapsHostCPUFeature `xml:"pae"`
	NonPAE *CapsHostCPUFeature `xml:"nonpae"`
	SVM    *CapsHostCPUFeature `xml:"svm"`
	VMX    *CapsHostCPUFeature `xml:"vmx"`
}

type CapsHostNUMAMemory struct {
	Size uint64 `xml:",chardata"`
	Unit string `xml:"unit,attr"`
}

type CapsHostNUMAPageInfo struct {
	Size  int    `xml:"size,attr"`
	Unit  string `xml:"unit,attr"`
	Count uint64 `xml:",chardata"`
}

type CapsHostNUMACPU struct {
	ID       int    `xml:"id,attr"`
	SocketID *int   `xml:"socket_id,attr"`
	CoreID   *int   `xml:"core_id,attr"`
	Siblings string `xml:"siblings,attr,omitempty"`
}

type CapsHostNUMASibling struct {
	ID    int `xml:"id,attr"`
	Value int `xml:"value,attr"`
}

type CapsHostNUMACell struct {
	ID        int                    `xml:"id,attr"`
	Memory    *CapsHostNUMAMemory    `xml:"memory"`
	PageInfo  []CapsHostNUMAPageInfo `xml:"pages"`
	Distances *CapsHostNUMADistances `xml:"distances"`
	CPUS      *CapsHostNUMACPUs      `xml:"cpus"`
}

type CapsHostNUMADistances struct {
	Siblings []CapsHostNUMASibling `xml:"sibling"`
}

type CapsHostNUMACPUs struct {
	Num  uint              `xml:"num,attr,omitempty"`
	CPUs []CapsHostNUMACPU `xml:"cpu"`
}

type CapsHostNUMATopology struct {
	Cells *CapsHostNUMACells `xml:"cells"`
}

type CapsHostNUMACells struct {
	Num   uint               `xml:"num,attr,omitempty"`
	Cells []CapsHostNUMACell `xml:"cell"`
}

type CapsHostSecModelLabel struct {
	Type  string `xml:"type,attr"`
	Value string `xml:",chardata"`
}

type CapsHostSecModel struct {
	Name   string                  `xml:"model"`
	DOI    string                  `xml:"doi"`
	Labels []CapsHostSecModelLabel `xml:"baselabel"`
}

type CapsHostMigrationFeatures struct {
	Live          *CapsHostMigrationLive          `xml:"live"`
	URITransports *CapsHostMigrationURITransports `xml:"uri_transports"`
}

type CapsHostMigrationLive struct {
}

type CapsHostMigrationURITransports struct {
	URI []string `xml:"uri_transport"`
}

type CapsHost struct {
	UUID              string                     `xml:"uuid,omitempty"`
	CPU               *CapsHostCPU               `xml:"cpu"`
	PowerManagement   *CapsHostPowerManagement   `xml:"power_management"`
	IOMMU             *CapsHostIOMMU             `xml:"iommu"`
	MigrationFeatures *CapsHostMigrationFeatures `xml:"migration_features"`
	NUMA              *CapsHostNUMATopology      `xml:"topology"`
	Cache             *CapsHostCache             `xml:"cache"`
	MemoryBandwidth   *CapsHostMemoryBandwidth   `xml:"memory_bandwidth"`
	SecModel          []CapsHostSecModel         `xml:"secmodel"`
}

type CapsHostPowerManagement struct {
	SuspendMem    *CapsHostPowerManagementMode `xml:"suspend_mem"`
	SuspendDisk   *CapsHostPowerManagementMode `xml:"suspend_disk"`
	SuspendHybrid *CapsHostPowerManagementMode `xml:"suspend_hybrid"`
}

type CapsHostPowerManagementMode struct {
}

type CapsHostIOMMU struct {
	Support string `xml:"support,attr"`
}

type CapsHostCache struct {
	Banks   []CapsHostCacheBank   `xml:"bank"`
	Monitor *CapsHostCacheMonitor `xml:"monitor"`
}

type CapsHostCacheBank struct {
	ID      uint                   `xml:"id,attr"`
	Level   uint                   `xml:"level,attr"`
	Type    string                 `xml:"type,attr"`
	Size    uint                   `xml:"size,attr"`
	Unit    string                 `xml:"unit,attr"`
	CPUs    string                 `xml:"cpus,attr"`
	Control []CapsHostCacheControl `xml:"control"`
}

type CapsHostCacheMonitor struct {
	Level          uint                          `xml:"level,attr,omitempty"`
	ResueThreshold uint                          `xml:"reuseThreshold,attr,omitempty"`
	MaxMonitors    uint                          `xml:"maxMonitors,attr"`
	Features       []CapsHostCacheMonitorFeature `xml:"feature"`
}

type CapsHostCacheMonitorFeature struct {
	Name string `xml:"name,attr"`
}

type CapsHostCacheControl struct {
	Granularity uint   `xml:"granularity,attr"`
	Min         uint   `xml:"min,attr,omitempty"`
	Unit        string `xml:"unit,attr"`
	Type        string `xml:"type,attr"`
	MaxAllows   uint   `xml:"maxAllocs,attr"`
}

type CapsHostMemoryBandwidth struct {
	Nodes   []CapsHostMemoryBandwidthNode   `xml:"node"`
	Monitor *CapsHostMemoryBandwidthMonitor `xml:"monitor"`
}

type CapsHostMemoryBandwidthNode struct {
	ID      uint                                `xml:"id,attr"`
	CPUs    string                              `xml:"cpus,attr"`
	Control *CapsHostMemoryBandwidthNodeControl `xml:"control"`
}

type CapsHostMemoryBandwidthNodeControl struct {
	Granularity uint `xml:"granularity,attr"`
	Min         uint `xml:"min,attr"`
	MaxAllocs   uint `xml:"maxAllocs,attr"`
}

type CapsHostMemoryBandwidthMonitor struct {
	MaxMonitors uint                                    `xml:"maxMonitors,attr"`
	Features    []CapsHostMemoryBandwidthMonitorFeature `xml:"feature"`
}

type CapsHostMemoryBandwidthMonitorFeature struct {
	Name string `xml:"name,attr"`
}

type CapsGuestMachine struct {
	Name      string `xml:",chardata"`
	MaxCPUs   int    `xml:"maxCpus,attr,omitempty"`
	Canonical string `xml:"canonical,attr,omitempty"`
}

type CapsGuestDomain struct {
	Type     string             `xml:"type,attr"`
	Emulator string             `xml:"emulator,omitempty"`
	Machines []CapsGuestMachine `xml:"machine"`
}

type CapsGuestArch struct {
	Name     string             `xml:"name,attr"`
	WordSize string             `xml:"wordsize"`
	Emulator string             `xml:"emulator"`
	Loader   string             `xml:"loader,omitempty"`
	Machines []CapsGuestMachine `xml:"machine"`
	Domains  []CapsGuestDomain  `xml:"domain"`
}

type CapsGuestFeatureCPUSelection struct {
}

type CapsGuestFeatureDeviceBoot struct {
}

type CapsGuestFeaturePAE struct {
}

type CapsGuestFeatureNonPAE struct {
}

type CapsGuestFeatureDiskSnapshot struct {
	Default string `xml:"default,attr,omitempty"`
	Toggle  string `xml:"toggle,attr,omitempty"`
}

type CapsGuestFeatureAPIC struct {
	Default string `xml:"default,attr,omitempty"`
	Toggle  string `xml:"toggle,attr,omitempty"`
}

type CapsGuestFeatureACPI struct {
	Default string `xml:"default,attr,omitempty"`
	Toggle  string `xml:"toggle,attr,omitempty"`
}

type CapsGuestFeatureIA64BE struct {
}

type CapsGuestFeatures struct {
	CPUSelection *CapsGuestFeatureCPUSelection `xml:"cpuselection"`
	DeviceBoot   *CapsGuestFeatureDeviceBoot   `xml:"deviceboot"`
	DiskSnapshot *CapsGuestFeatureDiskSnapshot `xml:"disksnapshot"`
	PAE          *CapsGuestFeaturePAE          `xml:"pae"`
	NonPAE       *CapsGuestFeatureNonPAE       `xml:"nonpae"`
	APIC         *CapsGuestFeatureAPIC         `xml:"apic"`
	ACPI         *CapsGuestFeatureACPI         `xml:"acpi"`
	IA64BE       *CapsGuestFeatureIA64BE       `xml:"ia64_be"`
}

type CapsGuest struct {
	OSType   string             `xml:"os_type"`
	Arch     CapsGuestArch      `xml:"arch"`
	Features *CapsGuestFeatures `xml:"features"`
}

type Caps struct {
	XMLName xml.Name    `xml:"capabilities"`
	Host    CapsHost    `xml:"host"`
	Guests  []CapsGuest `xml:"guest"`
}

func (c *CapsHostCPU) Unmarshal(doc string) error {
	return xml.Unmarshal([]byte(doc), c)
}

func (c *CapsHostCPU) Marshal() (string, error) {
	doc, err := xml.MarshalIndent(c, "", "  ")
	if err != nil {
		return "", err
	}
	return string(doc), nil
}

func (c *Caps) Unmarshal(doc string) error {
	return xml.Unmarshal([]byte(doc), c)
}

func (c *Caps) Marshal() (string, error) {
	doc, err := xml.MarshalIndent(c, "", "  ")
	if err != nil {
		return "", err
	}
	return string(doc), nil
}
<|endoftext|>


<commit_before>/*
 * This file is part of the libvirt-go-xml project
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * Copyright (C) 2016 Red Hat, Inc.
 *
 */

package libvirtxml

import (
	"encoding/xml"
)

type CapsHostCPUTopology struct {
	Sockets int `xml:"sockets,attr"`
	Cores   int `xml:"cores,attr"`
	Threads int `xml:"threads,attr"`
}

type CapsHostCPUFeature struct {
	Name string `xml:"name,attr"`
}

type CapsHostCPUPageSize struct {
	Size int    `xml:"size,attr"`
	Unit string `xml:"unit,attr"`
}

type CapsHostCPU struct {
	Arch      string                `xml:"arch,omitempty"`
	Model     string                `xml:"model,omitempty"`
	Vendor    string                `xml:"vendor,omitempty"`
	Topology  *CapsHostCPUTopology  `xml:"topology"`
	Features  []CapsHostCPUFeature  `xml:"feature"`
	PageSizes []CapsHostCPUPageSize `xml:"pages"`
}

type CapsHostNUMAMemory struct {
	Size uint64 `xml:"size,attr"`
	Unit string `xml:"unit,attr"`
}

type CapsHostNUMAPageInfo struct {
	Size  int    `xml:"size,attr"`
	Unit  string `xml:"unit,attr"`
	Count uint64 `xml:",chardata"`
}

type CapsHostNUMACPU struct {
	ID       int    `xml:"id,attr"`
	SocketID *int   `xml:"socket_id,attr"`
	CoreID   *int   `xml:"core_id,attr"`
	Siblings string `xml:"siblings,attr,omitempty"`
}

type CapsHostNUMASibling struct {
	ID    int `xml:"id,attr"`
	Value int `xml:"value,attr"`
}

type CapsHostNUMACell struct {
	ID        int                    `xml:"id,attr"`
	Memory    *CapsHostNUMAMemory    `xml:"memory"`
	PageInfo  []CapsHostNUMAPageInfo `xml:"pages"`
	Distances *CapsHostNUMADistances `xml:"distances"`
	CPUS      *CapsHostNUMACPUs      `xml:"cpus"`
}

type CapsHostNUMADistances struct {
	Siblings []CapsHostNUMASibling `xml:"sibling"`
}

type CapsHostNUMACPUs struct {
	Num  uint              `xml:"num,attr,omitempty"`
	CPUs []CapsHostNUMACPU `xml:"cpu"`
}

type CapsHostNUMATopology struct {
	Cells *CapsHostNUMACells `xml:"cells"`
}

type CapsHostNUMACells struct {
	Num   uint               `xml:"num,attr,omitempty"`
	Cells []CapsHostNUMACell `xml:"cell"`
}

type CapsHostSecModelLabel struct {
	Type  string `xml:"type,attr"`
	Value string `xml:",chardata"`
}

type CapsHostSecModel struct {
	Name   string                  `xml:"model"`
	DOI    string                  `xml:"doi"`
	Labels []CapsHostSecModelLabel `xml:"baselabel"`
}

type CapsHostMigrationFeatures struct {
	Live          *CapsHostMigrationLive          `xml:"live"`
	URITransports *CapsHostMigrationURITransports `xml:"uri_transports"`
}

type CapsHostMigrationLive struct {
}

type CapsHostMigrationURITransports struct {
	URI []string `xml:"uri_transport"`
}

type CapsHost struct {
	UUID              string                     `xml:"uuid,omitempty"`
	CPU               *CapsHostCPU               `xml:"cpu"`
	MigrationFeatures *CapsHostMigrationFeatures `xml:"migration_features"`
	NUMA              *CapsHostNUMATopology      `xml:"topology"`
	SecModel          []CapsHostSecModel         `xml:"secmodel"`
}

type CapsGuestMachine struct {
	Name      string `xml:",chardata"`
	MaxCPUs   int    `xml:"maxCpus,attr,omitempty"`
	Canonical string `xml:"canonical,attr,omitempty"`
}

type CapsGuestDomain struct {
	Type     string             `xml:"type,attr"`
	Emulator string             `xml:"emulator,omitempty"`
	Machines []CapsGuestMachine `xml:"machine"`
}

type CapsGuestArch struct {
	Name     string             `xml:"name,attr"`
	WordSize string             `xml:"wordsize"`
	Emulator string             `xml:"emulator"`
	Machines []CapsGuestMachine `xml:"machine"`
	Domains  []CapsGuestDomain  `xml:"domain"`
}

type CapsGuestFeatures struct {
	CPUSelection *struct{} `xml:"cpuselection"`
	DeviceBoot   *struct{} `xml:"deviceboot"`
}

type CapsGuest struct {
	OSType   string             `xml:"os_type"`
	Arch     CapsGuestArch      `xml:"arch"`
	Features *CapsGuestFeatures `xml:"features"`
}

type Caps struct {
	XMLName xml.Name    `xml:"capabilities"`
	Host    CapsHost    `xml:"host"`
	Guests  []CapsGuest `xml:"guest"`
}

func (c *Caps) Unmarshal(doc string) error {
	return xml.Unmarshal([]byte(doc), c)
}

func (c *Caps) Marshal() (string, error) {
	doc, err := xml.MarshalIndent(c, "", "  ")
	if err != nil {
		return "", err
	}
	return string(doc), nil
}
<commit_msg>Handle host CPU legacy features (as distinct from CPU feature flags)<commit_after>/*
 * This file is part of the libvirt-go-xml project
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * Copyright (C) 2016 Red Hat, Inc.
 *
 */

package libvirtxml

import (
	"encoding/xml"
)

type CapsHostCPUTopology struct {
	Sockets int `xml:"sockets,attr"`
	Cores   int `xml:"cores,attr"`
	Threads int `xml:"threads,attr"`
}

type CapsHostCPUFeatureFlag struct {
	Name string `xml:"name,attr"`
}

type CapsHostCPUPageSize struct {
	Size int    `xml:"size,attr"`
	Unit string `xml:"unit,attr"`
}

type CapsHostCPU struct {
	Arch         string                   `xml:"arch,omitempty"`
	Model        string                   `xml:"model,omitempty"`
	Vendor       string                   `xml:"vendor,omitempty"`
	Topology     *CapsHostCPUTopology     `xml:"topology"`
	FeatureFlags []CapsHostCPUFeatureFlag `xml:"feature"`
	Features     *CapsHostCPUFeatures     `xml:"features"`
	PageSizes    []CapsHostCPUPageSize    `xml:"pages"`
}

type CapsHostCPUFeature struct {
}

type CapsHostCPUFeatures struct {
	PAE    *CapsHostCPUFeature `xml:"pae"`
	NonPAE *CapsHostCPUFeature `xml:"nonpae"`
	SVM    *CapsHostCPUFeature `xml:"svm"`
	VMX    *CapsHostCPUFeature `xml:"vmx"`
}

type CapsHostNUMAMemory struct {
	Size uint64 `xml:"size,attr"`
	Unit string `xml:"unit,attr"`
}

type CapsHostNUMAPageInfo struct {
	Size  int    `xml:"size,attr"`
	Unit  string `xml:"unit,attr"`
	Count uint64 `xml:",chardata"`
}

type CapsHostNUMACPU struct {
	ID       int    `xml:"id,attr"`
	SocketID *int   `xml:"socket_id,attr"`
	CoreID   *int   `xml:"core_id,attr"`
	Siblings string `xml:"siblings,attr,omitempty"`
}

type CapsHostNUMASibling struct {
	ID    int `xml:"id,attr"`
	Value int `xml:"value,attr"`
}

type CapsHostNUMACell struct {
	ID        int                    `xml:"id,attr"`
	Memory    *CapsHostNUMAMemory    `xml:"memory"`
	PageInfo  []CapsHostNUMAPageInfo `xml:"pages"`
	Distances *CapsHostNUMADistances `xml:"distances"`
	CPUS      *CapsHostNUMACPUs      `xml:"cpus"`
}

type CapsHostNUMADistances struct {
	Siblings []CapsHostNUMASibling `xml:"sibling"`
}

type CapsHostNUMACPUs struct {
	Num  uint              `xml:"num,attr,omitempty"`
	CPUs []CapsHostNUMACPU `xml:"cpu"`
}

type CapsHostNUMATopology struct {
	Cells *CapsHostNUMACells `xml:"cells"`
}

type CapsHostNUMACells struct {
	Num   uint               `xml:"num,attr,omitempty"`
	Cells []CapsHostNUMACell `xml:"cell"`
}

type CapsHostSecModelLabel struct {
	Type  string `xml:"type,attr"`
	Value string `xml:",chardata"`
}

type CapsHostSecModel struct {
	Name   string                  `xml:"model"`
	DOI    string                  `xml:"doi"`
	Labels []CapsHostSecModelLabel `xml:"baselabel"`
}

type CapsHostMigrationFeatures struct {
	Live          *CapsHostMigrationLive          `xml:"live"`
	URITransports *CapsHostMigrationURITransports `xml:"uri_transports"`
}

type CapsHostMigrationLive struct {
}

type CapsHostMigrationURITransports struct {
	URI []string `xml:"uri_transport"`
}

type CapsHost struct {
	UUID              string                     `xml:"uuid,omitempty"`
	CPU               *CapsHostCPU               `xml:"cpu"`
	MigrationFeatures *CapsHostMigrationFeatures `xml:"migration_features"`
	NUMA              *CapsHostNUMATopology      `xml:"topology"`
	SecModel          []CapsHostSecModel         `xml:"secmodel"`
}

type CapsGuestMachine struct {
	Name      string `xml:",chardata"`
	MaxCPUs   int    `xml:"maxCpus,attr,omitempty"`
	Canonical string `xml:"canonical,attr,omitempty"`
}

type CapsGuestDomain struct {
	Type     string             `xml:"type,attr"`
	Emulator string             `xml:"emulator,omitempty"`
	Machines []CapsGuestMachine `xml:"machine"`
}

type CapsGuestArch struct {
	Name     string             `xml:"name,attr"`
	WordSize string             `xml:"wordsize"`
	Emulator string             `xml:"emulator"`
	Machines []CapsGuestMachine `xml:"machine"`
	Domains  []CapsGuestDomain  `xml:"domain"`
}

type CapsGuestFeatureCPUSelection struct {
}

type CapsGuestFeatureDeviceBoot struct {
}

type CapsGuestFeaturePAE struct {
}

type CapsGuestFeatureNonPAE struct {
}

type CapsGuestFeatureAPIC struct {
	Default string `xml:"default,attr,omitempty"`
	Toggle  string `xml:"toggle,attr,omitempty"`
}

type CapsGuestFeatureACPI struct {
	Default string `xml:"default,attr,omitempty"`
	Toggle  string `xml:"toggle,attr,omitempty"`
}

type CapsGuestFeatures struct {
	CPUSelection *CapsGuestFeatureCPUSelection `xml:"cpuselection"`
	DeviceBoot   *CapsGuestFeatureDeviceBoot   `xml:"deviceboot"`
	PAE          *CapsGuestFeaturePAE          `xml:"pae"`
	NonPAE       *CapsGuestFeatureNonPAE       `xml:"nonpae"`
	APIC         *CapsGuestFeatureAPIC         `xml:"apic"`
	ACPI         *CapsGuestFeatureACPI         `xml:"acpi"`
}

type CapsGuest struct {
	OSType   string             `xml:"os_type"`
	Arch     CapsGuestArch      `xml:"arch"`
	Features *CapsGuestFeatures `xml:"features"`
}

type Caps struct {
	XMLName xml.Name    `xml:"capabilities"`
	Host    CapsHost    `xml:"host"`
	Guests  []CapsGuest `xml:"guest"`
}

func (c *Caps) Unmarshal(doc string) error {
	return xml.Unmarshal([]byte(doc), c)
}

func (c *Caps) Marshal() (string, error) {
	doc, err := xml.MarshalIndent(c, "", "  ")
	if err != nil {
		return "", err
	}
	return string(doc), nil
}
<|endoftext|>