<commit_before>package instance

import (
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/cozy/cozy-stack/web/jsonapi"
)

// Warnings returns a list of possible warnings associated with the instance.
func (i *Instance) Warnings() (warnings []*jsonapi.Error) {
	notSigned, deadline := i.CheckTOSNotSignedAndDeadline()
	if notSigned && deadline >= TOSWarning {
		tosLink, _ := i.ManagerURL(ManagerTOSURL)
		warnings = append(warnings, &jsonapi.Error{
			Status: http.StatusPaymentRequired,
			Title:  "TOS Updated",
			Code:   "tos-updated",
			Detail: i.Translate("Terms of services have been updated"),
			Links:  &jsonapi.LinksList{Self: tosLink},
		})
	}
	return
}

// TOSDeadline represent the state for reaching the TOS deadline.
type TOSDeadline int

const (
	// TOSNone when no deadline is reached.
	TOSNone TOSDeadline = iota
	// TOSWarning when the warning deadline is reached, 2 weeks before the actual
	// activation of the CGU.
	TOSWarning
	// TOSBlocked when the deadline is reached and the access should be blocked.
	TOSBlocked
)

// CheckInstanceBlocked returns whether or not the instance is currently in a
// blocked state: meaning it should be accessible.
func (i *Instance) CheckInstanceBlocked() bool {
	if i.Blocked {
		return true
	}
	if len(i.RegisterToken) > 0 {
		return false
	}
	_, deadline := i.CheckTOSNotSignedAndDeadline()
	return deadline == TOSBlocked
}

// CheckTOSNotSigned checks whether or not the current Term of Services have
// been signed by the user.
func (i *Instance) CheckTOSNotSigned(args ...string) (notSigned bool) {
	notSigned, _ = i.CheckTOSNotSignedAndDeadline(args...)
	return
}

// CheckTOSNotSigned checks whether or not the current Term of Services have
// been signed by the user and returns the deadline state to perform this
// signature.
func (i *Instance) CheckTOSNotSignedAndDeadline(args ...string) (notSigned bool, deadline TOSDeadline) {
	tosLatest := i.TOSLatest
	if len(args) > 0 {
		tosLatest = args[0]
	}
	latest, latestDate, ok := parseTOSVersion(tosLatest)
	if !ok || latestDate.IsZero() {
		return
	}
	defer func() {
		if notSigned {
			now := time.Now()
			if now.After(latestDate) {
				deadline = TOSBlocked
			} else if now.After(latestDate.Add(-30 * 24 * time.Hour)) {
				deadline = TOSWarning
			} else {
				deadline = TOSNone
			}
		}
	}()
	signed, _, ok := parseTOSVersion(i.TOSSigned)
	if !ok {
		notSigned = true
	} else {
		notSigned = signed < latest
	}
	return
}

// parseTOSVersion returns the "major" and the date encoded in a version string
// with the following format:
//    parseTOSVersion(A.B.C-YYYYMMDD) -> (A, YYYY, true)
func parseTOSVersion(v string) (major int, date time.Time, ok bool) {
	if v == "" {
		return
	}
	if len(v) == 8 {
		var err error
		major = 1
		date, err = time.Parse("20060102", v)
		ok = err == nil
		return
	}
	if v[0] == 'v' {
		v = v[1:]
	}
	a := strings.SplitN(v, ".", 3)
	if len(a) != 3 {
		return
	}
	major, err := strconv.Atoi(a[0])
	if err != nil {
		return
	}
	suffix := strings.SplitN(a[2], "-", 2)
	if len(suffix) < 2 {
		ok = true
		return
	}
	date, err = time.Parse("20060102", suffix[1])
	ok = err == nil
	return
}
<commit_msg>Fix lint<commit_after>package instance

import (
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/cozy/cozy-stack/web/jsonapi"
)

// Warnings returns a list of possible warnings associated with the instance.
func (i *Instance) Warnings() (warnings []*jsonapi.Error) {
	notSigned, deadline := i.CheckTOSNotSignedAndDeadline()
	if notSigned && deadline >= TOSWarning {
		tosLink, _ := i.ManagerURL(ManagerTOSURL)
		warnings = append(warnings, &jsonapi.Error{
			Status: http.StatusPaymentRequired,
			Title:  "TOS Updated",
			Code:   "tos-updated",
			Detail: i.Translate("Terms of services have been updated"),
			Links:  &jsonapi.LinksList{Self: tosLink},
		})
	}
	return
}

// TOSDeadline represent the state for reaching the TOS deadline.
type TOSDeadline int

const (
	// TOSNone when no deadline is reached.
	TOSNone TOSDeadline = iota
	// TOSWarning when the warning deadline is reached, 2 weeks before the actual
	// activation of the CGU.
	TOSWarning
	// TOSBlocked when the deadline is reached and the access should be blocked.
	TOSBlocked
)

// CheckInstanceBlocked returns whether or not the instance is currently in a
// blocked state: meaning it should be accessible.
func (i *Instance) CheckInstanceBlocked() bool {
	if i.Blocked {
		return true
	}
	if len(i.RegisterToken) > 0 {
		return false
	}
	_, deadline := i.CheckTOSNotSignedAndDeadline()
	return deadline == TOSBlocked
}

// CheckTOSNotSigned checks whether or not the current Term of Services have
// been signed by the user.
func (i *Instance) CheckTOSNotSigned(args ...string) (notSigned bool) {
	notSigned, _ = i.CheckTOSNotSignedAndDeadline(args...)
	return
}

// CheckTOSNotSignedAndDeadline checks whether or not the current Term of
// Services have been signed by the user and returns the deadline state to
// perform this signature.
func (i *Instance) CheckTOSNotSignedAndDeadline(args ...string) (notSigned bool, deadline TOSDeadline) {
	tosLatest := i.TOSLatest
	if len(args) > 0 {
		tosLatest = args[0]
	}
	latest, latestDate, ok := parseTOSVersion(tosLatest)
	if !ok || latestDate.IsZero() {
		return
	}
	defer func() {
		if notSigned {
			now := time.Now()
			if now.After(latestDate) {
				deadline = TOSBlocked
			} else if now.After(latestDate.Add(-30 * 24 * time.Hour)) {
				deadline = TOSWarning
			} else {
				deadline = TOSNone
			}
		}
	}()
	signed, _, ok := parseTOSVersion(i.TOSSigned)
	if !ok {
		notSigned = true
	} else {
		notSigned = signed < latest
	}
	return
}

// parseTOSVersion returns the "major" and the date encoded in a version string
// with the following format:
//    parseTOSVersion(A.B.C-YYYYMMDD) -> (A, YYYY, true)
func parseTOSVersion(v string) (major int, date time.Time, ok bool) {
	if v == "" {
		return
	}
	if len(v) == 8 {
		var err error
		major = 1
		date, err = time.Parse("20060102", v)
		ok = err == nil
		return
	}
	if v[0] == 'v' {
		v = v[1:]
	}
	a := strings.SplitN(v, ".", 3)
	if len(a) != 3 {
		return
	}
	major, err := strconv.Atoi(a[0])
	if err != nil {
		return
	}
	suffix := strings.SplitN(a[2], "-", 2)
	if len(suffix) < 2 {
		ok = true
		return
	}
	date, err = time.Parse("20060102", suffix[1])
	ok = err == nil
	return
}
<|endoftext|>


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

// Package keystore implements the ACI keystore.
package keystore

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types"
	"github.com/coreos/rkt/Godeps/_workspace/src/golang.org/x/crypto/openpgp"
	"github.com/coreos/rkt/common"
)

// A Config structure is used to configure a Keystore.
type Config struct {
	LocalRootPath    string
	LocalPrefixPath  string
	SystemRootPath   string
	SystemPrefixPath string
}

// A Keystore represents a repository of trusted public keys which can be
// used to verify PGP signatures.
type Keystore struct {
	*Config
}

// New returns a new Keystore based on config.
func New(config *Config) *Keystore {
	if config == nil {
		config = defaultConfig
	}
	return &Keystore{config}
}

func NewConfig(systemPath, localPath string) *Config {
	return &Config{
		LocalRootPath:    filepath.Join(localPath, "trustedkeys", "root.d"),
		LocalPrefixPath:  filepath.Join(localPath, "trustedkeys", "prefix.d"),
		SystemRootPath:   filepath.Join(systemPath, "trustedkeys", "root.d"),
		SystemPrefixPath: filepath.Join(systemPath, "trustedkeys", "prefix.d"),
	}
}

var defaultConfig = NewConfig(common.DefaultSystemConfigDir, common.DefaultLocalConfigDir)

// CheckSignature is a convenience method for creating a Keystore with a default
// configuration and invoking CheckSignature.
func CheckSignature(prefix string, signed, signature io.Reader) (*openpgp.Entity, error) {
	ks := New(defaultConfig)
	return checkSignature(ks, prefix, signed, signature)
}

// CheckSignature takes a signed file and a detached signature and returns the signer
// if the signature is signed by a trusted signer.
// If the signer is unknown or not trusted, opengpg.ErrUnknownIssuer is returned.
func (ks *Keystore) CheckSignature(prefix string, signed, signature io.Reader) (*openpgp.Entity, error) {
	return checkSignature(ks, prefix, signed, signature)
}

func checkSignature(ks *Keystore, prefix string, signed, signature io.Reader) (*openpgp.Entity, error) {
	acname, err := types.NewACName(prefix)
	if err != nil {
		return nil, err
	}
	keyring, err := ks.loadKeyring(acname.String())
	if err != nil {
		return nil, fmt.Errorf("keystore: error loading keyring %v", err)
	}
	entities, err := openpgp.CheckArmoredDetachedSignature(keyring, signed, signature)
	if err == io.EOF {
		// otherwise, the client failure is just "EOF", which is not helpful
		return nil, fmt.Errorf("keystore: no signatures found")
	}
	return entities, err
}

// DeleteTrustedKeyPrefix deletes the prefix trusted key identified by fingerprint.
func (ks *Keystore) DeleteTrustedKeyPrefix(prefix, fingerprint string) error {
	acname, err := types.NewACName(prefix)
	if err != nil {
		return err
	}
	return os.Remove(path.Join(ks.LocalPrefixPath, acname.String(), fingerprint))
}

// MaskTrustedKeySystemPrefix masks the system prefix trusted key identified by fingerprint.
func (ks *Keystore) MaskTrustedKeySystemPrefix(prefix, fingerprint string) (string, error) {
	acname, err := types.NewACName(prefix)
	if err != nil {
		return "", err
	}
	dst := path.Join(ks.LocalPrefixPath, acname.String(), fingerprint)
	return dst, ioutil.WriteFile(dst, []byte(""), 0644)
}

// DeleteTrustedKeyRoot deletes the root trusted key identified by fingerprint.
func (ks *Keystore) DeleteTrustedKeyRoot(fingerprint string) error {
	return os.Remove(path.Join(ks.LocalRootPath, fingerprint))
}

// MaskTrustedKeySystemRoot masks the system root trusted key identified by fingerprint.
func (ks *Keystore) MaskTrustedKeySystemRoot(fingerprint string) (string, error) {
	dst := path.Join(ks.LocalRootPath, fingerprint)
	return dst, ioutil.WriteFile(dst, []byte(""), 0644)
}

// StoreTrustedKeyPrefix stores the contents of public key r as a prefix trusted key.
func (ks *Keystore) StoreTrustedKeyPrefix(prefix string, r io.Reader) (string, error) {
	acname, err := types.NewACName(prefix)
	if err != nil {
		return "", err
	}
	return storeTrustedKey(path.Join(ks.LocalPrefixPath, acname.String()), r)
}

