<commit_before>package optigo

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

type actionType int

const (
	atINCREMENT actionType = iota
	atAPPEND
	atASSIGN
)

type dataType int

const (
	dtSTRING dataType = iota
	dtINTEGER
	dtFLOAT
	dtBOOLEAN
)

type option struct {
	name     string
	unary    bool
	dest     reflect.Value
	action   actionType
	dataType dataType
}

func (o *option) parseValue(val string) (interface{}, error) {
	switch o.dataType {
	case dtSTRING:
		return val, nil
	case dtINTEGER:
		if i, err := strconv.ParseInt(val, 10, 64); err == nil {
			return i, nil
		} else {
			return nil, err
		}
	case dtFLOAT:
		if f, err := strconv.ParseFloat(val, 64); err == nil {
			return f, nil
		} else {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("Unable to parse value: %s", val)
	}
}

type actions map[string]option

func parseAction(spec string, dest interface{}, actions map[string]option) error {
	unary := false
	var a actionType
	var t dataType
	if spec[len(spec)-1] == '+' {
		unary = true
		a = atINCREMENT
		spec = spec[0 : len(spec)-1]
	} else if spec[len(spec)-1] == '@' {
		a = atAPPEND
		spec = spec[0 : len(spec)-1]
	} else {
		a = atASSIGN
	}

	switch spec[len(spec)-2:] {
	case "=s":
		t = dtSTRING
		spec = spec[0 : len(spec)-2]
	case "=i":
		t = dtINTEGER
		spec = spec[0 : len(spec)-2]
	case "=f":
		t = dtFLOAT
		spec = spec[0 : len(spec)-2]
	default:
		if a == atINCREMENT {
			t = dtINTEGER
		} else {
			t = dtBOOLEAN
		}
		unary = true
	}

	if unary && a == atAPPEND {
		return fmt.Errorf("invalid spec, using @ to parse repeated options, but not specifying type with either =i =s or =f: %s", spec)
	}

	optionNames := strings.Split(spec, "|")
	name := optionNames[len(optionNames)-1]
	for _, opt := range optionNames {
		var dashName string
		if len(opt) == 1 {
			dashName = "-" + opt
		} else {
			dashName = "--" + opt
		}
		actions[dashName] = option{name, unary, reflect.ValueOf(dest), a, t}
	}
	return nil
}

func increment(val reflect.Value) reflect.Value {
	return reflect.ValueOf(int(val.Int()) + 1)
}

func push(arr reflect.Value, val interface{}) reflect.Value {
	return reflect.Append(arr, reflect.ValueOf(val))
}

func initResultMap(actions actions) map[string]interface{} {
	results := make(map[string]interface{})
	for _, opt := range actions {
		if opt.unary {
			if opt.action == atINCREMENT {
				results[opt.name] = int64(0)
			} else {
				results[opt.name] = false
			}
		} else {
			if opt.action == atAPPEND {
				switch opt.dataType {
				case dtSTRING:
					results[opt.name] = make([]string, 0)
				case dtINTEGER:
					results[opt.name] = make([]int64, 0)
				case dtFLOAT:
					results[opt.name] = make([]float64, 0)
				}
			} else {
				switch opt.dataType {
				case dtSTRING:
					results[opt.name] = ""
				case dtINTEGER:
					results[opt.name] = int64(0)
				case dtFLOAT:
					results[opt.name] = float64(0)
				case dtBOOLEAN:
					results[opt.name] = false
				}
			}
		}
	}
	return results
}

// OptionParser struct will contain the `Results` and `Args` after
// one of the Process routines is called.  A OptionParser object
// is created with either NewParser or NewInlineParser
type OptionParser struct {
	actions actions
	Results map[string]interface{}
	Args    []string
}

// NewParser generates an OptionParser object from the opts passed in.
// After calling OptionParser.Parser([]string) the option results will
// be stored in OptionParser.Results
func NewParser(opts []string) OptionParser {
	actions := make(actions)
	for _, spec := range opts {
		if err := parseAction(spec, nil, actions); err != nil {
			panic(err)
		}
	}
	results := initResultMap(actions)
	return OptionParser{actions, results, nil}
}

// NewDirectAssignParser generates an OptionParser object from the `opts` passed in.
// After calling OptionParser.Parser([]string) the options will be assigned directly
// to the references passed in `opts`.
func NewDirectAssignParser(opts map[string]interface{}) OptionParser {
	actions := make(actions)
	for spec, ref := range opts {
		if err := parseAction(spec, ref, actions); err != nil {
			panic(err)
		}
	}
	return OptionParser{actions, nil, nil}
}

// ProcessAll will parse all arguments in args.  If there are any
// arguments in args that start with '-' and are not known
// options then an error will be returned.  Any non-options will
// be available in OptionParser.Args.
func (o *OptionParser) ProcessAll(args []string) error {
	err := o.ProcessSome(args)
	if err != nil {
		return err
	} else {
		for _, opt := range o.Args {
			if opt[0] == '-' {
				return fmt.Errorf("Unknown option: %s", opt)
			}
		}
	}
	return nil
}

// ProcessSome will parse all known arguments in args.  Any non-options
// and unknown options will be available in OPtionParser.Args.  This
// can be used to implement multple pass options parsing, for example
// perhaps sub-commands options are parsed seperately from global options.
func (o *OptionParser) ProcessSome(args []string) error {
	o.Args = make([]string, 0)
	for len(args) > 0 {
		if args[0] == "--" {
			o.Args = append(o.Args, args[1:]...)
			return nil
		}
		if opt, ok := o.actions[args[0]]; ok {
			var value interface{}
			var err error
			if opt.unary {
				value = true
				args = args[1:]
			} else {
				if len(args) < 2 {
					return fmt.Errorf("missing argument value for option: --%s", opt.name)
				} else {
					if value, err = opt.parseValue(args[1]); err != nil {
						return err
					}
				}
				args = args[2:]
			}

			if opt.dest.IsValid() {
				switch opt.action {
				case atINCREMENT:
					opt.dest.Elem().Set(increment(opt.dest.Elem()))
				case atAPPEND:
					opt.dest.Elem().Set(push(opt.dest.Elem(), value))
				case atASSIGN:
					if opt.dest.Kind() == reflect.Func {
						t := reflect.TypeOf(opt.dest.Interface())
						if t.NumIn() == 1 {
							callbackArgs := make([]reflect.Value, 1)
							callbackArgs[0] = reflect.ValueOf(value)
							opt.dest.Call(callbackArgs)
						} else {
							opt.dest.Call(nil)
						}
					} else {
						opt.dest.Elem().Set(reflect.ValueOf(value))
					}
				}
			} else {
				switch opt.action {
				case atINCREMENT:
					o.Results[opt.name] = increment(reflect.ValueOf(o.Results[opt.name])).Interface()
				case atAPPEND:
					o.Results[opt.name] = push(reflect.ValueOf(o.Results[opt.name]), value).Interface()
				case atASSIGN:
					o.Results[opt.name] = reflect.ValueOf(value).Interface()
				}
			}
		} else {
			o.Args = append(o.Args, args[0])
			args = args[1:]
		}
	}
	return nil
}
<commit_msg>fix non-unique options fix for type conversion (float64 -> float32) add 2 param callbacks (option name, option value)<commit_after>package optigo

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

type actionType int

const (
	atINCREMENT actionType = iota
	atAPPEND
	atASSIGN
)

type dataType int

const (
	dtSTRING dataType = iota
	dtINTEGER
	dtFLOAT
	dtBOOLEAN
)

type option struct {
	name     string
	unary    bool
	dest     reflect.Value
	action   actionType
	dataType dataType
}

func (o *option) parseValue(val string) (interface{}, error) {
	switch o.dataType {
	case dtSTRING:
		return val, nil
	case dtINTEGER:
		if i, err := strconv.ParseInt(val, 10, 64); err == nil {
			return i, nil
		} else {
			return nil, err
		}
	case dtFLOAT:
		if f, err := strconv.ParseFloat(val, 64); err == nil {
			return f, nil
		} else {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("Unable to parse value: %s", val)
	}
}

type actions map[string]option

func parseAction(spec string, dest interface{}, actions map[string]option) error {
	unary := false
	var a actionType
	var t dataType
	if spec[len(spec)-1] == '+' {
		unary = true
		a = atINCREMENT
		spec = spec[0 : len(spec)-1]
	} else if spec[len(spec)-1] == '@' {
		a = atAPPEND
		spec = spec[0 : len(spec)-1]
	} else {
		a = atASSIGN
	}

	switch spec[len(spec)-2:] {
	case "=s":
		t = dtSTRING
		spec = spec[0 : len(spec)-2]
	case "=i":
		t = dtINTEGER
		spec = spec[0 : len(spec)-2]
	case "=f":
		t = dtFLOAT
		spec = spec[0 : len(spec)-2]
	default:
		if a == atINCREMENT {
			t = dtINTEGER
		} else {
			t = dtBOOLEAN
		}
		unary = true
	}

	if unary && a == atAPPEND {
		return fmt.Errorf("invalid spec, using @ to parse repeated options, but not specifying type with either =i =s or =f: %s", spec)
	}

	optionNames := strings.Split(spec, "|")
	name := optionNames[len(optionNames)-1]
	for _, opt := range optionNames {
		var dashName string
		if len(opt) == 1 {
			dashName = "-" + opt
		} else {
			dashName = "--" + opt
		}
		if _, ok := actions[dashName]; ok {
			return fmt.Errorf("invalid option spec: %s is not unique from %s", dashName, spec)
		}
		actions[dashName] = option{name, unary, reflect.ValueOf(dest), a, t}
	}
	return nil
}

func increment(val reflect.Value) reflect.Value {
	return reflect.ValueOf(val.Int() + 1)
}

func push(arr reflect.Value, val interface{}) reflect.Value {
	rVal := reflect.ValueOf(val)
	if rVal.Type() != arr.Type().Elem() {
		// The value type is not the same as the array value type
		// so try to convert the passed in value to the array value type
		newValPtr := reflect.New( arr.Type().Elem() )
		newValPtr.Elem().Set(rVal.Convert(arr.Type().Elem()))
		rVal = newValPtr.Elem()
	}
	return reflect.Append(arr, rVal)
}

func initResultMap(actions actions) map[string]interface{} {
	results := make(map[string]interface{})
	for _, opt := range actions {
		if opt.unary {
			if opt.action == atINCREMENT {
				results[opt.name] = int64(0)
			} else {
				results[opt.name] = false
			}
		} else {
			if opt.action == atAPPEND {
				switch opt.dataType {
				case dtSTRING:
					results[opt.name] = make([]string, 0)
				case dtINTEGER:
					results[opt.name] = make([]int64, 0)
				case dtFLOAT:
					results[opt.name] = make([]float64, 0)
				}
			} else {
				switch opt.dataType {
				case dtSTRING:
					results[opt.name] = ""
				case dtINTEGER:
					results[opt.name] = int64(0)
				case dtFLOAT:
					results[opt.name] = float64(0)
				case dtBOOLEAN:
					results[opt.name] = false
				}
			}
		}
	}
	return results
}

// OptionParser struct will contain the `Results` and `Args` after
// one of the Process routines is called.  A OptionParser object
// is created with either NewParser or NewInlineParser
type OptionParser struct {
	actions actions
	Results map[string]interface{}
	Args    []string
}

// NewParser generates an OptionParser object from the opts passed in.
// After calling OptionParser.Parser([]string) the option results will
// be stored in OptionParser.Results
func NewParser(opts []string) OptionParser {
	actions := make(actions)
	for _, spec := range opts {
		if err := parseAction(spec, nil, actions); err != nil {
			panic(err)
		}
	}
	results := initResultMap(actions)
	return OptionParser{actions, results, nil}
}

// NewDirectAssignParser generates an OptionParser object from the `opts` passed in.
// After calling OptionParser.Parser([]string) the options will be assigned directly
// to the references passed in `opts`.
func NewDirectAssignParser(opts map[string]interface{}) OptionParser {
	actions := make(actions)
	for spec, ref := range opts {
		if err := parseAction(spec, ref, actions); err != nil {
			panic(err)
		}
	}
	return OptionParser{actions, nil, nil}
}

// ProcessAll will parse all arguments in args.  If there are any
// arguments in args that start with '-' and are not known
// options then an error will be returned.  Any non-options will
// be available in OptionParser.Args.
func (o *OptionParser) ProcessAll(args []string) error {
	err := o.ProcessSome(args)
	if err != nil {
		return err
	} else {
		for _, opt := range o.Args {
			if opt[0] == '-' {
				return fmt.Errorf("Unknown option: %s", opt)
			}
		}
	}
	return nil
}

// ProcessSome will parse all known arguments in args.  Any non-options
// and unknown options will be available in OPtionParser.Args.  This
// can be used to implement multple pass options parsing, for example
// perhaps sub-commands options are parsed seperately from global options.
func (o *OptionParser) ProcessSome(args []string) error {
	o.Args = make([]string, 0)
	for len(args) > 0 {
		if args[0] == "--" {
			o.Args = append(o.Args, args[1:]...)
			return nil
		}
		if opt, ok := o.actions[args[0]]; ok {
			var value interface{}
			var err error
			if opt.unary {
				value = true
				args = args[1:]
			} else {
				if len(args) < 2 {
					return fmt.Errorf("missing argument value for option: --%s", opt.name)
				} else {
					if value, err = opt.parseValue(args[1]); err != nil {
						return err
					}
				}
				args = args[2:]
			}

			if opt.dest.IsValid() {
				switch opt.action {
				case atINCREMENT:
					opt.dest.Elem().Set(increment(opt.dest.Elem()))
				case atAPPEND:
					opt.dest.Elem().Set(push(opt.dest.Elem(), value))
				case atASSIGN:
					if opt.dest.Kind() == reflect.Func {
						t := reflect.TypeOf(opt.dest.Interface())
						var cbArgs []reflect.Value
						if t.NumIn() == 1 {
							cbArgs = make([]reflect.Value, 1)
							cbArgs[0] = reflect.ValueOf(value)
						} else if t.NumIn() == 2 {
							cbArgs = make([]reflect.Value, 2)
							cbArgs[0] = reflect.ValueOf(opt.name)
							cbArgs[1] = reflect.ValueOf(value)
						}
						opt.dest.Call(cbArgs)
					} else {
						opt.dest.Elem().Set(reflect.ValueOf(value))
					}
				}
			} else {
				switch opt.action {
				case atINCREMENT:
					o.Results[opt.name] = increment(reflect.ValueOf(o.Results[opt.name])).Interface()
				case atAPPEND:
					o.Results[opt.name] = push(reflect.ValueOf(o.Results[opt.name]), value).Interface()
				case atASSIGN:
					o.Results[opt.name] = reflect.ValueOf(value).Interface()
				}
			}
		} else {
			o.Args = append(o.Args, args[0])
			args = args[1:]
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package ndp

import (
	"encoding"
	"fmt"
	"io"
	"net"
)

// A Direction specifies the direction of an Option as a source or target.
type Direction int

// Possible Direction values.
const (
	Source Direction = 1
	Target Direction = 2
)

// An Option is a Neighbor Discovery Protocol option.
type Option interface {
	encoding.BinaryMarshaler
	encoding.BinaryUnmarshaler

	code() uint8
}

var _ Option = &LinkLayerAddress{}

// A LinkLayerAddress is a Source or Target Link-Layer Address option, as
// described in RFC 4861, Section 4.6.1.
type LinkLayerAddress struct {
	Direction Direction
	Addr      net.HardwareAddr
}

// TODO(mdlayher): deal with non-ethernet links and variable option length?

func (lla *LinkLayerAddress) code() byte { return byte(lla.Direction) }

// MarshalBinary implements Option.
func (lla *LinkLayerAddress) MarshalBinary() ([]byte, error) {
	if d := lla.Direction; d != Source && d != Target {
		return nil, fmt.Errorf("ndp: invalid link-layer address direction: %d", d)
	}

	if len(lla.Addr) != 6 {
		return nil, fmt.Errorf("ndp: invalid link-layer address: %q", lla.Addr.String())
	}

	b := make([]byte, 8)
	b[0] = lla.code()
	b[1] = 1
	copy(b[2:], lla.Addr)

	return b, nil
}

// UnmarshalBinary implements Option.
func (lla *LinkLayerAddress) UnmarshalBinary(b []byte) error {
	if len(b) < 8 {
		return io.ErrUnexpectedEOF
	}

	d := Direction(b[0])
	if d != Source && d != Target {
		return fmt.Errorf("ndp: invalid link-layer address direction: %d", d)
	}

	if l := b[1]; l != 1 {
		return fmt.Errorf("ndp: unexpected link-layer address option length: %d", l)
	}

	*lla = LinkLayerAddress{
		Direction: d,
		Addr:      make(net.HardwareAddr, 6),
	}

	copy(lla.Addr, b[2:])

	return nil
}
<commit_msg>ndp: named constants for some Option magic numbers<commit_after>package ndp

import (
	"encoding"
	"fmt"
	"io"
	"net"
)

const (
	// Length of a link-layer address for Ethernet networks.
	ethAddrLen = 6

	// The assumed NDP option length (in units of 8 bytes) for a source or
	// target link layer address option for Ethernet networks.
	llaOptLen = 1

	// Byte length values required for each type of valid Option.
	llaByteLen = 8
)

// A Direction specifies the direction of an Option as a source or target.
type Direction int

// Possible Direction values.
const (
	Source Direction = 1
	Target Direction = 2
)

// An Option is a Neighbor Discovery Protocol option.
type Option interface {
	encoding.BinaryMarshaler
	encoding.BinaryUnmarshaler

	code() uint8
}

var _ Option = &LinkLayerAddress{}

// A LinkLayerAddress is a Source or Target Link-Layer Address option, as
// described in RFC 4861, Section 4.6.1.
type LinkLayerAddress struct {
	Direction Direction
	Addr      net.HardwareAddr
}

// TODO(mdlayher): deal with non-ethernet links and variable option length?

func (lla *LinkLayerAddress) code() byte { return byte(lla.Direction) }

// MarshalBinary implements Option.
func (lla *LinkLayerAddress) MarshalBinary() ([]byte, error) {
	if d := lla.Direction; d != Source && d != Target {
		return nil, fmt.Errorf("ndp: invalid link-layer address direction: %d", d)
	}

	if len(lla.Addr) != ethAddrLen {
		return nil, fmt.Errorf("ndp: invalid link-layer address: %q", lla.Addr.String())
	}

	b := make([]byte, llaByteLen)
	b[0] = lla.code()
	b[1] = llaOptLen
	copy(b[2:], lla.Addr)

	return b, nil
}

// UnmarshalBinary implements Option.
func (lla *LinkLayerAddress) UnmarshalBinary(b []byte) error {
	if len(b) < llaByteLen {
		return io.ErrUnexpectedEOF
	}

	d := Direction(b[0])
	if d != Source && d != Target {
		return fmt.Errorf("ndp: invalid link-layer address direction: %d", d)
	}

	if l := b[1]; l != 1 {
		return fmt.Errorf("ndp: unexpected link-layer address option length: %d", l)
	}

	*lla = LinkLayerAddress{
		Direction: d,
		Addr:      make(net.HardwareAddr, 6),
	}

	copy(lla.Addr, b[2:])

	return nil
}
<|endoftext|>


<commit_before>package GOsu

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
)

// Game Types which can be accessed via GOsu.<GAMETYPE>.
const (
	OSU   = "0"
	TAIKO = "1"
	CTB   = "2"
	MANIA = "3"
)

// Beatmap Identifier Types for use with GetBeatmaps.
const (
	BEATMAPSET = "s"
	BEATMAPID  = "b"
	USERID     = "u"
)

// Osu Mods which are accessed by the GetMod function.
// iota moves the 'binary 1' to the left.
// e.g. NoFail = 1, Easy = 10 in binary which is 2.
// NoVideo = 100 in binary which is 4.
// This is done in order for any combination of mods to be enabled at once.
const (
	None   = 0
	NoFail = 1 << (iota - 1)
	Easy
	NoVideo
	Hidden
	HardRock
	SuddenDeath
	DoubleTime
	Relax
	HalfTime
	Nightcore
	Flashlight
	Autoplay
	SpunOut
	Relax2
	Perfect
	Key4
	Key5
	Key6
	Key7
	Key8
	FadeIn
	Random
	LastMod
)

var (
	API_URL           string = "https://osu.ppy.sh/api/"
	API_RECENT_PLAYS  string = "get_user_recent"
	API_GET_BEATMAPS  string = "get_beatmaps"
	API_GET_USER      string = "get_user"
	API_GET_SCORES    string = "get_scores"
	API_GET_USER_BEST string = "get_user_best"
	API_GET_MATCH     string = "get_match"
)

type Database struct {
	API_KEY string
}

type Beatmap struct {
	Beatmapset_ID     string
	Beatmap_ID        string
	Approved          string
	Approved_Date     string
	Last_Update       string
	Total_Length      string
	Hit_Length        string
	Version           string
	Artist            string
	Title             string
	Creator           string
	Bpm               string
	Source            string
	Difficulty_Rating string
	Diff_Size         string
	Diff_Overall      string
	Diff_Approach     string
	Diff_Drain        string
	Mode              string
}

type Song struct {
	Beatmap_ID   string
	Score        string
	MaxCombo     string
	Count50      string
	Count100     string
	Count300     string
	CountMiss    string
	CountKatu    string
	CountGeki    string
	Perfect      string
	Enabled_Mods string
	User_ID      string
	Date         string
	Rank         string
}

type User struct {
	User_ID       string
	Username      string
	Count300      string
	Count100      string
	Count50       string
	PlayCount     string
	Ranked_Score  string
	Total_Score   string
	PP_Rank       string
	Level         string
	PP_Raw        string
	Accuracy      string
	Count_Rank_SS string
	Count_Rank_S  string
	Count_Rank_A  string
	Country       string
	Events        []Event
}

type Event struct {
	Display_HTML  string
	Beatmap_ID    string
	Beatmapset_ID string
	Date          string
	EpicFactor    string
}

type Score struct {
	Score        string
	Username     string
	MaxCombo     string
	Count50      string
	Count100     string
	Count300     string
	CountMiss    string
	CountKatu    string
	CountGeki    string
	Perfect      string
	Enabled_Mods string
	User_ID      string
	Date         string
	Rank         string
	PP           string
}

type PPSong struct {
	Beatmap_ID   string
	Score        string
	MaxCombo     string
	Count50      string
	Count100     string
	Count300     string
	CountMiss    string
	CountKatu    string
	CountGeki    string
	Perfect      string
	Enabled_Mods string
	User_ID      string
	Date         string
	Rank         string
	PP           string
}

type Game struct {
	Match MPMatch
	Games []MPGame
}

type MPMatch struct {
	Match_ID   string
	Name       string
	Start_Time string
	End_Time   string
}

type MPGame struct {
	Game_ID      string
	Start_Time   string
	End_Time     string
	Beatmap_ID   string
	Play_Mode    string
	Match_Type   string
	Scoring_Type string
	Team_Type    string
	Mods         string
	Scores       []MPScore
}

type MPScore struct {
	Slot      string
	Team      string
	User_ID   string
	Score     string
	MaxCombo  string
	Rank      string
	Count50   string
	Count100  string
	Count300  string
	CountMiss string
	CountGeki string
	CountKatu string
	Perfect   string
	Pass      string
}

func (d *Database) SetAPIKey(API_KEY string) error {
	var err error
	if len(API_KEY) <= 1 {
		err = errors.New("API Key: invalid/non-existant API key.")
		return err
	} else {
		d.API_KEY = API_KEY
	}

	return err
}

func (d Database) BuildRecentURL(USER_ID string, GAME_TYPE string) string {
	return API_URL + API_RECENT_PLAYS + "?k=" + d.API_KEY + "&u=" + USER_ID + "&m=" + GAME_TYPE + "&limit=50"
}

func (d Database) BuildBeatmapURL(ID string, TYPE string) string {
	return API_URL + API_GET_BEATMAPS + "?k=" + d.API_KEY + "&" + TYPE + "=" + ID
}

func (d Database) BuildUserURL(USER_ID string, GAME_TYPE string, DAYS string) string {
	return API_URL + API_GET_USER + "?k=" + d.API_KEY + "&u=" + USER_ID + "&m=" + GAME_TYPE + "&event_days=" + DAYS
}

func (d Database) BuildUserBestURL(USER_ID string, GAME_TYPE string) string {
	return API_URL + API_GET_USER_BEST + "?k=" + d.API_KEY + "&u=" + USER_ID + "&m=" + GAME_TYPE
}

func (d Database) BuildScoreURL(BEATMAP_ID string, USER_ID string, GAME_TYPE string) string {
	return API_URL + API_GET_SCORES + "?k=" + d.API_KEY + "&b=" + BEATMAP_ID + "&m=" + GAME_TYPE + "&u=" + USER_ID
}

func (d Database) BuildMatchURL(MATCH_ID string) string {
	return API_URL + API_GET_MATCH + "?k=" + d.API_KEY + "&mp=" + MATCH_ID
}

func RetrieveHTML(URL string) ([]byte, error) {
	res, err := http.Get(URL)

	if err != nil {
		return nil, errors.New("HTTP: Could not open a connection to the Osu! API server.")
	}

	defer res.Body.Close()

	html, err := ioutil.ReadAll(res.Body)

	if err != nil {
		return nil, errors.New("HTML: Could not read the HTML page grabbed.")
	}

	return html, err
}

func (d Database) GetUser(USER_ID string, GAME_TYPE string, DAYS string) ([]User, error) {
	var user []User
	url := d.BuildUserURL(USER_ID, GAME_TYPE, DAYS)
	html, err := RetrieveHTML(url)

	if err != nil {
		return user, err
	}

	err = json.Unmarshal(html, &user)

	if err != nil {
		return user, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return user, err
}

func (d Database) GetBeatmaps(ID string, BM_TYPE string) ([]Beatmap, error) {
	var beatmaps []Beatmap
	url := d.BuildBeatmapURL(ID, BM_TYPE)
	html, err := RetrieveHTML(url)

	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(html, &beatmaps)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return beatmaps, err
}

func (d Database) GetRecentPlays(USER_ID string, GAME_TYPE string) ([]Song, error) {
	var songs []Song
	url := d.BuildRecentURL(USER_ID, GAME_TYPE)
	html, err := RetrieveHTML(url)

	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(html, &songs)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return songs, err
}

func (d Database) GetScores(BEATMAP_ID string, USER_ID string, GAME_TYPE string) ([]Score, error) {
	var scores []Score
	url := d.BuildScoreURL(BEATMAP_ID, USER_ID, GAME_TYPE)
	html, err := RetrieveHTML(url)

	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(html, &scores)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return scores, err
}

func (d Database) GetUserBest(USER_ID string, GAME_TYPE string) ([]PPSong, error) {
	var songs []PPSong
	url := d.BuildUserBestURL(USER_ID, GAME_TYPE)
	html, err := RetrieveHTML(url)

	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(html, &songs)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return songs, err
}

func (d Database) GetMatch(MATCH_ID string) (Game, error) {
	var game Game
	url := d.BuildMatchURL(MATCH_ID)
	html, err := RetrieveHTML(url)

	if err != nil {
		return game, err
	}

	err = json.Unmarshal(html, &game)

	if err != nil {
		return game, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return game, err
}

// ONLY A TEMPORARY FUNCTION.
// Use this function if you are behind a proxy/corporate network and want to work off a local file.
// It will serve as a local HTML file for you to test the website.
func GetLocalPlays(path string) ([]Song, error) {
	var songs []Song

	html, err := ioutil.ReadFile(path)

	if err != nil {
		return nil, errors.New("HTML: Could not read the local HTML page properly.")
	}

	err = json.Unmarshal(html, &songs)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the local HTML page, most likely due to not being in the right format.")
	}

	return songs, err
}

func GetMods(mods int) []string {
	var resultantMods []string

	if mods == 0 {
		resultantMods = append(resultantMods, "None")
		return resultantMods
	}

	AddMod(&resultantMods, &mods, LastMod, "Last Mod")
	AddMod(&resultantMods, &mods, Random, "Random")
	AddMod(&resultantMods, &mods, FadeIn, "Fade In")
	AddMod(&resultantMods, &mods, Key8, "Key 8")
	AddMod(&resultantMods, &mods, Key7, "Key 7")
	AddMod(&resultantMods, &mods, Key6, "Key 6")
	AddMod(&resultantMods, &mods, Key5, "Key 5")
	AddMod(&resultantMods, &mods, Key4, "Key 4")
	AddMod(&resultantMods, &mods, Perfect, "Perfect")
	AddMod(&resultantMods, &mods, Relax2, "Relax 2")
	AddMod(&resultantMods, &mods, SpunOut, "Spun Out")
	AddMod(&resultantMods, &mods, Autoplay, "Autoplay")
	AddMod(&resultantMods, &mods, Flashlight, "Flashlight")
	AddMod(&resultantMods, &mods, Nightcore, "Nightcore")
	AddMod(&resultantMods, &mods, HalfTime, "Half Time")
	AddMod(&resultantMods, &mods, Relax, "Relax")
	AddMod(&resultantMods, &mods, DoubleTime, "Double Time")
	AddMod(&resultantMods, &mods, SuddenDeath, "Sudden Death")
	AddMod(&resultantMods, &mods, HardRock, "Hard Rock")
	AddMod(&resultantMods, &mods, Hidden, "Hidden")
	AddMod(&resultantMods, &mods, NoVideo, "No Video")
	AddMod(&resultantMods, &mods, Easy, "Easy")
	AddMod(&resultantMods, &mods, NoFail, "No Fail")

	return resultantMods
}

func AddMod(array *[]string, remainingMods *int, mod int, modName string) {
	if mod <= *remainingMods {
		*remainingMods -= mod
		*array = append(*array, modName)
	}
}
<commit_msg>Add new entry.<commit_after>package GOsu

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
)

// Game Types which can be accessed via GOsu.<GAMETYPE>.
const (
	OSU   = "0"
	TAIKO = "1"
	CTB   = "2"
	MANIA = "3"
)

// Beatmap Identifier Types for use with GetBeatmaps.
const (
	BEATMAPSET = "s"
	BEATMAPID  = "b"
	USERID     = "u"
)

// Osu Mods which are accessed by the GetMod function.
// iota moves the 'binary 1' to the left.
// e.g. NoFail = 1, Easy = 10 in binary which is 2.
// NoVideo = 100 in binary which is 4.
// This is done in order for any combination of mods to be enabled at once.
const (
	None   = 0
	NoFail = 1 << (iota - 1)
	Easy
	NoVideo
	Hidden
	HardRock
	SuddenDeath
	DoubleTime
	Relax
	HalfTime
	Nightcore
	Flashlight
	Autoplay
	SpunOut
	Relax2
	Perfect
	Key4
	Key5
	Key6
	Key7
	Key8
	FadeIn
	Random
	LastMod
)

var (
	API_URL           string = "https://osu.ppy.sh/api/"
	API_RECENT_PLAYS  string = "get_user_recent"
	API_GET_BEATMAPS  string = "get_beatmaps"
	API_GET_USER      string = "get_user"
	API_GET_SCORES    string = "get_scores"
	API_GET_USER_BEST string = "get_user_best"
	API_GET_MATCH     string = "get_match"
)

type Database struct {
	API_KEY string
}

type Beatmap struct {
	Beatmapset_ID     string
	Beatmap_ID        string
	Approved          string
	Approved_Date     string
	Last_Update       string
	Total_Length      string
	Hit_Length        string
	Version           string
	Artist            string
	Title             string
	Creator           string
	Bpm               string
	Source            string
	Difficulty_Rating string
	Diff_Size         string
	Diff_Overall      string
	Diff_Approach     string
	Diff_Drain        string
	Mode              string
}

type Song struct {
	Beatmap_ID   string
	Score        string
	MaxCombo     string
	Count50      string
	Count100     string
	Count300     string
	CountMiss    string
	CountKatu    string
	CountGeki    string
	Perfect      string
	Enabled_Mods string
	User_ID      string
	Date         string
	Rank         string
}

type User struct {
	User_ID       string
	Username      string
	Count300      string
	Count100      string
	Count50       string
	PlayCount     string
	Ranked_Score  string
	Total_Score   string
	PP_Rank       string
	Level         string
	PP_Raw        string
	Accuracy      string
	Count_Rank_SS string
	Count_Rank_S  string
	Count_Rank_A  string
	Country       string
	Country_Rank  string
	Events        []Event
}

type Event struct {
	Display_HTML  string
	Beatmap_ID    string
	Beatmapset_ID string
	Date          string
	EpicFactor    string
}

type Score struct {
	Score        string
	Username     string
	MaxCombo     string
	Count50      string
	Count100     string
	Count300     string
	CountMiss    string
	CountKatu    string
	CountGeki    string
	Perfect      string
	Enabled_Mods string
	User_ID      string
	Date         string
	Rank         string
	PP           string
}

type PPSong struct {
	Beatmap_ID   string
	Score        string
	MaxCombo     string
	Count50      string
	Count100     string
	Count300     string
	CountMiss    string
	CountKatu    string
	CountGeki    string
	Perfect      string
	Enabled_Mods string
	User_ID      string
	Date         string
	Rank         string
	PP           string
}

type Game struct {
	Match MPMatch
	Games []MPGame
}

type MPMatch struct {
	Match_ID   string
	Name       string
	Start_Time string
	End_Time   string
}

type MPGame struct {
	Game_ID      string
	Start_Time   string
	End_Time     string
	Beatmap_ID   string
	Play_Mode    string
	Match_Type   string
	Scoring_Type string
	Team_Type    string
	Mods         string
	Scores       []MPScore
}

type MPScore struct {
	Slot      string
	Team      string
	User_ID   string
	Score     string
	MaxCombo  string
	Rank      string
	Count50   string
	Count100  string
	Count300  string
	CountMiss string
	CountGeki string
	CountKatu string
	Perfect   string
	Pass      string
}

func (d *Database) SetAPIKey(API_KEY string) error {
	var err error
	if len(API_KEY) <= 1 {
		err = errors.New("API Key: invalid/non-existant API key.")
		return err
	} else {
		d.API_KEY = API_KEY
	}

	return err
}

func (d Database) BuildRecentURL(USER_ID string, GAME_TYPE string) string {
	return API_URL + API_RECENT_PLAYS + "?k=" + d.API_KEY + "&u=" + USER_ID + "&m=" + GAME_TYPE + "&limit=50"
}

func (d Database) BuildBeatmapURL(ID string, TYPE string) string {
	return API_URL + API_GET_BEATMAPS + "?k=" + d.API_KEY + "&" + TYPE + "=" + ID
}

func (d Database) BuildUserURL(USER_ID string, GAME_TYPE string, DAYS string) string {
	return API_URL + API_GET_USER + "?k=" + d.API_KEY + "&u=" + USER_ID + "&m=" + GAME_TYPE + "&event_days=" + DAYS
}

func (d Database) BuildUserBestURL(USER_ID string, GAME_TYPE string) string {
	return API_URL + API_GET_USER_BEST + "?k=" + d.API_KEY + "&u=" + USER_ID + "&m=" + GAME_TYPE
}

func (d Database) BuildScoreURL(BEATMAP_ID string, USER_ID string, GAME_TYPE string) string {
	return API_URL + API_GET_SCORES + "?k=" + d.API_KEY + "&b=" + BEATMAP_ID + "&m=" + GAME_TYPE + "&u=" + USER_ID
}

func (d Database) BuildMatchURL(MATCH_ID string) string {
	return API_URL + API_GET_MATCH + "?k=" + d.API_KEY + "&mp=" + MATCH_ID
}

func RetrieveHTML(URL string) ([]byte, error) {
	res, err := http.Get(URL)

	if err != nil {
		return nil, errors.New("HTTP: Could not open a connection to the Osu! API server.")
	}

	defer res.Body.Close()

	html, err := ioutil.ReadAll(res.Body)

	if err != nil {
		return nil, errors.New("HTML: Could not read the HTML page grabbed.")
	}

	return html, err
}

func (d Database) GetUser(USER_ID string, GAME_TYPE string, DAYS string) ([]User, error) {
	var user []User
	url := d.BuildUserURL(USER_ID, GAME_TYPE, DAYS)
	html, err := RetrieveHTML(url)

	if err != nil {
		return user, err
	}

	err = json.Unmarshal(html, &user)

	if err != nil {
		return user, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return user, err
}

func (d Database) GetBeatmaps(ID string, BM_TYPE string) ([]Beatmap, error) {
	var beatmaps []Beatmap
	url := d.BuildBeatmapURL(ID, BM_TYPE)
	html, err := RetrieveHTML(url)

	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(html, &beatmaps)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return beatmaps, err
}

func (d Database) GetRecentPlays(USER_ID string, GAME_TYPE string) ([]Song, error) {
	var songs []Song
	url := d.BuildRecentURL(USER_ID, GAME_TYPE)
	html, err := RetrieveHTML(url)

	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(html, &songs)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return songs, err
}

func (d Database) GetScores(BEATMAP_ID string, USER_ID string, GAME_TYPE string) ([]Score, error) {
	var scores []Score
	url := d.BuildScoreURL(BEATMAP_ID, USER_ID, GAME_TYPE)
	html, err := RetrieveHTML(url)

	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(html, &scores)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return scores, err
}

func (d Database) GetUserBest(USER_ID string, GAME_TYPE string) ([]PPSong, error) {
	var songs []PPSong
	url := d.BuildUserBestURL(USER_ID, GAME_TYPE)
	html, err := RetrieveHTML(url)

	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(html, &songs)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return songs, err
}

func (d Database) GetMatch(MATCH_ID string) (Game, error) {
	var game Game
	url := d.BuildMatchURL(MATCH_ID)
	html, err := RetrieveHTML(url)

	if err != nil {
		return game, err
	}

	err = json.Unmarshal(html, &game)

	if err != nil {
		return game, errors.New("JSON: Couldn't process the HTML into JSON. Possible wrong page/wrong API key/offline server.")
	}

	return game, err
}

// ONLY A TEMPORARY FUNCTION.
// Use this function if you are behind a proxy/corporate network and want to work off a local file.
// It will serve as a local HTML file for you to test the website.
func GetLocalPlays(path string) ([]Song, error) {
	var songs []Song

	html, err := ioutil.ReadFile(path)

	if err != nil {
		return nil, errors.New("HTML: Could not read the local HTML page properly.")
	}

	err = json.Unmarshal(html, &songs)

	if err != nil {
		return nil, errors.New("JSON: Couldn't process the local HTML page, most likely due to not being in the right format.")
	}

	return songs, err
}

func GetMods(mods int) []string {
	var resultantMods []string

	if mods == 0 {
		resultantMods = append(resultantMods, "None")
		return resultantMods
	}

	AddMod(&resultantMods, &mods, LastMod, "Last Mod")
	AddMod(&resultantMods, &mods, Random, "Random")
	AddMod(&resultantMods, &mods, FadeIn, "Fade In")
	AddMod(&resultantMods, &mods, Key8, "Key 8")
	AddMod(&resultantMods, &mods, Key7, "Key 7")
	AddMod(&resultantMods, &mods, Key6, "Key 6")
	AddMod(&resultantMods, &mods, Key5, "Key 5")
	AddMod(&resultantMods, &mods, Key4, "Key 4")
	AddMod(&resultantMods, &mods, Perfect, "Perfect")
	AddMod(&resultantMods, &mods, Relax2, "Relax 2")
	AddMod(&resultantMods, &mods, SpunOut, "Spun Out")
	AddMod(&resultantMods, &mods, Autoplay, "Autoplay")
	AddMod(&resultantMods, &mods, Flashlight, "Flashlight")
	AddMod(&resultantMods, &mods, Nightcore, "Nightcore")
	AddMod(&resultantMods, &mods, HalfTime, "Half Time")
	AddMod(&resultantMods, &mods, Relax, "Relax")
	AddMod(&resultantMods, &mods, DoubleTime, "Double Time")
	AddMod(&resultantMods, &mods, SuddenDeath, "Sudden Death")
	AddMod(&resultantMods, &mods, HardRock, "Hard Rock")
	AddMod(&resultantMods, &mods, Hidden, "Hidden")
	AddMod(&resultantMods, &mods, NoVideo, "No Video")
	AddMod(&resultantMods, &mods, Easy, "Easy")
	AddMod(&resultantMods, &mods, NoFail, "No Fail")

	return resultantMods
}

func AddMod(array *[]string, remainingMods *int, mod int, modName string) {
	if mod <= *remainingMods {
		*remainingMods -= mod
		*array = append(*array, modName)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"github.com/dynport/dgtk/pubsub"
	"github.com/dynport/urknall"
	"github.com/dynport/urknall/cmd"
	"github.com/dynport/urknall/fw"
	"github.com/dynport/urknall/pkg/nginx"
	"github.com/dynport/urknall/pkg/ruby"
	"log"
	"os"
	"text/template"
)

var ps = pubsub.New()

func main() {
	if len(os.Args) != 2 {
		log.Fatalf("USAGE: %s <IP>", os.Args[0])
	}

	// urknall does not do any logging publishes notifications for specific events to which
	// one can subscribe by registering a pubsub from github.com/dynport/dgtk/pubsub exchange with urknall.RegisterPubSub()
	// but there is also a default StdoutLogger() which can be activated like this:
	l, e := urknall.OpenStdoutLogger()
	if e != nil {
		log.Fatal(e.Error())
	}
	defer l.Close()

	// condfigure the host to provision
	host := &urknall.Host{
		IP: os.Args[1],
	}

	// upgrade all system packages
	host.AddCommands("package_upgrade", cmd.UpdatePackages())

	// use some standard packages
	nx := nginx.New("1.4.4")
	host.AddPackage("ruby", nx)

	rb := ruby.New("2.0.0-p353")
	host.AddPackage("nginx", rb)

	// install some custom system commands
	host.AddCommands("packages", cmd.InstallPackages("ngrep", "dnsutils", "whois"))

	// firewall setup: allow inbound http and https (currently ssh (22) is also allowed by default)
	host.Firewall = fw.Firewall{
		{
			Description: "Allow incoming http",
			Chain:       fw.ChainInput,
			Protocol:    fw.ProtocolTcp,
			Destination: &fw.Target{Port: fw.PortHttp},
		},
		{
			Description: "Allow incoming https",
			Chain:       fw.ChainInput,
			Protocol:    fw.ProtocolTcp,
			Destination: &fw.Target{Port: fw.PortHttps},
		},
	}

	app := &App{
		RubyInstallPath:  rb.InstallPath(),
		NginxInstallPath: nx.InstallPath(),
		SocketPath:       "/tmp/rack.socket",
		CurrentPath:      "/app/current",
	}
	host.AddPackage("app", app)

	// provision the host
	e = host.Provision(nil)
	if e != nil {
		log.Fatal(e.Error())
	}
}

type App struct {
	RubyInstallPath  string `urknall:"required=true"`
	NginxInstallPath string `urknall:"required=true"`
	SocketPath       string `urknall:"required=true"`
	CurrentPath      string `urknall:"required=true"`
}

func (app *App) Package(r *urknall.Runlist) {
	r.Add(
		"{{ .RubyInstallPath }}/bin/gem install puma --no-ri --no-rdoc", // is executed as plain bash command
		cmd.Mkdir("/app", "root", 0755),
		cmd.Mkdir("/app/public", "root", 0755),
		cmd.WriteFile(app.NginxInstallPath+"/conf/nginx.conf", nginxConfig, "root", 0644),
		"{ service nginx status && service nginx restart; } || service nginx start",
		cmd.WriteFile("/etc/init/app.conf", upstart, "root", 0755),
		cmd.WriteFile("/app/config.ru", configRu, "root", 0644),
		"{ service app status && service app restart; } || service app start",
	)
}

func mustRenderTemplate(src string, i interface{}) string {
	buf := &bytes.Buffer{}
	e := template.Must(template.New("nginx_config").Parse(nginxConfig)).Execute(buf, struct{ SocketPath, CurrentPath string }{"/tmp/rack.socket", "/app"})
	if e != nil {
		panic(e.Error())
	}
	return buf.String()
}

const upstart = `env PATH={{ .RubyInstallPath }}/bin:$PATH
chdir /app
exec puma -e production -b unix://{{ .SocketPath }} --pidfile /var/run/app.pid
`

const runSh = `
#!/bin/bash

export PATH={{ .RubyInstallPath }}/bin:$PATH
which puma || gem install puma --no-ri --no-rdoc
`

const configRu = `
app = lambda do |env|
  [200, { "Content-Type" => "text/plain" }, ["Hello from urknall!"]]
end

run app
`

const nginxConfig = `
syslog user nginx;

events {
  worker_connections  1024;
}

worker_processes 4;
pid /var/run/nginx.pid;

http {
  include mime.types;
  upstream rack {
      server unix:{{ .SocketPath }} fail_timeout=0;
  }

  log_format default
    'ip=$remote_addr forwarded=$http_x_forwarded_for host=$http_host method=$request_method status=$status length=$body_bytes_sent '
    'total=$request_time upstream_time=$upstream_response_time ua="$http_user_agent" uri="$request_uri" ref="$http_referer"';

  access_log syslog:notice default;
  error_log syslog:error;

  server {
    more_set_headers "Server: nginx";

    server_name _;
    listen 80;

    root {{ .CurrentPath }}/public;

    try_files $uri @rack;

    location @rack {
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      proxy_pass http://rack;
      proxy_temp_path /tmp/nginx;
    }
  }
}
`
<commit_msg>update example<commit_after>package main

import (
	"bytes"
	"github.com/dynport/urknall"
	"github.com/dynport/urknall/cmd"
	"github.com/dynport/urknall/fw"
	"github.com/dynport/urknall/pkg/nginx"
	"github.com/dynport/urknall/pkg/ruby"
	"log"
	"os"
	"text/template"
)

func main() {
	if len(os.Args) != 2 {
		log.Fatalf("USAGE: %s <IP>", os.Args[0])
	}

	// activate logging to stdout
	l, e := urknall.OpenStdoutLogger()
	if e != nil {
		log.Fatal(e.Error())
	}
	defer l.Close()

	// condfigure the host to provision
	host := &urknall.Host{
		IP: os.Args[1],
	}

	// upgrade all system packages
	host.AddCommands("package_upgrade", cmd.UpdatePackages())

	// use some standard packages
	nx := nginx.New("1.4.4")
	host.AddPackage("ruby", nx)

	rb := ruby.New("2.0.0-p353")
	host.AddPackage("nginx", rb)

	// install some custom system commands
	host.AddCommands("packages", cmd.InstallPackages("ngrep", "dnsutils", "whois"))

	// firewall setup: allow inbound http and https (currently ssh (22) is also allowed by default)
	host.Firewall = fw.Firewall{
		{
			Description: "Allow incoming http",
			Chain:       fw.ChainInput,
			Protocol:    fw.ProtocolTcp,
			Destination: &fw.Target{Port: fw.PortHttp},
		},
		{
			Description: "Allow incoming https",
			Chain:       fw.ChainInput,
			Protocol:    fw.ProtocolTcp,
			Destination: &fw.Target{Port: fw.PortHttps},
		},
	}

	// install a custom packag (must implement urknall.Package)
	host.AddPackage("app", &App{
		RubyInstallPath:  rb.InstallPath(),
		NginxInstallPath: nx.InstallPath(),
		SocketPath:       "/tmp/rack.socket",
		CurrentPath:      "/app/current",
	},
	)

	// provision the host
	e = host.Provision(nil)
	if e != nil {
		log.Fatal(e.Error())
	}
}

type App struct {
	RubyInstallPath  string `urknall:"required=true"`
	NginxInstallPath string `urknall:"required=true"`
	SocketPath       string `urknall:"required=true"`
	CurrentPath      string `urknall:"required=true"`
}

// all commands of packages accept go templates and can access the attributes of the package
func (app *App) Package(r *urknall.Runlist) {
	r.Add(
		"{{ .RubyInstallPath }}/bin/gem install puma --no-ri --no-rdoc", // is executed as plain bash command
		cmd.Mkdir("/app", "root", 0755),
		cmd.Mkdir("/app/public", "root", 0755),
		cmd.WriteFile(app.NginxInstallPath+"/conf/nginx.conf", nginxConfig, "root", 0644),
		"{ service nginx status && service nginx restart; } || service nginx start",
		cmd.WriteFile("/etc/init/app.conf", upstart, "root", 0755),
		cmd.WriteFile("/app/config.ru", configRu, "root", 0644),
		"{ service app status && service app restart; } || service app start",
	)
}

func mustRenderTemplate(src string, i interface{}) string {
	buf := &bytes.Buffer{}
	e := template.Must(template.New("nginx_config").Parse(nginxConfig)).Execute(buf, struct{ SocketPath, CurrentPath string }{"/tmp/rack.socket", "/app"})
	if e != nil {
		panic(e.Error())
	}
	return buf.String()
}

const upstart = `env PATH={{ .RubyInstallPath }}/bin:$PATH
chdir /app
exec puma -e production -b unix://{{ .SocketPath }} --pidfile /var/run/app.pid
`

const runSh = `
#!/bin/bash

export PATH={{ .RubyInstallPath }}/bin:$PATH
which puma || gem install puma --no-ri --no-rdoc
`

const configRu = `
app = lambda do |env|
  [200, { "Content-Type" => "text/plain" }, ["Hello from urknall!"]]
end

run app
`

const nginxConfig = `
syslog user nginx;

events {
  worker_connections  1024;
}

worker_processes 4;
pid /var/run/nginx.pid;

http {
  include mime.types;
  upstream rack {
      server unix:{{ .SocketPath }} fail_timeout=0;
  }

  log_format default
    'ip=$remote_addr forwarded=$http_x_forwarded_for host=$http_host method=$request_method status=$status length=$body_bytes_sent '
    'total=$request_time upstream_time=$upstream_response_time ua="$http_user_agent" uri="$request_uri" ref="$http_referer"';

  access_log syslog:notice default;
  error_log syslog:error;

  server {
    more_set_headers "Server: nginx";

    server_name _;
    listen 80;

    root {{ .CurrentPath }}/public;

    try_files $uri @rack;

    location @rack {
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      proxy_pass http://rack;
      proxy_temp_path /tmp/nginx;
    }
  }
}
`
<|endoftext|>


<commit_before>// Copyright 2016 Markus Lindenberg, Stig Bakken
//
// 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 main

import (
	"bufio"
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"regexp"
	"syscall"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
)

const (
	namespace = "varnish_request"
)

type path_mappings struct {
	Pattern     *regexp.Regexp
	Replacement string
}

func main() {
	var (
		listenAddress = flag.String("http.port", ":9151", "Host/port for HTTP server")
		metricsPath   = flag.String("http.metricsurl", "/metrics", "Prometheus metrics path")
		httpHost      = flag.String("varnish.host", "", "Virtual host to look for in Varnish logs (defaults to all hosts)")
		mappingsFile  = flag.String("varnish.path-mappings", "", "Name of file with path mappings")
		instance      = flag.String("varnish.instance", "", "Name of Varnish instance")
		befirstbyte   = flag.Bool("varnish.firstbyte", false, "Also export metrics for backend time to first byte")
	)
	flag.Parse()

	// Listen to signals
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGINT)

	// Set up 'varnishncsa' pipe
	cmdName := "varnishncsa"
	cmdArgs := buildVarnishncsaArgs(*httpHost, *instance, *befirstbyte)
	log.Infof("Running command: %v %v\n", cmdName, cmdArgs)
	cmd := exec.Command(cmdName, cmdArgs...)
	cmdReader, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}
	scanner := bufio.NewScanner(cmdReader)

	path_mappings, err := parseMappings(*mappingsFile)
	if err != nil {
		log.Fatal(err)
	}

	// Setup metrics
	varnishMessages := prometheus.NewCounter(prometheus.CounterOpts{
		Namespace: namespace,
		Name:      "exporter_log_messages",
		Help:      "Current total log messages received.",
	})
	err = prometheus.Register(varnishMessages)
	if err != nil {
		log.Fatal(err)
	}
	varnishParseFailures := prometheus.NewCounter(prometheus.CounterOpts{
		Namespace: namespace,
		Name:      "exporter_log_parse_failure",
		Help:      "Number of errors while parsing log messages.",
	})
	err = prometheus.Register(varnishParseFailures)
	if err != nil {
		log.Fatal(err)
	}
	var msgs int64

	go func() {
		for scanner.Scan() {
			varnishMessages.Inc()
			content := scanner.Text()
			msgs++
			metrics, labels, err := parseMessage(content, path_mappings)
			if err != nil {
				log.Error(err)
				continue
			}
			for _, metric := range metrics {
				var collector prometheus.Collector
				collector, err = prometheus.RegisterOrGet(prometheus.NewHistogramVec(prometheus.HistogramOpts{
					Namespace: namespace,
					Name:      metric.Name,
					Help:      fmt.Sprintf("Varnish request log value for %s", metric.Name),
				}, labels.Names))
				if err != nil {
					log.Error(err)
					continue
				}
				collector.(*prometheus.HistogramVec).WithLabelValues(labels.Values...).Observe(metric.Value)
			}
		}
	}()

	// Setup HTTP server
	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
             <title>Varnish Request Exporter</title>

Varnish Request Exporter

<a href='` + *metricsPath + `'>Metrics

             `))
	})

	go func() {
		log.Infof("Starting Server: %s", *listenAddress)
		log.Fatal(http.ListenAndServe(*listenAddress, nil))
	}()

	go func() {
		err = cmd.Start()
		if err != nil {
			log.Fatal(err)
		}
		err = cmd.Wait()
		if err != nil {
			log.Fatal(err)
		}
		log.Infof("varnishncsa command exited")
		log.Infof("Messages received: %d", msgs)
		os.Exit(0)
	}()

	s := <-sigchan
	log.Infof("Received %v, terminating", s)
	log.Infof("Messages received: %d", msgs)

	os.Exit(0)
}

func parseMappings(mappings_file string) (mappings []path_mappings, err error) {
	mappings = make([]path_mappings, 0)
	if mappings_file == "" {
		return
	}
	in_file, err := os.Open(mappings_file)
	if err != nil {
		log.Fatal(err)
	}
	scanner := bufio.NewScanner(in_file)
	scanner.Split(bufio.ScanLines)
	comment_re := regexp.MustCompile("(#.*|^\\s+|\\s+$)")
	split_re := regexp.MustCompile("\\s+")
	line_no := 0
	for scanner.Scan() {
		line_no++
		line := comment_re.ReplaceAllString(scanner.Text(), "")
		if len(line) == 0 {
			continue
		}
		parts := split_re.Split(line, 2)
		switch len(parts) {
		case 1:
			log.Debugf("mapping strip: %s", parts[0])
			mappings = append(mappings, path_mappings{regexp.MustCompile(parts[0]), ""})
		case 2:
			log.Debugf("mapping replace: %s => %s", parts[0], parts[1])
			mappings = append(mappings, path_mappings{regexp.MustCompile(parts[0]), parts[1]})
		}
	}
	in_file.Close()
	return
}

func buildVarnishncsaArgs(httpHost string, instance string, befirstbyte bool) (args []string) {
	format := "method=\"%m\" status=%s path=\"%U\""
	if len(httpHost) == 0 {
		format += " host=\"%{host}i\""
	}
	format += " time:%D"
	if befirstbyte {
		format += " time_firstbyte:%{Varnish:time_firstbyte}x"
	}
	args = make([]string, 0, 6)
	args = append(args, "-F")
	args = append(args, format)
	if len(httpHost) > 0 {
		args = append(args, "-q")
		args = append(args, "ReqHeader:host eq \""+httpHost+"\"")
	}
	if instance != "" {
		args = append(args, "-n", instance)
	}
	return
}
<commit_msg>Add "cache" label ("hit" or "miss")<commit_after>// Copyright 2016 Markus Lindenberg, Stig Bakken
//
// 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 main

import (
	"bufio"
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"regexp"
	"syscall"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
)

const (
	namespace = "varnish_request"
)

type path_mappings struct {
	Pattern     *regexp.Regexp
	Replacement string
}

func main() {
	var (
		listenAddress = flag.String("http.port", ":9151", "Host/port for HTTP server")
		metricsPath   = flag.String("http.metricsurl", "/metrics", "Prometheus metrics path")
		httpHost      = flag.String("varnish.host", "", "Virtual host to look for in Varnish logs (defaults to all hosts)")
		mappingsFile  = flag.String("varnish.path-mappings", "", "Name of file with path mappings")
		instance      = flag.String("varnish.instance", "", "Name of Varnish instance")
		befirstbyte   = flag.Bool("varnish.firstbyte", false, "Also export metrics for backend time to first byte")
	)
	flag.Parse()

	// Listen to signals
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGINT)

	// Set up 'varnishncsa' pipe
	cmdName := "varnishncsa"
	cmdArgs := buildVarnishncsaArgs(*httpHost, *instance, *befirstbyte)
	log.Infof("Running command: %v %v\n", cmdName, cmdArgs)
	cmd := exec.Command(cmdName, cmdArgs...)
	cmdReader, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}
	scanner := bufio.NewScanner(cmdReader)

	path_mappings, err := parseMappings(*mappingsFile)
	if err != nil {
		log.Fatal(err)
	}

	// Setup metrics
	varnishMessages := prometheus.NewCounter(prometheus.CounterOpts{
		Namespace: namespace,
		Name:      "exporter_log_messages",
		Help:      "Current total log messages received.",
	})
	err = prometheus.Register(varnishMessages)
	if err != nil {
		log.Fatal(err)
	}
	varnishParseFailures := prometheus.NewCounter(prometheus.CounterOpts{
		Namespace: namespace,
		Name:      "exporter_log_parse_failure",
		Help:      "Number of errors while parsing log messages.",
	})
	err = prometheus.Register(varnishParseFailures)
	if err != nil {
		log.Fatal(err)
	}
	var msgs int64

	go func() {
		for scanner.Scan() {
			varnishMessages.Inc()
			content := scanner.Text()
			msgs++
			metrics, labels, err := parseMessage(content, path_mappings)
			if err != nil {
				log.Error(err)
				continue
			}
			for _, metric := range metrics {
				var collector prometheus.Collector
				collector, err = prometheus.RegisterOrGet(prometheus.NewHistogramVec(prometheus.HistogramOpts{
					Namespace: namespace,
					Name:      metric.Name,
					Help:      fmt.Sprintf("Varnish request log value for %s", metric.Name),
				}, labels.Names))
				if err != nil {
					log.Error(err)
					continue
				}
				collector.(*prometheus.HistogramVec).WithLabelValues(labels.Values...).Observe(metric.Value)
			}
		}
	}()

	// Setup HTTP server
	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
             <title>Varnish Request Exporter</title>

Varnish Request Exporter

<a href='` + *metricsPath + `'>Metrics

             `))
	})

	go func() {
		log.Infof("Starting Server: %s", *listenAddress)
		log.Fatal(http.ListenAndServe(*listenAddress, nil))
	}()

	go func() {
		err = cmd.Start()
		if err != nil {
			log.Fatal(err)
		}
		err = cmd.Wait()
		if err != nil {
			log.Fatal(err)
		}
		log.Infof("varnishncsa command exited")
		log.Infof("Messages received: %d", msgs)
		os.Exit(0)
	}()

	s := <-sigchan
	log.Infof("Received %v, terminating", s)
	log.Infof("Messages received: %d", msgs)

	os.Exit(0)
}

