<commit_before>package libkb

import (
	"crypto/hmac"
	"crypto/sha512"
	"encoding/base64"
	"encoding/hex"
	"fmt"

	jsonw "github.com/keybase/go-jsonw"
	triplesec "github.com/keybase/go-triplesec"
	keybase_1 "github.com/keybase/protocol/go"
)

type LoggedInResult struct {
	SessionId string
	CsrfToken string
	Uid       UID
	Username  string
}

type LoginState struct {
	Configured      bool
	LoggedIn        bool
	SessionVerified bool

	salt            []byte
	loginSession    []byte
	loginSessionB64 string
	tsec            *triplesec.Cipher
	tspkey          *TSPassKey
	sharedSecret    []byte
	sessionFor      string

	loggedInRes *LoggedInResult
}

const SharedSecretLen = 32

func NewLoginState() *LoginState {
	return &LoginState{}
}

func (s LoginState) GetSharedSecret() []byte {
	return s.sharedSecret
}

func (s LoginState) IsLoggedIn() bool {
	return s.LoggedIn
}

func (s *LoginState) GetSalt() (salt []byte, err error) {
	if s.salt == nil {
		s.salt = G.Env.GetSalt()
	}
	salt = s.salt
	return
}

func (s *LoginState) GenerateNewSalt() error {
	var err error
	s.salt, err = RandBytes(triplesec.SaltLen)
	if err != nil {
		return err
	}
	return nil
}

func (s *LoginState) GetSaltAndLoginSession(email_or_username string) error {

	if s.salt != nil && s.loginSession != nil && s.sessionFor == email_or_username {
		return nil
	}
	s.sessionFor = ""

	res, err := G.API.Get(ApiArg{
		Endpoint:    "getsalt",
		NeedSession: false,
		Args: HttpArgs{
			"email_or_username": S{email_or_username},
		},
	})
	if err != nil {
		return err
	}

	shex, err := res.Body.AtKey("salt").GetString()
	if err != nil {
		return err
	}

	s.salt, err = hex.DecodeString(shex)
	if err != nil {
		return err
	}

	ls_b64, err := res.Body.AtKey("login_session").GetString()
	if err != nil {
		return err
	}

	s.loginSession, err = base64.StdEncoding.DecodeString(ls_b64)
	if err != nil {
		return err
	}

	s.loginSessionB64 = ls_b64
	s.sessionFor = email_or_username

	return nil
}

func (s *LoginState) StretchKey(passphrase string) (err error) {
	if s.tsec == nil {
		if s.tsec, err = triplesec.NewCipher([]byte(passphrase), s.salt); err != nil {
			return err
		}
	}
	if s.tspkey == nil {
		if tk, err := NewTSPassKey(passphrase, s.salt); err != nil {
			return err
		} else {
			s.tspkey = &tk
		}
	}
	_, s.sharedSecret, err = s.tsec.DeriveKey(SharedSecretLen)
	return nil
}

func (s *LoginState) ComputeLoginPw() ([]byte, error) {
	mac := hmac.New(sha512.New, s.sharedSecret)
	mac.Write(s.loginSession)
	return mac.Sum(nil), nil
}

func (s *LoginState) PostLoginToServer(eOu string, lgpw []byte) error {
	res, err := G.API.Post(ApiArg{
		Endpoint:    "login",
		NeedSession: false,
		Args: HttpArgs{
			"email_or_username": S{eOu},
			"hmac_pwh":          S{hex.EncodeToString(lgpw)},
			"login_session":     S{s.loginSessionB64},
		},
		AppStatus: []string{"OK", "BAD_LOGIN_PASSWORD"},
	})
	if err != nil {
		return err
	}
	if res.AppStatus == "BAD_LOGIN_PASSWORD" {
		err = PassphraseError{"server rejected login attempt"}
		return err
	}

	b := res.Body
	sessionId, err := b.AtKey("session").GetString()
	if err != nil {
		return err
	}
	csrfToken, err := b.AtKey("csrf_token").GetString()
	if err != nil {
		return err
	}
	uid, err := GetUid(b.AtKey("uid"))
	if err != nil {
		return nil
	}
	uname, err := b.AtKey("me").AtKey("basics").AtKey("username").GetString()
	if err != nil {
		return nil
	}

	s.loggedInRes = &LoggedInResult{sessionId, csrfToken, *uid, uname}
	return nil
}

func (s *LoginState) SaveLoginState(uidVerified bool) (err error) {
	s.LoggedIn = true
	s.SessionVerified = true
	s.loginSession = nil
	s.loginSessionB64 = ""

	if cfg := G.Env.GetConfigWriter(); cfg != nil {

		if err = cfg.SetUserConfig(NewUserConfig(s.loggedInRes.Uid, s.loggedInRes.Username,
			s.salt, false, nil), false); err != nil {
			return err
		}

		if err = cfg.Write(); err != nil {
			return err
		}
	}

	if sw := G.SessionWriter; sw != nil {
		sw.SetLoggedIn(*s.loggedInRes)
		if err = sw.Write(); err != nil {
			return err
		}
	}

	return nil
}

func (s *LoginState) Logout() error {
	G.Log.Debug("+ Logout called")
	err := G.Session.Logout()
	if err == nil {
		s.LoggedIn = false
		s.SessionVerified = false
		if s.tsec != nil {
			s.tsec.Scrub()
			s.tsec = nil
		}
		if s.tspkey != nil {
			s.tspkey.Scrub()
			s.tspkey = nil
		}
	}
	if G.SecretSyncer != nil {
		G.SecretSyncer.Clear()
	}
	G.Log.Debug("- Logout called")
	return err
}

type LoginArg struct {
	Force      bool
	Prompt     bool
	Retry      int
	RetryMsg   string
	Username   string
	Passphrase string
	Ui         LoginUI
	SecretUI   SecretUI
	NoUi       bool
}

func (r PostAuthProofRes) ToLoggedInResult() (ret *LoggedInResult, err error) {
	var uid *UID
	if uid, err = UidFromHex(r.UidHex); err != nil {
		return
	}
	ret = &LoggedInResult{
		SessionId: r.SessionId,
		CsrfToken: r.CsrfToken,
		Uid:       *uid,
		Username:  r.Username,
	}
	return
}

// PubkeyLogin looks for a locally available private key and tries
// to establish a session via public key signature.
func (s *LoginState) PubkeyLogin(ui SecretUI) (err error) {
	var key GenericKey
	var me *User
	var proof *jsonw.Wrapper
	var sig string
	var pres *PostAuthProofRes

	G.Log.Debug("+ PubkeyLogin()")
	defer func() { G.Log.Debug("- PubkeyLogin() -> %s", ErrToOk(err)) }()

	if me, err = LoadMe(LoadUserArg{}); err != nil {
		return
	}

	// Need the loginSession; the salt doesn't really matter here.
	if err = s.GetSaltAndLoginSession(me.name); err != nil {
		return
	}

	if key, err = G.Keyrings.GetSecretKey("login", ui, me); err != nil {
		return
	}

	if proof, err = me.AuthenticationProof(key, s.loginSessionB64, AUTH_EXPIRE_IN); err != nil {
		return
	}

	if sig, _, _, err = SignJson(proof, key); err != nil {
		return
	}

	arg := PostAuthProofArg{
		uid: me.id,
		sig: sig,
		key: key,
	}
	if pres, err = PostAuthProof(arg); err != nil {
		return
	}

	if s.loggedInRes, err = pres.ToLoggedInResult(); err != nil {
		return
	}

	err = s.SaveLoginState(false)

	return
}

func (s *LoginState) Login(arg LoginArg) (err error) {
	G.Log.Debug("+ Login called")
	defer func() { G.Log.Debug("- Login -> %s", ErrToOk(err)) }()

	n_tries := arg.Retry
	if n_tries == 0 {
		n_tries = 1
	}

	if arg.Ui == nil && !arg.NoUi {
		if G.UI != nil {
			arg.Ui = G.UI.GetLoginUI()
		} else {
			err = NoUiError{"login"}
			return
		}
	}

	if arg.SecretUI == nil && !arg.NoUi {
		if G.UI != nil {
			arg.SecretUI = G.UI.GetSecretUI()
		} else {
			err = NoUiError{"secret"}
			return
		}
	}

	if err = s.PubkeyLogin(arg.SecretUI); err != nil {
		G.Log.Info("Public key login failed: %s", err.Error())
		G.Log.Info("Falling back to passphrase login")
		err = nil
	} else {
		G.Log.Debug("| Pubkey login succeeded")
	}

	for i := 0; i < n_tries; i++ {
		err = s.login(&arg)
		if err == nil {
			break
		} else if _, badpw := err.(PassphraseError); !badpw || len(arg.Passphrase) > 0 {
			break
		} else {
			arg.RetryMsg = err.Error()
		}
	}
	return
}

func (s *LoginState) getEmailOrUsername(arg *LoginArg) (res string, prompted bool, err error) {
	if res = arg.Username; len(res) != 0 {
	} else if res = G.Env.GetEmailOrUsername(); len(res) > 0 || !arg.Prompt || arg.Ui == nil {
	} else if res, err = arg.Ui.GetEmailOrUsername(); err != nil {
	} else {
		arg.Username = res
		prompted = true
	}

	if len(res) == 0 {
		err = NoUsernameError{}
	}
	return
}

func (s *LoginState) login(arg *LoginArg) (err error) {
	G.Log.Debug("+ LoginState.login (username=%s)", arg.Username)
	defer func() {
		G.Log.Debug("- LoginState.login -> %s", ErrToOk(err))
	}()

	if s.LoggedIn && !arg.Force {
		G.Log.Debug("- Login short-circuited; already logged in")
		return
	}

	if !arg.Force {
		var is_valid bool
		is_valid, err = G.Session.LoadAndCheck()
		if err != nil {
			return
		}

		if is_valid {
			s.LoggedIn = true
			s.SessionVerified = true
			G.Log.Debug("Our session token is still valid; we're logged in")
			return nil
		}
	} else if err = G.Session.Load(); err != nil {
		return
	}

	var emailOrUsername string
	var prompted bool

	if emailOrUsername, prompted, err = s.getEmailOrUsername(arg); err != nil {
		return
	}

	G.Log.Debug(fmt.Sprintf("| got username: %s\n", emailOrUsername))

	if err = s.GetSaltAndLoginSession(emailOrUsername); err != nil {
		return
	}

	if _, err = s.GetTriplesec(emailOrUsername, arg.Passphrase, arg.RetryMsg, arg.SecretUI); err != nil {
		return
	}

	var lgpw []byte
	lgpw, err = s.ComputeLoginPw()

	if err != nil {
		return
	}

	err = s.PostLoginToServer(emailOrUsername, lgpw)
	if err != nil {
		s.tsec = nil
		s.tspkey = nil
		return err
	}

	err = s.SaveLoginState(prompted)
	if err != nil {
		return err
	}

	return
}

func (s *LoginState) GetTriplesec(un string, pp string, retry string, ui SecretUI) (ret *triplesec.Cipher, err error) {
	if s.tsec != nil {
		ret = s.tsec
		return
	}
	var salt []byte
	if salt, err = s.GetSalt(); err != nil {
		return
	} else if salt == nil {
		err = fmt.Errorf("Cannot encrypt; no salt found")
	}

	arg := keybase_1.GetKeybasePassphraseArg{
		Username: un,
		Retry:    retry,
	}

	if len(pp) > 0 {
	} else if ui == nil {
		err = NoUiError{"secret"}
	} else if pp, err = ui.GetKeybasePassphrase(arg); err != nil {
		return
	}

	if err = s.StretchKey(pp); err != nil {
		return
	}

	ret = s.tsec
	return
}

func (s *LoginState) GetCachedTriplesec() *triplesec.Cipher {
	return s.tsec
}

func (s *LoginState) GetCachedTSPassKey() *TSPassKey {
	return s.tspkey
}
<commit_msg>Fix bug: make PubkeyLogin actually switch users<commit_after>package libkb

import (
	"crypto/hmac"
	"crypto/sha512"
	"encoding/base64"
	"encoding/hex"
	"fmt"

	jsonw "github.com/keybase/go-jsonw"
	triplesec "github.com/keybase/go-triplesec"
	keybase_1 "github.com/keybase/protocol/go"
)

type LoggedInResult struct {
	SessionId string
	CsrfToken string
	Uid       UID
	Username  string
}

type LoginState struct {
	Configured      bool
	LoggedIn        bool
	SessionVerified bool

	salt            []byte
	loginSession    []byte
	loginSessionB64 string
	tsec            *triplesec.Cipher
	tspkey          *TSPassKey
	sharedSecret    []byte
	sessionFor      string

	loggedInRes *LoggedInResult
}

