<commit_before>// Copyright 2015 Geofrey Ernest <geofreyernest@live.com>. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// Package auth is a collection of authentication middlewares for zedlist.
package auth

import (
	"net/http"

	"github.com/gernest/zedlist/modules/db"
	"github.com/gernest/zedlist/modules/log"
	"github.com/gernest/zedlist/modules/query"
	"github.com/gernest/zedlist/modules/session"
	"github.com/gernest/zedlist/modules/utils"

	"github.com/gernest/zedlist/modules/settings"
	"github.com/labstack/echo"
)

var store = session.New()

// Normal just adds user detail to templates data context. it is wise to add this before
// Must(). This unlike Must will not return an error, regardless of if the user is loged
// in or not
func Normal() echo.HandlerFunc {
	return func(ctx echo.Context) error {
		if v := ctx.Get("IsLoged"); v != nil && v.(bool) == true {
			return nil
		}
		ss, err := store.Get(ctx.Request(), settings.App.Session.Name)
		if err != nil {
			log.Error(ctx, err)
		}
		if v, ok := ss.Values["userID"]; ok {
			id := v.(int64)
			person, err := query.GetPersonByUserID(db.Conn, id)
			if err != nil {
				log.Error(ctx, err)
				return err
			}
			ctx.Set("IsLoged", true)
			ctx.Set("User", person)
			ctx.Set("UserID", id)
			utils.SetData(ctx, "IsLoged", true)
			utils.SetData(ctx, "Person", person)
			utils.SetData(ctx, "UserID", id)
			return nil
		}
		return nil
	}
}

// Must ensures that any route is authorized to access the next handler
// otherwise an error is returned.
//
// TODO custom not authorized handler?
func Must() echo.HandlerFunc {
	return func(ctx echo.Context) error {
		if v := ctx.Get("IsLoged"); v != nil && v.(bool) == true {
			return nil
		}
		ss, err := store.Get(ctx.Request(), settings.App.Session.Name)
		if err != nil {
			log.Error(ctx, err)
		}
		if v, ok := ss.Values["userID"]; ok {
			id := v.(int64)
			person, err := query.GetPersonByUserID(db.Conn, id)
			if err != nil {
				log.Error(ctx, err)
				return err
			}
			ctx.Set("IsLoged", true)
			ctx.Set("User", person)
			ctx.Set("UserID", id)
			utils.SetData(ctx, "IsLoged", true)
			utils.SetData(ctx, "User", person)
			utils.SetData(ctx, "UserID", id)
			return nil
		}
		return ctx.Redirect(http.StatusFound, "/auth/login")
	}
}
<commit_msg>add active person to template context data<commit_after>// Copyright 2015 Geofrey Ernest <geofreyernest@live.com>. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// Package auth is a collection of authentication middlewares for zedlist.
package auth

import (
	"net/http"

	"github.com/gernest/zedlist/modules/db"
	"github.com/gernest/zedlist/modules/log"
	"github.com/gernest/zedlist/modules/query"
	"github.com/gernest/zedlist/modules/session"
	"github.com/gernest/zedlist/modules/utils"

	"github.com/gernest/zedlist/modules/settings"
	"github.com/labstack/echo"
)

var store = session.New()

// Normal just adds user detail to templates data context. it is wise to add this before
// Must(). This unlike Must will not return an error, regardless of if the user is loged
// in or not
func Normal() echo.HandlerFunc {
	return func(ctx echo.Context) error {
		if v := ctx.Get("IsLoged"); v != nil && v.(bool) == true {
			return nil
		}
		ss, err := store.Get(ctx.Request(), settings.App.Session.Name)
		if err != nil {
			log.Error(ctx, err)
		}
		if v, ok := ss.Values["userID"]; ok {
			id := v.(int64)
			person, err := query.GetPersonByUserID(db.Conn, id)
			if err != nil {
				log.Error(ctx, err)
				return err
			}
			ctx.Set("IsLoged", true)
			ctx.Set("User", person)
			ctx.Set("UserID", id)
			utils.SetData(ctx, "IsLoged", true)
			utils.SetData(ctx, "Person", person)
			utils.SetData(ctx, "UserID", id)
			return nil
		}
		return nil
	}
}

// Must ensures that any route is authorized to access the next handler
// otherwise an error is returned.
//
// TODO custom not authorized handler?
func Must() echo.HandlerFunc {
	return func(ctx echo.Context) error {
		if v := ctx.Get("IsLoged"); v != nil && v.(bool) == true {
			return nil
		}
		ss, err := store.Get(ctx.Request(), settings.App.Session.Name)
		if err != nil {
			log.Error(ctx, err)
		}
		if v, ok := ss.Values["userID"]; ok {
			id := v.(int64)
			person, err := query.GetPersonByUserID(db.Conn, id)
			if err != nil {
				log.Error(ctx, err)
				return err
			}
			ctx.Set("IsLoged", true)
			ctx.Set("User", person)
			ctx.Set("UserID", id)
			utils.SetData(ctx, "IsLoged", true)
			utils.SetData(ctx, "Person", person)
			utils.SetData(ctx, "UserID", id)
			return nil
		}
		return ctx.Redirect(http.StatusFound, "/auth/login")
	}
}
<|endoftext|>


<commit_before>package middlewares

import (
	"crypto/md5"
	"fmt"
	"net/http"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	"github.com/solefaucet/sole-server/models"
)

// PersonalyAuthRequired rejects request if client ip is not in the list and signature not match
func PersonalyAuthRequired(whitelistIPs, appHash, secretKey string) gin.HandlerFunc {
	ips := make(map[string]struct{})
	for _, v := range strings.Split(whitelistIPs, ",") {
		ips[v] = struct{}{}
	}

	return func(c *gin.Context) {
		if _, ok := ips[c.ClientIP()]; !ok {
			c.String(http.StatusForbidden, "0")
			return
		}

		data := fmt.Sprintf("%v:%v:%v", c.Query("user_id"), appHash, secretKey)
		if sign := fmt.Sprintf("%x", md5.Sum([]byte(data))); sign != c.Query("signature") {
			logrus.WithFields(logrus.Fields{
				"event":       models.EventPersonalyInvalidSignature,
				"user_id":     c.Query("user_id"),
				"signature":   sign,
				"q_signature": c.Query("signature"),
			}).Error("signature not matched")
			c.String(http.StatusForbidden, "0")
			return
		}

		c.Next()
	}
}
<commit_msg>Abort request if invalid<commit_after>package middlewares

import (
	"crypto/md5"
	"fmt"
	"net/http"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	"github.com/solefaucet/sole-server/models"
)

// PersonalyAuthRequired rejects request if client ip is not in the list and signature not match
func PersonalyAuthRequired(whitelistIPs, appHash, secretKey string) gin.HandlerFunc {
	ips := make(map[string]struct{})
	for _, v := range strings.Split(whitelistIPs, ",") {
		ips[v] = struct{}{}
	}

	return func(c *gin.Context) {
		if _, ok := ips[c.ClientIP()]; !ok {
			c.AbortWithStatus(http.StatusForbidden)
			return
		}

		data := fmt.Sprintf("%v:%v:%v", c.Query("user_id"), appHash, secretKey)
		if sign := fmt.Sprintf("%x", md5.Sum([]byte(data))); sign != c.Query("signature") {
			logrus.WithFields(logrus.Fields{
				"event":       models.EventPersonalyInvalidSignature,
				"user_id":     c.Query("user_id"),
				"signature":   sign,
				"q_signature": c.Query("signature"),
			}).Error("signature not matched")
			c.AbortWithStatus(http.StatusForbidden)
			return
		}

		c.Next()
	}
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/Dataman-Cloud/swan/mesos"
	"github.com/Dataman-Cloud/swan/types"
	"github.com/Dataman-Cloud/swan/utils"
	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
)

func (r *Server) runCompose(w http.ResponseWriter, req *http.Request) {
	var err error

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

	var cmp types.Compose
	if err = decode(req.Body, &cmp); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// check conflict
	if cmp, _ := r.db.GetCompose(cmp.Name); cmp != nil {
		http.Error(w, fmt.Sprintf("compose %s already exists", cmp.Name), http.StatusConflict)
		return
	}

	// convert raw yaml to service group
	if cmp.RequireConvert() {
		if cmp.ServiceGroup, err = cmp.ToServiceGroup(); err != nil {
			http.Error(w, fmt.Sprintf("yaml convert error: %v", err), http.StatusBadRequest)
			return
		}
	}

	// verify
	if err := cmp.Valid(); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// ensure all settings could be converted to App Version
	for name, service := range cmp.ServiceGroup {
		if _, err := service.ToVersion(cmp.Name, r.driver.ClusterName()); err != nil {
			http.Error(w, fmt.Sprintf("%s: version convert error: %v", name, err), http.StatusBadRequest)
			return
		}
	}

	// get runas
	var runAs = cmp.RunAs()

	// db save
	cmp.ID = utils.RandomString(16)
	cmp.DisplayName = fmt.Sprintf("%s.%s.%s", cmp.Name, runAs, r.driver.ClusterName())
	cmp.OpStatus = types.OpStatusCreating
	cmp.CreatedAt = time.Now()

	if err := r.db.CreateCompose(&cmp); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	srvOrders, err := cmp.ServiceGroup.PrioritySort()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	go func() {
		var err error

		// defer mark compose op status
		defer func() {
			if err != nil {
				log.Errorf("launch compose %s error: %v", cmp.Name, err)
				r.memoComposeStatus(cmp.ID, types.OpStatusNoop, err.Error())
			} else {
				r.memoComposeStatus(cmp.ID, types.OpStatusNoop, "")
			}
		}()

		// create each App by order
		for _, name := range srvOrders {
			var (
				service = cmp.ServiceGroup[name]
				ver, _  = service.ToVersion(cmp.Name, r.driver.ClusterName())
				cluster = r.driver.ClusterName()
				appId   = fmt.Sprintf("%s.%s.%s.%s", ver.Name, cmp.Name, ver.RunAs, cluster)
			)

			app := &types.Application{
				ID:        appId,
				Name:      ver.Name,
				RunAs:     ver.RunAs,
				Cluster:   cluster,
				OpStatus:  types.OpStatusCreating,
				CreatedAt: time.Now(),
				UpdatedAt: time.Now(),
			}

			if err = r.db.CreateApp(app); err != nil {
				err = fmt.Errorf("create App %s db App error: %v", appId, err)
				return
			}

			if err = r.db.CreateVersion(appId, ver); err != nil {
				err = fmt.Errorf("create App %s db Version error: %v", appId, err)
				return
			}

			count := int(ver.Instances)
			log.Debugf("Preparing to launch %d App %s tasks", count, appId)

			for i := 0; i < count; i++ {
				var (
					taskName = fmt.Sprintf("%d.%s", i, appId)
					taskId   = fmt.Sprintf("%s.%s", utils.RandomString(12), taskName)
				)

				cfg := types.NewTaskConfig(ver, i)

				if cfg.Network != "host" && cfg.Network != "bridge" {
					cfg.Parameters = append(cfg.Parameters, &types.Parameter{
						Key:   "ip",
						Value: ver.IPs[i],
					})

					cfg.IP = ver.IPs[i]
				}

				task := &types.Task{
					ID:      taskId,
					Name:    taskName,
					Weight:  100,
					Status:  "pending",
					Healthy: types.TaskHealthyUnset,
					Version: ver.ID,
					Created: time.Now(),
					Updated: time.Now(),
				}

				healthSet := ver.HealthCheck != nil && !ver.HealthCheck.IsEmpty()
				if healthSet {
					task.Healthy = types.TaskUnHealthy
				}

				log.Debugf("Create task %s in db", taskId)
				if err = r.db.CreateTask(appId, task); err != nil {
					err = fmt.Errorf("create db task failed: %s", err)
					return
				}

				var (
					t     = mesos.NewTask(cfg, taskId, taskName)
					tasks = []*mesos.Task{t}
				)

				err = r.driver.LaunchTasks(tasks)
				if err != nil {
					err = fmt.Errorf("launch compose tasks %s error: %v", taskName, err)
					return
				}
			}

			// max wait for 5 seconds to confirm the preivous app get normal
			if err = r.ensureAppReady(appId, time.Second*5); err != nil {
				return
			}

			// mark app status
			r.memoAppStatus(appId, types.OpStatusNoop, "")
		}
	}()

	writeJSON(w, http.StatusCreated, map[string]string{"Id": cmp.ID})
}

