<commit_before>package namesys

import (
	"bytes"
	"errors"
	"fmt"
	"time"

	proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
	ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore"
	context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"

	key "github.com/ipfs/go-ipfs/blocks/key"
	dag "github.com/ipfs/go-ipfs/merkledag"
	pb "github.com/ipfs/go-ipfs/namesys/pb"
	path "github.com/ipfs/go-ipfs/path"
	pin "github.com/ipfs/go-ipfs/pin"
	routing "github.com/ipfs/go-ipfs/routing"
	dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb"
	record "github.com/ipfs/go-ipfs/routing/record"
	ft "github.com/ipfs/go-ipfs/unixfs"
	u "github.com/ipfs/go-ipfs/util"
	ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto"
	peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer"
)

// ErrExpiredRecord should be returned when an ipns record is
// invalid due to being too old
var ErrExpiredRecord = errors.New("expired record")

// ErrUnrecognizedValidity is returned when an IpnsRecord has an
// unknown validity type.
var ErrUnrecognizedValidity = errors.New("unrecognized validity type")

var PublishPutValTimeout = time.Minute

// ipnsPublisher is capable of publishing and resolving names to the IPFS
// routing system.
type ipnsPublisher struct {
	routing routing.IpfsRouting
	ds      ds.Datastore
}

// NewRoutingPublisher constructs a publisher for the IPFS Routing name system.
func NewRoutingPublisher(route routing.IpfsRouting, ds ds.Datastore) *ipnsPublisher {
	if ds == nil {
		panic("nil datastore")
	}
	return &ipnsPublisher{routing: route, ds: ds}
}

// Publish implements Publisher. Accepts a keypair and a value,
// and publishes it out to the routing system
func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error {
	log.Debugf("Publish %s", value)
	return p.PublishWithEOL(ctx, k, value, time.Now().Add(time.Hour*24))
}

// PublishWithEOL is a temporary stand in for the ipns records implementation
// see here for more details: https://github.com/ipfs/specs/tree/master/records
func (p *ipnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error {

	id, err := peer.IDFromPrivateKey(k)
	if err != nil {
		return err
	}

	_, ipnskey := IpnsKeysForID(id)

	// get previous records sequence number
	seqnum, err := p.getPreviousSeqNo(ctx, ipnskey)
	if err != nil {
		return err
	}

	// increment it
	seqnum++

	return PutRecordToRouting(ctx, k, value, seqnum, eol, p.routing, id)
}

func (p *ipnsPublisher) getPreviousSeqNo(ctx context.Context, ipnskey key.Key) (uint64, error) {
	prevrec, err := p.ds.Get(ipnskey.DsKey())
	if err != nil && err != ds.ErrNotFound {
		// None found, lets start at zero!
		return 0, err
	}
	var val []byte
	if err == nil {
		prbytes, ok := prevrec.([]byte)
		if !ok {
			return 0, fmt.Errorf("unexpected type returned from datastore: %#v", prevrec)
		}
		dhtrec := new(dhtpb.Record)
		err := proto.Unmarshal(prbytes, dhtrec)
		if err != nil {
			return 0, err
		}

		val = dhtrec.GetValue()
	} else {
		// try and check the dht for a record
		ctx, cancel := context.WithTimeout(ctx, time.Second*30)
		defer cancel()

		rv, err := p.routing.GetValue(ctx, ipnskey)
		if err != nil {
			// no such record found, start at zero!
			return 0, nil
		}

		val = rv
	}

	e := new(pb.IpnsEntry)
	err = proto.Unmarshal(val, e)
	if err != nil {
		return 0, err
	}

	return e.GetSequence(), nil
}

// setting the TTL on published records is an experimental feature.
// as such, i'm using the context to wire it through to avoid changing too
// much code along the way.
func checkCtxTTL(ctx context.Context) (time.Duration, bool) {
	v := ctx.Value("ipns-publish-ttl")
	if v == nil {
		return 0, false
	}

	d, ok := v.(time.Duration)
	return d, ok
}

func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqnum uint64, eol time.Time, r routing.IpfsRouting, id peer.ID) error {
	namekey, ipnskey := IpnsKeysForID(id)
	entry, err := CreateRoutingEntryData(k, value, seqnum, eol)
	if err != nil {
		return err
	}

	ttl, ok := checkCtxTTL(ctx)
	if ok {
		entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds()))
	}

	errs := make(chan error)

	go func() {
		errs <- PublishEntry(ctx, r, ipnskey, entry)
	}()

	go func() {
		errs <- PublishPublicKey(ctx, r, namekey, k.GetPublic())
	}()

	err = waitOnErrChan(ctx, errs)
	if err != nil {
		return err
	}

	err = waitOnErrChan(ctx, errs)
	if err != nil {
		return err
	}

	return nil
}

func waitOnErrChan(ctx context.Context, errs chan error) error {
	select {
	case err := <-errs:
		if err != nil {
			return err
		}
	case <-ctx.Done():
		return ctx.Err()
	}

	return nil
}

func PublishPublicKey(ctx context.Context, r routing.IpfsRouting, k key.Key, pubk ci.PubKey) error {
	log.Debugf("Storing pubkey at: %s", k)
	pkbytes, err := pubk.Bytes()
	if err != nil {
		return err
	}

	// Store associated public key
	timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout)
	defer cancel()
	err = r.PutValue(timectx, k, pkbytes)
	if err != nil {
		return err
	}

	return nil
}

func PublishEntry(ctx context.Context, r routing.IpfsRouting, ipnskey key.Key, rec *pb.IpnsEntry) error {
	timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout)
	defer cancel()

	data, err := proto.Marshal(rec)
	if err != nil {
		return err
	}

	log.Debugf("Storing ipns entry at: %s", ipnskey)
	// Store ipns entry at "/ipns/"+b58(h(pubkey))
	if err := r.PutValue(timectx, ipnskey, data); err != nil {
		return err
	}

	return nil
}

func CreateRoutingEntryData(pk ci.PrivKey, val path.Path, seq uint64, eol time.Time) (*pb.IpnsEntry, error) {
	entry := new(pb.IpnsEntry)

	entry.Value = []byte(val)
	typ := pb.IpnsEntry_EOL
	entry.ValidityType = &typ
	entry.Sequence = proto.Uint64(seq)
	entry.Validity = []byte(u.FormatRFC3339(eol))

	sig, err := pk.Sign(ipnsEntryDataForSig(entry))
	if err != nil {
		return nil, err
	}
	entry.Signature = sig
	return entry, nil
}

func ipnsEntryDataForSig(e *pb.IpnsEntry) []byte {
	return bytes.Join([][]byte{
		e.Value,
		e.Validity,
		[]byte(fmt.Sprint(e.GetValidityType())),
	},
		[]byte{})
}

var IpnsRecordValidator = &record.ValidChecker{
	Func: ValidateIpnsRecord,
	Sign: true,
}

func IpnsSelectorFunc(k key.Key, vals [][]byte) (int, error) {
	var recs []*pb.IpnsEntry
	for _, v := range vals {
		e := new(pb.IpnsEntry)
		err := proto.Unmarshal(v, e)
		if err == nil {
			recs = append(recs, e)
		} else {
			recs = append(recs, nil)
		}
	}

	return selectRecord(recs, vals)
}

func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) {
	var best_seq uint64
	best_i := -1

	for i, r := range recs {
		if r == nil || r.GetSequence() < best_seq {
			continue
		}

		if best_i == -1 || r.GetSequence() > best_seq {
			best_seq = r.GetSequence()
			best_i = i
		} else if r.GetSequence() == best_seq {
			rt, err := u.ParseRFC3339(string(r.GetValidity()))
			if err != nil {
				continue
			}

			bestt, err := u.ParseRFC3339(string(recs[best_i].GetValidity()))
			if err != nil {
				continue
			}

			if rt.After(bestt) {
				best_i = i
			} else if rt == bestt {
				if bytes.Compare(vals[i], vals[best_i]) > 0 {
					best_i = i
				}
			}
		}
	}
	if best_i == -1 {
		return 0, errors.New("no usable records in given set")
	}

	return best_i, nil
}

// ValidateIpnsRecord implements ValidatorFunc and verifies that the
// given 'val' is an IpnsEntry and that that entry is valid.
func ValidateIpnsRecord(k key.Key, val []byte) error {
	entry := new(pb.IpnsEntry)
	err := proto.Unmarshal(val, entry)
	if err != nil {
		return err
	}
	switch entry.GetValidityType() {
	case pb.IpnsEntry_EOL:
		t, err := u.ParseRFC3339(string(entry.GetValidity()))
		if err != nil {
			log.Debug("Failed parsing time for ipns record EOL")
			return err
		}
		if time.Now().After(t) {
			return ErrExpiredRecord
		}
	default:
		return ErrUnrecognizedValidity
	}
	return nil
}

// InitializeKeyspace sets the ipns record for the given key to
// point to an empty directory.
// TODO: this doesnt feel like it belongs here
func InitializeKeyspace(ctx context.Context, ds dag.DAGService, pub Publisher, pins pin.Pinner, key ci.PrivKey) error {
	emptyDir := &dag.Node{Data: ft.FolderPBData()}
	nodek, err := ds.Add(emptyDir)
	if err != nil {
		return err
	}

	// pin recursively because this might already be pinned
	// and doing a direct pin would throw an error in that case
	err = pins.Pin(ctx, emptyDir, true)
	if err != nil {
		return err
	}

	err = pins.Flush()
	if err != nil {
		return err
	}

	err = pub.Publish(ctx, key, path.FromKey(nodek))
	if err != nil {
		return err
	}

	return nil
}

func IpnsKeysForID(id peer.ID) (name, ipns key.Key) {
	namekey := key.Key("/pk/" + id)
	ipnskey := key.Key("/ipns/" + id)

	return namekey, ipnskey
}
<commit_msg>cleanup waitfunc<commit_after>package namesys

import (
	"bytes"
	"errors"
	"fmt"
	"time"

	proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
	ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore"
	context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"

	key "github.com/ipfs/go-ipfs/blocks/key"
	dag "github.com/ipfs/go-ipfs/merkledag"
	pb "github.com/ipfs/go-ipfs/namesys/pb"
	path "github.com/ipfs/go-ipfs/path"
	pin "github.com/ipfs/go-ipfs/pin"
	routing "github.com/ipfs/go-ipfs/routing"
	dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb"
	record "github.com/ipfs/go-ipfs/routing/record"
	ft "github.com/ipfs/go-ipfs/unixfs"
	u "github.com/ipfs/go-ipfs/util"
	ci "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/crypto"
	peer "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/peer"
)

// ErrExpiredRecord should be returned when an ipns record is
// invalid due to being too old
var ErrExpiredRecord = errors.New("expired record")

// ErrUnrecognizedValidity is returned when an IpnsRecord has an
// unknown validity type.
var ErrUnrecognizedValidity = errors.New("unrecognized validity type")

var PublishPutValTimeout = time.Minute

// ipnsPublisher is capable of publishing and resolving names to the IPFS
// routing system.
type ipnsPublisher struct {
	routing routing.IpfsRouting
	ds      ds.Datastore
}

// NewRoutingPublisher constructs a publisher for the IPFS Routing name system.
func NewRoutingPublisher(route routing.IpfsRouting, ds ds.Datastore) *ipnsPublisher {
	if ds == nil {
		panic("nil datastore")
	}
	return &ipnsPublisher{routing: route, ds: ds}
}

// Publish implements Publisher. Accepts a keypair and a value,
// and publishes it out to the routing system
func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error {
	log.Debugf("Publish %s", value)
	return p.PublishWithEOL(ctx, k, value, time.Now().Add(time.Hour*24))
}

