<commit_before>package main

import (
  "bytes"
  "encoding/json"
  "fmt"
  "github.com/fatih/color"
  "github.com/skratchdot/open-golang/open"
  "github.com/urfave/cli"
  "io"
  "io/ioutil"
  "log"
  "mime/multipart"
  "net/http"
  "os"
  "path/filepath"
)

var (
  notebookHost        = "http://annotate.notable.dev"
  supportedExtensions = []string{".jpg", ".jpeg", ".png", ".gif"}
)

func runNotebook(c *cli.Context) {
  // get images
  images := findImages()
  // send multipart form data
  postNotebook(images)
  // open set in Notebooks
}

func stringInSlice(str string, list []string) bool {
  for _, v := range list {
    if v == str {
      return true
    }
  }
  return false
}

func findImages() []string {
  images := []string{}

  d, err := os.Open(currentPath())
  if err != nil {
    fmt.Println(err)
    os.Exit(1)
  }
  defer d.Close()

  files, err := d.Readdir(-1)
  if err != nil {
    fmt.Println(err)
    os.Exit(1)
  }

  for _, file := range files {
    if file.Mode().IsRegular() {
      ext := filepath.Ext(file.Name())
      // fmt.Printf("%s\n", ext)
      if stringInSlice(ext, supportedExtensions) {
        images = append(images, file.Name())
      }
    }
  }

  return images
}

func postNotebook(images []string) {
  url := fmt.Sprintf("%s/api/v1/posts", notebookHost)
  /* Create a buffer to hold this multi-part form */
  bodyBuf := bytes.NewBufferString("")
  bodyWriter := multipart.NewWriter(bodyBuf)
  contentType := bodyWriter.FormDataContentType()

  for _, name := range images {
    path := fmt.Sprintf("%s/%s", currentPath(), name)
    file, err := os.Open(path)
    if err != nil {
      log.Fatal(err)
    }
    defer file.Close()

    // paramName := fmt.Sprintf("image[]", index)

    /* Create a completely custom Form Part (or in this case, a file) */
    // http://golang.org/src/pkg/mime/multipart/writer.go?s=2274:2352#L86
    part, err := bodyWriter.CreateFormFile("image[]", filepath.Base(path))
    if err != nil {
      log.Fatal(err)
    }
    _, err = io.Copy(part, file)
  }

  /* Create a Form Field in a simpler way */
  bodyWriter.WriteField("name", url)
  bodyWriter.WriteField("current_platform_user_token", envConfig.AuthToken)
  /* Close the body and send the request */
  bodyWriter.Close()
  resp, err := http.Post(url, contentType, bodyBuf)
  if nil != err {
    panic(err.Error())
  }

  /* Handle the response */
  defer resp.Body.Close()
  body, err := ioutil.ReadAll(resp.Body)

  if nil != err {
    fmt.Println("Error happened reading the body", err)
    return
  }

  var data map[string]interface{}
  if err := json.Unmarshal(body, &data); err != nil {
    panic(err)
  }

  if data["error"] != nil {
    color.Red("\nYour Notable credentials are invalid, please login again:\n")
    color.White("%s login", os.Args[0])
    os.Exit(1)
  }

  fmt.Println(data)

  s.Stop()
  color.Cyan("✓ Upload: complete!\n\n")
  color.Cyan("Done! Go give feedback!")
  responseURL := data["url"].(string)
  color.Magenta(responseURL)

  open.Run(responseURL)
}
<commit_msg>Open in browser.<commit_after>package main

import (
  "bytes"
  "encoding/json"
  "fmt"
  "github.com/fatih/color"
  "github.com/skratchdot/open-golang/open"
  "github.com/urfave/cli"
  "io"
  "io/ioutil"
  "log"
  "mime/multipart"
  "net/http"
  "os"
  "path/filepath"
)

var (
  notebookHost        = "http://annotate.notable.dev"
  supportedExtensions = []string{".jpg", ".jpeg", ".png", ".gif"}
)

func runNotebook(c *cli.Context) {
  // get images
  images := findImages()
  // send multipart form data
  postNotebook(images)
  // open set in Notebooks
}

func stringInSlice(str string, list []string) bool {
  for _, v := range list {
    if v == str {
      return true
    }
  }
  return false
}

func findImages() []string {
  images := []string{}

  d, err := os.Open(currentPath())
  if err != nil {
    fmt.Println(err)
    os.Exit(1)
  }
  defer d.Close()

  files, err := d.Readdir(-1)
  if err != nil {
    fmt.Println(err)
    os.Exit(1)
  }

  for _, file := range files {
    if file.Mode().IsRegular() {
      ext := filepath.Ext(file.Name())
      // fmt.Printf("%s\n", ext)
      if stringInSlice(ext, supportedExtensions) {
        images = append(images, file.Name())
      }
    }
  }

  return images
}

func postNotebook(images []string) {
  url := fmt.Sprintf("%s/api/v1/posts", notebookHost)
  /* Create a buffer to hold this multi-part form */
  bodyBuf := bytes.NewBufferString("")
  bodyWriter := multipart.NewWriter(bodyBuf)
  contentType := bodyWriter.FormDataContentType()

  for _, name := range images {
    path := fmt.Sprintf("%s/%s", currentPath(), name)
    file, err := os.Open(path)
    if err != nil {
      log.Fatal(err)
    }
    defer file.Close()

    /* Create a completely custom Form Part (or in this case, a file) */
    // http://golang.org/src/pkg/mime/multipart/writer.go?s=2274:2352#L86
    part, err := bodyWriter.CreateFormFile("image[]", filepath.Base(path))
    if err != nil {
      log.Fatal(err)
    }
    _, err = io.Copy(part, file)
  }

  /* Create a Form Field in a simpler way */
  bodyWriter.WriteField("name", url)
  bodyWriter.WriteField("current_platform_user_token", envConfig.AuthToken)
  /* Close the body and send the request */
  bodyWriter.Close()
  resp, err := http.Post(url, contentType, bodyBuf)
  if nil != err {
    panic(err.Error())
  }

  /* Handle the response */
  defer resp.Body.Close()
  body, err := ioutil.ReadAll(resp.Body)

  if nil != err {
    fmt.Println("Error happened reading the body", err)
    return
  }

  var data map[string]interface{}
  if err := json.Unmarshal(body, &data); err != nil {
    panic(err)
  }

  s.Stop()
  color.Cyan("✓ Upload: complete!\n\n")
  color.Cyan("Done! Go give feedback!")
  responseURL := data["dashboard_item_url"].(string)
  color.Magenta(responseURL)

  open.Run(responseURL)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/charles-l/gitamite"
	"github.com/tucnak/climax"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
)

func errx(code int, s string) {
	fmt.Fprintf(os.Stderr, "gitamite: %s\n", s)
	os.Exit(code)
}

func createRepoRequest(ctx climax.Context) int {
	u := url.URL{
		Scheme: "http",
		Host:   "localhost:8000",
		Path:   "/repo",
	}

	if len(ctx.Args) < 2 {
		errx(1, "need a name")
	}
	a, err := gitamite.CreateAuthRequest(struct {
		Name string
	}{
		ctx.Args[1],
	})
	if err != nil {
		log.Fatal(err)
	}

	j, _ := json.Marshal(a)
	r, err := http.Post(u.String(), "text/json", bytes.NewReader(j))
	if err != nil || r.StatusCode != http.StatusOK {
		b, _ := ioutil.ReadAll(r.Body)
		errx(2, "request to remote failed: "+string(b))
	}
	return 0
}

func main() {
	cli := climax.New("gitamite")
	cli.Brief = "gitamite client"
	cli.Version = "1.0"
	createCmd := climax.Command{
		Name:   "create",
		Brief:  "creates a new repo",
		Usage:  "[REPO]",
		Help:   "creates a new repo",
		Handle: createRepoRequest,
	}

	cli.AddCommand(createCmd)
	cli.Run()
	return
}
<commit_msg>added delete call to client<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/charles-l/gitamite"
	"github.com/tucnak/climax"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
)

func errx(code int, s string) {
	fmt.Fprintf(os.Stderr, "gitamite: %s\n", s)
	os.Exit(code)
}

func makeRequest(args []string, f func(url.URL, []byte) *http.Response) {
	u := url.URL{
		Scheme: "http",
		Host:   "localhost:8000", // TODO: read this from the config file
		Path:   "/repo",
	}

	// TODO: generalize the request struct when needed
	if len(args) < 2 {
		errx(1, "need a name")
	}

	a, err := gitamite.CreateAuthRequest(struct {
		Name string
	}{
		args[1],
	})
	if err != nil {
		errx(1, err.Error())
	}

	blob, _ := json.Marshal(a)
	r := f(u, blob)

	if err != nil || r.StatusCode != http.StatusOK {
		b, _ := ioutil.ReadAll(r.Body)
		errx(2, "request to remote failed: "+string(b))
	}
}

func createRepoRequest(ctx climax.Context) int {
	makeRequest(ctx.Args, func(u url.URL, blob []byte) *http.Response {
		r, err := http.Post(u.String(), "text/json", bytes.NewReader(blob))
		if err != nil {
			errx(3, err.Error())
		}
		return r
	})
	return 0
}

func deleteRepoRequest(ctx climax.Context) int {
	makeRequest(ctx.Args, func(u url.URL, blob []byte) *http.Response {
		d, _ := http.NewRequest(http.MethodDelete, u.String(), bytes.NewReader(blob))
		client := &http.Client{}
		r, err := client.Do(d)
		if err != nil {
			errx(3, err.Error())
		}
		return r
	})
	return 0
}

func main() {
	cli := climax.New("gitamite")
	cli.Brief = "gitamite client"
	cli.Version = "1.0"

	createCmd := climax.Command{
		Name:   "create",
		Brief:  "creates a new repo",
		Usage:  "[REPO]",
		Help:   "creates a new repo",
		Handle: createRepoRequest,
	}
	cli.AddCommand(createCmd)

	deleteCmd := climax.Command{
		Name:   "delete",
		Brief:  "deletes a repo",
		Usage:  "[REPO]",
		Help:   "deletes a repo",
		Handle: deleteRepoRequest,
	}
	cli.AddCommand(deleteCmd)

	cli.Run()
	return
}
<|endoftext|>


<commit_before>/*
This is the unifying package between a 32bit and 64 bit implementations of
Hash Array Mapped Tries (HAMT). You can `import hamt "github.com/lleo/go-hamt"'
then instantiate either a hamt32 or hamt64 datastructure with the
hamt.NewHamt32() or hamt.NewHamt64() functions. Both datastructures have the
same exported API defined by the Hamt interface.

Given how wide a HAMT node is (either 32 or 64 nodes wide) HAMT datastructures
not very deep; either 6, for 32bit, or 10, for 64bit implementations, nodes
deep. This neans HAMTs are effectively O(1) for Search, Insertions, and
Deletions.

Both 32 and 64 bit implementations of HAMTs are of fixed depth is because they
are [Tries](https://en.wikipedia.org/wiki/Trie). The key of a Trie is split
into n-number smaller indecies and each node from the root uses each successive
index. For example a Trie with a string key would be split into chanracters and
each node from root would be indexed by the next character in the string key.

In the case of a this HAMT implementation the key is hashed into a 30 or 60 bit
number. In the case of the string_key we take the []byte slice of the string
and feed it to hash.fnv.New32() or New64() hash generator. Since these
generate 32 and 64 bit hash values respectively and we need 30 and 60 bit
values, we use the [xor-fold technique](http://www.isthe.com/chongo/tech/comp/fnv/index.html#xor-fold)
to "fold" the high 2 or 4 bits of the 32 and 64 bit hash values into 30 and
60 bit values for our needs.

We want 30 and 60 bit values because they split nicely into six 5bit and ten
6bit values respectively. Each of these 5 and 6 bit values become the indexies
of our Trie nodes with a maximum depth of 6 or 10 respectively. Further 5 bits
indexes into a 32 entry table nodes for 32 bit HAMTs and 6 bit index into 64
entry table nodes for 64 bit HAMTs; isn't that symmetrical :).

For a this HAMT implementation, when key/value pair must be created, deleted,
or changed the key is hashed into a 30 or 60 bit value (described above) and
that hash30 or hash60 value represents a path of 5 or 6 bit values to place a
leaf containing the key, value pair. For a Get() or Del() operation we lookup
the deepest node along that pate that is not-nil. For a Put() operation we
lookup the deepest location that is nil and not beyond the lenth of the path.

You may implement your own Key type by implementeding the Key interface
defined in "github.com/lleo/go-hamt/key" or you may used the example
StringKey interface described in "github.com/lleo/go-hamt/string_key".

A HAMT is a Hashed Array Mapped Trie datastructure. FIXME: explain HAMT
*/
package hamt

