<commit_before>// Package querybuilder parses ResourceD query and turn it into PostgreSQL query.
package querybuilder

import (
	"fmt"
	"strings"

	"github.com/resourced/resourced-master/libstring"
)

// Parse parses ResourceD query and turns it into postgres query.
func Parse(input string) string {
	return parseAnd(input)
}

// parseAnd parses and conjunctive operator.
func parseAnd(input string) string {
	pgQueryParts := make([]string, 0)

	// normalize variation of AND
	input = strings.Replace(input, " AND ", " and ", -1)

	statements := strings.Split(input, " and ")
	for _, statement := range statements {
		pgStatement := parseStatement(statement)
		if pgStatement != "" {
			pgQueryParts = append(pgQueryParts, pgStatement)
		}
	}

	if len(pgQueryParts) > 1 {
		return strings.Join(pgQueryParts, " and ")
	} else if len(pgQueryParts) == 1 {
		return pgQueryParts[0]
	}

	return ""
}

// parseStringField parses ResourceD "string" query and turns it into postgres statement.
func parseStringField(statement, field, operator string) string {
	parts := strings.Split(statement, operator)

	data := parts[len(parts)-1]
	data = strings.TrimSpace(data)
	data = libstring.StripChars(data, `"'`)

	return fmt.Sprintf("%v %v '%v'", field, operator, data)
}

// parseFullTextSearchField parses ResourceD "search" query and turns it into postgres statement.
// Operator is "search" in this context.
func parseFullTextSearchField(statement, field, operator string) string {
	parts := strings.Split(statement, operator)

	searchQuery := parts[len(parts)-1]
	searchQuery = strings.TrimSpace(searchQuery)
	searchQuery = libstring.StripChars(searchQuery, `"'`)

	// Split search query into multiple soon-to-be-tsquery-function-argument (+ boolean operators)
	searchQueryChunks := strings.Fields(searchQuery)

	tsQueryChunksWithBoolOperators := make([]string, 0)
	tsQueryChunk := ""

	for i, chunk := range searchQueryChunks {
		// If chunk is not boolean operator, keep on appending to the same chunk.
		if chunk != "||" && chunk != "&&" {
			tsQueryChunk = tsQueryChunk + " " + chunk

		} else {
			tsQueryChunksWithBoolOperators = append(tsQueryChunksWithBoolOperators, strings.TrimSpace(tsQueryChunk))
			tsQueryChunk = ""

			// append boolean operator as well
			tsQueryChunksWithBoolOperators = append(tsQueryChunksWithBoolOperators, strings.TrimSpace(chunk))
		}

		// If reached the end
		if i == len(searchQueryChunks)-1 {
			if strings.TrimSpace(tsQueryChunk) != "" {
				tsQueryChunksWithBoolOperators = append(tsQueryChunksWithBoolOperators, strings.TrimSpace(tsQueryChunk))
			}
		}
	}

	// Now, let's build series of tsquery functions
	tsQuerySlice := make([]string, 0)

	for _, chunk := range tsQueryChunksWithBoolOperators {
		if chunk == "||" || chunk == "&&" {
			tsQuerySlice = append(tsQuerySlice, chunk)

		} else if strings.Contains(chunk, "|") || strings.Contains(chunk, "&") {
			tsQuerySlice = append(tsQuerySlice, fmt.Sprintf("to_tsquery('english', '%v')", chunk))

		} else {
			tsQuerySlice = append(tsQuerySlice, fmt.Sprintf("plainto_tsquery('english', '%v')", chunk))
		}
	}

	tsQueries := strings.Join(tsQuerySlice, " ")

	return fmt.Sprintf(`to_tsvector('english', regexp_replace(%v, '[^\w]+', ' ', 'gi')) || to_tsvector('english', %v) @@ (%v)`, field, field, tsQueries)
}

// parseStatement parses ResourceD statement and turns it into postgres statement.
func parseStatement(statement string) string {
	statement = strings.TrimSpace(statement)

	// Querying tags.
	// There can only be 1 operator for tags: "="
	if strings.HasPrefix(statement, "Tags") || strings.HasPrefix(statement, "tags") {
		operator := ""

		if strings.Contains(statement, "=") {
			operator = "="
		}

		if operator != "" {
			parts := strings.Split(statement, operator)

			// Remove tags prefix
			parts[0] = strings.Replace(parts[0], "tags.", "", -1)

			pgJsonPath := strings.Replace(parts[0], ".", ",", -1)
			pgJsonPath = strings.TrimSpace(pgJsonPath)

			value := parts[len(parts)-1]
			value = strings.TrimSpace(value)
			value = libstring.StripChars(value, `"'`)

			return fmt.Sprintf("(tags #>> '{%v}' %v '%v' OR master_tags #>> '{%v}' %v '%v')", pgJsonPath, operator, value, pgJsonPath, operator, value)
		}
	}

	// Querying hostname.
	// Operators:
	// "="   : Exact match.
	// "!~*" : Does not match regular expression, case insensitive.
	// "!~"  : Does not match regular expression, case sensitive.
	// "~*"  : Matches regular expression, case insensitive.
	// "~^"  : Starts with, case sensitive.
	// "~"   : Matches regular expression, case sensitive.
	if strings.HasPrefix(statement, "Hostname") || strings.HasPrefix(statement, "hostname") {
		if strings.Contains(statement, "=") {
			return parseStringField(statement, "hostname", "=")

		} else if strings.Contains(statement, "!~*") {
			return parseStringField(statement, "hostname", "!~*")

		} else if strings.Contains(statement, "!~") {
			return parseStringField(statement, "hostname", "!~")

		} else if strings.Contains(statement, "~*") {
			return parseStringField(statement, "hostname", "~*")

		} else if strings.Contains(statement, "~^") {
			parts := strings.Split(statement, "~^")

			hostname := parts[len(parts)-1]
			hostname = strings.TrimSpace(hostname)
			hostname = libstring.StripChars(hostname, `"'`)

			return `hostname LIKE '` + hostname + `%'`

		} else if strings.Contains(statement, "~") {
			return parseStringField(statement, "hostname", "~")
		}
	}

	// Querying filename.
	// Operators:
	// "="   : Exact match.
	// "!~*" : Does not match regular expression, case insensitive.
	// "!~"  : Does not match regular expression, case sensitive.
	// "~*"  : Matches regular expression, case insensitive.
	// "~^"  : Starts with, case sensitive.
	// "~"   : Matches regular expression, case sensitive.
	if strings.HasPrefix(statement, "Filename") || strings.HasPrefix(statement, "filename") {
		if strings.Contains(statement, "=") {
			return parseStringField(statement, "filename", "=")

		} else if strings.Contains(statement, "!~*") {
			return parseStringField(statement, "filename", "!~*")

		} else if strings.Contains(statement, "!~") {
			return parseStringField(statement, "filename", "!~")

		} else if strings.Contains(statement, "~*") {
			return parseStringField(statement, "filename", "~*")

		} else if strings.Contains(statement, "~^") {
			parts := strings.Split(statement, "~^")

			filename := parts[len(parts)-1]
			filename = strings.TrimSpace(filename)
			filename = libstring.StripChars(filename, `"'`)

			return `filename LIKE '` + filename + `%'`

		} else if strings.Contains(statement, "~") {
			return parseStringField(statement, "filename", "~")
		}
	}

	// Querying data.
	// Operators: >=, <=, =, <, >
	// Expected output: data #>> '{/free,Swap.Free}' = '0'
	if strings.HasPrefix(statement, "/") {
		operator := ""

		if strings.Contains(statement, ">=") {
			operator = ">="
		} else if strings.Contains(statement, "<=") {
			operator = "<="
		} else if strings.Contains(statement, "=") {
			operator = "="
		} else if strings.Contains(statement, "<") {
			operator = ">"
		} else if strings.Contains(statement, ">") {
			operator = ">"
		}

		if operator != "" {
			parts := strings.Split(statement, operator)

			// On metric key, replace only the first dot because we flatten every metric key stored in hosts JSON data.
			pgJsonPath := strings.Replace(parts[0], ".", ",", 1)
			pgJsonPath = strings.TrimSpace(pgJsonPath)

			value := parts[len(parts)-1]
			value = strings.TrimSpace(value)
			value = libstring.StripChars(value, `"'`)

			return fmt.Sprintf("data #>> '{%v}' %v '%v'", pgJsonPath, operator, value)
		}
	}

	// Querying logline.
	// Operators:
	// "search" : Full text search.
	if strings.HasPrefix(statement, "Logline") || strings.HasPrefix(statement, "logline") {
		if strings.Contains(statement, "search") {
			return parseFullTextSearchField(statement, "logline", "search")
		}
	}

	return ""
}
<commit_msg>Fixed floating point arithmetic comparison when fetching hosts data.<commit_after>// Package querybuilder parses ResourceD query and turn it into PostgreSQL query.
package querybuilder

import (
	"fmt"
	"strings"

	"github.com/resourced/resourced-master/libstring"
)

// Parse parses ResourceD query and turns it into postgres query.
func Parse(input string) string {
	return parseAnd(input)
}

// parseAnd parses and conjunctive operator.
func parseAnd(input string) string {
	pgQueryParts := make([]string, 0)

	// normalize variation of AND
	input = strings.Replace(input, " AND ", " and ", -1)

	statements := strings.Split(input, " and ")
	for _, statement := range statements {
		pgStatement := parseStatement(statement)
		if pgStatement != "" {
			pgQueryParts = append(pgQueryParts, pgStatement)
		}
	}

	if len(pgQueryParts) > 1 {
		return strings.Join(pgQueryParts, " and ")
	} else if len(pgQueryParts) == 1 {
		return pgQueryParts[0]
	}

	return ""
}

// parseStringField parses ResourceD "string" query and turns it into postgres statement.
func parseStringField(statement, field, operator string) string {
	parts := strings.Split(statement, operator)

	data := parts[len(parts)-1]
	data = strings.TrimSpace(data)
	data = libstring.StripChars(data, `"'`)

	return fmt.Sprintf("%v %v '%v'", field, operator, data)
}

// parseFullTextSearchField parses ResourceD "search" query and turns it into postgres statement.
// Operator is "search" in this context.
func parseFullTextSearchField(statement, field, operator string) string {
	parts := strings.Split(statement, operator)

	searchQuery := parts[len(parts)-1]
	searchQuery = strings.TrimSpace(searchQuery)
	searchQuery = libstring.StripChars(searchQuery, `"'`)

	// Split search query into multiple soon-to-be-tsquery-function-argument (+ boolean operators)
	searchQueryChunks := strings.Fields(searchQuery)

	tsQueryChunksWithBoolOperators := make([]string, 0)
	tsQueryChunk := ""

	for i, chunk := range searchQueryChunks {
		// If chunk is not boolean operator, keep on appending to the same chunk.
		if chunk != "||" && chunk != "&&" {
			tsQueryChunk = tsQueryChunk + " " + chunk

		} else {
			tsQueryChunksWithBoolOperators = append(tsQueryChunksWithBoolOperators, strings.TrimSpace(tsQueryChunk))
			tsQueryChunk = ""

			// append boolean operator as well
			tsQueryChunksWithBoolOperators = append(tsQueryChunksWithBoolOperators, strings.TrimSpace(chunk))
		}

		// If reached the end
		if i == len(searchQueryChunks)-1 {
			if strings.TrimSpace(tsQueryChunk) != "" {
				tsQueryChunksWithBoolOperators = append(tsQueryChunksWithBoolOperators, strings.TrimSpace(tsQueryChunk))
			}
		}
	}

	// Now, let's build series of tsquery functions
	tsQuerySlice := make([]string, 0)

	for _, chunk := range tsQueryChunksWithBoolOperators {
		if chunk == "||" || chunk == "&&" {
			tsQuerySlice = append(tsQuerySlice, chunk)

		} else if strings.Contains(chunk, "|") || strings.Contains(chunk, "&") {
			tsQuerySlice = append(tsQuerySlice, fmt.Sprintf("to_tsquery('english', '%v')", chunk))

		} else {
			tsQuerySlice = append(tsQuerySlice, fmt.Sprintf("plainto_tsquery('english', '%v')", chunk))
		}
	}

	tsQueries := strings.Join(tsQuerySlice, " ")

	return fmt.Sprintf(`to_tsvector('english', regexp_replace(%v, '[^\w]+', ' ', 'gi')) || to_tsvector('english', %v) @@ (%v)`, field, field, tsQueries)
}