// PublishWithEOL is a temporary stand in for the ipns records implementation
// see here for more details: https://github.com/ipfs/specs/tree/master/records
func (p *ipnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error {

	id, err := peer.IDFromPrivateKey(k)
	if err != nil {
		return err
	}

	_, ipnskey := IpnsKeysForID(id)

	// get previous records sequence number
	seqnum, err := p.getPreviousSeqNo(ctx, ipnskey)
	if err != nil {
		return err
	}

	// increment it
	seqnum++

	return PutRecordToRouting(ctx, k, value, seqnum, eol, p.routing, id)
}

func (p *ipnsPublisher) getPreviousSeqNo(ctx context.Context, ipnskey key.Key) (uint64, error) {
	prevrec, err := p.ds.Get(ipnskey.DsKey())
	if err != nil && err != ds.ErrNotFound {
		// None found, lets start at zero!
		return 0, err
	}
	var val []byte
	if err == nil {
		prbytes, ok := prevrec.([]byte)
		if !ok {
			return 0, fmt.Errorf("unexpected type returned from datastore: %#v", prevrec)
		}
		dhtrec := new(dhtpb.Record)
		err := proto.Unmarshal(prbytes, dhtrec)
		if err != nil {
			return 0, err
		}

		val = dhtrec.GetValue()
	} else {
		// try and check the dht for a record
		ctx, cancel := context.WithTimeout(ctx, time.Second*30)
		defer cancel()

		rv, err := p.routing.GetValue(ctx, ipnskey)
		if err != nil {
			// no such record found, start at zero!
			return 0, nil
		}

		val = rv
	}

	e := new(pb.IpnsEntry)
	err = proto.Unmarshal(val, e)
	if err != nil {
		return 0, err
	}

	return e.GetSequence(), nil
}

// setting the TTL on published records is an experimental feature.
// as such, i'm using the context to wire it through to avoid changing too
// much code along the way.
func checkCtxTTL(ctx context.Context) (time.Duration, bool) {
	v := ctx.Value("ipns-publish-ttl")
	if v == nil {
		return 0, false
	}

	d, ok := v.(time.Duration)
	return d, ok
}

func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqnum uint64, eol time.Time, r routing.IpfsRouting, id peer.ID) error {
	namekey, ipnskey := IpnsKeysForID(id)
	entry, err := CreateRoutingEntryData(k, value, seqnum, eol)
	if err != nil {
		return err
	}

	ttl, ok := checkCtxTTL(ctx)
	if ok {
		entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds()))
	}

	errs := make(chan error)

	go func() {
		errs <- PublishEntry(ctx, r, ipnskey, entry)
	}()

	go func() {
		errs <- PublishPublicKey(ctx, r, namekey, k.GetPublic())
	}()

	err = waitOnErrChan(ctx, errs)
	if err != nil {
		return err
	}

	err = waitOnErrChan(ctx, errs)
	if err != nil {
		return err
	}

	return nil
}

func waitOnErrChan(ctx context.Context, errs chan error) error {
	select {
	case err := <-errs:
		return err
	case <-ctx.Done():
		return ctx.Err()
	}
}

func PublishPublicKey(ctx context.Context, r routing.IpfsRouting, k key.Key, pubk ci.PubKey) error {
	log.Debugf("Storing pubkey at: %s", k)
	pkbytes, err := pubk.Bytes()
	if err != nil {
		return err
	}

	// Store associated public key
	timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout)
	defer cancel()
	err = r.PutValue(timectx, k, pkbytes)
	if err != nil {
		return err
	}

	return nil
}

func PublishEntry(ctx context.Context, r routing.IpfsRouting, ipnskey key.Key, rec *pb.IpnsEntry) error {
	timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout)
	defer cancel()

	data, err := proto.Marshal(rec)
	if err != nil {
		return err
	}

	log.Debugf("Storing ipns entry at: %s", ipnskey)
	// Store ipns entry at "/ipns/"+b58(h(pubkey))
	if err := r.PutValue(timectx, ipnskey, data); err != nil {
		return err
	}

	return nil
}

func CreateRoutingEntryData(pk ci.PrivKey, val path.Path, seq uint64, eol time.Time) (*pb.IpnsEntry, error) {
	entry := new(pb.IpnsEntry)

	entry.Value = []byte(val)
	typ := pb.IpnsEntry_EOL
	entry.ValidityType = &typ
	entry.Sequence = proto.Uint64(seq)
	entry.Validity = []byte(u.FormatRFC3339(eol))

	sig, err := pk.Sign(ipnsEntryDataForSig(entry))
	if err != nil {
		return nil, err
	}
	entry.Signature = sig
	return entry, nil
}

func ipnsEntryDataForSig(e *pb.IpnsEntry) []byte {
	return bytes.Join([][]byte{
		e.Value,
		e.Validity,
		[]byte(fmt.Sprint(e.GetValidityType())),
	},
		[]byte{})
}

var IpnsRecordValidator = &record.ValidChecker{
	Func: ValidateIpnsRecord,
	Sign: true,
}

func IpnsSelectorFunc(k key.Key, vals [][]byte) (int, error) {
	var recs []*pb.IpnsEntry
	for _, v := range vals {
		e := new(pb.IpnsEntry)
		err := proto.Unmarshal(v, e)
		if err == nil {
			recs = append(recs, e)
		} else {
			recs = append(recs, nil)
		}
	}

	return selectRecord(recs, vals)
}

func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) {
	var best_seq uint64
	best_i := -1

	for i, r := range recs {
		if r == nil || r.GetSequence() < best_seq {
			continue
		}

		if best_i == -1 || r.GetSequence() > best_seq {
			best_seq = r.GetSequence()
			best_i = i
		} else if r.GetSequence() == best_seq {
			rt, err := u.ParseRFC3339(string(r.GetValidity()))
			if err != nil {
				continue
			}

			bestt, err := u.ParseRFC3339(string(recs[best_i].GetValidity()))
			if err != nil {
				continue
			}

			if rt.After(bestt) {
				best_i = i
			} else if rt == bestt {
				if bytes.Compare(vals[i], vals[best_i]) > 0 {
					best_i = i
				}
			}
		}
	}
	if best_i == -1 {
		return 0, errors.New("no usable records in given set")
	}

	return best_i, nil
}

// ValidateIpnsRecord implements ValidatorFunc and verifies that the
// given 'val' is an IpnsEntry and that that entry is valid.
func ValidateIpnsRecord(k key.Key, val []byte) error {
	entry := new(pb.IpnsEntry)
	err := proto.Unmarshal(val, entry)
	if err != nil {
		return err
	}
	switch entry.GetValidityType() {
	case pb.IpnsEntry_EOL:
		t, err := u.ParseRFC3339(string(entry.GetValidity()))
		if err != nil {
			log.Debug("Failed parsing time for ipns record EOL")
			return err
		}
		if time.Now().After(t) {
			return ErrExpiredRecord
		}
	default:
		return ErrUnrecognizedValidity
	}
	return nil
}

// InitializeKeyspace sets the ipns record for the given key to
// point to an empty directory.
// TODO: this doesnt feel like it belongs here
func InitializeKeyspace(ctx context.Context, ds dag.DAGService, pub Publisher, pins pin.Pinner, key ci.PrivKey) error {
	emptyDir := &dag.Node{Data: ft.FolderPBData()}
	nodek, err := ds.Add(emptyDir)
	if err != nil {
		return err
	}

	// pin recursively because this might already be pinned
	// and doing a direct pin would throw an error in that case
	err = pins.Pin(ctx, emptyDir, true)
	if err != nil {
		return err
	}

	err = pins.Flush()
	if err != nil {
		return err
	}

	err = pub.Publish(ctx, key, path.FromKey(nodek))
	if err != nil {
		return err
	}

	return nil
}

func IpnsKeysForID(id peer.ID) (name, ipns key.Key) {
	namekey := key.Key("/pk/" + id)
	ipnskey := key.Key("/ipns/" + id)

	return namekey, ipnskey
}
<|endoftext|>


<commit_before>// datastore.go

package naprrql

import (
	"fmt"
	"log"

	"github.com/syndtr/goleveldb/leveldb"
	"github.com/syndtr/goleveldb/leveldb/opt"
	"github.com/syndtr/goleveldb/leveldb/util"
)

var db *leveldb.DB
var dbOpen bool = false

var ge = GobEncoder{}

// if allow_empty is false, abort if database is empty: enforces
// requirement to run ingest first
func GetDB() *leveldb.DB {
	if !dbOpen {
		log.Println("DB not initialised. Opening...")
		openDB()
	}
	return db
}

//
// Check if the databse contains any entries
//
func DatabaseIsEmpty() bool {

	iter := GetDB().NewIterator(nil, nil)
	iter.Next()
	if len(iter.Key()) == 0 {
		return true
	}
	return false
}

//
// run compaction on db, typically post-ingest
//
func CompactDatastore() {
	err := GetDB().CompactRange(util.Range{Limit: nil, Start: nil})
	if err != nil {
		log.Println("Error compacting datastore: ", err)
	}
}

//
// open the kv store, this must be called before any access is attempted
//
func openDB() {

	workingDir := "kvs"

	o := &opt.Options{
		// Filter:             filter.NewBloomFilter(10),
		// BlockCacheCapacity: 128 * 1024 * 1024,
		// NoSync: true,
		// OpenFilesCacheCapacity: 1024,
		// ReadOnly:            true,
		CompactionTableSize: (4 * opt.MiB),
	}
	var dbErr error
	db, dbErr = leveldb.OpenFile(workingDir, o)
	if dbErr != nil {
		log.Fatalln("DB Create error: ", dbErr)
	}

	dbOpen = true
}

//
// Given a key or key-prefix, returns the reference ids that
// can be used in a Get operation to retreive the
// desired object
//
func getIdentifiers(keyPrefix string) []string {

	db = GetDB()
	objIDs := make([]string, 0)

	searchKey := []byte(keyPrefix)
	// log.Printf("search_key: %s\n\n", searchKey)
	iter := db.NewIterator(util.BytesPrefix(searchKey), nil)
	for iter.Next() {
		id := fmt.Sprintf("%s", iter.Value())
		objIDs = append(objIDs, id)
		// break
	}
	iter.Release()
	err := iter.Error()
	if err != nil {
		log.Println("Iterator error: ", err)
	}

	return objIDs
}

//
// get objects stored against the list of keys
//
func getObjects(objIDs []string) ([]interface{}, error) {

	db = GetDB()
	objects := []interface{}{}

	for _, objID := range objIDs {
		var object interface{}
		data, err := db.Get([]byte(objID), nil)
		if err != nil {
			log.Println("Cannot find object with key: ", string(objID))
			return objects, err
		}
		err = ge.Decode(data, &object)
		if err != nil {
			log.Println("Cannot decode object with key: ", objID, err)
			return objects, err
		}
		objects = append(objects, object)
	}

	return objects, nil

}

//
//
//
<commit_msg>With params not done<commit_after>// datastore.go

package naprrql

import (
	"fmt"
	"log"

	"github.com/syndtr/goleveldb/leveldb"
	"github.com/syndtr/goleveldb/leveldb/filter"
	"github.com/syndtr/goleveldb/leveldb/opt"
	"github.com/syndtr/goleveldb/leveldb/util"
)

var db *leveldb.DB
var dbOpen bool = false

var ge = GobEncoder{}

// if allow_empty is false, abort if database is empty: enforces
// requirement to run ingest first
func GetDB() *leveldb.DB {
	if !dbOpen {
		log.Println("DB not initialised. Opening...")
		openDB()
	}
	return db
}

//
// Check if the databse contains any entries
//
func DatabaseIsEmpty() bool {

	iter := GetDB().NewIterator(nil, nil)
	iter.Next()
	if len(iter.Key()) == 0 {
		return true
	}
	return false
}

//
// run compaction on db, typically post-ingest
//
func CompactDatastore() {
	err := GetDB().CompactRange(util.Range{Limit: nil, Start: nil})
	if err != nil {
		log.Println("Error compacting datastore: ", err)
	}
}

