<commit_before>// Copyright 2015 realglobe, Inc.
//
// 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.

// TA 間連携要請先仲介エンドポイント。
package coopto

import (
	"github.com/realglobe-Inc/edo-id-provider/database/account"
	"github.com/realglobe-Inc/edo-id-provider/database/consent"
	"github.com/realglobe-Inc/edo-id-provider/database/coopcode"
	jtidb "github.com/realglobe-Inc/edo-id-provider/database/jti"
	keydb "github.com/realglobe-Inc/edo-id-provider/database/key"
	"github.com/realglobe-Inc/edo-id-provider/database/pairwise"
	"github.com/realglobe-Inc/edo-id-provider/database/sector"
	"github.com/realglobe-Inc/edo-id-provider/database/token"
	"github.com/realglobe-Inc/edo-id-provider/idputil"
	tadb "github.com/realglobe-Inc/edo-idp-selector/database/ta"
	idperr "github.com/realglobe-Inc/edo-idp-selector/error"
	requtil "github.com/realglobe-Inc/edo-idp-selector/request"
	"github.com/realglobe-Inc/edo-lib/jwt"
	"github.com/realglobe-Inc/edo-lib/jwt/audience"
	logutil "github.com/realglobe-Inc/edo-lib/log"
	"github.com/realglobe-Inc/edo-lib/rand"
	"github.com/realglobe-Inc/edo-lib/server"
	"github.com/realglobe-Inc/go-lib/erro"
	"github.com/realglobe-Inc/go-lib/rglog/level"
	"net/http"
	"time"
)

type handler struct {
	stopper *server.Stopper

	selfId string
	sigAlg string
	sigKid string

	pathCoopTo string

	pwSaltLen  int
	tokLen     int
	tokExpIn   time.Duration
	tokDbExpIn time.Duration
	jtiExpIn   time.Duration

	keyDb  keydb.Db
	acntDb account.Db
	consDb consent.Db
	taDb   tadb.Db
	sectDb sector.Db
	pwDb   pairwise.Db
	codDb  coopcode.Db
	tokDb  token.Db
	jtiDb  jtidb.Db

	idGen rand.Generator
}

func New(
	stopper *server.Stopper,
	selfId string,
	sigAlg string,
	sigKid string,
	pathCoopTo string,
	pwSaltLen int,
	tokLen int,
	tokExpIn time.Duration,
	tokDbExpIn time.Duration,
	jtiExpIn time.Duration,
	keyDb keydb.Db,
	acntDb account.Db,
	consDb consent.Db,
	taDb tadb.Db,
	sectDb sector.Db,
	pwDb pairwise.Db,
	codDb coopcode.Db,
	tokDb token.Db,
	jtiDb jtidb.Db,
	idGen rand.Generator,
) http.Handler {
	return &handler{
		stopper:    stopper,
		selfId:     selfId,
		sigAlg:     sigAlg,
		sigKid:     sigKid,
		pathCoopTo: pathCoopTo,
		pwSaltLen:  pwSaltLen,
		tokLen:     tokLen,
		tokExpIn:   tokExpIn,
		tokDbExpIn: tokDbExpIn,
		jtiExpIn:   jtiExpIn,
		keyDb:      keyDb,
		acntDb:     acntDb,
		consDb:     consDb,
		taDb:       taDb,
		sectDb:     sectDb,
		pwDb:       pwDb,
		codDb:      codDb,
		tokDb:      tokDb,
		jtiDb:      jtiDb,
		idGen:      idGen,
	}
}

func (this *handler) PairwiseSaltLength() int     { return this.pwSaltLen }
func (this *handler) SectorDb() sector.Db         { return this.sectDb }
func (this *handler) PairwiseDb() pairwise.Db     { return this.pwDb }
func (this *handler) IdGenerator() rand.Generator { return this.idGen }

// 主にテスト用。
func (this *handler) SetSelfId(selfId string) {
	this.selfId = selfId
}

func (this *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var sender *requtil.Request

	// panic 対策。
	defer func() {
		if rcv := recover(); rcv != nil {
			idperr.RespondApiError(w, r, erro.New(rcv), sender)
			return
		}
	}()

	if this.stopper != nil {
		this.stopper.Stop()
		defer this.stopper.Unstop()
	}

	//////////////////////////////
	server.LogRequest(level.DEBUG, r, true)
	//////////////////////////////

	sender = requtil.Parse(r, "")
	log.Info(sender, ": Received cooperation-to request")
	defer log.Info(sender, ": Handled cooperation-to request")

	if err := this.serve(w, r, sender); err != nil {
		idperr.RespondApiError(w, r, erro.Wrap(err), sender)
		return
	}
}

func (this *handler) serve(w http.ResponseWriter, r *http.Request, sender *requtil.Request) error {
	req, err := parseRequest(r)
	if err != nil {
		return erro.Wrap(idperr.New(idperr.Invalid_request, erro.Unwrap(err).Error(), http.StatusBadRequest, err))
	} else if req.grantType() != tagCooperation_code {
		return erro.Wrap(idperr.New(idperr.Invalid_request, "invalid grant type "+req.grantType(), http.StatusBadRequest, nil))
	}

	cod, err := this.codDb.Get(req.code())
	if err != nil {
		return erro.Wrap(err)
	} else if cod == nil {
		return erro.Wrap(idperr.New(idperr.Invalid_grant, "declared code is not exist", http.StatusForbidden, nil))
	}

	log.Debug(sender, ": Declared code is exist")

	taTo, err := this.taDb.Get(cod.ToTa())
	if err != nil {
		return erro.Wrap(err)
	} else if taTo == nil {
		return erro.Wrap(idperr.New(idperr.Invalid_request, "to-TA "+cod.ToTa()+" is not exist", http.StatusBadRequest, nil))
	} else if jti, err := idputil.VerifyAssertion(req.taAssertion(), taTo.Id(), taTo.Keys(), this.selfId+this.pathCoopTo); err != nil {
		return erro.Wrap(idperr.New(idperr.Invalid_client, erro.Unwrap(err).Error(), http.StatusBadRequest, err))
	} else if ok, err := this.jtiDb.SaveIfAbsent(jti); err != nil {
		return erro.Wrap(err)
	} else if !ok {
		return erro.New("JWT ID overlaps")
	}

	log.Debug(sender, ": Verified to-TA "+taTo.Id())

	tagToId := map[string]string{}
	for _, acnt := range cod.Accounts() {
		tagToId[acnt.Tag()] = acnt.Id()
	}
	for tag := range req.subClaims() {
		if tagToId[tag] == "" {
			return erro.Wrap(idperr.New(idperr.Invalid_grant, "invalid user tag "+tag, http.StatusForbidden, nil))
		}
	}

	if cod.SourceToken() != "" {
		return this.serveAsMain(w, r, req, cod, taTo, sender)
	} else {
		return this.serveAsSub(w, r, req, cod, taTo, sender)
	}
}

func (this *handler) serveAsMain(w http.ResponseWriter, r *http.Request, req *request, cod *coopcode.Element, taTo tadb.Element, sender *requtil.Request) error {
	ids := map[string]map[string]interface{}{}

	var tok *token.Element
	{
		acnt, err := this.acntDb.Get(cod.Account().Id())
		if err != nil {
			return erro.Wrap(err)
		} else if acnt == nil {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "accout "+cod.Account().Tag()+" is not exist", http.StatusForbidden, nil))
		} else if err := idputil.SetSub(this, acnt, taTo); err != nil {
			return erro.Wrap(err)
		} else if err := idputil.CheckClaims(acnt, req.claims().IdTokenEntries()); err != nil {
			return erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
		} else if err := idputil.CheckClaims(acnt, req.claims().AccountEntries()); err != nil {
			return erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
		}
		cons, err := this.consDb.Get(acnt.Id(), taTo.Id())
		if err != nil {
			return erro.Wrap(err)
		} else if cons == nil {
			return erro.Wrap(idperr.New(idperr.Access_denied, "accout "+cod.Account().Tag()+" allowwd nothing", http.StatusForbidden, nil))
		}

		scop, err := idputil.ProvidedScopes(cons.Scope(), cod.Scope())
		if err != nil {
			return erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
		}

		acntAttrs, err := idputil.ProvidedAttributes(cons.Scope(), cons.Attribute(), scop, req.claims().AccountEntries())
		if err != nil {
			return erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
		}
		tok = token.New(
			this.idGen.String(this.tokLen),
			cod.TokenExpires(),
			cod.Account().Id(),
			cod.Scope(),
			acntAttrs,
			taTo.Id(),
		)
		idTokAttrs, err := idputil.ProvidedAttributes(cons.Scope(), cons.Attribute(), nil, req.claims().IdTokenEntries())
		if err != nil {
			return erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
		}
		m := map[string]interface{}{}
		for attr := range idTokAttrs {
			m[attr] = acnt.Attribute(attr)
		}
		ids[cod.Account().Tag()] = m

		log.Debug(sender, ": Account "+cod.Account().Tag()+" allowed required attributes")
	}

	for _, codAcnt := range cod.Accounts() {
		acnt, err := this.acntDb.Get(codAcnt.Id())
		if err != nil {
			return erro.Wrap(err)
		} else if acnt == nil {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "accout "+codAcnt.Tag()+" is not exist", http.StatusForbidden, nil))
		} else if err := idputil.SetSub(this, acnt, taTo); err != nil {
			return erro.Wrap(err)
		} else if err := idputil.CheckClaims(acnt, req.subClaims()[codAcnt.Tag()]); err != nil {
			return erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
		}
		cons, err := this.consDb.Get(acnt.Id(), taTo.Id())
		if err != nil {
			return erro.Wrap(err)
		} else if cons == nil {
			return erro.Wrap(idperr.New(idperr.Access_denied, "account "+codAcnt.Tag()+" allowed nothing", http.StatusForbidden, nil))
		}

		attrs, err := idputil.ProvidedAttributes(cons.Scope(), cons.Attribute(), nil, req.subClaims()[codAcnt.Tag()])
		if err != nil {
			return erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
		}
		m := map[string]interface{}{}
		for attr := range attrs {
			m[attr] = acnt.Attribute(attr)
		}
		ids[codAcnt.Tag()] = m

		log.Debug(sender, ": Account "+codAcnt.Tag()+" allowed required attributes")
	}

	now := time.Now()
	jt := jwt.New()
	jt.SetHeader(tagAlg, this.sigAlg)
	if this.sigKid != "" {
		jt.SetHeader(tagKid, this.sigKid)
	}
	jt.SetClaim(tagIss, this.selfId)
	jt.SetClaim(tagSub, cod.FromTa())
	jt.SetClaim(tagAud, audience.New(taTo.Id()))
	jt.SetClaim(tagExp, now.Add(this.jtiExpIn).Unix())
	jt.SetClaim(tagIat, now.Unix())
	jt.SetClaim(tagIds, ids)
	if keys, err := this.keyDb.Get(); err != nil {
		return erro.Wrap(err)
	} else if err := jt.Sign(keys); err != nil {
		return erro.Wrap(err)
	}
	idsTok, err := jt.Encode()
	if err != nil {
		return erro.Wrap(err)
	}

	// TODO アクセストークンを元になったアクセストークンに紐付ける。
	log.Warn(sender, ": Not linked token "+logutil.Mosaic(tok.Id())+" to source token "+logutil.Mosaic(cod.SourceToken()))

	// アクセストークンを保存する。
	if err := this.tokDb.Save(tok, tok.Expires().Add(this.tokDbExpIn-this.tokExpIn)); err != nil {
		return erro.Wrap(err)
	}

	log.Debug(sender, ": Saved token "+logutil.Mosaic(tok.Id()))

	respParams := map[string]interface{}{
		tagAccess_token: tok.Id(),
		tagToken_type:   tagBearer,
		tagIds_token:    string(idsTok),
	}
	if !tok.Expires().IsZero() {
		respParams[tagExpires_in] = int64(tok.Expires().Sub(time.Now()).Seconds())
	}
	if len(tok.Scope()) > 0 {
		respParams[tagScope] = requtil.ValueSetForm(tok.Scope())
	}
	return idputil.RespondJson(w, respParams)
}

