<commit_before>package main

import (
	"bytes"
	"fmt"
	"strings"
	"time"
)

//Channel...
//represents an irc channel
type Channel struct {
	name      string
	epoch     time.Time
	userlist  map[int]*User
	usermodes map[*User]string
	banlist   map[int]*Ban
	cmodes    string
	topic     string
	topichost string
	topictime int64
}

func (channel *Channel) SetTopic(newtopic string, hostmask string) {
	channel.topic = newtopic
	channel.topichost = hostmask
	channel.topictime = time.Now().Unix()
	channel.SendLinef(":%s TOPIC %s :%s", hostmask, channel.name, newtopic)
}

func NewChannel(newname string) *Channel {
	chann := &Channel{name: newname, epoch: time.Now()}
	chann.userlist = make(map[int]*User)
	chann.usermodes = make(map[*User]string)
	chann.banlist = make(map[int]*Ban)
	chanlist[strings.ToLower(chann.name)] = chann
	chann.cmodes = config.DefaultCmode
	log.Printf("Channel %s created", chann.name)
	return chann
}

func (channel *Channel) len() int {
	k := len(channel.userlist)
	var check bool
	for _, k := range config.LogChannels {
		if channel == GetChannelByName(k) {
			check = true
			break
		}
	}
	if channel.HasUser(SystemUser) && !check {
		k--
	}
	return k
}

func (channel *Channel) JoinUser(user *User) {
	channel.userlist[user.id] = user
	if channel.len() == 1 {
		channel.usermodes[user] = "o"
		if config.SystemJoinChannels {
			SystemUser.JoinHandler([]string{"JOIN", channel.name})
		}
	}
	channel.SendLinef(":%s JOIN %s", user.GetHostMask(), channel.name)
	if len(channel.topic) > 0 {
		channel.FireTopic(user)
	}
	channel.FireNames(user)
}

func (channel *Channel) GetUserPrefix(user *User) string {
	if strings.Contains(channel.usermodes[user], "o") {
		return "@"
	}
	if strings.Contains(channel.usermodes[user], "v") {
		return "+"
	}
	return ""
}

func (channel *Channel) FireTopic(user *User) {
	if len(channel.topic) > 0 {
		user.FireNumeric(RPL_TOPIC, channel.name, channel.topic)
		user.FireNumeric(RPL_TOPICWHOTIME, channel.name, channel.topichost, channel.topictime)
	} else {
		user.FireNumeric(RPL_NOTOPIC, channel.name)
	}
}

func (channel *Channel) FireNames(user *User) {
	var buffer bytes.Buffer
	for _, k := range channel.userlist {
		if buffer.Len()+len(channel.GetUserPrefix(k))+len(user.nick) > 500 {
			user.FireNumeric(RPL_NAMEPLY, channel.name, strings.TrimSpace(buffer.String()))
			buffer.Reset()
		}
		buffer.WriteString(channel.GetUserPrefix(k))
		buffer.WriteString(k.nick)
		buffer.WriteString(" ")
	}
	if buffer.Len() > 1 {
		resp := strings.TrimSpace(buffer.String())
		user.FireNumeric(RPL_NAMEPLY, channel.name, resp)
	}
	user.FireNumeric(RPL_ENDOFNAMES, channel.name)
}

func (channel *Channel) GetUserList() []*User {
	list := []*User{}
	for _, k := range channel.userlist {
		list = append(list, k)
	}
	return list
}

func (channel *Channel) GetUserPriv(user *User) int {
	score := 0
	if strings.Contains(channel.usermodes[user], "o") {
		score += 100
	}
	if strings.Contains(channel.usermodes[user], "v") {
		score += 10
	}
	if user.oper {
		score += 1000
	}
	return score
}

func (channel *Channel) ShouldIDie() {
	if channel.len() < 1 {
		if channel.HasUser(SystemUser) {
			SystemUser.PartHandler([]string{"PART", channel.name})
		}
		delete(chanlist, strings.ToLower(channel.name))
		log.Printf("Channel %s has no users, destroying\n", channel.name)
	}
}

func (channel *Channel) FireModes(user *User) {
	user.FireNumeric(RPL_CHANNELMODEIS, channel.name, channel.cmodes)
	user.FireNumeric(RPL_CREATIONTIME, channel.name, channel.epoch.Unix())
}

func (channel *Channel) HasMode(mode string) bool {
	if strings.Contains(channel.cmodes, mode) {
		return true
	} else {
		return false
	}
}

func (channel *Channel) SetUmode(user *User, changing *User, mode string) {
	if channel.CheckYourPrivlege(changing) {
		return
	}
	if !strings.Contains(channel.usermodes[user], mode) {
		channel.usermodes[user] = channel.usermodes[user] + mode
		channel.SendLinef(":%s MODE %s +%s %s", changing.GetHostMask(), channel.name, mode, user.nick)
	}
}

func (channel *Channel) UnsetUmode(user *User, changing *User, mode string) {
	if channel.CheckYourPrivlege(changing) {
		return
	}
	if strings.Contains(channel.usermodes[user], mode) {
		channel.usermodes[user] = strings.Replace(channel.usermodes[user], mode, "", 1)
		channel.SendLinef(":%s MODE %s -%s %s", changing.GetHostMask(), channel.name, mode, user.nick)
	}
}

func (channel *Channel) SetMode(mode string, changing *User) {
	if channel.CheckYourPrivlege(changing) {
		return
	}
	if !strings.Contains(channel.cmodes, mode) {
		channel.cmodes = channel.cmodes + mode
		channel.SendLinef(":%s MODE %s +%s", changing.GetHostMask(), channel.name, mode)
	}
}

func (channel *Channel) UnsetMode(mode string, changing *User) {
	if channel.CheckYourPrivlege(changing) {
		return
	}
	if strings.Contains(channel.cmodes, mode) {
		channel.cmodes = strings.Replace(channel.cmodes, mode, "", 1)
		channel.SendLinef(":%s MODE %s -%s", changing.GetHostMask(), channel.name, mode)
	}
}

func (channel *Channel) HasUser(user *User) bool {
	if channel.userlist[user.id] == user {
		return true
	} else {
		return false
	}
}

func (channel *Channel) SendLinef(msg string, args ...interface{}) {
	for _, k := range channel.userlist {
		k.SendLine(fmt.Sprintf(msg, args...))
	}
}

func (channel *Channel) CheckYourPrivlege(user *User) bool {
	if channel.GetUserPriv(user) < 100 {
		//SHITLORD!
		user.FireNumeric(ERR_CHANOPRIVSNEEDED, channel.name)
		return true //privlege successfully checked.
	} else {
		return false
	}
}

func (channel *Channel) SetBan(m string, user *User) {
	if channel.CheckYourPrivlege(user) {
		return
	}
	if GetBanByMask(channel, m) != nil {
		return
	}
	hm := user.GetHostMask()
	b := NewBan(m, hm)
	channel.banlist[b.id] = b
	channel.SendLinef(":%s MODE %s +b %s", hm, channel.name, m)
}

func (channel *Channel) UnsetBan(m string, user *User) {
	if channel.CheckYourPrivlege(user) {
		return
	}
	ban := GetBanByMask(channel, m)
	if ban != nil {
		delete(channel.banlist, ban.id)
		channel.SendLinef(":%s MODE %s -b %s", user.GetHostMask(), channel.name, ban.mask)
	}
}

func (channel *Channel) IsUserBanned(user *User) bool {
	hm := user.GetHostMask()
	for _, k := range channel.banlist {
		if WildcardMatch(hm, k.mask) {
			return true
		}
	}
	return false
}

func (channel *Channel) FireBanlist(user *User) {
	for _, b := range channel.banlist {
		user.FireNumeric(RPL_BANLIST, channel.name, b.mask, b.whoset, b.ts.Unix())
	}
	user.FireNumeric(RPL_ENDOFBANLIST, channel.name)
}
<commit_msg>channels.go: add function to determine if channel is log channel<commit_after>package main

import (
	"bytes"
	"fmt"
	"strings"
	"time"
)

//Channel...
//represents an irc channel
type Channel struct {
	name      string
	epoch     time.Time
	userlist  map[int]*User
	usermodes map[*User]string
	banlist   map[int]*Ban
	cmodes    string
	topic     string
	topichost string
	topictime int64
}

func (channel *Channel) SetTopic(newtopic string, hostmask string) {
	channel.topic = newtopic
	channel.topichost = hostmask
	channel.topictime = time.Now().Unix()
	channel.SendLinef(":%s TOPIC %s :%s", hostmask, channel.name, newtopic)
}

func NewChannel(newname string) *Channel {
	chann := &Channel{name: newname, epoch: time.Now()}
	chann.userlist = make(map[int]*User)
	chann.usermodes = make(map[*User]string)
	chann.banlist = make(map[int]*Ban)
	chanlist[strings.ToLower(chann.name)] = chann
	chann.cmodes = config.DefaultCmode
	log.Printf("Channel %s created", chann.name)
	return chann
}

func (channel *Channel) len() int {
	k := len(channel.userlist)
	var check bool
	for _, k := range config.LogChannels {
		if channel == GetChannelByName(k) {
			check = true
			break
		}
	}
	if channel.HasUser(SystemUser) && !check {
		k--
	}
	return k
}

func (channel *Channel) JoinUser(user *User) {
	channel.userlist[user.id] = user
	if channel.len() == 1 {
		channel.usermodes[user] = "o"
		if config.SystemJoinChannels {
			SystemUser.JoinHandler([]string{"JOIN", channel.name})
		}
	}
	channel.SendLinef(":%s JOIN %s", user.GetHostMask(), channel.name)
	if len(channel.topic) > 0 {
		channel.FireTopic(user)
	}
	channel.FireNames(user)
}

func (channel *Channel) GetUserPrefix(user *User) string {
	if strings.Contains(channel.usermodes[user], "o") {
		return "@"
	}
	if strings.Contains(channel.usermodes[user], "v") {
		return "+"
	}
	return ""
}

func (channel *Channel) FireTopic(user *User) {
	if len(channel.topic) > 0 {
		user.FireNumeric(RPL_TOPIC, channel.name, channel.topic)
		user.FireNumeric(RPL_TOPICWHOTIME, channel.name, channel.topichost, channel.topictime)
	} else {
		user.FireNumeric(RPL_NOTOPIC, channel.name)
	}
}

func (channel *Channel) FireNames(user *User) {
	var buffer bytes.Buffer
	for _, k := range channel.userlist {
		if buffer.Len()+len(channel.GetUserPrefix(k))+len(user.nick) > 500 {
			user.FireNumeric(RPL_NAMEPLY, channel.name, strings.TrimSpace(buffer.String()))
			buffer.Reset()
		}
		buffer.WriteString(channel.GetUserPrefix(k))
		buffer.WriteString(k.nick)
		buffer.WriteString(" ")
	}
	if buffer.Len() > 1 {
		resp := strings.TrimSpace(buffer.String())
		user.FireNumeric(RPL_NAMEPLY, channel.name, resp)
	}
	user.FireNumeric(RPL_ENDOFNAMES, channel.name)
}

func (channel *Channel) GetUserList() []*User {
	list := []*User{}
	for _, k := range channel.userlist {
		list = append(list, k)
	}
	return list
}

func (channel *Channel) GetUserPriv(user *User) int {
	score := 0
	if strings.Contains(channel.usermodes[user], "o") {
		score += 100
	}
	if strings.Contains(channel.usermodes[user], "v") {
		score += 10
	}
	if user.oper {
		score += 1000
	}
	return score
}

func (channel *Channel) ShouldIDie() {
	if channel.len() < 1 {
		if channel.HasUser(SystemUser) {
			SystemUser.PartHandler([]string{"PART", channel.name})
		}
		delete(chanlist, strings.ToLower(channel.name))
		log.Printf("Channel %s has no users, destroying\n", channel.name)
	}
}

func (channel *Channel) FireModes(user *User) {
	user.FireNumeric(RPL_CHANNELMODEIS, channel.name, channel.cmodes)
	user.FireNumeric(RPL_CREATIONTIME, channel.name, channel.epoch.Unix())
}

func (channel *Channel) HasMode(mode string) bool {
	if strings.Contains(channel.cmodes, mode) {
		return true
	} else {
		return false
	}
}

func (channel *Channel) SetUmode(user *User, changing *User, mode string) {
	if channel.CheckYourPrivlege(changing) {
		return
	}
	if !strings.Contains(channel.usermodes[user], mode) {
		channel.usermodes[user] = channel.usermodes[user] + mode
		channel.SendLinef(":%s MODE %s +%s %s", changing.GetHostMask(), channel.name, mode, user.nick)
	}
}

func (channel *Channel) UnsetUmode(user *User, changing *User, mode string) {
	if channel.CheckYourPrivlege(changing) {
		return
	}
	if strings.Contains(channel.usermodes[user], mode) {
		channel.usermodes[user] = strings.Replace(channel.usermodes[user], mode, "", 1)
		channel.SendLinef(":%s MODE %s -%s %s", changing.GetHostMask(), channel.name, mode, user.nick)
	}
}

