<commit_before>package resource

import (
	"io"
	"os"
)

// An Instance represents the content at some location accessed through some
// Provider. It's the data at some URL.
type Instance interface {
	Get() (io.ReadCloser, error)
	Put(io.Reader) error
	Stat() (os.FileInfo, error)
	ReadAt([]byte, int64) (int, error)
	WriteAt([]byte, int64) (int, error)
	Delete() error
}

// Creates a io.ReadSeeker to an Instance.
func ReadSeeker(r Instance) io.ReadSeeker {
	fi, err := r.Stat()
	if err != nil {
		return nil
	}
	return io.NewSectionReader(r, 0, fi.Size())
}

// Move instance content, deleting the source if it succeeds.
func Move(from, to Instance) (err error) {
	r, err := from.Get()
	if err != nil {
		return
	}
	err = to.Put(r)
	if err != nil {
		return
	}
	from.Delete()
	return
}
<commit_msg>resource.Move: Wasn't closing from Instance<commit_after>package resource

import (
	"io"
	"os"
)

// An Instance represents the content at some location accessed through some
// Provider. It's the data at some URL.
type Instance interface {
	Get() (io.ReadCloser, error)
	Put(io.Reader) error
	Stat() (os.FileInfo, error)
	ReadAt([]byte, int64) (int, error)
	WriteAt([]byte, int64) (int, error)
	Delete() error
}

// Creates a io.ReadSeeker to an Instance.
func ReadSeeker(r Instance) io.ReadSeeker {
	fi, err := r.Stat()
	if err != nil {
		return nil
	}
	return io.NewSectionReader(r, 0, fi.Size())
}

// Move instance content, deleting the source if it succeeds.
func Move(from, to Instance) (err error) {
	rc, err := from.Get()
	if err != nil {
		return
	}
	defer rc.Close()
	err = to.Put(rc)
	if err != nil {
		return
	}
	from.Delete()
	return
}
<|endoftext|>


<commit_before>package larix

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"reflect"
	"strings"
	"sync"
	"time"
)

type Log struct {
	//Log file name
	File string

	//error file , default
	errorFile string

	// if log rotate, current support for hour
	Rotate bool

	// basic fields, will out in every log
	BasicFields map[string]interface{}

	//log level
	Level int

	//current rotate sign, we use year montn day hour
	CurrRotateSign int

	// ensure atomic log handler update
	mu sync.Mutex

	// normal log file handler
	nfd *os.File

	// wrong log file handler
	wfd *os.File

	// normal Log Handler
	nlog *log.Logger

	// wrong log Handler
	wlog *log.Logger
}

//control fields
// file : string , if nil, we'll set to stdout
// level : int  , if nil, we'll set to debug
// rotate : bool  , if nil, we'll set to true
// like:
/*
	conf = map[string]interface{}{
		"file" : "./logs/test",
		"rotate": true,
		"level": 0,
	}
*/
// conf define for ini conf to map
// now not use
type LogConf struct {
	File   string `ini:"file"`
	Rotate bool   `ini:"rotate"`
	Level  int    `ini:"level"`
}

//log level
const (
	LOG_DEBUG int = iota
	LOG_TRACE
	LOG_NOTICE
	LOG_WARN
	LOG_FATAL
	LOG_OVER
)

var logString map[int]string = map[int]string{
	LOG_DEBUG:  "debug",
	LOG_TRACE:  "trace",
	LOG_NOTICE: "notice",
	LOG_WARN:   "warning",
	LOG_FATAL:  "fatal",
}

//log level string

//Log handler
var logHdr *Log = nil

//interface
func LogInit(conf map[string]interface{}) error {
	if logHdr != nil {
		LogFatal("re init log handler, invalid")
		return nil
	}

	logHdr = &Log{}

	//why use exists ,not use err; for get memeber succ ,exists set to true, not false,
	tmpFile, exists := conf["log_path"]
	if exists {
		file_path, exists := tmpFile.(string)
		if !exists {
			panic("log file path type error, we need string\n")
		}
		//we'll not check file path here
		logHdr.File = file_path
	} else {
		//now we'll not support to write to stdout or stderr
		panic("log file path not found, please set by file field\n")
	}

	tmpRotate, exists := conf["log_rotate"]
	if exists {
		logHdr.Rotate, exists = tmpRotate.(bool)
		if !exists {
			io.WriteString(os.Stdout, "log rotate get failed, we need bool type, and now set to false\n")
			logHdr.Rotate = false
		}
	} else {
		logHdr.Rotate = false
	}

	tmpLevel, exists := conf["log_level"]
	if exists {
		logHdr.Level, exists = tmpLevel.(int)
		if !exists {
			io.WriteString(os.Stdout, "log level get failed, we need int type, and now set to debug\n")
			logHdr.Level = LOG_DEBUG
		}
		if logHdr.Level >= LOG_OVER {
			io.WriteString(os.Stdout, "log level invalid, now set to debug\n")
			logHdr.Level = LOG_DEBUG
		}
	} else {
		logHdr.Level = LOG_DEBUG
	}

	//todo: basic fields support
	var nlfile string
	var wlfile string

	if logHdr.Rotate {
		logHdr.CurrRotateSign = getRotateSign()
		nlfile = fmt.Sprintf("%s.%d", logHdr.File, logHdr.CurrRotateSign)
		wlfile = fmt.Sprintf("%s.wf.%d", logHdr.File, logHdr.CurrRotateSign)
	} else {
		logHdr.CurrRotateSign = 0
		nlfile = logHdr.File
		wlfile = fmt.Sprintf("%s.wf", logHdr.File)
	}

	//open file and add log handler
	var errf error
	logHdr.nfd, errf = os.OpenFile(nlfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModeSetuid|os.ModeSetgid|0660)
	if errf != nil {
		panic(fmt.Sprintf("open log file %s failed, %s\n", nlfile, errf.Error()))
	}

	logHdr.wfd, errf = os.OpenFile(wlfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModeSetuid|os.ModeSetgid|0660)
	if errf != nil {
		logHdr.nfd.Close()
		panic(fmt.Sprintf("open wflog file %s failed, %s\n", wlfile, errf.Error()))
	}

	// normal Log Handler
	logHdr.nlog = log.New(logHdr.nfd, "", log.Ldate|log.Ltime|log.Lshortfile)

	// wrong log Handler
	logHdr.wlog = log.New(logHdr.wfd, "", log.Ldate|log.Ltime|log.Lshortfile)
	return nil
}

func getRotateSign() int {
	t_case := time.Now()
	return t_case.Year()*1000000 + int(t_case.Month())*10000 + t_case.Day()*100 + t_case.Hour()
}

func (l *Log) rotate() error {
	if !l.Rotate {
		return nil
	}

	curr_rotate_sign := getRotateSign()
	if l.CurrRotateSign == curr_rotate_sign {
		return nil
	}

	l.mu.Lock()
	defer l.mu.Unlock()
	if l.nfd != nil {
		l.nfd.Close()
	}
	if l.wfd != nil {
		l.wfd.Close()
	}

	nlfile := fmt.Sprintf("%s.%d", l.File, curr_rotate_sign)
	wlfile := fmt.Sprintf("%s.wf.%d", l.File, curr_rotate_sign)

	//open file and add log handler
	var err error
	l.nfd, err = os.OpenFile(nlfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModeSetuid|os.ModeSetgid|0660)
	if err != nil {
		//todo: to write error msg to stderr
		return err
	}

	l.wfd, err = os.OpenFile(wlfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModeSetuid|os.ModeSetgid|0660)
	if err != nil {
		//todo: to write error msg to stderr
		return err
	}

	// normal Log Handler
	l.nlog = log.New(l.nfd, "", log.Ldate|log.Ltime|log.Lshortfile)

	// wrong log Handler
	l.wlog = log.New(l.wfd, "", log.Ldate|log.Ltime|log.Lshortfile)

	//when rotate done, we change the sign ,for we can reentrant
	l.CurrRotateSign = curr_rotate_sign
	return nil
}

func (l *Log) WriteLog(level int, v ...interface{}) {
	//step1: rotate
	if l.rotate() != nil {
		return
	}

	//step2: gen log string
	log_str := l.genLogString(level, v...)

	//step3: write log
	if level < LOG_WARN {
		l.nlog.Print(log_str)
	} else {
		l.wlog.Print(log_str)
	}

	return
}

//todo
func (l *Log) genLogString(level int, v ...interface{}) string {
	if len(v) >= 2 {
		format_str, err := v[0].(string)

		//if v[0] is string
		if err {
			return strings.TrimSpace(fmt.Sprintf("["+logString[level]+"] "+format_str, v[1:]...)) + "\n"
		}
	}

	var res bytes.Buffer
	res.WriteString("[" + logString[level] + "]")
	//compate with multi params
	for _, val := range v {
		res.WriteString(" ")
		//for standard logger just check last character if is "\n",
		//can't deal multi space characters,
		//so we need to deal space characters by ourself
		res.WriteString(strings.TrimSpace(l.dealFields(val)))
	}
	res.WriteString("\n")

	return res.String()
}

func (l *Log) dealFields(v interface{}) string {
	//now we just deal string ,map and
	value := reflect.ValueOf(v)
	var vKind = value.Kind()

	switch {
	case vKind == 0:
		return "nil"

	//bool + int +uint
	case vKind < 12:
		return fmt.Sprintf("%v", value)

	//uintptr
	case vKind == 12:
		return "[uptr]"

	//float + complex
	case vKind < 17:
		return fmt.Sprintf("%v", value)

	//Array
	case vKind == 17:
		return fmt.Sprintf("array[%v]", value)

	//chan\func\Interface
	case vKind < 21:
		return fmt.Sprintf("%v", value)

	//Map
	case vKind == 21:
		var res bytes.Buffer
		keys := value.MapKeys()

		for _, key := range keys {
			res.WriteString(fmt.Sprintf("%v[%v] ", key, value.MapIndex(key)))
		}
		return res.String()

	//ptr
	case vKind == 22:
		return "[ptr]"

	//slice
	case vKind == 23:
		return fmt.Sprintf("slice[%v]", value)

	//string
	case vKind == 24:
		return fmt.Sprintf("%v", value)

	//Struct
	case vKind == 25:
		return fmt.Sprintf("struct[%s]", value.Type())

	//Unsafeptr
	case vKind == 26:
		return "[Unsafeptr]"

	default:
		return "Unknown"
	}

	return "Unknown"
}

func LogDestory() {
	if logHdr == nil {
		return
	}

	if logHdr.nfd != nil {
		logHdr.nfd.Close()
	}
	if logHdr.wfd != nil {
		logHdr.wfd.Close()
	}
	logHdr = nil
}

//todo
func LogAddBasic() {
}

//todo
func LogRmBasic() {
}

func LogDebug(v ...interface{}) {
	if logHdr == nil {
		return
	}

	if LOG_DEBUG < logHdr.Level {
		return
	}

	logHdr.WriteLog(LOG_DEBUG, v...)
}

func LogTrace(v ...interface{}) {
	if logHdr == nil {
		return
	}

	if LOG_TRACE < logHdr.Level {
		return
	}

	logHdr.WriteLog(LOG_TRACE, v...)
}

func LogNotice(v ...interface{}) {
	if logHdr == nil {
		return
	}

	if LOG_NOTICE < logHdr.Level {
		return
	}

	logHdr.WriteLog(LOG_NOTICE, v...)
}

func LogWarn(v ...interface{}) {
	if logHdr == nil {
		return
	}

	if LOG_WARN < logHdr.Level {
		return
	}

	logHdr.WriteLog(LOG_WARN, v...)
}

func LogFatal(v ...interface{}) {
	if logHdr == nil {
		return
	}

	if LOG_FATAL < logHdr.Level {
		return
	}

	logHdr.WriteLog(LOG_FATAL, v...)
}
<commit_msg>fix callback file and line number error<commit_after>package larix

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"reflect"
	"strings"
	"sync"
	"time"
)

type Log struct {
	//Log file name
	File string

	//error file , default
	errorFile string

	// if log rotate, current support for hour
	Rotate bool

	// basic fields, will out in every log
	BasicFields map[string]interface{}

	//log level
	Level int

	//current rotate sign, we use year montn day hour
	CurrRotateSign int

	// ensure atomic log handler update
	mu sync.Mutex

	// normal log file handler
	nfd *os.File

	// wrong log file handler
	wfd *os.File

	// normal Log Handler
	nlog *log.Logger

	// wrong log Handler
	wlog *log.Logger
}

//control fields
// file : string , if nil, we'll set to stdout
// level : int  , if nil, we'll set to debug
// rotate : bool  , if nil, we'll set to true
// like:
/*
	conf = map[string]interface{}{
		"file" : "./logs/test",
		"rotate": true,
		"level": 0,
	}
*/
// conf define for ini conf to map
// now not use
type LogConf struct {
	File   string `ini:"file"`
	Rotate bool   `ini:"rotate"`
	Level  int    `ini:"level"`
}