func (r *Server) parseYAML(w http.ResponseWriter, req *http.Request) {
	var param struct {
		YAML string `json:"yaml"`
	}

	if err := decode(req.Body, &param); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	cfg, err := utils.YamlServices([]byte(param.YAML), nil)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var (
		vars = utils.YamlVariables([]byte(param.YAML))
		srvs = make([]string, 0, 0)
	)
	for _, srv := range cfg.Services {
		srvs = append(srvs, srv.Name)
	}

	if len(srvs) == 0 {
		http.Error(w, "at least one of docker service definition required", http.StatusBadRequest)
		return
	}

	writeJSON(w, http.StatusOK, map[string]interface{}{
		"services":  srvs,
		"variables": vars,
	})
}

func (r *Server) listComposes(w http.ResponseWriter, req *http.Request) {
	cs, err := r.db.ListComposes()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	sort.Sort(types.ComposeSorter(cs))
	writeJSON(w, http.StatusOK, cs)
}

func (r *Server) getCompose(w http.ResponseWriter, req *http.Request) {
	composeId := mux.Vars(req)["compose_id"]
	cmp, err := r.db.GetCompose(composeId)
	if err != nil {
		if strings.Contains(err.Error(), "no such compose") {
			http.Error(w, err.Error(), http.StatusNotFound)
			return
		}
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	wrapper, err := r.wrapCompose(cmp)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	writeJSON(w, http.StatusOK, wrapper)
}

func (r *Server) getComposeDependency(w http.ResponseWriter, req *http.Request) {
	composeId := mux.Vars(req)["compose_id"]
	cmp, err := r.db.GetCompose(composeId)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	dependency, _ := cmp.ServiceGroup.DependMap()
	writeJSON(w, http.StatusOK, dependency)
}

func (r *Server) deleteCompose(w http.ResponseWriter, req *http.Request) {
	composeId := mux.Vars(req)["compose_id"]
	cmp, err := r.db.GetCompose(composeId)
	if err != nil {
		if strings.Contains(err.Error(), "no such compose") {
			http.Error(w, err.Error(), http.StatusNotFound)
			return
		}
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// mark db status
	if err := r.memoComposeStatus(cmp.ID, types.OpStatusDeleting, ""); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	go func() {
		var err error

		// defer mark db status
		defer func() {
			if err != nil {
				log.Errorf("remove compose %s error: %v", cmp.Name, err)
				r.memoComposeStatus(cmp.ID, types.OpStatusNoop, err.Error())
			}
		}()

		// remove each of app
		for name := range cmp.ServiceGroup {
			appId := fmt.Sprintf("%s.%s.%s.%s", name, cmp.Name, cmp.RunAs(), r.driver.ClusterName())

			if _, err := r.db.GetApp(appId); err != nil {
				err = fmt.Errorf("get App %s error: %v", appId, err)
				return
			}

			tasks, err := r.db.ListTasks(appId)
			if err != nil {
				err = fmt.Errorf("get App %s tasks error: %v", appId, err)
				return
			}

			versions, err := r.db.ListVersions(appId)
			if err != nil {
				err = fmt.Errorf("get App %s versions error: %v", appId, err)
				return
			}

			if err = r.delApp(appId, tasks, versions); err != nil {
				return
			}
		}

		// remove db compose
		if err = r.db.DeleteCompose(composeId); err != nil {
			err = fmt.Errorf("remove db compose error: %v", err)
			return
		}
	}()

	writeJSON(w, http.StatusNoContent, "")
}

// short hands to memo update Compose.OpStatus & Compose.ErrMsg
// it's the caller responsibility to process the db error.
func (r *Server) memoComposeStatus(cmpId, op, errmsg string) error {
	cmp, err := r.db.GetCompose(cmpId)
	if err != nil {
		log.Errorf("memoComposeStatus() get db compose %s error: %v", cmpId, err)
		return err
	}

	var (
		prevOp = cmp.OpStatus
	)

	cmp.OpStatus = op
	cmp.ErrMsg = errmsg
	cmp.UpdatedAt = time.Now()

	if err := r.db.UpdateCompose(cmp); err != nil {
		log.Errorf("memoComposeStatus() update app compose status from %s -> %s error: %v",
			prevOp, op, err)
		return err
	}

	return nil
}

// sometimes mesos executor emit consecutive events TASKRUNNING and TASKFAILED if without healthy check,
// which will cause the scheduler treats the task launching as succeed, actually it does NOT.
// we use this to make sure the previous app is ready within compose launching
func (r *Server) ensureAppReady(appId string, maxWait time.Duration) error {
	var (
		app *types.Application
		err error
	)

	for goesby := int64(0); goesby <= int64(maxWait); goesby += int64(time.Second) {
		time.Sleep(time.Second)
		if app, err = r.db.GetApp(appId); err != nil {
			continue
		}

		if app.TasksStatus["TASK_RUNNING"] == app.TaskCount {
			return nil
		}
	}

	if err != nil {
		return err
	}

	return fmt.Errorf("app %s not ready, only %d/%d tasks running",
		appId, app.TasksStatus["TASK_RUNNING"], app.TaskCount)
}

func (r *Server) wrapCompose(cmp *types.Compose) (*types.ComposeWrapper, error) {
	wrapper := &types.ComposeWrapper{Compose: cmp}

	apps, err := r.db.ListApps()
	if err != nil {
		return nil, err
	}

	for _, app := range apps {
		if app.Name+"."+cmp.DisplayName == app.ID {
			wrapper.Apps = append(wrapper.Apps, app)
		}
	}
	return wrapper, nil
}
<commit_msg>similar to #869 but for compose launching<commit_after>package api

import (
	"fmt"
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/Dataman-Cloud/swan/mesos"
	"github.com/Dataman-Cloud/swan/types"
	"github.com/Dataman-Cloud/swan/utils"
	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
)

func (r *Server) runCompose(w http.ResponseWriter, req *http.Request) {
	var err error

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

	var cmp types.Compose
	if err = decode(req.Body, &cmp); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// check conflict
	if cmp, _ := r.db.GetCompose(cmp.Name); cmp != nil {
		http.Error(w, fmt.Sprintf("compose %s already exists", cmp.Name), http.StatusConflict)
		return
	}

	// convert raw yaml to service group
	if cmp.RequireConvert() {
		if cmp.ServiceGroup, err = cmp.ToServiceGroup(); err != nil {
			http.Error(w, fmt.Sprintf("yaml convert error: %v", err), http.StatusBadRequest)
			return
		}
	}

	// verify
	if err := cmp.Valid(); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// ensure all settings could be converted to App Version
	for name, service := range cmp.ServiceGroup {
		if _, err := service.ToVersion(cmp.Name, r.driver.ClusterName()); err != nil {
			http.Error(w, fmt.Sprintf("%s: version convert error: %v", name, err), http.StatusBadRequest)
			return
		}
	}

	// ensure proxy uniq & not occupied
	for _, service := range cmp.ServiceGroup {
		// ensure proxy Listen & Alias uniq
		if err := r.checkProxyDuplication(service.Extra.Proxy); err != nil {
			http.Error(w, err.Error(), http.StatusConflict)
			return
		}

		// ensure os ports not in using
		if err := r.checkPortListening(service.Extra.Proxy); err != nil {
			http.Error(w, err.Error(), http.StatusConflict)
			return
		}
	}

	// get runas
	var runAs = cmp.RunAs()

	// db save
	cmp.ID = utils.RandomString(16)
	cmp.DisplayName = fmt.Sprintf("%s.%s.%s", cmp.Name, runAs, r.driver.ClusterName())
	cmp.OpStatus = types.OpStatusCreating
	cmp.CreatedAt = time.Now()

	if err := r.db.CreateCompose(&cmp); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	srvOrders, err := cmp.ServiceGroup.PrioritySort()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	go func() {
		var err error

		// defer mark compose op status
		defer func() {
			if err != nil {
				log.Errorf("launch compose %s error: %v", cmp.Name, err)
				r.memoComposeStatus(cmp.ID, types.OpStatusNoop, err.Error())
			} else {
				r.memoComposeStatus(cmp.ID, types.OpStatusNoop, "")
			}
		}()

		// create each App by order
		for _, name := range srvOrders {
			var (
				service = cmp.ServiceGroup[name]
				ver, _  = service.ToVersion(cmp.Name, r.driver.ClusterName())
				cluster = r.driver.ClusterName()
				appId   = fmt.Sprintf("%s.%s.%s.%s", ver.Name, cmp.Name, ver.RunAs, cluster)
			)

			app := &types.Application{
				ID:        appId,
				Name:      ver.Name,
				RunAs:     ver.RunAs,
				Cluster:   cluster,
				OpStatus:  types.OpStatusCreating,
				CreatedAt: time.Now(),
				UpdatedAt: time.Now(),
			}

			if err = r.db.CreateApp(app); err != nil {
				err = fmt.Errorf("create App %s db App error: %v", appId, err)
				return
			}

			if err = r.db.CreateVersion(appId, ver); err != nil {
				err = fmt.Errorf("create App %s db Version error: %v", appId, err)
				return
			}

			count := int(ver.Instances)
			log.Debugf("Preparing to launch %d App %s tasks", count, appId)

			for i := 0; i < count; i++ {
				var (
					taskName = fmt.Sprintf("%d.%s", i, appId)
					taskId   = fmt.Sprintf("%s.%s", utils.RandomString(12), taskName)
				)

				cfg := types.NewTaskConfig(ver, i)

				if cfg.Network != "host" && cfg.Network != "bridge" {
					cfg.Parameters = append(cfg.Parameters, &types.Parameter{
						Key:   "ip",
						Value: ver.IPs[i],
					})

					cfg.IP = ver.IPs[i]
				}

				task := &types.Task{
					ID:      taskId,
					Name:    taskName,
					Weight:  100,
					Status:  "pending",
					Healthy: types.TaskHealthyUnset,
					Version: ver.ID,
					Created: time.Now(),
					Updated: time.Now(),
				}

				healthSet := ver.HealthCheck != nil && !ver.HealthCheck.IsEmpty()
				if healthSet {
					task.Healthy = types.TaskUnHealthy
				}

				log.Debugf("Create task %s in db", taskId)
				if err = r.db.CreateTask(appId, task); err != nil {
					err = fmt.Errorf("create db task failed: %s", err)
					return
				}

				var (
					t     = mesos.NewTask(cfg, taskId, taskName)
					tasks = []*mesos.Task{t}
				)

				err = r.driver.LaunchTasks(tasks)
				if err != nil {
					err = fmt.Errorf("launch compose tasks %s error: %v", taskName, err)
					return
				}
			}

			// max wait for 5 seconds to confirm the preivous app get normal
			if err = r.ensureAppReady(appId, time.Second*5); err != nil {
				return
			}

			// mark app status
			r.memoAppStatus(appId, types.OpStatusNoop, "")
		}
	}()

	writeJSON(w, http.StatusCreated, map[string]string{"Id": cmp.ID})
}

func (r *Server) parseYAML(w http.ResponseWriter, req *http.Request) {
	var param struct {
		YAML string `json:"yaml"`
	}

	if err := decode(req.Body, &param); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	cfg, err := utils.YamlServices([]byte(param.YAML), nil)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var (
		vars = utils.YamlVariables([]byte(param.YAML))
		srvs = make([]string, 0, 0)
	)
	for _, srv := range cfg.Services {
		srvs = append(srvs, srv.Name)
	}

	if len(srvs) == 0 {
		http.Error(w, "at least one of docker service definition required", http.StatusBadRequest)
		return
	}

	writeJSON(w, http.StatusOK, map[string]interface{}{
		"services":  srvs,
		"variables": vars,
	})
}

func (r *Server) listComposes(w http.ResponseWriter, req *http.Request) {
	cs, err := r.db.ListComposes()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	sort.Sort(types.ComposeSorter(cs))
	writeJSON(w, http.StatusOK, cs)
}

func (r *Server) getCompose(w http.ResponseWriter, req *http.Request) {
	composeId := mux.Vars(req)["compose_id"]
	cmp, err := r.db.GetCompose(composeId)
	if err != nil {
		if strings.Contains(err.Error(), "no such compose") {
			http.Error(w, err.Error(), http.StatusNotFound)
			return
		}
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	wrapper, err := r.wrapCompose(cmp)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	writeJSON(w, http.StatusOK, wrapper)
}

func (r *Server) getComposeDependency(w http.ResponseWriter, req *http.Request) {
	composeId := mux.Vars(req)["compose_id"]
	cmp, err := r.db.GetCompose(composeId)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	dependency, _ := cmp.ServiceGroup.DependMap()
	writeJSON(w, http.StatusOK, dependency)
}

func (r *Server) deleteCompose(w http.ResponseWriter, req *http.Request) {
	composeId := mux.Vars(req)["compose_id"]
	cmp, err := r.db.GetCompose(composeId)
	if err != nil {
		if strings.Contains(err.Error(), "no such compose") {
			http.Error(w, err.Error(), http.StatusNotFound)
			return
		}
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// mark db status
	if err := r.memoComposeStatus(cmp.ID, types.OpStatusDeleting, ""); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	go func() {
		var err error

		// defer mark db status
		defer func() {
			if err != nil {
				log.Errorf("remove compose %s error: %v", cmp.Name, err)
				r.memoComposeStatus(cmp.ID, types.OpStatusNoop, err.Error())
			}
		}()

		// remove each of app
		for name := range cmp.ServiceGroup {
			appId := fmt.Sprintf("%s.%s.%s.%s", name, cmp.Name, cmp.RunAs(), r.driver.ClusterName())

			if _, err := r.db.GetApp(appId); err != nil {
				err = fmt.Errorf("get App %s error: %v", appId, err)
				return
			}

			tasks, err := r.db.ListTasks(appId)
			if err != nil {
				err = fmt.Errorf("get App %s tasks error: %v", appId, err)
				return
			}

			versions, err := r.db.ListVersions(appId)
			if err != nil {
				err = fmt.Errorf("get App %s versions error: %v", appId, err)
				return
			}

			if err = r.delApp(appId, tasks, versions); err != nil {
				return
			}
		}

		// remove db compose
		if err = r.db.DeleteCompose(composeId); err != nil {
			err = fmt.Errorf("remove db compose error: %v", err)
			return
		}
	}()

	writeJSON(w, http.StatusNoContent, "")
}

// short hands to memo update Compose.OpStatus & Compose.ErrMsg
// it's the caller responsibility to process the db error.
func (r *Server) memoComposeStatus(cmpId, op, errmsg string) error {
	cmp, err := r.db.GetCompose(cmpId)
	if err != nil {
		log.Errorf("memoComposeStatus() get db compose %s error: %v", cmpId, err)
		return err
	}

	var (
		prevOp = cmp.OpStatus
	)

	cmp.OpStatus = op
	cmp.ErrMsg = errmsg
	cmp.UpdatedAt = time.Now()

	if err := r.db.UpdateCompose(cmp); err != nil {
		log.Errorf("memoComposeStatus() update app compose status from %s -> %s error: %v",
			prevOp, op, err)
		return err
	}

	return nil
}

// sometimes mesos executor emit consecutive events TASKRUNNING and TASKFAILED if without healthy check,
// which will cause the scheduler treats the task launching as succeed, actually it does NOT.
// we use this to make sure the previous app is ready within compose launching
func (r *Server) ensureAppReady(appId string, maxWait time.Duration) error {
	var (
		app *types.Application
		err error
	)

	for goesby := int64(0); goesby <= int64(maxWait); goesby += int64(time.Second) {
		time.Sleep(time.Second)
		if app, err = r.db.GetApp(appId); err != nil {
			continue
		}

		if app.TasksStatus["TASK_RUNNING"] == app.TaskCount {
			return nil
		}
	}

	if err != nil {
		return err
	}

	return fmt.Errorf("app %s not ready, only %d/%d tasks running",
		appId, app.TasksStatus["TASK_RUNNING"], app.TaskCount)
}

func (r *Server) wrapCompose(cmp *types.Compose) (*types.ComposeWrapper, error) {
	wrapper := &types.ComposeWrapper{Compose: cmp}

	apps, err := r.db.ListApps()
	if err != nil {
		return nil, err
	}

	for _, app := range apps {
		if app.Name+"."+cmp.DisplayName == app.ID {
			wrapper.Apps = append(wrapper.Apps, app)
		}
	}
	return wrapper, nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/korfuri/goref"

	"fmt"
	"log"
)

func main() {
	m := goref.NewPackageGraph()
	m.LoadProgram("github.com/korfuri/codesearch", "main.go")

	fmt.Printf("%d packages in the graph\n", len(m.Packages))
	fmt.Printf("%d files in the graph\n", len(m.Files))

	fmt.Printf("Packages that depend on `fmt`:\n")
	for d, _ := range m.Packages["fmt"].Dependents {
		fmt.Printf("   - %s\n", d)
	}

	fmt.Printf("Packages that `goref` depends on:\n")
	for d, _ := range m.Packages["github.com/korfuri/goref"].Dependencies {
		fmt.Printf("   - %s\n", d)
	}

	fmt.Printf("Package `goref` has these files:\n")
	for d, _ := range m.Packages["github.com/korfuri/goref"].Files {
		fmt.Printf("   - %s\n", d)
	}

	fmt.Printf("Package `fmt` has these files:\n")
	for d, _ := range m.Packages["fmt"].Files {
		fmt.Printf("   - %s\n", d)
	}

	fmt.Printf("Here are the uses of objects in `log`:\n")
	for pos, ref := range m.Packages["log"].InRefs {
		fmt.Printf("   - %s %s\n", pos, ref)
	}
}

func unused() interface{} {
	b := log.Logger{}
	log.Print(b)
	return log.Fatalf
}
<commit_msg>Add some time/memory tracing to main() for development purposes.<commit_after>package main

import (
	"github.com/dustin/go-humanize"
	"github.com/korfuri/goref"

	"fmt"
	"log"
	"runtime"
	"time"
)

func reportMemory() {
	var mem runtime.MemStats
	runtime.ReadMemStats(&mem)
	log.Printf("Allocated memory: %s\n", humanize.Bytes(mem.Alloc))
	log.Printf("Total allocated memory: %s\n", humanize.Bytes(mem.TotalAlloc))
	log.Printf("Heap allocated memory: %s\n", humanize.Bytes(mem.HeapAlloc))
	log.Printf("System heap allocated memory: %s\n", humanize.Bytes(mem.HeapSys))
}

func main() {
	reportMemory()

	start := time.Now()

	m := goref.NewPackageGraph()
	m.LoadProgram("github.com/korfuri/goref/main", "main.go")

	fmt.Printf("Loading took %s\n", time.Since(start))

	reportMemory()

	fmt.Printf("%d packages in the graph\n", len(m.Packages))
	fmt.Printf("%d files in the graph\n", len(m.Files))

	loadingDone := time.Now()

	fmt.Printf("Packages that depend on `fmt`:\n")
	for d, _ := range m.Packages["fmt"].Dependents {
		fmt.Printf("   - %s\n", d)
	}

	fmt.Printf("Packages that `goref` depends on:\n")
	for d, _ := range m.Packages["github.com/korfuri/goref"].Dependencies {
		fmt.Printf("   - %s\n", d)
	}

	fmt.Printf("Package `goref` has these files:\n")
	for d, _ := range m.Packages["github.com/korfuri/goref"].Files {
		fmt.Printf("   - %s\n", d)
	}

	fmt.Printf("Package `fmt` has these files:\n")
	for d, _ := range m.Packages["fmt"].Files {
		fmt.Printf("   - %s\n", d)
	}

	fmt.Printf("Here are the uses of objects in `goref`:\n")
	for pos, ref := range m.Packages["github.com/korfuri/goref"].InRefs {
		fmt.Printf("   - %s %s\n", pos, ref)
	}

	fmt.Printf("Here is where `goref`.`InRefs` is used:\n")
	for pos, ref := range m.Packages["github.com/korfuri/goref"].InRefs {
		if ref.Ident == "InRefs" {
			fmt.Printf("   - %s\n", pos)
		}
	}

	fmt.Printf("Displaying took %s (total runtime: %s)\n", time.Since(loadingDone), time.Since(start))
}

func unused() interface{} {
	b := log.Logger{}
	log.Print(b)
	return log.Fatalf
}
<|endoftext|>


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

package benchmark
<|endoftext|>


<commit_before>package main

import (
	//"github.com/minio/minio-go"
	"github.com/stretchr/testify/assert"
	"os"
	"testing"
)

func TestGetConfig(t *testing.T) {
	// parameters are passed as:
	// binary Command Pwd [CmdParams ...] Bucket AccessKey
	testData := []struct {
		input          []string
		inputSecretKey string
		output         params
	}{
		{
			input: []string{
				"null",
				"comMand",
				"PWD",
				"cmdParams",
				"bukkit",
				"aCceSsKey"},
			inputSecretKey: "seKreTkEy",
			output: params{
				accessKey: "aCceSsKey",
				secretKey: "seKreTkEy",
				command:   "comMand",
				pwd:       "PWD",
				bucket:    "bukkit",
				cmdParams: []string{
					"cmdParams",
				},
			},
		}, {
			input: []string{
				"",
				"comMand",
				"PWD",
				"cmdParams",
				"secondParam",
				"bukkit",
				"aCceSsKey"},
			inputSecretKey: "seKreTkEy",
			output: params{
				accessKey: "aCceSsKey",
				secretKey: "seKreTkEy",
				command:   "comMand",
				pwd:       "PWD",
				bucket:    "bukkit",
				cmdParams: []string{
					"cmdParams",
					"secondParam",
				},
			},
		},
	}

	for _, test := range testData {
		os.Setenv("PASSWORD", test.inputSecretKey)
		config := getConfig(test.input)
		assert.Equal(t, test.output, config)
	}
}

func TestGetClient(t *testing.T) {
	config := params{
		accessKey: os.Getenv("TESTUSER"),
		secretKey: os.Getenv("TESTPASS"),
		bucket:    os.Getenv("BUCKET"),
	}
	if config.accessKey != "" && config.secretKey != "" && config.bucket != ""{
		_, err := getClient(config)
		assert.Nil(t, err, "%v", err)
	}
}

func TestExpandPath(t *testing.T) {
	testData := []struct {
		pwd      string
		path     string
		expected string
	}{
		{
			pwd:      "/path/to/file",
			path:     "filename.txt",
			expected: "/path/to/file/filename.txt",
		}, {
			pwd:      "path/to/file",
			path:     "filename.txt",
			expected: "/path/to/file/filename.txt",
		}, {
			pwd:      "/path/to/file/",
			path:     "filename.txt",
			expected: "/path/to/file/filename.txt",
		}, {
			pwd:      "/path/to/file/",
			path:     "/full/path/filename.txt",
			expected: "/full/path/filename.txt",
		}, {
			pwd:      "/path/to/file",
			path:     "filename.txt/",
			expected: "/path/to/file/filename.txt",
		}, {
			pwd:      "/path///to/file",
			path:     "filename.txt",
			expected: "/path/to/file/filename.txt",
		}, {
			pwd:      "/path/./to/file",
			path:     "filename.txt",
			expected: "/path/to/file/filename.txt",
		}, {
			pwd:      "/path/to/file",
			path:     ".",
			expected: "/path/to/file",
		}, {
			pwd:      "/path/to/file",
			path:     "",
			expected: "/path/to/file",
		}, {
			pwd:      "/path/../to/file",
			path:     "filename.txt",
			expected: "/to/file/filename.txt",
		}, {
			pwd:      "/../path/to/file",
			path:     "filename.txt",
			expected: "/path/to/file/filename.txt",
		}, {
			pwd:      "",
			path:     "/",
			expected: "/",
		},
	}

	var result string
	for id, test := range testData {
		result = expandPath(test.pwd, test.path)
		assert.Equal(t, test.expected, result, "test %d pwd [%q] path [%q] - did not match", id, test.pwd, test.path)
	}
}

// func TestChdir(t *testing.T) {
// 	client, _ := minio.New(endpoint, os.Getenv("accessKey"), os.Getenv("testpass"), false)
// 	r, w := io.Pipe()

// 	go chdir(client, params{cmdParams: []string{"/test/folder/within/bucket"}}, w)
// 	r.Read()

// }

// func TestFileContentType(t *testing.T) {

// }
<commit_msg>added a test to hit the last bit of coverage of `expandPath`<commit_after>package main

import (
	//"github.com/minio/minio-go"
	"github.com/stretchr/testify/assert"
	"os"
	"testing"
)

func TestGetConfig(t *testing.T) {
	// parameters are passed as:
	// binary Command Pwd [CmdParams ...] Bucket AccessKey
	testData := []struct {
		input          []string
		inputSecretKey string
		output         params
	}{
		{
			input: []string{
				"null",
				"comMand",
				"PWD",
				"cmdParams",
				"bukkit",
				"aCceSsKey"},
			inputSecretKey: "seKreTkEy",
			output: params{
				accessKey: "aCceSsKey",
				secretKey: "seKreTkEy",
				command:   "comMand",
				pwd:       "PWD",
				bucket:    "bukkit",
				cmdParams: []string{
					"cmdParams",
				},
			},
		}, {
			input: []string{
				"",
				"comMand",
				"PWD",
				"cmdParams",
				"secondParam",
				"bukkit",
				"aCceSsKey"},
			inputSecretKey: "seKreTkEy",
			output: params{
				accessKey: "aCceSsKey",
				secretKey: "seKreTkEy",
				command:   "comMand",
				pwd:       "PWD",
				bucket:    "bukkit",
				cmdParams: []string{
					"cmdParams",
					"secondParam",
				},
			},
		},
	}

	for _, test := range testData {
		os.Setenv("PASSWORD", test.inputSecretKey)
		config := getConfig(test.input)
		assert.Equal(t, test.output, config)
	}
}

func TestGetClient(t *testing.T) {
	config := params{
		accessKey: os.Getenv("TESTUSER"),
		secretKey: os.Getenv("TESTPASS"),
		bucket:    os.Getenv("BUCKET"),
	}
	if config.accessKey != "" && config.secretKey != "" && config.bucket != ""{
		_, err := getClient(config)
		assert.Nil(t, err, "%v", err)
	}
}

func TestExpandPath(t *testing.T) {
	testData := []struct {
		pwd      string
		path     string
		expected string
	}{
		{
			pwd:      "/path/to/file",
			path:     "filename.txt",
			expected: "/path/to/file/filename.txt",
		}, {
			pwd:      "path/to/file",
			path:     "filename.txt",
			expected: "/path/to/file/filename.txt",
		}, {
			pwd:      "/path/to/file/",
			path:     "filename.txt",
			expected: "/path/to/file/filename.txt",
		}, {
			pwd:      "/path/to/file/",
			path:     "/full/path/filename.txt",
			expected: "/full/path/filename.txt",
		}, {
			pwd:      "/path/to/file",
			path:     "filename.txt/",
			expected: "/path/to/file/filename.txt",
		}, {
			pwd:      "/path///to/file",
			path:     "filename.txt",
			expected: "/path/to/file/filename.txt",
		}, {
			pwd:      "/path/./to/file",
			path:     "filename.txt",
			expected: "/path/to/file/filename.txt",
		}, {
			pwd:      "/path/to/file",
			path:     ".",
			expected: "/path/to/file",
		}, {
			pwd:      "/path/to/file",
			path:     "",
			expected: "/path/to/file",
		}, {
			pwd:      "/path/../to/file",
			path:     "filename.txt",
			expected: "/to/file/filename.txt",
		}, {
			pwd:      "/../path/to/file",
			path:     "filename.txt",
			expected: "/path/to/file/filename.txt",
		}, {
			pwd:      "/path/to/file",
			path:     "..",
			expected: "/path/to",
		}, {
			pwd:      "",
			path:     "/",
			expected: "/",
		},
	}

	var result string
	for id, test := range testData {
		result = expandPath(test.pwd, test.path)
		assert.Equal(t, test.expected, result, "test %d pwd [%q] path [%q] - did not match", id, test.pwd, test.path)
	}
}

// func TestChdir(t *testing.T) {
// 	client, _ := minio.New(endpoint, os.Getenv("accessKey"), os.Getenv("testpass"), false)
// 	r, w := io.Pipe()

// 	go chdir(client, params{cmdParams: []string{"/test/folder/within/bucket"}}, w)
// 	r.Read()

// }

// func TestFileContentType(t *testing.T) {

// }
<|endoftext|>


<commit_before>package main

import (
	"encoding/xml"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

func TestStraingToVoiceMailResponse(t *testing.T) {
	recorder := httptest.NewRecorder()
	StraightToVoiceMail(recorder, nil)
	if status := recorder.Code; status != http.StatusOK {
		t.Errorf("returned wrong status code: got %v expected %v", status, http.StatusOK)
	}
	if err := checkWellFormetXML(recorder.Body.String()); err != nil {
		t.Errorf("body need to be valid XML: %v", err)
	}
}

func checkWellFormetXML(s string) error {
	d := xml.NewDecoder(strings.NewReader(s))
	t, err := d.Token()
	if err != nil {
		return err
	}

	v, ok := t.(xml.ProcInst)
	if !ok || v.Target != "xml" || !strings.Contains(string(v.Inst), "version=\"1.0\"") {
		return fmt.Errorf("No XML header detected with version 1.0 at the start")
	}

	return nil
}
<commit_msg>Correct typo in test method name<commit_after>package main

import (
	"encoding/xml"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

func TestStraingToVoiceMailResponse(t *testing.T) {
	recorder := httptest.NewRecorder()
	StraightToVoiceMail(recorder, nil)
	if status := recorder.Code; status != http.StatusOK {
		t.Errorf("returned wrong status code: got %v expected %v", status, http.StatusOK)
	}
	if err := checkWellFormedXML(recorder.Body.String()); err != nil {
		t.Errorf("body need to be valid XML: %v", err)
	}
}

func checkWellFormedXML(s string) error {
	d := xml.NewDecoder(strings.NewReader(s))
	t, err := d.Token()
	if err != nil {
		return err
	}

	v, ok := t.(xml.ProcInst)
	if !ok || v.Target != "xml" || !strings.Contains(string(v.Inst), "version=\"1.0\"") {
		return fmt.Errorf("No XML header detected with version 1.0 at the start")
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/julienschmidt/httprouter"
	"github.com/stretchr/testify/assert"
)

// func TestMain(m *testing.M) {
//     a = main.App{}
//     a.Initialize(
//         os.Getenv("TEST_DB_USERNAME"),
//         os.Getenv("TEST_DB_PASSWORD"),
//         os.Getenv("TEST_DB_NAME"))
//
//     ensureTableExists()
//
//     code := m.Run()
//
//     clearTable()
//
//     os.Exit(codunc TestMain(m *testing.M) {
//     a = main.App{}
//     a.Initialize(
//         os.Getenv("TEST_DB_USERNAME"),
//         os.Getenv("TEST_DB_PASSWORD"),
//         os.Getenv("TEST_DB_NAME"))
//
//     ensureTableExists()
//
//     code := m.Run()
//
//     clearTable()
//
//     os.Exit(code)
// }e)
// }

func TestSimple(t *testing.T) {
	req, _ := http.NewRequest("GET", "/doesntmatter:(", nil)
	response := httptest.NewRecorder()

	oi := []httprouter.Param{httprouter.Param{Key: "file", Value: "jade"}}

	FetcherFunc(response, req, oi)

	assert.Equal(t, response.Body.String(), "Hello jade\n", "they should be equal")
}

func TestFromOutside(t *testing.T) {
	ts := httptest.NewServer(GetApp())
	defer ts.Close()

	res, err := http.Get(fmt.Sprintf("%s/%s", ts.URL, "jade"))
	if err != nil {
		log.Fatal(err)
	}

	greeting, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		log.Fatal(err)
	}

	assert.Equal(t, 200, res.StatusCode, "status code should be 200")
	assert.Equal(t, string(greeting), "Hello jade\n", "they should be equal")
}

func TestNoUrlReturnsBadRequest(t *testing.T) {
	ts := httptest.NewServer(GetApp())
	defer ts.Close()

	res, err := http.Get(fmt.Sprintf("%s/%s", ts.URL, ""))
	if err != nil {
		log.Fatal(err)
	}

	body, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		log.Fatal(err)
	}

	assert.Equal(t, 400, res.StatusCode, "status code should be 400")
	assert.Equal(t, string(body), "No image url provided\n", "they should be equal")
}
<commit_msg>Use tests setup/teardown and use dummy images server<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/suite"
)

type FakeExternalServerTestSuite struct {
	suite.Suite
	fakeServer    *http.Server
	subjectServer *httptest.Server
}

func (suite *FakeExternalServerTestSuite) SetupSuite() {
	suite.fakeServer = &http.Server{Addr: ":8081", Handler: http.FileServer(http.Dir("."))}
	go func() {
		suite.fakeServer.ListenAndServe()
		// http.ListenAndServe(":8081", suite.fakeServer)
	}()
}

func TestFakeExternalServerTestSuite(t *testing.T) {
	suite.Run(t, new(FakeExternalServerTestSuite))
}

func (suite *FakeExternalServerTestSuite) SetupTest() {
	suite.subjectServer = httptest.NewServer(GetApp())
	// defer suite.subjectServer.Close()
}

func (suite *FakeExternalServerTestSuite) TestExample() {
	res, err := http.Get(fmt.Sprintf("%s/%s",
		suite.subjectServer.URL, "http://localhost:8081/oi.txt"))
	if err != nil {
		log.Fatal(err)
	}

	body, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		log.Fatal(err)
	}

	suite.Equal(404, res.StatusCode, "status code should be 404")
	suite.Equal(string(body), "\n", "they should be equal")
}

func (suite *FakeExternalServerTestSuite) TearDownTest() {
	suite.subjectServer.Close()
}

func (suite *FakeExternalServerTestSuite) TearDownSuite() {
	if err := suite.fakeServer.Shutdown(nil); err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package main

// This is an integration Test which runs against real directories and
// compares the transformation against golden directories, containing
// the expected content and structure.
//
// This was heavily inspired by oracle_test.go, thanks.

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

var updateFlag = flag.Bool("update", false, "Update golden directories")

func TestMain(t *testing.T) {
	cases := []struct {
		referenceDir string
		dryRun       bool
	}{
		{"testdata/t1", false},
		{"testdata/t2", false},
		{"testdata/t1", true},
	}
	for _, c := range cases {
		referenceDir := c.referenceDir
		workingDir := referenceDir + ".got"
		goldenDir := referenceDir + ".golden"

		os.RemoveAll(workingDir)
		err := copyDirectory(referenceDir, workingDir)
		if err != nil {
			panic(err)
		}

		run(workingDir, c.dryRun, false)

		// compare directory.got with directory.golden
		compareDir := goldenDir
		if c.dryRun {
			compareDir = referenceDir
		}
		cmd := exec.Command("diff", "-ru", workingDir, compareDir)
		cmd.Stdout = new(bytes.Buffer)
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			t.Errorf("Test for %s (dryRun: %v) failed: %s.\n%s\n",
				workingDir, c.dryRun, err, cmd.Stdout)

			if *updateFlag {
				t.Logf("Updating golden: %s...", goldenDir)
				os.RemoveAll(goldenDir)
				err := copyDirectory(workingDir, goldenDir)
				if err != nil {
					t.Errorf("Update failed: %s", err)
				}
			}
		}
	}
}

func TestPermissionProblems(t *testing.T) {
	stdout := run("testdata/t3", false, true)
	os.Chmod("testdata/t3/foo-not-writable.txt", 0444)
	if !strings.Contains(stdout, "Could not write: testdata/t3/foo-not-writable") {
		t.Errorf("Missing [Could not write...] message in(%s)", stdout)
	}
	if !strings.Contains(stdout, "Could not move: testdata/t3/foo-not-moveable") {
		t.Errorf("Missing [Could not open...] message in(%s)", stdout)
	}
	// fmt.Println(stdout)
}

func run(workingDir string, dryRun, verbose bool) string {
	var stdout bytes.Buffer
	program := Program{
		RootDirectory: workingDir,
		Search:        "foo",
		Replace:       "bar",
		Stdout:        &stdout,
		DryRun:        dryRun,
		Verbose:       verbose,
	}
	err := program.Execute()
	if err != nil {
		panic(fmt.Errorf("Program-Execution error(%s)", err))
	}

	return stdout.String()
}

func copyDirectory(source, target string) (err error) {
	err = filepath.Walk(source, func(path string, f os.FileInfo, err error) (werr error) {
		newPath := strings.Replace(path, source, target, 1)
		if f.IsDir() {
			if werr = os.Mkdir(newPath, f.Mode()); werr != nil {
				return
			}
		} else {
			if werr = copyFile(path, newPath); werr != nil {
				return
			}
		}
		return
	})
	if err != nil {
		return fmt.Errorf("Could not copy directory(%s)", err)
	}
	return
}

// see: http://stackoverflow.com/a/21067803
func copyFile(source, target string) (err error) {
	in, err := os.Open(source)
	if err != nil {
		return
	}
	defer in.Close()
	out, err := os.Create(target)
	if err != nil {
		return
	}
	defer func() {
		cerr := out.Close()
		if err == nil {
			err = cerr
		}
	}()

	if _, err = io.Copy(out, in); err != nil {
		return
	}
	err = out.Sync()

	fi, err := in.Stat()
	if err != nil {
		return
	}
	if err = os.Chmod(target, fi.Mode()); err != nil {
		return
	}

	return
}
<commit_msg>try to fix TestPermissionProblems on travis ci<commit_after>package main

// This is an integration Test which runs against real directories and
// compares the transformation against golden directories, containing
// the expected content and structure.
//
// This was heavily inspired by oracle_test.go, thanks.

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

var updateFlag = flag.Bool("update", false, "Update golden directories")

func TestMain(t *testing.T) {
	cases := []struct {
		referenceDir string
		dryRun       bool
	}{
		{"testdata/t1", false},
		{"testdata/t2", false},
		{"testdata/t1", true},
	}
	for _, c := range cases {
		referenceDir := c.referenceDir
		workingDir := referenceDir + ".got"
		goldenDir := referenceDir + ".golden"

		os.RemoveAll(workingDir)
		err := copyDirectory(referenceDir, workingDir)
		if err != nil {
			panic(err)
		}

		run(workingDir, c.dryRun, false)

		// compare directory.got with directory.golden
		compareDir := goldenDir
		if c.dryRun {
			compareDir = referenceDir
		}
		cmd := exec.Command("diff", "-ru", workingDir, compareDir)
		cmd.Stdout = new(bytes.Buffer)
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			t.Errorf("Test for %s (dryRun: %v) failed: %s.\n%s\n",
				workingDir, c.dryRun, err, cmd.Stdout)

			if *updateFlag {
				t.Logf("Updating golden: %s...", goldenDir)
				os.RemoveAll(goldenDir)
				err := copyDirectory(workingDir, goldenDir)
				if err != nil {
					t.Errorf("Update failed: %s", err)
				}
			}
		}
	}
}

func TestPermissionProblems(t *testing.T) {
	stdout := run("testdata/t3", false, true)
	err := os.Chmod("testdata/t3/foo-not-writable.txt", 0444)
	if err != nil {
		panic(err)
	}
	if !strings.Contains(stdout, "Could not write: testdata/t3/foo-not-writable") {
		t.Errorf("Missing [Could not write...] message in(%s)", stdout)
	}
	if !strings.Contains(stdout, "Could not move: testdata/t3/foo-not-moveable") {
		t.Errorf("Missing [Could not open...] message in(%s)", stdout)
	}
	// fmt.Println(stdout)
}

func run(workingDir string, dryRun, verbose bool) string {
	var stdout bytes.Buffer
	program := Program{
		RootDirectory: workingDir,
		Search:        "foo",
		Replace:       "bar",
		Stdout:        &stdout,
		DryRun:        dryRun,
		Verbose:       verbose,
	}
	err := program.Execute()
	if err != nil {
		panic(fmt.Errorf("Program-Execution error(%s)", err))
	}

	return stdout.String()
}

func copyDirectory(source, target string) (err error) {
	err = filepath.Walk(source, func(path string, f os.FileInfo, err error) (werr error) {
		newPath := strings.Replace(path, source, target, 1)
		if f.IsDir() {
			if werr = os.Mkdir(newPath, f.Mode()); werr != nil {
				return
			}
		} else {
			if werr = copyFile(path, newPath); werr != nil {
				return
			}
		}
		return
	})
	if err != nil {
		return fmt.Errorf("Could not copy directory(%s)", err)
	}
	return
}

// see: http://stackoverflow.com/a/21067803
func copyFile(source, target string) (err error) {
	in, err := os.Open(source)
	if err != nil {
		return
	}
	defer in.Close()
	out, err := os.Create(target)
	if err != nil {
		return
	}
	defer func() {
		cerr := out.Close()
		if err == nil {
			err = cerr
		}
	}()

	if _, err = io.Copy(out, in); err != nil {
		return
	}
	err = out.Sync()

	fi, err := in.Stat()
	if err != nil {
		return
	}
	if err = os.Chmod(target, fi.Mode()); err != nil {
		return
	}

	return
}
<|endoftext|>


<commit_before>package blog

import (
	"fmt"
	"github.com/hoisie/web"
	"github.com/jmoiron/monet/app"
	"github.com/jmoiron/monet/db"
	"github.com/jmoiron/monet/template"
	"github.com/jmoiron/syndicate"
	"labix.org/v2/mgo/bson"
	"time"
)

var base = template.Base{Path: "base.mandira"}

// Attach the blog app frontend
func Attach(url string) {
	web.Get(url+"blog/page/(\\d+)", blogPage)
	web.Get(url+"blog/([^/]+)/", blogDetail)
	web.Get(url+"blog/", blogIndex)
	web.Get(url+"stream/page/(\\d+)", streamPage)
	web.Get(url+"stream/", streamIndex)
	web.Get(url+"blog/rss", rss)
	web.Get(url+"blog/atom", atom)
}

// Render the post, using the cached ContentRendered if available, or generating
// and re-saving it to the database if not
func RenderPost(post *Post) string {
	if len(post.ContentRendered) == 0 {
		db.Upsert(post)
	}
	return template.Render("blog/post.mandira", post)
}

// A Flatpage view.  Attach it via web.Get wherever you want flatpages to be available
func Flatpage(ctx *web.Context, url string) string {
	p := GetPage(url)
	if p == nil {
		ctx.Abort(404, "Page not found")
		return ""
	}

	return template.Render("base.mandira", M{
		"body":        p.ContentRendered,
		"title":       "jmoiron.net",
		"description": "Blog and assorted media from Jason Moiron.",
	})
}

func Index(s string) string {
	var post *Post
	var entry *Entry
	var posts []Post
	var entries []*Entry

	err := db.Latest(post, M{"published": 1}).Limit(7).All(&posts)
	if err != nil {
		fmt.Println(err)
	}
	err = db.Latest(entry, nil).Limit(4).All(&entries)
	if err != nil {
		fmt.Println(err)
	}

	post = &posts[0]
	return base.Render("index.mandira", M{
		"Post":        RenderPost(post),
		"Posts":       posts[1:],
		"Entries":     entries,
		"title":       "jmoiron.net",
		"description": post.Summary})
}

func blogIndex(ctx *web.Context) string {
	return blogPage(ctx, "1")
}

func blogPage(ctx *web.Context, page string) string {
	pn := app.PageNumber(page)
	perPage := 15
	paginator := app.NewPaginator(pn, perPage)
	paginator.Link = "/blog/page/"

	var post *Post
	var posts []Post
	// do a search, if required, of title and content
	var err error
	var numObjects int

	if len(ctx.Params["Search"]) > 0 {
		term := M{"$regex": ctx.Params["Search"]}
		search := M{"published": 1, "$or": []M{M{"title": term}, M{"content": term}}}
		err = db.Latest(post, search).Skip(paginator.Skip).Limit(perPage).All(&posts)
		numObjects, _ = db.Latest(post, search).Count()
	} else {
		err = db.Latest(post, M{"published": 1}).Skip(paginator.Skip).
			Limit(perPage).Iter().All(&posts)
		numObjects, _ = db.Find(post, M{"published": 1}).Count()
	}

	if err != nil {
		fmt.Println(err)
	}

	return base.Render("blog/index.mandira", M{
		"Posts": posts, "Pagination": paginator.Render(numObjects)}, ctx.Params)
}

func _createFeed() *syndicate.Feed {
	var posts []Post
	var post *Post

	err := db.Latest(post, M{"published": 1}).Limit(10).Iter().All(&posts)
	if err != nil {
		fmt.Println(err)
		return nil
	}

	feed := &syndicate.Feed{
		Title:       "jmoiron.net blog",
		Link:        &syndicate.Link{Href: "http://jmoiron.net"},
		Description: "the blog of Jason Moiron, all thoughts his own",
		Author:      &syndicate.Author{"Jason Moiron", "jmoiron@jmoiron.net"},
		Updated:     time.Now(),
	}

	for _, post := range posts {
		feed.Add(&syndicate.Item{
			Title:       post.Title,
			Link:        &syndicate.Link{Href: "http://jmoiron.net/blog/" + post.Slug + "/"},
			Description: post.ContentRendered,
			Created:     time.Unix(int64(post.Timestamp), 0),
		})
	}
	return feed
}

func atom(ctx *web.Context) string {
	feed := _createFeed()
	ctx.Header().Set("Content-Type", "application/xml")
	if feed == nil {
		return "<!-- error -->"
	}
	text, err := feed.ToAtom()
	if err != nil {
		fmt.Println(err)
		return "<!-- error -->"
	}
	return text
}

func rss(ctx *web.Context) string {
	feed := _createFeed()
	ctx.Header().Set("Content-Type", "application/xml")
	if feed == nil {
		return "<!-- error -->"
	}
	text, err := feed.ToRss()
	if err != nil {
		fmt.Println(err)
		return "<!-- error -->"
	}
	return text
}

func blogDetail(ctx *web.Context, slug string) string {
	var post = new(Post)
	err := db.Find(post, M{"slug": slug}).One(&post)
	if err != nil {
		fmt.Println(err)
		ctx.Abort(404, "Page not found")
		return ""
	}

	return template.Render("base.mandira", M{
		"body":        RenderPost(post),
		"title":       post.Title,
		"description": post.Summary})
}

func streamIndex(ctx *web.Context) string {
	return streamPage(ctx, "1")
}

func streamPage(ctx *web.Context, page string) string {
	num := app.PageNumber(page)
	perPage := 25
	paginator := app.NewPaginator(num, perPage)
	paginator.Link = "/stream/page/"

	var entry *Entry
	var entries []*Entry

	// do a search, if required, of title and content
	var err error
	var numObjects int

	if len(ctx.Params["Search"]) > 0 {
		re := new(bson.RegEx)
		re.Pattern = ctx.Params["Search"]
		re.Options = "i"
		term := M{"$regex": re}
		search := M{"summaryrendered": term}
		//search := M{"$or": []M{M{"title": term}, M{"summaryrendered": term}}}
		err = db.Latest(entry, search).Skip(paginator.Skip).Limit(perPage).All(&entries)
		numObjects, _ = db.Latest(entry, search).Count()
	} else {
		err = db.Latest(entry, nil).Skip(paginator.Skip).Limit(perPage).Iter().All(&entries)
		numObjects, _ = db.Cursor(entry).Count()
	}

	if err != nil {
		fmt.Println(err)
	}

	return base.Render("blog/stream/index.mandira", M{
		"Entries":    entries,
		"Pagination": paginator.Render(numObjects),
		"title":      "Lifestream"}, ctx.Params)
}
<commit_msg>syndicate -> feeds<commit_after>package blog

import (
	"fmt"
	"github.com/gorilla/feeds"
	"github.com/hoisie/web"
	"github.com/jmoiron/monet/app"
	"github.com/jmoiron/monet/db"
	"github.com/jmoiron/monet/template"
	"labix.org/v2/mgo/bson"
	"time"
)

var base = template.Base{Path: "base.mandira"}

// Attach the blog app frontend
func Attach(url string) {
	web.Get(url+"blog/page/(\\d+)", blogPage)
	web.Get(url+"blog/([^/]+)/", blogDetail)
	web.Get(url+"blog/", blogIndex)
	web.Get(url+"stream/page/(\\d+)", streamPage)
	web.Get(url+"stream/", streamIndex)
	web.Get(url+"blog/rss", rss)
	web.Get(url+"blog/atom", atom)
}

// Render the post, using the cached ContentRendered if available, or generating
// and re-saving it to the database if not
func RenderPost(post *Post) string {
	if len(post.ContentRendered) == 0 {
		db.Upsert(post)
	}
	return template.Render("blog/post.mandira", post)
}

// A Flatpage view.  Attach it via web.Get wherever you want flatpages to be available
func Flatpage(ctx *web.Context, url string) string {
	p := GetPage(url)
	if p == nil {
		ctx.Abort(404, "Page not found")
		return ""
	}

	return template.Render("base.mandira", M{
		"body":        p.ContentRendered,
		"title":       "jmoiron.net",
		"description": "Blog and assorted media from Jason Moiron.",
	})
}

func Index(s string) string {
	var post *Post
	var entry *Entry
	var posts []Post
	var entries []*Entry

	err := db.Latest(post, M{"published": 1}).Limit(7).All(&posts)
	if err != nil {
		fmt.Println(err)
	}
	err = db.Latest(entry, nil).Limit(4).All(&entries)
	if err != nil {
		fmt.Println(err)
	}

	post = &posts[0]
	return base.Render("index.mandira", M{
		"Post":        RenderPost(post),
		"Posts":       posts[1:],
		"Entries":     entries,
		"title":       "jmoiron.net",
		"description": post.Summary})
}

func blogIndex(ctx *web.Context) string {
	return blogPage(ctx, "1")
}

func blogPage(ctx *web.Context, page string) string {
	pn := app.PageNumber(page)
	perPage := 15
	paginator := app.NewPaginator(pn, perPage)
	paginator.Link = "/blog/page/"

	var post *Post
	var posts []Post
	// do a search, if required, of title and content
	var err error
	var numObjects int

	if len(ctx.Params["Search"]) > 0 {
		term := M{"$regex": ctx.Params["Search"]}
		search := M{"published": 1, "$or": []M{M{"title": term}, M{"content": term}}}
		err = db.Latest(post, search).Skip(paginator.Skip).Limit(perPage).All(&posts)
		numObjects, _ = db.Latest(post, search).Count()
	} else {
		err = db.Latest(post, M{"published": 1}).Skip(paginator.Skip).
			Limit(perPage).Iter().All(&posts)
		numObjects, _ = db.Find(post, M{"published": 1}).Count()
	}

	if err != nil {
		fmt.Println(err)
	}

	return base.Render("blog/index.mandira", M{
		"Posts": posts, "Pagination": paginator.Render(numObjects)}, ctx.Params)
}

func _createFeed() *feeds.Feed {
	var posts []Post
	var post *Post

	err := db.Latest(post, M{"published": 1}).Limit(10).Iter().All(&posts)
	if err != nil {
		fmt.Println(err)
		return nil
	}

	feed := &feeds.Feed{
		Title:       "jmoiron.net blog",
		Link:        &feeds.Link{Href: "http://jmoiron.net"},
		Description: "the blog of Jason Moiron, all thoughts his own",
		Author:      &feeds.Author{"Jason Moiron", "jmoiron@jmoiron.net"},
		Updated:     time.Now(),
	}

	for _, post := range posts {
		feed.Add(&feeds.Item{
			Title:       post.Title,
			Link:        &feeds.Link{Href: "http://jmoiron.net/blog/" + post.Slug + "/"},
			Description: post.ContentRendered,
			Created:     time.Unix(int64(post.Timestamp), 0),
		})
	}
	return feed
}

func atom(ctx *web.Context) string {
	feed := _createFeed()
	ctx.Header().Set("Content-Type", "application/xml")
	if feed == nil {
		return "<!-- error -->"
	}
	text, err := feed.ToAtom()
	if err != nil {
		fmt.Println(err)
		return "<!-- error -->"
	}
	return text
}

func rss(ctx *web.Context) string {
	feed := _createFeed()
	ctx.Header().Set("Content-Type", "application/xml")
	if feed == nil {
		return "<!-- error -->"
	}
	text, err := feed.ToRss()
	if err != nil {
		fmt.Println(err)
		return "<!-- error -->"
	}
	return text
}

func blogDetail(ctx *web.Context, slug string) string {
	var post = new(Post)
	err := db.Find(post, M{"slug": slug}).One(&post)
	if err != nil {
		fmt.Println(err)
		ctx.Abort(404, "Page not found")
		return ""
	}

	return template.Render("base.mandira", M{
		"body":        RenderPost(post),
		"title":       post.Title,
		"description": post.Summary})
}

func streamIndex(ctx *web.Context) string {
	return streamPage(ctx, "1")
}

func streamPage(ctx *web.Context, page string) string {
	num := app.PageNumber(page)
	perPage := 25
	paginator := app.NewPaginator(num, perPage)
	paginator.Link = "/stream/page/"

	var entry *Entry
	var entries []*Entry

	// do a search, if required, of title and content
	var err error
	var numObjects int

	if len(ctx.Params["Search"]) > 0 {
		re := new(bson.RegEx)
		re.Pattern = ctx.Params["Search"]
		re.Options = "i"
		term := M{"$regex": re}
		search := M{"summaryrendered": term}
		//search := M{"$or": []M{M{"title": term}, M{"summaryrendered": term}}}
		err = db.Latest(entry, search).Skip(paginator.Skip).Limit(perPage).All(&entries)
		numObjects, _ = db.Latest(entry, search).Count()
	} else {
		err = db.Latest(entry, nil).Skip(paginator.Skip).Limit(perPage).Iter().All(&entries)
		numObjects, _ = db.Cursor(entry).Count()
	}

	if err != nil {
		fmt.Println(err)
	}

	return base.Render("blog/stream/index.mandira", M{
		"Entries":    entries,
		"Pagination": paginator.Render(numObjects),
		"title":      "Lifestream"}, ctx.Params)
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package dynamicinformer_test

import (
	"context"
	"testing"
	"time"

	"k8s.io/apimachinery/pkg/api/equality"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/diff"
	"k8s.io/client-go/dynamic/dynamicinformer"
	"k8s.io/client-go/dynamic/fake"
	"k8s.io/client-go/tools/cache"
)

func TestDynamicSharedInformerFactory(t *testing.T) {
	scenarios := []struct {
		name        string
		existingObj *unstructured.Unstructured
		gvr         schema.GroupVersionResource
		ns          string
		trigger     func(gvr schema.GroupVersionResource, ns string, fakeClient *fake.FakeDynamicClient, testObject *unstructured.Unstructured) *unstructured.Unstructured
		handler     func(rcvCh chan<- *unstructured.Unstructured) *cache.ResourceEventHandlerFuncs
	}{
		// scenario 1
		{
			name: "scenario 1: test if adding an object triggers AddFunc",
			ns:   "ns-foo",
			gvr:  schema.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "deployments"},
			trigger: func(gvr schema.GroupVersionResource, ns string, fakeClient *fake.FakeDynamicClient, _ *unstructured.Unstructured) *unstructured.Unstructured {
				testObject := newUnstructured("extensions/v1beta1", "Deployment", "ns-foo", "name-foo")
				createdObj, err := fakeClient.Resource(gvr).Namespace(ns).Create(context.TODO(), testObject, metav1.CreateOptions{})
				if err != nil {
					t.Error(err)
				}
				return createdObj
			},
			handler: func(rcvCh chan<- *unstructured.Unstructured) *cache.ResourceEventHandlerFuncs {
				return &cache.ResourceEventHandlerFuncs{
					AddFunc: func(obj interface{}) {
						rcvCh <- obj.(*unstructured.Unstructured)
					},
				}
			},
		},

		// scenario 2
		{
			name:        "scenario 2: tests if updating an object triggers UpdateFunc",
			ns:          "ns-foo",
			gvr:         schema.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "deployments"},
			existingObj: newUnstructured("extensions/v1beta1", "Deployment", "ns-foo", "name-foo"),
			trigger: func(gvr schema.GroupVersionResource, ns string, fakeClient *fake.FakeDynamicClient, testObject *unstructured.Unstructured) *unstructured.Unstructured {
				testObject.Object["spec"] = "updatedName"
				updatedObj, err := fakeClient.Resource(gvr).Namespace(ns).Update(context.TODO(), testObject, metav1.UpdateOptions{})
				if err != nil {
					t.Error(err)
				}
				return updatedObj
			},
			handler: func(rcvCh chan<- *unstructured.Unstructured) *cache.ResourceEventHandlerFuncs {
				return &cache.ResourceEventHandlerFuncs{
					UpdateFunc: func(old, updated interface{}) {
						rcvCh <- updated.(*unstructured.Unstructured)
					},
				}
			},
		},

		// scenario 3
		{
			name:        "scenario 3: test if deleting an object triggers DeleteFunc",
			ns:          "ns-foo",
			gvr:         schema.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "deployments"},
			existingObj: newUnstructured("extensions/v1beta1", "Deployment", "ns-foo", "name-foo"),
			trigger: func(gvr schema.GroupVersionResource, ns string, fakeClient *fake.FakeDynamicClient, testObject *unstructured.Unstructured) *unstructured.Unstructured {
				err := fakeClient.Resource(gvr).Namespace(ns).Delete(context.TODO(), testObject.GetName(), metav1.DeleteOptions{})
				if err != nil {
					t.Error(err)
				}
				return testObject
			},
			handler: func(rcvCh chan<- *unstructured.Unstructured) *cache.ResourceEventHandlerFuncs {
				return &cache.ResourceEventHandlerFuncs{
					DeleteFunc: func(obj interface{}) {
						rcvCh <- obj.(*unstructured.Unstructured)
					},
				}
			},
		},
	}

	for _, ts := range scenarios {
		t.Run(ts.name, func(t *testing.T) {
			// test data
			timeout := time.Duration(3 * time.Second)
			ctx, cancel := context.WithTimeout(context.Background(), timeout)
			defer cancel()
			scheme := runtime.NewScheme()
			informerReciveObjectCh := make(chan *unstructured.Unstructured, 1)
			objs := []runtime.Object{}
			if ts.existingObj != nil {
				objs = append(objs, ts.existingObj)
			}
			fakeClient := fake.NewSimpleDynamicClient(scheme, objs...)
			target := dynamicinformer.NewDynamicSharedInformerFactory(fakeClient, 0)

			// act
			informerListerForGvr := target.ForResource(ts.gvr)
			informerListerForGvr.Informer().AddEventHandler(ts.handler(informerReciveObjectCh))
			target.Start(ctx.Done())
			if synced := target.WaitForCacheSync(ctx.Done()); !synced[ts.gvr] {
				t.Errorf("informer for %s hasn't synced", ts.gvr)
			}

			testObject := ts.trigger(ts.gvr, ts.ns, fakeClient, ts.existingObj)
			select {
			case objFromInformer := <-informerReciveObjectCh:
				if !equality.Semantic.DeepEqual(testObject, objFromInformer) {
					t.Fatalf("%v", diff.ObjectDiff(testObject, objFromInformer))
				}
			case <-ctx.Done():
				t.Errorf("tested informer haven't received an object, waited %v", timeout)
			}
		})
	}
}

