<commit_before>package gherkin

import (
	"regexp"
	"strings"
	"unicode/utf8"
)

const (
	DEFAULT_DIALECT                 = "en"
	COMMENT_PREFIX                  = "#"
	TAG_PREFIX                      = "@"
	TITLE_KEYWORD_SEPARATOR         = ":"
	TABLE_CELL_SEPARATOR            = '|'
	ESCAPED_NEWLINE                 = 'n'
	DOCSTRING_SEPARATOR             = "\"\"\""
	DOCSTRING_ALTERNATIVE_SEPARATOR = "```"
)

type matcher struct {
	gdp                      GherkinDialectProvider
        default_lang             string
	lang                     string
	dialect                  *GherkinDialect
	activeDocStringSeparator string
	indentToRemove           int
	languagePattern          *regexp.Regexp
}

func NewMatcher(gdp GherkinDialectProvider) Matcher {
	return &matcher{
		gdp:             gdp,
		default_lang:    DEFAULT_DIALECT,
		lang:            DEFAULT_DIALECT,
		dialect:         gdp.GetDialect(DEFAULT_DIALECT),
		languagePattern: regexp.MustCompile("^\\s*#\\s*language\\s*:\\s*([a-zA-Z\\-_]+)\\s*$"),
	}
}

func NewLanguageMatcher(gdp GherkinDialectProvider, language string) Matcher {
	return &matcher{
		gdp:             gdp,
		default_lang:    language,
		lang:            language,
		dialect:         gdp.GetDialect(language),
		languagePattern: regexp.MustCompile("^\\s*#\\s*language\\s*:\\s*([a-zA-Z\\-_]+)\\s*$"),
	}
}

func (m *matcher) Reset() {
	m.indentToRemove = 0
	m.activeDocStringSeparator = ""
	if m.lang != "en" {
		m.dialect = m.gdp.GetDialect(m.default_lang)
		m.lang = "en"
	}
}

func (m *matcher) newTokenAtLocation(line, index int) (token *Token) {
	column := index + 1
	token = new(Token)
	token.GherkinDialect = m.lang
	token.Location = &Location{line, column}
	return
}

func (m *matcher) MatchEOF(line *Line) (ok bool, token *Token, err error) {
	if line.IsEof() {
		token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true
		token.Type = TokenType_EOF
	}
	return
}

func (m *matcher) MatchEmpty(line *Line) (ok bool, token *Token, err error) {
	if line.IsEmpty() {
		token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true
		token.Type = TokenType_Empty
	}
	return
}

func (m *matcher) MatchComment(line *Line) (ok bool, token *Token, err error) {
	if line.StartsWith(COMMENT_PREFIX) {
		token, ok = m.newTokenAtLocation(line.LineNumber, 0), true
		token.Type = TokenType_Comment
		token.Text = line.LineText
	}
	return
}

func (m *matcher) MatchTagLine(line *Line) (ok bool, token *Token, err error) {
	if line.StartsWith(TAG_PREFIX) {
		var tags []*LineSpan
		var column = line.Indent()
		splits := strings.Split(line.TrimmedLineText, TAG_PREFIX)
		for i := range splits {
			txt := strings.Trim(splits[i], " ")
			if txt != "" {
				tags = append(tags, &LineSpan{column, TAG_PREFIX + txt})
			}
			column = column + len(splits[i]) + 1
		}

		token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true
		token.Type = TokenType_TagLine
		token.Items = tags
	}
	return
}

func (m *matcher) matchTitleLine(line *Line, tokenType TokenType, keywords []string) (ok bool, token *Token, err error) {
	for i := range keywords {
		keyword := keywords[i]
		if line.StartsWith(keyword + TITLE_KEYWORD_SEPARATOR) {
			token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true
			token.Type = tokenType
			token.Keyword = keyword
			token.Text = strings.Trim(line.TrimmedLineText[len(keyword)+1:], " ")
			return
		}
	}
	return
}

func (m *matcher) MatchFeatureLine(line *Line) (ok bool, token *Token, err error) {
	return m.matchTitleLine(line, TokenType_FeatureLine, m.dialect.FeatureKeywords())
}
func (m *matcher) MatchBackgroundLine(line *Line) (ok bool, token *Token, err error) {
	return m.matchTitleLine(line, TokenType_BackgroundLine, m.dialect.BackgroundKeywords())
}
func (m *matcher) MatchScenarioLine(line *Line) (ok bool, token *Token, err error) {
	return m.matchTitleLine(line, TokenType_ScenarioLine, m.dialect.ScenarioKeywords())
}
func (m *matcher) MatchScenarioOutlineLine(line *Line) (ok bool, token *Token, err error) {
	return m.matchTitleLine(line, TokenType_ScenarioOutlineLine, m.dialect.ScenarioOutlineKeywords())
}
func (m *matcher) MatchExamplesLine(line *Line) (ok bool, token *Token, err error) {
	return m.matchTitleLine(line, TokenType_ExamplesLine, m.dialect.ExamplesKeywords())
}
func (m *matcher) MatchStepLine(line *Line) (ok bool, token *Token, err error) {
	keywords := m.dialect.StepKeywords()
	for i := range keywords {
		keyword := keywords[i]
		if line.StartsWith(keyword) {
			token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true
			token.Type = TokenType_StepLine
			token.Keyword = keyword
			token.Text = strings.Trim(line.TrimmedLineText[len(keyword):], " ")
			return
		}
	}
	return
}

func (m *matcher) MatchDocStringSeparator(line *Line) (ok bool, token *Token, err error) {
	if m.activeDocStringSeparator != "" {
		if line.StartsWith(m.activeDocStringSeparator) {
			// close
			token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true
			token.Type = TokenType_DocStringSeparator

			m.indentToRemove = 0
			m.activeDocStringSeparator = ""
		}
		return
	}
	if line.StartsWith(DOCSTRING_SEPARATOR) {
		m.activeDocStringSeparator = DOCSTRING_SEPARATOR
	} else if line.StartsWith(DOCSTRING_ALTERNATIVE_SEPARATOR) {
		m.activeDocStringSeparator = DOCSTRING_ALTERNATIVE_SEPARATOR
	}
	if m.activeDocStringSeparator != "" {
		// open
		contentType := line.TrimmedLineText[len(m.activeDocStringSeparator):]
		m.indentToRemove = line.Indent()
		token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true
		token.Type = TokenType_DocStringSeparator
		token.Text = contentType
	}
	return
}

func (m *matcher) MatchTableRow(line *Line) (ok bool, token *Token, err error) {
	var firstChar, firstPos = utf8.DecodeRuneInString(line.TrimmedLineText)
	if firstChar == TABLE_CELL_SEPARATOR {
		var cells []*LineSpan
		var cell []rune
		var startCol = line.Indent() + 2 // column where the current cell started
		// start after the first separator, it's not included in the cell
		for i, w, col := firstPos, 0, startCol; i < len(line.TrimmedLineText); i += w {
			var char rune
			char, w = utf8.DecodeRuneInString(line.TrimmedLineText[i:])
			if char == TABLE_CELL_SEPARATOR {
				// append current cell
				txt := string(cell)
				txtTrimmed := strings.TrimLeft(txt, " ")
				ind := len(txt) - len(txtTrimmed)
				cells = append(cells, &LineSpan{startCol + ind, strings.TrimRight(txtTrimmed, " ")})
				// start building next
				cell = make([]rune, 0)
				startCol = col + 1
			} else if char == '\\' {
				// skip this character but count the column
				i += w
				col++
				char, w = utf8.DecodeRuneInString(line.TrimmedLineText[i:])
				if char == ESCAPED_NEWLINE {
					cell = append(cell, '\n')
				} else {
					cell = append(cell, char)
				}
			} else {
				cell = append(cell, char)
			}
			col++
		}

		token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true
		token.Type = TokenType_TableRow
		token.Items = cells
	}
	return
}

func (m *matcher) MatchLanguage(line *Line) (ok bool, token *Token, err error) {
	matches := m.languagePattern.FindStringSubmatch(line.TrimmedLineText)
	if len(matches) > 0 {
		lang := matches[1]
		token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true
		token.Type = TokenType_Language
		token.Text = lang

		dialect := m.gdp.GetDialect(lang)
		if dialect == nil {
			err = &parseError{"Language not supported: " + lang, token.Location}
		} else {
			m.lang = lang
			m.dialect = dialect
		}
	}
	return
}

func (m *matcher) MatchOther(line *Line) (ok bool, token *Token, err error) {
	token, ok = m.newTokenAtLocation(line.LineNumber, 0), true
	token.Type = TokenType_Other

	element := line.LineText
	txt := strings.TrimLeft(element, " ")

	if len(element)-len(txt) > m.indentToRemove {
		token.Text = unescapeDocString(element[m.indentToRemove:])
	} else {
		token.Text = unescapeDocString(txt)
	}
	return
}

func unescapeDocString(text string) string {
	return strings.Replace(text, "\\\"\\\"\\\"", "\"\"\"", -1)
}
<commit_msg>Do not change escaped docstring separators in descriptions.<commit_after>package gherkin

import (
	"regexp"
	"strings"
	"unicode/utf8"
)

const (
	DEFAULT_DIALECT                 = "en"
	COMMENT_PREFIX                  = "#"
	TAG_PREFIX                      = "@"
	TITLE_KEYWORD_SEPARATOR         = ":"
	TABLE_CELL_SEPARATOR            = '|'
	ESCAPED_NEWLINE                 = 'n'
	DOCSTRING_SEPARATOR             = "\"\"\""
	DOCSTRING_ALTERNATIVE_SEPARATOR = "```"
)

type matcher struct {
	gdp                      GherkinDialectProvider
        default_lang             string
	lang                     string
	dialect                  *GherkinDialect
	activeDocStringSeparator string
	indentToRemove           int
	languagePattern          *regexp.Regexp
}

func NewMatcher(gdp GherkinDialectProvider) Matcher {
	return &matcher{
		gdp:             gdp,
		default_lang:    DEFAULT_DIALECT,
		lang:            DEFAULT_DIALECT,
		dialect:         gdp.GetDialect(DEFAULT_DIALECT),
		languagePattern: regexp.MustCompile("^\\s*#\\s*language\\s*:\\s*([a-zA-Z\\-_]+)\\s*$"),
	}
}

func NewLanguageMatcher(gdp GherkinDialectProvider, language string) Matcher {
	return &matcher{
		gdp:             gdp,
		default_lang:    language,
		lang:            language,
		dialect:         gdp.GetDialect(language),
		languagePattern: regexp.MustCompile("^\\s*#\\s*language\\s*:\\s*([a-zA-Z\\-_]+)\\s*$"),
	}
}

func (m *matcher) Reset() {
	m.indentToRemove = 0
	m.activeDocStringSeparator = ""
	if m.lang != "en" {
		m.dialect = m.gdp.GetDialect(m.default_lang)
		m.lang = "en"
	}
}

func (m *matcher) newTokenAtLocation(line, index int) (token *Token) {
	column := index + 1
	token = new(Token)
	token.GherkinDialect = m.lang
	token.Location = &Location{line, column}
	return
}

func (m *matcher) MatchEOF(line *Line) (ok bool, token *Token, err error) {
	if line.IsEof() {
		token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true
		token.Type = TokenType_EOF
	}
	return
}

func (m *matcher) MatchEmpty(line *Line) (ok bool, token *Token, err error) {
	if line.IsEmpty() {
		token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true
		token.Type = TokenType_Empty
	}
	return
}

func (m *matcher) MatchComment(line *Line) (ok bool, token *Token, err error) {
	if line.StartsWith(COMMENT_PREFIX) {
		token, ok = m.newTokenAtLocation(line.LineNumber, 0), true
		token.Type = TokenType_Comment
		token.Text = line.LineText
	}
	return
}

func (m *matcher) MatchTagLine(line *Line) (ok bool, token *Token, err error) {
	if line.StartsWith(TAG_PREFIX) {
		var tags []*LineSpan
		var column = line.Indent()
		splits := strings.Split(line.TrimmedLineText, TAG_PREFIX)
		for i := range splits {
			txt := strings.Trim(splits[i], " ")
			if txt != "" {
				tags = append(tags, &LineSpan{column, TAG_PREFIX + txt})
			}
			column = column + len(splits[i]) + 1
		}

		token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true
		token.Type = TokenType_TagLine
		token.Items = tags
	}
	return
}

