<commit_before>package gomail

import (
	"bytes"
	"encoding/base64"
	"io"
	"mime/multipart"
	"net/mail"
	"time"

	"gopkg.in/alexcesaro/quotedprintable.v2"
)

// Export converts the message into a net/mail.Message.
func (msg *Message) Export() *mail.Message {
	w := newMessageWriter(msg)

	if msg.hasMixedPart() {
		w.openMultipart("mixed")
	}

	if msg.hasRelatedPart() {
		w.openMultipart("related")
	}

	if msg.hasAlternativePart() {
		w.openMultipart("alternative")
	}
	for _, part := range msg.parts {
		h := make(map[string][]string)
		h["Content-Type"] = []string{part.contentType + "; charset=" + msg.charset}
		h["Content-Transfer-Encoding"] = []string{string(msg.encoding)}

		w.write(h, part.body.Bytes(), msg.encoding)
	}
	if msg.hasAlternativePart() {
		w.closeMultipart()
	}

	w.addFiles(msg.embedded, false)
	if msg.hasRelatedPart() {
		w.closeMultipart()
	}

	w.addFiles(msg.attachments, true)
	if msg.hasMixedPart() {
		w.closeMultipart()
	}

	return w.export()
}

func (msg *Message) hasMixedPart() bool {
	return (len(msg.parts) > 0 && (len(msg.attachments) > 0 || len(msg.embedded) > 0)) || len(msg.attachments) > 1
}

func (msg *Message) hasRelatedPart() bool {
	return (len(msg.parts) > 0 && len(msg.embedded) > 0) || len(msg.embedded) > 1
}

func (msg *Message) hasAlternativePart() bool {
	return len(msg.parts) > 1
}

// messageWriter helps converting the message into a net/mail.Message
type messageWriter struct {
	header     map[string][]string
	buf        *bytes.Buffer
	writers    [3]*multipart.Writer
	partWriter io.Writer
	depth      uint8
}

func newMessageWriter(msg *Message) *messageWriter {
	// We copy the header so Export does not modify the message
	header := make(map[string][]string, len(msg.header)+2)
	for k, v := range msg.header {
		header[k] = v
	}

	if _, ok := header["Mime-Version"]; !ok {
		header["Mime-Version"] = []string{"1.0"}
	}
	if _, ok := header["Date"]; !ok {
		header["Date"] = []string{msg.FormatDate(now())}
	}

	return &messageWriter{header: header, buf: new(bytes.Buffer)}
}

// Stubbed out for testing.
var now = time.Now

func (w *messageWriter) openMultipart(mimeType string) {
	w.writers[w.depth] = multipart.NewWriter(w.buf)
	contentType := "multipart/" + mimeType + "; boundary=" + w.writers[w.depth].Boundary()

	if w.depth == 0 {
		w.header["Content-Type"] = []string{contentType}
	} else {
		h := make(map[string][]string)
		h["Content-Type"] = []string{contentType}
		w.createPart(h)
	}
	w.depth++
}

func (w *messageWriter) createPart(h map[string][]string) {
	// No need to check the error since the underlying writer is a bytes.Buffer
	w.partWriter, _ = w.writers[w.depth-1].CreatePart(h)
}

func (w *messageWriter) closeMultipart() {
	if w.depth > 0 {
		w.writers[w.depth-1].Close()
		w.depth--
	}
}

func (w *messageWriter) addFiles(files []*File, isAttachment bool) {
	for _, f := range files {
		h := make(map[string][]string)
		h["Content-Type"] = []string{f.MimeType + "; name=\"" + f.Name + "\""}
		h["Content-Transfer-Encoding"] = []string{string(Base64)}
		if isAttachment {
			h["Content-Disposition"] = []string{"attachment; filename=\"" + f.Name + "\""}
		} else {
			h["Content-Disposition"] = []string{"inline; filename=\"" + f.Name + "\""}
			if f.ContentID != "" {
				h["Content-ID"] = []string{"<" + f.ContentID + ">"}
			} else {
				h["Content-ID"] = []string{"<" + f.Name + ">"}
			}
		}

		w.write(h, f.Content, Base64)
	}
}

func (w *messageWriter) write(h map[string][]string, body []byte, enc Encoding) {
	w.writeHeader(h)
	w.writeBody(body, enc)
}

func (w *messageWriter) writeHeader(h map[string][]string) {
	if w.depth == 0 {
		for field, value := range h {
			w.header[field] = value
		}
	} else {
		w.createPart(h)
	}
}

func (w *messageWriter) writeBody(body []byte, enc Encoding) {
	var subWriter io.Writer
	if w.depth == 0 {
		subWriter = w.buf
	} else {
		subWriter = w.partWriter
	}

	// The errors returned by writers are not checked since these writers cannot
	// return errors.
	if enc == Base64 {
		writer := base64.NewEncoder(base64.StdEncoding, newBase64LineWriter(subWriter))
		writer.Write(body)
		writer.Close()
	} else if enc == Unencoded {
		subWriter.Write(body)
	} else {
		writer := quotedprintable.NewEncoder(newQpLineWriter(subWriter))
		writer.Write(body)
	}
}

func (w *messageWriter) export() *mail.Message {
	return &mail.Message{Header: w.header, Body: w.buf}
}

// As required by RFC 2045, 6.7. (page 21) for quoted-printable, and
// RFC 2045, 6.8. (page 25) for base64.
const maxLineLen = 76

// base64LineWriter limits text encoded in base64 to 76 characters per line
type base64LineWriter struct {
	w       io.Writer
	lineLen int
}

func newBase64LineWriter(w io.Writer) *base64LineWriter {
	return &base64LineWriter{w: w}
}

func (w *base64LineWriter) Write(p []byte) (int, error) {
	n := 0
	for len(p)+w.lineLen > maxLineLen {
		w.w.Write(p[:maxLineLen-w.lineLen])
		w.w.Write([]byte("\r\n"))
		p = p[maxLineLen-w.lineLen:]
		n += maxLineLen - w.lineLen
		w.lineLen = 0
	}

	w.w.Write(p)
	w.lineLen += len(p)

	return n + len(p), nil
}

// qpLineWriter limits text encoded in quoted-printable to 76 characters per
// line
type qpLineWriter struct {
	w       io.Writer
	lineLen int
}

func newQpLineWriter(w io.Writer) *qpLineWriter {
	return &qpLineWriter{w: w}
}

func (w *qpLineWriter) Write(p []byte) (int, error) {
	n := 0
	for len(p) > 0 {
		// If the text is not over the limit, write everything
		if len(p) < maxLineLen-w.lineLen {
			w.w.Write(p)
			w.lineLen += len(p)
			return n + len(p), nil
		}

		i := bytes.IndexAny(p[:maxLineLen-w.lineLen+2], "\n")
		// If there is a newline before the limit, write the end of the line
		if i != -1 && (i != maxLineLen-w.lineLen+1 || p[i-1] == '\r') {
			w.w.Write(p[:i+1])
			p = p[i+1:]
			n += i + 1
			w.lineLen = 0
			continue
		}

		// Quoted-printable text must not be cut between an equal sign and the
		// two following characters
		var toWrite int
		if maxLineLen-w.lineLen-2 >= 0 && p[maxLineLen-w.lineLen-2] == '=' {
			toWrite = maxLineLen - w.lineLen - 2
		} else if p[maxLineLen-w.lineLen-1] == '=' {
			toWrite = maxLineLen - w.lineLen - 1
		} else {
			toWrite = maxLineLen - w.lineLen
		}

		// Insert the newline where it is needed
		w.w.Write(p[:toWrite])
		w.w.Write([]byte("=\r\n"))
		p = p[toWrite:]
		n += toWrite
		w.lineLen = 0
	}

	return n, nil
}
<commit_msg>Switch the quotedprintable to golang library<commit_after>package gomail

import (
	"bytes"
	"encoding/base64"
	"io"
	"mime/multipart"
	"net/mail"
	"time"

	"github.com/cention-sany/quotedprintable"
)

// Export converts the message into a net/mail.Message.
func (msg *Message) Export() *mail.Message {
	w := newMessageWriter(msg)

	if msg.hasMixedPart() {
		w.openMultipart("mixed")
	}

	if msg.hasRelatedPart() {
		w.openMultipart("related")
	}

	if msg.hasAlternativePart() {
		w.openMultipart("alternative")
	}
	for _, part := range msg.parts {
		h := make(map[string][]string)
		h["Content-Type"] = []string{part.contentType + "; charset=" + msg.charset}
		h["Content-Transfer-Encoding"] = []string{string(msg.encoding)}

		w.write(h, part.body.Bytes(), msg.encoding)
	}
	if msg.hasAlternativePart() {
		w.closeMultipart()
	}

	w.addFiles(msg.embedded, false)
	if msg.hasRelatedPart() {
		w.closeMultipart()
	}

	w.addFiles(msg.attachments, true)
	if msg.hasMixedPart() {
		w.closeMultipart()
	}

	return w.export()
}

func (msg *Message) hasMixedPart() bool {
	return (len(msg.parts) > 0 && (len(msg.attachments) > 0 || len(msg.embedded) > 0)) || len(msg.attachments) > 1
}

func (msg *Message) hasRelatedPart() bool {
	return (len(msg.parts) > 0 && len(msg.embedded) > 0) || len(msg.embedded) > 1
}

func (msg *Message) hasAlternativePart() bool {
	return len(msg.parts) > 1
}

// messageWriter helps converting the message into a net/mail.Message
type messageWriter struct {
	header     map[string][]string
	buf        *bytes.Buffer
	writers    [3]*multipart.Writer
	partWriter io.Writer
	depth      uint8
}

func newMessageWriter(msg *Message) *messageWriter {
	// We copy the header so Export does not modify the message
	header := make(map[string][]string, len(msg.header)+2)
	for k, v := range msg.header {
		header[k] = v
	}

	if _, ok := header["Mime-Version"]; !ok {
		header["Mime-Version"] = []string{"1.0"}
	}
	if _, ok := header["Date"]; !ok {
		header["Date"] = []string{msg.FormatDate(now())}
	}

	return &messageWriter{header: header, buf: new(bytes.Buffer)}
}

// Stubbed out for testing.
var now = time.Now

func (w *messageWriter) openMultipart(mimeType string) {
	w.writers[w.depth] = multipart.NewWriter(w.buf)
	contentType := "multipart/" + mimeType + "; boundary=" + w.writers[w.depth].Boundary()

	if w.depth == 0 {
		w.header["Content-Type"] = []string{contentType}
	} else {
		h := make(map[string][]string)
		h["Content-Type"] = []string{contentType}
		w.createPart(h)
	}
	w.depth++
}

func (w *messageWriter) createPart(h map[string][]string) {
	// No need to check the error since the underlying writer is a bytes.Buffer
	w.partWriter, _ = w.writers[w.depth-1].CreatePart(h)
}

func (w *messageWriter) closeMultipart() {
	if w.depth > 0 {
		w.writers[w.depth-1].Close()
		w.depth--
	}
}

func (w *messageWriter) addFiles(files []*File, isAttachment bool) {
	for _, f := range files {
		h := make(map[string][]string)
		h["Content-Type"] = []string{f.MimeType + "; name=\"" + f.Name + "\""}
		h["Content-Transfer-Encoding"] = []string{string(Base64)}
		if isAttachment {
			h["Content-Disposition"] = []string{"attachment; filename=\"" + f.Name + "\""}
		} else {
			h["Content-Disposition"] = []string{"inline; filename=\"" + f.Name + "\""}
			if f.ContentID != "" {
				h["Content-ID"] = []string{"<" + f.ContentID + ">"}
			} else {
				h["Content-ID"] = []string{"<" + f.Name + ">"}
			}
		}

		w.write(h, f.Content, Base64)
	}
}

func (w *messageWriter) write(h map[string][]string, body []byte, enc Encoding) {
	w.writeHeader(h)
	w.writeBody(body, enc)
}

func (w *messageWriter) writeHeader(h map[string][]string) {
	if w.depth == 0 {
		for field, value := range h {
			w.header[field] = value
		}
	} else {
		w.createPart(h)
	}
}

func (w *messageWriter) writeBody(body []byte, enc Encoding) {
	var subWriter io.Writer
	if w.depth == 0 {
		subWriter = w.buf
	} else {
		subWriter = w.partWriter
	}

	// The errors returned by writers are not checked since these writers cannot
	// return errors.
	if enc == Base64 {
		writer := base64.NewEncoder(base64.StdEncoding, newBase64LineWriter(subWriter))
		writer.Write(body)
		writer.Close()
	} else if enc == Unencoded {
		subWriter.Write(body)
	} else {
		writer := quotedprintable.NewWriter(subWriter)
		_, err := writer.Write(body)
		if err != nil {
			//log.Println("error while converting to qp")
		}
		err = writer.Close()
		if err != nil {
			//log.Println("error while closing writer for qp")
		}
	}
}

func (w *messageWriter) export() *mail.Message {
	return &mail.Message{Header: w.header, Body: w.buf}
}