const SharedSecretLen = 32

func NewLoginState() *LoginState {
	return &LoginState{}
}

func (s LoginState) GetSharedSecret() []byte {
	return s.sharedSecret
}

func (s LoginState) IsLoggedIn() bool {
	return s.LoggedIn
}

func (s *LoginState) GetSalt() (salt []byte, err error) {
	if s.salt == nil {
		s.salt = G.Env.GetSalt()
	}
	salt = s.salt
	return
}

func (s *LoginState) GenerateNewSalt() error {
	var err error
	s.salt, err = RandBytes(triplesec.SaltLen)
	if err != nil {
		return err
	}
	return nil
}

func (s *LoginState) GetSaltAndLoginSession(email_or_username string) error {

	if s.salt != nil && s.loginSession != nil && s.sessionFor == email_or_username {
		return nil
	}
	s.sessionFor = ""

	res, err := G.API.Get(ApiArg{
		Endpoint:    "getsalt",
		NeedSession: false,
		Args: HttpArgs{
			"email_or_username": S{email_or_username},
		},
	})
	if err != nil {
		return err
	}

	shex, err := res.Body.AtKey("salt").GetString()
	if err != nil {
		return err
	}

	s.salt, err = hex.DecodeString(shex)
	if err != nil {
		return err
	}

	ls_b64, err := res.Body.AtKey("login_session").GetString()
	if err != nil {
		return err
	}

	s.loginSession, err = base64.StdEncoding.DecodeString(ls_b64)
	if err != nil {
		return err
	}

	s.loginSessionB64 = ls_b64
	s.sessionFor = email_or_username

	return nil
}

func (s *LoginState) StretchKey(passphrase string) (err error) {
	if s.tsec == nil {
		if s.tsec, err = triplesec.NewCipher([]byte(passphrase), s.salt); err != nil {
			return err
		}
	}
	if s.tspkey == nil {
		if tk, err := NewTSPassKey(passphrase, s.salt); err != nil {
			return err
		} else {
			s.tspkey = &tk
		}
	}
	_, s.sharedSecret, err = s.tsec.DeriveKey(SharedSecretLen)
	return nil
}

func (s *LoginState) ComputeLoginPw() ([]byte, error) {
	mac := hmac.New(sha512.New, s.sharedSecret)
	mac.Write(s.loginSession)
	return mac.Sum(nil), nil
}

func (s *LoginState) PostLoginToServer(eOu string, lgpw []byte) error {
	res, err := G.API.Post(ApiArg{
		Endpoint:    "login",
		NeedSession: false,
		Args: HttpArgs{
			"email_or_username": S{eOu},
			"hmac_pwh":          S{hex.EncodeToString(lgpw)},
			"login_session":     S{s.loginSessionB64},
		},
		AppStatus: []string{"OK", "BAD_LOGIN_PASSWORD"},
	})
	if err != nil {
		return err
	}
	if res.AppStatus == "BAD_LOGIN_PASSWORD" {
		err = PassphraseError{"server rejected login attempt"}
		return err
	}

	b := res.Body
	sessionId, err := b.AtKey("session").GetString()
	if err != nil {
		return err
	}
	csrfToken, err := b.AtKey("csrf_token").GetString()
	if err != nil {
		return err
	}
	uid, err := GetUid(b.AtKey("uid"))
	if err != nil {
		return nil
	}
	uname, err := b.AtKey("me").AtKey("basics").AtKey("username").GetString()
	if err != nil {
		return nil
	}

	s.loggedInRes = &LoggedInResult{sessionId, csrfToken, *uid, uname}
	return nil
}

func (s *LoginState) SaveLoginState(uidVerified bool) (err error) {
	s.LoggedIn = true
	s.SessionVerified = true
	s.loginSession = nil
	s.loginSessionB64 = ""

	if cfg := G.Env.GetConfigWriter(); cfg != nil {

		if err = cfg.SetUserConfig(NewUserConfig(s.loggedInRes.Uid, s.loggedInRes.Username,
			s.salt, false, nil), false); err != nil {
			return err
		}

		if err = cfg.Write(); err != nil {
			return err
		}
	}

	if sw := G.SessionWriter; sw != nil {
		sw.SetLoggedIn(*s.loggedInRes)
		if err = sw.Write(); err != nil {
			return err
		}
	}

	return nil
}

func (s *LoginState) Logout() error {
	G.Log.Debug("+ Logout called")
	err := G.Session.Logout()
	if err == nil {
		s.LoggedIn = false
		s.SessionVerified = false
		if s.tsec != nil {
			s.tsec.Scrub()
			s.tsec = nil
		}
		if s.tspkey != nil {
			s.tspkey.Scrub()
			s.tspkey = nil
		}
	}
	if G.SecretSyncer != nil {
		G.SecretSyncer.Clear()
	}
	G.Log.Debug("- Logout called")
	return err
}

type LoginArg struct {
	Force      bool
	Prompt     bool
	Retry      int
	RetryMsg   string
	Username   string
	Passphrase string
	Ui         LoginUI
	SecretUI   SecretUI
	NoUi       bool
}

func (r PostAuthProofRes) ToLoggedInResult() (ret *LoggedInResult, err error) {
	var uid *UID
	if uid, err = UidFromHex(r.UidHex); err != nil {
		return
	}
	ret = &LoggedInResult{
		SessionId: r.SessionId,
		CsrfToken: r.CsrfToken,
		Uid:       *uid,
		Username:  r.Username,
	}
	return
}

// PubkeyLogin looks for a locally available private key and tries
// to establish a session via public key signature.
func (s *LoginState) PubkeyLogin(name string, ui SecretUI) (err error) {
	var key GenericKey
	var me *User
	var proof *jsonw.Wrapper
	var sig string
	var pres *PostAuthProofRes
	var uc *UserConfig

	G.Log.Debug("+ PubkeyLogin()")
	defer func() { G.Log.Debug("- PubkeyLogin() -> %s", ErrToOk(err)) }()

	if len(name) == 0 {
		if uc, err = G.Env.GetConfig().GetUserConfig(); err != nil {
			G.Log.Debug("| Can't find current UserConfig")
		} else {
			name = uc.Name
		}
	} else if uc, err = G.Env.GetConfig().GetUserConfigForUsername(name); err != nil {
		G.Log.Debug("| No Userconfig for %s", name)
	}
	if err != nil {
		return
	}

	if me, err = LoadUser(LoadUserArg{Name: name}); err != nil {
		return
	}

	// Need the loginSession; the salt doesn't really matter here.
	if err = s.GetSaltAndLoginSession(name); err != nil {
		return
	}

	if key, err = G.Keyrings.GetSecretKey("login", ui, me); err != nil {
		return
	}

	if proof, err = me.AuthenticationProof(key, s.loginSessionB64, AUTH_EXPIRE_IN); err != nil {
		return
	}

	if sig, _, _, err = SignJson(proof, key); err != nil {
		return
	}

	arg := PostAuthProofArg{
		uid: me.id,
		sig: sig,
		key: key,
	}
	if pres, err = PostAuthProof(arg); err != nil {
		return
	}

	if s.loggedInRes, err = pres.ToLoggedInResult(); err != nil {
		return
	}

	err = s.SaveLoginState(false)

	return
}

func (s *LoginState) Login(arg LoginArg) (err error) {
	G.Log.Debug("+ Login called")
	defer func() { G.Log.Debug("- Login -> %s", ErrToOk(err)) }()

	n_tries := arg.Retry
	if n_tries == 0 {
		n_tries = 1
	}

	if arg.Ui == nil && !arg.NoUi {
		if G.UI != nil {
			arg.Ui = G.UI.GetLoginUI()
		} else {
			err = NoUiError{"login"}
			return
		}
	}

	if arg.SecretUI == nil && !arg.NoUi {
		if G.UI != nil {
			arg.SecretUI = G.UI.GetSecretUI()
		} else {
			err = NoUiError{"secret"}
			return
		}
	}

	if err = s.PubkeyLogin(arg.Username, arg.SecretUI); err != nil {
		G.Log.Info("Public key login failed: %s", err.Error())
		G.Log.Info("Falling back to passphrase login")
		err = nil
	} else {
		G.Log.Debug("| Pubkey login succeeded")
	}

	for i := 0; i < n_tries; i++ {
		err = s.login(&arg)
		if err == nil {
			break
		} else if _, badpw := err.(PassphraseError); !badpw || len(arg.Passphrase) > 0 {
			break
		} else {
			arg.RetryMsg = err.Error()
		}
	}
	return
}

func (s *LoginState) getEmailOrUsername(arg *LoginArg) (res string, prompted bool, err error) {
	if res = arg.Username; len(res) != 0 {
	} else if res = G.Env.GetEmailOrUsername(); len(res) > 0 || !arg.Prompt || arg.Ui == nil {
	} else if res, err = arg.Ui.GetEmailOrUsername(); err != nil {
	} else {
		arg.Username = res
		prompted = true
	}

	if len(res) == 0 {
		err = NoUsernameError{}
	}
	return
}

func (s *LoginState) login(arg *LoginArg) (err error) {
	G.Log.Debug("+ LoginState.login (username=%s)", arg.Username)
	defer func() {
		G.Log.Debug("- LoginState.login -> %s", ErrToOk(err))
	}()

	if s.LoggedIn && !arg.Force {
		G.Log.Debug("- Login short-circuited; already logged in")
		return
	}

	if !arg.Force {
		var is_valid bool
		is_valid, err = G.Session.LoadAndCheck()
		if err != nil {
			return
		}

		if is_valid {
			s.LoggedIn = true
			s.SessionVerified = true
			G.Log.Debug("Our session token is still valid; we're logged in")
			return nil
		}
	} else if err = G.Session.Load(); err != nil {
		return
	}

	var emailOrUsername string
	var prompted bool

	if emailOrUsername, prompted, err = s.getEmailOrUsername(arg); err != nil {
		return
	}

	G.Log.Debug(fmt.Sprintf("| got username: %s\n", emailOrUsername))

	if err = s.GetSaltAndLoginSession(emailOrUsername); err != nil {
		return
	}

	if _, err = s.GetTriplesec(emailOrUsername, arg.Passphrase, arg.RetryMsg, arg.SecretUI); err != nil {
		return
	}

	var lgpw []byte
	lgpw, err = s.ComputeLoginPw()

	if err != nil {
		return
	}

	err = s.PostLoginToServer(emailOrUsername, lgpw)
	if err != nil {
		s.tsec = nil
		s.tspkey = nil
		return err
	}

	err = s.SaveLoginState(prompted)
	if err != nil {
		return err
	}

	return
}

func (s *LoginState) GetTriplesec(un string, pp string, retry string, ui SecretUI) (ret *triplesec.Cipher, err error) {
	if s.tsec != nil {
		ret = s.tsec
		return
	}
	var salt []byte
	if salt, err = s.GetSalt(); err != nil {
		return
	} else if salt == nil {
		err = fmt.Errorf("Cannot encrypt; no salt found")
	}

	arg := keybase_1.GetKeybasePassphraseArg{
		Username: un,
		Retry:    retry,
	}

	if len(pp) > 0 {
	} else if ui == nil {
		err = NoUiError{"secret"}
	} else if pp, err = ui.GetKeybasePassphrase(arg); err != nil {
		return
	}

	if err = s.StretchKey(pp); err != nil {
		return
	}

	ret = s.tsec
	return
}

func (s *LoginState) GetCachedTriplesec() *triplesec.Cipher {
	return s.tsec
}

func (s *LoginState) GetCachedTSPassKey() *TSPassKey {
	return s.tspkey
}
<|endoftext|>


<commit_before>package base

import (
	"bytes"
	"encoding/json"
	"log"
	"net/http"
	"os"
	"time"
)

/*
Event is the struct we push up to ELK.
{
  hostname: "",
  timestamp: RFC 3339 Format,
  localEnvironment: bool,
  callingIP: "",
  event: " ",
  responseCode: int,
  building: "",
  room: ""
}
*/
type Event struct {
	Hostname         string `json:"hostname,omitempty"`
	Timestamp        string `json:"timestamp,omitempty"`
	LocalEnvironment bool   `json:"localEnvironment,omitempty"`
	Event            string `json:"event,omitempty"`
	ResponseCode     int    `json:"responseCode,omitempty"`
	Success          bool   `json:"success,omitempty"`
	Building         string `json:"building,omitempty"`
	Room             string `json:"room,omitempty"`
	Device           string `json:"device,omitempty"`
}

