<commit_before>package service

import (
	"encoding/json"
	"fmt"
	"github.com/timeredbull/tsuru/api/app"
	"github.com/timeredbull/tsuru/api/auth"
	"github.com/timeredbull/tsuru/db"
	"github.com/timeredbull/tsuru/ec2"
	"github.com/timeredbull/tsuru/errors"
	"github.com/timeredbull/tsuru/log"
	"io/ioutil"
	"labix.org/v2/mgo/bson"
	"launchpad.net/goyaml"
	"net/http"
	"time"
)

type serviceYaml struct {
	Id        string
	Endpoint  map[string]string
	Bootstrap map[string]string
}

// a service with a pointer to it's type
type serviceT struct {
	Name string
}

func CreateHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	var sy serviceYaml
	err = goyaml.Unmarshal(body, &sy)
	if err != nil {
		return err
	}
	var teams []auth.Team
	db.Session.Teams().Find(bson.M{"users.email": u.Email}).All(&teams)
	if len(teams) == 0 {
		msg := "In order to create a service, you should be member of at least one team"
		return &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	n, err := db.Session.Services().Find(bson.M{"_id": sy.Id}).Count()
	if err != nil {
		return &errors.Http{Code: http.StatusInternalServerError, Message: err.Error()}
	}
	if n != 0 {
		msg := fmt.Sprintf("Service with name %s already exists.", sy.Id)
		return &errors.Http{Code: http.StatusInternalServerError, Message: msg}
	}
	s := Service{
		Name:      sy.Id,
		Endpoint:  sy.Endpoint,
		Bootstrap: sy.Bootstrap,
		Teams:     auth.GetTeamsNames(teams),
	}
	err = s.Create()
	if err != nil {
		return err
	}
	fmt.Fprint(w, "success")
	return nil
}

func CreateInstanceHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	log.Print("Receiving request to create a service instance")
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Print("Got error while reading request body:")
		log.Print(err.Error())
		return &errors.Http{Code: http.StatusInternalServerError, Message: err.Error()}
	}
	var sJson map[string]string
	err = json.Unmarshal(b, &sJson)
	if err != nil {
		log.Print("Got a problem while unmarshalling request's json:")
		log.Print(err.Error())
		return &errors.Http{Code: http.StatusInternalServerError, Message: err.Error()}
	}
	var s Service
	err = validateForInstanceCreation(&s, sJson, u)
	if err != nil {
		log.Print("Got error while validation:")
		log.Print(err.Error())
		return err
	}
	instance := &ec2.Instance{}
	if s.Bootstrap["when"] == OnNewInstance {
		instance, err = ec2.RunInstance(s.Bootstrap["ami"], "") //missing user data
		if err != nil {
			msg := fmt.Sprintf("Instance for service could not be created. \nError: %s", err.Error())
			return &errors.Http{Code: http.StatusInternalServerError, Message: msg}
		}
	}
	var teamNames []string
	teams, err := u.Teams()
	if err != nil {
		return err
	}
	for _, t := range teams {
		if s.hasTeam(&t) {
			teamNames = append(teamNames, t.Name)
		}
	}
	si := ServiceInstance{
		Name:        sJson["name"],
		ServiceName: sJson["service_name"],
		Instance:    instance.Id,
		Teams:       teamNames,
	}
	go callServiceApi(s, si)
	err = si.Create()
	if err != nil {
		return err
	}
	fmt.Fprint(w, "success")
	return nil
}

func callServiceApi(s Service, si ServiceInstance) {
	checkInstanceState := func() bool {
		db.Session.ServiceInstances().Find(bson.M{"_id": si.Name}).One(&si)
		return si.Host != "" && si.State == "running"
	}
	ch := time.Tick(1e9)
	for _ = range ch {
		if checkInstanceState() {
			if cli, err := s.GetClient("production"); err == nil {
				si.Env, err = cli.Create(&si)
				if err != nil {
					log.Print("Error while calling create action from service api.")
					log.Print(err.Error())
				}
				db.Session.ServiceInstances().Update(bson.M{"_id": si.Name}, si)
			}
			break
		}
	}
}

func validateForInstanceCreation(s *Service, sJson map[string]string, u *auth.User) error {
	err := db.Session.Services().Find(bson.M{"_id": sJson["service_name"]}).One(&s)
	if err != nil {
		msg := err.Error()
		if msg == "not found" {
			msg = fmt.Sprintf("Service %s does not exists.", sJson["service_name"])
		}
		return &errors.Http{Code: http.StatusNotFound, Message: msg}
	}
	var teams []auth.Team
	err = db.Session.Teams().Find(bson.M{"users.email": u.Email}).All(&teams)
	if err != nil {
		return &errors.Http{Code: http.StatusInternalServerError, Message: err.Error()}
	}
	q := bson.M{"_id": sJson["service_name"], "teams": bson.M{"$in": auth.GetTeamsNames(teams)}}
	n, err := db.Session.Services().Find(q).Count()
	if n == 0 {
		msg := fmt.Sprintf("You don't have access to service %s", sJson["service_name"])
		return &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	return nil
}

func DeleteHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	s := Service{Name: r.URL.Query().Get(":name")}
	err := s.Get()
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "Service not found"}
	}
	if !auth.CheckUserAccess(s.Teams, u) {
		msg := "This user does not have access to this service"
		return &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	s.Delete()
	fmt.Fprint(w, "success")
	return nil
}

func BindHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	instanceQuery := bson.M{"_id": r.URL.Query().Get(":instance")}
	var instance ServiceInstance
	err := db.Session.ServiceInstances().Find(instanceQuery).One(&instance)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "Instance not found"}
	}
	if !auth.CheckUserAccess(instance.Teams, u) {
		return &errors.Http{Code: http.StatusForbidden, Message: "This user does not have access to this instance"}
	}
	if instance.State != "CREATED" {
		return &errors.Http{Code: http.StatusPreconditionFailed, Message: "This service instance is not ready yet."}
	}
	appQuery := bson.M{"name": r.URL.Query().Get(":app")}
	var a app.App
	err = db.Session.Apps().Find(appQuery).One(&a)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "App not found"}
	}
	if !auth.CheckUserAccess(a.Teams, u) {
		return &errors.Http{Code: http.StatusForbidden, Message: "This user does not have access to this app"}
	}
	instance.Apps = append(instance.Apps, a.Name)
	err = db.Session.ServiceInstances().Update(instanceQuery, instance)
	if err != nil {
		return err
	}
	var envVars []app.EnvVar
	var setEnv = func(a app.App, env map[string]string) {
		for k, v := range env {
			envVars = append(envVars, app.EnvVar{
				Name:         k,
				Value:        v,
				Public:       false,
				InstanceName: instance.Name,
			})
		}
	}
	setEnv(a, instance.Env)
	err = db.Session.Apps().Update(appQuery, a)
	if err != nil {
		return err
	}
	var cli *Client
	if cli, err = instance.Service().GetClient("production"); err == nil {
		if len(a.Units) == 0 {
			return &errors.Http{Code: http.StatusPreconditionFailed, Message: "This app does not have an IP yet."}
		}
		env, err := cli.Bind(&instance, &a)
		if err != nil {
			return err
		}
		setEnv(a, env)
	}
	return app.SetEnvsToApp(&a, envVars, false)
}

// func UnbindHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
// 	var b bindJson
// 	defer r.Body.Close()
// 	body, err := ioutil.ReadAll(r.Body)
// 	if err != nil {
// 		return err
// 	}
// 	err = json.Unmarshal(body, &b)
// 	if err != nil {
// 		return err
// 	}
// 	s := Service{Name: b.Service}
// 	err = s.Get()
// 	if err != nil {
// 		return &errors.Http{Code: http.StatusNotFound, Message: "Service not found"}
// 	}
// 	if !s.CheckUserAccess(u) {
// 		return &errors.Http{Code: http.StatusForbidden, Message: "This user does not have access to this service"}
// 	}
// 	a := app.App{Name: b.App}
// 	err = a.Get()
// 	if err != nil {
// 		return &errors.Http{Code: http.StatusNotFound, Message: "App not found"}
// 	}
// 	if !a.CheckUserAccess(u) {
// 		return &errors.Http{Code: http.StatusForbidden, Message: "This user does not have access to this app"}
// 	}
// 	err = s.Unbind(&a)
// 	if err != nil {
// 		return err
// 	}
// 	fmt.Fprint(w, "success")
// 	return nil
// }

func getServiceAndTeamOrError(serviceName string, teamName string, u *auth.User) (*Service, *auth.Team, error) {
	service := &Service{Name: serviceName}
	err := service.Get()
	if err != nil {
		return nil, nil, &errors.Http{Code: http.StatusNotFound, Message: "Service not found"}
	}
	if !auth.CheckUserAccess(service.Teams, u) {
		msg := "This user does not have access to this service"
		return nil, nil, &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	t := new(auth.Team)
	err = db.Session.Teams().Find(bson.M{"name": teamName}).One(t)
	if err != nil {
		return nil, nil, &errors.Http{Code: http.StatusNotFound, Message: "Team not found"}
	}
	return service, t, nil
}

func GrantAccessToTeamHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	service, t, err := getServiceAndTeamOrError(r.URL.Query().Get(":service"), r.URL.Query().Get(":team"), u)
	if err != nil {
		return err
	}
	err = service.GrantAccess(t)
	if err != nil {
		return &errors.Http{Code: http.StatusConflict, Message: err.Error()}
	}
	return db.Session.Services().Update(bson.M{"_id": service.Name}, service)
}

func RevokeAccessFromTeamHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	service, t, err := getServiceAndTeamOrError(r.URL.Query().Get(":service"), r.URL.Query().Get(":team"), u)
	if err != nil {
		return err
	}
	if len(service.Teams) < 2 {
		msg := "You can not revoke the access from this team, because it is the unique team with access to this service, and a service can not be orphaned"
		return &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	err = service.RevokeAccess(t)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: err.Error()}
	}
	return db.Session.Services().Update(bson.M{"_id": service.Name}, service)
}

func ServicesHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	teams, err := u.Teams()
	if err != nil {
		return err
	}
	var teamNames []string
	for _, team := range teams {
		teamNames = append(teamNames, team.Name)
	}
	response := make(map[string][]string)
	var services []Service
	err = db.Session.Services().Find(bson.M{"teams": bson.M{"$in": teamNames}}).All(&services)
	if err != nil {
		return err
	}
	if len(services) == 0 {
		w.Write([]byte("null"))
		return nil
	}
	for _, service := range services {
		response[service.Name] = []string{}
	}
	iter := db.Session.ServiceInstances().Find(bson.M{"teams": bson.M{"$in": teamNames}}).Iter()
	var instance ServiceInstance
	for iter.Next(&instance) {
		service := response[instance.ServiceName]
		response[instance.ServiceName] = append(service, instance.Name)
	}
	err = iter.Err()
	if err != nil {
		return err
	}
	body, err := json.Marshal(response)
	if err != nil {
		return err
	}
	n, err := w.Write(body)
	if n != len(body) {
		return &errors.Http{Code: http.StatusInternalServerError, Message: "Failed to write the response body."}
	}
	return err
}
<commit_msg>api/service: if there is no instance to create, just return true<commit_after>package service

import (
	"encoding/json"
	"fmt"
	"github.com/timeredbull/tsuru/api/app"
	"github.com/timeredbull/tsuru/api/auth"
	"github.com/timeredbull/tsuru/db"
	"github.com/timeredbull/tsuru/ec2"
	"github.com/timeredbull/tsuru/errors"
	"github.com/timeredbull/tsuru/log"
	"io/ioutil"
	"labix.org/v2/mgo/bson"
	"launchpad.net/goyaml"
	"net/http"
	"time"
)