func (this *handler) serveAsSub(w http.ResponseWriter, r *http.Request, req *request, cod *coopcode.Element, taTo tadb.Element, sender *requtil.Request) error {
	panic("not yet implemented")
}
<commit_msg>仲介コードが無かったときのエラーステータスを 403 から 400 に変更<commit_after>// Copyright 2015 realglobe, Inc.
//
// 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.

// TA 間連携要請先仲介エンドポイント。
package coopto

import (
	"github.com/realglobe-Inc/edo-id-provider/database/account"
	"github.com/realglobe-Inc/edo-id-provider/database/consent"
	"github.com/realglobe-Inc/edo-id-provider/database/coopcode"
	jtidb "github.com/realglobe-Inc/edo-id-provider/database/jti"
	keydb "github.com/realglobe-Inc/edo-id-provider/database/key"
	"github.com/realglobe-Inc/edo-id-provider/database/pairwise"
	"github.com/realglobe-Inc/edo-id-provider/database/sector"
	"github.com/realglobe-Inc/edo-id-provider/database/token"
	"github.com/realglobe-Inc/edo-id-provider/idputil"
	tadb "github.com/realglobe-Inc/edo-idp-selector/database/ta"
	idperr "github.com/realglobe-Inc/edo-idp-selector/error"
	requtil "github.com/realglobe-Inc/edo-idp-selector/request"
	"github.com/realglobe-Inc/edo-lib/jwt"
	"github.com/realglobe-Inc/edo-lib/jwt/audience"
	logutil "github.com/realglobe-Inc/edo-lib/log"
	"github.com/realglobe-Inc/edo-lib/rand"
	"github.com/realglobe-Inc/edo-lib/server"
	"github.com/realglobe-Inc/go-lib/erro"
	"github.com/realglobe-Inc/go-lib/rglog/level"
	"net/http"
	"time"
)

type handler struct {
	stopper *server.Stopper

	selfId string
	sigAlg string
	sigKid string

	pathCoopTo string

	pwSaltLen  int
	tokLen     int
	tokExpIn   time.Duration
	tokDbExpIn time.Duration
	jtiExpIn   time.Duration

	keyDb  keydb.Db
	acntDb account.Db
	consDb consent.Db
	taDb   tadb.Db
	sectDb sector.Db
	pwDb   pairwise.Db
	codDb  coopcode.Db
	tokDb  token.Db
	jtiDb  jtidb.Db

	idGen rand.Generator
}

func New(
	stopper *server.Stopper,
	selfId string,
	sigAlg string,
	sigKid string,
	pathCoopTo string,
	pwSaltLen int,
	tokLen int,
	tokExpIn time.Duration,
	tokDbExpIn time.Duration,
	jtiExpIn time.Duration,
	keyDb keydb.Db,
	acntDb account.Db,
	consDb consent.Db,
	taDb tadb.Db,
	sectDb sector.Db,
	pwDb pairwise.Db,
	codDb coopcode.Db,
	tokDb token.Db,
	jtiDb jtidb.Db,
	idGen rand.Generator,
) http.Handler {
	return &handler{
		stopper:    stopper,
		selfId:     selfId,
		sigAlg:     sigAlg,
		sigKid:     sigKid,
		pathCoopTo: pathCoopTo,
		pwSaltLen:  pwSaltLen,
		tokLen:     tokLen,
		tokExpIn:   tokExpIn,
		tokDbExpIn: tokDbExpIn,
		jtiExpIn:   jtiExpIn,
		keyDb:      keyDb,
		acntDb:     acntDb,
		consDb:     consDb,
		taDb:       taDb,
		sectDb:     sectDb,
		pwDb:       pwDb,
		codDb:      codDb,
		tokDb:      tokDb,
		jtiDb:      jtiDb,
		idGen:      idGen,
	}
}

func (this *handler) PairwiseSaltLength() int     { return this.pwSaltLen }
func (this *handler) SectorDb() sector.Db         { return this.sectDb }
func (this *handler) PairwiseDb() pairwise.Db     { return this.pwDb }
func (this *handler) IdGenerator() rand.Generator { return this.idGen }

// 主にテスト用。
func (this *handler) SetSelfId(selfId string) {
	this.selfId = selfId
}

func (this *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var sender *requtil.Request

	// panic 対策。
	defer func() {
		if rcv := recover(); rcv != nil {
			idperr.RespondApiError(w, r, erro.New(rcv), sender)
			return
		}
	}()

	if this.stopper != nil {
		this.stopper.Stop()
		defer this.stopper.Unstop()
	}

	//////////////////////////////
	server.LogRequest(level.DEBUG, r, true)
	//////////////////////////////

	sender = requtil.Parse(r, "")
	log.Info(sender, ": Received cooperation-to request")
	defer log.Info(sender, ": Handled cooperation-to request")

	if err := this.serve(w, r, sender); err != nil {
		idperr.RespondApiError(w, r, erro.Wrap(err), sender)
		return
	}
}

func (this *handler) serve(w http.ResponseWriter, r *http.Request, sender *requtil.Request) error {
	req, err := parseRequest(r)
	if err != nil {
		return erro.Wrap(idperr.New(idperr.Invalid_request, erro.Unwrap(err).Error(), http.StatusBadRequest, err))
	} else if req.grantType() != tagCooperation_code {
		return erro.Wrap(idperr.New(idperr.Invalid_request, "invalid grant type "+req.grantType(), http.StatusBadRequest, nil))
	}

	cod, err := this.codDb.Get(req.code())
	if err != nil {
		return erro.Wrap(err)
	} else if cod == nil {
		return erro.Wrap(idperr.New(idperr.Invalid_grant, "declared code is not exist", http.StatusBadRequest, nil))
	}

	log.Debug(sender, ": Declared code is exist")

	taTo, err := this.taDb.Get(cod.ToTa())
	if err != nil {
		return erro.Wrap(err)
	} else if taTo == nil {
		return erro.Wrap(idperr.New(idperr.Invalid_request, "to-TA "+cod.ToTa()+" is not exist", http.StatusBadRequest, nil))
	} else if jti, err := idputil.VerifyAssertion(req.taAssertion(), taTo.Id(), taTo.Keys(), this.selfId+this.pathCoopTo); err != nil {
		return erro.Wrap(idperr.New(idperr.Invalid_client, erro.Unwrap(err).Error(), http.StatusBadRequest, err))
	} else if ok, err := this.jtiDb.SaveIfAbsent(jti); err != nil {
		return erro.Wrap(err)
	} else if !ok {
		return erro.New("JWT ID overlaps")
	}

	log.Debug(sender, ": Verified to-TA "+taTo.Id())

	tagToId := map[string]string{}
	for _, acnt := range cod.Accounts() {
		tagToId[acnt.Tag()] = acnt.Id()
	}
	for tag := range req.subClaims() {
		if tagToId[tag] == "" {
			return erro.Wrap(idperr.New(idperr.Invalid_grant, "invalid user tag "+tag, http.StatusForbidden, nil))
		}
	}

	if cod.SourceToken() != "" {
		return this.serveAsMain(w, r, req, cod, taTo, sender)
	} else {
		return this.serveAsSub(w, r, req, cod, taTo, sender)
	}
}

func (this *handler) serveAsMain(w http.ResponseWriter, r *http.Request, req *request, cod *coopcode.Element, taTo tadb.Element, sender *requtil.Request) error {
	ids := map[string]map[string]interface{}{}

	var tok *token.Element
	{
		acnt, err := this.acntDb.Get(cod.Account().Id())
		if err != nil {
			return erro.Wrap(err)
		} else if acnt == nil {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "accout "+cod.Account().Tag()+" is not exist", http.StatusForbidden, nil))
		} else if err := idputil.SetSub(this, acnt, taTo); err != nil {
			return erro.Wrap(err)
		} else if err := idputil.CheckClaims(acnt, req.claims().IdTokenEntries()); err != nil {
			return erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
		} else if err := idputil.CheckClaims(acnt, req.claims().AccountEntries()); err != nil {
			return erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
		}
		cons, err := this.consDb.Get(acnt.Id(), taTo.Id())
		if err != nil {
			return erro.Wrap(err)
		} else if cons == nil {
			return erro.Wrap(idperr.New(idperr.Access_denied, "accout "+cod.Account().Tag()+" allowwd nothing", http.StatusForbidden, nil))
		}

		scop, err := idputil.ProvidedScopes(cons.Scope(), cod.Scope())
		if err != nil {
			return erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
		}

		acntAttrs, err := idputil.ProvidedAttributes(cons.Scope(), cons.Attribute(), scop, req.claims().AccountEntries())
		if err != nil {
			return erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
		}
		tok = token.New(
			this.idGen.String(this.tokLen),
			cod.TokenExpires(),
			cod.Account().Id(),
			cod.Scope(),
			acntAttrs,
			taTo.Id(),
		)
		idTokAttrs, err := idputil.ProvidedAttributes(cons.Scope(), cons.Attribute(), nil, req.claims().IdTokenEntries())
		if err != nil {
			return erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
		}
		m := map[string]interface{}{}
		for attr := range idTokAttrs {
			m[attr] = acnt.Attribute(attr)
		}
		ids[cod.Account().Tag()] = m

		log.Debug(sender, ": Account "+cod.Account().Tag()+" allowed required attributes")
	}

	for _, codAcnt := range cod.Accounts() {
		acnt, err := this.acntDb.Get(codAcnt.Id())
		if err != nil {
			return erro.Wrap(err)
		} else if acnt == nil {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "accout "+codAcnt.Tag()+" is not exist", http.StatusForbidden, nil))
		} else if err := idputil.SetSub(this, acnt, taTo); err != nil {
			return erro.Wrap(err)
		} else if err := idputil.CheckClaims(acnt, req.subClaims()[codAcnt.Tag()]); err != nil {
			return erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
		}
		cons, err := this.consDb.Get(acnt.Id(), taTo.Id())
		if err != nil {
			return erro.Wrap(err)
		} else if cons == nil {
			return erro.Wrap(idperr.New(idperr.Access_denied, "account "+codAcnt.Tag()+" allowed nothing", http.StatusForbidden, nil))
		}

		attrs, err := idputil.ProvidedAttributes(cons.Scope(), cons.Attribute(), nil, req.subClaims()[codAcnt.Tag()])
		if err != nil {
			return erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
		}
		m := map[string]interface{}{}
		for attr := range attrs {
			m[attr] = acnt.Attribute(attr)
		}
		ids[codAcnt.Tag()] = m

		log.Debug(sender, ": Account "+codAcnt.Tag()+" allowed required attributes")
	}

	now := time.Now()
	jt := jwt.New()
	jt.SetHeader(tagAlg, this.sigAlg)
	if this.sigKid != "" {
		jt.SetHeader(tagKid, this.sigKid)
	}
	jt.SetClaim(tagIss, this.selfId)
	jt.SetClaim(tagSub, cod.FromTa())
	jt.SetClaim(tagAud, audience.New(taTo.Id()))
	jt.SetClaim(tagExp, now.Add(this.jtiExpIn).Unix())
	jt.SetClaim(tagIat, now.Unix())
	jt.SetClaim(tagIds, ids)
	if keys, err := this.keyDb.Get(); err != nil {
		return erro.Wrap(err)
	} else if err := jt.Sign(keys); err != nil {
		return erro.Wrap(err)
	}
	idsTok, err := jt.Encode()
	if err != nil {
		return erro.Wrap(err)
	}

	// TODO アクセストークンを元になったアクセストークンに紐付ける。
	log.Warn(sender, ": Not linked token "+logutil.Mosaic(tok.Id())+" to source token "+logutil.Mosaic(cod.SourceToken()))

	// アクセストークンを保存する。
	if err := this.tokDb.Save(tok, tok.Expires().Add(this.tokDbExpIn-this.tokExpIn)); err != nil {
		return erro.Wrap(err)
	}

	log.Debug(sender, ": Saved token "+logutil.Mosaic(tok.Id()))

	respParams := map[string]interface{}{
		tagAccess_token: tok.Id(),
		tagToken_type:   tagBearer,
		tagIds_token:    string(idsTok),
	}
	if !tok.Expires().IsZero() {
		respParams[tagExpires_in] = int64(tok.Expires().Sub(time.Now()).Seconds())
	}
	if len(tok.Scope()) > 0 {
		respParams[tagScope] = requtil.ValueSetForm(tok.Scope())
	}
	return idputil.RespondJson(w, respParams)
}