//log level
const (
	LOG_DEBUG int = iota
	LOG_TRACE
	LOG_NOTICE
	LOG_WARN
	LOG_FATAL
	LOG_OVER
)

var logString map[int]string = map[int]string{
	LOG_DEBUG:  "debug",
	LOG_TRACE:  "trace",
	LOG_NOTICE: "notice",
	LOG_WARN:   "warning",
	LOG_FATAL:  "fatal",
}

//log level string

//Log handler
var logHdr *Log = nil

//interface
func LogInit(conf map[string]interface{}) error {
	if logHdr != nil {
		LogFatal("re init log handler, invalid")
		return nil
	}

	logHdr = &Log{}

	//why use exists ,not use err; for get memeber succ ,exists set to true, not false,
	tmpFile, exists := conf["log_path"]
	if exists {
		file_path, exists := tmpFile.(string)
		if !exists {
			panic("log file path type error, we need string\n")
		}
		//we'll not check file path here
		logHdr.File = file_path
	} else {
		//now we'll not support to write to stdout or stderr
		panic("log file path not found, please set by file field\n")
	}

	tmpRotate, exists := conf["log_rotate"]
	if exists {
		logHdr.Rotate, exists = tmpRotate.(bool)
		if !exists {
			io.WriteString(os.Stdout, "log rotate get failed, we need bool type, and now set to false\n")
			logHdr.Rotate = false
		}
	} else {
		logHdr.Rotate = false
	}

	tmpLevel, exists := conf["log_level"]
	if exists {
		logHdr.Level, exists = tmpLevel.(int)
		if !exists {
			io.WriteString(os.Stdout, "log level get failed, we need int type, and now set to debug\n")
			logHdr.Level = LOG_DEBUG
		}
		if logHdr.Level >= LOG_OVER {
			io.WriteString(os.Stdout, "log level invalid, now set to debug\n")
			logHdr.Level = LOG_DEBUG
		}
	} else {
		logHdr.Level = LOG_DEBUG
	}

	//todo: basic fields support
	var nlfile string
	var wlfile string

	if logHdr.Rotate {
		logHdr.CurrRotateSign = getRotateSign()
		nlfile = fmt.Sprintf("%s.%d", logHdr.File, logHdr.CurrRotateSign)
		wlfile = fmt.Sprintf("%s.wf.%d", logHdr.File, logHdr.CurrRotateSign)
	} else {
		logHdr.CurrRotateSign = 0
		nlfile = logHdr.File
		wlfile = fmt.Sprintf("%s.wf", logHdr.File)
	}

	//open file and add log handler
	var errf error
	logHdr.nfd, errf = os.OpenFile(nlfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModeSetuid|os.ModeSetgid|0660)
	if errf != nil {
		panic(fmt.Sprintf("open log file %s failed, %s\n", nlfile, errf.Error()))
	}

	logHdr.wfd, errf = os.OpenFile(wlfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModeSetuid|os.ModeSetgid|0660)
	if errf != nil {
		logHdr.nfd.Close()
		panic(fmt.Sprintf("open wflog file %s failed, %s\n", wlfile, errf.Error()))
	}

	// normal Log Handler
	logHdr.nlog = log.New(logHdr.nfd, "", log.Ldate|log.Ltime|log.Lshortfile)

	// wrong log Handler
	logHdr.wlog = log.New(logHdr.wfd, "", log.Ldate|log.Ltime|log.Lshortfile)
	return nil
}

func getRotateSign() int {
	t_case := time.Now()
	return t_case.Year()*1000000 + int(t_case.Month())*10000 + t_case.Day()*100 + t_case.Hour()
}

func (l *Log) rotate() error {
	if !l.Rotate {
		return nil
	}

	curr_rotate_sign := getRotateSign()
	if l.CurrRotateSign == curr_rotate_sign {
		return nil
	}

	l.mu.Lock()
	defer l.mu.Unlock()
	if l.nfd != nil {
		l.nfd.Close()
	}
	if l.wfd != nil {
		l.wfd.Close()
	}

	nlfile := fmt.Sprintf("%s.%d", l.File, curr_rotate_sign)
	wlfile := fmt.Sprintf("%s.wf.%d", l.File, curr_rotate_sign)

	//open file and add log handler
	var err error
	l.nfd, err = os.OpenFile(nlfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModeSetuid|os.ModeSetgid|0660)
	if err != nil {
		//todo: to write error msg to stderr
		return err
	}

	l.wfd, err = os.OpenFile(wlfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModeSetuid|os.ModeSetgid|0660)
	if err != nil {
		//todo: to write error msg to stderr
		return err
	}

	// normal Log Handler
	l.nlog = log.New(l.nfd, "", log.Ldate|log.Ltime|log.Lshortfile)

	// wrong log Handler
	l.wlog = log.New(l.wfd, "", log.Ldate|log.Ltime|log.Lshortfile)

	//when rotate done, we change the sign ,for we can reentrant
	l.CurrRotateSign = curr_rotate_sign
	return nil
}

func (l *Log) WriteLog(level int, v ...interface{}) {
	//step1: rotate
	if l.rotate() != nil {
		return
	}

	//step2: gen log string
	log_str := l.genLogString(level, v...)

	//step3: write log
	if level < LOG_WARN {
		l.nlog.Output(3, log_str)
	} else {
		l.wlog.Output(3, log_str)
	}

	return
}

//todo
func (l *Log) genLogString(level int, v ...interface{}) string {
	if len(v) >= 2 {
		format_str, err := v[0].(string)

		//if v[0] is string
		if err {
			return strings.TrimSpace(fmt.Sprintf("["+logString[level]+"] "+format_str, v[1:]...)) + "\n"
		}
	}

	var res bytes.Buffer
	res.WriteString("[" + logString[level] + "]")
	//compate with multi params
	for _, val := range v {
		res.WriteString(" ")
		//for standard logger just check last character if is "\n",
		//can't deal multi space characters,
		//so we need to deal space characters by ourself
		res.WriteString(strings.TrimSpace(l.dealFields(val)))
	}
	res.WriteString("\n")

	return res.String()
}

func (l *Log) dealFields(v interface{}) string {
	//now we just deal string ,map and
	value := reflect.ValueOf(v)
	var vKind = value.Kind()

	switch {
	case vKind == 0:
		return "nil"

	//bool + int +uint
	case vKind < 12:
		return fmt.Sprintf("%v", value)

	//uintptr
	case vKind == 12:
		return "[uptr]"

	//float + complex
	case vKind < 17:
		return fmt.Sprintf("%v", value)

	//Array
	case vKind == 17:
		return fmt.Sprintf("array[%v]", value)

	//chan\func\Interface
	case vKind < 21:
		return fmt.Sprintf("%v", value)

	//Map
	case vKind == 21:
		var res bytes.Buffer
		keys := value.MapKeys()

		for _, key := range keys {
			res.WriteString(fmt.Sprintf("%v[%v] ", key, value.MapIndex(key)))
		}
		return res.String()

	//ptr
	case vKind == 22:
		return "[ptr]"

	//slice
	case vKind == 23:
		return fmt.Sprintf("slice[%v]", value)

	//string
	case vKind == 24:
		return fmt.Sprintf("%v", value)

	//Struct
	case vKind == 25:
		return fmt.Sprintf("struct[%s]", value.Type())

	//Unsafeptr
	case vKind == 26:
		return "[Unsafeptr]"

	default:
		return "Unknown"
	}

	return "Unknown"
}

func LogDestory() {
	if logHdr == nil {
		return
	}

	if logHdr.nfd != nil {
		logHdr.nfd.Close()
	}
	if logHdr.wfd != nil {
		logHdr.wfd.Close()
	}
	logHdr = nil
}

//todo
func LogAddBasic() {
}

//todo
func LogRmBasic() {
}

func LogDebug(v ...interface{}) {
	if logHdr == nil {
		return
	}

	if LOG_DEBUG < logHdr.Level {
		return
	}

	logHdr.WriteLog(LOG_DEBUG, v...)
}

func LogTrace(v ...interface{}) {
	if logHdr == nil {
		return
	}

	if LOG_TRACE < logHdr.Level {
		return
	}

	logHdr.WriteLog(LOG_TRACE, v...)
}

func LogNotice(v ...interface{}) {
	if logHdr == nil {
		return
	}

	if LOG_NOTICE < logHdr.Level {
		return
	}

	logHdr.WriteLog(LOG_NOTICE, v...)
}

func LogWarn(v ...interface{}) {
	if logHdr == nil {
		return
	}

	if LOG_WARN < logHdr.Level {
		return
	}

	logHdr.WriteLog(LOG_WARN, v...)
}

func LogFatal(v ...interface{}) {
	if logHdr == nil {
		return
	}

	if LOG_FATAL < logHdr.Level {
		return
	}

	logHdr.WriteLog(LOG_FATAL, v...)
}
<|endoftext|>


<commit_before>package presenter

import (
	"github.com/davidnix/ffdraft/players"
)

var startCols = []string{
	"Player",
	"Pos",
	"Team",
}

var endCols = []string{
	"Tier",
	"Rank",
	"PRank",
	"Drop",
	"StdDev",
	"VOR",
	"Risk",
	"Injury",
}

func buildRow(p players.Player, middleVals ...string) (row []string) {
	if p.Name == "" {
		// blank row
		return make([]string, len(middleVals)+11)
	}
	row = append(row,
		p.Name,
		p.Position,
		p.Team,
	)
	row = append(row, middleVals...)
	row = append(row,
		Int(p.Tier).String(),
		Int(p.OverallRank).String(),
		Int(p.PositionRank).String(),
		Float(p.Dropoff).String(),
		Float(p.StdDevPoints).String(),
		Float(p.Vor).String(),
		Float(p.Risk).String(),
		Injury(p.Injury).String(),
	)
	return row
}

type Players []players.Player

func (ps Players) Headers() []string {
	cols := append([]string{}, startCols...)
	cols = append(cols, "Floor", "Ceil")
	return append(cols, endCols...)
}

func (ps Players) Rows() (rows [][]string) {
	for _, p := range ps {
		row := buildRow(p,
			Float(p.Floor).String(),
			Float(p.Ceil).String(),
		)
		rows = append(rows, row)
	}
	return rows
}

type FloorPlayers []players.Player

func (fp FloorPlayers) Headers() []string {
	cols := append([]string{}, startCols...)
	cols = append(cols, "Floor", "FRank", "FVor")
	return append(cols, endCols...)
}

func (fp FloorPlayers) Rows() (rows [][]string) {
	for _, p := range fp {
		row := buildRow(p,
			Float(p.Floor).String(),
			Float(p.FloorRank).String(),
			Float(p.FloorVor).String(),
		)
		rows = append(rows, row)
	}
	return rows
}

type CeilPlayers []players.Player

func (cp CeilPlayers) Headers() []string {
	cols := append([]string{}, startCols...)
	cols = append(cols, "Ceil", "CRank", "CVor")
	return append(cols, endCols...)
}

func (cp CeilPlayers) Rows() (rows [][]string) {
	for _, p := range cp {
		row := buildRow(p,
			Float(p.Ceil).String(),
			Float(p.CeilRank).String(),
			Float(p.CeilVor).String(),
		)
		rows = append(rows, row)
	}
	return rows
}
<commit_msg>fix number formatting<commit_after>package presenter

import (
	"github.com/davidnix/ffdraft/players"
)

var startCols = []string{
	"Player",
	"Pos",
	"Team",
}

var endCols = []string{
	"Tier",
	"Rank",
	"PRank",
	"Drop",
	"StdDev",
	"VOR",
	"Risk",
	"Injury",
}

func buildRow(p players.Player, middleVals ...string) (row []string) {
	if p.Name == "" {
		// blank row
		return make([]string, len(middleVals)+11)
	}
	row = append(row,
		p.Name,
		p.Position,
		p.Team,
	)
	row = append(row, middleVals...)
	row = append(row,
		Int(p.Tier).String(),
		Int(p.OverallRank).String(),
		Int(p.PositionRank).String(),
		Float(p.Dropoff).String(),
		Float(p.StdDevPoints).String(),
		Float(p.Vor).String(),
		Float(p.Risk).String(),
		Injury(p.Injury).String(),
	)
	return row
}

type Players []players.Player

func (ps Players) Headers() []string {
	cols := append([]string{}, startCols...)
	cols = append(cols, "Floor", "Ceil")
	return append(cols, endCols...)
}

func (ps Players) Rows() (rows [][]string) {
	for _, p := range ps {
		row := buildRow(p,
			Float(p.Floor).String(),
			Float(p.Ceil).String(),
		)
		rows = append(rows, row)
	}
	return rows
}

