<commit_before>package factorioSave

import (
	"log"
	"encoding/binary"
	"errors"
	"io"
	"strconv"
	"github.com/Masterminds/semver"
)

type version16 struct {
	versionShort16
	Revision    uint16  `json:"revision"`
}
type versionShort16 struct {
	Major       uint16  `json:"major"`
	Minor       uint16  `json:"minor"`
	Build       uint16  `json:"build"`
}
type versionShort8 struct {
	Major       uint8   `json:"major"`
	Minor       uint8   `json:"minor"`
	Build       uint8   `json:"build"`
}
type Header struct {
	FactorioVersion           version16     `json:"factorio_version"`
	Campaign                  string        `json:"campaign"`
	Name                      string        `json:"name"`
	BaseMod                   string        `json:"base_mod"`
	Difficulty                uint8         `json:"difficulty"`
	Finished                  bool          `json:"finished"`
	PlayerWon                 bool          `json:"player_won"`
	NextLevel                 string        `json:"next_level"`
	CanContinue               bool          `json:"can_continue"`
	FinishedButContinuing     bool          `json:"finished_but_continuing"`
	SavingReplay              bool          `json:"saving_replay"`
	AllowNonAdminDebugOptions bool          `json:"allow_non_admin_debug_options"`
	LoadedFrom                versionShort8 `json:"loaded_from"`
	LoadedFromBuild           uint16        `json:"loaded_from_build"`
	AllowedCommads            uint8         `json:"allowed_commads"`
	NumMods                   uint8         `json:"num_mods"`
	Mods                      []singleMod   `json:"mods"`
}
type singleMod struct {
	Name    string          `json:"name"`
	Version versionShort8   `json:"version"`
	CRC     uint32          `json:"crc"`
}

var ErrorIncompatible = errors.New("incompatible save")
var data Header

func ReadHeader(filePath string) (Header, error) {
	var err error

	datFile, err := openSave(filePath)
	if err != nil {
		log.Printf("error opening file: %s", err)
		return data, err
	}
	defer datFile.Close()

	data.FactorioVersion, err = readVersion16(datFile)
	if err != nil {
		log.Printf("Cant read FactorioVersion: %s", err)
		return data, err
	}

	Constraint, _ := semver.NewConstraint("0.16.0 - 0.17.0")
	Compatible, err := data.FactorioVersion.CheckCompatibility(Constraint)
	if err != nil {
		log.Printf("Error checking compatibility: %s", err)
		return data, err
	}
	if !Compatible {
		log.Printf("NOT COMPATIBLE Save-File")
		log.Println(data)
		return data, ErrorIncompatible
	}

	data.Campaign, err = readUTF8String(datFile)
	if err != nil {
		log.Printf("Cant read Campaign: %s", err)
		return data, err
	}

	data.Name, err = readUTF8String(datFile)
	if err != nil {
		log.Printf("Cant read Name: %s", err)
		return data, err
	}

	data.BaseMod, err = readUTF8String(datFile)
	if err != nil {
		log.Printf("Cant read BaseMod: %s", err)
		return data, err
	}

	data.Difficulty, err = readUint8(datFile)
	if err != nil {
		log.Printf("Cant read Difficulty: %s", err)
		return data, err
	}

	data.Finished, err = readBool(datFile)
	if err != nil {
		log.Printf("Couln't read Finished bool: %s", err)
		return data, err
	}

	data.PlayerWon, err = readBool(datFile)
	if err != nil {
		log.Printf("Couldn't read PlayerWon: %s", err)
		return data, err
	}

	data.NextLevel, err = readUTF8String(datFile)
	if err != nil {
		log.Printf("Couldn't read NextLevel: %s", err)
		return data, err
	}

	data.CanContinue, err = readBool(datFile)
	if err != nil {
		log.Printf("Couldn't read CanContinue: %s", err)
		return data, err
	}

	data.FinishedButContinuing, err = readBool(datFile)
	if err != nil {
		log.Printf("Couldn't read FinishedButContinuing: %s", err)
		return data, err
	}

	data.SavingReplay, err = readBool(datFile)
	if err != nil {
		log.Printf("Couldn't read SavingReplay: %s", err)
		return data, err
	}

	Constraint, _ = semver.NewConstraint(">= 0.16.0")
	Used, err := data.FactorioVersion.CheckCompatibility(Constraint)
	if err != nil {
		log.Printf("Error checking if used: %s", err)
		return data, err
	}
	if Used {
		data.AllowNonAdminDebugOptions, err = readBool(datFile)
		if err != nil {
			log.Printf("Couldn't read allow_non_admin_debug_options: %s", err)
			return data, err
		}
	}

	data.LoadedFrom, err = readVersionShort8(datFile)
	if err != nil {
		log.Printf("Couldn't read LoadedFrom: %s", err)
		return data, err
	}

	data.LoadedFromBuild, err = readUint16(datFile)
	if err != nil {
		log.Printf("Couldn't read LoadedFromBuild: %s", err)
		return data, err
	}

	data.AllowedCommads, err = readUint8(datFile)
	if err != nil {
		log.Printf("Couldn't read AllowedCommands: %s", err)
		return data, err
	}

	data.NumMods, err = readUint8(datFile)
	if err != nil {
		log.Printf("Couldn't read NumMods: %s", err)
		return data, err
	}

	for i := uint8(0); i < data.NumMods; i++ {
		SingleMod, err := readSingleMod(datFile)
		if err != nil {
			log.Printf("Couldn't read SingleMod: %s", err)
			return data, err
		}

		data.Mods = append(data.Mods, SingleMod)
	}

	log.Println(data)
	return data, nil
}

func readUTF8String(file io.ReadCloser) (string, error) {
	var err error
	infoByte := make([]byte, 1)

	_, err = file.Read(infoByte)
	if err != nil {
		log.Printf("Error reading infoByte: %s", err)
		return "", nil
	}
	stringLengthInBytes := int8(infoByte[0])

	stringBytes := make([]byte, stringLengthInBytes)
	_, err = file.Read(stringBytes)
	if err != nil {
		log.Printf("error reading bytes: %s", err)
		return "", err
	}
	finalizedString := string(stringBytes[:])

	return finalizedString, nil
}

func readUint8(file io.ReadCloser) (uint8, error) {
	var err error
	var temp [1]byte
	_, err = file.Read(temp[:])
	if err != nil {
		log.Printf("error reading byte: %s", err)
		return 0, nil
	}

	return uint8(temp[0]), nil
}

func readUint16(file io.ReadCloser) (uint16, error) {
	var err error
	var temp [2]byte

	_, err = file.Read(temp[:])
	if err != nil {
		log.Printf("error reading bytes: %s", err)
		return 0, err
	}

	return binary.LittleEndian.Uint16(temp[:]), nil
}

func readUint32(file io.ReadCloser) (uint32, error) {
	var err error
	var temp [4]byte

	_, err = file.Read(temp[:])
	if err != nil {
		log.Printf("error reading bytes: %s", err)
		return 0, err
	}

	return binary.LittleEndian.Uint32(temp[:]), nil
}

func readBool(file io.ReadCloser) (bool, error) {
	byteAsInt, err := readUint8(file)
	if err != nil {
		log.Printf("error loading Uint8: %s", err)
		return false, err
	}

	return byteAsInt != 0, nil
}

func readVersion16(file io.ReadCloser) (version16, error) {
	var Version version16
	var VersionShort versionShort16
	var err error

	VersionShort, err = readVersionShort16(file)
	if err != nil {
		log.Printf("error reading VersionShort")
		return Version, err
	}

	Version.Major = VersionShort.Major
	Version.Minor = VersionShort.Minor
	Version.Build = VersionShort.Build

	Version.Revision, err = readUint16(file)
	if err != nil {
		log.Printf("error reading revision: %s", err)
		return Version, err
	}

	return Version, nil
}

func readVersionShort16(file io.ReadCloser) (versionShort16, error) {
	var Version versionShort16
	var err error

	Version.Major, err = readUint16(file)
	if err != nil {
		log.Printf("error reading major: %s", err)
		return Version, err
	}

	Version.Minor, err = readUint16(file)
	if err != nil {
		log.Printf("error reading minor: %s", err)
		return Version, err
	}

	Version.Build, err = readUint16(file)
	if err != nil {
		log.Printf("error reading build: %s", err)
		return Version, err
	}

	return Version, err
}

func readVersionShort8(file io.ReadCloser) (versionShort8, error) {
	var Version versionShort8
	var err error

	Version.Major, err = readUint8(file)
	if err != nil {
		log.Printf("error reading major: %s", err)
		return Version, err
	}

	Version.Minor, err = readUint8(file)
	if err != nil {
		log.Printf("error reading minor: %s", err)
		return Version, err
	}

	Version.Build, err = readUint8(file)
	if err != nil {
		log.Printf("error reading build: %s", err)
		return Version, err
	}

	return Version, nil
}

func readSingleMod(file io.ReadCloser) (singleMod, error) {
	var Mod singleMod
	var err error

	Mod.Name, err = readUTF8String(file)
	if err != nil {
		log.Printf("error loading modName: %s", err)
		return Mod, err
	}

	Mod.Version, err = readVersionShort8(file)
	if err != nil {
		log.Printf("error loading modVersion: %s", err)
		return Mod, err
	}

	Constraint, _ := semver.NewConstraint("> 0.15.0")
	Used, err := data.FactorioVersion.CheckCompatibility(Constraint)
	if err != nil {
		log.Printf("Error checking used of CRC: %s", err)
		return Mod, err
	}
	if Used {
		Mod.CRC, err = readUint32(file)
		if err != nil {
			log.Printf("error loading CRC: %s", err)
			return Mod, err
		}
	}

	return Mod, err
}

func (Version *versionShort16) CheckCompatibility(constraints *semver.Constraints) (bool, error) {
	Ver, err := semver.NewVersion(strconv.Itoa(int(Version.Major)) + "." + strconv.Itoa(int(Version.Minor)) + "." + strconv.Itoa(int(Version.Build)))
	if err != nil {
		log.Printf("Error creating semver-version: %s", err)
		return false, err
	}

	return constraints.Check(Ver), nil
}
<commit_msg>added compatibility for 0.14.14 - 0.17.0<commit_after>package factorioSave

import (
	"log"
	"encoding/binary"
	"errors"
	"io"
	"strconv"
	"github.com/Masterminds/semver"
)

type version16 struct {
	versionShort16
	Revision    uint16  `json:"revision"`
}
type versionShort16 struct {
	Major       uint16  `json:"major"`
	Minor       uint16  `json:"minor"`
	Build       uint16  `json:"build"`
}
type versionShort8 struct {
	Major       uint8   `json:"major"`
	Minor       uint8   `json:"minor"`
	Build       uint8   `json:"build"`
}
type Header struct {
	FactorioVersion           version16     `json:"factorio_version"`
	Campaign                  string        `json:"campaign"`
	Name                      string        `json:"name"`
	BaseMod                   string        `json:"base_mod"`
	Difficulty                uint8         `json:"difficulty"`
	Finished                  bool          `json:"finished"`
	PlayerWon                 bool          `json:"player_won"`
	NextLevel                 string        `json:"next_level"`
	CanContinue               bool          `json:"can_continue"`
	FinishedButContinuing     bool          `json:"finished_but_continuing"`
	SavingReplay              bool          `json:"saving_replay"`
	AllowNonAdminDebugOptions bool          `json:"allow_non_admin_debug_options"`
	LoadedFrom                versionShort8 `json:"loaded_from"`
	LoadedFromBuild           uint16        `json:"loaded_from_build"`
	AllowedCommads            uint8         `json:"allowed_commads"`
	NumMods                   uint32        `json:"num_mods"`
	Mods                      []singleMod   `json:"mods"`
}
type singleMod struct {
	Name    string          `json:"name"`
	Version versionShort8   `json:"version"`
	CRC     uint32          `json:"crc"`
}

var ErrorIncompatible = errors.New("incompatible save")
var data Header
var constraintGreaterThan0_16 *semver.Constraints