func (this *handler) serveAsSub(w http.ResponseWriter, r *http.Request, req *request, cod *coopcode.Element, taTo tadb.Element, sender *requtil.Request) error {
	panic("not yet implemented")
}
<|endoftext|>


<commit_before>package fcm

import (
	"errors"
	"github.com/Bogh/gcm"
	log "github.com/Sirupsen/logrus"
	"github.com/smancke/guble/protocol"
	"github.com/smancke/guble/server/router"
	"github.com/smancke/guble/server/store"
	"strconv"
	"strings"
	"time"
)

const (
	// default subscription channel buffer size
	subBufferSize = 50

	// applicationIDKey is the key name set on the route params to identify the application
	applicationIDKey = "device_id"

	// userIDKey is the key name set on the route params to identify the user
	userIDKey = "user_id"
)

var (
	errIgnoreMessage        = errors.New("Message ignored")
	errSubscriptionExists   = errors.New("Subscription exists")
	errSubscriptionReplaced = errors.New("Subscription replaced")
)

// subscription represents a FCM subscription
type subscription struct {
	connector *Connector
	route     *router.Route
	lastID    uint64 // Last sent message id

	logger *log.Entry
}

// initSubscription creates a subscription and adds it in router/kvstore then starts listening for messages
func initSubscription(connector *Connector, topic, userID, fcmID string, lastID uint64, store bool) (*subscription, error) {
	route := router.NewRoute(router.RouteConfig{
		RouteParams: router.RouteParams{userIDKey: userID, applicationIDKey: fcmID},
		Path:        protocol.Path(topic),
		ChannelSize: subBufferSize,
		Matcher:     subscriptionMatcher,
	})

	s := newSubscription(connector, route, lastID)
	if s.exists() {
		return nil, errSubscriptionExists
	}

	// add subscription to map
	s.connector.subscriptions[s.Key()] = s

	s.logger.Debug("New subscription")
	if store {
		if err := s.store(); err != nil {
			return nil, err
		}
	}

	return s, s.restart()
}

func subscriptionMatcher(route, other router.RouteConfig, keys ...string) bool {
	return route.Path == other.Path && route.Get(applicationIDKey) == other.Get(applicationIDKey)
}

// newSubscription creates a subscription and returns the pointer
func newSubscription(connector *Connector, route *router.Route, lastID uint64) *subscription {
	subLogger := logger.WithFields(log.Fields{
		"fcmID":  route.Get(applicationIDKey),
		"userID": route.Get(userIDKey),
		"topic":  string(route.Path),
		"lastID": lastID,
	})
	if connector.cluster != nil {
		subLogger = subLogger.WithField("nodeID", connector.cluster.Config.ID)
	}

	return &subscription{
		connector: connector,
		route:     route,
		lastID:    lastID,
		logger:    subLogger,
	}
}

// exists returns true if the subscription is present with the same key in subscriptions map
func (s *subscription) exists() bool {
	_, ok := s.connector.subscriptions[s.Key()]
	return ok
}

// restart recreates the route and resubscribes
func (s *subscription) restart() error {
	s.route = router.NewRoute(router.RouteConfig{
		RouteParams: s.route.RouteParams,
		Path:        s.route.Path,
		ChannelSize: subBufferSize,
	})

	// subscribe to the router and start the loop
	return s.start()
}

// start loop to receive messages from route
func (s *subscription) start() error {
	s.route.FetchRequest = s.createFetchRequest()
	go s.subscriptionLoop()
	if err := s.route.Provide(s.connector.router, true); err != nil {
		return err
	}
	return nil
}

func (s *subscription) createFetchRequest() *store.FetchRequest {
	if s.lastID <= 0 {
		return nil
	}
	return store.NewFetchRequest("", s.lastID+1, 0, store.DirectionForward, -1)
}

// subscriptionLoop that will run in a goroutine and pipe messages from route to fcm
// Attention: in order for this loop to finish the route channel must stop sending messages
func (s *subscription) subscriptionLoop() {
	s.logger.Debug("Starting subscription loop")

	s.connector.wg.Add(1)
	defer func() {
		s.logger.Debug("Stopped subscription loop")
		s.connector.wg.Done()
	}()

	var (
		m      *protocol.Message
		opened = true
	)
	for opened {
		select {
		case m, opened = <-s.route.MessagesChannel():
			// TODO Bogdan This needs to be remade and we should gracefully shutdown
			// and wait for this channel to empty before stopping the loop
			select {
			case <-s.connector.stopC:
				return
			case <-time.After(5 * time.Millisecond):
			}

			if !opened {
				s.logger.Error("Route channel is closed")
				continue
			}

			if err := s.pipe(m); err != nil {
				// abandon route if the following 2 errors are met
				// the subscription has been replaced
				if err == errSubscriptionReplaced {
					return
				}
				// the subscription is not registered with FCM anymore
				if _, ok := err.(*jsonError); ok {
					return
				}

				s.logger.WithError(err).Error("Error pipelining message")
			}
		case <-s.connector.stopC:
			return
		}
	}

	// if route is closed and we are actually stopping then return
	if s.isStopping() {
		return
	}

	// assume that the route channel has been closed because of slow processing
	// try restarting, by fetching from lastId and then subscribing again
	if err := s.restart(); err != nil {
		if stoppingErr, ok := err.(*router.ModuleStoppingError); ok {
			s.logger.WithField("error", stoppingErr).Debug("Error restarting subscription")
		}
	}
}

// isStopping returns true if we are actually stopping the service
func (s *subscription) isStopping() bool {
	select {
	case <-s.connector.stopC:
		return true
	default:
	}
	return false
}

// Key returns a string that uniquely identifies this subscription
func (s *subscription) Key() string {
	return s.route.Key()
}

// pipe sends a message into the pipeline and waits for response saving the last id in the kvstore
func (s *subscription) pipe(m *protocol.Message) error {
	subscriptionMessage := newSubscriptionMessage(s, m)
	defer subscriptionMessage.closeChannels()

	s.connector.pipelineC <- subscriptionMessage

	// wait for response
	select {
	case response := <-subscriptionMessage.resultC:
		s.logger.WithField("messageID", m.ID).Debug("Delivered message to FCM")
		if err := s.setLastID(subscriptionMessage.message.ID); err != nil {
			return err
		}
		if response.Ok() {
			return nil
		}

		s.logger.WithField("count", response.Success).Debug("Handling FCM Error")
		if err := s.handleJSONError(response); err != nil {
			return err
		}

		if response.CanonicalIDs != 0 {
			// we only send to one receiver,
			// so we know that we can replace the old id with the first registration id (=canonical id)
			return s.replaceCanonical(response.Results[0].RegistrationID)
		}
		return nil

	case err := <-subscriptionMessage.errC:
		if err == errIgnoreMessage {
			s.logger.WithField("message", m).Info("Ignoring message")
			return nil
		}
		s.logger.WithField("error", err.Error()).Error("Error sending message to FCM")
		return err
	}
}

func (s *subscription) handleJSONError(response *gcm.Response) error {
	err := response.Error
	errText := err.Error()

	if errText == "" {
		return nil
	}
	if errText == "NotRegistered" {
		s.logger.Debug("Removing not registered FCM subscription")
		s.remove()
		return &jsonError{errText}
	}
	if errText == "InvalidRegistration" {
		s.logger.WithField("jsonError", errText).Error("Subscription is not registered")
	} else {
		s.logger.WithField("jsonError", errText).Error("Unexpected error while sending to FCM")
	}
	return nil
}

// replaceCanonical replaces subscription with canonical id,
// creates a new subscription but alters the route to have the new ApplicationID
func (s *subscription) replaceCanonical(newFCMID string) error {
	s.logger.WithField("newFCMID", newFCMID).Info("Replacing with FCM canonicalID")
	// delete current route from kvstore
	s.remove()

	// reuse the route but change the ApplicationID
	route := s.route
	route.Set(applicationIDKey, newFCMID)
	newSub := newSubscription(s.connector, route, s.lastID)

	if err := newSub.store(); err != nil {
		return err
	}
	newSub.start()
	return errSubscriptionReplaced
}

func (s *subscription) setLastID(ID uint64) error {
	s.lastID = ID
	// update KV when last id is set
	return s.store()
}

// store data in kvstore
func (s *subscription) store() error {
	s.logger.WithField("lastID", s.lastID).Debug("Storing subscription")
	applicationID := s.route.Get(applicationIDKey)
	err := s.connector.kvStore.Put(schema, applicationID, s.bytes())
	if err != nil {
		s.logger.WithError(err).Error("Error storing in KVStore")
	}
	return err
}

// bytes returns the data to store in kvStore
func (s *subscription) bytes() []byte {
	return []byte(strings.Join([]string{
		s.route.Get(userIDKey),
		string(s.route.Path),
		strconv.FormatUint(s.lastID, 10),
	}, ":"))
}

// remove unsubscribes from router, delete from connector's subscriptions, and remove from KVStore
func (s *subscription) remove() *subscription {
	s.logger.Debug("Removing subscription")
	s.connector.router.Unsubscribe(s.route)
	delete(s.connector.subscriptions, s.Key())
	s.connector.kvStore.Delete(schema, s.Key())
	return s
}
<commit_msg>refactored error-handling in FCM<commit_after>package fcm

import (
	"errors"
	"github.com/Bogh/gcm"
	log "github.com/Sirupsen/logrus"
	"github.com/smancke/guble/protocol"
	"github.com/smancke/guble/server/router"
	"github.com/smancke/guble/server/store"
	"strconv"
	"strings"
	"time"
)

const (
	// default subscription channel buffer size
	subBufferSize = 50

	// applicationIDKey is the key name set on the route params to identify the application
	applicationIDKey = "device_id"

	// userIDKey is the key name set on the route params to identify the user
	userIDKey = "user_id"
)

var (
	errIgnoreMessage        = errors.New("Message ignored")
	errSubscriptionExists   = errors.New("Subscription exists")
	errSubscriptionReplaced = errors.New("Subscription replaced")
)

// subscription represents a FCM subscription
type subscription struct {
	connector *Connector
	route     *router.Route
	lastID    uint64 // Last sent message id

	logger *log.Entry
}

// initSubscription creates a subscription and adds it in router/kvstore then starts listening for messages
func initSubscription(connector *Connector, topic, userID, fcmID string, lastID uint64, store bool) (*subscription, error) {
	route := router.NewRoute(router.RouteConfig{
		RouteParams: router.RouteParams{userIDKey: userID, applicationIDKey: fcmID},
		Path:        protocol.Path(topic),
		ChannelSize: subBufferSize,
		Matcher:     subscriptionMatcher,
	})

	s := newSubscription(connector, route, lastID)
	if s.exists() {
		return nil, errSubscriptionExists
	}

	// add subscription to map
	s.connector.subscriptions[s.Key()] = s

	s.logger.Debug("New subscription")
	if store {
		if err := s.store(); err != nil {
			return nil, err
		}
	}

	return s, s.restart()
}