//
// open the kv store, this must be called before any access is attempted
//
func openDB() {

	workingDir := "kvs"

	o := &opt.Options{

		// Original
		// // Filter:             filter.NewBloomFilter(10),
		// // BlockCacheCapacity: 128 * 1024 * 1024,
		// // NoSync: true,
		// // OpenFilesCacheCapacity: 1024,
		// // ReadOnly:            true,
		// CompactionTableSize: (4 * opt.MiB),

		// Experiment from Matt 2019-04-12
		Filter:                        filter.NewBloomFilter(10),
		BlockCacheCapacity:            2 * opt.GiB,
		CompactionTotalSizeMultiplier: 20,
		OpenFilesCacheCapacity:        1024,
		ReadOnly:                      true,
		CompactionTableSize:           (4 * opt.MiB),   //default 2
		CompactionTotalSize:           (128 * opt.MiB), //default 10 mb
	}
	var dbErr error
	db, dbErr = leveldb.OpenFile(workingDir, o)
	if dbErr != nil {
		log.Fatalln("DB Create error: ", dbErr)
	}

	dbOpen = true
}

//
// Given a key or key-prefix, returns the reference ids that
// can be used in a Get operation to retreive the
// desired object
//
func getIdentifiers(keyPrefix string) []string {

	db = GetDB()
	objIDs := make([]string, 0)

	searchKey := []byte(keyPrefix)
	// log.Printf("search_key: %s\n\n", searchKey)
	iter := db.NewIterator(util.BytesPrefix(searchKey), nil)
	for iter.Next() {
		id := fmt.Sprintf("%s", iter.Value())
		objIDs = append(objIDs, id)
		// break
	}
	iter.Release()
	err := iter.Error()
	if err != nil {
		log.Println("Iterator error: ", err)
	}

	return objIDs
}

//
// get objects stored against the list of keys
//
func getObjects(objIDs []string) ([]interface{}, error) {

	db = GetDB()
	objects := []interface{}{}

	for _, objID := range objIDs {
		var object interface{}
		data, err := db.Get([]byte(objID), nil)
		if err != nil {
			log.Println("Cannot find object with key: ", string(objID))
			return objects, err
		}
		err = ge.Decode(data, &object)
		if err != nil {
			log.Println("Cannot decode object with key: ", objID, err)
			return objects, err
		}
		objects = append(objects, object)
	}

	return objects, nil

}

//
//
//
<|endoftext|>


<commit_before>package http

import (
	"fmt"
	"math"
	"math/rand"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/trafficstars/registry"
)

var _balancer balancer

func Init(discovery registry.Discovery) {
	rand.Seed(time.Now().UnixNano())
	_balancer = balancer{
		upstreams: make(map[string]*upstream),
		discovery: discovery,
	}
	_balancer.lookup()
	go _balancer.supervisor()
}

type (
	backend struct {
		weight      int
		skipCounter int
		address     string
	}
	backends []*backend
	upstream struct {
		index         int
		gcd           int //greatest common divisor
		maxWeight     int
		currentWeight int
		backends      backends
	}
)

func (b *backend) skip() {
	b.skipCounter = 7
}
func (b backends) maxWeight() int {
	maxWeight := -1
	for _, backend := range b {
		if backend.weight > maxWeight {
			maxWeight = backend.weight
		}
	}
	return maxWeight
}

func (b backends) gcd() int {
	divisor := -1
	for _, backend := range b {
		if divisor == -1 {
			divisor = backend.weight
		} else {
			divisor = gcd(divisor, backend.weight)
		}
	}
	return divisor
}

func gcd(a, b int) int {
	for b != 0 {
		a, b = b, a%b
	}
	return a
}

type balancer struct {
	mutex     sync.RWMutex
	upstreams map[string]*upstream
	backends  map[string]backends
	discovery registry.Discovery
}

func (b *balancer) lookup() {
	var (
		backends      = make(map[string]backends, len(b.backends))
		services, err = b.discovery.Lookup(nil)
	)
	if err != nil {
		return
	}
	for _, service := range services {
		if service.Status == registry.SERVICE_STATUS_PASSING {
			backends[service.Name] = append(backends[service.Name], &backend{
				weight:  serverWeight(&service),
				address: net.JoinHostPort(service.Address, strconv.Itoa(service.Port)),
			})
		}
	}
	b.mutex.Lock()
	defer b.mutex.Unlock()
	for key := range b.upstreams {
		if _, found := backends[key]; !found {
			delete(b.upstreams, key)
		}
	}
	for key, backends := range backends {
		b.upstreams[key] = &upstream{
			backends:  backends,
			gcd:       backends.gcd(),
			maxWeight: backends.maxWeight(),
		}
	}
}

func (b *balancer) supervisor() {
	tick := time.Tick(5 * time.Second)
	for {
		select {
		case <-tick:
			b.lookup()
		}
	}
}

func (b *balancer) countOfBackends(service string) int {
	b.mutex.RLock()
	defer b.mutex.RUnlock()
	if upstream, found := b.upstreams[service]; found {
		return len(upstream.backends)
	}
	return 0
}

func (b *balancer) next(service string) (*backend, error) {
	b.mutex.RLock()
	defer b.mutex.RUnlock()
	if upstream, found := b.upstreams[service]; found {
		for {
			upstream.index = (upstream.index + 1) % len(upstream.backends)
			if upstream.index == 0 {
				upstream.currentWeight = upstream.currentWeight - upstream.gcd
				if upstream.currentWeight <= 0 {
					upstream.currentWeight = upstream.maxWeight
					if upstream.currentWeight == 0 {
						return upstream.backends[upstream.index], nil
					}
				}
			}
			if backend := upstream.backends[upstream.index]; backend.weight >= upstream.currentWeight {
				if backend.skipCounter != 0 {
					backend.skipCounter--
					continue
				}
				return backend, nil
			}
		}
	}
	return nil, fmt.Errorf("Service '%s' not found", service)
}

func serverWeight(s *registry.Service) int {
	weight := 1
	for _, tag := range s.Tags {
		if strings.HasPrefix(tag, "SERVICE_WEIGHT=") {
			if v, _ := strconv.ParseInt(strings.TrimPrefix(tag, "SERVICE_WEIGHT="), 10, 64); v != 0 {
				weight = int(v)
			}
		}
	}
	weight *= 100
	for _, tag := range s.Tags {
		if strings.HasPrefix(tag, "CPU_USAGE=") {
			if v, _ := strconv.ParseFloat(strings.TrimPrefix(tag, "CPU_USAGE="), 64); v != 0 {
				weight = weight / int(math.Ceil(v/4.0))
			}
		}
	}
	return weight
}
<commit_msg>fix: possible division by zero<commit_after>package http

import (
	"fmt"
	"math"
	"math/rand"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/trafficstars/registry"
)

var _balancer balancer

func Init(discovery registry.Discovery) {
	rand.Seed(time.Now().UnixNano())
	_balancer = balancer{
		upstreams: make(map[string]*upstream),
		discovery: discovery,
	}
	_balancer.lookup()
	go _balancer.supervisor()
}

type (
	backend struct {
		weight      int
		skipCounter int
		address     string
	}
	backends []*backend
	upstream struct {
		index         int
		gcd           int //greatest common divisor
		maxWeight     int
		currentWeight int
		backends      backends
	}
)

func (b *backend) skip() {
	b.skipCounter = 7
}
func (b backends) maxWeight() int {
	maxWeight := -1
	for _, backend := range b {
		if backend.weight > maxWeight {
			maxWeight = backend.weight
		}
	}
	return maxWeight
}

func (b backends) gcd() int {
	divisor := -1
	for _, backend := range b {
		if divisor == -1 {
			divisor = backend.weight
		} else {
			divisor = gcd(divisor, backend.weight)
		}
	}
	return divisor
}

func gcd(a, b int) int {
	for b != 0 {
		a, b = b, a%b
	}
	return a
}

type balancer struct {
	mutex     sync.RWMutex
	upstreams map[string]*upstream
	backends  map[string]backends
	discovery registry.Discovery
}

func (b *balancer) lookup() {
	var (
		backends      = make(map[string]backends, len(b.backends))
		services, err = b.discovery.Lookup(nil)
	)
	if err != nil {
		return
	}
	for _, service := range services {
		if service.Status == registry.SERVICE_STATUS_PASSING {
			backends[service.Name] = append(backends[service.Name], &backend{
				weight:  serverWeight(&service),
				address: net.JoinHostPort(service.Address, strconv.Itoa(service.Port)),
			})
		}
	}
	b.mutex.Lock()
	defer b.mutex.Unlock()
	for key := range b.upstreams {
		if _, found := backends[key]; !found {
			delete(b.upstreams, key)
		}
	}
	for key, backends := range backends {
		b.upstreams[key] = &upstream{
			backends:  backends,
			gcd:       backends.gcd(),
			maxWeight: backends.maxWeight(),
		}
	}
}

func (b *balancer) supervisor() {
	tick := time.Tick(5 * time.Second)
	for {
		select {
		case <-tick:
			b.lookup()
		}
	}
}

func (b *balancer) countOfBackends(service string) int {
	b.mutex.RLock()
	defer b.mutex.RUnlock()
	if upstream, found := b.upstreams[service]; found {
		return len(upstream.backends)
	}
	return 0
}

func (b *balancer) next(service string) (*backend, error) {
	b.mutex.RLock()
	defer b.mutex.RUnlock()
	if upstream, found := b.upstreams[service]; found {
		for {
			upstream.index = (upstream.index + 1) % len(upstream.backends)
			if upstream.index == 0 {
				upstream.currentWeight = upstream.currentWeight - upstream.gcd
				if upstream.currentWeight <= 0 {
					upstream.currentWeight = upstream.maxWeight
					if upstream.currentWeight == 0 {
						return upstream.backends[upstream.index], nil
					}
				}
			}
			if backend := upstream.backends[upstream.index]; backend.weight >= upstream.currentWeight {
				if backend.skipCounter != 0 {
					backend.skipCounter--
					continue
				}
				return backend, nil
			}
		}
	}
	return nil, fmt.Errorf("Service '%s' not found", service)
}

func serverWeight(s *registry.Service) int {
	weight := 1
	for _, tag := range s.Tags {
		if strings.HasPrefix(tag, "SERVICE_WEIGHT=") {
			if v, _ := strconv.ParseInt(strings.TrimPrefix(tag, "SERVICE_WEIGHT="), 10, 64); v != 0 {
				weight = int(v)
			}
		}
	}
	weight *= 100
	for _, tag := range s.Tags {
		if strings.HasPrefix(tag, "CPU_USAGE=") {
			if v, _ := strconv.ParseFloat(strings.TrimPrefix(tag, "CPU_USAGE="), 64); v != 0 {
				if usage := int(math.Ceil(v / 4.0)); usage != 0 {
					weight = weight / int(math.Ceil(v/4.0))
				}
			}
		}
	}
	return weight
}
<|endoftext|>


<commit_before>package multiconfig

type multiValidator []Validator

// MultiValidator accepts variadic validators and satisfies Validator interface.
func MultiValidator(validators ...Validator) Validator {
	return multiValidator(validators)
}

// Validate tries to validate given struct with all the validators. If it doesnt
// have any Validator it will simply skip the validation step. If any of the
// given validators return err, it will stop validating and return it.
func (d multiValidator) Validate(s interface{}) error {
	for _, validator := range d {
		if err := validator.Validate(s); err != nil {
			return err
		}
	}

	return nil
}

// MustValidate validates the struct, it panics if gets any error
func (d multiValidator) MustValidate(s interface{}) {
	if err := d.Validate(s); err != nil {
		panic(err)
	}
}
<commit_msg>fix typo<commit_after>package multiconfig

