<commit_before>package filestore

import (
	"errors"
	"io"
	"os"
	"path/filepath"

	ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore"
	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/query"
	k "github.com/ipfs/go-ipfs/blocks/key"
	//mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash"
	u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
)

type datastore struct {
	ds           ds.Datastore
	alwaysVerify bool
}

func New(d ds.Datastore, fileStorePath string) (ds.Datastore, error) {
	return &datastore{d, true}, nil
}

func (d *datastore) Put(key ds.Key, value interface{}) (err error) {
	val, ok := value.(*DataWOpts)
	if !ok {
		panic(ds.ErrInvalidType)
	}

	addType, ok := val.AddOpts.(int)
	if !ok {
		panic(ds.ErrInvalidType)
	}
	if addType != AddNoCopy {
		return errors.New("Only \"no-copy\" mode supported for now.")
	}

	dataObj, ok := val.DataObj.(*DataObj)
	if !ok {
		panic(ds.ErrInvalidType)
	}

	// Make sure the filename is an absolute path
	if !filepath.IsAbs(dataObj.FilePath) {
		return errors.New("datastore put: non-absolute filename: " + dataObj.FilePath)
	}

	// Make sure we can read the file as a sanity check
	if file, err := os.Open(dataObj.FilePath); err != nil {
		return err
	} else {
		file.Close()
	}

	data, err := dataObj.Marshal()
	if err != nil {
		return err
	}
	return d.ds.Put(key, data)
}

func (d *datastore) Get(key ds.Key) (value interface{}, err error) {
	dataObj, err := d.ds.Get(key)
	if err != nil {
		return nil, err
	}
	data := dataObj.([]byte)
	val := new(DataObj)
	err = val.Unmarshal(data)
	if err != nil {
		return nil, err
	}
	if val.NoBlockData {
		file, err := os.Open(val.FilePath)
		if err != nil {
			return nil, err
		}
		_, err = file.Seek(int64(val.Offset), 0)
		if err != nil {
			return nil, err
		}
		buf := make([]byte, val.Size)
		_, err = io.ReadFull(file, buf)
		if err != nil {
			return nil, err
		}
		data, err := reconstruct(val.Data, buf)
		if err != nil {
			return nil, err
		}
		if d.alwaysVerify {
			newKey := k.Key(u.Hash(data)).DsKey()
			if newKey != key {
				return nil, errors.New("Filestore: Block Verification Failed")
			}
		}
		return data, nil
	} else {
		return val.Data, nil
	}
}

func (d *datastore) Has(key ds.Key) (exists bool, err error) {
	return d.ds.Has(key)
}

func (d *datastore) Delete(key ds.Key) error {
	return ds.ErrNotFound
}

func (d *datastore) Query(q query.Query) (query.Results, error) {
	return nil, errors.New("queries not supported yet")
}

func (d *datastore) Close() error {
	c, ok := d.ds.(io.Closer)
	if ok {
		return c.Close()
	} else {
		return nil
	}
}

func (d *datastore) Batch() (ds.Batch, error) {
	return ds.NewBasicBatch(d), nil
}
<commit_msg>Add Basic Query and "Direct" commands to filestore.  Needs Testing.<commit_after>package filestore

import (
	"errors"
	"io"
	"os"
	"path/filepath"

	ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore"
	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/query"
	k "github.com/ipfs/go-ipfs/blocks/key"
	//mh "gx/ipfs/QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku/go-multihash"
	u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
)

type datastore struct {
	ds           ds.Datastore
	alwaysVerify bool
}

func New(d ds.Datastore, fileStorePath string) (ds.Datastore, error) {
	return &datastore{d, true}, nil
}

func (d *datastore) Put(key ds.Key, value interface{}) (err error) {
	val, ok := value.(*DataWOpts)
	if !ok {
		panic(ds.ErrInvalidType)
	}

	addType, ok := val.AddOpts.(int)
	if !ok {
		panic(ds.ErrInvalidType)
	}
	if addType != AddNoCopy {
		return errors.New("Only \"no-copy\" mode supported for now.")
	}

	dataObj, ok := val.DataObj.(*DataObj)
	if !ok {
		panic(ds.ErrInvalidType)
	}

	// Make sure the filename is an absolute path
	if !filepath.IsAbs(dataObj.FilePath) {
		return errors.New("datastore put: non-absolute filename: " + dataObj.FilePath)
	}

	// Make sure we can read the file as a sanity check
	if file, err := os.Open(dataObj.FilePath); err != nil {
		return err
	} else {
		file.Close()
	}

	data, err := dataObj.Marshal()
	if err != nil {
		return err
	}
	return d.ds.Put(key, data)
}

func (d *datastore) Get(key ds.Key) (value interface{}, err error) {
	dataObj, err := d.ds.Get(key)
	if err != nil {
		return nil, err
	}
	val, err := d.decode(dataObj)
	if err != nil {
		return nil, err
	}
	return d.GetData(key, val, d.alwaysVerify)
}

// Get the key as a DataObj
func (d *datastore) GetDirect(key ds.Key) (*DataObj, error) {
	dataObj, err := d.ds.Get(key)
	if err != nil {
		return nil, err
	}
	return d.decode(dataObj)
}

func (d *datastore) decode(dataObj interface{}) (*DataObj, error) {
	data := dataObj.([]byte)
	val := new(DataObj)
	err := val.Unmarshal(data)
	if err != nil {
		return nil, err
	}
	return val, nil
}

// Get the orignal data out of the DataObj
func (d *datastore) GetData(key ds.Key, val *DataObj, verify bool) ([]byte, error) {
	if val.NoBlockData {
		file, err := os.Open(val.FilePath)
		if err != nil {
			return nil, err
		}
		_, err = file.Seek(int64(val.Offset), 0)
		if err != nil {
			return nil, err
		}
		buf := make([]byte, val.Size)
		_, err = io.ReadFull(file, buf)
		if err != nil {
			return nil, err
		}
		data, err := reconstruct(val.Data, buf)
		if err != nil {
			return nil, err
		}
		if verify {
			newKey := k.Key(u.Hash(data)).DsKey()
			if newKey != key {
				return nil, errors.New("Filestore: Block Verification Failed")
			}
		}
		return data, nil
	} else {
		return val.Data, nil
	}
}

func (d *datastore) Has(key ds.Key) (exists bool, err error) {
	return d.ds.Has(key)
}

func (d *datastore) Delete(key ds.Key) error {
	return ds.ErrNotFound
}

func (d *datastore) DeleteDirect(key ds.Key) error {
	return d.ds.Delete(key)
}

func (d *datastore) Query(q query.Query) (query.Results, error) {
	res, err := d.ds.Query(q)
	if err != nil {
		return nil, err
	}
	if q.KeysOnly {
		return res, nil
	}
	return nil, errors.New("filestore currently only supports keyonly queries")
	// return &queryResult{res, func(r query.Result) query.Result {
	// 	val, err := d.decode(r.Value)
	// 	if err != nil {
	// 		return query.Result{query.Entry{r.Key, nil}, err}
	// 	}
	// 	// Note: It should not be necessary to reclean the key
	// 	// here (by calling ds.NewKey) just to convert the
	// 	// string back to a ds.Key
	// 	data, err := d.GetData(ds.NewKey(r.Key), val, d.alwaysVerify)
	// 	if err != nil {
	// 		return query.Result{query.Entry{r.Key, nil}, err}
	// 	}
	// 	return query.Result{query.Entry{r.Key, data}, r.Error}
	// }}, nil
}

func (d *datastore) QueryDirect(q query.Query) (query.Results, error) {
	res, err := d.ds.Query(q)
	if err != nil {
		return nil, err
	}
	if q.KeysOnly {
		return res, nil
	}
	return nil, errors.New("filestore currently only supports keyonly queries")
	// return &queryResult{res, func(r query.Result) query.Result {
	// 	val, err := d.decode(r.Value)
	// 	if err != nil {
	// 		return query.Result{query.Entry{r.Key, nil}, err}
	// 	}
	// 	return query.Result{query.Entry{r.Key, val}, r.Error}
	// }}, nil
}

// type queryResult struct {
// 	query.Results
// 	adjResult func(query.Result) query.Result
// }

// func (q *queryResult) Next() <-chan query.Result {
// 	in := q.Results.Next()
// 	out := make(chan query.Result)
// 	go func() {
// 		res := <-in
// 		if res.Error == nil {
// 			out <- res
// 		}
// 		out <- q.adjResult(res)
// 	}()
// 	return out
// }

// func (q *queryResult) Rest() ([]query.Entry, error) {
// 	res, err := q.Results.Rest()
// 	if err != nil {
// 		return nil, err
// 	}
// 	for _, entry := range res {
// 		newRes := q.adjResult(query.Result{entry, nil})
// 		if newRes.Error != nil {
// 			return nil, newRes.Error
// 		}
// 		entry.Value = newRes.Value
// 	}
// 	return res, nil
// }

func (d *datastore) Close() error {
	c, ok := d.ds.(io.Closer)
	if ok {
		return c.Close()
	} else {
		return nil
	}
}

func (d *datastore) Batch() (ds.Batch, error) {
	return ds.NewBasicBatch(d), nil
}
<|endoftext|>


<commit_before><commit_msg>Start with the first text field focused<commit_after><|endoftext|>


<commit_before>// Copyright © 2017 Pennock Tech, LLC.
// All rights reserved, except as granted under license.
// Licensed per file LICENSE.txt

package main

import (
	"errors"
	"fmt"
	"net"
	"os"
	"regexp"
	"strconv"
	"sync"

	"go.pennock.tech/smtpdane/internal/errorlist"

	"github.com/miekg/dns"
)

const EnvKeyDNSResolver = "DNS_RESOLVER"

var dnsSettings struct {
	sync.RWMutex
	conf   *dns.ClientConfig
	client *dns.Client
}

func initDNS() (*dns.ClientConfig, *dns.Client, error) {
	dnsSettings.RLock()
	if dnsSettings.client != nil {
		defer dnsSettings.RUnlock()
		return dnsSettings.conf, dnsSettings.client, nil
	}
	dnsSettings.RUnlock()
	dnsSettings.Lock()
	defer dnsSettings.Unlock()
	if dnsSettings.client != nil {
		return dnsSettings.conf, dnsSettings.client, nil
	}

	var (
		conf *dns.ClientConfig
		err  error
	)
	if os.Getenv(EnvKeyDNSResolver) == "" {
		conf, err = dns.ClientConfigFromFile("/etc/resolv.conf")
		if err != nil {
			return nil, nil, err
		}
	} else {
		// we now use the config always, for things like timeouts,
		// so construct a skeletal one
		conf = &dns.ClientConfig{
			Attempts: 3,
		}
	}

	c := new(dns.Client)

	dnsSettings.conf = conf
	dnsSettings.client = c

	return dnsSettings.conf, dnsSettings.client, nil
}

func resolversFromList(input []string, defDNSPort string) []string {
	r := make([]string, len(input))
	for i := range input {
		r[i] = HostPortWithDefaultPort(input[i], defDNSPort)
	}
	return r
}

var resolverSplitRE *regexp.Regexp

func init() {
	resolverSplitRE = regexp.MustCompile(`[,\s]+`)
}

func resolversFromString(input string) []string {
	return resolversFromList(resolverSplitRE.Split(input, -1), "53")
}