// As required by RFC 2045, 6.7. (page 21) for quoted-printable, and
// RFC 2045, 6.8. (page 25) for base64.
const maxLineLen = 76

// base64LineWriter limits text encoded in base64 to 76 characters per line
type base64LineWriter struct {
	w       io.Writer
	lineLen int
}

func newBase64LineWriter(w io.Writer) *base64LineWriter {
	return &base64LineWriter{w: w}
}

func (w *base64LineWriter) Write(p []byte) (int, error) {
	n := 0
	for len(p)+w.lineLen > maxLineLen {
		w.w.Write(p[:maxLineLen-w.lineLen])
		w.w.Write([]byte("\r\n"))
		p = p[maxLineLen-w.lineLen:]
		n += maxLineLen - w.lineLen
		w.lineLen = 0
	}

	w.w.Write(p)
	w.lineLen += len(p)

	return n + len(p), nil
}
<|endoftext|>


<commit_before>package getter

import (
	"bufio"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"fmt"
	"hash"
	"io"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	urlhelper "github.com/hashicorp/go-getter/helper/url"
)

// fileChecksum helps verifying the checksum for a file.
type fileChecksum struct {
	Type     string
	Hash     hash.Hash
	Value    []byte
	Filename string
}

// extractChecksum will return a fileChecksum based on the 'checksum'
// parameter of u.
// ex:
//  http://hashicorp.com/terraform?checksum=<checksumValue>
//  http://hashicorp.com/terraform?checksum=<checksumType>:<checksumValue>
//  http://hashicorp.com/terraform?checksum=file:<checksum_url>
// when checksumming from a file, extractChecksum will go get checksum_url
// in a temporary directory, parse the content of the file then delete it.
// Content of files are expected to be BSD style or GNU style.
//
// BSD-style checksum:
//  MD5 (file1) = <checksum>
//  MD5 (file2) = <checksum>
//
// GNU-style:
//  <checksum>  file1
//  <checksum> *file2
//
// see parseChecksumLine for more detail on checksum file parsing
func (c *Client) extractChecksum(u *url.URL) (*fileChecksum, error) {
	q := u.Query()
	v := q.Get("checksum")

	if v == "" {
		return nil, nil
	}

	vs := strings.SplitN(v, ":", 2)
	switch len(vs) {
	case 2:
		break // good
	default:
		// here, we try to guess the checksum from it's length
		// if the type was not passed
		return newChecksumFromValue(v, filepath.Base(u.EscapedPath()))
	}

	checksumType, checksumValue := vs[0], vs[1]

	switch checksumType {
	case "file":
		return c.checksumFromFile(checksumValue, u)
	default:
		return newChecksumFromType(checksumType, checksumValue, filepath.Base(u.EscapedPath()))
	}
}

func newChecksum(checksumValue, filename string) (*fileChecksum, error) {
	c := &fileChecksum{
		Filename: filename,
	}
	var err error
	c.Value, err = hex.DecodeString(checksumValue)
	if err != nil {
		return nil, fmt.Errorf("invalid checksum: %s", err)
	}
	return c, nil
}

func newChecksumFromType(checksumType, checksumValue, filename string) (*fileChecksum, error) {
	c, err := newChecksum(checksumValue, filename)
	if err != nil {
		return nil, err
	}

	c.Type = strings.ToLower(checksumType)
	switch c.Type {
	case "md5":
		c.Hash = md5.New()
	case "sha1":
		c.Hash = sha1.New()
	case "sha256":
		c.Hash = sha256.New()
	case "sha512":
		c.Hash = sha512.New()
	default:
		return nil, fmt.Errorf(
			"unsupported checksum type: %s", checksumType)
	}

	return c, nil
}

func newChecksumFromValue(checksumValue, filename string) (*fileChecksum, error) {
	c, err := newChecksum(checksumValue, filename)
	if err != nil {
		return nil, err
	}

	switch len(c.Value) {
	case md5.Size:
		c.Hash = md5.New()
		c.Type = "md5"
	case sha1.Size:
		c.Hash = sha1.New()
		c.Type = "sha1"
	case sha256.Size:
		c.Hash = sha256.New()
		c.Type = "sha256"
	case sha512.Size:
		c.Hash = sha512.New()
		c.Type = "sha512"
	default:
		return nil, fmt.Errorf("Unknown type for checksum %s", checksumValue)
	}

	return c, nil
}

// checksumsFromFile will return all the fileChecksums found in file
//
// checksumsFromFile will try to guess the hashing algorithm based on content
// of checksum file
//
// checksumsFromFile will only return checksums for files that match file
// behind src
func (c *Client) checksumFromFile(checksumFile string, src *url.URL) (*fileChecksum, error) {
	checksumFileURL, err := urlhelper.Parse(checksumFile)
	if err != nil {
		return nil, err
	}

	tempfile, err := tmpFile("", filepath.Base(checksumFileURL.Path))
	if err != nil {
		return nil, err
	}
	defer func() {
		os.Remove(tempfile)
	}()

	c2 := &Client{
		Getters:       c.Getters,
		Decompressors: c.Decompressors,
		Detectors:     c.Detectors,
		Pwd:           c.Pwd,
		Dir:           false,
		Src:           checksumFile,
		Dst:           tempfile,
	}
	if err = c2.Get(); err != nil {
		return nil, fmt.Errorf(
			"Error downloading checksum file: %s", err)
	}

	filename := filepath.Base(src.Path)
	absPath, err := filepath.Abs(src.Path)
	if err != nil {
		return nil, err
	}
	relpath, err := filepath.Rel(filepath.Dir(checksumFileURL.Path), absPath)
	if err != nil {
		return nil, err
	}

	// possible file identifiers:
	options := []string{
		filename,       // ubuntu-14.04.1-server-amd64.iso
		"*" + filename, // *ubuntu-14.04.1-server-amd64.iso  Standard checksum
		"?" + filename, // ?ubuntu-14.04.1-server-amd64.iso  shasum -p
		relpath,        // dir/ubuntu-14.04.1-server-amd64.iso
		"./" + relpath, // ./dir/ubuntu-14.04.1-server-amd64.iso
		absPath,        // fullpath; set if local
	}

	f, err := os.Open(tempfile)
	if err != nil {
		return nil, fmt.Errorf(
			"Error opening downloaded file: %s", err)
	}
	defer f.Close()
	rd := bufio.NewReader(f)
	for {
		line, err := rd.ReadString('\n')
		if err != nil {
			if err != io.EOF {
				return nil, fmt.Errorf(
					"Error reading checksum file: %s", err)
			}
			break
		}
		checksum, err := parseChecksumLine(line)
		if err != nil || checksum == nil {
			continue
		}
		if checksum.Filename == "" {
			// filename not sure, let's try
			return checksum, nil
		}
		// make sure the checksum is for the right file
		for _, option := range options {
			if checksum.Filename == option {
				// any checksum will work so we return the first one
				return checksum, nil
			}
		}
	}
	return nil, fmt.Errorf("no checksum found in: %s", checksumFile)
}

// parseChecksumLine takes a line from a checksum file and returns
// checksumType, checksumValue and filename parseChecksumLine guesses the style
// of the checksum BSD vs GNU by splitting the line and by counting the parts.
// of a line.
// for BSD type sums parseChecksumLine guesses the hashing algorithm
// by checking the length of the checksum.
func parseChecksumLine(line string) (*fileChecksum, error) {
	parts := strings.Fields(line)

	switch len(parts) {
	case 4:
		// BSD-style checksum:
		//  MD5 (file1) = <checksum>
		//  MD5 (file2) = <checksum>
		if len(parts[1]) <= 2 ||
			parts[1][0] != '(' || parts[1][len(parts[1])-1] != ')' {
			return nil, fmt.Errorf(
				"Unexpected BSD-style-checksum filename format: %s", line)
		}
		filename := parts[1][1 : len(parts[1])-1]
		return newChecksumFromType(parts[0], parts[3], filename)
	case 2:
		// GNU-style:
		//  <checksum>  file1
		//  <checksum> *file2
		return newChecksumFromValue(parts[0], parts[1])
	case 0:
		return nil, nil // empty line
	default:
		return newChecksumFromValue(parts[0], "")
	}
}
<commit_msg>checksumming: simplify a defer os.Remove(tempfile) call<commit_after>package getter

import (
	"bufio"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"fmt"
	"hash"
	"io"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	urlhelper "github.com/hashicorp/go-getter/helper/url"
)

// fileChecksum helps verifying the checksum for a file.
type fileChecksum struct {
	Type     string
	Hash     hash.Hash
	Value    []byte
	Filename string
}

// extractChecksum will return a fileChecksum based on the 'checksum'
// parameter of u.
// ex:
//  http://hashicorp.com/terraform?checksum=<checksumValue>
//  http://hashicorp.com/terraform?checksum=<checksumType>:<checksumValue>
//  http://hashicorp.com/terraform?checksum=file:<checksum_url>
// when checksumming from a file, extractChecksum will go get checksum_url
// in a temporary directory, parse the content of the file then delete it.
// Content of files are expected to be BSD style or GNU style.
//
// BSD-style checksum:
//  MD5 (file1) = <checksum>
//  MD5 (file2) = <checksum>
//
// GNU-style:
//  <checksum>  file1
//  <checksum> *file2
//
// see parseChecksumLine for more detail on checksum file parsing
func (c *Client) extractChecksum(u *url.URL) (*fileChecksum, error) {
	q := u.Query()
	v := q.Get("checksum")

	if v == "" {
		return nil, nil
	}

	vs := strings.SplitN(v, ":", 2)
	switch len(vs) {
	case 2:
		break // good
	default:
		// here, we try to guess the checksum from it's length
		// if the type was not passed
		return newChecksumFromValue(v, filepath.Base(u.EscapedPath()))
	}

	checksumType, checksumValue := vs[0], vs[1]

	switch checksumType {
	case "file":
		return c.checksumFromFile(checksumValue, u)
	default:
		return newChecksumFromType(checksumType, checksumValue, filepath.Base(u.EscapedPath()))
	}
}

func newChecksum(checksumValue, filename string) (*fileChecksum, error) {
	c := &fileChecksum{
		Filename: filename,
	}
	var err error
	c.Value, err = hex.DecodeString(checksumValue)
	if err != nil {
		return nil, fmt.Errorf("invalid checksum: %s", err)
	}
	return c, nil
}

func newChecksumFromType(checksumType, checksumValue, filename string) (*fileChecksum, error) {
	c, err := newChecksum(checksumValue, filename)
	if err != nil {
		return nil, err
	}

	c.Type = strings.ToLower(checksumType)
	switch c.Type {
	case "md5":
		c.Hash = md5.New()
	case "sha1":
		c.Hash = sha1.New()
	case "sha256":
		c.Hash = sha256.New()
	case "sha512":
		c.Hash = sha512.New()
	default:
		return nil, fmt.Errorf(
			"unsupported checksum type: %s", checksumType)
	}

	return c, nil
}

func newChecksumFromValue(checksumValue, filename string) (*fileChecksum, error) {
	c, err := newChecksum(checksumValue, filename)
	if err != nil {
		return nil, err
	}

	switch len(c.Value) {
	case md5.Size:
		c.Hash = md5.New()
		c.Type = "md5"
	case sha1.Size:
		c.Hash = sha1.New()
		c.Type = "sha1"
	case sha256.Size:
		c.Hash = sha256.New()
		c.Type = "sha256"
	case sha512.Size:
		c.Hash = sha512.New()
		c.Type = "sha512"
	default:
		return nil, fmt.Errorf("Unknown type for checksum %s", checksumValue)
	}

	return c, nil
}

// checksumsFromFile will return all the fileChecksums found in file
//
// checksumsFromFile will try to guess the hashing algorithm based on content
// of checksum file
//
// checksumsFromFile will only return checksums for files that match file
// behind src
func (c *Client) checksumFromFile(checksumFile string, src *url.URL) (*fileChecksum, error) {
	checksumFileURL, err := urlhelper.Parse(checksumFile)
	if err != nil {
		return nil, err
	}

	tempfile, err := tmpFile("", filepath.Base(checksumFileURL.Path))
	if err != nil {
		return nil, err
	}
	defer os.Remove(tempfile)

	c2 := &Client{
		Getters:       c.Getters,
		Decompressors: c.Decompressors,
		Detectors:     c.Detectors,
		Pwd:           c.Pwd,
		Dir:           false,
		Src:           checksumFile,
		Dst:           tempfile,
	}
	if err = c2.Get(); err != nil {
		return nil, fmt.Errorf(
			"Error downloading checksum file: %s", err)
	}

	filename := filepath.Base(src.Path)
	absPath, err := filepath.Abs(src.Path)
	if err != nil {
		return nil, err
	}
	relpath, err := filepath.Rel(filepath.Dir(checksumFileURL.Path), absPath)
	if err != nil {
		return nil, err
	}

	// possible file identifiers:
	options := []string{
		filename,       // ubuntu-14.04.1-server-amd64.iso
		"*" + filename, // *ubuntu-14.04.1-server-amd64.iso  Standard checksum
		"?" + filename, // ?ubuntu-14.04.1-server-amd64.iso  shasum -p
		relpath,        // dir/ubuntu-14.04.1-server-amd64.iso
		"./" + relpath, // ./dir/ubuntu-14.04.1-server-amd64.iso
		absPath,        // fullpath; set if local
	}

	f, err := os.Open(tempfile)
	if err != nil {
		return nil, fmt.Errorf(
			"Error opening downloaded file: %s", err)
	}
	defer f.Close()
	rd := bufio.NewReader(f)
	for {
		line, err := rd.ReadString('\n')
		if err != nil {
			if err != io.EOF {
				return nil, fmt.Errorf(
					"Error reading checksum file: %s", err)
			}
			break
		}
		checksum, err := parseChecksumLine(line)
		if err != nil || checksum == nil {
			continue
		}
		if checksum.Filename == "" {
			// filename not sure, let's try
			return checksum, nil
		}
		// make sure the checksum is for the right file
		for _, option := range options {
			if checksum.Filename == option {
				// any checksum will work so we return the first one
				return checksum, nil
			}
		}
	}
	return nil, fmt.Errorf("no checksum found in: %s", checksumFile)
}

