<commit_before>package mal

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"strings"
	"testing"
)

var (
	// client is the MyAnimeList client being tested.
	client *Client

	// server is a test HTTP server used to provide mock API responses.
	server *httptest.Server

	// mux is the HTTP request multiplexer that the test HTTP server will use
	// to mock API responses.
	mux *http.ServeMux
)

// setup sets up a test HTTP server along with a mal.Client that is
// configured to talk to that test server. Tests should register handlers on
// mux which provide mock responses for the API method being tested.
func setup() {
	// test server
	mux = http.NewServeMux()
	server = httptest.NewServer(mux)

	// mal client configured to use test server
	client = NewClient(nil)
	client.BaseURL, _ = url.Parse(server.URL)
}

// teardown closes the test HTTP server.
func teardown() {
	server.Close()
}

type urlValues map[string]string

func testURLValues(t *testing.T, r *http.Request, values urlValues) {
	want := url.Values{}
	for k, v := range values {
		want.Add(k, v)
	}
	actual := r.URL.Query()
	if !reflect.DeepEqual(want, actual) {
		t.Errorf("URL Values = %v, want %v", actual, want)
	}
}

func testBasicAuth(t *testing.T, r *http.Request, usedWant bool, unameWant, passWant string) {
	uname, pass, used := r.BasicAuth()
	if used != usedWant {
		t.Errorf("BasicAuth used = %v, want %v", used, usedWant)
	}
	if uname != unameWant {
		t.Errorf("BasicAuth username = %v, want %v", uname, unameWant)
	}
	if pass != passWant {
		t.Errorf("BasicAuth password = %v, want %v", pass, passWant)
	}
}

func testUserAgent(t *testing.T, r *http.Request, want string) {
	agent := r.Header.Get("User-Agent")
	if want != agent {
		t.Errorf("User-Agent = %v, want %v", agent, want)
	}
}

func testMethod(t *testing.T, r *http.Request, want string) {
	if want != r.Method {
		t.Errorf("Request method = %v, want %v", r.Method, want)
	}
}

func testBody(t *testing.T, r *http.Request, want string) {
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		t.Errorf("Unable to read body")
	}
	body := string(b)
	if body != want {
		t.Errorf("body = %v, want %v", body, want)
	}
}

func testContentType(t *testing.T, r *http.Request, want string) {
	ct := r.Header.Get("Content-Type")
	if ct != want {
		t.Errorf("Content-Type = %v, want %v", ct, want)
	}
}

func testID(t *testing.T, r *http.Request, want string) {
	idXML := r.URL.Path[strings.LastIndex(r.URL.Path, "/")+1:]
	xml := idXML[len(idXML)-4:]
	if xml != ".xml" {
		t.Errorf("URL path %v does not end in .xml", r.URL.Path)
	}
	id := idXML[:len(idXML)-4]
	if id != want {
		t.Errorf("provided id = %v, want %v", id, want)
	}
}

func testFormValue(t *testing.T, r *http.Request, value, want string) {
	v := r.FormValue(value)
	if v != want {
		t.Errorf("form value %v = %v, want %v", value, v, want)
	}
}

func TestNewClient(t *testing.T) {
	c := NewClient(nil)

	// test default base URL
	if got, want := c.BaseURL.String(), defaultBaseURL; got != want {
		t.Errorf("NewClient.BaseURL = %v, want %v", got, want)
	}

	// test default user agent
	if got, want := c.UserAgent, defaultUserAgent; got != want {
		t.Errorf("NewClient.UserAgent = %v, want %v", got, want)
	}

	// test account default endpoint
	if got, want := c.Account.Endpoint.String(), defaultAccountEndpoint; got != want {
		t.Errorf("NewClient.Account.Endpoint = %v, want %v", got, want)
	}

	// test anime default endpoints
	if got, want := c.Anime.AddEndpoint.String(), defaultAnimeAddEndpoint; got != want {
		t.Errorf("NewClient.Anime.Addpoint = %v, want %v", got, want)
	}
	if got, want := c.Anime.UpdateEndpoint.String(), defaultAnimeUpdateEndpoint; got != want {
		t.Errorf("NewClient.Anime.UpdateEndpoint = %v, want %v", got, want)
	}
	if got, want := c.Anime.DeleteEndpoint.String(), defaultAnimeDeleteEndpoint; got != want {
		t.Errorf("NewClient.Anime.DeleteEndpoint = %v, want %v", got, want)
	}
	if got, want := c.Anime.SearchEndpoint.String(), defaultAnimeSearchEndpoint; got != want {
		t.Errorf("NewClient.Anime.SearchEndpoint = %v, want %v", got, want)
	}
	if got, want := c.Anime.ListEndpoint.String(), defaultListEndpoint; got != want {
		t.Errorf("NewClient.Anime.ListEndpoint = %v, want %v", got, want)
	}

	// test manga default endpoints
	if got, want := c.Manga.AddEndpoint.String(), defaultMangaAddEndpoint; got != want {
		t.Errorf("NewClient.Manga.Addpoint = %v, want %v", got, want)
	}
	if got, want := c.Manga.UpdateEndpoint.String(), defaultMangaUpdateEndpoint; got != want {
		t.Errorf("NewClient.Manga.UpdateEndpoint = %v, want %v", got, want)
	}
	if got, want := c.Manga.DeleteEndpoint.String(), defaultMangaDeleteEndpoint; got != want {
		t.Errorf("NewClient.Manga.DeleteEndpoint = %v, want %v", got, want)
	}
	if got, want := c.Manga.SearchEndpoint.String(), defaultMangaSearchEndpoint; got != want {
		t.Errorf("NewClient.Manga.SearchEndpoint = %v, want %v", got, want)
	}
	if got, want := c.Manga.ListEndpoint.String(), defaultListEndpoint; got != want {
		t.Errorf("NewClient.Manga.ListEndpoint = %v, want %v", got, want)
	}
}

func TestClient_NewRequest(t *testing.T) {
	c := NewClient(nil)

	inURL, outURL := "/foo", defaultBaseURL+"foo"

	inData := &User{ID: 5, Username: "TestUser"}
	v := url.Values{}
	v.Set("data", "<user><id>5</id><username>TestUser</username></user>")
	urlEncData, _ := url.Parse(v.Encode())
	outData := urlEncData.Path

	req, _ := c.NewRequest("GET", inURL, inData)

	// test that the endpoint URL was correctly added to the base URL
	if got, want := req.URL.String(), outURL; got != want {
		t.Errorf("NewRequest(%q) URL =  %v, want %v", inURL, got, want)
	}

	// test that body was encoded to XML and then URL enconded as data=...
	body, _ := ioutil.ReadAll(req.Body)
	urlEncBody, _ := url.Parse(string(body)) // url.Path holds the URL decoded string
	if got, want := urlEncBody.Path, outData; got != want {
		t.Errorf("NewRequest(%+v) Body = '%v', want '%v'", inData, got, want)
	}

	testBasicAuth(t, req, false, "", "")
	testUserAgent(t, req, defaultUserAgent)
}

func TestClient_NewRequest_HTTPS(t *testing.T) {
	c := NewClient(nil)

	req, err := c.NewRequest("GET", "/foo", nil)
	if err != nil {
		t.Error("NewRequest returned err:", err)
	}
	if got, want := req.URL.Scheme, "https"; got != want {
		t.Errorf("NewRequest scheme = %q, want %q", got, want)
	}
}

func TestClient_NewRequest_invalidMethod(t *testing.T) {
	c := NewClient(nil)

	_, err := c.NewRequest("invalid method", "/foo", nil)
	if err == nil {
		t.Error("NewRequest with invalid method expected to return err")
	}
}

func TestClient_Do(t *testing.T) {
	setup()
	defer teardown()

	type foo struct {
		Bar string `xml:"bar"`
	}

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if want := "GET"; r.Method != want {
			t.Errorf("request method = %v, want %v", r.Method, want)
		}
		fmt.Fprint(w, `<foo><bar>• foobar</bar></foo>`)
	})

	req, _ := client.NewRequest("GET", "/", nil)

	body := new(foo)
	response, err := client.Do(req, body)

	want := &foo{"• foobar"}
	if !reflect.DeepEqual(body, want) {
		t.Errorf("Do() response body = %v, want %v", body, want)
	}

	if err != nil {
		t.Fatalf("Do() returned err = %v", err)
	}

	if want, got := "<foo><bar>• foobar</bar></foo>", string(response.Body); want != got {
		t.Errorf("Do() mal.Response.Body = %v, want %v", got, want)
	}
}

func TestClient_Do_invalidXMLEntity(t *testing.T) {
	setup()
	defer teardown()

	type foo struct {
		Bar string `xml:"bar"`
	}

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if want := "GET"; r.Method != want {
			t.Errorf("request method = %v, want %v", r.Method, want)
		}
		fmt.Fprint(w, `<foo><bar>&foo; bar</bar></foo>`)
	})

	req, _ := client.NewRequest("GET", "/", nil)

	body := new(foo)
	response, err := client.Do(req, body)

	if err == nil {
		t.Errorf("Do() receiving XML with invalid entity should return err.")
	}

	if response == nil {
		t.Fatal("Do() receiving XML with invalid entity should also return response.")
	}

	if want, got := "<foo><bar>&foo; bar</bar></foo>", string(response.Body); want != got {
		t.Errorf("Do() receiving XML with invalid entity mal.Response.Body = %v, want %v", got, want)
	}
}

func TestClient_Do_notFound(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "not found", http.StatusNotFound)
	})

	req, _ := client.NewRequest("GET", "/", nil)

	response, err := client.Do(req, nil)

	if err == nil {
		t.Error("Expected HTTP 404 error.")
	}

	if response == nil {
		t.Fatal("Expected response with HTTP 404 error.")
	}

	// http.Error seems to be adding a new line to the message.
	if want, got := "not found\n", string(response.Body); want != got {
		t.Errorf("Do() mal.Response.Body = %v, want %v", got, want)
	}
}

func TestClient_Do_connectionRefused(t *testing.T) {
	req, _ := client.NewRequest("GET", "/", nil)
	_, err := client.Do(req, nil)
	if err == nil {
		t.Error("Expected connection refused error.")
	}
}

func TestClient_post_invalidID(t *testing.T) {
	setup()
	defer teardown()

	client.SetCredentials("TestUser", "TestPass")
	client.SetUserAgent("TestAgent")

	mux.HandleFunc("/api/animelist/update/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		testID(t, r, "0")
		testBasicAuth(t, r, true, "TestUser", "TestPass")
		testUserAgent(t, r, "TestAgent")
		testContentType(t, r, "application/x-www-form-urlencoded")
		// zeroEntry defined in anime_test.go
		testFormValue(t, r, "data", fmt.Sprintf(zeroEntry, "onhold"))
		http.Error(w, "Invalid ID", http.StatusNotImplemented)
	})

	response, err := client.post("api/animelist/update/", 0, AnimeEntry{Status: "onhold"})

	if err == nil {
		t.Errorf("Anime.Update invalid ID should return err")
	}

	if response == nil {
		t.Errorf("Anime.Update invalid ID should return also return response")
	}
}

func TestClient_delete_invalidID(t *testing.T) {
	setup()
	defer teardown()

	client.SetCredentials("TestUser", "TestPass")
	client.SetUserAgent("TestAgent")

	mux.HandleFunc("/api/animelist/delete/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
		testID(t, r, "0")
		testBasicAuth(t, r, true, "TestUser", "TestPass")
		testUserAgent(t, r, "TestAgent")
		http.Error(w, "Invalid ID", http.StatusNotImplemented)
	})

	response, err := client.delete("api/animelist/delete/", 0)

	if err == nil {
		t.Errorf("Anime.Delete invalid ID should return err")
	}

	if response == nil {
		t.Errorf("Anime.Delete invalid ID should return also return response")
	}
}