type FloorPlayers []players.Player

func (fp FloorPlayers) Headers() []string {
	cols := append([]string{}, startCols...)
	cols = append(cols, "Floor", "FRank", "FVor")
	return append(cols, endCols...)
}

func (fp FloorPlayers) Rows() (rows [][]string) {
	for _, p := range fp {
		row := buildRow(p,
			Float(p.Floor).String(),
			Int(p.FloorRank).String(),
			Float(p.FloorVor).String(),
		)
		rows = append(rows, row)
	}
	return rows
}

type CeilPlayers []players.Player

func (cp CeilPlayers) Headers() []string {
	cols := append([]string{}, startCols...)
	cols = append(cols, "Ceil", "CRank", "CVor")
	return append(cols, endCols...)
}

func (cp CeilPlayers) Rows() (rows [][]string) {
	for _, p := range cp {
		row := buildRow(p,
			Float(p.Ceil).String(),
			Int(p.CeilRank).String(),
			Float(p.CeilVor).String(),
		)
		rows = append(rows, row)
	}
	return rows
}
<|endoftext|>


<commit_before>package nos

func maxSum(A []int, l, m int) int {
    return 0
}
<commit_msg>fix: solve 1031 based on discussion<commit_after>package nos

import "github.com/catorpilor/leetcode/utils"

func maxSum(A []int, l, m int) int {
    n := len(A)
    if n < 1 {
        return 0
    }
    presum := make([]int, n)
    presum[0] = A[0]
    for i := 1; i < n; i++ {
        presum[i] = presum[i-1] + A[i]
    }
    return withPresum(presum, l, m)
}

func withPresum(preSum []int, l, m int) int {
    res, lmax, mmax := preSum[l+m-1], preSum[l-1], preSum[m-1]
    for i := l + m; i < len(preSum); i++ {
        // lmax represents the max L subarray before M
        lmax = utils.Max(lmax, preSum[i-m]-preSum[i-m-l])
        // mmax represents the max M subarray before L
        mmax = utils.Max(mmax, preSum[i-l]-preSum[i-l-m])
        // lmax+presum[i]-presum[i-m] represents L subarray before M subarray
        // mmax+presum[i]-presum[i-l] represents M subarray before L subarray
        res = utils.Max(res, utils.Max(lmax+preSum[i]-preSum[i-m], mmax+preSum[i]-preSum[i-l]))
    }
    return res
}
<|endoftext|>


<commit_before>// Package std contains standard helpers.
package std

import (
	"fmt"
	"io/ioutil"
	"log"
	"strings"

	yaml "gopkg.in/yaml.v2"

	"github.com/mh-cbon/emd/emd"
	"github.com/mh-cbon/emd/utils"
)

// Cat displays a file header, returns file body.
func Cat(g *emd.Generator) func(string) (string, error) {
	return func(f string) (string, error) {
		s, err := Read(f)
		if err != nil {
			return "", err
		}
		pre := g.GetSKey("emd_cat_pre")
		_, err = g.WriteString(pre + f + "\n")
		return strings.TrimSpace(string(s)), err
	}
}

// Read returns file body.
func Read(f string) (string, error) {
	s, err := ioutil.ReadFile(f)
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(s)), err
}

// makeMapOf given arguments.
func makeMapOf(keyValuesMap ...interface{}) (map[string]interface{}, error) {
	ret := map[string]interface{}{}
	if len(keyValuesMap) > 0 {
		if len(keyValuesMap)%2 != 0 {
			return ret, fmt.Errorf("Incorrect arguments number in call to render template function, args are: %#v", keyValuesMap)
		}
		for i := 0; i < len(keyValuesMap); i += 2 {
			key, ok := keyValuesMap[i].(string)
			if ok == false {
				return ret, fmt.Errorf("Incorrect key type %T of arg %#v in call to render template function, expected a string, args are: %#v",
					keyValuesMap[i],
					keyValuesMap[i],
					keyValuesMap)
			}
			ret[key] = keyValuesMap[i+1]
		}
	}
	return ret, nil
}

// Render renders template wih given name, and map arguments to its data argument.
func Render(g *emd.Generator) func(string, map[string]interface{}, ...interface{}) (string, error) {
	return func(name string, data map[string]interface{}, keyValuesMap ...interface{}) (string, error) {
		extraData := map[string]interface{}{}
		for k, v := range data {
			extraData[k] = v
		}
		y, err := makeMapOf(keyValuesMap...)
		if err != nil {
			return "", err
		}
		for k, v := range y {
			extraData[k] = v
		}
		return "", g.GetTemplate().ExecuteTemplate(g.GetOut(), name, extraData)
	}
}

// Exec display a program invokation header, returns its output.
func Exec(g *emd.Generator) func(string, ...string) (string, error) {
	return func(bin string, args ...string) (string, error) {
		out, err := utils.Exec(bin, args)
		if err != nil {
			return "", err
		}

		f := utils.GetCmdStr(bin, args)
		pre := g.GetSKey("emd_exec_pre")
		_, err = g.WriteString(pre + f + "\n")

		return strings.TrimSpace(out), err
	}
}

// Shell display a cli invokation header, returns the cli output.
func Shell(g *emd.Generator) func(string) (string, error) {
	return func(s string) (string, error) {
		out, err := utils.Shell("", s)
		if err != nil {
			return "", err
		}

		pre := g.GetSKey("emd_shell_pre")
		_, err = g.WriteString(pre + s + "\n")

		return strings.TrimSpace(string(out)), err
	}
}

// Color embeds a text block with markdown triple backquotes syntax makrup.
func Color(syntax, content string) string {
	if content == "" && syntax != "" {
		content = syntax
		syntax = "sh" // set the default color
	}
	return fmt.Sprintf("```%v\n%v\n```", syntax, content)
}

// Yaml parses given file as yaml, locate given path, build a new map, yaml encode it, returns its string.
func Yaml(file string, paths ...string) (string, error) {
	s, err := ioutil.ReadFile(file)
	if err != nil {
		return "", err
	}

	m := yaml.MapSlice{}
	err = yaml.Unmarshal(s, &m)
	if err != nil {
		return "", err
	}

	res := yaml.MapSlice{}
	if len(paths) > 0 {
		// make it more complex later
		for _, p := range paths {
			for _, k := range m {
				if k.Key.(string) == p {
					res = append(res, k)

				}
			}
		}
	} else {
		res = m
	}

	var d []byte
	d, err = yaml.Marshal(&res)
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(d)), nil
}

// Preline prepends every line of content with pre.
func Preline(pre, content string) string {
	res := ""
	for _, c := range content {
		res += string(c)
		if c == '\n' {
			res += pre
		}
	}
	if res != "" {
		res = pre + res
	}
	return res
}

// Echo prints given strings.
func Echo(content ...string) string {
	return strings.Join(content, " ")
}

var replaceIndex = 0

// Toc generates and prints a TOC.
func Toc(g *emd.Generator) func(int, ...string) string {
	return func(depth int, toctitles ...string) string {
		toctitle := "TOC"
		if len(toctitles) > 0 {
			toctitle = toctitles[0]
		}
		replaceToken := fmt.Sprintf("%v%v", "REPLACETOKENGOESHERE", replaceIndex)
		replaceIndex++

		g.AddPostProcess(func(s string) string {
			// a quick and dirty md parser of titles (###) and block (```)
			lineIndex := utils.LineIndex(s, replaceToken)
			if lineIndex > -1 {

				lines := strings.Split(s, "\n")

				titles := utils.GetAllMdTitles(s)
				x := titles[:0]
				for _, t := range titles {
					if t.Line > lineIndex {
						x = append(x, t)
					}
				}
				root := utils.MakeTitleTree(x)
				toc := ""
				e := -1

				lastPower := -1
				root.Traverse(utils.PowerLess(5, func(n *utils.MdTitleTree) {
					if n.Power < 2 {
						e = 0
					} else if lastPower < n.Power {
						e++
					} else {
						e--
					}
					if e < 0 {
						e = 0
					}
					lastPower = n.Power

					link := utils.GetMdLinkHash(n.Title)
					if n.Duplicate > -1 {
						link += fmt.Sprintf("-%v", n.Duplicate)
					}
					x := strings.Repeat("  ", e)
					toc += fmt.Sprintf("%v- [%v](#%v)\n", x, n.Title, link)
				}))

				lines[lineIndex] = strings.Replace(lines[lineIndex], replaceToken, toctitle, -1)
				lines = append(lines[:lineIndex+1], lines[lineIndex:]...)
				lines[lineIndex+1] = strings.TrimRight(toc, "\n")
				return strings.Join(lines, "\n")
			}
			log.Println("weird, a toc was generated, but it was not added to the final content.")
			return s
		})
		return replaceToken
	}
}

// GHReleasePages is a template to show a notice about the gh releases page.
var GHReleasePages = `{{define "gh/releases" -}}
Check the [release page](https://github.com/{{.User}}/{{.Name}}/releases)!
{{- end}}`

// BadgeTravis is a template to show a travis badge.
var BadgeTravis = `{{define "badge/travis" -}}
[!` +
	`[travis Status]` +
	`(https://travis-ci.org/{{.User}}/{{.Name}}.svg?branch={{.Branch}})` +
	`]` +
	`(https://travis-ci.org/{{.User}}/{{.Name}})
{{- end}}`

// BadgeAppveyor is a template to show an appveyor badge.
var BadgeAppveyor = `{{define "badge/appveyor"}}
[!` +
	`[appveyor Status]` +
	`(https://ci.appveyor.com/api/projects/status/{{.ProviderName}}/{{.User}}/{{.Name}}?branch={{.Branch}}&svg=true)` +
	`]` +
	`(https://ci.appveyor.com/project/{{.User}}/{{.Name}})
{{- end}}`

// BadgeCodeship is a template to show a codehsip badge.
var BadgeCodeship = `{{define "badge/codeship" -}}
[!` +
	`[codeship Status]` +
	`(https://codeship.com/projects/{{.CsUUID}}/status?branch={{.Branch}})` +
	`]` +
	`(https://codeship.com/projects/{{.CsProjectID}})` + `
{{- end}}`

// InstructionChocoInstall is a template to show instructions to install the package with chocolatey.
var InstructionChocoInstall = `{{define "choco/install" -}}
` + "```sh" + `
choco install {{.Name}}
` + "```" + `
{{- end}}`

// InstructionGhRepo is a template to show instructions to install the rpm/deb repositories with gh-pages.
var InstructionGhRepo = `{{define "linux/gh_src_repo" -}}
` + "```sh" + `
wget -O - https://raw.githubusercontent.com/mh-cbon/latest/master/source.sh \
| GH={{.User}}/{{.Name}} sh -xe
# or
curl -L https://raw.githubusercontent.com/mh-cbon/latest/master/source.sh \
| GH={{.User}}/{{.Name}} sh -xe
` + "```" + `
{{- end}}`

// InstructionGhPkg is a template to show instructions to install the rpm/deb package with gh-pages.
var InstructionGhPkg = `{{define "linux/gh_pkg" -}}
` + "```sh" + `
curl -L https://raw.githubusercontent.com/mh-cbon/latest/master/install.sh \
| GH={{.User}}/{{.Name}} sh -xe
# or
wget -q -O - --no-check-certificate \
https://raw.githubusercontent.com/mh-cbon/latest/master/install.sh \
| GH={{.User}}/{{.Name}} sh -xe
` + "```" + `
{{- end}}`

// BadgeLicense shows a badge for a license.
var BadgeLicense = `{{define "license/shields" -}}
[!` +
	`[{{.License}} License]` +
	`(http://img.shields.io/badge/License-{{.License}}-{{or .LicenseColor "blue"}}.svg)` +
	`]` +
	`({{or .LicenseFile "LICENSE"}})
{{- end}}`

// Register standard helpers to the generator.
func Register(g *emd.Generator) error {

	g.AddFunc("cat", Cat(g))
	g.AddFunc("read", Read)
	g.AddFunc("render", Render(g))
	g.AddFunc("exec", Exec(g))
	g.AddFunc("shell", Shell(g))
	g.AddFunc("color", Color)
	g.AddFunc("toc", Toc(g))
	g.AddFunc("yaml", Yaml)
	g.AddFunc("preline", Preline)
	g.AddFunc("echo", Echo)

	g.AddTemplate(GHReleasePages)
	g.AddTemplate(BadgeTravis)
	g.AddTemplate(BadgeAppveyor)
	g.AddTemplate(BadgeCodeship)
	g.AddTemplate(InstructionChocoInstall)
	g.AddTemplate(InstructionGhRepo)
	g.AddTemplate(InstructionGhPkg)
	g.AddTemplate(BadgeLicense)

	return nil
}
<commit_msg>toc: one more fix about traversing and printing<commit_after>// Package std contains standard helpers.
package std