func parseMappings(mappings_file string) (mappings []path_mappings, err error) {
	mappings = make([]path_mappings, 0)
	if mappings_file == "" {
		return
	}
	in_file, err := os.Open(mappings_file)
	if err != nil {
		log.Fatal(err)
	}
	scanner := bufio.NewScanner(in_file)
	scanner.Split(bufio.ScanLines)
	comment_re := regexp.MustCompile("(#.*|^\\s+|\\s+$)")
	split_re := regexp.MustCompile("\\s+")
	line_no := 0
	for scanner.Scan() {
		line_no++
		line := comment_re.ReplaceAllString(scanner.Text(), "")
		if len(line) == 0 {
			continue
		}
		parts := split_re.Split(line, 2)
		switch len(parts) {
		case 1:
			log.Debugf("mapping strip: %s", parts[0])
			mappings = append(mappings, path_mappings{regexp.MustCompile(parts[0]), ""})
		case 2:
			log.Debugf("mapping replace: %s => %s", parts[0], parts[1])
			mappings = append(mappings, path_mappings{regexp.MustCompile(parts[0]), parts[1]})
		}
	}
	in_file.Close()
	return
}

func buildVarnishncsaArgs(httpHost string, instance string, befirstbyte bool) (args []string) {
	format := "method=\"%m\" status=%s path=\"%U\" cache=\"%{Varnish:hitmiss}x\""
	if len(httpHost) == 0 {
		format += " host=\"%{host}i\""
	}
	format += " time:%D"
	if befirstbyte {
		format += " time_firstbyte:%{Varnish:time_firstbyte}x"
	}
	args = make([]string, 0, 6)
	args = append(args, "-F")
	args = append(args, format)
	if len(httpHost) > 0 {
		args = append(args, "-q")
		args = append(args, "ReqHeader:host eq \""+httpHost+"\"")
	}
	if instance != "" {
		args = append(args, "-n", instance)
	}
	return
}
<|endoftext|>