func ReportToELK(e Event) error {
	var err error

	e.Timestamp = time.Now().Format(time.RFC3339)
	if len(os.Getenv("LOCAL_ENVIRONMENT")) > 0 {
		e.Hostname = os.Getenv("PI_HOSTNAME")
	} else {
		e.Hostname, err = os.Hostname()
	}

	if err != nil {
		return err
	}

	e.LocalEnvironment = len(os.Getenv("LOCAL_ENVIRONMENT")) > 0

	log.Printf("Elastic event to send: %+v", e)

	toSend, err := json.Marshal(&e)
	if err != nil {
		return err
	}

	log.Print("Sending event to: " + os.Getenv("ELASTIC_API_EVENTS"))

	_, err = http.Post(os.Getenv("ELASTIC_API_EVENTS"),
		"application/json",
		bytes.NewBuffer(toSend))

	return err
}
<commit_msg>create an event to send<commit_after>package base

import (
	"bytes"
	"encoding/json"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/byuoitav/event-router-microservice/eventinfrastructure"
)

/*
Event is the struct we push up to ELK.
{
	hostname: "",
	timestamp: RFC 3339 Format,
	localEnvironment: bool,
	callingIP: "",
	event: " ",
	responseCode: int,
	building: "",
	room: ""
}
*/

// Reports an event to ELK
// (depreciated) now reported by the event-translator-microservice
func ReportToELK(e eventinfrastructure.Event) error {
	var err error

	e.Timestamp = time.Now().Format(time.RFC3339)
	if len(os.Getenv("LOCAL_ENVIRONMENT")) > 0 {
		e.Hostname = os.Getenv("PI_HOSTNAME")
	} else {
		e.Hostname, err = os.Hostname()
	}

	if err != nil {
		return err
	}

	e.LocalEnvironment = len(os.Getenv("LOCAL_ENVIRONMENT")) > 0

	log.Printf("Elastic event to send: %+v", e)

	toSend, err := json.Marshal(&e)
	if err != nil {
		return err
	}

	log.Print("Sending event to: " + os.Getenv("ELASTIC_API_EVENTS"))

	_, err = http.Post(os.Getenv("ELASTIC_API_EVENTS"),
		"application/json",
		bytes.NewBuffer(toSend))

	return err
}

func Event(e eventinfrastructure.Event) error {
	var err error

	e.Timestamp = time.Now().Format(time.RFC3339)
	if len(os.Getenv("LOCAL_ENVIRONMENT")) > 0 {
		e.Hostname = os.Getenv("PI_HOSTNAME")
	} else {
		e.Hostname, err = os.Hostname()
	}
	if err != nil {
		return err
	}

	e.LocalEnvironment = len(os.Getenv("LOCAL_ENVIRONMENT")) > 0

	log.Printf("Event to send: %+v", e)

	toSend, err := json.Marshal(&e)
	if err != nil {
		return err
	}

	log.Print("Sending event to event router.")

	/* send error */
	_, err = http.Post("hi",
		"application/json",
		bytes.NewBuffer(toSend))

	return err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"

	"github.com/ansel1/merry"
	"github.com/dghubble/sling"
	"github.com/dickeyxxx/golock"
)

func init() {
	CLITopics = append(CLITopics, &Topic{
		Name:        "update",
		Description: "update heroku-cli",
		Commands: Commands{
			{
				Topic:            "update",
				Hidden:           true,
				Description:      "updates the Heroku CLI",
				DisableAnalytics: true,
				Args:             []Arg{{Name: "channel", Optional: true}},
				Run: func(ctx *Context) {
					channel := ctx.Args.(map[string]string)["channel"]
					if channel == "" {
						channel = Channel
					}
					Update(channel)
				},
			},
		},
	})
}

// Autoupdate is a flag to enable/disable CLI autoupdating
var Autoupdate = "no"

// UpdateLockPath is the path to the updating lock file
var UpdateLockPath = filepath.Join(CacheHome, "updating.lock")
var autoupdateFile = filepath.Join(CacheHome, "autoupdate")

// Update updates the CLI and plugins
func Update(channel string) {
	touchAutoupdateFile()
	SubmitAnalytics()
	updateCLI(channel)
	UserPlugins.Update()
	UserPlugins.MigrateRubyPlugins()
	deleteOldPluginsDirectory()
	truncate(ErrLogPath, 1000)
	cleanTmp()
}

func updateCLI(channel string) {
	if Autoupdate == "no" {
		return
	}
	manifest := GetUpdateManifest(channel)
	if npmExists() && manifest.Version == Version && manifest.Channel == Channel {
		return
	}
	DownloadCLI(channel, filepath.Join(DataHome, "cli"), manifest)
	loadNewCLI()
}

// DownloadCLI downloads a CLI update to a given path
func DownloadCLI(channel, path string, manifest *Manifest) {
	locked, err := golock.IsLocked(UpdateLockPath)
	LogIfError(err)
	if locked {
		ExitWithMessage("Update in progress")
	}
	LogIfError(golock.Lock(UpdateLockPath))
	unlock := func() {
		golock.Unlock(UpdateLockPath)
	}
	defer unlock()
	hideCursor()
	downloadingMessage := fmt.Sprintf("heroku-cli: Updating to %s...", manifest.Version)
	if manifest.Channel != "stable" {
		downloadingMessage = fmt.Sprintf("%s (%s)", downloadingMessage, manifest.Channel)
	}
	Logln(downloadingMessage)
	build := manifest.Builds[runtime.GOOS+"-"+runtime.GOARCH]
	if build == nil {
		must(merry.Errorf("no build for %s", manifest.Channel))
	}
	reader, getSha, err := downloadXZ(build.URL, downloadingMessage)
	must(err)
	tmp := tmpDir(DataHome)
	must(extractTar(reader, tmp))
	sha := getSha()
	if sha != build.Sha256 {
		must(merry.Errorf("SHA mismatch: expected %s to be %s", sha, build.Sha256))
	}
	exists, _ := FileExists(path)
	if exists {
		must(os.Rename(path, filepath.Join(tmpDir(DataHome), "heroku")))
	}
	must(os.Rename(filepath.Join(tmp, "heroku"), path))
	Debugf("updated to %s\n", manifest.Version)
}

// IsUpdateNeeded checks if an update is available
func IsUpdateNeeded() bool {
	f, err := os.Stat(autoupdateFile)
	if err != nil {
		if os.IsNotExist(err) {
			return true
		}
		LogIfError(err)
		return true
	}
	return time.Since(f.ModTime()) > 4*time.Hour
}

func touchAutoupdateFile() {
	out, err := os.OpenFile(autoupdateFile, os.O_WRONLY|os.O_CREATE, 0644)
	must(err)
	_, err = out.WriteString(time.Now().String())
	must(err)
	err = out.Close()
	must(err)
}

// Manifest is the manifest.json for releases
type Manifest struct {
	ReleasedAt string            `json:"released_at"`
	Version    string            `json:"version"`
	Channel    string            `json:"channel"`
	Builds     map[string]*Build `json:"builds"`
}

// Build is a part of a Manifest
type Build struct {
	URL    string `json:"url"`
	Sha256 string `json:"sha256"`
	Bytes  int64  `json:"bytes"`
}

var updateManifestRetrying = false

// GetUpdateManifest loads the manifest.json for a channel
func GetUpdateManifest(channel string) *Manifest {
	var m Manifest
	url := "https://cli-assets.heroku.com/branches/" + channel + "/manifest.json"
	rsp, err := sling.New().Get(url).ReceiveSuccess(&m)
	if err != nil && !updateManifestRetrying {
		updateManifestRetrying = true
		return GetUpdateManifest(channel)
	}
	must(err)
	must(getHTTPError(rsp))
	return &m
}

// TriggerBackgroundUpdate will trigger an update to the client in the background
func TriggerBackgroundUpdate() {
	if IsUpdateNeeded() {
		Debugln("triggering background update")
		touchAutoupdateFile()
		exec.Command(BinPath, "update").Start()
	}
}

func cleanTmp() {
	clean := func(base string) {
		dir := filepath.Join(base, "tmp")
		if exists, _ := FileExists(dir); !exists {
			return
		}
		files, err := ioutil.ReadDir(dir)
		LogIfError(err)
		for _, file := range files {
			if time.Since(file.ModTime()) > 24*time.Hour {
				path := filepath.Join(dir, file.Name())
				Debugf("removing old tmp: %s\n", path)
				LogIfError(os.RemoveAll(path))
			}
		}
	}
	clean(DataHome)
	clean(CacheHome)
}

func expectedBinPath() string {
	bin := filepath.Join(DataHome, "cli", "bin", "heroku")
	if runtime.GOOS == WINDOWS {
		bin = bin + ".exe"
	}
	return bin
}

func loadNewCLI() {
	if Autoupdate == "no" {
		return
	}
	expected, err := os.Stat(expectedBinPath())
	if err != nil {
		if os.IsNotExist(err) {
			if !npmExists() {
				Debugln("npm does not exist, forcing update")
				Update(Channel)
			}
			return
		}
		must(err)
	}
	current, err := os.Stat(BinPath)
	must(err)
	if !os.SameFile(current, expected) {
		execBin(expected.Name(), Args...)
	}
}

func npmExists() bool {
	exists, _ := FileExists(npmBinPath())
	return exists
}

// deleteOldPluginsDirectory removes the v4 directory
// this is a problem on windows because node will recurse up until it finds a node_modules directory
func deleteOldPluginsDirectory() {
	os.RemoveAll(filepath.Join(DataHome, "node_modules"))
}
<commit_msg>submit analytics after everything else<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"

	"github.com/ansel1/merry"
	"github.com/dghubble/sling"
	"github.com/dickeyxxx/golock"
)

func init() {
	CLITopics = append(CLITopics, &Topic{
		Name:        "update",
		Description: "update heroku-cli",
		Commands: Commands{
			{
				Topic:            "update",
				Hidden:           true,
				Description:      "updates the Heroku CLI",
				DisableAnalytics: true,
				Args:             []Arg{{Name: "channel", Optional: true}},
				Run: func(ctx *Context) {
					channel := ctx.Args.(map[string]string)["channel"]
					if channel == "" {
						channel = Channel
					}
					Update(channel)
				},
			},
		},
	})
}

// Autoupdate is a flag to enable/disable CLI autoupdating
var Autoupdate = "no"

// UpdateLockPath is the path to the updating lock file
var UpdateLockPath = filepath.Join(CacheHome, "updating.lock")
var autoupdateFile = filepath.Join(CacheHome, "autoupdate")

// Update updates the CLI and plugins
func Update(channel string) {
	touchAutoupdateFile()
	updateCLI(channel)
	UserPlugins.Update()
	UserPlugins.MigrateRubyPlugins()
	deleteOldPluginsDirectory()
	truncate(ErrLogPath, 1000)
	cleanTmp()
	SubmitAnalytics()
}

func updateCLI(channel string) {
	if Autoupdate == "no" {
		return
	}
	manifest := GetUpdateManifest(channel)
	if npmExists() && manifest.Version == Version && manifest.Channel == Channel {
		return
	}
	DownloadCLI(channel, filepath.Join(DataHome, "cli"), manifest)
	loadNewCLI()
}

// DownloadCLI downloads a CLI update to a given path
func DownloadCLI(channel, path string, manifest *Manifest) {
	locked, err := golock.IsLocked(UpdateLockPath)
	LogIfError(err)
	if locked {
		ExitWithMessage("Update in progress")
	}
	LogIfError(golock.Lock(UpdateLockPath))
	unlock := func() {
		golock.Unlock(UpdateLockPath)
	}
	defer unlock()
	hideCursor()
	downloadingMessage := fmt.Sprintf("heroku-cli: Updating to %s...", manifest.Version)
	if manifest.Channel != "stable" {
		downloadingMessage = fmt.Sprintf("%s (%s)", downloadingMessage, manifest.Channel)
	}
	Logln(downloadingMessage)
	build := manifest.Builds[runtime.GOOS+"-"+runtime.GOARCH]
	if build == nil {
		must(merry.Errorf("no build for %s", manifest.Channel))
	}
	reader, getSha, err := downloadXZ(build.URL, downloadingMessage)
	must(err)
	tmp := tmpDir(DataHome)
	must(extractTar(reader, tmp))
	sha := getSha()
	if sha != build.Sha256 {
		must(merry.Errorf("SHA mismatch: expected %s to be %s", sha, build.Sha256))
	}
	exists, _ := FileExists(path)
	if exists {
		must(os.Rename(path, filepath.Join(tmpDir(DataHome), "heroku")))
	}
	must(os.Rename(filepath.Join(tmp, "heroku"), path))
	Debugf("updated to %s\n", manifest.Version)
}

// IsUpdateNeeded checks if an update is available
func IsUpdateNeeded() bool {
	f, err := os.Stat(autoupdateFile)
	if err != nil {
		if os.IsNotExist(err) {
			return true
		}
		LogIfError(err)
		return true
	}
	return time.Since(f.ModTime()) > 4*time.Hour
}