func TestClient_NewRequest_badEndpoint(t *testing.T) {
	c := NewClient(nil)
	inURL := "%foo"
	_, err := c.NewRequest("GET", inURL, nil)
	if err == nil {
		t.Errorf("NewRequest(%q) should return parse err", inURL)
	}
}

func TestClient_NewRequest_xmlEncodeError(t *testing.T) {
	c := NewClient(nil)
	in := func() {} // xml.Marshal cannot encode a func
	_, err := c.NewRequest("GET", "/foo", in)
	if err == nil {
		t.Errorf("NewRequest receiving a function as body should return XML encode err")
	}
}
<commit_msg>Change invalid method test to run on >= go1.7<commit_after>package mal

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"runtime"
	"strings"
	"testing"
)

var (
	// client is the MyAnimeList client being tested.
	client *Client

	// server is a test HTTP server used to provide mock API responses.
	server *httptest.Server

	// mux is the HTTP request multiplexer that the test HTTP server will use
	// to mock API responses.
	mux *http.ServeMux
)

// setup sets up a test HTTP server along with a mal.Client that is
// configured to talk to that test server. Tests should register handlers on
// mux which provide mock responses for the API method being tested.
func setup() {
	// test server
	mux = http.NewServeMux()
	server = httptest.NewServer(mux)

	// mal client configured to use test server
	client = NewClient(nil)
	client.BaseURL, _ = url.Parse(server.URL)
}

// teardown closes the test HTTP server.
func teardown() {
	server.Close()
}

type urlValues map[string]string

func testURLValues(t *testing.T, r *http.Request, values urlValues) {
	want := url.Values{}
	for k, v := range values {
		want.Add(k, v)
	}
	actual := r.URL.Query()
	if !reflect.DeepEqual(want, actual) {
		t.Errorf("URL Values = %v, want %v", actual, want)
	}
}

func testBasicAuth(t *testing.T, r *http.Request, usedWant bool, unameWant, passWant string) {
	uname, pass, used := r.BasicAuth()
	if used != usedWant {
		t.Errorf("BasicAuth used = %v, want %v", used, usedWant)
	}
	if uname != unameWant {
		t.Errorf("BasicAuth username = %v, want %v", uname, unameWant)
	}
	if pass != passWant {
		t.Errorf("BasicAuth password = %v, want %v", pass, passWant)
	}
}

func testUserAgent(t *testing.T, r *http.Request, want string) {
	agent := r.Header.Get("User-Agent")
	if want != agent {
		t.Errorf("User-Agent = %v, want %v", agent, want)
	}
}

func testMethod(t *testing.T, r *http.Request, want string) {
	if want != r.Method {
		t.Errorf("Request method = %v, want %v", r.Method, want)
	}
}

func testBody(t *testing.T, r *http.Request, want string) {
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		t.Errorf("Unable to read body")
	}
	body := string(b)
	if body != want {
		t.Errorf("body = %v, want %v", body, want)
	}
}

func testContentType(t *testing.T, r *http.Request, want string) {
	ct := r.Header.Get("Content-Type")
	if ct != want {
		t.Errorf("Content-Type = %v, want %v", ct, want)
	}
}

func testID(t *testing.T, r *http.Request, want string) {
	idXML := r.URL.Path[strings.LastIndex(r.URL.Path, "/")+1:]
	xml := idXML[len(idXML)-4:]
	if xml != ".xml" {
		t.Errorf("URL path %v does not end in .xml", r.URL.Path)
	}
	id := idXML[:len(idXML)-4]
	if id != want {
		t.Errorf("provided id = %v, want %v", id, want)
	}
}

func testFormValue(t *testing.T, r *http.Request, value, want string) {
	v := r.FormValue(value)
	if v != want {
		t.Errorf("form value %v = %v, want %v", value, v, want)
	}
}

func TestNewClient(t *testing.T) {
	c := NewClient(nil)

	// test default base URL
	if got, want := c.BaseURL.String(), defaultBaseURL; got != want {
		t.Errorf("NewClient.BaseURL = %v, want %v", got, want)
	}

	// test default user agent
	if got, want := c.UserAgent, defaultUserAgent; got != want {
		t.Errorf("NewClient.UserAgent = %v, want %v", got, want)
	}

	// test account default endpoint
	if got, want := c.Account.Endpoint.String(), defaultAccountEndpoint; got != want {
		t.Errorf("NewClient.Account.Endpoint = %v, want %v", got, want)
	}

	// test anime default endpoints
	if got, want := c.Anime.AddEndpoint.String(), defaultAnimeAddEndpoint; got != want {
		t.Errorf("NewClient.Anime.Addpoint = %v, want %v", got, want)
	}
	if got, want := c.Anime.UpdateEndpoint.String(), defaultAnimeUpdateEndpoint; got != want {
		t.Errorf("NewClient.Anime.UpdateEndpoint = %v, want %v", got, want)
	}
	if got, want := c.Anime.DeleteEndpoint.String(), defaultAnimeDeleteEndpoint; got != want {
		t.Errorf("NewClient.Anime.DeleteEndpoint = %v, want %v", got, want)
	}
	if got, want := c.Anime.SearchEndpoint.String(), defaultAnimeSearchEndpoint; got != want {
		t.Errorf("NewClient.Anime.SearchEndpoint = %v, want %v", got, want)
	}
	if got, want := c.Anime.ListEndpoint.String(), defaultListEndpoint; got != want {
		t.Errorf("NewClient.Anime.ListEndpoint = %v, want %v", got, want)
	}

	// test manga default endpoints
	if got, want := c.Manga.AddEndpoint.String(), defaultMangaAddEndpoint; got != want {
		t.Errorf("NewClient.Manga.Addpoint = %v, want %v", got, want)
	}
	if got, want := c.Manga.UpdateEndpoint.String(), defaultMangaUpdateEndpoint; got != want {
		t.Errorf("NewClient.Manga.UpdateEndpoint = %v, want %v", got, want)
	}
	if got, want := c.Manga.DeleteEndpoint.String(), defaultMangaDeleteEndpoint; got != want {
		t.Errorf("NewClient.Manga.DeleteEndpoint = %v, want %v", got, want)
	}
	if got, want := c.Manga.SearchEndpoint.String(), defaultMangaSearchEndpoint; got != want {
		t.Errorf("NewClient.Manga.SearchEndpoint = %v, want %v", got, want)
	}
	if got, want := c.Manga.ListEndpoint.String(), defaultListEndpoint; got != want {
		t.Errorf("NewClient.Manga.ListEndpoint = %v, want %v", got, want)
	}
}

func TestClient_NewRequest(t *testing.T) {
	c := NewClient(nil)

	inURL, outURL := "/foo", defaultBaseURL+"foo"

	inData := &User{ID: 5, Username: "TestUser"}
	v := url.Values{}
	v.Set("data", "<user><id>5</id><username>TestUser</username></user>")
	urlEncData, _ := url.Parse(v.Encode())
	outData := urlEncData.Path

	req, _ := c.NewRequest("GET", inURL, inData)

	// test that the endpoint URL was correctly added to the base URL
	if got, want := req.URL.String(), outURL; got != want {
		t.Errorf("NewRequest(%q) URL =  %v, want %v", inURL, got, want)
	}

	// test that body was encoded to XML and then URL enconded as data=...
	body, _ := ioutil.ReadAll(req.Body)
	urlEncBody, _ := url.Parse(string(body)) // url.Path holds the URL decoded string
	if got, want := urlEncBody.Path, outData; got != want {
		t.Errorf("NewRequest(%+v) Body = '%v', want '%v'", inData, got, want)
	}

	testBasicAuth(t, req, false, "", "")
	testUserAgent(t, req, defaultUserAgent)
}

func TestClient_NewRequest_HTTPS(t *testing.T) {
	c := NewClient(nil)

	req, err := c.NewRequest("GET", "/foo", nil)
	if err != nil {
		t.Error("NewRequest returned err:", err)
	}
	if got, want := req.URL.Scheme, "https"; got != want {
		t.Errorf("NewRequest scheme = %q, want %q", got, want)
	}
}

func TestClient_NewRequest_invalidMethod(t *testing.T) {
	s := strings.Split(runtime.Version(), ".")
	// This test requires Go version 1.7 or higher.
	if len(s) >= 2 && s[0] == "go1" && s[1] == "7" {
		c := NewClient(nil)
		_, err := c.NewRequest("invalid method", "/foo", nil)
		if err == nil {
			t.Error("NewRequest with invalid method expected to return err")
		}
	}
}

func TestClient_Do(t *testing.T) {
	setup()
	defer teardown()

	type foo struct {
		Bar string `xml:"bar"`
	}

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if want := "GET"; r.Method != want {
			t.Errorf("request method = %v, want %v", r.Method, want)
		}
		fmt.Fprint(w, `<foo><bar>• foobar</bar></foo>`)
	})

	req, _ := client.NewRequest("GET", "/", nil)

	body := new(foo)
	response, err := client.Do(req, body)

	want := &foo{"• foobar"}
	if !reflect.DeepEqual(body, want) {
		t.Errorf("Do() response body = %v, want %v", body, want)
	}

	if err != nil {
		t.Fatalf("Do() returned err = %v", err)
	}

	if want, got := "<foo><bar>• foobar</bar></foo>", string(response.Body); want != got {
		t.Errorf("Do() mal.Response.Body = %v, want %v", got, want)
	}
}

func TestClient_Do_invalidXMLEntity(t *testing.T) {
	setup()
	defer teardown()

	type foo struct {
		Bar string `xml:"bar"`
	}

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if want := "GET"; r.Method != want {
			t.Errorf("request method = %v, want %v", r.Method, want)
		}
		fmt.Fprint(w, `<foo><bar>&foo; bar</bar></foo>`)
	})

	req, _ := client.NewRequest("GET", "/", nil)

	body := new(foo)
	response, err := client.Do(req, body)

	if err == nil {
		t.Errorf("Do() receiving XML with invalid entity should return err.")
	}

	if response == nil {
		t.Fatal("Do() receiving XML with invalid entity should also return response.")
	}

	if want, got := "<foo><bar>&foo; bar</bar></foo>", string(response.Body); want != got {
		t.Errorf("Do() receiving XML with invalid entity mal.Response.Body = %v, want %v", got, want)
	}
}

func TestClient_Do_notFound(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "not found", http.StatusNotFound)
	})

	req, _ := client.NewRequest("GET", "/", nil)

	response, err := client.Do(req, nil)

	if err == nil {
		t.Error("Expected HTTP 404 error.")
	}

	if response == nil {
		t.Fatal("Expected response with HTTP 404 error.")
	}

	// http.Error seems to be adding a new line to the message.
	if want, got := "not found\n", string(response.Body); want != got {
		t.Errorf("Do() mal.Response.Body = %v, want %v", got, want)
	}
}

func TestClient_Do_connectionRefused(t *testing.T) {
	req, _ := client.NewRequest("GET", "/", nil)
	_, err := client.Do(req, nil)
	if err == nil {
		t.Error("Expected connection refused error.")
	}
}

func TestClient_post_invalidID(t *testing.T) {
	setup()
	defer teardown()

	client.SetCredentials("TestUser", "TestPass")
	client.SetUserAgent("TestAgent")

	mux.HandleFunc("/api/animelist/update/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		testID(t, r, "0")
		testBasicAuth(t, r, true, "TestUser", "TestPass")
		testUserAgent(t, r, "TestAgent")
		testContentType(t, r, "application/x-www-form-urlencoded")
		// zeroEntry defined in anime_test.go
		testFormValue(t, r, "data", fmt.Sprintf(zeroEntry, "onhold"))
		http.Error(w, "Invalid ID", http.StatusNotImplemented)
	})

	response, err := client.post("api/animelist/update/", 0, AnimeEntry{Status: "onhold"})

	if err == nil {
		t.Errorf("Anime.Update invalid ID should return err")
	}

	if response == nil {
		t.Errorf("Anime.Update invalid ID should return also return response")
	}
}