import (
	"fmt"
	"io/ioutil"
	"log"
	"strings"

	yaml "gopkg.in/yaml.v2"

	"github.com/mh-cbon/emd/emd"
	"github.com/mh-cbon/emd/utils"
)

// Cat displays a file header, returns file body.
func Cat(g *emd.Generator) func(string) (string, error) {
	return func(f string) (string, error) {
		s, err := Read(f)
		if err != nil {
			return "", err
		}
		pre := g.GetSKey("emd_cat_pre")
		_, err = g.WriteString(pre + f + "\n")
		return strings.TrimSpace(string(s)), err
	}
}

// Read returns file body.
func Read(f string) (string, error) {
	s, err := ioutil.ReadFile(f)
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(s)), err
}

// makeMapOf given arguments.
func makeMapOf(keyValuesMap ...interface{}) (map[string]interface{}, error) {
	ret := map[string]interface{}{}
	if len(keyValuesMap) > 0 {
		if len(keyValuesMap)%2 != 0 {
			return ret, fmt.Errorf("Incorrect arguments number in call to render template function, args are: %#v", keyValuesMap)
		}
		for i := 0; i < len(keyValuesMap); i += 2 {
			key, ok := keyValuesMap[i].(string)
			if ok == false {
				return ret, fmt.Errorf("Incorrect key type %T of arg %#v in call to render template function, expected a string, args are: %#v",
					keyValuesMap[i],
					keyValuesMap[i],
					keyValuesMap)
			}
			ret[key] = keyValuesMap[i+1]
		}
	}
	return ret, nil
}

// Render renders template wih given name, and map arguments to its data argument.
func Render(g *emd.Generator) func(string, map[string]interface{}, ...interface{}) (string, error) {
	return func(name string, data map[string]interface{}, keyValuesMap ...interface{}) (string, error) {
		extraData := map[string]interface{}{}
		for k, v := range data {
			extraData[k] = v
		}
		y, err := makeMapOf(keyValuesMap...)
		if err != nil {
			return "", err
		}
		for k, v := range y {
			extraData[k] = v
		}
		return "", g.GetTemplate().ExecuteTemplate(g.GetOut(), name, extraData)
	}
}

// Exec display a program invokation header, returns its output.
func Exec(g *emd.Generator) func(string, ...string) (string, error) {
	return func(bin string, args ...string) (string, error) {
		out, err := utils.Exec(bin, args)
		if err != nil {
			return "", err
		}

		f := utils.GetCmdStr(bin, args)
		pre := g.GetSKey("emd_exec_pre")
		_, err = g.WriteString(pre + f + "\n")

		return strings.TrimSpace(out), err
	}
}

// Shell display a cli invokation header, returns the cli output.
func Shell(g *emd.Generator) func(string) (string, error) {
	return func(s string) (string, error) {
		out, err := utils.Shell("", s)
		if err != nil {
			return "", err
		}

		pre := g.GetSKey("emd_shell_pre")
		_, err = g.WriteString(pre + s + "\n")

		return strings.TrimSpace(string(out)), err
	}
}

// Color embeds a text block with markdown triple backquotes syntax makrup.
func Color(syntax, content string) string {
	if content == "" && syntax != "" {
		content = syntax
		syntax = "sh" // set the default color
	}
	return fmt.Sprintf("```%v\n%v\n```", syntax, content)
}

// Yaml parses given file as yaml, locate given path, build a new map, yaml encode it, returns its string.
func Yaml(file string, paths ...string) (string, error) {
	s, err := ioutil.ReadFile(file)
	if err != nil {
		return "", err
	}

	m := yaml.MapSlice{}
	err = yaml.Unmarshal(s, &m)
	if err != nil {
		return "", err
	}

	res := yaml.MapSlice{}
	if len(paths) > 0 {
		// make it more complex later
		for _, p := range paths {
			for _, k := range m {
				if k.Key.(string) == p {
					res = append(res, k)

				}
			}
		}
	} else {
		res = m
	}

	var d []byte
	d, err = yaml.Marshal(&res)
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(d)), nil
}

// Preline prepends every line of content with pre.
func Preline(pre, content string) string {
	res := ""
	for _, c := range content {
		res += string(c)
		if c == '\n' {
			res += pre
		}
	}
	if res != "" {
		res = pre + res
	}
	return res
}

// Echo prints given strings.
func Echo(content ...string) string {
	return strings.Join(content, " ")
}

var replaceIndex = 0

// Toc generates and prints a TOC.
func Toc(g *emd.Generator) func(int, ...string) string {
	return func(depth int, toctitles ...string) string {
		toctitle := "TOC"
		if len(toctitles) > 0 {
			toctitle = toctitles[0]
		}
		replaceToken := fmt.Sprintf("%v%v", "REPLACETOKENGOESHERE", replaceIndex)
		replaceIndex++

		g.AddPostProcess(func(s string) string {
			// a quick and dirty md parser of titles (###) and block (```)
			lineIndex := utils.LineIndex(s, replaceToken)
			if lineIndex > -1 {

				lines := strings.Split(s, "\n")

				titles := utils.GetAllMdTitles(s)
				x := titles[:0]
				for _, t := range titles {
					if t.Line > lineIndex {
						x = append(x, t)
					}
				}
				root := utils.MakeTitleTree(x)
				toc := ""
				e := -1

				lastPower := -1
				root.Traverse(utils.PowerLess(5, func(n *utils.MdTitleTree) {
					if n.Power < 2 {
						e = 0
					} else if lastPower < n.Power {
						e++
					} else if lastPower > n.Power {
						e--
					}
					if e < 0 {
						e = 0
					}
					lastPower = n.Power

					link := utils.GetMdLinkHash(n.Title)
					if n.Duplicate > -1 {
						link += fmt.Sprintf("-%v", n.Duplicate)
					}
					x := strings.Repeat("  ", e)
					toc += fmt.Sprintf("%v- [%v](#%v)\n", x, n.Title, link)
				}))

				lines[lineIndex] = strings.Replace(lines[lineIndex], replaceToken, toctitle, -1)
				lines = append(lines[:lineIndex+1], lines[lineIndex:]...)
				lines[lineIndex+1] = strings.TrimRight(toc, "\n")
				return strings.Join(lines, "\n")
			}
			log.Println("weird, a toc was generated, but it was not added to the final content.")
			return s
		})
		return replaceToken
	}
}

// GHReleasePages is a template to show a notice about the gh releases page.
var GHReleasePages = `{{define "gh/releases" -}}
Check the [release page](https://github.com/{{.User}}/{{.Name}}/releases)!
{{- end}}`

// BadgeTravis is a template to show a travis badge.
var BadgeTravis = `{{define "badge/travis" -}}
[!` +
	`[travis Status]` +
	`(https://travis-ci.org/{{.User}}/{{.Name}}.svg?branch={{.Branch}})` +
	`]` +
	`(https://travis-ci.org/{{.User}}/{{.Name}})
{{- end}}`

// BadgeAppveyor is a template to show an appveyor badge.
var BadgeAppveyor = `{{define "badge/appveyor"}}
[!` +
	`[appveyor Status]` +
	`(https://ci.appveyor.com/api/projects/status/{{.ProviderName}}/{{.User}}/{{.Name}}?branch={{.Branch}}&svg=true)` +
	`]` +
	`(https://ci.appveyor.com/project/{{.User}}/{{.Name}})
{{- end}}`

// BadgeCodeship is a template to show a codehsip badge.
var BadgeCodeship = `{{define "badge/codeship" -}}
[!` +
	`[codeship Status]` +
	`(https://codeship.com/projects/{{.CsUUID}}/status?branch={{.Branch}})` +
	`]` +
	`(https://codeship.com/projects/{{.CsProjectID}})` + `
{{- end}}`

// InstructionChocoInstall is a template to show instructions to install the package with chocolatey.
var InstructionChocoInstall = `{{define "choco/install" -}}
` + "```sh" + `
choco install {{.Name}}
` + "```" + `
{{- end}}`

// InstructionGhRepo is a template to show instructions to install the rpm/deb repositories with gh-pages.
var InstructionGhRepo = `{{define "linux/gh_src_repo" -}}
` + "```sh" + `
wget -O - https://raw.githubusercontent.com/mh-cbon/latest/master/source.sh \
| GH={{.User}}/{{.Name}} sh -xe
# or
curl -L https://raw.githubusercontent.com/mh-cbon/latest/master/source.sh \
| GH={{.User}}/{{.Name}} sh -xe
` + "```" + `
{{- end}}`

// InstructionGhPkg is a template to show instructions to install the rpm/deb package with gh-pages.
var InstructionGhPkg = `{{define "linux/gh_pkg" -}}
` + "```sh" + `
curl -L https://raw.githubusercontent.com/mh-cbon/latest/master/install.sh \
| GH={{.User}}/{{.Name}} sh -xe
# or
wget -q -O - --no-check-certificate \
https://raw.githubusercontent.com/mh-cbon/latest/master/install.sh \
| GH={{.User}}/{{.Name}} sh -xe
` + "```" + `
{{- end}}`

// BadgeLicense shows a badge for a license.
var BadgeLicense = `{{define "license/shields" -}}
[!` +
	`[{{.License}} License]` +
	`(http://img.shields.io/badge/License-{{.License}}-{{or .LicenseColor "blue"}}.svg)` +
	`]` +
	`({{or .LicenseFile "LICENSE"}})
{{- end}}`

// Register standard helpers to the generator.
func Register(g *emd.Generator) error {

	g.AddFunc("cat", Cat(g))
	g.AddFunc("read", Read)
	g.AddFunc("render", Render(g))
	g.AddFunc("exec", Exec(g))
	g.AddFunc("shell", Shell(g))
	g.AddFunc("color", Color)
	g.AddFunc("toc", Toc(g))
	g.AddFunc("yaml", Yaml)
	g.AddFunc("preline", Preline)
	g.AddFunc("echo", Echo)

	g.AddTemplate(GHReleasePages)
	g.AddTemplate(BadgeTravis)
	g.AddTemplate(BadgeAppveyor)
	g.AddTemplate(BadgeCodeship)
	g.AddTemplate(InstructionChocoInstall)
	g.AddTemplate(InstructionGhRepo)
	g.AddTemplate(InstructionGhPkg)
	g.AddTemplate(BadgeLicense)

	return nil
}
<|endoftext|>


<commit_before>package relay

import (
	"fmt"
	"net"

	ic "github.com/libp2p/go-libp2p-crypto"
	iconn "github.com/libp2p/go-libp2p-interface-conn"
	inet "github.com/libp2p/go-libp2p-net"
	peer "github.com/libp2p/go-libp2p-peer"
	pstore "github.com/libp2p/go-libp2p-peerstore"
	tpt "github.com/libp2p/go-libp2p-transport"
	ma "github.com/multiformats/go-multiaddr"
	manet "github.com/multiformats/go-multiaddr-net"
)

type Conn struct {
	inet.Stream
	remote    pstore.PeerInfo
	transport tpt.Transport
}

var _ iconn.Conn = (*Conn)(nil)

type NetAddr struct {
	Relay  string
	Remote string
}

func (n *NetAddr) Network() string {
	return "libp2p-circuit-relay"
}

func (n *NetAddr) String() string {
	return fmt.Sprintf("relay[%s-%s]", n.Remote, n.Relay)
}

func (c *Conn) RemoteAddr() net.Addr {
	return &NetAddr{
		Relay:  c.Conn().RemotePeer().Pretty(),
		Remote: c.remote.ID.Pretty(),
	}
}

func (c *Conn) RemoteMultiaddr() ma.Multiaddr {
	a, err := ma.NewMultiaddr(fmt.Sprintf("/ipfs/%s/p2p-circuit", c.Conn().RemotePeer().Pretty()))
	if err != nil {
		panic(err)
	}
	return a
}

func (c *Conn) LocalMultiaddr() ma.Multiaddr {
	return c.Conn().LocalMultiaddr()
}

func (c *Conn) LocalAddr() net.Addr {
	na, err := manet.ToNetAddr(c.Conn().LocalMultiaddr())
	if err != nil {
		log.Error("failed to convert local multiaddr to net addr:", err)
		return nil
	}
	return na
}

func (c *Conn) Transport() tpt.Transport {
	return c.transport
}

func (c *Conn) LocalPeer() peer.ID {
	return c.Conn().LocalPeer()
}

func (c *Conn) RemotePeer() peer.ID {
	return c.remote.ID
}

func (c *Conn) LocalPrivateKey() ic.PrivKey {
	return nil
}

func (c *Conn) RemotePublicKey() ic.PubKey {
	return nil
}

func (c *Conn) ID() string {
	return iconn.ID(c)
}
<commit_msg>Revert "Report partial circuit address in Conn.RemoteMultiaddr"<commit_after>package relay