import (
	"fmt"

	"github.com/lleo/go-hamt/hamt32"
	"github.com/lleo/go-hamt/hamt64"
	"github.com/lleo/go-hamt/key"
)

// Hamt interface defines all behavior for implementations of a classic
// Hash Array Mapped Trie datastructure.
type Hamt interface {
	Get(key.Key) (interface{}, bool)
	Put(key.Key, interface{}) bool
	Del(key.Key) (interface{}, bool)
	IsEmpty() bool
	String() string
	LongString(indent string) string
}

type keyVal struct {
	key key.Key
	val interface{}
}

func (kv keyVal) String() string {
	return fmt.Sprintf("keyVal{%s, %v}", kv.key, kv.val)
}

func NewHamt32() Hamt {
	return hamt32.NewHamt()
}

func NewHamt64() Hamt {
	return hamt64.NewHamt()
}
<commit_msg>a very minor change in doc<commit_after>/*
This is the unifying package between 32bit and 64 bit implementations of
Hash Array Mapped Tries (HAMT). You can `import hamt "github.com/lleo/go-hamt"'
then instantiate either a hamt32 or hamt64 datastructure with the
hamt.NewHamt32() or hamt.NewHamt64() functions. Both datastructures have the
same exported API defined by the Hamt interface.

Given how wide a HAMT node is (either 32 or 64 nodes wide) HAMT datastructures
not very deep; either 6, for 32bit, or 10, for 64bit implementations, nodes
deep. This neans HAMTs are effectively O(1) for Search, Insertions, and
Deletions.

Both 32 and 64 bit implementations of HAMTs are of fixed depth is because they
are [Tries](https://en.wikipedia.org/wiki/Trie). The key of a Trie is split
into n-number smaller indecies and each node from the root uses each successive
index. For example a Trie with a string key would be split into chanracters and
each node from root would be indexed by the next character in the string key.

In the case of a this HAMT implementation the key is hashed into a 30 or 60 bit
number. In the case of the string_key we take the []byte slice of the string
and feed it to hash.fnv.New32() or New64() hash generator. Since these
generate 32 and 64 bit hash values respectively and we need 30 and 60 bit
values, we use the [xor-fold technique](http://www.isthe.com/chongo/tech/comp/fnv/index.html#xor-fold)
to "fold" the high 2 or 4 bits of the 32 and 64 bit hash values into 30 and
60 bit values for our needs.

We want 30 and 60 bit values because they split nicely into six 5bit and ten
6bit values respectively. Each of these 5 and 6 bit values become the indexies
of our Trie nodes with a maximum depth of 6 or 10 respectively. Further 5 bits
indexes into a 32 entry table nodes for 32 bit HAMTs and 6 bit index into 64
entry table nodes for 64 bit HAMTs; isn't that symmetrical :).

For a this HAMT implementation, when key/value pair must be created, deleted,
or changed the key is hashed into a 30 or 60 bit value (described above) and
that hash30 or hash60 value represents a path of 5 or 6 bit values to place a
leaf containing the key, value pair. For a Get() or Del() operation we lookup
the deepest node along that pate that is not-nil. For a Put() operation we
lookup the deepest location that is nil and not beyond the lenth of the path.

You may implement your own Key type by implementeding the Key interface
defined in "github.com/lleo/go-hamt/key" or you may used the example
StringKey interface described in "github.com/lleo/go-hamt/string_key".

A HAMT is a Hashed Array Mapped Trie datastructure. FIXME: explain HAMT
*/
package hamt

import (
	"fmt"

	"github.com/lleo/go-hamt/hamt32"
	"github.com/lleo/go-hamt/hamt64"
	"github.com/lleo/go-hamt/key"
)

// Hamt interface defines all behavior for implementations of a classic
// Hash Array Mapped Trie datastructure.
type Hamt interface {
	Get(key.Key) (interface{}, bool)
	Put(key.Key, interface{}) bool
	Del(key.Key) (interface{}, bool)
	IsEmpty() bool
	String() string
	LongString(indent string) string
}

type keyVal struct {
	key key.Key
	val interface{}
}

func (kv keyVal) String() string {
	return fmt.Sprintf("keyVal{%s, %v}", kv.key, kv.val)
}

func NewHamt32() Hamt {
	return hamt32.NewHamt()
}

func NewHamt64() Hamt {
	return hamt64.NewHamt()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"encoding/json"
	"net"
	"time"
	"golang.org/x/net/ipv4"
)

type TokenCookie struct {
	Host  string `json:"host"`
	Time  string `json:"time"`
	Token string `json:"token"`
	DSCP  int    `json:"dscp"`
}

type DSCP struct {
	Name string
	Value int
}

func main() {
	url := "http://trace.enoti.me/start"

	dscp_map := [21]DSCP{
		{Name:"BE",   Value:0x00},
		{Name:"EF",   Value:0x2e},
		{Name:"CS1",  Value:0x08},
		{Name:"CS2",  Value:0x10},
		{Name:"CS3",  Value:0x18},
		{Name:"CS4",  Value:0x20},
		{Name:"CS5",  Value:0x28},
		{Name:"CS6",  Value:0x30},
		{Name:"CS7",  Value:0x38},
		{Name:"AF11", Value:0x0a},
		{Name:"AF12", Value:0x0c},
		{Name:"AF13", Value:0x0e},
		{Name:"AF21", Value:0x12},
		{Name:"AF22", Value:0x14},
		{Name:"AF23", Value:0x16},
		{Name:"AF31", Value:0x1a},
		{Name:"AF32", Value:0x1c},
		{Name:"AF33", Value:0x1e},
		{Name:"AF41", Value:0x22},
		{Name:"AF42", Value:0x24},
		{Name:"AF43", Value:0x26},
	}

	res, err := http.Get(url)
	if err != nil {
		log.Fatal(err)
	}

	data, err := ioutil.ReadAll(res.Body)

	res.Body.Close()
	if err != nil {
		log.Fatal(err)
	}

	var token TokenCookie
	err = json.Unmarshal(data, &token)

	if err != nil {
		fmt.Println("error:", err)
	}

	fmt.Printf("%+v\n", token)

    for _, mark := range dscp_map {
		conn, err := net.Dial("udp", "trace.enoti.me:60606")
		fmt.Println("Created UDP Connection")

		fmt.Printf("sending mark: %v\n", mark.Value)

		token.DSCP = mark.Value
		if err := ipv4.NewConn(conn).SetTOS(token.DSCP); err != nil {
			// error handling
		}

		if err != nil {
			fmt.Printf("Some error %v", err)
			return
		}

		for i := 1; i <= 10; i++ {
			token.Time = time.Now().UTC().Format("20060102150405")

			json.NewEncoder(conn).Encode(token)
			time.Sleep(time.Millisecond * 100)
		}

		conn.Close()
	}
}
<commit_msg>make the client much friendlier<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"encoding/json"
	"net"
	"time"

	"golang.org/x/net/ipv4"
	"gopkg.in/cheggaaa/pb.v1"
)

type TokenCookie struct {
	Host  string `json:"host"`
	Time  string `json:"time"`
	Token string `json:"token"`
	DSCP  int    `json:"dscp"`
}

type DSCP struct {
	Name string
	Value int
}

func main() {
	url := "http://trace.enoti.me/start"

	dscp_map := [21]DSCP{
		{Name:"BE",   Value:0x00},
		{Name:"EF",   Value:0x2e},
		{Name:"CS1",  Value:0x08},
		{Name:"CS2",  Value:0x10},
		{Name:"CS3",  Value:0x18},
		{Name:"CS4",  Value:0x20},
		{Name:"CS5",  Value:0x28},
		{Name:"CS6",  Value:0x30},
		{Name:"CS7",  Value:0x38},
		{Name:"AF11", Value:0x0a},
		{Name:"AF12", Value:0x0c},
		{Name:"AF13", Value:0x0e},
		{Name:"AF21", Value:0x12},
		{Name:"AF22", Value:0x14},
		{Name:"AF23", Value:0x16},
		{Name:"AF31", Value:0x1a},
		{Name:"AF32", Value:0x1c},
		{Name:"AF33", Value:0x1e},
		{Name:"AF41", Value:0x22},
		{Name:"AF42", Value:0x24},
		{Name:"AF43", Value:0x26},
	}

	fmt.Printf("connecting to %s .  .  . ", url)
	res, err := http.Get(url)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf(". . ... done\n")
	data, err := ioutil.ReadAll(res.Body)

	res.Body.Close()
	if err != nil {
		log.Fatal(err)
	}

	var token TokenCookie
	err = json.Unmarshal(data, &token)

	if err != nil {
		fmt.Println("error:", err)
	}

	fmt.Printf("received token:\n %+v\n", token)

	bar := pb.StartNew(10 * len(dscp_map))
	conn, err := net.Dial("udp", "trace.enoti.me:60606")

	start := time.Now()
	send_count := 10

	fmt.Println("Sending Datagrams")
    for _, mark := range dscp_map {
		token.DSCP = mark.Value

		if err := ipv4.NewConn(conn).SetTOS(token.DSCP); err != nil {
			// error handling
		}

		if err != nil {
			fmt.Printf("Some error %v", err)
			return
		}

		for i := 1; i <= send_count ; i++ {
			token.Time = time.Now().UTC().Format("20060102150405")

			json.NewEncoder(conn).Encode(token)
			time.Sleep(time.Millisecond * 200)
			bar.Increment()
		}

		conn.Close()
	}

	fmt.Printf("%v Datagrams representing %v DSCP Marks sent in %v seconds\n",
		len(dscp_map) * send_count,
		len(dscp_map),
		time.Since(start))
	fmt.Println("Thank you for helping make the internet better")
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"syscall"

	"github.com/gorilla/websocket"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/lxc/config"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/gnuflag"
	"github.com/lxc/lxd/shared/i18n"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/termios"
)

type envList []string

func (f *envList) String() string {
	return fmt.Sprint(*f)
}

func (f *envList) Set(value string) error {
	if f == nil {
		*f = make(envList, 1)
	} else {
		*f = append(*f, value)
	}
	return nil
}

type execCmd struct {
	modeFlag            string
	envArgs             envList
	forceInteractive    bool
	forceNonInteractive bool
	disableStdin        bool
}

func (c *execCmd) showByDefault() bool {
	return true
}

func (c *execCmd) usage() string {
	return i18n.G(
		`Usage: lxc exec [<remote>:]<container> [-t] [-T] [-n] [--mode=auto|interactive|non-interactive] [--env KEY=VALUE...] [--] <command line>

Execute commands in containers.

Mode defaults to non-interactive, interactive mode is selected if both stdin AND stdout are terminals (stderr is ignored).`)
}

func (c *execCmd) flags() {
	gnuflag.Var(&c.envArgs, "env", i18n.G("Environment variable to set (e.g. HOME=/home/foo)"))
	gnuflag.StringVar(&c.modeFlag, "mode", "auto", i18n.G("Override the terminal mode (auto, interactive or non-interactive)"))
	gnuflag.BoolVar(&c.forceInteractive, "t", false, i18n.G("Force pseudo-terminal allocation"))
	gnuflag.BoolVar(&c.forceNonInteractive, "T", false, i18n.G("Disable pseudo-terminal allocation"))
	gnuflag.BoolVar(&c.disableStdin, "n", false, i18n.G("Disable stdin (reads from /dev/null)"))
}