func (channel *Channel) SetMode(mode string, changing *User) {
	if channel.CheckYourPrivlege(changing) {
		return
	}
	if !strings.Contains(channel.cmodes, mode) {
		channel.cmodes = channel.cmodes + mode
		channel.SendLinef(":%s MODE %s +%s", changing.GetHostMask(), channel.name, mode)
	}
}

func (channel *Channel) UnsetMode(mode string, changing *User) {
	if channel.CheckYourPrivlege(changing) {
		return
	}
	if strings.Contains(channel.cmodes, mode) {
		channel.cmodes = strings.Replace(channel.cmodes, mode, "", 1)
		channel.SendLinef(":%s MODE %s -%s", changing.GetHostMask(), channel.name, mode)
	}
}

func (channel *Channel) HasUser(user *User) bool {
	if channel.userlist[user.id] == user {
		return true
	} else {
		return false
	}
}

func (channel *Channel) SendLinef(msg string, args ...interface{}) {
	for _, k := range channel.userlist {
		k.SendLine(fmt.Sprintf(msg, args...))
	}
}

func (channel *Channel) CheckYourPrivlege(user *User) bool {
	if channel.GetUserPriv(user) < 100 {
		//SHITLORD!
		user.FireNumeric(ERR_CHANOPRIVSNEEDED, channel.name)
		return true //privlege successfully checked.
	} else {
		return false
	}
}

func (channel *Channel) SetBan(m string, user *User) {
	if channel.CheckYourPrivlege(user) {
		return
	}
	if GetBanByMask(channel, m) != nil {
		return
	}
	hm := user.GetHostMask()
	b := NewBan(m, hm)
	channel.banlist[b.id] = b
	channel.SendLinef(":%s MODE %s +b %s", hm, channel.name, m)
}

func (channel *Channel) UnsetBan(m string, user *User) {
	if channel.CheckYourPrivlege(user) {
		return
	}
	ban := GetBanByMask(channel, m)
	if ban != nil {
		delete(channel.banlist, ban.id)
		channel.SendLinef(":%s MODE %s -b %s", user.GetHostMask(), channel.name, ban.mask)
	}
}

func (channel *Channel) IsUserBanned(user *User) bool {
	hm := user.GetHostMask()
	for _, k := range channel.banlist {
		if WildcardMatch(hm, k.mask) {
			return true
		}
	}
	return false
}

func (channel *Channel) FireBanlist(user *User) {
	for _, b := range channel.banlist {
		user.FireNumeric(RPL_BANLIST, channel.name, b.mask, b.whoset, b.ts.Unix())
	}
	user.FireNumeric(RPL_ENDOFBANLIST, channel.name)
}

func (channel *Channel) IsLogChan(user *User) bool {
  me := strings.ToLower(channel.name)
  for _, k := range config.LogChannels {
    if me == strings.ToLower(k) {
      return true
    }
  }
  return false
}
<|endoftext|>


<commit_before>package lib

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

const (
	// DefaultHooksDirPath Default directory containing hooks config files
	DefaultHooksDirPath = "/usr/share/containers/oci/hooks.d"
	// OverrideHooksDirPath Directory where admin can override the default configuration
	OverrideHooksDirPath = "/etc/containers/oci/hooks.d"
)

// HookParams is the structure returned from read the hooks configuration
type HookParams struct {
	Hook          string   `json:"hook"`
	Stage         []string `json:"stage"`
	Cmds          []string `json:"cmd"`
	Annotations   []string `json:"annotation"`
	HasBindMounts bool     `json:"hasbindmounts"`
	Arguments     []string `json:"arguments"`
}

var (
	// ErrNoJSONSuffix represents hook-add attempts where the filename
	// does not end in '.json'.
	ErrNoJSONSuffix = errors.New("hook filename does not end in '.json'")
)

// readHook reads hooks json files, verifies it and returns the json config
func readHook(hookPath string) (HookParams, error) {
	var hook HookParams
	if !strings.HasSuffix(hookPath, ".json") {
		return hook, ErrNoJSONSuffix
	}
	raw, err := ioutil.ReadFile(hookPath)
	if err != nil {
		return hook, errors.Wrapf(err, "error Reading hook %q", hookPath)
	}
	if err := json.Unmarshal(raw, &hook); err != nil {
		return hook, errors.Wrapf(err, "error Unmarshalling JSON for %q", hookPath)
	}
	if _, err := os.Stat(hook.Hook); err != nil {
		return hook, errors.Wrapf(err, "unable to stat hook %q in hook config %q", hook.Hook, hookPath)
	}
	validStage := map[string]bool{"prestart": true, "poststart": true, "poststop": true}
	for _, cmd := range hook.Cmds {
		if _, err = regexp.Compile(cmd); err != nil {
			return hook, errors.Wrapf(err, "invalid cmd regular expression %q defined in hook config %q", cmd, hookPath)
		}
	}
	for _, cmd := range hook.Annotations {
		if _, err = regexp.Compile(cmd); err != nil {
			return hook, errors.Wrapf(err, "invalid cmd regular expression %q defined in hook config %q", cmd, hookPath)
		}
	}
	if len(hook.Stage) == 0 {
		logrus.Warnf("No stage defined in hook config %q, hook will never run", hookPath)
	} else {
		for _, stage := range hook.Stage {
			if !validStage[stage] {
				return hook, errors.Wrapf(err, "unknown stage %q defined in hook config %q", stage, hookPath)
			}
		}
	}
	return hook, nil
}

// readHooks reads hooks json files in directory to setup OCI Hooks
// adding hooks to the passedin hooks map.
func readHooks(hooksPath string, hooks map[string]HookParams) error {
	if _, err := os.Stat(hooksPath); err != nil {
		if os.IsNotExist(err) {
			logrus.Warnf("hooks path: %q does not exist", hooksPath)
			return nil
		}
		return errors.Wrapf(err, "unable to stat hooks path %q", hooksPath)
	}

	files, err := ioutil.ReadDir(hooksPath)
	if err != nil {
		return err
	}

	for _, file := range files {
		hook, err := readHook(filepath.Join(hooksPath, file.Name()))
		if err != nil {
			if err == ErrNoJSONSuffix {
				continue
			}
			return err
		}
		for key, h := range hooks {
			// hook.Hook can only be defined in one hook file, unless it has the
			// same name in the override path.
			if hook.Hook == h.Hook && key != file.Name() {
				return errors.Wrapf(syscall.EINVAL, "duplicate path,  hook %q from %q already defined in %q", hook.Hook, hooksPath, key)
			}
		}
		hooks[file.Name()] = hook
	}
	return nil
}
<commit_msg>lib/hooks: Allow the same command in multiple hook files<commit_after>package lib

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

const (
	// DefaultHooksDirPath Default directory containing hooks config files
	DefaultHooksDirPath = "/usr/share/containers/oci/hooks.d"
	// OverrideHooksDirPath Directory where admin can override the default configuration
	OverrideHooksDirPath = "/etc/containers/oci/hooks.d"
)

// HookParams is the structure returned from read the hooks configuration
type HookParams struct {
	Hook          string   `json:"hook"`
	Stage         []string `json:"stage"`
	Cmds          []string `json:"cmd"`
	Annotations   []string `json:"annotation"`
	HasBindMounts bool     `json:"hasbindmounts"`
	Arguments     []string `json:"arguments"`
}

var (
	// ErrNoJSONSuffix represents hook-add attempts where the filename
	// does not end in '.json'.
	ErrNoJSONSuffix = errors.New("hook filename does not end in '.json'")
)

// readHook reads hooks json files, verifies it and returns the json config
func readHook(hookPath string) (HookParams, error) {
	var hook HookParams
	if !strings.HasSuffix(hookPath, ".json") {
		return hook, ErrNoJSONSuffix
	}
	raw, err := ioutil.ReadFile(hookPath)
	if err != nil {
		return hook, errors.Wrapf(err, "error Reading hook %q", hookPath)
	}
	if err := json.Unmarshal(raw, &hook); err != nil {
		return hook, errors.Wrapf(err, "error Unmarshalling JSON for %q", hookPath)
	}
	if _, err := os.Stat(hook.Hook); err != nil {
		return hook, errors.Wrapf(err, "unable to stat hook %q in hook config %q", hook.Hook, hookPath)
	}
	validStage := map[string]bool{"prestart": true, "poststart": true, "poststop": true}
	for _, cmd := range hook.Cmds {
		if _, err = regexp.Compile(cmd); err != nil {
			return hook, errors.Wrapf(err, "invalid cmd regular expression %q defined in hook config %q", cmd, hookPath)
		}
	}
	for _, cmd := range hook.Annotations {
		if _, err = regexp.Compile(cmd); err != nil {
			return hook, errors.Wrapf(err, "invalid cmd regular expression %q defined in hook config %q", cmd, hookPath)
		}
	}
	if len(hook.Stage) == 0 {
		logrus.Warnf("No stage defined in hook config %q, hook will never run", hookPath)
	} else {
		for _, stage := range hook.Stage {
			if !validStage[stage] {
				return hook, errors.Wrapf(err, "unknown stage %q defined in hook config %q", stage, hookPath)
			}
		}
	}
	return hook, nil
}

// readHooks reads hooks json files in directory to setup OCI Hooks
// adding hooks to the passedin hooks map.
func readHooks(hooksPath string, hooks map[string]HookParams) error {
	if _, err := os.Stat(hooksPath); err != nil {
		if os.IsNotExist(err) {
			logrus.Warnf("hooks path: %q does not exist", hooksPath)
			return nil
		}
		return errors.Wrapf(err, "unable to stat hooks path %q", hooksPath)
	}

	files, err := ioutil.ReadDir(hooksPath)
	if err != nil {
		return err
	}

	for _, file := range files {
		hook, err := readHook(filepath.Join(hooksPath, file.Name()))
		if err != nil {
			if err == ErrNoJSONSuffix {
				continue
			}
			return err
		}
		hooks[file.Name()] = hook
	}
	return nil
}
<|endoftext|>


<commit_before>package ui

// Cell is an indivisible unit on the screen. It is not necessarily 1 column
// wide.
type Cell struct {
	Text  string
	Width byte
	Style string
}

// Pos is the position within a buffer.
type Pos struct {
	Line, Col int
}

// CellsWidth returns the total width of a Cell slice.
func CellsWidth(cs []Cell) int {
	w := 0
	for _, c := range cs {
		w += int(c.Width)
	}
	return w
}

// CompareCells returns whether two Cell slices are equal, and when they are
// not, the first index at which they differ.
func CompareCells(r1, r2 []Cell) (bool, int) {
	for i, c := range r1 {
		if i >= len(r2) || c != r2[i] {
			return false, i
		}
	}
	if len(r1) < len(r2) {
		return false, len(r1)
	}
	return true, 0
}

// Buffer reflects a continuous range of lines on the terminal.
//
// The Unix terminal API provides only awkward ways of querying the terminal
// Buffer, so we keep an internal reflection and do one-way synchronizations
// (Buffer -> terminal, and not the other way around). This requires us to
// exactly match the terminal's idea of the width of characters (wcwidth) and
// where to insert soft carriage returns, so there could be bugs.
type Buffer struct {
	Width, Col, Indent int
	// EagerWrap controls whether to wrap line as soon as the cursor reaches the
	// right edge of the terminal. This is not often desirable as it creates
	// unneessary line breaks, but is is useful when echoing the user input.
	// will otherwise
	EagerWrap bool
	// Lines the content of the buffer.
	Lines [][]Cell
	// Dot is what the user perceives as the cursor.
	Dot Pos
}

// NewBuffer builds a new buffer, with one empty line.
func NewBuffer(width int) *Buffer {
	return &Buffer{Width: width, Lines: [][]Cell{make([]Cell, 0, width)}}
}

func (b *Buffer) SetIndent(indent int) *Buffer {
	b.Indent = indent
	return b
}

func (b *Buffer) SetEagerWrap(v bool) *Buffer {
	b.EagerWrap = v
	return b
}

func (b *Buffer) SetLines(lines ...[]Cell) *Buffer {
	b.Lines = lines
	b.Col = CellsWidth(lines[len(lines)-1])
	return b
}

func (b *Buffer) SetDot(dot Pos) *Buffer {
	b.Dot = dot
	return b
}

// Cursor returns the current position of the cursor.
func (b *Buffer) Cursor() Pos {
	return Pos{len(b.Lines) - 1, b.Col}
}

// BuffersHeight computes the combined height of a number of buffers.
func BuffersHeight(bufs ...*Buffer) (l int) {
	for _, buf := range bufs {
		if buf != nil {
			l += len(buf.Lines)
		}
	}
	return
}