type multiValidator []Validator

// MultiValidator accepts variadic validators and satisfies Validator interface.
func MultiValidator(validators ...Validator) Validator {
	return multiValidator(validators)
}

// Validate tries to validate given struct with all the validators. If it doesn't
// have any Validator it will simply skip the validation step. If any of the
// given validators return err, it will stop validating and return it.
func (d multiValidator) Validate(s interface{}) error {
	for _, validator := range d {
		if err := validator.Validate(s); err != nil {
			return err
		}
	}

	return nil
}

// MustValidate validates the struct, it panics if gets any error
func (d multiValidator) MustValidate(s interface{}) {
	if err := d.Validate(s); err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package service

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"time"

	"github.com/syou6162/go-active-learning/lib/model"
)

func (app *goActiveLearningApp) InsertOrUpdateExample(e *model.Example) error {
	return app.repo.InsertOrUpdateExample(e)
}

func (app *goActiveLearningApp) InsertExampleFromScanner(scanner *bufio.Scanner) (*model.Example, error) {
	return app.repo.InsertExampleFromScanner(scanner)
}

func (app *goActiveLearningApp) InsertExamplesFromReader(reader io.Reader) error {
	return app.repo.InsertExamplesFromReader(reader)
}

func (app *goActiveLearningApp) ReadExamples() (model.Examples, error) {
	return app.repo.ReadExamples()
}

func (app *goActiveLearningApp) ReadRecentExamples(from time.Time) (model.Examples, error) {
	return app.repo.ReadRecentExamples(from)
}

func (app *goActiveLearningApp) ReadExamplesByLabel(label model.LabelType, limit int) (model.Examples, error) {
	return app.repo.ReadExamplesByLabel(label, limit)
}

func (app *goActiveLearningApp) ReadLabeledExamples(limit int) (model.Examples, error) {
	return app.repo.ReadLabeledExamples(limit)
}

func (app *goActiveLearningApp) ReadPositiveExamples(limit int) (model.Examples, error) {
	return app.repo.ReadPositiveExamples(limit)
}

func (app *goActiveLearningApp) ReadNegativeExamples(limit int) (model.Examples, error) {
	return app.repo.ReadNegativeExamples(limit)
}

func (app *goActiveLearningApp) ReadUnlabeledExamples(limit int) (model.Examples, error) {
	return app.repo.ReadUnlabeledExamples(limit)
}

func (app *goActiveLearningApp) FindExampleByUlr(url string) (*model.Example, error) {
	return app.repo.FindExampleByUlr(url)
}

func (app *goActiveLearningApp) SearchExamplesByUlrs(urls []string) (model.Examples, error) {
	return app.repo.SearchExamplesByUlrs(urls)
}

func (app *goActiveLearningApp) DeleteAllExamples() error {
	return app.repo.DeleteAllExamples()
}

func (app *goActiveLearningApp) UpdateExampleMetadata(e model.Example) error {
	if err := app.cache.UpdateExampleMetadata(e); err != nil {
		return err
	}
	if err := app.repo.InsertOrUpdateExample(&e); err != nil {
		log.Println(fmt.Sprintf("Error occured proccessing %s %s", e.Url, err.Error()))
	}
	if err := app.repo.UpdateFeatureVector(&e); err != nil {
		log.Println(fmt.Sprintf("Error occured updating feature vector %s %s", e.Url, err.Error()))
	}
	if err := app.repo.UpdateHatenaBookmark(&e); err != nil {
		log.Println(fmt.Sprintf("Error occured updating bookmark info %s %s", e.Url, err.Error()))
	}
	return nil
}

func (app *goActiveLearningApp) UpdateExamplesMetadata(examples model.Examples) error {
	if err := app.cache.UpdateExamplesMetadata(examples); err != nil {
		return err
	}
	for _, e := range examples {
		app.UpdateExampleMetadata(*e)
	}
	return nil
}

func (app *goActiveLearningApp) UpdateExampleExpire(e model.Example, duration time.Duration) error {
	return app.cache.UpdateExampleExpire(e, duration)
}

func hatenaBookmarkByExampleId(hatenaBookmarks []*model.HatenaBookmark) map[int]*model.HatenaBookmark {
	result := make(map[int]*model.HatenaBookmark)
	for _, hb := range hatenaBookmarks {
		result[hb.ExampleId] = hb
	}
	return result
}

func (app *goActiveLearningApp) AttachMetadata(examples model.Examples) error {
	fvList, err := app.repo.SearchFeatureVector(examples)
	if err != nil {
		return err
	}

	for idx, e := range examples {
		e.Fv = fvList[idx]
	}

	hatenaBookmarks, err := app.repo.SearchHatenaBookmarks(examples)
	if err != nil {
		return err
	}
	hbByid := hatenaBookmarkByExampleId(hatenaBookmarks)
	for idx, e := range examples {
		if b, ok := hbByid[idx]; ok {
			e.HatenaBookmark = b
		}
	}
	return nil
}

func (app *goActiveLearningApp) AttachLightMetadata(examples model.Examples) error {
	hatenaBookmarks, err := app.repo.SearchHatenaBookmarks(examples)
	if err != nil {
		return err
	}
	hbByid := hatenaBookmarkByExampleId(hatenaBookmarks)
	for idx, e := range examples {
		if b, ok := hbByid[idx]; ok {
			e.HatenaBookmark = b
		}
	}
	return nil
}

func (app *goActiveLearningApp) Fetch(examples model.Examples) {
	app.cache.Fetch(examples)
}

func (app *goActiveLearningApp) AddExamplesToList(listName string, examples model.Examples) error {
	return app.cache.AddExamplesToList(listName, examples)
}

func (app *goActiveLearningApp) GetUrlsFromList(listName string, from int64, to int64) ([]string, error) {
	return app.cache.GetUrlsFromList(listName, from, to)
}
<commit_msg>ディフォルト値を与えておく<commit_after>package service

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"time"

	"github.com/syou6162/go-active-learning/lib/model"
)

func (app *goActiveLearningApp) InsertOrUpdateExample(e *model.Example) error {
	return app.repo.InsertOrUpdateExample(e)
}

func (app *goActiveLearningApp) InsertExampleFromScanner(scanner *bufio.Scanner) (*model.Example, error) {
	return app.repo.InsertExampleFromScanner(scanner)
}

func (app *goActiveLearningApp) InsertExamplesFromReader(reader io.Reader) error {
	return app.repo.InsertExamplesFromReader(reader)
}

func (app *goActiveLearningApp) ReadExamples() (model.Examples, error) {
	return app.repo.ReadExamples()
}

func (app *goActiveLearningApp) ReadRecentExamples(from time.Time) (model.Examples, error) {
	return app.repo.ReadRecentExamples(from)
}

func (app *goActiveLearningApp) ReadExamplesByLabel(label model.LabelType, limit int) (model.Examples, error) {
	return app.repo.ReadExamplesByLabel(label, limit)
}

func (app *goActiveLearningApp) ReadLabeledExamples(limit int) (model.Examples, error) {
	return app.repo.ReadLabeledExamples(limit)
}

func (app *goActiveLearningApp) ReadPositiveExamples(limit int) (model.Examples, error) {
	return app.repo.ReadPositiveExamples(limit)
}

func (app *goActiveLearningApp) ReadNegativeExamples(limit int) (model.Examples, error) {
	return app.repo.ReadNegativeExamples(limit)
}

func (app *goActiveLearningApp) ReadUnlabeledExamples(limit int) (model.Examples, error) {
	return app.repo.ReadUnlabeledExamples(limit)
}

func (app *goActiveLearningApp) FindExampleByUlr(url string) (*model.Example, error) {
	return app.repo.FindExampleByUlr(url)
}

func (app *goActiveLearningApp) SearchExamplesByUlrs(urls []string) (model.Examples, error) {
	return app.repo.SearchExamplesByUlrs(urls)
}

func (app *goActiveLearningApp) DeleteAllExamples() error {
	return app.repo.DeleteAllExamples()
}

func (app *goActiveLearningApp) UpdateExampleMetadata(e model.Example) error {
	if err := app.cache.UpdateExampleMetadata(e); err != nil {
		return err
	}
	if err := app.repo.InsertOrUpdateExample(&e); err != nil {
		log.Println(fmt.Sprintf("Error occured proccessing %s %s", e.Url, err.Error()))
	}
	if err := app.repo.UpdateFeatureVector(&e); err != nil {
		log.Println(fmt.Sprintf("Error occured updating feature vector %s %s", e.Url, err.Error()))
	}
	if err := app.repo.UpdateHatenaBookmark(&e); err != nil {
		log.Println(fmt.Sprintf("Error occured updating bookmark info %s %s", e.Url, err.Error()))
	}
	return nil
}

func (app *goActiveLearningApp) UpdateExamplesMetadata(examples model.Examples) error {
	if err := app.cache.UpdateExamplesMetadata(examples); err != nil {
		return err
	}
	for _, e := range examples {
		app.UpdateExampleMetadata(*e)
	}
	return nil
}

func (app *goActiveLearningApp) UpdateExampleExpire(e model.Example, duration time.Duration) error {
	return app.cache.UpdateExampleExpire(e, duration)
}

func hatenaBookmarkByExampleId(hatenaBookmarks []*model.HatenaBookmark) map[int]*model.HatenaBookmark {
	result := make(map[int]*model.HatenaBookmark)
	for _, hb := range hatenaBookmarks {
		result[hb.ExampleId] = hb
	}
	return result
}

func (app *goActiveLearningApp) AttachMetadata(examples model.Examples) error {
	fvList, err := app.repo.SearchFeatureVector(examples)
	if err != nil {
		return err
	}

	for idx, e := range examples {
		e.Fv = fvList[idx]
	}

	hatenaBookmarks, err := app.repo.SearchHatenaBookmarks(examples)
	if err != nil {
		return err
	}
	hbByid := hatenaBookmarkByExampleId(hatenaBookmarks)
	for idx, e := range examples {
		if b, ok := hbByid[idx]; ok {
			e.HatenaBookmark = b
		} else {
			e.HatenaBookmark = &model.HatenaBookmark{Bookmarks:[]*model.Bookmark{}}
		}
	}
	return nil
}

func (app *goActiveLearningApp) AttachLightMetadata(examples model.Examples) error {
	hatenaBookmarks, err := app.repo.SearchHatenaBookmarks(examples)
	if err != nil {
		return err
	}
	hbByid := hatenaBookmarkByExampleId(hatenaBookmarks)
	for idx, e := range examples {
		if b, ok := hbByid[idx]; ok {
			e.HatenaBookmark = b
		} else {
			e.HatenaBookmark = &model.HatenaBookmark{Bookmarks:[]*model.Bookmark{}}
		}
	}
	return nil
}

func (app *goActiveLearningApp) Fetch(examples model.Examples) {
	app.cache.Fetch(examples)
}

func (app *goActiveLearningApp) AddExamplesToList(listName string, examples model.Examples) error {
	return app.cache.AddExamplesToList(listName, examples)
}

func (app *goActiveLearningApp) GetUrlsFromList(listName string, from int64, to int64) ([]string, error) {
	return app.cache.GetUrlsFromList(listName, from, to)
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"
)

func main() {
	s := &http.Server{
		Addr:           ":8080",
		ReadTimeout:    5 * time.Second,
		WriteTimeout:   5 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			process := os.Getenv("fprocess")
			parts := strings.Split(process, " ")
			targetCmd := exec.Command(parts[0], parts[1:]...)
			writer, _ := targetCmd.StdinPipe()
			res, _ := ioutil.ReadAll(r.Body)
			writer.Write(res)
			writer.Close()
			out, err := targetCmd.Output()
			if err != nil {
				panic(err)
			}

			os.Stdout.Write(out)
			w.Write(out)
		}
	})

	log.Fatal(s.ListenAndServe())
}
<commit_msg>Better debugging infos<commit_after>package main

