<commit_before>package commands

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"strconv"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/robdimsdale/wundergo"
	"github.com/robdimsdale/wundergo/logger"
	"github.com/robdimsdale/wundergo/oauth"
	"github.com/spf13/cobra"
)

const (
	// Global flags
	accessTokenEnvVariable = "WL_ACCESS_TOKEN"
	clientIDEnvVariable    = "WL_CLIENT_ID"

	accessTokenLongFlag = "accessToken"
	clientIDLongFlag    = "clientID"

	verboseLongFlag  = "verbose"
	verboseShortFlag = "v"

	useJSONLongFlag  = "useJSON"
	useJSONShortFlag = "j"

	// Shared, non-global flags
	listIDLongFlag  = "listID"
	listIDShortFlag = "l"

	taskIDLongFlag  = "taskID"
	taskIDShortFlag = "t"

	titleLongFlag = "title"

	completedLongFlag = "completed"

	listIDsLongFlag = "listIDs"
)

var (
	// Global flags
	accessToken string
	clientID    string
	verbose     bool
	useJSON     bool

	// Non-global, shared flags
	taskID    uint
	listID    uint
	title     string
	completed bool
	listIDs   string

	// WundergoCmd is the root command. All other commands are subcommands of it.
	WundergoCmd = &cobra.Command{Use: "wl"}
)

// Execute adds all child commands to the root command WundergoCmd,
// and executes the root command.
func Execute() {
	addCommands()
	WundergoCmd.Execute()
}

// Sets global flags
func init() {
	WundergoCmd.PersistentFlags().BoolVarP(&verbose, verboseLongFlag, verboseShortFlag, false, "verbose output")
	WundergoCmd.PersistentFlags().StringVarP(&accessToken, accessTokenLongFlag, "", "", `Wunderlist access token.
                      	Required, but can be provided via WL_ACCESS_TOKEN environment variable instead.`)
	WundergoCmd.PersistentFlags().StringVarP(&clientID, clientIDLongFlag, "", "", `Wunderlist client ID.
                     Required, but can be provided via WL_CLIENT_ID environment variable instead.`)
	WundergoCmd.PersistentFlags().BoolVarP(&useJSON, useJSONLongFlag, useJSONShortFlag, false, "render output as JSON instead of YAML.")
}

func addCommands() {
	WundergoCmd.AddCommand(cmdInbox)
	WundergoCmd.AddCommand(cmdRoot)
	WundergoCmd.AddCommand(cmdLists)
	WundergoCmd.AddCommand(cmdList)
	WundergoCmd.AddCommand(cmdCreateList)
	WundergoCmd.AddCommand(cmdUpdateList)
	WundergoCmd.AddCommand(cmdDeleteList)
	WundergoCmd.AddCommand(cmdDeleteAllLists)

	WundergoCmd.AddCommand(cmdFolders)
	WundergoCmd.AddCommand(cmdFolder)
	WundergoCmd.AddCommand(cmdCreateFolder)
	WundergoCmd.AddCommand(cmdUpdateFolder)
	WundergoCmd.AddCommand(cmdDeleteFolder)
	WundergoCmd.AddCommand(cmdDeleteAllFolders)

	WundergoCmd.AddCommand(cmdTasks)
	WundergoCmd.AddCommand(cmdTask)
	WundergoCmd.AddCommand(cmdCreateTask)
	WundergoCmd.AddCommand(cmdUpdateTask)
	WundergoCmd.AddCommand(cmdDeleteTask)
	WundergoCmd.AddCommand(cmdDeleteAllTasks)

	WundergoCmd.AddCommand(cmdUploadFile)
	WundergoCmd.AddCommand(cmdCreateFile)
	WundergoCmd.AddCommand(cmdFile)
	WundergoCmd.AddCommand(cmdFiles)
	WundergoCmd.AddCommand(cmdDestroyFile)
	WundergoCmd.AddCommand(cmdFilePreview)

	WundergoCmd.AddCommand(cmdUsers)
	WundergoCmd.AddCommand(cmdUser)
	WundergoCmd.AddCommand(cmdUpdateUser)
	WundergoCmd.AddCommand(cmdAvatarURL)

	WundergoCmd.AddCommand(cmdNotes)
	WundergoCmd.AddCommand(cmdNote)
	WundergoCmd.AddCommand(cmdCreateNote)
	WundergoCmd.AddCommand(cmdUpdateNote)
	WundergoCmd.AddCommand(cmdDeleteNote)

	WundergoCmd.AddCommand(cmdSubtasks)
	WundergoCmd.AddCommand(cmdCreateSubtask)
	WundergoCmd.AddCommand(cmdSubtask)
	WundergoCmd.AddCommand(cmdUpdateSubtask)
	WundergoCmd.AddCommand(cmdDeleteSubtask)

	WundergoCmd.AddCommand(cmdWebhooks)
	WundergoCmd.AddCommand(cmdWebhook)
	WundergoCmd.AddCommand(cmdCreateWebhook)
	WundergoCmd.AddCommand(cmdDeleteWebhook)

	WundergoCmd.AddCommand(cmdReminders)
	WundergoCmd.AddCommand(cmdCreateReminder)
	WundergoCmd.AddCommand(cmdReminder)
	WundergoCmd.AddCommand(cmdUpdateReminder)
	WundergoCmd.AddCommand(cmdDeleteReminder)

	WundergoCmd.AddCommand(cmdTaskComments)
	WundergoCmd.AddCommand(cmdCreateTaskComment)
	WundergoCmd.AddCommand(cmdTaskComment)
	WundergoCmd.AddCommand(cmdDeleteTaskComment)

	WundergoCmd.AddCommand(cmdMemberships)
	WundergoCmd.AddCommand(cmdMembership)
	WundergoCmd.AddCommand(cmdRejectMembership)
	WundergoCmd.AddCommand(cmdAcceptMembership)
	WundergoCmd.AddCommand(cmdRemoveMembership)
	WundergoCmd.AddCommand(cmdInviteMember)

	WundergoCmd.AddCommand(cmdListPositions)
	WundergoCmd.AddCommand(cmdListPosition)
	WundergoCmd.AddCommand(cmdUpdateListPosition)

	WundergoCmd.AddCommand(cmdTaskPositions)
	WundergoCmd.AddCommand(cmdTaskPosition)
	WundergoCmd.AddCommand(cmdUpdateTaskPosition)

	WundergoCmd.AddCommand(cmdSubtaskPositions)
	WundergoCmd.AddCommand(cmdSubtaskPosition)
	WundergoCmd.AddCommand(cmdUpdateSubtaskPosition)
}

func newClient(cmd *cobra.Command) wundergo.Client {
	var l logger.Logger
	if verbose {
		l = logger.NewLogger(logger.DEBUG)
	} else {
		l = logger.NewLogger(logger.INFO)
	}

	if accessToken == "" {
		accessToken = os.Getenv(accessTokenEnvVariable)
	}

	if accessToken == "" {
		l.Error(
			"exiting",
			errors.New("accessToken not found. Either provide the flag -"+accessTokenLongFlag+" or set the environment variable "+accessTokenEnvVariable))
		os.Exit(2)
	}

	if clientID == "" {
		clientID = os.Getenv(clientIDEnvVariable)
	}

	if clientID == "" {
		l.Error(
			"exiting",
			errors.New("clientID not found. Either provide the flag -"+clientIDLongFlag+" or set the environment variable "+clientIDEnvVariable))
		os.Exit(2)
	}

	return oauth.NewClient(accessToken, clientID, wundergo.APIURL, l)
}

func handleError(err error) {
	fmt.Printf("exiting - error: %v\n", err)
	os.Exit(1)
}

func renderOutput(output interface{}, err error) {
	if err != nil {
		handleError(err)
	}

	var data []byte
	if useJSON {
		data, err = json.Marshal(output)
		data = append(data, '\n')
	} else {
		data, err = yaml.Marshal(output)
	}

	if err != nil {
		fmt.Printf("exiting - failed to render output - error: %v\n", err)
		os.Exit(1)
	}

	// The JSON package escapes & which we do not want.
	// It also escapes < and > but those are not present in URLs
	data = bytes.Replace(data, []byte("\\u0026"), []byte("&"), -1)

	fmt.Printf("%s", string(data))
}

func splitStringToUints(input string) ([]uint, error) {
	split := strings.Split(input, ",")
	splitUints := make([]uint, len(split))

	for i, s := range split {
		idInt, err := strconv.Atoi(s)
		if err != nil {
			return nil, fmt.Errorf("%v at index %d", err, i)
		}
		splitUints[i] = uint(idInt)
	}

	return splitUints, nil
}
<commit_msg>Trim space from comma-separated uints in CLI.<commit_after>package commands

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"strconv"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/robdimsdale/wundergo"
	"github.com/robdimsdale/wundergo/logger"
	"github.com/robdimsdale/wundergo/oauth"
	"github.com/spf13/cobra"
)

const (
	// Global flags
	accessTokenEnvVariable = "WL_ACCESS_TOKEN"
	clientIDEnvVariable    = "WL_CLIENT_ID"

	accessTokenLongFlag = "accessToken"
	clientIDLongFlag    = "clientID"

	verboseLongFlag  = "verbose"
	verboseShortFlag = "v"

	useJSONLongFlag  = "useJSON"
	useJSONShortFlag = "j"

	// Shared, non-global flags
	listIDLongFlag  = "listID"
	listIDShortFlag = "l"

	taskIDLongFlag  = "taskID"
	taskIDShortFlag = "t"

	titleLongFlag = "title"

	completedLongFlag = "completed"

	listIDsLongFlag = "listIDs"
)

var (
	// Global flags
	accessToken string
	clientID    string
	verbose     bool
	useJSON     bool

	// Non-global, shared flags
	taskID    uint
	listID    uint
	title     string
	completed bool
	listIDs   string

	// WundergoCmd is the root command. All other commands are subcommands of it.
	WundergoCmd = &cobra.Command{Use: "wl"}
)

// Execute adds all child commands to the root command WundergoCmd,
// and executes the root command.
func Execute() {
	addCommands()
	WundergoCmd.Execute()
}

// Sets global flags
func init() {
	WundergoCmd.PersistentFlags().BoolVarP(&verbose, verboseLongFlag, verboseShortFlag, false, "verbose output")
	WundergoCmd.PersistentFlags().StringVarP(&accessToken, accessTokenLongFlag, "", "", `Wunderlist access token.
                      	Required, but can be provided via WL_ACCESS_TOKEN environment variable instead.`)
	WundergoCmd.PersistentFlags().StringVarP(&clientID, clientIDLongFlag, "", "", `Wunderlist client ID.
                     Required, but can be provided via WL_CLIENT_ID environment variable instead.`)
	WundergoCmd.PersistentFlags().BoolVarP(&useJSON, useJSONLongFlag, useJSONShortFlag, false, "render output as JSON instead of YAML.")
}

func addCommands() {
	WundergoCmd.AddCommand(cmdInbox)
	WundergoCmd.AddCommand(cmdRoot)
	WundergoCmd.AddCommand(cmdLists)
	WundergoCmd.AddCommand(cmdList)
	WundergoCmd.AddCommand(cmdCreateList)
	WundergoCmd.AddCommand(cmdUpdateList)
	WundergoCmd.AddCommand(cmdDeleteList)
	WundergoCmd.AddCommand(cmdDeleteAllLists)

	WundergoCmd.AddCommand(cmdFolders)
	WundergoCmd.AddCommand(cmdFolder)
	WundergoCmd.AddCommand(cmdCreateFolder)
	WundergoCmd.AddCommand(cmdUpdateFolder)
	WundergoCmd.AddCommand(cmdDeleteFolder)
	WundergoCmd.AddCommand(cmdDeleteAllFolders)

	WundergoCmd.AddCommand(cmdTasks)
	WundergoCmd.AddCommand(cmdTask)
	WundergoCmd.AddCommand(cmdCreateTask)
	WundergoCmd.AddCommand(cmdUpdateTask)
	WundergoCmd.AddCommand(cmdDeleteTask)
	WundergoCmd.AddCommand(cmdDeleteAllTasks)

	WundergoCmd.AddCommand(cmdUploadFile)
	WundergoCmd.AddCommand(cmdCreateFile)
	WundergoCmd.AddCommand(cmdFile)
	WundergoCmd.AddCommand(cmdFiles)
	WundergoCmd.AddCommand(cmdDestroyFile)
	WundergoCmd.AddCommand(cmdFilePreview)

	WundergoCmd.AddCommand(cmdUsers)
	WundergoCmd.AddCommand(cmdUser)
	WundergoCmd.AddCommand(cmdUpdateUser)
	WundergoCmd.AddCommand(cmdAvatarURL)

	WundergoCmd.AddCommand(cmdNotes)
	WundergoCmd.AddCommand(cmdNote)
	WundergoCmd.AddCommand(cmdCreateNote)
	WundergoCmd.AddCommand(cmdUpdateNote)
	WundergoCmd.AddCommand(cmdDeleteNote)

	WundergoCmd.AddCommand(cmdSubtasks)
	WundergoCmd.AddCommand(cmdCreateSubtask)
	WundergoCmd.AddCommand(cmdSubtask)
	WundergoCmd.AddCommand(cmdUpdateSubtask)
	WundergoCmd.AddCommand(cmdDeleteSubtask)

	WundergoCmd.AddCommand(cmdWebhooks)
	WundergoCmd.AddCommand(cmdWebhook)
	WundergoCmd.AddCommand(cmdCreateWebhook)
	WundergoCmd.AddCommand(cmdDeleteWebhook)

	WundergoCmd.AddCommand(cmdReminders)
	WundergoCmd.AddCommand(cmdCreateReminder)
	WundergoCmd.AddCommand(cmdReminder)
	WundergoCmd.AddCommand(cmdUpdateReminder)
	WundergoCmd.AddCommand(cmdDeleteReminder)

	WundergoCmd.AddCommand(cmdTaskComments)
	WundergoCmd.AddCommand(cmdCreateTaskComment)
	WundergoCmd.AddCommand(cmdTaskComment)
	WundergoCmd.AddCommand(cmdDeleteTaskComment)

	WundergoCmd.AddCommand(cmdMemberships)
	WundergoCmd.AddCommand(cmdMembership)
	WundergoCmd.AddCommand(cmdRejectMembership)
	WundergoCmd.AddCommand(cmdAcceptMembership)
	WundergoCmd.AddCommand(cmdRemoveMembership)
	WundergoCmd.AddCommand(cmdInviteMember)

	WundergoCmd.AddCommand(cmdListPositions)
	WundergoCmd.AddCommand(cmdListPosition)
	WundergoCmd.AddCommand(cmdUpdateListPosition)

	WundergoCmd.AddCommand(cmdTaskPositions)
	WundergoCmd.AddCommand(cmdTaskPosition)
	WundergoCmd.AddCommand(cmdUpdateTaskPosition)

	WundergoCmd.AddCommand(cmdSubtaskPositions)
	WundergoCmd.AddCommand(cmdSubtaskPosition)
	WundergoCmd.AddCommand(cmdUpdateSubtaskPosition)
}

