<commit_before>package tradier

import (
	"encoding/json"
	"time"
)

// Orders represents the orders JSON object.
type Orders []*Order

// AccountOrders is used to hold user orders
type AccountOrders Orders

// Order represents the `order` JSON object.
type Order struct {
	AvgFillPrice      *float64   `json:"avg_fill_price,omitempty"`
	Class             *string    `json:"class,omitempty"`
	CreateDate        *time.Time `json:"create_date,omitempty"`
	Duration          *string    `json:"duration,omitempty"`
	ExecQuantity      *float64   `json:"exec_quantity,omitempty"`
	ID                *int       `json:"id,omitempty"`
	LastFillPrice     *float64   `json:"last_fill_price,omitempty"`
	LastFillQuantity  *float64   `json:"last_fill_quantity,omitempty"`
	Quantity          *float64   `json:"quantity,omitempty"`
	RemainingQuantity *float64   `json:"remaining_quantity,omitempty"`
	Side              *string    `json:"side,omitempty"`
	Status            *string    `json:"status,omitempty"`
	Symbol            *string    `json:"symbol,omitempty"`
	TransactionDate   *time.Time `json:"transaction_date,omitempty"`
	Type              *string    `json:"type,omitempty"`

	// Specific to order creation
	PartnerID *string `json:"partner_id,omitempty"`

	// Specific to order preview
	Commission    *float64 `json:"commission,omitempty"`
	Cost          *float64 `json:"cost,omitempty"`
	ExtendedHours *bool    `json:"extended_hours,omitempty"`
	Fees          *float64 `json:"fees,omitempty"`
	MarginChange  *float64 `json:"margin_change,omitempty"`
	Result        *bool    `json:"result,omitempty"`   // Not present in documentation
	Strategy      *string  `json:"strategy,omitempty"` // Not present in documentation, specific to multileg
	unwrapped     bool
}

type order Order

// UnmarshalJSON unmarshals order into Order object.
func (o *Order) UnmarshalJSON(b []byte) error {
	var oc struct {
		*order `json:"order,omitempty"`
	}
	oObj := order{}

	// If wrapped in watchlist object
	if err := json.Unmarshal(b, &oc); err == nil {
		if oc.order != nil {
			*o = Order(*oc.order)
			return nil
		}
	}

	// If not wrapped in anything
	if err := json.Unmarshal(b, &oObj); err == nil {
		*o = Order(oObj)
		return nil
	}

	return nil
}

// MarshalJSON marshals Order into its JSON representation.
func (o *Order) MarshalJSON() ([]byte, error) {
	if o.unwrapped {
		return json.Marshal(*o)
	}

	return json.Marshal(map[string]interface{}{
		"order": *o,
	})
}

// UnmarshalJSON unmarshals orders into Orders object.
func (o *Orders) UnmarshalJSON(b []byte) (err error) {
	// fmt.Println(string(b))
	var oCollection struct {
		O struct {
			O []*Order `json:"order,omitempty"`
		} `json:"orders,omitempty"`
	}
	var oObject struct {
		O struct {
			O *Order `json:"order,omitempty"`
		} `json:"orders,omitempty"`
	}
	var oNull string

	var oUnwrapped struct {
		O []*Order `json:"order,omitempty"`
	}

	var oSingle struct {
		O *Order `json:"order,omitempty"`
	}

	// If unwrapped from user object
	if err = json.Unmarshal(b, &oUnwrapped); err == nil {
		if len(oUnwrapped.O) > 0 {
			*o = oUnwrapped.O
			return nil
		}
	}

	// If unwrapped from user object
	if err = json.Unmarshal(b, &oSingle); err == nil {
		if oSingle.O != nil {
			*o = Orders{oSingle.O}
			return nil
		}
	}

	// If order is a string, i.e. "null"
	if err = json.Unmarshal(b, &oNull); err == nil {
		return nil
	}

	// If order is a JSON array
	if err = json.Unmarshal(b, &oCollection); err == nil {
		*o = oCollection.O.O
		return nil
	}

	// If order is an object
	if err = json.Unmarshal(b, &oObject); err == nil {
		tmp := make([]*Order, 0)
		tmp = append(tmp, oObject.O.O)
		*o = Orders(tmp)
		return nil
	}

	return nil
}

// MarshalJSON marshals Orders into JSON.
func (o *Orders) MarshalJSON() ([]byte, error) {
	// Set wrapped to true to marshal differently
	for _, order := range *o {
		order.unwrapped = true
	}

	// If []Watchlist is empty
	if len(*o) == 0 {
		return json.Marshal(map[string]interface{}{
			"orders": "null",
		})
	}

	// If []Watchlist is size 1, return first and only object
	if len(*o) == 1 {
		order := *o
		return json.Marshal(map[string]interface{}{
			"orders": map[string]interface{}{
				"order": order[0],
			},
		})
	}

	// Otherwhise marshal normally
	return json.Marshal(map[string]interface{}{
		"orders": map[string]interface{}{
			"order": *o,
		},
	})
}

// UnmarshalJSON unmarshals orders into Orders object.
func (o *AccountOrders) UnmarshalJSON(b []byte) (err error) {
	// fmt.Println(string(b))
	var oCollection struct {
		O struct {
			O []*Order `json:"order,omitempty"`
		} `json:"orders,omitempty"`
	}
	var oObject struct {
		O struct {
			O *Order `json:"order,omitempty"`
		} `json:"orders,omitempty"`
	}
	var oNull string

	var oUnwrapped struct {
		O []*Order `json:"order,omitempty"`
	}

	var oSingle struct {
		O *Order `json:"order,omitempty"`
	}

	// If unwrapped from user object
	if err = json.Unmarshal(b, &oUnwrapped); err == nil {
		if len(oUnwrapped.O) > 0 {
			*o = oUnwrapped.O
			return nil
		}
	}

	// If unwrapped from user object
	if err = json.Unmarshal(b, &oSingle); err == nil {
		if oSingle.O != nil {
			*o = AccountOrders{oSingle.O}
			return nil
		}
	}

	// If order is a string, i.e. "null"
	if err = json.Unmarshal(b, &oNull); err == nil {
		return nil
	}

	// If order is a JSON array
	if err = json.Unmarshal(b, &oCollection); err == nil {
		*o = oCollection.O.O
		return nil
	}

	// If order is an object
	if err = json.Unmarshal(b, &oObject); err == nil {
		tmp := make([]*Order, 0)
		tmp = append(tmp, oObject.O.O)
		*o = AccountOrders(tmp)
		return nil
	}

	return nil
}

// MarshalJSON marshals Orders into JSON.
func (o *AccountOrders) MarshalJSON() ([]byte, error) {
	// Set wrapped to true to marshal differently
	for _, order := range *o {
		order.unwrapped = true
	}

	// If []Watchlist is empty
	if len(*o) == 0 {
		return json.Marshal(map[string]interface{}{
			"orders": "null",
		})
	}

	// If []Watchlist is size 1, return first and only object
	if len(*o) == 1 {
		order := *o
		return json.Marshal(map[string]interface{}{
			"order": order[0],
		})
	}

	// Otherwhise marshal normally
	return json.Marshal(map[string]interface{}{
		"order": *o,
	})
}
<commit_msg>Add missing order_cost field<commit_after>package tradier

import (
	"encoding/json"
	"time"
)

// Orders represents the orders JSON object.
type Orders []*Order

// AccountOrders is used to hold user orders
type AccountOrders Orders

// Order represents the `order` JSON object.
type Order struct {
	AvgFillPrice      *float64   `json:"avg_fill_price,omitempty"`
	Class             *string    `json:"class,omitempty"`
	CreateDate        *time.Time `json:"create_date,omitempty"`
	Duration          *string    `json:"duration,omitempty"`
	ExecQuantity      *float64   `json:"exec_quantity,omitempty"`
	ID                *int       `json:"id,omitempty"`
	LastFillPrice     *float64   `json:"last_fill_price,omitempty"`
	LastFillQuantity  *float64   `json:"last_fill_quantity,omitempty"`
	Quantity          *float64   `json:"quantity,omitempty"`
	RemainingQuantity *float64   `json:"remaining_quantity,omitempty"`
	Side              *string    `json:"side,omitempty"`
	Status            *string    `json:"status,omitempty"`
	Symbol            *string    `json:"symbol,omitempty"`
	TransactionDate   *time.Time `json:"transaction_date,omitempty"`
	Type              *string    `json:"type,omitempty"`

	// Specific to order creation
	PartnerID *string `json:"partner_id,omitempty"`

	// Specific to order preview
	Commission    *float64 `json:"commission,omitempty"`
	Cost          *float64 `json:"cost,omitempty"`
	ExtendedHours *bool    `json:"extended_hours,omitempty"`
	Fees          *float64 `json:"fees,omitempty"`
	MarginChange  *float64 `json:"margin_change,omitempty"`
	OrderCost     *float64 `json:"order_cost,omitempty"`
	Result        *bool    `json:"result,omitempty"`   // Not present in documentation
	Strategy      *string  `json:"strategy,omitempty"` // Not present in documentation, specific to multileg
	unwrapped     bool
}

type order Order

// UnmarshalJSON unmarshals order into Order object.
func (o *Order) UnmarshalJSON(b []byte) error {
	var oc struct {
		*order `json:"order,omitempty"`
	}
	oObj := order{}

	// If wrapped in watchlist object
	if err := json.Unmarshal(b, &oc); err == nil {
		if oc.order != nil {
			*o = Order(*oc.order)
			return nil
		}
	}

	// If not wrapped in anything
	if err := json.Unmarshal(b, &oObj); err == nil {
		*o = Order(oObj)
		return nil
	}

	return nil
}

// MarshalJSON marshals Order into its JSON representation.
func (o *Order) MarshalJSON() ([]byte, error) {
	if o.unwrapped {
		return json.Marshal(*o)
	}

	return json.Marshal(map[string]interface{}{
		"order": *o,
	})
}

// UnmarshalJSON unmarshals orders into Orders object.
func (o *Orders) UnmarshalJSON(b []byte) (err error) {
	// fmt.Println(string(b))
	var oCollection struct {
		O struct {
			O []*Order `json:"order,omitempty"`
		} `json:"orders,omitempty"`
	}
	var oObject struct {
		O struct {
			O *Order `json:"order,omitempty"`
		} `json:"orders,omitempty"`
	}
	var oNull string

	var oUnwrapped struct {
		O []*Order `json:"order,omitempty"`
	}

	var oSingle struct {
		O *Order `json:"order,omitempty"`
	}

	// If unwrapped from user object
	if err = json.Unmarshal(b, &oUnwrapped); err == nil {
		if len(oUnwrapped.O) > 0 {
			*o = oUnwrapped.O
			return nil
		}
	}

	// If unwrapped from user object
	if err = json.Unmarshal(b, &oSingle); err == nil {
		if oSingle.O != nil {
			*o = Orders{oSingle.O}
			return nil
		}
	}

	// If order is a string, i.e. "null"
	if err = json.Unmarshal(b, &oNull); err == nil {
		return nil
	}

	// If order is a JSON array
	if err = json.Unmarshal(b, &oCollection); err == nil {
		*o = oCollection.O.O
		return nil
	}

	// If order is an object
	if err = json.Unmarshal(b, &oObject); err == nil {
		tmp := make([]*Order, 0)
		tmp = append(tmp, oObject.O.O)
		*o = Orders(tmp)
		return nil
	}

	return nil
}

// MarshalJSON marshals Orders into JSON.
func (o *Orders) MarshalJSON() ([]byte, error) {
	// Set wrapped to true to marshal differently
	for _, order := range *o {
		order.unwrapped = true
	}

	// If []Watchlist is empty
	if len(*o) == 0 {
		return json.Marshal(map[string]interface{}{
			"orders": "null",
		})
	}

	// If []Watchlist is size 1, return first and only object
	if len(*o) == 1 {
		order := *o
		return json.Marshal(map[string]interface{}{
			"orders": map[string]interface{}{
				"order": order[0],
			},
		})
	}

	// Otherwhise marshal normally
	return json.Marshal(map[string]interface{}{
		"orders": map[string]interface{}{
			"order": *o,
		},
	})
}

// UnmarshalJSON unmarshals orders into Orders object.
func (o *AccountOrders) UnmarshalJSON(b []byte) (err error) {
	// fmt.Println(string(b))
	var oCollection struct {
		O struct {
			O []*Order `json:"order,omitempty"`
		} `json:"orders,omitempty"`
	}
	var oObject struct {
		O struct {
			O *Order `json:"order,omitempty"`
		} `json:"orders,omitempty"`
	}
	var oNull string

	var oUnwrapped struct {
		O []*Order `json:"order,omitempty"`
	}

	var oSingle struct {
		O *Order `json:"order,omitempty"`
	}

	// If unwrapped from user object
	if err = json.Unmarshal(b, &oUnwrapped); err == nil {
		if len(oUnwrapped.O) > 0 {
			*o = oUnwrapped.O
			return nil
		}
	}

	// If unwrapped from user object
	if err = json.Unmarshal(b, &oSingle); err == nil {
		if oSingle.O != nil {
			*o = AccountOrders{oSingle.O}
			return nil
		}
	}

	// If order is a string, i.e. "null"
	if err = json.Unmarshal(b, &oNull); err == nil {
		return nil
	}

	// If order is a JSON array
	if err = json.Unmarshal(b, &oCollection); err == nil {
		*o = oCollection.O.O
		return nil
	}

	// If order is an object
	if err = json.Unmarshal(b, &oObject); err == nil {
		tmp := make([]*Order, 0)
		tmp = append(tmp, oObject.O.O)
		*o = AccountOrders(tmp)
		return nil
	}

	return nil
}

