<commit_before>package ambex

import (
	"context"
	"os"
	"strconv"
	"time"
)

// An Update encapsulates everything needed to perform an update (of envoy configuration). The
// version string is for logging purposes, the Updator func does the actual work of updating.
type Update struct {
	Version string
	Update  func() error
}

// Function type for fetching memory usage as a percentage.
type MemoryGetter func() int

// The Updator function will run forever (or until the ctx is canceled) and look for updates on the
// incoming channel. If memory usage is constrained as reported by the getUsage function, updates
// will be rate limited to guarantee that there are only so many stale configs in memory at a
// time. The function assumes updates are cumulative and it will drop old queued updates if a new
// update arrives.
func Updater(ctx context.Context, updates <-chan Update, getUsage MemoryGetter) error {
	drainTime := GetAmbassadorDrainTime()
	ticker := time.NewTicker(drainTime)
	defer ticker.Stop()
	return updaterWithTicker(ctx, updates, getUsage, drainTime, ticker, time.Now)
}

func updaterWithTicker(ctx context.Context, updates <-chan Update, getUsage MemoryGetter,
	drainTime time.Duration, ticker *time.Ticker, clock func() time.Time) error {
	// Holds the times of any updates between now - drain-time and now.
	updateTimes := []time.Time{}
	var latest Update
	gotFirst := false
	pushed := false
	for {
		var now time.Time
		tick := false
		select {
		case up := <-updates:
			latest = up
			pushed = false
			gotFirst = true
			now = clock()
		case now = <-ticker.C:
			if pushed {
				continue
			}
			tick = true
		case <-ctx.Done():
			return nil
		}

		// Remove updates that were longer than drain-time ago
		updateTimes = gcUpdateTimes(updateTimes, now, drainTime)

		usagePercent := getUsage()
		var maxStaleReconfigs int
		switch {
		case usagePercent >= 90:
			// With the default 10 minute drain time this works out to an average of one reconfig
			// every 10 minutes. This will guarantee the minimum possible memory usage due to stale
			// configs.
			maxStaleReconfigs = 1
		case usagePercent >= 80:
			// With the default 10 minute drain time this works out to one reconfigs every 10
			// seconds on average within the window. (They could all happen in one burst.)
			maxStaleReconfigs = 15
		case usagePercent >= 70:
			// With the default 10 minute drain time this works out to one reconfigs every 10
			// seconds on average within the window. (They could all happen in one burst.)
			maxStaleReconfigs = 30
		case usagePercent >= 60:
			// With the default 10 minute drain time this works out to one reconfigs every 10
			// seconds on average within the window. (They could all happen in one burst.)
			maxStaleReconfigs = 60
		case usagePercent >= 50:
			// With the default 10 minute drain time this works out to one reconfig every 5 seconds
			// on average within the window. (They could all happen in one burst.)
			maxStaleReconfigs = 120
		default:
			// Zero means no limit. This is what we want by default when memory usage is in the 0 to
			// 50 range.
			maxStaleReconfigs = 0
		}

		staleReconfigs := len(updateTimes)

		if maxStaleReconfigs > 0 && staleReconfigs >= maxStaleReconfigs {
			if !tick {
				log.Warnf("Memory Usage: throttling reconfig %+v due to constrained memory with %d stale reconfigs (%d max)",
					latest.Version, staleReconfigs, maxStaleReconfigs)
			}
			continue
		}

		if !gotFirst {
			continue
		}

		err := latest.Update()
		if err != nil {
			return err
		}
		updateTimes = append(updateTimes, now)
		log.Infof("Pushing snapshot %+v", latest.Version)
		pushed = true
	}
}

// The gcUpdateTimes function filters out timestamps that should have drained by now.
func gcUpdateTimes(updateTimes []time.Time, now time.Time, drainTime time.Duration) []time.Time {
	result := []time.Time{}
	for _, ut := range updateTimes {
		if ut.Add(drainTime).After(now) {
			result = append(result, ut)
		}
	}
	return result
}

// The GetAmbassadorDrainTime function retuns the AMBASSADOR_DRAIN_TIME env var as a time.Duration
func GetAmbassadorDrainTime() time.Duration {
	s := os.Getenv("AMBASSADOR_DRAIN_TIME")
	if s == "" {
		s = "600"
	}
	i, err := strconv.Atoi(s)
	if err != nil {
		log.Printf("Error parsing AMBASSADOR_DRAIN_TIME: %v", err)
		i = 600
	}

	return time.Duration(i) * time.Second
}
<commit_msg>(from AES) fixed some typos<commit_after>package ambex

import (
	"context"
	"os"
	"strconv"
	"time"
)

// An Update encapsulates everything needed to perform an update (of envoy configuration). The
// version string is for logging purposes, the Updator func does the actual work of updating.
type Update struct {
	Version string
	Update  func() error
}

// Function type for fetching memory usage as a percentage.
type MemoryGetter func() int

// The Updator function will run forever (or until the ctx is canceled) and look for updates on the
// incoming channel. If memory usage is constrained as reported by the getUsage function, updates
// will be rate limited to guarantee that there are only so many stale configs in memory at a
// time. The function assumes updates are cumulative and it will drop old queued updates if a new
// update arrives.
func Updater(ctx context.Context, updates <-chan Update, getUsage MemoryGetter) error {
	drainTime := GetAmbassadorDrainTime()
	ticker := time.NewTicker(drainTime)
	defer ticker.Stop()
	return updaterWithTicker(ctx, updates, getUsage, drainTime, ticker, time.Now)
}

func updaterWithTicker(ctx context.Context, updates <-chan Update, getUsage MemoryGetter,
	drainTime time.Duration, ticker *time.Ticker, clock func() time.Time) error {
	// Holds the times of any updates between now - drain-time and now.
	updateTimes := []time.Time{}
	var latest Update
	gotFirst := false
	pushed := false
	for {
		var now time.Time
		tick := false
		select {
		case up := <-updates:
			latest = up
			pushed = false
			gotFirst = true
			now = clock()
		case now = <-ticker.C:
			if pushed {
				continue
			}
			tick = true
		case <-ctx.Done():
			return nil
		}

		// Remove updates that were longer than drain-time ago
		updateTimes = gcUpdateTimes(updateTimes, now, drainTime)

		usagePercent := getUsage()
		var maxStaleReconfigs int
		switch {
		case usagePercent >= 90:
			// With the default 10 minute drain time this works out to an average of one reconfig
			// every 10 minutes. This will guarantee the minimum possible memory usage due to stale
			// configs.
			maxStaleReconfigs = 1
		case usagePercent >= 80:
			// With the default 10 minute drain time this works out to one reconfigs every 40
			// seconds on average within the window. (They could all happen in one burst.)
			maxStaleReconfigs = 15
		case usagePercent >= 70:
			// With the default 10 minute drain time this works out to one reconfigs every 20
			// seconds on average within the window. (They could all happen in one burst.)
			maxStaleReconfigs = 30
		case usagePercent >= 60:
			// With the default 10 minute drain time this works out to one reconfigs every 10
			// seconds on average within the window. (They could all happen in one burst.)
			maxStaleReconfigs = 60
		case usagePercent >= 50:
			// With the default 10 minute drain time this works out to one reconfig every 5 seconds
			// on average within the window. (They could all happen in one burst.)
			maxStaleReconfigs = 120
		default:
			// Zero means no limit. This is what we want by default when memory usage is in the 0 to
			// 50 range.
			maxStaleReconfigs = 0
		}

		staleReconfigs := len(updateTimes)

		if maxStaleReconfigs > 0 && staleReconfigs >= maxStaleReconfigs {
			if !tick {
				log.Warnf("Memory Usage: throttling reconfig %+v due to constrained memory with %d stale reconfigs (%d max)",
					latest.Version, staleReconfigs, maxStaleReconfigs)
			}
			continue
		}

		if !gotFirst {
			continue
		}

		err := latest.Update()
		if err != nil {
			return err
		}
		updateTimes = append(updateTimes, now)
		log.Infof("Pushing snapshot %+v", latest.Version)
		pushed = true
	}
}

// The gcUpdateTimes function filters out timestamps that should have drained by now.
func gcUpdateTimes(updateTimes []time.Time, now time.Time, drainTime time.Duration) []time.Time {
	result := []time.Time{}
	for _, ut := range updateTimes {
		if ut.Add(drainTime).After(now) {
			result = append(result, ut)
		}
	}
	return result
}

// The GetAmbassadorDrainTime function retuns the AMBASSADOR_DRAIN_TIME env var as a time.Duration
func GetAmbassadorDrainTime() time.Duration {
	s := os.Getenv("AMBASSADOR_DRAIN_TIME")
	if s == "" {
		s = "600"
	}
	i, err := strconv.Atoi(s)
	if err != nil {
		log.Printf("Error parsing AMBASSADOR_DRAIN_TIME: %v", err)
		i = 600
	}

	return time.Duration(i) * time.Second
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/stevvooe/continuity/commands"
)

func main() {
	commands.MainCmd.Execute()
}
<commit_msg>Import sha256 to avoid panic from digest package<commit_after>package main

import (
	_ "crypto/sha256"

	"github.com/stevvooe/continuity/commands"
)

func main() {
	commands.MainCmd.Execute()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/pierrre/geohash"
	"strconv"
	"strings"
)

func main() {
	var precision int
	flag.IntVar(&precision, "precision", 0, "Precision")
	flag.Parse()

	if flag.NArg() == 0 {
		flag.Usage()
	}

	results, err := processArgs(flag.Args(), precision)
	if err != nil {
		panic(err)
	}

	fmt.Println(strings.Join(results, " "))
}

func processArgs(args []string, precision int) ([]string, error) {
	var results []string
	for _, arg := range flag.Args() {
		result, err := processArg(arg, precision)
		if err != nil {
			return nil, err
		}
		results = append(results, result)
	}
	return results, nil
}

func processArg(arg string, precision int) (string, error) {
	if strings.Contains(arg, ",") {
		return processArgLatLon(arg, precision)
	} else {
		return processArgGeohash(arg)
	}
}

func processArgLatLon(arg string, precision int) (string, error) {
	latLon := strings.Split(arg, ",")
	if len(latLon) != 2 {
		return "", fmt.Errorf("'%s'' is not a valid location (lat,lon)", arg)
	}

	lat, err := strconv.ParseFloat(latLon[0], 64)
	if err != nil {
		return "", err
	}

	lon, err := strconv.ParseFloat(latLon[1], 64)
	if err != nil {
		return "", err
	}

	var gh string
	if precision > 0 {
		gh = geohash.Encode(lat, lon, precision)
	} else {
		gh = geohash.EncodeAuto(lat, lon)
	}
	return gh, nil
}

func processArgGeohash(arg string) (string, error) {
	box, err := geohash.Decode(arg)
	if err != nil {
		return "", err
	}

	round := box.Round()

	return fmt.Sprintf("%v,%v", round.Lat, round.Lon), nil
}
<commit_msg>golint!<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/pierrre/geohash"
	"strconv"
	"strings"
)

func main() {
	var precision int
	flag.IntVar(&precision, "precision", 0, "Precision")
	flag.Parse()

	if flag.NArg() == 0 {
		flag.Usage()
	}

	results, err := processArgs(flag.Args(), precision)
	if err != nil {
		panic(err)
	}

	fmt.Println(strings.Join(results, " "))
}

func processArgs(args []string, precision int) ([]string, error) {
	var results []string
	for _, arg := range flag.Args() {
		result, err := processArg(arg, precision)
		if err != nil {
			return nil, err
		}
		results = append(results, result)
	}
	return results, nil
}

