<commit_before>package handler

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"time"

	"koding/tools/utils"
	"socialapi/models"
	"socialapi/workers/common/response"
	"strconv"
	"strings"

	"koding/db/mongodb/modelhelper"

	"github.com/PuerkitoBio/throttled"
	"github.com/koding/bongo"
	"github.com/koding/logging"
	kmetrics "github.com/koding/metrics"
	"github.com/koding/redis"
	"github.com/koding/runner"
	tigertonic "github.com/rcrowley/go-tigertonic"

	gometrics "github.com/rcrowley/go-metrics"
)

const (
	PostRequest   = "POST"
	GetRequest    = "GET"
	DeleteRequest = "DELETE"
)

var (
	// TODO allowed origins must be configurable
	cors         = tigertonic.NewCORSBuilder().AddAllowedOrigins("*")
	genericError = errors.New("an error occurred")
)

type Request struct {
	Handler        interface{}
	Endpoint       string
	Type           string
	Name           string
	CollectMetrics bool
	Metrics        *kmetrics.Metrics

	// Securer holds the secure functions for handlers
	Securer interface{}

	// used for external requests
	Params    map[string]string
	Cookie    string
	Cookies   []*http.Cookie
	Body      interface{}
	Headers   map[string]string
	Ratelimit *throttled.Throttler
}

var throttleErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
	runner.MustGetLogger().Error("Throttling error: %s", err)

	writeJSONError(w, genericError)
}

var throttleDenyHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	writeJSONError(w, response.LimitRateExceededError{})
})

// getAccount tries to retrieve account information from incoming request,
// should always return a valid account, not nil
func getAccount(r *http.Request, groupName string) *models.Account {
	cookie, err := r.Cookie("clientId")
	if err != nil {
		return models.NewAccount()
	}

	// if cookie doenst exists return empty account
	if cookie.Value == "" {
		return models.NewAccount()
	}

	session, err := models.Cache.Session.ById(cookie.Value)
	if err != nil {
		return models.NewAccount()
	}

	// if session doesnt have username, return empty account
	if session.Username == "" {
		return models.NewAccount()
	}

	acc, err := makeSureAccount(groupName, session.Username)
	if err != nil {
		if err != bongo.RecordNotFound {
			runner.MustGetLogger().Error("Err while getting account: %s, err :%s", session.Username, err.Error())
		}

		return models.NewAccount()
	}

	groupChannel, err := models.Cache.Channel.ByGroupName(groupName)
	if err != nil {
		if err != bongo.RecordNotFound {
			runner.MustGetLogger().Error("Err while getting group channel: %s, err :%s", groupName, err.Error())
		}

		return models.NewAccount()
	}

	if err := makeSureMembership(groupChannel, acc.Id); err != nil {
		runner.MustGetLogger().Error("Err while making sure account: %s, err :%s", groupName, err.Error())
		return models.NewAccount()
	}

	return acc
}

// makeSureAccount checks if incoming account is in postgres, if not creates it
// lazily and sets socialapi id of it in mongo
func makeSureAccount(groupName string, username string) (*models.Account, error) {
	// try to fetch account from postgres
	acc := models.NewAccount()
	err := acc.ByNick(username)
	if err == nil {
		return acc, nil
	}

	if err != bongo.RecordNotFound {
		return acc, err
	}

	// if account is not in postgres, try to create it

	// we need account first
	macc, err := modelhelper.GetAccount(username)
	if err != nil {
		return nil, err
	}

	acc = models.NewAccount() // account is nil, set it
	acc.OldId = macc.Id.Hex()
	acc.Nick = username
	if err := acc.Create(); err != nil {
		return nil, err
	}

	// set it to cache, in case we may need it
	if err := models.Cache.Account.SetToCache(acc); err != nil {
		return nil, err
	}

	// we just created the account in postgres, so update mongo with
	// socialApiId
	s := modelhelper.Selector{
		"profile.nickname": username,
	}
	o := modelhelper.Selector{"$set": modelhelper.Selector{
		"socialApiId": strconv.FormatInt(acc.Id, 10),
	}}
	if err := modelhelper.UpdateAccount(s, o); err != nil {
		return nil, err
	}

	return acc, nil
}

// makeSureMembership checks if the incoming account is a member of the group
// channel that is persisted within incoming session data
func makeSureMembership(groupChannel *models.Channel, accountId int64) error {
	isMember, err := models.Cache.Participant.ByChannelIdAndAccountId(groupChannel.Id, accountId)
	if err != nil {
		return err
	}
	// we dont need to do anything if the user is a member of group channel
	if isMember {
		return nil
	}

	_, err = groupChannel.AddParticipant(accountId)
	if err != nil && err != models.ErrAccountIsAlreadyInTheChannel {
		return err
	}

	return models.Cache.Participant.SetToCache(groupChannel.Id, accountId)
}

func getGroupName(r *http.Request) string {
	const groupName = "koding"

	cookie, err := r.Cookie("clientId")
	if err != nil {
		return groupName
	}

	// if cookie doenst exists return empty account
	if cookie.Value == "" {
		return groupName
	}

	session, err := models.Cache.Session.ById(cookie.Value)
	if err != nil {
		return groupName
	}

	if session.GroupName == "" {
		return groupName
	}

	return session.GroupName
}

const timedOutMsg = `{"description":"request timed out","error":"koding.RequestTimedoutError"}`
const timeoutDuration = time.Second * 30

func Wrapper(r Request) http.Handler {
	handler := r.Handler

	var hHandler http.Handler

	if r.Securer != nil {
		hHandler = Secure(handler, r.Securer, r.Name)
	} else {
		hHandler = tigertonic.Marshaled(handler)
	}

	hHandler = buildHandlerWithTimeTracking(hHandler, r)

	// every request should return under 30 secs, if not return 503 service
	// unavailable
	hHandler = http.TimeoutHandler(hHandler, timeoutDuration, timedOutMsg)

	// set rate limiting if the handler has one
	if r.Ratelimit != nil {
		hHandler = BuildHandlerWithRateLimit(hHandler, r.Ratelimit)
	}

	// create the final handler
	return cors.Build(hHandler)
}

// add request time tracking
func buildHandlerWithTimeTracking(handler http.Handler, r Request) http.Handler {
	var registry gometrics.Registry

	if r.Metrics != nil {
		registry = r.Metrics.Registry
	}

	return tigertonic.Timed(
		handler,
		r.Name,
		registry,
	)
}

func BuildHandlerWithContext(handler http.Handler, redis *redis.RedisSession, log logging.Logger) http.Handler {
	// add context
	return tigertonic.If(
		func(r *http.Request) (http.Header, error) {
			context := models.NewContext(redis, log)
			context.GroupName = getGroupName(r)
			context.Client = &models.Client{
				Account: getAccount(r, context.GroupName),
				IP:      net.ParseIP(utils.GetIpAddress(r)),
			}

			*(tigertonic.Context(r).(*models.Context)) = *context
			return nil, nil
		},
		handler,
	)
}

func BuildHandlerWithRateLimit(handler http.Handler, t *throttled.Throttler) http.Handler {
	throttled.Error = throttleErrorHandler
	throttled.DefaultDeniedHandler = throttleDenyHandler

	return t.Throttle(handler)
}

func DoRequest(request *Request) (*http.Response, error) {
	if request.Cookie != "" {
		request.Cookies = parseCookiesToArray(request.Cookie)
	}

	request.Endpoint = prepareQueryString(request.Endpoint, request.Params)

	client := new(http.Client)

	var byteData io.Reader
	if request.Body != nil {
		body, err := json.Marshal(request.Body)
		if err != nil {
			return nil, err
		}
		byteData = bytes.NewReader(body)
	}

	req, err := http.NewRequest(request.Type, request.Endpoint, byteData)
	req = prepareHeaders(req, request.Headers)

	if err != nil {
		return nil, err
	}

	// Add cookies
	for _, cookie := range request.Cookies {
		req.AddCookie(cookie)
	}

	return client.Do(req)
}

func prepareQueryString(endpoint string, params map[string]string) string {
	if len(params) == 0 {
		return endpoint
	}

	fullPath := fmt.Sprintf("%s?", endpoint)

	for key, value := range params {
		fullPath = fmt.Sprintf("%s%s=%s&", fullPath, key, value)
	}

	return fullPath[0 : len(fullPath)-1]
}

func prepareHeaders(req *http.Request, headers map[string]string) *http.Request {
	newReq := new(http.Request)
	newReq = newReq
	*newReq = *req
	for k, v := range headers {
		newReq.Header.Set(k, v)
	}

	return newReq
}

func parseCookiesToArray(cookie string) []*http.Cookie {
	pairs := strings.Split(cookie, "; ")
	cookies := make([]*http.Cookie, 0)

	if len(pairs) == 0 {
		return cookies
	}

	for _, val := range pairs {
		cp := strings.Split(val, "=")
		if len(cp) != 2 {
			continue
		}

		c := new(http.Cookie)
		c.Name = cp[0]
		c.Value = cp[1]

		cookies = append(cookies, c)
	}

	return cookies
}
<commit_msg>Socialapi: ignore ErrGroupNotFound<commit_after>package handler

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"time"

	"koding/tools/utils"
	"socialapi/models"
	"socialapi/workers/common/response"
	"strconv"
	"strings"

	"koding/db/mongodb/modelhelper"

	"github.com/PuerkitoBio/throttled"
	"github.com/koding/bongo"
	"github.com/koding/logging"
	kmetrics "github.com/koding/metrics"
	"github.com/koding/redis"
	"github.com/koding/runner"
	tigertonic "github.com/rcrowley/go-tigertonic"

	gometrics "github.com/rcrowley/go-metrics"
)

const (
	PostRequest   = "POST"
	GetRequest    = "GET"
	DeleteRequest = "DELETE"
)

var (
	// TODO allowed origins must be configurable
	cors         = tigertonic.NewCORSBuilder().AddAllowedOrigins("*")
	genericError = errors.New("an error occurred")
)

type Request struct {
	Handler        interface{}
	Endpoint       string
	Type           string
	Name           string
	CollectMetrics bool
	Metrics        *kmetrics.Metrics

	// Securer holds the secure functions for handlers
	Securer interface{}

	// used for external requests
	Params    map[string]string
	Cookie    string
	Cookies   []*http.Cookie
	Body      interface{}
	Headers   map[string]string
	Ratelimit *throttled.Throttler
}

var throttleErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
	runner.MustGetLogger().Error("Throttling error: %s", err)

	writeJSONError(w, genericError)
}

var throttleDenyHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	writeJSONError(w, response.LimitRateExceededError{})
})

// getAccount tries to retrieve account information from incoming request,
// should always return a valid account, not nil
func getAccount(r *http.Request, groupName string) *models.Account {
	cookie, err := r.Cookie("clientId")
	if err != nil {
		return models.NewAccount()
	}

	// if cookie doenst exists return empty account
	if cookie.Value == "" {
		return models.NewAccount()
	}

	session, err := models.Cache.Session.ById(cookie.Value)
	if err != nil {
		return models.NewAccount()
	}

	// if session doesnt have username, return empty account
	if session.Username == "" {
		return models.NewAccount()
	}

	acc, err := makeSureAccount(groupName, session.Username)
	if err != nil {
		if err != bongo.RecordNotFound {
			runner.MustGetLogger().Error("Err while getting account: %s, err :%s", session.Username, err.Error())
		}

		return models.NewAccount()
	}

	groupChannel, err := models.Cache.Channel.ByGroupName(groupName)
	if err != nil {
		if err != bongo.RecordNotFound && err != models.ErrGroupNotFound {
			runner.MustGetLogger().Error("Err while getting group channel: %s, err :%s", groupName, err.Error())
		}

		return models.NewAccount()
	}

	if err := makeSureMembership(groupChannel, acc.Id); err != nil {
		runner.MustGetLogger().Error("Err while making sure account: %s, err :%s", groupName, err.Error())
		return models.NewAccount()
	}

	return acc
}

// makeSureAccount checks if incoming account is in postgres, if not creates it
// lazily and sets socialapi id of it in mongo
func makeSureAccount(groupName string, username string) (*models.Account, error) {
	// try to fetch account from postgres
	acc := models.NewAccount()
	err := acc.ByNick(username)
	if err == nil {
		return acc, nil
	}

	if err != bongo.RecordNotFound {
		return acc, err
	}

	// if account is not in postgres, try to create it

	// we need account first
	macc, err := modelhelper.GetAccount(username)
	if err != nil {
		return nil, err
	}

	acc = models.NewAccount() // account is nil, set it
	acc.OldId = macc.Id.Hex()
	acc.Nick = username
	if err := acc.Create(); err != nil {
		return nil, err
	}

	// set it to cache, in case we may need it
	if err := models.Cache.Account.SetToCache(acc); err != nil {
		return nil, err
	}

	// we just created the account in postgres, so update mongo with
	// socialApiId
	s := modelhelper.Selector{
		"profile.nickname": username,
	}
	o := modelhelper.Selector{"$set": modelhelper.Selector{
		"socialApiId": strconv.FormatInt(acc.Id, 10),
	}}
	if err := modelhelper.UpdateAccount(s, o); err != nil {
		return nil, err
	}

	return acc, nil
}