func (m *matcher) matchTitleLine(line *Line, tokenType TokenType, keywords []string) (ok bool, token *Token, err error) {
	for i := range keywords {
		keyword := keywords[i]
		if line.StartsWith(keyword + TITLE_KEYWORD_SEPARATOR) {
			token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true
			token.Type = tokenType
			token.Keyword = keyword
			token.Text = strings.Trim(line.TrimmedLineText[len(keyword)+1:], " ")
			return
		}
	}
	return
}

func (m *matcher) MatchFeatureLine(line *Line) (ok bool, token *Token, err error) {
	return m.matchTitleLine(line, TokenType_FeatureLine, m.dialect.FeatureKeywords())
}
func (m *matcher) MatchBackgroundLine(line *Line) (ok bool, token *Token, err error) {
	return m.matchTitleLine(line, TokenType_BackgroundLine, m.dialect.BackgroundKeywords())
}
func (m *matcher) MatchScenarioLine(line *Line) (ok bool, token *Token, err error) {
	return m.matchTitleLine(line, TokenType_ScenarioLine, m.dialect.ScenarioKeywords())
}
func (m *matcher) MatchScenarioOutlineLine(line *Line) (ok bool, token *Token, err error) {
	return m.matchTitleLine(line, TokenType_ScenarioOutlineLine, m.dialect.ScenarioOutlineKeywords())
}
func (m *matcher) MatchExamplesLine(line *Line) (ok bool, token *Token, err error) {
	return m.matchTitleLine(line, TokenType_ExamplesLine, m.dialect.ExamplesKeywords())
}
func (m *matcher) MatchStepLine(line *Line) (ok bool, token *Token, err error) {
	keywords := m.dialect.StepKeywords()
	for i := range keywords {
		keyword := keywords[i]
		if line.StartsWith(keyword) {
			token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true
			token.Type = TokenType_StepLine
			token.Keyword = keyword
			token.Text = strings.Trim(line.TrimmedLineText[len(keyword):], " ")
			return
		}
	}
	return
}

func (m *matcher) MatchDocStringSeparator(line *Line) (ok bool, token *Token, err error) {
	if m.activeDocStringSeparator != "" {
		if line.StartsWith(m.activeDocStringSeparator) {
			// close
			token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true
			token.Type = TokenType_DocStringSeparator

			m.indentToRemove = 0
			m.activeDocStringSeparator = ""
		}
		return
	}
	if line.StartsWith(DOCSTRING_SEPARATOR) {
		m.activeDocStringSeparator = DOCSTRING_SEPARATOR
	} else if line.StartsWith(DOCSTRING_ALTERNATIVE_SEPARATOR) {
		m.activeDocStringSeparator = DOCSTRING_ALTERNATIVE_SEPARATOR
	}
	if m.activeDocStringSeparator != "" {
		// open
		contentType := line.TrimmedLineText[len(m.activeDocStringSeparator):]
		m.indentToRemove = line.Indent()
		token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true
		token.Type = TokenType_DocStringSeparator
		token.Text = contentType
	}
	return
}

func (m *matcher) MatchTableRow(line *Line) (ok bool, token *Token, err error) {
	var firstChar, firstPos = utf8.DecodeRuneInString(line.TrimmedLineText)
	if firstChar == TABLE_CELL_SEPARATOR {
		var cells []*LineSpan
		var cell []rune
		var startCol = line.Indent() + 2 // column where the current cell started
		// start after the first separator, it's not included in the cell
		for i, w, col := firstPos, 0, startCol; i < len(line.TrimmedLineText); i += w {
			var char rune
			char, w = utf8.DecodeRuneInString(line.TrimmedLineText[i:])
			if char == TABLE_CELL_SEPARATOR {
				// append current cell
				txt := string(cell)
				txtTrimmed := strings.TrimLeft(txt, " ")
				ind := len(txt) - len(txtTrimmed)
				cells = append(cells, &LineSpan{startCol + ind, strings.TrimRight(txtTrimmed, " ")})
				// start building next
				cell = make([]rune, 0)
				startCol = col + 1
			} else if char == '\\' {
				// skip this character but count the column
				i += w
				col++
				char, w = utf8.DecodeRuneInString(line.TrimmedLineText[i:])
				if char == ESCAPED_NEWLINE {
					cell = append(cell, '\n')
				} else {
					cell = append(cell, char)
				}
			} else {
				cell = append(cell, char)
			}
			col++
		}

		token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true
		token.Type = TokenType_TableRow
		token.Items = cells
	}
	return
}

func (m *matcher) MatchLanguage(line *Line) (ok bool, token *Token, err error) {
	matches := m.languagePattern.FindStringSubmatch(line.TrimmedLineText)
	if len(matches) > 0 {
		lang := matches[1]
		token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true
		token.Type = TokenType_Language
		token.Text = lang

		dialect := m.gdp.GetDialect(lang)
		if dialect == nil {
			err = &parseError{"Language not supported: " + lang, token.Location}
		} else {
			m.lang = lang
			m.dialect = dialect
		}
	}
	return
}

func (m *matcher) MatchOther(line *Line) (ok bool, token *Token, err error) {
	token, ok = m.newTokenAtLocation(line.LineNumber, 0), true
	token.Type = TokenType_Other

	element := line.LineText
	txt := strings.TrimLeft(element, " ")

	if len(element)-len(txt) > m.indentToRemove {
		token.Text = m.unescapeDocString(element[m.indentToRemove:])
	} else {
		token.Text = m.unescapeDocString(txt)
	}
	return
}

func (m *matcher) unescapeDocString(text string) string {
	if m.activeDocStringSeparator != "" {
		return strings.Replace(text, "\\\"\\\"\\\"", "\"\"\"", -1)
	} else {
		return text
	}
}
<|endoftext|>


<commit_before>package googlecompute

import (
	"errors"
	"fmt"
	"time"

	"github.com/mitchellh/packer/common"
	"github.com/mitchellh/packer/packer"
)

// Config is the configuration structure for the GCE builder. It stores
// both the publicly settable state as well as the privately generated
// state of the config object.
type Config struct {
	common.PackerConfig `mapstructure:",squash"`

	BucketName        string            `mapstructure:"bucket_name"`
	ClientSecretsFile string            `mapstructure:"client_secrets_file"`
	ImageName         string            `mapstructure:"image_name"`
	ImageDescription  string            `mapstructure:"image_description"`
	MachineType       string            `mapstructure:"machine_type"`
	Metadata          map[string]string `mapstructure:"metadata"`
	Network           string            `mapstructure:"network"`
	Passphrase        string            `mapstructure:"passphrase"`
	PrivateKeyFile    string            `mapstructure:"private_key_file"`
	ProjectId         string            `mapstructure:"project_id"`
	SourceImage       string            `mapstructure:"source_image"`
	SSHUsername       string            `mapstructure:"ssh_username"`
	SSHPort           uint              `mapstructure:"ssh_port"`
	RawSSHTimeout     string            `mapstructure:"ssh_timeout"`
	RawStateTimeout   string            `mapstructure:"state_timeout"`
	Tags              []string          `mapstructure:"tags"`
	Zone              string            `mapstructure:"zone"`

	clientSecrets   *clientSecrets
	instanceName    string
	privateKeyBytes []byte
	sshTimeout      time.Duration
	stateTimeout    time.Duration
	tpl             *packer.ConfigTemplate
}

func NewConfig(raws ...interface{}) (*Config, []string, error) {
	c := new(Config)
	md, err := common.DecodeConfig(c, raws...)
	if err != nil {
		return nil, nil, err
	}

	c.tpl, err = packer.NewConfigTemplate()
	if err != nil {
		return nil, nil, err
	}
	c.tpl.UserVars = c.PackerUserVars

	// Prepare the errors
	errs := common.CheckUnusedConfig(md)

	// Set defaults.
	if c.Network == "" {
		c.Network = "default"
	}

	if c.ImageDescription == "" {
		c.ImageDescription = "Created by Packer"
	}

	if c.ImageName == "" {
		c.ImageName = "packer-{{timestamp}}"
	}

	if c.MachineType == "" {
		c.MachineType = "n1-standard-1"
	}

	if c.RawSSHTimeout == "" {
		c.RawSSHTimeout = "5m"
	}

	if c.RawStateTimeout == "" {
		c.RawStateTimeout = "5m"
	}

	if c.SSHUsername == "" {
		c.SSHUsername = "root"
	}

	if c.SSHPort == 0 {
		c.SSHPort = 22
	}

	// Process Templates
	templates := map[string]*string{
		"bucket_name":         &c.BucketName,
		"client_secrets_file": &c.ClientSecretsFile,
		"image_name":          &c.ImageName,
		"image_description":   &c.ImageDescription,
		"machine_type":        &c.MachineType,
		"network":             &c.Network,
		"passphrase":          &c.Passphrase,
		"private_key_file":    &c.PrivateKeyFile,
		"project_id":          &c.ProjectId,
		"source_image":        &c.SourceImage,
		"ssh_username":        &c.SSHUsername,
		"ssh_timeout":         &c.RawSSHTimeout,
		"state_timeout":       &c.RawStateTimeout,
		"zone":                &c.Zone,
	}

	for n, ptr := range templates {
		var err error
		*ptr, err = c.tpl.Process(*ptr, nil)
		if err != nil {
			errs = packer.MultiErrorAppend(
				errs, fmt.Errorf("Error processing %s: %s", n, err))
		}
	}

	// Process required parameters.
	if c.BucketName == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("a bucket_name must be specified"))
	}

	if c.ClientSecretsFile == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("a client_secrets_file must be specified"))
	}

	if c.PrivateKeyFile == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("a private_key_file must be specified"))
	}

	if c.ProjectId == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("a project_id must be specified"))
	}

	if c.SourceImage == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("a source_image must be specified"))
	}

	if c.Zone == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("a zone must be specified"))
	}

	// Process timeout settings.
	sshTimeout, err := time.ParseDuration(c.RawSSHTimeout)
	if err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Failed parsing ssh_timeout: %s", err))
	}
	c.sshTimeout = sshTimeout

	stateTimeout, err := time.ParseDuration(c.RawStateTimeout)
	if err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Failed parsing state_timeout: %s", err))
	}
	c.stateTimeout = stateTimeout

	// Load the client secrets file.
	cs, err := loadClientSecrets(c.ClientSecretsFile)
	if err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Failed parsing client secrets file: %s", err))
	}
	c.clientSecrets = cs

	// Load the private key.
	c.privateKeyBytes, err = processPrivateKeyFile(c.PrivateKeyFile, c.Passphrase)
	if err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Failed loading private key file: %s", err))
	}

	// Check for any errors.
	if errs != nil && len(errs.Errors) > 0 {
		return nil, nil, errs
	}

	return c, nil, nil
}
<commit_msg>builder/googlecompute: only load secrets/private key if given<commit_after>package googlecompute

import (
	"errors"
	"fmt"
	"time"

	"github.com/mitchellh/packer/common"
	"github.com/mitchellh/packer/packer"
)

// Config is the configuration structure for the GCE builder. It stores
// both the publicly settable state as well as the privately generated
// state of the config object.
type Config struct {
	common.PackerConfig `mapstructure:",squash"`

	BucketName        string            `mapstructure:"bucket_name"`
	ClientSecretsFile string            `mapstructure:"client_secrets_file"`
	ImageName         string            `mapstructure:"image_name"`
	ImageDescription  string            `mapstructure:"image_description"`
	MachineType       string            `mapstructure:"machine_type"`
	Metadata          map[string]string `mapstructure:"metadata"`
	Network           string            `mapstructure:"network"`
	Passphrase        string            `mapstructure:"passphrase"`
	PrivateKeyFile    string            `mapstructure:"private_key_file"`
	ProjectId         string            `mapstructure:"project_id"`
	SourceImage       string            `mapstructure:"source_image"`
	SSHUsername       string            `mapstructure:"ssh_username"`
	SSHPort           uint              `mapstructure:"ssh_port"`
	RawSSHTimeout     string            `mapstructure:"ssh_timeout"`
	RawStateTimeout   string            `mapstructure:"state_timeout"`
	Tags              []string          `mapstructure:"tags"`
	Zone              string            `mapstructure:"zone"`

	clientSecrets   *clientSecrets
	instanceName    string
	privateKeyBytes []byte
	sshTimeout      time.Duration
	stateTimeout    time.Duration
	tpl             *packer.ConfigTemplate
}

