<commit_before>// Package odin ...
package odin

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/jcelliott/lumber"

	"github.com/nanobox-io/nanobox/commands/registry"
	"github.com/nanobox-io/nanobox/models"
	"github.com/nanobox-io/nanobox/util"
)

const (
	NANOBOX  = "https://api.nanobox.io/v1/"
	BONESALT = "https://api.bonesalt.com/v1/"
	DEV      = "http://api.nanobox.dev:8080/v1/"
	SIM      = "http://api.nanobox.sim/v1/"
)

var (
	// set the default endpoint to nanobox
	endpoint = "nanobox"
	apiKey   string
)

type (
	evar struct {
		ID    string `json:"id"`
		Key   string `json:"title"`
		Value string `json:"value"`
	}
)

// sets the odin endpoint
func SetEndpoint(stage string) {
	endpoint = stage
}

// Auth ...
func Auth(username, password string) (string, error) {

	//
	params := url.Values{}
	params.Set("password", password)

	//
	resBody := map[string]string{}

	//
	if err := doRequest("GET", fmt.Sprintf("users/%s/auth_token", username), params, nil, &resBody); err != nil {
		return "", err
	}

	return resBody["authentication_token"], nil
}

// App ...
func App(slug string) (models.App, error) {
	app := models.App{}
	var params url.Values
	if strings.Contains(slug, "/") {
		appNameParts := strings.Split(slug, "/")
		if len(appNameParts) == 2 {
			params = url.Values{}
			params.Set("ci", appNameParts[0])
			slug = appNameParts[1]
		}

	}

	return app, doRequest("GET", "apps/"+slug, params, nil, &app)
}

// Deploy ...
func Deploy(appID, id, boxfile, message string) error {

	//
	body := map[string]map[string]string{
		"deploy": {
			"boxfile_content": boxfile,
			"build_id":        id,
			"commit_message":  message,
		},
	}

	var params url.Values

	if strings.Contains(appID, "/") {
		appNameParts := strings.Split(appID, "/")
		if len(appNameParts) == 2 {
			params = url.Values{}
			params.Set("ci", appNameParts[0])
			appID = appNameParts[1]
		}

	}

	return doRequest("POST", fmt.Sprintf("apps/%s/deploys", appID), params, body, nil)
}

func ListEvars(appID string) ([]evar, error) {
	evars := []evar{}

	var params url.Values
	if strings.Contains(appID, "/") {
		appNameParts := strings.Split(appID, "/")
		if len(appNameParts) == 2 {
			params = url.Values{}
			params.Set("ci", appNameParts[0])
			appID = appNameParts[1]
		}

	}

	return evars, doRequest("GET", fmt.Sprintf("apps/%s/evars", appID), params, nil, &evars)
}

func AddEvar(appID, key, val string) error {
	body := map[string]map[string]string{
		"evar": {
			"title": key,
			"value": val,
		},
	}

	var params url.Values

	if strings.Contains(appID, "/") {
		appNameParts := strings.Split(appID, "/")
		if len(appNameParts) == 2 {
			params = url.Values{}
			params.Set("ci", appNameParts[0])
			appID = appNameParts[1]
		}

	}

	return doRequest("POST", fmt.Sprintf("apps/%s/evars", appID), params, body, nil)
}

func RemoveEvar(appID, id string) error {
	var params url.Values

	if strings.Contains(appID, "/") {
		appNameParts := strings.Split(appID, "/")
		if len(appNameParts) == 2 {
			params = url.Values{}
			params.Set("ci", appNameParts[0])
			appID = appNameParts[1]
		}

	}

	return doRequest("DELETE", fmt.Sprintf("apps/%s/evars/%s", appID, id), params, nil, nil)
}

// EstablishTunnel requests a tunnel from odin.
func EstablishTunnel(tunCfg models.TunnelConfig) (models.TunnelInfo, error) {
	r := models.TunnelInfo{Port: tunCfg.DestPort}

	params := url.Values{}
	if strings.Contains(tunCfg.AppName, "/") {
		appNameParts := strings.Split(tunCfg.AppName, "/")
		if len(appNameParts) == 2 {
			params = url.Values{}
			// ci is contextID for teams `team/app`
			params.Set("ci", appNameParts[0])
			tunCfg.AppName = appNameParts[1]
		}
	}

	err := doRequest("GET", fmt.Sprintf("apps/%s/tunnels/%s", tunCfg.AppName, tunCfg.Component), params, r, &r)
	if err != nil {
		if strings.Contains(err.Error(), "port 0") {
			if err2, ok := err.(util.Err); ok {
				err2.Suggest = "Specify a destination port with `-p source:destination`."
				err2.Message = fmt.Sprintf("%s - the component has nothing to tunnel to", err.Error())
				return r, err2
			}
			err = fmt.Errorf("%s - the component has nothing to tunnel to", err.Error())
		}
		if strings.Contains(err.Error(), "Not Found") {
			if err2, ok := err.(util.Err); ok {
				err2.Message = fmt.Sprintf("%s - component '%s' not found on app '%s'", err.Error(), tunCfg.Component, tunCfg.AppName)
				return r, err2
			}
			err = fmt.Errorf("%s - component '%s' not found on app '%s'", err.Error(), tunCfg.Component, tunCfg.AppName)
		}
	}

	return r, err
}

// EstablishConsole ...
// protocol ssh/docker
func EstablishConsole(appID, id string) (string, string, string, error) {
	// use a default user
	params := url.Values{}
	params.Set("user", "gonano")
	if registry.GetString("console_user") != "" {
		params.Set("user", registry.GetString("console_user"))
	}

	if strings.Contains(appID, "/") {
		appNameParts := strings.Split(appID, "/")
		if len(appNameParts) == 2 {
			params.Set("ci", appNameParts[0])
			appID = appNameParts[1]
		}

	}

	r := map[string]string{}
	err := doRequest("GET", fmt.Sprintf("apps/%s/consoles/%s", appID, id), params, nil, &r)

	return r["token"], r["url"], r["protocol"], err
}

// GetWarehouse ...
func GetWarehouse(appID string) (string, string, error) {
	r := map[string]string{}

	var params url.Values
	if strings.Contains(appID, "/") {
		appNameParts := strings.Split(appID, "/")
		if len(appNameParts) == 2 {
			params = url.Values{}
			params.Set("ci", appNameParts[0])
			appID = appNameParts[1]
		}

	}

	err := doRequest("GET", fmt.Sprintf("apps/%s/services/warehouse", appID), params, nil, &r)

	return r["token"], r["url"], err
}

// GetComponent gets the token and url for a given component
func GetComponent(appID, component string) (string, string, error) {
	r := map[string]string{}

	var params url.Values
	if strings.Contains(appID, "/") {
		appNameParts := strings.Split(appID, "/")
		if len(appNameParts) == 2 {
			params = url.Values{}
			params.Set("ci", appNameParts[0])
			appID = appNameParts[1]
		}

	}

	err := doRequest("GET", fmt.Sprintf("apps/%s/services/%s", appID, component), params, nil, &r)
	// r = map["uid":"pusher1" "name":"message bus" "slug":"pusher" "url":"x.x.x.x" "token":"secret" "mode":"simple" "ip":"x.x.x.x" "id":"d371c59b-fbbc-4248-8f3b-7f694a62b5e1"]

	return r["token"], r["url"], err
}

func GetPreviousBuild(appID string) (string, error) {
	r := []map[string]string{}

	var params url.Values
	if strings.Contains(appID, "/") {
		appNameParts := strings.Split(appID, "/")
		if len(appNameParts) == 2 {
			params = url.Values{}
			params.Set("ci", appNameParts[0])
			appID = appNameParts[1]
		}

	}

	err := doRequest("GET", fmt.Sprintf("apps/%s/deploys", appID), params, nil, &r)
	if err != nil {
		return "", err
	}

	if len(r) > 0 {
		return r[0]["build_id"], nil
	}

	return "", nil
}

func SubmitEvent(action, message, app string, meta map[string]interface{}) error {
	params := url.Values{}
	params.Set("api_key", apiKey)

	request := struct {
		Action  string                 `json:"action"`
		App     string                 `json:"eventable_id,omitempty"`
		Meta    map[string]interface{} `json:"meta"`
		Message string                 `json:"message"`
	}{
		Action:  action,
		App:     app,
		Meta:    meta,
		Message: message,
	}

	err := doRequest("POST", "events", params, map[string]interface{}{"event": request}, nil)
	if err != nil {
		return err
	}

	return nil
}

// doRequest ...
func doRequest(method, path string, params url.Values, requestBody, responseBody interface{}) error {

	var rbodyReader io.Reader

	//
	if requestBody != nil {
		jsonBytes, err := json.Marshal(requestBody)
		if err != nil {
			return err
		}
		rbodyReader = bytes.NewBuffer(jsonBytes)
	}

	auth, _ := models.LoadAuthByEndpoint(endpoint)

	// if they have not logged in but the user name and password are both set
	// use attempt to authenticate
	if auth.Key == "" &&
		os.Getenv("NANOBOX_PASSWORD") != "" &&
		os.Getenv("NANOBOX_USERNAME") != "" &&
		path != fmt.Sprintf("users/%s/auth_token", os.Getenv("NANOBOX_USERNAME")) {

		auth.Key, _ = Auth(os.Getenv("NANOBOX_USERNAME"), os.Getenv("NANOBOX_PASSWORD"))
	}

	if params == nil {
		params = url.Values{}
	}
	if auth.Key != "" {
		params.Set("auth_token", auth.Key)
	}

	// fetch the correct url from the endpoint
	url := odinURL()

	//
	lumber.Debug("%s%s?%s\n", url, path, params.Encode())
	req, err := http.NewRequest(method, fmt.Sprintf("%s%s?%s", url, path, params.Encode()), rbodyReader)
	if err != nil {
		return err
	}

	req.Header.Set("Content-Type", "application/json")
	lumber.Trace("REQ: %s %s %s", req.Method, req.URL, req.Proto)

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}

	lumber.Debug("RES: %d %s %s %s (%s)", res.StatusCode, req.Method, req.URL, req.Proto, res.Header.Get("Content-Length"))

	// print the body even if status is not 2XX
	b, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return err
	}

	if res.StatusCode == 401 {
		err = util.ErrorfQuiet("[USER] Unauthorized (%s)", b)
		if err != nil {
			if err2, ok := err.(util.Err); ok {
				err2.Suggest = "It appears you are not logged in, run `nanobox login` and try again"
				return err2
			}
		}
		return err
	}

	if res.StatusCode == 404 {
		err = util.ErrorfQuiet("[USER] Not Found (%s)", b)
		if err != nil {
			if err2, ok := err.(util.Err); ok {
				err2.Suggest = "It appears that app or component wasn't found, check the team/app name and try again"
				return err2
			}
		}
		return err
	}

	// if it is a 400 but not
	if res.StatusCode >= 400 && res.StatusCode < 500 {
		rb := map[string]string{}
		err = json.Unmarshal(b, &rb)
		if err != nil {
			return util.ErrorfQuiet("[NANOBOX] %s", b)
		}

		errorMessage, ok := rb["error"]
		if !ok {
			return util.ErrorfQuiet("[ODIN] %s", b)
		}
		return util.ErrorfQuiet("[ODIN] %s", errorMessage)
	}

	if res.StatusCode == 500 {
		return util.ErrorfQuiet("[ODIN] Internal Server Error (%s)", b)
	}

	if res.StatusCode < 200 || res.StatusCode >= 300 {
		return util.ErrorfQuiet("[ODIN] bad exit response(%d %s %s %s (%s) %s)", res.StatusCode, req.Method, req.URL, req.Proto, res.Header.Get("Content-Length"), b)
	}

	if responseBody != nil {
		lumber.Debug("response body: '%s'\n", b)
		err = json.Unmarshal(b, responseBody)
		if err != nil {
			return err
		}
	}

	return nil
}

func odinURL() string {
	switch endpoint {
	case "bonesalt":
		return BONESALT
	case "dev":
		return DEV
	case "sim":
		return SIM
	default:
		return NANOBOX
	}
}
<commit_msg>Allow either username or email address for login<commit_after>// Package odin ...
package odin

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/jcelliott/lumber"

	"github.com/nanobox-io/nanobox/commands/registry"
	"github.com/nanobox-io/nanobox/models"
	"github.com/nanobox-io/nanobox/util"
)