func processArg(arg string, precision int) (string, error) {
	if strings.Contains(arg, ",") {
		return processArgLatLon(arg, precision)
	}
	return processArgGeohash(arg)
}

func processArgLatLon(arg string, precision int) (string, error) {
	latLon := strings.Split(arg, ",")
	if len(latLon) != 2 {
		return "", fmt.Errorf("'%s'' is not a valid location (lat,lon)", arg)
	}

	lat, err := strconv.ParseFloat(latLon[0], 64)
	if err != nil {
		return "", err
	}

	lon, err := strconv.ParseFloat(latLon[1], 64)
	if err != nil {
		return "", err
	}

	var gh string
	if precision > 0 {
		gh = geohash.Encode(lat, lon, precision)
	} else {
		gh = geohash.EncodeAuto(lat, lon)
	}
	return gh, nil
}

func processArgGeohash(arg string) (string, error) {
	box, err := geohash.Decode(arg)
	if err != nil {
		return "", err
	}

	round := box.Round()

	return fmt.Sprintf("%v,%v", round.Lat, round.Lon), nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/gobs/args"
	"github.com/gobs/cmd"
	"github.com/gobs/httpclient"
	"github.com/gobs/jsonpath"
	"github.com/gobs/simplejson"

	"fmt"
	"net/url"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var (
	completion_words = []string{}
	env              = map[string]string{}

	reFieldValue = regexp.MustCompile(`(\w[\d\w-]*)(=(.*))?`) // field-name=value
)

func CompletionFunction(text, line string) (matches []string) {
	// for the "ls" command we let readline show real file names
	if strings.HasPrefix(line, "ls ") {
		return
	}

	// for all other commands, we pick from our list of completion words
	for _, w := range completion_words {
		if strings.HasPrefix(w, text) {
			matches = append(matches, w)
		}
	}

	return
}

func request(client *httpclient.HttpClient, method, params string, print bool) *httpclient.HttpResponse {
	env["error"] = ""
	env["body"] = ""

	options := []httpclient.RequestOption{client.Method(method)}
	args := args.ParseArgs(params)

	if len(args.Arguments) > 0 {
		options = append(options, client.Path(args.Arguments[0]))
	}

	if len(args.Arguments) > 1 {
		data := strings.Join(args.Arguments[1:], " ")
		options = append(options, client.Body(strings.NewReader(data)))
	}

	if len(args.Options) > 0 {
		options = append(options, client.StringParams(args.Options))
	}

	res, err := client.SendRequest(options...)
	if err == nil {
		err = res.ResponseError()
	}
	if err != nil {
		fmt.Println("ERROR:", err)
		env["error"] = err.Error()
	}

	body := res.Content()
	if len(body) > 0 && print {
		if strings.Contains(res.Header.Get("Content-Type"), "json") {
			jbody, err := simplejson.LoadBytes(body)
			if err != nil {
				fmt.Println(err)
			} else {
				printJson(jbody.Data())
			}
		} else {
			fmt.Println(string(body))
		}
	}

	env["body"] = string(body)
	return res
}

func headerName(s string) string {
	s = strings.ToLower(s)
	parts := strings.Split(s, "-")
	for i, p := range parts {
		if len(p) > 0 {
			parts[i] = strings.ToUpper(p[0:1]) + p[1:]
		}
	}
	return strings.Join(parts, "-")
}

func unquote(s string) string {
	if res, err := strconv.Unquote(strings.TrimSpace(s)); err == nil {
		return res
	}

	return s
}

func printJson(v interface{}) {
	fmt.Println(simplejson.MustDumpString(v, simplejson.Indent("  ")))
}

func parseValue(v string) (interface{}, error) {
	switch {
	case strings.HasPrefix(v, "{") || strings.HasPrefix(v, "["):
		j, err := simplejson.LoadString(v)
		if err != nil {
			return nil, fmt.Errorf("error parsing %q", v)
		} else {
			return j.Data(), nil
		}

	case strings.HasPrefix(v, `"`):
		return strings.Trim(v, `"`), nil

	case strings.HasPrefix(v, `'`):
		return strings.Trim(v, `'`), nil

	case v == "":
		return v, nil

	case v == "true":
		return true, nil

	case v == "false":
		return false, nil

	case v == "null":
		return nil, nil

	default:
		if i, err := strconv.ParseInt(v, 10, 64); err == nil {
			return i, nil
		}
		if f, err := strconv.ParseFloat(v, 64); err == nil {
			return f, nil
		}

		return v, nil
	}
}

func main() {
	var interrupted bool
	var client = httpclient.NewHttpClient("")

	client.UserAgent = "httpclient/0.1"

	commander := &cmd.Cmd{
		HistoryFile: ".httpclient_history",
		Complete:    CompletionFunction,
		EnableShell: true,
		Interrupt:   func(sig os.Signal) bool { interrupted = true; return false },
	}

	commander.Init()

	commander.Vars = env
	commander.SetVar("print", true, false)

	commander.Add(cmd.Command{
		"base",
		`base [url]`,
		func(line string) (stop bool) {
			if line != "" {
				val, err := url.Parse(line)
				if err != nil {
					fmt.Println(err)
					return
				}

				client.BaseURL = val
				commander.SetPrompt(fmt.Sprintf("%v> ", client.BaseURL), 40)
				if !commander.GetBoolVar("print", true) {
					return
				}
			}

			fmt.Println("base", client.BaseURL)
			return
		},
		nil})

	commander.Add(cmd.Command{
		"insecure",
		`insecure [true|false]`,
		func(line string) (stop bool) {
			if line != "" {
				val, err := strconv.ParseBool(line)
				if err != nil {
					fmt.Println(err)
					return
				}

				client.AllowInsecure(val)
			}

			// assume if there is a transport, it's because we set AllowInsecure
			fmt.Println("insecure", client.GetTransport() != nil)

			return
		},
		nil})

	commander.Add(cmd.Command{
		"timeout",
		`timeout [duration]`,
		func(line string) (stop bool) {
			if line != "" {
				val, err := time.ParseDuration(line)
				if err != nil {
					fmt.Println(err)
					return
				}

				client.SetTimeout(val)
			}

			fmt.Println("timeout", client.GetTimeout())
			return
		},
		nil})

	commander.Add(cmd.Command{
		"verbose",
		`verbose [true|false]`,
		func(line string) (stop bool) {
			if line != "" {
				val, err := strconv.ParseBool(line)
				if err != nil {
					fmt.Println(err)
					return
				}

				client.Verbose = val
			}

			fmt.Println("Verbose", client.Verbose)
			return
		},
		nil})

	commander.Add(cmd.Command{
		"agent",
		`agent user-agent-string`,
		func(line string) (stop bool) {
			if line != "" {
				client.UserAgent = line
			}

			fmt.Println("User-Agent:", client.UserAgent)
			return
		},
		nil})

	commander.Add(cmd.Command{
		"header",
		`header [name [value]]`,
		func(line string) (stop bool) {
			if line == "" {
				if len(client.Headers) == 0 {
					fmt.Println("No headers")
				} else {
					fmt.Println("Headers:")
					for k, v := range client.Headers {
						fmt.Printf("  %v: %v\n", k, v)
					}
				}

				return
			}

			parts := args.GetArgsN(line, 2)
			name := headerName(parts[0])

			if len(parts) == 2 {
				client.Headers[name] = unquote(parts[1])
				if !commander.GetBoolVar("print", true) {
					return
				}
			}

			fmt.Printf("%v: %v\n", name, client.Headers[name])
			return
		},
		nil})

	commander.Add(cmd.Command{"head",
		`
                head [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			res := request(client, "head", line, false)
			if res != nil {
				printJson(res.Header)
			}
			return
		},
		nil})

	commander.Add(cmd.Command{"get",
		`
                get [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			request(client, "get", line, commander.GetBoolVar("print", true))
			return
		},
		nil})

	commander.Add(cmd.Command{"post",
		`
                post [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			request(client, "post", line, commander.GetBoolVar("print", true))
			return
		},
		nil})

	commander.Add(cmd.Command{"put",
		`
                put [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			request(client, "put", line, commander.GetBoolVar("print", true))
			return
		},
		nil})

	commander.Add(cmd.Command{"delete",
		`
                delete [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			request(client, "delete", line, commander.GetBoolVar("print", true))
			return
		},
		nil})

	commander.Add(cmd.Command{"json",
		`
                json field1=value1 field2=value2...       // json object
                json {"name1":"value1", "name2":"value2"}
                json [value1 value2...]                   // json array
                json [value1, value2...]
                `,
		func(line string) (stop bool) {
			var res interface{}

			if strings.HasPrefix(line, "{") { // assume is already a JSON object

				if jbody, err := simplejson.LoadString(line); err != nil {
					err = fmt.Errorf("error parsing object %q", line)
					env["error"] = err.Error()
					fmt.Println(err)
					return
				} else {
					res = jbody.Data()
				}
			} else if strings.HasPrefix(line, "[") { // could be a JSON array

				if jbody, err := simplejson.LoadString(line); err == nil {
					res = jbody.Data()
				} else { // try a sequence of values (that need to be parsed)
					line = strings.TrimPrefix(line, "[")
					line = strings.TrimSuffix(line, "]")
					line = strings.TrimSpace(line)

					var ares []interface{}

					for _, f := range args.GetArgs(line) {
						v, err := parseValue(f)
						if err != nil {
							fmt.Println(err)
							env["error"] = err.Error()
							return
						}

						ares = append(ares, v)
					}

					res = ares
				}
			} else { // a sequence of name=value pairs
				var err error
				mres := map[string]interface{}{}

				for _, f := range args.GetArgs(line, args.InfieldBrackets()) {
					matches := reFieldValue.FindStringSubmatch(f)
					if len(matches) > 0 { // [field=value field =value value]
						name, value := matches[1], matches[3]
						mres[name], err = parseValue(value)

						if err != nil {
							fmt.Println(err)
							env["error"] = err.Error()
							return
						}
					} else {
						fmt.Println("invalid name=value pair:", f)
						env["error"] = "invalid name=value pair"
						return
					}
				}

				res = mres
			}

			if commander.GetBoolVar("print", true) {
				printJson(res)
			}

			env["error"] = ""
			env["json"] = unquote(simplejson.MustDumpString(res))
			return
		},
		nil})

	commander.Add(cmd.Command{
		"jsonpath",
		`jsonpath [-e] [-c] path {json}`,
		func(line string) (stop bool) {
			var joptions jsonpath.ProcessOptions

			options, line := args.GetOptions(line)
			for _, o := range options {
				if o == "-e" || o == "--enhanced" {
					joptions |= jsonpath.Enhanced
				} else if o == "-c" || o == "--collapse" {
					joptions |= jsonpath.Collapse
				} else {
					line = "" // to force an error
					break
				}
			}

			parts := args.GetArgsN(line, 2)
			if len(parts) != 2 {
				fmt.Println("use: jsonpath [-e|--enhanced] path {json}")
				env["error"] = "invalid-usage"
				return
			}

			path := parts[0]
			if !strings.HasPrefix(path, "$.") {
				path = "$." + path
			}

			jbody, err := simplejson.LoadString(parts[1])
			if err != nil {
				fmt.Println("json:", err)
				env["error"] = err.Error()
				return
			}

			jp := jsonpath.NewProcessor()
			if !jp.Parse(path) {
				env["error"] = fmt.Sprintf("failed to parse %q", path)
				return // syntax error
			}

			res := jp.Process(jbody, joptions)
			if commander.GetBoolVar("print", true) {
				printJson(res)
			}
			env["error"] = ""
			env["json"] = unquote(simplejson.MustDumpString(res))
			return
		},
		nil})

	commander.Add(cmd.Command{
		"format",
		`format object`,
		func(line string) (stop bool) {
			jbody, err := simplejson.LoadString(line)
			if err != nil {
				fmt.Println("json:", err)
				env["error"] = err.Error()
				return
			}

			printJson(jbody.Data())
			return
		},
		nil})
	commander.Add(cmd.Command{
		"exit",
		`exit script`,
		func(line string) (stop bool) {
			fmt.Println("goodbye!")
			return true
		},
		nil})

	commander.Commands["set"] = commander.Commands["var"]

	switch len(os.Args) {
	case 1: // program name only
		break

	case 2: // one arg - expect URL or @filename
		cmd := os.Args[1]
		if !strings.HasPrefix(cmd, "@") {
			cmd = "base " + cmd
		}

		commander.OneCmd(cmd)

	default:
		fmt.Println("usage:", os.Args[0], "[base-url]")
		return
	}

	commander.CmdLoop()
}
<commit_msg>update to new cmd with jsonpath plugin<commit_after>package main

import (
	"github.com/gobs/args"
	"github.com/gobs/cmd"
	"github.com/gobs/cmd/plugins/controlflow"
	"github.com/gobs/cmd/plugins/json"
	"github.com/gobs/httpclient"
	"github.com/gobs/simplejson"

	"fmt"
	"net/url"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var (
	reFieldValue = regexp.MustCompile(`(\w[\d\w-]*)(=(.*))?`) // field-name=value
)

func request(cmd *cmd.Cmd, client *httpclient.HttpClient, method, params string, print bool) *httpclient.HttpResponse {
	cmd.SetVar("error", "", true)
	cmd.SetVar("body", "", true)

	options := []httpclient.RequestOption{client.Method(method)}
	args := args.ParseArgs(params)

	if len(args.Arguments) > 0 {
		options = append(options, client.Path(args.Arguments[0]))
	}

	if len(args.Arguments) > 1 {
		data := strings.Join(args.Arguments[1:], " ")
		options = append(options, client.Body(strings.NewReader(data)))
	}

	if len(args.Options) > 0 {
		options = append(options, client.StringParams(args.Options))
	}

	res, err := client.SendRequest(options...)
	if err == nil {
		err = res.ResponseError()
	}
	if err != nil {
		fmt.Println("ERROR:", err)
		cmd.SetVar("error", err, true)
	}

	body := res.Content()
	if len(body) > 0 && print {
		if strings.Contains(res.Header.Get("Content-Type"), "json") {
			jbody, err := simplejson.LoadBytes(body)
			if err != nil {
				fmt.Println(err)
			} else {
				json.PrintJson(jbody.Data())
			}
		} else {
			fmt.Println(string(body))
		}
	}

	cmd.SetVar("body", string(body), true)
	return res
}

func headerName(s string) string {
	s = strings.ToLower(s)
	parts := strings.Split(s, "-")
	for i, p := range parts {
		if len(p) > 0 {
			parts[i] = strings.ToUpper(p[0:1]) + p[1:]
		}
	}
	return strings.Join(parts, "-")
}

func unquote(s string) string {
	if res, err := strconv.Unquote(strings.TrimSpace(s)); err == nil {
		return res
	}

	return s
}

func parseValue(v string) (interface{}, error) {
	switch {
	case strings.HasPrefix(v, "{") || strings.HasPrefix(v, "["):
		j, err := simplejson.LoadString(v)
		if err != nil {
			return nil, fmt.Errorf("error parsing %q", v)
		} else {
			return j.Data(), nil
		}

	case strings.HasPrefix(v, `"`):
		return strings.Trim(v, `"`), nil

	case strings.HasPrefix(v, `'`):
		return strings.Trim(v, `'`), nil

	case v == "":
		return v, nil

	case v == "true":
		return true, nil

	case v == "false":
		return false, nil

	case v == "null":
		return nil, nil

	default:
		if i, err := strconv.ParseInt(v, 10, 64); err == nil {
			return i, nil
		}
		if f, err := strconv.ParseFloat(v, 64); err == nil {
			return f, nil
		}

		return v, nil
	}
}

func main() {
	var interrupted bool
	var client = httpclient.NewHttpClient("")

	client.UserAgent = "httpclient/0.1"

	commander := &cmd.Cmd{
		HistoryFile: ".httpclient_history",
		EnableShell: true,
		Interrupt:   func(sig os.Signal) bool { interrupted = true; return false },
	}

	commander.Init(controlflow.Plugin, json.Plugin)

	commander.SetVar("print", true, false)

	commander.Add(cmd.Command{
		"base",
		`base [url]`,
		func(line string) (stop bool) {
			if line != "" {
				val, err := url.Parse(line)
				if err != nil {
					fmt.Println(err)
					return
				}

				client.BaseURL = val
				commander.SetPrompt(fmt.Sprintf("%v> ", client.BaseURL), 40)
				if !commander.GetBoolVar("print") {
					return
				}
			}

			fmt.Println("base", client.BaseURL)
			return
		},
		nil})

	commander.Add(cmd.Command{
		"insecure",
		`insecure [true|false]`,
		func(line string) (stop bool) {
			if line != "" {
				val, err := strconv.ParseBool(line)
				if err != nil {
					fmt.Println(err)
					return
				}

				client.AllowInsecure(val)
			}

			// assume if there is a transport, it's because we set AllowInsecure
			fmt.Println("insecure", client.GetTransport() != nil)

			return
		},
		nil})

	commander.Add(cmd.Command{
		"timeout",
		`timeout [duration]`,
		func(line string) (stop bool) {
			if line != "" {
				val, err := time.ParseDuration(line)
				if err != nil {
					fmt.Println(err)
					return
				}

				client.SetTimeout(val)
			}

			fmt.Println("timeout", client.GetTimeout())
			return
		},
		nil})

	commander.Add(cmd.Command{
		"verbose",
		`verbose [true|false]`,
		func(line string) (stop bool) {
			if line != "" {
				val, err := strconv.ParseBool(line)
				if err != nil {
					fmt.Println(err)
					return
				}

				client.Verbose = val
			}

			fmt.Println("Verbose", client.Verbose)
			return
		},
		nil})

	commander.Add(cmd.Command{
		"timing",
		`timing [true|false]`,
		func(line string) (stop bool) {
			if line != "" {
				val, err := strconv.ParseBool(line)
				if err != nil {
					fmt.Println(err)
					return
				}

				commander.Timing = val
			}

			fmt.Println("Timing", commander.Timing)
			return
		},
		nil})

	commander.Add(cmd.Command{
		"agent",
		`agent user-agent-string`,
		func(line string) (stop bool) {
			if line != "" {
				client.UserAgent = line
			}

			fmt.Println("User-Agent:", client.UserAgent)
			return
		},
		nil})

	commander.Add(cmd.Command{
		"header",
		`header [name [value]]`,
		func(line string) (stop bool) {
			if line == "" {
				if len(client.Headers) == 0 {
					fmt.Println("No headers")
				} else {
					fmt.Println("Headers:")
					for k, v := range client.Headers {
						fmt.Printf("  %v: %v\n", k, v)
					}
				}

				return
			}

			parts := args.GetArgsN(line, 2)
			name := headerName(parts[0])

			if len(parts) == 2 {
				client.Headers[name] = unquote(parts[1])
				if !commander.GetBoolVar("print") {
					return
				}
			}

			fmt.Printf("%v: %v\n", name, client.Headers[name])
			return
		},
		nil})

	commander.Add(cmd.Command{"head",
		`
                head [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			res := request(commander, client, "head", line, false)
			if res != nil {
				json.PrintJson(res.Header)
			}
			return
		},
		nil})

	commander.Add(cmd.Command{"get",
		`
                get [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			request(commander, client, "get", line, commander.GetBoolVar("print"))
			return
		},
		nil})

	commander.Add(cmd.Command{"post",
		`
                post [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			request(commander, client, "post", line, commander.GetBoolVar("print"))
			return
		},
		nil})

	commander.Add(cmd.Command{"put",
		`
                put [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			request(commander, client, "put", line, commander.GetBoolVar("print"))
			return
		},
		nil})

	commander.Add(cmd.Command{"delete",
		`
                delete [url-path] [short-data]
                `,
		func(line string) (stop bool) {
			request(commander, client, "delete", line, commander.GetBoolVar("print"))
			return
		},
		nil})

	commander.Commands["set"] = commander.Commands["var"]

	switch len(os.Args) {
	case 1: // program name only
		break

	case 2: // one arg - expect URL or @filename
		cmd := os.Args[1]
		if !strings.HasPrefix(cmd, "@") {
			cmd = "base " + cmd
		}

		commander.OneCmd(cmd)

	default:
		fmt.Println("usage:", os.Args[0], "[base-url]")
		return
	}

	commander.CmdLoop()
}
<|endoftext|>


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

package main

import (
	"fmt"
	"strings"

	"launchpad.net/gnuflag"

	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/cmd/envcmd"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/environs/manual"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/juju"
	"launchpad.net/juju-core/names"
	"launchpad.net/juju-core/state/api/params"
)

// sshHostPrefix is the prefix for a machine to be "manually provisioned".
const sshHostPrefix = "ssh:"

var addMachineDoc = `

If no container is specified, a new machine will be
provisioned.  If a container is specified, a new machine will be provisioned
with that container.

To add a container to an existing machine, use the <container>:<machinenumber>
format.

When adding a new machine, you may specify constraints for the machine to be
provisioned.  Constraints cannot be combined with deploying a container to an
existing machine.

Currently, the only supported container type is lxc.

Machines are created in a clean state and ready to have units deployed.

This command also supports manual provisioning of existing machines via SSH. The
target machine must be able to communicate with the API server, and be able to
access the environment storage.

Examples:
   juju add-machine                      (starts a new machine)
   juju add-machine lxc                  (starts a new machine with an lxc container)
   juju add-machine lxc:4                (starts a new lxc container on machine 4)
   juju add-machine --constraints mem=8G (starts a machine with at least 8GB RAM)
   juju add-machine ssh:user@10.10.0.3   (manually provisions a machine with ssh)

See Also:
   juju help constraints
`

// AddMachineCommand starts a new machine and registers it in the environment.
type AddMachineCommand struct {
	envcmd.EnvCommandBase
	// If specified, use this series, else use the environment default-series
	Series string
	// If specified, these constraints are merged with those already in the environment.
	Constraints   constraints.Value
	MachineId     string
	ContainerType instance.ContainerType
	SSHHost       string
}

func (c *AddMachineCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "add-machine",
		Args:    "[<container>:machine | <container> | ssh:[user@]host]",
		Purpose: "start a new, empty machine and optionally a container, or add a container to a machine",
		Doc:     addMachineDoc,
	}
}

func (c *AddMachineCommand) SetFlags(f *gnuflag.FlagSet) {
	c.EnvCommandBase.SetFlags(f)
	f.StringVar(&c.Series, "series", "", "the charm series")
	f.Var(constraints.ConstraintsValue{Target: &c.Constraints}, "constraints", "additional machine constraints")
}

func (c *AddMachineCommand) Init(args []string) error {
	err := c.EnvCommandBase.Init()
	if err != nil {
		return err
	}
	if c.Constraints.Container != nil {
		return fmt.Errorf("container constraint %q not allowed when adding a machine", *c.Constraints.Container)
	}
	containerSpec, err := cmd.ZeroOrOneArgs(args)
	if err != nil {
		return err
	}
	if containerSpec == "" {
		return nil
	}
	if strings.HasPrefix(containerSpec, sshHostPrefix) {
		c.SSHHost = containerSpec[len(sshHostPrefix):]
	} else {
		// container arg can either be 'type:machine' or 'type'
		if c.ContainerType, err = instance.ParseContainerType(containerSpec); err != nil {
			if names.IsMachine(containerSpec) || !cmd.IsMachineOrNewContainer(containerSpec) {
				return fmt.Errorf("malformed container argument %q", containerSpec)
			}
			sep := strings.Index(containerSpec, ":")
			c.MachineId = containerSpec[sep+1:]
			c.ContainerType, err = instance.ParseContainerType(containerSpec[:sep])
		}
	}
	return err
}

func (c *AddMachineCommand) Run(ctx *cmd.Context) error {
	if c.SSHHost != "" {
		args := manual.ProvisionMachineArgs{
			Host:    c.SSHHost,
			EnvName: c.EnvName,
			Stdin:   ctx.Stdin,
			Stdout:  ctx.Stdout,
			Stderr:  ctx.Stderr,
		}
		_, err := manual.ProvisionMachine(args)
		return err
	}

	client, err := juju.NewAPIClientFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer client.Close()

	machineParams := params.AddMachineParams{
		ParentId:      c.MachineId,
		ContainerType: c.ContainerType,
		Series:        c.Series,
		Constraints:   c.Constraints,
		Jobs:          []params.MachineJob{params.JobHostUnits},
	}
	results, err := client.AddMachines([]params.AddMachineParams{machineParams})
	if err != nil {
		return err
	}
	// Currently, only one machine is added, but in future there may be several added in one call.
	machineInfo := results[0]
	if machineInfo.Error != nil {
		return machineInfo.Error
	}
	machineId := machineInfo.Machine
	if c.ContainerType == "" {
		logger.Infof("created machine %v", machineId)
	} else {
		logger.Infof("created %q container on machine %v", c.ContainerType, machineId)
	}
	return nil
}
<commit_msg>remove an unused import after the merge<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"fmt"
	"strings"

	"launchpad.net/gnuflag"

	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/cmd/envcmd"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/environs/manual"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/juju"
	"launchpad.net/juju-core/names"
	"launchpad.net/juju-core/state/api/params"
)