// parseStatement parses ResourceD statement and turns it into postgres statement.
func parseStatement(statement string) string {
	statement = strings.TrimSpace(statement)

	// Querying tags.
	// There can only be 1 operator for tags: "="
	if strings.HasPrefix(statement, "Tags") || strings.HasPrefix(statement, "tags") {
		operator := ""

		if strings.Contains(statement, "=") {
			operator = "="
		}

		if operator != "" {
			parts := strings.Split(statement, operator)

			// Remove tags prefix
			parts[0] = strings.Replace(parts[0], "tags.", "", -1)

			pgJsonPath := strings.Replace(parts[0], ".", ",", -1)
			pgJsonPath = strings.TrimSpace(pgJsonPath)

			value := parts[len(parts)-1]
			value = strings.TrimSpace(value)
			value = libstring.StripChars(value, `"'`)

			return fmt.Sprintf("(tags #>> '{%v}' %v '%v' OR master_tags #>> '{%v}' %v '%v')", pgJsonPath, operator, value, pgJsonPath, operator, value)
		}
	}

	// Querying hostname.
	// Operators:
	// "="   : Exact match.
	// "!~*" : Does not match regular expression, case insensitive.
	// "!~"  : Does not match regular expression, case sensitive.
	// "~*"  : Matches regular expression, case insensitive.
	// "~^"  : Starts with, case sensitive.
	// "~"   : Matches regular expression, case sensitive.
	if strings.HasPrefix(statement, "Hostname") || strings.HasPrefix(statement, "hostname") {
		if strings.Contains(statement, "=") {
			return parseStringField(statement, "hostname", "=")

		} else if strings.Contains(statement, "!~*") {
			return parseStringField(statement, "hostname", "!~*")

		} else if strings.Contains(statement, "!~") {
			return parseStringField(statement, "hostname", "!~")

		} else if strings.Contains(statement, "~*") {
			return parseStringField(statement, "hostname", "~*")

		} else if strings.Contains(statement, "~^") {
			parts := strings.Split(statement, "~^")

			hostname := parts[len(parts)-1]
			hostname = strings.TrimSpace(hostname)
			hostname = libstring.StripChars(hostname, `"'`)

			return `hostname LIKE '` + hostname + `%'`

		} else if strings.Contains(statement, "~") {
			return parseStringField(statement, "hostname", "~")
		}
	}

	// Querying filename.
	// Operators:
	// "="   : Exact match.
	// "!~*" : Does not match regular expression, case insensitive.
	// "!~"  : Does not match regular expression, case sensitive.
	// "~*"  : Matches regular expression, case insensitive.
	// "~^"  : Starts with, case sensitive.
	// "~"   : Matches regular expression, case sensitive.
	if strings.HasPrefix(statement, "Filename") || strings.HasPrefix(statement, "filename") {
		if strings.Contains(statement, "=") {
			return parseStringField(statement, "filename", "=")

		} else if strings.Contains(statement, "!~*") {
			return parseStringField(statement, "filename", "!~*")

		} else if strings.Contains(statement, "!~") {
			return parseStringField(statement, "filename", "!~")

		} else if strings.Contains(statement, "~*") {
			return parseStringField(statement, "filename", "~*")

		} else if strings.Contains(statement, "~^") {
			parts := strings.Split(statement, "~^")

			filename := parts[len(parts)-1]
			filename = strings.TrimSpace(filename)
			filename = libstring.StripChars(filename, `"'`)

			return `filename LIKE '` + filename + `%'`

		} else if strings.Contains(statement, "~") {
			return parseStringField(statement, "filename", "~")
		}
	}

	// Querying data.
	// Operators for floating point data: >=, <=, =, <, >
	//     Expected output: (data #>> '{/free,Memory.TotalGB}')::float8
	// Operators for string data:
	//     "!~*" : Does not match regular expression, case insensitive.
	//     "!~"  : Does not match regular expression, case sensitive.
	//     "~*"  : Matches regular expression, case insensitive.
	//     "~^"  : Starts with, case sensitive.
	//     "~"   : Matches regular expression, case sensitive.
	if strings.HasPrefix(statement, "/") {
		operator := ""

		for _, op := range []string{">=", "<=", "=", "<", ">", "!~*", "!~", "~*", "~^", "~"} {
			if strings.Contains(statement, op) {
				operator = op
			}
		}

		if operator != "" {
			parts := strings.Split(statement, operator)

			// On metric key, replace only the first dot because we flatten every metric key stored in hosts JSON data.
			pgJsonPath := strings.Replace(parts[0], ".", ",", 1)
			pgJsonPath = strings.TrimSpace(pgJsonPath)

			value := parts[len(parts)-1]
			value = strings.TrimSpace(value)
			value = libstring.StripChars(value, `"'`)

			// The following operators should be able to handle string data types.
			if operator == "=" || operator == "!~*" || operator == "!~" || operator == "~*" || operator == "~^" || operator == "~" {
				return fmt.Sprintf("data #>> '{%v}' %v '%v'", pgJsonPath, operator, value)
			}

			// Example: (data #>> '{/free,Memory.TotalGB}')::float8
			return fmt.Sprintf("(data #>> '{%v}')::float8 %v %v", pgJsonPath, operator, value)
		}
	}

	// Querying logline.
	// Operators:
	// "search" : Full text search.
	if strings.HasPrefix(statement, "Logline") || strings.HasPrefix(statement, "logline") {
		if strings.Contains(statement, "search") {
			return parseFullTextSearchField(statement, "logline", "search")
		}
	}

	return ""
}
<|endoftext|>


<commit_before>// Copyright 2014 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package ra

import (
	"crypto/x509"
	"fmt"
	"math/big"
	"net/url"
	"regexp"
	"strconv"
	"time"

	"github.com/letsencrypt/boulder/core"
	blog "github.com/letsencrypt/boulder/log"
	"github.com/letsencrypt/boulder/policy"
)

// All of the fields in RegistrationAuthorityImpl need to be
// populated, or there is a risk of panic.
type RegistrationAuthorityImpl struct {
	CA  core.CertificateAuthority
	VA  core.ValidationAuthority
	SA  core.StorageAuthority
	PA  core.PolicyAuthority
	log *blog.AuditLogger

	AuthzBase string
}

func NewRegistrationAuthorityImpl() RegistrationAuthorityImpl {
	logger := blog.GetAuditLogger()
	logger.Notice("Registration Authority Starting")

	ra := RegistrationAuthorityImpl{log: logger}
	ra.PA = policy.NewPolicyAuthorityImpl()
	return ra
}

var allButLastPathSegment = regexp.MustCompile("^.*/")

func lastPathSegment(url core.AcmeURL) string {
	return allButLastPathSegment.ReplaceAllString(url.Path, "")
}

type certificateRequestEvent struct {
	ID                  string    `json:",omitempty"`
	Requester           int64     `json:",omitempty"`
	SerialNumber        *big.Int  `json:",omitempty"`
	RequestMethod       string    `json:",omitempty"`
	VerificationMethods []string  `json:",omitempty"`
	VerifiedFields      []string  `json:",omitempty"`
	CommonName          string    `json:",omitempty"`
	Names               []string  `json:",omitempty"`
	NotBefore           time.Time `json:",omitempty"`
	NotAfter            time.Time `json:",omitempty"`
	RequestTime         time.Time `json:",omitempty"`
	ResponseTime        time.Time `json:",omitempty"`
	Error               string    `json:",omitempty"`
}

func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration) (reg core.Registration, err error) {
	if !core.GoodKey(init.Key.Key) {
		return core.Registration{}, core.UnauthorizedError("Invalid public key.")
	}
	reg = core.Registration{
		RecoveryToken: core.NewToken(),
		Key:           init.Key,
	}
	reg.MergeUpdate(init)

	// Store the authorization object, then return it
	reg, err = ra.SA.NewRegistration(reg)
	if err != nil {
		err = core.InternalServerError(err.Error())
	}
	return
}

func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization, regID int64) (authz core.Authorization, err error) {
	if regID <= 0 {
		err = core.InternalServerError("Invalid registration ID")
		return authz, err
	}

	identifier := request.Identifier

	// Check that the identifier is present and appropriate
	if err = ra.PA.WillingToIssue(identifier); err != nil {
		err = core.UnauthorizedError(err.Error())
		return authz, err
	}

	// Create validations, but we have to update them with URIs later
	challenges, combinations := ra.PA.ChallengesFor(identifier)

	// Partially-filled object
	authz = core.Authorization{
		Identifier:     identifier,
		RegistrationID: regID,
		Status:         core.StatusPending,
		Combinations:   combinations,
	}

	// Get a pending Auth first so we can get our ID back, then update with challenges
	authz, err = ra.SA.NewPendingAuthorization(authz)
	if err != nil {
		err = core.InternalServerError(err.Error())
		return authz, err
	}

	// Construct all the challenge URIs
	for i := range challenges {
		// Ignoring these errors because we construct the URLs to be correct
		challengeURI, _ := url.Parse(ra.AuthzBase + authz.ID + "?challenge=" + strconv.Itoa(i))
		challenges[i].URI = core.AcmeURL(*challengeURI)

		if !challenges[i].IsSane(false) {
			err = core.InternalServerError(fmt.Sprintf("Challenge didn't pass sanity check: %+v", challenges[i]))
			return authz, err
		}
	}

	// Update object
	authz.Challenges = challenges

	// Store the authorization object, then return it
	err = ra.SA.UpdatePendingAuthorization(authz)
	if err != nil {
		err = core.InternalServerError(err.Error())
	}
	return authz, err
}

func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, regID int64) (cert core.Certificate, err error) {
	emptyCert := core.Certificate{}
	var logEventResult string

	// Assume the worst
	logEventResult = "error"

	// Construct the log event
	logEvent := certificateRequestEvent{
		ID:            core.NewToken(),
		Requester:     regID,
		RequestMethod: "online",
		RequestTime:   time.Now(),
	}

	// No matter what, log the request
	defer func() {
		// AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c
		ra.log.AuditObject(fmt.Sprintf("Certificate request - %s", logEventResult), logEvent)
	}()

	if regID <= 0 {
		err = core.InternalServerError("Invalid registration ID")
		return emptyCert, err
	}

	registration, err := ra.SA.GetRegistration(regID)
	if err != nil {
		logEvent.Error = err.Error()
		return emptyCert, err
	}

	registration, err := ra.SA.GetRegistration(regID)
	if err != nil {
		logEvent.Error = err.Error()
		return emptyCert, err
	}

	// Verify the CSR
	csr := req.CSR
	if err = core.VerifyCSR(csr); err != nil {
		logEvent.Error = err.Error()
		err = core.UnauthorizedError("Invalid signature on CSR")
		return emptyCert, err
	}

	logEvent.CommonName = csr.Subject.CommonName
	logEvent.Names = csr.DNSNames

	// Validate that authorization key is authorized for all domains
	names := make([]string, len(csr.DNSNames))
	copy(names, csr.DNSNames)
	if len(csr.Subject.CommonName) > 0 {
		names = append(names, csr.Subject.CommonName)
	}

	if len(names) == 0 {
		err = core.UnauthorizedError("CSR has no names in it")
		logEvent.Error = err.Error()
		return emptyCert, err
	}

	csrPreviousDenied, err := ra.SA.AlreadyDeniedCSR(names)
	if err != nil {
		logEvent.Error = err.Error()
		return emptyCert, err
	}
	if csrPreviousDenied {
		err = core.UnauthorizedError("CSR has already been revoked/denied")
		logEvent.Error = err.Error()
		return emptyCert, err
	}

	if core.KeyDigestEquals(csr.PublicKey, registration.Key) {
		err = core.MalformedRequestError("Certificate public key must be different than account key")
		return emptyCert, err
	}

	// Gather authorized domains from the referenced authorizations
	authorizedDomains := map[string]bool{}
	verificationMethodSet := map[string]bool{}
	earliestExpiry := time.Date(2100, 01, 01, 0, 0, 0, 0, time.UTC)
	now := time.Now()
	for _, url := range req.Authorizations {
		id := lastPathSegment(url)
		authz, err := ra.SA.GetAuthorization(id)
		if err != nil || // Couldn't find authorization
			authz.RegistrationID != registration.ID || // Not for this account
			authz.Status != core.StatusValid || // Not finalized or not successful
			authz.Expires.Before(now) || // Expired
			authz.Identifier.Type != core.IdentifierDNS {
			// XXX: It may be good to fail here instead of ignoring invalid authorizations.
			//      However, it seems like this treatment is more in the spirit of Postel's
			//      law, and it hides information from attackers.
			continue
		}

		if authz.Expires.Before(earliestExpiry) {
			earliestExpiry = authz.Expires
		}

		for _, challenge := range authz.Challenges {
			if challenge.Status == core.StatusValid {
				verificationMethodSet[challenge.Type] = true
			}
		}

		authorizedDomains[authz.Identifier.Value] = true
	}
	verificationMethods := []string{}
	for method, _ := range verificationMethodSet {
		verificationMethods = append(verificationMethods, method)
	}
	logEvent.VerificationMethods = verificationMethods

	// Validate all domains
	for _, name := range names {
		if !authorizedDomains[name] {
			err = core.UnauthorizedError(fmt.Sprintf("Key not authorized for name %s", name))
			logEvent.Error = err.Error()
			return emptyCert, err
		}
	}

	// Mark that we verified the CN and SANs
	logEvent.VerifiedFields = []string{"subject.commonName", "subjectAltName"}

	// Create the certificate and log the result
	if cert, err = ra.CA.IssueCertificate(*csr, regID, earliestExpiry); err != nil {
		err = core.InternalServerError(err.Error())
		logEvent.Error = err.Error()
		return emptyCert, err
	}

	parsedCertificate, err := x509.ParseCertificate([]byte(cert.DER))
	if err != nil {
		err = core.InternalServerError(err.Error())
		logEvent.Error = err.Error()
		return emptyCert, err
	}

	logEvent.SerialNumber = parsedCertificate.SerialNumber
	logEvent.CommonName = parsedCertificate.Subject.CommonName
	logEvent.NotBefore = parsedCertificate.NotBefore
	logEvent.NotAfter = parsedCertificate.NotAfter
	logEvent.ResponseTime = time.Now()

	logEventResult = "successful"
	return cert, nil
}

