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

package main

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	gorillacontext "github.com/gorilla/context"
	"github.com/gorilla/mux"
	"golang.org/x/net/context"
	"io"
	"io/ioutil"
	_ "log"
	"net/http"
	_ "regexp"
	_ "strings"
	"time"
)

type action struct {
	Name string  `json:"name"`
	Cost float64 `json:"cost"`
}

type actionRequest struct {
	States           []event  `json:"states"`
	Window           int64    `json:"window"`
	Goal             string   `json:"goal"`
	Actions          []action `json:"actions"`
	Reward           float64  `json:"reward"`
	AnchorID         string   `json:"anchorid"`
	GainOnly         bool     `json:"gainonly"`
	Explore          int64    `json:"explore"` // FIXME: hidden feature in API DOC
	MuteActionRecord bool     `json:"mute"`
	CtxLength        int64    `json:"ctxlength"`
	Label            string   `json:"label"` // FIXME: hidden feature: new Tag to match goal label and event label
	Detail           bool     `json:"detail"`
}

type actionResult struct {
	Goal        string     `json:"goal"`
	Action      string     `json:"action"`
	Probability float64    `json:"baseline-prob"`
	ActTrue     float64    `json:"action-prob"` // FIXME: WHAT?
	Lift        float64    `json:"prob-lift-pct"`
	Gain        float64    `json:"expected-gain"`
	Error       bigioError `json:"item-status"`
}

// this matches response json result
type actionResponse struct {
	Results  []actionResult `json:"results"`
	Goal     string         `json:"goal"`
	Decision string         `json:"decision"` // FIXME: missing in API DOC
	Mode     string         `json:"mode"`
	Error    bigioError     `json:"status"`
}

func actionHandlerCommon(w http.ResponseWriter, r *http.Request, body []byte) []byte {
	c := context.Background()
	// get projectId from URL
	projectID := mux.Vars(r)["projectId"]
	sessionID := mux.Vars(r)["sessionId"]
	bigioDebugf(c, string(projectID))
	bigioDebugf(c, string(sessionID))

	var req actionRequest
	err := json.Unmarshal(body, &req)
	if err != nil {
		bigioDebugf(c, string(body))
		http.Error(w, "json parse error\n", 400)
		return []byte("")
	}

	var windowDays int64
	if req.Window == 0 {
		windowDays = 7
	} else {
		windowDays = req.Window
	}
	startDate, endDate := getDatestamps(windowDays)

	var goal string
	goal = req.Goal //better validation, if no goal, we throw error
	if goal == "" {
		bigioDebugf(c, string(body))
		http.Error(w, "required field goal is missing or empty\n", 400)
		return []byte("")
	}
	bigioDebugf(c, "Query Goal=%s", goal)

	var reward float64
	reward = req.Reward //better validation, if no reward, we default it to 1.0
	bigioDebugf(c, "Reward=%f", reward)
	if reward == 0 {
		reward = 1.0
		bigioDebugf(c, "reset default Reward=%f", reward)
	}

	var gainonly bool
	gainonly = req.GainOnly
	/* actions and their costs are array */

	var label string
	label = req.Label

	bigioDebugf(c, "Actions len=%d", len(req.Actions))
	if len(req.Actions) == 0 || len(req.Actions) > 10 {
		http.Error(w, "Min 1 action, Max 10 actions in one API call\n", 400)
		return []byte("")
	}

	//ConsistentId
	var anchorid string
	anchorid = req.AnchorID
	bigioDebugf(c, "AnchorId=%s", anchorid)
	//if anchorid exits, meaning consistent is turn on
	//check if we did any action decision for this session before
	//if yes, we return the old result.
	if len(anchorid) > 0 {
		anchorKey := fmt.Sprintf("pact:%s:%s:%s", projectID, sessionID, anchorid)
		prevResult, keyexist := cacheGet(anchorKey)
		if keyexist {
			bigioDebugf(c, "found previous action result for %s", anchorid)
			//set the cache expire for another 30 mins.
			cacheSetExpire30mins(anchorKey)
			//return prevResult and 200
			//w.Write([]byte(prevResult))
			return []byte(prevResult)
		}
	}

	// build supplemental historical state in "type#name" style from request event array
	reqStates, err := validateEachEventType(req.States)
	if err != nil {
		http.Error(w, fmt.Sprintf("Json field : %v, the event(s) after this are not processed", err.Error()), 400)
	}

	// retrieve context+atribute state from session_store
	var states []string
	CtxLength := req.CtxLength
	if len(sessionID) > 0 && CtxLength != -1 {
		if CtxLength == 0 {
			CtxLength = 10
		}
		states = sessionStore.mergeStates(projectID, sessionID, CtxLength, reqStates)
		bigioDebugf(c, "events with session store: %v", states)
	} else {
		states = reqStates
		bigioDebugf(c, "history events without session store: %v", states)
	}

	var truePosterior float64
	var actResults = make([]actionResult, 0)

	truePosterior, predStatus, predError := mutantClassify(projectID, goal, states, startDate, endDate)

	bigioDebugf(c, "CLASSIFY truePosterior=%f", truePosterior)

	var expectedRewards = make([]float64, len(req.Actions)) //this for active learning
	var baselineReward = 0.0
	var actionsStringArray = make([]string, len(req.Actions))
	var actPredictStatus = make([]bool, len(req.Actions))

	for i, action := range req.Actions {
		actionsStringArray[i] = action.Name

		if truePosterior > 0 {
			bigioDebugf(c, "Query Action=%s", action)
			lift := 0.0
			gain := 0.0
			atruePosterior, status, errorMsg := mutantActionify(projectID, action.Name, goal, states, startDate, endDate)
			//log.Debugf(c, "ACTIONIFY atruePosterior=%f", atruePosterior)
			if atruePosterior != -1 {
				lift = (atruePosterior - truePosterior) / truePosterior * 100         //show lift in percentage
				gain = atruePosterior*(reward-action.Cost) - (truePosterior * reward) //show gain in expected value minus cost_of_action
				expectedRewards[i] = atruePosterior * (reward - action.Cost)          //for active learning
				baselineReward = truePosterior * reward
				actPredictStatus[i] = true
			} else {
				actPredictStatus[i] = false
			}

			//append enture struct instead of pointer reference
			actResults = append(actResults, actionResult{
				Goal:        goal,
				Action:      action.Name,
				Probability: truePosterior,
				ActTrue:     atruePosterior,
				Lift:        lift,
				Gain:        gain,
				Error:       bigioError{Code: status, Message: errorMsg}})

		} else {
			//fix me, do we need need to anything?
		}
	}

	//Active Learning
	bigioDebugf(c, "expectedRewards: %v", expectedRewards)
	var pickedByActive string
	var pickedIndex int
	var pickedMode string
	//var probArray []float64

	pickedIndex, pickedMode = activeLearning(req.Explore, actPredictStatus, expectedRewards, baselineReward, gainonly)
	if pickedIndex > 0 {
		pickedByActive = actionsStringArray[pickedIndex]
	} else {
		pickedByActive = "no-action"
	}

	var res *actionResponse

	if predStatus == 0 {
		if req.Detail == true {
			res = &actionResponse{
				Results:  actResults,
				Goal:     goal,
				Decision: pickedByActive,
				Mode:     pickedMode,
			}
		} else {
			//var actResultsEmpty []ActionResult
			var actResultsEmpty = make([]actionResult, 0)
			res = &actionResponse{
				Results:  actResultsEmpty,
				Goal:     goal,
				Decision: pickedByActive,
				Mode:     pickedMode,
			}
		}
	} else {
		//when there is no goal event found, we always explore (learn)
		pickedIndex, _ = alwaysExploreAL(actPredictStatus, expectedRewards, baselineReward, gainonly)
		pickedByActive = actionsStringArray[pickedIndex]

		bigioDebugf(c, "alwaysExplore predStatus=0")
		res = &actionResponse{
			Results:  actResults,
			Goal:     goal,
			Decision: pickedByActive,
			Mode:     "learn-goal",
			Error:    bigioError{Code: predStatus, Message: predError},
		}

	}

	returnBody, _ := json.Marshal(res)
	if len(anchorid) > 0 {
		anchorKey := fmt.Sprintf("pact:%s:%s:%s", projectID, sessionID, anchorid)
		cacheSet30mins(anchorKey, string(returnBody))
	}

	if req.MuteActionRecord == false {
		var evt pubSubEvent
		evt.ProjectID = projectID //this is from URL wildcard pattern
		evt.ID = sessionID
		evt.Timestamp = time.Now().UnixNano() / 1000000

		evt.SsnConf = gorillacontext.Get(r, "ssnConf").(string)
		//evt.EventName = pickedByActive + "#ACT" //Suffix
		evt.EventName = pickedByActive
		evt.EventType = "ACT"
		evt.Label = label
		err := fireEvent(evt)
		if err != nil {
			//log.Errorf(c, "failed to record action")
			bigioErrorf(c, "failed to record action")
		} else {
			bigioDebugf(c, "action recorded")
		}
	}

	return returnBody
}

func actionHandlerPOST(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "unsupported HTTP verb", 400)
		return
	}

	//c := appengine.NewContext(r)
	c := context.Background()
	//ctx, _ := appengine.Namespace(c, datastoreNamespace) // remove
	//	ctx_bg := context.Background()
	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1048576)) //max json input 1MB
	if err != nil {
		http.Error(w, "json payload too large, max 1 MByte\n", 400) //413
		return
	}
	bigioDebugf(c, "%s", body)

	returnBody := actionHandlerCommon(w, r, body)
	w.Write(returnBody)
}

func actionHandlerGET(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, "unsupported HTTP verb", 400)
		return
	}

	//c := appengine.NewContext(r)
	c := context.Background()
	//ctx, _ := appengine.Namespace(c, datastoreNamespace) //remove

	//get it from Querystring "data=", base64 encoded
	base64Str := r.URL.Query().Get("data")
	body, err := base64.StdEncoding.DecodeString(base64Str)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	bigioDebugf(c, "%s", body)

	returnBody := actionHandlerCommon(w, r, body)
	w.Write(returnBody)
}
<commit_msg>refactor action api.<commit_after>// Copyright 2017 BigIO.host. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	gorillacontext "github.com/gorilla/context"
	"github.com/gorilla/mux"
	"golang.org/x/net/context"
	"io"
	"io/ioutil"
	_ "log"
	"net/http"
	_ "regexp"
	_ "strings"
	"time"
)

type action struct {
	Name string  `json:"name"`
	Cost float64 `json:"cost"`
}

type actionRequest struct {
	States           []event  `json:"states"`
	Window           int64    `json:"window"`
	Goal             string   `json:"goal"`
	Actions          []action `json:"actions"`
	Reward           float64  `json:"reward"`
	AnchorID         string   `json:"anchorid"`
	GainOnly         bool     `json:"gainonly"`
	Explore          int64    `json:"explore"` // FIXME: hidden feature in API DOC
	MuteActionRecord bool     `json:"mute"`
	CtxLength        int64    `json:"ctxlength"`
	Label            string   `json:"label"` // FIXME: hidden feature: new Tag to match goal label and event label
	Detail           bool     `json:"detail"`
}

type actionResult struct {
	Goal        string     `json:"goal"`
	Action      string     `json:"action"`
	Probability float64    `json:"baseline-prob"`
	ActTrue     float64    `json:"action-prob"` // FIXME: WHAT?
	Lift        float64    `json:"prob-lift-pct"`
	Gain        float64    `json:"expected-gain"`
	Error       bigioError `json:"item-status"`
}

// this matches response json result
type actionResponse struct {
	Results  []actionResult `json:"results"`
	Goal     string         `json:"goal"`
	Decision string         `json:"decision"` // FIXME: missing in API DOC
	Mode     string         `json:"mode"`
	Error    bigioError     `json:"status"`
}