// parseChecksumLine takes a line from a checksum file and returns
// checksumType, checksumValue and filename parseChecksumLine guesses the style
// of the checksum BSD vs GNU by splitting the line and by counting the parts.
// of a line.
// for BSD type sums parseChecksumLine guesses the hashing algorithm
// by checking the length of the checksum.
func parseChecksumLine(line string) (*fileChecksum, error) {
	parts := strings.Fields(line)

	switch len(parts) {
	case 4:
		// BSD-style checksum:
		//  MD5 (file1) = <checksum>
		//  MD5 (file2) = <checksum>
		if len(parts[1]) <= 2 ||
			parts[1][0] != '(' || parts[1][len(parts[1])-1] != ')' {
			return nil, fmt.Errorf(
				"Unexpected BSD-style-checksum filename format: %s", line)
		}
		filename := parts[1][1 : len(parts[1])-1]
		return newChecksumFromType(parts[0], parts[3], filename)
	case 2:
		// GNU-style:
		//  <checksum>  file1
		//  <checksum> *file2
		return newChecksumFromValue(parts[0], parts[1])
	case 0:
		return nil, nil // empty line
	default:
		return newChecksumFromValue(parts[0], "")
	}
}
<|endoftext|>


<commit_before>// +build linux

package resource

import (
	"os/exec"
	"strings"

	"github.com/hashicorp/hcl"
	"github.com/hashicorp/hcl/hcl/ast"
	"github.com/imdario/mergo"
)

// Path to the pacman package manager
const pacmanPath = "/usr/bin/pacman"

// Name and description of the resource
const pacmanResourceType = "pacman"
const pacmanResourceDesc = "manages packages using the pacman package manager"

// PacmanResource type represents the resource for
// package management on Arch Linux systems
type PacmanResource struct {
	BaseResource `hcl:",squash"`

	// Name of the package
	Name string `hcl:"name"`
}

// NewPacmanResource creates a new resource for managing packages
// using the pacman package manager on an Arch Linux system
func NewPacmanResource(title string, obj *ast.ObjectItem, config *Config) (Resource, error) {
	// Resource defaults
	defaults := &PacmanResource{
		BaseResource: BaseResource{
			Title:  title,
			Type:   pacmanResourceType,
			State:  StatePresent,
			Config: config,
		},
		Name: title,
	}

	// Decode the object from HCL
	var p PacmanResource
	err := hcl.DecodeObject(&p, obj)
	if err != nil {
		return nil, err
	}

	// Merge in the decoded object with the resource defaults
	err = mergo.Merge(&p, defaults)

	return &p, err
}

// Evaluate evaluates the state of the resource
func (pr *PacmanResource) Evaluate() (State, error) {
	s := State{
		Current: StateUnknown,
		Want:    pr.State,
	}

	_, err := exec.LookPath(pacmanPath)
	if err != nil {
		return s, err
	}

	cmd := exec.Command(pacmanPath, "--query", pr.Name)
	err = cmd.Run()

	if err != nil {
		s.Current = StateAbsent
	} else {
		s.Current = StatePresent
	}

	return s, nil
}

// Create installs packages
func (pr *PacmanResource) Create() error {
	pr.Printf("installing package\n")

	cmd := exec.Command(pacmanPath, "--sync", "--noconfirm", pr.Name)
	out, err := cmd.CombinedOutput()

	for _, line := range strings.Split(string(out), "\n") {
		pr.Printf("%s\n", line)
	}

	return err
}

// Delete deletes packages
func (pr *PacmanResource) Delete() error {
	pr.Printf("removing package\n")

	cmd := exec.Command(pacmanPath, "--remove", "--noconfirm", pr.Name)
	out, err := cmd.CombinedOutput()

	for _, line := range strings.Split(string(out), "\n") {
		pr.Printf("%s\n", line)
	}

	return err
}

// Update updates packages
func (pr *PacmanResource) Update() error {
	pr.Printf("updating package\n")

	return pr.Create()
}

func init() {
	item := RegistryItem{
		Name:        pacmanResourceType,
		Description: pacmanResourceDesc,
		Provider:    NewPacmanResource,
	}

	Register(item)
}
<commit_msg>resource: PacmanResource embeds BasePackageResource type<commit_after>// +build linux

package resource

import (
	"os/exec"
	"strings"

	"github.com/hashicorp/hcl"
	"github.com/hashicorp/hcl/hcl/ast"
	"github.com/imdario/mergo"
)

// Path to the pacman package manager
const pacmanPath = "/usr/bin/pacman"

// Name and description of the resource
const pacmanResourceType = "pacman"
const pacmanResourceDesc = "manages packages using the pacman package manager"

// PacmanResource type represents the resource for
// package management on Arch Linux systems
type PacmanResource struct {
	BasePackageResource `hcl:",squash"`
}

// NewPacmanResource creates a new resource for managing packages
// using the pacman package manager on an Arch Linux system
func NewPacmanResource(title string, obj *ast.ObjectItem, config *Config) (Resource, error) {
	// Resource defaults
	defaults := &PacmanResource{
		BasePackageResource: BasePackageResource{
			BaseResource: BaseResource{
				Title:  title,
				Type:   pacmanResourceType,
				State:  StatePresent,
				Config: config,
			},
			Name: title,
		},
	}

	// Decode the object from HCL
	var pr PacmanResource
	err := hcl.DecodeObject(&pr, obj)
	if err != nil {
		return nil, err
	}

	// Merge in the decoded object with the resource defaults
	err = mergo.Merge(&pr, defaults)

	return &pr, err
}

// Evaluate evaluates the state of the resource
func (pr *PacmanResource) Evaluate() (State, error) {
	s := State{
		Current: StateUnknown,
		Want:    pr.State,
	}

	_, err := exec.LookPath(pacmanPath)
	if err != nil {
		return s, err
	}

	cmd := exec.Command(pacmanPath, "--query", pr.Name)
	err = cmd.Run()

	if err != nil {
		s.Current = StateAbsent
	} else {
		s.Current = StatePresent
	}

	return s, nil
}

// Create installs packages
func (pr *PacmanResource) Create() error {
	pr.Printf("installing package\n")

	cmd := exec.Command(pacmanPath, "--sync", "--noconfirm", pr.Name)
	out, err := cmd.CombinedOutput()

	for _, line := range strings.Split(string(out), "\n") {
		pr.Printf("%s\n", line)
	}

	return err
}

// Delete deletes packages
func (pr *PacmanResource) Delete() error {
	pr.Printf("removing package\n")

	cmd := exec.Command(pacmanPath, "--remove", "--noconfirm", pr.Name)
	out, err := cmd.CombinedOutput()

	for _, line := range strings.Split(string(out), "\n") {
		pr.Printf("%s\n", line)
	}

	return err
}

// Update updates packages
func (pr *PacmanResource) Update() error {
	pr.Printf("updating package\n")

	return pr.Create()
}

func init() {
	item := RegistryItem{
		Name:        pacmanResourceType,
		Description: pacmanResourceDesc,
		Provider:    NewPacmanResource,
	}

	Register(item)
}
<|endoftext|>


<commit_before>package cfschema

import (
	"fmt"
)

// Expand replaces all Definition and Property JSON Pointer references with their content.
// This functionality removes the need for recursive logic when accessing Definitions and Properties.
// In unresolved form nested properties are not allowed, instead nested properties use a '$ref' JSON Pointer to reference a definition.
// See https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-schema.html#schema-properties-properties.
func (r *Resource) Expand() error {
	if r == nil {
		return nil
	}

	err := r.ResolveProperties(r.Definitions)

	if err != nil {
		return fmt.Errorf("error expanding Resource (%s) Definitions: %w", *r.TypeName, err)
	}

	err = r.ResolveProperties(r.Properties)

	if err != nil {
		return fmt.Errorf("error expanding Resource (%s) Properties: %w", *r.TypeName, err)
	}

	return nil
}

// ResolveProperties resolves all References in a top-level name-to-property map.
// In unresolved form nested properties are not allowed so we don't need to recurse.
func (r *Resource) ResolveProperties(properties map[string]*Property) error {
	for propertyName, property := range properties {
		resolved, err := r.ResolveProperty(property)

		if err != nil {
			return fmt.Errorf("error resolving %s: %w", propertyName, err)
		}

		if resolved {
			// For example:
			// "Configuration": {
			// 	  "$ref": "#/definitions/ClusterConfiguration"
			// },
			continue
		}

		switch property.Type.String() {
		case PropertyTypeArray:
			// For example:
			// "DefaultCapacityProviderStrategy": {
			//   "type": "array",
			//   "items": {
			//     "$ref": "#/definitions/CapacityProviderStrategyItem"
			// 	 }
			// },
			_, err = r.ResolveProperty(property.Items)

			if err != nil {
				return fmt.Errorf("error resolving %s Items: %w", propertyName, err)
			}
		case PropertyTypeObject:
			for objPropertyName, objProperty := range property.Properties {
				resolved, err := r.ResolveProperty(objProperty)

				if err != nil {
					return fmt.Errorf("error resolving %s Property (%s): %w", propertyName, objPropertyName, err)
				}

				if resolved {
					// For example:
					// "ClusterConfiguration": {
					//   "type": "object",
					// 	 "properties": {
					// 	   "ExecuteCommandConfiguration": {
					// 	     "$ref": "#/definitions/ExecuteCommandConfiguration"
					// 	   }
					// 	 }
					// },
					continue
				}

				switch objProperty.Type.String() {
				case PropertyTypeArray:
					// For example:
					// "LambdaContainerParams": {
					//   "type": "object",
					// 	 "properties": {
					// 	   "Volumes": {
					// 		 "type": "array",
					// 		 "items": {
					// 		   "$ref": "#/definitions/LambdaVolumeMount"
					// 		 }
					// 	   }
					// 	 }
					// },
					_, err = r.ResolveProperty(objProperty.Items)

					if err != nil {
						return fmt.Errorf("error resolving %s Property (%s) Items: %w", propertyName, objPropertyName, err)
					}
				case PropertyTypeObject:
					// Pragmatically resolve any References at this level even though they are not allowed.
					for objPropertyName2, objProperty2 := range objProperty.Properties {
						_, err := r.ResolveProperty(objProperty2)

						if err != nil {
							return fmt.Errorf("error resolving %s Property (%s) Property (%s): %w", propertyName, objPropertyName, objPropertyName2, err)
						}
					}

					for pattern, patternProperty := range objProperty.PatternProperties {
						// For example:
						// "LambdaFunctionRecipeSource": {
						//   "type": "object",
						// 	 "properties": {
						// 	   "ComponentDependencies": {
						// 		 "type": "object",
						// 		 "patternProperties": {
						// 		   "": {
						// 			 "$ref": "#/definitions/ComponentDependencyRequirement"
						// 		   }
						// 		 }
						// 	   }
						// 	 }
						// },
						_, err = r.ResolveProperty(patternProperty)

						if err != nil {
							return fmt.Errorf("error resolving %s Property (%s) Pattern(%s): %w", propertyName, objPropertyName, pattern, err)
						}
					}
				}
			}

			for patternName, objProperty := range property.PatternProperties {
				resolved, err := r.ResolveProperty(objProperty)

				if err != nil {
					return fmt.Errorf("error resolving %s pattern Property (%s): %w", propertyName, patternName, err)
				}

				if resolved {
					// For example:
					// "Tags": {
					// 	 "type": "object",
					// 	 "patternProperties": {
					// 	   "": {
					// 		 "$ref": "#/definitions/TagValue"
					// 	   }
					// 	 }
					// },
					continue
				}

				switch objProperty.Type.String() {
				case PropertyTypeArray:
					// For example:
					// "Tags": {
					// 	 "type": "object",
					// 	 "patternProperties": {
					// 	   "": {
					// 		 "type": "array",
					// 		 "items": {
					// 		   "$ref": "#/definitions/TagValue"
					// 		 }
					// 	   }
					// 	 }
					// },
					_, err = r.ResolveProperty(objProperty.Items)

					if err != nil {
						return fmt.Errorf("error resolving %s Property (%s) Items: %w", propertyName, patternName, err)
					}
				}
			}
		}
	}

	return nil
}