func (c *execCmd) sendTermSize(control *websocket.Conn) error {
	width, height, err := termios.GetSize(int(syscall.Stdout))
	if err != nil {
		return err
	}

	logger.Debugf("Window size is now: %dx%d", width, height)

	w, err := control.NextWriter(websocket.TextMessage)
	if err != nil {
		return err
	}

	msg := api.ContainerExecControl{}
	msg.Command = "window-resize"
	msg.Args = make(map[string]string)
	msg.Args["width"] = strconv.Itoa(width)
	msg.Args["height"] = strconv.Itoa(height)

	buf, err := json.Marshal(msg)
	if err != nil {
		return err
	}
	_, err = w.Write(buf)

	w.Close()
	return err
}

func (c *execCmd) forwardSignal(control *websocket.Conn, sig syscall.Signal) error {
	logger.Debugf("Forwarding signal: %s", sig)

	w, err := control.NextWriter(websocket.TextMessage)
	if err != nil {
		return err
	}

	msg := api.ContainerExecControl{}
	msg.Command = "signal"
	msg.Signal = int(sig)

	buf, err := json.Marshal(msg)
	if err != nil {
		return err
	}
	_, err = w.Write(buf)

	w.Close()
	return err
}

func (c *execCmd) run(conf *config.Config, args []string) error {
	if len(args) < 2 {
		return errArgs
	}

	if c.forceInteractive && c.forceNonInteractive {
		return fmt.Errorf(i18n.G("You can't pass -t and -T at the same time"))
	}

	if c.modeFlag != "auto" && (c.forceInteractive || c.forceNonInteractive) {
		return fmt.Errorf(i18n.G("You can't pass -t or -T at the same time as --mode"))
	}

	remote, name, err := conf.ParseRemote(args[0])
	if err != nil {
		return err
	}

	d, err := conf.GetContainerServer(remote)
	if err != nil {
		return err
	}

	/* FIXME: Default values for HOME and USER are now handled by LXD.
	   This code should be removed after most users upgraded.
	*/
	env := map[string]string{"HOME": "/root", "USER": "root"}
	if myTerm, ok := c.getTERM(); ok {
		env["TERM"] = myTerm
	}

	for _, arg := range c.envArgs {
		pieces := strings.SplitN(arg, "=", 2)
		value := ""
		if len(pieces) > 1 {
			value = pieces[1]
		}
		env[pieces[0]] = value
	}

	cfd := int(syscall.Stdin)

	var interactive bool
	if c.disableStdin {
		interactive = false
	} else if c.modeFlag == "interactive" || c.forceInteractive {
		interactive = true
	} else if c.modeFlag == "non-interactive" || c.forceNonInteractive {
		interactive = false
	} else {
		interactive = termios.IsTerminal(cfd) && termios.IsTerminal(int(syscall.Stdout))
	}

	var oldttystate *termios.State
	if interactive {
		oldttystate, err = termios.MakeRaw(cfd)
		if err != nil {
			return err
		}
		defer termios.Restore(cfd, oldttystate)
	}

	handler := c.controlSocketHandler
	if !interactive {
		handler = nil
	}

	var width, height int
	if interactive {
		width, height, err = termios.GetSize(int(syscall.Stdout))
		if err != nil {
			return err
		}
	}

	var stdin io.ReadCloser
	stdin = os.Stdin
	if c.disableStdin {
		stdin = ioutil.NopCloser(bytes.NewReader(nil))
	}

	stdout := c.getStdout()

	req := api.ContainerExecPost{
		Command:     args[1:],
		WaitForWS:   true,
		Interactive: interactive,
		Environment: env,
		Width:       width,
		Height:      height,
	}

	execArgs := lxd.ContainerExecArgs{
		Stdin:    stdin,
		Stdout:   stdout,
		Stderr:   os.Stderr,
		Control:  handler,
		DataDone: make(chan bool),
	}

	// Run the command in the container
	op, err := d.ExecContainer(name, req, &execArgs)
	if err != nil {
		return err
	}

	// Wait for the operation to complete
	err = op.Wait()
	if err != nil {
		return err
	}

	// Wait for any remaining I/O to be flushed
	<-execArgs.DataDone

	if oldttystate != nil {
		/* A bit of a special case here: we want to exit with the same code as
		 * the process inside the container, so we explicitly exit here
		 * instead of returning an error.
		 *
		 * Additionally, since os.Exit() exits without running deferred
		 * functions, we restore the terminal explicitly.
		 */
		termios.Restore(cfd, oldttystate)
	}

	os.Exit(int(op.Metadata["return"].(float64)))
	return nil
}
<commit_msg>Added documentation about shell env to lxc exec<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"syscall"

	"github.com/gorilla/websocket"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/lxc/config"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/gnuflag"
	"github.com/lxc/lxd/shared/i18n"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/termios"
)

type envList []string

func (f *envList) String() string {
	return fmt.Sprint(*f)
}

func (f *envList) Set(value string) error {
	if f == nil {
		*f = make(envList, 1)
	} else {
		*f = append(*f, value)
	}
	return nil
}

type execCmd struct {
	modeFlag            string
	envArgs             envList
	forceInteractive    bool
	forceNonInteractive bool
	disableStdin        bool
}

func (c *execCmd) showByDefault() bool {
	return true
}

func (c *execCmd) usage() string {
	return i18n.G(
		`Usage: lxc exec [<remote>:]<container> [-t] [-T] [-n] [--mode=auto|interactive|non-interactive] [--env KEY=VALUE...] [--] <command line>

Execute commands in containers.

The command is executed directly using exec, so there is no shell and shell patterns (variables, file redirects, ...)
won't be understood. If you need a shell environment you need to execute the shell executeable, passing the shell commands
as arguments, for example:

    lxc exec <container> -- sh -c "cd /tmp && pwd"

Mode defaults to non-interactive, interactive mode is selected if both stdin AND stdout are terminals (stderr is ignored).`)
}

func (c *execCmd) flags() {
	gnuflag.Var(&c.envArgs, "env", i18n.G("Environment variable to set (e.g. HOME=/home/foo)"))
	gnuflag.StringVar(&c.modeFlag, "mode", "auto", i18n.G("Override the terminal mode (auto, interactive or non-interactive)"))
	gnuflag.BoolVar(&c.forceInteractive, "t", false, i18n.G("Force pseudo-terminal allocation"))
	gnuflag.BoolVar(&c.forceNonInteractive, "T", false, i18n.G("Disable pseudo-terminal allocation"))
	gnuflag.BoolVar(&c.disableStdin, "n", false, i18n.G("Disable stdin (reads from /dev/null)"))
}

func (c *execCmd) sendTermSize(control *websocket.Conn) error {
	width, height, err := termios.GetSize(int(syscall.Stdout))
	if err != nil {
		return err
	}

	logger.Debugf("Window size is now: %dx%d", width, height)

	w, err := control.NextWriter(websocket.TextMessage)
	if err != nil {
		return err
	}

	msg := api.ContainerExecControl{}
	msg.Command = "window-resize"
	msg.Args = make(map[string]string)
	msg.Args["width"] = strconv.Itoa(width)
	msg.Args["height"] = strconv.Itoa(height)

	buf, err := json.Marshal(msg)
	if err != nil {
		return err
	}
	_, err = w.Write(buf)

	w.Close()
	return err
}

func (c *execCmd) forwardSignal(control *websocket.Conn, sig syscall.Signal) error {
	logger.Debugf("Forwarding signal: %s", sig)

	w, err := control.NextWriter(websocket.TextMessage)
	if err != nil {
		return err
	}

	msg := api.ContainerExecControl{}
	msg.Command = "signal"
	msg.Signal = int(sig)

	buf, err := json.Marshal(msg)
	if err != nil {
		return err
	}
	_, err = w.Write(buf)

	w.Close()
	return err
}

func (c *execCmd) run(conf *config.Config, args []string) error {
	if len(args) < 2 {
		return errArgs
	}

	if c.forceInteractive && c.forceNonInteractive {
		return fmt.Errorf(i18n.G("You can't pass -t and -T at the same time"))
	}

	if c.modeFlag != "auto" && (c.forceInteractive || c.forceNonInteractive) {
		return fmt.Errorf(i18n.G("You can't pass -t or -T at the same time as --mode"))
	}

	remote, name, err := conf.ParseRemote(args[0])
	if err != nil {
		return err
	}

	d, err := conf.GetContainerServer(remote)
	if err != nil {
		return err
	}

	/* FIXME: Default values for HOME and USER are now handled by LXD.
	   This code should be removed after most users upgraded.
	*/
	env := map[string]string{"HOME": "/root", "USER": "root"}
	if myTerm, ok := c.getTERM(); ok {
		env["TERM"] = myTerm
	}

	for _, arg := range c.envArgs {
		pieces := strings.SplitN(arg, "=", 2)
		value := ""
		if len(pieces) > 1 {
			value = pieces[1]
		}
		env[pieces[0]] = value
	}

	cfd := int(syscall.Stdin)

	var interactive bool
	if c.disableStdin {
		interactive = false
	} else if c.modeFlag == "interactive" || c.forceInteractive {
		interactive = true
	} else if c.modeFlag == "non-interactive" || c.forceNonInteractive {
		interactive = false
	} else {
		interactive = termios.IsTerminal(cfd) && termios.IsTerminal(int(syscall.Stdout))
	}

	var oldttystate *termios.State
	if interactive {
		oldttystate, err = termios.MakeRaw(cfd)
		if err != nil {
			return err
		}
		defer termios.Restore(cfd, oldttystate)
	}

	handler := c.controlSocketHandler
	if !interactive {
		handler = nil
	}

	var width, height int
	if interactive {
		width, height, err = termios.GetSize(int(syscall.Stdout))
		if err != nil {
			return err
		}
	}

	var stdin io.ReadCloser
	stdin = os.Stdin
	if c.disableStdin {
		stdin = ioutil.NopCloser(bytes.NewReader(nil))
	}

	stdout := c.getStdout()

	req := api.ContainerExecPost{
		Command:     args[1:],
		WaitForWS:   true,
		Interactive: interactive,
		Environment: env,
		Width:       width,
		Height:      height,
	}

	execArgs := lxd.ContainerExecArgs{
		Stdin:    stdin,
		Stdout:   stdout,
		Stderr:   os.Stderr,
		Control:  handler,
		DataDone: make(chan bool),
	}

	// Run the command in the container
	op, err := d.ExecContainer(name, req, &execArgs)
	if err != nil {
		return err
	}

	// Wait for the operation to complete
	err = op.Wait()
	if err != nil {
		return err
	}

	// Wait for any remaining I/O to be flushed
	<-execArgs.DataDone

	if oldttystate != nil {
		/* A bit of a special case here: we want to exit with the same code as
		 * the process inside the container, so we explicitly exit here
		 * instead of returning an error.
		 *
		 * Additionally, since os.Exit() exits without running deferred
		 * functions, we restore the terminal explicitly.
		 */
		termios.Restore(cfd, oldttystate)
	}

	os.Exit(int(op.Metadata["return"].(float64)))
	return nil
}
<|endoftext|>


<commit_before>package server_test

// TODO: review the http testing
import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/mch1307/gomotics/nhc"
	. "github.com/mch1307/gomotics/server"
	"github.com/mch1307/gomotics/testutil"
	"github.com/mch1307/gomotics/types"
)

var baseUrl string

//const healthMsg = `{"alive":true}`

func init() {
	fmt.Println("starting server test")
	baseUrl = "http://" + testutil.ConnectHost + ":8081"
	testutil.InitStubNHC()
}

func TestHealth(t *testing.T) {
	req, err := http.NewRequest("GET", baseUrl+"/health", nil)
	if err != nil {
		t.Fatal(err)
	}
	rr := httptest.NewRecorder()
	handler := http.HandlerFunc(Health)
	handler.ServeHTTP(rr, req)
	if rr.Body.String() != HealthMsg {
		t.Errorf("health test failed: got %v, expect: %v", rr.Body.String(), HealthMsg)
	}

}