func actionHandlerCommon(w http.ResponseWriter, r *http.Request, body []byte) []byte {
	c := context.Background()
	// get projectId from URL
	projectID := mux.Vars(r)["projectId"]
	sessionID := mux.Vars(r)["sessionId"]
	bigioDebugf(c, string(projectID))
	bigioDebugf(c, string(sessionID))

	var req actionRequest
	err := json.Unmarshal(body, &req)
	if err != nil {
		bigioDebugf(c, string(body))
		http.Error(w, "json parse error\n", 400)
		return []byte("")
	}

	var windowDays int64
	if req.Window == 0 {
		windowDays = 7
	} else {
		windowDays = req.Window
	}
	startDate, endDate := getDatestamps(windowDays)

	var goal string
	goal = req.Goal //better validation, if no goal, we throw error
	if goal == "" {
		bigioDebugf(c, string(body))
		http.Error(w, "required field goal is missing or empty\n", 400)
		return []byte("")
	}
	bigioDebugf(c, "Query Goal=%s", goal)

	var reward float64
	reward = req.Reward //better validation, if no reward, we default it to 1.0
	bigioDebugf(c, "Reward=%f", reward)
	if reward == 0 {
		reward = 1.0
		bigioDebugf(c, "reset default Reward=%f", reward)
	}

	var gainonly bool
	gainonly = req.GainOnly
	/* actions and their costs are array */

	var label string
	label = req.Label

	bigioDebugf(c, "Actions len=%d", len(req.Actions))
	if len(req.Actions) == 0 || len(req.Actions) > 10 {
		http.Error(w, "Min 1 action, Max 10 actions in one API call\n", 400)
		return []byte("")
	}

	//ConsistentId
	var anchorid string
	anchorid = req.AnchorID
	bigioDebugf(c, "AnchorId=%s", anchorid)
	//if anchorid exits, meaning consistent is turn on
	//check if we did any action decision for this session before
	//if yes, we return the old result.
	if len(anchorid) > 0 {
		anchorKey := fmt.Sprintf("pact:%s:%s:%s", projectID, sessionID, anchorid)
		prevResult, keyexist := cacheGet(anchorKey)
		if keyexist {
			bigioDebugf(c, "found previous action result for %s", anchorid)
			//set the cache expire for another 30 mins.
			cacheSetExpire30mins(anchorKey)
			//return prevResult and 200
			//w.Write([]byte(prevResult))
			return []byte(prevResult)
		}
	}

	// build supplemental historical state in "type#name" style from request event array
	reqStates, err := validateEachEventType(req.States)
	if err != nil {
		http.Error(w, fmt.Sprintf("Json field : %v, the event(s) after this are not processed", err.Error()), 400)
	}

	// retrieve context+atribute state from session_store
	var states []string
	CtxLength := req.CtxLength
	if len(sessionID) > 0 && CtxLength != -1 {
		if CtxLength == 0 {
			CtxLength = 10
		}
		states = sessionStore.mergeStates(projectID, sessionID, CtxLength, reqStates)
		bigioDebugf(c, "events with session store: %v", states)
	} else {
		states = reqStates
		bigioDebugf(c, "history events without session store: %v", states)
	}

	var truePosterior float64
	var actResults = make([]actionResult, 0)

	truePosterior, predStatus, predError := mutantClassify(projectID, goal, states, startDate, endDate)
	bigioDebugf(c, "CLASSIFY truePosterior=%f", truePosterior)

	var expectedRewards = make([]float64, len(req.Actions)) //this for active learning
	var baselineReward = 0.0
	var actionsStringArray = make([]string, len(req.Actions))
	var actPredictStatus = make([]bool, len(req.Actions))

	var pickedByActive string
	var pickedIndex int
	var pickedMode string
	//var probArray []float64

	var res *actionResponse

	for i, action := range req.Actions {
		actionsStringArray[i] = action.Name
		actPredictStatus[i] = false
	}

	//if truePosterior > 0 {
	if predStatus == 0 && truePosterior > 0 {
		//for each action we need to consider
		for i, action := range req.Actions {
			bigioDebugf(c, "Query Action=%s", action)
			lift := 0.0
			gain := 0.0

			atruePosterior, status, errorMsg := mutantActionify(projectID, action.Name, goal, states, startDate, endDate)
			//log.Debugf(c, "ACTIONIFY atruePosterior=%f", atruePosterior)
			if status == 0 {
				lift = (atruePosterior - truePosterior) / truePosterior * 100         //show lift in percentage
				gain = atruePosterior*(reward-action.Cost) - (truePosterior * reward) //show gain in expected value minus cost_of_action
				expectedRewards[i] = atruePosterior * (reward - action.Cost)          //for active learning
				baselineReward = truePosterior * reward
				actPredictStatus[i] = true
			}

			actR := actionResult{
				Goal:        goal,
				Action:      action.Name,
				Probability: truePosterior,
				ActTrue:     atruePosterior,
				Lift:        lift,
				Gain:        gain,
				Error:       bigioError{Code: status, Message: errorMsg}}

			//append enture struct instead of pointer reference
			actResults = append(actResults, actR)
		}

		//Active Learning
		bigioDebugf(c, "expectedRewards: %v", expectedRewards)
		pickedIndex, pickedMode = activeLearning(req.Explore, actPredictStatus, expectedRewards, baselineReward, gainonly)
		if pickedIndex > 0 {
			pickedByActive = actionsStringArray[pickedIndex]
		} else {
			pickedByActive = "no-action"
		}

		var actResultsPointer = make([]actionResult, 0)
		if req.Detail == true {
			actResultsPointer = actResults
		}
		res = &actionResponse{
			Results:  actResultsPointer,
			Goal:     goal,
			Decision: pickedByActive,
			Mode:     pickedMode,
		}

	} else {
		//when there is no goal event found, we always explore (learn)
		pickedIndex, _ = alwaysExploreAL(actPredictStatus, expectedRewards, baselineReward, gainonly)
		pickedByActive = actionsStringArray[pickedIndex]

		bigioDebugf(c, "alwaysExplore predStatus=0")
		res = &actionResponse{
			Results:  actResults,
			Goal:     goal,
			Decision: pickedByActive,
			Mode:     "learn-goal",
			Error:    bigioError{Code: predStatus, Message: predError},
		}
	}

	returnBody, _ := json.Marshal(res)

	if len(anchorid) > 0 {
		anchorKey := fmt.Sprintf("pact:%s:%s:%s", projectID, sessionID, anchorid)
		cacheSet30mins(anchorKey, string(returnBody))
	}

	if req.MuteActionRecord == false {
		var evt pubSubEvent
		evt.ProjectID = projectID //this is from URL wildcard pattern
		evt.ID = sessionID
		evt.Timestamp = time.Now().UnixNano() / 1000000

		evt.SsnConf = gorillacontext.Get(r, "ssnConf").(string)
		evt.EventName = pickedByActive
		evt.EventType = "ACT"
		evt.Label = label
		err := fireEvent(evt)
		if err != nil {
			//log.Errorf(c, "failed to record action")
			bigioErrorf(c, "failed to record action")
		} else {
			bigioDebugf(c, "action recorded")
		}
	}

	return returnBody
}

func actionHandlerPOST(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "unsupported HTTP verb", 400)
		return
	}

	//c := appengine.NewContext(r)
	c := context.Background()
	//ctx, _ := appengine.Namespace(c, datastoreNamespace) // remove
	//	ctx_bg := context.Background()
	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1048576)) //max json input 1MB
	if err != nil {
		http.Error(w, "json payload too large, max 1 MByte\n", 400) //413
		return
	}
	bigioDebugf(c, "%s", body)

	returnBody := actionHandlerCommon(w, r, body)
	w.Write(returnBody)
}

func actionHandlerGET(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, "unsupported HTTP verb", 400)
		return
	}

	//c := appengine.NewContext(r)
	c := context.Background()
	//ctx, _ := appengine.Namespace(c, datastoreNamespace) //remove

	//get it from Querystring "data=", base64 encoded
	base64Str := r.URL.Query().Get("data")
	body, err := base64.StdEncoding.DecodeString(base64Str)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	bigioDebugf(c, "%s", body)

	returnBody := actionHandlerCommon(w, r, body)
	w.Write(returnBody)
}
<|endoftext|>


<commit_before>package teams

import (
	"errors"
	"fmt"

	"golang.org/x/net/context"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
)

func membersUIDsToUsernames(ctx context.Context, g *libkb.GlobalContext, m keybase1.TeamMembers, forceRepoll bool) (keybase1.TeamMembersDetails, error) {
	var ret keybase1.TeamMembersDetails
	var err error
	ret.Owners, err = userVersionsToDetails(ctx, g, m.Owners, forceRepoll)
	if err != nil {
		return ret, err
	}
	ret.Admins, err = userVersionsToDetails(ctx, g, m.Admins, forceRepoll)
	if err != nil {
		return ret, err
	}
	ret.Writers, err = userVersionsToDetails(ctx, g, m.Writers, forceRepoll)
	if err != nil {
		return ret, err
	}
	ret.Readers, err = userVersionsToDetails(ctx, g, m.Readers, forceRepoll)
	if err != nil {
		return ret, err
	}
	return ret, nil
}

func Details(ctx context.Context, g *libkb.GlobalContext, name string, forceRepoll bool) (res keybase1.TeamDetails, err error) {
	t, err := GetForTeamManagementByStringName(ctx, g, name)
	if err != nil {
		return res, err
	}
	res.KeyGeneration = t.Generation()
	res.Members, err = members(ctx, g, t, forceRepoll)
	if err != nil {
		return res, err
	}
	return res, nil
}

func members(ctx context.Context, g *libkb.GlobalContext, t *Team, forceRepoll bool) (keybase1.TeamMembersDetails, error) {
	members, err := t.Members()
	if err != nil {
		return keybase1.TeamMembersDetails{}, err
	}
	return membersUIDsToUsernames(ctx, g, members, forceRepoll)
}

func userVersionToDetails(ctx context.Context, g *libkb.GlobalContext, uv keybase1.UserVersion, forceRepoll bool) (res keybase1.TeamMemberDetails, err error) {
	_, nun, err := loadMember(ctx, g, uv, forceRepoll)
	active := true
	if err != nil {
		if _, reset := err.(libkb.AccountResetError); reset {
			active = false
		} else {
			return res, err
		}
	}
	return keybase1.TeamMemberDetails{
		Uv:       uv,
		Username: nun.String(),
		Active:   active,
	}, nil
}

func userVersionsToDetails(ctx context.Context, g *libkb.GlobalContext, uvs []keybase1.UserVersion, forceRepoll bool) (ret []keybase1.TeamMemberDetails, err error) {
	for _, uv := range uvs {
		det, err := userVersionToDetails(ctx, g, uv, forceRepoll)
		if err != nil {
			return nil, err
		}
		ret = append(ret, det)
	}
	return ret, nil
}

func SetRoleOwner(ctx context.Context, g *libkb.GlobalContext, teamname, username string) error {
	uv, err := loadUserVersionByUsername(ctx, g, username)
	if err != nil {
		return err
	}
	return ChangeRoles(ctx, g, teamname, keybase1.TeamChangeReq{Owners: []keybase1.UserVersion{uv}})
}

func SetRoleAdmin(ctx context.Context, g *libkb.GlobalContext, teamname, username string) error {
	uv, err := loadUserVersionByUsername(ctx, g, username)
	if err != nil {
		return err
	}
	return ChangeRoles(ctx, g, teamname, keybase1.TeamChangeReq{Admins: []keybase1.UserVersion{uv}})
}

func SetRoleWriter(ctx context.Context, g *libkb.GlobalContext, teamname, username string) error {
	uv, err := loadUserVersionByUsername(ctx, g, username)
	if err != nil {
		return err
	}
	return ChangeRoles(ctx, g, teamname, keybase1.TeamChangeReq{Writers: []keybase1.UserVersion{uv}})
}

func SetRoleReader(ctx context.Context, g *libkb.GlobalContext, teamname, username string) error {
	uv, err := loadUserVersionByUsername(ctx, g, username)
	if err != nil {
		return err
	}
	return ChangeRoles(ctx, g, teamname, keybase1.TeamChangeReq{Readers: []keybase1.UserVersion{uv}})
}

func AddMember(ctx context.Context, g *libkb.GlobalContext, teamname, username string, role keybase1.TeamRole) error {
	t, err := GetForTeamManagementByStringName(ctx, g, teamname)
	if err != nil {
		return err
	}
	uv, err := loadUserVersionByUsername(ctx, g, username)
	if err != nil {
		return err
	}
	if t.IsMember(ctx, uv) {
		return fmt.Errorf("user %q is already a member of team %q", username, teamname)
	}
	req, err := reqFromRole(uv, role)
	if err != nil {
		return err
	}

	return t.ChangeMembership(ctx, req)
}

func EditMember(ctx context.Context, g *libkb.GlobalContext, teamname, username string, role keybase1.TeamRole) error {
	t, err := GetForTeamManagementByStringName(ctx, g, teamname)
	if err != nil {
		return err
	}
	uv, err := loadUserVersionByUsername(ctx, g, username)
	if err != nil {
		return err
	}
	if !t.IsMember(ctx, uv) {
		return fmt.Errorf("user %q is not a member of team %q", username, teamname)
	}
	existingRole, err := t.MemberRole(ctx, uv)
	if err != nil {
		return err
	}
	if existingRole == role {
		return fmt.Errorf("user %q in team %q already has the role %s", username, teamname, role)
	}

	req, err := reqFromRole(uv, role)
	if err != nil {
		return err
	}

	return t.ChangeMembership(ctx, req)
}

func MemberRole(ctx context.Context, g *libkb.GlobalContext, teamname, username string) (keybase1.TeamRole, error) {
	t, err := GetForTeamManagementByStringName(ctx, g, teamname)
	if err != nil {
		return keybase1.TeamRole_NONE, err
	}
	uv, err := loadUserVersionByUsername(ctx, g, username)
	if err != nil {
		return keybase1.TeamRole_NONE, err
	}
	return t.MemberRole(ctx, uv)
}

func RemoveMember(ctx context.Context, g *libkb.GlobalContext, teamname, username string) error {
	t, err := GetForTeamManagementByStringName(ctx, g, teamname)
	if err != nil {
		return err
	}
	uv, err := loadUserVersionByUsername(ctx, g, username)
	if err != nil {
		return err
	}
	if !t.IsMember(ctx, uv) {
		return libkb.NotFoundError{Msg: fmt.Sprintf("user %q is not a member of team %q", username, teamname)}
	}

	me, err := libkb.LoadMe(libkb.NewLoadUserArgWithContext(ctx, g))
	if err != nil {
		return err
	}

	if me.GetNormalizedName().Eq(libkb.NewNormalizedUsername(username)) {
		return Leave(ctx, g, teamname, false)
	}
	req := keybase1.TeamChangeReq{None: []keybase1.UserVersion{uv}}
	return t.ChangeMembership(ctx, req)
}