// StoreTrustedKeyRoot stores the contents of public key r as a root trusted key.
func (ks *Keystore) StoreTrustedKeyRoot(r io.Reader) (string, error) {
	return storeTrustedKey(ks.LocalRootPath, r)
}

func storeTrustedKey(dir string, r io.Reader) (string, error) {
	pubkeyBytes, err := ioutil.ReadAll(r)
	if err != nil {
		return "", err
	}
	if err := os.MkdirAll(dir, 0755); err != nil {
		return "", err
	}
	entityList, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(pubkeyBytes))
	if err != nil {
		return "", err
	}
	pubKey := entityList[0].PrimaryKey
	trustedKeyPath := path.Join(dir, fingerprintToFilename(pubKey.Fingerprint))
	if err := ioutil.WriteFile(trustedKeyPath, pubkeyBytes, 0644); err != nil {
		return "", err
	}
	return trustedKeyPath, nil
}

func entityFromFile(path string) (*openpgp.Entity, error) {
	trustedKey, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer trustedKey.Close()
	entityList, err := openpgp.ReadArmoredKeyRing(trustedKey)
	if err != nil {
		return nil, err
	}
	if len(entityList) < 1 {
		return nil, errors.New("missing opengpg entity")
	}
	fingerprint := fingerprintToFilename(entityList[0].PrimaryKey.Fingerprint)
	keyFile := filepath.Base(trustedKey.Name())
	if fingerprint != keyFile {
		return nil, fmt.Errorf("fingerprint mismatch: %q:%q", keyFile, fingerprint)
	}
	return entityList[0], nil
}

func (ks *Keystore) loadKeyring(prefix string) (openpgp.KeyRing, error) {
	acname, err := types.NewACName(prefix)
	if err != nil {
		return nil, err
	}
	var keyring openpgp.EntityList
	trustedKeys := make(map[string]*openpgp.Entity)

	prefixRoot := strings.Split(acname.String(), "/")[0]
	paths := []struct {
		root     string
		fullPath string
	}{
		{ks.SystemRootPath, ks.SystemRootPath},
		{ks.LocalRootPath, ks.LocalRootPath},
		{path.Join(ks.SystemPrefixPath, prefixRoot), path.Join(ks.SystemPrefixPath, acname.String())},
		{path.Join(ks.LocalPrefixPath, prefixRoot), path.Join(ks.LocalPrefixPath, acname.String())},
	}
	for _, p := range paths {
		err := filepath.Walk(p.root, func(path string, info os.FileInfo, err error) error {
			if err != nil && !os.IsNotExist(err) {
				return err
			}
			if info == nil {
				return nil
			}
			if info.IsDir() {
				switch {
				case strings.HasPrefix(p.fullPath, path):
					return nil
				default:
					return filepath.SkipDir
				}
			}
			// Remove trust for default keys.
			if info.Size() == 0 {
				delete(trustedKeys, info.Name())
				return nil
			}
			entity, err := entityFromFile(path)
			if err != nil {
				return err
			}
			trustedKeys[fingerprintToFilename(entity.PrimaryKey.Fingerprint)] = entity
			return nil
		})
		if err != nil {
			return nil, err
		}
	}

	for _, v := range trustedKeys {
		keyring = append(keyring, v)
	}
	return keyring, nil
}

func fingerprintToFilename(fp [20]byte) string {
	return fmt.Sprintf("%x", fp)
}

// NewTestKeystore creates a new KeyStore backed by a temp directory.
// NewTestKeystore returns a KeyStore, the path to the temp directory, and
// an error if any.
func NewTestKeystore() (*Keystore, string, error) {
	dir, err := ioutil.TempDir("", "keystore-test")
	if err != nil {
		return nil, "", err
	}
	systemDir := filepath.Join(dir, common.DefaultSystemConfigDir)
	localDir := filepath.Join(dir, common.DefaultLocalConfigDir)
	c := NewConfig(systemDir, localDir)
	for _, path := range []string{c.LocalRootPath, c.SystemRootPath, c.LocalPrefixPath, c.SystemPrefixPath} {
		if err := os.MkdirAll(path, 0755); err != nil {
			return nil, "", err
		}
	}
	return New(c), dir, nil
}
<commit_msg>keystore: improve missing signature error message<commit_after>// Copyright 2014 The rkt Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package keystore implements the ACI keystore.
package keystore

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types"
	"github.com/coreos/rkt/Godeps/_workspace/src/golang.org/x/crypto/openpgp"
	"github.com/coreos/rkt/common"
)

// A Config structure is used to configure a Keystore.
type Config struct {
	LocalRootPath    string
	LocalPrefixPath  string
	SystemRootPath   string
	SystemPrefixPath string
}

// A Keystore represents a repository of trusted public keys which can be
// used to verify PGP signatures.
type Keystore struct {
	*Config
}

// New returns a new Keystore based on config.
func New(config *Config) *Keystore {
	if config == nil {
		config = defaultConfig
	}
	return &Keystore{config}
}

func NewConfig(systemPath, localPath string) *Config {
	return &Config{
		LocalRootPath:    filepath.Join(localPath, "trustedkeys", "root.d"),
		LocalPrefixPath:  filepath.Join(localPath, "trustedkeys", "prefix.d"),
		SystemRootPath:   filepath.Join(systemPath, "trustedkeys", "root.d"),
		SystemPrefixPath: filepath.Join(systemPath, "trustedkeys", "prefix.d"),
	}
}

var defaultConfig = NewConfig(common.DefaultSystemConfigDir, common.DefaultLocalConfigDir)

// CheckSignature is a convenience method for creating a Keystore with a default
// configuration and invoking CheckSignature.
func CheckSignature(prefix string, signed, signature io.Reader) (*openpgp.Entity, error) {
	ks := New(defaultConfig)
	return checkSignature(ks, prefix, signed, signature)
}

// CheckSignature takes a signed file and a detached signature and returns the signer
// if the signature is signed by a trusted signer.
// If the signer is unknown or not trusted, opengpg.ErrUnknownIssuer is returned.
func (ks *Keystore) CheckSignature(prefix string, signed, signature io.Reader) (*openpgp.Entity, error) {
	return checkSignature(ks, prefix, signed, signature)
}

func checkSignature(ks *Keystore, prefix string, signed, signature io.Reader) (*openpgp.Entity, error) {
	acname, err := types.NewACName(prefix)
	if err != nil {
		return nil, err
	}
	keyring, err := ks.loadKeyring(acname.String())
	if err != nil {
		return nil, fmt.Errorf("keystore: error loading keyring %v", err)
	}
	entities, err := openpgp.CheckArmoredDetachedSignature(keyring, signed, signature)
	if err == io.EOF {
		// otherwise, the client failure is just "EOF", which is not helpful
		return nil, fmt.Errorf("keystore: no valid signatures found in signature file")
	}
	return entities, err
}

// DeleteTrustedKeyPrefix deletes the prefix trusted key identified by fingerprint.
func (ks *Keystore) DeleteTrustedKeyPrefix(prefix, fingerprint string) error {
	acname, err := types.NewACName(prefix)
	if err != nil {
		return err
	}
	return os.Remove(path.Join(ks.LocalPrefixPath, acname.String(), fingerprint))
}

// MaskTrustedKeySystemPrefix masks the system prefix trusted key identified by fingerprint.
func (ks *Keystore) MaskTrustedKeySystemPrefix(prefix, fingerprint string) (string, error) {
	acname, err := types.NewACName(prefix)
	if err != nil {
		return "", err
	}
	dst := path.Join(ks.LocalPrefixPath, acname.String(), fingerprint)
	return dst, ioutil.WriteFile(dst, []byte(""), 0644)
}

// DeleteTrustedKeyRoot deletes the root trusted key identified by fingerprint.
func (ks *Keystore) DeleteTrustedKeyRoot(fingerprint string) error {
	return os.Remove(path.Join(ks.LocalRootPath, fingerprint))
}

// MaskTrustedKeySystemRoot masks the system root trusted key identified by fingerprint.
func (ks *Keystore) MaskTrustedKeySystemRoot(fingerprint string) (string, error) {
	dst := path.Join(ks.LocalRootPath, fingerprint)
	return dst, ioutil.WriteFile(dst, []byte(""), 0644)
}

// StoreTrustedKeyPrefix stores the contents of public key r as a prefix trusted key.
func (ks *Keystore) StoreTrustedKeyPrefix(prefix string, r io.Reader) (string, error) {
	acname, err := types.NewACName(prefix)
	if err != nil {
		return "", err
	}
	return storeTrustedKey(path.Join(ks.LocalPrefixPath, acname.String()), r)
}

// StoreTrustedKeyRoot stores the contents of public key r as a root trusted key.
func (ks *Keystore) StoreTrustedKeyRoot(r io.Reader) (string, error) {
	return storeTrustedKey(ks.LocalRootPath, r)
}

func storeTrustedKey(dir string, r io.Reader) (string, error) {
	pubkeyBytes, err := ioutil.ReadAll(r)
	if err != nil {
		return "", err
	}
	if err := os.MkdirAll(dir, 0755); err != nil {
		return "", err
	}
	entityList, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(pubkeyBytes))
	if err != nil {
		return "", err
	}
	pubKey := entityList[0].PrimaryKey
	trustedKeyPath := path.Join(dir, fingerprintToFilename(pubKey.Fingerprint))
	if err := ioutil.WriteFile(trustedKeyPath, pubkeyBytes, 0644); err != nil {
		return "", err
	}
	return trustedKeyPath, nil
}

func entityFromFile(path string) (*openpgp.Entity, error) {
	trustedKey, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer trustedKey.Close()
	entityList, err := openpgp.ReadArmoredKeyRing(trustedKey)
	if err != nil {
		return nil, err
	}
	if len(entityList) < 1 {
		return nil, errors.New("missing opengpg entity")
	}
	fingerprint := fingerprintToFilename(entityList[0].PrimaryKey.Fingerprint)
	keyFile := filepath.Base(trustedKey.Name())
	if fingerprint != keyFile {
		return nil, fmt.Errorf("fingerprint mismatch: %q:%q", keyFile, fingerprint)
	}
	return entityList[0], nil
}

func (ks *Keystore) loadKeyring(prefix string) (openpgp.KeyRing, error) {
	acname, err := types.NewACName(prefix)
	if err != nil {
		return nil, err
	}
	var keyring openpgp.EntityList
	trustedKeys := make(map[string]*openpgp.Entity)

	prefixRoot := strings.Split(acname.String(), "/")[0]
	paths := []struct {
		root     string
		fullPath string
	}{
		{ks.SystemRootPath, ks.SystemRootPath},
		{ks.LocalRootPath, ks.LocalRootPath},
		{path.Join(ks.SystemPrefixPath, prefixRoot), path.Join(ks.SystemPrefixPath, acname.String())},
		{path.Join(ks.LocalPrefixPath, prefixRoot), path.Join(ks.LocalPrefixPath, acname.String())},
	}
	for _, p := range paths {
		err := filepath.Walk(p.root, func(path string, info os.FileInfo, err error) error {
			if err != nil && !os.IsNotExist(err) {
				return err
			}
			if info == nil {
				return nil
			}
			if info.IsDir() {
				switch {
				case strings.HasPrefix(p.fullPath, path):
					return nil
				default:
					return filepath.SkipDir
				}
			}
			// Remove trust for default keys.
			if info.Size() == 0 {
				delete(trustedKeys, info.Name())
				return nil
			}
			entity, err := entityFromFile(path)
			if err != nil {
				return err
			}
			trustedKeys[fingerprintToFilename(entity.PrimaryKey.Fingerprint)] = entity
			return nil
		})
		if err != nil {
			return nil, err
		}
	}

	for _, v := range trustedKeys {
		keyring = append(keyring, v)
	}
	return keyring, nil
}

