<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"

	"github.com/codegangsta/cli"

	forecast "github.com/mlbright/forecast/v2"
)

const (
	// geocodeHost is the host at which we access the geocoding API.
	// TODO(alex): allow option for HTTPS with key set up.
	geocodeHost = "maps.googleapis.com/"
	// geocodePath is the path at which we access the geocoding API.
	geocodePath = "maps/api/geocode/json"

	// forecastIoEnvKey is the environment variable key that should be set with
	// the forecastIo API key.
	forecastIoEnvKey = "FORECAST_IO_API_KEY"

	// goforecastState is the name of the file that contains the forecast io
	// key.
	goforecastState = ".goforecast"
)

// stateFilePath is where the goforecastState is located.
var stateFilePath = os.Getenv("HOME")

func main() {
	app := setupCliApp()

	if err := app.Run(os.Args); err != nil {
		printError(err)
	}
}

// printError handles the exiting of the program and displaying all errors. No-
// op if error is nil.
func printError(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: "+err.Error())
		os.Exit(1)
	}
}

// setupCliApp initializes a new *cli.App and populates its fields and flags.
func setupCliApp() *cli.App {
	app := cli.NewApp()
	app.Name = "goforecast"
	app.Usage = `goforecast looks up three days of weather based upon a partial
	    address; e.g., a zip code.`
	app.Author = "Alex Toombs"

	populateCommands(app)
	return app
}

// populateCommands sets up all commands on the new command line application.
func populateCommands(app *cli.App) {
	app.Commands = []cli.Command{
		cli.Command{
			Name:        "lookup",
			ShortName:   "l",
			Description: "`lookup` looks up weather for a partial or whole address.",
			Usage:       "lookup \"[address]...\"",
			Action: func(c *cli.Context) {
				if len(c.Args()) == 0 {
					printError(fmt.Errorf("missing address"))
				}

				addr := c.Args().First()
				u, err := buildGeocodingURL("http", parseGeocodingAddr(addr))
				if err != nil {
					printError(err)
				}

				geoResp, err := getGeocodingLocation(http.DefaultClient, u)
				if err != nil {
					printError(err)
				}

				fc, err := getForecast(geoResp.Results[0].Geometry.Location)
				if err != nil {
					printError(err)
				}

				renderForecast(fc, addr)
			},
		},
	}
}

// buildGeocodingURL builds a parsed URL with query values passed in.
func buildGeocodingURL(scheme string, vals url.Values) (*url.URL, error) {
	u := &url.URL{
		Scheme: scheme,
		Host:   geocodeHost,
		Path:   geocodePath,
	}
	if len(vals) != 0 {
		u.RawQuery = vals.Encode()

	}
	return url.Parse(u.String())
}

// parseGeocodingAddr parses a string address into url query values.
func parseGeocodingAddr(addr string) url.Values {
	vals := url.Values{}
	vals.Add("address", url.QueryEscape(addr))
	vals.Add("sensor", "false")
	return vals
}

// getGeocodingLocation attempts to get a valid geocoding response back for the
// entered address.
func getGeocodingLocation(client *http.Client, u *url.URL) (*geocodingResponse, error) {
	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	errStr := "on request: got code %d"
	switch resp.StatusCode {
	case http.StatusOK:
	case http.StatusCreated:
	default:
		return nil, fmt.Errorf(errStr, resp.StatusCode)
	}

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	geoResp := new(geocodingResponse)
	if err := json.Unmarshal(b, &geoResp); err != nil {
		return nil, err
	}

	if len(geoResp.Results) == 0 {
		return nil, fmt.Errorf("no geocoding results returned")
	}
	return geoResp, nil
}

// geocodingResponse is a golang struct that some fields of a geocoding response
// will cleanly unmarshal onto.
type geocodingResponse struct {
	Results []geocodingResult `json:"results"`
}

// geocodingResult is one result from the geocoding lookup.
type geocodingResult struct {
	Geometry *geometry `json:"geometry"`
}

// geometry embeds a ocation.
type geometry struct {
	Location *location `json:"location"`
}

// location is a latitude/longitutde pair.
type location struct {
	Lat float64 `json:"lat"`
	Lng float64 `json:"lng"`
}

// getForecast gets the forecast from forecast.io.
func getForecast(loc *location) (*forecast.Forecast, error) {
	key, err := getForecastIOKey()
	if err != nil {
		return nil, err
	}
	println("got key", key)

	// Record state.
	if err := dumpState(&forecastIoState{
		ApiKey: key,
	}); err != nil {
		return nil, err
	}

	// TODO(alex): allow for multiple time windows and units.
	return forecast.Get(key,
		fmt.Sprintf("%.2f", loc.Lat),
		fmt.Sprintf("%.2f", loc.Lng),
		"now",
		forecast.US,
	)
}

// renderForecast nicely displays the forecast.
// TODO(alex): more information rendered!
// TODO(alex): rendered units should be dynamic.
func renderForecast(fc *forecast.Forecast, addr string) {
	fmt.Printf("Displaying current forecast for %s\n\n", addr)

	fmt.Println("---Currently---")
	fmt.Printf("Summary: %s\n\n", fc.Currently.Summary)
	fmt.Printf("Temperature: %.2f F\n", fc.Currently.Temperature)
	fmt.Printf("Pressure: %.2f kPa\n", fc.Currently.Pressure)
	fmt.Printf("Wind Speed: %.2f mph\n", fc.Currently.WindSpeed)
	fmt.Printf("Precipitation Chance: %.2f%%\n", fc.Currently.PrecipProbability)
}

// forecastIoState is a json struct that we read from/write to disk to keep
// state.
type forecastIoState struct {
	ApiKey string `json:"api_key"`
}

// getForecastIoKey looks up the Forecast IO API key from disk or the user's
// environment.
func getForecastIOKey() (string, error) {
	state, err := restoreState()
	if err != nil && !os.IsNotExist(err) {
		return "", err
	}

	if state != nil && state.ApiKey != "" {
		return state.ApiKey, nil
	}

	// Try environment.
	k := os.Getenv(forecastIoEnvKey)
	if k == "" {
		return "", fmt.Errorf("could not find Forecast IO API key. Please set with \"export %s=<key>\"",
			forecastIoEnvKey)
	}
	return k, nil
}

// restoreState restores state from disk.
func restoreState() (*forecastIoState, error) {
	f, err := os.Open(filepath.Join(stateFilePath, goforecastState))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	b, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, err
	}

	var state *forecastIoState
	if err := json.Unmarshal(b, &state); err != nil {
		return nil, err
	}
	return state, nil
}

// dumpState writes state to disk.
func dumpState(state *forecastIoState) error {
	b, err := json.Marshal(state)
	if err != nil {
		return err
	}

	if err := ioutil.WriteFile(filepath.Join(stateFilePath, goforecastState), b, 0644); err != nil {
		return err
	}
	return nil
}
<commit_msg>Debug statement.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"

	"github.com/codegangsta/cli"

	forecast "github.com/mlbright/forecast/v2"
)

const (
	// geocodeHost is the host at which we access the geocoding API.
	// TODO(alex): allow option for HTTPS with key set up.
	geocodeHost = "maps.googleapis.com/"
	// geocodePath is the path at which we access the geocoding API.
	geocodePath = "maps/api/geocode/json"

	// forecastIoEnvKey is the environment variable key that should be set with
	// the forecastIo API key.
	forecastIoEnvKey = "FORECAST_IO_API_KEY"

	// goforecastState is the name of the file that contains the forecast io
	// key.
	goforecastState = ".goforecast"
)

// stateFilePath is where the goforecastState is located.
var stateFilePath = os.Getenv("HOME")

func main() {
	app := setupCliApp()

	if err := app.Run(os.Args); err != nil {
		printError(err)
	}
}

// printError handles the exiting of the program and displaying all errors. No-
// op if error is nil.
func printError(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: "+err.Error())
		os.Exit(1)
	}
}

// setupCliApp initializes a new *cli.App and populates its fields and flags.
func setupCliApp() *cli.App {
	app := cli.NewApp()
	app.Name = "goforecast"
	app.Usage = `goforecast looks up three days of weather based upon a partial
	    address; e.g., a zip code.`
	app.Author = "Alex Toombs"

	populateCommands(app)
	return app
}

// populateCommands sets up all commands on the new command line application.
func populateCommands(app *cli.App) {
	app.Commands = []cli.Command{
		cli.Command{
			Name:        "lookup",
			ShortName:   "l",
			Description: "`lookup` looks up weather for a partial or whole address.",
			Usage:       "lookup \"[address]...\"",
			Action: func(c *cli.Context) {
				if len(c.Args()) == 0 {
					printError(fmt.Errorf("missing address"))
				}

				addr := c.Args().First()
				u, err := buildGeocodingURL("http", parseGeocodingAddr(addr))
				if err != nil {
					printError(err)
				}

				geoResp, err := getGeocodingLocation(http.DefaultClient, u)
				if err != nil {
					printError(err)
				}

				fc, err := getForecast(geoResp.Results[0].Geometry.Location)
				if err != nil {
					printError(err)
				}

				renderForecast(fc, addr)
			},
		},
	}
}

// buildGeocodingURL builds a parsed URL with query values passed in.
func buildGeocodingURL(scheme string, vals url.Values) (*url.URL, error) {
	u := &url.URL{
		Scheme: scheme,
		Host:   geocodeHost,
		Path:   geocodePath,
	}
	if len(vals) != 0 {
		u.RawQuery = vals.Encode()

	}
	return url.Parse(u.String())
}

// parseGeocodingAddr parses a string address into url query values.
func parseGeocodingAddr(addr string) url.Values {
	vals := url.Values{}
	vals.Add("address", url.QueryEscape(addr))
	vals.Add("sensor", "false")
	return vals
}

// getGeocodingLocation attempts to get a valid geocoding response back for the
// entered address.
func getGeocodingLocation(client *http.Client, u *url.URL) (*geocodingResponse, error) {
	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	errStr := "on request: got code %d"
	switch resp.StatusCode {
	case http.StatusOK:
	case http.StatusCreated:
	default:
		return nil, fmt.Errorf(errStr, resp.StatusCode)
	}

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	geoResp := new(geocodingResponse)
	if err := json.Unmarshal(b, &geoResp); err != nil {
		return nil, err
	}

	if len(geoResp.Results) == 0 {
		return nil, fmt.Errorf("no geocoding results returned")
	}
	return geoResp, nil
}

// geocodingResponse is a golang struct that some fields of a geocoding response
// will cleanly unmarshal onto.
type geocodingResponse struct {
	Results []geocodingResult `json:"results"`
}

// geocodingResult is one result from the geocoding lookup.
type geocodingResult struct {
	Geometry *geometry `json:"geometry"`
}

// geometry embeds a ocation.
type geometry struct {
	Location *location `json:"location"`
}

// location is a latitude/longitutde pair.
type location struct {
	Lat float64 `json:"lat"`
	Lng float64 `json:"lng"`
}

// getForecast gets the forecast from forecast.io.
func getForecast(loc *location) (*forecast.Forecast, error) {
	key, err := getForecastIOKey()
	if err != nil {
		return nil, err
	}

	// Record state.
	if err := dumpState(&forecastIoState{
		ApiKey: key,
	}); err != nil {
		return nil, err
	}

	// TODO(alex): allow for multiple time windows and units.
	return forecast.Get(key,
		fmt.Sprintf("%.2f", loc.Lat),
		fmt.Sprintf("%.2f", loc.Lng),
		"now",
		forecast.US,
	)
}

// renderForecast nicely displays the forecast.
// TODO(alex): more information rendered!
// TODO(alex): rendered units should be dynamic.
func renderForecast(fc *forecast.Forecast, addr string) {
	fmt.Printf("Displaying current forecast for %s\n\n", addr)

	fmt.Println("---Currently---")
	fmt.Printf("Summary: %s\n\n", fc.Currently.Summary)
	fmt.Printf("Temperature: %.2f F\n", fc.Currently.Temperature)
	fmt.Printf("Pressure: %.2f kPa\n", fc.Currently.Pressure)
	fmt.Printf("Wind Speed: %.2f mph\n", fc.Currently.WindSpeed)
	fmt.Printf("Precipitation Chance: %.2f%%\n", fc.Currently.PrecipProbability)
}