// TrimToLines trims a buffer to the lines [low, high).
func (b *Buffer) TrimToLines(low, high int) {
	for i := 0; i < low; i++ {
		b.Lines[i] = nil
	}
	for i := high; i < len(b.Lines); i++ {
		b.Lines[i] = nil
	}
	b.Lines = b.Lines[low:high]
	b.Dot.Line -= low
	if b.Dot.Line < 0 {
		b.Dot.Line = 0
	}
}
<commit_msg>edit/ui: Remove Indent and EagerWrap from Buffer.<commit_after>package ui

// Cell is an indivisible unit on the screen. It is not necessarily 1 column
// wide.
type Cell struct {
	Text  string
	Width byte
	Style string
}

// Pos is the position within a buffer.
type Pos struct {
	Line, Col int
}

// CellsWidth returns the total width of a Cell slice.
func CellsWidth(cs []Cell) int {
	w := 0
	for _, c := range cs {
		w += int(c.Width)
	}
	return w
}

// CompareCells returns whether two Cell slices are equal, and when they are
// not, the first index at which they differ.
func CompareCells(r1, r2 []Cell) (bool, int) {
	for i, c := range r1 {
		if i >= len(r2) || c != r2[i] {
			return false, i
		}
	}
	if len(r1) < len(r2) {
		return false, len(r1)
	}
	return true, 0
}

// Buffer reflects a continuous range of lines on the terminal.
//
// The Unix terminal API provides only awkward ways of querying the terminal
// Buffer, so we keep an internal reflection and do one-way synchronizations
// (Buffer -> terminal, and not the other way around). This requires us to
// exactly match the terminal's idea of the width of characters (wcwidth) and
// where to insert soft carriage returns, so there could be bugs.
type Buffer struct {
	Width, Col int
	// Lines the content of the buffer.
	Lines [][]Cell
	// Dot is what the user perceives as the cursor.
	Dot Pos
}

// NewBuffer builds a new buffer, with one empty line.
func NewBuffer(width int) *Buffer {
	return &Buffer{Width: width, Lines: [][]Cell{make([]Cell, 0, width)}}
}

func (b *Buffer) SetLines(lines ...[]Cell) *Buffer {
	b.Lines = lines
	b.Col = CellsWidth(lines[len(lines)-1])
	return b
}

func (b *Buffer) SetDot(dot Pos) *Buffer {
	b.Dot = dot
	return b
}

// Cursor returns the current position of the cursor.
func (b *Buffer) Cursor() Pos {
	return Pos{len(b.Lines) - 1, b.Col}
}

// BuffersHeight computes the combined height of a number of buffers.
func BuffersHeight(bufs ...*Buffer) (l int) {
	for _, buf := range bufs {
		if buf != nil {
			l += len(buf.Lines)
		}
	}
	return
}

// TrimToLines trims a buffer to the lines [low, high).
func (b *Buffer) TrimToLines(low, high int) {
	for i := 0; i < low; i++ {
		b.Lines[i] = nil
	}
	for i := high; i < len(b.Lines); i++ {
		b.Lines[i] = nil
	}
	b.Lines = b.Lines[low:high]
	b.Dot.Line -= low
	if b.Dot.Line < 0 {
		b.Dot.Line = 0
	}
}
<|endoftext|>


<commit_before>package version

// Version tells us the app version string
const Version = "3.1.0-rc1"
<commit_msg>Bump to v3.1.0-rc2<commit_after>package version

// Version tells us the app version string
const Version = "3.1.0-rc2"
<|endoftext|>


<commit_before>//go:generate go run gen.go

package version

// Version tells us the app version string
const Version = "4.0.1"
<commit_msg>Bump to v4.0.2<commit_after>//go:generate go run gen.go

package version

// Version tells us the app version string
const Version = "4.0.2"
<|endoftext|>


<commit_before>package main

import (
	"remmler.org/go/bump.git"
	"code.google.com/p/go.net/websocket"

	"go/build"
	"math/rand"
	"net/http"
	"os"
	"time"
)

func main() {
	rand.Seed(time.Now().UTC().UnixNano())
	bump := bump.NewBump()
	bump.Run()

	clientDir := build.Default.GOPATH + "/src/remmler.org/go/bump.git/client"
	http.Handle("/bump/", websocket.Handler(bump.WSHandler()))
	http.Handle("/", http.FileServer(http.Dir(clientDir)))
	port := ":8000"
	if len(os.Args) > 1 {
		port = ":" + os.Args[1]
	}
	if err := http.ListenAndServe(port, nil); err != nil {
		panic("ListenAndServe: " + err.Error())
	}
}
<commit_msg>Githubified paths.<commit_after>package main

import (
	"github.com/ianremmler/bump"
	"code.google.com/p/go.net/websocket"

	"go/build"
	"math/rand"
	"net/http"
	"os"
	"time"
)

func main() {
	rand.Seed(time.Now().UTC().UnixNano())
	bump := bump.NewBump()
	bump.Run()

	clientDir := build.Default.GOPATH + "/src/github.com/ianremmler/bump/client"
	http.Handle("/bump/", websocket.Handler(bump.WSHandler()))
	http.Handle("/", http.FileServer(http.Dir(clientDir)))
	port := ":8000"
	if len(os.Args) > 1 {
		port = ":" + os.Args[1]
	}
	if err := http.ListenAndServe(port, nil); err != nil {
		panic("ListenAndServe: " + err.Error())
	}
}
<|endoftext|>


<commit_before>package cache

import "sync"

type LRU struct {
	sync.Mutex

	toList     map[string]*list
	head, tail list
}

func NewLRU() *LRU {

	lru := new(LRU)

	lru.toList = make(map[string]*list)
	lru.head.next = &lru.tail
	lru.tail.prev = &lru.head

	return lru
}

func (lru *LRU) Push(id string) {

	if _, ok := lru.toList[id]; ok {
		return
	}

	lru.push(id)
}

func (lru *LRU) push(id string) {
	l := list{id: id}

	prev := lru.last()
	l.prev = prev
	prev.next = &l

	l.next = &lru.tail
	lru.tail.prev = &l

	lru.toList[id] = &l
}

func (lru *LRU) Visit(id string) {

	lru.Lock()
	defer lru.Unlock()

	lru.toList[id].remove()
	lru.push(id)
}

func (lru *LRU) Pop() (id string) {

	first := lru.first()
	if first == &lru.tail {
		panic("LRU underflow")
	}

	id = first.id
	first.remove()
	delete(lru.toList, id)

	return
}

func (lru LRU) first() *list {
	return lru.head.next
}

func (lru LRU) last() *list {
	return lru.tail.prev
}

type list struct {
	next, prev *list
	id         string
}

func (l list) remove() {
	l.prev.next = l.next
	l.next.prev = l.prev
}
<commit_msg>fixed lru.go data race caused by cache copying<commit_after>package cache

import "sync"

type LRU struct {
	sync.Mutex

	toList     map[string]*list
	head, tail list
}

func NewLRU() *LRU {

	lru := new(LRU)

	lru.toList = make(map[string]*list)
	lru.head.next = &lru.tail
	lru.tail.prev = &lru.head

	return lru
}

func (lru *LRU) Push(id string) {

	if _, ok := lru.toList[id]; ok {
		return
	}

	lru.push(id)
}

func (lru *LRU) push(id string) {
	l := list{id: id}

	prev := lru.last()
	l.prev = prev
	prev.next = &l

	l.next = &lru.tail
	lru.tail.prev = &l

	lru.toList[id] = &l
}

func (lru *LRU) Visit(id string) {

	lru.Lock()
	defer lru.Unlock()

	lru.toList[id].remove()
	lru.push(id)
}

func (lru *LRU) Pop() (id string) {

	first := lru.first()
	if first == &lru.tail {
		panic("LRU underflow")
	}

	id = first.id
	first.remove()
	delete(lru.toList, id)

	return
}

func (lru *LRU) first() *list {
	return lru.head.next
}

func (lru *LRU) last() *list {
	return lru.tail.prev
}

type list struct {
	next, prev *list
	id         string
}

func (l list) remove() {
	l.prev.next = l.next
	l.next.prev = l.prev
}

<|endoftext|>


<commit_before>package server

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

	"github.com/Sirupsen/logrus"
	"github.com/kubernetes-incubator/cri-o/oci"
	"github.com/kubernetes-incubator/cri-o/utils"
	"github.com/opencontainers/runtime-tools/generate"
	"golang.org/x/net/context"
	pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
)

const (
	// ContainerStateCreated represents the created state of a container
	ContainerStateCreated = "created"
	// ContainerStateRunning represents the running state of a container
	ContainerStateRunning = "running"
	// ContainerStateStopped represents the stopped state of a container
	ContainerStateStopped = "stopped"
)

// CreateContainer creates a new container in specified PodSandbox
func (s *Server) CreateContainer(ctx context.Context, req *pb.CreateContainerRequest) (*pb.CreateContainerResponse, error) {
	sbID := req.GetPodSandboxId()
	if sbID == "" {
		return nil, fmt.Errorf("PodSandboxId should not be empty")
	}

	sandboxID, err := s.podIDIndex.Get(sbID)
	if err != nil {
		return nil, fmt.Errorf("PodSandbox with ID starting with %s not found: %v", sbID, err)
	}

	sb := s.getSandbox(sandboxID)
	if sb == nil {
		return nil, fmt.Errorf("specified sandbox not found: %s", sandboxID)
	}

	// The config of the container
	containerConfig := req.GetConfig()
	if containerConfig == nil {
		return nil, fmt.Errorf("CreateContainerRequest.ContainerConfig is nil")
	}

	name := containerConfig.GetMetadata().GetName()
	if name == "" {
		return nil, fmt.Errorf("CreateContainerRequest.ContainerConfig.Name is empty")
	}

	// containerDir is the dir for the container bundle.
	containerDir := filepath.Join(s.runtime.ContainerDir(), name)

	if _, err = os.Stat(containerDir); err == nil {
		return nil, fmt.Errorf("container (%s) already exists", containerDir)
	}

	if err = os.MkdirAll(containerDir, 0755); err != nil {
		return nil, err
	}

	container, err := s.createSandboxContainer(name, sb, req.GetSandboxConfig(), containerDir, containerConfig)
	if err != nil {
		return nil, err
	}

	if err := s.runtime.CreateContainer(container); err != nil {
		return nil, err
	}

	if err := s.runtime.UpdateStatus(container); err != nil {
		return nil, err
	}

	s.addContainer(container)

	return &pb.CreateContainerResponse{
		ContainerId: &name,
	}, nil
}