// FIXME: This is not doing DNS validation locally.
// It's resolving DNS, delegating trust in validation to the resolver by
// trusting the AD bit.
// I want to get this working without needing a validating resolver.
// This should be a standalone monitoring tool.
func resolveRRSecure(
	// the cbfunc is called the the confirmed RR type and the rr and the rrname;
	// it should return an item to be added to the resolveRRSecure return list,
	// and an error; non-nil error inhibits appending to the list.
	cbfunc func(typ uint16, rr dns.RR, rrname string) (interface{}, error),
	rrname string,
	typlist ...uint16,
) ([]interface{}, error) {
	config, c, err := initDNS()
	if err != nil {
		return nil, err
	}

	var resolvers []string
	if r := os.Getenv(EnvKeyDNSResolver); r != "" {
		resolvers = resolversFromString(r)
	} else {
		resolvers = resolversFromList(config.Servers, config.Port)
	}

	resultList := make([]interface{}, 0, 20)
	errList := errorlist.New()

	m := new(dns.Msg)
	m.SetEdns0(dns.DefaultMsgSize, true)

	// why is this uint16 ipv dns.Type ?  Infelicity stuck in API?
DNS_RRTYPE_LOOP:
	for _, typ := range typlist {
		m.SetQuestion(rrname, typ)

		var (
			r   *dns.Msg
			err error
		)

	DNS_RESOLVER_LOOP:
		for _, resolver := range resolvers {
			c.Net = "udp"
		RETRY_DNS_LOOKUP:
			for i := 0; i < config.Attempts; i++ {
				r, _, err = c.Exchange(m, resolver)
				if err != nil {
					if nerr, ok := err.(net.Error); ok && nerr.Timeout() && i < config.Attempts {
						continue
					}
					errList.Add(err)
					r = nil
					continue DNS_RESOLVER_LOOP
				}
			}

			if r.Rcode != dns.RcodeSuccess {
				failure, known := dns.RcodeToString[r.Rcode]
				if !known {
					failure = fmt.Sprintf("Rcode<%d> (unknown)", r.Rcode)
				}
				errList.AddErrorf("DNS lookup non-successful [resolver %v]: %v", resolver, failure)
				r = nil
				continue DNS_RESOLVER_LOOP
			}

			// Check for truncation first, in case some bad servers truncate
			// the DNSSEC data needed to be AD.
			if r.Truncated {
				c.Net = "tcp"
				goto RETRY_DNS_LOOKUP
			}

			// Here we depend upon AD bit and so are still secure, assuming secure
			// link to trusted resolver.
			// Assume all our resolvers are equivalent for AD/not, so if not AD, try the
			// next type (because some DNS servers break horribly on AAAA).
			if !r.AuthenticatedData {
				errList.AddErrorf("not AD set for results from %v for %q/%v query", resolver, rrname, dns.Type(typ))
				r = nil
				continue DNS_RRTYPE_LOOP
			}
		}

		if r == nil {
			errList.AddErrorf("[%q/%v]: all DNS resolver queries failed, unable to get authentic result", rrname, dns.Type(typ))
			// seems likely might be SERVFAIL from broken auth servers for AAAA records
			continue DNS_RRTYPE_LOOP
		}

		for _, rr := range r.Answer {
			// TODO: CNAME?
			if rr.Header().Rrtype != typ {
				continue
			}
			x, err := cbfunc(typ, dns.Copy(rr), rrname)
			if err != nil {
				errList.Add(err)
			} else {
				resultList = append(resultList, x)
			}
		}
	}

	if len(resultList) == 0 {
		errList.Add(errors.New("no results found"))
		return nil, errList
	}
	return resultList, errList.Maybe()
}

// There's a lot of repetition/boilerplate in the below.
// If we expand beyond where we are at now, then we really should consider reflection; more complexity, less repetition.

func cbRRTypeAddr(typ uint16, rr dns.RR, rrname string) (interface{}, error) {
	switch typ {
	case dns.TypeA:
		if ip, ok := rr.(*dns.A); ok {
			return ip.A, nil
		} else {
			return nil, fmt.Errorf("A record failed to cast to *dns.A [%q/%v]", rrname, dns.Type(typ))
		}
	case dns.TypeAAAA:
		if ip, ok := rr.(*dns.AAAA); ok {
			return ip.AAAA, nil
		} else {
			return nil, fmt.Errorf("AAAA record failed to cast to *dns.AAAA [%q/%v]", rrname, dns.Type(typ))
		}
	}
	return nil, fmt.Errorf("BUG: cbRRTypeAddr(%v,..,%q) called, expected A/AAAA", dns.Type(typ), rrname)
}

func ResolveAddrSecure(hostname string) ([]net.IP, error) {
	rl, e := resolveRRSecure(cbRRTypeAddr, dns.Fqdn(hostname), dns.TypeAAAA, dns.TypeA)
	if e != nil {
		return nil, e
	}
	addrList := make([]net.IP, len(rl))
	for i := range rl {
		addrList[i] = rl[i].(net.IP)
	}
	return addrList, nil
}

type TLSAset struct {
	RRs       []*dns.TLSA
	name      string
	foundName string
}

func cbRRTypeTLSA(typ uint16, rr dns.RR, rrname string) (interface{}, error) {
	switch typ {
	case dns.TypeTLSA:
		if tlsa, ok := rr.(*dns.TLSA); ok {
			return tlsa, nil
		} else {
			return nil, fmt.Errorf("TLSA record failed to cast to *dns.TLSA [%q/%v]", rrname, dns.Type(typ))
		}
	}
	return nil, fmt.Errorf("BUG: cbRRTypeTLSA(%v,..,%q) called, expected TLSA", dns.Type(typ), rrname)
}

func ResolveTLSA(hostname string, port int) (*TLSAset, error) {
	tlsaName := fmt.Sprintf("_%d._tcp.%s", port, dns.Fqdn(hostname))
	rl, e := resolveRRSecure(cbRRTypeTLSA, tlsaName, dns.TypeTLSA)
	if e != nil {
		return nil, e
	}

	TLSAList := make([]*dns.TLSA, len(rl))
	for i := range rl {
		TLSAList[i] = rl[i].(*dns.TLSA)
	}

	return &TLSAset{
		RRs:       TLSAList,
		name:      tlsaName,
		foundName: TLSAList[0].Hdr.Name,
	}, nil
}

// TLSAShortString provides something suitable for output without showing the
// full contents; for our uses, we don't need the RR_Header and for
// full-certs-in-DNS we don't _want_ to print it all.
// Viktor points out that for full certs in DNS, the start of the record will
// be less useful, so show the _last_ 16 octets
// TLSAShortString is "enough to probably fit on a line with much other text".
func TLSAShortString(rr *dns.TLSA) string {
	offset := len(rr.Certificate) - 16
	prefix := "..."
	if offset < 0 {
		offset = 0
		prefix = ""
	}
	return strconv.Itoa(int(rr.Usage)) + " " +
		strconv.Itoa(int(rr.Selector)) + " " +
		strconv.Itoa(int(rr.MatchingType)) + " " +
		prefix + rr.Certificate[offset:]
}

// TLSAMediumString is for where the TLSA record is probably all that's on a line.
// Assume 2 leading spaces, 1 digit for each of the three leading fields, a space
// after each, that's 8, allow for 70.
func TLSAMediumString(rr *dns.TLSA) string {
	var rest, prefix string
	if len(rr.Certificate) <= 70 {
		rest = rr.Certificate
	} else {
		prefix = "..."
		rest = rr.Certificate[(len(rr.Certificate) - 67):]
	}
	return strconv.Itoa(int(rr.Usage)) + " " +
		strconv.Itoa(int(rr.Selector)) + " " +
		strconv.Itoa(int(rr.MatchingType)) + " " +
		prefix + rest
}

func cbRRTypeMX(typ uint16, rr dns.RR, rrname string) (interface{}, error) {
	switch typ {
	case dns.TypeMX:
		if mx, ok := rr.(*dns.MX); ok {
			return mx.Mx, nil
		} else {
			return nil, fmt.Errorf("MX record failed to cast to *dns.MX [%q/%v]", rrname, dns.Type(typ))
		}
	}
	return nil, fmt.Errorf("BUG: cbRRTypeMX(%v,..,%q) called, expected MX", dns.Type(typ), rrname)
}

// ResolveMX only returns the hostnames, we don't care about the Preference
func ResolveMX(hostname string) ([]string, error) {
	rl, e := resolveRRSecure(cbRRTypeMX, dns.Fqdn(hostname), dns.TypeMX)
	if e != nil {
		return nil, e
	}
	hostnameList := make([]string, len(rl))
	for i := range rl {
		hostnameList[i] = rl[i].(string)
	}
	return hostnameList, nil
}

func cbRRTypeSRV(typ uint16, rr dns.RR, rrname string) (interface{}, error) {
	switch typ {
	case dns.TypeSRV:
		if srv, ok := rr.(*dns.SRV); ok {
			return srv, nil
		} else {
			return nil, fmt.Errorf("SRV record failed to cast to *dns.SRV [%q/%v]", rrname, dns.Type(typ))
		}
	}
	return nil, fmt.Errorf("BUG: cbRRTypeSRV(%v,..,%q) called, expected SRV", dns.Type(typ), rrname)
}

// ResolveSRV returns MX records, we need at least the Port, not just the Target
func ResolveSRV(lookup string) ([]*dns.SRV, error) {
	rl, e := resolveRRSecure(cbRRTypeSRV, dns.Fqdn(lookup), dns.TypeSRV)
	if e != nil {
		return nil, e
	}
	srvList := make([]*dns.SRV, len(rl))
	for i := range rl {
		srvList[i] = rl[i].(*dns.SRV)
	}
	return srvList, nil
}
<commit_msg>Treat NXDOMAIN as definitive, skip other resolvers<commit_after>// Copyright © 2017 Pennock Tech, LLC.
// All rights reserved, except as granted under license.
// Licensed per file LICENSE.txt

package main

import (
	"errors"
	"fmt"
	"net"
	"os"
	"regexp"
	"strconv"
	"sync"

	"go.pennock.tech/smtpdane/internal/errorlist"

	"github.com/miekg/dns"
)

const EnvKeyDNSResolver = "DNS_RESOLVER"

var dnsSettings struct {
	sync.RWMutex
	conf   *dns.ClientConfig
	client *dns.Client
}

func initDNS() (*dns.ClientConfig, *dns.Client, error) {
	dnsSettings.RLock()
	if dnsSettings.client != nil {
		defer dnsSettings.RUnlock()
		return dnsSettings.conf, dnsSettings.client, nil
	}
	dnsSettings.RUnlock()
	dnsSettings.Lock()
	defer dnsSettings.Unlock()
	if dnsSettings.client != nil {
		return dnsSettings.conf, dnsSettings.client, nil
	}

	var (
		conf *dns.ClientConfig
		err  error
	)
	if os.Getenv(EnvKeyDNSResolver) == "" {
		conf, err = dns.ClientConfigFromFile("/etc/resolv.conf")
		if err != nil {
			return nil, nil, err
		}
	} else {
		// we now use the config always, for things like timeouts,
		// so construct a skeletal one
		conf = &dns.ClientConfig{
			Attempts: 3,
		}
	}

	c := new(dns.Client)

	dnsSettings.conf = conf
	dnsSettings.client = c

	return dnsSettings.conf, dnsSettings.client, nil
}

func resolversFromList(input []string, defDNSPort string) []string {
	r := make([]string, len(input))
	for i := range input {
		r[i] = HostPortWithDefaultPort(input[i], defDNSPort)
	}
	return r
}

var resolverSplitRE *regexp.Regexp

func init() {
	resolverSplitRE = regexp.MustCompile(`[,\s]+`)
}

func resolversFromString(input string) []string {
	return resolversFromList(resolverSplitRE.Split(input, -1), "53")
}

// FIXME: This is not doing DNS validation locally.
// It's resolving DNS, delegating trust in validation to the resolver by
// trusting the AD bit.
// I want to get this working without needing a validating resolver.
// This should be a standalone monitoring tool.
func resolveRRSecure(
	// the cbfunc is called the the confirmed RR type and the rr and the rrname;
	// it should return an item to be added to the resolveRRSecure return list,
	// and an error; non-nil error inhibits appending to the list.
	cbfunc func(typ uint16, rr dns.RR, rrname string) (interface{}, error),
	rrname string,
	typlist ...uint16,
) ([]interface{}, error) {
	config, c, err := initDNS()
	if err != nil {
		return nil, err
	}

	var resolvers []string
	if r := os.Getenv(EnvKeyDNSResolver); r != "" {
		resolvers = resolversFromString(r)
	} else {
		resolvers = resolversFromList(config.Servers, config.Port)
	}

	resultList := make([]interface{}, 0, 20)
	errList := errorlist.New()

	m := new(dns.Msg)
	m.SetEdns0(dns.DefaultMsgSize, true)

	// why is this uint16 ipv dns.Type ?  Infelicity stuck in API?
DNS_RRTYPE_LOOP:
	for _, typ := range typlist {
		m.SetQuestion(rrname, typ)

		var (
			r   *dns.Msg
			err error
		)

	DNS_RESOLVER_LOOP:
		for _, resolver := range resolvers {
			c.Net = "udp"
		RETRY_DNS_LOOKUP:
			for i := 0; i < config.Attempts; i++ {
				r, _, err = c.Exchange(m, resolver)
				if err != nil {
					if nerr, ok := err.(net.Error); ok && nerr.Timeout() && i < config.Attempts {
						continue
					}
					errList.Add(err)
					r = nil
					continue DNS_RESOLVER_LOOP
				}
			}

			if r.Rcode != dns.RcodeSuccess {
				failure, known := dns.RcodeToString[r.Rcode]
				if !known {
					failure = fmt.Sprintf("Rcode<%d> (unknown)", r.Rcode)
				}
				errList.AddErrorf("DNS lookup non-successful [resolver %v]: %v", resolver, failure)
				rcode := r.Rcode
				r = nil
				// There are enough broken server implementations when it comes
				// to AD and unknown types (often including AAAA) that we
				// currently only consider NXDOMAIN definitive.
				// We can expand upon this as needed.
				switch rcode {
				case dns.RcodeNameError:
					continue DNS_RRTYPE_LOOP
				default:
					continue DNS_RESOLVER_LOOP
				}
			}

			// Check for truncation first, in case some bad servers truncate
			// the DNSSEC data needed to be AD.
			if r.Truncated {
				c.Net = "tcp"
				goto RETRY_DNS_LOOKUP
			}

			// Here we depend upon AD bit and so are still secure, assuming secure
			// link to trusted resolver.
			// Assume all our resolvers are equivalent for AD/not, so if not AD, try the
			// next type (because some DNS servers break horribly on AAAA).
			if !r.AuthenticatedData {
				errList.AddErrorf("not AD set for results from %v for %q/%v query", resolver, rrname, dns.Type(typ))
				r = nil
				continue DNS_RRTYPE_LOOP
			}
		}

		if r == nil {
			errList.AddErrorf("[%q/%v]: all DNS resolver queries failed, unable to get authentic result", rrname, dns.Type(typ))
			// seems likely might be SERVFAIL from broken auth servers for AAAA records
			continue DNS_RRTYPE_LOOP
		}

		for _, rr := range r.Answer {
			// TODO: CNAME?
			if rr.Header().Rrtype != typ {
				continue
			}
			x, err := cbfunc(typ, dns.Copy(rr), rrname)
			if err != nil {
				errList.Add(err)
			} else {
				resultList = append(resultList, x)
			}
		}
	}

	if len(resultList) == 0 {
		errList.Add(errors.New("no results found"))
		return nil, errList
	}
	return resultList, errList.Maybe()
}