// MarshalJSON marshals Orders into JSON.
func (o *AccountOrders) MarshalJSON() ([]byte, error) {
	// Set wrapped to true to marshal differently
	for _, order := range *o {
		order.unwrapped = true
	}

	// If []Watchlist is empty
	if len(*o) == 0 {
		return json.Marshal(map[string]interface{}{
			"orders": "null",
		})
	}

	// If []Watchlist is size 1, return first and only object
	if len(*o) == 1 {
		order := *o
		return json.Marshal(map[string]interface{}{
			"order": order[0],
		})
	}

	// Otherwhise marshal normally
	return json.Marshal(map[string]interface{}{
		"order": *o,
	})
}
<|endoftext|>


<commit_before>package main

// keys.go contains functions for generating and printing keys.

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

	"github.com/spf13/cobra"

	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/types"
)

const (
	// The header for all siakg files. Do not change.
	FileHeader = "siakg"
)

// A KeyPair is the object that gets saved to disk for a signature key. All the
// information necessary to sign a transaction is in the struct, and the struct
// can be directly written to disk.
type KeyPair struct {
	Header           string
	Version          string
	Index            int
	SecretKey        crypto.SecretKey
	PublicKey        crypto.PublicKey
	UnlockConditions types.UnlockConditions
}

// generateKeys will generate a set of keys and save the keyfiles to disk.
func generateKeys(*cobra.Command, []string) {
	// Check that the key requirements make sense.
	if config.Siakg.RequiredKeys == 0 {
		fmt.Println("An address with 0 required keys is not useful.")
		return
	}
	if config.Siakg.TotalKeys < config.Siakg.RequiredKeys {
		fmt.Printf("Total Keys (%v) must be greater than or equal to Required Keys (%v)\n", config.Siakg.TotalKeys, config.Siakg.RequiredKeys)
		return
	}

	fmt.Printf("Creating key '%s' with %v total keys and %v required keys.\n", config.Siakg.KeyName, config.Siakg.TotalKeys, config.Siakg.RequiredKeys)

	// Generate 'TotalKeys' keyparis and fill out the metadata.
	keys := make([]KeyPair, config.Siakg.TotalKeys)
	for i := range keys {
		keys[i].Header = FileHeader
		keys[i].Version = Version
		keys[i].Index = i
	}

	// Add the keys to each keypair.
	for i := 0; i < config.Siakg.TotalKeys; i++ {
		var err error
		keys[i].SecretKey, keys[i].PublicKey, err = crypto.GenerateSignatureKeys()
		if err != nil {
			fmt.Println(err)
			return
		}
	}

	// Generate the unlock conditions and add them to each KeyPair object.
	uc := types.UnlockConditions{
		Timelock:      0,
		NumSignatures: uint64(config.Siakg.RequiredKeys),
	}
	for i := range keys {
		uc.PublicKeys = append(uc.PublicKeys, types.SiaPublicKey{
			Algorithm: types.SignatureEd25519,
			Key:       string(keys[i].PublicKey[:]),
		})
	}
	for i := range keys {
		keys[i].UnlockConditions = uc
	}

	// Save the KeyPairs to disk.
	if config.Siakg.Folder != "" {
		err := os.MkdirAll(config.Siakg.Folder, 0700)
		if err != nil {
			fmt.Println(err)
			return
		}
	}
	for i, key := range keys {
		err := encoding.WriteFile(filepath.Join(config.Siakg.Folder, config.Siakg.KeyName)+"_Key"+strconv.Itoa(i)+FileExtension, key)
		if err != nil {
			fmt.Println(err)
			return
		}
	}

	// Load the keys from disk back into memory, then verifiy that the keys on
	// disk are able to sign outputs in transactions.
	loadedKeys := make([]KeyPair, config.Siakg.TotalKeys)
	for i := 0; i < len(loadedKeys); i++ {
		err := encoding.ReadFile(filepath.Join(config.Siakg.Folder, config.Siakg.KeyName)+"_Key"+strconv.Itoa(i)+FileExtension, &loadedKeys[i])
		if err != nil {
			fmt.Println(err)
			return
		}
	}

	// Check that the keys can be used to spend transactions. Load them back into memory.
	for _, loadedKey := range loadedKeys {
		if loadedKey.UnlockConditions.UnlockHash() != uc.UnlockHash() {
			fmt.Println("corruption occured while saving the keys to disk")
		}
	}
	// Create a transaction for the keys to sign.
	txn := types.Transaction{
		SiafundInputs: []types.SiafundInput{
			types.SiafundInput{
				UnlockConditions: loadedKeys[0].UnlockConditions,
			},
		},
	}
	// Loop through and sign the transaction multiple times. All keys will be
	// used at least once by the time the loop terminates.
	var i int
	for i != config.Siakg.TotalKeys {
		// i tracks which key is next to be used. If i + RequiredKeys results
		// in going out-of-bounds, reduce i so that the last key will be used
		// for the final signature.
		if i+config.Siakg.RequiredKeys > config.Siakg.TotalKeys {
			i = config.Siakg.TotalKeys - config.Siakg.RequiredKeys
		}
		var j int
		for j < config.Siakg.RequiredKeys {
			txn.Signatures = append(txn.Signatures, types.TransactionSignature{
				PublicKeyIndex: uint64(i),
				CoveredFields:  types.CoveredFields{WholeTransaction: true},
			})
			sigHash := txn.SigHash(j)
			sig, err := crypto.SignHash(sigHash, loadedKeys[i].SecretKey)
			if err != nil {
				fmt.Println(err)
				return
			}
			txn.Signatures[j].Signature = types.Signature(sig[:])
			i++
			j++
		}
		// Check that the signature is valid.
		err := txn.StandaloneValid(0)
		if err != nil {
			fmt.Println(err)
		}
		// Delete all of the signatures for the next iteration.
		txn.Signatures = nil
	}

	fmt.Printf("Success, the address for this set of keys is: %x\n", uc.UnlockHash())
}

// printKey opens a keyfile and prints the contents.
func printKey(*cobra.Command, []string) {
	var kp KeyPair
	err := encoding.ReadFile(config.Address.Filename, &kp)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Printf("Found a key for a %v of %v address.\n", kp.UnlockConditions.NumSignatures, len(kp.UnlockConditions.PublicKeys))
	fmt.Printf("The address is: %x\n", kp.UnlockConditions.UnlockHash())
}
<commit_msg>remove the public key from siakg<commit_after>package main

// keys.go contains functions for generating and printing keys.

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

	"github.com/spf13/cobra"

	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/types"
)

const (
	// The header for all siakg files. Do not change.
	FileHeader = "siakg"
)

// A KeyPair is the object that gets saved to disk for a signature key. All the
// information necessary to sign a transaction is in the struct, and the struct
// can be directly written to disk.
type KeyPair struct {
	Header           string
	Version          string
	Index            int
	SecretKey        crypto.SecretKey
	UnlockConditions types.UnlockConditions
}

// generateKeys will generate a set of keys and save the keyfiles to disk.
func generateKeys(*cobra.Command, []string) {
	// Check that the key requirements make sense.
	if config.Siakg.RequiredKeys == 0 {
		fmt.Println("An address with 0 required keys is not useful.")
		return
	}
	if config.Siakg.TotalKeys < config.Siakg.RequiredKeys {
		fmt.Printf("Total Keys (%v) must be greater than or equal to Required Keys (%v)\n", config.Siakg.TotalKeys, config.Siakg.RequiredKeys)
		return
	}

	fmt.Printf("Creating key '%s' with %v total keys and %v required keys.\n", config.Siakg.KeyName, config.Siakg.TotalKeys, config.Siakg.RequiredKeys)

	// Generate 'TotalKeys' keyparis and fill out the metadata.
	keys := make([]KeyPair, config.Siakg.TotalKeys)
	for i := range keys {
		keys[i].Header = FileHeader
		keys[i].Version = Version
		keys[i].Index = i
	}

	// Add the keys to each keypair.
	pubKeys := make([]crypto.PublicKey, config.Siakg.TotalKeys)
	for i := 0; i < config.Siakg.TotalKeys; i++ {
		var err error
		keys[i].SecretKey, pubKeys[i], err = crypto.GenerateSignatureKeys()
		if err != nil {
			fmt.Println(err)
			return
		}
	}

	// Generate the unlock conditions and add them to each KeyPair object.
	uc := types.UnlockConditions{
		Timelock:      0,
		NumSignatures: uint64(config.Siakg.RequiredKeys),
	}
	for i := range keys {
		uc.PublicKeys = append(uc.PublicKeys, types.SiaPublicKey{
			Algorithm: types.SignatureEd25519,
			Key:       string(pubKeys[i][:]),
		})
	}
	for i := range keys {
		keys[i].UnlockConditions = uc
	}

	// Save the KeyPairs to disk.
	if config.Siakg.Folder != "" {
		err := os.MkdirAll(config.Siakg.Folder, 0700)
		if err != nil {
			fmt.Println(err)
			return
		}
	}
	for i, key := range keys {
		err := encoding.WriteFile(filepath.Join(config.Siakg.Folder, config.Siakg.KeyName)+"_Key"+strconv.Itoa(i)+FileExtension, key)
		if err != nil {
			fmt.Println(err)
			return
		}
	}

	// Load the keys from disk back into memory, then verifiy that the keys on
	// disk are able to sign outputs in transactions.
	loadedKeys := make([]KeyPair, config.Siakg.TotalKeys)
	for i := 0; i < len(loadedKeys); i++ {
		err := encoding.ReadFile(filepath.Join(config.Siakg.Folder, config.Siakg.KeyName)+"_Key"+strconv.Itoa(i)+FileExtension, &loadedKeys[i])
		if err != nil {
			fmt.Println(err)
			return
		}
	}

	// Check that the keys can be used to spend transactions. Load them back into memory.
	for _, loadedKey := range loadedKeys {
		if loadedKey.UnlockConditions.UnlockHash() != uc.UnlockHash() {
			fmt.Println("corruption occured while saving the keys to disk")
		}
	}
	// Create a transaction for the keys to sign.
	txn := types.Transaction{
		SiafundInputs: []types.SiafundInput{
			types.SiafundInput{
				UnlockConditions: loadedKeys[0].UnlockConditions,
			},
		},
	}
	// Loop through and sign the transaction multiple times. All keys will be
	// used at least once by the time the loop terminates.
	var i int
	for i != config.Siakg.TotalKeys {
		// i tracks which key is next to be used. If i + RequiredKeys results
		// in going out-of-bounds, reduce i so that the last key will be used
		// for the final signature.
		if i+config.Siakg.RequiredKeys > config.Siakg.TotalKeys {
			i = config.Siakg.TotalKeys - config.Siakg.RequiredKeys
		}
		var j int
		for j < config.Siakg.RequiredKeys {
			txn.Signatures = append(txn.Signatures, types.TransactionSignature{
				PublicKeyIndex: uint64(i),
				CoveredFields:  types.CoveredFields{WholeTransaction: true},
			})
			sigHash := txn.SigHash(j)
			sig, err := crypto.SignHash(sigHash, loadedKeys[i].SecretKey)
			if err != nil {
				fmt.Println(err)
				return
			}
			txn.Signatures[j].Signature = types.Signature(sig[:])
			i++
			j++
		}
		// Check that the signature is valid.
		err := txn.StandaloneValid(0)
		if err != nil {
			fmt.Println(err)
		}
		// Delete all of the signatures for the next iteration.
		txn.Signatures = nil
	}

	fmt.Printf("Success, the address for this set of keys is: %x\n", uc.UnlockHash())
}

// printKey opens a keyfile and prints the contents.
func printKey(*cobra.Command, []string) {
	var kp KeyPair
	err := encoding.ReadFile(config.Address.Filename, &kp)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Printf("Found a key for a %v of %v address.\n", kp.UnlockConditions.NumSignatures, len(kp.UnlockConditions.PublicKeys))
	fmt.Printf("The address is: %x\n", kp.UnlockConditions.UnlockHash())
}
<|endoftext|>


<commit_before>/**
 * (C) Copyright 2014, Deft Labs
 */

package deftlabsds

import (
	"labix.org/v2/mgo"
	"deftlabs.com/log"
)

type DataSource struct {
	DbName string
	CollectionName string
	Mongo *Mongo
	slogger.Logger
}

// Returns the collection from the session.
func (self *DataSource) Collection() *mgo.Collection { return self.Mongo.Collection(self.DbName, self.CollectionName) }

// Returns the database from the session.
func (self *DataSource) Db() *mgo.Database { return self.Mongo.Db(self.DbName) }

// Returns the session struct.
func (self *DataSource) Session() *mgo.Session { return self.Mongo.session }

// Returns a clone of the session struct.
func (self *DataSource) SessionClone() *mgo.Session { return self.Mongo.session.Clone() }

// Returns a copy of the session struct.
func (self *DataSource) SessionCopy() *mgo.Session { return self.Mongo.session.Clone() }

<commit_msg>added conv methods.<commit_after>/**
 * (C) Copyright 2014, Deft Labs
 */

package deftlabsds

import (
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"deftlabs.com/log"
)

type DataSource struct {
	DbName string
	CollectionName string
	Mongo *Mongo
	slogger.Logger
}

// Insert a document into a collection with the base configured write concern.
func (self *DataSource) Insert(doc interface{}) error { return self.Mongo.Collection(self.DbName, self.CollectionName).Insert(doc) }