import (
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"
)

func main() {
	s := &http.Server{
		Addr:           ":8080",
		ReadTimeout:    5 * time.Second,
		WriteTimeout:   5 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			process := os.Getenv("fprocess")
			parts := strings.Split(process, " ")
			targetCmd := exec.Command(parts[0], parts[1:]...)
			writer, _ := targetCmd.StdinPipe()
			res, _ := ioutil.ReadAll(r.Body)
			writer.Write(res)
			writer.Close()
			out, err := targetCmd.CombinedOutput()
			if err != nil {
				panic(err.Error() + "\n" + string(out))
			}

			os.Stdout.Write(out)
			w.Write(out)
		}
	})

	log.Fatal(s.ListenAndServe())
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/PuerkitoBio/throttled"
	"github.com/daaku/go.httpgzip"
	"github.com/rs/cors"
	"net/http"
)

func Middleware(fn func(http.ResponseWriter, *http.Request), o ServerOptions) http.Handler {
	next := http.Handler(http.HandlerFunc(fn))

	if o.Concurrency > 0 {
		next = throttle(next, o)
	}
	if o.Gzip {
		next = httpgzip.NewHandler(next)
	}
	if o.CORS {
		next = cors.Default().Handler(next)
	}
	if o.ApiKey != "" {
		next = validateApiKey(next, o.ApiKey)
	}

	return validate(defaultHeaders(next))
}

func ImageMiddleware(o ServerOptions) func(Operation) http.Handler {
	return func(fn Operation) http.Handler {
		return Middleware(func(w http.ResponseWriter, r *http.Request) {
			imageController(w, r, Operation(fn))
		}, o)
	}
}

func throttle(next http.Handler, o ServerOptions) http.Handler {
	th := throttled.Interval(throttled.PerSec(o.Concurrency), o.Burst, &throttled.VaryBy{Method: true}, o.Burst)
	return th.Throttle(next)
}

func validate(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" && r.Method != "POST" {
			ErrorReply(w, "Method not allowed: "+r.Method, NOT_ALLOWED)
			return
		}
		next.ServeHTTP(w, r)
	})
}

func validateApiKey(next http.Handler, validKey string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		key := r.Header.Get("API-Key")
		if key == "" {
			key = r.URL.Query().Get("key")
		}

		if key != validKey {
			ErrorReply(w, "Invalid or missing API key", UNAUTHORIZED)
			return
		}

		next.ServeHTTP(w, r)
	})
}

func defaultHeaders(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Server", "imaginary "+Version)
		next.ServeHTTP(w, r)
	})
}
<commit_msg>feat(http): add bimg header<commit_after>package main

import (
	"fmt"
	"github.com/PuerkitoBio/throttled"
	"github.com/daaku/go.httpgzip"
	"github.com/rs/cors"
	"gopkg.in/h2non/bimg.v0"
	"net/http"
)

func Middleware(fn func(http.ResponseWriter, *http.Request), o ServerOptions) http.Handler {
	next := http.Handler(http.HandlerFunc(fn))

	if o.Concurrency > 0 {
		next = throttle(next, o)
	}
	if o.Gzip {
		next = httpgzip.NewHandler(next)
	}
	if o.CORS {
		next = cors.Default().Handler(next)
	}
	if o.ApiKey != "" {
		next = validateApiKey(next, o.ApiKey)
	}

	return validate(defaultHeaders(next))
}

func ImageMiddleware(o ServerOptions) func(Operation) http.Handler {
	return func(fn Operation) http.Handler {
		return Middleware(func(w http.ResponseWriter, r *http.Request) {
			imageController(w, r, Operation(fn))
		}, o)
	}
}

func throttle(next http.Handler, o ServerOptions) http.Handler {
	th := throttled.Interval(throttled.PerSec(o.Concurrency), o.Burst, &throttled.VaryBy{Method: true}, o.Burst)
	return th.Throttle(next)
}

func validate(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" && r.Method != "POST" {
			ErrorReply(w, "Method not allowed: "+r.Method, NOT_ALLOWED)
			return
		}
		next.ServeHTTP(w, r)
	})
}

func validateApiKey(next http.Handler, validKey string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		key := r.Header.Get("API-Key")
		if key == "" {
			key = r.URL.Query().Get("key")
		}

		if key != validKey {
			ErrorReply(w, "Invalid or missing API key", UNAUTHORIZED)
			return
		}

		next.ServeHTTP(w, r)
	})
}

func defaultHeaders(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Server", fmt.Sprintf("imaginary %s (using bimg %s)", Version, bimg.Version))
		next.ServeHTTP(w, r)
	})
}
<|endoftext|>


<commit_before>package validator

import (
	"log"
	"regexp"
	"time"
)

var allowedChars = regexp.MustCompile(`^[a-zA-Z0-9_]+$`)
var validObjectId = regexp.MustCompile(`^[a-f0-9]{24}$`)

func ValidObjectId(id string) bool {
	return validObjectId.MatchString(id)
}

func ValidateEntityName(name string) string {
	if allowedChars.MatchString(name) {
		return name
	}
	return ""
}

type SearchSchema struct {
	Validated bool
	Entity    string
}

func ValidateSearch(message map[string]interface{}) SearchSchema {
	entity, _ := message["entity"].(string)
	entity = ValidateEntityName(entity)

	return SearchSchema{entity != "", entity}
}

func ValidateEntity(message map[string]interface{}) (string, map[string]interface{}, bool) {
	/*Allow type:
	int
	float
	date: 2006-01-02 03:04:01
	boolean
	array[int, float]
	*/
	errCounter := 0
	data, _ := message["data"].(map[string]interface{})
	entity, _ := message["entity"].(string)
	newData := map[string]interface{}{}

	entity = ValidateEntityName(entity)

	for k, v := range data {
		if !allowedChars.MatchString(k) {
			errCounter += 1
			continue
		}
		switch vv := v.(type) {
		case int:
			newData[k] = vv
		case float64:
			newData[k] = vv
		case bool:
			newData[k] = vv
		case []interface{}:
			newArr := make([]interface{}, 0)
			for _, u := range vv {
				switch u.(type) {
				case int:
					newArr = append(newArr, u)
				case float64:
					newArr = append(newArr, u)
				default:
					errCounter += 1
				}
			}
			newData[k] = newArr
		case string:
			vvv, err := time.Parse("2006-01-02 03:04:01", vv)
			if err != nil {
				log.Println(err)
				errCounter += 1
			} else {
				newData[k] = vvv
			}
		default:
			errCounter += 1
		}
	}
	if errCounter > 0 {
		return entity, map[string]interface{}{}, false
	}
	return entity, newData, len(newData) > 0
}
<commit_msg>Add array to newData only if it has data else our check willl be wrong at the end<commit_after>package validator

import (
	"log"
	"regexp"
	"time"
)

var allowedChars = regexp.MustCompile(`^[a-zA-Z0-9_]+$`)
var validObjectId = regexp.MustCompile(`^[a-f0-9]{24}$`)

func ValidObjectId(id string) bool {
	return validObjectId.MatchString(id)
}

func ValidateEntityName(name string) string {
	if allowedChars.MatchString(name) {
		return name
	}
	return ""
}

type SearchSchema struct {
	Validated bool
	Entity    string
}

func ValidateSearch(message map[string]interface{}) SearchSchema {
	entity, _ := message["entity"].(string)
	entity = ValidateEntityName(entity)

	return SearchSchema{entity != "", entity}
}

func ValidateEntity(message map[string]interface{}) (string, map[string]interface{}, bool) {
	/*Allow type:
	int
	float
	date: 2006-01-02 03:04:01
	boolean
	array[int, float]
	*/
	errCounter := 0
	data, _ := message["data"].(map[string]interface{})
	entity, _ := message["entity"].(string)
	newData := map[string]interface{}{}

	entity = ValidateEntityName(entity)

	for k, v := range data {
		if !allowedChars.MatchString(k) {
			errCounter += 1
			continue
		}
		switch vv := v.(type) {
		case int:
			newData[k] = vv
		case float64:
			newData[k] = vv
		case bool:
			newData[k] = vv
		case []interface{}:
			newArr := make([]interface{}, 0)
			for _, u := range vv {
				switch u.(type) {
				case int:
					newArr = append(newArr, u)
				case float64:
					newArr = append(newArr, u)
				default:
					errCounter += 1
				}
			}
			if len(newArr) > 0 {
				newData[k] = newArr
			}
		case string:
			vvv, err := time.Parse("2006-01-02 03:04:01", vv)
			if err != nil {
				log.Println(err)
				errCounter += 1
			} else {
				newData[k] = vvv
			}
		default:
			errCounter += 1
		}
	}
	if errCounter > 0 {
		return entity, map[string]interface{}{}, false
	}
	return entity, newData, len(newData) > 0
}
<|endoftext|>


<commit_before>package builder

import (
	"bytes"
	"strings"
)

//BuildQuery - get sql string from expression
func (query *Query) BuildQuery() string {
	var buffer bytes.Buffer
	buffer.WriteString(query.TypeQuery)
	buffer.WriteString(" ")
	columns := interfaceToString(query.Columns)
	buffer.WriteString(strings.Join(columns[:], ", "))

	buffer.WriteString(" From ")
	buffer.WriteString(query.TableName)

	if len(query.WhereCond) != 0 {
		buffer.WriteString(" Where ")
		for index, element := range query.WhereCond {
			if index == 0 {
				buffer.WriteString(
					strings.Replace(element.Expression, "?", convertValueToString(element.Value), -1),
				)
			} else {
				buffer.WriteString(element.Delimiter)
				buffer.WriteString(
					strings.Replace(element.Expression, "?", convertValueToString(element.Value), -1),
				)
			}
		}
	}

	if len(query.GroupBy) != 0 {
		buffer.WriteString(" Group By ")
		for index, element := range query.GroupByStruct {
			buffer.WriteString(
				convertValueToString(element.Value),
			)
		}
	}
	buffer.WriteString(";")
	return buffer.String()
}
<commit_msg>change error in BuildQuery<commit_after>package builder

import (
	"bytes"
	"strings"
)

//BuildQuery - get sql string from expression
func (query *Query) BuildQuery() string {
	var buffer bytes.Buffer
	buffer.WriteString(query.TypeQuery)
	buffer.WriteString(" ")
	columns := interfaceToString(query.Columns)
	buffer.WriteString(strings.Join(columns[:], ", "))

	buffer.WriteString(" From ")
	buffer.WriteString(query.TableName)

	if len(query.WhereCond) != 0 {
		buffer.WriteString(" Where ")
		for index, element := range query.WhereCond {
			if index == 0 {
				buffer.WriteString(
					strings.Replace(element.Expression, "?", convertValueToString(element.Value), -1),
				)
			} else {
				buffer.WriteString(element.Delimiter)
				buffer.WriteString(
					strings.Replace(element.Expression, "?", convertValueToString(element.Value), -1),
				)
			}
		}
	}

	if len(query.GroupByStruct) != 0 {
		buffer.WriteString(" Group By ")
		for index, element := range query.GroupByStruct {
			buffer.WriteString(
				convertValueToString(element.Value),
			)
		}
	}
	buffer.WriteString(";")
	return buffer.String()
}
<|endoftext|>


<commit_before>package template

import (
	"fmt"
	"io/ioutil"
	"log"
	"path/filepath"

	"gopkg.in/yaml.v2"

	"github.com/flexiant/digger"
	"github.com/flexiant/kdeploy/utils"
	ymlutil "github.com/ghodss/yaml"
)

// SingleAttributeMetadata holds metadata for a configuration attribute
type SingleAttributeMetadata struct {
	Description string      // Description of the attribute
	Default     interface{} // Default value for the attribute: could be string, number, or bool
	Required    bool        // Required or not
}