<commit_before>package handlers

import (
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"

	"github.com/docker/distribution"
	ctxu "github.com/docker/distribution/context"
	"github.com/docker/distribution/digest"
	"github.com/docker/distribution/registry/api/v2"
	"github.com/gorilla/handlers"
)

// layerUploadDispatcher constructs and returns the layer upload handler for
// the given request context.
func layerUploadDispatcher(ctx *Context, r *http.Request) http.Handler {
	luh := &layerUploadHandler{
		Context: ctx,
		UUID:    getUploadUUID(ctx),
	}

	handler := http.Handler(handlers.MethodHandler{
		"POST": http.HandlerFunc(luh.StartLayerUpload),
		"GET":  http.HandlerFunc(luh.GetUploadStatus),
		"HEAD": http.HandlerFunc(luh.GetUploadStatus),
		// TODO(stevvooe): Must implement patch support.
		// "PATCH":    http.HandlerFunc(luh.PutLayerChunk),
		"PUT":    http.HandlerFunc(luh.PutLayerUploadComplete),
		"DELETE": http.HandlerFunc(luh.CancelLayerUpload),
	})

	if luh.UUID != "" {
		state, err := hmacKey(ctx.Config.HTTP.Secret).unpackUploadState(r.FormValue("_state"))
		if err != nil {
			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				ctxu.GetLogger(ctx).Infof("error resolving upload: %v", err)
				w.WriteHeader(http.StatusBadRequest)
				luh.Errors.Push(v2.ErrorCodeBlobUploadInvalid, err)
			})
		}
		luh.State = state

		if state.Name != ctx.Repository.Name() {
			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				ctxu.GetLogger(ctx).Infof("mismatched repository name in upload state: %q != %q", state.Name, luh.Repository.Name())
				w.WriteHeader(http.StatusBadRequest)
				luh.Errors.Push(v2.ErrorCodeBlobUploadInvalid, err)
			})
		}

		if state.UUID != luh.UUID {
			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				ctxu.GetLogger(ctx).Infof("mismatched uuid in upload state: %q != %q", state.UUID, luh.UUID)
				w.WriteHeader(http.StatusBadRequest)
				luh.Errors.Push(v2.ErrorCodeBlobUploadInvalid, err)
			})
		}

		layers := ctx.Repository.Layers()
		upload, err := layers.Resume(luh.UUID)
		if err != nil {
			ctxu.GetLogger(ctx).Errorf("error resolving upload: %v", err)
			if err == distribution.ErrLayerUploadUnknown {
				return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
					w.WriteHeader(http.StatusNotFound)
					luh.Errors.Push(v2.ErrorCodeBlobUploadUnknown, err)
				})
			}

			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				w.WriteHeader(http.StatusInternalServerError)
				luh.Errors.Push(v2.ErrorCodeUnknown, err)
			})
		}
		luh.Upload = upload

		if state.Offset > 0 {
			// Seek the layer upload to the correct spot if it's non-zero.
			// These error conditions should be rare and demonstrate really
			// problems. We basically cancel the upload and tell the client to
			// start over.
			if nn, err := upload.Seek(luh.State.Offset, os.SEEK_SET); err != nil {
				defer upload.Close()
				ctxu.GetLogger(ctx).Infof("error seeking layer upload: %v", err)
				return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
					w.WriteHeader(http.StatusBadRequest)
					luh.Errors.Push(v2.ErrorCodeBlobUploadInvalid, err)
					upload.Cancel()
				})
			} else if nn != luh.State.Offset {
				defer upload.Close()
				ctxu.GetLogger(ctx).Infof("seek to wrong offest: %d != %d", nn, luh.State.Offset)
				return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
					w.WriteHeader(http.StatusBadRequest)
					luh.Errors.Push(v2.ErrorCodeBlobUploadInvalid, err)
					upload.Cancel()
				})
			}
		}

		handler = closeResources(handler, luh.Upload)
	}

	return handler
}