// makeSureMembership checks if the incoming account is a member of the group
// channel that is persisted within incoming session data
func makeSureMembership(groupChannel *models.Channel, accountId int64) error {
	isMember, err := models.Cache.Participant.ByChannelIdAndAccountId(groupChannel.Id, accountId)
	if err != nil {
		return err
	}
	// we dont need to do anything if the user is a member of group channel
	if isMember {
		return nil
	}

	_, err = groupChannel.AddParticipant(accountId)
	if err != nil && err != models.ErrAccountIsAlreadyInTheChannel {
		return err
	}

	return models.Cache.Participant.SetToCache(groupChannel.Id, accountId)
}

func getGroupName(r *http.Request) string {
	const groupName = "koding"

	cookie, err := r.Cookie("clientId")
	if err != nil {
		return groupName
	}

	// if cookie doenst exists return empty account
	if cookie.Value == "" {
		return groupName
	}

	session, err := models.Cache.Session.ById(cookie.Value)
	if err != nil {
		return groupName
	}

	if session.GroupName == "" {
		return groupName
	}

	return session.GroupName
}

const timedOutMsg = `{"description":"request timed out","error":"koding.RequestTimedoutError"}`
const timeoutDuration = time.Second * 30

func Wrapper(r Request) http.Handler {
	handler := r.Handler

	var hHandler http.Handler

	if r.Securer != nil {
		hHandler = Secure(handler, r.Securer, r.Name)
	} else {
		hHandler = tigertonic.Marshaled(handler)
	}

	hHandler = buildHandlerWithTimeTracking(hHandler, r)

	// every request should return under 30 secs, if not return 503 service
	// unavailable
	hHandler = http.TimeoutHandler(hHandler, timeoutDuration, timedOutMsg)

	// set rate limiting if the handler has one
	if r.Ratelimit != nil {
		hHandler = BuildHandlerWithRateLimit(hHandler, r.Ratelimit)
	}

	// create the final handler
	return cors.Build(hHandler)
}

// add request time tracking
func buildHandlerWithTimeTracking(handler http.Handler, r Request) http.Handler {
	var registry gometrics.Registry

	if r.Metrics != nil {
		registry = r.Metrics.Registry
	}

	return tigertonic.Timed(
		handler,
		r.Name,
		registry,
	)
}

func BuildHandlerWithContext(handler http.Handler, redis *redis.RedisSession, log logging.Logger) http.Handler {
	// add context
	return tigertonic.If(
		func(r *http.Request) (http.Header, error) {
			context := models.NewContext(redis, log)
			context.GroupName = getGroupName(r)
			context.Client = &models.Client{
				Account: getAccount(r, context.GroupName),
				IP:      net.ParseIP(utils.GetIpAddress(r)),
			}

			*(tigertonic.Context(r).(*models.Context)) = *context
			return nil, nil
		},
		handler,
	)
}

func BuildHandlerWithRateLimit(handler http.Handler, t *throttled.Throttler) http.Handler {
	throttled.Error = throttleErrorHandler
	throttled.DefaultDeniedHandler = throttleDenyHandler

	return t.Throttle(handler)
}

func DoRequest(request *Request) (*http.Response, error) {
	if request.Cookie != "" {
		request.Cookies = parseCookiesToArray(request.Cookie)
	}

	request.Endpoint = prepareQueryString(request.Endpoint, request.Params)

	client := new(http.Client)

	var byteData io.Reader
	if request.Body != nil {
		body, err := json.Marshal(request.Body)
		if err != nil {
			return nil, err
		}
		byteData = bytes.NewReader(body)
	}

	req, err := http.NewRequest(request.Type, request.Endpoint, byteData)
	req = prepareHeaders(req, request.Headers)

	if err != nil {
		return nil, err
	}

	// Add cookies
	for _, cookie := range request.Cookies {
		req.AddCookie(cookie)
	}

	return client.Do(req)
}

func prepareQueryString(endpoint string, params map[string]string) string {
	if len(params) == 0 {
		return endpoint
	}

	fullPath := fmt.Sprintf("%s?", endpoint)

	for key, value := range params {
		fullPath = fmt.Sprintf("%s%s=%s&", fullPath, key, value)
	}

	return fullPath[0 : len(fullPath)-1]
}

func prepareHeaders(req *http.Request, headers map[string]string) *http.Request {
	newReq := new(http.Request)
	newReq = newReq
	*newReq = *req
	for k, v := range headers {
		newReq.Header.Set(k, v)
	}

	return newReq
}

func parseCookiesToArray(cookie string) []*http.Cookie {
	pairs := strings.Split(cookie, "; ")
	cookies := make([]*http.Cookie, 0)

	if len(pairs) == 0 {
		return cookies
	}

	for _, val := range pairs {
		cp := strings.Split(val, "=")
		if len(cp) != 2 {
			continue
		}

		c := new(http.Cookie)
		c.Name = cp[0]
		c.Value = cp[1]

		cookies = append(cookies, c)
	}

	return cookies
}
<|endoftext|>


<commit_before>package game

type Room int

type RoomStat struct {
	Lv1Name     string
	Lv2Name     string
	Lv3Name     string
	Tier        int
	Category    string
	Ability     string
	MinDwellers int
	CostBase    int
	CostAdd     int
	CostLv2     int
	CostLv3     int
	Storage     [3][3]int
	Yield       [3][3]int
}

func (rs RoomStat) Name(level int) string {
	switch level {
	case 1:
		return rs.Lv1Name
	case 2:
		if rs.Lv2Name != "" {
			return rs.Lv2Name
		}
	case 3:
		if rs.Lv3Name != "" {
			return rs.Lv3Name
		}
	}
	return rs.Lv1Name
}

type Storage int

const (
	People Storage = iota
	Items
	Food
	Water
	Power
	Health
	Radiation
)

func (rs RoomStat) Stores() []Storage {
	switch rs.Category {
	case "People":
		return []Storage{People}
	case "Storage":
		return []Storage{
			Items,
			Health,
			Radiation,
		}
	case "Food":
		return []Storage{Food}
	case "Water":
		return []Storage{Water}
	case "Power":
		return []Storage{Power}
	case "Health":
		return []Storage{Health}
	case "Radiation":
		return []Storage{Radiation}
	}
	return []Storage{}
}

func (rs RoomStat) Produces() []Storage {
	switch rs.Category {
	case "Food":
		return []Storage{Food}
	case "Water":
		return []Storage{Water}
	case "Food+Water":
		return []Storage{Food, Water}
	case "Power":
		return []Storage{Power}
	case "Health":
		return []Storage{Health}
	case "Radiation":
		return []Storage{Radiation}
	}
	return []Storage{}
}

func (rs RoomStat) Capacity(level, size int) int {
	return rs.Storage[level-1][size-1]
}

func (rs RoomStat) Production(level, size int) int {
	return rs.Yield[level-1][size-1]
}

// UpgradeCost computes the cost to upgrade a room of size provided
// from provided level to the next level.
func (rs RoomStat) UpgradeCost(level, size int) int {
	var cost int
	switch level {
	case 1:
		cost = rs.CostLv2
	case 2:
		cost = rs.CostLv3
	default:
		return 0
	}

	// vault door
	if rs.Category == "" {
		return cost
	}

	return cost + cost*(size-1)/2
}

var Rooms = map[Room]RoomStat{
	-1: {
		Lv1Name: "Vault Door",
		CostLv2: 500,
		CostLv3: 2000,
	},
	0: {
		Lv1Name: "Dirt",
	},
	1: {
		Lv1Name:  "Elevator",
		CostBase: 100,
		CostAdd:  25,
	},
	// storage
	2: {
		Lv1Name:  "PeopleU1",
		Lv2Name:  "PeopleU2",
		Lv3Name:  "PeopleU3",
		Category: "People",
		CostBase: 100,
		CostAdd:  25,
		CostLv2:  250,
		CostLv3:  750,
		Storage: [3][3]int{
			{8, 18, 32},
			{10, 24, 38},
			{12, 28, 46},
		},
	},
	3: {
		Lv1Name:     "StorageU1",
		Lv2Name:     "StorageU2",
		Lv3Name:     "StorageU3",
		Category:    "Storage",
		MinDwellers: 12,
		CostBase:    100,
		CostAdd:     25,
		CostLv2:     750,
		CostLv3:     2250,
		Storage: [3][3]int{
			{10, 20, 30},
			{15, 30, 45},
			{20, 40, 60},
		},
	},
	// food production
	4: {
		Lv1Name:  "Food1U1",
		Lv2Name:  "Food1U2",
		Lv3Name:  "Food1U3",
		Category: "Food",
		CostBase: 100,
		CostAdd:  25,
		CostLv2:  250,
		CostLv3:  750,
		Yield: [3][3]int{
			{8, 18, 30},
			{10, 22, 36},
			{12, 26, 42},
		},
		Storage: [3][3]int{
			{50, 100, 150},
			{75, 150, 225},
			{100, 200, 300},
		},
		Tier: 1,
	},
	5: {
		Lv1Name:  "Food2U1",
		Lv2Name:  "Food2U2",
		Lv3Name:  "Food2U3",
		Category: "Food",
		CostBase: 1200,
		CostAdd:  300,
		CostLv2:  3000,
		CostLv3:  9000,
		Yield: [3][3]int{
			{10, 23, 36},
			{13, 29, 45},
			{16, 35, 54},
		},
		Storage: [3][3]int{
			{60, 120, 180},
			{90, 180, 270},
			{120, 240, 360},
		},
		Tier:        2,
		MinDwellers: 70,
	},
	// power production
	6: {
		Lv1Name:  "Power1U1",
		Lv2Name:  "Power1U2",
		Lv3Name:  "Power1U3",
		Category: "Power",
		CostBase: 100,
		CostAdd:  25,
		CostLv2:  250,
		CostLv3:  750,
		Yield: [3][3]int{
			{14, 30, 44},
			{17, 36, 55},
			{20, 42, 66},
		},
		Storage: [3][3]int{
			{75, 150, 225},
			{112, 224, 336},
			{150, 300, 450},
		},
		Tier: 1,
	},
	7: {
		Lv1Name:  "Power2U1",
		Lv2Name:  "Power2U2",
		Lv3Name:  "Power2U3",
		Category: "Power",
		CostBase: 1200,
		CostAdd:  300,
		CostLv2:  3000,
		CostLv3:  9000,
		Yield: [3][3]int{
			{20, 44, 66},
			{24, 55, 77},
			{28, 66, 88},
		},
		Storage: [3][3]int{
			{200, 400, 600},
			{300, 600, 900},
			{400, 800, 1200},
		},
		Tier:        2,
		MinDwellers: 60,
	},
	// water production
	8: {
		Lv1Name:  "Water1U1",
		Lv2Name:  "Water1U2",
		Lv3Name:  "Water1U3",
		Category: "Water",
		CostBase: 100,
		CostAdd:  25,
		CostLv2:  250,
		CostLv3:  750,
		Yield: [3][3]int{
			{8, 18, 30},
			{10, 22, 36},
			{12, 26, 42},
		},
		Storage: [3][3]int{
			{8, 18, 32},
			{10, 24, 38},
			{12, 28, 46},
		},
		Tier: 1,
	},
	9: {
		Lv1Name:  "Water2U1",
		Lv2Name:  "Water2U2",
		Lv3Name:  "Water2U3",
		Category: "Water",
		CostBase: 1200,
		CostAdd:  300,
		CostLv2:  3000,
		CostLv3:  9000,
		Yield: [3][3]int{
			{10, 23, 36},
			{13, 29, 45},
			{16, 35, 54},
		},
		Storage: [3][3]int{
			{50, 100, 150},
			{75, 150, 225},
			{100, 200, 300},
		},
		Tier:        2,
		MinDwellers: 80,
	},
	// medical
	10: {
		Lv1Name:  "Health1U1",
		Lv2Name:  "Health1U2",
		Lv3Name:  "Health1U3",
		Category: "Health",
		CostBase: 400,
		CostAdd:  100,
		CostLv2:  1000,
		CostLv3:  3000,
		Yield: [3][3]int{
			{8, 18, 32},
			{10, 24, 38},
			{12, 28, 46},
		},
		Storage: [3][3]int{
			{60, 120, 180},
			{90, 180, 270},
			{120, 240, 360},
		},
		Tier:        1,
		MinDwellers: 14,
	},
	11: {
		Lv1Name:  "Radiation1U1",
		Lv2Name:  "Radiation1U2",
		Lv3Name:  "Radiation1U3",
		Category: "Radiation",
		CostBase: 400,
		CostAdd:  100,
		CostLv2:  1000,
		CostLv3:  3000,
		Yield: [3][3]int{
			{8, 18, 32},
			{10, 24, 38},
			{12, 28, 46},
		},
		Storage: [3][3]int{
			{8, 18, 32},
			{10, 24, 38},
			{12, 28, 46},
		},
		Tier:        1,
		MinDwellers: 16,
	},
	//
}
<commit_msg>Add food+water room<commit_after>package game

type Room int

type RoomStat struct {
	Lv1Name     string
	Lv2Name     string
	Lv3Name     string
	Tier        int
	Category    string
	Ability     string
	MinDwellers int
	CostBase    int
	CostAdd     int
	CostLv2     int
	CostLv3     int
	Storage     [3][3]int
	Yield       [3][3]int
}

func (rs RoomStat) Name(level int) string {
	switch level {
	case 1:
		return rs.Lv1Name
	case 2:
		if rs.Lv2Name != "" {
			return rs.Lv2Name
		}
	case 3:
		if rs.Lv3Name != "" {
			return rs.Lv3Name
		}
	}
	return rs.Lv1Name
}

type Storage int

const (
	People Storage = iota
	Items
	Food
	Water
	Power
	Health
	Radiation
)

