<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gonuts/commander"
	"github.com/gonuts/flag"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

func makeRegisterCmd() *commander.Command {
	var registerCmd = &commander.Command{
		Run:       runRegisterCmd,
		UsageLine: "register [options]",
		Flag:      *flag.NewFlagSet("mezasi-register", flag.ExitOnError),
	}

	registerCmd.Flag.String("name", "", "vm name (* required)")
	registerCmd.Flag.String("base", "", "base image (*)")
	registerCmd.Flag.String("public-key", "", "path to ssh public key file (for root@vm)")
	registerCmd.Flag.String("user-data", "", "path to shell script executed on boot up")
	registerCmd.Flag.Bool("wait", false, "wait for vm boot up")
	return registerCmd
}

var listCmd = &commander.Command{
	Run:       runListCmd,
	UsageLine: "list",
}

var configCmd = &commander.Command{
	Run:       runConfigCmd,
	UsageLine: "config",
}

var infoCmd = &commander.Command{
	Run:       runInfoCmd,
	UsageLine: "info <name>",
}

var startCmd = &commander.Command{
	Run:       runPostHandleNameCmd,
	UsageLine: "start <name>",
}

var stopCmd = &commander.Command{
	Run:       runPostHandleNameCmd,
	UsageLine: "stop <name>",
}

var forceStopCmd = &commander.Command{
	Run:       runPostHandleNameCmd,
	UsageLine: "force_stop <name>",
}

func makeRemoveCmd() *commander.Command {
	var removeCmd = &commander.Command{
		Run:       runRemoveCmd,
		UsageLine: "remove <name>",
		Flag:      *flag.NewFlagSet("mezasi-remove", flag.ExitOnError),
	}
	removeCmd.Flag.Bool("yes", false, "")
	return removeCmd
}

var publicKeyCmd = &commander.Command{
	Run:       runPostFileHandleNameCmd,
	UsageLine: "public_key <name> [file]",
}

var userDataCmd = &commander.Command{
	Run:       runPostFileHandleNameCmd,
	UsageLine: "user_data <name> [file]",
}

var sshCmd = &commander.Command{
	Run:       runSshCmd,
	UsageLine: "ssh <name> ...",
}

var portMapCmd = &commander.Command{
	Run:       runPostPortMapCmd,
	UsageLine: "port_map <name> [port]",
}

func runListCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("GET", "vm/list", nil)
	if err != nil {
		return err
	}
	if _, err = pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runConfigCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("GET", "config", nil)
	if err != nil {
		return err
	}
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runInfoCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("GET", "vm/info/"+args[0], nil)
	if err != nil {
		return err
	}
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runPostHandleNameCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("POST", "vm/"+cmd.Name()+"/"+args[0], nil)
	if err != nil {
		return err
	}
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runRemoveCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	yes := cmd.Flag.Lookup("yes").Value.Get().(bool)
	name := args[0]

	if !yes {
		fmt.Printf("Really remove %s? [y/N]\n", name)
		var y string
		fmt.Scanf("%s", &y)
		if y != "y" && y != "Y" {
			os.Exit(1)
		}
	}
	req, err := client.NewRequest("POST", "vm/remove/"+name, nil)
	if err != nil {
		return err
	}
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runRegisterCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	name := cmd.Flag.Lookup("name").Value.Get().(string)
	base := cmd.Flag.Lookup("base").Value.Get().(string)
	if name == "" || base == "" {
		return errors.New("Usage: " + cmd.UsageLine)
	}

	var b bytes.Buffer
	w := multipart.NewWriter(&b)
	if err := w.WriteField("name", name); err != nil {
		return err
	}
	if err := w.WriteField("base", base); err != nil {
		return err
	}

	if publicKey := cmd.Flag.Lookup("public-key").Value.Get().(string); publicKey != "" {
		if err := writeFileField(w, "public_key", publicKey); err != nil {
			return err
		}
	}
	if userData := cmd.Flag.Lookup("user-data").Value.Get().(string); userData != "" {
		if err := writeFileField(w, "user_data", userData); err != nil {
			return err
		}
	}
	if err := w.Close(); err != nil {
		return err
	}

	req, err := client.NewRequest("POST", "vm/register", &b)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", w.FormDataContentType())

	if _, err := pp(client.Do(req)); err != nil {
		return err
	}

	wait := cmd.Flag.Lookup("wait").Value.Get().(bool)
	if !wait {
		return nil
	}
	log.Println("waiting for vm boot up...")

	req, err = client.NewRequest("GET", "notify/"+name, nil)
	if err != nil {
		return err
	}
	if _, err = pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runPostFileHandleNameCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	cmdName := cmd.Name()
	pathStr := cmdName + "/" + args[0]
	switch len(args) {
	case 1:
		req, err := client.NewRequest("GET", pathStr, nil)
		if err != nil {
			return err
		}
		if _, err := pp(client.Do(req)); err != nil {
			return err
		}
	case 2:
		var b bytes.Buffer
		w := multipart.NewWriter(&b)
		if err := writeFileField(w, cmdName, args[1]); err != nil {
			return err
		}
		if err := w.Close(); err != nil {
			return err
		}

		req, err := client.NewRequest("POST", pathStr, &b)
		if err != nil {
			return err
		}
		req.Header.Set("Content-Type", w.FormDataContentType())

		if _, err := pp(client.Do(req)); err != nil {
			return err
		}
	default:
		return errors.New("invalid arguments")
	}
	return nil
}

func runSshCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("GET", "vm/info/"+args[0], nil)
	if err != nil {
		return err
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode != 200 {
		pp(resp, err)
		return err
	}

	dec := json.NewDecoder(resp.Body)
	defer resp.Body.Close()

	m := make(map[string]interface{})
	if err := dec.Decode(&m); err != nil {
		return err
	}
	cmdPath, err := exec.LookPath("ssh")
	if err != nil {
		return err
	}
	execArgs := []string{m["ip_addr"].(string)}
	execArgs = append(execArgs, args[1:]...)

	execCmd := exec.Command(cmdPath, execArgs...)
	execCmd.Stdin = os.Stdin
	execCmd.Stdout = os.Stdout
	execCmd.Stderr = os.Stderr

	return execCmd.Run()
}

func runPostPortMapCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	cmdName := cmd.Name()
	pathStr := cmdName + "/" + args[0]
	switch len(args) {
	case 2:
		var b bytes.Buffer
		w := multipart.NewWriter(&b)
		if err := w.WriteField("port", args[1]); err != nil {
			return err
		}
		if err := w.Close(); err != nil {
			return err
		}
		req, err := client.NewRequest("POST", pathStr, &b)
		if err != nil {
			return err
		}
		req.Header.Set("Content-Type", w.FormDataContentType())

		if _, err := pp(client.Do(req)); err != nil {
			return err
		}
	default:
		return errors.New("invalid arguments")
	}
	return nil
}

func validateCmdArgs(cmd *commander.Command, args []string) error {
	mustVal := 0
	optionVal := 0
	must := regexp.MustCompile(`<\w+>`)
	option := regexp.MustCompile(`\[\w+\]`)

	for _, action := range strings.Split(cmd.UsageLine, " ") {
		if must.MatchString(action) {
			mustVal++
		}
		if option.MatchString(action) {
			optionVal++
		}
		// skip arguments validation
		if action == "..." {
			if mustVal <= len(args) {
				return nil
			}
		}
	}
	if mustVal <= len(args) && len(args) <= mustVal+optionVal {
		return nil
	}
	return errors.New("invalid argument\nUsage: " + cmd.UsageLine)
}

func writeFileField(w *multipart.Writer, fieldName string, filePath string) error {
	ff, err := w.CreateFormField(fieldName)
	if err != nil {
		return err
	}
	file, err := os.Open(filePath)
	if err != nil {
		return err
	}
	if _, err := io.Copy(ff, file); err != nil {
		return err
	}
	return nil
}

func pp(resp *http.Response, err error) (*http.Response, error) {
	if err != nil {
		return resp, err
	}
	log.Println(resp.Status)

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

	index := strings.Index(resp.Header.Get("Content-Type"), "json")

	if index > 0 {
		var b bytes.Buffer
		err := json.Indent(&b, src, "", "    ")
		if err != nil {
			return resp, err
		}
		fmt.Println(b.String())
	} else {
		fmt.Println(string(src))
	}
	return resp, err
}
<commit_msg>fix UsageLine for validation<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gonuts/commander"
	"github.com/gonuts/flag"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

func makeRegisterCmd() *commander.Command {
	var registerCmd = &commander.Command{
		Run:       runRegisterCmd,
		UsageLine: "register [options]",
		Flag:      *flag.NewFlagSet("mezasi-register", flag.ExitOnError),
	}

	registerCmd.Flag.String("name", "", "vm name (* required)")
	registerCmd.Flag.String("base", "", "base image (*)")
	registerCmd.Flag.String("public-key", "", "path to ssh public key file (for root@vm)")
	registerCmd.Flag.String("user-data", "", "path to shell script executed on boot up")
	registerCmd.Flag.Bool("wait", false, "wait for vm boot up")
	return registerCmd
}

var listCmd = &commander.Command{
	Run:       runListCmd,
	UsageLine: "list",
}

var configCmd = &commander.Command{
	Run:       runConfigCmd,
	UsageLine: "config",
}

var infoCmd = &commander.Command{
	Run:       runInfoCmd,
	UsageLine: "info <name>",
}

var startCmd = &commander.Command{
	Run:       runPostHandleNameCmd,
	UsageLine: "start <name>",
}

var stopCmd = &commander.Command{
	Run:       runPostHandleNameCmd,
	UsageLine: "stop <name>",
}

var forceStopCmd = &commander.Command{
	Run:       runPostHandleNameCmd,
	UsageLine: "force_stop <name>",
}

func makeRemoveCmd() *commander.Command {
	var removeCmd = &commander.Command{
		Run:       runRemoveCmd,
		UsageLine: "remove <name>",
		Flag:      *flag.NewFlagSet("mezasi-remove", flag.ExitOnError),
	}
	removeCmd.Flag.Bool("yes", false, "")
	return removeCmd
}

var publicKeyCmd = &commander.Command{
	Run:       runPostFileHandleNameCmd,
	UsageLine: "public_key <name> [file]",
}

var userDataCmd = &commander.Command{
	Run:       runPostFileHandleNameCmd,
	UsageLine: "user_data <name> [file]",
}

var sshCmd = &commander.Command{
	Run:       runSshCmd,
	UsageLine: "ssh <name> ...",
}

var portMapCmd = &commander.Command{
	Run:       runPostPortMapCmd,
	UsageLine: "port_map <name> <port>",
}

func runListCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("GET", "vm/list", nil)
	if err != nil {
		return err
	}
	if _, err = pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runConfigCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("GET", "config", nil)
	if err != nil {
		return err
	}
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runInfoCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("GET", "vm/info/"+args[0], nil)
	if err != nil {
		return err
	}
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runPostHandleNameCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("POST", "vm/"+cmd.Name()+"/"+args[0], nil)
	if err != nil {
		return err
	}
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runRemoveCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	yes := cmd.Flag.Lookup("yes").Value.Get().(bool)
	name := args[0]

	if !yes {
		fmt.Printf("Really remove %s? [y/N]\n", name)
		var y string
		fmt.Scanf("%s", &y)
		if y != "y" && y != "Y" {
			os.Exit(1)
		}
	}
	req, err := client.NewRequest("POST", "vm/remove/"+name, nil)
	if err != nil {
		return err
	}
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runRegisterCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	name := cmd.Flag.Lookup("name").Value.Get().(string)
	base := cmd.Flag.Lookup("base").Value.Get().(string)
	if name == "" || base == "" {
		return errors.New("Usage: " + cmd.UsageLine)
	}

	var b bytes.Buffer
	w := multipart.NewWriter(&b)
	if err := w.WriteField("name", name); err != nil {
		return err
	}
	if err := w.WriteField("base", base); err != nil {
		return err
	}

	if publicKey := cmd.Flag.Lookup("public-key").Value.Get().(string); publicKey != "" {
		if err := writeFileField(w, "public_key", publicKey); err != nil {
			return err
		}
	}
	if userData := cmd.Flag.Lookup("user-data").Value.Get().(string); userData != "" {
		if err := writeFileField(w, "user_data", userData); err != nil {
			return err
		}
	}
	if err := w.Close(); err != nil {
		return err
	}

	req, err := client.NewRequest("POST", "vm/register", &b)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", w.FormDataContentType())

	if _, err := pp(client.Do(req)); err != nil {
		return err
	}

	wait := cmd.Flag.Lookup("wait").Value.Get().(bool)
	if !wait {
		return nil
	}
	log.Println("waiting for vm boot up...")

	req, err = client.NewRequest("GET", "notify/"+name, nil)
	if err != nil {
		return err
	}
	if _, err = pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runPostFileHandleNameCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	cmdName := cmd.Name()
	pathStr := cmdName + "/" + args[0]
	switch len(args) {
	case 1:
		req, err := client.NewRequest("GET", pathStr, nil)
		if err != nil {
			return err
		}
		if _, err := pp(client.Do(req)); err != nil {
			return err
		}
	case 2:
		var b bytes.Buffer
		w := multipart.NewWriter(&b)
		if err := writeFileField(w, cmdName, args[1]); err != nil {
			return err
		}
		if err := w.Close(); err != nil {
			return err
		}

		req, err := client.NewRequest("POST", pathStr, &b)
		if err != nil {
			return err
		}
		req.Header.Set("Content-Type", w.FormDataContentType())

		if _, err := pp(client.Do(req)); err != nil {
			return err
		}
	default:
		return errors.New("invalid arguments")
	}
	return nil
}

func runSshCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("GET", "vm/info/"+args[0], nil)
	if err != nil {
		return err
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode != 200 {
		pp(resp, err)
		return err
	}

	dec := json.NewDecoder(resp.Body)
	defer resp.Body.Close()

	m := make(map[string]interface{})
	if err := dec.Decode(&m); err != nil {
		return err
	}
	cmdPath, err := exec.LookPath("ssh")
	if err != nil {
		return err
	}
	execArgs := []string{m["ip_addr"].(string)}
	execArgs = append(execArgs, args[1:]...)

	execCmd := exec.Command(cmdPath, execArgs...)
	execCmd.Stdin = os.Stdin
	execCmd.Stdout = os.Stdout
	execCmd.Stderr = os.Stderr

	return execCmd.Run()
}

func runPostPortMapCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	cmdName := cmd.Name()
	pathStr := cmdName + "/" + args[0]
	var b bytes.Buffer
	w := multipart.NewWriter(&b)
	if err := w.WriteField("port", args[1]); err != nil {
		return err
	}
	if err := w.Close(); err != nil {
		return err
	}
	req, err := client.NewRequest("POST", pathStr, &b)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", w.FormDataContentType())
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func validateCmdArgs(cmd *commander.Command, args []string) error {
	mustVal := 0
	optionVal := 0
	must := regexp.MustCompile(`<\w+>`)
	option := regexp.MustCompile(`\[\w+\]`)

	for _, action := range strings.Split(cmd.UsageLine, " ") {
		if must.MatchString(action) {
			mustVal++
		}
		if option.MatchString(action) {
			optionVal++
		}
		// skip arguments validation
		if action == "..." {
			if mustVal <= len(args) {
				return nil
			}
		}
	}
	if mustVal <= len(args) && len(args) <= mustVal+optionVal {
		return nil
	}
	return errors.New("invalid argument\nUsage: " + cmd.UsageLine)
}

func writeFileField(w *multipart.Writer, fieldName string, filePath string) error {
	ff, err := w.CreateFormField(fieldName)
	if err != nil {
		return err
	}
	file, err := os.Open(filePath)
	if err != nil {
		return err
	}
	if _, err := io.Copy(ff, file); err != nil {
		return err
	}
	return nil
}

func pp(resp *http.Response, err error) (*http.Response, error) {
	if err != nil {
		return resp, err
	}
	log.Println(resp.Status)

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

	index := strings.Index(resp.Header.Get("Content-Type"), "json")

	if index > 0 {
		var b bytes.Buffer
		err := json.Indent(&b, src, "", "    ")
		if err != nil {
			return resp, err
		}
		fmt.Println(b.String())
	} else {
		fmt.Println(string(src))
	}
	return resp, err
}
<|endoftext|>


<commit_before>package microdata

import (
	"strings"
	"testing"
)

func ReadOneItem(html string, t *testing.T) *Item {
	p := NewParser(strings.NewReader(html))

	data, err := p.Parse()
	if err != nil {
		t.Errorf("Expected no error but got %d", err)
	}

	if data == nil {
		t.Errorf("Expected non-nil data")
	}

	return data.items[0]
}

func TestRead(t *testing.T) {
	html := `

My name is Elizabeth.

`

	item := ReadOneItem(html, t)

	if item.properties["name"][0].(string) != "Elizabeth" {
		t.Errorf("Property value not found")
	}

}

func TestReadActuallyParses(t *testing.T) {
	html := `

My name is Daniel.

`
	item := ReadOneItem(html, t)

	if item.properties["name"][0].(string) != "Daniel" {
		t.Errorf("Property value not found")
	}

}

func TestReadThreeProps(t *testing.T) {
	html := `

My name is Neil.

My band is called Four Parts Water.

I am British.

`

	item := ReadOneItem(html, t)

	if item.properties["name"][0].(string) != "Neil" {
		t.Errorf("Property value not found")
	}

	if item.properties["band"][0].(string) != "Four Parts Water" {
		t.Errorf("Property value not found")
	}

	if item.properties["nationality"][0].(string) != "British" {
		t.Errorf("Property value not found")
	}
}

func TestReadImgSrc(t *testing.T) {
	html := `

`

	item := ReadOneItem(html, t)

	if item.properties["image"][0].(string) != "google-logo.png" {
		t.Errorf("Property value not found")
	}
}

func TestReadAHref(t *testing.T) {
	html := `

	 foo

`

	item := ReadOneItem(html, t)

	if item.properties["image"][0].(string) != "google-logo.png" {
		t.Errorf("Property value not found")
	}
}

func TestReadAreaHref(t *testing.T) {
	html := `

<map name="shapes">
	 <area itemprop="foo" href="target.html" shape=rect coords="50,50,100,100">

	</map>
`

	item := ReadOneItem(html, t)

	if item.properties["foo"][0].(string) != "target.html" {
		t.Errorf("Property value not found")
	}
}

func TestReadLinkHref(t *testing.T) {
	html := `

		<link itemprop="foo" rel="author" href="target.html">

`

	item := ReadOneItem(html, t)

	if item.properties["foo"][0].(string) != "target.html" {
		t.Errorf("Property value not found")
	}
}

func TestReadAudioSrc(t *testing.T) {
	html := `

	 <audio itemprop="foo" src="target"></audio>

`

	item := ReadOneItem(html, t)

	if item.properties["foo"][0].(string) != "target" {
		t.Errorf("Property value not found")
	}
}

func TestReadSourceSrc(t *testing.T) {
	html := `

	 <source itemprop="foo" src="target"></source>

`

	item := ReadOneItem(html, t)

	if item.properties["foo"][0].(string) != "target" {
		t.Errorf("Property value not found")
	}
}

func TestReadVideoSrc(t *testing.T) {
	html := `

	 <video itemprop="foo" src="target"></video>

`

	item := ReadOneItem(html, t)

	if item.properties["foo"][0].(string) != "target" {
		t.Errorf("Property value not found")
	}
}

func TestReadEmbedSrc(t *testing.T) {
	html := `

	 <embed itemprop="foo" src="target"></embed>

`

	item := ReadOneItem(html, t)

	if item.properties["foo"][0].(string) != "target" {
		t.Errorf("Property value not found")
	}
}

func TestReadTrackSrc(t *testing.T) {
	html := `

	 <track itemprop="foo" src="target"></track>

`

	item := ReadOneItem(html, t)

	if item.properties["foo"][0].(string) != "target" {
		t.Errorf("Property value not found")
	}
}

func TestReadIFrameSrc(t *testing.T) {
	html := `

`

	item := ReadOneItem(html, t)

	if item.properties["foo"][0].(string) != "target" {
		t.Errorf("Property value not found")
	}
}

func TestReadDataValue(t *testing.T) {
	html := `

 		<data itemprop="product-id" value="9678AOU879">The Instigator 2000</data>

`

	item := ReadOneItem(html, t)

	if item.properties["product-id"][0].(string) != "9678AOU879" {
		t.Errorf("Property value not found")
	}
}

func TestReadTimeDatetime(t *testing.T) {
	html := `

 		I was born on <time itemprop="birthday" datetime="2009-05-10">May 10th 2009</time>.

`

	item := ReadOneItem(html, t)

	if item.properties["birthday"][0].(string) != "2009-05-10" {
		t.Errorf("Property value not found")
	}
}

<commit_msg>Initial commit<commit_after>package microdata

import (
	"strings"
	"testing"
)

func ReadData(html string, t *testing.T) *Microdata {
	p := NewParser(strings.NewReader(html))

	data, err := p.Parse()
	if err != nil {
		t.Errorf("Expected no error but got %d", err)
	}

	if data == nil {
		t.Errorf("Expected non-nil data")
	}

	return data
}

func ReadOneItem(html string, t *testing.T) *Item {
	data := ReadData(html, t)
	return data.items[0]
}

func TestRead(t *testing.T) {
	html := `

My name is Elizabeth.

`

	item := ReadOneItem(html, t)

	if item.properties["name"][0].(string) != "Elizabeth" {
		t.Errorf("Property value not found")
	}

}

func TestReadActuallyParses(t *testing.T) {
	html := `

My name is Daniel.

`
	item := ReadOneItem(html, t)

	if item.properties["name"][0].(string) != "Daniel" {
		t.Errorf("Property value not found")
	}

}

func TestReadThreeProps(t *testing.T) {
	html := `

My name is Neil.

My band is called Four Parts Water.

I am British.

`

	item := ReadOneItem(html, t)

	if item.properties["name"][0].(string) != "Neil" {
		t.Errorf("Property value not found")
	}

	if item.properties["band"][0].(string) != "Four Parts Water" {
		t.Errorf("Property value not found")
	}

	if item.properties["nationality"][0].(string) != "British" {
		t.Errorf("Property value not found")
	}
}

func TestReadImgSrc(t *testing.T) {
	html := `

`

	item := ReadOneItem(html, t)

	if item.properties["image"][0].(string) != "google-logo.png" {
		t.Errorf("Property value not found")
	}
}

func TestReadAHref(t *testing.T) {
	html := `

	 foo

`

	item := ReadOneItem(html, t)

	if item.properties["image"][0].(string) != "google-logo.png" {
		t.Errorf("Property value not found")
	}
}

func TestReadAreaHref(t *testing.T) {
	html := `

<map name="shapes">
	 <area itemprop="foo" href="target.html" shape=rect coords="50,50,100,100">

	</map>
`

	item := ReadOneItem(html, t)

	if item.properties["foo"][0].(string) != "target.html" {
		t.Errorf("Property value not found")
	}
}

func TestReadLinkHref(t *testing.T) {
	html := `

		<link itemprop="foo" rel="author" href="target.html">

`

	item := ReadOneItem(html, t)

	if item.properties["foo"][0].(string) != "target.html" {
		t.Errorf("Property value not found")
	}
}

func TestReadAudioSrc(t *testing.T) {
	html := `

	 <audio itemprop="foo" src="target"></audio>

`

	item := ReadOneItem(html, t)

	if item.properties["foo"][0].(string) != "target" {
		t.Errorf("Property value not found")
	}
}

func TestReadSourceSrc(t *testing.T) {
	html := `

	 <source itemprop="foo" src="target"></source>

`

	item := ReadOneItem(html, t)

	if item.properties["foo"][0].(string) != "target" {
		t.Errorf("Property value not found")
	}
}