// sshHostPrefix is the prefix for a machine to be "manually provisioned".
const sshHostPrefix = "ssh:"

var addMachineDoc = `

If no container is specified, a new machine will be
provisioned.  If a container is specified, a new machine will be provisioned
with that container.

To add a container to an existing machine, use the <container>:<machinenumber>
format.

When adding a new machine, you may specify constraints for the machine to be
provisioned.  Constraints cannot be combined with deploying a container to an
existing machine.

Currently, the only supported container type is lxc.

Machines are created in a clean state and ready to have units deployed.

This command also supports manual provisioning of existing machines via SSH. The
target machine must be able to communicate with the API server, and be able to
access the environment storage.

Examples:
   juju add-machine                      (starts a new machine)
   juju add-machine lxc                  (starts a new machine with an lxc container)
   juju add-machine lxc:4                (starts a new lxc container on machine 4)
   juju add-machine --constraints mem=8G (starts a machine with at least 8GB RAM)
   juju add-machine ssh:user@10.10.0.3   (manually provisions a machine with ssh)

See Also:
   juju help constraints
`

// AddMachineCommand starts a new machine and registers it in the environment.
type AddMachineCommand struct {
	envcmd.EnvCommandBase
	// If specified, use this series, else use the environment default-series
	Series string
	// If specified, these constraints are merged with those already in the environment.
	Constraints   constraints.Value
	MachineId     string
	ContainerType instance.ContainerType
	SSHHost       string
}