func TestClient_delete_invalidID(t *testing.T) {
	setup()
	defer teardown()

	client.SetCredentials("TestUser", "TestPass")
	client.SetUserAgent("TestAgent")

	mux.HandleFunc("/api/animelist/delete/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
		testID(t, r, "0")
		testBasicAuth(t, r, true, "TestUser", "TestPass")
		testUserAgent(t, r, "TestAgent")
		http.Error(w, "Invalid ID", http.StatusNotImplemented)
	})

	response, err := client.delete("api/animelist/delete/", 0)

	if err == nil {
		t.Errorf("Anime.Delete invalid ID should return err")
	}

	if response == nil {
		t.Errorf("Anime.Delete invalid ID should return also return response")
	}
}

func TestClient_NewRequest_badEndpoint(t *testing.T) {
	c := NewClient(nil)
	inURL := "%foo"
	_, err := c.NewRequest("GET", inURL, nil)
	if err == nil {
		t.Errorf("NewRequest(%q) should return parse err", inURL)
	}
}

func TestClient_NewRequest_xmlEncodeError(t *testing.T) {
	c := NewClient(nil)
	in := func() {} // xml.Marshal cannot encode a func
	_, err := c.NewRequest("GET", "/foo", in)
	if err == nil {
		t.Errorf("NewRequest receiving a function as body should return XML encode err")
	}
}
<|endoftext|>


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

package main

import (
	log "github.com/couchbaselabs/clog"
)

// JSON/struct definitions of what the Manager stores in the Cfg.
// NOTE: You *must* update VERSION if you change these
// definitions or the planning algorithms change.
const VERSION = "0.0.0"
const VERSION_KEY = "version"

type Plan struct {
}

type LogicalIndex struct {
}

type LogicalIndexes []*LogicalIndex

type Indexer struct {
}

type Indexers []*Indexer