func ReadHeader(filePath string) (Header, error) {
	var err error

	constraintGreaterThan0_16, _ = semver.NewConstraint(">= 0.16.0")

	datFile, err := openSave(filePath)
	if err != nil {
		log.Printf("error opening file: %s", err)
		return data, err
	}
	defer datFile.Close()

	data.FactorioVersion, err = readVersion16(datFile)
	if err != nil {
		log.Printf("Cant read FactorioVersion: %s", err)
		return data, err
	}

	Constraint, _ := semver.NewConstraint("0.14.14 - 0.17.0")
	Compatible, err := data.FactorioVersion.CheckCompatibility(Constraint)
	if err != nil {
		log.Printf("Error checking compatibility: %s", err)
		return data, err
	}
	if !Compatible {
		log.Printf("NOT COMPATIBLE Save-File")
		log.Println(data)
		return data, ErrorIncompatible
	}

	data.Campaign, err = readUTF8String(datFile, false)
	if err != nil {
		log.Printf("Cant read Campaign: %s", err)
		return data, err
	}

	data.Name, err = readUTF8String(datFile, false)
	if err != nil {
		log.Printf("Cant read Name: %s", err)
		return data, err
	}

	data.BaseMod, err = readUTF8String(datFile, false)
	if err != nil {
		log.Printf("Cant read BaseMod: %s", err)
		return data, err
	}

	data.Difficulty, err = readUint8(datFile)
	if err != nil {
		log.Printf("Cant read Difficulty: %s", err)
		return data, err
	}

	data.Finished, err = readBool(datFile)
	if err != nil {
		log.Printf("Couln't read Finished bool: %s", err)
		return data, err
	}

	data.PlayerWon, err = readBool(datFile)
	if err != nil {
		log.Printf("Couldn't read PlayerWon: %s", err)
		return data, err
	}

	data.NextLevel, err = readUTF8String(datFile, false)
	if err != nil {
		log.Printf("Couldn't read NextLevel: %s", err)
		return data, err
	}

	data.CanContinue, err = readBool(datFile)
	if err != nil {
		log.Printf("Couldn't read CanContinue: %s", err)
		return data, err
	}

	data.FinishedButContinuing, err = readBool(datFile)
	if err != nil {
		log.Printf("Couldn't read FinishedButContinuing: %s", err)
		return data, err
	}

	data.SavingReplay, err = readBool(datFile)
	if err != nil {
		log.Printf("Couldn't read SavingReplay: %s", err)
		return data, err
	}

	Used, err := data.FactorioVersion.CheckCompatibility(constraintGreaterThan0_16)
	if err != nil {
		log.Printf("Error checking if used: %s", err)
		return data, err
	}
	if Used {
		data.AllowNonAdminDebugOptions, err = readBool(datFile)
		if err != nil {
			log.Printf("Couldn't read allow_non_admin_debug_options: %s", err)
			return data, err
		}
	}

	data.LoadedFrom, err = readVersionShort8(datFile)
	if err != nil {
		log.Printf("Couldn't read LoadedFrom: %s", err)
		return data, err
	}

	data.LoadedFromBuild, err = readUint16(datFile)
	if err != nil {
		log.Printf("Couldn't read LoadedFromBuild: %s", err)
		return data, err
	}

	data.AllowedCommads, err = readUint8(datFile)
	if err != nil {
		log.Printf("Couldn't read AllowedCommands: %s", err)
		return data, err
	}

	New, err := data.FactorioVersion.CheckCompatibility(constraintGreaterThan0_16)
	if err != nil {
		log.Printf("error checking compatibility: %s", err)
		return data, err
	}

	if New {
		numMods8, err2 := readUint8(datFile) //TODO read Optim. int
		err = err2
		data.NumMods = uint32(numMods8)
	} else {
		data.NumMods, err = readUint32(datFile)
	}
	if err != nil {
		log.Printf("Couldn't read NumMods: %s", err)
		return data, err
	}

	for i := uint32(0); i < data.NumMods; i++ {
		SingleMod, err := readSingleMod(datFile)
		if err != nil {
			log.Printf("Couldn't read SingleMod: %s", err)
			return data, err
		}

		data.Mods = append(data.Mods, SingleMod)
	}

	log.Println(data)
	return data, nil
}

func readUTF8String(file io.ReadCloser, forcedOptim bool) (string, error) {
	var err error

	New, err :=  data.FactorioVersion.CheckCompatibility(constraintGreaterThan0_16)
	if err != nil {
		log.Printf("Couldn't checkCompatibility: %s", err)
		return "", err
	}

	var infoByteStringLength uint32
	if New || forcedOptim {
		infoByteInt8, err2 := readUint8(file) //TODO read optimized int
		err = err2
		infoByteStringLength = uint32(infoByteInt8)
	} else {
		infoByteStringLength, err = readUint32(file) //uint32
	}

	if err != nil {
		log.Printf("Couldn't read infoByteStringLength: %s", err)
		return "", err
	}

	stringBytes := make([]byte, infoByteStringLength)
	_, err = file.Read(stringBytes)
	if err != nil {
		log.Printf("error reading bytes: %s", err)
		return "", err
	}
	finalizedString := string(stringBytes[:])

	return finalizedString, nil
}

func readUint8(file io.ReadCloser) (uint8, error) {
	var err error
	var temp [1]byte
	_, err = file.Read(temp[:])
	if err != nil {
		log.Printf("error reading byte: %s", err)
		return 0, nil
	}

	return uint8(temp[0]), nil
}

func readUint16(file io.ReadCloser) (uint16, error) {
	var err error
	var temp [2]byte

	_, err = file.Read(temp[:])
	if err != nil {
		log.Printf("error reading bytes: %s", err)
		return 0, err
	}

	return binary.LittleEndian.Uint16(temp[:]), nil
}

func readUint32(file io.ReadCloser) (uint32, error) {
	var err error
	var temp [4]byte

	_, err = file.Read(temp[:])
	if err != nil {
		log.Printf("error reading bytes: %s", err)
		return 0, err
	}

	return binary.LittleEndian.Uint32(temp[:]), nil
}

func readBool(file io.ReadCloser) (bool, error) {
	byteAsInt, err := readUint8(file)
	if err != nil {
		log.Printf("error loading Uint8: %s", err)
		return false, err
	}

	return byteAsInt != 0, nil
}

func readVersion16(file io.ReadCloser) (version16, error) {
	var Version version16
	var VersionShort versionShort16
	var err error

	VersionShort, err = readVersionShort16(file)
	if err != nil {
		log.Printf("error reading VersionShort")
		return Version, err
	}

	Version.Major = VersionShort.Major
	Version.Minor = VersionShort.Minor
	Version.Build = VersionShort.Build

	Version.Revision, err = readUint16(file)
	if err != nil {
		log.Printf("error reading revision: %s", err)
		return Version, err
	}

	return Version, nil
}

func readVersionShort16(file io.ReadCloser) (versionShort16, error) {
	var Version versionShort16
	var err error

	Version.Major, err = readUint16(file)
	if err != nil {
		log.Printf("error reading major: %s", err)
		return Version, err
	}

	Version.Minor, err = readUint16(file)
	if err != nil {
		log.Printf("error reading minor: %s", err)
		return Version, err
	}

	Version.Build, err = readUint16(file)
	if err != nil {
		log.Printf("error reading build: %s", err)
		return Version, err
	}

	return Version, err
}

func readVersionShort8(file io.ReadCloser) (versionShort8, error) {
	var Version versionShort8
	var err error

	Version.Major, err = readUint8(file)
	if err != nil {
		log.Printf("error reading major: %s", err)
		return Version, err
	}

	Version.Minor, err = readUint8(file)
	if err != nil {
		log.Printf("error reading minor: %s", err)
		return Version, err
	}

	Version.Build, err = readUint8(file)
	if err != nil {
		log.Printf("error reading build: %s", err)
		return Version, err
	}

	return Version, nil
}

func readSingleMod(file io.ReadCloser) (singleMod, error) {
	var Mod singleMod
	var err error

	Mod.Name, err = readUTF8String(file, true)
	if err != nil {
		log.Printf("error loading modName: %s", err)
		return Mod, err
	}

	Mod.Version, err = readVersionShort8(file)
	if err != nil {
		log.Printf("error loading modVersion: %s", err)
		return Mod, err
	}

	Constraint, _ := semver.NewConstraint("> 0.15.0")
	Used, err := data.FactorioVersion.CheckCompatibility(Constraint)
	if err != nil {
		log.Printf("Error checking used of CRC: %s", err)
		return Mod, err
	}
	if Used {
		Mod.CRC, err = readUint32(file)
		if err != nil {
			log.Printf("error loading CRC: %s", err)
			return Mod, err
		}
	}

	return Mod, err
}

func (Version *versionShort16) CheckCompatibility(constraints *semver.Constraints) (bool, error) {
	Ver, err := semver.NewVersion(strconv.Itoa(int(Version.Major)) + "." + strconv.Itoa(int(Version.Minor)) + "." + strconv.Itoa(int(Version.Build)))
	if err != nil {
		log.Printf("Error creating semver-version: %s", err)
		return false, err
	}

	return constraints.Check(Ver), nil
}
<|endoftext|>


<commit_before>package conntrack

import (
	"fmt"
	"io"
	"sync"
	"text/tabwriter"
	"time"

	"github.com/anacrolix/missinggo/orderedmap"
)

type reason = string

type Instance struct {
	maxEntries int
	Timeout    func(Entry) time.Duration

	mu                sync.Mutex
	entries           map[Entry]handles
	waitersByPriority orderedmap.OrderedMap
	waitersByReason   map[reason]entryHandleSet
	waitersByEntry    map[Entry][]*EntryHandle
}

type (
	entryHandleSet         = map[*EntryHandle]struct{}
	waitersByPriorityValue = entryHandleSet
	priority               int
	handles                = map[*EntryHandle]struct{}
)

func NewInstance() *Instance {
	i := &Instance{
		maxEntries: 200,
		Timeout: func(e Entry) time.Duration {
			// udp is the main offender, and the default is allegedly 30s.
			return 30 * time.Second
		},
		entries: make(map[Entry]handles),
		waitersByPriority: orderedmap.New(func(_l, _r interface{}) bool {
			return _l.(priority) > _r.(priority)
		}),
		waitersByReason: make(map[reason]entryHandleSet),
		waitersByEntry:  make(map[Entry][]*EntryHandle),
	}
	return i
}

func (i *Instance) SetMaxEntries(max int) {
	i.mu.Lock()
	defer i.mu.Unlock()
	prev := i.maxEntries
	i.maxEntries = max
	for j := prev; j < max; j++ {
		i.wakeOne()
	}
}

func (i *Instance) remove(eh *EntryHandle) {
	i.mu.Lock()
	defer i.mu.Unlock()
	hs := i.entries[eh.e]
	delete(hs, eh)
	if len(hs) == 0 {
		delete(i.entries, eh.e)
		i.wakeOne()
	}
}

func (i *Instance) wakeOne() {
	i.waitersByPriority.Iter(func(key interface{}) bool {
		value := i.waitersByPriority.Get(key).(entryHandleSet)
		for eh := range value {
			i.wakeEntry(eh.e)
			break
		}
		return false
	})
}

func (i *Instance) deleteWaiter(eh *EntryHandle) {
	p := i.waitersByPriority.Get(eh.priority).(entryHandleSet)
	delete(p, eh)
	if len(p) == 0 {
		i.waitersByPriority.Unset(eh.priority)
	}
	r := i.waitersByReason[eh.reason]
	delete(r, eh)
	if len(r) == 0 {
		delete(i.waitersByReason, eh.reason)
	}
}

func (i *Instance) addWaiter(eh *EntryHandle) {
	p, ok := i.waitersByPriority.GetOk(eh.priority)
	if ok {
		p.(entryHandleSet)[eh] = struct{}{}
	} else {
		i.waitersByPriority.Set(eh.priority, entryHandleSet{eh: struct{}{}})
	}
	if r := i.waitersByReason[eh.reason]; r == nil {
		i.waitersByReason[eh.reason] = entryHandleSet{eh: struct{}{}}
	} else {
		r[eh] = struct{}{}
	}
	i.waitersByEntry[eh.e] = append(i.waitersByEntry[eh.e], eh)
}

func (i *Instance) wakeEntry(e Entry) {
	i.entries[e] = make(handles)
	for _, eh := range i.waitersByEntry[e] {
		i.entries[e][eh] = struct{}{}
		i.deleteWaiter(eh)
		eh.added.Unlock()
	}
	delete(i.waitersByEntry, e)
}

func (i *Instance) Wait(e Entry, reason string, p priority) (eh *EntryHandle) {
	eh = &EntryHandle{
		reason:   reason,
		e:        e,
		i:        i,
		priority: p,
	}
	i.mu.Lock()
	hs, ok := i.entries[eh.e]
	if ok {
		hs[eh] = struct{}{}
		i.mu.Unlock()
		expvars.Add("waits for existing entry", 1)
		return
	}
	if len(i.entries) < i.maxEntries {
		i.entries[eh.e] = handles{
			eh: struct{}{},
		}
		i.mu.Unlock()
		expvars.Add("waits with space in table", 1)
		return
	}
	eh.added.Lock()
	i.addWaiter(eh)
	i.mu.Unlock()
	expvars.Add("waits that blocked", 1)
	eh.added.Lock()
	return
}

func (i *Instance) PrintStatus(w io.Writer) {
	tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0)
	i.mu.Lock()
	fmt.Fprintf(w, "num entries: %d\n", len(i.entries))
	fmt.Fprintln(w)
	fmt.Fprintln(w, "waiters:")
	fmt.Fprintf(tw, "num\treason\n")
	for r, ws := range i.waitersByReason {
		fmt.Fprintf(tw, "%d\t%q\n", len(ws), r)
	}
	tw.Flush()
	fmt.Fprintln(w)
	fmt.Fprintln(w, "handles:")
	fmt.Fprintf(tw, "protocol\tlocal\tremote\treason\texpires\n")
	for e, hs := range i.entries {
		for h := range hs {
			fmt.Fprintf(tw,
				"%q\t%q\t%q\t%q\t%s\n",
				e.Protocol, e.LocalAddr, e.RemoteAddr, h.reason,
				func() interface{} {
					if h.expires.IsZero() {
						return "not done"
					} else {
						return time.Until(h.expires)
					}
				}(),
			)
		}
	}
	i.mu.Unlock()
	tw.Flush()
}
<commit_msg>conntrack: Set the default max entries to 1<<14<commit_after>package conntrack

import (
	"fmt"
	"io"
	"sync"
	"text/tabwriter"
	"time"

	"github.com/anacrolix/missinggo/orderedmap"
)

type reason = string