// layerUploadHandler handles the http layer upload process.
type layerUploadHandler struct {
	*Context

	// UUID identifies the upload instance for the current request.
	UUID string

	Upload distribution.LayerUpload

	State layerUploadState
}

// StartLayerUpload begins the layer upload process and allocates a server-
// side upload session.
func (luh *layerUploadHandler) StartLayerUpload(w http.ResponseWriter, r *http.Request) {
	layers := luh.Repository.Layers()
	upload, err := layers.Upload()
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError) // Error conditions here?
		luh.Errors.Push(v2.ErrorCodeUnknown, err)
		return
	}

	luh.Upload = upload
	defer luh.Upload.Close()

	if err := luh.layerUploadResponse(w, r); err != nil {
		w.WriteHeader(http.StatusInternalServerError) // Error conditions here?
		luh.Errors.Push(v2.ErrorCodeUnknown, err)
		return
	}
	w.WriteHeader(http.StatusAccepted)
}

// GetUploadStatus returns the status of a given upload, identified by uuid.
func (luh *layerUploadHandler) GetUploadStatus(w http.ResponseWriter, r *http.Request) {
	if luh.Upload == nil {
		w.WriteHeader(http.StatusNotFound)
		luh.Errors.Push(v2.ErrorCodeBlobUploadUnknown)
		return
	}

	if err := luh.layerUploadResponse(w, r); err != nil {
		w.WriteHeader(http.StatusInternalServerError) // Error conditions here?
		luh.Errors.Push(v2.ErrorCodeUnknown, err)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

// PutLayerUploadComplete takes the final request of a layer upload. The final
// chunk may include all the layer data, the final chunk of layer data or no
// layer data. Any data provided is received and verified. If successful, the
// layer is linked into the blob store and 201 Created is returned with the
// canonical url of the layer.
func (luh *layerUploadHandler) PutLayerUploadComplete(w http.ResponseWriter, r *http.Request) {
	if luh.Upload == nil {
		w.WriteHeader(http.StatusNotFound)
		luh.Errors.Push(v2.ErrorCodeBlobUploadUnknown)
		return
	}

	dgstStr := r.FormValue("digest") // TODO(stevvooe): Support multiple digest parameters!

	if dgstStr == "" {
		// no digest? return error, but allow retry.
		w.WriteHeader(http.StatusNotFound)
		luh.Errors.Push(v2.ErrorCodeDigestInvalid, "digest missing")
		return
	}

	dgst, err := digest.ParseDigest(dgstStr)
	if err != nil {
		// no digest? return error, but allow retry.
		w.WriteHeader(http.StatusNotFound)
		luh.Errors.Push(v2.ErrorCodeDigestInvalid, "digest parsing failed")
		return
	}

	// TODO(stevvooe): Check the incoming range header here, per the
	// specification. LayerUpload should be seeked (sought?) to that position.

	// Read in the final chunk, if any.
	io.Copy(luh.Upload, r.Body)

	layer, err := luh.Upload.Finish(dgst)
	if err != nil {
		switch err := err.(type) {
		case distribution.ErrLayerInvalidDigest:
			w.WriteHeader(http.StatusBadRequest)
			luh.Errors.Push(v2.ErrorCodeDigestInvalid, err)
		default:
			ctxu.GetLogger(luh).Errorf("unknown error completing upload: %#v", err)
			w.WriteHeader(http.StatusInternalServerError)
			luh.Errors.Push(v2.ErrorCodeUnknown, err)
		}

		// Clean up the backend layer data if there was an error.
		if err := luh.Upload.Cancel(); err != nil {
			// If the cleanup fails, all we can do is observe and report.
			ctxu.GetLogger(luh).Errorf("error canceling upload after error: %v", err)
		}

		return
	}

	// Build our canonical layer url
	layerURL, err := luh.urlBuilder.BuildBlobURL(luh.Repository.Name(), layer.Digest())
	if err != nil {
		luh.Errors.Push(v2.ErrorCodeUnknown, err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.Header().Set("Location", layerURL)
	w.Header().Set("Content-Length", "0")
	w.WriteHeader(http.StatusCreated)
}

// CancelLayerUpload cancels an in-progress upload of a layer.
func (luh *layerUploadHandler) CancelLayerUpload(w http.ResponseWriter, r *http.Request) {
	if luh.Upload == nil {
		w.WriteHeader(http.StatusNotFound)
		luh.Errors.Push(v2.ErrorCodeBlobUploadUnknown)
		return
	}

	if err := luh.Upload.Cancel(); err != nil {
		ctxu.GetLogger(luh).Errorf("error encountered canceling upload: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		luh.Errors.PushErr(err)
	}

	w.WriteHeader(http.StatusNoContent)
}

// layerUploadResponse provides a standard request for uploading layers and
// chunk responses. This sets the correct headers but the response status is
// left to the caller.
func (luh *layerUploadHandler) layerUploadResponse(w http.ResponseWriter, r *http.Request) error {

	offset, err := luh.Upload.Seek(0, os.SEEK_CUR)
	if err != nil {
		ctxu.GetLogger(luh).Errorf("unable get current offset of layer upload: %v", err)
		return err
	}

	// TODO(stevvooe): Need a better way to manage the upload state automatically.
	luh.State.Name = luh.Repository.Name()
	luh.State.UUID = luh.Upload.UUID()
	luh.State.Offset = offset
	luh.State.StartedAt = luh.Upload.StartedAt()

	token, err := hmacKey(luh.Config.HTTP.Secret).packUploadState(luh.State)
	if err != nil {
		ctxu.GetLogger(luh).Infof("error building upload state token: %s", err)
		return err
	}

	uploadURL, err := luh.urlBuilder.BuildBlobUploadChunkURL(
		luh.Repository.Name(), luh.Upload.UUID(),
		url.Values{
			"_state": []string{token},
		})
	if err != nil {
		ctxu.GetLogger(luh).Infof("error building upload url: %s", err)
		return err
	}

	w.Header().Set("Location", uploadURL)
	w.Header().Set("Content-Length", "0")
	w.Header().Set("Range", fmt.Sprintf("0-%d", luh.State.Offset))

	return nil
}
<commit_msg>A digest missing parameter should result in a bad request<commit_after>package handlers

import (
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"

	"github.com/docker/distribution"
	ctxu "github.com/docker/distribution/context"
	"github.com/docker/distribution/digest"
	"github.com/docker/distribution/registry/api/v2"
	"github.com/gorilla/handlers"
)

// layerUploadDispatcher constructs and returns the layer upload handler for
// the given request context.
func layerUploadDispatcher(ctx *Context, r *http.Request) http.Handler {
	luh := &layerUploadHandler{
		Context: ctx,
		UUID:    getUploadUUID(ctx),
	}

	handler := http.Handler(handlers.MethodHandler{
		"POST": http.HandlerFunc(luh.StartLayerUpload),
		"GET":  http.HandlerFunc(luh.GetUploadStatus),
		"HEAD": http.HandlerFunc(luh.GetUploadStatus),
		// TODO(stevvooe): Must implement patch support.
		// "PATCH":    http.HandlerFunc(luh.PutLayerChunk),
		"PUT":    http.HandlerFunc(luh.PutLayerUploadComplete),
		"DELETE": http.HandlerFunc(luh.CancelLayerUpload),
	})

	if luh.UUID != "" {
		state, err := hmacKey(ctx.Config.HTTP.Secret).unpackUploadState(r.FormValue("_state"))
		if err != nil {
			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				ctxu.GetLogger(ctx).Infof("error resolving upload: %v", err)
				w.WriteHeader(http.StatusBadRequest)
				luh.Errors.Push(v2.ErrorCodeBlobUploadInvalid, err)
			})
		}
		luh.State = state

		if state.Name != ctx.Repository.Name() {
			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				ctxu.GetLogger(ctx).Infof("mismatched repository name in upload state: %q != %q", state.Name, luh.Repository.Name())
				w.WriteHeader(http.StatusBadRequest)
				luh.Errors.Push(v2.ErrorCodeBlobUploadInvalid, err)
			})
		}

		if state.UUID != luh.UUID {
			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				ctxu.GetLogger(ctx).Infof("mismatched uuid in upload state: %q != %q", state.UUID, luh.UUID)
				w.WriteHeader(http.StatusBadRequest)
				luh.Errors.Push(v2.ErrorCodeBlobUploadInvalid, err)
			})
		}

		layers := ctx.Repository.Layers()
		upload, err := layers.Resume(luh.UUID)
		if err != nil {
			ctxu.GetLogger(ctx).Errorf("error resolving upload: %v", err)
			if err == distribution.ErrLayerUploadUnknown {
				return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
					w.WriteHeader(http.StatusNotFound)
					luh.Errors.Push(v2.ErrorCodeBlobUploadUnknown, err)
				})
			}

			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				w.WriteHeader(http.StatusInternalServerError)
				luh.Errors.Push(v2.ErrorCodeUnknown, err)
			})
		}
		luh.Upload = upload

		if state.Offset > 0 {
			// Seek the layer upload to the correct spot if it's non-zero.
			// These error conditions should be rare and demonstrate really
			// problems. We basically cancel the upload and tell the client to
			// start over.
			if nn, err := upload.Seek(luh.State.Offset, os.SEEK_SET); err != nil {
				defer upload.Close()
				ctxu.GetLogger(ctx).Infof("error seeking layer upload: %v", err)
				return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
					w.WriteHeader(http.StatusBadRequest)
					luh.Errors.Push(v2.ErrorCodeBlobUploadInvalid, err)
					upload.Cancel()
				})
			} else if nn != luh.State.Offset {
				defer upload.Close()
				ctxu.GetLogger(ctx).Infof("seek to wrong offest: %d != %d", nn, luh.State.Offset)
				return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
					w.WriteHeader(http.StatusBadRequest)
					luh.Errors.Push(v2.ErrorCodeBlobUploadInvalid, err)
					upload.Cancel()
				})
			}
		}

		handler = closeResources(handler, luh.Upload)
	}

	return handler
}