type serviceYaml struct {
	Id        string
	Endpoint  map[string]string
	Bootstrap map[string]string
}

// a service with a pointer to it's type
type serviceT struct {
	Name string
}

func CreateHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	var sy serviceYaml
	err = goyaml.Unmarshal(body, &sy)
	if err != nil {
		return err
	}
	var teams []auth.Team
	db.Session.Teams().Find(bson.M{"users.email": u.Email}).All(&teams)
	if len(teams) == 0 {
		msg := "In order to create a service, you should be member of at least one team"
		return &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	n, err := db.Session.Services().Find(bson.M{"_id": sy.Id}).Count()
	if err != nil {
		return &errors.Http{Code: http.StatusInternalServerError, Message: err.Error()}
	}
	if n != 0 {
		msg := fmt.Sprintf("Service with name %s already exists.", sy.Id)
		return &errors.Http{Code: http.StatusInternalServerError, Message: msg}
	}
	s := Service{
		Name:      sy.Id,
		Endpoint:  sy.Endpoint,
		Bootstrap: sy.Bootstrap,
		Teams:     auth.GetTeamsNames(teams),
	}
	err = s.Create()
	if err != nil {
		return err
	}
	fmt.Fprint(w, "success")
	return nil
}

func CreateInstanceHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	log.Print("Receiving request to create a service instance")
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Print("Got error while reading request body:")
		log.Print(err.Error())
		return &errors.Http{Code: http.StatusInternalServerError, Message: err.Error()}
	}
	var sJson map[string]string
	err = json.Unmarshal(b, &sJson)
	if err != nil {
		log.Print("Got a problem while unmarshalling request's json:")
		log.Print(err.Error())
		return &errors.Http{Code: http.StatusInternalServerError, Message: err.Error()}
	}
	var s Service
	err = validateForInstanceCreation(&s, sJson, u)
	if err != nil {
		log.Print("Got error while validation:")
		log.Print(err.Error())
		return err
	}
	instance := &ec2.Instance{}
	if s.Bootstrap["when"] == OnNewInstance {
		instance, err = ec2.RunInstance(s.Bootstrap["ami"], "") //missing user data
		if err != nil {
			msg := fmt.Sprintf("Instance for service could not be created. \nError: %s", err.Error())
			return &errors.Http{Code: http.StatusInternalServerError, Message: msg}
		}
	}
	var teamNames []string
	teams, err := u.Teams()
	if err != nil {
		return err
	}
	for _, t := range teams {
		if s.hasTeam(&t) {
			teamNames = append(teamNames, t.Name)
		}
	}
	si := ServiceInstance{
		Name:        sJson["name"],
		ServiceName: sJson["service_name"],
		Instance:    instance.Id,
		Teams:       teamNames,
	}
	go callServiceApi(s, si)
	err = si.Create()
	if err != nil {
		return err
	}
	fmt.Fprint(w, "success")
	return nil
}

func callServiceApi(s Service, si ServiceInstance) {
	checkInstanceState := func() bool {
		if when, ok := s.Bootstrap["when"]; !ok || when != OnNewInstance {
			return true
		}
		db.Session.ServiceInstances().Find(bson.M{"_id": si.Name}).One(&si)
		return si.Host != "" && si.State == "running"
	}
	ch := time.Tick(1e9)
	for _ = range ch {
		if checkInstanceState() {
			if cli, err := s.GetClient("production"); err == nil {
				si.Env, err = cli.Create(&si)
				if err != nil {
					log.Print("Error while calling create action from service api.")
					log.Print(err.Error())
				}
				si.State = "running"
				db.Session.ServiceInstances().Update(bson.M{"_id": si.Name}, si)
			}
			break
		}
	}
}

func validateForInstanceCreation(s *Service, sJson map[string]string, u *auth.User) error {
	err := db.Session.Services().Find(bson.M{"_id": sJson["service_name"]}).One(&s)
	if err != nil {
		msg := err.Error()
		if msg == "not found" {
			msg = fmt.Sprintf("Service %s does not exists.", sJson["service_name"])
		}
		return &errors.Http{Code: http.StatusNotFound, Message: msg}
	}
	var teams []auth.Team
	err = db.Session.Teams().Find(bson.M{"users.email": u.Email}).All(&teams)
	if err != nil {
		return &errors.Http{Code: http.StatusInternalServerError, Message: err.Error()}
	}
	q := bson.M{"_id": sJson["service_name"], "teams": bson.M{"$in": auth.GetTeamsNames(teams)}}
	n, err := db.Session.Services().Find(q).Count()
	if n == 0 {
		msg := fmt.Sprintf("You don't have access to service %s", sJson["service_name"])
		return &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	return nil
}

func DeleteHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	s := Service{Name: r.URL.Query().Get(":name")}
	err := s.Get()
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "Service not found"}
	}
	if !auth.CheckUserAccess(s.Teams, u) {
		msg := "This user does not have access to this service"
		return &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	s.Delete()
	fmt.Fprint(w, "success")
	return nil
}

func BindHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	instanceQuery := bson.M{"_id": r.URL.Query().Get(":instance")}
	var instance ServiceInstance
	err := db.Session.ServiceInstances().Find(instanceQuery).One(&instance)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "Instance not found"}
	}
	if !auth.CheckUserAccess(instance.Teams, u) {
		return &errors.Http{Code: http.StatusForbidden, Message: "This user does not have access to this instance"}
	}
	if instance.State != "CREATED" {
		return &errors.Http{Code: http.StatusPreconditionFailed, Message: "This service instance is not ready yet."}
	}
	appQuery := bson.M{"name": r.URL.Query().Get(":app")}
	var a app.App
	err = db.Session.Apps().Find(appQuery).One(&a)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "App not found"}
	}
	if !auth.CheckUserAccess(a.Teams, u) {
		return &errors.Http{Code: http.StatusForbidden, Message: "This user does not have access to this app"}
	}
	instance.Apps = append(instance.Apps, a.Name)
	err = db.Session.ServiceInstances().Update(instanceQuery, instance)
	if err != nil {
		return err
	}
	var envVars []app.EnvVar
	var setEnv = func(a app.App, env map[string]string) {
		for k, v := range env {
			envVars = append(envVars, app.EnvVar{
				Name:         k,
				Value:        v,
				Public:       false,
				InstanceName: instance.Name,
			})
		}
	}
	setEnv(a, instance.Env)
	err = db.Session.Apps().Update(appQuery, a)
	if err != nil {
		return err
	}
	var cli *Client
	if cli, err = instance.Service().GetClient("production"); err == nil {
		if len(a.Units) == 0 {
			return &errors.Http{Code: http.StatusPreconditionFailed, Message: "This app does not have an IP yet."}
		}
		env, err := cli.Bind(&instance, &a)
		if err != nil {
			return err
		}
		setEnv(a, env)
	}
	return app.SetEnvsToApp(&a, envVars, false)
}

// func UnbindHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
// 	var b bindJson
// 	defer r.Body.Close()
// 	body, err := ioutil.ReadAll(r.Body)
// 	if err != nil {
// 		return err
// 	}
// 	err = json.Unmarshal(body, &b)
// 	if err != nil {
// 		return err
// 	}
// 	s := Service{Name: b.Service}
// 	err = s.Get()
// 	if err != nil {
// 		return &errors.Http{Code: http.StatusNotFound, Message: "Service not found"}
// 	}
// 	if !s.CheckUserAccess(u) {
// 		return &errors.Http{Code: http.StatusForbidden, Message: "This user does not have access to this service"}
// 	}
// 	a := app.App{Name: b.App}
// 	err = a.Get()
// 	if err != nil {
// 		return &errors.Http{Code: http.StatusNotFound, Message: "App not found"}
// 	}
// 	if !a.CheckUserAccess(u) {
// 		return &errors.Http{Code: http.StatusForbidden, Message: "This user does not have access to this app"}
// 	}
// 	err = s.Unbind(&a)
// 	if err != nil {
// 		return err
// 	}
// 	fmt.Fprint(w, "success")
// 	return nil
// }

func getServiceAndTeamOrError(serviceName string, teamName string, u *auth.User) (*Service, *auth.Team, error) {
	service := &Service{Name: serviceName}
	err := service.Get()
	if err != nil {
		return nil, nil, &errors.Http{Code: http.StatusNotFound, Message: "Service not found"}
	}
	if !auth.CheckUserAccess(service.Teams, u) {
		msg := "This user does not have access to this service"
		return nil, nil, &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	t := new(auth.Team)
	err = db.Session.Teams().Find(bson.M{"name": teamName}).One(t)
	if err != nil {
		return nil, nil, &errors.Http{Code: http.StatusNotFound, Message: "Team not found"}
	}
	return service, t, nil
}

func GrantAccessToTeamHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	service, t, err := getServiceAndTeamOrError(r.URL.Query().Get(":service"), r.URL.Query().Get(":team"), u)
	if err != nil {
		return err
	}
	err = service.GrantAccess(t)
	if err != nil {
		return &errors.Http{Code: http.StatusConflict, Message: err.Error()}
	}
	return db.Session.Services().Update(bson.M{"_id": service.Name}, service)
}

func RevokeAccessFromTeamHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	service, t, err := getServiceAndTeamOrError(r.URL.Query().Get(":service"), r.URL.Query().Get(":team"), u)
	if err != nil {
		return err
	}
	if len(service.Teams) < 2 {
		msg := "You can not revoke the access from this team, because it is the unique team with access to this service, and a service can not be orphaned"
		return &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	err = service.RevokeAccess(t)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: err.Error()}
	}
	return db.Session.Services().Update(bson.M{"_id": service.Name}, service)
}

func ServicesHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	teams, err := u.Teams()
	if err != nil {
		return err
	}
	var teamNames []string
	for _, team := range teams {
		teamNames = append(teamNames, team.Name)
	}
	response := make(map[string][]string)
	var services []Service
	err = db.Session.Services().Find(bson.M{"teams": bson.M{"$in": teamNames}}).All(&services)
	if err != nil {
		return err
	}
	if len(services) == 0 {
		w.Write([]byte("null"))
		return nil
	}
	for _, service := range services {
		response[service.Name] = []string{}
	}
	iter := db.Session.ServiceInstances().Find(bson.M{"teams": bson.M{"$in": teamNames}}).Iter()
	var instance ServiceInstance
	for iter.Next(&instance) {
		service := response[instance.ServiceName]
		response[instance.ServiceName] = append(service, instance.Name)
	}
	err = iter.Err()
	if err != nil {
		return err
	}
	body, err := json.Marshal(response)
	if err != nil {
		return err
	}
	n, err := w.Write(body)
	if n != len(body) {
		return &errors.Http{Code: http.StatusInternalServerError, Message: "Failed to write the response body."}
	}
	return err
}
<|endoftext|>


<commit_before>package flags

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

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/cliconfig"
	"github.com/docker/docker/opts"
	"github.com/docker/go-connections/tlsconfig"
	"github.com/spf13/pflag"
)

const (
	// DefaultTrustKeyFile is the default filename for the trust key
	DefaultTrustKeyFile = "key.json"
	// DefaultCaFile is the default filename for the CA pem file
	DefaultCaFile = "ca.pem"
	// DefaultKeyFile is the default filename for the key pem file
	DefaultKeyFile = "key.pem"
	// DefaultCertFile is the default filename for the cert pem file
	DefaultCertFile = "cert.pem"
	// FlagTLSVerify is the flag name for the tls verification option
	FlagTLSVerify = "tlsverify"
)

var (
	dockerCertPath  = os.Getenv("DOCKER_CERT_PATH")
	dockerTLSVerify = os.Getenv("DOCKER_TLS_VERIFY") != ""
)