func fingerprintToFilename(fp [20]byte) string {
	return fmt.Sprintf("%x", fp)
}

// NewTestKeystore creates a new KeyStore backed by a temp directory.
// NewTestKeystore returns a KeyStore, the path to the temp directory, and
// an error if any.
func NewTestKeystore() (*Keystore, string, error) {
	dir, err := ioutil.TempDir("", "keystore-test")
	if err != nil {
		return nil, "", err
	}
	systemDir := filepath.Join(dir, common.DefaultSystemConfigDir)
	localDir := filepath.Join(dir, common.DefaultLocalConfigDir)
	c := NewConfig(systemDir, localDir)
	for _, path := range []string{c.LocalRootPath, c.SystemRootPath, c.LocalPrefixPath, c.SystemPrefixPath} {
		if err := os.MkdirAll(path, 0755); err != nil {
			return nil, "", err
		}
	}
	return New(c), dir, nil
}
<|endoftext|>


<commit_before>package app

import (
	"crypto/rand"
	"crypto/rsa"

	"bytes"
	"crypto/x509"
	"encoding/pem"

	"encoding/asn1"

	"github.com/feedhenry/mcp-standalone/pkg/mobile"
	"github.com/pkg/errors"
)

type Build struct {
}

func NewBuild() *Build {
	return &Build{}
}

type AppBuildCreatedResponse struct {
	PublicKey string `json:"publicKey"`
	BuildID   string `json:buildID`
}

func (b *Build) CreateAppBuild(buildRepo mobile.BuildCruder, build *mobile.Build) (*AppBuildCreatedResponse, error) {
	var res = &AppBuildCreatedResponse{BuildID: build.Name}
	if build.GitRepo.JenkinsFilePath == "" {
		build.GitRepo.JenkinsFilePath = "Jenkinsfile"
	}
	if !build.GitRepo.Private {
		if err := buildRepo.Create(build); err != nil {
			return nil, errors.Wrap(err, "CreateAppBuild: failed to create build")
		}
		return res, nil
	}
	assetName, pubkey, err := b.CreateBuildSrcKeySecret(buildRepo, build.Name)
	if err != nil {
		return nil, errors.Wrap(err, "failed to setup src keys when creating app build ")
	}
	res.PublicKey = string(pubkey)
	build.GitRepo.PublicKeyID = assetName
	if err := buildRepo.Create(build); err != nil {
		return nil, errors.Wrap(err, "CreateAppBuild: failed to create build")
	}
	return res, nil

}

// CreateBuildSrcKeySecret creates a public private key pair and returns the secret name it is stored in and the public part of the key as bytes
func (b *Build) CreateBuildSrcKeySecret(br mobile.BuildCruder, buildName string) (string, []byte, error) {
	var (
		buildAsset    = mobile.BuildAsset{}
		privateKeyVal *bytes.Buffer
		publicKeyVal  *bytes.Buffer
		reader        = rand.Reader
		bitSize       = 2048
	)
	key, err := rsa.GenerateKey(reader, bitSize)
	if err != nil {
		return "", nil, errors.Wrap(err, "CreateAppBuild: failed to generate a new rsa key pair when creating new app build")
	}
	var privateKey = &pem.Block{
		Type:  "PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(key),
	}
	privateKeyVal = &bytes.Buffer{}
	err = pem.Encode(privateKeyVal, privateKey)
	if err != nil {
		return "", nil, errors.Wrap(err, "CreateAppBuild: failed to encode private key for new app build ")
	}
	pKey, err := asn1.Marshal(key.PublicKey)
	if err != nil {
		return "", nil, errors.Wrap(err, "CreateAppBuild: failed to marshal public key when creating app build")
	}
	var pubkey = &pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: pKey,
	}
	publicKeyVal = &bytes.Buffer{}
	if err = pem.Encode(publicKeyVal, pubkey); err != nil {
		return "", nil, errors.Wrap(err, "CreateAppBuild: failed to encode public key for new app build")
	}
	buildAsset.BuildName = buildName
	buildAsset.Type = mobile.BuildAssetTypeSourceCredential
	buildAsset.Name = buildName + "ssh-key"
	buildAsset.AssetData = map[string][]byte{"ssh-privatekey": privateKeyVal.Bytes(), "ssh-publickey": publicKeyVal.Bytes()}
	assetName, err := br.AddBuildAsset(buildAsset)
	if err != nil {
		return "", nil, errors.Wrap(err, "CreateAppBuild: failed to add build asset ssh-key ")
	}
	return assetName, publicKeyVal.Bytes(), nil
}
<commit_msg>fix json tag<commit_after>package app

import (
	"crypto/rand"
	"crypto/rsa"

	"bytes"
	"crypto/x509"
	"encoding/pem"

	"encoding/asn1"

	"github.com/feedhenry/mcp-standalone/pkg/mobile"
	"github.com/pkg/errors"
)

type Build struct {
}

func NewBuild() *Build {
	return &Build{}
}

type AppBuildCreatedResponse struct {
	PublicKey string `json:"publicKey"`
	BuildID   string `json:"buildID"`
}

func (b *Build) CreateAppBuild(buildRepo mobile.BuildCruder, build *mobile.Build) (*AppBuildCreatedResponse, error) {
	var res = &AppBuildCreatedResponse{BuildID: build.Name}
	if build.GitRepo.JenkinsFilePath == "" {
		build.GitRepo.JenkinsFilePath = "Jenkinsfile"
	}
	if !build.GitRepo.Private {
		if err := buildRepo.Create(build); err != nil {
			return nil, errors.Wrap(err, "CreateAppBuild: failed to create build")
		}
		return res, nil
	}
	assetName, pubkey, err := b.CreateBuildSrcKeySecret(buildRepo, build.Name)
	if err != nil {
		return nil, errors.Wrap(err, "failed to setup src keys when creating app build ")
	}
	res.PublicKey = string(pubkey)
	build.GitRepo.PublicKeyID = assetName
	if err := buildRepo.Create(build); err != nil {
		return nil, errors.Wrap(err, "CreateAppBuild: failed to create build")
	}
	return res, nil

}

// CreateBuildSrcKeySecret creates a public private key pair and returns the secret name it is stored in and the public part of the key as bytes
func (b *Build) CreateBuildSrcKeySecret(br mobile.BuildCruder, buildName string) (string, []byte, error) {
	var (
		buildAsset    = mobile.BuildAsset{}
		privateKeyVal *bytes.Buffer
		publicKeyVal  *bytes.Buffer
		reader        = rand.Reader
		bitSize       = 2048
	)
	key, err := rsa.GenerateKey(reader, bitSize)
	if err != nil {
		return "", nil, errors.Wrap(err, "CreateAppBuild: failed to generate a new rsa key pair when creating new app build")
	}
	var privateKey = &pem.Block{
		Type:  "PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(key),
	}
	privateKeyVal = &bytes.Buffer{}
	err = pem.Encode(privateKeyVal, privateKey)
	if err != nil {
		return "", nil, errors.Wrap(err, "CreateAppBuild: failed to encode private key for new app build ")
	}
	pKey, err := asn1.Marshal(key.PublicKey)
	if err != nil {
		return "", nil, errors.Wrap(err, "CreateAppBuild: failed to marshal public key when creating app build")
	}
	var pubkey = &pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: pKey,
	}
	publicKeyVal = &bytes.Buffer{}
	if err = pem.Encode(publicKeyVal, pubkey); err != nil {
		return "", nil, errors.Wrap(err, "CreateAppBuild: failed to encode public key for new app build")
	}
	buildAsset.BuildName = buildName
	buildAsset.Type = mobile.BuildAssetTypeSourceCredential
	buildAsset.Name = buildName + "ssh-key"
	buildAsset.AssetData = map[string][]byte{"ssh-privatekey": privateKeyVal.Bytes(), "ssh-publickey": publicKeyVal.Bytes()}
	assetName, err := br.AddBuildAsset(buildAsset)
	if err != nil {
		return "", nil, errors.Wrap(err, "CreateAppBuild: failed to add build asset ssh-key ")
	}
	return assetName, publicKeyVal.Bytes(), nil
}
<|endoftext|>


<commit_before>package client

import (
	"context"
	"crypto/hmac"
	"crypto/sha512"
	"encoding/hex"
	"encoding/json"
	"errors"
	"net"

	"github.com/bitfinexcom/bitfinex-api-go/pkg/models/event"
	"github.com/bitfinexcom/bitfinex-api-go/pkg/mux/msg"
	"github.com/bitfinexcom/bitfinex-api-go/pkg/utils"
	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
)

type Client struct {
	id        int
	conn      net.Conn
	nonceGen  *utils.EpochNonceGenerator
	subsLimit int
	subs      map[event.Subscribe]bool
}

// New returns pointer to Client instance
func New() *Client {
	return &Client{
		subs:     make(map[event.Subscribe]bool),
		nonceGen: utils.NewEpochNonceGenerator(),
	}
}

// WithID assigns clinet ID
func (c *Client) WithID(ID int) *Client {
	c.id = ID
	return c
}

// WithSubsLimit sets limit of subscriptions on the instance
func (c *Client) WithSubsLimit(limit int) *Client {
	c.subsLimit = limit
	return c
}

// Public creates and returns client to interact with public channels
func (c *Client) Public(url string) (*Client, error) {
	conn, _, _, err := ws.DefaultDialer.Dial(context.Background(), url)
	if err != nil {
		return nil, err
	}
	c.conn = conn
	return c, nil
}

// Private creates and returns client to interact with private channels
func (c *Client) Private(key, sec, url string, dms int) (*Client, error) {
	nonce := c.nonceGen.GetNonce()
	conn, _, _, err := ws.DefaultDialer.Dial(context.Background(), url)
	if err != nil {
		return nil, err
	}

	c.conn = conn
	payload := "AUTH" + nonce
	sig := hmac.New(sha512.New384, []byte(sec))
	if _, err := sig.Write([]byte(payload)); err != nil {
		return nil, err
	}

	pldSign := hex.EncodeToString(sig.Sum(nil))
	sub := event.Subscribe{
		Event:       "auth",
		APIKEY:      key,
		AuthSig:     pldSign,
		AuthPayload: payload,
		AuthNonce:   nonce,
		DMS:         dms,
	}

	if err := c.Subscribe(sub); err != nil {
		return nil, err
	}
	return c, nil
}

// Subscribe takes subscription payload as per docs and subscribes client to it.
// We keep track of subscriptions so that when client failes, we can resubscribe.
func (c *Client) Subscribe(sub event.Subscribe) error {
	if err := c.Send(sub); err != nil {
		return err
	}

	c.AddSub(sub)
	return nil
}

// Send takes payload in form of interface and sends it to api
func (c *Client) Send(pld interface{}) error {
	b, err := json.Marshal(pld)
	if err != nil {
		return err
	}

	return wsutil.WriteClientBinary(c.conn, b)
}

// Close closes the socket connection
func (c *Client) Close() error {
	return c.conn.Close()
}

// Read starts consuming data stream
func (c *Client) Read(ch chan<- msg.Msg) {
	defer c.conn.Close()

	for {
		ms, opCode, err := wsutil.ReadServerData(c.conn)
		m := msg.Msg{Data: ms, CID: c.id}

		if err != nil {
			m.Err = err
			ch <- m
			return
		}

		if opCode == ws.OpClose {
			m.Err = errors.New("client has closed unexpectedly")
			ch <- m
			return
		}

		ch <- m
	}
}

// SubsLimitReached returns true if number of subs > subsLimit
func (c *Client) SubsLimitReached() bool {
	if c.subsLimit == 0 {
		return false
	}
	return len(c.subs) == c.subsLimit
}