// layerUploadHandler handles the http layer upload process.
type layerUploadHandler struct {
	*Context

	// UUID identifies the upload instance for the current request.
	UUID string

	Upload distribution.LayerUpload

	State layerUploadState
}

// StartLayerUpload begins the layer upload process and allocates a server-
// side upload session.
func (luh *layerUploadHandler) StartLayerUpload(w http.ResponseWriter, r *http.Request) {
	layers := luh.Repository.Layers()
	upload, err := layers.Upload()
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError) // Error conditions here?
		luh.Errors.Push(v2.ErrorCodeUnknown, err)
		return
	}

	luh.Upload = upload
	defer luh.Upload.Close()

	if err := luh.layerUploadResponse(w, r); err != nil {
		w.WriteHeader(http.StatusInternalServerError) // Error conditions here?
		luh.Errors.Push(v2.ErrorCodeUnknown, err)
		return
	}
	w.WriteHeader(http.StatusAccepted)
}

// GetUploadStatus returns the status of a given upload, identified by uuid.
func (luh *layerUploadHandler) GetUploadStatus(w http.ResponseWriter, r *http.Request) {
	if luh.Upload == nil {
		w.WriteHeader(http.StatusNotFound)
		luh.Errors.Push(v2.ErrorCodeBlobUploadUnknown)
		return
	}

	if err := luh.layerUploadResponse(w, r); err != nil {
		w.WriteHeader(http.StatusInternalServerError) // Error conditions here?
		luh.Errors.Push(v2.ErrorCodeUnknown, err)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

// PutLayerUploadComplete takes the final request of a layer upload. The final
// chunk may include all the layer data, the final chunk of layer data or no
// layer data. Any data provided is received and verified. If successful, the
// layer is linked into the blob store and 201 Created is returned with the
// canonical url of the layer.
func (luh *layerUploadHandler) PutLayerUploadComplete(w http.ResponseWriter, r *http.Request) {
	if luh.Upload == nil {
		w.WriteHeader(http.StatusNotFound)
		luh.Errors.Push(v2.ErrorCodeBlobUploadUnknown)
		return
	}

	dgstStr := r.FormValue("digest") // TODO(stevvooe): Support multiple digest parameters!

	if dgstStr == "" {
		// no digest? return error, but allow retry.
		w.WriteHeader(http.StatusBadRequest)
		luh.Errors.Push(v2.ErrorCodeDigestInvalid, "digest missing")
		return
	}

	dgst, err := digest.ParseDigest(dgstStr)
	if err != nil {
		// no digest? return error, but allow retry.
		w.WriteHeader(http.StatusNotFound)
		luh.Errors.Push(v2.ErrorCodeDigestInvalid, "digest parsing failed")
		return
	}

	// TODO(stevvooe): Check the incoming range header here, per the
	// specification. LayerUpload should be seeked (sought?) to that position.

	// Read in the final chunk, if any.
	io.Copy(luh.Upload, r.Body)

	layer, err := luh.Upload.Finish(dgst)
	if err != nil {
		switch err := err.(type) {
		case distribution.ErrLayerInvalidDigest:
			w.WriteHeader(http.StatusBadRequest)
			luh.Errors.Push(v2.ErrorCodeDigestInvalid, err)
		default:
			ctxu.GetLogger(luh).Errorf("unknown error completing upload: %#v", err)
			w.WriteHeader(http.StatusInternalServerError)
			luh.Errors.Push(v2.ErrorCodeUnknown, err)
		}

		// Clean up the backend layer data if there was an error.
		if err := luh.Upload.Cancel(); err != nil {
			// If the cleanup fails, all we can do is observe and report.
			ctxu.GetLogger(luh).Errorf("error canceling upload after error: %v", err)
		}

		return
	}

	// Build our canonical layer url
	layerURL, err := luh.urlBuilder.BuildBlobURL(luh.Repository.Name(), layer.Digest())
	if err != nil {
		luh.Errors.Push(v2.ErrorCodeUnknown, err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.Header().Set("Location", layerURL)
	w.Header().Set("Content-Length", "0")
	w.WriteHeader(http.StatusCreated)
}

// CancelLayerUpload cancels an in-progress upload of a layer.
func (luh *layerUploadHandler) CancelLayerUpload(w http.ResponseWriter, r *http.Request) {
	if luh.Upload == nil {
		w.WriteHeader(http.StatusNotFound)
		luh.Errors.Push(v2.ErrorCodeBlobUploadUnknown)
		return
	}

	if err := luh.Upload.Cancel(); err != nil {
		ctxu.GetLogger(luh).Errorf("error encountered canceling upload: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		luh.Errors.PushErr(err)
	}

	w.WriteHeader(http.StatusNoContent)
}

// layerUploadResponse provides a standard request for uploading layers and
// chunk responses. This sets the correct headers but the response status is
// left to the caller.
func (luh *layerUploadHandler) layerUploadResponse(w http.ResponseWriter, r *http.Request) error {

	offset, err := luh.Upload.Seek(0, os.SEEK_CUR)
	if err != nil {
		ctxu.GetLogger(luh).Errorf("unable get current offset of layer upload: %v", err)
		return err
	}

	// TODO(stevvooe): Need a better way to manage the upload state automatically.
	luh.State.Name = luh.Repository.Name()
	luh.State.UUID = luh.Upload.UUID()
	luh.State.Offset = offset
	luh.State.StartedAt = luh.Upload.StartedAt()

	token, err := hmacKey(luh.Config.HTTP.Secret).packUploadState(luh.State)
	if err != nil {
		ctxu.GetLogger(luh).Infof("error building upload state token: %s", err)
		return err
	}

	uploadURL, err := luh.urlBuilder.BuildBlobUploadChunkURL(
		luh.Repository.Name(), luh.Upload.UUID(),
		url.Values{
			"_state": []string{token},
		})
	if err != nil {
		ctxu.GetLogger(luh).Infof("error building upload url: %s", err)
		return err
	}

	w.Header().Set("Location", uploadURL)
	w.Header().Set("Content-Length", "0")
	w.Header().Set("Range", fmt.Sprintf("0-%d", luh.State.Offset))

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2012 Google Inc. All Rights Reserved.
//
// 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.

// Author: jsing@google.com (Joel Sing)

// The seesaw_cli binary implements the Seesaw v2 Command Line Interface (CLI),
// which allows for user control of the Seesaw v2 Engine component.
package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"os/user"
	"strings"
	"syscall"
	"time"

	"github.com/google/seesaw/cli"
	"github.com/google/seesaw/common/conn"
	"github.com/google/seesaw/common/ipc"
	"github.com/google/seesaw/common/seesaw"

	"golang.org/x/crypto/ssh/terminal"
)

var (
	command      = flag.String("c", "", "Command to execute")
	engineSocket = flag.String("engine", seesaw.EngineSocket, "Seesaw Engine Socket")

	oldTermState *terminal.State
	prompt       string
	seesawCLI    *cli.SeesawCLI
	seesawConn   *conn.Seesaw
	term         *terminal.Terminal
)

func exit() {
	if oldTermState != nil {
		terminal.Restore(syscall.Stdin, oldTermState)
	}
	fmt.Printf("\n")
	os.Exit(0)
}

func fatalf(format string, a ...interface{}) {
	if oldTermState != nil {
		terminal.Restore(syscall.Stdin, oldTermState)
	}
	fmt.Fprintf(os.Stderr, format, a...)
	fmt.Fprintf(os.Stderr, "\n")
	os.Exit(1)
}

func suspend() {
	if oldTermState != nil {
		terminal.Restore(syscall.Stdin, oldTermState)
	}
	go resume()
	syscall.Kill(os.Getpid(), syscall.SIGTSTP)
}

func resume() {
	time.Sleep(1 * time.Second)
	fmt.Println("resuming...")
	terminalInit()
}

func terminalInit() {
	var err error
	oldTermState, err = terminal.MakeRaw(syscall.Stdin)
	if err != nil {
		fatalf("Failed to get raw terminal: %v", err)
	}

	term = terminal.NewTerminal(os.Stdin, prompt)
	term.AutoCompleteCallback = autoComplete
}

// commandChain builds a command chain from the given command slice.
func commandChain(chain []*cli.Command, args []string) string {
	s := make([]string, 0)
	for _, c := range chain {
		s = append(s, c.Command)
	}
	s = append(s, args...)
	if len(s) > 0 && len(args) == 0 {
		s = append(s, "")
	}
	return strings.Join(s, " ")
}

// autoComplete attempts to complete the user's input when certain
// characters are typed.
func autoComplete(line string, pos int, key rune) (string, int, bool) {
	switch key {
	case 0x01: // Ctrl-A
		return line, 0, true
	case 0x03: // Ctrl-C
		exit()
	case 0x05: // Ctrl-E
		return line, len(line), true
	case 0x09: // Ctrl-I (Tab)
		_, _, chain, args := cli.FindCommand(string(line))
		line := commandChain(chain, args)
		return line, len(line), true
	case 0x15: // Ctrl-U
		return "", 0, true
	case 0x1a: // Ctrl-Z
		suspend()
	case '?':
		cmd, subcmds, chain, args := cli.FindCommand(string(line[0:pos]))
		if cmd == nil {
			term.Write([]byte(prompt))
			term.Write([]byte(line))
			term.Write([]byte("?\n"))
		}
		if subcmds != nil {
			for _, c := range *subcmds {
				term.Write([]byte(" " + c.Command))
				term.Write([]byte("\n"))
			}
		} else if cmd == nil {
			term.Write([]byte("Unknown command.\n"))
		}

		line := commandChain(chain, args)
		return line, len(line), true
	}
	return "", 0, false
}

// interactive invokes the interactive CLI interface.
func interactive() {
	status, err := seesawConn.ClusterStatus()
	if err != nil {
		fatalf("Failed to get cluster status: %v", err)
	}
	fmt.Printf("\nSeesaw CLI - Engine version %d\n\n", status.Version)

	u, err := user.Current()
	if err != nil {
		fatalf("Failed to get current user: %v", err)
	}

	ha, err := seesawConn.HAStatus()
	if err != nil {
		fatalf("Failed to get HA status: %v", err)
	}
	if ha.State != seesaw.HAMaster {
		fmt.Println("WARNING: This seesaw is not currently the master.")
	}

	prompt = fmt.Sprintf("%s@%s> ", u.Username, status.Site)

	// Setup signal handler before we switch to a raw terminal.
	sigc := make(chan os.Signal, 3)
	signal.Notify(sigc, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM)
	go func() {
		<-sigc
		exit()
	}()

	terminalInit()

	for {
		cmdline, err := term.ReadLine()
		if err != nil {
			break
		}
		cmdline = strings.TrimSpace(cmdline)
		if cmdline == "" {
			continue
		}
		if err := seesawCLI.Execute(cmdline); err != nil {
			fmt.Println(err)
		}
	}
}

func main() {
	flag.Parse()

	ctx := ipc.NewTrustedContext(seesaw.SCLocalCLI)

	var err error
	seesawConn, err = conn.NewSeesawIPC(ctx)
	if err != nil {
		fatalf("Failed to connect to engine: %v", err)
	}
	if err := seesawConn.Dial(*engineSocket); err != nil {
		fatalf("Failed to connect to engine: %v", err)
	}
	defer seesawConn.Close()
	seesawCLI = cli.NewSeesawCLI(seesawConn, exit)

	if *command == "" {
		interactive()
		exit()
	}

	if err := seesawCLI.Execute(*command); err != nil {
		fatalf("%v", err)
	}
}
<commit_msg>fix newline in seesaw_cli<commit_after>// Copyright 2012 Google Inc. All Rights Reserved.
//
// 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.

// Author: jsing@google.com (Joel Sing)

// The seesaw_cli binary implements the Seesaw v2 Command Line Interface (CLI),
// which allows for user control of the Seesaw v2 Engine component.
package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"os/user"
	"strings"
	"syscall"
	"time"

	"github.com/google/seesaw/cli"
	"github.com/google/seesaw/common/conn"
	"github.com/google/seesaw/common/ipc"
	"github.com/google/seesaw/common/seesaw"

	"golang.org/x/crypto/ssh/terminal"
)

var (
	command      = flag.String("c", "", "Command to execute")
	engineSocket = flag.String("engine", seesaw.EngineSocket, "Seesaw Engine Socket")

	oldTermState *terminal.State
	prompt       string
	seesawCLI    *cli.SeesawCLI
	seesawConn   *conn.Seesaw
	term         *terminal.Terminal
)

func exit() {
	if oldTermState != nil {
		terminal.Restore(syscall.Stdin, oldTermState)
	}
	fmt.Printf("\n")
	os.Exit(0)
}

func fatalf(format string, a ...interface{}) {
	if oldTermState != nil {
		terminal.Restore(syscall.Stdin, oldTermState)
	}
	fmt.Fprintf(os.Stderr, format, a...)
	fmt.Fprintf(os.Stderr, "\n")
	os.Exit(1)
}

func suspend() {
	if oldTermState != nil {
		terminal.Restore(syscall.Stdin, oldTermState)
	}
	go resume()
	syscall.Kill(os.Getpid(), syscall.SIGTSTP)
}

func resume() {
	time.Sleep(1 * time.Second)
	fmt.Println("resuming...")
	terminalInit()
}

func terminalInit() {
	var err error
	oldTermState, err = terminal.MakeRaw(syscall.Stdin)
	if err != nil {
		fatalf("Failed to get raw terminal: %v", err)
	}

	term = terminal.NewTerminal(os.Stdin, prompt)
	term.AutoCompleteCallback = autoComplete
}

// commandChain builds a command chain from the given command slice.
func commandChain(chain []*cli.Command, args []string) string {
	s := make([]string, 0)
	for _, c := range chain {
		s = append(s, c.Command)
	}
	s = append(s, args...)
	if len(s) > 0 && len(args) == 0 {
		s = append(s, "")
	}
	return strings.Join(s, " ")
}

// autoComplete attempts to complete the user's input when certain
// characters are typed.
func autoComplete(line string, pos int, key rune) (string, int, bool) {
	switch key {
	case 0x01: // Ctrl-A
		return line, 0, true
	case 0x03: // Ctrl-C
		exit()
	case 0x05: // Ctrl-E
		return line, len(line), true
	case 0x09: // Ctrl-I (Tab)
		_, _, chain, args := cli.FindCommand(string(line))
		line := commandChain(chain, args)
		return line, len(line), true
	case 0x15: // Ctrl-U
		return "", 0, true
	case 0x1a: // Ctrl-Z
		suspend()
	case '?':
		cmd, subcmds, chain, args := cli.FindCommand(string(line[0:pos]))
		if cmd == nil {
			term.Write([]byte(prompt))
			term.Write([]byte(line))
			term.Write([]byte("?\n"))
		}
		if subcmds != nil {
			for _, c := range *subcmds {
				term.Write([]byte(" " + c.Command))
				term.Write([]byte("\n"))
			}
		} else if cmd == nil {
			term.Write([]byte("Unknown command.\n"))
		}

		line := commandChain(chain, args)
		return line, len(line), true
	}
	return "", 0, false
}

// interactive invokes the interactive CLI interface.
func interactive() {
	status, err := seesawConn.ClusterStatus()
	if err != nil {
		fatalf("Failed to get cluster status: %v", err)
	}
	fmt.Printf("\nSeesaw CLI - Engine version %d\n\n", status.Version)

	u, err := user.Current()
	if err != nil {
		fatalf("Failed to get current user: %v", err)
	}

	ha, err := seesawConn.HAStatus()
	if err != nil {
		fatalf("Failed to get HA status: %v", err)
	}
	if ha.State != seesaw.HAMaster {
		fmt.Println("WARNING: This seesaw is not currently the master.")
	}

	prompt = fmt.Sprintf("%s@%s> ", u.Username, status.Site)

	// Setup signal handler before we switch to a raw terminal.
	sigc := make(chan os.Signal, 3)
	signal.Notify(sigc, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM)
	go func() {
		<-sigc
		exit()
	}()

	terminalInit()
	rawState, err := terminal.GetState(syscall.Stdin)
	if err != nil {
		fatalf("Failed to get raw terminal state: %v", err)
	}

	for {
		cmdline, err := term.ReadLine()
		if err != nil {
			break
		}
		cmdline = strings.TrimSpace(cmdline)
		if cmdline == "" {
			continue
		}
		terminal.Restore(syscall.Stdin, oldTermState)
		if err := seesawCLI.Execute(cmdline); err != nil {
			fmt.Println(err)
		}
		terminal.Restore(syscall.Stdin, rawState)
	}
}

func main() {
	flag.Parse()

	ctx := ipc.NewTrustedContext(seesaw.SCLocalCLI)

	var err error
	seesawConn, err = conn.NewSeesawIPC(ctx)
	if err != nil {
		fatalf("Failed to connect to engine: %v", err)
	}
	if err := seesawConn.Dial(*engineSocket); err != nil {
		fatalf("Failed to connect to engine: %v", err)
	}
	defer seesawConn.Close()
	seesawCLI = cli.NewSeesawCLI(seesawConn, exit)

	if *command == "" {
		interactive()
		exit()
	}

	if err := seesawCLI.Execute(*command); err != nil {
		fatalf("%v", err)
	}
}
<|endoftext|>


<commit_before>package main

import "flag"

var PythonReserved = []string{
	"and",       "del",       "from",      "not",       "while",
	"as",        "elif",      "global",    "or",        "with",
	"assert",    "else",      "if",        "pass",      "yield",
	"break",     "except",    "import",    "print",     "len",
	"class",     "exec",      "in",        "raise", 	"open",
	"continue",  "finally",   "is",        "return",    "bool",
	"def",       "for",       "lambda",    "try",		"copy",
	"list",

}

//This is the Java compiler for uct.
var Python bool

func init() {
	flag.BoolVar(&Python, "py", false, "Target Python")

	RegisterAssembler(PythonAssembly, &Python, "py", "#")

	for _, word := range PythonReserved {
		PythonAssembly[word] = Reserved()
	}
}

var PythonAssembly = Assemblable{
	//Special commands.
	"HEADER": Instruction{
		Data:   "#! /bin/python3\nimport stack\nimport sys\nimport threading",
		Args: 1,
	},

	"FOOTER": Instruction{},

	"FILE": Instruction{
		Path: "stack.py",
	},

	"PYTHON": Instruction{All:true},

	"NUMBER": is("%s", 1),
	"BIG": 	is("%s", 1),
	"SIZE":   is("len(%s)", 1),
	"STRING": is("list(bytes(%s, 'utf-8'))", 1),
	"ERRORS":  is("stack.ERROR", 1),

	"LINK":  is("stack.link()"),
	"CONNECT":  is("stack.connect()"),
	"SLICE":  is("stack.slice()"),

	"SOFTWARE": Instruction{
		Data:   "stack = stack.Stack()\n",
	},
	"EXIT": Instruction{
		Data:        "sys.exit(stack.ERROR)",
	},

	"FUNCTION": is("def %s(stack):", 1, 1),
	"RETURN": Instruction{
		Pass: "pass\n",
		Check: "FUNCTION",
		Indented:    1,
		Data:        "\n",
		Indent:      -1,
		Else: &Instruction{
			Data: "return",
		},
	},

	"SCOPE": is(`stack.relay(stack.pipe(%s))`, 1),

	"EXE": is("%s.exe(stack)", 1),

	"PUSH": is("stack.push(%s)", 1),
	"PULL": is("%s = stack.pull()", 1),

	"PUT":   is("stack.put(%s)", 1),
	"POP":   is("%s = stack.pop()", 1),
	"PLACE": is("stack.activearray = %s", 1),
	"ARRAY":  is("%s = stack.array()", 1),
	"RENAME": is("%s = stack.activearray", 1),
	"RELOAD": is("%s = stack.take()", 1),

	"SHARE": is("stack.share(%s)", 1),
	"GRAB":  is("%s = stack.grab()", 1),

	"RELAY": is("stack.relay(%s)", 1),
	"TAKE":  is("%s = stack.take()", 1),

	"GET": is("%s = stack.get()", 1),
	"SET": is("stack.set(%s)", 1),

	"VAR": is("%s = 0", 1),

	"PIPE": is("%s = stack.queue()", 1),
	"INBOX":   is("stack.share(stack.inbox.get(True))\nstack.inbox.task_done()"),
	"OUTBOX":   is("stack.outbox.put(stack.grab())"),

	"EVAL": is("eval(bytes(stack.grab()).decode()+'(stack)')"),

	"OPEN":   is("stack.open()"),
	"EXECUTE": is("stack.execute()"),
	"DELETE": is("stack.delete()"),
	"LOAD":   is("stack.load()"),
	"OUT":    is("stack.out()"),
	"STAT":   is("stack.info()"),
	"IN":     is("stack.inn()"),
	"STDOUT": is("stack.stdout()"),
	"STDIN":  is("stack.stdin()"),
	"HEAP":   is("stack.heap()"),
	"HEAPIT":   is("stack.heapit()"),
	"MAKE":   is("stack.share([0]*stack.pull())"),

	"CLOSE": is("%s.close()", 1),

	"LOOP":   is("while 1:", 0, 1),
	"BREAK":  is("break"),
	"REPEAT": is("\n", 0, -1, -1),

	"IF":   is("if %s != 0:", 1, 1),
	"ELSE": is("else:", 0, 0, -1),
	"END":  is("\n", 0, -1, -1),

	"RUN":  is("%s(stack)", 1),
	"DATA": is("%s = %s", 2),

	"FORK": is("threading.Thread(target=%s, args=(stack.copy(),)).start()\n", 1),

	"ADD": is("%s = %s + %s", 3),
	"SUB": is("%s = %s - %s", 3),
	"MUL": is("%s = stack.mul(%s, %s)", 3),
	"DIV": is("%s = stack.div(%s, %s)", 3),
	"MOD": is("%s = stack.mod(%s, %s)", 3),
	"POW": is("%s = stack.pow(%s, %s)", 3),

	"SLT": is("%s = int(%s <  %s)", 3),
	"SEQ": is("%s = int(%s == %s)", 3),
	"SGE": is("%s = int(%s >= %s)", 3),
	"SGT": is("%s = int(%s >  %s)", 3),
	"SNE": is("%s = int(%s != %s)", 3),
	"SLE": is("%s = int(%s <= %s)", 3),

	"JOIN": is("%s = %s + %s", 3),
	"ERROR": is("stack.ERROR = %s", 1),
}
<commit_msg>pass on blank python if<commit_after>package main

import "flag"

var PythonReserved = []string{
	"and",       "del",       "from",      "not",       "while",
	"as",        "elif",      "global",    "or",        "with",
	"assert",    "else",      "if",        "pass",      "yield",
	"break",     "except",    "import",    "print",     "len",
	"class",     "exec",      "in",        "raise", 	"open",
	"continue",  "finally",   "is",        "return",    "bool",
	"def",       "for",       "lambda",    "try",		"copy",
	"list",

}

//This is the Java compiler for uct.
var Python bool

func init() {
	flag.BoolVar(&Python, "py", false, "Target Python")

	instruction := PythonAssembly["IF"]
	instruction.Pass = "pass\n"
	PythonAssembly["IF"] = instruction

	RegisterAssembler(PythonAssembly, &Python, "py", "#")

	for _, word := range PythonReserved {
		PythonAssembly[word] = Reserved()
	}
}

var PythonAssembly = Assemblable{
	//Special commands.
	"HEADER": Instruction{
		Data:   "#! /bin/python3\nimport stack\nimport sys\nimport threading",
		Args: 1,
	},

	"FOOTER": Instruction{},

	"FILE": Instruction{
		Path: "stack.py",
	},

	"PYTHON": Instruction{All:true},

	"NUMBER": is("%s", 1),
	"BIG": 	is("%s", 1),
	"SIZE":   is("len(%s)", 1),
	"STRING": is("list(bytes(%s, 'utf-8'))", 1),
	"ERRORS":  is("stack.ERROR", 1),

	"LINK":  is("stack.link()"),
	"CONNECT":  is("stack.connect()"),
	"SLICE":  is("stack.slice()"),

	"SOFTWARE": Instruction{
		Data:   "stack = stack.Stack()\n",
	},
	"EXIT": Instruction{
		Data:        "sys.exit(stack.ERROR)",
	},

	"FUNCTION": is("def %s(stack):", 1, 1),
	"RETURN": Instruction{
		Pass: "pass\n",
		Check: "FUNCTION",
		Indented:    1,
		Data:        "\n",
		Indent:      -1,
		Else: &Instruction{
			Data: "return",
		},
	},

	"SCOPE": is(`stack.relay(stack.pipe(%s))`, 1),

	"EXE": is("%s.exe(stack)", 1),

	"PUSH": is("stack.push(%s)", 1),
	"PULL": is("%s = stack.pull()", 1),

	"PUT":   is("stack.put(%s)", 1),
	"POP":   is("%s = stack.pop()", 1),
	"PLACE": is("stack.activearray = %s", 1),
	"ARRAY":  is("%s = stack.array()", 1),
	"RENAME": is("%s = stack.activearray", 1),
	"RELOAD": is("%s = stack.take()", 1),

	"SHARE": is("stack.share(%s)", 1),
	"GRAB":  is("%s = stack.grab()", 1),

	"RELAY": is("stack.relay(%s)", 1),
	"TAKE":  is("%s = stack.take()", 1),

	"GET": is("%s = stack.get()", 1),
	"SET": is("stack.set(%s)", 1),

	"VAR": is("%s = 0", 1),

	"PIPE": is("%s = stack.queue()", 1),
	"INBOX":   is("stack.share(stack.inbox.get(True))\nstack.inbox.task_done()"),
	"OUTBOX":   is("stack.outbox.put(stack.grab())"),

	"EVAL": is("eval(bytes(stack.grab()).decode()+'(stack)')"),

	"OPEN":   is("stack.open()"),
	"EXECUTE": is("stack.execute()"),
	"DELETE": is("stack.delete()"),
	"LOAD":   is("stack.load()"),
	"OUT":    is("stack.out()"),
	"STAT":   is("stack.info()"),
	"IN":     is("stack.inn()"),
	"STDOUT": is("stack.stdout()"),
	"STDIN":  is("stack.stdin()"),
	"HEAP":   is("stack.heap()"),
	"HEAPIT":   is("stack.heapit()"),
	"MAKE":   is("stack.share([0]*stack.pull())"),

	"CLOSE": is("%s.close()", 1),

	"LOOP":   is("while 1:", 0, 1),
	"BREAK":  is("break"),
	"REPEAT": is("\n", 0, -1, -1),

	"IF":   is("if %s != 0:", 1, 1),
	"ELSE": is("else:", 0, 0, -1),
	"END":  is("\n", 0, -1, -1),

	"RUN":  is("%s(stack)", 1),
	"DATA": is("%s = %s", 2),

	"FORK": is("threading.Thread(target=%s, args=(stack.copy(),)).start()\n", 1),

	"ADD": is("%s = %s + %s", 3),
	"SUB": is("%s = %s - %s", 3),
	"MUL": is("%s = stack.mul(%s, %s)", 3),
	"DIV": is("%s = stack.div(%s, %s)", 3),
	"MOD": is("%s = stack.mod(%s, %s)", 3),
	"POW": is("%s = stack.pow(%s, %s)", 3),

	"SLT": is("%s = int(%s <  %s)", 3),
	"SEQ": is("%s = int(%s == %s)", 3),
	"SGE": is("%s = int(%s >= %s)", 3),
	"SGT": is("%s = int(%s >  %s)", 3),
	"SNE": is("%s = int(%s != %s)", 3),
	"SLE": is("%s = int(%s <= %s)", 3),

	"JOIN": is("%s = %s + %s", 3),
	"ERROR": is("stack.ERROR = %s", 1),
}
<|endoftext|>