func (ra *RegistrationAuthorityImpl) UpdateRegistration(base core.Registration, update core.Registration) (reg core.Registration, err error) {
	base.MergeUpdate(update)
	reg = base
	err = ra.SA.UpdateRegistration(base)
	if err != nil {
		err = core.InternalServerError(err.Error())
	}
	return
}

func (ra *RegistrationAuthorityImpl) UpdateAuthorization(base core.Authorization, challengeIndex int, response core.Challenge) (authz core.Authorization, err error) {
	// Copy information over that the client is allowed to supply
	authz = base
	if challengeIndex >= len(authz.Challenges) {
		err = core.MalformedRequestError("Invalid challenge index")
		return
	}
	authz.Challenges[challengeIndex] = authz.Challenges[challengeIndex].MergeResponse(response)

	// Store the updated version
	if err = ra.SA.UpdatePendingAuthorization(authz); err != nil {
		err = core.InternalServerError(err.Error())
		return
	}

	// Dispatch to the VA for service
	ra.VA.UpdateValidations(authz, challengeIndex)

	return
}

func (ra *RegistrationAuthorityImpl) RevokeCertificate(cert x509.Certificate) (err error) {
	serialString := core.SerialToString(cert.SerialNumber)
	err = ra.CA.RevokeCertificate(serialString, 0)

	// AUDIT[ Revocation Requests ] 4e85d791-09c0-4ab3-a837-d3d67e945134
	if err != nil {
		ra.log.Audit(fmt.Sprintf("Revocation error - %s - %s", serialString, err))
		return err
	}

	ra.log.Audit(fmt.Sprintf("Revocation - %s", serialString))
	return err
}

func (ra *RegistrationAuthorityImpl) OnValidationUpdate(authz core.Authorization) error {
	// Consider validation successful if any of the combinations
	// specified in the authorization has been fulfilled
	validated := map[int]bool{}
	for i, ch := range authz.Challenges {
		if ch.Status == core.StatusValid {
			validated[i] = true
		}
	}
	for _, combo := range authz.Combinations {
		comboValid := true
		for _, i := range combo {
			if !validated[i] {
				comboValid = false
				break
			}
		}
		if comboValid {
			authz.Status = core.StatusValid
		}
	}

	// If no validation succeeded, then the authorization is invalid
	// NOTE: This only works because we only ever do one validation
	if authz.Status != core.StatusValid {
		authz.Status = core.StatusInvalid
	} else {
		// TODO: Enable configuration of expiry time
		authz.Expires = time.Now().Add(365 * 24 * time.Hour)
	}

	// Finalize the authorization (error ignored)
	return ra.SA.FinalizeAuthorization(authz)
}
<commit_msg>Fixing build error induced in rebase<commit_after>// Copyright 2014 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package ra

import (
	"crypto/x509"
	"fmt"
	"math/big"
	"net/url"
	"regexp"
	"strconv"
	"time"

	"github.com/letsencrypt/boulder/core"
	blog "github.com/letsencrypt/boulder/log"
	"github.com/letsencrypt/boulder/policy"
)

// All of the fields in RegistrationAuthorityImpl need to be
// populated, or there is a risk of panic.
type RegistrationAuthorityImpl struct {
	CA  core.CertificateAuthority
	VA  core.ValidationAuthority
	SA  core.StorageAuthority
	PA  core.PolicyAuthority
	log *blog.AuditLogger

	AuthzBase string
}

func NewRegistrationAuthorityImpl() RegistrationAuthorityImpl {
	logger := blog.GetAuditLogger()
	logger.Notice("Registration Authority Starting")

	ra := RegistrationAuthorityImpl{log: logger}
	ra.PA = policy.NewPolicyAuthorityImpl()
	return ra
}

var allButLastPathSegment = regexp.MustCompile("^.*/")

func lastPathSegment(url core.AcmeURL) string {
	return allButLastPathSegment.ReplaceAllString(url.Path, "")
}

type certificateRequestEvent struct {
	ID                  string    `json:",omitempty"`
	Requester           int64     `json:",omitempty"`
	SerialNumber        *big.Int  `json:",omitempty"`
	RequestMethod       string    `json:",omitempty"`
	VerificationMethods []string  `json:",omitempty"`
	VerifiedFields      []string  `json:",omitempty"`
	CommonName          string    `json:",omitempty"`
	Names               []string  `json:",omitempty"`
	NotBefore           time.Time `json:",omitempty"`
	NotAfter            time.Time `json:",omitempty"`
	RequestTime         time.Time `json:",omitempty"`
	ResponseTime        time.Time `json:",omitempty"`
	Error               string    `json:",omitempty"`
}

func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration) (reg core.Registration, err error) {
	if !core.GoodKey(init.Key.Key) {
		return core.Registration{}, core.UnauthorizedError("Invalid public key.")
	}
	reg = core.Registration{
		RecoveryToken: core.NewToken(),
		Key:           init.Key,
	}
	reg.MergeUpdate(init)

	// Store the authorization object, then return it
	reg, err = ra.SA.NewRegistration(reg)
	if err != nil {
		err = core.InternalServerError(err.Error())
	}
	return
}

func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization, regID int64) (authz core.Authorization, err error) {
	if regID <= 0 {
		err = core.InternalServerError("Invalid registration ID")
		return authz, err
	}

	identifier := request.Identifier

	// Check that the identifier is present and appropriate
	if err = ra.PA.WillingToIssue(identifier); err != nil {
		err = core.UnauthorizedError(err.Error())
		return authz, err
	}

	// Create validations, but we have to update them with URIs later
	challenges, combinations := ra.PA.ChallengesFor(identifier)

	// Partially-filled object
	authz = core.Authorization{
		Identifier:     identifier,
		RegistrationID: regID,
		Status:         core.StatusPending,
		Combinations:   combinations,
	}

	// Get a pending Auth first so we can get our ID back, then update with challenges
	authz, err = ra.SA.NewPendingAuthorization(authz)
	if err != nil {
		err = core.InternalServerError(err.Error())
		return authz, err
	}

	// Construct all the challenge URIs
	for i := range challenges {
		// Ignoring these errors because we construct the URLs to be correct
		challengeURI, _ := url.Parse(ra.AuthzBase + authz.ID + "?challenge=" + strconv.Itoa(i))
		challenges[i].URI = core.AcmeURL(*challengeURI)

		if !challenges[i].IsSane(false) {
			err = core.InternalServerError(fmt.Sprintf("Challenge didn't pass sanity check: %+v", challenges[i]))
			return authz, err
		}
	}

	// Update object
	authz.Challenges = challenges

	// Store the authorization object, then return it
	err = ra.SA.UpdatePendingAuthorization(authz)
	if err != nil {
		err = core.InternalServerError(err.Error())
	}
	return authz, err
}

func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, regID int64) (cert core.Certificate, err error) {
	emptyCert := core.Certificate{}
	var logEventResult string

	// Assume the worst
	logEventResult = "error"

	// Construct the log event
	logEvent := certificateRequestEvent{
		ID:            core.NewToken(),
		Requester:     regID,
		RequestMethod: "online",
		RequestTime:   time.Now(),
	}

	// No matter what, log the request
	defer func() {
		// AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c
		ra.log.AuditObject(fmt.Sprintf("Certificate request - %s", logEventResult), logEvent)
	}()

	if regID <= 0 {
		err = core.InternalServerError("Invalid registration ID")
		return emptyCert, err
	}

	registration, err := ra.SA.GetRegistration(regID)
	if err != nil {
		logEvent.Error = err.Error()
		return emptyCert, err
	}

	// Verify the CSR
	csr := req.CSR
	if err = core.VerifyCSR(csr); err != nil {
		logEvent.Error = err.Error()
		err = core.UnauthorizedError("Invalid signature on CSR")
		return emptyCert, err
	}

	logEvent.CommonName = csr.Subject.CommonName
	logEvent.Names = csr.DNSNames

	// Validate that authorization key is authorized for all domains
	names := make([]string, len(csr.DNSNames))
	copy(names, csr.DNSNames)
	if len(csr.Subject.CommonName) > 0 {
		names = append(names, csr.Subject.CommonName)
	}

	if len(names) == 0 {
		err = core.UnauthorizedError("CSR has no names in it")
		logEvent.Error = err.Error()
		return emptyCert, err
	}

	csrPreviousDenied, err := ra.SA.AlreadyDeniedCSR(names)
	if err != nil {
		logEvent.Error = err.Error()
		return emptyCert, err
	}
	if csrPreviousDenied {
		err = core.UnauthorizedError("CSR has already been revoked/denied")
		logEvent.Error = err.Error()
		return emptyCert, err
	}

	if core.KeyDigestEquals(csr.PublicKey, registration.Key) {
		err = core.MalformedRequestError("Certificate public key must be different than account key")
		return emptyCert, err
	}

	// Gather authorized domains from the referenced authorizations
	authorizedDomains := map[string]bool{}
	verificationMethodSet := map[string]bool{}
	earliestExpiry := time.Date(2100, 01, 01, 0, 0, 0, 0, time.UTC)
	now := time.Now()
	for _, url := range req.Authorizations {
		id := lastPathSegment(url)
		authz, err := ra.SA.GetAuthorization(id)
		if err != nil || // Couldn't find authorization
			authz.RegistrationID != registration.ID || // Not for this account
			authz.Status != core.StatusValid || // Not finalized or not successful
			authz.Expires.Before(now) || // Expired
			authz.Identifier.Type != core.IdentifierDNS {
			// XXX: It may be good to fail here instead of ignoring invalid authorizations.
			//      However, it seems like this treatment is more in the spirit of Postel's
			//      law, and it hides information from attackers.
			continue
		}

		if authz.Expires.Before(earliestExpiry) {
			earliestExpiry = authz.Expires
		}

		for _, challenge := range authz.Challenges {
			if challenge.Status == core.StatusValid {
				verificationMethodSet[challenge.Type] = true
			}
		}

		authorizedDomains[authz.Identifier.Value] = true
	}
	verificationMethods := []string{}
	for method, _ := range verificationMethodSet {
		verificationMethods = append(verificationMethods, method)
	}
	logEvent.VerificationMethods = verificationMethods

	// Validate all domains
	for _, name := range names {
		if !authorizedDomains[name] {
			err = core.UnauthorizedError(fmt.Sprintf("Key not authorized for name %s", name))
			logEvent.Error = err.Error()
			return emptyCert, err
		}
	}

	// Mark that we verified the CN and SANs
	logEvent.VerifiedFields = []string{"subject.commonName", "subjectAltName"}

	// Create the certificate and log the result
	if cert, err = ra.CA.IssueCertificate(*csr, regID, earliestExpiry); err != nil {
		err = core.InternalServerError(err.Error())
		logEvent.Error = err.Error()
		return emptyCert, err
	}

	parsedCertificate, err := x509.ParseCertificate([]byte(cert.DER))
	if err != nil {
		err = core.InternalServerError(err.Error())
		logEvent.Error = err.Error()
		return emptyCert, err
	}

	logEvent.SerialNumber = parsedCertificate.SerialNumber
	logEvent.CommonName = parsedCertificate.Subject.CommonName
	logEvent.NotBefore = parsedCertificate.NotBefore
	logEvent.NotAfter = parsedCertificate.NotAfter
	logEvent.ResponseTime = time.Now()

	logEventResult = "successful"
	return cert, nil
}

func (ra *RegistrationAuthorityImpl) UpdateRegistration(base core.Registration, update core.Registration) (reg core.Registration, err error) {
	base.MergeUpdate(update)
	reg = base
	err = ra.SA.UpdateRegistration(base)
	if err != nil {
		err = core.InternalServerError(err.Error())
	}
	return
}

func (ra *RegistrationAuthorityImpl) UpdateAuthorization(base core.Authorization, challengeIndex int, response core.Challenge) (authz core.Authorization, err error) {
	// Copy information over that the client is allowed to supply
	authz = base
	if challengeIndex >= len(authz.Challenges) {
		err = core.MalformedRequestError("Invalid challenge index")
		return
	}
	authz.Challenges[challengeIndex] = authz.Challenges[challengeIndex].MergeResponse(response)

	// Store the updated version
	if err = ra.SA.UpdatePendingAuthorization(authz); err != nil {
		err = core.InternalServerError(err.Error())
		return
	}

	// Dispatch to the VA for service
	ra.VA.UpdateValidations(authz, challengeIndex)

	return
}