// SubAdded checks if given subscription is already added. Used to
// avoid duplicate subscriptions per client
func (c *Client) SubAdded(sub event.Subscribe) (isAdded bool) {
	_, isAdded = c.subs[sub]
	return
}

// AddSub adds new subscription to the list
func (c *Client) AddSub(sub event.Subscribe) {
	c.subs[sub] = true
}

// RemoveSub adds new subscription to the list
func (c *Client) RemoveSub(sub event.Subscribe) {
	delete(c.subs, sub)
}

// GetAllSubs returns all subscriptions
func (c *Client) GetAllSubs() (res []event.Subscribe) {
	for sub := range c.subs {
		res = append(res, sub)
	}
	return
}
<commit_msg>comment change<commit_after>package client

import (
	"context"
	"crypto/hmac"
	"crypto/sha512"
	"encoding/hex"
	"encoding/json"
	"errors"
	"net"

	"github.com/bitfinexcom/bitfinex-api-go/pkg/models/event"
	"github.com/bitfinexcom/bitfinex-api-go/pkg/mux/msg"
	"github.com/bitfinexcom/bitfinex-api-go/pkg/utils"
	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
)

type Client struct {
	id        int
	conn      net.Conn
	nonceGen  *utils.EpochNonceGenerator
	subsLimit int
	subs      map[event.Subscribe]bool
}

// New returns pointer to Client instance
func New() *Client {
	return &Client{
		subs:     make(map[event.Subscribe]bool),
		nonceGen: utils.NewEpochNonceGenerator(),
	}
}

// WithID assigns clinet ID
func (c *Client) WithID(ID int) *Client {
	c.id = ID
	return c
}

// WithSubsLimit sets limit of subscriptions on the instance
func (c *Client) WithSubsLimit(limit int) *Client {
	c.subsLimit = limit
	return c
}

// Public creates and returns client to interact with public channels
func (c *Client) Public(url string) (*Client, error) {
	conn, _, _, err := ws.DefaultDialer.Dial(context.Background(), url)
	if err != nil {
		return nil, err
	}
	c.conn = conn
	return c, nil
}

// Private creates and returns client to interact with private channels
func (c *Client) Private(key, sec, url string, dms int) (*Client, error) {
	nonce := c.nonceGen.GetNonce()
	conn, _, _, err := ws.DefaultDialer.Dial(context.Background(), url)
	if err != nil {
		return nil, err
	}

	c.conn = conn
	payload := "AUTH" + nonce
	sig := hmac.New(sha512.New384, []byte(sec))
	if _, err := sig.Write([]byte(payload)); err != nil {
		return nil, err
	}

	pldSign := hex.EncodeToString(sig.Sum(nil))
	sub := event.Subscribe{
		Event:       "auth",
		APIKEY:      key,
		AuthSig:     pldSign,
		AuthPayload: payload,
		AuthNonce:   nonce,
		DMS:         dms,
	}

	if err := c.Subscribe(sub); err != nil {
		return nil, err
	}
	return c, nil
}

// Subscribe takes subscription payload as per docs and subscribes client to it.
// We keep track of subscriptions so that when client failes, we can resubscribe.
func (c *Client) Subscribe(sub event.Subscribe) error {
	if err := c.Send(sub); err != nil {
		return err
	}

	c.AddSub(sub)
	return nil
}

// Send takes payload in form of interface and sends it to api
func (c *Client) Send(pld interface{}) error {
	b, err := json.Marshal(pld)
	if err != nil {
		return err
	}

	return wsutil.WriteClientBinary(c.conn, b)
}

// Close closes the socket connection
func (c *Client) Close() error {
	return c.conn.Close()
}

// Read starts consuming data stream
func (c *Client) Read(ch chan<- msg.Msg) {
	defer c.conn.Close()

	for {
		ms, opCode, err := wsutil.ReadServerData(c.conn)
		m := msg.Msg{Data: ms, CID: c.id}

		if err != nil {
			m.Err = err
			ch <- m
			return
		}

		if opCode == ws.OpClose {
			m.Err = errors.New("client has closed unexpectedly")
			ch <- m
			return
		}

		ch <- m
	}
}

// SubsLimitReached returns true if number of subs > subsLimit
func (c *Client) SubsLimitReached() bool {
	if c.subsLimit == 0 {
		return false
	}
	return len(c.subs) == c.subsLimit
}

// SubAdded checks if given subscription is already added. Used to
// avoid duplicate subscriptions per client
func (c *Client) SubAdded(sub event.Subscribe) (isAdded bool) {
	_, isAdded = c.subs[sub]
	return
}

// AddSub adds new subscription to the list
func (c *Client) AddSub(sub event.Subscribe) {
	c.subs[sub] = true
}

// RemoveSub removes new subscription to the list
func (c *Client) RemoveSub(sub event.Subscribe) {
	delete(c.subs, sub)
}

// GetAllSubs returns all subscriptions
func (c *Client) GetAllSubs() (res []event.Subscribe) {
	for sub := range c.subs {
		res = append(res, sub)
	}
	return
}
<|endoftext|>


<commit_before>package plugin

import (
	"github.com/Aptomi/aptomi/pkg/engine/resolve"
	"github.com/Aptomi/aptomi/pkg/event"
	"github.com/Aptomi/aptomi/pkg/external"
	"github.com/Aptomi/aptomi/pkg/lang"
	"github.com/Aptomi/aptomi/pkg/util"
)

// Registry is a registry of all Aptomi engine plugins
type Registry interface {
	ForCluster(cluster *lang.Cluster) (ClusterPlugin, error)
	ForCodeType(cluster *lang.Cluster, codeType string) (CodePlugin, error)
	PostProcess() []PostProcessPlugin
}

// RegistryFactory returns plugins registry on demand
type RegistryFactory func() Registry

// Base is a base interface for all engine plugins
type Base interface {
	Init() error
	Cleanup() error
}

// ClusterPlugin is a definition of cluster plugin which takes care of cluster operations such as validation
// in the cloud. It's created for specific cluster and enforcement cycle or API call.
type ClusterPlugin interface {
	Base

	Validate() error
}

type ClusterPluginConstructor func(cluster *lang.Cluster) ClusterPlugin

// CodePlugin is a definition of deployment plugin which takes care of creating, updating and destroying
// component instances in the cloud. It's created for specific cluster and enforcement cycle or API call.
type CodePlugin interface {
	Base

	Create(deployName string, params util.NestedParameterMap, eventLog *event.Log) error
	Update(deployName string, params util.NestedParameterMap, eventLog *event.Log) error
	Destroy(deployName string, params util.NestedParameterMap, eventLog *event.Log) error
	Endpoints(deployName string, params util.NestedParameterMap, eventLog *event.Log) (map[string]string, error)
}

type CodePluginConstructor func(cluster ClusterPlugin) CodePlugin

// PostProcessPlugin is a definition of post-processing plugin which gets called once by an action from the engine
// applier, after engine is done processing all component instances.
type PostProcessPlugin interface {
	Base

	Process(desiredPolicy *lang.Policy, desiredState *resolve.PolicyResolution, externalData *external.Data, eventLog *event.Log) error
}
<commit_msg>Update plugin constructors to include plugin configs and return error<commit_after>package plugin

import (
	"github.com/Aptomi/aptomi/pkg/config"
	"github.com/Aptomi/aptomi/pkg/engine/resolve"
	"github.com/Aptomi/aptomi/pkg/event"
	"github.com/Aptomi/aptomi/pkg/external"
	"github.com/Aptomi/aptomi/pkg/lang"
	"github.com/Aptomi/aptomi/pkg/util"
)

// Registry is a registry of all Aptomi engine plugins
type Registry interface {
	ForCluster(cluster *lang.Cluster) (ClusterPlugin, error)
	ForCodeType(cluster *lang.Cluster, codeType string) (CodePlugin, error)
	PostProcess() []PostProcessPlugin
}

// RegistryFactory returns plugins registry on demand
type RegistryFactory func() Registry

// Base is a base interface for all engine plugins
type Base interface {
	Cleanup() error
}

// ClusterPlugin is a definition of cluster plugin which takes care of cluster operations such as validation
// in the cloud. It's created for specific cluster and enforcement cycle or API call.
type ClusterPlugin interface {
	Base

	Validate() error
}

type ClusterPluginConstructor func(cluster *lang.Cluster, cfg config.Plugins) (ClusterPlugin, error)

// CodePlugin is a definition of deployment plugin which takes care of creating, updating and destroying
// component instances in the cloud. It's created for specific cluster and enforcement cycle or API call.
type CodePlugin interface {
	Base

	Create(deployName string, params util.NestedParameterMap, eventLog *event.Log) error
	Update(deployName string, params util.NestedParameterMap, eventLog *event.Log) error
	Destroy(deployName string, params util.NestedParameterMap, eventLog *event.Log) error
	Endpoints(deployName string, params util.NestedParameterMap, eventLog *event.Log) (map[string]string, error)
}

type CodePluginConstructor func(cluster ClusterPlugin, cfg config.Plugins) (CodePlugin, error)

// PostProcessPlugin is a definition of post-processing plugin which gets called once by an action from the engine
// applier, after engine is done processing all component instances.
type PostProcessPlugin interface {
	Base

	Process(desiredPolicy *lang.Policy, desiredState *resolve.PolicyResolution, externalData *external.Data, eventLog *event.Log) error
}
<|endoftext|>


<commit_before>package registry

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"time"

	"github.com/labstack/echo"
)

const defaultLimit = 50

// A Version describes a specific release of an application.
type Version struct {
	Name      string          `json:"name"`
	Version   string          `json:"version"`
	URL       string          `json:"url"`
	Sha256    string          `json:"sha256"`
	CreatedAt time.Time       `json:"created_at"`
	Size      string          `json:"size"`
	Manifest  json.RawMessage `json:"manifest"`
	TarPrefix string          `json:"tar_prefix"`
}

var errVersionNotFound = errors.New("Version not found")

var proxyClient = &http.Client{
	Timeout: 10 * time.Second,
}

// GetLatestVersion returns the latest version available from the list of
// registries by resolving them in sequence using the specified application
// name and channel name.
func GetLatestVersion(appName, channel string, registries []*url.URL) (*Version, error) {
	requestURI := fmt.Sprintf("/registry/%s/%s/latest",
		url.PathEscape(appName),
		url.PathEscape(channel))
	rc, ok, err := fetchUntilFound(registries, requestURI)
	if err != nil {
		return nil, err
	}
	if !ok {
		return nil, errVersionNotFound
	}
	defer rc.Close()
	var v *Version
	if err = json.NewDecoder(rc).Decode(&v); err != nil {
		return nil, err
	}
	return v, nil
}

// Proxy will proxy the given request to the registries in sequence and return
// the response as io.ReadCloser when finding a registry returning a HTTP 200OK
// response.
func Proxy(req *http.Request, registries []*url.URL) (io.ReadCloser, error) {
	rc, ok, err := fetchUntilFound(registries, req.RequestURI)
	if err != nil {
		return nil, err
	}
	if !ok {
		return nil, echo.NewHTTPError(http.StatusNotFound)
	}
	return rc, nil
}