// ResolveProperty resolves any Reference (JSON Pointer) in a Property.
// Returns whether a Reference was resolved.
func (r *Resource) ResolveProperty(property *Property) (bool, error) {
	if property != nil && property.Ref != nil {
		ref := property.Ref
		resolution, err := r.ResolveReference(*ref)

		if err != nil {
			return false, err
		}

		*property = *resolution

		return true, nil
	}

	return false, nil
}
<commit_msg>Ensure that property default value is maintained after expansion.<commit_after>package cfschema

import (
	"fmt"
)

// Expand replaces all Definition and Property JSON Pointer references with their content.
// This functionality removes the need for recursive logic when accessing Definitions and Properties.
// In unresolved form nested properties are not allowed, instead nested properties use a '$ref' JSON Pointer to reference a definition.
// See https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-schema.html#schema-properties-properties.
func (r *Resource) Expand() error {
	if r == nil {
		return nil
	}

	err := r.ResolveProperties(r.Definitions)

	if err != nil {
		return fmt.Errorf("error expanding Resource (%s) Definitions: %w", *r.TypeName, err)
	}

	err = r.ResolveProperties(r.Properties)

	if err != nil {
		return fmt.Errorf("error expanding Resource (%s) Properties: %w", *r.TypeName, err)
	}

	return nil
}

// ResolveProperties resolves all References in a top-level name-to-property map.
// In unresolved form nested properties are not allowed so we don't need to recurse.
func (r *Resource) ResolveProperties(properties map[string]*Property) error {
	for propertyName, property := range properties {
		resolved, err := r.ResolveProperty(property)

		if err != nil {
			return fmt.Errorf("error resolving %s: %w", propertyName, err)
		}

		if resolved {
			// For example:
			// "Configuration": {
			// 	  "$ref": "#/definitions/ClusterConfiguration"
			// },
			continue
		}

		switch property.Type.String() {
		case PropertyTypeArray:
			// For example:
			// "DefaultCapacityProviderStrategy": {
			//   "type": "array",
			//   "items": {
			//     "$ref": "#/definitions/CapacityProviderStrategyItem"
			// 	 }
			// },
			_, err = r.ResolveProperty(property.Items)

			if err != nil {
				return fmt.Errorf("error resolving %s Items: %w", propertyName, err)
			}
		case PropertyTypeObject:
			for objPropertyName, objProperty := range property.Properties {
				resolved, err := r.ResolveProperty(objProperty)

				if err != nil {
					return fmt.Errorf("error resolving %s Property (%s): %w", propertyName, objPropertyName, err)
				}

				if resolved {
					// For example:
					// "ClusterConfiguration": {
					//   "type": "object",
					// 	 "properties": {
					// 	   "ExecuteCommandConfiguration": {
					// 	     "$ref": "#/definitions/ExecuteCommandConfiguration"
					// 	   }
					// 	 }
					// },
					continue
				}

				switch objProperty.Type.String() {
				case PropertyTypeArray:
					// For example:
					// "LambdaContainerParams": {
					//   "type": "object",
					// 	 "properties": {
					// 	   "Volumes": {
					// 		 "type": "array",
					// 		 "items": {
					// 		   "$ref": "#/definitions/LambdaVolumeMount"
					// 		 }
					// 	   }
					// 	 }
					// },
					_, err = r.ResolveProperty(objProperty.Items)

					if err != nil {
						return fmt.Errorf("error resolving %s Property (%s) Items: %w", propertyName, objPropertyName, err)
					}
				case PropertyTypeObject:
					// Pragmatically resolve any References at this level even though they are not allowed.
					for objPropertyName2, objProperty2 := range objProperty.Properties {
						_, err := r.ResolveProperty(objProperty2)

						if err != nil {
							return fmt.Errorf("error resolving %s Property (%s) Property (%s): %w", propertyName, objPropertyName, objPropertyName2, err)
						}
					}

					for pattern, patternProperty := range objProperty.PatternProperties {
						// For example:
						// "LambdaFunctionRecipeSource": {
						//   "type": "object",
						// 	 "properties": {
						// 	   "ComponentDependencies": {
						// 		 "type": "object",
						// 		 "patternProperties": {
						// 		   "": {
						// 			 "$ref": "#/definitions/ComponentDependencyRequirement"
						// 		   }
						// 		 }
						// 	   }
						// 	 }
						// },
						_, err = r.ResolveProperty(patternProperty)

						if err != nil {
							return fmt.Errorf("error resolving %s Property (%s) Pattern(%s): %w", propertyName, objPropertyName, pattern, err)
						}
					}
				}
			}

			for patternName, objProperty := range property.PatternProperties {
				resolved, err := r.ResolveProperty(objProperty)

				if err != nil {
					return fmt.Errorf("error resolving %s pattern Property (%s): %w", propertyName, patternName, err)
				}

				if resolved {
					// For example:
					// "Tags": {
					// 	 "type": "object",
					// 	 "patternProperties": {
					// 	   "": {
					// 		 "$ref": "#/definitions/TagValue"
					// 	   }
					// 	 }
					// },
					continue
				}

				switch objProperty.Type.String() {
				case PropertyTypeArray:
					// For example:
					// "Tags": {
					// 	 "type": "object",
					// 	 "patternProperties": {
					// 	   "": {
					// 		 "type": "array",
					// 		 "items": {
					// 		   "$ref": "#/definitions/TagValue"
					// 		 }
					// 	   }
					// 	 }
					// },
					_, err = r.ResolveProperty(objProperty.Items)

					if err != nil {
						return fmt.Errorf("error resolving %s Property (%s) Items: %w", propertyName, patternName, err)
					}
				}
			}
		}
	}

	return nil
}

// ResolveProperty resolves any Reference (JSON Pointer) in a Property.
// Returns whether a Reference was resolved.
func (r *Resource) ResolveProperty(property *Property) (bool, error) {
	if property != nil && property.Ref != nil {
		defaultValue := property.Default
		ref := property.Ref
		resolution, err := r.ResolveReference(*ref)

		if err != nil {
			return false, err
		}

		*property = *resolution

		// Ensure that any default value is not lost.
		property.Default = defaultValue

		return true, nil
	}

	return false, nil
}
<|endoftext|>


<commit_before>package terraform_vix

import (
	"log"

	"github.com/c4milo/govix"
	"github.com/hashicorp/terraform/flatmap"
	"github.com/hashicorp/terraform/helper/config"
	"github.com/hashicorp/terraform/helper/diff"
	"github.com/hashicorp/terraform/terraform"
)

func resource_vix_vm_validation() *config.Validator {
	return &config.Validator{
		Required: []string{
			"name",
			"image.*",
			"image.*.url",
			"image.*.checksum",
			"image.*.checksum_type",
		},
		Optional: []string{
			"description",
			"image.*.password",
			"cpus",
			"memory",
			"hardware_version",
			"network_driver",
			"networks.*",
		},
	}
}

func resource_vix_vm_create(
	s *terraform.ResourceState,
	d *terraform.ResourceDiff,
	meta interface{}) (*terraform.ResourceState, error) {
	//p := meta.(*ResourceProvider)
	//	client := p.client

	// Merge the diff into the state so that we have all the attributes
	// properly.
	rs := s.MergeDiff(d)

	// TODO(c4milo): Get image if it does not exist in ~/.terraform/vix/boxes
	image, ok := flatmap.Expand(rs.Attributes, "image").([]interface{})
	if ok {
		log.Printf("[DEBUG] Image ==> %v", image)
	}

	// TODO(c4milo): Check image integrity
	// TODO(c4milo): Unpack it in ~/.terraform/vix/images. if it does exist, clone the box into images
	// TODO(c4milo): OpenVM passing vmx path

	// TODO(c4milo): Set memory
	// TODO(c4milo): Set cpus
	// TODO(c4milo): Set networks
	// TODO(c4milo): Set hardware version
	// TODO(c4milo): Set network driver

	return nil, nil
}

func resource_vix_vm_update(
	s *terraform.ResourceState,
	d *terraform.ResourceDiff,
	meta interface{}) (*terraform.ResourceState, error) {
	//p := meta.(*ResourceProvider)

	return nil, nil
}

func resource_vix_vm_destroy(
	s *terraform.ResourceState,
	meta interface{}) error {
	// p := meta.(*ResourceProvider)
	// client := p.client

	return nil
}

func resource_vix_vm_diff(
	s *terraform.ResourceState,
	c *terraform.ResourceConfig,
	meta interface{}) (*terraform.ResourceDiff, error) {

	b := &diff.ResourceBuilder{
		// We have to choose whether a change in an attribute triggers a new
		// resource creation or updates the existing resource.
		Attrs: map[string]diff.AttrType{
			"name":             diff.AttrTypeCreate,
			"description":      diff.AttrTypeUpdate,
			"image":            diff.AttrTypeCreate,
			"cpus":             diff.AttrTypeUpdate,
			"memory":           diff.AttrTypeUpdate,
			"networks":         diff.AttrTypeUpdate,
			"hardware_version": diff.AttrTypeUpdate,
			"network_driver":   diff.AttrTypeUpdate,
		},

		ComputedAttrs: []string{
			"ip_address",
		},
	}

	return b.Diff(s, c)
}

func resource_vix_vm_refresh(
	s *terraform.ResourceState,
	meta interface{}) (*terraform.ResourceState, error) {

	return nil, nil
}

func resource_vix_vm_update_state(
	s *terraform.ResourceState,
	vm *vix.VM) (*terraform.ResourceState, error) {

	return nil, nil
}
<commit_msg>Work in progress<commit_after>package terraform_vix

import (
	"log"
	"strconv"

	"github.com/c4milo/govix"
	"github.com/hashicorp/terraform/flatmap"
	"github.com/hashicorp/terraform/helper/config"
	"github.com/hashicorp/terraform/helper/diff"
	"github.com/hashicorp/terraform/terraform"
)

func resource_vix_vm_validation() *config.Validator {
	return &config.Validator{
		Required: []string{
			"name",
			"image.*",
			"image.*.url",
			"image.*.checksum",
			"image.*.checksum_type",
		},
		Optional: []string{
			"description",
			"image.*.password",
			"cpus",
			"memory",
			"hardware_version",
			"network_driver",
			"networks.*",
		},
	}
}

func resource_vix_vm_create(
	s *terraform.ResourceState,
	d *terraform.ResourceDiff,
	meta interface{}) (*terraform.ResourceState, error) {
	//p := meta.(*ResourceProvider)
	//client := p.client

	// Merge the diff into the state so that we have all the attributes
	// properly.
	rs := s.MergeDiff(d)

	name := rs.Attributes["name"]
	description := rs.Attributes["name"]
	image := flatmap.Expand(rs.Attributes, "image").([]interface{})
	cpus, err := strconv.ParseInt(rs.Attributes["cpus"], 0, 16)
	memory := rs.Attributes["memory"]
	hwversion, err := strconv.ParseInt(rs.Attributes["hardware_version"], 0, 8)
	netdrv := rs.Attributes["network_driver"]
	var networks []string

	if err != nil {
		return nil, err
	}

	if raw := flatmap.Expand(rs.Attributes, "networks"); raw != nil {
		if nets, ok := raw.([]interface{}); ok {
			for _, net := range nets {
				str, ok := net.(string)
				if !ok {
					continue
				}

				networks = append(networks, str)
			}
		}
	}

	log.Printf("[DEBUG] networks => %v", networks)

	if len(networks) == 0 {
		networks = append(networks, "bridged")
	}

	log.Printf("[DEBUG] Name => %s", name)
	log.Printf("[DEBUG] Description => %s", description)
	log.Printf("[DEBUG] image => %v", image)
	log.Printf("[DEBUG] CPUs => %s", cpus)
	log.Printf("[DEBUG] Memory => %s", memory)
	log.Printf("[DEBUG] hwversion => %s", hwversion)
	log.Printf("[DEBUG] netdrv => %s", netdrv)

	// TODO: Check if there is an image already in ~/.terraform/vix/images/{.Name}
	// usr, err := user.Current()
	// if err != nil {
	// 	return nil, err
	// }
	// fmt.Println(usr.HomeDir)

	// path := ""
	// _, err := os.Stat(filepath.Join(usr.HomeDir, fmt.Sprintf(".terraform/vix/images/%s", ))
	// if err == nil {
	// 	return true, nil
	// }
	// if os.IsNotExist(err) {
	// 	return false, nil
	// }

	// TODO(c4milo): Get image
	// TODO(c4milo): Check image integrity
	// TODO(c4milo): Unpack it in ~/.terraform/vix/images. if it does exist, clone the box into images
	// TODO(c4milo): OpenVM passing vmx path

	// TODO(c4milo): Set memory
	// TODO(c4milo): Set cpus
	// TODO(c4milo): Set networks
	// TODO(c4milo): Set hardware version
	// TODO(c4milo): Set network driver

	return nil, nil
}