const (
	NANOBOX  = "https://api.nanobox.io/v1/"
	BONESALT = "https://api.bonesalt.com/v1/"
	DEV      = "http://api.nanobox.dev:8080/v1/"
	SIM      = "http://api.nanobox.sim/v1/"
)

var (
	// set the default endpoint to nanobox
	endpoint = "nanobox"
	apiKey   string
)

type (
	evar struct {
		ID    string `json:"id"`
		Key   string `json:"title"`
		Value string `json:"value"`
	}
)

// sets the odin endpoint
func SetEndpoint(stage string) {
	endpoint = stage
}

// Auth authenticates the user with odin.
func Auth(username, password string) (string, error) {

	loginInfo := struct {
		Slug     string `json:"slug"`
		Password string `json:"password"`
	}{username, password}

	resBody := map[string]string{}

	if err := doRequest("GET", "user_auth_token", nil, loginInfo, &resBody); err != nil {
		return "", err
	}

	return resBody["authentication_token"], nil
}

// App ...
func App(slug string) (models.App, error) {
	app := models.App{}
	var params url.Values
	if strings.Contains(slug, "/") {
		appNameParts := strings.Split(slug, "/")
		if len(appNameParts) == 2 {
			params = url.Values{}
			params.Set("ci", appNameParts[0])
			slug = appNameParts[1]
		}

	}

	return app, doRequest("GET", "apps/"+slug, params, nil, &app)
}

// Deploy ...
func Deploy(appID, id, boxfile, message string) error {

	//
	body := map[string]map[string]string{
		"deploy": {
			"boxfile_content": boxfile,
			"build_id":        id,
			"commit_message":  message,
		},
	}

	var params url.Values

	if strings.Contains(appID, "/") {
		appNameParts := strings.Split(appID, "/")
		if len(appNameParts) == 2 {
			params = url.Values{}
			params.Set("ci", appNameParts[0])
			appID = appNameParts[1]
		}

	}

	return doRequest("POST", fmt.Sprintf("apps/%s/deploys", appID), params, body, nil)
}

func ListEvars(appID string) ([]evar, error) {
	evars := []evar{}

	var params url.Values
	if strings.Contains(appID, "/") {
		appNameParts := strings.Split(appID, "/")
		if len(appNameParts) == 2 {
			params = url.Values{}
			params.Set("ci", appNameParts[0])
			appID = appNameParts[1]
		}

	}

	return evars, doRequest("GET", fmt.Sprintf("apps/%s/evars", appID), params, nil, &evars)
}

func AddEvar(appID, key, val string) error {
	body := map[string]map[string]string{
		"evar": {
			"title": key,
			"value": val,
		},
	}

	var params url.Values

	if strings.Contains(appID, "/") {
		appNameParts := strings.Split(appID, "/")
		if len(appNameParts) == 2 {
			params = url.Values{}
			params.Set("ci", appNameParts[0])
			appID = appNameParts[1]
		}

	}

	return doRequest("POST", fmt.Sprintf("apps/%s/evars", appID), params, body, nil)
}

func RemoveEvar(appID, id string) error {
	var params url.Values

	if strings.Contains(appID, "/") {
		appNameParts := strings.Split(appID, "/")
		if len(appNameParts) == 2 {
			params = url.Values{}
			params.Set("ci", appNameParts[0])
			appID = appNameParts[1]
		}

	}

	return doRequest("DELETE", fmt.Sprintf("apps/%s/evars/%s", appID, id), params, nil, nil)
}

// EstablishTunnel requests a tunnel from odin.
func EstablishTunnel(tunCfg models.TunnelConfig) (models.TunnelInfo, error) {
	r := models.TunnelInfo{Port: tunCfg.DestPort}

	params := url.Values{}
	if strings.Contains(tunCfg.AppName, "/") {
		appNameParts := strings.Split(tunCfg.AppName, "/")
		if len(appNameParts) == 2 {
			params = url.Values{}
			// ci is contextID for teams `team/app`
			params.Set("ci", appNameParts[0])
			tunCfg.AppName = appNameParts[1]
		}
	}

	err := doRequest("GET", fmt.Sprintf("apps/%s/tunnels/%s", tunCfg.AppName, tunCfg.Component), params, r, &r)
	if err != nil {
		if strings.Contains(err.Error(), "valid port") {
			if err2, ok := err.(util.Err); ok {
				err2.Suggest = "Specify a valid destination port with `-p source:destination`"
				err2.Message = err.Error()
				return r, err2
			}
		}
		if strings.Contains(err.Error(), "no default") {
			if err2, ok := err.(util.Err); ok {
				err2.Suggest = "Specify a destination port with `-p source:destination`"
				err2.Message = err.Error()
				return r, err2
			}
		}
		if strings.Contains(err.Error(), "Not Found") {
			if err2, ok := err.(util.Err); ok {
				err2.Message = fmt.Sprintf("%s - component '%s' not found on app '%s'", err.Error(), tunCfg.Component, tunCfg.AppName)
				return r, err2
			}
			err = fmt.Errorf("%s - component '%s' not found on app '%s'", err.Error(), tunCfg.Component, tunCfg.AppName)
		}
	}

	return r, err
}

// EstablishConsole ...
// protocol ssh/docker
func EstablishConsole(appID, id string) (string, string, string, error) {
	// use a default user
	params := url.Values{}
	params.Set("user", "gonano")
	if registry.GetString("console_user") != "" {
		params.Set("user", registry.GetString("console_user"))
	}

	if strings.Contains(appID, "/") {
		appNameParts := strings.Split(appID, "/")
		if len(appNameParts) == 2 {
			params.Set("ci", appNameParts[0])
			appID = appNameParts[1]
		}

	}

	r := map[string]string{}
	err := doRequest("GET", fmt.Sprintf("apps/%s/consoles/%s", appID, id), params, nil, &r)

	return r["token"], r["url"], r["protocol"], err
}

// GetWarehouse ...
func GetWarehouse(appID string) (string, string, error) {
	r := map[string]string{}

	var params url.Values
	if strings.Contains(appID, "/") {
		appNameParts := strings.Split(appID, "/")
		if len(appNameParts) == 2 {
			params = url.Values{}
			params.Set("ci", appNameParts[0])
			appID = appNameParts[1]
		}

	}

	err := doRequest("GET", fmt.Sprintf("apps/%s/services/warehouse", appID), params, nil, &r)

	return r["token"], r["url"], err
}

// GetComponent gets the token and url for a given component
func GetComponent(appID, component string) (string, string, error) {
	r := map[string]string{}

	var params url.Values
	if strings.Contains(appID, "/") {
		appNameParts := strings.Split(appID, "/")
		if len(appNameParts) == 2 {
			params = url.Values{}
			params.Set("ci", appNameParts[0])
			appID = appNameParts[1]
		}

	}

	err := doRequest("GET", fmt.Sprintf("apps/%s/services/%s", appID, component), params, nil, &r)
	// r = map["uid":"pusher1" "name":"message bus" "slug":"pusher" "url":"x.x.x.x" "token":"secret" "mode":"simple" "ip":"x.x.x.x" "id":"d371c59b-fbbc-4248-8f3b-7f694a62b5e1"]

	return r["token"], r["url"], err
}

func GetPreviousBuild(appID string) (string, error) {
	r := []map[string]string{}

	var params url.Values
	if strings.Contains(appID, "/") {
		appNameParts := strings.Split(appID, "/")
		if len(appNameParts) == 2 {
			params = url.Values{}
			params.Set("ci", appNameParts[0])
			appID = appNameParts[1]
		}

	}

	err := doRequest("GET", fmt.Sprintf("apps/%s/deploys", appID), params, nil, &r)
	if err != nil {
		return "", err
	}

	if len(r) > 0 {
		return r[0]["build_id"], nil
	}

	return "", nil
}

func SubmitEvent(action, message, app string, meta map[string]interface{}) error {
	params := url.Values{}
	params.Set("api_key", apiKey)

	request := struct {
		Action  string                 `json:"action"`
		App     string                 `json:"eventable_id,omitempty"`
		Meta    map[string]interface{} `json:"meta"`
		Message string                 `json:"message"`
	}{
		Action:  action,
		App:     app,
		Meta:    meta,
		Message: message,
	}

	err := doRequest("POST", "events", params, map[string]interface{}{"event": request}, nil)
	if err != nil {
		return err
	}

	return nil
}

// doRequest ...
func doRequest(method, path string, params url.Values, requestBody, responseBody interface{}) error {

	var rbodyReader io.Reader

	//
	if requestBody != nil {
		jsonBytes, err := json.Marshal(requestBody)
		if err != nil {
			return err
		}
		rbodyReader = bytes.NewBuffer(jsonBytes)
	}

	auth, _ := models.LoadAuthByEndpoint(endpoint)

	// if they have not logged in but the user name and password are both set
	// use attempt to authenticate
	if auth.Key == "" &&
		os.Getenv("NANOBOX_PASSWORD") != "" &&
		os.Getenv("NANOBOX_USERNAME") != "" &&
		path != fmt.Sprintf("users/%s/auth_token", os.Getenv("NANOBOX_USERNAME")) {

		auth.Key, _ = Auth(os.Getenv("NANOBOX_USERNAME"), os.Getenv("NANOBOX_PASSWORD"))
	}

	if params == nil {
		params = url.Values{}
	}
	if auth.Key != "" {
		params.Set("auth_token", auth.Key)
	}

	// fetch the correct url from the endpoint
	url := odinURL()

	//
	lumber.Debug("%s%s?%s\n", url, path, params.Encode())
	req, err := http.NewRequest(method, fmt.Sprintf("%s%s?%s", url, path, params.Encode()), rbodyReader)
	if err != nil {
		return err
	}

	req.Header.Set("Content-Type", "application/json")
	lumber.Trace("REQ: %s %s %s", req.Method, req.URL, req.Proto)

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}

	lumber.Debug("RES: %d %s %s %s (%s)", res.StatusCode, req.Method, req.URL, req.Proto, res.Header.Get("Content-Length"))

	// print the body even if status is not 2XX
	b, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return err
	}

	if res.StatusCode == 401 {
		err = util.ErrorfQuiet("[USER] Unauthorized (%s)", b)
		if err != nil {
			if err2, ok := err.(util.Err); ok {
				err2.Suggest = "It appears you are not logged in, run `nanobox login` and try again"
				return err2
			}
		}
		return err
	}

	if res.StatusCode == 404 {
		err = util.ErrorfQuiet("[USER] Not Found (%s)", b)
		if err != nil {
			if err2, ok := err.(util.Err); ok {
				err2.Suggest = "It appears that app or component wasn't found, check the team/app name and try again"
				return err2
			}
		}
		return err
	}

	// if it is a 400 but not
	if res.StatusCode >= 400 && res.StatusCode < 500 {
		rb := map[string]string{}
		err = json.Unmarshal(b, &rb)
		if err != nil {
			return util.ErrorfQuiet("[NANOBOX] %s", b)
		}

		errorMessage, ok := rb["error"]
		if !ok {
			return util.ErrorfQuiet("[ODIN] %s", b)
		}
		return util.ErrorfQuiet("[ODIN] %s", errorMessage)
	}

	if res.StatusCode == 500 {
		return util.ErrorfQuiet("[ODIN] Internal Server Error (%s)", b)
	}

	if res.StatusCode < 200 || res.StatusCode >= 300 {
		return util.ErrorfQuiet("[ODIN] bad exit response(%d %s %s %s (%s) %s)", res.StatusCode, req.Method, req.URL, req.Proto, res.Header.Get("Content-Length"), b)
	}

	if responseBody != nil {
		lumber.Debug("response body: '%s'\n", b)
		err = json.Unmarshal(b, responseBody)
		if err != nil {
			return err
		}
	}

	return nil
}

func odinURL() string {
	switch endpoint {
	case "bonesalt":
		return BONESALT
	case "dev":
		return DEV
	case "sim":
		return SIM
	default:
		return NANOBOX
	}
}
<|endoftext|>