type Instance struct {
	maxEntries int
	Timeout    func(Entry) time.Duration

	mu                sync.Mutex
	entries           map[Entry]handles
	waitersByPriority orderedmap.OrderedMap
	waitersByReason   map[reason]entryHandleSet
	waitersByEntry    map[Entry][]*EntryHandle
}

type (
	entryHandleSet         = map[*EntryHandle]struct{}
	waitersByPriorityValue = entryHandleSet
	priority               int
	handles                = map[*EntryHandle]struct{}
)

func NewInstance() *Instance {
	i := &Instance{
		// A quarter of the commonly quoted absolute max on a Linux system.
		maxEntries: 1 << 14,
		Timeout: func(e Entry) time.Duration {
			// udp is the main offender, and the default is allegedly 30s.
			return 30 * time.Second
		},
		entries: make(map[Entry]handles),
		waitersByPriority: orderedmap.New(func(_l, _r interface{}) bool {
			return _l.(priority) > _r.(priority)
		}),
		waitersByReason: make(map[reason]entryHandleSet),
		waitersByEntry:  make(map[Entry][]*EntryHandle),
	}
	return i
}

func (i *Instance) SetMaxEntries(max int) {
	i.mu.Lock()
	defer i.mu.Unlock()
	prev := i.maxEntries
	i.maxEntries = max
	for j := prev; j < max; j++ {
		i.wakeOne()
	}
}

func (i *Instance) remove(eh *EntryHandle) {
	i.mu.Lock()
	defer i.mu.Unlock()
	hs := i.entries[eh.e]
	delete(hs, eh)
	if len(hs) == 0 {
		delete(i.entries, eh.e)
		i.wakeOne()
	}
}

func (i *Instance) wakeOne() {
	i.waitersByPriority.Iter(func(key interface{}) bool {
		value := i.waitersByPriority.Get(key).(entryHandleSet)
		for eh := range value {
			i.wakeEntry(eh.e)
			break
		}
		return false
	})
}

func (i *Instance) deleteWaiter(eh *EntryHandle) {
	p := i.waitersByPriority.Get(eh.priority).(entryHandleSet)
	delete(p, eh)
	if len(p) == 0 {
		i.waitersByPriority.Unset(eh.priority)
	}
	r := i.waitersByReason[eh.reason]
	delete(r, eh)
	if len(r) == 0 {
		delete(i.waitersByReason, eh.reason)
	}
}

func (i *Instance) addWaiter(eh *EntryHandle) {
	p, ok := i.waitersByPriority.GetOk(eh.priority)
	if ok {
		p.(entryHandleSet)[eh] = struct{}{}
	} else {
		i.waitersByPriority.Set(eh.priority, entryHandleSet{eh: struct{}{}})
	}
	if r := i.waitersByReason[eh.reason]; r == nil {
		i.waitersByReason[eh.reason] = entryHandleSet{eh: struct{}{}}
	} else {
		r[eh] = struct{}{}
	}
	i.waitersByEntry[eh.e] = append(i.waitersByEntry[eh.e], eh)
}

func (i *Instance) wakeEntry(e Entry) {
	i.entries[e] = make(handles)
	for _, eh := range i.waitersByEntry[e] {
		i.entries[e][eh] = struct{}{}
		i.deleteWaiter(eh)
		eh.added.Unlock()
	}
	delete(i.waitersByEntry, e)
}

func (i *Instance) Wait(e Entry, reason string, p priority) (eh *EntryHandle) {
	eh = &EntryHandle{
		reason:   reason,
		e:        e,
		i:        i,
		priority: p,
	}
	i.mu.Lock()
	hs, ok := i.entries[eh.e]
	if ok {
		hs[eh] = struct{}{}
		i.mu.Unlock()
		expvars.Add("waits for existing entry", 1)
		return
	}
	if len(i.entries) < i.maxEntries {
		i.entries[eh.e] = handles{
			eh: struct{}{},
		}
		i.mu.Unlock()
		expvars.Add("waits with space in table", 1)
		return
	}
	eh.added.Lock()
	i.addWaiter(eh)
	i.mu.Unlock()
	expvars.Add("waits that blocked", 1)
	eh.added.Lock()
	return
}

func (i *Instance) PrintStatus(w io.Writer) {
	tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0)
	i.mu.Lock()
	fmt.Fprintf(w, "num entries: %d\n", len(i.entries))
	fmt.Fprintln(w)
	fmt.Fprintln(w, "waiters:")
	fmt.Fprintf(tw, "num\treason\n")
	for r, ws := range i.waitersByReason {
		fmt.Fprintf(tw, "%d\t%q\n", len(ws), r)
	}
	tw.Flush()
	fmt.Fprintln(w)
	fmt.Fprintln(w, "handles:")
	fmt.Fprintf(tw, "protocol\tlocal\tremote\treason\texpires\n")
	for e, hs := range i.entries {
		for h := range hs {
			fmt.Fprintf(tw,
				"%q\t%q\t%q\t%q\t%s\n",
				e.Protocol, e.LocalAddr, e.RemoteAddr, h.reason,
				func() interface{} {
					if h.expires.IsZero() {
						return "not done"
					} else {
						return time.Until(h.expires)
					}
				}(),
			)
		}
	}
	i.mu.Unlock()
	tw.Flush()
}
<|endoftext|>


<commit_before>package consensus

import (
	"github.com/FactomProject/btcd/wire"
	"sync"
)

// Process list contains a list of valid confirmation messages
// and is used for consensus building
type ProcessListMgr struct {
	sync.RWMutex
	MyProcessList     *ProcessList
	OtherProcessLists []*ProcessList

	NextDBlockHeight uint32

	// Orphan process list map to hold our of order confirmation messages
	// key: MsgAcknowledgement.MsgHash.String()
	OrphanPLMap map[string]*ProcessListItem
}

// create a new process list
func NewProcessListMgr(height uint32, otherPLSize int, plSizeHint uint) *ProcessListMgr {

	plMgr := new(ProcessListMgr)
	plMgr.MyProcessList = NewProcessList(plSizeHint)
	plMgr.OtherProcessLists = make([]*ProcessList, otherPLSize, otherPLSize)
	for i := 0; i < len(plMgr.OtherProcessLists); i++ {
		plMgr.OtherProcessLists[i] = NewProcessList(plSizeHint)
	}
	plMgr.NextDBlockHeight = height

	return plMgr
}

// Add a ProcessListItem into the corresponding process list
/*func (plMgr *ProcessListMgr) AddToProcessList(plItem *ProcessListItem) error {

	// If the item belongs to my process list
	if plItem.Ack == nil {
		plMgr.AddToMyProcessList(plItem)
	} else {
		plMgr.AddToOtherProcessList(plItem)
	}

	return nil
}*/

//Added to OtherPL[0] - to be improved after milestone 1??
func (plMgr *ProcessListMgr) AddToOtherProcessList(plItem *ProcessListItem) error {
	// Determin which process list to add
	plMgr.OtherProcessLists[0].AddToProcessList(plItem)
	return nil
}

//Added to OtherPL[0] - to be improved after milestone 1??
func (plMgr *ProcessListMgr) AddToOrphanProcessList(plItem *ProcessListItem) error {
	// Determin which process list to add
	//	plMgr.OrphanPLMap[string(plItem.ack.Affirmation)] = plItem
	return nil
}

// Add a factom transaction to the my process list
// Each of the federated servers has one MyProcessList
/*func (plMgr *ProcessListMgr) AddToMyProcessList(plItem *ProcessListItem, msgType byte) error {

	// Come up with the right process list index for the new item
	index := uint32(len(plMgr.MyProcessList.plItems))
	if index > 0 {
		lastPlItem := plMgr.MyProcessList.plItems[index-1]
		if lastPlItem.Ack == nil {
			return errors.New("Invalid process list.")
		}
		if index != lastPlItem.Ack.Index+1 {
			return errors.New("Invalid process list index.")
		}
	}
	msgAck := wire.NewMsgAcknowledgement(plMgr.NextDBlockHeight, index, plItem.MsgHash, msgType)

	//msgAck.Affirmation = plItem.msgHash.Bytes
	plItem.Ack = msgAck

	//Add the item into my process list
	plMgr.MyProcessList.AddToProcessList(plItem)

	//Broadcast the plitem into the network??

	return nil
}
*/
// Initialize the process list from the orphan process list map
// Out of order Ack messages are stored in OrphanPLMap
func (plMgr *ProcessListMgr) InitProcessListFromOrphanMap() error {

	for key, plItem := range plMgr.OrphanPLMap {
		if uint64(plMgr.NextDBlockHeight) == plItem.Ack.Height {//??
			plMgr.MyProcessList.AddToProcessList(plItem)
			delete(plMgr.OrphanPLMap, key)
		}

	}

	return nil
}

// Create a new process list item and add it to the MyProcessList
func (plMgr *ProcessListMgr) AddMyProcessListItem(msg wire.FtmInternalMsg, hash *wire.ShaHash, msgType byte) error {

	ack := wire.NewMsgAcknowledgement(uint64(plMgr.NextDBlockHeight), uint32(plMgr.MyProcessList.nextIndex), hash, msgType) //??
	plMgr.MyProcessList.nextIndex++

	plItem := &ProcessListItem{
		Ack:     ack,
		Msg:     msg,
		MsgHash: hash,
	}
	plMgr.MyProcessList.AddToProcessList(plItem)

	return nil
}
<commit_msg>Changed block height in ack to 4 bytes<commit_after>package consensus

import (
	"github.com/FactomProject/btcd/wire"
	"sync"
)

// Process list contains a list of valid confirmation messages
// and is used for consensus building
type ProcessListMgr struct {
	sync.RWMutex
	MyProcessList     *ProcessList
	OtherProcessLists []*ProcessList

	NextDBlockHeight uint32

	// Orphan process list map to hold our of order confirmation messages
	// key: MsgAcknowledgement.MsgHash.String()
	OrphanPLMap map[string]*ProcessListItem
}

// create a new process list
func NewProcessListMgr(height uint32, otherPLSize int, plSizeHint uint) *ProcessListMgr {

	plMgr := new(ProcessListMgr)
	plMgr.MyProcessList = NewProcessList(plSizeHint)
	plMgr.OtherProcessLists = make([]*ProcessList, otherPLSize, otherPLSize)
	for i := 0; i < len(plMgr.OtherProcessLists); i++ {
		plMgr.OtherProcessLists[i] = NewProcessList(plSizeHint)
	}
	plMgr.NextDBlockHeight = height

	return plMgr
}

// Add a ProcessListItem into the corresponding process list
/*func (plMgr *ProcessListMgr) AddToProcessList(plItem *ProcessListItem) error {

	// If the item belongs to my process list
	if plItem.Ack == nil {
		plMgr.AddToMyProcessList(plItem)
	} else {
		plMgr.AddToOtherProcessList(plItem)
	}

	return nil
}*/

//Added to OtherPL[0] - to be improved after milestone 1??
func (plMgr *ProcessListMgr) AddToOtherProcessList(plItem *ProcessListItem) error {
	// Determin which process list to add
	plMgr.OtherProcessLists[0].AddToProcessList(plItem)
	return nil
}

//Added to OtherPL[0] - to be improved after milestone 1??
func (plMgr *ProcessListMgr) AddToOrphanProcessList(plItem *ProcessListItem) error {
	// Determin which process list to add
	//	plMgr.OrphanPLMap[string(plItem.ack.Affirmation)] = plItem
	return nil
}

// Add a factom transaction to the my process list
// Each of the federated servers has one MyProcessList
/*func (plMgr *ProcessListMgr) AddToMyProcessList(plItem *ProcessListItem, msgType byte) error {

	// Come up with the right process list index for the new item
	index := uint32(len(plMgr.MyProcessList.plItems))
	if index > 0 {
		lastPlItem := plMgr.MyProcessList.plItems[index-1]
		if lastPlItem.Ack == nil {
			return errors.New("Invalid process list.")
		}
		if index != lastPlItem.Ack.Index+1 {
			return errors.New("Invalid process list index.")
		}
	}
	msgAck := wire.NewMsgAcknowledgement(plMgr.NextDBlockHeight, index, plItem.MsgHash, msgType)

	//msgAck.Affirmation = plItem.msgHash.Bytes
	plItem.Ack = msgAck

	//Add the item into my process list
	plMgr.MyProcessList.AddToProcessList(plItem)

	//Broadcast the plitem into the network??

	return nil
}
*/
// Initialize the process list from the orphan process list map
// Out of order Ack messages are stored in OrphanPLMap
func (plMgr *ProcessListMgr) InitProcessListFromOrphanMap() error {

	for key, plItem := range plMgr.OrphanPLMap {
		if plMgr.NextDBlockHeight == plItem.Ack.Height {//??
			plMgr.MyProcessList.AddToProcessList(plItem)
			delete(plMgr.OrphanPLMap, key)
		}

	}

	return nil
}

// Create a new process list item and add it to the MyProcessList
func (plMgr *ProcessListMgr) AddMyProcessListItem(msg wire.FtmInternalMsg, hash *wire.ShaHash, msgType byte) error {

	ack := wire.NewMsgAcknowledgement(plMgr.NextDBlockHeight, uint32(plMgr.MyProcessList.nextIndex), hash, msgType) //??
	plMgr.MyProcessList.nextIndex++

	plItem := &ProcessListItem{
		Ack:     ack,
		Msg:     msg,
		MsgHash: hash,
	}
	plMgr.MyProcessList.AddToProcessList(plItem)

	return nil
}
<|endoftext|>