func resource_vix_vm_update(
	s *terraform.ResourceState,
	d *terraform.ResourceDiff,
	meta interface{}) (*terraform.ResourceState, error) {
	//p := meta.(*ResourceProvider)

	return nil, nil
}

func resource_vix_vm_destroy(
	s *terraform.ResourceState,
	meta interface{}) error {
	// p := meta.(*ResourceProvider)
	// client := p.client

	return nil
}

func resource_vix_vm_diff(
	s *terraform.ResourceState,
	c *terraform.ResourceConfig,
	meta interface{}) (*terraform.ResourceDiff, error) {

	b := &diff.ResourceBuilder{
		// We have to choose whether a change in an attribute triggers a new
		// resource creation or updates the existing resource.
		Attrs: map[string]diff.AttrType{
			"name":             diff.AttrTypeCreate,
			"description":      diff.AttrTypeUpdate,
			"image":            diff.AttrTypeCreate,
			"cpus":             diff.AttrTypeUpdate,
			"memory":           diff.AttrTypeUpdate,
			"networks":         diff.AttrTypeUpdate,
			"hardware_version": diff.AttrTypeUpdate,
			"network_driver":   diff.AttrTypeUpdate,
		},

		ComputedAttrs: []string{
			"ip_address",
		},
	}

	return b.Diff(s, c)
}

func resource_vix_vm_refresh(
	s *terraform.ResourceState,
	meta interface{}) (*terraform.ResourceState, error) {

	return nil, nil
}

func resource_vix_vm_update_state(
	s *terraform.ResourceState,
	vm *vix.VM) (*terraform.ResourceState, error) {

	return nil, nil
}
<|endoftext|>


<commit_before><commit_msg>Simplify page name logic<commit_after><|endoftext|>


<commit_before>// Copyright 2016 Apcera Inc. All rights reserved.

package bench

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"log"
	"math"
	"strconv"
	"time"

	"github.com/nats-io/go-nats"
	"github.com/nats-io/nuid"
	"sort"
	"os"
)

// A Sample for a particular client
type Sample struct {
	JobMsgCnt int
	MsgCnt    uint64
	MsgBytes  uint64
	IOBytes   uint64
	Start     time.Time
	End       time.Time
}

// SampleGroup for a number of samples, the group is a Sample itself agregating the values the Samples
type SampleGroup struct {
	Sample
	Samples []*Sample
}

// Benchmark to hold the various Samples organized by publishers and subscribers
type Benchmark struct {
	Sample
	Name       string
	RunID      string
	Pubs       *SampleGroup
	Subs       *SampleGroup
	subChannel chan *Sample
	pubChannel chan *Sample
	subLatency []int
	subLatencyIndex []int
}

// NewBenchmark initializes a Benchmark. After creating a bench call AddSubSample/AddPubSample.
// When done collecting samples, call EndBenchmark
func NewBenchmark(name string, subCnt, pubCnt int, num int) *Benchmark {
	bm := Benchmark{Name: name, RunID: nuid.Next()}
	bm.Subs = NewSampleGroup()
	bm.Pubs = NewSampleGroup()
	bm.subChannel = make(chan *Sample, subCnt)
	bm.pubChannel = make(chan *Sample, pubCnt)
	bm.subLatency= make([]int, subCnt*num)
	bm.subLatencyIndex= make([]int, num)
	return &bm
}

func (bm *Benchmark) AddSubLatency(subIndex int, latency int) {
	bm.subLatency[bm.subLatencyIndex[subIndex]]=latency
	bm.subLatencyIndex[subIndex]+=1
}

func (bm *Benchmark) ExportLatency(size int, pariNum int){
	sort.Ints(bm.subLatency)
	TIME_SLICE:=1000
	numMsgsForEachSlice := len(bm.subLatency) / TIME_SLICE
	f, _ := os.Create(fmt.Sprintf("%s%d%s%d%s","./latency/",pariNum,"pair_subLatency_",size,".csv"))
	f.WriteString("Index,Latency\n")
	for i := 0; i < len(bm.subLatency); i++ {
		if i%numMsgsForEachSlice == 0 {
			f.WriteString(fmt.Sprintf("%d%s%d%s",i/numMsgsForEachSlice,
				",", bm.subLatency[i+numMsgsForEachSlice-1], "\n"))
		}
	}
	f.Sync()
}

// Close organizes collected Samples and calculates aggregates. After Close(), no more samples can be added.
func (bm *Benchmark) Close() {
	close(bm.subChannel)
	close(bm.pubChannel)

	for s := range bm.subChannel {
		bm.Subs.AddSample(s)
	}
	for s := range bm.pubChannel {
		bm.Pubs.AddSample(s)
	}

	if bm.Subs.HasSamples() {
		bm.Start = bm.Subs.Start
		bm.End = bm.Subs.End
	} else {
		bm.Start = bm.Pubs.Start
		bm.End = bm.Pubs.End
	}

	if bm.Subs.HasSamples() && bm.Pubs.HasSamples() {
		if bm.Start.After(bm.Subs.Start) {
			bm.Start = bm.Subs.Start
		}
		if bm.Start.After(bm.Pubs.Start) {
			bm.Start = bm.Pubs.Start
		}

		if bm.End.Before(bm.Subs.End) {
			bm.End = bm.Subs.End
		}
		if bm.End.Before(bm.Pubs.End) {
			bm.End = bm.Pubs.End
		}
	}

	bm.MsgBytes = bm.Pubs.MsgBytes + bm.Subs.MsgBytes
	bm.IOBytes = bm.Pubs.IOBytes + bm.Subs.IOBytes
	bm.MsgCnt = bm.Pubs.MsgCnt + bm.Subs.MsgCnt
	bm.JobMsgCnt = bm.Pubs.JobMsgCnt + bm.Subs.JobMsgCnt
}

// AddSubSample to the benchmark
func (bm *Benchmark) AddSubSample(s *Sample) {
	bm.subChannel <- s
}

// AddPubSample to the benchmark
func (bm *Benchmark) AddPubSample(s *Sample) {
	bm.pubChannel <- s
}

// CSV generates a csv report of all the samples collected
func (bm *Benchmark) CSV() string {
	var buffer bytes.Buffer
	writer := csv.NewWriter(&buffer)
	headers := []string{"#RunID", "ClientID", "MsgCount", "MsgBytes", "MsgsPerSec", "BytesPerSec", "DurationSecs"}
	if err := writer.Write(headers); err != nil {
		log.Fatalf("Error while serializing headers %q: %v", headers, err)
	}
	groups := []*SampleGroup{bm.Subs, bm.Pubs}
	pre := "S"
	for i, g := range groups {
		if i == 1 {
			pre = "P"
		}
		for j, c := range g.Samples {
			r := []string{bm.RunID, fmt.Sprintf("%s%d", pre, j), fmt.Sprintf("%d", c.MsgCnt), fmt.Sprintf("%d", c.MsgBytes), fmt.Sprintf("%d", c.Rate()), fmt.Sprintf("%f", c.Throughput()), fmt.Sprintf("%f", c.Duration().Seconds())}
			if err := writer.Write(r); err != nil {
				log.Fatalf("Error while serializing %v: %v", c, err)
			}
		}
	}

	writer.Flush()
	return buffer.String()
}

// NewSample creates a new Sample initialized to the provided values. The nats.Conn information captured
func NewSample(jobCount int, msgSize int, start, end time.Time, nc *nats.Conn) *Sample {
	s := Sample{JobMsgCnt: jobCount, Start: start, End: end}
	s.MsgBytes = uint64(msgSize * jobCount)
	s.MsgCnt = nc.OutMsgs + nc.InMsgs
	s.IOBytes = nc.OutBytes + nc.InBytes
	return &s
}

// Throughput of bytes per second
func (s *Sample) Throughput() float64 {
	return float64(s.MsgBytes) / s.Duration().Seconds()
}

// Rate of meessages in the job per second
func (s *Sample) Rate() int64 {
	return int64(float64(s.JobMsgCnt) / s.Duration().Seconds())
}

func (s *Sample) String() string {
	rate := commaFormat(s.Rate())
	throughput := HumanBytes(s.Throughput(), false)
	return fmt.Sprintf("%s msgs/sec ~ %s/sec", rate, throughput)
}

// Duration that the sample was active
func (s *Sample) Duration() time.Duration {
	return s.End.Sub(s.Start)
}

// Seconds that the sample or samples were active
func (s *Sample) Seconds() float64 {
	return s.Duration().Seconds()
}

// NewSampleGroup initializer
func NewSampleGroup() *SampleGroup {
	s := new(SampleGroup)
	s.Samples = make([]*Sample, 0)
	return s
}

// Statistics information of the sample group (min, average, max and standard deviation)
func (sg *SampleGroup) Statistics() string {
	return fmt.Sprintf("min %s | avg %s | max %s | stddev %s msgs", commaFormat(sg.MinRate()), commaFormat(sg.AvgRate()), commaFormat(sg.MaxRate()), commaFormat(int64(sg.StdDev())))
}

// MinRate returns the smallest message rate in the SampleGroup
func (sg *SampleGroup) MinRate() int64 {
	m := int64(0)
	for i, s := range sg.Samples {
		if i == 0 {
			m = s.Rate()
		}
		m = min(m, s.Rate())
	}
	return m
}

// MaxRate returns the largest message rate in the SampleGroup
func (sg *SampleGroup) MaxRate() int64 {
	m := int64(0)
	for i, s := range sg.Samples {
		if i == 0 {
			m = s.Rate()
		}
		m = max(m, s.Rate())
	}
	return m
}

// AvgRate returns the average of all the message rates in the SampleGroup
func (sg *SampleGroup) AvgRate() int64 {
	sum := uint64(0)
	for _, s := range sg.Samples {
		sum += uint64(s.Rate())
	}
	return int64(sum / uint64(len(sg.Samples)))
}

// StdDev returns the standard deviation the message rates in the SampleGroup
func (sg *SampleGroup) StdDev() float64 {
	avg := float64(sg.AvgRate())
	sum := float64(0)
	for _, c := range sg.Samples {
		sum += math.Pow(float64(c.Rate())-avg, 2)
	}
	variance := sum / float64(len(sg.Samples))
	return math.Sqrt(variance)
}

// AddSample adds a Sample to the SampleGroup. After adding a Sample it shouldn't be modified.
func (sg *SampleGroup) AddSample(e *Sample) {
	sg.Samples = append(sg.Samples, e)

	if len(sg.Samples) == 1 {
		sg.Start = e.Start
		sg.End = e.End
	}
	sg.IOBytes += e.IOBytes
	sg.JobMsgCnt += e.JobMsgCnt
	sg.MsgCnt += e.MsgCnt
	sg.MsgBytes += e.MsgBytes

	if e.Start.Before(sg.Start) {
		sg.Start = e.Start
	}

	if e.End.After(sg.End) {
		sg.End = e.End
	}
}

// HasSamples returns true if the group has samples
func (sg *SampleGroup) HasSamples() bool {
	return len(sg.Samples) > 0
}

// Report returns a human readable report of the samples taken in the Benchmark
func (bm *Benchmark) Report() string {
	var buffer bytes.Buffer

	indent := ""
	if !bm.Pubs.HasSamples() && !bm.Subs.HasSamples() {
		return "No publisher or subscribers. Nothing to report."
	}

	if bm.Pubs.HasSamples() && bm.Subs.HasSamples() {
		buffer.WriteString(fmt.Sprintf("%s Pub/Sub stats: %s\n", bm.Name, bm))
		indent += " "
	}
	if bm.Pubs.HasSamples() {
		buffer.WriteString(fmt.Sprintf("%sPub stats: %s\n", indent, bm.Pubs))
		if len(bm.Pubs.Samples) > 1 {
			for i, stat := range bm.Pubs.Samples {
				buffer.WriteString(fmt.Sprintf("%s [%d] %v (%d msgs)\n", indent, i+1, stat, stat.JobMsgCnt))
			}
			buffer.WriteString(fmt.Sprintf("%s %s\n", indent, bm.Pubs.Statistics()))
		}
	}

	if bm.Subs.HasSamples() {
		buffer.WriteString(fmt.Sprintf("%sSub stats: %s\n", indent, bm.Subs))
		if len(bm.Subs.Samples) > 1 {
			for i, stat := range bm.Subs.Samples {
				buffer.WriteString(fmt.Sprintf("%s [%d] %v (%d msgs)\n", indent, i+1, stat, stat.JobMsgCnt))
			}
			buffer.WriteString(fmt.Sprintf("%s %s\n", indent, bm.Subs.Statistics()))
		}
	}
	return buffer.String()
}

