<commit_before>package main

// vim: set noexpandtab:

import (
	"bytes"
	"code.google.com/p/go.crypto/ssh/terminal"
	//"crypto/rand"
	"crypto/sha512"
	"encoding/base64"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/gokyle/cryptobox/box"
	"github.com/gokyle/cryptobox/secretbox"
	"hash"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
)

const (
	VERSION  string = "0.0.6-dev"
	jsonMime string = "application/json"
	_               = iota
	E_SERVER uint   = 1 << iota
	E_AUTH
	E_PASSPHRASE
	E_NOKEY
	E_BADKEY
)

var stdout *log.Logger

var forceInsecure bool
var DEBUG bool
var username string
var baseUrl string
var confPath string
var api map[string]*url.URL

var keyFile string
var myId int64
var client *http.Client
var srvKey box.PublicKey

type internalError struct {
	Code uint
}

func (i *internalError) Error() string {
	var ret string
	switch i.Code {
	case E_SERVER:
		ret = "vault: server returned HTTP 500"
	case E_AUTH:
		ret = "vault: could not authenticate"
	case E_PASSPHRASE:
		ret = "vault: bad passphrase"
	case E_NOKEY:
		ret = "vault: no key"
	case E_BADKEY:
		ret = "vault: key malformed/corrupted/(un)encrypted"
	default:
		ret = "vault: unknown error"
	}
	return ret
}

type User struct {
	Id   int64
	Name string
	//PubKey   box.PublicKey
	PubKey   []byte
	Creation time.Time
	Admin    bool
}

type Group struct {
	Id        int64
	Name      string
	Admin     []int64
	Member    []int64
	UserGroup bool
}

func decodeBase64(in string) []byte {
	out := make([]byte, base64.StdEncoding.DecodedLen(len(in)))
	n, err := base64.StdEncoding.Decode(out, []byte(in))
	if err != nil {
		return nil
	}
	return out[0:n]
}

func getPassphrase(prompt string) []byte {
	var h hash.Hash = sha512.New384()
	fmt.Fprintf(os.Stdout, prompt)
	phrase, err := terminal.ReadPassword(int(os.Stdin.Fd()))
	fmt.Fprintf(os.Stdout, "\n")
	if err != nil {
		log.Fatal(err)
	}
	io.Copy(h, bytes.NewReader(phrase))
	return h.Sum(nil)
}

func fetchServerKey() (box.PublicKey, error) {
	var key *bytes.Buffer = new(bytes.Buffer)
	resp, err := http.Get(api["key"].String())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.ContentLength <= 0 {
		log.Fatal("key ContentLength <= 0")
	}
	// TODO: error check
	io.Copy(key, resp.Body)

	return box.PublicKey(key.Bytes()), nil
}

func loadKey() (box.PrivateKey, error) {
	var err error
	var raw []byte
	i, err := os.Stat(keyFile)
	if err != nil {
		os.MkdirAll(confPath, 0700)
		log.Fatalf("Couldn't find Private key, looked for %s\n", keyFile)
	}

	raw = make([]byte, i.Size())

	f, err := os.Open(keyFile)
	if err != nil {
		return nil, err
	}
	if _, err = io.ReadFull(f, raw); err != nil {
		return nil, err
	}

	//if !box.KeyIsSuitable(raw) {
	//	phrase := getPassphrase("Passphrase: ")
	//	if !secretbox.KeyIsSuitable(phrase) {
	//		return nil, &internalError{E_BADKEY}
	//	}

	//	ret, ok := secretbox.Open(raw, secretbox.Key(phrase))
	//	if !ok {
	//		return nil, &internalError{E_BADKEY}
	//	}

	//	if !box.KeyIsSuitable(ret) {
	//		return nil, &internalError{E_BADKEY}
	//	}
	//	return box.PrivateKey(ret), nil
	//} else {
	stdout.Printf("This key is unencrypted!\nEncrypt this key with: '%s encrypt'\n", os.Args[0])
	return box.PrivateKey(raw), nil
	//}
}

func encryptKey(key box.PrivateKey) error {
	phrase := getPassphrase("New Passphrase: ")

	box, ok := secretbox.Seal([]byte(key), phrase)
	if !ok {
		return &internalError{E_BADKEY}
	}

	err := ioutil.WriteFile(keyFile, box, 0600)
	if err != nil {
		log.Fatal(err)
	}
	return nil
}

func login(privKey box.PrivateKey) error {
	var err error
	var challenge []byte
	var ok bool
	var jar *cookiejar.Jar
	c := make(map[string]string)
	// Step1: request challenge
	resp, err := http.Get(fmt.Sprintf("%s/%s", api["auth"].String(), username))
	if err != nil {
		return err
	}
	d := json.NewDecoder(resp.Body)
	defer resp.Body.Close()
	d.Decode(&c)
	data := decodeBase64(c["challenge"])
	if box.BoxIsSigned(data) {
		log.Println("opening signed box...")
		challenge, ok = box.OpenAndVerify(data, privKey, srvKey)
	} else {
		log.Println("opening box...")
		challenge, ok = box.Open(data, privKey)
	}
	if !ok {
		log.Println("unboxing returned not ok")
		return &internalError{E_AUTH}
	}

	// Step2: validate challenge
	step2, err := json.Marshal(&map[string]string{"id": c["id"], "challenge": base64.StdEncoding.EncodeToString(challenge)})
	if err != nil {
		return err
	}
	resp, err = http.Post(api["auth"].String(), jsonMime, strings.NewReader(string(step2)))
	if err != nil {
		return err
	}
	if resp.StatusCode == http.StatusUnauthorized {
		return &internalError{E_AUTH}
	}
	if resp.StatusCode != http.StatusOK {
		return &internalError{E_SERVER}
	}

	jar, err = cookiejar.New(nil)
	if err != nil {
		log.Fatalf("Failed opening cookiejar: %v\n", err)
	}
	jar.SetCookies(api["/"], resp.Cookies())
	myId, _ = strconv.ParseInt(c["id"], 10, 64)
	client = &http.Client{Jar: jar}

	return nil
}

func isValid() bool {
	res, err := client.Get(api["valid"].String())
	if err != nil {
		return false
	}
	return res.StatusCode == http.StatusOK
}

func fetchUser(id int64) (*User, error) {
	var u User = User{}
	res, err := client.Get(fmt.Sprintf("%s/%d", api["user"].String(), id))
	if err != nil {
		return nil, err
	}
	d := json.NewDecoder(res.Body)
	defer res.Body.Close()
	d.Decode(&u)
	return &u, nil
}

func searchUser(name string) ([]int64, error) {
	var u struct{ Id []int64 }
	v := url.Values{}
	v.Set("name", name)
	res, err := client.Get(fmt.Sprintf("%s?%s", api["user"].String(), v.Encode()))
	log.Print(res)
	if err != nil {
		return nil, err
	}
	d := json.NewDecoder(res.Body)
	defer res.Body.Close()
	d.Decode(&u)
	return u.Id, nil
}

func fetchGroup(id int64) (*Group, error) {
	var g Group = Group{}
	return &g, nil
}

func searchGroup(name string) (*Group, error) {
	return fetchGroup(0)
}

func createUser(name string, groups []int64, admin bool) error {
	type body struct {
		Admin  bool
		Name   string
		Groups []int64
	}
	b, err := json.Marshal(&body{Admin: admin, Name: name, Groups: groups})
	if err != nil {
		return err
	}
	resp, err := client.Post(api["user"].String(), jsonMime, bytes.NewBuffer(b))
	if err != nil {
		return err
	}
	log.Print(resp)
	return nil
}

var Usage = func() {
	fmt.Fprintf(os.Stderr, "%s v%s -- Usage:\n\n", os.Args[0], VERSION)
	fmt.Fprintf(os.Stderr, "Options:\n")
	flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, "\nCommands:\n")
	fmt.Fprintf(os.Stderr, "  help:\n    Print this help.\n\n")
	fmt.Fprintf(os.Stderr, "  test:\n    Run misc tests. For debugging.\n\n")
}

func init() {
	//var err error
	var conf string = os.Getenv("XDG_CONFIG_HOME")
	if conf == "" {
		conf = os.ExpandEnv("$HOME/.config")
	}

	log.SetFlags(0)
	log.SetOutput(os.Stderr)
	stdout = log.New(os.Stdout, "", 0)

	flag.BoolVar(&forceInsecure, "forceInsecure", false, "Don't use HTTPS to connect")
	flag.BoolVar(&DEBUG, "debug", false, "enable debugging output")
	flag.StringVar(&username, "username", os.Getenv("USER"), "Username")
	flag.StringVar(&baseUrl, "url", "http://localhost:8080", "Url to contact server at (excludes path)")
	flag.StringVar(&confPath, "conf", fmt.Sprintf("%s/vault/", conf), "Path to configs and key")
	flag.Parse()

	if sha512.Size384 != secretbox.KeySize {
		log.Fatalf("Hash size and key size mismatch: %d != %d\n", sha512.Size384, secretbox.KeySize)
	}

	// This is bad, need real options here
	keyFile = fmt.Sprintf("%skey", confPath)

	api = make(map[string]*url.URL)
	api["/"], _ = url.Parse(baseUrl)
	api["key"], _ = url.Parse(fmt.Sprintf("%s/key", baseUrl))
	api["auth"], _ = url.Parse(fmt.Sprintf("%s/api/auth", baseUrl))
	api["valid"], _ = url.Parse(fmt.Sprintf("%s/api/noop", baseUrl))
	api["user"], _ = url.Parse(fmt.Sprintf("%s/api/user", baseUrl))
}

func main() {
	if flag.Arg(0) == "help" {
		Usage()
		os.Exit(1)
	}
	privKey, err := loadKey()
	if err != nil {
		log.Fatal(err)
	}
	srvKey, err := fetchServerKey()
	if err != nil {
		log.Fatalf("Can't fetch server key: %v\n", err)
	}
	if DEBUG {
		log.Printf("server public key: %x\n", srvKey)
	}
	switch flag.Arg(0) {
	case "test":
		var me *User
		if err = login(privKey); err != nil {
			log.Fatal(err)
		}
		if isValid() {
			stdout.Printf("Token is valid, successful login.\n")
		} else {
			stdout.Fatalf("Token isn't valid, unsuccessful login.\n")
		}
		me, err = fetchUser(myId)
		if err != nil {
			log.Fatalf("Could not fetch self: %v\n", err)
		} else {
			log.Printf("Fetched self!: %+v\n", me)
		}
	case "user":
		if err = login(privKey); err != nil {
			log.Fatal(err)
		}
		switch flag.Arg(1) {
		case "new":
			var group []int64 = make([]int64, 0)
			newU := flag.NewFlagSet("user new", flag.ExitOnError)
			admin := *newU.Bool("admin", false, "Should user be an admin")
			groupStr := strings.Split(*newU.String("groups", "", "Additional groups to add a user to"), ",")
			newU.Parse(flag.Args()[2:])
			if newU.NArg() != 1 {
				log.Fatalf("Wrong number of arguments!\n")
			}
			for _, v := range groupStr {
				g, err := searchGroup(v)
				if err != nil {
					log.Printf("Can't find group '%s', skipping...\n", v)
					continue
				}
				group = append(group, g.Id)
			}

			err := createUser(newU.Arg(0), group, admin)
			if err != nil {
				log.Fatal(err)
			}
		case "search":
			u, err := searchUser(flag.Arg(2))
			if err != nil {
				log.Fatal(err)
			}
			log.Print(u)
		default:
			log.Println("HELP")
		}
	case "reencrypt", "encrypt":
		err = encryptKey(privKey)
		if err != nil {
			log.Fatal(err)
		}
	default:
		Usage()
	}
	os.Exit(0)
}
<commit_msg>update client<commit_after>package main

// vim: set noexpandtab:

import (
	"bytes"
	"code.google.com/p/go.crypto/ssh/terminal"
	//"crypto/rand"
	"crypto/sha512"
	"encoding/base64"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/gokyle/cryptobox/box"
	"github.com/gokyle/cryptobox/secretbox"
	"hash"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
)

const (
	VERSION  string = "0.0.6-dev"
	jsonMime string = "application/json"
	_               = iota
	E_SERVER uint   = 1 << iota
	E_AUTH
	E_PASSPHRASE
	E_NOKEY
	E_BADKEY
)

var stdout *log.Logger

var forceInsecure bool
var DEBUG bool
var username string
var baseUrl string
var confPath string
var api map[string]*url.URL

var keyFile string
var myId int64
var client *http.Client
var srvKey box.PublicKey

type internalError struct {
	Code uint
}

func (i *internalError) Error() string {
	var ret string
	switch i.Code {
	case E_SERVER:
		ret = "vault: server returned HTTP 500"
	case E_AUTH:
		ret = "vault: could not authenticate"
	case E_PASSPHRASE:
		ret = "vault: bad passphrase"
	case E_NOKEY:
		ret = "vault: no key"
	case E_BADKEY:
		ret = "vault: key malformed/corrupted/(un)encrypted"
	default:
		ret = "vault: unknown error"
	}
	return ret
}