<commit_before>package consul

import consulapi "github.com/hashicorp/consul/api"
import "time"
import "fmt"
import "math/rand"
import "github.com/zaunerc/cntrinfod/system"
import "github.com/zaunerc/cntrinfod/docker"
import "strconv"

var consulClient *consulapi.Client

func createConsulClient(consulUrl string) (*consulapi.Client, error) {
	config := consulapi.DefaultConfig()
	config.Address = consulUrl
	consul, err := consulapi.NewClient(config)

	return consul, err
}

/**
 * ScheduleRegistration return immediately after the
 * container registration job is scheduled.
 */
func ScheduleRegistration(consulUrl string, cntrInfodHttpPort int) {

	consulClient, err := createConsulClient(consulUrl)
	if err != nil {
		fmt.Printf("Disabling consul registration. Error while trying to create consul HTTP client: %s", err)
	} else {
		serviceId := RandStringBytesMaskImprSrc(8)
		fmt.Printf("Scheduling registration task using consul URL >%s< and service id >%s<.\n", consulUrl, serviceId)
		go registerContainer(consulClient, cntrInfodHttpPort, 5, serviceId)
	}
}

func registerContainer(consul *consulapi.Client, cntrInfodHttpPort int, sleepSeconds int, serviceId string) {
	firstIteration := true
	for {

		if firstIteration {
			firstIteration = false
		} else {
			time.Sleep(time.Duration(sleepSeconds) * time.Second)
		}

		fmt.Printf("Registering container...\n")

		kv := consul.KV()

		// cntrInfodUrl
		cntrInfodHttpUrl := "http://" + system.FetchContainerHostname() + ":" + strconv.Itoa(cntrInfodHttpPort)
		data := &consulapi.KVPair{Key: "containers/" + serviceId + "/cntrInfodHttpUrl",
			Value: []byte(cntrInfodHttpUrl)}
		_, err := kv.Put(data, nil)
		if err != nil {
			fmt.Printf("Error while trying to register container: %s\n", err)
			continue
		}

		// MAC
		macAdress := system.FetchFirstMac()
		data = &consulapi.KVPair{Key: "containers/" + serviceId + "/macAdress",
			Value: []byte(macAdress)}
		_, err = kv.Put(data, nil)
		if err != nil {
			fmt.Printf("Error while trying to register container: %s\n", err)
			continue
		}

		// IP Adress
		ipAdress := system.FetchFirstIp()
		data = &consulapi.KVPair{Key: "containers/" + serviceId + "/ipAdress",
			Value: []byte(ipAdress)}
		_, err = kv.Put(data, nil)
		if err != nil {
			fmt.Printf("Error while trying to register container: %s\n", err)
			continue
		}

		// Unix Epoch Timestamp
		unixEpochTimestamp := strconv.FormatInt(time.Now().Unix(), 10)
		data = &consulapi.KVPair{Key: "containers/" + serviceId + "/unixEpochTimestamp",
			Value: []byte(unixEpochTimestamp)}
		_, err = kv.Put(data, nil)
		if err != nil {
			fmt.Printf("Error while trying to register container: %s\n", err)
			continue
		}

		// Hostname
		hostname := system.FetchContainerHostname()
		data = &consulapi.KVPair{Key: "containers/" + serviceId + "/hostname",
			Value: []byte(hostname)}
		_, err = kv.Put(data, nil)
		if err != nil {
			fmt.Printf("Error while trying to register container: %s\n", err)
			continue
		}

		// HostHostname
		hostHostname := docker.FetchHostHostname()
		data = &consulapi.KVPair{Key: "containers/" + serviceId + "/hostinfo/hostname",
			Value: []byte(hostHostname)}
		_, err = kv.Put(data, nil)
		if err != nil {
			fmt.Printf("Error while trying to register container: %s\n", err)
			continue
		}

		fmt.Printf("Successfully registered container. Next registration in %d seconds...\n", sleepSeconds)
	}
}

/*
 * RandStringBytesMaskImprSrc acquires a new seed each time it is called.
 * Inspired by http://stackoverflow.com/a/31832326/6551760.
 */
func RandStringBytesMaskImprSrc(n int) string {

	var src = rand.NewSource(time.Now().UnixNano())

	const letterBytes = "abcdefghijklmnopqrstuvwxyz"
	const (
		letterIdxBits = 6                    // 6 bits to represent a letter index
		letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
		letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
	)

	b := make([]byte, n)
	// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
	for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
		if remain == 0 {
			cache, remain = src.Int63(), letterIdxMax
		}
		if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
			b[i] = letterBytes[idx]
			i--
		}
		cache >>= letterIdxBits
		remain--
	}

	return string(b)
}
<commit_msg>Use consul commons package.<commit_after>package consul

import consulapi "github.com/zaunerc/consul/api"
import "time"
import "fmt"
import "math/rand"
import "github.com/zaunerc/cntrinfod/system"
import "github.com/zaunerc/cntrinfod/docker"
import cc "github.com/zaunerc/go_consul_commons"
import "strconv"

var consulClient *consulapi.Client

func createConsulClient(consulUrl string) (*consulapi.Client, error) {
	config := consulapi.DefaultConfig()
	config.Address = consulUrl
	consul, err := consulapi.NewClient(config)

	return consul, err
}

/**
 * ScheduleRegistration return immediately after the
 * container registration job is scheduled.
 */
func ScheduleRegistration(consulUrl string, cntrInfodHttpPort int) {
	serviceId := RandStringBytesMaskImprSrc(8)
	fmt.Printf("Scheduling registration task using consul URL >%s< and service id >%s<.\n", consulUrl, serviceId)
	go registerContainer(consulUrl, cntrInfodHttpPort, 5, serviceId)
}

func registerContainer(consulUrl string, cntrInfodHttpPort int, sleepSeconds int, serviceId string) {
	firstIteration := true
	for {

		if firstIteration {
			firstIteration = false
		} else {
			time.Sleep(time.Duration(sleepSeconds) * time.Second)
		}

		fmt.Printf("Registering container...\n")

		consulClient, err := cc.GetConsulClientForUrl(consulUrl)
		if err != nil {
			fmt.Printf("Skipping current consul registration. Error while trying to get consul HTTP client: %s", err)
			continue
		}

		kv := consulClient.KV()

		// cntrInfodUrl
		cntrInfodHttpUrl := "http://" + system.FetchContainerHostname() + ":" + strconv.Itoa(cntrInfodHttpPort)
		data := &consulapi.KVPair{Key: "containers/" + serviceId + "/cntrInfodHttpUrl",
			Value: []byte(cntrInfodHttpUrl)}
		_, err = kv.Put(data, nil)
		if err != nil {
			fmt.Printf("Error while trying to register container: %s\n", err)
			continue
		}

		// MAC
		macAdress := system.FetchFirstMac()
		data = &consulapi.KVPair{Key: "containers/" + serviceId + "/macAdress",
			Value: []byte(macAdress)}
		_, err = kv.Put(data, nil)
		if err != nil {
			fmt.Printf("Error while trying to register container: %s\n", err)
			continue
		}

		// IP Adress
		ipAdress := system.FetchFirstIp()
		data = &consulapi.KVPair{Key: "containers/" + serviceId + "/ipAdress",
			Value: []byte(ipAdress)}
		_, err = kv.Put(data, nil)
		if err != nil {
			fmt.Printf("Error while trying to register container: %s\n", err)
			continue
		}

		// Unix Epoch Timestamp
		unixEpochTimestamp := strconv.FormatInt(time.Now().Unix(), 10)
		data = &consulapi.KVPair{Key: "containers/" + serviceId + "/unixEpochTimestamp",
			Value: []byte(unixEpochTimestamp)}
		_, err = kv.Put(data, nil)
		if err != nil {
			fmt.Printf("Error while trying to register container: %s\n", err)
			continue
		}

		// Hostname
		hostname := system.FetchContainerHostname()
		data = &consulapi.KVPair{Key: "containers/" + serviceId + "/hostname",
			Value: []byte(hostname)}
		_, err = kv.Put(data, nil)
		if err != nil {
			fmt.Printf("Error while trying to register container: %s\n", err)
			continue
		}

		// HostHostname
		hostHostname := docker.FetchHostHostname()
		data = &consulapi.KVPair{Key: "containers/" + serviceId + "/hostinfo/hostname",
			Value: []byte(hostHostname)}
		_, err = kv.Put(data, nil)
		if err != nil {
			fmt.Printf("Error while trying to register container: %s\n", err)
			continue
		}

		fmt.Printf("Successfully registered container. Next registration in %d seconds...\n", sleepSeconds)
	}
}

/*
 * RandStringBytesMaskImprSrc acquires a new seed each time it is called.
 * Inspired by http://stackoverflow.com/a/31832326/6551760.
 */
func RandStringBytesMaskImprSrc(n int) string {

	var src = rand.NewSource(time.Now().UnixNano())

	const letterBytes = "abcdefghijklmnopqrstuvwxyz"
	const (
		letterIdxBits = 6                    // 6 bits to represent a letter index
		letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
		letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
	)

	b := make([]byte, n)
	// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
	for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
		if remain == 0 {
			cache, remain = src.Int63(), letterIdxMax
		}
		if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
			b[i] = letterBytes[idx]
			i--
		}
		cache >>= letterIdxBits
		remain--
	}

	return string(b)
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// Functions defined in this file should *ONLY* be used for testing.  These
// functions are exported for testing purposes only, and shouldn't be called
// from code that isn't in a test file.

package lxc

import (
	gc "launchpad.net/gocheck"
	"launchpad.net/golxc"

	"launchpad.net/juju-core/container/lxc/mock"
	"launchpad.net/juju-core/testing/testbase"
)

// SetContainerDir allows tests in other packages to override the
// containerDir.
func SetContainerDir(dir string) (old string) {
	old, containerDir = containerDir, dir
	return
}

// SetLxcContainerDir allows tests in other packages to override the
// lxcContainerDir.
func SetLxcContainerDir(dir string) (old string) {
	old, lxcContainerDir = lxcContainerDir, dir
	return
}

// SetLxcRestartDir allows tests in other packages to override the
// lxcRestartDir, which contains the symlinks to the config files so
// containers can be auto-restarted on reboot.
func SetLxcRestartDir(dir string) (old string) {
	old, lxcRestartDir = lxcRestartDir, dir
	return
}

// SetRemovedContainerDir allows tests in other packages to override the
// removedContainerDir.
func SetRemovedContainerDir(dir string) (old string) {
	old, removedContainerDir = removedContainerDir, dir
	return
}

// SetLxcFactory allows tests in other packages to override the lxcObjectFactory
func SetLxcFactory(factory golxc.ContainerFactory) (old golxc.ContainerFactory) {
	logger.Infof("lxcObjectFactory replaced with %v", factory)
	old, lxcObjectFactory = lxcObjectFactory, factory
	return
}

// TestSuite replaces the lxc factory that the broker uses with a mock
// implementation.
type TestSuite struct {
	testbase.LoggingSuite
	Factory      mock.ContainerFactory
	oldFactory   golxc.ContainerFactory
	ContainerDir string
	RemovedDir   string
	LxcDir       string
	RestartDir   string
}

func (s *TestSuite) SetUpTest(c *gc.C) {
	s.LoggingSuite.SetUpTest(c)
	s.ContainerDir = c.MkDir()
	s.PatchValue(&containerDir, s.ContainerDir)
	s.RemovedDir = c.MkDir()
	s.PatchValue(&removedContainerDir, s.RemovedDir)
	s.LxcDir = c.MkDir()
	s.PatchValue(&lxcContainerDir, s.LxcDir)
	s.RestartDir = c.MkDir()
	s.PatchValue(&lxcRestartDir, s.RestartDir)
	s.Factory = mock.MockFactory()
	s.PatchValue(&lxcObjectFactory, s.Factory)
}
<commit_msg>Remove methods only used in this file.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// Functions defined in this file should *ONLY* be used for testing.  These
// functions are exported for testing purposes only, and shouldn't be called
// from code that isn't in a test file.

package lxc

import (
	gc "launchpad.net/gocheck"
	"launchpad.net/golxc"

	"launchpad.net/juju-core/container/lxc/mock"
	"launchpad.net/juju-core/testing/testbase"
)

// TestSuite replaces the lxc factory that the broker uses with a mock
// implementation.
type TestSuite struct {
	testbase.LoggingSuite
	Factory      mock.ContainerFactory
	oldFactory   golxc.ContainerFactory
	ContainerDir string
	RemovedDir   string
	LxcDir       string
	RestartDir   string
}

func (s *TestSuite) SetUpTest(c *gc.C) {
	s.LoggingSuite.SetUpTest(c)
	s.ContainerDir = c.MkDir()
	s.PatchValue(&containerDir, s.ContainerDir)
	s.RemovedDir = c.MkDir()
	s.PatchValue(&removedContainerDir, s.RemovedDir)
	s.LxcDir = c.MkDir()
	s.PatchValue(&lxcContainerDir, s.LxcDir)
	s.RestartDir = c.MkDir()
	s.PatchValue(&lxcRestartDir, s.RestartDir)
	s.Factory = mock.MockFactory()
	s.PatchValue(&lxcObjectFactory, s.Factory)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/jackc/pgx"
	"github.com/flynn/flynn/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/flynn/flynn/controller/schema"
	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/flynn/host/resource"
	"github.com/flynn/flynn/pkg/httphelper"
	"github.com/flynn/flynn/pkg/postgres"
	"github.com/flynn/flynn/pkg/random"
)