func (s *Server) createSandboxContainer(name string, sb *sandbox, SandboxConfig *pb.PodSandboxConfig, containerDir string, containerConfig *pb.ContainerConfig) (*oci.Container, error) {
	if sb == nil {
		return nil, errors.New("createSandboxContainer needs a sandbox")
	}
	// creates a spec Generator with the default spec.
	specgen := generate.New()

	// by default, the root path is an empty string.
	// here set it to be "rootfs".
	specgen.SetRootPath("rootfs")

	args := containerConfig.GetArgs()
	if args == nil {
		args = []string{"/bin/sh"}
	}
	specgen.SetProcessArgs(args)

	cwd := containerConfig.GetWorkingDir()
	if cwd == "" {
		cwd = "/"
	}
	specgen.SetProcessCwd(cwd)

	envs := containerConfig.GetEnvs()
	if envs != nil {
		for _, item := range envs {
			key := item.GetKey()
			value := item.GetValue()
			if key == "" {
				continue
			}
			env := fmt.Sprintf("%s=%s", key, value)
			specgen.AddProcessEnv(env)
		}
	}

	mounts := containerConfig.GetMounts()
	for _, mount := range mounts {
		dest := mount.GetContainerPath()
		if dest == "" {
			return nil, fmt.Errorf("Mount.ContainerPath is empty")
		}

		src := mount.GetHostPath()
		if src == "" {
			return nil, fmt.Errorf("Mount.HostPath is empty")
		}

		options := "rw"
		if mount.GetReadonly() {
			options = "ro"
		}

		//TODO(hmeng): how to use this info? Do we need to handle relabel a FS with Selinux?
		//selinuxRelabel := mount.GetSelinuxRelabel()

		specgen.AddBindMount(src, dest, options)

	}

	labels := containerConfig.GetLabels()

	annotations := containerConfig.GetAnnotations()
	if annotations != nil {
		for k, v := range annotations {
			specgen.AddAnnotation(k, v)
		}
	}

	if containerConfig.GetPrivileged() {
		specgen.SetupPrivileged(true)
	}

	if containerConfig.GetReadonlyRootfs() {
		specgen.SetRootReadonly(true)
	}

	logPath := containerConfig.GetLogPath()

	if containerConfig.GetTty() {
		specgen.SetProcessTerminal(true)
	}

	linux := containerConfig.GetLinux()
	if linux != nil {
		resources := linux.GetResources()
		if resources != nil {
			cpuPeriod := resources.GetCpuPeriod()
			if cpuPeriod != 0 {
				specgen.SetLinuxResourcesCPUPeriod(uint64(cpuPeriod))
			}

			cpuQuota := resources.GetCpuQuota()
			if cpuQuota != 0 {
				specgen.SetLinuxResourcesCPUQuota(uint64(cpuQuota))
			}

			cpuShares := resources.GetCpuShares()
			if cpuShares != 0 {
				specgen.SetLinuxResourcesCPUShares(uint64(cpuShares))
			}

			memoryLimit := resources.GetMemoryLimitInBytes()
			if memoryLimit != 0 {
				specgen.SetLinuxResourcesMemoryLimit(uint64(memoryLimit))
			}

			oomScoreAdj := resources.GetOomScoreAdj()
			specgen.SetLinuxResourcesOOMScoreAdj(int(oomScoreAdj))
		}

		capabilities := linux.GetCapabilities()
		if capabilities != nil {
			addCaps := capabilities.GetAddCapabilities()
			if addCaps != nil {
				for _, cap := range addCaps {
					if err := specgen.AddProcessCapability(cap); err != nil {
						return nil, err
					}
				}
			}

			dropCaps := capabilities.GetDropCapabilities()
			if dropCaps != nil {
				for _, cap := range dropCaps {
					if err := specgen.DropProcessCapability(cap); err != nil {
						return nil, err
					}
				}
			}
		}

		selinuxOptions := linux.GetSelinuxOptions()
		if selinuxOptions != nil {
			user := selinuxOptions.GetUser()
			if user == "" {
				return nil, fmt.Errorf("SELinuxOption.User is empty")
			}

			role := selinuxOptions.GetRole()
			if role == "" {
				return nil, fmt.Errorf("SELinuxOption.Role is empty")
			}

			t := selinuxOptions.GetType()
			if t == "" {
				return nil, fmt.Errorf("SELinuxOption.Type is empty")
			}

			level := selinuxOptions.GetLevel()
			if level == "" {
				return nil, fmt.Errorf("SELinuxOption.Level is empty")
			}

			specgen.SetProcessSelinuxLabel(fmt.Sprintf("%s:%s:%s:%s", user, role, t, level))
		}

		user := linux.GetUser()
		if user != nil {
			uid := user.GetUid()
			specgen.SetProcessUID(uint32(uid))

			gid := user.GetGid()
			specgen.SetProcessGID(uint32(gid))

			groups := user.GetAdditionalGids()
			if groups != nil {
				for _, group := range groups {
					specgen.AddProcessAdditionalGid(uint32(group))
				}
			}
		}
	}
	// Join the namespace paths for the pod sandbox container.
	podContainerName := sb.name + "-infra"
	podInfraContainer := s.state.containers.Get(podContainerName)
	podInfraState := s.runtime.ContainerStatus(podInfraContainer)

	logrus.Infof("pod container state %v", podInfraState)

	for nsType, nsFile := range map[string]string{
		"ipc":     "ipc",
		"uts":     "uts",
		"network": "net",
	} {
		nsPath := fmt.Sprintf("/proc/%d/ns/%s", podInfraState.Pid, nsFile)
		if err := specgen.AddOrReplaceLinuxNamespace(nsType, nsPath); err != nil {
			return nil, err
		}
	}

	if err := specgen.SaveToFile(filepath.Join(containerDir, "config.json")); err != nil {
		return nil, err
	}

	imageSpec := containerConfig.GetImage()
	if imageSpec == nil {
		return nil, fmt.Errorf("CreateContainerRequest.ContainerConfig.Image is nil")
	}

	image := imageSpec.GetImage()
	if image == "" {
		return nil, fmt.Errorf("CreateContainerRequest.ContainerConfig.Image.Image is empty")
	}

	// TODO: copy the rootfs into the bundle.
	// Currently, utils.CreateFakeRootfs is used to populate the rootfs.
	if err := utils.CreateFakeRootfs(containerDir, image); err != nil {
		return nil, err
	}

	container, err := oci.NewContainer(name, containerDir, logPath, labels, sb.id, containerConfig.GetTty())
	if err != nil {
		return nil, err
	}

	return container, nil
}

// StartContainer starts the container.
func (s *Server) StartContainer(ctx context.Context, req *pb.StartContainerRequest) (*pb.StartContainerResponse, error) {
	containerName := req.ContainerId

	if *containerName == "" {
		return nil, fmt.Errorf("container ID should not be empty")
	}
	c := s.state.containers.Get(*containerName)
	if c == nil {
		return nil, fmt.Errorf("specified container not found: %s", *containerName)
	}

	if err := s.runtime.StartContainer(c); err != nil {
		return nil, fmt.Errorf("failed to start container %s in sandbox %s: %v", c.Name(), *containerName, err)
	}

	return &pb.StartContainerResponse{}, nil
}

// StopContainer stops a running container with a grace period (i.e., timeout).
func (s *Server) StopContainer(ctx context.Context, req *pb.StopContainerRequest) (*pb.StopContainerResponse, error) {
	containerName := req.ContainerId

	if *containerName == "" {
		return nil, fmt.Errorf("container ID should not be empty")
	}
	c := s.state.containers.Get(*containerName)
	if c == nil {
		return nil, fmt.Errorf("specified container not found: %s", *containerName)
	}

	if err := s.runtime.StopContainer(c); err != nil {
		return nil, fmt.Errorf("failed to stop container %s: %v", *containerName, err)
	}

	return &pb.StopContainerResponse{}, nil
}

// RemoveContainer removes the container. If the container is running, the container
// should be force removed.
func (s *Server) RemoveContainer(ctx context.Context, req *pb.RemoveContainerRequest) (*pb.RemoveContainerResponse, error) {
	containerName := req.ContainerId

	if *containerName == "" {
		return nil, fmt.Errorf("container ID should not be empty")
	}
	c := s.state.containers.Get(*containerName)
	if c == nil {
		return nil, fmt.Errorf("specified container not found: %s", *containerName)
	}

	if err := s.runtime.DeleteContainer(c); err != nil {
		return nil, fmt.Errorf("failed to delete container %s: %v", *containerName, err)
	}

	containerDir := filepath.Join(s.runtime.ContainerDir(), *containerName)
	if err := os.RemoveAll(containerDir); err != nil {
		return nil, fmt.Errorf("failed to remove container %s directory: %v", *containerName, err)
	}

	s.removeContainer(c)

	return &pb.RemoveContainerResponse{}, nil
}

// ListContainers lists all containers by filters.
func (s *Server) ListContainers(context.Context, *pb.ListContainersRequest) (*pb.ListContainersResponse, error) {
	return nil, nil
}

// ContainerStatus returns status of the container.
func (s *Server) ContainerStatus(ctx context.Context, req *pb.ContainerStatusRequest) (*pb.ContainerStatusResponse, error) {
	containerName := req.ContainerId

	if *containerName == "" {
		return nil, fmt.Errorf("container ID should not be empty")
	}
	c := s.state.containers.Get(*containerName)

	if c == nil {
		return nil, fmt.Errorf("specified container not found: %s", *containerName)
	}

	if err := s.runtime.UpdateStatus(c); err != nil {
		return nil, err
	}

	csr := &pb.ContainerStatusResponse{
		Status: &pb.ContainerStatus{
			Id: containerName,
		},
	}

	cState := s.runtime.ContainerStatus(c)
	rStatus := pb.ContainerState_UNKNOWN

	switch cState.Status {
	case ContainerStateCreated:
		rStatus = pb.ContainerState_CREATED
		created := cState.Created.Unix()
		csr.Status.CreatedAt = int64Ptr(created)
	case ContainerStateRunning:
		rStatus = pb.ContainerState_RUNNING
		created := cState.Created.Unix()
		csr.Status.CreatedAt = int64Ptr(created)
		started := cState.Started.Unix()
		csr.Status.StartedAt = int64Ptr(started)
	case ContainerStateStopped:
		rStatus = pb.ContainerState_EXITED
		created := cState.Created.Unix()
		csr.Status.CreatedAt = int64Ptr(created)
		started := cState.Started.Unix()
		csr.Status.StartedAt = int64Ptr(started)
		finished := cState.Finished.Unix()
		csr.Status.FinishedAt = int64Ptr(finished)
		csr.Status.ExitCode = int32Ptr(cState.ExitCode)
	}

	csr.Status.State = &rStatus

	return csr, nil
}

// Exec executes the command in the container.
func (s *Server) Exec(pb.RuntimeService_ExecServer) error {
	return nil
}
<commit_msg>Add server impl for listing containers<commit_after>package server

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

	"github.com/Sirupsen/logrus"
	"github.com/kubernetes-incubator/cri-o/oci"
	"github.com/kubernetes-incubator/cri-o/utils"
	"github.com/opencontainers/runtime-tools/generate"
	"golang.org/x/net/context"
	pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
)

const (
	// ContainerStateCreated represents the created state of a container
	ContainerStateCreated = "created"
	// ContainerStateRunning represents the running state of a container
	ContainerStateRunning = "running"
	// ContainerStateStopped represents the stopped state of a container
	ContainerStateStopped = "stopped"
)

// CreateContainer creates a new container in specified PodSandbox
func (s *Server) CreateContainer(ctx context.Context, req *pb.CreateContainerRequest) (*pb.CreateContainerResponse, error) {
	sbID := req.GetPodSandboxId()
	if sbID == "" {
		return nil, fmt.Errorf("PodSandboxId should not be empty")
	}

	sandboxID, err := s.podIDIndex.Get(sbID)
	if err != nil {
		return nil, fmt.Errorf("PodSandbox with ID starting with %s not found: %v", sbID, err)
	}

	sb := s.getSandbox(sandboxID)
	if sb == nil {
		return nil, fmt.Errorf("specified sandbox not found: %s", sandboxID)
	}

	// The config of the container
	containerConfig := req.GetConfig()
	if containerConfig == nil {
		return nil, fmt.Errorf("CreateContainerRequest.ContainerConfig is nil")
	}

	name := containerConfig.GetMetadata().GetName()
	if name == "" {
		return nil, fmt.Errorf("CreateContainerRequest.ContainerConfig.Name is empty")
	}

	// containerDir is the dir for the container bundle.
	containerDir := filepath.Join(s.runtime.ContainerDir(), name)

	if _, err = os.Stat(containerDir); err == nil {
		return nil, fmt.Errorf("container (%s) already exists", containerDir)
	}

	if err = os.MkdirAll(containerDir, 0755); err != nil {
		return nil, err
	}

	container, err := s.createSandboxContainer(name, sb, req.GetSandboxConfig(), containerDir, containerConfig)
	if err != nil {
		return nil, err
	}

	if err := s.runtime.CreateContainer(container); err != nil {
		return nil, err
	}

	if err := s.runtime.UpdateStatus(container); err != nil {
		return nil, err
	}

	s.addContainer(container)

	return &pb.CreateContainerResponse{
		ContainerId: &name,
	}, nil
}