func newClient(cmd *cobra.Command) wundergo.Client {
	var l logger.Logger
	if verbose {
		l = logger.NewLogger(logger.DEBUG)
	} else {
		l = logger.NewLogger(logger.INFO)
	}

	if accessToken == "" {
		accessToken = os.Getenv(accessTokenEnvVariable)
	}

	if accessToken == "" {
		l.Error(
			"exiting",
			errors.New("accessToken not found. Either provide the flag -"+accessTokenLongFlag+" or set the environment variable "+accessTokenEnvVariable))
		os.Exit(2)
	}

	if clientID == "" {
		clientID = os.Getenv(clientIDEnvVariable)
	}

	if clientID == "" {
		l.Error(
			"exiting",
			errors.New("clientID not found. Either provide the flag -"+clientIDLongFlag+" or set the environment variable "+clientIDEnvVariable))
		os.Exit(2)
	}

	return oauth.NewClient(accessToken, clientID, wundergo.APIURL, l)
}

func handleError(err error) {
	fmt.Printf("exiting - error: %v\n", err)
	os.Exit(1)
}

func renderOutput(output interface{}, err error) {
	if err != nil {
		handleError(err)
	}

	var data []byte
	if useJSON {
		data, err = json.Marshal(output)
		data = append(data, '\n')
	} else {
		data, err = yaml.Marshal(output)
	}

	if err != nil {
		fmt.Printf("exiting - failed to render output - error: %v\n", err)
		os.Exit(1)
	}

	// The JSON package escapes & which we do not want.
	// It also escapes < and > but those are not present in URLs
	data = bytes.Replace(data, []byte("\\u0026"), []byte("&"), -1)

	fmt.Printf("%s", string(data))
}

func splitStringToUints(input string) ([]uint, error) {
	split := strings.Split(input, ",")
	splitUints := make([]uint, len(split))

	for i, s := range split {
		s = strings.TrimSpace(s)
		idInt, err := strconv.Atoi(s)
		if err != nil {
			return nil, fmt.Errorf("%v at index %d", err, i)
		}
		splitUints[i] = uint(idInt)
	}

	return splitUints, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"strconv"

	"github.com/gorilla/mux"

	"github.com/lxc/lxd/shared"
)

// Helper functions
func networkGetInterfaces(d *Daemon) ([]string, error) {
	networks, err := dbNetworks(d.db)
	if err != nil {
		return nil, err
	}

	ifaces, err := net.Interfaces()
	if err != nil {
		return nil, err
	}

	for _, iface := range ifaces {
		if !shared.StringInSlice(iface.Name, networks) {
			networks = append(networks, iface.Name)
		}
	}

	return networks, nil
}

func networkIsInUse(c container, name string) bool {
	for _, d := range c.ExpandedDevices() {
		if d["type"] != "nic" {
			continue
		}

		if !shared.StringInSlice(d["nictype"], []string{"bridged", "macvlan"}) {
			continue
		}

		if d["parent"] == "" {
			continue
		}

		if d["parent"] == name {
			return true
		}
	}

	return false
}

func networkValidateName(name string) error {
	// Validate the length
	if len(name) < 2 {
		return fmt.Errorf("Interface name is too short (minimum 2 characters)")
	}

	if len(name) > 15 {
		return fmt.Errorf("Interface name is too long (maximum 15 characters)")
	}

	// Validate the character set
	match, _ := regexp.MatchString("^[-a-zA-Z0-9]*$", name)
	if !match {
		return fmt.Errorf("Interface name contains invalid characters")
	}

	return nil
}

// API endpoints
func networksGet(d *Daemon, r *http.Request) Response {
	recursionStr := r.FormValue("recursion")
	recursion, err := strconv.Atoi(recursionStr)
	if err != nil {
		recursion = 0
	}

	ifs, err := networkGetInterfaces(d)
	if err != nil {
		return InternalError(err)
	}

	resultString := []string{}
	resultMap := []shared.NetworkConfig{}
	for _, iface := range ifs {
		if recursion == 0 {
			resultString = append(resultString, fmt.Sprintf("/%s/networks/%s", shared.APIVersion, iface))
		} else {
			net, err := doNetworkGet(d, iface)
			if err != nil {
				continue
			}
			resultMap = append(resultMap, net)
		}
	}

	if recursion == 0 {
		return SyncResponse(true, resultString)
	}

	return SyncResponse(true, resultMap)
}

func networksPost(d *Daemon, r *http.Request) Response {
	req := shared.NetworkConfig{}

	// Parse the request
	err := json.NewDecoder(r.Body).Decode(&req)
	if err != nil {
		return BadRequest(err)
	}

	// Sanity checks
	if req.Name == "" {
		return BadRequest(fmt.Errorf("No name provided"))
	}

	err = networkValidateName(req.Name)
	if err != nil {
		return BadRequest(err)
	}

	if req.Type != "" && req.Type != "bridge" {
		return BadRequest(fmt.Errorf("Only 'bridge' type networks can be created"))
	}

	networks, err := networkGetInterfaces(d)
	if err != nil {
		return InternalError(err)
	}

	if shared.StringInSlice(req.Name, networks) {
		return BadRequest(fmt.Errorf("The network already exists"))
	}

	err = networkValidateConfig(req.Config)
	if err != nil {
		return BadRequest(err)
	}

	// Set some default values where needed
	if req.Config["bridge.mode"] == "fan" {
		if req.Config["fan.underlay_subnet"] == "" {
			req.Config["fan.underlay_subnet"] = "auto"
		}
	} else {
		if req.Config["ipv4.address"] == "" {
			req.Config["ipv4.address"] = "auto"
			if req.Config["ipv4.nat"] == "" {
				req.Config["ipv4.nat"] = "true"
			}
		}

		if req.Config["ipv6.address"] == "" {
			req.Config["ipv6.address"] = "auto"
			if req.Config["ipv6.nat"] == "" {
				req.Config["ipv6.nat"] = "true"
			}
		}
	}

	// Create the database entry
	_, err = dbNetworkCreate(d.db, req.Name, req.Config)
	if err != nil {
		return InternalError(
			fmt.Errorf("Error inserting %s into database: %s", req.Name, err))
	}

	return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/networks/%s", shared.APIVersion, req.Name))
}

var networksCmd = Command{name: "networks", get: networksGet, post: networksPost}

func networkGet(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]

	n, err := doNetworkGet(d, name)
	if err != nil {
		return SmartError(err)
	}

	etag := []interface{}{n.Name, n.Managed, n.Type, n.Config}

	return SyncResponseETag(true, &n, etag)
}

func doNetworkGet(d *Daemon, name string) (shared.NetworkConfig, error) {
	// Get some information
	osInfo, _ := net.InterfaceByName(name)
	_, dbInfo, _ := dbNetworkGet(d.db, name)

	// Sanity check
	if osInfo == nil && dbInfo == nil {
		return shared.NetworkConfig{}, os.ErrNotExist
	}

	// Prepare the response
	n := shared.NetworkConfig{}
	n.Name = name
	n.UsedBy = []string{}
	n.Config = map[string]string{}

	// Look for containers using the interface
	cts, err := dbContainersList(d.db, cTypeRegular)
	if err != nil {
		return shared.NetworkConfig{}, err
	}

	for _, ct := range cts {
		c, err := containerLoadByName(d, ct)
		if err != nil {
			return shared.NetworkConfig{}, err
		}

		if networkIsInUse(c, n.Name) {
			n.UsedBy = append(n.UsedBy, fmt.Sprintf("/%s/containers/%s", shared.APIVersion, ct))
		}
	}

	// Set the device type as needed
	if osInfo != nil && shared.IsLoopback(osInfo) {
		n.Type = "loopback"
	} else if dbInfo != nil || shared.PathExists(fmt.Sprintf("/sys/class/net/%s/bridge", n.Name)) {
		if dbInfo != nil {
			n.Managed = true
			n.Config = dbInfo.Config
		}

		n.Type = "bridge"
	} else if shared.PathExists(fmt.Sprintf("/sys/class/net/%s/device", n.Name)) {
		n.Type = "physical"
	} else if shared.PathExists(fmt.Sprintf("/sys/class/net/%s/bonding", n.Name)) {
		n.Type = "bond"
	} else {
		_, err := exec.Command("ovs-vsctl", "br-exists", n.Name).CombinedOutput()
		if err == nil {
			n.Type = "bridge"
		} else {
			n.Type = "unknown"
		}
	}

	return n, nil
}

func networkDelete(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]

	// Get the existing network
	_, dbInfo, _ := dbNetworkGet(d.db, name)
	if dbInfo == nil {
		return NotFound
	}

	// Sanity checks
	if len(dbInfo.UsedBy) != 0 {
		return BadRequest(fmt.Errorf("Network is currently in use)"))
	}

	// Remove the network
	err := dbNetworkDelete(d.db, name)
	if err != nil {
		return SmartError(err)
	}

	return EmptySyncResponse
}

func networkPost(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]
	req := shared.NetworkConfig{}

	// Parse the request
	err := json.NewDecoder(r.Body).Decode(&req)
	if err != nil {
		return BadRequest(err)
	}

	// Get the existing network
	_, dbInfo, _ := dbNetworkGet(d.db, name)
	if dbInfo == nil {
		return NotFound
	}

	// Sanity checks
	if len(dbInfo.UsedBy) != 0 {
		return BadRequest(fmt.Errorf("Network is currently in use)"))
	}

	if req.Name == "" {
		return BadRequest(fmt.Errorf("No name provided"))
	}

	err = networkValidateName(req.Name)
	if err != nil {
		return BadRequest(err)
	}

	// Check that the name isn't already in use
	networks, err := networkGetInterfaces(d)
	if err != nil {
		return InternalError(err)
	}

	if shared.StringInSlice(req.Name, networks) {
		return Conflict
	}

	// Rename the database entry
	err = dbNetworkRename(d.db, name, req.Name)
	if err != nil {
		return SmartError(err)
	}

	return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/networks/%s", shared.APIVersion, req.Name))
}

func networkPut(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]

	// Get the existing network
	_, dbInfo, _ := dbNetworkGet(d.db, name)
	if dbInfo == nil {
		return NotFound
	}

	// Validate the ETag
	etag := []interface{}{dbInfo.Name, dbInfo.Managed, dbInfo.Type, dbInfo.Config}

	err := etagCheck(r, etag)
	if err != nil {
		return PreconditionFailed(err)
	}

	req := shared.NetworkConfig{}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		return BadRequest(err)
	}

	return doNetworkUpdate(d, name, dbInfo.Config, req.Config)
}

func networkPatch(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]

	// Get the existing network
	_, dbInfo, _ := dbNetworkGet(d.db, name)
	if dbInfo == nil {
		return NotFound
	}

	// Validate the ETag
	etag := []interface{}{dbInfo.Name, dbInfo.Managed, dbInfo.Type, dbInfo.Config}

	err := etagCheck(r, etag)
	if err != nil {
		return PreconditionFailed(err)
	}

	req := shared.NetworkConfig{}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		return BadRequest(err)
	}

	// Config stacking
	if req.Config == nil {
		req.Config = map[string]string{}
	}

	for k, v := range dbInfo.Config {
		_, ok := req.Config[k]
		if !ok {
			req.Config[k] = v
		}
	}

	return doNetworkUpdate(d, name, dbInfo.Config, req.Config)
}