func TestReadVideoSrc(t *testing.T) {
	html := `

	 <video itemprop="foo" src="target"></video>

`

	item := ReadOneItem(html, t)

	if item.properties["foo"][0].(string) != "target" {
		t.Errorf("Property value not found")
	}
}

func TestReadEmbedSrc(t *testing.T) {
	html := `

	 <embed itemprop="foo" src="target"></embed>

`

	item := ReadOneItem(html, t)

	if item.properties["foo"][0].(string) != "target" {
		t.Errorf("Property value not found")
	}
}

func TestReadTrackSrc(t *testing.T) {
	html := `

	 <track itemprop="foo" src="target"></track>

`

	item := ReadOneItem(html, t)

	if item.properties["foo"][0].(string) != "target" {
		t.Errorf("Property value not found")
	}
}

func TestReadIFrameSrc(t *testing.T) {
	html := `

`

	item := ReadOneItem(html, t)

	if item.properties["foo"][0].(string) != "target" {
		t.Errorf("Property value not found")
	}
}

func TestReadDataValue(t *testing.T) {
	html := `

 		<data itemprop="product-id" value="9678AOU879">The Instigator 2000</data>

`

	item := ReadOneItem(html, t)

	if item.properties["product-id"][0].(string) != "9678AOU879" {
		t.Errorf("Property value not found")
	}
}

func TestReadTimeDatetime(t *testing.T) {
	html := `

 		I was born on <time itemprop="birthday" datetime="2009-05-10">May 10th 2009</time>.

`

	item := ReadOneItem(html, t)

	if item.properties["birthday"][0].(string) != "2009-05-10" {
		t.Errorf("Property value not found")
	}
}

func TestReadTwoValues(t *testing.T) {
	html := `

Flavors in my favorite ice cream:

Lemon sorbet

Apricot sorbet

`

	item := ReadOneItem(html, t)

	if item.properties["birthday"][0].(string) != "2009-05-10" {
		t.Errorf("Property value not found")
	}
}
<|endoftext|>


<commit_before>package minecraft

import (
	"image"
	"image/png"
	"net/http"
)

func GetSkin(c *http.Client, pc Profile) (image.Image, error) {
	td, err := pc.Textures()
	switch {
	case err == ERR_HAS_NO_SKIN:
		return fallbackSkin(c, pc)
	case err != nil:
		return nil, err
	}

	if skin, ok := td.Textures["SKIN"]; !ok {
		return fallbackSkin(c, pc)
	} else {
		resp, err := c.Get(skin.Url)
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()

		return png.Decode(resp.Body)
	}
}
<commit_msg>Support JPEG skins<commit_after>package minecraft

import (
	"image"
	_ "image/jpeg"
	_ "image/png"
	"net/http"
)