func (ra *RegistrationAuthorityImpl) RevokeCertificate(cert x509.Certificate) (err error) {
	serialString := core.SerialToString(cert.SerialNumber)
	err = ra.CA.RevokeCertificate(serialString, 0)

	// AUDIT[ Revocation Requests ] 4e85d791-09c0-4ab3-a837-d3d67e945134
	if err != nil {
		ra.log.Audit(fmt.Sprintf("Revocation error - %s - %s", serialString, err))
		return err
	}

	ra.log.Audit(fmt.Sprintf("Revocation - %s", serialString))
	return err
}

func (ra *RegistrationAuthorityImpl) OnValidationUpdate(authz core.Authorization) error {
	// Consider validation successful if any of the combinations
	// specified in the authorization has been fulfilled
	validated := map[int]bool{}
	for i, ch := range authz.Challenges {
		if ch.Status == core.StatusValid {
			validated[i] = true
		}
	}
	for _, combo := range authz.Combinations {
		comboValid := true
		for _, i := range combo {
			if !validated[i] {
				comboValid = false
				break
			}
		}
		if comboValid {
			authz.Status = core.StatusValid
		}
	}

	// If no validation succeeded, then the authorization is invalid
	// NOTE: This only works because we only ever do one validation
	if authz.Status != core.StatusValid {
		authz.Status = core.StatusInvalid
	} else {
		// TODO: Enable configuration of expiry time
		authz.Expires = time.Now().Add(365 * 24 * time.Hour)
	}

	// Finalize the authorization (error ignored)
	return ra.SA.FinalizeAuthorization(authz)
}
<|endoftext|>


<commit_before>package acceptance_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"

	"testing"
)

func TestVxlan(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Vxlan Acceptance Suite")
}

var pathToVxlan, cniPath string

type paths struct {
	VXLAN    string `json:"vxlan"`
	CNI      string `json:"cni"`
	FAKEIPAM string `json:"fake_ipam"`
}

var _ = SynchronizedBeforeSuite(
	func() []byte {
		if runtime.GOOS != "linux" {
			Skip("Cannot run suite for non linux platform: " + runtime.GOOS)
		}

		cniDir := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "appc", "cni")
		cmd := exec.Command("/bin/sh", filepath.Join(cniDir, "build"))
		cmd.Dir = cniDir
		err := cmd.Run()
		Expect(err).NotTo(HaveOccurred())

		vxlan, err := gexec.Build("github.com/cloudfoundry-incubator/ducati-cni-plugins/cmd/vxlan", "-race")
		Expect(err).NotTo(HaveOccurred())

		fakeIpam, err := gexec.Build("github.com/cloudfoundry-incubator/ducati-cni-plugins/fake_plugins", "-race")
		Expect(err).NotTo(HaveOccurred())

		result, err := json.Marshal(paths{
			VXLAN:    vxlan,
			CNI:      cniDir,
			FAKEIPAM: fakeIpam,
		})
		Expect(err).NotTo(HaveOccurred())

		return result
	},
	func(result []byte) {
		var paths paths
		err := json.Unmarshal(result, &paths)
		Expect(err).NotTo(HaveOccurred())

		cniBinDir := filepath.Join(paths.CNI, "bin")
		vxlanBinDir := filepath.Dir(paths.VXLAN)
		fakeIpamDir := filepath.Dir(paths.FAKEIPAM)

		cniPath = fmt.Sprintf("%s%c%s%c%s", vxlanBinDir, os.PathListSeparator, cniBinDir, os.PathListSeparator, fakeIpamDir)
		pathToVxlan = paths.VXLAN
	},
)

var _ = SynchronizedAfterSuite(func() {
	return
}, func() {
	gexec.CleanupBuildArtifacts()
})

func buildCNICmdLight(
	operation string,
	netConfig Config,
	containerNSPath string,
	containerID string,
) (*exec.Cmd, error) {

	input, err := json.Marshal(netConfig)
	if err != nil {
		return nil, err
	}

	cmd := exec.Command(pathToVxlan)
	cmd.Stdin = bytes.NewReader(input)
	cmd.Env = append(
		os.Environ(),
		fmt.Sprintf("CNI_COMMAND=%s", operation),
		fmt.Sprintf("CNI_CONTAINERID=%s", containerID),
		fmt.Sprintf("CNI_PATH=%s", cniPath),
		fmt.Sprintf("CNI_NETNS=%s", containerNSPath),
		fmt.Sprintf("CNI_IFNAME=%s", "vx-eth0"),
	)

	return cmd, nil
}
<commit_msg>Remove legacy CNI plugin cruft<commit_after>package acceptance_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"

	"testing"
)

func TestVxlan(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Vxlan Acceptance Suite")
}

var pathToVxlan, cniPath string

type paths struct {
	VXLAN string `json:"vxlan"`
}

var _ = SynchronizedBeforeSuite(
	func() []byte {
		vxlan, err := gexec.Build("github.com/cloudfoundry-incubator/ducati-cni-plugins/cmd/vxlan", "-race")
		Expect(err).NotTo(HaveOccurred())

		result, err := json.Marshal(paths{
			VXLAN: vxlan,
		})
		Expect(err).NotTo(HaveOccurred())

		return result
	},
	func(result []byte) {
		var paths paths
		err := json.Unmarshal(result, &paths)
		Expect(err).NotTo(HaveOccurred())

		cniPath = filepath.Dir(paths.VXLAN)
		pathToVxlan = paths.VXLAN
	},
)

var _ = SynchronizedAfterSuite(func() {
	return
}, func() {
	gexec.CleanupBuildArtifacts()
})