<commit_before>package push

import (
	"context"
	"encoding/json"
	"fmt"
	"sync"
	"time"

	"github.com/containerd/containerd/content"
	"github.com/containerd/containerd/images"
	"github.com/containerd/containerd/remotes"
	"github.com/containerd/containerd/remotes/docker"
	"github.com/docker/distribution/reference"
	"github.com/moby/buildkit/session"
	"github.com/moby/buildkit/session/auth"
	"github.com/moby/buildkit/util/imageutil"
	"github.com/moby/buildkit/util/progress"
	"github.com/moby/buildkit/util/tracing"
	digest "github.com/opencontainers/go-digest"
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/sirupsen/logrus"
)

func getCredentialsFunc(ctx context.Context, sm *session.Manager) func(string) (string, string, error) {
	id := session.FromContext(ctx)
	if id == "" {
		return nil
	}
	return func(host string) (string, string, error) {
		timeoutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()

		caller, err := sm.Get(timeoutCtx, id)
		if err != nil {
			return "", "", err
		}

		return auth.CredentialsFunc(context.TODO(), caller)(host)
	}
}

func Push(ctx context.Context, sm *session.Manager, cs content.Store, dgst digest.Digest, ref string, insecure bool) error {

	parsed, err := reference.ParseNormalizedNamed(ref)
	if err != nil {
		return err
	}
	ref = reference.TagNameOnly(parsed).String()

	resolver := docker.NewResolver(docker.ResolverOptions{
		Client:      tracing.DefaultClient,
		Credentials: getCredentialsFunc(ctx, sm),
		PlainHTTP:   insecure,
	})

	pusher, err := resolver.Pusher(ctx, ref)
	if err != nil {
		return err
	}

	var m sync.Mutex
	manifestStack := []ocispec.Descriptor{}

	filterHandler := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
		switch desc.MediaType {
		case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest,
			images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
			m.Lock()
			manifestStack = append(manifestStack, desc)
			m.Unlock()
			return nil, images.ErrStopHandler
		default:
			return nil, nil
		}
	})

	pushHandler := remotes.PushHandler(pusher, cs)

	handlers := append([]images.Handler{},
		childrenHandler(cs),
		filterHandler,
		pushHandler,
	)

	info, err := cs.Info(ctx, dgst)
	if err != nil {
		return err
	}

	ra, err := cs.ReaderAt(ctx, dgst)
	if err != nil {
		return err
	}

	mtype, err := imageutil.DetectManifestMediaType(ra)
	if err != nil {
		return err
	}

	layersDone := oneOffProgress(ctx, "pushing layers")
	err = images.Dispatch(ctx, images.Handlers(handlers...), ocispec.Descriptor{
		Digest:    dgst,
		Size:      info.Size,
		MediaType: mtype,
	})
	layersDone(err)
	if err != nil {
		return err
	}

	mfstDone := oneOffProgress(ctx, fmt.Sprintf("pushing manifest for %s", ref))
	for i := len(manifestStack) - 1; i >= 0; i-- {
		_, err := pushHandler(ctx, manifestStack[i])
		if err != nil {
			mfstDone(err)
			return err
		}
	}
	mfstDone(nil)
	return nil
}

func oneOffProgress(ctx context.Context, id string) func(err error) error {
	pw, _, _ := progress.FromContext(ctx)
	now := time.Now()
	st := progress.Status{
		Started: &now,
	}
	pw.Write(id, st)
	return func(err error) error {
		// TODO: set error on status
		now := time.Now()
		st.Completed = &now
		pw.Write(id, st)
		pw.Close()
		return err
	}
}

func childrenHandler(provider content.Provider) images.HandlerFunc {
	return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
		var descs []ocispec.Descriptor
		switch desc.MediaType {
		case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
			p, err := content.ReadBlob(ctx, provider, desc.Digest)
			if err != nil {
				return nil, err
			}

			// TODO(stevvooe): We just assume oci manifest, for now. There may be
			// subtle differences from the docker version.
			var manifest ocispec.Manifest
			if err := json.Unmarshal(p, &manifest); err != nil {
				return nil, err
			}

			descs = append(descs, manifest.Config)
			descs = append(descs, manifest.Layers...)
		case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
			p, err := content.ReadBlob(ctx, provider, desc.Digest)
			if err != nil {
				return nil, err
			}

			var index ocispec.Index
			if err := json.Unmarshal(p, &index); err != nil {
				return nil, err
			}

			for _, m := range index.Manifests {
				if m.Digest != "" {
					descs = append(descs, m)
				}
			}
		case images.MediaTypeDockerSchema2Layer, images.MediaTypeDockerSchema2LayerGzip,
			images.MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig,
			ocispec.MediaTypeImageLayer, ocispec.MediaTypeImageLayerGzip:
			// childless data types.
			return nil, nil
		default:
			logrus.Warnf("encountered unknown type %v; children may not be fetched", desc.MediaType)
		}

		return descs, nil
	}
}
<commit_msg>util: make push depend on provider<commit_after>package push

import (
	"context"
	"encoding/json"
	"fmt"
	"sync"
	"time"

	"github.com/containerd/containerd/content"
	"github.com/containerd/containerd/images"
	"github.com/containerd/containerd/remotes"
	"github.com/containerd/containerd/remotes/docker"
	"github.com/docker/distribution/reference"
	"github.com/moby/buildkit/session"
	"github.com/moby/buildkit/session/auth"
	"github.com/moby/buildkit/util/imageutil"
	"github.com/moby/buildkit/util/progress"
	"github.com/moby/buildkit/util/tracing"
	digest "github.com/opencontainers/go-digest"
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/sirupsen/logrus"
)

func getCredentialsFunc(ctx context.Context, sm *session.Manager) func(string) (string, string, error) {
	id := session.FromContext(ctx)
	if id == "" {
		return nil
	}
	return func(host string) (string, string, error) {
		timeoutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()

		caller, err := sm.Get(timeoutCtx, id)
		if err != nil {
			return "", "", err
		}

		return auth.CredentialsFunc(context.TODO(), caller)(host)
	}
}

func Push(ctx context.Context, sm *session.Manager, cs content.Provider, dgst digest.Digest, ref string, insecure bool) error {

	parsed, err := reference.ParseNormalizedNamed(ref)
	if err != nil {
		return err
	}
	ref = reference.TagNameOnly(parsed).String()

	resolver := docker.NewResolver(docker.ResolverOptions{
		Client:      tracing.DefaultClient,
		Credentials: getCredentialsFunc(ctx, sm),
		PlainHTTP:   insecure,
	})

	pusher, err := resolver.Pusher(ctx, ref)
	if err != nil {
		return err
	}

	var m sync.Mutex
	manifestStack := []ocispec.Descriptor{}

	filterHandler := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
		switch desc.MediaType {
		case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest,
			images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
			m.Lock()
			manifestStack = append(manifestStack, desc)
			m.Unlock()
			return nil, images.ErrStopHandler
		default:
			return nil, nil
		}
	})

	pushHandler := remotes.PushHandler(pusher, cs)

	handlers := append([]images.Handler{},
		childrenHandler(cs),
		filterHandler,
		pushHandler,
	)

	ra, err := cs.ReaderAt(ctx, dgst)
	if err != nil {
		return err
	}

	mtype, err := imageutil.DetectManifestMediaType(ra)
	if err != nil {
		return err
	}

	layersDone := oneOffProgress(ctx, "pushing layers")
	err = images.Dispatch(ctx, images.Handlers(handlers...), ocispec.Descriptor{
		Digest:    dgst,
		Size:      ra.Size(),
		MediaType: mtype,
	})
	layersDone(err)
	if err != nil {
		return err
	}

	mfstDone := oneOffProgress(ctx, fmt.Sprintf("pushing manifest for %s", ref))
	for i := len(manifestStack) - 1; i >= 0; i-- {
		_, err := pushHandler(ctx, manifestStack[i])
		if err != nil {
			mfstDone(err)
			return err
		}
	}
	mfstDone(nil)
	return nil
}

func oneOffProgress(ctx context.Context, id string) func(err error) error {
	pw, _, _ := progress.FromContext(ctx)
	now := time.Now()
	st := progress.Status{
		Started: &now,
	}
	pw.Write(id, st)
	return func(err error) error {
		// TODO: set error on status
		now := time.Now()
		st.Completed = &now
		pw.Write(id, st)
		pw.Close()
		return err
	}
}

func childrenHandler(provider content.Provider) images.HandlerFunc {
	return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
		var descs []ocispec.Descriptor
		switch desc.MediaType {
		case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
			p, err := content.ReadBlob(ctx, provider, desc.Digest)
			if err != nil {
				return nil, err
			}

			// TODO(stevvooe): We just assume oci manifest, for now. There may be
			// subtle differences from the docker version.
			var manifest ocispec.Manifest
			if err := json.Unmarshal(p, &manifest); err != nil {
				return nil, err
			}

			descs = append(descs, manifest.Config)
			descs = append(descs, manifest.Layers...)
		case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
			p, err := content.ReadBlob(ctx, provider, desc.Digest)
			if err != nil {
				return nil, err
			}

			var index ocispec.Index
			if err := json.Unmarshal(p, &index); err != nil {
				return nil, err
			}

			for _, m := range index.Manifests {
				if m.Digest != "" {
					descs = append(descs, m)
				}
			}
		case images.MediaTypeDockerSchema2Layer, images.MediaTypeDockerSchema2LayerGzip,
			images.MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig,
			ocispec.MediaTypeImageLayer, ocispec.MediaTypeImageLayerGzip:
			// childless data types.
			return nil, nil
		default:
			logrus.Warnf("encountered unknown type %v; children may not be fetched", desc.MediaType)
		}

		return descs, nil
	}
}
<|endoftext|>


<commit_before>package utils

import "time"

// InTimeSpan check time between start and end
// return boolean////
func InTimeSpan(start, end, check time.Time) bool {
	return check.After(start) && check.Before(end)
}
<commit_msg>Update datetime.go<commit_after>package utils

import "time"

// InTimeSpan check time between start and end
// return boolean
func InTimeSpan(start, end, check time.Time) bool {
	return check.After(start) && check.Before(end)
}
<|endoftext|>


<commit_before>package utils