func GetSkin(c *http.Client, pc Profile) (image.Image, error) {
	td, err := pc.Textures()
	switch {
	case err == ERR_HAS_NO_SKIN:
		return fallbackSkin(c, pc)
	case err != nil:
		return nil, err
	}

	if skin, ok := td.Textures["SKIN"]; !ok {
		return fallbackSkin(c, pc)
	} else {
		resp, err := c.Get(skin.Url)
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()

		img, _, err := image.Decode(resp.Body)
		return img, err
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 sql

import (
	"bytes"
	"fmt"
	"strings"
)

// Expression represents part of SQL statement and optional binding ("?")
type Expression interface {
	// Stringer prints default representation of SQL to String
	// Different implementations can override this using package specific
	// func ExpToString().
	String() string

	// Binding are values referenced ("?") from the statement.
	GetBinding() []interface{}

	// Accepts calls the methods on Visitor.
	Accept(Visitor)
}

// Visitor is used for traversing expression tree.
type Visitor interface {
	VisitPrefixedExp(*PrefixedExp)
	VisitFieldExpression(*FieldExpression)
}

// PrefixedExp covers many SQL constructions. It implements sql.Expression
// interface. Instance of this structure is returned by many helper functions
// below.
type PrefixedExp struct {
	Prefix      string
	AfterPrefix []Expression
	Suffix      string
	Binding     []interface{}
}

// String returns Prefix + " " + AfterPrefix.
func (exp *PrefixedExp) String() string {
	if exp.AfterPrefix == nil {
		return exp.Prefix
	}

	if exp.Prefix == "FROM" && len(exp.Binding) > 0 {
		return exp.Prefix + " " + EntityTableName(exp.Binding[0]) + " " + ExpsToString(exp.AfterPrefix)
	}

	return exp.Prefix + " " + ExpsToString(exp.AfterPrefix)
}

// ExpsToString joins (without separator) individual expression string representations
func ExpsToString(exps []Expression) string {
	if exps != nil {
		if len(exps) == 1 {
			return exps[0].String()
		}

		var buffer bytes.Buffer
		for _, exp := range exps {
			buffer.WriteString(exp.String())
			exp.String()
		}

		return buffer.String()
	}
	return ""
}

// GetBinding is a getter...
func (exp *PrefixedExp) GetBinding() []interface{} {
	return exp.Binding
}

// Accept calls VisitPrefixedExp(...) & Accept(AfterPrefix)
func (exp *PrefixedExp) Accept(visitor Visitor) {
	visitor.VisitPrefixedExp(exp)
}

// FieldExpression for addressing field of an entity in SQL expression.
type FieldExpression struct {
	PointerToAField interface{}
	AfterField      Expression
}

// String returns Prefix + " " + AfterPrefix.
func (exp *FieldExpression) String() string {
	prefix := fmt.Sprint("<field on ", exp.PointerToAField, ">")
	if exp.AfterField == nil {
		return prefix
	}
	return prefix + " " + exp.AfterField.String()
}

// GetBinding is a getter...
func (exp *FieldExpression) GetBinding() []interface{} {
	return nil
}

// Accept calls VisitFieldExpression(...) & Accept(AfterField)
func (exp *FieldExpression) Accept(visitor Visitor) {
	visitor.VisitFieldExpression(exp)
}

// SELECT keyword of SQL expression.
func SELECT(entity interface{}, afterKeyword Expression, binding ...interface{}) Expression {
	return &PrefixedExp{"SELECT", []Expression{FROM(entity, afterKeyword)}, "", binding}
}

// FROM keyword of SQL expression
// Note, pointerToAStruct is assigned to Expression.binding.
// The implementation is supposed try to cast to the sql.TableName & sql.SchemaName.
func FROM(pointerToAStruct interface{}, afterKeyword Expression) Expression {
	return &PrefixedExp{"FROM", []Expression{afterKeyword}, "", []interface{}{pointerToAStruct}}
}

// WHERE keyword of SQL statement
func WHERE(afterKeyword ...Expression) Expression {
	return &PrefixedExp{" WHERE ", afterKeyword, "", nil}
}

// DELETE keyword of SQL statement
func DELETE(entity interface{}, afterKeyword Expression) Expression {
	return &PrefixedExp{"DELETE", []Expression{afterKeyword}, "", nil}
}

// Exp function creates instance of sql.Expression from string statement & optional binding.
// Useful for:
// - rarely used parts of SQL statements
// - create if not exists... statements
func Exp(statement string, binding ...interface{}) Expression {
	return &PrefixedExp{statement, nil, "", binding}
}

var emptyAND = &PrefixedExp{" AND ", nil, "", nil}

// AND keyword of SQL expression
//
// Example usage (alternative 1 - spare sequence of partenthesis):
//
// 		WHERE(FieldEQ(&JamesBond.FirstName), AND(), FieldEQ(&JamesBond.LastName))
//
// Example usage (alternative 2 - useful for nesting):
//
// 		WHERE(AND(FieldEQ(&JamesBond.FirstName), FieldEQ(&JamesBond.LastName)))
func AND(inside ...Expression) Expression {
	return intelligentOperator(emptyAND, inside...)
}

var emptyOR = &PrefixedExp{" OR ", nil, "", nil}

// OR keyword of SQL expression
//
// Example usage 1 (generated string does not contains parenthesis surrounding OR):
//
// 		WHERE(FieldEQ(&PeterBond.FirstName), OR(), FieldEQ(&JamesBond.FirstName))
//
// Example usage 2 (generated string does not contains parenthesis surrounding OR):
//
// 		WHERE(FieldEQ(&PeterBond.LastName), OR(FieldEQ(&PeterBond.FirstName), FieldEQ(&JamesBond.FirstName)))
//
func OR(inside ...Expression) Expression {
	return intelligentOperator(emptyOR, inside...)
}

func intelligentOperator(emptyOperator *PrefixedExp, inside ...Expression) Expression {
	lenInside := len(inside)
	if lenInside == 0 {
		return emptyOperator
	}
	if lenInside == 1 {
		return &PrefixedExp{emptyOperator.Prefix, inside, "", nil}
	}
	inside2 := []Expression{}
	for i, exp := range inside {
		if i > 0 {
			inside2 = append(inside2, emptyOperator, exp)
		} else {
			inside2 = append(inside2, exp)
		}
	}

	return Parenthesis(inside2...)
}

// Field is a helper function to address field of a structure
//
// Example usage:
//   Where(Field(&UsersTable.LastName, UsersTable, EQ('Bond'))
//   // generates for example "WHERE last_name='Bond'"
func Field(pointerToAField interface{}, rigthOperand ...Expression) (exp Expression) {
	if len(rigthOperand) == 0 {
		return &FieldExpression{pointerToAField, nil}
	}
	return &FieldExpression{pointerToAField, rigthOperand[0]}
}

// FieldEQ is combination of Field & EQ on same pointerToAField
//
// Example usage:
//   FROM(JamesBond, Where(FieldEQ(&JamesBond.LastName))
//   // generates for example "WHERE last_name='Bond'"
//   // because JamesBond is a pointer to an instance of a structure that in field LastName contains "Bond"
func FieldEQ(pointerToAField interface{}) (exp Expression) {
	return &FieldExpression{pointerToAField, EQ(pointerToAField)}
}

// PK is alias FieldEQ (user for better readability)
//
// Example usage:
//   FROM(JamesBond, Where(PK(&JamesBond.LastName))
//   // generates for example "WHERE last_name='Bond'"
//   // because JamesBond is a pointer to an instance of a structure that in field LastName contains "Bond"
func PK(pointerToAField interface{}) (exp Expression) {
	return FieldEQ(pointerToAField)
}

// EQ operator "=" used in SQL expressions
func EQ(binding interface{}) (exp Expression) {
	return &PrefixedExp{" = ", []Expression{Exp("?", binding)}, "", nil}
}

// Parenthesis expression that surrounds "inside Expression" with "(" and ")"
func Parenthesis(inside ...Expression) (exp Expression) {
	return &PrefixedExp{"(", inside, ")", nil}
}

// IN operator of SQL expression
// 		FROM(UserTable,WHERE(FieldEQ(&UserTable.FirstName, IN(JamesBond.FirstName, PeterBond.FirstName)))
func IN(binding ...interface{}) (exp Expression) {
	bindingRefs := strings.Repeat(",?", len(binding))
	return &PrefixedExp{" IN(", nil, bindingRefs[1:] + ")", binding}
}
<commit_msg>Cassandra GT, LT operators in sql_expression.go<commit_after>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 sql

import (
	"bytes"
	"fmt"
	"strings"
)

// Expression represents part of SQL statement and optional binding ("?")
type Expression interface {
	// Stringer prints default representation of SQL to String
	// Different implementations can override this using package specific
	// func ExpToString().
	String() string

	// Binding are values referenced ("?") from the statement.
	GetBinding() []interface{}

	// Accepts calls the methods on Visitor.
	Accept(Visitor)
}

// Visitor is used for traversing expression tree.
type Visitor interface {
	VisitPrefixedExp(*PrefixedExp)
	VisitFieldExpression(*FieldExpression)
}

// PrefixedExp covers many SQL constructions. It implements sql.Expression
// interface. Instance of this structure is returned by many helper functions
// below.
type PrefixedExp struct {
	Prefix      string
	AfterPrefix []Expression
	Suffix      string
	Binding     []interface{}
}

// String returns Prefix + " " + AfterPrefix.
func (exp *PrefixedExp) String() string {
	if exp.AfterPrefix == nil {
		return exp.Prefix
	}

	if exp.Prefix == "FROM" && len(exp.Binding) > 0 {
		return exp.Prefix + " " + EntityTableName(exp.Binding[0]) + " " + ExpsToString(exp.AfterPrefix)
	}

	return exp.Prefix + " " + ExpsToString(exp.AfterPrefix)
}

// ExpsToString joins (without separator) individual expression string representations
func ExpsToString(exps []Expression) string {
	if exps != nil {
		if len(exps) == 1 {
			return exps[0].String()
		}

		var buffer bytes.Buffer
		for _, exp := range exps {
			buffer.WriteString(exp.String())
			exp.String()
		}

		return buffer.String()
	}
	return ""
}

// GetBinding is a getter...
func (exp *PrefixedExp) GetBinding() []interface{} {
	return exp.Binding
}

// Accept calls VisitPrefixedExp(...) & Accept(AfterPrefix)
func (exp *PrefixedExp) Accept(visitor Visitor) {
	visitor.VisitPrefixedExp(exp)
}

// FieldExpression for addressing field of an entity in SQL expression.
type FieldExpression struct {
	PointerToAField interface{}
	AfterField      Expression
}

// String returns Prefix + " " + AfterPrefix.
func (exp *FieldExpression) String() string {
	prefix := fmt.Sprint("<field on ", exp.PointerToAField, ">")
	if exp.AfterField == nil {
		return prefix
	}
	return prefix + " " + exp.AfterField.String()
}

// GetBinding is a getter...
func (exp *FieldExpression) GetBinding() []interface{} {
	return nil
}

// Accept calls VisitFieldExpression(...) & Accept(AfterField)
func (exp *FieldExpression) Accept(visitor Visitor) {
	visitor.VisitFieldExpression(exp)
}

// SELECT keyword of SQL expression.
func SELECT(entity interface{}, afterKeyword Expression, binding ...interface{}) Expression {
	return &PrefixedExp{"SELECT", []Expression{FROM(entity, afterKeyword)}, "", binding}
}

// FROM keyword of SQL expression
// Note, pointerToAStruct is assigned to Expression.binding.
// The implementation is supposed try to cast to the sql.TableName & sql.SchemaName.
func FROM(pointerToAStruct interface{}, afterKeyword Expression) Expression {
	return &PrefixedExp{"FROM", []Expression{afterKeyword}, "", []interface{}{pointerToAStruct}}
}

// WHERE keyword of SQL statement
func WHERE(afterKeyword ...Expression) Expression {
	return &PrefixedExp{" WHERE ", afterKeyword, "", nil}
}

// DELETE keyword of SQL statement
func DELETE(entity interface{}, afterKeyword Expression) Expression {
	return &PrefixedExp{"DELETE", []Expression{afterKeyword}, "", nil}
}

// Exp function creates instance of sql.Expression from string statement & optional binding.
// Useful for:
// - rarely used parts of SQL statements
// - create if not exists... statements
func Exp(statement string, binding ...interface{}) Expression {
	return &PrefixedExp{statement, nil, "", binding}
}

var emptyAND = &PrefixedExp{" AND ", nil, "", nil}

// AND keyword of SQL expression
//
// Example usage (alternative 1 - spare sequence of partenthesis):
//
// 		WHERE(FieldEQ(&JamesBond.FirstName), AND(), FieldEQ(&JamesBond.LastName))
//
// Example usage (alternative 2 - useful for nesting):
//
// 		WHERE(AND(FieldEQ(&JamesBond.FirstName), FieldEQ(&JamesBond.LastName)))
func AND(inside ...Expression) Expression {
	return intelligentOperator(emptyAND, inside...)
}

var emptyOR = &PrefixedExp{" OR ", nil, "", nil}

// OR keyword of SQL expression
//
// Example usage 1 (generated string does not contains parenthesis surrounding OR):
//
// 		WHERE(FieldEQ(&PeterBond.FirstName), OR(), FieldEQ(&JamesBond.FirstName))
//
// Example usage 2 (generated string does not contains parenthesis surrounding OR):
//
// 		WHERE(FieldEQ(&PeterBond.LastName), OR(FieldEQ(&PeterBond.FirstName), FieldEQ(&JamesBond.FirstName)))
//
func OR(inside ...Expression) Expression {
	return intelligentOperator(emptyOR, inside...)
}

func intelligentOperator(emptyOperator *PrefixedExp, inside ...Expression) Expression {
	lenInside := len(inside)
	if lenInside == 0 {
		return emptyOperator
	}
	if lenInside == 1 {
		return &PrefixedExp{emptyOperator.Prefix, inside, "", nil}
	}
	inside2 := []Expression{}
	for i, exp := range inside {
		if i > 0 {
			inside2 = append(inside2, emptyOperator, exp)
		} else {
			inside2 = append(inside2, exp)
		}
	}

	return Parenthesis(inside2...)
}

// Field is a helper function to address field of a structure
//
// Example usage:
//   Where(Field(&UsersTable.LastName, UsersTable, EQ('Bond'))
//   // generates for example "WHERE last_name='Bond'"
func Field(pointerToAField interface{}, rigthOperand ...Expression) (exp Expression) {
	if len(rigthOperand) == 0 {
		return &FieldExpression{pointerToAField, nil}
	}
	return &FieldExpression{pointerToAField, rigthOperand[0]}
}

// FieldEQ is combination of Field & EQ on same pointerToAField
//
// Example usage:
//   FROM(JamesBond, Where(FieldEQ(&JamesBond.LastName))
//   // generates for example "WHERE last_name='Bond'"
//   // because JamesBond is a pointer to an instance of a structure that in field LastName contains "Bond"
func FieldEQ(pointerToAField interface{}) (exp Expression) {
	return &FieldExpression{pointerToAField, EQ(pointerToAField)}
}

// PK is alias FieldEQ (user for better readability)
//
// Example usage:
//   FROM(JamesBond, Where(PK(&JamesBond.LastName))
//   // generates for example "WHERE last_name='Bond'"
//   // because JamesBond is a pointer to an instance of a structure that in field LastName contains "Bond"
func PK(pointerToAField interface{}) (exp Expression) {
	return FieldEQ(pointerToAField)
}

// EQ operator "=" used in SQL expressions
func EQ(binding interface{}) (exp Expression) {
	return &PrefixedExp{" = ", []Expression{Exp("?", binding)}, "", nil}
}

// GT operator ">" used in SQL expressions
func GT(binding interface{}) (exp Expression) {
	return &PrefixedExp{" > ", []Expression{Exp("?", binding)}, "", nil}
}

// GTE operator "=>" used in SQL expressions
func GTE(binding interface{}) (exp Expression) {
	return &PrefixedExp{" => ", []Expression{Exp("?", binding)}, "", nil}
}

// LT operator "<" used in SQL expressions
func LT(binding interface{}) (exp Expression) {
	return &PrefixedExp{" < ", []Expression{Exp("?", binding)}, "", nil}
}

// LTE operator "=<" used in SQL expressions
func LTE(binding interface{}) (exp Expression) {
	return &PrefixedExp{" =< ", []Expression{Exp("?", binding)}, "", nil}
}

// Parenthesis expression that surrounds "inside Expression" with "(" and ")"
func Parenthesis(inside ...Expression) (exp Expression) {
	return &PrefixedExp{"(", inside, ")", nil}
}

// IN operator of SQL expression
// 		FROM(UserTable,WHERE(FieldEQ(&UserTable.FirstName, IN(JamesBond.FirstName, PeterBond.FirstName)))
func IN(binding ...interface{}) (exp Expression) {
	bindingRefs := strings.Repeat(",?", len(binding))
	return &PrefixedExp{" IN(", nil, bindingRefs[1:] + ")", binding}
}
<|endoftext|>


<commit_before>package ci_env_test

import (
	"io/ioutil"
	"path/filepath"
	"strings"

	"github.com/bmatcuk/doublestar"

	"github.com/flant/werf/pkg/testing/utils"
	"github.com/flant/werf/pkg/werf"

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

var _ = Describe("ci-env", func() {
	BeforeEach(func() {
		Ω(werf.Init("", "")).Should(Succeed())
	})

	ciSystems := []string{"gitlab"}

	for i := range ciSystems {
		ciSystem := ciSystems[i]

		Context(ciSystem, func() {
			It("should print only script path", func() {
				output := utils.SucceedCommandOutputString(
					"",
					werfBinPath,
					utils.WerfBinArgs("ci-env", ciSystem, "--as-file")...,
				)

				expectedPathGlob := filepath.Join(
					werf.GetServiceDir(),
					"tmp",
					"ci_env",
					"source_*_*",
				)

				resultPath := strings.TrimSuffix(output, "\n")
				matched, err := doublestar.PathMatch(expectedPathGlob, resultPath)
				Ω(err).ShouldNot(HaveOccurred())

				Ω(matched).Should(BeTrue(), output)
				Ω(resultPath).Should(BeARegularFile())
			})

			It("should print only shell script", func() {
				output := utils.SucceedCommandOutputString(
					"",
					werfBinPath,
					utils.WerfBinArgs("ci-env", ciSystem)...,
				)

				useAsFileOutput := utils.SucceedCommandOutputString(
					"",
					werfBinPath,
					utils.WerfBinArgs("ci-env", ciSystem, "--as-file")...,
				)

				scriptPath := strings.TrimSpace(useAsFileOutput)
				scriptData, err := ioutil.ReadFile(scriptPath)
				Ω(err).ShouldNot(HaveOccurred())

				Ω(len(string(scriptData))).Should(Equal(len(output)))
			})
		})
	}
})
<commit_msg>[testing] CI-env Suite: add github<commit_after>package ci_env_test

import (
	"io/ioutil"
	"path/filepath"
	"strings"

	"github.com/bmatcuk/doublestar"

	"github.com/flant/werf/pkg/testing/utils"
	"github.com/flant/werf/pkg/werf"

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

var _ = Describe("ci-env", func() {
	BeforeEach(func() {
		Ω(werf.Init("", "")).Should(Succeed())
	})

	ciSystems := []string{
		"gitlab",
		"github",
	}

	for i := range ciSystems {
		ciSystem := ciSystems[i]

		Context(ciSystem, func() {
			It("should print only script path", func() {
				output := utils.SucceedCommandOutputString(
					"",
					werfBinPath,
					utils.WerfBinArgs("ci-env", ciSystem, "--as-file")...,
				)

				expectedPathGlob := filepath.Join(
					werf.GetServiceDir(),
					"tmp",
					"ci_env",
					"source_*_*",
				)

				resultPath := strings.TrimSuffix(output, "\n")
				matched, err := doublestar.PathMatch(expectedPathGlob, resultPath)
				Ω(err).ShouldNot(HaveOccurred())

				Ω(matched).Should(BeTrue(), output)
				Ω(resultPath).Should(BeARegularFile())
			})

			It("should print only shell script", func() {
				output := utils.SucceedCommandOutputString(
					"",
					werfBinPath,
					utils.WerfBinArgs("ci-env", ciSystem)...,
				)

				useAsFileOutput := utils.SucceedCommandOutputString(
					"",
					werfBinPath,
					utils.WerfBinArgs("ci-env", ciSystem, "--as-file")...,
				)

				scriptPath := strings.TrimSpace(useAsFileOutput)
				scriptData, err := ioutil.ReadFile(scriptPath)
				Ω(err).ShouldNot(HaveOccurred())

				Ω(len(string(scriptData))).Should(Equal(len(output)))
			})
		})
	}
})
<|endoftext|>


<commit_before>package integration_test

import (
	"os"

	"github.com/cloudfoundry-incubator/runtime-schema/models"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/tedsuo/ifrit"
)

var auctioneer ifrit.Process
var dummyActions = []models.ExecutorAction{
	{
		Action: models.RunAction{
			Path: "cat",
			Args: []string{"/tmp/file"},
		},
	},
}

var _ = Describe("Integration", func() {
	BeforeEach(func() {
		auctioneer = ifrit.Envoke(runner)
	})

	AfterEach(func() {
		auctioneer.Signal(os.Kill)
		Eventually(auctioneer.Wait()).Should(Receive())
	})

	Context("when a start auction message arrives", func() {
		BeforeEach(func() {
			bbs.RequestLRPStartAuction(models.LRPStartAuction{
				DesiredLRP: models.DesiredLRP{
					ProcessGuid: "app-guid",
					DiskMB:      1,
					MemoryMB:    1,
					Stack:       lucidStack,
					Actions:     dummyActions,
				},

				InstanceGuid: "instance-guid-1",
				Index:        0,
			})

			bbs.RequestLRPStartAuction(models.LRPStartAuction{
				DesiredLRP: models.DesiredLRP{
					ProcessGuid: "app-guid",
					DiskMB:      1,
					MemoryMB:    1,
					Stack:       lucidStack,
					Actions:     dummyActions,
				},

				InstanceGuid: "instance-guid-2",
				Index:        1,
			})
		})

		It("should start the app running on reps of the appropriate stack", func() {
			Eventually(func() interface{} {
				return repClient.SimulatedInstances(lucidGuid)
			}).Should(HaveLen(2))

			Ω(repClient.SimulatedInstances(dotNetGuid)).Should(BeEmpty())
		})
	})

	Context("when a stop auction message arrives", func() {
		BeforeEach(func() {
			bbs.RequestLRPStartAuction(models.LRPStartAuction{
				DesiredLRP: models.DesiredLRP{
					ProcessGuid: "app-guid",
					DiskMB:      1,
					MemoryMB:    1,
					Stack:       lucidStack,
					Actions:     dummyActions,
				},

				InstanceGuid: "duplicate-instance-guid-1",
				Index:        0,
			})

			Eventually(bbs.GetAllLRPStartAuctions).Should(HaveLen(0))

			bbs.RequestLRPStartAuction(models.LRPStartAuction{
				DesiredLRP: models.DesiredLRP{
					ProcessGuid: "app-guid",
					DiskMB:      1,
					MemoryMB:    1,
					Stack:       lucidStack,
					Actions:     dummyActions,
				},

				InstanceGuid: "duplicate-instance-guid-2",
				Index:        0,
			})

			Eventually(bbs.GetAllLRPStartAuctions).Should(HaveLen(0))

			bbs.RequestLRPStartAuction(models.LRPStartAuction{
				DesiredLRP: models.DesiredLRP{
					ProcessGuid: "app-guid",
					DiskMB:      1,
					MemoryMB:    1,
					Stack:       lucidStack,
					Actions:     dummyActions,
				},

				InstanceGuid: "duplicate-instance-guid-3",
				Index:        0,
			})

			Eventually(bbs.GetAllLRPStartAuctions).Should(HaveLen(0))

			Ω(repClient.SimulatedInstances(lucidGuid)).Should(HaveLen(3))
		})

		It("should stop all but one instance of the app", func() {
			bbs.RequestLRPStopAuction(models.LRPStopAuction{
				ProcessGuid: "app-guid",
				Index:       0,
			})

			Eventually(func() interface{} {
				return repClient.SimulatedInstances(lucidGuid)
			}).Should(HaveLen(1))
		})
	})
})
<commit_msg>use ginkgomon.Invoke to check for start failure<commit_after>package integration_test

import (
	"os"

	"github.com/cloudfoundry-incubator/runtime-schema/models"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"
)

var auctioneer ifrit.Process
var dummyActions = []models.ExecutorAction{
	{
		Action: models.RunAction{
			Path: "cat",
			Args: []string{"/tmp/file"},
		},
	},
}

var _ = Describe("Integration", func() {
	BeforeEach(func() {
		auctioneer = ginkgomon.Invoke(runner)
	})

	AfterEach(func() {
		auctioneer.Signal(os.Kill)
		Eventually(auctioneer.Wait()).Should(Receive())
	})

	Context("when a start auction message arrives", func() {
		BeforeEach(func() {
			bbs.RequestLRPStartAuction(models.LRPStartAuction{
				DesiredLRP: models.DesiredLRP{
					ProcessGuid: "app-guid",
					DiskMB:      1,
					MemoryMB:    1,
					Stack:       lucidStack,
					Actions:     dummyActions,
				},

				InstanceGuid: "instance-guid-1",
				Index:        0,
			})

			bbs.RequestLRPStartAuction(models.LRPStartAuction{
				DesiredLRP: models.DesiredLRP{
					ProcessGuid: "app-guid",
					DiskMB:      1,
					MemoryMB:    1,
					Stack:       lucidStack,
					Actions:     dummyActions,
				},

				InstanceGuid: "instance-guid-2",
				Index:        1,
			})
		})

		It("should start the app running on reps of the appropriate stack", func() {
			Eventually(func() interface{} {
				return repClient.SimulatedInstances(lucidGuid)
			}).Should(HaveLen(2))

			Ω(repClient.SimulatedInstances(dotNetGuid)).Should(BeEmpty())
		})
	})

	Context("when a stop auction message arrives", func() {
		BeforeEach(func() {
			bbs.RequestLRPStartAuction(models.LRPStartAuction{
				DesiredLRP: models.DesiredLRP{
					ProcessGuid: "app-guid",
					DiskMB:      1,
					MemoryMB:    1,
					Stack:       lucidStack,
					Actions:     dummyActions,
				},

				InstanceGuid: "duplicate-instance-guid-1",
				Index:        0,
			})

			Eventually(bbs.GetAllLRPStartAuctions).Should(HaveLen(0))

			bbs.RequestLRPStartAuction(models.LRPStartAuction{
				DesiredLRP: models.DesiredLRP{
					ProcessGuid: "app-guid",
					DiskMB:      1,
					MemoryMB:    1,
					Stack:       lucidStack,
					Actions:     dummyActions,
				},

				InstanceGuid: "duplicate-instance-guid-2",
				Index:        0,
			})

			Eventually(bbs.GetAllLRPStartAuctions).Should(HaveLen(0))

			bbs.RequestLRPStartAuction(models.LRPStartAuction{
				DesiredLRP: models.DesiredLRP{
					ProcessGuid: "app-guid",
					DiskMB:      1,
					MemoryMB:    1,
					Stack:       lucidStack,
					Actions:     dummyActions,
				},

				InstanceGuid: "duplicate-instance-guid-3",
				Index:        0,
			})

			Eventually(bbs.GetAllLRPStartAuctions).Should(HaveLen(0))

			Ω(repClient.SimulatedInstances(lucidGuid)).Should(HaveLen(3))
		})

		It("should stop all but one instance of the app", func() {
			bbs.RequestLRPStopAuction(models.LRPStopAuction{
				ProcessGuid: "app-guid",
				Index:       0,
			})

			Eventually(func() interface{} {
				return repClient.SimulatedInstances(lucidGuid)
			}).Should(HaveLen(1))
		})
	})
})
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/url"
	"os"

	functions "github.com/funcy/functions_go"
	"github.com/urfave/cli"
)

// Version of Functions CLI
var Version = "0.3.55"

func version() cli.Command {
	r := versionCmd{VersionApi: functions.NewVersionApi()}
	return cli.Command{
		Name:   "version",
		Usage:  "displays fn and functions daemon versions",
		Action: r.version,
	}
}

type versionCmd struct {
	*functions.VersionApi
}

func (r *versionCmd) version(c *cli.Context) error {
	apiURL := os.Getenv("API_URL")
	if apiURL == "" {
		apiURL = "http://localhost:8080"
	}

	u, err := url.Parse(apiURL)
	if err != nil {
		return err
	}
	r.Configuration.BasePath = u.String()

	fmt.Println("Client version:", Version)
	v, _, err := r.VersionGet()
	if err != nil {
		return err
	}
	fmt.Println("Server version", v.Version)
	return nil
}
<commit_msg>fn tool: 0.3.56 release [skip ci]<commit_after>package main

import (
	"fmt"
	"net/url"
	"os"

	functions "github.com/funcy/functions_go"
	"github.com/urfave/cli"
)

// Version of Functions CLI
var Version = "0.3.56"

func version() cli.Command {
	r := versionCmd{VersionApi: functions.NewVersionApi()}
	return cli.Command{
		Name:   "version",
		Usage:  "displays fn and functions daemon versions",
		Action: r.version,
	}
}

type versionCmd struct {
	*functions.VersionApi
}

func (r *versionCmd) version(c *cli.Context) error {
	apiURL := os.Getenv("API_URL")
	if apiURL == "" {
		apiURL = "http://localhost:8080"
	}

	u, err := url.Parse(apiURL)
	if err != nil {
		return err
	}
	r.Configuration.BasePath = u.String()

	fmt.Println("Client version:", Version)
	v, _, err := r.VersionGet()
	if err != nil {
		return err
	}
	fmt.Println("Server version", v.Version)
	return nil
}
<|endoftext|>


<commit_before>package azure

import (
	"context"
	"io"
	"os"
	"path"
	"strings"
	"time"

	"github.com/Azure/azure-sdk-for-go/storage"
	"github.com/pkg/errors"
	"github.com/restic/restic/internal/backend"
	"github.com/restic/restic/internal/debug"
	"github.com/restic/restic/internal/restic"
)

// Backend stores data on an azure endpoint.
type Backend struct {
	accountName string
	container   *storage.Container
	sem         *backend.Semaphore
	prefix      string
	backend.Layout
}

// make sure that *Backend implements backend.Backend
var _ restic.Backend = &Backend{}

func open(cfg Config) (*Backend, error) {
	debug.Log("open, config %#v", cfg)

	client, err := storage.NewBasicClient(cfg.AccountName, cfg.AccountKey)
	if err != nil {
		return nil, errors.Wrap(err, "NewBasicClient")
	}

	service := client.GetBlobService()

	sem, err := backend.NewSemaphore(cfg.Connections)
	if err != nil {
		return nil, err
	}

	be := &Backend{
		container:   service.GetContainerReference(cfg.Container),
		accountName: cfg.AccountName,
		sem:         sem,
		prefix:      cfg.Prefix,
		Layout: &backend.DefaultLayout{
			Path: cfg.Prefix,
			Join: path.Join,
		},
	}

	return be, nil
}

// Open opens the Azure backend at specified container.
func Open(cfg Config) (restic.Backend, error) {
	return open(cfg)
}

// Create opens the Azure backend at specified container and creates the container if
// it does not exist yet.
func Create(cfg Config) (restic.Backend, error) {
	be, err := open(cfg)

	if err != nil {
		return nil, errors.Wrap(err, "open")
	}

	options := storage.CreateContainerOptions{
		Access: storage.ContainerAccessTypePrivate,
	}

	_, err = be.container.CreateIfNotExists(&options)
	if err != nil {
		return nil, errors.Wrap(err, "container.CreateIfNotExists")
	}

	return be, nil
}

// IsNotExist returns true if the error is caused by a not existing file.
func (be *Backend) IsNotExist(err error) bool {
	debug.Log("IsNotExist(%T, %#v)", err, err)
	return os.IsNotExist(err)
}

// Join combines path components with slashes.
func (be *Backend) Join(p ...string) string {
	return path.Join(p...)
}

type fileInfo struct {
	name    string
	size    int64
	mode    os.FileMode
	modTime time.Time
	isDir   bool
}

func (fi fileInfo) Name() string       { return fi.name }    // base name of the file
func (fi fileInfo) Size() int64        { return fi.size }    // length in bytes for regular files; system-dependent for others
func (fi fileInfo) Mode() os.FileMode  { return fi.mode }    // file mode bits
func (fi fileInfo) ModTime() time.Time { return fi.modTime } // modification time
func (fi fileInfo) IsDir() bool        { return fi.isDir }   // abbreviation for Mode().IsDir()
func (fi fileInfo) Sys() interface{}   { return nil }        // underlying data source (can return nil)

// ReadDir returns the entries for a directory.
func (be *Backend) ReadDir(dir string) (list []os.FileInfo, err error) {
	debug.Log("ReadDir(%v)", dir)

	// make sure dir ends with a slash
	if dir[len(dir)-1] != '/' {
		dir += "/"
	}

	obj, err := be.container.ListBlobs(storage.ListBlobsParameters{Prefix: dir, Delimiter: "/"})
	if err != nil {
		return nil, err
	}

	for _, item := range obj.BlobPrefixes {
		entry := fileInfo{
			name:  strings.TrimPrefix(item, dir),
			isDir: true,
			mode:  os.ModeDir | 0755,
		}
		if entry.name != "" {
			list = append(list, entry)
		}
	}

	for _, item := range obj.Blobs {
		entry := fileInfo{
			name:    strings.TrimPrefix(item.Name, dir),
			isDir:   false,
			mode:    0644,
			size:    item.Properties.ContentLength,
			modTime: time.Time(item.Properties.LastModified),
		}
		if entry.name != "" {
			list = append(list, entry)
		}
	}

	return list, nil
}

// Location returns this backend's location (the container name).
func (be *Backend) Location() string {
	return be.Join(be.container.Name, be.prefix)
}

// Path returns the path in the bucket that is used for this backend.
func (be *Backend) Path() string {
	return be.prefix
}

// preventCloser wraps an io.Reader to run a function instead of the original Close() function.
type preventCloser struct {
	io.Reader
	f func()
}

func (wr preventCloser) Close() error {
	wr.f()
	return nil
}

// Save stores data in the backend at the handle.
func (be *Backend) Save(ctx context.Context, h restic.Handle, rd io.Reader) (err error) {
	if err := h.Valid(); err != nil {
		return err
	}

	objName := be.Filename(h)

	debug.Log("Save %v at %v", h, objName)

	// Check key does not already exist
	found, err := be.container.GetBlobReference(objName).Exists()
	if err != nil {
		return errors.Wrap(err, "GetBlobReference().Exists()")
	}
	if found {
		debug.Log("%v already exists", h)
		return errors.New("key already exists")
	}

	be.sem.GetToken()

	// wrap the reader so that net/http client cannot close the reader, return
	// the token instead.
	rd = preventCloser{
		Reader: rd,
		f: func() {
			debug.Log("Close()")
		},
	}

	debug.Log("InsertObject(%v, %v)", be.container.Name, objName)

	err = be.container.GetBlobReference(objName).CreateBlockBlobFromReader(rd, nil)

	be.sem.ReleaseToken()
	debug.Log("%v, err %#v", objName, err)

	return errors.Wrap(err, "CreateBlockBlobFromReader")
}

// wrapReader wraps an io.ReadCloser to run an additional function on Close.
type wrapReader struct {
	io.ReadCloser
	f func()
}

func (wr wrapReader) Close() error {
	err := wr.ReadCloser.Close()
	wr.f()
	return err
}

// Load returns a reader that yields the contents of the file at h at the
// given offset. If length is nonzero, only a portion of the file is
// returned. rd must be closed after use.
func (be *Backend) Load(ctx context.Context, h restic.Handle, length int, offset int64) (io.ReadCloser, error) {
	debug.Log("Load %v, length %v, offset %v from %v", h, length, offset, be.Filename(h))
	if err := h.Valid(); err != nil {
		return nil, err
	}

	if offset < 0 {
		return nil, errors.New("offset is negative")
	}

	if length < 0 {
		return nil, errors.Errorf("invalid length %d", length)
	}

	objName := be.Filename(h)
	blob := be.container.GetBlobReference(objName)

	start := uint64(offset)
	var end uint64

	if length > 0 {
		end = uint64(offset + int64(length) - 1)
	} else {
		end = 0
	}

	be.sem.GetToken()

	rd, err := blob.GetRange(&storage.GetBlobRangeOptions{Range: &storage.BlobRange{Start: start, End: end}})
	if err != nil {
		be.sem.ReleaseToken()
		return nil, err
	}

	closeRd := wrapReader{
		ReadCloser: rd,
		f: func() {
			debug.Log("Close()")
			be.sem.ReleaseToken()
		},
	}

	return closeRd, err
}

// Stat returns information about a blob.
func (be *Backend) Stat(ctx context.Context, h restic.Handle) (bi restic.FileInfo, err error) {
	debug.Log("%v", h)

	objName := be.Filename(h)
	blob := be.container.GetBlobReference(objName)

	if err := blob.GetProperties(nil); err != nil {
		debug.Log("blob.GetProperties err %v", err)
		return restic.FileInfo{}, errors.Wrap(err, "blob.GetProperties")
	}

	return restic.FileInfo{Size: int64(blob.Properties.ContentLength)}, nil
}

// Test returns true if a blob of the given type and name exists in the backend.
func (be *Backend) Test(ctx context.Context, h restic.Handle) (bool, error) {
	objName := be.Filename(h)
	found, err := be.container.GetBlobReference(objName).Exists()
	if err != nil {
		return false, err
	}
	return found, nil
}

// Remove removes the blob with the given name and type.
func (be *Backend) Remove(ctx context.Context, h restic.Handle) error {
	objName := be.Filename(h)
	_, err := be.container.GetBlobReference(objName).DeleteIfExists(nil)
	debug.Log("Remove(%v) at %v -> err %v", h, objName, err)
	return errors.Wrap(err, "client.RemoveObject")
}

// List returns a channel that yields all names of blobs of type t. A
// goroutine is started for this. If the channel done is closed, sending
// stops.
func (be *Backend) List(ctx context.Context, t restic.FileType) <-chan string {
	debug.Log("listing %v", t)
	ch := make(chan string)

	prefix := be.Dirname(restic.Handle{Type: t})

	// make sure prefix ends with a slash
	if prefix[len(prefix)-1] != '/' {
		prefix += "/"
	}

	go func() {
		defer close(ch)

		obj, err := be.container.ListBlobs(storage.ListBlobsParameters{Prefix: prefix})
		if err != nil {
			return
		}

		for _, item := range obj.Blobs {
			m := strings.TrimPrefix(item.Name, prefix)
			if m == "" {
				continue
			}

			select {
			case ch <- path.Base(m):
			case <-ctx.Done():
				return
			}
		}
	}()

	return ch
}

// Remove keys for a specified backend type.
func (be *Backend) removeKeys(ctx context.Context, t restic.FileType) error {
	for key := range be.List(ctx, restic.DataFile) {
		err := be.Remove(ctx, restic.Handle{Type: restic.DataFile, Name: key})
		if err != nil {
			return err
		}
	}

	return nil
}

// Delete removes all restic keys in the bucket. It will not remove the bucket itself.
func (be *Backend) Delete(ctx context.Context) error {
	alltypes := []restic.FileType{
		restic.DataFile,
		restic.KeyFile,
		restic.LockFile,
		restic.SnapshotFile,
		restic.IndexFile}

	for _, t := range alltypes {
		err := be.removeKeys(ctx, t)
		if err != nil {
			return nil
		}
	}

	return be.Remove(ctx, restic.Handle{Type: restic.ConfigFile})
}

// Close does nothing
func (be *Backend) Close() error { return nil }
<commit_msg>Azure: Use default HTTP transport<commit_after>package azure

import (
	"context"
	"io"
	"net/http"
	"os"
	"path"
	"strings"
	"time"

	"github.com/Azure/azure-sdk-for-go/storage"
	"github.com/pkg/errors"
	"github.com/restic/restic/internal/backend"
	"github.com/restic/restic/internal/debug"
	"github.com/restic/restic/internal/restic"
)

// Backend stores data on an azure endpoint.
type Backend struct {
	accountName string
	container   *storage.Container
	sem         *backend.Semaphore
	prefix      string
	backend.Layout
}

// make sure that *Backend implements backend.Backend
var _ restic.Backend = &Backend{}

func open(cfg Config) (*Backend, error) {
	debug.Log("open, config %#v", cfg)

	client, err := storage.NewBasicClient(cfg.AccountName, cfg.AccountKey)
	if err != nil {
		return nil, errors.Wrap(err, "NewBasicClient")
	}

	client.HTTPClient = &http.Client{Transport: backend.Transport()}

	service := client.GetBlobService()

	sem, err := backend.NewSemaphore(cfg.Connections)
	if err != nil {
		return nil, err
	}

	be := &Backend{
		container:   service.GetContainerReference(cfg.Container),
		accountName: cfg.AccountName,
		sem:         sem,
		prefix:      cfg.Prefix,
		Layout: &backend.DefaultLayout{
			Path: cfg.Prefix,
			Join: path.Join,
		},
	}

	return be, nil
}

// Open opens the Azure backend at specified container.
func Open(cfg Config) (restic.Backend, error) {
	return open(cfg)
}

// Create opens the Azure backend at specified container and creates the container if
// it does not exist yet.
func Create(cfg Config) (restic.Backend, error) {
	be, err := open(cfg)

	if err != nil {
		return nil, errors.Wrap(err, "open")
	}

	options := storage.CreateContainerOptions{
		Access: storage.ContainerAccessTypePrivate,
	}

	_, err = be.container.CreateIfNotExists(&options)
	if err != nil {
		return nil, errors.Wrap(err, "container.CreateIfNotExists")
	}

	return be, nil
}

// IsNotExist returns true if the error is caused by a not existing file.
func (be *Backend) IsNotExist(err error) bool {
	debug.Log("IsNotExist(%T, %#v)", err, err)
	return os.IsNotExist(err)
}

// Join combines path components with slashes.
func (be *Backend) Join(p ...string) string {
	return path.Join(p...)
}

type fileInfo struct {
	name    string
	size    int64
	mode    os.FileMode
	modTime time.Time
	isDir   bool
}

func (fi fileInfo) Name() string       { return fi.name }    // base name of the file
func (fi fileInfo) Size() int64        { return fi.size }    // length in bytes for regular files; system-dependent for others
func (fi fileInfo) Mode() os.FileMode  { return fi.mode }    // file mode bits
func (fi fileInfo) ModTime() time.Time { return fi.modTime } // modification time
func (fi fileInfo) IsDir() bool        { return fi.isDir }   // abbreviation for Mode().IsDir()
func (fi fileInfo) Sys() interface{}   { return nil }        // underlying data source (can return nil)

// ReadDir returns the entries for a directory.
func (be *Backend) ReadDir(dir string) (list []os.FileInfo, err error) {
	debug.Log("ReadDir(%v)", dir)

	// make sure dir ends with a slash
	if dir[len(dir)-1] != '/' {
		dir += "/"
	}

	obj, err := be.container.ListBlobs(storage.ListBlobsParameters{Prefix: dir, Delimiter: "/"})
	if err != nil {
		return nil, err
	}

	for _, item := range obj.BlobPrefixes {
		entry := fileInfo{
			name:  strings.TrimPrefix(item, dir),
			isDir: true,
			mode:  os.ModeDir | 0755,
		}
		if entry.name != "" {
			list = append(list, entry)
		}
	}

	for _, item := range obj.Blobs {
		entry := fileInfo{
			name:    strings.TrimPrefix(item.Name, dir),
			isDir:   false,
			mode:    0644,
			size:    item.Properties.ContentLength,
			modTime: time.Time(item.Properties.LastModified),
		}
		if entry.name != "" {
			list = append(list, entry)
		}
	}

	return list, nil
}

// Location returns this backend's location (the container name).
func (be *Backend) Location() string {
	return be.Join(be.container.Name, be.prefix)
}

// Path returns the path in the bucket that is used for this backend.
func (be *Backend) Path() string {
	return be.prefix
}

// preventCloser wraps an io.Reader to run a function instead of the original Close() function.
type preventCloser struct {
	io.Reader
	f func()
}

func (wr preventCloser) Close() error {
	wr.f()
	return nil
}

// Save stores data in the backend at the handle.
func (be *Backend) Save(ctx context.Context, h restic.Handle, rd io.Reader) (err error) {
	if err := h.Valid(); err != nil {
		return err
	}

	objName := be.Filename(h)

	debug.Log("Save %v at %v", h, objName)

	// Check key does not already exist
	found, err := be.container.GetBlobReference(objName).Exists()
	if err != nil {
		return errors.Wrap(err, "GetBlobReference().Exists()")
	}
	if found {
		debug.Log("%v already exists", h)
		return errors.New("key already exists")
	}

	be.sem.GetToken()

	// wrap the reader so that net/http client cannot close the reader, return
	// the token instead.
	rd = preventCloser{
		Reader: rd,
		f: func() {
			debug.Log("Close()")
		},
	}

	debug.Log("InsertObject(%v, %v)", be.container.Name, objName)

	err = be.container.GetBlobReference(objName).CreateBlockBlobFromReader(rd, nil)

	be.sem.ReleaseToken()
	debug.Log("%v, err %#v", objName, err)

	return errors.Wrap(err, "CreateBlockBlobFromReader")
}

// wrapReader wraps an io.ReadCloser to run an additional function on Close.
type wrapReader struct {
	io.ReadCloser
	f func()
}

func (wr wrapReader) Close() error {
	err := wr.ReadCloser.Close()
	wr.f()
	return err
}

// Load returns a reader that yields the contents of the file at h at the
// given offset. If length is nonzero, only a portion of the file is
// returned. rd must be closed after use.
func (be *Backend) Load(ctx context.Context, h restic.Handle, length int, offset int64) (io.ReadCloser, error) {
	debug.Log("Load %v, length %v, offset %v from %v", h, length, offset, be.Filename(h))
	if err := h.Valid(); err != nil {
		return nil, err
	}

	if offset < 0 {
		return nil, errors.New("offset is negative")
	}

	if length < 0 {
		return nil, errors.Errorf("invalid length %d", length)
	}

	objName := be.Filename(h)
	blob := be.container.GetBlobReference(objName)

	start := uint64(offset)
	var end uint64

	if length > 0 {
		end = uint64(offset + int64(length) - 1)
	} else {
		end = 0
	}

	be.sem.GetToken()

	rd, err := blob.GetRange(&storage.GetBlobRangeOptions{Range: &storage.BlobRange{Start: start, End: end}})
	if err != nil {
		be.sem.ReleaseToken()
		return nil, err
	}

	closeRd := wrapReader{
		ReadCloser: rd,
		f: func() {
			debug.Log("Close()")
			be.sem.ReleaseToken()
		},
	}

	return closeRd, err
}

// Stat returns information about a blob.
func (be *Backend) Stat(ctx context.Context, h restic.Handle) (bi restic.FileInfo, err error) {
	debug.Log("%v", h)

	objName := be.Filename(h)
	blob := be.container.GetBlobReference(objName)

	if err := blob.GetProperties(nil); err != nil {
		debug.Log("blob.GetProperties err %v", err)
		return restic.FileInfo{}, errors.Wrap(err, "blob.GetProperties")
	}

	return restic.FileInfo{Size: int64(blob.Properties.ContentLength)}, nil
}

// Test returns true if a blob of the given type and name exists in the backend.
func (be *Backend) Test(ctx context.Context, h restic.Handle) (bool, error) {
	objName := be.Filename(h)
	found, err := be.container.GetBlobReference(objName).Exists()
	if err != nil {
		return false, err
	}
	return found, nil
}

// Remove removes the blob with the given name and type.
func (be *Backend) Remove(ctx context.Context, h restic.Handle) error {
	objName := be.Filename(h)
	_, err := be.container.GetBlobReference(objName).DeleteIfExists(nil)
	debug.Log("Remove(%v) at %v -> err %v", h, objName, err)
	return errors.Wrap(err, "client.RemoveObject")
}

// List returns a channel that yields all names of blobs of type t. A
// goroutine is started for this. If the channel done is closed, sending
// stops.
func (be *Backend) List(ctx context.Context, t restic.FileType) <-chan string {
	debug.Log("listing %v", t)
	ch := make(chan string)

	prefix := be.Dirname(restic.Handle{Type: t})

	// make sure prefix ends with a slash
	if prefix[len(prefix)-1] != '/' {
		prefix += "/"
	}

	go func() {
		defer close(ch)

		obj, err := be.container.ListBlobs(storage.ListBlobsParameters{Prefix: prefix})
		if err != nil {
			return
		}

		for _, item := range obj.Blobs {
			m := strings.TrimPrefix(item.Name, prefix)
			if m == "" {
				continue
			}

			select {
			case ch <- path.Base(m):
			case <-ctx.Done():
				return
			}
		}
	}()

	return ch
}

// Remove keys for a specified backend type.
func (be *Backend) removeKeys(ctx context.Context, t restic.FileType) error {
	for key := range be.List(ctx, restic.DataFile) {
		err := be.Remove(ctx, restic.Handle{Type: restic.DataFile, Name: key})
		if err != nil {
			return err
		}
	}

	return nil
}

// Delete removes all restic keys in the bucket. It will not remove the bucket itself.
func (be *Backend) Delete(ctx context.Context) error {
	alltypes := []restic.FileType{
		restic.DataFile,
		restic.KeyFile,
		restic.LockFile,
		restic.SnapshotFile,
		restic.IndexFile}

	for _, t := range alltypes {
		err := be.removeKeys(ctx, t)
		if err != nil {
			return nil
		}
	}

	return be.Remove(ctx, restic.Handle{Type: restic.ConfigFile})
}

// Close does nothing
func (be *Backend) Close() error { return nil }
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2018 gRPC authors.
 *
 * 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 envconfig contains grpc settings configured by environment variables.
package envconfig

import (
	"os"
	"strings"

	xdsenv "google.golang.org/grpc/internal/xds/env"
)

const (
	prefix          = "GRPC_GO_"
	retryStr        = prefix + "RETRY"
	txtErrIgnoreStr = prefix + "IGNORE_TXT_ERRORS"
)

var (
	// Retry is set if retry is explicitly enabled via "GRPC_GO_RETRY=on" or if XDS retry support is enabled.
	Retry = strings.EqualFold(os.Getenv(retryStr), "on") || xdsenv.RetrySupport
	// TXTErrIgnore is set if TXT errors should be ignored ("GRPC_GO_IGNORE_TXT_ERRORS" is not "false").
	TXTErrIgnore = !strings.EqualFold(os.Getenv(txtErrIgnoreStr), "false")
)
<commit_msg>client: properly disable retry if GRPC_GO_RETRY=off (#4899)<commit_after>/*
 *
 * Copyright 2018 gRPC authors.
 *
 * 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 envconfig contains grpc settings configured by environment variables.
package envconfig

import (
	"os"
	"strings"

	xdsenv "google.golang.org/grpc/internal/xds/env"
)

const (
	prefix          = "GRPC_GO_"
	retryStr        = prefix + "RETRY"
	txtErrIgnoreStr = prefix + "IGNORE_TXT_ERRORS"
)

var (
	// Retry is enabled unless explicitly disabled via "GRPC_GO_RETRY=off" or
	// if XDS retry support is explicitly disabled.
	Retry = !strings.EqualFold(os.Getenv(retryStr), "off") && xdsenv.RetrySupport
	// TXTErrIgnore is set if TXT errors should be ignored ("GRPC_GO_IGNORE_TXT_ERRORS" is not "false").
	TXTErrIgnore = !strings.EqualFold(os.Getenv(txtErrIgnoreStr), "false")
)
<|endoftext|>


<commit_before>// Copyright 2020 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 frontend

import (
	"context"
	"net/http"
	"strconv"
	"testing"
	"time"

	"golang.org/x/pkgsite/internal"
	"golang.org/x/pkgsite/internal/derrors"
	"golang.org/x/pkgsite/internal/experiment"
	"golang.org/x/pkgsite/internal/proxy"
	"golang.org/x/pkgsite/internal/testing/sample"
	"golang.org/x/pkgsite/internal/testing/testhelper"
)

var (
	sourceTimeout       = 1 * time.Second
	testModulePath      = "github.com/module"
	testSemver          = "v1.5.2"
	testFetchTimeout    = 100 * time.Second
	testModulesForProxy = []*proxy.TestModule{
		{
			ModulePath: testModulePath,
			Version:    testSemver,
			Files: map[string]string{
				"bar/foo/foo.go": "// Package foo\npackage foo\n\nconst Foo = 42",
				"README.md":      "This is a readme",
				"LICENSE":        testhelper.MITLicense,
			},
		},
	}
)

func TestFetch(t *testing.T) {
	for _, test := range []struct {
		name, fullPath, version, want string
	}{
		{
			name:     "path at master package is in module root",
			fullPath: testModulePath,
			version:  internal.MasterVersion,
		},
		{
			name:     "path at latest package is in module root",
			fullPath: testModulePath,
			version:  internal.LatestVersion,
		},
		{
			name:     "path at semver package is in module root",
			fullPath: testModulePath,
			version:  "v1.5.2",
		},
		{
			name:     "package at latest package is not in module root",
			fullPath: testModulePath + "/bar/foo",
			version:  internal.LatestVersion,
		},
		{
			name:     "directory at master package is not in module root",
			fullPath: testModulePath + "/bar",
			version:  internal.MasterVersion,
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			s, _, teardown := newTestServer(t, testModulesForProxy)
			defer teardown()

			ctx, cancel := context.WithTimeout(context.Background(), testFetchTimeout)
			defer cancel()
			ctx = experiment.NewContext(ctx,
				internal.ExperimentFrontendFetch,
				internal.ExperimentMasterVersion,
				internal.ExperimentUsePathInfo)

			status, responseText := s.fetchAndPoll(ctx, testModulePath, test.fullPath, test.version)
			if status != http.StatusOK {
				t.Fatalf("fetchAndPoll(%q, %q, %q) = %d, %s; want status = %d",
					testModulePath, test.fullPath, test.version, status, responseText, http.StatusOK)
			}
		})
	}
}

func TestFetchErrors(t *testing.T) {
	for _, test := range []struct {
		name, modulePath, fullPath, version string
		fetchTimeout                        time.Duration
		want                                int
	}{
		{
			name:       "non-existent module",
			modulePath: "github.com/nonexistent",
			fullPath:   "github.com/nonexistent",
			version:    internal.LatestVersion,
			want:       http.StatusNotFound,
		},
		{
			name:       "version invalid",
			modulePath: testModulePath,
			fullPath:   testModulePath,
			version:    "random-version",
			want:       http.StatusBadRequest,
		},
		{
			name:       "module found but package does not exist",
			modulePath: testModulePath,
			fullPath:   "github.com/module/pkg-nonexistent",
			version:    internal.LatestVersion,
			want:       http.StatusNotFound,
		},
		{
			name:         "module exists but fetch timed out",
			modulePath:   testModulePath,
			fullPath:     testModulePath,
			version:      internal.LatestVersion,
			fetchTimeout: 1 * time.Millisecond,
			want:         http.StatusRequestTimeout,
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			if test.fetchTimeout == 0 {
				test.fetchTimeout = testFetchTimeout
			}
			ctx, cancel := context.WithTimeout(context.Background(), test.fetchTimeout)
			defer cancel()

			ctx = experiment.NewContext(ctx, internal.ExperimentFrontendFetch)
			s, _, teardown := newTestServer(t, testModulesForProxy)
			defer teardown()
			got, _ := s.fetchAndPoll(ctx, test.modulePath, test.fullPath, test.version)
			if got != test.want {
				t.Fatalf("fetchAndPoll(ctx, testDB, q, %q, %q, %q): %d; want = %d",
					test.modulePath, test.fullPath, test.version, got, test.want)
			}
		})
	}
}

func TestFetchPathAlreadyExists(t *testing.T) {
	for _, status := range []int{
		http.StatusOK,
		http.StatusNotFound,
		derrors.ToHTTPStatus(derrors.AlternativeModule),
	} {
		t.Run(strconv.Itoa(status), func(t *testing.T) {
			ctx, cancel := context.WithTimeout(context.Background(), testFetchTimeout)
			defer cancel()
			ctx = experiment.NewContext(ctx,
				internal.ExperimentFrontendFetch,
			)
			if err := testDB.InsertModule(ctx, sample.DefaultModule()); err != nil {
				t.Fatal(err)
			}
			if err := testDB.UpsertVersionMap(ctx, &internal.VersionMap{
				ModulePath:       sample.ModulePath,
				RequestedVersion: sample.VersionString,
				ResolvedVersion:  sample.VersionString,
				Status:           status,
			}); err != nil {
				t.Fatal(err)
			}

			s, _, teardown := newTestServer(t, testModulesForProxy)
			defer teardown()
			got, _ := s.fetchAndPoll(ctx, sample.ModulePath, sample.PackagePath, sample.VersionString)
			if got != status {
				t.Fatalf("fetchAndPoll for status %d: %d; want = %d)", status, got, status)
			}
		})
	}
}
<commit_msg>internal/frontend: fix TestFetchPathAlreadyExists/491<commit_after>// Copyright 2020 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 frontend

import (
	"context"
	"net/http"
	"strconv"
	"testing"
	"time"

	"golang.org/x/pkgsite/internal"
	"golang.org/x/pkgsite/internal/derrors"
	"golang.org/x/pkgsite/internal/experiment"
	"golang.org/x/pkgsite/internal/proxy"
	"golang.org/x/pkgsite/internal/testing/sample"
	"golang.org/x/pkgsite/internal/testing/testhelper"
)

var (
	sourceTimeout       = 1 * time.Second
	testModulePath      = "github.com/module"
	testSemver          = "v1.5.2"
	testFetchTimeout    = 100 * time.Second
	testModulesForProxy = []*proxy.TestModule{
		{
			ModulePath: testModulePath,
			Version:    testSemver,
			Files: map[string]string{
				"bar/foo/foo.go": "// Package foo\npackage foo\n\nconst Foo = 42",
				"README.md":      "This is a readme",
				"LICENSE":        testhelper.MITLicense,
			},
		},
	}
)

func TestFetch(t *testing.T) {
	for _, test := range []struct {
		name, fullPath, version, want string
	}{
		{
			name:     "path at master package is in module root",
			fullPath: testModulePath,
			version:  internal.MasterVersion,
		},
		{
			name:     "path at latest package is in module root",
			fullPath: testModulePath,
			version:  internal.LatestVersion,
		},
		{
			name:     "path at semver package is in module root",
			fullPath: testModulePath,
			version:  "v1.5.2",
		},
		{
			name:     "package at latest package is not in module root",
			fullPath: testModulePath + "/bar/foo",
			version:  internal.LatestVersion,
		},
		{
			name:     "directory at master package is not in module root",
			fullPath: testModulePath + "/bar",
			version:  internal.MasterVersion,
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			s, _, teardown := newTestServer(t, testModulesForProxy)
			defer teardown()

			ctx, cancel := context.WithTimeout(context.Background(), testFetchTimeout)
			defer cancel()
			ctx = experiment.NewContext(ctx,
				internal.ExperimentFrontendFetch,
				internal.ExperimentMasterVersion,
				internal.ExperimentUsePathInfo)

			status, responseText := s.fetchAndPoll(ctx, testModulePath, test.fullPath, test.version)
			if status != http.StatusOK {
				t.Fatalf("fetchAndPoll(%q, %q, %q) = %d, %s; want status = %d",
					testModulePath, test.fullPath, test.version, status, responseText, http.StatusOK)
			}
		})
	}
}

func TestFetchErrors(t *testing.T) {
	for _, test := range []struct {
		name, modulePath, fullPath, version string
		fetchTimeout                        time.Duration
		want                                int
	}{
		{
			name:       "non-existent module",
			modulePath: "github.com/nonexistent",
			fullPath:   "github.com/nonexistent",
			version:    internal.LatestVersion,
			want:       http.StatusNotFound,
		},
		{
			name:       "version invalid",
			modulePath: testModulePath,
			fullPath:   testModulePath,
			version:    "random-version",
			want:       http.StatusBadRequest,
		},
		{
			name:       "module found but package does not exist",
			modulePath: testModulePath,
			fullPath:   "github.com/module/pkg-nonexistent",
			version:    internal.LatestVersion,
			want:       http.StatusNotFound,
		},
		{
			name:         "module exists but fetch timed out",
			modulePath:   testModulePath,
			fullPath:     testModulePath,
			version:      internal.LatestVersion,
			fetchTimeout: 1 * time.Millisecond,
			want:         http.StatusRequestTimeout,
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			if test.fetchTimeout == 0 {
				test.fetchTimeout = testFetchTimeout
			}
			ctx, cancel := context.WithTimeout(context.Background(), test.fetchTimeout)
			defer cancel()

			ctx = experiment.NewContext(ctx, internal.ExperimentFrontendFetch)
			s, _, teardown := newTestServer(t, testModulesForProxy)
			defer teardown()
			got, _ := s.fetchAndPoll(ctx, test.modulePath, test.fullPath, test.version)
			if got != test.want {
				t.Fatalf("fetchAndPoll(ctx, testDB, q, %q, %q, %q): %d; want = %d",
					test.modulePath, test.fullPath, test.version, got, test.want)
			}
		})
	}
}