func commaFormat(n int64) string {
	in := strconv.FormatInt(n, 10)
	out := make([]byte, len(in)+(len(in)-2+int(in[0]/'0'))/3)
	if in[0] == '-' {
		in, out[0] = in[1:], '-'
	}
	for i, j, k := len(in)-1, len(out)-1, 0; ; i, j = i-1, j-1 {
		out[j] = in[i]
		if i == 0 {
			return string(out)
		}
		if k++; k == 3 {
			j, k = j-1, 0
			out[j] = ','
		}
	}
}

// HumanBytes formats bytes as a human readable string
func HumanBytes(bytes float64, si bool) string {
	var base = 1024
	pre := []string{"K", "M", "G", "T", "P", "E"}
	var post = "B"
	if si {
		base = 1000
		pre = []string{"k", "M", "G", "T", "P", "E"}
		post = "iB"
	}
	if bytes < float64(base) {
		return fmt.Sprintf("%.2f B", bytes)
	}
	exp := int(math.Log(bytes) / math.Log(float64(base)))
	index := exp - 1
	units := pre[index] + post
	return fmt.Sprintf("%.2f %s", bytes/math.Pow(float64(base), float64(exp)), units)
}

func min(x, y int64) int64 {
	if x < y {
		return x
	}
	return y
}

func max(x, y int64) int64 {
	if x > y {
		return x
	}
	return y
}

// MsgsPerClient divides the number of messages by the number of clients and tries to distribute them as evenly as possible
func MsgsPerClient(numMsgs, numClients int) []int {
	var counts []int
	if numClients == 0 || numMsgs == 0 {
		return counts
	}
	counts = make([]int, numClients)
	mc := numMsgs / numClients
	for i := 0; i < numClients; i++ {
		counts[i] = mc
	}
	extra := numMsgs % numClients
	for i := 0; i < extra; i++ {
		counts[i]++
	}
	return counts
}
<commit_msg>fix small bug<commit_after>// Copyright 2016 Apcera Inc. All rights reserved.

package bench

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"log"
	"math"
	"strconv"
	"time"

	"github.com/nats-io/go-nats"
	"github.com/nats-io/nuid"
	"sort"
	"os"
)

// A Sample for a particular client
type Sample struct {
	JobMsgCnt int
	MsgCnt    uint64
	MsgBytes  uint64
	IOBytes   uint64
	Start     time.Time
	End       time.Time
}

// SampleGroup for a number of samples, the group is a Sample itself agregating the values the Samples
type SampleGroup struct {
	Sample
	Samples []*Sample
}

// Benchmark to hold the various Samples organized by publishers and subscribers
type Benchmark struct {
	Sample
	Name       string
	RunID      string
	Pubs       *SampleGroup
	Subs       *SampleGroup
	subChannel chan *Sample
	pubChannel chan *Sample
	subLatency []int
	subLatencyIndex []int
	pairNum int
}

// NewBenchmark initializes a Benchmark. After creating a bench call AddSubSample/AddPubSample.
// When done collecting samples, call EndBenchmark
func NewBenchmark(name string, subCnt, pubCnt int, num int) *Benchmark {
	bm := Benchmark{Name: name, RunID: nuid.Next()}
	bm.Subs = NewSampleGroup()
	bm.Pubs = NewSampleGroup()
	bm.subChannel = make(chan *Sample, subCnt)
	bm.pubChannel = make(chan *Sample, pubCnt)
	bm.subLatency= make([]int, subCnt*num)
	bm.subLatencyIndex= make([]int, num)
	bm.pairNum=subCnt
	return &bm
}

func (bm *Benchmark) AddSubLatency(subIndex int, latency int) {
	bm.subLatency[bm.subLatencyIndex[subIndex]]=latency
	bm.subLatencyIndex[subIndex]+=1
}

func (bm *Benchmark) ExportLatency(size int){
	sort.Ints(bm.subLatency)
	TIME_SLICE:=1000
	numMsgsForEachSlice := len(bm.subLatency) / TIME_SLICE
	f, _ := os.Create(fmt.Sprintf("%s%d%s%d%s","./latency/",bm.pairNum,"pair_subLatency_",size,".csv"))
	f.WriteString("Index,Latency\n")
	for i := 0; i < len(bm.subLatency); i++ {
		if i%numMsgsForEachSlice == 0 {
			f.WriteString(fmt.Sprintf("%d%s%d%s",i/numMsgsForEachSlice,
				",", bm.subLatency[i+numMsgsForEachSlice-1], "\n"))
		}
	}
	f.Sync()
}

// Close organizes collected Samples and calculates aggregates. After Close(), no more samples can be added.
func (bm *Benchmark) Close() {
	close(bm.subChannel)
	close(bm.pubChannel)

	for s := range bm.subChannel {
		bm.Subs.AddSample(s)
	}
	for s := range bm.pubChannel {
		bm.Pubs.AddSample(s)
	}

	if bm.Subs.HasSamples() {
		bm.Start = bm.Subs.Start
		bm.End = bm.Subs.End
	} else {
		bm.Start = bm.Pubs.Start
		bm.End = bm.Pubs.End
	}

	if bm.Subs.HasSamples() && bm.Pubs.HasSamples() {
		if bm.Start.After(bm.Subs.Start) {
			bm.Start = bm.Subs.Start
		}
		if bm.Start.After(bm.Pubs.Start) {
			bm.Start = bm.Pubs.Start
		}

		if bm.End.Before(bm.Subs.End) {
			bm.End = bm.Subs.End
		}
		if bm.End.Before(bm.Pubs.End) {
			bm.End = bm.Pubs.End
		}
	}

	bm.MsgBytes = bm.Pubs.MsgBytes + bm.Subs.MsgBytes
	bm.IOBytes = bm.Pubs.IOBytes + bm.Subs.IOBytes
	bm.MsgCnt = bm.Pubs.MsgCnt + bm.Subs.MsgCnt
	bm.JobMsgCnt = bm.Pubs.JobMsgCnt + bm.Subs.JobMsgCnt
}

// AddSubSample to the benchmark
func (bm *Benchmark) AddSubSample(s *Sample) {
	bm.subChannel <- s
}

// AddPubSample to the benchmark
func (bm *Benchmark) AddPubSample(s *Sample) {
	bm.pubChannel <- s
}

// CSV generates a csv report of all the samples collected
func (bm *Benchmark) CSV() string {
	var buffer bytes.Buffer
	writer := csv.NewWriter(&buffer)
	headers := []string{"#RunID", "ClientID", "MsgCount", "MsgBytes", "MsgsPerSec", "BytesPerSec", "DurationSecs"}
	if err := writer.Write(headers); err != nil {
		log.Fatalf("Error while serializing headers %q: %v", headers, err)
	}
	groups := []*SampleGroup{bm.Subs, bm.Pubs}
	pre := "S"
	for i, g := range groups {
		if i == 1 {
			pre = "P"
		}
		for j, c := range g.Samples {
			r := []string{bm.RunID, fmt.Sprintf("%s%d", pre, j), fmt.Sprintf("%d", c.MsgCnt), fmt.Sprintf("%d", c.MsgBytes), fmt.Sprintf("%d", c.Rate()), fmt.Sprintf("%f", c.Throughput()), fmt.Sprintf("%f", c.Duration().Seconds())}
			if err := writer.Write(r); err != nil {
				log.Fatalf("Error while serializing %v: %v", c, err)
			}
		}
	}

	writer.Flush()
	return buffer.String()
}

// NewSample creates a new Sample initialized to the provided values. The nats.Conn information captured
func NewSample(jobCount int, msgSize int, start, end time.Time, nc *nats.Conn) *Sample {
	s := Sample{JobMsgCnt: jobCount, Start: start, End: end}
	s.MsgBytes = uint64(msgSize * jobCount)
	s.MsgCnt = nc.OutMsgs + nc.InMsgs
	s.IOBytes = nc.OutBytes + nc.InBytes
	return &s
}

// Throughput of bytes per second
func (s *Sample) Throughput() float64 {
	return float64(s.MsgBytes) / s.Duration().Seconds()
}

// Rate of meessages in the job per second
func (s *Sample) Rate() int64 {
	return int64(float64(s.JobMsgCnt) / s.Duration().Seconds())
}

func (s *Sample) String() string {
	rate := commaFormat(s.Rate())
	throughput := HumanBytes(s.Throughput(), false)
	return fmt.Sprintf("%s msgs/sec ~ %s/sec", rate, throughput)
}

// Duration that the sample was active
func (s *Sample) Duration() time.Duration {
	return s.End.Sub(s.Start)
}

// Seconds that the sample or samples were active
func (s *Sample) Seconds() float64 {
	return s.Duration().Seconds()
}

// NewSampleGroup initializer
func NewSampleGroup() *SampleGroup {
	s := new(SampleGroup)
	s.Samples = make([]*Sample, 0)
	return s
}

// Statistics information of the sample group (min, average, max and standard deviation)
func (sg *SampleGroup) Statistics() string {
	return fmt.Sprintf("min %s | avg %s | max %s | stddev %s msgs", commaFormat(sg.MinRate()), commaFormat(sg.AvgRate()), commaFormat(sg.MaxRate()), commaFormat(int64(sg.StdDev())))
}

// MinRate returns the smallest message rate in the SampleGroup
func (sg *SampleGroup) MinRate() int64 {
	m := int64(0)
	for i, s := range sg.Samples {
		if i == 0 {
			m = s.Rate()
		}
		m = min(m, s.Rate())
	}
	return m
}

// MaxRate returns the largest message rate in the SampleGroup
func (sg *SampleGroup) MaxRate() int64 {
	m := int64(0)
	for i, s := range sg.Samples {
		if i == 0 {
			m = s.Rate()
		}
		m = max(m, s.Rate())
	}
	return m
}

// AvgRate returns the average of all the message rates in the SampleGroup
func (sg *SampleGroup) AvgRate() int64 {
	sum := uint64(0)
	for _, s := range sg.Samples {
		sum += uint64(s.Rate())
	}
	return int64(sum / uint64(len(sg.Samples)))
}

// StdDev returns the standard deviation the message rates in the SampleGroup
func (sg *SampleGroup) StdDev() float64 {
	avg := float64(sg.AvgRate())
	sum := float64(0)
	for _, c := range sg.Samples {
		sum += math.Pow(float64(c.Rate())-avg, 2)
	}
	variance := sum / float64(len(sg.Samples))
	return math.Sqrt(variance)
}

// AddSample adds a Sample to the SampleGroup. After adding a Sample it shouldn't be modified.
func (sg *SampleGroup) AddSample(e *Sample) {
	sg.Samples = append(sg.Samples, e)

	if len(sg.Samples) == 1 {
		sg.Start = e.Start
		sg.End = e.End
	}
	sg.IOBytes += e.IOBytes
	sg.JobMsgCnt += e.JobMsgCnt
	sg.MsgCnt += e.MsgCnt
	sg.MsgBytes += e.MsgBytes

	if e.Start.Before(sg.Start) {
		sg.Start = e.Start
	}

	if e.End.After(sg.End) {
		sg.End = e.End
	}
}

// HasSamples returns true if the group has samples
func (sg *SampleGroup) HasSamples() bool {
	return len(sg.Samples) > 0
}

// Report returns a human readable report of the samples taken in the Benchmark
func (bm *Benchmark) Report() string {
	var buffer bytes.Buffer

	indent := ""
	if !bm.Pubs.HasSamples() && !bm.Subs.HasSamples() {
		return "No publisher or subscribers. Nothing to report."
	}

	if bm.Pubs.HasSamples() && bm.Subs.HasSamples() {
		buffer.WriteString(fmt.Sprintf("%s Pub/Sub stats: %s\n", bm.Name, bm))
		indent += " "
	}
	if bm.Pubs.HasSamples() {
		buffer.WriteString(fmt.Sprintf("%sPub stats: %s\n", indent, bm.Pubs))
		if len(bm.Pubs.Samples) > 1 {
			for i, stat := range bm.Pubs.Samples {
				buffer.WriteString(fmt.Sprintf("%s [%d] %v (%d msgs)\n", indent, i+1, stat, stat.JobMsgCnt))
			}
			buffer.WriteString(fmt.Sprintf("%s %s\n", indent, bm.Pubs.Statistics()))
		}
	}

	if bm.Subs.HasSamples() {
		buffer.WriteString(fmt.Sprintf("%sSub stats: %s\n", indent, bm.Subs))
		if len(bm.Subs.Samples) > 1 {
			for i, stat := range bm.Subs.Samples {
				buffer.WriteString(fmt.Sprintf("%s [%d] %v (%d msgs)\n", indent, i+1, stat, stat.JobMsgCnt))
			}
			buffer.WriteString(fmt.Sprintf("%s %s\n", indent, bm.Subs.Statistics()))
		}
	}
	return buffer.String()
}

func commaFormat(n int64) string {
	in := strconv.FormatInt(n, 10)
	out := make([]byte, len(in)+(len(in)-2+int(in[0]/'0'))/3)
	if in[0] == '-' {
		in, out[0] = in[1:], '-'
	}
	for i, j, k := len(in)-1, len(out)-1, 0; ; i, j = i-1, j-1 {
		out[j] = in[i]
		if i == 0 {
			return string(out)
		}
		if k++; k == 3 {
			j, k = j-1, 0
			out[j] = ','
		}
	}
}