func newUnstructured(apiVersion, kind, namespace, name string) *unstructured.Unstructured {
	return &unstructured.Unstructured{
		Object: map[string]interface{}{
			"apiVersion": apiVersion,
			"kind":       kind,
			"metadata": map[string]interface{}{
				"namespace": namespace,
				"name":      name,
			},
			"spec": name,
		},
	}
}
<commit_msg>add tests for NewFilteredDynamicSharedInformerFactory namespace<commit_after>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package dynamicinformer_test

import (
	"context"
	"testing"
	"time"

	"k8s.io/apimachinery/pkg/api/equality"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/diff"
	"k8s.io/client-go/dynamic/dynamicinformer"
	"k8s.io/client-go/dynamic/fake"
	"k8s.io/client-go/tools/cache"
)

type triggerFunc func(gvr schema.GroupVersionResource, ns string, fakeClient *fake.FakeDynamicClient, testObject *unstructured.Unstructured) *unstructured.Unstructured

func triggerFactory(t *testing.T) triggerFunc {
	return func(gvr schema.GroupVersionResource, ns string, fakeClient *fake.FakeDynamicClient, _ *unstructured.Unstructured) *unstructured.Unstructured {
		testObject := newUnstructured("apps/v1", "Deployment", "ns-foo", "name-foo")
		createdObj, err := fakeClient.Resource(gvr).Namespace(ns).Create(context.TODO(), testObject, metav1.CreateOptions{})
		if err != nil {
			t.Error(err)
		}
		return createdObj
	}
}