func (rs RoomStat) Stores() []Storage {
	switch rs.Category {
	case "People":
		return []Storage{People}
	case "Storage":
		return []Storage{
			Items,
			Health,
			Radiation,
		}
	case "Food":
		return []Storage{Food}
	case "Water":
		return []Storage{Water}
	case "Power":
		return []Storage{Power}
	case "Health":
		return []Storage{Health}
	case "Radiation":
		return []Storage{Radiation}
	}
	return []Storage{}
}

func (rs RoomStat) Produces() []Storage {
	switch rs.Category {
	case "Food":
		return []Storage{Food}
	case "Water":
		return []Storage{Water}
	case "Food+Water":
		return []Storage{Food, Water}
	case "Power":
		return []Storage{Power}
	case "Health":
		return []Storage{Health}
	case "Radiation":
		return []Storage{Radiation}
	}
	return []Storage{}
}

func (rs RoomStat) Capacity(level, size int) int {
	return rs.Storage[level-1][size-1]
}

func (rs RoomStat) Production(level, size int) int {
	return rs.Yield[level-1][size-1]
}

// UpgradeCost computes the cost to upgrade a room of size provided
// from provided level to the next level.
func (rs RoomStat) UpgradeCost(level, size int) int {
	var cost int
	switch level {
	case 1:
		cost = rs.CostLv2
	case 2:
		cost = rs.CostLv3
	default:
		return 0
	}

	// vault door
	if rs.Category == "" {
		return cost
	}

	return cost + cost*(size-1)/2
}

var Rooms = map[Room]RoomStat{
	-1: {
		Lv1Name: "Vault Door",
		CostLv2: 500,
		CostLv3: 2000,
	},
	0: {
		Lv1Name: "Dirt",
	},
	1: {
		Lv1Name:  "Elevator",
		CostBase: 100,
		CostAdd:  25,
	},
	// storage
	2: {
		Lv1Name:  "PeopleU1",
		Lv2Name:  "PeopleU2",
		Lv3Name:  "PeopleU3",
		Category: "People",
		CostBase: 100,
		CostAdd:  25,
		CostLv2:  250,
		CostLv3:  750,
		Storage: [3][3]int{
			{8, 18, 32},
			{10, 24, 38},
			{12, 28, 46},
		},
	},
	3: {
		Lv1Name:     "StorageU1",
		Lv2Name:     "StorageU2",
		Lv3Name:     "StorageU3",
		Category:    "Storage",
		MinDwellers: 12,
		CostBase:    100,
		CostAdd:     25,
		CostLv2:     750,
		CostLv3:     2250,
		Storage: [3][3]int{
			{10, 20, 30},
			{15, 30, 45},
			{20, 40, 60},
		},
	},
	// food production
	4: {
		Lv1Name:  "Food1U1",
		Lv2Name:  "Food1U2",
		Lv3Name:  "Food1U3",
		Category: "Food",
		CostBase: 100,
		CostAdd:  25,
		CostLv2:  250,
		CostLv3:  750,
		Yield: [3][3]int{
			{8, 18, 30},
			{10, 22, 36},
			{12, 26, 42},
		},
		Storage: [3][3]int{
			{50, 100, 150},
			{75, 150, 225},
			{100, 200, 300},
		},
		Tier: 1,
	},
	5: {
		Lv1Name:  "Food2U1",
		Lv2Name:  "Food2U2",
		Lv3Name:  "Food2U3",
		Category: "Food",
		CostBase: 1200,
		CostAdd:  300,
		CostLv2:  3000,
		CostLv3:  9000,
		Yield: [3][3]int{
			{10, 23, 36},
			{13, 29, 45},
			{16, 35, 54},
		},
		Storage: [3][3]int{
			{60, 120, 180},
			{90, 180, 270},
			{120, 240, 360},
		},
		Tier:        2,
		MinDwellers: 70,
	},
	// power production
	6: {
		Lv1Name:  "Power1U1",
		Lv2Name:  "Power1U2",
		Lv3Name:  "Power1U3",
		Category: "Power",
		CostBase: 100,
		CostAdd:  25,
		CostLv2:  250,
		CostLv3:  750,
		Yield: [3][3]int{
			{14, 30, 44},
			{17, 36, 55},
			{20, 42, 66},
		},
		Storage: [3][3]int{
			{75, 150, 225},
			{112, 224, 336},
			{150, 300, 450},
		},
		Tier: 1,
	},
	7: {
		Lv1Name:  "Power2U1",
		Lv2Name:  "Power2U2",
		Lv3Name:  "Power2U3",
		Category: "Power",
		CostBase: 1200,
		CostAdd:  300,
		CostLv2:  3000,
		CostLv3:  9000,
		Yield: [3][3]int{
			{20, 44, 66},
			{24, 55, 77},
			{28, 66, 88},
		},
		Storage: [3][3]int{
			{200, 400, 600},
			{300, 600, 900},
			{400, 800, 1200},
		},
		Tier:        2,
		MinDwellers: 60,
	},
	// water production
	8: {
		Lv1Name:  "Water1U1",
		Lv2Name:  "Water1U2",
		Lv3Name:  "Water1U3",
		Category: "Water",
		CostBase: 100,
		CostAdd:  25,
		CostLv2:  250,
		CostLv3:  750,
		Yield: [3][3]int{
			{8, 18, 30},
			{10, 22, 36},
			{12, 26, 42},
		},
		Storage: [3][3]int{
			{8, 18, 32},
			{10, 24, 38},
			{12, 28, 46},
		},
		Tier: 1,
	},
	9: {
		Lv1Name:  "Water2U1",
		Lv2Name:  "Water2U2",
		Lv3Name:  "Water2U3",
		Category: "Water",
		CostBase: 1200,
		CostAdd:  300,
		CostLv2:  3000,
		CostLv3:  9000,
		Yield: [3][3]int{
			{10, 23, 36},
			{13, 29, 45},
			{16, 35, 54},
		},
		Storage: [3][3]int{
			{50, 100, 150},
			{75, 150, 225},
			{100, 200, 300},
		},
		Tier:        2,
		MinDwellers: 80,
	},
	// medical
	10: {
		Lv1Name:  "Health1U1",
		Lv2Name:  "Health1U2",
		Lv3Name:  "Health1U3",
		Category: "Health",
		CostBase: 400,
		CostAdd:  100,
		CostLv2:  1000,
		CostLv3:  3000,
		Yield: [3][3]int{
			{3, 6, 9},
			{4, 8, 12},
			{6, 12, 18},
		},
		Storage: [3][3]int{
			{10, 20, 30},
			{12, 24, 36},
			{15, 30, 45},
		},
		Tier:        1,
		MinDwellers: 14,
	},
	11: {
		Lv1Name:  "Radiation1U1",
		Lv2Name:  "Radiation1U2",
		Lv3Name:  "Radiation1U3",
		Category: "Radiation",
		CostBase: 400,
		CostAdd:  100,
		CostLv2:  1000,
		CostLv3:  3000,
		Yield: [3][3]int{
			{3, 6, 9},
			{4, 8, 12},
			{6, 12, 18},
		},
		Storage: [3][3]int{
			{10, 20, 30},
			{12, 24, 36},
			{15, 30, 45},
		},
		Tier:        1,
		MinDwellers: 16,
	},
	// food+water
	12: {
		Lv1Name:  "Meal1U1",
		Lv2Name:  "Meal1U2",
		Lv3Name:  "Meal1U3",
		Category: "Food+Water",
		CostBase: 2500,
		CostAdd:  500,
		CostLv2:  4000,
		CostLv3:  10000,
		Yield: [3][3]int{
			{8, 18, 30},
			{10, 22, 36},
			{12, 26, 42},
		},
		Storage: [3][3]int{
			{50, 100, 150},
			{75, 150, 225},
			{100, 200, 300},
		},
		Tier: 1,
	},
}
<|endoftext|>


<commit_before>package zog

import "fmt"

func Decode(inCh chan byte) (chan instruction, chan error) {
	errCh := make(chan error)
	iCh := make(chan instruction)
	go decode(inCh, iCh, errCh)
	return iCh, errCh
}

func DecodeBytes(buf []byte) ([]instruction, error) {

	ch := make(chan byte)

	go func() {
		for _, n := range buf {
			ch <- n
		}
		close(ch)
	}()

	var insts []instruction
	var err error
	var ok bool

	instCh, errCh := Decode(ch)
	looping := true
	for looping {
		select {
		case inst, ok := <-instCh:
			if !ok {
				looping = false
				break
			}
			insts = append(insts, inst)
		case err, ok = <-errCh:
			if !ok {
				looping = false
				break
			}
			break
		}
	}

	return insts, err
}

func getImmd(inCh chan byte) (Disp, error) {
	d, ok := <-inCh
	if !ok {
		return 0, fmt.Errorf("getImmd: Can't get byte")
	}
	return Disp(d), nil
}
func getImmN(inCh chan byte) (Imm8, error) {
	n, ok := <-inCh
	if !ok {
		return 0, fmt.Errorf("getImmN: Can't get byte")
	}
	return Imm8(n), nil
}

func getImmNN(inCh chan byte) (Imm16, error) {
	l, ok := <-inCh
	if !ok {
		return 0, fmt.Errorf("getImmNN: Can't get lo byte")
	}
	h, ok := <-inCh
	if !ok {
		return 0, fmt.Errorf("getImmNN: Can't get hi byte")
	}
	return Imm16(uint16(h)<<8 | uint16(l)), nil
}

func decode(inCh chan byte, iCh chan instruction, errCh chan error) {

	// Set to 0 if no prefix in effect
	var prefix byte

	for n := range inCh {
		// Prefix bytes
		switch n {
		case 0xDD:
		case 0xFD:
			if prefix != 0 {
				errCh <- fmt.Errorf("Double prefix: %02% %02X", prefix, n)
			}
			prefix = n
			continue

		}

		var inst instruction
		var instErr error
		x, y, z, p, q := decomposeByte(n)
		fmt.Printf("D: N %02X, x %d y %d z %d p %d q %d\n", n, x, y, z, p, q)
		switch x {
		case 0:
			switch z {
			case 0:
				switch y {
				case 0:
					inst = NOP
				case 1:
					inst = &EX{a: AF, b: AF_PRIME}
				case 2:
					d, err := getImmd(inCh)
					if err == nil {
						inst = &DJNZ{d}
					} else {
						instErr = err
					}
				case 3:
					d, err := getImmd(inCh)
					if err == nil {
						inst = &JR{True, d}
					} else {
						instErr = err
					}
				case 4, 5, 6, 7:
					d, err := getImmd(inCh)
					if err == nil {
						inst = &JR{tableCC[y-4], d}
					} else {
						instErr = err
					}
				}
			case 1:
				if q == 0 {
					nn, err := getImmNN(inCh)
					if err == nil {
						inst = &LD16{tableRP[p], nn}
					} else {
						instErr = err
					}
				} else {
					inst = &ADD16{HL, tableRP[p]}
				}
			case 2:
				if q == 0 {
					switch p {
					case 0:
						inst = &LD8{Contents{BC}, A}
					case 1:
						inst = &LD8{Contents{DE}, A}
					case 2:
						nn, err := getImmNN(inCh)
						if err == nil {
							inst = &LD16{Contents{nn}, HL}
						} else {
							instErr = err
						}
					case 3:
						nn, err := getImmNN(inCh)
						if err == nil {
							inst = &LD8{Contents{nn}, A}
						} else {
							instErr = err
						}
					}
				} else {
					switch p {
					case 0:
						inst = &LD8{A, Contents{BC}}
					case 1:
						inst = &LD8{A, Contents{DE}}
					case 2:
						nn, err := getImmNN(inCh)
						if err == nil {
							inst = &LD16{HL, Contents{nn}}
						} else {
							instErr = err
						}
					case 3:
						nn, err := getImmNN(inCh)
						if err == nil {
							inst = &LD8{A, Contents{nn}}
						} else {
							instErr = err
						}
					}
				}
			case 3:
				if q == 0 {
					inst = &INC16{tableRP[p]}
				} else {
					inst = &DEC16{tableRP[p]}
				}
			case 4:
				inst = &INC8{tableR[y]}
			case 5:
				inst = &DEC8{tableR[y]}
			case 6:
				n, err := getImmN(inCh)
				if err == nil {
					inst = &LD8{tableR[y], n}
				} else {
					instErr = err
				}
			case 7:
				switch y {
				case 0:
					inst = RLCA
				case 1:
					inst = RRCA
				case 2:
					inst = RLA
				case 3:
					inst = RRA
				case 4:
					inst = DAA
				case 5:
					inst = CPL
				case 6:
					inst = SCF
				case 7:
					inst = CCF
				}
			}
		case 1:
			if x == 6 && y == 6 {
				inst = HALT
			} else {
				inst = &LD8{tableR[y], tableR[z]}
			}
		}
		fmt.Printf("D: inst [%v] err [%v]\n", inst, instErr)

		if inst == nil {
			err := instErr
			if err == nil {
				err = fmt.Errorf("TODO - impl %02X [%02X]", n, prefix)
			}
			errCh <- err
		} else {
			iCh <- inst
		}

		prefix = 0
	}
	close(iCh)
	close(errCh)
}

var tableR []Loc8 = []Loc8{B, C, D, E, H, L, Contents{HL}, A}
var tableRP []Loc16 = []Loc16{BC, DE, HL, SP}
var tableRP2 []Loc16 = []Loc16{BC, DE, HL, AF}
var tableCC []Conditional = []Conditional{Not{FT_Z}, FT_Z, Not{FT_C}, FT_C, FT_PO, FT_PE, FT_P, FT_M}