func NewConfig(raws ...interface{}) (*Config, []string, error) {
	c := new(Config)
	md, err := common.DecodeConfig(c, raws...)
	if err != nil {
		return nil, nil, err
	}

	c.tpl, err = packer.NewConfigTemplate()
	if err != nil {
		return nil, nil, err
	}
	c.tpl.UserVars = c.PackerUserVars

	// Prepare the errors
	errs := common.CheckUnusedConfig(md)

	// Set defaults.
	if c.Network == "" {
		c.Network = "default"
	}

	if c.ImageDescription == "" {
		c.ImageDescription = "Created by Packer"
	}

	if c.ImageName == "" {
		c.ImageName = "packer-{{timestamp}}"
	}

	if c.MachineType == "" {
		c.MachineType = "n1-standard-1"
	}

	if c.RawSSHTimeout == "" {
		c.RawSSHTimeout = "5m"
	}

	if c.RawStateTimeout == "" {
		c.RawStateTimeout = "5m"
	}

	if c.SSHUsername == "" {
		c.SSHUsername = "root"
	}

	if c.SSHPort == 0 {
		c.SSHPort = 22
	}

	// Process Templates
	templates := map[string]*string{
		"bucket_name":         &c.BucketName,
		"client_secrets_file": &c.ClientSecretsFile,
		"image_name":          &c.ImageName,
		"image_description":   &c.ImageDescription,
		"machine_type":        &c.MachineType,
		"network":             &c.Network,
		"passphrase":          &c.Passphrase,
		"private_key_file":    &c.PrivateKeyFile,
		"project_id":          &c.ProjectId,
		"source_image":        &c.SourceImage,
		"ssh_username":        &c.SSHUsername,
		"ssh_timeout":         &c.RawSSHTimeout,
		"state_timeout":       &c.RawStateTimeout,
		"zone":                &c.Zone,
	}

	for n, ptr := range templates {
		var err error
		*ptr, err = c.tpl.Process(*ptr, nil)
		if err != nil {
			errs = packer.MultiErrorAppend(
				errs, fmt.Errorf("Error processing %s: %s", n, err))
		}
	}

	// Process required parameters.
	if c.BucketName == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("a bucket_name must be specified"))
	}

	if c.ClientSecretsFile == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("a client_secrets_file must be specified"))
	}

	if c.PrivateKeyFile == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("a private_key_file must be specified"))
	}

	if c.ProjectId == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("a project_id must be specified"))
	}

	if c.SourceImage == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("a source_image must be specified"))
	}

	if c.Zone == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("a zone must be specified"))
	}

	// Process timeout settings.
	sshTimeout, err := time.ParseDuration(c.RawSSHTimeout)
	if err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Failed parsing ssh_timeout: %s", err))
	}
	c.sshTimeout = sshTimeout

	stateTimeout, err := time.ParseDuration(c.RawStateTimeout)
	if err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Failed parsing state_timeout: %s", err))
	}
	c.stateTimeout = stateTimeout

	if c.ClientSecretsFile != "" {
		// Load the client secrets file.
		cs, err := loadClientSecrets(c.ClientSecretsFile)
		if err != nil {
			errs = packer.MultiErrorAppend(
				errs, fmt.Errorf("Failed parsing client secrets file: %s", err))
		}
		c.clientSecrets = cs
	}

	if c.PrivateKeyFile != "" {
		// Load the private key.
		c.privateKeyBytes, err = processPrivateKeyFile(c.PrivateKeyFile, c.Passphrase)
		if err != nil {
			errs = packer.MultiErrorAppend(
				errs, fmt.Errorf("Failed loading private key file: %s", err))
		}
	}

	// Check for any errors.
	if errs != nil && len(errs.Errors) > 0 {
		return nil, nil, errs
	}

	return c, nil, nil
}
<|endoftext|>


<commit_before>// Package jsonhal provides structs and methods to easily wrap your own data
// in a HAL compatible struct with support for hyperlinks and embedded resources
// HAL specification: http://stateless.co/hal_specification.html
package jsonhal

import (
	"fmt"
)

// Link represents a link in "_links" object
type Link struct {
	Href  string `json:"href"`
	Title string `json:"title,omitempty"`
}

// Embedded represents a resource in "_embedded" object
type Embedded interface{}

// Hal is used for composition, include it as anonymous field in your structs
type Hal struct {
	Links    map[string]*Link    `json:"_links,omitempty"`
	Embedded map[string]Embedded `json:"_embedded,omitempty"`
}

// SetLink sets a link (self, next, etc). Title argument is optional
func (h *Hal) SetLink(name, href, title string) {
	if h.Links == nil {
		h.Links = make(map[string]*Link, 0)
	}
	h.Links[name] = &Link{Href: href, Title: title}
}

// DeleteLink removes a link named name if it is found
func (h *Hal) DeleteLink(name string) {
	if h.Links != nil {
		delete(h.Links, name)
	}
}

// GetLink returns a link by name or error
func (h *Hal) GetLink(name string) (*Link, error) {
	if h.Links == nil {
		return nil, fmt.Errorf("Link \"%s\" not found", name)
	}
	link, ok := h.Links[name]
	if !ok {
		return nil, fmt.Errorf("Link \"%s\" not found", name)
	}
	return link, nil
}

// SetEmbedded adds a slice of objects under a named key in the embedded map
func (h *Hal) SetEmbedded(name string, embedded Embedded) {
	if h.Embedded == nil {
		h.Embedded = make(map[string]Embedded, 0)
	}
	h.Embedded[name] = embedded
}

// GetEmbedded returns a slice of embedded resources by name or error
func (h *Hal) GetEmbedded(name string) (Embedded, error) {
	if h.Embedded == nil {
		return nil, fmt.Errorf("Embedded \"%s\" not found", name)
	}
	embedded, ok := h.Embedded[name]
	if !ok {
		return nil, fmt.Errorf("Embedded \"%s\" not found", name)
	}
	return embedded, nil
}

// DeleteEmbedded removes an embedded resource named name if it is found
func (h *Hal) DeleteEmbedded(name string) {
	if h.Embedded != nil {
		delete(h.Embedded, name)
	}
}
<commit_msg>Added Embedded interfaces to jsonhal<commit_after>// Package jsonhal provides structs and methods to easily wrap your own data
// in a HAL compatible struct with support for hyperlinks and embedded resources
// HAL specification: http://stateless.co/hal_specification.html
package jsonhal

import (
	"fmt"
)

// Link represents a link in "_links" object
type Link struct {
	Href  string `json:"href"`
	Title string `json:"title,omitempty"`
}

// Embedded represents a resource in "_embedded" object
type Embedded interface{}

// EmbedSetter is the interface that wraps the basic setEmbedded method.
//
// SetEmbedded adds a slice of objects under a named key in the embedded map
type EmbedSetter interface {
	SetEmbedded(name string, embedded Embedded)
}

// EmbedSetter is the interface that wraps the basic setEmbedded method.
//
// GetEmbedded returns a slice of embedded resources by name or error
type EmbedGetter interface {
	GetEmbedded(name string) (Embedded, error)
}

// Embeddeer is the interface that wraps the basic setEmbedded and getEmbedded methods.
type Embedder interface {
	EmbedSetter
	EmbedGetter
}

// Hal is used for composition, include it as anonymous field in your structs
type Hal struct {
	Links    map[string]*Link    `json:"_links,omitempty"`
	Embedded map[string]Embedded `json:"_embedded,omitempty"`
}

// SetLink sets a link (self, next, etc). Title argument is optional
func (h *Hal) SetLink(name, href, title string) {
	if h.Links == nil {
		h.Links = make(map[string]*Link, 0)
	}
	h.Links[name] = &Link{Href: href, Title: title}
}

// DeleteLink removes a link named name if it is found
func (h *Hal) DeleteLink(name string) {
	if h.Links != nil {
		delete(h.Links, name)
	}
}

// GetLink returns a link by name or error
func (h *Hal) GetLink(name string) (*Link, error) {
	if h.Links == nil {
		return nil, fmt.Errorf("Link \"%s\" not found", name)
	}
	link, ok := h.Links[name]
	if !ok {
		return nil, fmt.Errorf("Link \"%s\" not found", name)
	}
	return link, nil
}

// SetEmbedded adds a slice of objects under a named key in the embedded map
func (h *Hal) SetEmbedded(name string, embedded Embedded) {
	if h.Embedded == nil {
		h.Embedded = make(map[string]Embedded, 0)
	}
	h.Embedded[name] = embedded
}

// GetEmbedded returns a slice of embedded resources by name or error
func (h *Hal) GetEmbedded(name string) (Embedded, error) {
	if h.Embedded == nil {
		return nil, fmt.Errorf("Embedded \"%s\" not found", name)
	}
	embedded, ok := h.Embedded[name]
	if !ok {
		return nil, fmt.Errorf("Embedded \"%s\" not found", name)
	}
	return embedded, nil
}

// DeleteEmbedded removes an embedded resource named name if it is found
func (h *Hal) DeleteEmbedded(name string) {
	if h.Embedded != nil {
		delete(h.Embedded, name)
	}
}
<|endoftext|>


<commit_before>package statefile

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"

	version "github.com/hashicorp/go-version"

	"github.com/hashicorp/terraform/tfdiags"
	tfversion "github.com/hashicorp/terraform/version"
)

// ErrNoState is returned by ReadState when the state file is empty.
var ErrNoState = errors.New("no state")

// Read reads a state from the given reader.
//
// Legacy state format versions 1 through 3 are supported, but the result will
// contain object attributes in the deprecated "flatmap" format and so must
// be upgraded by the caller before use.
//
// If the state file is empty, the special error value ErrNoState is returned.
// Otherwise, the returned error might be a wrapper around tfdiags.Diagnostics
// potentially describing multiple errors.
func Read(r io.Reader) (*File, error) {
	// Some callers provide us a "typed nil" *os.File here, which would
	// cause us to panic below if we tried to use it.
	if f, ok := r.(*os.File); ok && f == nil {
		return nil, ErrNoState
	}

	var diags tfdiags.Diagnostics

	// We actually just buffer the whole thing in memory, because states are
	// generally not huge and we need to do be able to sniff for a version
	// number before full parsing.
	src, err := ioutil.ReadAll(r)
	if err != nil {
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			"Failed to read state file",
			fmt.Sprintf("The state file could not be read: %s", err),
		))
		return nil, diags.Err()
	}

	if len(src) == 0 {
		return nil, ErrNoState
	}

	state, diags := readState(src)
	if diags.HasErrors() {
		return nil, diags.Err()
	}

	if state == nil {
		// Should never happen
		panic("readState returned nil state with no errors")
	}

	return state, diags.Err()
}

func readState(src []byte) (*File, tfdiags.Diagnostics) {
	var diags tfdiags.Diagnostics

	if looksLikeVersion0(src) {
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			unsupportedFormat,
			"The state is stored in a legacy binary format that is not supported since Terraform v0.7. To continue, first upgrade the state using Terraform 0.6.16 or earlier.",
		))
		return nil, diags
	}

	version, versionDiags := sniffJSONStateVersion(src)
	diags = diags.Append(versionDiags)
	if versionDiags.HasErrors() {
		return nil, diags
	}

	switch version {
	case 0:
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			unsupportedFormat,
			"The state file uses JSON syntax but has a version number of zero. There was never a JSON-based state format zero, so this state file is invalid and cannot be processed.",
		))
		return nil, diags
	case 1:
		return readStateV1(src)
	case 2:
		return readStateV2(src)
	case 3:
		return readStateV3(src)
	case 4:
		return readStateV4(src)
	default:
		thisVersion := tfversion.SemVer.String()
		creatingVersion := sniffJSONStateTerraformVersion(src)
		switch {
		case creatingVersion != "":
			diags = diags.Append(tfdiags.Sourceless(
				tfdiags.Error,
				unsupportedFormat,
				fmt.Sprintf("The state file uses format version %d, which is not supported by Terraform %s. This state file was created by Terraform %s.", version, thisVersion, creatingVersion),
			))
		default:
			diags = diags.Append(tfdiags.Sourceless(
				tfdiags.Error,
				unsupportedFormat,
				fmt.Sprintf("The state file uses format version %d, which is not supported by Terraform %s. This state file may have been created by a newer version of Terraform.", version, thisVersion),
			))
		}
		return nil, diags
	}
}