type ReleaseRepo struct {
	db *postgres.DB
}

func NewReleaseRepo(db *postgres.DB) *ReleaseRepo {
	return &ReleaseRepo{db}
}

func scanRelease(s postgres.Scanner) (*ct.Release, error) {
	var artifactID *string
	release := &ct.Release{}
	err := s.Scan(&release.ID, &artifactID, &release.Env, &release.Processes, &release.Meta, &release.CreatedAt)
	if err != nil {
		if err == pgx.ErrNoRows {
			err = ErrNotFound
		}
		return nil, err
	}
	if artifactID != nil {
		release.ArtifactID = *artifactID
	}
	return release, err
}

func (r *ReleaseRepo) Add(data interface{}) error {
	release := data.(*ct.Release)
	releaseCopy := *release

	releaseCopy.ID = ""
	releaseCopy.ArtifactID = ""
	releaseCopy.CreatedAt = nil
	releaseCopy.Meta = nil

	for typ, proc := range releaseCopy.Processes {
		resource.SetDefaults(&proc.Resources)
		releaseCopy.Processes[typ] = proc
	}

	if release.ID == "" {
		release.ID = random.UUID()
	}

	var artifactID *string
	if release.ArtifactID != "" {
		artifactID = &release.ArtifactID
	}

	tx, err := r.db.Begin()
	if err != nil {
		return err
	}

	err = tx.QueryRow("release_insert", release.ID, artifactID, release.Env, release.Processes, release.Meta).Scan(&release.CreatedAt)
	if err != nil {
		tx.Rollback()
		return err
	}

	if err := createEvent(tx.Exec, &ct.Event{
		ObjectID:   release.ID,
		ObjectType: ct.EventTypeRelease,
	}, release); err != nil {
		tx.Rollback()
		return err
	}

	return tx.Commit()
}

func (r *ReleaseRepo) Get(id string) (interface{}, error) {
	row := r.db.QueryRow("release_select", id)
	return scanRelease(row)
}

func releaseList(rows *pgx.Rows) ([]*ct.Release, error) {
	var releases []*ct.Release
	for rows.Next() {
		release, err := scanRelease(rows)
		if err != nil {
			rows.Close()
			return nil, err
		}
		releases = append(releases, release)
	}
	return releases, rows.Err()
}

func (r *ReleaseRepo) List() (interface{}, error) {
	rows, err := r.db.Query("release_list")
	if err != nil {
		return nil, err
	}
	return releaseList(rows)
}

func (r *ReleaseRepo) AppList(appID string) ([]*ct.Release, error) {
	rows, err := r.db.Query(`release_app_list`, appID)
	if err != nil {
		return nil, err
	}
	return releaseList(rows)
}

type releaseID struct {
	ID string `json:"id"`
}

func (c *controllerAPI) GetAppReleases(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	list, err := c.releaseRepo.AppList(c.getApp(ctx).ID)
	if err != nil {
		respondWithError(w, err)
		return
	}
	httphelper.JSON(w, 200, list)
}

func (c *controllerAPI) SetAppRelease(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	var rid releaseID
	if err := httphelper.DecodeJSON(req, &rid); err != nil {
		respondWithError(w, err)
		return
	}

	rel, err := c.releaseRepo.Get(rid.ID)
	if err != nil {
		if err == ErrNotFound {
			err = ct.ValidationError{
				Message: fmt.Sprintf("could not find release with ID %s", rid.ID),
			}
		}
		respondWithError(w, err)
		return
	}
	release := rel.(*ct.Release)

	if err := schema.Validate(release); err != nil {
		respondWithError(w, err)
		return
	}

	app := c.getApp(ctx)
	c.appRepo.SetRelease(app, release.ID)
	httphelper.JSON(w, 200, release)
}

func (c *controllerAPI) GetAppRelease(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	release, err := c.appRepo.GetRelease(c.getApp(ctx).ID)
	if err != nil {
		respondWithError(w, err)
		return
	}
	httphelper.JSON(w, 200, release)
}
<commit_msg>controller: Remove unused releaseCopy variable<commit_after>package main

import (
	"fmt"
	"net/http"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/jackc/pgx"
	"github.com/flynn/flynn/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/flynn/flynn/controller/schema"
	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/flynn/host/resource"
	"github.com/flynn/flynn/pkg/httphelper"
	"github.com/flynn/flynn/pkg/postgres"
	"github.com/flynn/flynn/pkg/random"
)

type ReleaseRepo struct {
	db *postgres.DB
}

func NewReleaseRepo(db *postgres.DB) *ReleaseRepo {
	return &ReleaseRepo{db}
}

func scanRelease(s postgres.Scanner) (*ct.Release, error) {
	var artifactID *string
	release := &ct.Release{}
	err := s.Scan(&release.ID, &artifactID, &release.Env, &release.Processes, &release.Meta, &release.CreatedAt)
	if err != nil {
		if err == pgx.ErrNoRows {
			err = ErrNotFound
		}
		return nil, err
	}
	if artifactID != nil {
		release.ArtifactID = *artifactID
	}
	return release, err
}

func (r *ReleaseRepo) Add(data interface{}) error {
	release := data.(*ct.Release)

	for typ, proc := range release.Processes {
		resource.SetDefaults(&proc.Resources)
		release.Processes[typ] = proc
	}

	if release.ID == "" {
		release.ID = random.UUID()
	}

	var artifactID *string
	if release.ArtifactID != "" {
		artifactID = &release.ArtifactID
	}

	tx, err := r.db.Begin()
	if err != nil {
		return err
	}

	err = tx.QueryRow("release_insert", release.ID, artifactID, release.Env, release.Processes, release.Meta).Scan(&release.CreatedAt)
	if err != nil {
		tx.Rollback()
		return err
	}

	if err := createEvent(tx.Exec, &ct.Event{
		ObjectID:   release.ID,
		ObjectType: ct.EventTypeRelease,
	}, release); err != nil {
		tx.Rollback()
		return err
	}

	return tx.Commit()
}

func (r *ReleaseRepo) Get(id string) (interface{}, error) {
	row := r.db.QueryRow("release_select", id)
	return scanRelease(row)
}

func releaseList(rows *pgx.Rows) ([]*ct.Release, error) {
	var releases []*ct.Release
	for rows.Next() {
		release, err := scanRelease(rows)
		if err != nil {
			rows.Close()
			return nil, err
		}
		releases = append(releases, release)
	}
	return releases, rows.Err()
}

func (r *ReleaseRepo) List() (interface{}, error) {
	rows, err := r.db.Query("release_list")
	if err != nil {
		return nil, err
	}
	return releaseList(rows)
}

func (r *ReleaseRepo) AppList(appID string) ([]*ct.Release, error) {
	rows, err := r.db.Query(`release_app_list`, appID)
	if err != nil {
		return nil, err
	}
	return releaseList(rows)
}

type releaseID struct {
	ID string `json:"id"`
}

func (c *controllerAPI) GetAppReleases(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	list, err := c.releaseRepo.AppList(c.getApp(ctx).ID)
	if err != nil {
		respondWithError(w, err)
		return
	}
	httphelper.JSON(w, 200, list)
}

func (c *controllerAPI) SetAppRelease(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	var rid releaseID
	if err := httphelper.DecodeJSON(req, &rid); err != nil {
		respondWithError(w, err)
		return
	}

	rel, err := c.releaseRepo.Get(rid.ID)
	if err != nil {
		if err == ErrNotFound {
			err = ct.ValidationError{
				Message: fmt.Sprintf("could not find release with ID %s", rid.ID),
			}
		}
		respondWithError(w, err)
		return
	}
	release := rel.(*ct.Release)

	if err := schema.Validate(release); err != nil {
		respondWithError(w, err)
		return
	}

	app := c.getApp(ctx)
	c.appRepo.SetRelease(app, release.ID)
	httphelper.JSON(w, 200, release)
}

func (c *controllerAPI) GetAppRelease(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	release, err := c.appRepo.GetRelease(c.getApp(ctx).ID)
	if err != nil {
		respondWithError(w, err)
		return
	}
	httphelper.JSON(w, 200, release)
}
<|endoftext|>


<commit_before>/**
Copyright (c) 2015 The ConnectorDB Contributors (see AUTHORS)
Licensed under the MIT license.
**/
package website

import (
	"html/template"
	"io"
	"io/ioutil"
	"path"
	"sync"

	"github.com/kardianos/osext"
	"gopkg.in/fsnotify.v1"

	log "github.com/Sirupsen/logrus"
)

var (
	//The prefix to use for the paths in web server
	WWWPrefix = "www"
	AppPrefix = "app"

	// WWWPath is the path to the not-logged-in website files
	WWWPath string
	// AppPath is the path to the logged-in user website files
	AppPath string

	//These are the pre-loaded templates for non-logged in users
	WWWIndex *FileTemplate
	WWWLogin *FileTemplate
	WWWJoin  *FileTemplate
	WWW404   *FileTemplate

	//These are the pre-loaded templates for logged in users
	AppIndex  *FileTemplate
	AppUser   *FileTemplate
	AppDevice *FileTemplate
	AppStream *FileTemplate
	App404    *FileTemplate
	AppError  *FileTemplate
)

//FileTemplate implements all the necessary logic to read/write a "special" templated file
// as well as to update it from the folder in real time as it is modified.
type FileTemplate struct {
	sync.RWMutex //RWMutex allows for writing the template during runtime

	FilePath string
	Template *template.Template

	Watcher *fsnotify.Watcher
	done    chan bool
}

//NewFileTemplate loads a template from file and subscribes to changes from the file system
func NewFileTemplate(fpath string, err error) (*FileTemplate, error) {
	if err != nil {
		return nil, err
	}
	f, err := ioutil.ReadFile(fpath)
	if err != nil {
		log.Warnf("Could not read '%s'", fpath)
		return nil, err
	}
	tmpl, err := template.New(fpath).Parse(string(f))
	if err != nil {
		log.Warnf("Failed to parse '%s'", fpath)
		return nil, err
	}

	watch, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}

	err = watch.Add(fpath)
	if err != nil {
		watch.Close()
	}

	done := make(chan bool)

	ft := &FileTemplate{
		RWMutex:  sync.RWMutex{},
		FilePath: fpath,
		Template: tmpl,
		Watcher:  watch,
		done:     done,
	}

	//Run the file watch in the background
	go ft.Watch()

	return ft, nil
}

//Watch is run in the background to watch for changes in the template files
func (f *FileTemplate) Watch() {
	for {
		select {
		case event := <-f.Watcher.Events:
			if event.Op&fsnotify.Write == fsnotify.Write {
				//We reload the file
				log.Infof("Reloading file: '%s'", f.FilePath)
				file, err := ioutil.ReadFile(f.FilePath)
				if err != nil {
					log.Errorf("Could not read '%s'", f.FilePath)
				} else {
					tmpl, err := template.New(f.FilePath).Parse(string(file))
					if err != nil {
						log.Errorf("Failed to parse '%s'", f.FilePath)
					} else {
						f.Lock()
						f.Template = tmpl
						f.Unlock()
					}
				}
			}
		case err := <-f.Watcher.Errors:
			log.Printf("Watcher for '%s' failed: %s", f.FilePath, err.Error())
			return
		case <-f.done:
			return
		}
	}
}

//Execute the template
func (f *FileTemplate) Execute(w io.Writer, data interface{}) error {
	f.RLock()
	err := f.Template.Execute(w, data)
	f.RUnlock()
	return err
}

// Close shuts down the file template
func (f *FileTemplate) Close() {
	f.Watcher.Close()
}

//LoadFiles sets up all the necessary files
func LoadFiles() error {
	//Now set up the app and www folder paths and make sure they exist
	exefolder, err := osext.ExecutableFolder()
	if err != nil {
		return err
	}
	WWWPath = path.Join(exefolder, WWWPrefix)
	log.Debugf("Hosting www from '%s'", WWWPath)

	AppPath = path.Join(exefolder, AppPrefix)
	log.Debugf("Hosting app from '%s'", AppPath)

	WWWIndex, err = NewFileTemplate(path.Join(WWWPath, "index.html"), err)
	WWWLogin, err = NewFileTemplate(path.Join(WWWPath, "login.html"), err)
	WWW404, err = NewFileTemplate(path.Join(WWWPath, "404.html"), err)
	WWWJoin, err = NewFileTemplate(path.Join(WWWPath, "join.html"), err)

	AppIndex, err = NewFileTemplate(path.Join(AppPath, "index.html"), err)
	AppUser, err = NewFileTemplate(path.Join(AppPath, "user.html"), err)
	AppDevice, err = NewFileTemplate(path.Join(AppPath, "device.html"), err)
	AppStream, err = NewFileTemplate(path.Join(AppPath, "stream.html"), err)
	App404, err = NewFileTemplate(path.Join(AppPath, "404.html"), err)
	AppError, err = NewFileTemplate(path.Join(AppPath, "error.html"), err)

	return err
}
<commit_msg>Fixed template reload<commit_after>/**
Copyright (c) 2015 The ConnectorDB Contributors (see AUTHORS)
Licensed under the MIT license.
**/
package website