import (
	"compress/bzip2"
	"compress/gzip"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

// Downloader is parallel HTTP fetcher
type Downloader interface {
	Download(url string, destination string, result chan<- error)
	DownloadWithChecksum(url string, destination string, result chan<- error, expected ChecksumInfo, ignoreMismatch bool)
	Pause()
	Resume()
	Shutdown()
	GetProgress() *Progress
}

// Check interface
var (
	_ Downloader = &downloaderImpl{}
)

// downloaderImpl is implementation of Downloader interface
type downloaderImpl struct {
	queue    chan *downloadTask
	stop     chan bool
	stopped  chan bool
	pause    chan bool
	unpause  chan bool
	progress *Progress
	threads  int
}

// downloadTask represents single item in queue
type downloadTask struct {
	url            string
	destination    string
	result         chan<- error
	expected       ChecksumInfo
	ignoreMismatch bool
}

// NewDownloader creates new instance of Downloader which specified number
// of threads
func NewDownloader(threads int) Downloader {
	downloader := &downloaderImpl{
		queue:    make(chan *downloadTask, 1000),
		stop:     make(chan bool),
		stopped:  make(chan bool),
		pause:    make(chan bool),
		unpause:  make(chan bool),
		threads:  threads,
		progress: NewProgress(),
	}

	downloader.progress.Start()

	for i := 0; i < downloader.threads; i++ {
		go downloader.process()
	}

	return downloader
}

// Shutdown stops downloader after current tasks are finished,
// but doesn't process rest of queue
func (downloader *downloaderImpl) Shutdown() {
	for i := 0; i < downloader.threads; i++ {
		downloader.stop <- true
	}

	for i := 0; i < downloader.threads; i++ {
		<-downloader.stopped
	}

	downloader.progress.Shutdown()
}

// Pause pauses task processing
func (downloader *downloaderImpl) Pause() {
	for i := 0; i < downloader.threads; i++ {
		downloader.pause <- true
	}
}

// Resume resumes task processing
func (downloader *downloaderImpl) Resume() {
	for i := 0; i < downloader.threads; i++ {
		downloader.unpause <- true
	}
}

// Resume resumes task processing
func (downloader *downloaderImpl) GetProgress() *Progress {
	return downloader.progress
}

// Download starts new download task
func (downloader *downloaderImpl) Download(url string, destination string, result chan<- error) {
	downloader.DownloadWithChecksum(url, destination, result, ChecksumInfo{Size: -1}, false)
}

// DownloadWithChecksum starts new download task with checksum verification
func (downloader *downloaderImpl) DownloadWithChecksum(url string, destination string, result chan<- error,
	expected ChecksumInfo, ignoreMismatch bool) {
	downloader.queue <- &downloadTask{url: url, destination: destination, result: result, expected: expected, ignoreMismatch: ignoreMismatch}
}

// handleTask processes single download task
func (downloader *downloaderImpl) handleTask(task *downloadTask) {
	downloader.progress.Printf("Downloading %s...\n", task.url)

	resp, err := http.Get(task.url)
	if err != nil {
		task.result <- err
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		task.result <- fmt.Errorf("HTTP code %d while fetching %s", resp.StatusCode, task.url)
		return
	}

	err = os.MkdirAll(filepath.Dir(task.destination), 0755)
	if err != nil {
		task.result <- err
		return
	}

	temppath := task.destination + ".down"

	outfile, err := os.Create(temppath)
	if err != nil {
		task.result <- err
		return
	}
	defer outfile.Close()

	checksummer := NewChecksumWriter()
	writers := []io.Writer{outfile}

	if task.expected.Size != -1 {
		writers = append(writers, checksummer)
	}

	if downloader.progress != nil {
		writers = append(writers, downloader.progress)
	}

	w := io.MultiWriter(writers...)

	_, err = io.Copy(w, resp.Body)
	if err != nil {
		os.Remove(temppath)
		task.result <- err
		return
	}

	if task.expected.Size != -1 {
		actual := checksummer.Sum()

		if actual.Size != task.expected.Size {
			err = fmt.Errorf("%s: size check mismatch %d != %d", task.url, actual.Size, task.expected.Size)
		} else if task.expected.MD5 != "" && actual.MD5 != task.expected.MD5 {
			err = fmt.Errorf("%s: md5 hash mismatch %#v != %#v", task.url, actual.MD5, task.expected.MD5)
		} else if task.expected.SHA1 != "" && actual.SHA1 != task.expected.SHA1 {
			err = fmt.Errorf("%s: sha1 hash mismatch %#v != %#v", task.url, actual.SHA1, task.expected.SHA1)
		} else if task.expected.SHA256 != "" && actual.SHA256 != task.expected.SHA256 {
			err = fmt.Errorf("%s: sha256 hash mismatch %#v != %#v", task.url, actual.SHA256, task.expected.SHA256)
		}

		if err != nil {
			if task.ignoreMismatch {
				downloader.progress.Printf("WARNING: %s\n", err.Error())
			} else {
				os.Remove(temppath)
				task.result <- err
				return
			}
		}
	}

	err = os.Rename(temppath, task.destination)
	if err != nil {
		os.Remove(temppath)
		task.result <- err
		return
	}

	task.result <- nil
}

// process implements download thread in goroutine
func (downloader *downloaderImpl) process() {
	for {
		select {
		case <-downloader.stop:
			downloader.stopped <- true
			return
		case <-downloader.pause:
			<-downloader.unpause
		case task := <-downloader.queue:
			downloader.handleTask(task)
		}
	}
}

// DownloadTemp starts new download to temporary file and returns File
//
// Temporary file would be already removed, so no need to cleanup
func DownloadTemp(downloader Downloader, url string) (*os.File, error) {
	return DownloadTempWithChecksum(downloader, url, ChecksumInfo{Size: -1}, false)
}

// DownloadTempWithChecksum is a DownloadTemp with checksum verification
//
// Temporary file would be already removed, so no need to cleanup
func DownloadTempWithChecksum(downloader Downloader, url string, expected ChecksumInfo, ignoreMismatch bool) (*os.File, error) {
	tempdir, err := ioutil.TempDir(os.TempDir(), "aptly")
	if err != nil {
		return nil, err
	}
	defer os.RemoveAll(tempdir)

	tempfile := filepath.Join(tempdir, "buffer")

	ch := make(chan error, 1)
	downloader.DownloadWithChecksum(url, tempfile, ch, expected, ignoreMismatch)

	err = <-ch
	if err != nil {
		return nil, err
	}

	file, err := os.Open(tempfile)
	if err != nil {
		return nil, err
	}

	return file, nil
}

// List of extensions + corresponding uncompression support
var compressionMethods = []struct {
	extenstion     string
	transformation func(io.Reader) (io.Reader, error)
}{
	{
		extenstion:     ".bz2",
		transformation: func(r io.Reader) (io.Reader, error) { return bzip2.NewReader(r), nil },
	},
	{
		extenstion:     ".gz",
		transformation: func(r io.Reader) (io.Reader, error) { return gzip.NewReader(r) },
	},
	{
		extenstion:     "",
		transformation: func(r io.Reader) (io.Reader, error) { return r, nil },
	},
}

// DownloadTryCompression tries to download from URL .bz2, .gz and raw extension until
// it finds existing file.
func DownloadTryCompression(downloader Downloader, url string, expectedChecksums map[string]ChecksumInfo, ignoreMismatch bool) (io.Reader, *os.File, error) {
	var err error

	for _, method := range compressionMethods {
		var file *os.File

		tryUrl := url + method.extenstion
		foundChecksum := false

		for suffix, expected := range expectedChecksums {
			if strings.HasSuffix(tryUrl, suffix) {
				file, err = DownloadTempWithChecksum(downloader, tryUrl, expected, ignoreMismatch)
				foundChecksum = true
				break
			}
		}

		if !foundChecksum {
			file, err = DownloadTemp(downloader, tryUrl)
		}

		if err != nil {
			continue
		}

		var uncompressed io.Reader
		uncompressed, err = method.transformation(file)
		if err != nil {
			continue
		}

		return uncompressed, file, err
	}
	return nil, nil, err
}
<commit_msg>Style fix.<commit_after>package utils

import (
	"compress/bzip2"
	"compress/gzip"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

// Downloader is parallel HTTP fetcher
type Downloader interface {
	Download(url string, destination string, result chan<- error)
	DownloadWithChecksum(url string, destination string, result chan<- error, expected ChecksumInfo, ignoreMismatch bool)
	Pause()
	Resume()
	Shutdown()
	GetProgress() *Progress
}

// Check interface
var (
	_ Downloader = &downloaderImpl{}
)

// downloaderImpl is implementation of Downloader interface
type downloaderImpl struct {
	queue    chan *downloadTask
	stop     chan bool
	stopped  chan bool
	pause    chan bool
	unpause  chan bool
	progress *Progress
	threads  int
}

// downloadTask represents single item in queue
type downloadTask struct {
	url            string
	destination    string
	result         chan<- error
	expected       ChecksumInfo
	ignoreMismatch bool
}

// NewDownloader creates new instance of Downloader which specified number
// of threads
func NewDownloader(threads int) Downloader {
	downloader := &downloaderImpl{
		queue:    make(chan *downloadTask, 1000),
		stop:     make(chan bool),
		stopped:  make(chan bool),
		pause:    make(chan bool),
		unpause:  make(chan bool),
		threads:  threads,
		progress: NewProgress(),
	}

	downloader.progress.Start()

	for i := 0; i < downloader.threads; i++ {
		go downloader.process()
	}

	return downloader
}

// Shutdown stops downloader after current tasks are finished,
// but doesn't process rest of queue
func (downloader *downloaderImpl) Shutdown() {
	for i := 0; i < downloader.threads; i++ {
		downloader.stop <- true
	}

	for i := 0; i < downloader.threads; i++ {
		<-downloader.stopped
	}

	downloader.progress.Shutdown()
}

// Pause pauses task processing
func (downloader *downloaderImpl) Pause() {
	for i := 0; i < downloader.threads; i++ {
		downloader.pause <- true
	}
}

// Resume resumes task processing
func (downloader *downloaderImpl) Resume() {
	for i := 0; i < downloader.threads; i++ {
		downloader.unpause <- true
	}
}

// Resume resumes task processing
func (downloader *downloaderImpl) GetProgress() *Progress {
	return downloader.progress
}

// Download starts new download task
func (downloader *downloaderImpl) Download(url string, destination string, result chan<- error) {
	downloader.DownloadWithChecksum(url, destination, result, ChecksumInfo{Size: -1}, false)
}

// DownloadWithChecksum starts new download task with checksum verification
func (downloader *downloaderImpl) DownloadWithChecksum(url string, destination string, result chan<- error,
	expected ChecksumInfo, ignoreMismatch bool) {
	downloader.queue <- &downloadTask{url: url, destination: destination, result: result, expected: expected, ignoreMismatch: ignoreMismatch}
}

// handleTask processes single download task
func (downloader *downloaderImpl) handleTask(task *downloadTask) {
	downloader.progress.Printf("Downloading %s...\n", task.url)

	resp, err := http.Get(task.url)
	if err != nil {
		task.result <- err
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		task.result <- fmt.Errorf("HTTP code %d while fetching %s", resp.StatusCode, task.url)
		return
	}

	err = os.MkdirAll(filepath.Dir(task.destination), 0755)
	if err != nil {
		task.result <- err
		return
	}

	temppath := task.destination + ".down"

	outfile, err := os.Create(temppath)
	if err != nil {
		task.result <- err
		return
	}
	defer outfile.Close()

	checksummer := NewChecksumWriter()
	writers := []io.Writer{outfile}

	if task.expected.Size != -1 {
		writers = append(writers, checksummer)
	}

	if downloader.progress != nil {
		writers = append(writers, downloader.progress)
	}

	w := io.MultiWriter(writers...)

	_, err = io.Copy(w, resp.Body)
	if err != nil {
		os.Remove(temppath)
		task.result <- err
		return
	}

	if task.expected.Size != -1 {
		actual := checksummer.Sum()

		if actual.Size != task.expected.Size {
			err = fmt.Errorf("%s: size check mismatch %d != %d", task.url, actual.Size, task.expected.Size)
		} else if task.expected.MD5 != "" && actual.MD5 != task.expected.MD5 {
			err = fmt.Errorf("%s: md5 hash mismatch %#v != %#v", task.url, actual.MD5, task.expected.MD5)
		} else if task.expected.SHA1 != "" && actual.SHA1 != task.expected.SHA1 {
			err = fmt.Errorf("%s: sha1 hash mismatch %#v != %#v", task.url, actual.SHA1, task.expected.SHA1)
		} else if task.expected.SHA256 != "" && actual.SHA256 != task.expected.SHA256 {
			err = fmt.Errorf("%s: sha256 hash mismatch %#v != %#v", task.url, actual.SHA256, task.expected.SHA256)
		}

		if err != nil {
			if task.ignoreMismatch {
				downloader.progress.Printf("WARNING: %s\n", err.Error())
			} else {
				os.Remove(temppath)
				task.result <- err
				return
			}
		}
	}

	err = os.Rename(temppath, task.destination)
	if err != nil {
		os.Remove(temppath)
		task.result <- err
		return
	}

	task.result <- nil
}

// process implements download thread in goroutine
func (downloader *downloaderImpl) process() {
	for {
		select {
		case <-downloader.stop:
			downloader.stopped <- true
			return
		case <-downloader.pause:
			<-downloader.unpause
		case task := <-downloader.queue:
			downloader.handleTask(task)
		}
	}
}

// DownloadTemp starts new download to temporary file and returns File
//
// Temporary file would be already removed, so no need to cleanup
func DownloadTemp(downloader Downloader, url string) (*os.File, error) {
	return DownloadTempWithChecksum(downloader, url, ChecksumInfo{Size: -1}, false)
}

// DownloadTempWithChecksum is a DownloadTemp with checksum verification
//
// Temporary file would be already removed, so no need to cleanup
func DownloadTempWithChecksum(downloader Downloader, url string, expected ChecksumInfo, ignoreMismatch bool) (*os.File, error) {
	tempdir, err := ioutil.TempDir(os.TempDir(), "aptly")
	if err != nil {
		return nil, err
	}
	defer os.RemoveAll(tempdir)

	tempfile := filepath.Join(tempdir, "buffer")

	ch := make(chan error, 1)
	downloader.DownloadWithChecksum(url, tempfile, ch, expected, ignoreMismatch)

	err = <-ch
	if err != nil {
		return nil, err
	}

	file, err := os.Open(tempfile)
	if err != nil {
		return nil, err
	}

	return file, nil
}

// List of extensions + corresponding uncompression support
var compressionMethods = []struct {
	extenstion     string
	transformation func(io.Reader) (io.Reader, error)
}{
	{
		extenstion:     ".bz2",
		transformation: func(r io.Reader) (io.Reader, error) { return bzip2.NewReader(r), nil },
	},
	{
		extenstion:     ".gz",
		transformation: func(r io.Reader) (io.Reader, error) { return gzip.NewReader(r) },
	},
	{
		extenstion:     "",
		transformation: func(r io.Reader) (io.Reader, error) { return r, nil },
	},
}

// DownloadTryCompression tries to download from URL .bz2, .gz and raw extension until
// it finds existing file.
func DownloadTryCompression(downloader Downloader, url string, expectedChecksums map[string]ChecksumInfo, ignoreMismatch bool) (io.Reader, *os.File, error) {
	var err error

	for _, method := range compressionMethods {
		var file *os.File

		tryURL := url + method.extenstion
		foundChecksum := false

		for suffix, expected := range expectedChecksums {
			if strings.HasSuffix(tryURL, suffix) {
				file, err = DownloadTempWithChecksum(downloader, tryURL, expected, ignoreMismatch)
				foundChecksum = true
				break
			}
		}

		if !foundChecksum {
			file, err = DownloadTemp(downloader, tryURL)
		}

		if err != nil {
			continue
		}

		var uncompressed io.Reader
		uncompressed, err = method.transformation(file)
		if err != nil {
			continue
		}

		return uncompressed, file, err
	}
	return nil, nil, err
}
<|endoftext|>


<commit_before>package tutum

import "encoding/json"

type SListResponse struct {
	Objects []Service `json: "objects"`
}

type Service struct {
	Autodestroy            string       `json:"autodestroy"`
	Autoredeploy           bool         `json:"autoredeploy"`
	Autorestart            string       `json:"autorestart"`
	Containers             []string     `json:"containers"`
	Container_ports        []SCPInfo    `json:"container_ports"`
	Container_size         string       `json:"container_size"`
	Current_num_containers int          `json:"current_num_containers"`
	Deployed_datetime      string       `json:"deployed_datetime"`
	Destroyed_datetime     string       `json:"destroyed_datetime"`
	Entrypoint             string       `json:"entrypoint"`
	Exit_code              int          `json:"exit_code"`
	Exit_code_message      string       `json:"exit_code_message"`
	Image_name             string       `json:"image_name"`
	Image_tag              string       `json:"image_tag"`
	Linked_to_service      []LinkToInfo `json:"linked_to_service"`
	Name                   string       `json:"name"`
	Public_dns             string       `json:"public_dns"`
	Resource_uri           string       `json:"resource_uri"`
	Run_command            string       `json:"run_command"`
	Started_datetime       string       `json:"started_datetime"`
	State                  string       `json:"state"`
	Stack                  string       `json:"stack"`
	Stopped_datetime       string       `json:"stopped_datetime"`
	Target_num_containers  int          `json:"target_num_containers"`
	Unique_name            string       `json:"unique_name"`
	Uuid                   string       `json:"uuid"`
}

type SCPInfo struct {
	Container  string `json:"container"`
	Inner_port int    `json:"inner_port"`
	Outer_port int    `json:"outer_port"`
	Protocol   string `json:"protocol"`
}

type LinkToInfo struct {
	From_service string `json:"from_service"`
	Name         string `json:"name"`
	To_service   string `json:"to_service"`
}

type BasicService struct {
	Image                 string `json:"image"`
	Name                  string `json:"name"`
	Target_num_containers int    `json:"target_num_containers"`
}

func ListServices() ([]Service, error) {
	url := "service/"
	request := "GET"
	body := []byte(`{}`)
	var response SListResponse

	data, err := TutumCall(url, request, body)
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(data, &response)
	if err != nil {
		return nil, err
	}
	return response.Objects, nil

}

func GetService(uuid string) (Service, error) {

	url := "service/" + uuid + "/"
	request := "GET"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil

}

func CreateService(newService []byte) (Service, error) {

	url := "service/"
	request := "POST"

	var response Service

	data, err := TutumCall(url, request, newService)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil

}

func GetServiceLogs(uuid string) (string, error) {

	url := "service/" + uuid + "/logs/"
	request := "GET"
	body := []byte(`{}`)

	data, err := TutumCall(url, request, body)
	if err != nil {
		return "", err
	}

	s := string(data)

	return s, nil

}

func UpdateService(uuid string, update []byte) (Service, error) {

	url := "service/" + uuid + "/"
	request := "PATCH"
	var response Service

	data, err := TutumCall(url, request, update)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil
}

func StartService(uuid string) (Service, error) {

	url := "service/" + uuid + "/start/"
	request := "POST"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil
}

func StopService(uuid string) (Service, error) {

	url := "service/" + uuid + "/stop/"
	request := "POST"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil
}

func RedeployService(uuid string) (Service, error) {

	url := "service/" + uuid + "/redeploy/"
	request := "POST"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil

}

func TerminateService(uuid string) (Service, error) {
	url := "service/" + uuid + "/"
	request := "DELETE"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil

}
<commit_msg>remove useless struct<commit_after>package tutum

import "encoding/json"

type SListResponse struct {
	Objects []Service `json: "objects"`
}

type Service struct {
	Autodestroy            string       `json:"autodestroy"`
	Autoredeploy           bool         `json:"autoredeploy"`
	Autorestart            string       `json:"autorestart"`
	Containers             []string     `json:"containers"`
	Container_ports        []SCPInfo    `json:"container_ports"`
	Container_size         string       `json:"container_size"`
	Current_num_containers int          `json:"current_num_containers"`
	Deployed_datetime      string       `json:"deployed_datetime"`
	Destroyed_datetime     string       `json:"destroyed_datetime"`
	Entrypoint             string       `json:"entrypoint"`
	Exit_code              int          `json:"exit_code"`
	Exit_code_message      string       `json:"exit_code_message"`
	Image_name             string       `json:"image_name"`
	Image_tag              string       `json:"image_tag"`
	Linked_to_service      []LinkToInfo `json:"linked_to_service"`
	Name                   string       `json:"name"`
	Public_dns             string       `json:"public_dns"`
	Resource_uri           string       `json:"resource_uri"`
	Run_command            string       `json:"run_command"`
	Started_datetime       string       `json:"started_datetime"`
	State                  string       `json:"state"`
	Stack                  string       `json:"stack"`
	Stopped_datetime       string       `json:"stopped_datetime"`
	Target_num_containers  int          `json:"target_num_containers"`
	Unique_name            string       `json:"unique_name"`
	Uuid                   string       `json:"uuid"`
}

type SCPInfo struct {
	Container  string `json:"container"`
	Inner_port int    `json:"inner_port"`
	Outer_port int    `json:"outer_port"`
	Protocol   string `json:"protocol"`
}

type LinkToInfo struct {
	From_service string `json:"from_service"`
	Name         string `json:"name"`
	To_service   string `json:"to_service"`
}

func ListServices() ([]Service, error) {
	url := "service/"
	request := "GET"
	body := []byte(`{}`)
	var response SListResponse

	data, err := TutumCall(url, request, body)
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(data, &response)
	if err != nil {
		return nil, err
	}
	return response.Objects, nil

}

func GetService(uuid string) (Service, error) {

	url := "service/" + uuid + "/"
	request := "GET"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil

}

func CreateService(newService []byte) (Service, error) {

	url := "service/"
	request := "POST"

	var response Service

	data, err := TutumCall(url, request, newService)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil

}

func GetServiceLogs(uuid string) (string, error) {

	url := "service/" + uuid + "/logs/"
	request := "GET"
	body := []byte(`{}`)

	data, err := TutumCall(url, request, body)
	if err != nil {
		return "", err
	}

	s := string(data)

	return s, nil

}

func UpdateService(uuid string, update []byte) (Service, error) {

	url := "service/" + uuid + "/"
	request := "PATCH"
	var response Service

	data, err := TutumCall(url, request, update)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil
}

func StartService(uuid string) (Service, error) {

	url := "service/" + uuid + "/start/"
	request := "POST"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil
}

func StopService(uuid string) (Service, error) {

	url := "service/" + uuid + "/stop/"
	request := "POST"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil
}

func RedeployService(uuid string) (Service, error) {

	url := "service/" + uuid + "/redeploy/"
	request := "POST"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil

}

func TerminateService(uuid string) (Service, error) {
	url := "service/" + uuid + "/"
	request := "DELETE"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil

}
<|endoftext|>


<commit_before>package consistence

import (
	"errors"
	"strconv"
)

var (
	ErrLeaderSessionAlreadyExist = errors.New("leader session already exist")
)

type NsqdNodeInfo struct {
	ID      string
	NodeIp  string
	TcpPort string
	RpcPort string
}

func (self *NsqdNodeInfo) GetID() string {
	return self.ID
}

type NsqLookupdNodeInfo struct {
	ID       string
	NodeIp   string
	HttpPort string
	TcpPort  string
	RpcPort  string
	Epoch    int
}

func (self *NsqLookupdNodeInfo) GetID() string {
	return self.ID
}

// topicLeaderEpoch increase while leader changed.
// topicEpoch increase while leader or isr changed. (seems not really needed?)
// lookupEpoch increase while lookup leader changed.

type TopicPartionMetaInfo struct {
	Name        string
	Partition   int
	Channels    []string
	Leader      string
	ISR         []string
	CatchupList []string
	Epoch       int
	Replica     int
	// the suggest load factor for each topic partition.
	SuggestLF int
}

func (self *TopicPartionMetaInfo) GetTopicDesp() string {
	return self.Name + "-" + strconv.Itoa(self.Partition)
}

type TopicLeaderSession struct {
	ClusterID   string
	Topic       string
	Partition   int
	LeaderNode  *NsqdNodeInfo
	Session     string
	LeaderEpoch int
}

func (self *TopicLeaderSession) IsSame(other *TopicLeaderSession) bool {
	if other == nil || self == nil {
		return false
	}
	if self.LeaderEpoch != other.LeaderEpoch {
		return false
	}
	if self.Session != "" && other.Session != "" && self.Session != other.Session {
		return false
	}
	if self.LeaderNode == nil || other.LeaderNode == nil {
		return false
	}
	if self.LeaderNode.GetID() != other.LeaderNode.GetID() {
		return false
	}
	return true
}

type ConsistentStore interface {
	WriteKey(key, value string) error
	ReadKey(key string) (string, error)
	ListKey(key string) ([]string, error)
}

// We need check leader lock session before do any modify to etcd.
// Make sure all returned value should be copied to avoid modify by outside.
type NSQLookupdLeadership interface {
	InitClusterID(id string)
	Register(value *NsqLookupdNodeInfo) error
	Unregister() error
	Stop()
	GetAllLookupdNodes() ([]NsqLookupdNodeInfo, error) // add
	AcquireAndWatchLeader(leader chan *NsqLookupdNodeInfo, stop chan struct{})
	CheckIfLeader(session string) bool
	UpdateLookupEpoch(key string, oldGen int) (int, error)
	WatchNsqdNodes(nsqds chan []*NsqdNodeInfo, stop chan struct{}) // update
	ScanTopics() ([]*TopicPartionMetaInfo, error) // update
	GetTopicInfo(topic string, partition int) (*TopicPartionMetaInfo, error)
	CreateTopicPartition(topic string, partition int) error
	CreateTopic(topic string, partitionNum int, replica int) error
	IsExistTopic(topic string) (bool, error)
	IsExistTopicPartition(topic string, partitionNum int) (bool, error)
	GetTopicPartitionNum(topic string) (int, error)
	DeleteTopic(topic string, partition int) error
	// update leader, isr, epoch
	// Note: update should do check-and-set to avoid unexpected override.
	CreateTopicNodeInfo(topic string, partition int, topicInfo *TopicPartionMetaInfo) (error, int)
	UpdateTopicNodeInfo(topic string, partition int, topicInfo *TopicPartionMetaInfo, oldGen int) error
	GetTopicLeaderSession(topic string, partition int) (*TopicLeaderSession, error)
	WatchTopicLeader(topic string, partition int, leader chan *TopicLeaderSession, stop chan struct{}) error
}

type NSQDLeadership interface {
	InitClusterID(id string)
	RegisterNsqd(nodeData *NsqdNodeInfo) error // update
	UnregisterNsqd(nodeData *NsqdNodeInfo) error
	AcquireTopicLeader(topic string, partition int, nodeData *NsqdNodeInfo) error
	ReleaseTopicLeader(topic string, partition int, session *TopicLeaderSession) error
	GetAllLookupdNodes() ([]NsqLookupdNodeInfo, error) // add
	WatchLookupdLeader(key string, leader chan *NsqLookupdNodeInfo, stop chan struct{}) error
	GetTopicInfo(topic string, partition int) (*TopicPartionMetaInfo, error)
	GetTopicLeaderSession(topic string, partition int) (*TopicLeaderSession, error)
}
<commit_msg>update<commit_after>package consistence

import (
	"errors"
	"strconv"
)

var (
	ErrLeaderSessionAlreadyExist = errors.New("leader session already exist")
)

type NsqdNodeInfo struct {
	ID      string
	NodeIp  string
	TcpPort string
	RpcPort string
}

func (self *NsqdNodeInfo) GetID() string {
	return self.ID
}

type NsqLookupdNodeInfo struct {
	ID       string
	NodeIp   string
	HttpPort string
	TcpPort  string
	RpcPort  string
	Epoch    int
}

func (self *NsqLookupdNodeInfo) GetID() string {
	return self.ID
}

// topicLeaderEpoch increase while leader changed.
// topicEpoch increase while leader or isr changed. (seems not really needed?)
// lookupEpoch increase while lookup leader changed.

type TopicPartionMetaInfo struct {
	Name        string
	Partition   int
	Channels    []string
	Leader      string
	ISR         []string
	CatchupList []string
	Epoch       int
	Replica     int
	// the suggest load factor for each topic partition.
	SuggestLF int
}

func (self *TopicPartionMetaInfo) GetTopicDesp() string {
	return self.Name + "-" + strconv.Itoa(self.Partition)
}

type TopicLeaderSession struct {
	ClusterID   string
	Topic       string
	Partition   int
	LeaderNode  *NsqdNodeInfo
	Session     string
	LeaderEpoch int
}

func (self *TopicLeaderSession) IsSame(other *TopicLeaderSession) bool {
	if other == nil || self == nil {
		return false
	}
	if self.LeaderEpoch != other.LeaderEpoch {
		return false
	}
	if self.Session != "" && other.Session != "" && self.Session != other.Session {
		return false
	}
	if self.LeaderNode == nil || other.LeaderNode == nil {
		return false
	}
	if self.LeaderNode.GetID() != other.LeaderNode.GetID() {
		return false
	}
	return true
}

type ConsistentStore interface {
	WriteKey(key, value string) error
	ReadKey(key string) (string, error)
	ListKey(key string) ([]string, error)
}

// We need check leader lock session before do any modify to etcd.
// Make sure all returned value should be copied to avoid modify by outside.
type NSQLookupdLeadership interface {
	InitClusterID(id string)
	Register(value *NsqLookupdNodeInfo) error
	Unregister() error
	Stop()
	GetAllLookupdNodes() ([]NsqLookupdNodeInfo, error) // add
	AcquireAndWatchLeader(leader chan *NsqLookupdNodeInfo, stop chan struct{})
	CheckIfLeader(session string) bool
	UpdateLookupEpoch(key string, oldGen int) (int, error)
	// cluster nsqd node
	WatchNsqdNodes(nsqds chan []NsqdNodeInfo, stop chan struct{})
	ScanTopics() ([]TopicPartionMetaInfo, error)
	GetTopicInfo(topic string, partition int) (*TopicPartionMetaInfo, error)
	CreateTopicPartition(topic string, partition int) error
	CreateTopic(topic string, partitionNum int, replica int) error
	IsExistTopic(topic string) (bool, error)
	IsExistTopicPartition(topic string, partitionNum int) (bool, error)
	GetTopicPartitionNum(topic string) (int, error)
	DeleteTopic(topic string, partition int) error
	// update leader, isr, epoch
	// Note: update should do check-and-set to avoid unexpected override.
	CreateTopicNodeInfo(topic string, partition int, topicInfo *TopicPartionMetaInfo) (error, int)
	UpdateTopicNodeInfo(topic string, partition int, topicInfo *TopicPartionMetaInfo, oldGen int) error
	// get leadership information
	GetTopicLeaderSession(topic string, partition int) (*TopicLeaderSession, error)
	// watch any leadership lock change for all topic partitions, should return the token used later by release.
	WatchTopicLeader(leader chan *TopicLeaderSession, stop chan struct{}) error
}

type NSQDLeadership interface {
	InitClusterID(id string)
	RegisterNsqd(nodeData *NsqdNodeInfo) error // update
	UnregisterNsqd(nodeData *NsqdNodeInfo) error
	// get the topic leadership lock.
	AcquireTopicLeader(topic string, partition int, nodeData *NsqdNodeInfo) error
	// release the lock using the acquired session.
	ReleaseTopicLeader(topic string, partition int, session *TopicLeaderSession) error
	// all registered lookup nodes.
	GetAllLookupdNodes() ([]NsqLookupdNodeInfo, error)
	WatchLookupdLeader(key string, leader chan *NsqLookupdNodeInfo, stop chan struct{}) error
	GetTopicInfo(topic string, partition int) (*TopicPartionMetaInfo, error)
	GetTopicLeaderSession(topic string, partition int) (*TopicLeaderSession, error)
}
<|endoftext|>


<commit_before>/*
   Copyright (C) 2014  Salsita s.r.o.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program. If not, see {http://www.gnu.org/licenses/}.
*/

package jira

import (
	// Stdlib
	"fmt"
	"net/http"

	// Vendor
	"github.com/google/go-querystring/query"
)

// Resources -------------------------------------------------------------------

// IssueList represents a list of issues, what a surprise.
type IssueList struct {
	Expand     string   `json:"expand,omitempty"`
	StartAt    int      `json:"startAt,omitempty"`
	MaxResults int      `json:"maxResults,omitempty"`
	Total      int      `json:"total,omitempty"`
	Issues     []*Issue `json:"issues,omitempty"`
}

// Issue represents the issue resource as produces by the REST API.
type Issue struct {
	Id     string `json:"id,omitempty"`
	Self   string `json:"self,omitempty"`
	Key    string `json:"key,omitempty"`
	Fields struct {
		Summary   string `json:"summary,omitempty"`
		IssueType struct {
			Id          string `json:"id,omitempty"`
			Self        string `json:"self,omitempty"`
			Name        string `json:"name,omitempty"`
			Description string `json:"description,omitempty"`
			Subtask     bool   `json:"subtask,omitempty"`
			IconURL     string `json:"iconUrl,omitempty"`
		} `json:"issuetype,omitempty"`
		Parent      *Issue       `json:"parent,omitempty"`
		Subtasks    []*Issue     `json:"subtasks,omitempty"`
		Assignee    *User        `json:"assignee,omitempty"`
		FixVersions []*Version   `json:"fixVersions,omitempty"`
		Status      *IssueStatus `json:"status,omitempty"`
	} `json:"fields,omitempty"`
}

// IssueStatus represents an issue status, e.g. "Scheduled".
type IssueStatus struct {
	Id          string `json:"id,omitempty"`
	Self        string `json:"self,omitempty"`
	Name        string `json:"name,omitempty"`
	Description string `json:"description,omitempty"`
	IconURL     string `json:"iconUrl,omitempty"`
}

// The service -----------------------------------------------------------------

type IssueService struct {
	client *Client
}

func newIssueService(client *Client) *IssueService {
	return &IssueService{client}
}

type SearchOptions struct {
	JQL           string `url:"jql,omitempty"`
	StartAt       int    `url:"startAt,omitempty"`
	MaxResults    int    `url:"maxResults,omitempty"`
	ValidateQuery bool   `url:"validateQuery,omitempty"`
}

func (service *IssueService) Search(opts *SearchOptions) ([]*Issue, *http.Response, error) {
	u := "search"
	if opts != nil {
		vs, err := query.Values(opts)
		if err != nil {
			panic(err)
		}
		u += "?" + vs.Encode()
	}

	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var issueList IssueList
	resp, err := service.client.Do(req, &issueList)
	if err != nil {
		return nil, nil, err
	}

	// TODO: Deal with pagination.
	return issueList.Issues, resp, nil
}

// Get returns the chosen issue.
func (service *IssueService) Get(issueIdOrKey string) (*Issue, *http.Response, error) {
	u := fmt.Sprintf("issue/%v", issueIdOrKey)
	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var issue Issue
	resp, err := service.client.Do(req, &issue)
	if err != nil {
		return nil, nil, err
	}
	return &issue, resp, nil
}

// Update updates the chosen issue.
func (service *IssueService) Update(issueIdOrKey string, body interface{}) (*http.Response, error) {
	u := fmt.Sprintf("issue/%v", issueIdOrKey)
	req, err := service.client.NewRequest("PUT", u, body)
	if err != nil {
		return nil, err
	}
	return service.client.Do(req, nil)
}

// PerformTransition performs the requested transition for the chosen issue.
func (service *IssueService) PerformTransition(
	issueIdOrKey string,
	transitionId string,
) (*http.Response, error) {

	u := fmt.Sprintf("issue/%v/transitions", issueIdOrKey)
	p := M{
		"transition": M{
			"id": transitionId,
		},
	}

	req, err := service.client.NewRequest("POST", u, p)
	if err != nil {
		return nil, err
	}

	return service.client.Do(req, nil)
}
<commit_msg>Issue: Add Fields.Labels field<commit_after>/*
   Copyright (C) 2014  Salsita s.r.o.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program. If not, see {http://www.gnu.org/licenses/}.
*/

package jira

import (
	// Stdlib
	"fmt"
	"net/http"

	// Vendor
	"github.com/google/go-querystring/query"
)

// Resources -------------------------------------------------------------------

// IssueList represents a list of issues, what a surprise.
type IssueList struct {
	Expand     string   `json:"expand,omitempty"`
	StartAt    int      `json:"startAt,omitempty"`
	MaxResults int      `json:"maxResults,omitempty"`
	Total      int      `json:"total,omitempty"`
	Issues     []*Issue `json:"issues,omitempty"`
}

// Issue represents the issue resource as produces by the REST API.
type Issue struct {
	Id     string `json:"id,omitempty"`
	Self   string `json:"self,omitempty"`
	Key    string `json:"key,omitempty"`
	Fields struct {
		Summary   string `json:"summary,omitempty"`
		IssueType struct {
			Id          string `json:"id,omitempty"`
			Self        string `json:"self,omitempty"`
			Name        string `json:"name,omitempty"`
			Description string `json:"description,omitempty"`
			Subtask     bool   `json:"subtask,omitempty"`
			IconURL     string `json:"iconUrl,omitempty"`
		} `json:"issuetype,omitempty"`
		Parent      *Issue       `json:"parent,omitempty"`
		Subtasks    []*Issue     `json:"subtasks,omitempty"`
		Assignee    *User        `json:"assignee,omitempty"`
		FixVersions []*Version   `json:"fixVersions,omitempty"`
		Labels      []string     `json:"labels,omitempty"`
		Status      *IssueStatus `json:"status,omitempty"`
	} `json:"fields,omitempty"`
}

// IssueStatus represents an issue status, e.g. "Scheduled".
type IssueStatus struct {
	Id          string `json:"id,omitempty"`
	Self        string `json:"self,omitempty"`
	Name        string `json:"name,omitempty"`
	Description string `json:"description,omitempty"`
	IconURL     string `json:"iconUrl,omitempty"`
}

// The service -----------------------------------------------------------------

type IssueService struct {
	client *Client
}

func newIssueService(client *Client) *IssueService {
	return &IssueService{client}
}

type SearchOptions struct {
	JQL           string `url:"jql,omitempty"`
	StartAt       int    `url:"startAt,omitempty"`
	MaxResults    int    `url:"maxResults,omitempty"`
	ValidateQuery bool   `url:"validateQuery,omitempty"`
}

func (service *IssueService) Search(opts *SearchOptions) ([]*Issue, *http.Response, error) {
	u := "search"
	if opts != nil {
		vs, err := query.Values(opts)
		if err != nil {
			panic(err)
		}
		u += "?" + vs.Encode()
	}

	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var issueList IssueList
	resp, err := service.client.Do(req, &issueList)
	if err != nil {
		return nil, nil, err
	}

	// TODO: Deal with pagination.
	return issueList.Issues, resp, nil
}

// Get returns the chosen issue.
func (service *IssueService) Get(issueIdOrKey string) (*Issue, *http.Response, error) {
	u := fmt.Sprintf("issue/%v", issueIdOrKey)
	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var issue Issue
	resp, err := service.client.Do(req, &issue)
	if err != nil {
		return nil, nil, err
	}
	return &issue, resp, nil
}

// Update updates the chosen issue.
func (service *IssueService) Update(issueIdOrKey string, body interface{}) (*http.Response, error) {
	u := fmt.Sprintf("issue/%v", issueIdOrKey)
	req, err := service.client.NewRequest("PUT", u, body)
	if err != nil {
		return nil, err
	}
	return service.client.Do(req, nil)
}

// PerformTransition performs the requested transition for the chosen issue.
func (service *IssueService) PerformTransition(
	issueIdOrKey string,
	transitionId string,
) (*http.Response, error) {

	u := fmt.Sprintf("issue/%v/transitions", issueIdOrKey)
	p := M{
		"transition": M{
			"id": transitionId,
		},
	}

	req, err := service.client.NewRequest("POST", u, p)
	if err != nil {
		return nil, err
	}

	return service.client.Do(req, nil)
}
<|endoftext|>


<commit_before>package singo

import (
	"testing"

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

func TestValidatorACValid(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "01.004.823/001-12" // Valido
	validator.UF = "AC"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do estado do Acre")
	}
	assert.True(result)
}

func TestValidatorACInvalid(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "01.054.823/001-12" // Invalido
	validator.UF = "AC"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do estado do Acre")
	}
	assert.False(result)
}

func TestValidatorACInvalidSizeIsNot13Characters(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "0105482012" // Menor que 13
	validator.UF = "AC"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do estado do Acre")
	}
	assert.False(result)

	validator.IE = "0105482012123234243" // Maior que 13
	validator.UF = "AC"

	result, err = validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do estado do Acre")
	}
	assert.False(result)
}