func doNetworkUpdate(d *Daemon, name string, oldConfig map[string]string, newConfig map[string]string) Response {
	err := networkValidateConfig(newConfig)
	if err != nil {
		return BadRequest(err)
	}

	if newConfig["bridge.mode"] == "fan" {
		if newConfig["fan.underlay_subnet"] == "" {
			newConfig["fan.underlay_subnet"] = "auto"
		}
	}

	err = dbNetworkUpdate(d.db, name, newConfig)
	if err != nil {
		return InternalError(err)
	}

	return EmptySyncResponse
}

var networkCmd = Command{name: "networks/{name}", get: networkGet, delete: networkDelete, post: networkPost, put: networkPut, patch: networkPatch}
<commit_msg>network: Detect IPv6 support<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"strconv"

	"github.com/gorilla/mux"

	"github.com/lxc/lxd/shared"
)

// Helper functions
func networkGetInterfaces(d *Daemon) ([]string, error) {
	networks, err := dbNetworks(d.db)
	if err != nil {
		return nil, err
	}

	ifaces, err := net.Interfaces()
	if err != nil {
		return nil, err
	}

	for _, iface := range ifaces {
		if !shared.StringInSlice(iface.Name, networks) {
			networks = append(networks, iface.Name)
		}
	}

	return networks, nil
}

func networkIsInUse(c container, name string) bool {
	for _, d := range c.ExpandedDevices() {
		if d["type"] != "nic" {
			continue
		}

		if !shared.StringInSlice(d["nictype"], []string{"bridged", "macvlan"}) {
			continue
		}

		if d["parent"] == "" {
			continue
		}

		if d["parent"] == name {
			return true
		}
	}

	return false
}

func networkValidateName(name string) error {
	// Validate the length
	if len(name) < 2 {
		return fmt.Errorf("Interface name is too short (minimum 2 characters)")
	}

	if len(name) > 15 {
		return fmt.Errorf("Interface name is too long (maximum 15 characters)")
	}

	// Validate the character set
	match, _ := regexp.MatchString("^[-a-zA-Z0-9]*$", name)
	if !match {
		return fmt.Errorf("Interface name contains invalid characters")
	}

	return nil
}

// API endpoints
func networksGet(d *Daemon, r *http.Request) Response {
	recursionStr := r.FormValue("recursion")
	recursion, err := strconv.Atoi(recursionStr)
	if err != nil {
		recursion = 0
	}

	ifs, err := networkGetInterfaces(d)
	if err != nil {
		return InternalError(err)
	}

	resultString := []string{}
	resultMap := []shared.NetworkConfig{}
	for _, iface := range ifs {
		if recursion == 0 {
			resultString = append(resultString, fmt.Sprintf("/%s/networks/%s", shared.APIVersion, iface))
		} else {
			net, err := doNetworkGet(d, iface)
			if err != nil {
				continue
			}
			resultMap = append(resultMap, net)
		}
	}

	if recursion == 0 {
		return SyncResponse(true, resultString)
	}

	return SyncResponse(true, resultMap)
}

func networksPost(d *Daemon, r *http.Request) Response {
	req := shared.NetworkConfig{}

	// Parse the request
	err := json.NewDecoder(r.Body).Decode(&req)
	if err != nil {
		return BadRequest(err)
	}

	// Sanity checks
	if req.Name == "" {
		return BadRequest(fmt.Errorf("No name provided"))
	}

	err = networkValidateName(req.Name)
	if err != nil {
		return BadRequest(err)
	}

	if req.Type != "" && req.Type != "bridge" {
		return BadRequest(fmt.Errorf("Only 'bridge' type networks can be created"))
	}

	networks, err := networkGetInterfaces(d)
	if err != nil {
		return InternalError(err)
	}

	if shared.StringInSlice(req.Name, networks) {
		return BadRequest(fmt.Errorf("The network already exists"))
	}

	err = networkValidateConfig(req.Config)
	if err != nil {
		return BadRequest(err)
	}

	// Set some default values where needed
	if req.Config["bridge.mode"] == "fan" {
		if req.Config["fan.underlay_subnet"] == "" {
			req.Config["fan.underlay_subnet"] = "auto"
		}
	} else {
		if req.Config["ipv4.address"] == "" {
			req.Config["ipv4.address"] = "auto"
			if req.Config["ipv4.nat"] == "" {
				req.Config["ipv4.nat"] = "true"
			}
		}

		if req.Config["ipv6.address"] == "" {
			content, err := ioutil.ReadFile("/proc/sys/net/ipv6/conf/default/disable_ipv6")
			if err == nil && string(content) == "0\n" {
				req.Config["ipv6.address"] = "auto"
				if req.Config["ipv6.nat"] == "" {
					req.Config["ipv6.nat"] = "true"
				}
			}
		}
	}

	// Create the database entry
	_, err = dbNetworkCreate(d.db, req.Name, req.Config)
	if err != nil {
		return InternalError(
			fmt.Errorf("Error inserting %s into database: %s", req.Name, err))
	}

	return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/networks/%s", shared.APIVersion, req.Name))
}

var networksCmd = Command{name: "networks", get: networksGet, post: networksPost}

func networkGet(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]

	n, err := doNetworkGet(d, name)
	if err != nil {
		return SmartError(err)
	}

	etag := []interface{}{n.Name, n.Managed, n.Type, n.Config}

	return SyncResponseETag(true, &n, etag)
}

func doNetworkGet(d *Daemon, name string) (shared.NetworkConfig, error) {
	// Get some information
	osInfo, _ := net.InterfaceByName(name)
	_, dbInfo, _ := dbNetworkGet(d.db, name)

	// Sanity check
	if osInfo == nil && dbInfo == nil {
		return shared.NetworkConfig{}, os.ErrNotExist
	}

	// Prepare the response
	n := shared.NetworkConfig{}
	n.Name = name
	n.UsedBy = []string{}
	n.Config = map[string]string{}

	// Look for containers using the interface
	cts, err := dbContainersList(d.db, cTypeRegular)
	if err != nil {
		return shared.NetworkConfig{}, err
	}

	for _, ct := range cts {
		c, err := containerLoadByName(d, ct)
		if err != nil {
			return shared.NetworkConfig{}, err
		}

		if networkIsInUse(c, n.Name) {
			n.UsedBy = append(n.UsedBy, fmt.Sprintf("/%s/containers/%s", shared.APIVersion, ct))
		}
	}

	// Set the device type as needed
	if osInfo != nil && shared.IsLoopback(osInfo) {
		n.Type = "loopback"
	} else if dbInfo != nil || shared.PathExists(fmt.Sprintf("/sys/class/net/%s/bridge", n.Name)) {
		if dbInfo != nil {
			n.Managed = true
			n.Config = dbInfo.Config
		}

		n.Type = "bridge"
	} else if shared.PathExists(fmt.Sprintf("/sys/class/net/%s/device", n.Name)) {
		n.Type = "physical"
	} else if shared.PathExists(fmt.Sprintf("/sys/class/net/%s/bonding", n.Name)) {
		n.Type = "bond"
	} else {
		_, err := exec.Command("ovs-vsctl", "br-exists", n.Name).CombinedOutput()
		if err == nil {
			n.Type = "bridge"
		} else {
			n.Type = "unknown"
		}
	}

	return n, nil
}

func networkDelete(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]

	// Get the existing network
	_, dbInfo, _ := dbNetworkGet(d.db, name)
	if dbInfo == nil {
		return NotFound
	}

	// Sanity checks
	if len(dbInfo.UsedBy) != 0 {
		return BadRequest(fmt.Errorf("Network is currently in use)"))
	}

	// Remove the network
	err := dbNetworkDelete(d.db, name)
	if err != nil {
		return SmartError(err)
	}

	return EmptySyncResponse
}

func networkPost(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]
	req := shared.NetworkConfig{}

	// Parse the request
	err := json.NewDecoder(r.Body).Decode(&req)
	if err != nil {
		return BadRequest(err)
	}

	// Get the existing network
	_, dbInfo, _ := dbNetworkGet(d.db, name)
	if dbInfo == nil {
		return NotFound
	}

	// Sanity checks
	if len(dbInfo.UsedBy) != 0 {
		return BadRequest(fmt.Errorf("Network is currently in use)"))
	}

	if req.Name == "" {
		return BadRequest(fmt.Errorf("No name provided"))
	}

	err = networkValidateName(req.Name)
	if err != nil {
		return BadRequest(err)
	}

	// Check that the name isn't already in use
	networks, err := networkGetInterfaces(d)
	if err != nil {
		return InternalError(err)
	}

	if shared.StringInSlice(req.Name, networks) {
		return Conflict
	}

	// Rename the database entry
	err = dbNetworkRename(d.db, name, req.Name)
	if err != nil {
		return SmartError(err)
	}

	return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/networks/%s", shared.APIVersion, req.Name))
}

func networkPut(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]

	// Get the existing network
	_, dbInfo, _ := dbNetworkGet(d.db, name)
	if dbInfo == nil {
		return NotFound
	}

	// Validate the ETag
	etag := []interface{}{dbInfo.Name, dbInfo.Managed, dbInfo.Type, dbInfo.Config}

	err := etagCheck(r, etag)
	if err != nil {
		return PreconditionFailed(err)
	}

	req := shared.NetworkConfig{}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		return BadRequest(err)
	}

	return doNetworkUpdate(d, name, dbInfo.Config, req.Config)
}

func networkPatch(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]

	// Get the existing network
	_, dbInfo, _ := dbNetworkGet(d.db, name)
	if dbInfo == nil {
		return NotFound
	}

	// Validate the ETag
	etag := []interface{}{dbInfo.Name, dbInfo.Managed, dbInfo.Type, dbInfo.Config}

	err := etagCheck(r, etag)
	if err != nil {
		return PreconditionFailed(err)
	}

	req := shared.NetworkConfig{}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		return BadRequest(err)
	}

	// Config stacking
	if req.Config == nil {
		req.Config = map[string]string{}
	}

	for k, v := range dbInfo.Config {
		_, ok := req.Config[k]
		if !ok {
			req.Config[k] = v
		}
	}

	return doNetworkUpdate(d, name, dbInfo.Config, req.Config)
}

func doNetworkUpdate(d *Daemon, name string, oldConfig map[string]string, newConfig map[string]string) Response {
	err := networkValidateConfig(newConfig)
	if err != nil {
		return BadRequest(err)
	}

	if newConfig["bridge.mode"] == "fan" {
		if newConfig["fan.underlay_subnet"] == "" {
			newConfig["fan.underlay_subnet"] = "auto"
		}
	}

	err = dbNetworkUpdate(d.db, name, newConfig)
	if err != nil {
		return InternalError(err)
	}

	return EmptySyncResponse
}

var networkCmd = Command{name: "networks/{name}", get: networkGet, delete: networkDelete, post: networkPost, put: networkPut, patch: networkPatch}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
)

type resp struct {
	Type       lxd.ResponseType       `json:"type"`
	Status     string                 `json:"status"`
	StatusCode shared.OperationStatus `json:"status_code"`
	Metadata   interface{}            `json:"metadata"`
}

type Response interface {
	Render(w http.ResponseWriter) error
}

type syncResponse struct {
	success  bool
	metadata interface{}
}

/*
  fname: name of the file without path
  headers: any other headers that should be set in the response
*/
type fileResponse struct {
	req      *http.Request
	path     string
	filename string
	headers  map[string]string
}

func FileResponse(r *http.Request, path string, filename string, headers map[string]string) Response {
	return &fileResponse{r, path, filename, headers}
}

func (r *fileResponse) Render(w http.ResponseWriter) error {

	f, err := os.Open(r.path)
	if err != nil {
		return err
	}
	defer f.Close()

	fi, err := f.Stat()
	if err != nil {
		return err
	}

	if r.headers != nil {
		for k, v := range r.headers {
			w.Header().Set(k, v)
		}
	}

	http.ServeContent(w, r.req, r.filename, fi.ModTime(), f)
	return nil
}

func (r *syncResponse) Render(w http.ResponseWriter) error {
	status := shared.Success
	if !r.success {
		status = shared.Failure
	}

	resp := resp{Type: lxd.Sync, Status: status.String(), StatusCode: status, Metadata: r.metadata}
	enc, err := json.Marshal(&resp)
	if err != nil {
		return err
	}

	_, err = w.Write(enc)
	return err
}

/*
 * This function and AsyncResponse are simply wrappers for the response so
 * users don't have to remember whether to use {}s or ()s when building
 * responses.
 */
func SyncResponse(success bool, metadata interface{}) Response {
	return &syncResponse{success, metadata}
}

var EmptySyncResponse = &syncResponse{true, make(map[string]interface{})}

type async struct {
	Type       lxd.ResponseType       `json:"type"`
	Status     string                 `json:"status"`
	StatusCode shared.OperationStatus `json:"status_code"`
	Operation  string                 `json:"operation"`
	Resources  map[string][]string    `json:"resources"`
	Metadata   interface{}            `json:"metadata"`
}