type User struct {
	Id   int64
	Name string
	//PubKey   box.PublicKey
	PubKey   []byte
	Creation time.Time
	Admin    bool
}

type Group struct {
	Id        int64
	Name      string
	Admin     []int64
	Member    []int64
	UserGroup bool
}

type secret struct {
	Id     int64
	Uri    string
	Note   string
	Secret []byte
	Signer int64
}

func decodeBase64(in string) []byte {
	out := make([]byte, base64.StdEncoding.DecodedLen(len(in)))
	n, err := base64.StdEncoding.Decode(out, []byte(in))
	if err != nil {
		return nil
	}
	return out[0:n]
}

func getPassphrase(prompt string) []byte {
	var h hash.Hash = sha512.New384()
	fmt.Fprintf(os.Stdout, prompt)
	phrase, err := terminal.ReadPassword(int(os.Stdin.Fd()))
	fmt.Fprintf(os.Stdout, "\n")
	if err != nil {
		log.Fatal(err)
	}
	io.Copy(h, bytes.NewReader(phrase))
	return h.Sum(nil)
}

func fetchServerKey() (box.PublicKey, error) {
	var key *bytes.Buffer = new(bytes.Buffer)
	resp, err := http.Get(api["key"].String())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.ContentLength <= 0 {
		log.Fatal("key ContentLength <= 0")
	}
	// TODO: error check
	io.Copy(key, resp.Body)

	return box.PublicKey(key.Bytes()), nil
}

func loadKey() (box.PrivateKey, error) {
	var err error
	var raw []byte
	i, err := os.Stat(keyFile)
	if err != nil {
		os.MkdirAll(confPath, 0700)
		log.Fatalf("Couldn't find Private key, looked for %s\n", keyFile)
	}

	raw = make([]byte, i.Size())

	f, err := os.Open(keyFile)
	if err != nil {
		return nil, err
	}
	if _, err = io.ReadFull(f, raw); err != nil {
		return nil, err
	}

	//if !box.KeyIsSuitable(raw) {
	//	phrase := getPassphrase("Passphrase: ")
	//	if !secretbox.KeyIsSuitable(phrase) {
	//		return nil, &internalError{E_BADKEY}
	//	}

	//	ret, ok := secretbox.Open(raw, secretbox.Key(phrase))
	//	if !ok {
	//		return nil, &internalError{E_BADKEY}
	//	}

	//	if !box.KeyIsSuitable(ret) {
	//		return nil, &internalError{E_BADKEY}
	//	}
	//	return box.PrivateKey(ret), nil
	//} else {
	stdout.Printf("This key is unencrypted!\nEncrypt this key with: '%s encrypt'\n", os.Args[0])
	return box.PrivateKey(raw), nil
	//}
}

func encryptKey(key box.PrivateKey) error {
	phrase := getPassphrase("New Passphrase: ")

	box, ok := secretbox.Seal([]byte(key), phrase)
	if !ok {
		return &internalError{E_BADKEY}
	}

	err := ioutil.WriteFile(keyFile, box, 0600)
	if err != nil {
		log.Fatal(err)
	}
	return nil
}

func login(privKey box.PrivateKey) error {
	var err error
	var challenge []byte
	var ok bool
	var jar *cookiejar.Jar
	c := make(map[string]string)
	// Step1: request challenge
	resp, err := http.Get(fmt.Sprintf("%s/%s", api["auth"].String(), username))
	if err != nil {
		return err
	}
	d := json.NewDecoder(resp.Body)
	defer resp.Body.Close()
	d.Decode(&c)
	data := decodeBase64(c["challenge"])
	if box.BoxIsSigned(data) {
		log.Println("opening signed box...")
		challenge, ok = box.OpenAndVerify(data, privKey, srvKey)
	} else {
		log.Println("opening box...")
		challenge, ok = box.Open(data, privKey)
	}
	if !ok {
		log.Println("unboxing returned not ok")
		return &internalError{E_AUTH}
	}

	// Step2: validate challenge
	step2, err := json.Marshal(&map[string]string{"id": c["id"], "challenge": base64.StdEncoding.EncodeToString(challenge)})
	if err != nil {
		return err
	}
	resp, err = http.Post(api["auth"].String(), jsonMime, strings.NewReader(string(step2)))
	if err != nil {
		return err
	}
	if resp.StatusCode == http.StatusUnauthorized {
		return &internalError{E_AUTH}
	}
	if resp.StatusCode != http.StatusOK {
		return &internalError{E_SERVER}
	}

	jar, err = cookiejar.New(nil)
	if err != nil {
		log.Fatalf("Failed opening cookiejar: %v\n", err)
	}
	jar.SetCookies(api["/"], resp.Cookies())
	myId, _ = strconv.ParseInt(c["id"], 10, 64)
	client = &http.Client{Jar: jar}

	return nil
}

func isValid() bool {
	res, err := client.Get(api["valid"].String())
	if err != nil {
		return false
	}
	return res.StatusCode == http.StatusOK
}

func fetchUser(id int64) (*User, error) {
	var u User = User{}
	res, err := client.Get(fmt.Sprintf("%s/%d", api["user"].String(), id))
	if err != nil {
		return nil, err
	}
	d := json.NewDecoder(res.Body)
	defer res.Body.Close()
	d.Decode(&u)
	return &u, nil
}

func searchUser(name string) ([]int64, error) {
	var u struct{ Id []int64 }
	v := url.Values{}
	v.Set("name", name)
	res, err := client.Get(fmt.Sprintf("%s?%s", api["user"].String(), v.Encode()))
	log.Print(res)
	if err != nil {
		return nil, err
	}
	d := json.NewDecoder(res.Body)
	defer res.Body.Close()
	d.Decode(&u)
	return u.Id, nil
}

func fetchGroup(id int64) (*Group, error) {
	var g Group = Group{}
	return &g, nil
}

func searchGroup(name string) (*Group, error) {
	return fetchGroup(0)
}

func createUser(name string, groups []int64, admin bool) error {
	type body struct {
		Admin  bool
		Name   string
		Groups []int64
	}
	b, err := json.Marshal(&body{Admin: admin, Name: name, Groups: groups})
	if err != nil {
		return err
	}
	resp, err := client.Post(api["user"].String(), jsonMime, bytes.NewBuffer(b))
	if err != nil {
		return err
	}
	log.Print(resp)
	return nil
}

func fetchSecrets() ([]secret, error) {
	var s []secret
	res, err := client.Get(api["secrets"].String())
	if err != nil {
		return nil, err
	}
	d := json.NewDecoder(res.Body)
	defer res.Body.Close()
	d.Decode(&s)
	return s, nil
}

var Usage = func() {
	fmt.Fprintf(os.Stderr, "%s v%s -- Usage:\n\n", os.Args[0], VERSION)
	fmt.Fprintf(os.Stderr, "Options:\n")
	flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, "\nCommands:\n")
	fmt.Fprintf(os.Stderr, "  help:\n    Print this help.\n\n")
	fmt.Fprintf(os.Stderr, "  test:\n    Run misc tests. For debugging.\n\n")
}

func init() {
	//var err error
	var conf string = os.Getenv("XDG_CONFIG_HOME")
	if conf == "" {
		conf = os.ExpandEnv("$HOME/.config")
	}

	log.SetFlags(0)
	log.SetOutput(os.Stderr)
	stdout = log.New(os.Stdout, "", 0)

	flag.BoolVar(&forceInsecure, "forceInsecure", false, "Don't use HTTPS to connect")
	flag.BoolVar(&DEBUG, "debug", false, "enable debugging output")
	flag.StringVar(&username, "username", os.Getenv("USER"), "Username")
	flag.StringVar(&baseUrl, "url", "http://localhost:8080", "Url to contact server at (excludes path)")
	flag.StringVar(&confPath, "conf", fmt.Sprintf("%s/vault/", conf), "Path to configs and key")
	flag.Parse()

	if sha512.Size384 != secretbox.KeySize {
		log.Fatalf("Hash size and key size mismatch: %d != %d\n", sha512.Size384, secretbox.KeySize)
	}

	// This is bad, need real options here
	keyFile = fmt.Sprintf("%skey", confPath)

	api = make(map[string]*url.URL)
	api["/"], _ = url.Parse(baseUrl)
	api["key"], _ = url.Parse(fmt.Sprintf("%s/key", baseUrl))
	api["auth"], _ = url.Parse(fmt.Sprintf("%s/api/auth", baseUrl))
	api["valid"], _ = url.Parse(fmt.Sprintf("%s/api/noop", baseUrl))
	api["user"], _ = url.Parse(fmt.Sprintf("%s/api/user", baseUrl))
	api["secrets"], _ = url.Parse(fmt.Sprintf("%s/api/secrets", baseUrl))
}

func main() {
	if flag.Arg(0) == "help" {
		Usage()
		os.Exit(1)
	}
	privKey, err := loadKey()
	if err != nil {
		log.Fatal(err)
	}
	srvKey, err := fetchServerKey()
	if err != nil {
		log.Fatalf("Can't fetch server key: %v\n", err)
	}
	if DEBUG {
		log.Printf("server public key: %x\n", srvKey)
	}
	switch flag.Arg(0) {
	case "test":
		var me *User
		if err = login(privKey); err != nil {
			log.Fatal(err)
		}
		if isValid() {
			stdout.Printf("Token is valid, successful login.\n")
		} else {
			stdout.Fatalf("Token isn't valid, unsuccessful login.\n")
		}
		me, err = fetchUser(myId)
		if err != nil {
			log.Fatalf("Could not fetch self: %v\n", err)
		} else {
			log.Printf("Fetched self!: %+v\n", me)
		}
		s, err := fetchSecrets()
		if err != nil {
			log.Fatalf("Could not fetch secrets: %v\n", err)
		} else {
			log.Printf("Fetched secrets!: %+v\n", s)
		}
	case "user":
		if err = login(privKey); err != nil {
			log.Fatal(err)
		}
		switch flag.Arg(1) {
		case "new":
			var group []int64 = make([]int64, 0)
			newU := flag.NewFlagSet("user new", flag.ExitOnError)
			admin := *newU.Bool("admin", false, "Should user be an admin")
			groupStr := strings.Split(*newU.String("groups", "", "Additional groups to add a user to"), ",")
			newU.Parse(flag.Args()[2:])
			if newU.NArg() != 1 {
				log.Fatalf("Wrong number of arguments!\n")
			}
			for _, v := range groupStr {
				g, err := searchGroup(v)
				if err != nil {
					log.Printf("Can't find group '%s', skipping...\n", v)
					continue
				}
				group = append(group, g.Id)
			}

			err := createUser(newU.Arg(0), group, admin)
			if err != nil {
				log.Fatal(err)
			}
		case "search":
			u, err := searchUser(flag.Arg(2))
			if err != nil {
				log.Fatal(err)
			}
			log.Print(u)
		default:
			log.Println("HELP")
		}
	case "reencrypt", "encrypt":
		err = encryptKey(privKey)
		if err != nil {
			log.Fatal(err)
		}
	default:
		Usage()
	}
	os.Exit(0)
}
<|endoftext|>


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

package dns

import (
	"bufio"
	"net"
	"os"
	"strconv"
	"strings"
)

// Wraps the contents of the /etc/resolv.conf.
type ClientConfig struct {
	Servers  []string // servers to use
	Search   []string // suffixes to append to local name
	Port     string   // what port to use
	Ndots    int      // number of dots in name to trigger absolute lookup
	Timeout  int      // seconds before giving up on packet
	Attempts int      // lost packets before giving up on server
}