func subscriptionMatcher(route, other router.RouteConfig, keys ...string) bool {
	return route.Path == other.Path && route.Get(applicationIDKey) == other.Get(applicationIDKey)
}

// newSubscription creates a subscription and returns the pointer
func newSubscription(connector *Connector, route *router.Route, lastID uint64) *subscription {
	subLogger := logger.WithFields(log.Fields{
		"fcmID":  route.Get(applicationIDKey),
		"userID": route.Get(userIDKey),
		"topic":  string(route.Path),
		"lastID": lastID,
	})
	if connector.cluster != nil {
		subLogger = subLogger.WithField("nodeID", connector.cluster.Config.ID)
	}

	return &subscription{
		connector: connector,
		route:     route,
		lastID:    lastID,
		logger:    subLogger,
	}
}

// exists returns true if the subscription is present with the same key in subscriptions map
func (s *subscription) exists() bool {
	_, ok := s.connector.subscriptions[s.Key()]
	return ok
}

// restart recreates the route and resubscribes
func (s *subscription) restart() error {
	s.route = router.NewRoute(router.RouteConfig{
		RouteParams: s.route.RouteParams,
		Path:        s.route.Path,
		ChannelSize: subBufferSize,
	})

	// subscribe to the router and start the loop
	return s.start()
}

// start loop to receive messages from route
func (s *subscription) start() error {
	s.route.FetchRequest = s.createFetchRequest()
	go s.subscriptionLoop()
	if err := s.route.Provide(s.connector.router, true); err != nil {
		return err
	}
	return nil
}

func (s *subscription) createFetchRequest() *store.FetchRequest {
	if s.lastID <= 0 {
		return nil
	}
	return store.NewFetchRequest("", s.lastID+1, 0, store.DirectionForward, -1)
}

// subscriptionLoop that will run in a goroutine and pipe messages from route to fcm
// Attention: in order for this loop to finish the route channel must stop sending messages
func (s *subscription) subscriptionLoop() {
	s.logger.Debug("Starting subscription loop")

	s.connector.wg.Add(1)
	defer func() {
		s.logger.Debug("Stopped subscription loop")
		s.connector.wg.Done()
	}()

	var (
		m      *protocol.Message
		opened = true
	)
	for opened {
		select {
		case m, opened = <-s.route.MessagesChannel():
			// TODO Bogdan This needs to be remade and we should gracefully shutdown
			// and wait for this channel to empty before stopping the loop
			select {
			case <-s.connector.stopC:
				return
			case <-time.After(5 * time.Millisecond):
			}

			if !opened {
				s.logger.Error("Route channel is closed")
				continue
			}

			if err := s.pipe(m); err != nil {
				// abandon route if the following 2 errors are met
				// the subscription has been replaced
				if err == errSubscriptionReplaced {
					return
				}
				// the subscription is not registered with FCM anymore
				if _, ok := err.(*jsonError); ok {
					return
				}

				s.logger.WithError(err).Error("Error pipelining message")
			}
		case <-s.connector.stopC:
			return
		}
	}

	// if route is closed and we are actually stopping then return
	if s.isStopping() {
		return
	}

	// assume that the route channel has been closed because of slow processing
	// try restarting, by fetching from lastId and then subscribing again
	if err := s.restart(); err != nil {
		if stoppingErr, ok := err.(*router.ModuleStoppingError); ok {
			s.logger.WithField("error", stoppingErr).Debug("Error restarting subscription")
		}
	}
}

// isStopping returns true if we are actually stopping the service
func (s *subscription) isStopping() bool {
	select {
	case <-s.connector.stopC:
		return true
	default:
	}
	return false
}

// Key returns a string that uniquely identifies this subscription
func (s *subscription) Key() string {
	return s.route.Key()
}

// pipe sends a message into the pipeline and waits for response saving the last id in the kvstore
func (s *subscription) pipe(m *protocol.Message) error {
	subscriptionMessage := newSubscriptionMessage(s, m)
	defer subscriptionMessage.closeChannels()

	s.connector.pipelineC <- subscriptionMessage

	// wait for response
	select {
	case response := <-subscriptionMessage.resultC:
		s.logger.WithField("messageID", m.ID).Debug("Delivered message to FCM")
		if err := s.setLastID(subscriptionMessage.message.ID); err != nil {
			return err
		}
		if response.Ok() {
			return nil
		}

		s.logger.WithField("count", response.Success).Debug("Handling FCM Error")
		if err := s.handleJSONError(response); err != nil {
			return err
		}

		if response.CanonicalIDs != 0 {
			// we only send to one receiver,
			// so we know that we can replace the old id with the first registration id (=canonical id)
			return s.replaceCanonical(response.Results[0].RegistrationID)
		}
		return nil

	case err := <-subscriptionMessage.errC:
		if err == errIgnoreMessage {
			s.logger.WithField("message", m).Info("Ignoring message")
			return nil
		}
		s.logger.WithField("error", err.Error()).Error("Error sending message to FCM")
		return err
	}
}

func (s *subscription) handleJSONError(response *gcm.Response) error {
	switch errText := response.Error.Error(); errText {
	case "NotRegistered":
		s.logger.Debug("Removing not registered FCM subscription")
		s.remove()
		return &jsonError{errText}
	case "InvalidRegistration":
		s.logger.WithField("jsonError", errText).Error("Subscription is not registered")
		return nil
	default:
		s.logger.WithField("jsonError", errText).Error("Unexpected error while sending to FCM")
		return nil
	}
}

// replaceCanonical replaces subscription with canonical id,
// creates a new subscription but alters the route to have the new ApplicationID
func (s *subscription) replaceCanonical(newFCMID string) error {
	s.logger.WithField("newFCMID", newFCMID).Info("Replacing with FCM canonicalID")
	// delete current route from kvstore
	s.remove()

	// reuse the route but change the ApplicationID
	route := s.route
	route.Set(applicationIDKey, newFCMID)
	newSub := newSubscription(s.connector, route, s.lastID)

	if err := newSub.store(); err != nil {
		return err
	}
	newSub.start()
	return errSubscriptionReplaced
}

func (s *subscription) setLastID(ID uint64) error {
	s.lastID = ID
	// update KV when last id is set
	return s.store()
}

// store data in kvstore
func (s *subscription) store() error {
	s.logger.WithField("lastID", s.lastID).Debug("Storing subscription")
	applicationID := s.route.Get(applicationIDKey)
	err := s.connector.kvStore.Put(schema, applicationID, s.bytes())
	if err != nil {
		s.logger.WithError(err).Error("Error storing in KVStore")
	}
	return err
}

// bytes returns the data to store in kvStore
func (s *subscription) bytes() []byte {
	return []byte(strings.Join([]string{
		s.route.Get(userIDKey),
		string(s.route.Path),
		strconv.FormatUint(s.lastID, 10),
	}, ":"))
}

// remove unsubscribes from router, delete from connector's subscriptions, and remove from KVStore
func (s *subscription) remove() *subscription {
	s.logger.Debug("Removing subscription")
	s.connector.router.Unsubscribe(s.route)
	delete(s.connector.subscriptions, s.Key())
	s.connector.kvStore.Delete(schema, s.Key())
	return s
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/go-martini/martini"
	"github.com/gorilla/websocket"
	"github.com/martini-contrib/sessions"
	"log"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"sync"
	"bytes"
	"time"
	"runtime/pprof"
)

const CLEAR string = "\033[H\033[2J"
const RESET string = "\033c\033(B\033[0m\033[J\033[?25h"

const MAX_CONSOLE int = 10000

const (
	WSTerm  = 1
	WSClick = 2
)

type Config struct {
	Secret  string
	Address string
}

type LockingWebsockets struct {
	sync.RWMutex
	byId        map[int64]*websocket.Conn
	consoleToId map[int64][]int64
	currentId   int64
}

var consoleBuffers map[int64]*bytes.Buffer

var websockets *LockingWebsockets

func (c *LockingWebsockets) deleteWebsocket(id int64) {
	c.Lock()
	defer c.Unlock()
	delete(c.byId, id)
}

func (c *LockingWebsockets) addWebsocket(ws *websocket.Conn) int64 {
	c.Lock()
	defer c.Unlock()
	c.currentId += 1
	retid := c.currentId
	c.byId[retid] = ws
	return retid
}

func main() {
	f, err := os.Create("pprof.out")
	if err != nil {
		log.Fatal(err)
	}
	pprof.StartCPUProfile(f)
	go func() {
		time.Sleep(time.Minute * 10)
		pprof.StopCPUProfile()
		log.Fatal("Done")
	}()
	consoleBuffers = make(map[int64]*bytes.Buffer)
	websockets = &LockingWebsockets{
		byId:        make(map[int64]*websocket.Conn),
		consoleToId: make(map[int64][]int64),
		currentId:   0,
	}
	consoleReadChannel = make(chan *ConsoleChunk)
	go consoleDispatch()
	vzcontrol := ConnectVZControl()
	defer vzcontrol.Close()

	file, _ := os.Open("game_server.cfg")
	decoder := json.NewDecoder(file)
	config := Config{}
	decoder.Decode(&config)

	m := martini.Classic()
	store := sessions.NewCookieStore([]byte(config.Secret))
	m.Use(sessions.Sessions("session", store))

	generating := false
	gr := NewGraph()
	m.Get("/reset/:secret", func(w http.ResponseWriter, r *http.Request, params martini.Params, session sessions.Session) string {
		if params["secret"] != config.Secret {
			return ""
		}
		err := vzcontrol.Reset()
		if err != nil {
			return err.Error()
		}
		generating = false
		gr = NewGraph()
		return "Done"
	})

	m.Get("/gen", func(w http.ResponseWriter, r *http.Request, session sessions.Session) string {
		if generating {
			return "Already generating"
		}
		generating = true
		maxNodes := 5
		maxEdges := 5
		startNodeId := 100

		startNode := Node{Id: NodeId(startNodeId)}
		gr.AddNode(startNode)
		err := vzcontrol.ContainerCreate(int64(startNode.Id))
		err = vzcontrol.ConsoleStart(int64(startNode.Id))

		nodes := make([]Node, 0)
		nodes = append(nodes, startNode)

		steps := 1
		for len(nodes) != 0 && steps < maxNodes {
			node, nodes := nodes[len(nodes)-1], nodes[:len(nodes)-1]

			numEdges := random(1, maxEdges)
			for i := 1; i <= numEdges; i++ {
				targetNode := Node{Id: NodeId(i*steps + startNodeId)}
				if gr.AddNode(targetNode) {
					err = vzcontrol.ContainerCreate(int64(targetNode.Id))
					if err != nil {
						return fmt.Sprintf("Container Create: %d, %d, %d\n%s", targetNode.Id, i*steps, numEdges, err.Error())
					}
					err = vzcontrol.ConsoleStart(int64(targetNode.Id))
					if err != nil {
						return fmt.Sprintf("Console Start: %d\n%s", targetNode.Id, err.Error())
					}
					nodes = append(nodes, targetNode)
					edgeid := int64(i * steps)
					if gr.AddEdge(Edge{Id: EdgeId(edgeid), Head: node.Id, Tail: targetNode.Id}) {
						err = vzcontrol.NetworkCreate(edgeid)
						if err != nil {
							return fmt.Sprintf("Network Create: %d\n%s", edgeid, err.Error())
						}
						err = vzcontrol.NetworkAdd(int64(node.Id), edgeid)
						if err != nil {
							return fmt.Sprintf("Network Add Node: %d, %d\n%s", node.Id, edgeid, err.Error())
						}
						err = vzcontrol.NetworkAdd(int64(targetNode.Id), edgeid)
						if err != nil {
							return fmt.Sprintf("Network Add Target: %d, %d\n%s", targetNode.Id, edgeid, err.Error())
						}
					}
				}

			}
			steps += 1
		}
		return gr.String()
	})

	m.Get("/graph", func(w http.ResponseWriter, r *http.Request, session sessions.Session) string {
		output, err := json.Marshal(gr)
		if err != nil {
			return err.Error()
		}
		return string(output)
	})

	m.Get("/ws", func(w http.ResponseWriter, r *http.Request, session sessions.Session) {
		var currentVm int64 = -1
		ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
		if _, ok := err.(websocket.HandshakeError); ok {
			http.Error(w, "Not a websocket handshake", 400)
			log.Println(err)
			return
		} else if err != nil {
			log.Println(err)
			return
		}
		defer ws.Close()
		websocketId := websockets.addWebsocket(ws)
		defer websockets.deleteWebsocket(websocketId)
		ws.WriteMessage(websocket.TextMessage, []byte("Welcome to ginux!\r\n"))
		for {
			_, message, err := ws.ReadMessage()
			if err != nil {
				log.Println(err)
				return
			} else {
				msgType := message[0]
				msgData := message[1:len(message)]
				switch msgType {
				case WSTerm:
					if currentVm != -1 {
						vzcontrol.ConsoleWrite(currentVm, msgData)
					}
				case WSClick:
					prevVm := currentVm
					tmp, _ := strconv.Atoi(string(msgData))
					currentVm = int64(tmp)
					websockets.Lock()
					if prevVm != -1 {
						for index, wsId := range websockets.consoleToId[prevVm] {
							if wsId == websocketId {
								websockets.consoleToId[prevVm] = append(websockets.consoleToId[prevVm][:index], websockets.consoleToId[prevVm][index+1:]...)
							}
						}
					}
					websockets.consoleToId[currentVm] = append(websockets.consoleToId[currentVm], websocketId)
					websockets.Unlock()
					//ws.WriteMessage(websocket.TextMessage, []byte(CLEAR))
					ws.WriteMessage(websocket.TextMessage, []byte(RESET))
					ws.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("Selected Container %d\r\n", currentVm)))
					ws.WriteMessage(websocket.TextMessage, consoleBuffers[currentVm].Bytes())
				}
			}
		}
	})
	log.Println("Game Server started on", config.Address)
	log.Fatal(http.ListenAndServe(config.Address, m))
}

