<commit_before>package archive

import (
	"fmt"
	"io"
	"os"
	"path"
	"strings"

	"compress/bzip2"
	"compress/gzip"
	"xi2.org/x/xz"

	"golang.org/x/crypto/openpgp"
	"pault.ag/go/debian/control"
	"pault.ag/go/debian/dependency"
)

// Common Types {{{

type Closer func() error
type PathReader func(path string) (io.Reader, Closer, error)

// known compression types {{{

type compressionReader func(io.Reader) (io.Reader, error)

func gzipNewReader(r io.Reader) (io.Reader, error) {
	return gzip.NewReader(r)
}

func xzNewReader(r io.Reader) (io.Reader, error) {
	return xz.NewReader(r, 0)
}

func bzipNewReader(r io.Reader) (io.Reader, error) {
	return bzip2.NewReader(r), nil
}

var knownCompressionAlgorithms = map[string]compressionReader{
	".gz":  gzipNewReader,
	".bz2": bzipNewReader,
	".xz":  xzNewReader,
}

// }}}

// }}}

// Archive {{{

type Archive struct {
	root       string
	pathReader PathReader
	keyring    *openpgp.EntityList
}

// Get an archive Suite for a given Archive {{{

func (a Archive) Suite(name string) (*Suite, error) {
	inReleasePath := path.Join("dists", name, "InRelease")
	reader, closer, err := a.getFile(inReleasePath)
	if err != nil {
		return nil, err
	}
	defer closer()

	release, err := LoadInRelease(reader, a.keyring)
	if err != nil {
		return nil, err
	}
	return &Suite{
		Release: *release,
		archive: a,
	}, nil
}

// }}}

// Suite {{{

// Suite struct {{{

type Suite struct {
	Release Release
	archive Archive
}

// }}}

// HasArch {{{

func (s Suite) HasArch(arch dependency.Arch) bool {
	for _, el := range s.Release.Architectures {
		if el.Is(&arch) {
			return true
		}
	}
	return false
}

// }}}

// HasComponent {{{

func (s Suite) HasComponent(component string) bool {
	for _, el := range s.Release.Components {
		if el == component {
			return true
		}
	}
	return false
}

// }}}

// getHashedFileReader - get a Hashed File {{{

func (s Suite) getHashedFileReader(suitePath string) (io.Reader, Closer, control.FileHashValidators, error) {
	packagesPath := path.Join("dists", s.Release.Suite, suitePath)
	reader, closer, err := s.archive.getFile(packagesPath)

	if err != nil {
		return nil, nil, nil, err
	}

	hashes := s.Release.Indices()[suitePath]
	if len(hashes) == 0 {
		closer()
		return nil, nil, nil, fmt.Errorf("Undeclared file in InRelease")
	}

	validators, err := hashes.Validators()
	if err != nil {
		closer()
		return nil, nil, nil, err
	}

	validationReader := io.TeeReader(reader, validators.Writer())
	return validationReader, closer, validators, err
}

// }}}

// Packages index for a Suite {{{

func (s Suite) Packages(component string, arch dependency.Arch) ([]Package, error) {
	ret := []Package{}

	if !s.HasComponent(component) {
		return ret, fmt.Errorf("No such component: '%s'", component)
	}
	if !s.HasArch(arch) {
		return ret, fmt.Errorf("No such arch: '%s'", arch.String())
	}
	suitePath := path.Join(
		component,
		fmt.Sprintf("binary-%s", arch.String()),
		"Packages",
	)

	validationReader, closer, validators, err := s.getHashedFileReader(suitePath)
	if err != nil {
		return []Package{}, err
	}
	defer closer()

	packages, err := LoadPackages(validationReader)
	if err != nil {
		return []Package{}, err
	}

	for {
		next, err := packages.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return []Package{}, err
		}
		ret = append(ret, *next)
	}

	if !validators.Validate() {
		return nil, fmt.Errorf("Index hashes don't match!")
	}

	return ret, nil
}

// }}}

// Sources index for a Suite {{{

func (s Suite) Sources(component string) ([]Source, error) {
	ret := []Source{}

	if !s.HasComponent(component) {
		return ret, fmt.Errorf("No such component: '%s'", component)
	}
	suitePath := path.Join(component, "source", "Sources")

	validationReader, closer, validators, err := s.getHashedFileReader(suitePath)
	if err != nil {
		return []Source{}, err
	}
	defer closer()

	packages, err := LoadSources(validationReader)
	if err != nil {
		return []Source{}, err
	}

	for {
		next, err := packages.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return []Source{}, err
		}
		ret = append(ret, *next)
	}

	if !validators.Validate() {
		return nil, fmt.Errorf("Index hashes don't match!")
	}

	return ret, nil
}

// }}}

// }}}

// getFile wrapper {{{

func (a Archive) getFile(requestPath string) (io.Reader, Closer, error) {
	archivePath := path.Join(a.root, requestPath)
	reader, closer, err := a.pathReader(archivePath)
	if err != nil {
		return nil, nil, err
	}

	for suffix, decompressor := range knownCompressionAlgorithms {
		if strings.HasSuffix(requestPath, suffix) {
			newReader, err := decompressor(reader)
			if err != nil {
				closer()
				return nil, nil, err
			}
			return newReader, closer, nil
		}
	}

	return reader, closer, err
}

// }}}

// Constructors {{{

func NewArchive(
	root string,
	pathReader PathReader,
	keyring *openpgp.EntityList,
) Archive {
	return Archive{
		root:       root,
		pathReader: pathReader,
		keyring:    keyring,
	}
}

// Filesystem Archive {{{

func NewFilesystemArchive(root string, keyring *openpgp.EntityList) Archive {
	return NewArchive(root, filesystemPathReader, keyring)
}

// }}}

// }}}

// Readers {{{

func filesystemPathReader(path string) (io.Reader, Closer, error) {
	fd, err := os.Open(path)
	return fd, fd.Close, err
}

// }}}

// }}}

// vim: foldmethod=marker
<commit_msg>overhaul<commit_after>package archive

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"path"
	"strings"

	"compress/bzip2"
	"compress/gzip"
	"xi2.org/x/xz"

	"golang.org/x/crypto/openpgp"
	"pault.ag/go/debian/control"
	"pault.ag/go/debian/dependency"
)

// Common Types {{{

type Closer func() error
type PathReader func(path string) (io.Reader, Closer, error)

// known compression types {{{

type compressionReader func(io.Reader) (io.Reader, error)

func gzipNewReader(r io.Reader) (io.Reader, error) {
	return gzip.NewReader(r)
}

func xzNewReader(r io.Reader) (io.Reader, error) {
	return xz.NewReader(r, 0)
}

func bzipNewReader(r io.Reader) (io.Reader, error) {
	return bzip2.NewReader(r), nil
}

var knownCompressionAlgorithms = map[string]compressionReader{
	".gz":  gzipNewReader,
	".bz2": bzipNewReader,
	".xz":  xzNewReader,
}

// }}}

// }}}

// Archive {{{

type Archive struct {
	root       string
	pathReader PathReader
	keyring    *openpgp.EntityList
}

// Get an archive Suite for a given Archive {{{

func (a Archive) Suite(name string) (*Suite, error) {
	inReleasePath := path.Join("dists", name, "InRelease")
	reader, closer, err := a.getFile(inReleasePath)
	if err != nil {
		return nil, err
	}
	defer closer()

	release, err := LoadInRelease(reader, a.keyring)
	if err != nil {
		return nil, err
	}
	return &Suite{
		Release: *release,
		archive: a,
	}, nil
}

// }}}

// Suite {{{

// Suite struct {{{

type Suite struct {
	Release Release
	archive Archive
}

// }}}

// HasArch {{{

func (s Suite) HasArch(arch dependency.Arch) bool {
	for _, el := range s.Release.Architectures {
		if el.Is(&arch) {
			return true
		}
	}
	return false
}

// }}}

// HasComponent {{{

func (s Suite) HasComponent(component string) bool {
	for _, el := range s.Release.Components {
		if el == component {
			return true
		}
	}
	return false
}

// }}}

// getHashedFileReader - get a Hashed File {{{

func (s Suite) getHashedFileReader(suitePath string) (io.Reader, Closer, control.FileHashValidators, error) {
	packagesPath := path.Join("dists", s.Release.Suite, suitePath)
	reader, closer, err := s.archive.getFile(packagesPath)

	if err != nil {
		return nil, nil, nil, err
	}

	hashes := s.Release.Indices()[suitePath]
	if len(hashes) == 0 {
		closer()
		return nil, nil, nil, fmt.Errorf("Undeclared file in InRelease")
	}

	validators, err := hashes.Validators()
	if err != nil {
		closer()
		return nil, nil, nil, err
	}

	validationReader := io.TeeReader(reader, validators.Writer())
	return validationReader, closer, validators, err
}

// }}}

// Packages index for a Suite {{{

func (s Suite) Packages(component string, arch dependency.Arch) ([]Package, error) {
	ret := []Package{}

	if !s.HasComponent(component) {
		return ret, fmt.Errorf("No such component: '%s'", component)
	}
	if !s.HasArch(arch) {
		return ret, fmt.Errorf("No such arch: '%s'", arch.String())
	}
	suitePath := path.Join(
		component,
		fmt.Sprintf("binary-%s", arch.String()),
		"Packages",
	)

	validationReader, closer, validators, err := s.getHashedFileReader(suitePath)
	if err != nil {
		return []Package{}, err
	}
	defer closer()

	packages, err := LoadPackages(validationReader)
	if err != nil {
		return []Package{}, err
	}

	for {
		next, err := packages.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return []Package{}, err
		}
		ret = append(ret, *next)
	}

	if !validators.Validate() {
		return nil, fmt.Errorf("Index hashes don't match!")
	}

	return ret, nil
}

// }}}

// Sources index for a Suite {{{

func (s Suite) Sources(component string) ([]Source, error) {
	ret := []Source{}

	if !s.HasComponent(component) {
		return ret, fmt.Errorf("No such component: '%s'", component)
	}
	suitePath := path.Join(component, "source", "Sources.gz")

	validationReader, closer, validators, err := s.getHashedFileReader(suitePath)
	if err != nil {
		return []Source{}, err
	}
	defer closer()

	packages, err := LoadSources(validationReader)
	if err != nil {
		return []Source{}, err
	}

	for {
		next, err := packages.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return []Source{}, err
		}
		ret = append(ret, *next)
	}

	if !validators.Validate() {
		return nil, fmt.Errorf("Index hashes don't match!")
	}

	return ret, nil
}

// }}}

// }}}

// getFile wrapper {{{

func (a Archive) getFile(requestPath string) (io.Reader, Closer, error) {
	archivePath := a.root + "/" + requestPath
	fmt.Printf("%s\n", archivePath)
	reader, closer, err := a.pathReader(archivePath)
	if err != nil {
		return nil, nil, err
	}

	for suffix, decompressor := range knownCompressionAlgorithms {
		if strings.HasSuffix(requestPath, suffix) {
			newReader, err := decompressor(reader)
			if err != nil {
				closer()
				return nil, nil, err
			}
			return newReader, closer, nil
		}
	}

	return reader, closer, err
}

// }}}

// Constructors {{{

func NewArchive(
	root string,
	pathReader PathReader,
	keyring *openpgp.EntityList,
) Archive {
	return Archive{
		root:       root,
		pathReader: pathReader,
		keyring:    keyring,
	}
}

// Filesystem Archive {{{

func NewFilesystemArchive(root string, keyring *openpgp.EntityList) Archive {
	return NewArchive(root, filesystemPathReader, keyring)
}

func NewHttpArchive(root string, keyring *openpgp.EntityList) Archive {
	return NewArchive(root, httpPathReader, keyring)
}

// }}}

// }}}

// Readers {{{

func filesystemPathReader(path string) (io.Reader, Closer, error) {
	fd, err := os.Open(path)
	return fd, fd.Close, err
}

func httpPathReader(uri string) (io.Reader, Closer, error) {
	resp, err := http.Get(uri)
	if err != nil {
		return nil, nil, err
	}
	return resp.Body, resp.Body.Close, nil
}