// CommonOptions are options common to both the client and the daemon.
type CommonOptions struct {
	Debug      bool
	Hosts      []string
	LogLevel   string
	TLS        bool
	TLSVerify  bool
	TLSOptions *tlsconfig.Options
	TrustKey   string
}

// NewCommonOptions returns a new CommonOptions
func NewCommonOptions() *CommonOptions {
	return &CommonOptions{}
}

// InstallFlags adds flags for the common options on the FlagSet
func (commonOpts *CommonOptions) InstallFlags(flags *pflag.FlagSet) {
	if dockerCertPath == "" {
		dockerCertPath = cliconfig.ConfigDir()
	}

	flags.BoolVarP(&commonOpts.Debug, "debug", "D", false, "Enable debug mode")
	flags.StringVarP(&commonOpts.LogLevel, "log-level", "l", "info", "Set the logging level (debug, info, warn, error, fatal)")
	flags.BoolVar(&commonOpts.TLS, "tls", false, "Use TLS; implied by --tlsverify")
	flags.BoolVar(&commonOpts.TLSVerify, FlagTLSVerify, dockerTLSVerify, "Use TLS and verify the remote")

	// TODO use flag flags.String("identity"}, "i", "", "Path to libtrust key file")

	commonOpts.TLSOptions = &tlsconfig.Options{}
	tlsOptions := commonOpts.TLSOptions
	flags.StringVar(&tlsOptions.CAFile, "tlscacert", filepath.Join(dockerCertPath, DefaultCaFile), "Trust certs signed only by this CA")
	flags.StringVar(&tlsOptions.CertFile, "tlscert", filepath.Join(dockerCertPath, DefaultCertFile), "Path to TLS certificate file")
	flags.StringVar(&tlsOptions.KeyFile, "tlskey", filepath.Join(dockerCertPath, DefaultKeyFile), "Path to TLS key file")

	hostOpt := opts.NewNamedListOptsRef("hosts", &commonOpts.Hosts, opts.ValidateHost)
	flags.VarP(hostOpt, "host", "H", "Daemon socket(s) to connect to")
}

// SetDefaultOptions sets default values for options after flag parsing is
// complete
func (commonOpts *CommonOptions) SetDefaultOptions(flags *pflag.FlagSet) {
	// Regardless of whether the user sets it to true or false, if they
	// specify --tlsverify at all then we need to turn on tls
	// TLSVerify can be true even if not set due to DOCKER_TLS_VERIFY env var, so we need
	// to check that here as well
	if flags.Changed(FlagTLSVerify) || commonOpts.TLSVerify {
		commonOpts.TLS = true
	}

	if !commonOpts.TLS {
		commonOpts.TLSOptions = nil
	} else {
		tlsOptions := commonOpts.TLSOptions
		tlsOptions.InsecureSkipVerify = !commonOpts.TLSVerify

		// Reset CertFile and KeyFile to empty string if the user did not specify
		// the respective flags and the respective default files were not found.
		if !flags.Changed("tlscert") {
			if _, err := os.Stat(tlsOptions.CertFile); os.IsNotExist(err) {
				tlsOptions.CertFile = ""
			}
		}
		if !flags.Changed("tlskey") {
			if _, err := os.Stat(tlsOptions.KeyFile); os.IsNotExist(err) {
				tlsOptions.KeyFile = ""
			}
		}
	}
}

// SetLogLevel sets the logrus logging level
func SetLogLevel(logLevel string) {
	if logLevel != "" {
		lvl, err := logrus.ParseLevel(logLevel)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Unable to parse logging level: %s\n", logLevel)
			os.Exit(1)
		}
		logrus.SetLevel(lvl)
	} else {
		logrus.SetLevel(logrus.InfoLevel)
	}
}
<commit_msg>Add for String<commit_after>package flags

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

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/cliconfig"
	"github.com/docker/docker/opts"
	"github.com/docker/go-connections/tlsconfig"
	"github.com/spf13/pflag"
)

const (
	// DefaultTrustKeyFile is the default filename for the trust key
	DefaultTrustKeyFile = "key.json"
	// DefaultCaFile is the default filename for the CA pem file
	DefaultCaFile = "ca.pem"
	// DefaultKeyFile is the default filename for the key pem file
	DefaultKeyFile = "key.pem"
	// DefaultCertFile is the default filename for the cert pem file
	DefaultCertFile = "cert.pem"
	// FlagTLSVerify is the flag name for the tls verification option
	FlagTLSVerify = "tlsverify"
)

var (
	dockerCertPath  = os.Getenv("DOCKER_CERT_PATH")
	dockerTLSVerify = os.Getenv("DOCKER_TLS_VERIFY") != ""
)

// CommonOptions are options common to both the client and the daemon.
type CommonOptions struct {
	Debug      bool
	Hosts      []string
	LogLevel   string
	TLS        bool
	TLSVerify  bool
	TLSOptions *tlsconfig.Options
	TrustKey   string
}

// NewCommonOptions returns a new CommonOptions
func NewCommonOptions() *CommonOptions {
	return &CommonOptions{}
}

// InstallFlags adds flags for the common options on the FlagSet
func (commonOpts *CommonOptions) InstallFlags(flags *pflag.FlagSet) {
	if dockerCertPath == "" {
		dockerCertPath = cliconfig.ConfigDir()
	}

	flags.BoolVarP(&commonOpts.Debug, "debug", "D", false, "Enable debug mode")
	flags.StringVarP(&commonOpts.LogLevel, "log-level", "l", "info", "Set the logging level (\"debug\", \"info\", \"warn\", \"error\", \"fatal\")")
	flags.BoolVar(&commonOpts.TLS, "tls", false, "Use TLS; implied by --tlsverify")
	flags.BoolVar(&commonOpts.TLSVerify, FlagTLSVerify, dockerTLSVerify, "Use TLS and verify the remote")

	// TODO use flag flags.String("identity"}, "i", "", "Path to libtrust key file")

	commonOpts.TLSOptions = &tlsconfig.Options{}
	tlsOptions := commonOpts.TLSOptions
	flags.StringVar(&tlsOptions.CAFile, "tlscacert", filepath.Join(dockerCertPath, DefaultCaFile), "Trust certs signed only by this CA")
	flags.StringVar(&tlsOptions.CertFile, "tlscert", filepath.Join(dockerCertPath, DefaultCertFile), "Path to TLS certificate file")
	flags.StringVar(&tlsOptions.KeyFile, "tlskey", filepath.Join(dockerCertPath, DefaultKeyFile), "Path to TLS key file")

	hostOpt := opts.NewNamedListOptsRef("hosts", &commonOpts.Hosts, opts.ValidateHost)
	flags.VarP(hostOpt, "host", "H", "Daemon socket(s) to connect to")
}

// SetDefaultOptions sets default values for options after flag parsing is
// complete
func (commonOpts *CommonOptions) SetDefaultOptions(flags *pflag.FlagSet) {
	// Regardless of whether the user sets it to true or false, if they
	// specify --tlsverify at all then we need to turn on tls
	// TLSVerify can be true even if not set due to DOCKER_TLS_VERIFY env var, so we need
	// to check that here as well
	if flags.Changed(FlagTLSVerify) || commonOpts.TLSVerify {
		commonOpts.TLS = true
	}

	if !commonOpts.TLS {
		commonOpts.TLSOptions = nil
	} else {
		tlsOptions := commonOpts.TLSOptions
		tlsOptions.InsecureSkipVerify = !commonOpts.TLSVerify

		// Reset CertFile and KeyFile to empty string if the user did not specify
		// the respective flags and the respective default files were not found.
		if !flags.Changed("tlscert") {
			if _, err := os.Stat(tlsOptions.CertFile); os.IsNotExist(err) {
				tlsOptions.CertFile = ""
			}
		}
		if !flags.Changed("tlskey") {
			if _, err := os.Stat(tlsOptions.KeyFile); os.IsNotExist(err) {
				tlsOptions.KeyFile = ""
			}
		}
	}
}

// SetLogLevel sets the logrus logging level
func SetLogLevel(logLevel string) {
	if logLevel != "" {
		lvl, err := logrus.ParseLevel(logLevel)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Unable to parse logging level: %s\n", logLevel)
			os.Exit(1)
		}
		logrus.SetLevel(lvl)
	} else {
		logrus.SetLevel(logrus.InfoLevel)
	}
}
<|endoftext|>


<commit_before>package cmux

import (
	"bytes"
	"io"
	"testing"
)

func TestBuffer(t *testing.T) {
	writeBytes := []byte("deadbeef")

	var b buffer
	for i := 0; i < 10; i++ {
		n, err := b.Write(writeBytes)
		if err != nil {
			t.Fatal(err)
		}
		if n != len(writeBytes) {
			t.Fatalf("cannot write all the bytes: want=%d got=%d", len(writeBytes), n)
		}
	}

	for j := 0; j < 2; j++ {
		readBytes := make([]byte, len(writeBytes))
		for i := 0; i < 10; i++ {
			n, err := b.Read(readBytes)
			if err != nil {
				t.Fatal(err)
			}
			if n != len(readBytes) {
				t.Fatalf("cannot read all the bytes: want=%d got=%d", len(readBytes), n)
			}
			if !bytes.Equal(writeBytes, readBytes) {
				t.Errorf("different bytes read: want=%d got=%d", writeBytes, readBytes)
			}
		}
		_, err := b.Read(readBytes)
		if err != io.EOF {
			t.Errorf("expected EOF")
		}

		b.resetRead()
	}
}

func TestBufferOffset(t *testing.T) {
	writeBytes := []byte("deadbeef")

	var b buffer
	n, err := b.Write(writeBytes)
	if err != nil {
		t.Fatal(err)
	}
	if n != len(writeBytes) {
		t.Fatalf("cannot write all the bytes: want=%d got=%d", len(writeBytes), n)
	}

	for i := 0; i < len(writeBytes)/2; i++ {
		readBytes := make([]byte, 2)
		n, err := b.Read(readBytes)
		if err != nil {
			t.Fatal(err)
		}
		if n != 2 {
			t.Fatal("cannot read the bytes: want=%d got=%d", 2, n)
		}
		if !bytes.Equal(readBytes, writeBytes[i*2:i*2+2]) {
			t.Fatalf("different bytes read: want=%s got=%s",
				readBytes, writeBytes[i*2:i*2+2])
		}
	}
}
<commit_msg>vet: buffer_test.go:65: possible formatting directive in Fatal call<commit_after>package cmux

import (
	"bytes"
	"io"
	"testing"
)

func TestBuffer(t *testing.T) {
	writeBytes := []byte("deadbeef")

	var b buffer
	for i := 0; i < 10; i++ {
		n, err := b.Write(writeBytes)
		if err != nil {
			t.Fatal(err)
		}
		if n != len(writeBytes) {
			t.Fatalf("cannot write all the bytes: want=%d got=%d", len(writeBytes), n)
		}
	}

	for j := 0; j < 2; j++ {
		readBytes := make([]byte, len(writeBytes))
		for i := 0; i < 10; i++ {
			n, err := b.Read(readBytes)
			if err != nil {
				t.Fatal(err)
			}
			if n != len(readBytes) {
				t.Fatalf("cannot read all the bytes: want=%d got=%d", len(readBytes), n)
			}
			if !bytes.Equal(writeBytes, readBytes) {
				t.Errorf("different bytes read: want=%d got=%d", writeBytes, readBytes)
			}
		}
		_, err := b.Read(readBytes)
		if err != io.EOF {
			t.Errorf("expected EOF")
		}

		b.resetRead()
	}
}