// TODO: add more test cases (test non existing item)
func Test_getNhcItem(t *testing.T) {
	req, err := http.NewRequest("GET", baseUrl+"/api/v1/nhc/99", nil)
	if err != nil {
		t.Fatal(err)
	}
	rr := httptest.NewRecorder()
	handler := http.HandlerFunc(nhc.GetNhcItem)
	handler.ServeHTTP(rr, req)
	expected := "light"
	var res types.Item
	json.Unmarshal(rr.Body.Bytes(), &res)
	if res.Name != expected {
		t.Errorf("getNhcItem failed: got %v, expect: %v", res, expected)
	}
}

func Test_getNhcItems(t *testing.T) {
	req, err := http.NewRequest("GET", baseUrl+"/api/v1/nhc", nil)
	if err != nil {
		t.Fatal(err)
	}
	rr := httptest.NewRecorder()
	handler := http.HandlerFunc(nhc.GetNhcItems)
	handler.ServeHTTP(rr, req)
	var found bool
	expected := "light"
	var res []types.Item
	json.Unmarshal(rr.Body.Bytes(), &res)
	for _, val := range res {
		if val.ID == 0 {
			if val.Name == expected {
				found = true
			}
		}
	}
	if !found {
		t.Error("GetNhcItems failed, expected light record not found")
	}
}

/* func Test_nhcCmd(t *testing.T) {
	expected := "Success"
	url := baseUrl + "/api/v1/nhc/action?id=1&value=100"
	hCli := http.Client{
		Timeout: time.Second * 2,
	}
	req, err := http.NewRequest(http.MethodPut, url, nil)
	if err != nil {
		t.Fatal(err)
	}
	//	req.Header.Set("User-Agent", "Test_nhcCmd")
	rsp, getErr := hCli.Do(req)
	if getErr != nil {
		fmt.Println(err)
	}
	got, readErr := ioutil.ReadAll(rsp.Body)
	if readErr != nil {
		fmt.Println("Read err: ", readErr)
	}
	//defer rsp.Body.Close()
	if string(got) != expected {
		t.Errorf("Test_nhcCmd failed, expecting %v, got %v", expected, string(got))
	} */

}
<commit_msg>fix issue with func comment<commit_after>package server_test

// TODO: review the http testing
import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/mch1307/gomotics/nhc"
	. "github.com/mch1307/gomotics/server"
	"github.com/mch1307/gomotics/testutil"
	"github.com/mch1307/gomotics/types"
)

var baseUrl string

//const healthMsg = `{"alive":true}`

func init() {
	fmt.Println("starting server test")
	baseUrl = "http://" + testutil.ConnectHost + ":8081"
	testutil.InitStubNHC()
}

func TestHealth(t *testing.T) {
	req, err := http.NewRequest("GET", baseUrl+"/health", nil)
	if err != nil {
		t.Fatal(err)
	}
	rr := httptest.NewRecorder()
	handler := http.HandlerFunc(Health)
	handler.ServeHTTP(rr, req)
	if rr.Body.String() != HealthMsg {
		t.Errorf("health test failed: got %v, expect: %v", rr.Body.String(), HealthMsg)
	}

}

// TODO: add more test cases (test non existing item)
func Test_getNhcItem(t *testing.T) {
	req, err := http.NewRequest("GET", baseUrl+"/api/v1/nhc/99", nil)
	if err != nil {
		t.Fatal(err)
	}
	rr := httptest.NewRecorder()
	handler := http.HandlerFunc(nhc.GetNhcItem)
	handler.ServeHTTP(rr, req)
	expected := "light"
	var res types.Item
	json.Unmarshal(rr.Body.Bytes(), &res)
	if res.Name != expected {
		t.Errorf("getNhcItem failed: got %v, expect: %v", res, expected)
	}
}

func Test_getNhcItems(t *testing.T) {
	req, err := http.NewRequest("GET", baseUrl+"/api/v1/nhc", nil)
	if err != nil {
		t.Fatal(err)
	}
	rr := httptest.NewRecorder()
	handler := http.HandlerFunc(nhc.GetNhcItems)
	handler.ServeHTTP(rr, req)
	var found bool
	expected := "light"
	var res []types.Item
	json.Unmarshal(rr.Body.Bytes(), &res)
	for _, val := range res {
		if val.ID == 0 {
			if val.Name == expected {
				found = true
			}
		}
	}
	if !found {
		t.Error("GetNhcItems failed, expected light record not found")
	}
}

/* func Test_nhcCmd(t *testing.T) {
	expected := "Success"
	url := baseUrl + "/api/v1/nhc/action?id=1&value=100"
	hCli := http.Client{
		Timeout: time.Second * 2,
	}
	req, err := http.NewRequest(http.MethodPut, url, nil)
	if err != nil {
		t.Fatal(err)
	}
	//	req.Header.Set("User-Agent", "Test_nhcCmd")
	rsp, getErr := hCli.Do(req)
	if getErr != nil {
		fmt.Println(err)
	}
	got, readErr := ioutil.ReadAll(rsp.Body)
	if readErr != nil {
		fmt.Println("Read err: ", readErr)
	}
	//defer rsp.Body.Close()
	if string(got) != expected {
		t.Errorf("Test_nhcCmd failed, expecting %v, got %v", expected, string(got))
	}

}*/
<|endoftext|>


<commit_before>// Copyright 2015 Ulrich Kunitz. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package lzma allows the decoding and encoding of LZMA streams.
//
// It provides a Writer and a Reader for decoding and encoding of LZMA
// stream with headers.
//
// The types Encoder and Decoder are provided to allow the decoding and
// encoding of raw LZMA streams without headers. The lzma2 package uses
// these types.
//
// The package is written completely in Go and doesn't rely on any external
// library.
package lzma
<commit_msg>lzma: updated package documentation<commit_after>// Copyright 2015 Ulrich Kunitz. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package lzma allows the decoding and encoding of LZMA streams. The
// Reader and Writer supports the decoding and encoding of LZMA streams
// with headers. The types Encoder and Decoder are provided to allow the
// decoding and encoding of raw LZMA streams without headers. The lzma2
// package uses these types.
//
// The package is written completely in Go and doesn't rely on any external
// library.
package lzma
<|endoftext|>


<commit_before>/*

static server is a development server for hosting your static client-side
files for the boardgame app. When you deploy, you just upload the bundled
output and set the ErrorPage to return index.html, and no server is necessary.

static server does a bit of magic during development. It presents a consistent
view of the world, but it actually shadows your local /webapp folder on top of
the package default /webapp folder. So if there's a hit in your /webapp, it
returns that. Otherwise, it defaults to the package /webapp.

The other magic it does is /static/config-src/boardgame-config.html is actually
fetched from /static/config-src/boardgame-config-dev.html, so you can have
different endpoints configured in production and in dev.

*/
package static

import (
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
	"os"
)

type Server struct{}

/*
NewServer returns a new server. Get it to run by calling Start().

Use it like so:

	func main() {
		static.NewServer().Start()
	}

*/
func NewServer() *Server {
	return &Server{}
}

//TODO: figure out a more dynamic way to figure out where the other resources are.
const (
	pathToLib = "$GOPATH/src/github.com/jkomoros/boardgame/server/static/"
)

func (s *Server) viewHandler(c *gin.Context) {
	//We serve the main app for every thing that we don't otherwise have a
	//handler for.
	c.HTML(http.StatusOK, "index.html", nil)

}

//ShadowedFS is a simple FileSystem that tries the first FS and if that fails falls back on the Secondary.
type shadowedFS struct {
	Primary   http.FileSystem
	Secondary http.FileSystem
	Redirects map[string]string
}

func (s *shadowedFS) Open(name string) (http.File, error) {

	for from, to := range s.Redirects {
		if name == from {
			log.Println("Found redirect for", name, "to", to)
			return s.Open(to)
		}
	}

	if file, err := s.Primary.Open(name); err == nil {
		log.Println("Serving", name, "from primary")
		return file, nil
	}
	log.Println("Attempting to serve", name, "from secondary")
	return s.Secondary.Open(name)
}

func newShadowedFS(primary http.FileSystem, secondary http.FileSystem) *shadowedFS {
	return &shadowedFS{
		Primary:   primary,
		Secondary: secondary,
		Redirects: make(map[string]string),
	}
}

//AddRedirect adds a redirect so whenever from is fetched, we'll actually
//return the result for to. Take care to not create loops!
func (s *shadowedFS) AddRedirect(from string, to string) {
	s.Redirects[from] = to
}

//Start is where you start the server, and it never returns until it's time to shut down.
func (s *Server) Start() {

	router := gin.Default()

	expandedPathToLib := os.ExpandEnv(pathToLib)

	router.NoRoute(s.viewHandler)

	router.LoadHTMLFiles(expandedPathToLib + "webapp/index.html")

	fs := newShadowedFS(http.Dir("webapp"), http.Dir(expandedPathToLib+"webapp"))

	fs.AddRedirect("/config-src/boardgame-config.html", "/config-src/boardgame-config-dev.html")

	router.StaticFS("/", fs)

	router.Run(":8080")

}
<commit_msg>STILL BROKEN, but now in a different way. Static hosting is done entirely in our NotFound handler, but some files (like polymer.html) load wonkily. part of #298.<commit_after>/*

static server is a development server for hosting your static client-side
files for the boardgame app. When you deploy, you just upload the bundled
output and set the ErrorPage to return index.html, and no server is necessary.

static server does a bit of magic during development. It presents a consistent
view of the world, but it actually shadows your local /webapp folder on top of
the package default /webapp folder. So if there's a hit in your /webapp, it
returns that. Otherwise, it defaults to the package /webapp.

The other magic it does is /static/config-src/boardgame-config.html is actually
fetched from /static/config-src/boardgame-config-dev.html, so you can have
different endpoints configured in production and in dev.

*/
package static

import (
	"github.com/gin-gonic/gin"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

type Server struct {
	fs http.FileSystem
}

/*
NewServer returns a new server. Get it to run by calling Start().

Use it like so:

	func main() {
		static.NewServer().Start()
	}

*/
func NewServer() *Server {
	return &Server{}
}

//TODO: figure out a more dynamic way to figure out where the other resources are.
const (
	pathToLib = "$GOPATH/src/github.com/jkomoros/boardgame/server/static/"
)

func (s *Server) staticHandler(c *gin.Context) {
	request := c.Request
	url := request.URL
	file, _ := s.fs.Open(url.String())

	if file != nil {

		contents, _ := ioutil.ReadAll(file)

		c.Status(http.StatusOK)

		c.Writer.Write(contents)
		return

	}

	c.HTML(http.StatusOK, "index.html", nil)
}

//ShadowedFS is a simple FileSystem that tries the first FS and if that fails falls back on the Secondary.
type shadowedFS struct {
	Primary   http.FileSystem
	Secondary http.FileSystem
	Redirects map[string]string
}

func (s *shadowedFS) Open(name string) (http.File, error) {

	for from, to := range s.Redirects {
		if name == from {
			log.Println("Found redirect for", name, "to", to)
			return s.Open(to)
		}
	}

	if file, err := s.Primary.Open(name); err == nil {
		log.Println("Serving", name, "from primary")
		return file, nil
	}
	log.Println("Attempting to serve", name, "from secondary")
	return s.Secondary.Open(name)
}

func newShadowedFS(primary http.FileSystem, secondary http.FileSystem) *shadowedFS {
	return &shadowedFS{
		Primary:   primary,
		Secondary: secondary,
		Redirects: make(map[string]string),
	}
}

//AddRedirect adds a redirect so whenever from is fetched, we'll actually
//return the result for to. Take care to not create loops!
func (s *shadowedFS) AddRedirect(from string, to string) {
	s.Redirects[from] = to
}

//Start is where you start the server, and it never returns until it's time to shut down.
func (s *Server) Start() {

	router := gin.Default()

	expandedPathToLib := os.ExpandEnv(pathToLib)

	router.NoRoute(s.staticHandler)

	router.LoadHTMLFiles(expandedPathToLib + "webapp/index.html")

	fs := newShadowedFS(http.Dir("webapp"), http.Dir(expandedPathToLib+"webapp"))

	fs.AddRedirect("/config-src/boardgame-config.html", "/config-src/boardgame-config-dev.html")

	s.fs = fs

	router.Run(":8080")

}
<|endoftext|>


<commit_before>// Copyright 2013 Joe Walnes and the websocketd team.
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
)

