<commit_before>package hawserclient

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/cheggaaa/pb"
	"github.com/hawser/git-hawser/hawser"
	"github.com/rubyist/tracerx"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
)

const (
	gitMediaType     = "application/vnd.hawser"
	gitMediaMetaType = gitMediaType + "+json; charset=utf-8"
	gitMediaHeader   = "--git-media."
)

type linkMeta struct {
	Links map[string]*link `json:"_links,omitempty"`
}

type link struct {
	Href   string            `json:"href"`
	Header map[string]string `json:"header,omitempty"`
}

func Options(filehash string) (int, error) {
	oid := filepath.Base(filehash)
	_, err := os.Stat(filehash)
	if err != nil {
		return 0, err
	}

	tracerx.Printf("api_options: %s", oid)
	req, creds, err := clientRequest("OPTIONS", oid)
	if err != nil {
		return 0, err
	}

	res, wErr := doRequest(req, creds)
	if wErr != nil {
		return 0, wErr
	}
	tracerx.Printf("api_options_status: %d", res.StatusCode)

	return res.StatusCode, nil
}

func Put(filehash, filename string, cb hawser.CopyCallback) error {
	if filename == "" {
		filename = filehash
	}

	oid := filepath.Base(filehash)
	file, err := os.Open(filehash)
	if err != nil {
		return err
	}
	defer file.Close()

	stat, err := file.Stat()
	if err != nil {
		return err
	}

	req, creds, err := clientRequest("PUT", oid)
	if err != nil {
		return err
	}

	fileSize := stat.Size()
	reader := &hawser.CallbackReader{
		C:         cb,
		TotalSize: fileSize,
		Reader:    file,
	}

	bar := pb.StartNew(int(fileSize))
	bar.SetUnits(pb.U_BYTES)
	bar.Start()

	req.Header.Set("Content-Type", gitMediaType)
	req.Header.Set("Accept", gitMediaMetaType)
	req.Body = ioutil.NopCloser(bar.NewProxyReader(reader))
	req.ContentLength = fileSize

	fmt.Printf("Sending %s\n", filename)

	tracerx.Printf("api_put: %s %s", oid, filename)
	res, wErr := doRequest(req, creds)
	if wErr != nil {
		return wErr
	}
	tracerx.Printf("api_put_status: %d", res.StatusCode)

	return nil
}

func ExternalPut(filehash, filename string, lm *linkMeta, cb hawser.CopyCallback) error {
	link, ok := lm.Links["upload"]
	if !ok {
		return hawser.Error(errors.New("No upload link provided"))
	}

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

	stat, err := file.Stat()
	if err != nil {
		return err
	}
	fileSize := stat.Size()
	reader := &hawser.CallbackReader{
		C:         cb,
		TotalSize: fileSize,
		Reader:    file,
	}

	req, err := http.NewRequest("PUT", link.Href, nil)
	if err != nil {
		return hawser.Error(err)
	}
	for h, v := range link.Header {
		req.Header.Set(h, v)
	}

	bar := pb.StartNew(int(fileSize))
	bar.SetUnits(pb.U_BYTES)
	bar.Start()

	req.Body = ioutil.NopCloser(bar.NewProxyReader(reader))
	req.ContentLength = fileSize

	tracerx.Printf("external_put: %s %s", filepath.Base(filehash), req.URL)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return hawser.Error(err)
	}
	tracerx.Printf("external_put_status: %d", res.StatusCode)

	// Run the callback
	if cb, ok := lm.Links["callback"]; ok {
		oid := filepath.Base(filehash)
		body, err := ioutil.ReadAll(res.Body)
		if err != nil {
			return hawser.Error(err)
		}

		cbreq, err := http.NewRequest("POST", cb.Href, nil)
		if err != nil {
			return hawser.Error(err)
		}
		for h, v := range cb.Header {
			cbreq.Header.Set(h, v)
		}

		d := fmt.Sprintf(`{"oid":"%s", "size":%d, "status":%d, "body":"%s"}`, oid, fileSize, res.StatusCode, string(body))
		cbreq.Body = ioutil.NopCloser(bytes.NewBufferString(d))

		tracerx.Printf("callback: %s %s", oid, cb.Href)
		cbres, err := http.DefaultClient.Do(cbreq)
		if err != nil {
			return hawser.Error(err)
		}
		tracerx.Printf("callback_status: %d", cbres.StatusCode)
	}

	return nil
}

func Post(filehash, filename string) (*linkMeta, int, error) {
	oid := filepath.Base(filehash)
	req, creds, err := clientRequest("POST", "")
	if err != nil {
		return nil, 0, hawser.Error(err)
	}

	file, err := os.Open(filehash)
	if err != nil {
		return nil, 0, hawser.Error(err)
	}
	defer file.Close()

	stat, err := file.Stat()
	if err != nil {
		return nil, 0, hawser.Error(err)
	}
	fileSize := stat.Size()

	d := fmt.Sprintf(`{"oid":"%s", "size":%d}`, oid, fileSize)
	req.Body = ioutil.NopCloser(bytes.NewBufferString(d))

	req.Header.Set("Accept", gitMediaMetaType)

	tracerx.Printf("api_post: %s %s", oid, filename)
	res, wErr := doRequest(req, creds)
	if wErr != nil {
		return nil, 0, wErr
	}
	tracerx.Printf("api_post_status: %d", res.StatusCode)

	if res.StatusCode == 201 {
		var lm linkMeta
		dec := json.NewDecoder(res.Body)
		err := dec.Decode(&lm)
		if err != nil {
			return nil, res.StatusCode, hawser.Error(err)
		}

		return &lm, res.StatusCode, nil
	}

	return nil, res.StatusCode, nil
}

func Get(filename string) (io.ReadCloser, int64, *hawser.WrappedError) {
	oid := filepath.Base(filename)
	req, creds, err := clientRequest("GET", oid)
	if err != nil {
		return nil, 0, hawser.Error(err)
	}

	req.Header.Set("Accept", gitMediaType)
	res, wErr := doRequest(req, creds)

	if wErr != nil {
		return nil, 0, wErr
	}

	contentType := res.Header.Get("Content-Type")
	if contentType == "" {
		wErr = hawser.Error(errors.New("Empty Content-Type"))
		setErrorResponseContext(wErr, res)
		return nil, 0, wErr
	}

	if ok, wErr := validateMediaHeader(contentType, res.Body); !ok {
		setErrorResponseContext(wErr, res)
		return nil, 0, wErr
	}

	return res.Body, res.ContentLength, nil
}

func validateMediaHeader(contentType string, reader io.Reader) (bool, *hawser.WrappedError) {
	mediaType, params, err := mime.ParseMediaType(contentType)
	if err != nil {
		return false, hawser.Errorf(err, "Invalid Media Type: %s", contentType)
	}

	if mediaType == gitMediaType {

		givenHeader, ok := params["header"]
		if !ok {
			return false, hawser.Error(fmt.Errorf("Missing Git Media header in %s", contentType))
		}

		fullGivenHeader := "--" + givenHeader + "\n"

		header := make([]byte, len(fullGivenHeader))
		_, err = io.ReadAtLeast(reader, header, len(fullGivenHeader))
		if err != nil {
			return false, hawser.Errorf(err, "Error reading response body.")
		}

		if string(header) != fullGivenHeader {
			return false, hawser.Error(fmt.Errorf("Invalid header: %s expected, got %s", fullGivenHeader, header))
		}
	}
	return true, nil
}

func doRequest(req *http.Request, creds Creds) (*http.Response, *hawser.WrappedError) {
	res, err := hawser.HttpClient().Do(req)

	var wErr *hawser.WrappedError

	if err == nil {
		if res.StatusCode > 299 {
			// An auth error should be 403.  Could be 404 also.
			if res.StatusCode < 405 {
				execCreds(creds, "reject")

				apierr := &Error{}
				dec := json.NewDecoder(res.Body)
				if err := dec.Decode(apierr); err != nil {
					wErr = hawser.Errorf(err, "Error decoding JSON from response")
				} else {
					wErr = hawser.Errorf(apierr, "Invalid response: %d", res.StatusCode)
				}
			}
		} else {
			execCreds(creds, "approve")
		}
	} else {
		wErr = hawser.Errorf(err, "Error sending HTTP request to %s", req.URL.String())
	}

	if wErr != nil {
		if res != nil {
			setErrorResponseContext(wErr, res)
		} else {
			setErrorRequestContext(wErr, req)
		}
	}

	return res, wErr
}

var hiddenHeaders = map[string]bool{
	"Authorization": true,
}

func setErrorRequestContext(err *hawser.WrappedError, req *http.Request) {
	err.Set("Endpoint", hawser.Config.Endpoint())
	err.Set("URL", fmt.Sprintf("%s %s", req.Method, req.URL.String()))
	setErrorHeaderContext(err, "Response", req.Header)
}

func setErrorResponseContext(err *hawser.WrappedError, res *http.Response) {
	err.Set("Status", res.Status)
	setErrorHeaderContext(err, "Request", res.Header)
	setErrorRequestContext(err, res.Request)
}

func setErrorHeaderContext(err *hawser.WrappedError, prefix string, head http.Header) {
	for key, _ := range head {
		contextKey := fmt.Sprintf("%s:%s", prefix, key)
		if _, skip := hiddenHeaders[key]; skip {
			err.Set(contextKey, "--")
		} else {
			err.Set(contextKey, head.Get(key))
		}
	}
}

func clientRequest(method, oid string) (*http.Request, Creds, error) {
	u := ObjectUrl(oid)
	req, err := http.NewRequest(method, u.String(), nil)
	req.Header.Set("User-Agent", hawser.UserAgent)
	if err == nil {
		creds, err := credentials(u)
		if err != nil {
			return req, nil, err
		}

		token := fmt.Sprintf("%s:%s", creds["username"], creds["password"])
		auth := "Basic " + base64.URLEncoding.EncodeToString([]byte(token))
		req.Header.Set("Authorization", auth)
		return req, creds, nil
	}

	return req, nil, err
}

func ObjectUrl(oid string) *url.URL {
	c := hawser.Config
	u, _ := url.Parse(c.Endpoint())
	u.Path = path.Join(u.Path, "objects", oid)
	return u
}

type Error struct {
	Message   string `json:"message"`
	RequestId string `json:"request_id,omitempty"`
}

func (e *Error) Error() string {
	return e.Message
}
<commit_msg>ラララララ ラー ウウウ フフフ<commit_after>package hawserclient

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/cheggaaa/pb"
	"github.com/hawser/git-hawser/hawser"
	"github.com/rubyist/tracerx"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
)

const (
	gitMediaType     = "application/vnd.hawser"
	gitMediaMetaType = gitMediaType + "+json; charset=utf-8"
)

type linkMeta struct {
	Links map[string]*link `json:"_links,omitempty"`
}

type link struct {
	Href   string            `json:"href"`
	Header map[string]string `json:"header,omitempty"`
}

func Options(filehash string) (int, error) {
	oid := filepath.Base(filehash)
	_, err := os.Stat(filehash)
	if err != nil {
		return 0, err
	}

	tracerx.Printf("api_options: %s", oid)
	req, creds, err := clientRequest("OPTIONS", oid)
	if err != nil {
		return 0, err
	}

	res, wErr := doRequest(req, creds)
	if wErr != nil {
		return 0, wErr
	}
	tracerx.Printf("api_options_status: %d", res.StatusCode)

	return res.StatusCode, nil
}

func Put(filehash, filename string, cb hawser.CopyCallback) error {
	if filename == "" {
		filename = filehash
	}

	oid := filepath.Base(filehash)
	file, err := os.Open(filehash)
	if err != nil {
		return err
	}
	defer file.Close()

	stat, err := file.Stat()
	if err != nil {
		return err
	}

	req, creds, err := clientRequest("PUT", oid)
	if err != nil {
		return err
	}

	fileSize := stat.Size()
	reader := &hawser.CallbackReader{
		C:         cb,
		TotalSize: fileSize,
		Reader:    file,
	}

	bar := pb.StartNew(int(fileSize))
	bar.SetUnits(pb.U_BYTES)
	bar.Start()

	req.Header.Set("Content-Type", gitMediaType)
	req.Header.Set("Accept", gitMediaMetaType)
	req.Body = ioutil.NopCloser(bar.NewProxyReader(reader))
	req.ContentLength = fileSize

	fmt.Printf("Sending %s\n", filename)

	tracerx.Printf("api_put: %s %s", oid, filename)
	res, wErr := doRequest(req, creds)
	if wErr != nil {
		return wErr
	}
	tracerx.Printf("api_put_status: %d", res.StatusCode)

	return nil
}