func (c *AddMachineCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "add-machine",
		Args:    "[<container>:machine | <container> | ssh:[user@]host]",
		Purpose: "start a new, empty machine and optionally a container, or add a container to a machine",
		Doc:     addMachineDoc,
	}
}

func (c *AddMachineCommand) SetFlags(f *gnuflag.FlagSet) {
	c.EnvCommandBase.SetFlags(f)
	f.StringVar(&c.Series, "series", "", "the charm series")
	f.Var(constraints.ConstraintsValue{Target: &c.Constraints}, "constraints", "additional machine constraints")
}

func (c *AddMachineCommand) Init(args []string) error {
	err := c.EnvCommandBase.Init()
	if err != nil {
		return err
	}
	if c.Constraints.Container != nil {
		return fmt.Errorf("container constraint %q not allowed when adding a machine", *c.Constraints.Container)
	}
	containerSpec, err := cmd.ZeroOrOneArgs(args)
	if err != nil {
		return err
	}
	if containerSpec == "" {
		return nil
	}
	if strings.HasPrefix(containerSpec, sshHostPrefix) {
		c.SSHHost = containerSpec[len(sshHostPrefix):]
	} else {
		// container arg can either be 'type:machine' or 'type'
		if c.ContainerType, err = instance.ParseContainerType(containerSpec); err != nil {
			if names.IsMachine(containerSpec) || !cmd.IsMachineOrNewContainer(containerSpec) {
				return fmt.Errorf("malformed container argument %q", containerSpec)
			}
			sep := strings.Index(containerSpec, ":")
			c.MachineId = containerSpec[sep+1:]
			c.ContainerType, err = instance.ParseContainerType(containerSpec[:sep])
		}
	}
	return err
}

func (c *AddMachineCommand) Run(ctx *cmd.Context) error {
	if c.SSHHost != "" {
		args := manual.ProvisionMachineArgs{
			Host:    c.SSHHost,
			EnvName: c.EnvName,
			Stdin:   ctx.Stdin,
			Stdout:  ctx.Stdout,
			Stderr:  ctx.Stderr,
		}
		_, err := manual.ProvisionMachine(args)
		return err
	}

	client, err := juju.NewAPIClientFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer client.Close()

	machineParams := params.AddMachineParams{
		ParentId:      c.MachineId,
		ContainerType: c.ContainerType,
		Series:        c.Series,
		Constraints:   c.Constraints,
		Jobs:          []params.MachineJob{params.JobHostUnits},
	}
	results, err := client.AddMachines([]params.AddMachineParams{machineParams})
	if err != nil {
		return err
	}
	// Currently, only one machine is added, but in future there may be several added in one call.
	machineInfo := results[0]
	if machineInfo.Error != nil {
		return machineInfo.Error
	}
	machineId := machineInfo.Machine
	if c.ContainerType == "" {
		logger.Infof("created machine %v", machineId)
	} else {
		logger.Infof("created %q container on machine %v", c.ContainerType, machineId)
	}
	return nil
}
<|endoftext|>


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

package space

import (
	"encoding/json"
	"fmt"
	"net"
	"strings"

	"launchpad.net/gnuflag"

	"github.com/juju/cmd"
	"github.com/juju/errors"
	"github.com/juju/juju/apiserver/params"
)

// ListCommand displays a list of all spaces known to Juju.
type ListCommand struct {
	SpaceCommandBase
	Short bool
	out   cmd.Output
}

const listCommandDoc = `
Displays all defined spaces. If --short is not given both spaces and
their subnets are displayed, otherwise just a list of spaces. The
--format argument has the same semantics as in other CLI commands -
"yaml" is the default. The --output argument always exists when
--format is supported, and allows to redirect the command output to a
file. `

// Info is defined on the cmd.Command interface.
func (c *ListCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "list",
		Args:    "[--short] [--format yaml|json] [--output <path>]",
		Purpose: "list spaces known to Juju, including associated subnets",
		Doc:     strings.TrimSpace(listCommandDoc),
	}
}

// SetFlags is defined on the cmd.Command interface.
func (c *ListCommand) SetFlags(f *gnuflag.FlagSet) {
	c.SpaceCommandBase.SetFlags(f)
	c.out.AddFlags(f, "yaml", map[string]cmd.Formatter{
		"yaml": cmd.FormatYaml,
		"json": cmd.FormatJson,
	})

	f.BoolVar(&c.Short, "short", false, "only display spaces.")
}

// Init is defined on the cmd.Command interface. It checks the
// arguments for sanity and sets up the command to run.
func (c *ListCommand) Init(args []string) error {
	// No arguments are accepted, just flags.
	if err := cmd.CheckEmpty(args); err != nil {
		return err
	}

	return nil
}

// Run implements Command.Run.
func (c *ListCommand) Run(ctx *cmd.Context) error {
	api, err := c.NewAPI()
	if err != nil {
		return errors.Annotate(err, "cannot connect to API server")
	}
	defer api.Close()

	spaces, err := api.AllSpaces()
	if err != nil {
		return errors.Annotate(err, "cannot list spaces")
	}
	if len(spaces) == 0 {
		ctx.Infof("no spaces to display")
		return c.out.Write(ctx, nil)
	}

	// Get the list of subnets
	subnets, err := api.AllSubnets()
	if err != nil {
		return errors.Annotate(err, "cannot list subnets")
	}
	if len(subnets) == 0 {
		return errors.NotValidf("no subnets found, but found spaces:")
	}

	// Create a map of CIDR -> subnet
	subnetMap := make(map[string]params.Subnet)
	for _, subnet := range subnets {
		subnetMap[subnet.CIDR] = subnet
	}

	if c.Short {
		result := formattedShortList{}
		for _, space := range spaces {
			result.Spaces = append(result.Spaces, space.Name)
		}
		return c.out.Write(ctx, result)
	} else {
		// Construct the output list for displaying with the chosen
		// format.
		result := formattedList{
			Spaces: make(map[string]map[string]formattedSubnet),
		}

		for _, space := range spaces {
			result.Spaces[space.Name] = make(map[string]formattedSubnet)
			for _, CIDR := range space.CIDRs {
				sub := subnetMap[CIDR]
				subResult := formattedSubnet{
					Type:       typeUnknown,
					ProviderId: sub.ProviderId,
					Zones:      sub.Zones,
				}
				// Display correct status according to the life cycle value.
				switch sub.Life {
				case params.Alive:
					subResult.Status = statusInUse
				case params.Dying, params.Dead:
					subResult.Status = statusTerminating
				}

				// Use the CIDR to determine the subnet type.
				if ip, _, err := net.ParseCIDR(sub.CIDR); err != nil {
					// This should never happen as subnets will be
					// validated before saving in state.
					msg := fmt.Sprintf("error: invalid subnet CIDR: %s", sub.CIDR)
					subResult.Status = msg
				} else if ip.To4() != nil {
					subResult.Type = typeIPv4
				} else if ip.To16() != nil {
					subResult.Type = typeIPv6
				}
				result.Spaces[space.Name][CIDR] = subResult
			}
		}
		return c.out.Write(ctx, result)
	}
}

const (
	typeUnknown = "unknown"
	typeIPv4    = "ipv4"
	typeIPv6    = "ipv6"

	statusInUse       = "in-use"
	statusTerminating = "terminating"
)

type formattedList struct {
	Spaces map[string]map[string]formattedSubnet `json:"spaces" yaml:"spaces"`
}

type formattedShortList struct {
	Spaces []string `json:"spaces" yaml:"spaces"`
}

// A goyaml bug means we can't declare these types locally to the
// GetYAML methods.
type formattedListNoMethods formattedList

// MarshalJSON is defined on json.Marshaller.
func (l formattedList) MarshalJSON() ([]byte, error) {
	return json.Marshal(formattedListNoMethods(l))
}

