<commit_before>package channeldb

import (
	"bytes"
	"crypto/sha256"
	"fmt"
	"io"
	"time"

	"github.com/boltdb/bolt"
	"github.com/lightningnetwork/lnd/lnwire"
	"github.com/roasbeef/btcd/wire"
)

var (
	// invoiceBucket is the name of the bucket within the database that
	// stores all data related to invoices no matter their final state.
	// Within the invoice bucket, each invoice is keyed by its invoice ID
	// which is a monotonically increasing uint32.
	invoiceBucket = []byte("invoices")

	// paymentHashIndexBucket is the name of the sub-bucket within the
	// invoiceBucket which indexes all invoices by their payment hash. The
	// payment hash is the sha256 of the invoice's payment preimage. This
	// index is used to detect duplicates, and also to provide a fast path
	// for looking up incoming HTLCs to determine if we're able to settle
	// them fully.
	invoiceIndexBucket = []byte("paymenthashes")

	// numInvoicesKey is the name of key which houses the auto-incrementing
	// invoice ID which is essentially used as a primary key. With each
	// invoice inserted, the primary key is incremented by one. This key is
	// stored within the invoiceIndexBucket. Within the invoiceBucket
	// invoices are uniquely identified by the invoice ID.
	numInvoicesKey = []byte("nik")
)

const (
	// MaxMemoSize is maximum size of the memo field within invoices stored
	// in the database.
	MaxMemoSize = 1024

	// MaxReceiptSize is the maximum size of the payment receipt stored
	// within the database along side incoming/outgoing invoices.
	MaxReceiptSize = 1024

	// MaxPaymentRequestSize is the max size of a a payment request for
	// this invoice.
	// TODO(halseth): determine the max length payment request when field
	// lengths are final.
	MaxPaymentRequestSize = 4096
)

// ContractTerm is a companion struct to the Invoice struct. This struct houses
// the necessary conditions required before the invoice can be considered fully
// settled by the payee.
type ContractTerm struct {
	// PaymentPreimage is the preimage which is to be revealed in the
	// occasion that an HTLC paying to the hash of this preimage is
	// extended.
	PaymentPreimage [32]byte

	// Value is the expected amount of milli-satoshis to be payed to an
	// HTLC which can be satisfied by the above preimage.
	Value lnwire.MilliSatoshi

	// Settled indicates if this particular contract term has been fully
	// settled by the payer.
	Settled bool
}

// Invoice is a payment invoice generated by a payee in order to request
// payment for some good or service. The inclusion of invoices within Lightning
// creates a payment work flow for merchants very similar to that of the
// existing financial system within PayPal, etc.  Invoices are added to the
// database when a payment is requested, then can be settled manually once the
// payment is received at the upper layer. For record keeping purposes,
// invoices are never deleted from the database, instead a bit is toggled
// denoting the invoice has been fully settled. Within the database, all
// invoices must have a unique payment hash which is generated by taking the
// sha256 of the payment
// preimage.
type Invoice struct {
	// Memo is an optional memo to be stored along side an invoice.  The
	// memo may contain further details pertaining to the invoice itself,
	// or any other message which fits within the size constraints.
	Memo []byte

	// Receipt is an optional field dedicated for storing a
	// cryptographically binding receipt of payment.
	//
	// TODO(roasbeef): document scheme.
	Receipt []byte

	// PaymentRequest is an optional field where a payment request created
	// for this invoice can be stored.
	PaymentRequest []byte

	// CreationDate is the exact time the invoice was created.
	CreationDate time.Time

	// Terms are the contractual payment terms of the invoice. Once
	// all the terms have been satisfied by the payer, then the invoice can
	// be considered fully fulfilled.
	//
	// TODO(roasbeef): later allow for multiple terms to fulfill the final
	// invoice: payment fragmentation, etc.
	Terms ContractTerm
}

func validateInvoice(i *Invoice) error {
	if len(i.Memo) > MaxMemoSize {
		return fmt.Errorf("max length a memo is %v, and invoice "+
			"of length %v was provided", MaxMemoSize, len(i.Memo))
	}
	if len(i.Receipt) > MaxReceiptSize {
		return fmt.Errorf("max length a receipt is %v, and invoice "+
			"of length %v was provided", MaxReceiptSize,
			len(i.Receipt))
	}
	if len(i.PaymentRequest) > MaxPaymentRequestSize {
		return fmt.Errorf("max length of payment request is %v, length "+
			"provided was %v", MaxPaymentRequestSize,
			len(i.PaymentRequest))
	}
	return nil
}

// AddInvoice inserts the targeted invoice into the database. If the invoice
// has *any* payment hashes which already exists within the database, then the
// insertion will be aborted and rejected due to the strict policy banning any
// duplicate payment hashes.
func (d *DB) AddInvoice(i *Invoice) error {
	if err := validateInvoice(i); err != nil {
		return err
	}
	return d.Update(func(tx *bolt.Tx) error {
		invoices, err := tx.CreateBucketIfNotExists(invoiceBucket)
		if err != nil {
			return err
		}

		invoiceIndex, err := invoices.CreateBucketIfNotExists(invoiceIndexBucket)
		if err != nil {
			return err
		}

		// Ensure that an invoice an identical payment hash doesn't
		// already exist within the index.
		paymentHash := sha256.Sum256(i.Terms.PaymentPreimage[:])
		if invoiceIndex.Get(paymentHash[:]) != nil {
			return ErrDuplicateInvoice
		}

		// If the current running payment ID counter hasn't yet been
		// created, then create it now.
		var invoiceNum uint32
		invoiceCounter := invoiceIndex.Get(numInvoicesKey)
		if invoiceCounter == nil {
			var scratch [4]byte
			byteOrder.PutUint32(scratch[:], invoiceNum)
			if err := invoiceIndex.Put(numInvoicesKey, scratch[:]); err != nil {
				return nil
			}
		} else {
			invoiceNum = byteOrder.Uint32(invoiceCounter)
		}

		return putInvoice(invoices, invoiceIndex, i, invoiceNum)
	})
}