func TestValidatorACInvalidNotStartWith01(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "0200482300112" // Não começa com 01
	validator.UF = "AC"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do estado do Acre")
	}
	assert.False(result)
}

func TestValidatorALValid(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "248186310" // Valido
	validator.UF = "AL"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do estado de Alagoas")
	}
	assert.True(result)
}

func TestValidatorALInvalid(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "248156310" // Invalido
	validator.UF = "AL"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do estado de Alagoas")
	}
	assert.False(result)
}

func TestValidatorACInvalidSizeIsNot9Characters(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "0105482" // Menor que 9
	validator.UF = "AL"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do estado de Alagoas")
	}
	assert.False(result)

	validator.IE = "0105482012123234243" // Maior que 9
	validator.UF = "AL"

	result, err = validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do estado de Alagoas")
	}
	assert.False(result)
}

func TestValidatorACInvalidNotStartWith24(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "1100482300112" // Não começa com 24
	validator.UF = "AL"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do estado de Alagoas")
	}
	assert.False(result)
}
<commit_msg>Enviando testes do estado do Amapa<commit_after>package singo

import (
	"testing"

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

func TestValidatorACValid(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "01.004.823/001-12" // Valido
	validator.UF = "AC"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do estado do Acre")
	}
	assert.True(result)
}