func Leave(ctx context.Context, g *libkb.GlobalContext, teamname string, permanent bool) error {
	t, err := GetForTeamManagementByStringName(ctx, g, teamname)
	if err != nil {
		return err
	}
	return t.Leave(ctx, permanent)
}

func ChangeRoles(ctx context.Context, g *libkb.GlobalContext, teamname string, req keybase1.TeamChangeReq) error {
	t, err := GetForTeamManagementByStringName(ctx, g, teamname)
	if err != nil {
		return err
	}
	return t.ChangeMembership(ctx, req)
}

var inviteRequired = errors.New("invite required for username")

func loadUserVersionByUsername(ctx context.Context, g *libkb.GlobalContext, username string) (keybase1.UserVersion, error) {
	res := g.Resolver.ResolveWithBody(username)
	if res.GetError() != nil {
		if e, ok := res.GetError().(libkb.ResolutionError); ok && e.Kind == libkb.ResolutionErrorNotFound {
			// couldn't find a keybase user for username assertion
			return keybase1.UserVersion{}, inviteRequired
		}
		return keybase1.UserVersion{}, res.GetError()
	}

	return loadUserVersionByUIDCheckUsername(ctx, g, res.GetUID(), username)
}

func loadUserVersionByUID(ctx context.Context, g *libkb.GlobalContext, uid keybase1.UID) (keybase1.UserVersion, error) {
	return loadUserVersionByUIDCheckUsername(ctx, g, uid, "")
}

func loadUserVersionByUIDCheckUsername(ctx context.Context, g *libkb.GlobalContext, uid keybase1.UID, un string) (keybase1.UserVersion, error) {
	upak, err := loadUPAK2(ctx, g, uid, true /*forcePoll */)
	if err != nil {
		return keybase1.UserVersion{}, err
	}
	if un != "" && !libkb.NormalizedUsername(un).Eq(libkb.NormalizedUsername(upak.Current.Username)) {
		return keybase1.UserVersion{}, libkb.BadUsernameError{N: un}
	}

	if len(upak.Current.PerUserKeys) == 0 {
		return NewUserVersion(upak.Current.Uid, upak.Current.EldestSeqno), inviteRequired
	}

	return NewUserVersion(upak.Current.Uid, upak.Current.EldestSeqno), nil
}

func reqFromRole(uv keybase1.UserVersion, role keybase1.TeamRole) (keybase1.TeamChangeReq, error) {

	var req keybase1.TeamChangeReq
	list := []keybase1.UserVersion{uv}
	switch role {
	case keybase1.TeamRole_OWNER:
		req.Owners = list
	case keybase1.TeamRole_ADMIN:
		req.Admins = list
	case keybase1.TeamRole_WRITER:
		req.Writers = list
	case keybase1.TeamRole_READER:
		req.Readers = list
	default:
		return keybase1.TeamChangeReq{}, errors.New("invalid team role")
	}

	return req, nil
}

func makeIdentifyLiteRes(id keybase1.TeamID, name keybase1.TeamName) keybase1.IdentifyLiteRes {
	return keybase1.IdentifyLiteRes{
		Ul: keybase1.UserOrTeamLite{
			Id:   id.AsUserOrTeam(),
			Name: name.String(),
		},
	}
}

func identifyLiteByID(ctx context.Context, g *libkb.GlobalContext, utid keybase1.UserOrTeamID, id2 keybase1.TeamID) (res keybase1.IdentifyLiteRes, err error) {

	var id1 keybase1.TeamID
	id1, err = utid.AsTeam()
	if err != nil {
		return res, err
	}

	if id1.Exists() && id2.Exists() && !id1.Eq(id2) {
		return res, errors.New("two team IDs given that don't match")
	}
	if !id1.Exists() {
		id1 = id2
	}
	if !id1.Exists() {
		return res, errors.New("empty IDs given")
	}
	var name keybase1.TeamName
	name, err = ResolveIDToName(ctx, g, id1)
	if err != nil {
		return res, err
	}

	return makeIdentifyLiteRes(id1, name), nil
}

func identifyLiteByName(ctx context.Context, g *libkb.GlobalContext, name keybase1.TeamName) (res keybase1.IdentifyLiteRes, err error) {
	var id keybase1.TeamID
	id, err = ResolveNameToID(ctx, g, name)
	if err != nil {
		return res, err
	}
	return makeIdentifyLiteRes(id, name), nil
}

func IdentifyLite(ctx context.Context, g *libkb.GlobalContext, arg keybase1.IdentifyLiteArg, au libkb.AssertionURL) (res keybase1.IdentifyLiteRes, err error) {

	if arg.Id.Exists() || au.IsTeamID() {
		return identifyLiteByID(ctx, g, arg.Id, au.ToTeamID())
	}
	if au.IsTeamName() {
		return identifyLiteByName(ctx, g, au.ToTeamName())
	}
	err = errors.New("could not identify team by ID or name")
	return res, err
}
<commit_msg>Add inviteMember stub<commit_after>package teams

import (
	"errors"
	"fmt"

	"golang.org/x/net/context"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
)

func membersUIDsToUsernames(ctx context.Context, g *libkb.GlobalContext, m keybase1.TeamMembers, forceRepoll bool) (keybase1.TeamMembersDetails, error) {
	var ret keybase1.TeamMembersDetails
	var err error
	ret.Owners, err = userVersionsToDetails(ctx, g, m.Owners, forceRepoll)
	if err != nil {
		return ret, err
	}
	ret.Admins, err = userVersionsToDetails(ctx, g, m.Admins, forceRepoll)
	if err != nil {
		return ret, err
	}
	ret.Writers, err = userVersionsToDetails(ctx, g, m.Writers, forceRepoll)
	if err != nil {
		return ret, err
	}
	ret.Readers, err = userVersionsToDetails(ctx, g, m.Readers, forceRepoll)
	if err != nil {
		return ret, err
	}
	return ret, nil
}

func Details(ctx context.Context, g *libkb.GlobalContext, name string, forceRepoll bool) (res keybase1.TeamDetails, err error) {
	t, err := GetForTeamManagementByStringName(ctx, g, name)
	if err != nil {
		return res, err
	}
	res.KeyGeneration = t.Generation()
	res.Members, err = members(ctx, g, t, forceRepoll)
	if err != nil {
		return res, err
	}
	return res, nil
}

func members(ctx context.Context, g *libkb.GlobalContext, t *Team, forceRepoll bool) (keybase1.TeamMembersDetails, error) {
	members, err := t.Members()
	if err != nil {
		return keybase1.TeamMembersDetails{}, err
	}
	return membersUIDsToUsernames(ctx, g, members, forceRepoll)
}

func userVersionToDetails(ctx context.Context, g *libkb.GlobalContext, uv keybase1.UserVersion, forceRepoll bool) (res keybase1.TeamMemberDetails, err error) {
	_, nun, err := loadMember(ctx, g, uv, forceRepoll)
	active := true
	if err != nil {
		if _, reset := err.(libkb.AccountResetError); reset {
			active = false
		} else {
			return res, err
		}
	}
	return keybase1.TeamMemberDetails{
		Uv:       uv,
		Username: nun.String(),
		Active:   active,
	}, nil
}

func userVersionsToDetails(ctx context.Context, g *libkb.GlobalContext, uvs []keybase1.UserVersion, forceRepoll bool) (ret []keybase1.TeamMemberDetails, err error) {
	for _, uv := range uvs {
		det, err := userVersionToDetails(ctx, g, uv, forceRepoll)
		if err != nil {
			return nil, err
		}
		ret = append(ret, det)
	}
	return ret, nil
}

func SetRoleOwner(ctx context.Context, g *libkb.GlobalContext, teamname, username string) error {
	uv, err := loadUserVersionByUsername(ctx, g, username)
	if err != nil {
		return err
	}
	return ChangeRoles(ctx, g, teamname, keybase1.TeamChangeReq{Owners: []keybase1.UserVersion{uv}})
}

func SetRoleAdmin(ctx context.Context, g *libkb.GlobalContext, teamname, username string) error {
	uv, err := loadUserVersionByUsername(ctx, g, username)
	if err != nil {
		return err
	}
	return ChangeRoles(ctx, g, teamname, keybase1.TeamChangeReq{Admins: []keybase1.UserVersion{uv}})
}

func SetRoleWriter(ctx context.Context, g *libkb.GlobalContext, teamname, username string) error {
	uv, err := loadUserVersionByUsername(ctx, g, username)
	if err != nil {
		return err
	}
	return ChangeRoles(ctx, g, teamname, keybase1.TeamChangeReq{Writers: []keybase1.UserVersion{uv}})
}

func SetRoleReader(ctx context.Context, g *libkb.GlobalContext, teamname, username string) error {
	uv, err := loadUserVersionByUsername(ctx, g, username)
	if err != nil {
		return err
	}
	return ChangeRoles(ctx, g, teamname, keybase1.TeamChangeReq{Readers: []keybase1.UserVersion{uv}})
}

func AddMember(ctx context.Context, g *libkb.GlobalContext, teamname, username string, role keybase1.TeamRole) error {
	t, err := GetForTeamManagementByStringName(ctx, g, teamname)
	if err != nil {
		return err
	}
	uv, err := loadUserVersionByUsername(ctx, g, username)
	if err != nil {
		if err == errInviteRequired {
			return inviteMember(ctx, g, t, username, role, uv)
		}
		return err
	}
	if t.IsMember(ctx, uv) {
		return fmt.Errorf("user %q is already a member of team %q", username, teamname)
	}
	req, err := reqFromRole(uv, role)
	if err != nil {
		return err
	}

	return t.ChangeMembership(ctx, req)
}

func inviteMember(ctx context.Context, g *libkb.GlobalContext, team *Team, username string, role keybase1.TeamRole, uv keybase1.UserVersion) error {
	return nil
}

func EditMember(ctx context.Context, g *libkb.GlobalContext, teamname, username string, role keybase1.TeamRole) error {
	t, err := GetForTeamManagementByStringName(ctx, g, teamname)
	if err != nil {
		return err
	}
	uv, err := loadUserVersionByUsername(ctx, g, username)
	if err != nil {
		return err
	}
	if !t.IsMember(ctx, uv) {
		return fmt.Errorf("user %q is not a member of team %q", username, teamname)
	}
	existingRole, err := t.MemberRole(ctx, uv)
	if err != nil {
		return err
	}
	if existingRole == role {
		return fmt.Errorf("user %q in team %q already has the role %s", username, teamname, role)
	}

	req, err := reqFromRole(uv, role)
	if err != nil {
		return err
	}

	return t.ChangeMembership(ctx, req)
}

func MemberRole(ctx context.Context, g *libkb.GlobalContext, teamname, username string) (keybase1.TeamRole, error) {
	t, err := GetForTeamManagementByStringName(ctx, g, teamname)
	if err != nil {
		return keybase1.TeamRole_NONE, err
	}
	uv, err := loadUserVersionByUsername(ctx, g, username)
	if err != nil {
		return keybase1.TeamRole_NONE, err
	}
	return t.MemberRole(ctx, uv)
}

func RemoveMember(ctx context.Context, g *libkb.GlobalContext, teamname, username string) error {
	t, err := GetForTeamManagementByStringName(ctx, g, teamname)
	if err != nil {
		return err
	}
	uv, err := loadUserVersionByUsername(ctx, g, username)
	if err != nil {
		return err
	}
	if !t.IsMember(ctx, uv) {
		return libkb.NotFoundError{Msg: fmt.Sprintf("user %q is not a member of team %q", username, teamname)}
	}

	me, err := libkb.LoadMe(libkb.NewLoadUserArgWithContext(ctx, g))
	if err != nil {
		return err
	}

	if me.GetNormalizedName().Eq(libkb.NewNormalizedUsername(username)) {
		return Leave(ctx, g, teamname, false)
	}
	req := keybase1.TeamChangeReq{None: []keybase1.UserVersion{uv}}
	return t.ChangeMembership(ctx, req)
}

func Leave(ctx context.Context, g *libkb.GlobalContext, teamname string, permanent bool) error {
	t, err := GetForTeamManagementByStringName(ctx, g, teamname)
	if err != nil {
		return err
	}
	return t.Leave(ctx, permanent)
}

func ChangeRoles(ctx context.Context, g *libkb.GlobalContext, teamname string, req keybase1.TeamChangeReq) error {
	t, err := GetForTeamManagementByStringName(ctx, g, teamname)
	if err != nil {
		return err
	}
	return t.ChangeMembership(ctx, req)
}

var errInviteRequired = errors.New("invite required for username")

func loadUserVersionByUsername(ctx context.Context, g *libkb.GlobalContext, username string) (keybase1.UserVersion, error) {
	res := g.Resolver.ResolveWithBody(username)
	if res.GetError() != nil {
		if e, ok := res.GetError().(libkb.ResolutionError); ok && e.Kind == libkb.ResolutionErrorNotFound {
			// couldn't find a keybase user for username assertion
			return keybase1.UserVersion{}, errInviteRequired
		}
		return keybase1.UserVersion{}, res.GetError()
	}

	return loadUserVersionByUIDCheckUsername(ctx, g, res.GetUID(), username)
}