// ProxyList will proxy the given request to the registries by aggregating the
// results along the way. It should be used for list endpoints.
func ProxyList(req *http.Request, registries []*url.URL) ([]json.RawMessage, error) {
	ref, err := url.Parse(req.RequestURI)
	if err != nil {
		return nil, err
	}

	refNoLimit := cloneURLWithoutQuery(ref, "cursor", "limit")

	var sortBy string
	var sortReverse bool
	var cursor, limit int

	q := ref.Query()
	if v, ok := q["cursor"]; ok {
		cursor, _ = strconv.Atoi(v[0])
	}
	if cursor <= 0 {
		cursor = 0
	}
	if v, ok := q["sort"]; ok {
		sortBy = v[0]
	}
	if len(sortBy) > 0 && sortBy[0] == '-' {
		sortReverse = true
		sortBy = sortBy[1:]
	}
	if sortBy == "" {
		sortBy = "name"
	}
	if v, ok := q["limit"]; ok {
		limit, _ = strconv.Atoi(v[0])
	}
	if limit <= 0 {
		limit = defaultLimit
	}

	uniques := make(map[string]struct{})

	list := make([]json.RawMessage, 0)
	for _, registry := range registries[:len(registries)-1] {
		list, err = fetchListUnique(registry, refNoLimit, list, uniques)
		if err != nil {
			return nil, err
		}
	}

	var ok bool
	list, ok = sortedList(list, cursor, sortBy, limit, sortReverse, false)
	if ok {
		return list, nil
	}

	rest := limit - (len(list) - cursor)

	lastRegistry := registries[len(registries)-1]
	refNewLimit, _ := url.Parse(refNoLimit.String())
	refQ := refNewLimit.Query()
	refQ.Add("limit", strconv.Itoa(rest))

	list, err = fetchListUnique(lastRegistry, ref, list, uniques)
	if err != nil {
		return nil, err
	}

	list, _ = sortedList(list, cursor, sortBy, limit, sortReverse, true)
	return list, nil
}

func fetchListUnique(registry, ref *url.URL, list []json.RawMessage, uniques map[string]struct{}) (result []json.RawMessage, err error) {
	result = list
	rc, ok, err := fetch(registry, ref)
	if err != nil {
		return
	}
	if !ok {
		return
	}
	defer rc.Close()
	var resp []json.RawMessage
	if err = json.NewDecoder(rc).Decode(&resp); err != nil {
		return
	}
	if len(resp) == 0 {
		return
	}
	var el struct {
		Name string `json:"name"`
	}
	for _, b := range resp {
		if err = json.Unmarshal(b, &el); err != nil {
			return
		}
		if _, ok = uniques[el.Name]; !ok {
			result = append(result, b)
			uniques[el.Name] = struct{}{}
		}
	}
	return
}

func fetchUntilFound(registries []*url.URL, requestURI string) (rc io.ReadCloser, ok bool, err error) {
	ref, err := url.Parse(requestURI)
	if err != nil {
		return
	}
	for _, registry := range registries {
		rc, ok, err = fetch(registry, ref)
		if err != nil {
			return
		}
		if !ok {
			continue
		}
		return
	}
	return nil, false, nil
}

func fetch(registry, ref *url.URL) (rc io.ReadCloser, ok bool, err error) {
	u := registry.ResolveReference(ref)
	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
	if err != nil {
		return
	}
	resp, err := proxyClient.Do(req)
	if err != nil {
		return
	}
	defer func() {
		if err != nil {
			resp.Body.Close()
		}
	}()
	if resp.StatusCode == 404 {
		return
	}
	if resp.StatusCode != 200 {
		var msg struct {
			Message string `json:"message"`
		}
		if err = json.NewDecoder(resp.Body).Decode(&msg); err != nil {
			err = echo.NewHTTPError(resp.StatusCode)
		} else {
			err = echo.NewHTTPError(resp.StatusCode, msg.Message)
		}
		return
	}
	return resp.Body, true, nil
}

func sortedList(list []json.RawMessage, cursor int, sortBy string, limit int, reverse, starved bool) ([]json.RawMessage, bool) {
	if cursor-len(list) < limit {
		if !starved {
			return list, false
		}
		if cursor > len(list) {
			cursor = len(list)
		}
		list = list[cursor:]
	}
	sort.Slice(list, func(i, j int) bool {
		var a, b map[string]interface{}
		json.Unmarshal(list[i], &a)
		json.Unmarshal(list[j], &b)
		var less bool
		switch valA := a[sortBy].(type) {
		case string:
			valB := b[sortBy].(string)
			less = valA < valB
		case int:
			valB := b[sortBy].(int)
			less = valA < valB
		}
		if reverse {
			return !less
		}
		return less
	})
	offset := cursor + limit
	if offset > len(list) {
		offset = len(list)
	}
	fmt.Printf("len(list)=%d cursor=%d offset=%d limit=%d\n", len(list), cursor, offset, limit)
	return list[cursor:offset], true
}

func cloneURLWithoutQuery(u *url.URL, filter ...string) *url.URL {
	u, _ = url.Parse(u.String())
	q1 := u.Query()
	q2 := make(url.Values)
	for k, v := range q1 {
		if len(v) == 0 {
			continue
		}
		var remove bool
		for _, f := range filter {
			if f == k {
				remove = true
				break
			}
		}
		if !remove {
			q2.Add(k, v[0])
		}
	}
	u.RawQuery = q2.Encode()
	return u
}
<commit_msg>Remove print<commit_after>package registry

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"time"

	"github.com/labstack/echo"
)

const defaultLimit = 50

// A Version describes a specific release of an application.
type Version struct {
	Name      string          `json:"name"`
	Version   string          `json:"version"`
	URL       string          `json:"url"`
	Sha256    string          `json:"sha256"`
	CreatedAt time.Time       `json:"created_at"`
	Size      string          `json:"size"`
	Manifest  json.RawMessage `json:"manifest"`
	TarPrefix string          `json:"tar_prefix"`
}

var errVersionNotFound = errors.New("Version not found")

var proxyClient = &http.Client{
	Timeout: 10 * time.Second,
}

// GetLatestVersion returns the latest version available from the list of
// registries by resolving them in sequence using the specified application
// name and channel name.
func GetLatestVersion(appName, channel string, registries []*url.URL) (*Version, error) {
	requestURI := fmt.Sprintf("/registry/%s/%s/latest",
		url.PathEscape(appName),
		url.PathEscape(channel))
	rc, ok, err := fetchUntilFound(registries, requestURI)
	if err != nil {
		return nil, err
	}
	if !ok {
		return nil, errVersionNotFound
	}
	defer rc.Close()
	var v *Version
	if err = json.NewDecoder(rc).Decode(&v); err != nil {
		return nil, err
	}
	return v, nil
}

// Proxy will proxy the given request to the registries in sequence and return
// the response as io.ReadCloser when finding a registry returning a HTTP 200OK
// response.
func Proxy(req *http.Request, registries []*url.URL) (io.ReadCloser, error) {
	rc, ok, err := fetchUntilFound(registries, req.RequestURI)
	if err != nil {
		return nil, err
	}
	if !ok {
		return nil, echo.NewHTTPError(http.StatusNotFound)
	}
	return rc, nil
}

// ProxyList will proxy the given request to the registries by aggregating the
// results along the way. It should be used for list endpoints.
func ProxyList(req *http.Request, registries []*url.URL) ([]json.RawMessage, error) {
	ref, err := url.Parse(req.RequestURI)
	if err != nil {
		return nil, err
	}

	refNoLimit := cloneURLWithoutQuery(ref, "cursor", "limit")

	var sortBy string
	var sortReverse bool
	var cursor, limit int

	q := ref.Query()
	if v, ok := q["cursor"]; ok {
		cursor, _ = strconv.Atoi(v[0])
	}
	if cursor <= 0 {
		cursor = 0
	}
	if v, ok := q["sort"]; ok {
		sortBy = v[0]
	}
	if len(sortBy) > 0 && sortBy[0] == '-' {
		sortReverse = true
		sortBy = sortBy[1:]
	}
	if sortBy == "" {
		sortBy = "name"
	}
	if v, ok := q["limit"]; ok {
		limit, _ = strconv.Atoi(v[0])
	}
	if limit <= 0 {
		limit = defaultLimit
	}

	uniques := make(map[string]struct{})

	list := make([]json.RawMessage, 0)
	for _, registry := range registries[:len(registries)-1] {
		list, err = fetchListUnique(registry, refNoLimit, list, uniques)
		if err != nil {
			return nil, err
		}
	}

	var ok bool
	list, ok = sortedList(list, cursor, sortBy, limit, sortReverse, false)
	if ok {
		return list, nil
	}

	rest := limit - (len(list) - cursor)

	lastRegistry := registries[len(registries)-1]
	refNewLimit, _ := url.Parse(refNoLimit.String())
	refQ := refNewLimit.Query()
	refQ.Add("limit", strconv.Itoa(rest))

	list, err = fetchListUnique(lastRegistry, ref, list, uniques)
	if err != nil {
		return nil, err
	}

	list, _ = sortedList(list, cursor, sortBy, limit, sortReverse, true)
	return list, nil
}

func fetchListUnique(registry, ref *url.URL, list []json.RawMessage, uniques map[string]struct{}) (result []json.RawMessage, err error) {
	result = list
	rc, ok, err := fetch(registry, ref)
	if err != nil {
		return
	}
	if !ok {
		return
	}
	defer rc.Close()
	var resp []json.RawMessage
	if err = json.NewDecoder(rc).Decode(&resp); err != nil {
		return
	}
	if len(resp) == 0 {
		return
	}
	var el struct {
		Name string `json:"name"`
	}
	for _, b := range resp {
		if err = json.Unmarshal(b, &el); err != nil {
			return
		}
		if _, ok = uniques[el.Name]; !ok {
			result = append(result, b)
			uniques[el.Name] = struct{}{}
		}
	}
	return
}

func fetchUntilFound(registries []*url.URL, requestURI string) (rc io.ReadCloser, ok bool, err error) {
	ref, err := url.Parse(requestURI)
	if err != nil {
		return
	}
	for _, registry := range registries {
		rc, ok, err = fetch(registry, ref)
		if err != nil {
			return
		}
		if !ok {
			continue
		}
		return
	}
	return nil, false, nil
}

func fetch(registry, ref *url.URL) (rc io.ReadCloser, ok bool, err error) {
	u := registry.ResolveReference(ref)
	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
	if err != nil {
		return
	}
	resp, err := proxyClient.Do(req)
	if err != nil {
		return
	}
	defer func() {
		if err != nil {
			resp.Body.Close()
		}
	}()
	if resp.StatusCode == 404 {
		return
	}
	if resp.StatusCode != 200 {
		var msg struct {
			Message string `json:"message"`
		}
		if err = json.NewDecoder(resp.Body).Decode(&msg); err != nil {
			err = echo.NewHTTPError(resp.StatusCode)
		} else {
			err = echo.NewHTTPError(resp.StatusCode, msg.Message)
		}
		return
	}
	return resp.Body, true, nil
}

func sortedList(list []json.RawMessage, cursor int, sortBy string, limit int, reverse, starved bool) ([]json.RawMessage, bool) {
	if cursor-len(list) < limit {
		if !starved {
			return list, false
		}
		if cursor > len(list) {
			cursor = len(list)
		}
		list = list[cursor:]
	}
	sort.Slice(list, func(i, j int) bool {
		var a, b map[string]interface{}
		json.Unmarshal(list[i], &a)
		json.Unmarshal(list[j], &b)
		var less bool
		switch valA := a[sortBy].(type) {
		case string:
			valB := b[sortBy].(string)
			less = valA < valB
		case int:
			valB := b[sortBy].(int)
			less = valA < valB
		}
		if reverse {
			return !less
		}
		return less
	})
	offset := cursor + limit
	if offset > len(list) {
		offset = len(list)
	}
	return list[cursor:offset], true
}