// Insert a document into a collection with the passed write concern.
func (self *DataSource) InsertSafe(doc interface{}, safeMode *mgo.Safe) error {
	session := self.SessionClone()

	defer session.Close()

	session.SetSafe(safeMode)
	return session.DB(self.DbName).C(self.CollectionName).Insert(doc)
}

// Finds one document or returns nil. If the document is not found an error of type mgo.ErrNotFound is
// returned. The result must be a pointer.
func (self *DataSource) FindOne(query *bson.M, result interface{}) error {
	return self.Collection().Find(query).One(result)
}

// Delete one or more documents from the collection. If the document(s) is/are not found, no error
// is returned.
func (self *DataSource) Delete(selector interface{}) error {
	_, err := self.Collection().RemoveAll(selector)
	return err
}

// Returns the collection from the session.
func (self *DataSource) Collection() *mgo.Collection { return self.Mongo.Collection(self.DbName, self.CollectionName) }

// Returns the database from the session.
func (self *DataSource) Db() *mgo.Database { return self.Mongo.Db(self.DbName) }

// Returns the session struct.
func (self *DataSource) Session() *mgo.Session { return self.Mongo.session }

// Returns a clone of the session struct.
func (self *DataSource) SessionClone() *mgo.Session { return self.Mongo.session.Clone() }

// Returns a copy of the session struct.
func (self *DataSource) SessionCopy() *mgo.Session { return self.Mongo.session.Clone() }

<|endoftext|>


<commit_before>package systemd

import (
	"errors"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"sync"

	systemdDbus "github.com/coreos/go-systemd/v22/dbus"
	"github.com/godbus/dbus/v5"
	"github.com/sirupsen/logrus"

	"github.com/opencontainers/runc/libcontainer/cgroups"
	"github.com/opencontainers/runc/libcontainer/cgroups/fs"
	"github.com/opencontainers/runc/libcontainer/configs"
)

type legacyManager struct {
	mu      sync.Mutex
	cgroups *configs.Cgroup
	paths   map[string]string
	dbus    *dbusConnManager
}

func NewLegacyManager(cg *configs.Cgroup, paths map[string]string) cgroups.Manager {
	return &legacyManager{
		cgroups: cg,
		paths:   paths,
		dbus:    newDbusConnManager(false),
	}
}

type subsystem interface {
	// Name returns the name of the subsystem.
	Name() string
	// Returns the stats, as 'stats', corresponding to the cgroup under 'path'.
	GetStats(path string, stats *cgroups.Stats) error
	// Set sets cgroup resource limits.
	Set(path string, r *configs.Resources) error
}

var errSubsystemDoesNotExist = errors.New("cgroup: subsystem does not exist")

var legacySubsystems = []subsystem{
	&fs.CpusetGroup{},
	&fs.DevicesGroup{},
	&fs.MemoryGroup{},
	&fs.CpuGroup{},
	&fs.CpuacctGroup{},
	&fs.PidsGroup{},
	&fs.BlkioGroup{},
	&fs.HugetlbGroup{},
	&fs.PerfEventGroup{},
	&fs.FreezerGroup{},
	&fs.NetPrioGroup{},
	&fs.NetClsGroup{},
	&fs.NameGroup{GroupName: "name=systemd"},
	&fs.RdmaGroup{},
}

func genV1ResourcesProperties(r *configs.Resources, cm *dbusConnManager) ([]systemdDbus.Property, error) {
	var properties []systemdDbus.Property

	deviceProperties, err := generateDeviceProperties(r)
	if err != nil {
		return nil, err
	}
	properties = append(properties, deviceProperties...)

	if r.Memory != 0 {
		properties = append(properties,
			newProp("MemoryLimit", uint64(r.Memory)))
	}

	if r.CpuShares != 0 {
		properties = append(properties,
			newProp("CPUShares", r.CpuShares))
	}

	addCpuQuota(cm, &properties, r.CpuQuota, r.CpuPeriod)

	if r.BlkioWeight != 0 {
		properties = append(properties,
			newProp("BlockIOWeight", uint64(r.BlkioWeight)))
	}

	if r.PidsLimit > 0 || r.PidsLimit == -1 {
		properties = append(properties,
			newProp("TasksMax", uint64(r.PidsLimit)))
	}

	err = addCpuset(cm, &properties, r.CpusetCpus, r.CpusetMems)
	if err != nil {
		return nil, err
	}

	return properties, nil
}

// initPaths initializes m.paths. Supposed to be called under m.mu held.
func (m *legacyManager) initPaths() error {
	if m.paths != nil {
		return nil
	}

	paths := make(map[string]string)
	for _, s := range legacySubsystems {
		subsystemPath, err := getSubsystemPath(m.cgroups, s.Name())
		if err != nil {
			// Even if it's `not found` error, we'll return err
			// because devices cgroup is hard requirement for
			// container security.
			if s.Name() == "devices" {
				return err
			}
			// Don't fail if a cgroup hierarchy was not found, just skip this subsystem
			if cgroups.IsNotFound(err) {
				continue
			}
			return err
		}
		paths[s.Name()] = subsystemPath
	}
	m.paths = paths
	return nil
}

func (m *legacyManager) Apply(pid int) error {
	var (
		c          = m.cgroups
		unitName   = getUnitName(c)
		slice      = "system.slice"
		properties []systemdDbus.Property
	)

	if c.Resources.Unified != nil {
		return cgroups.ErrV1NoUnified
	}

	m.mu.Lock()
	defer m.mu.Unlock()

	if c.Parent != "" {
		slice = c.Parent
	}

	properties = append(properties, systemdDbus.PropDescription("libcontainer container "+c.Name))

	if strings.HasSuffix(unitName, ".slice") {
		// If we create a slice, the parent is defined via a Wants=.
		properties = append(properties, systemdDbus.PropWants(slice))
	} else {
		// Otherwise it's a scope, which we put into a Slice=.
		properties = append(properties, systemdDbus.PropSlice(slice))
		// Assume scopes always support delegation (supported since systemd v218).
		properties = append(properties, newProp("Delegate", true))
	}

	// only add pid if its valid, -1 is used w/ general slice creation.
	if pid != -1 {
		properties = append(properties, newProp("PIDs", []uint32{uint32(pid)}))
	}

	// Always enable accounting, this gets us the same behaviour as the fs implementation,
	// plus the kernel has some problems with joining the memory cgroup at a later time.
	properties = append(properties,
		newProp("MemoryAccounting", true),
		newProp("CPUAccounting", true),
		newProp("BlockIOAccounting", true),
		newProp("TasksAccounting", true),
	)

	// Assume DefaultDependencies= will always work (the check for it was previously broken.)
	properties = append(properties,
		newProp("DefaultDependencies", false))

	properties = append(properties, c.SystemdProps...)

	if err := startUnit(m.dbus, unitName, properties); err != nil {
		return err
	}

	if err := m.initPaths(); err != nil {
		return err
	}
	if err := m.joinCgroups(pid); err != nil {
		return err
	}

	return nil
}

func (m *legacyManager) Destroy() error {
	m.mu.Lock()
	defer m.mu.Unlock()

	stopErr := stopUnit(m.dbus, getUnitName(m.cgroups))

	// Both on success and on error, cleanup all the cgroups
	// we are aware of, as some of them were created directly
	// by Apply() and are not managed by systemd.
	if err := cgroups.RemovePaths(m.paths); err != nil && stopErr == nil {
		return err
	}

	return stopErr
}

func (m *legacyManager) Path(subsys string) string {
	m.mu.Lock()
	defer m.mu.Unlock()
	return m.paths[subsys]
}

func (m *legacyManager) joinCgroups(pid int) error {
	for _, sys := range legacySubsystems {
		name := sys.Name()
		switch name {
		case "name=systemd":
			// let systemd handle this
		case "cpuset":
			if path, ok := m.paths[name]; ok {
				s := &fs.CpusetGroup{}
				if err := s.ApplyDir(path, m.cgroups.Resources, pid); err != nil {
					return err
				}
			}
		default:
			if path, ok := m.paths[name]; ok {
				if err := os.MkdirAll(path, 0o755); err != nil {
					return err
				}
				if err := cgroups.WriteCgroupProc(path, pid); err != nil {
					return err
				}
			}
		}
	}

	return nil
}

func getSubsystemPath(c *configs.Cgroup, subsystem string) (string, error) {
	mountpoint, err := cgroups.FindCgroupMountpoint("", subsystem)
	if err != nil {
		return "", err
	}

	initPath, err := cgroups.GetInitCgroup(subsystem)
	if err != nil {
		return "", err
	}
	// if pid 1 is systemd 226 or later, it will be in init.scope, not the root
	initPath = strings.TrimSuffix(filepath.Clean(initPath), "init.scope")

	slice := "system.slice"
	if c.Parent != "" {
		slice = c.Parent
	}

	slice, err = ExpandSlice(slice)
	if err != nil {
		return "", err
	}

	return filepath.Join(mountpoint, initPath, slice, getUnitName(c)), nil
}

func (m *legacyManager) Freeze(state configs.FreezerState) error {
	err := m.doFreeze(state)
	if err == nil {
		m.cgroups.Resources.Freezer = state
	}
	return err
}

// doFreeze is the same as Freeze but without
// changing the m.cgroups.Resources.Frozen field.
func (m *legacyManager) doFreeze(state configs.FreezerState) error {
	path, ok := m.paths["freezer"]
	if !ok {
		return errSubsystemDoesNotExist
	}
	freezer := &fs.FreezerGroup{}
	resources := &configs.Resources{Freezer: state}
	return freezer.Set(path, resources)
}

func (m *legacyManager) GetPids() ([]int, error) {
	path, ok := m.paths["devices"]
	if !ok {
		return nil, errSubsystemDoesNotExist
	}
	return cgroups.GetPids(path)
}

func (m *legacyManager) GetAllPids() ([]int, error) {
	path, ok := m.paths["devices"]
	if !ok {
		return nil, errSubsystemDoesNotExist
	}
	return cgroups.GetAllPids(path)
}

func (m *legacyManager) GetStats() (*cgroups.Stats, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	stats := cgroups.NewStats()
	for _, sys := range legacySubsystems {
		path := m.paths[sys.Name()]
		if path == "" {
			continue
		}
		if err := sys.GetStats(path, stats); err != nil {
			return nil, err
		}
	}

	return stats, nil
}

// freezeBeforeSet answers whether there is a need to freeze the cgroup before
// applying its systemd unit properties, and thaw after, while avoiding
// unnecessary freezer state changes.
//
// The reason why we have to freeze is that systemd's application of device
// rules is done disruptively, resulting in spurious errors to common devices
// (unlike our fs driver, they will happily write deny-all rules to running
// containers). So we have to freeze the container to avoid the container get
// an occasional "permission denied" error.
func (m *legacyManager) freezeBeforeSet(unitName string, r *configs.Resources) (needsFreeze, needsThaw bool, err error) {
	// Special case for SkipDevices, as used by Kubernetes to create pod
	// cgroups with allow-all device policy).
	if r.SkipDevices {
		if r.SkipFreezeOnSet {
			// Both needsFreeze and needsThaw are false.
			return
		}

		// No need to freeze if SkipDevices is set, and either
		// (1) systemd unit does not (yet) exist, or
		// (2) it has DevicePolicy=auto and empty DeviceAllow list.
		//
		// Interestingly, (1) and (2) are the same here because
		// a non-existent unit returns default properties,
		// and settings in (2) are the defaults.
		//
		// Do not return errors from getUnitTypeProperty, as they alone
		// should not prevent Set from working.

		unitType := getUnitType(unitName)

		devPolicy, e := getUnitTypeProperty(m.dbus, unitName, unitType, "DevicePolicy")
		if e == nil && devPolicy.Value == dbus.MakeVariant("auto") {
			devAllow, e := getUnitTypeProperty(m.dbus, unitName, unitType, "DeviceAllow")
			if e == nil {
				if rv := reflect.ValueOf(devAllow.Value.Value()); rv.Kind() == reflect.Slice && rv.Len() == 0 {
					needsFreeze = false
					needsThaw = false
					return
				}
			}
		}
	}

	needsFreeze = true
	needsThaw = true

	// Check the current freezer state.
	freezerState, err := m.GetFreezerState()
	if err != nil {
		return
	}
	if freezerState == configs.Frozen {
		// Already frozen, and should stay frozen.
		needsFreeze = false
		needsThaw = false
	}

	if r.Freezer == configs.Frozen {
		// Will be frozen anyway -- no need to thaw.
		needsThaw = false
	}
	return
}

func (m *legacyManager) Set(r *configs.Resources) error {
	if r.Unified != nil {
		return cgroups.ErrV1NoUnified
	}
	properties, err := genV1ResourcesProperties(r, m.dbus)
	if err != nil {
		return err
	}

	unitName := getUnitName(m.cgroups)
	needsFreeze, needsThaw, err := m.freezeBeforeSet(unitName, r)
	if err != nil {
		return err
	}

	if needsFreeze {
		if err := m.doFreeze(configs.Frozen); err != nil {
			// If freezer cgroup isn't supported, we just warn about it.
			logrus.Infof("freeze container before SetUnitProperties failed: %v", err)
		}
	}
	setErr := setUnitProperties(m.dbus, unitName, properties...)
	if needsThaw {
		if err := m.doFreeze(configs.Thawed); err != nil {
			logrus.Infof("thaw container after SetUnitProperties failed: %v", err)
		}
	}
	if setErr != nil {
		return setErr
	}

	for _, sys := range legacySubsystems {
		// Get the subsystem path, but don't error out for not found cgroups.
		path, ok := m.paths[sys.Name()]
		if !ok {
			continue
		}
		if err := sys.Set(path, r); err != nil {
			return err
		}
	}

	return nil
}