type asyncResponse struct {
	run        func() shared.OperationResult
	cancel     func() error
	ws         shared.OperationWebsocket
	containers []string
	done       chan shared.OperationResult
}

func (r *asyncResponse) Render(w http.ResponseWriter) error {

	op, err := CreateOperation(nil, r.run, r.cancel, r.ws)
	if err != nil {
		return err
	}

	err = StartOperation(op)
	if err != nil {
		return err
	}

	body := async{Type: lxd.Async, Status: shared.OK.String(), StatusCode: shared.OK, Operation: op}
	if r.ws != nil {
		body.Metadata = r.ws.Metadata()
	}

	if r.containers != nil && len(r.containers) > 0 {
		body.Resources = map[string][]string{}
		var containers []string
		for _, c := range r.containers {
			containers = append(containers, fmt.Sprintf("/%s/containers/%s", shared.Version, c))
		}

		body.Resources["containers"] = containers
	}

	w.Header().Set("Location", op)
	w.WriteHeader(202)
	return json.NewEncoder(w).Encode(body)
}

func AsyncResponse(run func() shared.OperationResult, cancel func() error) Response {
	return &asyncResponse{run: run, cancel: cancel}
}

func AsyncResponseWithWs(ws shared.OperationWebsocket, cancel func() error) Response {
	return &asyncResponse{run: ws.Do, cancel: cancel, ws: ws}
}

type ErrorResponse struct {
	code int
	msg  string
}

func (r *ErrorResponse) Render(w http.ResponseWriter) error {
	buf := bytes.Buffer{}
	err := json.NewEncoder(&buf).Encode(shared.Jmap{"type": lxd.Error, "error": r.msg, "error_code": r.code})

	if err != nil {
		return err
	}

	http.Error(w, buf.String(), r.code)
	return nil
}

/* Some standard responses */
var NotImplemented = &ErrorResponse{http.StatusNotImplemented, "not implemented"}
var NotFound = &ErrorResponse{http.StatusNotFound, "not found"}
var Forbidden = &ErrorResponse{http.StatusForbidden, "not authorized"}
var Conflict = &ErrorResponse{http.StatusConflict, "already exists"}

func BadRequest(err error) Response {
	return &ErrorResponse{http.StatusBadRequest, err.Error()}
}

func InternalError(err error) Response {
	return &ErrorResponse{http.StatusInternalServerError, err.Error()}
}

/*
 * Write the right error message based on err.
 */
func SmartError(err error) Response {
	switch err {
	case os.ErrNotExist:
		return NotFound
	case os.ErrPermission:
		return Forbidden
	default:
		return InternalError(err)
	}
}
<commit_msg>use APIVersion instead of Version in affected container urls<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
)

type resp struct {
	Type       lxd.ResponseType       `json:"type"`
	Status     string                 `json:"status"`
	StatusCode shared.OperationStatus `json:"status_code"`
	Metadata   interface{}            `json:"metadata"`
}

type Response interface {
	Render(w http.ResponseWriter) error
}

type syncResponse struct {
	success  bool
	metadata interface{}
}

/*
  fname: name of the file without path
  headers: any other headers that should be set in the response
*/
type fileResponse struct {
	req      *http.Request
	path     string
	filename string
	headers  map[string]string
}

func FileResponse(r *http.Request, path string, filename string, headers map[string]string) Response {
	return &fileResponse{r, path, filename, headers}
}

func (r *fileResponse) Render(w http.ResponseWriter) error {

	f, err := os.Open(r.path)
	if err != nil {
		return err
	}
	defer f.Close()

	fi, err := f.Stat()
	if err != nil {
		return err
	}

	if r.headers != nil {
		for k, v := range r.headers {
			w.Header().Set(k, v)
		}
	}

	http.ServeContent(w, r.req, r.filename, fi.ModTime(), f)
	return nil
}

func (r *syncResponse) Render(w http.ResponseWriter) error {
	status := shared.Success
	if !r.success {
		status = shared.Failure
	}

	resp := resp{Type: lxd.Sync, Status: status.String(), StatusCode: status, Metadata: r.metadata}
	enc, err := json.Marshal(&resp)
	if err != nil {
		return err
	}

	_, err = w.Write(enc)
	return err
}

/*
 * This function and AsyncResponse are simply wrappers for the response so
 * users don't have to remember whether to use {}s or ()s when building
 * responses.
 */
func SyncResponse(success bool, metadata interface{}) Response {
	return &syncResponse{success, metadata}
}

var EmptySyncResponse = &syncResponse{true, make(map[string]interface{})}

type async struct {
	Type       lxd.ResponseType       `json:"type"`
	Status     string                 `json:"status"`
	StatusCode shared.OperationStatus `json:"status_code"`
	Operation  string                 `json:"operation"`
	Resources  map[string][]string    `json:"resources"`
	Metadata   interface{}            `json:"metadata"`
}

type asyncResponse struct {
	run        func() shared.OperationResult
	cancel     func() error
	ws         shared.OperationWebsocket
	containers []string
	done       chan shared.OperationResult
}

func (r *asyncResponse) Render(w http.ResponseWriter) error {

	op, err := CreateOperation(nil, r.run, r.cancel, r.ws)
	if err != nil {
		return err
	}

	err = StartOperation(op)
	if err != nil {
		return err
	}

	body := async{Type: lxd.Async, Status: shared.OK.String(), StatusCode: shared.OK, Operation: op}
	if r.ws != nil {
		body.Metadata = r.ws.Metadata()
	}

	if r.containers != nil && len(r.containers) > 0 {
		body.Resources = map[string][]string{}
		var containers []string
		for _, c := range r.containers {
			containers = append(containers, fmt.Sprintf("/%s/containers/%s", shared.APIVersion, c))
		}

		body.Resources["containers"] = containers
	}

	w.Header().Set("Location", op)
	w.WriteHeader(202)
	return json.NewEncoder(w).Encode(body)
}

func AsyncResponse(run func() shared.OperationResult, cancel func() error) Response {
	return &asyncResponse{run: run, cancel: cancel}
}

func AsyncResponseWithWs(ws shared.OperationWebsocket, cancel func() error) Response {
	return &asyncResponse{run: ws.Do, cancel: cancel, ws: ws}
}

type ErrorResponse struct {
	code int
	msg  string
}

func (r *ErrorResponse) Render(w http.ResponseWriter) error {
	buf := bytes.Buffer{}
	err := json.NewEncoder(&buf).Encode(shared.Jmap{"type": lxd.Error, "error": r.msg, "error_code": r.code})

	if err != nil {
		return err
	}

	http.Error(w, buf.String(), r.code)
	return nil
}

/* Some standard responses */
var NotImplemented = &ErrorResponse{http.StatusNotImplemented, "not implemented"}
var NotFound = &ErrorResponse{http.StatusNotFound, "not found"}
var Forbidden = &ErrorResponse{http.StatusForbidden, "not authorized"}
var Conflict = &ErrorResponse{http.StatusConflict, "already exists"}

func BadRequest(err error) Response {
	return &ErrorResponse{http.StatusBadRequest, err.Error()}
}

func InternalError(err error) Response {
	return &ErrorResponse{http.StatusInternalServerError, err.Error()}
}

/*
 * Write the right error message based on err.
 */