func buildCNICmdLight(
	operation string,
	netConfig Config,
	containerNSPath string,
	containerID string,
) (*exec.Cmd, error) {

	input, err := json.Marshal(netConfig)
	if err != nil {
		return nil, err
	}

	cmd := exec.Command(pathToVxlan)
	cmd.Stdin = bytes.NewReader(input)
	cmd.Env = append(
		os.Environ(),
		fmt.Sprintf("CNI_COMMAND=%s", operation),
		fmt.Sprintf("CNI_CONTAINERID=%s", containerID),
		fmt.Sprintf("CNI_PATH=%s", cniPath),
		fmt.Sprintf("CNI_NETNS=%s", containerNSPath),
		fmt.Sprintf("CNI_IFNAME=%s", "vx-eth0"),
	)

	return cmd, nil
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 Matthew Dawson <matthew@mjdsystems.ca>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package main

import (
	"net/url"

	riak "github.com/tpjg/goriakpbc"

	"math/rand"
	"reflect"

	"strconv"
	"time"

	"testing"
	"testing/quick"
)

func makeIdGenerator() <-chan uint64 {
	ch := make(chan uint64)

	go func(ch chan<- uint64) {
		var id uint64
		for {
			ch <- id
			id++
		}
	}(ch)

	return ch
}

var testIdGenerator <-chan uint64 = makeIdGenerator()

func fixFeedForMerging(toFix *ParsedFeedData) *ParsedFeedData {
	// Instead of sorting, verify we are sorted.  Otherwise items without publication dates can end
	// up in the wrong place.
	if !toFix.Items.VerifySort() {
		panic("Items are not sorted!!!!")
	}

	for i, _ := range toFix.Items {
		item := &toFix.Items[i]
		item.GenericKey = makeHash(string(item.GenericKey) + key_uniquer)
	}
	return toFix
}

func compareParsedToFinalFeed(t *testing.T, data *ParsedFeedData, model *Feed, con *riak.Client) bool {
	// Compare basics:
	if data.Title != model.Title {
		t.Errorf("Feed title didn't match '%s' vs '%s'!", data.Title, model.Title)
		return false
	}

	if len(data.Items) != len(model.ItemKeys) {
		if len(data.Items) > MaximumFeedItems {
			t.Errorf("Item count differs due to items count greater then Maximum number of feed items (%v of %v)", len(data.Items), MaximumFeedItems)
		} else {
			t.Errorf("Item count is different %v vs %v!", len(data.Items), len(model.ItemKeys))
		}
		return false
	}
	if len(model.InsertedItemKeys) != 0 || len(model.DeletedItemKeys) != 0 {
		t.Error("There are left over inserted or deleted item keys!")
		return false
	}

	type FeedItemCh struct {
		item FeedItem
		ch   chan FeedItemCh
	}

	itemChOut := make(chan FeedItemCh)
	itemCh := make(chan FeedItem)

	go func(itemCh chan FeedItem, itemChOut chan FeedItemCh) {
		defer close(itemCh)
		for item, ok := <-itemChOut; ok; item, ok = <-itemChOut {
			itemCh <- item.item
			itemChOut = item.ch
		}
	}(itemCh, itemChOut)

	itemChIn := make(chan FeedItemCh)

	for _, itemKey := range model.ItemKeys {
		go func(itemKey ItemKey, itemChOut, itemChIn chan FeedItemCh) {
			defer close(itemChOut)

			modelItem := FeedItem{}
			if err := con.LoadModel(itemKey.GetRiakKey(), &modelItem, riak.R1); err != nil {
				t.Errorf("Failed to load item! Error: %s item %s", err, itemKey.GetRiakKey())
			}
			itemChOut <- FeedItemCh{modelItem, itemChIn}
		}(itemKey, itemChOut, itemChIn)
		itemChOut = itemChIn
		itemChIn = make(chan FeedItemCh)
	}

	//Compare saved feed items.  This means a trip through riak!  The order should match though ...
	for i, _ := range model.ItemKeys {
		var modelItem FeedItem
		select {
		case modelItem = <-itemCh:
		case <-time.After(time.Minute * 5):
			t.Fatalf("Failed to get an item before timeout, item %v", i)
		}

		dataItem := data.Items[i]

		if dataItem.Title != modelItem.Title ||
			dataItem.Author != modelItem.Author ||
			dataItem.Content != modelItem.Content ||
			dataItem.Url != modelItem.Url ||
			!dataItem.PubDate.Equal(modelItem.PubDate) {
			t.Errorf("Item data didn't match! Original:\n%#v\nLoaded:\n%#v", dataItem, modelItem)
			return false
		}
	}

	return true
}

func checkAllItemsDeleted(t *testing.T, feed *Feed, con *riak.Client) bool {
	ch := make(chan bool)
	for _, itemKey := range feed.ItemKeys {
		go func(itemKey ItemKey, ch chan<- bool) {
			modelItem := FeedItem{}
			if err := con.LoadModel(itemKey.GetRiakKey(), &modelItem, riak.R1); err == riak.NotFound {
				ch <- false
			} else {
				ch <- true
			}
		}(itemKey, ch)
	}

	problems := 0
	for _, _ = range feed.ItemKeys {
		found := <-ch
		if found {
			//t.Errorf("Found deleted item %s", itemKey.GetRiakKey())
			problems++
		}
	}
	return problems == 0
}

func CreateFeed(t *testing.T, con *riak.Client, Url *url.URL) *Feed {
	feedModel := &Feed{Url: *Url}
	if err := con.LoadModel(feedModel.UrlKey(), feedModel); err == nil && err != riak.NotFound {
		t.Fatalf("Failed to initialize feed model (%s)!", err)
	} else {
		modelElement := feedModel.Model
		*feedModel = Feed{Url: *Url}
		feedModel.Model = modelElement
		if err = feedModel.Save(); err != nil {
			t.Fatalf("Failed to store feed model (%s)!", err)
		}
	}
	return feedModel
}

func MustUpdateFeedTo(t *testing.T, con *riak.Client, url *url.URL, feedName string, updateCount int) (feed *ParsedFeedData) {
	for i := 0; i < updateCount; i++ {
		feed = getFeedDataFor(t, feedName, i)
		fixFeedForMerging(feed)

		if err := updateFeed(con, *url, *feed, testIdGenerator); err != nil {
			t.Fatalf("Failed to update simple single feed (%s)!", err)
		}
	}

	return
}

var urlKeyRand = rand.New(rand.NewSource(0))

func getUniqueExampleComUrl(t *testing.T) *url.URL {
	url, err := url.Parse("http://example.com/" + key_uniquer + strconv.Itoa(urlKeyRand.Int()) + "/rss")
	if err != nil {
		t.Fatalf("Failed to generate url (%s)", err)
	}
	return url
}

func TestSingleFeedInsert(t *testing.T) {
	con := getTestConnection(t)
	defer killTestDb(con, t)

	feed := getFeedDataFor(t, "simple", 0)
	fixFeedForMerging(feed)

	url := getUniqueExampleComUrl(t)

	err := updateFeed(con, *url, *feed, testIdGenerator)
	if err != FeedNotFound {
		t.Fatalf("Failed to insert simple single feed (%s)!", err)
	}

	feedModel := CreateFeed(t, con, url)

	err = updateFeed(con, *url, *feed, testIdGenerator)
	if err != nil {
		t.Errorf("Failed to update simple single feed (%s)!", err)
	}

	// Finally, load the feed again and verify properties!
	loadFeed := &Feed{}
	if err = con.LoadModel(feedModel.UrlKey(), loadFeed); err != nil {
		t.Fatalf("Failed to initialize feed model (%s)!", err)
	} else if compareParsedToFinalFeed(t, feed, loadFeed, con) != true {
		t.Errorf("Saved feed does not match what was inserted! Original:\n%+v\nLoaded:\n%+v", feed, loadFeed)
	}
}

func TestSingleFeedUpdate(t *testing.T) {
	con := getTestConnection(t)
	defer killTestDb(con, t)

	url := getUniqueExampleComUrl(t)

	feedModel := CreateFeed(t, con, url)

	feed := MustUpdateFeedTo(t, con, url, "simple", 2)

	// Finally, load the feed again and verify properties!
	loadFeed := &Feed{}
	if err := con.LoadModel(feedModel.UrlKey(), loadFeed); err != nil {
		t.Fatalf("Failed to initialize feed model (%s)!", err)
	} else if compareParsedToFinalFeed(t, feed, loadFeed, con) != true {
		t.Errorf("Saved feed does not match what was inserted! Original:\n%+v\nLoaded:\n%+v", feed, loadFeed)
	}
}

func TestFeedUpdateAndInsert(t *testing.T) {
	con := getTestConnection(t)
	defer killTestDb(con, t)

	url := getUniqueExampleComUrl(t)

	feedModel := CreateFeed(t, con, url)

	feed := MustUpdateFeedTo(t, con, url, "simple", 3)

	// Finally, load the feed again and verify properties!
	loadFeed := &Feed{}
	if err := con.LoadModel(feedModel.UrlKey(), loadFeed); err != nil {
		t.Fatalf("Failed to initialize feed model (%s)!", err)
	} else if compareParsedToFinalFeed(t, feed, loadFeed, con) != true {
		t.Errorf("Saved feed does not match what was inserted! Original:\n%+v\nLoaded:\n%+v", feed, loadFeed)
	}
}

func TestFeedUpdateWithChangingPubDates(t *testing.T) {
	con := getTestConnection(t)
	defer killTestDb(con, t)

	url := getUniqueExampleComUrl(t)

	feedModel := CreateFeed(t, con, url)

	feed := MustUpdateFeedTo(t, con, url, "simple", 4)

	// Finally, load the feed again and verify properties!
	loadFeed := &Feed{}
	if err := con.LoadModel(feedModel.UrlKey(), loadFeed); err != nil {
		t.Fatalf("Failed to initialize feed model (%s)!", err)
	} else if compareParsedToFinalFeed(t, feed, loadFeed, con) != true {
		t.Errorf("Saved feed does not match what was inserted! Original:\n%+v\nLoaded:\n%+v", feed, loadFeed)
	}
}

func GenerateParsedFeed(rand *rand.Rand) (out ParsedFeedData) {
	out = ParsedFeedData{}
	if titleOut, ok := quick.Value(reflect.TypeOf(out.Title), rand); ok {
		out.Title = titleOut.Interface().(string)
	} else {
		panic("Couldn't make a title!")
	}

	out.Items = make([]ParsedFeedItem, MaximumFeedItems+20)
	for i, _ := range out.Items {
		out.Items[i].GenericKey = makeHash(key_uniquer + strconv.Itoa(rand.Int()))
		out.Items[i].Title = strconv.Itoa(i)
	}

	return
}

func TestFeedDealingWithOverLargeFeed(t *testing.T) {
	con := getTestConnection(t)
	defer killTestDb(con, t)

	rand := rand.New(rand.NewSource(0))

	url := getUniqueExampleComUrl(t)
	feedModel := CreateFeed(t, con, url)

	t.Log("Test creating overlarge feed.")

	x := GenerateParsedFeed(rand)
	if err := updateFeed(con, *url, x, testIdGenerator); err != nil {
		t.Fatalf("Failed to update simple single feed (%s)!", err)
	}

	x.Items = x.Items[:MaximumFeedItems]

	origLoadFeed := &Feed{}
	if err := con.LoadModel(feedModel.UrlKey(), origLoadFeed); err != nil {
		t.Fatalf("Failed to initialize feed model (%s)!", err)
	} else if compareParsedToFinalFeed(t, &x, origLoadFeed, con) == false {
		t.Fatalf("Inserted data did not match original data (minus overage!)")
	}

	t.Log("Test completely replacing said feed with a new Oversized feed.")

	x = GenerateParsedFeed(rand) // This should generate all new items.
	if err := updateFeed(con, *url, x, testIdGenerator); err != nil {
		t.Fatalf("Failed to replace simple single feed (%s)!", err)
	}

	x.Items = x.Items[:MaximumFeedItems]

	newLoadFeed := &Feed{}
	if err := con.LoadModel(feedModel.UrlKey(), newLoadFeed); err != nil {
		t.Fatalf("Failed to initialize feed model (%s)!", err)
	} else if compareParsedToFinalFeed(t, &x, newLoadFeed, con) == false {
		t.Fatalf("Inserted data did not match original data (minus overage!)")
	} else if checkAllItemsDeleted(t, origLoadFeed, con) == false {
		t.Fatalf("There are left over deleted items!")
	}

	t.Log("Test adding a few new entries")

	newFeedData := getFeedDataFor(t, "simple", 0)
	fixFeedForMerging(newFeedData)
	fullItems := x.Items
	x.Items = newFeedData.Items

	if err := updateFeed(con, *url, x, testIdGenerator); err != nil {
		t.Fatalf("Failed to update simple single feed (%s)!", err)
	}
	x.Items = append(x.Items, fullItems[:MaximumFeedItems-len(newFeedData.Items)]...)
	if err := con.LoadModel(feedModel.UrlKey(), newLoadFeed); err != nil {
		t.Fatalf("Failed to initialize feed model (%s)!", err)
	} else if compareParsedToFinalFeed(t, &x, newLoadFeed, con) == false {
		t.Fatalf("Inserted data did not match original data (minus overage!)")
	}
}
<commit_msg>Make checkAllItemsDeleted more generic, taking an ItemKeyList.<commit_after>/*
 * Copyright (C) 2013 Matthew Dawson <matthew@mjdsystems.ca>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package main

import (
	"net/url"

	riak "github.com/tpjg/goriakpbc"

	"math/rand"
	"reflect"

	"strconv"
	"time"

	"testing"
	"testing/quick"
)

func makeIdGenerator() <-chan uint64 {
	ch := make(chan uint64)

	go func(ch chan<- uint64) {
		var id uint64
		for {
			ch <- id
			id++
		}
	}(ch)

	return ch
}

var testIdGenerator <-chan uint64 = makeIdGenerator()

func fixFeedForMerging(toFix *ParsedFeedData) *ParsedFeedData {
	// Instead of sorting, verify we are sorted.  Otherwise items without publication dates can end
	// up in the wrong place.
	if !toFix.Items.VerifySort() {
		panic("Items are not sorted!!!!")
	}

	for i, _ := range toFix.Items {
		item := &toFix.Items[i]
		item.GenericKey = makeHash(string(item.GenericKey) + key_uniquer)
	}
	return toFix
}

func compareParsedToFinalFeed(t *testing.T, data *ParsedFeedData, model *Feed, con *riak.Client) bool {
	// Compare basics:
	if data.Title != model.Title {
		t.Errorf("Feed title didn't match '%s' vs '%s'!", data.Title, model.Title)
		return false
	}

	if len(data.Items) != len(model.ItemKeys) {
		if len(data.Items) > MaximumFeedItems {
			t.Errorf("Item count differs due to items count greater then Maximum number of feed items (%v of %v)", len(data.Items), MaximumFeedItems)
		} else {
			t.Errorf("Item count is different %v vs %v!", len(data.Items), len(model.ItemKeys))
		}
		return false
	}
	if len(model.InsertedItemKeys) != 0 || len(model.DeletedItemKeys) != 0 {
		t.Error("There are left over inserted or deleted item keys!")
		return false
	}

	type FeedItemCh struct {
		item FeedItem
		ch   chan FeedItemCh
	}

	itemChOut := make(chan FeedItemCh)
	itemCh := make(chan FeedItem)

	go func(itemCh chan FeedItem, itemChOut chan FeedItemCh) {
		defer close(itemCh)
		for item, ok := <-itemChOut; ok; item, ok = <-itemChOut {
			itemCh <- item.item
			itemChOut = item.ch
		}
	}(itemCh, itemChOut)

	itemChIn := make(chan FeedItemCh)

	for _, itemKey := range model.ItemKeys {
		go func(itemKey ItemKey, itemChOut, itemChIn chan FeedItemCh) {
			defer close(itemChOut)

			modelItem := FeedItem{}
			if err := con.LoadModel(itemKey.GetRiakKey(), &modelItem, riak.R1); err != nil {
				t.Errorf("Failed to load item! Error: %s item %s", err, itemKey.GetRiakKey())
			}
			itemChOut <- FeedItemCh{modelItem, itemChIn}
		}(itemKey, itemChOut, itemChIn)
		itemChOut = itemChIn
		itemChIn = make(chan FeedItemCh)
	}

	//Compare saved feed items.  This means a trip through riak!  The order should match though ...
	for i, _ := range model.ItemKeys {
		var modelItem FeedItem
		select {
		case modelItem = <-itemCh:
		case <-time.After(time.Minute * 5):
			t.Fatalf("Failed to get an item before timeout, item %v", i)
		}

		dataItem := data.Items[i]

		if dataItem.Title != modelItem.Title ||
			dataItem.Author != modelItem.Author ||
			dataItem.Content != modelItem.Content ||
			dataItem.Url != modelItem.Url ||
			!dataItem.PubDate.Equal(modelItem.PubDate) {
			t.Errorf("Item data didn't match! Original:\n%#v\nLoaded:\n%#v", dataItem, modelItem)
			return false
		}
	}

	return true
}

func checkAllItemsDeleted(t *testing.T, itemKeyList ItemKeyList, con *riak.Client) bool {
	ch := make(chan bool)
	for _, itemKey := range itemKeyList {
		go func(itemKey ItemKey, ch chan<- bool) {
			modelItem := FeedItem{}
			if err := con.LoadModel(itemKey.GetRiakKey(), &modelItem, riak.R1); err == riak.NotFound {
				ch <- false
			} else {
				ch <- true
			}
		}(itemKey, ch)
	}

	problems := 0
	for _, _ = range itemKeyList {
		found := <-ch
		if found {
			//t.Errorf("Found deleted item %s", itemKey.GetRiakKey())
			problems++
		}
	}
	return problems == 0
}

func CreateFeed(t *testing.T, con *riak.Client, Url *url.URL) *Feed {
	feedModel := &Feed{Url: *Url}
	if err := con.LoadModel(feedModel.UrlKey(), feedModel); err == nil && err != riak.NotFound {
		t.Fatalf("Failed to initialize feed model (%s)!", err)
	} else {
		modelElement := feedModel.Model
		*feedModel = Feed{Url: *Url}
		feedModel.Model = modelElement
		if err = feedModel.Save(); err != nil {
			t.Fatalf("Failed to store feed model (%s)!", err)
		}
	}
	return feedModel
}

func MustUpdateFeedTo(t *testing.T, con *riak.Client, url *url.URL, feedName string, updateCount int) (feed *ParsedFeedData) {
	for i := 0; i < updateCount; i++ {
		feed = getFeedDataFor(t, feedName, i)
		fixFeedForMerging(feed)

		if err := updateFeed(con, *url, *feed, testIdGenerator); err != nil {
			t.Fatalf("Failed to update simple single feed (%s)!", err)
		}
	}

	return
}

var urlKeyRand = rand.New(rand.NewSource(0))

func getUniqueExampleComUrl(t *testing.T) *url.URL {
	url, err := url.Parse("http://example.com/" + key_uniquer + strconv.Itoa(urlKeyRand.Int()) + "/rss")
	if err != nil {
		t.Fatalf("Failed to generate url (%s)", err)
	}
	return url
}

func TestSingleFeedInsert(t *testing.T) {
	con := getTestConnection(t)
	defer killTestDb(con, t)

	feed := getFeedDataFor(t, "simple", 0)
	fixFeedForMerging(feed)

	url := getUniqueExampleComUrl(t)

	err := updateFeed(con, *url, *feed, testIdGenerator)
	if err != FeedNotFound {
		t.Fatalf("Failed to insert simple single feed (%s)!", err)
	}

	feedModel := CreateFeed(t, con, url)

	err = updateFeed(con, *url, *feed, testIdGenerator)
	if err != nil {
		t.Errorf("Failed to update simple single feed (%s)!", err)
	}

	// Finally, load the feed again and verify properties!
	loadFeed := &Feed{}
	if err = con.LoadModel(feedModel.UrlKey(), loadFeed); err != nil {
		t.Fatalf("Failed to initialize feed model (%s)!", err)
	} else if compareParsedToFinalFeed(t, feed, loadFeed, con) != true {
		t.Errorf("Saved feed does not match what was inserted! Original:\n%+v\nLoaded:\n%+v", feed, loadFeed)
	}
}

func TestSingleFeedUpdate(t *testing.T) {
	con := getTestConnection(t)
	defer killTestDb(con, t)

	url := getUniqueExampleComUrl(t)

	feedModel := CreateFeed(t, con, url)

	feed := MustUpdateFeedTo(t, con, url, "simple", 2)

	// Finally, load the feed again and verify properties!
	loadFeed := &Feed{}
	if err := con.LoadModel(feedModel.UrlKey(), loadFeed); err != nil {
		t.Fatalf("Failed to initialize feed model (%s)!", err)
	} else if compareParsedToFinalFeed(t, feed, loadFeed, con) != true {
		t.Errorf("Saved feed does not match what was inserted! Original:\n%+v\nLoaded:\n%+v", feed, loadFeed)
	}
}

func TestFeedUpdateAndInsert(t *testing.T) {
	con := getTestConnection(t)
	defer killTestDb(con, t)

	url := getUniqueExampleComUrl(t)

	feedModel := CreateFeed(t, con, url)

	feed := MustUpdateFeedTo(t, con, url, "simple", 3)

	// Finally, load the feed again and verify properties!
	loadFeed := &Feed{}
	if err := con.LoadModel(feedModel.UrlKey(), loadFeed); err != nil {
		t.Fatalf("Failed to initialize feed model (%s)!", err)
	} else if compareParsedToFinalFeed(t, feed, loadFeed, con) != true {
		t.Errorf("Saved feed does not match what was inserted! Original:\n%+v\nLoaded:\n%+v", feed, loadFeed)
	}
}

func TestFeedUpdateWithChangingPubDates(t *testing.T) {
	con := getTestConnection(t)
	defer killTestDb(con, t)

	url := getUniqueExampleComUrl(t)

	feedModel := CreateFeed(t, con, url)

	feed := MustUpdateFeedTo(t, con, url, "simple", 4)

	// Finally, load the feed again and verify properties!
	loadFeed := &Feed{}
	if err := con.LoadModel(feedModel.UrlKey(), loadFeed); err != nil {
		t.Fatalf("Failed to initialize feed model (%s)!", err)
	} else if compareParsedToFinalFeed(t, feed, loadFeed, con) != true {
		t.Errorf("Saved feed does not match what was inserted! Original:\n%+v\nLoaded:\n%+v", feed, loadFeed)
	}
}

func GenerateParsedFeed(rand *rand.Rand) (out ParsedFeedData) {
	out = ParsedFeedData{}
	if titleOut, ok := quick.Value(reflect.TypeOf(out.Title), rand); ok {
		out.Title = titleOut.Interface().(string)
	} else {
		panic("Couldn't make a title!")
	}

	out.Items = make([]ParsedFeedItem, MaximumFeedItems+20)
	for i, _ := range out.Items {
		out.Items[i].GenericKey = makeHash(key_uniquer + strconv.Itoa(rand.Int()))
		out.Items[i].Title = strconv.Itoa(i)
	}

	return
}

func TestFeedDealingWithOverLargeFeed(t *testing.T) {
	con := getTestConnection(t)
	defer killTestDb(con, t)

	rand := rand.New(rand.NewSource(0))

	url := getUniqueExampleComUrl(t)
	feedModel := CreateFeed(t, con, url)

	t.Log("Test creating overlarge feed.")

	x := GenerateParsedFeed(rand)
	if err := updateFeed(con, *url, x, testIdGenerator); err != nil {
		t.Fatalf("Failed to update simple single feed (%s)!", err)
	}

	x.Items = x.Items[:MaximumFeedItems]

	origLoadFeed := &Feed{}
	if err := con.LoadModel(feedModel.UrlKey(), origLoadFeed); err != nil {
		t.Fatalf("Failed to initialize feed model (%s)!", err)
	} else if compareParsedToFinalFeed(t, &x, origLoadFeed, con) == false {
		t.Fatalf("Inserted data did not match original data (minus overage!)")
	}

	t.Log("Test completely replacing said feed with a new Oversized feed.")

	x = GenerateParsedFeed(rand) // This should generate all new items.
	if err := updateFeed(con, *url, x, testIdGenerator); err != nil {
		t.Fatalf("Failed to replace simple single feed (%s)!", err)
	}

	x.Items = x.Items[:MaximumFeedItems]

	newLoadFeed := &Feed{}
	if err := con.LoadModel(feedModel.UrlKey(), newLoadFeed); err != nil {
		t.Fatalf("Failed to initialize feed model (%s)!", err)
	} else if compareParsedToFinalFeed(t, &x, newLoadFeed, con) == false {
		t.Fatalf("Inserted data did not match original data (minus overage!)")
	} else if checkAllItemsDeleted(t, origLoadFeed.ItemKeys, con) == false {
		t.Fatalf("There are left over deleted items!")
	}

	t.Log("Test adding a few new entries")

	newFeedData := getFeedDataFor(t, "simple", 0)
	fixFeedForMerging(newFeedData)
	fullItems := x.Items
	x.Items = newFeedData.Items

	if err := updateFeed(con, *url, x, testIdGenerator); err != nil {
		t.Fatalf("Failed to update simple single feed (%s)!", err)
	}
	x.Items = append(x.Items, fullItems[:MaximumFeedItems-len(newFeedData.Items)]...)
	if err := con.LoadModel(feedModel.UrlKey(), newLoadFeed); err != nil {
		t.Fatalf("Failed to initialize feed model (%s)!", err)
	} else if compareParsedToFinalFeed(t, &x, newLoadFeed, con) == false {
		t.Fatalf("Inserted data did not match original data (minus overage!)")
	}
}
<|endoftext|>


<commit_before>package client

import (
	"bytes"
	"fmt"
	"testing"

	"github.com/eliothedeman/bangarang/event"
)

var (
	numEvents = 0
)

type testTcpClient struct {
	c    *TcpClient
	buff *bytes.Buffer
}

func newTestTcpClient() *testTcpClient {
	t := &testTcpClient{}
	c, _ := NewTcpClient("", "json", 4)
	buff := bytes.NewBuffer([]byte{})
	c.conn = buff
	t.c = c
	t.buff = buff
	return t
}

func newTestEvent() *event.Event {
	numEvents += 1
	e := &event.Event{
		Host:    fmt.Sprintf("test_%d", numEvents),
		Service: "test_service",
		Metric:  float64(numEvents),
	}

	return e
}

func TestNewTcpClient(t *testing.T) {
	c := newTestTcpClient()
	e := newTestEvent()
	err := c.c.Send(e)
	if err != nil {
		t.Error(err)
	}

	expected := `{ "host":"test_1","service":"test_service","sub_type":"","metric":1,"occurences":0,"tags":null,"status":0}`
	if expected != c.buff.String()[:c.buff.Len()-len(DELIMITER)] {
		t.Error(c.buff.String())
	}
}
<commit_msg>fixed test<commit_after>package client

import (
	"bytes"
	"fmt"
	"testing"

	"github.com/eliothedeman/bangarang/event"
)

var (
	numEvents = 0
)

type testTcpClient struct {
	c    *TcpClient
	buff *bytes.Buffer
}

func newTestTcpClient() *testTcpClient {
	t := &testTcpClient{}
	c, _ := NewTcpClient("", "json", 4)
	buff := bytes.NewBuffer([]byte{})
	c.conn = buff
	t.c = c
	t.buff = buff
	return t
}

func newTestEvent() *event.Event {
	numEvents += 1
	e := &event.Event{
		Host:    fmt.Sprintf("test_%d", numEvents),
		Service: "test_service",
		Metric:  float64(numEvents),
	}

	return e
}

func TestNewTcpClient(t *testing.T) {
	c := newTestTcpClient()
	e := newTestEvent()
	err := c.c.Send(e)
	if err != nil {
		t.Error(err)
	}

	expected := `{ "host":"test_1","service":"test_service","sub_type":"","metric":1,"occurences":0,"tags":null,"status":0}`
	if expected != c.buff.String()[8:c.buff.Len()] {
		t.Error(c.buff.String())
	}
}
<|endoftext|>


<commit_before>// Package math contains wdte.Funcs for performing mathematical
// operations.
package math

import (
	"math"

	"github.com/DeedleFake/wdte"
)

// Pi ignores its arguments and returns π as a wdte.Number.
func Pi(frame []wdte.Func, args ...wdte.Func) wdte.Func {
	return wdte.Number(math.Pi)
}

// Sin returns sin(args[0]).
func Sin(frame []wdte.Func, args ...wdte.Func) wdte.Func {
	if len(args) == 0 {
		return wdte.GoFunc(Sin)
	}

	a := args[0].Call(frame).(wdte.Number)
	return wdte.Number(math.Sin(float64(a)))
}

// Cos returns cos(args[0]).
func Cos(frame []wdte.Func, args ...wdte.Func) wdte.Func {
	if len(args) == 0 {
		return wdte.GoFunc(Cos)
	}

	a := args[0].Call(frame).(wdte.Number)
	return wdte.Number(math.Cos(float64(a)))
}

// Tan returns tan(args[0]).
func Tan(frame []wdte.Func, args ...wdte.Func) wdte.Func {
	if len(args) == 0 {
		return wdte.GoFunc(Tan)
	}

	a := args[0].Call(frame).(wdte.Number)
	return wdte.Number(math.Tan(float64(a)))
}

// Module returns a module that contains the functions in this
// package. This can be used by an Importer to import them more
// easily. The functions in the returned module have the same names as
// those in this package except that they are lowercase.
func Module() *wdte.Module {
	return &wdte.Module{
		Funcs: map[wdte.ID]wdte.Func{
			"pi": wdte.GoFunc(Pi),

			"sin": wdte.GoFunc(Sin),
			"cos": wdte.GoFunc(Cos),
			"tan": wdte.GoFunc(Tan),
		},
	}
}
<commit_msg>std/math: Add floor, ceil, and abs.<commit_after>// Package math contains wdte.Funcs for performing mathematical
// operations.
package math

import (
	"math"

	"github.com/DeedleFake/wdte"
)

// Pi ignores its arguments and returns π as a wdte.Number.
func Pi(frame []wdte.Func, args ...wdte.Func) wdte.Func {
	return wdte.Number(math.Pi)
}

// Sin returns sin(args[0]).
func Sin(frame []wdte.Func, args ...wdte.Func) wdte.Func {
	if len(args) == 0 {
		return wdte.GoFunc(Sin)
	}

	a := args[0].Call(frame).(wdte.Number)
	return wdte.Number(math.Sin(float64(a)))
}

// Cos returns cos(args[0]).
func Cos(frame []wdte.Func, args ...wdte.Func) wdte.Func {
	if len(args) == 0 {
		return wdte.GoFunc(Cos)
	}

	a := args[0].Call(frame).(wdte.Number)
	return wdte.Number(math.Cos(float64(a)))
}

// Tan returns tan(args[0]).
func Tan(frame []wdte.Func, args ...wdte.Func) wdte.Func {
	if len(args) == 0 {
		return wdte.GoFunc(Tan)
	}

	a := args[0].Call(frame).(wdte.Number)
	return wdte.Number(math.Tan(float64(a)))
}

// Floor returns ⌊args[0]⌋.
func Floor(frame []wdte.Func, args ...wdte.Func) wdte.Func {
	if len(args) == 0 {
		return wdte.GoFunc(Floor)
	}

	a := args[0].Call(frame).(wdte.Number)
	return wdte.Number(math.Floor(float64(a)))
}

// Ceil returns ⌈args[0]⌉.
func Ceil(frame []wdte.Func, args ...wdte.Func) wdte.Func {
	if len(args) == 0 {
		return wdte.GoFunc(Ceil)
	}

	a := args[0].Call(frame).(wdte.Number)
	return wdte.Number(math.Ceil(float64(a)))
}

// Abs returns |args[0]|.
func Abs(frame []wdte.Func, args ...wdte.Func) wdte.Func {
	if len(args) == 0 {
		return wdte.GoFunc(Abs)
	}

	a := args[0].Call(frame).(wdte.Number)
	return wdte.Number(math.Abs(float64(a)))
}

// Module returns a module that contains the functions in this
// package. This can be used by an Importer to import them more
// easily. The functions in the returned module have the same names as
// those in this package except that they are lowercase.
func Module() *wdte.Module {
	return &wdte.Module{
		Funcs: map[wdte.ID]wdte.Func{
			"pi": wdte.GoFunc(Pi),

			"sin": wdte.GoFunc(Sin),
			"cos": wdte.GoFunc(Cos),
			"tan": wdte.GoFunc(Tan),

			"floor": wdte.GoFunc(Floor),
			"ceil":  wdte.GoFunc(Ceil),
			"abs":   wdte.GoFunc(Abs),
		},
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 Globo.com. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cluster

import (
	"github.com/fsouza/go-dockerclient"
	"sync"
	"sync/atomic"
)

type Node struct {
	ID      string
	Address string
}

type node struct {
	*docker.Client
	id   string
	load int64
}

type Cluster struct {
	nodes    []node
	lastUsed int64
	mut      sync.RWMutex
}

func New(nodes ...Node) (*Cluster, error) {
	c := Cluster{
		nodes:    make([]node, len(nodes)),
		lastUsed: -1,
	}
	for i, n := range nodes {
		client, err := docker.NewClient(n.Address)
		if err != nil {
			return nil, err
		}
		c.nodes[i] = node{
			id:     n.ID,
			Client: client,
		}
	}
	return &c, nil
}

func (c *Cluster) next() node {
	c.mut.RLock()
	defer c.mut.RUnlock()
	index := atomic.AddInt64(&c.lastUsed, 1) % int64(len(c.nodes))
	return c.nodes[index]
}

func (c *Cluster) Register(nodes ...Node) error {
	c.mut.Lock()
	defer c.mut.Unlock()
	for _, n := range nodes {
		client, err := docker.NewClient(n.Address)
		if err != nil {
			return err
		}
		c.nodes = append(c.nodes, node{id: n.ID, Client: client})
	}
	return nil
}
<commit_msg>cluster: add some documentation<commit_after>// Copyright 2013 Globo.com. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package cluster provides types and functions for management of Docker
// clusters, scheduling container operations among hosts running Docker
// (nodes).
package cluster

import (
	"github.com/fsouza/go-dockerclient"
	"sync"
	"sync/atomic"
)

// Node represents a host running Docker. Each node has an ID and an address
// (in the form <scheme>://<host>:<port>/).
type Node struct {
	ID      string
	Address string
}

type node struct {
	*docker.Client
	id   string
	load int64
}

// Cluster is the basic type of the package. It manages internal nodes, and
// provide methods for interaction with those nodes, like CreateContainer,
// which creates a container in one node of the cluster.
type Cluster struct {
	nodes    []node
	lastUsed int64
	mut      sync.RWMutex
}

// New creates a new Cluster, composed of the given nodes.
func New(nodes ...Node) (*Cluster, error) {
	c := Cluster{
		nodes:    make([]node, len(nodes)),
		lastUsed: -1,
	}
	for i, n := range nodes {
		client, err := docker.NewClient(n.Address)
		if err != nil {
			return nil, err
		}
		c.nodes[i] = node{
			id:     n.ID,
			Client: client,
		}
	}
	return &c, nil
}

func (c *Cluster) next() node {
	c.mut.RLock()
	defer c.mut.RUnlock()
	index := atomic.AddInt64(&c.lastUsed, 1) % int64(len(c.nodes))
	return c.nodes[index]
}

// Register adds new nodes to the cluster.
func (c *Cluster) Register(nodes ...Node) error {
	c.mut.Lock()
	defer c.mut.Unlock()
	for _, n := range nodes {
		client, err := docker.NewClient(n.Address)
		if err != nil {
			return err
		}
		c.nodes = append(c.nodes, node{id: n.ID, Client: client})
	}
	return nil
}
<|endoftext|>


<commit_before>/*
This package is just a collection of test cases
*/
package main

import (
	"github.com/keltia/ripe-atlas"
	"github.com/urfave/cli"
	"log"
	"os"
	"sort"
)

var (
	// flags
	fWant4    bool
	fWant6    bool
	fWantMine bool

	fAllProbes       bool
	fAllMeasurements bool

	fAsn         string
	fCountry     string
	fFieldList   string
	fFormat      string
	fOptFields   string
	fProtocol    string
	fSortOrder   string
	fMeasureType string

	fVerbose    bool
	fWantAnchor bool

	fMaxHops    int
	fPacketSize int

	mycnf *atlas.Config

	cliCommands []cli.Command
)

const (
	atlasVersion = "0.9"
)

// main is the starting point (and everything)
func main() {
	cli.VersionFlag = cli.BoolFlag{Name: "version, V"}

	cli.VersionPrinter = func(c *cli.Context) {
		log.Printf("API wrapper: %s Atlas CLI: %s\n", c.App.Version, atlas.GetVersion())
	}

	app := cli.NewApp()
	app.Name = "atlas"
	app.Usage = "RIPE Atlas CLI interface"
	app.Author = "Ollivier Robert <roberto@keltia.net>"
	app.Version = atlasVersion
	//app.HideVersion = true

	// General flags
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:        "format,f",
			Usage:       "specify output format",
			Destination: &fFormat,
		},
		cli.BoolFlag{
			Name:        "verbose,v",
			Usage:       "verbose mode",
			Destination: &fVerbose,
		},
		cli.StringFlag{
			Name:        "fields,F",
			Usage:       "specify which fields are wanted",
			Destination: &fFieldList,
		},
		cli.StringFlag{
			Name:        "opt-fields,O",
			Usage:       "specify which optional fields are wanted",
			Destination: &fOptFields,
		},
		cli.StringFlag{
			Name:        "sort,S",
			Usage:       "sort results",
			Destination: &fSortOrder,
		},
	}

	var err error

	mycnf, err = atlas.LoadConfig("ripe-atlas")
	if mycnf.APIKey != "" && err == nil {
		atlas.SetAuth(mycnf.APIKey)
		log.Printf("Found API key!")
	} else {
		log.Printf("No API key!")
	}
	if mycnf.DefaultProbe != 0 && err == nil {
		log.Printf("Found default probe: %d\n", mycnf.DefaultProbe)
	}
	sort.Sort(ByAlphabet(cliCommands))
	app.Commands = cliCommands
	app.Run(os.Args)
}
<commit_msg>Add some HTTP-related variables.<commit_after>/*
This package is just a collection of test cases
*/
package main