// ClientConfigFromFile parses a resolv.conf(5) like file and returns
// a *ClientConfig.
func ClientConfigFromFile(conf string) (*ClientConfig, error) {
	file, err := os.Open(conf)
	defer file.Close()
	if err != nil {
		return nil, err
	}
	c := new(ClientConfig)
	b := bufio.NewReader(file)
	c.Servers = make([]string, 0)
	c.Search = make([]string, 0)
	c.Port = "53"
	c.Ndots = 1
	c.Timeout = 5
	c.Attempts = 2
	for line, ok := b.ReadString('\n'); ok == nil; line, ok = b.ReadString('\n') {
		f := strings.Fields(line)
		if len(f) < 1 {
			continue
		}
		switch f[0] {
		case "nameserver": // add one name server
			if len(f) > 1 {
				// One more check: make sure server name is
				// just an IP address.  Otherwise we need DNS
				// to look it up.
				name := f[1]
				switch x := net.ParseIP(name); true {
				case x.To4() != nil:
					c.Servers = append(c.Servers, name)
				case x.To16() != nil:
					name = "[" + name + "]"
					c.Servers = append(c.Servers, name)
				}
			}

		case "domain": // set search path to just this domain
			if len(f) > 1 {
				c.Search = make([]string, 1)
				c.Search[0] = f[1]
			} else {
				c.Search = make([]string, 0)
			}

		case "search": // set search path to given servers
			c.Search = make([]string, len(f)-1)
			for i := 0; i < len(c.Search); i++ {
				c.Search[i] = f[i+1]
			}

		case "options": // magic options
			for i := 1; i < len(f); i++ {
				s := f[i]
				switch {
				case len(s) >= 6 && s[:6] == "ndots:":
					n, _ := strconv.Atoi(s[6:])
					if n < 1 {
						n = 1
					}
					c.Ndots = n
				case len(s) >= 8 && s[:8] == "timeout:":
					n, _ := strconv.Atoi(s[8:])
					if n < 1 {
						n = 1
					}
					c.Timeout = n
				case len(s) >= 8 && s[:9] == "attempts:":
					n, _ := strconv.Atoi(s[9:])
					if n < 1 {
						n = 1
					}
					c.Attempts = n
				case s == "rotate":
					/* not imp */
				}
			}
		}
	}
	return c, nil
}
<commit_msg>net.JoinHostPort will add [ and ]<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package dns

import (
	"bufio"
	"net"
	"os"
	"strconv"
	"strings"
)

// Wraps the contents of the /etc/resolv.conf.
type ClientConfig struct {
	Servers  []string // servers to use
	Search   []string // suffixes to append to local name
	Port     string   // what port to use
	Ndots    int      // number of dots in name to trigger absolute lookup
	Timeout  int      // seconds before giving up on packet
	Attempts int      // lost packets before giving up on server
}

// ClientConfigFromFile parses a resolv.conf(5) like file and returns
// a *ClientConfig.
func ClientConfigFromFile(conf string) (*ClientConfig, error) {
	file, err := os.Open(conf)
	defer file.Close()
	if err != nil {
		return nil, err
	}
	c := new(ClientConfig)
	b := bufio.NewReader(file)
	c.Servers = make([]string, 0)
	c.Search = make([]string, 0)
	c.Port = "53"
	c.Ndots = 1
	c.Timeout = 5
	c.Attempts = 2
	for line, ok := b.ReadString('\n'); ok == nil; line, ok = b.ReadString('\n') {
		f := strings.Fields(line)
		if len(f) < 1 {
			continue
		}
		switch f[0] {
		case "nameserver": // add one name server
			if len(f) > 1 {
				// One more check: make sure server name is
				// just an IP address.  Otherwise we need DNS
				// to look it up.
				name := f[1]
				// Don't use this. net.JoinHostPort will fix this for you
				//				switch x := net.ParseIP(name); true {
				//				case x.To4() != nil:
				//					c.Servers = append(c.Servers, name)
				//				case x.To16() != nil:
				//					name = "[" + name + "]"
				c.Servers = append(c.Servers, name)
				//				}
			}

		case "domain": // set search path to just this domain
			if len(f) > 1 {
				c.Search = make([]string, 1)
				c.Search[0] = f[1]
			} else {
				c.Search = make([]string, 0)
			}

		case "search": // set search path to given servers
			c.Search = make([]string, len(f)-1)
			for i := 0; i < len(c.Search); i++ {
				c.Search[i] = f[i+1]
			}

		case "options": // magic options
			for i := 1; i < len(f); i++ {
				s := f[i]
				switch {
				case len(s) >= 6 && s[:6] == "ndots:":
					n, _ := strconv.Atoi(s[6:])
					if n < 1 {
						n = 1
					}
					c.Ndots = n
				case len(s) >= 8 && s[:8] == "timeout:":
					n, _ := strconv.Atoi(s[8:])
					if n < 1 {
						n = 1
					}
					c.Timeout = n
				case len(s) >= 8 && s[:9] == "attempts:":
					n, _ := strconv.Atoi(s[9:])
					if n < 1 {
						n = 1
					}
					c.Attempts = n
				case s == "rotate":
					/* not imp */
				}
			}
		}
	}
	return c, nil
}
<|endoftext|>


<commit_before><commit_msg>Fail TestGCPBrokerMetrics test immediately if event cannot be sent (#1309)<commit_after><|endoftext|>


<commit_before>//+build linux

package driver

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/client/fingerprint"
	"github.com/hashicorp/nomad/client/stats"
	"github.com/hashicorp/nomad/helper/fields"
	"github.com/hashicorp/nomad/nomad/structs"
	"github.com/mitchellh/mapstructure"

	dstructs "github.com/hashicorp/nomad/client/driver/structs"
	cstructs "github.com/hashicorp/nomad/client/structs"
	lxc "gopkg.in/lxc/go-lxc.v2"
)

const (
	containerMonitorIntv = 2 * time.Second
)

var (
	LXCMeasuredCpuStats = []string{"System Mode", "User Mode", "Percent"}
	LXCMeasuredMemStats = []string{"RSS", "Cache", "Swap", "Max Usage", "Kernel Usage", "Kernel Max Usage"}
)

// Add the lxc driver to the list of builtin drivers
func init() {
	BuiltinDrivers["lxc"] = NewLxcDriver
}

// LxcDriver allows users to run LXC Containers
type LxcDriver struct {
	DriverContext
	fingerprint.StaticFingerprinter
}

// LxcDriverConfig is the configuration of the LXC Container
type LxcDriverConfig struct {
	Template             string
	Distro               string
	Release              string
	Arch                 string
	ImageVariant         string   "mapstructure:`image_variant`"
	ImageServer          string   "mapstructure:`image_server`"
	GPGKeyID             string   "mapstructure:`gpg_key_id`"
	GPGKeyServer         string   "mapstructure:`gpg_key_server`"
	DisableGPGValidation bool     "mapstructure:`disable_gpg`"
	FlushCache           bool     "mapstructure:`flush_cache`"
	ForceCache           bool     "mapstructure:`force_cache`"
	TemplateArgs         []string "mapstructure:`template_args`"
}

// NewLxcDriver returns a new instance of the LXC driver
func NewLxcDriver(ctx *DriverContext) Driver {
	return &LxcDriver{DriverContext: *ctx}
}

// Validate validates the lxc driver configuration
func (d *LxcDriver) Validate(config map[string]interface{}) error {
	fd := &fields.FieldData{
		Raw: config,
		Schema: map[string]*fields.FieldSchema{
			"template": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: true,
			},
			"distro": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"release": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"arch": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"image_variant": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"image_server": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"gpg_key_id": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"gpg_key_server": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"disable_gpg": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"flush_cache": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"force_cache": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"template_args": &fields.FieldSchema{
				Type:     fields.TypeArray,
				Required: false,
			},
		},
	}

	if err := fd.Validate(); err != nil {
		return err
	}
	return nil
}

// Fingerprint fingerprints the lxc driver configuration
func (d *LxcDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) {
	version := lxc.Version()
	if version == "" {
		return false, nil
	}
	node.Attributes["driver.lxc.version"] = version
	node.Attributes["driver.lxc"] = "1"
	return true, nil
}

// Start starts the LXC Driver
func (d *LxcDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) {
	var driverConfig LxcDriverConfig
	if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil {
		return nil, err
	}
	lxcPath := lxc.DefaultConfigPath()
	if path := d.config.Read("lxc.path"); path != "" {
		lxcPath = path
	}

	containerName := fmt.Sprintf("%s-%s", task.Name, ctx.AllocID)
	c, err := lxc.NewContainer(containerName, lxcPath)
	if err != nil {
		return nil, fmt.Errorf("unable to create container: %v", err)
	}

	// TODO make them driver configuration
	c.SetVerbosity(lxc.Verbose)
	c.SetLogLevel(lxc.TRACE)

	logFile := filepath.Join(ctx.AllocDir.LogDir(), fmt.Sprintf("%v-lxc.log", task.Name))
	c.SetLogFile(logFile)

	options := lxc.TemplateOptions{
		Template:             driverConfig.Template,
		Distro:               driverConfig.Distro,
		Release:              driverConfig.Release,
		Arch:                 driverConfig.Arch,
		FlushCache:           driverConfig.FlushCache,
		DisableGPGValidation: driverConfig.DisableGPGValidation,
	}

	if err := c.Create(options); err != nil {
		return nil, fmt.Errorf("unable to create container: %v", err)
	}
	if err := c.Start(); err != nil {
		return nil, fmt.Errorf("unable to start container: %v", err)
	}
	// Set the resource limits
	if err := c.SetMemoryLimit(lxc.ByteSize(task.Resources.MemoryMB) * lxc.MB); err != nil {
		return nil, fmt.Errorf("unable to set memory limits: %v", err)
	}
	if err := c.SetCgroupItem("cpu.shares", strconv.Itoa(task.Resources.CPU)); err != nil {
		return nil, fmt.Errorf("unable to set cpu shares: %v", err)
	}

	handle := lxcDriverHandle{
		container:      c,
		initPid:        c.InitPid(),
		lxcPath:        lxcPath,
		logger:         d.logger,
		killTimeout:    GetKillTimeout(task.KillTimeout, d.DriverContext.config.MaxKillTimeout),
		maxKillTimeout: d.DriverContext.config.MaxKillTimeout,
		totalCpuStats:  stats.NewCpuStats(),
		userCpuStats:   stats.NewCpuStats(),
		systemCpuStats: stats.NewCpuStats(),
		waitCh:         make(chan *dstructs.WaitResult, 1),
		doneCh:         make(chan bool, 1),
	}

	go handle.run()

	return &handle, nil
}

// Open opena the driver to monitor an existing LXC container
func (d *LxcDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) {
	pid := &lxcPID{}
	if err := json.Unmarshal([]byte(handleID), pid); err != nil {
		return nil, fmt.Errorf("Failed to parse handle '%s': %v", handleID, err)
	}

	var container *lxc.Container
	containers := lxc.Containers(pid.LxcPath)
	for _, c := range containers {
		if c.Name() == pid.ContainerName {
			container = &c
			break
		}
	}

	if container == nil {
		return nil, fmt.Errorf("container %v not found", pid.ContainerName)
	}

	handle := lxcDriverHandle{
		container:      container,
		initPid:        container.InitPid(),
		lxcPath:        pid.LxcPath,
		logger:         d.logger,
		killTimeout:    pid.KillTimeout,
		maxKillTimeout: d.DriverContext.config.MaxKillTimeout,
		totalCpuStats:  stats.NewCpuStats(),
		userCpuStats:   stats.NewCpuStats(),
		systemCpuStats: stats.NewCpuStats(),
		waitCh:         make(chan *dstructs.WaitResult, 1),
		doneCh:         make(chan bool, 1),
	}
	go handle.run()

	return &handle, nil
}

// lxcDriverHandle allows controlling the lifecycle of an lxc container
type lxcDriverHandle struct {
	container *lxc.Container
	initPid   int
	lxcPath   string

	logger *log.Logger

	killTimeout    time.Duration
	maxKillTimeout time.Duration

	totalCpuStats  *stats.CpuStats
	userCpuStats   *stats.CpuStats
	systemCpuStats *stats.CpuStats

	waitCh chan *dstructs.WaitResult
	doneCh chan bool
}

type lxcPID struct {
	ContainerName string
	InitPid       int
	LxcPath       string
	KillTimeout   time.Duration
}

func (h *lxcDriverHandle) ID() string {
	pid := lxcPID{
		ContainerName: h.container.Name(),
		InitPid:       h.initPid,
		LxcPath:       h.lxcPath,
		KillTimeout:   h.killTimeout,
	}
	data, err := json.Marshal(pid)
	if err != nil {
		h.logger.Printf("[ERR] driver.lxc: failed to marshal lxc PID to JSON: %v", err)
	}
	return string(data)
}

func (h *lxcDriverHandle) WaitCh() chan *dstructs.WaitResult {
	return h.waitCh
}

func (h *lxcDriverHandle) Update(task *structs.Task) error {
	h.killTimeout = GetKillTimeout(task.KillTimeout, h.killTimeout)
	return nil
}

func (h *lxcDriverHandle) Kill() error {
	h.logger.Printf("[INFO] driver.lxc: shutting down container %q", h.container.Name())
	if err := h.container.Shutdown(h.killTimeout); err != nil {
		h.logger.Printf("[INFO] driver.lxc: shutting down container %q failed: %v", h.container.Name(), err)
		if err := h.container.Stop(); err != nil {
			h.logger.Printf("[ERR] driver.lxc: error stopping container %q: %v", h.container.Name(), err)
		}
	}
	close(h.doneCh)
	return nil
}