func TestValidatorACInvalid(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "01.054.823/001-12" // Invalido
	validator.UF = "AC"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do estado do Acre")
	}
	assert.False(result)
}

func TestValidatorACInvalidSizeIsNot13Characters(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "0105482012" // Menor que 13
	validator.UF = "AC"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do tamanho do estado do Acre")
	}
	assert.False(result)

	validator.IE = "0105482012123234243" // Maior que 13
	validator.UF = "AC"

	result, err = validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do tamanho do estado do Acre")
	}
	assert.False(result)
}

func TestValidatorACInvalidNotStartWith01(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "0200482300112" // Não começa com 01
	validator.UF = "AC"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do inicio do estado do Acre")
	}
	assert.False(result)
}

func TestValidatorALValid(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "248186310" // Valido
	validator.UF = "AL"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do estado de Alagoas")
	}
	assert.True(result)
}

func TestValidatorALInvalid(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "248156310" // Invalido
	validator.UF = "AL"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do estado de Alagoas")
	}
	assert.False(result)
}

func TestValidatorACInvalidSizeIsNot9Characters(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "0105482" // Menor que 9
	validator.UF = "AL"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do tamanho do estado de Alagoas")
	}
	assert.False(result)

	validator.IE = "0105482012123234243" // Maior que 9
	validator.UF = "AL"

	result, err = validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do tamanho do estado de Alagoas")
	}
	assert.False(result)
}