const (
	help = `
{{binary}} ({{version}})

{{binary}} is a command line tool that will allow any executable program
that accepts input on stdin and produces output on stdout to be turned into
a WebSocket server.

Usage:

  Export a single executable program a WebSocket server:
    {{binary}} [options] COMMAND [command args]

  Or, export an entire directory of executables as WebSocket endpoints:
    {{binary}} [options] --dir=SOMEDIR

Options:

  --port=PORT                    HTTP port to listen on.

  --address=ADDRESS              Address to bind to (multiple options allowed)
                                 Use square brackets to specify IPv6 address.
                                 Default: "" (all)

  --sameorigin={true,false}      Restrict (HTTP 403) protocol upgrades if the
                                 Origin header does not match to requested HTTP
                                 Host. Default: false.

  --origin=host[:port][,host[:port]...]
                                 Restrict (HTTP 403) protocol upgrades if the
                                 Origin header does not match to one of the host
                                 and port combinations listed. If the port is not
                                 specified, any port number will match.
                                 Default: "" (allow any origin)

  --ssl                          Listen for HTTPS socket instead of HTTP.
  --sslcert=FILE                 All three options must be used or all of
  --sslkey=FILE                  them should be omitted.

  --redirport=PORT               Open alternative port and redirect HTTP traffic
                                 from it to canonical address (mostly useful
                                 for HTTPS-only configurations to redirect HTTP
                                 traffic)

  --passenv VAR[,VAR...]         Lists environment variables allowed to be
                                 passed to executed scripts. Does not work for
																 Windows since all the variables are kept there.

  --binary={true,false}          Switches communication to binary, process reads
                                 send to browser as blobs and all reads from the
                                 browser are immediately flushed to the process.
                                 Default: false

  --reverselookup={true,false}   Perform DNS reverse lookups on remote clients.
                                 Default: false

  --dir=DIR                      Allow all scripts in the local directory
                                 to be accessed as WebSockets. If using this,
                                 option, then the standard program and args
                                 options should not be specified.

  --staticdir=DIR                Serve static files in this directory over HTTP.

  --cgidir=DIR                   Serve CGI scripts in this directory over HTTP.

  --maxforks=N                   Limit number of processes that websocketd is
                                 able to execute with WS and CGI handlers.
                                 When maxforks reached the server will be
                                 rejecting requests that require executing
                                 another process (unlimited when 0 or negative).
                                 Default: 0

  --closems=milliseconds         Specifies additional time process needs to gracefully
                                 finish before websocketd will send termination signals
                                 to it. Default: 0 (signals sent after 100ms, 250ms,
                                 and 500ms of waiting)

  --header="..."                 Set custom HTTP header to each answer. For
                                 example: --header="Server: someserver/0.0.1"

  --header-ws="...."             Same as --header, just applies to only those
                                 responses that indicate upgrade of TCP connection
                                 to a WebSockets protocol.

  --header-http="...."           Same as --header, just applies to only to plain
                                 HTTP responses that do not indicate WebSockets
                                 upgrade

  --help                         Print help and exit.

  --version                      Print version and exit.

  --license                      Print license and exit.

  --devconsole                   Enable interactive development console.
                                 This enables you to access the websocketd
                                 server with a web-browser and use a
                                 user interface to quickly test WebSocket
                                 endpoints. For example, to test an
                                 endpoint at ws://[host]/foo, you can
                                 visit http://[host]/foo in your browser.
                                 This flag cannot be used in conjunction
                                 with --staticdir or --cgidir.

  --loglevel=LEVEL               Log level to use (default access).
                                 From most to least verbose:
                                 debug, trace, access, info, error, fatal

Full documentation at http://websocketd.com/

Copyright 2013 Joe Walnes and the websocketd team. All rights reserved.
BSD license: Run '{{binary}} --license' for details.
`
	short = `
Usage:

  Export a single executable program a WebSocket server:
    {{binary}} [options] COMMAND [command args]

  Or, export an entire directory of executables as WebSocket endpoints:
    {{binary}} [options] --dir=SOMEDIR

  Or, show extended help message using:
    {{binary}} --help
`
)

func get_help_message(content string) string {
	msg := strings.Trim(content, " \n")
	msg = strings.Replace(msg, "{{binary}}", HelpProcessName(), -1)
	return strings.Replace(msg, "{{version}}", Version(), -1)
}

func HelpProcessName() string {
	binary := os.Args[0]
	if strings.Contains(binary, "/go-build") { // this was run using "go run", let's use something appropriate
		binary = "websocketd"
	} else {
		binary = filepath.Base(binary)
	}
	return binary
}

func PrintHelp() {
	fmt.Fprintf(os.Stderr, "%s\n", get_help_message(help))
}

func ShortHelp() {
	// Shown after some error
	fmt.Fprintf(os.Stderr, "\n%s\n", get_help_message(short))
}
<commit_msg>Fix long line breaking in help output<commit_after>// Copyright 2013 Joe Walnes and the websocketd team.
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
)

const (
	help = `
{{binary}} ({{version}})

{{binary}} is a command line tool that will allow any executable program
that accepts input on stdin and produces output on stdout to be turned into
a WebSocket server.

Usage:

  Export a single executable program a WebSocket server:
    {{binary}} [options] COMMAND [command args]

  Or, export an entire directory of executables as WebSocket endpoints:
    {{binary}} [options] --dir=SOMEDIR

Options:

  --port=PORT                    HTTP port to listen on.

  --address=ADDRESS              Address to bind to (multiple options allowed)
                                 Use square brackets to specify IPv6 address.
                                 Default: "" (all)

  --sameorigin={true,false}      Restrict (HTTP 403) protocol upgrades if the
                                 Origin header does not match to requested HTTP
                                 Host. Default: false.

  --origin=host[:port][,host[:port]...]
                                 Restrict (HTTP 403) protocol upgrades if the
                                 Origin header does not match to one of the host
                                 and port combinations listed. If the port is not
                                 specified, any port number will match.
                                 Default: "" (allow any origin)

  --ssl                          Listen for HTTPS socket instead of HTTP.
  --sslcert=FILE                 All three options must be used or all of
  --sslkey=FILE                  them should be omitted.

  --redirport=PORT               Open alternative port and redirect HTTP traffic
                                 from it to canonical address (mostly useful
                                 for HTTPS-only configurations to redirect HTTP
                                 traffic)

  --passenv VAR[,VAR...]         Lists environment variables allowed to be
                                 passed to executed scripts. Does not work for
                                 Windows since all the variables are kept there.

  --binary={true,false}          Switches communication to binary, process reads
                                 send to browser as blobs and all reads from the
                                 browser are immediately flushed to the process.
                                 Default: false

  --reverselookup={true,false}   Perform DNS reverse lookups on remote clients.
                                 Default: false

  --dir=DIR                      Allow all scripts in the local directory
                                 to be accessed as WebSockets. If using this,
                                 option, then the standard program and args
                                 options should not be specified.

  --staticdir=DIR                Serve static files in this directory over HTTP.

  --cgidir=DIR                   Serve CGI scripts in this directory over HTTP.

  --maxforks=N                   Limit number of processes that websocketd is
                                 able to execute with WS and CGI handlers.
                                 When maxforks reached the server will be
                                 rejecting requests that require executing
                                 another process (unlimited when 0 or negative).
                                 Default: 0

  --closems=milliseconds         Specifies additional time process needs to gracefully
                                 finish before websocketd will send termination signals
                                 to it. Default: 0 (signals sent after 100ms, 250ms,
                                 and 500ms of waiting)

  --header="..."                 Set custom HTTP header to each answer. For
                                 example: --header="Server: someserver/0.0.1"

  --header-ws="...."             Same as --header, just applies to only those
                                 responses that indicate upgrade of TCP connection
                                 to a WebSockets protocol.

  --header-http="...."           Same as --header, just applies to only to plain
                                 HTTP responses that do not indicate WebSockets
                                 upgrade

  --help                         Print help and exit.

  --version                      Print version and exit.

  --license                      Print license and exit.

  --devconsole                   Enable interactive development console.
                                 This enables you to access the websocketd
                                 server with a web-browser and use a
                                 user interface to quickly test WebSocket
                                 endpoints. For example, to test an
                                 endpoint at ws://[host]/foo, you can
                                 visit http://[host]/foo in your browser.
                                 This flag cannot be used in conjunction
                                 with --staticdir or --cgidir.

  --loglevel=LEVEL               Log level to use (default access).
                                 From most to least verbose:
                                 debug, trace, access, info, error, fatal

Full documentation at http://websocketd.com/

Copyright 2013 Joe Walnes and the websocketd team. All rights reserved.
BSD license: Run '{{binary}} --license' for details.
`
	short = `
Usage:

  Export a single executable program a WebSocket server:
    {{binary}} [options] COMMAND [command args]

  Or, export an entire directory of executables as WebSocket endpoints:
    {{binary}} [options] --dir=SOMEDIR

  Or, show extended help message using:
    {{binary}} --help
`
)

func get_help_message(content string) string {
	msg := strings.Trim(content, " \n")
	msg = strings.Replace(msg, "{{binary}}", HelpProcessName(), -1)
	return strings.Replace(msg, "{{version}}", Version(), -1)
}

func HelpProcessName() string {
	binary := os.Args[0]
	if strings.Contains(binary, "/go-build") { // this was run using "go run", let's use something appropriate
		binary = "websocketd"
	} else {
		binary = filepath.Base(binary)
	}
	return binary
}

func PrintHelp() {
	fmt.Fprintf(os.Stderr, "%s\n", get_help_message(help))
}

func ShortHelp() {
	// Shown after some error
	fmt.Fprintf(os.Stderr, "\n%s\n", get_help_message(short))
}
<|endoftext|>


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

package writ

import (
	"bytes"
	"fmt"
	"strings"
	"text/template"
)

var templateFuncs = map[string]interface{}{
	"formatCommand": formatCommand,
	"formatOption":  formatOption,
	"wrapText":      wrapText,
}

// The Help type is used for presentation purposes only, and does not affect
// argument parsing.
//
// The Command.ExitHelp() and Command.WriteHelp() methods execute the
// template assigned to the Template field, passing the Command as input.
// If the Template field is nil, the writ package's default template is used.
type Help struct {
	OptionGroups  []OptionGroup
	CommandGroups []CommandGroup

	// Optional
	Template *template.Template // Used to render output
	Usage    string             // Short message displayed at the top of output
	Header   string             // Displayed after Usage
	Footer   string             // Displayed at the end of output
}

// OptionGroup is used to customize help output.  It groups related Options
// for output.  When New() parses an input spec, it creates a single OptionGroup
// for all parsed options that have descriptions.
type OptionGroup struct {
	Options []*Option

	// Optional
	Name   string // Not displayed; for matching purposes within the template
	Header string // Displayed before the group
	Footer string // Displayed after the group
}

// CommandGroup is used to customize help output.  It groups related Commands
// for output.  When New() parses an input spec, it creates a single CommandGroup
// for all parsed commands that have descriptions.
type CommandGroup struct {
	Commands []*Command

	// Optional
	Name   string // Not displayed; for matching purposes within the template
	Header string // Displayed before the group
	Footer string // Displayed after the group
}

func formatOption(o *Option) string {
	var placeholder string
	if !o.Flag {
		placeholder = o.Placeholder
		if placeholder == "" {
			placeholder = "ARG"
		}
	}
	names := ""
	short := o.ShortNames()
	long := o.LongNames()
	for i, s := range short {
		names += "-" + s
		if (i < len(short)-1) || len(long) != 0 {
			names += ", "
		}
	}
	if len(long) == 0 && placeholder != "" {
		names += " " + placeholder
	}
	for i, l := range long {
		names += "--" + l
		if i < len(long)-1 {
			names += ", "
		} else if placeholder != "" {
			names += "=" + placeholder
		}
	}

	formatted := fmt.Sprintf("  %-24s  %s", names, o.Description)
	return wrapText(formatted, 80, 28)
}

func formatCommand(c *Command) string {
	formatted := fmt.Sprintf("  %-24s  %s", c.Name, c.Description)
	return wrapText(formatted, 80, 28)
}

