<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package libkb

import (
	"fmt"
	"io"
	"os"
	"strings"
	"time"

	"github.com/keybase/go-crypto/openpgp"
)

type KeyringFile struct {
	filename         string
	Entities         openpgp.EntityList
	isPublic         bool
	indexID          map[string](*openpgp.Entity) // Map of 64-bit uppercase-hex KeyIds
	indexFingerprint map[PGPFingerprint](*openpgp.Entity)
	Contextified
}

type Keyrings struct {
	Contextified
}

func NewKeyrings(g *GlobalContext) *Keyrings {
	ret := &Keyrings{
		Contextified: Contextified{g: g},
	}
	return ret
}

//===================================================================

func (g *GlobalContext) SKBFilenameForUser(un NormalizedUsername) string {
	tmp := g.Env.GetSecretKeyringTemplate()
	token := "%u"
	if strings.Index(tmp, token) < 0 {
		return tmp
	}

	return strings.Replace(tmp, token, un.String(), -1)
}

func LoadSKBKeyring(un NormalizedUsername, g *GlobalContext) (*SKBKeyringFile, error) {
	if un.IsNil() {
		return nil, NoUsernameError{}
	}

	skbfile := NewSKBKeyringFile(g, un)
	err := skbfile.LoadAndIndex()
	if err != nil && !os.IsNotExist(err) {
		return nil, err
	}
	return skbfile, nil
}

func StatSKBKeyringMTime(un NormalizedUsername, g *GlobalContext) (mtime time.Time, err error) {
	if un.IsNil() {
		return mtime, NoUsernameError{}
	}
	return NewSKBKeyringFile(g, un).MTime()
}

func (k *KeyringFile) LoadAndIndex() error {
	var err error
	k.G().Log.Debug("+ LoadAndIndex on %s", k.filename)
	if err = k.Load(); err == nil {
		err = k.Index()
	}
	k.G().Log.Debug("- LoadAndIndex on %s -> %s", k.filename, ErrToOk(err))
	return err
}

func (k *KeyringFile) Index() error {
	k.G().Log.Debug("+ Index on %s", k.filename)
	k.indexID = make(map[string](*openpgp.Entity))
	k.indexFingerprint = make(map[PGPFingerprint](*openpgp.Entity))
	p := 0
	s := 0
	for _, entity := range k.Entities {
		if entity.PrimaryKey != nil {
			id := entity.PrimaryKey.KeyIdString()
			k.indexID[id] = entity
			fp := PGPFingerprint(entity.PrimaryKey.Fingerprint)
			k.indexFingerprint[fp] = entity
			p++
		}
		for _, subkey := range entity.Subkeys {
			if subkey.PublicKey != nil {
				id := subkey.PublicKey.KeyIdString()
				k.indexID[id] = entity
				fp := PGPFingerprint(subkey.PublicKey.Fingerprint)
				k.indexFingerprint[fp] = entity
				s++
			}
		}
	}
	k.G().Log.Debug("| Indexed %d primary and %d subkeys", p, s)
	k.G().Log.Debug("- Index on %s -> %s", k.filename, "OK")
	return nil
}

func (k *KeyringFile) Load() error {
	k.G().Log.Debug(fmt.Sprintf("+ Loading PGP Keyring %s", k.filename))
	file, err := os.Open(k.filename)
	if os.IsNotExist(err) {
		G.Log.Warning(fmt.Sprintf("No PGP Keyring found at %s", k.filename))
		err = nil
	} else if err != nil {
		G.Log.Errorf("Cannot open keyring %s: %s\n", k.filename, err)
		return err
	}
	if file != nil {
		defer file.Close()
		k.Entities, err = openpgp.ReadKeyRing(file)
		if err != nil {
			G.Log.Errorf("Cannot parse keyring %s: %s\n", k.filename, err)
			return err
		}
	}
	k.G().Log.Debug(fmt.Sprintf("- Successfully loaded PGP Keyring"))
	return nil
}

func (k KeyringFile) WriteTo(w io.Writer) (int64, error) {
	for _, e := range k.Entities {
		if err := e.Serialize(w); err != nil {
			return 0, err
		}
	}
	return 0, nil
}

func (k KeyringFile) GetFilename() string { return k.filename }

func (k KeyringFile) Save(g *GlobalContext) error {
	return SafeWriteToFile(g.Log, k, 0)
}

type SecretKeyType int

const (
	// The current device signing key.
	DeviceSigningKeyType SecretKeyType = iota
	// The current device encryption key.
	DeviceEncryptionKeyType
	// A PGP key (including the synced PGP key, if there is one).
	PGPKeyType
)

func (t SecretKeyType) String() string {
	switch t {
	case DeviceSigningKeyType:
		return "DeviceSigningKeyType"
	case DeviceEncryptionKeyType:
		return "DeviceEncryptionKeyType"
	case PGPKeyType:
		return "PGPKeyType"
	default:
		return "<Unknown secret key type>"
	}
}

func (t SecretKeyType) nonDeviceKeyMatches(key GenericKey) bool {
	if IsPGP(key) && (t == PGPKeyType) {
		return true
	}

	return false
}

type SecretKeyArg struct {
	// Whose keys to use. Must be non-nil.
	Me *User

	// The allowed key types.
	KeyType SecretKeyType

	// For non-device keys, a string that the key has to match. If
	// empty, any valid key is allowed.
	KeyQuery   string
	ExactMatch bool // if set, full equality required
}

// GetSecretKeyLocked gets a secret key for the current user by first
// looking for keys synced from the server, and if that fails, tries
// those in the local Keyring that are also active for the user.
// In any case, the key will be locked.
func (k *Keyrings) GetSecretKeyLocked(lctx LoginContext, ska SecretKeyArg) (ret *SKB, err error) {
	k.G().Log.Debug("+ GetSecretKeyLocked()")
	defer func() {
		k.G().Log.Debug("- GetSecretKeyLocked() -> %s", ErrToOk(err))
	}()

	k.G().Log.Debug("| LoadMe w/ Secrets on")

	if ska.Me == nil {
		if ska.Me, err = LoadMe(NewLoadUserArg(k.G())); err != nil {
			return
		}
	}

	if lctx != nil {
		ret, err = lctx.LockedLocalSecretKey(ska)
		if err != nil {
			return ret, err
		}
	} else {
		aerr := k.G().LoginState().Account(func(a *Account) {
			ret, err = a.LockedLocalSecretKey(ska)
		}, "LockedLocalSecretKey")
		if err != nil {
			return ret, err
		}
		if aerr != nil {
			return nil, aerr
		}
	}

	if ret != nil {
		k.G().Log.Debug("| Getting local secret key")
		return ret, nil
	}

	var pub GenericKey

	if ska.KeyType != PGPKeyType {
		k.G().Log.Debug("| Skipped Synced PGP key (via options)")
		err = NoSecretKeyError{}
		return nil, err
	}

	if ret, err = ska.Me.SyncedSecretKey(lctx); err != nil {
		k.G().Log.Warning("Error fetching synced PGP secret key: %s", err)
		return nil, err
	}
	if ret == nil {
		err = NoSecretKeyError{}
		return nil, err
	}

	if pub, err = ret.GetPubKey(); err != nil {
		return nil, err
	}

	if !KeyMatchesQuery(pub, ska.KeyQuery, ska.ExactMatch) {
		k.G().Log.Debug("| Can't use Synced PGP key; doesn't match query %s", ska.KeyQuery)
		err = NoSecretKeyError{}
		return nil, err

	}

	return ret, nil
}

func (k *Keyrings) cachedSecretKey(lctx LoginContext, ska SecretKeyArg) GenericKey {
	key, err := k.G().ActiveDevice.KeyByType(ska.KeyType)

	if key != nil && err == nil {
		k.G().Log.Debug("found cached secret key for ska: %+v", ska)
	} else if err != nil {
		if _, notFound := err.(NotFoundError); !notFound {
			k.G().Log.Debug("error getting cached secret key: %s", err)
		}
	}

	return key
}

func (k *Keyrings) setCachedSecretKey(lctx LoginContext, ska SecretKeyArg, key GenericKey) {
	k.G().Log.Debug("caching secret key for ska: %+v", ska)
	var setErr error
	if lctx != nil {
		setErr = lctx.SetCachedSecretKey(ska, key, nil)
	} else {
		aerr := k.G().LoginState().Account(func(a *Account) {
			setErr = a.SetCachedSecretKey(ska, key, nil)
		}, "GetSecretKeyWithPrompt - SetCachedSecretKey")
		if aerr != nil {
			k.G().Log.Debug("Account error: %s", aerr)
		}
	}
	if setErr != nil {
		k.G().Log.Debug("SetCachedSecretKey error: %s", setErr)
	}
}

type SecretKeyPromptArg struct {
	LoginContext   LoginContext
	Ska            SecretKeyArg
	SecretUI       SecretUI
	Reason         string
	UseCancelCache bool /* if true, when user cancels prompt, don't prompt again for 5m */
}

// TODO: Figure out whether and how to dep-inject the SecretStore.
func (k *Keyrings) GetSecretKeyWithPrompt(arg SecretKeyPromptArg) (key GenericKey, err error) {
	k.G().Log.Debug("+ GetSecretKeyWithPrompt(%s)", arg.Reason)
	defer func() {
		k.G().Log.Debug("- GetSecretKeyWithPrompt() -> %s", ErrToOk(err))
	}()

	key = k.cachedSecretKey(arg.LoginContext, arg.Ska)
	if key != nil {
		return key, err
	}

	key, _, err = k.GetSecretKeyAndSKBWithPrompt(arg)

	if key != nil && err == nil {
		k.setCachedSecretKey(arg.LoginContext, arg.Ska, key)
	}

	return key, err
}

func (k *Keyrings) GetSecretKeyWithoutPrompt(lctx LoginContext, ska SecretKeyArg) (key GenericKey, err error) {
	k.G().Log.Debug("+ GetSecretKeyWithoutPrompt()")
	defer func() {
		k.G().Log.Debug("- GetSecretKeyWithoutPrompt() -> %s", ErrToOk(err))
	}()

	key = k.cachedSecretKey(lctx, ska)
	if key != nil {
		k.G().Log.Debug("  found cached secret key")
		return key, err
	}

	k.G().Log.Debug("  no cached secret key, trying via secretStore")

	// not cached, so try to unlock without prompting
	if ska.Me == nil {
		err = NoUsernameError{}
		return nil, err
	}
	secretStore := NewSecretStore(k.G(), ska.Me.GetNormalizedName())

	skb, err := k.GetSecretKeyLocked(lctx, ska)
	if err != nil {
		return nil, err
	}

	key, err = skb.UnlockNoPrompt(lctx, secretStore)
	if key != nil && err == nil {
		k.setCachedSecretKey(lctx, ska, key)
	}

	return key, err
}

func (k *Keyrings) GetSecretKeyAndSKBWithPrompt(arg SecretKeyPromptArg) (key GenericKey, skb *SKB, err error) {
	k.G().Log.Debug("+ GetSecretKeyAndSKBWithPrompt(%s)", arg.Reason)
	defer func() {
		k.G().Log.Debug("- GetSecretKeyAndSKBWithPrompt() -> %s", ErrToOk(err))
	}()
	if skb, err = k.GetSecretKeyLocked(arg.LoginContext, arg.Ska); err != nil {
		skb = nil
		return
	}
	var secretStore SecretStore
	if arg.Ska.Me != nil {
		skb.SetUID(arg.Ska.Me.GetUID())
		secretStore = NewSecretStore(k.G(), arg.Ska.Me.GetNormalizedName())
	}
	if key, err = skb.PromptAndUnlock(arg, secretStore, arg.Ska.Me); err != nil {
		key = nil
		skb = nil
		return
	}
	return
}

func (k *Keyrings) GetSecretKeyWithStoredSecret(lctx LoginContext, ska SecretKeyArg, me *User, secretRetriever SecretRetriever) (key GenericKey, err error) {
	k.G().Log.Debug("+ GetSecretKeyWithStoredSecret()")
	defer func() {
		k.G().Log.Debug("- GetSecretKeyWithStoredSecret() -> %s", ErrToOk(err))
	}()
	var skb *SKB
	skb, err = k.GetSecretKeyLocked(lctx, ska)
	if err != nil {
		return
	}
	skb.SetUID(me.GetUID())
	return skb.UnlockWithStoredSecret(lctx, secretRetriever)
}

func (k *Keyrings) GetSecretKeyWithPassphrase(lctx LoginContext, me *User, passphrase string, secretStorer SecretStorer) (key GenericKey, err error) {
	k.G().Log.Debug("+ GetSecretKeyWithPassphrase()")
	defer func() {
		k.G().Log.Debug("- GetSecretKeyWithPassphrase() -> %s", ErrToOk(err))
	}()
	ska := SecretKeyArg{
		Me:      me,
		KeyType: DeviceSigningKeyType,
	}
	var skb *SKB
	skb, err = k.GetSecretKeyLocked(lctx, ska)
	if err != nil {
		return
	}
	skb.SetUID(me.GetUID())
	var tsec Triplesec
	var pps *PassphraseStream
	if lctx != nil {
		tsec = lctx.PassphraseStreamCache().Triplesec()
		pps = lctx.PassphraseStreamCache().PassphraseStream()
	} else {
		k.G().LoginState().PassphraseStreamCache(func(sc *PassphraseStreamCache) {
			tsec = sc.Triplesec()
			pps = sc.PassphraseStream()
		}, "StreamCache - tsec, pps")
	}
	return skb.UnlockSecretKey(lctx, passphrase, tsec, pps, secretStorer)
}

type EmptyKeyRing struct{}