func (m *legacyManager) GetPaths() map[string]string {
	m.mu.Lock()
	defer m.mu.Unlock()
	return m.paths
}

func (m *legacyManager) GetCgroups() (*configs.Cgroup, error) {
	return m.cgroups, nil
}

func (m *legacyManager) GetFreezerState() (configs.FreezerState, error) {
	path, ok := m.paths["freezer"]
	if !ok {
		return configs.Undefined, nil
	}
	freezer := &fs.FreezerGroup{}
	return freezer.GetState(path)
}

func (m *legacyManager) Exists() bool {
	return cgroups.PathExists(m.Path("devices"))
}

func (m *legacyManager) OOMKillCount() (uint64, error) {
	return fs.OOMKillCount(m.Path("memory"))
}
<commit_msg>libct/cg/sd/v1: optimize initPaths<commit_after>package systemd

import (
	"errors"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"sync"

	systemdDbus "github.com/coreos/go-systemd/v22/dbus"
	"github.com/godbus/dbus/v5"
	"github.com/sirupsen/logrus"

	"github.com/opencontainers/runc/libcontainer/cgroups"
	"github.com/opencontainers/runc/libcontainer/cgroups/fs"
	"github.com/opencontainers/runc/libcontainer/configs"
)

type legacyManager struct {
	mu      sync.Mutex
	cgroups *configs.Cgroup
	paths   map[string]string
	dbus    *dbusConnManager
}

func NewLegacyManager(cg *configs.Cgroup, paths map[string]string) cgroups.Manager {
	return &legacyManager{
		cgroups: cg,
		paths:   paths,
		dbus:    newDbusConnManager(false),
	}
}

type subsystem interface {
	// Name returns the name of the subsystem.
	Name() string
	// Returns the stats, as 'stats', corresponding to the cgroup under 'path'.
	GetStats(path string, stats *cgroups.Stats) error
	// Set sets cgroup resource limits.
	Set(path string, r *configs.Resources) error
}

var errSubsystemDoesNotExist = errors.New("cgroup: subsystem does not exist")

var legacySubsystems = []subsystem{
	&fs.CpusetGroup{},
	&fs.DevicesGroup{},
	&fs.MemoryGroup{},
	&fs.CpuGroup{},
	&fs.CpuacctGroup{},
	&fs.PidsGroup{},
	&fs.BlkioGroup{},
	&fs.HugetlbGroup{},
	&fs.PerfEventGroup{},
	&fs.FreezerGroup{},
	&fs.NetPrioGroup{},
	&fs.NetClsGroup{},
	&fs.NameGroup{GroupName: "name=systemd"},
	&fs.RdmaGroup{},
}

func genV1ResourcesProperties(r *configs.Resources, cm *dbusConnManager) ([]systemdDbus.Property, error) {
	var properties []systemdDbus.Property

	deviceProperties, err := generateDeviceProperties(r)
	if err != nil {
		return nil, err
	}
	properties = append(properties, deviceProperties...)

	if r.Memory != 0 {
		properties = append(properties,
			newProp("MemoryLimit", uint64(r.Memory)))
	}

	if r.CpuShares != 0 {
		properties = append(properties,
			newProp("CPUShares", r.CpuShares))
	}

	addCpuQuota(cm, &properties, r.CpuQuota, r.CpuPeriod)

	if r.BlkioWeight != 0 {
		properties = append(properties,
			newProp("BlockIOWeight", uint64(r.BlkioWeight)))
	}

	if r.PidsLimit > 0 || r.PidsLimit == -1 {
		properties = append(properties,
			newProp("TasksMax", uint64(r.PidsLimit)))
	}

	err = addCpuset(cm, &properties, r.CpusetCpus, r.CpusetMems)
	if err != nil {
		return nil, err
	}

	return properties, nil
}

// initPaths initializes m.paths. Supposed to be called under m.mu held.
func (m *legacyManager) initPaths() error {
	if m.paths != nil {
		return nil
	}

	c := m.cgroups

	slice := "system.slice"
	if c.Parent != "" {
		slice = c.Parent
	}

	var err error
	slice, err = ExpandSlice(slice)
	if err != nil {
		return err
	}
	unit := getUnitName(c)

	paths := make(map[string]string)
	for _, s := range legacySubsystems {
		subsystemPath, err := getSubsystemPath(slice, unit, s.Name())
		if err != nil {
			// Even if it's `not found` error, we'll return err
			// because devices cgroup is hard requirement for
			// container security.
			if s.Name() == "devices" {
				return err
			}
			// Don't fail if a cgroup hierarchy was not found, just skip this subsystem
			if cgroups.IsNotFound(err) {
				continue
			}
			return err
		}
		paths[s.Name()] = subsystemPath
	}
	m.paths = paths
	return nil
}

func (m *legacyManager) Apply(pid int) error {
	var (
		c          = m.cgroups
		unitName   = getUnitName(c)
		slice      = "system.slice"
		properties []systemdDbus.Property
	)

	if c.Resources.Unified != nil {
		return cgroups.ErrV1NoUnified
	}

	m.mu.Lock()
	defer m.mu.Unlock()

	if c.Parent != "" {
		slice = c.Parent
	}

	properties = append(properties, systemdDbus.PropDescription("libcontainer container "+c.Name))

	if strings.HasSuffix(unitName, ".slice") {
		// If we create a slice, the parent is defined via a Wants=.
		properties = append(properties, systemdDbus.PropWants(slice))
	} else {
		// Otherwise it's a scope, which we put into a Slice=.
		properties = append(properties, systemdDbus.PropSlice(slice))
		// Assume scopes always support delegation (supported since systemd v218).
		properties = append(properties, newProp("Delegate", true))
	}

	// only add pid if its valid, -1 is used w/ general slice creation.
	if pid != -1 {
		properties = append(properties, newProp("PIDs", []uint32{uint32(pid)}))
	}

	// Always enable accounting, this gets us the same behaviour as the fs implementation,
	// plus the kernel has some problems with joining the memory cgroup at a later time.
	properties = append(properties,
		newProp("MemoryAccounting", true),
		newProp("CPUAccounting", true),
		newProp("BlockIOAccounting", true),
		newProp("TasksAccounting", true),
	)

	// Assume DefaultDependencies= will always work (the check for it was previously broken.)
	properties = append(properties,
		newProp("DefaultDependencies", false))

	properties = append(properties, c.SystemdProps...)

	if err := startUnit(m.dbus, unitName, properties); err != nil {
		return err
	}

	if err := m.initPaths(); err != nil {
		return err
	}
	if err := m.joinCgroups(pid); err != nil {
		return err
	}

	return nil
}

func (m *legacyManager) Destroy() error {
	m.mu.Lock()
	defer m.mu.Unlock()

	stopErr := stopUnit(m.dbus, getUnitName(m.cgroups))

	// Both on success and on error, cleanup all the cgroups
	// we are aware of, as some of them were created directly
	// by Apply() and are not managed by systemd.
	if err := cgroups.RemovePaths(m.paths); err != nil && stopErr == nil {
		return err
	}

	return stopErr
}

func (m *legacyManager) Path(subsys string) string {
	m.mu.Lock()
	defer m.mu.Unlock()
	return m.paths[subsys]
}

func (m *legacyManager) joinCgroups(pid int) error {
	for _, sys := range legacySubsystems {
		name := sys.Name()
		switch name {
		case "name=systemd":
			// let systemd handle this
		case "cpuset":
			if path, ok := m.paths[name]; ok {
				s := &fs.CpusetGroup{}
				if err := s.ApplyDir(path, m.cgroups.Resources, pid); err != nil {
					return err
				}
			}
		default:
			if path, ok := m.paths[name]; ok {
				if err := os.MkdirAll(path, 0o755); err != nil {
					return err
				}
				if err := cgroups.WriteCgroupProc(path, pid); err != nil {
					return err
				}
			}
		}
	}

	return nil
}

func getSubsystemPath(slice, unit, subsystem string) (string, error) {
	mountpoint, err := cgroups.FindCgroupMountpoint("", subsystem)
	if err != nil {
		return "", err
	}

	initPath, err := cgroups.GetInitCgroup(subsystem)
	if err != nil {
		return "", err
	}
	// if pid 1 is systemd 226 or later, it will be in init.scope, not the root
	initPath = strings.TrimSuffix(filepath.Clean(initPath), "init.scope")

	return filepath.Join(mountpoint, initPath, slice, unit), nil
}

func (m *legacyManager) Freeze(state configs.FreezerState) error {
	err := m.doFreeze(state)
	if err == nil {
		m.cgroups.Resources.Freezer = state
	}
	return err
}

// doFreeze is the same as Freeze but without
// changing the m.cgroups.Resources.Frozen field.
func (m *legacyManager) doFreeze(state configs.FreezerState) error {
	path, ok := m.paths["freezer"]
	if !ok {
		return errSubsystemDoesNotExist
	}
	freezer := &fs.FreezerGroup{}
	resources := &configs.Resources{Freezer: state}
	return freezer.Set(path, resources)
}

func (m *legacyManager) GetPids() ([]int, error) {
	path, ok := m.paths["devices"]
	if !ok {
		return nil, errSubsystemDoesNotExist
	}
	return cgroups.GetPids(path)
}

func (m *legacyManager) GetAllPids() ([]int, error) {
	path, ok := m.paths["devices"]
	if !ok {
		return nil, errSubsystemDoesNotExist
	}
	return cgroups.GetAllPids(path)
}

func (m *legacyManager) GetStats() (*cgroups.Stats, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	stats := cgroups.NewStats()
	for _, sys := range legacySubsystems {
		path := m.paths[sys.Name()]
		if path == "" {
			continue
		}
		if err := sys.GetStats(path, stats); err != nil {
			return nil, err
		}
	}

	return stats, nil
}

// freezeBeforeSet answers whether there is a need to freeze the cgroup before
// applying its systemd unit properties, and thaw after, while avoiding
// unnecessary freezer state changes.
//
// The reason why we have to freeze is that systemd's application of device
// rules is done disruptively, resulting in spurious errors to common devices
// (unlike our fs driver, they will happily write deny-all rules to running
// containers). So we have to freeze the container to avoid the container get
// an occasional "permission denied" error.
func (m *legacyManager) freezeBeforeSet(unitName string, r *configs.Resources) (needsFreeze, needsThaw bool, err error) {
	// Special case for SkipDevices, as used by Kubernetes to create pod
	// cgroups with allow-all device policy).
	if r.SkipDevices {
		if r.SkipFreezeOnSet {
			// Both needsFreeze and needsThaw are false.
			return
		}

		// No need to freeze if SkipDevices is set, and either
		// (1) systemd unit does not (yet) exist, or
		// (2) it has DevicePolicy=auto and empty DeviceAllow list.
		//
		// Interestingly, (1) and (2) are the same here because
		// a non-existent unit returns default properties,
		// and settings in (2) are the defaults.
		//
		// Do not return errors from getUnitTypeProperty, as they alone
		// should not prevent Set from working.

		unitType := getUnitType(unitName)

		devPolicy, e := getUnitTypeProperty(m.dbus, unitName, unitType, "DevicePolicy")
		if e == nil && devPolicy.Value == dbus.MakeVariant("auto") {
			devAllow, e := getUnitTypeProperty(m.dbus, unitName, unitType, "DeviceAllow")
			if e == nil {
				if rv := reflect.ValueOf(devAllow.Value.Value()); rv.Kind() == reflect.Slice && rv.Len() == 0 {
					needsFreeze = false
					needsThaw = false
					return
				}
			}
		}
	}

	needsFreeze = true
	needsThaw = true

	// Check the current freezer state.
	freezerState, err := m.GetFreezerState()
	if err != nil {
		return
	}
	if freezerState == configs.Frozen {
		// Already frozen, and should stay frozen.
		needsFreeze = false
		needsThaw = false
	}

	if r.Freezer == configs.Frozen {
		// Will be frozen anyway -- no need to thaw.
		needsThaw = false
	}
	return
}

func (m *legacyManager) Set(r *configs.Resources) error {
	if r.Unified != nil {
		return cgroups.ErrV1NoUnified
	}
	properties, err := genV1ResourcesProperties(r, m.dbus)
	if err != nil {
		return err
	}

	unitName := getUnitName(m.cgroups)
	needsFreeze, needsThaw, err := m.freezeBeforeSet(unitName, r)
	if err != nil {
		return err
	}

	if needsFreeze {
		if err := m.doFreeze(configs.Frozen); err != nil {
			// If freezer cgroup isn't supported, we just warn about it.
			logrus.Infof("freeze container before SetUnitProperties failed: %v", err)
		}
	}
	setErr := setUnitProperties(m.dbus, unitName, properties...)
	if needsThaw {
		if err := m.doFreeze(configs.Thawed); err != nil {
			logrus.Infof("thaw container after SetUnitProperties failed: %v", err)
		}
	}
	if setErr != nil {
		return setErr
	}

	for _, sys := range legacySubsystems {
		// Get the subsystem path, but don't error out for not found cgroups.
		path, ok := m.paths[sys.Name()]
		if !ok {
			continue
		}
		if err := sys.Set(path, r); err != nil {
			return err
		}
	}

	return nil
}

func (m *legacyManager) GetPaths() map[string]string {
	m.mu.Lock()
	defer m.mu.Unlock()
	return m.paths
}