func decomposeByte(n byte) (byte, byte, byte, byte, byte) {
	// We follow terminology from http://www.z80.info/decoding.htm
	// x = the opcode's 1st octal digit (i.e. bits 7-6)
	// y = the opcode's 2nd octal digit (i.e. bits 5-3)
	// z = the opcode's 3rd octal digit (i.e. bits 2-0)
	// p = y rightshifted one position (i.e. bits 5-4)
	// q = y modulo 2 (i.e. bit 3)
	z := n & 0x07
	y := (n >> 3) & 0x07
	x := (n >> 6) & 0x07
	p := y >> 1
	q := y & 0x01

	return x, y, z, p, q
}
<commit_msg>fix HALT<commit_after>package zog

import "fmt"

func Decode(inCh chan byte) (chan instruction, chan error) {
	errCh := make(chan error)
	iCh := make(chan instruction)
	go decode(inCh, iCh, errCh)
	return iCh, errCh
}

func DecodeBytes(buf []byte) ([]instruction, error) {

	ch := make(chan byte)

	go func() {
		for _, n := range buf {
			ch <- n
		}
		close(ch)
	}()

	var insts []instruction
	var err error
	var ok bool

	instCh, errCh := Decode(ch)
	looping := true
	for looping {
		select {
		case inst, ok := <-instCh:
			if !ok {
				looping = false
				break
			}
			insts = append(insts, inst)
		case err, ok = <-errCh:
			if !ok {
				looping = false
				break
			}
			break
		}
	}

	return insts, err
}

func getImmd(inCh chan byte) (Disp, error) {
	d, ok := <-inCh
	if !ok {
		return 0, fmt.Errorf("getImmd: Can't get byte")
	}
	return Disp(d), nil
}
func getImmN(inCh chan byte) (Imm8, error) {
	n, ok := <-inCh
	if !ok {
		return 0, fmt.Errorf("getImmN: Can't get byte")
	}
	return Imm8(n), nil
}

func getImmNN(inCh chan byte) (Imm16, error) {
	l, ok := <-inCh
	if !ok {
		return 0, fmt.Errorf("getImmNN: Can't get lo byte")
	}
	h, ok := <-inCh
	if !ok {
		return 0, fmt.Errorf("getImmNN: Can't get hi byte")
	}
	return Imm16(uint16(h)<<8 | uint16(l)), nil
}

func decode(inCh chan byte, iCh chan instruction, errCh chan error) {

	// Set to 0 if no prefix in effect
	var prefix byte

	for n := range inCh {
		// Prefix bytes
		switch n {
		case 0xDD:
		case 0xFD:
			if prefix != 0 {
				errCh <- fmt.Errorf("Double prefix: %02% %02X", prefix, n)
			}
			prefix = n
			continue

		}

		var inst instruction
		var instErr error
		x, y, z, p, q := decomposeByte(n)
		fmt.Printf("D: N %02X, x %d y %d z %d p %d q %d\n", n, x, y, z, p, q)
		switch x {
		case 0:
			switch z {
			case 0:
				switch y {
				case 0:
					inst = NOP
				case 1:
					inst = &EX{a: AF, b: AF_PRIME}
				case 2:
					d, err := getImmd(inCh)
					if err == nil {
						inst = &DJNZ{d}
					} else {
						instErr = err
					}
				case 3:
					d, err := getImmd(inCh)
					if err == nil {
						inst = &JR{True, d}
					} else {
						instErr = err
					}
				case 4, 5, 6, 7:
					d, err := getImmd(inCh)
					if err == nil {
						inst = &JR{tableCC[y-4], d}
					} else {
						instErr = err
					}
				}
			case 1:
				if q == 0 {
					nn, err := getImmNN(inCh)
					if err == nil {
						inst = &LD16{tableRP[p], nn}
					} else {
						instErr = err
					}
				} else {
					inst = &ADD16{HL, tableRP[p]}
				}
			case 2:
				if q == 0 {
					switch p {
					case 0:
						inst = &LD8{Contents{BC}, A}
					case 1:
						inst = &LD8{Contents{DE}, A}
					case 2:
						nn, err := getImmNN(inCh)
						if err == nil {
							inst = &LD16{Contents{nn}, HL}
						} else {
							instErr = err
						}
					case 3:
						nn, err := getImmNN(inCh)
						if err == nil {
							inst = &LD8{Contents{nn}, A}
						} else {
							instErr = err
						}
					}
				} else {
					switch p {
					case 0:
						inst = &LD8{A, Contents{BC}}
					case 1:
						inst = &LD8{A, Contents{DE}}
					case 2:
						nn, err := getImmNN(inCh)
						if err == nil {
							inst = &LD16{HL, Contents{nn}}
						} else {
							instErr = err
						}
					case 3:
						nn, err := getImmNN(inCh)
						if err == nil {
							inst = &LD8{A, Contents{nn}}
						} else {
							instErr = err
						}
					}
				}
			case 3:
				if q == 0 {
					inst = &INC16{tableRP[p]}
				} else {
					inst = &DEC16{tableRP[p]}
				}
			case 4:
				inst = &INC8{tableR[y]}
			case 5:
				inst = &DEC8{tableR[y]}
			case 6:
				n, err := getImmN(inCh)
				if err == nil {
					inst = &LD8{tableR[y], n}
				} else {
					instErr = err
				}
			case 7:
				switch y {
				case 0:
					inst = RLCA
				case 1:
					inst = RRCA
				case 2:
					inst = RLA
				case 3:
					inst = RRA
				case 4:
					inst = DAA
				case 5:
					inst = CPL
				case 6:
					inst = SCF
				case 7:
					inst = CCF
				}
			}
		case 1:
			if z == 6 && y == 6 {
				inst = HALT
			} else {
				inst = &LD8{tableR[y], tableR[z]}
			}
		}
		fmt.Printf("D: inst [%v] err [%v]\n", inst, instErr)

		if inst == nil {
			err := instErr
			if err == nil {
				err = fmt.Errorf("TODO - impl %02X [%02X]", n, prefix)
			}
			errCh <- err
		} else {
			iCh <- inst
		}

		prefix = 0
	}
	close(iCh)
	close(errCh)
}

var tableR []Loc8 = []Loc8{B, C, D, E, H, L, Contents{HL}, A}
var tableRP []Loc16 = []Loc16{BC, DE, HL, SP}
var tableRP2 []Loc16 = []Loc16{BC, DE, HL, AF}
var tableCC []Conditional = []Conditional{Not{FT_Z}, FT_Z, Not{FT_C}, FT_C, FT_PO, FT_PE, FT_P, FT_M}

func decomposeByte(n byte) (byte, byte, byte, byte, byte) {
	// We follow terminology from http://www.z80.info/decoding.htm
	// x = the opcode's 1st octal digit (i.e. bits 7-6)
	// y = the opcode's 2nd octal digit (i.e. bits 5-3)
	// z = the opcode's 3rd octal digit (i.e. bits 2-0)
	// p = y rightshifted one position (i.e. bits 5-4)
	// q = y modulo 2 (i.e. bit 3)
	z := n & 0x07
	y := (n >> 3) & 0x07
	x := (n >> 6) & 0x07
	p := y >> 1
	q := y & 0x01

	return x, y, z, p, q
}
<|endoftext|>


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

package inspectors

import (
	"flag"
	"os/exec"
	"strings"
	"time"

	log "github.com/cihub/seelog"
	"github.com/mitchellh/cli"

	inspector "github.com/osrg/namazu/nmz/inspector/proc"
)

type procFlags struct {
	commonFlags
	RootPID       int
	WatchInterval time.Duration
	Cmd           string
}

var (
	procFlagset = flag.NewFlagSet("proc", flag.ExitOnError)
	_procFlags  = procFlags{}
)

func init() {
	initCommon(procFlagset, &_procFlags.commonFlags, "_namazu_proc_inspector")
	procFlagset.IntVar(&_procFlags.RootPID, "pid", -1, "PID for the target process tree")
	procFlagset.DurationVar(&_procFlags.WatchInterval, "watch-interval", 1*time.Second, "Watching interval")
	procFlagset.StringVar(&_procFlags.Cmd, "cmd", "", "Command for target process")
}

type procCmd struct {
}

func ProcCommandFactory() (cli.Command, error) {
	return procCmd{}, nil
}

func (cmd procCmd) Help() string {
	return "Please run `nmz --help inspectors` instead"
}

func (cmd procCmd) Synopsis() string {
	return "Start process inspector"
}