// There's a lot of repetition/boilerplate in the below.
// If we expand beyond where we are at now, then we really should consider reflection; more complexity, less repetition.

func cbRRTypeAddr(typ uint16, rr dns.RR, rrname string) (interface{}, error) {
	switch typ {
	case dns.TypeA:
		if ip, ok := rr.(*dns.A); ok {
			return ip.A, nil
		} else {
			return nil, fmt.Errorf("A record failed to cast to *dns.A [%q/%v]", rrname, dns.Type(typ))
		}
	case dns.TypeAAAA:
		if ip, ok := rr.(*dns.AAAA); ok {
			return ip.AAAA, nil
		} else {
			return nil, fmt.Errorf("AAAA record failed to cast to *dns.AAAA [%q/%v]", rrname, dns.Type(typ))
		}
	}
	return nil, fmt.Errorf("BUG: cbRRTypeAddr(%v,..,%q) called, expected A/AAAA", dns.Type(typ), rrname)
}

func ResolveAddrSecure(hostname string) ([]net.IP, error) {
	rl, e := resolveRRSecure(cbRRTypeAddr, dns.Fqdn(hostname), dns.TypeAAAA, dns.TypeA)
	if e != nil {
		return nil, e
	}
	addrList := make([]net.IP, len(rl))
	for i := range rl {
		addrList[i] = rl[i].(net.IP)
	}
	return addrList, nil
}

type TLSAset struct {
	RRs       []*dns.TLSA
	name      string
	foundName string
}

func cbRRTypeTLSA(typ uint16, rr dns.RR, rrname string) (interface{}, error) {
	switch typ {
	case dns.TypeTLSA:
		if tlsa, ok := rr.(*dns.TLSA); ok {
			return tlsa, nil
		} else {
			return nil, fmt.Errorf("TLSA record failed to cast to *dns.TLSA [%q/%v]", rrname, dns.Type(typ))
		}
	}
	return nil, fmt.Errorf("BUG: cbRRTypeTLSA(%v,..,%q) called, expected TLSA", dns.Type(typ), rrname)
}

func ResolveTLSA(hostname string, port int) (*TLSAset, error) {
	tlsaName := fmt.Sprintf("_%d._tcp.%s", port, dns.Fqdn(hostname))
	rl, e := resolveRRSecure(cbRRTypeTLSA, tlsaName, dns.TypeTLSA)
	if e != nil {
		return nil, e
	}

	TLSAList := make([]*dns.TLSA, len(rl))
	for i := range rl {
		TLSAList[i] = rl[i].(*dns.TLSA)
	}

	return &TLSAset{
		RRs:       TLSAList,
		name:      tlsaName,
		foundName: TLSAList[0].Hdr.Name,
	}, nil
}

// TLSAShortString provides something suitable for output without showing the
// full contents; for our uses, we don't need the RR_Header and for
// full-certs-in-DNS we don't _want_ to print it all.
// Viktor points out that for full certs in DNS, the start of the record will
// be less useful, so show the _last_ 16 octets
// TLSAShortString is "enough to probably fit on a line with much other text".
func TLSAShortString(rr *dns.TLSA) string {
	offset := len(rr.Certificate) - 16
	prefix := "..."
	if offset < 0 {
		offset = 0
		prefix = ""
	}
	return strconv.Itoa(int(rr.Usage)) + " " +
		strconv.Itoa(int(rr.Selector)) + " " +
		strconv.Itoa(int(rr.MatchingType)) + " " +
		prefix + rr.Certificate[offset:]
}

// TLSAMediumString is for where the TLSA record is probably all that's on a line.
// Assume 2 leading spaces, 1 digit for each of the three leading fields, a space
// after each, that's 8, allow for 70.
func TLSAMediumString(rr *dns.TLSA) string {
	var rest, prefix string
	if len(rr.Certificate) <= 70 {
		rest = rr.Certificate
	} else {
		prefix = "..."
		rest = rr.Certificate[(len(rr.Certificate) - 67):]
	}
	return strconv.Itoa(int(rr.Usage)) + " " +
		strconv.Itoa(int(rr.Selector)) + " " +
		strconv.Itoa(int(rr.MatchingType)) + " " +
		prefix + rest
}

func cbRRTypeMX(typ uint16, rr dns.RR, rrname string) (interface{}, error) {
	switch typ {
	case dns.TypeMX:
		if mx, ok := rr.(*dns.MX); ok {
			return mx.Mx, nil
		} else {
			return nil, fmt.Errorf("MX record failed to cast to *dns.MX [%q/%v]", rrname, dns.Type(typ))
		}
	}
	return nil, fmt.Errorf("BUG: cbRRTypeMX(%v,..,%q) called, expected MX", dns.Type(typ), rrname)
}

// ResolveMX only returns the hostnames, we don't care about the Preference
func ResolveMX(hostname string) ([]string, error) {
	rl, e := resolveRRSecure(cbRRTypeMX, dns.Fqdn(hostname), dns.TypeMX)
	if e != nil {
		return nil, e
	}
	hostnameList := make([]string, len(rl))
	for i := range rl {
		hostnameList[i] = rl[i].(string)
	}
	return hostnameList, nil
}

func cbRRTypeSRV(typ uint16, rr dns.RR, rrname string) (interface{}, error) {
	switch typ {
	case dns.TypeSRV:
		if srv, ok := rr.(*dns.SRV); ok {
			return srv, nil
		} else {
			return nil, fmt.Errorf("SRV record failed to cast to *dns.SRV [%q/%v]", rrname, dns.Type(typ))
		}
	}
	return nil, fmt.Errorf("BUG: cbRRTypeSRV(%v,..,%q) called, expected SRV", dns.Type(typ), rrname)
}

// ResolveSRV returns MX records, we need at least the Port, not just the Target
func ResolveSRV(lookup string) ([]*dns.SRV, error) {
	rl, e := resolveRRSecure(cbRRTypeSRV, dns.Fqdn(lookup), dns.TypeSRV)
	if e != nil {
		return nil, e
	}
	srvList := make([]*dns.SRV, len(rl))
	for i := range rl {
		srvList[i] = rl[i].(*dns.SRV)
	}
	return srvList, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"time"
)

type FileMeta struct {
	FileName  string
	ModTime   time.Time
	LineCount int
}

func main() {
	flag.Parse()
	args := flag.Args()

	if len(args) != 2 {
		fmt.Printf("Command line usage: file-watcher <folder> <file-pattern>\n")
		return
	}

	root := flag.Arg(0)
	pattern := flag.Arg(1)

	var previousFiles, currentFiles, modifiedFiles map[string]FileMeta

	previousFiles = getFilesMatchingPattern(pattern, root)
	countFilesLines(previousFiles)

	for fileName, fileMeta := range previousFiles {
		fmt.Printf("Start values: %s %d\n", fileName, fileMeta.LineCount)
	}

	ticker := time.NewTicker(1 * time.Second)
	for {
		select {
		case <-ticker.C:
			// Get a current listing of the files and their mod times
			currentFiles = getFilesMatchingPattern(pattern, root)

			newFiles, deletedFiles := getNewAndDeletedFiles(previousFiles, currentFiles)

			for newFile := range newFiles {
				fmt.Printf("New file: %s\n", newFile)
			}

			for deletedFile := range deletedFiles {
				fmt.Printf("Deleted file: %s\n", deletedFile)
			}

			// Get a list of the file names that have been modified based off the mod time
			// Go through and get updated line counts for just the modified files
			modifiedFiles = getModifiedFiles(previousFiles, currentFiles)
			if len(modifiedFiles) > 0 {
				countFilesLines(modifiedFiles)
				// Loop through the modified files and list the line count change compared to the preivous check.
				// Update the LineCount property in the currentFiles list as we go.
				for fileName, fileMeta := range modifiedFiles {
					previousFileMeta, _ := previousFiles[fileName]
					fmt.Printf("Last count: %d new count: %d\n", previousFileMeta.LineCount, fileMeta.LineCount)
					currentFiles[fileName] = fileMeta
				}

			}
			previousFiles = currentFiles
		}
	}
}

func getFilesMatchingPattern(pattern string, root string) map[string]FileMeta {
	files := make(map[string]FileMeta)
	visit := func(fileName string, f os.FileInfo, err error) error {
		match, err := path.Match(pattern, f.Name())
		if match {
			files[fileName] = FileMeta{FileName: fileName, ModTime: f.ModTime()}
		}
		return nil
	}
	filepath.Walk(root, visit)
	return files
}

func getModifiedFiles(previousFiles, currentFiles map[string]FileMeta) map[string]FileMeta {
	modifiedFiles := make(map[string]FileMeta)

	for fileName, previousFileMeta := range previousFiles {
		if currentFileMeta, ok := currentFiles[fileName]; ok {
			if currentFileMeta.ModTime.After(previousFileMeta.ModTime) {
				modifiedFiles[fileName] = currentFileMeta
			}
		}
	}
	return modifiedFiles
}

func getNewAndDeletedFiles(previousFiles, currentFiles map[string]FileMeta) ([]string, []string) {
	var newFiles, deletedFiles []string
	for fileName, _ := range previousFiles {
		if _, ok := currentFiles[fileName]; !ok {
			deletedFiles = append(deletedFiles, fileName)
		}
	}

	for fileName, _ := range currentFiles {
		if _, ok := previousFiles[fileName]; !ok {
			newFiles = append(newFiles, fileName)
		}
	}

	return newFiles, deletedFiles
}

func countFilesLines(files map[string]FileMeta) {
	doneCountingLinesChan := make(chan int)
	for fileName, fileMeta := range files {
		go func(fileName string, fileMeta FileMeta, doneCountingLinesChan chan<- int) {
			currentFileMeta, _ := files[fileName]
			currentFileMeta.LineCount = countFileLines(fileMeta)
			files[fileName] = currentFileMeta
			doneCountingLinesChan <- 1
		}(fileName, fileMeta, doneCountingLinesChan)
	}
	for i := 0; i < len(files); i++ {
		<-doneCountingLinesChan
	}
	return
}

func countFileLines(fileMeta FileMeta) int {
	file, _ := os.Open(fileMeta.FileName)
	scanner := bufio.NewScanner(file)
	lineCount := 0

	for scanner.Scan() {
		lineCount++
	}

	return lineCount
}
<commit_msg>Almost finished. Just cleaning up<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"time"
)

type FileStatus struct {
	FileName  string
	ModTime   time.Time
	LineCount int
}

var root, pattern string

func init() {
	flag.Parse()
	args := flag.Args()

	if len(args) != 2 {
		fmt.Printf("Command line usage: file-watcher <folder> <file-pattern>\n")
		return
	}

	root = args[0]
	pattern = args[1]
}

func main() {
	var previousFiles, currentFiles, modifiedFiles map[string]FileStatus

	previousFiles = getFilesMatchingPattern(pattern, root)
	countFilesLines(previousFiles)

	for fileName, fileStatus := range previousFiles {
		fmt.Printf("Start values: %s %d\n", fileName, fileStatus.LineCount)
	}

	ticker := time.NewTicker(1 * time.Second)
	for {
		select {
		case <-ticker.C:
			// Get a current listing of the files and their mod times
			currentFiles = getFilesMatchingPattern(pattern, root)

			// Get a list of new files and deleted files
			newFiles, deletedFiles := getNewAndDeletedFiles(previousFiles, currentFiles)
			for fileName, fileStatus := range newFiles {
				fmt.Printf("%s: %s: %d\n", "New", fileName, fileStatus.LineCount)
			}

			for _, fileName := range deletedFiles {
				fmt.Printf("%s: %s\n", "Deleted", fileName)
			}

			// Get a list of the file names that have been modified based off the mod time
			// Go through and get updated line counts for just the modified files
			modifiedFiles = getModifiedFiles(previousFiles, currentFiles)
			if len(modifiedFiles) > 0 {
				countFilesLines(modifiedFiles)
				// Loop through the modified files and list the line count change compared to the previous check.
				for fileName, fileStatus := range modifiedFiles {
					previousFileStatus, _ := previousFiles[fileName]
					displayFileDiff(previousFileStatus, fileStatus)
				}
			}

			// Make sure new files have line counts, and only include the actual current state of the file system.
			previousFiles = syncFileStatus(previousFiles, currentFiles, modifiedFiles, newFiles)
		}
	}
}