// LookupInvoice attempts to look up an invoice according to it's 32 byte
// payment hash. In an invoice which can settle the HTLC identified by the
// passed payment hash isn't found, then an error is returned. Otherwise, the
// full invoice is returned. Before setting the incoming HTLC, the values
// SHOULD be checked to ensure the payer meets the agreed upon contractual
// terms of the payment.
func (d *DB) LookupInvoice(paymentHash [32]byte) (*Invoice, error) {
	var invoice *Invoice
	err := d.View(func(tx *bolt.Tx) error {
		invoices := tx.Bucket(invoiceBucket)
		if invoices == nil {
			return ErrNoInvoicesCreated
		}
		invoiceIndex := invoices.Bucket(invoiceIndexBucket)
		if invoiceIndex == nil {
			return ErrNoInvoicesCreated
		}

		// Check the invoice index to see if an invoice paying to this
		// hash exists within the DB.
		invoiceNum := invoiceIndex.Get(paymentHash[:])
		if invoiceNum == nil {
			return ErrInvoiceNotFound
		}

		// An invoice matching the payment hash has been found, so
		// retrieve the record of the invoice itself.
		i, err := fetchInvoice(invoiceNum, invoices)
		if err != nil {
			return err
		}
		invoice = i

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

	return invoice, nil
}

// FetchAllInvoices returns all invoices currently stored within the database.
// If the pendingOnly param is true, then only unsettled invoices will be
// returned, skipping all invoices that are fully settled.
func (d *DB) FetchAllInvoices(pendingOnly bool) ([]*Invoice, error) {
	var invoices []*Invoice

	err := d.View(func(tx *bolt.Tx) error {
		invoiceB := tx.Bucket(invoiceBucket)
		if invoiceB == nil {
			return ErrNoInvoicesCreated
		}

		// Iterate through the entire key space of the top-level
		// invoice bucket. If key with a non-nil value stores the next
		// invoice ID which maps to the corresponding invoice.
		return invoiceB.ForEach(func(k, v []byte) error {
			if v == nil {
				return nil
			}

			invoiceReader := bytes.NewReader(v)
			invoice, err := deserializeInvoice(invoiceReader)
			if err != nil {
				return err
			}

			if pendingOnly && invoice.Terms.Settled {
				return nil
			}

			invoices = append(invoices, invoice)

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

	return invoices, nil
}

// SettleInvoice attempts to mark an invoice corresponding to the passed
// payment hash as fully settled. If an invoice matching the passed payment
// hash doesn't existing within the database, then the action will fail with a
// "not found" error.
func (d *DB) SettleInvoice(paymentHash [32]byte) error {
	return d.Update(func(tx *bolt.Tx) error {
		invoices, err := tx.CreateBucketIfNotExists(invoiceBucket)
		if err != nil {
			return err
		}
		invoiceIndex, err := invoices.CreateBucketIfNotExists(invoiceIndexBucket)
		if err != nil {
			return err
		}

		// Check the invoice index to see if an invoice paying to this
		// hash exists within the DB.
		invoiceNum := invoiceIndex.Get(paymentHash[:])
		if invoiceNum == nil {
			return ErrInvoiceNotFound
		}

		return settleInvoice(invoices, invoiceNum)
	})
}

func putInvoice(invoices *bolt.Bucket, invoiceIndex *bolt.Bucket,
	i *Invoice, invoiceNum uint32) error {

	// Create the invoice key which is just the big-endian representation
	// of the invoice number.
	var invoiceKey [4]byte
	byteOrder.PutUint32(invoiceKey[:], invoiceNum)

	// Increment the num invoice counter index so the next invoice bares
	// the proper ID.
	var scratch [4]byte
	invoiceCounter := invoiceNum + 1
	byteOrder.PutUint32(scratch[:], invoiceCounter)
	if err := invoiceIndex.Put(numInvoicesKey, scratch[:]); err != nil {
		return err
	}

	// Add the payment hash to the invoice index. This'll let us quickly
	// identify if we can settle an incoming payment, and also to possibly
	// allow a single invoice to have multiple payment installations.
	paymentHash := sha256.Sum256(i.Terms.PaymentPreimage[:])
	if err := invoiceIndex.Put(paymentHash[:], invoiceKey[:]); err != nil {
		return err
	}

	// Finally, serialize the invoice itself to be written to the disk.
	var buf bytes.Buffer
	if err := serializeInvoice(&buf, i); err != nil {
		return nil
	}

	return invoices.Put(invoiceKey[:], buf.Bytes())
}

func serializeInvoice(w io.Writer, i *Invoice) error {
	if err := wire.WriteVarBytes(w, 0, i.Memo[:]); err != nil {
		return err
	}
	if err := wire.WriteVarBytes(w, 0, i.Receipt[:]); err != nil {
		return err
	}
	if err := wire.WriteVarBytes(w, 0, i.PaymentRequest[:]); err != nil {
		return err
	}

	birthBytes, err := i.CreationDate.MarshalBinary()
	if err != nil {
		return err
	}
	if err := wire.WriteVarBytes(w, 0, birthBytes); err != nil {
		return err
	}

	if _, err := w.Write(i.Terms.PaymentPreimage[:]); err != nil {
		return err
	}

	var scratch [8]byte
	byteOrder.PutUint64(scratch[:], uint64(i.Terms.Value))
	if _, err := w.Write(scratch[:]); err != nil {
		return err
	}

	var settleByte [1]byte
	if i.Terms.Settled {
		settleByte[0] = 1
	}
	if _, err := w.Write(settleByte[:]); err != nil {
		return err
	}

	return nil
}

func fetchInvoice(invoiceNum []byte, invoices *bolt.Bucket) (*Invoice, error) {
	invoiceBytes := invoices.Get(invoiceNum)
	if invoiceBytes == nil {
		return nil, ErrInvoiceNotFound
	}

	invoiceReader := bytes.NewReader(invoiceBytes)

	return deserializeInvoice(invoiceReader)
}

func deserializeInvoice(r io.Reader) (*Invoice, error) {
	var err error
	invoice := &Invoice{}

	// TODO(roasbeef): use read full everywhere
	invoice.Memo, err = wire.ReadVarBytes(r, 0, MaxMemoSize, "")
	if err != nil {
		return nil, err
	}
	invoice.Receipt, err = wire.ReadVarBytes(r, 0, MaxReceiptSize, "")
	if err != nil {
		return nil, err
	}

	invoice.PaymentRequest, err = wire.ReadVarBytes(r, 0, MaxPaymentRequestSize, "")
	if err != nil {
		return nil, err
	}

	birthBytes, err := wire.ReadVarBytes(r, 0, 300, "birth")
	if err != nil {
		return nil, err
	}
	if err := invoice.CreationDate.UnmarshalBinary(birthBytes); err != nil {
		return nil, err
	}

	if _, err := io.ReadFull(r, invoice.Terms.PaymentPreimage[:]); err != nil {
		return nil, err
	}
	var scratch [8]byte
	if _, err := io.ReadFull(r, scratch[:]); err != nil {
		return nil, err
	}
	invoice.Terms.Value = lnwire.MilliSatoshi(byteOrder.Uint64(scratch[:]))

	var settleByte [1]byte
	if _, err := io.ReadFull(r, settleByte[:]); err != nil {
		return nil, err
	}
	if settleByte[0] == 1 {
		invoice.Terms.Settled = true
	}

	return invoice, nil
}

func settleInvoice(invoices *bolt.Bucket, invoiceNum []byte) error {
	invoice, err := fetchInvoice(invoiceNum, invoices)
	if err != nil {
		return err
	}

	invoice.Terms.Settled = true

	var buf bytes.Buffer
	if err := serializeInvoice(&buf, invoice); err != nil {
		return nil
	}

	// TODO(roasbeef): add timestamp

	return invoices.Put(invoiceNum[:], buf.Bytes())
}
<commit_msg>channeldb: use binary.Read/Write in invoices.go<commit_after>package channeldb

import (
	"bytes"
	"crypto/sha256"
	"encoding/binary"
	"fmt"
	"io"
	"time"

	"github.com/boltdb/bolt"
	"github.com/lightningnetwork/lnd/lnwire"
	"github.com/roasbeef/btcd/wire"
)

var (
	// invoiceBucket is the name of the bucket within the database that
	// stores all data related to invoices no matter their final state.
	// Within the invoice bucket, each invoice is keyed by its invoice ID
	// which is a monotonically increasing uint32.
	invoiceBucket = []byte("invoices")

	// paymentHashIndexBucket is the name of the sub-bucket within the
	// invoiceBucket which indexes all invoices by their payment hash. The
	// payment hash is the sha256 of the invoice's payment preimage. This
	// index is used to detect duplicates, and also to provide a fast path
	// for looking up incoming HTLCs to determine if we're able to settle
	// them fully.
	invoiceIndexBucket = []byte("paymenthashes")

	// numInvoicesKey is the name of key which houses the auto-incrementing
	// invoice ID which is essentially used as a primary key. With each
	// invoice inserted, the primary key is incremented by one. This key is
	// stored within the invoiceIndexBucket. Within the invoiceBucket
	// invoices are uniquely identified by the invoice ID.
	numInvoicesKey = []byte("nik")
)

const (
	// MaxMemoSize is maximum size of the memo field within invoices stored
	// in the database.
	MaxMemoSize = 1024

	// MaxReceiptSize is the maximum size of the payment receipt stored
	// within the database along side incoming/outgoing invoices.
	MaxReceiptSize = 1024

	// MaxPaymentRequestSize is the max size of a a payment request for
	// this invoice.
	// TODO(halseth): determine the max length payment request when field
	// lengths are final.
	MaxPaymentRequestSize = 4096
)

// ContractTerm is a companion struct to the Invoice struct. This struct houses
// the necessary conditions required before the invoice can be considered fully
// settled by the payee.
type ContractTerm struct {
	// PaymentPreimage is the preimage which is to be revealed in the
	// occasion that an HTLC paying to the hash of this preimage is
	// extended.
	PaymentPreimage [32]byte

	// Value is the expected amount of milli-satoshis to be payed to an
	// HTLC which can be satisfied by the above preimage.
	Value lnwire.MilliSatoshi

	// Settled indicates if this particular contract term has been fully
	// settled by the payer.
	Settled bool
}

// Invoice is a payment invoice generated by a payee in order to request
// payment for some good or service. The inclusion of invoices within Lightning
// creates a payment work flow for merchants very similar to that of the
// existing financial system within PayPal, etc.  Invoices are added to the
// database when a payment is requested, then can be settled manually once the
// payment is received at the upper layer. For record keeping purposes,
// invoices are never deleted from the database, instead a bit is toggled
// denoting the invoice has been fully settled. Within the database, all
// invoices must have a unique payment hash which is generated by taking the
// sha256 of the payment
// preimage.
type Invoice struct {
	// Memo is an optional memo to be stored along side an invoice.  The
	// memo may contain further details pertaining to the invoice itself,
	// or any other message which fits within the size constraints.
	Memo []byte

	// Receipt is an optional field dedicated for storing a
	// cryptographically binding receipt of payment.
	//
	// TODO(roasbeef): document scheme.
	Receipt []byte

	// PaymentRequest is an optional field where a payment request created
	// for this invoice can be stored.
	PaymentRequest []byte

	// CreationDate is the exact time the invoice was created.
	CreationDate time.Time

	// Terms are the contractual payment terms of the invoice. Once
	// all the terms have been satisfied by the payer, then the invoice can
	// be considered fully fulfilled.
	//
	// TODO(roasbeef): later allow for multiple terms to fulfill the final
	// invoice: payment fragmentation, etc.
	Terms ContractTerm
}

func validateInvoice(i *Invoice) error {
	if len(i.Memo) > MaxMemoSize {
		return fmt.Errorf("max length a memo is %v, and invoice "+
			"of length %v was provided", MaxMemoSize, len(i.Memo))
	}
	if len(i.Receipt) > MaxReceiptSize {
		return fmt.Errorf("max length a receipt is %v, and invoice "+
			"of length %v was provided", MaxReceiptSize,
			len(i.Receipt))
	}
	if len(i.PaymentRequest) > MaxPaymentRequestSize {
		return fmt.Errorf("max length of payment request is %v, length "+
			"provided was %v", MaxPaymentRequestSize,
			len(i.PaymentRequest))
	}
	return nil
}

// AddInvoice inserts the targeted invoice into the database. If the invoice
// has *any* payment hashes which already exists within the database, then the
// insertion will be aborted and rejected due to the strict policy banning any
// duplicate payment hashes.
func (d *DB) AddInvoice(i *Invoice) error {
	if err := validateInvoice(i); err != nil {
		return err
	}
	return d.Update(func(tx *bolt.Tx) error {
		invoices, err := tx.CreateBucketIfNotExists(invoiceBucket)
		if err != nil {
			return err
		}

		invoiceIndex, err := invoices.CreateBucketIfNotExists(invoiceIndexBucket)
		if err != nil {
			return err
		}

		// Ensure that an invoice an identical payment hash doesn't
		// already exist within the index.
		paymentHash := sha256.Sum256(i.Terms.PaymentPreimage[:])
		if invoiceIndex.Get(paymentHash[:]) != nil {
			return ErrDuplicateInvoice
		}

		// If the current running payment ID counter hasn't yet been
		// created, then create it now.
		var invoiceNum uint32
		invoiceCounter := invoiceIndex.Get(numInvoicesKey)
		if invoiceCounter == nil {
			var scratch [4]byte
			byteOrder.PutUint32(scratch[:], invoiceNum)
			if err := invoiceIndex.Put(numInvoicesKey, scratch[:]); err != nil {
				return nil
			}
		} else {
			invoiceNum = byteOrder.Uint32(invoiceCounter)
		}

		return putInvoice(invoices, invoiceIndex, i, invoiceNum)
	})
}

// LookupInvoice attempts to look up an invoice according to it's 32 byte
// payment hash. In an invoice which can settle the HTLC identified by the
// passed payment hash isn't found, then an error is returned. Otherwise, the
// full invoice is returned. Before setting the incoming HTLC, the values
// SHOULD be checked to ensure the payer meets the agreed upon contractual
// terms of the payment.
func (d *DB) LookupInvoice(paymentHash [32]byte) (*Invoice, error) {
	var invoice *Invoice
	err := d.View(func(tx *bolt.Tx) error {
		invoices := tx.Bucket(invoiceBucket)
		if invoices == nil {
			return ErrNoInvoicesCreated
		}
		invoiceIndex := invoices.Bucket(invoiceIndexBucket)
		if invoiceIndex == nil {
			return ErrNoInvoicesCreated
		}

		// Check the invoice index to see if an invoice paying to this
		// hash exists within the DB.
		invoiceNum := invoiceIndex.Get(paymentHash[:])
		if invoiceNum == nil {
			return ErrInvoiceNotFound
		}

		// An invoice matching the payment hash has been found, so
		// retrieve the record of the invoice itself.
		i, err := fetchInvoice(invoiceNum, invoices)
		if err != nil {
			return err
		}
		invoice = i

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

	return invoice, nil
}

// FetchAllInvoices returns all invoices currently stored within the database.
// If the pendingOnly param is true, then only unsettled invoices will be
// returned, skipping all invoices that are fully settled.
func (d *DB) FetchAllInvoices(pendingOnly bool) ([]*Invoice, error) {
	var invoices []*Invoice

	err := d.View(func(tx *bolt.Tx) error {
		invoiceB := tx.Bucket(invoiceBucket)
		if invoiceB == nil {
			return ErrNoInvoicesCreated
		}

		// Iterate through the entire key space of the top-level
		// invoice bucket. If key with a non-nil value stores the next
		// invoice ID which maps to the corresponding invoice.
		return invoiceB.ForEach(func(k, v []byte) error {
			if v == nil {
				return nil
			}

			invoiceReader := bytes.NewReader(v)
			invoice, err := deserializeInvoice(invoiceReader)
			if err != nil {
				return err
			}

			if pendingOnly && invoice.Terms.Settled {
				return nil
			}

			invoices = append(invoices, invoice)

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

	return invoices, nil
}

// SettleInvoice attempts to mark an invoice corresponding to the passed
// payment hash as fully settled. If an invoice matching the passed payment
// hash doesn't existing within the database, then the action will fail with a
// "not found" error.
func (d *DB) SettleInvoice(paymentHash [32]byte) error {
	return d.Update(func(tx *bolt.Tx) error {
		invoices, err := tx.CreateBucketIfNotExists(invoiceBucket)
		if err != nil {
			return err
		}
		invoiceIndex, err := invoices.CreateBucketIfNotExists(invoiceIndexBucket)
		if err != nil {
			return err
		}

		// Check the invoice index to see if an invoice paying to this
		// hash exists within the DB.
		invoiceNum := invoiceIndex.Get(paymentHash[:])
		if invoiceNum == nil {
			return ErrInvoiceNotFound
		}

		return settleInvoice(invoices, invoiceNum)
	})
}

func putInvoice(invoices *bolt.Bucket, invoiceIndex *bolt.Bucket,
	i *Invoice, invoiceNum uint32) error {

	// Create the invoice key which is just the big-endian representation
	// of the invoice number.
	var invoiceKey [4]byte
	byteOrder.PutUint32(invoiceKey[:], invoiceNum)

	// Increment the num invoice counter index so the next invoice bares
	// the proper ID.
	var scratch [4]byte
	invoiceCounter := invoiceNum + 1
	byteOrder.PutUint32(scratch[:], invoiceCounter)
	if err := invoiceIndex.Put(numInvoicesKey, scratch[:]); err != nil {
		return err
	}

	// Add the payment hash to the invoice index. This'll let us quickly
	// identify if we can settle an incoming payment, and also to possibly
	// allow a single invoice to have multiple payment installations.
	paymentHash := sha256.Sum256(i.Terms.PaymentPreimage[:])
	if err := invoiceIndex.Put(paymentHash[:], invoiceKey[:]); err != nil {
		return err
	}

	// Finally, serialize the invoice itself to be written to the disk.
	var buf bytes.Buffer
	if err := serializeInvoice(&buf, i); err != nil {
		return nil
	}

	return invoices.Put(invoiceKey[:], buf.Bytes())
}

func serializeInvoice(w io.Writer, i *Invoice) error {
	if err := wire.WriteVarBytes(w, 0, i.Memo[:]); err != nil {
		return err
	}
	if err := wire.WriteVarBytes(w, 0, i.Receipt[:]); err != nil {
		return err
	}
	if err := wire.WriteVarBytes(w, 0, i.PaymentRequest[:]); err != nil {
		return err
	}

	birthBytes, err := i.CreationDate.MarshalBinary()
	if err != nil {
		return err
	}
	if err := wire.WriteVarBytes(w, 0, birthBytes); err != nil {
		return err
	}

	if _, err := w.Write(i.Terms.PaymentPreimage[:]); err != nil {
		return err
	}

	var scratch [8]byte
	byteOrder.PutUint64(scratch[:], uint64(i.Terms.Value))
	if _, err := w.Write(scratch[:]); err != nil {
		return err
	}

	if err := binary.Write(w, byteOrder, i.Terms.Settled); err != nil {
		return err
	}

	return nil
}

func fetchInvoice(invoiceNum []byte, invoices *bolt.Bucket) (*Invoice, error) {
	invoiceBytes := invoices.Get(invoiceNum)
	if invoiceBytes == nil {
		return nil, ErrInvoiceNotFound
	}

	invoiceReader := bytes.NewReader(invoiceBytes)

	return deserializeInvoice(invoiceReader)
}

func deserializeInvoice(r io.Reader) (*Invoice, error) {
	var err error
	invoice := &Invoice{}

	// TODO(roasbeef): use read full everywhere
	invoice.Memo, err = wire.ReadVarBytes(r, 0, MaxMemoSize, "")
	if err != nil {
		return nil, err
	}
	invoice.Receipt, err = wire.ReadVarBytes(r, 0, MaxReceiptSize, "")
	if err != nil {
		return nil, err
	}

	invoice.PaymentRequest, err = wire.ReadVarBytes(r, 0, MaxPaymentRequestSize, "")
	if err != nil {
		return nil, err
	}

	birthBytes, err := wire.ReadVarBytes(r, 0, 300, "birth")
	if err != nil {
		return nil, err
	}
	if err := invoice.CreationDate.UnmarshalBinary(birthBytes); err != nil {
		return nil, err
	}

	if _, err := io.ReadFull(r, invoice.Terms.PaymentPreimage[:]); err != nil {
		return nil, err
	}
	var scratch [8]byte
	if _, err := io.ReadFull(r, scratch[:]); err != nil {
		return nil, err
	}
	invoice.Terms.Value = lnwire.MilliSatoshi(byteOrder.Uint64(scratch[:]))

	if err := binary.Read(r, byteOrder, &invoice.Terms.Settled); err != nil {
		return nil, err
	}

	return invoice, nil
}

func settleInvoice(invoices *bolt.Bucket, invoiceNum []byte) error {
	invoice, err := fetchInvoice(invoiceNum, invoices)
	if err != nil {
		return err
	}

	invoice.Terms.Settled = true

	var buf bytes.Buffer
	if err := serializeInvoice(&buf, invoice); err != nil {
		return nil
	}

	// TODO(roasbeef): add timestamp

	return invoices.Put(invoiceNum[:], buf.Bytes())
}
<|endoftext|>


<commit_before>package database

import (
	"database/sql"
	"fmt"
	"log"
	"time"

	"github.com/lib/pq"

	"github.com/seadsystem/Backend/DB/landingzone/constants"
	"github.com/seadsystem/Backend/DB/landingzone/decoders"
)

type DB struct {
	conn *sql.DB
}

func New() (DB, error) {
	conn, err := sql.Open("postgres", fmt.Sprintf("host=%s user=%s dbname=%s password=%s port=%d sslmode=disable", constants.DB_SOCKET, constants.DB_USER, constants.DB_NAME, constants.DB_PASSWORD, constants.DB_PORT))
	return DB{conn}, err
}

func (db DB) InsertRaw(database_channel <-chan decoders.SeadPacket) {
	// Infinite loop with no breaks.
	for {
		log.Println("Waiting for data...")
		data := <-database_channel // Wait for first piece of data before starting transaction
		log.Println("Got data.")

		// Begin transaction
		txn, err := db.conn.Begin()
		if err != nil {
			log.Fatal(err)
		}

		// Prepare statement
		stmt, err := txn.Prepare(pq.CopyIn("data_raw", "serial", "type", "data", "time"))
		if err != nil {
			log.Fatal(err)
		}

	Data_processing:
		for {
			// Process data
			scale := constants.Scale[data.Type]
			data_type := string(data.Type)
			interp_time := data.Timestamp
			period := Duration(data.Period * float64(time.Second))
			for _, element := range data.Data {
				_, err = stmt.Exec(data.Serial, data_type, float32(element)*scale, interp_time.Format(time.RFC3339))
				interp_time.Add(period)
				if err != nil {
					log.Println(err)
					break
				}
			}

			log.Println("Waiting for more data...")

			// Receive result of read
			select {
			case data = <-database_channel:
				log.Println("Got data.")
			case <-time.After(time.Second * constants.DB_TIME_LIMIT):
				log.Println("Transaction timed out.")
				break Data_processing
			}
		}

		log.Println("Closing off transaction...")

		// Flush buffer
		_, err = stmt.Exec()
		if err != nil {
			log.Fatal(err)
		}

		// Close prepared statement
		err = stmt.Close()
		if err != nil {
			log.Fatal(err)
		}

		// Commit transaction
		err = txn.Commit()
		if err != nil {
			log.Fatal(err)
		}

		log.Println("Transaction closed")
	}
}
<commit_msg>Added missing package qualifier<commit_after>package database

import (
	"database/sql"
	"fmt"
	"log"
	"time"

	"github.com/lib/pq"

	"github.com/seadsystem/Backend/DB/landingzone/constants"
	"github.com/seadsystem/Backend/DB/landingzone/decoders"
)

type DB struct {
	conn *sql.DB
}

func New() (DB, error) {
	conn, err := sql.Open("postgres", fmt.Sprintf("host=%s user=%s dbname=%s password=%s port=%d sslmode=disable", constants.DB_SOCKET, constants.DB_USER, constants.DB_NAME, constants.DB_PASSWORD, constants.DB_PORT))
	return DB{conn}, err
}

func (db DB) InsertRaw(database_channel <-chan decoders.SeadPacket) {
	// Infinite loop with no breaks.
	for {
		log.Println("Waiting for data...")
		data := <-database_channel // Wait for first piece of data before starting transaction
		log.Println("Got data.")

		// Begin transaction
		txn, err := db.conn.Begin()
		if err != nil {
			log.Fatal(err)
		}

		// Prepare statement
		stmt, err := txn.Prepare(pq.CopyIn("data_raw", "serial", "type", "data", "time"))
		if err != nil {
			log.Fatal(err)
		}

	Data_processing:
		for {
			// Process data
			scale := constants.Scale[data.Type]
			data_type := string(data.Type)
			interp_time := data.Timestamp
			period := time.Duration(data.Period * float64(time.Second))
			for _, element := range data.Data {
				_, err = stmt.Exec(data.Serial, data_type, float32(element)*scale, interp_time.Format(time.RFC3339))
				interp_time.Add(period)
				if err != nil {
					log.Println(err)
					break
				}
			}

			log.Println("Waiting for more data...")

			// Receive result of read
			select {
			case data = <-database_channel:
				log.Println("Got data.")
			case <-time.After(time.Second * constants.DB_TIME_LIMIT):
				log.Println("Transaction timed out.")
				break Data_processing
			}
		}

		log.Println("Closing off transaction...")

		// Flush buffer
		_, err = stmt.Exec()
		if err != nil {
			log.Fatal(err)
		}

		// Close prepared statement
		err = stmt.Close()
		if err != nil {
			log.Fatal(err)
		}

		// Commit transaction
		err = txn.Commit()
		if err != nil {
			log.Fatal(err)
		}

		log.Println("Transaction closed")
	}
}
<|endoftext|>


<commit_before>package handlers

import (
	"errors"
	"io"
	"log"
	"net"
	"time"

	"github.com/seadsystem/Backend/DB/landingzone/constants"
)

// Errors
var Timeout = errors.New("Action timed out.")

// Handle a client's request
func HandleRequest(conn net.Conn) {
	log.Println("Got a connection.")

	log.Println("Sending HEAD.")
	conn.Write([]byte(constants.HEAD))

	for {
		log.Println("Reading length header...")
		length_header, err := read_bytes(conn, constants.LENGTH_HEADER_SIZE)
		if err != nil {
			read_error(err)
			break
		}

		data_length := int(length_header[1])

		// Check that we got a length header
		if length_header[0] != 'L' || data_length == 0 {
			log.Println("Invalid length header.")

			// TODO: resync here

			break

		} else {
			log.Printf("Length: %d\n", length_header[1])

			// Get the rest of the packet
			data, err := read_bytes(conn, data_length-constants.LENGTH_HEADER_SIZE)

			if err != nil {
				read_error(err)
				break
			}

			log.Println("Read data:")
			log.Println(string(data))
		}

	}

	conn.Write([]byte("Response"))
	conn.Close()
}

// read_error checks the error and prints an appropriate friendly error message.
func read_error(err error) {
	if err != io.EOF {
		log.Println("Read error:", err)
	} else {
		log.Println("Done reading bytes.")
	}
}

// read_bytes reads the specified number of bytes from the connection with an appropriate time limit.
func read_bytes(conn net.Conn, bytes int) (data []byte, err error) {
	// Setup channels
	data_channel := make(chan []byte, 1)
	error_channel := make(chan error, 1)

	// Initiate read in new go routine
	go func() {
		buffer := make([]byte, bytes)
		n, ierr := conn.Read(buffer)
		if ierr != nil {
			error_channel <- ierr
			return
		}
		if bytes != n {
			error_channel <- io.ErrShortWrite
			return
		}
		data_channel <- buffer
	}()

	// Receive result of read
	select {
	case data := <-data_channel:
		// Read resulted in data
	case err := <-error_channel:
		// Read resulted in an error
	case <-time.After(time.Second * constants.READ_TIME_LIMIT):
		// Read timed out
		err = Timeout
	}
}
<commit_msg>Fixed minor errors blocking compilation<commit_after>package handlers

import (
	"errors"
	"io"
	"log"
	"net"
	"time"

	"github.com/seadsystem/Backend/DB/landingzone/constants"
)

// Errors
var Timeout = errors.New("Action timed out.")

// Handle a client's request
func HandleRequest(conn net.Conn) {
	log.Println("Got a connection.")

	log.Println("Sending HEAD.")
	conn.Write([]byte(constants.HEAD))

	for {
		log.Println("Reading length header...")
		length_header, err := read_bytes(conn, constants.LENGTH_HEADER_SIZE)
		if err != nil {
			read_error(err)
			break
		}

		data_length := int(length_header[1])

		// Check that we got a length header
		if length_header[0] != 'L' || data_length == 0 {
			log.Println("Invalid length header.")

			// TODO: resync here

			break

		} else {
			log.Printf("Length: %d\n", length_header[1])

			// Get the rest of the packet
			data, err := read_bytes(conn, data_length-constants.LENGTH_HEADER_SIZE)

			if err != nil {
				read_error(err)
				break
			}

			log.Println("Read data:")
			log.Println(string(data))
		}

	}

	conn.Write([]byte("Response"))
	conn.Close()
}

// read_error checks the error and prints an appropriate friendly error message.
func read_error(err error) {
	if err != io.EOF {
		log.Println("Read error:", err)
	} else {
		log.Println("Done reading bytes.")
	}
}

// read_bytes reads the specified number of bytes from the connection with an appropriate time limit.
func read_bytes(conn net.Conn, bytes int) (data []byte, err error) {
	// Setup channels
	data_channel := make(chan []byte, 1)
	error_channel := make(chan error, 1)

	// Initiate read in new go routine
	go func() {
		buffer := make([]byte, bytes)
		n, ierr := conn.Read(buffer)
		if ierr != nil {
			error_channel <- ierr
			return
		}
		if bytes != n {
			error_channel <- io.ErrShortWrite
			return
		}
		data_channel <- buffer
	}()

	// Receive result of read
	select {
	case data := <-data_channel:
		// Read resulted in data
	case err := <-error_channel:
		// Read resulted in an error
	case <-time.After(time.Second * constants.READ_TIME_LIMIT):
		// Read timed out
		err = Timeout
	}

	return
}
<|endoftext|>


<commit_before>//   Copyright 2017 MSolution.IO
//
//   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 anomalies

import (
	"context"
	"encoding/json"
	"math"

	"github.com/trackit/jsonlog"
	"gopkg.in/olivere/elastic.v5"
)

type (
	// esTypedResult is	used to store the raw ElasticSearch response.
	esTypedResult struct {
		Products struct {
			Buckets []struct {
				Key string `json:"key"`
				Dates struct {
					Buckets []struct {
						Key string `json:"key_as_string"`
						Cost struct {
							Value float64 `json:"value"`
						} `json:"cost"`
					} `json:"buckets"`
				} `json:"dates"`
			} `json:"buckets"`
		}
	}

	// CostAnomaly represents a day and contains
	// the date of beginning, the cost for the 24h,
	// the value of the upper band and an abnormal value
	// representing if the day is tagged as abnormal,
	// which is an alert.
	CostAnomaly struct {
		Date      string  `json:"date"`
		Cost      float64 `json:"cost"`
		UpperBand float64 `json:"upper_band"`
		Abnormal  bool    `json:"abnormal"`
	}

	// ProductsCostAnomalies is used as http response.
	// Keys are products and values are a slice of CostAnomaly.
	ProductsCostAnomalies map[string][]CostAnomaly
)

// const values used by the Bollinger Bands algorithm.
const (
	// period is the number of day took.
	// A bigger period means more stability in the cost so
	// it will be more sensitive to the picks.
	period = 3

	// standardDeviationCoefficient allows to add a
	// coefficient to the standard deviation.
	// A standardDeviationCoefficient bigger makes
	// the algorithm more flexible.
	standardDeviationCoefficient = 3.0

	// margin of error set to 5% of the price.
	// Upper band will be higher by 5%.
	margin = 1.05

	// minCostPercent is set to 4%.
	// If an anomaly is detected, the cost has to be
	// higher than 4% of the total bill.
	minCostPercent = 0.04
)

// sum adds every element of a CostAnomaly slice.
func sum(costAnomalies []CostAnomaly) float64 {
	var sum float64
	for _, a := range costAnomalies {
		sum += a.Cost
	}
	return sum
}

// average calculates the average of a CostAnomaly slice.
func average(costAnomalies []CostAnomaly) float64 {
	return sum(costAnomalies) / float64(len(costAnomalies))
}

// sigma calculates the sigma in the standard deviation formula.
func sigma(costAnomalies []CostAnomaly, avg float64) float64 {
	var sigma float64
	for _, a := range costAnomalies {
		sigma += math.Pow(a.Cost-avg, 2)
	}
	return sigma
}

// deviation calculates the standard deviation.
func deviation(sigma float64, period int) float64 {
	var deviation float64
	deviation = 1 / float64(period) * math.Pow(sigma, 0.5)
	return deviation
}

// clearDisturbances clears the fake alerts.
// Alerts below the minCostPercent are removed.
func clearDisturbances(costAnomalies []CostAnomaly, totalCostAnomalies map[string]float64) []CostAnomaly {
	for index := range costAnomalies {
		date := costAnomalies[index].Date
		if costAnomalies[index].Cost < totalCostAnomalies[date]*minCostPercent {
			costAnomalies[index].Abnormal = false
		}
	}
	return costAnomalies
}

func getTotalCostAnomalies(c ProductsCostAnomalies) map[string]float64 {
	totalCostAnomalies := map[string]float64{}
	for _, costAnomalies := range c {
		for _, an := range costAnomalies {
			totalCostAnomalies[an.Date] += an.Cost
		}
	}
	return totalCostAnomalies
}

// analyseAnomalies calculates anomalies with Bollinger Bands algorithm and
// the const above. It consists in generating an upper band, which, if
// exceeded, make an alert.
func analyseAnomalies(c ProductsCostAnomalies) ProductsCostAnomalies {
	totalCostAnomalies := getTotalCostAnomalies(c)
	for key, costAnomalies := range c {
		for index := range costAnomalies {
			if index > 0 {
				a := &costAnomalies[index]
				tempSliceSize := int(math.Min(float64(index), period))
				tempSlice := costAnomalies[index-tempSliceSize : index]
				avg := average(tempSlice)
				sigma := sigma(tempSlice, avg)
				deviation := deviation(sigma, tempSliceSize)
				a.UpperBand = avg*margin + (deviation * standardDeviationCoefficient)
				if a.Cost > a.UpperBand {
					a.Abnormal = true
				}
			}
		}
		c[key] = clearDisturbances(costAnomalies, totalCostAnomalies)
	}
	return c
}

// parseAnomalies transforms the esTypedResult in a ProductsCostAnomalies
// empty of alerts. It calls analyseAnomalies then to fill the alerts.
func parseAnomalies(typedDocument esTypedResult) ProductsCostAnomalies {
	c := ProductsCostAnomalies{}
	for _, product := range typedDocument.Products.Buckets {
		costAnomalies := make([]CostAnomaly, 0, len(product.Dates.Buckets))
		for _, date := range product.Dates.Buckets {
			costAnomalies = append(costAnomalies, CostAnomaly{
				date.Key,
				date.Cost.Value,
				0,
				false,
			})
		}
		c[product.Key] = costAnomalies
	}
	return analyseAnomalies(c)
}

// prepareAnomalyData calls ElasticSearch and stores
// the result in a esTypedResult type. It calls parseAnomalies
// then.
func prepareAnomalyData(ctx context.Context, sr *elastic.SearchResult) (ProductsCostAnomalies, error) {
	var logger = jsonlog.LoggerFromContextOrDefault(ctx)
	var typedDocument esTypedResult
	err := json.Unmarshal(*sr.Aggregations["products"], &typedDocument.Products)
	if err != nil {
		logger.Error("Failed to parse elasticsearch document.", err.Error())
		return ProductsCostAnomalies{}, err
	}
	return parseAnomalies(typedDocument), nil
}
<commit_msg>anomaly detection adjustement<commit_after>//   Copyright 2017 MSolution.IO
//
//   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 anomalies

import (
	"context"
	"encoding/json"
	"math"

	"github.com/trackit/jsonlog"
	"gopkg.in/olivere/elastic.v5"
)

type (
	// esTypedResult is	used to store the raw ElasticSearch response.
	esTypedResult struct {
		Products struct {
			Buckets []struct {
				Key string `json:"key"`
				Dates struct {
					Buckets []struct {
						Key string `json:"key_as_string"`
						Cost struct {
							Value float64 `json:"value"`
						} `json:"cost"`
					} `json:"buckets"`
				} `json:"dates"`
			} `json:"buckets"`
		}
	}

	// CostAnomaly represents a day and contains
	// the date of beginning, the cost for the 24h,
	// the value of the upper band and an abnormal value
	// representing if the day is tagged as abnormal,
	// which is an alert.
	CostAnomaly struct {
		Date      string  `json:"date"`
		Cost      float64 `json:"cost"`
		UpperBand float64 `json:"upper_band"`
		Abnormal  bool    `json:"abnormal"`
	}

	// ProductsCostAnomalies is used as http response.
	// Keys are products and values are a slice of CostAnomaly.
	ProductsCostAnomalies map[string][]CostAnomaly
)

// const values used by the Bollinger Bands algorithm.
const (
	// period is the number of day took.
	// A bigger period means more stability in the cost so
	// it will be more sensitive to the picks.
	period = 3

	// standardDeviationCoefficient allows to add a
	// coefficient to the standard deviation.
	// A standardDeviationCoefficient bigger makes
	// the algorithm more flexible.
	standardDeviationCoefficient = 3.0

	// margin of error set to 5% of the price.
	// Upper band will be higher by 5%.
	margin = 1.05

	// minCostPercent is set to 4%.
	// If an anomaly is detected, the cost has to be
	// higher than 2% of the total bill.
	minCostPercent = 0.02
)

// sum adds every element of a CostAnomaly slice.
func sum(costAnomalies []CostAnomaly) float64 {
	var sum float64
	for _, a := range costAnomalies {
		sum += a.Cost
	}
	return sum
}

// average calculates the average of a CostAnomaly slice.
func average(costAnomalies []CostAnomaly) float64 {
	return sum(costAnomalies) / float64(len(costAnomalies))
}

// sigma calculates the sigma in the standard deviation formula.
func sigma(costAnomalies []CostAnomaly, avg float64) float64 {
	var sigma float64
	for _, a := range costAnomalies {
		sigma += math.Pow(a.Cost-avg, 2)
	}
	return sigma
}

// deviation calculates the standard deviation.
func deviation(sigma float64, period int) float64 {
	var deviation float64
	deviation = 1 / float64(period) * math.Pow(sigma, 0.5)
	return deviation
}

// clearDisturbances clears the fake alerts.
// Alerts below the minCostPercent are removed.
func clearDisturbances(costAnomalies []CostAnomaly, totalCostAnomalies map[string]float64) []CostAnomaly {
	for index := range costAnomalies {
		date := costAnomalies[index].Date
		if costAnomalies[index].Cost-costAnomalies[index].UpperBand < totalCostAnomalies[date]*minCostPercent {
			costAnomalies[index].Abnormal = false
		}
	}
	return costAnomalies
}

func getTotalCostAnomalies(c ProductsCostAnomalies) map[string]float64 {
	totalCostAnomalies := map[string]float64{}
	for _, costAnomalies := range c {
		for _, an := range costAnomalies {
			totalCostAnomalies[an.Date] += an.Cost
		}
	}
	return totalCostAnomalies
}

// analyseAnomalies calculates anomalies with Bollinger Bands algorithm and
// the const above. It consists in generating an upper band, which, if
// exceeded, make an alert.
func analyseAnomalies(c ProductsCostAnomalies) ProductsCostAnomalies {
	totalCostAnomalies := getTotalCostAnomalies(c)
	for key, costAnomalies := range c {
		for index := range costAnomalies {
			if index > 0 {
				a := &costAnomalies[index]
				tempSliceSize := int(math.Min(float64(index), period))
				tempSlice := costAnomalies[index-tempSliceSize : index]
				avg := average(tempSlice)
				sigma := sigma(tempSlice, avg)
				deviation := deviation(sigma, tempSliceSize)
				a.UpperBand = avg*margin + (deviation * standardDeviationCoefficient)
				if a.Cost > a.UpperBand {
					a.Abnormal = true
				}
			}
		}
		c[key] = clearDisturbances(costAnomalies, totalCostAnomalies)
	}
	return c
}

// parseAnomalies transforms the esTypedResult in a ProductsCostAnomalies
// empty of alerts. It calls analyseAnomalies then to fill the alerts.
func parseAnomalies(typedDocument esTypedResult) ProductsCostAnomalies {
	c := ProductsCostAnomalies{}
	for _, product := range typedDocument.Products.Buckets {
		costAnomalies := make([]CostAnomaly, 0, len(product.Dates.Buckets))
		for _, date := range product.Dates.Buckets {
			costAnomalies = append(costAnomalies, CostAnomaly{
				date.Key,
				date.Cost.Value,
				0,
				false,
			})
		}
		c[product.Key] = costAnomalies
	}
	return analyseAnomalies(c)
}

// prepareAnomalyData calls ElasticSearch and stores
// the result in a esTypedResult type. It calls parseAnomalies
// then.
func prepareAnomalyData(ctx context.Context, sr *elastic.SearchResult) (ProductsCostAnomalies, error) {
	var logger = jsonlog.LoggerFromContextOrDefault(ctx)
	var typedDocument esTypedResult
	err := json.Unmarshal(*sr.Aggregations["products"], &typedDocument.Products)
	if err != nil {
		logger.Error("Failed to parse elasticsearch document.", err.Error())
		return ProductsCostAnomalies{}, err
	}
	return parseAnomalies(typedDocument), nil
}
<|endoftext|>


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

// +build linux

// Package keyctl is a Go interface to linux kernel keyrings (keyctl interface)
//
// Deprecated: Most callers should use either golang.org/x/sys/unix directly,
// or the original (and more extensive) github.com/jsipprell/keyctl .
package keyctl

import (
	"unsafe"

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

// Keyring is the basic interface to a linux keyctl keyring.
type Keyring interface {
	ID
	Add(string, []byte) (*Key, error)
	Search(string) (*Key, error)
}

type keyring struct {
	id keyID
}

// ID is unique 32-bit serial number identifiers for all Keys and Keyrings have.
type ID interface {
	ID() int32
}

// Add a new key to a keyring. The key can be searched for later by name.
func (kr *keyring) Add(name string, key []byte) (*Key, error) {
	r, err := unix.AddKey("user", name, key, int(kr.id))
	if err == nil {
		key := &Key{Name: name, id: keyID(r), ring: kr.id}
		return key, nil
	}
	return nil, err
}

// Search for a key by name, this also searches child keyrings linked to this
// one. The key, if found, is linked to the top keyring that Search() was called
// from.
func (kr *keyring) Search(name string) (*Key, error) {
	id, err := unix.KeyctlSearch(int(kr.id), "user", name, 0)
	if err == nil {
		return &Key{Name: name, id: keyID(id), ring: kr.id}, nil
	}
	return nil, err
}

// ID returns the 32-bit kernel identifier of a keyring
func (kr *keyring) ID() int32 {
	return int32(kr.id)
}

// SessionKeyring returns the current login session keyring
func SessionKeyring() (Keyring, error) {
	return newKeyring(unix.KEY_SPEC_SESSION_KEYRING)
}

// UserKeyring  returns the keyring specific to the current user.
func UserKeyring() (Keyring, error) {
	return newKeyring(unix.KEY_SPEC_USER_KEYRING)
}

// Unlink an object from a keyring
func Unlink(parent Keyring, child ID) error {
	_, err := unix.KeyctlInt(unix.KEYCTL_UNLINK, int(child.ID()), int(parent.ID()), 0, 0)
	return err
}

// Link a key into a keyring
func Link(parent Keyring, child ID) error {
	_, err := unix.KeyctlInt(unix.KEYCTL_LINK, int(child.ID()), int(parent.ID()), 0, 0)
	return err
}

// ReadUserKeyring reads user keyring and returns slice of key with id(key_serial_t) representing the IDs of all the keys that are linked to it
func ReadUserKeyring() ([]*Key, error) {
	var (
		b        []byte
		err      error
		sizeRead int
	)
	krSize := 4
	size := krSize
	b = make([]byte, size)
	sizeRead = size + 1
	for sizeRead > size {
		r1, err := unix.KeyctlBuffer(unix.KEYCTL_READ, unix.KEY_SPEC_USER_KEYRING, b, size)
		if err != nil {
			return nil, err
		}

		if sizeRead = int(r1); sizeRead > size {
			b = make([]byte, sizeRead)
			size = sizeRead
			sizeRead = size + 1
		} else {
			krSize = sizeRead
		}
	}
	keyIDs := getKeyIDsFromByte(b[:krSize])
	return keyIDs, err
}

func getKeyIDsFromByte(byteKeyIDs []byte) []*Key {
	idSize := 4
	var keys []*Key
	for idx := 0; idx+idSize <= len(byteKeyIDs); idx = idx + idSize {
		tempID := *(*int32)(unsafe.Pointer(&byteKeyIDs[idx]))
		keys = append(keys, &Key{id: keyID(tempID)})
	}
	return keys
}
<commit_msg>internal/pkg/keyctl: drop deprecation warning<commit_after>// Copyright 2015 Jesse Sipprell. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build linux

// Package keyctl is a Go interface to linux kernel keyrings (keyctl interface)
package keyctl

import (
	"unsafe"

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

// Keyring is the basic interface to a linux keyctl keyring.
type Keyring interface {
	ID
	Add(string, []byte) (*Key, error)
	Search(string) (*Key, error)
}

type keyring struct {
	id keyID
}

// ID is unique 32-bit serial number identifiers for all Keys and Keyrings have.
type ID interface {
	ID() int32
}

// Add a new key to a keyring. The key can be searched for later by name.
func (kr *keyring) Add(name string, key []byte) (*Key, error) {
	r, err := unix.AddKey("user", name, key, int(kr.id))
	if err == nil {
		key := &Key{Name: name, id: keyID(r), ring: kr.id}
		return key, nil
	}
	return nil, err
}

// Search for a key by name, this also searches child keyrings linked to this
// one. The key, if found, is linked to the top keyring that Search() was called
// from.
func (kr *keyring) Search(name string) (*Key, error) {
	id, err := unix.KeyctlSearch(int(kr.id), "user", name, 0)
	if err == nil {
		return &Key{Name: name, id: keyID(id), ring: kr.id}, nil
	}
	return nil, err
}

// ID returns the 32-bit kernel identifier of a keyring
func (kr *keyring) ID() int32 {
	return int32(kr.id)
}

// SessionKeyring returns the current login session keyring
func SessionKeyring() (Keyring, error) {
	return newKeyring(unix.KEY_SPEC_SESSION_KEYRING)
}

// UserKeyring  returns the keyring specific to the current user.
func UserKeyring() (Keyring, error) {
	return newKeyring(unix.KEY_SPEC_USER_KEYRING)
}

// Unlink an object from a keyring
func Unlink(parent Keyring, child ID) error {
	_, err := unix.KeyctlInt(unix.KEYCTL_UNLINK, int(child.ID()), int(parent.ID()), 0, 0)
	return err
}

// Link a key into a keyring
func Link(parent Keyring, child ID) error {
	_, err := unix.KeyctlInt(unix.KEYCTL_LINK, int(child.ID()), int(parent.ID()), 0, 0)
	return err
}

// ReadUserKeyring reads user keyring and returns slice of key with id(key_serial_t) representing the IDs of all the keys that are linked to it
func ReadUserKeyring() ([]*Key, error) {
	var (
		b        []byte
		err      error
		sizeRead int
	)
	krSize := 4
	size := krSize
	b = make([]byte, size)
	sizeRead = size + 1
	for sizeRead > size {
		r1, err := unix.KeyctlBuffer(unix.KEYCTL_READ, unix.KEY_SPEC_USER_KEYRING, b, size)
		if err != nil {
			return nil, err
		}

		if sizeRead = int(r1); sizeRead > size {
			b = make([]byte, sizeRead)
			size = sizeRead
			sizeRead = size + 1
		} else {
			krSize = sizeRead
		}
	}
	keyIDs := getKeyIDsFromByte(b[:krSize])
	return keyIDs, err
}

func getKeyIDsFromByte(byteKeyIDs []byte) []*Key {
	idSize := 4
	var keys []*Key
	for idx := 0; idx+idSize <= len(byteKeyIDs); idx = idx + idSize {
		tempID := *(*int32)(unsafe.Pointer(&byteKeyIDs[idx]))
		keys = append(keys, &Key{id: keyID(tempID)})
	}
	return keys
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"log"
	wyc "wysteria/wysteria_common"
)

func (s *WysteriaServer) handleCreateCollection(data []byte) ([]byte, error) {
	col := wyc.Collection{}
	err := json.Unmarshal(data, &col)
	if err != nil {
		return nil, err
	}

	if col.Name == "" { // Check required field
		return nil, errors.New("Name required for Collection")
	}

	col.Id = NewId()
	err = s.database.InsertCollection(col.Id, col)
	if err != nil {
		return nil, err
	}

	return json.Marshal(col)
}

func (s *WysteriaServer) handleCreateItem(data []byte) ([]byte, error) {
	i := wyc.Item{}
	err := json.Unmarshal(data, &i)
	if err != nil {
		return nil, err
	}

	if i.Parent == "" || i.Variant == "" || i.ItemType == "" { // Check required fields
		return nil, errors.New("Parent, ItemType, Variant required for Item")
	}

	i.Id = NewId()
	err = s.database.InsertItem(i.Id, i)
	if err != nil {
		return nil, err
	}

	err = s.searchbase.InsertItem(i.Id, i)
	if err != nil {
		log.Println(err)
	}

	return json.Marshal(i)
}

func (s *WysteriaServer) handleCreateVersion(data []byte) ([]byte, error) {
	i := wyc.Version{}
	err := json.Unmarshal(data, &i)
	if err != nil {
		return nil, err
	}

	if i.Parent == "" {
		return nil, errors.New("Parent required for Version")
	}

	i.Id = NewId()
	number, err := s.database.InsertNextVersion(i.Id, i)
	if err != nil {
		return nil, err
	}

	i.Number = number
	err = s.searchbase.InsertVersion(i.Id, i)
	if err != nil {
		return nil, err
	}

	return json.Marshal(i)
}

func (s *WysteriaServer) handleCreateFileResource(data []byte) ([]byte, error) {
	i := wyc.FileResource{}
	err := json.Unmarshal(data, &i)
	if err != nil {
		return nil, err
	}

	if i.Name == "" || i.ResourceType == "" || i.Location == "" {
		return nil, errors.New("Name, ResourceType and Location required for FileResource")
	}

	i.Id = NewId()
	err = s.database.InsertFileResource(i.Id, i)
	if err != nil {
		return nil, err
	}

	err = s.searchbase.InsertFileResource(i.Id, i)
	if err != nil {
		log.Println(err)
	}

	return json.Marshal(i)
}

func (s *WysteriaServer) handleCreateLink(data []byte) ([]byte, error) {
	i := wyc.Link{}
	err := json.Unmarshal(data, &i)
	if err != nil {
		return nil, err
	}

	if i.Name == "" || i.Src == "" || i.Dst == "" {
		return nil, errors.New("Name, Src and Dst required for Link")
	}

	i.Id = NewId()
	err = s.database.InsertLink(i.Id, i)
	if err != nil {
		return nil, err
	}

	err = s.searchbase.InsertLink(i.Id, i)
	if err != nil {
		log.Println(err)
	}

	return json.Marshal(i)
}
<commit_msg>Restrict a collections items to one per type/variant<commit_after>package main

import (
	"encoding/json"
	"errors"
	"log"
	wyc "wysteria/wysteria_common"
	"fmt"
)

func (s *WysteriaServer) handleCreateCollection(data []byte) ([]byte, error) {
	col := wyc.Collection{}
	err := json.Unmarshal(data, &col)
	if err != nil {
		return nil, err
	}

	if col.Name == "" { // Check required field
		return nil, errors.New("Name required for Collection")
	}

	col.Id = NewId()
	err = s.database.InsertCollection(col.Id, col)
	if err != nil {
		return nil, err
	}

	return json.Marshal(col)
}

func (s *WysteriaServer) handleCreateItem(data []byte) ([]byte, error) {
	i := wyc.Item{}
	err := json.Unmarshal(data, &i)
	if err != nil {
		return nil, err
	}

	if i.Parent == "" || i.Variant == "" || i.ItemType == "" { // Check required fields
		return nil, errors.New("Parent, ItemType, Variant required for Item")
	}

	results, err := s.searchbase.QueryItem("", true, 0, wyc.QueryDesc{
		Parent: i.Parent,
		ItemType: i.ItemType,
		Variant: i.Variant,
	})
	if err != nil {
		return nil, err
	}
	if len(results) > 0 {
		return nil, errors.New(fmt.Sprintf("A child item of %s already exists with %s %s", i.Parent, i.ItemType, i.Variant))
	}

	i.Id = NewId()
	err = s.database.InsertItem(i.Id, i)
	if err != nil {
		return nil, err
	}

	err = s.searchbase.InsertItem(i.Id, i)
	if err != nil {
		log.Println(err)
	}

	return json.Marshal(i)
}

func (s *WysteriaServer) handleCreateVersion(data []byte) ([]byte, error) {
	i := wyc.Version{}
	err := json.Unmarshal(data, &i)
	if err != nil {
		return nil, err
	}

	if i.Parent == "" {
		return nil, errors.New("Parent required for Version")
	}

	i.Id = NewId()
	number, err := s.database.InsertNextVersion(i.Id, i)
	if err != nil {
		return nil, err
	}

	i.Number = number
	err = s.searchbase.InsertVersion(i.Id, i)
	if err != nil {
		return nil, err
	}

	return json.Marshal(i)
}

func (s *WysteriaServer) handleCreateFileResource(data []byte) ([]byte, error) {
	i := wyc.FileResource{}
	err := json.Unmarshal(data, &i)
	if err != nil {
		return nil, err
	}

	if i.Name == "" || i.ResourceType == "" || i.Location == "" {
		return nil, errors.New("Name, ResourceType and Location required for FileResource")
	}

	i.Id = NewId()
	err = s.database.InsertFileResource(i.Id, i)
	if err != nil {
		return nil, err
	}

	err = s.searchbase.InsertFileResource(i.Id, i)
	if err != nil {
		log.Println(err)
	}

	return json.Marshal(i)
}

func (s *WysteriaServer) handleCreateLink(data []byte) ([]byte, error) {
	i := wyc.Link{}
	err := json.Unmarshal(data, &i)
	if err != nil {
		return nil, err
	}

	if i.Name == "" || i.Src == "" || i.Dst == "" {
		return nil, errors.New("Name, Src and Dst required for Link")
	}

	i.Id = NewId()
	err = s.database.InsertLink(i.Id, i)
	if err != nil {
		return nil, err
	}

	err = s.searchbase.InsertLink(i.Id, i)
	if err != nil {
		log.Println(err)
	}

	return json.Marshal(i)
}
<|endoftext|>


<commit_before>package watch

import (
	"fmt"

	"k8s.io/client-go/kubernetes"
	kubeapi "k8s.io/client-go/pkg/api"
	k8sv1 "k8s.io/client-go/pkg/api/v1"
	metav1 "k8s.io/client-go/pkg/apis/meta/v1"
	"k8s.io/client-go/pkg/fields"
	"k8s.io/client-go/pkg/util/workqueue"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	"kubevirt.io/kubevirt/pkg/api/v1"
	"kubevirt.io/kubevirt/pkg/kubecli"
	"kubevirt.io/kubevirt/pkg/logging"
	"kubevirt.io/kubevirt/pkg/virt-controller/services"
)

func NewMigrationController(migrationService services.VMService, recorder record.EventRecorder, restClient *rest.RESTClient, clientset *kubernetes.Clientset) (cache.Store, *kubecli.Controller, *workqueue.RateLimitingInterface) {
	lw := cache.NewListWatchFromClient(restClient, "migrations", k8sv1.NamespaceDefault, fields.Everything())
	queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
	store, controller := kubecli.NewController(lw, queue, &v1.Migration{}, NewMigrationControllerDispatch(migrationService, restClient, clientset))
	return store, controller, &queue
}

func NewMigrationControllerDispatch(vmService services.VMService, restClient *rest.RESTClient, clientset *kubernetes.Clientset) kubecli.ControllerDispatch {

	dispatch := MigrationDispatch{
		restClient: restClient,
		vmService:  vmService,
		clientset:  clientset,
	}
	return &dispatch
}

type MigrationDispatch struct {
	restClient *rest.RESTClient
	vmService  services.VMService
	clientset  *kubernetes.Clientset
}

func (md *MigrationDispatch) Execute(store cache.Store, queue workqueue.RateLimitingInterface, key interface{}) {
	if err := md.execute(store, key.(string)); err != nil {
		logging.DefaultLogger().Info().Reason(err).Msgf("reenqueuing migration %v", key)
		queue.AddRateLimited(key)
	} else {
		logging.DefaultLogger().Info().V(4).Msgf("processed migration %v", key)
		queue.Forget(key)
	}
}

func (md *MigrationDispatch) execute(store cache.Store, key string) error {

	setMigrationPhase := func(migration *v1.Migration, phase v1.MigrationPhase) error {

		if migration.Status.Phase == phase {
			return nil
		}

		logger := logging.DefaultLogger().Object(migration)

		// Copy migration for future modifications
		migrationCopy, err := copy(migration)
		if err != nil {
			logger.Error().Reason(err).Msg("could not copy migration object")
			return err
		}

		migrationCopy.Status.Phase = phase
		// TODO indicate why it was set to failed
		err = md.vmService.UpdateMigration(migrationCopy)
		if err != nil {
			logger.Error().Reason(err).Msgf("updating migration state failed: %v ", err)
			return err
		}
		return nil
	}

	setMigrationFailed := func(mig *v1.Migration) error {
		return setMigrationPhase(mig, v1.MigrationFailed)
	}

	obj, exists, err := store.GetByKey(key)
	if err != nil {
		return err
	}
	if !exists {
		return nil
	}

	var migration *v1.Migration = obj.(*v1.Migration)
	logger := logging.DefaultLogger().Object(migration)

	vm, exists, err := md.vmService.FetchVM(migration.Spec.Selector.Name)
	if err != nil {
		logger.Error().Reason(err).Msgf("fetching the vm %s failed", migration.Spec.Selector.Name)
		return err
	}

	if !exists {
		logger.Info().Msgf("VM with name %s does not exist, marking migration as failed", migration.Spec.Selector.Name)
		if err = setMigrationFailed(migration); err != nil {
			return err
		}
		return nil
	}

	switch migration.Status.Phase {
	case v1.MigrationUnknown:
		if vm.Status.Phase != v1.Running {
			logger.Error().Msgf("VM with name %s is in state %s, no migration possible. Marking migration as failed", vm.GetObjectMeta().GetName(), vm.Status.Phase)
			if err = setMigrationFailed(migration); err != nil {
				return err
			}
			return nil
		}

		if err := mergeConstraints(migration, vm); err != nil {
			logger.Error().Reason(err).Msg("merging Migration and VM placement constraints failed.")
			return err
		}
		podList, err := md.vmService.GetRunningVMPods(vm)
		if err != nil {
			logger.Error().Reason(err).Msg("could not fetch a list of running VM target pods")
			return err
		}

		numOfPods, targetPod := investigateTargetPodSituation(migration, podList)

		if targetPod == nil {
			if numOfPods > 1 {
				logger.Error().Msg("another migration seems to be in progress, marking Migration as failed")
				// Another migration is currently going on
				if err = setMigrationFailed(migration); err != nil {
					return err
				}
				return nil
			} else if numOfPods == 1 {
				// We need to start a migration target pod
				// TODO, this detection is not optimal, it can lead to strange situations
				err := md.vmService.CreateMigrationTargetPod(migration, vm)
				if err != nil {
					logger.Error().Reason(err).Msg("creating a migration target pod failed")
					return err
				}
			}
		} else {
			if targetPod.Status.Phase == k8sv1.PodFailed {
				logger.Error().Msg("migration target pod is in failed state")
				if err = setMigrationFailed(migration); err != nil {
					return err
				}
				return nil
			}
			// Unlikely to hit this case, but prevents erroring out
			// if we re-enter this loop
			logger.Info().Msgf("migration appears to be set up, but was not set to %s", v1.MigrationScheduled)
		}
		err = setMigrationPhase(migration, v1.MigrationScheduled)
		if err != nil {
			return err
		}
		return nil
	case v1.MigrationScheduled:
		podList, err := md.vmService.GetRunningVMPods(vm)
		if err != nil {
			logger.Error().Reason(err).Msg("could not fetch a list of running VM target pods")
			return err
		}

		_, targetPod := investigateTargetPodSituation(migration, podList)

		if targetPod == nil {
			logger.Error().Msg("migration target pod does not exist or is an end state")
			if err = setMigrationFailed(migration); err != nil {
				return err
			}
			return nil
		}
		// Migration has been scheduled but no update on the status has been recorded
		err = setMigrationPhase(migration, v1.MigrationRunning)
		if err != nil {
			return err
		}
		return nil
	case v1.MigrationRunning:
		podList, err := md.vmService.GetRunningVMPods(vm)
		if err != nil {
			logger.Error().Reason(err).Msg("could not fetch a list of running VM target pods")
			return err
		}
		_, targetPod := investigateTargetPodSituation(migration, podList)
		if targetPod == nil {
			logger.Error().Msg("migration target pod does not exist or is in an end state")
			if err = setMigrationFailed(migration); err != nil {
				return err
			}
			return nil
		}
		switch targetPod.Status.Phase {
		case k8sv1.PodRunning:
			break
		case k8sv1.PodSucceeded, k8sv1.PodFailed:
			logger.Error().Msgf("migration target pod is in end state %s", targetPod.Status.Phase)
			if err = setMigrationFailed(migration); err != nil {
				return err
			}
			return nil
		default:
			//Not requeuing, just not far enough along to proceed
			logger.Info().V(3).Msg("target Pod not running yet")
			return nil
		}

		if vm.Status.MigrationNodeName != targetPod.Spec.NodeName {
			vm.Status.Phase = v1.Migrating
			vm.Status.MigrationNodeName = targetPod.Spec.NodeName
			if _, err = md.vmService.PutVm(vm); err != nil {
				logger.Error().Reason(err).Msgf("failed to update VM to state %s", v1.Migrating)
				return err
			}
		}

		// Let's check if the job already exists, it can already exist in case we could not update the VM object in a previous run
		migrationPod, exists, err := md.vmService.GetMigrationJob(migration)

		if err != nil {
			logger.Error().Reason(err).Msg("Checking for an existing migration job failed.")
			return err
		}

		if !exists {
			sourceNode, err := md.clientset.CoreV1().Nodes().Get(vm.Status.NodeName, metav1.GetOptions{})
			if err != nil {
				logger.Error().Reason(err).Msgf("fetching source node %s failed", vm.Status.NodeName)
				return err
			}
			targetNode, err := md.clientset.CoreV1().Nodes().Get(vm.Status.MigrationNodeName, metav1.GetOptions{})
			if err != nil {
				logger.Error().Reason(err).Msgf("fetching target node %s failed", vm.Status.MigrationNodeName)
				return err
			}

			if err := md.vmService.StartMigration(migration, vm, sourceNode, targetNode, targetPod); err != nil {
				logger.Error().Reason(err).Msg("Starting the migration job failed.")
				return err
			}
			return nil
		}

		// FIXME, the final state updates must come from virt-handler
		switch migrationPod.Status.Phase {
		case k8sv1.PodFailed:
			vm.Status.Phase = v1.Running
			vm.Status.MigrationNodeName = ""
			if _, err = md.vmService.PutVm(vm); err != nil {
				return err
			}
			if err = setMigrationFailed(migration); err != nil {
				return err
			}
		case k8sv1.PodSucceeded:
			vm.Status.NodeName = targetPod.Spec.NodeName
			vm.Status.MigrationNodeName = ""
			vm.Status.Phase = v1.Running
			if _, err = md.vmService.PutVm(vm); err != nil {
				logger.Error().Reason(err).Msg("updating the VM failed.")
				return err
			}
			if err = setMigrationPhase(migration, v1.MigrationSucceeded); err != nil {
				return err
			}
		}
	}
	return nil
}

func copy(migration *v1.Migration) (*v1.Migration, error) {
	obj, err := kubeapi.Scheme.Copy(migration)
	if err != nil {
		return nil, err
	}
	return obj.(*v1.Migration), nil
}

// Returns the number of  running pods and if a pod for exactly that migration is currently running
func investigateTargetPodSituation(migration *v1.Migration, podList *k8sv1.PodList) (int, *k8sv1.Pod) {
	var targetPod *k8sv1.Pod = nil
	for _, pod := range podList.Items {
		if pod.Labels[v1.MigrationUIDLabel] == string(migration.GetObjectMeta().GetUID()) {
			targetPod = &pod
		}
	}
	return len(podList.Items), targetPod
}

func mergeConstraints(migration *v1.Migration, vm *v1.VM) error {

	merged := map[string]string{}
	for k, v := range vm.Spec.NodeSelector {
		merged[k] = v
	}
	conflicts := []string{}
	for k, v := range migration.Spec.NodeSelector {
		val, exists := vm.Spec.NodeSelector[k]
		if exists && val != v {
			conflicts = append(conflicts, k)
		} else {
			merged[k] = v
		}
	}
	if len(conflicts) > 0 {
		return fmt.Errorf("Conflicting node selectors: %v", conflicts)
	}
	vm.Spec.NodeSelector = merged
	return nil
}
<commit_msg>break after selecting proper pod for migration<commit_after>package watch

import (
	"fmt"

	"k8s.io/client-go/kubernetes"
	kubeapi "k8s.io/client-go/pkg/api"
	k8sv1 "k8s.io/client-go/pkg/api/v1"
	metav1 "k8s.io/client-go/pkg/apis/meta/v1"
	"k8s.io/client-go/pkg/fields"
	"k8s.io/client-go/pkg/util/workqueue"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	"kubevirt.io/kubevirt/pkg/api/v1"
	"kubevirt.io/kubevirt/pkg/kubecli"
	"kubevirt.io/kubevirt/pkg/logging"
	"kubevirt.io/kubevirt/pkg/virt-controller/services"
)

func NewMigrationController(migrationService services.VMService, recorder record.EventRecorder, restClient *rest.RESTClient, clientset *kubernetes.Clientset) (cache.Store, *kubecli.Controller, *workqueue.RateLimitingInterface) {
	lw := cache.NewListWatchFromClient(restClient, "migrations", k8sv1.NamespaceDefault, fields.Everything())
	queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
	store, controller := kubecli.NewController(lw, queue, &v1.Migration{}, NewMigrationControllerDispatch(migrationService, restClient, clientset))
	return store, controller, &queue
}

func NewMigrationControllerDispatch(vmService services.VMService, restClient *rest.RESTClient, clientset *kubernetes.Clientset) kubecli.ControllerDispatch {

	dispatch := MigrationDispatch{
		restClient: restClient,
		vmService:  vmService,
		clientset:  clientset,
	}
	return &dispatch
}

type MigrationDispatch struct {
	restClient *rest.RESTClient
	vmService  services.VMService
	clientset  *kubernetes.Clientset
}

func (md *MigrationDispatch) Execute(store cache.Store, queue workqueue.RateLimitingInterface, key interface{}) {
	if err := md.execute(store, key.(string)); err != nil {
		logging.DefaultLogger().Info().Reason(err).Msgf("reenqueuing migration %v", key)
		queue.AddRateLimited(key)
	} else {
		logging.DefaultLogger().Info().V(4).Msgf("processed migration %v", key)
		queue.Forget(key)
	}
}

func (md *MigrationDispatch) execute(store cache.Store, key string) error {

	setMigrationPhase := func(migration *v1.Migration, phase v1.MigrationPhase) error {

		if migration.Status.Phase == phase {
			return nil
		}

		logger := logging.DefaultLogger().Object(migration)

		// Copy migration for future modifications
		migrationCopy, err := copy(migration)
		if err != nil {
			logger.Error().Reason(err).Msg("could not copy migration object")
			return err
		}

		migrationCopy.Status.Phase = phase
		// TODO indicate why it was set to failed
		err = md.vmService.UpdateMigration(migrationCopy)
		if err != nil {
			logger.Error().Reason(err).Msgf("updating migration state failed: %v ", err)
			return err
		}
		return nil
	}

	setMigrationFailed := func(mig *v1.Migration) error {
		return setMigrationPhase(mig, v1.MigrationFailed)
	}

	obj, exists, err := store.GetByKey(key)
	if err != nil {
		return err
	}
	if !exists {
		return nil
	}

	var migration *v1.Migration = obj.(*v1.Migration)
	logger := logging.DefaultLogger().Object(migration)

	vm, exists, err := md.vmService.FetchVM(migration.Spec.Selector.Name)
	if err != nil {
		logger.Error().Reason(err).Msgf("fetching the vm %s failed", migration.Spec.Selector.Name)
		return err
	}

	if !exists {
		logger.Info().Msgf("VM with name %s does not exist, marking migration as failed", migration.Spec.Selector.Name)
		if err = setMigrationFailed(migration); err != nil {
			return err
		}
		return nil
	}

	switch migration.Status.Phase {
	case v1.MigrationUnknown:
		if vm.Status.Phase != v1.Running {
			logger.Error().Msgf("VM with name %s is in state %s, no migration possible. Marking migration as failed", vm.GetObjectMeta().GetName(), vm.Status.Phase)
			if err = setMigrationFailed(migration); err != nil {
				return err
			}
			return nil
		}

		if err := mergeConstraints(migration, vm); err != nil {
			logger.Error().Reason(err).Msg("merging Migration and VM placement constraints failed.")
			return err
		}
		podList, err := md.vmService.GetRunningVMPods(vm)
		if err != nil {
			logger.Error().Reason(err).Msg("could not fetch a list of running VM target pods")
			return err
		}

		numOfPods, targetPod := investigateTargetPodSituation(migration, podList)

		if targetPod == nil {
			if numOfPods > 1 {
				logger.Error().Msg("another migration seems to be in progress, marking Migration as failed")
				// Another migration is currently going on
				if err = setMigrationFailed(migration); err != nil {
					return err
				}
				return nil
			} else if numOfPods == 1 {
				// We need to start a migration target pod
				// TODO, this detection is not optimal, it can lead to strange situations
				err := md.vmService.CreateMigrationTargetPod(migration, vm)
				if err != nil {
					logger.Error().Reason(err).Msg("creating a migration target pod failed")
					return err
				}
			}
		} else {
			if targetPod.Status.Phase == k8sv1.PodFailed {
				logger.Error().Msg("migration target pod is in failed state")
				if err = setMigrationFailed(migration); err != nil {
					return err
				}
				return nil
			}
			// Unlikely to hit this case, but prevents erroring out
			// if we re-enter this loop
			logger.Info().Msgf("migration appears to be set up, but was not set to %s", v1.MigrationScheduled)
		}
		err = setMigrationPhase(migration, v1.MigrationScheduled)
		if err != nil {
			return err
		}
		return nil
	case v1.MigrationScheduled:
		podList, err := md.vmService.GetRunningVMPods(vm)
		if err != nil {
			logger.Error().Reason(err).Msg("could not fetch a list of running VM target pods")
			return err
		}

		_, targetPod := investigateTargetPodSituation(migration, podList)

		if targetPod == nil {
			logger.Error().Msg("migration target pod does not exist or is an end state")
			if err = setMigrationFailed(migration); err != nil {
				return err
			}
			return nil
		}
		// Migration has been scheduled but no update on the status has been recorded
		err = setMigrationPhase(migration, v1.MigrationRunning)
		if err != nil {
			return err
		}
		return nil
	case v1.MigrationRunning:
		podList, err := md.vmService.GetRunningVMPods(vm)
		if err != nil {
			logger.Error().Reason(err).Msg("could not fetch a list of running VM target pods")
			return err
		}
		_, targetPod := investigateTargetPodSituation(migration, podList)
		if targetPod == nil {
			logger.Error().Msg("migration target pod does not exist or is in an end state")
			if err = setMigrationFailed(migration); err != nil {
				return err
			}
			return nil
		}
		switch targetPod.Status.Phase {
		case k8sv1.PodRunning:
			break
		case k8sv1.PodSucceeded, k8sv1.PodFailed:
			logger.Error().Msgf("migration target pod is in end state %s", targetPod.Status.Phase)
			if err = setMigrationFailed(migration); err != nil {
				return err
			}
			return nil
		default:
			//Not requeuing, just not far enough along to proceed
			logger.Info().V(3).Msg("target Pod not running yet")
			return nil
		}

		if vm.Status.MigrationNodeName != targetPod.Spec.NodeName {
			vm.Status.Phase = v1.Migrating
			vm.Status.MigrationNodeName = targetPod.Spec.NodeName
			if _, err = md.vmService.PutVm(vm); err != nil {
				logger.Error().Reason(err).Msgf("failed to update VM to state %s", v1.Migrating)
				return err
			}
		}

		// Let's check if the job already exists, it can already exist in case we could not update the VM object in a previous run
		migrationPod, exists, err := md.vmService.GetMigrationJob(migration)

		if err != nil {
			logger.Error().Reason(err).Msg("Checking for an existing migration job failed.")
			return err
		}

		if !exists {
			sourceNode, err := md.clientset.CoreV1().Nodes().Get(vm.Status.NodeName, metav1.GetOptions{})
			if err != nil {
				logger.Error().Reason(err).Msgf("fetching source node %s failed", vm.Status.NodeName)
				return err
			}
			targetNode, err := md.clientset.CoreV1().Nodes().Get(vm.Status.MigrationNodeName, metav1.GetOptions{})
			if err != nil {
				logger.Error().Reason(err).Msgf("fetching target node %s failed", vm.Status.MigrationNodeName)
				return err
			}

			if err := md.vmService.StartMigration(migration, vm, sourceNode, targetNode, targetPod); err != nil {
				logger.Error().Reason(err).Msg("Starting the migration job failed.")
				return err
			}
			return nil
		}

		// FIXME, the final state updates must come from virt-handler
		switch migrationPod.Status.Phase {
		case k8sv1.PodFailed:
			vm.Status.Phase = v1.Running
			vm.Status.MigrationNodeName = ""
			if _, err = md.vmService.PutVm(vm); err != nil {
				return err
			}
			if err = setMigrationFailed(migration); err != nil {
				return err
			}
		case k8sv1.PodSucceeded:
			vm.Status.NodeName = targetPod.Spec.NodeName
			vm.Status.MigrationNodeName = ""
			vm.Status.Phase = v1.Running
			if _, err = md.vmService.PutVm(vm); err != nil {
				logger.Error().Reason(err).Msg("updating the VM failed.")
				return err
			}
			if err = setMigrationPhase(migration, v1.MigrationSucceeded); err != nil {
				return err
			}
		}
	}
	return nil
}

func copy(migration *v1.Migration) (*v1.Migration, error) {
	obj, err := kubeapi.Scheme.Copy(migration)
	if err != nil {
		return nil, err
	}
	return obj.(*v1.Migration), nil
}

// Returns the number of  running pods and if a pod for exactly that migration is currently running
func investigateTargetPodSituation(migration *v1.Migration, podList *k8sv1.PodList) (int, *k8sv1.Pod) {
	var targetPod *k8sv1.Pod = nil
	for _, pod := range podList.Items {
		if pod.Labels[v1.MigrationUIDLabel] == string(migration.GetObjectMeta().GetUID()) {
			targetPod = &pod
			break
		}
	}
	return len(podList.Items), targetPod
}

func mergeConstraints(migration *v1.Migration, vm *v1.VM) error {

	merged := map[string]string{}
	for k, v := range vm.Spec.NodeSelector {
		merged[k] = v
	}
	conflicts := []string{}
	for k, v := range migration.Spec.NodeSelector {
		val, exists := vm.Spec.NodeSelector[k]
		if exists && val != v {
			conflicts = append(conflicts, k)
		} else {
			merged[k] = v
		}
	}
	if len(conflicts) > 0 {
		return fmt.Errorf("Conflicting node selectors: %v", conflicts)
	}
	vm.Spec.NodeSelector = merged
	return nil
}
<|endoftext|>


<commit_before>package message

import "fmt"

const (
	// Reset resets the color
	Reset = "\033[0m"

	// Bold makes the following text bold
	Bold = "\033[1m"

	// Dim dims the following text
	Dim = "\033[2m"

	// Italic makes the following text italic
	Italic = "\033[3m"

	// Underline underlines the following text
	Underline = "\033[4m"

	// Blink blinks the following text
	Blink = "\033[5m"

	// Invert inverts the following text
	Invert = "\033[7m"

	// Newline
	Newline = "\r\n"

	// BEL
	Bel = "\007"
)

// Interface for Styles
type Style interface {
	String() string
	Format(string) string
}

// General hardcoded style, mostly used as a crutch until we flesh out the
// framework to support backgrounds etc.
type style string

func (c style) String() string {
	return string(c)
}

func (c style) Format(s string) string {
	return c.String() + s + Reset
}

// 256 color type, for terminals who support it
type Color256 uint8

// String version of this color
func (c Color256) String() string {
	return fmt.Sprintf("38;05;%d", c)
}

// Return formatted string with this color
func (c Color256) Format(s string) string {
	return "\033[" + c.String() + "m" + s + Reset
}

// No color, used for mono theme
type Color0 struct{}

// No-op for Color0
func (c Color0) String() string {
	return ""
}

// No-op for Color0
func (c Color0) Format(s string) string {
	return s
}

// Container for a collection of colors
type Palette struct {
	colors []Style
	size   int
}

// Get a color by index, overflows are looped around.
func (p Palette) Get(i int) Style {
	if p.size == 1 {
		return p.colors[0]
	}
	return p.colors[i%(p.size-1)]
}

func (p Palette) Len() int {
	return p.size
}

func (p Palette) String() string {
	r := ""
	for _, c := range p.colors {
		r += c.Format("X")
	}
	return r
}

// Collection of settings for chat
type Theme struct {
	id        string
	sys       Style
	pm        Style
	highlight Style
	names     *Palette
}

func (t Theme) Id() string {
	return t.id
}

// Colorize name string given some index
func (t Theme) ColorName(u *User) string {
	if t.names == nil {
		return u.Name()
	}

	return t.names.Get(u.colorIdx).Format(u.Name())
}

// Colorize the PM string
func (t Theme) ColorPM(s string) string {
	if t.pm == nil {
		return s
	}

	return t.pm.Format(s)
}

// Colorize the Sys message
func (t Theme) ColorSys(s string) string {
	if t.sys == nil {
		return s
	}

	return t.sys.Format(s)
}

// Highlight a matched string, usually name
func (t Theme) Highlight(s string) string {
	if t.highlight == nil {
		return s
	}
	return t.highlight.Format(s)
}

// List of initialzied themes
var Themes []Theme

// Default theme to use
var DefaultTheme *Theme

func readableColors256() *Palette {
	size := 247
	p := Palette{
		colors: make([]Style, size),
		size:   size,
	}
	j := 0
	for i := 0; i < 256; i++ {
		if (16 <= i && i <= 18) || (232 <= i && i <= 237) {
			// Remove the ones near black, this is kinda sadpanda.
			continue
		}
		p.colors[j] = Color256(i)
		j++
	}
	return &p
}

func init() {
	palette := readableColors256()

	Themes = []Theme{
		{
			id:        "colors",
			names:     palette,
			sys:       palette.Get(8),                             // Grey
			pm:        palette.Get(7),                             // White
			highlight: style(Bold + "\033[48;5;11m\033[38;5;16m"), // Yellow highlight
		},
		{
			id: "mono",
		},
	}

	// Debug for printing colors:
	//for _, color := range palette.colors {
	//	fmt.Print(color.Format(color.String() + " "))
	//}

	DefaultTheme = &Themes[0]
}
<commit_msg>chat/message/theme: Two new themes, solarized and hacker (#196)<commit_after>package message

import "fmt"

const (
	// Reset resets the color
	Reset = "\033[0m"

	// Bold makes the following text bold
	Bold = "\033[1m"

	// Dim dims the following text
	Dim = "\033[2m"

	// Italic makes the following text italic
	Italic = "\033[3m"

	// Underline underlines the following text
	Underline = "\033[4m"

	// Blink blinks the following text
	Blink = "\033[5m"

	// Invert inverts the following text
	Invert = "\033[7m"

	// Newline
	Newline = "\r\n"

	// BEL
	Bel = "\007"
)

// Interface for Styles
type Style interface {
	String() string
	Format(string) string
}

// General hardcoded style, mostly used as a crutch until we flesh out the
// framework to support backgrounds etc.
type style string

func (c style) String() string {
	return string(c)
}

func (c style) Format(s string) string {
	return c.String() + s + Reset
}

// 256 color type, for terminals who support it
type Color256 uint8

// String version of this color
func (c Color256) String() string {
	return fmt.Sprintf("38;05;%d", c)
}

// Return formatted string with this color
func (c Color256) Format(s string) string {
	return "\033[" + c.String() + "m" + s + Reset
}

// No color, used for mono theme
type Color0 struct{}

// No-op for Color0
func (c Color0) String() string {
	return ""
}

// No-op for Color0
func (c Color0) Format(s string) string {
	return s
}

// Container for a collection of colors
type Palette struct {
	colors []Style
	size   int
}

// Get a color by index, overflows are looped around.
func (p Palette) Get(i int) Style {
	if p.size == 1 {
		return p.colors[0]
	}
	return p.colors[i%(p.size-1)]
}

func (p Palette) Len() int {
	return p.size
}

func (p Palette) String() string {
	r := ""
	for _, c := range p.colors {
		r += c.Format("X")
	}
	return r
}

// Collection of settings for chat
type Theme struct {
	id        string
	sys       Style
	pm        Style
	highlight Style
	names     *Palette
}

func (t Theme) Id() string {
	return t.id
}

// Colorize name string given some index
func (t Theme) ColorName(u *User) string {
	if t.names == nil {
		return u.Name()
	}

	return t.names.Get(u.colorIdx).Format(u.Name())
}

// Colorize the PM string
func (t Theme) ColorPM(s string) string {
	if t.pm == nil {
		return s
	}

	return t.pm.Format(s)
}

// Colorize the Sys message
func (t Theme) ColorSys(s string) string {
	if t.sys == nil {
		return s
	}

	return t.sys.Format(s)
}

// Highlight a matched string, usually name
func (t Theme) Highlight(s string) string {
	if t.highlight == nil {
		return s
	}
	return t.highlight.Format(s)
}

// List of initialzied themes
var Themes []Theme

// Default theme to use
var DefaultTheme *Theme

func readableColors256() *Palette {
	size := 247
	p := Palette{
		colors: make([]Style, size),
		size:   size,
	}
	j := 0
	for i := 0; i < 256; i++ {
		if (16 <= i && i <= 18) || (232 <= i && i <= 237) {
			// Remove the ones near black, this is kinda sadpanda.
			continue
		}
		p.colors[j] = Color256(i)
		j++
	}
	return &p
}

// A theme that users Solarized theme accents
func solarizedColors() *Palette {
	size := 9
	p := Palette{
		colors: make([]Style, size),
		size:   size,
	}
	nums := [9]int{1, 2, 3, 4, 5, 6, 7, 9, 13}
	for x := 0; x < 9; x++ {
		p.colors[x] = Color256(nums[x])
	}
	return &p
}

// Hacker green colors (only uses one color)
func hackerColors() *Palette {
	size := 1
	p := Palette{
		colors: make([]Style, size),
		size:   size,
	}
	p.colors[0] = Color256(82)
	return &p
}

func init() {
	palette := readableColors256()
	solar   := solarizedColors()
	hacker  := hackerColors()

	Themes = []Theme{
		{
			id:        "colors",
			names:     palette,
			sys:       palette.Get(8),                             // Grey
			pm:        palette.Get(7),                             // White
			highlight: style(Bold + "\033[48;5;11m\033[38;5;16m"), // Yellow highlight
		},
		{
			id:        "solarized",
			names:     solar,
			sys:       Color256(11),
			pm:        Color256(15),
			highlight: style(Bold + "\033[48;5;202m\033[38;5;256m"), // orange highlight
		},
		{
			id:        "hacker",
			names:     hacker,
			sys:       Color256(22),
			pm:        Color256(22),
			highlight: style(Bold + "\033[48;5;22m\033[38;5;256m"), // green bg black fg
		},
		{
			id: "mono",
		},
	}

	// Debug for printing colors:
	//for _, color := range palette.colors {
	//	fmt.Print(color.Format(color.String() + " "))
	//}

	DefaultTheme = &Themes[0]
}
<|endoftext|>


<commit_before>package HologramGo

import (
	"fmt"
	"os"
)

type Plan map[string]interface{}

// Plans is just a list of Plan(s).
type Plans []Plan

// EFFECTS: Returns device data plans.
func GetDeviceDataPlans() Plan {

	req := createGetRequest("/plans/")

	resp, err := sendRequest(req)
	if err != nil {
		fmt.Printf("Could not send request: %v\n", err)
		os.Exit(1)
	}

	var payload = Placeholder{}
	err = resp.Parse(&payload)
	// error handling
	if err != nil {
		fmt.Printf("Problem parsing response: %v\n", err)
		os.Exit(1)
	}

	return payload["data"].(map[string]interface{})
}

// REQUIRES: A plan id.
// EFFECTS: Returns a given device data plan
func GetDeviceDataPlan(planid string) Plan {

	req := createGetRequest("/plans/" + string(planid))

	resp, err := sendRequest(req)
	if err != nil {
		fmt.Printf("Could not send request: %v\n", err)
		os.Exit(1)
	}

	var payload = Placeholder{}
	err = resp.Parse(&payload)
	// error handling
	if err != nil {
		fmt.Printf("Problem parsing response: %v\n", err)
		os.Exit(1)
	}

	return payload["data"].(map[string]interface{})
}
<commit_msg>added Plan returned value getters<commit_after>package HologramGo

import (
	"fmt"
	"os"
)

type Plan map[string]interface{}

// Plans is just a list of Plan(s).
type Plans []Plan

// EFFECTS: Returns device data plans.
func GetDeviceDataPlans() Plan {

	req := createGetRequest("/plans/")

	resp, err := sendRequest(req)
	if err != nil {
		fmt.Printf("Could not send request: %v\n", err)
		os.Exit(1)
	}

	var payload = Placeholder{}
	err = resp.Parse(&payload)
	// error handling
	if err != nil {
		fmt.Printf("Problem parsing response: %v\n", err)
		os.Exit(1)
	}

	return payload["data"].(map[string]interface{})
}

// REQUIRES: A plan id.
// EFFECTS: Returns a given device data plan
func GetDeviceDataPlan(planid string) Plan {

	req := createGetRequest("/plans/" + string(planid))

	resp, err := sendRequest(req)
	if err != nil {
		fmt.Printf("Could not send request: %v\n", err)
		os.Exit(1)
	}

	var payload = Placeholder{}
	err = resp.Parse(&payload)
	// error handling
	if err != nil {
		fmt.Printf("Problem parsing response: %v\n", err)
		os.Exit(1)
	}

	return payload["data"].(map[string]interface{})
}

// EFFECTS: Returns the data plan id.
func (plan Plan) GetDataPlanId(map[string]interface{}) float64 {
	return plan["id"].(float64)
}

// EFFECTS: Returns the data plan partner id.
func (plan Plan) GetDataPlanPartnerId(map[string]interface{}) float64 {
	return plan["partnerid"].(float64)
}

// EFFECTS: Returns the data plan name.
func (plan Plan) GetDataPlanName(map[string]interface{}) string {
	return plan["name"].(string)
}

// EFFECTS: Returns the data plan description.
func (plan Plan) GetDataPlanDescription(map[string]interface{}) string {
	return plan["description"].(string)
}

// EFFECTS: Returns the data size.
func (plan Plan) GetDataPlanSize(map[string]interface{}) float64 {
	return plan["size"].(float64)
}

// EFFECTS: Returns true if it is recurring.
func (plan Plan) IsDataPlanRecurring(map[string]interface{}) bool {
	return plan["recurring"].(bool)
}

// EFFECTS: Returns true if the data plan is enabled.
func (plan Plan) IsDataPlanEnabled(map[string]interface{}) bool {
	return plan["enabled"].(bool)
}

// EFFECTS: Returns the billing period.
func (plan Plan) GetDataPlanBillingPeriod(map[string]interface{}) float64 {
	return plan["billingperiod"].(float64)
}

// EFFECTS: Returns the number of trial days left.
func (plan Plan) GetDataPlanTrialDays(map[string]interface{}) float64 {
	return plan["traildays"].(float64)
}

// EFFECTS: Returns the data plan template id.
func (plan Plan) GetDataPlanTemplateId(map[string]interface{}) float64 {
	return plan["templateid"].(float64)
}

// EFFECTS: Returns the carrier id of the data plan.
func (plan Plan) GetDataPlanCarrierId(map[string]interface{}) float64 {
	return plan["carrierid"].(float64)
}

// EFFECTS: Returns the groupid of the data plan.
func (plan Plan) GetDataPlanGroupId(map[string]interface{}) float64 {
	return plan["groupid"].(float64)
}
<|endoftext|>


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

package git

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

// objectCache provides thread-safe cache opeations.
type objectCache struct {
	lock  sync.RWMutex
	cache map[string]interface{}
}

func newObjectCache() *objectCache {
	return &objectCache{
		cache: make(map[string]interface{}, 10),
	}
}

func (oc *objectCache) Set(id string, obj interface{}) {
	oc.lock.Lock()
	defer oc.lock.Unlock()

	oc.cache[id] = obj
}

func (oc *objectCache) Get(id string) (interface{}, bool) {
	oc.lock.RLock()
	defer oc.lock.RUnlock()

	obj, has := oc.cache[id]
	return obj, has
}

// isDir returns true if given path is a directory,
// or returns false when it's a file or does not exist.
func isDir(dir string) bool {
	f, e := os.Stat(dir)
	if e != nil {
		return false
	}
	return f.IsDir()
}

// isFile returns true if given path is a file,
// or returns false when it's a directory or does not exist.
func isFile(filePath string) bool {
	f, e := os.Stat(filePath)
	if e != nil {
		return false
	}
	return !f.IsDir()
}

// isExist checks whether a file or directory exists.
// It returns false when the file or directory does not exist.
func isExist(path string) bool {
	_, err := os.Stat(path)
	return err == nil || os.IsExist(err)
}

func concatenateError(err error, stderr string) error {
	if len(stderr) == 0 {
		return err
	}
	return fmt.Errorf("%v - %s", err, stderr)
}

// If the object is stored in its own file (i.e not in a pack file),
// this function returns the full path to the object file.
// It does not test if the file exists.
func filepathFromSHA1(rootdir, sha1 string) string {
	return filepath.Join(rootdir, "objects", sha1[:2], sha1[2:])
}

func RefEndName(refStr string) string {
	if strings.HasPrefix(refStr, "refs/heads/") {
		// trim the "refs/heads/"
		return refStr[len("refs/heads/"):]
	}

	if strings.HasPrefix(refStr, "refs/tags/") {
		// trim the "refs/heads/"
		return refStr[len("refs/tags/"):]
	}

	return refStr
}
<commit_msg>Minor fix for #6<commit_after>// Copyright 2015 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package git

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

// objectCache provides thread-safe cache opeations.
type objectCache struct {
	lock  sync.RWMutex
	cache map[string]interface{}
}

func newObjectCache() *objectCache {
	return &objectCache{
		cache: make(map[string]interface{}, 10),
	}
}

func (oc *objectCache) Set(id string, obj interface{}) {
	oc.lock.Lock()
	defer oc.lock.Unlock()

	oc.cache[id] = obj
}

func (oc *objectCache) Get(id string) (interface{}, bool) {
	oc.lock.RLock()
	defer oc.lock.RUnlock()

	obj, has := oc.cache[id]
	return obj, has
}

// isDir returns true if given path is a directory,
// or returns false when it's a file or does not exist.
func isDir(dir string) bool {
	f, e := os.Stat(dir)
	if e != nil {
		return false
	}
	return f.IsDir()
}

// isFile returns true if given path is a file,
// or returns false when it's a directory or does not exist.
func isFile(filePath string) bool {
	f, e := os.Stat(filePath)
	if e != nil {
		return false
	}
	return !f.IsDir()
}

// isExist checks whether a file or directory exists.
// It returns false when the file or directory does not exist.
func isExist(path string) bool {
	_, err := os.Stat(path)
	return err == nil || os.IsExist(err)
}

func concatenateError(err error, stderr string) error {
	if len(stderr) == 0 {
		return err
	}
	return fmt.Errorf("%v - %s", err, stderr)
}

// If the object is stored in its own file (i.e not in a pack file),
// this function returns the full path to the object file.
// It does not test if the file exists.
func filepathFromSHA1(rootdir, sha1 string) string {
	return filepath.Join(rootdir, "objects", sha1[:2], sha1[2:])
}

func RefEndName(refStr string) string {
	if strings.HasPrefix(refStr, BRANCH_PREFIX) {
		return refStr[len(BRANCH_PREFIX):]
	}

	if strings.HasPrefix(refStr, TAG_PREFIX) {
		return refStr[len(TAG_PREFIX):]
	}

	return refStr
}
<|endoftext|>