func (cmd procCmd) Run(args []string) int {
	if err := procFlagset.Parse(args); err != nil {
		log.Critical(err)
		return 1
	}

	pid := _procFlags.RootPID
	endCh := make(chan struct{})

	if pid <= 0 {
		if _procFlags.Cmd != "" {
			args := strings.Split(_procFlags.Cmd, " ")
			cmd := exec.Command(args[0], args[1:]...)
			err := cmd.Start()
			if err != nil {
				log.Critical("failed to cmd.Start: %s", err)
				return 1
			}

			pid = cmd.Process.Pid

			go func() {
				err := cmd.Wait()
				if err != nil {
					log.Critical("failed to cmd.Wait: %s", err)
				}
				endCh <- struct{}{}
			}()
		} else {
			log.Critical("pid and command line are not set (or set to non-positive value)")
			return 1
		}
	} else if _procFlags.Cmd != "" {
		log.Critical("you cannot set both pid and command line")
		return 1
	}

	autopilot, err := conditionalStartAutopilotOrchestrator(_procFlags.commonFlags)
	if err != nil {
		log.Critical(err)
		return 1
	}
	log.Infof("Autopilot-mode: %t", autopilot)

	procInspector := &inspector.ProcInspector{
		OrchestratorURL: _procFlags.OrchestratorURL,
		EntityID:        _procFlags.EntityID,
		RootPID:         pid,
		WatchInterval:   _procFlags.WatchInterval,
	}

	if err := procInspector.Serve(endCh); err != nil {
		panic(log.Critical(err))
	}

	// NOTREACHED
	return 0
}
<commit_msg>cli: -stdout and -stderr option for process inspector (#154)<commit_after>// Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package inspectors

import (
	"flag"
	"os"
	"os/exec"
	"strings"
	"time"

	log "github.com/cihub/seelog"
	"github.com/mitchellh/cli"

	inspector "github.com/osrg/namazu/nmz/inspector/proc"
)

type procFlags struct {
	commonFlags
	RootPID       int
	WatchInterval time.Duration
	Cmd           string
	Stdout        string
	Stderr        string
}

var (
	procFlagset = flag.NewFlagSet("proc", flag.ExitOnError)
	_procFlags  = procFlags{}
)

func init() {
	initCommon(procFlagset, &_procFlags.commonFlags, "_namazu_proc_inspector")
	procFlagset.IntVar(&_procFlags.RootPID, "pid", -1, "PID for the target process tree")
	procFlagset.DurationVar(&_procFlags.WatchInterval, "watch-interval", 1*time.Second, "Watching interval")
	procFlagset.StringVar(&_procFlags.Cmd, "cmd", "", "Command for target process")
	procFlagset.StringVar(&_procFlags.Stdout, "stdout", "", "Stdout for target process (used if -cmd option is given)")
	procFlagset.StringVar(&_procFlags.Stderr, "stderr", "", "Stderr for target process (used if -cmd option is given)")
}

type procCmd struct {
}

func ProcCommandFactory() (cli.Command, error) {
	return procCmd{}, nil
}

func (cmd procCmd) Help() string {
	return "Please run `nmz --help inspectors` instead"
}

func (cmd procCmd) Synopsis() string {
	return "Start process inspector"
}

func (cmd procCmd) Run(args []string) int {
	if err := procFlagset.Parse(args); err != nil {
		log.Critical(err)
		return 1
	}

	pid := _procFlags.RootPID
	endCh := make(chan struct{})

	if pid <= 0 {
		if _procFlags.Cmd != "" {
			args := strings.Split(_procFlags.Cmd, " ")
			cmd := exec.Command(args[0], args[1:]...)

			if _procFlags.Stdout == "" {
				cmd.Stdout = os.Stdout
			} else {
				f, err := os.OpenFile(_procFlags.Stdout, os.O_WRONLY|os.O_CREATE, 0622)
				if err != nil {
					log.Critical("failed to open a file %s for stdout: %s", _procFlags.Stdout, err)
					return 1
				}
				cmd.Stdout = f
			}

			if _procFlags.Stderr == "" {
				cmd.Stderr = os.Stderr
			} else {
				f, err := os.OpenFile(_procFlags.Stderr, os.O_WRONLY|os.O_CREATE, 0622)
				if err != nil {
					log.Critical("failed to open a file %s for stderr: %s", _procFlags.Stderr, err)
					return 1
				}
				cmd.Stderr = f
			}

			err := cmd.Start()
			if err != nil {
				log.Critical("failed to cmd.Start: %s", err)
				return 1
			}

			pid = cmd.Process.Pid

			go func() {
				err := cmd.Wait()
				if err != nil {
					log.Critical("failed to cmd.Wait: %s", err)
				}
				endCh <- struct{}{}
			}()
		} else {
			log.Critical("pid and command line are not set (or set to non-positive value)")
			return 1
		}
	} else if _procFlags.Cmd != "" {
		log.Critical("you cannot set both pid and command line")
		return 1
	}

	autopilot, err := conditionalStartAutopilotOrchestrator(_procFlags.commonFlags)
	if err != nil {
		log.Critical(err)
		return 1
	}
	log.Infof("Autopilot-mode: %t", autopilot)

	procInspector := &inspector.ProcInspector{
		OrchestratorURL: _procFlags.OrchestratorURL,
		EntityID:        _procFlags.EntityID,
		RootPID:         pid,
		WatchInterval:   _procFlags.WatchInterval,
	}

	if err := procInspector.Serve(endCh); err != nil {
		panic(log.Critical(err))
	}

	// NOTREACHED
	return 0
}
<|endoftext|>


<commit_before>// Google Image Search functionality
package gis

import (
	"fmt"
	"github.com/collinvandyck/gesture/core"
	"github.com/collinvandyck/gesture/util"
	"math/rand"
	neturl "net/url"
	"strings"
	"time"
)

func Create(bot *core.Gobot, config map[string]interface{}) {
	bot.ListenFor("^gis (.*)", func(msg core.Message, matches []string) core.Response {
		link, err := search(matches[1])
		if err != nil {
			return bot.Error(err)
		}
		msg.Ftfy(link)
		return bot.Stop()
	})
}

// these structs really tie the room together, man
type gisResult struct {
	Url string
}
type gisResponse struct {
	ResponseData struct {
		Results *[]gisResult // use a pointer here b/c sometimes the results are null :(
	}
}

// Search queries google for some images, and then randomly selects one
func search(search string) (string, error) {
	searchUrl := "http://ajax.googleapis.com/ajax/services/search/images?v=1.0&q=" + neturl.QueryEscape(search)
	var gisResponse gisResponse
	if err := util.UnmarshalUrl(searchUrl, &gisResponse); err != nil {
		return "", err
	}

	if gisResponse.ResponseData.Results == nil {
		return "", fmt.Errorf("No results were returned for query %s", search)
	}

	results := *gisResponse.ResponseData.Results

	if len(results) > 0 {

		// start a goroutine to determine image info for each response result
		imageUrlCh := make(chan string)
		errorsCh := make(chan error)
		for _, resultUrl := range results {
			go getImageInfo(resultUrl.Url, imageUrlCh, errorsCh)
		}

		// until a timeout is met, build a collection of urls
		totalResults := len(results)
		remainingResults := totalResults
		urls := make([]string, 0, totalResults)
		errors := make([]error, 0, totalResults)
		timeout := time.After(2 * time.Second)

	SEARCH:
		for remainingResults > 0 {
			select {
			case url := <-imageUrlCh:
				urls = append(urls, url)
				remainingResults--
			case err := <-errorsCh:
				errors = append(errors, err)
				remainingResults--
			case <-timeout:
				break SEARCH
			}
		}
		if len(urls) == 0 {
			return "", fmt.Errorf("No image could be found for \"%s\"", search)
		}
		return urls[rand.Intn(len(urls))], nil

	}
	return "", fmt.Errorf("No image could be found for \"%s\"", search)
}

// getImageInfo looks at the header info for the url, and if it is an image, it sends an imageInfo on the channel
func getImageInfo(url string, ch chan<- string, failures chan<- error) {
	imageUrl, contentType, err := util.ResponseHeaderContentType(url)
	if err == nil && strings.HasPrefix(contentType, "image/") {
		url, err := ensureSuffix(imageUrl, "."+contentType[len("image/"):])
		if err != nil {
			failures <- err
		} else {
			ch <- url
		}
	} else {
		failures <- fmt.Errorf("Not an image: %s", url)
	}
}

// ensureSuffix ensures a url ends with suffixes like .jpg, .png, etc
func ensureSuffix(url, suffix string) (string, error) {
	var err error
	unescapedUrl, err := neturl.QueryUnescape(url)
	if err != nil {
		return "", err
	}
	lowerSuffix := strings.ToLower(suffix)
	lowerUrl := strings.ToLower(unescapedUrl)
	if lowerSuffix == ".jpeg" && strings.HasSuffix(lowerUrl, ".jpg") {
		return url, nil
	}
	if lowerSuffix == ".jpg" && strings.HasSuffix(lowerUrl, ".jpeg") {
		return url, nil
	}
	if strings.HasSuffix(lowerUrl, lowerSuffix) {
		return url, nil
	}
	if strings.Contains(url, "?") {
		return url + "&lol=lol" + suffix, nil
	}
	return url + "?lol=lol" + suffix, nil
}
<commit_msg>Use buffered channels to prevent goroutine leaks<commit_after>// Google Image Search functionality
package gis

import (
	"fmt"
	"github.com/collinvandyck/gesture/core"
	"github.com/collinvandyck/gesture/util"
	"math/rand"
	neturl "net/url"
	"strings"
	"time"
)

func Create(bot *core.Gobot, config map[string]interface{}) {
	bot.ListenFor("^gis (.*)", func(msg core.Message, matches []string) core.Response {
		link, err := search(matches[1])
		if err != nil {
			return bot.Error(err)
		}
		msg.Ftfy(link)
		return bot.Stop()
	})
}

// these structs really tie the room together, man
type gisResult struct {
	Url string
}
type gisResponse struct {
	ResponseData struct {
		Results *[]gisResult // use a pointer here b/c sometimes the results are null :(
	}
}

// Search queries google for some images, and then randomly selects one
func search(search string) (string, error) {
	searchUrl := "http://ajax.googleapis.com/ajax/services/search/images?v=1.0&q=" + neturl.QueryEscape(search)
	var gisResponse gisResponse
	if err := util.UnmarshalUrl(searchUrl, &gisResponse); err != nil {
		return "", err
	}

	if gisResponse.ResponseData.Results == nil {
		return "", fmt.Errorf("No results were returned for query %s", search)
	}

	results := *gisResponse.ResponseData.Results

	if len(results) > 0 {

		// start a goroutine to determine image info for each response result
		// we have to use buffered channels so that the senders don't hang on send after the main method exits
		imageUrlCh := make(chan string, len(results))
		errorsCh := make(chan error, len(results))
		for _, resultUrl := range results {
			go getImageInfo(resultUrl.Url, imageUrlCh, errorsCh)
		}

		// until a timeout is met, build a collection of urls
		totalResults := len(results)
		remainingResults := totalResults
		urls := make([]string, 0, totalResults)
		errors := make([]error, 0, totalResults)
		timeout := time.After(2 * time.Second)

	SEARCH:
		for remainingResults > 0 {
			select {
			case url := <-imageUrlCh:
				urls = append(urls, url)
				remainingResults--
			case err := <-errorsCh:
				errors = append(errors, err)
				remainingResults--
			case <-timeout:
				break SEARCH
			}
		}
		if len(urls) == 0 {
			return "", fmt.Errorf("No image could be found for \"%s\"", search)
		}
		return urls[rand.Intn(len(urls))], nil

	}
	return "", fmt.Errorf("No image could be found for \"%s\"", search)
}

// getImageInfo looks at the header info for the url, and if it is an image, it sends an imageInfo on the channel
func getImageInfo(url string, ch chan<- string, failures chan<- error) {
	imageUrl, contentType, err := util.ResponseHeaderContentType(url)
	if err == nil && strings.HasPrefix(contentType, "image/") {
		url, err := ensureSuffix(imageUrl, "."+contentType[len("image/"):])
		if err != nil {
			failures <- err
		} else {
			ch <- url
		}
	} else {
		failures <- fmt.Errorf("Not an image: %s", url)
	}
}

// ensureSuffix ensures a url ends with suffixes like .jpg, .png, etc
func ensureSuffix(url, suffix string) (string, error) {
	var err error
	unescapedUrl, err := neturl.QueryUnescape(url)
	if err != nil {
		return "", err
	}
	lowerSuffix := strings.ToLower(suffix)
	lowerUrl := strings.ToLower(unescapedUrl)
	if lowerSuffix == ".jpeg" && strings.HasSuffix(lowerUrl, ".jpg") {
		return url, nil
	}
	if lowerSuffix == ".jpg" && strings.HasSuffix(lowerUrl, ".jpeg") {
		return url, nil
	}
	if strings.HasSuffix(lowerUrl, lowerSuffix) {
		return url, nil
	}
	if strings.Contains(url, "?") {
		return url + "&lol=lol" + suffix, nil
	}
	return url + "?lol=lol" + suffix, nil
}
<|endoftext|>


<commit_before>package plugin

import (
	_ "github.com/davecgh/go-spew/spew"
	"github.com/rcrowley/go-metrics"
	. "github.com/talbright/go-curator"
	"github.com/talbright/go-zookeeper/zk"
)

type Metrics struct {
	Registry metrics.Registry
	client   *Client
}

func (p *Metrics) Name() string {
	return "Metrics"
}

func (p *Metrics) Accepts(eventType EventType) bool {
	return true
}

func (p *Metrics) Notify(event Event) {
	p.eventCounter().Inc(1)
	switch event.Type {
	case ConnectionEvent:
		p.metricsForConnection(event)
	case LeaderEventElected:
		fallthrough
	case LeaderEventCandidate:
		fallthrough
	case LeaderEventResigned:
		p.metricsForLeadership(event)
	case MemberEventRegistered:
		fallthrough
	case MemberEventUnregistered:
		p.metricsForMembership(event)
	case DiscoveryEventActive:
		fallthrough
	case DiscoveryEventInactive:
		p.metricsForDiscovery(event)
	case WorkLeaderChangeset:
		fallthrough
	case WorkLeaderActive:
		fallthrough
	case WorkLeaderInactive:
		p.metricsForWorkLeader(event)
	}
}

func (p *Metrics) OnLoad(curator *Curator) {
	if p.Registry == nil {
		p.Registry = metrics.NewPrefixedRegistry("curator.")
	}
	p.client = curator.Client
	p.initMetrics()
}

func (p *Metrics) OnUnload() {}

func (p *Metrics) metricsForWorkLeader(event Event) {
	switch event.Type {
	case WorkLeaderChangeset:
		if event.Data != nil {
			if _, ok := event.Data["worker_added"]; ok {
				p.workLeaderWorkerCount().Inc(1)
			}
			if _, ok := event.Data["worker_removed"]; ok {
				p.workLeaderWorkerCount().Dec(1)
			}
			if _, ok := event.Data["work_added"]; ok {
				if added, ok := event.Data["work_added"].(map[string]Znode); ok {
					p.workLeaderWorkCount().Inc(int64(len(added)))
				}
			}
			if _, ok := event.Data["work_removed"]; ok {
				if removed, ok := event.Data["work_removed"].(map[string]Znode); ok {
					p.workLeaderWorkCount().Dec(int64(len(removed)))
				}
			}
		}
	case WorkLeaderActive:
		p.workLeaderActiveGauge().Update(1)
	case WorkLeaderInactive:
		p.workLeaderActiveGauge().Update(0)
	}
}

func (p *Metrics) metricsForDiscovery(event Event) {
	switch event.Type {
	case DiscoveryEventActive:
		p.discoveryCounter().Inc(1)
	case DiscoveryEventInactive:
		if p.discoveryCounter().Count() > 0 {
			p.discoveryCounter().Dec(1)
		}
	}
}

func (p *Metrics) metricsForMembership(event Event) {
	switch event.Type {
	case MemberEventRegistered:
		p.registeredGauge().Update(1)
	case MemberEventUnregistered:
		p.registeredGauge().Update(0)
	}
}

func (p *Metrics) metricsForLeadership(event Event) {
	switch event.Type {
	case LeaderEventElected:
		p.leaderGauge().Update(1)
	case LeaderEventCandidate:
		fallthrough
	case LeaderEventResigned:
		p.leaderGauge().Update(0)
	}
}

func (p *Metrics) metricsForConnection(event Event) {
	if event.Source != nil {
		switch event.Source.State {
		case zk.StateHasSession:
			p.sessionGauge().Update(1)
		case zk.StateConnecting:
			p.sessionGauge().Update(0)
			p.connectingCounter().Inc(1)
		case zk.StateUnknown:
			break
		default:
			p.sessionGauge().Update(0)
		}
	}
}

func (p *Metrics) initMetrics() {
	p.workLeaderActiveGauge().Update(0)
	p.workLeaderWorkerCount().Clear()
	p.workLeaderWorkCount().Clear()
	p.discoveryCounter().Clear()
	p.eventCounter().Clear()
	p.registeredGauge().Update(0)
	p.leaderGauge().Update(0)
	p.sessionGauge().Update(0)
	p.connectingCounter().Clear()
}

func (p *Metrics) workLeaderActiveGauge() metrics.Gauge {
	return metrics.GetOrRegisterGauge("work_leader.active", p.Registry)
}

func (p *Metrics) workLeaderWorkerCount() metrics.Counter {
	return metrics.GetOrRegisterCounter("work_leader.workers", p.Registry)
}

func (p *Metrics) workLeaderWorkCount() metrics.Counter {
	return metrics.GetOrRegisterCounter("work_leader.work", p.Registry)
}

func (p *Metrics) discoveryCounter() metrics.Counter {
	return metrics.GetOrRegisterCounter("discovery.discovered", p.Registry)
}

func (p *Metrics) eventCounter() metrics.Counter {
	return metrics.GetOrRegisterCounter("events", p.Registry)
}

func (p *Metrics) registeredGauge() metrics.Gauge {
	return metrics.GetOrRegisterGauge("member.registered", p.Registry)
}

func (p *Metrics) leaderGauge() metrics.Gauge {
	return metrics.GetOrRegisterGauge("leader.elected", p.Registry)
}

func (p *Metrics) sessionGauge() metrics.Gauge {
	return metrics.GetOrRegisterGauge("connection.session", p.Registry)
}

func (p *Metrics) connectingCounter() metrics.Counter {
	return metrics.GetOrRegisterCounter("connection.attempts", p.Registry)
}
<commit_msg>Tweak metrics<commit_after>package plugin

import (
	_ "github.com/davecgh/go-spew/spew"
	"github.com/rcrowley/go-metrics"
	. "github.com/talbright/go-curator"
	"github.com/talbright/go-zookeeper/zk"
)

type Metrics struct {
	Registry metrics.Registry
	client   *Client
}

func (p *Metrics) Name() string {
	return "Metrics"
}

func (p *Metrics) Accepts(eventType EventType) bool {
	return true
}

func (p *Metrics) Notify(event Event) {
	p.eventMeter().Mark(1)
	switch event.Type {
	case ConnectionEvent:
		p.metricsForConnection(event)
	case LeaderEventElected:
		fallthrough
	case LeaderEventCandidate:
		fallthrough
	case LeaderEventResigned:
		p.metricsForLeadership(event)
	case MemberEventRegistered:
		fallthrough
	case MemberEventUnregistered:
		p.metricsForMembership(event)
	case DiscoveryEventActive:
		fallthrough
	case DiscoveryEventInactive:
		p.metricsForDiscovery(event)
	case WorkLeaderChangeset:
		fallthrough
	case WorkLeaderActive:
		fallthrough
	case WorkLeaderInactive:
		p.metricsForWorkLeader(event)
	}
}

func (p *Metrics) OnLoad(curator *Curator) {
	if p.Registry == nil {
		p.Registry = metrics.NewPrefixedRegistry("curator.")
	}
	p.client = curator.Client
}

func (p *Metrics) OnUnload() {}

func (p *Metrics) metricsForWorkLeader(event Event) {
	switch event.Type {
	case WorkLeaderChangeset:
		if event.Data != nil {
			if _, ok := event.Data["worker_added"]; ok {
				p.workLeaderWorkerCount().Inc(1)
			}
			if _, ok := event.Data["worker_removed"]; ok {
				p.workLeaderWorkerCount().Dec(1)
			}
			if _, ok := event.Data["work_added"]; ok {
				if added, ok := event.Data["work_added"].(map[string]Znode); ok {
					p.workLeaderWorkCount().Inc(int64(len(added)))
				}
			}
			if _, ok := event.Data["work_removed"]; ok {
				if removed, ok := event.Data["work_removed"].(map[string]Znode); ok {
					p.workLeaderWorkCount().Dec(int64(len(removed)))
				}
			}
		}
	case WorkLeaderActive:
		p.workLeaderActiveGauge().Update(1)
	case WorkLeaderInactive:
		p.workLeaderActiveGauge().Update(0)
	}
}

func (p *Metrics) metricsForDiscovery(event Event) {
	switch event.Type {
	case DiscoveryEventActive:
		p.discoveryCounter().Inc(1)
	case DiscoveryEventInactive:
		if p.discoveryCounter().Count() > 0 {
			p.discoveryCounter().Dec(1)
		}
	}
}

func (p *Metrics) metricsForMembership(event Event) {
	switch event.Type {
	case MemberEventRegistered:
		p.registeredGauge().Update(1)
	case MemberEventUnregistered:
		p.registeredGauge().Update(0)
	}
}

func (p *Metrics) metricsForLeadership(event Event) {
	switch event.Type {
	case LeaderEventElected:
		p.leaderGauge().Update(1)
	case LeaderEventCandidate:
		fallthrough
	case LeaderEventResigned:
		p.leaderGauge().Update(0)
	}
}

func (p *Metrics) metricsForConnection(event Event) {
	if event.Source != nil {
		switch event.Source.State {
		case zk.StateHasSession:
			p.sessionGauge().Update(1)
		case zk.StateConnecting:
			p.sessionGauge().Update(0)
			p.connectingMeter().Mark(1)
		case zk.StateUnknown:
			break
		default:
			p.sessionGauge().Update(0)
		}
	}
}

func (p *Metrics) workLeaderActiveGauge() metrics.Gauge {
	return metrics.GetOrRegisterGauge("work_leader.active", p.Registry)
}

func (p *Metrics) workLeaderWorkerCount() metrics.Counter {
	return metrics.GetOrRegisterCounter("work_leader.workers", p.Registry)
}

func (p *Metrics) workLeaderWorkCount() metrics.Counter {
	return metrics.GetOrRegisterCounter("work_leader.work", p.Registry)
}

func (p *Metrics) discoveryCounter() metrics.Counter {
	return metrics.GetOrRegisterCounter("discovery.discovered", p.Registry)
}

func (p *Metrics) eventMeter() metrics.Meter {
	return metrics.GetOrRegisterMeter("events", p.Registry)
}

func (p *Metrics) registeredGauge() metrics.Gauge {
	return metrics.GetOrRegisterGauge("member.registered", p.Registry)
}

func (p *Metrics) leaderGauge() metrics.Gauge {
	return metrics.GetOrRegisterGauge("leader.elected", p.Registry)
}

func (p *Metrics) sessionGauge() metrics.Gauge {
	return metrics.GetOrRegisterGauge("connection.session", p.Registry)
}

func (p *Metrics) connectingMeter() metrics.Meter {
	return metrics.GetOrRegisterMeter("connection.attempts", p.Registry)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package fuzzer

import (
	"fmt"
	"math/rand"
	"sort"
	"strconv"
	"strings"

	"github.com/google/gofuzz"

	"k8s.io/apimachinery/pkg/api/resource"
	apitesting "k8s.io/apimachinery/pkg/api/testing"
	"k8s.io/apimachinery/pkg/api/testing/fuzzer"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
	"k8s.io/apimachinery/pkg/runtime"
	runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/apimachinery/pkg/types"
)

func genericFuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} {
	return []interface{}{
		func(q *resource.Quantity, c fuzz.Continue) {
			*q = *resource.NewQuantity(c.Int63n(1000), resource.DecimalExponent)
		},
		func(j *int, c fuzz.Continue) {
			*j = int(c.Int31())
		},
		func(j **int, c fuzz.Continue) {
			if c.RandBool() {
				i := int(c.Int31())
				*j = &i
			} else {
				*j = nil
			}
		},
		func(j *runtime.TypeMeta, c fuzz.Continue) {
			// We have to customize the randomization of TypeMetas because their
			// APIVersion and Kind must remain blank in memory.
			j.APIVersion = ""
			j.Kind = ""
		},
		func(j *runtime.Object, c fuzz.Continue) {
			// TODO: uncomment when round trip starts from a versioned object
			if true { //c.RandBool() {
				*j = &runtime.Unknown{
					// We do not set TypeMeta here because it is not carried through a round trip
					Raw:         []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`),
					ContentType: runtime.ContentTypeJSON,
				}
			} else {
				types := []runtime.Object{&metav1.Status{}, &metav1.APIGroup{}}
				t := types[c.Rand.Intn(len(types))]
				c.Fuzz(t)
				*j = t
			}
		},
		func(r *runtime.RawExtension, c fuzz.Continue) {
			// Pick an arbitrary type and fuzz it
			types := []runtime.Object{&metav1.Status{}, &metav1.APIGroup{}}
			obj := types[c.Rand.Intn(len(types))]
			c.Fuzz(obj)

			// Find a codec for converting the object to raw bytes.  This is necessary for the
			// api version and kind to be correctly set be serialization.
			var codec = apitesting.TestCodec(codecs, metav1.SchemeGroupVersion)

			// Convert the object to raw bytes
			bytes, err := runtime.Encode(codec, obj)
			if err != nil {
				panic(fmt.Sprintf("Failed to encode object: %v", err))
			}

			// strip trailing newlines which do not survive roundtrips
			for len(bytes) >= 1 && bytes[len(bytes)-1] == 10 {
				bytes = bytes[:len(bytes)-1]
			}

			// Set the bytes field on the RawExtension
			r.Raw = bytes
		},
	}
}

// taken from gofuzz internals for RandString
type charRange struct {
	first, last rune
}

func (c *charRange) choose(r *rand.Rand) rune {
	count := int64(c.last - c.first + 1)
	ch := c.first + rune(r.Int63n(count))

	return ch
}

// randomLabelPart produces a valid random label value or name-part
// of a label key.
func randomLabelPart(c fuzz.Continue, canBeEmpty bool) string {
	validStartEnd := []charRange{{'0', '9'}, {'a', 'z'}, {'A', 'Z'}}
	validMiddle := []charRange{{'0', '9'}, {'a', 'z'}, {'A', 'Z'},
		{'.', '.'}, {'-', '-'}, {'_', '_'}}

	partLen := c.Rand.Intn(64) // len is [0, 63]
	if !canBeEmpty {
		partLen = c.Rand.Intn(63) + 1 // len is [1, 63]
	}

	runes := make([]rune, partLen)
	if partLen == 0 {
		return string(runes)
	}

	runes[0] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand)
	for i := range runes[1:] {
		runes[i+1] = validMiddle[c.Rand.Intn(len(validMiddle))].choose(c.Rand)
	}
	runes[len(runes)-1] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand)

	return string(runes)
}

func randomDNSLabel(c fuzz.Continue) string {
	validStartEnd := []charRange{{'0', '9'}, {'a', 'z'}}
	validMiddle := []charRange{{'0', '9'}, {'a', 'z'}, {'-', '-'}}

	partLen := c.Rand.Intn(63) + 1 // len is [1, 63]
	runes := make([]rune, partLen)

	runes[0] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand)
	for i := range runes[1:] {
		runes[i+1] = validMiddle[c.Rand.Intn(len(validMiddle))].choose(c.Rand)
	}
	runes[len(runes)-1] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand)

	return string(runes)
}

func randomLabelKey(c fuzz.Continue) string {
	namePart := randomLabelPart(c, false)
	prefixPart := ""

	usePrefix := c.RandBool()
	if usePrefix {
		// we can fit, with dots, at most 3 labels in the 253 allotted characters
		prefixPartsLen := c.Rand.Intn(2) + 1
		prefixParts := make([]string, prefixPartsLen)
		for i := range prefixParts {
			prefixParts[i] = randomDNSLabel(c)
		}
		prefixPart = strings.Join(prefixParts, ".") + "/"
	}

	return prefixPart + namePart
}

func v1FuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} {

	return []interface{}{
		func(j *metav1.TypeMeta, c fuzz.Continue) {
			// We have to customize the randomization of TypeMetas because their
			// APIVersion and Kind must remain blank in memory.
			j.APIVersion = ""
			j.Kind = ""
		},
		func(j *metav1.ObjectMeta, c fuzz.Continue) {
			j.Name = c.RandString()
			j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10)
			j.SelfLink = c.RandString()
			j.UID = types.UID(c.RandString())
			j.GenerateName = c.RandString()

			var sec, nsec int64
			c.Fuzz(&sec)
			c.Fuzz(&nsec)
			j.CreationTimestamp = metav1.Unix(sec, nsec).Rfc3339Copy()
		},
		func(j *metav1.ListMeta, c fuzz.Continue) {
			j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10)
			j.SelfLink = c.RandString()
		},
		func(j *metav1.LabelSelector, c fuzz.Continue) {
			c.FuzzNoCustom(j)
			// we can't have an entirely empty selector, so force
			// use of MatchExpression if necessary
			if len(j.MatchLabels) == 0 && len(j.MatchExpressions) == 0 {
				j.MatchExpressions = make([]metav1.LabelSelectorRequirement, c.Rand.Intn(2)+1)
			}

			if j.MatchLabels != nil {
				fuzzedMatchLabels := make(map[string]string, len(j.MatchLabels))
				for i := 0; i < len(j.MatchLabels); i++ {
					fuzzedMatchLabels[randomLabelKey(c)] = randomLabelPart(c, true)
				}
				j.MatchLabels = fuzzedMatchLabels
			}

			validOperators := []metav1.LabelSelectorOperator{
				metav1.LabelSelectorOpIn,
				metav1.LabelSelectorOpNotIn,
				metav1.LabelSelectorOpExists,
				metav1.LabelSelectorOpDoesNotExist,
			}

			if j.MatchExpressions != nil {
				// NB: the label selector parser code sorts match expressions by key, and sorts the values,
				// so we need to make sure ours are sorted as well here to preserve round-trip comparison.
				// In practice, not sorting doesn't hurt anything...

				for i := range j.MatchExpressions {
					req := metav1.LabelSelectorRequirement{}
					c.Fuzz(&req)
					req.Key = randomLabelKey(c)
					req.Operator = validOperators[c.Rand.Intn(len(validOperators))]
					if req.Operator == metav1.LabelSelectorOpIn || req.Operator == metav1.LabelSelectorOpNotIn {
						if len(req.Values) == 0 {
							// we must have some values here, so randomly choose a short length
							req.Values = make([]string, c.Rand.Intn(2)+1)
						}
						for i := range req.Values {
							req.Values[i] = randomLabelPart(c, true)
						}
						sort.Strings(req.Values)
					} else {
						req.Values = nil
					}
					j.MatchExpressions[i] = req
				}

				sort.Slice(j.MatchExpressions, func(a, b int) bool { return j.MatchExpressions[a].Key < j.MatchExpressions[b].Key })
			}
		},
	}
}

func v1alpha1FuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} {
	return []interface{}{
		func(r *metav1beta1.TableRow, c fuzz.Continue) {
			c.Fuzz(&r.Object)
			c.Fuzz(&r.Conditions)
			if len(r.Conditions) == 0 {
				r.Conditions = nil
			}
			n := c.Intn(10)
			if n > 0 {
				r.Cells = make([]interface{}, n)
			}
			for i := range r.Cells {
				t := c.Intn(6)
				switch t {
				case 0:
					r.Cells[i] = c.RandString()
				case 1:
					r.Cells[i] = c.Int63()
				case 2:
					r.Cells[i] = c.RandBool()
				case 3:
					x := map[string]interface{}{}
					for j := c.Intn(10) + 1; j >= 0; j-- {
						x[c.RandString()] = c.RandString()
					}
					r.Cells[i] = x
				case 4:
					x := make([]interface{}, c.Intn(10))
					for i := range x {
						x[i] = c.Int63()
					}
					r.Cells[i] = x
				default:
					r.Cells[i] = nil
				}
			}
		},
	}
}

var Funcs = fuzzer.MergeFuzzerFuncs(
	genericFuzzerFuncs,
	v1FuzzerFuncs,
	v1alpha1FuzzerFuncs,
)
<commit_msg>apiextensions: add ObjectMeta schema validation and pruning<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package fuzzer

import (
	"fmt"
	"math/rand"
	"sort"
	"strconv"
	"strings"

	"github.com/google/gofuzz"

	"k8s.io/apimachinery/pkg/api/resource"
	apitesting "k8s.io/apimachinery/pkg/api/testing"
	"k8s.io/apimachinery/pkg/api/testing/fuzzer"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
	"k8s.io/apimachinery/pkg/runtime"
	runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/apimachinery/pkg/types"
)

func genericFuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} {
	return []interface{}{
		func(q *resource.Quantity, c fuzz.Continue) {
			*q = *resource.NewQuantity(c.Int63n(1000), resource.DecimalExponent)
		},
		func(j *int, c fuzz.Continue) {
			*j = int(c.Int31())
		},
		func(j **int, c fuzz.Continue) {
			if c.RandBool() {
				i := int(c.Int31())
				*j = &i
			} else {
				*j = nil
			}
		},
		func(j *runtime.TypeMeta, c fuzz.Continue) {
			// We have to customize the randomization of TypeMetas because their
			// APIVersion and Kind must remain blank in memory.
			j.APIVersion = ""
			j.Kind = ""
		},
		func(j *runtime.Object, c fuzz.Continue) {
			// TODO: uncomment when round trip starts from a versioned object
			if true { //c.RandBool() {
				*j = &runtime.Unknown{
					// We do not set TypeMeta here because it is not carried through a round trip
					Raw:         []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`),
					ContentType: runtime.ContentTypeJSON,
				}
			} else {
				types := []runtime.Object{&metav1.Status{}, &metav1.APIGroup{}}
				t := types[c.Rand.Intn(len(types))]
				c.Fuzz(t)
				*j = t
			}
		},
		func(r *runtime.RawExtension, c fuzz.Continue) {
			// Pick an arbitrary type and fuzz it
			types := []runtime.Object{&metav1.Status{}, &metav1.APIGroup{}}
			obj := types[c.Rand.Intn(len(types))]
			c.Fuzz(obj)

			// Find a codec for converting the object to raw bytes.  This is necessary for the
			// api version and kind to be correctly set be serialization.
			var codec = apitesting.TestCodec(codecs, metav1.SchemeGroupVersion)

			// Convert the object to raw bytes
			bytes, err := runtime.Encode(codec, obj)
			if err != nil {
				panic(fmt.Sprintf("Failed to encode object: %v", err))
			}

			// strip trailing newlines which do not survive roundtrips
			for len(bytes) >= 1 && bytes[len(bytes)-1] == 10 {
				bytes = bytes[:len(bytes)-1]
			}

			// Set the bytes field on the RawExtension
			r.Raw = bytes
		},
	}
}