// forecastIoState is a json struct that we read from/write to disk to keep
// state.
type forecastIoState struct {
	ApiKey string `json:"api_key"`
}

// getForecastIoKey looks up the Forecast IO API key from disk or the user's
// environment.
func getForecastIOKey() (string, error) {
	state, err := restoreState()
	if err != nil && !os.IsNotExist(err) {
		return "", err
	}

	if state != nil && state.ApiKey != "" {
		return state.ApiKey, nil
	}

	// Try environment.
	k := os.Getenv(forecastIoEnvKey)
	if k == "" {
		return "", fmt.Errorf("could not find Forecast IO API key. Please set with \"export %s=<key>\"",
			forecastIoEnvKey)
	}
	return k, nil
}

// restoreState restores state from disk.
func restoreState() (*forecastIoState, error) {
	f, err := os.Open(filepath.Join(stateFilePath, goforecastState))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	b, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, err
	}

	var state *forecastIoState
	if err := json.Unmarshal(b, &state); err != nil {
		return nil, err
	}
	return state, nil
}

// dumpState writes state to disk.
func dumpState(state *forecastIoState) error {
	b, err := json.Marshal(state)
	if err != nil {
		return err
	}

	if err := ioutil.WriteFile(filepath.Join(stateFilePath, goforecastState), b, 0644); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package main
/*
*  File: gonew_main.go
*  Author: Bryan Matsuo [bmatsuo@soe.ucsc.edu]
*  Created: Sat Jul  2 19:17:53 PDT 2011
*  Usage: gonew [options] */
import (
    "fmt"
    "flag"
    "os"
    "github.com/kr/pretty.go"
    //"io"
    //"log"
)

const (
    DEBUG = true
	DEBUG_LEVEL = 0
)

func parseArgs() Project {
    var (
        usage = "gonew [options] TYPE NAME"
        printUsageHead = func () { fmt.Fprint(os.Stderr, usage, "\n\n") }
        name   string
        ptype  string
        repo   string
        host   string
        target string
        help   bool
    )
    var fs = flag.NewFlagSet("gonew", flag.ExitOnError)
    fs.StringVar(&repo,
        "repo", "git", "Repository type (used only if host not given).")
    fs.StringVar(&host,
        "host", "", "Remote repository remote host if any.")
    fs.StringVar(&target,
        "target", "", "Binary name for 'cmd' projects. Default based on name.")
    fs.BoolVar(&help,
        "help", false, "Show this message.")
    var usageTemp = fs.Usage
    fs.Usage = func () {
        printUsageHead()
        usageTemp()
    }
    fs.Parse(os.Args[1:])
    if help {
        fs.Usage()
    }
    var narg  = fs.NArg()
    if narg < 1 {
        fmt.Fprint(os.Stderr, "missing TYPE argument")
        os.Exit(1)
    }
    if narg < 2 {
        fmt.Fprint(os.Stderr, "mising NAME argument")
        os.Exit(1)
    }
    ptype = fs.Arg(0)
    name = fs.Arg(1)

    if target == "" {
        target = DefaultTarget(name)
    }

    var project = Project{
            Name:name, Target:target, Type: NilProjectType,
            Host:AppConfig.Host, Repo:AppConfig.Repo}
    switch ptype {
    case "cmd":
        project.Type = CmdType
    case "pkg":
        project.Type = PkgType
    default:
        fmt.Fprintf(os.Stderr, "Unknown TYPE %s\n", ptype)
        os.Exit(1)
    }
    switch host {
    case "":
        break
    case "github":
        project.Host = GitHubHost
        project.Repo = GitType
    default:
        fmt.Fprintf(os.Stderr, "Unknown HOST %s\n", host)
        os.Exit(1)
    }
    if project.Host == NilRepoHost {
        switch repo {
        case "":
            break
        case "git":
            project.Repo = GitType
        default:
            fmt.Fprintf(os.Stderr, "Unknown REPO %s\n", repo)
            os.Exit(1)
        }
    }
    // TODO check target for improper characters.
    return project
}

func main() {
	ReadConfig()
    var project = parseArgs()
    fmt.Printf("%v\n", pretty.Formatter(project))
    var errCreate = project.Create()
    if errCreate != nil {
        fmt.Fprintf(os.Stderr, errCreate.String())
        os.Exit(1)
    }
}
<commit_msg>comment edit.<commit_after>package main
/*
*  File: gonew_main.go
*  Author: Bryan Matsuo [bmatsuo@soe.ucsc.edu]
*  Created: Sat Jul  2 19:17:53 PDT 2011
*  Usage: gonew [options]
*/
import (
    "os"
    //"io"
    //"log"
    "fmt"
    "flag"
    "github.com/kr/pretty.go"
)

const (
    DEBUG = true
	DEBUG_LEVEL = 0
)

func parseArgs() Project {
    var (
        usage = "gonew [options] TYPE NAME"
        printUsageHead = func () { fmt.Fprint(os.Stderr, usage, "\n\n") }
        name   string
        ptype  string
        repo   string
        host   string
        target string
        help   bool
    )
    var fs = flag.NewFlagSet("gonew", flag.ExitOnError)
    fs.StringVar(&repo,
        "repo", "git", "Repository type (used only if host not given).")
    fs.StringVar(&host,
        "host", "", "Remote repository remote host if any.")
    fs.StringVar(&target,
        "target", "", "Binary name for 'cmd' projects. Default based on name.")
    fs.BoolVar(&help,
        "help", false, "Show this message.")
    var usageTemp = fs.Usage
    fs.Usage = func () {
        printUsageHead()
        usageTemp()
    }
    fs.Parse(os.Args[1:])
    if help {
        fs.Usage()
    }
    var narg  = fs.NArg()
    if narg < 1 {
        fmt.Fprint(os.Stderr, "missing TYPE argument")
        os.Exit(1)
    }
    if narg < 2 {
        fmt.Fprint(os.Stderr, "mising NAME argument")
        os.Exit(1)
    }
    ptype = fs.Arg(0)
    name = fs.Arg(1)

    if target == "" {
        target = DefaultTarget(name)
    }

    var project = Project{
            Name:name, Target:target, Type: NilProjectType,
            Host:AppConfig.Host, Repo:AppConfig.Repo}
    switch ptype {
    case "cmd":
        project.Type = CmdType
    case "pkg":
        project.Type = PkgType
    default:
        fmt.Fprintf(os.Stderr, "Unknown TYPE %s\n", ptype)
        os.Exit(1)
    }
    switch host {
    case "":
        break
    case "github":
        project.Host = GitHubHost
        project.Repo = GitType
    default:
        fmt.Fprintf(os.Stderr, "Unknown HOST %s\n", host)
        os.Exit(1)
    }
    if project.Host == NilRepoHost {
        switch repo {
        case "":
            break
        case "git":
            project.Repo = GitType
        default:
            fmt.Fprintf(os.Stderr, "Unknown REPO %s\n", repo)
            os.Exit(1)
        }
    }
    // TODO check target for improper characters.
    return project
}

func main() {
	ReadConfig()
    var project = parseArgs()
    fmt.Printf("%v\n", pretty.Formatter(project))
    var errCreate = project.Create()
    if errCreate != nil {
        fmt.Fprintf(os.Stderr, errCreate.String())
        os.Exit(1)
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Datawire. All rights reserved.

// +build ignore

// gotest2tap.go translates `go test -json` on stdin to TAP v13 on
// stdout.
package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"time"
)

// TestEvent is taken verbatim from `go doc test2json`
type TestEvent struct {
	Time    time.Time // encodes as an RFC3339-format string
	Action  string
	Package string
	Test    string
	Elapsed float64 // seconds
	Output  string
}

func main() {
	fmt.Println("TAP version 13")

	testCnt := 0
	bailed := false

	stdin := bufio.NewScanner(os.Stdin)
	for stdin.Scan() {
		var event TestEvent
		if err := json.Unmarshal(stdin.Bytes(), &event); err != nil {
			fmt.Printf("Bail out! Invalid JSON: %v: %q\n", err, stdin.Text())
			bailed = true
		}

		Time := event.Time.Format("2006-01-02T15:04:05.000000000")
		Elapsed := time.Duration(float64(time.Second) * event.Elapsed)
		Output := strings.TrimSuffix(event.Output, "\n")
		if event.Test == "" {
			fmt.Println("#",
				Time,
				"(took "+Elapsed.String()+")",
				fmt.Sprintf("%-6s", event.Action),
				event.Package,
				Output)
		} else {
			Name := event.Package + "." + event.Test
			// TODO(lukeshu): I think maybe this should also handle "bench"?
			switch event.Action {
			case "pass":
				testCnt++
				fmt.Printf("ok %d %v # %v (%v) %v\n", testCnt, Name, Time, Elapsed, Output)
			case "fail":
				testCnt++
				fmt.Printf("not ok %d %v.%v # %v (%v) %v\n", testCnt, Name, Time, Elapsed, Output)
			case "skip":
				testCnt++
				fmt.Printf("ok %d %v.%v # SKIP %v (%v) %v\n", testCnt, Name, Time, Elapsed, Output)
			default:
				fmt.Sprintln("#",
					Time,
					"(took "+Elapsed.String()+")",
					fmt.Sprintf("%-6s", event.Action),
					Name,
					Output)
			}
		}
	}
	if err := stdin.Err(); err != nil {
		fmt.Println("Bail out!", err)
		bailed = true
	}
	if !bailed {
		fmt.Printf("1..%d\n", testCnt)
	}
}
<commit_msg>(apro) build-aux/gotest2tap.go: Properly detect panics as errors<commit_after>// Copyright 2018 Datawire. All rights reserved.

// +build ignore

// gotest2tap.go translates `go test -json` on stdin to TAP v13 on
// stdout.
package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"time"
)

// TestEvent is taken verbatim from `go doc test2json`
type TestEvent struct {
	Time    time.Time // encodes as an RFC3339-format string
	Action  string
	Package string
	Test    string
	Elapsed float64 // seconds
	Output  string
}

func main() {
	fmt.Println("TAP version 13")

	testCnt := 0
	pkgTestCnt := map[string]int{}
	bailed := false

	stdin := bufio.NewScanner(os.Stdin)
	for stdin.Scan() {
		var event TestEvent
		if err := json.Unmarshal(stdin.Bytes(), &event); err != nil {
			fmt.Printf("Bail out! Invalid JSON: %v: %q\n", err, stdin.Text())
			bailed = true
		}

		Time := event.Time.Format("2006-01-02T15:04:05.000000000")
		Elapsed := time.Duration(float64(time.Second) * event.Elapsed)
		Output := strings.TrimSuffix(event.Output, "\n")
		if event.Test == "" {
			if event.Action == "fail" && pkgTestCnt[event.Package] == 0 {
				fmt.Println("Bail out!", event.Package)
				bailed = true
			} else {
				fmt.Println("#",
					Time,
					"(took "+Elapsed.String()+")",
					fmt.Sprintf("%-6s", event.Action),
					event.Package,
					Output)
			}
		} else {
			Name := event.Package + "." + event.Test
			// TODO(lukeshu): I think maybe this should also handle "bench"?
			switch event.Action {
			case "pass":
				testCnt++
				pkgTestCnt[event.Package] = pkgTestCnt[event.Package] + 1
				fmt.Printf("ok %d %v # %v (%v) %v\n", testCnt, Name, Time, Elapsed, Output)
			case "fail":
				testCnt++
				pkgTestCnt[event.Package] = pkgTestCnt[event.Package] + 1
				fmt.Printf("not ok %d %v.%v # %v (%v) %v\n", testCnt, Name, Time, Elapsed, Output)
			case "skip":
				testCnt++
				pkgTestCnt[event.Package] = pkgTestCnt[event.Package] + 1
				fmt.Printf("ok %d %v.%v # SKIP %v (%v) %v\n", testCnt, Name, Time, Elapsed, Output)
			default:
				fmt.Sprintln("#",
					Time,
					"(took "+Elapsed.String()+")",
					fmt.Sprintf("%-6s", event.Action),
					Name,
					Output)
			}
		}
	}
	if err := stdin.Err(); err != nil {
		fmt.Println("Bail out!", err)
		bailed = true
	}
	if !bailed {
		fmt.Printf("1..%d\n", testCnt)
	}
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package vm

import (
	"context"
	"flag"
	"fmt"
	"strings"
	"time"

	"github.com/vmware/govmomi/govc/cli"
	"github.com/vmware/govmomi/govc/flags"
	"github.com/vmware/govmomi/govc/host/esxcli"
	"github.com/vmware/govmomi/object"
)

type ip struct {
	*flags.OutputFlag
	*flags.SearchFlag

	esx  bool
	all  bool
	v4   bool
	wait bool
	nic  string
}

func init() {
	cli.Register("vm.ip", &ip{})
}

func (cmd *ip) Register(ctx context.Context, f *flag.FlagSet) {
	cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
	cmd.OutputFlag.Register(ctx, f)

	cmd.SearchFlag, ctx = flags.NewSearchFlag(ctx, flags.SearchVirtualMachines)
	cmd.SearchFlag.Register(ctx, f)

	f.BoolVar(&cmd.esx, "esxcli", false, "Use esxcli instead of guest tools")
	f.BoolVar(&cmd.all, "a", false, "Wait for an IP address on all NICs")
	f.StringVar(&cmd.nic, "n", "", "Wait for IP address on NIC, specified by device name or MAC")
	f.BoolVar(&cmd.v4, "v4", false, "Only report IPv4 addresses")
	f.BoolVar(&cmd.wait, "wait", true, "Exit only when the VM obtain an IP address")
}

func (cmd *ip) Usage() string {
	return "VM..."
}

func (cmd *ip) Description() string {
	return `List IPs for VM.

By default the vm.ip command depends on vmware-tools to report the 'guest.ipAddress' field and will
wait until it has done so.  This value can also be obtained using:

  govc vm.info -json $vm | jq -r .VirtualMachines[].Guest.IpAddress

When given the '-a' flag, only IP addresses for which there is a corresponding virtual nic are listed.
If there are multiple nics, the listed addresses will be comma delimited.  The '-a' flag depends on
vmware-tools to report the 'guest.net' field and will wait until it has done so for all nics.
Note that this list includes IPv6 addresses if any, use '-v4' to filter them out.  IP addresses reported
by tools for which there is no virtual nic are not included, for example that of the 'docker0' interface.

These values can also be obtained using:

  govc vm.info -json $vm | jq -r .VirtualMachines[].Guest.Net[].IpConfig.IpAddress[].IpAddress

When given the '-n' flag, filters '-a' behavior to the nic specified by MAC address or device name.

The 'esxcli' flag does not require vmware-tools to be installed, but does require the ESX host to
have the /Net/GuestIPHack setting enabled.

Examples:
  govc vm.ip $vm
  govc vm.ip -a -v4 $vm
  govc vm.ip -n 00:0c:29:57:7b:c3 $vm
  govc vm.ip -n ethernet-0 $vm
  govc host.esxcli system settings advanced set -o /Net/GuestIPHack -i 1
  govc vm.ip -esxcli $vm`
}

func (cmd *ip) Process(ctx context.Context) error {
	if err := cmd.OutputFlag.Process(ctx); err != nil {
		return err
	}
	if err := cmd.SearchFlag.Process(ctx); err != nil {
		return err
	}
	return nil
}

func (cmd *ip) Run(ctx context.Context, f *flag.FlagSet) error {
	c, err := cmd.Client()
	if err != nil {
		return err
	}

	vms, err := cmd.VirtualMachines(f.Args())
	if err != nil {
		return err
	}

	var get func(*object.VirtualMachine) (string, error)

	if cmd.esx {
		get = func(vm *object.VirtualMachine) (string, error) {
			guest := esxcli.NewGuestInfo(c)

			ticker := time.NewTicker(time.Millisecond * 500)
			defer ticker.Stop()

			for {
				select {
				case <-ticker.C:
					ip, err := guest.IpAddress(vm)
					if err != nil {
						return "", err
					}

					if ip != "0.0.0.0" || !cmd.wait {
						return ip, nil
					}
				}
			}
		}
	} else {
		var hwaddr []string
		if cmd.nic != "" {
			hwaddr = strings.Split(cmd.nic, ",")
		}

		get = func(vm *object.VirtualMachine) (string, error) {
			if cmd.all || hwaddr != nil {
				macs, err := vm.WaitForNetIP(ctx, cmd.v4, hwaddr...)
				if err != nil {
					return "", err
				}

				var ips []string
				for _, addrs := range macs {
					for _, ip := range addrs {
						ips = append(ips, ip)
					}
				}
				return strings.Join(ips, ","), nil
			}
			return vm.WaitForIP(ctx)
		}
	}

	for _, vm := range vms {
		ip, err := get(vm)
		if err != nil {
			return err
		}

		// TODO(PN): Display inventory path to VM
		fmt.Fprintf(cmd, "%s\n", ip)
	}

	return nil
}
<commit_msg>Use Duration flag for vm.ip -wait flag<commit_after>/*
Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package vm

import (
	"context"
	"flag"
	"fmt"
	"strings"
	"time"

	"github.com/vmware/govmomi/govc/cli"
	"github.com/vmware/govmomi/govc/flags"
	"github.com/vmware/govmomi/govc/host/esxcli"
	"github.com/vmware/govmomi/object"
)

type ip struct {
	*flags.OutputFlag
	*flags.SearchFlag

	esx  bool
	all  bool
	v4   bool
	wait time.Duration
	nic  string
}

func init() {
	cli.Register("vm.ip", &ip{})
}

func (cmd *ip) Register(ctx context.Context, f *flag.FlagSet) {
	cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
	cmd.OutputFlag.Register(ctx, f)

	cmd.SearchFlag, ctx = flags.NewSearchFlag(ctx, flags.SearchVirtualMachines)
	cmd.SearchFlag.Register(ctx, f)

	f.BoolVar(&cmd.esx, "esxcli", false, "Use esxcli instead of guest tools")
	f.BoolVar(&cmd.all, "a", false, "Wait for an IP address on all NICs")
	f.StringVar(&cmd.nic, "n", "", "Wait for IP address on NIC, specified by device name or MAC")
	f.BoolVar(&cmd.v4, "v4", false, "Only report IPv4 addresses")
	f.DurationVar(&cmd.wait, "wait", time.Hour, "Wait time for the VM obtain an IP address")
}

func (cmd *ip) Usage() string {
	return "VM..."
}

func (cmd *ip) Description() string {
	return `List IPs for VM.

By default the vm.ip command depends on vmware-tools to report the 'guest.ipAddress' field and will
wait until it has done so.  This value can also be obtained using:

  govc vm.info -json $vm | jq -r .VirtualMachines[].Guest.IpAddress

When given the '-a' flag, only IP addresses for which there is a corresponding virtual nic are listed.
If there are multiple nics, the listed addresses will be comma delimited.  The '-a' flag depends on
vmware-tools to report the 'guest.net' field and will wait until it has done so for all nics.
Note that this list includes IPv6 addresses if any, use '-v4' to filter them out.  IP addresses reported
by tools for which there is no virtual nic are not included, for example that of the 'docker0' interface.

These values can also be obtained using:

  govc vm.info -json $vm | jq -r .VirtualMachines[].Guest.Net[].IpConfig.IpAddress[].IpAddress

When given the '-n' flag, filters '-a' behavior to the nic specified by MAC address or device name.

The 'esxcli' flag does not require vmware-tools to be installed, but does require the ESX host to
have the /Net/GuestIPHack setting enabled.

The 'wait' flag default to 1hr (original default was infinite).  If a VM does not obtain an IP within
the wait time, the command will still exit with status 0.

Examples:
  govc vm.ip $vm
  govc vm.ip -wait 5m $vm
  govc vm.ip -a -v4 $vm
  govc vm.ip -n 00:0c:29:57:7b:c3 $vm
  govc vm.ip -n ethernet-0 $vm
  govc host.esxcli system settings advanced set -o /Net/GuestIPHack -i 1
  govc vm.ip -esxcli $vm`
}

func (cmd *ip) Process(ctx context.Context) error {
	if err := cmd.OutputFlag.Process(ctx); err != nil {
		return err
	}
	if err := cmd.SearchFlag.Process(ctx); err != nil {
		return err
	}
	return nil
}

func (cmd *ip) Run(ctx context.Context, f *flag.FlagSet) error {
	c, err := cmd.Client()
	if err != nil {
		return err
	}

	vms, err := cmd.VirtualMachines(f.Args())
	if err != nil {
		return err
	}

	var get func(*object.VirtualMachine, context.Context) (string, error)

	if cmd.esx {
		get = func(vm *object.VirtualMachine, deadline context.Context) (string, error) {
			guest := esxcli.NewGuestInfo(c)

			ticker := time.NewTicker(time.Millisecond * 500)
			defer ticker.Stop()

			for {
				select {
				case <-ticker.C:
					ip, err := guest.IpAddress(vm)
					if err != nil {
						return "", err
					}

					if ip != "0.0.0.0" {
						return ip, nil
					}
				case <-deadline.Done():
					return "", nil
				}
			}
		}
	} else {
		var hwaddr []string
		if cmd.nic != "" {
			hwaddr = strings.Split(cmd.nic, ",")
		}

		get = func(vm *object.VirtualMachine, deadline context.Context) (string, error) {
			if cmd.all || hwaddr != nil {
				macs, err := vm.WaitForNetIP(deadline, cmd.v4, hwaddr...)
				if err != nil {
					return "", err
				}

				var ips []string
				for _, addrs := range macs {
					for _, ip := range addrs {
						ips = append(ips, ip)
					}
				}
				return strings.Join(ips, ","), nil
			}
			return vm.WaitForIP(deadline)
		}
	}

	for _, vm := range vms {
		deadline, cancel := context.WithDeadline(ctx, time.Now().Add(cmd.wait))

		ip, err := get(vm, deadline)
		if err != nil {
			if deadline.Err() != context.DeadlineExceeded {
				cancel()
				return err
			}
		}

		cancel()

		if ip == "" {
			continue
		}

		// TODO(PN): Display inventory path to VM
		fmt.Fprintf(cmd, "%s\n", ip)
	}

	return nil
}
<|endoftext|>


<commit_before>package nmea

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

type AttrTest struct {
	Attribute string
	Value     string
}

func TestGPGGAGoodSentence(t *testing.T) {
	goodMsg := "$GPGGA,034225.077,3356.4650,S,15124.5567,E,1,03,9.7,-25.0,M,21.0,M,,0000*51"
	sentence, err := Parse(goodMsg)

	assert.NoError(t, err, "Unexpected error parsing good sentence")

	lat, _ := NewLatLong("3356.4650 S")
	lon, _ := NewLatLong("15124.5567 E")
	// Attributes of the parsed sentence, and their expected values.
	expected := GPGGA{
		Sentence: Sentence{
			Type:     "GPGGA",
			Fields:   []string{"034225.077", "3356.4650", "S", "15124.5567", "E", "1", "03", "9.7", "-25.0", "M", "21.0", "M", "", "0000"},
			Checksum: "51",
			Raw:      "$GPGGA,034225.077,3356.4650,S,15124.5567,E,1,03,9.7,-25.0,M,21.0,M,,0000*51",
		},
		Time:          "034225.077",
		Latitude:      lat,
		Longitude:     lon,
		FixQuality:    GPS,
		NumSatellites: "03",
		HDOP:          "9.7",
		Altitude:      "-25.0",
		Separation:    "21.0",
		DGPSAge:       "",
		DGPSId:        "0000",
	}

	assert.EqualValues(t, expected, sentence, "Sentence values do not match")
}

func TestGPGGABadFixQuality(t *testing.T) {
	// Make sure bad fix mode is detected.
	badMode := "$GPGGA,034225.077,3356.4650,S,15124.5567,E,5,03,9.7,-25.0,M,21.0,M,,0000*55"
	_, err := Parse(badMode)

	assert.Error(t, err, "Parse error not returned")
	assert.Equal(t, err.Error(), "Invalid fix quality [5]", "Error message not as expected")
}
<commit_msg>added more unit test functions for GPGGA sentences<commit_after>package nmea

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestGPGGAGoodSentence(t *testing.T) {
	goodMsg := "$GPGGA,034225.077,3356.4650,S,15124.5567,E,1,03,9.7,-25.0,M,21.0,M,,0000*51"
	sentence, err := Parse(goodMsg)

	assert.NoError(t, err, "Unexpected error parsing good sentence")

	lat, _ := NewLatLong("3356.4650 S")
	lon, _ := NewLatLong("15124.5567 E")
	// Attributes of the parsed sentence, and their expected values.
	expected := GPGGA{
		Sentence: Sentence{
			Type:     "GPGGA",
			Fields:   []string{"034225.077", "3356.4650", "S", "15124.5567", "E", "1", "03", "9.7", "-25.0", "M", "21.0", "M", "", "0000"},
			Checksum: "51",
			Raw:      "$GPGGA,034225.077,3356.4650,S,15124.5567,E,1,03,9.7,-25.0,M,21.0,M,,0000*51",
		},
		Time:          "034225.077",
		Latitude:      lat,
		Longitude:     lon,
		FixQuality:    GPS,
		NumSatellites: "03",
		HDOP:          "9.7",
		Altitude:      "-25.0",
		Separation:    "21.0",
		DGPSAge:       "",
		DGPSId:        "0000",
	}

	assert.EqualValues(t, expected, sentence, "Sentence values do not match")
}

func TestGPGGABadType(t *testing.T) {
	badType := "$GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70"
	s, err := Parse(badType)

	assert.NoError(t, err, "Unexpected error parsing sentence")
	assert.NotEqual(t, "GPGGA", s.GetSentence().Type, "Unexpected sentence type")
}

func TestGPGGABadLatitude(t *testing.T) {
	badLat := "$GPGGA,034225.077,A,S,15124.5567,E,1,03,9.7,-25.0,M,21.0,M,,0000*3A"
	_, err := Parse(badLat)

	assert.Error(t, err, "Parse error not returned")
	assert.Equal(t, "GPGGA decode error: cannot parse [A S], unknown format", err.Error(), "Error message does not match")
}

func TestGPGGABadLongitude(t *testing.T) {
	badLon := "$GPGGA,034225.077,3356.4650,S,A,E,1,03,9.7,-25.0,M,21.0,M,,0000*0C"
	_, err := Parse(badLon)

	assert.Error(t, err, "Parse error not returned")
	assert.Equal(t, "GPGGA decode error: cannot parse [A E], unknown format", err.Error(), "Error message does not match")
}

func TestGPGGABadFixQuality(t *testing.T) {
	// Make sure bad fix mode is detected.
	badMode := "$GPGGA,034225.077,3356.4650,S,15124.5567,E,5,03,9.7,-25.0,M,21.0,M,,0000*55"
	_, err := Parse(badMode)

	assert.Error(t, err, "Parse error not returned")
	assert.Equal(t, err.Error(), "Invalid fix quality [5]", "Error message not as expected")
}
<|endoftext|>


<commit_before>package autoscale

import (
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"golang.org/x/net/context"
)

func TestGroupMonitor(t *testing.T) {
	ogTimeout := groupCheckTimeout
	defer func() {
		groupCheckTimeout = ogTimeout
	}()

	groupCheckTimeout = time.Millisecond

	ctx := context.Background()
	repo := &MockRepository{}

	groups := []Group{
		{Name: "g1"},
		{Name: "g2"},
	}

	repo.On("ListGroups", ctx).Return(groups, nil)

	runList := &MockRunList{}
	for _, g := range groups {
		runList.On("Add", g.Name).Return(nil)
		runList.On("IsRunning", g.Name).Return(false, nil)
	}
	runList.On("Reset").Return(nil)

	gm, err := NewGroupMonitor(ctx, repo, OptionRunList(runList))
	require.NoError(t, err)

	callCount := 0
	wg := sync.WaitGroup{}
	wg.Add(len(groups))
	done := gm.Start(func(groupName string) {
		callCount++
		wg.Done()
	})

	gm.Stop()

	wg.Wait()

	<-done

	require.Equal(t, 2, callCount)
	require.True(t, repo.AssertExpectations(t))
	require.True(t, runList.AssertExpectations(t))
}
<commit_msg>fix timing issue in group monitor<commit_after>package autoscale

import (
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"golang.org/x/net/context"
)

func TestGroupMonitor(t *testing.T) {
	ogTimeout := groupCheckTimeout
	defer func() {
		groupCheckTimeout = ogTimeout
	}()

	groupCheckTimeout = time.Millisecond

	ctx := context.Background()
	repo := &MockRepository{}

	groups := []Group{
		{Name: "g1"},
		{Name: "g2"},
	}

	repo.On("ListGroups", ctx).Return(groups, nil)

	runList := &MockRunList{}
	for _, g := range groups {
		runList.On("Add", g.Name).Return(nil)
		runList.On("IsRunning", g.Name).Return(false, nil).Once()
		runList.On("IsRunning", g.Name).Return(true, nil)
	}
	runList.On("Reset").Return(nil)

	gm, err := NewGroupMonitor(ctx, repo, OptionRunList(runList))
	require.NoError(t, err)

	callCount := 0
	wg := sync.WaitGroup{}
	wg.Add(len(groups))
	done := gm.Start(func(groupName string) {
		callCount++
		wg.Done()
	})

	gm.Stop()

	wg.Wait()

	<-done

	require.Equal(t, 2, callCount)
	require.True(t, repo.AssertExpectations(t))
	require.True(t, runList.AssertExpectations(t))
}
<|endoftext|>


<commit_before>package gochimp3

import "fmt"

const (
	segments_path       = "/lists/%s/segments"
	single_segment_path = segments_path + "/%s"
)

type ListOfSegments struct {
	baseList

	Segments []Segment `json:"segments"`
	ListID   string    `json:"list_id"`
}

type SegmentRequest struct {
	Name          string         `json:"name"`
	StaticSegment []string       `json:"static_segments"`
	Options       SegmentOptions `json:"options"`
}

type Segment struct {
	SegmentRequest

	ID          int `json:"id"`
	MemberCount int    `json:"member_count"`
	Type        string `json:"type"`
	CreatedAt   string `json:"created_at"`
	UpdatedAt   string `json:"updated_at"`
	ListID      string `json:"list_id"`

	withLinks
}

type SegmentOptions struct {
	Match      string               `json:"match"`
	Conditions []SegmentConditional `json:"conditions"`
}

// SegmentConditional represents parameters to filter by
type SegmentConditional struct {
	Type  string  `json:"condition_type"`
	Field string  `json:"field"`
	OP    string  `json:"op"`
	Value []string `json:"value"`
}

type SegmentQueryParams struct {
	ExtendedQueryParams

	Type            string
	SinceCreatedAt  string
	BeforeCreatedAt string
	SinceUpdatedAt  string
	BeforeUpdatedAt string
}

func (q SegmentQueryParams) Params() map[string]string {
	m := q.ExtendedQueryParams.Params()

	m["type"] = q.Type
	m["since_created_at"] = q.SinceCreatedAt
	m["since_updated_at"] = q.SinceUpdatedAt
	m["before_created_at"] = q.BeforeCreatedAt
	m["before_updated_at"] = q.BeforeUpdatedAt

	return m
}

func (list ListResponse) GetSegments(params *SegmentQueryParams) (*ListOfSegments, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(segments_path, list.ID)
	response := new(ListOfSegments)

	return response, list.api.Request("GET", endpoint, params, nil, response)
}

func (list ListResponse) GetSegment(id string, params *BasicQueryParams) (*Segment, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_segment_path, list.ID, id)
	response := new(Segment)

	return response, list.api.Request("GET", endpoint, params, nil, response)
}

func (list ListResponse) CreateSegment(body *SegmentRequest) (*Segment, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(segments_path, list.ID)
	response := new(Segment)

	return response, list.api.Request("POST", endpoint, nil, &body, response)
}

func (list ListResponse) UpdateSegment(id string, body *SegmentRequest) (*Segment, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_segment_path, list.ID, id)
	response := new(Segment)

	return response, list.api.Request("PATCH", endpoint, nil, &body, response)
}

func (list ListResponse) DeleteSegment(id string) (bool, error) {
	if err := list.CanMakeRequest(); err != nil {
		return false, err
	}

	endpoint := fmt.Sprintf(single_segment_path, list.ID, id)
	return list.api.RequestOk("DELETE", endpoint)
}
<commit_msg>Revert "Fix retrieving segments"<commit_after>package gochimp3

import "fmt"

const (
	segments_path       = "/lists/%s/segments"
	single_segment_path = segments_path + "/%s"
)

type ListOfSegments struct {
	baseList

	Segments []Segment `json:"segments"`
	ListID   string    `json:"list_id"`
}

type SegmentRequest struct {
	Name          string         `json:"name"`
	StaticSegment []string       `json:"static_segments"`
	Options       SegmentOptions `json:"options"`
}

type Segment struct {
	SegmentRequest

	ID          string `json:"id"`
	MemberCount int    `json:"member_count"`
	Type        string `json:"type"`
	CreatedAt   string `json:"created_at"`
	UpdatedAt   string `json:"updated_at"`
	ListID      string `json:"list_id"`

	withLinks
}

type SegmentOptions struct {
	Match      string               `json:"match"`
	Conditions []SegmentConditional `json:"conditions"`
}

// SegmentConditional represents parameters to filter by
type SegmentConditional struct {
	Field string  `json:"field"`
	OP    string  `json:"op"`
	Value float64 `json:"value"`
}

type SegmentQueryParams struct {
	ExtendedQueryParams

	Type            string
	SinceCreatedAt  string
	BeforeCreatedAt string
	SinceUpdatedAt  string
	BeforeUpdatedAt string
}

func (q SegmentQueryParams) Params() map[string]string {
	m := q.ExtendedQueryParams.Params()

	m["type"] = q.Type
	m["since_created_at"] = q.SinceCreatedAt
	m["since_updated_at"] = q.SinceUpdatedAt
	m["before_created_at"] = q.BeforeCreatedAt
	m["before_updated_at"] = q.BeforeUpdatedAt

	return m
}

func (list ListResponse) GetSegments(params *SegmentQueryParams) (*ListOfSegments, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(segments_path, list.ID)
	response := new(ListOfSegments)

	return response, list.api.Request("GET", endpoint, params, nil, response)
}

func (list ListResponse) GetSegment(id string, params *BasicQueryParams) (*Segment, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_segment_path, list.ID, id)
	response := new(Segment)

	return response, list.api.Request("GET", endpoint, params, nil, response)
}

func (list ListResponse) CreateSegment(body *SegmentRequest) (*Segment, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(segments_path, list.ID)
	response := new(Segment)

	return response, list.api.Request("POST", endpoint, nil, &body, response)
}

func (list ListResponse) UpdateSegment(id string, body *SegmentRequest) (*Segment, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_segment_path, list.ID, id)
	response := new(Segment)

	return response, list.api.Request("PATCH", endpoint, nil, &body, response)
}

func (list ListResponse) DeleteSegment(id string) (bool, error) {
	if err := list.CanMakeRequest(); err != nil {
		return false, err
	}

	endpoint := fmt.Sprintf(single_segment_path, list.ID, id)
	return list.api.RequestOk("DELETE", endpoint)
}
<|endoftext|>


<commit_before>package cascadia

import (
	"exp/html"
	"fmt"
	"strings"
)

// the Selector type, and functions for creating them

// A Selector is a function which tells whether a node matches or not.
type Selector func(*html.Node) bool

// Compile parses a selector and returns, if successful, a Selector object
// that can be used to match against html.Node objects.
func Compile(sel string) (Selector, error) {
	p := &parser{s: sel}
	compiled, err := p.parseSelectorGroup()
	if err != nil {
		return nil, err
	}

	if p.i < len(sel) {
		return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i)
	}

	return compiled, nil
}

// MustCompile is like Compile, but panics instead of returning an error.
func MustCompile(sel string) Selector {
	compiled, err := Compile(sel)
	if err != nil {
		panic(err)
	}
	return compiled
}

// MatchAll returns a slice of the nodes that match the selector,
// from n and its children.
func (s Selector) MatchAll(n *html.Node) (result []*html.Node) {
	if s(n) {
		result = append(result, n)
	}

	for _, child := range n.Child {
		result = append(result, s.MatchAll(child)...)
	}

	return
}

// Match returns true if the node matches the selector.
func (s Selector) Match(n *html.Node) bool {
	return s(n)
}

// Filter returns the nodes in nodes that match the selector.
func (s Selector) Filter(nodes []*html.Node) (result []*html.Node) {
	for _, n := range nodes {
		if s(n) {
			result = append(result, n)
		}
	}
	return result
}

// typeSelector returns a Selector that matches elements with a given tag name.
func typeSelector(tag string) Selector {
	tag = toLowerASCII(tag)
	return func(n *html.Node) bool {
		return n.Type == html.ElementNode && n.Data == tag
	}
}

// toLowerASCII returns s with all ASCII capital letters lowercased.
func toLowerASCII(s string) string {
	var b []byte
	for i := 0; i < len(s); i++ {
		if c := s[i]; 'A' <= c && c <= 'Z' {
			if b == nil {
				b = make([]byte, len(s))
				copy(b, s)
			}
			b[i] = s[i] + ('a' - 'A')
		}
	}

	if b == nil {
		return s
	}

	return string(b)
}

// attributeSelector returns a Selector that matches elements
// where the attribute named key satisifes the function f.
func attributeSelector(key string, f func(string) bool) Selector {
	key = toLowerASCII(key)
	return func(n *html.Node) bool {
		if n.Type != html.ElementNode {
			return false
		}
		for _, a := range n.Attr {
			if a.Key == key && f(a.Val) {
				return true
			}
		}
		return false
	}
}

// attributeExistsSelector returns a Selector that matches elements that have
// an attribute named key.
func attributeExistsSelector(key string) Selector {
	return attributeSelector(key, func(string) bool { return true })
}

// attributeEqualsSelector returns a Selector that matches elements where
// the attribute named key has the value val.
func attributeEqualsSelector(key, val string) Selector {
	return attributeSelector(key,
		func(s string) bool {
			return s == val
		})
}

// attributeIncludesSelector returns a Selector that matches elements where
// the attribute named key is a whitespace-separated list that includes val.
func attributeIncludesSelector(key, val string) Selector {
	return attributeSelector(key,
		func(s string) bool {
			for s != "" {
				i := strings.IndexAny(s, " \t\r\n\f")
				if i == -1 {
					return s == val
				}
				if s[:i] == val {
					return true
				}
				s = s[i+1:]
			}
			return false
		})
}

// attributeDashmatchSelector returns a Selector that matches elements where
// the attribute named key equals val or starts with val plus a hyphen.
func attributeDashmatchSelector(key, val string) Selector {
	return attributeSelector(key,
		func(s string) bool {
			if s == val {
				return true
			}
			if len(s) <= len(val) {
				return false
			}
			if s[:len(val)] == val && s[len(val)] == '-' {
				return true
			}
			return false
		})
}

// attributePrefixSelector returns a Selector that matches elements where
// the attribute named key starts with val.
func attributePrefixSelector(key, val string) Selector {
	return attributeSelector(key,
		func(s string) bool {
			return strings.HasPrefix(s, val)
		})
}

// attributeSuffixSelector returns a Selector that matches elements where
// the attribute named key ends with val.
func attributeSuffixSelector(key, val string) Selector {
	return attributeSelector(key,
		func(s string) bool {
			return strings.HasSuffix(s, val)
		})
}

// attributeSubstringSelector returns a Selector that matches nodes where
// the attribute named key contains val.
func attributeSubstringSelector(key, val string) Selector {
	return attributeSelector(key,
		func(s string) bool {
			return strings.Contains(s, val)
		})
}

// intersectionSelector returns a selector that matches nodes that match
// both a and b.
func intersectionSelector(a, b Selector) Selector {
	return func(n *html.Node) bool {
		return a(n) && b(n)
	}
}

// unionSelector returns a selector that matches elements that match
// either a or b.
func unionSelector(a, b Selector) Selector {
	return func(n *html.Node) bool {
		return a(n) || b(n)
	}
}

// negatedSelector returns a selector that matches elements that do not match a.
func negatedSelector(a Selector) Selector {
	return func(n *html.Node) bool {
		if n.Type != html.ElementNode {
			return false
		}
		return !a(n)
	}
}

// nthChildSelector returns a selector that implements :nth-child(an+b).
// If last is true, implements :nth-last-child instead.
// If ofType is true, implements :nth-of-type instead.
func nthChildSelector(a, b int, last, ofType bool) Selector {
	return func(n *html.Node) bool {
		if n.Type != html.ElementNode {
			return false
		}

		parent := n.Parent
		if parent == nil {
			return false
		}

		i := -1
		count := 0
		for _, c := range parent.Child {
			if (c.Type != html.ElementNode) || (ofType && c.Data != n.Data) {
				continue
			}
			count++
			if c == n {
				i = count
				if !last {
					break
				}
			}
		}

		if i == -1 {
			// This shouldn't happen, since n should always be one of its parent's children.
			return false
		}

		if last {
			i = count - i + 1
		}

		i -= b
		if a == 0 {
			return i == 0
		}

		return i%a == 0 && i/a >= 0
	}
}

// onlyChildSelector returns a selector that implements :only-child.
// If ofType is true, it implements :only-of-type instead.
func onlyChildSelector(ofType bool) Selector {
	return func(n *html.Node) bool {
		if n.Type != html.ElementNode {
			return false
		}

		parent := n.Parent
		if parent == nil {
			return false
		}

		count := 0
		for _, c := range parent.Child {
			if (c.Type != html.ElementNode) || (ofType && c.Data != n.Data) {
				continue
			}
			count++
			if count > 1 {
				return false
			}
		}

		return count == 1
	}
}

// emptyElementSelector is a Selector that matches empty elements.
func emptyElementSelector(n *html.Node) bool {
	if n.Type != html.ElementNode {
		return false
	}

	for _, c := range n.Child {
		switch c.Type {
		case html.ElementNode, html.TextNode:
			return false
		}
	}

	return true
}

// descendantSelector returns a Selector that matches an element if
// it matches d and has an ancestor that matches a.
func descendantSelector(a, d Selector) Selector {
	return func(n *html.Node) bool {
		if !d(n) {
			return false
		}

		for p := n.Parent; p != nil; p = p.Parent {
			if a(p) {
				return true
			}
		}

		return false
	}
}

// childSelector returns a Selector that matches an element if
// it matches d and its parent matches a.
func childSelector(a, d Selector) Selector {
	return func(n *html.Node) bool {
		return d(n) && n.Parent != nil && a(n.Parent)
	}
}

// siblingSelector returns a Selector that matches an element
// if it matches s2 and in is preceded by an element that matches s1.
// If adjacent is true, the sibling must be immediately before the element.
func siblingSelector(s1, s2 Selector, adjacent bool) Selector {
	return func(n *html.Node) bool {
		if !s2(n) {
			return false
		}

		p := n.Parent
		if p == nil {
			return false
		}
		c := p.Child
		var i int
		for i = 0; i < len(c); i++ {
			if c[i] == n {
				break
			}
		}

		if i == len(c) {
			return false
		}
		for j := i - 1; j >= 0; j-- {
			s := c[j]
			if s.Type == html.ElementNode {
				if s1(s) {
					return true
				}
				if adjacent {
					// Only test the first sibling we find if adjacent is true.
					return false
				}
			}
		}

		return false
	}
}
<commit_msg>Update for change in exp/html to use linked list of child nodes.<commit_after>package cascadia

import (
	"exp/html"
	"fmt"
	"strings"
)

// the Selector type, and functions for creating them

// A Selector is a function which tells whether a node matches or not.
type Selector func(*html.Node) bool

// Compile parses a selector and returns, if successful, a Selector object
// that can be used to match against html.Node objects.
func Compile(sel string) (Selector, error) {
	p := &parser{s: sel}
	compiled, err := p.parseSelectorGroup()
	if err != nil {
		return nil, err
	}

	if p.i < len(sel) {
		return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i)
	}

	return compiled, nil
}

// MustCompile is like Compile, but panics instead of returning an error.
func MustCompile(sel string) Selector {
	compiled, err := Compile(sel)
	if err != nil {
		panic(err)
	}
	return compiled
}

// MatchAll returns a slice of the nodes that match the selector,
// from n and its children.
func (s Selector) MatchAll(n *html.Node) (result []*html.Node) {
	if s(n) {
		result = append(result, n)
	}

	for child := n.FirstChild; child != nil; child = child.NextSibling {
		result = append(result, s.MatchAll(child)...)
	}

	return
}

// Match returns true if the node matches the selector.
func (s Selector) Match(n *html.Node) bool {
	return s(n)
}

// Filter returns the nodes in nodes that match the selector.
func (s Selector) Filter(nodes []*html.Node) (result []*html.Node) {
	for _, n := range nodes {
		if s(n) {
			result = append(result, n)
		}
	}
	return result
}

// typeSelector returns a Selector that matches elements with a given tag name.
func typeSelector(tag string) Selector {
	tag = toLowerASCII(tag)
	return func(n *html.Node) bool {
		return n.Type == html.ElementNode && n.Data == tag
	}
}

// toLowerASCII returns s with all ASCII capital letters lowercased.
func toLowerASCII(s string) string {
	var b []byte
	for i := 0; i < len(s); i++ {
		if c := s[i]; 'A' <= c && c <= 'Z' {
			if b == nil {
				b = make([]byte, len(s))
				copy(b, s)
			}
			b[i] = s[i] + ('a' - 'A')
		}
	}

	if b == nil {
		return s
	}

	return string(b)
}

// attributeSelector returns a Selector that matches elements
// where the attribute named key satisifes the function f.
func attributeSelector(key string, f func(string) bool) Selector {
	key = toLowerASCII(key)
	return func(n *html.Node) bool {
		if n.Type != html.ElementNode {
			return false
		}
		for _, a := range n.Attr {
			if a.Key == key && f(a.Val) {
				return true
			}
		}
		return false
	}
}

// attributeExistsSelector returns a Selector that matches elements that have
// an attribute named key.
func attributeExistsSelector(key string) Selector {
	return attributeSelector(key, func(string) bool { return true })
}

// attributeEqualsSelector returns a Selector that matches elements where
// the attribute named key has the value val.
func attributeEqualsSelector(key, val string) Selector {
	return attributeSelector(key,
		func(s string) bool {
			return s == val
		})
}

// attributeIncludesSelector returns a Selector that matches elements where
// the attribute named key is a whitespace-separated list that includes val.
func attributeIncludesSelector(key, val string) Selector {
	return attributeSelector(key,
		func(s string) bool {
			for s != "" {
				i := strings.IndexAny(s, " \t\r\n\f")
				if i == -1 {
					return s == val
				}
				if s[:i] == val {
					return true
				}
				s = s[i+1:]
			}
			return false
		})
}

// attributeDashmatchSelector returns a Selector that matches elements where
// the attribute named key equals val or starts with val plus a hyphen.
func attributeDashmatchSelector(key, val string) Selector {
	return attributeSelector(key,
		func(s string) bool {
			if s == val {
				return true
			}
			if len(s) <= len(val) {
				return false
			}
			if s[:len(val)] == val && s[len(val)] == '-' {
				return true
			}
			return false
		})
}

// attributePrefixSelector returns a Selector that matches elements where
// the attribute named key starts with val.
func attributePrefixSelector(key, val string) Selector {
	return attributeSelector(key,
		func(s string) bool {
			return strings.HasPrefix(s, val)
		})
}

// attributeSuffixSelector returns a Selector that matches elements where
// the attribute named key ends with val.
func attributeSuffixSelector(key, val string) Selector {
	return attributeSelector(key,
		func(s string) bool {
			return strings.HasSuffix(s, val)
		})
}

// attributeSubstringSelector returns a Selector that matches nodes where
// the attribute named key contains val.
func attributeSubstringSelector(key, val string) Selector {
	return attributeSelector(key,
		func(s string) bool {
			return strings.Contains(s, val)
		})
}

// intersectionSelector returns a selector that matches nodes that match
// both a and b.
func intersectionSelector(a, b Selector) Selector {
	return func(n *html.Node) bool {
		return a(n) && b(n)
	}
}

// unionSelector returns a selector that matches elements that match
// either a or b.
func unionSelector(a, b Selector) Selector {
	return func(n *html.Node) bool {
		return a(n) || b(n)
	}
}

// negatedSelector returns a selector that matches elements that do not match a.
func negatedSelector(a Selector) Selector {
	return func(n *html.Node) bool {
		if n.Type != html.ElementNode {
			return false
		}
		return !a(n)
	}
}

// nthChildSelector returns a selector that implements :nth-child(an+b).
// If last is true, implements :nth-last-child instead.
// If ofType is true, implements :nth-of-type instead.
func nthChildSelector(a, b int, last, ofType bool) Selector {
	return func(n *html.Node) bool {
		if n.Type != html.ElementNode {
			return false
		}

		parent := n.Parent
		if parent == nil {
			return false
		}

		i := -1
		count := 0
		for c := parent.FirstChild; c != nil; c = c.NextSibling {
			if (c.Type != html.ElementNode) || (ofType && c.Data != n.Data) {
				continue
			}
			count++
			if c == n {
				i = count
				if !last {
					break
				}
			}
		}

		if i == -1 {
			// This shouldn't happen, since n should always be one of its parent's children.
			return false
		}

		if last {
			i = count - i + 1
		}

		i -= b
		if a == 0 {
			return i == 0
		}

		return i%a == 0 && i/a >= 0
	}
}

// onlyChildSelector returns a selector that implements :only-child.
// If ofType is true, it implements :only-of-type instead.
func onlyChildSelector(ofType bool) Selector {
	return func(n *html.Node) bool {
		if n.Type != html.ElementNode {
			return false
		}

		parent := n.Parent
		if parent == nil {
			return false
		}

		count := 0
		for c := parent.FirstChild; c != nil; c = c.NextSibling {
			if (c.Type != html.ElementNode) || (ofType && c.Data != n.Data) {
				continue
			}
			count++
			if count > 1 {
				return false
			}
		}

		return count == 1
	}
}

// emptyElementSelector is a Selector that matches empty elements.
func emptyElementSelector(n *html.Node) bool {
	if n.Type != html.ElementNode {
		return false
	}

	for c := n.FirstChild; c != nil; c = c.NextSibling {
		switch c.Type {
		case html.ElementNode, html.TextNode:
			return false
		}
	}

	return true
}

// descendantSelector returns a Selector that matches an element if
// it matches d and has an ancestor that matches a.
func descendantSelector(a, d Selector) Selector {
	return func(n *html.Node) bool {
		if !d(n) {
			return false
		}

		for p := n.Parent; p != nil; p = p.Parent {
			if a(p) {
				return true
			}
		}

		return false
	}
}

// childSelector returns a Selector that matches an element if
// it matches d and its parent matches a.
func childSelector(a, d Selector) Selector {
	return func(n *html.Node) bool {
		return d(n) && n.Parent != nil && a(n.Parent)
	}
}

// siblingSelector returns a Selector that matches an element
// if it matches s2 and in is preceded by an element that matches s1.
// If adjacent is true, the sibling must be immediately before the element.
func siblingSelector(s1, s2 Selector, adjacent bool) Selector {
	return func(n *html.Node) bool {
		if !s2(n) {
			return false
		}

		if adjacent {
			if n.PrevSibling == nil {
				return false
			}
			return s1(n.PrevSibling)
		}

		// Walk backwards looking for element that matches s1
		for c := n.PrevSibling; c != nil; c = c.PrevSibling {
			if s1(c) {
				return true
			}
		}

		return false
	}
}
<|endoftext|>


<commit_before>package integration

import (
	"os/exec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("Help Flag", func() {
	Context("when cf is run without providing a command or a flag", func() {
		var (
			commandErr error
			session    *Session
		)

		BeforeEach(func() {
			command := exec.Command("cf")
			session, commandErr = Start(command, GinkgoWriter, GinkgoWriter)
		})

		It("displays the help text", func() {
			Expect(commandErr).NotTo(HaveOccurred())
			Eventually(session).Should(Exit(0))
			Eventually(session).Should(Say("NAME:"))
			Eventually(session).Should(Say("USAGE:"))
			Eventually(session).Should(Say("VERSION:"))
			Eventually(session).Should(Say("GETTING STARTED:"))
			Eventually(session).Should(Say("ENVIRONMENT VARIABLES:"))
			Eventually(session).Should(Say("GLOBAL OPTIONS:"))
		})
	})

	Context("when cf is run with -h flag alone", func() {
		var (
			commandErr error
			session    *Session
		)

		BeforeEach(func() {
			command := exec.Command("cf", "-h")
			session, commandErr = Start(command, GinkgoWriter, GinkgoWriter)
		})

		It("displays the help text", func() {
			Expect(commandErr).NotTo(HaveOccurred())
			Eventually(session).Should(Exit(0))
			Eventually(session).Should(Say("NAME:"))
			Eventually(session).Should(Say("USAGE:"))
			Eventually(session).Should(Say("VERSION:"))
			Eventually(session).Should(Say("GETTING STARTED:"))
			Eventually(session).Should(Say("ENVIRONMENT VARIABLES:"))
			Eventually(session).Should(Say("GLOBAL OPTIONS:"))
		})
	})

	Context("when cf is run with -h flag on a command", func() {
		var (
			commandErr error
			session    *Session
		)

		BeforeEach(func() {
			command := exec.Command("cf", "app", "-h")
			session, commandErr = Start(command, GinkgoWriter, GinkgoWriter)
		})

		It("displays the help text", func() {
			Expect(commandErr).NotTo(HaveOccurred())
			Eventually(session).Should(Exit(0))
			Eventually(session).Should(Say("NAME:"))
			Eventually(session).Should(Say("app - Display health and status for ap"))
			Eventually(session).Should(Say("USAGE:"))
			Eventually(session).Should(Say("OPTIONS:"))
		})
	})
})
<commit_msg>test refactor<commit_after>package integration

import (
	"os/exec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("Help Flag", func() {
	DescribeTable("displays the help text",
		func(setup func() *exec.Cmd) {
			cmd := setup()
			session, err := Start(cmd, GinkgoWriter, GinkgoWriter)
			Expect(err).NotTo(HaveOccurred())

			Eventually(session).Should(Say("NAME:"))
			Eventually(session).Should(Say("USAGE:"))
			Eventually(session).Should(Say("VERSION:"))
			Eventually(session).Should(Say("GETTING STARTED:"))
			Eventually(session).Should(Say("ENVIRONMENT VARIABLES:"))
			Eventually(session).Should(Say("GLOBAL OPTIONS:"))
			Eventually(session).Should(Exit(0))
		},

		Entry("when cf is run without providing a command or a flag", func() *exec.Cmd {
			return exec.Command("cf")
		}),

		Entry("when cf is run with -h flag alone", func() *exec.Cmd {
			return exec.Command("cf", "-h")
		}),

		Entry("when cf is run with --help flag alone", func() *exec.Cmd {
			return exec.Command("cf", "--help")
		}),
	)

	Context("when cf is run with -h flag on a command", func() {
		It("displays the help text", func() {
			command := exec.Command("cf", "app", "-h")
			session, err := Start(command, GinkgoWriter, GinkgoWriter)
			Expect(err).NotTo(HaveOccurred())

			Eventually(session).Should(Say("NAME:"))
			Eventually(session).Should(Say("app - Display health and status for ap"))
			Eventually(session).Should(Say("USAGE:"))
			Eventually(session).Should(Say("OPTIONS:"))
			Eventually(session).Should(Exit(0))
		})
	})
})
<|endoftext|>


<commit_before>// Copyright 2016 Mender Software AS
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.

package integration

import (
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestGetDeviceInventory(t *testing.T) {

	t.Parallel()

	testCases := map[string]struct {
		// Input
		Code int
		Body interface{}

		//Output
		Device *Device
		Err    error
	}{
		"random server error": {
			Code: http.StatusUnavailableForLegalReasons,
			Err:  errors.New("error server response: parsing server error response: EOF"),
		},
		"internal server error with payload": {
			Code: http.StatusInternalServerError,
			Body: struct {
				Error string `json:"error"`
			}{Error: "dead db"},

			Err: errors.New("error server response: dead db"),
		},
		"not found": {
			Code: http.StatusNotFound,
		},
		"success - broken payload": {
			Code: http.StatusOK,
			Body: &Device{},

			Err: errors.New("validating server response: ID: non zero value required;"),
		},
		"success": {
			Code: http.StatusOK,
			Body: &Device{
				ID:      "lalala",
				Updated: time.Unix(10, 10),
				Attributes: []*Attribute{
					&Attribute{
						Name:        "sialalala",
						Description: "lala",
						Value:       "something",
					},
				},
			},

			Device: &Device{
				ID:      "lalala",
				Updated: time.Unix(10, 10),
				Attributes: []*Attribute{
					&Attribute{
						Name:        "sialalala",
						Description: "lala",
						Value:       "something",
					},
				},
			},
		},
	}

	for caseName, test := range testCases {

		t.Logf("Case: %s\n", caseName)

		ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(test.Code)
			if test.Body != nil {
				payload, err := json.Marshal(test.Body)
				assert.NoError(t, err, "invalid test")

				_, err = w.Write(payload)
				assert.NoError(t, err, "invalid test")
			}
		}))
		defer ts.Close()

		api, err := NewMenderAPI(ts.URL)
		assert.NoError(t, err, "api client init")

		device, err := api.GetDeviceInventory(context.TODO(), DeviceID("whatever"))

		if test.Err != nil {
			assert.EqualError(t, err, test.Err.Error())
		} else {
			assert.NoError(t, err)
		}
		assert.Equal(t, test.Device, device)
	}

}
<commit_msg>integration: update tests to work with go 1.8<commit_after>// Copyright 2016 Mender Software AS
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.

package integration

import (
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestGetDeviceInventory(t *testing.T) {

	t.Parallel()

	testCases := map[string]struct {
		// Input
		Code int
		Body interface{}

		//Output
		Device *Device
		Err    error
	}{
		"random server error": {
			Code: http.StatusUnavailableForLegalReasons,
			Err:  errors.New("error server response: parsing server error response: EOF"),
		},
		"internal server error with payload": {
			Code: http.StatusInternalServerError,
			Body: struct {
				Error string `json:"error"`
			}{Error: "dead db"},

			Err: errors.New("error server response: dead db"),
		},
		"not found": {
			Code: http.StatusNotFound,
		},
		"success - broken payload": {
			Code: http.StatusOK,
			Body: &Device{},

			Err: errors.New("validating server response: ID: non zero value required;Updated: non zero value required;"),
		},
		"success": {
			Code: http.StatusOK,
			Body: &Device{
				ID:      "lalala",
				Updated: time.Unix(10, 10),
				Attributes: []*Attribute{
					&Attribute{
						Name:        "sialalala",
						Description: "lala",
						Value:       "something",
					},
				},
			},

			Device: &Device{
				ID:      "lalala",
				Updated: time.Unix(10, 10),
				Attributes: []*Attribute{
					&Attribute{
						Name:        "sialalala",
						Description: "lala",
						Value:       "something",
					},
				},
			},
		},
	}

	for caseName, test := range testCases {

		t.Logf("Case: %s\n", caseName)

		ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(test.Code)
			if test.Body != nil {
				payload, err := json.Marshal(test.Body)
				assert.NoError(t, err, "invalid test")

				_, err = w.Write(payload)
				assert.NoError(t, err, "invalid test")
			}
		}))
		defer ts.Close()

		api, err := NewMenderAPI(ts.URL)
		assert.NoError(t, err, "api client init")

		device, err := api.GetDeviceInventory(context.TODO(), DeviceID("whatever"))

		if test.Err != nil {
			assert.EqualError(t, err, test.Err.Error())
		} else {
			assert.NoError(t, err)
		}
		assert.Equal(t, test.Device, device)
	}

}
<|endoftext|>


<commit_before>package auth

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

	"github.com/gempir/gempbot/internal/api"
	"github.com/gempir/gempbot/internal/config"
	"github.com/gempir/gempbot/internal/helixclient"
	"github.com/gempir/gempbot/internal/log"
	"github.com/gempir/gempbot/internal/store"
	"github.com/golang-jwt/jwt"
	"github.com/nicklaw5/helix/v2"
)

func CreateApiToken(secret string, resp *helix.ValidateTokenResponse) string {
	expirationTime := time.Now().Add(365 * 24 * time.Hour)
	claims := &TokenClaims{
		UserID: resp.Data.UserID,
		Login:  resp.Data.Login,
		StandardClaims: jwt.StandardClaims{
			// In JWT, the expiry time is expressed as unix milliseconds
			ExpiresAt: expirationTime.Unix(),
		},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, _ := token.SignedString([]byte(secret))

	return tokenString
}

type TokenClaims struct {
	UserID         string
	Login          string
	StandardClaims jwt.StandardClaims
}

func (t *TokenClaims) Valid() error {
	return nil
}

func NewAuth(cfg *config.Config, db *store.Database, helixClient helixclient.Client) *Auth {
	return &Auth{
		cfg:         cfg,
		db:          db,
		helixClient: helixClient,
	}
}

type Auth struct {
	helixClient helixclient.Client
	db          *store.Database
	cfg         *config.Config
}

func (a *Auth) AttemptAuth(r *http.Request, w http.ResponseWriter) (helix.ValidateTokenResponse, store.UserAccessToken, api.Error) {
	resp, token, err := a.Authenticate(r)
	if err != nil {
		a.WriteDeleteCookieResponse(w, err)
		return helix.ValidateTokenResponse{}, store.UserAccessToken{}, err
	}

	return resp, token, nil
}

func (a *Auth) CanAuthenticate(r *http.Request) bool {
	scToken := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
	if scToken != "" {
		return true
	}

	for _, cookie := range r.Cookies() {
		if cookie.Name == "scToken" {
			return true
		}
	}

	return false
}

func (a *Auth) Authenticate(r *http.Request) (helix.ValidateTokenResponse, store.UserAccessToken, api.Error) {
	scToken := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
	for _, cookie := range r.Cookies() {
		if cookie.Name == "scToken" {
			scToken = cookie.Value
		}
	}

	if scToken == "" {
		return helix.ValidateTokenResponse{}, store.UserAccessToken{}, api.NewApiError(http.StatusUnauthorized, fmt.Errorf("no scToken cookie set"))
	}

	// Initialize a new instance of `Claims`
	claims := &TokenClaims{}

	// Parse the JWT string and store the result in `claims`.
	// Note that we are passing the key in this method as well. This method will return an error
	// if the token is invalid (if it has expired according to the expiry time we set on sign in),
	// or if the signature does not match
	tkn, err := jwt.ParseWithClaims(scToken, claims, func(token *jwt.Token) (interface{}, error) {
		return []byte(a.cfg.Secret), nil
	})
	if err != nil || !tkn.Valid {
		log.Errorf("found to validate jwt: %s", err)
		return helix.ValidateTokenResponse{}, store.UserAccessToken{}, api.NewApiError(http.StatusUnauthorized, fmt.Errorf("bad authentication"))
	}

	token, err := a.db.GetUserAccessToken(claims.UserID)
	if err != nil {
		log.Errorf("Failed to get userAccessTokenData: %s", err.Error())
		return helix.ValidateTokenResponse{}, store.UserAccessToken{}, api.NewApiError(http.StatusUnauthorized, fmt.Errorf("failed to get userAccessTokenData: %s", err.Error()))
	}

	success, resp, err := a.helixClient.ValidateToken(token.AccessToken)
	if !success || err != nil {
		if err != nil {
			log.Errorf("token did not validate: %s", err)
		}

		// Token might be expired, let's try refreshing
		if resp.Error == "Unauthorized" {
			err := a.helixClient.RefreshToken(token)
			if err != nil {
				return helix.ValidateTokenResponse{}, store.UserAccessToken{}, api.NewApiError(http.StatusUnauthorized, fmt.Errorf("failed to refresh token"))
			}

			refreshedToken, err := a.db.GetUserAccessToken(claims.UserID)
			if err != nil {
				log.Errorf("Failed to get userAccessTokenData: %s", err.Error())
				return helix.ValidateTokenResponse{}, store.UserAccessToken{}, api.NewApiError(http.StatusUnauthorized, fmt.Errorf("failed to get userAccessTokenData: %s", err.Error()))
			}

			success, resp, err = a.helixClient.ValidateToken(refreshedToken.AccessToken)
			if !success || err != nil {
				if err != nil {
					log.Errorf("refreshed Token did not validate: %s", err)
				}

				return helix.ValidateTokenResponse{}, refreshedToken, api.NewApiError(http.StatusUnauthorized, fmt.Errorf("refreshed token did not validate"))
			}

			return *resp, refreshedToken, nil
		}

		return helix.ValidateTokenResponse{}, store.UserAccessToken{}, api.NewApiError(http.StatusUnauthorized, fmt.Errorf("token not valid: %s", resp.ErrorMessage))
	}

	return *resp, token, nil
}

func (a *Auth) WriteDeleteCookieResponse(w http.ResponseWriter, err api.Error) {
	cookie := &http.Cookie{
		Name:     "scToken",
		Value:    "",
		Path:     "/",
		Domain:   a.cfg.CookieDomain,
		MaxAge:   -1,
		HttpOnly: true,
	}

	http.SetCookie(w, cookie)
	http.Error(w, err.Error(), err.Status())
}
<commit_msg>better check for potential refreshes<commit_after>package auth

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

	"github.com/gempir/gempbot/internal/api"
	"github.com/gempir/gempbot/internal/config"
	"github.com/gempir/gempbot/internal/helixclient"
	"github.com/gempir/gempbot/internal/log"
	"github.com/gempir/gempbot/internal/store"
	"github.com/golang-jwt/jwt"
	"github.com/nicklaw5/helix/v2"
)

func CreateApiToken(secret string, resp *helix.ValidateTokenResponse) string {
	expirationTime := time.Now().Add(365 * 24 * time.Hour)
	claims := &TokenClaims{
		UserID: resp.Data.UserID,
		Login:  resp.Data.Login,
		StandardClaims: jwt.StandardClaims{
			// In JWT, the expiry time is expressed as unix milliseconds
			ExpiresAt: expirationTime.Unix(),
		},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, _ := token.SignedString([]byte(secret))

	return tokenString
}

type TokenClaims struct {
	UserID         string
	Login          string
	StandardClaims jwt.StandardClaims
}

func (t *TokenClaims) Valid() error {
	return nil
}

func NewAuth(cfg *config.Config, db *store.Database, helixClient helixclient.Client) *Auth {
	return &Auth{
		cfg:         cfg,
		db:          db,
		helixClient: helixClient,
	}
}

type Auth struct {
	helixClient helixclient.Client
	db          *store.Database
	cfg         *config.Config
}

func (a *Auth) AttemptAuth(r *http.Request, w http.ResponseWriter) (helix.ValidateTokenResponse, store.UserAccessToken, api.Error) {
	resp, token, err := a.Authenticate(r)
	if err != nil {
		a.WriteDeleteCookieResponse(w, err)
		return helix.ValidateTokenResponse{}, store.UserAccessToken{}, err
	}

	return resp, token, nil
}

func (a *Auth) CanAuthenticate(r *http.Request) bool {
	scToken := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
	if scToken != "" {
		return true
	}

	for _, cookie := range r.Cookies() {
		if cookie.Name == "scToken" {
			return true
		}
	}

	return false
}

func (a *Auth) Authenticate(r *http.Request) (helix.ValidateTokenResponse, store.UserAccessToken, api.Error) {
	scToken := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
	for _, cookie := range r.Cookies() {
		if cookie.Name == "scToken" {
			scToken = cookie.Value
		}
	}

	if scToken == "" {
		return helix.ValidateTokenResponse{}, store.UserAccessToken{}, api.NewApiError(http.StatusUnauthorized, fmt.Errorf("no scToken cookie set"))
	}

	// Initialize a new instance of `Claims`
	claims := &TokenClaims{}

	// Parse the JWT string and store the result in `claims`.
	// Note that we are passing the key in this method as well. This method will return an error
	// if the token is invalid (if it has expired according to the expiry time we set on sign in),
	// or if the signature does not match
	tkn, err := jwt.ParseWithClaims(scToken, claims, func(token *jwt.Token) (interface{}, error) {
		return []byte(a.cfg.Secret), nil
	})
	if err != nil || !tkn.Valid {
		log.Errorf("found to validate jwt: %s", err)
		return helix.ValidateTokenResponse{}, store.UserAccessToken{}, api.NewApiError(http.StatusUnauthorized, fmt.Errorf("bad authentication"))
	}

	token, err := a.db.GetUserAccessToken(claims.UserID)
	if err != nil {
		log.Errorf("Failed to get userAccessTokenData: %s", err.Error())
		return helix.ValidateTokenResponse{}, store.UserAccessToken{}, api.NewApiError(http.StatusUnauthorized, fmt.Errorf("failed to get userAccessTokenData: %s", err.Error()))
	}

	success, resp, err := a.helixClient.ValidateToken(token.AccessToken)
	if !success || err != nil {
		if err != nil {
			log.Errorf("token did not validate: %s", err)
		}

		// Token might be expired, let's try refreshing
		if resp.Error == "Unauthorized" || resp.ErrorMessage == "invalid access token" {
			err := a.helixClient.RefreshToken(token)
			if err != nil {
				return helix.ValidateTokenResponse{}, store.UserAccessToken{}, api.NewApiError(http.StatusUnauthorized, fmt.Errorf("failed to refresh token"))
			}

			refreshedToken, err := a.db.GetUserAccessToken(claims.UserID)
			if err != nil {
				log.Errorf("Failed to get userAccessTokenData: %s", err.Error())
				return helix.ValidateTokenResponse{}, store.UserAccessToken{}, api.NewApiError(http.StatusUnauthorized, fmt.Errorf("failed to get userAccessTokenData: %s", err.Error()))
			}

			success, resp, err = a.helixClient.ValidateToken(refreshedToken.AccessToken)
			if !success || err != nil {
				if err != nil {
					log.Errorf("refreshed Token did not validate: %s", err)
				}

				return helix.ValidateTokenResponse{}, refreshedToken, api.NewApiError(http.StatusUnauthorized, fmt.Errorf("refreshed token did not validate"))
			}

			return *resp, refreshedToken, nil
		}

		return helix.ValidateTokenResponse{}, store.UserAccessToken{}, api.NewApiError(http.StatusUnauthorized, fmt.Errorf("token not valid: %s", resp.ErrorMessage))
	}

	return *resp, token, nil
}

func (a *Auth) WriteDeleteCookieResponse(w http.ResponseWriter, err api.Error) {
	cookie := &http.Cookie{
		Name:     "scToken",
		Value:    "",
		Path:     "/",
		Domain:   a.cfg.CookieDomain,
		MaxAge:   -1,
		HttpOnly: true,
	}

	http.SetCookie(w, cookie)
	http.Error(w, err.Error(), err.Status())
}
<|endoftext|>


<commit_before>package http

import (
	"github.com/Terry-Mao/goim/internal/logic"
	"github.com/Terry-Mao/goim/internal/logic/conf"

	"github.com/gin-gonic/gin"
)

// Server is http server.
type Server struct {
	engine *gin.Engine
	logic  *logic.Logic
}

// New new a http server.
func New(c *conf.HTTPServer, l *logic.Logic) *Server {
	engine := gin.New()
	engine.Use(loggerHandler, recoverHandler)
	go func() {
		if err := engine.Run(c.Addr); err != nil {
			panic(err)
		}
	}()
	s := &Server{
		engine: engine,
		logic:  l,
	}
	s.initRouter()
	return s
}

func (s *Server) initRouter() {
	group := s.engine.Group("/goim")
	group.POST("/goim/push/keys", s.pushKeys)
	group.POST("/goim/push/mids", s.pushMids)
	group.POST("/goim/push/room", s.pushRoom)
	group.POST("/goim/push/all", s.pushAll)
	group.GET("/goim/online/top", s.onlineTop)
	group.GET("/goim/online/room", s.onlineRoom)
	group.GET("/goim/online/total", s.onlineTotal)
	group.GET("/goim/nodes/weighted", s.nodesWeighted)
	group.GET("/goim/nodes/instances", s.nodesInstances)
}

// Close close the server.
func (s *Server) Close() {

}
<commit_msg>fix api path<commit_after>package http

import (
	"github.com/Terry-Mao/goim/internal/logic"
	"github.com/Terry-Mao/goim/internal/logic/conf"

	"github.com/gin-gonic/gin"
)

// Server is http server.
type Server struct {
	engine *gin.Engine
	logic  *logic.Logic
}

// New new a http server.
func New(c *conf.HTTPServer, l *logic.Logic) *Server {
	engine := gin.New()
	engine.Use(loggerHandler, recoverHandler)
	go func() {
		if err := engine.Run(c.Addr); err != nil {
			panic(err)
		}
	}()
	s := &Server{
		engine: engine,
		logic:  l,
	}
	s.initRouter()
	return s
}

func (s *Server) initRouter() {
	group := s.engine.Group("/goim")
	group.POST("/push/keys", s.pushKeys)
	group.POST("/push/mids", s.pushMids)
	group.POST("/push/room", s.pushRoom)
	group.POST("/push/all", s.pushAll)
	group.GET("/online/top", s.onlineTop)
	group.GET("/online/room", s.onlineRoom)
	group.GET("/online/total", s.onlineTotal)
	group.GET("/nodes/weighted", s.nodesWeighted)
	group.GET("/nodes/instances", s.nodesInstances)
}

// Close close the server.
func (s *Server) Close() {

}
<|endoftext|>


<commit_before>// Copyright 2016 The clang-server Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package pathutil

import (
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"

	"github.com/pkgutil/osutil"
	"github.com/zchee/clang-server/internal/hashutil"
	xdgbasedir "github.com/zchee/go-xdgbasedir"
)

const dirname = "clang-server"

// CacheDir return the clang-server cache directory path.
func CacheDir() string {
	dir := filepath.Join(xdgbasedir.CacheHome(), dirname)
	if !osutil.IsExist(dir) {
		if err := os.MkdirAll(dir, 0755); err != nil {
			log.Fatal(err)
		}
	}
	return dir
}

// ProjectCacheDir return the project path based cache directory path.
func ProjectCacheDir(root string) string {
	// assume the absolude directory path
	id := hashutil.NewHashString(root)
	dir := filepath.Join(CacheDir(), fmt.Sprintf("%s.%s", filepath.Base(root), hashutil.EncodeToString(id)))
	return dir
}

// ProjectASTDir return the project path based AST cache directory path.
func ProjectASTDir(root string) string {
	cacheDir := ProjectCacheDir(root)
	astDir := filepath.Join(cacheDir, "ast")
	if err := osutil.MkdirAll(astDir, 0755); err != nil {
		log.Fatal(err)
	}
	return astDir
}

var rootMarks = []string{
	".git",                      // git
	"LICENSE",                   // project license
	".gitignore",                // git
	".travis.yml", "circle.yml", // CI service config files
	"CMakeLists.txt",                                                   // CMake
	"autogen.sh", "configure", "Makefile.am", "Makefile.in", "INSTALL", // GNU Autotools
	".hg", ".svn", ".bzr", "_darcs", ".tup", // typical vcs directories
}

// FindProjectRoot finds the project root directory path from path.
func FindProjectRoot(path string) (string, error) {
	if path == "" {
		return "", errors.New("project root is blank")
	}

	if !filepath.IsAbs(path) {
		abs, err := filepath.Abs(path)
		if err == nil {
			path = abs
		}
	}

	first := path
	for path != "/" {
		for _, c := range rootMarks {
			if p := filepath.Join(path, c); osutil.IsExist(p) {
				return path, nil
			}
		}
		path = filepath.Dir(path)
	}

	return "", fmt.Errorf("couldn't find project root in %s", first)
}
<commit_msg>internal/pathutil: remove CMakeLists.txt mark on rootMarks<commit_after>// Copyright 2016 The clang-server Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package pathutil

import (
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"

	"github.com/pkgutil/osutil"
	"github.com/zchee/clang-server/internal/hashutil"
	xdgbasedir "github.com/zchee/go-xdgbasedir"
)

const dirname = "clang-server"

// CacheDir return the clang-server cache directory path.
func CacheDir() string {
	dir := filepath.Join(xdgbasedir.CacheHome(), dirname)
	if !osutil.IsExist(dir) {
		if err := os.MkdirAll(dir, 0755); err != nil {
			log.Fatal(err)
		}
	}
	return dir
}

// ProjectCacheDir return the project path based cache directory path.
func ProjectCacheDir(root string) string {
	// assume the absolude directory path
	id := hashutil.NewHashString(root)
	dir := filepath.Join(CacheDir(), fmt.Sprintf("%s.%s", filepath.Base(root), hashutil.EncodeToString(id)))
	return dir
}

// ProjectASTDir return the project path based AST cache directory path.
func ProjectASTDir(root string) string {
	cacheDir := ProjectCacheDir(root)
	astDir := filepath.Join(cacheDir, "ast")
	if err := osutil.MkdirAll(astDir, 0755); err != nil {
		log.Fatal(err)
	}
	return astDir
}

var rootMarks = []string{
	".git",                      // git
	"LICENSE",                   // project license
	".gitignore",                // git
	".travis.yml", "circle.yml", // CI service config files
	"autogen.sh", "configure", "Makefile.am", "Makefile.in", "INSTALL", // GNU Autotools
	".hg", ".svn", ".bzr", "_darcs", ".tup", // typical vcs directories
}

// FindProjectRoot finds the project root directory path from path.
func FindProjectRoot(path string) (string, error) {
	if path == "" {
		return "", errors.New("project root is blank")
	}

	if !filepath.IsAbs(path) {
		abs, err := filepath.Abs(path)
		if err == nil {
			path = abs
		}
	}

	first := path
	for path != "/" {
		for _, c := range rootMarks {
			if p := filepath.Join(path, c); osutil.IsExist(p) {
				return path, nil
			}
		}
		path = filepath.Dir(path)
	}

	return "", fmt.Errorf("couldn't find project root in %s", first)
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ec2

import (
	"fmt"
	"net"
	"time"

	"github.com/coreos/coreos-metadata/internal/providers"
	"github.com/coreos/coreos-metadata/internal/retry"
)

func FetchMetadata() (providers.Metadata, error) {
	public, err := fetchIP("public-ipv4")
	if err != nil {
		return providers.Metadata{}, err
	}
	local, err := fetchIP("local-ipv4")
	if err != nil {
		return providers.Metadata{}, err
	}
	hostname, err := fetchString("hostname")
	if err != nil {
		return providers.Metadata{}, err
	}

	return providers.Metadata{
		Attributes: map[string]string{
			"EC2_IPV4_LOCAL":  local.String(),
			"EC2_IPV4_PUBLIC": public.String(),
			"EC2_HOSTNAME":    hostname,
		},
	}, nil
}

func fetchString(key string) (string, error) {
	body, err := retry.Client{
		InitialBackoff: time.Second,
		MaxBackoff:     time.Second * 5,
		MaxAttempts:    10,
	}.Get("http://169.254.169.254/2009-04-04/meta-data/" + key)
	return string(body), err
}

func fetchIP(key string) (net.IP, error) {
	str, err := fetchString(key)
	if err != nil {
		return nil, err
	}
	if ip := net.ParseIP(str); ip != nil {
		return ip, nil
	} else {
		return nil, fmt.Errorf("couldn't parse %q as IP address", str)
	}
}
<commit_msg>providers/ec2: add support for fetching keys<commit_after>// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ec2

import (
	"bufio"
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/coreos/coreos-metadata/internal/providers"
	"github.com/coreos/coreos-metadata/internal/retry"
)

func FetchMetadata() (providers.Metadata, error) {
	public, err := fetchIP("public-ipv4")
	if err != nil {
		return providers.Metadata{}, err
	}
	local, err := fetchIP("local-ipv4")
	if err != nil {
		return providers.Metadata{}, err
	}
	hostname, err := fetchString("hostname")
	if err != nil {
		return providers.Metadata{}, err
	}

	sshKeys, err := fetchSshKeys()
	if err != nil {
		return providers.Metadata{}, err
	}

	return providers.Metadata{
		Attributes: map[string]string{
			"EC2_IPV4_LOCAL":  local.String(),
			"EC2_IPV4_PUBLIC": public.String(),
			"EC2_HOSTNAME":    hostname,
		},
		SshKeys: sshKeys,
	}, nil
}

func fetchString(key string) (string, error) {
	body, err := retry.Client{
		InitialBackoff: time.Second,
		MaxBackoff:     time.Second * 5,
		MaxAttempts:    10,
	}.Get("http://169.254.169.254/2009-04-04/meta-data/" + key)
	return string(body), err
}

func fetchIP(key string) (net.IP, error) {
	str, err := fetchString(key)
	if err != nil {
		return nil, err
	}
	if ip := net.ParseIP(str); ip != nil {
		return ip, nil
	} else {
		return nil, fmt.Errorf("couldn't parse %q as IP address", str)
	}
}

func fetchSshKeys() ([]string, error) {
	keydata, err := fetchString("public-keys")
	if err != nil {
		return nil, fmt.Errorf("error reading keys: %v", err)
	}

	scanner := bufio.NewScanner(strings.NewReader(keydata))
	keynames := []string{}
	for scanner.Scan() {
		keynames = append(keynames, scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		return nil, fmt.Errorf("error parsing keys: %v", err)
	}

	keyIDs := make(map[string]string)
	for _, keyname := range keynames {
		tokens := strings.SplitN(keyname, "=", 2)
		if len(tokens) != 2 {
			return nil, fmt.Errorf("malformed public key: %q", keyname)
		}
		keyIDs[tokens[1]] = tokens[0]
	}

	keys := []string{}
	for _, id := range keyIDs {
		sshkey, err := fetchString(fmt.Sprintf("public-keys/%s/openssh-key", id))
		if err != nil {
			return nil, err
		}
		keys = append(keys, sshkey)
	}

	return keys, nil
}
<|endoftext|>