func handler(rcvCh chan<- *unstructured.Unstructured) *cache.ResourceEventHandlerFuncs {
	return &cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			rcvCh <- obj.(*unstructured.Unstructured)
		},
	}
}

func TestFilteredDynamicSharedInformerFactory(t *testing.T) {
	scenarios := []struct {
		name        string
		existingObj *unstructured.Unstructured
		gvr         schema.GroupVersionResource
		informNS    string
		ns          string
		trigger     func(gvr schema.GroupVersionResource, ns string, fakeClient *fake.FakeDynamicClient, testObject *unstructured.Unstructured) *unstructured.Unstructured
		handler     func(rcvCh chan<- *unstructured.Unstructured) *cache.ResourceEventHandlerFuncs
	}{
		// scenario 1
		{
			name:     "scenario 1: test adding an object in different namespace should not trigger AddFunc",
			informNS: "ns-bar",
			ns:       "ns-foo",
			gvr:      schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
			trigger:  triggerFactory(t),
			handler:  handler,
		},
		// scenario 2
		{
			name:     "scenario 2: test adding an object should trigger AddFunc",
			informNS: "ns-foo",
			ns:       "ns-foo",
			gvr:      schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
			trigger:  triggerFactory(t),
			handler:  handler,
		},
	}

	for _, ts := range scenarios {
		t.Run(ts.name, func(t *testing.T) {
			// test data
			timeout := time.Duration(3 * time.Second)
			ctx, cancel := context.WithTimeout(context.Background(), timeout)
			defer cancel()
			scheme := runtime.NewScheme()
			informerReciveObjectCh := make(chan *unstructured.Unstructured, 1)
			objs := []runtime.Object{}
			if ts.existingObj != nil {
				objs = append(objs, ts.existingObj)
			}
			fakeClient := fake.NewSimpleDynamicClient(scheme, objs...)
			target := dynamicinformer.NewFilteredDynamicSharedInformerFactory(fakeClient, 0, ts.informNS, nil)

			// act
			informerListerForGvr := target.ForResource(ts.gvr)
			informerListerForGvr.Informer().AddEventHandler(ts.handler(informerReciveObjectCh))
			target.Start(ctx.Done())
			if synced := target.WaitForCacheSync(ctx.Done()); !synced[ts.gvr] {
				t.Errorf("informer for %s hasn't synced", ts.gvr)
			}

			testObject := ts.trigger(ts.gvr, ts.ns, fakeClient, ts.existingObj)
			select {
			case objFromInformer := <-informerReciveObjectCh:
				if ts.ns != ts.informNS {
					t.Errorf("informer received an object for namespace %s when watching namespace %s", ts.ns, ts.informNS)
				}
				if !equality.Semantic.DeepEqual(testObject, objFromInformer) {
					t.Fatalf("%v", diff.ObjectDiff(testObject, objFromInformer))
				}
			case <-ctx.Done():
				if ts.ns == ts.informNS {
					t.Errorf("tested informer haven't received an object, waited %v", timeout)
				}
			}
		})
	}

}