// taken from gofuzz internals for RandString
type charRange struct {
	first, last rune
}

func (c *charRange) choose(r *rand.Rand) rune {
	count := int64(c.last - c.first + 1)
	ch := c.first + rune(r.Int63n(count))

	return ch
}

// randomLabelPart produces a valid random label value or name-part
// of a label key.
func randomLabelPart(c fuzz.Continue, canBeEmpty bool) string {
	validStartEnd := []charRange{{'0', '9'}, {'a', 'z'}, {'A', 'Z'}}
	validMiddle := []charRange{{'0', '9'}, {'a', 'z'}, {'A', 'Z'},
		{'.', '.'}, {'-', '-'}, {'_', '_'}}

	partLen := c.Rand.Intn(64) // len is [0, 63]
	if !canBeEmpty {
		partLen = c.Rand.Intn(63) + 1 // len is [1, 63]
	}

	runes := make([]rune, partLen)
	if partLen == 0 {
		return string(runes)
	}

	runes[0] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand)
	for i := range runes[1:] {
		runes[i+1] = validMiddle[c.Rand.Intn(len(validMiddle))].choose(c.Rand)
	}
	runes[len(runes)-1] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand)

	return string(runes)
}

func randomDNSLabel(c fuzz.Continue) string {
	validStartEnd := []charRange{{'0', '9'}, {'a', 'z'}}
	validMiddle := []charRange{{'0', '9'}, {'a', 'z'}, {'-', '-'}}

	partLen := c.Rand.Intn(63) + 1 // len is [1, 63]
	runes := make([]rune, partLen)

	runes[0] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand)
	for i := range runes[1:] {
		runes[i+1] = validMiddle[c.Rand.Intn(len(validMiddle))].choose(c.Rand)
	}
	runes[len(runes)-1] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand)

	return string(runes)
}