// AttributesMetadata holds a whole collection of MetadataAttribute organized into three levels:
// <resource-type>/<resource-name>/<attribute-name> (e.g. "svc/frontend/balancer")
type AttributesMetadata map[string]map[string]map[string]SingleAttributeMetadata

// Metadata holds generic info about the deployment's resources and attributes
type Metadata struct {
	Name                   string
	Maintainer             string
	Email                  string
	Description            string
	Version                string
	Attributes             AttributesMetadata
	ReplicationControllers map[string]string
	Services               map[string]string
	path                   string
}

// ParseMetadata parses the metadata file in the kube dir
func ParseMetadata(path string) Metadata {
	absPath, err := filepath.Abs(path)
	utils.CheckError(err)

	metadataFile := fmt.Sprintf("%s/metadata.yaml", filepath.Clean(absPath))
	utils.CheckError(err)

	metadataContent, err := ioutil.ReadFile(metadataFile)
	utils.CheckError(err)

	var metadata Metadata
	err = yaml.Unmarshal(metadataContent, &metadata)
	utils.CheckError(err)

	metadata.path = filepath.Dir(metadataFile)
	return metadata
}

// RequiredAttributes returns default values for attributes
func (m Metadata) RequiredAttributes() ([]string, error) {
	var reqs = []string{}
	for resourceType, resources := range m.Attributes {
		for resourceName, attributes := range resources {
			for attributeName, attrMetadata := range attributes {
				if attrMetadata.Required {
					reqs = append(reqs, fmt.Sprintf("%s/%s/%s", resourceType, resourceName, attributeName))
				}
			}
		}
	}
	return reqs, nil
}

// CheckRequiredAttributes returns an error if some required attribute is missing
func (m Metadata) CheckRequiredAttributes(attributes digger.Digger) error {
	reqs, err := m.RequiredAttributes()
	if err != nil {
		return fmt.Errorf("could not calculate required attributes: %v", err)
	}
	for _, att := range reqs {
		_, err := attributes.Get(att)
		if err != nil {
			return fmt.Errorf("required attribute not present: '%s'", att)
		}
	}
	return nil
}

// AttributeDefaults returns default values for attributes
func (m Metadata) AttributeDefaults() (digger.Digger, error) {
	defaults, err := defaultsMapFromMetadata(m.Attributes)
	if err != nil {
		return nil, fmt.Errorf("could not build defaults: %v", err)
	}
	digger, err := digger.NewMapDigger(defaults)
	if err != nil {
		return nil, fmt.Errorf("could not build digger: %v", err)
	}
	return digger, nil
}

func defaultsMapFromMetadata(md AttributesMetadata) (interface{}, error) {
	defaults := make(map[string]interface{})

	for resourceType, resources := range md {
		for resourceName, attributes := range resources {
			for attributeName, attrMetadata := range attributes {
				// take default value for the attribute
				val := attrMetadata.Default
				// check if resourceType already in map
				if defaults[resourceType] == nil {
					defaults[resourceType] = make(map[string]interface{})
				}
				// check if resource already in map
				if defaults[resourceType].(map[string]interface{})[resourceName] == nil {
					defaults[resourceType].(map[string]interface{})[resourceName] = make(map[string]interface{})
				}
				defaults[resourceType].(map[string]interface{})[resourceName].(map[string]interface{})[attributeName] = val
			}
		}
	}

	return defaults, nil
}

// ParseServices parses the service templates in the kube and returns their JSON representations
func (m Metadata) ParseServices(attributes digger.Digger) ([]string, error) {
	err := m.CheckRequiredAttributes(attributes)
	if err != nil {
		return nil, err
	}
	return parseTemplates(m.path, m.Services, attributes)
}

// ParseControllers parses the replication controllers in the kube and returns their JSON representations
func (m Metadata) ParseControllers(attributes digger.Digger) ([]string, error) {
	err := m.CheckRequiredAttributes(attributes)
	if err != nil {
		return nil, err
	}
	return parseTemplates(m.path, m.ReplicationControllers, attributes)
}

func parseTemplates(path string, templates map[string]string, attributes digger.Digger) ([]string, error) {
	var specs = []string{}
	for _, templateFile := range templates {
		specYaml, err := ResolveTemplate(fmt.Sprintf("%s/%s", path, templateFile), attributes)
		if err != nil {
			return nil, fmt.Errorf("error resolving template %s: %v", templateFile, err)
		}
		specJSON, err := ymlutil.YAMLToJSON([]byte(specYaml))
		if err != nil {
			log.Printf("yaml:\n%s", specYaml)
			return nil, fmt.Errorf("error converting template %s: %v", templateFile, err)
		}
		specs = append(specs, string(specJSON))
	}
	return specs, nil
}
<commit_msg>Auto label kubeware name<commit_after>package template

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"path/filepath"

	"gopkg.in/yaml.v2"

	"github.com/flexiant/digger"
	"github.com/flexiant/kdeploy/utils"
)

// SingleAttributeMetadata holds metadata for a configuration attribute
type SingleAttributeMetadata struct {
	Description string      // Description of the attribute
	Default     interface{} // Default value for the attribute: could be string, number, or bool
	Required    bool        // Required or not
}

// AttributesMetadata holds a whole collection of MetadataAttribute organized into three levels:
// <resource-type>/<resource-name>/<attribute-name> (e.g. "svc/frontend/balancer")
type AttributesMetadata map[string]map[string]map[string]SingleAttributeMetadata

// Metadata holds generic info about the deployment's resources and attributes
type Metadata struct {
	Name                   string
	Maintainer             string
	Email                  string
	Description            string
	Version                string
	Attributes             AttributesMetadata
	ReplicationControllers map[string]string
	Services               map[string]string
	path                   string
}

// ParseMetadata parses the metadata file in the kube dir
func ParseMetadata(path string) Metadata {
	absPath, err := filepath.Abs(path)
	utils.CheckError(err)

	metadataFile := fmt.Sprintf("%s/metadata.yaml", filepath.Clean(absPath))
	utils.CheckError(err)

	metadataContent, err := ioutil.ReadFile(metadataFile)
	utils.CheckError(err)

	var metadata Metadata
	err = yaml.Unmarshal(metadataContent, &metadata)
	utils.CheckError(err)

	metadata.path = filepath.Dir(metadataFile)
	return metadata
}

// RequiredAttributes returns default values for attributes
func (m Metadata) RequiredAttributes() ([]string, error) {
	var reqs = []string{}
	for resourceType, resources := range m.Attributes {
		for resourceName, attributes := range resources {
			for attributeName, attrMetadata := range attributes {
				if attrMetadata.Required {
					reqs = append(reqs, fmt.Sprintf("%s/%s/%s", resourceType, resourceName, attributeName))
				}
			}
		}
	}
	return reqs, nil
}

// CheckRequiredAttributes returns an error if some required attribute is missing
func (m Metadata) CheckRequiredAttributes(attributes digger.Digger) error {
	reqs, err := m.RequiredAttributes()
	if err != nil {
		return fmt.Errorf("could not calculate required attributes: %v", err)
	}
	for _, att := range reqs {
		_, err := attributes.Get(att)
		if err != nil {
			return fmt.Errorf("required attribute not present: '%s'", att)
		}
	}
	return nil
}

// AttributeDefaults returns default values for attributes
func (m Metadata) AttributeDefaults() (digger.Digger, error) {
	defaults, err := defaultsMapFromMetadata(m.Attributes)
	if err != nil {
		return nil, fmt.Errorf("could not build defaults: %v", err)
	}
	digger, err := digger.NewMapDigger(defaults)
	if err != nil {
		return nil, fmt.Errorf("could not build digger: %v", err)
	}
	return digger, nil
}

func defaultsMapFromMetadata(md AttributesMetadata) (interface{}, error) {
	defaults := make(map[string]interface{})

	for resourceType, resources := range md {
		for resourceName, attributes := range resources {
			for attributeName, attrMetadata := range attributes {
				// take default value for the attribute
				val := attrMetadata.Default
				// check if resourceType already in map
				if defaults[resourceType] == nil {
					defaults[resourceType] = make(map[string]interface{})
				}
				// check if resource already in map
				if defaults[resourceType].(map[string]interface{})[resourceName] == nil {
					defaults[resourceType].(map[string]interface{})[resourceName] = make(map[string]interface{})
				}
				defaults[resourceType].(map[string]interface{})[resourceName].(map[string]interface{})[attributeName] = val
			}
		}
	}

	return defaults, nil
}

// ParseServices parses the service templates in the kube and returns their JSON representations
func (m Metadata) ParseServices(attributes digger.Digger) ([]string, error) {
	err := m.CheckRequiredAttributes(attributes)
	if err != nil {
		return nil, err
	}
	return m.parseTemplates(m.Services, attributes)
}

// ParseControllers parses the replication controllers in the kube and returns their JSON representations
func (m Metadata) ParseControllers(attributes digger.Digger) ([]string, error) {
	err := m.CheckRequiredAttributes(attributes)
	if err != nil {
		return nil, err
	}
	return m.parseTemplates(m.ReplicationControllers, attributes)
}

func (m Metadata) parseTemplates(templates map[string]string, attributes digger.Digger) ([]string, error) {
	var specs = []string{}
	for _, templateFile := range templates {
		specMap, err := parseTemplate(fmt.Sprintf("%s/%s", m.path, templateFile), attributes)
		if err != nil {
			return nil, fmt.Errorf("error parsing template %s: %v", templateFile, err)
		}
		err = addKubewareLabel(m.Name, specMap)
		if err != nil {
			return nil, fmt.Errorf("error adding kubeware labels to %s: %v", templateFile, err)
		}
		specJSON, err := json.Marshal(specMap)
		if err != nil {
			return nil, fmt.Errorf("error marshalling into json (%s): %v", templateFile, err)
		}
		specs = append(specs, string(specJSON))
	}
	return specs, nil
}

func parseTemplate(templateFile string, attributes digger.Digger) (map[string]interface{}, error) {
	specYaml, err := ResolveTemplate(templateFile, attributes)
	if err != nil {
		return nil, fmt.Errorf("error resolving template %s: %v", templateFile, err)
	}
	var specMap map[interface{}]interface{}
	err = yaml.Unmarshal([]byte(specYaml), &specMap)
	if err != nil {
		return nil, fmt.Errorf("error parsing yaml for %s: %v", templateFile, err)
	}
	normalizedMap, err := normalizeValue(specMap)
	return normalizedMap.(map[string]interface{}), nil
}

func addKubewareLabel(name string, specmap map[string]interface{}) error {
	metadata := specmap["metadata"].(map[string]interface{})
	labels := metadata["labels"].(map[string]interface{})
	labels["kubeware"] = name
	return nil
}

func normalizeValue(value interface{}) (interface{}, error) {
	switch value := value.(type) {
	case map[interface{}]interface{}:
		node := make(map[string]interface{}, len(value))
		for k, v := range value {
			key, ok := k.(string)
			if !ok {
				return nil, fmt.Errorf("Unsupported map key: %#v", k)
			}
			item, err := normalizeValue(v)
			if err != nil {
				return nil, fmt.Errorf("Unsupported map value: %#v", v)
			}
			node[key] = item
		}
		return node, nil
	case map[string]interface{}:
		node := make(map[string]interface{}, len(value))
		for key, v := range value {
			item, err := normalizeValue(v)
			if err != nil {
				return nil, fmt.Errorf("Unsupported map value: %#v", v)
			}
			node[key] = item
		}
		return node, nil
	case []interface{}:
		node := make([]interface{}, len(value))
		for key, v := range value {
			item, err := normalizeValue(v)
			if err != nil {
				return nil, fmt.Errorf("Unsupported list item: %#v", v)
			}
			node[key] = item
		}
		return node, nil
	case bool, float64, int, string:
		return value, nil
	}
	return nil, fmt.Errorf("Unsupported type: %T", value)
}
<|endoftext|>