// Returns ok if our version is ok to write to the Cfg.
func (mgr *Manager) CheckVersion() bool {
	for mgr.cfg != nil {
		version, cas, err := mgr.cfg.Get(VERSION_KEY, 0)
		if version == nil || version == "" || err != nil {
			version = VERSION
		}
		if !VersionGTE(VERSION, version.(string)) {
			return false
		}
		if VERSION == version {
			return true
		}
		// We have a higher VERSION than the version just read from
		// cfg, so save our VERSION and retry.
		cas, err = mgr.cfg.Set(VERSION_KEY, VERSION, cas)
		if err != nil {
			log.Printf("error: could not save VERSION to cfg, err: %v", err)
			return false
		}
	}

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

package main

import (
	log "github.com/couchbaselabs/clog"
)

// JSON/struct definitions of what the Manager stores in the Cfg.
// NOTE: You *must* update VERSION if you change these
// definitions or the planning algorithms change.
const VERSION = "0.0.0"
const VERSION_KEY = "version"

type Plan struct {
}

type LogicalIndex struct {
}

type LogicalIndexes []*LogicalIndex

type Indexer struct {
}

type Indexers []*Indexer

// Returns ok if our version is ok to write to the Cfg.
func (mgr *Manager) CheckVersion() bool {
	for mgr.cfg != nil {
		version, cas, err := mgr.cfg.Get(VERSION_KEY, 0)
		if version == nil || version == "" || err != nil {
			version = VERSION
		}
		if !VersionGTE(VERSION, version.(string)) {
			return false
		}
		if VERSION == version {
			return true
		}
		// We have a higher VERSION than the version just read from
		// cfg, so save our VERSION and retry.
		_, err = mgr.cfg.Set(VERSION_KEY, VERSION, cas)
		if err != nil {
			log.Printf("error: could not save VERSION to cfg, err: %v", err)
			return false
		}
	}

	return false
}
<|endoftext|>


<commit_before>package vsolver

import (
	"fmt"
	"go/build"
	"os"
	"path"
	"runtime"
	"testing"

	"github.com/Masterminds/semver"
)

var cpath = path.Join(os.TempDir(), "smcache")
var bd string

type dummyAnalyzer struct{}

func (dummyAnalyzer) GetInfo(ctx build.Context, p ProjectName) (Manifest, Lock, error) {
	return nil, nil, fmt.Errorf("just a dummy analyzer")
}

func sv(s string) *semver.Version {
	sv, err := semver.NewVersion(s)
	if err != nil {
		panic(fmt.Sprintf("Error creating semver from %q: %s", s, err))
	}

	return sv
}

func init() {
	_, filename, _, _ := runtime.Caller(1)
	bd = path.Dir(filename)
}

func TestSourceManagerInit(t *testing.T) {
	// Just to ensure it's all clean
	os.RemoveAll(cpath)

	_, err := NewSourceManager(cpath, bd, true, false, dummyAnalyzer{})

	if err != nil {
		t.Errorf("Unexpected error on SourceManager creation: %s", err)
	}

	_, err = NewSourceManager(cpath, bd, true, false, dummyAnalyzer{})
	if err == nil {
		t.Errorf("Creating second SourceManager should have failed due to file lock contention")
	}

	sm, err := NewSourceManager(cpath, bd, true, true, dummyAnalyzer{})
	defer sm.Release()
	if err != nil {
		t.Errorf("Creating second SourceManager should have succeeded when force flag was passed, but failed with err %s", err)
	}

	if _, err = os.Stat(path.Join(cpath, "sm.lock")); err != nil {
		t.Errorf("Global cache lock file not created correctly")
	}
}

func TestProjectManagerInit(t *testing.T) {
	// Just to ensure it's all clean
	os.RemoveAll(cpath)
	sm, err := NewSourceManager(cpath, bd, true, false, dummyAnalyzer{})
	defer sm.Release()

	if err != nil {
		t.Errorf("Unexpected error on SourceManager creation: %s", err)
		t.FailNow()
	}

	pn := ProjectName("github.com/Masterminds/VCSTestRepo")
	v, err := sm.ListVersions(pn)
	if err != nil {
		t.Errorf("Unexpected error during initial project setup/fetching %s", err)
	}

	if len(v) != 3 {
		t.Errorf("Expected three version results from the test repo, got %v", len(v))
	} else {
		rev := Revision("30605f6ac35fcb075ad0bfa9296f90a7d891523e")
		expected := []Version{
			NewVersion("1.0.0").Is(rev),
			NewBranch("master").Is(rev),
			NewBranch("test").Is(rev),
		}

		for k, e := range expected {
			if v[k] != e {
				t.Errorf("Expected version %s in position %v but got %s", e, k, v[k])
			}
		}
	}

	// Ensure that the appropriate cache dirs and files exist
	_, err = os.Stat(path.Join(cpath, "src", "github.com", "Masterminds", "VCSTestRepo", ".git"))
	if err != nil {
		t.Error("Cache repo does not exist in expected location")
	}

	_, err = os.Stat(path.Join(cpath, "metadata", "github.com", "Masterminds", "VCSTestRepo", "cache.json"))
	if err != nil {
		// TODO temporarily disabled until we turn caching back on
		//t.Error("Metadata cache json file does not exist in expected location")
	}

	// Ensure project existence values are what we expect
	var exists bool
	exists, err = sm.RepoExists(pn)
	if err != nil {
		t.Errorf("Error on checking RepoExists: %s", err)
	}
	if !exists {
		t.Error("Repo should exist after non-erroring call to ListVersions")
	}

	exists, err = sm.VendorCodeExists(pn)
	if err != nil {
		t.Errorf("Error on checking VendorCodeExists: %s", err)
	}
	if exists {
		t.Error("Shouldn't be any vendor code after just calling ListVersions")
	}

	// Now reach inside the black box
	pms, err := sm.(*sourceManager).getProjectManager(pn)
	if err != nil {
		t.Errorf("Error on grabbing project manager obj: %s", err)
	}

	// Check upstream existence flag
	if !pms.pm.CheckExistence(ExistsUpstream) {
		t.Errorf("ExistsUpstream flag not being correctly set the project")
	}
}

func TestRepoVersionFetching(t *testing.T) {
	os.RemoveAll(cpath)
	smi, err := NewSourceManager(cpath, bd, true, false, dummyAnalyzer{})
	if err != nil {
		t.Errorf("Unexpected error on SourceManager creation: %s", err)
		t.FailNow()
	}

	sm := smi.(*sourceManager)
	upstreams := []ProjectName{
		"github.com/Masterminds/VCSTestRepo",
		"bitbucket.org/mattfarina/testhgrepo",
		"launchpad.net/govcstestbzrrepo",
	}

	pms := make([]*projectManager, len(upstreams))
	for k, u := range upstreams {
		pmi, err := sm.getProjectManager(u)
		if err != nil {
			sm.Release()
			t.Errorf("Unexpected error on ProjectManager creation: %s", err)
			t.FailNow()
		}
		pms[k] = pmi.pm.(*projectManager)
	}

	defer sm.Release()

	// test git first
	vlist, exbits, err := pms[0].crepo.getCurrentVersionPairs()
	if err != nil {
		t.Errorf("Unexpected error getting version pairs from git repo: %s", err)
	}
	if exbits != ExistsUpstream {
		t.Errorf("git pair fetch should only set upstream existence bits, but got %v", exbits)
	}
	if len(vlist) != 3 {
		t.Errorf("git test repo should've produced three versions, got %v", len(vlist))
	} else {
		v := NewBranch("master").Is(Revision("30605f6ac35fcb075ad0bfa9296f90a7d891523e"))
		if vlist[0] != v {
			t.Errorf("git pair fetch reported incorrect first version, got %s", vlist[0])
		}

		v = NewBranch("test").Is(Revision("30605f6ac35fcb075ad0bfa9296f90a7d891523e"))
		if vlist[1] != v {
			t.Errorf("git pair fetch reported incorrect second version, got %s", vlist[1])
		}

		v = NewVersion("1.0.0").Is(Revision("30605f6ac35fcb075ad0bfa9296f90a7d891523e"))
		if vlist[2] != v {
			t.Errorf("git pair fetch reported incorrect third version, got %s", vlist[2])
		}
	}

	// now hg
	vlist, exbits, err = pms[1].crepo.getCurrentVersionPairs()
	if err != nil {
		t.Errorf("Unexpected error getting version pairs from hg repo: %s", err)
	}
	if exbits != ExistsUpstream|ExistsInCache {
		t.Errorf("hg pair fetch should set upstream and cache existence bits, but got %v", exbits)
	}
	if len(vlist) != 2 {
		t.Errorf("hg test repo should've produced two versions, got %v", len(vlist))
	} else {
		v := NewVersion("1.0.0").Is(Revision("d680e82228d206935ab2eaa88612587abe68db07"))
		if vlist[0] != v {
			t.Errorf("hg pair fetch reported incorrect first version, got %s", vlist[0])
		}

		v = NewBranch("test").Is(Revision("6c44ee3fe5d87763616c19bf7dbcadb24ff5a5ce"))
		if vlist[1] != v {
			t.Errorf("hg pair fetch reported incorrect second version, got %s", vlist[1])
		}
	}

	// bzr last
	vlist, exbits, err = pms[2].crepo.getCurrentVersionPairs()
	if err != nil {
		t.Errorf("Unexpected error getting version pairs from bzr repo: %s", err)
	}
	if exbits != ExistsUpstream|ExistsInCache {
		t.Errorf("bzr pair fetch should set upstream and cache existence bits, but got %v", exbits)
	}
	if len(vlist) != 1 {
		t.Errorf("bzr test repo should've produced one version, got %v", len(vlist))
	} else {
		v := NewVersion("1.0.0").Is(Revision("matt@mattfarina.com-20150731135137-pbphasfppmygpl68"))
		if vlist[0] != v {
			t.Errorf("bzr pair fetch reported incorrect first version, got %s", vlist[0])
		}
	}
	// no svn for now, because...svn
}
<commit_msg>Defer after we know we don't have nil pointer<commit_after>package vsolver

import (
	"fmt"
	"go/build"
	"os"
	"path"
	"runtime"
	"testing"

	"github.com/Masterminds/semver"
)

var cpath = path.Join(os.TempDir(), "smcache")
var bd string

type dummyAnalyzer struct{}

func (dummyAnalyzer) GetInfo(ctx build.Context, p ProjectName) (Manifest, Lock, error) {
	return nil, nil, fmt.Errorf("just a dummy analyzer")
}

func sv(s string) *semver.Version {
	sv, err := semver.NewVersion(s)
	if err != nil {
		panic(fmt.Sprintf("Error creating semver from %q: %s", s, err))
	}

	return sv
}

func init() {
	_, filename, _, _ := runtime.Caller(1)
	bd = path.Dir(filename)
}

func TestSourceManagerInit(t *testing.T) {
	// Just to ensure it's all clean
	os.RemoveAll(cpath)

	_, err := NewSourceManager(cpath, bd, true, false, dummyAnalyzer{})

	if err != nil {
		t.Errorf("Unexpected error on SourceManager creation: %s", err)
	}

	_, err = NewSourceManager(cpath, bd, true, false, dummyAnalyzer{})
	if err == nil {
		t.Errorf("Creating second SourceManager should have failed due to file lock contention")
	}

	sm, err := NewSourceManager(cpath, bd, true, true, dummyAnalyzer{})
	defer sm.Release()
	if err != nil {
		t.Errorf("Creating second SourceManager should have succeeded when force flag was passed, but failed with err %s", err)
	}

	if _, err = os.Stat(path.Join(cpath, "sm.lock")); err != nil {
		t.Errorf("Global cache lock file not created correctly")
	}
}

func TestProjectManagerInit(t *testing.T) {
	// Just to ensure it's all clean
	os.RemoveAll(cpath)
	sm, err := NewSourceManager(cpath, bd, true, false, dummyAnalyzer{})

	if err != nil {
		t.Errorf("Unexpected error on SourceManager creation: %s", err)
		t.FailNow()
	}
	defer sm.Release()

	pn := ProjectName("github.com/Masterminds/VCSTestRepo")
	v, err := sm.ListVersions(pn)
	if err != nil {
		t.Errorf("Unexpected error during initial project setup/fetching %s", err)
	}

	if len(v) != 3 {
		t.Errorf("Expected three version results from the test repo, got %v", len(v))
	} else {
		rev := Revision("30605f6ac35fcb075ad0bfa9296f90a7d891523e")
		expected := []Version{
			NewVersion("1.0.0").Is(rev),
			NewBranch("master").Is(rev),
			NewBranch("test").Is(rev),
		}

		for k, e := range expected {
			if v[k] != e {
				t.Errorf("Expected version %s in position %v but got %s", e, k, v[k])
			}
		}
	}

	// Ensure that the appropriate cache dirs and files exist
	_, err = os.Stat(path.Join(cpath, "src", "github.com", "Masterminds", "VCSTestRepo", ".git"))
	if err != nil {
		t.Error("Cache repo does not exist in expected location")
	}

	_, err = os.Stat(path.Join(cpath, "metadata", "github.com", "Masterminds", "VCSTestRepo", "cache.json"))
	if err != nil {
		// TODO temporarily disabled until we turn caching back on
		//t.Error("Metadata cache json file does not exist in expected location")
	}

	// Ensure project existence values are what we expect
	var exists bool
	exists, err = sm.RepoExists(pn)
	if err != nil {
		t.Errorf("Error on checking RepoExists: %s", err)
	}
	if !exists {
		t.Error("Repo should exist after non-erroring call to ListVersions")
	}

	exists, err = sm.VendorCodeExists(pn)
	if err != nil {
		t.Errorf("Error on checking VendorCodeExists: %s", err)
	}
	if exists {
		t.Error("Shouldn't be any vendor code after just calling ListVersions")
	}

	// Now reach inside the black box
	pms, err := sm.(*sourceManager).getProjectManager(pn)
	if err != nil {
		t.Errorf("Error on grabbing project manager obj: %s", err)
	}

	// Check upstream existence flag
	if !pms.pm.CheckExistence(ExistsUpstream) {
		t.Errorf("ExistsUpstream flag not being correctly set the project")
	}
}

func TestRepoVersionFetching(t *testing.T) {
	os.RemoveAll(cpath)
	smi, err := NewSourceManager(cpath, bd, true, false, dummyAnalyzer{})
	if err != nil {
		t.Errorf("Unexpected error on SourceManager creation: %s", err)
		t.FailNow()
	}

	sm := smi.(*sourceManager)
	upstreams := []ProjectName{
		"github.com/Masterminds/VCSTestRepo",
		"bitbucket.org/mattfarina/testhgrepo",
		"launchpad.net/govcstestbzrrepo",
	}

	pms := make([]*projectManager, len(upstreams))
	for k, u := range upstreams {
		pmi, err := sm.getProjectManager(u)
		if err != nil {
			sm.Release()
			t.Errorf("Unexpected error on ProjectManager creation: %s", err)
			t.FailNow()
		}
		pms[k] = pmi.pm.(*projectManager)
	}

	defer sm.Release()

	// test git first
	vlist, exbits, err := pms[0].crepo.getCurrentVersionPairs()
	if err != nil {
		t.Errorf("Unexpected error getting version pairs from git repo: %s", err)
	}
	if exbits != ExistsUpstream {
		t.Errorf("git pair fetch should only set upstream existence bits, but got %v", exbits)
	}
	if len(vlist) != 3 {
		t.Errorf("git test repo should've produced three versions, got %v", len(vlist))
	} else {
		v := NewBranch("master").Is(Revision("30605f6ac35fcb075ad0bfa9296f90a7d891523e"))
		if vlist[0] != v {
			t.Errorf("git pair fetch reported incorrect first version, got %s", vlist[0])
		}

		v = NewBranch("test").Is(Revision("30605f6ac35fcb075ad0bfa9296f90a7d891523e"))
		if vlist[1] != v {
			t.Errorf("git pair fetch reported incorrect second version, got %s", vlist[1])
		}

		v = NewVersion("1.0.0").Is(Revision("30605f6ac35fcb075ad0bfa9296f90a7d891523e"))
		if vlist[2] != v {
			t.Errorf("git pair fetch reported incorrect third version, got %s", vlist[2])
		}
	}

	// now hg
	vlist, exbits, err = pms[1].crepo.getCurrentVersionPairs()
	if err != nil {
		t.Errorf("Unexpected error getting version pairs from hg repo: %s", err)
	}
	if exbits != ExistsUpstream|ExistsInCache {
		t.Errorf("hg pair fetch should set upstream and cache existence bits, but got %v", exbits)
	}
	if len(vlist) != 2 {
		t.Errorf("hg test repo should've produced two versions, got %v", len(vlist))
	} else {
		v := NewVersion("1.0.0").Is(Revision("d680e82228d206935ab2eaa88612587abe68db07"))
		if vlist[0] != v {
			t.Errorf("hg pair fetch reported incorrect first version, got %s", vlist[0])
		}

		v = NewBranch("test").Is(Revision("6c44ee3fe5d87763616c19bf7dbcadb24ff5a5ce"))
		if vlist[1] != v {
			t.Errorf("hg pair fetch reported incorrect second version, got %s", vlist[1])
		}
	}

	// bzr last
	vlist, exbits, err = pms[2].crepo.getCurrentVersionPairs()
	if err != nil {
		t.Errorf("Unexpected error getting version pairs from bzr repo: %s", err)
	}
	if exbits != ExistsUpstream|ExistsInCache {
		t.Errorf("bzr pair fetch should set upstream and cache existence bits, but got %v", exbits)
	}
	if len(vlist) != 1 {
		t.Errorf("bzr test repo should've produced one version, got %v", len(vlist))
	} else {
		v := NewVersion("1.0.0").Is(Revision("matt@mattfarina.com-20150731135137-pbphasfppmygpl68"))
		if vlist[0] != v {
			t.Errorf("bzr pair fetch reported incorrect first version, got %s", vlist[0])
		}
	}
	// no svn for now, because...svn
}
<|endoftext|>


<commit_before>// Copyright ©2015 The Gonum 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 distuv

type LogProber interface {
	LogProb(float64) float64
}

type Rander interface {
	Rand() float64
}

type RandLogProber interface {
	Rander
	LogProber
}

type Quantiler interface {
	Quantile(p float64) float64
}
<commit_msg>stat/distuv: improve interface documentation<commit_after>// Copyright ©2015 The Gonum 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 distuv

// LogProber wraps the LogProb method.
type LogProber interface {
	// LogProb returns the natural logarithm of the
	// value of the probability density or probability
	// mass function at x.
	LogProb(x float64) float64
}

// Rander wraps the Rand method.
type Rander interface {
	// Rand returns a random sample drawn from the distribution.
	Rand() float64
}

// RandLogProber is the interface that groups the Rander and LogProber methods.
type RandLogProber interface {
	Rander
	LogProber
}

// Quantiler wraps the Quantile method.
type Quantiler interface {
	// Quantile returns the minimum value of x from amongst
	// all those values whose CDF value exceeds or equals p.
	Quantile(p float64) float64
}
<|endoftext|>


<commit_before>package lib

import (
	"github.com/Sirupsen/logrus"
	"golang.org/x/net/context"
)

type contextKey int

const (
	uuidKey contextKey = iota
	processorKey
)

func contextFromUUID(ctx context.Context, uuid string) context.Context {
	return context.WithValue(ctx, uuidKey, uuid)
}

func contextFromProcessor(ctx context.Context, processor string) context.Context {
	return context.WithValue(ctx, processorKey, processor)
}

func uuidFromContext(ctx context.Context) (string, bool) {
	uuid, ok := ctx.Value(uuidKey).(string)
	return uuid, ok
}

func processorFromContext(ctx context.Context) (string, bool) {
	processor, ok := ctx.Value(processorKey).(string)
	return processor, ok
}

func LoggerFromContext(ctx context.Context) *logrus.Entry {
	entry := logrus.NewEntry(logrus.New()).WithField("pid", os.Getpid())

	if uuid, ok := uuidFromContext(ctx); ok {
		entry = entry.WithField("uuid", uuid)
	}

	if processor, ok := processorFromContext(ctx); ok {
		entry = entry.WithField("processor", processor)
	}

	return entry
}
<commit_msg>context: add a component key to context<commit_after>package lib

import (
	"os"

	"github.com/Sirupsen/logrus"
	"golang.org/x/net/context"
)

type contextKey int

const (
	uuidKey contextKey = iota
	processorKey
	componentKey
)

func contextFromUUID(ctx context.Context, uuid string) context.Context {
	return context.WithValue(ctx, uuidKey, uuid)
}

func contextFromProcessor(ctx context.Context, processor string) context.Context {
	return context.WithValue(ctx, processorKey, processor)
}

func contextFromComponent(ctx context.Context, component string) context.Context {
	return context.WithValue(ctx, componentKey, component)
}

func uuidFromContext(ctx context.Context) (string, bool) {
	uuid, ok := ctx.Value(uuidKey).(string)
	return uuid, ok
}

func processorFromContext(ctx context.Context) (string, bool) {
	processor, ok := ctx.Value(processorKey).(string)
	return processor, ok
}

func componentFromContext(ctx context.Context) (string, bool) {
	component, ok := ctx.Value(componentKey).(string)
	return component, ok
}

func LoggerFromContext(ctx context.Context) *logrus.Entry {
	entry := logrus.NewEntry(logrus.New()).WithField("pid", os.Getpid())

	if uuid, ok := uuidFromContext(ctx); ok {
		entry = entry.WithField("uuid", uuid)
	}

	if processor, ok := processorFromContext(ctx); ok {
		entry = entry.WithField("processor", processor)
	}

	if component, ok := componentFromContext(ctx); ok {
		entry = entry.WithField("component", component)
	}

	return entry
}
<|endoftext|>


<commit_before>package log

type Logger interface {
	Fatal(v ...interface{})
	Fatalf(format string, v ...interface{})
	Fatalln(v ...interface{})
	Panic(v ...interface{})
	Panicf(format string, v ...interface{})
	Panicln(v ...interface{})
	Print(v ...interface{})
	Printf(format string, v ...interface{})
	Println(v ...interface{})
}

type DebugLogger interface {
	Debug(level uint8, v ...interface{})
	Debugf(level uint8, format string, v ...interface{})
	Debugln(level uint8, v ...interface{})
	Logger
}

type DebugLogLevelSetter interface {
	SetLevel(maxLevel int16)
}
<commit_msg>Add lib/log.DebugLogLevelGetter interface.<commit_after>package log

type Logger interface {
	Fatal(v ...interface{})
	Fatalf(format string, v ...interface{})
	Fatalln(v ...interface{})
	Panic(v ...interface{})
	Panicf(format string, v ...interface{})
	Panicln(v ...interface{})
	Print(v ...interface{})
	Printf(format string, v ...interface{})
	Println(v ...interface{})
}

type DebugLogger interface {
	Debug(level uint8, v ...interface{})
	Debugf(level uint8, format string, v ...interface{})
	Debugln(level uint8, v ...interface{})
	Logger
}

type DebugLogLevelGetter interface {
	GetLevel() int16
}

type DebugLogLevelSetter interface {
	SetLevel(maxLevel int16)
}
<|endoftext|>


<commit_before>package lib

import (
    "fmt"
    "os"
    "time"

    . "gopkg.in/check.v1"
)

func (s *OssutilCommandSuite) rawList(args []string, shortFormat, directory bool) (bool, error) {
    command := "ls"
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &configFile,
        "shortFormat": &shortFormat,
        "directory": &directory,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    return showElapse, err
}

// test list buckets
func (s *OssutilCommandSuite) TestListLoadConfig(c *C) {
    command := "ls"
    var args []string
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &configFile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    args = []string{"oss://"}
    options = OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &configFile,
    }
    showElapse, err = cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)
}