func cloneURLWithoutQuery(u *url.URL, filter ...string) *url.URL {
	u, _ = url.Parse(u.String())
	q1 := u.Query()
	q2 := make(url.Values)
	for k, v := range q1 {
		if len(v) == 0 {
			continue
		}
		var remove bool
		for _, f := range filter {
			if f == k {
				remove = true
				break
			}
		}
		if !remove {
			q2.Add(k, v[0])
		}
	}
	u.RawQuery = q2.Encode()
	return u
}
<|endoftext|>


<commit_before>// +build integration

package snap

import (
	"errors"
	"fmt"
	"github.com/intelsdi-x/snap/mgmt/rest/client"
	"github.com/intelsdi-x/snap/scheduler/wmap"
	"github.com/intelsdi-x/swan/pkg/executor"
	"io/ioutil"
	"net"
	"os"
	"path"
	"strings"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

type Snapd struct {
	task executor.Task
}

func NewSnapd() *Snapd {
	return &Snapd{}
}

func (s *Snapd) Execute() error {
	l := executor.NewLocal()
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		return errors.New("Cannot find GOPATH")
	}

	snapRoot := path.Join(gopath, "src", "github.com", "intelsdi-x", "snap", "build", "bin", "snapd")
	snapCommand := fmt.Sprintf("%s -t 0", snapRoot)

	task, err := l.Execute(snapCommand)
	if err != nil {
		return err
	}

	s.task = task

	return nil
}

func (s *Snapd) Stop() error {
	if s.task == nil {
		return errors.New("Snapd not started: cannot find task")
	}

	return s.task.Stop()
}

func (s *Snapd) Connected() bool {
	retries := 5
	connected := false
	for i := 0; i < retries; i++ {
		conn, err := net.Dial("tcp", "127.0.0.1:8181")
		if err != nil {
			time.Sleep(100 * time.Millisecond)
			continue
		}
		defer conn.Close()
		connected = true
	}

	return connected
}

func TestSnap(t *testing.T) {
	var snapd *Snapd
	var c *client.Client
	var s *Session
	var publisher *wmap.PublishWorkflowMapNode
	var metricsFile string

	Convey("Testing snap session", t, func() {
		Convey("Starting snapd", func() {
			snapd = NewSnapd()
			snapd.Execute()

			// Wait until snap is up.
			So(snapd.Connected(), ShouldBeTrue)
		})

		Convey("Connecting to snapd", func() {
			ct, err := client.New("http://127.0.0.1:8181", "v1", true)

			Convey("Shouldn't return any errors", func() {
				So(err, ShouldBeNil)
			})

			c = ct
		})

		Convey("Loading collectors", func() {
			plugins := NewPlugins(c)
			So(plugins, ShouldNotBeNil)
			err := plugins.Load("snap-plugin-collector-session-test")

			if err != nil {
				fmt.Printf("%v\n", err)
			}

			// Wait until metric is available in namespace.
			retries := 10
			found := false
			for i := 0; i < retries && !found; i++ {
				m := c.GetMetricCatalog()
				So(m.Err, ShouldBeNil)
				for _, metric := range m.Catalog {
					if metric.Namespace == "/intel/swan/session/metric1" {
						found = true
						break
					}
				}
				time.Sleep(500 * time.Millisecond)
			}
			So(found, ShouldBeTrue)
		})

		Convey("Loading publishers", func() {
			plugins := NewPlugins(c)
			So(plugins, ShouldNotBeNil)

			plugins.Load("snap-plugin-publisher-session-test")

			publisher = wmap.NewPublishNode("session-test", 1)

			So(publisher, ShouldNotBeNil)

			tmpfile, err := ioutil.TempFile("", "session_test")
			tmpfile.Close()
			So(err, ShouldBeNil)

			metricsFile = tmpfile.Name()

			publisher.AddConfigItem("file", metricsFile)
		})

		Convey("Creating a Snap experiment session", func() {
			s = NewSession([]string{"/intel/swan/session/metric1"}, 1*time.Second, c, publisher)
			So(s, ShouldNotBeNil)
		})

		Convey("Starting a session", func() {
			So(s, ShouldNotBeNil)
			err := s.Start("foobar", "barbaz")

			Convey("Shouldn't return any errors", func() {
				So(err, ShouldBeNil)
			})
		})

		Convey("Contacting snap to get the task status", func() {
			status, err := s.Status()

			So(err, ShouldBeNil)

			Convey("And the task should be running", func() {
				So(status, ShouldEqual, "Running")
			})
		})

		Convey("Reading samples from file", func() {
			retries := 5
			found := false
			for i := 0; i < retries; i++ {
				time.Sleep(500 * time.Millisecond)

				dat, err := ioutil.ReadFile(metricsFile)
				if err != nil {
					continue
				}

				if len(dat) > 0 {
					// Look for tag on metric line.
					lines := strings.Split(string(dat), "\n")
					if len(lines) < 1 {
						continue
					}

					columns := strings.Split(lines[0], "\t")
					if len(columns) < 2 {
						continue
					}

					tags := strings.Split(columns[1], ",")
					if len(tags) < 2 {
						continue
					}

					So(columns[0], ShouldEqual, "/intel/swan/session/metric1")
					So(tags[0], ShouldEqual, "swan_experiment=foobar")
					So(tags[1], ShouldEqual, "swan_phase=barbaz")

					found = true

					break
				}
			}

			So(found, ShouldBeTrue)
		})

		Convey("Stopping a session", func() {
			So(s, ShouldNotBeNil)
			err := s.Stop()

			So(err, ShouldBeNil)

			_, err = s.Status()
			So(err, ShouldNotBeNil)
		})

		Convey("Stopping snapd", func() {
			So(snapd, ShouldNotBeNil)

			if snapd != nil {
				snapd.Stop()
			}
		})
	})
}
<commit_msg>SCE-331: Fixed bug in loading plugin with newest snap.<commit_after>// +build integration

package snap

import (
	"errors"
	"fmt"
	"github.com/intelsdi-x/snap/mgmt/rest/client"
	"github.com/intelsdi-x/snap/scheduler/wmap"
	"github.com/intelsdi-x/swan/pkg/executor"
	"io/ioutil"
	"net"
	"os"
	"path"
	"strings"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

type Snapd struct {
	task executor.Task
}

func NewSnapd() *Snapd {
	return &Snapd{}
}

func (s *Snapd) Execute() error {
	l := executor.NewLocal()
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		return errors.New("Cannot find GOPATH")
	}

	snapRoot := path.Join(gopath, "src", "github.com", "intelsdi-x", "snap", "build", "bin", "snapd")
	snapCommand := fmt.Sprintf("%s -t 0", snapRoot)

	task, err := l.Execute(snapCommand)
	if err != nil {
		return err
	}

	s.task = task

	return nil
}

func (s *Snapd) Stop() error {
	if s.task == nil {
		return errors.New("Snapd not started: cannot find task")
	}

	return s.task.Stop()
}

func (s *Snapd) CleanAndEraseOutput() error {
	if s.task == nil {
		return errors.New("Snapd not started: cannot find task")
	}

	s.task.Clean()
	return s.task.EraseOutput()
}

func (s *Snapd) Connected() bool {
	retries := 5
	connected := false
	for i := 0; i < retries; i++ {
		conn, err := net.Dial("tcp", "127.0.0.1:8181")
		if err != nil {
			time.Sleep(100 * time.Millisecond)
			continue
		}
		defer conn.Close()
		connected = true
	}

	return connected
}

func TestSnap(t *testing.T) {
	var snapd *Snapd
	var c *client.Client
	var s *Session
	var publisher *wmap.PublishWorkflowMapNode
	var metricsFile string

	Convey("Testing snap session", t, func() {
		Convey("Starting snapd", func() {
			snapd = NewSnapd()
			snapd.Execute()

			// Wait until snap is up.
			So(snapd.Connected(), ShouldBeTrue)
		})

		Convey("Connecting to snapd", func() {
			ct, err := client.New("http://127.0.0.1:8181", "v1", true)

			Convey("Shouldn't return any errors", func() {
				So(err, ShouldBeNil)
			})

			c = ct
		})

		Convey("Loading collectors", func() {
			plugins := NewPlugins(c)
			So(plugins, ShouldNotBeNil)
			err := plugins.Load("snap-plugin-collector-session-test")
			So(err, ShouldBeNil)

			// Wait until metric is available in namespace.
			retries := 10
			found := false
			for i := 0; i < retries && !found; i++ {
				m := c.GetMetricCatalog()
				So(m.Err, ShouldBeNil)
				for _, metric := range m.Catalog {
					if metric.Namespace == "/intel/swan/session/metric1" {
						found = true
						break
					}
				}
				time.Sleep(500 * time.Millisecond)
			}
			So(found, ShouldBeTrue)
		})

		Convey("Loading publishers", func() {
			plugins := NewPlugins(c)
			So(plugins, ShouldNotBeNil)

			plugins.Load("snap-plugin-publisher-session-test")

			publisher = wmap.NewPublishNode("session-test", 1)

			So(publisher, ShouldNotBeNil)

			tmpfile, err := ioutil.TempFile("", "session_test")
			tmpfile.Close()
			So(err, ShouldBeNil)

			metricsFile = tmpfile.Name()

			publisher.AddConfigItem("file", metricsFile)
		})

		Convey("Creating a Snap experiment session", func() {
			s = NewSession([]string{"/intel/swan/session/metric1"}, 1*time.Second, c, publisher)
			So(s, ShouldNotBeNil)
		})

		Convey("Starting a session", func() {
			So(s, ShouldNotBeNil)
			err := s.Start("foobar", "barbaz")

			Convey("Shouldn't return any errors", func() {
				So(err, ShouldBeNil)
			})
		})

		Convey("Contacting snap to get the task status", func() {
			status, err := s.Status()

			So(err, ShouldBeNil)

			Convey("And the task should be running", func() {
				So(status, ShouldEqual, "Running")
			})
		})

		Convey("Reading samples from file", func() {
			retries := 5
			found := false
			for i := 0; i < retries; i++ {
				time.Sleep(500 * time.Millisecond)

				dat, err := ioutil.ReadFile(metricsFile)
				if err != nil {
					continue
				}

				if len(dat) > 0 {
					// Look for tag on metric line.
					lines := strings.Split(string(dat), "\n")
					if len(lines) < 1 {
						continue
					}

					columns := strings.Split(lines[0], "\t")
					if len(columns) < 2 {
						continue
					}

					tags := strings.Split(columns[1], ",")
					if len(tags) < 2 {
						continue
					}

					So(columns[0], ShouldEqual, "/intel/swan/session/metric1")
					So(tags[0], ShouldEqual, "swan_experiment=foobar")
					So(tags[1], ShouldEqual, "swan_phase=barbaz")

					found = true

					break
				}
			}

			So(found, ShouldBeTrue)
		})

		Convey("Stopping a session", func() {
			So(s, ShouldNotBeNil)
			err := s.Stop()

			So(err, ShouldBeNil)

			_, err = s.Status()
			So(err, ShouldNotBeNil)
		})

		Convey("Stopping snapd", func() {
			So(snapd, ShouldNotBeNil)

			if snapd != nil {
				snapd.Stop()
				snapd.CleanAndEraseOutput()
			}
		})
	})
}
<|endoftext|>


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

package spanstat

import (
	"time"
)

// SpanStat measures the total duration of all time spent in between Start()
// and Stop() calls.
type SpanStat struct {
	spanStart       time.Time
	successDuration time.Duration
	failureDuration time.Duration
}

// Start starts a new span
func (s *SpanStat) Start() {
	s.spanStart = time.Now()
}

// End ends the current span and adds the measured duration to the total
// cumulated duration, and to the success or failure cumulated duration
// depending on the given success flag
func (s *SpanStat) End(success bool) {
	if !s.spanStart.IsZero() {
		d := time.Since(s.spanStart)
		if success {
			s.successDuration += d
		} else {
			s.failureDuration += d
		}
	}
	s.spanStart = time.Time{}
}