// This is a pretty naiive implementation, but it's late and I'm tired
// TODO: cleanup and probably try to wrap on nearest space or punctuation
func wrapText(s string, width int, indent int) string {
	buf := bytes.NewBuffer(nil)
	runes := []rune(s)
	linelen, i := 0, 0
	for i < len(runes) {
		if linelen == width {
			buf.WriteString("\n")
			if i < len(runes) {
				buf.WriteString(strings.Repeat(" ", indent))
				linelen = indent
			}
		}
		buf.WriteRune(runes[i])
		i++
		linelen++
	}
	return buf.String()
}
<commit_msg>Fix wrapping for multi-line descriptions<commit_after>// Copyright (c) 2016 Bob Ziuchkovski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package writ

import (
	"bytes"
	"fmt"
	"strings"
	"text/template"
)

var templateFuncs = map[string]interface{}{
	"formatCommand": formatCommand,
	"formatOption":  formatOption,
	"wrapText":      wrapText,
}

// The Help type is used for presentation purposes only, and does not affect
// argument parsing.
//
// The Command.ExitHelp() and Command.WriteHelp() methods execute the
// template assigned to the Template field, passing the Command as input.
// If the Template field is nil, the writ package's default template is used.
type Help struct {
	OptionGroups  []OptionGroup
	CommandGroups []CommandGroup

	// Optional
	Template *template.Template // Used to render output
	Usage    string             // Short message displayed at the top of output
	Header   string             // Displayed after Usage
	Footer   string             // Displayed at the end of output
}

// OptionGroup is used to customize help output.  It groups related Options
// for output.  When New() parses an input spec, it creates a single OptionGroup
// for all parsed options that have descriptions.
type OptionGroup struct {
	Options []*Option

	// Optional
	Name   string // Not displayed; for matching purposes within the template
	Header string // Displayed before the group
	Footer string // Displayed after the group
}

// CommandGroup is used to customize help output.  It groups related Commands
// for output.  When New() parses an input spec, it creates a single CommandGroup
// for all parsed commands that have descriptions.
type CommandGroup struct {
	Commands []*Command

	// Optional
	Name   string // Not displayed; for matching purposes within the template
	Header string // Displayed before the group
	Footer string // Displayed after the group
}

func formatOption(o *Option) string {
	var placeholder string
	if !o.Flag {
		placeholder = o.Placeholder
		if placeholder == "" {
			placeholder = "ARG"
		}
	}
	names := ""
	short := o.ShortNames()
	long := o.LongNames()
	for i, s := range short {
		names += "-" + s
		if (i < len(short)-1) || len(long) != 0 {
			names += ", "
		}
	}
	if len(long) == 0 && placeholder != "" {
		names += " " + placeholder
	}
	for i, l := range long {
		names += "--" + l
		if i < len(long)-1 {
			names += ", "
		} else if placeholder != "" {
			names += "=" + placeholder
		}
	}

	formatted := fmt.Sprintf("  %-24s  %s", names, o.Description)
	return wrapText(formatted, 80, 28)
}

func formatCommand(c *Command) string {
	formatted := fmt.Sprintf("  %-24s  %s", c.Name, c.Description)
	return wrapText(formatted, 80, 28)
}

// This is a pretty naiive implementation, but it's late and I'm tired
// TODO: cleanup and probably try to wrap on nearest space or punctuation
func wrapText(s string, width int, indent int) string {
	buf := bytes.NewBuffer(nil)
	runes := []rune(s)
	linelen, i := 0, 0
	for i < len(runes) {
		if runes[i] == '\n' {
			buf.WriteString("\n")
			if i < len(runes) {
				buf.WriteString(strings.Repeat(" ", indent))
				linelen = indent
			}
		} else if linelen == width {
			buf.WriteString("\n")
			if i < len(runes) {
				buf.WriteString(strings.Repeat(" ", indent))
				linelen = indent
			}
			buf.WriteRune(runes[i])
		} else {
			buf.WriteRune(runes[i])
		}
		i++
		linelen++
	}
	return buf.String()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"runtime"
	"strconv"
	"syscall"
	"time"
)

var (
	loadAvgTypeToIdx = map[int]int{1: 0, 5: 1, 15: 2}
	nCPU             float64
)

func init() { nCPU = float64(runtime.NumCPU()) }

func osLoadAverages() (avgs [3]float64, err error) {
	text, err := ioutil.ReadFile("/proc/loadavg")
	if err != nil {
		return avgs, err
	}
	fields := bytes.Fields(text)
	if len(fields) < 3 {
		return avgs, fmt.Errorf("found fewer than 3 fields in /proc/loadavg")
	}
	for i := range avgs {
		avgs[i], err = strconv.ParseFloat(string(fields[i]), 64)
		if err != nil {
			return avgs, fmt.Errorf("error parsing /proc/loadavg: %s", err)
		}
	}
	return avgs, nil
}

func osGauge(name string, value float64) {
	incoming <- &Stat{
		Type:       StatGauge,
		Name:       "gost.os_stats" + name,
		Value:      value,
		SampleRate: 1.0,
	}
}

func reportLoadAverages() {
	if len(conf.OsStats.LoadAvg) == 0 && len(conf.OsStats.LoadAvgPerCPU) == 0 {
		return
	}
	loadAverages, err := osLoadAverages()
	if err != nil {
		metaCount("load_avg_check_failures")
		dbg.Println("failed to check OS load average:", err)
		return
	}
	for _, typ := range conf.OsStats.LoadAvg {
		osGauge(fmt.Sprintf("load_avg_%d", typ), loadAverages[loadAvgTypeToIdx[typ]])
	}

	for _, typ := range conf.OsStats.LoadAvgPerCPU {
		osGauge(fmt.Sprintf("load_avg_per_cpu_%d", typ), loadAverages[loadAvgTypeToIdx[typ]]/nCPU)
	}
}

func reportDiskUsage() {
	for name, options := range conf.OsStats.DiskUsage {
		buf := &syscall.Statfs_t{}
		if err := syscall.Statfs(options.Path, buf); err != nil {
			metaCount("disk_usage_check_failure")
			dbg.Printf("failed to check OS disk usage for %s at path %s\n", name, options.Path)
			return
		}
		usedBlocks := buf.Blocks - buf.Bavail // blocks used
		var used float64
		if options.Values == "absolute" {
			used = float64(usedBlocks * uint64(buf.Bsize)) // number of bytes used
		} else {
			used = float64(usedBlocks) / float64(buf.Blocks) // fraction of space used
		}
		osGauge("disk_usage"+name, used)
	}
}

func checkOsStats() {
	ticker := time.NewTicker(time.Duration(conf.OsStats.CheckIntervalMS) * time.Millisecond)
	for _ = range ticker.C {
		reportLoadAverages()
		reportDiskUsage()
	}
}
<commit_msg>Add some missing dots to OS stats<commit_after>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"runtime"
	"strconv"
	"syscall"
	"time"
)

var (
	loadAvgTypeToIdx = map[int]int{1: 0, 5: 1, 15: 2}
	nCPU             float64
)

func init() { nCPU = float64(runtime.NumCPU()) }

func osLoadAverages() (avgs [3]float64, err error) {
	text, err := ioutil.ReadFile("/proc/loadavg")
	if err != nil {
		return avgs, err
	}
	fields := bytes.Fields(text)
	if len(fields) < 3 {
		return avgs, fmt.Errorf("found fewer than 3 fields in /proc/loadavg")
	}
	for i := range avgs {
		avgs[i], err = strconv.ParseFloat(string(fields[i]), 64)
		if err != nil {
			return avgs, fmt.Errorf("error parsing /proc/loadavg: %s", err)
		}
	}
	return avgs, nil
}

func osGauge(name string, value float64) {
	incoming <- &Stat{
		Type:       StatGauge,
		Name:       "gost.os_stats." + name,
		Value:      value,
		SampleRate: 1.0,
	}
}

func reportLoadAverages() {
	if len(conf.OsStats.LoadAvg) == 0 && len(conf.OsStats.LoadAvgPerCPU) == 0 {
		return
	}
	loadAverages, err := osLoadAverages()
	if err != nil {
		metaCount("load_avg_check_failures")
		dbg.Println("failed to check OS load average:", err)
		return
	}
	for _, typ := range conf.OsStats.LoadAvg {
		osGauge(fmt.Sprintf("load_avg_%d", typ), loadAverages[loadAvgTypeToIdx[typ]])
	}

	for _, typ := range conf.OsStats.LoadAvgPerCPU {
		osGauge(fmt.Sprintf("load_avg_per_cpu_%d", typ), loadAverages[loadAvgTypeToIdx[typ]]/nCPU)
	}
}

func reportDiskUsage() {
	for name, options := range conf.OsStats.DiskUsage {
		buf := &syscall.Statfs_t{}
		if err := syscall.Statfs(options.Path, buf); err != nil {
			metaCount("disk_usage_check_failure")
			dbg.Printf("failed to check OS disk usage for %s at path %s\n", name, options.Path)
			return
		}
		usedBlocks := buf.Blocks - buf.Bavail // blocks used
		var used float64
		if options.Values == "absolute" {
			used = float64(usedBlocks * uint64(buf.Bsize)) // number of bytes used
		} else {
			used = float64(usedBlocks) / float64(buf.Blocks) // fraction of space used
		}
		osGauge("disk_usage."+name, used)
	}
}

func checkOsStats() {
	ticker := time.NewTicker(time.Duration(conf.OsStats.CheckIntervalMS) * time.Millisecond)
	for _ = range ticker.C {
		reportLoadAverages()
		reportDiskUsage()
	}
}
<|endoftext|>


<commit_before>package mackerel

import (
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"
)

const (
	defaultBaseURL    = "https://mackerel.io/api/v0"
	defaultUserAgent  = "mackerel-client-go"
	apiRequestTimeout = 30 * time.Second
)

// Client api client for mackerel
type Client struct {
	BaseURL           *url.URL
	APIKey            string
	Verbose           bool
	UserAgent         string
	AdditionalHeaders http.Header
}

func init() {
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
}

// NewClient returns new mackerel.Client
func NewClient(apikey string) *Client {
	u, _ := url.Parse(defaultBaseURL)
	return &Client{u, apikey, false, defaultUserAgent, http.Header{}}
}

// NewClientWithOptions returns new mackerel.Client
func NewClientWithOptions(apikey string, rawurl string, verbose bool) (*Client, error) {
	u, err := url.Parse(rawurl)
	if err != nil {
		return nil, err
	}
	return &Client{u, apikey, verbose, defaultUserAgent, http.Header{}}, nil
}

func (c *Client) urlFor(path string) *url.URL {
	newURL, err := url.Parse(c.BaseURL.String())
	if err != nil {
		panic("invalid url passed")
	}

	newURL.Path = path

	return newURL
}

func (c *Client) buildReq(req *http.Request) *http.Request {
	for header, values := range c.AdditionalHeaders {
		for _, v := range values {
			req.Header.Add(header, v)
		}
	}
	req.Header.Set("X-Api-Key", c.APIKey)
	req.Header.Set("User-Agent", c.UserAgent)
	return req
}

// Request request to mackerel and receive response
func (c *Client) Request(req *http.Request) (resp *http.Response, err error) {
	req = c.buildReq(req)

	if c.Verbose {
		dump, err := httputil.DumpRequest(req, true)
		if err == nil {
			log.Printf("%s", dump)
		}
	}

	client := &http.Client{} // same as http.DefaultClient
	client.Timeout = apiRequestTimeout
	resp, err = client.Do(req)
	if err != nil {
		return nil, err
	}
	if c.Verbose {
		dump, err := httputil.DumpResponse(resp, true)
		if err == nil {
			log.Printf("%s", dump)
		}
	}
	return resp, nil
}
<commit_msg>Improve error message about response status code<commit_after>package mackerel

import (
	"errors"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"
)

const (
	defaultBaseURL    = "https://mackerel.io/api/v0"
	defaultUserAgent  = "mackerel-client-go"
	apiRequestTimeout = 30 * time.Second
)