func displayFileDiff(previousFileStatus, currentFileStatus FileStatus) {
	if lineDiff := currentFileStatus.LineCount - previousFileStatus.LineCount; lineDiff == 0 {
		return
	}
	sign := ""
	if lineDiff > 0 {
		sign = "+"
	}
	fmt.Printf("%s %s%d\n", currentFileStatus.FileName, sign, lineDiff)
}

func getFilesMatchingPattern(pattern string, root string) map[string]FileStatus {
	files := make(map[string]FileStatus)
	visit := func(fileName string, f os.FileInfo, err error) error {
		match, err := path.Match(pattern, f.Name())
		if match {
			files[fileName] = FileStatus{FileName: fileName, ModTime: f.ModTime()}
		}
		return nil
	}
	filepath.Walk(root, visit)
	return files
}

func getModifiedFiles(previousFiles, currentFiles map[string]FileStatus) map[string]FileStatus {
	modifiedFiles := make(map[string]FileStatus)

	for fileName, previousFileStatus := range previousFiles {
		if currentFileStatus, ok := currentFiles[fileName]; ok {
			if currentFileStatus.ModTime.After(previousFileStatus.ModTime) {
				modifiedFiles[fileName] = currentFileStatus
			}
		}
	}
	return modifiedFiles
}

func getNewAndDeletedFiles(previousFiles, currentFiles map[string]FileStatus) (map[string]FileStatus, []string) {
	newFiles := make(map[string]FileStatus)
	var deletedFiles []string

	for fileName, fileStatus := range currentFiles {
		if _, ok := previousFiles[fileName]; !ok {
			newFiles[fileName] = fileStatus
		}
	}
	countFilesLines(newFiles)

	for fileName, _ := range previousFiles {
		if _, ok := currentFiles[fileName]; !ok {
			deletedFiles = append(deletedFiles, fileName)
		}
	}

	return newFiles, deletedFiles
}

func countFilesLines(files map[string]FileStatus) {
	done := make(chan int)
	for fileName, fileStatus := range files {
		go func(fileName string, fileStatus FileStatus, done chan<- int) {
			currentFileStatus, _ := files[fileName]
			currentFileStatus.LineCount = countFileLines(fileStatus.FileName)
			files[fileName] = currentFileStatus
			done <- 1
		}(fileName, fileStatus, done)
	}
	for i := 0; i < len(files); i++ {
		<-done
	}
	return
}

func countFileLines(fileName string) int {
	file, err := os.Open(fileName)
	defer file.Close()
	if err != nil {
		panic(fmt.Printf("Error opening file: %s", fileName))
	}

	scanner := bufio.NewScanner(file)
	lineCount := 0

	for scanner.Scan() {
		lineCount++
	}

	return lineCount
}

func syncFileStatus(previousFiles, currentFiles, modifiedFiles, newFiles map[string]FileStatus) map[string]FileStatus {
	// Loop through the current files, and get the FileStatus information from the previous files so we don't have to count the lines again.
	// Only do this for items where the ModTime is the same - otherwise keep the currentFiles copy.
	for fileName, fileStatus := range currentFiles {
		if previousFileStatus, ok := previousFiles[fileName]; ok {
			if fileStatus.ModTime == previousFileStatus.ModTime {
				// Keep the old state - it has the accurate line count!
				currentFiles[fileName] = previousFileStatus
			}
		}
	}

	for fileName, fileStatus := range modifiedFiles {
		currentFiles[fileName] = fileStatus
	}

	for fileName, fileStatus := range newFiles {
		currentFiles[fileName] = fileStatus
	}

	return currentFiles
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"path"
	"path/filepath"
)

func newFileStorage(params *params) storager {
	return &fileStorage{params}
}

type fileStorage struct {
	params *params
}

func (f fileStorage) isExist() bool {
	if _, err := os.Stat(f.pathByParams()); err != nil {
		return false
	}
	return true
}

func (f fileStorage) save(from string) error {
	if err := os.MkdirAll(f.pathByParams(), 0755); err != nil {
		return err
	}
	return os.Rename(from, filepath.Join(f.pathByParams(), path.Base(from)))
}

func (f fileStorage) pathByParams() string {
	return filepath.Join(
		storageDir,
		f.params.remote,
		f.params.owner(),
		f.params.repo,
		f.params.goos,
		f.params.goarch,
		f.params.version,
	)
}

func (f fileStorage) get(file string) (string, error) {
	return filepath.Join(f.pathByParams(), file), nil
}
<commit_msg>Use mv command instead of os.Rename because avoid invalid cross-device link error.<commit_after>package main

import (
	"os"
	"os/exec"
	"path"
	"path/filepath"
)

func newFileStorage(params *params) storager {
	return &fileStorage{params}
}

type fileStorage struct {
	params *params
}

func (f fileStorage) isExist() bool {
	if _, err := os.Stat(f.pathByParams()); err != nil {
		return false
	}
	return true
}

func (f fileStorage) save(from string) error {
	if err := os.MkdirAll(f.pathByParams(), 0755); err != nil {
		return err
	}
	return exec.Command("mv", from, filepath.Join(f.pathByParams(), path.Base(from))).Run()
}

func (f fileStorage) pathByParams() string {
	return filepath.Join(
		storageDir,
		f.params.remote,
		f.params.owner(),
		f.params.repo,
		f.params.goos,
		f.params.goarch,
		f.params.version,
	)
}

func (f fileStorage) get(file string) (string, error) {
	return filepath.Join(f.pathByParams(), file), nil
}
<|endoftext|>