func touchAutoupdateFile() {
	out, err := os.OpenFile(autoupdateFile, os.O_WRONLY|os.O_CREATE, 0644)
	must(err)
	_, err = out.WriteString(time.Now().String())
	must(err)
	err = out.Close()
	must(err)
}

// Manifest is the manifest.json for releases
type Manifest struct {
	ReleasedAt string            `json:"released_at"`
	Version    string            `json:"version"`
	Channel    string            `json:"channel"`
	Builds     map[string]*Build `json:"builds"`
}

// Build is a part of a Manifest
type Build struct {
	URL    string `json:"url"`
	Sha256 string `json:"sha256"`
	Bytes  int64  `json:"bytes"`
}

var updateManifestRetrying = false

// GetUpdateManifest loads the manifest.json for a channel
func GetUpdateManifest(channel string) *Manifest {
	var m Manifest
	url := "https://cli-assets.heroku.com/branches/" + channel + "/manifest.json"
	rsp, err := sling.New().Get(url).ReceiveSuccess(&m)
	if err != nil && !updateManifestRetrying {
		updateManifestRetrying = true
		return GetUpdateManifest(channel)
	}
	must(err)
	must(getHTTPError(rsp))
	return &m
}

// TriggerBackgroundUpdate will trigger an update to the client in the background
func TriggerBackgroundUpdate() {
	if IsUpdateNeeded() {
		Debugln("triggering background update")
		touchAutoupdateFile()
		exec.Command(BinPath, "update").Start()
	}
}

func cleanTmp() {
	clean := func(base string) {
		dir := filepath.Join(base, "tmp")
		if exists, _ := FileExists(dir); !exists {
			return
		}
		files, err := ioutil.ReadDir(dir)
		LogIfError(err)
		for _, file := range files {
			if time.Since(file.ModTime()) > 24*time.Hour {
				path := filepath.Join(dir, file.Name())
				Debugf("removing old tmp: %s\n", path)
				LogIfError(os.RemoveAll(path))
			}
		}
	}
	clean(DataHome)
	clean(CacheHome)
}

func expectedBinPath() string {
	bin := filepath.Join(DataHome, "cli", "bin", "heroku")
	if runtime.GOOS == WINDOWS {
		bin = bin + ".exe"
	}
	return bin
}

func loadNewCLI() {
	if Autoupdate == "no" {
		return
	}
	expected, err := os.Stat(expectedBinPath())
	if err != nil {
		if os.IsNotExist(err) {
			if !npmExists() {
				Debugln("npm does not exist, forcing update")
				Update(Channel)
			}
			return
		}
		must(err)
	}
	current, err := os.Stat(BinPath)
	must(err)
	if !os.SameFile(current, expected) {
		execBin(expected.Name(), Args...)
	}
}

func npmExists() bool {
	exists, _ := FileExists(npmBinPath())
	return exists
}

// deleteOldPluginsDirectory removes the v4 directory
// this is a problem on windows because node will recurse up until it finds a node_modules directory
func deleteOldPluginsDirectory() {
	os.RemoveAll(filepath.Join(DataHome, "node_modules"))
}
<|endoftext|>


<commit_before><commit_msg>Add InteractiveButtonRequest<commit_after><|endoftext|>


<commit_before>package hcsshim

import (
	"io"
	"time"
)

// ProcessConfig is used as both the input of Container.CreateProcess
// and to convert the parameters to JSON for passing onto the HCS
type ProcessConfig struct {
	ApplicationName  string
	CommandLine      string
	WorkingDirectory string
	Environment      map[string]string
	EmulateConsole   bool
	CreateStdInPipe  bool
	CreateStdOutPipe bool
	CreateStdErrPipe bool
	ConsoleSize      [2]int
}

type Layer struct {
	ID   string
	Path string
}

type MappedDir struct {
	HostPath      string
	ContainerPath string
	ReadOnly      bool
}

type HvRuntime struct {
	ImagePath string `json:",omitempty"`
}

// ContainerConfig is used as both the input of CreateContainer
// and to convert the parameters to JSON for passing onto the HCS
// TODO Windows: @darrenstahlmsft Add ProcessorCount
type ContainerConfig struct {
	SystemType              string      // HCS requires this to be hard-coded to "Container"
	Name                    string      // Name of the container. We use the docker ID.
	Owner                   string      // The management platform that created this container
	IsDummy                 bool        // Used for development purposes.
	VolumePath              string      // Windows volume path for scratch space
	IgnoreFlushesDuringBoot bool        // Optimization hint for container startup in Windows
	LayerFolderPath         string      // Where the layer folders are located
	Layers                  []Layer     // List of storage layers
	Credentials             string      `json:",omitempty"` // Credentials information
	ProcessorWeight         uint64      `json:",omitempty"` // CPU Shares 0..10000 on Windows; where 0 will be omitted and HCS will default.
	ProcessorMaximum        int64       `json:",omitempty"` // CPU maximum usage percent 1..100
	StorageIOPSMaximum      uint64      `json:",omitempty"` // Maximum Storage IOPS
	StorageBandwidthMaximum uint64      `json:",omitempty"` // Maximum Storage Bandwidth in bytes per second
	StorageSandboxSize      uint64      `json:",omitempty"` // Size in bytes that the container system drive should be expanded to if smaller
	MemoryMaximumInMB       int64       `json:",omitempty"` // Maximum memory available to the container in Megabytes
	HostName                string      // Hostname
	MappedDirectories       []MappedDir // List of mapped directories (volumes/mounts)
	SandboxPath             string      // Location of unmounted sandbox (used for Hyper-V containers)
	HvPartition             bool        // True if it a Hyper-V Container
	EndpointList            []string    // List of networking endpoints to be attached to container
	HvRuntime               *HvRuntime  // Hyper-V container settings
	Servicing               bool        // True if this container is for servicing
}

const (
	notificationTypeNone           string = "None"
	notificationTypeGracefulExit   string = "GracefulExit"
	notificationTypeForcedExit     string = "ForcedExit"
	notificationTypeUnexpectedExit string = "UnexpectedExit"
	notificationTypeReboot         string = "Reboot"
	notificationTypeConstructed    string = "Constructed"
	notificationTypeStarted        string = "Started"
	notificationTypePaused         string = "Paused"
	notificationTypeUnknown        string = "Unknown"
)

// Container represents a created (but not necessarily running) container.
type Container interface {
	// Start synchronously starts the container.
	Start() error

	// Shutdown requests a container shutdown, but it may not actually be shutdown until Wait() succeeds.
	Shutdown() error

	// Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds.
	Terminate() error

	// Waits synchronously waits for the container to shutdown or terminate.
	Wait() error

	// WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It
	// returns false if timeout occurs.
	WaitTimeout(time.Duration) error

	// Pause pauses the execution of a container.
	Pause() error

	// Resume resumes the execution of a container.
	Resume() error

	// HasPendingUpdates returns true if the container has updates pending to install.
	HasPendingUpdates() (bool, error)

	// CreateProcess launches a new process within the container.
	CreateProcess(c *ProcessConfig) (Process, error)

	// OpenProcess gets an interface to an existing process within the container.
	OpenProcess(pid int) (Process, error)

	// Close cleans up any state associated with the container but does not terminate or wait for it.
	Close() error
}

// Process represents a running or exited process.
type Process interface {
	// Pid returns the process ID of the process within the container.
	Pid() int

	// Kill signals the process to terminate but does not wait for it to finish terminating.
	Kill() error

	// Wait waits for the process to exit.
	Wait() error

	// WaitTimeout waits for the process to exit or the duration to elapse. It returns
	// false if timeout occurs.
	WaitTimeout(time.Duration) error

	// ExitCode returns the exit code of the process. The process must have
	// already terminated.
	ExitCode() (int, error)

	// ResizeConsole resizes the console of the process.
	ResizeConsole(width, height uint16) error

	// Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing
	// these pipes does not close the underlying pipes; it should be possible to
	// call this multiple times to get multiple interfaces.
	Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error)

	// CloseStdin closes the write side of the stdin pipe so that the process is
	// notified on the read side that there is no more data in stdin.
	CloseStdin() error

	// Close cleans up any state associated with the process but does not kill
	// or wait on it.
	Close() error
}
<commit_msg>Add some missing fields to container config<commit_after>package hcsshim

import (
	"io"
	"time"
)

// ProcessConfig is used as both the input of Container.CreateProcess
// and to convert the parameters to JSON for passing onto the HCS
type ProcessConfig struct {
	ApplicationName  string
	CommandLine      string
	WorkingDirectory string
	Environment      map[string]string
	EmulateConsole   bool
	CreateStdInPipe  bool
	CreateStdOutPipe bool
	CreateStdErrPipe bool
	ConsoleSize      [2]int
}

type Layer struct {
	ID   string
	Path string
}

type MappedDir struct {
	HostPath      string
	ContainerPath string
	ReadOnly      bool
}

type HvRuntime struct {
	ImagePath    string `json:",omitempty"`
	SkipTemplate bool   `json:",omitempty"`
}

// ContainerConfig is used as both the input of CreateContainer
// and to convert the parameters to JSON for passing onto the HCS
type ContainerConfig struct {
	SystemType              string      // HCS requires this to be hard-coded to "Container"
	Name                    string      // Name of the container. We use the docker ID.
	Owner                   string      // The management platform that created this container
	IsDummy                 bool        // Used for development purposes.
	VolumePath              string      // Windows volume path for scratch space
	IgnoreFlushesDuringBoot bool        // Optimization hint for container startup in Windows
	LayerFolderPath         string      // Where the layer folders are located
	Layers                  []Layer     // List of storage layers
	Credentials             string      `json:",omitempty"` // Credentials information
	ProcessorCount          uint32      `json:",omitempty"` // Number of processors to assign to the container.
	ProcessorWeight         uint64      `json:",omitempty"` // CPU Shares 0..10000 on Windows; where 0 will be omitted and HCS will default.
	ProcessorMaximum        int64       `json:",omitempty"` // CPU maximum usage percent 1..100
	StorageIOPSMaximum      uint64      `json:",omitempty"` // Maximum Storage IOPS
	StorageBandwidthMaximum uint64      `json:",omitempty"` // Maximum Storage Bandwidth in bytes per second
	StorageSandboxSize      uint64      `json:",omitempty"` // Size in bytes that the container system drive should be expanded to if smaller
	MemoryMaximumInMB       int64       `json:",omitempty"` // Maximum memory available to the container in Megabytes
	HostName                string      // Hostname
	MappedDirectories       []MappedDir // List of mapped directories (volumes/mounts)
	SandboxPath             string      // Location of unmounted sandbox (used for Hyper-V containers)
	HvPartition             bool        // True if it a Hyper-V Container
	EndpointList            []string    // List of networking endpoints to be attached to container
	HvRuntime               *HvRuntime  // Hyper-V container settings
	Servicing               bool        // True if this container is for servicing
}

// Container represents a created (but not necessarily running) container.
type Container interface {
	// Start synchronously starts the container.
	Start() error

	// Shutdown requests a container shutdown, but it may not actually be shutdown until Wait() succeeds.
	Shutdown() error

	// Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds.
	Terminate() error

	// Waits synchronously waits for the container to shutdown or terminate.
	Wait() error

	// WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It
	// returns false if timeout occurs.
	WaitTimeout(time.Duration) error

	// Pause pauses the execution of a container.
	Pause() error

	// Resume resumes the execution of a container.
	Resume() error

	// HasPendingUpdates returns true if the container has updates pending to install.
	HasPendingUpdates() (bool, error)

	// CreateProcess launches a new process within the container.
	CreateProcess(c *ProcessConfig) (Process, error)

	// OpenProcess gets an interface to an existing process within the container.
	OpenProcess(pid int) (Process, error)

	// Close cleans up any state associated with the container but does not terminate or wait for it.
	Close() error
}

// Process represents a running or exited process.
type Process interface {
	// Pid returns the process ID of the process within the container.
	Pid() int

	// Kill signals the process to terminate but does not wait for it to finish terminating.
	Kill() error

	// Wait waits for the process to exit.
	Wait() error

	// WaitTimeout waits for the process to exit or the duration to elapse. It returns
	// false if timeout occurs.
	WaitTimeout(time.Duration) error

	// ExitCode returns the exit code of the process. The process must have
	// already terminated.
	ExitCode() (int, error)

	// ResizeConsole resizes the console of the process.
	ResizeConsole(width, height uint16) error

	// Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing
	// these pipes does not close the underlying pipes; it should be possible to
	// call this multiple times to get multiple interfaces.
	Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error)

	// CloseStdin closes the write side of the stdin pipe so that the process is
	// notified on the read side that there is no more data in stdin.
	CloseStdin() error

	// Close cleans up any state associated with the process but does not kill
	// or wait on it.
	Close() error
}
<|endoftext|>