func (s *Server) createSandboxContainer(name string, sb *sandbox, SandboxConfig *pb.PodSandboxConfig, containerDir string, containerConfig *pb.ContainerConfig) (*oci.Container, error) {
	if sb == nil {
		return nil, errors.New("createSandboxContainer needs a sandbox")
	}
	// creates a spec Generator with the default spec.
	specgen := generate.New()

	// by default, the root path is an empty string.
	// here set it to be "rootfs".
	specgen.SetRootPath("rootfs")

	args := containerConfig.GetArgs()
	if args == nil {
		args = []string{"/bin/sh"}
	}
	specgen.SetProcessArgs(args)

	cwd := containerConfig.GetWorkingDir()
	if cwd == "" {
		cwd = "/"
	}
	specgen.SetProcessCwd(cwd)

	envs := containerConfig.GetEnvs()
	if envs != nil {
		for _, item := range envs {
			key := item.GetKey()
			value := item.GetValue()
			if key == "" {
				continue
			}
			env := fmt.Sprintf("%s=%s", key, value)
			specgen.AddProcessEnv(env)
		}
	}

	mounts := containerConfig.GetMounts()
	for _, mount := range mounts {
		dest := mount.GetContainerPath()
		if dest == "" {
			return nil, fmt.Errorf("Mount.ContainerPath is empty")
		}

		src := mount.GetHostPath()
		if src == "" {
			return nil, fmt.Errorf("Mount.HostPath is empty")
		}

		options := "rw"
		if mount.GetReadonly() {
			options = "ro"
		}

		//TODO(hmeng): how to use this info? Do we need to handle relabel a FS with Selinux?
		//selinuxRelabel := mount.GetSelinuxRelabel()

		specgen.AddBindMount(src, dest, options)

	}

	labels := containerConfig.GetLabels()

	annotations := containerConfig.GetAnnotations()
	if annotations != nil {
		for k, v := range annotations {
			specgen.AddAnnotation(k, v)
		}
	}

	if containerConfig.GetPrivileged() {
		specgen.SetupPrivileged(true)
	}

	if containerConfig.GetReadonlyRootfs() {
		specgen.SetRootReadonly(true)
	}

	logPath := containerConfig.GetLogPath()

	if containerConfig.GetTty() {
		specgen.SetProcessTerminal(true)
	}

	linux := containerConfig.GetLinux()
	if linux != nil {
		resources := linux.GetResources()
		if resources != nil {
			cpuPeriod := resources.GetCpuPeriod()
			if cpuPeriod != 0 {
				specgen.SetLinuxResourcesCPUPeriod(uint64(cpuPeriod))
			}

			cpuQuota := resources.GetCpuQuota()
			if cpuQuota != 0 {
				specgen.SetLinuxResourcesCPUQuota(uint64(cpuQuota))
			}

			cpuShares := resources.GetCpuShares()
			if cpuShares != 0 {
				specgen.SetLinuxResourcesCPUShares(uint64(cpuShares))
			}

			memoryLimit := resources.GetMemoryLimitInBytes()
			if memoryLimit != 0 {
				specgen.SetLinuxResourcesMemoryLimit(uint64(memoryLimit))
			}

			oomScoreAdj := resources.GetOomScoreAdj()
			specgen.SetLinuxResourcesOOMScoreAdj(int(oomScoreAdj))
		}

		capabilities := linux.GetCapabilities()
		if capabilities != nil {
			addCaps := capabilities.GetAddCapabilities()
			if addCaps != nil {
				for _, cap := range addCaps {
					if err := specgen.AddProcessCapability(cap); err != nil {
						return nil, err
					}
				}
			}

			dropCaps := capabilities.GetDropCapabilities()
			if dropCaps != nil {
				for _, cap := range dropCaps {
					if err := specgen.DropProcessCapability(cap); err != nil {
						return nil, err
					}
				}
			}
		}

		selinuxOptions := linux.GetSelinuxOptions()
		if selinuxOptions != nil {
			user := selinuxOptions.GetUser()
			if user == "" {
				return nil, fmt.Errorf("SELinuxOption.User is empty")
			}

			role := selinuxOptions.GetRole()
			if role == "" {
				return nil, fmt.Errorf("SELinuxOption.Role is empty")
			}

			t := selinuxOptions.GetType()
			if t == "" {
				return nil, fmt.Errorf("SELinuxOption.Type is empty")
			}

			level := selinuxOptions.GetLevel()
			if level == "" {
				return nil, fmt.Errorf("SELinuxOption.Level is empty")
			}

			specgen.SetProcessSelinuxLabel(fmt.Sprintf("%s:%s:%s:%s", user, role, t, level))
		}

		user := linux.GetUser()
		if user != nil {
			uid := user.GetUid()
			specgen.SetProcessUID(uint32(uid))

			gid := user.GetGid()
			specgen.SetProcessGID(uint32(gid))

			groups := user.GetAdditionalGids()
			if groups != nil {
				for _, group := range groups {
					specgen.AddProcessAdditionalGid(uint32(group))
				}
			}
		}
	}
	// Join the namespace paths for the pod sandbox container.
	podContainerName := sb.name + "-infra"
	podInfraContainer := s.state.containers.Get(podContainerName)
	podInfraState := s.runtime.ContainerStatus(podInfraContainer)

	logrus.Infof("pod container state %v", podInfraState)

	for nsType, nsFile := range map[string]string{
		"ipc":     "ipc",
		"uts":     "uts",
		"network": "net",
	} {
		nsPath := fmt.Sprintf("/proc/%d/ns/%s", podInfraState.Pid, nsFile)
		if err := specgen.AddOrReplaceLinuxNamespace(nsType, nsPath); err != nil {
			return nil, err
		}
	}

	if err := specgen.SaveToFile(filepath.Join(containerDir, "config.json")); err != nil {
		return nil, err
	}

	imageSpec := containerConfig.GetImage()
	if imageSpec == nil {
		return nil, fmt.Errorf("CreateContainerRequest.ContainerConfig.Image is nil")
	}

	image := imageSpec.GetImage()
	if image == "" {
		return nil, fmt.Errorf("CreateContainerRequest.ContainerConfig.Image.Image is empty")
	}

	// TODO: copy the rootfs into the bundle.
	// Currently, utils.CreateFakeRootfs is used to populate the rootfs.
	if err := utils.CreateFakeRootfs(containerDir, image); err != nil {
		return nil, err
	}

	container, err := oci.NewContainer(name, containerDir, logPath, labels, sb.id, containerConfig.GetTty())
	if err != nil {
		return nil, err
	}

	return container, nil
}

// StartContainer starts the container.
func (s *Server) StartContainer(ctx context.Context, req *pb.StartContainerRequest) (*pb.StartContainerResponse, error) {
	containerName := req.ContainerId

	if *containerName == "" {
		return nil, fmt.Errorf("container ID should not be empty")
	}
	c := s.state.containers.Get(*containerName)
	if c == nil {
		return nil, fmt.Errorf("specified container not found: %s", *containerName)
	}

	if err := s.runtime.StartContainer(c); err != nil {
		return nil, fmt.Errorf("failed to start container %s in sandbox %s: %v", c.Name(), *containerName, err)
	}

	return &pb.StartContainerResponse{}, nil
}

// StopContainer stops a running container with a grace period (i.e., timeout).
func (s *Server) StopContainer(ctx context.Context, req *pb.StopContainerRequest) (*pb.StopContainerResponse, error) {
	containerName := req.ContainerId

	if *containerName == "" {
		return nil, fmt.Errorf("container ID should not be empty")
	}
	c := s.state.containers.Get(*containerName)
	if c == nil {
		return nil, fmt.Errorf("specified container not found: %s", *containerName)
	}

	if err := s.runtime.StopContainer(c); err != nil {
		return nil, fmt.Errorf("failed to stop container %s: %v", *containerName, err)
	}

	return &pb.StopContainerResponse{}, nil
}

// RemoveContainer removes the container. If the container is running, the container
// should be force removed.
func (s *Server) RemoveContainer(ctx context.Context, req *pb.RemoveContainerRequest) (*pb.RemoveContainerResponse, error) {
	containerName := req.ContainerId

	if *containerName == "" {
		return nil, fmt.Errorf("container ID should not be empty")
	}
	c := s.state.containers.Get(*containerName)
	if c == nil {
		return nil, fmt.Errorf("specified container not found: %s", *containerName)
	}

	if err := s.runtime.DeleteContainer(c); err != nil {
		return nil, fmt.Errorf("failed to delete container %s: %v", *containerName, err)
	}

	containerDir := filepath.Join(s.runtime.ContainerDir(), *containerName)
	if err := os.RemoveAll(containerDir); err != nil {
		return nil, fmt.Errorf("failed to remove container %s directory: %v", *containerName, err)
	}

	s.removeContainer(c)

	return &pb.RemoveContainerResponse{}, nil
}

// ListContainers lists all containers by filters.
func (s *Server) ListContainers(ctx context.Context, req *pb.ListContainersRequest) (*pb.ListContainersResponse, error) {
	var ctrs []*pb.Container
	for _, ctr := range s.state.containers.List() {
		if err := s.runtime.UpdateStatus(ctr); err != nil {
			return nil, err
		}

		podSandboxID := ctr.Sandbox()
		cState := s.runtime.ContainerStatus(ctr)
		created := cState.Created.Unix()
		rState := pb.ContainerState_UNKNOWN

		c := &pb.Container{
			Id:           &cState.ID,
			PodSandboxId: &podSandboxID,
			CreatedAt:    int64Ptr(created),
		}

		switch cState.Status {
		case ContainerStateCreated:
			rState = pb.ContainerState_CREATED
		case ContainerStateRunning:
			rState = pb.ContainerState_RUNNING
		case ContainerStateStopped:
			rState = pb.ContainerState_EXITED
		}
		c.State = &rState

		ctrs = append(ctrs, c)
	}

	return &pb.ListContainersResponse{
		Containers: ctrs,
	}, nil
}

// ContainerStatus returns status of the container.
func (s *Server) ContainerStatus(ctx context.Context, req *pb.ContainerStatusRequest) (*pb.ContainerStatusResponse, error) {
	containerName := req.ContainerId

	if *containerName == "" {
		return nil, fmt.Errorf("container ID should not be empty")
	}
	c := s.state.containers.Get(*containerName)

	if c == nil {
		return nil, fmt.Errorf("specified container not found: %s", *containerName)
	}

	if err := s.runtime.UpdateStatus(c); err != nil {
		return nil, err
	}

	csr := &pb.ContainerStatusResponse{
		Status: &pb.ContainerStatus{
			Id: containerName,
		},
	}

	cState := s.runtime.ContainerStatus(c)
	rStatus := pb.ContainerState_UNKNOWN

	switch cState.Status {
	case ContainerStateCreated:
		rStatus = pb.ContainerState_CREATED
		created := cState.Created.Unix()
		csr.Status.CreatedAt = int64Ptr(created)
	case ContainerStateRunning:
		rStatus = pb.ContainerState_RUNNING
		created := cState.Created.Unix()
		csr.Status.CreatedAt = int64Ptr(created)
		started := cState.Started.Unix()
		csr.Status.StartedAt = int64Ptr(started)
	case ContainerStateStopped:
		rStatus = pb.ContainerState_EXITED
		created := cState.Created.Unix()
		csr.Status.CreatedAt = int64Ptr(created)
		started := cState.Started.Unix()
		csr.Status.StartedAt = int64Ptr(started)
		finished := cState.Finished.Unix()
		csr.Status.FinishedAt = int64Ptr(finished)
		csr.Status.ExitCode = int32Ptr(cState.ExitCode)
	}

	csr.Status.State = &rStatus

	return csr, nil
}

// Exec executes the command in the container.
func (s *Server) Exec(pb.RuntimeService_ExecServer) error {
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/NYTimes/gziphandler"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/namsral/flag"
	"github.com/oxtoacart/bpool"
	"github.com/tilezen/tapalcatl"
	"github.com/whosonfirst/go-httpony/stats"
	"log"
	"net"
	"net/http"
	"os"
	"strconv"
	"time"
)

// the handler config is the container for the json configuration
// storageDefinition contains the base options for a particular storage
// storageConfig contains the specific options for a particular pattern
// pattern ties together request patterns with storageConfig
// the storageConfig "Type_" needs to match the key mapping names in Storage
// awsConfig contains session-wide options for aws backed storage

// "s3" and "file" are possible storage definitions

type storageDefinition struct {
	// common fields across all storage types
	// these can be overridden in specific storage configuration
	MetatileSize int

	// s3 specific fields
	Layer      string
	Bucket     string
	KeyPattern string

	// file specific fields
	BaseDir string
}

// generic aws configuration applied to whole session
type awsConfig struct {
	Region *string
}

// storage configuration, specific to a pattern
type storageConfig struct {
	// should match storage definition name, "s3" or "file"
	Type_ string `json:"type"`

	MetatileSize *int

	// Prefix is required to be set for s3 storage
	Prefix     *string
	KeyPattern *string
	Layer      *string

	BaseDir *string
}

type handlerConfig struct {
	Aws     *awsConfig
	Storage map[string]storageDefinition
	Pattern map[string]storageConfig
	Mime    map[string]string
}

func (h *handlerConfig) String() string {
	return fmt.Sprintf("%#v", *h)
}

func (h *handlerConfig) Set(line string) error {
	err := json.Unmarshal([]byte(line), h)
	if err != nil {
		return fmt.Errorf("Unable to parse value as a JSON object: %s", err.Error())
	}
	return nil
}

// try and parse a range of different date formats which are allowed by HTTP.
func parseHTTPDates(date string) (*time.Time, error) {
	time_layouts := []string{
		http.TimeFormat,
		time.RFC1123, time.RFC1123Z,
		time.RFC822, time.RFC822Z,
		time.RFC850, time.ANSIC,
	}

	var err error
	var ts time.Time

	for _, layout := range time_layouts {
		ts, err = time.Parse(layout, date)
		if err == nil {
			return &ts, nil
		}
	}

	// give the error for our preferred format
	_, err = time.Parse(http.TimeFormat, date)
	return nil, err
}

// MuxParser parses the tile coordinate from the captured arguments from
// the gorilla mux router.
type MuxParser struct {
	mimeMap map[string]string
}

type MimeParseError struct {
	BadFormat string
}

func (mpe *MimeParseError) Error() string {
	return fmt.Sprintf("Invalid format: %s", mpe.BadFormat)
}

type CoordParseError struct {
	// relevant values are set when parse fails
	BadZ string
	BadX string
	BadY string
}

func (cpe *CoordParseError) IsError() bool {
	return cpe.BadZ != "" || cpe.BadX != "" || cpe.BadY != ""
}

func (cpe *CoordParseError) Error() string {
	// TODO on multiple parse failures, can return back a concatenated string
	if cpe.BadZ != "" {
		return fmt.Sprintf("Invalid z: %s", cpe.BadZ)
	}
	if cpe.BadX != "" {
		return fmt.Sprintf("Invalid x: %s", cpe.BadX)
	}
	if cpe.BadY != "" {
		return fmt.Sprintf("Invalid y: %s", cpe.BadY)
	}
	panic("No coord parse error")
}

type CondParseError struct {
	IfModifiedSinceError error
}

func (cpe *CondParseError) Error() string {
	return cpe.IfModifiedSinceError.Error()
}