func sniffJSONStateVersion(src []byte) (uint64, tfdiags.Diagnostics) {
	var diags tfdiags.Diagnostics

	type VersionSniff struct {
		Version *uint64 `json:"version"`
	}
	var sniff VersionSniff
	err := json.Unmarshal(src, &sniff)
	if err != nil {
		switch tErr := err.(type) {
		case *json.SyntaxError:
			diags = diags.Append(tfdiags.Sourceless(
				tfdiags.Error,
				unsupportedFormat,
				fmt.Sprintf("The state file could not be parsed as JSON: syntax error at byte offset %d.", tErr.Offset),
			))
		case *json.UnmarshalTypeError:
			diags = diags.Append(tfdiags.Sourceless(
				tfdiags.Error,
				unsupportedFormat,
				fmt.Sprintf("The version in the state file is %s. A positive whole number is required.", tErr.Value),
			))
		default:
			diags = diags.Append(tfdiags.Sourceless(
				tfdiags.Error,
				unsupportedFormat,
				"The state file could not be parsed as JSON.",
			))
		}
	}

	if sniff.Version == nil {
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			unsupportedFormat,
			"The state file does not have a \"version\" attribute, which is required to identify the format version.",
		))
		return 0, diags
	}

	return *sniff.Version, diags
}

// sniffJSONStateTerraformVersion attempts to sniff the Terraform version
// specification from the given state file source code. The result is either
// a version string or an empty string if no version number could be extracted.
//
// This is a best-effort function intended to produce nicer error messages. It
// should not be used for any real processing.
func sniffJSONStateTerraformVersion(src []byte) string {
	type VersionSniff struct {
		Version string `json:"terraform_version"`
	}
	var sniff VersionSniff

	err := json.Unmarshal(src, &sniff)
	if err != nil {
		return ""
	}

	// Attempt to parse the string as a version so we won't report garbage
	// as a version number.
	_, err = version.NewVersion(sniff.Version)
	if err != nil {
		return ""
	}

	return sniff.Version
}

// unsupportedFormat is a diagnostic summary message for when the state file
// seems to not be a state file at all, or is not a supported version.
//
// Use invalidFormat instead for the subtly-different case of "this looks like
// it's intended to be a state file but it's not structured correctly".
const unsupportedFormat = "Unsupported state file format"

const upgradeFailed = "State format upgrade failed"
<commit_msg>command: Fix TestInit_getProvider<commit_after>package statefile

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"

	version "github.com/hashicorp/go-version"

	"github.com/hashicorp/terraform/tfdiags"
	tfversion "github.com/hashicorp/terraform/version"
)

// ErrNoState is returned by ReadState when the state file is empty.
var ErrNoState = errors.New("no state")

// Read reads a state from the given reader.
//
// Legacy state format versions 1 through 3 are supported, but the result will
// contain object attributes in the deprecated "flatmap" format and so must
// be upgraded by the caller before use.
//
// If the state file is empty, the special error value ErrNoState is returned.
// Otherwise, the returned error might be a wrapper around tfdiags.Diagnostics
// potentially describing multiple errors.
func Read(r io.Reader) (*File, error) {
	// Some callers provide us a "typed nil" *os.File here, which would
	// cause us to panic below if we tried to use it.
	if f, ok := r.(*os.File); ok && f == nil {
		return nil, ErrNoState
	}

	var diags tfdiags.Diagnostics

	// We actually just buffer the whole thing in memory, because states are
	// generally not huge and we need to do be able to sniff for a version
	// number before full parsing.
	src, err := ioutil.ReadAll(r)
	if err != nil {
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			"Failed to read state file",
			fmt.Sprintf("The state file could not be read: %s", err),
		))
		return nil, diags.Err()
	}

	if len(src) == 0 {
		return nil, ErrNoState
	}

	state, diags := readState(src)
	if diags.HasErrors() {
		return nil, diags.Err()
	}

	if state == nil {
		// Should never happen
		panic("readState returned nil state with no errors")
	}

	if state.TerraformVersion != nil && state.TerraformVersion.GreaterThan(tfversion.SemVer) {
		return state, fmt.Errorf(
			"state snapshot was created by Terraform v%s, which is newer than current v%s; upgrade to Terraform v%s or greater to work with this state",
			state.TerraformVersion,
			tfversion.SemVer,
			state.TerraformVersion,
		)
	}

	return state, diags.Err()
}

func readState(src []byte) (*File, tfdiags.Diagnostics) {
	var diags tfdiags.Diagnostics

	if looksLikeVersion0(src) {
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			unsupportedFormat,
			"The state is stored in a legacy binary format that is not supported since Terraform v0.7. To continue, first upgrade the state using Terraform 0.6.16 or earlier.",
		))
		return nil, diags
	}

	version, versionDiags := sniffJSONStateVersion(src)
	diags = diags.Append(versionDiags)
	if versionDiags.HasErrors() {
		return nil, diags
	}

	switch version {
	case 0:
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			unsupportedFormat,
			"The state file uses JSON syntax but has a version number of zero. There was never a JSON-based state format zero, so this state file is invalid and cannot be processed.",
		))
		return nil, diags
	case 1:
		return readStateV1(src)
	case 2:
		return readStateV2(src)
	case 3:
		return readStateV3(src)
	case 4:
		return readStateV4(src)
	default:
		thisVersion := tfversion.SemVer.String()
		creatingVersion := sniffJSONStateTerraformVersion(src)
		switch {
		case creatingVersion != "":
			diags = diags.Append(tfdiags.Sourceless(
				tfdiags.Error,
				unsupportedFormat,
				fmt.Sprintf("The state file uses format version %d, which is not supported by Terraform %s. This state file was created by Terraform %s.", version, thisVersion, creatingVersion),
			))
		default:
			diags = diags.Append(tfdiags.Sourceless(
				tfdiags.Error,
				unsupportedFormat,
				fmt.Sprintf("The state file uses format version %d, which is not supported by Terraform %s. This state file may have been created by a newer version of Terraform.", version, thisVersion),
			))
		}
		return nil, diags
	}
}

func sniffJSONStateVersion(src []byte) (uint64, tfdiags.Diagnostics) {
	var diags tfdiags.Diagnostics

	type VersionSniff struct {
		Version *uint64 `json:"version"`
	}
	var sniff VersionSniff
	err := json.Unmarshal(src, &sniff)
	if err != nil {
		switch tErr := err.(type) {
		case *json.SyntaxError:
			diags = diags.Append(tfdiags.Sourceless(
				tfdiags.Error,
				unsupportedFormat,
				fmt.Sprintf("The state file could not be parsed as JSON: syntax error at byte offset %d.", tErr.Offset),
			))
		case *json.UnmarshalTypeError:
			diags = diags.Append(tfdiags.Sourceless(
				tfdiags.Error,
				unsupportedFormat,
				fmt.Sprintf("The version in the state file is %s. A positive whole number is required.", tErr.Value),
			))
		default:
			diags = diags.Append(tfdiags.Sourceless(
				tfdiags.Error,
				unsupportedFormat,
				"The state file could not be parsed as JSON.",
			))
		}
	}

	if sniff.Version == nil {
		diags = diags.Append(tfdiags.Sourceless(
			tfdiags.Error,
			unsupportedFormat,
			"The state file does not have a \"version\" attribute, which is required to identify the format version.",
		))
		return 0, diags
	}

	return *sniff.Version, diags
}

// sniffJSONStateTerraformVersion attempts to sniff the Terraform version
// specification from the given state file source code. The result is either
// a version string or an empty string if no version number could be extracted.
//
// This is a best-effort function intended to produce nicer error messages. It
// should not be used for any real processing.
func sniffJSONStateTerraformVersion(src []byte) string {
	type VersionSniff struct {
		Version string `json:"terraform_version"`
	}
	var sniff VersionSniff

	err := json.Unmarshal(src, &sniff)
	if err != nil {
		return ""
	}

	// Attempt to parse the string as a version so we won't report garbage
	// as a version number.
	_, err = version.NewVersion(sniff.Version)
	if err != nil {
		return ""
	}

	return sniff.Version
}

// unsupportedFormat is a diagnostic summary message for when the state file
// seems to not be a state file at all, or is not a supported version.
//
// Use invalidFormat instead for the subtly-different case of "this looks like
// it's intended to be a state file but it's not structured correctly".
const unsupportedFormat = "Unsupported state file format"

const upgradeFailed = "State format upgrade failed"
<|endoftext|>


<commit_before>package md5

import (
	"hash"
)

// The blocksize of MD5 in bytes.
const BlockSize = 64

// The size of an MD5 checksum in bytes.
const Size = 16

// New returns a new hash.Hash computing the MD5 checksum.
func New() hash.Hash {
	return &hash.Hash
}

// Sum returns the MD5 checksum of the data.
func Sum(data []byte) [Size]byte {
}
<commit_msg>now we have the md5 drop-in<commit_after>package md5

/*
#include "openssl/md5.h"
#cgo pkg-config: openssl
*/
import "C"
import (
	"hash"
	"unsafe"
)

// The blocksize of MD5 in bytes.
const BlockSize = 64

// The size of an MD5 checksum in bytes.
const Size = 16

type MD5Hash struct {
	md5 C.MD5_CTX
}

func (mh *MD5Hash) Write(msg []byte) (n int, err error) {
	size := C.size_t(len(msg))
	if C.MD5_Update(&mh.md5, unsafe.Pointer(C.CString(string(msg))), size) != 1 {
		panic("problem updating hash")
	}
	return len(msg), nil
}
func (mh *MD5Hash) BlockSize() int {
	return C.MD5_DIGEST_LENGTH
}
func (mh *MD5Hash) Size() int {
	return C.MD5_DIGEST_LENGTH
}
func (mh *MD5Hash) Reset() {
	C.MD5_Init(&mh.md5)
}
func (mh *MD5Hash) Sum(b []byte) []byte {
	digest := make([]C.uchar, mh.Size())
	// make a copy of the pointer, so our context does not get freed.
	// this allows further writes.
	// TODO perhaps we should think about runtime.SetFinalizer to free the context?
	s_tmp := C.MD5_CTX(mh.md5)
	if C.MD5_Final(&digest[0], &s_tmp) != 1 {
		// TODO maybe not panic here?
		panic("couldn't finalize digest")
	}
	var result []byte
	if b != nil {
		result = make([]byte, 0)
	} else {
		result = b
	}
	for _, value := range digest {
		result = append(result, byte(value))
	}
	return result
}

// Sum returns the MD5 checksum of the data.
func Sum(b []byte) [Size]byte {
	s := New()
	s.Reset()
	s.Write(b)
	var cs [Size]byte
	copy(cs[:], s.Sum(nil))
	return cs
}

// New returns a new hash.Hash computing the MD5 checksum.
func New() hash.Hash {
	h := new(MD5Hash)
	if C.MD5_Init(&h.md5) != 1 {
		panic("problem creating hash")
	}
	return h
}
<|endoftext|>


<commit_before>package turnpike

// Message is a generic container for a WAMP message.
type Message interface {
	MessageType() MessageType
}

type MessageType int

func (mt MessageType) New() Message {
	switch mt {
	case HELLO:
		return new(Hello)
	case WELCOME:
		return new(Welcome)
	case ABORT:
		return new(Abort)
	case CHALLENGE:
		return new(Challenge)
	case AUTHENTICATE:
		return new(Authenticate)
	case GOODBYE:
		return new(Goodbye)
	case HEARTBEAT:
		return new(Heartbeat)
	case ERROR:
		return new(Error)

	case PUBLISH:
		return new(Publish)
	case PUBLISHED:
		return new(Published)

	case SUBSCRIBE:
		return new(Subscribe)
	case SUBSCRIBED:
		return new(Subscribed)
	case UNSUBSCRIBE:
		return new(Unsubscribe)
	case UNSUBSCRIBED:
		return new(Unsubscribed)
	case EVENT:
		return new(Event)

	case CALL:
		return new(Call)
	case CANCEL:
		return new(Cancel)
	case RESULT:
		return new(Result)

	case REGISTER:
		return new(Register)
	case REGISTERED:
		return new(Registered)
	case UNREGISTER:
		return new(Unregister)
	case UNREGISTERED:
		return new(Unregistered)
	case INVOCATION:
		return new(Invocation)
	case INTERRUPT:
		return new(Interrupt)
	case YIELD:
		return new(Yield)
	default:
		// TODO: allow custom message types?
		return nil
	}
}