func (h *lxcDriverHandle) Stats() (*cstructs.TaskResourceUsage, error) {
	cpuStats, err := h.container.CPUStats()
	if err != nil {
		return nil, nil
	}
	total, err := h.container.CPUTime()
	if err != nil {
		return nil, nil
	}

	t := time.Now()

	// Get the cpu stats
	system := cpuStats["system"]
	user := cpuStats["user"]
	cs := &cstructs.CpuStats{
		SystemMode: h.systemCpuStats.Percent(float64(system)),
		UserMode:   h.systemCpuStats.Percent(float64(user)),
		Percent:    h.totalCpuStats.Percent(float64(total)),
		TotalTicks: float64(user + system),
		Measured:   LXCMeasuredCpuStats,
	}

	// Get the Memory Stats
	memData := map[string]uint64{
		"rss":   0,
		"cache": 0,
		"swap":  0,
	}
	rawMemStats := h.container.CgroupItem("memory.stat")
	for _, rawMemStat := range rawMemStats {
		key, val, err := keysToVal(rawMemStat)
		if err != nil {
			h.logger.Printf("[ERR] driver.lxc: error getting stat for key %q: %v", key, err)
			continue
		}
		if _, ok := memData[key]; ok {
			memData[key] = val

		}
	}
	ms := &cstructs.MemoryStats{
		RSS:      memData["rss"],
		Cache:    memData["cache"],
		Swap:     memData["swap"],
		Measured: LXCMeasuredMemStats,
	}

	mu := h.container.CgroupItem("memory.max_usage_in_bytes")
	for _, rawMemMaxUsage := range mu {
		val, err := strconv.ParseUint(rawMemMaxUsage, 10, 64)
		if err != nil {
			h.logger.Printf("[ERR] driver.lxc: unable to get max memory usage: %v", err)
			continue
		}
		ms.MaxUsage = val
	}
	ku := h.container.CgroupItem("memory.kmem.usage_in_bytes")
	for _, rawKernelUsage := range ku {
		val, err := strconv.ParseUint(rawKernelUsage, 10, 64)
		if err != nil {
			h.logger.Printf("[ERR] driver.lxc: unable to get kernel memory usage: %v", err)
			continue
		}
		ms.KernelUsage = val
	}

	mku := h.container.CgroupItem("memory.kmem.max_usage_in_bytes")
	for _, rawMaxKernelUsage := range mku {
		val, err := strconv.ParseUint(rawMaxKernelUsage, 10, 64)
		if err != nil {
			h.logger.Printf("[ERR] driver.lxc: unable to get max kernel memory usage: %v", err)
			continue
		}
		ms.KernelMaxUsage = val
	}

	taskResUsage := cstructs.TaskResourceUsage{
		ResourceUsage: &cstructs.ResourceUsage{
			CpuStats:    cs,
			MemoryStats: ms,
		},
		Timestamp: t.UTC().UnixNano(),
	}

	return &taskResUsage, nil
}

func (h *lxcDriverHandle) run() {
	defer close(h.waitCh)
	timer := time.NewTimer(containerMonitorIntv)
	for {
		select {
		case <-timer.C:
			process, err := os.FindProcess(h.initPid)
			if err != nil {
				h.waitCh <- &dstructs.WaitResult{Err: err}
				return
			}
			if err := process.Signal(syscall.Signal(0)); err != nil {
				h.waitCh <- &dstructs.WaitResult{}
				return
			}
			timer.Reset(containerMonitorIntv)
		case <-h.doneCh:
			h.waitCh <- &dstructs.WaitResult{}
			return
		}
	}
}

func keysToVal(line string) (string, uint64, error) {
	tokens := strings.Split(line, " ")
	key := tokens[0]
	val, err := strconv.ParseUint(tokens[1], 10, 64)
	return key, val, err
}
<commit_msg>Setting the network type<commit_after>//+build linux

package driver

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/client/fingerprint"
	"github.com/hashicorp/nomad/client/stats"
	"github.com/hashicorp/nomad/helper/fields"
	"github.com/hashicorp/nomad/nomad/structs"
	"github.com/mitchellh/mapstructure"

	dstructs "github.com/hashicorp/nomad/client/driver/structs"
	cstructs "github.com/hashicorp/nomad/client/structs"
	lxc "gopkg.in/lxc/go-lxc.v2"
)

const (
	containerMonitorIntv = 2 * time.Second
)

var (
	LXCMeasuredCpuStats = []string{"System Mode", "User Mode", "Percent"}
	LXCMeasuredMemStats = []string{"RSS", "Cache", "Swap", "Max Usage", "Kernel Usage", "Kernel Max Usage"}
)

// Add the lxc driver to the list of builtin drivers
func init() {
	BuiltinDrivers["lxc"] = NewLxcDriver
}

// LxcDriver allows users to run LXC Containers
type LxcDriver struct {
	DriverContext
	fingerprint.StaticFingerprinter
}

// LxcDriverConfig is the configuration of the LXC Container
type LxcDriverConfig struct {
	Template             string
	Distro               string
	Release              string
	Arch                 string
	ImageVariant         string   "mapstructure:`image_variant`"
	ImageServer          string   "mapstructure:`image_server`"
	GPGKeyID             string   "mapstructure:`gpg_key_id`"
	GPGKeyServer         string   "mapstructure:`gpg_key_server`"
	DisableGPGValidation bool     "mapstructure:`disable_gpg`"
	FlushCache           bool     "mapstructure:`flush_cache`"
	ForceCache           bool     "mapstructure:`force_cache`"
	TemplateArgs         []string "mapstructure:`template_args`"
}

// NewLxcDriver returns a new instance of the LXC driver
func NewLxcDriver(ctx *DriverContext) Driver {
	return &LxcDriver{DriverContext: *ctx}
}

// Validate validates the lxc driver configuration
func (d *LxcDriver) Validate(config map[string]interface{}) error {
	fd := &fields.FieldData{
		Raw: config,
		Schema: map[string]*fields.FieldSchema{
			"template": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: true,
			},
			"distro": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"release": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"arch": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"image_variant": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"image_server": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"gpg_key_id": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"gpg_key_server": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"disable_gpg": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"flush_cache": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"force_cache": &fields.FieldSchema{
				Type:     fields.TypeString,
				Required: false,
			},
			"template_args": &fields.FieldSchema{
				Type:     fields.TypeArray,
				Required: false,
			},
		},
	}

	if err := fd.Validate(); err != nil {
		return err
	}
	return nil
}

// Fingerprint fingerprints the lxc driver configuration
func (d *LxcDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) {
	version := lxc.Version()
	if version == "" {
		return false, nil
	}
	node.Attributes["driver.lxc.version"] = version
	node.Attributes["driver.lxc"] = "1"
	return true, nil
}

// Start starts the LXC Driver
func (d *LxcDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) {
	var driverConfig LxcDriverConfig
	if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil {
		return nil, err
	}
	lxcPath := lxc.DefaultConfigPath()
	if path := d.config.Read("lxc.path"); path != "" {
		lxcPath = path
	}

	containerName := fmt.Sprintf("%s-%s", task.Name, ctx.AllocID)
	c, err := lxc.NewContainer(containerName, lxcPath)
	if err != nil {
		return nil, fmt.Errorf("unable to create container: %v", err)
	}

	// TODO make them driver configuration
	c.SetVerbosity(lxc.Verbose)
	c.SetLogLevel(lxc.TRACE)

	logFile := filepath.Join(ctx.AllocDir.LogDir(), fmt.Sprintf("%v-lxc.log", task.Name))
	c.SetLogFile(logFile)

	options := lxc.TemplateOptions{
		Template:             driverConfig.Template,
		Distro:               driverConfig.Distro,
		Release:              driverConfig.Release,
		Arch:                 driverConfig.Arch,
		FlushCache:           driverConfig.FlushCache,
		DisableGPGValidation: driverConfig.DisableGPGValidation,
	}

	if err := c.Create(options); err != nil {
		return nil, fmt.Errorf("unable to create container: %v", err)
	}

	// Set the network type to none
	if err := c.SetConfigItem("lxc.network.type", "none"); err != nil {
		return nil, fmt.Errorf("error setting network type configuration: %v", err)
	}

	if err := c.Start(); err != nil {
		return nil, fmt.Errorf("unable to start container: %v", err)
	}

	// Set the resource limits
	if err := c.SetMemoryLimit(lxc.ByteSize(task.Resources.MemoryMB) * lxc.MB); err != nil {
		return nil, fmt.Errorf("unable to set memory limits: %v", err)
	}
	if err := c.SetCgroupItem("cpu.shares", strconv.Itoa(task.Resources.CPU)); err != nil {
		return nil, fmt.Errorf("unable to set cpu shares: %v", err)
	}

	handle := lxcDriverHandle{
		container:      c,
		initPid:        c.InitPid(),
		lxcPath:        lxcPath,
		logger:         d.logger,
		killTimeout:    GetKillTimeout(task.KillTimeout, d.DriverContext.config.MaxKillTimeout),
		maxKillTimeout: d.DriverContext.config.MaxKillTimeout,
		totalCpuStats:  stats.NewCpuStats(),
		userCpuStats:   stats.NewCpuStats(),
		systemCpuStats: stats.NewCpuStats(),
		waitCh:         make(chan *dstructs.WaitResult, 1),
		doneCh:         make(chan bool, 1),
	}

	go handle.run()

	return &handle, nil
}

// Open opena the driver to monitor an existing LXC container
func (d *LxcDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) {
	pid := &lxcPID{}
	if err := json.Unmarshal([]byte(handleID), pid); err != nil {
		return nil, fmt.Errorf("Failed to parse handle '%s': %v", handleID, err)
	}

	var container *lxc.Container
	containers := lxc.Containers(pid.LxcPath)
	for _, c := range containers {
		if c.Name() == pid.ContainerName {
			container = &c
			break
		}
	}

	if container == nil {
		return nil, fmt.Errorf("container %v not found", pid.ContainerName)
	}

	handle := lxcDriverHandle{
		container:      container,
		initPid:        container.InitPid(),
		lxcPath:        pid.LxcPath,
		logger:         d.logger,
		killTimeout:    pid.KillTimeout,
		maxKillTimeout: d.DriverContext.config.MaxKillTimeout,
		totalCpuStats:  stats.NewCpuStats(),
		userCpuStats:   stats.NewCpuStats(),
		systemCpuStats: stats.NewCpuStats(),
		waitCh:         make(chan *dstructs.WaitResult, 1),
		doneCh:         make(chan bool, 1),
	}
	go handle.run()

	return &handle, nil
}

// lxcDriverHandle allows controlling the lifecycle of an lxc container
type lxcDriverHandle struct {
	container *lxc.Container
	initPid   int
	lxcPath   string

	logger *log.Logger

	killTimeout    time.Duration
	maxKillTimeout time.Duration

	totalCpuStats  *stats.CpuStats
	userCpuStats   *stats.CpuStats
	systemCpuStats *stats.CpuStats

	waitCh chan *dstructs.WaitResult
	doneCh chan bool
}

type lxcPID struct {
	ContainerName string
	InitPid       int
	LxcPath       string
	KillTimeout   time.Duration
}

func (h *lxcDriverHandle) ID() string {
	pid := lxcPID{
		ContainerName: h.container.Name(),
		InitPid:       h.initPid,
		LxcPath:       h.lxcPath,
		KillTimeout:   h.killTimeout,
	}
	data, err := json.Marshal(pid)
	if err != nil {
		h.logger.Printf("[ERR] driver.lxc: failed to marshal lxc PID to JSON: %v", err)
	}
	return string(data)
}

func (h *lxcDriverHandle) WaitCh() chan *dstructs.WaitResult {
	return h.waitCh
}

func (h *lxcDriverHandle) Update(task *structs.Task) error {
	h.killTimeout = GetKillTimeout(task.KillTimeout, h.killTimeout)
	return nil
}

func (h *lxcDriverHandle) Kill() error {
	h.logger.Printf("[INFO] driver.lxc: shutting down container %q", h.container.Name())
	if err := h.container.Shutdown(h.killTimeout); err != nil {
		h.logger.Printf("[INFO] driver.lxc: shutting down container %q failed: %v", h.container.Name(), err)
		if err := h.container.Stop(); err != nil {
			h.logger.Printf("[ERR] driver.lxc: error stopping container %q: %v", h.container.Name(), err)
		}
	}
	close(h.doneCh)
	return nil
}