func TestValidatorACInvalidNotStartWith24(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "1100482300112" // Não começa com 24
	validator.UF = "AL"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do inicio do estado de Alagoas")
	}
	assert.False(result)
}

func TestValidatorAPValid(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "032277393" // Valido
	validator.UF = "AP"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do estado do Amapá")
	}
	assert.True(result)

	validator.IE = "038126273" // Valido
	validator.UF = "AP"

	result, err = validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do estado do Amapá")
	}
	assert.True(result)

	validator.IE = "030141163" // Valido
	validator.UF = "AP"

	result, err = validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do estado do Amapá")
	}
	assert.True(result)
}

func TestValidatorAPInvalid(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "032278393" // Invalido
	validator.UF = "AP"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do estado do Amapá")
	}
	assert.False(result)
}

func TestValidatorAPInvalidSizeIsNot9Characters(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "0105482" // Menor que 9
	validator.UF = "AP"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do tamanho estado do Amapá")
	}
	assert.False(result)

	validator.IE = "0105482012123234243" // Maior que 9
	validator.UF = "AP"

	result, err = validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do tamanho estado do Amapá")
	}
	assert.False(result)
}

func TestValidatorAPInvalidNotStartWith03(t *testing.T) {

	assert := assert.New(t)
	validator := NewIEValidator()

	validator.IE = "1100482300112" // Não começa com 03
	validator.UF = "AP"

	result, err := validator.Validate()
	if err != nil {
		t.Error("Erro na validacao do inicio do estado do Amapá")
	}
	assert.False(result)
}
<|endoftext|>


<commit_before>// Package tracer implements a Dapper-style tracing system. It is
// compatible with the Open Tracing specification.
package tracer

import (
	"crypto/rand"
	"encoding/binary"
	"encoding/hex"
	"io"
	"time"

	"github.com/opentracing/opentracing-go"
)

type Joiner func(carrier interface{}) (traceID, parentID, spanID uint64, err error)
type Injecter func(sp *Span, carrier interface{}) error

var joiners = map[interface{}]Joiner{
	opentracing.TextMap: textJoiner,
	opentracing.Binary:  binaryJoiner,
}

var injecters = map[interface{}]Injecter{
	opentracing.TextMap: textInjecter,
	opentracing.Binary:  binaryInjecter,
}

func RegisterJoiner(format interface{}, joiner Joiner) {
	joiners[format] = joiner
}

func RegisterInjecter(format interface{}, injecter Injecter) {
	injecters[format] = injecter
}

func textInjecter(sp *Span, carrier interface{}) error {
	w := carrier.(opentracing.TextMapWriter)
	w.Set("X-B3-TraceId", idToHex(sp.TraceID))
	w.Set("X-B3-SpanId", idToHex(sp.SpanID))
	w.Set("X-B3-ParentSpanId", idToHex(sp.ParentID))
	return nil
}

func textJoiner(carrier interface{}) (traceID, parentID, spanID uint64, err error) {
	r := carrier.(opentracing.TextMapReader)
	err = r.ForeachKey(func(key string, val string) error {
		switch key {
		case "X-B3-TraceId":
			traceID = idFromHex(val)
		case "X-B3-SpanId":
			spanID = idFromHex(val)
		case "X-B3-ParentSpanId":
			parentID = idFromHex(val)
		}
		return nil
	})
	return traceID, parentID, spanID, err
}

func binaryInjecter(sp *Span, carrier interface{}) error {
	w := carrier.(io.Writer)
	b := make([]byte, 24)
	binary.BigEndian.PutUint64(b, sp.TraceID)
	binary.BigEndian.PutUint64(b[8:], sp.TraceID)
	binary.BigEndian.PutUint64(b[16:], sp.TraceID)
	_, err := w.Write(b)
	return err
}

func binaryJoiner(carrier interface{}) (traceID, parentID, spanID uint64, err error) {
	r := carrier.(io.Reader)
	b := make([]byte, 24)
	if _, err := io.ReadFull(r, b); err != nil {
		return 0, 0, 0, err
	}
	traceID = binary.BigEndian.Uint64(b)
	spanID = binary.BigEndian.Uint64(b[8:])
	parentID = binary.BigEndian.Uint64(b[16:])
	return traceID, parentID, spanID, err
}

// Span is an implementation of the Open Tracing Span interface.
type Span struct {
	tracer *Tracer

	SpanID        uint64
	ParentID      uint64
	TraceID       uint64
	OperationName string
	StartTime     time.Time
}

func (sp *Span) SetOperationName(name string) opentracing.Span {
	sp.tracer.storer.SetOperationName(sp, name)
	sp.OperationName = name
	return sp
}

func (sp *Span) SetTag(key string, value interface{}) opentracing.Span {
	sp.tracer.storer.SetTag(sp, key, value)
	return sp
}

func (sp *Span) Finish() {
	sp.FinishWithOptions(opentracing.FinishOptions{})
}

func (sp *Span) FinishWithOptions(opts opentracing.FinishOptions) {
	if opts.FinishTime.IsZero() {
		opts.FinishTime = time.Now()
	}
	sp.tracer.storer.FinishWithOptions(sp, opts)
}

func (sp *Span) LogEvent(event string) {
	sp.Log(opentracing.LogData{
		Event: event,
	})
}

func (sp *Span) LogEventWithPayload(event string, payload interface{}) {
	sp.Log(opentracing.LogData{
		Event:   event,
		Payload: payload,
	})
}

func (sp *Span) Log(data opentracing.LogData) {
	if data.Timestamp.IsZero() {
		data.Timestamp = time.Now()
	}
	sp.tracer.storer.Log(sp, data)
}

func (sp *Span) SetBaggageItem(key, value string) opentracing.Span {
	// TODO implement
	panic("not implemented")
	return sp

}
func (sp *Span) BaggageItem(key string) string {
	// TODO implement
	panic("not implemented")
	return ""
}

func (sp *Span) Tracer() opentracing.Tracer {
	return sp.tracer
}

// Tracer is an implementation of the Open Tracing Tracer interface.
type Tracer struct {
	storer      Storer
	idGenerator IDGenerator
}

func NewTracer(storer Storer, idGenerator IDGenerator) *Tracer {
	return &Tracer{storer, idGenerator}
}

func (tr *Tracer) StartSpan(operationName string) opentracing.Span {
	return tr.StartSpanWithOptions(opentracing.StartSpanOptions{
		OperationName: operationName,
	})
}