type ParseError struct {
	MimeError  *MimeParseError
	CoordError *CoordParseError
	CondError  *CondParseError
}

func (pe *ParseError) Error() string {
	if pe.MimeError != nil {
		return pe.MimeError.Error()
	} else if pe.CoordError != nil {
		return pe.CoordError.Error()
	} else if pe.CondError != nil {
		return pe.CondError.Error()
	} else {
		panic("ParseError: No error")
	}
}

// Parse ignores its argument and uses values from the capture.
func (mp *MuxParser) Parse(req *http.Request) (*ParseResult, error) {
	m := mux.Vars(req)

	var t tapalcatl.TileCoord
	var contentType string
	var err error
	var ok bool

	var apiKey string
	q := req.URL.Query()
	if apiKeys, ok := q["api_key"]; ok && len(apiKeys) > 0 {
		apiKey = apiKeys[0]
	}

	parseResult := &ParseResult{
		HttpData: HttpRequestData{
			Path:      req.URL.Path,
			ApiKey:    apiKey,
			UserAgent: req.UserAgent(),
			Referrer:  req.Referer(),
		},
	}

	t.Format = m["fmt"]
	if contentType, ok = mp.mimeMap[t.Format]; !ok {
		return parseResult, &ParseError{
			MimeError: &MimeParseError{
				BadFormat: t.Format,
			},
		}
	}
	parseResult.ContentType = contentType

	var coordError CoordParseError
	z := m["z"]
	t.Z, err = strconv.Atoi(z)
	if err != nil {
		coordError.BadZ = z
	}

	x := m["x"]
	t.X, err = strconv.Atoi(x)
	if err != nil {
		coordError.BadX = x
	}

	y := m["y"]
	t.Y, err = strconv.Atoi(y)
	if err != nil {
		coordError.BadY = y
	}

	if coordError.IsError() {
		return parseResult, &ParseError{
			CoordError: &coordError,
		}
	}

	parseResult.Coord = t

	ifNoneMatch := req.Header.Get("If-None-Match")
	if ifNoneMatch != "" {
		parseResult.Cond.IfNoneMatch = &ifNoneMatch
	}

	ifModifiedSince := req.Header.Get("If-Modified-Since")
	if ifModifiedSince != "" {
		parseResult.Cond.IfModifiedSince, err = parseHTTPDates(ifModifiedSince)
		if err != nil {
			return parseResult, &ParseError{
				CondError: &CondParseError{
					IfModifiedSinceError: err,
				},
			}
		}
	}

	return parseResult, nil
}

type OnDemandBufferManager struct{}

func (bm *OnDemandBufferManager) Get() *bytes.Buffer {
	return &bytes.Buffer{}
}

func (bm *OnDemandBufferManager) Put(buf *bytes.Buffer) {
}

func logFatalCfgErr(logger JsonLogger, msg string, xs ...interface{}) {
	logger.Error(LogCategory_ConfigError, msg, xs...)
	os.Exit(1)
}

func main() {
	var listen, healthcheck, debugHost string
	var poolNumEntries, poolEntrySize int
	var metricsStatsdAddr, metricsStatsdPrefix string

	hc := handlerConfig{}

	systemLogger := log.New(os.Stdout, "", log.LstdFlags|log.LUTC|log.Lmicroseconds)
	hostname, err := os.Hostname()
	if err != nil {
		// NOTE: if there are legitimate cases when this can fail, we
		// can leave off the hostname in the logger.
		// But for now we prefer to get notified of it.
		systemLogger.Fatalf("ERROR: Cannot find hostname to use for logger")
	}
	// use this logger everywhere.
	logger := NewJsonLogger(systemLogger, hostname)

	f := flag.NewFlagSetWithEnvPrefix(os.Args[0], "TAPALCATL", 0)
	f.Var(&hc, "handler",
		`JSON object defining how request patterns will be handled.
	 Aws { Object present when Aws-wide configuration is needed, eg session config.
     Region string Name of aws region
   }
   Storage { key -> storage definition mapping
     storage name (type) string -> {
     	 MetatileSize int      Number of tiles in each dimension of the metatile.

       (s3 storage)
    	 Layer      string   Name of layer to use in this bucket. Only relevant for s3.
    	 Bucket     string   Name of S3 bucket to fetch from.
       KeyPattern string   Pattern to fill with variables from the main pattern to make the S3 key.

       (file storage)
       BaseDir    string   Base directory to look for files under.
     }
   }
   Pattern { request pattern -> storage configuration mapping
     request pattern string -> {
       type string Name of storage defintion to use
       list of optional storage configuration to use:
       prefix is required for s3, others are optional overrides of relevant definition
     	 Prefix string  Prefix to use in this bucket.
     }
   }
   Mime { extension -> content-type used in http response
   }
`)
	f.StringVar(&listen, "listen", ":8080", "interface and port to listen on")
	f.String("config", "", "Config file to read values from.")
	f.StringVar(&healthcheck, "healthcheck", "", "A path to respond to with a blank 200 OK. Intended for use by load balancer health checks.")
	f.StringVar(&debugHost, "debugHost", "", "IP address of remote debug host allowed to read expvars at /debug/vars.")

	f.IntVar(&poolNumEntries, "poolnumentries", 0, "Number of buffers to pool.")
	f.IntVar(&poolEntrySize, "poolentrysize", 0, "Size of each buffer in pool.")

	f.StringVar(&metricsStatsdAddr, "metrics-statsd-addr", "", "host:port to use to send data to statsd")
	f.StringVar(&metricsStatsdPrefix, "metrics-statsd-prefix", "", "prefix to prepend to metrics")

	err = f.Parse(os.Args[1:])
	if err == flag.ErrHelp {
		return
	} else if err != nil {
		logFatalCfgErr(logger, "Unable to parse input command line, environment or config: %s", err.Error())
	}

	if len(hc.Pattern) == 0 {
		logFatalCfgErr(logger, "You must provide at least one pattern.")
	}
	if len(hc.Storage) == 0 {
		logFatalCfgErr(logger, "You must provide at least one storage.")
	}

	r := mux.NewRouter()

	// buffer manager shared by all handlers
	var bufferManager BufferManager

	if poolNumEntries > 0 && poolEntrySize > 0 {
		bufferManager = bpool.NewSizedBufferPool(poolNumEntries, poolEntrySize)
	} else {
		bufferManager = &OnDemandBufferManager{}
	}

	// metrics writer configuration
	var mw metricsWriter
	if metricsStatsdAddr != "" {
		udpAddr, err := net.ResolveUDPAddr("udp4", metricsStatsdAddr)
		if err != nil {
			logFatalCfgErr(logger, "Invalid metricsstatsdaddr %s: %s", metricsStatsdAddr, err)
		}
		mw = NewStatsdMetricsWriter(udpAddr, metricsStatsdPrefix, logger)
	} else {
		mw = &nilMetricsWriter{}
	}

	// set if we have s3 storage configured, and shared across all s3 sessions
	var awsSession *session.Session

	// create the storage implementations and handler routes for patterns
	var storage Storage
	for reqPattern, sc := range hc.Pattern {

		t := sc.Type_
		sd, ok := hc.Storage[t]
		if !ok {
			logFatalCfgErr(logger, "Missing storage definition: %s", t)
		}
		metatileSize := sd.MetatileSize
		if sc.MetatileSize != nil {
			metatileSize = *sc.MetatileSize
		}
		layer := sd.Layer
		if sc.Layer != nil {
			layer = *sc.Layer
		}
		if layer == "" {
			logFatalCfgErr(logger, "Missing layer for storage: %s", t)
		}

		switch t {
		case "s3":
			if sc.Prefix == nil {
				logFatalCfgErr(logger, "S3 configuration requires prefix")
			}
			prefix := *sc.Prefix

			if awsSession == nil {
				if hc.Aws != nil && hc.Aws.Region != nil {
					awsSession, err = session.NewSessionWithOptions(session.Options{
						Config: aws.Config{Region: hc.Aws.Region},
					})
				} else {
					awsSession, err = session.NewSession()
				}
			}
			if err != nil {
				logFatalCfgErr(logger, "Unable to set up AWS session: %s", err.Error())
			}
			keyPattern := sd.KeyPattern
			if sc.KeyPattern != nil {
				keyPattern = *sc.KeyPattern
			}

			if sd.Bucket == "" {
				logFatalCfgErr(logger, "S3 storage missing bucket configuration")
			}
			if keyPattern == "" {
				logFatalCfgErr(logger, "S3 storage missing key pattern")
			}

			s3Client := s3.New(awsSession)
			storage = NewS3Storage(s3Client, sd.Bucket, keyPattern, prefix, layer)

		case "file":
			sd, ok := hc.Storage[t]
			if !ok {
				logFatalCfgErr(logger, "Missing file storage definition")
			}

			if sd.BaseDir == "" {
				logFatalCfgErr(logger, "File storage missing base dir")
			}

			storage = NewFileStorage(sd.BaseDir, layer)

		default:
			logFatalCfgErr(logger, "Unknown storage %s", t)
		}

		parser := &MuxParser{
			mimeMap: hc.Mime,
		}

		h := MetatileHandler(parser, metatileSize, hc.Mime, storage, bufferManager, mw, logger)
		gzipped := gziphandler.GzipHandler(h)

		r.Handle(reqPattern, gzipped).Methods("GET")
	}

	if len(healthcheck) > 0 {
		r.HandleFunc(healthcheck, getHealth).Methods("GET")
	}

	// serve expvar stats to localhost and debugHost
	expvar_func, err := stats.HandlerFunc(debugHost)
	if err != nil {
		logFatalCfgErr(logger, "Failed to initialize stats.HandlerFunc: %s", err.Error())
	}
	r.HandleFunc("/debug/vars", expvar_func).Methods("GET")

	corsHandler := handlers.CORS()(r)
	logHandler := handlers.CombinedLoggingHandler(os.Stdout, corsHandler)

	logger.Info("Server started and listening on %s\n", listen)

	systemLogger.Fatal(http.ListenAndServe(listen, logHandler))
}

func getHealth(rw http.ResponseWriter, _ *http.Request) {
	rw.WriteHeader(200)
}
<commit_msg>Remove gorilla log handler<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/NYTimes/gziphandler"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/namsral/flag"
	"github.com/oxtoacart/bpool"
	"github.com/tilezen/tapalcatl"
	"github.com/whosonfirst/go-httpony/stats"
	"log"
	"net"
	"net/http"
	"os"
	"strconv"
	"time"
)

// the handler config is the container for the json configuration
// storageDefinition contains the base options for a particular storage
// storageConfig contains the specific options for a particular pattern
// pattern ties together request patterns with storageConfig
// the storageConfig "Type_" needs to match the key mapping names in Storage
// awsConfig contains session-wide options for aws backed storage

// "s3" and "file" are possible storage definitions

type storageDefinition struct {
	// common fields across all storage types
	// these can be overridden in specific storage configuration
	MetatileSize int

	// s3 specific fields
	Layer      string
	Bucket     string
	KeyPattern string

	// file specific fields
	BaseDir string
}

// generic aws configuration applied to whole session
type awsConfig struct {
	Region *string
}

// storage configuration, specific to a pattern
type storageConfig struct {
	// should match storage definition name, "s3" or "file"
	Type_ string `json:"type"`

	MetatileSize *int

	// Prefix is required to be set for s3 storage
	Prefix     *string
	KeyPattern *string
	Layer      *string

	BaseDir *string
}

type handlerConfig struct {
	Aws     *awsConfig
	Storage map[string]storageDefinition
	Pattern map[string]storageConfig
	Mime    map[string]string
}

func (h *handlerConfig) String() string {
	return fmt.Sprintf("%#v", *h)
}

func (h *handlerConfig) Set(line string) error {
	err := json.Unmarshal([]byte(line), h)
	if err != nil {
		return fmt.Errorf("Unable to parse value as a JSON object: %s", err.Error())
	}
	return nil
}

// try and parse a range of different date formats which are allowed by HTTP.
func parseHTTPDates(date string) (*time.Time, error) {
	time_layouts := []string{
		http.TimeFormat,
		time.RFC1123, time.RFC1123Z,
		time.RFC822, time.RFC822Z,
		time.RFC850, time.ANSIC,
	}

	var err error
	var ts time.Time

	for _, layout := range time_layouts {
		ts, err = time.Parse(layout, date)
		if err == nil {
			return &ts, nil
		}
	}

	// give the error for our preferred format
	_, err = time.Parse(http.TimeFormat, date)
	return nil, err
}

// MuxParser parses the tile coordinate from the captured arguments from
// the gorilla mux router.
type MuxParser struct {
	mimeMap map[string]string
}

type MimeParseError struct {
	BadFormat string
}

func (mpe *MimeParseError) Error() string {
	return fmt.Sprintf("Invalid format: %s", mpe.BadFormat)
}

type CoordParseError struct {
	// relevant values are set when parse fails
	BadZ string
	BadX string
	BadY string
}