func loadUserVersionByUID(ctx context.Context, g *libkb.GlobalContext, uid keybase1.UID) (keybase1.UserVersion, error) {
	return loadUserVersionByUIDCheckUsername(ctx, g, uid, "")
}

func loadUserVersionByUIDCheckUsername(ctx context.Context, g *libkb.GlobalContext, uid keybase1.UID, un string) (keybase1.UserVersion, error) {
	upak, err := loadUPAK2(ctx, g, uid, true /*forcePoll */)
	if err != nil {
		return keybase1.UserVersion{}, err
	}
	if un != "" && !libkb.NormalizedUsername(un).Eq(libkb.NormalizedUsername(upak.Current.Username)) {
		return keybase1.UserVersion{}, libkb.BadUsernameError{N: un}
	}

	if len(upak.Current.PerUserKeys) == 0 {
		return NewUserVersion(upak.Current.Uid, upak.Current.EldestSeqno), errInviteRequired
	}

	return NewUserVersion(upak.Current.Uid, upak.Current.EldestSeqno), nil
}

func reqFromRole(uv keybase1.UserVersion, role keybase1.TeamRole) (keybase1.TeamChangeReq, error) {

	var req keybase1.TeamChangeReq
	list := []keybase1.UserVersion{uv}
	switch role {
	case keybase1.TeamRole_OWNER:
		req.Owners = list
	case keybase1.TeamRole_ADMIN:
		req.Admins = list
	case keybase1.TeamRole_WRITER:
		req.Writers = list
	case keybase1.TeamRole_READER:
		req.Readers = list
	default:
		return keybase1.TeamChangeReq{}, errors.New("invalid team role")
	}

	return req, nil
}

func makeIdentifyLiteRes(id keybase1.TeamID, name keybase1.TeamName) keybase1.IdentifyLiteRes {
	return keybase1.IdentifyLiteRes{
		Ul: keybase1.UserOrTeamLite{
			Id:   id.AsUserOrTeam(),
			Name: name.String(),
		},
	}
}

func identifyLiteByID(ctx context.Context, g *libkb.GlobalContext, utid keybase1.UserOrTeamID, id2 keybase1.TeamID) (res keybase1.IdentifyLiteRes, err error) {

	var id1 keybase1.TeamID
	id1, err = utid.AsTeam()
	if err != nil {
		return res, err
	}

	if id1.Exists() && id2.Exists() && !id1.Eq(id2) {
		return res, errors.New("two team IDs given that don't match")
	}
	if !id1.Exists() {
		id1 = id2
	}
	if !id1.Exists() {
		return res, errors.New("empty IDs given")
	}
	var name keybase1.TeamName
	name, err = ResolveIDToName(ctx, g, id1)
	if err != nil {
		return res, err
	}

	return makeIdentifyLiteRes(id1, name), nil
}

func identifyLiteByName(ctx context.Context, g *libkb.GlobalContext, name keybase1.TeamName) (res keybase1.IdentifyLiteRes, err error) {
	var id keybase1.TeamID
	id, err = ResolveNameToID(ctx, g, name)
	if err != nil {
		return res, err
	}
	return makeIdentifyLiteRes(id, name), nil
}

func IdentifyLite(ctx context.Context, g *libkb.GlobalContext, arg keybase1.IdentifyLiteArg, au libkb.AssertionURL) (res keybase1.IdentifyLiteRes, err error) {

	if arg.Id.Exists() || au.IsTeamID() {
		return identifyLiteByID(ctx, g, arg.Id, au.ToTeamID())
	}
	if au.IsTeamName() {
		return identifyLiteByName(ctx, g, au.ToTeamName())
	}
	err = errors.New("could not identify team by ID or name")
	return res, err
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"database/sql"
	"encoding/gob"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"os"

	"github.com/boltdb/bolt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/karlbunch/tablewriter"
	"github.com/mgutz/ansi"
)

var (
	count  = 0
	dbname = []byte("dnas")
	layout = "01/02/06 03:04pm (MST)"
	lime   = ansi.ColorCode("green+h:black")
	red    = ansi.ColorCode("red")
	green  = ansi.ColorCode("green")
	reset  = ansi.ColorCode("reset")
)

// Value holds the dns answers and bloom filter fior database storage
type Value struct {
	Answers []Answer
	Bloom   []byte
}

func MysqlConnect(options *Options) (db *sql.DB, err error) {

	var connect string

	connect = options.MysqlUser + ":" +
		options.MysqlPassword + "@" + options.MysqlHost + "/" +
		options.MysqlDatabase

	db, err = sql.Open("mysql", connect)

	if err != nil {
		return db, err
	}

	defer db.Close()

	err = db.Ping()

	if err != nil {
		return db, err
	}

	checkQuestionTable := `SELECT COUNT(*)
	FROM information_schema.tables
	WHERE table_schema = '` + options.MysqlDatabase + `'
	AND table_name = 'questions';`

	checkAnswerTable := `SELECT COUNT(*)
	FROM information_schema.tables
	WHERE table_schema = '` + options.MysqlDatabase + `'
	AND table_name = 'answers';`

	var countQ int
	var countA int

	err = db.QueryRow(checkQuestionTable).Scan(&countQ)

	if err != nil {
		panic(err)
	}

	err = db.QueryRow(checkAnswerTable).Scan(&countA)

	if err != nil {
		panic(err)
	}

	fmt.Println(countQ, countA)

	// var questionTable string
	// var answerTable string
	// var r *sql.Result

	if countQ == 0 {
		questionTable := `
		CREATE TABLE ` + "`" + `questions` + "`" + ` (
			` + "`" + `id` + "`" + ` int(11) unsigned NOT NULL AUTO_INCREMENT,
			` + "`" + `packet` + "`" + ` text,
			` + "`" + `dst_ip` + "`" + ` text,
			` + "`" + `src_ip` + "`" + ` text,
			` + "`" + `timestamp` + "`" + ` datetime DEFAULT NULL,
			` + "`" + `protocol` + "`" + ` text,
			PRIMARY KEY (` + "`" + `id` + "`" + `)
		) ENGINE=InnoDB DEFAULT CHARSET=latin1;
		`

		_, err := db.Exec(questionTable)

		if err != nil {
			panic(err)
		}
	}

	if countA == 0 {
		answerTable := `
		CREATE TABLE ` + "`" + `answers` + "`" + ` (
			` + "`" + `id` + "`" + ` int(11) unsigned NOT NULL AUTO_INCREMENT,
			` + "`" + `question_id` + "`" + ` int(11) NOT NULL,
			` + "`" + `name` + "`" + ` tinytext,
			` + "`" + `record` + "`" + ` tinytext,
			` + "`" + `data` + "`" + ` text,
			` + "`" + `created_at` + "`" + ` datetime DEFAULT NULL,
			` + "`" + `updated_at` + "`" + ` datetime DEFAULT NULL,
			` + "`" + `active` + "`" + ` tinyint(1) NOT NULL,
			PRIMARY KEY (` + "`" + `id` + "`" + `)
		) ENGINE=InnoDB DEFAULT CHARSET=latin1;
		`

		_, err := db.Exec(answerTable)
		if err != nil {
			panic(err)
		}
	}

	// check for tables or create
	// CREATE TABLE questions (id INT);

	return db, nil
}

// EncodeDNS encode the Value struct for storage in the database.
func EncodeDNS(data Value) (buff bytes.Buffer, err error) {
	var buf bytes.Buffer

	enc := gob.NewEncoder(&buf)
	err = enc.Encode(data)

	if err != nil {
		return buf, err
	}

	return buf, nil
}

// DecodeDNS decode the Value struct from the database
func DecodeDNS(data []byte) (buff Value, err error) {
	var a Value
	enc := gob.NewDecoder(bytes.NewReader(data))
	eerr := enc.Decode(&a)

	if eerr != nil {
		return a, eerr
	}

	return a, nil
}

// MakeDB create or open an existing database file
func MakeDB(path string) (db *bolt.DB, err error) {
	db, err = bolt.Open(path, 0644, nil)

	return db, err
}

func contains(m Answer, list []Answer) int {
	for i, b := range list {
		if b.Record == m.Record && b.Name == m.Name && b.Data == m.Data {
			return i
		}
	}

	return -1
}

func prettyPrint(question string, a Value, count int) {

	fmt.Printf("---   %d   ---\n", count)

	fmt.Printf("\nQuestion: \033[0;31;49m%s\033[0m\n\n", question)

	fmt.Printf("\033[0;32;49mAnswers (%d):\033[0m\n\n", len(a.Answers))

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"RR", "Name", "Data", "Last Seen", "Active"})
	// table.SetBorder(false)

	for i, aa := range a.Answers {

		active := green + "Yes" + reset

		if !a.Answers[i].Active {
			active = red + "No" + reset
		}

		table.Append([]string{
			aa.Record,
			aa.Name,
			lime + aa.Data + reset,
			aa.UpdatedAt.Format(layout),
			active,
		})
	}

	table.Render()
	fmt.Printf("\n")
}

func (message *Message) ToMysql(db *sql.DB, options *Options) (err error) {
	fmt.Println(db)
	return nil
}

// ToStdout write data to standard out
func (message *Message) ToStdout(options *Options) {

	count++

	val := Value{Answers: message.DNS.Answers, Bloom: message.Bloom}

	prettyPrint(message.DNS.Question, val, count)

	if options.Hexdump {
		fmt.Printf("\033[0;32;49mHexdump:\033[0m\n\n%s\n", hex.Dump(message.Packet))
	}

}

// ToJSON convert Message struct to json
func (message *Message) ToJSON() ([]byte, error) {
	return json.Marshal(message)
}
<commit_msg>more clean<commit_after>package main

import (
	"database/sql"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"os"

	_ "github.com/go-sql-driver/mysql"
	"github.com/karlbunch/tablewriter"
	"github.com/mgutz/ansi"
)

var (
	count  = 0
	dbname = []byte("dnas")
	layout = "01/02/06 03:04pm (MST)"
	lime   = ansi.ColorCode("green+h:black")
	red    = ansi.ColorCode("red")
	green  = ansi.ColorCode("green")
	reset  = ansi.ColorCode("reset")
)

// Value holds the dns answers and bloom filter fior database storage
type Value struct {
	Answers []Answer
	Bloom   []byte
}

func MysqlConnect(options *Options) (db *sql.DB, err error) {

	var connect string

	connect = options.MysqlUser + ":" +
		options.MysqlPassword + "@" + options.MysqlHost + "/" +
		options.MysqlDatabase

	db, err = sql.Open("mysql", connect)

	if err != nil {
		return db, err
	}

	defer db.Close()

	err = db.Ping()

	if err != nil {
		return db, err
	}

	checkQuestionTable := `SELECT COUNT(*)
	FROM information_schema.tables
	WHERE table_schema = '` + options.MysqlDatabase + `'
	AND table_name = 'questions';`

	checkAnswerTable := `SELECT COUNT(*)
	FROM information_schema.tables
	WHERE table_schema = '` + options.MysqlDatabase + `'
	AND table_name = 'answers';`

	var countQ int
	var countA int

	err = db.QueryRow(checkQuestionTable).Scan(&countQ)

	if err != nil {
		panic(err)
	}

	err = db.QueryRow(checkAnswerTable).Scan(&countA)

	if err != nil {
		panic(err)
	}

	fmt.Println(countQ, countA)

	// var questionTable string
	// var answerTable string
	// var r *sql.Result

	if countQ == 0 {
		questionTable := `
		CREATE TABLE ` + "`" + `questions` + "`" + ` (
			` + "`" + `id` + "`" + ` int(11) unsigned NOT NULL AUTO_INCREMENT,
			` + "`" + `packet` + "`" + ` text,
			` + "`" + `dst_ip` + "`" + ` text,
			` + "`" + `src_ip` + "`" + ` text,
			` + "`" + `timestamp` + "`" + ` datetime DEFAULT NULL,
			` + "`" + `protocol` + "`" + ` text,
			PRIMARY KEY (` + "`" + `id` + "`" + `)
		) ENGINE=InnoDB DEFAULT CHARSET=latin1;
		`

		_, err := db.Exec(questionTable)

		if err != nil {
			panic(err)
		}
	}

	if countA == 0 {
		answerTable := `
		CREATE TABLE ` + "`" + `answers` + "`" + ` (
			` + "`" + `id` + "`" + ` int(11) unsigned NOT NULL AUTO_INCREMENT,
			` + "`" + `question_id` + "`" + ` int(11) NOT NULL,
			` + "`" + `name` + "`" + ` tinytext,
			` + "`" + `record` + "`" + ` tinytext,
			` + "`" + `data` + "`" + ` text,
			` + "`" + `created_at` + "`" + ` datetime DEFAULT NULL,
			` + "`" + `updated_at` + "`" + ` datetime DEFAULT NULL,
			` + "`" + `active` + "`" + ` tinyint(1) NOT NULL,
			PRIMARY KEY (` + "`" + `id` + "`" + `)
		) ENGINE=InnoDB DEFAULT CHARSET=latin1;
		`

		_, err := db.Exec(answerTable)
		if err != nil {
			panic(err)
		}
	}

	// check for tables or create
	// CREATE TABLE questions (id INT);

	return db, nil
}