// Client api client for mackerel
type Client struct {
	BaseURL           *url.URL
	APIKey            string
	Verbose           bool
	UserAgent         string
	AdditionalHeaders http.Header
}

func init() {
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
}

// NewClient returns new mackerel.Client
func NewClient(apikey string) *Client {
	u, _ := url.Parse(defaultBaseURL)
	return &Client{u, apikey, false, defaultUserAgent, http.Header{}}
}

// NewClientWithOptions returns new mackerel.Client
func NewClientWithOptions(apikey string, rawurl string, verbose bool) (*Client, error) {
	u, err := url.Parse(rawurl)
	if err != nil {
		return nil, err
	}
	return &Client{u, apikey, verbose, defaultUserAgent, http.Header{}}, nil
}

func (c *Client) urlFor(path string) *url.URL {
	newURL, err := url.Parse(c.BaseURL.String())
	if err != nil {
		panic("invalid url passed")
	}

	newURL.Path = path

	return newURL
}

func (c *Client) buildReq(req *http.Request) *http.Request {
	for header, values := range c.AdditionalHeaders {
		for _, v := range values {
			req.Header.Add(header, v)
		}
	}
	req.Header.Set("X-Api-Key", c.APIKey)
	req.Header.Set("User-Agent", c.UserAgent)
	return req
}

// Request request to mackerel and receive response
func (c *Client) Request(req *http.Request) (resp *http.Response, err error) {
	req = c.buildReq(req)

	if c.Verbose {
		dump, err := httputil.DumpRequest(req, true)
		if err == nil {
			log.Printf("%s", dump)
		}
	}

	client := &http.Client{} // same as http.DefaultClient
	client.Timeout = apiRequestTimeout
	resp, err = client.Do(req)
	if err != nil {
		return nil, err
	}
	if c.Verbose {
		dump, err := httputil.DumpResponse(resp, true)
		if err == nil {
			log.Printf("%s", dump)
		}
	}
	if resp.StatusCode != 200 {
		return nil, errors.New(resp.Status)
	}
	return resp, nil
}
<|endoftext|>


<commit_before>package checkdisk

import (
	"errors"
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"

	"github.com/jessevdk/go-flags"
	"github.com/mackerelio/checkers"
	gpud "github.com/shirou/gopsutil/v3/disk"
)

var opts struct {
	Warning       *string   `short:"w" long:"warning" value-name:"N, N%" description:"Exit with WARNING status if less than N units or N% of disk are free"`
	Critical      *string   `short:"c" long:"critical" value-name:"N, N%" description:"Exit with CRITICAL status if less than N units or N% of disk are free"`
	InodeWarning  *string   `short:"W" long:"iwarning" value-name:"N%" description:"Exit with WARNING status if less than PERCENT of inode space is free"`
	InodeCritical *string   `short:"K" long:"icritical" value-name:"N%" description:"Exit with CRITICAL status if less than PERCENT of inode space is free"`
	Path          *[]string `short:"p" long:"path" value-name:"PATH" description:"Mount point or block device as emitted by the mount(8) command (may be repeated)"`
	Exclude       *[]string `short:"x" long:"exclude-device" value-name:"EXCLUDE PATH" description:"Ignore device (may be repeated; only works if -p unspecified)"`
	All           bool      `short:"A" long:"all" description:"Explicitly select all paths."`
	ExcludeType   *[]string `short:"X" long:"exclude-type" value-name:"TYPE" description:"Ignore all filesystems of indicated type (may be repeated)"`
	IncludeType   *[]string `short:"N" long:"include-type" value-name:"TYPE" description:"Check only filesystems of indicated type (may be repeated)"`
	Units         *string   `short:"u" long:"units" value-name:"STRING" description:"Choose bytes, kB, MB, GB, TB (default: MB)"`
}

const (
	b  = float64(1)
	kb = float64(1024) * b
	mb = float64(1024) * kb
	gb = float64(1024) * mb
	tb = float64(1024) * gb
)

type unit struct {
	Name string
	Size float64
}

func checkDisk(current checkers.Status, threshold string, units float64, disk *gpud.UsageStat, status checkers.Status) (checkers.Status, error) {
	if strings.HasSuffix(threshold, "%") {
		thresholdPct, err := strconv.ParseFloat(strings.TrimRight(threshold, "%"), 64)
		if err != nil {
			return checkers.UNKNOWN, err
		}

		freePct := float64(100) - disk.UsedPercent
		if thresholdPct > freePct {
			current = status
		}
	} else {
		thresholdVal, err := strconv.ParseFloat(threshold, 64)
		if err != nil {
			return checkers.UNKNOWN, err
		}

		free := float64(disk.Free) / units
		if thresholdVal > free {
			current = status
		}
	}

	return current, nil
}

func checkInodes(current checkers.Status, threshold string, disk *gpud.UsageStat, status checkers.Status) (checkers.Status, error) {
	if !strings.HasSuffix(threshold, "%") {
		return checkers.UNKNOWN, errors.New("-W, -K value should be N%")
	}

	thresholdPct, err := strconv.ParseFloat(strings.TrimRight(threshold, "%"), 64)
	if err != nil {
		return checkers.UNKNOWN, err
	}

	if disk.InodesTotal == 0 {
		return checkers.UNKNOWN, fmt.Errorf("Disk %s does not have inodes", disk.Path)
	}

	inodesFreePct := float64(100) - disk.InodesUsedPercent
	if thresholdPct > inodesFreePct {
		current = status
	}

	return current, nil
}

func genMessage(disk *gpud.UsageStat, u unit) string {
	all := float64(disk.Total) / u.Size
	used := float64(disk.Used) / u.Size
	free := float64(disk.Free) / u.Size
	freePct := float64(100) - disk.UsedPercent

	inodesFreePctStr := ""
	if disk.InodesTotal != 0 {
		inodesFreePct := float64(100) - disk.InodesUsedPercent
		inodesFreePctStr = fmt.Sprintf(" (inodes %.2f)", inodesFreePct)
	}

	return fmt.Sprintf("Path: %v, All: %.2f %v, Used: %.2f %v, Free: %.2f %v, Free percentage: %.2f%s", disk.Path, all, u.Name, used, u.Name, free, u.Name, freePct, inodesFreePctStr)
}

// Do the plugin
func Do() {
	ckr := run(os.Args[1:])
	ckr.Name = "Disk"
	ckr.Exit()
}

func run(args []string) *checkers.Checker {
	_, err := flags.ParseArgs(&opts, args)
	if err != nil {
		os.Exit(1)
	}

	partitions, err := listPartitions()
	if err != nil {
		return checkers.Unknown(fmt.Sprintf("Failed to fetch partitions: %s", err))
	}

	if !opts.All {
		// Filtering partitions by Fstype
		if opts.IncludeType != nil {
			partitions = filterPartitionsByInclusion(partitions, *opts.IncludeType, fstypeOfPartition)
			if len(partitions) == 0 {
				return checkers.Unknown(fmt.Sprintf("Failed to fetch partitions: %s", errors.New("No device found for the specified *FsType*")))
			}
		}

		if opts.ExcludeType != nil {
			partitions = filterPartitionsByExclusion(partitions, *opts.ExcludeType, fstypeOfPartition)
		}

		// Filtering partions by Mountpoint
		if opts.Path != nil {
			if opts.Exclude != nil {
				return checkers.Unknown(fmt.Sprintf("Invalid arguments: %s", errors.New("-x does not work with -p")))
			}

			partitions = filterPartitionsByInclusion(partitions, *opts.Path, mountpointOfPartition)
			if len(partitions) == 0 {
				return checkers.Unknown(fmt.Sprintf("Failed to fetch partitions: %s", errors.New("No device found for the specified *Mountpoint*")))
			}
		}

		if opts.Path == nil && opts.Exclude != nil {
			partitions = filterPartitionsByExclusion(partitions, *opts.Exclude, mountpointOfPartition)
		}
	}

	if len(partitions) == 0 {
		return checkers.Unknown(fmt.Sprintf("Failed to fetch partitions: %s", errors.New("No device found")))
	}

	var disks []*gpud.UsageStat

	for _, partition := range partitions {
		disk, err := gpud.Usage(partition.Mountpoint)
		if err != nil {
			return checkers.Unknown(fmt.Sprintf("%s[type=%s]: Failed to fetch disk usage: %s", partition.Mountpoint, partition.Fstype, err))
		}

		if disk.Total != 0 {
			disks = append(disks, disk)
		}
	}

	u := unit{"MB", mb}
	if opts.Units != nil {
		us := strings.ToLower(*opts.Units)
		if us == "bytes" {
			u = unit{us, b}
		} else if us == "kb" {
			u = unit{us, mb}
		} else if us == "gb" {
			u = unit{us, gb}
		} else if us == "tb" {
			u = unit{us, tb}
		} else {
			return checkers.Unknown(fmt.Sprintf("Failed to check disk status: %s", errors.New("Invalid argument flag '-u, --units'")))
		}
	}

	checkSt := checkers.OK
	if opts.InodeCritical != nil {
		for _, disk := range disks {
			checkSt, err = checkInodes(checkSt, *opts.InodeCritical, disk, checkers.CRITICAL)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("%s: Failed to check disk status: %s", disk.Path, err))
			}

			if checkSt == checkers.CRITICAL {
				break
			}
		}
	}

	if checkSt != checkers.CRITICAL && opts.Critical != nil {
		for _, disk := range disks {
			checkSt, err = checkDisk(checkSt, *opts.Critical, u.Size, disk, checkers.CRITICAL)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("%s: Failed to check disk status: %s", disk.Path, err))
			}

			if checkSt == checkers.CRITICAL {
				break
			}
		}
	}

	if checkSt != checkers.CRITICAL && opts.InodeWarning != nil {
		for _, disk := range disks {
			checkSt, err = checkInodes(checkSt, *opts.InodeWarning, disk, checkers.WARNING)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("%s: Failed to check disk status: %s", disk.Path, err))
			}

			if checkSt == checkers.WARNING {
				break
			}
		}
	}

	if checkSt == checkers.OK && opts.Warning != nil {
		for _, disk := range disks {
			checkSt, err = checkDisk(checkSt, *opts.Warning, u.Size, disk, checkers.WARNING)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("%s: Failed to check disk status: %s", disk.Path, err))
			}

			if checkSt == checkers.WARNING {
				break
			}
		}
	}

	sortDisks(disks)

	var msgs []string

	for _, disk := range disks {
		msg := genMessage(disk, u)
		msgs = append(msgs, msg)
	}
	msgss := strings.Join(msgs, ";\n")

	return checkers.NewChecker(checkSt, msgss)
}

// ref: mountlist.c in gnulib
// https://github.com/coreutils/gnulib/blob/a742bdb3/lib/mountlist.c#L168
func listPartitions() ([]gpud.PartitionStat, error) {
	allPartitions, err := gpud.Partitions(true)
	if err != nil {
		return nil, err
	}
	partitions := make([]gpud.PartitionStat, 0, len(allPartitions))
	for _, p := range allPartitions {
		switch p.Fstype {
		case "autofs",
			"proc",
			"subfs",
			"debugfs",
			"devpts",
			"fusectl",
			"mqueue",
			"rpc_pipefs",
			"sysfs",
			"devfs",
			"kernfs",
			"ignore":
			continue
		case "none":
			if !isBindMount(p.Opts) {
				continue
			}
			partitions = append(partitions, p)
		default:
			partitions = append(partitions, p)
		}
	}

	return partitions, nil
}

func isBindMount(mountOpts []string) bool {
	for _, opt := range mountOpts {
		if opt == "bind" {
			return true
		}
	}
	return false
}

func mountpointOfPartition(partition gpud.PartitionStat) string {
	return partition.Mountpoint
}

func fstypeOfPartition(partition gpud.PartitionStat) string {
	return partition.Fstype
}

func filterPartitionsByInclusion(partitions []gpud.PartitionStat, list []string, key func(_ gpud.PartitionStat) string) []gpud.PartitionStat {
	newPartitions := make([]gpud.PartitionStat, 0, len(partitions))
	for _, partition := range partitions {
		var ok = false
		for _, l := range list {
			if l == key(partition) {
				ok = true
				break
			}
		}
		if ok {
			newPartitions = append(newPartitions, partition)
		}
	}

	return newPartitions
}