func TestFetchPathAlreadyExists(t *testing.T) {
	for _, test := range []struct {
		status, want int
	}{
		{http.StatusOK, http.StatusOK},
		{http.StatusNotFound, http.StatusNotFound},
		{derrors.ToHTTPStatus(derrors.AlternativeModule), http.StatusSeeOther},
	} {
		t.Run(strconv.Itoa(test.status), func(t *testing.T) {
			ctx, cancel := context.WithTimeout(context.Background(), testFetchTimeout)
			defer cancel()
			ctx = experiment.NewContext(ctx,
				internal.ExperimentFrontendFetch,
			)
			if err := testDB.InsertModule(ctx, sample.DefaultModule()); err != nil {
				t.Fatal(err)
			}
			if err := testDB.UpsertVersionMap(ctx, &internal.VersionMap{
				ModulePath:       sample.ModulePath,
				RequestedVersion: sample.VersionString,
				ResolvedVersion:  sample.VersionString,
				Status:           test.status,
			}); err != nil {
				t.Fatal(err)
			}

			s, _, teardown := newTestServer(t, testModulesForProxy)
			defer teardown()
			got, _ := s.fetchAndPoll(ctx, sample.ModulePath, sample.PackagePath, sample.VersionString)
			if got != test.want {
				t.Fatalf("fetchAndPoll for status %d: %d; want = %d)", test.status, got, test.want)
			}
		})
	}
}
<|endoftext|>