<commit_before>package template

import (
	"bytes"
	"fmt"
	"regexp"
	text_template "text/template"
)

var (
	// $name__filter1__filter2$
	shortFormat = regexp.MustCompile(`\$([a-zA-Z0-9]+(__[a-zA-Z0-9]+)*)\$`)

	// $name;format="filter1,filter2"$
	longFormat = regexp.MustCompile(`\$((\w+);format="([^"]+)")\$`)
)

// converts data on the other end of the reader to a golang template
func Parse(text []byte) (*text_template.Template, error) {
	text = transform(text)

	return text_template.New("template").Funcs(funcMap).Parse(string(text))
}

func Render(text []byte, data interface{}) ([]byte, error) {
	t, err := Parse(text)
	if err != nil {
		return nil, err
	}

	buffer := bytes.NewBuffer([]byte{})
	err = t.Execute(buffer, data)
	if err != nil {
		return nil, err
	}

	return buffer.Bytes(), nil
}

// transform templates in the short format to go templates e.g. name__filter1__filter2
func transformShort(text []byte) []byte {
	results := text
	matches := shortFormat.FindAllSubmatch(results, -1)
	for _, match := range matches {
		segments := bytes.Split(match[1], []byte("__"))
		macro := fmt.Sprintf("{{ .%s }}", bytes.Join(segments, []byte(" | ")))
		results = bytes.Replace(results, match[0], []byte(macro), -1)
	}

	return results
}

// transform templates in the long format to go templates e.g. name;filter="lower,snake"
func transformLong(text []byte) []byte {
	results := text
	matches := longFormat.FindAllSubmatch(results, -1)
	for _, match := range matches {
		field := match[2]
		filters := match[3]

		segments := [][]byte{field}
		segments = append(segments, bytes.Split(filters, []byte(","))...)

		macro := fmt.Sprintf("{{ .%s }}", bytes.Join(segments, []byte(" | ")))
		results = bytes.Replace(results, match[0], []byte(macro), -1)
	}

	return results
}

// helper to combine both short and long transforms
func transform(text []byte) []byte {
	text = transformShort(text)
	text = transformLong(text)
	return text
}
<commit_msg>- added backward compatibility for escaped strings in giter8<commit_after>package template

import (
	"bytes"
	"fmt"
	"regexp"
	text_template "text/template"
)

var (
	// $name__filter1__filter2$
	shortFormat = regexp.MustCompile(`\$([a-zA-Z0-9]+(__[a-zA-Z0-9]+)*)\$`)

	// $name;format="filter1,filter2"$
	longFormat = regexp.MustCompile(`\$((\w+);format="([^"]+)")\$`)
)

// converts data on the other end of the reader to a golang template
func Parse(text []byte) (*text_template.Template, error) {
	text = transform(text)

	return text_template.New("template").Funcs(funcMap).Parse(string(text))
}

func Render(text []byte, data interface{}) ([]byte, error) {
	t, err := Parse(text)
	if err != nil {
		return nil, err
	}

	buffer := bytes.NewBuffer([]byte{})
	err = t.Execute(buffer, data)
	if err != nil {
		return nil, err
	}

	return buffer.Bytes(), nil
}

// transform templates in the short format to go templates e.g. name__filter1__filter2
func transformShort(text []byte) []byte {
	results := text
	matches := shortFormat.FindAllSubmatch(results, -1)
	for _, match := range matches {
		segments := bytes.Split(match[1], []byte("__"))
		macro := fmt.Sprintf("{{ .%s }}", bytes.Join(segments, []byte(" | ")))
		results = bytes.Replace(results, match[0], []byte(macro), -1)
	}

	return results
}

// transform templates in the long format to go templates e.g. name;filter="lower,snake"
func transformLong(text []byte) []byte {
	results := text
	matches := longFormat.FindAllSubmatch(results, -1)
	for _, match := range matches {
		field := match[2]
		filters := match[3]

		segments := [][]byte{field}
		segments = append(segments, bytes.Split(filters, []byte(","))...)

		macro := fmt.Sprintf("{{ .%s }}", bytes.Join(segments, []byte(" | ")))
		results = bytes.Replace(results, match[0], []byte(macro), -1)
	}

	return results
}

// helper to combine both short and long transforms
func transform(text []byte) []byte {
	text = giter8ify(text)
	text = transformShort(text)
	text = transformLong(text)
	return text
}
<|endoftext|>


<commit_before>// 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.

// Binary btbolt injects the btbolt driver into the bw command line tool.
package main

import (
	"flag"
	"io"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"strings"
	"time"

	"github.com/boltdb/bolt"
	"github.com/google/badwolf/storage"
	"github.com/google/badwolf/storage/memory"
	"github.com/google/badwolf/tools/vcli/bw/common"
	"github.com/google/badwolf/tools/vcli/bw/repl"
	"github.com/google/badwolf/triple/literal"
	"github.com/peterh/liner"
	"github.com/xllora/bwdrivers/bwbolt"
)

var (
	// drivers contains the registed drivers available for this command line tool.
	registeredDrivers map[string]common.StoreGenerator

	// Available flags.
	driverName = flag.String("driver", "VOLATILE", "The storage driver to use {VOLATILE|BWBOLT}.")

	bqlChannelSize        = flag.Int("bql_channel_size", 0, "Internal channel size to use on BQL queries.")
	bulkTripleOpSize      = flag.Int("bulk_triple_op_size", 1000, "Number of triples to use in bulk load operations.")
	bulkTripleBuildersize = flag.Int("bulk_triple_builder_size_in_bytes", 1000, "Maximum size of literals when parsing a triple.")

	// Add your driver flags below.

	// BwBolt driver.
	boltDBPath   = flag.String("bolt_db_path", "", "The path to the Bolt database to use.")
	boldTimeout  = flag.Duration("bolt_db_timeout", 3*time.Second, "The duration of the timeout while opening the Bolt database.")
	boltReadOnly = flag.Bool("bolt_db_read_only", false, "Use te Bolt DB only in read only mode.")

	// Driver specific variables.
	db *bolt.DB
)

// Registers the available drivers.
func registerDrivers() {
	registeredDrivers = map[string]common.StoreGenerator{
		// Memory only storage driver.
		"VOLATILE": func() (storage.Store, error) {
			return memory.NewStore(), nil
		},
		"BWBOLT": func() (storage.Store, error) {
			s, bdb, err := bwbolt.New(*boltDBPath, literal.DefaultBuilder(), *boldTimeout, *boltReadOnly)
			db = bdb
			return s, err
		},
	}
}

// Advanced line editing.
func NewAdvancedReadLiner() repl.ReadLiner {
	return func(done chan bool) <-chan string {
		line, c := liner.NewLiner(), make(chan string)
		usr, err := user.Current()
		if err != nil {
			log.Fatal(err)
		}
		historyFile := filepath.Join(usr.HomeDir, ".badwolf_history")
		go func() {
			defer close(c)
			defer line.Close()

			// Load the history.
			line.SetCtrlCAborts(true)
			line.SetTabCompletionStyle(liner.TabCircular)

			f, err := os.OpenFile(historyFile, os.O_RDONLY, 0600)
			if err != nil {
				f, err = os.OpenFile(historyFile, os.O_CREATE, 0600)
				if err != nil {
					panic("Cannot open history file in " + historyFile)
				}
			}
			line.ReadHistory(f)
			f.Close()

			// Run the read loop.
			cmd := ""
			for {
				text, err := line.Prompt("bql> ")
				if err == liner.ErrPromptAborted || err == io.EOF {
					break
				}
				cmd = strings.TrimSpace(cmd + " " + strings.TrimSpace(text))
				if strings.HasSuffix(cmd, ";") {
					c <- cmd
					line.AppendHistory(cmd)
					if <-done {
						break
					}
					cmd = ""
				}
			}

			// Write the history.
			f, err = os.Create(historyFile)
			if err != nil {
				panic("Cannot rewrite history to " + historyFile + "; " + err.Error())
			}
			defer f.Close()
			line.WriteHistory(f)
		}()
		return c
	}
}

func main() {
	flag.Parse()
	registerDrivers()
	ret := common.Run(*driverName, flag.Args(), registeredDrivers, *bqlChannelSize, *bulkTripleOpSize, *bulkTripleBuildersize, NewAdvancedReadLiner())
	// Clean up.
	if db != nil {
		db.Close()
	}
	os.Exit(ret)
}
<commit_msg>Fix migration typo<commit_after>// 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.

// Binary btbolt injects the btbolt driver into the bw command line tool.
package main

import (
	"flag"
	"io"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"strings"
	"time"

	"github.com/boltdb/bolt"
	"github.com/google/badwolf-drivers/bwbolt"
	"github.com/google/badwolf/storage"
	"github.com/google/badwolf/storage/memory"
	"github.com/google/badwolf/tools/vcli/bw/common"
	"github.com/google/badwolf/tools/vcli/bw/repl"
	"github.com/google/badwolf/triple/literal"
	"github.com/peterh/liner"
)

var (
	// drivers contains the registed drivers available for this command line tool.
	registeredDrivers map[string]common.StoreGenerator

	// Available flags.
	driverName = flag.String("driver", "VOLATILE", "The storage driver to use {VOLATILE|BWBOLT}.")

	bqlChannelSize        = flag.Int("bql_channel_size", 0, "Internal channel size to use on BQL queries.")
	bulkTripleOpSize      = flag.Int("bulk_triple_op_size", 1000, "Number of triples to use in bulk load operations.")
	bulkTripleBuildersize = flag.Int("bulk_triple_builder_size_in_bytes", 1000, "Maximum size of literals when parsing a triple.")

	// Add your driver flags below.

	// BwBolt driver.
	boltDBPath   = flag.String("bolt_db_path", "", "The path to the Bolt database to use.")
	boldTimeout  = flag.Duration("bolt_db_timeout", 3*time.Second, "The duration of the timeout while opening the Bolt database.")
	boltReadOnly = flag.Bool("bolt_db_read_only", false, "Use te Bolt DB only in read only mode.")

	// Driver specific variables.
	db *bolt.DB
)

// Registers the available drivers.
func registerDrivers() {
	registeredDrivers = map[string]common.StoreGenerator{
		// Memory only storage driver.
		"VOLATILE": func() (storage.Store, error) {
			return memory.NewStore(), nil
		},
		"BWBOLT": func() (storage.Store, error) {
			s, bdb, err := bwbolt.New(*boltDBPath, literal.DefaultBuilder(), *boldTimeout, *boltReadOnly)
			db = bdb
			return s, err
		},
	}
}

// Advanced line editing.
func NewAdvancedReadLiner() repl.ReadLiner {
	return func(done chan bool) <-chan string {
		line, c := liner.NewLiner(), make(chan string)
		usr, err := user.Current()
		if err != nil {
			log.Fatal(err)
		}
		historyFile := filepath.Join(usr.HomeDir, ".badwolf_history")
		go func() {
			defer close(c)
			defer line.Close()

			// Load the history.
			line.SetCtrlCAborts(true)
			line.SetTabCompletionStyle(liner.TabCircular)

			f, err := os.OpenFile(historyFile, os.O_RDONLY, 0600)
			if err != nil {
				f, err = os.OpenFile(historyFile, os.O_CREATE, 0600)
				if err != nil {
					panic("Cannot open history file in " + historyFile)
				}
			}
			line.ReadHistory(f)
			f.Close()

			// Run the read loop.
			cmd := ""
			for {
				text, err := line.Prompt("bql> ")
				if err == liner.ErrPromptAborted || err == io.EOF {
					break
				}
				cmd = strings.TrimSpace(cmd + " " + strings.TrimSpace(text))
				if strings.HasSuffix(cmd, ";") {
					c <- cmd
					line.AppendHistory(cmd)
					if <-done {
						break
					}
					cmd = ""
				}
			}

			// Write the history.
			f, err = os.Create(historyFile)
			if err != nil {
				panic("Cannot rewrite history to " + historyFile + "; " + err.Error())
			}
			defer f.Close()
			line.WriteHistory(f)
		}()
		return c
	}
}