func SmartError(err error) Response {
	switch err {
	case os.ErrNotExist:
		return NotFound
	case os.ErrPermission:
		return Forbidden
	default:
		return InternalError(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"

	"github.com/mattn/go-sqlite3"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
)

type resp struct {
	Type       lxd.ResponseType  `json:"type"`
	Status     string            `json:"status"`
	StatusCode shared.StatusCode `json:"status_code"`
	Metadata   interface{}       `json:"metadata"`
	Operation  string            `json:"operation"`
}

type Response interface {
	Render(w http.ResponseWriter) error
}

// Sync response
type syncResponse struct {
	success  bool
	metadata interface{}
}

func (r *syncResponse) Render(w http.ResponseWriter) error {
	status := shared.Success
	if !r.success {
		status = shared.Failure
	}

	resp := resp{Type: lxd.Sync, Status: status.String(), StatusCode: status, Metadata: r.metadata}
	return WriteJSON(w, resp)
}

func SyncResponse(success bool, metadata interface{}) Response {
	return &syncResponse{success, metadata}
}

var EmptySyncResponse = &syncResponse{true, make(map[string]interface{})}

// File transfer response
type fileResponseEntry struct {
	identifier string
	path       string
	filename   string
}

type fileResponse struct {
	req              *http.Request
	files            []fileResponseEntry
	headers          map[string]string
	removeAfterServe bool
}

func (r *fileResponse) Render(w http.ResponseWriter) error {
	if r.headers != nil {
		for k, v := range r.headers {
			w.Header().Set(k, v)
		}
	}

	// No file, well, it's easy then
	if len(r.files) == 0 {
		return nil
	}

	// For a single file, return it inline
	if len(r.files) == 1 {
		w.Header().Set("Content-Type", "application/octet-stream")
		w.Header().Set("Content-Disposition", fmt.Sprintf("inline;filename=%s", r.files[0].filename))

		f, err := os.Open(r.files[0].path)
		if err != nil {
			return err
		}
		defer f.Close()

		fi, err := f.Stat()
		if err != nil {
			return err
		}

		http.ServeContent(w, r.req, r.files[0].filename, fi.ModTime(), f)
		if r.removeAfterServe {
			os.Remove(r.files[0].filename)
		}

		return nil
	}

	// Now the complex multipart answer
	body := &bytes.Buffer{}
	mw := multipart.NewWriter(body)

	for _, entry := range r.files {
		fd, err := os.Open(entry.path)
		if err != nil {
			return err
		}
		defer fd.Close()

		fw, err := mw.CreateFormFile(entry.identifier, entry.filename)
		if err != nil {
			return err
		}

		_, err = io.Copy(fw, fd)
		if err != nil {
			return err
		}
	}

	mw.Close()
	w.Header().Set("Content-Type", mw.FormDataContentType())
	_, err := io.Copy(w, body)
	return err
}

func FileResponse(r *http.Request, files []fileResponseEntry, headers map[string]string, removeAfterServe bool) Response {
	return &fileResponse{r, files, headers, removeAfterServe}
}

// Operation response
type operationResponse struct {
	op *operation
}

func (r *operationResponse) Render(w http.ResponseWriter) error {
	_, err := r.op.Run()
	if err != nil {
		return err
	}

	url, md, err := r.op.Render()
	if err != nil {
		return err
	}

	body := resp{
		Type:       lxd.Async,
		Status:     shared.OperationCreated.String(),
		StatusCode: shared.OperationCreated,
		Operation:  url,
		Metadata:   md}

	w.Header().Set("Location", url)
	w.WriteHeader(202)

	return WriteJSON(w, body)
}

func OperationResponse(op *operation) Response {
	return &operationResponse{op}
}

// Error response
type errorResponse struct {
	code int
	msg  string
}

func (r *errorResponse) Render(w http.ResponseWriter) error {
	var output io.Writer

	buf := &bytes.Buffer{}
	output = buf
	var captured *bytes.Buffer
	if debug {
		captured = &bytes.Buffer{}
		output = io.MultiWriter(buf, captured)
	}

	err := json.NewEncoder(output).Encode(shared.Jmap{"type": lxd.Error, "error": r.msg, "error_code": r.code})

	if err != nil {
		return err
	}

	if debug {
		shared.DebugJson(captured)
	}
	http.Error(w, buf.String(), r.code)
	return nil
}

/* Some standard responses */
var NotImplemented = &errorResponse{http.StatusNotImplemented, "not implemented"}
var NotFound = &errorResponse{http.StatusNotFound, "not found"}
var Forbidden = &errorResponse{http.StatusForbidden, "not authorized"}
var Conflict = &errorResponse{http.StatusConflict, "already exists"}

func BadRequest(err error) Response {
	return &errorResponse{http.StatusBadRequest, err.Error()}
}

func InternalError(err error) Response {
	return &errorResponse{http.StatusInternalServerError, err.Error()}
}

/*
 * SmartError returns the right error message based on err.
 */
func SmartError(err error) Response {
	switch err {
	case nil:
		return EmptySyncResponse
	case os.ErrNotExist:
		return NotFound
	case sql.ErrNoRows:
		return NotFound
	case NoSuchObjectError:
		return NotFound
	case os.ErrPermission:
		return Forbidden
	case DbErrAlreadyDefined:
		return Conflict
	case sqlite3.ErrConstraintUnique:
		return Conflict
	default:
		return InternalError(err)
	}
}
<commit_msg>Fix temp file leak<commit_after>package main

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"

	"github.com/mattn/go-sqlite3"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
)

type resp struct {
	Type       lxd.ResponseType  `json:"type"`
	Status     string            `json:"status"`
	StatusCode shared.StatusCode `json:"status_code"`
	Metadata   interface{}       `json:"metadata"`
	Operation  string            `json:"operation"`
}

type Response interface {
	Render(w http.ResponseWriter) error
}

// Sync response
type syncResponse struct {
	success  bool
	metadata interface{}
}

func (r *syncResponse) Render(w http.ResponseWriter) error {
	status := shared.Success
	if !r.success {
		status = shared.Failure
	}

	resp := resp{Type: lxd.Sync, Status: status.String(), StatusCode: status, Metadata: r.metadata}
	return WriteJSON(w, resp)
}

func SyncResponse(success bool, metadata interface{}) Response {
	return &syncResponse{success, metadata}
}

var EmptySyncResponse = &syncResponse{true, make(map[string]interface{})}

// File transfer response
type fileResponseEntry struct {
	identifier string
	path       string
	filename   string
}

type fileResponse struct {
	req              *http.Request
	files            []fileResponseEntry
	headers          map[string]string
	removeAfterServe bool
}

func (r *fileResponse) Render(w http.ResponseWriter) error {
	if r.headers != nil {
		for k, v := range r.headers {
			w.Header().Set(k, v)
		}
	}

	// No file, well, it's easy then
	if len(r.files) == 0 {
		return nil
	}

	// For a single file, return it inline
	if len(r.files) == 1 {
		w.Header().Set("Content-Type", "application/octet-stream")
		w.Header().Set("Content-Disposition", fmt.Sprintf("inline;filename=%s", r.files[0].filename))

		f, err := os.Open(r.files[0].path)
		if err != nil {
			return err
		}
		defer f.Close()

		fi, err := f.Stat()
		if err != nil {
			return err
		}

		http.ServeContent(w, r.req, r.files[0].filename, fi.ModTime(), f)
		if r.removeAfterServe {
			err = os.Remove(r.files[0].path)
			if err != nil {
				return err
			}
		}

		return nil
	}

	// Now the complex multipart answer
	body := &bytes.Buffer{}
	mw := multipart.NewWriter(body)

	for _, entry := range r.files {
		fd, err := os.Open(entry.path)
		if err != nil {
			return err
		}
		defer fd.Close()

		fw, err := mw.CreateFormFile(entry.identifier, entry.filename)
		if err != nil {
			return err
		}

		_, err = io.Copy(fw, fd)
		if err != nil {
			return err
		}
	}

	mw.Close()
	w.Header().Set("Content-Type", mw.FormDataContentType())
	_, err := io.Copy(w, body)
	return err
}

func FileResponse(r *http.Request, files []fileResponseEntry, headers map[string]string, removeAfterServe bool) Response {
	return &fileResponse{r, files, headers, removeAfterServe}
}

// Operation response
type operationResponse struct {
	op *operation
}

func (r *operationResponse) Render(w http.ResponseWriter) error {
	_, err := r.op.Run()
	if err != nil {
		return err
	}

	url, md, err := r.op.Render()
	if err != nil {
		return err
	}

	body := resp{
		Type:       lxd.Async,
		Status:     shared.OperationCreated.String(),
		StatusCode: shared.OperationCreated,
		Operation:  url,
		Metadata:   md}

	w.Header().Set("Location", url)
	w.WriteHeader(202)

	return WriteJSON(w, body)
}

func OperationResponse(op *operation) Response {
	return &operationResponse{op}
}

// Error response
type errorResponse struct {
	code int
	msg  string
}

func (r *errorResponse) Render(w http.ResponseWriter) error {
	var output io.Writer

	buf := &bytes.Buffer{}
	output = buf
	var captured *bytes.Buffer
	if debug {
		captured = &bytes.Buffer{}
		output = io.MultiWriter(buf, captured)
	}

	err := json.NewEncoder(output).Encode(shared.Jmap{"type": lxd.Error, "error": r.msg, "error_code": r.code})

	if err != nil {
		return err
	}

	if debug {
		shared.DebugJson(captured)
	}
	http.Error(w, buf.String(), r.code)
	return nil
}

/* Some standard responses */
var NotImplemented = &errorResponse{http.StatusNotImplemented, "not implemented"}
var NotFound = &errorResponse{http.StatusNotFound, "not found"}
var Forbidden = &errorResponse{http.StatusForbidden, "not authorized"}
var Conflict = &errorResponse{http.StatusConflict, "already exists"}

func BadRequest(err error) Response {
	return &errorResponse{http.StatusBadRequest, err.Error()}
}

func InternalError(err error) Response {
	return &errorResponse{http.StatusInternalServerError, err.Error()}
}

/*
 * SmartError returns the right error message based on err.
 */
func SmartError(err error) Response {
	switch err {
	case nil:
		return EmptySyncResponse
	case os.ErrNotExist:
		return NotFound
	case sql.ErrNoRows:
		return NotFound
	case NoSuchObjectError:
		return NotFound
	case os.ErrPermission:
		return Forbidden
	case DbErrAlreadyDefined:
		return Conflict
	case sqlite3.ErrConstraintUnique:
		return Conflict
	default:
		return InternalError(err)
	}
}
<|endoftext|>


<commit_before>// Package api is an API Gateway
package api

import (
	"fmt"
	"net/http"
	"os"
	"strings"

	"github.com/go-acme/lego/v3/providers/dns/cloudflare"
	"github.com/gorilla/mux"
	"github.com/micro/cli/v2"
	"github.com/micro/go-micro/v2"
	ahandler "github.com/micro/go-micro/v2/api/handler"
	aapi "github.com/micro/go-micro/v2/api/handler/api"
	"github.com/micro/go-micro/v2/api/handler/event"
	ahttp "github.com/micro/go-micro/v2/api/handler/http"
	arpc "github.com/micro/go-micro/v2/api/handler/rpc"
	"github.com/micro/go-micro/v2/api/handler/web"
	"github.com/micro/go-micro/v2/api/resolver"
	"github.com/micro/go-micro/v2/api/resolver/grpc"
	"github.com/micro/go-micro/v2/api/resolver/host"
	"github.com/micro/go-micro/v2/api/resolver/path"
	"github.com/micro/go-micro/v2/api/router"
	regRouter "github.com/micro/go-micro/v2/api/router/registry"
	"github.com/micro/go-micro/v2/api/server"
	"github.com/micro/go-micro/v2/api/server/acme"
	"github.com/micro/go-micro/v2/api/server/acme/autocert"
	"github.com/micro/go-micro/v2/api/server/acme/certmagic"
	httpapi "github.com/micro/go-micro/v2/api/server/http"
	log "github.com/micro/go-micro/v2/logger"
	"github.com/micro/go-micro/v2/sync/memory"
	"github.com/micro/micro/v2/client/api/auth"
	"github.com/micro/micro/v2/internal/handler"
	"github.com/micro/micro/v2/internal/helper"
	rrmicro "github.com/micro/micro/v2/internal/resolver/api"
	"github.com/micro/micro/v2/internal/stats"
	"github.com/micro/micro/v2/plugin"
)

var (
	Name                  = "go.micro.api"
	Address               = ":8080"
	Handler               = "meta"
	Resolver              = "micro"
	RPCPath               = "/rpc"
	APIPath               = "/"
	ProxyPath             = "/{service:[a-zA-Z0-9]+}"
	Namespace             = "go.micro"
	Type                  = "api"
	HeaderPrefix          = "X-Micro-"
	EnableRPC             = false
	ACMEProvider          = "autocert"
	ACMEChallengeProvider = "cloudflare"
	ACMECA                = acme.LetsEncryptProductionCA
)

func Run(ctx *cli.Context, srvOpts ...micro.Option) {
	log.Init(log.WithFields(map[string]interface{}{"service": "api"}))

	if len(ctx.String("server_name")) > 0 {
		Name = ctx.String("server_name")
	}
	if len(ctx.String("address")) > 0 {
		Address = ctx.String("address")
	}
	if len(ctx.String("handler")) > 0 {
		Handler = ctx.String("handler")
	}
	if len(ctx.String("resolver")) > 0 {
		Resolver = ctx.String("resolver")
	}
	if len(ctx.String("enable_rpc")) > 0 {
		EnableRPC = ctx.Bool("enable_rpc")
	}
	if len(ctx.String("acme_provider")) > 0 {
		ACMEProvider = ctx.String("acme_provider")
	}
	if len(ctx.String("type")) > 0 {
		Type = ctx.String("type")
	}
	if len(ctx.String("namespace")) > 0 {
		// remove the service type from the namespace to allow for
		// backwards compatability
		Namespace = strings.TrimSuffix(ctx.String("namespace"), "."+Type)
	}

	// if the namespace was foo.api.v1, it would excape the trim suffix check
	// above and we want to use this as our API namespace
	var apiNamespace string
	if strings.Contains(Namespace, ".api.") {
		apiNamespace = Namespace
	} else {
		apiNamespace = Namespace + "." + Type
	}

	// append name to opts
	srvOpts = append(srvOpts, micro.Name(Name))

	// initialise service
	service := micro.NewService(srvOpts...)

	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	// Init API
	var opts []server.Option

	if ctx.Bool("enable_acme") {
		hosts := helper.ACMEHosts(ctx)
		opts = append(opts, server.EnableACME(true))
		opts = append(opts, server.ACMEHosts(hosts...))
		switch ACMEProvider {
		case "autocert":
			opts = append(opts, server.ACMEProvider(autocert.NewProvider()))
		case "certmagic":
			if ACMEChallengeProvider != "cloudflare" {
				log.Fatal("The only implemented DNS challenge provider is cloudflare")
			}

			apiToken := os.Getenv("CF_API_TOKEN")
			if len(apiToken) == 0 {
				log.Fatal("env variables CF_API_TOKEN and CF_ACCOUNT_ID must be set")
			}

			storage := certmagic.NewStorage(
				memory.NewSync(),
				service.Options().Store,
			)

			config := cloudflare.NewDefaultConfig()
			config.AuthToken = apiToken
			config.ZoneToken = apiToken
			challengeProvider, err := cloudflare.NewDNSProviderConfig(config)
			if err != nil {
				log.Fatal(err.Error())
			}

			opts = append(opts,
				server.ACMEProvider(
					certmagic.NewProvider(
						acme.AcceptToS(true),
						acme.CA(ACMECA),
						acme.Cache(storage),
						acme.ChallengeProvider(challengeProvider),
						acme.OnDemand(false),
					),
				),
			)
		default:
			log.Fatalf("%s is not a valid ACME provider\n", ACMEProvider)
		}
	} else if ctx.Bool("enable_tls") {
		config, err := helper.TLSConfig(ctx)
		if err != nil {
			fmt.Println(err.Error())
			return
		}

		opts = append(opts, server.EnableTLS(true))
		opts = append(opts, server.TLSConfig(config))
	}

	if ctx.Bool("enable_cors") {
		opts = append(opts, server.EnableCORS(true))
	}

	// create the router
	var h http.Handler
	r := mux.NewRouter()
	h = r

	if ctx.Bool("enable_stats") {
		st := stats.New()
		r.HandleFunc("/stats", st.StatsHandler)
		h = st.ServeHTTP(r)
		st.Start()
		defer st.Stop()
	}

	// return version and list of services
	r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "OPTIONS" {
			return
		}

		response := fmt.Sprintf(`{"version": "%s"}`, ctx.App.Version)
		w.Write([]byte(response))
	})

	// strip favicon.ico
	r.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {})

	// resolver options
	ropts := []resolver.Option{
		resolver.WithServicePrefix(Namespace + "." + Type),
		resolver.WithHandler(Handler),
	}

	// default resolver
	rr := rrmicro.NewResolver(ropts...)

	switch Resolver {
	case "host":
		rr = host.NewResolver(ropts...)
	case "path":
		rr = path.NewResolver(ropts...)
	case "grpc":
		rr = grpc.NewResolver(ropts...)
	}

	// register rpc handler
	if EnableRPC {
		log.Infof("Registering RPC Handler at %s", RPCPath)
		r.Handle(RPCPath, handler.NewRPCHandler(rr))
	}

	switch Handler {
	case "rpc":
		log.Infof("Registering API RPC Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithHandler(arpc.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		rp := arpc.NewHandler(
			ahandler.WithNamespace(apiNamespace),
			ahandler.WithRouter(rt),
			ahandler.WithClient(service.Client()),
		)
		r.PathPrefix(APIPath).Handler(rp)
	case "api":
		log.Infof("Registering API Request Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithHandler(aapi.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		ap := aapi.NewHandler(
			ahandler.WithNamespace(apiNamespace),
			ahandler.WithRouter(rt),
			ahandler.WithClient(service.Client()),
		)
		r.PathPrefix(APIPath).Handler(ap)
	case "event":
		log.Infof("Registering API Event Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithHandler(event.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		ev := event.NewHandler(
			ahandler.WithNamespace(apiNamespace),
			ahandler.WithRouter(rt),
			ahandler.WithClient(service.Client()),
		)
		r.PathPrefix(APIPath).Handler(ev)
	case "http", "proxy":
		log.Infof("Registering API HTTP Handler at %s", ProxyPath)
		rt := regRouter.NewRouter(
			router.WithHandler(ahttp.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		ht := ahttp.NewHandler(
			ahandler.WithNamespace(apiNamespace),
			ahandler.WithRouter(rt),
			ahandler.WithClient(service.Client()),
		)
		r.PathPrefix(ProxyPath).Handler(ht)
	case "web":
		log.Infof("Registering API Web Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithHandler(web.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		w := web.NewHandler(
			ahandler.WithNamespace(apiNamespace),
			ahandler.WithRouter(rt),
			ahandler.WithClient(service.Client()),
		)
		r.PathPrefix(APIPath).Handler(w)
	default:
		log.Infof("Registering API Default Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		r.PathPrefix(APIPath).Handler(handler.Meta(service, rt, Namespace+"."+Type))
	}

	// reverse wrap handler
	plugins := append(Plugins(), plugin.Plugins()...)
	for i := len(plugins); i > 0; i-- {
		h = plugins[i-1].Handler()(h)
	}

	// create the auth wrapper and the server
	authWrapper := auth.Wrapper(rr, Namespace+"."+Type)
	api := httpapi.NewServer(Address, server.WrapHandler(authWrapper))

	api.Init(opts...)
	api.Handle("/", h)

	// Start API
	if err := api.Start(); err != nil {
		log.Fatal(err)
	}

	// Run server
	if err := service.Run(); err != nil {
		log.Fatal(err)
	}

	// Stop API
	if err := api.Stop(); err != nil {
		log.Fatal(err)
	}
}

func Commands(options ...micro.Option) []*cli.Command {
	command := &cli.Command{
		Name:  "api",
		Usage: "Run the api gateway",
		Action: func(ctx *cli.Context) error {
			Run(ctx, options...)
			return nil
		},
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:    "address",
				Usage:   "Set the api address e.g 0.0.0.0:8080",
				EnvVars: []string{"MICRO_API_ADDRESS"},
			},
			&cli.StringFlag{
				Name:    "handler",
				Usage:   "Specify the request handler to be used for mapping HTTP requests to services; {api, event, http, rpc}",
				EnvVars: []string{"MICRO_API_HANDLER"},
			},
			&cli.StringFlag{
				Name:    "namespace",
				Usage:   "Set the namespace used by the API e.g. com.example",
				EnvVars: []string{"MICRO_API_NAMESPACE"},
			},
			&cli.StringFlag{
				Name:    "type",
				Usage:   "Set the service type used by the API e.g. api",
				EnvVars: []string{"MICRO_API_TYPE"},
			},
			&cli.StringFlag{
				Name:    "resolver",
				Usage:   "Set the hostname resolver used by the API {host, path, grpc}",
				EnvVars: []string{"MICRO_API_RESOLVER"},
			},
			&cli.BoolFlag{
				Name:    "enable_rpc",
				Usage:   "Enable call the backend directly via /rpc",
				EnvVars: []string{"MICRO_API_ENABLE_RPC"},
			},
			&cli.BoolFlag{
				Name:    "enable_cors",
				Usage:   "Enable CORS, allowing the API to be called by frontend applications",
				EnvVars: []string{"MICRO_API_ENABLE_CORS"},
				Value:   true,
			},
		},
	}

	for _, p := range Plugins() {
		if cmds := p.Commands(); len(cmds) > 0 {
			command.Subcommands = append(command.Subcommands, cmds...)
		}

		if flags := p.Flags(); len(flags) > 0 {
			command.Flags = append(command.Flags, flags...)
		}
	}

	return []*cli.Command{command}
}
<commit_msg>client/api: bugfix for namespace with versioning<commit_after>// Package api is an API Gateway
package api