// GetYAML is defined on yaml.Getter.
func (l formattedList) GetYAML() (tag string, value interface{}) {
	return "", formattedListNoMethods(l)
}

type formattedSubnet struct {
	Type       string   `json:"type" yaml:"type"`
	ProviderId string   `json:"provider-id,omitempty" yaml:"provider-id,omitempty"`
	Status     string   `json:"status,omitempty" yaml:"status,omitempty"`
	Zones      []string `json:"zones" yaml:"zones"`
}

// A goyaml bug means we can't declare these types locally to the
// GetYAML methods.
type formattedSubnetNoMethods formattedSubnet

// MarshalJSON is defined on json.Marshaller.
func (s formattedSubnet) MarshalJSON() ([]byte, error) {
	return json.Marshal(formattedSubnetNoMethods(s))
}

// GetYAML is defined on yaml.Getter.
func (s formattedSubnet) GetYAML() (tag string, value interface{}) {
	return "", formattedSubnetNoMethods(s)
}
<commit_msg>Comment correction<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package space

import (
	"encoding/json"
	"fmt"
	"net"
	"strings"

	"launchpad.net/gnuflag"

	"github.com/juju/cmd"
	"github.com/juju/errors"
	"github.com/juju/juju/apiserver/params"
)

// ListCommand displays a list of all spaces known to Juju.
type ListCommand struct {
	SpaceCommandBase
	Short bool
	out   cmd.Output
}

const listCommandDoc = `
Displays all defined spaces. If --short is not given both spaces and
their subnets are displayed, otherwise just a list of spaces. The
--format argument has the same semantics as in other CLI commands -
"yaml" is the default. The --output argument allows the command
output to be redirected to a file. `

// Info is defined on the cmd.Command interface.
func (c *ListCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "list",
		Args:    "[--short] [--format yaml|json] [--output <path>]",
		Purpose: "list spaces known to Juju, including associated subnets",
		Doc:     strings.TrimSpace(listCommandDoc),
	}
}

// SetFlags is defined on the cmd.Command interface.
func (c *ListCommand) SetFlags(f *gnuflag.FlagSet) {
	c.SpaceCommandBase.SetFlags(f)
	c.out.AddFlags(f, "yaml", map[string]cmd.Formatter{
		"yaml": cmd.FormatYaml,
		"json": cmd.FormatJson,
	})

	f.BoolVar(&c.Short, "short", false, "only display spaces.")
}

// Init is defined on the cmd.Command interface. It checks the
// arguments for sanity and sets up the command to run.
func (c *ListCommand) Init(args []string) error {
	// No arguments are accepted, just flags.
	if err := cmd.CheckEmpty(args); err != nil {
		return err
	}

	return nil
}

// Run implements Command.Run.
func (c *ListCommand) Run(ctx *cmd.Context) error {
	api, err := c.NewAPI()
	if err != nil {
		return errors.Annotate(err, "cannot connect to API server")
	}
	defer api.Close()

	spaces, err := api.AllSpaces()
	if err != nil {
		return errors.Annotate(err, "cannot list spaces")
	}
	if len(spaces) == 0 {
		ctx.Infof("no spaces to display")
		return c.out.Write(ctx, nil)
	}

	// Get the list of subnets
	subnets, err := api.AllSubnets()
	if err != nil {
		return errors.Annotate(err, "cannot list subnets")
	}
	if len(subnets) == 0 {
		return errors.NotValidf("no subnets found, but found spaces:")
	}

	// Create a map of CIDR -> subnet
	subnetMap := make(map[string]params.Subnet)
	for _, subnet := range subnets {
		subnetMap[subnet.CIDR] = subnet
	}

	if c.Short {
		result := formattedShortList{}
		for _, space := range spaces {
			result.Spaces = append(result.Spaces, space.Name)
		}
		return c.out.Write(ctx, result)
	} else {
		// Construct the output list for displaying with the chosen
		// format.
		result := formattedList{
			Spaces: make(map[string]map[string]formattedSubnet),
		}

		for _, space := range spaces {
			result.Spaces[space.Name] = make(map[string]formattedSubnet)
			for _, CIDR := range space.CIDRs {
				sub := subnetMap[CIDR]
				subResult := formattedSubnet{
					Type:       typeUnknown,
					ProviderId: sub.ProviderId,
					Zones:      sub.Zones,
				}
				// Display correct status according to the life cycle value.
				switch sub.Life {
				case params.Alive:
					subResult.Status = statusInUse
				case params.Dying, params.Dead:
					subResult.Status = statusTerminating
				}

				// Use the CIDR to determine the subnet type.
				if ip, _, err := net.ParseCIDR(sub.CIDR); err != nil {
					// This should never happen as subnets will be
					// validated before saving in state.
					msg := fmt.Sprintf("error: invalid subnet CIDR: %s", sub.CIDR)
					subResult.Status = msg
				} else if ip.To4() != nil {
					subResult.Type = typeIPv4
				} else if ip.To16() != nil {
					subResult.Type = typeIPv6
				}
				result.Spaces[space.Name][CIDR] = subResult
			}
		}
		return c.out.Write(ctx, result)
	}
}

const (
	typeUnknown = "unknown"
	typeIPv4    = "ipv4"
	typeIPv6    = "ipv6"

	statusInUse       = "in-use"
	statusTerminating = "terminating"
)

type formattedList struct {
	Spaces map[string]map[string]formattedSubnet `json:"spaces" yaml:"spaces"`
}

type formattedShortList struct {
	Spaces []string `json:"spaces" yaml:"spaces"`
}

// A goyaml bug means we can't declare these types locally to the
// GetYAML methods.
type formattedListNoMethods formattedList

// MarshalJSON is defined on json.Marshaller.
func (l formattedList) MarshalJSON() ([]byte, error) {
	return json.Marshal(formattedListNoMethods(l))
}

// GetYAML is defined on yaml.Getter.
func (l formattedList) GetYAML() (tag string, value interface{}) {
	return "", formattedListNoMethods(l)
}

type formattedSubnet struct {
	Type       string   `json:"type" yaml:"type"`
	ProviderId string   `json:"provider-id,omitempty" yaml:"provider-id,omitempty"`
	Status     string   `json:"status,omitempty" yaml:"status,omitempty"`
	Zones      []string `json:"zones" yaml:"zones"`
}

// A goyaml bug means we can't declare these types locally to the
// GetYAML methods.
type formattedSubnetNoMethods formattedSubnet

// MarshalJSON is defined on json.Marshaller.
func (s formattedSubnet) MarshalJSON() ([]byte, error) {
	return json.Marshal(formattedSubnetNoMethods(s))
}

// GetYAML is defined on yaml.Getter.
func (s formattedSubnet) GetYAML() (tag string, value interface{}) {
	return "", formattedSubnetNoMethods(s)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/slashquery/resolver"
	"github.com/slashquery/slashquery"
)

var version string

func main() {
	var (
		v = flag.Bool("v", false, fmt.Sprintf("Print version: %s", version))
		f = flag.String("f", "slashquery.yaml", "Configuration `slashquery.yaml`")
	)

	flag.Parse()

	if *v {
		fmt.Printf("%s\n", version)
		os.Exit(0)
	}

	if _, err := os.Stat(*f); os.IsNotExist(err) {
		fmt.Printf("Cannot read configuration file: %s, use -h for more info.\n", *f)
		os.Exit(1)
	}

	sq, err := slashquery.New(*f)
	if err != nil {
		log.Fatalln(err)
	}

	r, _ := resolver.New("8.8.8.8")
	for k, v := range sq.Upstreams {
		fmt.Printf("upstream = %+v\n", k)
		fmt.Printf("servers = %+v\n", v.Servers)
		for _, v := range v.Servers {
			ans, err := r.Resolve(v)
			if err != nil {
				fmt.Printf("err = %+v\n", err)
			}
			sq.Servers[k] = slashquery.Servers{
				Addresses: ans.Addresses,
				Expire:    time.Now().Add(time.Duration(ans.TTL) * time.Second),
			}
		}
		println()
	}

	for k, v := range sq.Servers {
		fmt.Printf("k = %+v\n", k)
		fmt.Printf("v = %+v\n", v)
	}
}
<commit_msg>	modified:   cmd/slashquery/main.go<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"

	"github.com/slashquery/slashquery"
)

var version string

func main() {
	var (
		v = flag.Bool("v", false, fmt.Sprintf("Print version: %s", version))
		f = flag.String("f", "slashquery.yaml", "Configuration `slashquery.yaml`")
	)

	flag.Parse()

	if *v {
		fmt.Printf("%s\n", version)
		os.Exit(0)
	}

	if _, err := os.Stat(*f); os.IsNotExist(err) {
		fmt.Printf("Cannot read configuration file: %s, use -h for more info.\n", *f)
		os.Exit(1)
	}

	sq, err := slashquery.New(*f)
	if err != nil {
		log.Fatalln(err)
	}

	// Get upstream IP's
	sq.ResolveUpstreams()

	for k, v := range sq.Servers {
		fmt.Printf("k = %+v\n", k)
		fmt.Printf("v = %+v\n", v)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"os"
)

type SlicerCmd struct {
	Bin  string
	Args []string
	Log  io.Writer
}

type Slicer interface {
	SlicerCmd() SlicerCmd
}

func Run(s Slicer, kill <-chan struct{}) error {
	return fmt.Errorf("x")
}

type Slic3r struct {
	Bin        string
	ConfigPath string
	OutPath    string
	InPath     string
}

func (s *Slic3r) SlicerCmd() *SlicerCmd {
	bin := s.Bin
	if bin == "" {
		bin = "slic3r"
	}
	var args []string
	config := s.ConfigPath
	if config != "" {
		args = append(args, config)
	}
	out := s.OutPath
	if out != "" {
		args = append(args, "-o", out)
	}
	in := s.InPath
	if in != "" {
		args = append(args, in)
	}
	return &SlicerCmd{
		Bin:  bin,
		Args: args,
		Log:  os.Stderr,
	}
}
<commit_msg>fill out logic for running slicers<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
)

type SlicerCmd struct {
	Bin    string
	Args   []string
	OutLog io.Writer
	ErrLog io.Writer
}

type Slicer interface {
	SlicerCmd() SlicerCmd
}

func Run(s Slicer, kill <-chan struct{}) error {
	scmd := s.SlicerCmd()
	cmd := exec.Command(scmd.Bin, scmd.Args...)
	cmd.Stdout = scmd.OutLog
	cmd.Stderr = scmd.ErrLog
	err := cmd.Start()
	if err != nil {
		return fmt.Errorf("%s: %v", scmd.Bin, err)
	}
	done := make(chan struct{})
	go func() {
		select {
		case <-done:
		case <-kill:
			err := cmd.Process.Kill()
			if err != nil {
				log.Printf("kill: %v", err)
			}
		}
	}()
	err = cmd.Wait()
	close(done)
	if err != nil {
		return err
	}
	return nil
}

type Slic3r struct {
	Bin        string
	ConfigPath string
	OutPath    string
	InPath     string
}