func (mt MessageType) String() string {
	switch mt {
	case HELLO:
		return "HELLO"
	case WELCOME:
		return "WELCOME"
	case ABORT:
		return "ABORT"
	case CHALLENGE:
		return "CHALLENGE"
	case AUTHENTICATE:
		return "AUTHENTICATE"
	case GOODBYE:
		return "GOODBYE"
	case HEARTBEAT:
		return "HEARTBEAT"
	case ERROR:
		return "ERROR"

	case PUBLISH:
		return "PUBLISH"
	case PUBLISHED:
		return "PUBLISHED"

	case SUBSCRIBE:
		return "SUBSCRIBE"
	case SUBSCRIBED:
		return "SUBSCRIBED"
	case UNSUBSCRIBE:
		return "UNSUBSCRIBE"
	case UNSUBSCRIBED:
		return "UNSUBSCRIBED"
	case EVENT:
		return "EVENT"

	case CALL:
		return "CALL"
	case CANCEL:
		return "CANCEL"
	case RESULT:
		return "RESULT"

	case REGISTER:
		return "REGISTER"
	case REGISTERED:
		return "REGISTERED"
	case UNREGISTER:
		return "UNREGISTER"
	case UNREGISTERED:
		return "UNREGISTERED"
	case INVOCATION:
		return "INVOCATION"
	case INTERRUPT:
		return "INTERRUPT"
	case YIELD:
		return "YIELD"
	default:
		// TODO: allow custom message types?
		panic("Invalid message type")
	}
}

const (
	HELLO        MessageType = 1
	WELCOME      MessageType = 2
	ABORT        MessageType = 3
	CHALLENGE    MessageType = 4
	AUTHENTICATE MessageType = 5
	GOODBYE      MessageType = 6
	HEARTBEAT    MessageType = 7
	ERROR        MessageType = 8

	PUBLISH   MessageType = 16 //	Tx 	Rx
	PUBLISHED MessageType = 17 //	Rx 	Tx

	SUBSCRIBE    MessageType = 32 //	Rx 	Tx
	SUBSCRIBED   MessageType = 33 //	Tx 	Rx
	UNSUBSCRIBE  MessageType = 34 //	Rx 	Tx
	UNSUBSCRIBED MessageType = 35 //	Tx 	Rx
	EVENT        MessageType = 36 //	Tx 	Rx

	CALL   MessageType = 48 //	Tx 	Rx
	CANCEL MessageType = 49 //	Tx 	Rx
	RESULT MessageType = 50 //	Rx 	Tx

	REGISTER     MessageType = 64 //	Rx 	Tx
	REGISTERED   MessageType = 65 //	Tx 	Rx
	UNREGISTER   MessageType = 66 //	Rx 	Tx
	UNREGISTERED MessageType = 67 //	Tx 	Rx
	INVOCATION   MessageType = 68 //	Tx 	Rx
	INTERRUPT    MessageType = 69 //	Tx 	Rx
	YIELD        MessageType = 70 //	Rx 	Tx
)

// URIs are dot-separated identifiers, where each component *should* only contain letters, numbers or underscores.
//
// See the documentation for specifics: https://github.com/tavendo/WAMP/blob/master/spec/basic.md#uris
type URI string

// An ID is a unique, non-negative number. Different uses may have additional restrictions.
type ID uint64

// [HELLO, Realm|uri, Details|dict]
type Hello struct {
	Realm   URI
	Details map[string]interface{}
}

func (msg *Hello) MessageType() MessageType {
	return HELLO
}

// [WELCOME, Session|id, Details|dict]
type Welcome struct {
	Id      ID
	Details map[string]interface{}
}

func (msg *Welcome) MessageType() MessageType {
	return WELCOME
}

// [ABORT, Details|dict, Reason|uri]
type Abort struct {
	Details map[string]interface{}
	Reason  URI
}

func (msg *Abort) MessageType() MessageType {
	return ABORT
}

// [CHALLENGE, AuthMethod|string, Extra|dict]
type Challenge struct {
	AuthMethod string
	Extra      map[string]interface{}
}

func (msg *Challenge) MessageType() MessageType {
	return CHALLENGE
}

// [AUTHENTICATE, Signature|string, Extra|dict]
type Authenticate struct {
	Signature string
	Extra     map[string]interface{}
}

func (msg *Authenticate) MessageType() MessageType {
	return AUTHENTICATE
}

// [GOODBYE, Details|dict, Reason|uri]
type Goodbye struct {
	Details map[string]interface{}
	Reason  URI
}

func (msg *Goodbye) MessageType() MessageType {
	return GOODBYE
}

// [HEARTBEAT, IncomingSeq|integer, OutgoingSeq|integer
// [HEARTBEAT, IncomingSeq|integer, OutgoingSeq|integer, Discard|string]
type Heartbeat struct {
	IncomingSeq uint
	OutgoingSeq uint
	Discard     string
}

func (msg *Heartbeat) MessageType() MessageType {
	return HEARTBEAT
}

// [ERROR, REQUEST.Type|int, REQUEST.Request|id, Details|dict, Error|uri]
// [ERROR, REQUEST.Type|int, REQUEST.Request|id, Details|dict, Error|uri, Arguments|list]
// [ERROR, REQUEST.Type|int, REQUEST.Request|id, Details|dict, Error|uri, Arguments|list, ArgumentsKw|dict]
type Error struct {
	Type        MessageType
	Request     ID
	Details     map[string]interface{}
	Error       URI
	Arguments   []interface{}          `wamp:"omitempty"`
	ArgumentsKw map[string]interface{} `wamp:"omitempty"`
}

func (msg *Error) MessageType() MessageType {
	return ERROR
}

// [PUBLISH, Request|id, Options|dict, Topic|uri]
// [PUBLISH, Request|id, Options|dict, Topic|uri, Arguments|list]
// [PUBLISH, Request|id, Options|dict, Topic|uri, Arguments|list, ArgumentsKw|dict]
type Publish struct {
	Request     ID
	Options     map[string]interface{}
	Topic       URI
	Arguments   []interface{}          `wamp:"omitempty"`
	ArgumentsKw map[string]interface{} `wamp:"omitempty"`
}

func (msg *Publish) MessageType() MessageType {
	return PUBLISH
}

// [PUBLISHED, PUBLISH.Request|id, Publication|id]
type Published struct {
	Request     ID
	Publication ID
}

func (msg *Published) MessageType() MessageType {
	return PUBLISHED
}

// [SUBSCRIBE, Request|id, Options|dict, Topic|uri]
type Subscribe struct {
	Request ID
	Options map[string]interface{}
	Topic   URI
}

func (msg *Subscribe) MessageType() MessageType {
	return SUBSCRIBE
}

// [SUBSCRIBED, SUBSCRIBE.Request|id, Subscription|id]
type Subscribed struct {
	Request      ID
	Subscription ID
}

func (msg *Subscribed) MessageType() MessageType {
	return SUBSCRIBED
}

// [UNSUBSCRIBE, Request|id, SUBSCRIBED.Subscription|id]
type Unsubscribe struct {
	Request      ID
	Subscription ID
}

func (msg *Unsubscribe) MessageType() MessageType {
	return UNSUBSCRIBE
}

// [UNSUBSCRIBED, UNSUBSCRIBE.Request|id]
type Unsubscribed struct {
	Request ID
}

func (msg *Unsubscribed) MessageType() MessageType {
	return UNSUBSCRIBED
}

// [EVENT, SUBSCRIBED.Subscription|id, PUBLISHED.Publication|id, Details|dict]
// [EVENT, SUBSCRIBED.Subscription|id, PUBLISHED.Publication|id, Details|dict, PUBLISH.Arguments|list]
// [EVENT, SUBSCRIBED.Subscription|id, PUBLISHED.Publication|id, Details|dict, PUBLISH.Arguments|list,
//     PUBLISH.ArgumentsKw|dict]
type Event struct {
	Subscription ID
	Publication  ID
	Details      map[string]interface{}
	Arguments    []interface{}          `wamp:"omitempty"`
	ArgumentsKw  map[string]interface{} `wamp:"omitempty"`
}

func (msg *Event) MessageType() MessageType {
	return EVENT
}

// CallResult represents the result of a CALL.
type CallResult struct {
	Args   []interface{}
	Kwargs map[string]interface{}
	Err    URI
}

// [CALL, Request|id, Options|dict, Procedure|uri]
// [CALL, Request|id, Options|dict, Procedure|uri, Arguments|list]
// [CALL, Request|id, Options|dict, Procedure|uri, Arguments|list, ArgumentsKw|dict]
type Call struct {
	Request     ID
	Options     map[string]interface{}
	Procedure   URI
	Arguments   []interface{}          `wamp:"omitempty"`
	ArgumentsKw map[string]interface{} `wamp:"omitempty"`
}

func (msg *Call) MessageType() MessageType {
	return CALL
}

// [RESULT, CALL.Request|id, Details|dict]
// [RESULT, CALL.Request|id, Details|dict, YIELD.Arguments|list]
// [RESULT, CALL.Request|id, Details|dict, YIELD.Arguments|list, YIELD.ArgumentsKw|dict]
type Result struct {
	Request     ID
	Details     map[string]interface{}
	Arguments   []interface{}          `wamp:"omitempty"`
	ArgumentsKw map[string]interface{} `wamp:"omitempty"`
}

func (msg *Result) MessageType() MessageType {
	return RESULT
}

// [REGISTER, Request|id, Options|dict, Procedure|uri]
type Register struct {
	Request   ID
	Options   map[string]interface{}
	Procedure URI
}

func (msg *Register) MessageType() MessageType {
	return REGISTER
}

// [REGISTERED, REGISTER.Request|id, Registration|id]
type Registered struct {
	Request      ID
	Registration ID
}

func (msg *Registered) MessageType() MessageType {
	return REGISTERED
}

// [UNREGISTER, Request|id, REGISTERED.Registration|id]
type Unregister struct {
	Request      ID
	Registration ID
}

func (msg *Unregister) MessageType() MessageType {
	return UNREGISTER
}

// [UNREGISTERED, UNREGISTER.Request|id]
type Unregistered struct {
	Request ID
}

func (msg *Unregistered) MessageType() MessageType {
	return UNREGISTERED
}

// [INVOCATION, Request|id, REGISTERED.Registration|id, Details|dict]
// [INVOCATION, Request|id, REGISTERED.Registration|id, Details|dict, CALL.Arguments|list]
// [INVOCATION, Request|id, REGISTERED.Registration|id, Details|dict, CALL.Arguments|list, CALL.ArgumentsKw|dict]
type Invocation struct {
	Request      ID
	Registration ID
	Details      map[string]interface{}
	Arguments    []interface{}          `wamp:"omitempty"`
	ArgumentsKw  map[string]interface{} `wamp:"omitempty"`
}

func (msg *Invocation) MessageType() MessageType {
	return INVOCATION
}

// [YIELD, INVOCATION.Request|id, Options|dict]
// [YIELD, INVOCATION.Request|id, Options|dict, Arguments|list]
// [YIELD, INVOCATION.Request|id, Options|dict, Arguments|list, ArgumentsKw|dict]
type Yield struct {
	Request     ID
	Options     map[string]interface{}
	Arguments   []interface{}          `wamp:"omitempty"`
	ArgumentsKw map[string]interface{} `wamp:"omitempty"`
}

func (msg *Yield) MessageType() MessageType {
	return YIELD
}

// [CANCEL, CALL.Request|id, Options|dict]
type Cancel struct {
	Request ID
	Options map[string]interface{}
}

func (msg *Cancel) MessageType() MessageType {
	return CANCEL
}

// [INTERRUPT, INVOCATION.Request|id, Options|dict]
type Interrupt struct {
	Request ID
	Options map[string]interface{}
}

func (msg *Interrupt) MessageType() MessageType {
	return INTERRUPT
}
<commit_msg>message.go: Updated link to URI spec<commit_after>package turnpike

// Message is a generic container for a WAMP message.
type Message interface {
	MessageType() MessageType
}

type MessageType int

func (mt MessageType) New() Message {
	switch mt {
	case HELLO:
		return new(Hello)
	case WELCOME:
		return new(Welcome)
	case ABORT:
		return new(Abort)
	case CHALLENGE:
		return new(Challenge)
	case AUTHENTICATE:
		return new(Authenticate)
	case GOODBYE:
		return new(Goodbye)
	case HEARTBEAT:
		return new(Heartbeat)
	case ERROR:
		return new(Error)

	case PUBLISH:
		return new(Publish)
	case PUBLISHED:
		return new(Published)

	case SUBSCRIBE:
		return new(Subscribe)
	case SUBSCRIBED:
		return new(Subscribed)
	case UNSUBSCRIBE:
		return new(Unsubscribe)
	case UNSUBSCRIBED:
		return new(Unsubscribed)
	case EVENT:
		return new(Event)

	case CALL:
		return new(Call)
	case CANCEL:
		return new(Cancel)
	case RESULT:
		return new(Result)

	case REGISTER:
		return new(Register)
	case REGISTERED:
		return new(Registered)
	case UNREGISTER:
		return new(Unregister)
	case UNREGISTERED:
		return new(Unregistered)
	case INVOCATION:
		return new(Invocation)
	case INTERRUPT:
		return new(Interrupt)
	case YIELD:
		return new(Yield)
	default:
		// TODO: allow custom message types?
		return nil
	}
}