func TestDynamicSharedInformerFactory(t *testing.T) {
	scenarios := []struct {
		name        string
		existingObj *unstructured.Unstructured
		gvr         schema.GroupVersionResource
		ns          string
		trigger     func(gvr schema.GroupVersionResource, ns string, fakeClient *fake.FakeDynamicClient, testObject *unstructured.Unstructured) *unstructured.Unstructured
		handler     func(rcvCh chan<- *unstructured.Unstructured) *cache.ResourceEventHandlerFuncs
	}{
		// scenario 1
		{
			name: "scenario 1: test if adding an object triggers AddFunc",
			ns:   "ns-foo",
			gvr:  schema.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "deployments"},
			trigger: func(gvr schema.GroupVersionResource, ns string, fakeClient *fake.FakeDynamicClient, _ *unstructured.Unstructured) *unstructured.Unstructured {
				testObject := newUnstructured("extensions/v1beta1", "Deployment", "ns-foo", "name-foo")
				createdObj, err := fakeClient.Resource(gvr).Namespace(ns).Create(context.TODO(), testObject, metav1.CreateOptions{})
				if err != nil {
					t.Error(err)
				}
				return createdObj
			},
			handler: func(rcvCh chan<- *unstructured.Unstructured) *cache.ResourceEventHandlerFuncs {
				return &cache.ResourceEventHandlerFuncs{
					AddFunc: func(obj interface{}) {
						rcvCh <- obj.(*unstructured.Unstructured)
					},
				}
			},
		},

		// scenario 2
		{
			name:        "scenario 2: tests if updating an object triggers UpdateFunc",
			ns:          "ns-foo",
			gvr:         schema.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "deployments"},
			existingObj: newUnstructured("extensions/v1beta1", "Deployment", "ns-foo", "name-foo"),
			trigger: func(gvr schema.GroupVersionResource, ns string, fakeClient *fake.FakeDynamicClient, testObject *unstructured.Unstructured) *unstructured.Unstructured {
				testObject.Object["spec"] = "updatedName"
				updatedObj, err := fakeClient.Resource(gvr).Namespace(ns).Update(context.TODO(), testObject, metav1.UpdateOptions{})
				if err != nil {
					t.Error(err)
				}
				return updatedObj
			},
			handler: func(rcvCh chan<- *unstructured.Unstructured) *cache.ResourceEventHandlerFuncs {
				return &cache.ResourceEventHandlerFuncs{
					UpdateFunc: func(old, updated interface{}) {
						rcvCh <- updated.(*unstructured.Unstructured)
					},
				}
			},
		},

		// scenario 3
		{
			name:        "scenario 3: test if deleting an object triggers DeleteFunc",
			ns:          "ns-foo",
			gvr:         schema.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "deployments"},
			existingObj: newUnstructured("extensions/v1beta1", "Deployment", "ns-foo", "name-foo"),
			trigger: func(gvr schema.GroupVersionResource, ns string, fakeClient *fake.FakeDynamicClient, testObject *unstructured.Unstructured) *unstructured.Unstructured {
				err := fakeClient.Resource(gvr).Namespace(ns).Delete(context.TODO(), testObject.GetName(), metav1.DeleteOptions{})
				if err != nil {
					t.Error(err)
				}
				return testObject
			},
			handler: func(rcvCh chan<- *unstructured.Unstructured) *cache.ResourceEventHandlerFuncs {
				return &cache.ResourceEventHandlerFuncs{
					DeleteFunc: func(obj interface{}) {
						rcvCh <- obj.(*unstructured.Unstructured)
					},
				}
			},
		},
	}

	for _, ts := range scenarios {
		t.Run(ts.name, func(t *testing.T) {
			// test data
			timeout := time.Duration(3 * time.Second)
			ctx, cancel := context.WithTimeout(context.Background(), timeout)
			defer cancel()
			scheme := runtime.NewScheme()
			informerReciveObjectCh := make(chan *unstructured.Unstructured, 1)
			objs := []runtime.Object{}
			if ts.existingObj != nil {
				objs = append(objs, ts.existingObj)
			}
			fakeClient := fake.NewSimpleDynamicClient(scheme, objs...)
			target := dynamicinformer.NewDynamicSharedInformerFactory(fakeClient, 0)

			// act
			informerListerForGvr := target.ForResource(ts.gvr)
			informerListerForGvr.Informer().AddEventHandler(ts.handler(informerReciveObjectCh))
			target.Start(ctx.Done())
			if synced := target.WaitForCacheSync(ctx.Done()); !synced[ts.gvr] {
				t.Errorf("informer for %s hasn't synced", ts.gvr)
			}

			testObject := ts.trigger(ts.gvr, ts.ns, fakeClient, ts.existingObj)
			select {
			case objFromInformer := <-informerReciveObjectCh:
				if !equality.Semantic.DeepEqual(testObject, objFromInformer) {
					t.Fatalf("%v", diff.ObjectDiff(testObject, objFromInformer))
				}
			case <-ctx.Done():
				t.Errorf("tested informer haven't received an object, waited %v", timeout)
			}
		})
	}
}