import (
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"os"
	"path"
	"sync"
	"time"

	"github.com/kardianos/osext"
	"gopkg.in/fsnotify.v1"

	log "github.com/Sirupsen/logrus"
)

var (
	//The prefix to use for the paths in web server
	WWWPrefix = "www"
	AppPrefix = "app"

	// WWWPath is the path to the not-logged-in website files
	WWWPath string
	// AppPath is the path to the logged-in user website files
	AppPath string

	//These are the pre-loaded templates for non-logged in users
	WWWIndex *FileTemplate
	WWWLogin *FileTemplate
	WWWJoin  *FileTemplate
	WWW404   *FileTemplate

	//These are the pre-loaded templates for logged in users
	AppIndex  *FileTemplate
	AppUser   *FileTemplate
	AppDevice *FileTemplate
	AppStream *FileTemplate
	App404    *FileTemplate
	AppError  *FileTemplate
)

//FileTemplate implements all the necessary logic to read/write a "special" templated file
// as well as to update it from the folder in real time as it is modified.
type FileTemplate struct {
	sync.RWMutex //RWMutex allows for writing the template during runtime

	FilePath string
	Template *template.Template

	Watcher *fsnotify.Watcher
	done    chan bool
}

//NewFileTemplate loads a template from file and subscribes to changes from the file system
func NewFileTemplate(fpath string, err error) (*FileTemplate, error) {
	if err != nil {
		return nil, err
	}
	f, err := ioutil.ReadFile(fpath)
	if err != nil {
		log.Warnf("Could not read '%s'", fpath)
		return nil, err
	}
	tmpl, err := template.New(fpath).Parse(string(f))
	if err != nil {
		log.Warnf("Failed to parse '%s'", fpath)
		return nil, err
	}

	watch, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}

	err = watch.Add(fpath)
	if err != nil {
		watch.Close()
	}

	done := make(chan bool)

	ft := &FileTemplate{
		RWMutex:  sync.RWMutex{},
		FilePath: fpath,
		Template: tmpl,
		Watcher:  watch,
		done:     done,
	}

	//Run the file watch in the background
	go ft.Watch()

	return ft, nil
}

// Reload loads up the template from the file path
func (f *FileTemplate) Reload() error {
	file, err := ioutil.ReadFile(f.FilePath)
	if err != nil {
		return err
	}

	tmpl, err := template.New(f.FilePath).Parse(string(file))
	if err != nil {
		return fmt.Errorf("Failed to parse '%s': %v", f.FilePath, err.Error())
	}
	f.Lock()
	f.Template = tmpl
	f.Unlock()

	return nil
}

//Watch is run in the background to watch for changes in the template files
func (f *FileTemplate) Watch() {
	for {
		select {
		case event := <-f.Watcher.Events:
			if event.Op&fsnotify.Write == fsnotify.Write {
				//We reload the file
				log.Infof("Reloading file: '%s'", f.FilePath)
				err := f.Reload()
				if err != nil {
					log.Warn(err.Error())
				}
			} else if event.Op&fsnotify.Remove == fsnotify.Remove || event.Op&fsnotify.Rename == fsnotify.Rename {
				log.Warningf("File '%s' removed. Using cached version.", f.FilePath)
				f.Watcher.Remove(f.FilePath)

				// Keep trying to see if the file exists until it is found again
				for {
					time.Sleep(2 * time.Second)
					v, err := os.Stat(f.FilePath)
					if err == nil && !v.IsDir() {

						err = f.Watcher.Add(f.FilePath)
						if err == nil {
							log.Infof("Reloading file: '%s'", f.FilePath)
							err := f.Reload()
							if err == nil {
								go f.Watch()
								return
							}
							log.Warn(err.Error())
						}

					}
				}

			}
		case err := <-f.Watcher.Errors:
			log.Errorf("Watcher for '%s' failed: %s", f.FilePath, err.Error())
			return
		case <-f.done:
			return
		}
	}
}

//Execute the template
func (f *FileTemplate) Execute(w io.Writer, data interface{}) error {
	f.RLock()
	err := f.Template.Execute(w, data)
	f.RUnlock()
	return err
}

// Close shuts down the file template
func (f *FileTemplate) Close() {
	f.Watcher.Close()
	f.done <- true
}

//LoadFiles sets up all the necessary files
func LoadFiles() error {
	//Now set up the app and www folder paths and make sure they exist
	exefolder, err := osext.ExecutableFolder()
	if err != nil {
		return err
	}
	WWWPath = path.Join(exefolder, WWWPrefix)
	log.Debugf("Hosting www from '%s'", WWWPath)

	AppPath = path.Join(exefolder, AppPrefix)
	log.Debugf("Hosting app from '%s'", AppPath)

	WWWIndex, err = NewFileTemplate(path.Join(WWWPath, "index.html"), err)
	WWWLogin, err = NewFileTemplate(path.Join(WWWPath, "login.html"), err)
	WWW404, err = NewFileTemplate(path.Join(WWWPath, "404.html"), err)
	WWWJoin, err = NewFileTemplate(path.Join(WWWPath, "join.html"), err)

	AppIndex, err = NewFileTemplate(path.Join(AppPath, "index.html"), err)
	AppUser, err = NewFileTemplate(path.Join(AppPath, "user.html"), err)
	AppDevice, err = NewFileTemplate(path.Join(AppPath, "device.html"), err)
	AppStream, err = NewFileTemplate(path.Join(AppPath, "stream.html"), err)
	App404, err = NewFileTemplate(path.Join(AppPath, "404.html"), err)
	AppError, err = NewFileTemplate(path.Join(AppPath, "error.html"), err)

	return err
}
<|endoftext|>


<commit_before>package admin

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/jinzhu/inflection"
	"github.com/jinzhu/now"
	"github.com/qor/qor"
	"github.com/qor/qor/resource"
	"github.com/qor/qor/roles"
	"github.com/qor/qor/utils"
)

type Resource struct {
	resource.Resource
	Config        *Config
	Metas         []*Meta
	SearchHandler func(keyword string, context *qor.Context) *gorm.DB

	admin          *Admin
	base           *Resource
	actions        []*Action
	scopes         []*Scope
	filters        map[string]*Filter
	searchAttrs    *[]string
	sortableAttrs  *[]string
	indexSections  []*Section
	newSections    []*Section
	editSections   []*Section
	showSections   []*Section
	isSetShowAttrs bool
	cachedMetas    *map[string][]*Meta
}

func (res *Resource) Meta(meta *Meta) {
	if res.GetMeta(meta.Name) != nil {
		utils.ExitWithMsg("Duplicated meta %v defined for resource %v", meta.Name, res.Name)
	}
	res.Metas = append(res.Metas, meta)
	meta.baseResource = res
	meta.updateMeta()
}

func (res Resource) GetAdmin() *Admin {
	return res.admin
}

func (res Resource) ToParam() string {
	if res.Config.Singleton == true {
		return utils.ToParamString(res.Name)
	}
	return utils.ToParamString(inflection.Plural(res.Name))
}

func (res Resource) UseTheme(theme string) []string {
	if res.Config != nil {
		for _, t := range res.Config.Themes {
			if t == theme {
				return res.Config.Themes
			}
		}

		res.Config.Themes = append(res.Config.Themes, theme)
	}
	return res.Config.Themes
}

func (res *Resource) convertObjectToJSONMap(context *Context, value interface{}, kind string) interface{} {
	reflectValue := reflect.ValueOf(value)
	for reflectValue.Kind() == reflect.Ptr {
		reflectValue = reflectValue.Elem()
	}

	switch reflectValue.Kind() {
	case reflect.Slice:
		values := []interface{}{}
		for i := 0; i < reflectValue.Len(); i++ {
			values = append(values, res.convertObjectToJSONMap(context, reflectValue.Index(i).Addr().Interface(), kind))
		}
		return values
	case reflect.Struct:
		var metas []*Meta
		if kind == "index" {
			metas = res.ConvertSectionToMetas(res.allowedSections(res.IndexAttrs(), context, roles.Update))
		} else if kind == "edit" {
			metas = res.ConvertSectionToMetas(res.allowedSections(res.EditAttrs(), context, roles.Update))
		} else if kind == "show" {
			metas = res.ConvertSectionToMetas(res.allowedSections(res.ShowAttrs(), context, roles.Read))
		}

		values := map[string]interface{}{}
		for _, meta := range metas {
			if meta.HasPermission(roles.Read, context.Context) {
				if valuer := meta.GetFormattedValuer(); valuer != nil {
					value := valuer(value, context.Context)
					if meta.GetResource() != nil {
						value = meta.Resource.convertObjectToJSONMap(context, value, kind)
					}
					values[meta.GetName()] = value
				}
			}
		}
		return values
	default:
		return value
	}
}

func (res *Resource) Decode(context *qor.Context, value interface{}) error {
	return resource.Decode(context, value, res)
}

func (res *Resource) allAttrs() []string {
	var attrs []string
	scope := &gorm.Scope{Value: res.Value}

Fields:
	for _, field := range scope.GetModelStruct().StructFields {
		for _, meta := range res.Metas {
			if field.Name == meta.FieldName {
				attrs = append(attrs, meta.Name)
				continue Fields
			}
		}

		if field.IsForeignKey {
			continue
		}

		for _, value := range []string{"CreatedAt", "UpdatedAt", "DeletedAt"} {
			if value == field.Name {
				continue Fields
			}
		}

		if (field.IsNormal || field.Relationship != nil) && !field.IsIgnored {
			attrs = append(attrs, field.Name)
		}
	}

MetaIncluded:
	for _, meta := range res.Metas {
		for _, attr := range attrs {
			if attr == meta.FieldName || attr == meta.Name {
				continue MetaIncluded
			}
		}
		attrs = append(attrs, meta.Name)
	}

	return attrs
}

func (res *Resource) getAttrs(attrs []string) []string {
	if len(attrs) == 0 {
		return res.allAttrs()
	} else {
		var onlyExcludeAttrs = true
		for _, attr := range attrs {
			if !strings.HasPrefix(attr, "-") {
				onlyExcludeAttrs = false
				break
			}
		}
		if onlyExcludeAttrs {
			return append(res.allAttrs(), attrs...)
		}
		return attrs
	}
}

func (res *Resource) IndexAttrs(values ...interface{}) []*Section {
	res.setSections(&res.indexSections, values...)
	return res.indexSections
}

func (res *Resource) NewAttrs(values ...interface{}) []*Section {
	res.setSections(&res.newSections, values...)
	return res.newSections
}

func (res *Resource) EditAttrs(values ...interface{}) []*Section {
	res.setSections(&res.editSections, values...)
	return res.editSections
}

func (res *Resource) ShowAttrs(values ...interface{}) []*Section {
	if len(values) > 0 {
		if values[len(values)-1] == false {
			values = values[:len(values)-1]
		} else {
			res.isSetShowAttrs = true
		}
	}
	res.setSections(&res.showSections, values...)
	return res.showSections
}

func (res *Resource) SortableAttrs(columns ...string) []string {
	if len(columns) != 0 || res.sortableAttrs == nil {
		if len(columns) == 0 {
			columns = res.ConvertSectionToStrings(res.indexSections)
		}
		res.sortableAttrs = &[]string{}
		scope := res.GetAdmin().Config.DB.NewScope(res.Value)
		for _, column := range columns {
			if field, ok := scope.FieldByName(column); ok && field.DBName != "" {
				attrs := append(*res.sortableAttrs, column)
				res.sortableAttrs = &attrs
			}
		}
	}
	return *res.sortableAttrs
}