func (mt MessageType) String() string {
	switch mt {
	case HELLO:
		return "HELLO"
	case WELCOME:
		return "WELCOME"
	case ABORT:
		return "ABORT"
	case CHALLENGE:
		return "CHALLENGE"
	case AUTHENTICATE:
		return "AUTHENTICATE"
	case GOODBYE:
		return "GOODBYE"
	case HEARTBEAT:
		return "HEARTBEAT"
	case ERROR:
		return "ERROR"

	case PUBLISH:
		return "PUBLISH"
	case PUBLISHED:
		return "PUBLISHED"

	case SUBSCRIBE:
		return "SUBSCRIBE"
	case SUBSCRIBED:
		return "SUBSCRIBED"
	case UNSUBSCRIBE:
		return "UNSUBSCRIBE"
	case UNSUBSCRIBED:
		return "UNSUBSCRIBED"
	case EVENT:
		return "EVENT"

	case CALL:
		return "CALL"
	case CANCEL:
		return "CANCEL"
	case RESULT:
		return "RESULT"

	case REGISTER:
		return "REGISTER"
	case REGISTERED:
		return "REGISTERED"
	case UNREGISTER:
		return "UNREGISTER"
	case UNREGISTERED:
		return "UNREGISTERED"
	case INVOCATION:
		return "INVOCATION"
	case INTERRUPT:
		return "INTERRUPT"
	case YIELD:
		return "YIELD"
	default:
		// TODO: allow custom message types?
		panic("Invalid message type")
	}
}

const (
	HELLO        MessageType = 1
	WELCOME      MessageType = 2
	ABORT        MessageType = 3
	CHALLENGE    MessageType = 4
	AUTHENTICATE MessageType = 5
	GOODBYE      MessageType = 6
	HEARTBEAT    MessageType = 7
	ERROR        MessageType = 8

	PUBLISH   MessageType = 16 //	Tx 	Rx
	PUBLISHED MessageType = 17 //	Rx 	Tx

	SUBSCRIBE    MessageType = 32 //	Rx 	Tx
	SUBSCRIBED   MessageType = 33 //	Tx 	Rx
	UNSUBSCRIBE  MessageType = 34 //	Rx 	Tx
	UNSUBSCRIBED MessageType = 35 //	Tx 	Rx
	EVENT        MessageType = 36 //	Tx 	Rx

	CALL   MessageType = 48 //	Tx 	Rx
	CANCEL MessageType = 49 //	Tx 	Rx
	RESULT MessageType = 50 //	Rx 	Tx

	REGISTER     MessageType = 64 //	Rx 	Tx
	REGISTERED   MessageType = 65 //	Tx 	Rx
	UNREGISTER   MessageType = 66 //	Rx 	Tx
	UNREGISTERED MessageType = 67 //	Tx 	Rx
	INVOCATION   MessageType = 68 //	Tx 	Rx
	INTERRUPT    MessageType = 69 //	Tx 	Rx
	YIELD        MessageType = 70 //	Rx 	Tx
)

// URIs are dot-separated identifiers, where each component *should* only contain letters, numbers or underscores.
//
// See the documentation for specifics: https://github.com/wamp-proto/wamp-proto/blob/master/rfc/text/basic/bp_identifiers.md#uris-uris
type URI string

// An ID is a unique, non-negative number. Different uses may have additional restrictions.
type ID uint64

// [HELLO, Realm|uri, Details|dict]
type Hello struct {
	Realm   URI
	Details map[string]interface{}
}

func (msg *Hello) MessageType() MessageType {
	return HELLO
}

// [WELCOME, Session|id, Details|dict]
type Welcome struct {
	Id      ID
	Details map[string]interface{}
}

func (msg *Welcome) MessageType() MessageType {
	return WELCOME
}

// [ABORT, Details|dict, Reason|uri]
type Abort struct {
	Details map[string]interface{}
	Reason  URI
}

func (msg *Abort) MessageType() MessageType {
	return ABORT
}

// [CHALLENGE, AuthMethod|string, Extra|dict]
type Challenge struct {
	AuthMethod string
	Extra      map[string]interface{}
}

func (msg *Challenge) MessageType() MessageType {
	return CHALLENGE
}

// [AUTHENTICATE, Signature|string, Extra|dict]
type Authenticate struct {
	Signature string
	Extra     map[string]interface{}
}

func (msg *Authenticate) MessageType() MessageType {
	return AUTHENTICATE
}

// [GOODBYE, Details|dict, Reason|uri]
type Goodbye struct {
	Details map[string]interface{}
	Reason  URI
}

func (msg *Goodbye) MessageType() MessageType {
	return GOODBYE
}

// [HEARTBEAT, IncomingSeq|integer, OutgoingSeq|integer
// [HEARTBEAT, IncomingSeq|integer, OutgoingSeq|integer, Discard|string]
type Heartbeat struct {
	IncomingSeq uint
	OutgoingSeq uint
	Discard     string
}

func (msg *Heartbeat) MessageType() MessageType {
	return HEARTBEAT
}

// [ERROR, REQUEST.Type|int, REQUEST.Request|id, Details|dict, Error|uri]
// [ERROR, REQUEST.Type|int, REQUEST.Request|id, Details|dict, Error|uri, Arguments|list]
// [ERROR, REQUEST.Type|int, REQUEST.Request|id, Details|dict, Error|uri, Arguments|list, ArgumentsKw|dict]
type Error struct {
	Type        MessageType
	Request     ID
	Details     map[string]interface{}
	Error       URI
	Arguments   []interface{}          `wamp:"omitempty"`
	ArgumentsKw map[string]interface{} `wamp:"omitempty"`
}

func (msg *Error) MessageType() MessageType {
	return ERROR
}

// [PUBLISH, Request|id, Options|dict, Topic|uri]
// [PUBLISH, Request|id, Options|dict, Topic|uri, Arguments|list]
// [PUBLISH, Request|id, Options|dict, Topic|uri, Arguments|list, ArgumentsKw|dict]
type Publish struct {
	Request     ID
	Options     map[string]interface{}
	Topic       URI
	Arguments   []interface{}          `wamp:"omitempty"`
	ArgumentsKw map[string]interface{} `wamp:"omitempty"`
}

func (msg *Publish) MessageType() MessageType {
	return PUBLISH
}

// [PUBLISHED, PUBLISH.Request|id, Publication|id]
type Published struct {
	Request     ID
	Publication ID
}

func (msg *Published) MessageType() MessageType {
	return PUBLISHED
}

// [SUBSCRIBE, Request|id, Options|dict, Topic|uri]
type Subscribe struct {
	Request ID
	Options map[string]interface{}
	Topic   URI
}

func (msg *Subscribe) MessageType() MessageType {
	return SUBSCRIBE
}

// [SUBSCRIBED, SUBSCRIBE.Request|id, Subscription|id]
type Subscribed struct {
	Request      ID
	Subscription ID
}

func (msg *Subscribed) MessageType() MessageType {
	return SUBSCRIBED
}

// [UNSUBSCRIBE, Request|id, SUBSCRIBED.Subscription|id]
type Unsubscribe struct {
	Request      ID
	Subscription ID
}

func (msg *Unsubscribe) MessageType() MessageType {
	return UNSUBSCRIBE
}

// [UNSUBSCRIBED, UNSUBSCRIBE.Request|id]
type Unsubscribed struct {
	Request ID
}

func (msg *Unsubscribed) MessageType() MessageType {
	return UNSUBSCRIBED
}

// [EVENT, SUBSCRIBED.Subscription|id, PUBLISHED.Publication|id, Details|dict]
// [EVENT, SUBSCRIBED.Subscription|id, PUBLISHED.Publication|id, Details|dict, PUBLISH.Arguments|list]
// [EVENT, SUBSCRIBED.Subscription|id, PUBLISHED.Publication|id, Details|dict, PUBLISH.Arguments|list,
//     PUBLISH.ArgumentsKw|dict]
type Event struct {
	Subscription ID
	Publication  ID
	Details      map[string]interface{}
	Arguments    []interface{}          `wamp:"omitempty"`
	ArgumentsKw  map[string]interface{} `wamp:"omitempty"`
}

func (msg *Event) MessageType() MessageType {
	return EVENT
}

// CallResult represents the result of a CALL.
type CallResult struct {
	Args   []interface{}
	Kwargs map[string]interface{}
	Err    URI
}

// [CALL, Request|id, Options|dict, Procedure|uri]
// [CALL, Request|id, Options|dict, Procedure|uri, Arguments|list]
// [CALL, Request|id, Options|dict, Procedure|uri, Arguments|list, ArgumentsKw|dict]
type Call struct {
	Request     ID
	Options     map[string]interface{}
	Procedure   URI
	Arguments   []interface{}          `wamp:"omitempty"`
	ArgumentsKw map[string]interface{} `wamp:"omitempty"`
}

func (msg *Call) MessageType() MessageType {
	return CALL
}

// [RESULT, CALL.Request|id, Details|dict]
// [RESULT, CALL.Request|id, Details|dict, YIELD.Arguments|list]
// [RESULT, CALL.Request|id, Details|dict, YIELD.Arguments|list, YIELD.ArgumentsKw|dict]
type Result struct {
	Request     ID
	Details     map[string]interface{}
	Arguments   []interface{}          `wamp:"omitempty"`
	ArgumentsKw map[string]interface{} `wamp:"omitempty"`
}

func (msg *Result) MessageType() MessageType {
	return RESULT
}

// [REGISTER, Request|id, Options|dict, Procedure|uri]
type Register struct {
	Request   ID
	Options   map[string]interface{}
	Procedure URI
}

func (msg *Register) MessageType() MessageType {
	return REGISTER
}

// [REGISTERED, REGISTER.Request|id, Registration|id]
type Registered struct {
	Request      ID
	Registration ID
}

func (msg *Registered) MessageType() MessageType {
	return REGISTERED
}

// [UNREGISTER, Request|id, REGISTERED.Registration|id]
type Unregister struct {
	Request      ID
	Registration ID
}

func (msg *Unregister) MessageType() MessageType {
	return UNREGISTER
}

// [UNREGISTERED, UNREGISTER.Request|id]
type Unregistered struct {
	Request ID
}

func (msg *Unregistered) MessageType() MessageType {
	return UNREGISTERED
}

// [INVOCATION, Request|id, REGISTERED.Registration|id, Details|dict]
// [INVOCATION, Request|id, REGISTERED.Registration|id, Details|dict, CALL.Arguments|list]
// [INVOCATION, Request|id, REGISTERED.Registration|id, Details|dict, CALL.Arguments|list, CALL.ArgumentsKw|dict]
type Invocation struct {
	Request      ID
	Registration ID
	Details      map[string]interface{}
	Arguments    []interface{}          `wamp:"omitempty"`
	ArgumentsKw  map[string]interface{} `wamp:"omitempty"`
}

func (msg *Invocation) MessageType() MessageType {
	return INVOCATION
}

// [YIELD, INVOCATION.Request|id, Options|dict]
// [YIELD, INVOCATION.Request|id, Options|dict, Arguments|list]
// [YIELD, INVOCATION.Request|id, Options|dict, Arguments|list, ArgumentsKw|dict]
type Yield struct {
	Request     ID
	Options     map[string]interface{}
	Arguments   []interface{}          `wamp:"omitempty"`
	ArgumentsKw map[string]interface{} `wamp:"omitempty"`
}

func (msg *Yield) MessageType() MessageType {
	return YIELD
}

// [CANCEL, CALL.Request|id, Options|dict]
type Cancel struct {
	Request ID
	Options map[string]interface{}
}

func (msg *Cancel) MessageType() MessageType {
	return CANCEL
}

// [INTERRUPT, INVOCATION.Request|id, Options|dict]
type Interrupt struct {
	Request ID
	Options map[string]interface{}
}

func (msg *Interrupt) MessageType() MessageType {
	return INTERRUPT
}
<|endoftext|>


<commit_before>package garminconnect

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
)

const (
	WORKOUT_FILE_TYPE = "FIT_TYPE_5"
)

type Queue struct {
	Host             string    `json:"serviceHost"`
	NumberOfMessages int       `json:"numOfMessages"`
	Messages         []Message `json:"messages"`
}