func (cpe *CoordParseError) IsError() bool {
	return cpe.BadZ != "" || cpe.BadX != "" || cpe.BadY != ""
}

func (cpe *CoordParseError) Error() string {
	// TODO on multiple parse failures, can return back a concatenated string
	if cpe.BadZ != "" {
		return fmt.Sprintf("Invalid z: %s", cpe.BadZ)
	}
	if cpe.BadX != "" {
		return fmt.Sprintf("Invalid x: %s", cpe.BadX)
	}
	if cpe.BadY != "" {
		return fmt.Sprintf("Invalid y: %s", cpe.BadY)
	}
	panic("No coord parse error")
}

type CondParseError struct {
	IfModifiedSinceError error
}

func (cpe *CondParseError) Error() string {
	return cpe.IfModifiedSinceError.Error()
}

type ParseError struct {
	MimeError  *MimeParseError
	CoordError *CoordParseError
	CondError  *CondParseError
}

func (pe *ParseError) Error() string {
	if pe.MimeError != nil {
		return pe.MimeError.Error()
	} else if pe.CoordError != nil {
		return pe.CoordError.Error()
	} else if pe.CondError != nil {
		return pe.CondError.Error()
	} else {
		panic("ParseError: No error")
	}
}

// Parse ignores its argument and uses values from the capture.
func (mp *MuxParser) Parse(req *http.Request) (*ParseResult, error) {
	m := mux.Vars(req)

	var t tapalcatl.TileCoord
	var contentType string
	var err error
	var ok bool

	var apiKey string
	q := req.URL.Query()
	if apiKeys, ok := q["api_key"]; ok && len(apiKeys) > 0 {
		apiKey = apiKeys[0]
	}

	parseResult := &ParseResult{
		HttpData: HttpRequestData{
			Path:      req.URL.Path,
			ApiKey:    apiKey,
			UserAgent: req.UserAgent(),
			Referrer:  req.Referer(),
		},
	}

	t.Format = m["fmt"]
	if contentType, ok = mp.mimeMap[t.Format]; !ok {
		return parseResult, &ParseError{
			MimeError: &MimeParseError{
				BadFormat: t.Format,
			},
		}
	}
	parseResult.ContentType = contentType

	var coordError CoordParseError
	z := m["z"]
	t.Z, err = strconv.Atoi(z)
	if err != nil {
		coordError.BadZ = z
	}

	x := m["x"]
	t.X, err = strconv.Atoi(x)
	if err != nil {
		coordError.BadX = x
	}

	y := m["y"]
	t.Y, err = strconv.Atoi(y)
	if err != nil {
		coordError.BadY = y
	}

	if coordError.IsError() {
		return parseResult, &ParseError{
			CoordError: &coordError,
		}
	}

	parseResult.Coord = t

	ifNoneMatch := req.Header.Get("If-None-Match")
	if ifNoneMatch != "" {
		parseResult.Cond.IfNoneMatch = &ifNoneMatch
	}

	ifModifiedSince := req.Header.Get("If-Modified-Since")
	if ifModifiedSince != "" {
		parseResult.Cond.IfModifiedSince, err = parseHTTPDates(ifModifiedSince)
		if err != nil {
			return parseResult, &ParseError{
				CondError: &CondParseError{
					IfModifiedSinceError: err,
				},
			}
		}
	}

	return parseResult, nil
}

type OnDemandBufferManager struct{}

func (bm *OnDemandBufferManager) Get() *bytes.Buffer {
	return &bytes.Buffer{}
}

func (bm *OnDemandBufferManager) Put(buf *bytes.Buffer) {
}

func logFatalCfgErr(logger JsonLogger, msg string, xs ...interface{}) {
	logger.Error(LogCategory_ConfigError, msg, xs...)
	os.Exit(1)
}

func main() {
	var listen, healthcheck, debugHost string
	var poolNumEntries, poolEntrySize int
	var metricsStatsdAddr, metricsStatsdPrefix string

	hc := handlerConfig{}

	systemLogger := log.New(os.Stdout, "", log.LstdFlags|log.LUTC|log.Lmicroseconds)
	hostname, err := os.Hostname()
	if err != nil {
		// NOTE: if there are legitimate cases when this can fail, we
		// can leave off the hostname in the logger.
		// But for now we prefer to get notified of it.
		systemLogger.Fatalf("ERROR: Cannot find hostname to use for logger")
	}
	// use this logger everywhere.
	logger := NewJsonLogger(systemLogger, hostname)

	f := flag.NewFlagSetWithEnvPrefix(os.Args[0], "TAPALCATL", 0)
	f.Var(&hc, "handler",
		`JSON object defining how request patterns will be handled.
	 Aws { Object present when Aws-wide configuration is needed, eg session config.
     Region string Name of aws region
   }
   Storage { key -> storage definition mapping
     storage name (type) string -> {
     	 MetatileSize int      Number of tiles in each dimension of the metatile.

       (s3 storage)
    	 Layer      string   Name of layer to use in this bucket. Only relevant for s3.
    	 Bucket     string   Name of S3 bucket to fetch from.
       KeyPattern string   Pattern to fill with variables from the main pattern to make the S3 key.

       (file storage)
       BaseDir    string   Base directory to look for files under.
     }
   }
   Pattern { request pattern -> storage configuration mapping
     request pattern string -> {
       type string Name of storage defintion to use
       list of optional storage configuration to use:
       prefix is required for s3, others are optional overrides of relevant definition
     	 Prefix string  Prefix to use in this bucket.
     }
   }
   Mime { extension -> content-type used in http response
   }
`)
	f.StringVar(&listen, "listen", ":8080", "interface and port to listen on")
	f.String("config", "", "Config file to read values from.")
	f.StringVar(&healthcheck, "healthcheck", "", "A path to respond to with a blank 200 OK. Intended for use by load balancer health checks.")
	f.StringVar(&debugHost, "debugHost", "", "IP address of remote debug host allowed to read expvars at /debug/vars.")

	f.IntVar(&poolNumEntries, "poolnumentries", 0, "Number of buffers to pool.")
	f.IntVar(&poolEntrySize, "poolentrysize", 0, "Size of each buffer in pool.")

	f.StringVar(&metricsStatsdAddr, "metrics-statsd-addr", "", "host:port to use to send data to statsd")
	f.StringVar(&metricsStatsdPrefix, "metrics-statsd-prefix", "", "prefix to prepend to metrics")

	err = f.Parse(os.Args[1:])
	if err == flag.ErrHelp {
		return
	} else if err != nil {
		logFatalCfgErr(logger, "Unable to parse input command line, environment or config: %s", err.Error())
	}

	if len(hc.Pattern) == 0 {
		logFatalCfgErr(logger, "You must provide at least one pattern.")
	}
	if len(hc.Storage) == 0 {
		logFatalCfgErr(logger, "You must provide at least one storage.")
	}

	r := mux.NewRouter()

	// buffer manager shared by all handlers
	var bufferManager BufferManager

	if poolNumEntries > 0 && poolEntrySize > 0 {
		bufferManager = bpool.NewSizedBufferPool(poolNumEntries, poolEntrySize)
	} else {
		bufferManager = &OnDemandBufferManager{}
	}

	// metrics writer configuration
	var mw metricsWriter
	if metricsStatsdAddr != "" {
		udpAddr, err := net.ResolveUDPAddr("udp4", metricsStatsdAddr)
		if err != nil {
			logFatalCfgErr(logger, "Invalid metricsstatsdaddr %s: %s", metricsStatsdAddr, err)
		}
		mw = NewStatsdMetricsWriter(udpAddr, metricsStatsdPrefix, logger)
	} else {
		mw = &nilMetricsWriter{}
	}

	// set if we have s3 storage configured, and shared across all s3 sessions
	var awsSession *session.Session

	// create the storage implementations and handler routes for patterns
	var storage Storage
	for reqPattern, sc := range hc.Pattern {

		t := sc.Type_
		sd, ok := hc.Storage[t]
		if !ok {
			logFatalCfgErr(logger, "Missing storage definition: %s", t)
		}
		metatileSize := sd.MetatileSize
		if sc.MetatileSize != nil {
			metatileSize = *sc.MetatileSize
		}
		layer := sd.Layer
		if sc.Layer != nil {
			layer = *sc.Layer
		}
		if layer == "" {
			logFatalCfgErr(logger, "Missing layer for storage: %s", t)
		}

		switch t {
		case "s3":
			if sc.Prefix == nil {
				logFatalCfgErr(logger, "S3 configuration requires prefix")
			}
			prefix := *sc.Prefix

			if awsSession == nil {
				if hc.Aws != nil && hc.Aws.Region != nil {
					awsSession, err = session.NewSessionWithOptions(session.Options{
						Config: aws.Config{Region: hc.Aws.Region},
					})
				} else {
					awsSession, err = session.NewSession()
				}
			}
			if err != nil {
				logFatalCfgErr(logger, "Unable to set up AWS session: %s", err.Error())
			}
			keyPattern := sd.KeyPattern
			if sc.KeyPattern != nil {
				keyPattern = *sc.KeyPattern
			}

			if sd.Bucket == "" {
				logFatalCfgErr(logger, "S3 storage missing bucket configuration")
			}
			if keyPattern == "" {
				logFatalCfgErr(logger, "S3 storage missing key pattern")
			}

			s3Client := s3.New(awsSession)
			storage = NewS3Storage(s3Client, sd.Bucket, keyPattern, prefix, layer)

		case "file":
			sd, ok := hc.Storage[t]
			if !ok {
				logFatalCfgErr(logger, "Missing file storage definition")
			}

			if sd.BaseDir == "" {
				logFatalCfgErr(logger, "File storage missing base dir")
			}

			storage = NewFileStorage(sd.BaseDir, layer)

		default:
			logFatalCfgErr(logger, "Unknown storage %s", t)
		}

		parser := &MuxParser{
			mimeMap: hc.Mime,
		}

		h := MetatileHandler(parser, metatileSize, hc.Mime, storage, bufferManager, mw, logger)
		gzipped := gziphandler.GzipHandler(h)

		r.Handle(reqPattern, gzipped).Methods("GET")
	}

	if len(healthcheck) > 0 {
		r.HandleFunc(healthcheck, getHealth).Methods("GET")
	}

	// serve expvar stats to localhost and debugHost
	expvar_func, err := stats.HandlerFunc(debugHost)
	if err != nil {
		logFatalCfgErr(logger, "Failed to initialize stats.HandlerFunc: %s", err.Error())
	}
	r.HandleFunc("/debug/vars", expvar_func).Methods("GET")

	corsHandler := handlers.CORS()(r)

	logger.Info("Server started and listening on %s\n", listen)

	systemLogger.Fatal(http.ListenAndServe(listen, corsHandler))
}

func getHealth(rw http.ResponseWriter, _ *http.Request) {
	rw.WriteHeader(200)
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"github.com/apex/log"
	"github.com/pkg/errors"
	"github.com/pterodactyl/wings/api"
	"github.com/pterodactyl/wings/config"
	"github.com/pterodactyl/wings/environment"
	"github.com/pterodactyl/wings/events"
	"regexp"
	"strconv"
)

var dockerEvents = []string{
	environment.DockerImagePullStatus,
	environment.DockerImagePullStarted,
	environment.DockerImagePullCompleted,
}

// Adds all of the internal event listeners we want to use for a server. These listeners can only be
// removed by deleting the server as they should last for the duration of the process' lifetime.
func (s *Server) StartEventListeners() {
	console := func(e events.Event) {
		t := s.Throttler()
		err := t.Increment(func() {
			s.PublishConsoleOutputFromDaemon("Your server is outputting too much data and is being throttled.")
		})

		// An error is only returned if the server has breached the thresholds set.
		if err != nil {
			// If the process is already stopping, just let it continue with that action rather than attempting
			// to terminate again.
			if s.GetState() != environment.ProcessStoppingState {
				s.SetState(environment.ProcessStoppingState)
				go func() {
					s.Log().Warn("stopping server instance, violating throttle limits")
					s.PublishConsoleOutputFromDaemon("Your server is being stopped for outputting too much data in a short period of time.")
					// Completely skip over server power actions and terminate the running instance. This gives the
					// server 15 seconds to finish stopping gracefully before it is forcefully terminated.
					if err := s.Environment.WaitForStop(config.Get().Throttles.StopGracePeriod, true); err != nil {
						// If there is an error set the process back to running so that this throttler is called
						// again and hopefully kills the server.
						if s.GetState() != environment.ProcessOfflineState {
							s.SetState(environment.ProcessRunningState)
						}

						s.Log().WithField("error", errors.WithStack(err)).Error("failed to terminate environment after triggering throttle")
					}
				}()
			}
		}

		// If we are not throttled, go ahead and output the data.
		if !t.Throttled() {
			s.Events().Publish(ConsoleOutputEvent, e.Data)
		}

		// Also pass the data along to the console output channel.
		s.onConsoleOutput(e.Data)
	}

	state := func(e events.Event) {
		// Reset the throttler when the process is started.
		if e.Data == environment.ProcessStartingState {
			s.Throttler().Reset()
		}

		s.SetState(e.Data)
	}

	stats := func(e events.Event) {
		st := new(environment.Stats)
		if err := json.Unmarshal([]byte(e.Data), st); err != nil {
			s.Log().WithField("error", errors.WithStack(err)).Warn("failed to unmarshal server environment stats")
			return
		}

		// Update the server resource tracking object with the resources we got here.
		s.resources.mu.Lock()
		s.resources.Stats = *st
		s.resources.mu.Unlock()

		s.Filesystem().HasSpaceAvailable(true)

		s.emitProcUsage()
	}

	docker := func(e events.Event) {
		if e.Topic == environment.DockerImagePullStatus {
			s.Events().Publish(InstallOutputEvent, e.Data)
		} else if e.Topic == environment.DockerImagePullStarted {
			s.PublishConsoleOutputFromDaemon("Pulling Docker container image, this could take a few minutes to complete...")
		} else {
			s.PublishConsoleOutputFromDaemon("Finished pulling Docker container image")
		}
	}

	s.Log().Info("registering event listeners: console, state, resources...")
	s.Environment.Events().On(environment.ConsoleOutputEvent, &console)
	s.Environment.Events().On(environment.StateChangeEvent, &state)
	s.Environment.Events().On(environment.ResourceEvent, &stats)
	for _, evt := range dockerEvents {
		s.Environment.Events().On(evt, &docker)
	}
}