func (h *lxcDriverHandle) Stats() (*cstructs.TaskResourceUsage, error) {
	cpuStats, err := h.container.CPUStats()
	if err != nil {
		return nil, nil
	}
	total, err := h.container.CPUTime()
	if err != nil {
		return nil, nil
	}

	t := time.Now()

	// Get the cpu stats
	system := cpuStats["system"]
	user := cpuStats["user"]
	cs := &cstructs.CpuStats{
		SystemMode: h.systemCpuStats.Percent(float64(system)),
		UserMode:   h.systemCpuStats.Percent(float64(user)),
		Percent:    h.totalCpuStats.Percent(float64(total)),
		TotalTicks: float64(user + system),
		Measured:   LXCMeasuredCpuStats,
	}

	// Get the Memory Stats
	memData := map[string]uint64{
		"rss":   0,
		"cache": 0,
		"swap":  0,
	}
	rawMemStats := h.container.CgroupItem("memory.stat")
	for _, rawMemStat := range rawMemStats {
		key, val, err := keysToVal(rawMemStat)
		if err != nil {
			h.logger.Printf("[ERR] driver.lxc: error getting stat for key %q: %v", key, err)
			continue
		}
		if _, ok := memData[key]; ok {
			memData[key] = val

		}
	}
	ms := &cstructs.MemoryStats{
		RSS:      memData["rss"],
		Cache:    memData["cache"],
		Swap:     memData["swap"],
		Measured: LXCMeasuredMemStats,
	}

	mu := h.container.CgroupItem("memory.max_usage_in_bytes")
	for _, rawMemMaxUsage := range mu {
		val, err := strconv.ParseUint(rawMemMaxUsage, 10, 64)
		if err != nil {
			h.logger.Printf("[ERR] driver.lxc: unable to get max memory usage: %v", err)
			continue
		}
		ms.MaxUsage = val
	}
	ku := h.container.CgroupItem("memory.kmem.usage_in_bytes")
	for _, rawKernelUsage := range ku {
		val, err := strconv.ParseUint(rawKernelUsage, 10, 64)
		if err != nil {
			h.logger.Printf("[ERR] driver.lxc: unable to get kernel memory usage: %v", err)
			continue
		}
		ms.KernelUsage = val
	}

	mku := h.container.CgroupItem("memory.kmem.max_usage_in_bytes")
	for _, rawMaxKernelUsage := range mku {
		val, err := strconv.ParseUint(rawMaxKernelUsage, 10, 64)
		if err != nil {
			h.logger.Printf("[ERR] driver.lxc: unable to get max kernel memory usage: %v", err)
			continue
		}
		ms.KernelMaxUsage = val
	}

	taskResUsage := cstructs.TaskResourceUsage{
		ResourceUsage: &cstructs.ResourceUsage{
			CpuStats:    cs,
			MemoryStats: ms,
		},
		Timestamp: t.UTC().UnixNano(),
	}

	return &taskResUsage, nil
}

func (h *lxcDriverHandle) run() {
	defer close(h.waitCh)
	timer := time.NewTimer(containerMonitorIntv)
	for {
		select {
		case <-timer.C:
			process, err := os.FindProcess(h.initPid)
			if err != nil {
				h.waitCh <- &dstructs.WaitResult{Err: err}
				return
			}
			if err := process.Signal(syscall.Signal(0)); err != nil {
				h.waitCh <- &dstructs.WaitResult{}
				return
			}
			timer.Reset(containerMonitorIntv)
		case <-h.doneCh:
			h.waitCh <- &dstructs.WaitResult{}
			return
		}
	}
}

func keysToVal(line string) (string, uint64, error) {
	tokens := strings.Split(line, " ")
	key := tokens[0]
	val, err := strconv.ParseUint(tokens[1], 10, 64)
	return key, val, err
}
<|endoftext|>


<commit_before>package cli

import (
	"fmt"

	kingpin "gopkg.in/alecthomas/kingpin.v2"

	"github.com/kopia/kopia/blob"
	"github.com/kopia/kopia/internal/units"
	"github.com/kopia/kopia/repo"
)

var (
	createCommand            = app.Command("create", "Create new repository in a specified location.")
	createRepositoryLocation = createCommand.Arg("location", "Location where to create the repository").Required().String()

	createMetadataEncryptionFormat = createCommand.Flag("metadata-encryption", "Metadata item encryption.").PlaceHolder("FORMAT").Default(repo.SupportedMetadataEncryptionAlgorithms[0]).Enum(repo.SupportedMetadataEncryptionAlgorithms...)
	createObjectFormat             = createCommand.Flag("object-format", "Format of repository objects.").PlaceHolder("FORMAT").Default(repo.DefaultObjectFormat).Enum(repo.SupportedObjectFormats...)
	createObjectSplitter           = createCommand.Flag("object-splitter", "The splitter to use for new objects in the repository").Default("DYNAMIC").Enum(repo.SupportedObjectSplitters...)

	createMinBlockSize = createCommand.Flag("min-block-size", "Minimum size of a data block.").PlaceHolder("KB").Default("1024").Int()
	createAvgBlockSize = createCommand.Flag("avg-block-size", "Average size of a data block.").PlaceHolder("KB").Default("10240").Int()
	createMaxBlockSize = createCommand.Flag("max-block-size", "Maximum size of a data block.").PlaceHolder("KB").Default("20480").Int()

	createInlineBlobSize = createCommand.Flag("inline-blob-size", "Maximum size of an inline data object.").PlaceHolder("KB").Default("0").Int()
	createOverwrite      = createCommand.Flag("overwrite", "Overwrite existing data (DANGEROUS).").Bool()
	createOnly           = createCommand.Flag("create-only", "Create repository, but don't connect to it.").Short('c').Bool()
)

func init() {
	setupConnectOptions(createCommand)
	createCommand.Action(runCreateCommand)
}

func newRepositoryOptionsFromFlags() *repo.NewRepositoryOptions {
	return &repo.NewRepositoryOptions{
		MetadataEncryptionAlgorithm: *createMetadataEncryptionFormat,
		MaxInlineContentLength:      *createInlineBlobSize * 1024,
		ObjectFormat:                *createObjectFormat,

		Splitter:     *createObjectSplitter,
		MinBlockSize: *createMinBlockSize * 1024,
		AvgBlockSize: *createAvgBlockSize * 1024,
		MaxBlockSize: *createMaxBlockSize * 1024,
	}
}

func openStorageAndEnsureEmpty(url string) (blob.Storage, error) {
	s, err := newStorageFromURL(getContext(), url)
	if err != nil {
		return nil, err
	}
	ch, cancel := s.ListBlocks("")
	_, hasData := <-ch
	cancel()

	if hasData && !*createOverwrite {
		return nil, fmt.Errorf("found existing data in %v, specify --overwrite to use anyway", url)
	}

	return s, nil
}

func runCreateCommand(_ *kingpin.ParseContext) error {
	st, err := openStorageAndEnsureEmpty(*createRepositoryLocation)
	if err != nil {
		return fmt.Errorf("unable to get repository storage: %v", err)
	}

	options := newRepositoryOptionsFromFlags()

	creds, err := getRepositoryCredentials(true)
	if err != nil {
		return fmt.Errorf("unable to get credentials: %v", err)
	}

	fmt.Printf("Initializing repository with:\n")
	fmt.Printf("  metadata encryption: %v\n", options.MetadataEncryptionAlgorithm)
	fmt.Printf("  object format:       %v\n", options.ObjectFormat)
	switch options.Splitter {
	case "DYNAMIC":
		fmt.Printf("  object splitter:     DYNAMIC with block sizes (min:%v avg:%v max:%v)\n",
			units.BytesStringBase2(int64(options.MinBlockSize)),
			units.BytesStringBase2(int64(options.AvgBlockSize)),
			units.BytesStringBase2(int64(options.MaxBlockSize)))

	case "FIXED":
		fmt.Printf("  object splitter:     FIXED with with block size: %v\n", units.BytesStringBase2(int64(options.MaxBlockSize)))

	case "NEVER":
		fmt.Printf("  object splitter:     NEVER\n")
	}

	if err := repo.Initialize(st, options, creds); err != nil {
		return fmt.Errorf("cannot initialize repository: %v", err)
	}

	if !*createOnly {
		err := repo.Connect(getContext(), repositoryConfigFileName(), st, creds, connectOptions())
		if err != nil {
			return err
		}

		fmt.Println("Connected to repository:", *createRepositoryLocation)
	}

	return nil
}
<commit_msg>fix for create incorrectly handling store errors<commit_after>package cli

import (
	"fmt"

	kingpin "gopkg.in/alecthomas/kingpin.v2"

	"github.com/kopia/kopia/blob"
	"github.com/kopia/kopia/internal/units"
	"github.com/kopia/kopia/repo"
)

var (
	createCommand            = app.Command("create", "Create new repository in a specified location.")
	createRepositoryLocation = createCommand.Arg("location", "Location where to create the repository").Required().String()

	createMetadataEncryptionFormat = createCommand.Flag("metadata-encryption", "Metadata item encryption.").PlaceHolder("FORMAT").Default(repo.SupportedMetadataEncryptionAlgorithms[0]).Enum(repo.SupportedMetadataEncryptionAlgorithms...)
	createObjectFormat             = createCommand.Flag("object-format", "Format of repository objects.").PlaceHolder("FORMAT").Default(repo.DefaultObjectFormat).Enum(repo.SupportedObjectFormats...)
	createObjectSplitter           = createCommand.Flag("object-splitter", "The splitter to use for new objects in the repository").Default("DYNAMIC").Enum(repo.SupportedObjectSplitters...)

	createMinBlockSize = createCommand.Flag("min-block-size", "Minimum size of a data block.").PlaceHolder("KB").Default("1024").Int()
	createAvgBlockSize = createCommand.Flag("avg-block-size", "Average size of a data block.").PlaceHolder("KB").Default("10240").Int()
	createMaxBlockSize = createCommand.Flag("max-block-size", "Maximum size of a data block.").PlaceHolder("KB").Default("20480").Int()

	createInlineBlobSize = createCommand.Flag("inline-blob-size", "Maximum size of an inline data object.").PlaceHolder("KB").Default("0").Int()
	createOverwrite      = createCommand.Flag("overwrite", "Overwrite existing data (DANGEROUS).").Bool()
	createOnly           = createCommand.Flag("create-only", "Create repository, but don't connect to it.").Short('c').Bool()
)

func init() {
	setupConnectOptions(createCommand)
	createCommand.Action(runCreateCommand)
}

func newRepositoryOptionsFromFlags() *repo.NewRepositoryOptions {
	return &repo.NewRepositoryOptions{
		MetadataEncryptionAlgorithm: *createMetadataEncryptionFormat,
		MaxInlineContentLength:      *createInlineBlobSize * 1024,
		ObjectFormat:                *createObjectFormat,

		Splitter:     *createObjectSplitter,
		MinBlockSize: *createMinBlockSize * 1024,
		AvgBlockSize: *createAvgBlockSize * 1024,
		MaxBlockSize: *createMaxBlockSize * 1024,
	}
}

func openStorageAndEnsureEmpty(url string) (blob.Storage, error) {
	s, err := newStorageFromURL(getContext(), url)
	if err != nil {
		return nil, err
	}
	ch, cancel := s.ListBlocks("")
	d, hasData := <-ch
	cancel()

	if hasData {
		if d.Error != nil {
			return nil, d.Error
		}

		if !*createOverwrite {
			return nil, fmt.Errorf("found existing data in %v, specify --overwrite to use anyway", url)
		}
	}

	return s, nil
}

func runCreateCommand(_ *kingpin.ParseContext) error {
	st, err := openStorageAndEnsureEmpty(*createRepositoryLocation)
	if err != nil {
		return fmt.Errorf("unable to get repository storage: %v", err)
	}

	options := newRepositoryOptionsFromFlags()

	creds, err := getRepositoryCredentials(true)
	if err != nil {
		return fmt.Errorf("unable to get credentials: %v", err)
	}

	fmt.Printf("Initializing repository with:\n")
	fmt.Printf("  metadata encryption: %v\n", options.MetadataEncryptionAlgorithm)
	fmt.Printf("  object format:       %v\n", options.ObjectFormat)
	switch options.Splitter {
	case "DYNAMIC":
		fmt.Printf("  object splitter:     DYNAMIC with block sizes (min:%v avg:%v max:%v)\n",
			units.BytesStringBase2(int64(options.MinBlockSize)),
			units.BytesStringBase2(int64(options.AvgBlockSize)),
			units.BytesStringBase2(int64(options.MaxBlockSize)))

	case "FIXED":
		fmt.Printf("  object splitter:     FIXED with with block size: %v\n", units.BytesStringBase2(int64(options.MaxBlockSize)))

	case "NEVER":
		fmt.Printf("  object splitter:     NEVER\n")
	}

	if err := repo.Initialize(st, options, creds); err != nil {
		return fmt.Errorf("cannot initialize repository: %v", err)
	}

	if !*createOnly {
		err := repo.Connect(getContext(), repositoryConfigFileName(), st, creds, connectOptions())
		if err != nil {
			return err
		}

		fmt.Println("Connected to repository:", *createRepositoryLocation)
	}

	return nil
}
<|endoftext|>


<commit_before>package client // import "github.com/docker/docker/client"

import (
	"context"
	"encoding/json"
	"fmt"
	"net/url"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/pkg/errors"
)