func prettyPrint(question string, a Value, count int) {

	fmt.Printf("---   %d   ---\n", count)

	fmt.Printf("\nQuestion: \033[0;31;49m%s\033[0m\n\n", question)

	fmt.Printf("\033[0;32;49mAnswers (%d):\033[0m\n\n", len(a.Answers))

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"RR", "Name", "Data", "Last Seen", "Active"})
	// table.SetBorder(false)

	for i, aa := range a.Answers {

		active := green + "Yes" + reset

		if !a.Answers[i].Active {
			active = red + "No" + reset
		}

		table.Append([]string{
			aa.Record,
			aa.Name,
			lime + aa.Data + reset,
			aa.UpdatedAt.Format(layout),
			active,
		})
	}

	table.Render()
	fmt.Printf("\n")
}

func (message *Message) ToMysql(db *sql.DB, options *Options) (err error) {
	fmt.Println(db)
	return nil
}

// ToStdout write data to standard out
func (message *Message) ToStdout(options *Options) {

	count++

	val := Value{Answers: message.DNS.Answers, Bloom: message.Bloom}

	prettyPrint(message.DNS.Question, val, count)

	if options.Hexdump {
		fmt.Printf("\033[0;32;49mHexdump:\033[0m\n\n%s\n", hex.Dump(message.Packet))
	}

}

// ToJSON convert Message struct to json
func (message *Message) ToJSON() ([]byte, error) {
	return json.Marshal(message)
}
<|endoftext|>


<commit_before>// 事前に以下の事をしておくこと。
// go getコマンドでパッケージを取得しておくこと。
// go get code.google.com/p/goauth2/oauth
// go get code.google.com/p/google-api-go-client/calendar/v3
//
// Cloud ConsoleのCredentialsでClient IDを作成しておく。
// Cloud ConsoleのAPIsでAPIをONにしておくこと。
package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"

	"code.google.com/p/goauth2/oauth"
	"code.google.com/p/google-api-go-client/calendar/v3"
)

var (
	cachefile = "cache.json"

	scope = "https://www.googleapis.com/auth/calendar"
	// request_urlは使用するAPIのURLを指定して下さい。（この例ではCalendarList）
	request_url       = "https://www.googleapis.com/calendar/v3/users/me/calendarList"
	request_token_url = "https://accounts.google.com/o/oauth2/auth"
	auth_token_url    = "https://accounts.google.com/o/oauth2/token"

	// clientID、secret、はDevelopers ConsoleのCredentialsからコピー＆ペーストして下さい。
	clientId     = ""
	clientSecret = ""
	//
	redirectURL = "http://localhost"
	//
)

func main() {
	runtime.GOMAXPROCS(2)

	var err error
	var lsConfig LocalServerConfig

	flag.IntVar(&lsConfig.Port, "p", 16061, "local port: 1024 < ")
	flag.IntVar(&lsConfig.Timeout, "t", 30, "redirect timeout: 0 - 90")

	flag.Parse()

	if lsConfig.Port <= 1024 ||
		lsConfig.Timeout < 0 || lsConfig.Timeout > 90 {
		fmt.Fprintf(os.Stderr, "Usage: \n")
		flag.PrintDefaults()
		os.Exit(2)
	}

	fmt.Println("Start Execute API")

	// 認証コードを引数で受け取る。
	code := flag.Arg(0)

	//
	checkClientIDandSecret()

	config := &oauth.Config{
		ClientId:     clientId,
		ClientSecret: clientSecret,
		RedirectURL:  fmt.Sprintf("%s:%d", redirectURL, lsConfig.Port),
		Scope:        scope,
		AuthURL:      request_token_url,
		TokenURL:     auth_token_url,
		TokenCache:   oauth.CacheFile(cachefile),
	}

	transport := &oauth.Transport{Config: config}

	// キャッシュからトークンファイルを取得
	_, err = config.TokenCache.Token()
	if err != nil {
		// キャッシュなし
		if code == "" {
			code, err = getAuthCode(config, lsConfig)

			if err != nil {
				fmt.Fprintf(os.Stderr, "%v\n", err)
				os.Exit(1)
			}
		}

		// 認証トークンを取得する。（取得後、キャッシュへ）
		_, err = transport.Exchange(code)
		if err != nil {
			fmt.Printf("Exchange Error: %v\n", err)
			os.Exit(1)
		}
	}

	//
	var svc *calendar.Service
	var cl *calendar.CalendarList

	svc, err = calendar.New(transport.Client())

	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}

	cl, err = svc.CalendarList.List().Do()

	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}

	fmt.Printf("--- Your calendars ---\n")

	for _, item := range cl.Items {
		fmt.Printf("%v, %v\n", item.Summary, item.Description)
	}
}

func getAuthCode(config *oauth.Config, lsConfig LocalServerConfig) (string, error) {
	url := config.AuthCodeURL("")

	var cmd *exec.Cmd

	switch runtime.GOOS {
	case "windows":
		url = strings.Replace(url, "&", `^&`, -1)
		cmd = exec.Command("cmd", "/c", "start", url)

	case "darwin":
		url = strings.Replace(url, "&", `\&`, -1)
		cmd = exec.Command("open", url)

	default:
		return "", fmt.Errorf("ブラウザで以下のURLにアクセスし、認証して下さい。\n%s\n", url)
	}

	redirectResult := make(chan RedirectResult, 1)
	serverStarted := make(chan bool, 1)
	//
	go func(rr chan<- RedirectResult, ss chan<- bool, p int) {
		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			code := r.URL.Query().Get("code")

			if code == "" {
				rr <- RedirectResult{Err: fmt.Errorf("codeを取得できませんでした。")}
			}

			fmt.Fprintf(w, `<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
</head>
<body onload="window.open('about:blank','_self').close();">
ブラウザが自動で閉じない場合は手動で閉じてください。
</body>
</html>
`)
			rr <- RedirectResult{Code: code}
		})

		host := fmt.Sprintf("localhost:%d", p)

		fmt.Printf("Start Listen: %s\n", host)
		ss <- true

		err := http.ListenAndServe(host, nil)

		if err != nil {
			rr <- RedirectResult{Err: err}
		}
	}(redirectResult, serverStarted, lsConfig.Port)

	<-serverStarted

	// set redirect timeout
	tch := time.After(time.Duration(lsConfig.Timeout) * time.Second)

	fmt.Println("Start your browser after 2sec.")

	time.Sleep(2 * time.Second)

	if err := cmd.Start(); err != nil {
		return "", fmt.Errorf("Browser Start Error: %v\n", err)
	}

	var rr RedirectResult

	select {
	case rr = <-redirectResult:
	case <-tch:
		return "", fmt.Errorf("Timeout: waiting redirect.")
	}

	if rr.Err != nil {
		return "", fmt.Errorf("Redirect Error: %v\n", rr.Err)
	}

	fmt.Printf("Got code.\n")

	return rr.Code, nil
}

type RedirectResult struct {
	Code string
	Err  error
}

type LocalServerConfig struct {
	Port    int
	Timeout int
}

func checkClientIDandSecret() {
	if _, err := os.Stat(cachefile); err == nil {
		return
	}

	if clientId == "" {
		fmt.Println("Input ClientID")
		fmt.Scanf("%s\n", &clientId)
	}

	if clientSecret == "" {
		fmt.Println("Input ClientSecret")
		fmt.Scanf("%s\n", &clientSecret)
	}
}
<commit_msg>fix bug. url replace for mac.<commit_after>// 事前に以下の事をしておくこと。
// go getコマンドでパッケージを取得しておくこと。
// go get code.google.com/p/goauth2/oauth
// go get code.google.com/p/google-api-go-client/calendar/v3
//
// Cloud ConsoleのCredentialsでClient IDを作成しておく。
// Cloud ConsoleのAPIsでAPIをONにしておくこと。
package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"

	"code.google.com/p/goauth2/oauth"
	"code.google.com/p/google-api-go-client/calendar/v3"
)

var (
	cachefile = "cache.json"

	scope = "https://www.googleapis.com/auth/calendar"
	// request_urlは使用するAPIのURLを指定して下さい。（この例ではCalendarList）
	request_url       = "https://www.googleapis.com/calendar/v3/users/me/calendarList"
	request_token_url = "https://accounts.google.com/o/oauth2/auth"
	auth_token_url    = "https://accounts.google.com/o/oauth2/token"

	// clientID、secret、はDevelopers ConsoleのCredentialsからコピー＆ペーストして下さい。
	clientId     = ""
	clientSecret = ""
	//
	redirectURL = "http://localhost"
	//
)

func main() {
	runtime.GOMAXPROCS(2)

	var err error
	var lsConfig LocalServerConfig

	flag.IntVar(&lsConfig.Port, "p", 16061, "local port: 1024 < ")
	flag.IntVar(&lsConfig.Timeout, "t", 30, "redirect timeout: 0 - 90")

	flag.Parse()

	if lsConfig.Port <= 1024 ||
		lsConfig.Timeout < 0 || lsConfig.Timeout > 90 {
		fmt.Fprintf(os.Stderr, "Usage: \n")
		flag.PrintDefaults()
		os.Exit(2)
	}

	fmt.Println("Start Execute API")

	// 認証コードを引数で受け取る。
	code := flag.Arg(0)

	//
	checkClientIDandSecret()

	config := &oauth.Config{
		ClientId:     clientId,
		ClientSecret: clientSecret,
		RedirectURL:  fmt.Sprintf("%s:%d", redirectURL, lsConfig.Port),
		Scope:        scope,
		AuthURL:      request_token_url,
		TokenURL:     auth_token_url,
		TokenCache:   oauth.CacheFile(cachefile),
	}

	transport := &oauth.Transport{Config: config}

	// キャッシュからトークンファイルを取得
	_, err = config.TokenCache.Token()
	if err != nil {
		// キャッシュなし
		if code == "" {
			code, err = getAuthCode(config, lsConfig)

			if err != nil {
				fmt.Fprintf(os.Stderr, "%v\n", err)
				os.Exit(1)
			}
		}

		// 認証トークンを取得する。（取得後、キャッシュへ）
		_, err = transport.Exchange(code)
		if err != nil {
			fmt.Printf("Exchange Error: %v\n", err)
			os.Exit(1)
		}
	}

	//
	var svc *calendar.Service
	var cl *calendar.CalendarList

	svc, err = calendar.New(transport.Client())

	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}

	cl, err = svc.CalendarList.List().Do()

	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}

	fmt.Printf("--- Your calendars ---\n")

	for _, item := range cl.Items {
		fmt.Printf("%v, %v\n", item.Summary, item.Description)
	}
}

func getAuthCode(config *oauth.Config, lsConfig LocalServerConfig) (string, error) {
	url := config.AuthCodeURL("")

	var cmd *exec.Cmd

	switch runtime.GOOS {
	case "windows":
		url = strings.Replace(url, "&", `^&`, -1)
		cmd = exec.Command("cmd", "/c", "start", url)

	case "darwin":
		//url = strings.Replace(url, "&", `\&`, -1)
		cmd = exec.Command("open", url)

	default:
		return "", fmt.Errorf("ブラウザで以下のURLにアクセスし、認証して下さい。\n%s\n", url)
	}

	redirectResult := make(chan RedirectResult, 1)
	serverStarted := make(chan bool, 1)
	//
	go func(rr chan<- RedirectResult, ss chan<- bool, p int) {
		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			code := r.URL.Query().Get("code")

			if code == "" {
				rr <- RedirectResult{Err: fmt.Errorf("codeを取得できませんでした。")}
			}

			fmt.Fprintf(w, `<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
</head>
<body onload="window.open('about:blank','_self').close();">
ブラウザが自動で閉じない場合は手動で閉じてください。
</body>
</html>
`)
			rr <- RedirectResult{Code: code}
		})

		host := fmt.Sprintf("localhost:%d", p)

		fmt.Printf("Start Listen: %s\n", host)
		ss <- true

		err := http.ListenAndServe(host, nil)

		if err != nil {
			rr <- RedirectResult{Err: err}
		}
	}(redirectResult, serverStarted, lsConfig.Port)

	<-serverStarted

	// set redirect timeout
	tch := time.After(time.Duration(lsConfig.Timeout) * time.Second)

	fmt.Println("Start your browser after 2sec.")

	time.Sleep(2 * time.Second)

	if err := cmd.Start(); err != nil {
		return "", fmt.Errorf("Browser Start Error: %v\n", err)
	}

	var rr RedirectResult

	select {
	case rr = <-redirectResult:
	case <-tch:
		return "", fmt.Errorf("Timeout: waiting redirect.")
	}

	if rr.Err != nil {
		return "", fmt.Errorf("Redirect Error: %v\n", rr.Err)
	}

	fmt.Printf("Got code.\n")

	return rr.Code, nil
}

type RedirectResult struct {
	Code string
	Err  error
}

type LocalServerConfig struct {
	Port    int
	Timeout int
}

func checkClientIDandSecret() {
	if _, err := os.Stat(cachefile); err == nil {
		return
	}

	if clientId == "" {
		fmt.Println("Input ClientID")
		fmt.Scanf("%s\n", &clientId)
	}

	if clientSecret == "" {
		fmt.Println("Input ClientSecret")
		fmt.Scanf("%s\n", &clientSecret)
	}
}
<|endoftext|>


<commit_before>package libkbfs

import (
	"errors"
	"fmt"
	"runtime"
	"sync"
	"time"

	"github.com/keybase/client/go/auth"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol"
	"golang.org/x/net/context"
)

// AuthTokenMinRefreshSeconds is the minimum number of seconds between refreshes.
const AuthTokenMinRefreshSeconds = 60