import (
	"fmt"
	"net/http"
	"os"
	"strings"

	"github.com/go-acme/lego/v3/providers/dns/cloudflare"
	"github.com/gorilla/mux"
	"github.com/micro/cli/v2"
	"github.com/micro/go-micro/v2"
	ahandler "github.com/micro/go-micro/v2/api/handler"
	aapi "github.com/micro/go-micro/v2/api/handler/api"
	"github.com/micro/go-micro/v2/api/handler/event"
	ahttp "github.com/micro/go-micro/v2/api/handler/http"
	arpc "github.com/micro/go-micro/v2/api/handler/rpc"
	"github.com/micro/go-micro/v2/api/handler/web"
	"github.com/micro/go-micro/v2/api/resolver"
	"github.com/micro/go-micro/v2/api/resolver/grpc"
	"github.com/micro/go-micro/v2/api/resolver/host"
	"github.com/micro/go-micro/v2/api/resolver/path"
	"github.com/micro/go-micro/v2/api/router"
	regRouter "github.com/micro/go-micro/v2/api/router/registry"
	"github.com/micro/go-micro/v2/api/server"
	"github.com/micro/go-micro/v2/api/server/acme"
	"github.com/micro/go-micro/v2/api/server/acme/autocert"
	"github.com/micro/go-micro/v2/api/server/acme/certmagic"
	httpapi "github.com/micro/go-micro/v2/api/server/http"
	log "github.com/micro/go-micro/v2/logger"
	"github.com/micro/go-micro/v2/sync/memory"
	"github.com/micro/micro/v2/client/api/auth"
	"github.com/micro/micro/v2/internal/handler"
	"github.com/micro/micro/v2/internal/helper"
	rrmicro "github.com/micro/micro/v2/internal/resolver/api"
	"github.com/micro/micro/v2/internal/stats"
	"github.com/micro/micro/v2/plugin"
)

var (
	Name                  = "go.micro.api"
	Address               = ":8080"
	Handler               = "meta"
	Resolver              = "micro"
	RPCPath               = "/rpc"
	APIPath               = "/"
	ProxyPath             = "/{service:[a-zA-Z0-9]+}"
	Namespace             = "go.micro"
	Type                  = "api"
	HeaderPrefix          = "X-Micro-"
	EnableRPC             = false
	ACMEProvider          = "autocert"
	ACMEChallengeProvider = "cloudflare"
	ACMECA                = acme.LetsEncryptProductionCA
)

func Run(ctx *cli.Context, srvOpts ...micro.Option) {
	log.Init(log.WithFields(map[string]interface{}{"service": "api"}))

	if len(ctx.String("server_name")) > 0 {
		Name = ctx.String("server_name")
	}
	if len(ctx.String("address")) > 0 {
		Address = ctx.String("address")
	}
	if len(ctx.String("handler")) > 0 {
		Handler = ctx.String("handler")
	}
	if len(ctx.String("resolver")) > 0 {
		Resolver = ctx.String("resolver")
	}
	if len(ctx.String("enable_rpc")) > 0 {
		EnableRPC = ctx.Bool("enable_rpc")
	}
	if len(ctx.String("acme_provider")) > 0 {
		ACMEProvider = ctx.String("acme_provider")
	}
	if len(ctx.String("type")) > 0 {
		Type = ctx.String("type")
	}
	if len(ctx.String("namespace")) > 0 {
		// remove the service type from the namespace to allow for
		// backwards compatability
		Namespace = strings.TrimSuffix(ctx.String("namespace"), "."+Type)
	}

	// if the namespace was foo.api.v1, it would excape the trim suffix check
	// above and we want to use this as our API namespace
	var apiNamespace string
	if strings.Contains(Namespace, ".api.") {
		apiNamespace = Namespace
	} else {
		apiNamespace = Namespace + "." + Type
	}

	// append name to opts
	srvOpts = append(srvOpts, micro.Name(Name))

	// initialise service
	service := micro.NewService(srvOpts...)

	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	// Init API
	var opts []server.Option

	if ctx.Bool("enable_acme") {
		hosts := helper.ACMEHosts(ctx)
		opts = append(opts, server.EnableACME(true))
		opts = append(opts, server.ACMEHosts(hosts...))
		switch ACMEProvider {
		case "autocert":
			opts = append(opts, server.ACMEProvider(autocert.NewProvider()))
		case "certmagic":
			if ACMEChallengeProvider != "cloudflare" {
				log.Fatal("The only implemented DNS challenge provider is cloudflare")
			}

			apiToken := os.Getenv("CF_API_TOKEN")
			if len(apiToken) == 0 {
				log.Fatal("env variables CF_API_TOKEN and CF_ACCOUNT_ID must be set")
			}

			storage := certmagic.NewStorage(
				memory.NewSync(),
				service.Options().Store,
			)

			config := cloudflare.NewDefaultConfig()
			config.AuthToken = apiToken
			config.ZoneToken = apiToken
			challengeProvider, err := cloudflare.NewDNSProviderConfig(config)
			if err != nil {
				log.Fatal(err.Error())
			}

			opts = append(opts,
				server.ACMEProvider(
					certmagic.NewProvider(
						acme.AcceptToS(true),
						acme.CA(ACMECA),
						acme.Cache(storage),
						acme.ChallengeProvider(challengeProvider),
						acme.OnDemand(false),
					),
				),
			)
		default:
			log.Fatalf("%s is not a valid ACME provider\n", ACMEProvider)
		}
	} else if ctx.Bool("enable_tls") {
		config, err := helper.TLSConfig(ctx)
		if err != nil {
			fmt.Println(err.Error())
			return
		}

		opts = append(opts, server.EnableTLS(true))
		opts = append(opts, server.TLSConfig(config))
	}

	if ctx.Bool("enable_cors") {
		opts = append(opts, server.EnableCORS(true))
	}

	// create the router
	var h http.Handler
	r := mux.NewRouter()
	h = r

	if ctx.Bool("enable_stats") {
		st := stats.New()
		r.HandleFunc("/stats", st.StatsHandler)
		h = st.ServeHTTP(r)
		st.Start()
		defer st.Stop()
	}

	// return version and list of services
	r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "OPTIONS" {
			return
		}

		response := fmt.Sprintf(`{"version": "%s"}`, ctx.App.Version)
		w.Write([]byte(response))
	})

	// strip favicon.ico
	r.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {})

	// resolver options
	ropts := []resolver.Option{
		resolver.WithServicePrefix(apiNamespace),
		resolver.WithHandler(Handler),
	}

	// default resolver
	rr := rrmicro.NewResolver(ropts...)

	switch Resolver {
	case "host":
		rr = host.NewResolver(ropts...)
	case "path":
		rr = path.NewResolver(ropts...)
	case "grpc":
		rr = grpc.NewResolver(ropts...)
	}

	// register rpc handler
	if EnableRPC {
		log.Infof("Registering RPC Handler at %s", RPCPath)
		r.Handle(RPCPath, handler.NewRPCHandler(rr))
	}

	switch Handler {
	case "rpc":
		log.Infof("Registering API RPC Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithHandler(arpc.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		rp := arpc.NewHandler(
			ahandler.WithNamespace(apiNamespace),
			ahandler.WithRouter(rt),
			ahandler.WithClient(service.Client()),
		)
		r.PathPrefix(APIPath).Handler(rp)
	case "api":
		log.Infof("Registering API Request Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithHandler(aapi.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		ap := aapi.NewHandler(
			ahandler.WithNamespace(apiNamespace),
			ahandler.WithRouter(rt),
			ahandler.WithClient(service.Client()),
		)
		r.PathPrefix(APIPath).Handler(ap)
	case "event":
		log.Infof("Registering API Event Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithHandler(event.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		ev := event.NewHandler(
			ahandler.WithNamespace(apiNamespace),
			ahandler.WithRouter(rt),
			ahandler.WithClient(service.Client()),
		)
		r.PathPrefix(APIPath).Handler(ev)
	case "http", "proxy":
		log.Infof("Registering API HTTP Handler at %s", ProxyPath)
		rt := regRouter.NewRouter(
			router.WithHandler(ahttp.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		ht := ahttp.NewHandler(
			ahandler.WithNamespace(apiNamespace),
			ahandler.WithRouter(rt),
			ahandler.WithClient(service.Client()),
		)
		r.PathPrefix(ProxyPath).Handler(ht)
	case "web":
		log.Infof("Registering API Web Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithHandler(web.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		w := web.NewHandler(
			ahandler.WithNamespace(apiNamespace),
			ahandler.WithRouter(rt),
			ahandler.WithClient(service.Client()),
		)
		r.PathPrefix(APIPath).Handler(w)
	default:
		log.Infof("Registering API Default Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		r.PathPrefix(APIPath).Handler(handler.Meta(service, rt, Namespace+"."+Type))
	}

	// reverse wrap handler
	plugins := append(Plugins(), plugin.Plugins()...)
	for i := len(plugins); i > 0; i-- {
		h = plugins[i-1].Handler()(h)
	}

	// create the auth wrapper and the server
	authWrapper := auth.Wrapper(rr, Namespace+"."+Type)
	api := httpapi.NewServer(Address, server.WrapHandler(authWrapper))

	api.Init(opts...)
	api.Handle("/", h)

	// Start API
	if err := api.Start(); err != nil {
		log.Fatal(err)
	}

	// Run server
	if err := service.Run(); err != nil {
		log.Fatal(err)
	}

	// Stop API
	if err := api.Stop(); err != nil {
		log.Fatal(err)
	}
}

func Commands(options ...micro.Option) []*cli.Command {
	command := &cli.Command{
		Name:  "api",
		Usage: "Run the api gateway",
		Action: func(ctx *cli.Context) error {
			Run(ctx, options...)
			return nil
		},
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:    "address",
				Usage:   "Set the api address e.g 0.0.0.0:8080",
				EnvVars: []string{"MICRO_API_ADDRESS"},
			},
			&cli.StringFlag{
				Name:    "handler",
				Usage:   "Specify the request handler to be used for mapping HTTP requests to services; {api, event, http, rpc}",
				EnvVars: []string{"MICRO_API_HANDLER"},
			},
			&cli.StringFlag{
				Name:    "namespace",
				Usage:   "Set the namespace used by the API e.g. com.example",
				EnvVars: []string{"MICRO_API_NAMESPACE"},
			},
			&cli.StringFlag{
				Name:    "type",
				Usage:   "Set the service type used by the API e.g. api",
				EnvVars: []string{"MICRO_API_TYPE"},
			},
			&cli.StringFlag{
				Name:    "resolver",
				Usage:   "Set the hostname resolver used by the API {host, path, grpc}",
				EnvVars: []string{"MICRO_API_RESOLVER"},
			},
			&cli.BoolFlag{
				Name:    "enable_rpc",
				Usage:   "Enable call the backend directly via /rpc",
				EnvVars: []string{"MICRO_API_ENABLE_RPC"},
			},
			&cli.BoolFlag{
				Name:    "enable_cors",
				Usage:   "Enable CORS, allowing the API to be called by frontend applications",
				EnvVars: []string{"MICRO_API_ENABLE_CORS"},
				Value:   true,
			},
		},
	}

	for _, p := range Plugins() {
		if cmds := p.Commands(); len(cmds) > 0 {
			command.Subcommands = append(command.Subcommands, cmds...)
		}

		if flags := p.Flags(); len(flags) > 0 {
			command.Flags = append(command.Flags, flags...)
		}
	}

	return []*cli.Command{command}
}
<|endoftext|>