var stripAnsiRegex = regexp.MustCompile("[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))")

// Custom listener for console output events that will check if the given line
// of output matches one that should mark the server as started or not.
func (s *Server) onConsoleOutput(data string) {
	// Get the server's process configuration.
	processConfiguration := s.ProcessConfiguration()

	// Check if the server is currently starting.
	if s.GetState() == environment.ProcessStartingState {
		// Check if we should strip ansi color codes.
		if processConfiguration.Startup.StripAnsi {
			// Strip ansi color codes from the data string.
			data = stripAnsiRegex.ReplaceAllString(data, "")
		}

		// Iterate over all the done lines.
		for _, l := range processConfiguration.Startup.Done {
			if !l.Matches(data) {
				continue
			}

			s.Log().WithFields(log.Fields{
				"match":   l.String(),
				"against": strconv.QuoteToASCII(data),
			}).Debug("detected server in running state based on console line output")

			// If the specific line of output is one that would mark the server as started,
			// set the server to that state. Only do this if the server is not currently stopped
			// or stopping.
			_ = s.SetState(environment.ProcessRunningState)
			break
		}
	}

	// If the command sent to the server is one that should stop the server we will need to
	// set the server to be in a stopping state, otherwise crash detection will kick in and
	// cause the server to unexpectedly restart on the user.
	if s.IsRunning() {
		stop := processConfiguration.Stop

		if stop.Type == api.ProcessStopCommand && data == stop.Value {
			_ = s.SetState(environment.ProcessOfflineState)
		}
	}
}
<commit_msg>Stop servers when exceeding their disk limits; closes pterodactyl/panel#2638<commit_after>package server

import (
	"encoding/json"
	"github.com/apex/log"
	"github.com/pkg/errors"
	"github.com/pterodactyl/wings/api"
	"github.com/pterodactyl/wings/config"
	"github.com/pterodactyl/wings/environment"
	"github.com/pterodactyl/wings/events"
	"regexp"
	"strconv"
	"sync"
)

var dockerEvents = []string{
	environment.DockerImagePullStatus,
	environment.DockerImagePullStarted,
	environment.DockerImagePullCompleted,
}

type diskSpaceLimiter struct {
	o      sync.Once
	mu     sync.Mutex
	server *Server
}

func newDiskLimiter(s *Server) *diskSpaceLimiter {
	return &diskSpaceLimiter{server: s}
}

// Reset the disk space limiter status.
func (dsl *diskSpaceLimiter) Reset() {
	dsl.mu.Lock()
	dsl.o = sync.Once{}
	dsl.mu.Unlock()
}

// Trigger the disk space limiter which will attempt to stop a running server instance within
// 15 seconds, and terminate it forcefully if it does not stop.
//
// This function is only executed one time, so whenever a server is marked as booting the limiter
// should be reset so it can properly be triggered as needed.
func (dsl *diskSpaceLimiter) Trigger() {
	dsl.o.Do(func() {
		dsl.server.PublishConsoleOutputFromDaemon("Server is exceeding the assigned disk space limit, stopping process now.")
		if err := dsl.server.Environment.WaitForStop(15, true); err != nil {
			dsl.server.Log().WithField("error", err).Error("failed to stop server after exceeding space limit!")
		}
	})
}

// Adds all of the internal event listeners we want to use for a server. These listeners can only be
// removed by deleting the server as they should last for the duration of the process' lifetime.
func (s *Server) StartEventListeners() {
	console := func(e events.Event) {
		t := s.Throttler()
		err := t.Increment(func() {
			s.PublishConsoleOutputFromDaemon("Your server is outputting too much data and is being throttled.")
		})

		// An error is only returned if the server has breached the thresholds set.
		if err != nil {
			// If the process is already stopping, just let it continue with that action rather than attempting
			// to terminate again.
			if s.GetState() != environment.ProcessStoppingState {
				s.SetState(environment.ProcessStoppingState)
				go func() {
					s.Log().Warn("stopping server instance, violating throttle limits")
					s.PublishConsoleOutputFromDaemon("Your server is being stopped for outputting too much data in a short period of time.")
					// Completely skip over server power actions and terminate the running instance. This gives the
					// server 15 seconds to finish stopping gracefully before it is forcefully terminated.
					if err := s.Environment.WaitForStop(config.Get().Throttles.StopGracePeriod, true); err != nil {
						// If there is an error set the process back to running so that this throttler is called
						// again and hopefully kills the server.
						if s.GetState() != environment.ProcessOfflineState {
							s.SetState(environment.ProcessRunningState)
						}

						s.Log().WithField("error", errors.WithStack(err)).Error("failed to terminate environment after triggering throttle")
					}
				}()
			}
		}

		// If we are not throttled, go ahead and output the data.
		if !t.Throttled() {
			s.Events().Publish(ConsoleOutputEvent, e.Data)
		}

		// Also pass the data along to the console output channel.
		s.onConsoleOutput(e.Data)
	}

	l := newDiskLimiter(s)
	state := func(e events.Event) {
		// Reset the throttler when the process is started.
		if e.Data == environment.ProcessStartingState {
			l.Reset()
			s.Throttler().Reset()
		}

		s.SetState(e.Data)
	}

	stats := func(e events.Event) {
		st := new(environment.Stats)
		if err := json.Unmarshal([]byte(e.Data), st); err != nil {
			s.Log().WithField("error", errors.WithStack(err)).Warn("failed to unmarshal server environment stats")
			return
		}

		// Update the server resource tracking object with the resources we got here.
		s.resources.mu.Lock()
		s.resources.Stats = *st
		s.resources.mu.Unlock()

		// If there is no disk space available at this point, trigger the server disk limiter logic
		// which will start to stop the running instance.
		if !s.Filesystem().HasSpaceAvailable(true) {
			l.Trigger()
		}

		s.emitProcUsage()
	}

	docker := func(e events.Event) {
		if e.Topic == environment.DockerImagePullStatus {
			s.Events().Publish(InstallOutputEvent, e.Data)
		} else if e.Topic == environment.DockerImagePullStarted {
			s.PublishConsoleOutputFromDaemon("Pulling Docker container image, this could take a few minutes to complete...")
		} else {
			s.PublishConsoleOutputFromDaemon("Finished pulling Docker container image")
		}
	}

	s.Log().Info("registering event listeners: console, state, resources...")
	s.Environment.Events().On(environment.ConsoleOutputEvent, &console)
	s.Environment.Events().On(environment.StateChangeEvent, &state)
	s.Environment.Events().On(environment.ResourceEvent, &stats)
	for _, evt := range dockerEvents {
		s.Environment.Events().On(evt, &docker)
	}
}

var stripAnsiRegex = regexp.MustCompile("[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))")

// Custom listener for console output events that will check if the given line
// of output matches one that should mark the server as started or not.
func (s *Server) onConsoleOutput(data string) {
	// Get the server's process configuration.
	processConfiguration := s.ProcessConfiguration()

	// Check if the server is currently starting.
	if s.GetState() == environment.ProcessStartingState {
		// Check if we should strip ansi color codes.
		if processConfiguration.Startup.StripAnsi {
			// Strip ansi color codes from the data string.
			data = stripAnsiRegex.ReplaceAllString(data, "")
		}

		// Iterate over all the done lines.
		for _, l := range processConfiguration.Startup.Done {
			if !l.Matches(data) {
				continue
			}

			s.Log().WithFields(log.Fields{
				"match":   l.String(),
				"against": strconv.QuoteToASCII(data),
			}).Debug("detected server in running state based on console line output")

			// If the specific line of output is one that would mark the server as started,
			// set the server to that state. Only do this if the server is not currently stopped
			// or stopping.
			_ = s.SetState(environment.ProcessRunningState)
			break
		}
	}

	// If the command sent to the server is one that should stop the server we will need to
	// set the server to be in a stopping state, otherwise crash detection will kick in and
	// cause the server to unexpectedly restart on the user.
	if s.IsRunning() {
		stop := processConfiguration.Stop

		if stop.Type == api.ProcessStopCommand && data == stop.Value {
			_ = s.SetState(environment.ProcessOfflineState)
		}
	}
}
<|endoftext|>


<commit_before>/**
 * MediaType
 *
 * Copyright © 2014 Trevor N. Suarez (Rican7)
 */

// Tests for the mediatype mutable structure

package mediatype

import (
	"testing"
)

/**
 * Tests functions
 */

func TestFullType(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.FullType() != "application/vnd.google-earth.kml+xml" {
			t.Errorf(mt.FullType())
			t.Errorf("Incorrect full type for %+v", mt)
		}
	}
}

func TestParameters(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {
		correctParameters := map[string]string{"charset": "utf-8"}

		for i, tree := range mt.Parameters() {
			if tree != correctParameters[i] {
				t.Errorf("Incorrect parameters for %+v", mt)
			}
		}
	}
}

func TestMainType(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.MainType() != "application" {
			t.Errorf("Incorrect main type for %+v", mt)
		}
	}
}

func TestSubType(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.SubType() != "kml" {
			t.Errorf("Incorrect sub type for %+v", mt)
		}
	}
}

func TestTrees(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {
		correctTrees := []string{"vnd", "google-earth"}

		for i, tree := range mt.Trees() {
			if tree != correctTrees[i] {
				t.Errorf("Incorrect trees for %+v", mt)
			}
		}
	}
}

func TestPrefix(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.Prefix() != "vnd" {
			t.Errorf("Incorrect prefix for %+v", mt)
		}
	}
}

func TestSuffix(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.Suffix() != "xml" {
			t.Errorf("Incorrect suffix for %+v", mt)
		}
	}
}

func TestString(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.String() != validComplexMediaType {
			t.Errorf("Incorrect string for %+v", mt)
		}
	}
}
<commit_msg>Adding a new test<commit_after>/**
 * MediaType
 *
 * Copyright © 2014 Trevor N. Suarez (Rican7)
 */

// Tests for the mediatype mutable structure

package mediatype

import (
	"testing"
)

/**
 * Tests functions
 */

func TestMainType(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.MainType() != "application" {
			t.Errorf("Incorrect main type for %+v", mt)
		}
	}
}

func TestSubType(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.SubType() != "kml" {
			t.Errorf("Incorrect sub type for %+v", mt)
		}
	}
}

func TestTrees(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {
		correctTrees := []string{"vnd", "google-earth"}

		for i, tree := range mt.Trees() {
			if tree != correctTrees[i] {
				t.Errorf("Incorrect trees for %+v", mt)
			}
		}
	}
}

func TestPrefix(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.Prefix() != "vnd" {
			t.Errorf("Incorrect prefix for %+v", mt)
		}
	}
}

func TestSuffix(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.Suffix() != "xml" {
			t.Errorf("Incorrect suffix for %+v", mt)
		}
	}
}

func TestParameters(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {
		correctParameters := map[string]string{"charset": "utf-8"}

		for i, tree := range mt.Parameters() {
			if tree != correctParameters[i] {
				t.Errorf("Incorrect parameters for %+v", mt)
			}
		}
	}
}

func TestFullType(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.FullType() != "application/vnd.google-earth.kml+xml" {
			t.Errorf("Incorrect full type for %+v", mt)
		}
	}
}

func TestFullTypeWithSimpleMainAndSub(t *testing.T) {
	mt, err := Parse("application/json")

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.FullType() != "application/json" {
			t.Errorf("Incorrect full type for %+v", mt)
		}
	}
}

func TestString(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.String() != validComplexMediaType {
			t.Errorf("Incorrect string for %+v", mt)
		}
	}
}
<|endoftext|>