// AuthToken encapsulates a timed authentication token.
type AuthToken struct {
	config         Config
	tokenType      string
	expireIn       int
	clientName     string
	clientVersion  string
	refreshHandler AuthTokenRefreshHandler
	tickerCancel   context.CancelFunc
	tickerMu       sync.Mutex // protects the ticker cancel function
}

// NewAuthToken creates a new authentication token.
func NewAuthToken(config Config, tokenType string, expireIn int,
	submoduleName string, rh AuthTokenRefreshHandler) *AuthToken {
	clientName := fmt.Sprintf("go %s %s %s", submoduleName, runtime.GOOS, runtime.GOARCH)
	authToken := &AuthToken{
		config:         config,
		tokenType:      tokenType,
		expireIn:       expireIn,
		clientName:     clientName,
		clientVersion:  VersionString(),
		refreshHandler: rh,
	}
	return authToken
}

// Sign is called to create a new signed authentication token.
func (a *AuthToken) signWithUserAndKeyInfo(ctx context.Context,
	challengeInfo keybase1.ChallengeInfo, uid keybase1.UID,
	username libkb.NormalizedUsername, key VerifyingKey) (string, error) {
	// create the token
	token := auth.NewToken(uid, username, key.kid, a.tokenType,
		challengeInfo.Challenge, challengeInfo.Now, a.expireIn,
		a.clientName, a.clientVersion)

	// sign the token
	signature, err := a.config.Crypto().SignToString(ctx, token.Bytes())
	if err != nil {
		return "", err
	}

	// reset the ticker
	refreshSeconds := a.expireIn / 2
	if refreshSeconds < AuthTokenMinRefreshSeconds {
		refreshSeconds = AuthTokenMinRefreshSeconds
	}
	a.startTicker(refreshSeconds)

	return signature, nil
}

// Sign is called to create a new signed authentication token,
// including a challenge and username/uid/kid identifiers.
func (a *AuthToken) Sign(ctx context.Context, challengeInfo keybase1.ChallengeInfo) (string, error) {
	// make sure we're being asked to sign a legit challenge
	if !auth.IsValidChallenge(challengeInfo.Challenge) {
		return "", errors.New("Invalid challenge")
	}

	// get UID, deviceKID and normalized username
	username, uid, err := a.config.KBPKI().GetCurrentUserInfo(ctx)
	if err != nil {
		return "", err
	}
	key, err := a.config.KBPKI().GetCurrentVerifyingKey(ctx)
	if err != nil {
		return "", err
	}

	return a.signWithUserAndKeyInfo(ctx, challengeInfo, uid, username, key)
}

// SignUserless signs the token without a username, UID, or challenge.
// This is useful for server-to-server communication where identity is
// established using only the KID.
func (a *AuthToken) SignUserless(ctx context.Context, key VerifyingKey) (
	string, error) {
	// Pass in a reserved, meaningless UID.
	return a.signWithUserAndKeyInfo(ctx, keybase1.ChallengeInfo{},
		keybase1.PublicUID, "", key)
}

// Shutdown is called to stop the refresh ticker.
func (a *AuthToken) Shutdown() {
	a.stopTicker()
}

// Helper to start the ticker (if not started.)
func (a *AuthToken) startTicker(intervalSeconds int) {
	a.tickerMu.Lock()
	defer a.tickerMu.Unlock()

	if a.tickerCancel != nil {
		return
	}

	var ctx context.Context
	ctx, a.tickerCancel = context.WithCancel(context.Background())
	go func() {
		ticker := time.NewTicker(time.Duration(intervalSeconds) * time.Second)
		for {
			select {
			case <-ticker.C:
				a.refreshHandler.RefreshAuthToken(ctx)
			case <-ctx.Done():
				ticker.Stop()
				return
			}
		}
	}()
}

// Helper to stop the refresh ticker.
func (a *AuthToken) stopTicker() {
	a.tickerMu.Lock()
	defer a.tickerMu.Unlock()

	if a.tickerCancel != nil {
		a.tickerCancel()
		a.tickerCancel = nil
	}
}
<commit_msg>auth_token: for SignUserless, use the local clock time<commit_after>package libkbfs

import (
	"errors"
	"fmt"
	"runtime"
	"sync"
	"time"

	"github.com/keybase/client/go/auth"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol"
	"golang.org/x/net/context"
)

// AuthTokenMinRefreshSeconds is the minimum number of seconds between refreshes.
const AuthTokenMinRefreshSeconds = 60

// AuthToken encapsulates a timed authentication token.
type AuthToken struct {
	config         Config
	tokenType      string
	expireIn       int
	clientName     string
	clientVersion  string
	refreshHandler AuthTokenRefreshHandler
	tickerCancel   context.CancelFunc
	tickerMu       sync.Mutex // protects the ticker cancel function
}

// NewAuthToken creates a new authentication token.
func NewAuthToken(config Config, tokenType string, expireIn int,
	submoduleName string, rh AuthTokenRefreshHandler) *AuthToken {
	clientName := fmt.Sprintf("go %s %s %s", submoduleName, runtime.GOOS, runtime.GOARCH)
	authToken := &AuthToken{
		config:         config,
		tokenType:      tokenType,
		expireIn:       expireIn,
		clientName:     clientName,
		clientVersion:  VersionString(),
		refreshHandler: rh,
	}
	return authToken
}

// Sign is called to create a new signed authentication token.
func (a *AuthToken) signWithUserAndKeyInfo(ctx context.Context,
	challengeInfo keybase1.ChallengeInfo, uid keybase1.UID,
	username libkb.NormalizedUsername, key VerifyingKey) (string, error) {
	// create the token
	token := auth.NewToken(uid, username, key.kid, a.tokenType,
		challengeInfo.Challenge, challengeInfo.Now, a.expireIn,
		a.clientName, a.clientVersion)

	// sign the token
	signature, err := a.config.Crypto().SignToString(ctx, token.Bytes())
	if err != nil {
		return "", err
	}

	// reset the ticker
	refreshSeconds := a.expireIn / 2
	if refreshSeconds < AuthTokenMinRefreshSeconds {
		refreshSeconds = AuthTokenMinRefreshSeconds
	}
	a.startTicker(refreshSeconds)

	return signature, nil
}

// Sign is called to create a new signed authentication token,
// including a challenge and username/uid/kid identifiers.
func (a *AuthToken) Sign(ctx context.Context, challengeInfo keybase1.ChallengeInfo) (string, error) {
	// make sure we're being asked to sign a legit challenge
	if !auth.IsValidChallenge(challengeInfo.Challenge) {
		return "", errors.New("Invalid challenge")
	}

	// get UID, deviceKID and normalized username
	username, uid, err := a.config.KBPKI().GetCurrentUserInfo(ctx)
	if err != nil {
		return "", err
	}
	key, err := a.config.KBPKI().GetCurrentVerifyingKey(ctx)
	if err != nil {
		return "", err
	}

	return a.signWithUserAndKeyInfo(ctx, challengeInfo, uid, username, key)
}

// SignUserless signs the token without a username, UID, or challenge.
// This is useful for server-to-server communication where identity is
// established using only the KID.  Assume the client and server
// clocks are roughly synchronized.
func (a *AuthToken) SignUserless(ctx context.Context, key VerifyingKey) (
	string, error) {
	// Pass in a reserved, meaningless UID.
	return a.signWithUserAndKeyInfo(ctx,
		keybase1.ChallengeInfo{Now: time.Now().Unix()},
		keybase1.PublicUID, "", key)
}

// Shutdown is called to stop the refresh ticker.
func (a *AuthToken) Shutdown() {
	a.stopTicker()
}

// Helper to start the ticker (if not started.)
func (a *AuthToken) startTicker(intervalSeconds int) {
	a.tickerMu.Lock()
	defer a.tickerMu.Unlock()

	if a.tickerCancel != nil {
		return
	}

	var ctx context.Context
	ctx, a.tickerCancel = context.WithCancel(context.Background())
	go func() {
		ticker := time.NewTicker(time.Duration(intervalSeconds) * time.Second)
		for {
			select {
			case <-ticker.C:
				a.refreshHandler.RefreshAuthToken(ctx)
			case <-ctx.Done():
				ticker.Stop()
				return
			}
		}
	}()
}

// Helper to stop the refresh ticker.
func (a *AuthToken) stopTicker() {
	a.tickerMu.Lock()
	defer a.tickerMu.Unlock()

	if a.tickerCancel != nil {
		a.tickerCancel()
		a.tickerCancel = nil
	}
}
<|endoftext|>


<commit_before>package log

import (
	"encoding/json"
	"fmt"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/fatih/camelcase"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/sirupsen/logrus"
)

const (
	bucketFactor = 2.0
	bucketCount  = 20 // Which makes the max bucket 2^20 seconds or ~12 days in size
)

// This needs to be a global var, not a field on the logger, because multiple servers
// create new loggers, and the prometheus registration uses a global namespace
var reportMetricGauge prometheus.Gauge
var reportMetricsOnce sync.Once

// Logger is a helper for emitting our grpc API logs
type Logger interface {
	Log(request interface{}, response interface{}, err error, duration time.Duration)
	LogAtLevelFromDepth(request interface{}, response interface{}, err error, duration time.Duration, level logrus.Level, depth int)
}

type logger struct {
	*logrus.Entry
	histogram map[string]*prometheus.HistogramVec
	counter   map[string]prometheus.Counter
	mutex     *sync.Mutex
}

// NewLogger creates a new logger
func NewLogger(service string) Logger {
	l := logrus.New()
	l.Formatter = new(prettyFormatter)
	newLogger := &logger{
		l.WithFields(logrus.Fields{"service": service}),
		make(map[string]*prometheus.HistogramVec),
		make(map[string]prometheus.Counter),
		&sync.Mutex{},
	}
	reportMetricsOnce.Do(func() {
		newReportMetricGauge := prometheus.NewGauge(
			prometheus.GaugeOpts{
				Namespace: "pachyderm",
				Subsystem: "pachd",
				Name:      "report_metric",
				Help:      "gauge of number of calls to ReportMetric()",
			},
		)
		if err := prometheus.Register(newReportMetricGauge); err != nil {
			entry := newLogger.WithFields(logrus.Fields{"method": "NewLogger"})
			newLogger.LogAtLevel(entry, logrus.WarnLevel, "error registering prometheus metric: %v", err)
		} else {
			reportMetricGauge = newReportMetricGauge
		}
	})
	return newLogger
}

// Helper function used to log requests and responses from our GRPC method
// implementations
func (l *logger) Log(request interface{}, response interface{}, err error, duration time.Duration) {
	if err != nil {
		l.LogAtLevelFromDepth(request, response, err, duration, logrus.ErrorLevel, 4)
	} else {
		l.LogAtLevelFromDepth(request, response, err, duration, logrus.InfoLevel, 4)
	}
	// We have to grab the method's name here before we
	// enter the goro's stack
	go l.ReportMetric(getMethodName(), duration, err)
}

func getMethodName() string {
	depth := 4
	pc := make([]uintptr, depth)
	runtime.Callers(depth, pc)
	split := strings.Split(runtime.FuncForPC(pc[0]).Name(), ".")
	return split[len(split)-1]
}

func (l *logger) ReportMetric(method string, duration time.Duration, err error) {
	// Count the number of ReportMetric() goros in case we start to leak them
	if reportMetricGauge != nil {
		reportMetricGauge.Inc()
	}
	defer func() {
		if reportMetricGauge != nil {
			reportMetricGauge.Dec()
		}
	}()
	l.mutex.Lock() // for conccurent map access (histogram,counter)
	defer l.mutex.Unlock()
	state := "started"
	if err != nil {
		state = "errored"
	} else {
		if duration.Seconds() > 0 {
			state = "finished"
		}
	}
	entry := l.WithFields(logrus.Fields{"method": method})

	var tokens []string
	for _, token := range camelcase.Split(method) {
		tokens = append(tokens, strings.ToLower(token))
	}
	rootStatName := strings.Join(tokens, "_")

	// Recording the distribution of started times is meaningless
	if state != "started" {
		runTimeName := fmt.Sprintf("%v_time", rootStatName)
		runTime, ok := l.histogram[runTimeName]
		if !ok {
			runTime = prometheus.NewHistogramVec(
				prometheus.HistogramOpts{
					Namespace: "pachyderm",
					Subsystem: "pachd",
					Name:      runTimeName,
					Help:      fmt.Sprintf("Run time of %v", method),
					Buckets:   prometheus.ExponentialBuckets(1.0, bucketFactor, bucketCount),
				},
				[]string{
					"state", // Since both finished and errored API calls can have run times
				},
			)
			if err := prometheus.Register(runTime); err != nil {
				l.LogAtLevel(entry, logrus.WarnLevel, "error registering prometheus metric: %v", err)
			} else {
				l.histogram[runTimeName] = runTime
			}
		}
		if hist, err := runTime.GetMetricWithLabelValues(state); err != nil {
			l.LogAtLevel(entry, logrus.WarnLevel, "failed to get histogram w labels: state (%v) with error %v", state, err)
		} else {
			hist.Observe(duration.Seconds())
		}
	}

	secondsCountName := fmt.Sprintf("%v_seconds_count", rootStatName)
	secondsCount, ok := l.counter[secondsCountName]
	if !ok {
		secondsCount = prometheus.NewCounter(
			prometheus.CounterOpts{
				Namespace: "pachyderm",
				Subsystem: "pachd",
				Name:      secondsCountName,
				Help:      fmt.Sprintf("cumulative number of seconds spent in %v", method),
			},
		)
		if err := prometheus.Register(secondsCount); err != nil {
			l.LogAtLevel(entry, logrus.WarnLevel, "error registering prometheus metric: %v", err)
		} else {
			l.counter[secondsCountName] = secondsCount
		}
	}
	secondsCount.Add(duration.Seconds())

}