func TestBufferOffset(t *testing.T) {
	writeBytes := []byte("deadbeef")

	var b buffer
	n, err := b.Write(writeBytes)
	if err != nil {
		t.Fatal(err)
	}
	if n != len(writeBytes) {
		t.Fatalf("cannot write all the bytes: want=%d got=%d", len(writeBytes), n)
	}

	for i := 0; i < len(writeBytes)/2; i++ {
		readBytes := make([]byte, 2)
		n, err := b.Read(readBytes)
		if err != nil {
			t.Fatal(err)
		}
		if n != 2 {
			t.Fatalf("cannot read the bytes: want=%d got=%d", 2, n)
		}
		if !bytes.Equal(readBytes, writeBytes[i*2:i*2+2]) {
			t.Fatalf("different bytes read: want=%s got=%s",
				readBytes, writeBytes[i*2:i*2+2])
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package discoverspaces_test

import (
	"errors"

	gc "gopkg.in/check.v1"

	"github.com/juju/juju/api/base"
	apitesting "github.com/juju/juju/api/base/testing"
	"github.com/juju/juju/api/discoverspaces"
	coretesting "github.com/juju/juju/testing"
)

type DiscoverSpacesSuite struct {
	coretesting.BaseSuite
}

var _ = gc.Suite(&DiscoverSpacesSuite{})

func (s *DiscoverSpacesSuite) TestNewAPI(c *gc.C) {
	var called int
	apiCaller := clientErrorAPICaller(c, "CleanupIPAddresses", nil, &called)
	api := discoverspaces.NewAPI(apiCaller)
	c.Check(api, gc.NotNil)
	c.Check(called, gc.Equals, 0)

	// Make a call so that an error will be returned.
	err, _ := api.ListSpaces()
	c.Assert(err, gc.ErrorMatches, "client error!")
	c.Assert(called, gc.Equals, 1)
}

func clientErrorAPICaller(c *gc.C, method string, expectArgs interface{}, numCalls *int) base.APICaller {
	args := &apitesting.CheckArgs{
		Facade:        "Addresser",
		VersionIsZero: true,
		IdIsEmpty:     true,
		Method:        method,
		Args:          expectArgs,
	}
	return apitesting.CheckingAPICaller(c, args, numCalls, errors.New("client error!"))
}
<commit_msg>Test fix<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package discoverspaces_test

import (
	"errors"

	gc "gopkg.in/check.v1"

	"github.com/juju/juju/api/base"
	apitesting "github.com/juju/juju/api/base/testing"
	"github.com/juju/juju/api/discoverspaces"
	coretesting "github.com/juju/juju/testing"
)

type DiscoverSpacesSuite struct {
	coretesting.BaseSuite
}

var _ = gc.Suite(&DiscoverSpacesSuite{})

func (s *DiscoverSpacesSuite) TestNewAPI(c *gc.C) {
	var called int
	apiCaller := clientErrorAPICaller(c, "ListSpaces", nil, &called)
	api := discoverspaces.NewAPI(apiCaller)
	c.Check(api, gc.NotNil)
	c.Check(called, gc.Equals, 0)

	// Make a call so that an error will be returned.
	_, err := api.ListSpaces()
	c.Assert(err, gc.ErrorMatches, "client error!")
	c.Assert(called, gc.Equals, 1)
}

func clientErrorAPICaller(c *gc.C, method string, expectArgs interface{}, numCalls *int) base.APICaller {
	args := &apitesting.CheckArgs{
		Facade:        "DiscoverSpaces",
		VersionIsZero: true,
		IdIsEmpty:     true,
		Method:        method,
		Args:          expectArgs,
	}
	return apitesting.CheckingAPICaller(c, args, numCalls, errors.New("client error!"))
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"log"
	"os"
)

func main() {
	if len(os.Args) != 2 {
		log.Fatalf("The usage of the command is: \n\t%s <log-file.json>", os.Args[0])
	}

	file, err := os.Open(os.Args[1])
	if err != nil {
		log.Fatal(err)
	}

	closeFile := func(file *os.File) {
		if err := file.Close(); err != nil {
			log.Fatal(err)
		}
	}
	defer closeFile(file)

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()

		var entry interface{}
		if err := json.Unmarshal([]byte(line), &entry); err != nil {
			log.Printf("failed to unmarshal a log entry: %s\n", line)
		}

		m := entry.(map[string]interface{})
		if payload, ok := m["textPayload"]; ok {
			fmt.Printf("%s", payload)
		} else {
			log.Printf("the log entry does not have the `textPayload` field: %s\n", line)
		}
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
}<commit_msg>Update main.go<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"log"
	"os"
)

func main() {
	if len(os.Args) != 2 {
		log.Fatalf("The usage of the command is: \n\t%s <log-file.json>", os.Args[0])
	}

	file, err := os.Open(os.Args[1])
	if err != nil {
		log.Fatal(err)
	}

	closeFile := func(file *os.File) {
		if err := file.Close(); err != nil {
			log.Fatal(err)
		}
	}
	defer closeFile(file)

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()

		var entry interface{}
		if err := json.Unmarshal([]byte(line), &entry); err != nil {
			log.Printf("failed to unmarshal a log entry: %s\n", line)
		}

		m := entry.(map[string]interface{})
		if payload, ok := m["textPayload"]; ok {
			fmt.Printf("%s", payload)
		} else {
			log.Printf("the log entry does not have the `textPayload` field: %s\n", line)
		}
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package fireauth

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"time"
)

const (
	// Version used for creating token
	Version = 0
	// TokenSep used as a delimiter for the token
	TokenSep = "."
	// MaxUIDLen is the maximum length for an UID
	MaxUIDLen = 256
)

// Firebase specific values for header
const (
	TokenAlgorithm = "HS256"
	TokenType      = "JWT"
)

var encodedHeader = encode([]byte(fmt.Sprintf(`{"alg": "%s", "typ": "%s"}`, TokenAlgorithm, TokenType)))

// Generic errors
var (
	ErrNoUIDKey           = errors.New(`Data payload must contain a "uid" key`)
	ErrUIDNotString       = errors.New(`Data payload key "uid" must be a string`)
	ErrUIDTooLong         = errors.New(`Data payload key "uid" must not be longer than 256 characters`)
	ErrEmptyDataNoOptions = errors.New("Data is empty and no options are set.  This token will have no effect on Firebase.")
	ErrTokenTooLong       = errors.New("Generated token is too long. The token cannot be longer than 1024 bytes.")
)

// Generator represents a token generator
type Generator struct {
	secret string
}

// Option represent the claims used when creating an authentication token
// https://www.firebase.com/docs/rest/guide/user-auth.html#section-rest-tokens-without-helpers
type Option struct {
	// NotBefote is the token "not before" date as a number of seconds since the Unix epoch.
	// If specified, the token will not be considered valid until after this date.
	NotBefore int64 `json:"nbf,omitempty"`

	// Expiration is the token expiration date as a number of seconds since the Unix epoch.
	// If not specified, by default the token will expire 24 hours after the "issued at" date (iat).
	Expiration int64 `json:"exp,omitempty"`

	// Admin when set to true to make this an "admin" token, which grants full read and
	// write access to all data.
	Admin bool `json:"admin,omitempty"`

	// Debug when set to true to enable debug mode, which provides verbose error messages
	// when Security and Firebase Rules fail.
	Debug bool `json:"debug,omitempty"`
}

// Data is used to create a token. The token data can contain any data of your choosing,
// however it must contain a `uid` key, which must be a string of less than 256 characters
type Data map[string]interface{}

// New creates a new Generator
func New(secret string) *Generator {
	return &Generator{
		secret: secret,
	}
}

func generateClaim(data Data, options *Option, issuedAt int64) ([]byte, error) {
	// setup the claims for the token
	return json.Marshal(struct {
		*Option
		Version  int   `json:"v"`
		Data     Data  `json:"d"`
		IssuedAt int64 `json:"iat"`
	}{
		Option:   options,
		Version:  Version,
		Data:     data,
		IssuedAt: issuedAt,
	})
}

// CreateToken generates a new token with the given Data and options
func (t *Generator) CreateToken(data Data, options *Option) (string, error) {
	// make sure we have valid parameters
	if data == nil && (options == nil || (!options.Admin && !options.Debug)) {
		return "", ErrEmptyDataNoOptions
	}

	// validate the data
	if err := validate(data, (options != nil && options.Admin)); err != nil {
		return "", err
	}

	claim, err := generateClaim(data, options, time.Now().UTC().Unix())
	if err != nil {
		return "", err
	}

	// create the token
	secureString := encodedHeader + TokenSep + encode(claim)
	signature := sign(secureString, t.secret)
	token := secureString + TokenSep + signature

	if len(token) > 1024 {
		return "", ErrTokenTooLong
	}
	return token, nil
}

func validate(data Data, isAdmind bool) error {
	uid, containsID := data["uid"]
	if !containsID && !isAdmind {
		return ErrNoUIDKey
	}

	if _, isString := uid.(string); containsID && !isString {
		return ErrUIDNotString
	}

	if containsID && len(uid.(string)) > MaxUIDLen {
		return ErrUIDTooLong
	}
	return nil
}

func encode(data []byte) string {
	return strings.Replace(base64.URLEncoding.EncodeToString(data), "=", "", -1)
}

func sign(message, secret string) string {
	return encode(hmac.New(sha256.New, []byte(secret)).Sum([]byte(message)))
}
<commit_msg>removing fmt as a dependency<commit_after>package fireauth

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"errors"
	"strings"
	"time"
)

const (
	// Version used for creating token
	Version = 0
	// TokenSep used as a delimiter for the token
	TokenSep = "."
	// MaxUIDLen is the maximum length for an UID
	MaxUIDLen = 256
)

// Firebase specific values for header
const (
	TokenAlgorithm = "HS256"
	TokenType      = "JWT"
)

var encodedHeader = encode([]byte(`{"alg": "` + TokenAlgorithm + `", "typ": "` + TokenType + `"}`))

// Generic errors
var (
	ErrNoUIDKey           = errors.New(`Data payload must contain a "uid" key`)
	ErrUIDNotString       = errors.New(`Data payload key "uid" must be a string`)
	ErrUIDTooLong         = errors.New(`Data payload key "uid" must not be longer than 256 characters`)
	ErrEmptyDataNoOptions = errors.New("Data is empty and no options are set.  This token will have no effect on Firebase.")
	ErrTokenTooLong       = errors.New("Generated token is too long. The token cannot be longer than 1024 bytes.")
)

// Generator represents a token generator
type Generator struct {
	secret string
}

// Option represent the claims used when creating an authentication token
// https://www.firebase.com/docs/rest/guide/user-auth.html#section-rest-tokens-without-helpers
type Option struct {
	// NotBefote is the token "not before" date as a number of seconds since the Unix epoch.
	// If specified, the token will not be considered valid until after this date.
	NotBefore int64 `json:"nbf,omitempty"`

	// Expiration is the token expiration date as a number of seconds since the Unix epoch.
	// If not specified, by default the token will expire 24 hours after the "issued at" date (iat).
	Expiration int64 `json:"exp,omitempty"`

	// Admin when set to true to make this an "admin" token, which grants full read and
	// write access to all data.
	Admin bool `json:"admin,omitempty"`

	// Debug when set to true to enable debug mode, which provides verbose error messages
	// when Security and Firebase Rules fail.
	Debug bool `json:"debug,omitempty"`
}

// Data is used to create a token. The token data can contain any data of your choosing,
// however it must contain a `uid` key, which must be a string of less than 256 characters
type Data map[string]interface{}

// New creates a new Generator
func New(secret string) *Generator {
	return &Generator{
		secret: secret,
	}
}

func generateClaim(data Data, options *Option, issuedAt int64) ([]byte, error) {
	// setup the claims for the token
	return json.Marshal(struct {
		*Option
		Version  int   `json:"v"`
		Data     Data  `json:"d"`
		IssuedAt int64 `json:"iat"`
	}{
		Option:   options,
		Version:  Version,
		Data:     data,
		IssuedAt: issuedAt,
	})
}

// CreateToken generates a new token with the given Data and options
func (t *Generator) CreateToken(data Data, options *Option) (string, error) {
	// make sure we have valid parameters
	if data == nil && (options == nil || (!options.Admin && !options.Debug)) {
		return "", ErrEmptyDataNoOptions
	}

	// validate the data
	if err := validate(data, (options != nil && options.Admin)); err != nil {
		return "", err
	}

	claim, err := generateClaim(data, options, time.Now().UTC().Unix())
	if err != nil {
		return "", err
	}

	// create the token
	secureString := encodedHeader + TokenSep + encode(claim)
	signature := sign(secureString, t.secret)
	token := secureString + TokenSep + signature

	if len(token) > 1024 {
		return "", ErrTokenTooLong
	}
	return token, nil
}

func validate(data Data, isAdmind bool) error {
	uid, containsID := data["uid"]
	if !containsID && !isAdmind {
		return ErrNoUIDKey
	}

	if _, isString := uid.(string); containsID && !isString {
		return ErrUIDNotString
	}

	if containsID && len(uid.(string)) > MaxUIDLen {
		return ErrUIDTooLong
	}
	return nil
}

func encode(data []byte) string {
	return strings.Replace(base64.URLEncoding.EncodeToString(data), "=", "", -1)
}

func sign(message, secret string) string {
	return encode(hmac.New(sha256.New, []byte(secret)).Sum([]byte(message)))
}
<|endoftext|>


<commit_before>package moulshowcase

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"net/url"

	"github.com/gorilla/schema"
	"github.com/moul/bolosseum/bots"
	"github.com/moul/tictactoe/pkg/tictactoebot"
)