<commit_before>package ui

import (
	"log"

	"github.com/nsf/termbox-go"
)

// TermboxUI handles ui using termbox-go
type TermboxUI struct {
	quit chan bool
}

// Run enters the main ui loop untill close is called on the quit channel.
func (tbw *TermboxUI) Run() {
	tbw.initializeQuitChannel()

	termbox.Init()

	go tbw.handleEvents()
	tbw.waitForQuit()
}

func (tbw *TermboxUI) initializeQuitChannel() {
	tbw.quit = make(chan bool)
}

// Stop terminates the Run loop.
func (tbw *TermboxUI) Stop() {
	close(tbw.quit)
}

func (tbw *TermboxUI) handleEvents() {
loop:
	for {
		select {
		case <-tbw.quit:
			break loop
		default:
			tbw.handleEvent()
		}
	}
}

func (tbw *TermboxUI) handleEvent() {
	event := termbox.PollEvent()
	log.Println(event)
}

func (tbw *TermboxUI) waitForQuit() {
	select {
	case <-tbw.quit:
		termbox.Close()
		return
	}
}
<commit_msg>Refactor UI initialization.<commit_after>package ui

import (
	"log"

	"github.com/nsf/termbox-go"
)

// TermboxUI handles ui using termbox-go
type TermboxUI struct {
	quit chan bool
}

// Run enters the main UI loop untill Stop() is called.
func (tbw *TermboxUI) Run() {
	tbw.initialize()

	go tbw.handleEvents()
	tbw.waitForQuit()
	tbw.cleanUp()
}

// Stop terminates the Run loop.
func (tbw *TermboxUI) Stop() {
	if tbw.quit == nil {
		return
	}
	close(tbw.quit)
}

func (tbw *TermboxUI) initialize() {
	tbw.initializeQuitChannel()
	termbox.Init()
}

func (tbw *TermboxUI) cleanUp() {
	termbox.Close()
}

func (tbw *TermboxUI) initializeQuitChannel() {
	tbw.quit = make(chan bool)
}

func (tbw *TermboxUI) handleEvents() {
loop:
	for {
		select {
		case <-tbw.quit:
			break loop
		default:
			tbw.handleEvent()
		}
	}
}

func (tbw *TermboxUI) handleEvent() {
	event := termbox.PollEvent()
	log.Println(event)
}

func (tbw *TermboxUI) waitForQuit() {
	select {
	case <-tbw.quit:
		return
	}
}
<|endoftext|>


<commit_before>package scheduler

import (
	"log"
	"math/rand"
	"time"

	"github.com/coreos/coreinit/job"
	"github.com/coreos/coreinit/machine"
	"github.com/coreos/coreinit/registry"
)

const (
	DefaultClaimTTL = "5s"
)

type Scheduler struct {
	Registry *registry.Registry
	Machine  *machine.Machine
	ClaimTTL time.Duration
}

func New(registry *registry.Registry, machine *machine.Machine) *Scheduler {
	claimTTL, _ := time.ParseDuration(DefaultClaimTTL)
	return &Scheduler{registry, machine, claimTTL}
}

func (s *Scheduler) DoSchedule() {
	for true {
		// Let's not be a job-hog
		time.Sleep(time.Second)

		jobs := s.Registry.GetGlobalJobs()
		if len(jobs) == 0 {
			continue
		}

		machines := s.Registry.GetActiveMachines()

		job := s.ClaimJob(jobs)
		if job == nil {
			continue
		}

		// If someone has reported state for this job, we assume
		// it's good to go.
		if jobState := s.Registry.GetJobState(job); jobState != nil {
			continue
		}

		// For now, we assume that if we can initially acquire the lock
		// we're safe to move forward with scheduling. This is not ideal.
		s.ScheduleJob(job, machines)
	}
}

func (s *Scheduler) ClaimJob(jobs map[string]job.Job) *job.Job {
	for _, job := range jobs {
		if s.Registry.AcquireLock(job.Name, s.Machine.BootId, s.ClaimTTL) {
			log.Println("Acquired lock on job", job.Name)
			return &job
		}
	}
	return nil
}

func (s *Scheduler) ScheduleJob(job *job.Job, machines map[string]machine.Machine) {
	machineSlice := make([]machine.Machine, 0)
	for _, v := range machines {
		machineSlice = append(machineSlice, v)
	}

	target := rand.Intn(len(machineSlice))
	machine := machineSlice[target]

	log.Println("Scheduling job", job.Name, "to machine", machine.BootId)
	s.Registry.ScheduleJob(job, &machine)
}
<commit_msg>fix(scheduler): Schedule .socket jobs to the host that holds the .service job<commit_after>package scheduler

import (
	"log"
	"math/rand"
	"regexp"
	"time"

	"github.com/coreos/coreinit/job"
	"github.com/coreos/coreinit/machine"
	"github.com/coreos/coreinit/registry"
)

const (
	DefaultClaimTTL = "5s"
)

type Scheduler struct {
	Registry *registry.Registry
	Machine  *machine.Machine
	ClaimTTL time.Duration
}

func New(registry *registry.Registry, machine *machine.Machine) *Scheduler {
	claimTTL, _ := time.ParseDuration(DefaultClaimTTL)
	return &Scheduler{registry, machine, claimTTL}
}

func (s *Scheduler) DoSchedule() {
	for true {
		// Let's not be a job-hog
		time.Sleep(time.Second)

		jobs := s.Registry.GetGlobalJobs()
		if len(jobs) == 0 {
			continue
		}

		machines := s.Registry.GetActiveMachines()

		job := s.ClaimJob(jobs)
		if job == nil {
			continue
		}

		// If someone has reported state for this job, we assume
		// it's good to go.
		if jobState := s.Registry.GetJobState(job); jobState != nil {
			continue
		}

		// For now, we assume that if we can initially acquire the lock
		// we're safe to move forward with scheduling. This is not ideal.
		s.ScheduleJob(job, machines)
	}
}

func (s *Scheduler) ClaimJob(jobs map[string]job.Job) *job.Job {
	for _, job := range jobs {
		if s.Registry.AcquireLock(job.Name, s.Machine.BootId, s.ClaimTTL) {
			log.Println("Acquired lock on job", job.Name)
			return &job
		}
	}
	return nil
}

func (s *Scheduler) ScheduleJob(j *job.Job, machines map[string]machine.Machine) {
	var mach *machine.Machine
	// If the Job being scheduled is a systemd service unit, we assume we
	// can put it anywhere. If not, we must find the machine where the
	// Job's related service file is currently scheuled.
	if j.Payload.Type == "systemd-service" {
		mach = pickRandomMachine(machines)
	} else {
		// This is intended to match a standard filetype (i.e. '.socket' in 'web.socket')
		re := regexp.MustCompile("\\.(.[a-z]*)$")
		serviceName := re.ReplaceAllString(j.Name, ".service")

		service := job.NewJob(serviceName, nil, nil)
		state := s.Registry.GetJobState(service)

		if state == nil {
			log.Printf("Unable to schedule job %s since corresponding " +
			           "service job %s could not be found", j.Name, serviceName)
		} else {
			mach = state.Machine
		}
	}

	if mach == nil {
		log.Printf("Not scheduling job %s", j.Name)
	} else {
		log.Println("Scheduling job", j.Name, "to machine", mach.BootId)
		s.Registry.ScheduleJob(j, mach)
	}
}

func pickRandomMachine(machines map[string]machine.Machine) *machine.Machine{
	machineSlice := make([]machine.Machine, 0)
	for _, v := range machines {
		machineSlice = append(machineSlice, v)
	}

	target := rand.Intn(len(machineSlice))
	return &machineSlice[target]
}
<|endoftext|>


<commit_before>package client

import (
	"bytes"
	"testing"
)

func TestBytes(t *testing.T) {
	r := client.Cmd("get", "k").Bytes()

	if !bytes.Equal(r, []byte("v")) {
		t.Logf("expect bytes [% #x], but get[ % #x]\n", []byte("v"), r)
		t.Fail()
	}
}

func TestString(t *testing.T) {
	r := client.Cmd("get", "k").String()

	if r != "v" {
		t.Logf("expect string [v], but get[ %s]\n", r)
		t.Fail()
	}

}

func TestInt(t *testing.T) {
	r := client.Cmd("get", "k").Int()

	if r != 0 {
		t.Logf("expect int [0], but get[ %d]\n", r)
		t.Fail()
	}

	client.Cmd("set", "int", "9223372036854775807")
	r = client.Cmd("get", "int").Int()

	if r != 9223372036854775807 {
		t.Logf("expect int [9223372036854775807], but get[ %d]\n", r)
		t.Fail()
	}

	// test value out of range
	client.Cmd("set", "int", "9223372036854775808")
	r = client.Cmd("get", "int").Int()

	if r != 9223372036854775807 {
		t.Logf("expect int [9223372036854775807], but get[ %d]\n", r)
		t.Fail()
	}
}

func TestInt32(t *testing.T) {
	client.Cmd("set", "int32", "2147483647")
	r := client.Cmd("get", "int32").Int32()

	if r != 2147483647 {
		t.Logf("expect int [2147483647], but get[ %d]\n", r)
		t.Fail()
	}

	client.Cmd("set", "int32", "-2147483648")
	r = client.Cmd("get", "int32").Int32()

	if r != -2147483648 {
		t.Logf("expect int32 [-2147483648], but get[ %d]\n", r)
		t.Fail()
	}

	client.Cmd("set", "int32", "2147483648")
	r = client.Cmd("get", "int32").Int32()

	if r != 2147483647 {
		t.Logf("expect int32 [2147483647], but get[ %d]\n", r)
		t.Fail()
	}
}

func TestInt64(t *testing.T) {
	client.Cmd("set", "int64", "9223372036854775807")
	r := client.Cmd("get", "int64").Int64()

	if r != 9223372036854775807 {
		t.Logf("expect int [9223372036854775807], but get[ %d]\n", r)
		t.Fail()
	}

	client.Cmd("set", "int64", "-9223372036854775808")
	r = client.Cmd("get", "int64").Int64()

	if r != -9223372036854775808 {
		t.Logf("expect int64 [-9223372036854775808], but get[ %d]\n", r)
		t.Fail()
	}

	client.Cmd("set", "int64", "9223372036854775808")
	r = client.Cmd("get", "int64").Int64()

	if r != 9223372036854775807 {
		t.Logf("expect int64 [9223372036854775807], but get[ %d]\n", r)
		t.Fail()
	}
}

func TestUint8(t *testing.T) {
	client.Cmd("set", "uint8", "255")
	r := client.Cmd("get", "int64").Uint8()

	if r != 255 {
		t.Logf("expect int [255], but get[ %d]\n", r)
		t.Fail()
	}

	client.Cmd("set", "uint8", "256")
	r = client.Cmd("get", "uint8").Uint8()

	if r != 255 {
		t.Logf("expect int8 [255], but get[ %d]\n", r)
		t.Fail()
	}

	client.Cmd("set", "uint8", "-1")
	r = client.Cmd("get", "uint8").Uint8()

	if r != 0 {
		t.Logf("expect uint8 [0], but get[ %d]\n", r)
		t.Fail()
	}
}

func TestUint16(t *testing.T) {
	client.Cmd("set", "uint16", "65535")
	r := client.Cmd("get", "int64").Uint16()

	if r != 65535 {
		t.Logf("expect int16 [65535], but get[ %d]\n", r)
		t.Fail()
	}

	client.Cmd("set", "uint16", "65536")
	r = client.Cmd("get", "uint16").Uint16()

	if r != 65535 {
		t.Logf("expect int16 [65535], but get[ %d]\n", r)
		t.Fail()
	}

	client.Cmd("set", "uint16", "-1")
	r = client.Cmd("get", "uint16").Uint16()

	if r != 0 {
		t.Logf("expect uint16 [0], but get[ %d]\n", r)
		t.Fail()
	}
}
<commit_msg>Fix test log info.<commit_after>package client