<commit_before>// Copyright 2019 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 postgres

import (
	"context"
	"database/sql"
	"fmt"

	"golang.org/x/pkgsite/internal"
	"golang.org/x/pkgsite/internal/derrors"
)

// GetExperiments fetches all experiments in the database.
func (db *DB) GetExperiments(ctx context.Context) (_ []*internal.Experiment, err error) {
	defer derrors.Wrap(&err, "DB.GetExperiments(ctx)")

	query := "SELECT name, rollout, description FROM experiments;"
	var experiments []*internal.Experiment
	err = db.db.RunQuery(ctx, query, func(rows *sql.Rows) error {
		var e internal.Experiment
		if err := rows.Scan(&e.Name, &e.Rollout, &e.Description); err != nil {
			return err
		}
		experiments = append(experiments, &e)
		return nil
	})
	return experiments, nil
}

// InsertExperiment inserts a row into the experiments table.
func (db *DB) InsertExperiment(ctx context.Context, e *internal.Experiment) (err error) {
	defer derrors.Wrap(&err, "DB.insertExperiment(ctx, %v)", e)
	if e.Name == "" || e.Description == "" {
		return fmt.Errorf("neither name nor description can be empty: %w", derrors.InvalidArgument)
	}

	_, err = db.db.Exec(ctx,
		`INSERT INTO experiments
		(name, rollout, description) VALUES ($1, $2, $3);`,
		e.Name, e.Rollout, e.Description)
	return err
}