func newUnstructured(apiVersion, kind, namespace, name string) *unstructured.Unstructured {
	return &unstructured.Unstructured{
		Object: map[string]interface{}{
			"apiVersion": apiVersion,
			"kind":       kind,
			"metadata": map[string]interface{}{
				"namespace": namespace,
				"name":      name,
			},
			"spec": name,
		},
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"strconv"
	"sync"
	"time"

	"github.com/brotherlogic/goserver"
	"golang.org/x/net/context"
	"google.golang.org/grpc"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
	pbg "github.com/brotherlogic/goserver/proto"
	"github.com/brotherlogic/goserver/utils"
)

const (
	intentWait = time.Second
)

// Server the main server type
type Server struct {
	*goserver.GoServer
	config     *pb.Config
	serving    bool
	LastIntent time.Time
	LastMaster time.Time
	worldMutex *sync.Mutex
	world      map[string]map[string]struct{}
	getter     getter
}

type prodGetter struct{}

func (g *prodGetter) getJobs(server *pbd.RegistryEntry) (*pbs.JobList, error) {
	list := &pbs.JobList{}

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	conn, err := grpc.Dial(server.GetIp()+":"+strconv.Itoa(int(server.GetPort())), grpc.WithInsecure())
	defer conn.Close()
	if err != nil {
		return list, err
	}

	slave := pbs.NewGoBuildSlaveClient(conn)
	r, err := slave.List(ctx, &pbs.Empty{}, grpc.FailFast(false))
	return r, err
}

func (s *Server) checkerThread(i *pb.Intent) {
	for true {
		time.Sleep(time.Minute)

		if len(s.world[i.GetSpec().GetName()]) != int(i.Count) {
			s.Log(fmt.Sprintf("MISMATCH: %v, %v", i, s.world[i.GetSpec().GetName()]))
			if len(s.world[i.GetSpec().GetName()]) < int(i.Count) {
				s.Log(fmt.Sprintf("UP : %v", i.GetSpec().GetName()))
			}
		}
	}
}

func (g *prodGetter) getSlaves() (*pbd.ServiceList, error) {
	ret := &pbd.ServiceList{}

	conn, err := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	if err != nil {
		return ret, err
	}
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := registry.ListAllServices(ctx, &pbd.Empty{}, grpc.FailFast(false))
	if err != nil {
		return ret, err
	}

	for _, s := range r.Services {
		if s.GetName() == "gobuildslave" {
			ret.Services = append(ret.Services, s)
		}
	}

	return ret, nil
}

type mainChecker struct {
	prev   []string
	logger func(string)
}

func getIP(servertype, servername string) (string, int) {
	conn, _ := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := registry.ListAllServices(ctx, &pbd.Empty{}, grpc.FailFast(false))
	if err != nil {
		return "", -1
	}
	for _, s := range r.Services {
		if s.Name == servertype && s.Identifier == servername {
			return s.Ip, int(s.Port)
		}
	}

	return "", -1
}

func (t *mainChecker) getprev() []string {
	return t.prev
}
func (t *mainChecker) setprev(v []string) {
	t.prev = v
}

func (t *mainChecker) assess(server string) (*pbs.JobList, *pbs.Config) {
	list := &pbs.JobList{}
	conf := &pbs.Config{}

	ip, port := getIP("gobuildslave", server)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	conn, err := grpc.Dial(ip+":"+strconv.Itoa(port), grpc.WithInsecure())
	defer conn.Close()
	if err != nil {
		return list, conf
	}

	slave := pbs.NewGoBuildSlaveClient(conn)
	r, err := slave.List(ctx, &pbs.Empty{}, grpc.FailFast(false))
	if err != nil {
		return list, conf
	}

	r2, err := slave.GetConfig(ctx, &pbs.Empty{}, grpc.FailFast(false))
	if err != nil {
		return list, conf
	}

	return r, r2
}

func (t *mainChecker) master(entry *pbd.RegistryEntry, master bool) bool {
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()
	conn, _ := grpc.Dial(entry.GetIp()+":"+strconv.Itoa(int(entry.GetPort())), grpc.WithInsecure())
	defer conn.Close()

	server := pbg.NewGoserverServiceClient(conn)
	_, err := server.Mote(ctx, &pbg.MoteRequest{Master: master}, grpc.FailFast(false))
	if err != nil {
		t.logger(fmt.Sprintf("Master REJECT(%v): %v", entry, err))
	}

	return err == nil
}

func runJob(job *pbs.JobSpec, server string) {
	if server != "" {
		ip, port := getIP("gobuildslave", server)
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()
		conn, _ := grpc.Dial(ip+":"+strconv.Itoa(port), grpc.WithInsecure())
		defer conn.Close()

		slave := pbs.NewGoBuildSlaveClient(conn)
		job.Server = server
		slave.Run(ctx, job, grpc.FailFast(false))
	}
}

func (t *mainChecker) discover() *pbd.ServiceList {
	ret := &pbd.ServiceList{}

	conn, _ := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := registry.ListAllServices(ctx, &pbd.Empty{}, grpc.FailFast(false))
	if err == nil {
		for _, s := range r.Services {
			ret.Services = append(ret.Services, s)
		}
	}

	return ret
}

// DoRegister Registers this server
func (s Server) DoRegister(server *grpc.Server) {
	pb.RegisterGoBuildMasterServer(server, &s)
}

// ReportHealth determines if the server is healthy
func (s Server) ReportHealth() bool {
	return true
}

// Mote promotes/demotes this server
func (s Server) Mote(master bool) error {
	return nil
}

//GetState gets the state of the server
func (s Server) GetState() []*pbg.State {
	return []*pbg.State{&pbg.State{Key: "last_intent", TimeValue: s.LastIntent.Unix()},
		&pbg.State{Key: "last_master", TimeValue: s.LastMaster.Unix()},
		&pbg.State{Key: "world", Text: fmt.Sprintf("%v", s.world)}}
}

//Compare compares current state to desired state
func (s Server) Compare(ctx context.Context, in *pb.Empty) (*pb.CompareResponse, error) {
	resp := &pb.CompareResponse{}
	list, _ := getFleetStatus(&mainChecker{logger: s.Log})
	cc := &pb.Config{}
	for _, jlist := range list {
		for _, job := range jlist.GetDetails() {
			cc.Intents = append(cc.Intents, &pb.Intent{Spec: job.GetSpec()})
		}
	}
	resp.Current = cc
	resp.Desired = s.config

	return resp, nil
}

func getConfig(c checker) *pb.Config {
	list, _ := getFleetStatus(c)
	config := &pb.Config{}

	for _, jlist := range list {
		for _, job := range jlist.Details {
			found := false
			for _, ij := range config.Intents {
				if job.Spec.Name == ij.Spec.Name {
					ij.Count++
					found = true
				}
			}

			if !found {
				config.Intents = append(config.Intents, &pb.Intent{Spec: &pbs.JobSpec{Name: job.Spec.Name}, Count: 1})
			}
		}
	}

	return config
}

// MatchIntent tries to match the intent with the state of production
func (s *Server) MatchIntent() {
	checker := &mainChecker{logger: s.Log}
	for s.serving {
		time.Sleep(intentWait)
		s.LastIntent = time.Now()

		state := getConfig(checker)
		diff := configDiff(s.config, state)
		joblist := runJobs(diff)
		for _, job := range joblist {
			runJob(job, chooseServer(job, checker))
		}
	}
}

// SetMaster sets up the master settings
func (s *Server) SetMaster() {
	t := time.Now()
	checker := &mainChecker{logger: s.Log}
	s.LastMaster = time.Now()

	fleet := checker.discover()
	matcher := make(map[string][]*pbd.RegistryEntry)
	hasMaster := make(map[string]int)
	for _, entry := range fleet.GetServices() {
		if !entry.GetIgnoresMaster() {
			if _, ok := matcher[entry.GetName()]; !ok {
				if entry.GetMaster() {
					hasMaster[entry.GetName()]++
				}
				matcher[entry.GetName()] = []*pbd.RegistryEntry{entry}
			} else {
				if entry.GetMaster() {
					hasMaster[entry.GetName()] = 1
					matcher[entry.GetName()] = append(matcher[entry.GetName()], entry)
				}
			}
		}
	}

	for key, entries := range matcher {
		if hasMaster[key] > 1 {
			hasMaster[key] = 1
			seen := false
			for _, entry := range entries {
				if seen && entry.GetMaster() {
					checker.master(entry, false)
				} else if entry.GetMaster() {
					seen = true
				}
			}
		}

		if hasMaster[key] == 0 {
			for _, entry := range entries {
				if checker.master(entry, true) {
					entry.Master = true
					break
				}
			}
		}
	}
	s.LogFunction("SetMasterRun", t)
}

//Init builds up the server
func Init(config *pb.Config) *Server {
	s := &Server{
		&goserver.GoServer{},
		config,
		true,
		time.Now(),
		time.Now(),
		&sync.Mutex{},
		make(map[string]map[string]struct{}),
		&prodGetter{},
	}
	return s
}

func (s *Server) becomeMaster() {
	for true {
		time.Sleep(time.Second * 5)
		_, _, err := utils.Resolve("gobuildmaster")
		if err != nil {
			s.Registry.Master = true
		}
	}
}

func main() {
	config, err := loadConfig("config.pb")
	if err != nil {
		log.Fatalf("Fatal loading of config: %v", err)
	}

	s := Init(config)

	var quiet = flag.Bool("quiet", false, "Show all output")
	flag.Parse()

	if *quiet {
		log.SetFlags(0)
		log.SetOutput(ioutil.Discard)
	}

	s.Register = s
	s.PrepServer()
	s.GoServer.Killme = false
	s.RegisterServer("gobuildmaster", false)
	s.RegisterServingTask(s.MatchIntent)
	s.RegisterServingTask(s.becomeMaster)
	s.RegisterRepeatingTask(s.SetMaster, time.Second)
	s.RegisterRepeatingTask(s.buildWorld, time.Minute)

	for i := 0; i < len(s.config.GetIntents()); i++ {
		go s.checkerThread(s.config.GetIntents()[i])
	}

	err = s.Serve()
	if err != nil {
		log.Fatalf("Serve error: %v", err)
	}
}
<commit_msg>Job reports a DOWN. This closes #98.<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"strconv"
	"sync"
	"time"

	"github.com/brotherlogic/goserver"
	"golang.org/x/net/context"
	"google.golang.org/grpc"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
	pbg "github.com/brotherlogic/goserver/proto"
	"github.com/brotherlogic/goserver/utils"
)