func (m *legacyManager) GetCgroups() (*configs.Cgroup, error) {
	return m.cgroups, nil
}

func (m *legacyManager) GetFreezerState() (configs.FreezerState, error) {
	path, ok := m.paths["freezer"]
	if !ok {
		return configs.Undefined, nil
	}
	freezer := &fs.FreezerGroup{}
	return freezer.GetState(path)
}

func (m *legacyManager) Exists() bool {
	return cgroups.PathExists(m.Path("devices"))
}

func (m *legacyManager) OOMKillCount() (uint64, error) {
	return fs.OOMKillCount(m.Path("memory"))
}
<|endoftext|>


<commit_before>package ps

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/dokku/dokku/plugins/common"
	dockeroptions "github.com/dokku/dokku/plugins/docker-options"
)

func canScaleApp(appName string) bool {
	return !common.FileExists(getScalefilePath(appName))
}

func extractProcfile(appName, image string, procfilePath string) error {
	if err := removeProcfile(appName); err != nil {
		return err
	}

	destination := getProcfilePath(appName)
	common.CopyFromImage(appName, image, "Procfile", destination)
	if !common.FileExists(destination) {
		common.LogInfo1Quiet("No Procfile found in app image")
		return nil
	}

	common.LogInfo1Quiet("App Procfile file found")
	checkCmd := common.NewShellCmd(strings.Join([]string{
		"procfile-util",
		"check",
		"--procfile",
		destination,
	}, " "))
	var stderr bytes.Buffer
	checkCmd.ShowOutput = false
	checkCmd.Command.Stderr = &stderr
	_, err := checkCmd.Output()

	if err != nil {
		return fmt.Errorf(strings.TrimSpace(stderr.String()))
	}

	return nil
}

func extractOrGenerateScalefile(appName string, image string) error {
	destination := getScalefilePath(appName)
	extracted := getScalefileExtractedPath(appName)
	if err := common.CopyFromImage(appName, image, "DOKKU_SCALE", destination); err != nil {
		os.Remove(extracted)
		os.Remove(destination)
	} else if err := common.CopyFile(destination, extracted); err != nil {
		return err
	}

	if common.FileExists(destination) {
		return nil
	}

	common.LogInfo1Quiet("DOKKU_SCALE file not found in app image. Generating one based on Procfile...")
	if err := generateScalefile(appName, destination); err != nil {
		common.LogDebug(fmt.Sprintf("Error: %s", err.Error()))
		return err
	}
	return nil
}

func generateScalefile(appName string, destination string) error {
	procfilePath := getProcfilePath(appName)
	content := []string{"web=1"}
	if !common.FileExists(procfilePath) {
		return common.WriteSliceToFile(destination, content)
	}

	lines, err := common.FileToSlice(procfilePath)
	if err != nil {
		return common.WriteSliceToFile(destination, content)
	}

	content = []string{}
	for _, line := range lines {
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}

		parts := strings.Split(line, ":")
		count := 0
		if parts[0] == "web" {
			count = 1
		}

		content = append(content, fmt.Sprintf("%s=%d", parts[0], count))
	}

	return common.WriteSliceToFile(destination, content)
}

func getProcfileCommand(procfilePath string, processType string, port int) (string, error) {
	if !common.FileExists(procfilePath) {
		return "", errors.New("No procfile found")
	}

	shellCmd := common.NewShellCmd(strings.Join([]string{
		"procfile-util",
		"show",
		"--procfile",
		procfilePath,
		"--process-type",
		processType,
		"--default-port",
		strconv.Itoa(port),
	}, " "))
	var stderr bytes.Buffer
	shellCmd.ShowOutput = false
	shellCmd.Command.Stderr = &stderr
	b, err := shellCmd.Output()

	if err != nil {
		return "", fmt.Errorf(strings.TrimSpace(stderr.String()))
	}

	return strings.TrimSpace(string(b[:])), nil
}

func getProcfilePath(appName string) string {
	directory := filepath.Join(common.MustGetEnv("DOKKU_LIB_ROOT"), "data", "ps", appName)
	return filepath.Join(directory, "Procfile")
}

func getRestartPolicy(appName string) (string, error) {
	options, err := dockeroptions.GetDockerOptionsForPhase(appName, "deploy")
	if err != nil {
		return "", err
	}

	for _, option := range options {
		if strings.HasPrefix(option, "--restart=") {
			return strings.TrimPrefix(option, "--restart="), nil
		}
	}

	return "", nil
}

func getRunningState(appName string) string {
	scheduler := common.GetAppScheduler(appName)
	b, _ := common.PlugnTriggerOutput("scheduler-app-status", []string{scheduler, appName}...)
	return strings.Split(strings.TrimSpace(string(b[:])), " ")[1]
}

func getScalefilePath(appName string) string {
	return filepath.Join(common.AppRoot(appName), "DOKKU_SCALE")
}

func getScalefileExtractedPath(appName string) string {
	return filepath.Join(common.AppRoot(appName), "DOKKU_SCALE.extracted")
}

func isValidRestartPolicy(policy string) bool {
	if policy == "" {
		return false
	}

	validRestartPolicies := map[string]bool{
		"no":             true,
		"always":         true,
		"unless-stopped": true,
		"on-failure":     true,
	}

	if _, ok := validRestartPolicies[policy]; ok {
		return true
	}

	return strings.HasPrefix(policy, "on-failure:")
}

func processesInProcfile(procfilePath string) (map[string]bool, error) {
	processes := map[string]bool{}

	shellCmd := common.NewShellCmd(strings.Join([]string{
		"procfile-util",
		"list",
		"--procfile",
		procfilePath,
	}, " "))
	var stderr bytes.Buffer
	shellCmd.ShowOutput = false
	shellCmd.Command.Stderr = &stderr
	b, err := shellCmd.Output()

	if err != nil {
		return processes, fmt.Errorf(strings.TrimSpace(stderr.String()))
	}

	for _, s := range strings.Split(strings.TrimSpace(string(b[:])), "\n") {
		processes[s] = true
	}

	return processes, nil
}

func removeProcfile(appName string) error {
	procfile := getProcfilePath(appName)
	if !common.FileExists(procfile) {
		return nil
	}

	return os.Remove(procfile)
}

func updateScalefile(appName string, processTuples []string) error {
	procfilePath := getProcfilePath(appName)
	scalefilePath := getScalefilePath(appName)
	lines, err := common.FileToSlice(scalefilePath)
	if err != nil {
		return err
	}

	processTypes, err := processesInProcfile(procfilePath)
	if err != nil {
		return err
	}

	newLines := []string{}
	for processType := range processTypes {
		found := false
		for _, line := range lines {
			if strings.HasPrefix(line, fmt.Sprintf("%s=", processType)) {
				newLines = append(newLines, line)
				found = true
				break
			}
		}

		if !found {
			newLines = append(newLines, fmt.Sprintf("%s=0", processType))
		}
	}

	scale := []string{}
	for _, processTuple := range processTuples {
		s := strings.Split(processTuple, "=")
		if len(s) == 1 {
			return fmt.Errorf("Missing count for process type %s", processTuple)
		}

		processType := s[0]
		count, err := strconv.Atoi(s[1])
		if err != nil {
			return fmt.Errorf("Invalid count for process type %s", s[0])
		}

		if _, ok := processTypes[processType]; !ok {
		    if count != 0 && len(processTypes) == 0 {
		      	return fmt.Errorf("%s is not a valid process name to scale up", processType)
		    }
		}

		scale = append(scale, fmt.Sprintf("%s=%d", processType, count))
	}

	for _, line := range newLines {
		s := strings.Split(line, "=")
		processType := s[0]

		found := false
		for _, s := range scale {
			if strings.HasPrefix(s, fmt.Sprintf("%s=", processType)) {
				found = true
				break
			}
		}

		if !found {
			scale = append(scale, line)
		}

	}

	if err := common.WriteSliceToFile(scalefilePath, scale); err != nil {
		return err
	}

	return err
}
<commit_msg>fix: check if it can scale by checking for an extracted file<commit_after>package ps

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/dokku/dokku/plugins/common"
	dockeroptions "github.com/dokku/dokku/plugins/docker-options"
)

func canScaleApp(appName string) bool {
	return !common.FileExists(getScalefileExtractedPath(appName))
}

func extractProcfile(appName, image string, procfilePath string) error {
	if err := removeProcfile(appName); err != nil {
		return err
	}

	destination := getProcfilePath(appName)
	common.CopyFromImage(appName, image, "Procfile", destination)
	if !common.FileExists(destination) {
		common.LogInfo1Quiet("No Procfile found in app image")
		return nil
	}

	common.LogInfo1Quiet("App Procfile file found")
	checkCmd := common.NewShellCmd(strings.Join([]string{
		"procfile-util",
		"check",
		"--procfile",
		destination,
	}, " "))
	var stderr bytes.Buffer
	checkCmd.ShowOutput = false
	checkCmd.Command.Stderr = &stderr
	_, err := checkCmd.Output()

	if err != nil {
		return fmt.Errorf(strings.TrimSpace(stderr.String()))
	}

	return nil
}

func extractOrGenerateScalefile(appName string, image string) error {
	destination := getScalefilePath(appName)
	extracted := getScalefileExtractedPath(appName)
	if err := common.CopyFromImage(appName, image, "DOKKU_SCALE", destination); err != nil {
		os.Remove(extracted)
		os.Remove(destination)
	} else if err := common.CopyFile(destination, extracted); err != nil {
		return err
	}

	if common.FileExists(destination) {
		return nil
	}

	common.LogInfo1Quiet("DOKKU_SCALE file not found in app image. Generating one based on Procfile...")
	if err := generateScalefile(appName, destination); err != nil {
		common.LogDebug(fmt.Sprintf("Error: %s", err.Error()))
		return err
	}
	return nil
}

func generateScalefile(appName string, destination string) error {
	procfilePath := getProcfilePath(appName)
	content := []string{"web=1"}
	if !common.FileExists(procfilePath) {
		return common.WriteSliceToFile(destination, content)
	}

	lines, err := common.FileToSlice(procfilePath)
	if err != nil {
		return common.WriteSliceToFile(destination, content)
	}

	content = []string{}
	for _, line := range lines {
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}

		parts := strings.Split(line, ":")
		count := 0
		if parts[0] == "web" {
			count = 1
		}

		content = append(content, fmt.Sprintf("%s=%d", parts[0], count))
	}

	return common.WriteSliceToFile(destination, content)
}

func getProcfileCommand(procfilePath string, processType string, port int) (string, error) {
	if !common.FileExists(procfilePath) {
		return "", errors.New("No procfile found")
	}

	shellCmd := common.NewShellCmd(strings.Join([]string{
		"procfile-util",
		"show",
		"--procfile",
		procfilePath,
		"--process-type",
		processType,
		"--default-port",
		strconv.Itoa(port),
	}, " "))
	var stderr bytes.Buffer
	shellCmd.ShowOutput = false
	shellCmd.Command.Stderr = &stderr
	b, err := shellCmd.Output()

	if err != nil {
		return "", fmt.Errorf(strings.TrimSpace(stderr.String()))
	}

	return strings.TrimSpace(string(b[:])), nil
}

func getProcfilePath(appName string) string {
	directory := filepath.Join(common.MustGetEnv("DOKKU_LIB_ROOT"), "data", "ps", appName)
	return filepath.Join(directory, "Procfile")
}

func getRestartPolicy(appName string) (string, error) {
	options, err := dockeroptions.GetDockerOptionsForPhase(appName, "deploy")
	if err != nil {
		return "", err
	}

	for _, option := range options {
		if strings.HasPrefix(option, "--restart=") {
			return strings.TrimPrefix(option, "--restart="), nil
		}
	}

	return "", nil
}

func getRunningState(appName string) string {
	scheduler := common.GetAppScheduler(appName)
	b, _ := common.PlugnTriggerOutput("scheduler-app-status", []string{scheduler, appName}...)
	return strings.Split(strings.TrimSpace(string(b[:])), " ")[1]
}

func getScalefilePath(appName string) string {
	return filepath.Join(common.AppRoot(appName), "DOKKU_SCALE")
}

func getScalefileExtractedPath(appName string) string {
	return filepath.Join(common.AppRoot(appName), "DOKKU_SCALE.extracted")
}

func isValidRestartPolicy(policy string) bool {
	if policy == "" {
		return false
	}

	validRestartPolicies := map[string]bool{
		"no":             true,
		"always":         true,
		"unless-stopped": true,
		"on-failure":     true,
	}

	if _, ok := validRestartPolicies[policy]; ok {
		return true
	}

	return strings.HasPrefix(policy, "on-failure:")
}

func processesInProcfile(procfilePath string) (map[string]bool, error) {
	processes := map[string]bool{}

	shellCmd := common.NewShellCmd(strings.Join([]string{
		"procfile-util",
		"list",
		"--procfile",
		procfilePath,
	}, " "))
	var stderr bytes.Buffer
	shellCmd.ShowOutput = false
	shellCmd.Command.Stderr = &stderr
	b, err := shellCmd.Output()

	if err != nil {
		return processes, fmt.Errorf(strings.TrimSpace(stderr.String()))
	}

	for _, s := range strings.Split(strings.TrimSpace(string(b[:])), "\n") {
		processes[s] = true
	}

	return processes, nil
}

func removeProcfile(appName string) error {
	procfile := getProcfilePath(appName)
	if !common.FileExists(procfile) {
		return nil
	}

	return os.Remove(procfile)
}