<commit_before>package protocol

import (
	"context"
	"io"
	"strings"
	"time"

	nnet "github.com/nimona/go-nimona/net"
	zap "go.uber.org/zap"
)

type RelayProtocol struct {
	connections map[string]nnet.Conn
	net         nnet.Net
}

func NewRelayProtocol(f nnet.Net) *RelayProtocol {
	return &RelayProtocol{
		net: f,
	}
}
func (m *RelayProtocol) Name() string {
	return "relay"
}

// The server offering the relay part
func (m *RelayProtocol) Handle(fn nnet.HandlerFunc) nnet.HandlerFunc {
	return func(ctx context.Context, c nnet.Conn) error {
		addr := c.GetAddress()
		lgr := nnet.Logger(ctx).With(
			zap.Namespace("protocol:relay"),
			zap.String("addr.current", addr.Current()),
			zap.String("addr.params", addr.CurrentParams()),
		)

		lgr.Debug("Handling Relay")

		// For param `keepalive` start listening for a token
		if strings.Contains(addr.CurrentParams(), "keepalive") {
			go func() {
				for {
					_, err := c.ReadToken()
					if err != nil {
						lgr.Error("Could not read token", zap.Error(err))
					}
					// lgr.Debug("Read relay ping", zap.String("token", string(token)))

				}
			}()
			return nil
		}

		host := addr.CurrentParams()
		addr.Pop()

		// Connect
		ctx, cn, err := m.net.DialContext(ctx, host)
		if err != nil {
			lgr.Error("Could not Dial client", zap.Error(err))
			return err
		}

		// Pipe
		go func() {
			err = m.pipe(ctx, cn, c)
			if err != nil {
				lgr.Error("Could not start pipe", zap.Error(err))
			}

		}()
		return nil
	}
}

// The client that wants to connect
func (m *RelayProtocol) Negotiate(fn nnet.NegotiatorFunc) nnet.NegotiatorFunc {
	return func(ctx context.Context, c nnet.Conn) error {
		addr := c.GetAddress()
		lgr := nnet.Logger(ctx).With(
			zap.Namespace("protocol:relay"),
			zap.String("addr.current", addr.Current()),
			zap.String("addr.params", addr.CurrentParams()),
		)

		lgr.Debug("Negotiating Relay")
		if strings.Contains(addr.CurrentParams(), "keepalive") {
			go func() {
				for {
					time.Sleep(10 * time.Second)
					if err := c.WriteToken([]byte("PONG")); err != nil {
						lgr.Error("Could not write token", zap.Error(err))
					}

					// lgr.Debug("Wrote relay ping")
				}
			}()
			return nil
		}

		addr.Pop()
		return fn(ctx, c)
	}
}

func (m *RelayProtocol) pipe(ctx context.Context,
	a, b io.ReadWriteCloser) error {
	lgr := nnet.Logger(ctx).With(
		zap.Namespace("protocol:relay"),
	)

	lgr.Debug("Piping")
	done := make(chan error, 1)

	cp := func(r, w io.ReadWriteCloser) {
		n, err := io.Copy(r, w)
		if err != nil {
			lgr.Error("Failed to copy bytes", zap.Error(err))
		}
		lgr.Debug("Bytes written", zap.Int64("bytes", n))
		done <- err
	}
	go cp(a, b)
	go cp(b, a)
	return <-done
}
func (s *RelayProtocol) GetAddresses() []string {
	return []string{}
}
<commit_msg>Fix relay<commit_after>package protocol

import (
	"context"
	"io"
	"strings"
	"time"

	nnet "github.com/nimona/go-nimona/net"
	zap "go.uber.org/zap"
)

type RelayProtocol struct {
	connections    map[string]nnet.Conn
	net            nnet.Net
	relayAddresses []string
}

func NewRelayProtocol(f nnet.Net, relayAddresses []string) *RelayProtocol {
	return &RelayProtocol{
		net:            f,
		relayAddresses: relayAddresses,
	}
}
func (m *RelayProtocol) Name() string {
	return "relay"
}

// The server offering the relay part
func (m *RelayProtocol) Handle(fn nnet.HandlerFunc) nnet.HandlerFunc {
	return func(ctx context.Context, c nnet.Conn) error {
		addr := c.GetAddress()
		lgr := nnet.Logger(ctx).With(
			zap.Namespace("protocol:relay"),
			zap.String("addr.current", addr.Current()),
			zap.String("addr.params", addr.CurrentParams()),
		)

		lgr.Debug("Handling Relay")

		// For param `keepalive` start listening for a token
		if strings.Contains(addr.CurrentParams(), "keepalive") {
			go func() {
				for {
					_, err := c.ReadToken()
					if err != nil {
						lgr.Error("Could not read token", zap.Error(err))
					}
					// lgr.Debug("Read relay ping", zap.String("token", string(token)))

				}
			}()
			return nil
		}

		host := addr.CurrentParams()
		addr.Pop()

		// Connect
		ctx, cn, err := m.net.DialContext(ctx, host)
		if err != nil {
			lgr.Error("Could not Dial client", zap.Error(err))
			return err
		}

		// Pipe
		go func() {
			err = m.pipe(ctx, cn, c)
			if err != nil {
				lgr.Error("Could not start pipe", zap.Error(err))
			}

		}()
		return nil
	}
}

// The client that wants to connect
func (m *RelayProtocol) Negotiate(fn nnet.NegotiatorFunc) nnet.NegotiatorFunc {
	return func(ctx context.Context, c nnet.Conn) error {
		addr := c.GetAddress()
		lgr := nnet.Logger(ctx).With(
			zap.Namespace("protocol:relay"),
			zap.String("addr.current", addr.Current()),
			zap.String("addr.params", addr.CurrentParams()),
		)

		lgr.Debug("Negotiating Relay")
		if strings.Contains(addr.CurrentParams(), "keepalive") {
			go func() {
				for {
					time.Sleep(10 * time.Second)
					if err := c.WriteToken([]byte("PONG")); err != nil {
						lgr.Error("Could not write token", zap.Error(err))
					}

					// lgr.Debug("Wrote relay ping")
				}
			}()
			return nil
		}

		addr.Pop()
		return fn(ctx, c)
	}
}

func (m *RelayProtocol) pipe(ctx context.Context,
	a, b io.ReadWriteCloser) error {
	lgr := nnet.Logger(ctx).With(
		zap.Namespace("protocol:relay"),
	)

	lgr.Debug("Piping")
	done := make(chan error, 1)

	cp := func(r, w io.ReadWriteCloser) {
		n, err := io.Copy(r, w)
		if err != nil {
			lgr.Error("Failed to copy bytes", zap.Error(err))
		}
		lgr.Debug("Bytes written", zap.Int64("bytes", n))
		done <- err
	}
	go cp(a, b)
	go cp(b, a)
	return <-done
}

func (s *RelayProtocol) GetAddresses() []string {
	return s.relayAddresses
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"log"
	"os/exec"
	"sort"
	"time"

	"launchpad.net/goamz/aws"
	"launchpad.net/goamz/s3"
)

var bucket *s3.Bucket

func init() {
	auth, err := aws.EnvAuth()
	if err != nil {
		log.Panicln("couldn't get AWS auth credentials: " + err.Error())
	}

	region := aws.USEast

	conn := s3.New(auth, region)
	bucket = conn.Bucket("bitjester.co")
}

func main() {
	filename := time.Now().Format("diacam/2006-01-02T15:04:05-0700.jpg")
	capture := exec.Command("raspistill", "-o", "-")

	image, err := capture.Output()
	if err != nil {
		log.Panicln("couldn't capture an image: " + err.Error())
	}

	err = upload(filename, image)
	if err != nil {
		log.Panicln("couldn't upload image to S3: " + err.Error())
	}

	log.Printf("uploaded %s\n", filename)

	writeManifest()
}

func upload(filename string, contents []byte) (err error) {
	err = bucket.Put(filename, contents, "image/jpeg", s3.PublicRead)
	return
}

func writeManifest() (err error) {
	resp, err := bucket.List("diacam/2014", "", "diacam/2014", 1000)
	if err != nil {
		return
	}

	keys := make([]string, 0)
	for _, key := range resp.Contents {
		keys = append(keys, key.Key)
	}
	sort.Sort(sort.Reverse(sort.StringSlice(keys)))

	log.Println(keys)

	json, err := json.Marshal(keys)
	if err != nil {
		return
	}

	bucket.Put("diacam/MANIFEST.json", json, "application/json", s3.PublicRead)

	return
}
<commit_msg>Take command-line args that allow customization (and one-off test shots).<commit_after>package main

import (
	"encoding/json"
	"flag"
	"log"
	"os/exec"
	"sort"
	"time"

	"launchpad.net/goamz/aws"
	"launchpad.net/goamz/s3"
)

var bucket *s3.Bucket

var bucketName = flag.String("bucket", "bitjester.co", "the name of the bucket to upload into")
var prefix = flag.String("prefix", "diacam/", "a string with which to prefix names of uploads")
var updateManifest = flag.Bool("update-manifest", false, "add uploaded file as an entry in ${prefix}MANIFEST.json")

func init() {
	auth, err := aws.EnvAuth()
	if err != nil {
		log.Panicln("couldn't get AWS auth credentials: " + err.Error())
	}

	region := aws.USEast

	conn := s3.New(auth, region)
	bucket = conn.Bucket(*bucketName)
}

func main() {
	flag.Parse()

	filename := time.Now().Format(*prefix + "2006-01-02T15:04:05-0700.jpg")
	capture := exec.Command("raspistill", "-o", "-")

	image, err := capture.Output()
	if err != nil {
		log.Panicln("couldn't capture an image: " + err.Error())
	}

	err = upload(filename, image)
	if err != nil {
		log.Panicln("couldn't upload image to S3: " + err.Error())
	}

	log.Printf("uploaded %s\n", filename)

	if *updateManifest {
		writeManifest()
	}
}

func upload(filename string, contents []byte) (err error) {
	err = bucket.Put(filename, contents, "image/jpeg", s3.PublicRead)
	return
}

func writeManifest() (err error) {
	resp, err := bucket.List(*prefix + "201", "", *prefix + "201", 1000)
	if err != nil {
		return
	}

	keys := make([]string, 0)
	for _, key := range resp.Contents {
		keys = append(keys, key.Key)
	}
	sort.Sort(sort.Reverse(sort.StringSlice(keys)))

	log.Println(keys)

	json, err := json.Marshal(keys)
	if err != nil {
		return
	}

	bucket.Put(*prefix + "MANIFEST.json", json, "application/json", s3.PublicRead)

	return
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha1"
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"github.com/dchest/uniuri"
	_ "github.com/go-sql-driver/mysql"
)

const (
	LENGTH         = 6
	PORT           = ":8080"
	UPDIRECTORY    = "/tmp/"
	GRILLDIRECTORY = "pomf/build/img/"
	POMFDIRECTORY  = "pomf/build"
	UPADDRESS      = "http://localhost"
	dbUSERNAME     = ""
	dbNAME         = ""
	dbPASSWORD     = ""
	DATABASE       = dbUSERNAME + ":" + dbPASSWORD + "@/" + dbNAME + "?charset=utf8"
	MAXSIZE        = 10 * 1024 * 1024
)

type Result struct {
	URL  string `json:"url"`
	Name string `json:"name"`
	Hash string `json:"hash"`
	Size int64  `json:"size"`
}

type Response struct {
	Success     bool     `json:"success"`
	ErrorCode   int      `json:"errorcode,omitempty"`
	Description string   `json:"description,omitempty"`
	Files       []Result `json:"files,omitempty"`
}