import (
	"bytes"
	"testing"
)

func TestBytes(t *testing.T) {
	r := client.Cmd("get", "k").Bytes()

	if !bytes.Equal(r, []byte("v")) {
		t.Logf("expect bytes [% #x], but get[ % #x]\n", []byte("v"), r)
		t.Fail()
	}
}

func TestString(t *testing.T) {
	r := client.Cmd("get", "k").String()

	if r != "v" {
		t.Logf("expect string [v], but get[ %s]\n", r)
		t.Fail()
	}

}

func TestInt(t *testing.T) {
	r := client.Cmd("get", "k").Int()

	if r != 0 {
		t.Logf("expect int [0], but get[ %d]\n", r)
		t.Fail()
	}

	client.Cmd("set", "int", "9223372036854775807")
	r = client.Cmd("get", "int").Int()

	if r != 9223372036854775807 {
		t.Logf("expect int [9223372036854775807], but get[ %d]\n", r)
		t.Fail()
	}

	// test value out of range
	client.Cmd("set", "int", "9223372036854775808")
	r = client.Cmd("get", "int").Int()

	if r != 9223372036854775807 {
		t.Logf("expect int [9223372036854775807], but get[ %d]\n", r)
		t.Fail()
	}
}

func TestInt32(t *testing.T) {
	client.Cmd("set", "int32", "2147483647")
	r := client.Cmd("get", "int32").Int32()

	if r != 2147483647 {
		t.Logf("expect int32 [2147483647], but get[ %d]\n", r)
		t.Fail()
	}

	client.Cmd("set", "int32", "-2147483648")
	r = client.Cmd("get", "int32").Int32()

	if r != -2147483648 {
		t.Logf("expect int32 [-2147483648], but get[ %d]\n", r)
		t.Fail()
	}

	client.Cmd("set", "int32", "2147483648")
	r = client.Cmd("get", "int32").Int32()

	if r != 2147483647 {
		t.Logf("expect int32 [2147483647], but get[ %d]\n", r)
		t.Fail()
	}
}

func TestInt64(t *testing.T) {
	client.Cmd("set", "int64", "9223372036854775807")
	r := client.Cmd("get", "int64").Int64()

	if r != 9223372036854775807 {
		t.Logf("expect int64 [9223372036854775807], but get[ %d]\n", r)
		t.Fail()
	}

	client.Cmd("set", "int64", "-9223372036854775808")
	r = client.Cmd("get", "int64").Int64()

	if r != -9223372036854775808 {
		t.Logf("expect int64 [-9223372036854775808], but get[ %d]\n", r)
		t.Fail()
	}

	client.Cmd("set", "int64", "9223372036854775808")
	r = client.Cmd("get", "int64").Int64()

	if r != 9223372036854775807 {
		t.Logf("expect int64 [9223372036854775807], but get[ %d]\n", r)
		t.Fail()
	}
}

func TestUint8(t *testing.T) {
	client.Cmd("set", "uint8", "255")
	r := client.Cmd("get", "int64").Uint8()

	if r != 255 {
		t.Logf("expect uint8 [255], but get[ %d]\n", r)
		t.Fail()
	}

	client.Cmd("set", "uint8", "256")
	r = client.Cmd("get", "uint8").Uint8()

	if r != 255 {
		t.Logf("expect uint8 [255], but get[ %d]\n", r)
		t.Fail()
	}

	client.Cmd("set", "uint8", "-1")
	r = client.Cmd("get", "uint8").Uint8()

	if r != 0 {
		t.Logf("expect uint8 [0], but get[ %d]\n", r)
		t.Fail()
	}
}

func TestUint16(t *testing.T) {
	client.Cmd("set", "uint16", "65535")
	r := client.Cmd("get", "int64").Uint16()

	if r != 65535 {
		t.Logf("expect uint16 [65535], but get[ %d]\n", r)
		t.Fail()
	}

	client.Cmd("set", "uint16", "65536")
	r = client.Cmd("get", "uint16").Uint16()

	if r != 65535 {
		t.Logf("expect uint16 [65535], but get[ %d]\n", r)
		t.Fail()
	}

	client.Cmd("set", "uint16", "-1")
	r = client.Cmd("get", "uint16").Uint16()

	if r != 0 {
		t.Logf("expect uint16 [0], but get[ %d]\n", r)
		t.Fail()
	}
}
<|endoftext|>


<commit_before>package virtualboxclient

import (
	"github.com/appropriate/go-virtualboxclient/vboxwebsrv"
)

type Machine struct {
	virtualbox      *VirtualBox
	managedObjectId string
}

func (m *Machine) GetChipsetType() (*vboxwebsrv.ChipsetType, error) {
	request := vboxwebsrv.IMachinegetChipsetType{This: m.managedObjectId}

	response, err := m.virtualbox.IMachinegetChipsetType(&request)
	if err != nil {
		return nil, err // TODO: Wrap the error
	}

	return response.Returnval, nil
}

func (m *Machine) GetNetworkAdapter(slot uint32) (*NetworkAdapter, error) {
	request := vboxwebsrv.IMachinegetNetworkAdapter{This: m.managedObjectId, Slot: slot}

	response, err := m.virtualbox.IMachinegetNetworkAdapter(&request)
	if err != nil {
		return nil, err // TODO: Wrap the error
	}

	return &NetworkAdapter{m.virtualbox, response.Returnval}, nil
}

func (m *Machine) GetSettingsFilePath() (string, error) {
	request := vboxwebsrv.IMachinegetSettingsFilePath{This: m.managedObjectId}

	response, err := m.virtualbox.IMachinegetSettingsFilePath(&request)
	if err != nil {
		return "", err // TODO: Wrap the error
	}

	return response.Returnval, nil
}

func (m *Machine) GetStorageControllers() ([]*StorageController, error) {
	request := vboxwebsrv.IMachinegetStorageControllers{This: m.managedObjectId}

	response, err := m.virtualbox.IMachinegetStorageControllers(&request)
	if err != nil {
		return nil, err // TODO: Wrap the error
	}

	storageControllers := make([]*StorageController, len(response.Returnval))
	for i, oid := range response.Returnval {
		storageControllers[i] = &StorageController{m.virtualbox, oid}
	}

	return storageControllers, nil
}
<commit_msg>Add GetMediumAttachments()<commit_after>package virtualboxclient

import (
	"github.com/appropriate/go-virtualboxclient/vboxwebsrv"
)

type Machine struct {
	virtualbox      *VirtualBox
	managedObjectId string
}

func (m *Machine) GetChipsetType() (*vboxwebsrv.ChipsetType, error) {
	request := vboxwebsrv.IMachinegetChipsetType{This: m.managedObjectId}

	response, err := m.virtualbox.IMachinegetChipsetType(&request)
	if err != nil {
		return nil, err // TODO: Wrap the error
	}

	return response.Returnval, nil
}

func (m *Machine) GetMediumAttachments() ([]*vboxwebsrv.IMediumAttachment, error) {
	request := vboxwebsrv.IMachinegetMediumAttachments{This: m.managedObjectId}

	response, err := m.virtualbox.IMachinegetMediumAttachments(&request)
	if err != nil {
		return nil, err // TODO: Wrap the error
	}

	return response.Returnval, nil
}

func (m *Machine) GetNetworkAdapter(slot uint32) (*NetworkAdapter, error) {
	request := vboxwebsrv.IMachinegetNetworkAdapter{This: m.managedObjectId, Slot: slot}

	response, err := m.virtualbox.IMachinegetNetworkAdapter(&request)
	if err != nil {
		return nil, err // TODO: Wrap the error
	}

	return &NetworkAdapter{m.virtualbox, response.Returnval}, nil
}

func (m *Machine) GetSettingsFilePath() (string, error) {
	request := vboxwebsrv.IMachinegetSettingsFilePath{This: m.managedObjectId}

	response, err := m.virtualbox.IMachinegetSettingsFilePath(&request)
	if err != nil {
		return "", err // TODO: Wrap the error
	}

	return response.Returnval, nil
}

func (m *Machine) GetStorageControllers() ([]*StorageController, error) {
	request := vboxwebsrv.IMachinegetStorageControllers{This: m.managedObjectId}

	response, err := m.virtualbox.IMachinegetStorageControllers(&request)
	if err != nil {
		return nil, err // TODO: Wrap the error
	}

	storageControllers := make([]*StorageController, len(response.Returnval))
	for i, oid := range response.Returnval {
		storageControllers[i] = &StorageController{m.virtualbox, oid}
	}

	return storageControllers, nil
}
<|endoftext|>


<commit_before>package discovery

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

func TestVersionListing(t *testing.T) {
	handler := http.NewServeMux()
	handler.HandleFunc("/terraform-providers/terraform-provider-test/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(versionList))
	})

	server := httptest.NewServer(handler)
	defer server.Close()

	providersURL.releases = server.URL + "/"

	versions, err := listProviderVersions("test")
	if err != nil {
		t.Fatal(err)
	}

	expectedSet := map[string]bool{
		"1.2.4": true,
		"1.2.3": true,
		"1.2.1": true,
	}

	for _, v := range versions {
		if !expectedSet[v.String()] {
			t.Fatalf("didn't get version %s in listing", v)
		}
		delete(expectedSet, v.String())
	}
}

const versionList = `<!DOCTYPE html>

    ../

    terraform-provider-test_1.2.3

    terraform-provider-test_1.2.1

    terraform-provider-test_1.2.4

    Proudly fronted by Fastly

`
<commit_msg>add test for newestVersion<commit_after>package discovery

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

func TestVersionListing(t *testing.T) {
	handler := http.NewServeMux()
	handler.HandleFunc("/terraform-providers/terraform-provider-test/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(versionList))
	})

	server := httptest.NewServer(handler)
	defer server.Close()

	providersURL.releases = server.URL + "/"

	versions, err := listProviderVersions("test")
	if err != nil {
		t.Fatal(err)
	}

	expectedSet := map[string]bool{
		"1.2.4": true,
		"1.2.3": true,
		"1.2.1": true,
	}

	for _, v := range versions {
		if !expectedSet[v.String()] {
			t.Fatalf("didn't get version %s in listing", v)
		}
		delete(expectedSet, v.String())
	}
}

func TestNewestVersion(t *testing.T) {
	var available []Version
	for _, v := range []string{"1.2.3", "1.2.1", "1.2.4"} {
		version, err := VersionStr(v).Parse()
		if err != nil {
			t.Fatal(err)
		}
		available = append(available, version)
	}

	reqd, err := ConstraintStr(">1.2.1").Parse()
	if err != nil {
		t.Fatal(err)
	}

	found, err := newestVersion(available, reqd)
	if err != nil {
		t.Fatal(err)
	}

	if found.String() != "1.2.4" {
		t.Fatalf("expected newest version 1.2.4, got: %s", found)
	}

	reqd, err = ConstraintStr("> 1.2.4").Parse()
	if err != nil {
		t.Fatal(err)
	}

	found, err = newestVersion(available, reqd)
	if err == nil {
		t.Fatalf("expceted error, got version %s", found)
	}
}

const versionList = `<!DOCTYPE html>

    ../

    terraform-provider-test_1.2.3

    terraform-provider-test_1.2.1

    terraform-provider-test_1.2.4

    Proudly fronted by Fastly

`
<|endoftext|>


<commit_before>package dbr

type union struct {
	builder []Builder
	all     bool
}

func Union(builder ...Builder) interface {
	Builder
	As(string) Builder
} {
	return &union{
		builder: builder,
	}
}

func UnionAll(builder ...Builder) interface {
	Builder
	As(string) Builder
} {
	return &union{
		builder: builder,
		all:     true,
	}
}

func (u *union) Build(d Dialect, buf Buffer) error {
	for i, b := range u.builder {
		if i > 0 {
			buf.WriteString(" UNION ")
			if u.all {
				buf.WriteString("ALL ")
			}
		}
		buf.WriteString(placeholder)
		buf.WriteValue(b)
	}
	return nil
}

func (u *union) As(alias string) Builder {
	return as(u, alias)
}
<commit_msg>Make union useable<commit_after>package dbr

type union struct {
	builder []SelectBuilder
	all     bool
}

func Union(builder ...SelectBuilder) interface {
	Builder
	As(string) Builder
} {
	return &union{
		builder: builder,
	}
}

func UnionAll(builder ...SelectBuilder) interface {
	Builder
	As(string) Builder
} {
	return &union{
		builder: builder,
		all:     true,
	}
}

func (u *union) Build(d Dialect, buf Buffer) error {
	for i, b := range u.builder {
		if i > 0 {
			buf.WriteString(" UNION ")
			if u.all {
				buf.WriteString("ALL ")
			}
		}
		buf.WriteString(placeholder)
		buf.WriteValue(b)
	}
	return nil
}

func (u *union) As(alias string) Builder {
	return as(u, alias)
}
<|endoftext|>