// UpdateExperiment updates the specified experiment with the provided rollout value.
func (db *DB) UpdateExperiment(ctx context.Context, e *internal.Experiment) (err error) {
	defer derrors.Wrap(&err, "DB.UpdateExperiment(ctx, %v)", e)
	if e.Name == "" || e.Description == "" {
		return fmt.Errorf("neither name nor description can be empty: %w", derrors.InvalidArgument)
	}

	query := `UPDATE experiments
		SET rollout = $2, description = $3
		WHERE name = $1;`
	n, err := db.db.Exec(ctx, query, e.Name, e.Rollout, e.Description)
	if err != nil {
		return err
	}
	if n == 0 {
		return derrors.NotFound
	}
	return nil
}

// RemoveExperiment removes the specified experiment.
func (db *DB) RemoveExperiment(ctx context.Context, name string) (err error) {
	defer derrors.Wrap(&err, "DB.RemoveExperiment(ctx, %q)", name)
	n, err := db.db.Exec(ctx, `DELETE FROM experiments WHERE name = $1`, name)
	if err != nil {
		return err
	}
	if n == 0 {
		return derrors.NotFound
	}
	return nil
}
<commit_msg>internal/postgres: add blank line before copyright notice<commit_after>// Copyright 2019 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 postgres

import (
	"context"
	"database/sql"
	"fmt"

	"golang.org/x/pkgsite/internal"
	"golang.org/x/pkgsite/internal/derrors"
)