func (res *Resource) SearchAttrs(columns ...string) []string {
	if len(columns) != 0 || res.searchAttrs == nil {
		if len(columns) == 0 {
			columns = res.ConvertSectionToStrings(res.indexSections)
		}

		if len(columns) > 0 {
			res.searchAttrs = &columns
			res.SearchHandler = func(keyword string, context *qor.Context) *gorm.DB {
				db := context.GetDB()
				var joinConditionsMap = map[string][]string{}
				var conditions []string
				var keywords []interface{}
				scope := db.NewScope(res.Value)

				for _, column := range columns {
					currentScope, nextScope := scope, scope

					if strings.Contains(column, ".") {
						for _, field := range strings.Split(column, ".") {
							column = field
							currentScope = nextScope
							if field, ok := scope.FieldByName(field); ok {
								if relationship := field.Relationship; relationship != nil {
									nextScope = currentScope.New(reflect.New(field.Field.Type()).Interface())
									key := fmt.Sprintf("LEFT JOIN %v ON", nextScope.TableName())

									for index := range relationship.ForeignDBNames {
										if relationship.Kind == "has_one" || relationship.Kind == "has_many" {
											joinConditionsMap[key] = append(joinConditionsMap[key],
												fmt.Sprintf("%v.%v = %v.%v",
													nextScope.QuotedTableName(), scope.Quote(relationship.ForeignDBNames[index]),
													currentScope.QuotedTableName(), scope.Quote(relationship.AssociationForeignDBNames[index]),
												))
										} else if relationship.Kind == "belongs_to" {
											joinConditionsMap[key] = append(joinConditionsMap[key],
												fmt.Sprintf("%v.%v = %v.%v",
													currentScope.QuotedTableName(), scope.Quote(relationship.ForeignDBNames[index]),
													nextScope.QuotedTableName(), scope.Quote(relationship.AssociationForeignDBNames[index]),
												))
										}
									}
								}
							}
						}
					}

					var tableName = currentScope.Quote(currentScope.TableName())
					if field, ok := currentScope.FieldByName(column); ok && field.IsNormal {
						switch field.Field.Kind() {
						case reflect.String:
							conditions = append(conditions, fmt.Sprintf("upper(%v.%v) like upper(?)", tableName, scope.Quote(field.DBName)))
							keywords = append(keywords, "%"+keyword+"%")
						case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
							if _, err := strconv.Atoi(keyword); err == nil {
								conditions = append(conditions, fmt.Sprintf("%v.%v = ?", tableName, scope.Quote(field.DBName)))
								keywords = append(keywords, keyword)
							}
						case reflect.Float32, reflect.Float64:
							if _, err := strconv.ParseFloat(keyword, 64); err == nil {
								conditions = append(conditions, fmt.Sprintf("%v.%v = ?", tableName, scope.Quote(field.DBName)))
								keywords = append(keywords, keyword)
							}
						case reflect.Bool:
							if value, err := strconv.ParseBool(keyword); err == nil {
								conditions = append(conditions, fmt.Sprintf("%v.%v = ?", tableName, scope.Quote(field.DBName)))
								keywords = append(keywords, value)
							}
						case reflect.Struct:
							// time ?
							if _, ok := field.Field.Interface().(time.Time); ok {
								if parsedTime, err := now.Parse(keyword); err == nil {
									conditions = append(conditions, fmt.Sprintf("%v.%v = ?", tableName, scope.Quote(field.DBName)))
									keywords = append(keywords, parsedTime)
								}
							}
						case reflect.Ptr:
							// time ?
							if _, ok := field.Field.Interface().(*time.Time); ok {
								if parsedTime, err := now.Parse(keyword); err == nil {
									conditions = append(conditions, fmt.Sprintf("%v.%v = ?", tableName, scope.Quote(field.DBName)))
									keywords = append(keywords, parsedTime)
								}
							}
						default:
							conditions = append(conditions, fmt.Sprintf("%v.%v = ?", tableName, scope.Quote(field.DBName)))
							keywords = append(keywords, keyword)
						}
					}
				}

				// join conditions
				if len(joinConditionsMap) > 0 {
					var joinConditions []string
					for key, values := range joinConditionsMap {
						joinConditions = append(joinConditions, fmt.Sprintf("%v %v", key, strings.Join(values, " AND ")))
					}
					db = db.Joins(strings.Join(joinConditions, " "))
				}

				// search conditions
				if len(conditions) > 0 {
					return db.Where(strings.Join(conditions, " OR "), keywords...)
				} else {
					return db
				}
			}
		}
	}

	return columns
}

func (res *Resource) getCachedMetas(cacheKey string, fc func() []resource.Metaor) []*Meta {
	if res.cachedMetas == nil {
		res.cachedMetas = &map[string][]*Meta{}
	}

	if values, ok := (*res.cachedMetas)[cacheKey]; ok {
		return values
	} else {
		values := fc()
		var metas []*Meta
		for _, value := range values {
			metas = append(metas, value.(*Meta))
		}
		(*res.cachedMetas)[cacheKey] = metas
		return metas
	}
}

func (res *Resource) GetMetas(attrs []string) []resource.Metaor {
	if len(attrs) == 0 {
		attrs = res.allAttrs()
	}
	var showSections, ignoredAttrs []string
	for _, attr := range attrs {
		if strings.HasPrefix(attr, "-") {
			ignoredAttrs = append(ignoredAttrs, strings.TrimLeft(attr, "-"))
		} else {
			showSections = append(showSections, attr)
		}
	}

	primaryKey := res.PrimaryFieldName()

	metas := []resource.Metaor{}

Attrs:
	for _, attr := range showSections {
		for _, a := range ignoredAttrs {
			if attr == a {
				continue Attrs
			}
		}

		var meta *Meta
		for _, m := range res.Metas {
			if m.GetName() == attr {
				meta = m
				break
			}
		}

		if meta == nil {
			meta = &Meta{}
			meta.Name = attr
			meta.baseResource = res
			if attr == primaryKey {
				meta.Type = "hidden"
			}
			meta.updateMeta()
		}

		metas = append(metas, meta)
	}

	return metas
}

func (res *Resource) GetMeta(name string) *Meta {
	for _, meta := range res.Metas {
		if meta.Name == name || meta.GetFieldName() == name {
			return meta
		}
	}
	return nil
}

func (res *Resource) GetMetaOrNew(name string) *Meta {
	for _, meta := range res.Metas {
		if meta.Name == name || meta.GetFieldName() == name {
			return meta
		}
	}
	for _, meta := range res.allMetas() {
		if meta.Name == name || meta.GetFieldName() == name {
			return meta
		}
	}
	return nil
}

func (res *Resource) allMetas() []*Meta {
	return res.getCachedMetas("all_metas", func() []resource.Metaor {
		return res.GetMetas([]string{})
	})
}

func (res *Resource) allowedSections(sections []*Section, context *Context, roles ...roles.PermissionMode) []*Section {
	for _, section := range sections {
		var editableRows [][]string
		for _, row := range section.Rows {
			var editableColumns []string
			for _, column := range row {
				for _, role := range roles {
					meta := res.GetMetaOrNew(column)
					if meta != nil && meta.HasPermission(role, context.Context) {
						editableColumns = append(editableColumns, column)
						break
					}
				}
			}
			if len(editableColumns) > 0 {
				editableRows = append(editableRows, editableColumns)
			}
		}
		section.Rows = editableRows
	}
	return sections
}

func (res *Resource) configure() {
	modelType := res.GetAdmin().Config.DB.NewScope(res.Value).GetModelStruct().ModelType
	for i := 0; i < modelType.NumField(); i++ {
		if fieldStruct := modelType.Field(i); fieldStruct.Anonymous {
			if injector, ok := reflect.New(fieldStruct.Type).Interface().(resource.ConfigureResourceInterface); ok {
				injector.ConfigureQorResource(res)
			}
		}
	}

	if injector, ok := res.Value.(resource.ConfigureResourceInterface); ok {
		injector.ConfigureQorResource(res)
	}
}
<commit_msg>Fix panic in admin interface<commit_after>package admin

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/jinzhu/inflection"
	"github.com/jinzhu/now"
	"github.com/qor/qor"
	"github.com/qor/qor/resource"
	"github.com/qor/qor/roles"
	"github.com/qor/qor/utils"
)

type Resource struct {
	resource.Resource
	Config        *Config
	Metas         []*Meta
	SearchHandler func(keyword string, context *qor.Context) *gorm.DB

	admin          *Admin
	base           *Resource
	actions        []*Action
	scopes         []*Scope
	filters        map[string]*Filter
	searchAttrs    *[]string
	sortableAttrs  *[]string
	indexSections  []*Section
	newSections    []*Section
	editSections   []*Section
	showSections   []*Section
	isSetShowAttrs bool
	cachedMetas    *map[string][]*Meta
}

func (res *Resource) Meta(meta *Meta) {
	if res.GetMeta(meta.Name) != nil {
		utils.ExitWithMsg("Duplicated meta %v defined for resource %v", meta.Name, res.Name)
	}
	res.Metas = append(res.Metas, meta)
	meta.baseResource = res
	meta.updateMeta()
}

func (res Resource) GetAdmin() *Admin {
	return res.admin
}

func (res Resource) ToParam() string {
	if res.Config.Singleton == true {
		return utils.ToParamString(res.Name)
	}
	return utils.ToParamString(inflection.Plural(res.Name))
}

func (res Resource) UseTheme(theme string) []string {
	if res.Config != nil {
		for _, t := range res.Config.Themes {
			if t == theme {
				return res.Config.Themes
			}
		}

		res.Config.Themes = append(res.Config.Themes, theme)
	}
	return res.Config.Themes
}

func (res *Resource) convertObjectToJSONMap(context *Context, value interface{}, kind string) interface{} {
	reflectValue := reflect.ValueOf(value)
	for reflectValue.Kind() == reflect.Ptr {
		reflectValue = reflectValue.Elem()
	}

	switch reflectValue.Kind() {
	case reflect.Slice:
		values := []interface{}{}
		for i := 0; i < reflectValue.Len(); i++ {
			values = append(values, res.convertObjectToJSONMap(context, reflectValue.Index(i).Addr().Interface(), kind))
		}
		return values
	case reflect.Struct:
		var metas []*Meta
		if kind == "index" {
			metas = res.ConvertSectionToMetas(res.allowedSections(res.IndexAttrs(), context, roles.Update))
		} else if kind == "edit" {
			metas = res.ConvertSectionToMetas(res.allowedSections(res.EditAttrs(), context, roles.Update))
		} else if kind == "show" {
			metas = res.ConvertSectionToMetas(res.allowedSections(res.ShowAttrs(), context, roles.Read))
		}

		values := map[string]interface{}{}
		for _, meta := range metas {
			if meta.HasPermission(roles.Read, context.Context) {
				if valuer := meta.GetFormattedValuer(); valuer != nil {
					value := valuer(value, context.Context)
					if meta.Resource != nil {
						value = meta.Resource.convertObjectToJSONMap(context, value, kind)
					}
					values[meta.GetName()] = value
				}
			}
		}
		return values
	default:
		return value
	}
}

func (res *Resource) Decode(context *qor.Context, value interface{}) error {
	return resource.Decode(context, value, res)
}

func (res *Resource) allAttrs() []string {
	var attrs []string
	scope := &gorm.Scope{Value: res.Value}

Fields:
	for _, field := range scope.GetModelStruct().StructFields {
		for _, meta := range res.Metas {
			if field.Name == meta.FieldName {
				attrs = append(attrs, meta.Name)
				continue Fields
			}
		}

		if field.IsForeignKey {
			continue
		}

		for _, value := range []string{"CreatedAt", "UpdatedAt", "DeletedAt"} {
			if value == field.Name {
				continue Fields
			}
		}

		if (field.IsNormal || field.Relationship != nil) && !field.IsIgnored {
			attrs = append(attrs, field.Name)
		}
	}

MetaIncluded:
	for _, meta := range res.Metas {
		for _, attr := range attrs {
			if attr == meta.FieldName || attr == meta.Name {
				continue MetaIncluded
			}
		}
		attrs = append(attrs, meta.Name)
	}

	return attrs
}

func (res *Resource) getAttrs(attrs []string) []string {
	if len(attrs) == 0 {
		return res.allAttrs()
	} else {
		var onlyExcludeAttrs = true
		for _, attr := range attrs {
			if !strings.HasPrefix(attr, "-") {
				onlyExcludeAttrs = false
				break
			}
		}
		if onlyExcludeAttrs {
			return append(res.allAttrs(), attrs...)
		}
		return attrs
	}
}

func (res *Resource) IndexAttrs(values ...interface{}) []*Section {
	res.setSections(&res.indexSections, values...)
	return res.indexSections
}

func (res *Resource) NewAttrs(values ...interface{}) []*Section {
	res.setSections(&res.newSections, values...)
	return res.newSections
}

func (res *Resource) EditAttrs(values ...interface{}) []*Section {
	res.setSections(&res.editSections, values...)
	return res.editSections
}

func (res *Resource) ShowAttrs(values ...interface{}) []*Section {
	if len(values) > 0 {
		if values[len(values)-1] == false {
			values = values[:len(values)-1]
		} else {
			res.isSetShowAttrs = true
		}
	}
	res.setSections(&res.showSections, values...)
	return res.showSections
}

func (res *Resource) SortableAttrs(columns ...string) []string {
	if len(columns) != 0 || res.sortableAttrs == nil {
		if len(columns) == 0 {
			columns = res.ConvertSectionToStrings(res.indexSections)
		}
		res.sortableAttrs = &[]string{}
		scope := res.GetAdmin().Config.DB.NewScope(res.Value)
		for _, column := range columns {
			if field, ok := scope.FieldByName(column); ok && field.DBName != "" {
				attrs := append(*res.sortableAttrs, column)
				res.sortableAttrs = &attrs
			}
		}
	}
	return *res.sortableAttrs
}