import (
	"github.com/keltia/ripe-atlas"
	"github.com/urfave/cli"
	"log"
	"os"
	"sort"
)

var (
	// flags
	fWant4    bool
	fWant6    bool
	fWantMine bool

	fAllProbes       bool
	fAllMeasurements bool

	fAsn         string
	fCountry     string
	fFieldList   string
	fFormat      string
	fOptFields   string
	fProtocol    string
	fSortOrder   string
	fMeasureType string

	fHTTPMethod	 string
	fUserAgent   string

	fBitCD         bool
	fDisableDNSSEC bool

	fVerbose    bool
	fWantAnchor bool

	fMaxHops    int
	fPacketSize int

	mycnf *atlas.Config

	cliCommands []cli.Command
)

const (
	atlasVersion = "0.9"
)

// main is the starting point (and everything)
func main() {
	cli.VersionFlag = cli.BoolFlag{Name: "version, V"}

	cli.VersionPrinter = func(c *cli.Context) {
		log.Printf("API wrapper: %s Atlas CLI: %s\n", c.App.Version, atlas.GetVersion())
	}

	app := cli.NewApp()
	app.Name = "atlas"
	app.Usage = "RIPE Atlas CLI interface"
	app.Author = "Ollivier Robert <roberto@keltia.net>"
	app.Version = atlasVersion
	//app.HideVersion = true

	// General flags
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:        "format,f",
			Usage:       "specify output format",
			Destination: &fFormat,
		},
		cli.BoolFlag{
			Name:        "verbose,v",
			Usage:       "verbose mode",
			Destination: &fVerbose,
		},
		cli.StringFlag{
			Name:        "fields,F",
			Usage:       "specify which fields are wanted",
			Destination: &fFieldList,
		},
		cli.StringFlag{
			Name:        "opt-fields,O",
			Usage:       "specify which optional fields are wanted",
			Destination: &fOptFields,
		},
		cli.StringFlag{
			Name:        "sort,S",
			Usage:       "sort results",
			Destination: &fSortOrder,
		},
	}

	var err error

	mycnf, err = atlas.LoadConfig("ripe-atlas")
	if mycnf.APIKey != "" && err == nil {
		atlas.SetAuth(mycnf.APIKey)
		log.Printf("Found API key!")
	} else {
		log.Printf("No API key!")
	}
	if mycnf.DefaultProbe != 0 && err == nil {
		log.Printf("Found default probe: %d\n", mycnf.DefaultProbe)
	}
	sort.Sort(ByAlphabet(cliCommands))
	app.Commands = cliCommands
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/urfave/cli"
	"log"
	"net"
	"net/url"
	"strconv"
	"strings"
	"github.com/keltia/ripe-atlas"
	"fmt"
)