// GetExperiments fetches all experiments in the database.
func (db *DB) GetExperiments(ctx context.Context) (_ []*internal.Experiment, err error) {
	defer derrors.Wrap(&err, "DB.GetExperiments(ctx)")

	query := "SELECT name, rollout, description FROM experiments;"
	var experiments []*internal.Experiment
	err = db.db.RunQuery(ctx, query, func(rows *sql.Rows) error {
		var e internal.Experiment
		if err := rows.Scan(&e.Name, &e.Rollout, &e.Description); err != nil {
			return err
		}
		experiments = append(experiments, &e)
		return nil
	})
	return experiments, nil
}

// InsertExperiment inserts a row into the experiments table.
func (db *DB) InsertExperiment(ctx context.Context, e *internal.Experiment) (err error) {
	defer derrors.Wrap(&err, "DB.insertExperiment(ctx, %v)", e)
	if e.Name == "" || e.Description == "" {
		return fmt.Errorf("neither name nor description can be empty: %w", derrors.InvalidArgument)
	}

	_, err = db.db.Exec(ctx,
		`INSERT INTO experiments
		(name, rollout, description) VALUES ($1, $2, $3);`,
		e.Name, e.Rollout, e.Description)
	return err
}

// UpdateExperiment updates the specified experiment with the provided rollout value.
func (db *DB) UpdateExperiment(ctx context.Context, e *internal.Experiment) (err error) {
	defer derrors.Wrap(&err, "DB.UpdateExperiment(ctx, %v)", e)
	if e.Name == "" || e.Description == "" {
		return fmt.Errorf("neither name nor description can be empty: %w", derrors.InvalidArgument)
	}

	query := `UPDATE experiments
		SET rollout = $2, description = $3
		WHERE name = $1;`
	n, err := db.db.Exec(ctx, query, e.Name, e.Rollout, e.Description)
	if err != nil {
		return err
	}
	if n == 0 {
		return derrors.NotFound
	}
	return nil
}

// RemoveExperiment removes the specified experiment.
func (db *DB) RemoveExperiment(ctx context.Context, name string) (err error) {
	defer derrors.Wrap(&err, "DB.RemoveExperiment(ctx, %q)", name)
	n, err := db.db.Exec(ctx, `DELETE FROM experiments WHERE name = $1`, name)
	if err != nil {
		return err
	}
	if n == 0 {
		return derrors.NotFound
	}
	return nil
}
<|endoftext|>