// BuildCachePrune requests the daemon to delete unused cache data
func (cli *Client) BuildCachePrune(ctx context.Context, opts types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) {
	if err := cli.NewVersionError("1.31", "build prune"); err != nil {
		return nil, err
	}

	report := types.BuildCachePruneReport{}

	query := url.Values{}
	if opts.All {
		query.Set("all", "1")
	}
	query.Set("keep-storage", fmt.Sprintf("%d", opts.KeepStorage))
	filters, err := filters.ToJSON(opts.Filters)
	if err != nil {
		return nil, errors.Wrap(err, "prune could not marshal filters option")
	}
	query.Set("filters", filters)

	serverResp, err := cli.post(ctx, "/build/prune", query, nil, nil)
	defer ensureReaderClosed(serverResp)

	if err != nil {
		return nil, err
	}

	if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil {
		return nil, fmt.Errorf("Error retrieving disk usage: %v", err)
	}

	return &report, nil
}
<commit_msg>client: use strconv instead of fmt.Sprintf()<commit_after>package client // import "github.com/docker/docker/client"

import (
	"context"
	"encoding/json"
	"net/url"
	"strconv"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/pkg/errors"
)

// BuildCachePrune requests the daemon to delete unused cache data
func (cli *Client) BuildCachePrune(ctx context.Context, opts types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) {
	if err := cli.NewVersionError("1.31", "build prune"); err != nil {
		return nil, err
	}

	report := types.BuildCachePruneReport{}

	query := url.Values{}
	if opts.All {
		query.Set("all", "1")
	}
	query.Set("keep-storage", strconv.Itoa(int(opts.KeepStorage)))
	f, err := filters.ToJSON(opts.Filters)
	if err != nil {
		return nil, errors.Wrap(err, "prune could not marshal filters option")
	}
	query.Set("filters", f)

	serverResp, err := cli.post(ctx, "/build/prune", query, nil, nil)
	defer ensureReaderClosed(serverResp)

	if err != nil {
		return nil, err
	}

	if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil {
		return nil, errors.Wrap(err, "error retrieving disk usage")
	}

	return &report, nil
}
<|endoftext|>


<commit_before>package couchbase

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"time"
)

// ViewRow represents a single result from a view.
//
// Doc is present only if include_docs was set on the request.
type ViewRow struct {
	ID    string
	Key   interface{}
	Value interface{}
	Doc   *interface{}
}

// A ViewError is a node-specific error indicating a partial failure
// within a view result.
type ViewError struct {
	From   string
	Reason string
}

func (ve ViewError) Error() string {
	return "Node: " + ve.From + ", reason: " + ve.Reason
}

// ViewResult holds the entire result set from a view request,
// including the rows and the errors.
type ViewResult struct {
	TotalRows int `json:"total_rows"`
	Rows      []ViewRow
	Errors    []ViewError
}

func (b *Bucket) randomBaseURL() (*url.URL, error) {
	nodes := []Node{}
	for _, n := range b.Nodes() {
		if n.Status == "healthy" && n.CouchAPIBase != "" {
			nodes = append(nodes, n)
		}
	}
	if len(nodes) == 0 {
		return nil, errors.New("no available couch rest URLs")
	}
	nodeNo := rand.Intn(len(nodes))
	node := nodes[nodeNo]
	u, err := ParseURL(node.CouchAPIBase)
	if err != nil {
		return nil, fmt.Errorf("config error: Bucket %q node #%d CouchAPIBase=%q: %v",
			b.Name, nodeNo, node.CouchAPIBase, err)
	} else if b.pool != nil {
		u.User = b.pool.client.BaseURL.User
	}
	return u, err
}

// DocID is the document ID type for the startkey_docid parameter in
// views.
type DocID string

func qParam(k, v string) string {
	format := `"%s"`
	switch k {
	case "startkey_docid", "stale":
		format = "%s"
	}
	return fmt.Sprintf(format, v)
}

// ViewURL constructs a URL for a view with the given ddoc, view name,
// and parameters.
func (b *Bucket) ViewURL(ddoc, name string,
	params map[string]interface{}) (string, error) {
	u, err := b.randomBaseURL()
	if err != nil {
		return "", err
	}

	values := url.Values{}
	for k, v := range params {
		switch t := v.(type) {
		case DocID:
			values[k] = []string{string(t)}
		case string:
			values[k] = []string{qParam(k, t)}
		case int:
			values[k] = []string{fmt.Sprintf(`%d`, t)}
		case bool:
			values[k] = []string{fmt.Sprintf(`%v`, t)}
		default:
			b, err := json.Marshal(v)
			if err != nil {
				return "", fmt.Errorf("unsupported value-type %T in Query, "+
					"json encoder said %v", t, err)
			}
			values[k] = []string{fmt.Sprintf(`%v`, string(b))}
		}
	}

	if ddoc == "" && name == "_all_docs" {
		u.Path = fmt.Sprintf("/%s/_all_docs", b.Name)
	} else {
		u.Path = fmt.Sprintf("/%s/_design/%s/_view/%s", b.Name, ddoc, name)
	}
	u.RawQuery = values.Encode()

	return u.String(), nil
}

// ViewCallback is called for each view invocation.
var ViewCallback func(ddoc, name string, start time.Time, err error)

// ViewCustom performs a view request that can map row values to a
// custom type.
//
// See the source to View for an example usage.
func (b *Bucket) ViewCustom(ddoc, name string, params map[string]interface{},
	vres interface{}) (err error) {
	if SlowServerCallWarningThreshold > 0 {
		defer slowLog(time.Now(), "call to ViewCustom(%q, %q)", ddoc, name)
	}

	if ViewCallback != nil {
		defer func(t time.Time) { ViewCallback(ddoc, name, t, err) }(time.Now())
	}

	u, err := b.ViewURL(ddoc, name, params)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return err
	}
	maybeAddAuth(req, b.authHandler())

	res, err := HTTPClient.Do(req)
	if err != nil {
		return fmt.Errorf("error starting view req at %v: %v", u, err)
	}
	defer res.Body.Close()

	if res.StatusCode != 200 {
		bod := make([]byte, 512)
		l, _ := res.Body.Read(bod)
		return fmt.Errorf("error executing view req at %v: %v - %s",
			u, res.Status, bod[:l])
	}

	body, err := ioutil.ReadAll(res.Body)
	if err := json.Unmarshal(body, vres); err != nil {
		return nil
	}

	return nil
}

// View executes a view.
//
// The ddoc parameter is just the bare name of your design doc without
// the "_design/" prefix.
//
// Parameters are string keys with values that correspond to couchbase
// view parameters.  Primitive should work fairly naturally (booleans,
// ints, strings, etc...) and other values will attempt to be JSON
// marshaled (useful for array indexing on on view keys, for example).
//
// Example:
//
//   res, err := couchbase.View("myddoc", "myview", map[string]interface{}{
//       "group_level": 2,
//       "start_key":    []interface{}{"thing"},
//       "end_key":      []interface{}{"thing", map[string]string{}},
//       "stale": false,
//       })
func (b *Bucket) View(ddoc, name string, params map[string]interface{}) (ViewResult, error) {
	vres := ViewResult{}
	return vres, b.ViewCustom(ddoc, name, params, &vres)
}
<commit_msg>Fix for MB-11617 After a rebalance/failover the list of  nodes may not be upto date which may result in the views code trying to reach a server that may have been removed. The fix for this is to refresh the bucket and retry the view atleast once before giving  up.<commit_after>package couchbase

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"time"
)

// ViewRow represents a single result from a view.
//
// Doc is present only if include_docs was set on the request.
type ViewRow struct {
	ID    string
	Key   interface{}
	Value interface{}
	Doc   *interface{}
}

// A ViewError is a node-specific error indicating a partial failure
// within a view result.
type ViewError struct {
	From   string
	Reason string
}

func (ve ViewError) Error() string {
	return "Node: " + ve.From + ", reason: " + ve.Reason
}

// ViewResult holds the entire result set from a view request,
// including the rows and the errors.
type ViewResult struct {
	TotalRows int `json:"total_rows"`
	Rows      []ViewRow
	Errors    []ViewError
}

func (b *Bucket) randomBaseURL() (*url.URL, error) {
	nodes := []Node{}
	for _, n := range b.Nodes() {
		if n.Status == "healthy" && n.CouchAPIBase != "" {
			nodes = append(nodes, n)
		}
	}
	if len(nodes) == 0 {
		return nil, errors.New("no available couch rest URLs")
	}
	nodeNo := rand.Intn(len(nodes))
	node := nodes[nodeNo]
	u, err := ParseURL(node.CouchAPIBase)
	if err != nil {
		return nil, fmt.Errorf("config error: Bucket %q node #%d CouchAPIBase=%q: %v",
			b.Name, nodeNo, node.CouchAPIBase, err)
	} else if b.pool != nil {
		u.User = b.pool.client.BaseURL.User
	}
	return u, err
}

// DocID is the document ID type for the startkey_docid parameter in
// views.
type DocID string

func qParam(k, v string) string {
	format := `"%s"`
	switch k {
	case "startkey_docid", "stale":
		format = "%s"
	}
	return fmt.Sprintf(format, v)
}

// ViewURL constructs a URL for a view with the given ddoc, view name,
// and parameters.
func (b *Bucket) ViewURL(ddoc, name string,
	params map[string]interface{}) (string, error) {
	u, err := b.randomBaseURL()
	if err != nil {
		return "", err
	}

	values := url.Values{}
	for k, v := range params {
		switch t := v.(type) {
		case DocID:
			values[k] = []string{string(t)}
		case string:
			values[k] = []string{qParam(k, t)}
		case int:
			values[k] = []string{fmt.Sprintf(`%d`, t)}
		case bool:
			values[k] = []string{fmt.Sprintf(`%v`, t)}
		default:
			b, err := json.Marshal(v)
			if err != nil {
				return "", fmt.Errorf("unsupported value-type %T in Query, "+
					"json encoder said %v", t, err)
			}
			values[k] = []string{fmt.Sprintf(`%v`, string(b))}
		}
	}

	if ddoc == "" && name == "_all_docs" {
		u.Path = fmt.Sprintf("/%s/_all_docs", b.Name)
	} else {
		u.Path = fmt.Sprintf("/%s/_design/%s/_view/%s", b.Name, ddoc, name)
	}
	u.RawQuery = values.Encode()

	return u.String(), nil
}

// ViewCallback is called for each view invocation.
var ViewCallback func(ddoc, name string, start time.Time, err error)

// ViewCustom performs a view request that can map row values to a
// custom type.
//
// See the source to View for an example usage.
func (b *Bucket) ViewCustom(ddoc, name string, params map[string]interface{},
	vres interface{}) (err error) {
	if SlowServerCallWarningThreshold > 0 {
		defer slowLog(time.Now(), "call to ViewCustom(%q, %q)", ddoc, name)
	}

	if ViewCallback != nil {
		defer func(t time.Time) { ViewCallback(ddoc, name, t, err) }(time.Now())
	}

	u, err := b.ViewURL(ddoc, name, params)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return err
	}
	maybeAddAuth(req, b.authHandler())

	res, err := HTTPClient.Do(req)
	if err != nil {
		return fmt.Errorf("error starting view req at %v: %v", u, err)
	}
	defer res.Body.Close()

	if res.StatusCode != 200 {
		bod := make([]byte, 512)
		l, _ := res.Body.Read(bod)
		return fmt.Errorf("error executing view req at %v: %v - %s",
			u, res.Status, bod[:l])
	}

	body, err := ioutil.ReadAll(res.Body)
	if err := json.Unmarshal(body, vres); err != nil {
		return nil
	}

	return nil
}

// View executes a view.
//
// The ddoc parameter is just the bare name of your design doc without
// the "_design/" prefix.
//
// Parameters are string keys with values that correspond to couchbase
// view parameters.  Primitive should work fairly naturally (booleans,
// ints, strings, etc...) and other values will attempt to be JSON
// marshaled (useful for array indexing on on view keys, for example).
//
// Example:
//
//   res, err := couchbase.View("myddoc", "myview", map[string]interface{}{
//       "group_level": 2,
//       "start_key":    []interface{}{"thing"},
//       "end_key":      []interface{}{"thing", map[string]string{}},
//       "stale": false,
//       })
func (b *Bucket) View(ddoc, name string, params map[string]interface{}) (ViewResult, error) {
	vres := ViewResult{}

	if err := b.ViewCustom(ddoc, name, params, &vres); err != nil {
		//error in accessing views. Retry once after a bucket refresh
		b.Refresh()
		return vres, b.ViewCustom(ddoc, name, params, &vres)
	} else {
		return vres, nil
	}
}
<|endoftext|>


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

package cmd

import (
	"regexp"

	"github.com/spf13/cobra"
)

var analysisCmdHelp = `Get a file or URL analysis.

This command receives one or more analysis identifiers and returns information
about the analysis. The data is returned in the same order as the identifiers
appear in the command line.

If the identifiers are not provided in the command line they will be read from
the standard input, one per line.`