func updateScalefile(appName string, processTuples []string) error {
	procfilePath := getProcfilePath(appName)
	scalefilePath := getScalefilePath(appName)
	lines, err := common.FileToSlice(scalefilePath)
	if err != nil {
		return err
	}

	processTypes, err := processesInProcfile(procfilePath)
	if err != nil {
		return err
	}

	newLines := []string{}
	for processType := range processTypes {
		found := false
		for _, line := range lines {
			if strings.HasPrefix(line, fmt.Sprintf("%s=", processType)) {
				newLines = append(newLines, line)
				found = true
				break
			}
		}

		if !found {
			newLines = append(newLines, fmt.Sprintf("%s=0", processType))
		}
	}

	scale := []string{}
	for _, processTuple := range processTuples {
		s := strings.Split(processTuple, "=")
		if len(s) == 1 {
			return fmt.Errorf("Missing count for process type %s", processTuple)
		}

		processType := s[0]
		count, err := strconv.Atoi(s[1])
		if err != nil {
			return fmt.Errorf("Invalid count for process type %s", s[0])
		}

		if _, ok := processTypes[processType]; !ok {
		    if count != 0 && len(processTypes) == 0 {
		      	return fmt.Errorf("%s is not a valid process name to scale up", processType)
		    }
		}

		scale = append(scale, fmt.Sprintf("%s=%d", processType, count))
	}

	for _, line := range newLines {
		s := strings.Split(line, "=")
		processType := s[0]

		found := false
		for _, s := range scale {
			if strings.HasPrefix(s, fmt.Sprintf("%s=", processType)) {
				found = true
				break
			}
		}

		if !found {
			scale = append(scale, line)
		}

	}

	if err := common.WriteSliceToFile(scalefilePath, scale); err != nil {
		return err
	}

	return err
}
<|endoftext|>


<commit_before>package main

import (
	crand "crypto/rand"
	"fmt"
	"io"
	"math/rand"
	"strings"
)

func NewRand() *rand.Rand {
	var b [8]byte
	_, err := io.ReadFull(crand.Reader, b[:])
	if err != nil {
		panic(err)
	}
	seed := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 |
		int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7])<<56
	return rand.New(rand.NewSource(seed))
}

func NewPassword(length int) string {
	const (
		upper    = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
		lower    = "abcdefghijklmnopqrstuvwxyz"
		digit    = "0123456789"
		alphabet = upper + lower + digit
	)
	pass := make([]byte, 0, length)
	r := NewRand()
	for {
		for i := 0; i < cap(pass); i++ {
			char := alphabet[r.Intn(len(alphabet))]
			pass = append(pass, char)
		}
		if strings.ContainsAny(string(pass), upper) &&
			strings.ContainsAny(string(pass), lower) &&
			strings.ContainsAny(string(pass), digit) {
			return string(pass)
		}
		pass = pass[:0]
	}
}

func main() {
	fmt.Println(NewPassword(8))
}
<commit_msg>NewPassword: Use bytes package<commit_after>package main

import (
	"bytes"
	crand "crypto/rand"
	"fmt"
	"io"
	"math/rand"
)

func NewRand() *rand.Rand {
	var b [8]byte
	_, err := io.ReadFull(crand.Reader, b[:])
	if err != nil {
		panic(err)
	}
	seed := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 |
		int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7])<<56
	return rand.New(rand.NewSource(seed))
}

func NewPassword(length int) string {
	const (
		upper    = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
		lower    = "abcdefghijklmnopqrstuvwxyz"
		digit    = "0123456789"
		alphabet = upper + lower + digit
	)
	pass := make([]byte, 0, length)
	r := NewRand()
	for {
		for i := 0; i < cap(pass); i++ {
			char := alphabet[r.Intn(len(alphabet))]
			pass = append(pass, char)
		}
		if bytes.ContainsAny(pass, upper) &&
			bytes.ContainsAny(pass, lower) &&
			bytes.ContainsAny(pass, digit) {
			return string(pass)
		}
		pass = pass[:0]
	}
}

func main() {
	fmt.Println(NewPassword(8))
}
<|endoftext|>


<commit_before>package graphite

import (
	"fmt"
	"math"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/influxdb/influxdb/tsdb"
)

var defaultTemplate *template

func init() {
	var err error
	defaultTemplate, err = NewTemplate("measurement*", nil)
	if err != nil {
		panic(err)
	}
}

// Parser encapulates a Graphite Parser.
type Parser struct {
	matcher *matcher
	tags    tsdb.Tags
}

// NewParser returns a GraphiteParser instance.
func NewParser(templates []string, defaultTags tsdb.Tags) (*Parser, error) {

	matcher := &matcher{}
	matcher.AddDefaultTemplate(defaultTemplate)

	for _, pattern := range templates {

		template := pattern
		filter := ""
		// Format is [filter] <template> [tags]
		parts := strings.Fields(pattern)
		if len(parts) >= 2 {
			filter = parts[0]
			template = parts[1]
		}

		tags := tsdb.Tags{}
		if strings.Contains(parts[len(parts)-1], "=") {
			tagStrs := strings.Split(parts[len(parts)-1], ",")
			for _, kv := range tagStrs {
				parts := strings.Split(kv, "=")
				tags[parts[0]] = parts[1]
			}
		}

		tmpl, err := NewTemplate(template, tags)
		if err != nil {
			return nil, err
		}
		matcher.Add(filter, tmpl)
	}
	return &Parser{matcher: matcher, tags: defaultTags}, nil
}

// Parse performs Graphite parsing of a single line.
func (p *Parser) Parse(line string) (tsdb.Point, error) {
	// Break into 3 fields (name, value, timestamp).
	fields := strings.Fields(line)
	if len(fields) != 3 {
		return nil, fmt.Errorf("received %q which doesn't have three fields", line)
	}

	// decode the name and tags
	matcher := p.matcher.Match(fields[0])
	name, tags := matcher.Apply(fields[0])
	if name == "" {
		return nil, fmt.Errorf("unable to parse measurement name from %s", line)
	}

	// Parse value.
	v, err := strconv.ParseFloat(fields[1], 64)
	if err != nil {
		return nil, fmt.Errorf("field \"%s\" value: %s", fields[0], err)
	}

	fieldValues := make(map[string]interface{})
	fieldValues["value"] = v

	// Parse timestamp.
	unixTime, err := strconv.ParseFloat(fields[2], 64)
	if err != nil {
		return nil, fmt.Errorf("field \"%s\" time: %s", fields[0], err)
	}

	// Check if we have fractional seconds
	timestamp := time.Unix(int64(unixTime), int64((unixTime-math.Floor(unixTime))*float64(time.Second)))

	for k, v := range p.tags {
		if _, ok := tags[k]; !ok {
			tags[k] = v
		}
	}
	point := tsdb.NewPoint(name, tags, fieldValues, timestamp)

	return point, nil
}

type template struct {
	tags              []string
	defaultTags       tsdb.Tags
	measurementPos    int
	greedyMeasurement bool
}

func NewTemplate(pattern string, defaultTags tsdb.Tags) (*template, error) {
	tags := strings.Split(pattern, ".")
	template := &template{tags: tags, measurementPos: -1, defaultTags: defaultTags}

	for i, tag := range tags {
		if strings.HasPrefix(tag, "measurement") {
			template.measurementPos = i
		}
		if tag == "measurement*" {
			template.greedyMeasurement = true
		}
	}

	if template.measurementPos == -1 {
		return nil, fmt.Errorf("no measurement specified for template. %q", pattern)
	}

	return template, nil
}

func (t *template) Apply(line string) (string, map[string]string) {
	fields := strings.Split(line, ".")
	var (
		measurement string
		tags        = make(map[string]string)
	)

	// Set any default tags
	for k, v := range t.defaultTags {
		tags[k] = v
	}

	for i, tag := range t.tags {
		if i >= len(fields) {
			continue
		}

		if i == t.measurementPos {
			measurement = fields[i]
			if t.greedyMeasurement {
				measurement = strings.Join(fields[i:len(fields)], ".")
			}
		}

		if tag == "measurement" {
			measurement = fields[i]
		} else if tag == "measurement*" {
			measurement = strings.Join(fields[i:len(fields)], ".")
			break
		} else if tag != "" {
			tags[tag] = fields[i]
		}
	}

	return measurement, tags
}

type entry struct {
	filter   *filter
	template *template
}

type entries []*entry

// Less returns a boolean indicating whether the filter at position j
// is less than the filter at postion k.  Filters are order by string
// comparison of each component parts.  A wildcard value "*" is never
// less than a non-wildcard values.
//
// For example, the filters:
//		"*.*"
//		"servers.*""
//		"servers.localhost""
//		"*.localhost"
//
// Would be sorted as:
//		"servers.localhost""
//		"servers.*""
//		"*.localhost"
//		"*.*"
func (s *entries) Less(j, k int) bool {
	jParts := (*s)[j].filter.sections
	kParts := (*s)[k].filter.sections

	i := 0
	for {
		if i >= len(jParts) && i >= len(kParts) {
			return false
		}

		if i < len(jParts) && i < len(kParts) {

			if jParts[i] == "*" && kParts[i] != "*" {
				return false
			}

			if jParts[i] != "*" && kParts[i] == "*" {
				return true
			}

			if jParts[i] < kParts[i] {
				return true
			}

			if jParts[i] > kParts[i] {
				return false
			}
		}

		if i >= len(jParts) && i < len(kParts) {
			return true
		}

		if i < len(jParts) && i >= len(kParts) {
			return false
		}

		i += 1
	}
}

func (s *entries) Swap(i, j int) {
	(*s)[i], (*s)[j] = (*s)[j], (*s)[i]
}

func (s *entries) Len() int {
	return len(*s)
}

type matcher struct {
	entries         entries
	defaultTemplate *template
}

func (m *matcher) Add(match string, template *template) {
	if match == "" {
		m.AddDefaultTemplate(template)
		return
	}
	m.entries = append(m.entries, &entry{&filter{sections: strings.Split(match, ".")}, template})
	sort.Sort(&m.entries)
}

func (m *matcher) AddDefaultTemplate(template *template) {
	m.defaultTemplate = template
}

func (m *matcher) Match(line string) *template {

	if len(m.entries) == 0 {
		return m.defaultTemplate
	}

	lineParts := strings.Split(line, ".")
	for _, entry := range m.entries {
		if entry.filter.Matches(lineParts) {
			return entry.template
		}
	}

	return m.defaultTemplate
}

type filter struct {
	sections []string
}

func (f *filter) Matches(lineParts []string) bool {
	for i, filterPart := range f.sections {
		if filterPart == "*" {
			continue
		}

		if i >= len(lineParts) {
			return false
		}

		if filterPart != lineParts[i] {
			return false
		}
	}
	return true
}
<commit_msg>Update tempalte format comment<commit_after>package graphite

import (
	"fmt"
	"math"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/influxdb/influxdb/tsdb"
)

var defaultTemplate *template

func init() {
	var err error
	defaultTemplate, err = NewTemplate("measurement*", nil)
	if err != nil {
		panic(err)
	}
}

// Parser encapulates a Graphite Parser.
type Parser struct {
	matcher *matcher
	tags    tsdb.Tags
}

// NewParser returns a GraphiteParser instance.
func NewParser(templates []string, defaultTags tsdb.Tags) (*Parser, error) {

	matcher := &matcher{}
	matcher.AddDefaultTemplate(defaultTemplate)

	for _, pattern := range templates {

		template := pattern
		filter := ""
		// Format is [filter] <template> [tag1=value1,tag2=value2]
		parts := strings.Fields(pattern)
		if len(parts) >= 2 {
			filter = parts[0]
			template = parts[1]
		}

		tags := tsdb.Tags{}
		if strings.Contains(parts[len(parts)-1], "=") {
			tagStrs := strings.Split(parts[len(parts)-1], ",")
			for _, kv := range tagStrs {
				parts := strings.Split(kv, "=")
				tags[parts[0]] = parts[1]
			}
		}

		tmpl, err := NewTemplate(template, tags)
		if err != nil {
			return nil, err
		}
		matcher.Add(filter, tmpl)
	}
	return &Parser{matcher: matcher, tags: defaultTags}, nil
}

// Parse performs Graphite parsing of a single line.
func (p *Parser) Parse(line string) (tsdb.Point, error) {
	// Break into 3 fields (name, value, timestamp).
	fields := strings.Fields(line)
	if len(fields) != 3 {
		return nil, fmt.Errorf("received %q which doesn't have three fields", line)
	}

	// decode the name and tags
	matcher := p.matcher.Match(fields[0])
	name, tags := matcher.Apply(fields[0])
	if name == "" {
		return nil, fmt.Errorf("unable to parse measurement name from %s", line)
	}

	// Parse value.
	v, err := strconv.ParseFloat(fields[1], 64)
	if err != nil {
		return nil, fmt.Errorf("field \"%s\" value: %s", fields[0], err)
	}

	fieldValues := make(map[string]interface{})
	fieldValues["value"] = v

	// Parse timestamp.
	unixTime, err := strconv.ParseFloat(fields[2], 64)
	if err != nil {
		return nil, fmt.Errorf("field \"%s\" time: %s", fields[0], err)
	}

	// Check if we have fractional seconds
	timestamp := time.Unix(int64(unixTime), int64((unixTime-math.Floor(unixTime))*float64(time.Second)))

	for k, v := range p.tags {
		if _, ok := tags[k]; !ok {
			tags[k] = v
		}
	}
	point := tsdb.NewPoint(name, tags, fieldValues, timestamp)

	return point, nil
}