func filterPartitionsByExclusion(partitions []gpud.PartitionStat, list []string, key func(_ gpud.PartitionStat) string) []gpud.PartitionStat {
	newPartitions := make([]gpud.PartitionStat, 0, len(partitions))
	for _, partition := range partitions {
		var ok = true
		for _, l := range list {
			if l == key(partition) {
				ok = false
				break
			}
		}
		if ok {
			newPartitions = append(newPartitions, partition)
		}
	}

	return newPartitions
}

// sortDisks sorts by UsedPercent only at the moment. If this is refactored, ideally this sort would match what threshold was tripped.
func sortDisks(disks []*gpud.UsageStat) {
	if len(disks) <= 1 {
		return
	}

	sort.SliceStable(disks, func(i, j int) bool {
		return disks[i].UsedPercent > disks[j].UsedPercent
	})

}
<commit_msg>[check-disk] ignores fuse.portal partitions<commit_after>package checkdisk

import (
	"errors"
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"

	"github.com/jessevdk/go-flags"
	"github.com/mackerelio/checkers"
	gpud "github.com/shirou/gopsutil/v3/disk"
)

var opts struct {
	Warning       *string   `short:"w" long:"warning" value-name:"N, N%" description:"Exit with WARNING status if less than N units or N% of disk are free"`
	Critical      *string   `short:"c" long:"critical" value-name:"N, N%" description:"Exit with CRITICAL status if less than N units or N% of disk are free"`
	InodeWarning  *string   `short:"W" long:"iwarning" value-name:"N%" description:"Exit with WARNING status if less than PERCENT of inode space is free"`
	InodeCritical *string   `short:"K" long:"icritical" value-name:"N%" description:"Exit with CRITICAL status if less than PERCENT of inode space is free"`
	Path          *[]string `short:"p" long:"path" value-name:"PATH" description:"Mount point or block device as emitted by the mount(8) command (may be repeated)"`
	Exclude       *[]string `short:"x" long:"exclude-device" value-name:"EXCLUDE PATH" description:"Ignore device (may be repeated; only works if -p unspecified)"`
	All           bool      `short:"A" long:"all" description:"Explicitly select all paths."`
	ExcludeType   *[]string `short:"X" long:"exclude-type" value-name:"TYPE" description:"Ignore all filesystems of indicated type (may be repeated)"`
	IncludeType   *[]string `short:"N" long:"include-type" value-name:"TYPE" description:"Check only filesystems of indicated type (may be repeated)"`
	Units         *string   `short:"u" long:"units" value-name:"STRING" description:"Choose bytes, kB, MB, GB, TB (default: MB)"`
}

const (
	b  = float64(1)
	kb = float64(1024) * b
	mb = float64(1024) * kb
	gb = float64(1024) * mb
	tb = float64(1024) * gb
)

type unit struct {
	Name string
	Size float64
}

func checkDisk(current checkers.Status, threshold string, units float64, disk *gpud.UsageStat, status checkers.Status) (checkers.Status, error) {
	if strings.HasSuffix(threshold, "%") {
		thresholdPct, err := strconv.ParseFloat(strings.TrimRight(threshold, "%"), 64)
		if err != nil {
			return checkers.UNKNOWN, err
		}

		freePct := float64(100) - disk.UsedPercent
		if thresholdPct > freePct {
			current = status
		}
	} else {
		thresholdVal, err := strconv.ParseFloat(threshold, 64)
		if err != nil {
			return checkers.UNKNOWN, err
		}

		free := float64(disk.Free) / units
		if thresholdVal > free {
			current = status
		}
	}

	return current, nil
}

func checkInodes(current checkers.Status, threshold string, disk *gpud.UsageStat, status checkers.Status) (checkers.Status, error) {
	if !strings.HasSuffix(threshold, "%") {
		return checkers.UNKNOWN, errors.New("-W, -K value should be N%")
	}

	thresholdPct, err := strconv.ParseFloat(strings.TrimRight(threshold, "%"), 64)
	if err != nil {
		return checkers.UNKNOWN, err
	}

	if disk.InodesTotal == 0 {
		return checkers.UNKNOWN, fmt.Errorf("Disk %s does not have inodes", disk.Path)
	}

	inodesFreePct := float64(100) - disk.InodesUsedPercent
	if thresholdPct > inodesFreePct {
		current = status
	}

	return current, nil
}

func genMessage(disk *gpud.UsageStat, u unit) string {
	all := float64(disk.Total) / u.Size
	used := float64(disk.Used) / u.Size
	free := float64(disk.Free) / u.Size
	freePct := float64(100) - disk.UsedPercent

	inodesFreePctStr := ""
	if disk.InodesTotal != 0 {
		inodesFreePct := float64(100) - disk.InodesUsedPercent
		inodesFreePctStr = fmt.Sprintf(" (inodes %.2f)", inodesFreePct)
	}

	return fmt.Sprintf("Path: %v, All: %.2f %v, Used: %.2f %v, Free: %.2f %v, Free percentage: %.2f%s", disk.Path, all, u.Name, used, u.Name, free, u.Name, freePct, inodesFreePctStr)
}

// Do the plugin
func Do() {
	ckr := run(os.Args[1:])
	ckr.Name = "Disk"
	ckr.Exit()
}

func run(args []string) *checkers.Checker {
	_, err := flags.ParseArgs(&opts, args)
	if err != nil {
		os.Exit(1)
	}

	partitions, err := listPartitions()
	if err != nil {
		return checkers.Unknown(fmt.Sprintf("Failed to fetch partitions: %s", err))
	}

	if !opts.All {
		// Filtering partitions by Fstype
		if opts.IncludeType != nil {
			partitions = filterPartitionsByInclusion(partitions, *opts.IncludeType, fstypeOfPartition)
			if len(partitions) == 0 {
				return checkers.Unknown(fmt.Sprintf("Failed to fetch partitions: %s", errors.New("No device found for the specified *FsType*")))
			}
		}

		if opts.ExcludeType != nil {
			partitions = filterPartitionsByExclusion(partitions, *opts.ExcludeType, fstypeOfPartition)
		}

		// Filtering partions by Mountpoint
		if opts.Path != nil {
			if opts.Exclude != nil {
				return checkers.Unknown(fmt.Sprintf("Invalid arguments: %s", errors.New("-x does not work with -p")))
			}

			partitions = filterPartitionsByInclusion(partitions, *opts.Path, mountpointOfPartition)
			if len(partitions) == 0 {
				return checkers.Unknown(fmt.Sprintf("Failed to fetch partitions: %s", errors.New("No device found for the specified *Mountpoint*")))
			}
		}

		if opts.Path == nil && opts.Exclude != nil {
			partitions = filterPartitionsByExclusion(partitions, *opts.Exclude, mountpointOfPartition)
		}
	}

	if len(partitions) == 0 {
		return checkers.Unknown(fmt.Sprintf("Failed to fetch partitions: %s", errors.New("No device found")))
	}

	var disks []*gpud.UsageStat

	for _, partition := range partitions {
		disk, err := gpud.Usage(partition.Mountpoint)
		if err != nil {
			return checkers.Unknown(fmt.Sprintf("%s[type=%s]: Failed to fetch disk usage: %s", partition.Mountpoint, partition.Fstype, err))
		}

		if disk.Total != 0 {
			disks = append(disks, disk)
		}
	}

	u := unit{"MB", mb}
	if opts.Units != nil {
		us := strings.ToLower(*opts.Units)
		if us == "bytes" {
			u = unit{us, b}
		} else if us == "kb" {
			u = unit{us, mb}
		} else if us == "gb" {
			u = unit{us, gb}
		} else if us == "tb" {
			u = unit{us, tb}
		} else {
			return checkers.Unknown(fmt.Sprintf("Failed to check disk status: %s", errors.New("Invalid argument flag '-u, --units'")))
		}
	}

	checkSt := checkers.OK
	if opts.InodeCritical != nil {
		for _, disk := range disks {
			checkSt, err = checkInodes(checkSt, *opts.InodeCritical, disk, checkers.CRITICAL)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("%s: Failed to check disk status: %s", disk.Path, err))
			}

			if checkSt == checkers.CRITICAL {
				break
			}
		}
	}

	if checkSt != checkers.CRITICAL && opts.Critical != nil {
		for _, disk := range disks {
			checkSt, err = checkDisk(checkSt, *opts.Critical, u.Size, disk, checkers.CRITICAL)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("%s: Failed to check disk status: %s", disk.Path, err))
			}

			if checkSt == checkers.CRITICAL {
				break
			}
		}
	}

	if checkSt != checkers.CRITICAL && opts.InodeWarning != nil {
		for _, disk := range disks {
			checkSt, err = checkInodes(checkSt, *opts.InodeWarning, disk, checkers.WARNING)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("%s: Failed to check disk status: %s", disk.Path, err))
			}

			if checkSt == checkers.WARNING {
				break
			}
		}
	}

	if checkSt == checkers.OK && opts.Warning != nil {
		for _, disk := range disks {
			checkSt, err = checkDisk(checkSt, *opts.Warning, u.Size, disk, checkers.WARNING)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("%s: Failed to check disk status: %s", disk.Path, err))
			}

			if checkSt == checkers.WARNING {
				break
			}
		}
	}

	sortDisks(disks)

	var msgs []string

	for _, disk := range disks {
		msg := genMessage(disk, u)
		msgs = append(msgs, msg)
	}
	msgss := strings.Join(msgs, ";\n")

	return checkers.NewChecker(checkSt, msgss)
}

// ref: mountlist.c in gnulib
// https://github.com/coreutils/gnulib/blob/df336dc/lib/mountlist.c#L164
func listPartitions() ([]gpud.PartitionStat, error) {
	allPartitions, err := gpud.Partitions(true)
	if err != nil {
		return nil, err
	}
	partitions := make([]gpud.PartitionStat, 0, len(allPartitions))
	for _, p := range allPartitions {
		switch p.Fstype {
		case "autofs",
			"proc",
			"subfs",
			"debugfs",
			"devpts",
			"fusectl",
			"fuse.portal",
			"mqueue",
			"rpc_pipefs",
			"sysfs",
			"devfs",
			"kernfs",
			"ignore":
			continue
		case "none":
			if !isBindMount(p.Opts) {
				continue
			}
			partitions = append(partitions, p)
		default:
			partitions = append(partitions, p)
		}
	}

	return partitions, nil
}

func isBindMount(mountOpts []string) bool {
	for _, opt := range mountOpts {
		if opt == "bind" {
			return true
		}
	}
	return false
}

func mountpointOfPartition(partition gpud.PartitionStat) string {
	return partition.Mountpoint
}

func fstypeOfPartition(partition gpud.PartitionStat) string {
	return partition.Fstype
}

func filterPartitionsByInclusion(partitions []gpud.PartitionStat, list []string, key func(_ gpud.PartitionStat) string) []gpud.PartitionStat {
	newPartitions := make([]gpud.PartitionStat, 0, len(partitions))
	for _, partition := range partitions {
		var ok = false
		for _, l := range list {
			if l == key(partition) {
				ok = true
				break
			}
		}
		if ok {
			newPartitions = append(newPartitions, partition)
		}
	}

	return newPartitions
}

func filterPartitionsByExclusion(partitions []gpud.PartitionStat, list []string, key func(_ gpud.PartitionStat) string) []gpud.PartitionStat {
	newPartitions := make([]gpud.PartitionStat, 0, len(partitions))
	for _, partition := range partitions {
		var ok = true
		for _, l := range list {
			if l == key(partition) {
				ok = false
				break
			}
		}
		if ok {
			newPartitions = append(newPartitions, partition)
		}
	}

	return newPartitions
}

// sortDisks sorts by UsedPercent only at the moment. If this is refactored, ideally this sort would match what threshold was tripped.
func sortDisks(disks []*gpud.UsageStat) {
	if len(disks) <= 1 {
		return
	}

	sort.SliceStable(disks, func(i, j int) bool {
		return disks[i].UsedPercent > disks[j].UsedPercent
	})

}
<|endoftext|>