func ExternalPut(filehash, filename string, lm *linkMeta, cb hawser.CopyCallback) error {
	link, ok := lm.Links["upload"]
	if !ok {
		return hawser.Error(errors.New("No upload link provided"))
	}

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

	stat, err := file.Stat()
	if err != nil {
		return err
	}
	fileSize := stat.Size()
	reader := &hawser.CallbackReader{
		C:         cb,
		TotalSize: fileSize,
		Reader:    file,
	}

	req, err := http.NewRequest("PUT", link.Href, nil)
	if err != nil {
		return hawser.Error(err)
	}
	for h, v := range link.Header {
		req.Header.Set(h, v)
	}

	bar := pb.StartNew(int(fileSize))
	bar.SetUnits(pb.U_BYTES)
	bar.Start()

	req.Body = ioutil.NopCloser(bar.NewProxyReader(reader))
	req.ContentLength = fileSize

	tracerx.Printf("external_put: %s %s", filepath.Base(filehash), req.URL)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return hawser.Error(err)
	}
	tracerx.Printf("external_put_status: %d", res.StatusCode)

	// Run the callback
	if cb, ok := lm.Links["callback"]; ok {
		oid := filepath.Base(filehash)
		body, err := ioutil.ReadAll(res.Body)
		if err != nil {
			return hawser.Error(err)
		}

		cbreq, err := http.NewRequest("POST", cb.Href, nil)
		if err != nil {
			return hawser.Error(err)
		}
		for h, v := range cb.Header {
			cbreq.Header.Set(h, v)
		}

		d := fmt.Sprintf(`{"oid":"%s", "size":%d, "status":%d, "body":"%s"}`, oid, fileSize, res.StatusCode, string(body))
		cbreq.Body = ioutil.NopCloser(bytes.NewBufferString(d))

		tracerx.Printf("callback: %s %s", oid, cb.Href)
		cbres, err := http.DefaultClient.Do(cbreq)
		if err != nil {
			return hawser.Error(err)
		}
		tracerx.Printf("callback_status: %d", cbres.StatusCode)
	}

	return nil
}

func Post(filehash, filename string) (*linkMeta, int, error) {
	oid := filepath.Base(filehash)
	req, creds, err := clientRequest("POST", "")
	if err != nil {
		return nil, 0, hawser.Error(err)
	}

	file, err := os.Open(filehash)
	if err != nil {
		return nil, 0, hawser.Error(err)
	}
	defer file.Close()

	stat, err := file.Stat()
	if err != nil {
		return nil, 0, hawser.Error(err)
	}
	fileSize := stat.Size()

	d := fmt.Sprintf(`{"oid":"%s", "size":%d}`, oid, fileSize)
	req.Body = ioutil.NopCloser(bytes.NewBufferString(d))

	req.Header.Set("Accept", gitMediaMetaType)

	tracerx.Printf("api_post: %s %s", oid, filename)
	res, wErr := doRequest(req, creds)
	if wErr != nil {
		return nil, 0, wErr
	}
	tracerx.Printf("api_post_status: %d", res.StatusCode)

	if res.StatusCode == 201 {
		var lm linkMeta
		dec := json.NewDecoder(res.Body)
		err := dec.Decode(&lm)
		if err != nil {
			return nil, res.StatusCode, hawser.Error(err)
		}

		return &lm, res.StatusCode, nil
	}

	return nil, res.StatusCode, nil
}

func Get(filename string) (io.ReadCloser, int64, *hawser.WrappedError) {
	oid := filepath.Base(filename)
	req, creds, err := clientRequest("GET", oid)
	if err != nil {
		return nil, 0, hawser.Error(err)
	}

	req.Header.Set("Accept", gitMediaType)
	res, wErr := doRequest(req, creds)

	if wErr != nil {
		return nil, 0, wErr
	}

	contentType := res.Header.Get("Content-Type")
	if contentType == "" {
		wErr = hawser.Error(errors.New("Empty Content-Type"))
		setErrorResponseContext(wErr, res)
		return nil, 0, wErr
	}

	if ok, wErr := validateMediaHeader(contentType, res.Body); !ok {
		setErrorResponseContext(wErr, res)
		return nil, 0, wErr
	}

	return res.Body, res.ContentLength, nil
}

func validateMediaHeader(contentType string, reader io.Reader) (bool, *hawser.WrappedError) {
	mediaType, params, err := mime.ParseMediaType(contentType)
	if err != nil {
		return false, hawser.Errorf(err, "Invalid Media Type: %s", contentType)
	}

	if mediaType == gitMediaType {

		givenHeader, ok := params["header"]
		if !ok {
			return false, hawser.Error(fmt.Errorf("Missing Git Media header in %s", contentType))
		}

		fullGivenHeader := "--" + givenHeader + "\n"

		header := make([]byte, len(fullGivenHeader))
		_, err = io.ReadAtLeast(reader, header, len(fullGivenHeader))
		if err != nil {
			return false, hawser.Errorf(err, "Error reading response body.")
		}

		if string(header) != fullGivenHeader {
			return false, hawser.Error(fmt.Errorf("Invalid header: %s expected, got %s", fullGivenHeader, header))
		}
	}
	return true, nil
}

func doRequest(req *http.Request, creds Creds) (*http.Response, *hawser.WrappedError) {
	res, err := hawser.HttpClient().Do(req)

	var wErr *hawser.WrappedError

	if err == nil {
		if res.StatusCode > 299 {
			// An auth error should be 403.  Could be 404 also.
			if res.StatusCode < 405 {
				execCreds(creds, "reject")

				apierr := &Error{}
				dec := json.NewDecoder(res.Body)
				if err := dec.Decode(apierr); err != nil {
					wErr = hawser.Errorf(err, "Error decoding JSON from response")
				} else {
					wErr = hawser.Errorf(apierr, "Invalid response: %d", res.StatusCode)
				}
			}
		} else {
			execCreds(creds, "approve")
		}
	} else {
		wErr = hawser.Errorf(err, "Error sending HTTP request to %s", req.URL.String())
	}

	if wErr != nil {
		if res != nil {
			setErrorResponseContext(wErr, res)
		} else {
			setErrorRequestContext(wErr, req)
		}
	}

	return res, wErr
}

var hiddenHeaders = map[string]bool{
	"Authorization": true,
}

func setErrorRequestContext(err *hawser.WrappedError, req *http.Request) {
	err.Set("Endpoint", hawser.Config.Endpoint())
	err.Set("URL", fmt.Sprintf("%s %s", req.Method, req.URL.String()))
	setErrorHeaderContext(err, "Response", req.Header)
}

func setErrorResponseContext(err *hawser.WrappedError, res *http.Response) {
	err.Set("Status", res.Status)
	setErrorHeaderContext(err, "Request", res.Header)
	setErrorRequestContext(err, res.Request)
}

func setErrorHeaderContext(err *hawser.WrappedError, prefix string, head http.Header) {
	for key, _ := range head {
		contextKey := fmt.Sprintf("%s:%s", prefix, key)
		if _, skip := hiddenHeaders[key]; skip {
			err.Set(contextKey, "--")
		} else {
			err.Set(contextKey, head.Get(key))
		}
	}
}

func clientRequest(method, oid string) (*http.Request, Creds, error) {
	u := ObjectUrl(oid)
	req, err := http.NewRequest(method, u.String(), nil)
	req.Header.Set("User-Agent", hawser.UserAgent)
	if err == nil {
		creds, err := credentials(u)
		if err != nil {
			return req, nil, err
		}

		token := fmt.Sprintf("%s:%s", creds["username"], creds["password"])
		auth := "Basic " + base64.URLEncoding.EncodeToString([]byte(token))
		req.Header.Set("Authorization", auth)
		return req, creds, nil
	}

	return req, nil, err
}

func ObjectUrl(oid string) *url.URL {
	c := hawser.Config
	u, _ := url.Parse(c.Endpoint())
	u.Path = path.Join(u.Path, "objects", oid)
	return u
}

type Error struct {
	Message   string `json:"message"`
	RequestId string `json:"request_id,omitempty"`
}

func (e *Error) Error() string {
	return e.Message
}
<|endoftext|>


<commit_before>// +build ignore

package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
)

func main() {
	flag.Parse()

	failed := 0
	for _, arg := range flag.Args() {
		cmd := exec.Command("bash", arg)
		if os.Getenv("TMPDIR") != "" {
			cmd.Env = append(cmd.Env, "TMPDIR="+os.Getenv("TMPDIR"))
		}
		cmd.Stderr = os.Stderr
		cmd.Stdout = os.Stdout
		if err := cmd.Run(); err != nil {
			failed++
			fmt.Fprintf(os.Stderr, "FAILED: %s\n", arg)
		}
	}
	if failed > 0 {
		fmt.Fprintf(os.Stderr, "%d FAILED tests\n", failed)
		os.Exit(1)
	}
	fmt.Fprintf(os.Stdout, "SUCCESS: no cli tests failed")
}
<commit_msg>test/cli: and a newline<commit_after>// +build ignore

package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
)

func main() {
	flag.Parse()

	failed := 0
	for _, arg := range flag.Args() {
		cmd := exec.Command("bash", arg)
		if os.Getenv("TMPDIR") != "" {
			cmd.Env = append(cmd.Env, "TMPDIR="+os.Getenv("TMPDIR"))
		}
		cmd.Stderr = os.Stderr
		cmd.Stdout = os.Stdout
		if err := cmd.Run(); err != nil {
			failed++
			fmt.Fprintf(os.Stderr, "FAILED: %s\n", arg)
		}
	}
	if failed > 0 {
		fmt.Fprintf(os.Stderr, "%d FAILED tests\n", failed)
		os.Exit(1)
	}
	fmt.Fprintf(os.Stdout, "SUCCESS: no cli tests failed\n")
}
<|endoftext|>


<commit_before>package main

import (
	mqttc "./utils/mqtt"
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	geoipc "github.com/rubiojr/freegeoip-client"
	"gopkg.in/alecthomas/kingpin.v1"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"
)

type Host struct {
	IP          string  `json:"ip"`
	Name        string  `json:"hostname"`
	Hop         int     `json:"hop-number"`
	Sent        int     `json:"sent"`
	LostPercent float64 `json:"lost-percent"`
	Last        float64 `json:"mean"`
	Avg         float64 `json:"mean"`
	Best        float64 `json:"best"`
	Worst       float64 `json:"worst"`
	StDev       float64 `json:"standard-dev"`
}

type Report struct {
	Time        time.Time       `json:"time"`
	Hosts       []*Host         `json:"hosts"`
	Hops        int             `json:"hops"`
	ElapsedTime time.Duration   `json:"elapsed_time"`
	Location    geoipc.Location `json:"location"`
}

func NewReport(reportCycles int, host string, args ...string) *Report {
	report := &Report{}
	report.Time = time.Now()
	args = append([]string{"--report", "-n", "-c", strconv.Itoa(reportCycles), host}, args...)

	tstart := time.Now()
	mtr := findMtrBin()
	rawOutput, err := exec.Command(mtr, args...).Output()

	if err != nil {
		panic("Error running the mtr command")
	}

	buf := bytes.NewBuffer(rawOutput)
	scanner := bufio.NewScanner(buf)
	scanner.Split(bufio.ScanLines)

	skipHeader := 2
	for scanner.Scan() {
		if skipHeader != 0 {
			skipHeader -= 1
			continue
		}

		tokens := strings.Fields(scanner.Text())
		sent, err := strconv.Atoi(tokens[3])
		if err != nil {
			panic("Error parsing sent field")
		}

		host := Host{
			IP:   tokens[1],
			Sent: sent,
		}

		f2F(strings.Replace(tokens[2], "%", "", -1), &host.LostPercent)
		f2F(tokens[4], &host.Last)
		f2F(tokens[5], &host.Avg)
		f2F(tokens[6], &host.Best)
		f2F(tokens[7], &host.Worst)
		f2F(tokens[8], &host.StDev)

		report.Hosts = append(report.Hosts, &host)
	}

	report.Hops = len(report.Hosts)
	report.ElapsedTime = time.Since(tstart)
	loc, err := geoipc.GetLocation()
	if err != nil {
		report.Location = geoipc.Location{}
	} else {
		report.Location = loc
	}

	return report
}

func f2F(val string, field *float64) {
	f, err := strconv.ParseFloat(val, 64)
	*field = f
	if err != nil {
		panic("Error parsing field")
	}
}

func findMtrBin() string {
	paths := os.Getenv("PATH")
	if paths == "" {
		return ""
	}

	for _, path := range strings.Split(paths, ":") {
		if _, err := os.Stat(path + "/mtr"); err == nil {
			return path + "/mtr"
		}
	}

	return ""
}