func random(min, max int) int {
	rand.Seed(time.Now().Unix())
	return rand.Intn(max-min) + min
}

func consoleDispatch() {
	for chunk := range consoleReadChannel {
		if _, ok := consoleBuffers[chunk.Id]; !ok {
			 consoleBuffers[chunk.Id] = &bytes.Buffer{}
		}
		consoleBuffers[chunk.Id].Write(chunk.Data)
		//if len(consoleBuffers[chunk.Id]) > MAX_CONSOLE {
		//	consoleBuffers[chunk.Id] = consoleBuffers[chunk.Id][len(string(chunk.Data)):]
		//}
		websockets.RLock()
		for _, wsId := range websockets.consoleToId[chunk.Id] {
			if socket, ok := websockets.byId[wsId]; ok {
				socket.WriteMessage(websocket.TextMessage, chunk.Data)
			}
		}
		websockets.RUnlock()
	}
}
<commit_msg>Removed pprof<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/go-martini/martini"
	"github.com/gorilla/websocket"
	"github.com/martini-contrib/sessions"
	"log"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"
	//"runtime/pprof"
)

const CLEAR string = "\033[H\033[2J"
const RESET string = "\033c\033(B\033[0m\033[J\033[?25h"

const MAX_CONSOLE int = 10000

const (
	WSTerm  = 1
	WSClick = 2
)

type Config struct {
	Secret  string
	Address string
}

type LockingWebsockets struct {
	sync.RWMutex
	byId        map[int64]*websocket.Conn
	consoleToId map[int64][]int64
	currentId   int64
}

var consoleBuffers map[int64]*bytes.Buffer

var websockets *LockingWebsockets

func (c *LockingWebsockets) deleteWebsocket(id int64) {
	c.Lock()
	defer c.Unlock()
	delete(c.byId, id)
}

func (c *LockingWebsockets) addWebsocket(ws *websocket.Conn) int64 {
	c.Lock()
	defer c.Unlock()
	c.currentId += 1
	retid := c.currentId
	c.byId[retid] = ws
	return retid
}

func main() {
	//pprof stuff
	/*f, err := os.Create("pprof.out")
	if err != nil {
		log.Fatal(err)
	}
	pprof.StartCPUProfile(f)
	go func() {
		time.Sleep(time.Minute * 10)
		pprof.StopCPUProfile()
		log.Fatal("Done")
	}()*/
	consoleBuffers = make(map[int64]*bytes.Buffer)
	websockets = &LockingWebsockets{
		byId:        make(map[int64]*websocket.Conn),
		consoleToId: make(map[int64][]int64),
		currentId:   0,
	}
	consoleReadChannel = make(chan *ConsoleChunk)
	go consoleDispatch()
	vzcontrol := ConnectVZControl()
	defer vzcontrol.Close()

	file, _ := os.Open("game_server.cfg")
	decoder := json.NewDecoder(file)
	config := Config{}
	decoder.Decode(&config)

	m := martini.Classic()
	store := sessions.NewCookieStore([]byte(config.Secret))
	m.Use(sessions.Sessions("session", store))

	generating := false
	gr := NewGraph()
	m.Get("/reset/:secret", func(w http.ResponseWriter, r *http.Request, params martini.Params, session sessions.Session) string {
		if params["secret"] != config.Secret {
			return ""
		}
		err := vzcontrol.Reset()
		if err != nil {
			return err.Error()
		}
		generating = false
		gr = NewGraph()
		return "Done"
	})

	m.Get("/gen", func(w http.ResponseWriter, r *http.Request, session sessions.Session) string {
		if generating {
			return "Already generating"
		}
		generating = true
		maxNodes := 5
		maxEdges := 5
		startNodeId := 100

		startNode := Node{Id: NodeId(startNodeId)}
		gr.AddNode(startNode)
		err := vzcontrol.ContainerCreate(int64(startNode.Id))
		err = vzcontrol.ConsoleStart(int64(startNode.Id))

		nodes := make([]Node, 0)
		nodes = append(nodes, startNode)

		steps := 1
		for len(nodes) != 0 && steps < maxNodes {
			node, nodes := nodes[len(nodes)-1], nodes[:len(nodes)-1]

			numEdges := random(1, maxEdges)
			for i := 1; i <= numEdges; i++ {
				targetNode := Node{Id: NodeId(i*steps + startNodeId)}
				if gr.AddNode(targetNode) {
					err = vzcontrol.ContainerCreate(int64(targetNode.Id))
					if err != nil {
						return fmt.Sprintf("Container Create: %d, %d, %d\n%s", targetNode.Id, i*steps, numEdges, err.Error())
					}
					err = vzcontrol.ConsoleStart(int64(targetNode.Id))
					if err != nil {
						return fmt.Sprintf("Console Start: %d\n%s", targetNode.Id, err.Error())
					}
					nodes = append(nodes, targetNode)
					edgeid := int64(i * steps)
					if gr.AddEdge(Edge{Id: EdgeId(edgeid), Head: node.Id, Tail: targetNode.Id}) {
						err = vzcontrol.NetworkCreate(edgeid)
						if err != nil {
							return fmt.Sprintf("Network Create: %d\n%s", edgeid, err.Error())
						}
						err = vzcontrol.NetworkAdd(int64(node.Id), edgeid)
						if err != nil {
							return fmt.Sprintf("Network Add Node: %d, %d\n%s", node.Id, edgeid, err.Error())
						}
						err = vzcontrol.NetworkAdd(int64(targetNode.Id), edgeid)
						if err != nil {
							return fmt.Sprintf("Network Add Target: %d, %d\n%s", targetNode.Id, edgeid, err.Error())
						}
					}
				}

			}
			steps += 1
		}
		return gr.String()
	})

	m.Get("/graph", func(w http.ResponseWriter, r *http.Request, session sessions.Session) string {
		output, err := json.Marshal(gr)
		if err != nil {
			return err.Error()
		}
		return string(output)
	})

	m.Get("/ws", func(w http.ResponseWriter, r *http.Request, session sessions.Session) {
		var currentVm int64 = -1
		ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
		if _, ok := err.(websocket.HandshakeError); ok {
			http.Error(w, "Not a websocket handshake", 400)
			log.Println(err)
			return
		} else if err != nil {
			log.Println(err)
			return
		}
		defer ws.Close()
		websocketId := websockets.addWebsocket(ws)
		defer websockets.deleteWebsocket(websocketId)
		ws.WriteMessage(websocket.TextMessage, []byte("Welcome to ginux!\r\n"))
		for {
			_, message, err := ws.ReadMessage()
			if err != nil {
				log.Println(err)
				return
			} else {
				msgType := message[0]
				msgData := message[1:len(message)]
				switch msgType {
				case WSTerm:
					if currentVm != -1 {
						vzcontrol.ConsoleWrite(currentVm, msgData)
					}
				case WSClick:
					prevVm := currentVm
					tmp, _ := strconv.Atoi(string(msgData))
					currentVm = int64(tmp)
					websockets.Lock()
					if prevVm != -1 {
						for index, wsId := range websockets.consoleToId[prevVm] {
							if wsId == websocketId {
								websockets.consoleToId[prevVm] = append(websockets.consoleToId[prevVm][:index], websockets.consoleToId[prevVm][index+1:]...)
							}
						}
					}
					websockets.consoleToId[currentVm] = append(websockets.consoleToId[currentVm], websocketId)
					websockets.Unlock()
					//ws.WriteMessage(websocket.TextMessage, []byte(CLEAR))
					ws.WriteMessage(websocket.TextMessage, []byte(RESET))
					ws.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("Selected Container %d\r\n", currentVm)))
					ws.WriteMessage(websocket.TextMessage, consoleBuffers[currentVm].Bytes())
				}
			}
		}
	})
	log.Println("Game Server started on", config.Address)
	log.Fatal(http.ListenAndServe(config.Address, m))
}

func random(min, max int) int {
	rand.Seed(time.Now().Unix())
	return rand.Intn(max-min) + min
}

func consoleDispatch() {
	for chunk := range consoleReadChannel {
		if _, ok := consoleBuffers[chunk.Id]; !ok {
			consoleBuffers[chunk.Id] = &bytes.Buffer{}
		}
		consoleBuffers[chunk.Id].Write(chunk.Data)
		//if len(consoleBuffers[chunk.Id]) > MAX_CONSOLE {
		//	consoleBuffers[chunk.Id] = consoleBuffers[chunk.Id][len(string(chunk.Data)):]
		//}
		websockets.RLock()
		for _, wsId := range websockets.consoleToId[chunk.Id] {
			if socket, ok := websockets.byId[wsId]; ok {
				socket.WriteMessage(websocket.TextMessage, chunk.Data)
			}
		}
		websockets.RUnlock()
	}
}
<|endoftext|>


<commit_before>package controllers

import (
  "io"
	"net/http"
  "time"
  "encoding/json"

  //"fmt"
  "github.com/johnosullivan/gomihome/websockets"
  //"github.com/johnosullivan/go-fun/db"
)

type SystemStatus struct {
  Date    time.Time `json:"date"`
  Status  bool      `json:"status"`
}