// }}}

// }}}

// vim: foldmethod=marker
<|endoftext|>


<commit_before>/*-
Copyright (c) 2016, Jörg Pernfuß <joerg.pernfuss@1und1.de>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"time"

	"github.com/mjolnir42/scrypth64"
	"github.com/satori/go.uuid"
)

func (s *supervisor) activate_user(q *msg.Request) {
	result := msg.Result{Type: `supervisor`, Action: `activate_user`}
	var (
		timer                               *time.Timer
		plain                               []byte
		err                                 error
		kex                                 *auth.Kex
		validFrom, expiresAt, credExpiresAt time.Time
		token                               auth.Token
		userId                              string
		userUUID                            uuid.UUID
		ok                                  bool
		mcf                                 scrypth64.Mcf
		tx                                  *sql.Tx
	)
	data := q.Super.Data

	if s.readonly {
		result.Conflict(fmt.Errorf(`Readonly instance`))
		goto conflict
	}

	// start response timer
	timer = time.NewTimer(1 * time.Second)
	defer timer.Stop()

	// -> get kex
	if kex = s.kex.read(q.Super.KexId); kex == nil {
		//    --> reply 404 if not found
		result.NotFound(fmt.Errorf(`Key exchange not found`))
		goto dispatch
	}
	// -> check kex.SameSource
	if !kex.IsSameSourceString(q.Super.RemoteAddr) {
		//    --> reply 404 if !SameSource
		result.NotFound(fmt.Errorf(`Key exchange not found`))
		goto dispatch
	}
	// -> delete kex from s.kex (kex is now used)
	s.kex.remove(q.Super.KexId)
	// -> rdata = kex.DecodeAndDecrypt(data)
	if err = kex.DecodeAndDecrypt(&data, &plain); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> json.Unmarshal(rdata, &token)
	if err = json.Unmarshal(plain, &token); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> check token.UserName != `root`
	if token.UserName == `root` {
		//    --> reply 401
		result.Unauthorized(fmt.Errorf(`Cannot activate root`))
		goto dispatch
	}

	// check we have the user
	if err = s.stmt_FindUser.QueryRow(token.UserName).Scan(&userId); err == sql.ErrNoRows {
		result.Unauthorized(fmt.Errorf("Unknown user: %s", token.UserName))
		goto dispatch
	} else if err != nil {
		result.ServerError(err)
	}
	userUUID, _ = uuid.FromString(userId)

	// no account ownership verification in open mode
	if !SomaCfg.OpenInstance {
		switch s.activation {
		case `ldap`:
			if ok, err = validateLdapCredentials(token.UserName, token.Token); err != nil {
				result.ServerError(err)
				goto dispatch
			} else if !ok {
				result.Unauthorized(nil)
				goto dispatch
			}
		case `token`: // TODO
			result.ServerError(fmt.Errorf(`Not implemented`))
			goto dispatch
		default:
			result.ServerError(fmt.Errorf("Unknown activation: %s",
				SomaCfg.Auth.Activation))
			goto dispatch
		}
	}
	// OK: validation success

	// -> scrypth64.Digest(Password, nil)
	if mcf, err = scrypth64.Digest(token.Password, nil); err != nil {
		result.Unauthorized(nil)
		goto dispatch
	}
	// -> generate token
	token.SetIPAddressString(q.Super.RemoteAddr)
	if err = token.Generate(mcf, s.key, s.seed); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	validFrom, _ = time.Parse(rfc3339Milli, token.ValidFrom)
	expiresAt, _ = time.Parse(rfc3339Milli, token.ExpiresAt)
	credExpiresAt = validFrom.Add(time.Duration(s.credExpiry) * time.Hour * 24).UTC()

	// -> open transaction
	if tx, err = s.conn.Begin(); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	defer tx.Rollback()
	// -> DB Insert: password data
	if _, err = tx.Exec(
		stmt.SetUserCredential,
		userUUID,
		mcf.String(),
		validFrom.UTC(),
		credExpiresAt.UTC(),
	); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> DB Update: activate user
	if _, err = tx.Exec(
		stmt.ActivateUser,
		userUUID,
	); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> DB Insert: token data
	if _, err = tx.Exec(
		stmt.InsertToken,
		token.Token,
		token.Salt,
		validFrom.UTC(),
		expiresAt.UTC(),
	); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> s.credentials Update
	s.credentials.insert(token.UserName, userUUID, validFrom.UTC(),
		credExpiresAt.UTC(), mcf)
	// -> s.tokens Update
	if err = s.tokens.insert(token.Token, token.ValidFrom, token.ExpiresAt,
		token.Salt); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// commit transaction
	if err = tx.Commit(); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> sdata = kex.EncryptAndEncode(&token)
	plain = []byte{}
	data = []byte{}
	if plain, err = json.Marshal(token); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	if err = kex.EncryptAndEncode(&plain, &data); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> send sdata reply
	result.Super = &msg.Supervisor{
		Verdict: 200,
		Data:    data,
	}
	result.OK()

dispatch:
	<-timer.C

conflict:
	q.Reply <- result
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Force failure if LDAP and SOMA passwords match<commit_after>/*-
Copyright (c) 2016, Jörg Pernfuß <joerg.pernfuss@1und1.de>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"time"

	"github.com/mjolnir42/scrypth64"
	"github.com/satori/go.uuid"
)

func (s *supervisor) activate_user(q *msg.Request) {
	result := msg.Result{Type: `supervisor`, Action: `activate_user`}
	var (
		timer                               *time.Timer
		plain                               []byte
		err                                 error
		kex                                 *auth.Kex
		validFrom, expiresAt, credExpiresAt time.Time
		token                               auth.Token
		userId                              string
		userUUID                            uuid.UUID
		ok                                  bool
		mcf                                 scrypth64.Mcf
		tx                                  *sql.Tx
	)
	data := q.Super.Data

	if s.readonly {
		result.Conflict(fmt.Errorf(`Readonly instance`))
		goto conflict
	}

	// start response timer
	timer = time.NewTimer(1 * time.Second)
	defer timer.Stop()

	// -> get kex
	if kex = s.kex.read(q.Super.KexId); kex == nil {
		//    --> reply 404 if not found
		result.NotFound(fmt.Errorf(`Key exchange not found`))
		goto dispatch
	}
	// -> check kex.SameSource
	if !kex.IsSameSourceString(q.Super.RemoteAddr) {
		//    --> reply 404 if !SameSource
		result.NotFound(fmt.Errorf(`Key exchange not found`))
		goto dispatch
	}
	// -> delete kex from s.kex (kex is now used)
	s.kex.remove(q.Super.KexId)
	// -> rdata = kex.DecodeAndDecrypt(data)
	if err = kex.DecodeAndDecrypt(&data, &plain); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> json.Unmarshal(rdata, &token)
	if err = json.Unmarshal(plain, &token); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> check token.UserName != `root`
	if token.UserName == `root` {
		//    --> reply 401
		result.Unauthorized(fmt.Errorf(`Cannot activate root`))
		goto dispatch
	}

	// check we have the user
	if err = s.stmt_FindUser.QueryRow(token.UserName).Scan(&userId); err == sql.ErrNoRows {
		result.Unauthorized(fmt.Errorf("Unknown user: %s", token.UserName))
		goto dispatch
	} else if err != nil {
		result.ServerError(err)
	}
	userUUID, _ = uuid.FromString(userId)

	// no account ownership verification in open mode
	if !SomaCfg.OpenInstance {
		switch s.activation {
		case `ldap`:
			if ok, err = validateLdapCredentials(token.UserName, token.Token); err != nil {
				result.ServerError(err)
				goto dispatch
			} else if !ok {
				result.Unauthorized(nil)
				goto dispatch
			}
			// fail activation if local password is the same as the
			// upstream password
			if token.Token == token.Password {
				result.Unauthorized(fmt.Errorf("User %s denied: matching local/upstream passwords", token.UserName))
				goto dispatch
			}
		case `token`: // TODO
			result.ServerError(fmt.Errorf(`Not implemented`))
			goto dispatch
		default:
			result.ServerError(fmt.Errorf("Unknown activation: %s",
				SomaCfg.Auth.Activation))
			goto dispatch
		}
	}
	// OK: validation success

	// -> scrypth64.Digest(Password, nil)
	if mcf, err = scrypth64.Digest(token.Password, nil); err != nil {
		result.Unauthorized(nil)
		goto dispatch
	}
	// -> generate token
	token.SetIPAddressString(q.Super.RemoteAddr)
	if err = token.Generate(mcf, s.key, s.seed); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	validFrom, _ = time.Parse(rfc3339Milli, token.ValidFrom)
	expiresAt, _ = time.Parse(rfc3339Milli, token.ExpiresAt)
	credExpiresAt = validFrom.Add(time.Duration(s.credExpiry) * time.Hour * 24).UTC()

	// -> open transaction
	if tx, err = s.conn.Begin(); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	defer tx.Rollback()
	// -> DB Insert: password data
	if _, err = tx.Exec(
		stmt.SetUserCredential,
		userUUID,
		mcf.String(),
		validFrom.UTC(),
		credExpiresAt.UTC(),
	); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> DB Update: activate user
	if _, err = tx.Exec(
		stmt.ActivateUser,
		userUUID,
	); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> DB Insert: token data
	if _, err = tx.Exec(
		stmt.InsertToken,
		token.Token,
		token.Salt,
		validFrom.UTC(),
		expiresAt.UTC(),
	); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> s.credentials Update
	s.credentials.insert(token.UserName, userUUID, validFrom.UTC(),
		credExpiresAt.UTC(), mcf)
	// -> s.tokens Update
	if err = s.tokens.insert(token.Token, token.ValidFrom, token.ExpiresAt,
		token.Salt); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// commit transaction
	if err = tx.Commit(); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> sdata = kex.EncryptAndEncode(&token)
	plain = []byte{}
	data = []byte{}
	if plain, err = json.Marshal(token); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	if err = kex.EncryptAndEncode(&plain, &data); err != nil {
		result.ServerError(err)
		goto dispatch
	}
	// -> send sdata reply
	result.Super = &msg.Supervisor{
		Verdict: 200,
		Data:    data,
	}
	result.OK()

dispatch:
	<-timer.C

conflict:
	q.Reply <- result
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>// Copyright (c) 2020 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package tally

import (
	"fmt"
	"strconv"
	"testing"
	"time"
)

func BenchmarkNameGeneration(b *testing.B) {
	root, _ := NewRootScope(ScopeOptions{
		Prefix:   "funkytown",
		Reporter: NullStatsReporter,
	}, 0)
	s := root.(*scope)
	for n := 0; n < b.N; n++ {
		s.fullyQualifiedName("take.me.to")
	}
}

func BenchmarkCounterAllocation(b *testing.B) {
	root, _ := NewRootScope(ScopeOptions{
		Prefix:   "funkytown",
		Reporter: NullStatsReporter,
	}, 0)
	s := root.(*scope)

	ids := make([]string, 0, b.N)
	for i := 0; i < b.N; i++ {
		ids = append(ids, fmt.Sprintf("take.me.to.%d", i))
	}
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		s.Counter(ids[n])
	}
}

func BenchmarkSanitizedCounterAllocation(b *testing.B) {
	root, _ := NewRootScope(ScopeOptions{
		Prefix:          "funkytown",
		Reporter:        NullStatsReporter,
		SanitizeOptions: &alphanumericSanitizerOpts,
	}, 0)
	s := root.(*scope)

	ids := make([]string, 0, b.N)
	for i := 0; i < b.N; i++ {
		ids = append(ids, fmt.Sprintf("take.me.to.%d", i))
	}
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		s.Counter(ids[n])
	}
}

func BenchmarkNameGenerationTagged(b *testing.B) {
	root, _ := NewRootScope(ScopeOptions{
		Prefix: "funkytown",
		Tags: map[string]string{
			"style":     "funky",
			"hair":      "wavy",
			"jefferson": "starship",
		},
		Reporter: NullStatsReporter,
	}, 0)
	s := root.(*scope)
	for n := 0; n < b.N; n++ {
		s.fullyQualifiedName("take.me.to")
	}
}

func BenchmarkNameGenerationNoPrefix(b *testing.B) {
	root, _ := NewRootScope(ScopeOptions{
		Reporter: NullStatsReporter,
	}, 0)
	s := root.(*scope)
	for n := 0; n < b.N; n++ {
		s.fullyQualifiedName("im.all.alone")
	}
}

func BenchmarkHistogramAllocation(b *testing.B) {
	root, _ := NewRootScope(ScopeOptions{
		Reporter: NullStatsReporter,
	}, 0)
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		root.Histogram("foo"+strconv.Itoa(i), DefaultBuckets)
	}
}

func BenchmarkHistogramExisting(b *testing.B) {
	root, _ := NewRootScope(ScopeOptions{
		Reporter: NullStatsReporter,
	}, 0)
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		root.Histogram("foo", DefaultBuckets)
	}
}

func benchmarkScopeReportingN(b *testing.B, numElems int) {
	root, _ := NewRootScope(ScopeOptions{
		Prefix:          "funkytown",
		CachedReporter:  noopCachedReporter{},
		SanitizeOptions: &alphanumericSanitizerOpts,
	}, 0)
	s := root.(*scope)

	ids := make([]string, 0, numElems)
	for i := 0; i < numElems; i++ {
		id := fmt.Sprintf("take.me.to.%d", i)
		ids = append(ids, id)
		s.Counter(id)
	}
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		s.cachedReport()
	}
}

func BenchmarkScopeReporting(b *testing.B) {
	for i := 1; i <= 1000000; i *= 10 {
		size := fmt.Sprintf("size%d", i)
		b.Run(size, func(b *testing.B) {
			benchmarkScopeReportingN(b, i)
		})
	}
}

type noopStat struct{}

func (s noopStat) ReportCount(value int64)            {}
func (s noopStat) ReportGauge(value float64)          {}
func (s noopStat) ReportTimer(interval time.Duration) {}
func (s noopStat) ValueBucket(bucketLowerBound, bucketUpperBound float64) CachedHistogramBucket {
	return s
}
func (s noopStat) DurationBucket(bucketLowerBound, bucketUpperBound time.Duration) CachedHistogramBucket {
	return s
}
func (s noopStat) ReportSamples(value int64) {}

type noopCachedReporter struct{}

func (n noopCachedReporter) Capabilities() Capabilities {
	return n
}

func (n noopCachedReporter) Reporting() bool { return true }
func (n noopCachedReporter) Tagging() bool   { return true }
func (n noopCachedReporter) Flush()          {}

func (n noopCachedReporter) ReportCounter(name string, tags map[string]string, value int64) {}
func (n noopCachedReporter) ReportGauge(name string, tags map[string]string, value float64) {}

func (n noopCachedReporter) ReportTimer(name string, tags map[string]string, interval time.Duration) {
}

func (n noopCachedReporter) ReportHistogramValueSamples(name string, tags map[string]string, buckets Buckets, bucketLowerBound float64, bucketUpperBound float64, samples int64) {
}
func (n noopCachedReporter) ReportHistogramDurationSamples(name string, tags map[string]string, buckets Buckets, bucketLowerBound time.Duration, bucketUpperBound time.Duration, samples int64) {
}

func (n noopCachedReporter) AllocateCounter(name string, tags map[string]string) CachedCount {
	return noopStat{}
}

func (n noopCachedReporter) AllocateGauge(name string, tags map[string]string) CachedGauge {
	return noopStat{}
}

func (n noopCachedReporter) AllocateTimer(name string, tags map[string]string) CachedTimer {
	return noopStat{}
}
func (n noopCachedReporter) AllocateHistogram(name string, tags map[string]string, buckets Buckets) CachedHistogram {
	return noopStat{}
}
<commit_msg>Add benchmark to measure scope.Tagged performance<commit_after>// Copyright (c) 2020 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package tally

import (
	"fmt"
	"strconv"
	"testing"
	"time"
)

func BenchmarkNameGeneration(b *testing.B) {
	root, _ := NewRootScope(ScopeOptions{
		Prefix:   "funkytown",
		Reporter: NullStatsReporter,
	}, 0)
	s := root.(*scope)
	for n := 0; n < b.N; n++ {
		s.fullyQualifiedName("take.me.to")
	}
}

func BenchmarkCounterAllocation(b *testing.B) {
	root, _ := NewRootScope(ScopeOptions{
		Prefix:   "funkytown",
		Reporter: NullStatsReporter,
	}, 0)
	s := root.(*scope)

	ids := make([]string, 0, b.N)
	for i := 0; i < b.N; i++ {
		ids = append(ids, fmt.Sprintf("take.me.to.%d", i))
	}
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		s.Counter(ids[n])
	}
}

func BenchmarkSanitizedCounterAllocation(b *testing.B) {
	root, _ := NewRootScope(ScopeOptions{
		Prefix:          "funkytown",
		Reporter:        NullStatsReporter,
		SanitizeOptions: &alphanumericSanitizerOpts,
	}, 0)
	s := root.(*scope)

	ids := make([]string, 0, b.N)
	for i := 0; i < b.N; i++ {
		ids = append(ids, fmt.Sprintf("take.me.to.%d", i))
	}
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		s.Counter(ids[n])
	}
}

func BenchmarkNameGenerationTagged(b *testing.B) {
	root, _ := NewRootScope(ScopeOptions{
		Prefix: "funkytown",
		Tags: map[string]string{
			"style":     "funky",
			"hair":      "wavy",
			"jefferson": "starship",
		},
		Reporter: NullStatsReporter,
	}, 0)
	s := root.(*scope)
	for n := 0; n < b.N; n++ {
		s.fullyQualifiedName("take.me.to")
	}
}

func BenchmarkScopeTaggedCachedSubscopes(b *testing.B) {
	root, _ := NewRootScope(ScopeOptions{
		Prefix:   "funkytown",
		Reporter: NullStatsReporter,
		Tags: map[string]string{
			"style":     "funky",
			"hair":      "wavy",
			"jefferson": "starship",
		},
	}, 0)
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		root.Tagged(map[string]string{
			"foo": "bar",
			"baz": "qux",
			"qux": "quux",
		})
	}
}

func BenchmarkScopeTaggedNoCachedSubscopes(b *testing.B) {
	root, _ := NewRootScope(ScopeOptions{
		Prefix:   "funkytown",
		Reporter: NullStatsReporter,
		Tags: map[string]string{
			"style":     "funky",
			"hair":      "wavy",
			"jefferson": "starship",
		},
	}, 0)

	values := make([]string, b.N)
	for i := 0; i < b.N; i++ {
		values[i] = strconv.Itoa(i)
	}

	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		root.Tagged(map[string]string{
			"foo": values[n],
			"baz": values[n],
			"qux": values[n],
		})
	}
}

func BenchmarkNameGenerationNoPrefix(b *testing.B) {
	root, _ := NewRootScope(ScopeOptions{
		Reporter: NullStatsReporter,
	}, 0)
	s := root.(*scope)
	for n := 0; n < b.N; n++ {
		s.fullyQualifiedName("im.all.alone")
	}
}

func BenchmarkHistogramAllocation(b *testing.B) {
	root, _ := NewRootScope(ScopeOptions{
		Reporter: NullStatsReporter,
	}, 0)
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		root.Histogram("foo"+strconv.Itoa(i), DefaultBuckets)
	}
}

func BenchmarkHistogramExisting(b *testing.B) {
	root, _ := NewRootScope(ScopeOptions{
		Reporter: NullStatsReporter,
	}, 0)
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		root.Histogram("foo", DefaultBuckets)
	}
}

func benchmarkScopeReportingN(b *testing.B, numElems int) {
	root, _ := NewRootScope(ScopeOptions{
		Prefix:          "funkytown",
		CachedReporter:  noopCachedReporter{},
		SanitizeOptions: &alphanumericSanitizerOpts,
	}, 0)
	s := root.(*scope)

	ids := make([]string, 0, numElems)
	for i := 0; i < numElems; i++ {
		id := fmt.Sprintf("take.me.to.%d", i)
		ids = append(ids, id)
		s.Counter(id)
	}
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		s.cachedReport()
	}
}

func BenchmarkScopeReporting(b *testing.B) {
	for i := 1; i <= 1000000; i *= 10 {
		size := fmt.Sprintf("size%d", i)
		b.Run(size, func(b *testing.B) {
			benchmarkScopeReportingN(b, i)
		})
	}
}

type noopStat struct{}

func (s noopStat) ReportCount(value int64)            {}
func (s noopStat) ReportGauge(value float64)          {}
func (s noopStat) ReportTimer(interval time.Duration) {}
func (s noopStat) ValueBucket(bucketLowerBound, bucketUpperBound float64) CachedHistogramBucket {
	return s
}
func (s noopStat) DurationBucket(bucketLowerBound, bucketUpperBound time.Duration) CachedHistogramBucket {
	return s
}
func (s noopStat) ReportSamples(value int64) {}

type noopCachedReporter struct{}

func (n noopCachedReporter) Capabilities() Capabilities {
	return n
}

func (n noopCachedReporter) Reporting() bool { return true }
func (n noopCachedReporter) Tagging() bool   { return true }
func (n noopCachedReporter) Flush()          {}

func (n noopCachedReporter) ReportCounter(name string, tags map[string]string, value int64) {}
func (n noopCachedReporter) ReportGauge(name string, tags map[string]string, value float64) {}

func (n noopCachedReporter) ReportTimer(name string, tags map[string]string, interval time.Duration) {
}

func (n noopCachedReporter) ReportHistogramValueSamples(name string, tags map[string]string, buckets Buckets, bucketLowerBound float64, bucketUpperBound float64, samples int64) {
}
func (n noopCachedReporter) ReportHistogramDurationSamples(name string, tags map[string]string, buckets Buckets, bucketLowerBound time.Duration, bucketUpperBound time.Duration, samples int64) {
}

func (n noopCachedReporter) AllocateCounter(name string, tags map[string]string) CachedCount {
	return noopStat{}
}

func (n noopCachedReporter) AllocateGauge(name string, tags map[string]string) CachedGauge {
	return noopStat{}
}

func (n noopCachedReporter) AllocateTimer(name string, tags map[string]string) CachedTimer {
	return noopStat{}
}
func (n noopCachedReporter) AllocateHistogram(name string, tags map[string]string, buckets Buckets) CachedHistogram {
	return noopStat{}
}
<|endoftext|>


<commit_before>// +build linux,!select

// Copyright (c) 2015, Jose Luis Aracil Gomez (pepe@diselpro.com)
// Based on Nick Patavalis (npat@efault.net) poller package.
// All rights reserved.
// Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE.txt file.

package poll

import (
	"log"
	"sync"
	"syscall"
)

var epfd int = -1
var fdm map[int]*File = map[int]*File{}
var fdmLock sync.Mutex

func init() {
	fd, err := syscall.EpollCreate1(syscall.EPOLL_CLOEXEC)
	if err != nil {
		log.Panicf("poller: EpollCreate1: %s", err.Error())
	}
	epfd = fd
	go evLoop()
}

func startTrack(fd int, write bool) {} // startTRack non needed in epoll loop
func stopTrack(fd int, write bool)  {} // stopTRack non needed in epoll loop

func register(f *File) (err error) {
	fdmLock.Lock()
	fdm[f.fd] = f
	ev := syscall.EpollEvent{
		Events: syscall.EPOLLIN |
			syscall.EPOLLOUT |
			syscall.EPOLLRDHUP |
			(syscall.EPOLLET & 0xffffffff),
		Fd: int32(f.fd)}
	err = syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, f.fd, &ev)
	fdmLock.Unlock()
	return
}

func unregister(f *File) (err error) {
	fdmLock.Lock()
	delete(fdm, f.fd)
	var ev syscall.EpollEvent
	err = syscall.EpollCtl(epfd, syscall.EPOLL_CTL_DEL, f.fd, &ev)
	fdmLock.Unlock()
	return
}

func epollEv(ev *syscall.EpollEvent, write bool) {
	var fdc *fdCtl

	fd := fdm[int(ev.Fd)]
	if fd == nil {
		// Drop event. Probably stale FD.
		return
	}
	if !write {
		fdc = &fd.r
	} else {
		fdc = &fd.w
	}
	fdc.cond.L.Lock()
	fdc.cond.Broadcast()
	fdc.cond.L.Unlock()
}

func evLoop() {
	events := make([]syscall.EpollEvent, 128)
	for {
		n, err := syscall.EpollWait(epfd, events, -1)
		if err != nil {
			if err == syscall.EINTR {
				continue
			}
			log.Panicf("poller: EpollWait: %s", err.Error())
		}
		for i := 0; i < n; i++ {
			ev := &events[i]
			if ev.Events&(syscall.EPOLLIN|
				syscall.EPOLLRDHUP|
				syscall.EPOLLHUP|
				syscall.EPOLLERR) != 0 {
				epollEv(ev, false)
			}
			if ev.Events&(syscall.EPOLLOUT|
				syscall.EPOLLHUP|
				syscall.EPOLLERR) != 0 {
				epollEv(ev, true)
			}
		}
	}
}
<commit_msg>epoll: fix data race warning<commit_after>//go:build linux && !select
// +build linux,!select

// Copyright (c) 2015, Jose Luis Aracil Gomez (pepe@diselpro.com)
// Based on Nick Patavalis (npat@efault.net) poller package.
// All rights reserved.
// Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE.txt file.

package poll

import (
	"log"
	"sync"
	"syscall"
)

var epfd int = -1
var fdm map[int]*File = map[int]*File{}
var fdmLock sync.Mutex

func init() {
	fd, err := syscall.EpollCreate1(syscall.EPOLL_CLOEXEC)
	if err != nil {
		log.Panicf("poller: EpollCreate1: %s", err.Error())
	}
	epfd = fd
	go evLoop()
}

func startTrack(fd int, write bool) {} // startTRack non needed in epoll loop
func stopTrack(fd int, write bool)  {} // stopTRack non needed in epoll loop

func register(f *File) (err error) {
	fdmLock.Lock()
	fdm[f.fd] = f
	ev := syscall.EpollEvent{
		Events: syscall.EPOLLIN |
			syscall.EPOLLOUT |
			syscall.EPOLLRDHUP |
			(syscall.EPOLLET & 0xffffffff),
		Fd: int32(f.fd)}
	err = syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, f.fd, &ev)
	fdmLock.Unlock()
	return
}

func unregister(f *File) (err error) {
	fdmLock.Lock()
	delete(fdm, f.fd)
	var ev syscall.EpollEvent
	err = syscall.EpollCtl(epfd, syscall.EPOLL_CTL_DEL, f.fd, &ev)
	fdmLock.Unlock()
	return
}

func epollEv(ev *syscall.EpollEvent, write bool) {
	var fdc *fdCtl
	fdmLock.Lock()
	fd := fdm[int(ev.Fd)]
	fdmLock.Unlock()
	if fd == nil {
		// Drop event. Probably stale FD.
		return
	}
	if !write {
		fdc = &fd.r
	} else {
		fdc = &fd.w
	}
	fdc.cond.L.Lock()
	fdc.cond.Broadcast()
	fdc.cond.L.Unlock()
}

func evLoop() {
	events := make([]syscall.EpollEvent, 128)
	for {
		n, err := syscall.EpollWait(epfd, events, -1)
		if err != nil {
			if err == syscall.EINTR {
				continue
			}
			log.Panicf("poller: EpollWait: %s", err.Error())
		}
		for i := 0; i < n; i++ {
			ev := &events[i]
			if ev.Events&(syscall.EPOLLIN|
				syscall.EPOLLRDHUP|
				syscall.EPOLLHUP|
				syscall.EPOLLERR) != 0 {
				epollEv(ev, false)
			}
			if ev.Events&(syscall.EPOLLOUT|
				syscall.EPOLLHUP|
				syscall.EPOLLERR) != 0 {
				epollEv(ev, true)
			}
		}
	}
}
<|endoftext|>


<commit_before>package sentry

import (
	"time"

	"github.com/getsentry/sentry-go"
	"github.com/sirupsen/logrus"
)

var (
	levelsMap = map[logrus.Level]sentry.Level{
		logrus.PanicLevel: sentry.LevelFatal,
		logrus.FatalLevel: sentry.LevelFatal,
		logrus.ErrorLevel: sentry.LevelError,
		logrus.WarnLevel:  sentry.LevelWarning,
		logrus.InfoLevel:  sentry.LevelInfo,
		logrus.DebugLevel: sentry.LevelDebug,
		logrus.TraceLevel: sentry.LevelDebug,
	}
)

type Options sentry.ClientOptions

type Hook struct {
	client       *sentry.Client
	levels       []logrus.Level
	tags         map[string]string
	release      string
	environment  string
	prefix       string
	flushTimeout time.Duration
}

func (hook *Hook) Levels() []logrus.Level {
	return hook.levels
}

func (hook *Hook) Fire(entry *logrus.Entry) error {
	exceptions := []sentry.Exception{}

	if err, ok := entry.Data[logrus.ErrorKey].(error); ok && err != nil {
		stacktrace := sentry.ExtractStacktrace(err)
		if stacktrace == nil {
			stacktrace = sentry.NewStacktrace()
		}
		exceptions = append(exceptions, sentry.Exception{
			Type:       entry.Message,
			Value:      err.Error(),
			Stacktrace: stacktrace,
		})
	}

	event := sentry.Event{
		Level:       levelsMap[entry.Level],
		Message:     hook.prefix + entry.Message,
		Extra:       map[string]interface{}(entry.Data),
		Tags:        hook.tags,
		Environment: hook.environment,
		Release:     hook.release,
		Exception:   exceptions,
	}

	hub := sentry.CurrentHub()
	hook.client.CaptureEvent(&event, nil, hub.Scope())

	return nil
}

func (hook *Hook) SetPrefix(prefix string) {
	hook.prefix = prefix
}

func (hook *Hook) SetTags(tags map[string]string) {
	hook.tags = tags
}

func (hook *Hook) AddTag(key, value string) {
	hook.tags[key] = value
}

func (hook *Hook) SetRelease(release string) {
	hook.release = release
}

func (hook *Hook) SetEnvironment(environment string) {
	hook.environment = environment
}

func (hook *Hook) SetFlushTimeout(timeout time.Duration) {
	hook.flushTimeout = timeout
}

func (hook *Hook) Flush() {
	hook.client.Flush(flushTimeout)
}

func NewHook(options Options, levels ...logrus.Level) (*Hook, error) {
	client, err := sentry.NewClient(sentry.ClientOptions(options))
	if err != nil {
		return nil, err
	}

	hook := Hook{
		client: client,
		levels: levels,
		tags:   map[string]string{},
	}
	if len(hook.levels) == 0 {
		hook.levels = logrus.AllLevels
	}

	return &hook, nil
}
<commit_msg>Update sentry.go<commit_after>package sentry

import (
	"time"

	"github.com/getsentry/sentry-go"
	"github.com/sirupsen/logrus"
)

var (
	levelsMap = map[logrus.Level]sentry.Level{
		logrus.PanicLevel: sentry.LevelFatal,
		logrus.FatalLevel: sentry.LevelFatal,
		logrus.ErrorLevel: sentry.LevelError,
		logrus.WarnLevel:  sentry.LevelWarning,
		logrus.InfoLevel:  sentry.LevelInfo,
		logrus.DebugLevel: sentry.LevelDebug,
		logrus.TraceLevel: sentry.LevelDebug,
	}
)

type Options sentry.ClientOptions

type Hook struct {
	client       *sentry.Client
	levels       []logrus.Level
	tags         map[string]string
	release      string
	environment  string
	prefix       string
	flushTimeout time.Duration
}

func (hook *Hook) Levels() []logrus.Level {
	return hook.levels
}

func (hook *Hook) Fire(entry *logrus.Entry) error {
	exceptions := []sentry.Exception{}

	if err, ok := entry.Data[logrus.ErrorKey].(error); ok && err != nil {
		stacktrace := sentry.ExtractStacktrace(err)
		if stacktrace == nil {
			stacktrace = sentry.NewStacktrace()
		}
		exceptions = append(exceptions, sentry.Exception{
			Type:       entry.Message,
			Value:      err.Error(),
			Stacktrace: stacktrace,
		})
	}

	event := sentry.Event{
		Level:       levelsMap[entry.Level],
		Message:     hook.prefix + entry.Message,
		Extra:       map[string]interface{}(entry.Data),
		Tags:        hook.tags,
		Environment: hook.environment,
		Release:     hook.release,
		Exception:   exceptions,
	}

	hub := sentry.CurrentHub()
	hook.client.CaptureEvent(&event, nil, hub.Scope())

	return nil
}

func (hook *Hook) SetPrefix(prefix string) {
	hook.prefix = prefix
}

func (hook *Hook) SetTags(tags map[string]string) {
	hook.tags = tags
}

func (hook *Hook) AddTag(key, value string) {
	hook.tags[key] = value
}

func (hook *Hook) SetRelease(release string) {
	hook.release = release
}

func (hook *Hook) SetEnvironment(environment string) {
	hook.environment = environment
}

func (hook *Hook) SetFlushTimeout(timeout time.Duration) {
	hook.flushTimeout = timeout
}

func (hook *Hook) Flush() {
	hook.client.Flush(hook.flushTimeout)
}

func NewHook(options Options, levels ...logrus.Level) (*Hook, error) {
	client, err := sentry.NewClient(sentry.ClientOptions(options))
	if err != nil {
		return nil, err
	}

	hook := Hook{
		client: client,
		levels: levels,
		tags:   map[string]string{},
	}
	if len(hook.levels) == 0 {
		hook.levels = logrus.AllLevels
	}

	return &hook, nil
}
<|endoftext|>


<commit_before>package powermux

import (
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
)

type dummyHandler string

func (h dummyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, string(h))
}

var (
	rightHandler = dummyHandler("right")
	wrongHandler = dummyHandler("wrong")
)

// Ensures that parameter routes have lower precedence than absolute routes
func TestServeMux_ParamPrecedence(t *testing.T) {
	s := NewServeMux()

	s.Route("/users/:id/info").Get(wrongHandler)
	s.Route("/users/jim/info").Get(rightHandler)
	s.Route("/users/:id/detail").Get(wrongHandler)

	req := httptest.NewRequest(http.MethodGet, "/users/jim/info", nil)
	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if GetPathParam(req, "id") != "jim" {
		t.Error("Wrong path param returned")
	}

	if path != "/users/jim/info" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensures that wildcards have the lowest of all precedences
func TestServeMux_WildcardPrecedence(t *testing.T) {
	s := NewServeMux()

	s.Route("/users/*").Get(wrongHandler)
	s.Route("/users/john").Get(rightHandler)

	req := httptest.NewRequest(http.MethodGet, "/users/john", nil)
	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if path != "/users/john" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensures the wildcard handler isn't called when a path param was available
func TestServeMux_WildcardPathPrecedence(t *testing.T) {
	s := NewServeMux()

	s.Route("/users/*").Get(wrongHandler)
	s.Route("/users/:id").Get(rightHandler)

	req := httptest.NewRequest(http.MethodGet, "/users/john", nil)
	h, _ := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if GetPathParam(req, "id") != "john" {
		t.Error("Wrong path param returned")
	}

}

// Ensures trailing slash redirects are working
func TestServeMux_RedirectSlash(t *testing.T) {
	s := NewServeMux()

	req := httptest.NewRequest(http.MethodGet, "/users/", nil)
	rec := httptest.NewRecorder()

	s.ServeHTTP(rec, req)

	if rec.Code != http.StatusPermanentRedirect {
		t.Error("Not redirected")
	}

	if rec.HeaderMap.Get("Location") != "/users" {
		t.Error("Mis-redirected")
	}
}

// Ensures we don't redirect the root
func TestServeMux_RedirectRoot(t *testing.T) {
	s := NewServeMux()

	req := httptest.NewRequest(http.MethodGet, "/", nil)
	rec := httptest.NewRecorder()

	s.ServeHTTP(rec, req)

	if rec.Code == http.StatusPermanentRedirect {
		t.Error("Redirected")
	}
}

// Ensure the correct path is matched 1 level
func TestServeMux_HandleCorrectRoute(t *testing.T) {
	s := NewServeMux()

	s.Route("/a").Get(rightHandler)
	s.Route("/b").Get(wrongHandler)

	req := httptest.NewRequest(http.MethodGet, "/a", nil)

	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if path != "/a" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensure the correct path is matched at two levels
func TestServeMux_HandleCorrectRouteAfterParam(t *testing.T) {
	s := NewServeMux()

	s.Route("/base/:id/a").Get(rightHandler)
	s.Route("/base/:id/b").Get(wrongHandler)

	req := httptest.NewRequest(http.MethodGet, "/base/llama/a", nil)

	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler retured")
	}

	if GetPathParam(req, "id") != "llama" {
		t.Error("Wrong path param returned")
	}

	if path != "/base/:id/a" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensure the correct method is matched
func TestServeMux_HandleCorrectMethod(t *testing.T) {
	s := NewServeMux()

	s.Route("/a").Post(rightHandler)
	s.Route("/a").Get(wrongHandler)

	req := httptest.NewRequest(http.MethodPost, "/a", nil)

	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if path != "/a" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensure the correct method is matched for any
func TestServeMux_HandleCorrectMethodAny(t *testing.T) {
	s := NewServeMux()

	s.Route("/a").Post(wrongHandler)
	s.Route("/a").Get(wrongHandler)
	s.Route("/a").Any(rightHandler)

	req := httptest.NewRequest(http.MethodDelete, "/a", nil)

	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if path != "/a" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensure the correct method is matched for head
func TestServeMux_HandleCorrectMethodHead(t *testing.T) {
	s := NewServeMux()

	s.Route("/a").Post(wrongHandler)
	s.Route("/a").Get(rightHandler)

	req := httptest.NewRequest(http.MethodHead, "/a", nil)

	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if path != "/a" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensure a wildcard matches
func TestServeMux_HandleWildcard(t *testing.T) {
	s := NewServeMux()

	s.Route("/a/*").Get(rightHandler)
	s.Route("/b").Get(wrongHandler)

	req := httptest.NewRequest(http.MethodGet, "/a/llama", nil)

	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if path != "/a/*" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensure a wildcard matches at depth
func TestServeMux_HandleWildcardDepth(t *testing.T) {
	s := NewServeMux()

	s.Route("/a/*").Get(rightHandler)
	s.Route("/b").Get(wrongHandler)

	req := httptest.NewRequest(http.MethodGet, "/a/llama/4/5", nil)

	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if path != "/a/*" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensure order doesn't matter
func TestServeMux_HandleOrder(t *testing.T) {
	s := NewServeMux()

	s.Route("/a").Get(wrongHandler)
	s.Route("/b").Get(rightHandler)

	req := httptest.NewRequest(http.MethodGet, "/b", nil)

	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if path != "/b" {
		t.Errorf("Wrong string path: %s", path)
	}
}

func TestServeMux_HandleOptionsAtDepth(t *testing.T) {
	s := NewServeMux()

	s.Route("/a").Options(rightHandler)
	s.Route("/a/b").Get(wrongHandler)

	req := httptest.NewRequest(http.MethodOptions, "/a/b", nil)

	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if path != "/a/b" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensure routing is not performed on decoded path components
func TestServeMux_Encoded(t *testing.T) {
	s := NewServeMux()

	s.Route("/users/:id/info").Get(rightHandler)

	req := httptest.NewRequest(http.MethodGet, "/users/ji%2Fm/info", nil)
	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if GetPathParam(req, "id") != "ji/m" {
		t.Error("Wrong path param returned")
	}

	if path != "/users/:id/info" {
		t.Errorf("Wrong string path: %s", path)
	}
}
<commit_msg>fix first test case: path param should not match<commit_after>package powermux

import (
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
)

type dummyHandler string

func (h dummyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, string(h))
}

var (
	rightHandler = dummyHandler("right")
	wrongHandler = dummyHandler("wrong")
)

// Ensures that parameter routes have lower precedence than absolute routes
func TestServeMux_ParamPrecedence(t *testing.T) {
	s := NewServeMux()

	s.Route("/users/:id/info").Get(wrongHandler)
	s.Route("/users/jim/info").Get(rightHandler)
	s.Route("/users/:id/detail").Get(wrongHandler)

	req := httptest.NewRequest(http.MethodGet, "/users/jim/info", nil)
	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if GetPathParam(req, "id") == "jim" {
		t.Error("Path param populated incorrectly")
	}

	if path != "/users/jim/info" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensures that wildcards have the lowest of all precedences
func TestServeMux_WildcardPrecedence(t *testing.T) {
	s := NewServeMux()

	s.Route("/users/*").Get(wrongHandler)
	s.Route("/users/john").Get(rightHandler)

	req := httptest.NewRequest(http.MethodGet, "/users/john", nil)
	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if path != "/users/john" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensures the wildcard handler isn't called when a path param was available
func TestServeMux_WildcardPathPrecedence(t *testing.T) {
	s := NewServeMux()

	s.Route("/users/*").Get(wrongHandler)
	s.Route("/users/:id").Get(rightHandler)

	req := httptest.NewRequest(http.MethodGet, "/users/john", nil)
	h, _ := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if GetPathParam(req, "id") != "john" {
		t.Error("Wrong path param returned")
	}

}

// Ensures trailing slash redirects are working
func TestServeMux_RedirectSlash(t *testing.T) {
	s := NewServeMux()

	req := httptest.NewRequest(http.MethodGet, "/users/", nil)
	rec := httptest.NewRecorder()

	s.ServeHTTP(rec, req)

	if rec.Code != http.StatusPermanentRedirect {
		t.Error("Not redirected")
	}

	if rec.HeaderMap.Get("Location") != "/users" {
		t.Error("Mis-redirected")
	}
}

// Ensures we don't redirect the root
func TestServeMux_RedirectRoot(t *testing.T) {
	s := NewServeMux()

	req := httptest.NewRequest(http.MethodGet, "/", nil)
	rec := httptest.NewRecorder()

	s.ServeHTTP(rec, req)

	if rec.Code == http.StatusPermanentRedirect {
		t.Error("Redirected")
	}
}

// Ensure the correct path is matched 1 level
func TestServeMux_HandleCorrectRoute(t *testing.T) {
	s := NewServeMux()

	s.Route("/a").Get(rightHandler)
	s.Route("/b").Get(wrongHandler)

	req := httptest.NewRequest(http.MethodGet, "/a", nil)

	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if path != "/a" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensure the correct path is matched at two levels
func TestServeMux_HandleCorrectRouteAfterParam(t *testing.T) {
	s := NewServeMux()

	s.Route("/base/:id/a").Get(rightHandler)
	s.Route("/base/:id/b").Get(wrongHandler)

	req := httptest.NewRequest(http.MethodGet, "/base/llama/a", nil)

	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler retured")
	}

	if GetPathParam(req, "id") != "llama" {
		t.Error("Wrong path param returned")
	}

	if path != "/base/:id/a" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensure the correct method is matched
func TestServeMux_HandleCorrectMethod(t *testing.T) {
	s := NewServeMux()

	s.Route("/a").Post(rightHandler)
	s.Route("/a").Get(wrongHandler)

	req := httptest.NewRequest(http.MethodPost, "/a", nil)

	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if path != "/a" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensure the correct method is matched for any
func TestServeMux_HandleCorrectMethodAny(t *testing.T) {
	s := NewServeMux()

	s.Route("/a").Post(wrongHandler)
	s.Route("/a").Get(wrongHandler)
	s.Route("/a").Any(rightHandler)

	req := httptest.NewRequest(http.MethodDelete, "/a", nil)

	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if path != "/a" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensure the correct method is matched for head
func TestServeMux_HandleCorrectMethodHead(t *testing.T) {
	s := NewServeMux()

	s.Route("/a").Post(wrongHandler)
	s.Route("/a").Get(rightHandler)

	req := httptest.NewRequest(http.MethodHead, "/a", nil)

	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if path != "/a" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensure a wildcard matches
func TestServeMux_HandleWildcard(t *testing.T) {
	s := NewServeMux()

	s.Route("/a/*").Get(rightHandler)
	s.Route("/b").Get(wrongHandler)

	req := httptest.NewRequest(http.MethodGet, "/a/llama", nil)

	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if path != "/a/*" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensure a wildcard matches at depth
func TestServeMux_HandleWildcardDepth(t *testing.T) {
	s := NewServeMux()

	s.Route("/a/*").Get(rightHandler)
	s.Route("/b").Get(wrongHandler)

	req := httptest.NewRequest(http.MethodGet, "/a/llama/4/5", nil)

	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if path != "/a/*" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensure order doesn't matter
func TestServeMux_HandleOrder(t *testing.T) {
	s := NewServeMux()

	s.Route("/a").Get(wrongHandler)
	s.Route("/b").Get(rightHandler)

	req := httptest.NewRequest(http.MethodGet, "/b", nil)

	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if path != "/b" {
		t.Errorf("Wrong string path: %s", path)
	}
}

func TestServeMux_HandleOptionsAtDepth(t *testing.T) {
	s := NewServeMux()

	s.Route("/a").Options(rightHandler)
	s.Route("/a/b").Get(wrongHandler)

	req := httptest.NewRequest(http.MethodOptions, "/a/b", nil)

	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if path != "/a/b" {
		t.Errorf("Wrong string path: %s", path)
	}
}

// Ensure routing is not performed on decoded path components
func TestServeMux_Encoded(t *testing.T) {
	s := NewServeMux()

	s.Route("/users/:id/info").Get(rightHandler)

	req := httptest.NewRequest(http.MethodGet, "/users/ji%2Fm/info", nil)
	h, path := s.Handler(req)

	if h != rightHandler {
		t.Error("Wrong handler returned")
	}

	if GetPathParam(req, "id") != "ji/m" {
		t.Error("Wrong path param returned")
	}

	if path != "/users/:id/info" {
		t.Errorf("Wrong string path: %s", path)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/DataDog/dd-trace-go/tracer"
	"github.com/Sirupsen/logrus"
	"github.com/flachnetz/dd-zipkin-proxy"
	"github.com/openzipkin/zipkin-go-opentracing/_thrift/gen-go/zipkincore"
	"regexp"
	"strconv"
	"strings"
)

func main() {
	converter := DefaultSpanConverter{}
	zipkinproxy.Main(converter.Convert)
}

var reHash = regexp.MustCompile("\\b(?:[a-f0-9]{32}|[a-f0-9-]{8}-[a-f0-9-]{4}-[a-f0-9-]{4}-[a-f0-9-]{4}-[a-f0-9-]{12})\\b")
var reNumber = regexp.MustCompile("\\b[0-9]{2,}\\b")
var reIwgHash = regexp.MustCompile("iwg\\.[A-Za-z0-9]{12}\\b")

func SimplifyResourceName(value string) string {
	// check if we need to apply the regexp by checking if a match is possible or not
	digitCount := 0
	hashCharCount := 0
	for _, char := range value {
		isDigit := char >= '0' && char <= '9'
		if isDigit {
			digitCount++
		}

		if isDigit || char >= 'a' && char <= 'f' {
			hashCharCount++
		}
	}

	// only search for hash, if we have enough chars for it
	if hashCharCount >= 32 {
		value = reHash.ReplaceAllString(value, "_HASH_")
	}

	// only replace numbers, if we have enough digits for a match
	if digitCount >= 2 {
		value = reNumber.ReplaceAllString(value, "_NUMBER_")
	}

	if strings.HasPrefix(value, "iwg.") {
		value = reIwgHash.ReplaceAllString(value, "iwg._HASH_")
	}

	return value
}

type DefaultSpanConverter struct {
	current  map[uint64]string
	previous map[uint64]string
}

func (converter *DefaultSpanConverter) Convert(span *zipkincore.Span) *tracer.Span {
	name := SimplifyResourceName(span.Name)

	converted := &tracer.Span{
		SpanID:   uint64(span.ID),
		ParentID: uint64(span.GetParentID()),
		TraceID:  uint64(span.TraceID),
		Name:     name,
		Resource: name,
		Start:    1000 * span.GetTimestamp(),
		Duration: 1000 * span.GetDuration(),
		Sampled:  true,
	}

	// datadog traces use a trace of 0
	if converted.ParentID == converted.SpanID {
		converted.ParentID = 0
	}

	// split "http:/some/url" or "get:/some/url"
	for _, prefix := range []string{"http:/", "get:/", "post:/"} {
		if strings.HasPrefix(converted.Name, prefix) {
			converted.Resource = converted.Name[len(prefix)-1:]
			converted.Name = prefix[:len(prefix)-2]
			break
		}
	}

	// convert binary annotations (like tags)
	if len(span.BinaryAnnotations) > 0 {
		converted.Meta = make(map[string]string, len(span.BinaryAnnotations))
		for _, an := range span.BinaryAnnotations {
			if an.AnnotationType == zipkincore.AnnotationType_STRING {
				key := an.Key

				// rename keys to better match the datadog one.
				switch key {
				case "http.status":
					key = "http.status_code"

				case "client.url":
					key = "http.url"
				}

				converted.Meta[key] = string(an.Value)
			}

			if an.Host != nil && an.Host.ServiceName != "" {
				converted.Service = an.Host.ServiceName
			}
		}

		if url := converted.Meta["http.url"]; url != "" {
			converted.Resource = SimplifyResourceName(url)
		}

		if status := converted.Meta["http.status_code"]; status != "" {
			if len(status) > 0 && '3' <= status[0] && status[0] <= '9' {
				if statusValue, err := strconv.Atoi(status); err == nil && statusValue >= 400 {
					converted.Error = int32(statusValue)
				}
			}
		}
	}

	// try to get the service from the cs/cr or sr/ss annotations
	var minTimestamp, maxTimestamp int64
	for _, an := range span.Annotations {
		if an.Host != nil && an.Host.ServiceName != "" {
			converted.Service = an.Host.ServiceName
		}

		if an.Timestamp < minTimestamp || minTimestamp == 0 {
			minTimestamp = an.Timestamp
		}

		if an.Timestamp > maxTimestamp {
			maxTimestamp = an.Timestamp
		}
	}

	if converted.Start == 0 {
		converted.Start = 1000 * minTimestamp
		converted.Duration = 1000 * (maxTimestamp - minTimestamp)
	}

	// simplify some names
	if strings.HasPrefix(converted.Name, "http:") {
		converted.Service = converted.Name[5:]
	}

	if converted.Name == "transaction" {
		converted.Service = "oracle"
	}

	if strings.HasPrefix(converted.Name, "redis:") {
		converted.Service = "redis"

		if key := converted.Meta["redis.key"]; key != "" {
			converted.Resource = SimplifyResourceName(key)

			// the hash is not really important later on. lets not spam datadog with it.
			delete(converted.Meta, "redis.key")
		}
	}

	if converted.Service == "core-services" {

		if strings.Contains(converted.Meta["http.url"], ":6080/") {
			converted.Service = "iwg-restrictor"
			converted.Name = getNameFromResource(converted.Resource, converted.Name)
		}

		if strings.Contains(converted.Meta["http.url"], ":2080/") {
			converted.Service = "iwg-game"
			converted.Name = "iwg"
		}

	}

	// If we could not get a service, we'll try to get it from the parent span.
	// Try first in the current map, then in the previous one.
	if converted.Service == "" {
		parentService := converter.current[converted.ParentID]
		if parentService != "" {
			converted.Service = parentService
		} else {
			parentService = converter.previous[converted.ParentID]
			if parentService != "" {
				converted.Service = parentService
			}
		}

		// if we did not get a service, use a fallback
		if converted.Service == "" {
			logrus.Warnf("Could not get a service for this span: %+v", span)
			converted.Service = "unknown"
		}
	}

	if converted.Name == "" && converted.Meta["lc"] != "" {
		converted.Name = converted.Meta["lc"]
	}

	// guess a type for the datadog ui.
	if converted.Name == "transaction" || converted.Service == "redis" {
		converted.Type = "db"
	} else {
		converted.Type = "http"
	}

	if converted.Name == "hystrix" {
		converted.Resource = converted.Meta["thread"]
	}

	if converted.Name == "" {
		logrus.Warnf("Could not get a name for this span: %+v converted: %+v", span, converted)
	}

	// initialize history maps for span -> parent assignment
	if len(converter.current) >= 40000 || converter.current == nil {
		converter.previous = converter.current
		converter.current = make(map[uint64]string, 40000)
	}

	// remember the service for a short while
	converter.current[converted.SpanID] = converted.Service

	return converted
}

func getNameFromResource(resource string, fallback string) string {
	splittedUrl := strings.Split(strings.Trim(resource, "/"), "/")
	if len(splittedUrl) == 1 && splittedUrl[0] != "" {
		return splittedUrl[0]
	} else if len(splittedUrl) > 1 {
		return splittedUrl[len(splittedUrl)-2] + "/" + splittedUrl[len(splittedUrl)-1]
	}
	return fallback
}
<commit_msg>Simplify converted.Resource<commit_after>package main

import (
	"github.com/DataDog/dd-trace-go/tracer"
	"github.com/Sirupsen/logrus"
	"github.com/flachnetz/dd-zipkin-proxy"
	"github.com/openzipkin/zipkin-go-opentracing/_thrift/gen-go/zipkincore"
	"regexp"
	"strconv"
	"strings"
)

func main() {
	converter := DefaultSpanConverter{}
	zipkinproxy.Main(converter.Convert)
}

var reHash = regexp.MustCompile("\\b(?:[a-f0-9]{32}|[a-f0-9-]{8}-[a-f0-9-]{4}-[a-f0-9-]{4}-[a-f0-9-]{4}-[a-f0-9-]{12})\\b")
var reNumber = regexp.MustCompile("\\b[0-9]{2,}\\b")
var reIwgHash = regexp.MustCompile("iwg\\.[A-Za-z0-9]{12}\\b")

func SimplifyResourceName(value string) string {
	// check if we need to apply the regexp by checking if a match is possible or not
	digitCount := 0
	hashCharCount := 0
	for _, char := range value {
		isDigit := char >= '0' && char <= '9'
		if isDigit {
			digitCount++
		}

		if isDigit || char >= 'a' && char <= 'f' {
			hashCharCount++
		}
	}

	// only search for hash, if we have enough chars for it
	if hashCharCount >= 32 {
		value = reHash.ReplaceAllString(value, "_HASH_")
	}

	// only replace numbers, if we have enough digits for a match
	if digitCount >= 2 {
		value = reNumber.ReplaceAllString(value, "_NUMBER_")
	}

	if strings.HasPrefix(value, "iwg.") {
		value = reIwgHash.ReplaceAllString(value, "iwg._HASH_")
	}

	return value
}

type DefaultSpanConverter struct {
	current  map[uint64]string
	previous map[uint64]string
}

func (converter *DefaultSpanConverter) Convert(span *zipkincore.Span) *tracer.Span {
	name := SimplifyResourceName(span.Name)

	converted := &tracer.Span{
		SpanID:   uint64(span.ID),
		ParentID: uint64(span.GetParentID()),
		TraceID:  uint64(span.TraceID),
		Name:     name,
		Resource: name,
		Start:    1000 * span.GetTimestamp(),
		Duration: 1000 * span.GetDuration(),
		Sampled:  true,
	}

	// datadog traces use a trace of 0
	if converted.ParentID == converted.SpanID {
		converted.ParentID = 0
	}

	// split "http:/some/url" or "get:/some/url"
	for _, prefix := range []string{"http:/", "get:/", "post:/"} {
		if strings.HasPrefix(converted.Name, prefix) {
			converted.Resource = converted.Name[len(prefix)-1:]
			converted.Name = prefix[:len(prefix)-2]
			break
		}
	}

	// convert binary annotations (like tags)
	if len(span.BinaryAnnotations) > 0 {
		converted.Meta = make(map[string]string, len(span.BinaryAnnotations))
		for _, an := range span.BinaryAnnotations {
			if an.AnnotationType == zipkincore.AnnotationType_STRING {
				key := an.Key

				// rename keys to better match the datadog one.
				switch key {
				case "http.status":
					key = "http.status_code"

				case "client.url":
					key = "http.url"
				}

				converted.Meta[key] = string(an.Value)
			}

			if an.Host != nil && an.Host.ServiceName != "" {
				converted.Service = an.Host.ServiceName
			}
		}

		if url := converted.Meta["http.url"]; url != "" {
			converted.Resource = SimplifyResourceName(url)
		}

		if status := converted.Meta["http.status_code"]; status != "" {
			if len(status) > 0 && '3' <= status[0] && status[0] <= '9' {
				if statusValue, err := strconv.Atoi(status); err == nil && statusValue >= 400 {
					converted.Error = int32(statusValue)
				}
			}
		}
	}

	// try to get the service from the cs/cr or sr/ss annotations
	var minTimestamp, maxTimestamp int64
	for _, an := range span.Annotations {
		if an.Host != nil && an.Host.ServiceName != "" {
			converted.Service = an.Host.ServiceName
		}

		if an.Timestamp < minTimestamp || minTimestamp == 0 {
			minTimestamp = an.Timestamp
		}

		if an.Timestamp > maxTimestamp {
			maxTimestamp = an.Timestamp
		}
	}

	if converted.Start == 0 {
		converted.Start = 1000 * minTimestamp
		converted.Duration = 1000 * (maxTimestamp - minTimestamp)
	}

	// simplify some names
	if strings.HasPrefix(converted.Name, "http:") {
		converted.Service = converted.Name[5:]
	}

	if converted.Name == "transaction" {
		converted.Service = "oracle"
	}

	if strings.HasPrefix(converted.Name, "redis:") {
		converted.Service = "redis"

		if key := converted.Meta["redis.key"]; key != "" {
			converted.Resource = SimplifyResourceName(key)

			// the hash is not really important later on. lets not spam datadog with it.
			delete(converted.Meta, "redis.key")
		}
	}

	if converted.Service == "core-services" {
		if strings.Contains(converted.Meta["http.url"], ":6080/") {
			converted.Service = "iwg-restrictor"
			converted.Name = iwgNameFromResource(converted.Resource, converted.Name)
			converted.Resource = dropDomainFromUrl(converted.Resource)
		}

		if strings.Contains(converted.Meta["http.url"], ":2080/") {
			converted.Service = "iwg-game"
			converted.Name = "iwg"
			converted.Resource = dropDomainFromUrl(converted.Resource)
		}
	}

	// If we could not get a service, we'll try to get it from the parent span.
	// Try first in the current map, then in the previous one.
	if converted.Service == "" {
		parentService := converter.current[converted.ParentID]
		if parentService != "" {
			converted.Service = parentService
		} else {
			parentService = converter.previous[converted.ParentID]
			if parentService != "" {
				converted.Service = parentService
			}
		}

		// if we did not get a service, use a fallback
		if converted.Service == "" {
			logrus.Warnf("Could not get a service for this span: %+v", span)
			converted.Service = "unknown"
		}
	}

	if converted.Name == "" && converted.Meta["lc"] != "" {
		converted.Name = converted.Meta["lc"]
	}

	// guess a type for the datadog ui.
	if converted.Name == "transaction" || converted.Service == "redis" {
		converted.Type = "db"
	} else {
		converted.Type = "http"
	}

	if converted.Name == "hystrix" {
		converted.Resource = converted.Meta["thread"]
	}

	if converted.Name == "" {
		logrus.Warnf("Could not get a name for this span: %+v converted: %+v", span, converted)
	}

	// initialize history maps for span -> parent assignment
	if len(converter.current) >= 40000 || converter.current == nil {
		converter.previous = converter.current
		converter.current = make(map[uint64]string, 40000)
	}

	// remember the service for a short while
	converter.current[converted.SpanID] = converted.Service

	return converted
}

func dropDomainFromUrl(url string) string {
	switch {
	case strings.HasPrefix(url, "http://"):
		index := strings.IndexRune(url[7:], '/')
		return url[7+index:]

	case strings.HasPrefix(url, "https://"):
		index := strings.IndexRune(url[8:], '/')
		return url[8+index:]
	}

	return url
}

func iwgNameFromResource(resource string, fallback string) string {
	splittedUrl := strings.Split(strings.Trim(resource, "/"), "/")
	if len(splittedUrl) == 1 && splittedUrl[0] != "" {
		return splittedUrl[0]
	} else if len(splittedUrl) > 1 {
		return splittedUrl[len(splittedUrl)-2] + "/" + splittedUrl[len(splittedUrl)-1]
	}
	return fallback
}
<|endoftext|>


<commit_before>package main

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

	"github.com/aws/aws-sdk-go/aws/defaults"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
	"github.com/ejholmes/cloudwatch"

	"code.google.com/p/go-uuid/uuid"
)

func main() {
	g := cloudwatch.NewGroup("test", cloudwatchlogs.New(defaults.DefaultConfig))

	stream := uuid.New()

	w, err := g.Create(stream)
	if err != nil {
		log.Fatal(err)
	}

	r, err := g.Open(stream)
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		var i int
		for {
			i++
			<-time.After(time.Second / 30)
			_, err := fmt.Fprintf(w, "Line %d\n", i)
			if err != nil {
				log.Println(err)
			}
		}
	}()

	io.Copy(os.Stdout, r)
}
<commit_msg>Fix import of UUID<commit_after>package main

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

	"github.com/aws/aws-sdk-go/aws/defaults"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
	"github.com/ejholmes/cloudwatch"
	"github.com/pborman/uuid"
)

func main() {
	g := cloudwatch.NewGroup("test", cloudwatchlogs.New(defaults.DefaultConfig))

	stream := uuid.New()

	w, err := g.Create(stream)
	if err != nil {
		log.Fatal(err)
	}

	r, err := g.Open(stream)
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		var i int
		for {
			i++
			<-time.After(time.Second / 30)
			_, err := fmt.Fprintf(w, "Line %d\n", i)
			if err != nil {
				log.Println(err)
			}
		}
	}()

	io.Copy(os.Stdout, r)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"image"
	"image/gif"
	"image/jpeg"
	"net/http"
	"os"
	"strings"

	marvel "github.com/ImJasonH/go-marvel"
)

var (
	seriesID = flag.Int("series", 2258, "Series ID (default: Uncanny X-Men)")
	apiKey   = flag.String("pub", "", "Public API key")
	secret   = flag.String("priv", "", "Private API secret")
)

func main() {
	flag.Parse()

	c := marvel.Client{
		PublicKey:  *apiKey,
		PrivateKey: *secret,
	}

	offset := 0
	limit := 100
	imgs := []image.Image{}
	for {
		r, err := c.SingleSeries(*seriesID).Comics(marvel.ComicsParams{
			CommonParams: marvel.CommonParams{
				Offset: offset,
				Limit:  limit,
			},
		})
		if err != nil {
			panic(err)
		}
		for _, iss := range r.Data.Results {
			url := iss.Thumbnail.URL(marvel.PortraitIncredible)
			img := fetchImage(url)
			if img != nil {
				imgs = append(imgs, img)
				fmt.Printf("fetched %v - %s\n", *iss.IssueNumber, url)
			} else {
				fmt.Printf("skipped %s\n", url)
			}
		}
		if len(r.Data.Results) < limit {
			break
		}
		offset += limit
	}

	if err := writeGIF(fmt.Sprintf("%d.gif", *seriesID), imgs); err != nil {
		fmt.Printf("error: %v", err)
	}
}

func fetchImage(url string) image.Image {
	if strings.Contains(url, "image_not_available") {
		return nil
	}

	resp, err := http.Get(url)
	if err != nil {
		return nil
	}
	if resp.StatusCode != http.StatusOK {
		return nil
	}
	defer resp.Body.Close()

	b := bufio.NewReaderSize(resp.Body, 1)
	if _, err := b.Peek(1); err == bufio.ErrBufferFull {
		return nil
	}

	img, err := jpeg.Decode(b)
	if err != nil {
		return nil
	}
	return img
}

func writeGIF(filename string, imgs []image.Image) error {
	f, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer f.Close()

	g := gif.GIF{
		Image: make([]*image.Paletted, len(imgs)),
		Delay: make([]int, len(imgs)),
	}
	b := make([]byte, 0, 1024)
	for i, img := range imgs {
		buf := bytes.NewBuffer(b)
		if err = gif.Encode(buf, img, &gif.Options{NumColors: 256}); err != nil {
			return err
		}
		gimg, err := gif.DecodeAll(buf)
		if err != nil {
			return err
		}
		g.Delay[i] = 0
		g.Image[len(imgs)-i-1] = gimg.Image[0]
	}

	return gif.EncodeAll(f, &g)
}
<commit_msg>example: Get frames in order instead of reversing when writing the gif<commit_after>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"image"
	"image/gif"
	"image/jpeg"
	"net/http"
	"os"
	"strings"

	marvel "github.com/ImJasonH/go-marvel"
)

var (
	seriesID = flag.Int("series", 2258, "Series ID (default: Uncanny X-Men)")
	apiKey   = flag.String("pub", "", "Public API key")
	secret   = flag.String("priv", "", "Private API secret")
)

func main() {
	flag.Parse()

	c := marvel.Client{
		PublicKey:  *apiKey,
		PrivateKey: *secret,
	}

	offset := 0
	limit := 100
	imgs := []image.Image{}
	for {
		r, err := c.SingleSeries(*seriesID).Comics(marvel.ComicsParams{
			CommonParams: marvel.CommonParams{
				Offset:  offset,
				Limit:   limit,
				OrderBy: "onsaleDate",
			},
		})
		if err != nil {
			panic(err)
		}
		for _, iss := range r.Data.Results {
			url := iss.Thumbnail.URL(marvel.PortraitIncredible)
			img := fetchImage(url)
			if img != nil {
				imgs = append(imgs, img)
				fmt.Printf("fetched %v - %s\n", *iss.IssueNumber, url)
			} else {
				fmt.Printf("skipped %s\n", url)
			}
		}
		if len(r.Data.Results) < limit {
			break
		}
		offset += limit
	}

	if err := writeGIF(fmt.Sprintf("%d.gif", *seriesID), imgs); err != nil {
		fmt.Printf("error: %v", err)
	}
}

func fetchImage(url string) image.Image {
	if strings.Contains(url, "image_not_available") {
		return nil
	}

	resp, err := http.Get(url)
	if err != nil {
		return nil
	}
	if resp.StatusCode != http.StatusOK {
		return nil
	}
	defer resp.Body.Close()

	b := bufio.NewReaderSize(resp.Body, 1)
	if _, err := b.Peek(1); err == bufio.ErrBufferFull {
		return nil
	}

	img, err := jpeg.Decode(b)
	if err != nil {
		return nil
	}
	return img
}

func writeGIF(filename string, imgs []image.Image) error {
	f, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer f.Close()

	g := gif.GIF{
		Image: make([]*image.Paletted, len(imgs)),
		Delay: make([]int, len(imgs)),
	}
	b := make([]byte, 0, 1024)
	for i, img := range imgs {
		buf := bytes.NewBuffer(b)
		if err = gif.Encode(buf, img, &gif.Options{NumColors: 256}); err != nil {
			return err
		}
		gimg, err := gif.DecodeAll(buf)
		if err != nil {
			return err
		}
		g.Delay[i] = 0
		g.Image[i] = gimg.Image[0]
	}

	return gif.EncodeAll(f, &g)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/sfi2k7/picoweb"
)

func main() {
	p := picoweb.New()
	p.StopOnInt()
	p.Get("/", func(c *picoweb.Context) {
		c.View("./index.html", struct{ Name string }{Name: "Faisal"})
		//c.String("Hello World")
	})
	fmt.Println(p.Listen(57432))
}
<commit_msg>Added Mongo<commit_after>package main

import (
	"fmt"

	"github.com/sfi2k7/picoweb"
	"gopkg.in/mgo.v2/bson"
)

func main() {
	p := picoweb.New()
	p.StopOnInt()
	p.Get("/", func(c *picoweb.Context) {
		s, err := c.Mongo()
		if err != nil {
			fmt.Println(err)
			return
		}
		defer s.Close()
		var all []bson.M
		s.DB("local").C("startup_log").Find(bson.M{}).All(&all)

		c.View("./index.html", struct {
			Name string
			All  interface{}
		}{
			All:  all,
			Name: "Faisal",
		})
	})
	fmt.Println(p.Listen(57432))
}
<|endoftext|>


<commit_before>package lib

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"
	"sort"
	"strings"
)

var (
	vUpdateEndpoint = updateEndpoint
	vUpdateBucket   = updateBucket
	vVersion        = Version
)

var specChineseUpdate = SpecText{

	synopsisText: "更新ossutil",

	paramText: "[options]",

	syntaxText: `
    ossutil update [-f]
`,

	detailHelpText: `
    该命令检查当前ossutil的版本与最新版本，输出两者的版本号，如果有更新版本，询问是否
    进行升级。如果指定了--force选项，则不询问，当有可用更新时，直接升级。

`,

	sampleText: `
    ossutil update
    ossutil update -f
`,
}

var specEnglishUpdate = SpecText{

	synopsisText: "Update ossutil",

	paramText: "[options]",

	syntaxText: `
    ossutil update [-f]
`,

	detailHelpText: `
    The command check version of current ossutil and get the latest version, output the
    versions, if any updated version exists, the command ask you for upgrading. If --force
    option is specified, the command upgrade without asking.
`,

	sampleText: `
    ossutil update
    ossutil update -f
`,
}

// UpdateCommand is the command update ossutil
type UpdateCommand struct {
	command Command
}

var updateCommand = UpdateCommand{
	command: Command{
		name:        "update",
		nameAlias:   []string{""},
		minArgc:     0,
		maxArgc:     0,
		specChinese: specChineseUpdate,
		specEnglish: specEnglishUpdate,
		group:       GroupTypeAdditionalCommand,
		validOptionNames: []string{
			OptionForce,
			OptionRetryTimes,
			OptionLanguage,
		},
	},
}

// function for RewriteLoadConfiger interface
func (uc *UpdateCommand) rewriteLoadConfig(configFile string) error {
	// read config file, if error exist, do not print error
	var err error
	if uc.command.configOptions, err = LoadConfig(configFile); err != nil {
		uc.command.configOptions = OptionMapType{}
	}
	return nil
}

// function for FormatHelper interface
func (uc *UpdateCommand) formatHelpForWhole() string {
	return uc.command.formatHelpForWhole()
}

func (uc *UpdateCommand) formatIndependHelp() string {
	return uc.command.formatIndependHelp()
}

// Init simulate inheritance, and polymorphism
func (uc *UpdateCommand) Init(args []string, options OptionMapType) error {
	return uc.command.Init(args, options, uc)
}

// RunCommand simulate inheritance, and polymorphism
func (uc *UpdateCommand) RunCommand() error {
	force, _ := GetBool(OptionForce, uc.command.options)
	language, _ := GetString(OptionLanguage, uc.command.options)
	language = strings.ToLower(language)

	// get lastest version
	version, err := uc.getLastestVersion()
	if err != nil {
		return fmt.Errorf("get lastest vsersion error, %s", err.Error())
	}

	if language == LEnglishLanguage {
		fmt.Printf("current version is: %s, the lastest version is: %s", vVersion, version)
	} else {
		fmt.Printf("当前版本为：%s，最新版本为：%s", vVersion, version)
	}
	if version == vVersion {
		if language == LEnglishLanguage {
			fmt.Println(", current version is the lastest version, no need to update.")
		} else {
			fmt.Println("，当前版本即为最新版本，无需更新。")
		}
		return nil
	}
	fmt.Println("")

	if !force {
		if language == LEnglishLanguage {
			fmt.Printf("sure to update ossutil(y or N)? ")
		} else {
			fmt.Printf("确定更新版本(y or N)? ")
		}

		var val string
		if _, err := fmt.Scanln(&val); err == nil && (strings.EqualFold(val, "yes") || strings.EqualFold(val, "y")) {
			return uc.updateVersion(version, language)
		}

		if language == LEnglishLanguage {
			fmt.Printf("operation is canceled.")
		} else {
			fmt.Println("操作取消。")
		}
	} else {
		return uc.updateVersion(version, language)
	}
	return nil
}

func (uc *UpdateCommand) getLastestVersion() (string, error) {
	if err := uc.anonymousGetToFileRetry(vUpdateBucket, updateVersionObject, updateTmpVersionFile); err != nil {
		return "", err
	}

	v, err := ioutil.ReadFile(updateTmpVersionFile)
	if err != nil {
		return "", err
	}
	versionStr := strings.TrimSpace(strings.Trim(string(v), "\n"))

	// get version list and sort
	sli := strings.Split(versionStr, "\n")
	vl := []string{}
	for _, vstr := range sli {
		vl = append(vl, strings.TrimSpace(strings.Trim(string(vstr), "\n")))
	}
	sort.Strings(vl)
	version := vl[len(vl)-1]

	os.Remove(updateTmpVersionFile)

	return version, nil
}

func (uc *UpdateCommand) anonymousGetToFileRetry(bucketName, objectName, filePath string) error {
	host := fmt.Sprintf("http://%s.%s/%s", bucketName, vUpdateEndpoint, objectName)
	retryTimes, _ := GetInt(OptionRetryTimes, uc.command.options)
	for i := 1; ; i++ {
		err := uc.ossAnonymousGetToFile(host, filePath)
		if err == nil {
			return err
		}
		if int64(i) >= retryTimes {
			return ObjectError{err, bucketName, objectName}
		}
	}
}

func (uc *UpdateCommand) ossAnonymousGetToFile(host, filePath string) error {
	response, err := http.Get(host)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	statusCode := response.StatusCode
	body, _ := ioutil.ReadAll(response.Body)
	if statusCode >= 300 {
		return fmt.Errorf(string(body))
	}

	fd, err := os.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0640)
	defer fd.Close()
	if err != nil {
		return err
	}

	_, err = io.WriteString(fd, string(body))
	if err != nil {
		return err
	}
	return nil
}

func (uc *UpdateCommand) updateVersion(version, language string) error {
	// get binary path
	filePath, renameFilePath := getBinaryPath()

	// get binary mode
	f, err := os.Stat(filePath)
	if err != nil {
		return err
	}
	mode := f.Mode()

	// rename the current binary to another one
	if err := os.Rename(filePath, renameFilePath); err != nil {
		return fmt.Errorf("update binary error, %s", err.Error())
	}

	// download the binary of the specified version
	if err := uc.getBinary(filePath, version); err != nil {
		uc.revertRename(filePath, renameFilePath)
		return fmt.Errorf("download binary of version: %s error, %s", version, err.Error())
	}

	if err := os.Chmod(filePath, mode); err != nil {
		uc.revertRename(filePath, renameFilePath)
		return fmt.Errorf("chmod binary error, %s", err.Error())
	}

	// remove the current one
	if runtime.GOOS != "windows" {
		if err := os.Remove(renameFilePath); err != nil {
			uc.revertRename(filePath, renameFilePath)
			return fmt.Errorf("remove old binary error, %s", err.Error())
		}
	}

	if language == LEnglishLanguage {
		fmt.Println("Update Success!")
	} else {
		fmt.Println("更新成功!")
	}
	return nil
}

func (uc *UpdateCommand) revertRename(filePath, renameFilePath string) error {
	if err := os.Remove(filePath); err != nil {
		return err
	}
	if err := os.Rename(renameFilePath, filePath); err != nil {
		return err
	}
	return nil
}

func (uc *UpdateCommand) getBinary(filePath, version string) error {
	// get os type
	var object string
	switch runtime.GOOS {
	case "darwin":
		object = updateBinaryMac64
	case "windows":
		object = updateBinaryWindow64
		if runtime.GOARCH == "386" {
			object = updateBinaryWindow32
		}
	default:
		object = updateBinaryLinux
	}

	object = version + "/" + object

	if err := uc.anonymousGetToFileRetry(vUpdateBucket, object, filePath); err != nil {
		return err
	}

	return nil
}
<commit_msg>fix bug:update faliure and delete ossutil file<commit_after>package lib

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"
	"sort"
	"strings"
)

var (
	vUpdateEndpoint = updateEndpoint
	vUpdateBucket   = updateBucket
	vVersion        = Version
)

var specChineseUpdate = SpecText{

	synopsisText: "更新ossutil",

	paramText: "[options]",

	syntaxText: `
    ossutil update [-f]
`,

	detailHelpText: `
    该命令检查当前ossutil的版本与最新版本，输出两者的版本号，如果有更新版本，询问是否
    进行升级。如果指定了--force选项，则不询问，当有可用更新时，直接升级。

`,

	sampleText: `
    ossutil update
    ossutil update -f
`,
}

var specEnglishUpdate = SpecText{

	synopsisText: "Update ossutil",

	paramText: "[options]",

	syntaxText: `
    ossutil update [-f]
`,

	detailHelpText: `
    The command check version of current ossutil and get the latest version, output the
    versions, if any updated version exists, the command ask you for upgrading. If --force
    option is specified, the command upgrade without asking.
`,

	sampleText: `
    ossutil update
    ossutil update -f
`,
}

// UpdateCommand is the command update ossutil
type UpdateCommand struct {
	command Command
}

var updateCommand = UpdateCommand{
	command: Command{
		name:        "update",
		nameAlias:   []string{""},
		minArgc:     0,
		maxArgc:     0,
		specChinese: specChineseUpdate,
		specEnglish: specEnglishUpdate,
		group:       GroupTypeAdditionalCommand,
		validOptionNames: []string{
			OptionForce,
			OptionRetryTimes,
			OptionLanguage,
		},
	},
}

// function for RewriteLoadConfiger interface
func (uc *UpdateCommand) rewriteLoadConfig(configFile string) error {
	// read config file, if error exist, do not print error
	var err error
	if uc.command.configOptions, err = LoadConfig(configFile); err != nil {
		uc.command.configOptions = OptionMapType{}
	}
	return nil
}

// function for FormatHelper interface
func (uc *UpdateCommand) formatHelpForWhole() string {
	return uc.command.formatHelpForWhole()
}

func (uc *UpdateCommand) formatIndependHelp() string {
	return uc.command.formatIndependHelp()
}

// Init simulate inheritance, and polymorphism
func (uc *UpdateCommand) Init(args []string, options OptionMapType) error {
	return uc.command.Init(args, options, uc)
}

// RunCommand simulate inheritance, and polymorphism
func (uc *UpdateCommand) RunCommand() error {
	force, _ := GetBool(OptionForce, uc.command.options)
	language, _ := GetString(OptionLanguage, uc.command.options)
	language = strings.ToLower(language)

	// get lastest version
	version, err := uc.getLastestVersion()
	if err != nil {
		return fmt.Errorf("get lastest vsersion error, %s", err.Error())
	}

	if language == LEnglishLanguage {
		fmt.Printf("current version is: %s, the lastest version is: %s", vVersion, version)
	} else {
		fmt.Printf("当前版本为：%s，最新版本为：%s", vVersion, version)
	}
	if version == vVersion {
		if language == LEnglishLanguage {
			fmt.Println(", current version is the lastest version, no need to update.")
		} else {
			fmt.Println("，当前版本即为最新版本，无需更新。")
		}
		return nil
	}
	fmt.Println("")

	if !force {
		if language == LEnglishLanguage {
			fmt.Printf("sure to update ossutil(y or N)? ")
		} else {
			fmt.Printf("确定更新版本(y or N)? ")
		}

		var val string
		if _, err := fmt.Scanln(&val); err == nil && (strings.EqualFold(val, "yes") || strings.EqualFold(val, "y")) {
			return uc.updateVersion(version, language)
		}

		if language == LEnglishLanguage {
			fmt.Printf("operation is canceled.")
		} else {
			fmt.Println("操作取消。")
		}
	} else {
		return uc.updateVersion(version, language)
	}
	return nil
}

func (uc *UpdateCommand) getLastestVersion() (string, error) {
	if err := uc.anonymousGetToFileRetry(vUpdateBucket, updateVersionObject, updateTmpVersionFile); err != nil {
		return "", err
	}

	v, err := ioutil.ReadFile(updateTmpVersionFile)
	if err != nil {
		return "", err
	}
	versionStr := strings.TrimSpace(strings.Trim(string(v), "\n"))

	// get version list and sort
	sli := strings.Split(versionStr, "\n")
	vl := []string{}
	for _, vstr := range sli {
		vl = append(vl, strings.TrimSpace(strings.Trim(string(vstr), "\n")))
	}
	sort.Strings(vl)
	version := vl[len(vl)-1]

	os.Remove(updateTmpVersionFile)

	return version, nil
}

func (uc *UpdateCommand) anonymousGetToFileRetry(bucketName, objectName, filePath string) error {
	host := fmt.Sprintf("http://%s.%s/%s", bucketName, vUpdateEndpoint, objectName)
	retryTimes, _ := GetInt(OptionRetryTimes, uc.command.options)
	for i := 1; ; i++ {
		err := uc.ossAnonymousGetToFile(host, filePath)
		if err == nil {
			return err
		}
		if int64(i) >= retryTimes {
			return ObjectError{err, bucketName, objectName}
		}
	}
}

func (uc *UpdateCommand) ossAnonymousGetToFile(host, filePath string) error {
	response, err := http.Get(host)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	statusCode := response.StatusCode
	body, _ := ioutil.ReadAll(response.Body)
	if statusCode >= 300 {
		return fmt.Errorf(string(body))
	}

	fd, err := os.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0640)
	defer fd.Close()
	if err != nil {
		return err
	}

	_, err = io.WriteString(fd, string(body))
	if err != nil {
		return err
	}
	return nil
}

func (uc *UpdateCommand) updateVersion(version, language string) error {
	// get binary path
	filePath, renameFilePath := getBinaryPath()

	// get binary mode
	f, err := os.Stat(filePath)
	if err != nil {
		return err
	}
	mode := f.Mode()

	// rename the current binary to another one
	if err := os.Rename(filePath, renameFilePath); err != nil {
		return fmt.Errorf("update binary error, %s", err.Error())
	}

	// download the binary of the specified version
	if err := uc.getBinary(filePath, version); err != nil {
		uc.revertRename(filePath, renameFilePath)
		return fmt.Errorf("download binary of version: %s error, %s", version, err.Error())
	}

	if err := os.Chmod(filePath, mode); err != nil {
		uc.revertRename(filePath, renameFilePath)
		return fmt.Errorf("chmod binary error, %s", err.Error())
	}

	// remove the current one
	if runtime.GOOS != "windows" {
		if err := os.Remove(renameFilePath); err != nil {
			uc.revertRename(filePath, renameFilePath)
			return fmt.Errorf("remove old binary error, %s", err.Error())
		}
	}

	if language == LEnglishLanguage {
		fmt.Println("Update Success!")
	} else {
		fmt.Println("更新成功!")
	}
	return nil
}

func (uc *UpdateCommand) revertRename(filePath, renameFilePath string) error {
	if _, err := os.Stat(filePath); err == nil {
		os.Remove(filePath)
	}

	if err := os.Rename(renameFilePath, filePath); err != nil {
		return err
	}
	return nil
}

func (uc *UpdateCommand) getBinary(filePath, version string) error {
	// get os type
	var object string
	switch runtime.GOOS {
	case "darwin":
		object = updateBinaryMac64
	case "windows":
		object = updateBinaryWindow64
		if runtime.GOARCH == "386" {
			object = updateBinaryWindow32
		}
	default:
		object = updateBinaryLinux
	}

	object = version + "/" + object

	if err := uc.anonymousGetToFileRetry(vUpdateBucket, object, filePath); err != nil {
		return err
	}

	return nil
}
<|endoftext|>