func (res *Resource) SearchAttrs(columns ...string) []string {
	if len(columns) != 0 || res.searchAttrs == nil {
		if len(columns) == 0 {
			columns = res.ConvertSectionToStrings(res.indexSections)
		}

		if len(columns) > 0 {
			res.searchAttrs = &columns
			res.SearchHandler = func(keyword string, context *qor.Context) *gorm.DB {
				db := context.GetDB()
				var joinConditionsMap = map[string][]string{}
				var conditions []string
				var keywords []interface{}
				scope := db.NewScope(res.Value)

				for _, column := range columns {
					currentScope, nextScope := scope, scope

					if strings.Contains(column, ".") {
						for _, field := range strings.Split(column, ".") {
							column = field
							currentScope = nextScope
							if field, ok := scope.FieldByName(field); ok {
								if relationship := field.Relationship; relationship != nil {
									nextScope = currentScope.New(reflect.New(field.Field.Type()).Interface())
									key := fmt.Sprintf("LEFT JOIN %v ON", nextScope.TableName())

									for index := range relationship.ForeignDBNames {
										if relationship.Kind == "has_one" || relationship.Kind == "has_many" {
											joinConditionsMap[key] = append(joinConditionsMap[key],
												fmt.Sprintf("%v.%v = %v.%v",
													nextScope.QuotedTableName(), scope.Quote(relationship.ForeignDBNames[index]),
													currentScope.QuotedTableName(), scope.Quote(relationship.AssociationForeignDBNames[index]),
												))
										} else if relationship.Kind == "belongs_to" {
											joinConditionsMap[key] = append(joinConditionsMap[key],
												fmt.Sprintf("%v.%v = %v.%v",
													currentScope.QuotedTableName(), scope.Quote(relationship.ForeignDBNames[index]),
													nextScope.QuotedTableName(), scope.Quote(relationship.AssociationForeignDBNames[index]),
												))
										}
									}
								}
							}
						}
					}

					var tableName = currentScope.Quote(currentScope.TableName())
					if field, ok := currentScope.FieldByName(column); ok && field.IsNormal {
						switch field.Field.Kind() {
						case reflect.String:
							conditions = append(conditions, fmt.Sprintf("upper(%v.%v) like upper(?)", tableName, scope.Quote(field.DBName)))
							keywords = append(keywords, "%"+keyword+"%")
						case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
							if _, err := strconv.Atoi(keyword); err == nil {
								conditions = append(conditions, fmt.Sprintf("%v.%v = ?", tableName, scope.Quote(field.DBName)))
								keywords = append(keywords, keyword)
							}
						case reflect.Float32, reflect.Float64:
							if _, err := strconv.ParseFloat(keyword, 64); err == nil {
								conditions = append(conditions, fmt.Sprintf("%v.%v = ?", tableName, scope.Quote(field.DBName)))
								keywords = append(keywords, keyword)
							}
						case reflect.Bool:
							if value, err := strconv.ParseBool(keyword); err == nil {
								conditions = append(conditions, fmt.Sprintf("%v.%v = ?", tableName, scope.Quote(field.DBName)))
								keywords = append(keywords, value)
							}
						case reflect.Struct:
							// time ?
							if _, ok := field.Field.Interface().(time.Time); ok {
								if parsedTime, err := now.Parse(keyword); err == nil {
									conditions = append(conditions, fmt.Sprintf("%v.%v = ?", tableName, scope.Quote(field.DBName)))
									keywords = append(keywords, parsedTime)
								}
							}
						case reflect.Ptr:
							// time ?
							if _, ok := field.Field.Interface().(*time.Time); ok {
								if parsedTime, err := now.Parse(keyword); err == nil {
									conditions = append(conditions, fmt.Sprintf("%v.%v = ?", tableName, scope.Quote(field.DBName)))
									keywords = append(keywords, parsedTime)
								}
							}
						default:
							conditions = append(conditions, fmt.Sprintf("%v.%v = ?", tableName, scope.Quote(field.DBName)))
							keywords = append(keywords, keyword)
						}
					}
				}

				// join conditions
				if len(joinConditionsMap) > 0 {
					var joinConditions []string
					for key, values := range joinConditionsMap {
						joinConditions = append(joinConditions, fmt.Sprintf("%v %v", key, strings.Join(values, " AND ")))
					}
					db = db.Joins(strings.Join(joinConditions, " "))
				}

				// search conditions
				if len(conditions) > 0 {
					return db.Where(strings.Join(conditions, " OR "), keywords...)
				} else {
					return db
				}
			}
		}
	}

	return columns
}

func (res *Resource) getCachedMetas(cacheKey string, fc func() []resource.Metaor) []*Meta {
	if res.cachedMetas == nil {
		res.cachedMetas = &map[string][]*Meta{}
	}

	if values, ok := (*res.cachedMetas)[cacheKey]; ok {
		return values
	} else {
		values := fc()
		var metas []*Meta
		for _, value := range values {
			metas = append(metas, value.(*Meta))
		}
		(*res.cachedMetas)[cacheKey] = metas
		return metas
	}
}

func (res *Resource) GetMetas(attrs []string) []resource.Metaor {
	if len(attrs) == 0 {
		attrs = res.allAttrs()
	}
	var showSections, ignoredAttrs []string
	for _, attr := range attrs {
		if strings.HasPrefix(attr, "-") {
			ignoredAttrs = append(ignoredAttrs, strings.TrimLeft(attr, "-"))
		} else {
			showSections = append(showSections, attr)
		}
	}

	primaryKey := res.PrimaryFieldName()

	metas := []resource.Metaor{}

Attrs:
	for _, attr := range showSections {
		for _, a := range ignoredAttrs {
			if attr == a {
				continue Attrs
			}
		}

		var meta *Meta
		for _, m := range res.Metas {
			if m.GetName() == attr {
				meta = m
				break
			}
		}

		if meta == nil {
			meta = &Meta{}
			meta.Name = attr
			meta.baseResource = res
			if attr == primaryKey {
				meta.Type = "hidden"
			}
			meta.updateMeta()
		}

		metas = append(metas, meta)
	}

	return metas
}

func (res *Resource) GetMeta(name string) *Meta {
	for _, meta := range res.Metas {
		if meta.Name == name || meta.GetFieldName() == name {
			return meta
		}
	}
	return nil
}

func (res *Resource) GetMetaOrNew(name string) *Meta {
	for _, meta := range res.Metas {
		if meta.Name == name || meta.GetFieldName() == name {
			return meta
		}
	}
	for _, meta := range res.allMetas() {
		if meta.Name == name || meta.GetFieldName() == name {
			return meta
		}
	}
	return nil
}

func (res *Resource) allMetas() []*Meta {
	return res.getCachedMetas("all_metas", func() []resource.Metaor {
		return res.GetMetas([]string{})
	})
}

func (res *Resource) allowedSections(sections []*Section, context *Context, roles ...roles.PermissionMode) []*Section {
	for _, section := range sections {
		var editableRows [][]string
		for _, row := range section.Rows {
			var editableColumns []string
			for _, column := range row {
				for _, role := range roles {
					meta := res.GetMetaOrNew(column)
					if meta != nil && meta.HasPermission(role, context.Context) {
						editableColumns = append(editableColumns, column)
						break
					}
				}
			}
			if len(editableColumns) > 0 {
				editableRows = append(editableRows, editableColumns)
			}
		}
		section.Rows = editableRows
	}
	return sections
}

func (res *Resource) configure() {
	modelType := res.GetAdmin().Config.DB.NewScope(res.Value).GetModelStruct().ModelType
	for i := 0; i < modelType.NumField(); i++ {
		if fieldStruct := modelType.Field(i); fieldStruct.Anonymous {
			if injector, ok := reflect.New(fieldStruct.Type).Interface().(resource.ConfigureResourceInterface); ok {
				injector.ConfigureQorResource(res)
			}
		}
	}

	if injector, ok := res.Value.(resource.ConfigureResourceInterface); ok {
		injector.ConfigureQorResource(res)
	}
}
<|endoftext|>


<commit_before>package agent

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httputil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/buildkite/agent/logger"
	"github.com/buildkite/agent/retry"
)

type Download struct {
	// The HTTP client to use for downloading
	Client http.Client

	// The actual URL to get the file from
	URL string

	// The root directory of the download
	Destination string

	// The relative path that should be preserved in the download folder
	Path string

	// How many times should it retry the download before giving up
	Retries int

	// If failed responses should be dumped to the log
	DebugHTTP bool
}

func (d Download) Start() error {
	return retry.Do(func(s *retry.Stats) error {
		err := d.try()
		if err != nil {
			logger.Warn("Error trying to download %s (%s) %s", d.URL, err, s)
		}
		return err
	}, &retry.Config{Maximum: d.Retries, Interval: 1 * time.Second})
}

func (d Download) try() error {
	// If we're downloading a file with a path of "pkg/foo.txt" to a folder
	// called "pkg", we should merge the two paths together. So, instead of it
	// downloading to: destination/pkg/pkg/foo.txt, it will just download to
	// destination/pkg/foo.txt
	destinationPaths := strings.Split(d.Destination, string(os.PathSeparator))
	downloadPaths := strings.Split(d.Path, string(os.PathSeparator))

	for i := 0; i < len(downloadPaths); i += 100 {
		// If the last part of the destination path matches
		// this path in the download, then cut it out.
		lastIndex := len(destinationPaths) - 1

		// Break if we've gone too far.
		if lastIndex == -1 {
			break
		}

		lastPathInDestination := destinationPaths[lastIndex]
		if lastPathInDestination == downloadPaths[i] {
			destinationPaths = destinationPaths[:lastIndex]
		}
	}

	finalizedDestination := strings.Join(destinationPaths, string(os.PathSeparator))

	targetFile := filepath.Join(finalizedDestination, d.Path)
	targetDirectory, _ := filepath.Split(targetFile)

	// Show a nice message that we're starting to download the file
	logger.Debug("Downloading %s to %s", d.URL, targetFile)

	// Start by downloading the file
	response, err := d.Client.Get(d.URL)
	if err != nil {
		return fmt.Errorf("Error while downloading %s (%T: %v)", d.URL, err, err)
	}
	defer response.Body.Close()

	// Double check the status
	if response.StatusCode/100 != 2 && response.StatusCode/100 != 3 {
		if d.DebugHTTP {
			responseDump, err := httputil.DumpResponse(response, true)
			logger.Debug("\nERR: %s\n%s", err, string(responseDump))
		}

		return &downloadError{response.Status}
	}

	// Now make the folder for our file
	err = os.MkdirAll(targetDirectory, 0777)
	if err != nil {
		return fmt.Errorf("Failed to create folder for %s (%T: %v)", targetFile, err, err)
	}

	// Create a file to handle the file
	fileBuffer, err := os.Create(targetFile)
	if err != nil {
		return fmt.Errorf("Failed to create file %s (%T: %v)", targetFile, err, err)
	}
	defer fileBuffer.Close()

	// Copy the data to the file
	bytes, err := io.Copy(fileBuffer, response.Body)
	if err != nil {
		return fmt.Errorf("Error when copying data %s (%T: %v)", d.URL, err, err)
	}

	logger.Info("Successfully downloaded \"%s\" %d bytes", d.Path, bytes)

	return nil
}

type downloadError struct {
	s string
}

func (e *downloadError) Error() string {
	return e.s
}
<commit_msg>Increase artifact download tries to once every 5 seconds<commit_after>package agent

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httputil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/buildkite/agent/logger"
	"github.com/buildkite/agent/retry"
)

type Download struct {
	// The HTTP client to use for downloading
	Client http.Client

	// The actual URL to get the file from
	URL string

	// The root directory of the download
	Destination string

	// The relative path that should be preserved in the download folder
	Path string

	// How many times should it retry the download before giving up
	Retries int

	// If failed responses should be dumped to the log
	DebugHTTP bool
}

func (d Download) Start() error {
	return retry.Do(func(s *retry.Stats) error {
		err := d.try()
		if err != nil {
			logger.Warn("Error trying to download %s (%s) %s", d.URL, err, s)
		}
		return err
	}, &retry.Config{Maximum: d.Retries, Interval: 5 * time.Second})
}

func (d Download) try() error {
	// If we're downloading a file with a path of "pkg/foo.txt" to a folder
	// called "pkg", we should merge the two paths together. So, instead of it
	// downloading to: destination/pkg/pkg/foo.txt, it will just download to
	// destination/pkg/foo.txt
	destinationPaths := strings.Split(d.Destination, string(os.PathSeparator))
	downloadPaths := strings.Split(d.Path, string(os.PathSeparator))

	for i := 0; i < len(downloadPaths); i += 100 {
		// If the last part of the destination path matches
		// this path in the download, then cut it out.
		lastIndex := len(destinationPaths) - 1

		// Break if we've gone too far.
		if lastIndex == -1 {
			break
		}

		lastPathInDestination := destinationPaths[lastIndex]
		if lastPathInDestination == downloadPaths[i] {
			destinationPaths = destinationPaths[:lastIndex]
		}
	}

	finalizedDestination := strings.Join(destinationPaths, string(os.PathSeparator))

	targetFile := filepath.Join(finalizedDestination, d.Path)
	targetDirectory, _ := filepath.Split(targetFile)

	// Show a nice message that we're starting to download the file
	logger.Debug("Downloading %s to %s", d.URL, targetFile)

	// Start by downloading the file
	response, err := d.Client.Get(d.URL)
	if err != nil {
		return fmt.Errorf("Error while downloading %s (%T: %v)", d.URL, err, err)
	}
	defer response.Body.Close()

	// Double check the status
	if response.StatusCode/100 != 2 && response.StatusCode/100 != 3 {
		if d.DebugHTTP {
			responseDump, err := httputil.DumpResponse(response, true)
			logger.Debug("\nERR: %s\n%s", err, string(responseDump))
		}

		return &downloadError{response.Status}
	}

	// Now make the folder for our file
	err = os.MkdirAll(targetDirectory, 0777)
	if err != nil {
		return fmt.Errorf("Failed to create folder for %s (%T: %v)", targetFile, err, err)
	}

	// Create a file to handle the file
	fileBuffer, err := os.Create(targetFile)
	if err != nil {
		return fmt.Errorf("Failed to create file %s (%T: %v)", targetFile, err, err)
	}
	defer fileBuffer.Close()

	// Copy the data to the file
	bytes, err := io.Copy(fileBuffer, response.Body)
	if err != nil {
		return fmt.Errorf("Error when copying data %s (%T: %v)", d.URL, err, err)
	}

	logger.Info("Successfully downloaded \"%s\" %d bytes", d.Path, bytes)

	return nil
}

type downloadError struct {
	s string
}

func (e *downloadError) Error() string {
	return e.s
}
<|endoftext|>