import (
	"fmt"
	"net"

	ic "github.com/libp2p/go-libp2p-crypto"
	iconn "github.com/libp2p/go-libp2p-interface-conn"
	inet "github.com/libp2p/go-libp2p-net"
	peer "github.com/libp2p/go-libp2p-peer"
	pstore "github.com/libp2p/go-libp2p-peerstore"
	tpt "github.com/libp2p/go-libp2p-transport"
	ma "github.com/multiformats/go-multiaddr"
	manet "github.com/multiformats/go-multiaddr-net"
)

type Conn struct {
	inet.Stream
	remote    pstore.PeerInfo
	transport tpt.Transport
}

var _ iconn.Conn = (*Conn)(nil)

type NetAddr struct {
	Relay  string
	Remote string
}

func (n *NetAddr) Network() string {
	return "libp2p-circuit-relay"
}

func (n *NetAddr) String() string {
	return fmt.Sprintf("relay[%s-%s]", n.Remote, n.Relay)
}

func (c *Conn) RemoteAddr() net.Addr {
	return &NetAddr{
		Relay:  c.Conn().RemotePeer().Pretty(),
		Remote: c.remote.ID.Pretty(),
	}
}

func (c *Conn) RemoteMultiaddr() ma.Multiaddr {
	a, err := ma.NewMultiaddr(fmt.Sprintf("/ipfs/%s/p2p-circuit/ipfs/%s", c.Conn().RemotePeer().Pretty(), c.remote.ID.Pretty()))
	if err != nil {
		panic(err)
	}
	return a
}

func (c *Conn) LocalMultiaddr() ma.Multiaddr {
	return c.Conn().LocalMultiaddr()
}

func (c *Conn) LocalAddr() net.Addr {
	na, err := manet.ToNetAddr(c.Conn().LocalMultiaddr())
	if err != nil {
		log.Error("failed to convert local multiaddr to net addr:", err)
		return nil
	}
	return na
}

func (c *Conn) Transport() tpt.Transport {
	return c.transport
}

func (c *Conn) LocalPeer() peer.ID {
	return c.Conn().LocalPeer()
}

func (c *Conn) RemotePeer() peer.ID {
	return c.remote.ID
}

func (c *Conn) LocalPrivateKey() ic.PrivKey {
	return nil
}

func (c *Conn) RemotePublicKey() ic.PubKey {
	return nil
}

func (c *Conn) ID() string {
	return iconn.ID(c)
}
<|endoftext|>


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

// Package k8s abstracts all Kubernetes specific behaviour
package k8s

import (
	"context"
	"fmt"
	"os"
	"time"

	"github.com/cilium/cilium/pkg/backoff"
	cilium_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
	"github.com/cilium/cilium/pkg/k8s/types"
	k8sversion "github.com/cilium/cilium/pkg/k8s/version"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/node"
	"github.com/cilium/cilium/pkg/option"
	"github.com/cilium/cilium/pkg/source"

	"github.com/sirupsen/logrus"
	apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
)

const (
	nodeRetrievalMaxRetries = 15
)

func waitForNodeInformation(nodeName string) *node.Node {
	backoff := backoff.Exponential{
		Min:    time.Duration(200) * time.Millisecond,
		Factor: 2.0,
		Name:   "k8s-node-retrieval",
	}

	for retry := 0; retry < nodeRetrievalMaxRetries; retry++ {
		n, err := retrieveNodeInformation(nodeName)
		if err != nil {
			log.WithError(err).Warning("Waiting for k8s node information")
			backoff.Wait(context.TODO())
			continue
		}

		return n
	}

	return nil
}

func retrieveNodeInformation(nodeName string) (*node.Node, error) {
	requireIPv4CIDR := option.Config.K8sRequireIPv4PodCIDR
	requireIPv6CIDR := option.Config.K8sRequireIPv6PodCIDR

	k8sNode, err := GetNode(Client(), nodeName)
	if err != nil {
		// If no CIDR is required, retrieving the node information is
		// optional
		if !requireIPv4CIDR && !requireIPv6CIDR {
			return nil, nil
		}

		return nil, fmt.Errorf("unable to retrieve k8s node information: %s", err)

	}

	nodeInterface := ConvertToNode(k8sNode)
	if nodeInterface == nil {
		// This will never happen and the GetNode on line 63 will be soon
		// make a request from the local store instead.
		return nil, fmt.Errorf("invalid k8s node: %s", k8sNode)
	}
	typesNode := nodeInterface.(*types.Node)

	// The source is left unspecified as this node resource should never be
	// used to update state
	n := ParseNode(typesNode, source.Unspec)
	log.WithField(logfields.NodeName, n.Name).Info("Retrieved node information from kubernetes")

	if requireIPv4CIDR && n.IPv4AllocCIDR == nil {
		return nil, fmt.Errorf("required IPv4 pod CIDR not present in node resource")
	}

	if requireIPv6CIDR && n.IPv6AllocCIDR == nil {
		return nil, fmt.Errorf("required IPv6 pod CIDR not present in node resource")
	}

	return n, nil
}

// useNodeCIDR sets the ipv4-range and ipv6-range values values from the
// addresses defined in the given node.
func useNodeCIDR(n *node.Node) {
	if n.IPv4AllocCIDR != nil && option.Config.EnableIPv4 {
		node.SetIPv4AllocRange(n.IPv4AllocCIDR)
	}
	if n.IPv6AllocCIDR != nil && option.Config.EnableIPv6 {
		if err := node.SetIPv6NodeRange(n.IPv6AllocCIDR.IPNet); err != nil {
			log.WithError(err).WithFields(logrus.Fields{
				logfields.Node:     n.Name,
				logfields.V6Prefix: n.IPv6AllocCIDR,
			}).Warn("k8s: Can't use IPv6 CIDR range from k8s")
		}
	}
}

// Init initializes the Kubernetes package. It is required to call Configure()
// beforehand.
func Init() error {
	if err := createDefaultClient(); err != nil {
		return fmt.Errorf("unable to create k8s client: %s", err)
	}

	if err := createDefaultCiliumClient(); err != nil {
		return fmt.Errorf("unable to create cilium k8s client: %s", err)
	}

	if err := k8sversion.Update(Client()); err != nil {
		return err
	}

	if !k8sversion.Capabilities().MinimalVersionMet {
		return fmt.Errorf("k8s version (%v) is not meeting the minimal requirement (%v)",
			k8sversion.Version(), k8sversion.MinimalVersionConstraint)
	}

	if nodeName := os.Getenv(EnvNodeNameSpec); nodeName != "" {
		// Use of the environment variable overwrites the node-name
		// automatically derived
		node.SetName(nodeName)

		if n := waitForNodeInformation(nodeName); n != nil {
			nodeIP4 := n.GetNodeIP(false)
			nodeIP6 := n.GetNodeIP(true)

			log.WithFields(logrus.Fields{
				logfields.NodeName:         n.Name,
				logfields.IPAddr + ".ipv4": nodeIP4,
				logfields.IPAddr + ".ipv6": nodeIP6,
				logfields.V4Prefix:         n.IPv4AllocCIDR,
				logfields.V6Prefix:         n.IPv6AllocCIDR,
			}).Info("Received own node information from API server")

			useNodeCIDR(n)

			// Note: Node IPs are derived regardless of
			// option.Config.EnableIPv4 and
			// option.Config.EnableIPv6. This is done to enable
			// underlay addressing to be different from overlay
			// addressing, e.g. an IPv6 only PodCIDR running over
			// IPv4 encapsulation.
			if nodeIP4 != nil {
				node.SetExternalIPv4(nodeIP4)
			}

			if nodeIP6 != nil {
				node.SetIPv6(nodeIP6)
			}
		} else {
			// if node resource could not be received, fail if
			// PodCIDR requirement has been requested
			if option.Config.K8sRequireIPv4PodCIDR || option.Config.K8sRequireIPv6PodCIDR {
				log.Fatal("Unable to derive PodCIDR from Kubernetes node resource, giving up")
			}
		}

		// Annotate addresses will occur later since the user might
		// want to specify them manually
	} else if option.Config.K8sRequireIPv4PodCIDR || option.Config.K8sRequireIPv6PodCIDR {
		return fmt.Errorf("node name must be specified via environment variable '%s' to retrieve Kubernetes PodCIDR range", EnvNodeNameSpec)
	}

	return nil
}