func (k EmptyKeyRing) KeysById(id uint64, fp []byte) []openpgp.Key {
	return []openpgp.Key{}
}
func (k EmptyKeyRing) KeysByIdUsage(id uint64, fp []byte, usage byte) []openpgp.Key {
	return []openpgp.Key{}
}
func (k EmptyKeyRing) DecryptionKeys() []openpgp.Key {
	return []openpgp.Key{}
}
<commit_msg>kill G in libkb/keyring (#8735)<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package libkb

import (
	"fmt"
	"io"
	"os"
	"strings"
	"time"

	"github.com/keybase/go-crypto/openpgp"
)

type KeyringFile struct {
	filename         string
	Entities         openpgp.EntityList
	isPublic         bool
	indexID          map[string](*openpgp.Entity) // Map of 64-bit uppercase-hex KeyIds
	indexFingerprint map[PGPFingerprint](*openpgp.Entity)
	Contextified
}

type Keyrings struct {
	Contextified
}

func NewKeyrings(g *GlobalContext) *Keyrings {
	ret := &Keyrings{
		Contextified: Contextified{g: g},
	}
	return ret
}

//===================================================================

func (g *GlobalContext) SKBFilenameForUser(un NormalizedUsername) string {
	tmp := g.Env.GetSecretKeyringTemplate()
	token := "%u"
	if strings.Index(tmp, token) < 0 {
		return tmp
	}

	return strings.Replace(tmp, token, un.String(), -1)
}

func LoadSKBKeyring(un NormalizedUsername, g *GlobalContext) (*SKBKeyringFile, error) {
	if un.IsNil() {
		return nil, NoUsernameError{}
	}

	skbfile := NewSKBKeyringFile(g, un)
	err := skbfile.LoadAndIndex()
	if err != nil && !os.IsNotExist(err) {
		return nil, err
	}
	return skbfile, nil
}

func StatSKBKeyringMTime(un NormalizedUsername, g *GlobalContext) (mtime time.Time, err error) {
	if un.IsNil() {
		return mtime, NoUsernameError{}
	}
	return NewSKBKeyringFile(g, un).MTime()
}

func (k *KeyringFile) LoadAndIndex() error {
	var err error
	k.G().Log.Debug("+ LoadAndIndex on %s", k.filename)
	if err = k.Load(); err == nil {
		err = k.Index()
	}
	k.G().Log.Debug("- LoadAndIndex on %s -> %s", k.filename, ErrToOk(err))
	return err
}

func (k *KeyringFile) Index() error {
	k.G().Log.Debug("+ Index on %s", k.filename)
	k.indexID = make(map[string](*openpgp.Entity))
	k.indexFingerprint = make(map[PGPFingerprint](*openpgp.Entity))
	p := 0
	s := 0
	for _, entity := range k.Entities {
		if entity.PrimaryKey != nil {
			id := entity.PrimaryKey.KeyIdString()
			k.indexID[id] = entity
			fp := PGPFingerprint(entity.PrimaryKey.Fingerprint)
			k.indexFingerprint[fp] = entity
			p++
		}
		for _, subkey := range entity.Subkeys {
			if subkey.PublicKey != nil {
				id := subkey.PublicKey.KeyIdString()
				k.indexID[id] = entity
				fp := PGPFingerprint(subkey.PublicKey.Fingerprint)
				k.indexFingerprint[fp] = entity
				s++
			}
		}
	}
	k.G().Log.Debug("| Indexed %d primary and %d subkeys", p, s)
	k.G().Log.Debug("- Index on %s -> %s", k.filename, "OK")
	return nil
}

func (k *KeyringFile) Load() error {
	k.G().Log.Debug(fmt.Sprintf("+ Loading PGP Keyring %s", k.filename))
	file, err := os.Open(k.filename)
	if os.IsNotExist(err) {
		k.G().Log.Warning(fmt.Sprintf("No PGP Keyring found at %s", k.filename))
		err = nil
	} else if err != nil {
		k.G().Log.Errorf("Cannot open keyring %s: %s\n", k.filename, err)
		return err
	}
	if file != nil {
		defer file.Close()
		k.Entities, err = openpgp.ReadKeyRing(file)
		if err != nil {
			k.G().Log.Errorf("Cannot parse keyring %s: %s\n", k.filename, err)
			return err
		}
	}
	k.G().Log.Debug(fmt.Sprintf("- Successfully loaded PGP Keyring"))
	return nil
}

func (k KeyringFile) WriteTo(w io.Writer) (int64, error) {
	for _, e := range k.Entities {
		if err := e.Serialize(w); err != nil {
			return 0, err
		}
	}
	return 0, nil
}

func (k KeyringFile) GetFilename() string { return k.filename }

func (k KeyringFile) Save(g *GlobalContext) error {
	return SafeWriteToFile(g.Log, k, 0)
}

type SecretKeyType int

const (
	// The current device signing key.
	DeviceSigningKeyType SecretKeyType = iota
	// The current device encryption key.
	DeviceEncryptionKeyType
	// A PGP key (including the synced PGP key, if there is one).
	PGPKeyType
)

func (t SecretKeyType) String() string {
	switch t {
	case DeviceSigningKeyType:
		return "DeviceSigningKeyType"
	case DeviceEncryptionKeyType:
		return "DeviceEncryptionKeyType"
	case PGPKeyType:
		return "PGPKeyType"
	default:
		return "<Unknown secret key type>"
	}
}

func (t SecretKeyType) nonDeviceKeyMatches(key GenericKey) bool {
	if IsPGP(key) && (t == PGPKeyType) {
		return true
	}

	return false
}

type SecretKeyArg struct {
	// Whose keys to use. Must be non-nil.
	Me *User

	// The allowed key types.
	KeyType SecretKeyType

	// For non-device keys, a string that the key has to match. If
	// empty, any valid key is allowed.
	KeyQuery   string
	ExactMatch bool // if set, full equality required
}

// GetSecretKeyLocked gets a secret key for the current user by first
// looking for keys synced from the server, and if that fails, tries
// those in the local Keyring that are also active for the user.
// In any case, the key will be locked.
func (k *Keyrings) GetSecretKeyLocked(lctx LoginContext, ska SecretKeyArg) (ret *SKB, err error) {
	k.G().Log.Debug("+ GetSecretKeyLocked()")
	defer func() {
		k.G().Log.Debug("- GetSecretKeyLocked() -> %s", ErrToOk(err))
	}()

	k.G().Log.Debug("| LoadMe w/ Secrets on")

	if ska.Me == nil {
		if ska.Me, err = LoadMe(NewLoadUserArg(k.G())); err != nil {
			return
		}
	}

	if lctx != nil {
		ret, err = lctx.LockedLocalSecretKey(ska)
		if err != nil {
			return ret, err
		}
	} else {
		aerr := k.G().LoginState().Account(func(a *Account) {
			ret, err = a.LockedLocalSecretKey(ska)
		}, "LockedLocalSecretKey")
		if err != nil {
			return ret, err
		}
		if aerr != nil {
			return nil, aerr
		}
	}

	if ret != nil {
		k.G().Log.Debug("| Getting local secret key")
		return ret, nil
	}

	var pub GenericKey

	if ska.KeyType != PGPKeyType {
		k.G().Log.Debug("| Skipped Synced PGP key (via options)")
		err = NoSecretKeyError{}
		return nil, err
	}

	if ret, err = ska.Me.SyncedSecretKey(lctx); err != nil {
		k.G().Log.Warning("Error fetching synced PGP secret key: %s", err)
		return nil, err
	}
	if ret == nil {
		err = NoSecretKeyError{}
		return nil, err
	}

	if pub, err = ret.GetPubKey(); err != nil {
		return nil, err
	}

	if !KeyMatchesQuery(pub, ska.KeyQuery, ska.ExactMatch) {
		k.G().Log.Debug("| Can't use Synced PGP key; doesn't match query %s", ska.KeyQuery)
		err = NoSecretKeyError{}
		return nil, err

	}

	return ret, nil
}

func (k *Keyrings) cachedSecretKey(lctx LoginContext, ska SecretKeyArg) GenericKey {
	key, err := k.G().ActiveDevice.KeyByType(ska.KeyType)

	if key != nil && err == nil {
		k.G().Log.Debug("found cached secret key for ska: %+v", ska)
	} else if err != nil {
		if _, notFound := err.(NotFoundError); !notFound {
			k.G().Log.Debug("error getting cached secret key: %s", err)
		}
	}

	return key
}

func (k *Keyrings) setCachedSecretKey(lctx LoginContext, ska SecretKeyArg, key GenericKey) {
	k.G().Log.Debug("caching secret key for ska: %+v", ska)
	var setErr error
	if lctx != nil {
		setErr = lctx.SetCachedSecretKey(ska, key, nil)
	} else {
		aerr := k.G().LoginState().Account(func(a *Account) {
			setErr = a.SetCachedSecretKey(ska, key, nil)
		}, "GetSecretKeyWithPrompt - SetCachedSecretKey")
		if aerr != nil {
			k.G().Log.Debug("Account error: %s", aerr)
		}
	}
	if setErr != nil {
		k.G().Log.Debug("SetCachedSecretKey error: %s", setErr)
	}
}

type SecretKeyPromptArg struct {
	LoginContext   LoginContext
	Ska            SecretKeyArg
	SecretUI       SecretUI
	Reason         string
	UseCancelCache bool /* if true, when user cancels prompt, don't prompt again for 5m */
}

// TODO: Figure out whether and how to dep-inject the SecretStore.
func (k *Keyrings) GetSecretKeyWithPrompt(arg SecretKeyPromptArg) (key GenericKey, err error) {
	k.G().Log.Debug("+ GetSecretKeyWithPrompt(%s)", arg.Reason)
	defer func() {
		k.G().Log.Debug("- GetSecretKeyWithPrompt() -> %s", ErrToOk(err))
	}()

	key = k.cachedSecretKey(arg.LoginContext, arg.Ska)
	if key != nil {
		return key, err
	}

	key, _, err = k.GetSecretKeyAndSKBWithPrompt(arg)

	if key != nil && err == nil {
		k.setCachedSecretKey(arg.LoginContext, arg.Ska, key)
	}

	return key, err
}

func (k *Keyrings) GetSecretKeyWithoutPrompt(lctx LoginContext, ska SecretKeyArg) (key GenericKey, err error) {
	k.G().Log.Debug("+ GetSecretKeyWithoutPrompt()")
	defer func() {
		k.G().Log.Debug("- GetSecretKeyWithoutPrompt() -> %s", ErrToOk(err))
	}()

	key = k.cachedSecretKey(lctx, ska)
	if key != nil {
		k.G().Log.Debug("  found cached secret key")
		return key, err
	}

	k.G().Log.Debug("  no cached secret key, trying via secretStore")

	// not cached, so try to unlock without prompting
	if ska.Me == nil {
		err = NoUsernameError{}
		return nil, err
	}
	secretStore := NewSecretStore(k.G(), ska.Me.GetNormalizedName())

	skb, err := k.GetSecretKeyLocked(lctx, ska)
	if err != nil {
		return nil, err
	}

	key, err = skb.UnlockNoPrompt(lctx, secretStore)
	if key != nil && err == nil {
		k.setCachedSecretKey(lctx, ska, key)
	}

	return key, err
}

func (k *Keyrings) GetSecretKeyAndSKBWithPrompt(arg SecretKeyPromptArg) (key GenericKey, skb *SKB, err error) {
	k.G().Log.Debug("+ GetSecretKeyAndSKBWithPrompt(%s)", arg.Reason)
	defer func() {
		k.G().Log.Debug("- GetSecretKeyAndSKBWithPrompt() -> %s", ErrToOk(err))
	}()
	if skb, err = k.GetSecretKeyLocked(arg.LoginContext, arg.Ska); err != nil {
		skb = nil
		return
	}
	var secretStore SecretStore
	if arg.Ska.Me != nil {
		skb.SetUID(arg.Ska.Me.GetUID())
		secretStore = NewSecretStore(k.G(), arg.Ska.Me.GetNormalizedName())
	}
	if key, err = skb.PromptAndUnlock(arg, secretStore, arg.Ska.Me); err != nil {
		key = nil
		skb = nil
		return
	}
	return
}

func (k *Keyrings) GetSecretKeyWithStoredSecret(lctx LoginContext, ska SecretKeyArg, me *User, secretRetriever SecretRetriever) (key GenericKey, err error) {
	k.G().Log.Debug("+ GetSecretKeyWithStoredSecret()")
	defer func() {
		k.G().Log.Debug("- GetSecretKeyWithStoredSecret() -> %s", ErrToOk(err))
	}()
	var skb *SKB
	skb, err = k.GetSecretKeyLocked(lctx, ska)
	if err != nil {
		return
	}
	skb.SetUID(me.GetUID())
	return skb.UnlockWithStoredSecret(lctx, secretRetriever)
}

func (k *Keyrings) GetSecretKeyWithPassphrase(lctx LoginContext, me *User, passphrase string, secretStorer SecretStorer) (key GenericKey, err error) {
	k.G().Log.Debug("+ GetSecretKeyWithPassphrase()")
	defer func() {
		k.G().Log.Debug("- GetSecretKeyWithPassphrase() -> %s", ErrToOk(err))
	}()
	ska := SecretKeyArg{
		Me:      me,
		KeyType: DeviceSigningKeyType,
	}
	var skb *SKB
	skb, err = k.GetSecretKeyLocked(lctx, ska)
	if err != nil {
		return
	}
	skb.SetUID(me.GetUID())
	var tsec Triplesec
	var pps *PassphraseStream
	if lctx != nil {
		tsec = lctx.PassphraseStreamCache().Triplesec()
		pps = lctx.PassphraseStreamCache().PassphraseStream()
	} else {
		k.G().LoginState().PassphraseStreamCache(func(sc *PassphraseStreamCache) {
			tsec = sc.Triplesec()
			pps = sc.PassphraseStream()
		}, "StreamCache - tsec, pps")
	}
	return skb.UnlockSecretKey(lctx, passphrase, tsec, pps, secretStorer)
}

type EmptyKeyRing struct{}

func (k EmptyKeyRing) KeysById(id uint64, fp []byte) []openpgp.Key {
	return []openpgp.Key{}
}
func (k EmptyKeyRing) KeysByIdUsage(id uint64, fp []byte, usage byte) []openpgp.Key {
	return []openpgp.Key{}
}
func (k EmptyKeyRing) DecryptionKeys() []openpgp.Key {
	return []openpgp.Key{}
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package libkb

// Version is the current version (should be MAJOR.MINOR.PATCH)
const Version = "1.0.43"
<commit_msg>vbump<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package libkb

// Version is the current version (should be MAJOR.MINOR.PATCH)
const Version = "1.0.44"
<|endoftext|>


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

package stats

import (
	"encoding/json"
	"fmt"
	"strings"
	"sync"
	"time"
)

// Timings is meant to tracks timing data
// by named categories as well as histograms.
type Timings struct {
	mu         sync.Mutex
	totalCount int64
	totalTime  int64
	histograms map[string]*Histogram
}

// NewTimings creates a new Timings object, and publishes it if name is set.
func NewTimings(name string) *Timings {
	t := &Timings{histograms: make(map[string]*Histogram)}
	if name != "" {
		Publish(name, t)
	}
	return t
}

// Add will add a new value to the named histogram.
func (t *Timings) Add(name string, elapsed time.Duration) {
	t.mu.Lock()
	defer t.mu.Unlock()

	hist, ok := t.histograms[name]
	if !ok {
		hist = NewGenericHistogram("", bucketCutoffs, bucketLabels, "Count", "Time")
		t.histograms[name] = hist
	}
	elapsedNs := int64(elapsed)
	hist.Add(elapsedNs)
	t.totalCount++
	t.totalTime += elapsedNs
}

// Record is a convenience function that records completion
// timing data based on the provided start time of an event.
func (t *Timings) Record(name string, startTime time.Time) {
	t.Add(name, time.Now().Sub(startTime))
}

// String is for expvar.
func (t *Timings) String() string {
	t.mu.Lock()
	defer t.mu.Unlock()

	tm := struct {
		TotalCount int64
		TotalTime  int64
		Histograms map[string]*Histogram
	}{
		t.totalCount,
		t.totalTime,
		t.histograms,
	}
	data, err := json.Marshal(tm)
	if err != nil {
		data, _ = json.Marshal(err.Error())
	}
	return string(data)
}

// Histograms returns a map pointing at the histograms.
func (t *Timings) Histograms() (h map[string]*Histogram) {
	t.mu.Lock()
	defer t.mu.Unlock()
	h = make(map[string]*Histogram, len(t.histograms))
	for k, v := range t.histograms {
		h[k] = v
	}
	return
}

// Count returns the total count for all values.
func (t *Timings) Count() int64 {
	t.mu.Lock()
	defer t.mu.Unlock()
	return t.totalCount
}

// Time returns the total time elapsed for all values.
func (t *Timings) Time() int64 {
	t.mu.Lock()
	defer t.mu.Unlock()
	return t.totalTime
}

// Counts returns the total count for each value.
func (t *Timings) Counts() map[string]int64 {
	t.mu.Lock()
	defer t.mu.Unlock()

	counts := make(map[string]int64, len(t.histograms)+1)
	for k, v := range t.histograms {
		counts[k] = v.Count()
	}
	counts["All"] = t.totalCount
	return counts
}

var bucketCutoffs = []int64{0.0005 * 1e9, 0.001 * 1e9, 0.005 * 1e9, 0.010 * 1e9, 0.050 * 1e9, 0.100 * 1e9, 0.500 * 1e9, 1.000 * 1e9, 5.000 * 1e9, 10.00 * 1e9}

var bucketLabels []string

func init() {
	bucketLabels = make([]string, len(bucketCutoffs)+1)
	for i, v := range bucketCutoffs {
		bucketLabels[i] = fmt.Sprintf("%d", v)
	}
	bucketLabels[len(bucketLabels)-1] = "inf"
}

// MultiTimings is meant to tracks timing data by categories as well
// as histograms. The names of the categories are compound names made
// with joining multiple strings with '.'.
type MultiTimings struct {
	Timings
	labels []string
}

// NewMultiTimings creates a new MultiTimings object.
func NewMultiTimings(name string, labels []string) *MultiTimings {
	t := &MultiTimings{
		Timings: Timings{histograms: make(map[string]*Histogram)},
		labels:  labels,
	}
	if name != "" {
		Publish(name, t)
	}
	return t
}

func (mt *MultiTimings) Labels() []string {
	return mt.labels
}

// Add will add a new value to the named histogram.
func (mt *MultiTimings) Add(names []string, elapsed time.Duration) {
	if len(names) != len(mt.labels) {
		panic("MultiTimings: wrong number of values in Add")
	}
	mt.Timings.Add(strings.Join(names, "."), elapsed)
}

// Record is a convenience function that records completion
// timing data based on the provided start time of an event.
func (mt *MultiTimings) Record(names []string, startTime time.Time) {
	if len(names) != len(mt.labels) {
		panic("MultiTimings: wrong number of values in Record")
	}
	mt.Timings.Record(strings.Join(names, "."), startTime)
}
<commit_msg>Reverting to state of main vitess repo.<commit_after>// Copyright 2012, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package stats

import (
	"encoding/json"
	"fmt"
	"strings"
	"sync"
	"time"
)

// Timings is meant to tracks timing data
// by named categories as well as histograms.
type Timings struct {
	mu         sync.Mutex
	totalCount int64
	totalTime  int64
	histograms map[string]*Histogram
}

// NewTimings creates a new Timings object, and publishes it if name is set.
func NewTimings(name string) *Timings {
	t := &Timings{histograms: make(map[string]*Histogram)}
	if name != "" {
		Publish(name, t)
	}
	return t
}

// Add will add a new value to the named histogram.
func (t *Timings) Add(name string, elapsed time.Duration) {
	t.mu.Lock()
	defer t.mu.Unlock()

	hist, ok := t.histograms[name]
	if !ok {
		hist = NewGenericHistogram("", bucketCutoffs, bucketLabels, "Count", "Time")
		t.histograms[name] = hist
	}
	elapsedNs := int64(elapsed)
	hist.Add(elapsedNs)
	t.totalCount++
	t.totalTime += elapsedNs
}

// Record is a convenience function that records completion
// timing data based on the provided start time of an event.
func (t *Timings) Record(name string, startTime time.Time) {
	t.Add(name, time.Now().Sub(startTime))
}

// String is for expvar.
func (t *Timings) String() string {
	t.mu.Lock()
	defer t.mu.Unlock()

	tm := struct {
		TotalCount int64
		TotalTime  int64
		Histograms map[string]*Histogram
	}{
		t.totalCount,
		t.totalTime,
		t.histograms,
	}
	data, err := json.Marshal(tm)
	if err != nil {
		data, _ = json.Marshal(err.Error())
	}
	return string(data)
}

// Histograms returns a map pointing at the histograms.
func (t *Timings) Histograms() (h map[string]*Histogram) {
	t.mu.Lock()
	defer t.mu.Unlock()
	h = make(map[string]*Histogram, len(t.histograms))
	for k, v := range t.histograms {
		h[k] = v
	}
	return
}

// Count returns the total count for all values.
func (t *Timings) Count() int64 {
	t.mu.Lock()
	defer t.mu.Unlock()
	return t.totalCount
}

// Time returns the total time elapsed for all values.
func (t *Timings) Time() int64 {
	t.mu.Lock()
	defer t.mu.Unlock()
	return t.totalTime
}

// Counts returns the total count for each value.
func (t *Timings) Counts() map[string]int64 {
	t.mu.Lock()
	defer t.mu.Unlock()

	counts := make(map[string]int64, len(t.histograms)+1)
	for k, v := range t.histograms {
		counts[k] = v.Count()
	}
	counts["All"] = t.totalCount
	return counts
}

var bucketCutoffs = []int64{0.0005 * 1e9, 0.001 * 1e9, 0.005 * 1e9, 0.010 * 1e9, 0.050 * 1e9, 0.100 * 1e9, 0.500 * 1e9, 1.000 * 1e9, 5.000 * 1e9, 10.00 * 1e9}

var bucketLabels []string

func init() {
	bucketLabels = make([]string, len(bucketCutoffs)+1)
	for i, v := range bucketCutoffs {
		bucketLabels[i] = fmt.Sprintf("%.4f", float64(v)/1e9)
	}
	bucketLabels[len(bucketLabels)-1] = "Max"
}

// MultiTimings is meant to tracks timing data by categories as well
// as histograms. The names of the categories are compound names made
// with joining multiple strings with '.'.
type MultiTimings struct {
	Timings
	labels []string
}

// NewMultiTimings creates a new MultiTimings object.
func NewMultiTimings(name string, labels []string) *MultiTimings {
	t := &MultiTimings{
		Timings: Timings{histograms: make(map[string]*Histogram)},
		labels:  labels,
	}
	if name != "" {
		Publish(name, t)
	}
	return t
}

func (mt *MultiTimings) Labels() []string {
	return mt.labels
}

// Add will add a new value to the named histogram.
func (mt *MultiTimings) Add(names []string, elapsed time.Duration) {
	if len(names) != len(mt.labels) {
		panic("MultiTimings: wrong number of values in Add")
	}
	mt.Timings.Add(strings.Join(names, "."), elapsed)
}

// Record is a convenience function that records completion
// timing data based on the provided start time of an event.
func (mt *MultiTimings) Record(names []string, startTime time.Time) {
	if len(names) != len(mt.labels) {
		panic("MultiTimings: wrong number of values in Record")
	}
	mt.Timings.Record(strings.Join(names, "."), startTime)
}
<|endoftext|>


<commit_before>package strain

type Ints []int
type Lists [][]int
type Strings []string

func (i Ints) Keep(filter func(int) bool) Ints {
	if i == nil {
		return nil
	}
	panic("Please implement the Keep function")
}

func (i Ints) Discard(filter func(int) bool) Ints {
	if i == nil {
		return nil
	}
	panic("Please implement the Discard function")
}

func (l Lists) Keep(filter func([]int) bool) Lists {
	if l == nil {
		return nil
	}
	panic("Please implement the Keep function")
}

func (s Strings) Keep(filter func(string) bool) Strings {
	if s == nil {
		return nil
	}
	panic("Please implement the Keep function")
}
<commit_msg>Implement Keep<commit_after>package strain

type Ints []int
type Lists [][]int
type Strings []string

func (i Ints) Keep(filter func(int) bool) Ints {
	if i == nil {
		return nil
	}
	result := []int{}
	for _, v := range i {
		if filter(v) {
			result = append(result, v)
		}
	}
	i = result
	return i
}

func (i Ints) Discard(filter func(int) bool) Ints {
	if i == nil {
		return nil
	}
	panic("Please implement the Discard function")
}

func (l Lists) Keep(filter func([]int) bool) Lists {
	if l == nil {
		return nil
	}
	panic("Please implement the Keep function")
}

func (s Strings) Keep(filter func(string) bool) Strings {
	if s == nil {
		return nil
	}
	panic("Please implement the Keep function")
}
<|endoftext|>


<commit_before>/*
Copyright 2017 Google Inc.

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

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

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

package vtctl

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/golang/protobuf/jsonpb"
	"io/ioutil"
	"path"

	"github.com/golang/protobuf/proto"
	"golang.org/x/net/context"

	"vitess.io/vitess/go/vt/topo"
	"vitess.io/vitess/go/vt/wrangler"

	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
	vschemapb "vitess.io/vitess/go/vt/proto/vschema"
)

// This file contains the topo command group for vtctl.

const topoGroupName = "Topo"

func init() {
	addCommandGroup(topoGroupName)

	addCommand(topoGroupName, command{
		"TopoCat",
		commandTopoCat,
		"[-cell <cell>] [-decode_proto] [-decode_proto_json] [-long] <path> [<path>...]",
		"Retrieves the file(s) at <path> from the topo service, and displays it. It can resolve wildcards, and decode the proto-encoded data."})

	addCommand(topoGroupName, command{
		"TopoCp",
		commandTopoCp,
		"[-cell <cell>] [-to_topo] <src> <dst>",
		"Copies a file from topo to local file structure, or the other way around"})
}

// DecodeContent uses the filename to imply a type, and proto-decodes
// the right object, then echoes it as a string.
func DecodeContent(filename string, data []byte, json bool) (string, error) {
	name := path.Base(filename)

	var p proto.Message
	switch name {
	case topo.CellInfoFile:
		p = new(topodatapb.CellInfo)
	case topo.KeyspaceFile:
		p = new(topodatapb.Keyspace)
	case topo.ShardFile:
		p = new(topodatapb.Shard)
	case topo.VSchemaFile:
		p = new(vschemapb.Keyspace)
	case topo.ShardReplicationFile:
		p = new(topodatapb.ShardReplication)
	case topo.TabletFile:
		p = new(topodatapb.Tablet)
	case topo.SrvVSchemaFile:
		p = new(vschemapb.SrvVSchema)
	case topo.SrvKeyspaceFile:
		p = new(topodatapb.SrvKeyspace)
	default:
		if json {
			return "", fmt.Errorf("unknown topo protobuf type for %v", name)
		} else {
			return string(data), nil
		}
	}

	if err := proto.Unmarshal(data, p); err != nil {
		return string(data), err
	}

	if json {
		return new(jsonpb.Marshaler).MarshalToString(p)
	} else {
		return proto.MarshalTextString(p), nil
	}
}

func commandTopoCat(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error {
	cell := subFlags.String("cell", topo.GlobalCell, "topology cell to cat the file from. Defaults to global cell.")
	long := subFlags.Bool("long", false, "long listing.")
	decodeProtoJson := subFlags.Bool("decode_proto_json", false, "decode proto files and display them as json")
	decodeProto := subFlags.Bool("decode_proto", false, "decode proto files and display them as text")
	subFlags.Parse(args)
	if subFlags.NArg() == 0 {
		return fmt.Errorf("TopoCat: no path specified")
	}
	resolved, err := wr.TopoServer().ResolveWildcards(ctx, *cell, subFlags.Args())
	if err != nil {
		return fmt.Errorf("TopoCat: invalid wildcards: %v", err)
	}
	if len(resolved) == 0 {
		// The wildcards didn't result in anything, we're done.
		return nil
	}

	conn, err := wr.TopoServer().ConnForCell(ctx, *cell)
	if err != nil {
		return err
	}

	var topologyDecoder TopologyDecoder
	if *decodeProtoJson {
		topologyDecoder = JsonTopologyDecoder{}
	} else if *decodeProto {
		topologyDecoder = ProtoTopologyDecoder{}
	} else {
		topologyDecoder = PlainTopologyDecoder{}
	}

	return topologyDecoder.decode(resolved, conn, ctx, wr, *long)
}

func commandTopoCp(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error {
	cell := subFlags.String("cell", topo.GlobalCell, "topology cell to use for the copy. Defaults to global cell.")
	toTopo := subFlags.Bool("to_topo", false, "copies from local server to topo instead (reverse direction).")
	subFlags.Parse(args)
	if subFlags.NArg() != 2 {
		return fmt.Errorf("TopoCp: need source and destination")
	}
	from := subFlags.Arg(0)
	to := subFlags.Arg(1)
	if *toTopo {
		return copyFileToTopo(ctx, wr.TopoServer(), *cell, from, to)
	}
	return copyFileFromTopo(ctx, wr.TopoServer(), *cell, from, to)
}

func copyFileFromTopo(ctx context.Context, ts *topo.Server, cell, from, to string) error {
	conn, err := ts.ConnForCell(ctx, cell)
	if err != nil {
		return err
	}
	data, _, err := conn.Get(ctx, from)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(to, data, 0644)
}

func copyFileToTopo(ctx context.Context, ts *topo.Server, cell, from, to string) error {
	conn, err := ts.ConnForCell(ctx, cell)
	if err != nil {
		return err
	}
	data, err := ioutil.ReadFile(from)
	if err != nil {
		return err
	}
	_, err = conn.Update(ctx, to, data, nil)
	return err
}

type TopologyDecoder interface {
	decode([]string, topo.Conn, context.Context, *wrangler.Wrangler, bool) error
}

type ProtoTopologyDecoder struct{}
type PlainTopologyDecoder struct{}
type JsonTopologyDecoder struct{}

func (d ProtoTopologyDecoder) decode(topoPaths []string, conn topo.Conn, ctx context.Context, wr *wrangler.Wrangler, long bool) error {
	hasError := false
	for _, topoPath := range topoPaths {
		data, version, err := conn.Get(ctx, topoPath)
		if err != nil {
			hasError = true
			wr.Logger().Printf("TopoCat: Get(%v) failed: %v\n", topoPath, err)
			continue
		}

		if long {
			wr.Logger().Printf("path=%v version=%v\n", topoPath, version)
		}

		decoded, err := DecodeContent(topoPath, data, false)
		if err != nil {
			wr.Logger().Warningf("TopoCat: cannot proto decode %v: %v", topoPath, err)
			decoded = string(data)
		}

		wr.Logger().Printf(decoded)
		if len(decoded) > 0 && decoded[len(decoded)-1] != '\n' && long {
			wr.Logger().Printf("\n")
		}
	}

	if hasError {
		return fmt.Errorf("TopoCat: some paths had errors")
	}
	return nil
}

func (d PlainTopologyDecoder) decode(topoPaths []string, conn topo.Conn, ctx context.Context, wr *wrangler.Wrangler, long bool) error {
	hasError := false
	for _, topoPath := range topoPaths {
		data, version, err := conn.Get(ctx, topoPath)
		if err != nil {
			hasError = true
			wr.Logger().Printf("TopoCat: Get(%v) failed: %v\n", topoPath, err)
			continue
		}

		if long {
			wr.Logger().Printf("path=%v version=%v\n", topoPath, version)
		}
		decoded := string(data)
		wr.Logger().Printf(decoded)
		if len(decoded) > 0 && decoded[len(decoded)-1] != '\n' && long {
			wr.Logger().Printf("\n")
		}
	}

	if hasError {
		return fmt.Errorf("TopoCat: some paths had errors")
	}
	return nil
}

func (d JsonTopologyDecoder) decode(topoPaths []string, conn topo.Conn, ctx context.Context, wr *wrangler.Wrangler, long bool) error {
	hasError := false
	var jsonData []interface{}
	for _, topoPath := range topoPaths {
		data, version, err := conn.Get(ctx, topoPath)
		if err != nil {
			hasError = true
			wr.Logger().Printf("TopoCat: Get(%v) failed: %v\n", topoPath, err)
			continue
		}

		decoded, err := DecodeContent(topoPath, data, true)
		if err != nil {
			hasError = true
			wr.Logger().Printf("TopoCat: cannot proto decode %v: %v", topoPath, err)
			continue
		}

		var jsonDatum map[string]interface{}
		if err = json.Unmarshal([]byte(decoded), &jsonDatum); err != nil {
			hasError = true
			wr.Logger().Printf("TopoCat: cannot json Unmarshal %v: %v", topoPath, err)
			continue
		}

		if long {
			jsonDatum["__path"] = topoPath
			jsonDatum["__version"] = version.String()
		}
		jsonData = append(jsonData, jsonDatum)
	}

	jsonBytes, err := json.Marshal(jsonData)
	if err != nil {
		hasError = true
		wr.Logger().Printf("TopoCat: cannot json Marshal: %v", err)
	} else {
		wr.Logger().Printf(string(jsonBytes) + "\n")
	}

	if hasError {
		return fmt.Errorf("TopoCat: some paths had errors")
	}
	return nil
}
<commit_msg>Add support for json output in vtctl TopoCat command, updates from review Signed-off-by: dleibovic <dleibovic@etsy.com><commit_after>/*
Copyright 2017 Google Inc.

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

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

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

package vtctl

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/golang/protobuf/jsonpb"
	"io/ioutil"
	"path"

	"github.com/golang/protobuf/proto"
	"golang.org/x/net/context"

	"vitess.io/vitess/go/vt/topo"
	"vitess.io/vitess/go/vt/wrangler"

	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
	vschemapb "vitess.io/vitess/go/vt/proto/vschema"
)

// This file contains the topo command group for vtctl.

const topoGroupName = "Topo"

func init() {
	addCommandGroup(topoGroupName)

	addCommand(topoGroupName, command{
		"TopoCat",
		commandTopoCat,
		"[-cell <cell>] [-decode_proto] [-decode_proto_json] [-long] <path> [<path>...]",
		"Retrieves the file(s) at <path> from the topo service, and displays it. It can resolve wildcards, and decode the proto-encoded data."})

	addCommand(topoGroupName, command{
		"TopoCp",
		commandTopoCp,
		"[-cell <cell>] [-to_topo] <src> <dst>",
		"Copies a file from topo to local file structure, or the other way around"})
}

// DecodeContent uses the filename to imply a type, and proto-decodes
// the right object, then echoes it as a string.
func DecodeContent(filename string, data []byte, json bool) (string, error) {
	name := path.Base(filename)

	var p proto.Message
	switch name {
	case topo.CellInfoFile:
		p = new(topodatapb.CellInfo)
	case topo.KeyspaceFile:
		p = new(topodatapb.Keyspace)
	case topo.ShardFile:
		p = new(topodatapb.Shard)
	case topo.VSchemaFile:
		p = new(vschemapb.Keyspace)
	case topo.ShardReplicationFile:
		p = new(topodatapb.ShardReplication)
	case topo.TabletFile:
		p = new(topodatapb.Tablet)
	case topo.SrvVSchemaFile:
		p = new(vschemapb.SrvVSchema)
	case topo.SrvKeyspaceFile:
		p = new(topodatapb.SrvKeyspace)
	default:
		if json {
			return "", fmt.Errorf("unknown topo protobuf type for %v", name)
		} else {
			return string(data), nil
		}
	}

	if err := proto.Unmarshal(data, p); err != nil {
		return string(data), err
	}

	if json {
		return new(jsonpb.Marshaler).MarshalToString(p)
	} else {
		return proto.MarshalTextString(p), nil
	}
}

func commandTopoCat(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error {
	cell := subFlags.String("cell", topo.GlobalCell, "topology cell to cat the file from. Defaults to global cell.")
	long := subFlags.Bool("long", false, "long listing.")
	decodeProtoJson := subFlags.Bool("decode_proto_json", false, "decode proto files and display them as json")
	decodeProto := subFlags.Bool("decode_proto", false, "decode proto files and display them as text")
	subFlags.Parse(args)
	if subFlags.NArg() == 0 {
		return fmt.Errorf("TopoCat: no path specified")
	}
	resolved, err := wr.TopoServer().ResolveWildcards(ctx, *cell, subFlags.Args())
	if err != nil {
		return fmt.Errorf("TopoCat: invalid wildcards: %v", err)
	}
	if len(resolved) == 0 {
		// The wildcards didn't result in anything, we're done.
		return nil
	}

	conn, err := wr.TopoServer().ConnForCell(ctx, *cell)
	if err != nil {
		return err
	}

	var topologyDecoder TopologyDecoder
	switch {
	case *decodeProtoJson:
		topologyDecoder = JsonTopologyDecoder{}
	case *decodeProto:
		topologyDecoder = ProtoTopologyDecoder{}
	default:
		topologyDecoder = PlainTopologyDecoder{}
	}

	return topologyDecoder.decode(resolved, conn, ctx, wr, *long)
}

func commandTopoCp(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error {
	cell := subFlags.String("cell", topo.GlobalCell, "topology cell to use for the copy. Defaults to global cell.")
	toTopo := subFlags.Bool("to_topo", false, "copies from local server to topo instead (reverse direction).")
	subFlags.Parse(args)
	if subFlags.NArg() != 2 {
		return fmt.Errorf("TopoCp: need source and destination")
	}
	from := subFlags.Arg(0)
	to := subFlags.Arg(1)
	if *toTopo {
		return copyFileToTopo(ctx, wr.TopoServer(), *cell, from, to)
	}
	return copyFileFromTopo(ctx, wr.TopoServer(), *cell, from, to)
}

func copyFileFromTopo(ctx context.Context, ts *topo.Server, cell, from, to string) error {
	conn, err := ts.ConnForCell(ctx, cell)
	if err != nil {
		return err
	}
	data, _, err := conn.Get(ctx, from)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(to, data, 0644)
}

func copyFileToTopo(ctx context.Context, ts *topo.Server, cell, from, to string) error {
	conn, err := ts.ConnForCell(ctx, cell)
	if err != nil {
		return err
	}
	data, err := ioutil.ReadFile(from)
	if err != nil {
		return err
	}
	_, err = conn.Update(ctx, to, data, nil)
	return err
}

type TopologyDecoder interface {
	decode([]string, topo.Conn, context.Context, *wrangler.Wrangler, bool) error
}

type ProtoTopologyDecoder struct{}
type PlainTopologyDecoder struct{}
type JsonTopologyDecoder struct{}

func (d ProtoTopologyDecoder) decode(topoPaths []string, conn topo.Conn, ctx context.Context, wr *wrangler.Wrangler, long bool) error {
	hasError := false
	for _, topoPath := range topoPaths {
		data, version, err := conn.Get(ctx, topoPath)
		if err != nil {
			hasError = true
			wr.Logger().Printf("TopoCat: Get(%v) failed: %v\n", topoPath, err)
			continue
		}

		if long {
			wr.Logger().Printf("path=%v version=%v\n", topoPath, version)
		}

		decoded, err := DecodeContent(topoPath, data, false)
		if err != nil {
			wr.Logger().Warningf("TopoCat: cannot proto decode %v: %v", topoPath, err)
			decoded = string(data)
		}

		wr.Logger().Printf(decoded)
		if len(decoded) > 0 && decoded[len(decoded)-1] != '\n' && long {
			wr.Logger().Printf("\n")
		}
	}

	if hasError {
		return fmt.Errorf("TopoCat: some paths had errors")
	}
	return nil
}

func (d PlainTopologyDecoder) decode(topoPaths []string, conn topo.Conn, ctx context.Context, wr *wrangler.Wrangler, long bool) error {
	hasError := false
	for _, topoPath := range topoPaths {
		data, version, err := conn.Get(ctx, topoPath)
		if err != nil {
			hasError = true
			wr.Logger().Printf("TopoCat: Get(%v) failed: %v\n", topoPath, err)
			continue
		}

		if long {
			wr.Logger().Printf("path=%v version=%v\n", topoPath, version)
		}
		decoded := string(data)
		wr.Logger().Printf(decoded)
		if len(decoded) > 0 && decoded[len(decoded)-1] != '\n' && long {
			wr.Logger().Printf("\n")
		}
	}

	if hasError {
		return fmt.Errorf("TopoCat: some paths had errors")
	}
	return nil
}

func (d JsonTopologyDecoder) decode(topoPaths []string, conn topo.Conn, ctx context.Context, wr *wrangler.Wrangler, long bool) error {
	hasError := false
	var jsonData []interface{}
	for _, topoPath := range topoPaths {
		data, version, err := conn.Get(ctx, topoPath)
		if err != nil {
			hasError = true
			wr.Logger().Printf("TopoCat: Get(%v) failed: %v\n", topoPath, err)
			continue
		}

		decoded, err := DecodeContent(topoPath, data, true)
		if err != nil {
			hasError = true
			wr.Logger().Printf("TopoCat: cannot proto decode %v: %v", topoPath, err)
			continue
		}

		var jsonDatum map[string]interface{}
		if err = json.Unmarshal([]byte(decoded), &jsonDatum); err != nil {
			hasError = true
			wr.Logger().Printf("TopoCat: cannot json Unmarshal %v: %v", topoPath, err)
			continue
		}

		if long {
			jsonDatum["__path"] = topoPath
			jsonDatum["__version"] = version.String()
		}
		jsonData = append(jsonData, jsonDatum)
	}

	jsonBytes, err := json.Marshal(jsonData)
	if err != nil {
		hasError = true
		wr.Logger().Printf("TopoCat: cannot json Marshal: %v", err)
	} else {
		wr.Logger().Printf(string(jsonBytes) + "\n")
	}

	if hasError {
		return fmt.Errorf("TopoCat: some paths had errors")
	}
	return nil
}
<|endoftext|>


<commit_before>package health

import (
	"testing"
	"time"

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

var _ = Describe("Ping", func() {
	var subject *Ping

	BeforeEach(func() {
		subject = NewPing(func() error {
			return nil
		}, time.Hour, 2, 3)
	})

	AfterEach(func() {
		subject.Stop()
	})

	It("should update health status", func() {
		Expect(subject.IsHealthy()).To(BeFalse())
		subject.update(true)
		Expect(subject.IsHealthy()).To(BeFalse())
		subject.update(true)
		Expect(subject.IsHealthy()).To(BeTrue())
		subject.update(true)
		Expect(subject.IsHealthy()).To(BeTrue())
		subject.update(false)
		Expect(subject.IsHealthy()).To(BeTrue())
		subject.update(false)
		Expect(subject.IsHealthy()).To(BeTrue())
		subject.update(false)
		Expect(subject.IsHealthy()).To(BeFalse())
		subject.update(false)
		Expect(subject.IsHealthy()).To(BeFalse())
		subject.update(true)
		Expect(subject.IsHealthy()).To(BeFalse())
		subject.update(true)
		Expect(subject.IsHealthy()).To(BeTrue())
	})

	It("should check periodically", func() {
		ping := NewPing(func() error {
			return nil
		}, time.Millisecond, 2, 3)
		defer ping.Stop()

		Expect(ping.IsHealthy()).To(BeFalse())
		Eventually(ping.IsHealthy, "5ms", "1ms").Should(BeTrue())
	})

})

func BenchmarkPing_IsHealthy(b *testing.B) {
	ping := NewPing(func() error { return nil }, time.Hour, 2, 3)
	defer ping.Stop()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		ping.IsHealthy()
	}
}

func BenchmarkPing_update(b *testing.B) {
	ping := NewPing(func() error { return nil }, time.Hour, 2, 3)
	defer ping.Stop()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		ping.update(true)
	}
}
<commit_msg>Fix slow CI<commit_after>package health

import (
	"testing"
	"time"

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

var _ = Describe("Ping", func() {
	var subject *Ping

	BeforeEach(func() {
		subject = NewPing(func() error {
			return nil
		}, time.Hour, 2, 3)
	})

	AfterEach(func() {
		subject.Stop()
	})

	It("should update health status", func() {
		Expect(subject.IsHealthy()).To(BeFalse())
		subject.update(true)
		Expect(subject.IsHealthy()).To(BeFalse())
		subject.update(true)
		Expect(subject.IsHealthy()).To(BeTrue())
		subject.update(true)
		Expect(subject.IsHealthy()).To(BeTrue())
		subject.update(false)
		Expect(subject.IsHealthy()).To(BeTrue())
		subject.update(false)
		Expect(subject.IsHealthy()).To(BeTrue())
		subject.update(false)
		Expect(subject.IsHealthy()).To(BeFalse())
		subject.update(false)
		Expect(subject.IsHealthy()).To(BeFalse())
		subject.update(true)
		Expect(subject.IsHealthy()).To(BeFalse())
		subject.update(true)
		Expect(subject.IsHealthy()).To(BeTrue())
	})

	It("should check periodically", func() {
		ping := NewPing(func() error {
			return nil
		}, time.Millisecond, 2, 3)
		defer ping.Stop()

		Expect(ping.IsHealthy()).To(BeFalse())
		Eventually(ping.IsHealthy, "10ms", "2ms").Should(BeTrue())
	})

})

func BenchmarkPing_IsHealthy(b *testing.B) {
	ping := NewPing(func() error { return nil }, time.Hour, 2, 3)
	defer ping.Stop()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		ping.IsHealthy()
	}
}

func BenchmarkPing_update(b *testing.B) {
	ping := NewPing(func() error { return nil }, time.Hour, 2, 3)
	defer ping.Stop()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		ping.update(true)
	}
}
<|endoftext|>


<commit_before>package htmldoc

import (
	"fmt"
	"github.com/wjdp/htmltest/output"
	"golang.org/x/net/html"
	"os"
	"path"
	"sync"
)

// Document struct, representation of a document within the tested site
type Document struct {
	FilePath           string                // Relative to the shell session
	SitePath           string                // Relative to the site root
	BasePath           string                // Base for relative links
	htmlMutex          *sync.Mutex           // Controls access to htmlNode
	htmlNode           *html.Node            // Parsed output
	hashMap            map[string]*html.Node // Map of valid id/names of nodes
	NodesOfInterest    []*html.Node          // Slice of nodes to run checks on
	State              DocumentState         // Link to a DocumentState struct
	DoctypeNode        *html.Node            // Pointer to doctype node if exists
	ignoreTagAttribute string                // Attribute to ignore element and children if found on element
}

// DocumentState struct, used by checks that depend on the document being
// parsed.
type DocumentState struct {
	FaviconPresent bool // Have we found a favicon in the document?
}

// Init : Initialise the Document struct doesn't mesh nice with the NewXYZ()
// convention but many optional parameters for Document and no parameter
// overloading in Go
func (doc *Document) Init() {
	// Setup the document,
	doc.htmlMutex = &sync.Mutex{}
	doc.NodesOfInterest = make([]*html.Node, 0)
	doc.hashMap = make(map[string]*html.Node)
}

// Parse : Ask Document to parse its HTML file. Returns quickly if this has
// already been done. Thread safe. Either called when the document is tested
// or when another document needs data from this one.
func (doc *Document) Parse() {
	// Only one routine may parse the doc
	doc.htmlMutex.Lock()
	defer doc.htmlMutex.Unlock()

	// If document has already been parsed, return early.
	if doc.htmlNode != nil {
		return
	}

	// Open, parse, and close document
	f, err := os.Open(doc.FilePath)
	output.CheckErrorPanic(err)
	defer f.Close()

	htmlNode, err := html.Parse(f)
	output.CheckErrorGeneric(err)

	doc.htmlNode = htmlNode
	doc.parseNode(htmlNode)
}

// Internal recursive function that delves into the node tree and captures
// nodes of interest and node id/names.
func (doc *Document) parseNode(n *html.Node) {
	// Ignore this tree if data-proofer-ignore set
	if doc.ignoreTagAttribute != "" && AttrPresent(n.Attr, doc.ignoreTagAttribute) {
		return
	}

	switch n.Type {
	case html.DoctypeNode:
		doc.DoctypeNode = n
	case html.ElementNode:
		// If present save fragment identifier to the hashMap
		nodeID := GetID(n.Attr)
		if nodeID != "" {
			doc.hashMap[nodeID] = n
		}
		// Identify and store tags of interest
		switch n.Data {
		case "a", "area", "audio", "blockquote", "del", "embed", "iframe", "img",
			"input", "ins", "link", "meta", "object", "q", "script", "source",
			"track", "video":
			// Nodes of interest
			doc.NodesOfInterest = append(doc.NodesOfInterest, n)
		case "base":
			// Set BasePath from <base> tag
			doc.BasePath = path.Join(doc.BasePath, GetAttr(n.Attr, "href"))
		case "pre", "code":
			return // Everything within these elements is not to be interpreted
		}
	case html.ErrorNode:
		fmt.Printf("%+v\n", n)
		fmt.Println("Oops, in parsing your HTML we fell over.\n",
			"Please let the developer know about this.\n",
			"https://github.com/wjdp/htmltest/issues/new")
	}

	// Iterate over children
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		doc.parseNode(c)
	}
}

// IsHashValid : Is a hash/fragment present in this Document.
func (doc *Document) IsHashValid(hash string) bool {
	doc.Parse() // Ensure doc has been parsed
	_, ok := doc.hashMap[hash]
	return ok
}
<commit_msg>Stop ignoring content of pre/code tags<commit_after>package htmldoc

import (
	"fmt"
	"github.com/wjdp/htmltest/output"
	"golang.org/x/net/html"
	"os"
	"path"
	"sync"
)

// Document struct, representation of a document within the tested site
type Document struct {
	FilePath           string                // Relative to the shell session
	SitePath           string                // Relative to the site root
	BasePath           string                // Base for relative links
	htmlMutex          *sync.Mutex           // Controls access to htmlNode
	htmlNode           *html.Node            // Parsed output
	hashMap            map[string]*html.Node // Map of valid id/names of nodes
	NodesOfInterest    []*html.Node          // Slice of nodes to run checks on
	State              DocumentState         // Link to a DocumentState struct
	DoctypeNode        *html.Node            // Pointer to doctype node if exists
	ignoreTagAttribute string                // Attribute to ignore element and children if found on element
}

// DocumentState struct, used by checks that depend on the document being
// parsed.
type DocumentState struct {
	FaviconPresent bool // Have we found a favicon in the document?
}

// Init : Initialise the Document struct doesn't mesh nice with the NewXYZ()
// convention but many optional parameters for Document and no parameter
// overloading in Go
func (doc *Document) Init() {
	// Setup the document,
	doc.htmlMutex = &sync.Mutex{}
	doc.NodesOfInterest = make([]*html.Node, 0)
	doc.hashMap = make(map[string]*html.Node)
}

// Parse : Ask Document to parse its HTML file. Returns quickly if this has
// already been done. Thread safe. Either called when the document is tested
// or when another document needs data from this one.
func (doc *Document) Parse() {
	// Only one routine may parse the doc
	doc.htmlMutex.Lock()
	defer doc.htmlMutex.Unlock()

	// If document has already been parsed, return early.
	if doc.htmlNode != nil {
		return
	}

	// Open, parse, and close document
	f, err := os.Open(doc.FilePath)
	output.CheckErrorPanic(err)
	defer f.Close()

	htmlNode, err := html.Parse(f)
	output.CheckErrorGeneric(err)

	doc.htmlNode = htmlNode
	doc.parseNode(htmlNode)
}

// Internal recursive function that delves into the node tree and captures
// nodes of interest and node id/names.
func (doc *Document) parseNode(n *html.Node) {
	// Ignore this tree if data-proofer-ignore set
	if doc.ignoreTagAttribute != "" && AttrPresent(n.Attr, doc.ignoreTagAttribute) {
		return
	}

	switch n.Type {
	case html.DoctypeNode:
		doc.DoctypeNode = n
	case html.ElementNode:
		// If present save fragment identifier to the hashMap
		nodeID := GetID(n.Attr)
		if nodeID != "" {
			doc.hashMap[nodeID] = n
		}
		// Identify and store tags of interest
		switch n.Data {
		case "a", "area", "audio", "blockquote", "del", "embed", "iframe", "img",
			"input", "ins", "link", "meta", "object", "q", "script", "source",
			"track", "video":
			// Nodes of interest
			doc.NodesOfInterest = append(doc.NodesOfInterest, n)
		case "base":
			// Set BasePath from <base> tag
			doc.BasePath = path.Join(doc.BasePath, GetAttr(n.Attr, "href"))
		}
	case html.ErrorNode:
		fmt.Printf("%+v\n", n)
		fmt.Println("Oops, in parsing your HTML we fell over.\n",
			"Please let the developer know about this.\n",
			"https://github.com/wjdp/htmltest/issues/new")
	}

	// Iterate over children
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		doc.parseNode(c)
	}
}

// IsHashValid : Is a hash/fragment present in this Document.
func (doc *Document) IsHashValid(hash string) bool {
	doc.Parse() // Ensure doc has been parsed
	_, ok := doc.hashMap[hash]
	return ok
}
<|endoftext|>


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

// Package build gathers information about Go packages.
//
// Go Path
//
// The Go path is a list of directory trees containing Go source code.
// It is consulted to resolve imports that cannot be found in the standard
// Go tree.  The default path is the value of the GOPATH environment
// variable, interpreted as a path list appropriate to the operating system
// (on Unix, the variable is a colon-separated string;
// on Windows, a semicolon-separated string;
// on Plan 9, a list).
//
// Each directory listed in the Go path must have a prescribed structure:
//
// The src/ directory holds source code.  The path below 'src' determines
// the import path or executable name.
//
// The pkg/ directory holds installed package objects.
// As in the Go tree, each target operating system and
// architecture pair has its own subdirectory of pkg
// (pkg/GOOS_GOARCH).
//
// If DIR is a directory listed in the Go path, a package with
// source in DIR/src/foo/bar can be imported as "foo/bar" and
// has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a"
// (or, for gccgo, "DIR/pkg/gccgo/foo/libbar.a").
//
// The bin/ directory holds compiled commands.
// Each command is named for its source directory, but only
// using the final element, not the entire path.  That is, the
// command with source in DIR/src/foo/quux is installed into
// DIR/bin/quux, not DIR/bin/foo/quux.  The foo/ is stripped
// so that you can add DIR/bin to your PATH to get at the
// installed commands.
//
// Here's an example directory layout:
//
//	GOPATH=/home/user/gocode
//
//	/home/user/gocode/
//	    src/
//	        foo/
//	            bar/               (go code in package bar)
//	                x.go
//	            quux/              (go code in package main)
//	                y.go
//	    bin/
//	        quux                   (installed command)
//	    pkg/
//	        linux_amd64/
//	            foo/
//	                bar.a          (installed package object)
//
// Build Constraints
//
// A build constraint is a line comment beginning with the directive +build
// that lists the conditions under which a file should be included in the package.
// Constraints may appear in any kind of source file (not just Go), but
// they must appear near the top of the file, preceded
// only by blank lines and other line comments.
//
// A build constraint is evaluated as the OR of space-separated options;
// each option evaluates as the AND of its comma-separated terms;
// and each term is an alphanumeric word or, preceded by !, its negation.
// That is, the build constraint:
//
//	// +build linux,386 darwin,!cgo
//
// corresponds to the boolean formula:
//
//	(linux AND 386) OR (darwin AND (NOT cgo))
//
// A file may have multiple build constraints. The overall constraint is the AND
// of the individual constraints. That is, the build constraints:
//
//	// +build linux darwin
//	// +build 386
//
// corresponds to the boolean formula:
//
//	(linux OR darwin) AND 386
//
// During a particular build, the following words are satisfied:
//
//	- the target operating system, as spelled by runtime.GOOS
//	- the target architecture, as spelled by runtime.GOARCH
//	- the compiler being used, currently either "gc" or "gccgo"
//	- "cgo", if ctxt.CgoEnabled is true
//	- any additional words listed in ctxt.BuildTags
//
// If a file's name, after stripping the extension and a possible _test suffix,
// matches *_GOOS, *_GOARCH, or *_GOOS_GOARCH for any known operating
// system and architecture values, then the file is considered to have an implicit
// build constraint requiring those terms.
//
// To keep a file from being considered for the build:
//
//	// +build ignore
//
// (any other unsatisfied word will work as well, but ``ignore'' is conventional.)
//
// To build a file only when using cgo, and only on Linux and OS X:
//
//	// +build linux,cgo darwin,cgo
//
// Such a file is usually paired with another file implementing the
// default functionality for other systems, which in this case would
// carry the constraint:
//
//	// +build !linux,!darwin !cgo
//
// Naming a file dns_windows.go will cause it to be included only when
// building the package for Windows; similarly, math_386.s will be included
// only when building the package for 32-bit x86.
//
package build
<commit_msg>go/build: document blank line required after build constraints<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package build gathers information about Go packages.
//
// Go Path
//
// The Go path is a list of directory trees containing Go source code.
// It is consulted to resolve imports that cannot be found in the standard
// Go tree.  The default path is the value of the GOPATH environment
// variable, interpreted as a path list appropriate to the operating system
// (on Unix, the variable is a colon-separated string;
// on Windows, a semicolon-separated string;
// on Plan 9, a list).
//
// Each directory listed in the Go path must have a prescribed structure:
//
// The src/ directory holds source code.  The path below 'src' determines
// the import path or executable name.
//
// The pkg/ directory holds installed package objects.
// As in the Go tree, each target operating system and
// architecture pair has its own subdirectory of pkg
// (pkg/GOOS_GOARCH).
//
// If DIR is a directory listed in the Go path, a package with
// source in DIR/src/foo/bar can be imported as "foo/bar" and
// has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a"
// (or, for gccgo, "DIR/pkg/gccgo/foo/libbar.a").
//
// The bin/ directory holds compiled commands.
// Each command is named for its source directory, but only
// using the final element, not the entire path.  That is, the
// command with source in DIR/src/foo/quux is installed into
// DIR/bin/quux, not DIR/bin/foo/quux.  The foo/ is stripped
// so that you can add DIR/bin to your PATH to get at the
// installed commands.
//
// Here's an example directory layout:
//
//	GOPATH=/home/user/gocode
//
//	/home/user/gocode/
//	    src/
//	        foo/
//	            bar/               (go code in package bar)
//	                x.go
//	            quux/              (go code in package main)
//	                y.go
//	    bin/
//	        quux                   (installed command)
//	    pkg/
//	        linux_amd64/
//	            foo/
//	                bar.a          (installed package object)
//
// Build Constraints
//
// A build constraint is a line comment beginning with the directive +build
// that lists the conditions under which a file should be included in the package.
// Constraints may appear in any kind of source file (not just Go), but
// they must appear near the top of the file, preceded
// only by blank lines and other line comments.
//
// To distinguish build constraints from package documentation, a series of
// build constraints must be followed by a blank line.
//
// A build constraint is evaluated as the OR of space-separated options;
// each option evaluates as the AND of its comma-separated terms;
// and each term is an alphanumeric word or, preceded by !, its negation.
// That is, the build constraint:
//
//	// +build linux,386 darwin,!cgo
//
// corresponds to the boolean formula:
//
//	(linux AND 386) OR (darwin AND (NOT cgo))
//
// A file may have multiple build constraints. The overall constraint is the AND
// of the individual constraints. That is, the build constraints:
//
//	// +build linux darwin
//	// +build 386
//
// corresponds to the boolean formula:
//
//	(linux OR darwin) AND 386
//
// During a particular build, the following words are satisfied:
//
//	- the target operating system, as spelled by runtime.GOOS
//	- the target architecture, as spelled by runtime.GOARCH
//	- the compiler being used, currently either "gc" or "gccgo"
//	- "cgo", if ctxt.CgoEnabled is true
//	- any additional words listed in ctxt.BuildTags
//
// If a file's name, after stripping the extension and a possible _test suffix,
// matches *_GOOS, *_GOARCH, or *_GOOS_GOARCH for any known operating
// system and architecture values, then the file is considered to have an implicit
// build constraint requiring those terms.
//
// To keep a file from being considered for the build:
//
//	// +build ignore
//
// (any other unsatisfied word will work as well, but ``ignore'' is conventional.)
//
// To build a file only when using cgo, and only on Linux and OS X:
//
//	// +build linux,cgo darwin,cgo
//
// Such a file is usually paired with another file implementing the
// default functionality for other systems, which in this case would
// carry the constraint:
//
//	// +build !linux,!darwin !cgo
//
// Naming a file dns_windows.go will cause it to be included only when
// building the package for Windows; similarly, math_386.s will be included
// only when building the package for 32-bit x86.
//
package build
<|endoftext|>


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

// Plan 9 environment variables.

package os

import "syscall"

// ENOENV is the Error indicating that an environment variable does not exist.
var ENOENV = NewError("no such environment variable")

// Getenverror retrieves the value of the environment variable named by the key.
// It returns the value and an error, if any.
func Getenverror(key string) (value string, err Error) {
	if len(key) == 0 {
		return "", EINVAL
	}
	f, e := Open("/env/" + key)
	if iserror(e) {
		return "", ENOENV
	}
	defer f.Close()

	var buf [4096]byte
	n, e := f.Read(buf[:len(buf)-1])
	if iserror(e) {
		return "", ENOENV
	}
	buf[n] = 0
	return string(buf[0:n]), nil
}

// Getenv retrieves the value of the environment variable named by the key.
// It returns the value, which will be empty if the variable is not present.
func Getenv(key string) string {
	v, _ := Getenverror(key)
	return v
}

// Setenv sets the value of the environment variable named by the key.
// It returns an Error, if any.
func Setenv(key, value string) Error {
	if len(key) == 0 {
		return EINVAL
	}

	f, e := Create("/env/" + key)
	if iserror(e) {
		return e
	}
	defer f.Close()

	_, e = f.Write(syscall.StringByteSlice(value))
	return nil
}

// Clearenv deletes all environment variables.
func Clearenv() {
	syscall.RawSyscall(syscall.SYS_RFORK, syscall.RFCENVG, 0, 0)
}

// Environ returns an array of strings representing the environment,
// in the form "key=value".
func Environ() []string {
	env := make([]string, 0, 100)

	f, e := Open("/env")
	if iserror(e) {
		panic(e)
	}
	defer f.Close()

	names, e := f.Readdirnames(-1)
	if iserror(e) {
		panic(e)
	}

	for _, k := range names {
		if v, e := Getenverror(k); !iserror(e) {
			env = append(env, k+"="+v)
		}
	}
	return env[0:len(env)]
}

// TempDir returns the default directory to use for temporary files.
func TempDir() string {
	return "/tmp"
}
<commit_msg>os: fix Getenv for Plan 9. Truncate the rightmost char if it is '\0'.<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Plan 9 environment variables.

package os

import "syscall"

// ENOENV is the Error indicating that an environment variable does not exist.
var ENOENV = NewError("no such environment variable")

// Getenverror retrieves the value of the environment variable named by the key.
// It returns the value and an error, if any.
func Getenverror(key string) (value string, err Error) {
	if len(key) == 0 {
		return "", EINVAL
	}
	f, e := Open("/env/" + key)
	if iserror(e) {
		return "", ENOENV
	}
	defer f.Close()

	l, _ := f.Seek(0, 2)
	f.Seek(0, 0)
	buf := make([]byte, l)
	n, e := f.Read(buf)
	if iserror(e) {
		return "", ENOENV
	}

	if n > 0 && buf[n-1] == 0 {
		buf = buf[:n-1]
	}
	return string(buf), nil
}

// Getenv retrieves the value of the environment variable named by the key.
// It returns the value, which will be empty if the variable is not present.
func Getenv(key string) string {
	v, _ := Getenverror(key)
	return v
}

// Setenv sets the value of the environment variable named by the key.
// It returns an Error, if any.
func Setenv(key, value string) Error {
	if len(key) == 0 {
		return EINVAL
	}

	f, e := Create("/env/" + key)
	if iserror(e) {
		return e
	}
	defer f.Close()

	_, e = f.Write([]byte(value))
	return nil
}

// Clearenv deletes all environment variables.
func Clearenv() {
	syscall.RawSyscall(syscall.SYS_RFORK, syscall.RFCENVG, 0, 0)
}

// Environ returns an array of strings representing the environment,
// in the form "key=value".
func Environ() []string {
	env := make([]string, 0, 100)

	f, e := Open("/env")
	if iserror(e) {
		panic(e)
	}
	defer f.Close()

	names, e := f.Readdirnames(-1)
	if iserror(e) {
		panic(e)
	}

	for _, k := range names {
		if v, e := Getenverror(k); !iserror(e) {
			env = append(env, k+"="+v)
		}
	}
	return env[0:len(env)]
}

// TempDir returns the default directory to use for temporary files.
func TempDir() string {
	return "/tmp"
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 etcdv3

import (
	"sync"
	"testing"
	"time"

	"github.com/coreos/etcd/etcdserver/api/v3client"
	"github.com/ligato/cn-infra/datasync"
	"github.com/ligato/cn-infra/db/keyval"
	"github.com/ligato/cn-infra/db/keyval/etcdv3/mocks"
	"github.com/ligato/cn-infra/logging/logrus"
	. "github.com/onsi/gomega"
)

const (
	prefix   = "/my/prefix/"
	key      = "key"
	watchKey = "vals/"
)

var (
	broker          *BytesConnectionEtcd
	prefixedBroker  keyval.BytesBroker
	prefixedWatcher keyval.BytesWatcher
	embd            mocks.Embedded
)

func TestDataBroker(t *testing.T) {
	//setup
	embd.Start(t)
	defer embd.Stop()
	RegisterTestingT(t)

	t.Run("putGetValue", testPutGetValuePrefixed)
	embd.CleanDs()
	t.Run("simpleWatcher", testPrefixedWatcher)
	embd.CleanDs()
	t.Run("listValues", testPrefixedListValues)
	embd.CleanDs()
	t.Run("txn", testPrefixedTxn)
	embd.CleanDs()
	t.Run("testDelWithPrefix", testDelWithPrefix)
	embd.CleanDs()
	t.Run("testPutIfNotExist", testPutIfNotExists)
	embd.CleanDs()
	t.Run("compact", testCompact)
}

func teardownBrokers() {
	broker.Close()
	broker = nil
	prefixedBroker = nil
	prefixedWatcher = nil
}

func testPutGetValuePrefixed(t *testing.T) {
	setupBrokers(t)
	defer teardownBrokers()

	data := []byte{1, 2, 3}

	// Insert key-value pair using databroker.
	err := broker.Put(prefix+key, data)
	Expect(err).To(BeNil())

	returnedData, found, _, err := prefixedBroker.GetValue(key)
	Expect(returnedData).NotTo(BeNil())
	Expect(found).To(BeTrue())
	Expect(err).To(BeNil())

	// not existing value
	returnedData, found, _, err = prefixedBroker.GetValue("unknown")
	Expect(returnedData).To(BeNil())
	Expect(found).To(BeFalse())
	Expect(err).To(BeNil())

}

func testPrefixedWatcher(t *testing.T) {
	setupBrokers(t)
	defer teardownBrokers()

	watchCh := make(chan keyval.BytesWatchResp)
	err := prefixedWatcher.Watch(keyval.ToChan(watchCh), nil, watchKey)
	Expect(err).To(BeNil())

	wg := sync.WaitGroup{}
	wg.Add(1)
	go expectWatchEvent(t, &wg, watchCh, watchKey+"val1")

	// Insert kv that doesn't match the watcher subscription.
	broker.Put(prefix+"/something/else/val1", []byte{0, 0, 7})

	// Insert kv for watcher.
	broker.Put(prefix+watchKey+"val1", []byte{0, 0, 7})

	wg.Wait()
}

func testPrefixedTxn(t *testing.T) {
	setupBrokers(t)
	defer teardownBrokers()

	tx := prefixedBroker.NewTxn()
	Expect(tx).NotTo(BeNil())

	tx.Put("b/val1", []byte{0, 1})
	tx.Put("b/val2", []byte{0, 1})
	tx.Put("b/val3", []byte{0, 1})
	tx.Commit()

	kvi, err := broker.ListValues(prefix + "b")
	Expect(err).To(BeNil())
	Expect(kvi).NotTo(BeNil())

	expectedKeys := []string{prefix + "b/val1", prefix + "b/val2", prefix + "b/val3"}
	for i := 0; i < 3; i++ {
		kv, all := kvi.GetNext()
		Expect(kv).NotTo(BeNil())
		Expect(all).To(BeFalse())
		Expect(kv.GetKey()).To(BeEquivalentTo(expectedKeys[i]))
	}
}

func testPrefixedListValues(t *testing.T) {
	setupBrokers(t)
	defer teardownBrokers()

	var err error
	// Insert values using databroker.
	err = broker.Put(prefix+"a/val1", []byte{0, 0, 7})
	Expect(err).To(BeNil())
	err = broker.Put(prefix+"a/val2", []byte{0, 0, 7})
	Expect(err).To(BeNil())
	err = broker.Put(prefix+"a/val3", []byte{0, 0, 7})
	Expect(err).To(BeNil())

	// List values using pluginDatabroker.
	kvi, err := prefixedBroker.ListValues("a")
	Expect(err).To(BeNil())
	Expect(kvi).NotTo(BeNil())

	expectedKeys := []string{"a/val1", "a/val2", "a/val3"}
	for i := 0; i < 3; i++ {
		kv, all := kvi.GetNext()
		Expect(kv).NotTo(BeNil())
		Expect(all).To(BeFalse())
		// verify that prefix of BytesBrokerWatcherEtcd is trimmed
		Expect(kv.GetKey()).To(BeEquivalentTo(expectedKeys[i]))
	}
}

func testDelWithPrefix(t *testing.T) {
	setupBrokers(t)
	defer teardownBrokers()

	err := broker.Put("something/a/val1", []byte{0, 0, 7})
	Expect(err).To(BeNil())
	err = broker.Put("something/a/val2", []byte{0, 0, 7})
	Expect(err).To(BeNil())
	err = broker.Put("something/a/val3", []byte{0, 0, 7})
	Expect(err).To(BeNil())

	_, found, _, err := broker.GetValue("something/a/val1")
	Expect(found).To(BeTrue())
	Expect(err).To(BeNil())

	_, found, _, err = broker.GetValue("something/a/val2")
	Expect(found).To(BeTrue())
	Expect(err).To(BeNil())

	_, found, _, err = broker.GetValue("something/a/val3")
	Expect(found).To(BeTrue())
	Expect(err).To(BeNil())

	_, err = broker.Delete("something/a", datasync.WithPrefix())
	Expect(err).To(BeNil())

	_, found, _, err = broker.GetValue("something/a/val1")
	Expect(found).To(BeFalse())
	Expect(err).To(BeNil())

	_, found, _, err = broker.GetValue("something/a/val2")
	Expect(found).To(BeFalse())
	Expect(err).To(BeNil())

	_, found, _, err = broker.GetValue("something/a/val3")
	Expect(found).To(BeFalse())
	Expect(err).To(BeNil())

}

func testPutIfNotExists(t *testing.T) {
	RegisterTestingT(t)

	conn, err := NewEtcdConnectionUsingClient(v3client.New(embd.ETCD.Server), logrus.DefaultLogger())

	Expect(err).To(BeNil())
	Expect(conn).NotTo(BeNil())

	const key = "myKey"
	var (
		intialValue  = []byte("abcd")
		changedValue = []byte("modified")
	)

	_, found, _, err := conn.GetValue(key)
	Expect(err).To(BeNil())
	Expect(found).To(BeFalse())

	inserted, err := conn.PutIfNotExists(key, intialValue)
	Expect(err).To(BeNil())
	Expect(inserted).To(BeTrue())

	data, found, _, err := conn.GetValue(key)
	Expect(err).To(BeNil())
	Expect(found).To(BeTrue())
	Expect(string(data)).To(BeEquivalentTo(string(intialValue)))

	inserted, err = conn.PutIfNotExists(key, changedValue)
	Expect(err).To(BeNil())
	Expect(inserted).To(BeFalse())

	data, found, _, err = conn.GetValue(key)
	Expect(err).To(BeNil())
	Expect(found).To(BeTrue())
	Expect(string(data)).To(BeEquivalentTo(string(intialValue)))

	_, err = conn.Delete(key)
	Expect(err).To(BeNil())

	inserted, err = conn.PutIfNotExists(key, changedValue)
	Expect(err).To(BeNil())
	Expect(inserted).To(BeTrue())

	data, found, _, err = conn.GetValue(key)
	Expect(err).To(BeNil())
	Expect(found).To(BeTrue())
	Expect(string(data)).To(BeEquivalentTo(string(changedValue)))

}

func testCompact(t *testing.T) {
	setupBrokers(t)
	defer teardownBrokers()

	mykey := "mykey"
	data := []byte{1, 2, 3}
	data2 := []byte{4, 5, 6}

	//broker.etcdClient.Maintenance.Status(context.TODO())
	revision, err := broker.GetRevision()
	Expect(err).To(BeNil())
	t.Log("current revision:", revision)

	// insert some data
	err = broker.Put(prefix+mykey, data)
	Expect(err).To(BeNil())

	// retrieve the data
	retData, found, modRev, err := prefixedBroker.GetValue(mykey)
	Expect(retData).NotTo(BeNil())
	Expect(found).To(BeTrue())
	Expect(err).To(BeNil())
	//Expect(rev).To(Equal(1))
	t.Log("data:", retData, "modrev:", modRev)

	// store its mod revision
	firsRev := modRev

	// overwrite the data with new data
	err = broker.Put(prefix+mykey, data2)
	Expect(err).To(BeNil())

	// retrieve the new data
	retData, found, modRev, err = prefixedBroker.GetValue(mykey)
	Expect(retData).NotTo(BeNil())
	Expect(found).To(BeTrue())
	Expect(err).To(BeNil())
	//Expect(rev).To(Equal(1))
	t.Log("data:", retData, "modrev:", modRev)

	// retrieve the previous revision
	retData, found, modRev, err = broker.GetValueRev(prefix+mykey, firsRev)
	Expect(retData).NotTo(BeNil())
	Expect(found).To(BeTrue())
	Expect(err).To(BeNil())
	//Expect(rev).To(Equal(1))
	t.Log("data:", retData, "modrev:", modRev)

	// get current revision
	revision, err = broker.GetRevision()
	Expect(err).To(BeNil())
	t.Log("current revision:", revision)

	// compact to current revision
	err = broker.Compact(revision)
	Expect(err).To(BeNil())
	t.Log("compacted to revision:", revision)

	// try retrieving previous revision
	retData, found, modRev, err = broker.GetValueRev(prefix+mykey, firsRev)
	Expect(retData).To(BeNil())
	Expect(found).NotTo(BeTrue())
	Expect(err).NotTo(BeNil())
}

func expectWatchEvent(t *testing.T, wg *sync.WaitGroup, watchCh chan keyval.BytesWatchResp, expectedKey string) {
	select {
	case resp := <-watchCh:
		Expect(resp).NotTo(BeNil())
		Expect(resp.GetKey()).To(BeEquivalentTo(expectedKey))
	case <-time.After(1 * time.Second):
		t.Error("Watch resp not received")
		t.FailNow()
	}
	wg.Done()
}

func setupBrokers(t *testing.T) {
	RegisterTestingT(t)

	var err error
	broker, err = NewEtcdConnectionUsingClient(v3client.New(embd.ETCD.Server), logrus.DefaultLogger())

	Expect(err).To(BeNil())
	Expect(broker).NotTo(BeNil())
	// Create BytesBrokerWatcherEtcd with prefix.
	prefixedBroker = broker.NewBroker(prefix)
	prefixedWatcher = broker.NewWatcher(prefix)
	Expect(prefixedBroker).NotTo(BeNil())
	Expect(prefixedWatcher).NotTo(BeNil())

}
<commit_msg>Add comment to compact test<commit_after>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 etcdv3

import (
	"sync"
	"testing"
	"time"

	"github.com/coreos/etcd/etcdserver/api/v3client"
	"github.com/ligato/cn-infra/datasync"
	"github.com/ligato/cn-infra/db/keyval"
	"github.com/ligato/cn-infra/db/keyval/etcdv3/mocks"
	"github.com/ligato/cn-infra/logging/logrus"
	. "github.com/onsi/gomega"
)

const (
	prefix   = "/my/prefix/"
	key      = "key"
	watchKey = "vals/"
)

var (
	broker          *BytesConnectionEtcd
	prefixedBroker  keyval.BytesBroker
	prefixedWatcher keyval.BytesWatcher
	embd            mocks.Embedded
)

func TestDataBroker(t *testing.T) {
	//setup
	embd.Start(t)
	defer embd.Stop()
	RegisterTestingT(t)

	t.Run("putGetValue", testPutGetValuePrefixed)
	embd.CleanDs()
	t.Run("simpleWatcher", testPrefixedWatcher)
	embd.CleanDs()
	t.Run("listValues", testPrefixedListValues)
	embd.CleanDs()
	t.Run("txn", testPrefixedTxn)
	embd.CleanDs()
	t.Run("testDelWithPrefix", testDelWithPrefix)
	embd.CleanDs()
	t.Run("testPutIfNotExist", testPutIfNotExists)
	embd.CleanDs()
	t.Run("compact", testCompact)
}

func teardownBrokers() {
	broker.Close()
	broker = nil
	prefixedBroker = nil
	prefixedWatcher = nil
}

func testPutGetValuePrefixed(t *testing.T) {
	setupBrokers(t)
	defer teardownBrokers()

	data := []byte{1, 2, 3}

	// Insert key-value pair using databroker.
	err := broker.Put(prefix+key, data)
	Expect(err).To(BeNil())

	returnedData, found, _, err := prefixedBroker.GetValue(key)
	Expect(returnedData).NotTo(BeNil())
	Expect(found).To(BeTrue())
	Expect(err).To(BeNil())

	// not existing value
	returnedData, found, _, err = prefixedBroker.GetValue("unknown")
	Expect(returnedData).To(BeNil())
	Expect(found).To(BeFalse())
	Expect(err).To(BeNil())

}

func testPrefixedWatcher(t *testing.T) {
	setupBrokers(t)
	defer teardownBrokers()

	watchCh := make(chan keyval.BytesWatchResp)
	err := prefixedWatcher.Watch(keyval.ToChan(watchCh), nil, watchKey)
	Expect(err).To(BeNil())

	wg := sync.WaitGroup{}
	wg.Add(1)
	go expectWatchEvent(t, &wg, watchCh, watchKey+"val1")

	// Insert kv that doesn't match the watcher subscription.
	broker.Put(prefix+"/something/else/val1", []byte{0, 0, 7})

	// Insert kv for watcher.
	broker.Put(prefix+watchKey+"val1", []byte{0, 0, 7})

	wg.Wait()
}

func testPrefixedTxn(t *testing.T) {
	setupBrokers(t)
	defer teardownBrokers()

	tx := prefixedBroker.NewTxn()
	Expect(tx).NotTo(BeNil())

	tx.Put("b/val1", []byte{0, 1})
	tx.Put("b/val2", []byte{0, 1})
	tx.Put("b/val3", []byte{0, 1})
	tx.Commit()

	kvi, err := broker.ListValues(prefix + "b")
	Expect(err).To(BeNil())
	Expect(kvi).NotTo(BeNil())

	expectedKeys := []string{prefix + "b/val1", prefix + "b/val2", prefix + "b/val3"}
	for i := 0; i < 3; i++ {
		kv, all := kvi.GetNext()
		Expect(kv).NotTo(BeNil())
		Expect(all).To(BeFalse())
		Expect(kv.GetKey()).To(BeEquivalentTo(expectedKeys[i]))
	}
}

func testPrefixedListValues(t *testing.T) {
	setupBrokers(t)
	defer teardownBrokers()

	var err error
	// Insert values using databroker.
	err = broker.Put(prefix+"a/val1", []byte{0, 0, 7})
	Expect(err).To(BeNil())
	err = broker.Put(prefix+"a/val2", []byte{0, 0, 7})
	Expect(err).To(BeNil())
	err = broker.Put(prefix+"a/val3", []byte{0, 0, 7})
	Expect(err).To(BeNil())

	// List values using pluginDatabroker.
	kvi, err := prefixedBroker.ListValues("a")
	Expect(err).To(BeNil())
	Expect(kvi).NotTo(BeNil())

	expectedKeys := []string{"a/val1", "a/val2", "a/val3"}
	for i := 0; i < 3; i++ {
		kv, all := kvi.GetNext()
		Expect(kv).NotTo(BeNil())
		Expect(all).To(BeFalse())
		// verify that prefix of BytesBrokerWatcherEtcd is trimmed
		Expect(kv.GetKey()).To(BeEquivalentTo(expectedKeys[i]))
	}
}

func testDelWithPrefix(t *testing.T) {
	setupBrokers(t)
	defer teardownBrokers()

	err := broker.Put("something/a/val1", []byte{0, 0, 7})
	Expect(err).To(BeNil())
	err = broker.Put("something/a/val2", []byte{0, 0, 7})
	Expect(err).To(BeNil())
	err = broker.Put("something/a/val3", []byte{0, 0, 7})
	Expect(err).To(BeNil())

	_, found, _, err := broker.GetValue("something/a/val1")
	Expect(found).To(BeTrue())
	Expect(err).To(BeNil())

	_, found, _, err = broker.GetValue("something/a/val2")
	Expect(found).To(BeTrue())
	Expect(err).To(BeNil())

	_, found, _, err = broker.GetValue("something/a/val3")
	Expect(found).To(BeTrue())
	Expect(err).To(BeNil())

	_, err = broker.Delete("something/a", datasync.WithPrefix())
	Expect(err).To(BeNil())

	_, found, _, err = broker.GetValue("something/a/val1")
	Expect(found).To(BeFalse())
	Expect(err).To(BeNil())

	_, found, _, err = broker.GetValue("something/a/val2")
	Expect(found).To(BeFalse())
	Expect(err).To(BeNil())

	_, found, _, err = broker.GetValue("something/a/val3")
	Expect(found).To(BeFalse())
	Expect(err).To(BeNil())

}

func testPutIfNotExists(t *testing.T) {
	RegisterTestingT(t)

	conn, err := NewEtcdConnectionUsingClient(v3client.New(embd.ETCD.Server), logrus.DefaultLogger())

	Expect(err).To(BeNil())
	Expect(conn).NotTo(BeNil())

	const key = "myKey"
	var (
		intialValue  = []byte("abcd")
		changedValue = []byte("modified")
	)

	_, found, _, err := conn.GetValue(key)
	Expect(err).To(BeNil())
	Expect(found).To(BeFalse())

	inserted, err := conn.PutIfNotExists(key, intialValue)
	Expect(err).To(BeNil())
	Expect(inserted).To(BeTrue())

	data, found, _, err := conn.GetValue(key)
	Expect(err).To(BeNil())
	Expect(found).To(BeTrue())
	Expect(string(data)).To(BeEquivalentTo(string(intialValue)))

	inserted, err = conn.PutIfNotExists(key, changedValue)
	Expect(err).To(BeNil())
	Expect(inserted).To(BeFalse())

	data, found, _, err = conn.GetValue(key)
	Expect(err).To(BeNil())
	Expect(found).To(BeTrue())
	Expect(string(data)).To(BeEquivalentTo(string(intialValue)))

	_, err = conn.Delete(key)
	Expect(err).To(BeNil())

	inserted, err = conn.PutIfNotExists(key, changedValue)
	Expect(err).To(BeNil())
	Expect(inserted).To(BeTrue())

	data, found, _, err = conn.GetValue(key)
	Expect(err).To(BeNil())
	Expect(found).To(BeTrue())
	Expect(string(data)).To(BeEquivalentTo(string(changedValue)))

}

func testCompact(t *testing.T) {
	setupBrokers(t)
	defer teardownBrokers()

	/*
		This test runs following scenario:
		- store some data to key
		- overwrite with new data 		=> expect mod revision to increment
		- get previous revision			=> expect original data to return
		- compact to current revision
		- try to retrieve original data	=> expect to fail
	*/

	mykey := "mykey"
	data := []byte{1, 2, 3}
	data2 := []byte{4, 5, 6}

	//broker.etcdClient.Maintenance.Status(context.TODO())
	revision, err := broker.GetRevision()
	Expect(err).To(BeNil())
	t.Log("current revision:", revision)

	// insert some data
	err = broker.Put(prefix+mykey, data)
	Expect(err).To(BeNil())

	// retrieve the data
	retData, found, modRev, err := prefixedBroker.GetValue(mykey)
	Expect(retData).NotTo(BeNil())
	Expect(found).To(BeTrue())
	Expect(err).To(BeNil())
	//Expect(rev).To(Equal(1))
	t.Log("data:", retData, "modrev:", modRev)

	// store its mod revision
	firsRev := modRev

	// overwrite the data with new data
	err = broker.Put(prefix+mykey, data2)
	Expect(err).To(BeNil())

	// retrieve the new data
	retData, found, modRev, err = prefixedBroker.GetValue(mykey)
	Expect(retData).NotTo(BeNil())
	Expect(found).To(BeTrue())
	Expect(err).To(BeNil())
	//Expect(rev).To(Equal(1))
	t.Log("data:", retData, "modrev:", modRev)

	// retrieve the previous revision
	retData, found, modRev, err = broker.GetValueRev(prefix+mykey, firsRev)
	Expect(retData).NotTo(BeNil())
	Expect(found).To(BeTrue())
	Expect(err).To(BeNil())
	//Expect(rev).To(Equal(1))
	t.Log("data:", retData, "modrev:", modRev)

	// get current revision
	revision, err = broker.GetRevision()
	Expect(err).To(BeNil())
	t.Log("current revision:", revision)

	// compact to current revision
	err = broker.Compact(revision)
	Expect(err).To(BeNil())
	t.Log("compacted to revision:", revision)

	// try retrieving previous revision
	retData, found, modRev, err = broker.GetValueRev(prefix+mykey, firsRev)
	Expect(retData).To(BeNil())
	Expect(found).NotTo(BeTrue())
	Expect(err).NotTo(BeNil())
}

func expectWatchEvent(t *testing.T, wg *sync.WaitGroup, watchCh chan keyval.BytesWatchResp, expectedKey string) {
	select {
	case resp := <-watchCh:
		Expect(resp).NotTo(BeNil())
		Expect(resp.GetKey()).To(BeEquivalentTo(expectedKey))
	case <-time.After(1 * time.Second):
		t.Error("Watch resp not received")
		t.FailNow()
	}
	wg.Done()
}

func setupBrokers(t *testing.T) {
	RegisterTestingT(t)

	var err error
	broker, err = NewEtcdConnectionUsingClient(v3client.New(embd.ETCD.Server), logrus.DefaultLogger())

	Expect(err).To(BeNil())
	Expect(broker).NotTo(BeNil())
	// Create BytesBrokerWatcherEtcd with prefix.
	prefixedBroker = broker.NewBroker(prefix)
	prefixedWatcher = broker.NewWatcher(prefix)
	Expect(prefixedBroker).NotTo(BeNil())
	Expect(prefixedWatcher).NotTo(BeNil())

}
<|endoftext|>


<commit_before>package internal

import (
	"errors"
	"fmt"
	"sync"
)

// ErrNotSupported indicates that a feature is not supported by the current kernel.
var ErrNotSupported = errors.New("not supported")

// UnsupportedFeatureError is returned by FeatureTest() functions.
type UnsupportedFeatureError struct {
	// The minimum Linux mainline version required for this feature.
	// Used for the error string, and for sanity checking during testing.
	MinimumVersion Version

	// The name of the feature that isn't supported.
	Name string
}

func (ufe *UnsupportedFeatureError) Error() string {
	if ufe.MinimumVersion.Unspecified() {
		return fmt.Sprintf("%s not supported", ufe.Name)
	}
	return fmt.Sprintf("%s not supported (requires >= %s)", ufe.Name, ufe.MinimumVersion)
}

// Is indicates that UnsupportedFeatureError is ErrNotSupported.
func (ufe *UnsupportedFeatureError) Is(target error) bool {
	return target == ErrNotSupported
}

type featureTest struct {
	sync.Mutex
	successful bool
	result     error
}

// FeatureTestFn is used to determine whether the kernel supports
// a certain feature.
//
// The return values have the following semantics:
//
//   err == ErrNotSupported: the feature is not available
//   err == nil: the feature is available
//   err != nil: the test couldn't be executed
type FeatureTestFn func() error

// FeatureTest wraps a function so that it is run at most once.
//
// name should identify the tested feature, while version must be in the
// form Major.Minor[.Patch].
//
// Returns an error wrapping ErrNotSupported if the feature is not supported.
func FeatureTest(name, version string, fn FeatureTestFn) func() error {
	v, err := NewVersion(version)
	if err != nil {
		return func() error { return err }
	}

	ft := new(featureTest)
	return func() error {
		ft.Lock()
		defer ft.Unlock()

		if ft.successful {
			return ft.result
		}

		err := fn()
		switch {
		case errors.Is(err, ErrNotSupported):
			ft.result = &UnsupportedFeatureError{
				MinimumVersion: v,
				Name:           name,
			}
			fallthrough

		case err == nil:
			ft.successful = true

		default:
			// We couldn't execute the feature test to a point
			// where it could make a determination.
			// Don't cache the result, just return it.
			return fmt.Errorf("detect support for %s: %w", name, err)
		}

		return ft.result
	}
}

// A Version in the form Major.Minor.Patch.
type Version [3]uint16

// NewVersion creates a version from a string like "Major.Minor.Patch".
//
// Patch is optional.
func NewVersion(ver string) (Version, error) {
	var major, minor, patch uint16
	n, _ := fmt.Sscanf(ver, "%d.%d.%d", &major, &minor, &patch)
	if n < 2 {
		return Version{}, fmt.Errorf("invalid version: %s", ver)
	}
	return Version{major, minor, patch}, nil
}

func (v Version) String() string {
	if v[2] == 0 {
		return fmt.Sprintf("v%d.%d", v[0], v[1])
	}
	return fmt.Sprintf("v%d.%d.%d", v[0], v[1], v[2])
}

// Less returns true if the version is less than another version.
func (v Version) Less(other Version) bool {
	for i, a := range v {
		if a == other[i] {
			continue
		}
		return a < other[i]
	}
	return false
}

// Unspecified returns true if the version is all zero.
func (v Version) Unspecified() bool {
	return v[0] == 0 && v[1] == 0 && v[2] == 0
}
<commit_msg>nit: Give Feature Test RWMutex<commit_after>package internal

import (
	"errors"
	"fmt"
	"sync"
)

// ErrNotSupported indicates that a feature is not supported by the current kernel.
var ErrNotSupported = errors.New("not supported")

// UnsupportedFeatureError is returned by FeatureTest() functions.
type UnsupportedFeatureError struct {
	// The minimum Linux mainline version required for this feature.
	// Used for the error string, and for sanity checking during testing.
	MinimumVersion Version

	// The name of the feature that isn't supported.
	Name string
}

func (ufe *UnsupportedFeatureError) Error() string {
	if ufe.MinimumVersion.Unspecified() {
		return fmt.Sprintf("%s not supported", ufe.Name)
	}
	return fmt.Sprintf("%s not supported (requires >= %s)", ufe.Name, ufe.MinimumVersion)
}

// Is indicates that UnsupportedFeatureError is ErrNotSupported.
func (ufe *UnsupportedFeatureError) Is(target error) bool {
	return target == ErrNotSupported
}

type featureTest struct {
	sync.RWMutex
	successful bool
	result     error
}

// FeatureTestFn is used to determine whether the kernel supports
// a certain feature.
//
// The return values have the following semantics:
//
//   err == ErrNotSupported: the feature is not available
//   err == nil: the feature is available
//   err != nil: the test couldn't be executed
type FeatureTestFn func() error

// FeatureTest wraps a function so that it is run at most once.
//
// name should identify the tested feature, while version must be in the
// form Major.Minor[.Patch].
//
// Returns an error wrapping ErrNotSupported if the feature is not supported.
func FeatureTest(name, version string, fn FeatureTestFn) func() error {
	v, err := NewVersion(version)
	if err != nil {
		return func() error { return err }
	}

	ft := new(featureTest)
	return func() error {
		ft.RLock()
		if ft.successful {
			defer ft.RUnlock()
			return ft.result
		}
		ft.RUnlock()
		ft.Lock()
		defer ft.Unlock()
		// check one more time on the off
		// chance that two go routines
		// were able to call into the write
		// lock
		if ft.successful {
			return ft.result
		}
		err := fn()
		switch {
		case errors.Is(err, ErrNotSupported):
			ft.result = &UnsupportedFeatureError{
				MinimumVersion: v,
				Name:           name,
			}
			fallthrough

		case err == nil:
			ft.successful = true

		default:
			// We couldn't execute the feature test to a point
			// where it could make a determination.
			// Don't cache the result, just return it.
			return fmt.Errorf("detect support for %s: %w", name, err)
		}

		return ft.result
	}
}

// A Version in the form Major.Minor.Patch.
type Version [3]uint16

// NewVersion creates a version from a string like "Major.Minor.Patch".
//
// Patch is optional.
func NewVersion(ver string) (Version, error) {
	var major, minor, patch uint16
	n, _ := fmt.Sscanf(ver, "%d.%d.%d", &major, &minor, &patch)
	if n < 2 {
		return Version{}, fmt.Errorf("invalid version: %s", ver)
	}
	return Version{major, minor, patch}, nil
}

func (v Version) String() string {
	if v[2] == 0 {
		return fmt.Sprintf("v%d.%d", v[0], v[1])
	}
	return fmt.Sprintf("v%d.%d.%d", v[0], v[1], v[2])
}

// Less returns true if the version is less than another version.
func (v Version) Less(other Version) bool {
	for i, a := range v {
		if a == other[i] {
			continue
		}
		return a < other[i]
	}
	return false
}

// Unspecified returns true if the version is all zero.
func (v Version) Unspecified() bool {
	return v[0] == 0 && v[1] == 0 && v[2] == 0
}
<|endoftext|>