func generateName() (string, error) {
	name := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	if err != nil {
		return "", err
	}
	query, err := db.Query("select id from files where id=?", name)
	if err != sql.ErrNoRows {
		for query.Next() {
			generateName()
		}
	}
	db.Close()

	return name, nil
}
func respond(w http.ResponseWriter, output string, resp Response) {
	if resp.ErrorCode != 0 {
		resp.Files = []Result{}
		resp.Success = false
	} else {
		resp.Success = true
	}

	switch output {
	case "json":

		w.Header().Set("Content-Type", "application/json")
		err := json.NewEncoder(w).Encode(resp)

		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	case "xml":
		x, err := xml.MarshalIndent(resp, "", "  ")
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "application/xml")
		w.Write(x)

	case "html":
		w.Header().Set("Content-Type", "text/html")
		for _, file := range resp.Files {
			io.WriteString(w, ""+file.URL+"
")
		}

	case "gyazo", "text":
		w.Header().Set("Content-Type", "plain/text")
		for _, file := range resp.Files {
			io.WriteString(w, file.URL+"\n")
		}

	case "csv":
		w.Header().Set("Content-Type", "text/csv")
		io.WriteString(w, "name, url, hash, size\n")
		for _, file := range resp.Files {
			io.WriteString(w, file.Name+","+file.URL+","+file.Hash+","+strconv.FormatInt(file.Size, 10)+"\n")
		}

	default:
		w.Header().Set("Content-Type", "application/json")
		err := json.NewEncoder(w).Encode(resp)

		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

	}

}
func grillHandler(w http.ResponseWriter, r *http.Request) {
	kawaii, err := ioutil.ReadDir(GRILLDIRECTORY)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	http.Redirect(w, r, "/img/"+kawaii[rand.Intn(len(kawaii))].Name(), http.StatusFound)
}
func uploadHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	output := r.FormValue("output")
	reader, err := r.MultipartReader()
	resp := Response{Files: []Result{}}
	if err != nil {
		resp.ErrorCode = http.StatusInternalServerError
		resp.Description = err.Error()
		respond(w, output, resp)
		return
	}

	db, err := sql.Open("mysql", DATABASE)
	if err != nil {
		panic(err)
	}

	for {
		part, err := reader.NextPart()
		if err == io.EOF {
			break
		}

		if part.FileName() == "" {
			continue
		}

		s := generateName()
		extName := filepath.Ext(part.FileName())
		filename := s + extName
		dst, err := os.Create(UPDIRECTORY + filename)
		defer dst.Close()

		if err != nil {
			resp.ErrorCode = http.StatusInternalServerError
			resp.Description = err.Error()
			respond(w, output, resp)
			break
		}

		h := sha1.New()
		t := io.TeeReader(part, h)
		_, err = io.Copy(dst, t)

		if err != nil {
			resp.ErrorCode = http.StatusInternalServerError
			resp.Description = err.Error()
			respond(w, output, resp)
			break
		}
		hash := h.Sum(nil)
		sha1 := base64.URLEncoding.EncodeToString(hash)
		stat, _ := dst.Stat()
		size := stat.Size()
		if size > MAXSIZE {
			resp.ErrorCode = http.StatusRequestEntityTooLarge
			resp.Description = err.Error()
			break
		}

		originalname := part.FileName()
		err = db.QueryRow("select originalname, filename, size from files where hash=?", sha1).Scan(&originalname, &filename, &size)
		res := Result{
			URL:  UPADDRESS + "/" + filename,
			Name: originalname,
			Hash: sha1,
			Size: size,
		}
		if err == sql.ErrNoRows {
			query, err := db.Prepare("INSERT into files(hash, originalname, filename, size, date) values(?, ?, ?, ?, ?)")
			if err != nil {
				resp.ErrorCode = http.StatusInternalServerError
				resp.Description = err.Error()
				break
			}
			_, err = query.Exec(res.Hash, res.Name, filename, res.Size, time.Now().Format("2016-01-02"))
			if err != nil {
				resp.ErrorCode = http.StatusInternalServerError
				resp.Description = err.Error()
				break
			}
		}
		resp.Files = append(resp.Files, res)
	}
	respond(w, output, resp)
}

func main() {
	http.HandleFunc("/upload.php", uploadHandler)
	http.HandleFunc("/grill.php", grillHandler)
	http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(POMFDIRECTORY))))
	err := http.ListenAndServe(PORT, nil)
	if err != nil {
		panic(err)
	}

}
<commit_msg>Add further error handling<commit_after>package main

import (
	"crypto/sha1"
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"github.com/dchest/uniuri"
	_ "github.com/go-sql-driver/mysql"
)

const (
	LENGTH         = 6
	PORT           = ":8080"
	UPDIRECTORY    = "/tmp/"
	GRILLDIRECTORY = "pomf/build/img/"
	POMFDIRECTORY  = "pomf/build"
	UPADDRESS      = "http://localhost"
	dbUSERNAME     = ""
	dbNAME         = ""
	dbPASSWORD     = ""
	DATABASE       = dbUSERNAME + ":" + dbPASSWORD + "@/" + dbNAME + "?charset=utf8"
	MAXSIZE        = 10 * 1024 * 1024
)

type Result struct {
	URL  string `json:"url"`
	Name string `json:"name"`
	Hash string `json:"hash"`
	Size int64  `json:"size"`
}

type Response struct {
	Success     bool     `json:"success"`
	ErrorCode   int      `json:"errorcode,omitempty"`
	Description string   `json:"description,omitempty"`
	Files       []Result `json:"files,omitempty"`
}

func generateName() (string, error) {
	name := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	if err != nil {
		return "", err
	}
	var id string
	err = db.QueryRow("select id from files where id=?", name).Scan(&id)
	if err != sql.ErrNoRows {
		generateName()
	}
	db.Close()

	return name, nil
}
func respond(w http.ResponseWriter, output string, resp Response) {
	if resp.ErrorCode != 0 {
		resp.Files = []Result{}
		resp.Success = false
	} else {
		resp.Success = true
	}

	switch output {
	case "json":

		w.Header().Set("Content-Type", "application/json")
		err := json.NewEncoder(w).Encode(resp)

		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	case "xml":
		x, err := xml.MarshalIndent(resp, "", "  ")
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "application/xml")
		w.Write(x)

	case "html":
		w.Header().Set("Content-Type", "text/html")
		for _, file := range resp.Files {
			io.WriteString(w, ""+file.URL+"
")
		}

	case "gyazo", "text":
		w.Header().Set("Content-Type", "plain/text")
		for _, file := range resp.Files {
			io.WriteString(w, file.URL+"\n")
		}

	case "csv":
		w.Header().Set("Content-Type", "text/csv")
		io.WriteString(w, "name, url, hash, size\n")
		for _, file := range resp.Files {
			io.WriteString(w, file.Name+","+file.URL+","+file.Hash+","+strconv.FormatInt(file.Size, 10)+"\n")
		}

	default:
		w.Header().Set("Content-Type", "application/json")
		err := json.NewEncoder(w).Encode(resp)

		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

	}

}
func grillHandler(w http.ResponseWriter, r *http.Request) {
	kawaii, err := ioutil.ReadDir(GRILLDIRECTORY)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	http.Redirect(w, r, "/img/"+kawaii[rand.Intn(len(kawaii))].Name(), http.StatusFound)
}
func uploadHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	output := r.FormValue("output")
	reader, err := r.MultipartReader()
	resp := Response{Files: []Result{}}
	if err != nil {
		resp.ErrorCode = http.StatusInternalServerError
		resp.Description = err.Error()
		respond(w, output, resp)
		return
	}

	db, err := sql.Open("mysql", DATABASE)
	if err != nil {
		panic(err)
	}

	for {
		part, err := reader.NextPart()
		if err == io.EOF {
			break
		}

		if part.FileName() == "" {
			continue
		}

		s, err := generateName()
		if err != nil {
			resp.ErrorCode = http.StatusInternalServerError
			resp.Description = err.Error()
			break
		}
		extName := filepath.Ext(part.FileName())
		filename := s + extName
		dst, err := os.Create(UPDIRECTORY + filename)

		if err != nil {
			resp.ErrorCode = http.StatusInternalServerError
			resp.Description = err.Error()
			respond(w, output, resp)
			break
		}
		defer dst.Close()

		h := sha1.New()
		t := io.TeeReader(part, h)
		_, err = io.Copy(dst, t)

		if err != nil {
			resp.ErrorCode = http.StatusInternalServerError
			resp.Description = err.Error()
			respond(w, output, resp)
			break
		}
		hash := h.Sum(nil)
		sha1 := base64.URLEncoding.EncodeToString(hash)
		stat, _ := dst.Stat()
		size := stat.Size()
		if size > MAXSIZE {
			resp.ErrorCode = http.StatusRequestEntityTooLarge
			resp.Description = err.Error()
			break
		}

		originalname := part.FileName()
		err = db.QueryRow("select originalname, filename, size from files where hash=?", sha1).Scan(&originalname, &filename, &size)
		res := Result{
			URL:  UPADDRESS + "/" + filename,
			Name: originalname,
			Hash: sha1,
			Size: size,
		}
		if err == sql.ErrNoRows {
			query, err := db.Prepare("INSERT into files(hash, originalname, filename, size, date) values(?, ?, ?, ?, ?)")
			if err != nil {
				resp.ErrorCode = http.StatusInternalServerError
				resp.Description = err.Error()
				break
			}
			_, err = query.Exec(res.Hash, res.Name, filename, res.Size, time.Now().Format("2016-01-02"))
			if err != nil {
				resp.ErrorCode = http.StatusInternalServerError
				resp.Description = err.Error()
				break
			}
		}
		resp.Files = append(resp.Files, res)
	}
	respond(w, output, resp)
}

func main() {
	http.HandleFunc("/upload.php", uploadHandler)
	http.HandleFunc("/grill.php", grillHandler)
	http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(POMFDIRECTORY))))
	err := http.ListenAndServe(PORT, nil)
	if err != nil {
		panic(err)
	}

}
<|endoftext|>


<commit_before>package cmap

import (
	"encoding/json"
	"sync"
)

// TODO: Add Keys function which returns an array of keys for the map.

// A "thread" safe map of type string:Anything.
type ConcurrentMap struct {
	m            map[string]interface{} // map with string key and any type of object as value.
	sync.RWMutex                        // Read Write mutex, guards access to internal map.
}

// Creates a new concurent map.
func NewConcurrentMap() *ConcurrentMap {
	return &ConcurrentMap{m: make(map[string]interface{})}
}

// Adds an element to the map.
func (m *ConcurrentMap) Add(key string, value interface{}) {
	m.Lock()
	defer m.Unlock()
	m.m[key] = value
}

// Returns the number of elements within the map.
func (m *ConcurrentMap) Count() int {
	return len(m.m)
}

// Retrives an element from map under given key.
func (m *ConcurrentMap) Get(key string) (interface{}, bool) {
	m.RLock()
	defer m.RUnlock()

	val, err := m.m[key]
	return val, err
}

// Looks up an item under specified key
func (m *ConcurrentMap) Has(key string) bool {
	m.RLock()
	defer m.RUnlock()
	_, ok := m.m[key]
	return ok
}

// Removes an element from the map.
func (m *ConcurrentMap) Remove(key string) {
	m.Lock()
	defer m.Unlock()
	delete(m.m, key)
}

// Clears map by constructing a new one.
func (m *ConcurrentMap) Clear() {
	m.Lock()
	defer m.Unlock()
	m.m = make(map[string]interface{})
}

// Checks if map is empty.
func (m *ConcurrentMap) IsEmpty() bool {
	return len(m.m) == 0
}

// Used by the Iter function to wrap two variables together over a channel,
type Tuple struct {
	Key string
	Val interface{}
}

// Returns an iterator which could be used in a for range loop.
func (m *ConcurrentMap) Iter() <-chan Tuple {
	//ch := make(chan interface{})
	ch := make(chan Tuple)
	go func() {
		m.RLock()
		defer m.RUnlock()
		for key, val := range m.m {
			ch <- Tuple{key, val}
		}
		close(ch)
	}()
	return ch
}

func (m ConcurrentMap) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		M map[string]interface{}
	}{M: m.m})
}
<commit_msg>forgot to guard Count function.<commit_after>package cmap

import (
	"encoding/json"
	"sync"
)

// TODO: Add Keys function which returns an array of keys for the map.

// A "thread" safe map of type string:Anything.
type ConcurrentMap struct {
	m            map[string]interface{} // map with string key and any type of object as value.
	sync.RWMutex                        // Read Write mutex, guards access to internal map.
}

// Creates a new concurent map.
func NewConcurrentMap() *ConcurrentMap {
	return &ConcurrentMap{m: make(map[string]interface{})}
}

// Adds an element to the map.
func (m *ConcurrentMap) Add(key string, value interface{}) {
	m.Lock()
	defer m.Unlock()
	m.m[key] = value
}

// Returns the number of elements within the map.
func (m *ConcurrentMap) Count() int {
	m.RLock()
	defer m.RUnlock()
	return len(m.m)
}

// Retrives an element from map under given key.
func (m *ConcurrentMap) Get(key string) (interface{}, bool) {
	m.RLock()
	defer m.RUnlock()

	val, err := m.m[key]
	return val, err
}

// Looks up an item under specified key
func (m *ConcurrentMap) Has(key string) bool {
	m.RLock()
	defer m.RUnlock()
	_, ok := m.m[key]
	return ok
}

// Removes an element from the map.
func (m *ConcurrentMap) Remove(key string) {
	m.Lock()
	defer m.Unlock()
	delete(m.m, key)
}