func (s *OssutilCommandSuite) TestListNotExistConfigFile(c *C) {
    command := "ls"
    var args []string
    str := ""
    cfile := "notexistfile"
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)
}

func (s *OssutilCommandSuite) TestListErrConfigFile(c *C) {
    cfile := "ossutil_test.config_boto"
    s.createFile(cfile, content, c)

    command := "ls"
    var args []string
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    _ = os.Remove(cfile)
}

func (s *OssutilCommandSuite) TestListConfigFile(c *C) {
    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\nretryTimes=%s", endpoint, accessKeyID, accessKeySecret, "errretry")
    s.createFile(cfile, data, c)

    command := "ls"
    var args []string
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
}

func (s *OssutilCommandSuite) TestListWithBucketEndpoint(c *C) {
    bucket := bucketNameExist

    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\n[Bucket-Endpoint]\n%s=%s", "abc", accessKeyID, accessKeySecret, bucket, endpoint)
    s.createFile(cfile, data, c)

    command := "ls"
    args := []string{CloudURLToString(bucket, "")}
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
}

func (s *OssutilCommandSuite) TestListWithBucketCname(c *C) {
    bucket := bucketNamePrefix + "ls1"
    s.putBucket(bucket, c)

    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\n[Bucket-Endpoint]\n%s=%s\n[Bucket-Cname]\n%s=%s", "abc", accessKeyID, accessKeySecret, bucket, "abc", bucket, bucket + "." + endpoint)
    s.createFile(cfile, data, c)

    command := "ls"
    args := []string{CloudURLToString(bucket, "")}
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
    s.removeBucket(bucket, true, c)
    time.Sleep(sleepTime)
}

func (s *OssutilCommandSuite) TestListBuckets(c *C) {
    // "ls"
    bucket := bucketNamePrefix + "ls2"
    s.removeBucket(bucket, true, c)
    time.Sleep(3*sleepTime)

    buckets := s.listBuckets(false, c)
    c.Assert(FindPos(bucket, buckets) == -1, Equals, true)
    bucketNum := len(buckets)

    // "ls -s"
    buckets = s.listBuckets(true, c)
    c.Assert(FindPos(bucket, buckets) == -1, Equals, true)

    // put bucket
    s.putBucket(bucket, c)
    time.Sleep(2*sleepTime)

    // get result
    buckets = s.listBuckets(false, c)
    c.Assert(FindPos(bucket, buckets) != -1, Equals, true)
    c.Assert(len(buckets) >= bucketNum, Equals, true)

    // remove bucket
    s.removeBucket(bucket, true, c)
    time.Sleep(sleepTime)

    // get result
    buckets = s.listBuckets(false, c)
    c.Assert(FindPos(bucket, buckets) == -1, Equals, true)
}

// list objects with not exist bucket
func (s *OssutilCommandSuite) TestListObjectsBucketNotExist(c *C) {
    bucket := bucketNameNotExist
    command := "ls"
    args := []string{CloudURLToString(bucket, "")}
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &configFile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)
}

// list objects
func (s *OssutilCommandSuite) TestListObjects(c *C) {
    bucket := bucketNameList

    // "ls oss://bucket"
    objects := s.listObjects(bucket, "", false, false, c)
    c.Assert(len(objects), Equals, 0)

    // put objects
    num := 10
    objectNames := []string{}
    for i := 0; i < num; i++ {
        object := fmt.Sprintf("lstest:#%d", i)
        s.putObject(bucket, object, uploadFileName, c)
        objectNames = append(objectNames, object)
    }

    object := "another_object"
    s.putObject(bucket, object, uploadFileName, c)
    objectNames = append(objectNames, object)

    // "ls oss://bucket -s"
    objects = s.listObjects(bucket, "", true, false, c)
    c.Assert(len(objects), Equals, len(objectNames))

    // "ls oss://bucket/prefix -s"
    objects = s.listObjects(bucket, "lstest:", true, false, c)
    c.Assert(len(objects), Equals, len(objectNames) - 1)

    //put directories
    num = 5
    objectNames = []string{}
    for i := 0; i < num; i++ {
        object := fmt.Sprintf("lstest:#%d/", i)
        s.putObject(bucket, object, uploadFileName, c)

        object = fmt.Sprintf("lstest:#%d/%d/", i, i)
        s.putObject(bucket, object, uploadFileName, c)
        objectNames = append(objectNames, object)
    }

    // "ls oss://bucket/prefix"
    objects = s.listObjects(bucket, "lstest:#", false, false, c)
    c.Assert(len(objects), Equals, 20)

    // "ls oss://bucket/prefix -d"
    objects = s.listObjects(bucket, "lstest:#", false, true, c)
    c.Assert(len(objects), Equals, 15)

    objects = s.listObjects(bucket, "lstest:#1/", false, true, c)
    c.Assert(len(objects), Equals, 2)
}

func (s *OssutilCommandSuite) TestErrList(c *C) {
    showElapse, err := s.rawList([]string{"../"}, true, false)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    bucket := bucketNameNotExist
    showElapse, err = s.rawList([]string{CloudURLToString(bucket, "")}, false, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    // list buckets with -d
    showElapse, err = s.rawList([]string{"oss://"}, false, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)
}

func (s *OssutilCommandSuite) TestListIDKey(c *C) {
    bucket := bucketNamePrefix + "lsidkey"

    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\n[Bucket-Endpoint]\n%s=%s[Bucket-Cname]\n%s=%s", "abc", "def", "ghi", bucket, "abc", bucket, "abc")
    s.createFile(cfile, data, c)

    command := "ls"
    str := ""
    args := []string{"oss://"}
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)

    options = OptionMapType{
        "endpoint": &endpoint,
        "accessKeyID": &accessKeyID,
        "accessKeySecret": &accessKeySecret,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err = cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
}

func (s *OssutilCommandSuite) TestListBucketIDKey(c *C) {
    bucket := bucketNameExist

    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\n[Bucket-Endpoint]\n%s=%s[Bucket-Cname]\n%s=%s", "abc", "def", "ghi", bucket, "abc", bucket, "abc")
    s.createFile(cfile, data, c)

    command := "ls"
    str := ""
    args := []string{CloudURLToString(bucket, "")}
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)

    options = OptionMapType{
        "endpoint": &endpoint,
        "accessKeyID": &accessKeyID,
        "accessKeySecret": &accessKeySecret,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err = cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
}
<commit_msg>modify case<commit_after>package lib

import (
    "fmt"
    "os"
    "time"

    . "gopkg.in/check.v1"
)

func (s *OssutilCommandSuite) rawList(args []string, shortFormat, directory bool) (bool, error) {
    command := "ls"
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &configFile,
        "shortFormat": &shortFormat,
        "directory": &directory,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    return showElapse, err
}

// test list buckets
func (s *OssutilCommandSuite) TestListLoadConfig(c *C) {
    command := "ls"
    var args []string
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &configFile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    args = []string{"oss://"}
    options = OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &configFile,
    }
    showElapse, err = cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)
}

func (s *OssutilCommandSuite) TestListNotExistConfigFile(c *C) {
    command := "ls"
    var args []string
    str := ""
    cfile := "notexistfile"
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)
}

func (s *OssutilCommandSuite) TestListErrConfigFile(c *C) {
    cfile := "ossutil_test.config_boto"
    s.createFile(cfile, content, c)

    command := "ls"
    var args []string
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    _ = os.Remove(cfile)
}

func (s *OssutilCommandSuite) TestListConfigFile(c *C) {
    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\nretryTimes=%s", endpoint, accessKeyID, accessKeySecret, "errretry")
    s.createFile(cfile, data, c)

    command := "ls"
    var args []string
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
}

func (s *OssutilCommandSuite) TestListWithBucketEndpoint(c *C) {
    bucket := bucketNameExist

    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\n[Bucket-Endpoint]\n%s=%s", "abc", accessKeyID, accessKeySecret, bucket, endpoint)
    s.createFile(cfile, data, c)

    command := "ls"
    args := []string{CloudURLToString(bucket, "")}
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
}

func (s *OssutilCommandSuite) TestListWithBucketCname(c *C) {
    bucket := bucketNamePrefix + "ls1"
    s.putBucket(bucket, c)

    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\n[Bucket-Endpoint]\n%s=%s\n[Bucket-Cname]\n%s=%s", "abc", accessKeyID, accessKeySecret, bucket, "abc", bucket, bucket + "." + endpoint)
    s.createFile(cfile, data, c)

    command := "ls"
    args := []string{CloudURLToString(bucket, "")}
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
    s.removeBucket(bucket, true, c)
    time.Sleep(sleepTime)
}

func (s *OssutilCommandSuite) TestListBuckets(c *C) {
    // "ls"
    bucket := bucketNamePrefix + "ls2"
    s.removeBucket(bucket, true, c)
    time.Sleep(3*sleepTime)

    buckets := s.listBuckets(false, c)
    c.Assert(FindPos(bucket, buckets) == -1, Equals, true)
    bucketNum := len(buckets)

    // "ls -s"
    buckets = s.listBuckets(true, c)
    c.Assert(FindPos(bucket, buckets) == -1, Equals, true)

    // put bucket
    s.putBucket(bucket, c)
    time.Sleep(2*sleepTime)

    // get result
    buckets = s.listBuckets(false, c)
    c.Assert(FindPos(bucket, buckets) != -1, Equals, true)
    c.Assert(len(buckets) >= bucketNum, Equals, true)

    // remove bucket
    s.removeBucket(bucket, true, c)
    time.Sleep(sleepTime)

    // get result
    buckets = s.listBuckets(false, c)
    c.Assert(FindPos(bucket, buckets) == -1, Equals, true)
}