func init() {
	RegisterAction("bolosseum-tictactoe", BolosseumTictactoeAction)
}

func BolosseumTictactoeAction(qs string, stdin io.Reader) (*ActionResponse, error) {
	// Define arguments
	var opts struct {
		Message string `schema:"message"`
	}
	// FIXME: handle --help

	// Parse query
	m, err := url.ParseQuery(qs)
	if err != nil {
		return nil, err
	}
	if len(m) > 0 {
		// FIXME: if in web mode -> redirect to have options demo in the URL
		decoder := schema.NewDecoder()
		if err := decoder.Decode(&opts, m); err != nil {
			return nil, err
		}
	}

	var inputMessage string

	if opts.Message != "" {
		inputMessage = opts.Message
	} else {
		scanner := bufio.NewScanner(stdin)
		for scanner.Scan() {
			line := scanner.Text()
			inputMessage += line
		}
	}

	var question bots.QuestionMessage
	if err := json.Unmarshal([]byte(inputMessage), &question); err != nil {
		return nil, err
	}

	// FIXME: validate input

	bot := tictactoebot.NewTictactoeBot()
	reply := &bots.ReplyMessage{}
	switch question.Action {
	case "init":
		reply = bot.Init(question)
	case "play-turn":
		reply = bot.PlayTurn(question)
	default:
		// FIXME: reply message error
		return nil, fmt.Errorf("Unknown action: %q", question.Action)
	}

	return JsonResponse(reply), nil
}
<commit_msg>Add connectfour bot<commit_after>package moulshowcase

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"net/url"

	"github.com/gorilla/schema"
	"github.com/moul/bolosseum/bots"
	"github.com/moul/bolosseum/sdks/go"
	"github.com/moul/connect-four"
	"github.com/moul/tictactoe/pkg/tictactoebot"
)

func init() {
	RegisterAction("bolosseum-tictactoe", BolosseumTictactoeAction)
	RegisterAction("bolosseum-connectfour", BolosseumConnectfourAction)
}

func BolosseumConnectfourAction(qs string, stdin io.Reader) (*ActionResponse, error) {
	bot := connectfour.NewConnectfourBot()
	return BolosseumGenericAction(bot, qs, stdin)
}

func BolosseumTictactoeAction(qs string, stdin io.Reader) (*ActionResponse, error) {
	bot := tictactoebot.NewTictactoeBot()
	return BolosseumGenericAction(bot, qs, stdin)
}