// Clears map by constructing a new one.
func (m *ConcurrentMap) Clear() {
	m.Lock()
	defer m.Unlock()
	m.m = make(map[string]interface{})
}

// Checks if map is empty.
func (m *ConcurrentMap) IsEmpty() bool {
	return len(m.m) == 0
}

// Used by the Iter function to wrap two variables together over a channel,
type Tuple struct {
	Key string
	Val interface{}
}

// Returns an iterator which could be used in a for range loop.
func (m *ConcurrentMap) Iter() <-chan Tuple {
	//ch := make(chan interface{})
	ch := make(chan Tuple)
	go func() {
		m.RLock()
		defer m.RUnlock()
		for key, val := range m.m {
			ch <- Tuple{key, val}
		}
		close(ch)
	}()
	return ch
}

func (m ConcurrentMap) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		M map[string]interface{}
	}{M: m.m})
}
<|endoftext|>


<commit_before>package io

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"strings"
	"unicode"

	"github.com/gonum/plot/plotter"
)

type Output struct {
	w io.Writer

	caseN int
	input *Input

	output *bytes.Buffer

	periodicPrint       chan struct{}
	previousPeriodicInt int
	periodicCount       int
	prevPeriodicCount   int

	points plotter.XYs
}

func newOutput(w io.Writer) *Output {
	return &Output{
		w:             w,
		output:        &bytes.Buffer{},
		periodicPrint: make(chan struct{}, 10),
	}
}

func (o *Output) resetPeriodic() {
	for {
		select {
		case <-o.periodicPrint:
		default:
			return
		}
	}
}

func (o *Output) triggerPeriodic() {
	o.resetPeriodic()
	o.periodicPrint <- struct{}{}
}

func (o *Output) Print(a ...interface{}) {
	fmt.Fprint(o.output, a...)
}

func (o *Output) Printf(format string, a ...interface{}) {
	fmt.Fprintf(o.output, format, a...)
}

func (o *Output) Fatal(a ...interface{}) {
	o.DebugCase()
	log.Fatalln(a...)
}

func (o *Output) Fatalf(format string, a ...interface{}) {
	o.DebugCase()
	log.Fatalf(format, a...)
}

func (o *Output) Debug(a ...interface{}) {
	o.DebugCase()
	log.Println(a...)
}

func (o *Output) Debugf(format string, a ...interface{}) {
	o.DebugCase()
	log.Printf(format, a...)
}

func (o *Output) DebugCase() {
	log.Printf("Case #%d, input: %v, output: %q\n", o.caseN, o.input.currentCase(), string(o.output.Bytes()))
}

func (o *Output) Periodic(a ...interface{}) {
	select {
	case <-o.periodicPrint:
		log.Println(a...)
	default:
	}
}

func (o *Output) PeriodicInt(a int) {
	select {
	case <-o.periodicPrint:
		log.Println(a, "Rate =", a-o.previousPeriodicInt)
		o.previousPeriodicInt = a
	default:
	}
}

func (o *Output) PeriodicCount() {
	o.periodicCount++
	select {
	case <-o.periodicPrint:
		c := o.periodicCount
		log.Println(c, "Rate =", c-o.prevPeriodicCount)
		o.prevPeriodicCount = c
	default:
	}
}

func (o *Output) Periodicf(format string, a ...interface{}) {
	select {
	case <-o.periodicPrint:
		log.Println(a...)
	default:
	}
}

func (o *Output) Point(x, y float64) {
	o.points = append(o.points, struct{ X, Y float64 }{
		X: x,
		Y: y,
	})
}

func (o *Output) PointInt(x, y int) {
	o.Point(float64(x), float64(y))
}

func (o *Output) init(input *Input, caseN int) {
	o.input = input
	o.caseN = caseN
}

func (o *Output) flush() {
	if o.output.Len() <= 0 {
		o.Fatal("No output")
	}
	fmt.Fprintf(o.w, "Case #%d:", o.caseN)
	if !unicode.In(rune(o.output.Bytes()[0]), unicode.White_Space) {
		o.w.Write([]byte{' '})
	}
	o.w.Write(o.output.Bytes())
	if o.output.Bytes()[o.output.Len()-1] != '\n' {
		o.w.Write([]byte{'\n'})
	}
	o.output.Reset()
}

func (o *Output) assertOutput(fatal []bool, a ...interface{}) {
	output := o.Debug
	if len(fatal) > 0 && fatal[0] {
		output = o.Fatal
	}
	output(a...)
}

func (o *Output) assertOutputf(fatal []bool, format string, a ...interface{}) {
	outputf := o.Debugf
	if len(fatal) > 0 && fatal[0] {
		outputf = o.Fatalf
	}
	outputf(format, a...)
}

func (o *Output) AssertByteCount(a byte, count int, fatal ...bool) {
	for _, b := range o.output.Bytes() {
		if a == b {
			count--
		}
	}
	if count != 0 {
		o.assertOutputf(fatal, "AssertByteCount: byte: %q difference: %d", string(a), count*-1)
	}
}

func (o *Output) AssertCount(count int, fatal ...bool) {
	if o.output.Len() != count {
		o.assertOutput(fatal, "AssertCount: ", count)
	}
}

func (o *Output) AssertEqual(data string, fatal ...bool) {
	if strings.TrimSpace(string(o.output.Bytes())) != strings.TrimSpace(data) {
		o.assertOutputf(fatal, "Output should be: %q", data)
	}
}
<commit_msg>Fix Periodicf bug<commit_after>package io

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"strings"
	"unicode"

	"github.com/gonum/plot/plotter"
)

type Output struct {
	w io.Writer

	caseN int
	input *Input

	output *bytes.Buffer

	periodicPrint       chan struct{}
	previousPeriodicInt int
	periodicCount       int
	prevPeriodicCount   int

	points plotter.XYs
}

func newOutput(w io.Writer) *Output {
	return &Output{
		w:             w,
		output:        &bytes.Buffer{},
		periodicPrint: make(chan struct{}, 10),
	}
}

func (o *Output) resetPeriodic() {
	for {
		select {
		case <-o.periodicPrint:
		default:
			return
		}
	}
}

func (o *Output) triggerPeriodic() {
	o.resetPeriodic()
	o.periodicPrint <- struct{}{}
}

func (o *Output) Print(a ...interface{}) {
	fmt.Fprint(o.output, a...)
}

func (o *Output) Printf(format string, a ...interface{}) {
	fmt.Fprintf(o.output, format, a...)
}

func (o *Output) Fatal(a ...interface{}) {
	o.DebugCase()
	log.Fatalln(a...)
}

func (o *Output) Fatalf(format string, a ...interface{}) {
	o.DebugCase()
	log.Fatalf(format, a...)
}

func (o *Output) Debug(a ...interface{}) {
	o.DebugCase()
	log.Println(a...)
}

func (o *Output) Debugf(format string, a ...interface{}) {
	o.DebugCase()
	log.Printf(format, a...)
}

func (o *Output) DebugCase() {
	log.Printf("Case #%d, input: %v, output: %q\n", o.caseN, o.input.currentCase(), string(o.output.Bytes()))
}

func (o *Output) Periodic(a ...interface{}) {
	select {
	case <-o.periodicPrint:
		log.Println(a...)
	default:
	}
}

func (o *Output) PeriodicInt(a int) {
	select {
	case <-o.periodicPrint:
		log.Println(a, "Rate =", a-o.previousPeriodicInt)
		o.previousPeriodicInt = a
	default:
	}
}

func (o *Output) PeriodicCount() {
	o.periodicCount++
	select {
	case <-o.periodicPrint:
		c := o.periodicCount
		log.Println(c, "Rate =", c-o.prevPeriodicCount)
		o.prevPeriodicCount = c
	default:
	}
}

func (o *Output) Periodicf(format string, a ...interface{}) {
	select {
	case <-o.periodicPrint:
		log.Printf(format, a...)
	default:
	}
}

func (o *Output) Point(x, y float64) {
	o.points = append(o.points, struct{ X, Y float64 }{
		X: x,
		Y: y,
	})
}

func (o *Output) PointInt(x, y int) {
	o.Point(float64(x), float64(y))
}

func (o *Output) init(input *Input, caseN int) {
	o.input = input
	o.caseN = caseN
}

func (o *Output) flush() {
	if o.output.Len() <= 0 {
		o.Fatal("No output")
	}
	fmt.Fprintf(o.w, "Case #%d:", o.caseN)
	if !unicode.In(rune(o.output.Bytes()[0]), unicode.White_Space) {
		o.w.Write([]byte{' '})
	}
	o.w.Write(o.output.Bytes())
	if o.output.Bytes()[o.output.Len()-1] != '\n' {
		o.w.Write([]byte{'\n'})
	}
	o.output.Reset()
}

func (o *Output) assertOutput(fatal []bool, a ...interface{}) {
	output := o.Debug
	if len(fatal) > 0 && fatal[0] {
		output = o.Fatal
	}
	output(a...)
}

func (o *Output) assertOutputf(fatal []bool, format string, a ...interface{}) {
	outputf := o.Debugf
	if len(fatal) > 0 && fatal[0] {
		outputf = o.Fatalf
	}
	outputf(format, a...)
}

func (o *Output) AssertByteCount(a byte, count int, fatal ...bool) {
	for _, b := range o.output.Bytes() {
		if a == b {
			count--
		}
	}
	if count != 0 {
		o.assertOutputf(fatal, "AssertByteCount: byte: %q difference: %d", string(a), count*-1)
	}
}

func (o *Output) AssertCount(count int, fatal ...bool) {
	if o.output.Len() != count {
		o.assertOutput(fatal, "AssertCount: ", count)
	}
}

func (o *Output) AssertEqual(data string, fatal ...bool) {
	if strings.TrimSpace(string(o.output.Bytes())) != strings.TrimSpace(data) {
		o.assertOutputf(fatal, "Output should be: %q", data)
	}
}
<|endoftext|>


<commit_before>package metrics

import (
	"net/http"

	"github.com/prometheus/client_golang/prometheus/promhttp"
	"gopkg.in/errgo.v1"
	"gopkg.in/tomb.v2"

	log "hockeypuck/logrus"
)

type Metrics struct {
	s   *Settings
	mux *http.ServeMux
	t   tomb.Tomb
}

func NewMetrics(s *Settings) *Metrics {
	if s == nil {
		s = DefaultSettings()
	}

	m := &Metrics{
		s:   s,
		mux: http.NewServeMux(),
	}
	m.mux.Handle(m.s.MetricsPath, promhttp.Handler())

	return m
}

func (m *Metrics) Start() {
	m.t.Go(func() error {
		log.Info("metrics: starting")
		if err := http.ListenAndServe(m.s.MetricsAddr, m.mux); err != nil {
			log.Errorf("failed to serve metrics: %v", err)
			return err
		}
		return nil
	})
}

func (m *Metrics) Stop() {
	log.Info("metrics: stopping")
	m.t.Kill(nil)
	if err := m.t.Wait(); err != nil {
		log.Error(errgo.Details(err))
	}
	log.Info("metrics: stopped")
}
<commit_msg>metrics: shut down the server when the tomb is killed<commit_after>package metrics

import (
	"net/http"

	"github.com/prometheus/client_golang/prometheus/promhttp"
	"gopkg.in/errgo.v1"
	"gopkg.in/tomb.v2"

	log "hockeypuck/logrus"
)

type Metrics struct {
	s   *Settings
	srv *http.Server
	t   tomb.Tomb
}

func NewMetrics(s *Settings) *Metrics {
	if s == nil {
		s = DefaultSettings()
	}

	mux := http.NewServeMux()
	mux.Handle(s.MetricsPath, promhttp.Handler())

	return &Metrics{
		s: s,
		srv: &http.Server{
			Addr:    s.MetricsAddr,
			Handler: mux,
		},
	}
}

func (m *Metrics) Start() {
	m.t.Go(func() error {
		log.Info("metrics: starting")
		if err := m.srv.ListenAndServe(); err != nil {
			if err != http.ErrServerClosed {
				log.Errorf("failed to serve metrics: %v", err)
				return err
			}
		}
		return tomb.ErrDying
	})
	m.t.Go(func() error {
		<-m.t.Dying()
		return m.srv.Close()
	})
}

func (m *Metrics) Stop() {
	log.Info("metrics: stopping")
	m.t.Kill(nil)
	if err := m.t.Wait(); err != nil {
		log.Error(errgo.Details(err))
	}
	log.Info("metrics: stopped")
}
<|endoftext|>


<commit_before>package ipam

import (
	"fmt"
	"net/http"

	"github.com/gorilla/mux"

	"github.com/weaveworks/weave/common"
	"github.com/weaveworks/weave/common/docker"
	"github.com/weaveworks/weave/net/address"
)