// ByAlphabet is for sorting
type ByAlphabet []cli.Command

func (a ByAlphabet) Len() int           { return len(a) }
func (a ByAlphabet) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByAlphabet) Less(i, j int) bool { return a[i].Name < a[j].Name }

// checkGlobalFlags is the place to check global parameters
func checkGlobalFlags(o map[string]string) (opts map[string]string) {
	opts = o

	for k, v := range map[string]string{
		"fields":          fFieldList,
		"format":          fFormat,
		"include":         fInclude,
		"optional_fields": fOptFields,
		"page":            fPageNum,
		"page_size":       fPageSize,
		"sort":            fSortOrder,
	} {
		if v != "" {
			client.SetOption(k, v)
		}
	}

	if fFormat != "" && validateFormat(fFormat) {
		client.SetOption("format", fFormat)
	}
	return opts
}

// validateFormat allows only supported formats
func validateFormat(fmt string) bool {
	f := strings.ToLower(fmt)
	if f == "json" || f == "xml" || f == "api" || f == "txt" || f == "jsonp" {
		return true
	}
	return false
}

func displayOptions(opts map[string]string) {
	log.Println("Options:")
	for key, val := range opts {
		log.Printf("  %s: %s", key, val)
	}
}

func boolToString(k bool) string {
	if k {
		return "true"
	} else {
		return "false"
	}
}

// analyzeTarget breaks up an url into its components
func analyzeTarget(target string) (proto, site, path string, port int) {
	uri, err := url.Parse(target)
	if err != nil {
		// This is no URL, make one
		proto = "https"
		site = target
		path = "/"
		port = 443
	} else {
		proto = uri.Scheme

		// might be host:port
		sp := strings.Split(uri.Host, ":")
		if len(sp) == 2 {
			port, _ = strconv.Atoi(sp[1])
			site = sp[0]
		} else {
			site = uri.Host
		}

		path = uri.Path
		// Path can't be null
		if path == "" {
			path = "/"
		}
	}
	return
}

const (
	Hostname = iota
	IPv4
	IPv6
)

// checkArgumentType checks whether we have a domain/host or an IPv4/v6 address
func checkArgumentType(arg string) int {
	ip := net.ParseIP(arg)
	if ip != nil {
		if ip.To4() == nil {
			return IPv6
		}
		return IPv4
	}
	return Hostname
}

// prepareFamily sets Want4 and/or Want6 depending on the argument
func prepareFamily(arg string) {
	switch checkArgumentType(arg) {
	case IPv4:
		mycnf.WantAF = Want4
	case IPv6:
		mycnf.WantAF = Want6
	default:
		mycnf.WantAF = WantBoth
	}
}

// displayMeasurementID display result of measurement requests.
func displayMeasurementID(list atlas.MeasurementResp) {
		fmt.Println("Measurements created:")
	for _, m := range list.Measurements {
		fmt.Printf("%d\n", m)
	}
	fmt.Printf(`
Use the following command to retrieve results in JSON:

  atlas r <id>

`)
}<commit_msg>golint: remove extra else.<commit_after>package main

import (
	"fmt"
	"github.com/keltia/ripe-atlas"
	"github.com/urfave/cli"
	"log"
	"net"
	"net/url"
	"strconv"
	"strings"
)

// ByAlphabet is for sorting
type ByAlphabet []cli.Command

func (a ByAlphabet) Len() int           { return len(a) }
func (a ByAlphabet) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByAlphabet) Less(i, j int) bool { return a[i].Name < a[j].Name }

// checkGlobalFlags is the place to check global parameters
func checkGlobalFlags(o map[string]string) (opts map[string]string) {
	opts = o

	for k, v := range map[string]string{
		"fields":          fFieldList,
		"format":          fFormat,
		"include":         fInclude,
		"optional_fields": fOptFields,
		"page":            fPageNum,
		"page_size":       fPageSize,
		"sort":            fSortOrder,
	} {
		if v != "" {
			client.SetOption(k, v)
		}
	}

	if fFormat != "" && validateFormat(fFormat) {
		client.SetOption("format", fFormat)
	}
	return opts
}

// validateFormat allows only supported formats
func validateFormat(fmt string) bool {
	f := strings.ToLower(fmt)
	if f == "json" || f == "xml" || f == "api" || f == "txt" || f == "jsonp" {
		return true
	}
	return false
}

func displayOptions(opts map[string]string) {
	log.Println("Options:")
	for key, val := range opts {
		log.Printf("  %s: %s", key, val)
	}
}

func boolToString(k bool) string {
	if k {
		return "true"
	}
	return "false"
}

// analyzeTarget breaks up an url into its components
func analyzeTarget(target string) (proto, site, path string, port int) {
	uri, err := url.Parse(target)
	if err != nil {
		// This is no URL, make one
		proto = "https"
		site = target
		path = "/"
		port = 443
	} else {
		proto = uri.Scheme

		// might be host:port
		sp := strings.Split(uri.Host, ":")
		if len(sp) == 2 {
			port, _ = strconv.Atoi(sp[1])
			site = sp[0]
		} else {
			site = uri.Host
		}

		path = uri.Path
		// Path can't be null
		if path == "" {
			path = "/"
		}
	}
	return
}

const (
	Hostname = iota
	IPv4
	IPv6
)

// checkArgumentType checks whether we have a domain/host or an IPv4/v6 address
func checkArgumentType(arg string) int {
	ip := net.ParseIP(arg)
	if ip != nil {
		if ip.To4() == nil {
			return IPv6
		}
		return IPv4
	}
	return Hostname
}