func main() {
	flag.Parse()
	registerDrivers()
	ret := common.Run(*driverName, flag.Args(), registeredDrivers, *bqlChannelSize, *bulkTripleOpSize, *bulkTripleBuildersize, NewAdvancedReadLiner())
	// Clean up.
	if db != nil {
		db.Close()
	}
	os.Exit(ret)
}
<|endoftext|>


<commit_before>// Copyright 2015 ChaiShushan <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package webp

//#include "webp.h"
import "C"
import (
	"errors"
	"io"
	"reflect"
	"unsafe"
)

var (
	_ CBuffer = (*cBuffer)(nil)
)

type CBuffer interface {
	CBufMagic() string
	CanResize() bool
	Resize(size int) error
	CData() []byte
	Own(d []byte) bool
	io.Closer
}

type cBuffer struct {
	dontResize bool
	cptr       unsafe.Pointer
	data       []byte
}

func NewCBuffer(size int, dontResize ...bool) CBuffer {
	p := new(cBuffer)
	if size > 0 {
		p.cptr = C.webpMalloc(C.size_t(size))
		p.data = (*[1 << 30]byte)(p.cptr)[0:size:size]
	}
	if len(dontResize) > 0 {
		p.dontResize = dontResize[0]
	}
	return p
}

func (p *cBuffer) CBufMagic() string {
	return "CBufMagic"
}

func (p *cBuffer) Close() error {
	if p.cptr != nil {
		C.webpFree(p.cptr)
	}
	p.cptr = nil
	p.data = nil
	return nil
}

func (p *cBuffer) CanResize() bool {
	return !p.dontResize
}

func (p *cBuffer) Resize(size int) error {
	if size < 0 {
		return errors.New("webp: cBuffer.Resize, bad size!")
	}
	if p.dontResize {
		return errors.New("webp: cBuffer.Resize, donot resize!")
	}
	p.Close()
	if size > 0 {
		p.cptr = C.webpMalloc(C.size_t(size))
		p.data = (*[1 << 30]byte)(p.cptr)[0:size:size]
	}
	return nil
}

func (p *cBuffer) CData() []byte {
	return p.data
}

func (p *cBuffer) Own(d []byte) bool {
	if cap(d) == 0 {
		return false
	}
	hdr := (*reflect.SliceHeader)(unsafe.Pointer(&d))
	if a, b := hdr.Data, uintptr(p.cptr); a < b || a >= b {
		return false
	}
	if a, b := hdr.Data+uintptr(hdr.Cap), uintptr(p.cptr); a < b || a >= b {
		return false
	}
	return true
}
<commit_msg>fix CBuffer.Own<commit_after>// Copyright 2015 ChaiShushan <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package webp

//#include "webp.h"
import "C"
import (
	"errors"
	"io"
	"reflect"
	"unsafe"
)

var (
	_ CBuffer = (*cBuffer)(nil)
)

type CBuffer interface {
	CBufMagic() string
	CanResize() bool
	Resize(size int) error
	CData() []byte
	Own(d []byte) bool
	io.Closer
}

type cBuffer struct {
	dontResize bool
	cptr       unsafe.Pointer
	data       []byte
}

func NewCBuffer(size int, dontResize ...bool) CBuffer {
	p := new(cBuffer)
	if size > 0 {
		p.cptr = C.webpMalloc(C.size_t(size))
		p.data = (*[1 << 30]byte)(p.cptr)[0:size:size]
	}
	if len(dontResize) > 0 {
		p.dontResize = dontResize[0]
	}
	return p
}

func (p *cBuffer) CBufMagic() string {
	return "CBufMagic"
}

func (p *cBuffer) Close() error {
	if p.cptr != nil {
		C.webpFree(p.cptr)
	}
	p.cptr = nil
	p.data = nil
	return nil
}

func (p *cBuffer) CanResize() bool {
	return !p.dontResize
}

func (p *cBuffer) Resize(size int) error {
	if size < 0 {
		return errors.New("webp: cBuffer.Resize, bad size!")
	}
	if p.dontResize {
		return errors.New("webp: cBuffer.Resize, donot resize!")
	}
	p.Close()
	if size > 0 {
		p.cptr = C.webpMalloc(C.size_t(size))
		p.data = (*[1 << 30]byte)(p.cptr)[0:size:size]
	}
	return nil
}

func (p *cBuffer) CData() []byte {
	return p.data
}

func (p *cBuffer) Own(d []byte) bool {
	if cap(d) == 0 || p.cptr == nil {
		return false
	}
	min := uintptr(p.cptr)
	max := uintptr(p.cptr) + uintptr(len(p.data)-1)
	hdr := (*reflect.SliceHeader)(unsafe.Pointer(&d))
	if x := hdr.Data; x < min || x > max {
		return false
	}
	if x := hdr.Data + uintptr(hdr.Cap-1); x < min || x > max {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/gin-gonic/gin"
	"github.com/jessevdk/go-flags"
	"github.com/robbiet480/cec"
	"os"
	"strings"
)

type Options struct {
	Host    string `short:"i" long:"ip" description:"ip to listen on" default:"127.0.0.1"`
	Port    string `short:"p" long:"port" description:"tcp port to listen on" default:"8080"`
	Adapter string `short:"a" long:"adapter" description:"cec adapter to connect to [RPI, usb, ...]"`
	Name    string `short:"n" long:"name" description:"OSD name to announce on the cec bus" default:"REST Gateway"`
	Type    string `short:"t" long:"type" description:"The device type to register as" default:"tuner"`
}

var options Options
var parser = flags.NewParser(&options, flags.Default)

func main() {
	if _, err := parser.Parse(); err != nil {
		os.Exit(1)
	}

	cec.Open(options.Adapter, options.Name, options.Type)

	r := gin.Default()
	r.GET("/info", info)
	r.GET("/source", source_status)
	r.GET("/power/:device", power_status)
	r.PUT("/power/:device", power_on)
	r.DELETE("/power/:device", power_off)
	r.PUT("/volume/up", vol_up)
	r.PUT("/volume/down", vol_down)
	r.PUT("/volume/mute", vol_mute)
	r.PUT("/key/:device/:key", key)
	r.PUT("/channel/:device/:channel", change_channel)
	r.POST("/transmit", transmit)

	r.Run(options.Host + ":" + options.Port)
}

func info(c *gin.Context) {
	c.JSON(200, cec.List())
}

func power_on(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))

	cec.PowerOn(addr)
	c.String(200, "on")
}

func power_off(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))

	cec.Standby(addr)
	c.String(200, "off")
}

func power_status(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))

	status := cec.GetDevicePowerStatus(addr)
	if status == "on" {
		c.String(200, "on")
	} else if status == "standby" {
		c.String(200, "off")
	} else {
		c.String(500, "invalid power state")
	}
}

func source_status(c *gin.Context) {
	active_devices := cec.GetActiveDevices()

	for address, active := range active_devices {
		if (active) && (cec.IsActiveSource(address)) {
			c.String(200, "INPUT HDMI "+strings.Split(cec.GetDevicePhysicalAddress(address), ".")[0])
		}
	}
}

func change_channel(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))
	channel := c.Params.ByName("channel")

	for _, number := range channel {
		cec.Key(addr, "0x2"+string(number))
	}

	c.String(200, channel)
}

func transmit(c *gin.Context) {
	var commands []string
	c.Bind(&commands)

	for _, val := range commands {
		cec.Transmit(val)
	}
	c.String(204, "")
}

func vol_up(c *gin.Context) {
	cec.VolumeUp()
	c.String(204, "")
}

func vol_down(c *gin.Context) {
	cec.VolumeDown()
	c.String(204, "")
}

func vol_mute(c *gin.Context) {
	cec.Mute()
	c.String(204, "")
}

func key(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))
	key := c.Params.ByName("key")

	cec.Key(addr, key)
	c.String(204, "")
}
<commit_msg>Add the volume step endpoint<commit_after>package main

import (
	"github.com/gin-gonic/gin"
	"github.com/jessevdk/go-flags"
	"github.com/robbiet480/cec"
	"os"
	"strconv"
	"strings"
)

type Options struct {
	Host    string `short:"i" long:"ip" description:"ip to listen on" default:"127.0.0.1"`
	Port    string `short:"p" long:"port" description:"tcp port to listen on" default:"8080"`
	Adapter string `short:"a" long:"adapter" description:"cec adapter to connect to [RPI, usb, ...]"`
	Name    string `short:"n" long:"name" description:"OSD name to announce on the cec bus" default:"REST Gateway"`
	Type    string `short:"t" long:"type" description:"The device type to register as" default:"tuner"`
}

var options Options
var parser = flags.NewParser(&options, flags.Default)

func main() {
	if _, err := parser.Parse(); err != nil {
		os.Exit(1)
	}

	cec.Open(options.Adapter, options.Name, options.Type)

	r := gin.Default()
	r.GET("/info", info)
	r.GET("/source", source_status)
	r.GET("/power/:device", power_status)
	r.PUT("/power/:device", power_on)
	r.DELETE("/power/:device", power_off)
	r.PUT("/volume/up", vol_up)
	r.PUT("/volume/down", vol_down)
	r.PUT("/volume/mute", vol_mute)
	r.PUT("/volume/step/:direction/:steps", vol_step)
	r.PUT("/key/:device/:key", key)
	r.PUT("/channel/:device/:channel", change_channel)
	r.POST("/transmit", transmit)

	r.Run(options.Host + ":" + options.Port)
}

func info(c *gin.Context) {
	c.JSON(200, cec.List())
}

func power_on(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))

	cec.PowerOn(addr)
	c.String(200, "on")
}

func power_off(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))

	cec.Standby(addr)
	c.String(200, "off")
}

func power_status(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))

	status := cec.GetDevicePowerStatus(addr)
	if status == "on" {
		c.String(200, "on")
	} else if status == "standby" {
		c.String(200, "off")
	} else {
		c.String(500, "invalid power state")
	}
}

func source_status(c *gin.Context) {
	active_devices := cec.GetActiveDevices()

	for address, active := range active_devices {
		if (active) && (cec.IsActiveSource(address)) {
			c.String(200, "INPUT HDMI "+strings.Split(cec.GetDevicePhysicalAddress(address), ".")[0])
		}
	}
}

func change_channel(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))
	channel := c.Params.ByName("channel")

	for _, number := range channel {
		cec.Key(addr, "0x2"+string(number))
	}

	c.String(200, channel)
}

func vol_step(c *gin.Context) {
	steps_str := c.Params.ByName("steps")
	steps_atoi, _ := strconv.Atoi(steps_str)
	steps := int(steps_atoi)
	direction := c.Params.ByName("direction")

	for i := 1; i < steps; i++ {
		if direction == "up" {
			cec.VolumeUp()
		} else if direction == "down" {
			cec.VolumeDown()
		} else {
			c.String(400, "Invalid direction. Valid directions are up or down.")
		}
	}

	c.String(204, "")
}

func transmit(c *gin.Context) {
	var commands []string
	c.Bind(&commands)

	for _, val := range commands {
		cec.Transmit(val)
	}
	c.String(204, "")
}

func vol_up(c *gin.Context) {
	cec.VolumeUp()
	c.String(204, "")
}

func vol_down(c *gin.Context) {
	cec.VolumeDown()
	c.String(204, "")
}

func vol_mute(c *gin.Context) {
	cec.Mute()
	c.String(204, "")
}

func key(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))
	key := c.Params.ByName("key")

	cec.Key(addr, key)
	c.String(204, "")
}
<|endoftext|>