const (
	intentWait = time.Second
)

// Server the main server type
type Server struct {
	*goserver.GoServer
	config     *pb.Config
	serving    bool
	LastIntent time.Time
	LastMaster time.Time
	worldMutex *sync.Mutex
	world      map[string]map[string]struct{}
	getter     getter
}

type prodGetter struct{}

func (g *prodGetter) getJobs(server *pbd.RegistryEntry) (*pbs.JobList, error) {
	list := &pbs.JobList{}

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	conn, err := grpc.Dial(server.GetIp()+":"+strconv.Itoa(int(server.GetPort())), grpc.WithInsecure())
	defer conn.Close()
	if err != nil {
		return list, err
	}

	slave := pbs.NewGoBuildSlaveClient(conn)
	r, err := slave.List(ctx, &pbs.Empty{}, grpc.FailFast(false))
	return r, err
}

func (s *Server) checkerThread(i *pb.Intent) {
	for true {
		time.Sleep(time.Minute)

		if len(s.world[i.GetSpec().GetName()]) != int(i.Count) {
			s.Log(fmt.Sprintf("MISMATCH: %v, %v", i, s.world[i.GetSpec().GetName()]))
			if len(s.world[i.GetSpec().GetName()]) < int(i.Count) {
				s.Log(fmt.Sprintf("UP : %v", i.GetSpec().GetName()))
			} else {
				s.Log(fmt.Sprintf("DOWN : %v", i.GetSpec().GetName()))
			}
		}
	}
}