// HumanBytes formats bytes as a human readable string
func HumanBytes(bytes float64, si bool) string {
	var base = 1024
	pre := []string{"K", "M", "G", "T", "P", "E"}
	var post = "B"
	if si {
		base = 1000
		pre = []string{"k", "M", "G", "T", "P", "E"}
		post = "iB"
	}
	if bytes < float64(base) {
		return fmt.Sprintf("%.2f B", bytes)
	}
	exp := int(math.Log(bytes) / math.Log(float64(base)))
	index := exp - 1
	units := pre[index] + post
	return fmt.Sprintf("%.2f %s", bytes/math.Pow(float64(base), float64(exp)), units)
}

func min(x, y int64) int64 {
	if x < y {
		return x
	}
	return y
}

func max(x, y int64) int64 {
	if x > y {
		return x
	}
	return y
}

// MsgsPerClient divides the number of messages by the number of clients and tries to distribute them as evenly as possible
func MsgsPerClient(numMsgs, numClients int) []int {
	var counts []int
	if numClients == 0 || numMsgs == 0 {
		return counts
	}
	counts = make([]int, numClients)
	mc := numMsgs / numClients
	for i := 0; i < numClients; i++ {
		counts[i] = mc
	}
	extra := numMsgs % numClients
	for i := 0; i < extra; i++ {
		counts[i]++
	}
	return counts
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	fmt.Println("")

	var minRanking int
	var maxRanking int

	// get all foods with rankings from db
	var foods = [3]Food{{"pizza", 5}, {"burger", 8}, {"salat", 16}}

	// calculate chances
	var rankings []Ranking

	for i := 0; i <= len(foods)-1; i++ {
		fmt.Printf("%v\n", foods[i])
		food := foods[i]
		maxRanking += food.rank
		rankings = append(rankings, Ranking{food.name, minRanking, maxRanking})
		minRanking += food.rank
	}

	// pick chance
	seed := rand.NewSource(time.Now().UnixNano())
	random := rand.New(seed)
	pick := random.Intn(maxRanking)
	fmt.Printf("pick is %v\n", pick)

	// select food from pick
	var pickedFood string
	for i := 0; i <= len(rankings)-1; i++ {
		rank := rankings[i]
		if pick >= rank.min && pick <= rank.max {
			pickedFood = rank.name
		}
	}
	// show picked food
	fmt.Printf("You have to eat %s\n", pickedFood)
	// update db

}

// Food holds name and ranking
type Food struct {
	name string
	rank int
}

// Ranking holds food namen and min / max ranking
type Ranking struct {
	name string
	min  int
	max  int
}

// GetName return name of the food
func (food Food) GetName() string {
	return food.name
}
<commit_msg>udpate db<commit_after>package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {

	var minRanking int
	var maxRanking int

	// get all foods with rankings from db
	var foods = [3]Food{{"pizza", 10}, {"burger", 20}, {"salat", 30}}

	// calculate chances
	var rankings []Ranking

	for i := 0; i <= len(foods)-1; i++ {
		food := foods[i]
		maxRanking += food.rank
		rankings = append(rankings, Ranking{food.name, minRanking, maxRanking})
		minRanking += food.rank
	}

	// show chances
	var ranking Ranking
	var chance float32
	fmt.Println("Chances are: ")
	for i := 0; i <= len(rankings)-1; i++ {
		ranking = rankings[i]
		chance = ((float32(ranking.max) - float32(ranking.min)) / float32(maxRanking))
		chance = chance * 100
		fmt.Printf("\n\t%s for\t %2.2f %s", ranking.name, chance, "%")
	}

	// pick chance
	seed := rand.NewSource(time.Now().UnixNano())
	random := rand.New(seed)
	pick := random.Intn(maxRanking)

	// select food from pick
	var pickedFood string
	for i := 0; i <= len(rankings)-1; i++ {
		ranking := rankings[i]
		if pick >= ranking.min && pick <= ranking.max {
			pickedFood = ranking.name
		}
	}

	// show picked food
	fmt.Printf("\nYou have to eat %s\n", pickedFood)

	// update db
	for i := 0; i <= len(foods)-1; i++ {
		food := foods[i]
		if food.name == pickedFood {
			foods[i].rank++
		}
	}
	// fmt.Println(foods)
}

// Food holds name and current ranking
type Food struct {
	name string
	rank int
}

// Ranking holds food name and rating range for pick
type Ranking struct {
	name string
	min  int
	max  int
}
<|endoftext|>


<commit_before>/* Sandbox structs, for testing whether cookbook files need to be uploaded */

/*
 * Copyright (c) 2013-2014, Jeremy Bingham (<jbingham@gmail.com>)
 *
 * 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 sandbox allows checking files before re-uploading the, so any given
// version of a file need only be uploaded once rather than being uploaded
// repeatedly.
package sandbox

import (
	"github.com/ctdk/goiardi/data_store"
	"github.com/ctdk/goiardi/filestore"
	"github.com/ctdk/goiardi/util"
	"fmt"
	"crypto/md5"
	"crypto/rand"
	"io"
	"log"
	"time"
	"database/sql"
)

/* The structure of the sandbox responses is... inconsistent. */

type Sandbox struct {
	Id string
	CreationTime time.Time
	Completed bool
	Checksums []string
}

/* We actually generate the sandbox_id ourselves, so we don't pass that in. */

// Create a new sandbox, given a map of null values with file checksums as keys.
func New(checksum_hash map[string]interface{}) (*Sandbox, error){
	/* For some reason the checksums come in a JSON hash that looks like
	 * this:
 	 * { "checksums": {
	 * "385ea5490c86570c7de71070bce9384a":null,
  	 * "f6f73175e979bd90af6184ec277f760c":null,
  	 * "2e03dd7e5b2e6c8eab1cf41ac61396d5":null
  	 * } } --- per the chef server api docs. Not sure why it comes in that
	 * way rather than as an array, since those nulls are apparently never
	 * anything but nulls. */

	/* First generate an id for this sandbox. Collisions are certainly
	 * possible, so we'll give it five tries to make a unique one before
	 * bailing. This may later turn out not to be the ideal sandbox creation
	 * method, but we'll see. */
	var sandbox_id string
	var err error
	for i := 0; i < 5; i++ {
		sandbox_id, err = generate_sandbox_id()
		if err != nil {
			/* Something went very wrong. */
			return nil, err
		}
		if s := Get(sandbox_id); s != nil {
			err = fmt.Errorf("Collision! Somehow %s already existed as a sandbox id on attempt %d. Trying again.", sandbox_id, i)
			sandbox_id = ""
			log.Println(err)
		}
	}

	if sandbox_id == "" {
		err = fmt.Errorf("Somehow every attempt to create a unique sandbox id failed. Bailing.")
		return nil, err
	}
	checksums := make([]string, len(checksum_hash))
	j := 0
	for k, _ := range checksum_hash {
		checksums[j] = k
		j++
	}

	sbox := &Sandbox{
		Id: sandbox_id,
		CreationTime: time.Now(),
		Completed: false,
		Checksums: checksums,
	}
	return sbox, nil
}

func generate_sandbox_id() (string, error) {
	randnum := 20
	b := make([]byte, randnum)
	n, err := io.ReadFull(rand.Reader, b)
	if n != len(b) || err != nil {
		return "", err
	}
	id_md5 := md5.New()
	id_md5.Write(b)
	sandbox_id := fmt.Sprintf("%x", id_md5.Sum(nil))
	return sandbox_id, nil
}

func (s *Sandbox)fillSandboxFromSQL(row *sql.Row) error {
	if config.Config.UseMySQL {
		var csb []byte
		err := row.Scan(&s.Id, &s.CreationTime, &csb, &s.Completed)
		if err != nil {
			return err
		}
		var q interface{}
		q, err = data_store.DecodeBlob(csb, s.Checksums)
		if err != nil {
			return err
		}
		s.Checksums = q.([]string)
	} else {
		err := fmt.Errorf("no database configured, operating in in-memory mode -- fillSandboxFromSQL cannot be run")
		return err
	}
}

func Get(sandbox_id string) (*Sandbox, error){
	var sandbox *Sandbox
	var found bool

	if config.Config.UseMySQL {
		sandbox = new(Sandbox)
		stmt, err := data_store.Dbh.Prepare("SELECT sbox_id, creation_time, checksums, completed FROM sandboxes WHERE sbox_id = ?")
		if err != nil {
			return nil, err
		}
		defer stmt.Close()
		row := stmt.QueryRow(sandbox_id)
		err = sandbox.fillSandboxFromSQL(sandbox_id)
		if err != nil {
			if err == sql.ErrNoRows {
				found = false
			} else {
				return nil, err
			}
		} else {
			found = true
		}
	} else {
		ds := data_store.New()
		var s interface{}
		s, found = ds.Get("sandbox", sandbox_id)
		sandbox = s.(*Sandbox)
	}

	if !found {
		err := fmt.Errorf("Sandbox %s not found", sandbox_id)
		return nil, err
	}
	return sandbox, nil
}

func (s *Sandbox) Save() error {
	if config.Config.UseMySQL {
		ckb, ckerr := data_store.EncodeBlob(s.Checksums)
		if ckerr != nil {
			return ckerr
		}
		tx, err := data_store.Dbh.Begin()
		if err != nil {
			return err
		}
		var sbox_id string
		err = tx.QueryRow(s.Id).Scan(&sbox_id)
		if err == nil {
			_, err = tx.Exec("UPDATE sandboxes SET checksums = ?, completed = ? WHERE sbox_id = ?", ckb, s.Completed, s.Id)
				if err != nil {
					tx.Rollback()
					return err
				}
		} else {
			if err != sql.ErrNoRows {
				tx.Rollback()
				return err
			}
			_, err = tx.Exec("INSERT INTO sandboxes (sbox_id, creation_time, checksums, completed) VALUES (?, ?, ?, ?)", s.Id, s.CreationTime, ckb, s.Completed)
			if err != nil {
				tx.Rollback()
				return err
			}
		}
		tx.Commit()
	} else {
		ds := data_store.New()
		ds.Set("sandbox", s.Id, s)
	}
	return nil
}

func (s *Sandbox) Delete() error {
	if config.Config.UseMySQL {
		tx, err := data_store.Dbh.Begin()
		if err != nil {
			return err
		}
		_, err := tx.Exec("DELETE FROM sandboxes WHERE sbox_id = ?", s.Id)
		if err != nil {
			terr := tx.Rollback()
			if terr != nil {
				err = fmt.Errorf("deleting sandbox %s had an error '%s', and then rolling back the transaction gave another error '%s'", s.Id, err.Error(), terr.Error())
			}
			return err
		}
		tx.Commit()
	} else {
		ds := data_store.New()
		ds.Delete("sandbox", s.Id)
	}
	return nil
}

func GetList() []string {
	var sandbox_list []string
	if config.Config.UseMySQL {
		rows, err := data_store.Dbh.Query("SELECT sbox_id FROM sandboxes")
		if err != nil {
			if err != sql.ErrNoRows {
				log.Fatal(err)
			}
			rows.Close()
			return sandbox_list
		}
		sandbox_list = make([]string, 0)
		for rows.Next() {
			var sbox_id string
			err = rows.Scan(&sbox_id)
			if err != nil {
				log.Fatal(err)
			}
			sandbox_list = append(sandbox_list, sbox_id)
		}
		rows.Close()
		if err = rows.Err(); err != nil {
			log.Fatal(err)
		}
	} else {
		ds := data_store.New()
		sandbox_list = ds.GetList("sandbox")
	}
	return sandbox_list
}

// Creates the list of file checksums and whether or not they need to be
// uploaded or not. If they do, the upload URL is also provided.
func (s *Sandbox) UploadChkList() map[string]map[string]interface{} {
	/* Uh... */
	chksum_stats := make(map[string]map[string]interface{})
	for _, chk := range s.Checksums {
		chksum_stats[chk] = make(map[string]interface{})
		k, _ := filestore.Get(chk)
		if k != nil {
			chksum_stats[chk]["needs_upload"] = false
		} else {
			item_url := fmt.Sprintf("/file_store/%s", chk)
			chksum_stats[chk]["url"] = util.CustomURL(item_url)
			chksum_stats[chk]["needs_upload"] = true
		}

	}
	return chksum_stats
}

// Is the sandbox complete?
func (s *Sandbox) IsComplete() error {
	for _, chk := range s.Checksums {
		k, _ := filestore.Get(chk)
		if k == nil {
			err := fmt.Errorf("Checksum %s not uploaded yet, %s not complete, cannot commit yet.", chk, s.Id)
			return err
		}
	}
	return nil
}

func (s *Sandbox) GetName() string {
	return s.Id
}