<commit_before>package random

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"math/big"
	"strings"

	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceId() *schema.Resource {
	return &schema.Resource{
		Create: CreateID,
		Read:   RepopulateEncodings,
		Delete: schema.RemoveFromState,
		Importer: &schema.ResourceImporter{
			State: ImportID,
		},

		Schema: map[string]*schema.Schema{
			"keepers": {
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
			},

			"byte_length": {
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: true,
			},

			"prefix": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},

			"b64": {
				Type:       schema.TypeString,
				Computed:   true,
				Deprecated: "Use b64_url for old behavior, or b64_std for standard base64 encoding",
			},

			"b64_url": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"b64_std": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"hex": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"dec": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func CreateID(d *schema.ResourceData, meta interface{}) error {
	byteLength := d.Get("byte_length").(int)
	bytes := make([]byte, byteLength)

	n, err := rand.Reader.Read(bytes)
	if n != byteLength {
		return errors.New("generated insufficient random bytes")
	}
	if err != nil {
		return errwrap.Wrapf("error generating random bytes: {{err}}", err)
	}

	b64Str := base64.RawURLEncoding.EncodeToString(bytes)
	d.SetId(b64Str)

	return RepopulateEncodings(d, meta)
}

func RepopulateEncodings(d *schema.ResourceData, _ interface{}) error {
	prefix := d.Get("prefix").(string)
	base64Str := d.Id()

	bytes, err := base64.RawURLEncoding.DecodeString(base64Str)
	if err != nil {
		return errwrap.Wrapf("Error decoding ID: {{err}}", err)
	}

	b64StdStr := base64.StdEncoding.EncodeToString(bytes)
	hexStr := hex.EncodeToString(bytes)

	bigInt := big.Int{}
	bigInt.SetBytes(bytes)
	decStr := bigInt.String()

	d.Set("b64", prefix+base64Str)
	d.Set("b64_url", prefix+base64Str)
	d.Set("b64_std", prefix+b64StdStr)

	d.Set("hex", prefix+hexStr)
	d.Set("dec", prefix+decStr)

	return nil
}

func ImportID(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
	id := d.Id()

	sep := strings.LastIndex(id, ",")
	if sep != -1 {
		d.Set("prefix", id[:sep])
		id = id[sep+1:]
	}

	bytes, err := base64.RawURLEncoding.DecodeString(id)
	if err != nil {
		return nil, errwrap.Wrapf("Error decoding ID: {{err}}", err)
	}

	d.Set("byte_length", len(bytes))
	d.SetId(id)

	return []*schema.ResourceData{d}, nil
}
<commit_msg>Remove deprecated attribute<commit_after>package random

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"math/big"
	"strings"

	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceId() *schema.Resource {
	return &schema.Resource{
		Create: CreateID,
		Read:   RepopulateEncodings,
		Delete: schema.RemoveFromState,
		Importer: &schema.ResourceImporter{
			State: ImportID,
		},

		Schema: map[string]*schema.Schema{
			"keepers": {
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
			},

			"byte_length": {
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: true,
			},

			"prefix": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},

			"b64_url": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"b64_std": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"hex": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"dec": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func CreateID(d *schema.ResourceData, meta interface{}) error {
	byteLength := d.Get("byte_length").(int)
	bytes := make([]byte, byteLength)

	n, err := rand.Reader.Read(bytes)
	if n != byteLength {
		return errors.New("generated insufficient random bytes")
	}
	if err != nil {
		return errwrap.Wrapf("error generating random bytes: {{err}}", err)
	}

	b64Str := base64.RawURLEncoding.EncodeToString(bytes)
	d.SetId(b64Str)

	return RepopulateEncodings(d, meta)
}

func RepopulateEncodings(d *schema.ResourceData, _ interface{}) error {
	prefix := d.Get("prefix").(string)
	base64Str := d.Id()

	bytes, err := base64.RawURLEncoding.DecodeString(base64Str)
	if err != nil {
		return errwrap.Wrapf("Error decoding ID: {{err}}", err)
	}

	b64StdStr := base64.StdEncoding.EncodeToString(bytes)
	hexStr := hex.EncodeToString(bytes)

	bigInt := big.Int{}
	bigInt.SetBytes(bytes)
	decStr := bigInt.String()

	d.Set("b64_url", prefix+base64Str)
	d.Set("b64_std", prefix+b64StdStr)

	d.Set("hex", prefix+hexStr)
	d.Set("dec", prefix+decStr)

	return nil
}

func ImportID(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
	id := d.Id()

	sep := strings.LastIndex(id, ",")
	if sep != -1 {
		d.Set("prefix", id[:sep])
		id = id[sep+1:]
	}

	bytes, err := base64.RawURLEncoding.DecodeString(id)
	if err != nil {
		return nil, errwrap.Wrapf("Error decoding ID: {{err}}", err)
	}

	d.Set("byte_length", len(bytes))
	d.SetId(id)

	return []*schema.ResourceData{d}, nil
}
<|endoftext|>


<commit_before>package revel

import (
	"reflect"
	"strings"
)

// Map from "Controller" or "Controller.Method" to the Filter chain
var filterOverrides = make(map[string][]Filter)

// FilterConfigurator allows the developer configure the filter chain on a
// per-controller or per-action basis.  The filter configuration is applied by
// the FilterConfiguringFilter, which is itself a filter stage.  For example,
//
// Assuming:
//   Filters = []Filter{
//     RouterFilter,
//     FilterConfiguringFilter,
//     SessionFilter,
//     ActionInvoker,
//   }
//
// Add:
//   FilterAction(App.Action).
//     Add(OtherFilter)
//
//   => RouterFilter, FilterConfiguringFilter, SessionFilter, OtherFilter, ActionInvoker
//
// Remove:
//   FilterAction(App.Action).
//     Remove(SessionFilter)
//
//   => RouterFilter, FilterConfiguringFilter, OtherFilter, ActionInvoker
//
// Insert:
//   FilterAction(App.Action).
//     Insert(OtherFilter, revel.BEFORE, SessionFilter)
//
//   => RouterFilter, FilterConfiguringFilter, OtherFilter, SessionFilter, ActionInvoker
//
// Filter modifications may be combined between Controller and Action.  For example:
//   FilterController(App{}).
//     Add(Filter1)
//   FilterAction(App.Action).
//     Add(Filter2)
//
//  .. would result in App.Action being filtered by both Filter1 and Filter2.
//
// Note: the last filter stage is not subject to the configurator.  In
// particular, Add() adds a filter to the second-to-last place.
type FilterConfigurator struct {
	key            string // e.g. "App", "App.Action"
	controllerName string // e.g. "App"
}

func newFilterConfigurator(controllerName, methodName string) FilterConfigurator {
	if methodName == "" {
		return FilterConfigurator{controllerName, controllerName}
	}
	return FilterConfigurator{controllerName + "." + methodName, controllerName}
}

// FilterController returns a configurator for the filters applied to all
// actions on the given controller instance.  For example:
//   FilterAction(MyController{})
func FilterController(controllerInstance interface{}) FilterConfigurator {
	t := reflect.TypeOf(controllerInstance)
	for t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	return newFilterConfigurator(t.Name(), "")
}

// FilterAction returns a configurator for the filters applied to the given
// controller method. For example:
//   FilterAction(MyController.MyAction)
func FilterAction(methodRef interface{}) FilterConfigurator {
	var (
		methodValue = reflect.ValueOf(methodRef)
		methodType  = methodValue.Type()
	)
	if methodType.Kind() != reflect.Func || methodType.NumIn() == 0 {
		panic("Expecting a controller method reference (e.g. Controller.Action), got a " +
			methodType.String())
	}

	controllerType := methodType.In(0)
	method := FindMethod(controllerType, methodValue)
	if method == nil {
		panic("Action not found on controller " + controllerType.Name())
	}

	for controllerType.Kind() == reflect.Ptr {
		controllerType = controllerType.Elem()
	}

	return newFilterConfigurator(controllerType.Name(), method.Name)
}

// Add the given filter in the second-to-last position in the filter chain.
// (Second-to-last so that it is before ActionInvoker)
func (conf FilterConfigurator) Add(f Filter) FilterConfigurator {
	conf.apply(func(fc []Filter) []Filter {
		return conf.addFilter(f, fc)
	})
	return conf
}

func (conf FilterConfigurator) addFilter(f Filter, fc []Filter) []Filter {
	return append(fc[:len(fc)-1], f, fc[len(fc)-1])
}

// Remove a filter from the filter chain.
func (conf FilterConfigurator) Remove(target Filter) FilterConfigurator {
	conf.apply(func(fc []Filter) []Filter {
		return conf.rmFilter(target, fc)
	})
	return conf
}

func (conf FilterConfigurator) rmFilter(target Filter, fc []Filter) []Filter {
	for i, f := range fc {
		if FilterEq(f, target) {
			return append(fc[:i], fc[i+1:]...)
		}
	}
	return fc
}

// Insert a filter into the filter chain before or after another.
// This may be called with the BEFORE or AFTER constants, for example:
//   revel.FilterAction(App.Index).
//     Insert(MyFilter, revel.BEFORE, revel.ActionInvoker).
//     Insert(MyFilter2, revel.AFTER, revel.PanicFilter)
func (conf FilterConfigurator) Insert(insert Filter, where When, target Filter) FilterConfigurator {
	if where != BEFORE && where != AFTER {
		panic("where must be BEFORE or AFTER")
	}
	conf.apply(func(fc []Filter) []Filter {
		return conf.insertFilter(insert, where, target, fc)
	})
	return conf
}

func (conf FilterConfigurator) insertFilter(insert Filter, where When, target Filter, fc []Filter) []Filter {
	for i, f := range fc {
		if FilterEq(f, target) {
			if where == BEFORE {
				return append(fc[:i], append([]Filter{insert}, fc[i:]...)...)
			} else {
				return append(fc[:i+1], append([]Filter{insert}, fc[i+1:]...)...)
			}
		}
	}
	return fc
}

// getChain returns the filter chain that applies to the given controller or
// action.  If no overrides are configured, then a copy of the default filter
// chain is returned.
func (conf FilterConfigurator) getChain() []Filter {
	var filters []Filter
	if filters = getOverrideChain(conf.controllerName, conf.key); filters == nil {
		// The override starts with all filters after FilterConfiguringFilter
		for i, f := range Filters {
			if FilterEq(f, FilterConfiguringFilter) {
				filters = make([]Filter, len(Filters)-i-1)
				copy(filters, Filters[i+1:])
				break
			}
		}
		if filters == nil {
			panic("FilterConfiguringFilter not found in revel.Filters.")
		}
	}
	return filters
}

// apply applies the given functional change to the filter overrides.
// No other function modifies the filterOverrides map.
func (conf FilterConfigurator) apply(f func([]Filter) []Filter) {
	// Updates any actions that have had their filters overridden, if this is a
	// Controller configurator.
	if conf.controllerName == conf.key {
		for k, v := range filterOverrides {
			if strings.HasPrefix(k, conf.controllerName+".") {
				filterOverrides[k] = f(v)
			}
		}
	}

	// Update the Controller or Action overrides.
	filterOverrides[conf.key] = f(conf.getChain())
}

// FilterEq returns true if the two filters reference the same filter.
func FilterEq(a, b Filter) bool {
	return reflect.ValueOf(a).Pointer() == reflect.ValueOf(b).Pointer()
}

// FilterConfiguringFilter is a filter stage that customizes the remaining
// filter chain for the action being invoked.
func FilterConfiguringFilter(c *Controller, fc []Filter) {
	if newChain := getOverrideChain(c.Name, c.Action); newChain != nil {
		newChain[0](c, newChain[1:])
		return
	}
	fc[0](c, fc[1:])
}

// getOverrideChain retrieves the overrides for the action that is set
func getOverrideChain(controllerName, action string) []Filter {
	if newChain, ok := filterOverrides[action]; ok {
		return newChain
	}
	if newChain, ok := filterOverrides[controllerName]; ok {
		return newChain
	}
	return nil
}
<commit_msg>fix FilterController example<commit_after>package revel

import (
	"reflect"
	"strings"
)

// Map from "Controller" or "Controller.Method" to the Filter chain
var filterOverrides = make(map[string][]Filter)

// FilterConfigurator allows the developer configure the filter chain on a
// per-controller or per-action basis.  The filter configuration is applied by
// the FilterConfiguringFilter, which is itself a filter stage.  For example,
//
// Assuming:
//   Filters = []Filter{
//     RouterFilter,
//     FilterConfiguringFilter,
//     SessionFilter,
//     ActionInvoker,
//   }
//
// Add:
//   FilterAction(App.Action).
//     Add(OtherFilter)
//
//   => RouterFilter, FilterConfiguringFilter, SessionFilter, OtherFilter, ActionInvoker
//
// Remove:
//   FilterAction(App.Action).
//     Remove(SessionFilter)
//
//   => RouterFilter, FilterConfiguringFilter, OtherFilter, ActionInvoker
//
// Insert:
//   FilterAction(App.Action).
//     Insert(OtherFilter, revel.BEFORE, SessionFilter)
//
//   => RouterFilter, FilterConfiguringFilter, OtherFilter, SessionFilter, ActionInvoker
//
// Filter modifications may be combined between Controller and Action.  For example:
//   FilterController(App{}).
//     Add(Filter1)
//   FilterAction(App.Action).
//     Add(Filter2)
//
//  .. would result in App.Action being filtered by both Filter1 and Filter2.
//
// Note: the last filter stage is not subject to the configurator.  In
// particular, Add() adds a filter to the second-to-last place.
type FilterConfigurator struct {
	key            string // e.g. "App", "App.Action"
	controllerName string // e.g. "App"
}

func newFilterConfigurator(controllerName, methodName string) FilterConfigurator {
	if methodName == "" {
		return FilterConfigurator{controllerName, controllerName}
	}
	return FilterConfigurator{controllerName + "." + methodName, controllerName}
}

// FilterController returns a configurator for the filters applied to all
// actions on the given controller instance.  For example:
//   FilterController(MyController{})
func FilterController(controllerInstance interface{}) FilterConfigurator {
	t := reflect.TypeOf(controllerInstance)
	for t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	return newFilterConfigurator(t.Name(), "")
}

// FilterAction returns a configurator for the filters applied to the given
// controller method. For example:
//   FilterAction(MyController.MyAction)
func FilterAction(methodRef interface{}) FilterConfigurator {
	var (
		methodValue = reflect.ValueOf(methodRef)
		methodType  = methodValue.Type()
	)
	if methodType.Kind() != reflect.Func || methodType.NumIn() == 0 {
		panic("Expecting a controller method reference (e.g. Controller.Action), got a " +
			methodType.String())
	}

	controllerType := methodType.In(0)
	method := FindMethod(controllerType, methodValue)
	if method == nil {
		panic("Action not found on controller " + controllerType.Name())
	}

	for controllerType.Kind() == reflect.Ptr {
		controllerType = controllerType.Elem()
	}

	return newFilterConfigurator(controllerType.Name(), method.Name)
}

// Add the given filter in the second-to-last position in the filter chain.
// (Second-to-last so that it is before ActionInvoker)
func (conf FilterConfigurator) Add(f Filter) FilterConfigurator {
	conf.apply(func(fc []Filter) []Filter {
		return conf.addFilter(f, fc)
	})
	return conf
}

func (conf FilterConfigurator) addFilter(f Filter, fc []Filter) []Filter {
	return append(fc[:len(fc)-1], f, fc[len(fc)-1])
}

// Remove a filter from the filter chain.
func (conf FilterConfigurator) Remove(target Filter) FilterConfigurator {
	conf.apply(func(fc []Filter) []Filter {
		return conf.rmFilter(target, fc)
	})
	return conf
}

func (conf FilterConfigurator) rmFilter(target Filter, fc []Filter) []Filter {
	for i, f := range fc {
		if FilterEq(f, target) {
			return append(fc[:i], fc[i+1:]...)
		}
	}
	return fc
}

// Insert a filter into the filter chain before or after another.
// This may be called with the BEFORE or AFTER constants, for example:
//   revel.FilterAction(App.Index).
//     Insert(MyFilter, revel.BEFORE, revel.ActionInvoker).
//     Insert(MyFilter2, revel.AFTER, revel.PanicFilter)
func (conf FilterConfigurator) Insert(insert Filter, where When, target Filter) FilterConfigurator {
	if where != BEFORE && where != AFTER {
		panic("where must be BEFORE or AFTER")
	}
	conf.apply(func(fc []Filter) []Filter {
		return conf.insertFilter(insert, where, target, fc)
	})
	return conf
}

func (conf FilterConfigurator) insertFilter(insert Filter, where When, target Filter, fc []Filter) []Filter {
	for i, f := range fc {
		if FilterEq(f, target) {
			if where == BEFORE {
				return append(fc[:i], append([]Filter{insert}, fc[i:]...)...)
			} else {
				return append(fc[:i+1], append([]Filter{insert}, fc[i+1:]...)...)
			}
		}
	}
	return fc
}

// getChain returns the filter chain that applies to the given controller or
// action.  If no overrides are configured, then a copy of the default filter
// chain is returned.
func (conf FilterConfigurator) getChain() []Filter {
	var filters []Filter
	if filters = getOverrideChain(conf.controllerName, conf.key); filters == nil {
		// The override starts with all filters after FilterConfiguringFilter
		for i, f := range Filters {
			if FilterEq(f, FilterConfiguringFilter) {
				filters = make([]Filter, len(Filters)-i-1)
				copy(filters, Filters[i+1:])
				break
			}
		}
		if filters == nil {
			panic("FilterConfiguringFilter not found in revel.Filters.")
		}
	}
	return filters
}

// apply applies the given functional change to the filter overrides.
// No other function modifies the filterOverrides map.
func (conf FilterConfigurator) apply(f func([]Filter) []Filter) {
	// Updates any actions that have had their filters overridden, if this is a
	// Controller configurator.
	if conf.controllerName == conf.key {
		for k, v := range filterOverrides {
			if strings.HasPrefix(k, conf.controllerName+".") {
				filterOverrides[k] = f(v)
			}
		}
	}

	// Update the Controller or Action overrides.
	filterOverrides[conf.key] = f(conf.getChain())
}

// FilterEq returns true if the two filters reference the same filter.
func FilterEq(a, b Filter) bool {
	return reflect.ValueOf(a).Pointer() == reflect.ValueOf(b).Pointer()
}

// FilterConfiguringFilter is a filter stage that customizes the remaining
// filter chain for the action being invoked.
func FilterConfiguringFilter(c *Controller, fc []Filter) {
	if newChain := getOverrideChain(c.Name, c.Action); newChain != nil {
		newChain[0](c, newChain[1:])
		return
	}
	fc[0](c, fc[1:])
}

// getOverrideChain retrieves the overrides for the action that is set
func getOverrideChain(controllerName, action string) []Filter {
	if newChain, ok := filterOverrides[action]; ok {
		return newChain
	}
	if newChain, ok := filterOverrides[controllerName]; ok {
		return newChain
	}
	return nil
}
<|endoftext|>


<commit_before>package vcsclient

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"path"

	"sort"

	"golang.org/x/tools/godoc/vfs"
	"sourcegraph.com/sourcegraph/go-vcs/vcs"
	"sourcegraph.com/sqs/pbtypes"
)

type FileSystem interface {
	vfs.FileSystem
	Get(path string) (*TreeEntry, error)
}

type repositoryFS struct {
	at   vcs.CommitID
	repo *repository
}

var _ FileSystem = &repositoryFS{}

func (fs *repositoryFS) Open(name string) (vfs.ReadSeekCloser, error) {
	e, err := fs.Get(name)
	if err != nil {
		return nil, err
	}

	return nopCloser{bytes.NewReader(e.Contents)}, nil
}

func (fs *repositoryFS) Lstat(path string) (os.FileInfo, error) {
	e, err := fs.Get(path)
	if err != nil {
		return nil, err
	}

	return e.Stat()
}

func (fs *repositoryFS) Stat(path string) (os.FileInfo, error) {
	// TODO(sqs): follow symlinks (as Stat specification requires)
	e, err := fs.Get(path)
	if err != nil {
		return nil, err
	}

	return e.Stat()
}

func (fs *repositoryFS) ReadDir(path string) ([]os.FileInfo, error) {
	e, err := fs.Get(path)
	if err != nil {
		return nil, err
	}

	fis := make([]os.FileInfo, len(e.Entries))
	for i, e := range e.Entries {
		fis[i], err = e.Stat()
		if err != nil {
			return nil, err
		}
	}

	return fis, nil
}

func (fs *repositoryFS) String() string {
	return fmt.Sprintf("repository %s commit %s (client)", fs.repo.repoPath, fs.at)
}

// Get returns the whole TreeEntry struct for a tree entry.
func (fs *repositoryFS) Get(path string) (*TreeEntry, error) {
	url, err := fs.url(path, nil)
	if err != nil {
		return nil, err
	}

	req, err := fs.repo.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, err
	}

	var entry *TreeEntry
	_, err = fs.repo.client.Do(req, &entry)
	if err != nil {
		return nil, err
	}

	return entry, nil
}

// FileWithRange is returned by GetFileWithOptions and includes the
// returned file's TreeEntry as well as the actual range of lines and
// bytes returned (based on the GetFileOptions parameters). That is,
// if Start/EndLine are set in GetFileOptions, this struct's
// Start/EndByte will be set to the actual start and end bytes of
// those specified lines, and so on for the other fields in
// GetFileOptions.
type FileWithRange struct {
	*TreeEntry
	FileRange // range of actual returned tree entry contents within file
}

// GetFileWithOptions gets a file and allows additional configuration
// of the range to return, etc.
func (fs *repositoryFS) GetFileWithOptions(path string, opt GetFileOptions) (*FileWithRange, error) {
	url, err := fs.url(path, opt)
	if err != nil {
		return nil, err
	}

	req, err := fs.repo.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, err
	}

	var file *FileWithRange
	_, err = fs.repo.client.Do(req, &file)
	if err != nil {
		return nil, err
	}

	return file, nil
}

// A FileGetter is a repository FileSystem that can get files with
// extended range options (GetFileWithOptions).
//
// It's generally more efficient to use the client's implementation of
// the GetFileWithOptions method instead of calling the
// vcsclient.GetFileWithOptions func because the former causes only
// the requested range to be sent over the network, while the latter
// requests the whole file and narrows the range on the client side.
type FileGetter interface {
	GetFileWithOptions(path string, opt GetFileOptions) (*FileWithRange, error)
}

// GetFileWithOptions gets a file and observes the options specified
// in opt. If fs implements FileGetter, fs.GetFileWithOptions is
// called; otherwise the options are applied on the client side after
// fetching the whole file.
func GetFileWithOptions(fs vfs.FileSystem, path string, opt GetFileOptions) (*FileWithRange, error) {
	if fg, ok := fs.(FileGetter); ok {
		return fg.GetFileWithOptions(path, opt)
	}

	fi, err := fs.Lstat(path)
	if err != nil {
		return nil, err
	}

	e := newTreeEntry(fi)
	fwr := FileWithRange{TreeEntry: e}

	if fi.Mode().IsDir() {
		ee, err := readDir(fs, path, int(opt.RecurseSingleSubfolder), true)
		if err != nil {
			return nil, err
		}
		sort.Sort(TreeEntriesByTypeByName(ee))
		e.Entries = ee
	} else if fi.Mode().IsRegular() {
		f, err := fs.Open(path)
		if err != nil {
			return nil, err
		}
		defer f.Close()

		contents, err := ioutil.ReadAll(f)
		if err != nil {
			return nil, err
		}

		e.Contents = contents

		if empty := (GetFileOptions{}); opt != empty {
			fr, _, err := ComputeFileRange(contents, opt)
			if err != nil {
				return nil, err
			}

			// Trim to only requested range.
			e.Contents = e.Contents[fr.StartByte:fr.EndByte]
			fwr.FileRange = *fr
		}
	}

	return &fwr, nil
}

// readDir uses the passed vfs.FileSystem to read from starting at the base path.
// If recurseSingleSubfolder is true, it will descend and include sub-folders
// with a single sub-folder inside. first should always be set to true, other values are used internally.
func readDir(fs vfs.FileSystem, base string, recurseSingleSubfolder int, first bool) ([]*TreeEntry, error) {
	entries, err := fs.ReadDir(base)
	if err != nil {
		return nil, err
	}
	if recurseSingleSubfolder > 0 && !first && !singleSubDir(entries) {
		return nil, nil
	}
	te := make([]*TreeEntry, len(entries))
	for i, fi := range entries {
		te[i] = newTreeEntry(fi)
	}

	if recurseSingleSubfolder > 0 {
		dirEntries := make(map[int]os.FileInfo)
		for i, fi := range entries {
			// expand at most recurseSingleSubfolder subdirectories
			if len(dirEntries) > recurseSingleSubfolder {
				break
			}

			if fi.Mode().IsDir() {
				dirEntries[i] = fi
			}
		}
		for i, fi := range dirEntries {
			ee, err := readDir(fs, path.Join(base, fi.Name()), recurseSingleSubfolder, false)
			if err != nil {
				return nil, err
			}
			te[i].Entries = ee
		}
	}

	return te, nil
}

func singleSubDir(entries []os.FileInfo) bool {
	return len(entries) == 1 && entries[0].IsDir()
}

func newTreeEntry(fi os.FileInfo) *TreeEntry {
	e := &TreeEntry{
		Name:    fi.Name(),
		Size:    fi.Size(),
		ModTime: pbtypes.NewTimestamp(fi.ModTime()),
	}
	if fi.Mode().IsDir() {
		e.Type = DirEntry
	} else if fi.Mode().IsRegular() {
		e.Type = FileEntry
	} else if fi.Mode()&os.ModeSymlink != 0 {
		e.Type = SymlinkEntry
	}
	return e
}

// url generates the URL to RouteRepoTreeEntry for the given path (all other
// route vars are taken from repositoryFS fields).
func (fs *repositoryFS) url(path string, opt interface{}) (*url.URL, error) {
	return fs.repo.url(RouteRepoTreeEntry, map[string]string{
		"CommitID": string(fs.at),
		"Path":     path,
	}, opt)
}

type nopCloser struct {
	io.ReadSeeker
}

func (nc nopCloser) Close() error { return nil }
<commit_msg>Simplify recursionLimit implementation for readDir<commit_after>package vcsclient

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"path"

	"sort"

	"golang.org/x/tools/godoc/vfs"
	"sourcegraph.com/sourcegraph/go-vcs/vcs"
	"sourcegraph.com/sqs/pbtypes"
)