func badRequest(w http.ResponseWriter, err error) {
	http.Error(w, err.Error(), http.StatusBadRequest)
	common.Log.Warningln("[allocator]:", err.Error())
}

func parseCIDR(w http.ResponseWriter, cidrStr string, net bool) (address.CIDR, bool) {
	var cidr address.CIDR
	var err error
	if net {
		cidr, err = ParseCIDRSubnet(cidrStr)
	} else {
		cidr, err = address.ParseCIDR(cidrStr)
	}
	if err != nil {
		badRequest(w, err)
		return address.CIDR{}, false
	}
	return cidr, true
}

func writeAddresses(w http.ResponseWriter, cidrs []address.CIDR) {
	for i, cidr := range cidrs {
		fmt.Fprint(w, cidr)
		if i < len(cidrs)-1 {
			w.Write([]byte{' '})
		}
	}
}

func hasBeenCancelled(dockerCli *docker.Client, closedChan <-chan bool, ident string, checkAlive bool) func() bool {
	return func() bool {
		select {
		case <-closedChan:
			return true
		default:
			res := checkAlive && dockerCli != nil && dockerCli.IsContainerNotRunning(ident)
			checkAlive = false // we check only once; if the container dies later we learn about that through events
			return res
		}
	}
}

func cancellationErr(w http.ResponseWriter, err error) bool {
	if _, ok := err.(*errorCancelled); ok {
		common.Log.Infoln("[allocator]:", err.Error())
		fmt.Fprint(w, "cancelled")
		return true
	}
	return false
}

func (alloc *Allocator) handleHTTPAllocate(dockerCli *docker.Client, w http.ResponseWriter, ident string, checkAlive bool, subnet address.CIDR) {
	addr, err := alloc.Allocate(ident, subnet, checkAlive,
		hasBeenCancelled(dockerCli, w.(http.CloseNotifier).CloseNotify(), ident, checkAlive))
	if err != nil {
		if !cancellationErr(w, err) {
			badRequest(w, err)
		}
		return
	}
	fmt.Fprintf(w, "%s/%d", addr, subnet.PrefixLen)
}

func (alloc *Allocator) handleHTTPClaim(dockerCli *docker.Client, w http.ResponseWriter, ident string, cidr address.CIDR, checkAlive, noErrorOnUnknown bool) {
	err := alloc.Claim(ident, cidr, checkAlive, noErrorOnUnknown,
		hasBeenCancelled(dockerCli, w.(http.CloseNotifier).CloseNotify(), ident, checkAlive))
	if err != nil {
		if !cancellationErr(w, err) {
			badRequest(w, fmt.Errorf("Unable to claim: %s", err))
		}
		return
	}
	w.WriteHeader(204)
}

// HandleHTTP wires up ipams HTTP endpoints to the provided mux.
func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CIDR, tracker string, dockerCli *docker.Client) {
	router.Methods("GET").Path("/ipinfo/defaultsubnet").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "%s", defaultSubnet)
	})

	router.Methods("PUT").Path("/ip/{id}/{ip}/{prefixlen}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		if cidr, ok := parseCIDR(w, vars["ip"]+"/"+vars["prefixlen"], false); ok {
			ident := vars["id"]
			checkAlive := r.FormValue("check-alive") == "true"
			noErrorOnUnknown := r.FormValue("noErrorOnUnknown") == "true"
			alloc.handleHTTPClaim(dockerCli, w, ident, cidr, checkAlive, noErrorOnUnknown)
		}
	})

	router.Methods("GET").Path("/ring").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		alloc.Prime()
	})

	router.Methods("GET").Path("/ip/{id}/{ip}/{prefixlen}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		if subnet, ok := parseCIDR(w, vars["ip"]+"/"+vars["prefixlen"], true); ok {
			cidrs, err := alloc.Lookup(vars["id"], subnet.HostRange())
			if err != nil {
				http.NotFound(w, r)
				return
			}
			writeAddresses(w, cidrs)
		}
	})

	router.Methods("GET").Path("/ip/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		addrs, err := alloc.Lookup(mux.Vars(r)["id"], defaultSubnet.HostRange())
		if err != nil {
			http.NotFound(w, r)
			return
		}
		writeAddresses(w, addrs)
	})

	router.Methods("POST").Path("/ip/{id}/{ip}/{prefixlen}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		if subnet, ok := parseCIDR(w, vars["ip"]+"/"+vars["prefixlen"], true); ok {
			alloc.handleHTTPAllocate(dockerCli, w, vars["id"], r.FormValue("check-alive") == "true", subnet)
		}
	})

	router.Methods("POST").Path("/ip/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		alloc.handleHTTPAllocate(dockerCli, w, vars["id"], r.FormValue("check-alive") == "true", defaultSubnet)
	})

	router.Methods("DELETE").Path("/ip/{id}/{ip}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		ident := vars["id"]
		ipStr := vars["ip"]
		if ip, err := address.ParseIP(ipStr); err != nil {
			badRequest(w, err)
			return
		} else if err := alloc.Free(ident, ip); err != nil {
			badRequest(w, fmt.Errorf("Unable to free: %s", err))
			return
		}

		w.WriteHeader(204)
	})

	router.Methods("DELETE").Path("/ip/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ident := mux.Vars(r)["id"]
		if err := alloc.Delete(ident); err != nil {
			badRequest(w, err)
			return
		}

		w.WriteHeader(204)
	})

	router.Methods("DELETE").Path("/peer").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		alloc.Shutdown()
		w.WriteHeader(204)
	})

	router.Methods("DELETE").Path("/peer/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ident := mux.Vars(r)["id"]
		transferred := alloc.AdminTakeoverRanges(ident)
		fmt.Fprintf(w, "%d IPs taken over from %s\n", transferred, ident)
	})

	router.Methods("GET").Path("/ipinfo/tracker").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, tracker)
	})
}
<commit_msg>Add GET /ip handler to return all IPAM mappings.<commit_after>package ipam

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/gorilla/mux"

	"github.com/weaveworks/weave/common"
	"github.com/weaveworks/weave/common/docker"
	"github.com/weaveworks/weave/net/address"
)

func badRequest(w http.ResponseWriter, err error) {
	http.Error(w, err.Error(), http.StatusBadRequest)
	common.Log.Warningln("[allocator]:", err.Error())
}

func parseCIDR(w http.ResponseWriter, cidrStr string, net bool) (address.CIDR, bool) {
	var cidr address.CIDR
	var err error
	if net {
		cidr, err = ParseCIDRSubnet(cidrStr)
	} else {
		cidr, err = address.ParseCIDR(cidrStr)
	}
	if err != nil {
		badRequest(w, err)
		return address.CIDR{}, false
	}
	return cidr, true
}

func writeAddresses(w http.ResponseWriter, cidrs []address.CIDR) {
	for i, cidr := range cidrs {
		fmt.Fprint(w, cidr)
		if i < len(cidrs)-1 {
			w.Write([]byte{' '})
		}
	}
}

func hasBeenCancelled(dockerCli *docker.Client, closedChan <-chan bool, ident string, checkAlive bool) func() bool {
	return func() bool {
		select {
		case <-closedChan:
			return true
		default:
			res := checkAlive && dockerCli != nil && dockerCli.IsContainerNotRunning(ident)
			checkAlive = false // we check only once; if the container dies later we learn about that through events
			return res
		}
	}
}

func cancellationErr(w http.ResponseWriter, err error) bool {
	if _, ok := err.(*errorCancelled); ok {
		common.Log.Infoln("[allocator]:", err.Error())
		fmt.Fprint(w, "cancelled")
		return true
	}
	return false
}

func (alloc *Allocator) handleHTTPAllocate(dockerCli *docker.Client, w http.ResponseWriter, ident string, checkAlive bool, subnet address.CIDR) {
	addr, err := alloc.Allocate(ident, subnet, checkAlive,
		hasBeenCancelled(dockerCli, w.(http.CloseNotifier).CloseNotify(), ident, checkAlive))
	if err != nil {
		if !cancellationErr(w, err) {
			badRequest(w, err)
		}
		return
	}
	fmt.Fprintf(w, "%s/%d", addr, subnet.PrefixLen)
}

func (alloc *Allocator) handleHTTPClaim(dockerCli *docker.Client, w http.ResponseWriter, ident string, cidr address.CIDR, checkAlive, noErrorOnUnknown bool) {
	err := alloc.Claim(ident, cidr, checkAlive, noErrorOnUnknown,
		hasBeenCancelled(dockerCli, w.(http.CloseNotifier).CloseNotify(), ident, checkAlive))
	if err != nil {
		if !cancellationErr(w, err) {
			badRequest(w, fmt.Errorf("Unable to claim: %s", err))
		}
		return
	}
	w.WriteHeader(204)
}

// HandleHTTP wires up ipams HTTP endpoints to the provided mux.
func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CIDR, tracker string, dockerCli *docker.Client) {
	router.Methods("GET").Path("/ipinfo/defaultsubnet").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "%s", defaultSubnet)
	})

	router.Methods("PUT").Path("/ip/{id}/{ip}/{prefixlen}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		if cidr, ok := parseCIDR(w, vars["ip"]+"/"+vars["prefixlen"], false); ok {
			ident := vars["id"]
			checkAlive := r.FormValue("check-alive") == "true"
			noErrorOnUnknown := r.FormValue("noErrorOnUnknown") == "true"
			alloc.handleHTTPClaim(dockerCli, w, ident, cidr, checkAlive, noErrorOnUnknown)
		}
	})

	router.Methods("GET").Path("/ring").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		alloc.Prime()
	})

	router.Methods("GET").Path("/ip/{id}/{ip}/{prefixlen}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		if subnet, ok := parseCIDR(w, vars["ip"]+"/"+vars["prefixlen"], true); ok {
			cidrs, err := alloc.Lookup(vars["id"], subnet.HostRange())
			if err != nil {
				http.NotFound(w, r)
				return
			}
			writeAddresses(w, cidrs)
		}
	})

	router.Methods("GET").Path("/ip/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		addrs, err := alloc.Lookup(mux.Vars(r)["id"], defaultSubnet.HostRange())
		if err != nil {
			http.NotFound(w, r)
			return
		}
		writeAddresses(w, addrs)
	})

	router.Methods("GET").Path("/ip").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		type mapping struct {
			ContainerID string   `json:"containerid"`
			Addrs       []string `json:"addrs"`
		}

		type mappings struct {
			Owned []mapping `json:"owned"`
		}

		resultChan := make(chan mappings)
		alloc.actionChan <- func() {
			ms := mappings{}
			for containerid, d := range alloc.owned {
				m := mapping{
					ContainerID: containerid,
					Addrs:       []string{},
				}
				for _, addr := range d.Cidrs {
					m.Addrs = append(m.Addrs, addr.String())
				}
				ms.Owned = append(ms.Owned, m)
			}
			resultChan <- ms
		}
		ms := <-resultChan

		w.Header().Set("Content-Type", "application/json")
		if err := json.NewEncoder(w).Encode(ms); err != nil {
			common.Log.Warningln("[allocator]:", err.Error())
		}
	})

	router.Methods("POST").Path("/ip/{id}/{ip}/{prefixlen}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		if subnet, ok := parseCIDR(w, vars["ip"]+"/"+vars["prefixlen"], true); ok {
			alloc.handleHTTPAllocate(dockerCli, w, vars["id"], r.FormValue("check-alive") == "true", subnet)
		}
	})

	router.Methods("POST").Path("/ip/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		alloc.handleHTTPAllocate(dockerCli, w, vars["id"], r.FormValue("check-alive") == "true", defaultSubnet)
	})

	router.Methods("DELETE").Path("/ip/{id}/{ip}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		ident := vars["id"]
		ipStr := vars["ip"]
		if ip, err := address.ParseIP(ipStr); err != nil {
			badRequest(w, err)
			return
		} else if err := alloc.Free(ident, ip); err != nil {
			badRequest(w, fmt.Errorf("Unable to free: %s", err))
			return
		}

		w.WriteHeader(204)
	})

	router.Methods("DELETE").Path("/ip/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ident := mux.Vars(r)["id"]
		if err := alloc.Delete(ident); err != nil {
			badRequest(w, err)
			return
		}

		w.WriteHeader(204)
	})

	router.Methods("DELETE").Path("/peer").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		alloc.Shutdown()
		w.WriteHeader(204)
	})

	router.Methods("DELETE").Path("/peer/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ident := mux.Vars(r)["id"]
		transferred := alloc.AdminTakeoverRanges(ident)
		fmt.Fprintf(w, "%d IPs taken over from %s\n", transferred, ident)
	})

	router.Methods("GET").Path("/ipinfo/tracker").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, tracker)
	})
}
<|endoftext|>