func (s *Slic3r) SlicerCmd() *SlicerCmd {
	bin := s.Bin
	if bin == "" {
		bin = "slic3r"
	}
	var args []string
	config := s.ConfigPath
	if config != "" {
		args = append(args, config)
	}
	out := s.OutPath
	if out != "" {
		args = append(args, "-o", out)
	}
	in := s.InPath
	if in != "" {
		args = append(args, in)
	}
	return &SlicerCmd{
		Bin:    bin,
		Args:   args,
		OutLog: os.Stderr,
		ErrLog: os.Stderr,
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Microsoft. All rights reserved.
// MIT License

package network

import (
	"net"

	"github.com/Azure/azure-container-networking/cni"
	"github.com/Azure/azure-container-networking/common"
	"github.com/Azure/azure-container-networking/log"
	"github.com/Azure/azure-container-networking/network"
	"github.com/Azure/azure-container-networking/platform"

	cniSkel "github.com/containernetworking/cni/pkg/skel"
	cniTypesCurr "github.com/containernetworking/cni/pkg/types/current"
)

const (
	// Plugin name.
	name = "azure-vnet"
)

// NetPlugin represents the CNI network plugin.
type netPlugin struct {
	*cni.Plugin
	nm network.NetworkManager
}

// NewPlugin creates a new netPlugin object.
func NewPlugin(config *common.PluginConfig) (*netPlugin, error) {
	// Setup base plugin.
	plugin, err := cni.NewPlugin(name, config.Version)
	if err != nil {
		return nil, err
	}

	// Setup network manager.
	nm, err := network.NewNetworkManager()
	if err != nil {
		return nil, err
	}

	config.NetApi = nm

	return &netPlugin{
		Plugin: plugin,
		nm:     nm,
	}, nil
}

// Starts the plugin.
func (plugin *netPlugin) Start(config *common.PluginConfig) error {
	// Initialize base plugin.
	err := plugin.Initialize(config)
	if err != nil {
		log.Printf("[cni-net] Failed to initialize base plugin, err:%v.", err)
		return err
	}

	// Log platform information.
	log.Printf("[cni-net] Plugin %v version %v.", plugin.Name, plugin.Version)
	log.Printf("[cni-net] Running on %v", platform.GetOSInfo())
	common.LogNetworkInterfaces()

	// Initialize network manager.
	err = plugin.nm.Initialize(config)
	if err != nil {
		log.Printf("[cni-net] Failed to initialize network manager, err:%v.", err)
		return err
	}

	log.Printf("[cni-net] Plugin started.")

	return nil
}

// Stops the plugin.
func (plugin *netPlugin) Stop() {
	plugin.nm.Uninitialize()
	plugin.Uninitialize()
	log.Printf("[cni-net] Plugin stopped.")
}

// FindMasterInterface returns the name of the master interface.
func (plugin *netPlugin) findMasterInterface(nwCfg *cni.NetworkConfig, subnetPrefix *net.IPNet) string {
	// An explicit master configuration wins. Explicitly specifying a master is
	// useful if host has multiple interfaces with addresses in the same subnet.
	if nwCfg.Master != "" {
		return nwCfg.Master
	}

	// Otherwise, pick the first interface with an IP address in the given subnet.
	subnetPrefixString := subnetPrefix.String()
	interfaces, _ := net.Interfaces()
	for _, iface := range interfaces {
		addrs, _ := iface.Addrs()
		for _, addr := range addrs {
			_, ipnet, err := net.ParseCIDR(addr.String())
			if err != nil {
				continue
			}
			if subnetPrefixString == ipnet.String() {
				return iface.Name
			}
		}
	}

	// Failed to find a suitable interface.
	return ""
}

//
// CNI implementation
// https://github.com/containernetworking/cni/blob/master/SPEC.md
//

// Add handles CNI add commands.
func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) error {
	var result *cniTypesCurr.Result
	var err error

	log.Printf("[cni-net] Processing ADD command with args {ContainerID:%v Netns:%v IfName:%v Args:%v Path:%v}.",
		args.ContainerID, args.Netns, args.IfName, args.Args, args.Path)

	defer func() { log.Printf("[cni-net] ADD command completed with result:%+v err:%v.", result, err) }()

	// Parse network configuration from stdin.
	nwCfg, err := cni.ParseNetworkConfig(args.StdinData)
	if err != nil {
		err = plugin.Errorf("Failed to parse network configuration: %v.", err)
		return err
	}

	log.Printf("[cni-net] Read network configuration %+v.", nwCfg)

	// Initialize values from network config.
	networkId := nwCfg.Name
	endpointId := plugin.GetEndpointID(args)

	// Check whether the network already exists.
	nwInfo, err := plugin.nm.GetNetworkInfo(networkId)
	if err != nil {
		// Network does not exist.
		log.Printf("[cni-net] Creating network %v.", networkId)

		// Call into IPAM plugin to allocate an address pool for the network.
		result, err = plugin.DelegateAdd(nwCfg.Ipam.Type, nwCfg)
		if err != nil {
			err = plugin.Errorf("Failed to allocate pool: %v", err)
			return err
		}

		// Derive the subnet prefix from allocated IP address.
		ipconfig := result.IPs[0]
		subnetPrefix := ipconfig.Address
		subnetPrefix.IP = subnetPrefix.IP.Mask(subnetPrefix.Mask)

		// On failure, call into IPAM plugin to release the address and address pool.
		defer func() {
			if err != nil {
				nwCfg.Ipam.Subnet = subnetPrefix.String()
				nwCfg.Ipam.Address = ipconfig.Address.IP.String()
				plugin.DelegateDel(nwCfg.Ipam.Type, nwCfg)

				nwCfg.Ipam.Address = ""
				plugin.DelegateDel(nwCfg.Ipam.Type, nwCfg)
			}
		}()

		// Find the master interface.
		masterIfName := plugin.findMasterInterface(nwCfg, &subnetPrefix)
		if masterIfName == "" {
			err = plugin.Errorf("Failed to find the master interface")
			return err
		}
		log.Printf("[cni-net] Found master interface %v.", masterIfName)

		// Add the master as an external interface.
		err = plugin.nm.AddExternalInterface(masterIfName, subnetPrefix.String())
		if err != nil {
			err = plugin.Errorf("Failed to add external interface: %v", err)
			return err
		}

		// Create the network.
		nwInfo := network.NetworkInfo{
			Id:   networkId,
			Mode: nwCfg.Mode,
			Subnets: []network.SubnetInfo{
				network.SubnetInfo{
					Family:  platform.AfINET,
					Prefix:  subnetPrefix,
					Gateway: ipconfig.Gateway,
				},
			},
			BridgeName: nwCfg.Bridge,
		}

		err = plugin.nm.CreateNetwork(&nwInfo)
		if err != nil {
			err = plugin.Errorf("Failed to create network: %v", err)
			return err
		}

		log.Printf("[cni-net] Created network %v with subnet %v.", networkId, subnetPrefix.String())
	} else {
		// Network already exists.
		subnetPrefix := nwInfo.Subnets[0].Prefix.String()
		log.Printf("[cni-net] Found network %v with subnet %v.", networkId, subnetPrefix)

		// Call into IPAM plugin to allocate an address for the endpoint.
		nwCfg.Ipam.Subnet = subnetPrefix
		result, err = plugin.DelegateAdd(nwCfg.Ipam.Type, nwCfg)
		if err != nil {
			err = plugin.Errorf("Failed to allocate address: %v", err)
			return err
		}

		ipconfig := result.IPs[0]

		// On failure, call into IPAM plugin to release the address.
		defer func() {
			if err != nil {
				nwCfg.Ipam.Address = ipconfig.Address.IP.String()
				plugin.DelegateDel(nwCfg.Ipam.Type, nwCfg)
			}
		}()
	}

	// Initialize endpoint info.
	epInfo := &network.EndpointInfo{
		Id:          endpointId,
		ContainerID: args.ContainerID,
		NetNsPath:   args.Netns,
		IfName:      args.IfName,
	}

	// Populate addresses.
	for _, ipconfig := range result.IPs {
		epInfo.IPAddresses = append(epInfo.IPAddresses, ipconfig.Address)
	}

	// Populate routes.
	for _, route := range result.Routes {
		epInfo.Routes = append(epInfo.Routes, network.RouteInfo{Dst: route.Dst, Gw: route.GW})
	}

	// Populate DNS info.
	epInfo.DNS.Suffix = result.DNS.Domain
	epInfo.DNS.Servers = result.DNS.Nameservers

	// Create the endpoint.
	log.Printf("[cni-net] Creating endpoint %v.", epInfo.Id)
	err = plugin.nm.CreateEndpoint(networkId, epInfo)
	if err != nil {
		err = plugin.Errorf("Failed to create endpoint: %v", err)
		return err
	}

	// Add Interfaces to result.
	iface := &cniTypesCurr.Interface{
		Name: epInfo.IfName,
	}
	result.Interfaces = append(result.Interfaces, iface)

	// Convert result to the requested CNI version.
	res, err := result.GetAsVersion(nwCfg.CNIVersion)
	if err != nil {
		err = plugin.Error(err)
		return err
	}

	// Output the result to stdout.
	res.Print()

	return nil
}

// Delete handles CNI delete commands.
func (plugin *netPlugin) Delete(args *cniSkel.CmdArgs) error {
	var err error

	log.Printf("[cni-net] Processing DEL command with args {ContainerID:%v Netns:%v IfName:%v Args:%v Path:%v}.",
		args.ContainerID, args.Netns, args.IfName, args.Args, args.Path)

	defer func() { log.Printf("[cni-net] DEL command completed with err:%v.", err) }()

	// Parse network configuration from stdin.
	nwCfg, err := cni.ParseNetworkConfig(args.StdinData)
	if err != nil {
		err = plugin.Errorf("Failed to parse network configuration: %v", err)
		return err
	}

	log.Printf("[cni-net] Read network configuration %+v.", nwCfg)

	// Initialize values from network config.
	networkId := nwCfg.Name
	endpointId := plugin.GetEndpointID(args)

	// Query the network.
	nwInfo, err := plugin.nm.GetNetworkInfo(networkId)
	if err != nil {
		// Log the error but return success if the endpoint being deleted is not found.
		plugin.Errorf("Failed to query network: %v", err)
		err = nil
		return err
	}

	// Query the endpoint.
	epInfo, err := plugin.nm.GetEndpointInfo(networkId, endpointId)
	if err != nil {
		// Log the error but return success if the endpoint being deleted is not found.
		plugin.Errorf("Failed to query endpoint: %v", err)
		err = nil
		return err
	}

	// Delete the endpoint.
	err = plugin.nm.DeleteEndpoint(networkId, endpointId)
	if err != nil {
		err = plugin.Errorf("Failed to delete endpoint: %v", err)
		return err
	}

	// Call into IPAM plugin to release the endpoint's addresses.
	nwCfg.Ipam.Subnet = nwInfo.Subnets[0].Prefix.String()
	for _, address := range epInfo.IPAddresses {
		nwCfg.Ipam.Address = address.IP.String()
		err = plugin.DelegateDel(nwCfg.Ipam.Type, nwCfg)
		if err != nil {
			err = plugin.Errorf("Failed to release address: %v", err)
			return err
		}
	}

	return nil
}
<commit_msg>Fix for leaking CA while deleting network. Even if deleting endpoint failed, CNI should invoke ipam release call.<commit_after>// Copyright 2017 Microsoft. All rights reserved.
// MIT License

package network

import (
	"net"

	"github.com/Azure/azure-container-networking/cni"
	"github.com/Azure/azure-container-networking/common"
	"github.com/Azure/azure-container-networking/log"
	"github.com/Azure/azure-container-networking/network"
	"github.com/Azure/azure-container-networking/platform"

	cniSkel "github.com/containernetworking/cni/pkg/skel"
	cniTypesCurr "github.com/containernetworking/cni/pkg/types/current"
)

const (
	// Plugin name.
	name = "azure-vnet"
)

// NetPlugin represents the CNI network plugin.
type netPlugin struct {
	*cni.Plugin
	nm network.NetworkManager
}

// NewPlugin creates a new netPlugin object.
func NewPlugin(config *common.PluginConfig) (*netPlugin, error) {
	// Setup base plugin.
	plugin, err := cni.NewPlugin(name, config.Version)
	if err != nil {
		return nil, err
	}

	// Setup network manager.
	nm, err := network.NewNetworkManager()
	if err != nil {
		return nil, err
	}

	config.NetApi = nm

	return &netPlugin{
		Plugin: plugin,
		nm:     nm,
	}, nil
}

// Starts the plugin.
func (plugin *netPlugin) Start(config *common.PluginConfig) error {
	// Initialize base plugin.
	err := plugin.Initialize(config)
	if err != nil {
		log.Printf("[cni-net] Failed to initialize base plugin, err:%v.", err)
		return err
	}

	// Log platform information.
	log.Printf("[cni-net] Plugin %v version %v.", plugin.Name, plugin.Version)
	log.Printf("[cni-net] Running on %v", platform.GetOSInfo())
	common.LogNetworkInterfaces()

	// Initialize network manager.
	err = plugin.nm.Initialize(config)
	if err != nil {
		log.Printf("[cni-net] Failed to initialize network manager, err:%v.", err)
		return err
	}

	log.Printf("[cni-net] Plugin started.")

	return nil
}

// Stops the plugin.
func (plugin *netPlugin) Stop() {
	plugin.nm.Uninitialize()
	plugin.Uninitialize()
	log.Printf("[cni-net] Plugin stopped.")
}

// FindMasterInterface returns the name of the master interface.
func (plugin *netPlugin) findMasterInterface(nwCfg *cni.NetworkConfig, subnetPrefix *net.IPNet) string {
	// An explicit master configuration wins. Explicitly specifying a master is
	// useful if host has multiple interfaces with addresses in the same subnet.
	if nwCfg.Master != "" {
		return nwCfg.Master
	}

	// Otherwise, pick the first interface with an IP address in the given subnet.
	subnetPrefixString := subnetPrefix.String()
	interfaces, _ := net.Interfaces()
	for _, iface := range interfaces {
		addrs, _ := iface.Addrs()
		for _, addr := range addrs {
			_, ipnet, err := net.ParseCIDR(addr.String())
			if err != nil {
				continue
			}
			if subnetPrefixString == ipnet.String() {
				return iface.Name
			}
		}
	}

	// Failed to find a suitable interface.
	return ""
}

//
// CNI implementation
// https://github.com/containernetworking/cni/blob/master/SPEC.md
//

// Add handles CNI add commands.
func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) error {
	var result *cniTypesCurr.Result
	var err error

	log.Printf("[cni-net] Processing ADD command with args {ContainerID:%v Netns:%v IfName:%v Args:%v Path:%v}.",
		args.ContainerID, args.Netns, args.IfName, args.Args, args.Path)

	defer func() { log.Printf("[cni-net] ADD command completed with result:%+v err:%v.", result, err) }()

	// Parse network configuration from stdin.
	nwCfg, err := cni.ParseNetworkConfig(args.StdinData)
	if err != nil {
		err = plugin.Errorf("Failed to parse network configuration: %v.", err)
		return err
	}

	log.Printf("[cni-net] Read network configuration %+v.", nwCfg)

	// Initialize values from network config.
	networkId := nwCfg.Name
	endpointId := plugin.GetEndpointID(args)

	// Check whether the network already exists.
	nwInfo, err := plugin.nm.GetNetworkInfo(networkId)
	if err != nil {
		// Network does not exist.
		log.Printf("[cni-net] Creating network %v.", networkId)

		// Call into IPAM plugin to allocate an address pool for the network.
		result, err = plugin.DelegateAdd(nwCfg.Ipam.Type, nwCfg)
		if err != nil {
			err = plugin.Errorf("Failed to allocate pool: %v", err)
			return err
		}

		// Derive the subnet prefix from allocated IP address.
		ipconfig := result.IPs[0]
		subnetPrefix := ipconfig.Address
		subnetPrefix.IP = subnetPrefix.IP.Mask(subnetPrefix.Mask)

		// On failure, call into IPAM plugin to release the address and address pool.
		defer func() {
			if err != nil {
				nwCfg.Ipam.Subnet = subnetPrefix.String()
				nwCfg.Ipam.Address = ipconfig.Address.IP.String()
				plugin.DelegateDel(nwCfg.Ipam.Type, nwCfg)

				nwCfg.Ipam.Address = ""
				plugin.DelegateDel(nwCfg.Ipam.Type, nwCfg)
			}
		}()

		// Find the master interface.
		masterIfName := plugin.findMasterInterface(nwCfg, &subnetPrefix)
		if masterIfName == "" {
			err = plugin.Errorf("Failed to find the master interface")
			return err
		}
		log.Printf("[cni-net] Found master interface %v.", masterIfName)

		// Add the master as an external interface.
		err = plugin.nm.AddExternalInterface(masterIfName, subnetPrefix.String())
		if err != nil {
			err = plugin.Errorf("Failed to add external interface: %v", err)
			return err
		}

		// Create the network.
		nwInfo := network.NetworkInfo{
			Id:   networkId,
			Mode: nwCfg.Mode,
			Subnets: []network.SubnetInfo{
				network.SubnetInfo{
					Family:  platform.AfINET,
					Prefix:  subnetPrefix,
					Gateway: ipconfig.Gateway,
				},
			},
			BridgeName: nwCfg.Bridge,
		}

		err = plugin.nm.CreateNetwork(&nwInfo)
		if err != nil {
			err = plugin.Errorf("Failed to create network: %v", err)
			return err
		}

		log.Printf("[cni-net] Created network %v with subnet %v.", networkId, subnetPrefix.String())
	} else {
		// Network already exists.
		subnetPrefix := nwInfo.Subnets[0].Prefix.String()
		log.Printf("[cni-net] Found network %v with subnet %v.", networkId, subnetPrefix)

		// Call into IPAM plugin to allocate an address for the endpoint.
		nwCfg.Ipam.Subnet = subnetPrefix
		result, err = plugin.DelegateAdd(nwCfg.Ipam.Type, nwCfg)
		if err != nil {
			err = plugin.Errorf("Failed to allocate address: %v", err)
			return err
		}

		ipconfig := result.IPs[0]

		// On failure, call into IPAM plugin to release the address.
		defer func() {
			if err != nil {
				nwCfg.Ipam.Address = ipconfig.Address.IP.String()
				plugin.DelegateDel(nwCfg.Ipam.Type, nwCfg)
			}
		}()
	}

	// Initialize endpoint info.
	epInfo := &network.EndpointInfo{
		Id:          endpointId,
		ContainerID: args.ContainerID,
		NetNsPath:   args.Netns,
		IfName:      args.IfName,
	}

	// Populate addresses.
	for _, ipconfig := range result.IPs {
		epInfo.IPAddresses = append(epInfo.IPAddresses, ipconfig.Address)
	}

	// Populate routes.
	for _, route := range result.Routes {
		epInfo.Routes = append(epInfo.Routes, network.RouteInfo{Dst: route.Dst, Gw: route.GW})
	}

	// Populate DNS info.
	epInfo.DNS.Suffix = result.DNS.Domain
	epInfo.DNS.Servers = result.DNS.Nameservers

	// Create the endpoint.
	log.Printf("[cni-net] Creating endpoint %v.", epInfo.Id)
	err = plugin.nm.CreateEndpoint(networkId, epInfo)
	if err != nil {
		err = plugin.Errorf("Failed to create endpoint: %v", err)
		return err
	}

	// Add Interfaces to result.
	iface := &cniTypesCurr.Interface{
		Name: epInfo.IfName,
	}
	result.Interfaces = append(result.Interfaces, iface)

	// Convert result to the requested CNI version.
	res, err := result.GetAsVersion(nwCfg.CNIVersion)
	if err != nil {
		err = plugin.Error(err)
		return err
	}

	// Output the result to stdout.
	res.Print()

	return nil
}