// list objects with not exist bucket
func (s *OssutilCommandSuite) TestListObjectsBucketNotExist(c *C) {
    bucket := bucketNameNotExist
    command := "ls"
    args := []string{CloudURLToString(bucket, "")}
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &configFile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)
}

// list objects
func (s *OssutilCommandSuite) TestListObjects(c *C) {
    bucket := bucketNameList
    s.removeObjects(bucket, "", true, true, c)
    time.Sleep(sleepTime)

    // "ls oss://bucket"
    objects := s.listObjects(bucket, "", false, false, c)
    c.Assert(len(objects), Equals, 0)

    // put objects
    num := 10
    objectNames := []string{}
    for i := 0; i < num; i++ {
        object := fmt.Sprintf("lstest:#%d", i)
        s.putObject(bucket, object, uploadFileName, c)
        objectNames = append(objectNames, object)
    }

    object := "another_object"
    s.putObject(bucket, object, uploadFileName, c)
    objectNames = append(objectNames, object)

    // "ls oss://bucket -s"
    objects = s.listObjects(bucket, "", true, false, c)
    c.Assert(len(objects), Equals, len(objectNames))

    // "ls oss://bucket/prefix -s"
    objects = s.listObjects(bucket, "lstest:", true, false, c)
    c.Assert(len(objects), Equals, len(objectNames) - 1)

    //put directories
    num = 5
    objectNames = []string{}
    for i := 0; i < num; i++ {
        object := fmt.Sprintf("lstest:#%d/", i)
        s.putObject(bucket, object, uploadFileName, c)

        object = fmt.Sprintf("lstest:#%d/%d/", i, i)
        s.putObject(bucket, object, uploadFileName, c)
        objectNames = append(objectNames, object)
    }

    // "ls oss://bucket/prefix"
    objects = s.listObjects(bucket, "lstest:#", false, false, c)
    c.Assert(len(objects), Equals, 20)

    // "ls oss://bucket/prefix -d"
    objects = s.listObjects(bucket, "lstest:#", false, true, c)
    c.Assert(len(objects), Equals, 15)

    objects = s.listObjects(bucket, "lstest:#1/", false, true, c)
    c.Assert(len(objects), Equals, 2)
}

func (s *OssutilCommandSuite) TestErrList(c *C) {
    showElapse, err := s.rawList([]string{"../"}, true, false)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    bucket := bucketNameNotExist
    showElapse, err = s.rawList([]string{CloudURLToString(bucket, "")}, false, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    // list buckets with -d
    showElapse, err = s.rawList([]string{"oss://"}, false, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)
}

func (s *OssutilCommandSuite) TestListIDKey(c *C) {
    bucket := bucketNamePrefix + "lsidkey"

    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\n[Bucket-Endpoint]\n%s=%s[Bucket-Cname]\n%s=%s", "abc", "def", "ghi", bucket, "abc", bucket, "abc")
    s.createFile(cfile, data, c)

    command := "ls"
    str := ""
    args := []string{"oss://"}
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)

    options = OptionMapType{
        "endpoint": &endpoint,
        "accessKeyID": &accessKeyID,
        "accessKeySecret": &accessKeySecret,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err = cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
}

func (s *OssutilCommandSuite) TestListBucketIDKey(c *C) {
    bucket := bucketNameExist

    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\n[Bucket-Endpoint]\n%s=%s[Bucket-Cname]\n%s=%s", "abc", "def", "ghi", bucket, "abc", bucket, "abc")
    s.createFile(cfile, data, c)

    command := "ls"
    str := ""
    args := []string{CloudURLToString(bucket, "")}
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)

    options = OptionMapType{
        "endpoint": &endpoint,
        "accessKeyID": &accessKeyID,
        "accessKeySecret": &accessKeySecret,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err = cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
}
<|endoftext|>


<commit_before>package lib

import "net/url"

// StartupScript on Vultr account
type StartupScript struct {
	ID      string `json:"SCRIPTID"`
	Name    string `json:"name"`
	Type    string `json:"type"`
	Content string `json:"script"`
}

func (c *Client) GetStartupScripts() (scripts []StartupScript, err error) {
	var scriptMap map[string]StartupScript
	if err := c.get(`startupscript/list`, &scriptMap); err != nil {
		return nil, err
	}

	for _, script := range scriptMap {
		if script.Type == "" {
			script.Type = "boot" // set default script type
		}
		scripts = append(scripts, script)
	}
	return scripts, nil
}

func (c *Client) GetStartupScript(id string) (StartupScript, error) {
	scripts, err := c.GetStartupScripts()
	if err != nil {
		return StartupScript{}, err
	}

	for _, s := range scripts {
		if s.ID == id {
			return s, nil
		}
	}
	return StartupScript{}, nil
}

func (c *Client) CreateStartupScript(name, content, scriptType string) (StartupScript, error) {
	values := url.Values{
		"name":   {name},
		"script": {content},
		"type":   {scriptType},
	}

	var script StartupScript
	if err := c.post(`startupscript/create`, values, &script); err != nil {
		return StartupScript{}, err
	}
	script.Name = name
	script.Content = content
	script.Type = scriptType

	return script, nil
}

func (c *Client) UpdateStartupScript(script StartupScript) error {
	values := url.Values{
		"SCRIPTID": {script.ID},
	}
	if script.Name != "" {
		values.Add("name", script.Name)
	}
	if script.Content != "" {
		values.Add("script", script.Content)
	}

	if err := c.post(`startupscript/update`, values, nil); err != nil {
		return err
	}
	return nil
}

func (c *Client) DeleteStartupScript(id string) error {
	values := url.Values{
		"SCRIPTID": {id},
	}

	if err := c.post(`startupscript/destroy`, values, nil); err != nil {
		return err
	}
	return nil
}
<commit_msg>handle inconsistent API responses for startupscripts<commit_after>package lib

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

// StartupScript on Vultr account
type StartupScript struct {
	ID      string `json:"SCRIPTID"`
	Name    string `json:"name"`
	Type    string `json:"type"`
	Content string `json:"script"`
}

// Implements json.Unmarshaller on StartupScript.
// Necessary because the SRIPTID field has inconsistent types.
func (s *StartupScript) UnmarshalJSON(data []byte) (err error) {
	if s == nil {
		*s = StartupScript{}
	}

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

	s.ID = fmt.Sprintf("%v", fields["SCRIPTID"])
	s.Name = fmt.Sprintf("%v", fields["name"])
	s.Type = fmt.Sprintf("%v", fields["type"])
	s.Content = fmt.Sprintf("%v", fields["script"])

	return
}

func (c *Client) GetStartupScripts() (scripts []StartupScript, err error) {
	var scriptMap map[string]StartupScript
	if err := c.get(`startupscript/list`, &scriptMap); err != nil {
		return nil, err
	}

	for _, script := range scriptMap {
		if script.Type == "" {
			script.Type = "boot" // set default script type
		}
		scripts = append(scripts, script)
	}
	return scripts, nil
}

func (c *Client) GetStartupScript(id string) (StartupScript, error) {
	scripts, err := c.GetStartupScripts()
	if err != nil {
		return StartupScript{}, err
	}

	for _, s := range scripts {
		if s.ID == id {
			return s, nil
		}
	}
	return StartupScript{}, nil
}

func (c *Client) CreateStartupScript(name, content, scriptType string) (StartupScript, error) {
	values := url.Values{
		"name":   {name},
		"script": {content},
		"type":   {scriptType},
	}

	var script StartupScript
	if err := c.post(`startupscript/create`, values, &script); err != nil {
		return StartupScript{}, err
	}
	script.Name = name
	script.Content = content
	script.Type = scriptType

	return script, nil
}

func (c *Client) UpdateStartupScript(script StartupScript) error {
	values := url.Values{
		"SCRIPTID": {script.ID},
	}
	if script.Name != "" {
		values.Add("name", script.Name)
	}
	if script.Content != "" {
		values.Add("script", script.Content)
	}

	if err := c.post(`startupscript/update`, values, nil); err != nil {
		return err
	}
	return nil
}

func (c *Client) DeleteStartupScript(id string) error {
	values := url.Values{
		"SCRIPTID": {id},
	}

	if err := c.post(`startupscript/destroy`, values, nil); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package main

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

	"github.com/gorilla/websocket"
	"github.com/spf13/cobra"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/shared/api"
	cli "github.com/lxc/lxd/shared/cmd"
	"github.com/lxc/lxd/shared/i18n"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/termios"
)

type cmdExec struct {
	global *cmdGlobal

	flagMode                string
	flagEnvironment         []string
	flagForceInteractive    bool
	flagForceNonInteractive bool
	flagDisableStdin        bool
}

func (c *cmdExec) Command() *cobra.Command {
	cmd := &cobra.Command{}
	cmd.Use = i18n.G("exec [<remote>:]<container> [flags] [--] <command line>")
	cmd.Short = i18n.G("Execute commands in containers")
	cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
		`Execute commands in containers

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

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

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

	cmd.RunE = c.Run
	cmd.Flags().StringArrayVar(&c.flagEnvironment, "env", nil, i18n.G("Environment variable to set (e.g. HOME=/home/foo)")+"``")
	cmd.Flags().StringVar(&c.flagMode, "mode", "auto", i18n.G("Override the terminal mode (auto, interactive or non-interactive)")+"``")
	cmd.Flags().BoolVarP(&c.flagForceInteractive, "force-interactive", "t", false, i18n.G("Force pseudo-terminal allocation"))
	cmd.Flags().BoolVarP(&c.flagForceNonInteractive, "force-noninteractive", "T", false, i18n.G("Disable pseudo-terminal allocation"))
	cmd.Flags().BoolVarP(&c.flagDisableStdin, "disable-stdin", "n", false, i18n.G("Disable stdin (reads from /dev/null)"))

	return cmd
}

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

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

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

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

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

	w.Close()
	return err
}

func (c *cmdExec) Run(cmd *cobra.Command, args []string) error {
	conf := c.global.conf

	// Sanity checks
	exit, err := c.global.CheckArgs(cmd, args, 2, -1)
	if exit {
		return err
	}

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

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

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

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

	// Set the environment
	env := map[string]string{}
	myTerm, ok := c.getTERM()
	if ok {
		env["TERM"] = myTerm
	}

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

	// Configure the terminal
	stdinFd := getStdinFd()
	stdoutFd := getStdoutFd()

	stdinTerminal := termios.IsTerminal(stdinFd)
	stdoutTerminal := termios.IsTerminal(stdoutFd)

	// Determine interaction mode
	var interactive bool
	if c.flagDisableStdin {
		interactive = false
	} else if c.flagMode == "interactive" || c.flagForceInteractive {
		interactive = true
	} else if c.flagMode == "non-interactive" || c.flagForceNonInteractive {
		interactive = false
	} else {
		interactive = stdinTerminal && stdoutTerminal
	}

	// Record terminal state
	var oldttystate *termios.State
	if interactive && stdinTerminal {
		oldttystate, err = termios.MakeRaw(stdinFd)
		if err != nil {
			return err
		}

		defer termios.Restore(stdinFd, oldttystate)
	}

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

	// Grab current terminal dimensions
	var width, height int
	if stdoutTerminal {
		width, height, err = termios.GetSize(getStdoutFd())
		if err != nil {
			return err
		}
	}

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

	stdout := getStdout()

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

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

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

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

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

	c.global.ret = int(opAPI.Metadata["return"].(float64))
	return nil
}
<commit_msg>lxc/exec: Add user, group and cwd<commit_after>package main

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

	"github.com/gorilla/websocket"
	"github.com/spf13/cobra"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/shared/api"
	cli "github.com/lxc/lxd/shared/cmd"
	"github.com/lxc/lxd/shared/i18n"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/termios"
)

type cmdExec struct {
	global *cmdGlobal

	flagMode                string
	flagEnvironment         []string
	flagForceInteractive    bool
	flagForceNonInteractive bool
	flagDisableStdin        bool
	flagUser                uint32
	flagGroup               uint32
	flagCwd                 string
}

func (c *cmdExec) Command() *cobra.Command {
	cmd := &cobra.Command{}
	cmd.Use = i18n.G("exec [<remote>:]<container> [flags] [--] <command line>")
	cmd.Short = i18n.G("Execute commands in containers")
	cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
		`Execute commands in containers

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

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

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

	cmd.RunE = c.Run
	cmd.Flags().StringArrayVar(&c.flagEnvironment, "env", nil, i18n.G("Environment variable to set (e.g. HOME=/home/foo)")+"``")
	cmd.Flags().StringVar(&c.flagMode, "mode", "auto", i18n.G("Override the terminal mode (auto, interactive or non-interactive)")+"``")
	cmd.Flags().BoolVarP(&c.flagForceInteractive, "force-interactive", "t", false, i18n.G("Force pseudo-terminal allocation"))
	cmd.Flags().BoolVarP(&c.flagForceNonInteractive, "force-noninteractive", "T", false, i18n.G("Disable pseudo-terminal allocation"))
	cmd.Flags().BoolVarP(&c.flagDisableStdin, "disable-stdin", "n", false, i18n.G("Disable stdin (reads from /dev/null)"))
	cmd.Flags().Uint32Var(&c.flagUser, "user", 0, i18n.G("User ID to run the command as (default 0)")+"``")
	cmd.Flags().Uint32Var(&c.flagGroup, "group", 0, i18n.G("Group ID to run the command as (default 0)")+"``")
	cmd.Flags().StringVar(&c.flagCwd, "cwd", "", i18n.G("Group ID to run the command as (default /root)")+"``")

	return cmd
}

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

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

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

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

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

	w.Close()
	return err
}