type Message struct {
	Id                int      `json:"messageId"`
	Type              string   `json:"messageType"`
	Status            string   `json:"messageStatus"`
	DeviceId          int      `json:"deviceId"`
	DeviceName        string   `json:"deviceName"`
	ApplicationKey    string   `json:"applicationKey"`
	FirmwareVersion   string   `json:"FirmwareVersion"`
	WifiSetup         bool     `json:"wifiSetup"`
	DeviceXmlDataType string   `json:"deviceXmlDataType"`
	Metadata          Metadata `json:"metadata"`
}

type Metadata struct {
	Filetype    string `json:"fileType"`
	MessageUrl  string `json:"messageUrl"`
	Absolute    bool   `json:"absolute"`
	MessageName string `json:"messageName"`
	GroupName   string `json:"groupName"`
	Priority    int    `json:"priority"`
	Id          int    `json:"metaDataId"`
	AppDetails  string `json:"appDetails"`
}

func (gc *Client) Messages() ([]Message, error) {
	response, err := gc.client.Get(GARMIN_CONNECT_URL + "/modern/proxy/device-service/devicemessage/messages")

	if err != nil {
		return []Message{}, err
	}

	defer response.Body.Close()

	var queue Queue

	err = json.NewDecoder(response.Body).Decode(&queue)

	if err != nil {
		panic(err)
	}

	return queue.Messages, nil
}

func (gc *Client) DeleteMessage(messageId int) error {
	request, err := http.NewRequest(http.MethodDelete, GARMIN_CONNECT_URL+fmt.Sprintf("/modern/proxy/device-service/devicemessage/message/%d", messageId), nil)

	if err != nil {
		return err
	}

	response, err := gc.client.Do(request)

	if err != nil {
		return err
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		return errors.New(fmt.Sprintf("%d", response.StatusCode))
	}

	return nil
}
<commit_msg>Set a message as received<commit_after>package garminconnect

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
)

const (
	WORKOUT_FILE_TYPE = "FIT_TYPE_5"
)

type Queue struct {
	Host             string    `json:"serviceHost"`
	NumberOfMessages int       `json:"numOfMessages"`
	Messages         []Message `json:"messages"`
}

type Message struct {
	Id                int      `json:"messageId"`
	Type              string   `json:"messageType"`
	Status            string   `json:"messageStatus"`
	DeviceId          int      `json:"deviceId"`
	DeviceName        string   `json:"deviceName"`
	ApplicationKey    string   `json:"applicationKey"`
	FirmwareVersion   string   `json:"FirmwareVersion"`
	WifiSetup         bool     `json:"wifiSetup"`
	DeviceXmlDataType string   `json:"deviceXmlDataType"`
	Metadata          Metadata `json:"metadata"`
}

type Metadata struct {
	Filetype    string `json:"fileType"`
	MessageUrl  string `json:"messageUrl"`
	Absolute    bool   `json:"absolute"`
	MessageName string `json:"messageName"`
	GroupName   string `json:"groupName"`
	Priority    int    `json:"priority"`
	Id          int    `json:"metaDataId"`
	AppDetails  string `json:"appDetails"`
}

func (gc *Client) Messages() ([]Message, error) {
	response, err := gc.client.Get(GARMIN_CONNECT_URL + "/modern/proxy/device-service/devicemessage/messages")

	if err != nil {
		return []Message{}, err
	}

	defer response.Body.Close()

	var queue Queue

	err = json.NewDecoder(response.Body).Decode(&queue)

	if err != nil {
		panic(err)
	}

	return queue.Messages, nil
}

func (gc *Client) MessageReceived(messageId int) error {
	response, err := gc.client.PostForm(GARMIN_CONNECT_URL+fmt.Sprintf("/modern/proxy/device-service/devicemessage/message/%d?status=received", messageId), nil)

	if err != nil {
		return err
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		return errors.New(fmt.Sprintf("%d", response.StatusCode))
	}

	return nil
}

func (gc *Client) DeleteMessage(messageId int) error {
	request, err := http.NewRequest(http.MethodDelete, GARMIN_CONNECT_URL+fmt.Sprintf("/modern/proxy/device-service/devicemessage/message/%d", messageId), nil)

	if err != nil {
		return err
	}

	response, err := gc.client.Do(request)

	if err != nil {
		return err
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		return errors.New(fmt.Sprintf("%d", response.StatusCode))
	}

	return nil
}
<|endoftext|>


<commit_before>package taskq

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"hash/fnv"
	"time"

	"github.com/valyala/gozstd"
	"github.com/vmihailenco/msgpack/v4"

	"github.com/vmihailenco/taskq/v2/internal"
)

// ErrDuplicate is returned when adding duplicate message to the queue.
var ErrDuplicate = errors.New("taskq: message with such name already exists")

// Message is used to create and retrieve messages from a queue.
type Message struct {
	Ctx context.Context `msgpack:"-"`

	// SQS/IronMQ message id.
	ID string `msgpack:",omitempty"`

	// Optional name for the message. Messages with the same name
	// are processed only once.
	Name string `msgpack:"-"`

	// Delay specifies the duration the queue must wait
	// before executing the message.
	Delay time.Duration `msgpack:"-"`

	// Function args passed to the handler.
	Args []interface{} `msgpack:"-"`

	// Binary representation of the args.
	ArgsCompression string `msgpack:",omitempty"`
	ArgsBin         []byte

	// SQS/IronMQ reservation id that is used to release/delete the message.
	ReservationID string `msgpack:"-"`

	// The number of times the message has been reserved or released.
	ReservedCount int `msgpack:",omitempty"`

	TaskName string
	Err      error `msgpack:"-"`

	evt                *ProcessMessageEvent
	marshalBinaryCache []byte
}

func NewMessage(ctx context.Context, args ...interface{}) *Message {
	return &Message{
		Ctx:  ctx,
		Args: args,
	}
}

func (m *Message) String() string {
	return fmt.Sprintf("Message<Id=%q Name=%q ReservedCount=%d>",
		m.ID, m.Name, m.ReservedCount)
}

// OnceInPeriod uses the period and the args to generate such a message name
// that message with such args is added to the queue once in a given period.
// If args are not provided then message args are used instead.
func (m *Message) OnceInPeriod(period time.Duration, args ...interface{}) *Message {
	if len(args) == 0 {
		args = m.Args
	}
	m.Name = fmt.Sprintf("%s-%s-%d", hashArgs(args), period, timeSlot(period))
	m.Delay = period + 5*time.Second
	return m
}

func hashArgs(args []interface{}) []byte {
	var buf bytes.Buffer
	enc := msgpack.NewEncoder(&buf)
	_ = enc.EncodeMulti(args...)
	b := buf.Bytes()

	if len(b) <= 32 {
		return b
	}

	h := fnv.New128a()
	_, _ = h.Write(b)
	return h.Sum(nil)
}

func timeSlot(period time.Duration) int64 {
	if period <= 0 {
		return 0
	}
	return time.Now().UnixNano() / int64(period)
}

func (m *Message) MarshalArgs() ([]byte, error) {
	if m.ArgsBin != nil {
		if m.ArgsCompression == "" {
			return m.ArgsBin, nil
		}
		if m.Args == nil {
			return gozstd.Decompress(nil, m.ArgsBin)
		}
	}

	b, err := msgpack.Marshal(m.Args)
	if err != nil {
		return nil, err
	}
	m.ArgsBin = b

	return b, nil
}

func (m *Message) MarshalBinary() ([]byte, error) {
	if m.TaskName == "" {
		return nil, internal.ErrTaskNameRequired
	}
	if m.marshalBinaryCache != nil {
		return m.marshalBinaryCache, nil
	}

	_, err := m.MarshalArgs()
	if err != nil {
		return nil, err
	}

	if m.ArgsCompression == "" && len(m.ArgsBin) >= 512 {
		compressed := gozstd.Compress(nil, m.ArgsBin)
		if len(compressed) < len(m.ArgsBin) {
			m.ArgsCompression = "zstd"
			m.ArgsBin = compressed
		}
	}

	b, err := msgpack.Marshal(m)
	if err != nil {
		return nil, err
	}

	m.marshalBinaryCache = b
	return b, nil
}

func (m *Message) UnmarshalBinary(b []byte) error {
	err := msgpack.Unmarshal(b, m)
	if err != nil {
		return err
	}

	switch m.ArgsCompression {
	case "":
	case "zstd":
		b, err = gozstd.Decompress(nil, m.ArgsBin)
		if err != nil {
			return err
		}
		m.ArgsCompression = ""
		m.ArgsBin = b
	default:
		return fmt.Errorf("taskq: unsupported compression=%s", m.ArgsCompression)
	}

	return nil
}
<commit_msg>Add Message.SetDelay<commit_after>package taskq

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"hash/fnv"
	"time"

	"github.com/valyala/gozstd"
	"github.com/vmihailenco/msgpack/v4"

	"github.com/vmihailenco/taskq/v2/internal"
)

// ErrDuplicate is returned when adding duplicate message to the queue.
var ErrDuplicate = errors.New("taskq: message with such name already exists")

// Message is used to create and retrieve messages from a queue.
type Message struct {
	Ctx context.Context `msgpack:"-"`

	// SQS/IronMQ message id.
	ID string `msgpack:",omitempty"`

	// Optional name for the message. Messages with the same name
	// are processed only once.
	Name string `msgpack:"-"`

	// Delay specifies the duration the queue must wait
	// before executing the message.
	Delay time.Duration `msgpack:"-"`

	// Function args passed to the handler.
	Args []interface{} `msgpack:"-"`

	// Binary representation of the args.
	ArgsCompression string `msgpack:",omitempty"`
	ArgsBin         []byte

	// SQS/IronMQ reservation id that is used to release/delete the message.
	ReservationID string `msgpack:"-"`

	// The number of times the message has been reserved or released.
	ReservedCount int `msgpack:",omitempty"`

	TaskName string
	Err      error `msgpack:"-"`

	evt                *ProcessMessageEvent
	marshalBinaryCache []byte
}

func NewMessage(ctx context.Context, args ...interface{}) *Message {
	return &Message{
		Ctx:  ctx,
		Args: args,
	}
}

func (m *Message) String() string {
	return fmt.Sprintf("Message<Id=%q Name=%q ReservedCount=%d>",
		m.ID, m.Name, m.ReservedCount)
}

// OnceInPeriod uses the period and the args to generate such a message name
// that message with such args is added to the queue once in a given period.
// If args are not provided then message args are used instead.
func (m *Message) OnceInPeriod(period time.Duration, args ...interface{}) *Message {
	if len(args) == 0 {
		args = m.Args
	}
	m.Name = fmt.Sprintf("%s-%s-%d", hashArgs(args), period, timeSlot(period))
	m.Delay = period + 5*time.Second
	return m
}

// SetDelay sets message delay.
func (m *Message) SetDelay(delay time.Duration) *Message {
	m.Delay = delay
	return m
}

func hashArgs(args []interface{}) []byte {
	var buf bytes.Buffer
	enc := msgpack.NewEncoder(&buf)
	_ = enc.EncodeMulti(args...)
	b := buf.Bytes()

	if len(b) <= 32 {
		return b
	}

	h := fnv.New128a()
	_, _ = h.Write(b)
	return h.Sum(nil)
}

func timeSlot(period time.Duration) int64 {
	if period <= 0 {
		return 0
	}
	return time.Now().UnixNano() / int64(period)
}

func (m *Message) MarshalArgs() ([]byte, error) {
	if m.ArgsBin != nil {
		if m.ArgsCompression == "" {
			return m.ArgsBin, nil
		}
		if m.Args == nil {
			return gozstd.Decompress(nil, m.ArgsBin)
		}
	}

	b, err := msgpack.Marshal(m.Args)
	if err != nil {
		return nil, err
	}
	m.ArgsBin = b

	return b, nil
}

func (m *Message) MarshalBinary() ([]byte, error) {
	if m.TaskName == "" {
		return nil, internal.ErrTaskNameRequired
	}
	if m.marshalBinaryCache != nil {
		return m.marshalBinaryCache, nil
	}

	_, err := m.MarshalArgs()
	if err != nil {
		return nil, err
	}

	if m.ArgsCompression == "" && len(m.ArgsBin) >= 512 {
		compressed := gozstd.Compress(nil, m.ArgsBin)
		if len(compressed) < len(m.ArgsBin) {
			m.ArgsCompression = "zstd"
			m.ArgsBin = compressed
		}
	}

	b, err := msgpack.Marshal(m)
	if err != nil {
		return nil, err
	}

	m.marshalBinaryCache = b
	return b, nil
}