type template struct {
	tags              []string
	defaultTags       tsdb.Tags
	measurementPos    int
	greedyMeasurement bool
}

func NewTemplate(pattern string, defaultTags tsdb.Tags) (*template, error) {
	tags := strings.Split(pattern, ".")
	template := &template{tags: tags, measurementPos: -1, defaultTags: defaultTags}

	for i, tag := range tags {
		if strings.HasPrefix(tag, "measurement") {
			template.measurementPos = i
		}
		if tag == "measurement*" {
			template.greedyMeasurement = true
		}
	}

	if template.measurementPos == -1 {
		return nil, fmt.Errorf("no measurement specified for template. %q", pattern)
	}

	return template, nil
}

func (t *template) Apply(line string) (string, map[string]string) {
	fields := strings.Split(line, ".")
	var (
		measurement string
		tags        = make(map[string]string)
	)

	// Set any default tags
	for k, v := range t.defaultTags {
		tags[k] = v
	}

	for i, tag := range t.tags {
		if i >= len(fields) {
			continue
		}

		if i == t.measurementPos {
			measurement = fields[i]
			if t.greedyMeasurement {
				measurement = strings.Join(fields[i:len(fields)], ".")
			}
		}

		if tag == "measurement" {
			measurement = fields[i]
		} else if tag == "measurement*" {
			measurement = strings.Join(fields[i:len(fields)], ".")
			break
		} else if tag != "" {
			tags[tag] = fields[i]
		}
	}

	return measurement, tags
}

type entry struct {
	filter   *filter
	template *template
}

type entries []*entry

// Less returns a boolean indicating whether the filter at position j
// is less than the filter at postion k.  Filters are order by string
// comparison of each component parts.  A wildcard value "*" is never
// less than a non-wildcard values.
//
// For example, the filters:
//		"*.*"
//		"servers.*""
//		"servers.localhost""
//		"*.localhost"
//
// Would be sorted as:
//		"servers.localhost""
//		"servers.*""
//		"*.localhost"
//		"*.*"
func (s *entries) Less(j, k int) bool {
	jParts := (*s)[j].filter.sections
	kParts := (*s)[k].filter.sections

	i := 0
	for {
		if i >= len(jParts) && i >= len(kParts) {
			return false
		}

		if i < len(jParts) && i < len(kParts) {

			if jParts[i] == "*" && kParts[i] != "*" {
				return false
			}

			if jParts[i] != "*" && kParts[i] == "*" {
				return true
			}

			if jParts[i] < kParts[i] {
				return true
			}

			if jParts[i] > kParts[i] {
				return false
			}
		}

		if i >= len(jParts) && i < len(kParts) {
			return true
		}

		if i < len(jParts) && i >= len(kParts) {
			return false
		}

		i += 1
	}
}

func (s *entries) Swap(i, j int) {
	(*s)[i], (*s)[j] = (*s)[j], (*s)[i]
}

func (s *entries) Len() int {
	return len(*s)
}

type matcher struct {
	entries         entries
	defaultTemplate *template
}

func (m *matcher) Add(match string, template *template) {
	if match == "" {
		m.AddDefaultTemplate(template)
		return
	}
	m.entries = append(m.entries, &entry{&filter{sections: strings.Split(match, ".")}, template})
	sort.Sort(&m.entries)
}

func (m *matcher) AddDefaultTemplate(template *template) {
	m.defaultTemplate = template
}

func (m *matcher) Match(line string) *template {

	if len(m.entries) == 0 {
		return m.defaultTemplate
	}

	lineParts := strings.Split(line, ".")
	for _, entry := range m.entries {
		if entry.filter.Matches(lineParts) {
			return entry.template
		}
	}

	return m.defaultTemplate
}

type filter struct {
	sections []string
}