type FileSystem interface {
	vfs.FileSystem
	Get(path string) (*TreeEntry, error)
}

type repositoryFS struct {
	at   vcs.CommitID
	repo *repository
}

var _ FileSystem = &repositoryFS{}

func (fs *repositoryFS) Open(name string) (vfs.ReadSeekCloser, error) {
	e, err := fs.Get(name)
	if err != nil {
		return nil, err
	}

	return nopCloser{bytes.NewReader(e.Contents)}, nil
}

func (fs *repositoryFS) Lstat(path string) (os.FileInfo, error) {
	e, err := fs.Get(path)
	if err != nil {
		return nil, err
	}

	return e.Stat()
}

func (fs *repositoryFS) Stat(path string) (os.FileInfo, error) {
	// TODO(sqs): follow symlinks (as Stat specification requires)
	e, err := fs.Get(path)
	if err != nil {
		return nil, err
	}

	return e.Stat()
}

func (fs *repositoryFS) ReadDir(path string) ([]os.FileInfo, error) {
	e, err := fs.Get(path)
	if err != nil {
		return nil, err
	}

	fis := make([]os.FileInfo, len(e.Entries))
	for i, e := range e.Entries {
		fis[i], err = e.Stat()
		if err != nil {
			return nil, err
		}
	}

	return fis, nil
}

func (fs *repositoryFS) String() string {
	return fmt.Sprintf("repository %s commit %s (client)", fs.repo.repoPath, fs.at)
}

// Get returns the whole TreeEntry struct for a tree entry.
func (fs *repositoryFS) Get(path string) (*TreeEntry, error) {
	url, err := fs.url(path, nil)
	if err != nil {
		return nil, err
	}

	req, err := fs.repo.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, err
	}

	var entry *TreeEntry
	_, err = fs.repo.client.Do(req, &entry)
	if err != nil {
		return nil, err
	}

	return entry, nil
}

// FileWithRange is returned by GetFileWithOptions and includes the
// returned file's TreeEntry as well as the actual range of lines and
// bytes returned (based on the GetFileOptions parameters). That is,
// if Start/EndLine are set in GetFileOptions, this struct's
// Start/EndByte will be set to the actual start and end bytes of
// those specified lines, and so on for the other fields in
// GetFileOptions.
type FileWithRange struct {
	*TreeEntry
	FileRange // range of actual returned tree entry contents within file
}

// GetFileWithOptions gets a file and allows additional configuration
// of the range to return, etc.
func (fs *repositoryFS) GetFileWithOptions(path string, opt GetFileOptions) (*FileWithRange, error) {
	url, err := fs.url(path, opt)
	if err != nil {
		return nil, err
	}

	req, err := fs.repo.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, err
	}

	var file *FileWithRange
	_, err = fs.repo.client.Do(req, &file)
	if err != nil {
		return nil, err
	}

	return file, nil
}

// A FileGetter is a repository FileSystem that can get files with
// extended range options (GetFileWithOptions).
//
// It's generally more efficient to use the client's implementation of
// the GetFileWithOptions method instead of calling the
// vcsclient.GetFileWithOptions func because the former causes only
// the requested range to be sent over the network, while the latter
// requests the whole file and narrows the range on the client side.
type FileGetter interface {
	GetFileWithOptions(path string, opt GetFileOptions) (*FileWithRange, error)
}

// GetFileWithOptions gets a file and observes the options specified
// in opt. If fs implements FileGetter, fs.GetFileWithOptions is
// called; otherwise the options are applied on the client side after
// fetching the whole file.
func GetFileWithOptions(fs vfs.FileSystem, path string, opt GetFileOptions) (*FileWithRange, error) {
	if fg, ok := fs.(FileGetter); ok {
		return fg.GetFileWithOptions(path, opt)
	}

	fi, err := fs.Lstat(path)
	if err != nil {
		return nil, err
	}

	e := newTreeEntry(fi)
	fwr := FileWithRange{TreeEntry: e}

	if fi.Mode().IsDir() {
		ee, err := readDir(fs, path, int(opt.RecurseSingleSubfolder), true)
		if err != nil {
			return nil, err
		}
		sort.Sort(TreeEntriesByTypeByName(ee))
		e.Entries = ee
	} else if fi.Mode().IsRegular() {
		f, err := fs.Open(path)
		if err != nil {
			return nil, err
		}
		defer f.Close()

		contents, err := ioutil.ReadAll(f)
		if err != nil {
			return nil, err
		}

		e.Contents = contents

		if empty := (GetFileOptions{}); opt != empty {
			fr, _, err := ComputeFileRange(contents, opt)
			if err != nil {
				return nil, err
			}

			// Trim to only requested range.
			e.Contents = e.Contents[fr.StartByte:fr.EndByte]
			fwr.FileRange = *fr
		}
	}

	return &fwr, nil
}

// readDir uses the passed vfs.FileSystem to read from starting at the base path.
// If recurseSingleSubfolder is true, it will descend and include sub-folders
// with a single sub-folder inside. first should always be set to true, other values are used internally.
func readDir(fs vfs.FileSystem, base string, recurseSingleSubfolder int, first bool) ([]*TreeEntry, error) {
	entries, err := fs.ReadDir(base)
	if err != nil {
		return nil, err
	}
	if recurseSingleSubfolder > 0 && !first && !singleSubDir(entries) {
		return nil, nil
	}
	te := make([]*TreeEntry, len(entries))
	dirCount := 0
	for i, fi := range entries {
		te[i] = newTreeEntry(fi)
		if fi.Mode().IsDir() && dirCount < recurseSingleSubfolder {
			dirCount++
			ee, err := readDir(fs, path.Join(base, fi.Name()), recurseSingleSubfolder, false)
			if err != nil {
				return nil, err
			}
			te[i].Entries = ee
		}
	}
	return te, nil
}

func singleSubDir(entries []os.FileInfo) bool {
	return len(entries) == 1 && entries[0].IsDir()
}

func newTreeEntry(fi os.FileInfo) *TreeEntry {
	e := &TreeEntry{
		Name:    fi.Name(),
		Size:    fi.Size(),
		ModTime: pbtypes.NewTimestamp(fi.ModTime()),
	}
	if fi.Mode().IsDir() {
		e.Type = DirEntry
	} else if fi.Mode().IsRegular() {
		e.Type = FileEntry
	} else if fi.Mode()&os.ModeSymlink != 0 {
		e.Type = SymlinkEntry
	}
	return e
}

// url generates the URL to RouteRepoTreeEntry for the given path (all other
// route vars are taken from repositoryFS fields).
func (fs *repositoryFS) url(path string, opt interface{}) (*url.URL, error) {
	return fs.repo.url(RouteRepoTreeEntry, map[string]string{
		"CommitID": string(fs.at),
		"Path":     path,
	}, opt)
}

type nopCloser struct {
	io.ReadSeeker
}

func (nc nopCloser) Close() error { return nil }
<|endoftext|>