func (l *logger) LogAtLevel(entry *logrus.Entry, level logrus.Level, args ...interface{}) {
	switch level {
	case logrus.PanicLevel:
		entry.Panic(args)
	case logrus.FatalLevel:
		entry.Fatal(args)
	case logrus.ErrorLevel:
		entry.Error(args)
	case logrus.WarnLevel:
		entry.Warn(args)
	case logrus.InfoLevel:
		entry.Info(args)
	case logrus.DebugLevel:
		entry.Debug(args)
	}
}

func (l *logger) LogAtLevelFromDepth(request interface{}, response interface{}, err error, duration time.Duration, level logrus.Level, depth int) {
	pc := make([]uintptr, depth)
	runtime.Callers(depth, pc)
	split := strings.Split(runtime.FuncForPC(pc[0]).Name(), ".")
	method := split[len(split)-1]

	fields := logrus.Fields{
		"method":  method,
		"request": request,
	}
	if response != nil {
		fields["response"] = response
	}
	if err != nil {
		// "err" itself might be a code or even an empty struct
		fields["error"] = err.Error()
	}
	if duration > 0 {
		fields["duration"] = duration
	}
	l.LogAtLevel(l.WithFields(fields), level)
}

type prettyFormatter struct{}

func (f *prettyFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	serialized := []byte(
		fmt.Sprintf(
			"%v %v ",
			entry.Time.Format(logrus.DefaultTimestampFormat),
			strings.ToUpper(entry.Level.String()),
		),
	)
	if entry.Data["service"] != nil {
		serialized = append(serialized, []byte(fmt.Sprintf("%v.%v ", entry.Data["service"], entry.Data["method"]))...)
	}
	if len(entry.Data) > 2 {
		delete(entry.Data, "service")
		delete(entry.Data, "method")
		if entry.Data["duration"] != nil {
			entry.Data["duration"] = entry.Data["duration"].(time.Duration).Seconds()
		}
		data, err := json.Marshal(entry.Data)
		if err != nil {
			return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
		}
		serialized = append(serialized, []byte(string(data))...)
		serialized = append(serialized, ' ')
	}

	serialized = append(serialized, []byte(entry.Message)...)
	serialized = append(serialized, '\n')
	return serialized, nil
}
<commit_msg>Disable prometheus metrics when running local PFS server<commit_after>package log

import (
	"encoding/json"
	"fmt"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/fatih/camelcase"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/sirupsen/logrus"
)

const (
	bucketFactor = 2.0
	bucketCount  = 20 // Which makes the max bucket 2^20 seconds or ~12 days in size
)

// This needs to be a global var, not a field on the logger, because multiple servers
// create new loggers, and the prometheus registration uses a global namespace
var reportMetricGauge prometheus.Gauge
var reportMetricsOnce sync.Once

// Logger is a helper for emitting our grpc API logs
type Logger interface {
	Log(request interface{}, response interface{}, err error, duration time.Duration)
	LogAtLevelFromDepth(request interface{}, response interface{}, err error, duration time.Duration, level logrus.Level, depth int)
}

type logger struct {
	*logrus.Entry
	histogram   map[string]*prometheus.HistogramVec
	counter     map[string]prometheus.Counter
	mutex       *sync.Mutex
	exportStats bool
}

// NewLogger creates a new logger
func NewLogger(service string) Logger {
	return newLogger(service, true)
}

// NewLogger creates a new logger for local testing (which does not report prometheus metrics)
func NewLocalLogger(service string) Logger {
	return newLogger(service, false)
}

func newLogger(service string, exportStats bool) Logger {
	l := logrus.New()
	l.Formatter = new(prettyFormatter)
	newLogger := &logger{
		l.WithFields(logrus.Fields{"service": service}),
		make(map[string]*prometheus.HistogramVec),
		make(map[string]prometheus.Counter),
		&sync.Mutex{},
		exportStats,
	}
	if exportStats {
		reportMetricsOnce.Do(func() {
			newReportMetricGauge := prometheus.NewGauge(
				prometheus.GaugeOpts{
					Namespace: "pachyderm",
					Subsystem: "pachd",
					Name:      "report_metric",
					Help:      "gauge of number of calls to ReportMetric()",
				},
			)
			if err := prometheus.Register(newReportMetricGauge); err != nil {
				entry := newLogger.WithFields(logrus.Fields{"method": "NewLogger"})
				newLogger.LogAtLevel(entry, logrus.WarnLevel, "error registering prometheus metric: %v", err)
			} else {
				reportMetricGauge = newReportMetricGauge
			}
		})
	}
	return newLogger
}

// Helper function used to log requests and responses from our GRPC method
// implementations
func (l *logger) Log(request interface{}, response interface{}, err error, duration time.Duration) {
	if err != nil {
		l.LogAtLevelFromDepth(request, response, err, duration, logrus.ErrorLevel, 4)
	} else {
		l.LogAtLevelFromDepth(request, response, err, duration, logrus.InfoLevel, 4)
	}
	// We have to grab the method's name here before we
	// enter the goro's stack
	go l.ReportMetric(getMethodName(), duration, err)
}

func getMethodName() string {
	depth := 4
	pc := make([]uintptr, depth)
	runtime.Callers(depth, pc)
	split := strings.Split(runtime.FuncForPC(pc[0]).Name(), ".")
	return split[len(split)-1]
}

func (l *logger) ReportMetric(method string, duration time.Duration, err error) {
	if !l.exportStats {
		return
	}
	// Count the number of ReportMetric() goros in case we start to leak them
	if reportMetricGauge != nil {
		reportMetricGauge.Inc()
	}
	defer func() {
		if reportMetricGauge != nil {
			reportMetricGauge.Dec()
		}
	}()
	l.mutex.Lock() // for conccurent map access (histogram,counter)
	defer l.mutex.Unlock()
	state := "started"
	if err != nil {
		state = "errored"
	} else {
		if duration.Seconds() > 0 {
			state = "finished"
		}
	}
	entry := l.WithFields(logrus.Fields{"method": method})

	var tokens []string
	for _, token := range camelcase.Split(method) {
		tokens = append(tokens, strings.ToLower(token))
	}
	rootStatName := strings.Join(tokens, "_")

	// Recording the distribution of started times is meaningless
	if state != "started" {
		runTimeName := fmt.Sprintf("%v_time", rootStatName)
		runTime, ok := l.histogram[runTimeName]
		if !ok {
			runTime = prometheus.NewHistogramVec(
				prometheus.HistogramOpts{
					Namespace: "pachyderm",
					Subsystem: "pachd",
					Name:      runTimeName,
					Help:      fmt.Sprintf("Run time of %v", method),
					Buckets:   prometheus.ExponentialBuckets(1.0, bucketFactor, bucketCount),
				},
				[]string{
					"state", // Since both finished and errored API calls can have run times
				},
			)
			if err := prometheus.Register(runTime); err != nil {
				l.LogAtLevel(entry, logrus.WarnLevel, "error registering prometheus metric: %v", err)
			} else {
				l.histogram[runTimeName] = runTime
			}
		}
		if hist, err := runTime.GetMetricWithLabelValues(state); err != nil {
			l.LogAtLevel(entry, logrus.WarnLevel, "failed to get histogram w labels: state (%v) with error %v", state, err)
		} else {
			hist.Observe(duration.Seconds())
		}
	}

	secondsCountName := fmt.Sprintf("%v_seconds_count", rootStatName)
	secondsCount, ok := l.counter[secondsCountName]
	if !ok {
		secondsCount = prometheus.NewCounter(
			prometheus.CounterOpts{
				Namespace: "pachyderm",
				Subsystem: "pachd",
				Name:      secondsCountName,
				Help:      fmt.Sprintf("cumulative number of seconds spent in %v", method),
			},
		)
		if err := prometheus.Register(secondsCount); err != nil {
			l.LogAtLevel(entry, logrus.WarnLevel, "error registering prometheus metric: %v", err)
		} else {
			l.counter[secondsCountName] = secondsCount
		}
	}
	secondsCount.Add(duration.Seconds())

}

func (l *logger) LogAtLevel(entry *logrus.Entry, level logrus.Level, args ...interface{}) {
	switch level {
	case logrus.PanicLevel:
		entry.Panic(args)
	case logrus.FatalLevel:
		entry.Fatal(args)
	case logrus.ErrorLevel:
		entry.Error(args)
	case logrus.WarnLevel:
		entry.Warn(args)
	case logrus.InfoLevel:
		entry.Info(args)
	case logrus.DebugLevel:
		entry.Debug(args)
	}
}

func (l *logger) LogAtLevelFromDepth(request interface{}, response interface{}, err error, duration time.Duration, level logrus.Level, depth int) {
	pc := make([]uintptr, depth)
	runtime.Callers(depth, pc)
	split := strings.Split(runtime.FuncForPC(pc[0]).Name(), ".")
	method := split[len(split)-1]

	fields := logrus.Fields{
		"method":  method,
		"request": request,
	}
	if response != nil {
		fields["response"] = response
	}
	if err != nil {
		// "err" itself might be a code or even an empty struct
		fields["error"] = err.Error()
	}
	if duration > 0 {
		fields["duration"] = duration
	}
	l.LogAtLevel(l.WithFields(fields), level)
}

type prettyFormatter struct{}

func (f *prettyFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	serialized := []byte(
		fmt.Sprintf(
			"%v %v ",
			entry.Time.Format(logrus.DefaultTimestampFormat),
			strings.ToUpper(entry.Level.String()),
		),
	)
	if entry.Data["service"] != nil {
		serialized = append(serialized, []byte(fmt.Sprintf("%v.%v ", entry.Data["service"], entry.Data["method"]))...)
	}
	if len(entry.Data) > 2 {
		delete(entry.Data, "service")
		delete(entry.Data, "method")
		if entry.Data["duration"] != nil {
			entry.Data["duration"] = entry.Data["duration"].(time.Duration).Seconds()
		}
		data, err := json.Marshal(entry.Data)
		if err != nil {
			return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
		}
		serialized = append(serialized, []byte(string(data))...)
		serialized = append(serialized, ' ')
	}

	serialized = append(serialized, []byte(entry.Message)...)
	serialized = append(serialized, '\n')
	return serialized, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"

	"github.com/gorilla/mux"

	"github.com/lxc/lxd/shared"
)

func containerLogsGet(d *Daemon, r *http.Request) Response {
	/* Let's explicitly *not* try to do a containerLoadByName here. In some
	 * cases (e.g. when container creation failed), the container won't
	 * exist in the DB but it does have some log files on disk.
	 *
	 * However, we should check this name and ensure it's a valid container
	 * name just so that people can't list arbitrary directories.
	 */
	name := mux.Vars(r)["name"]

	if err := containerValidName(name); err != nil {
		return BadRequest(err)
	}

	result := []map[string]interface{}{}

	dents, err := ioutil.ReadDir(shared.LogPath(name))
	if err != nil {
		return SmartError(err)
	}

	for _, f := range dents {
		result = append(result, map[string]interface{}{
			"name": f.Name(),
			"size": f.Size(),
		})
	}

	return SyncResponse(true, result)
}

var containerLogsCmd = Command{
	name: "containers/{name}/logs",
	get:  containerLogsGet,
}

func validLogFileName(fname string) bool {
	/* Let's just require that the paths be relative, so that we don't have
	 * to deal with any escaping or whatever.
	 */
	return fname == "lxc.log" ||
		fname == "lxc.conf" ||
		strings.HasPrefix(fname, "migration_") ||
		strings.HasPrefix(fname, "snapshot_")
}

func containerLogGet(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]
	file := mux.Vars(r)["file"]

	if err := containerValidName(name); err != nil {
		return BadRequest(err)
	}

	if !validLogFileName(file) {
		return BadRequest(fmt.Errorf("log file name %s not valid", file))
	}

	ent := fileResponseEntry{
		path:     shared.LogPath(name, file),
		filename: file,
	}

	return FileResponse(r, []fileResponseEntry{ent}, nil, false)
}

func containerLogDelete(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]
	file := mux.Vars(r)["file"]

	if err := containerValidName(name); err != nil {
		return BadRequest(err)
	}

	if !validLogFileName(file) {
		return BadRequest(fmt.Errorf("log file name %s not valid", file))
	}

	return SmartError(os.Remove(shared.LogPath(name, file)))
}

var containerLogCmd = Command{
	name:   "containers/{name}/logs/{file}",
	get:    containerLogGet,
	delete: containerLogDelete,
}
<commit_msg>Make the logs API respect the spec<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"

	"github.com/gorilla/mux"

	"github.com/lxc/lxd/shared"
)