// Total returns the total duration of all spans measured, including both
// successes and failures
func (s *SpanStat) Total() time.Duration {
	return s.successDuration + s.failureDuration
}

// SuccessTotal returns the total duration of all successful spans measured
func (s *SpanStat) SuccessTotal() time.Duration {
	return s.successDuration
}

// FailureTotal returns the total duration of all successful spans measured
func (s *SpanStat) FailureTotal() time.Duration {
	return s.failureDuration
}

// Reset rests the duration measurements
func (s *SpanStat) Reset() {
	s.successDuration = 0
	s.failureDuration = 0
}
<commit_msg>Spanstat: Validate that the duration is not negative when call End.<commit_after>// Copyright 2018 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package spanstat

import (
	"runtime/debug"
	"time"

	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/pkg/logging/logfields"
)

var (
	subSystem = "spanstat"
	log       = logging.DefaultLogger.WithField(logfields.LogSubsys, subSystem)
)

// SpanStat measures the total duration of all time spent in between Start()
// and Stop() calls.
type SpanStat struct {
	spanStart       time.Time
	successDuration time.Duration
	failureDuration time.Duration
}

// Start starts a new span
func (s *SpanStat) Start() {
	s.spanStart = time.Now()
}

// End ends the current span and adds the measured duration to the total
// cumulated duration, and to the success or failure cumulated duration
// depending on the given success flag
func (s *SpanStat) End(success bool) {
	if !s.spanStart.IsZero() {
		d := time.Since(s.spanStart)
		if d >= time.Duration(0) {
			if success {
				s.successDuration += d
			} else {
				s.failureDuration += d
			}
		} else {
			log.WithField("duration", d).Warn("Duration is negative on End")
			debug.PrintStack()
		}
	}
	s.spanStart = time.Time{}
}

// Total returns the total duration of all spans measured, including both
// successes and failures
func (s *SpanStat) Total() time.Duration {
	return s.successDuration + s.failureDuration
}

// SuccessTotal returns the total duration of all successful spans measured
func (s *SpanStat) SuccessTotal() time.Duration {
	return s.successDuration
}

// FailureTotal returns the total duration of all successful spans measured
func (s *SpanStat) FailureTotal() time.Duration {
	return s.failureDuration
}

// Reset rests the duration measurements
func (s *SpanStat) Reset() {
	s.successDuration = 0
	s.failureDuration = 0
}
<|endoftext|>


<commit_before>// +build windows

package term

import (
	"io"
	"os"
	"os/signal"
	"syscall"

	"github.com/Azure/go-ansiterm/winterm"
	"github.com/docker/docker/pkg/system"
	"github.com/docker/docker/pkg/term/windows"
)

// State holds the console mode for the terminal.
type State struct {
	inMode, outMode     uint32
	inHandle, outHandle syscall.Handle
}

// Winsize is used for window size.
type Winsize struct {
	Height uint16
	Width  uint16
	x      uint16
	y      uint16
}

const (
	// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx
	enableVirtualTerminalInput      = 0x0200
	enableVirtualTerminalProcessing = 0x0004
)

// usingNativeConsole is true if we are using the Windows native console
var usingNativeConsole bool

// StdStreams returns the standard streams (stdin, stdout, stedrr).
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
	switch {
	case os.Getenv("ConEmuANSI") == "ON":
		// The ConEmu terminal emulates ANSI on output streams well.
		return windows.ConEmuStreams()
	case os.Getenv("MSYSTEM") != "":
		// MSYS (mingw) does not emulate ANSI well.
		return windows.ConsoleStreams()
	default:
		if useNativeConsole() {
			usingNativeConsole = true
			return os.Stdin, os.Stdout, os.Stderr
		}
		return windows.ConsoleStreams()
	}
}

// useNativeConsole determines if the docker client should use the built-in
// console which supports ANSI emulation, or fall-back to the golang emulator
// (github.com/azure/go-ansiterm).
func useNativeConsole() bool {
	osv, err := system.GetOSVersion()
	if err != nil {
		return false
	}

	// Native console is not available before major version 10
	if osv.MajorVersion < 10 {
		return false
	}

	// Must have a late pre-release TP4 build of Windows Server 2016/Windows 10 TH2 or later
	if osv.Build < 10578 {
		return false
	}

	// Get the console modes. If this fails, we can't use the native console
	state, err := getNativeConsole()
	if err != nil {
		return false
	}

	// Probe the console to see if it can be enabled.
	if nil != probeNativeConsole(state) {
		return false
	}

	// Environment variable override
	if e := os.Getenv("USE_NATIVE_CONSOLE"); e != "" {
		if e == "1" {
			return true
		}
		return false
	}

	// TODO Windows. The native emulator still has issues which
	// mean it shouldn't be enabled for everyone. Change this next line to true
	// to change the default to "enable if available". In the meantime, users
	// can still try it out by using USE_NATIVE_CONSOLE env variable.
	return false
}

// getNativeConsole returns the console modes ('state') for the native Windows console
func getNativeConsole() (State, error) {
	var (
		err   error
		state State
	)

	// Get the handle to stdout
	if state.outHandle, err = syscall.GetStdHandle(syscall.STD_OUTPUT_HANDLE); err != nil {
		return state, err
	}

	// Get the console mode from the consoles stdout handle
	if err = syscall.GetConsoleMode(state.outHandle, &state.outMode); err != nil {
		return state, err
	}

	// Get the handle to stdin
	if state.inHandle, err = syscall.GetStdHandle(syscall.STD_INPUT_HANDLE); err != nil {
		return state, err
	}

	// Get the console mode from the consoles stdin handle
	if err = syscall.GetConsoleMode(state.inHandle, &state.inMode); err != nil {
		return state, err
	}

	return state, nil
}

// probeNativeConsole probes the console to determine if native can be supported,
func probeNativeConsole(state State) error {
	if err := winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode|enableVirtualTerminalProcessing); err != nil {
		return err
	}
	defer winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode)

	if err := winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode|enableVirtualTerminalInput); err != nil {
		return err
	}
	defer winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode)

	return nil
}

// enableNativeConsole turns on native console mode
func enableNativeConsole(state State) error {
	if err := winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode|enableVirtualTerminalProcessing); err != nil {
		return err
	}

	if err := winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode|enableVirtualTerminalInput); err != nil {
		winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode) // restore out if we can
		return err
	}

	return nil
}

// disableNativeConsole turns off native console mode
func disableNativeConsole(state *State) error {
	// Try and restore both in an out before error checking.
	errout := winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode)
	errin := winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode)
	if errout != nil {
		return errout
	}
	if errin != nil {
		return errin
	}
	return nil
}

// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
func GetFdInfo(in interface{}) (uintptr, bool) {
	return windows.GetHandleInfo(in)
}

// GetWinsize returns the window size based on the specified file descriptor.
func GetWinsize(fd uintptr) (*Winsize, error) {
	info, err := winterm.GetConsoleScreenBufferInfo(fd)
	if err != nil {
		return nil, err
	}

	winsize := &Winsize{
		Width:  uint16(info.Window.Right - info.Window.Left + 1),
		Height: uint16(info.Window.Bottom - info.Window.Top + 1),
		x:      0,
		y:      0}

	return winsize, nil
}

// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd uintptr) bool {
	return windows.IsConsole(fd)
}

// RestoreTerminal restores the terminal connected to the given file descriptor
// to a previous state.
func RestoreTerminal(fd uintptr, state *State) error {
	if usingNativeConsole {
		return disableNativeConsole(state)
	}
	return winterm.SetConsoleMode(fd, state.outMode)
}

// SaveState saves the state of the terminal connected to the given file descriptor.
func SaveState(fd uintptr) (*State, error) {
	if usingNativeConsole {
		state, err := getNativeConsole()
		if err != nil {
			return nil, err
		}
		return &state, nil
	}

	mode, e := winterm.GetConsoleMode(fd)
	if e != nil {
		return nil, e
	}

	return &State{outMode: mode}, nil
}

// DisableEcho disables echo for the terminal connected to the given file descriptor.
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
func DisableEcho(fd uintptr, state *State) error {
	mode := state.inMode
	mode &^= winterm.ENABLE_ECHO_INPUT
	mode |= winterm.ENABLE_PROCESSED_INPUT | winterm.ENABLE_LINE_INPUT
	err := winterm.SetConsoleMode(fd, mode)
	if err != nil {
		return err
	}

	// Register an interrupt handler to catch and restore prior state
	restoreAtInterrupt(fd, state)
	return nil
}

// SetRawTerminal puts the terminal connected to the given file descriptor into raw
// mode and returns the previous state.
func SetRawTerminal(fd uintptr) (*State, error) {
	state, err := MakeRaw(fd)
	if err != nil {
		return nil, err
	}

	// Register an interrupt handler to catch and restore prior state
	restoreAtInterrupt(fd, state)
	return state, err
}

// MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be restored.
func MakeRaw(fd uintptr) (*State, error) {
	state, err := SaveState(fd)
	if err != nil {
		return nil, err
	}

	mode := state.inMode
	if usingNativeConsole {
		if err := enableNativeConsole(*state); err != nil {
			return nil, err
		}
		mode |= enableVirtualTerminalInput
	}

	// See
	// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
	// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx

	// Disable these modes
	mode &^= winterm.ENABLE_ECHO_INPUT
	mode &^= winterm.ENABLE_LINE_INPUT
	mode &^= winterm.ENABLE_MOUSE_INPUT
	mode &^= winterm.ENABLE_WINDOW_INPUT
	mode &^= winterm.ENABLE_PROCESSED_INPUT

	// Enable these modes
	mode |= winterm.ENABLE_EXTENDED_FLAGS
	mode |= winterm.ENABLE_INSERT_MODE
	mode |= winterm.ENABLE_QUICK_EDIT_MODE

	err = winterm.SetConsoleMode(fd, mode)
	if err != nil {
		return nil, err
	}
	return state, nil
}

func restoreAtInterrupt(fd uintptr, state *State) {
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, os.Interrupt)

	go func() {
		_ = <-sigchan
		RestoreTerminal(fd, state)
		os.Exit(0)
	}()
}
<commit_msg>Windows: Native console disableNewlineAutoReturn<commit_after>// +build windows

package term

import (
	"io"
	"os"
	"os/signal"
	"syscall"

	"github.com/Azure/go-ansiterm/winterm"
	"github.com/docker/docker/pkg/system"
	"github.com/docker/docker/pkg/term/windows"
)

// State holds the console mode for the terminal.
type State struct {
	inMode, outMode     uint32
	inHandle, outHandle syscall.Handle
}

// Winsize is used for window size.
type Winsize struct {
	Height uint16
	Width  uint16
	x      uint16
	y      uint16
}

const (
	// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx
	enableVirtualTerminalInput      = 0x0200
	enableVirtualTerminalProcessing = 0x0004
	disableNewlineAutoReturn        = 0x0008
)

// usingNativeConsole is true if we are using the Windows native console
var usingNativeConsole bool

// StdStreams returns the standard streams (stdin, stdout, stedrr).
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
	switch {
	case os.Getenv("ConEmuANSI") == "ON":
		// The ConEmu terminal emulates ANSI on output streams well.
		return windows.ConEmuStreams()
	case os.Getenv("MSYSTEM") != "":
		// MSYS (mingw) does not emulate ANSI well.
		return windows.ConsoleStreams()
	default:
		if useNativeConsole() {
			usingNativeConsole = true
			return os.Stdin, os.Stdout, os.Stderr
		}
		return windows.ConsoleStreams()
	}
}