func (tr *Tracer) StartSpanWithOptions(opts opentracing.StartSpanOptions) opentracing.Span {
	if opts.StartTime.IsZero() {
		opts.StartTime = time.Now()
	}
	var traceID uint64
	var parentID uint64
	if opts.Parent != nil {
		parent, ok := opts.Parent.(*Span)
		if !ok {
			panic("parent span must be of type *Span")
		}
		parentID = parent.SpanID
		traceID = parent.TraceID
	}
	if traceID == 0 {
		traceID = tr.idGenerator.GenerateID()
	}
	id := tr.idGenerator.GenerateID()
	sp := &Span{
		tracer:        tr,
		OperationName: opts.OperationName,
		SpanID:        id,
		ParentID:      parentID,
		TraceID:       traceID,
		StartTime:     opts.StartTime,
	}
	tr.storer.AddSpan(sp)
	return sp
}

func idToHex(id uint64) string {
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, id)
	return hex.EncodeToString(b)
}

func idFromHex(s string) uint64 {
	b, _ := hex.DecodeString(s)
	return binary.BigEndian.Uint64(b)
}

func (tr *Tracer) Inject(sp opentracing.Span, format interface{}, carrier interface{}) error {
	// TODO(dh): support sampling
	span := sp.(*Span)
	injecter, ok := injecters[format]
	if !ok {
		return opentracing.ErrUnsupportedFormat
	}
	return injecter(span, carrier)
}

func (tr *Tracer) Join(operationName string, format interface{}, carrier interface{}) (opentracing.Span, error) {
	// TODO(dh): support sampling
	joiner, ok := joiners[format]
	if !ok {
		return nil, opentracing.ErrUnsupportedFormat
	}
	traceID, _, spanID, err := joiner(carrier)
	if err != nil {
		return nil, opentracing.ErrUnsupportedFormat
	}

	return tr.StartSpanWithOptions(opentracing.StartSpanOptions{
		OperationName: operationName,
		Parent:        &Span{TraceID: traceID, SpanID: spanID},
	}), nil
}

// IDGenerator generates IDs for traces and spans. The ID with value 0
// is reserved to mean "no parent span" and should not be generated.
type IDGenerator interface {
	GenerateID() uint64
}

// Storer maps Open Tracing operations to a backend. The backend can
// be actual storage in a database, or a transport to a remote server,
// or coalescion of spans, and so on.
//
// It is up to the implementation whether it acts on operations right
// away or if it caches them. For example, an implementation could
// wait for a call to FinishWithOptions before storing a span, at the
// risk of losing spans in case of crashes.
type Storer interface {
	// AddSpan creates a new span.
	AddSpan(sp *Span)
	// SetOperationName sets the operation name of the span.
	SetOperationName(sp *Span, name string)
	// SetTag sets the tag key to value. Duplicate keys overwrite each
	// other. Any value that can marshal to JSON is allowed.
	SetTag(sp *Span, key string, value interface{})
	// FinishWithOptions marks the span sp as done. FinishTime will
	// already be populated.
	FinishWithOptions(sp *Span, opts opentracing.FinishOptions)
	// Log logs an event. Timestamp will already be populated. The
	// payload must be a value that can marshal to JSON.
	Log(sp *Span, data opentracing.LogData)
}

type Queryer interface {
	SpanWithID(id uint64) (*Span, bool)
}

// RandomID generates random IDs by using crypto/rand.
type RandomID struct{}

// GenerateID generates an ID.
func (RandomID) GenerateID() uint64 {
	b := make([]byte, 8)
	for {
		_, _ = rand.Read(b)
		x := binary.BigEndian.Uint64(b)
		if x != 0 {
			return x
		}
	}
}
<commit_msg>Return ErrTraceNotFound when appropriate<commit_after>// Package tracer implements a Dapper-style tracing system. It is
// compatible with the Open Tracing specification.
package tracer

import (
	"crypto/rand"
	"encoding/binary"
	"encoding/hex"
	"io"
	"time"

	"github.com/opentracing/opentracing-go"
)

type Joiner func(carrier interface{}) (traceID, parentID, spanID uint64, err error)
type Injecter func(sp *Span, carrier interface{}) error

var joiners = map[interface{}]Joiner{
	opentracing.TextMap: textJoiner,
	opentracing.Binary:  binaryJoiner,
}

var injecters = map[interface{}]Injecter{
	opentracing.TextMap: textInjecter,
	opentracing.Binary:  binaryInjecter,
}

func RegisterJoiner(format interface{}, joiner Joiner) {
	joiners[format] = joiner
}

func RegisterInjecter(format interface{}, injecter Injecter) {
	injecters[format] = injecter
}

func textInjecter(sp *Span, carrier interface{}) error {
	w := carrier.(opentracing.TextMapWriter)
	w.Set("X-B3-TraceId", idToHex(sp.TraceID))
	w.Set("X-B3-SpanId", idToHex(sp.SpanID))
	w.Set("X-B3-ParentSpanId", idToHex(sp.ParentID))
	return nil
}

func textJoiner(carrier interface{}) (traceID, parentID, spanID uint64, err error) {
	r := carrier.(opentracing.TextMapReader)
	err = r.ForeachKey(func(key string, val string) error {
		switch key {
		case "X-B3-TraceId":
			traceID = idFromHex(val)
		case "X-B3-SpanId":
			spanID = idFromHex(val)
		case "X-B3-ParentSpanId":
			parentID = idFromHex(val)
		}
		return nil
	})
	if traceID == 0 {
		return 0, 0, 0, opentracing.ErrTraceNotFound
	}
	return traceID, parentID, spanID, err
}

func binaryInjecter(sp *Span, carrier interface{}) error {
	w := carrier.(io.Writer)
	b := make([]byte, 24)
	binary.BigEndian.PutUint64(b, sp.TraceID)
	binary.BigEndian.PutUint64(b[8:], sp.TraceID)
	binary.BigEndian.PutUint64(b[16:], sp.TraceID)
	_, err := w.Write(b)
	return err
}

func binaryJoiner(carrier interface{}) (traceID, parentID, spanID uint64, err error) {
	r := carrier.(io.Reader)
	b := make([]byte, 24)
	if _, err := io.ReadFull(r, b); err != nil {
		if err == io.ErrUnexpectedEOF {
			return 0, 0, 0, opentracing.ErrTraceNotFound
		}
		return 0, 0, 0, err
	}
	traceID = binary.BigEndian.Uint64(b)
	spanID = binary.BigEndian.Uint64(b[8:])
	parentID = binary.BigEndian.Uint64(b[16:])
	return traceID, parentID, spanID, err
}

// Span is an implementation of the Open Tracing Span interface.
type Span struct {
	tracer *Tracer

	SpanID        uint64
	ParentID      uint64
	TraceID       uint64
	OperationName string
	StartTime     time.Time
}

func (sp *Span) SetOperationName(name string) opentracing.Span {
	sp.tracer.storer.SetOperationName(sp, name)
	sp.OperationName = name
	return sp
}

func (sp *Span) SetTag(key string, value interface{}) opentracing.Span {
	sp.tracer.storer.SetTag(sp, key, value)
	return sp
}

func (sp *Span) Finish() {
	sp.FinishWithOptions(opentracing.FinishOptions{})
}

func (sp *Span) FinishWithOptions(opts opentracing.FinishOptions) {
	if opts.FinishTime.IsZero() {
		opts.FinishTime = time.Now()
	}
	sp.tracer.storer.FinishWithOptions(sp, opts)
}

func (sp *Span) LogEvent(event string) {
	sp.Log(opentracing.LogData{
		Event: event,
	})
}

func (sp *Span) LogEventWithPayload(event string, payload interface{}) {
	sp.Log(opentracing.LogData{
		Event:   event,
		Payload: payload,
	})
}

func (sp *Span) Log(data opentracing.LogData) {
	if data.Timestamp.IsZero() {
		data.Timestamp = time.Now()
	}
	sp.tracer.storer.Log(sp, data)
}

func (sp *Span) SetBaggageItem(key, value string) opentracing.Span {
	// TODO implement
	panic("not implemented")
	return sp

}
func (sp *Span) BaggageItem(key string) string {
	// TODO implement
	panic("not implemented")
	return ""
}

func (sp *Span) Tracer() opentracing.Tracer {
	return sp.tracer
}

// Tracer is an implementation of the Open Tracing Tracer interface.
type Tracer struct {
	storer      Storer
	idGenerator IDGenerator
}

func NewTracer(storer Storer, idGenerator IDGenerator) *Tracer {
	return &Tracer{storer, idGenerator}
}

func (tr *Tracer) StartSpan(operationName string) opentracing.Span {
	return tr.StartSpanWithOptions(opentracing.StartSpanOptions{
		OperationName: operationName,
	})
}

func (tr *Tracer) StartSpanWithOptions(opts opentracing.StartSpanOptions) opentracing.Span {
	if opts.StartTime.IsZero() {
		opts.StartTime = time.Now()
	}
	var traceID uint64
	var parentID uint64
	if opts.Parent != nil {
		parent, ok := opts.Parent.(*Span)
		if !ok {
			panic("parent span must be of type *Span")
		}
		parentID = parent.SpanID
		traceID = parent.TraceID
	}
	if traceID == 0 {
		traceID = tr.idGenerator.GenerateID()
	}
	id := tr.idGenerator.GenerateID()
	sp := &Span{
		tracer:        tr,
		OperationName: opts.OperationName,
		SpanID:        id,
		ParentID:      parentID,
		TraceID:       traceID,
		StartTime:     opts.StartTime,
	}
	tr.storer.AddSpan(sp)
	return sp
}

func idToHex(id uint64) string {
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, id)
	return hex.EncodeToString(b)
}

func idFromHex(s string) uint64 {
	b, _ := hex.DecodeString(s)
	return binary.BigEndian.Uint64(b)
}

func (tr *Tracer) Inject(sp opentracing.Span, format interface{}, carrier interface{}) error {
	// TODO(dh): support sampling
	span := sp.(*Span)
	injecter, ok := injecters[format]
	if !ok {
		return opentracing.ErrUnsupportedFormat
	}
	return injecter(span, carrier)
}

func (tr *Tracer) Join(operationName string, format interface{}, carrier interface{}) (opentracing.Span, error) {
	// TODO(dh): support sampling
	joiner, ok := joiners[format]
	if !ok {
		return nil, opentracing.ErrUnsupportedFormat
	}
	traceID, _, spanID, err := joiner(carrier)
	if err != nil {
		return nil, opentracing.ErrUnsupportedFormat
	}

	return tr.StartSpanWithOptions(opentracing.StartSpanOptions{
		OperationName: operationName,
		Parent:        &Span{TraceID: traceID, SpanID: spanID},
	}), nil
}

// IDGenerator generates IDs for traces and spans. The ID with value 0
// is reserved to mean "no parent span" and should not be generated.
type IDGenerator interface {
	GenerateID() uint64
}

// Storer maps Open Tracing operations to a backend. The backend can
// be actual storage in a database, or a transport to a remote server,
// or coalescion of spans, and so on.
//
// It is up to the implementation whether it acts on operations right
// away or if it caches them. For example, an implementation could
// wait for a call to FinishWithOptions before storing a span, at the
// risk of losing spans in case of crashes.
type Storer interface {
	// AddSpan creates a new span.
	AddSpan(sp *Span)
	// SetOperationName sets the operation name of the span.
	SetOperationName(sp *Span, name string)
	// SetTag sets the tag key to value. Duplicate keys overwrite each
	// other. Any value that can marshal to JSON is allowed.
	SetTag(sp *Span, key string, value interface{})
	// FinishWithOptions marks the span sp as done. FinishTime will
	// already be populated.
	FinishWithOptions(sp *Span, opts opentracing.FinishOptions)
	// Log logs an event. Timestamp will already be populated. The
	// payload must be a value that can marshal to JSON.
	Log(sp *Span, data opentracing.LogData)
}

type Queryer interface {
	SpanWithID(id uint64) (*Span, bool)
}

// RandomID generates random IDs by using crypto/rand.
type RandomID struct{}

// GenerateID generates an ID.
func (RandomID) GenerateID() uint64 {
	b := make([]byte, 8)
	for {
		_, _ = rand.Read(b)
		x := binary.BigEndian.Uint64(b)
		if x != 0 {
			return x
		}
	}
}
<|endoftext|>