var analysisCmdExample = `  vt analysis 8739c76e681f900923b900c9df0ef75cf421d39cabb54650c4b9ad19b6a76d8
  vt analysis 76cdb2bad9582d23c1f6f4d868218d6c
  vt analysis 76cdb2bad9582d23c1f6f4d868218d6c 44d88612fea8a8f36de82e1278abb02f
  cat list_of_analysis_ids | vt analysis`

// NewAnalysisCmd returns a new instance of the 'analysis' command.
func NewAnalysisCmd() *cobra.Command {
	cmd := &cobra.Command{
		Aliases: []string{"an"},
		Use:     "analysis [hash]...",
		Short:   "Get a file or URL analysis",
		Long:    analysisCmdHelp,
		Example: analysisCmdExample,

		RunE: func(cmd *cobra.Command, args []string) error {
			re, _ := regexp.Compile(`(f|u)-[[:xdigit:]]{64}-\d+|[\d\w=]{20,}`)
			p, err := NewObjectPrinter(cmd)
			if err != nil {
				return err
			}
			return p.Print("analyses", args, re)
		},
	}

	addThreadsFlag(cmd.Flags())
	addIncludeExcludeFlags(cmd.Flags())
	addIDOnlyFlag(cmd.Flags())

	return cmd
}
<commit_msg>Fix examples for command "analysis".<commit_after>// Copyright © 2017 The VirusTotal CLI authors. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"regexp"

	"github.com/spf13/cobra"
)

var analysisCmdHelp = `Get a file or URL analysis.

This command receives one or more analysis identifiers and returns information
about the analysis. The data is returned in the same order as the identifiers
appear in the command line.

If the identifiers are not provided in the command line they will be read from
the standard input, one per line.`

var analysisCmdExample = `  vt analysis f-e04b82f7f8afc6e599d4913bee5eb571921ec8958d1ea5e3bbffe9c7ea9a0960-1542306475
  vt analysis u-1db0ad7dbcec0676710ea0eaacd35d5e471d3e11944d53bcbd31f0cbd11bce31-1542292491
  cat list_of_analysis_ids | vt analysis`

// NewAnalysisCmd returns a new instance of the 'analysis' command.
func NewAnalysisCmd() *cobra.Command {
	cmd := &cobra.Command{
		Aliases: []string{"an"},
		Use:     "analysis [hash]...",
		Short:   "Get a file or URL analysis",
		Long:    analysisCmdHelp,
		Example: analysisCmdExample,

		RunE: func(cmd *cobra.Command, args []string) error {
			re, _ := regexp.Compile(`(f|u)-[[:xdigit:]]{64}-\d+|[\d\w=]{20,}`)
			p, err := NewObjectPrinter(cmd)
			if err != nil {
				return err
			}
			return p.Print("analyses", args, re)
		},
	}

	addThreadsFlag(cmd.Flags())
	addIncludeExcludeFlags(cmd.Flags())
	addIDOnlyFlag(cmd.Flags())

	return cmd
}
<|endoftext|>


<commit_before>package main

import (
	"image"
	"os"
	"runtime"

	"code.google.com/p/jnj.plan9/draw"
	"code.google.com/p/jnj/die"
	"github.com/jnjackins/graphics/text"
)

var (
	disp   *draw.Display
	screen *draw.Image
	buf    *text.Buffer
)

type mouseEvent struct {
	m draw.Mouse
}

func (e mouseEvent) Pos() image.Point {
	return e.m.Point
}

func (e mouseEvent) Buttons() int {
	return e.m.Buttons
}

func main() {
	var err error
	runtime.GOMAXPROCS(runtime.NumCPU())
	gopath := os.Getenv("GOPATH")
	fontpath := gopath + "/src/github.com/jnjackins/graphics/cmd/buf/proggyfont.ttf"
	buf, err = text.NewBuffer(image.Rect(0, 0, 300, 300), fontpath)
	die.On(err)
	buf.LoadString("test")
	buf.Select(text.Address{0, 0}, text.Address{0, 0})
	disp, err = draw.Init(nil, "buf", "300x322")
	die.On(err)
	defer disp.Close()
	screen = disp.ScreenImage
	mouse := disp.InitMouse()
	kbd := disp.InitKeyboard()
	redraw()

loop:
	for {
		select {
		case <-mouse.Resize:
			resize()
		case me := <-mouse.C:
			buf.SendMouseEvent(me.Point, me.Buttons)
		case ke := <-kbd.C:
			// esc
			if ke == 27 {
				break loop
			}
			buf.SendKey(ke)
		}
		if buf.Dirty() {
			redraw()
		}
	}
}

func redraw() {
	_, err := screen.Load(screen.Bounds(), buf.Img().Pix)
	die.On(err)
	disp.Flush()
}

func resize() {
	err := disp.Attach(draw.Refmesg)
	die.On(err, "error reattaching display after resize")
	buf.Resize(screen.Bounds())
}
<commit_msg>cmd/buf: read and write files<commit_after>package main

import (
	"flag"
	"fmt"
	"image"
	"io/ioutil"
	"os"
	"runtime"

	"code.google.com/p/jnj.plan9/draw"
	"code.google.com/p/jnj/die"
	"github.com/jnjackins/graphics/text"
)

var (
	disp   *draw.Display
	screen *draw.Image
	buf    *text.Buffer
)

type mouseEvent struct {
	m draw.Mouse
}

func (e mouseEvent) Pos() image.Point {
	return e.m.Point
}

func (e mouseEvent) Buttons() int {
	return e.m.Buttons
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	flag.Parse()
	inputfile := false
	if len(flag.Args()) == 1 {
		inputfile = true
	} else if len(flag.Args()) > 1 {
		fmt.Fprintln(os.Stderr, "Usage: buf [file]")
		os.Exit(1)
	}

	gopath := os.Getenv("GOPATH")
	fontpath := gopath + "/src/github.com/jnjackins/graphics/cmd/buf/proggyfont.ttf"
	var err error
	buf, err = text.NewBuffer(image.Rect(0, 0, 800, 600), fontpath)
	die.On(err)

	var path string
	if inputfile {
		path = flag.Arg(0)
		_, err := os.Stat(path)
		if err != nil {
			// if there's no file, no worries. otherwise, bail.
			if !os.IsNotExist(err) {
				die.On(err)
			}
		} else {
			// no issues, open file for reading
			f, err := os.Open(path)
			die.On(err)
			s, err := ioutil.ReadAll(f)
			buf.LoadString(string(s))
			buf.Select(text.Selection{})
			f.Close()
		}
	}

	disp, err = draw.Init(nil, "buf", "800x622")
	die.On(err)
	defer disp.Close()
	screen = disp.ScreenImage
	mouse := disp.InitMouse()
	kbd := disp.InitKeyboard()
	redraw()

loop:
	for {
		select {
		case <-mouse.Resize:
			resize()
		case me := <-mouse.C:
			buf.SendMouseEvent(me.Point, me.Buttons)
		case ke := <-kbd.C:
			// esc
			if ke == 27 {
				if path != "" {
					ioutil.WriteFile(path, []byte(buf.Contents()), 0666)
				}
				break loop
			}
			buf.SendKey(ke)
		}
		if buf.Dirty() {
			redraw()
		}
	}
}

func redraw() {
	_, err := screen.Load(screen.Bounds(), buf.Img().Pix)
	die.On(err)
	disp.Flush()
}

func resize() {
	err := disp.Attach(draw.Refmesg)
	die.On(err, "error reattaching display after resize")
	buf.Resize(screen.Bounds())
}
<|endoftext|>


<commit_before>package cmd

import (
	"github.com/spf13/cobra"
)

var manifestCmd = &cobra.Command{
	Use: "manifest",
}

func init() {
	RootCmd.AddCommand(manifestCmd)
}
<commit_msg>Issue #37: add subcommand to 'manifest' command usage; add lon/short description<commit_after>package cmd

import (
	"github.com/spf13/cobra"
)

var manifestCmd = &cobra.Command{
	Use:   "manifest [subcommand]",
	Short: "See subcommands",
	Long:  "See subcommands. Run with --help to list the available commands.",
}

func init() {
	RootCmd.AddCommand(manifestCmd)
}
<|endoftext|>


<commit_before>package cmd

import (
	"context"
	"fmt"
	"os"
	"sort"
	"strings"
	"sync"
	"text/tabwriter"

	"github.com/urfave/cli"

	"github.com/arigatomachine/cli/api"
	"github.com/arigatomachine/cli/config"
	"github.com/arigatomachine/cli/identity"
)

func init() {
	policies := cli.Command{
		Name:     "policies",
		Usage:    "View and manipulate access control list policies",
		Category: "ACCESS CONTROL",
		Subcommands: []cli.Command{
			{
				Name:  "list",
				Usage: "List ACL policies for an organization",
				Flags: []cli.Flag{
					orgFlag("org to show policies for", true),
				},
				Action: chain(
					ensureDaemon, ensureSession, loadDirPrefs, loadPrefDefaults,
					setUserEnv, checkRequiredFlags, listPolicies,
				),
			},
			{
				Name:      "view",
				Usage:     "Display the contents of an ACL policy",
				ArgsUsage: "<policy>",
				Flags: []cli.Flag{
					orgFlag("org to show policies for", true),
				},
				Action: chain(
					ensureDaemon, ensureSession, loadDirPrefs, loadPrefDefaults,
					setUserEnv, checkRequiredFlags, viewPolicyCmd,
				),
			},

			{
				Name:      "detach",
				Usage:     "Detach a policy from a team, does not delete the policy",
				ArgsUsage: "<name> <team>",
				Flags: []cli.Flag{
					orgFlag("org to detach policy from", true),
				},
				Action: chain(
					ensureDaemon, ensureSession, loadDirPrefs, loadPrefDefaults,
					setUserEnv, checkRequiredFlags, detachPolicies,
				),
			},
		},
	}
	Cmds = append(Cmds, policies)
}

const policyDetachFailed = "Could not detach policy, please try again."

func detachPolicies(ctx *cli.Context) error {
	cfg, err := config.LoadConfig()
	if err != nil {
		return err
	}

	var text string
	usage := usageString(ctx)
	args := ctx.Args()

	if len(args) < 1 {
		text = "Too few arguments\n\n"
		text += usage
		return cli.NewExitError(text, -1)

	} else if len(args) > 2 {
		text = "Too few arguments\n\n"
		text += usage
		return cli.NewExitError(text, -1)
	}

	policyName := args[0]
	teamName := args[1]

	client := api.NewClient(cfg)
	c := context.Background()

	// Look up the target org
	var org *api.OrgResult
	org, err = client.Orgs.GetByName(c, ctx.String("org"))
	if err != nil {
		return cli.NewExitError(projectListFailed, -1)
	}
	if org == nil {
		return cli.NewExitError("Org not found.", -1)
	}

	var waitPolicy sync.WaitGroup
	waitPolicy.Add(2)

	var team *api.TeamResult
	var policy *api.PoliciesResult
	var pErr, tErr error

	go func() {
		var policies []api.PoliciesResult
		policies, pErr = client.Policies.List(c, org.ID, "")
		for _, p := range policies {
			if p.Body.Policy.Name == policyName {
				policy = &p
				break
			}
		}
		waitPolicy.Done()
	}()

	go func() {
		teams, tErr := client.Teams.GetByName(c, org.ID, teamName)
		if len(teams) < 1 || tErr != nil {
			waitPolicy.Done()
			return
		}
		team = &teams[0]
		waitPolicy.Done()
	}()

	waitPolicy.Wait()
	if tErr != nil || pErr != nil {
		return cli.NewMultiError(
			tErr,
			pErr,
		)
	}
	if team == nil {
		return cli.NewExitError("Team "+teamName+" not found", -1)
	}
	if policy == nil {
		return cli.NewExitError("Policy "+policyName+" not found", -1)
	}

	attachments, err := client.Policies.AttachmentsList(c, org.ID, team.ID, policy.ID)
	if err != nil {
		return cli.NewExitError(policyDetachFailed, -1)
	}
	if len(attachments) < 1 {
		return cli.NewExitError(policyName+" policy is not currently attached to "+teamName, -1)
	}

	err = client.Policies.Detach(c, attachments[0].ID)
	if err != nil {
		if strings.Contains(err.Error(), "system team") {
			return cli.NewExitError("Cannot delete system team attachment", -1)
		}
		return cli.NewExitError(policyDetachFailed, -1)
	}

	fmt.Println("Policy " + policyName + " has been detached from team " + teamName)
	return nil
}

const policyListFailed = "Could not list policies, please try again."