func BolosseumGenericAction(bot bolosseumbot.BolosseumBot, qs string, stdin io.Reader) (*ActionResponse, error) {
	// Define arguments
	var opts struct {
		Message string `schema:"message"`
	}
	// FIXME: handle --help

	// Parse query
	m, err := url.ParseQuery(qs)
	if err != nil {
		return nil, err
	}
	if len(m) > 0 {
		// FIXME: if in web mode -> redirect to have options demo in the URL
		decoder := schema.NewDecoder()
		if err := decoder.Decode(&opts, m); err != nil {
			return nil, err
		}
	}

	var inputMessage string

	if opts.Message != "" {
		inputMessage = opts.Message
	} else {
		scanner := bufio.NewScanner(stdin)
		for scanner.Scan() {
			line := scanner.Text()
			inputMessage += line
		}
	}

	var question bots.QuestionMessage
	if err := json.Unmarshal([]byte(inputMessage), &question); err != nil {
		return nil, err
	}

	// FIXME: validate input

	reply := &bots.ReplyMessage{}
	switch question.Action {
	case "init":
		reply = bot.Init(question)
	case "play-turn":
		reply = bot.PlayTurn(question)
	default:
		// FIXME: reply message error
		return nil, fmt.Errorf("Unknown action: %q", question.Action)
	}

	return JsonResponse(reply), nil
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package charmrevisionupdater

import (
	"github.com/juju/loggo"
	"gopkg.in/juju/charm.v6-unstable"

	"github.com/juju/juju/apiserver/common"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/charmstore"
	"github.com/juju/juju/state"
)

var logger = loggo.GetLogger("juju.apiserver.charmrevisionupdater")

func init() {
	common.RegisterStandardFacade("CharmRevisionUpdater", 1, NewCharmRevisionUpdaterAPI)
}

// CharmRevisionUpdater defines the methods on the charmrevisionupdater API end point.
type CharmRevisionUpdater interface {
	UpdateLatestRevisions() (params.ErrorResult, error)
}

// CharmRevisionUpdaterAPI implements the CharmRevisionUpdater interface and is the concrete
// implementation of the api end point.
type CharmRevisionUpdaterAPI struct {
	state      *state.State
	resources  *common.Resources
	authorizer common.Authorizer
}

var _ CharmRevisionUpdater = (*CharmRevisionUpdaterAPI)(nil)

// NewCharmRevisionUpdaterAPI creates a new server-side charmrevisionupdater API end point.
func NewCharmRevisionUpdaterAPI(
	st *state.State,
	resources *common.Resources,
	authorizer common.Authorizer,
) (*CharmRevisionUpdaterAPI, error) {
	if !authorizer.AuthMachineAgent() && !authorizer.AuthModelManager() {
		return nil, common.ErrPerm
	}
	return &CharmRevisionUpdaterAPI{
		state: st, resources: resources, authorizer: authorizer}, nil
}

// UpdateLatestRevisions retrieves the latest revision information from the charm store for all deployed charms
// and records this information in state.
func (api *CharmRevisionUpdaterAPI) UpdateLatestRevisions() (params.ErrorResult, error) {
	if err := api.updateLatestRevisions(); err != nil {
		return params.ErrorResult{Error: common.ServerError(err)}, nil
	}
	return params.ErrorResult{}, nil
}

func (api *CharmRevisionUpdaterAPI) updateLatestRevisions() error {
	// Get the handlers to use.
	handlers, err := createHandlers(api.state)
	if err != nil {
		return err
	}

	// Look up the information for all the deployed charms. This is the
	// "expensive" part.
	latest, err := retrieveLatestCharmInfo(api.state)
	if err != nil {
		return err
	}

	// Process the resulting info for each charm.
	for _, info := range latest {
		// First, add a charm placeholder to the model for each.
		if err = api.state.AddStoreCharmPlaceholder(info.LatestURL()); err != nil {
			return err
		}

		// Then run through the handlers.
		serviceID := info.service.ServiceTag()
		for _, handler := range handlers {
			if err := handler.HandleLatest(serviceID, info.CharmInfo); err != nil {
				return err
			}
		}
	}

	return nil
}

// NewCharmStoreClient instantiates a new charm store repository.
// It is defined at top level for testing purposes.
var NewCharmStoreClient = func() charmstore.Client {
	// TODO(ericsnow) Use the Juju "HTTP context" once we have one.
	return charmstore.NewClient(nil)
}

type latestCharmInfo struct {
	charmstore.CharmInfo
	service *state.Service
}

// retrieveLatestCharmInfo looks up the charm store to return the charm URLs for the
// latest revision of the deployed charms.
func retrieveLatestCharmInfo(st *state.State) ([]latestCharmInfo, error) {
	// First get the uuid for the environment to use when querying the charm store.
	env, err := st.Model()
	if err != nil {
		return nil, err
	}
	uuid := env.UUID()

	services, err := st.AllServices()
	if err != nil {
		return nil, err
	}

	var curls []*charm.URL
	var resultsIndexedServices []*state.Service
	for _, service := range services {
		curl, _ := service.CharmURL()
		if curl.Schema == "local" {
			// Version checking for charms from local repositories is not
			// currently supported, since we don't yet support passing in
			// a path to the local repo. This may change if the need arises.
			continue
		}
		curls = append(curls, curl)
		resultsIndexedServices = append(resultsIndexedServices, service)
	}

	client := NewCharmStoreClient()

	results, err := charmstore.LatestCharmInfo(client, uuid, curls)
	if err != nil {
		return nil, err
	}

	var latest []latestCharmInfo
	for i, result := range results {
		if result.Error != nil {
			logger.Errorf("retrieving charm info for %s: %v", curls[i], result.Error)
			continue
		}
		service := resultsIndexedServices[i]
		latest = append(latest, latestCharmInfo{
			CharmInfo: result.CharmInfo,
			service:   service,
		})
	}
	return latest, nil
}
<commit_msg>uuid -> modelUUID.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package charmrevisionupdater

import (
	"github.com/juju/loggo"
	"gopkg.in/juju/charm.v6-unstable"

	"github.com/juju/juju/apiserver/common"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/charmstore"
	"github.com/juju/juju/state"
)

var logger = loggo.GetLogger("juju.apiserver.charmrevisionupdater")

func init() {
	common.RegisterStandardFacade("CharmRevisionUpdater", 1, NewCharmRevisionUpdaterAPI)
}

// CharmRevisionUpdater defines the methods on the charmrevisionupdater API end point.
type CharmRevisionUpdater interface {
	UpdateLatestRevisions() (params.ErrorResult, error)
}

// CharmRevisionUpdaterAPI implements the CharmRevisionUpdater interface and is the concrete
// implementation of the api end point.
type CharmRevisionUpdaterAPI struct {
	state      *state.State
	resources  *common.Resources
	authorizer common.Authorizer
}

var _ CharmRevisionUpdater = (*CharmRevisionUpdaterAPI)(nil)

// NewCharmRevisionUpdaterAPI creates a new server-side charmrevisionupdater API end point.
func NewCharmRevisionUpdaterAPI(
	st *state.State,
	resources *common.Resources,
	authorizer common.Authorizer,
) (*CharmRevisionUpdaterAPI, error) {
	if !authorizer.AuthMachineAgent() && !authorizer.AuthModelManager() {
		return nil, common.ErrPerm
	}
	return &CharmRevisionUpdaterAPI{
		state: st, resources: resources, authorizer: authorizer}, nil
}

// UpdateLatestRevisions retrieves the latest revision information from the charm store for all deployed charms
// and records this information in state.
func (api *CharmRevisionUpdaterAPI) UpdateLatestRevisions() (params.ErrorResult, error) {
	if err := api.updateLatestRevisions(); err != nil {
		return params.ErrorResult{Error: common.ServerError(err)}, nil
	}
	return params.ErrorResult{}, nil
}

func (api *CharmRevisionUpdaterAPI) updateLatestRevisions() error {
	// Get the handlers to use.
	handlers, err := createHandlers(api.state)
	if err != nil {
		return err
	}

	// Look up the information for all the deployed charms. This is the
	// "expensive" part.
	latest, err := retrieveLatestCharmInfo(api.state)
	if err != nil {
		return err
	}

	// Process the resulting info for each charm.
	for _, info := range latest {
		// First, add a charm placeholder to the model for each.
		if err = api.state.AddStoreCharmPlaceholder(info.LatestURL()); err != nil {
			return err
		}

		// Then run through the handlers.
		serviceID := info.service.ServiceTag()
		for _, handler := range handlers {
			if err := handler.HandleLatest(serviceID, info.CharmInfo); err != nil {
				return err
			}
		}
	}

	return nil
}

// NewCharmStoreClient instantiates a new charm store repository.
// It is defined at top level for testing purposes.
var NewCharmStoreClient = func() charmstore.Client {
	// TODO(ericsnow) Use the Juju "HTTP context" once we have one.
	return charmstore.NewClient(nil)
}

type latestCharmInfo struct {
	charmstore.CharmInfo
	service *state.Service
}

// retrieveLatestCharmInfo looks up the charm store to return the charm URLs for the
// latest revision of the deployed charms.
func retrieveLatestCharmInfo(st *state.State) ([]latestCharmInfo, error) {
	// First get the uuid for the environment to use when querying the charm store.
	env, err := st.Model()
	if err != nil {
		return nil, err
	}
	modelUUID := env.UUID()

	services, err := st.AllServices()
	if err != nil {
		return nil, err
	}

	var curls []*charm.URL
	var resultsIndexedServices []*state.Service
	for _, service := range services {
		curl, _ := service.CharmURL()
		if curl.Schema == "local" {
			// Version checking for charms from local repositories is not
			// currently supported, since we don't yet support passing in
			// a path to the local repo. This may change if the need arises.
			continue
		}
		curls = append(curls, curl)
		resultsIndexedServices = append(resultsIndexedServices, service)
	}

	client := NewCharmStoreClient()

	results, err := charmstore.LatestCharmInfo(client, modelUUID, curls)
	if err != nil {
		return nil, err
	}

	var latest []latestCharmInfo
	for i, result := range results {
		if result.Error != nil {
			logger.Errorf("retrieving charm info for %s: %v", curls[i], result.Error)
			continue
		}
		service := resultsIndexedServices[i]
		latest = append(latest, latestCharmInfo{
			CharmInfo: result.CharmInfo,
			service:   service,
		})
	}
	return latest, nil
}
<|endoftext|>


<commit_before>package gallery

import (
	"fmt"
	"github.com/hoisie/web"
	"github.com/jmoiron/monet/app"
	"github.com/jmoiron/monet/db"
	"github.com/jmoiron/monet/template"
	"time"
)

type M map[string]interface{}

var (
	adminBase     = template.Base{Path: "admin/base.mustache"}
	listPageSize  = 20
	indexListSize = 6
)

func auth(f func(*web.Context) string) func(*web.Context) string {
	ret := func(ctx *web.Context) string {
		if app.RequireAuthentication(ctx) {
			return ""
		}
		return f(ctx)
	}
	return ret
}

func AttachAdmin(url string) {
	web.Get(url+"gallery/", auth(galleryList))
	web.Post(url+"gallery/update-settings/", auth(updateSettings))
	web.Get(url+"gallery/force-update/", auth(forceUpdate))
}

func galleryList(ctx *web.Context) string {
	gc := LoadGalleryConfig()
	err := gc.Check()
	if err != nil {
		return missingConfig(ctx, err)
	}

	var albums []*PicasaAlbum
	db.Find(&PicasaAlbum{}, nil).All(&albums)

	panels := []M{}

	for i, album := range albums {
		side := "left"
		if i%2 != 0 {
			side = "right"
		}
		panels = append(panels, M{
			"side":  side,
			"right": side == "right",
			"Album": album,
		})
		fmt.Println(app.PrettyPrint(album))
	}

	return adminBase.Render("gallery/admin/index.mustache", M{
		"GalleryConfig": gc,
		"Albums":        panels,
	})
}

func forceUpdate(ctx *web.Context) string {
	gc := LoadGalleryConfig()
	err := gc.Check()
	if err != nil {
		return missingConfig(ctx, err)
	}
	api := NewPicasaAPI(gc.UserID)
	albums, err := api.ListAlbums()
	if err != nil {
		return fmt.Sprintf("Error: %s", err.Error())
	}
	for _, album := range albums {
		db.Upsert(album)
	}
	gc.LastRun = time.Now().Unix()
	ctx.Redirect(302, "/admin/")
	return ""
}

func updateSettings(ctx *web.Context) string {
	gc := LoadGalleryConfig()
	gc.UserID = ctx.Params["userid"]
	gc.Type = ctx.Params["type"]
	gc.Save()
	ctx.Redirect(302, "/admin/")
	return ""
}

func missingConfig(ctx *web.Context, err error) string {
	gc := LoadGalleryConfig()
	return adminBase.Render("gallery/missing-config.mustache", M{
		"GalleryConfig": gc, "Error": err.Error()})
}

// *** Panels ***

type GalleryPanel struct{}

func (gp *GalleryPanel) Render() string {
	gc := LoadGalleryConfig()
	err := gc.Check()
	if err != nil {
		return template.Render("gallery/admin/gallery-panel-badconfig.mustache", M{
			"GalleryConfig": gc,
			"Error":         err.Error(),
		})
	}

	var albums []PicasaAlbum
	db.Find(&PicasaAlbum{}, nil).Limit(indexListSize).All(&albums)
	return template.Render("gallery/admin/gallery-panel.mustache", M{
		"GalleryConfig": gc,
		"Albums":        albums,
		"HasRun":        gc.LastRun > 0,
	})
}
<commit_msg>save the gallery config so it remembers that it actually ran<commit_after>package gallery

import (
	"fmt"
	"github.com/hoisie/web"
	"github.com/jmoiron/monet/app"
	"github.com/jmoiron/monet/db"
	"github.com/jmoiron/monet/template"
	"time"
)

type M map[string]interface{}

var (
	adminBase     = template.Base{Path: "admin/base.mustache"}
	listPageSize  = 20
	indexListSize = 6
)

func auth(f func(*web.Context) string) func(*web.Context) string {
	ret := func(ctx *web.Context) string {
		if app.RequireAuthentication(ctx) {
			return ""
		}
		return f(ctx)
	}
	return ret
}

func AttachAdmin(url string) {
	web.Get(url+"gallery/", auth(galleryList))
	web.Post(url+"gallery/update-settings/", auth(updateSettings))
	web.Get(url+"gallery/force-update/", auth(forceUpdate))
}

func galleryList(ctx *web.Context) string {
	gc := LoadGalleryConfig()
	err := gc.Check()
	if err != nil {
		return missingConfig(ctx, err)
	}

	var albums []*PicasaAlbum
	db.Find(&PicasaAlbum{}, nil).All(&albums)

	panels := []M{}

	for i, album := range albums {
		side := "left"
		if i%2 != 0 {
			side = "right"
		}
		panels = append(panels, M{
			"side":  side,
			"right": side == "right",
			"Album": album,
		})
		fmt.Println(app.PrettyPrint(album))
	}

	return adminBase.Render("gallery/admin/index.mustache", M{
		"GalleryConfig": gc,
		"Albums":        panels,
	})
}

func forceUpdate(ctx *web.Context) string {
	gc := LoadGalleryConfig()
	err := gc.Check()
	if err != nil {
		return missingConfig(ctx, err)
	}
	api := NewPicasaAPI(gc.UserID)
	albums, err := api.ListAlbums()
	if err != nil {
		return fmt.Sprintf("Error: %s", err.Error())
	}
	for _, album := range albums {
		db.Upsert(album)
	}
	gc.LastRun = time.Now().Unix()
	gc.Save()
	ctx.Redirect(302, "/admin/")
	return ""
}

func updateSettings(ctx *web.Context) string {
	gc := LoadGalleryConfig()
	gc.UserID = ctx.Params["userid"]
	gc.Type = ctx.Params["type"]
	gc.Save()
	ctx.Redirect(302, "/admin/")
	return ""
}

func missingConfig(ctx *web.Context, err error) string {
	gc := LoadGalleryConfig()
	return adminBase.Render("gallery/missing-config.mustache", M{
		"GalleryConfig": gc, "Error": err.Error()})
}

// *** Panels ***

type GalleryPanel struct{}

func (gp *GalleryPanel) Render() string {
	gc := LoadGalleryConfig()
	err := gc.Check()
	if err != nil {
		return template.Render("gallery/admin/gallery-panel-badconfig.mustache", M{
			"GalleryConfig": gc,
			"Error":         err.Error(),
		})
	}

	var albums []PicasaAlbum
	db.Find(&PicasaAlbum{}, nil).Limit(indexListSize).All(&albums)
	return template.Render("gallery/admin/gallery-panel.mustache", M{
		"GalleryConfig": gc,
		"Albums":        albums,
		"HasRun":        gc.LastRun > 0,
	})
}
<|endoftext|>


<commit_before>package manager

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/common/log"
	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/common/poller"
	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/config"
	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/enum"
	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/peer"
	to "github.com/apache/incubator-trafficcontrol/traffic_ops/client"
)

// CopyTrafficMonitorConfigMap returns a deep copy of the given TrafficMonitorConfigMap
func CopyTrafficMonitorConfigMap(a *to.TrafficMonitorConfigMap) to.TrafficMonitorConfigMap {
	b := to.TrafficMonitorConfigMap{}
	b.TrafficServer = map[string]to.TrafficServer{}
	b.CacheGroup = map[string]to.TMCacheGroup{}
	b.Config = map[string]interface{}{}
	b.TrafficMonitor = map[string]to.TrafficMonitor{}
	b.DeliveryService = map[string]to.TMDeliveryService{}
	b.Profile = map[string]to.TMProfile{}
	for k, v := range a.TrafficServer {
		b.TrafficServer[k] = v
	}
	for k, v := range a.CacheGroup {
		b.CacheGroup[k] = v
	}
	for k, v := range a.Config {
		b.Config[k] = v
	}
	for k, v := range a.TrafficMonitor {
		b.TrafficMonitor[k] = v
	}
	for k, v := range a.DeliveryService {
		b.DeliveryService[k] = v
	}
	for k, v := range a.Profile {
		b.Profile[k] = v
	}
	return b
}

// TrafficMonitorConfigMapThreadsafe encapsulates a TrafficMonitorConfigMap safe for multiple readers and a single writer.
type TrafficMonitorConfigMapThreadsafe struct {
	monitorConfig *to.TrafficMonitorConfigMap
	m             *sync.RWMutex
}

// NewTrafficMonitorConfigMapThreadsafe returns an encapsulated TrafficMonitorConfigMap safe for multiple readers and a single writer.
func NewTrafficMonitorConfigMapThreadsafe() TrafficMonitorConfigMapThreadsafe {
	return TrafficMonitorConfigMapThreadsafe{monitorConfig: &to.TrafficMonitorConfigMap{}, m: &sync.RWMutex{}}
}

// Get returns the TrafficMonitorConfigMap. Callers MUST NOT modify, it is not threadsafe for mutation. If mutation is necessary, call CopyTrafficMonitorConfigMap().
func (t *TrafficMonitorConfigMapThreadsafe) Get() to.TrafficMonitorConfigMap {
	t.m.RLock()
	defer t.m.RUnlock()
	return *t.monitorConfig
}