func containerLogsGet(d *Daemon, r *http.Request) Response {
	/* Let's explicitly *not* try to do a containerLoadByName here. In some
	 * cases (e.g. when container creation failed), the container won't
	 * exist in the DB but it does have some log files on disk.
	 *
	 * However, we should check this name and ensure it's a valid container
	 * name just so that people can't list arbitrary directories.
	 */
	name := mux.Vars(r)["name"]

	if err := containerValidName(name); err != nil {
		return BadRequest(err)
	}

	result := []string{}

	dents, err := ioutil.ReadDir(shared.LogPath(name))
	if err != nil {
		return SmartError(err)
	}

	for _, f := range dents {
		result = append(result, fmt.Sprintf("/%s/containers/%s/logs/%s", shared.APIVersion, name, f.Name()))
	}

	return SyncResponse(true, result)
}

var containerLogsCmd = Command{
	name: "containers/{name}/logs",
	get:  containerLogsGet,
}

func validLogFileName(fname string) bool {
	/* Let's just require that the paths be relative, so that we don't have
	 * to deal with any escaping or whatever.
	 */
	return fname == "lxc.log" ||
		fname == "lxc.conf" ||
		strings.HasPrefix(fname, "migration_") ||
		strings.HasPrefix(fname, "snapshot_")
}

func containerLogGet(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]
	file := mux.Vars(r)["file"]

	if err := containerValidName(name); err != nil {
		return BadRequest(err)
	}

	if !validLogFileName(file) {
		return BadRequest(fmt.Errorf("log file name %s not valid", file))
	}

	ent := fileResponseEntry{
		path:     shared.LogPath(name, file),
		filename: file,
	}

	return FileResponse(r, []fileResponseEntry{ent}, nil, false)
}

func containerLogDelete(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]
	file := mux.Vars(r)["file"]

	if err := containerValidName(name); err != nil {
		return BadRequest(err)
	}

	if !validLogFileName(file) {
		return BadRequest(fmt.Errorf("log file name %s not valid", file))
	}

	return SmartError(os.Remove(shared.LogPath(name, file)))
}

var containerLogCmd = Command{
	name:   "containers/{name}/logs/{file}",
	get:    containerLogGet,
	delete: containerLogDelete,
}
<|endoftext|>


<commit_before>package network

import (
	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared/api"
)

// Network represents a LXD network.
type Network interface {
	// Load.
	init(state *state.State, id int64, name string, netType string, description string, config map[string]string)

	// Config.
	Validate(config map[string]string) error
	Name() string
	Type() string
	Config() map[string]string
	IsUsed() bool
	HasDHCPv4() bool
	HasDHCPv6() bool
	DHCPv4Ranges() []DHCPRange
	DHCPv6Ranges() []DHCPRange

	// Actions.
	Start() error
	Stop() error
	Rename(name string) error
	Update(newNetwork api.NetworkPut, clusterNotification bool) error
	HandleHeartbeat(heartbeatData *cluster.APIHeartbeat) error
	Delete(withDatabase bool) error
}
<commit_msg>lxd/network/network/interface: Renames Delete withDatabase arg to clusterNotification<commit_after>package network

import (
	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared/api"
)

// Network represents a LXD network.
type Network interface {
	// Load.
	init(state *state.State, id int64, name string, netType string, description string, config map[string]string)

	// Config.
	Validate(config map[string]string) error
	Name() string
	Type() string
	Config() map[string]string
	IsUsed() bool
	HasDHCPv4() bool
	HasDHCPv6() bool
	DHCPv4Ranges() []DHCPRange
	DHCPv6Ranges() []DHCPRange

	// Actions.
	Start() error
	Stop() error
	Rename(name string) error
	Update(newNetwork api.NetworkPut, clusterNotification bool) error
	HandleHeartbeat(heartbeatData *cluster.APIHeartbeat) error
	Delete(clusterNotification bool) error
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path"
	"syscall"
	"time"

	"github.com/kardianos/osext"
	lumberjack "github.com/natefinch/lumberjack"
	"github.com/samuel/go-zookeeper/zk"
	"github.com/zenazn/goji"

	"github.com/QubitProducts/bamboo/api"
	"github.com/QubitProducts/bamboo/configuration"
	"github.com/QubitProducts/bamboo/qzk"
	"github.com/QubitProducts/bamboo/services/event_bus"
)

/*
	Commandline arguments
*/
var configFilePath string
var logPath string

func init() {
	flag.StringVar(&configFilePath, "config", "config/development.json", "Full path of the configuration JSON file")
	flag.StringVar(&logPath, "log", "", "Log path to a file. Default logs to stdout")
}

func main() {
	flag.Parse()
	configureLog()

	// Load configuration
	conf, err := configuration.FromFile(configFilePath)
	if err != nil {
		log.Fatal(err)
	}

	eventBus := event_bus.New()

	// Wait for died children to avoid zombies
	signalChannel := make(chan os.Signal, 2)
	signal.Notify(signalChannel, os.Interrupt, syscall.SIGCHLD)
	go func() {
		for {
			sig := <-signalChannel
			if sig == syscall.SIGCHLD {
				r := syscall.Rusage{}
				syscall.Wait4(-1, nil, 0, &r)
			}
		}
	}()

	// Create StatsD client
	conf.StatsD.CreateClient()

	// Create Zookeeper connection
	zkConn := listenToZookeeper(conf, eventBus)

	// Register handlers
	handlers := event_bus.Handlers{Conf: &conf, Zookeeper: zkConn}
	eventBus.Register(handlers.MarathonEventHandler)
	eventBus.Register(handlers.ServiceEventHandler)

	// Start server
	initServer(&conf, zkConn, eventBus)
}

func initServer(conf *configuration.Configuration, conn *zk.Conn, eventBus *event_bus.EventBus) {
	stateAPI := api.StateAPI{Config: conf, Zookeeper: conn}
	serviceAPI := api.ServiceAPI{Config: conf, Zookeeper: conn}
	eventSubAPI := api.EventSubscriptionAPI{Conf: conf, EventBus: eventBus}

	conf.StatsD.Increment(1.0, "restart", 1)
	// Status live information
	goji.Get("/status", api.HandleStatus)

	// State API
	goji.Get("/api/state", stateAPI.Get)

	// Service API
	goji.Get("/api/services", serviceAPI.All)
	goji.Post("/api/services", serviceAPI.Create)
	goji.Put("/api/services/:id", serviceAPI.Put)
	goji.Delete("/api/services/:id", serviceAPI.Delete)
	goji.Post("/api/marathon/event_callback", eventSubAPI.Callback)

	// Static pages
	goji.Get("/*", http.FileServer(http.Dir(path.Join(executableFolder(), "webapp"))))

	registerMarathonEvent(conf)

	goji.Serve()
}

// Get current executable folder path
func executableFolder() string {
	folderPath, err := osext.ExecutableFolder()
	if err != nil {
		log.Fatal(err)
	}
	return folderPath
}

func registerMarathonEvent(conf *configuration.Configuration) {

	client := &http.Client{}
	// it's safe to register with multiple marathon nodes
	for _, marathon := range conf.Marathon.Endpoints() {
		url := marathon + "/v2/eventSubscriptions?callbackUrl=" + conf.Bamboo.Endpoint + "/api/marathon/event_callback"
		req, _ := http.NewRequest("POST", url, nil)
		req.Header.Add("Content-Type", "application/json")
		client.Do(req)
	}
}

func createAndListen(conf configuration.Zookeeper) (chan zk.Event, *zk.Conn) {
	conn, _, err := zk.Connect(conf.ConnectionString(), time.Second*10)

	if err != nil {
		log.Panic(err)
	}

	ch, _ := qzk.ListenToConn(conn, conf.Path, true, conf.Delay())
	return ch, conn
}

func listenToZookeeper(conf configuration.Configuration, eventBus *event_bus.EventBus) *zk.Conn {
	serviceCh, serviceConn := createAndListen(conf.Bamboo.Zookeeper)

	go func() {
		for {
			select {
			case _ = <-serviceCh:
				eventBus.Publish(event_bus.ServiceEvent{EventType: "change"})
			}
		}
	}()
	return serviceConn
}

func configureLog() {
	if len(logPath) > 0 {
		log.SetOutput(io.MultiWriter(&lumberjack.Logger{
			Filename: logPath,
			// megabytes
			MaxSize:    100,
			MaxBackups: 3,
			//days
			MaxAge: 28,
		}, os.Stdout))
	}
}
<commit_msg>Record the error of failure of Marathon callback system access.<commit_after>package main

import (
	"flag"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path"
	"strings"
	"syscall"
	"time"

	"github.com/kardianos/osext"
	lumberjack "github.com/natefinch/lumberjack"
	"github.com/samuel/go-zookeeper/zk"
	"github.com/zenazn/goji"

	"github.com/QubitProducts/bamboo/api"
	"github.com/QubitProducts/bamboo/configuration"
	"github.com/QubitProducts/bamboo/qzk"
	"github.com/QubitProducts/bamboo/services/event_bus"
)

/*
	Commandline arguments
*/
var configFilePath string
var logPath string

func init() {
	flag.StringVar(&configFilePath, "config", "config/development.json", "Full path of the configuration JSON file")
	flag.StringVar(&logPath, "log", "", "Log path to a file. Default logs to stdout")
}

func main() {
	flag.Parse()
	configureLog()

	// Load configuration
	conf, err := configuration.FromFile(configFilePath)
	if err != nil {
		log.Fatal(err)
	}

	eventBus := event_bus.New()

	// Wait for died children to avoid zombies
	signalChannel := make(chan os.Signal, 2)
	signal.Notify(signalChannel, os.Interrupt, syscall.SIGCHLD)
	go func() {
		for {
			sig := <-signalChannel
			if sig == syscall.SIGCHLD {
				r := syscall.Rusage{}
				syscall.Wait4(-1, nil, 0, &r)
			}
		}
	}()

	// Create StatsD client
	conf.StatsD.CreateClient()

	// Create Zookeeper connection
	zkConn := listenToZookeeper(conf, eventBus)

	// Register handlers
	handlers := event_bus.Handlers{Conf: &conf, Zookeeper: zkConn}
	eventBus.Register(handlers.MarathonEventHandler)
	eventBus.Register(handlers.ServiceEventHandler)

	// Start server
	initServer(&conf, zkConn, eventBus)
}

func initServer(conf *configuration.Configuration, conn *zk.Conn, eventBus *event_bus.EventBus) {
	stateAPI := api.StateAPI{Config: conf, Zookeeper: conn}
	serviceAPI := api.ServiceAPI{Config: conf, Zookeeper: conn}
	eventSubAPI := api.EventSubscriptionAPI{Conf: conf, EventBus: eventBus}

	conf.StatsD.Increment(1.0, "restart", 1)
	// Status live information
	goji.Get("/status", api.HandleStatus)

	// State API
	goji.Get("/api/state", stateAPI.Get)

	// Service API
	goji.Get("/api/services", serviceAPI.All)
	goji.Post("/api/services", serviceAPI.Create)
	goji.Put("/api/services/:id", serviceAPI.Put)
	goji.Delete("/api/services/:id", serviceAPI.Delete)
	goji.Post("/api/marathon/event_callback", eventSubAPI.Callback)

	// Static pages
	goji.Get("/*", http.FileServer(http.Dir(path.Join(executableFolder(), "webapp"))))

	registerMarathonEvent(conf)

	goji.Serve()
}

// Get current executable folder path
func executableFolder() string {
	folderPath, err := osext.ExecutableFolder()
	if err != nil {
		log.Fatal(err)
	}
	return folderPath
}

func registerMarathonEvent(conf *configuration.Configuration) {

	client := &http.Client{}
	// it's safe to register with multiple marathon nodes
	for _, marathon := range conf.Marathon.Endpoints() {
		url := marathon + "/v2/eventSubscriptions?callbackUrl=" + conf.Bamboo.Endpoint + "/api/marathon/event_callback"
		req, _ := http.NewRequest("POST", url, nil)
		req.Header.Add("Content-Type", "application/json")
		resp, err := client.Do(req)
		if err != nil {
			errorMsg := "An error occurred while accessing Marathon callback system: %s\n"
			log.Printf(errorMsg, err)
			return
		}
		bodyBytes, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			log.Fatal(err)
			return
		}
		body := string(bodyBytes)
		if strings.HasPrefix(body, "{\"message") {
			warningMsg := "Access to the callback system of Marathon seems to be failed, response: %s\n"
			log.Printf(warningMsg, body)
		}
	}
}

func createAndListen(conf configuration.Zookeeper) (chan zk.Event, *zk.Conn) {
	conn, _, err := zk.Connect(conf.ConnectionString(), time.Second*10)

	if err != nil {
		log.Panic(err)
	}

	ch, _ := qzk.ListenToConn(conn, conf.Path, true, conf.Delay())
	return ch, conn
}

func listenToZookeeper(conf configuration.Configuration, eventBus *event_bus.EventBus) *zk.Conn {
	serviceCh, serviceConn := createAndListen(conf.Bamboo.Zookeeper)

	go func() {
		for {
			select {
			case _ = <-serviceCh:
				eventBus.Publish(event_bus.ServiceEvent{EventType: "change"})
			}
		}
	}()
	return serviceConn
}

func configureLog() {
	if len(logPath) > 0 {
		log.SetOutput(io.MultiWriter(&lumberjack.Logger{
			Filename: logPath,
			// megabytes
			MaxSize:    100,
			MaxBackups: 3,
			//days
			MaxAge: 28,
		}, os.Stdout))
	}
}
<|endoftext|>