func (c *cmdExec) Run(cmd *cobra.Command, args []string) error {
	conf := c.global.conf

	// Sanity checks
	exit, err := c.global.CheckArgs(cmd, args, 2, -1)
	if exit {
		return err
	}

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

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

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

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

	// Set the environment
	env := map[string]string{}
	myTerm, ok := c.getTERM()
	if ok {
		env["TERM"] = myTerm
	}

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

	// Configure the terminal
	stdinFd := getStdinFd()
	stdoutFd := getStdoutFd()

	stdinTerminal := termios.IsTerminal(stdinFd)
	stdoutTerminal := termios.IsTerminal(stdoutFd)

	// Determine interaction mode
	var interactive bool
	if c.flagDisableStdin {
		interactive = false
	} else if c.flagMode == "interactive" || c.flagForceInteractive {
		interactive = true
	} else if c.flagMode == "non-interactive" || c.flagForceNonInteractive {
		interactive = false
	} else {
		interactive = stdinTerminal && stdoutTerminal
	}

	// Record terminal state
	var oldttystate *termios.State
	if interactive && stdinTerminal {
		oldttystate, err = termios.MakeRaw(stdinFd)
		if err != nil {
			return err
		}

		defer termios.Restore(stdinFd, oldttystate)
	}

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

	// Grab current terminal dimensions
	var width, height int
	if stdoutTerminal {
		width, height, err = termios.GetSize(getStdoutFd())
		if err != nil {
			return err
		}
	}

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

	stdout := getStdout()

	// Prepare the command
	req := api.ContainerExecPost{
		Command:     args[1:],
		WaitForWS:   true,
		Interactive: interactive,
		Environment: env,
		Width:       width,
		Height:      height,
		User:        c.flagUser,
		Group:       c.flagGroup,
		Cwd:         c.flagCwd,
	}

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

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

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

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

	c.global.ret = int(opAPI.Metadata["return"].(float64))
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/exec"
	"path"
	"strings"
	"syscall"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/gnuflag"
	"github.com/lxc/lxd/shared/i18n"
	"github.com/lxc/lxd/shared/logging"
)

var configPath string

func main() {
	if err := run(); err != nil {
		msg := fmt.Sprintf(i18n.G("error: %v"), err)

		lxdErr := lxd.GetLocalLXDErr(err)
		switch lxdErr {
		case syscall.ENOENT:
			msg = i18n.G("LXD socket not found; is LXD installed and running?")
		case syscall.ECONNREFUSED:
			msg = i18n.G("Connection refused; is LXD running?")
		case syscall.EACCES:
			msg = i18n.G("Permission denied, are you in the lxd group?")
		}

		fmt.Fprintln(os.Stderr, fmt.Sprintf("%s", msg))
		os.Exit(1)
	}
}

func run() error {
	verbose := gnuflag.Bool("verbose", false, i18n.G("Enables verbose mode."))
	debug := gnuflag.Bool("debug", false, i18n.G("Enables debug mode."))
	forceLocal := gnuflag.Bool("force-local", false, i18n.G("Force using the local unix socket."))
	noAlias := gnuflag.Bool("no-alias", false, i18n.G("Ignore aliases when determining what command to run."))

	configDir := "$HOME/.config/lxc"
	if os.Getenv("LXD_CONF") != "" {
		configDir = os.Getenv("LXD_CONF")
	}
	configPath = os.ExpandEnv(path.Join(configDir, "config.yml"))

	if len(os.Args) >= 3 && os.Args[1] == "config" && os.Args[2] == "profile" {
		fmt.Fprintf(os.Stderr, i18n.G("`lxc config profile` is deprecated, please use `lxc profile`")+"\n")
		os.Args = append(os.Args[:1], os.Args[2:]...)
	}

	if len(os.Args) >= 2 && (os.Args[1] == "-h" || os.Args[1] == "--help") {
		os.Args[1] = "help"
	}

	if len(os.Args) >= 2 && (os.Args[1] == "--all") {
		os.Args[1] = "help"
		os.Args = append(os.Args, "--all")
	}

	if len(os.Args) == 2 && os.Args[1] == "--version" {
		os.Args[1] = "version"
	}

	if len(os.Args) == 2 && os.Args[1] == "--man" {
		os.Args[1] = "manpage"
	}

	if len(os.Args) < 2 {
		commands["help"].run(nil, nil)
		os.Exit(1)
	}

	var config *lxd.Config
	var err error

	if *forceLocal {
		config = &lxd.DefaultConfig
	} else {
		config, err = lxd.LoadConfig(configPath)
		if err != nil {
			return err
		}
	}

	// This is quite impolite, but it seems gnuflag needs us to shift our
	// own exename out of the arguments before parsing them. However, this
	// is useful for execIfAlias, which wants to know exactly the command
	// line we received, and in some cases is called before this shift, and
	// in others after. So, let's save the original args.
	origArgs := os.Args
	name := os.Args[1]

	/* at this point we haven't parsed the args, so we have to look for
	 * --no-alias by hand.
	 */
	if !shared.StringInSlice("--no-alias", origArgs) {
		execIfAliases(config, origArgs)
	}
	cmd, ok := commands[name]
	if !ok {
		commands["help"].run(nil, nil)
		fmt.Fprintf(os.Stderr, "\n"+i18n.G("error: unknown command: %s")+"\n", name)
		os.Exit(1)
	}
	cmd.flags()
	gnuflag.Usage = func() {
		fmt.Fprintf(os.Stderr, i18n.G("Usage: %s")+"\n\n"+i18n.G("Options:")+"\n\n", strings.TrimSpace(cmd.usage()))
		gnuflag.PrintDefaults()
	}

	os.Args = os.Args[1:]
	gnuflag.Parse(true)

	shared.Log, err = logging.GetLogger("", "", *verbose, *debug, nil)
	if err != nil {
		return err
	}

	// If the user is running a command that may attempt to connect to the local daemon
	// and this is the first time the client has been run by the user, then check to see
	// if LXD has been properly configured.  Don't display the message if the var path
	// does not exist (LXD not installed), as the user may be targeting a remote daemon.
	if os.Args[0] != "help" && os.Args[0] != "version" && shared.PathExists(shared.VarPath("")) && !shared.PathExists(config.ConfigDir) {

		// Create the config dir so that we don't get in here again for this user.
		err = os.MkdirAll(config.ConfigDir, 0750)
		if err != nil {
			return err
		}

		fmt.Fprintf(os.Stderr, i18n.G("If this is your first time using LXD, you should also run: sudo lxd init")+"\n")
		fmt.Fprintf(os.Stderr, i18n.G("To start your first container, try: lxc launch ubuntu:16.04")+"\n\n")
	}

	err = cmd.run(config, gnuflag.Args())
	if err == errArgs {
		/* If we got an error about invalid arguments, let's try to
		 * expand this as an alias
		 */
		if !*noAlias {
			execIfAliases(config, origArgs)
		}
		fmt.Fprintf(os.Stderr, "%s\n\n"+i18n.G("error: %v")+"\n", cmd.usage(), err)
		os.Exit(1)
	}
	return err
}

type command interface {
	usage() string
	flags()
	showByDefault() bool
	run(config *lxd.Config, args []string) error
}

var commands = map[string]command{
	"config":  &configCmd{},
	"copy":    &copyCmd{},
	"delete":  &deleteCmd{},
	"exec":    &execCmd{},
	"file":    &fileCmd{},
	"finger":  &fingerCmd{},
	"help":    &helpCmd{},
	"image":   &imageCmd{},
	"info":    &infoCmd{},
	"init":    &initCmd{},
	"launch":  &launchCmd{},
	"list":    &listCmd{},
	"manpage": &manpageCmd{},
	"monitor": &monitorCmd{},
	"move":    &moveCmd{},
	"pause": &actionCmd{
		action:         shared.Freeze,
		name:           "pause",
		additionalHelp: i18n.G("The opposite of `lxc pause` is `lxc start`."),
	},
	"profile": &profileCmd{},
	"publish": &publishCmd{},
	"remote":  &remoteCmd{},
	"restart": &actionCmd{
		action:     shared.Restart,
		hasTimeout: true,
		visible:    true,
		name:       "restart",
		timeout:    -1,
	},
	"restore":  &restoreCmd{},
	"snapshot": &snapshotCmd{},
	"start": &actionCmd{
		action:  shared.Start,
		visible: true,
		name:    "start",
	},
	"stop": &actionCmd{
		action:     shared.Stop,
		hasTimeout: true,
		visible:    true,
		name:       "stop",
		timeout:    -1,
	},
	"version": &versionCmd{},
}

// defaultAliases contains LXC's built-in command line aliases.  The built-in
// aliases are checked only if no user-defined alias was found.
var defaultAliases = map[string]string{
	"shell": "exec @ARGS@ -- login -f root",

	"cp": "copy",
	"ls": "list",
	"mv": "move",
	"rm": "delete",

	"image cp": "image copy",
	"image ls": "image list",
	"image rm": "image delete",

	"image alias ls": "image alias list",
	"image alias rm": "image alias delete",

	"remote ls": "remote list",
	"remote mv": "remote rename",
	"remote rm": "remote remove",

	"config device ls": "config device list",
	"config device rm": "config device remove",
}

var errArgs = fmt.Errorf(i18n.G("wrong number of subcommand arguments"))

func findAlias(aliases map[string]string, origArgs []string) ([]string, []string, bool) {
	foundAlias := false
	aliasKey := []string{}
	aliasValue := []string{}

	for k, v := range aliases {
		foundAlias = true
		for i, key := range strings.Split(k, " ") {
			if len(origArgs) <= i+1 || origArgs[i+1] != key {
				foundAlias = false
				break
			}
		}

		if foundAlias {
			aliasKey = strings.Split(k, " ")
			aliasValue = strings.Split(v, " ")
			break
		}
	}

	return aliasKey, aliasValue, foundAlias
}