func PingLink(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
      case http.MethodGet: {
          sysStatus := SystemStatus{time.Now(), true}
          js, err := json.Marshal(sysStatus)
          if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
          }

          /*
          command := r.URL.Query().Get("data")
          data := "{\"type\": \"" + command+ "\",\"payload\": {}}"
          hub := websockets.GetHub()
          hub.Broadcast <- []byte(data)
          */

          key := r.URL.Query().Get("key")

          websockets.SendToClient(key, []byte("PING"))

          w.Header().Set("Content-Type", "application/json")
          w.Write(js)
      }
      default:
          w.WriteHeader(http.StatusMethodNotAllowed)
    }
}

func AuthPingHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Add("Content-Type", "application/json")
    io.WriteString(w, `{"status":"ok"}`)
}
<commit_msg>Update system.go<commit_after>package controllers

import (
  "io"
	"net/http"
  "time"
  "encoding/json"

  //"fmt"
  "github.com/johnosullivan/gomihome/websockets"
  //"github.com/johnosullivan/go-fun/db"
)

type SystemStatus struct {
  Date    time.Time `json:"date"`
  Status  bool      `json:"status"`
}

func PingLink(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
      case http.MethodGet: {
          sysStatus := SystemStatus{time.Now(), true}
          js, err := json.Marshal(sysStatus)
          if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
          }

          /*
          command := r.URL.Query().Get("data")
          data := "{\"type\": \"" + command+ "\",\"payload\": {}}"
          hub := websockets.GetHub()
          hub.Broadcast <- []byte(data)
          */

          key := r.URL.Query().Get("key")

          websockets.SendToClient(key, []byte("{\"type\":\"PING\",\"payload\": {}}"))

          w.Header().Set("Content-Type", "application/json")
          w.Write(js)
      }
      default:
          w.WriteHeader(http.StatusMethodNotAllowed)
    }
}

func AuthPingHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Add("Content-Type", "application/json")
    io.WriteString(w, `{"status":"ok"}`)
}
<|endoftext|>


<commit_before>package graphdriver

import (
	"errors"
	"fmt"
	"os"
	"path"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/archive"
)

type FsMagic uint32

const (
	FsMagicBtrfs = FsMagic(0x9123683E)
	FsMagicAufs  = FsMagic(0x61756673)
)

type InitFunc func(root string, options []string) (Driver, error)

// ProtoDriver defines the basic capabilities of a driver.
// This interface exists solely to be a minimum set of methods
// for client code which choose not to implement the entire Driver
// interface and use the NaiveDiffDriver wrapper constructor.
//
// Use of ProtoDriver directly by client code is not recommended.
type ProtoDriver interface {
	// String returns a string representation of this driver.
	String() string
	// Create creates a new, empty, filesystem layer with the
	// specified id and parent. Parent may be "".
	Create(id, parent string) error
	// Remove attempts to remove the filesystem layer with this id.
	Remove(id string) error
	// Get returns the mountpoint for the layered filesystem referred
	// to by this id. You can optionally specify a mountLabel or "".
	// Returns the absolute path to the mounted layered filesystem.
	Get(id, mountLabel string) (dir string, err error)
	// Put releases the system resources for the specified id,
	// e.g, unmounting layered filesystem.
	Put(id string) error
	// Exists returns whether a filesystem layer with the specified
	// ID exists on this driver.
	Exists(id string) bool
	// Status returns a set of key-value pairs which give low
	// level diagnostic status about this driver.
	Status() [][2]string
	// Cleanup performs necessary tasks to release resources
	// held by the driver, e.g., unmounting all layered filesystems
	// known to this driver.
	Cleanup() error
}

// Driver is the interface for layered/snapshot file system drivers.
type Driver interface {
	ProtoDriver
	// Diff produces an archive of the changes between the specified
	// layer and its parent layer which may be "".
	Diff(id, parent string) (archive.Archive, error)
	// Changes produces a list of changes between the specified layer
	// and its parent layer. If parent is "", then all changes will be ADD changes.
	Changes(id, parent string) ([]archive.Change, error)
	// ApplyDiff extracts the changeset from the given diff into the
	// layer with the specified id and parent, returning the size of the
	// new layer in bytes.
	ApplyDiff(id, parent string, diff archive.ArchiveReader) (size int64, err error)
	// DiffSize calculates the changes between the specified id
	// and its parent and returns the size in bytes of the changes
	// relative to its base filesystem directory.
	DiffSize(id, parent string) (size int64, err error)
}

var (
	DefaultDriver string
	// All registred drivers
	drivers map[string]InitFunc
	// Slice of drivers that should be used in an order
	priority = []string{
		"aufs",
		"btrfs",
		"devicemapper",
		"vfs",
		// experimental, has to be enabled manually for now
		"overlay",
	}

	ErrNotSupported   = errors.New("driver not supported")
	ErrPrerequisites  = errors.New("prerequisites for driver not satisfied (wrong filesystem?)")
	ErrIncompatibleFS = fmt.Errorf("backing file system is unsupported for this graph driver")
)

func init() {
	drivers = make(map[string]InitFunc)
}

func Register(name string, initFunc InitFunc) error {
	if _, exists := drivers[name]; exists {
		return fmt.Errorf("Name already registered %s", name)
	}
	drivers[name] = initFunc

	return nil
}

func GetDriver(name, home string, options []string) (Driver, error) {
	if initFunc, exists := drivers[name]; exists {
		return initFunc(path.Join(home, name), options)
	}
	return nil, ErrNotSupported
}

func New(root string, options []string) (driver Driver, err error) {
	for _, name := range []string{os.Getenv("DOCKER_DRIVER"), DefaultDriver} {
		if name != "" {
			return GetDriver(name, root, options)
		}
	}

	// Check for priority drivers first
	for _, name := range priority {
		driver, err = GetDriver(name, root, options)
		if err != nil {
			if err == ErrNotSupported || err == ErrPrerequisites || err == ErrIncompatibleFS {
				continue
			}
			return nil, err
		}
		checkPriorDriver(name, root)
		return driver, nil
	}

	// Check all registered drivers if no priority driver is found
	for name, initFunc := range drivers {
		if driver, err = initFunc(root, options); err != nil {
			if err == ErrNotSupported || err == ErrPrerequisites || err == ErrIncompatibleFS {
				continue
			}
			return nil, err
		}
		checkPriorDriver(name, root)
		return driver, nil
	}
	return nil, fmt.Errorf("No supported storage backend found")
}

func checkPriorDriver(name, root string) {
	priorDrivers := []string{}
	for prior := range drivers {
		if prior != name {
			if _, err := os.Stat(path.Join(root, prior)); err == nil {
				priorDrivers = append(priorDrivers, prior)
			}
		}
	}
	if len(priorDrivers) > 0 {
		log.Warnf("graphdriver %s selected. Warning: your graphdriver directory %s already contains data managed by other graphdrivers: %s", name, root, strings.Join(priorDrivers, ","))
	}
}
<commit_msg>ignore vfs from warning<commit_after>package graphdriver

import (
	"errors"
	"fmt"
	"os"
	"path"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/archive"
)

type FsMagic uint32

const (
	FsMagicBtrfs = FsMagic(0x9123683E)
	FsMagicAufs  = FsMagic(0x61756673)
)

type InitFunc func(root string, options []string) (Driver, error)

// ProtoDriver defines the basic capabilities of a driver.
// This interface exists solely to be a minimum set of methods
// for client code which choose not to implement the entire Driver
// interface and use the NaiveDiffDriver wrapper constructor.
//
// Use of ProtoDriver directly by client code is not recommended.
type ProtoDriver interface {
	// String returns a string representation of this driver.
	String() string
	// Create creates a new, empty, filesystem layer with the
	// specified id and parent. Parent may be "".
	Create(id, parent string) error
	// Remove attempts to remove the filesystem layer with this id.
	Remove(id string) error
	// Get returns the mountpoint for the layered filesystem referred
	// to by this id. You can optionally specify a mountLabel or "".
	// Returns the absolute path to the mounted layered filesystem.
	Get(id, mountLabel string) (dir string, err error)
	// Put releases the system resources for the specified id,
	// e.g, unmounting layered filesystem.
	Put(id string) error
	// Exists returns whether a filesystem layer with the specified
	// ID exists on this driver.
	Exists(id string) bool
	// Status returns a set of key-value pairs which give low
	// level diagnostic status about this driver.
	Status() [][2]string
	// Cleanup performs necessary tasks to release resources
	// held by the driver, e.g., unmounting all layered filesystems
	// known to this driver.
	Cleanup() error
}

// Driver is the interface for layered/snapshot file system drivers.
type Driver interface {
	ProtoDriver
	// Diff produces an archive of the changes between the specified
	// layer and its parent layer which may be "".
	Diff(id, parent string) (archive.Archive, error)
	// Changes produces a list of changes between the specified layer
	// and its parent layer. If parent is "", then all changes will be ADD changes.
	Changes(id, parent string) ([]archive.Change, error)
	// ApplyDiff extracts the changeset from the given diff into the
	// layer with the specified id and parent, returning the size of the
	// new layer in bytes.
	ApplyDiff(id, parent string, diff archive.ArchiveReader) (size int64, err error)
	// DiffSize calculates the changes between the specified id
	// and its parent and returns the size in bytes of the changes
	// relative to its base filesystem directory.
	DiffSize(id, parent string) (size int64, err error)
}

var (
	DefaultDriver string
	// All registred drivers
	drivers map[string]InitFunc
	// Slice of drivers that should be used in an order
	priority = []string{
		"aufs",
		"btrfs",
		"devicemapper",
		"vfs",
		// experimental, has to be enabled manually for now
		"overlay",
	}

	ErrNotSupported   = errors.New("driver not supported")
	ErrPrerequisites  = errors.New("prerequisites for driver not satisfied (wrong filesystem?)")
	ErrIncompatibleFS = fmt.Errorf("backing file system is unsupported for this graph driver")
)

func init() {
	drivers = make(map[string]InitFunc)
}

func Register(name string, initFunc InitFunc) error {
	if _, exists := drivers[name]; exists {
		return fmt.Errorf("Name already registered %s", name)
	}
	drivers[name] = initFunc

	return nil
}

func GetDriver(name, home string, options []string) (Driver, error) {
	if initFunc, exists := drivers[name]; exists {
		return initFunc(path.Join(home, name), options)
	}
	return nil, ErrNotSupported
}

func New(root string, options []string) (driver Driver, err error) {
	for _, name := range []string{os.Getenv("DOCKER_DRIVER"), DefaultDriver} {
		if name != "" {
			return GetDriver(name, root, options)
		}
	}

	// Check for priority drivers first
	for _, name := range priority {
		driver, err = GetDriver(name, root, options)
		if err != nil {
			if err == ErrNotSupported || err == ErrPrerequisites || err == ErrIncompatibleFS {
				continue
			}
			return nil, err
		}
		checkPriorDriver(name, root)
		return driver, nil
	}

	// Check all registered drivers if no priority driver is found
	for name, initFunc := range drivers {
		if driver, err = initFunc(root, options); err != nil {
			if err == ErrNotSupported || err == ErrPrerequisites || err == ErrIncompatibleFS {
				continue
			}
			return nil, err
		}
		checkPriorDriver(name, root)
		return driver, nil
	}
	return nil, fmt.Errorf("No supported storage backend found")
}