func (g *prodGetter) getSlaves() (*pbd.ServiceList, error) {
	ret := &pbd.ServiceList{}

	conn, err := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	if err != nil {
		return ret, err
	}
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := registry.ListAllServices(ctx, &pbd.Empty{}, grpc.FailFast(false))
	if err != nil {
		return ret, err
	}

	for _, s := range r.Services {
		if s.GetName() == "gobuildslave" {
			ret.Services = append(ret.Services, s)
		}
	}

	return ret, nil
}

type mainChecker struct {
	prev   []string
	logger func(string)
}

func getIP(servertype, servername string) (string, int) {
	conn, _ := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := registry.ListAllServices(ctx, &pbd.Empty{}, grpc.FailFast(false))
	if err != nil {
		return "", -1
	}
	for _, s := range r.Services {
		if s.Name == servertype && s.Identifier == servername {
			return s.Ip, int(s.Port)
		}
	}

	return "", -1
}

func (t *mainChecker) getprev() []string {
	return t.prev
}
func (t *mainChecker) setprev(v []string) {
	t.prev = v
}

func (t *mainChecker) assess(server string) (*pbs.JobList, *pbs.Config) {
	list := &pbs.JobList{}
	conf := &pbs.Config{}

	ip, port := getIP("gobuildslave", server)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	conn, err := grpc.Dial(ip+":"+strconv.Itoa(port), grpc.WithInsecure())
	defer conn.Close()
	if err != nil {
		return list, conf
	}

	slave := pbs.NewGoBuildSlaveClient(conn)
	r, err := slave.List(ctx, &pbs.Empty{}, grpc.FailFast(false))
	if err != nil {
		return list, conf
	}

	r2, err := slave.GetConfig(ctx, &pbs.Empty{}, grpc.FailFast(false))
	if err != nil {
		return list, conf
	}

	return r, r2
}

func (t *mainChecker) master(entry *pbd.RegistryEntry, master bool) bool {
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()
	conn, _ := grpc.Dial(entry.GetIp()+":"+strconv.Itoa(int(entry.GetPort())), grpc.WithInsecure())
	defer conn.Close()

	server := pbg.NewGoserverServiceClient(conn)
	_, err := server.Mote(ctx, &pbg.MoteRequest{Master: master}, grpc.FailFast(false))
	if err != nil {
		t.logger(fmt.Sprintf("Master REJECT(%v): %v", entry, err))
	}

	return err == nil
}

func runJob(job *pbs.JobSpec, server string) {
	if server != "" {
		ip, port := getIP("gobuildslave", server)
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()
		conn, _ := grpc.Dial(ip+":"+strconv.Itoa(port), grpc.WithInsecure())
		defer conn.Close()

		slave := pbs.NewGoBuildSlaveClient(conn)
		job.Server = server
		slave.Run(ctx, job, grpc.FailFast(false))
	}
}

func (t *mainChecker) discover() *pbd.ServiceList {
	ret := &pbd.ServiceList{}

	conn, _ := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := registry.ListAllServices(ctx, &pbd.Empty{}, grpc.FailFast(false))
	if err == nil {
		for _, s := range r.Services {
			ret.Services = append(ret.Services, s)
		}
	}

	return ret
}

// DoRegister Registers this server
func (s Server) DoRegister(server *grpc.Server) {
	pb.RegisterGoBuildMasterServer(server, &s)
}

// ReportHealth determines if the server is healthy
func (s Server) ReportHealth() bool {
	return true
}

// Mote promotes/demotes this server
func (s Server) Mote(master bool) error {
	return nil
}

//GetState gets the state of the server
func (s Server) GetState() []*pbg.State {
	return []*pbg.State{&pbg.State{Key: "last_intent", TimeValue: s.LastIntent.Unix()},
		&pbg.State{Key: "last_master", TimeValue: s.LastMaster.Unix()},
		&pbg.State{Key: "world", Text: fmt.Sprintf("%v", s.world)}}
}

//Compare compares current state to desired state
func (s Server) Compare(ctx context.Context, in *pb.Empty) (*pb.CompareResponse, error) {
	resp := &pb.CompareResponse{}
	list, _ := getFleetStatus(&mainChecker{logger: s.Log})
	cc := &pb.Config{}
	for _, jlist := range list {
		for _, job := range jlist.GetDetails() {
			cc.Intents = append(cc.Intents, &pb.Intent{Spec: job.GetSpec()})
		}
	}
	resp.Current = cc
	resp.Desired = s.config

	return resp, nil
}

func getConfig(c checker) *pb.Config {
	list, _ := getFleetStatus(c)
	config := &pb.Config{}

	for _, jlist := range list {
		for _, job := range jlist.Details {
			found := false
			for _, ij := range config.Intents {
				if job.Spec.Name == ij.Spec.Name {
					ij.Count++
					found = true
				}
			}

			if !found {
				config.Intents = append(config.Intents, &pb.Intent{Spec: &pbs.JobSpec{Name: job.Spec.Name}, Count: 1})
			}
		}
	}

	return config
}

// MatchIntent tries to match the intent with the state of production
func (s *Server) MatchIntent() {
	checker := &mainChecker{logger: s.Log}
	for s.serving {
		time.Sleep(intentWait)
		s.LastIntent = time.Now()

		state := getConfig(checker)
		diff := configDiff(s.config, state)
		joblist := runJobs(diff)
		for _, job := range joblist {
			runJob(job, chooseServer(job, checker))
		}
	}
}

// SetMaster sets up the master settings
func (s *Server) SetMaster() {
	t := time.Now()
	checker := &mainChecker{logger: s.Log}
	s.LastMaster = time.Now()

	fleet := checker.discover()
	matcher := make(map[string][]*pbd.RegistryEntry)
	hasMaster := make(map[string]int)
	for _, entry := range fleet.GetServices() {
		if !entry.GetIgnoresMaster() {
			if _, ok := matcher[entry.GetName()]; !ok {
				if entry.GetMaster() {
					hasMaster[entry.GetName()]++
				}
				matcher[entry.GetName()] = []*pbd.RegistryEntry{entry}
			} else {
				if entry.GetMaster() {
					hasMaster[entry.GetName()] = 1
					matcher[entry.GetName()] = append(matcher[entry.GetName()], entry)
				}
			}
		}
	}

	for key, entries := range matcher {
		if hasMaster[key] > 1 {
			hasMaster[key] = 1
			seen := false
			for _, entry := range entries {
				if seen && entry.GetMaster() {
					checker.master(entry, false)
				} else if entry.GetMaster() {
					seen = true
				}
			}
		}

		if hasMaster[key] == 0 {
			for _, entry := range entries {
				if checker.master(entry, true) {
					entry.Master = true
					break
				}
			}
		}
	}
	s.LogFunction("SetMasterRun", t)
}

//Init builds up the server
func Init(config *pb.Config) *Server {
	s := &Server{
		&goserver.GoServer{},
		config,
		true,
		time.Now(),
		time.Now(),
		&sync.Mutex{},
		make(map[string]map[string]struct{}),
		&prodGetter{},
	}
	return s
}

func (s *Server) becomeMaster() {
	for true {
		time.Sleep(time.Second * 5)
		_, _, err := utils.Resolve("gobuildmaster")
		if err != nil {
			s.Registry.Master = true
		}
	}
}

func main() {
	config, err := loadConfig("config.pb")
	if err != nil {
		log.Fatalf("Fatal loading of config: %v", err)
	}

	s := Init(config)

	var quiet = flag.Bool("quiet", false, "Show all output")
	flag.Parse()

	if *quiet {
		log.SetFlags(0)
		log.SetOutput(ioutil.Discard)
	}

	s.Register = s
	s.PrepServer()
	s.GoServer.Killme = false
	s.RegisterServer("gobuildmaster", false)
	s.RegisterServingTask(s.MatchIntent)
	s.RegisterServingTask(s.becomeMaster)
	s.RegisterRepeatingTask(s.SetMaster, time.Second)
	s.RegisterRepeatingTask(s.buildWorld, time.Minute)

	for i := 0; i < len(s.config.GetIntents()); i++ {
		go s.checkerThread(s.config.GetIntents()[i])
	}

	err = s.Serve()
	if err != nil {
		log.Fatalf("Serve error: %v", err)
	}
}
<|endoftext|>


<commit_before><commit_msg>removes logging<commit_after><|endoftext|>