func randomLabelKey(c fuzz.Continue) string {
	namePart := randomLabelPart(c, false)
	prefixPart := ""

	usePrefix := c.RandBool()
	if usePrefix {
		// we can fit, with dots, at most 3 labels in the 253 allotted characters
		prefixPartsLen := c.Rand.Intn(2) + 1
		prefixParts := make([]string, prefixPartsLen)
		for i := range prefixParts {
			prefixParts[i] = randomDNSLabel(c)
		}
		prefixPart = strings.Join(prefixParts, ".") + "/"
	}

	return prefixPart + namePart
}

func v1FuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} {

	return []interface{}{
		func(j *metav1.TypeMeta, c fuzz.Continue) {
			// We have to customize the randomization of TypeMetas because their
			// APIVersion and Kind must remain blank in memory.
			j.APIVersion = ""
			j.Kind = ""
		},
		func(j *metav1.ObjectMeta, c fuzz.Continue) {
			c.FuzzNoCustom(j)

			j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10)
			j.UID = types.UID(c.RandString())

			var sec, nsec int64
			c.Fuzz(&sec)
			c.Fuzz(&nsec)
			j.CreationTimestamp = metav1.Unix(sec, nsec).Rfc3339Copy()

			if j.DeletionTimestamp != nil {
				c.Fuzz(&sec)
				c.Fuzz(&nsec)
				t := metav1.Unix(sec, nsec).Rfc3339Copy()
				j.DeletionTimestamp = &t
			}

			if len(j.Labels) == 0 {
				j.Labels = nil
			}
			if len(j.Annotations) == 0 {
				j.Annotations = nil
			}
			if len(j.OwnerReferences) == 0 {
				j.OwnerReferences = nil
			}
			if len(j.Finalizers) == 0 {
				j.Finalizers = nil
			}
		},
		func(j *metav1.Initializers, c fuzz.Continue) {
			c.FuzzNoCustom(j)
			if len(j.Pending) == 0 {
				j.Pending = nil
			}
		},
		func(j *metav1.ListMeta, c fuzz.Continue) {
			j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10)
			j.SelfLink = c.RandString()
		},
		func(j *metav1.LabelSelector, c fuzz.Continue) {
			c.FuzzNoCustom(j)
			// we can't have an entirely empty selector, so force
			// use of MatchExpression if necessary
			if len(j.MatchLabels) == 0 && len(j.MatchExpressions) == 0 {
				j.MatchExpressions = make([]metav1.LabelSelectorRequirement, c.Rand.Intn(2)+1)
			}

			if j.MatchLabels != nil {
				fuzzedMatchLabels := make(map[string]string, len(j.MatchLabels))
				for i := 0; i < len(j.MatchLabels); i++ {
					fuzzedMatchLabels[randomLabelKey(c)] = randomLabelPart(c, true)
				}
				j.MatchLabels = fuzzedMatchLabels
			}

			validOperators := []metav1.LabelSelectorOperator{
				metav1.LabelSelectorOpIn,
				metav1.LabelSelectorOpNotIn,
				metav1.LabelSelectorOpExists,
				metav1.LabelSelectorOpDoesNotExist,
			}

			if j.MatchExpressions != nil {
				// NB: the label selector parser code sorts match expressions by key, and sorts the values,
				// so we need to make sure ours are sorted as well here to preserve round-trip comparison.
				// In practice, not sorting doesn't hurt anything...

				for i := range j.MatchExpressions {
					req := metav1.LabelSelectorRequirement{}
					c.Fuzz(&req)
					req.Key = randomLabelKey(c)
					req.Operator = validOperators[c.Rand.Intn(len(validOperators))]
					if req.Operator == metav1.LabelSelectorOpIn || req.Operator == metav1.LabelSelectorOpNotIn {
						if len(req.Values) == 0 {
							// we must have some values here, so randomly choose a short length
							req.Values = make([]string, c.Rand.Intn(2)+1)
						}
						for i := range req.Values {
							req.Values[i] = randomLabelPart(c, true)
						}
						sort.Strings(req.Values)
					} else {
						req.Values = nil
					}
					j.MatchExpressions[i] = req
				}

				sort.Slice(j.MatchExpressions, func(a, b int) bool { return j.MatchExpressions[a].Key < j.MatchExpressions[b].Key })
			}
		},
	}
}

func v1alpha1FuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} {
	return []interface{}{
		func(r *metav1beta1.TableRow, c fuzz.Continue) {
			c.Fuzz(&r.Object)
			c.Fuzz(&r.Conditions)
			if len(r.Conditions) == 0 {
				r.Conditions = nil
			}
			n := c.Intn(10)
			if n > 0 {
				r.Cells = make([]interface{}, n)
			}
			for i := range r.Cells {
				t := c.Intn(6)
				switch t {
				case 0:
					r.Cells[i] = c.RandString()
				case 1:
					r.Cells[i] = c.Int63()
				case 2:
					r.Cells[i] = c.RandBool()
				case 3:
					x := map[string]interface{}{}
					for j := c.Intn(10) + 1; j >= 0; j-- {
						x[c.RandString()] = c.RandString()
					}
					r.Cells[i] = x
				case 4:
					x := make([]interface{}, c.Intn(10))
					for i := range x {
						x[i] = c.Int63()
					}
					r.Cells[i] = x
				default:
					r.Cells[i] = nil
				}
			}
		},
	}
}

var Funcs = fuzzer.MergeFuzzerFuncs(
	genericFuzzerFuncs,
	v1FuzzerFuncs,
	v1alpha1FuzzerFuncs,
)
<|endoftext|>


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

package fuse

import (
	"errors"

	"github.com/jacobsa/bazilfuse"
	"golang.org/x/net/context"
)

// A struct representing the status of a mount operation, with methods for
// waiting on the mount to complete, waiting for unmounting, and causing
// unmounting.
type MountedFileSystem struct {
	dir string

	// The result to return from WaitForReady. Not valid until the channel is
	// closed.
	readyStatus          error
	readyStatusAvailable chan struct{}

	// The result to return from Join. Not valid until the channel is closed.
	joinStatus          error
	joinStatusAvailable chan struct{}
}

// Return the directory on which the file system is mounted (or where we
// attempted to mount it.)
func (mfs *MountedFileSystem) Dir() string {
	return mfs.dir
}

// Wait until the mount point is ready to be used. After a successful return
// from this function, the contents of the mounted file system should be
// visible in the directory supplied to NewMountPoint. May be called multiple
// times.
func (mfs *MountedFileSystem) WaitForReady(ctx context.Context) error {
	select {
	case <-mfs.readyStatusAvailable:
		return mfs.readyStatus
	case <-ctx.Done():
		return ctx.Err()
	}
}

// Block until a mounted file system has been unmounted. The return value will
// be non-nil if anything unexpected happened while serving. May be called
// multiple times. Must not be called unless WaitForReady has returned nil.
func (mfs *MountedFileSystem) Join(ctx context.Context) error {
	select {
	case <-mfs.joinStatusAvailable:
		return mfs.joinStatus
	case <-ctx.Done():
		return ctx.Err()
	}
}

// Attempt to unmount the file system. Use Join to wait for it to actually be
// unmounted. You must first call WaitForReady to ensure there is no race with
// mounting.
func (mfs *MountedFileSystem) Unmount() error {
	return bazilfuse.Unmount(mfs.dir)
}

// Runs in the background.
func (mfs *MountedFileSystem) mountAndServe(
	server *server,
	options []bazilfuse.MountOption) {
	logger := getLogger()

	// Open a FUSE connection.
	logger.Println("Opening a FUSE connection.")
	c, err := bazilfuse.Mount(mfs.dir, options...)
	if err != nil {
		mfs.readyStatus = errors.New("bazilfuse.Mount: " + err.Error())
		close(mfs.readyStatusAvailable)
		return
	}

	defer c.Close()

	// Start a goroutine that will notify the MountedFileSystem object when the
	// connection says it is ready (or it fails to become ready).
	go func() {
		logger.Println("Waiting for the FUSE connection to be ready.")
		<-c.Ready
		logger.Println("The FUSE connection is ready.")

		mfs.readyStatus = c.MountError
		close(mfs.readyStatusAvailable)
	}()

	// Serve the connection using the file system object.
	logger.Println("Serving the FUSE connection.")
	if err := server.Serve(c); err != nil {
		mfs.joinStatus = errors.New("Serve: " + err.Error())
		close(mfs.joinStatusAvailable)
		return
	}

	// Signal that everything is okay.
	close(mfs.joinStatusAvailable)
}