// Delete handles CNI delete commands.
func (plugin *netPlugin) Delete(args *cniSkel.CmdArgs) error {
	var err error

	log.Printf("[cni-net] Processing DEL command with args {ContainerID:%v Netns:%v IfName:%v Args:%v Path:%v}.",
		args.ContainerID, args.Netns, args.IfName, args.Args, args.Path)

	defer func() { log.Printf("[cni-net] DEL command completed with err:%v.", err) }()

	// Parse network configuration from stdin.
	nwCfg, err := cni.ParseNetworkConfig(args.StdinData)
	if err != nil {
		err = plugin.Errorf("Failed to parse network configuration: %v", err)
		return err
	}

	log.Printf("[cni-net] Read network configuration %+v.", nwCfg)

	// Initialize values from network config.
	networkId := nwCfg.Name
	endpointId := plugin.GetEndpointID(args)

	// Query the network.
	nwInfo, err := plugin.nm.GetNetworkInfo(networkId)
	if err != nil {
		// Log the error but return success if the endpoint being deleted is not found.
		plugin.Errorf("Failed to query network: %v", err)
		err = nil
		return err
	}

	// Query the endpoint.
	epInfo, err := plugin.nm.GetEndpointInfo(networkId, endpointId)
	if err != nil {
		// Log the error but return success if the endpoint being deleted is not found.
		plugin.Errorf("Failed to query endpoint: %v", err)
		err = nil
		return err
	}

	// Delete the endpoint.
	err = plugin.nm.DeleteEndpoint(networkId, endpointId)
	if err != nil {
		// Log the error but don't return before releasing address
		log.Printf("Container Namespace might have been removed. Failed to delete endpoint: %v", err)
	}

	// Call into IPAM plugin to release the endpoint's addresses.
	nwCfg.Ipam.Subnet = nwInfo.Subnets[0].Prefix.String()
	for _, address := range epInfo.IPAddresses {
		nwCfg.Ipam.Address = address.IP.String()
		err = plugin.DelegateDel(nwCfg.Ipam.Type, nwCfg)
		if err != nil {
			log.Printf("Failed to release address: %v", err)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"
	"time"
	"unsafe"

	"golang.org/x/sys/windows/svc"
	"golang.org/x/sys/windows/svc/eventlog"
)

const name = "mackerel-agent"

const defaultEid = 1
const startEid = 2
const stopEid = 3
const loggerEid = 4

var (
	kernel32                     = syscall.NewLazyDLL("kernel32")
	procAllocConsole             = kernel32.NewProc("AllocConsole")
	procGenerateConsoleCtrlEvent = kernel32.NewProc("GenerateConsoleCtrlEvent")
	procGetModuleFileName        = kernel32.NewProc("GetModuleFileNameW")
)

func main() {
	if len(os.Args) == 2 {
		var err error
		switch os.Args[1] {
		case "install":
			err = installService("mackerel-agent", "mackerel agent")
		case "remove":
			err = removeService("mackerel-agent")
		}
		if err != nil {
			log.Fatal(err)
		}
		return
	}

	elog, err := eventlog.Open(name)
	if err != nil {
		log.Fatal(err.Error())
	}
	defer elog.Close()

	// `svc.Run` blocks until windows service will stopped.
	// ref. https://msdn.microsoft.com/library/cc429362.aspx
	err = svc.Run(name, &handler{elog: elog})
	if err != nil {
		log.Fatal(err.Error())
	}
}

type handler struct {
	elog *eventlog.Log
	cmd  *exec.Cmd
}

// ex.
// verbose log: 2017/01/21 22:21:08 command.go:434: DEBUG <command> received 'immediate' chan
// normal log:  2017/01/24 14:14:27 INFO <main> Starting mackerel-agent version:0.36.0
var logRe = regexp.MustCompile(`^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} (?:.+\.go:\d+: )?([A-Z]+) `)

func (h *handler) start() error {
	procAllocConsole.Call()
	dir := execdir()
	cmd := exec.Command(filepath.Join(dir, "mackerel-agent.exe"))
	cmd.SysProcAttr = &syscall.SysProcAttr{
		CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
	}
	cmd.Dir = dir

	h.cmd = cmd
	r, w := io.Pipe()
	cmd.Stderr = w

	br := bufio.NewReader(r)
	lc := make(chan string, 10)
	done := make(chan struct{})

	err := cmd.Start()
	if err != nil {
		return err
	}

	// It need to read data from pipe continuously. And it need to close handle
	// when process finished.
	// read data from pipe. When data arrived at EOL, send line-string to the
	// channel. Also remaining line to EOF.
	go func() {
		defer w.Close()

		// pipe stderr to windows event log
		var body bytes.Buffer
		for {
			b, err := br.ReadByte()
			if err != nil {
				if err != nil {
					h.elog.Error(loggerEid, err.Error())
				}
				break
			}
			if b == '\n' {
				if body.Len() > 0 {
					lc <- body.String()
					body.Reset()
				}
				continue
			}
			// Read size should be 4096 because default size of PIPE is 4096.
			// We know this is not a limit size. This is workaround to avoid
			// that plugin stop writing to pipe.
			if body.Len() < 4096 {
				body.WriteByte(b)
			}
		}
		if body.Len() > 0 {
			lc <- body.String()
		}
		done <- struct{}{}
	}()

	go func() {
		linebuf := []string{}
	loop:
		for {
			select {
			case line := <-lc:
				linebuf = append(linebuf, line)
			case <-time.After(10 * time.Millisecond):
				// When it take 10ms, it is located at end of paragraph. Then
				// slice appended at above should be the paragraph.
				if len(linebuf) > 0 {
					if match := logRe.FindStringSubmatch(linebuf[0]); match != nil {
						line := strings.Join(linebuf, "\n")
						level := match[1]
						switch level {
						case "TRACE", "DEBUG", "INFO":
							h.elog.Info(defaultEid, line)
						case "WARNING":
							h.elog.Warning(defaultEid, line)
						case "ERROR", "CRITICAL":
							h.elog.Error(defaultEid, line)
						default:
							h.elog.Error(loggerEid, line)
						}
					}
					linebuf = nil
				}
				select {
				case <-done:
					break loop
				default:
				}
			}
		}
		close(lc)
		close(done)
	}()
	return nil
}

func interrupt(p *os.Process) error {
	r1, _, err := procGenerateConsoleCtrlEvent.Call(syscall.CTRL_BREAK_EVENT, uintptr(p.Pid))
	if r1 == 0 {
		return err
	}
	return nil
}

func (h *handler) stop() error {
	if h.cmd != nil && h.cmd.Process != nil {
		err := interrupt(h.cmd.Process)
		if err == nil {
			end := time.Now().Add(10 * time.Second)
			for time.Now().Before(end) {
				if h.cmd.ProcessState != nil && h.cmd.ProcessState.Exited() {
					return nil
				}
				time.Sleep(1 * time.Second)
			}
		}
		return h.cmd.Process.Kill()
	}
	return nil
}

// implement https://godoc.org/golang.org/x/sys/windows/svc#Handler
func (h *handler) Execute(args []string, r <-chan svc.ChangeRequest, s chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
	s <- svc.Status{State: svc.StartPending}
	defer func() {
		s <- svc.Status{State: svc.Stopped}
	}()

	if err := h.start(); err != nil {
		h.elog.Error(startEid, err.Error())
		// https://msdn.microsoft.com/library/windows/desktop/ms681383(v=vs.85).aspx
		// use ERROR_SERVICE_SPECIFIC_ERROR
		return true, 1
	}

	exit := make(chan struct{})
	go func() {
		err := h.cmd.Wait()
		// enter when the child process exited
		if err != nil {
			h.elog.Error(stopEid, err.Error())
		}
		exit <- struct{}{}
	}()

	s <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown}
L:
	for {
		select {
		case req := <-r:
			switch req.Cmd {
			case svc.Interrogate:
				s <- req.CurrentStatus
			case svc.Stop, svc.Shutdown:
				s <- svc.Status{State: svc.StopPending, Accepts: svc.AcceptStop | svc.AcceptShutdown}
				if err := h.stop(); err != nil {
					h.elog.Error(stopEid, err.Error())
					s <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown}
				}
			}
		case <-exit:
			break L
		}
	}

	return
}