func (m *Message) UnmarshalBinary(b []byte) error {
	err := msgpack.Unmarshal(b, m)
	if err != nil {
		return err
	}

	switch m.ArgsCompression {
	case "":
	case "zstd":
		b, err = gozstd.Decompress(nil, m.ArgsBin)
		if err != nil {
			return err
		}
		m.ArgsCompression = ""
		m.ArgsBin = b
	default:
		return fmt.Errorf("taskq: unsupported compression=%s", m.ArgsCompression)
	}

	return nil
}
<|endoftext|>


<commit_before>package assert

import (
	"fmt"
	"io"
	"os"
)

var writer io.Writer = os.Stdout

type ThatInt int

func (thatInt ThatInt) IsZero() ThatInt {
	if thatInt != 0 {
		fmt.Fprintf(writer, "Expected <0>, but was <%d>.\n", thatInt)
		panic("Expected int to be zero but wasn't.")
	}
	return thatInt
}

func (thatInt ThatInt) IsEqualTo(comparedInt int) ThatInt {
	if int(thatInt) != comparedInt {
		fmt.Fprintf(writer, "Expected <%d>, but was <%d>.\n", comparedInt, thatInt)
		panic("Expected int to be equal but wasn't.")
	}
	return thatInt
}
<commit_msg>removed duplicate code<commit_after>package assert

import (
	"fmt"
	"io"
	"os"
)

var writer io.Writer = os.Stdout

type ThatInt int

func (thatInt ThatInt) IsZero() ThatInt {
	return thatInt.IsEqualTo(0)
}

func (thatInt ThatInt) IsEqualTo(comparedInt int) ThatInt {
	if int(thatInt) != comparedInt {
		fmt.Fprintf(writer, "Expected <%d>, but was <%d>.\n", comparedInt, thatInt)
		panic("Expected int to be equal but wasn't.")
	}
	return thatInt
}
<|endoftext|>


<commit_before>package auctioneer

import "github.com/cloudfoundry-incubator/runtime-schema/metric"

const (
	LRPAuctionsStarted      = metric.Counter("AuctioneerLRPAuctionsStarted")
	LRPAuctionsFailed       = metric.Counter("AuctioneerLRPAuctionsFailed")
	TaskAuctionsStarted     = metric.Counter("AuctioneerTaskAuctionsStarted")
	TaskAuctionsFailed      = metric.Counter("AuctioneerTaskAuctionsFailed")
	FetchStatesDuration     = metric.Duration("AuctioneerFetchStatesDuration")
	FailedCellStateRequests = metric.Counter("AuctioneerFailedCellStateRequests")
)
<commit_msg>Update/Rename runtime-schema -> runtimeschema<commit_after>package auctioneer

import "code.cloudfoundry.org/runtimeschema/metric"

const (
	LRPAuctionsStarted      = metric.Counter("AuctioneerLRPAuctionsStarted")
	LRPAuctionsFailed       = metric.Counter("AuctioneerLRPAuctionsFailed")
	TaskAuctionsStarted     = metric.Counter("AuctioneerTaskAuctionsStarted")
	TaskAuctionsFailed      = metric.Counter("AuctioneerTaskAuctionsFailed")
	FetchStatesDuration     = metric.Duration("AuctioneerFetchStatesDuration")
	FailedCellStateRequests = metric.Counter("AuctioneerFailedCellStateRequests")
)
<|endoftext|>


<commit_before>// Package mgotest provides standalone test instances of mongo sutable for use
// in tests.
package mgotest

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"text/template"
	"time"

	"labix.org/v2/mgo"

	"github.com/facebookgo/freeport"
	"github.com/facebookgo/stack"
	"github.com/facebookgo/waitout"
)

var mgoWaitingForConnections = []byte("waiting for connections on port")

var configTemplate, configTemplateErr = template.New("config").Parse(`
bind_ip          = 127.0.0.1
dbpath           = {{.DBPath}}
nohttpinterface  = true
nojournal        = true
noprealloc       = true
nounixsocket     = true
nssize           = 2
port             = {{.Port}}
quiet            = true
smallfiles       = true
{{if .ReplSet}}
oplogSize        = 1
replSet          = rs
{{end}}
`)

func init() {
	if configTemplateErr != nil {
		panic(configTemplateErr)
	}
}

// Fatalf is satisfied by testing.T or testing.B.
type Fatalf interface {
	Fatalf(format string, args ...interface{})
}

// Server is a unique instance of a mongod.
type Server struct {
	Port        int
	DBPath      string
	ReplSet     bool
	StopTimeout time.Duration
	T           Fatalf
	cmd         *exec.Cmd
}

// Start the server, this will return once the server has been started.
func (s *Server) Start() {
	if s.Port == 0 {
		port, err := freeport.Get()
		if err != nil {
			s.T.Fatalf(err.Error())
		}
		s.Port = port
	}

	dir, err := ioutil.TempDir("", "mgotest-dbpath-"+getTestNameFromStack())
	if err != nil {
		s.T.Fatalf(err.Error())
	}
	s.DBPath = dir

	cf, err := ioutil.TempFile(s.DBPath, "config-")
	if err != nil {
		s.T.Fatalf(err.Error())
	}
	if err := configTemplate.Execute(cf, s); err != nil {
		s.T.Fatalf(err.Error())
	}
	if err := cf.Close(); err != nil {
		s.T.Fatalf(err.Error())
	}

	waiter := waitout.New(mgoWaitingForConnections)
	s.cmd = exec.Command("mongod", "--config", cf.Name())
	s.cmd.Env = envPlusLcAll()
	if os.Getenv("MGOTEST_VERBOSE") == "1" {
		s.cmd.Stdout = io.MultiWriter(os.Stdout, waiter)
		s.cmd.Stderr = os.Stderr
	} else {
		s.cmd.Stdout = waiter
	}
	if err := s.cmd.Start(); err != nil {
		s.T.Fatalf(err.Error())
	}
	waiter.Wait()
}

// Stop the server, this will also remove all data.
func (s *Server) Stop() {
	fin := make(chan struct{})
	go func() {
		defer close(fin)
		s.cmd.Process.Kill()
		os.RemoveAll(s.DBPath)
	}()
	select {
	case <-fin:
	case <-time.After(s.StopTimeout):
	}
}

// URL for the mongo server, suitable for use with mgo.Dial.
func (s *Server) URL() string {
	return fmt.Sprintf("127.0.0.1:%d", s.Port)
}

// Session for the mongo server.
func (s *Server) Session() *mgo.Session {
	session, err := mgo.Dial(s.URL())
	if err != nil {
		s.T.Fatalf(err.Error())
	}
	return session
}

// NewStartedServer creates a new server starts it.
func NewStartedServer(t Fatalf) *Server {
	s := &Server{
		T:           t,
		StopTimeout: 15 * time.Second,
	}
	s.Start()
	return s
}

// NewReplSetServer creates a new server starts it with ReplSet enabled.
func NewReplSetServer(t Fatalf) *Server {
	s := &Server{
		T:           t,
		StopTimeout: 15 * time.Second,
		ReplSet:     true,
	}
	s.Start()
	return s
}

func envPlusLcAll() []string {
	env := os.Environ()
	return append(env, "LC_ALL=C")
}

func getTestNameFromStack() string {
	s := stack.Callers(1)

	for _, f := range s {
		if strings.HasSuffix(f.File, "_test.go") && strings.HasPrefix(f.Name, "Test") {
			return fmt.Sprintf("%s_", f.Name)
		}
	}

	// find the first caller outside ourselves
	outside := s[0].File
	for _, f := range s {
		if f.File != s[0].File {
			outside = f.Name
			break
		}
	}

	return fmt.Sprintf("TestNameNotFound_%s_", outside)
}
<commit_msg>add retries for sporadic failures from inherent freeport race<commit_after>// Package mgotest provides standalone test instances of mongo sutable for use
// in tests.
package mgotest

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"text/template"
	"time"

	"labix.org/v2/mgo"

	"github.com/facebookgo/freeport"
	"github.com/facebookgo/stack"
	"github.com/facebookgo/waitout"
)

var mgoWaitingForConnections = []byte("waiting for connections on port")

var configTemplate, configTemplateErr = template.New("config").Parse(`
bind_ip          = 127.0.0.1
dbpath           = {{.DBPath}}
nohttpinterface  = true
nojournal        = true
noprealloc       = true
nounixsocket     = true
nssize           = 2
port             = {{.Port}}
quiet            = true
smallfiles       = true
{{if .ReplSet}}
oplogSize        = 1
replSet          = rs
{{end}}
`)

func init() {
	if configTemplateErr != nil {
		panic(configTemplateErr)
	}
}

// Fatalf is satisfied by testing.T or testing.B.
type Fatalf interface {
	Fatalf(format string, args ...interface{})
}

// Server is a unique instance of a mongod.
type Server struct {
	Port        int
	DBPath      string
	ReplSet     bool
	StopTimeout time.Duration
	T           Fatalf
	cmd         *exec.Cmd
}

// Start the server, this will return once the server has been started.
func (s *Server) Start() {
	if s.Port == 0 {
		port, err := freeport.Get()
		if err != nil {
			s.T.Fatalf(err.Error())
		}
		s.Port = port
	}

	dir, err := ioutil.TempDir("", "mgotest-dbpath-"+getTestNameFromStack())
	if err != nil {
		s.T.Fatalf(err.Error())
	}
	s.DBPath = dir

	cf, err := ioutil.TempFile(s.DBPath, "config-")
	if err != nil {
		s.T.Fatalf(err.Error())
	}
	if err := configTemplate.Execute(cf, s); err != nil {
		s.T.Fatalf(err.Error())
	}
	if err := cf.Close(); err != nil {
		s.T.Fatalf(err.Error())
	}

	waiter := waitout.New(mgoWaitingForConnections)
	s.cmd = exec.Command("mongod", "--config", cf.Name())
	s.cmd.Env = envPlusLcAll()
	if os.Getenv("MGOTEST_VERBOSE") == "1" {
		s.cmd.Stdout = io.MultiWriter(os.Stdout, waiter)
		s.cmd.Stderr = os.Stderr
	} else {
		s.cmd.Stdout = waiter
	}
	if err := s.cmd.Start(); err != nil {
		s.T.Fatalf(err.Error())
	}
	waiter.Wait()
}

// Stop the server, this will also remove all data.
func (s *Server) Stop() {
	fin := make(chan struct{})
	go func() {
		defer close(fin)
		s.cmd.Process.Kill()
		os.RemoveAll(s.DBPath)
	}()
	select {
	case <-fin:
	case <-time.After(s.StopTimeout):
	}
}

// URL for the mongo server, suitable for use with mgo.Dial.
func (s *Server) URL() string {
	return fmt.Sprintf("127.0.0.1:%d", s.Port)
}

// Session for the mongo server.
func (s *Server) Session() *mgo.Session {
	session, err := mgo.Dial(s.URL())
	if err != nil {
		s.T.Fatalf(err.Error())
	}
	return session
}

// NewStartedServer creates a new server starts it.
func NewStartedServer(t Fatalf) *Server {
	for {
		s := &Server{
			T:           t,
			StopTimeout: 15 * time.Second,
		}
		start := make(chan struct{})
		go func() {
			defer close(start)
			s.Start()
		}()
		select {
		case <-start:
			return s
		case <-time.After(10 * time.Second):
		}
	}
}

// NewReplSetServer creates a new server starts it with ReplSet enabled.
func NewReplSetServer(t Fatalf) *Server {
	for {
		s := &Server{
			T:           t,
			StopTimeout: 15 * time.Second,
			ReplSet:     true,
		}
		start := make(chan struct{})
		go func() {
			defer close(start)
			s.Start()
		}()
		select {
		case <-start:
			return s
		case <-time.After(10 * time.Second):
		}
	}
}

func envPlusLcAll() []string {
	env := os.Environ()
	return append(env, "LC_ALL=C")
}

func getTestNameFromStack() string {
	s := stack.Callers(1)

	for _, f := range s {
		if strings.HasSuffix(f.File, "_test.go") && strings.HasPrefix(f.Name, "Test") {
			return fmt.Sprintf("%s_", f.Name)
		}
	}

	// find the first caller outside ourselves
	outside := s[0].File
	for _, f := range s {
		if f.File != s[0].File {
			outside = f.Name
			break
		}
	}

	return fmt.Sprintf("TestNameNotFound_%s_", outside)
}
<|endoftext|>