func expandAlias(config *lxd.Config, origArgs []string) ([]string, bool) {
	aliasKey, aliasValue, foundAlias := findAlias(config.Aliases, origArgs)
	if !foundAlias {
		aliasKey, aliasValue, foundAlias = findAlias(defaultAliases, origArgs)
		if !foundAlias {
			return []string{}, false
		}
	}

	newArgs := []string{origArgs[0]}
	hasReplacedArgsVar := false

	for i, aliasArg := range aliasValue {
		if aliasArg == "@ARGS@" && len(origArgs) > i {
			newArgs = append(newArgs, origArgs[i+1:]...)
			hasReplacedArgsVar = true
		} else {
			newArgs = append(newArgs, aliasArg)
		}
	}

	if !hasReplacedArgsVar {
		/* add the rest of the arguments */
		newArgs = append(newArgs, origArgs[len(aliasKey)+1:]...)
	}

	/* don't re-do aliases the next time; this allows us to have recursive
	 * aliases, e.g. `lxc list` to `lxc list -c n`
	 */
	newArgs = append(newArgs[:2], append([]string{"--no-alias"}, newArgs[2:]...)...)

	return newArgs, true
}

func execIfAliases(config *lxd.Config, origArgs []string) {
	newArgs, expanded := expandAlias(config, origArgs)
	if !expanded {
		return
	}

	path, err := exec.LookPath(origArgs[0])
	if err != nil {
		fmt.Fprintf(os.Stderr, i18n.G("processing aliases failed %s\n"), err)
		os.Exit(5)
	}
	ret := syscall.Exec(path, newArgs, syscall.Environ())
	fmt.Fprintf(os.Stderr, i18n.G("processing aliases failed %s\n"), ret)
	os.Exit(5)
}
<commit_msg>lxc: Add a "rename" alias<commit_after>package main

import (
	"fmt"
	"os"
	"os/exec"
	"path"
	"strings"
	"syscall"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/gnuflag"
	"github.com/lxc/lxd/shared/i18n"
	"github.com/lxc/lxd/shared/logging"
)

var configPath string

func main() {
	if err := run(); err != nil {
		msg := fmt.Sprintf(i18n.G("error: %v"), err)

		lxdErr := lxd.GetLocalLXDErr(err)
		switch lxdErr {
		case syscall.ENOENT:
			msg = i18n.G("LXD socket not found; is LXD installed and running?")
		case syscall.ECONNREFUSED:
			msg = i18n.G("Connection refused; is LXD running?")
		case syscall.EACCES:
			msg = i18n.G("Permission denied, are you in the lxd group?")
		}

		fmt.Fprintln(os.Stderr, fmt.Sprintf("%s", msg))
		os.Exit(1)
	}
}

func run() error {
	verbose := gnuflag.Bool("verbose", false, i18n.G("Enables verbose mode."))
	debug := gnuflag.Bool("debug", false, i18n.G("Enables debug mode."))
	forceLocal := gnuflag.Bool("force-local", false, i18n.G("Force using the local unix socket."))
	noAlias := gnuflag.Bool("no-alias", false, i18n.G("Ignore aliases when determining what command to run."))

	configDir := "$HOME/.config/lxc"
	if os.Getenv("LXD_CONF") != "" {
		configDir = os.Getenv("LXD_CONF")
	}
	configPath = os.ExpandEnv(path.Join(configDir, "config.yml"))

	if len(os.Args) >= 3 && os.Args[1] == "config" && os.Args[2] == "profile" {
		fmt.Fprintf(os.Stderr, i18n.G("`lxc config profile` is deprecated, please use `lxc profile`")+"\n")
		os.Args = append(os.Args[:1], os.Args[2:]...)
	}

	if len(os.Args) >= 2 && (os.Args[1] == "-h" || os.Args[1] == "--help") {
		os.Args[1] = "help"
	}

	if len(os.Args) >= 2 && (os.Args[1] == "--all") {
		os.Args[1] = "help"
		os.Args = append(os.Args, "--all")
	}

	if len(os.Args) == 2 && os.Args[1] == "--version" {
		os.Args[1] = "version"
	}

	if len(os.Args) == 2 && os.Args[1] == "--man" {
		os.Args[1] = "manpage"
	}

	if len(os.Args) < 2 {
		commands["help"].run(nil, nil)
		os.Exit(1)
	}

	var config *lxd.Config
	var err error

	if *forceLocal {
		config = &lxd.DefaultConfig
	} else {
		config, err = lxd.LoadConfig(configPath)
		if err != nil {
			return err
		}
	}

	// This is quite impolite, but it seems gnuflag needs us to shift our
	// own exename out of the arguments before parsing them. However, this
	// is useful for execIfAlias, which wants to know exactly the command
	// line we received, and in some cases is called before this shift, and
	// in others after. So, let's save the original args.
	origArgs := os.Args
	name := os.Args[1]

	/* at this point we haven't parsed the args, so we have to look for
	 * --no-alias by hand.
	 */
	if !shared.StringInSlice("--no-alias", origArgs) {
		execIfAliases(config, origArgs)
	}
	cmd, ok := commands[name]
	if !ok {
		commands["help"].run(nil, nil)
		fmt.Fprintf(os.Stderr, "\n"+i18n.G("error: unknown command: %s")+"\n", name)
		os.Exit(1)
	}
	cmd.flags()
	gnuflag.Usage = func() {
		fmt.Fprintf(os.Stderr, i18n.G("Usage: %s")+"\n\n"+i18n.G("Options:")+"\n\n", strings.TrimSpace(cmd.usage()))
		gnuflag.PrintDefaults()
	}

	os.Args = os.Args[1:]
	gnuflag.Parse(true)

	shared.Log, err = logging.GetLogger("", "", *verbose, *debug, nil)
	if err != nil {
		return err
	}

	// If the user is running a command that may attempt to connect to the local daemon
	// and this is the first time the client has been run by the user, then check to see
	// if LXD has been properly configured.  Don't display the message if the var path
	// does not exist (LXD not installed), as the user may be targeting a remote daemon.
	if os.Args[0] != "help" && os.Args[0] != "version" && shared.PathExists(shared.VarPath("")) && !shared.PathExists(config.ConfigDir) {

		// Create the config dir so that we don't get in here again for this user.
		err = os.MkdirAll(config.ConfigDir, 0750)
		if err != nil {
			return err
		}

		fmt.Fprintf(os.Stderr, i18n.G("If this is your first time using LXD, you should also run: sudo lxd init")+"\n")
		fmt.Fprintf(os.Stderr, i18n.G("To start your first container, try: lxc launch ubuntu:16.04")+"\n\n")
	}

	err = cmd.run(config, gnuflag.Args())
	if err == errArgs {
		/* If we got an error about invalid arguments, let's try to
		 * expand this as an alias
		 */
		if !*noAlias {
			execIfAliases(config, origArgs)
		}
		fmt.Fprintf(os.Stderr, "%s\n\n"+i18n.G("error: %v")+"\n", cmd.usage(), err)
		os.Exit(1)
	}
	return err
}

type command interface {
	usage() string
	flags()
	showByDefault() bool
	run(config *lxd.Config, args []string) error
}

var commands = map[string]command{
	"config":  &configCmd{},
	"copy":    &copyCmd{},
	"delete":  &deleteCmd{},
	"exec":    &execCmd{},
	"file":    &fileCmd{},
	"finger":  &fingerCmd{},
	"help":    &helpCmd{},
	"image":   &imageCmd{},
	"info":    &infoCmd{},
	"init":    &initCmd{},
	"launch":  &launchCmd{},
	"list":    &listCmd{},
	"manpage": &manpageCmd{},
	"monitor": &monitorCmd{},
	"move":    &moveCmd{},
	"pause": &actionCmd{
		action:         shared.Freeze,
		name:           "pause",
		additionalHelp: i18n.G("The opposite of `lxc pause` is `lxc start`."),
	},
	"profile": &profileCmd{},
	"publish": &publishCmd{},
	"remote":  &remoteCmd{},
	"restart": &actionCmd{
		action:     shared.Restart,
		hasTimeout: true,
		visible:    true,
		name:       "restart",
		timeout:    -1,
	},
	"restore":  &restoreCmd{},
	"snapshot": &snapshotCmd{},
	"start": &actionCmd{
		action:  shared.Start,
		visible: true,
		name:    "start",
	},
	"stop": &actionCmd{
		action:     shared.Stop,
		hasTimeout: true,
		visible:    true,
		name:       "stop",
		timeout:    -1,
	},
	"version": &versionCmd{},
}

// defaultAliases contains LXC's built-in command line aliases.  The built-in
// aliases are checked only if no user-defined alias was found.
var defaultAliases = map[string]string{
	"shell": "exec @ARGS@ -- login -f root",

	"cp":     "copy",
	"ls":     "list",
	"mv":     "move",
	"rename": "move",
	"rm":     "delete",

	"image cp": "image copy",
	"image ls": "image list",
	"image rm": "image delete",

	"image alias ls": "image alias list",
	"image alias rm": "image alias delete",

	"remote ls": "remote list",
	"remote mv": "remote rename",
	"remote rm": "remote remove",

	"config device ls": "config device list",
	"config device rm": "config device remove",
}

var errArgs = fmt.Errorf(i18n.G("wrong number of subcommand arguments"))

func findAlias(aliases map[string]string, origArgs []string) ([]string, []string, bool) {
	foundAlias := false
	aliasKey := []string{}
	aliasValue := []string{}

	for k, v := range aliases {
		foundAlias = true
		for i, key := range strings.Split(k, " ") {
			if len(origArgs) <= i+1 || origArgs[i+1] != key {
				foundAlias = false
				break
			}
		}

		if foundAlias {
			aliasKey = strings.Split(k, " ")
			aliasValue = strings.Split(v, " ")
			break
		}
	}

	return aliasKey, aliasValue, foundAlias
}

func expandAlias(config *lxd.Config, origArgs []string) ([]string, bool) {
	aliasKey, aliasValue, foundAlias := findAlias(config.Aliases, origArgs)
	if !foundAlias {
		aliasKey, aliasValue, foundAlias = findAlias(defaultAliases, origArgs)
		if !foundAlias {
			return []string{}, false
		}
	}

	newArgs := []string{origArgs[0]}
	hasReplacedArgsVar := false

	for i, aliasArg := range aliasValue {
		if aliasArg == "@ARGS@" && len(origArgs) > i {
			newArgs = append(newArgs, origArgs[i+1:]...)
			hasReplacedArgsVar = true
		} else {
			newArgs = append(newArgs, aliasArg)
		}
	}

	if !hasReplacedArgsVar {
		/* add the rest of the arguments */
		newArgs = append(newArgs, origArgs[len(aliasKey)+1:]...)
	}

	/* don't re-do aliases the next time; this allows us to have recursive
	 * aliases, e.g. `lxc list` to `lxc list -c n`
	 */
	newArgs = append(newArgs[:2], append([]string{"--no-alias"}, newArgs[2:]...)...)

	return newArgs, true
}

func execIfAliases(config *lxd.Config, origArgs []string) {
	newArgs, expanded := expandAlias(config, origArgs)
	if !expanded {
		return
	}

	path, err := exec.LookPath(origArgs[0])
	if err != nil {
		fmt.Fprintf(os.Stderr, i18n.G("processing aliases failed %s\n"), err)
		os.Exit(5)
	}
	ret := syscall.Exec(path, newArgs, syscall.Environ())
	fmt.Fprintf(os.Stderr, i18n.G("processing aliases failed %s\n"), ret)
	os.Exit(5)
}
<|endoftext|>