func run(count int, host, brokerUrl, topic string, stdout bool) error {
	r := NewReport(count, host)

	if stdout {
		msg, _ := json.MarshalIndent(r, "", "  ")
		fmt.Println(string(msg))
		return nil
	} else {
		msg, _ := json.Marshal(r)
		err := mqttc.PushMsg("push-mtr", brokerUrl, topic, string(msg))
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error sending report: %s", err)
		}
		return err
	}
}

func main() {
	count := kingpin.Flag("count", "Report cycles (mtr -c)").
		Default("10").Int()

	topic := kingpin.Flag("topic", "MTTQ topic").Default("/metrics/mtr").
		String()

	host := kingpin.Arg("host", "Target host").Required().String()

	repeat := kingpin.Flag("repeat", "Send the report every X seconds").
		Default("0").Int()

	brokerUrl := kingpin.Flag("broker-url", "MQTT broker URL").
		Default("").String()

	stdout := kingpin.Flag("stdout", "Print the report to stdout").
		Default("false").Bool()

	kingpin.Version("0.1")
	kingpin.Parse()

	if findMtrBin() == "" {
		fmt.Fprintf(os.Stderr, "mtr binary not found in path")
		os.Exit(1)
	}

	if *brokerUrl == "" {
		*brokerUrl = os.Getenv("MQTT_URL")
		if *brokerUrl == "" {
			fmt.Fprintf(os.Stderr, "Invalid MQTT broker URL")
			os.Exit(1)
		}
	}

	if *repeat != 0 {
		timer := time.NewTicker(1 * time.Second)
		for _ = range timer.C {
			run(*count, *host, *brokerUrl, *topic, *stdout)
		}
	} else {
		err := run(*count, *host, *brokerUrl, *topic, *stdout)
		if err != nil {
			os.Exit(1)
		}
	}
}
<commit_msg>Add eol when printing errors<commit_after>package main

import (
	mqttc "./utils/mqtt"
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	geoipc "github.com/rubiojr/freegeoip-client"
	"gopkg.in/alecthomas/kingpin.v1"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"
)

type Host struct {
	IP          string  `json:"ip"`
	Name        string  `json:"hostname"`
	Hop         int     `json:"hop-number"`
	Sent        int     `json:"sent"`
	LostPercent float64 `json:"lost-percent"`
	Last        float64 `json:"mean"`
	Avg         float64 `json:"mean"`
	Best        float64 `json:"best"`
	Worst       float64 `json:"worst"`
	StDev       float64 `json:"standard-dev"`
}

type Report struct {
	Time        time.Time       `json:"time"`
	Hosts       []*Host         `json:"hosts"`
	Hops        int             `json:"hops"`
	ElapsedTime time.Duration   `json:"elapsed_time"`
	Location    geoipc.Location `json:"location"`
}

func NewReport(reportCycles int, host string, args ...string) *Report {
	report := &Report{}
	report.Time = time.Now()
	args = append([]string{"--report", "-n", "-c", strconv.Itoa(reportCycles), host}, args...)

	tstart := time.Now()
	mtr := findMtrBin()
	rawOutput, err := exec.Command(mtr, args...).Output()

	if err != nil {
		panic("Error running the mtr command")
	}

	buf := bytes.NewBuffer(rawOutput)
	scanner := bufio.NewScanner(buf)
	scanner.Split(bufio.ScanLines)

	skipHeader := 2
	for scanner.Scan() {
		if skipHeader != 0 {
			skipHeader -= 1
			continue
		}

		tokens := strings.Fields(scanner.Text())
		sent, err := strconv.Atoi(tokens[3])
		if err != nil {
			panic("Error parsing sent field")
		}

		host := Host{
			IP:   tokens[1],
			Sent: sent,
		}

		f2F(strings.Replace(tokens[2], "%", "", -1), &host.LostPercent)
		f2F(tokens[4], &host.Last)
		f2F(tokens[5], &host.Avg)
		f2F(tokens[6], &host.Best)
		f2F(tokens[7], &host.Worst)
		f2F(tokens[8], &host.StDev)

		report.Hosts = append(report.Hosts, &host)
	}

	report.Hops = len(report.Hosts)
	report.ElapsedTime = time.Since(tstart)
	loc, err := geoipc.GetLocation()
	if err != nil {
		report.Location = geoipc.Location{}
	} else {
		report.Location = loc
	}

	return report
}

func f2F(val string, field *float64) {
	f, err := strconv.ParseFloat(val, 64)
	*field = f
	if err != nil {
		panic("Error parsing field")
	}
}

func findMtrBin() string {
	paths := os.Getenv("PATH")
	if paths == "" {
		return ""
	}

	for _, path := range strings.Split(paths, ":") {
		if _, err := os.Stat(path + "/mtr"); err == nil {
			return path + "/mtr"
		}
	}

	return ""
}

func run(count int, host, brokerUrl, topic string, stdout bool) error {
	r := NewReport(count, host)

	if stdout {
		msg, _ := json.MarshalIndent(r, "", "  ")
		fmt.Println(string(msg))
		return nil
	} else {
		msg, _ := json.Marshal(r)
		err := mqttc.PushMsg("push-mtr", brokerUrl, topic, string(msg))
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error sending report: %s\n", err)
		}
		return err
	}
}