// Set sets the TrafficMonitorConfigMap. This is only safe for one writer. This MUST NOT be called by multiple threads.
func (t *TrafficMonitorConfigMapThreadsafe) Set(c to.TrafficMonitorConfigMap) {
	t.m.Lock()
	*t.monitorConfig = c
	t.m.Unlock()
}

// StartMonitorConfigManager runs the monitor config manager goroutine, and returns the threadsafe data which it sets.
func StartMonitorConfigManager(
	monitorConfigPollChan <-chan to.TrafficMonitorConfigMap,
	localStates peer.CRStatesThreadsafe,
	statURLSubscriber chan<- poller.HttpPollerConfig,
	healthURLSubscriber chan<- poller.HttpPollerConfig,
	peerURLSubscriber chan<- poller.HttpPollerConfig,
	cachesChangeSubscriber chan<- struct{},
	cfg config.Config,
	staticAppData StaticAppData,
) TrafficMonitorConfigMapThreadsafe {
	monitorConfig := NewTrafficMonitorConfigMapThreadsafe()
	go monitorConfigListen(monitorConfig,
		monitorConfigPollChan,
		localStates,
		statURLSubscriber,
		healthURLSubscriber,
		peerURLSubscriber,
		cachesChangeSubscriber,
		cfg,
		staticAppData,
	)
	return monitorConfig
}

// trafficOpsHealthConnectionTimeoutToDuration takes the int from Traffic Ops, which is in milliseconds, and returns a time.Duration
// TODO change Traffic Ops Client API to a time.Duration
func trafficOpsHealthConnectionTimeoutToDuration(t int) time.Duration {
	return time.Duration(t) * time.Millisecond
}

// trafficOpsPeerPollIntervalToDuration takes the int from Traffic Ops, which is in milliseconds, and returns a time.Duration
// TODO change Traffic Ops Client API to a time.Duration
func trafficOpsPeerPollIntervalToDuration(t int) time.Duration {
	return time.Duration(t) * time.Millisecond
}

// trafficOpsStatPollIntervalToDuration takes the int from Traffic Ops, which is in milliseconds, and returns a time.Duration
// TODO change Traffic Ops Client API to a time.Duration
func trafficOpsStatPollIntervalToDuration(t int) time.Duration {
	return time.Duration(t) * time.Millisecond
}

// trafficOpsHealthPollIntervalToDuration takes the int from Traffic Ops, which is in milliseconds, and returns a time.Duration
// TODO change Traffic Ops Client API to a time.Duration
func trafficOpsHealthPollIntervalToDuration(t int) time.Duration {
	return time.Duration(t) * time.Millisecond
}

// getPollIntervals reads the Traffic Ops Client monitorConfig structure, and parses and returns the health, peer, and stat poll intervals
func getHealthPeerStatPollIntervals(monitorConfig to.TrafficMonitorConfigMap, cfg config.Config) (time.Duration, time.Duration, time.Duration, error) {
	healthPollIntervalI, healthPollIntervalExists := monitorConfig.Config["health.polling.interval"]
	if !healthPollIntervalExists {
		return 0, 0, 0, fmt.Errorf("Traffic Ops Monitor config missing 'health.polling.interval', not setting config changes.\n")
	}
	healthPollIntervalInt, healthPollIntervalIsInt := healthPollIntervalI.(float64)
	if !healthPollIntervalIsInt {
		return 0, 0, 0, fmt.Errorf("Traffic Ops Monitor config 'health.polling.interval' value '%v' type %T is not an integer, not setting config changes.\n", healthPollIntervalI, healthPollIntervalI)
	}
	healthPollInterval := trafficOpsHealthPollIntervalToDuration(int(healthPollIntervalInt))

	peerPollIntervalI, peerPollIntervalExists := monitorConfig.Config["peers.polling.interval"]
	if !peerPollIntervalExists {
		return 0, 0, 0, fmt.Errorf("Traffic Ops Monitor config missing 'peers.polling.interval', not setting config changes.\n")
	}
	peerPollIntervalInt, peerPollIntervalIsInt := peerPollIntervalI.(float64)
	if !peerPollIntervalIsInt {
		return 0, 0, 0, fmt.Errorf("Traffic Ops Monitor config 'peers.polling.interval' value '%v' type %T is not an integer, not setting config changes.\n", peerPollIntervalI, peerPollIntervalI)
	}
	peerPollInterval := trafficOpsPeerPollIntervalToDuration(int(peerPollIntervalInt))

	statPollIntervalI, statPollIntervalExists := monitorConfig.Config["stat.polling.interval"]
	if !statPollIntervalExists {
		log.Warnf("Traffic Ops Monitor config missing 'stat.polling.interval', using health for stat.\n")
		statPollIntervalI = healthPollIntervalI
	}
	statPollIntervalInt, statPollIntervalIsInt := statPollIntervalI.(float64)
	if !statPollIntervalIsInt {
		log.Warnf("Traffic Ops Monitor config 'stat.polling.interval' value '%v' type %T is not an integer, using health for stat\n", statPollIntervalI, statPollIntervalI)
		statPollIntervalInt = healthPollIntervalInt
	}
	statPollInterval := trafficOpsStatPollIntervalToDuration(int(statPollIntervalInt))

	// Formerly, only 'health' polling existed. If TO still has old configuration and doesn't have a 'stat' parameter, this allows us to assume the 'health' poll is slow, and sets it to the stat poll (which used to be the only poll, getting all astats data) to the given presumed-slow health poll, and set the now-fast-and-small health poll to a short fraction of that.
	if healthPollIntervalExists && !statPollIntervalExists {
		healthPollInterval = time.Duration(float64(healthPollInterval) / float64(cfg.HealthToStatRatio))
	}

	return healthPollInterval, peerPollInterval, statPollInterval, nil
}

// TODO timing, and determine if the case, or its internal `for`, should be put in a goroutine
// TODO determine if subscribers take action on change, and change to mutexed objects if not.
func monitorConfigListen(
	monitorConfigTS TrafficMonitorConfigMapThreadsafe,
	monitorConfigPollChan <-chan to.TrafficMonitorConfigMap,
	localStates peer.CRStatesThreadsafe,
	statURLSubscriber chan<- poller.HttpPollerConfig,
	healthURLSubscriber chan<- poller.HttpPollerConfig,
	peerURLSubscriber chan<- poller.HttpPollerConfig,
	cachesChangeSubscriber chan<- struct{},
	cfg config.Config,
	staticAppData StaticAppData,
) {
	for monitorConfig := range monitorConfigPollChan {
		monitorConfigTS.Set(monitorConfig)
		healthURLs := map[string]poller.PollConfig{}
		statURLs := map[string]poller.PollConfig{}
		peerURLs := map[string]poller.PollConfig{}
		caches := map[string]string{}

		healthPollInterval, peerPollInterval, statPollInterval, err := getHealthPeerStatPollIntervals(monitorConfig, cfg)
		if err != nil {
			continue
		}

		for _, srv := range monitorConfig.TrafficServer {
			caches[srv.HostName] = srv.Status

			cacheName := enum.CacheName(srv.HostName)

			srvStatus := enum.CacheStatusFromString(srv.Status)
			if srvStatus == enum.CacheStatusOnline {
				localStates.SetCache(cacheName, peer.IsAvailable{IsAvailable: true})
				continue
			}
			if srvStatus == enum.CacheStatusOffline {
				continue
			}
			// seed states with available = false until our polling cycle picks up a result
			if _, exists := localStates.GetCache(cacheName); !exists {
				localStates.SetCache(cacheName, peer.IsAvailable{IsAvailable: false})
			}

			url := monitorConfig.Profile[srv.Profile].Parameters.HealthPollingURL
			if url == "" {
				log.Errorf("monitor config server %v profile %v has no polling URL; can't poll", srv.HostName, srv.Profile)
				continue
			}
			r := strings.NewReplacer(
				"${hostname}", srv.IP,
				"${interface_name}", srv.InterfaceName,
				"application=system", "application=plugin.remap",
				"application=", "application=plugin.remap",
			)
			url = r.Replace(url)

			connTimeout := trafficOpsHealthConnectionTimeoutToDuration(monitorConfig.Profile[srv.Profile].Parameters.HealthConnectionTimeout)
			healthURLs[srv.HostName] = poller.PollConfig{URL: url, Timeout: connTimeout}
			r = strings.NewReplacer("application=plugin.remap", "application=")
			statURL := r.Replace(url)
			statURLs[srv.HostName] = poller.PollConfig{URL: statURL, Timeout: connTimeout}
		}

		for _, srv := range monitorConfig.TrafficMonitor {
			if srv.HostName == staticAppData.Hostname {
				continue
			}
			if enum.CacheStatusFromString(srv.Status) != enum.CacheStatusOnline {
				continue
			}
			// TODO: the URL should be config driven. -jse
			url := fmt.Sprintf("http://%s:%d/publish/CrStates?raw", srv.IP, srv.Port)
			peerURLs[srv.HostName] = poller.PollConfig{URL: url} // TODO determine timeout.
		}

		statURLSubscriber <- poller.HttpPollerConfig{Urls: statURLs, Interval: statPollInterval}
		healthURLSubscriber <- poller.HttpPollerConfig{Urls: healthURLs, Interval: healthPollInterval}
		peerURLSubscriber <- poller.HttpPollerConfig{Urls: peerURLs, Interval: peerPollInterval}

		for cacheName := range localStates.GetCaches() {
			if _, exists := monitorConfig.TrafficServer[string(cacheName)]; !exists {
				log.Warnf("Removing %s from localStates", cacheName)
				localStates.DeleteCache(cacheName)
			}
		}

		cachesChangeSubscriber <- struct{}{}

		// TODO because there are multiple writers to localStates.DeliveryService, there is a race condition, where MonitorConfig (this func) and HealthResultManager could write at the same time, and the HealthResultManager could overwrite a delivery service addition or deletion here. Probably the simplest and most performant fix would be a lock-free algorithm using atomic compare-and-swaps.
		for _, ds := range monitorConfig.DeliveryService {
			// since caches default to unavailable, also default DS false
			if _, exists := localStates.GetDeliveryService(enum.DeliveryServiceName(ds.XMLID)); !exists {
				localStates.SetDeliveryService(enum.DeliveryServiceName(ds.XMLID), peer.Deliveryservice{IsAvailable: false, DisabledLocations: []enum.CacheName{}}) // important to initialize DisabledLocations, so JSON is `[]` not `null`
			}
		}
		for ds := range localStates.GetDeliveryServices() {
			if _, exists := monitorConfig.DeliveryService[string(ds)]; !exists {
				localStates.DeleteDeliveryService(ds)
			}
		}
	}
}
<commit_msg>Change TM2 poll params to match TM1<commit_after>package manager

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/common/log"
	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/common/poller"
	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/config"
	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/enum"
	"github.com/apache/incubator-trafficcontrol/traffic_monitor/experimental/traffic_monitor/peer"
	to "github.com/apache/incubator-trafficcontrol/traffic_ops/client"
)

// CopyTrafficMonitorConfigMap returns a deep copy of the given TrafficMonitorConfigMap
func CopyTrafficMonitorConfigMap(a *to.TrafficMonitorConfigMap) to.TrafficMonitorConfigMap {
	b := to.TrafficMonitorConfigMap{}
	b.TrafficServer = map[string]to.TrafficServer{}
	b.CacheGroup = map[string]to.TMCacheGroup{}
	b.Config = map[string]interface{}{}
	b.TrafficMonitor = map[string]to.TrafficMonitor{}
	b.DeliveryService = map[string]to.TMDeliveryService{}
	b.Profile = map[string]to.TMProfile{}
	for k, v := range a.TrafficServer {
		b.TrafficServer[k] = v
	}
	for k, v := range a.CacheGroup {
		b.CacheGroup[k] = v
	}
	for k, v := range a.Config {
		b.Config[k] = v
	}
	for k, v := range a.TrafficMonitor {
		b.TrafficMonitor[k] = v
	}
	for k, v := range a.DeliveryService {
		b.DeliveryService[k] = v
	}
	for k, v := range a.Profile {
		b.Profile[k] = v
	}
	return b
}