func (f *filter) Matches(lineParts []string) bool {
	for i, filterPart := range f.sections {
		if filterPart == "*" {
			continue
		}

		if i >= len(lineParts) {
			return false
		}

		if filterPart != lineParts[i] {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>package models

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"
	"time"

	"github.com/gorilla/mux"
)

func TestPurchasesByArgument(t *testing.T) {
	// Imitating a third party API. Unlike the real one this always
	// returns the same JSON response.
	r := mux.NewRouter()
	a := r.Path("/api/").Subrouter()
	a.HandleFunc("/purchases/by_user/test", http.NotFoundHandler().ServeHTTP)
	a.HandleFunc("/purchases/by_user/{id}", testPurchasesByArgH)
	a.HandleFunc("/purchases/by_product/{id}", testPurchasesByArgH)

	// Creating a test server with the API.
	s := httptest.NewServer(a)
	defer s.Close()

	// Setting the API's URI.
	Init(s.URL + "/api/")

	// Check the case when the API's response is a valid JSON.
	ps, err := PurchasesByProductID("xxx", 0)
	if err != nil || !reflect.DeepEqual(ps, testPurchases) {
		t.Errorf(`Expected %v, nil. Got %v, "%v".`, testPurchases, ps, err)
	}
	ps, err = PurchasesByUsername("xxx", 0)
	if err != nil || !reflect.DeepEqual(ps, testPurchases) {
		t.Errorf(`Expected %v, nil. Got %v, "%v".`, testPurchases, ps, err)
	}

	// Make sure that there's an error returned if response status is incorrect.
	ps, err = PurchasesByUsername("test", 0)
	if ps != nil || err == nil {
		t.Errorf(`Expected no result and an error. Got %v, "%v".`, ps, err)
	}
}

// testPurchasesByArgH is a handler that imitates the third
// party API that provides purchases by username / id.
var testPurchasesByArgH = func(w http.ResponseWriter, r *http.Request) {
	// Transform the test list of purchases into JSON.
	res, err := json.Marshal(testPurchases)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	// Render the result.
	w.Header().Add("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	w.Write(res)
}

var testPurchases = []Purchase{
	{123, 321, "JohnDoe", time.Now()},
	{222, 444, "Mr.X", time.Now()},
}
<commit_msg>Full test coverage of the models<commit_after>package models

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"
	"time"

	"github.com/gorilla/mux"
)

func TestPurchasesByArgument(t *testing.T) {
	// Imitating a third party API. Unlike the real one this always
	// returns the same JSON response.
	r := mux.NewRouter()
	a := r.Path("/api/").Subrouter()
	a.HandleFunc("/purchases/by_user/test", http.NotFoundHandler().ServeHTTP)
	a.HandleFunc("/purchases/by_user/empty", emptyH)
	a.HandleFunc("/purchases/by_user/{id}", testPurchasesByArgH)
	a.HandleFunc("/purchases/by_product/{id}", testPurchasesByArgH)

	// Creating a test server with the API.
	s := httptest.NewServer(a)
	defer s.Close()

	// Setting the API's URI.
	Init(s.URL + "/api/")

	// Check the case when the API's response is a valid JSON.
	ps, err := PurchasesByProductID("xxx", 0)
	if err != nil || !reflect.DeepEqual(ps, testPurchases) {
		t.Errorf(`Expected %v, nil. Got %v, "%v".`, testPurchases, ps, err)
	}
	ps, err = PurchasesByUsername("xxx", 0)
	if err != nil || !reflect.DeepEqual(ps, testPurchases) {
		t.Errorf(`Expected %v, nil. Got %v, "%v".`, testPurchases, ps, err)
	}

	// Check all possible errors, including:
	// 1. incorrect response status.
	// 2. invalid JSON.
	for i, arg := range []string{"test", "empty"} {
		ps, err = PurchasesByUsername(arg, 0)
		if ps != nil || err == nil {
			t.Errorf(`Test %d: Expected no result and an error. Got %v, "%v".`, i, ps, err)
		}
	}
}

func TestPurchasesFromURN(t *testing.T) {
	ps, err := purchasesFromURN("urn_that_doesnt_exist")
	if ps != nil || err == nil {
		t.Errorf(`Expected no result and an error. Got %v, "%v".`, ps, err)
	}
}

// testPurchasesByArgH is a handler that imitates the third
// party API that provides purchases by username / id.
var testPurchasesByArgH = func(w http.ResponseWriter, r *http.Request) {
	// Transform the test list of purchases into JSON.
	res, err := json.Marshal(testPurchases)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	// Render the result.
	w.Header().Add("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	w.Write(res)
}

// emptyH is a handler that renders an empty page.
var emptyH = func(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
}

var testPurchases = []Purchase{
	{123, 321, "JohnDoe", time.Now()},
	{222, 444, "Mr.X", time.Now()},
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"log"
	"net/url"
	"strings"
	"time"

	"github.com/UniversityRadioYork/2016-site/structs"
	"github.com/UniversityRadioYork/myradio-go"
)

// ScheduleItem contains information about one item in a URY schedule.
type ScheduleItem struct {
	// Name is the display name of the schedule item.
	Name string

	// Desc is the description of the schedule item.
	Desc string

	// Start is the start time of the schedule item.
	Start time.Time

	// Finish is the finish time of the schedule item.
	Finish time.Time

	// Block is the block name of the schedule item.
	Block string

	// PageURL is the root-relative URL to this schedule item's page,
	// or "" if there is no URL.
	PageURL string
}

// IsSustainer checks whether this schedule item is the URY sustainer.
func (s *ScheduleItem) IsSustainer() bool {
	return s.Block == "sustainer"
}

// NewSustainerItem creates a new sustainer schedule item lasting from start to finish.
// It takes a sustainer config, c, to work out the sustainer name.
func NewSustainerItem(c structs.SustainerConfig, start, finish time.Time) *ScheduleItem {
	return &ScheduleItem{
		Name:    c.Name,
		Desc:    c.Desc,
		Start:   start,
		Finish:  finish,
		Block:   "sustainer",
		PageURL: "",
	}
}

// NewTimeslotItem converts a myradio.Timeslot into a TimeslotItem.
// It accepts a separate finish time to account for any truncating that occurs when resolving overlaps.
func NewTimeslotItem(t *myradio.Timeslot, finish time.Time, u func(*myradio.Timeslot) (*url.URL, error)) (*ScheduleItem, error) {
	if t == nil {
		return nil, errors.New("NewTimeslotItem: given nil timeslot")
	}

	url, err := u(t)
	if err != nil {
		return nil, err
	}
	return &ScheduleItem{
		Name:    t.Title,
		Desc:    t.Description,
		Start:   t.StartTime,
		Finish:  finish,
		Block:   getBlock(t.Title, t.StartTime),
		PageURL: url.Path,
	}, nil
}

func getBlock(name string, StartTime time.Time) string {
	name = strings.ToLower(name)

	type blockMatch struct {
		nameFragment string
		block        string
	}
	var blockMatches = []blockMatch{
		{"ury: early morning", "primetime"},
		{"ury breakfast", "primetime"},
		{"ury lunch", "primetime"},
		{"ury brunch", "primetime"},
		{"URY Brunch", "primetime"},
		{"URY Afternoon Tea:", "primetime"},
		{"URY:PM", "primetime"},
		{"Alumni Takeover:", "primetime"},

		{"ury news", "news"},
		{"ury sports", "news"},
		{"ury football", "news"},
		{"york sport report", "news"},
		{"university radio talk", "news"},
		{"candidate interview night", "news"},
		{"election results night", "news"},
		{"yusu election", "news"},
		{"The Second Half With Josh Kerr", "news"},
		{"URY SPORT", "news"},
		{"URY News & Sport:", "news"},

		{"ury speech", "speech"},
		{"yorworld", "speech"},
		{"in the stalls", "speech"},
		{"screen", "speech"},
		{"stage", "speech"},
		{"game breaking", "speech"},
		{"radio drama", "speech"},
		{"Book Corner", "speech"},
		{"Saturated Facts", "speech"},
		{"URWatch", "speech"},
		{"Society Challenge", "speech"},
		{"Speech Showcase", "speech"},
		{"URY Speech:", "speech"},

		{"URY Music:", "music"},

		{"roses live 20", "event"},
		{"roses 20", "event"},
    {"freshers 20", "event"},
		{"woodstock", "event"},
		{"movember", "event"},
		{"panto", "event"},
		{"101:", "event"},
		{"Vanbrugh Chair Debate", "event"},
		{"URY Does RAG Courtyard Takeover", "event"},
		{"URY Presents", "event"},
		{"URYOnTour", "event"},
		{"URY On Tour", "event"},

		{"YSTV", "collab"},
		{"Nouse", "collab"},
		{"York Politics Digest", "collab"},
    {"Breakz", "collab"},
	}
	for _, bm := range blockMatches {
		if strings.Contains(name, strings.ToLower(bm.nameFragment)) {
			return bm.block
		}
	}
	// certain times of the day correspond to a specific show type.
	if (StartTime.Hour() == 11) || (StartTime.Hour() == 19) { // missed flagship
		return "primetime"
	}
	return "regular"
}

// scheduleBuilder is an internal type holding information about a schedule slice under construction.
type scheduleBuilder struct {
	// config is the sustainer config to use when creating sustainer slots.
	config structs.SustainerConfig
	// slice is the schedule slice being constructed.
	items []*ScheduleItem
	// nitems is the number of items currently inside the schedule.
	nitems int
	// tbuilder is the function used to create schedule items from timeslots.
	tbuilder func(*myradio.Timeslot, time.Time) (*ScheduleItem, error)
	// err stores any error caused while building the schedule.
	err error
}

// newScheduleBuilder creates an empty schedule builder for nslots shows, given config c and builder tbuilder.
func newScheduleBuilder(c structs.SustainerConfig, tbuilder func(*myradio.Timeslot, time.Time) (*ScheduleItem, error), nslots int) *scheduleBuilder {
	return &scheduleBuilder{
		config: c,
		// nslots slots, (nslots - 1) sustainers in between, and 2 sustainers at the ends.
		items:    make([]*ScheduleItem, ((2 * nslots) + 1)),
		nitems:   0,
		tbuilder: tbuilder,
		err:      nil,
	}
}

// add adds an item to the scheduleBuilder s.
func (s *scheduleBuilder) add(i *ScheduleItem) {
	s.items[s.nitems] = i
	s.nitems++
}

// fill adds a sustainer timeslot between start and finish into the scheduleBuilder s if one needs to be there.
func (s *scheduleBuilder) fill(start, finish time.Time) {
	if start.Before(finish) {
		s.add(NewSustainerItem(s.config, start, finish))
	}
}

// addTimeslot converts a timeslot t to a schedule item, then adds it to the scheduleBuilder s.
// It takes an overlap-adjusted finish, and does not add an item if this adjustment causes t to disappear.
func (s *scheduleBuilder) addTimeslot(t *myradio.Timeslot, finish time.Time) {
	if s.err != nil || !t.StartTime.Before(finish) {
		return
	}

	var ts *ScheduleItem
	if ts, s.err = s.tbuilder(t, finish); s.err != nil {
		return
	}

	s.add(ts)
}

// schedule gets the schedule from a scheduleBuilder, or an err if schedule building failed.
func (s *scheduleBuilder) schedule() ([]*ScheduleItem, error) {
	if s.err != nil {
		return nil, s.err
	}
	return s.items[:s.nitems], nil
}

// truncateOverlap clips finish to nextStart if the two overlap and nextShow exists.
// If so, we log an overlap warning, whose content depends on show and nextShow.
// If nextShow is nil, we've overlapped with the end of the schedule, which doesn't need truncating.
func truncateOverlap(finish, nextStart time.Time, show, nextShow *myradio.Timeslot) time.Time {
	if nextShow == nil || !finish.After(nextStart) {
		return finish
	}

	// If the show starts after the next ends then there is no overlap
	if show.StartTime.After(nextStart.Add(nextShow.Duration)) {
		return finish
	}

	log.Println("Truncating" + show.Title)

	log.Printf(
		"Timeslot '%s', ID %d, finishing at %v overlaps with timeslot '%s', ID %d, starting at %v'",
		show.Title,
		show.TimeslotID,
		finish,
		nextShow.Title,
		nextShow.TimeslotID,
		nextStart,
	)

	return nextStart
}

// MakeScheduleSlice converts a slice of Timeslots to a slice of ScheduleItems.
// It does so by filling in any gaps between the start time and the first show, the final show and the finish time, and any two shows.
// Any overlaps are resolved by truncating the timeslot finish time, and dropping it if this makes the timeslot disappear.
// It expects a constructor function for lifting Timeslots (and overlap-adjusted finish times) to TimeslotItems.
// It will return an error if any two shows overlap.
// It presumes the timeslot slice is already sorted in chronological order.
func MakeScheduleSlice(c structs.SustainerConfig, start, finish time.Time, slots []myradio.Timeslot, tbuilder func(*myradio.Timeslot, time.Time) (*ScheduleItem, error)) ([]*ScheduleItem, error) {
	nslots := len(slots)
	if nslots == 0 {
		return []*ScheduleItem{NewSustainerItem(c, start, finish)}, nil
	}

	s := newScheduleBuilder(c, tbuilder, nslots)
	s.fill(start, slots[0].StartTime)

	// Now, if possible, start filling between.
	var show, nextShow *myradio.Timeslot
	for i := range slots {
		show = &slots[i]
		rawShowFinish := show.StartTime.Add(show.Duration)

		var nextStart time.Time
		// Is the next start another show, or the end of the schedule?
		if i < nslots-1 {
			nextShow = &slots[i+1]
			nextStart = nextShow.StartTime
		} else {
			nextShow = nil
			nextStart = finish
		}

		showFinish := truncateOverlap(rawShowFinish, nextStart, show, nextShow)
		s.addTimeslot(show, showFinish)
		s.fill(showFinish, nextStart)
	}

	return s.schedule()
}
<commit_msg>Suppert "URY N&S" as a news & sport prefix<commit_after>package models

import (
	"errors"
	"log"
	"net/url"
	"strings"
	"time"

	"github.com/UniversityRadioYork/2016-site/structs"
	"github.com/UniversityRadioYork/myradio-go"
)

// ScheduleItem contains information about one item in a URY schedule.
type ScheduleItem struct {
	// Name is the display name of the schedule item.
	Name string

	// Desc is the description of the schedule item.
	Desc string

	// Start is the start time of the schedule item.
	Start time.Time

	// Finish is the finish time of the schedule item.
	Finish time.Time

	// Block is the block name of the schedule item.
	Block string

	// PageURL is the root-relative URL to this schedule item's page,
	// or "" if there is no URL.
	PageURL string
}

// IsSustainer checks whether this schedule item is the URY sustainer.
func (s *ScheduleItem) IsSustainer() bool {
	return s.Block == "sustainer"
}

// NewSustainerItem creates a new sustainer schedule item lasting from start to finish.
// It takes a sustainer config, c, to work out the sustainer name.
func NewSustainerItem(c structs.SustainerConfig, start, finish time.Time) *ScheduleItem {
	return &ScheduleItem{
		Name:    c.Name,
		Desc:    c.Desc,
		Start:   start,
		Finish:  finish,
		Block:   "sustainer",
		PageURL: "",
	}
}

// NewTimeslotItem converts a myradio.Timeslot into a TimeslotItem.
// It accepts a separate finish time to account for any truncating that occurs when resolving overlaps.
func NewTimeslotItem(t *myradio.Timeslot, finish time.Time, u func(*myradio.Timeslot) (*url.URL, error)) (*ScheduleItem, error) {
	if t == nil {
		return nil, errors.New("NewTimeslotItem: given nil timeslot")
	}

	url, err := u(t)
	if err != nil {
		return nil, err
	}
	return &ScheduleItem{
		Name:    t.Title,
		Desc:    t.Description,
		Start:   t.StartTime,
		Finish:  finish,
		Block:   getBlock(t.Title, t.StartTime),
		PageURL: url.Path,
	}, nil
}

func getBlock(name string, StartTime time.Time) string {
	name = strings.ToLower(name)

	type blockMatch struct {
		nameFragment string
		block        string
	}
	var blockMatches = []blockMatch{
		{"ury: early morning", "primetime"},
		{"ury breakfast", "primetime"},
		{"ury lunch", "primetime"},
		{"ury brunch", "primetime"},
		{"URY Brunch", "primetime"},
		{"URY Afternoon Tea:", "primetime"},
		{"URY:PM", "primetime"},
		{"Alumni Takeover:", "primetime"},

		{"ury news", "news"},
		{"ury sports", "news"},
		{"ury football", "news"},
		{"york sport report", "news"},
		{"university radio talk", "news"},
		{"candidate interview night", "news"},
		{"election results night", "news"},
		{"yusu election", "news"},
		{"The Second Half With Josh Kerr", "news"},
		{"URY SPORT", "news"},
		{"URY News & Sport:", "news"},
    {"URY N&S:", "news"},

		{"ury speech", "speech"},
		{"yorworld", "speech"},
		{"in the stalls", "speech"},
		{"screen", "speech"},
		{"stage", "speech"},
		{"game breaking", "speech"},
		{"radio drama", "speech"},
		{"Book Corner", "speech"},
		{"Saturated Facts", "speech"},
		{"URWatch", "speech"},
		{"Society Challenge", "speech"},
		{"Speech Showcase", "speech"},
		{"URY Speech:", "speech"},

		{"URY Music:", "music"},

		{"roses live 20", "event"},
		{"roses 20", "event"},
    {"freshers 20", "event"},
		{"woodstock", "event"},
		{"movember", "event"},
		{"panto", "event"},
		{"101:", "event"},
		{"Vanbrugh Chair Debate", "event"},
		{"URY Does RAG Courtyard Takeover", "event"},
		{"URY Presents", "event"},
		{"URYOnTour", "event"},
		{"URY On Tour", "event"},

		{"YSTV", "collab"},
		{"Nouse", "collab"},
		{"York Politics Digest", "collab"},
    {"Breakz", "collab"},
	}
	for _, bm := range blockMatches {
		if strings.Contains(name, strings.ToLower(bm.nameFragment)) {
			return bm.block
		}
	}
	// certain times of the day correspond to a specific show type.
	if (StartTime.Hour() == 11) || (StartTime.Hour() == 19) { // missed flagship
		return "primetime"
	}
	return "regular"
}

// scheduleBuilder is an internal type holding information about a schedule slice under construction.
type scheduleBuilder struct {
	// config is the sustainer config to use when creating sustainer slots.
	config structs.SustainerConfig
	// slice is the schedule slice being constructed.
	items []*ScheduleItem
	// nitems is the number of items currently inside the schedule.
	nitems int
	// tbuilder is the function used to create schedule items from timeslots.
	tbuilder func(*myradio.Timeslot, time.Time) (*ScheduleItem, error)
	// err stores any error caused while building the schedule.
	err error
}

// newScheduleBuilder creates an empty schedule builder for nslots shows, given config c and builder tbuilder.
func newScheduleBuilder(c structs.SustainerConfig, tbuilder func(*myradio.Timeslot, time.Time) (*ScheduleItem, error), nslots int) *scheduleBuilder {
	return &scheduleBuilder{
		config: c,
		// nslots slots, (nslots - 1) sustainers in between, and 2 sustainers at the ends.
		items:    make([]*ScheduleItem, ((2 * nslots) + 1)),
		nitems:   0,
		tbuilder: tbuilder,
		err:      nil,
	}
}

// add adds an item to the scheduleBuilder s.
func (s *scheduleBuilder) add(i *ScheduleItem) {
	s.items[s.nitems] = i
	s.nitems++
}

// fill adds a sustainer timeslot between start and finish into the scheduleBuilder s if one needs to be there.
func (s *scheduleBuilder) fill(start, finish time.Time) {
	if start.Before(finish) {
		s.add(NewSustainerItem(s.config, start, finish))
	}
}

// addTimeslot converts a timeslot t to a schedule item, then adds it to the scheduleBuilder s.
// It takes an overlap-adjusted finish, and does not add an item if this adjustment causes t to disappear.
func (s *scheduleBuilder) addTimeslot(t *myradio.Timeslot, finish time.Time) {
	if s.err != nil || !t.StartTime.Before(finish) {
		return
	}

	var ts *ScheduleItem
	if ts, s.err = s.tbuilder(t, finish); s.err != nil {
		return
	}

	s.add(ts)
}

// schedule gets the schedule from a scheduleBuilder, or an err if schedule building failed.
func (s *scheduleBuilder) schedule() ([]*ScheduleItem, error) {
	if s.err != nil {
		return nil, s.err
	}
	return s.items[:s.nitems], nil
}

// truncateOverlap clips finish to nextStart if the two overlap and nextShow exists.
// If so, we log an overlap warning, whose content depends on show and nextShow.
// If nextShow is nil, we've overlapped with the end of the schedule, which doesn't need truncating.
func truncateOverlap(finish, nextStart time.Time, show, nextShow *myradio.Timeslot) time.Time {
	if nextShow == nil || !finish.After(nextStart) {
		return finish
	}

	// If the show starts after the next ends then there is no overlap
	if show.StartTime.After(nextStart.Add(nextShow.Duration)) {
		return finish
	}

	log.Println("Truncating" + show.Title)

	log.Printf(
		"Timeslot '%s', ID %d, finishing at %v overlaps with timeslot '%s', ID %d, starting at %v'",
		show.Title,
		show.TimeslotID,
		finish,
		nextShow.Title,
		nextShow.TimeslotID,
		nextStart,
	)

	return nextStart
}

// MakeScheduleSlice converts a slice of Timeslots to a slice of ScheduleItems.
// It does so by filling in any gaps between the start time and the first show, the final show and the finish time, and any two shows.
// Any overlaps are resolved by truncating the timeslot finish time, and dropping it if this makes the timeslot disappear.
// It expects a constructor function for lifting Timeslots (and overlap-adjusted finish times) to TimeslotItems.
// It will return an error if any two shows overlap.
// It presumes the timeslot slice is already sorted in chronological order.
func MakeScheduleSlice(c structs.SustainerConfig, start, finish time.Time, slots []myradio.Timeslot, tbuilder func(*myradio.Timeslot, time.Time) (*ScheduleItem, error)) ([]*ScheduleItem, error) {
	nslots := len(slots)
	if nslots == 0 {
		return []*ScheduleItem{NewSustainerItem(c, start, finish)}, nil
	}

	s := newScheduleBuilder(c, tbuilder, nslots)
	s.fill(start, slots[0].StartTime)

	// Now, if possible, start filling between.
	var show, nextShow *myradio.Timeslot
	for i := range slots {
		show = &slots[i]
		rawShowFinish := show.StartTime.Add(show.Duration)

		var nextStart time.Time
		// Is the next start another show, or the end of the schedule?
		if i < nslots-1 {
			nextShow = &slots[i+1]
			nextStart = nextShow.StartTime
		} else {
			nextShow = nil
			nextStart = finish
		}

		showFinish := truncateOverlap(rawShowFinish, nextStart, show, nextShow)
		s.addTimeslot(show, showFinish)
		s.fill(showFinish, nextStart)
	}

	return s.schedule()
}
<|endoftext|>