func main() {
	count := kingpin.Flag("count", "Report cycles (mtr -c)").
		Default("10").Int()

	topic := kingpin.Flag("topic", "MTTQ topic").Default("/metrics/mtr").
		String()

	host := kingpin.Arg("host", "Target host").Required().String()

	repeat := kingpin.Flag("repeat", "Send the report every X seconds").
		Default("0").Int()

	brokerUrl := kingpin.Flag("broker-url", "MQTT broker URL").
		Default("").String()

	stdout := kingpin.Flag("stdout", "Print the report to stdout").
		Default("false").Bool()

	kingpin.Version("0.1")
	kingpin.Parse()

	if findMtrBin() == "" {
		fmt.Fprintf(os.Stderr, "mtr binary not found in path\n")
		os.Exit(1)
	}

	if *brokerUrl == "" {
		*brokerUrl = os.Getenv("MQTT_URL")
		if *brokerUrl == "" {
			fmt.Fprintf(os.Stderr, "Invalid MQTT broker URL\n")
			os.Exit(1)
		}
	}

	if *repeat != 0 {
		timer := time.NewTicker(1 * time.Second)
		for _ = range timer.C {
			run(*count, *host, *brokerUrl, *topic, *stdout)
		}
	} else {
		err := run(*count, *host, *brokerUrl, *topic, *stdout)
		if err != nil {
			os.Exit(1)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"time"

	cl "github.com/rdwilliamson/cl11"
)

func check(err error) {
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func main() {
	platforms, err := cl.GetPlatforms()
	check(err)
	for _, p := range platforms {
		devices, err := p.GetDevices()
		check(err)
		for _, d := range devices {

			c, err := cl.CreateContext([]*cl.Device{d}, cl.ContextProperties{}, nil)
			check(err)

			cq, err := c.CreateCommandQueue(d, cl.CommandQueueProperties{Profiling: true})
			check(err)

			e, err := c.CreateUserEvent()
			check(err)

			size := int(d.MaxMemAllocSize)

			host, err := c.CreateHostBuffer(size, 0)
			check(err)

			device, err := c.CreateDeviceBuffer(size, 0)
			check(err)

			start := time.Now()

			check(cq.CopyBuffer(host, device, 0, 0, size, nil, &e))
			check(cq.Finish())

			duration := time.Since(start)

			transfered := float64(size) / 1024 / 1024
			transferSpeed := transfered / duration.Seconds() / 1024

			fmt.Printf("%v: %.2f MiB in %v (%.2f GiB/s)\n", d, transfered, duration, transferSpeed)

			check(host.Release())
			check(device.Release())
		}
	}
}
<commit_msg>Explicitly print device name.<commit_after>package main

import (
	"fmt"
	"os"
	"time"

	cl "github.com/rdwilliamson/cl11"
)

func check(err error) {
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func main() {
	platforms, err := cl.GetPlatforms()
	check(err)
	for _, p := range platforms {
		devices, err := p.GetDevices()
		check(err)
		for _, d := range devices {

			c, err := cl.CreateContext([]*cl.Device{d}, cl.ContextProperties{}, nil)
			check(err)

			cq, err := c.CreateCommandQueue(d, cl.CommandQueueProperties{Profiling: true})
			check(err)

			e, err := c.CreateUserEvent()
			check(err)

			size := int(d.MaxMemAllocSize)

			host, err := c.CreateHostBuffer(size, 0)
			check(err)

			device, err := c.CreateDeviceBuffer(size, 0)
			check(err)

			start := time.Now()

			check(cq.CopyBuffer(host, device, 0, 0, size, nil, &e))
			check(cq.Finish())

			duration := time.Since(start)

			transfered := float64(size) / 1024 / 1024
			transferSpeed := transfered / duration.Seconds() / 1024

			fmt.Printf("%s: %.2f MiB in %v (%.2f GiB/s)\n", d.Name, transfered, duration, transferSpeed)

			check(host.Release())
			check(device.Release())
		}
	}
}
<|endoftext|>


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

// Package edn implements encoding and decoding of EDN values as defined in
// https://github.com/edn-format/edn. For a full introduction on how to use
// go-edn, see https://github.com/go-edn/edn/blob/v1/docs/introduction.md. Fully
// self-contained examples of go-edn can be found at
// https://github.com/go-edn/edn/tree/v1/examples.
//
// Note that the small examples in this package is not checking errors as
// persively as you should do when you use this package. This is done because
// I'd like the examples to be easily readable and understandable. The bigger
// examples provide proper error handling.
package edn

import (
	"encoding/base64"
	"errors"
	"math/big"
	"reflect"
	"sync"
	"time"
)

var (
	ErrNotFunc         = errors.New("Value is not a function")
	ErrMismatchArities = errors.New("Function does not have single argument in, two argument out")
	ErrNotConcrete     = errors.New("Value is not a concrete non-function type")
	ErrTagOverwritten  = errors.New("Previous tag implementation was overwritten")
)

var globalTags TagMap

// A TagMap contains mappings from tag literals to functions and structs that is
// used when decoding.
type TagMap struct {
	sync.RWMutex
	m map[string]reflect.Value
}

var errorType = reflect.TypeOf((*error)(nil)).Elem()

// AddTagFn adds fn as a converter function for tagname tags to this TagMap. fn
// must have the signature func(T) (U, error), where T is the expected input
// type and U is the output type. See Decoder.AddTagFn for examples.
func (tm *TagMap) AddTagFn(tagname string, fn interface{}) error {
	// TODO: check name
	rfn := reflect.ValueOf(fn)
	rtyp := rfn.Type()
	if rtyp.Kind() != reflect.Func {
		return ErrNotFunc
	}
	if rtyp.NumIn() != 1 || rtyp.NumOut() != 2 || !rtyp.Out(1).Implements(errorType) {
		// ok to have variadic arity?
		return ErrMismatchArities
	}
	return tm.addVal(tagname, rfn)
}

// MustAddTagFn adds fn as a converter function for tagname tags to this TagMap
// like AddTagFn, except this function panics if the tag could not be added.
func (tm *TagMap) MustAddTagFn(tagname string, fn interface{}) {
	if err := tm.AddTagFn(tagname, fn); err != nil {
		panic(err)
	}
}

func (tm *TagMap) addVal(name string, val reflect.Value) error {
	tm.Lock()
	if tm.m == nil {
		tm.m = map[string]reflect.Value{}
	}
	_, ok := tm.m[name]
	tm.m[name] = val
	tm.Unlock()
	if ok {
		return ErrTagOverwritten
	} else {
		return nil
	}
}

// AddTagFn adds fn as a converter function for tagname tags to the global
// TagMap. fn must have the signature func(T) (U, error), where T is the
// expected input type and U is the output type. See Decoder.AddTagFn for
// examples.
func AddTagFn(tagname string, fn interface{}) error {
	return globalTags.AddTagFn(tagname, fn)
}

// AddTagStructs adds the struct as a matching struct for tagname tags to this
// TagMap. val can not be a channel, function, interface or an unsafe pointer.
// See Decoder.AddTagStruct for examples.
func (tm *TagMap) AddTagStruct(tagname string, val interface{}) error {
	rstruct := reflect.ValueOf(val)
	switch rstruct.Type().Kind() {
	case reflect.Invalid, reflect.Chan, reflect.Func, reflect.Interface, reflect.UnsafePointer:
		return ErrNotConcrete
	}
	return tm.addVal(tagname, rstruct)
}

// AddTagStructs adds the struct as a matching struct for tagname tags to the
// global TagMap. val can not be a channel, function, interface or an unsafe
// pointer. See Decoder.AddTagStruct for examples.
func AddTagStruct(tagname string, val interface{}) error {
	return globalTags.AddTagStruct(tagname, val)
}

func init() {
	err := AddTagFn("inst", func(s string) (time.Time, error) {
		return time.Parse(time.RFC3339Nano, s)
	})
	if err != nil {
		panic(err)
	}
	err = AddTagFn("base64", base64.StdEncoding.DecodeString)
	if err != nil {
		panic(err)
	}
}

// A MathContext specifies the precision and rounding mode for
// `math/big.Float`s when decoding.
type MathContext struct {
	Precision uint
	Mode      big.RoundingMode
}

// The GlobalMathContext is the global MathContext. It is used if no other
// context is provided. See MathContext for example usage.
var GlobalMathContext = MathContext{
	Mode:      big.ToNearestEven,
	Precision: 192,
}
<commit_msg>(tags): implement MustAddTagFn for global TagMap<commit_after>// Copyright 2015 Jean Niklas L'orange.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package edn implements encoding and decoding of EDN values as defined in
// https://github.com/edn-format/edn. For a full introduction on how to use
// go-edn, see https://github.com/go-edn/edn/blob/v1/docs/introduction.md. Fully
// self-contained examples of go-edn can be found at
// https://github.com/go-edn/edn/tree/v1/examples.
//
// Note that the small examples in this package is not checking errors as
// persively as you should do when you use this package. This is done because
// I'd like the examples to be easily readable and understandable. The bigger
// examples provide proper error handling.
package edn

import (
	"encoding/base64"
	"errors"
	"math/big"
	"reflect"
	"sync"
	"time"
)

var (
	ErrNotFunc         = errors.New("Value is not a function")
	ErrMismatchArities = errors.New("Function does not have single argument in, two argument out")
	ErrNotConcrete     = errors.New("Value is not a concrete non-function type")
	ErrTagOverwritten  = errors.New("Previous tag implementation was overwritten")
)

var globalTags TagMap

// A TagMap contains mappings from tag literals to functions and structs that is
// used when decoding.
type TagMap struct {
	sync.RWMutex
	m map[string]reflect.Value
}

var errorType = reflect.TypeOf((*error)(nil)).Elem()

// AddTagFn adds fn as a converter function for tagname tags to this TagMap. fn
// must have the signature func(T) (U, error), where T is the expected input
// type and U is the output type. See Decoder.AddTagFn for examples.
func (tm *TagMap) AddTagFn(tagname string, fn interface{}) error {
	// TODO: check name
	rfn := reflect.ValueOf(fn)
	rtyp := rfn.Type()
	if rtyp.Kind() != reflect.Func {
		return ErrNotFunc
	}
	if rtyp.NumIn() != 1 || rtyp.NumOut() != 2 || !rtyp.Out(1).Implements(errorType) {
		// ok to have variadic arity?
		return ErrMismatchArities
	}
	return tm.addVal(tagname, rfn)
}

// MustAddTagFn adds fn as a converter function for tagname tags to this TagMap
// like AddTagFn, except this function panics if the tag could not be added.
func (tm *TagMap) MustAddTagFn(tagname string, fn interface{}) {
	if err := tm.AddTagFn(tagname, fn); err != nil {
		panic(err)
	}
}

func (tm *TagMap) addVal(name string, val reflect.Value) error {
	tm.Lock()
	if tm.m == nil {
		tm.m = map[string]reflect.Value{}
	}
	_, ok := tm.m[name]
	tm.m[name] = val
	tm.Unlock()
	if ok {
		return ErrTagOverwritten
	} else {
		return nil
	}
}

// AddTagFn adds fn as a converter function for tagname tags to the global
// TagMap. fn must have the signature func(T) (U, error), where T is the
// expected input type and U is the output type. See Decoder.AddTagFn for
// examples.
func AddTagFn(tagname string, fn interface{}) error {
	return globalTags.AddTagFn(tagname, fn)
}

// MustAddTagFn adds fn as a converter function for tagname tags to the global
// TagMap like AddTagFn, except this function panics if the tag could not be added.
func MustAddTagFn(tagname string, fn interface{}) {
	globalTags.MustAddTagFn(tagname, fn)
}

// AddTagStructs adds the struct as a matching struct for tagname tags to this
// TagMap. val can not be a channel, function, interface or an unsafe pointer.
// See Decoder.AddTagStruct for examples.
func (tm *TagMap) AddTagStruct(tagname string, val interface{}) error {
	rstruct := reflect.ValueOf(val)
	switch rstruct.Type().Kind() {
	case reflect.Invalid, reflect.Chan, reflect.Func, reflect.Interface, reflect.UnsafePointer:
		return ErrNotConcrete
	}
	return tm.addVal(tagname, rstruct)
}

// AddTagStructs adds the struct as a matching struct for tagname tags to the
// global TagMap. val can not be a channel, function, interface or an unsafe
// pointer. See Decoder.AddTagStruct for examples.
func AddTagStruct(tagname string, val interface{}) error {
	return globalTags.AddTagStruct(tagname, val)
}

func init() {
	err := AddTagFn("inst", func(s string) (time.Time, error) {
		return time.Parse(time.RFC3339Nano, s)
	})
	if err != nil {
		panic(err)
	}
	err = AddTagFn("base64", base64.StdEncoding.DecodeString)
	if err != nil {
		panic(err)
	}
}

// A MathContext specifies the precision and rounding mode for
// `math/big.Float`s when decoding.
type MathContext struct {
	Precision uint
	Mode      big.RoundingMode
}

// The GlobalMathContext is the global MathContext. It is used if no other
// context is provided. See MathContext for example usage.
var GlobalMathContext = MathContext{
	Mode:      big.ToNearestEven,
	Precision: 192,
}
<|endoftext|>


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

package memfs

import (
	"fmt"
	"io"
	"os"
	"time"

	"github.com/googlecloudplatform/gcsfuse/timeutil"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/gcloud/syncutil"
)

// Common attributes for files and directories.
type inode struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	clock timeutil.Clock

	/////////////////////////
	// Mutable state
	/////////////////////////

	mu syncutil.InvariantMutex

	// The current attributes of this inode.
	//
	// INVARIANT: attrs.Mode &^ (os.ModePerm|os.ModeDir|os.ModeSymlink) == 0
	// INVARIANT: !(isDir() && isSymlink())
	// INVARIANT: attrs.Size == len(contents)
	attrs fuseops.InodeAttributes // GUARDED_BY(mu)

	// For directories, entries describing the children of the directory. Unused
	// entries are of type DT_Unknown.
	//
	// This array can never be shortened, nor can its elements be moved, because
	// we use its indices for Dirent.Offset, which is exposed to the user who
	// might be calling readdir in a loop while concurrently modifying the
	// directory. Unused entries can, however, be reused.
	//
	// INVARIANT: If !isDir(), len(entries) == 0
	// INVARIANT: For each i, entries[i].Offset == i+1
	// INVARIANT: Contains no duplicate names in used entries.
	entries []fuseutil.Dirent // GUARDED_BY(mu)

	// For files, the current contents of the file.
	//
	// INVARIANT: If !isFile(), len(contents) == 0
	contents []byte // GUARDED_BY(mu)

	// For symlinks, the target of the symlink.
	//
	// INVARIANT: If !isSymlink(), len(target) == 0
	//
	// GUARDED_BY(mu)
	target string
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// Create a new inode with the supplied attributes, which need not contain
// time-related information (the inode object will take care of that).
func newInode(
	clock timeutil.Clock,
	attrs fuseops.InodeAttributes) (in *inode) {
	// Update time info.
	now := clock.Now()
	attrs.Mtime = now
	attrs.Crtime = now

	// Create the object.
	in = &inode{
		clock:      clock,
		dir:        (attrs.Mode&os.ModeDir != 0),
		attributes: attrs,
	}

	in.mu = syncutil.NewInvariantMutex(in.checkInvariants)
	return
}

func (in *inode) checkInvariants() {
	// INVARIANT: attrs.Mode &^ (os.ModePerm|os.ModeDir|os.ModeSymlink) == 0
	if !(in.attrs.Mode&^(os.ModePerm|os.ModeDir|os.ModeSymlink) == 0) {
		panic(fmt.Sprintf("Unexpected mode: %v", in.attrs.Mode))
	}

	// INVARIANT: !(isDir() && isSymlink())
	if in.isDir() && in.isSymlink() {
		panic(fmt.Sprintf("Unexpected mode: %v", in.attrs.Mode))
	}

	// INVARIANT: attrs.Size == len(contents)
	if in.attrs.Size != len(in.contents) {
		panic(fmt.Sprintf(
			"Size mismatch: %d vs. %d",
			in.attrs.Size,
			len(in.contents)))
	}

	// INVARIANT: If !isDir(), len(entries) == 0
	if !in.isDir() && len(entries) != 0 {
		panic(fmt.Sprintf("Unexpected entries length: %d", len(entries)))
	}

	// INVARIANT: For each i, entries[i].Offset == i+1
	for i, e := range in.entries {
		if !(e.Offset == i+1) {
			panic(fmt.Sprintf("Unexpected offset for index %d: %d", i, e.Offset))
		}
	}

	// INVARIANT: Contains no duplicate names in used entries.
	childNames := make(map[string]struct{})
	for i, e := range in.entries {
		if e.Type != fuseutil.DT_Unknown {
			if _, ok := childNames[e.Name]; ok {
				panic(fmt.Sprintf("Duplicate name: %s", e.Name))
			}

			childNames[e.Name] = struct{}{}
		}
	}

	// INVARIANT: If !isFile(), len(contents) == 0
	if !in.isFile() && len(in.contents) != 0 {
		panic(fmt.Sprintf("Unexpected length: %d", len(in.contents)))
	}

	// INVARIANT: If !isSymlink(), len(target) == 0
	if !in.isSymlink() && len(in.target) != 0 {
		panic(fmt.Sprintf("Unexpected target length: %d", len(in.target)))
	}

	return
}

// LOCKS_REQUIRED(in.mu)
func (in *inode) isDir() bool {
	return in.attrs.Mode&os.ModeDir != 0
}

// LOCKS_REQUIRED(in.mu)
func (in *inode) isSymlink() bool {
	return in.attrs.Mode&os.ModeSymlink != 0
}

// LOCKS_REQUIRED(in.mu)
func (in *inode) isFile() bool {
	return !(in.isDir() || in.isSymlink())
}

////////////////////////////////////////////////////////////////////////
// Public methods
////////////////////////////////////////////////////////////////////////

// Return the number of children of the directory.
//
// REQUIRES: in.isDir()
// LOCKS_REQUIRED(in.mu)
func (in *inode) Len() (n int) {
	for _, e := range in.entries {
		if e.Type != fuseutil.DT_Unknown {
			n++
		}
	}

	return
}

// Find an entry for the given child name and return its inode ID.
//
// REQUIRES: in.isDir()
// LOCKS_REQUIRED(in.mu)
func (in *inode) LookUpChild(name string) (id fuseops.InodeID, ok bool) {
	index, ok := in.findChild(name)
	if ok {
		id = in.entries[index].Inode
	}

	return
}

// Add an entry for a child.
//
// REQUIRES: in.isDir()
// REQUIRES: dt != fuseutil.DT_Unknown
// LOCKS_REQUIRED(in.mu)
func (in *inode) AddChild(
	id fuseops.InodeID,
	name string,
	dt fuseutil.DirentType) {
	var index int

	// Update the modification time.
	in.attributes.Mtime = in.clock.Now()

	// No matter where we place the entry, make sure it has the correct Offset
	// field.
	defer func() {
		in.entries[index].Offset = fuseops.DirOffset(index + 1)
	}()

	// Set up the entry.
	e := fuseutil.Dirent{
		Inode: id,
		Name:  name,
		Type:  dt,
	}

	// Look for a gap in which we can insert it.
	for index = range in.entries {
		if in.entries[index].Type == fuseutil.DT_Unknown {
			in.entries[index] = e
			return
		}
	}

	// Append it to the end.
	index = len(in.entries)
	in.entries = append(in.entries, e)
}

// Remove an entry for a child.
//
// REQUIRES: in.isDir()
// REQUIRES: An entry for the given name exists.
// LOCKS_REQUIRED(in.mu)
func (in *inode) RemoveChild(name string) {
	// Update the modification time.
	in.attributes.Mtime = in.clock.Now()

	// Find the entry.
	i, ok := in.findChild(name)
	if !ok {
		panic(fmt.Sprintf("Unknown child: %s", name))
	}

	// Mark it as unused.
	in.entries[i] = fuseutil.Dirent{
		Type:   fuseutil.DT_Unknown,
		Offset: fuseops.DirOffset(i + 1),
	}
}

// Serve a ReadDir request.
//
// REQUIRES: in.isDir()
// LOCKS_REQUIRED(in.mu)
func (in *inode) ReadDir(offset int, size int) (data []byte, err error) {
	if !in.dir {
		panic("ReadDir called on non-directory.")
	}

	for i := offset; i < len(in.entries); i++ {
		e := in.entries[i]

		// Skip unused entries.
		if e.Type == fuseutil.DT_Unknown {
			continue
		}

		data = fuseutil.AppendDirent(data, in.entries[i])

		// Trim and stop early if we've exceeded the requested size.
		if len(data) > size {
			data = data[:size]
			break
		}
	}

	return
}

// Read from the file's contents. See documentation for ioutil.ReaderAt.
//
// REQUIRES: in.isFile()
// LOCKS_REQUIRED(in.mu)
func (in *inode) ReadAt(p []byte, off int64) (n int, err error) {
	if in.dir {
		panic("ReadAt called on directory.")
	}

	// Ensure the offset is in range.
	if off > int64(len(in.contents)) {
		err = io.EOF
		return
	}

	// Read what we can.
	n = copy(p, in.contents[off:])
	if n < len(p) {
		err = io.EOF
	}

	return
}

// Write to the file's contents. See documentation for ioutil.WriterAt.
//
// REQUIRES: in.isFile()
// LOCKS_REQUIRED(in.mu)
func (in *inode) WriteAt(p []byte, off int64) (n int, err error) {
	if in.dir {
		panic("WriteAt called on directory.")
	}

	// Update the modification time.
	in.attributes.Mtime = in.clock.Now()

	// Ensure that the contents slice is long enough.
	newLen := int(off) + len(p)
	if len(in.contents) < newLen {
		padding := make([]byte, newLen-len(in.contents))
		in.contents = append(in.contents, padding...)
		in.attributes.Size = uint64(newLen)
	}

	// Copy in the data.
	n = copy(in.contents[off:], p)

	// Sanity check.
	if n != len(p) {
		panic(fmt.Sprintf("Unexpected short copy: %v", n))
	}

	return
}

// Update attributes from non-nil parameters.
//
// LOCKS_REQUIRED(in.mu)
func (in *inode) SetAttributes(
	size *uint64,
	mode *os.FileMode,
	mtime *time.Time) {
	// Update the modification time.
	in.attributes.Mtime = in.clock.Now()

	// Truncate?
	if size != nil {
		intSize := int(*size)

		// Update contents.
		if intSize <= len(in.contents) {
			in.contents = in.contents[:intSize]
		} else {
			padding := make([]byte, intSize-len(in.contents))
			in.contents = append(in.contents, padding...)
		}

		// Update attributes.
		in.attributes.Size = *size
	}

	// Change mode?
	if mode != nil {
		in.attributes.Mode = *mode
	}

	// Change mtime?
	if mtime != nil {
		in.attributes.Mtime = *mtime
	}
}
<commit_msg>Fixed more build errors.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package memfs

import (
	"fmt"
	"io"
	"os"
	"time"

	"github.com/googlecloudplatform/gcsfuse/timeutil"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/gcloud/syncutil"
)

// Common attributes for files and directories.
type inode struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	clock timeutil.Clock

	/////////////////////////
	// Mutable state
	/////////////////////////

	mu syncutil.InvariantMutex

	// The current attributes of this inode.
	//
	// INVARIANT: attrs.Mode &^ (os.ModePerm|os.ModeDir|os.ModeSymlink) == 0
	// INVARIANT: !(isDir() && isSymlink())
	// INVARIANT: attrs.Size == len(contents)
	attrs fuseops.InodeAttributes // GUARDED_BY(mu)

	// For directories, entries describing the children of the directory. Unused
	// entries are of type DT_Unknown.
	//
	// This array can never be shortened, nor can its elements be moved, because
	// we use its indices for Dirent.Offset, which is exposed to the user who
	// might be calling readdir in a loop while concurrently modifying the
	// directory. Unused entries can, however, be reused.
	//
	// INVARIANT: If !isDir(), len(entries) == 0
	// INVARIANT: For each i, entries[i].Offset == i+1
	// INVARIANT: Contains no duplicate names in used entries.
	entries []fuseutil.Dirent // GUARDED_BY(mu)

	// For files, the current contents of the file.
	//
	// INVARIANT: If !isFile(), len(contents) == 0
	contents []byte // GUARDED_BY(mu)

	// For symlinks, the target of the symlink.
	//
	// INVARIANT: If !isSymlink(), len(target) == 0
	//
	// GUARDED_BY(mu)
	target string
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// Create a new inode with the supplied attributes, which need not contain
// time-related information (the inode object will take care of that).
func newInode(
	clock timeutil.Clock,
	attrs fuseops.InodeAttributes) (in *inode) {
	// Update time info.
	now := clock.Now()
	attrs.Mtime = now
	attrs.Crtime = now

	// Create the object.
	in = &inode{
		clock: clock,
		attrs: attrs,
	}

	in.mu = syncutil.NewInvariantMutex(in.checkInvariants)
	return
}

func (in *inode) checkInvariants() {
	// INVARIANT: attrs.Mode &^ (os.ModePerm|os.ModeDir|os.ModeSymlink) == 0
	if !(in.attrs.Mode&^(os.ModePerm|os.ModeDir|os.ModeSymlink) == 0) {
		panic(fmt.Sprintf("Unexpected mode: %v", in.attrs.Mode))
	}

	// INVARIANT: !(isDir() && isSymlink())
	if in.isDir() && in.isSymlink() {
		panic(fmt.Sprintf("Unexpected mode: %v", in.attrs.Mode))
	}

	// INVARIANT: attrs.Size == len(contents)
	if in.attrs.Size != uint64(len(in.contents)) {
		panic(fmt.Sprintf(
			"Size mismatch: %d vs. %d",
			in.attrs.Size,
			len(in.contents)))
	}

	// INVARIANT: If !isDir(), len(entries) == 0
	if !in.isDir() && len(in.entries) != 0 {
		panic(fmt.Sprintf("Unexpected entries length: %d", len(in.entries)))
	}

	// INVARIANT: For each i, entries[i].Offset == i+1
	for i, e := range in.entries {
		if !(e.Offset == fuseops.DirOffset(i+1)) {
			panic(fmt.Sprintf("Unexpected offset for index %d: %d", i, e.Offset))
		}
	}

	// INVARIANT: Contains no duplicate names in used entries.
	childNames := make(map[string]struct{})
	for i, e := range in.entries {
		if e.Type != fuseutil.DT_Unknown {
			if _, ok := childNames[e.Name]; ok {
				panic(fmt.Sprintf("Duplicate name: %s", e.Name))
			}

			childNames[e.Name] = struct{}{}
		}
	}

	// INVARIANT: If !isFile(), len(contents) == 0
	if !in.isFile() && len(in.contents) != 0 {
		panic(fmt.Sprintf("Unexpected length: %d", len(in.contents)))
	}

	// INVARIANT: If !isSymlink(), len(target) == 0
	if !in.isSymlink() && len(in.target) != 0 {
		panic(fmt.Sprintf("Unexpected target length: %d", len(in.target)))
	}

	return
}

// LOCKS_REQUIRED(in.mu)
func (in *inode) isDir() bool {
	return in.attrs.Mode&os.ModeDir != 0
}

// LOCKS_REQUIRED(in.mu)
func (in *inode) isSymlink() bool {
	return in.attrs.Mode&os.ModeSymlink != 0
}

// LOCKS_REQUIRED(in.mu)
func (in *inode) isFile() bool {
	return !(in.isDir() || in.isSymlink())
}

////////////////////////////////////////////////////////////////////////
// Public methods
////////////////////////////////////////////////////////////////////////

// Return the number of children of the directory.
//
// REQUIRES: in.isDir()
// LOCKS_REQUIRED(in.mu)
func (in *inode) Len() (n int) {
	for _, e := range in.entries {
		if e.Type != fuseutil.DT_Unknown {
			n++
		}
	}

	return
}

// Find an entry for the given child name and return its inode ID.
//
// REQUIRES: in.isDir()
// LOCKS_REQUIRED(in.mu)
func (in *inode) LookUpChild(name string) (id fuseops.InodeID, ok bool) {
	index, ok := in.findChild(name)
	if ok {
		id = in.entries[index].Inode
	}

	return
}

// Add an entry for a child.
//
// REQUIRES: in.isDir()
// REQUIRES: dt != fuseutil.DT_Unknown
// LOCKS_REQUIRED(in.mu)
func (in *inode) AddChild(
	id fuseops.InodeID,
	name string,
	dt fuseutil.DirentType) {
	var index int

	// Update the modification time.
	in.attrs.Mtime = in.clock.Now()

	// No matter where we place the entry, make sure it has the correct Offset
	// field.
	defer func() {
		in.entries[index].Offset = fuseops.DirOffset(index + 1)
	}()

	// Set up the entry.
	e := fuseutil.Dirent{
		Inode: id,
		Name:  name,
		Type:  dt,
	}

	// Look for a gap in which we can insert it.
	for index = range in.entries {
		if in.entries[index].Type == fuseutil.DT_Unknown {
			in.entries[index] = e
			return
		}
	}

	// Append it to the end.
	index = len(in.entries)
	in.entries = append(in.entries, e)
}

// Remove an entry for a child.
//
// REQUIRES: in.isDir()
// REQUIRES: An entry for the given name exists.
// LOCKS_REQUIRED(in.mu)
func (in *inode) RemoveChild(name string) {
	// Update the modification time.
	in.attrs.Mtime = in.clock.Now()

	// Find the entry.
	i, ok := in.findChild(name)
	if !ok {
		panic(fmt.Sprintf("Unknown child: %s", name))
	}

	// Mark it as unused.
	in.entries[i] = fuseutil.Dirent{
		Type:   fuseutil.DT_Unknown,
		Offset: fuseops.DirOffset(i + 1),
	}
}

// Serve a ReadDir request.
//
// REQUIRES: in.isDir()
// LOCKS_REQUIRED(in.mu)
func (in *inode) ReadDir(offset int, size int) (data []byte, err error) {
	if !in.dir {
		panic("ReadDir called on non-directory.")
	}

	for i := offset; i < len(in.entries); i++ {
		e := in.entries[i]

		// Skip unused entries.
		if e.Type == fuseutil.DT_Unknown {
			continue
		}

		data = fuseutil.AppendDirent(data, in.entries[i])

		// Trim and stop early if we've exceeded the requested size.
		if len(data) > size {
			data = data[:size]
			break
		}
	}

	return
}

// Read from the file's contents. See documentation for ioutil.ReaderAt.
//
// REQUIRES: in.isFile()
// LOCKS_REQUIRED(in.mu)
func (in *inode) ReadAt(p []byte, off int64) (n int, err error) {
	if in.dir {
		panic("ReadAt called on directory.")
	}

	// Ensure the offset is in range.
	if off > int64(len(in.contents)) {
		err = io.EOF
		return
	}

	// Read what we can.
	n = copy(p, in.contents[off:])
	if n < len(p) {
		err = io.EOF
	}

	return
}

// Write to the file's contents. See documentation for ioutil.WriterAt.
//
// REQUIRES: in.isFile()
// LOCKS_REQUIRED(in.mu)
func (in *inode) WriteAt(p []byte, off int64) (n int, err error) {
	if in.dir {
		panic("WriteAt called on directory.")
	}

	// Update the modification time.
	in.attrs.Mtime = in.clock.Now()

	// Ensure that the contents slice is long enough.
	newLen := int(off) + len(p)
	if len(in.contents) < newLen {
		padding := make([]byte, newLen-len(in.contents))
		in.contents = append(in.contents, padding...)
		in.attrs.Size = uint64(newLen)
	}

	// Copy in the data.
	n = copy(in.contents[off:], p)

	// Sanity check.
	if n != len(p) {
		panic(fmt.Sprintf("Unexpected short copy: %v", n))
	}

	return
}

// Update attributes from non-nil parameters.
//
// LOCKS_REQUIRED(in.mu)
func (in *inode) SetAttributes(
	size *uint64,
	mode *os.FileMode,
	mtime *time.Time) {
	// Update the modification time.
	in.attrs.Mtime = in.clock.Now()

	// Truncate?
	if size != nil {
		intSize := int(*size)

		// Update contents.
		if intSize <= len(in.contents) {
			in.contents = in.contents[:intSize]
		} else {
			padding := make([]byte, intSize-len(in.contents))
			in.contents = append(in.contents, padding...)
		}

		// Update attributes.
		in.attrs.Size = *size
	}

	// Change mode?
	if mode != nil {
		in.attrs.Mode = *mode
	}

	// Change mtime?
	if mtime != nil {
		in.attrs.Mtime = *mtime
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Jetstack cert-manager contributors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package akamai

import (
	"bytes"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"
	"unicode"
)

// EdgeGridAuth holds all values required to perform Akamai API Client Authentication.
// See https://developer.akamai.com/introduction/Client_Auth.html.
type EdgeGridAuth struct {
	ClientToken   string
	ClientSecret  string
	AccessToken   string
	HeadersToSign []string
	MaxBody       int

	now         func() time.Time
	createNonce func() (string, error)
}

type signingData struct {
	timestamp  string
	authHeader string
	dataToSign string
}

// edgeGridAuthTimeFormat is used for timestamps in request signatures.
const edgeGridAuthTimeFormat = "20060102T15:04:05-0700" // yyyyMMddTHH:mm:ss+0000

const NoMaxBody = -1

// NewEdgeGridAuth returns a new request signer for Akamai EdgeGrid
func NewEdgeGridAuth(clientToken, clientSecret, accessToken string, headersToSign ...string) *EdgeGridAuth {
	return &EdgeGridAuth{
		ClientToken:   clientToken,
		ClientSecret:  clientSecret,
		AccessToken:   accessToken,
		HeadersToSign: headersToSign,
		MaxBody:       NoMaxBody,

		now:         time.Now,
		createNonce: createRandomNonce,
	}
}

// SignRequest calculates the signature for Akamai Open API and adds it as the Authorization header.
// The Authorization header starts with the signing algorithm moniker (name of the algorithm) used to sign the request.
// The moniker below identifies EdgeGrid V1, hash message authentication code, SHA–256 as the hash standard.
// This moniker is then followed by a space and an ordered list of name value pairs with each field separated by a semicolon.
func (e *EdgeGridAuth) SignRequest(req *http.Request) error {
	signingData, err := e.signingData(req)
	if err != nil {
		return err
	}

	req.Header.Set("Authorization", fmt.Sprintf(
		"%ssignature=%s",
		signingData.authHeader,
		e.calculateRequestSignature(signingData)))

	return nil
}

func (e *EdgeGridAuth) calculateRequestSignature(signingData *signingData) string {
	return computeSignature(
		signingData.dataToSign,
		e.signingKey(signingData.timestamp))
}

func (e *EdgeGridAuth) signingData(req *http.Request) (*signingData, error) {
	nonce, err := e.createNonce()
	if err != nil {
		return nil, err
	}

	timestamp := e.now().UTC().Format(edgeGridAuthTimeFormat)
	authHeader := fmt.Sprintf("EG1-HMAC-SHA256 client_token=%s;access_token=%s;timestamp=%s;nonce=%s;",
		e.ClientToken,
		e.AccessToken,
		timestamp,
		nonce)

	return &signingData{
		timestamp:  timestamp,
		authHeader: authHeader,
		dataToSign: e.dataToSign(req, authHeader),
	}, nil
}

// dataToSign includes the information from the HTTP request that is relevant to ensuring that the request is authentic.
// This data set comprised of the request data combined with the authorization header value (excluding the signature field,
// but including the ; right before the signature field).
func (e *EdgeGridAuth) dataToSign(req *http.Request, authHeader string) string {
	var buffer bytes.Buffer

	buffer.WriteString(req.Method)
	buffer.WriteRune('\t')
	buffer.WriteString(req.URL.Scheme)
	buffer.WriteRune('\t')
	buffer.WriteString(req.URL.Host)
	buffer.WriteRune('\t')
	buffer.WriteString(relativeURL(req.URL))
	buffer.WriteRune('\t')
	buffer.WriteString(e.canonicalizedHeaders(req))
	buffer.WriteRune('\t')
	buffer.WriteString(e.computeBodyHash(req))
	buffer.WriteRune('\t')
	buffer.WriteString(authHeader)

	return buffer.String()
}

// signingKey is derived from the client secret.
// The signing key is computed as the base64 encoding of the SHA–256 HMAC of the timestamp string
// (the field value included in the HTTP authorization header described above) with the client secret as the key.
func (e *EdgeGridAuth) signingKey(timestamp string) string {
	return computeSignature(timestamp, e.ClientSecret)
}

// realtiveURL is the part of the URL that starts from the root path and includes the query string, with the handling of following special cases:
// If the path is null or empty, set it to / (forward-slash).
// If the path does not start with /, add / to the beginning.
func relativeURL(url *url.URL) string {
	relativeURL := url.Path
	if relativeURL == "" {
		return "/"
	}

	if relativeURL[0] != '/' {
		relativeURL = "/" + relativeURL
	}

	if url.RawQuery != "" {
		relativeURL += "?"
		relativeURL += url.RawQuery
	}

	return relativeURL
}

// computeBodyHash returns the base64-encoded SHA–256 hash of the POST body.
// For any other request methods, this field is empty. But the tac separator (\t) must be included.
// The size of the POST body must be less than or equal to the value specified by the service.
// Any request that does not meet this criteria SHOULD be rejected during the signing process,
// as the request will be rejected by EdgeGrid.
func (e *EdgeGridAuth) computeBodyHash(req *http.Request) string {
	if req.Body != nil {
		bodyBytes, _ := ioutil.ReadAll(req.Body)
		req.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

		if req.Method == http.MethodPost && len(bodyBytes) > 0 {
			dataToHash := bodyBytes
			if e.MaxBody != NoMaxBody && len(dataToHash) > e.MaxBody {
				dataToHash = dataToHash[0:e.MaxBody]
			}
			sha256Sum := sha256.Sum256(dataToHash)
			return base64.StdEncoding.EncodeToString(sha256Sum[:])
		}
	}

	return ""
}

// canonicalizedHeaders returns the request headers as a canonicalized string.
//
// The protocol does not support multiple request headers with the same header name.
// Such requests SHOULD be rejected during the signing process. Otherwise, EdgeGrid
// will not produce the intended results by rejecting such requests or removing all
// (but one) duplicated headers.
//
//    Header names are case-insensitive per rfc2616.
//
// For each entry in the list of headers designated by the service provider to include
// in the signature in the specified order, the canonicalization of the request header
// is done as follows:
//
//    Get the first header value for the name.
//    Trim the leading and trailing white spaces.
//    Replace all repeated white spaces with a single space.
//    Concatenate the name:value pairs with the tab (\t) separator (name field is all in lower case).
//    Terminate the headers with another tab (\t) separator.
//
// NOTE: The canonicalized data is used for creating the signature only, as this step
// might alter the header value. If a header in the list is not present in the request,
// or the header value is empty, nothing for that header, neither the name nor the tab
// separator, may be included.
func (e *EdgeGridAuth) canonicalizedHeaders(req *http.Request) string {
	if len(e.HeadersToSign) < 1 {
		return ""
	}

	var headerNamesToSign []string
	for headerName := range req.Header {
		for _, sign := range e.HeadersToSign {
			if strings.EqualFold(sign, headerName) {
				headerNamesToSign = append(headerNamesToSign, headerName)
				break
			}
		}
	}

	if len(headerNamesToSign) < 1 {
		return ""
	}

	sort.Strings(headerNamesToSign)

	var buffer bytes.Buffer
	for _, headerName := range headerNamesToSign {
		for _, c := range headerName {
			buffer.WriteRune(unicode.ToLower(c))
		}

		buffer.WriteRune(':')

		white := false
		empty := true
		for _, c := range req.Header.Get(headerName) {
			if unicode.IsSpace(c) {
				white = true
			} else {
				if white && !empty {
					buffer.WriteRune(' ')
				}
				buffer.WriteRune(unicode.ToLower(c))
				empty = false
				white = false
			}
		}

		buffer.WriteRune('\t')
	}

	return buffer.String()
}

// calculateSignature is the base64-encoding of the SHA–256 HMAC of the data to sign with the signing key.
func computeSignature(message string, secret string) string {
	key := []byte(secret)
	h := hmac.New(sha256.New, key)
	h.Write([]byte(message))
	return base64.StdEncoding.EncodeToString(h.Sum(nil))
}

func createRandomNonce() (string, error) {
	bytes := make([]byte, 18)
	_, err := rand.Read(bytes)
	if err != nil {
		return "", err
	}

	return base64.URLEncoding.EncodeToString(bytes), nil
}
<commit_msg>spelling: relative<commit_after>/*
Copyright 2019 The Jetstack cert-manager contributors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package akamai

import (
	"bytes"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"
	"unicode"
)

// EdgeGridAuth holds all values required to perform Akamai API Client Authentication.
// See https://developer.akamai.com/introduction/Client_Auth.html.
type EdgeGridAuth struct {
	ClientToken   string
	ClientSecret  string
	AccessToken   string
	HeadersToSign []string
	MaxBody       int

	now         func() time.Time
	createNonce func() (string, error)
}

type signingData struct {
	timestamp  string
	authHeader string
	dataToSign string
}

// edgeGridAuthTimeFormat is used for timestamps in request signatures.
const edgeGridAuthTimeFormat = "20060102T15:04:05-0700" // yyyyMMddTHH:mm:ss+0000

const NoMaxBody = -1

// NewEdgeGridAuth returns a new request signer for Akamai EdgeGrid
func NewEdgeGridAuth(clientToken, clientSecret, accessToken string, headersToSign ...string) *EdgeGridAuth {
	return &EdgeGridAuth{
		ClientToken:   clientToken,
		ClientSecret:  clientSecret,
		AccessToken:   accessToken,
		HeadersToSign: headersToSign,
		MaxBody:       NoMaxBody,

		now:         time.Now,
		createNonce: createRandomNonce,
	}
}

// SignRequest calculates the signature for Akamai Open API and adds it as the Authorization header.
// The Authorization header starts with the signing algorithm moniker (name of the algorithm) used to sign the request.
// The moniker below identifies EdgeGrid V1, hash message authentication code, SHA–256 as the hash standard.
// This moniker is then followed by a space and an ordered list of name value pairs with each field separated by a semicolon.
func (e *EdgeGridAuth) SignRequest(req *http.Request) error {
	signingData, err := e.signingData(req)
	if err != nil {
		return err
	}

	req.Header.Set("Authorization", fmt.Sprintf(
		"%ssignature=%s",
		signingData.authHeader,
		e.calculateRequestSignature(signingData)))

	return nil
}

func (e *EdgeGridAuth) calculateRequestSignature(signingData *signingData) string {
	return computeSignature(
		signingData.dataToSign,
		e.signingKey(signingData.timestamp))
}

func (e *EdgeGridAuth) signingData(req *http.Request) (*signingData, error) {
	nonce, err := e.createNonce()
	if err != nil {
		return nil, err
	}

	timestamp := e.now().UTC().Format(edgeGridAuthTimeFormat)
	authHeader := fmt.Sprintf("EG1-HMAC-SHA256 client_token=%s;access_token=%s;timestamp=%s;nonce=%s;",
		e.ClientToken,
		e.AccessToken,
		timestamp,
		nonce)

	return &signingData{
		timestamp:  timestamp,
		authHeader: authHeader,
		dataToSign: e.dataToSign(req, authHeader),
	}, nil
}

// dataToSign includes the information from the HTTP request that is relevant to ensuring that the request is authentic.
// This data set comprised of the request data combined with the authorization header value (excluding the signature field,
// but including the ; right before the signature field).
func (e *EdgeGridAuth) dataToSign(req *http.Request, authHeader string) string {
	var buffer bytes.Buffer

	buffer.WriteString(req.Method)
	buffer.WriteRune('\t')
	buffer.WriteString(req.URL.Scheme)
	buffer.WriteRune('\t')
	buffer.WriteString(req.URL.Host)
	buffer.WriteRune('\t')
	buffer.WriteString(relativeURL(req.URL))
	buffer.WriteRune('\t')
	buffer.WriteString(e.canonicalizedHeaders(req))
	buffer.WriteRune('\t')
	buffer.WriteString(e.computeBodyHash(req))
	buffer.WriteRune('\t')
	buffer.WriteString(authHeader)

	return buffer.String()
}

// signingKey is derived from the client secret.
// The signing key is computed as the base64 encoding of the SHA–256 HMAC of the timestamp string
// (the field value included in the HTTP authorization header described above) with the client secret as the key.
func (e *EdgeGridAuth) signingKey(timestamp string) string {
	return computeSignature(timestamp, e.ClientSecret)
}

// relativeURL is the part of the URL that starts from the root path and includes the query string, with the handling of following special cases:
// If the path is null or empty, set it to / (forward-slash).
// If the path does not start with /, add / to the beginning.
func relativeURL(url *url.URL) string {
	relativeURL := url.Path
	if relativeURL == "" {
		return "/"
	}

	if relativeURL[0] != '/' {
		relativeURL = "/" + relativeURL
	}

	if url.RawQuery != "" {
		relativeURL += "?"
		relativeURL += url.RawQuery
	}

	return relativeURL
}

// computeBodyHash returns the base64-encoded SHA–256 hash of the POST body.
// For any other request methods, this field is empty. But the tac separator (\t) must be included.
// The size of the POST body must be less than or equal to the value specified by the service.
// Any request that does not meet this criteria SHOULD be rejected during the signing process,
// as the request will be rejected by EdgeGrid.
func (e *EdgeGridAuth) computeBodyHash(req *http.Request) string {
	if req.Body != nil {
		bodyBytes, _ := ioutil.ReadAll(req.Body)
		req.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

		if req.Method == http.MethodPost && len(bodyBytes) > 0 {
			dataToHash := bodyBytes
			if e.MaxBody != NoMaxBody && len(dataToHash) > e.MaxBody {
				dataToHash = dataToHash[0:e.MaxBody]
			}
			sha256Sum := sha256.Sum256(dataToHash)
			return base64.StdEncoding.EncodeToString(sha256Sum[:])
		}
	}

	return ""
}

// canonicalizedHeaders returns the request headers as a canonicalized string.
//
// The protocol does not support multiple request headers with the same header name.
// Such requests SHOULD be rejected during the signing process. Otherwise, EdgeGrid
// will not produce the intended results by rejecting such requests or removing all
// (but one) duplicated headers.
//
//    Header names are case-insensitive per rfc2616.
//
// For each entry in the list of headers designated by the service provider to include
// in the signature in the specified order, the canonicalization of the request header
// is done as follows:
//
//    Get the first header value for the name.
//    Trim the leading and trailing white spaces.
//    Replace all repeated white spaces with a single space.
//    Concatenate the name:value pairs with the tab (\t) separator (name field is all in lower case).
//    Terminate the headers with another tab (\t) separator.
//
// NOTE: The canonicalized data is used for creating the signature only, as this step
// might alter the header value. If a header in the list is not present in the request,
// or the header value is empty, nothing for that header, neither the name nor the tab
// separator, may be included.
func (e *EdgeGridAuth) canonicalizedHeaders(req *http.Request) string {
	if len(e.HeadersToSign) < 1 {
		return ""
	}

	var headerNamesToSign []string
	for headerName := range req.Header {
		for _, sign := range e.HeadersToSign {
			if strings.EqualFold(sign, headerName) {
				headerNamesToSign = append(headerNamesToSign, headerName)
				break
			}
		}
	}

	if len(headerNamesToSign) < 1 {
		return ""
	}

	sort.Strings(headerNamesToSign)

	var buffer bytes.Buffer
	for _, headerName := range headerNamesToSign {
		for _, c := range headerName {
			buffer.WriteRune(unicode.ToLower(c))
		}

		buffer.WriteRune(':')

		white := false
		empty := true
		for _, c := range req.Header.Get(headerName) {
			if unicode.IsSpace(c) {
				white = true
			} else {
				if white && !empty {
					buffer.WriteRune(' ')
				}
				buffer.WriteRune(unicode.ToLower(c))
				empty = false
				white = false
			}
		}

		buffer.WriteRune('\t')
	}

	return buffer.String()
}

// calculateSignature is the base64-encoding of the SHA–256 HMAC of the data to sign with the signing key.
func computeSignature(message string, secret string) string {
	key := []byte(secret)
	h := hmac.New(sha256.New, key)
	h.Write([]byte(message))
	return base64.StdEncoding.EncodeToString(h.Sum(nil))
}

func createRandomNonce() (string, error) {
	bytes := make([]byte, 18)
	_, err := rand.Read(bytes)
	if err != nil {
		return "", err
	}

	return base64.URLEncoding.EncodeToString(bytes), nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package allocate

import (
	"context"
	"sync"

	"github.com/golang/glog"
	"k8s.io/client-go/util/workqueue"

	"github.com/kubernetes-sigs/kube-batch/pkg/scheduler/api"
	"github.com/kubernetes-sigs/kube-batch/pkg/scheduler/framework"
	"github.com/kubernetes-sigs/kube-batch/pkg/scheduler/util"
)

type allocateAction struct {
	ssn *framework.Session
}

func New() *allocateAction {
	return &allocateAction{}
}

func (alloc *allocateAction) Name() string {
	return "allocate"
}

func (alloc *allocateAction) Initialize() {}

func (alloc *allocateAction) Execute(ssn *framework.Session) {
	glog.V(3).Infof("Enter Allocate ...")
	defer glog.V(3).Infof("Leaving Allocate ...")

	queues := util.NewPriorityQueue(ssn.QueueOrderFn)
	jobsMap := map[api.QueueID]*util.PriorityQueue{}

	for _, job := range ssn.Jobs {
		if queue, found := ssn.Queues[job.Queue]; found {
			queues.Push(queue)
		} else {
			glog.Warningf("Skip adding Job <%s/%s> because its queue %s is not found",
				job.Namespace, job.Name, job.Queue)
			continue
		}

		if _, found := jobsMap[job.Queue]; !found {
			jobsMap[job.Queue] = util.NewPriorityQueue(ssn.JobOrderFn)
		}

		glog.V(4).Infof("Added Job <%s/%s> into Queue <%s>", job.Namespace, job.Name, job.Queue)
		jobsMap[job.Queue].Push(job)
	}

	glog.V(3).Infof("Try to allocate resource to %d Queues", len(jobsMap))

	pendingTasks := map[api.JobID]*util.PriorityQueue{}

	var allNodes []*api.NodeInfo
	for _, v := range ssn.Nodes {
		allNodes = append(allNodes, v)
	}

	for {
		if queues.Empty() {
			break
		}

		queue := queues.Pop().(*api.QueueInfo)
		if ssn.Overused(queue) {
			glog.V(3).Infof("Queue <%s> is overused, ignore it.", queue.Name)
			continue
		}

		jobs, found := jobsMap[queue.UID]

		glog.V(3).Infof("Try to allocate resource to Jobs in Queue <%v>", queue.Name)

		if !found || jobs.Empty() {
			glog.V(4).Infof("Can not find jobs for queue %s.", queue.Name)
			continue
		}

		job := jobs.Pop().(*api.JobInfo)
		if _, found := pendingTasks[job.UID]; !found {
			tasks := util.NewPriorityQueue(ssn.TaskOrderFn)
			for _, task := range job.TaskStatusIndex[api.Pending] {
				// Skip BestEffort task in 'allocate' action.
				if task.Resreq.IsEmpty() {
					glog.V(4).Infof("Task <%v/%v> is BestEffort task, skip it.",
						task.Namespace, task.Name)
					continue
				}

				tasks.Push(task)
			}
			pendingTasks[job.UID] = tasks
		}
		tasks := pendingTasks[job.UID]

		glog.V(3).Infof("Try to allocate resource to %d tasks of Job <%v/%v>",
			tasks.Len(), job.Namespace, job.Name)

		for !tasks.Empty() {
			predicateNodes := []*api.NodeInfo{}
			nodeScores := map[int][]*api.NodeInfo{}

			task := tasks.Pop().(*api.TaskInfo)
			assigned := false

			glog.V(3).Infof("There are <%d> nodes for Job <%v/%v>",
				len(ssn.Nodes), job.Namespace, job.Name)

			//any task that doesn't fit will be the last processed
			//within this loop context so any existing contents of
			//NodesFitDelta are for tasks that eventually did fit on a
			//node
			if len(job.NodesFitDelta) > 0 {
				job.NodesFitDelta = make(api.NodeResourceMap)
			}

			var workerLock sync.Mutex
			checkNode := func(index int) {
				node := allNodes[index]
				glog.V(3).Infof("Considering Task <%v/%v> on node <%v>: <%v> vs. <%v>",
					task.Namespace, task.Name, node.Name, task.Resreq, node.Idle)

				// TODO (k82cn): Enable eCache for performance improvement.
				if err := ssn.PredicateFn(task, node); err != nil {
					glog.V(3).Infof("Predicates failed for task <%s/%s> on node <%s>: %v",
						task.Namespace, task.Name, node.Name, err)
				} else {
					workerLock.Lock()
					predicateNodes = append(predicateNodes, node)
					workerLock.Unlock()
				}
			}
			workqueue.ParallelizeUntil(context.TODO(), 16, len(allNodes), checkNode)

			scoreNode := func(index int) {
				node := predicateNodes[index]
				score, err := ssn.NodeOrderFn(task, node)
				if err != nil {
					glog.V(3).Infof("Error in Calculating Priority for the node:%v", err)
				} else {
					workerLock.Lock()
					nodeScores[score] = append(nodeScores[score], node)
					workerLock.Unlock()
				}
			}
			workqueue.ParallelizeUntil(context.TODO(), 16, len(predicateNodes), scoreNode)

			selectedNodes := util.SelectBestNode(nodeScores)
			for _, node := range selectedNodes {
				// Allocate idle resource to the task.
				if task.InitResreq.LessEqual(node.Idle) {
					glog.V(3).Infof("Binding Task <%v/%v> to node <%v>",
						task.Namespace, task.Name, node.Name)
					if err := ssn.Allocate(task, node.Name); err != nil {
						glog.Errorf("Failed to bind Task %v on %v in Session %v, err: %v",
							task.UID, node.Name, ssn.UID, err)
						continue
					}
					assigned = true
					break
				} else {
					//store information about missing resources
					job.NodesFitDelta[node.Name] = node.Idle.Clone()
					job.NodesFitDelta[node.Name].FitDelta(task.Resreq)
					glog.V(3).Infof("Predicates failed for task <%s/%s> on node <%s> with limited resources",
						task.Namespace, task.Name, node.Name)
				}

				// Allocate releasing resource to the task if any.
				if task.InitResreq.LessEqual(node.Releasing) {
					glog.V(3).Infof("Pipelining Task <%v/%v> to node <%v> for <%v> on <%v>",
						task.Namespace, task.Name, node.Name, task.InitResreq, node.Releasing)
					if err := ssn.Pipeline(task, node.Name); err != nil {
						glog.Errorf("Failed to pipeline Task %v on %v in Session %v",
							task.UID, node.Name, ssn.UID)
						continue
					}

					assigned = true
					break
				}
			}

			if !assigned {
				break
			}

			if ssn.JobReady(job) {
				jobs.Push(job)
				break
			}
		}

		// Added Queue back until no job in Queue.
		queues.Push(queue)
	}
}

func (alloc *allocateAction) UnInitialize() {}
<commit_msg>Use scheduler helper functions in Allocate<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package allocate

import (
	"github.com/golang/glog"

	"github.com/kubernetes-sigs/kube-batch/pkg/scheduler/api"
	"github.com/kubernetes-sigs/kube-batch/pkg/scheduler/framework"
	"github.com/kubernetes-sigs/kube-batch/pkg/scheduler/util"
)

type allocateAction struct {
	ssn *framework.Session
}

func New() *allocateAction {
	return &allocateAction{}
}

func (alloc *allocateAction) Name() string {
	return "allocate"
}

func (alloc *allocateAction) Initialize() {}

func (alloc *allocateAction) Execute(ssn *framework.Session) {
	glog.V(3).Infof("Enter Allocate ...")
	defer glog.V(3).Infof("Leaving Allocate ...")

	queues := util.NewPriorityQueue(ssn.QueueOrderFn)
	jobsMap := map[api.QueueID]*util.PriorityQueue{}

	for _, job := range ssn.Jobs {
		if queue, found := ssn.Queues[job.Queue]; found {
			queues.Push(queue)
		} else {
			glog.Warningf("Skip adding Job <%s/%s> because its queue %s is not found",
				job.Namespace, job.Name, job.Queue)
			continue
		}

		if _, found := jobsMap[job.Queue]; !found {
			jobsMap[job.Queue] = util.NewPriorityQueue(ssn.JobOrderFn)
		}

		glog.V(4).Infof("Added Job <%s/%s> into Queue <%s>", job.Namespace, job.Name, job.Queue)
		jobsMap[job.Queue].Push(job)
	}

	glog.V(3).Infof("Try to allocate resource to %d Queues", len(jobsMap))

	pendingTasks := map[api.JobID]*util.PriorityQueue{}

	allNodes := util.GetNodeList(ssn.Nodes)

	for {
		if queues.Empty() {
			break
		}

		queue := queues.Pop().(*api.QueueInfo)
		if ssn.Overused(queue) {
			glog.V(3).Infof("Queue <%s> is overused, ignore it.", queue.Name)
			continue
		}

		jobs, found := jobsMap[queue.UID]

		glog.V(3).Infof("Try to allocate resource to Jobs in Queue <%v>", queue.Name)

		if !found || jobs.Empty() {
			glog.V(4).Infof("Can not find jobs for queue %s.", queue.Name)
			continue
		}

		job := jobs.Pop().(*api.JobInfo)
		if _, found := pendingTasks[job.UID]; !found {
			tasks := util.NewPriorityQueue(ssn.TaskOrderFn)
			for _, task := range job.TaskStatusIndex[api.Pending] {
				// Skip BestEffort task in 'allocate' action.
				if task.Resreq.IsEmpty() {
					glog.V(4).Infof("Task <%v/%v> is BestEffort task, skip it.",
						task.Namespace, task.Name)
					continue
				}

				tasks.Push(task)
			}
			pendingTasks[job.UID] = tasks
		}
		tasks := pendingTasks[job.UID]

		glog.V(3).Infof("Try to allocate resource to %d tasks of Job <%v/%v>",
			tasks.Len(), job.Namespace, job.Name)

		for !tasks.Empty() {
			task := tasks.Pop().(*api.TaskInfo)
			assigned := false

			glog.V(3).Infof("There are <%d> nodes for Job <%v/%v>",
				len(ssn.Nodes), job.Namespace, job.Name)

			//any task that doesn't fit will be the last processed
			//within this loop context so any existing contents of
			//NodesFitDelta are for tasks that eventually did fit on a
			//node
			if len(job.NodesFitDelta) > 0 {
				job.NodesFitDelta = make(api.NodeResourceMap)
			}

			predicateNodes := util.FindNodesThatFit(task, allNodes, ssn.PredicateFn)

			nodeScores := util.PrioritizeNodes(task, predicateNodes, ssn.NodeOrderFn)

			selectedNodes := util.SelectBestNode(nodeScores)
			for _, node := range selectedNodes {
				// Allocate idle resource to the task.
				if task.InitResreq.LessEqual(node.Idle) {
					glog.V(3).Infof("Binding Task <%v/%v> to node <%v>",
						task.Namespace, task.Name, node.Name)
					if err := ssn.Allocate(task, node.Name); err != nil {
						glog.Errorf("Failed to bind Task %v on %v in Session %v, err: %v",
							task.UID, node.Name, ssn.UID, err)
						continue
					}
					assigned = true
					break
				} else {
					//store information about missing resources
					job.NodesFitDelta[node.Name] = node.Idle.Clone()
					job.NodesFitDelta[node.Name].FitDelta(task.Resreq)
					glog.V(3).Infof("Predicates failed for task <%s/%s> on node <%s> with limited resources",
						task.Namespace, task.Name, node.Name)
				}

				// Allocate releasing resource to the task if any.
				if task.InitResreq.LessEqual(node.Releasing) {
					glog.V(3).Infof("Pipelining Task <%v/%v> to node <%v> for <%v> on <%v>",
						task.Namespace, task.Name, node.Name, task.InitResreq, node.Releasing)
					if err := ssn.Pipeline(task, node.Name); err != nil {
						glog.Errorf("Failed to pipeline Task %v on %v in Session %v",
							task.UID, node.Name, ssn.UID)
						continue
					}

					assigned = true
					break
				}
			}

			if !assigned {
				break
			}

			if ssn.JobReady(job) {
				jobs.Push(job)
				break
			}
		}

		// Added Queue back until no job in Queue.
		queues.Push(queue)
	}
}

func (alloc *allocateAction) UnInitialize() {}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/user"
	"path"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/route53"
)

type recordSet struct {
	Name         string
	Value        string // ip
	Type         string
	TTL          int64
	HostedZoneId string
}

const progName = "dyndns53"

func main() {
	log.SetPrefix(progName + ": ")
	log.SetFlags(0)

	var recSet recordSet
	var logFn string
	flag.StringVar(&recSet.Name, "name", "", "record set name (domain)")
	flag.StringVar(&recSet.Type, "type", "A", `record set type; "A" or "AAAA"`)
	flag.Int64Var(&recSet.TTL, "ttl", 300, "TTL (time to live) in seconds")
	flag.StringVar(&recSet.HostedZoneId, "zone", "", "hosted zone id")
	flag.StringVar(&logFn, "log", "", "file name to log to (default is stdout)")
	if len(os.Args) == 1 {
		flag.Usage()
		os.Exit(1)
	}
	flag.Parse()

	recSet.Name = strings.TrimSuffix(recSet.Name, ".") + "." // append . if missing
	if err := recSet.validate(); err != nil {
		log.Fatal(err)
	}

	if logFn != "" {
		f, err := os.OpenFile(logFn, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			log.Fatal(err)
		}
		defer f.Close()

		log.SetFlags(log.LstdFlags) // restore standard flags
		log.SetOutput(f)            // log to file
	}

	var err error
	recSet.Value, err = getCurrentIP()
	if err != nil {
		log.Fatal(err)
	}

	domain := strings.TrimSuffix(recSet.Name, ".")
	if domainResolvesToIP(domain, recSet.Value) {
		log.Fatalf("%s already resolves to %s; nothing to do", domain, recSet.Value)
	}

	resp, err := recSet.upsert()
	if err != nil {
		log.Fatal(err)
	}

	log.Println(resp)
}

func getCurrentIP() (string, error) {
	resp, err := http.Get("http://checkip.amazonaws.com/")
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	ip := strings.TrimSpace(string(body))
	return ip, nil
}

func domainResolvesToIP(domain, checkIP string) bool {
	ips, err := net.LookupIP(domain)
	if err != nil {
		return false
	}
	for _, ip := range ips {
		if ip.String() == checkIP {
			return true
		}
	}
	return false
}

func (rs *recordSet) upsert() (*route53.ChangeResourceRecordSetsOutput, error) {
	usr, err := user.Current()
	if err != nil {
		return nil, err
	}
	credentialsPath := path.Join(usr.HomeDir, ".aws", "credentials")
	credentials := credentials.NewSharedCredentials(credentialsPath, progName)

	sess, err := session.NewSession()
	if err != nil {
		return nil, err
	}

	svc := route53.New(sess, &aws.Config{Credentials: credentials})

	params := &route53.ChangeResourceRecordSetsInput{
		ChangeBatch: &route53.ChangeBatch{
			Changes: []*route53.Change{
				{
					Action: aws.String("UPSERT"),
					ResourceRecordSet: &route53.ResourceRecordSet{
						Name: aws.String(rs.Name),
						Type: aws.String(rs.Type),
						TTL:  aws.Int64(rs.TTL),
						ResourceRecords: []*route53.ResourceRecord{
							{
								Value: aws.String(rs.Value),
							},
						},
					},
				},
			},
		},
		HostedZoneId: aws.String(rs.HostedZoneId),
	}
	resp, err := svc.ChangeResourceRecordSets(params)
	if err != nil {
		return nil, err
	}

	return resp, nil
}

func (rs *recordSet) validate() error {
	if rs.Name == "" {
		return fmt.Errorf("missing record set name")
	}

	if !strings.HasSuffix(rs.Name, ".") {
		return fmt.Errorf(`record set name must end with a "."`)
	}

	if rs.Type == "" {
		return fmt.Errorf("missing record set type")
	}

	if rs.Type != "A" && rs.Type != "AAAA" {
		return fmt.Errorf("invalid record set type: %s", rs.Type)
	}

	if rs.TTL < 1 {
		return fmt.Errorf("invalid record set TTL: %d", rs.TTL)
	}

	if rs.HostedZoneId == "" {
		return fmt.Errorf("missing hosted zone id")
	}

	return nil
}
<commit_msg>Provide context in error messages<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/user"
	"path"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/route53"
)

type recordSet struct {
	Name         string
	Value        string // ip
	Type         string
	TTL          int64
	HostedZoneId string
}

const progName = "dyndns53"

func main() {
	log.SetPrefix(progName + ": ")
	log.SetFlags(0)

	var recSet recordSet
	var logFn string
	flag.StringVar(&recSet.Name, "name", "", "record set name (domain)")
	flag.StringVar(&recSet.Type, "type", "A", `record set type; "A" or "AAAA"`)
	flag.Int64Var(&recSet.TTL, "ttl", 300, "TTL (time to live) in seconds")
	flag.StringVar(&recSet.HostedZoneId, "zone", "", "hosted zone id")
	flag.StringVar(&logFn, "log", "", "file name to log to (default is stdout)")
	if len(os.Args) == 1 {
		flag.Usage()
		os.Exit(1)
	}
	flag.Parse()

	recSet.Name = strings.TrimSuffix(recSet.Name, ".") + "." // append . if missing
	if err := recSet.validate(); err != nil {
		log.Fatal(err)
	}

	if logFn != "" {
		f, err := os.OpenFile(logFn, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			log.Fatalf("log file: %v", err)
		}
		defer f.Close()

		log.SetFlags(log.LstdFlags) // restore standard flags
		log.SetOutput(f)            // log to file
	}

	var err error
	recSet.Value, err = getCurrentIP()
	if err != nil {
		log.Fatal(err)
	}

	domain := strings.TrimSuffix(recSet.Name, ".")
	if domainResolvesToIP(domain, recSet.Value) {
		log.Fatalf("%s already resolves to %s; nothing to do", domain, recSet.Value)
	}

	resp, err := recSet.upsert()
	if err != nil {
		log.Fatal(err)
	}

	log.Println(resp)
}

func getCurrentIP() (string, error) {
	resp, err := http.Get("http://checkip.amazonaws.com/")
	if err != nil {
		return "", fmt.Errorf("getCurrentIP: %v", err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("getCurrentIP: %v", err)
	}
	ip := strings.TrimSpace(string(body))
	return ip, nil
}

func domainResolvesToIP(domain, checkIP string) bool {
	ips, err := net.LookupIP(domain)
	if err != nil {
		return false
	}
	for _, ip := range ips {
		if ip.String() == checkIP {
			return true
		}
	}
	return false
}

func (rs *recordSet) upsert() (*route53.ChangeResourceRecordSetsOutput, error) {
	usr, err := user.Current()
	if err != nil {
		return nil, fmt.Errorf("(*recordSet).upsert: %v", err)
	}
	credentialsPath := path.Join(usr.HomeDir, ".aws", "credentials")
	credentials := credentials.NewSharedCredentials(credentialsPath, progName)

	sess, err := session.NewSession()
	if err != nil {
		return nil, fmt.Errorf("(*recordSet).upsert: %v", err)
	}

	svc := route53.New(sess, &aws.Config{Credentials: credentials})

	params := &route53.ChangeResourceRecordSetsInput{
		ChangeBatch: &route53.ChangeBatch{
			Changes: []*route53.Change{
				{
					Action: aws.String("UPSERT"),
					ResourceRecordSet: &route53.ResourceRecordSet{
						Name: aws.String(rs.Name),
						Type: aws.String(rs.Type),
						TTL:  aws.Int64(rs.TTL),
						ResourceRecords: []*route53.ResourceRecord{
							{
								Value: aws.String(rs.Value),
							},
						},
					},
				},
			},
		},
		HostedZoneId: aws.String(rs.HostedZoneId),
	}
	resp, err := svc.ChangeResourceRecordSets(params)
	if err != nil {
		return nil, fmt.Errorf("(*recordSet).upsert: %v", err)
	}

	return resp, nil
}

func (rs *recordSet) validate() error {
	if rs.Name == "" {
		return fmt.Errorf("missing record set name")
	}

	if !strings.HasSuffix(rs.Name, ".") {
		return fmt.Errorf(`record set name must end with a "."`)
	}

	if rs.Type == "" {
		return fmt.Errorf("missing record set type")
	}

	if rs.Type != "A" && rs.Type != "AAAA" {
		return fmt.Errorf("invalid record set type: %s", rs.Type)
	}

	if rs.TTL < 1 {
		return fmt.Errorf("invalid record set TTL: %d", rs.TTL)
	}

	if rs.HostedZoneId == "" {
		return fmt.Errorf("missing hosted zone id")
	}

	return nil
}
<|endoftext|>