// TrafficMonitorConfigMapThreadsafe encapsulates a TrafficMonitorConfigMap safe for multiple readers and a single writer.
type TrafficMonitorConfigMapThreadsafe struct {
	monitorConfig *to.TrafficMonitorConfigMap
	m             *sync.RWMutex
}

// NewTrafficMonitorConfigMapThreadsafe returns an encapsulated TrafficMonitorConfigMap safe for multiple readers and a single writer.
func NewTrafficMonitorConfigMapThreadsafe() TrafficMonitorConfigMapThreadsafe {
	return TrafficMonitorConfigMapThreadsafe{monitorConfig: &to.TrafficMonitorConfigMap{}, m: &sync.RWMutex{}}
}

// Get returns the TrafficMonitorConfigMap. Callers MUST NOT modify, it is not threadsafe for mutation. If mutation is necessary, call CopyTrafficMonitorConfigMap().
func (t *TrafficMonitorConfigMapThreadsafe) Get() to.TrafficMonitorConfigMap {
	t.m.RLock()
	defer t.m.RUnlock()
	return *t.monitorConfig
}

// Set sets the TrafficMonitorConfigMap. This is only safe for one writer. This MUST NOT be called by multiple threads.
func (t *TrafficMonitorConfigMapThreadsafe) Set(c to.TrafficMonitorConfigMap) {
	t.m.Lock()
	*t.monitorConfig = c
	t.m.Unlock()
}

// StartMonitorConfigManager runs the monitor config manager goroutine, and returns the threadsafe data which it sets.
func StartMonitorConfigManager(
	monitorConfigPollChan <-chan to.TrafficMonitorConfigMap,
	localStates peer.CRStatesThreadsafe,
	statURLSubscriber chan<- poller.HttpPollerConfig,
	healthURLSubscriber chan<- poller.HttpPollerConfig,
	peerURLSubscriber chan<- poller.HttpPollerConfig,
	cachesChangeSubscriber chan<- struct{},
	cfg config.Config,
	staticAppData StaticAppData,
) TrafficMonitorConfigMapThreadsafe {
	monitorConfig := NewTrafficMonitorConfigMapThreadsafe()
	go monitorConfigListen(monitorConfig,
		monitorConfigPollChan,
		localStates,
		statURLSubscriber,
		healthURLSubscriber,
		peerURLSubscriber,
		cachesChangeSubscriber,
		cfg,
		staticAppData,
	)
	return monitorConfig
}

// trafficOpsHealthConnectionTimeoutToDuration takes the int from Traffic Ops, which is in milliseconds, and returns a time.Duration
// TODO change Traffic Ops Client API to a time.Duration
func trafficOpsHealthConnectionTimeoutToDuration(t int) time.Duration {
	return time.Duration(t) * time.Millisecond
}

// trafficOpsPeerPollIntervalToDuration takes the int from Traffic Ops, which is in milliseconds, and returns a time.Duration
// TODO change Traffic Ops Client API to a time.Duration
func trafficOpsPeerPollIntervalToDuration(t int) time.Duration {
	return time.Duration(t) * time.Millisecond
}

// trafficOpsStatPollIntervalToDuration takes the int from Traffic Ops, which is in milliseconds, and returns a time.Duration
// TODO change Traffic Ops Client API to a time.Duration
func trafficOpsStatPollIntervalToDuration(t int) time.Duration {
	return time.Duration(t) * time.Millisecond
}

// trafficOpsHealthPollIntervalToDuration takes the int from Traffic Ops, which is in milliseconds, and returns a time.Duration
// TODO change Traffic Ops Client API to a time.Duration
func trafficOpsHealthPollIntervalToDuration(t int) time.Duration {
	return time.Duration(t) * time.Millisecond
}

// getPollIntervals reads the Traffic Ops Client monitorConfig structure, and parses and returns the health, peer, and stat poll intervals
func getHealthPeerStatPollIntervals(monitorConfig to.TrafficMonitorConfigMap, cfg config.Config) (time.Duration, time.Duration, time.Duration, error) {
	peerPollIntervalI, peerPollIntervalExists := monitorConfig.Config["peers.polling.interval"]
	if !peerPollIntervalExists {
		return 0, 0, 0, fmt.Errorf("Traffic Ops Monitor config missing 'peers.polling.interval', not setting config changes.\n")
	}
	peerPollIntervalInt, peerPollIntervalIsInt := peerPollIntervalI.(float64)
	if !peerPollIntervalIsInt {
		return 0, 0, 0, fmt.Errorf("Traffic Ops Monitor config 'peers.polling.interval' value '%v' type %T is not an integer, not setting config changes.\n", peerPollIntervalI, peerPollIntervalI)
	}
	peerPollInterval := trafficOpsPeerPollIntervalToDuration(int(peerPollIntervalInt))

	statPollIntervalI, statPollIntervalExists := monitorConfig.Config["health.polling.interval"]
	if !statPollIntervalExists {
		return 0, 0, 0, fmt.Errorf("Traffic Ops Monitor config missing 'health.polling.interval', not setting config changes.\n")
	}
	statPollIntervalInt, statPollIntervalIsInt := statPollIntervalI.(float64)
	if !statPollIntervalIsInt {
		return 0, 0, 0, fmt.Errorf("Traffic Ops Monitor config 'health.polling.interval' value '%v' type %T is not an integer, not setting config changes.\n", statPollIntervalI, statPollIntervalI)
	}
	statPollInterval := trafficOpsStatPollIntervalToDuration(int(statPollIntervalInt))

	healthPollIntervalI, healthPollIntervalExists := monitorConfig.Config["heartbeat.polling.interval"]
	healthPollIntervalInt, healthPollIntervalIsInt := healthPollIntervalI.(float64)
	if !healthPollIntervalExists {
		log.Warnf("Traffic Ops Monitor config missing 'heartbeat.polling.interval', using health for heartbeat.\n")
		healthPollIntervalInt = statPollIntervalInt
	} else if !healthPollIntervalIsInt {
		log.Warnf("Traffic Ops Monitor config 'heartbeat.polling.interval' value '%v' type %T is not an integer, using health for heartbeat\n", statPollIntervalI, statPollIntervalI)
		healthPollIntervalInt = statPollIntervalInt
	}
	healthPollInterval := trafficOpsHealthPollIntervalToDuration(int(healthPollIntervalInt))

	return healthPollInterval, peerPollInterval, statPollInterval, nil
}

// TODO timing, and determine if the case, or its internal `for`, should be put in a goroutine
// TODO determine if subscribers take action on change, and change to mutexed objects if not.
func monitorConfigListen(
	monitorConfigTS TrafficMonitorConfigMapThreadsafe,
	monitorConfigPollChan <-chan to.TrafficMonitorConfigMap,
	localStates peer.CRStatesThreadsafe,
	statURLSubscriber chan<- poller.HttpPollerConfig,
	healthURLSubscriber chan<- poller.HttpPollerConfig,
	peerURLSubscriber chan<- poller.HttpPollerConfig,
	cachesChangeSubscriber chan<- struct{},
	cfg config.Config,
	staticAppData StaticAppData,
) {
	for monitorConfig := range monitorConfigPollChan {
		monitorConfigTS.Set(monitorConfig)
		healthURLs := map[string]poller.PollConfig{}
		statURLs := map[string]poller.PollConfig{}
		peerURLs := map[string]poller.PollConfig{}
		caches := map[string]string{}

		healthPollInterval, peerPollInterval, statPollInterval, err := getHealthPeerStatPollIntervals(monitorConfig, cfg)
		if err != nil {
			continue
		}

		for _, srv := range monitorConfig.TrafficServer {
			caches[srv.HostName] = srv.Status

			cacheName := enum.CacheName(srv.HostName)

			srvStatus := enum.CacheStatusFromString(srv.Status)
			if srvStatus == enum.CacheStatusOnline {
				localStates.SetCache(cacheName, peer.IsAvailable{IsAvailable: true})
				continue
			}
			if srvStatus == enum.CacheStatusOffline {
				continue
			}
			// seed states with available = false until our polling cycle picks up a result
			if _, exists := localStates.GetCache(cacheName); !exists {
				localStates.SetCache(cacheName, peer.IsAvailable{IsAvailable: false})
			}

			url := monitorConfig.Profile[srv.Profile].Parameters.HealthPollingURL
			if url == "" {
				log.Errorf("monitor config server %v profile %v has no polling URL; can't poll", srv.HostName, srv.Profile)
				continue
			}
			r := strings.NewReplacer(
				"${hostname}", srv.IP,
				"${interface_name}", srv.InterfaceName,
				"application=system", "application=plugin.remap",
				"application=", "application=plugin.remap",
			)
			url = r.Replace(url)

			connTimeout := trafficOpsHealthConnectionTimeoutToDuration(monitorConfig.Profile[srv.Profile].Parameters.HealthConnectionTimeout)
			healthURLs[srv.HostName] = poller.PollConfig{URL: url, Timeout: connTimeout}
			r = strings.NewReplacer("application=plugin.remap", "application=")
			statURL := r.Replace(url)
			statURLs[srv.HostName] = poller.PollConfig{URL: statURL, Timeout: connTimeout}
		}

		for _, srv := range monitorConfig.TrafficMonitor {
			if srv.HostName == staticAppData.Hostname {
				continue
			}
			if enum.CacheStatusFromString(srv.Status) != enum.CacheStatusOnline {
				continue
			}
			// TODO: the URL should be config driven. -jse
			url := fmt.Sprintf("http://%s:%d/publish/CrStates?raw", srv.IP, srv.Port)
			peerURLs[srv.HostName] = poller.PollConfig{URL: url} // TODO determine timeout.
		}

		statURLSubscriber <- poller.HttpPollerConfig{Urls: statURLs, Interval: statPollInterval}
		healthURLSubscriber <- poller.HttpPollerConfig{Urls: healthURLs, Interval: healthPollInterval}
		peerURLSubscriber <- poller.HttpPollerConfig{Urls: peerURLs, Interval: peerPollInterval}

		for cacheName := range localStates.GetCaches() {
			if _, exists := monitorConfig.TrafficServer[string(cacheName)]; !exists {
				log.Warnf("Removing %s from localStates", cacheName)
				localStates.DeleteCache(cacheName)
			}
		}

		cachesChangeSubscriber <- struct{}{}

		// TODO because there are multiple writers to localStates.DeliveryService, there is a race condition, where MonitorConfig (this func) and HealthResultManager could write at the same time, and the HealthResultManager could overwrite a delivery service addition or deletion here. Probably the simplest and most performant fix would be a lock-free algorithm using atomic compare-and-swaps.
		for _, ds := range monitorConfig.DeliveryService {
			// since caches default to unavailable, also default DS false
			if _, exists := localStates.GetDeliveryService(enum.DeliveryServiceName(ds.XMLID)); !exists {
				localStates.SetDeliveryService(enum.DeliveryServiceName(ds.XMLID), peer.Deliveryservice{IsAvailable: false, DisabledLocations: []enum.CacheName{}}) // important to initialize DisabledLocations, so JSON is `[]` not `null`
			}
		}
		for ds := range localStates.GetDeliveryServices() {
			if _, exists := monitorConfig.DeliveryService[string(ds)]; !exists {
				localStates.DeleteDeliveryService(ds)
			}
		}
	}
}
<|endoftext|>