func checkPriorDriver(name, root string) {
	priorDrivers := []string{}
	for prior := range drivers {
		if prior != name && prior != "vfs" {
			if _, err := os.Stat(path.Join(root, prior)); err == nil {
				priorDrivers = append(priorDrivers, prior)
			}
		}
	}
	if len(priorDrivers) > 0 {
		log.Warnf("graphdriver %s selected. Warning: your graphdriver directory %s already contains data managed by other graphdrivers: %s", name, root, strings.Join(priorDrivers, ","))
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Circonus, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Metric Cluster API support - Fetch, Create, Update, Delete, and Search
// See: https://login.circonus.com/resources/api/calls/metric_cluster

package api

import (
	"encoding/json"
	"fmt"
	"net/url"
	"regexp"

	"github.com/circonus-labs/circonus-gometrics/api/config"
)

// MetricQuery object
type MetricQuery struct {
	Query string `json:"query"`
	Type  string `json:"type"`
}

// MetricCluster defines a metric cluster. See https://login.circonus.com/resources/api/calls/metric_cluster for more information.
type MetricCluster struct {
	CID                 string              `json:"_cid,omitempty"`
	MatchingMetrics     []string            `json:"_matching_metrics,omitempty"`
	MatchingUUIDMetrics map[string][]string `json:"_matching_uuid_metrics,omitempty"`
	Description         string              `json:"description"`
	Name                string              `json:"name"`
	Queries             []MetricQuery       `json:"queries"`
	Tags                []string            `json:"tags"`
}

// NewMetricCluster returns a new MetricCluster (with defaults, if applicable)
func NewMetricCluster() *MetricCluster {
	return &MetricCluster{}
}

// FetchMetricCluster retrieves metric cluster with passed cid.
func (a *API) FetchMetricCluster(cid CIDType, extras string) (*MetricCluster, error) {
	if cid == nil || *cid == "" {
		return nil, fmt.Errorf("Invalid metric cluster CID [none]")
	}

	clusterCID := string(*cid)

	matched, err := regexp.MatchString(config.MetricClusterCIDRegex, clusterCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid metric cluster CID [%s]", clusterCID)
	}

	reqURL := url.URL{
		Path: clusterCID,
	}

	extra := ""
	switch extras {
	case "metrics":
		extra = "_matching_metrics"
	case "uuids":
		extra = "_matching_uuid_metrics"
	}

	if extra != "" {
		q := url.Values{}
		q.Set("extra", extra)
		reqURL.RawQuery = q.Encode()
	}

	result, err := a.Get(reqURL.String())
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] fetch metric cluster, received JSON: %s", string(result))
	}

	cluster := &MetricCluster{}
	if err := json.Unmarshal(result, cluster); err != nil {
		return nil, err
	}

	return cluster, nil
}

// FetchMetricClusters retrieves all metric clusters available to API Token.
func (a *API) FetchMetricClusters(extras string) (*[]MetricCluster, error) {
	reqURL := url.URL{
		Path: config.MetricClusterPrefix,
	}

	extra := ""
	switch extras {
	case "metrics":
		extra = "_matching_metrics"
	case "uuids":
		extra = "_matching_uuid_metrics"
	}

	if extra != "" {
		q := url.Values{}
		q.Set("extra", extra)
		reqURL.RawQuery = q.Encode()
	}

	result, err := a.Get(reqURL.String())
	if err != nil {
		return nil, err
	}

	var clusters []MetricCluster
	if err := json.Unmarshal(result, &clusters); err != nil {
		return nil, err
	}

	return &clusters, nil
}

// UpdateMetricCluster updates passed metric cluster.
func (a *API) UpdateMetricCluster(cfg *MetricCluster) (*MetricCluster, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid metric cluster config [nil]")
	}

	clusterCID := string(cfg.CID)

	matched, err := regexp.MatchString(config.MetricClusterCIDRegex, clusterCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid metric cluster CID [%s]", clusterCID)
	}

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

	if a.Debug {
		a.Log.Printf("[DEBUG] update metric cluster, sending JSON: %s", string(jsonCfg))
	}

	result, err := a.Put(clusterCID, jsonCfg)
	if err != nil {
		return nil, err
	}

	cluster := &MetricCluster{}
	if err := json.Unmarshal(result, cluster); err != nil {
		return nil, err
	}

	return cluster, nil
}

// CreateMetricCluster creates a new metric cluster.
func (a *API) CreateMetricCluster(cfg *MetricCluster) (*MetricCluster, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid metric cluster config [nil]")
	}

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

	if a.Debug {
		a.Log.Printf("[DEBUG] create metric cluster, sending JSON: %s", string(jsonCfg))
	}

	result, err := a.Post(config.MetricClusterPrefix, jsonCfg)
	if err != nil {
		return nil, err
	}

	cluster := &MetricCluster{}
	if err := json.Unmarshal(result, cluster); err != nil {
		return nil, err
	}

	return cluster, nil
}

// DeleteMetricCluster deletes passed metric cluster.
func (a *API) DeleteMetricCluster(cfg *MetricCluster) (bool, error) {
	if cfg == nil {
		return false, fmt.Errorf("Invalid metric cluster config [nil]")
	}
	return a.DeleteMetricClusterByCID(CIDType(&cfg.CID))
}

// DeleteMetricClusterByCID deletes metric cluster with passed cid.
func (a *API) DeleteMetricClusterByCID(cid CIDType) (bool, error) {
	if cid == nil || *cid == "" {
		return false, fmt.Errorf("Invalid metric cluster CID [none]")
	}

	clusterCID := string(*cid)

	matched, err := regexp.MatchString(config.MetricClusterCIDRegex, clusterCID)
	if err != nil {
		return false, err
	}
	if !matched {
		return false, fmt.Errorf("Invalid metric cluster CID [%s]", clusterCID)
	}

	_, err = a.Delete(clusterCID)
	if err != nil {
		return false, err
	}

	return true, nil
}

// SearchMetricClusters returns metric clusters matching the specified
// search query and/or filter. If nil is passed for both parameters
// all metric clusters will be returned.
func (a *API) SearchMetricClusters(searchCriteria *SearchQueryType, filterCriteria *SearchFilterType) (*[]MetricCluster, error) {
	q := url.Values{}

	if searchCriteria != nil && *searchCriteria != "" {
		q.Set("search", string(*searchCriteria))
	}

	if filterCriteria != nil && len(*filterCriteria) > 0 {
		for filter, criteria := range *filterCriteria {
			for _, val := range criteria {
				q.Add(filter, val)
			}
		}
	}

	if q.Encode() == "" {
		return a.FetchMetricClusters("")
	}

	reqURL := url.URL{
		Path:     config.MetricClusterPrefix,
		RawQuery: q.Encode(),
	}

	result, err := a.Get(reqURL.String())
	if err != nil {
		return nil, fmt.Errorf("[ERROR] API call error %+v", err)
	}

	var clusters []MetricCluster
	if err := json.Unmarshal(result, &clusters); err != nil {
		return nil, err
	}

	return &clusters, nil
}
<commit_msg>upd: document/update struct member types to reflect what is received from api<commit_after>// Copyright 2016 Circonus, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Metric Cluster API support - Fetch, Create, Update, Delete, and Search
// See: https://login.circonus.com/resources/api/calls/metric_cluster

package api

import (
	"encoding/json"
	"fmt"
	"net/url"
	"regexp"

	"github.com/circonus-labs/circonus-gometrics/api/config"
)

// MetricQuery object
type MetricQuery struct {
	Query string `json:"query"`
	Type  string `json:"type"`
}

// MetricCluster defines a metric cluster. See https://login.circonus.com/resources/api/calls/metric_cluster for more information.
type MetricCluster struct {
	CID                 string              `json:"_cid,omitempty"`                   // string
	MatchingMetrics     []string            `json:"_matching_metrics,omitempty"`      // [] >= 1 (result info only, if query has extras - cannot be set)
	MatchingUUIDMetrics map[string][]string `json:"_matching_uuid_metrics,omitempty"` // [] >= 1 (result info only, if query has extras - cannot be set)
	Description         string              `json:"description"`                      // string
	Name                string              `json:"name"`                             // string
	Queries             []MetricQuery       `json:"queries"`                          // [] >= 1
	Tags                []string            `json:"tags"`                             // [] >= 0
}

// NewMetricCluster returns a new MetricCluster (with defaults, if applicable)
func NewMetricCluster() *MetricCluster {
	return &MetricCluster{}
}

// FetchMetricCluster retrieves metric cluster with passed cid.
func (a *API) FetchMetricCluster(cid CIDType, extras string) (*MetricCluster, error) {
	if cid == nil || *cid == "" {
		return nil, fmt.Errorf("Invalid metric cluster CID [none]")
	}

	clusterCID := string(*cid)

	matched, err := regexp.MatchString(config.MetricClusterCIDRegex, clusterCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid metric cluster CID [%s]", clusterCID)
	}

	reqURL := url.URL{
		Path: clusterCID,
	}

	extra := ""
	switch extras {
	case "metrics":
		extra = "_matching_metrics"
	case "uuids":
		extra = "_matching_uuid_metrics"
	}

	if extra != "" {
		q := url.Values{}
		q.Set("extra", extra)
		reqURL.RawQuery = q.Encode()
	}

	result, err := a.Get(reqURL.String())
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] fetch metric cluster, received JSON: %s", string(result))
	}

	cluster := &MetricCluster{}
	if err := json.Unmarshal(result, cluster); err != nil {
		return nil, err
	}

	return cluster, nil
}

// FetchMetricClusters retrieves all metric clusters available to API Token.
func (a *API) FetchMetricClusters(extras string) (*[]MetricCluster, error) {
	reqURL := url.URL{
		Path: config.MetricClusterPrefix,
	}

	extra := ""
	switch extras {
	case "metrics":
		extra = "_matching_metrics"
	case "uuids":
		extra = "_matching_uuid_metrics"
	}

	if extra != "" {
		q := url.Values{}
		q.Set("extra", extra)
		reqURL.RawQuery = q.Encode()
	}

	result, err := a.Get(reqURL.String())
	if err != nil {
		return nil, err
	}

	var clusters []MetricCluster
	if err := json.Unmarshal(result, &clusters); err != nil {
		return nil, err
	}

	return &clusters, nil
}

// UpdateMetricCluster updates passed metric cluster.
func (a *API) UpdateMetricCluster(cfg *MetricCluster) (*MetricCluster, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid metric cluster config [nil]")
	}

	clusterCID := string(cfg.CID)

	matched, err := regexp.MatchString(config.MetricClusterCIDRegex, clusterCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid metric cluster CID [%s]", clusterCID)
	}

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

	if a.Debug {
		a.Log.Printf("[DEBUG] update metric cluster, sending JSON: %s", string(jsonCfg))
	}

	result, err := a.Put(clusterCID, jsonCfg)
	if err != nil {
		return nil, err
	}

	cluster := &MetricCluster{}
	if err := json.Unmarshal(result, cluster); err != nil {
		return nil, err
	}

	return cluster, nil
}

// CreateMetricCluster creates a new metric cluster.
func (a *API) CreateMetricCluster(cfg *MetricCluster) (*MetricCluster, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid metric cluster config [nil]")
	}

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

	if a.Debug {
		a.Log.Printf("[DEBUG] create metric cluster, sending JSON: %s", string(jsonCfg))
	}

	result, err := a.Post(config.MetricClusterPrefix, jsonCfg)
	if err != nil {
		return nil, err
	}

	cluster := &MetricCluster{}
	if err := json.Unmarshal(result, cluster); err != nil {
		return nil, err
	}

	return cluster, nil
}

// DeleteMetricCluster deletes passed metric cluster.
func (a *API) DeleteMetricCluster(cfg *MetricCluster) (bool, error) {
	if cfg == nil {
		return false, fmt.Errorf("Invalid metric cluster config [nil]")
	}
	return a.DeleteMetricClusterByCID(CIDType(&cfg.CID))
}