// RegisterCRDs registers all CRDs
func RegisterCRDs() error {
	if option.Config.SkipCRDCreation {
		return nil
	}

	restConfig, err := CreateConfig()
	if err != nil {
		return fmt.Errorf("Unable to create rest configuration: %s", err)
	}

	apiextensionsclientset, err := apiextensionsclient.NewForConfig(restConfig)
	if err != nil {
		return fmt.Errorf("Unable to create rest configuration for k8s CRD: %s", err)
	}

	err = cilium_v2.CreateCustomResourceDefinitions(apiextensionsclientset)
	if err != nil {
		return fmt.Errorf("Unable to create custom resource definition: %s", err)
	}

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

// Package k8s abstracts all Kubernetes specific behaviour
package k8s

import (
	"context"
	"fmt"
	"os"
	"time"

	"github.com/cilium/cilium/pkg/backoff"
	cilium_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
	"github.com/cilium/cilium/pkg/k8s/types"
	k8sversion "github.com/cilium/cilium/pkg/k8s/version"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/node"
	"github.com/cilium/cilium/pkg/option"
	"github.com/cilium/cilium/pkg/source"

	"github.com/sirupsen/logrus"
	apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
)

const (
	nodeRetrievalMaxRetries = 15
)

func waitForNodeInformation(ctx context.Context, nodeName string) *node.Node {
	backoff := backoff.Exponential{
		Min:    time.Duration(200) * time.Millisecond,
		Factor: 2.0,
		Name:   "k8s-node-retrieval",
	}

	for retry := 0; retry < nodeRetrievalMaxRetries; retry++ {
		n, err := retrieveNodeInformation(nodeName)
		if err != nil {
			log.WithError(err).Warning("Waiting for k8s node information")
			backoff.Wait(ctx)
			continue
		}

		return n
	}

	return nil
}

func retrieveNodeInformation(nodeName string) (*node.Node, error) {
	requireIPv4CIDR := option.Config.K8sRequireIPv4PodCIDR
	requireIPv6CIDR := option.Config.K8sRequireIPv6PodCIDR

	k8sNode, err := GetNode(Client(), nodeName)
	if err != nil {
		// If no CIDR is required, retrieving the node information is
		// optional
		if !requireIPv4CIDR && !requireIPv6CIDR {
			return nil, nil
		}

		return nil, fmt.Errorf("unable to retrieve k8s node information: %s", err)

	}

	nodeInterface := ConvertToNode(k8sNode)
	if nodeInterface == nil {
		// This will never happen and the GetNode on line 63 will be soon
		// make a request from the local store instead.
		return nil, fmt.Errorf("invalid k8s node: %s", k8sNode)
	}
	typesNode := nodeInterface.(*types.Node)

	// The source is left unspecified as this node resource should never be
	// used to update state
	n := ParseNode(typesNode, source.Unspec)
	log.WithField(logfields.NodeName, n.Name).Info("Retrieved node information from kubernetes")

	if requireIPv4CIDR && n.IPv4AllocCIDR == nil {
		return nil, fmt.Errorf("required IPv4 pod CIDR not present in node resource")
	}

	if requireIPv6CIDR && n.IPv6AllocCIDR == nil {
		return nil, fmt.Errorf("required IPv6 pod CIDR not present in node resource")
	}

	return n, nil
}

// useNodeCIDR sets the ipv4-range and ipv6-range values values from the
// addresses defined in the given node.
func useNodeCIDR(n *node.Node) {
	if n.IPv4AllocCIDR != nil && option.Config.EnableIPv4 {
		node.SetIPv4AllocRange(n.IPv4AllocCIDR)
	}
	if n.IPv6AllocCIDR != nil && option.Config.EnableIPv6 {
		if err := node.SetIPv6NodeRange(n.IPv6AllocCIDR.IPNet); err != nil {
			log.WithError(err).WithFields(logrus.Fields{
				logfields.Node:     n.Name,
				logfields.V6Prefix: n.IPv6AllocCIDR,
			}).Warn("k8s: Can't use IPv6 CIDR range from k8s")
		}
	}
}

// Init initializes the Kubernetes package. It is required to call Configure()
// beforehand.
func Init() error {
	if err := createDefaultClient(); err != nil {
		return fmt.Errorf("unable to create k8s client: %s", err)
	}

	if err := createDefaultCiliumClient(); err != nil {
		return fmt.Errorf("unable to create cilium k8s client: %s", err)
	}

	if err := k8sversion.Update(Client()); err != nil {
		return err
	}

	if !k8sversion.Capabilities().MinimalVersionMet {
		return fmt.Errorf("k8s version (%v) is not meeting the minimal requirement (%v)",
			k8sversion.Version(), k8sversion.MinimalVersionConstraint)
	}

	if nodeName := os.Getenv(EnvNodeNameSpec); nodeName != "" {
		// Use of the environment variable overwrites the node-name
		// automatically derived
		node.SetName(nodeName)

		if n := waitForNodeInformation(context.TODO(), nodeName); n != nil {
			nodeIP4 := n.GetNodeIP(false)
			nodeIP6 := n.GetNodeIP(true)

			log.WithFields(logrus.Fields{
				logfields.NodeName:         n.Name,
				logfields.IPAddr + ".ipv4": nodeIP4,
				logfields.IPAddr + ".ipv6": nodeIP6,
				logfields.V4Prefix:         n.IPv4AllocCIDR,
				logfields.V6Prefix:         n.IPv6AllocCIDR,
			}).Info("Received own node information from API server")

			useNodeCIDR(n)

			// Note: Node IPs are derived regardless of
			// option.Config.EnableIPv4 and
			// option.Config.EnableIPv6. This is done to enable
			// underlay addressing to be different from overlay
			// addressing, e.g. an IPv6 only PodCIDR running over
			// IPv4 encapsulation.
			if nodeIP4 != nil {
				node.SetExternalIPv4(nodeIP4)
			}

			if nodeIP6 != nil {
				node.SetIPv6(nodeIP6)
			}
		} else {
			// if node resource could not be received, fail if
			// PodCIDR requirement has been requested
			if option.Config.K8sRequireIPv4PodCIDR || option.Config.K8sRequireIPv6PodCIDR {
				log.Fatal("Unable to derive PodCIDR from Kubernetes node resource, giving up")
			}
		}

		// Annotate addresses will occur later since the user might
		// want to specify them manually
	} else if option.Config.K8sRequireIPv4PodCIDR || option.Config.K8sRequireIPv6PodCIDR {
		return fmt.Errorf("node name must be specified via environment variable '%s' to retrieve Kubernetes PodCIDR range", EnvNodeNameSpec)
	}

	return nil
}

// RegisterCRDs registers all CRDs
func RegisterCRDs() error {
	if option.Config.SkipCRDCreation {
		return nil
	}

	restConfig, err := CreateConfig()
	if err != nil {
		return fmt.Errorf("Unable to create rest configuration: %s", err)
	}

	apiextensionsclientset, err := apiextensionsclient.NewForConfig(restConfig)
	if err != nil {
		return fmt.Errorf("Unable to create rest configuration for k8s CRD: %s", err)
	}

	err = cilium_v2.CreateCustomResourceDefinitions(apiextensionsclientset)
	if err != nil {
		return fmt.Errorf("Unable to create custom resource definition: %s", err)
	}

	return nil
}
<|endoftext|>


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

package state

import (
	"fmt"
	"io"
	"os"
	"path"
	"time"

	"github.com/juju/errors"
	"github.com/juju/utils"
	"github.com/juju/utils/filestorage"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"gopkg.in/mgo.v2/txn"

	"github.com/juju/juju/environs/storage"
	"github.com/juju/juju/state/backups/metadata"
	"github.com/juju/juju/version"
)

/*
Backups are not a part of juju state nor of normal state operations.
However, they certainly are tightly coupled with state (the very
subject of backups).  This puts backups in an odd position,
particularly with regard to the storage of backup metadata and
archives.  As a result, here are a couple concerns worth mentioning.

First, as noted above backup is about state but not a part of state.
So exposing backup-related methods on State would imply the wrong
thing.  Thus the backup functionality here in the state package (not
state/backups) is exposed as functions to which you pass a state
object.

Second, backup creates an archive file containing a dump of state's
mongo DB.  Storing backup metadata/archives in mongo is thus a
somewhat circular proposition that has the potential to cause
problems.  That may need further attention.

Note that state (and juju as a whole) currently does not have a
persistence layer abstraction to facilitate separating different
persistence needs and implementations.  As a consequence, state's
data, whether about how an environment should look or about existing
resources within an environment, is dumped essentially straight into
State's mongo connection.  The code in the state package does not
make any distinction between the two (nor does the package clearly
distinguish between state-related abstractions and state-related
data).

Backup adds yet another category, merely taking advantage of
State's DB.  In the interest of making the distinction clear, the
code that directly interacts with State (and its DB) lives in this
file.  As mentioned previously, the functionality here is exposed
through functions that take State, rather than as methods on State.
Furthermore, the bulk of the backup-related code, which does not need
direct interaction with State, lives in the state/backups package.
*/

// backupMetadataDoc is a mirror of metadata.Metadata, used just for DB storage.
type backupMetadataDoc struct {
	ID             string `bson:"_id"`
	Started        int64  `bson:"started,minsize"`
	Finished       int64  `bson:"finished,minsize"`
	Checksum       string `bson:"checksum"`
	ChecksumFormat string `bson:"checksumformat"`
	Size           int64  `bson:"size,minsize"`
	Stored         bool   `bson:"stored"`
	Notes          string `bson:"notes,omitempty"`

	// origin
	Environment string         `bson:"environment"`
	Machine     string         `bson:"machine"`
	Hostname    string         `bson:"hostname"`
	Version     version.Number `bson:"version"`
}

func (doc *backupMetadataDoc) fileNotSet() bool {
	if doc.Finished != 0 {
		return false
	}
	if doc.Checksum != "" {
		return false
	}
	if doc.ChecksumFormat != "" {
		return false
	}
	if doc.Size != 0 {
		return false
	}
	return true
}

func (doc *backupMetadataDoc) validate() error {
	if doc.ID == "" {
		return errors.New("missing ID")
	}
	if doc.Started == 0 {
		return errors.New("missing Started")
	}
	if doc.Environment == "" {
		return errors.New("missing Environment")
	}
	if doc.Machine == "" {
		return errors.New("missing Machine")
	}
	if doc.Hostname == "" {
		return errors.New("missing Hostname")
	}
	if doc.Version.Major == 0 {
		return errors.New("missing Version")
	}

	// Check the file-related fields.
	if doc.fileNotSet() {
		if doc.Stored {
			return errors.New(`"Stored" flag is unexpectedly true`)
		}
		// Don't check the file-related fields.
		return nil
	}
	if doc.Finished == 0 {
		return errors.New("missing Finished")
	}
	if doc.Checksum == "" {
		return errors.New("missing Checksum")
	}
	if doc.ChecksumFormat == "" {
		return errors.New("missing ChecksumFormat")
	}
	if doc.Size == 0 {
		return errors.New("missing Size")
	}

	return nil
}

// asMetadata returns a new metadata.Metadata based on the backupMetadataDoc.
func (doc *backupMetadataDoc) asMetadata() *metadata.Metadata {
	// Create a new Metadata.
	origin := metadata.NewOriginFull(
		doc.Environment,
		doc.Machine,
		doc.Hostname,
		doc.Version,
	)

	started := time.Unix(doc.Started, 0).UTC()
	meta := metadata.NewMetadata(
		*origin,
		doc.Notes,
		&started,
	)

	// The ID is already set.
	meta.SetID(doc.ID)

	// Exit early if file-related fields not set.
	if doc.fileNotSet() {
		return meta
	}

	// Set the file-related fields.
	var finished *time.Time
	if doc.Finished != 0 {
		val := time.Unix(doc.Finished, 0).UTC()
		finished = &val
	}
	err := meta.Finish(doc.Size, doc.Checksum, doc.ChecksumFormat, finished)
	if err != nil {
		// The doc should have already been validated.  An error here
		// indicates that Metadata changed and backupMetadataDoc did not
		// accommodate the change.  Thus an error here indicates a
		// developer "error".  A caller should not need to worry about
		// that case so we panic instead of passing the error out.
		panic(fmt.Sprintf("unexpectedly invalid metadata doc: %v", err))
	}
	if doc.Stored {
		meta.SetStored()
	}
	return meta
}

// updateFromMetadata copies the corresponding data from the backup
// Metadata into the backupMetadataDoc.
func (doc *backupMetadataDoc) updateFromMetadata(metadata *metadata.Metadata) {
	finished := metadata.Finished()
	// Ignore metadata.ID.
	doc.Started = metadata.Started().Unix()
	if finished != nil {
		doc.Finished = finished.Unix()
	}
	doc.Checksum = metadata.Checksum()
	doc.ChecksumFormat = metadata.ChecksumFormat()
	doc.Size = metadata.Size()
	doc.Stored = metadata.Stored()
	doc.Notes = metadata.Notes()

	origin := metadata.Origin()
	doc.Environment = origin.Environment()
	doc.Machine = origin.Machine()
	doc.Hostname = origin.Hostname()
	doc.Version = origin.Version()
}

//---------------------------
// DB operations

// getBackupMetadata returns the backup metadata associated with "id".
// If "id" does not match any stored records, an error satisfying
// juju/errors.IsNotFound() is returned.
func getBackupMetadata(st *State, id string) (*metadata.Metadata, error) {
	collection, closer := st.getCollection(backupsMetaC)
	defer closer()

	var doc backupMetadataDoc
	// There can only be one!
	err := collection.FindId(id).One(&doc)
	if err == mgo.ErrNotFound {
		return nil, errors.NotFoundf("backup metadata %q", id)
	} else if err != nil {
		return nil, errors.Annotate(err, "error getting backup metadata")
	}

	if err := doc.validate(); err != nil {
		return nil, errors.Trace(err)
	}
	return doc.asMetadata(), nil
}

// addBackupMetadata stores metadata for a backup where it can be
// accessed later.  It returns a new ID that is associated with the
// backup.  If the provided metadata already has an ID set, it is
// ignored.
func addBackupMetadata(st *State, metadata *metadata.Metadata) (string, error) {
	// We use our own mongo _id value since the auto-generated one from
	// mongo may contain sensitive data (see bson.ObjectID).
	id, err := utils.NewUUID()
	if err != nil {
		return "", errors.Annotate(err, "error generating new ID")
	}
	idStr := id.String()
	return idStr, addBackupMetadataID(st, metadata, idStr)
}

func addBackupMetadataID(st *State, metadata *metadata.Metadata, id string) error {
	var doc backupMetadataDoc
	doc.updateFromMetadata(metadata)
	doc.ID = id
	if err := doc.validate(); err != nil {
		return errors.Trace(err)
	}

	ops := []txn.Op{{
		C:      backupsMetaC,
		Id:     doc.ID,
		Assert: txn.DocMissing,
		Insert: doc,
	}}
	if err := st.runTransaction(ops); err != nil {
		if err == txn.ErrAborted {
			return errors.AlreadyExistsf("backup metadata %q", doc.ID)
		}
		return errors.Annotate(err, "error running transaction")
	}

	return nil
}

// setBackupStored updates the backup metadata associated with "id"
// to indicate that a backup archive has been stored.  If "id" does
// not match any stored records, an error satisfying
// juju/errors.IsNotFound() is returned.
func setBackupStored(st *State, id string) error {
	ops := []txn.Op{{
		C:      backupsMetaC,
		Id:     id,
		Assert: txn.DocExists,
		Update: bson.D{{"$set", bson.D{
			{"stored", true},
		}}},
	}}
	if err := st.runTransaction(ops); err != nil {
		if err == txn.ErrAborted {
			return errors.NotFoundf(id)
		}
		return errors.Annotate(err, "error running transaction")
	}
	return nil
}

//---------------------------
// metadata storage

// NewBackupsOrigin returns a snapshot of where backup was run.  That
// snapshot is a new backup Origin value, for use in a backup's
// metadata.  Every value except for the machine name is populated
// either from juju state or some other implicit mechanism.
func NewBackupsOrigin(st *State, machine string) *metadata.Origin {
	// hostname could be derived from the environment...
	hostname, err := os.Hostname()
	if err != nil {
		// If os.Hostname() is not working, something is woefully wrong.
		// Run for the hills.
		panic(fmt.Sprintf("could not get hostname (system unstable?): %v", err))
	}
	origin := metadata.NewOrigin(
		st.EnvironTag().Id(),
		machine,
		hostname,
	)
	return origin
}

// Ensure we satisfy the interface.
var _ = filestorage.MetadataStorage((*backupMetadataStorage)(nil))

type backupMetadataStorage struct {
	state *State
}

func newBackupMetadataStorage(st *State) filestorage.MetadataStorage {
	stor := backupMetadataStorage{
		state: st,
	}
	return &stor
}

func (s *backupMetadataStorage) AddDoc(doc interface{}) (string, error) {
	metadata, ok := doc.(metadata.Metadata)
	if !ok {
		return "", errors.Errorf("doc must be of type state.backups.metadata.Metadata")
	}
	return addBackupMetadata(s.state, &metadata)
}

func (s *backupMetadataStorage) Doc(id string) (interface{}, error) {
	return s.Metadata(id)
}

func (s *backupMetadataStorage) Metadata(id string) (filestorage.Metadata, error) {
	metadata, err := getBackupMetadata(s.state, id)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return metadata, nil
}

func (s *backupMetadataStorage) ListDocs() ([]interface{}, error) {
	metas, err := s.ListMetadata()
	if err != nil {
		return nil, errors.Trace(err)
	}
	docs := []interface{}{}
	for _, meta := range metas {
		docs = append(docs, meta)
	}
	return docs, nil
}

func (s *backupMetadataStorage) ListMetadata() ([]filestorage.Metadata, error) {
	return nil, errors.Errorf("not implemented yet")
}

func (s *backupMetadataStorage) RemoveDoc(id string) error {
	return errors.Errorf("not implemented yet")
}

func (s *backupMetadataStorage) New() filestorage.Metadata {
	origin := NewBackupsOrigin(s.state, "")
	return metadata.NewMetadata(*origin, "", nil)
}

func (s *backupMetadataStorage) SetStored(meta filestorage.Metadata) error {
	err := setBackupStored(s.state, meta.ID())
	if err != nil {
		return errors.Trace(err)
	}
	meta.SetStored()
	return nil
}

//---------------------------
// raw file storage

const backupStorageRoot = "/"

// Ensure we satisfy the interface.
var _ = filestorage.RawFileStorage((*envFileStorage)(nil))

type envFileStorage struct {
	envStor storage.Storage
	root    string
}

func newBackupFileStorage(envStor storage.Storage, root string) filestorage.RawFileStorage {
	// Due to circular imports we cannot simply get the storage from
	// State using environs.GetStorage().
	stor := envFileStorage{
		envStor: envStor,
		root:    root,
	}
	return &stor
}

func (s *envFileStorage) path(id string) string {
	// Use of path.Join instead of filepath.Join is intentional - this
	// is an environment storage path not a filesystem path.
	return path.Join(s.root, id)
}

func (s *envFileStorage) File(id string) (io.ReadCloser, error) {
	return s.envStor.Get(s.path(id))
}

func (s *envFileStorage) AddFile(id string, file io.Reader, size int64) error {
	return s.envStor.Put(s.path(id), file, size)
}

func (s *envFileStorage) RemoveFile(id string) error {
	return s.envStor.Remove(s.path(id))
}

//---------------------------
// backup storage

// NewBackupsStorage returns a new FileStorage to use for storing backup
// archives (and metadata).
func NewBackupsStorage(st *State, envStor storage.Storage) filestorage.FileStorage {
	files := newBackupFileStorage(envStor, backupStorageRoot)
	docs := newBackupMetadataStorage(st)
	return filestorage.NewFileStorage(docs, files)
}
<commit_msg>Explain why there are unimplemented methods in backupMetadataStorage.<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package state

import (
	"fmt"
	"io"
	"os"
	"path"
	"time"

	"github.com/juju/errors"
	"github.com/juju/utils"
	"github.com/juju/utils/filestorage"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"gopkg.in/mgo.v2/txn"

	"github.com/juju/juju/environs/storage"
	"github.com/juju/juju/state/backups/metadata"
	"github.com/juju/juju/version"
)

/*
Backups are not a part of juju state nor of normal state operations.
However, they certainly are tightly coupled with state (the very
subject of backups).  This puts backups in an odd position,
particularly with regard to the storage of backup metadata and
archives.  As a result, here are a couple concerns worth mentioning.

First, as noted above backup is about state but not a part of state.
So exposing backup-related methods on State would imply the wrong
thing.  Thus the backup functionality here in the state package (not
state/backups) is exposed as functions to which you pass a state
object.

Second, backup creates an archive file containing a dump of state's
mongo DB.  Storing backup metadata/archives in mongo is thus a
somewhat circular proposition that has the potential to cause
problems.  That may need further attention.

Note that state (and juju as a whole) currently does not have a
persistence layer abstraction to facilitate separating different
persistence needs and implementations.  As a consequence, state's
data, whether about how an environment should look or about existing
resources within an environment, is dumped essentially straight into
State's mongo connection.  The code in the state package does not
make any distinction between the two (nor does the package clearly
distinguish between state-related abstractions and state-related
data).

Backup adds yet another category, merely taking advantage of
State's DB.  In the interest of making the distinction clear, the
code that directly interacts with State (and its DB) lives in this
file.  As mentioned previously, the functionality here is exposed
through functions that take State, rather than as methods on State.
Furthermore, the bulk of the backup-related code, which does not need
direct interaction with State, lives in the state/backups package.
*/

// backupMetadataDoc is a mirror of metadata.Metadata, used just for DB storage.
type backupMetadataDoc struct {
	ID             string `bson:"_id"`
	Started        int64  `bson:"started,minsize"`
	Finished       int64  `bson:"finished,minsize"`
	Checksum       string `bson:"checksum"`
	ChecksumFormat string `bson:"checksumformat"`
	Size           int64  `bson:"size,minsize"`
	Stored         bool   `bson:"stored"`
	Notes          string `bson:"notes,omitempty"`

	// origin
	Environment string         `bson:"environment"`
	Machine     string         `bson:"machine"`
	Hostname    string         `bson:"hostname"`
	Version     version.Number `bson:"version"`
}

func (doc *backupMetadataDoc) fileNotSet() bool {
	if doc.Finished != 0 {
		return false
	}
	if doc.Checksum != "" {
		return false
	}
	if doc.ChecksumFormat != "" {
		return false
	}
	if doc.Size != 0 {
		return false
	}
	return true
}

func (doc *backupMetadataDoc) validate() error {
	if doc.ID == "" {
		return errors.New("missing ID")
	}
	if doc.Started == 0 {
		return errors.New("missing Started")
	}
	if doc.Environment == "" {
		return errors.New("missing Environment")
	}
	if doc.Machine == "" {
		return errors.New("missing Machine")
	}
	if doc.Hostname == "" {
		return errors.New("missing Hostname")
	}
	if doc.Version.Major == 0 {
		return errors.New("missing Version")
	}

	// Check the file-related fields.
	if doc.fileNotSet() {
		if doc.Stored {
			return errors.New(`"Stored" flag is unexpectedly true`)
		}
		// Don't check the file-related fields.
		return nil
	}
	if doc.Finished == 0 {
		return errors.New("missing Finished")
	}
	if doc.Checksum == "" {
		return errors.New("missing Checksum")
	}
	if doc.ChecksumFormat == "" {
		return errors.New("missing ChecksumFormat")
	}
	if doc.Size == 0 {
		return errors.New("missing Size")
	}

	return nil
}

// asMetadata returns a new metadata.Metadata based on the backupMetadataDoc.
func (doc *backupMetadataDoc) asMetadata() *metadata.Metadata {
	// Create a new Metadata.
	origin := metadata.NewOriginFull(
		doc.Environment,
		doc.Machine,
		doc.Hostname,
		doc.Version,
	)

	started := time.Unix(doc.Started, 0).UTC()
	meta := metadata.NewMetadata(
		*origin,
		doc.Notes,
		&started,
	)

	// The ID is already set.
	meta.SetID(doc.ID)

	// Exit early if file-related fields not set.
	if doc.fileNotSet() {
		return meta
	}

	// Set the file-related fields.
	var finished *time.Time
	if doc.Finished != 0 {
		val := time.Unix(doc.Finished, 0).UTC()
		finished = &val
	}
	err := meta.Finish(doc.Size, doc.Checksum, doc.ChecksumFormat, finished)
	if err != nil {
		// The doc should have already been validated.  An error here
		// indicates that Metadata changed and backupMetadataDoc did not
		// accommodate the change.  Thus an error here indicates a
		// developer "error".  A caller should not need to worry about
		// that case so we panic instead of passing the error out.
		panic(fmt.Sprintf("unexpectedly invalid metadata doc: %v", err))
	}
	if doc.Stored {
		meta.SetStored()
	}
	return meta
}

// updateFromMetadata copies the corresponding data from the backup
// Metadata into the backupMetadataDoc.
func (doc *backupMetadataDoc) updateFromMetadata(metadata *metadata.Metadata) {
	finished := metadata.Finished()
	// Ignore metadata.ID.
	doc.Started = metadata.Started().Unix()
	if finished != nil {
		doc.Finished = finished.Unix()
	}
	doc.Checksum = metadata.Checksum()
	doc.ChecksumFormat = metadata.ChecksumFormat()
	doc.Size = metadata.Size()
	doc.Stored = metadata.Stored()
	doc.Notes = metadata.Notes()

	origin := metadata.Origin()
	doc.Environment = origin.Environment()
	doc.Machine = origin.Machine()
	doc.Hostname = origin.Hostname()
	doc.Version = origin.Version()
}

//---------------------------
// DB operations

// getBackupMetadata returns the backup metadata associated with "id".
// If "id" does not match any stored records, an error satisfying
// juju/errors.IsNotFound() is returned.
func getBackupMetadata(st *State, id string) (*metadata.Metadata, error) {
	collection, closer := st.getCollection(backupsMetaC)
	defer closer()

	var doc backupMetadataDoc
	// There can only be one!
	err := collection.FindId(id).One(&doc)
	if err == mgo.ErrNotFound {
		return nil, errors.NotFoundf("backup metadata %q", id)
	} else if err != nil {
		return nil, errors.Annotate(err, "error getting backup metadata")
	}

	if err := doc.validate(); err != nil {
		return nil, errors.Trace(err)
	}
	return doc.asMetadata(), nil
}

// addBackupMetadata stores metadata for a backup where it can be
// accessed later.  It returns a new ID that is associated with the
// backup.  If the provided metadata already has an ID set, it is
// ignored.
func addBackupMetadata(st *State, metadata *metadata.Metadata) (string, error) {
	// We use our own mongo _id value since the auto-generated one from
	// mongo may contain sensitive data (see bson.ObjectID).
	id, err := utils.NewUUID()
	if err != nil {
		return "", errors.Annotate(err, "error generating new ID")
	}
	idStr := id.String()
	return idStr, addBackupMetadataID(st, metadata, idStr)
}

func addBackupMetadataID(st *State, metadata *metadata.Metadata, id string) error {
	var doc backupMetadataDoc
	doc.updateFromMetadata(metadata)
	doc.ID = id
	if err := doc.validate(); err != nil {
		return errors.Trace(err)
	}

	ops := []txn.Op{{
		C:      backupsMetaC,
		Id:     doc.ID,
		Assert: txn.DocMissing,
		Insert: doc,
	}}
	if err := st.runTransaction(ops); err != nil {
		if err == txn.ErrAborted {
			return errors.AlreadyExistsf("backup metadata %q", doc.ID)
		}
		return errors.Annotate(err, "error running transaction")
	}

	return nil
}

// setBackupStored updates the backup metadata associated with "id"
// to indicate that a backup archive has been stored.  If "id" does
// not match any stored records, an error satisfying
// juju/errors.IsNotFound() is returned.
func setBackupStored(st *State, id string) error {
	ops := []txn.Op{{
		C:      backupsMetaC,
		Id:     id,
		Assert: txn.DocExists,
		Update: bson.D{{"$set", bson.D{
			{"stored", true},
		}}},
	}}
	if err := st.runTransaction(ops); err != nil {
		if err == txn.ErrAborted {
			return errors.NotFoundf(id)
		}
		return errors.Annotate(err, "error running transaction")
	}
	return nil
}

//---------------------------
// metadata storage

// NewBackupsOrigin returns a snapshot of where backup was run.  That
// snapshot is a new backup Origin value, for use in a backup's
// metadata.  Every value except for the machine name is populated
// either from juju state or some other implicit mechanism.
func NewBackupsOrigin(st *State, machine string) *metadata.Origin {
	// hostname could be derived from the environment...
	hostname, err := os.Hostname()
	if err != nil {
		// If os.Hostname() is not working, something is woefully wrong.
		// Run for the hills.
		panic(fmt.Sprintf("could not get hostname (system unstable?): %v", err))
	}
	origin := metadata.NewOrigin(
		st.EnvironTag().Id(),
		machine,
		hostname,
	)
	return origin
}

// Ensure we satisfy the interface.
var _ = filestorage.MetadataStorage((*backupMetadataStorage)(nil))

type backupMetadataStorage struct {
	state *State
}

func newBackupMetadataStorage(st *State) filestorage.MetadataStorage {
	stor := backupMetadataStorage{
		state: st,
	}
	return &stor
}

func (s *backupMetadataStorage) AddDoc(doc interface{}) (string, error) {
	metadata, ok := doc.(metadata.Metadata)
	if !ok {
		return "", errors.Errorf("doc must be of type state.backups.metadata.Metadata")
	}
	return addBackupMetadata(s.state, &metadata)
}

func (s *backupMetadataStorage) Doc(id string) (interface{}, error) {
	return s.Metadata(id)
}

func (s *backupMetadataStorage) Metadata(id string) (filestorage.Metadata, error) {
	metadata, err := getBackupMetadata(s.state, id)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return metadata, nil
}

func (s *backupMetadataStorage) ListDocs() ([]interface{}, error) {
	metas, err := s.ListMetadata()
	if err != nil {
		return nil, errors.Trace(err)
	}
	docs := []interface{}{}
	for _, meta := range metas {
		docs = append(docs, meta)
	}
	return docs, nil
}

func (s *backupMetadataStorage) ListMetadata() ([]filestorage.Metadata, error) {
	// This will be implemented when backups needs this functionality.
	// For now the method is stubbed out for the same of the
	// MetadataStorage interface.
	return nil, errors.Errorf("not implemented yet")
}

func (s *backupMetadataStorage) RemoveDoc(id string) error {
	// This will be implemented when backups needs this functionality.
	// For now the method is stubbed out for the same of the
	// MetadataStorage interface.
	return errors.Errorf("not implemented yet")
}

func (s *backupMetadataStorage) New() filestorage.Metadata {
	origin := NewBackupsOrigin(s.state, "")
	return metadata.NewMetadata(*origin, "", nil)
}

func (s *backupMetadataStorage) SetStored(meta filestorage.Metadata) error {
	err := setBackupStored(s.state, meta.ID())
	if err != nil {
		return errors.Trace(err)
	}
	meta.SetStored()
	return nil
}

//---------------------------
// raw file storage

const backupStorageRoot = "/"

// Ensure we satisfy the interface.
var _ = filestorage.RawFileStorage((*envFileStorage)(nil))

type envFileStorage struct {
	envStor storage.Storage
	root    string
}

func newBackupFileStorage(envStor storage.Storage, root string) filestorage.RawFileStorage {
	// Due to circular imports we cannot simply get the storage from
	// State using environs.GetStorage().
	stor := envFileStorage{
		envStor: envStor,
		root:    root,
	}
	return &stor
}

func (s *envFileStorage) path(id string) string {
	// Use of path.Join instead of filepath.Join is intentional - this
	// is an environment storage path not a filesystem path.
	return path.Join(s.root, id)
}

func (s *envFileStorage) File(id string) (io.ReadCloser, error) {
	return s.envStor.Get(s.path(id))
}

func (s *envFileStorage) AddFile(id string, file io.Reader, size int64) error {
	return s.envStor.Put(s.path(id), file, size)
}

func (s *envFileStorage) RemoveFile(id string) error {
	return s.envStor.Remove(s.path(id))
}

//---------------------------
// backup storage

// NewBackupsStorage returns a new FileStorage to use for storing backup
// archives (and metadata).
func NewBackupsStorage(st *State, envStor storage.Storage) filestorage.FileStorage {
	files := newBackupFileStorage(envStor, backupStorageRoot)
	docs := newBackupMetadataStorage(st)
	return filestorage.NewFileStorage(docs, files)
}
<|endoftext|>


<commit_before>package via

import (
	"debug/elf"
	"errors"
	"fmt"
	"github.com/str1ngs/util/json"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"sort"
	"time"
)

func strip(p string) error {
	ef, err := elf.Open(p)
	// if elf.Open fails then its not a elf file skip it.
	if err != nil {
		return nil
	}
	defer ef.Close()
	if verbose {
		fmt.Printf(lfmt, os.Getenv("STRIP"), base(p))
	}
	cmd := exec.Command("strip", p)
	return cmd.Run()
}

// Walk the package directory and make a file list.
// The resulting file list and plan data, is saved
// to manifest.json which then gets tar/gzipped into
// the package file.
//
// CreateManifest also perform strip and removale of
// blacklisted files.
func CreateManifest(dir string, plan *Plan) (err error) {
	var (
		size  int64
		mfile = join(dir, "manifest.json.gz")
		files = []string{}
	)
	walkFn := func(path string, fi os.FileInfo, err error) error {
		if path == dir {
			return nil
		}
		spath := path[len(dir)+1:]
		removes := append(config.Remove, plan.Remove...)
		// If the file is in config.Remove or plan.Removes delete it
		if contains(removes, spath) {
			err := os.RemoveAll(path)
			if err != nil {
				return err
			}
			fmt.Printf(lfmt, "removing", spath)
			if fi.IsDir() {
				return filepath.SkipDir
			}
			return nil
		}
		if fi.IsDir() {
			return nil
		}
		if err := strip(path); err != nil {
			return err
		}
		// We need to restat after strip.
		si, err := os.Lstat(path)
		if err != nil {
			return err
		}
		size += si.Size()
		files = append(files, spath)
		return nil
	}
	err = filepath.Walk(dir, walkFn)
	if err != nil {
		return err
	}
	plan.Files = files
	plan.Depends, err = Depends(dir, plan)
	if err != nil {
		return err
	}
	plan.Date = time.Now()
	plan.Size = size
	plan.Save()
	return json.WriteGz(&plan, mfile)
}

func filesContains(files []string, file string) bool {
	for _, f := range files {
		if base(f) == file {
			return true
		}
	}
	return false
}

func Depends(dir string, plan *Plan) ([]string, error) {
	depends := []string{}
	rfiles, err := ReadRepoFiles()
	if err != nil {
		return nil, err
	}
	for _, f := range plan.Files {
		n := needs(join(dir, f))
		if len(n) == 0 {
			continue
		}
		for _, d := range n {
			if filesContains(plan.Files, d) {
				// skip this file if this plan owns this file
				continue
			}
			owner := rfiles.Owns(d)
			if !contains(depends, owner) {
				depends = append(depends, owner)
			}
		}
	}
	sort.Strings(depends)
	return depends, nil
}

func needs(file string) []string {
	f, err := elf.Open(file)
	if err != nil {
		return nil
	}
	im, err := f.ImportedLibraries()
	if err != nil {
		return nil
	}
	return im
}

func ReadManifest(name string) (man *Plan, err error) {
	man = new(Plan)
	err = json.Read(man, path.Join(config.DB.Installed(), name, "manifest.json"))
	if err != nil {
		return
	}
	return
}

func Readelf(p string) error {
	f, err := elf.Open(p)
	if err != nil {
		return nil
	}
	defer f.Close()
	im, err := f.ImportedLibraries()
	if err != nil {
		return err
	}
	fmt.Printf(lfmt, "libs", im)
	sec := f.Section(".interp")
	d, err := sec.Data()
	if err != nil {
		return err
	}
	fmt.Printf(lfmt, "intr", string(d))
	ds := f.SectionByType(elf.SHT_DYNAMIC)
	d, err = ds.Data()
	if err != nil {
		return err
	}
	str, err := stringTable(f, ds.Link)
	if err != nil {
		return err
	}
	for len(d) > 0 {
		// TODO: add byteorder for ELFCLASS32
		tag := elf.DynTag(f.ByteOrder.Uint64(d[0:8]))
		val := uint64(f.ByteOrder.Uint64(d[8:16]))
		d = d[16:]
		if tag == elf.DT_RPATH {
			s, ok := getString(str, int(val))
			if ok {
				fmt.Printf(lfmt, "rpath", s)
			}
		}
	}
	return nil
}

// FIXME: These 2 functions are taken from GOROOT/src/pkg/elf.
// add license or request they be exported?
// getString extracts a string from an ELF string table.
func getString(section []byte, start int) (string, bool) {
	if start < 0 || start >= len(section) {
		return "", false
	}

	for end := start; end < len(section); end++ {
		if section[end] == 0 {
			return string(section[start:end]), true
		}
	}
	return "", false
}

// stringTable reads and returns the string table given by the
// specified link value.
func stringTable(f *elf.File, link uint32) ([]byte, error) {
	if link <= 0 || link >= uint32(len(f.Sections)) {
		return nil, errors.New("section has invalid string table link")
	}
	return f.Sections[link].Data()
}
<commit_msg>dont strip packages fix gcc packaging<commit_after>package via

import (
	"debug/elf"
	"errors"
	"fmt"
	"github.com/str1ngs/util/json"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"sort"
	"time"
)

func strip(p string) error {
	ef, err := elf.Open(p)
	// if elf.Open fails then its not a elf file skip it.
	if err != nil {
		return nil
	}
	defer ef.Close()
	if verbose {
		fmt.Printf(lfmt, os.Getenv("STRIP"), base(p))
	}
	cmd := exec.Command("strip", p)
	return cmd.Run()
}

// Walk the package directory and make a file list.
// The resulting file list and plan data, is saved
// to manifest.json which then gets tar/gzipped into
// the package file.
//
// CreateManifest also perform strip and removale of
// blacklisted files.
func CreateManifest(dir string, plan *Plan) (err error) {
	var (
		size  int64
		mfile = join(dir, "manifest.json.gz")
		files = []string{}
	)
	walkFn := func(path string, fi os.FileInfo, err error) error {
		if path == dir {
			return nil
		}
		spath := path[len(dir)+1:]
		removes := append(config.Remove, plan.Remove...)
		// If the file is in config.Remove or plan.Removes delete it
		if contains(removes, spath) {
			err := os.RemoveAll(path)
			if err != nil {
				return err
			}
			fmt.Printf(lfmt, "removing", spath)
			if fi.IsDir() {
				return filepath.SkipDir
			}
			return nil
		}
		if fi.IsDir() {
			return nil
		}
		//FIXME: stripping breaks gcc packaging
		//if err := strip(path); err != nil {
			//return err
		//}
		// We need to restat after strip.
		si, err := os.Lstat(path)
		if err != nil {
			return err
		}
		size += si.Size()
		files = append(files, spath)
		return nil
	}
	err = filepath.Walk(dir, walkFn)
	if err != nil {
		return err
	}
	plan.Files = files
	plan.Depends, err = Depends(dir, plan)
	if err != nil {
		return err
	}
	plan.Date = time.Now()
	plan.Size = size
	plan.Save()
	return json.WriteGz(&plan, mfile)
}

func filesContains(files []string, file string) bool {
	for _, f := range files {
		if base(f) == file {
			return true
		}
	}
	return false
}

func Depends(dir string, plan *Plan) ([]string, error) {
	depends := []string{}
	rfiles, err := ReadRepoFiles()
	if err != nil {
		return nil, err
	}
	for _, f := range plan.Files {
		n := needs(join(dir, f))
		if len(n) == 0 {
			continue
		}
		for _, d := range n {
			if filesContains(plan.Files, d) {
				// skip this file if this plan owns this file
				continue
			}
			owner := rfiles.Owns(d)
			if !contains(depends, owner) {
				depends = append(depends, owner)
			}
		}
	}
	sort.Strings(depends)
	return depends, nil
}

func needs(file string) []string {
	f, err := elf.Open(file)
	if err != nil {
		return nil
	}
	im, err := f.ImportedLibraries()
	if err != nil {
		return nil
	}
	return im
}

func ReadManifest(name string) (man *Plan, err error) {
	man = new(Plan)
	err = json.Read(man, path.Join(config.DB.Installed(), name, "manifest.json"))
	if err != nil {
		return
	}
	return
}

func Readelf(p string) error {
	f, err := elf.Open(p)
	if err != nil {
		return nil
	}
	defer f.Close()
	im, err := f.ImportedLibraries()
	if err != nil {
		return err
	}
	fmt.Printf(lfmt, "libs", im)
	sec := f.Section(".interp")
	d, err := sec.Data()
	if err != nil {
		return err
	}
	fmt.Printf(lfmt, "intr", string(d))
	ds := f.SectionByType(elf.SHT_DYNAMIC)
	d, err = ds.Data()
	if err != nil {
		return err
	}
	str, err := stringTable(f, ds.Link)
	if err != nil {
		return err
	}
	for len(d) > 0 {
		// TODO: add byteorder for ELFCLASS32
		tag := elf.DynTag(f.ByteOrder.Uint64(d[0:8]))
		val := uint64(f.ByteOrder.Uint64(d[8:16]))
		d = d[16:]
		if tag == elf.DT_RPATH {
			s, ok := getString(str, int(val))
			if ok {
				fmt.Printf(lfmt, "rpath", s)
			}
		}
	}
	return nil
}

// FIXME: These 2 functions are taken from GOROOT/src/pkg/elf.
// add license or request they be exported?
// getString extracts a string from an ELF string table.
func getString(section []byte, start int) (string, bool) {
	if start < 0 || start >= len(section) {
		return "", false
	}

	for end := start; end < len(section); end++ {
		if section[end] == 0 {
			return string(section[start:end]), true
		}
	}
	return "", false
}

// stringTable reads and returns the string table given by the
// specified link value.
func stringTable(f *elf.File, link uint32) ([]byte, error) {
	if link <= 0 || link >= uint32(len(f.Sections)) {
		return nil, errors.New("section has invalid string table link")
	}
	return f.Sections[link].Data()
}
<|endoftext|>