// prepareFamily sets Want4 and/or Want6 depending on the argument
func prepareFamily(arg string) {
	switch checkArgumentType(arg) {
	case IPv4:
		mycnf.WantAF = Want4
	case IPv6:
		mycnf.WantAF = Want6
	default:
		mycnf.WantAF = WantBoth
	}
}

// displayMeasurementID display result of measurement requests.
func displayMeasurementID(list atlas.MeasurementResp) {
	fmt.Println("Measurements created:")
	for _, m := range list.Measurements {
		fmt.Printf("%d\n", m)
	}
	fmt.Printf(`
Use the following command to retrieve results in JSON:

  atlas r <id>

`)
}
<|endoftext|>


<commit_before>/*
Copyright 2011 Google Inc.

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

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

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

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"

	"camlistore.org/pkg/blob"
	"camlistore.org/pkg/client/android"
	"camlistore.org/pkg/cmdmain"
	"camlistore.org/pkg/jsonsign"
	"camlistore.org/pkg/osutil"
	"camlistore.org/pkg/types/clientconfig"
)

type initCmd struct {
	newKey     bool   // whether to create a new GPG ring and key.
	noconfig   bool   // whether to generate a client config file.
	keyId      string // GPG key ID to use.
	secretRing string // GPG secret ring file to use.
}

func init() {
	cmdmain.RegisterCommand("init", func(flags *flag.FlagSet) cmdmain.CommandRunner {
		cmd := new(initCmd)
		flags.BoolVar(&cmd.newKey, "newkey", false,
			fmt.Sprintf("Automatically generate a new identity in a new secret ring at %s", osutil.DefaultSecretRingFile()))
		flags.StringVar(&cmd.keyId, "gpgkey", "", "GPG key ID to use for signing (overrides $GPGKEY environment)")
		flags.BoolVar(&cmd.noconfig, "noconfig", false, "Stop after creating the public key blob, and do not try and create a config file.")
		return cmd
	})
}

func (c *initCmd) Describe() string {
	return "Initialize the camput configuration file. With no option, it tries to use the GPG key found in the default identity secret ring."
}

func (c *initCmd) Usage() {
	fmt.Fprintf(cmdmain.Stderr, "Usage: camput init [opts]")
}

func (c *initCmd) Examples() []string {
	return []string{
		"",
		"--gpgkey=XXXXX",
		"--newkey Creates a new identity",
	}
}

// initSecretRing sets c.secretRing. It tries, in this order, the --secret-keyring flag,
// the CAMLI_SECRET_RING env var, then defaults to the operating system dependent location
// otherwise.
// It returns an error if the file does not exist.
func (c *initCmd) initSecretRing() error {
	if secretRing, ok := osutil.ExplicitSecretRingFile(); ok {
		c.secretRing = secretRing
	} else {
		if android.OnAndroid() {
			panic("on android, so CAMLI_SECRET_RING should have been defined, or --secret-keyring used.")
		}
		c.secretRing = osutil.SecretRingFile()
	}
	if _, err := os.Stat(c.secretRing); err != nil {
		hint := "\nA GPG key is required, please use 'camput init --newkey'.\n\nOr if you know what you're doing, you can set the global camput flag --secret-keyring, or the CAMLI_SECRET_RING env var, to use your own GPG ring. And --gpgkey=<pubid> or GPGKEY to select which key ID to use."
		return fmt.Errorf("Could not use secret ring file %v: %v.\n%v", c.secretRing, err, hint)
	}
	return nil
}

// initKeyId sets c.keyId. It checks, in this order, the --gpgkey flag, the GPGKEY env var,
// and in the default identity secret ring.
func (c *initCmd) initKeyId() error {
	if k := c.keyId; k != "" {
		return nil
	}
	if k := os.Getenv("GPGKEY"); k != "" {
		c.keyId = k
		return nil
	}

	k, err := jsonsign.KeyIdFromRing(c.secretRing)
	if err != nil {
		hint := "You can set --gpgkey=<pubid> or the GPGKEY env var to select which key ID to use.\n"
		return fmt.Errorf("No suitable gpg key was found in %v: %v.\n%v", c.secretRing, err, hint)
	}
	c.keyId = k
	log.Printf("Re-using identity with keyId %q found in file %s", c.keyId, c.secretRing)
	return nil
}

func (c *initCmd) getPublicKeyArmored() ([]byte, error) {
	entity, err := jsonsign.EntityFromSecring(c.keyId, c.secretRing)
	if err != nil {
		return nil, fmt.Errorf("Could not find keyId %v in ring %v: %v", c.keyId, c.secretRing, err)
	}
	pubArmor, err := jsonsign.ArmoredPublicKey(entity)
	if err != nil {
		return nil, fmt.Errorf("failed to export armored public key ID %q from %v: %v", c.keyId, c.secretRing, err)
	}
	return []byte(pubArmor), nil
}

func (c *initCmd) RunCommand(args []string) error {
	if len(args) > 0 {
		return cmdmain.ErrUsage
	}

	if c.newKey && c.keyId != "" {
		log.Fatal("--newkey and --gpgkey are mutually exclusive")
	}

	var err error
	if c.newKey {
		c.secretRing = osutil.DefaultSecretRingFile()
		c.keyId, err = jsonsign.GenerateNewSecRing(c.secretRing)
		if err != nil {
			return err
		}
	} else {
		if err := c.initSecretRing(); err != nil {
			return err
		}
		if err := c.initKeyId(); err != nil {
			return err
		}
	}

	pubArmor, err := c.getPublicKeyArmored()
	if err != nil {
		return err
	}

	bref := blob.SHA1FromString(string(pubArmor))

	log.Printf("Your Camlistore identity (your GPG public key's blobref) is: %s", bref.String())

	if c.noconfig {
		return nil
	}

	configFilePath := osutil.UserClientConfigPath()
	_, err = os.Stat(configFilePath)
	if err == nil {
		log.Fatalf("Config file %q already exists; quitting without touching it.", configFilePath)
	}
	if err := os.MkdirAll(filepath.Dir(configFilePath), 0700); err != nil {
		return err
	}
	if f, err := os.OpenFile(configFilePath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600); err == nil {
		defer f.Close()
		m := &clientconfig.Config{
			Servers: map[string]*clientconfig.Server{
				"localhost": {
					Server:    "http://localhost:3179",
					IsDefault: true,
					Auth:      "localhost",
				},
			},
			Identity:     c.keyId,
			IgnoredFiles: []string{".DS_Store"},
		}

		jsonBytes, err := json.MarshalIndent(m, "", "  ")
		if err != nil {
			log.Fatalf("JSON serialization error: %v", err)
		}
		_, err = f.Write(jsonBytes)
		if err != nil {
			log.Fatalf("Error writing to %q: %v", configFilePath, err)
		}
		log.Printf("Wrote %q; modify as necessary.", configFilePath)
	} else {
		return fmt.Errorf("could not write client config file %v: %v", configFilePath, err)
	}
	return nil
}
<commit_msg>camput init: do not call DefaultSecretRingFile in init()<commit_after>/*
Copyright 2011 Google Inc.

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

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

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

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"

	"camlistore.org/pkg/blob"
	"camlistore.org/pkg/client/android"
	"camlistore.org/pkg/cmdmain"
	"camlistore.org/pkg/jsonsign"
	"camlistore.org/pkg/osutil"
	"camlistore.org/pkg/types/clientconfig"
)

type initCmd struct {
	newKey     bool   // whether to create a new GPG ring and key.
	noconfig   bool   // whether to generate a client config file.
	keyId      string // GPG key ID to use.
	secretRing string // GPG secret ring file to use.
}

func init() {
	cmdmain.RegisterCommand("init", func(flags *flag.FlagSet) cmdmain.CommandRunner {
		cmd := new(initCmd)
		flags.BoolVar(&cmd.newKey, "newkey", false,
			"Automatically generate a new identity in a new secret ring at the default location (~/.config/camlistore/identity-secring.gpg on linux).")
		flags.StringVar(&cmd.keyId, "gpgkey", "", "GPG key ID to use for signing (overrides $GPGKEY environment)")
		flags.BoolVar(&cmd.noconfig, "noconfig", false, "Stop after creating the public key blob, and do not try and create a config file.")
		return cmd
	})
}

func (c *initCmd) Describe() string {
	return "Initialize the camput configuration file. With no option, it tries to use the GPG key found in the default identity secret ring."
}

func (c *initCmd) Usage() {
	fmt.Fprintf(cmdmain.Stderr, "Usage: camput init [opts]")
}

func (c *initCmd) Examples() []string {
	return []string{
		"",
		"--gpgkey=XXXXX",
		"--newkey Creates a new identity",
	}
}

// initSecretRing sets c.secretRing. It tries, in this order, the --secret-keyring flag,
// the CAMLI_SECRET_RING env var, then defaults to the operating system dependent location
// otherwise.
// It returns an error if the file does not exist.
func (c *initCmd) initSecretRing() error {
	if secretRing, ok := osutil.ExplicitSecretRingFile(); ok {
		c.secretRing = secretRing
	} else {
		if android.OnAndroid() {
			panic("on android, so CAMLI_SECRET_RING should have been defined, or --secret-keyring used.")
		}
		c.secretRing = osutil.SecretRingFile()
	}
	if _, err := os.Stat(c.secretRing); err != nil {
		hint := "\nA GPG key is required, please use 'camput init --newkey'.\n\nOr if you know what you're doing, you can set the global camput flag --secret-keyring, or the CAMLI_SECRET_RING env var, to use your own GPG ring. And --gpgkey=<pubid> or GPGKEY to select which key ID to use."
		return fmt.Errorf("Could not use secret ring file %v: %v.\n%v", c.secretRing, err, hint)
	}
	return nil
}

// initKeyId sets c.keyId. It checks, in this order, the --gpgkey flag, the GPGKEY env var,
// and in the default identity secret ring.
func (c *initCmd) initKeyId() error {
	if k := c.keyId; k != "" {
		return nil
	}
	if k := os.Getenv("GPGKEY"); k != "" {
		c.keyId = k
		return nil
	}

	k, err := jsonsign.KeyIdFromRing(c.secretRing)
	if err != nil {
		hint := "You can set --gpgkey=<pubid> or the GPGKEY env var to select which key ID to use.\n"
		return fmt.Errorf("No suitable gpg key was found in %v: %v.\n%v", c.secretRing, err, hint)
	}
	c.keyId = k
	log.Printf("Re-using identity with keyId %q found in file %s", c.keyId, c.secretRing)
	return nil
}

func (c *initCmd) getPublicKeyArmored() ([]byte, error) {
	entity, err := jsonsign.EntityFromSecring(c.keyId, c.secretRing)
	if err != nil {
		return nil, fmt.Errorf("Could not find keyId %v in ring %v: %v", c.keyId, c.secretRing, err)
	}
	pubArmor, err := jsonsign.ArmoredPublicKey(entity)
	if err != nil {
		return nil, fmt.Errorf("failed to export armored public key ID %q from %v: %v", c.keyId, c.secretRing, err)
	}
	return []byte(pubArmor), nil
}

func (c *initCmd) RunCommand(args []string) error {
	if len(args) > 0 {
		return cmdmain.ErrUsage
	}

	if c.newKey && c.keyId != "" {
		log.Fatal("--newkey and --gpgkey are mutually exclusive")
	}

	var err error
	if c.newKey {
		c.secretRing = osutil.DefaultSecretRingFile()
		c.keyId, err = jsonsign.GenerateNewSecRing(c.secretRing)
		if err != nil {
			return err
		}
	} else {
		if err := c.initSecretRing(); err != nil {
			return err
		}
		if err := c.initKeyId(); err != nil {
			return err
		}
	}

	pubArmor, err := c.getPublicKeyArmored()
	if err != nil {
		return err
	}

	bref := blob.SHA1FromString(string(pubArmor))

	log.Printf("Your Camlistore identity (your GPG public key's blobref) is: %s", bref.String())

	if c.noconfig {
		return nil
	}

	configFilePath := osutil.UserClientConfigPath()
	_, err = os.Stat(configFilePath)
	if err == nil {
		log.Fatalf("Config file %q already exists; quitting without touching it.", configFilePath)
	}
	if err := os.MkdirAll(filepath.Dir(configFilePath), 0700); err != nil {
		return err
	}
	if f, err := os.OpenFile(configFilePath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600); err == nil {
		defer f.Close()
		m := &clientconfig.Config{
			Servers: map[string]*clientconfig.Server{
				"localhost": {
					Server:    "http://localhost:3179",
					IsDefault: true,
					Auth:      "localhost",
				},
			},
			Identity:     c.keyId,
			IgnoredFiles: []string{".DS_Store"},
		}

		jsonBytes, err := json.MarshalIndent(m, "", "  ")
		if err != nil {
			log.Fatalf("JSON serialization error: %v", err)
		}
		_, err = f.Write(jsonBytes)
		if err != nil {
			log.Fatalf("Error writing to %q: %v", configFilePath, err)
		}
		log.Printf("Wrote %q; modify as necessary.", configFilePath)
	} else {
		return fmt.Errorf("could not write client config file %v: %v", configFilePath, err)
	}
	return nil
}
<|endoftext|>