<commit_before>package mackerel

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

/*
`/dashboards` Response
Legacy
{
  "dashboards": [
	{
	  "id": "2c5bLca8d",
	  "title": "My Dashboard",
	  "bodyMarkdown": "# A test dashboard",
	  "urlPath": "2u4PP3TJqbu",
	  "createdAt": 1439346145003,
	  "updatedAt": 1439346145003,
	  "isLegacy": true
	}
  ]
}

Current
{
	"dashboards": [
		{
			"id":        "2c5bLca8e",
			"title":     "My Custom Dashboard(Current)",
			"urlPath":   "2u4PP3TJqbv",
			"createdAt": 1552909732,
			"updatedAt": 1552992837,
			"memo":      "A test Current Dashboard"
		}
	]
}
*/

/*
`/dashboards/${ID}` Response`
Legacy
{
	"id": "2c5bLca8d",
	"title": "My Dashboard",
	"bodyMarkdown": "# A test dashboard",
	"urlPath": "2u4PP3TJqbu",
	"createdAt": 1439346145003,
	"updatedAt": 1439346145003,
	"isLegacy": true
}
Current
{
  "id": "2c5bLca8e",
  "createdAt": 1552909732,
  "updatedAt": 1552992837,
  "title": "My Custom Dashboard(Current),
  "urlPath": "2u4PP3TJqbv",
  "memo": "A test Current Dashboard",
  "widgets": [
    {
      "type": "markdown",
      "title": "markdown",
      "markdown": "# body",
      "layout": {
        "x": 0,
        "y": 0,
        "width": 24,
        "height": 3
      }
    },
    {
      "type": "graph",
      "title": "graph",
      "graph": {
        "type": "host",
        "hostId": "2u4PP3TJqbw",
        "name": "loadavg.loadavg15"
      },
      "layout": {
        "x": 0,
        "y": 7,
        "width": 8,
        "height": 10
      }
    },
    {
      "type": "value",
      "title": "value",
      "metric": {
        "type": "expression",
        "expression": "alias(scale(\nsum(\n  group(\n    host(2u4PP3TJqbx,loadavg.*)\n  )\n),\n1\n), 'test')"
      },
      "layout": {
        "x": 0,
        "y": 17,
        "width": 8,
        "height": 5
      }
    }
  ]
}
*/

// Dashboard information
type Dashboard struct {
	ID           string   `json:"id,omitempty"`
	Title        string   `json:"title,omitempty"`
	BodyMarkDown string   `json:"bodyMarkdown,omitempty"`
	URLPath      string   `json:"urlPath,omitempty"`
	CreatedAt    int64    `json:"createdAt,omitempty"`
	UpdatedAt    int64    `json:"updatedAt,omitempty"`
	IsLegacy     bool     `json:"isLegacy,omitempty"`
	Memo         string   `json:"memo,omitempty"`
	Widgets      []Widget `json:"widgets,omitenpty"`
}

// Widget information
type Widget struct {
	Type     string `json:"type,omitempty"`
	Title    string `json:"title,omitempty"`
	Metric   Metric `json:"metric,omitempty"`
	Graph    Graph  `json:"graph,omitempty"`
	Layout   Layout `json:"layout,omitempty"`
	Markdown string `json:"markdown,omitempty"`
	Range    Range  `json:"range,omitempty"`
}

// Metric information
type Metric struct {
	Type        string `json:"type,omitempty"`
	HostID      string `json:"hostId,omitempty"`
	Name        string `json:"name,omitempty"`
	ServiceName string `json:"seriviceName,omitempty"`
	Expression  string `json:"expression,omitempty"`
}

// Graph information
type Graph struct {
	Type         string `json:"type,omitempty"`
	HostID       string `json:"hostId,omitempty"`
	Name         string `json:"name,omitempty"`
	RoleFullName string `json:"roleFullname,omitempty"`
	ServiceName  string `json:"seriviceName,omitempty"`
	Expression   string `json:"expression,omitempty"`
	IsStacked    bool   `json:"isStacked,omitempty"`
}

// Range information
type Range struct {
	Type   string `json:"type,omitempty"`
	Period int64  `json:"period,omitempty"`
	Offset int64  `json:"offset,omitempty"`
	Start  int64  `json:"start,omitempty"`
	End    int64  `json:"end,omitempty"`
}

// Layout information
type Layout struct {
	X      int64 `json:"x,omitempty"`
	Y      int64 `json:"y,omitempty"`
	Width  int64 `json:"width,omitempty"`
	Height int64 `json:"height,omitempty"`
}

// FindDashboards find dashboards
func (c *Client) FindDashboards() ([]*Dashboard, error) {
	req, err := http.NewRequest("GET", c.urlFor("/api/v0/dashboards").String(), nil)
	if err != nil {
		return nil, err
	}
	resp, err := c.Request(req)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data struct {
		Dashboards []*Dashboard `json:"dashboards"`
	}
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}

	return data.Dashboards, err
}

// FindDashboard find dashboard
func (c *Client) FindDashboard(dashboardID string) (*Dashboard, error) {
	req, err := http.NewRequest("GET", c.urlFor(fmt.Sprintf("/api/v0/dashboards/%s", dashboardID)).String(), nil)
	if err != nil {
		return nil, err
	}
	resp, err := c.Request(req)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data Dashboard
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}
	return &data, err
}

// CreateDashboard creating dashboard
func (c *Client) CreateDashboard(param *Dashboard) (*Dashboard, error) {
	resp, err := c.PostJSON("/api/v0/dashboards", param)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data Dashboard
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}
	return &data, nil
}

// UpdateDashboard update dashboard
func (c *Client) UpdateDashboard(dashboardID string, param *Dashboard) (*Dashboard, error) {
	resp, err := c.PutJSON(fmt.Sprintf("/api/v0/dashboards/%s", dashboardID), param)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data Dashboard
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}
	return &data, nil
}

// DeleteDashboard delete dashboard
func (c *Client) DeleteDashboard(dashboardID string) (*Dashboard, error) {
	req, err := http.NewRequest(
		"DELETE",
		c.urlFor(fmt.Sprintf("/api/v0/dashboards/%s", dashboardID)).String(),
		nil,
	)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", "application/json")

	resp, err := c.Request(req)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data Dashboard
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}
	return &data, nil
}
<commit_msg>fix typo<commit_after>package mackerel

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

/*
`/dashboards` Response
Legacy
{
  "dashboards": [
	{
	  "id": "2c5bLca8d",
	  "title": "My Dashboard",
	  "bodyMarkdown": "# A test dashboard",
	  "urlPath": "2u4PP3TJqbu",
	  "createdAt": 1439346145003,
	  "updatedAt": 1439346145003,
	  "isLegacy": true
	}
  ]
}

Current
{
	"dashboards": [
		{
			"id":        "2c5bLca8e",
			"title":     "My Custom Dashboard(Current)",
			"urlPath":   "2u4PP3TJqbv",
			"createdAt": 1552909732,
			"updatedAt": 1552992837,
			"memo":      "A test Current Dashboard"
		}
	]
}
*/

/*
`/dashboards/${ID}` Response`
Legacy
{
	"id": "2c5bLca8d",
	"title": "My Dashboard",
	"bodyMarkdown": "# A test dashboard",
	"urlPath": "2u4PP3TJqbu",
	"createdAt": 1439346145003,
	"updatedAt": 1439346145003,
	"isLegacy": true
}
Current
{
  "id": "2c5bLca8e",
  "createdAt": 1552909732,
  "updatedAt": 1552992837,
  "title": "My Custom Dashboard(Current),
  "urlPath": "2u4PP3TJqbv",
  "memo": "A test Current Dashboard",
  "widgets": [
    {
      "type": "markdown",
      "title": "markdown",
      "markdown": "# body",
      "layout": {
        "x": 0,
        "y": 0,
        "width": 24,
        "height": 3
      }
    },
    {
      "type": "graph",
      "title": "graph",
      "graph": {
        "type": "host",
        "hostId": "2u4PP3TJqbw",
        "name": "loadavg.loadavg15"
      },
      "layout": {
        "x": 0,
        "y": 7,
        "width": 8,
        "height": 10
      }
    },
    {
      "type": "value",
      "title": "value",
      "metric": {
        "type": "expression",
        "expression": "alias(scale(\nsum(\n  group(\n    host(2u4PP3TJqbx,loadavg.*)\n  )\n),\n1\n), 'test')"
      },
      "layout": {
        "x": 0,
        "y": 17,
        "width": 8,
        "height": 5
      }
    }
  ]
}
*/

// Dashboard information
type Dashboard struct {
	ID           string   `json:"id,omitempty"`
	Title        string   `json:"title,omitempty"`
	BodyMarkDown string   `json:"bodyMarkdown,omitempty"`
	URLPath      string   `json:"urlPath,omitempty"`
	CreatedAt    int64    `json:"createdAt,omitempty"`
	UpdatedAt    int64    `json:"updatedAt,omitempty"`
	IsLegacy     bool     `json:"isLegacy,omitempty"`
	Memo         string   `json:"memo,omitempty"`
	Widgets      []Widget `json:"widgets,omitenpty"`
}

// Widget information
type Widget struct {
	Type     string `json:"type,omitempty"`
	Title    string `json:"title,omitempty"`
	Metric   Metric `json:"metric,omitempty"`
	Graph    Graph  `json:"graph,omitempty"`
	Layout   Layout `json:"layout,omitempty"`
	Markdown string `json:"markdown,omitempty"`
	Range    Range  `json:"range,omitempty"`
}

// Metric information
type Metric struct {
	Type        string `json:"type,omitempty"`
	HostID      string `json:"hostId,omitempty"`
	Name        string `json:"name,omitempty"`
	ServiceName string `json:"serviceName,omitempty"`
	Expression  string `json:"expression,omitempty"`
}

// Graph information
type Graph struct {
	Type         string `json:"type,omitempty"`
	HostID       string `json:"hostId,omitempty"`
	Name         string `json:"name,omitempty"`
	RoleFullName string `json:"roleFullname,omitempty"`
	ServiceName  string `json:"seriviceName,omitempty"`
	Expression   string `json:"expression,omitempty"`
	IsStacked    bool   `json:"isStacked,omitempty"`
}

// Range information
type Range struct {
	Type   string `json:"type,omitempty"`
	Period int64  `json:"period,omitempty"`
	Offset int64  `json:"offset,omitempty"`
	Start  int64  `json:"start,omitempty"`
	End    int64  `json:"end,omitempty"`
}

// Layout information
type Layout struct {
	X      int64 `json:"x,omitempty"`
	Y      int64 `json:"y,omitempty"`
	Width  int64 `json:"width,omitempty"`
	Height int64 `json:"height,omitempty"`
}

// FindDashboards find dashboards
func (c *Client) FindDashboards() ([]*Dashboard, error) {
	req, err := http.NewRequest("GET", c.urlFor("/api/v0/dashboards").String(), nil)
	if err != nil {
		return nil, err
	}
	resp, err := c.Request(req)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data struct {
		Dashboards []*Dashboard `json:"dashboards"`
	}
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}

	return data.Dashboards, err
}

// FindDashboard find dashboard
func (c *Client) FindDashboard(dashboardID string) (*Dashboard, error) {
	req, err := http.NewRequest("GET", c.urlFor(fmt.Sprintf("/api/v0/dashboards/%s", dashboardID)).String(), nil)
	if err != nil {
		return nil, err
	}
	resp, err := c.Request(req)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data Dashboard
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}
	return &data, err
}

// CreateDashboard creating dashboard
func (c *Client) CreateDashboard(param *Dashboard) (*Dashboard, error) {
	resp, err := c.PostJSON("/api/v0/dashboards", param)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data Dashboard
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}
	return &data, nil
}

// UpdateDashboard update dashboard
func (c *Client) UpdateDashboard(dashboardID string, param *Dashboard) (*Dashboard, error) {
	resp, err := c.PutJSON(fmt.Sprintf("/api/v0/dashboards/%s", dashboardID), param)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data Dashboard
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}
	return &data, nil
}

// DeleteDashboard delete dashboard
func (c *Client) DeleteDashboard(dashboardID string) (*Dashboard, error) {
	req, err := http.NewRequest(
		"DELETE",
		c.urlFor(fmt.Sprintf("/api/v0/dashboards/%s", dashboardID)).String(),
		nil,
	)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", "application/json")

	resp, err := c.Request(req)
	defer closeResponse(resp)
	if err != nil {
		return nil, err
	}

	var data Dashboard
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}
	return &data, nil
}
<|endoftext|>