// DeleteMetricClusterByCID deletes metric cluster with passed cid.
func (a *API) DeleteMetricClusterByCID(cid CIDType) (bool, error) {
	if cid == nil || *cid == "" {
		return false, fmt.Errorf("Invalid metric cluster CID [none]")
	}

	clusterCID := string(*cid)

	matched, err := regexp.MatchString(config.MetricClusterCIDRegex, clusterCID)
	if err != nil {
		return false, err
	}
	if !matched {
		return false, fmt.Errorf("Invalid metric cluster CID [%s]", clusterCID)
	}

	_, err = a.Delete(clusterCID)
	if err != nil {
		return false, err
	}

	return true, nil
}

// SearchMetricClusters returns metric clusters matching the specified
// search query and/or filter. If nil is passed for both parameters
// all metric clusters will be returned.
func (a *API) SearchMetricClusters(searchCriteria *SearchQueryType, filterCriteria *SearchFilterType) (*[]MetricCluster, error) {
	q := url.Values{}

	if searchCriteria != nil && *searchCriteria != "" {
		q.Set("search", string(*searchCriteria))
	}

	if filterCriteria != nil && len(*filterCriteria) > 0 {
		for filter, criteria := range *filterCriteria {
			for _, val := range criteria {
				q.Add(filter, val)
			}
		}
	}

	if q.Encode() == "" {
		return a.FetchMetricClusters("")
	}

	reqURL := url.URL{
		Path:     config.MetricClusterPrefix,
		RawQuery: q.Encode(),
	}

	result, err := a.Get(reqURL.String())
	if err != nil {
		return nil, fmt.Errorf("[ERROR] API call error %+v", err)
	}

	var clusters []MetricCluster
	if err := json.Unmarshal(result, &clusters); err != nil {
		return nil, err
	}

	return &clusters, nil
}
<|endoftext|>


<commit_before>package option

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"
)

// The PutObjectInput type is an adapter to change a parameter in
// s3.PutObjectInput.
type PutObjectInput func(req *s3.PutObjectInput)

// SSEKMSKeyID returns a PutObjectInput that changes a SSE-KMS Key ID.
func SSEKMSKeyID(keyID string) PutObjectInput {
	return func(req *s3.PutObjectInput) {
		req.SSEKMSKeyId = aws.String(keyID)
	}
}

// ACLPrivate returns a PutObjectInput that set ACL private.
func ACLPrivate() PutObjectInput {
	return func(req *s3.PutObjectInput) {
		req.ACL = aws.String(s3.ObjectCannedACLPrivate)
	}
}
<commit_msg>Add Content{Type,Length}<commit_after>package option

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"
)

// The PutObjectInput type is an adapter to change a parameter in
// s3.PutObjectInput.
type PutObjectInput func(req *s3.PutObjectInput)

// SSEKMSKeyID returns a PutObjectInput that changes a SSE-KMS Key ID.
func SSEKMSKeyID(keyID string) PutObjectInput {
	return func(req *s3.PutObjectInput) {
		req.SSEKMSKeyId = aws.String(keyID)
	}
}

// ACLPrivate returns a PutObjectInput that set ACL private.
func ACLPrivate() PutObjectInput {
	return func(req *s3.PutObjectInput) {
		req.ACL = aws.String(s3.ObjectCannedACLPrivate)
	}
}

// ContentType returns a PutObjectInput that set Content-Type.
func ContentType(ct string) PutObjectInput {
	return func(req *s3.PutObjectInput) {
		req.ContentType = aws.String(ct)
	}
}

// ContentLength returns a PutObjectInput that set Content-Length.
func ContentLength(length int) PutObjectInput {
	return func(req *s3.PutObjectInput) {
		req.ContentLength = aws.Int64(int64(length))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"github.com/SoftwareDefinedBuildings/mr-plotter/keys"
	etcd "github.com/coreos/etcd/clientv3"
)

func main() {
	if len(os.Args) != 3 {
		fmt.Printf("Usage: %s cert.pem key.pem\n", os.Args[0])
		return
	}

	httpscert, err := ioutil.ReadFile(os.Args[1])
	if err != nil {
		log.Fatalf("Could not read HTTPS certificate file: %v", err)
	}
	httpskey, err := ioutil.ReadFile(os.Args[2])
	if err != nil {
		log.Fatalf("Could not read HTTPS certificate file: %v", err)
	}

	hardcoded := &keys.HardcodedTLSCertificate{
		Cert: httpscert,
		Key:  httpskey,
	}

	var etcdEndpoint = os.Getenv("ETCD_ENDPOINT")
	if len(etcdEndpoint) == 0 {
		etcdEndpoint = "localhost:2379"
		log.Printf("ETCD_ENDPOINT is not set; using %s", etcdEndpoint)
	}
	var etcdConfig = etcd.Config{Endpoints: []string{etcdEndpoint}}
	log.Println("Connecting to etcd...")
	etcdConn, err := etcd.New(etcdConfig)
	if err != nil {
		log.Fatalf("Error: %v", err)
	}
	defer etcdConn.Close()

	success, err := keys.UpsertHardcodedTLSCertificateAtomically(context.Background(), etcdConn, hardcoded)
	if err != nil {
		log.Fatalf("Could not update hardcoded TLS certificate: %v", err)
	}

	if success {
		log.Println("Success")
	} else {
		log.Println("Already exists")
	}
}
<commit_msg>Update hardcodecert.go<commit_after>package main

import (
	"context"
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"github.com/PingThingsIO/pt-mr-plotter/keys"
	etcd "github.com/coreos/etcd/clientv3"
)

func main() {
	if len(os.Args) != 3 {
		fmt.Printf("Usage: %s cert.pem key.pem\n", os.Args[0])
		return
	}

	httpscert, err := ioutil.ReadFile(os.Args[1])
	if err != nil {
		log.Fatalf("Could not read HTTPS certificate file: %v", err)
	}
	httpskey, err := ioutil.ReadFile(os.Args[2])
	if err != nil {
		log.Fatalf("Could not read HTTPS certificate file: %v", err)
	}

	hardcoded := &keys.HardcodedTLSCertificate{
		Cert: httpscert,
		Key:  httpskey,
	}

	var etcdEndpoint = os.Getenv("ETCD_ENDPOINT")
	if len(etcdEndpoint) == 0 {
		etcdEndpoint = "localhost:2379"
		log.Printf("ETCD_ENDPOINT is not set; using %s", etcdEndpoint)
	}
	var etcdConfig = etcd.Config{Endpoints: []string{etcdEndpoint}}
	log.Println("Connecting to etcd...")
	etcdConn, err := etcd.New(etcdConfig)
	if err != nil {
		log.Fatalf("Error: %v", err)
	}
	defer etcdConn.Close()

	success, err := keys.UpsertHardcodedTLSCertificateAtomically(context.Background(), etcdConn, hardcoded)
	if err != nil {
		log.Fatalf("Could not update hardcoded TLS certificate: %v", err)
	}

	if success {
		log.Println("Success")
	} else {
		log.Println("Already exists")
	}
}
<|endoftext|>


<commit_before>package structor

import (
	"reflect"
	"strings"
)

// Codec represents a struct and its fields.
type Codec struct {
	// Type returns the struct's type.
	Type reflect.Type

	// Fields returns the struct's field codecs.
	Fields []*FieldCodec

	// FieldNames returns the struct's exportable field names.
	FieldNames []string

	// Attrs can contain custom attributes of the codec.
	Attrs map[string]interface{}

	// Complete is whether the codec is completely processed.
	// An incomplete codec may be encountered when walking a recursive struct.
	Complete bool
}

func newCodec(rType reflect.Type, tagName string) *Codec {
	ret := &Codec{
		Type:  rType,
		Attrs: make(map[string]interface{}),
	}

	fieldsCount := rType.NumField()
	for idx := 0; idx < fieldsCount; idx++ {
		fCodec := newFieldCodec(rType, idx, tagName)
		if fCodec == nil {
			continue
		}
		fCodec.Parent = ret

		ret.Fields = append(ret.Fields, fCodec)
		ret.FieldNames = append(ret.FieldNames, fCodec.Name)
	}

	return ret
}

// FieldCodec represents a struct field.
type FieldCodec struct {
	Parent   *Codec
	Index    int
	Name     string
	Tag      *TagCodec
	Type     reflect.Type
	KeyType  *reflect.Type
	ElemType *reflect.Type
}

func newFieldCodec(rType reflect.Type, idx int, tagName string) *FieldCodec {
	fld := rType.Field(idx)
	if !isExportableField(fld) {
		return nil
	}

	fType := fld.Type
	fName := fld.Name
	fTag := newTagCodec(fld.Tag.Get(tagName))
	keyType, elemType := subTypesOf(fType)

	return &FieldCodec{
		Index:    idx,
		Name:     fName,
		Tag:      fTag,
		Type:     fType,
		KeyType:  keyType,
		ElemType: elemType,
	}
}

// TagCodec represents a struct field's tag.
type TagCodec struct {
	Values map[int]string
}

func newTagCodec(tag string) *TagCodec {
	vals := make(map[int]string, 0)
	if strings.TrimSpace(tag) != "" {
		for i, t := range strings.Split(tag, ",") {
			vals[i] = t
		}
	}
	return &TagCodec{vals}
}
<commit_msg>add comments<commit_after>package structor

import (
	"reflect"
	"strings"
)

// Codec represents a struct and its fields.
type Codec struct {
	// Type returns the struct's type.
	Type reflect.Type

	// Fields returns the struct's field codecs.
	Fields []*FieldCodec

	// FieldNames returns the struct's exportable field names.
	FieldNames []string

	// Attrs can contain custom attributes of the codec.
	Attrs map[string]interface{}

	// Complete is whether the codec is completely processed.
	// An incomplete codec may be encountered when walking a recursive struct.
	Complete bool
}

func newCodec(rType reflect.Type, tagName string) *Codec {
	ret := &Codec{
		Type:  rType,
		Attrs: make(map[string]interface{}),
	}

	fieldsCount := rType.NumField()
	for idx := 0; idx < fieldsCount; idx++ {
		fCodec := newFieldCodec(rType, idx, tagName)
		if fCodec == nil {
			continue
		}
		fCodec.Parent = ret

		ret.Fields = append(ret.Fields, fCodec)
		ret.FieldNames = append(ret.FieldNames, fCodec.Name)
	}

	return ret
}

// FieldCodec represents a struct field.
type FieldCodec struct {

	// Parent is a reference to the struct the field is in.
	Parent *Codec

	// Index is the index of the field in the struct.
	Index int

	// Name is the name of the field in the struct.
	Name string

	// Tag is the codec for the field's tags.
	Tag *TagCodec

	// Attrs can contain custom attributes of the field's codec.
	Attrs map[string]interface{}

	// Type is the field's type.
	Type reflect.Type

	// KeyType is the type of the field's key, if any.
	KeyType *reflect.Type

	// ElemType is the type of the field's collection's value type, if any.
	ElemType *reflect.Type
}

func newFieldCodec(rType reflect.Type, idx int, tagName string) *FieldCodec {
	fld := rType.Field(idx)
	if !isExportableField(fld) {
		return nil
	}

	fType := fld.Type
	fName := fld.Name
	fTag := newTagCodec(fld.Tag.Get(tagName))
	keyType, elemType := subTypesOf(fType)

	return &FieldCodec{
		Index:    idx,
		Name:     fName,
		Tag:      fTag,
		Type:     fType,
		KeyType:  keyType,
		ElemType: elemType,
	}
}

// TagCodec represents a struct field's tag.
type TagCodec struct {
	Values map[int]string
}

func newTagCodec(tag string) *TagCodec {
	vals := make(map[int]string, 0)
	if strings.TrimSpace(tag) != "" {
		for i, t := range strings.Split(tag, ",") {
			vals[i] = t
		}
	}
	return &TagCodec{vals}
}
<|endoftext|>