// Attempt to mount the supplied file system on the given directory.
// mfs.WaitForReady() must be called to find out whether the mount was
// successful.
func Mount(
	dir string,
	fs FileSystem,
	options ...bazilfuse.MountOption) (mfs *MountedFileSystem, err error) {
	// Create a server object.
	server, err := newServer(fs)
	if err != nil {
		return
	}

	// Initialize the struct.
	mfs = &MountedFileSystem{
		dir:                  dir,
		readyStatusAvailable: make(chan struct{}),
		joinStatusAvailable:  make(chan struct{}),
	}

	// Mount in the background.
	go mfs.mountAndServe(server, options)

	return
}
<commit_msg>Reworked the public interface for mount options.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fuse

import (
	"errors"

	"github.com/jacobsa/bazilfuse"
	"golang.org/x/net/context"
)

// A struct representing the status of a mount operation, with methods for
// waiting on the mount to complete, waiting for unmounting, and causing
// unmounting.
type MountedFileSystem struct {
	dir string

	// The result to return from WaitForReady. Not valid until the channel is
	// closed.
	readyStatus          error
	readyStatusAvailable chan struct{}

	// The result to return from Join. Not valid until the channel is closed.
	joinStatus          error
	joinStatusAvailable chan struct{}
}

// Return the directory on which the file system is mounted (or where we
// attempted to mount it.)
func (mfs *MountedFileSystem) Dir() string {
	return mfs.dir
}

// Wait until the mount point is ready to be used. After a successful return
// from this function, the contents of the mounted file system should be
// visible in the directory supplied to NewMountPoint. May be called multiple
// times.
func (mfs *MountedFileSystem) WaitForReady(ctx context.Context) error {
	select {
	case <-mfs.readyStatusAvailable:
		return mfs.readyStatus
	case <-ctx.Done():
		return ctx.Err()
	}
}

// Block until a mounted file system has been unmounted. The return value will
// be non-nil if anything unexpected happened while serving. May be called
// multiple times. Must not be called unless WaitForReady has returned nil.
func (mfs *MountedFileSystem) Join(ctx context.Context) error {
	select {
	case <-mfs.joinStatusAvailable:
		return mfs.joinStatus
	case <-ctx.Done():
		return ctx.Err()
	}
}

// Attempt to unmount the file system. Use Join to wait for it to actually be
// unmounted. You must first call WaitForReady to ensure there is no race with
// mounting.
func (mfs *MountedFileSystem) Unmount() error {
	return bazilfuse.Unmount(mfs.dir)
}

// Runs in the background.
func (mfs *MountedFileSystem) mountAndServe(
	server *server,
	options []bazilfuse.MountOption) {
	logger := getLogger()

	// Open a FUSE connection.
	logger.Println("Opening a FUSE connection.")
	c, err := bazilfuse.Mount(mfs.dir, options...)
	if err != nil {
		mfs.readyStatus = errors.New("bazilfuse.Mount: " + err.Error())
		close(mfs.readyStatusAvailable)
		return
	}

	defer c.Close()

	// Start a goroutine that will notify the MountedFileSystem object when the
	// connection says it is ready (or it fails to become ready).
	go func() {
		logger.Println("Waiting for the FUSE connection to be ready.")
		<-c.Ready
		logger.Println("The FUSE connection is ready.")

		mfs.readyStatus = c.MountError
		close(mfs.readyStatusAvailable)
	}()

	// Serve the connection using the file system object.
	logger.Println("Serving the FUSE connection.")
	if err := server.Serve(c); err != nil {
		mfs.joinStatus = errors.New("Serve: " + err.Error())
		close(mfs.joinStatusAvailable)
		return
	}

	// Signal that everything is okay.
	close(mfs.joinStatusAvailable)
}

// Optional configuration accepted by Mount.
type MountConfig struct {
}

// Convert to mount options to be passed to package bazilfuse.
func (c *MountConfig) bazilfuseOptions() []bazilfuse.MountOption

// Attempt to mount the supplied file system on the given directory.
// mfs.WaitForReady() must be called to find out whether the mount was
// successful.
func Mount(
	dir string,
	fs FileSystem,
	config *MountConfig) (mfs *MountedFileSystem, err error) {
	// Create a server object.
	server, err := newServer(fs)
	if err != nil {
		return
	}

	// Initialize the struct.
	mfs = &MountedFileSystem{
		dir:                  dir,
		readyStatusAvailable: make(chan struct{}),
		joinStatusAvailable:  make(chan struct{}),
	}

	// Mount in the background.
	go mfs.mountAndServe(server, config.bazilfuseOptions())

	return
}
<|endoftext|>


<commit_before>package bot

import (
	"strings"

	"github.com/charliemaiors/golang-wol/types"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/viper"
	tgbotapi "gopkg.in/telegram-bot-api.v4"
)

var (
	deviceChan       chan *types.AliasResponse
	getChan          chan *types.GetDev
	delDevChan       chan *types.DelDev
	passHandlingChan chan *types.PasswordHandling
	updatePassChan   chan *types.PasswordUpdate
	getAliases       chan chan string
	allowedUser      *tgbotapi.User
	bot              *tgbotapi.BotAPI
)

func RunBot(deviceChan chan *types.AliasResponse, getChan chan *types.GetDev, delDevChan chan *types.DelDev, passHandlingChan chan *types.PasswordHandling, updatePassChan chan *types.PasswordUpdate, getAliases chan chan string) {
	initBot(deviceChan, getChan, delDevChan, passHandlingChan, updatePassChan, getAliases)
	var err error
	token := viper.GetString("bot.telegram.token")
	bot, err = tgbotapi.NewBotAPI(token)
	if err != nil {
		panic(err)
	}

	log.Debugf("Authorized bot %s", bot.Self.String())
	bot.Debug = true
	u := tgbotapi.NewUpdate(0)
	u.Timeout = 60

	updates, err := bot.GetUpdatesChan(u)
	if err != nil {
		log.Errorf("Could not get updates, %v error", err)
		return
	}

	for update := range updates {
		if update.Message == nil {
			continue
		}
		go handleUpdate(update)

	}
}

func initBot(deviceChanDef chan *types.AliasResponse, getChanDef chan *types.GetDev, delDevChanDef chan *types.DelDev, passHandlingChanDef chan *types.PasswordHandling, updatePassChanDef chan *types.PasswordUpdate, getAliasesDef chan chan string) {
	deviceChan = deviceChanDef
	getChan = getChanDef
	delDevChan = delDevChanDef
	passHandlingChan = passHandlingChanDef
	updatePassChan = updatePassChanDef
	getAliases = getAliasesDef

	allowedUser = &tgbotapi.User{
		FirstName: viper.GetString("bot.telegram.firstname"),
		LastName:  viper.GetString("bot.telegram.lastname"),
		UserName:  viper.GetString("bot.telegram.username"),
		IsBot:     false,
	}
}

func handleUpdate(update tgbotapi.Update) {
	var msg tgbotapi.MessageConfig
	if !checkValidUser(update.Message.From) {
		msg := tgbotapi.NewMessage(update.Message.Chat.ID, "You are not allowed to chat with this bot")
	}
	if update.Message.IsCommand() {
		switch update.Message.Command() {
		case "list":
			devices := getAllDevices()
			msg := tgbotapi.NewMessage(update.Message.Chat.ID, "You have registered this devices: "+devices)
		case "add":
			args := update.Message.CommandArguments()
			resp := deviceAdd(args)
		}

	}

	msg.ReplyToMessageID = update.Message.MessageID
	_, err := bot.Send(msg)
	if err != nil {
		log.Errorf("Got error while sending answer %v", err)
	}
}

func deviceAdd(args string) string {
	if args == "" {
		return "Usage: /add <device-alias> <device-ip> <device-mac>"
	}

	params := strings.Split(args, " ")
	if len(params) < 3 {
		return "Usage: /add <device-alias> <device-ip> <device-mac>"
	}

	dev := &types.Device{IP: params[1], Mac: params[2]}
	alias := types.Alias{Device: dev, Name: params[0]}
	resp := make(chan struct{})
	aliasResp := &types.AliasResponse{Alias: alias, Response: resp}
	deviceChan <- aliasResp
	respTmp := <-resp
	if respTmp != nil {
		return aliasResp.String()
	}
	return "Got error adding device"
}

func getAllDevices() string {
	resp := ""
	aliasChannel := make(chan string)
	getAliases <- aliasChannel
	for tmp := range aliasChannel {
		resp += ", " + tmp
	}
	return resp
}

func checkValidUser(sender *tgbotapi.User) bool {
	return sender.UserName == allowedUser.UserName && sender.FirstName == allowedUser.FirstName && sender.LastName == allowedUser.LastName
}
<commit_msg>addressing problems<commit_after>package bot

import (
	"strings"

	"github.com/charliemaiors/golang-wol/types"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/viper"
	tgbotapi "gopkg.in/telegram-bot-api.v4"
)

var (
	deviceChan       chan *types.AliasResponse
	getChan          chan *types.GetDev
	delDevChan       chan *types.DelDev
	passHandlingChan chan *types.PasswordHandling
	updatePassChan   chan *types.PasswordUpdate
	getAliases       chan chan string
	allowedUser      *tgbotapi.User
	bot              *tgbotapi.BotAPI
)

func RunBot(deviceChan chan *types.AliasResponse, getChan chan *types.GetDev, delDevChan chan *types.DelDev, passHandlingChan chan *types.PasswordHandling, updatePassChan chan *types.PasswordUpdate, getAliases chan chan string) {
	initBot(deviceChan, getChan, delDevChan, passHandlingChan, updatePassChan, getAliases)
	var err error
	token := viper.GetString("bot.telegram.token")
	bot, err = tgbotapi.NewBotAPI(token)
	if err != nil {
		panic(err)
	}

	log.Debugf("Authorized bot %s", bot.Self.String())
	bot.Debug = true
	u := tgbotapi.NewUpdate(0)
	u.Timeout = 60

	updates, err := bot.GetUpdatesChan(u)
	if err != nil {
		log.Errorf("Could not get updates, %v error", err)
		return
	}

	for update := range updates {
		if update.Message == nil {
			continue
		}
		go handleUpdate(update)

	}
}

func initBot(deviceChanDef chan *types.AliasResponse, getChanDef chan *types.GetDev, delDevChanDef chan *types.DelDev, passHandlingChanDef chan *types.PasswordHandling, updatePassChanDef chan *types.PasswordUpdate, getAliasesDef chan chan string) {
	deviceChan = deviceChanDef
	getChan = getChanDef
	delDevChan = delDevChanDef
	passHandlingChan = passHandlingChanDef
	updatePassChan = updatePassChanDef
	getAliases = getAliasesDef

	allowedUser = &tgbotapi.User{
		FirstName: viper.GetString("bot.telegram.firstname"),
		LastName:  viper.GetString("bot.telegram.lastname"),
		UserName:  viper.GetString("bot.telegram.username"),
		IsBot:     false,
	}
}

func handleUpdate(update tgbotapi.Update) {
	var msg tgbotapi.MessageConfig
	if !checkValidUser(update.Message.From) {
		msg = tgbotapi.NewMessage(update.Message.Chat.ID, "You are not allowed to chat with this bot")
	}
	if update.Message.IsCommand() {
		switch update.Message.Command() {
		case "list":
			devices := getAllDevices()
			msg = tgbotapi.NewMessage(update.Message.Chat.ID, "You have registered this devices: "+devices)
		case "add":
			args := update.Message.CommandArguments()
			resp := deviceAdd(args)
			msg = tgbotapi.NewMessage(update.Message.Chat.ID, resp)
		case "wake":
			args := update.Message.CommandArguments()
			resp := deviceWake(args)
		}

	}

	msg.ReplyToMessageID = update.Message.MessageID
	_, err := bot.Send(msg)
	if err != nil {
		log.Errorf("Got error while sending answer %v", err)
	}
}

func deviceWake(args string) string {
	if args == "" {
		return "Usage: /wake <device-alias>"
	}
	params := strings.Split(args, " ")

}

func deviceAdd(args string) string {
	if args == "" {
		return "Usage: /add <device-alias> <device-ip> <device-mac>"
	}

	params := strings.Split(args, " ")
	if len(params) < 3 {
		return "Usage: /add <device-alias> <device-ip> <device-mac>"
	}

	dev := &types.Device{IP: params[1], Mac: params[2]}
	alias := types.Alias{Device: dev, Name: params[0]}
	resp := make(chan struct{})
	aliasResp := &types.AliasResponse{Alias: alias, Response: resp}
	deviceChan <- aliasResp
	_, ok := <-resp
	if ok {
		return aliasResp.String()
	}
	return "Got error adding device"
}

func getAllDevices() string {
	resp := ""
	aliasChannel := make(chan string)
	getAliases <- aliasChannel
	for tmp := range aliasChannel {
		resp += ", " + tmp
	}
	return resp
}

func checkValidUser(sender *tgbotapi.User) bool {
	return sender.UserName == allowedUser.UserName && sender.FirstName == allowedUser.FirstName && sender.LastName == allowedUser.LastName
}
<|endoftext|>