func execdir() string {
	var wpath [syscall.MAX_PATH]uint16
	r1, _, err := procGetModuleFileName.Call(0, uintptr(unsafe.Pointer(&wpath[0])), uintptr(len(wpath)))
	if r1 == 0 {
		log.Fatal(err)
	}
	return filepath.Dir(syscall.UTF16ToString(wpath[:]))
}
<commit_msg>should check io.EOF<commit_after>package main

import (
	"bufio"
	"bytes"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"
	"time"
	"unsafe"

	"golang.org/x/sys/windows/svc"
	"golang.org/x/sys/windows/svc/eventlog"
)

const name = "mackerel-agent"

const defaultEid = 1
const startEid = 2
const stopEid = 3
const loggerEid = 4

var (
	kernel32                     = syscall.NewLazyDLL("kernel32")
	procAllocConsole             = kernel32.NewProc("AllocConsole")
	procGenerateConsoleCtrlEvent = kernel32.NewProc("GenerateConsoleCtrlEvent")
	procGetModuleFileName        = kernel32.NewProc("GetModuleFileNameW")
)

func main() {
	if len(os.Args) == 2 {
		var err error
		switch os.Args[1] {
		case "install":
			err = installService("mackerel-agent", "mackerel agent")
		case "remove":
			err = removeService("mackerel-agent")
		}
		if err != nil {
			log.Fatal(err)
		}
		return
	}

	elog, err := eventlog.Open(name)
	if err != nil {
		log.Fatal(err.Error())
	}
	defer elog.Close()

	// `svc.Run` blocks until windows service will stopped.
	// ref. https://msdn.microsoft.com/library/cc429362.aspx
	err = svc.Run(name, &handler{elog: elog})
	if err != nil {
		log.Fatal(err.Error())
	}
}

type handler struct {
	elog *eventlog.Log
	cmd  *exec.Cmd
}

// ex.
// verbose log: 2017/01/21 22:21:08 command.go:434: DEBUG <command> received 'immediate' chan
// normal log:  2017/01/24 14:14:27 INFO <main> Starting mackerel-agent version:0.36.0
var logRe = regexp.MustCompile(`^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} (?:.+\.go:\d+: )?([A-Z]+) `)

func (h *handler) start() error {
	procAllocConsole.Call()
	dir := execdir()
	cmd := exec.Command(filepath.Join(dir, "mackerel-agent.exe"))
	cmd.SysProcAttr = &syscall.SysProcAttr{
		CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
	}
	cmd.Dir = dir

	h.cmd = cmd
	r, w := io.Pipe()
	cmd.Stderr = w

	br := bufio.NewReader(r)
	lc := make(chan string, 10)
	done := make(chan struct{})

	err := cmd.Start()
	if err != nil {
		return err
	}

	// It need to read data from pipe continuously. And it need to close handle
	// when process finished.
	// read data from pipe. When data arrived at EOL, send line-string to the
	// channel. Also remaining line to EOF.
	go func() {
		defer w.Close()

		// pipe stderr to windows event log
		var body bytes.Buffer
		for {
			b, err := br.ReadByte()
			if err != nil {
				if err != io.EOF {
					h.elog.Error(loggerEid, err.Error())
				}
				break
			}
			if b == '\n' {
				if body.Len() > 0 {
					lc <- body.String()
					body.Reset()
				}
				continue
			}
			// Read size should be 4096 because default size of PIPE is 4096.
			// We know this is not a limit size. This is workaround to avoid
			// that plugin stop writing to pipe.
			if body.Len() < 4096 {
				body.WriteByte(b)
			}
		}
		if body.Len() > 0 {
			lc <- body.String()
		}
		done <- struct{}{}
	}()

	go func() {
		linebuf := []string{}
	loop:
		for {
			select {
			case line := <-lc:
				linebuf = append(linebuf, line)
			case <-time.After(10 * time.Millisecond):
				// When it take 10ms, it is located at end of paragraph. Then
				// slice appended at above should be the paragraph.
				if len(linebuf) > 0 {
					if match := logRe.FindStringSubmatch(linebuf[0]); match != nil {
						line := strings.Join(linebuf, "\n")
						level := match[1]
						switch level {
						case "TRACE", "DEBUG", "INFO":
							h.elog.Info(defaultEid, line)
						case "WARNING":
							h.elog.Warning(defaultEid, line)
						case "ERROR", "CRITICAL":
							h.elog.Error(defaultEid, line)
						default:
							h.elog.Error(loggerEid, line)
						}
					}
					linebuf = nil
				}
				select {
				case <-done:
					break loop
				default:
				}
			}
		}
		close(lc)
		close(done)
	}()
	return nil
}

func interrupt(p *os.Process) error {
	r1, _, err := procGenerateConsoleCtrlEvent.Call(syscall.CTRL_BREAK_EVENT, uintptr(p.Pid))
	if r1 == 0 {
		return err
	}
	return nil
}

func (h *handler) stop() error {
	if h.cmd != nil && h.cmd.Process != nil {
		err := interrupt(h.cmd.Process)
		if err == nil {
			end := time.Now().Add(10 * time.Second)
			for time.Now().Before(end) {
				if h.cmd.ProcessState != nil && h.cmd.ProcessState.Exited() {
					return nil
				}
				time.Sleep(1 * time.Second)
			}
		}
		return h.cmd.Process.Kill()
	}
	return nil
}

// implement https://godoc.org/golang.org/x/sys/windows/svc#Handler
func (h *handler) Execute(args []string, r <-chan svc.ChangeRequest, s chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
	s <- svc.Status{State: svc.StartPending}
	defer func() {
		s <- svc.Status{State: svc.Stopped}
	}()

	if err := h.start(); err != nil {
		h.elog.Error(startEid, err.Error())
		// https://msdn.microsoft.com/library/windows/desktop/ms681383(v=vs.85).aspx
		// use ERROR_SERVICE_SPECIFIC_ERROR
		return true, 1
	}

	exit := make(chan struct{})
	go func() {
		err := h.cmd.Wait()
		// enter when the child process exited
		if err != nil {
			h.elog.Error(stopEid, err.Error())
		}
		exit <- struct{}{}
	}()

	s <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown}
L:
	for {
		select {
		case req := <-r:
			switch req.Cmd {
			case svc.Interrogate:
				s <- req.CurrentStatus
			case svc.Stop, svc.Shutdown:
				s <- svc.Status{State: svc.StopPending, Accepts: svc.AcceptStop | svc.AcceptShutdown}
				if err := h.stop(); err != nil {
					h.elog.Error(stopEid, err.Error())
					s <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown}
				}
			}
		case <-exit:
			break L
		}
	}

	return
}

func execdir() string {
	var wpath [syscall.MAX_PATH]uint16
	r1, _, err := procGetModuleFileName.Call(0, uintptr(unsafe.Pointer(&wpath[0])), uintptr(len(wpath)))
	if r1 == 0 {
		log.Fatal(err)
	}
	return filepath.Dir(syscall.UTF16ToString(wpath[:]))
}
<|endoftext|>