<commit_before>package easyssh

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestGetKeyFile(t *testing.T) {
	// missing file
	_, err := getKeyFile("abc")
	assert.Error(t, err)
	assert.Equal(t, "open abc: no such file or directory", err.Error())

	// wrong format
	_, err = getKeyFile("../tests/.ssh/id_rsa.pub")
	assert.Error(t, err)
	assert.Equal(t, "ssh: no key found", err.Error())

	_, err = getKeyFile("../tests/.ssh/id_rsa")
	assert.NoError(t, err)
}
<commit_msg>test: add easyssh scp and ssh command testing. (#29)<commit_after>package easyssh

import (
	"os"
	"os/user"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestGetKeyFile(t *testing.T) {
	// missing file
	_, err := getKeyFile("abc")
	assert.Error(t, err)
	assert.Equal(t, "open abc: no such file or directory", err.Error())

	// wrong format
	_, err = getKeyFile("../tests/.ssh/id_rsa.pub")
	assert.Error(t, err)
	assert.Equal(t, "ssh: no key found", err.Error())

	_, err = getKeyFile("../tests/.ssh/id_rsa")
	assert.NoError(t, err)
}

func TestRunCommand(t *testing.T) {
	ssh := &MakeConfig{
		Server:  "localhost",
		User:    "drone-scp",
		Port:    "22",
		KeyPath: "../tests/.ssh/id_rsa",
	}

	output, err := ssh.Run("whoami")
	assert.Equal(t, "drone-scp\n", output)
	assert.NoError(t, err)
}

func TestSCPCommand(t *testing.T) {
	ssh := &MakeConfig{
		Server:  "localhost",
		User:    "drone-scp",
		Port:    "22",
		KeyPath: "../tests/.ssh/id_rsa",
	}

	err := ssh.Scp("../tests/a.txt")
	assert.NoError(t, err)

	u, err := user.Lookup("drone-scp")
	if err != nil {
		t.Fatalf("Lookup: %v", err)
	}

	// check file exist
	if _, err := os.Stat(u.HomeDir + "/a.txt"); os.IsNotExist(err) {
		t.Fatalf("SCP-error: %v", err)
	}
}
<|endoftext|>


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

package reader

import (
	"math"
	"sort"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestListMetrics(t *testing.T) {
	reader := NewReader("testdata")
	metrics, err := reader.Metrics()
	require.NoError(t, err)

	sort.Strings(metrics)
	expected := []string{
		"test-whisper.load.load.longterm",
		"test-whisper.load.load.shortterm",
		"test-whisper.load.load.midterm",
	}

	sort.Strings(expected)

	require.Equal(t, expected, metrics)
}

func TestGetMinAndMaxTimestamp(t *testing.T) {
	reader := NewReader("testdata")
	min, max, err := reader.GetMinAndMaxTimestamps()
	require.NoError(t, err)

	require.True(t, min > math.MinInt64)
	require.Equal(t, int64(1611068400000), max)
}

func TestGetPoints(t *testing.T) {
	reader := NewReader("testdata")
	points, err := reader.Points("test-whisper.load.load.longterm", 1000*math.MinInt32, 1000*math.MaxInt32)
	require.NoError(t, err)

	expectedLastPoints := []Point{
		{
			Timestamp: 1611067800000,
			Value:     1.0511666666666666,
		},
		{
			Timestamp: 1611068400000,
			Value:     1.0636666666666668,
		},
	}

	for i, p := range expectedLastPoints {
		pos := len(points) - len(expectedLastPoints) + i
		require.Equal(t, p, points[pos])
	}
}
<commit_msg>Fix Whisper reader tests<commit_after>// Copyright 2021 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package reader

import (
	"math"
	"sort"
	"testing"
	"time"

	"github.com/go-graphite/go-whisper"
	"github.com/stretchr/testify/require"
)

func init() {
	// Pin go-whisper to a fixed timestamp so that the test data is in the window of retention.
	whisper.Now = func() time.Time { return time.Unix(1640000000, 0) }
}

func TestListMetrics(t *testing.T) {
	reader := NewReader("testdata")
	metrics, err := reader.Metrics()
	require.NoError(t, err)

	sort.Strings(metrics)
	expected := []string{
		"test-whisper.load.load.longterm",
		"test-whisper.load.load.shortterm",
		"test-whisper.load.load.midterm",
	}

	sort.Strings(expected)

	require.Equal(t, expected, metrics)
}

func TestGetMinAndMaxTimestamp(t *testing.T) {
	reader := NewReader("testdata")
	min, max, err := reader.GetMinAndMaxTimestamps()
	require.NoError(t, err)

	require.True(t, min > math.MinInt64)
	require.Equal(t, int64(1611068400000), max)
}

func TestGetPoints(t *testing.T) {
	reader := NewReader("testdata")
	points, err := reader.Points("test-whisper.load.load.longterm", 1000*math.MinInt32, 1000*math.MaxInt32)
	require.NoError(t, err)

	expectedLastPoints := []Point{
		{
			Timestamp: 1611067800000,
			Value:     1.0511666666666666,
		},
		{
			Timestamp: 1611068400000,
			Value:     1.0636666666666668,
		},
	}

	for i, p := range expectedLastPoints {
		pos := len(points) - len(expectedLastPoints) + i
		require.Equal(t, p, points[pos])
	}
}
<|endoftext|>


<commit_before>package platform // import "v2ray.com/core/common/platform"

import (
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

type EnvFlag struct {
	Name    string
	AltName string
}

func NewEnvFlag(name string) EnvFlag {
	return EnvFlag{
		Name:    name,
		AltName: NormalizeEnvName(name),
	}
}

func (f EnvFlag) GetValue(defaultValue func() string) string {
	if v, found := os.LookupEnv(f.Name); found {
		return v
	}
	if len(f.AltName) > 0 {
		if v, found := os.LookupEnv(f.AltName); found {
			return v
		}
	}

	return defaultValue()
}

func (f EnvFlag) GetValueAsInt(defaultValue int) int {
	useDefaultValue := false
	s := f.GetValue(func() string {
		useDefaultValue = true
		return ""
	})
	if useDefaultValue {
		return defaultValue
	}
	v, err := strconv.ParseInt(s, 10, 32)
	if err != nil {
		return defaultValue
	}
	return int(v)
}

func NormalizeEnvName(name string) string {
	return strings.Replace(strings.ToUpper(strings.TrimSpace(name)), ".", "_", -1)
}

func getExecutableDir() string {
	exec, err := os.Executable()
	if err != nil {
		return ""
	}
	return filepath.Dir(exec)
}

func getExecutableSubDir(dir string) func() string {
	return func() string {
		return filepath.Join(getExecutableDir(), dir)
	}
}

func GetAssetLocation(file string) string {
	const name = "v2ray.location.asset"
	assetPath := NewEnvFlag(name).GetValue(getExecutableDir)
	return filepath.Join(assetPath, file)
}

func GetPluginDirectory() string {
	const name = "v2ray.location.plugin"
	pluginDir := NewEnvFlag(name).GetValue(getExecutableSubDir("plugins"))
	return pluginDir
}

func GetConfigurationPath() string {
	const name = "v2ray.location.config"
	configPath := NewEnvFlag(name).GetValue(getExecutableDir)
	return filepath.Join(configPath, "config.json")
}

func GetConfDirPath() string {
	const name = "v2ray.location.confdir"
	configPath := NewEnvFlag(name).GetValue(func() string { return "" })
	return configPath
}
<commit_msg>add comment to please codacy<commit_after>package platform // import "v2ray.com/core/common/platform"

import (
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

type EnvFlag struct {
	Name    string
	AltName string
}

func NewEnvFlag(name string) EnvFlag {
	return EnvFlag{
		Name:    name,
		AltName: NormalizeEnvName(name),
	}
}

func (f EnvFlag) GetValue(defaultValue func() string) string {
	if v, found := os.LookupEnv(f.Name); found {
		return v
	}
	if len(f.AltName) > 0 {
		if v, found := os.LookupEnv(f.AltName); found {
			return v
		}
	}

	return defaultValue()
}

func (f EnvFlag) GetValueAsInt(defaultValue int) int {
	useDefaultValue := false
	s := f.GetValue(func() string {
		useDefaultValue = true
		return ""
	})
	if useDefaultValue {
		return defaultValue
	}
	v, err := strconv.ParseInt(s, 10, 32)
	if err != nil {
		return defaultValue
	}
	return int(v)
}

func NormalizeEnvName(name string) string {
	return strings.Replace(strings.ToUpper(strings.TrimSpace(name)), ".", "_", -1)
}

func getExecutableDir() string {
	exec, err := os.Executable()
	if err != nil {
		return ""
	}
	return filepath.Dir(exec)
}

func getExecutableSubDir(dir string) func() string {
	return func() string {
		return filepath.Join(getExecutableDir(), dir)
	}
}

func GetAssetLocation(file string) string {
	const name = "v2ray.location.asset"
	assetPath := NewEnvFlag(name).GetValue(getExecutableDir)
	return filepath.Join(assetPath, file)
}

func GetPluginDirectory() string {
	const name = "v2ray.location.plugin"
	pluginDir := NewEnvFlag(name).GetValue(getExecutableSubDir("plugins"))
	return pluginDir
}

func GetConfigurationPath() string {
	const name = "v2ray.location.config"
	configPath := NewEnvFlag(name).GetValue(getExecutableDir)
	return filepath.Join(configPath, "config.json")
}

// GetConfDirPath reads "v2ray.location.confdir"
func GetConfDirPath() string {
	const name = "v2ray.location.confdir"
	configPath := NewEnvFlag(name).GetValue(func() string { return "" })
	return configPath
}
<|endoftext|>


<commit_before>package context

import (
	"encoding/json"
	"fmt"
	"log"
	"strings"
)

var WechatTypeEnum = map[string]int{
	"Private": 1,
	"Group":   2,
	"GroupAt": 3,
}

func PrivateChat(ctx Context) bool {
	t := wechatType(ctx)

	return t == 1 || t == 3
}

func GroupChat(ctx Context) bool {
	t := wechatType(ctx)

	return t == 2
}

// {"BotName":"客服",
// "Content":"这个新版的微信, 容易挂掉",
// "MessageId":"859f8ba8-4ff7-44ae-8d13-1ed3deedd6a9",
// "MsgID":"5571466961726285032",
// "MsgType":"1",
// "ReceiptHandle":"xxx",
// "RoomID":"@Room\u003c测试群\u003e",
// "RoomName":"测试群",
// "SendId":"@f048968daa9e01bb4fbf98067a21ec32",
// "SenderNickName":"胡义",
// "TaskId":"528219641",
// "WeixinUin":"528219641"}
func wechatType(ctx Context) int {
	// log.Printf("[WECHAT GROUP] BEGIN")
	data, _ := json.Marshal(ctx.Value("WECHAT_INFO"))
	log.Printf("[WECHAT GROUP] ctx value: %v", string(data))
	// log.Printf("[WECHAT GROUP] END")

	wechatInfo := ctx.Value("WECHAT_INFO")

	if wechatInfo == nil {
		return WechatTypeEnum["Private"]
	} else {
		message := wechatInfo.(map[string]string)

		rooId, ok := message["RoomID"]
		if ok && rooId != "" {
			if bootName, ok := message["BotName"]; ok {
				content, _ := message["Content"]
				if strings.Contains(content, fmt.Sprintf("@%v", bootName)) {
					return WechatTypeEnum["GroupAt"]
				}
			}

			return WechatTypeEnum["Group"]
		} else {
			return WechatTypeEnum["Private"]
		}
	}

	return 0
}
<commit_msg>remove debug output<commit_after>package context

import (
	// "encoding/json"
	"fmt"
	// "log"
	"strings"
)

var WechatTypeEnum = map[string]int{
	"Private": 1,
	"Group":   2,
	"GroupAt": 3,
}

func PrivateChat(ctx Context) bool {
	t := wechatType(ctx)

	return t == 1 || t == 3
}

func GroupChat(ctx Context) bool {
	t := wechatType(ctx)

	return t == 2
}

// {"BotName":"客服",
// "Content":"这个新版的微信, 容易挂掉",
// "MessageId":"859f8ba8-4ff7-44ae-8d13-1ed3deedd6a9",
// "MsgID":"5571466961726285032",
// "MsgType":"1",
// "ReceiptHandle":"xxx",
// "RoomID":"@Room\u003c测试群\u003e",
// "RoomName":"测试群",
// "SendId":"@f048968daa9e01bb4fbf98067a21ec32",
// "SenderNickName":"胡义",
// "TaskId":"528219641",
// "WeixinUin":"528219641"}
func wechatType(ctx Context) int {
	// log.Printf("[WECHAT GROUP] BEGIN")
	// data, _ := json.Marshal(ctx.Value("WECHAT_INFO"))
	// log.Printf("[WECHAT GROUP] ctx value: %v", string(data))
	// log.Printf("[WECHAT GROUP] END")

	wechatInfo := ctx.Value("WECHAT_INFO")

	if wechatInfo == nil {
		return WechatTypeEnum["Private"]
	} else {
		message := wechatInfo.(map[string]string)

		rooId, ok := message["RoomID"]
		if ok && rooId != "" {
			if bootName, ok := message["BotName"]; ok {
				content, _ := message["Content"]
				if strings.Contains(content, fmt.Sprintf("@%v", bootName)) {
					return WechatTypeEnum["GroupAt"]
				}
			}

			return WechatTypeEnum["Group"]
		} else {
			return WechatTypeEnum["Private"]
		}
	}

	return 0
}
<|endoftext|>