// useNativeConsole determines if the docker client should use the built-in
// console which supports ANSI emulation, or fall-back to the golang emulator
// (github.com/azure/go-ansiterm).
func useNativeConsole() bool {
	osv, err := system.GetOSVersion()
	if err != nil {
		return false
	}

	// Native console is not available before major version 10
	if osv.MajorVersion < 10 {
		return false
	}

	// Must have a late pre-release TP4 build of Windows Server 2016/Windows 10 TH2 or later
	if osv.Build < 10578 {
		return false
	}

	// Get the console modes. If this fails, we can't use the native console
	state, err := getNativeConsole()
	if err != nil {
		return false
	}

	// Probe the console to see if it can be enabled.
	if nil != probeNativeConsole(state) {
		return false
	}

	// Environment variable override
	if e := os.Getenv("USE_NATIVE_CONSOLE"); e != "" {
		if e == "1" {
			return true
		}
		return false
	}

	// TODO Windows. The native emulator still has issues which
	// mean it shouldn't be enabled for everyone. Change this next line to true
	// to change the default to "enable if available". In the meantime, users
	// can still try it out by using USE_NATIVE_CONSOLE env variable.
	return false
}

// getNativeConsole returns the console modes ('state') for the native Windows console
func getNativeConsole() (State, error) {
	var (
		err   error
		state State
	)

	// Get the handle to stdout
	if state.outHandle, err = syscall.GetStdHandle(syscall.STD_OUTPUT_HANDLE); err != nil {
		return state, err
	}

	// Get the console mode from the consoles stdout handle
	if err = syscall.GetConsoleMode(state.outHandle, &state.outMode); err != nil {
		return state, err
	}

	// Get the handle to stdin
	if state.inHandle, err = syscall.GetStdHandle(syscall.STD_INPUT_HANDLE); err != nil {
		return state, err
	}

	// Get the console mode from the consoles stdin handle
	if err = syscall.GetConsoleMode(state.inHandle, &state.inMode); err != nil {
		return state, err
	}

	return state, nil
}

// probeNativeConsole probes the console to determine if native can be supported,
func probeNativeConsole(state State) error {
	if err := winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode|enableVirtualTerminalProcessing); err != nil {
		return err
	}
	defer winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode)

	if err := winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode|enableVirtualTerminalInput); err != nil {
		return err
	}
	defer winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode)

	return nil
}

// enableNativeConsole turns on native console mode
func enableNativeConsole(state State) error {
	// First attempt both enableVirtualTerminalProcessing and disableNewlineAutoReturn
	if err := winterm.SetConsoleMode(uintptr(state.outHandle),
		state.outMode|(enableVirtualTerminalProcessing|disableNewlineAutoReturn)); err != nil {

		// That may fail, so fallback to trying just enableVirtualTerminalProcessing
		if err := winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode|enableVirtualTerminalProcessing); err != nil {
			return err
		}
	}

	if err := winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode|enableVirtualTerminalInput); err != nil {
		winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode) // restore out if we can
		return err
	}

	return nil
}

// disableNativeConsole turns off native console mode
func disableNativeConsole(state *State) error {
	// Try and restore both in an out before error checking.
	errout := winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode)
	errin := winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode)
	if errout != nil {
		return errout
	}
	if errin != nil {
		return errin
	}
	return nil
}

// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
func GetFdInfo(in interface{}) (uintptr, bool) {
	return windows.GetHandleInfo(in)
}

// GetWinsize returns the window size based on the specified file descriptor.
func GetWinsize(fd uintptr) (*Winsize, error) {
	info, err := winterm.GetConsoleScreenBufferInfo(fd)
	if err != nil {
		return nil, err
	}

	winsize := &Winsize{
		Width:  uint16(info.Window.Right - info.Window.Left + 1),
		Height: uint16(info.Window.Bottom - info.Window.Top + 1),
		x:      0,
		y:      0}

	return winsize, nil
}

// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd uintptr) bool {
	return windows.IsConsole(fd)
}

// RestoreTerminal restores the terminal connected to the given file descriptor
// to a previous state.
func RestoreTerminal(fd uintptr, state *State) error {
	if usingNativeConsole {
		return disableNativeConsole(state)
	}
	return winterm.SetConsoleMode(fd, state.outMode)
}

// SaveState saves the state of the terminal connected to the given file descriptor.
func SaveState(fd uintptr) (*State, error) {
	if usingNativeConsole {
		state, err := getNativeConsole()
		if err != nil {
			return nil, err
		}
		return &state, nil
	}

	mode, e := winterm.GetConsoleMode(fd)
	if e != nil {
		return nil, e
	}

	return &State{outMode: mode}, nil
}

// DisableEcho disables echo for the terminal connected to the given file descriptor.
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
func DisableEcho(fd uintptr, state *State) error {
	mode := state.inMode
	mode &^= winterm.ENABLE_ECHO_INPUT
	mode |= winterm.ENABLE_PROCESSED_INPUT | winterm.ENABLE_LINE_INPUT
	err := winterm.SetConsoleMode(fd, mode)
	if err != nil {
		return err
	}

	// Register an interrupt handler to catch and restore prior state
	restoreAtInterrupt(fd, state)
	return nil
}

// SetRawTerminal puts the terminal connected to the given file descriptor into raw
// mode and returns the previous state.
func SetRawTerminal(fd uintptr) (*State, error) {
	state, err := MakeRaw(fd)
	if err != nil {
		return nil, err
	}

	// Register an interrupt handler to catch and restore prior state
	restoreAtInterrupt(fd, state)
	return state, err
}

// MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be restored.
func MakeRaw(fd uintptr) (*State, error) {
	state, err := SaveState(fd)
	if err != nil {
		return nil, err
	}

	mode := state.inMode
	if usingNativeConsole {
		if err := enableNativeConsole(*state); err != nil {
			return nil, err
		}
		mode |= enableVirtualTerminalInput
	}

	// See
	// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
	// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx

	// Disable these modes
	mode &^= winterm.ENABLE_ECHO_INPUT
	mode &^= winterm.ENABLE_LINE_INPUT
	mode &^= winterm.ENABLE_MOUSE_INPUT
	mode &^= winterm.ENABLE_WINDOW_INPUT
	mode &^= winterm.ENABLE_PROCESSED_INPUT

	// Enable these modes
	mode |= winterm.ENABLE_EXTENDED_FLAGS
	mode |= winterm.ENABLE_INSERT_MODE
	mode |= winterm.ENABLE_QUICK_EDIT_MODE

	err = winterm.SetConsoleMode(fd, mode)
	if err != nil {
		return nil, err
	}
	return state, nil
}

func restoreAtInterrupt(fd uintptr, state *State) {
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, os.Interrupt)

	go func() {
		_ = <-sigchan
		RestoreTerminal(fd, state)
		os.Exit(0)
	}()
}
<|endoftext|>


<commit_before>// +build darwin dragonfly freebsd linux netbsd openbsd solaris

// Functions shared between linux/darwin.
package allocdir

import (
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"strconv"

	"golang.org/x/sys/unix"
)

var (
	// SharedAllocContainerPath is the path inside container for mounted
	// directory shared across tasks in a task group.
	SharedAllocContainerPath = filepath.Join("/", SharedAllocName)

	// TaskLocalContainer is the path inside a container for mounted directory
	// for local storage.
	TaskLocalContainerPath = filepath.Join("/", TaskLocal)

	// TaskSecretsContainerPath is the path inside a container for mounted
	// secrets directory
	TaskSecretsContainerPath = filepath.Join("/", TaskSecrets)
)

func (d *AllocDir) linkOrCopy(src, dst string, perm os.FileMode) error {
	// Avoid link/copy if the file already exists in the chroot
	// TODO 0.6 clean this up. This was needed because chroot creation fails
	// when a process restarts.
	if fileInfo, _ := os.Stat(dst); fileInfo != nil {
		return nil
	}
	// Attempt to hardlink.
	if err := os.Link(src, dst); err == nil {
		return nil
	}

	return fileCopy(src, dst, perm)
}

func (d *AllocDir) dropDirPermissions(path string) error {
	// Can't do anything if not root.
	if unix.Geteuid() != 0 {
		return nil
	}

	u, err := user.Lookup("nobody")
	if err != nil {
		return err
	}

	uid, err := getUid(u)
	if err != nil {
		return err
	}

	gid, err := getGid(u)
	if err != nil {
		return err
	}

	if err := os.Chown(path, uid, gid); err != nil {
		return fmt.Errorf("Couldn't change owner/group of %v to (uid: %v, gid: %v): %v", path, uid, gid, err)
	}

	if err := os.Chmod(path, 0777); err != nil {
		return fmt.Errorf("Chmod(%v) failed: %v", path, err)
	}

	return nil
}

func getUid(u *user.User) (int, error) {
	uid, err := strconv.Atoi(u.Uid)
	if err != nil {
		return 0, fmt.Errorf("Unable to convert Uid to an int: %v", err)
	}

	return uid, nil
}

func getGid(u *user.User) (int, error) {
	gid, err := strconv.Atoi(u.Gid)
	if err != nil {
		return 0, fmt.Errorf("Unable to convert Gid to an int: %v", err)
	}

	return gid, nil
}
<commit_msg>Change folder permission event when not root<commit_after>// +build darwin dragonfly freebsd linux netbsd openbsd solaris

// Functions shared between linux/darwin.
package allocdir

import (
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"strconv"

	"golang.org/x/sys/unix"
)

var (
	// SharedAllocContainerPath is the path inside container for mounted
	// directory shared across tasks in a task group.
	SharedAllocContainerPath = filepath.Join("/", SharedAllocName)

	// TaskLocalContainer is the path inside a container for mounted directory
	// for local storage.
	TaskLocalContainerPath = filepath.Join("/", TaskLocal)

	// TaskSecretsContainerPath is the path inside a container for mounted
	// secrets directory
	TaskSecretsContainerPath = filepath.Join("/", TaskSecrets)
)

func (d *AllocDir) linkOrCopy(src, dst string, perm os.FileMode) error {
	// Avoid link/copy if the file already exists in the chroot
	// TODO 0.6 clean this up. This was needed because chroot creation fails
	// when a process restarts.
	if fileInfo, _ := os.Stat(dst); fileInfo != nil {
		return nil
	}
	// Attempt to hardlink.
	if err := os.Link(src, dst); err == nil {
		return nil
	}

	return fileCopy(src, dst, perm)
}

func (d *AllocDir) dropDirPermissions(path string) error {
	if err := os.Chmod(path, 0777); err != nil {
		return fmt.Errorf("Chmod(%v) failed: %v", path, err)
	}

	// Can't change owner if not root.
	if unix.Geteuid() != 0 {
		return nil
	}

	u, err := user.Lookup("nobody")
	if err != nil {
		return err
	}

	uid, err := getUid(u)
	if err != nil {
		return err
	}

	gid, err := getGid(u)
	if err != nil {
		return err
	}

	if err := os.Chown(path, uid, gid); err != nil {
		return fmt.Errorf("Couldn't change owner/group of %v to (uid: %v, gid: %v): %v", path, uid, gid, err)
	}

	return nil
}

func getUid(u *user.User) (int, error) {
	uid, err := strconv.Atoi(u.Uid)
	if err != nil {
		return 0, fmt.Errorf("Unable to convert Uid to an int: %v", err)
	}

	return uid, nil
}

func getGid(u *user.User) (int, error) {
	gid, err := strconv.Atoi(u.Gid)
	if err != nil {
		return 0, fmt.Errorf("Unable to convert Gid to an int: %v", err)
	}

	return gid, nil
}
<|endoftext|>