func (s *Sandbox) URLType() string {
	return "sandboxes"
}
<commit_msg>bluh.<commit_after>/* Sandbox structs, for testing whether cookbook files need to be uploaded */

/*
 * Copyright (c) 2013-2014, Jeremy Bingham (<jbingham@gmail.com>)
 *
 * 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 sandbox allows checking files before re-uploading the, so any given
// version of a file need only be uploaded once rather than being uploaded
// repeatedly.
package sandbox

import (
	"github.com/ctdk/goiardi/config"
	"github.com/ctdk/goiardi/data_store"
	"github.com/ctdk/goiardi/filestore"
	"github.com/ctdk/goiardi/util"
	"fmt"
	"crypto/md5"
	"crypto/rand"
	"io"
	"log"
	"time"
	"database/sql"
)

/* The structure of the sandbox responses is... inconsistent. */

type Sandbox struct {
	Id string
	CreationTime time.Time
	Completed bool
	Checksums []string
}

/* We actually generate the sandbox_id ourselves, so we don't pass that in. */

// Create a new sandbox, given a map of null values with file checksums as keys.
func New(checksum_hash map[string]interface{}) (*Sandbox, error){
	/* For some reason the checksums come in a JSON hash that looks like
	 * this:
 	 * { "checksums": {
	 * "385ea5490c86570c7de71070bce9384a":null,
  	 * "f6f73175e979bd90af6184ec277f760c":null,
  	 * "2e03dd7e5b2e6c8eab1cf41ac61396d5":null
  	 * } } --- per the chef server api docs. Not sure why it comes in that
	 * way rather than as an array, since those nulls are apparently never
	 * anything but nulls. */

	/* First generate an id for this sandbox. Collisions are certainly
	 * possible, so we'll give it five tries to make a unique one before
	 * bailing. This may later turn out not to be the ideal sandbox creation
	 * method, but we'll see. */
	var sandbox_id string
	var err error
	for i := 0; i < 5; i++ {
		sandbox_id, err = generate_sandbox_id()
		if err != nil {
			/* Something went very wrong. */
			return nil, err
		}
		if s, _ := Get(sandbox_id); s != nil {
			err = fmt.Errorf("Collision! Somehow %s already existed as a sandbox id on attempt %d. Trying again.", sandbox_id, i)
			sandbox_id = ""
			log.Println(err)
		}
	}

	if sandbox_id == "" {
		err = fmt.Errorf("Somehow every attempt to create a unique sandbox id failed. Bailing.")
		return nil, err
	}
	checksums := make([]string, len(checksum_hash))
	j := 0
	for k, _ := range checksum_hash {
		checksums[j] = k
		j++
	}

	sbox := &Sandbox{
		Id: sandbox_id,
		CreationTime: time.Now(),
		Completed: false,
		Checksums: checksums,
	}
	return sbox, nil
}

func generate_sandbox_id() (string, error) {
	randnum := 20
	b := make([]byte, randnum)
	n, err := io.ReadFull(rand.Reader, b)
	if n != len(b) || err != nil {
		return "", err
	}
	id_md5 := md5.New()
	id_md5.Write(b)
	sandbox_id := fmt.Sprintf("%x", id_md5.Sum(nil))
	return sandbox_id, nil
}

func (s *Sandbox)fillSandboxFromSQL(row *sql.Row) error {
	if config.Config.UseMySQL {
		var csb []byte
		err := row.Scan(&s.Id, &s.CreationTime, &csb, &s.Completed)
		if err != nil {
			return err
		}
		var q interface{}
		q, err = data_store.DecodeBlob(csb, s.Checksums)
		if err != nil {
			return err
		}
		s.Checksums = q.([]string)
	} else {
		err := fmt.Errorf("no database configured, operating in in-memory mode -- fillSandboxFromSQL cannot be run")
		return err
	}
	return nil
}

func Get(sandbox_id string) (*Sandbox, error){
	var sandbox *Sandbox
	var found bool

	if config.Config.UseMySQL {
		sandbox = new(Sandbox)
		stmt, err := data_store.Dbh.Prepare("SELECT sbox_id, creation_time, checksums, completed FROM sandboxes WHERE sbox_id = ?")
		if err != nil {
			return nil, err
		}
		defer stmt.Close()
		row := stmt.QueryRow(sandbox_id)
		err = sandbox.fillSandboxFromSQL(row)
		if err != nil {
			if err == sql.ErrNoRows {
				found = false
			} else {
				return nil, err
			}
		} else {
			found = true
		}
	} else {
		ds := data_store.New()
		var s interface{}
		s, found = ds.Get("sandbox", sandbox_id)
		sandbox = s.(*Sandbox)
	}

	if !found {
		err := fmt.Errorf("Sandbox %s not found", sandbox_id)
		return nil, err
	}
	return sandbox, nil
}

func (s *Sandbox) Save() error {
	if config.Config.UseMySQL {
		ckb, ckerr := data_store.EncodeBlob(s.Checksums)
		if ckerr != nil {
			return ckerr
		}
		tx, err := data_store.Dbh.Begin()
		if err != nil {
			return err
		}
		var sbox_id string
		err = tx.QueryRow(s.Id).Scan(&sbox_id)
		if err == nil {
			_, err = tx.Exec("UPDATE sandboxes SET checksums = ?, completed = ? WHERE sbox_id = ?", ckb, s.Completed, s.Id)
				if err != nil {
					tx.Rollback()
					return err
				}
		} else {
			if err != sql.ErrNoRows {
				tx.Rollback()
				return err
			}
			_, err = tx.Exec("INSERT INTO sandboxes (sbox_id, creation_time, checksums, completed) VALUES (?, ?, ?, ?)", s.Id, s.CreationTime, ckb, s.Completed)
			if err != nil {
				tx.Rollback()
				return err
			}
		}
		tx.Commit()
	} else {
		ds := data_store.New()
		ds.Set("sandbox", s.Id, s)
	}
	return nil
}

func (s *Sandbox) Delete() error {
	if config.Config.UseMySQL {
		tx, err := data_store.Dbh.Begin()
		if err != nil {
			return err
		}
		_, err = tx.Exec("DELETE FROM sandboxes WHERE sbox_id = ?", s.Id)
		if err != nil {
			terr := tx.Rollback()
			if terr != nil {
				err = fmt.Errorf("deleting sandbox %s had an error '%s', and then rolling back the transaction gave another error '%s'", s.Id, err.Error(), terr.Error())
			}
			return err
		}
		tx.Commit()
	} else {
		ds := data_store.New()
		ds.Delete("sandbox", s.Id)
	}
	return nil
}

func GetList() []string {
	var sandbox_list []string
	if config.Config.UseMySQL {
		rows, err := data_store.Dbh.Query("SELECT sbox_id FROM sandboxes")
		if err != nil {
			if err != sql.ErrNoRows {
				log.Fatal(err)
			}
			rows.Close()
			return sandbox_list
		}
		sandbox_list = make([]string, 0)
		for rows.Next() {
			var sbox_id string
			err = rows.Scan(&sbox_id)
			if err != nil {
				log.Fatal(err)
			}
			sandbox_list = append(sandbox_list, sbox_id)
		}
		rows.Close()
		if err = rows.Err(); err != nil {
			log.Fatal(err)
		}
	} else {
		ds := data_store.New()
		sandbox_list = ds.GetList("sandbox")
	}
	return sandbox_list
}

// Creates the list of file checksums and whether or not they need to be
// uploaded or not. If they do, the upload URL is also provided.
func (s *Sandbox) UploadChkList() map[string]map[string]interface{} {
	/* Uh... */
	chksum_stats := make(map[string]map[string]interface{})
	for _, chk := range s.Checksums {
		chksum_stats[chk] = make(map[string]interface{})
		k, _ := filestore.Get(chk)
		if k != nil {
			chksum_stats[chk]["needs_upload"] = false
		} else {
			item_url := fmt.Sprintf("/file_store/%s", chk)
			chksum_stats[chk]["url"] = util.CustomURL(item_url)
			chksum_stats[chk]["needs_upload"] = true
		}

	}
	return chksum_stats
}

// Is the sandbox complete?
func (s *Sandbox) IsComplete() error {
	for _, chk := range s.Checksums {
		k, _ := filestore.Get(chk)
		if k == nil {
			err := fmt.Errorf("Checksum %s not uploaded yet, %s not complete, cannot commit yet.", chk, s.Id)
			return err
		}
	}
	return nil
}

func (s *Sandbox) GetName() string {
	return s.Id
}

func (s *Sandbox) URLType() string {
	return "sandboxes"
}
<|endoftext|>


<commit_before>package channel

import (
	"net/http"
	"net/url"
	"socialapi/models"
	"socialapi/workers/api/modules/helpers"
	"strconv"

	"github.com/jinzhu/gorm"
)

func Create(u *url.URL, h http.Header, req *models.Channel) (int, http.Header, interface{}, error) {
	if err := req.Save(); err != nil {
		return helpers.NewBadRequestResponse()
	}

	return helpers.NewOKResponse(req)
}

func Delete(u *url.URL, h http.Header, req *models.Channel) (int, http.Header, interface{}, error) {
	id, err := strconv.ParseInt(u.Query().Get("id"), 10, 64)
	if err != nil {
		return helpers.NewBadRequestResponse()
	}

	req.Id = id

	if err := req.Delete(); err != nil {
		return helpers.NewBadRequestResponse()
	}
	// yes it is deleted but not removed completely from our system
	return helpers.NewDeletedResponse()
}

func Update(u *url.URL, h http.Header, req *models.Channel) (int, http.Header, interface{}, error) {
	id, err := strconv.ParseInt(u.Query().Get("id"), 10, 64)
	if err != nil {
		return helpers.NewBadRequestResponse()
	}
	req.Id = id

	if req.Id == 0 {
		return helpers.NewBadRequestResponse()
	}

	if err := req.Save(); err != nil {
		return helpers.NewBadRequestResponse()
	}

	return helpers.NewOKResponse(req)
}

func Get(u *url.URL, h http.Header, req *models.Channel) (int, http.Header, interface{}, error) {
	id, err := strconv.ParseInt(u.Query().Get("id"), 10, 64)
	if err != nil {
		return helpers.NewBadRequestResponse()
	}

	req.Id = id
	if err := req.Fetch(); err != nil {
		if err == gorm.RecordNotFound {
			return helpers.NewNotFoundResponse()
		}
		return helpers.NewBadRequestResponse()
	}

	return helpers.NewOKResponse(req)
}
<commit_msg>Social: add post message handler<commit_after>package channel

import (
	"net/http"
	"net/url"
	"socialapi/models"
	"socialapi/workers/api/modules/helpers"
	"strconv"

	"github.com/jinzhu/gorm"
)

func Create(u *url.URL, h http.Header, req *models.Channel) (int, http.Header, interface{}, error) {
	if err := req.Save(); err != nil {
		return helpers.NewBadRequestResponse()
	}

	return helpers.NewOKResponse(req)
}

func Delete(u *url.URL, h http.Header, req *models.Channel) (int, http.Header, interface{}, error) {
	id, err := strconv.ParseInt(u.Query().Get("id"), 10, 64)
	if err != nil {
		return helpers.NewBadRequestResponse()
	}

	req.Id = id

	if err := req.Delete(); err != nil {
		return helpers.NewBadRequestResponse()
	}
	// yes it is deleted but not removed completely from our system
	return helpers.NewDeletedResponse()
}

func Update(u *url.URL, h http.Header, req *models.Channel) (int, http.Header, interface{}, error) {
	id, err := strconv.ParseInt(u.Query().Get("id"), 10, 64)
	if err != nil {
		return helpers.NewBadRequestResponse()
	}
	req.Id = id

	if req.Id == 0 {
		return helpers.NewBadRequestResponse()
	}

	if err := req.Save(); err != nil {
		return helpers.NewBadRequestResponse()
	}

	return helpers.NewOKResponse(req)
}

func Get(u *url.URL, h http.Header, req *models.Channel) (int, http.Header, interface{}, error) {
	id, err := strconv.ParseInt(u.Query().Get("id"), 10, 64)
	if err != nil {
		return helpers.NewBadRequestResponse()
	}

	req.Id = id
	if err := req.Fetch(); err != nil {
		if err == gorm.RecordNotFound {
			return helpers.NewNotFoundResponse()
		}
		return helpers.NewBadRequestResponse()
	}

	return helpers.NewOKResponse(req)
}

func PostMessage(u *url.URL, h http.Header, req *models.Channel) (int, http.Header, interface{}, error) {
	// id, err := strconv.ParseInt(u.Query().Get("id"), 10, 64)
	// if err != nil {
	// 	return helpers.NewBadRequestResponse()
	// }

	// req.Id = id
	// // TODO - check if the user is member of the channnel

	// if err := req.Fetch(); err != nil {
	// 	if err == gorm.RecordNotFound {
	// 		return helpers.NewNotFoundResponse()
	// 	}
	// 	return helpers.NewBadRequestResponse()
	// }

	return helpers.NewOKResponse(req)
}
<|endoftext|>