func listPolicies(ctx *cli.Context) error {
	cfg, err := config.LoadConfig()
	if err != nil {
		return err
	}

	client := api.NewClient(cfg)
	c := context.Background()

	// Look up the target org
	var org *api.OrgResult
	org, err = client.Orgs.GetByName(c, ctx.String("org"))
	if err != nil {
		return cli.NewExitError(policyListFailed, -1)
	}
	if org == nil {
		return cli.NewExitError("Org not found.", -1)
	}

	var getAttachments, display sync.WaitGroup
	getAttachments.Add(3)
	display.Add(1)

	var policies []api.PoliciesResult
	var pErr error
	go func() {
		policies, pErr = client.Policies.List(c, org.ID, "")
		getAttachments.Done()
	}()

	var attachments []api.PolicyAttachmentResult
	var aErr error
	go func() {
		attachments, aErr = client.Policies.AttachmentsList(c, org.ID, nil, nil)
		getAttachments.Done()
	}()

	var teams []api.TeamResult
	var tErr error
	go func() {
		teams, tErr = client.Teams.GetByOrg(c, org.ID)
		getAttachments.Done()
	}()

	if aErr != nil || pErr != nil || tErr != nil {
		return cli.NewMultiError(
			pErr,
			aErr,
			tErr,
			cli.NewExitError(policyListFailed, -1),
		)
	}

	teamsByID := make(map[identity.ID]api.TeamResult)
	policiesByName := make(map[string]api.PoliciesResult)
	attachedTeamsByPolicyID := make(map[identity.ID][]string)
	var sortedNames []string

	go func() {
		getAttachments.Wait()
		for _, t := range teams {
			teamsByID[*t.ID] = t
		}
		for _, p := range policies {
			policiesByName[p.Body.Policy.Name] = p
			sortedNames = append(sortedNames, p.Body.Policy.Name)
		}
		sort.Strings(sortedNames)
		for _, a := range attachments {
			ID := *a.Body.PolicyID
			attachedTeamsByPolicyID[ID] = append(attachedTeamsByPolicyID[ID], teamsByID[*a.Body.OwnerID].Body.Name)
		}
		display.Done()
	}()

	display.Wait()
	fmt.Println("")
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
	fmt.Fprintln(w, "POLICY NAME\tTYPE\tATTACHED TO")
	fmt.Fprintln(w, " \t \t ")
	for _, name := range sortedNames {
		teamNames := ""
		policy := policiesByName[name]
		policyID := *policy.ID
		if len(attachedTeamsByPolicyID[policyID]) > 0 {
			teamNames = strings.Join(attachedTeamsByPolicyID[policyID], ", ")
		}
		fmt.Fprintf(w, "%s\t%s\t%s\n", policy.Body.Policy.Name, policy.Body.PolicyType, teamNames)
	}

	w.Flush()
	fmt.Println("")
	return nil
}

func viewPolicyCmd(ctx *cli.Context) error {
	args := ctx.Args()
	if len(args) != 1 {
		msg := "policy name is required.\n"
		if len(args) > 1 {
			msg = "Too many arguments provided.\n"
		}
		msg += usageString(ctx)
		return cli.NewExitError(msg, -1)
	}

	cfg, err := config.LoadConfig()
	if err != nil {
		return err
	}

	client := api.NewClient(cfg)
	c := context.Background()

	org, err := client.Orgs.GetByName(c, ctx.String("org"))
	if err != nil {
		return cli.NewExitError("Unable to lookup org. Please try again.", -1)
	}
	if org == nil {
		return cli.NewExitError("Org not found.", -1)
	}

	policies, err := client.Policies.List(c, org.ID, args[0])
	if err != nil {
		return cli.NewExitError("Unable to list policies. Please try again.", -1)
	}

	if len(policies) < 1 {
		return cli.NewExitError("Policy '"+args[0]+"' not found.", -1)
	}

	policy := policies[0]
	p := policy.Body.Policy

	w := tabwriter.NewWriter(os.Stdout, 2, 0, 1, ' ', 0)

	fmt.Fprintf(w, "Name:\t%s\n", p.Name)
	fmt.Fprintf(w, "Description:\t%s\n", p.Description)
	fmt.Fprintln(w, "")
	w.Flush()

	for _, stmt := range p.Statements {
		fmt.Fprintf(w, "%s\t%s\t%s\n", stmt.Effect.String(), stmt.Action.ShortString(), stmt.Resource)
	}
	w.Flush()

	return nil
}
<commit_msg>Error properly if too few args supplied to detach<commit_after>package cmd

import (
	"context"
	"fmt"
	"os"
	"sort"
	"strings"
	"sync"
	"text/tabwriter"

	"github.com/urfave/cli"

	"github.com/arigatomachine/cli/api"
	"github.com/arigatomachine/cli/config"
	"github.com/arigatomachine/cli/identity"
)

func init() {
	policies := cli.Command{
		Name:     "policies",
		Usage:    "View and manipulate access control list policies",
		Category: "ACCESS CONTROL",
		Subcommands: []cli.Command{
			{
				Name:  "list",
				Usage: "List ACL policies for an organization",
				Flags: []cli.Flag{
					orgFlag("org to show policies for", true),
				},
				Action: chain(
					ensureDaemon, ensureSession, loadDirPrefs, loadPrefDefaults,
					setUserEnv, checkRequiredFlags, listPolicies,
				),
			},
			{
				Name:      "view",
				Usage:     "Display the contents of an ACL policy",
				ArgsUsage: "<policy>",
				Flags: []cli.Flag{
					orgFlag("org to show policies for", true),
				},
				Action: chain(
					ensureDaemon, ensureSession, loadDirPrefs, loadPrefDefaults,
					setUserEnv, checkRequiredFlags, viewPolicyCmd,
				),
			},

			{
				Name:      "detach",
				Usage:     "Detach a policy from a team, does not delete the policy",
				ArgsUsage: "<name> <team>",
				Flags: []cli.Flag{
					orgFlag("org to detach policy from", true),
				},
				Action: chain(
					ensureDaemon, ensureSession, loadDirPrefs, loadPrefDefaults,
					setUserEnv, checkRequiredFlags, detachPolicies,
				),
			},
		},
	}
	Cmds = append(Cmds, policies)
}

const policyDetachFailed = "Could not detach policy, please try again."

func detachPolicies(ctx *cli.Context) error {
	cfg, err := config.LoadConfig()
	if err != nil {
		return err
	}

	var text string
	usage := usageString(ctx)
	args := ctx.Args()

	if len(args) < 2 {
		text = "Too few arguments\n\n"
		text += usage
		return cli.NewExitError(text, -1)

	} else if len(args) > 2 {
		text = "Too many arguments\n\n"
		text += usage
		return cli.NewExitError(text, -1)
	}

	policyName := args[0]
	teamName := args[1]

	client := api.NewClient(cfg)
	c := context.Background()

	// Look up the target org
	var org *api.OrgResult
	org, err = client.Orgs.GetByName(c, ctx.String("org"))
	if err != nil {
		return cli.NewExitError(projectListFailed, -1)
	}
	if org == nil {
		return cli.NewExitError("Org not found.", -1)
	}

	var waitPolicy sync.WaitGroup
	waitPolicy.Add(2)

	var team *api.TeamResult
	var policy *api.PoliciesResult
	var pErr, tErr error

	go func() {
		var policies []api.PoliciesResult
		policies, pErr = client.Policies.List(c, org.ID, "")
		for _, p := range policies {
			if p.Body.Policy.Name == policyName {
				policy = &p
				break
			}
		}
		waitPolicy.Done()
	}()

	go func() {
		teams, tErr := client.Teams.GetByName(c, org.ID, teamName)
		if len(teams) < 1 || tErr != nil {
			waitPolicy.Done()
			return
		}
		team = &teams[0]
		waitPolicy.Done()
	}()

	waitPolicy.Wait()
	if tErr != nil || pErr != nil {
		return cli.NewMultiError(
			tErr,
			pErr,
		)
	}
	if team == nil {
		return cli.NewExitError("Team "+teamName+" not found", -1)
	}
	if policy == nil {
		return cli.NewExitError("Policy "+policyName+" not found", -1)
	}

	attachments, err := client.Policies.AttachmentsList(c, org.ID, team.ID, policy.ID)
	if err != nil {
		return cli.NewExitError(policyDetachFailed, -1)
	}
	if len(attachments) < 1 {
		return cli.NewExitError(policyName+" policy is not currently attached to "+teamName, -1)
	}

	err = client.Policies.Detach(c, attachments[0].ID)
	if err != nil {
		if strings.Contains(err.Error(), "system team") {
			return cli.NewExitError("Cannot delete system team attachment", -1)
		}
		return cli.NewExitError(policyDetachFailed, -1)
	}

	fmt.Println("Policy " + policyName + " has been detached from team " + teamName)
	return nil
}

const policyListFailed = "Could not list policies, please try again."

func listPolicies(ctx *cli.Context) error {
	cfg, err := config.LoadConfig()
	if err != nil {
		return err
	}

	client := api.NewClient(cfg)
	c := context.Background()

	// Look up the target org
	var org *api.OrgResult
	org, err = client.Orgs.GetByName(c, ctx.String("org"))
	if err != nil {
		return cli.NewExitError(policyListFailed, -1)
	}
	if org == nil {
		return cli.NewExitError("Org not found.", -1)
	}

	var getAttachments, display sync.WaitGroup
	getAttachments.Add(3)
	display.Add(1)

	var policies []api.PoliciesResult
	var pErr error
	go func() {
		policies, pErr = client.Policies.List(c, org.ID, "")
		getAttachments.Done()
	}()

	var attachments []api.PolicyAttachmentResult
	var aErr error
	go func() {
		attachments, aErr = client.Policies.AttachmentsList(c, org.ID, nil, nil)
		getAttachments.Done()
	}()

	var teams []api.TeamResult
	var tErr error
	go func() {
		teams, tErr = client.Teams.GetByOrg(c, org.ID)
		getAttachments.Done()
	}()

	if aErr != nil || pErr != nil || tErr != nil {
		return cli.NewMultiError(
			pErr,
			aErr,
			tErr,
			cli.NewExitError(policyListFailed, -1),
		)
	}

	teamsByID := make(map[identity.ID]api.TeamResult)
	policiesByName := make(map[string]api.PoliciesResult)
	attachedTeamsByPolicyID := make(map[identity.ID][]string)
	var sortedNames []string

	go func() {
		getAttachments.Wait()
		for _, t := range teams {
			teamsByID[*t.ID] = t
		}
		for _, p := range policies {
			policiesByName[p.Body.Policy.Name] = p
			sortedNames = append(sortedNames, p.Body.Policy.Name)
		}
		sort.Strings(sortedNames)
		for _, a := range attachments {
			ID := *a.Body.PolicyID
			attachedTeamsByPolicyID[ID] = append(attachedTeamsByPolicyID[ID], teamsByID[*a.Body.OwnerID].Body.Name)
		}
		display.Done()
	}()

	display.Wait()
	fmt.Println("")
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
	fmt.Fprintln(w, "POLICY NAME\tTYPE\tATTACHED TO")
	fmt.Fprintln(w, " \t \t ")
	for _, name := range sortedNames {
		teamNames := ""
		policy := policiesByName[name]
		policyID := *policy.ID
		if len(attachedTeamsByPolicyID[policyID]) > 0 {
			teamNames = strings.Join(attachedTeamsByPolicyID[policyID], ", ")
		}
		fmt.Fprintf(w, "%s\t%s\t%s\n", policy.Body.Policy.Name, policy.Body.PolicyType, teamNames)
	}

	w.Flush()
	fmt.Println("")
	return nil
}

func viewPolicyCmd(ctx *cli.Context) error {
	args := ctx.Args()
	if len(args) != 1 {
		msg := "policy name is required.\n"
		if len(args) > 1 {
			msg = "Too many arguments provided.\n"
		}
		msg += usageString(ctx)
		return cli.NewExitError(msg, -1)
	}

	cfg, err := config.LoadConfig()
	if err != nil {
		return err
	}

	client := api.NewClient(cfg)
	c := context.Background()

	org, err := client.Orgs.GetByName(c, ctx.String("org"))
	if err != nil {
		return cli.NewExitError("Unable to lookup org. Please try again.", -1)
	}
	if org == nil {
		return cli.NewExitError("Org not found.", -1)
	}

	policies, err := client.Policies.List(c, org.ID, args[0])
	if err != nil {
		return cli.NewExitError("Unable to list policies. Please try again.", -1)
	}

	if len(policies) < 1 {
		return cli.NewExitError("Policy '"+args[0]+"' not found.", -1)
	}

	policy := policies[0]
	p := policy.Body.Policy

	w := tabwriter.NewWriter(os.Stdout, 2, 0, 1, ' ', 0)

	fmt.Fprintf(w, "Name:\t%s\n", p.Name)
	fmt.Fprintf(w, "Description:\t%s\n", p.Description)
	fmt.Fprintln(w, "")
	w.Flush()

	for _, stmt := range p.Statements {
		fmt.Fprintf(w, "%s\t%s\t%s\n", stmt.Effect.String(), stmt.Action.ShortString(), stmt.Resource)
	}
	w.Flush()

	return nil
}
<|endoftext|>