<commit_before>// Package api is an API Gateway
package api

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

	"github.com/gorilla/mux"
	"github.com/micro/cli"
	"github.com/micro/go-micro"
	ahandler "github.com/micro/go-micro/api/handler"
	aapi "github.com/micro/go-micro/api/handler/api"
	"github.com/micro/go-micro/api/handler/event"
	ahttp "github.com/micro/go-micro/api/handler/http"
	arpc "github.com/micro/go-micro/api/handler/rpc"
	"github.com/micro/go-micro/api/handler/web"
	"github.com/micro/go-micro/api/resolver"
	"github.com/micro/go-micro/api/resolver/grpc"
	"github.com/micro/go-micro/api/resolver/host"
	rrmicro "github.com/micro/go-micro/api/resolver/micro"
	"github.com/micro/go-micro/api/resolver/path"
	"github.com/micro/go-micro/api/router"
	regRouter "github.com/micro/go-micro/api/router/registry"
	"github.com/micro/go-micro/api/server"
	httpapi "github.com/micro/go-micro/api/server/http"
	"github.com/micro/go-micro/util/log"
	"github.com/micro/micro/internal/handler"
	"github.com/micro/micro/internal/helper"
	"github.com/micro/micro/internal/stats"
	"github.com/micro/micro/plugin"
)

var (
	Name         = "go.micro.api"
	Address      = ":8080"
	Handler      = "meta"
	Resolver     = "micro"
	RPCPath      = "/rpc"
	APIPath      = "/"
	ProxyPath    = "/{service:[a-zA-Z0-9]+}"
	Namespace    = "go.micro.api"
	HeaderPrefix = "X-Micro-"
	EnableRPC    = false
)

func run(ctx *cli.Context, srvOpts ...micro.Option) {
	if len(ctx.GlobalString("server_name")) > 0 {
		Name = ctx.GlobalString("server_name")
	}
	if len(ctx.String("address")) > 0 {
		Address = ctx.String("address")
	}
	if len(ctx.String("handler")) > 0 {
		Handler = ctx.String("handler")
	}
	if len(ctx.String("namespace")) > 0 {
		Namespace = ctx.String("namespace")
	}
	if len(ctx.String("resolver")) > 0 {
		Resolver = ctx.String("resolver")
	}
	if len(ctx.String("enable_rpc")) > 0 {
		EnableRPC = ctx.Bool("enable_rpc")
	}

	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	// Init API
	var opts []server.Option

	if ctx.GlobalBool("enable_acme") {
		hosts := helper.ACMEHosts(ctx)
		opts = append(opts, server.EnableACME(true))
		opts = append(opts, server.ACMEHosts(hosts...))
	} else if ctx.GlobalBool("enable_tls") {
		config, err := helper.TLSConfig(ctx)
		if err != nil {
			fmt.Println(err.Error())
			return
		}

		opts = append(opts, server.EnableTLS(true))
		opts = append(opts, server.TLSConfig(config))
	}

	// create the router
	var h http.Handler
	r := mux.NewRouter()
	h = r

	if ctx.GlobalBool("enable_stats") {
		st := stats.New()
		r.HandleFunc("/stats", st.StatsHandler)
		h = st.ServeHTTP(r)
		st.Start()
		defer st.Stop()
	}

	// return version and list of services
	r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		response := fmt.Sprintf(`{"version": "%s"}`, ctx.App.Version)
		w.Write([]byte(response))
	})

	srvOpts = append(srvOpts, micro.Name(Name))
	if i := time.Duration(ctx.GlobalInt("register_ttl")); i > 0 {
		srvOpts = append(srvOpts, micro.RegisterTTL(i*time.Second))
	}
	if i := time.Duration(ctx.GlobalInt("register_interval")); i > 0 {
		srvOpts = append(srvOpts, micro.RegisterInterval(i*time.Second))
	}

	// initialise service
	service := micro.NewService(srvOpts...)

	// register rpc handler
	if EnableRPC {
		log.Logf("Registering RPC Handler at %s", RPCPath)
		r.HandleFunc(RPCPath, handler.RPC)
	}

	// resolver options
	ropts := []resolver.Option{
		resolver.WithNamespace(Namespace),
		resolver.WithHandler(Handler),
	}

	// default resolver
	rr := rrmicro.NewResolver(ropts...)

	switch Resolver {
	case "host":
		rr = host.NewResolver(ropts...)
	case "path":
		rr = path.NewResolver(ropts...)
	case "grpc":
		rr = grpc.NewResolver(ropts...)
	}

	switch Handler {
	case "rpc":
		log.Logf("Registering API RPC Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithNamespace(Namespace),
			router.WithHandler(arpc.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		rp := arpc.NewHandler(
			ahandler.WithNamespace(Namespace),
			ahandler.WithRouter(rt),
			ahandler.WithService(service),
		)
		r.PathPrefix(APIPath).Handler(rp)
	case "api":
		log.Logf("Registering API Request Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithNamespace(Namespace),
			router.WithHandler(aapi.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		ap := aapi.NewHandler(
			ahandler.WithNamespace(Namespace),
			ahandler.WithRouter(rt),
			ahandler.WithService(service),
		)
		r.PathPrefix(APIPath).Handler(ap)
	case "event":
		log.Logf("Registering API Event Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithNamespace(Namespace),
			router.WithHandler(event.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		ev := event.NewHandler(
			ahandler.WithNamespace(Namespace),
			ahandler.WithRouter(rt),
			ahandler.WithService(service),
		)
		r.PathPrefix(APIPath).Handler(ev)
	case "http", "proxy":
		log.Logf("Registering API HTTP Handler at %s", ProxyPath)
		rt := regRouter.NewRouter(
			router.WithNamespace(Namespace),
			router.WithHandler(ahttp.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		ht := ahttp.NewHandler(
			ahandler.WithNamespace(Namespace),
			ahandler.WithRouter(rt),
			ahandler.WithService(service),
		)
		r.PathPrefix(ProxyPath).Handler(ht)
	case "web":
		log.Logf("Registering API Web Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithNamespace(Namespace),
			router.WithHandler(web.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		w := web.NewHandler(
			ahandler.WithNamespace(Namespace),
			ahandler.WithRouter(rt),
			ahandler.WithService(service),
		)
		r.PathPrefix(APIPath).Handler(w)
	default:
		log.Logf("Registering API Default Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithNamespace(Namespace),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		r.PathPrefix(APIPath).Handler(handler.Meta(service, rt))
	}

	// reverse wrap handler
	plugins := append(Plugins(), plugin.Plugins()...)
	for i := len(plugins); i > 0; i-- {
		h = plugins[i-1].Handler()(h)
	}

	// create the server
	api := httpapi.NewServer(Address)
	api.Init(opts...)
	api.Handle("/", h)

	// Start API
	if err := api.Start(); err != nil {
		log.Fatal(err)
	}

	// Run server
	if err := service.Run(); err != nil {
		log.Fatal(err)
	}

	// Stop API
	if err := api.Stop(); err != nil {
		log.Fatal(err)
	}
}

func Commands(options ...micro.Option) []cli.Command {
	command := cli.Command{
		Name:  "api",
		Usage: "Run the api gateway",
		Action: func(ctx *cli.Context) {
			run(ctx, options...)
		},
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:   "address",
				Usage:  "Set the api address e.g 0.0.0.0:8080",
				EnvVar: "MICRO_API_ADDRESS",
			},
			cli.StringFlag{
				Name:   "handler",
				Usage:  "Specify the request handler to be used for mapping HTTP requests to services; {api, event, http, rpc}",
				EnvVar: "MICRO_API_HANDLER",
			},
			cli.StringFlag{
				Name:   "namespace",
				Usage:  "Set the namespace used by the API e.g. com.example.api",
				EnvVar: "MICRO_API_NAMESPACE",
			},
			cli.StringFlag{
				Name:   "resolver",
				Usage:  "Set the hostname resolver used by the API {host, path, grpc}",
				EnvVar: "MICRO_API_RESOLVER",
			},
			cli.BoolFlag{
				Name:   "enable_rpc",
				Usage:  "Enable call the backend directly via /rpc",
				EnvVar: "MICRO_API_ENABLE_RPC",
			},
		},
	}

	for _, p := range Plugins() {
		if cmds := p.Commands(); len(cmds) > 0 {
			command.Subcommands = append(command.Subcommands, cmds...)
		}

		if flags := p.Flags(); len(flags) > 0 {
			command.Flags = append(command.Flags, flags...)
		}
	}

	return []cli.Command{command}
}
<commit_msg>Handler favicon so it doesn't call the registry<commit_after>// Package api is an API Gateway
package api

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

	"github.com/gorilla/mux"
	"github.com/micro/cli"
	"github.com/micro/go-micro"
	ahandler "github.com/micro/go-micro/api/handler"
	aapi "github.com/micro/go-micro/api/handler/api"
	"github.com/micro/go-micro/api/handler/event"
	ahttp "github.com/micro/go-micro/api/handler/http"
	arpc "github.com/micro/go-micro/api/handler/rpc"
	"github.com/micro/go-micro/api/handler/web"
	"github.com/micro/go-micro/api/resolver"
	"github.com/micro/go-micro/api/resolver/grpc"
	"github.com/micro/go-micro/api/resolver/host"
	rrmicro "github.com/micro/go-micro/api/resolver/micro"
	"github.com/micro/go-micro/api/resolver/path"
	"github.com/micro/go-micro/api/router"
	regRouter "github.com/micro/go-micro/api/router/registry"
	"github.com/micro/go-micro/api/server"
	httpapi "github.com/micro/go-micro/api/server/http"
	"github.com/micro/go-micro/util/log"
	"github.com/micro/micro/internal/handler"
	"github.com/micro/micro/internal/helper"
	"github.com/micro/micro/internal/stats"
	"github.com/micro/micro/plugin"
)

var (
	Name         = "go.micro.api"
	Address      = ":8080"
	Handler      = "meta"
	Resolver     = "micro"
	RPCPath      = "/rpc"
	APIPath      = "/"
	ProxyPath    = "/{service:[a-zA-Z0-9]+}"
	Namespace    = "go.micro.api"
	HeaderPrefix = "X-Micro-"
	EnableRPC    = false
)

func run(ctx *cli.Context, srvOpts ...micro.Option) {
	if len(ctx.GlobalString("server_name")) > 0 {
		Name = ctx.GlobalString("server_name")
	}
	if len(ctx.String("address")) > 0 {
		Address = ctx.String("address")
	}
	if len(ctx.String("handler")) > 0 {
		Handler = ctx.String("handler")
	}
	if len(ctx.String("namespace")) > 0 {
		Namespace = ctx.String("namespace")
	}
	if len(ctx.String("resolver")) > 0 {
		Resolver = ctx.String("resolver")
	}
	if len(ctx.String("enable_rpc")) > 0 {
		EnableRPC = ctx.Bool("enable_rpc")
	}

	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	// Init API
	var opts []server.Option

	if ctx.GlobalBool("enable_acme") {
		hosts := helper.ACMEHosts(ctx)
		opts = append(opts, server.EnableACME(true))
		opts = append(opts, server.ACMEHosts(hosts...))
	} else if ctx.GlobalBool("enable_tls") {
		config, err := helper.TLSConfig(ctx)
		if err != nil {
			fmt.Println(err.Error())
			return
		}

		opts = append(opts, server.EnableTLS(true))
		opts = append(opts, server.TLSConfig(config))
	}

	// create the router
	var h http.Handler
	r := mux.NewRouter()
	h = r

	if ctx.GlobalBool("enable_stats") {
		st := stats.New()
		r.HandleFunc("/stats", st.StatsHandler)
		h = st.ServeHTTP(r)
		st.Start()
		defer st.Stop()
	}

	// return version and list of services
	r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		response := fmt.Sprintf(`{"version": "%s"}`, ctx.App.Version)
		w.Write([]byte(response))
	})

	// strip favicon.ico
	r.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {})

	srvOpts = append(srvOpts, micro.Name(Name))
	if i := time.Duration(ctx.GlobalInt("register_ttl")); i > 0 {
		srvOpts = append(srvOpts, micro.RegisterTTL(i*time.Second))
	}
	if i := time.Duration(ctx.GlobalInt("register_interval")); i > 0 {
		srvOpts = append(srvOpts, micro.RegisterInterval(i*time.Second))
	}

	// initialise service
	service := micro.NewService(srvOpts...)

	// register rpc handler
	if EnableRPC {
		log.Logf("Registering RPC Handler at %s", RPCPath)
		r.HandleFunc(RPCPath, handler.RPC)
	}

	// resolver options
	ropts := []resolver.Option{
		resolver.WithNamespace(Namespace),
		resolver.WithHandler(Handler),
	}

	// default resolver
	rr := rrmicro.NewResolver(ropts...)

	switch Resolver {
	case "host":
		rr = host.NewResolver(ropts...)
	case "path":
		rr = path.NewResolver(ropts...)
	case "grpc":
		rr = grpc.NewResolver(ropts...)
	}

	switch Handler {
	case "rpc":
		log.Logf("Registering API RPC Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithNamespace(Namespace),
			router.WithHandler(arpc.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		rp := arpc.NewHandler(
			ahandler.WithNamespace(Namespace),
			ahandler.WithRouter(rt),
			ahandler.WithService(service),
		)
		r.PathPrefix(APIPath).Handler(rp)
	case "api":
		log.Logf("Registering API Request Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithNamespace(Namespace),
			router.WithHandler(aapi.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		ap := aapi.NewHandler(
			ahandler.WithNamespace(Namespace),
			ahandler.WithRouter(rt),
			ahandler.WithService(service),
		)
		r.PathPrefix(APIPath).Handler(ap)
	case "event":
		log.Logf("Registering API Event Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithNamespace(Namespace),
			router.WithHandler(event.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		ev := event.NewHandler(
			ahandler.WithNamespace(Namespace),
			ahandler.WithRouter(rt),
			ahandler.WithService(service),
		)
		r.PathPrefix(APIPath).Handler(ev)
	case "http", "proxy":
		log.Logf("Registering API HTTP Handler at %s", ProxyPath)
		rt := regRouter.NewRouter(
			router.WithNamespace(Namespace),
			router.WithHandler(ahttp.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		ht := ahttp.NewHandler(
			ahandler.WithNamespace(Namespace),
			ahandler.WithRouter(rt),
			ahandler.WithService(service),
		)
		r.PathPrefix(ProxyPath).Handler(ht)
	case "web":
		log.Logf("Registering API Web Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithNamespace(Namespace),
			router.WithHandler(web.Handler),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		w := web.NewHandler(
			ahandler.WithNamespace(Namespace),
			ahandler.WithRouter(rt),
			ahandler.WithService(service),
		)
		r.PathPrefix(APIPath).Handler(w)
	default:
		log.Logf("Registering API Default Handler at %s", APIPath)
		rt := regRouter.NewRouter(
			router.WithNamespace(Namespace),
			router.WithResolver(rr),
			router.WithRegistry(service.Options().Registry),
		)
		r.PathPrefix(APIPath).Handler(handler.Meta(service, rt))
	}

	// reverse wrap handler
	plugins := append(Plugins(), plugin.Plugins()...)
	for i := len(plugins); i > 0; i-- {
		h = plugins[i-1].Handler()(h)
	}

	// create the server
	api := httpapi.NewServer(Address)
	api.Init(opts...)
	api.Handle("/", h)

	// Start API
	if err := api.Start(); err != nil {
		log.Fatal(err)
	}

	// Run server
	if err := service.Run(); err != nil {
		log.Fatal(err)
	}

	// Stop API
	if err := api.Stop(); err != nil {
		log.Fatal(err)
	}
}

func Commands(options ...micro.Option) []cli.Command {
	command := cli.Command{
		Name:  "api",
		Usage: "Run the api gateway",
		Action: func(ctx *cli.Context) {
			run(ctx, options...)
		},
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:   "address",
				Usage:  "Set the api address e.g 0.0.0.0:8080",
				EnvVar: "MICRO_API_ADDRESS",
			},
			cli.StringFlag{
				Name:   "handler",
				Usage:  "Specify the request handler to be used for mapping HTTP requests to services; {api, event, http, rpc}",
				EnvVar: "MICRO_API_HANDLER",
			},
			cli.StringFlag{
				Name:   "namespace",
				Usage:  "Set the namespace used by the API e.g. com.example.api",
				EnvVar: "MICRO_API_NAMESPACE",
			},
			cli.StringFlag{
				Name:   "resolver",
				Usage:  "Set the hostname resolver used by the API {host, path, grpc}",
				EnvVar: "MICRO_API_RESOLVER",
			},
			cli.BoolFlag{
				Name:   "enable_rpc",
				Usage:  "Enable call the backend directly via /rpc",
				EnvVar: "MICRO_API_ENABLE_RPC",
			},
		},
	}

	for _, p := range Plugins() {
		if cmds := p.Commands(); len(cmds) > 0 {
			command.Subcommands = append(command.Subcommands, cmds...)
		}

		if flags := p.Flags(); len(flags) > 0 {
			command.Flags = append(command.Flags, flags...)
		}
	}

	return []cli.Command{command}
}
<|endoftext|>


<commit_before>package api

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"strings"
	"sync/atomic"
	"time"
)

type RestClient struct {
	client  *http.Client
	cookie  interface{}
	Verbose bool
}

var OperationCallback func(op string, start time.Time, err error)

var lastSerialNumber uint64

func (c *RestClient) DoRaw(req *http.Request) (resp *http.Response, serialNumber uint64) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatal(err)
		}
	}()

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept-Language", "en-us")
	//req.Header.Set("Accept-Encoding", "gzip, deflate")
	if c.cookie != nil {
		req.AddCookie(c.cookie.(*http.Cookie))
	}

	serialNumber = atomic.AddUint64(&lastSerialNumber, 1)
	if req.URL.RawQuery != "" {
		req.URL.RawQuery += "&"
	}
	req.URL.RawQuery += fmt.Sprintf("n=%d", serialNumber)
	var start time.Time
	if c.Verbose {
		log.Printf("#%05d: %s %s", serialNumber, req.Method, req.URL)
		start = time.Now()
	}
	resp, err := c.client.Do(req)
	if err != nil {
		log.Printf("WARNING: Network error: %v for %s", err, req.URL)
		return nil, 0
	} else if resp.StatusCode >= 300 {
		log.Printf("WARNING: HTTP error: %s for %s", resp.Status, req.URL)
		return nil, 0
	} else if c.Verbose {
		log.Printf("#%05d:   <--%d (%v)", serialNumber, resp.StatusCode, time.Since(start))
	}
	return
}

func (c *RestClient) Do(req *http.Request) (mresp map[string]interface{}) {
	start := time.Now()
	resp, serialNumber := c.DoRaw(req)
	if resp == nil {
		return
	}
	if !strings.HasPrefix(resp.Header.Get("Content-Type"), "application/json") {
		log.Panicf("Non-JSON response for %v", req)
		return
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Panicf("Can't read HTTP response: %v", err)
		return
	}

	if err = json.Unmarshal(body, &mresp); err != nil {
		log.Panicf("Can't parse response JSON: %v\nrequest = %v\n%s", err, req, body)
	}
	if c.Verbose {
		log.Printf("#%05d:      finished in %v", serialNumber, time.Since(start))
	}
	return
}

func (c *RestClient) DoAndIgnore(req *http.Request) {
	start := time.Now()
	resp, serialNumber := c.DoRaw(req)
	if resp == nil {
		return
	}
	defer resp.Body.Close()
	io.Copy(ioutil.Discard, resp.Body)
	if c.Verbose {
		log.Printf("#%05d:      finished in %v", serialNumber, time.Since(start))
	}
}

type SyncGatewayClient struct {
	baseURI      string
	baseAdminURI string
	client       *RestClient
}

const MaxIdleConnsPerHost = 28000

func (c *SyncGatewayClient) Init(hostname, db string, port, adminPort int, verbose bool) {
	c.baseURI = fmt.Sprintf("http://%s:%d/%s", hostname, port, db)
	c.baseAdminURI = fmt.Sprintf("http://%s:%d/%s", hostname, adminPort, db)
	t := &http.Transport{MaxIdleConnsPerHost: MaxIdleConnsPerHost}
	c.client = &RestClient{&http.Client{Transport: t}, nil, verbose}
}

func (c *SyncGatewayClient) Valid() bool {
	req, _ := http.NewRequest("HEAD", c.baseAdminURI, nil)
	resp, _ := c.client.DoRaw(req)
	return resp != nil
}

func (c *SyncGatewayClient) AddCookie(cookie *http.Cookie) {
	c.client.cookie = cookie
}

type Doc struct {
	Id        string                 `json:"_id"`
	Rev       string                 `json:"_rev"`
	Channels  []string               `json:"channels"`
	Data      map[string]string      `json:"data"`
	Revisions map[string]interface{} `json:"_revisions"`
	Created   time.Time              `json:"created"`
}

func (c *SyncGatewayClient) PutSingleDoc(docid string, doc Doc) {

	if OperationCallback != nil {
		defer func(t time.Time) { OperationCallback("PutSingleDoc", t, nil) }(time.Now())
	}

	b, _ := json.Marshal(doc)
	j := bytes.NewReader(b)
	uri := fmt.Sprintf("%s/%s?new_edits=false", c.baseURI, docid)
	req, _ := http.NewRequest("PUT", uri, j)

	c.client.Do(req)
}

func (c *SyncGatewayClient) PostRevsDiff(revsDiff map[string][]string) {

	if OperationCallback != nil {
		defer func(t time.Time) { OperationCallback("PostRevsDiff", t, nil) }(time.Now())
	}

	b, _ := json.Marshal(revsDiff)
	j := bytes.NewReader(b)
	uri := fmt.Sprintf("%s/_revs_diff", c.baseURI)
	req, _ := http.NewRequest("POST", uri, j)
	c.client.DoAndIgnore(req)
}

func (c *SyncGatewayClient) PostBulkDocs(docs map[string]interface{}) {

	if OperationCallback != nil {
		defer func(t time.Time) { OperationCallback("PostBulkDocs", t, nil) }(time.Now())
	}

	b, _ := json.Marshal(docs)
	j := bytes.NewReader(b)
	uri := fmt.Sprintf("%s/_bulk_docs", c.baseURI)
	req, _ := http.NewRequest("POST", uri, j)
	c.client.DoAndIgnore(req)
}

type BulkDocsEntry struct {
	ID  string `json:"id"`
	Rev string `json:"rev"`
}

func (c *SyncGatewayClient) GetBulkDocs(docs []BulkDocsEntry, wakeup time.Time) bool {
	if OperationCallback != nil {
		defer func(t time.Time) { OperationCallback("GetBulkDocs", t, nil) }(time.Now())
	}

	body := map[string][]BulkDocsEntry{"docs": docs}
	b, _ := json.Marshal(body)
	j := bytes.NewReader(b)
	uri := fmt.Sprintf("%s/_bulk_get?revs=true&attachments=true", c.baseURI)
	req, _ := http.NewRequest("POST", uri, j)

	start := time.Now()
	resp, serialNumber := c.client.DoRaw(req) // _bulk_get returns MIME multipart, not JSON
	if resp == nil {
		return false
	}
	defer resp.Body.Close()
	for _, d := range docs {
		createdTime := createdTimeFromDocId(d.ID)
		if createdTime != nil {
			logPushToSubscriberTime(createdTime, wakeup)
		}
	}
	io.Copy(ioutil.Discard, resp.Body)

	if c.client.Verbose {
		log.Printf("#%05d:      finished in %v", serialNumber, time.Since(start))
	}
	return true
}

func (c *SyncGatewayClient) GetSingleDoc(docid string, revid string, wakeup time.Time) bool {

	if OperationCallback != nil {
		defer func(t time.Time) { OperationCallback("GetSingle", t, nil) }(time.Now())
	}

	uri := fmt.Sprintf("%s/%s", c.baseURI, docid)
	if revid != "" {
		uri += "?rev=" + revid
	}
	req, _ := http.NewRequest("GET", uri, nil)
	start := time.Now()
	resp, serialNumber := c.client.DoRaw(req)
	if resp == nil {
		return false
	}
	createdTime := createdTimeFromDocId(docid)
	if createdTime != nil {
		logPushToSubscriberTime(createdTime, wakeup)
	}
	io.Copy(ioutil.Discard, resp.Body)

	if c.client.Verbose {
		log.Printf("#%05d:      finished in %v", serialNumber, time.Since(start))
	}
	return true
}

func createdTimeFromDocId(docid string) *time.Time {
	parts := strings.SplitN(docid, "_", 2)
	if len(parts) < 2 {
		return nil
	}
	n, err := strconv.ParseInt(parts[1], 10, 64)
	if err != nil {
		return nil
	}
	res := time.Unix(n/1e3, (n%1e3)*1e6)
	return &res
}

func logPushToSubscriberTime(createdTime *time.Time, wakeup time.Time) {
	if wakeup.After(*createdTime) {
		OperationCallback("PushToSubscriberBackfill", wakeup, nil)
	} else {
		OperationCallback("PushToSubscriberInteractive", *createdTime, nil)
	}
}

func (c *SyncGatewayClient) GetLastSeq() float64 {
	if OperationCallback != nil {
		defer func(t time.Time) { OperationCallback("GetLastSeq", t, nil) }(time.Now())
	}

	uri := fmt.Sprintf("%s/", c.baseURI)
	req, _ := http.NewRequest("GET", uri, nil)

	resp := c.client.Do(req)
	return resp["committed_update_seq"].(float64)
}

type Change struct {
	Seq     interface{}
	ID      string
	Changes []ChangeRev
}

type ChangeRev struct {
	Rev string
}

func (c *SyncGatewayClient) changesFeedRequest(feedType, since interface{}) *http.Request {
	var uri string
	uri = fmt.Sprintf("%s/_changes?feed=%s&heartbeat=300000&style=all_docs", c.baseURI, feedType)
	if since != nil {
		uri += fmt.Sprintf("&since=%v", since)
	}
	req, _ := http.NewRequest("GET", uri, nil)
	return req
}

func (c *SyncGatewayClient) GetChangesFeed(feedType string, since interface{}) (<-chan *Change, *bool, *http.Response) {
	if feedType == "continuous" {
		return c.runContinuousChangesFeed(since)
	}

	out := make(chan *Change)
	running := true
	go func() {
		defer close(out)
		for running {
			feed := c.client.Do(c.changesFeedRequest(feedType, since))
			results := feed["results"].([]interface{})
			for _, result := range results {
				doc := result.(map[string]interface{})
				var change Change
				change.ID = doc["id"].(string)
				change.Seq = doc["seq"]
				for _, item := range doc["changes"].([]interface{}) {
					dict := item.(map[string]interface{})
					revID := dict["rev"].(string)
					change.Changes = append(change.Changes, ChangeRev{Rev: revID})
				}
				out <- &change
			}
			since = feed["last_seq"]
			if feedType != "longpoll" {
				break
			}
		}
		if !running {
			log.Printf("Changes feed closed at client request")
		}
	}()
	return out, &running, nil
}

func (c *SyncGatewayClient) runContinuousChangesFeed(since interface{}) (<-chan *Change, *bool, *http.Response) {
	running := true
	response, _ := c.client.DoRaw(c.changesFeedRequest("continuous", since))
	if response == nil {
		return nil, &running, nil
	}
	out := make(chan *Change)
	go func() {
		defer close(out)
		defer response.Body.Close()
		scanner := bufio.NewScanner(response.Body)
		for running && scanner.Scan() {
			if line := scanner.Bytes(); len(line) > 0 {
				var change Change
				if err := json.Unmarshal(line, &change); err != nil {
					log.Printf("Warning: Unparseable line from continuous _changes: %s", scanner.Bytes())
					continue
				}
				out <- &change
			}
		}
		if running {
			log.Printf("Warning: Continuous changes feed closed with error %v", scanner.Err())
		}
	}()
	return out, &running, response
}

type Checkpoint struct {
	LastSequence interface{} `json:"lastSequence"`
}

func (c *SyncGatewayClient) SaveCheckpoint(id string, checkpoint Checkpoint) {

	if OperationCallback != nil {
		defer func(t time.Time) { OperationCallback("SaveCheckpoint", t, nil) }(time.Now())
	}

	b, _ := json.Marshal(checkpoint)
	j := bytes.NewReader(b)
	uri := fmt.Sprintf("%s/_local/%s", c.baseURI, id)
	req, _ := http.NewRequest("PUT", uri, j)

	c.client.Do(req)
}

type UserAuth struct {
	Name          string   `json:"name"`
	Password      string   `json:"password"`
	AdminChannels []string `json:"admin_channels"`
}

func (c *SyncGatewayClient) AddUser(name string, auth UserAuth) {

	if OperationCallback != nil {
		defer func(t time.Time) { OperationCallback("AddUser", t, nil) }(time.Now())
	}

	b, _ := json.Marshal(auth)
	j := bytes.NewReader(b)
	uri := fmt.Sprintf("%s/_user/%s", c.baseAdminURI, name)
	req, _ := http.NewRequest("PUT", uri, j)

	log.Printf("Adding user %s", name)
	c.client.DoAndIgnore(req)
}

type Session struct {
	Name string `json:"name"`
	TTL  int    `json:"ttl"`
}

func (c *SyncGatewayClient) CreateSession(name string, session Session) http.Cookie {

	if OperationCallback != nil {
		defer func(t time.Time) { OperationCallback("CreateSession", t, nil) }(time.Now())
	}

	b, _ := json.Marshal(session)
	j := bytes.NewReader(b)
	uri := fmt.Sprintf("%s/_session", c.baseAdminURI)
	req, _ := http.NewRequest("POST", uri, j)

	resp := c.client.Do(req)
	return http.Cookie{
		Name:  resp["cookie_name"].(string),
		Value: resp["session_id"].(string),
	}
}
<commit_msg>record same measurement from charts to logs for doc gets<commit_after>package api

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"strings"
	"sync/atomic"
	"time"
)

type RestClient struct {
	client  *http.Client
	cookie  interface{}
	Verbose bool
}

var OperationCallback func(op string, start time.Time, err error)

var lastSerialNumber uint64

func (c *RestClient) DoRaw(req *http.Request) (resp *http.Response, serialNumber uint64) {
	defer func() {
		if err := recover(); err != nil {
			log.Fatal(err)
		}
	}()

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept-Language", "en-us")
	//req.Header.Set("Accept-Encoding", "gzip, deflate")
	if c.cookie != nil {
		req.AddCookie(c.cookie.(*http.Cookie))
	}

	serialNumber = atomic.AddUint64(&lastSerialNumber, 1)
	if req.URL.RawQuery != "" {
		req.URL.RawQuery += "&"
	}
	req.URL.RawQuery += fmt.Sprintf("n=%d", serialNumber)
	var start time.Time
	if c.Verbose {
		log.Printf("#%05d: %s %s", serialNumber, req.Method, req.URL)
		start = time.Now()
	}
	resp, err := c.client.Do(req)
	if err != nil {
		log.Printf("WARNING: Network error: %v for %s", err, req.URL)
		return nil, 0
	} else if resp.StatusCode >= 300 {
		log.Printf("WARNING: HTTP error: %s for %s", resp.Status, req.URL)
		return nil, 0
	} else if c.Verbose {
		log.Printf("#%05d:   <--%d (%v)", serialNumber, resp.StatusCode, time.Since(start))
	}
	return
}

func (c *RestClient) Do(req *http.Request) (mresp map[string]interface{}) {
	start := time.Now()
	resp, serialNumber := c.DoRaw(req)
	if resp == nil {
		return
	}
	if !strings.HasPrefix(resp.Header.Get("Content-Type"), "application/json") {
		log.Panicf("Non-JSON response for %v", req)
		return
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Panicf("Can't read HTTP response: %v", err)
		return
	}

	if err = json.Unmarshal(body, &mresp); err != nil {
		log.Panicf("Can't parse response JSON: %v\nrequest = %v\n%s", err, req, body)
	}
	if c.Verbose {
		log.Printf("#%05d:      finished in %v", serialNumber, time.Since(start))
	}
	return
}

func (c *RestClient) DoAndIgnore(req *http.Request) {
	start := time.Now()
	resp, serialNumber := c.DoRaw(req)
	if resp == nil {
		return
	}
	defer resp.Body.Close()
	io.Copy(ioutil.Discard, resp.Body)
	if c.Verbose {
		log.Printf("#%05d:      finished in %v", serialNumber, time.Since(start))
	}
}

type SyncGatewayClient struct {
	baseURI      string
	baseAdminURI string
	client       *RestClient
}

const MaxIdleConnsPerHost = 28000

func (c *SyncGatewayClient) Init(hostname, db string, port, adminPort int, verbose bool) {
	c.baseURI = fmt.Sprintf("http://%s:%d/%s", hostname, port, db)
	c.baseAdminURI = fmt.Sprintf("http://%s:%d/%s", hostname, adminPort, db)
	t := &http.Transport{MaxIdleConnsPerHost: MaxIdleConnsPerHost}
	c.client = &RestClient{&http.Client{Transport: t}, nil, verbose}
}

func (c *SyncGatewayClient) Valid() bool {
	req, _ := http.NewRequest("HEAD", c.baseAdminURI, nil)
	resp, _ := c.client.DoRaw(req)
	return resp != nil
}

func (c *SyncGatewayClient) AddCookie(cookie *http.Cookie) {
	c.client.cookie = cookie
}

type Doc struct {
	Id        string                 `json:"_id"`
	Rev       string                 `json:"_rev"`
	Channels  []string               `json:"channels"`
	Data      map[string]string      `json:"data"`
	Revisions map[string]interface{} `json:"_revisions"`
	Created   time.Time              `json:"created"`
}

func (c *SyncGatewayClient) PutSingleDoc(docid string, doc Doc) {

	if OperationCallback != nil {
		defer func(t time.Time) { OperationCallback("PutSingleDoc", t, nil) }(time.Now())
	}

	b, _ := json.Marshal(doc)
	j := bytes.NewReader(b)
	uri := fmt.Sprintf("%s/%s?new_edits=false", c.baseURI, docid)
	req, _ := http.NewRequest("PUT", uri, j)

	c.client.Do(req)
}

func (c *SyncGatewayClient) PostRevsDiff(revsDiff map[string][]string) {

	if OperationCallback != nil {
		defer func(t time.Time) { OperationCallback("PostRevsDiff", t, nil) }(time.Now())
	}

	b, _ := json.Marshal(revsDiff)
	j := bytes.NewReader(b)
	uri := fmt.Sprintf("%s/_revs_diff", c.baseURI)
	req, _ := http.NewRequest("POST", uri, j)
	c.client.DoAndIgnore(req)
}

func (c *SyncGatewayClient) PostBulkDocs(docs map[string]interface{}) {

	if OperationCallback != nil {
		defer func(t time.Time) { OperationCallback("PostBulkDocs", t, nil) }(time.Now())
	}

	b, _ := json.Marshal(docs)
	j := bytes.NewReader(b)
	uri := fmt.Sprintf("%s/_bulk_docs", c.baseURI)
	req, _ := http.NewRequest("POST", uri, j)
	c.client.DoAndIgnore(req)
}

type BulkDocsEntry struct {
	ID  string `json:"id"`
	Rev string `json:"rev"`
}

func (c *SyncGatewayClient) GetBulkDocs(docs []BulkDocsEntry, wakeup time.Time) bool {
	oldStart := time.Now()

	body := map[string][]BulkDocsEntry{"docs": docs}
	b, _ := json.Marshal(body)
	j := bytes.NewReader(b)
	uri := fmt.Sprintf("%s/_bulk_get?revs=true&attachments=true", c.baseURI)
	req, _ := http.NewRequest("POST", uri, j)

	start := time.Now()
	resp, serialNumber := c.client.DoRaw(req) // _bulk_get returns MIME multipart, not JSON
	if OperationCallback != nil {
		defer func(t time.Time) {
			OperationCallback("GetBulkDocs", t, nil)
			log.Printf("#%05d:      oldfinished in %v", serialNumber, time.Since(t))
		}(oldStart)
	}
	if resp == nil {
		return false
	}
	defer resp.Body.Close()
	for _, d := range docs {
		createdTime := createdTimeFromDocId(d.ID)
		if createdTime != nil {
			logPushToSubscriberTime(createdTime, wakeup)
		}
	}
	io.Copy(ioutil.Discard, resp.Body)

	if c.client.Verbose {
		log.Printf("#%05d:      finished in %v", serialNumber, time.Since(start))
	}
	return true
}

func (c *SyncGatewayClient) GetSingleDoc(docid string, revid string, wakeup time.Time) bool {
	oldStart := time.Now()

	uri := fmt.Sprintf("%s/%s", c.baseURI, docid)
	if revid != "" {
		uri += "?rev=" + revid
	}
	req, _ := http.NewRequest("GET", uri, nil)
	start := time.Now()
	resp, serialNumber := c.client.DoRaw(req)
	if OperationCallback != nil {
		defer func(t time.Time) {
			OperationCallback("GetSingle", t, nil)
			log.Printf("#%05d:      oldfinished in %v", serialNumber, time.Since(t))
		}(oldStart)
	}
	if resp == nil {
		return false
	}
	createdTime := createdTimeFromDocId(docid)
	if createdTime != nil {
		logPushToSubscriberTime(createdTime, wakeup)
	}
	io.Copy(ioutil.Discard, resp.Body)

	if c.client.Verbose {
		log.Printf("#%05d:      finished in %v", serialNumber, time.Since(start))
	}
	return true
}

func createdTimeFromDocId(docid string) *time.Time {
	parts := strings.SplitN(docid, "_", 2)
	if len(parts) < 2 {
		return nil
	}
	n, err := strconv.ParseInt(parts[1], 10, 64)
	if err != nil {
		return nil
	}
	res := time.Unix(n/1e3, (n%1e3)*1e6)
	return &res
}

func logPushToSubscriberTime(createdTime *time.Time, wakeup time.Time) {
	if wakeup.After(*createdTime) {
		OperationCallback("PushToSubscriberBackfill", wakeup, nil)
	} else {
		OperationCallback("PushToSubscriberInteractive", *createdTime, nil)
	}
}

func (c *SyncGatewayClient) GetLastSeq() float64 {
	if OperationCallback != nil {
		defer func(t time.Time) { OperationCallback("GetLastSeq", t, nil) }(time.Now())
	}

	uri := fmt.Sprintf("%s/", c.baseURI)
	req, _ := http.NewRequest("GET", uri, nil)

	resp := c.client.Do(req)
	return resp["committed_update_seq"].(float64)
}

type Change struct {
	Seq     interface{}
	ID      string
	Changes []ChangeRev
}

type ChangeRev struct {
	Rev string
}

func (c *SyncGatewayClient) changesFeedRequest(feedType, since interface{}) *http.Request {
	var uri string
	uri = fmt.Sprintf("%s/_changes?feed=%s&heartbeat=300000&style=all_docs", c.baseURI, feedType)
	if since != nil {
		uri += fmt.Sprintf("&since=%v", since)
	}
	req, _ := http.NewRequest("GET", uri, nil)
	return req
}

func (c *SyncGatewayClient) GetChangesFeed(feedType string, since interface{}) (<-chan *Change, *bool, *http.Response) {
	if feedType == "continuous" {
		return c.runContinuousChangesFeed(since)
	}

	out := make(chan *Change)
	running := true
	go func() {
		defer close(out)
		for running {
			feed := c.client.Do(c.changesFeedRequest(feedType, since))
			results := feed["results"].([]interface{})
			for _, result := range results {
				doc := result.(map[string]interface{})
				var change Change
				change.ID = doc["id"].(string)
				change.Seq = doc["seq"]
				for _, item := range doc["changes"].([]interface{}) {
					dict := item.(map[string]interface{})
					revID := dict["rev"].(string)
					change.Changes = append(change.Changes, ChangeRev{Rev: revID})
				}
				out <- &change
			}
			since = feed["last_seq"]
			if feedType != "longpoll" {
				break
			}
		}
		if !running {
			log.Printf("Changes feed closed at client request")
		}
	}()
	return out, &running, nil
}

func (c *SyncGatewayClient) runContinuousChangesFeed(since interface{}) (<-chan *Change, *bool, *http.Response) {
	running := true
	response, _ := c.client.DoRaw(c.changesFeedRequest("continuous", since))
	if response == nil {
		return nil, &running, nil
	}
	out := make(chan *Change)
	go func() {
		defer close(out)
		defer response.Body.Close()
		scanner := bufio.NewScanner(response.Body)
		for running && scanner.Scan() {
			if line := scanner.Bytes(); len(line) > 0 {
				var change Change
				if err := json.Unmarshal(line, &change); err != nil {
					log.Printf("Warning: Unparseable line from continuous _changes: %s", scanner.Bytes())
					continue
				}
				out <- &change
			}
		}
		if running {
			log.Printf("Warning: Continuous changes feed closed with error %v", scanner.Err())
		}
	}()
	return out, &running, response
}

type Checkpoint struct {
	LastSequence interface{} `json:"lastSequence"`
}

func (c *SyncGatewayClient) SaveCheckpoint(id string, checkpoint Checkpoint) {

	if OperationCallback != nil {
		defer func(t time.Time) { OperationCallback("SaveCheckpoint", t, nil) }(time.Now())
	}

	b, _ := json.Marshal(checkpoint)
	j := bytes.NewReader(b)
	uri := fmt.Sprintf("%s/_local/%s", c.baseURI, id)
	req, _ := http.NewRequest("PUT", uri, j)

	c.client.Do(req)
}

type UserAuth struct {
	Name          string   `json:"name"`
	Password      string   `json:"password"`
	AdminChannels []string `json:"admin_channels"`
}

func (c *SyncGatewayClient) AddUser(name string, auth UserAuth) {

	if OperationCallback != nil {
		defer func(t time.Time) { OperationCallback("AddUser", t, nil) }(time.Now())
	}

	b, _ := json.Marshal(auth)
	j := bytes.NewReader(b)
	uri := fmt.Sprintf("%s/_user/%s", c.baseAdminURI, name)
	req, _ := http.NewRequest("PUT", uri, j)

	log.Printf("Adding user %s", name)
	c.client.DoAndIgnore(req)
}

type Session struct {
	Name string `json:"name"`
	TTL  int    `json:"ttl"`
}

func (c *SyncGatewayClient) CreateSession(name string, session Session) http.Cookie {

	if OperationCallback != nil {
		defer func(t time.Time) { OperationCallback("CreateSession", t, nil) }(time.Now())
	}

	b, _ := json.Marshal(session)
	j := bytes.NewReader(b)
	uri := fmt.Sprintf("%s/_session", c.baseAdminURI)
	req, _ := http.NewRequest("POST", uri, j)

	resp := c.client.Do(req)
	return http.Cookie{
		Name:  resp["cookie_name"].(string),
		Value: resp["session_id"].(string),
	}
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"log"
	"net/http"
	"net/url"
	"strings"
)

const (
	GET    = "GET"
	PUT    = "PUT"
	POST   = "POST"
	DELETE = "DELETE"
)

//we now need to wrap the integer HTTP Reply code in this struct
//to be able to support the correct ID for the continuous pulling scheme
type HdrReply struct {
	Code  int
	Extra string
}

type Reply struct {
	Data []byte
	Err  error
}

type Resource interface {
	Get(url.Values) (HdrReply, chan Reply)
	Put(url.Values) (HdrReply, chan Reply)
	Post(url.Values) (HdrReply, chan Reply)
	Delete(url.Values) (HdrReply, chan Reply)
}

type (
	GetNotAllowed    struct{}
	PutNotAllowed    struct{}
	PostNotAllowed   struct{}
	DeleteNotAllowed struct{}
)

func (GetNotAllowed) Get(vals url.Values) (HdrReply, chan Reply) {
	return HdrReply{Code: 405}, nil
}

func (PutNotAllowed) Put(vals url.Values) (HdrReply, chan Reply) {
	return HdrReply{Code: 405}, nil
}

func (PostNotAllowed) Post(vals url.Values) (HdrReply, chan Reply) {
	return HdrReply{Code: 405}, nil
}

func (DeleteNotAllowed) Delete(vals url.Values) (HdrReply, chan Reply) {
	return HdrReply{Code: 405}, nil
}

type API struct {
	mux *http.ServeMux
}

func NewAPI() *API {
	return &API{http.NewServeMux()}
}

func (api *API) requestHandlerFunc(resource Resource) http.HandlerFunc {
	return func(rw http.ResponseWriter, req *http.Request) {
		var (
			datac chan Reply
			code  HdrReply
		)
		log.Printf("--Request From:%s --", req.RemoteAddr)
		req.ParseForm()
		method := req.Method
		vals := req.Form
		//here i plug the remote address in the vals map for the Get function to have
		ip := strings.Split(req.RemoteAddr, ":") //split cause it's ip:port
		vals["remoteaddr"] = ip
		switch method {
		case GET:
			code, datac = resource.Get(vals)
		case PUT:
			code, datac = resource.Put(vals)
		case POST:
			code, datac = resource.Post(vals)
		case DELETE:
			code, datac = resource.Delete(vals)
		}
		if code.Extra != "" { //he have a uuid for continuous pulling
			rw.Header().Set("Next-Pull-ID", code.Extra)
		}
		//set the CORS header
		rw.Header().Set("Access-Control-Allow-Origin", "*")
		rw.WriteHeader(code.Code)
		if datac != nil { // we got a proper channel to get datafrom
			//go func(dc <-chan Reply) { // fire a goroutine that will end upon the chan getting closed
			for r := range datac {
				if r.Err == nil {
					rw.Write(r.Data)
				} else {
					log.Printf("Error in received from data channel:%s\n", r.Err)
					rw.Write([]byte(fmt.Sprintf("%s\n", r.Err)))
				}
			}
			//}(datac)
		}
	}
}

func (api *API) AddResource(resource Resource, path string) {
	api.mux.HandleFunc(path, api.requestHandlerFunc(resource))
}

func (api *API) Start(port int) {
	portstr := fmt.Sprintf(":%d", port)
	http.ListenAndServe(portstr, api.mux)
}
<commit_msg>Added context to api<commit_after>package api

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"strings"
)

const (
	GET    = "GET"
	PUT    = "PUT"
	POST   = "POST"
	DELETE = "DELETE"
)

//we now need to wrap the integer HTTP Reply code in this struct
//to be able to support the correct ID for the continuous pulling scheme
type HdrReply struct {
	Code  int
	Extra string
}

type Reply struct {
	Data []byte
	Err  error
}

type Resource interface {
	Get(context.Context, url.Values) (HdrReply, chan Reply)
	Put(context.Context, url.Values) (HdrReply, chan Reply)
	Post(context.Context, url.Values) (HdrReply, chan Reply)
	Delete(context.Context, url.Values) (HdrReply, chan Reply)
}

type (
	GetNotAllowed    struct{}
	PutNotAllowed    struct{}
	PostNotAllowed   struct{}
	DeleteNotAllowed struct{}
)

func (GetNotAllowed) Get(ctx context.Context, vals url.Values) (HdrReply, chan Reply) {
	return HdrReply{Code: 405}, nil
}

func (PutNotAllowed) Put(ctx context.Context, vals url.Values) (HdrReply, chan Reply) {
	return HdrReply{Code: 405}, nil
}

func (PostNotAllowed) Post(ctx context.Context, vals url.Values) (HdrReply, chan Reply) {
	return HdrReply{Code: 405}, nil
}

func (DeleteNotAllowed) Delete(ctx context.Context, vals url.Values) (HdrReply, chan Reply) {
	return HdrReply{Code: 405}, nil
}

type API struct {
	mux *http.ServeMux
}

func NewAPI() *API {
	return &API{http.NewServeMux()}
}

func (api *API) requestHandlerFunc(resource Resource) http.HandlerFunc {
	return func(rw http.ResponseWriter, req *http.Request) {
		var (
			datac chan Reply
			code  HdrReply
		)
		ctx, cf := context.WithCancel(context.Background())
		log.Printf("--Request From:%s --", req.RemoteAddr)
		req.ParseForm()
		method := req.Method
		vals := req.Form
		//here i plug the remote address in the vals map for the Get function to have
		ip := strings.Split(req.RemoteAddr, ":") //split cause it's ip:port
		vals["remoteaddr"] = ip
		switch method {
		case GET:
			code, datac = resource.Get(ctx, vals)
		case PUT:
			code, datac = resource.Put(ctx, vals)
		case POST:
			code, datac = resource.Post(ctx, vals)
		case DELETE:
			code, datac = resource.Delete(ctx, vals)
		}
		if code.Extra != "" { //he have a uuid for continuous pulling
			rw.Header().Set("Next-Pull-ID", code.Extra)
		}
		//set the CORS header
		rw.Header().Set("Access-Control-Allow-Origin", "*")
		rw.WriteHeader(code.Code)
		if datac != nil { // we got a proper channel to get datafrom
			//go func(dc <-chan Reply) { // fire a goroutine that will end upon the chan getting closed
			reqOpen := true
			for reqOpen {
				select {
				case r, reqOpen := <-datac:
					if reqOpen {
						if r.Err == nil {
							rw.Write(r.Data)
						} else {
							log.Printf("Error in received from data channel:%s\n", r.Err)
							rw.Write([]byte(fmt.Sprintf("%s\n", r.Err)))
						}
					}
				case <-rw.(http.CloseNotifier).CloseNotify():
					cf()
					reqOpen = false
				}
			}
			//}(datac)
		}
	}
}

func (api *API) AddResource(resource Resource, path string) {
	api.mux.HandleFunc(path, api.requestHandlerFunc(resource))
}

func (api *API) Start(port int) {
	portstr := fmt.Sprintf(":%d", port)
	http.ListenAndServe(portstr, api.mux)
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package media

// An alternative would be: code.google.com/p/graphics-go/graphics

import (
	"image"
	"image/color"
)

// Resize returns a scaled copy of the image slice r of m.
// The returned image has width w and height h.
func _ResizeImage(m image.Image, r image.Rectangle, w, h int) image.Image {
	if w < 0 || h < 0 {
		return nil
	}
	if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 {
		return image.NewRGBA64(image.Rect(0, 0, w, h))
	}
	switch m := m.(type) {
	case *image.RGBA:
		return resizeRGBA(m, r, w, h)
	case *image.YCbCr:
		if m, ok := resizeYCbCr(m, r, w, h); ok {
			return m
		}
	}
	ww, hh := uint64(w), uint64(h)
	dx, dy := uint64(r.Dx()), uint64(r.Dy())
	// The scaling algorithm is to nearest-neighbor magnify the dx * dy source
	// to a (ww*dx) * (hh*dy) intermediate image and then minify the intermediate
	// image back down to a ww * hh destination with a simple box filter.
	// The intermediate image is implied, we do not physically allocate a slice
	// of length ww*dx*hh*dy.
	// For example, consider a 4*3 source image. Label its pixels from a-l:
	//	abcd
	//	efgh
	//	ijkl
	// To resize this to a 3*2 destination image, the intermediate is 12*6.
	// Whitespace has been added to delineate the destination pixels:
	//	aaab bbcc cddd
	//	aaab bbcc cddd
	//	eeef ffgg ghhh
	//
	//	eeef ffgg ghhh
	//	iiij jjkk klll
	//	iiij jjkk klll
	// Thus, the 'b' source pixel contributes one third of its value to the
	// (0, 0) destination pixel and two thirds to (1, 0).
	// The implementation is a two-step process. First, the source pixels are
	// iterated over and each source pixel's contribution to 1 or more
	// destination pixels are summed. Second, the sums are divided by a scaling
	// factor to yield the destination pixels.
	// TODO: By interleaving the two steps, instead of doing all of
	// step 1 first and all of step 2 second, we could allocate a smaller sum
	// slice of length 4*w*2 instead of 4*w*h, although the resultant code
	// would become more complicated.
	n, sum := dx*dy, make([]uint64, 4*w*h)
	for y := r.Min.Y; y < r.Max.Y; y++ {
		for x := r.Min.X; x < r.Max.X; x++ {
			// Get the source pixel.
			r32, g32, b32, a32 := m.At(x, y).RGBA()
			r64 := uint64(r32)
			g64 := uint64(g32)
			b64 := uint64(b32)
			a64 := uint64(a32)
			// Spread the source pixel over 1 or more destination rows.
			py := uint64(y) * hh
			for remy := hh; remy > 0; {
				qy := dy - (py % dy)
				if qy > remy {
					qy = remy
				}
				// Spread the source pixel over 1 or more destination columns.
				px := uint64(x) * ww
				index := 4 * ((py/dy)*ww + (px / dx))
				for remx := ww; remx > 0; {
					qx := dx - (px % dx)
					if qx > remx {
						qx = remx
					}
					sum[index+0] += r64 * qx * qy
					sum[index+1] += g64 * qx * qy
					sum[index+2] += b64 * qx * qy
					sum[index+3] += a64 * qx * qy
					index += 4
					px += qx
					remx -= qx
				}
				py += qy
				remy -= qy
			}
		}
	}
	return average(sum, w, h, n*0x0101)
}

// average convert the sums to averages and returns the result.
func average(sum []uint64, w, h int, n uint64) image.Image {
	ret := image.NewRGBA(image.Rect(0, 0, w, h))
	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			index := 4 * (y*w + x)
			ret.SetRGBA(x, y, color.RGBA{
				uint8(sum[index+0] / n),
				uint8(sum[index+1] / n),
				uint8(sum[index+2] / n),
				uint8(sum[index+3] / n),
			})
		}
	}
	return ret
}

// resizeYCbCr returns a scaled copy of the YCbCr image slice r of m.
// The returned image has width w and height h.
func resizeYCbCr(m *image.YCbCr, r image.Rectangle, w, h int) (image.Image, bool) {
	var verticalRes int
	switch m.SubsampleRatio {
	case image.YCbCrSubsampleRatio420:
		verticalRes = 2
	case image.YCbCrSubsampleRatio422:
		verticalRes = 1
	default:
		return nil, false
	}
	ww, hh := uint64(w), uint64(h)
	dx, dy := uint64(r.Dx()), uint64(r.Dy())
	// See comment in Resize.
	n, sum := dx*dy, make([]uint64, 4*w*h)
	for y := r.Min.Y; y < r.Max.Y; y++ {
		Y := m.Y[y*m.YStride:]
		Cb := m.Cb[y/verticalRes*m.CStride:]
		Cr := m.Cr[y/verticalRes*m.CStride:]
		for x := r.Min.X; x < r.Max.X; x++ {
			// Get the source pixel.
			r8, g8, b8 := color.YCbCrToRGB(Y[x], Cb[x/2], Cr[x/2])
			r64 := uint64(r8)
			g64 := uint64(g8)
			b64 := uint64(b8)
			// Spread the source pixel over 1 or more destination rows.
			py := uint64(y) * hh
			for remy := hh; remy > 0; {
				qy := dy - (py % dy)
				if qy > remy {
					qy = remy
				}
				// Spread the source pixel over 1 or more destination columns.
				px := uint64(x) * ww
				index := 4 * ((py/dy)*ww + (px / dx))
				for remx := ww; remx > 0; {
					qx := dx - (px % dx)
					if qx > remx {
						qx = remx
					}
					qxy := qx * qy
					sum[index+0] += r64 * qxy
					sum[index+1] += g64 * qxy
					sum[index+2] += b64 * qxy
					sum[index+3] += 0xFFFF * qxy
					index += 4
					px += qx
					remx -= qx
				}
				py += qy
				remy -= qy
			}
		}
	}
	return average(sum, w, h, n), true
}

// resizeRGBA returns a scaled copy of the RGBA image slice r of m.
// The returned image has width w and height h.
func resizeRGBA(m *image.RGBA, r image.Rectangle, w, h int) image.Image {
	ww, hh := uint64(w), uint64(h)
	dx, dy := uint64(r.Dx()), uint64(r.Dy())
	// See comment in Resize.
	n, sum := dx*dy, make([]uint64, 4*w*h)
	for y := r.Min.Y; y < r.Max.Y; y++ {
		pixOffset := m.PixOffset(r.Min.X, y)
		for x := r.Min.X; x < r.Max.X; x++ {
			// Get the source pixel.
			r64 := uint64(m.Pix[pixOffset+0])
			g64 := uint64(m.Pix[pixOffset+1])
			b64 := uint64(m.Pix[pixOffset+2])
			a64 := uint64(m.Pix[pixOffset+3])
			pixOffset += 4
			// Spread the source pixel over 1 or more destination rows.
			py := uint64(y) * hh
			for remy := hh; remy > 0; {
				qy := dy - (py % dy)
				if qy > remy {
					qy = remy
				}
				// Spread the source pixel over 1 or more destination columns.
				px := uint64(x) * ww
				index := 4 * ((py/dy)*ww + (px / dx))
				for remx := ww; remx > 0; {
					qx := dx - (px % dx)
					if qx > remx {
						qx = remx
					}
					qxy := qx * qy
					sum[index+0] += r64 * qxy
					sum[index+1] += g64 * qxy
					sum[index+2] += b64 * qxy
					sum[index+3] += a64 * qxy
					index += 4
					px += qx
					remx -= qx
				}
				py += qy
				remy -= qy
			}
		}
	}
	return average(sum, w, h, n)
}

// Resample returns a resampled copy of the image slice r of m.
// The returned image has width w and height h.
func ResampleImage(m image.Image, r image.Rectangle, w, h int) image.Image {
	if w < 0 || h < 0 {
		return nil
	}
	if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 {
		return image.NewRGBA64(image.Rect(0, 0, w, h))
	}
	curw, curh := r.Dx(), r.Dy()
	img := image.NewRGBA(image.Rect(0, 0, w, h))
	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			// Get a source pixel.
			subx := x * curw / w
			suby := y * curh / h
			r32, g32, b32, a32 := m.At(subx, suby).RGBA()
			r := uint8(r32 >> 8)
			g := uint8(g32 >> 8)
			b := uint8(b32 >> 8)
			a := uint8(a32 >> 8)
			img.SetRGBA(x, y, color.RGBA{r, g, b, a})
		}
	}
	return img
}
<commit_msg>resample image bug fix - aligning image<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package media

// An alternative would be: code.google.com/p/graphics-go/graphics

import (
	"image"
	"image/color"
)

// Resize returns a scaled copy of the image slice r of m.
// The returned image has width w and height h.
func _ResizeImage(m image.Image, r image.Rectangle, w, h int) image.Image {
	if w < 0 || h < 0 {
		return nil
	}
	if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 {
		return image.NewRGBA64(image.Rect(0, 0, w, h))
	}
	switch m := m.(type) {
	case *image.RGBA:
		return resizeRGBA(m, r, w, h)
	case *image.YCbCr:
		if m, ok := resizeYCbCr(m, r, w, h); ok {
			return m
		}
	}
	ww, hh := uint64(w), uint64(h)
	dx, dy := uint64(r.Dx()), uint64(r.Dy())
	// The scaling algorithm is to nearest-neighbor magnify the dx * dy source
	// to a (ww*dx) * (hh*dy) intermediate image and then minify the intermediate
	// image back down to a ww * hh destination with a simple box filter.
	// The intermediate image is implied, we do not physically allocate a slice
	// of length ww*dx*hh*dy.
	// For example, consider a 4*3 source image. Label its pixels from a-l:
	//	abcd
	//	efgh
	//	ijkl
	// To resize this to a 3*2 destination image, the intermediate is 12*6.
	// Whitespace has been added to delineate the destination pixels:
	//	aaab bbcc cddd
	//	aaab bbcc cddd
	//	eeef ffgg ghhh
	//
	//	eeef ffgg ghhh
	//	iiij jjkk klll
	//	iiij jjkk klll
	// Thus, the 'b' source pixel contributes one third of its value to the
	// (0, 0) destination pixel and two thirds to (1, 0).
	// The implementation is a two-step process. First, the source pixels are
	// iterated over and each source pixel's contribution to 1 or more
	// destination pixels are summed. Second, the sums are divided by a scaling
	// factor to yield the destination pixels.
	// TODO: By interleaving the two steps, instead of doing all of
	// step 1 first and all of step 2 second, we could allocate a smaller sum
	// slice of length 4*w*2 instead of 4*w*h, although the resultant code
	// would become more complicated.
	n, sum := dx*dy, make([]uint64, 4*w*h)
	for y := r.Min.Y; y < r.Max.Y; y++ {
		for x := r.Min.X; x < r.Max.X; x++ {
			// Get the source pixel.
			r32, g32, b32, a32 := m.At(x, y).RGBA()
			r64 := uint64(r32)
			g64 := uint64(g32)
			b64 := uint64(b32)
			a64 := uint64(a32)
			// Spread the source pixel over 1 or more destination rows.
			py := uint64(y) * hh
			for remy := hh; remy > 0; {
				qy := dy - (py % dy)
				if qy > remy {
					qy = remy
				}
				// Spread the source pixel over 1 or more destination columns.
				px := uint64(x) * ww
				index := 4 * ((py/dy)*ww + (px / dx))
				for remx := ww; remx > 0; {
					qx := dx - (px % dx)
					if qx > remx {
						qx = remx
					}
					sum[index+0] += r64 * qx * qy
					sum[index+1] += g64 * qx * qy
					sum[index+2] += b64 * qx * qy
					sum[index+3] += a64 * qx * qy
					index += 4
					px += qx
					remx -= qx
				}
				py += qy
				remy -= qy
			}
		}
	}
	return average(sum, w, h, n*0x0101)
}

// average convert the sums to averages and returns the result.
func average(sum []uint64, w, h int, n uint64) image.Image {
	ret := image.NewRGBA(image.Rect(0, 0, w, h))
	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			index := 4 * (y*w + x)
			ret.SetRGBA(x, y, color.RGBA{
				uint8(sum[index+0] / n),
				uint8(sum[index+1] / n),
				uint8(sum[index+2] / n),
				uint8(sum[index+3] / n),
			})
		}
	}
	return ret
}

// resizeYCbCr returns a scaled copy of the YCbCr image slice r of m.
// The returned image has width w and height h.
func resizeYCbCr(m *image.YCbCr, r image.Rectangle, w, h int) (image.Image, bool) {
	var verticalRes int
	switch m.SubsampleRatio {
	case image.YCbCrSubsampleRatio420:
		verticalRes = 2
	case image.YCbCrSubsampleRatio422:
		verticalRes = 1
	default:
		return nil, false
	}
	ww, hh := uint64(w), uint64(h)
	dx, dy := uint64(r.Dx()), uint64(r.Dy())
	// See comment in Resize.
	n, sum := dx*dy, make([]uint64, 4*w*h)
	for y := r.Min.Y; y < r.Max.Y; y++ {
		Y := m.Y[y*m.YStride:]
		Cb := m.Cb[y/verticalRes*m.CStride:]
		Cr := m.Cr[y/verticalRes*m.CStride:]
		for x := r.Min.X; x < r.Max.X; x++ {
			// Get the source pixel.
			r8, g8, b8 := color.YCbCrToRGB(Y[x], Cb[x/2], Cr[x/2])
			r64 := uint64(r8)
			g64 := uint64(g8)
			b64 := uint64(b8)
			// Spread the source pixel over 1 or more destination rows.
			py := uint64(y) * hh
			for remy := hh; remy > 0; {
				qy := dy - (py % dy)
				if qy > remy {
					qy = remy
				}
				// Spread the source pixel over 1 or more destination columns.
				px := uint64(x) * ww
				index := 4 * ((py/dy)*ww + (px / dx))
				for remx := ww; remx > 0; {
					qx := dx - (px % dx)
					if qx > remx {
						qx = remx
					}
					qxy := qx * qy
					sum[index+0] += r64 * qxy
					sum[index+1] += g64 * qxy
					sum[index+2] += b64 * qxy
					sum[index+3] += 0xFFFF * qxy
					index += 4
					px += qx
					remx -= qx
				}
				py += qy
				remy -= qy
			}
		}
	}
	return average(sum, w, h, n), true
}

// resizeRGBA returns a scaled copy of the RGBA image slice r of m.
// The returned image has width w and height h.
func resizeRGBA(m *image.RGBA, r image.Rectangle, w, h int) image.Image {
	ww, hh := uint64(w), uint64(h)
	dx, dy := uint64(r.Dx()), uint64(r.Dy())
	// See comment in Resize.
	n, sum := dx*dy, make([]uint64, 4*w*h)
	for y := r.Min.Y; y < r.Max.Y; y++ {
		pixOffset := m.PixOffset(r.Min.X, y)
		for x := r.Min.X; x < r.Max.X; x++ {
			// Get the source pixel.
			r64 := uint64(m.Pix[pixOffset+0])
			g64 := uint64(m.Pix[pixOffset+1])
			b64 := uint64(m.Pix[pixOffset+2])
			a64 := uint64(m.Pix[pixOffset+3])
			pixOffset += 4
			// Spread the source pixel over 1 or more destination rows.
			py := uint64(y) * hh
			for remy := hh; remy > 0; {
				qy := dy - (py % dy)
				if qy > remy {
					qy = remy
				}
				// Spread the source pixel over 1 or more destination columns.
				px := uint64(x) * ww
				index := 4 * ((py/dy)*ww + (px / dx))
				for remx := ww; remx > 0; {
					qx := dx - (px % dx)
					if qx > remx {
						qx = remx
					}
					qxy := qx * qy
					sum[index+0] += r64 * qxy
					sum[index+1] += g64 * qxy
					sum[index+2] += b64 * qxy
					sum[index+3] += a64 * qxy
					index += 4
					px += qx
					remx -= qx
				}
				py += qy
				remy -= qy
			}
		}
	}
	return average(sum, w, h, n)
}

// Resample returns a resampled copy of the image slice r of m.
// The returned image has width w and height h.
func ResampleImage(m image.Image, r image.Rectangle, w, h int) image.Image {
	if w < 0 || h < 0 {
		return nil
	}
	if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 {
		return image.NewRGBA64(image.Rect(0, 0, w, h))
	}
	curw, curh := r.Dx(), r.Dy()
	img := image.NewRGBA(image.Rect(0, 0, w, h))
	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			// Get a source pixel.
			subx := r.Min.X + x*curw/w
			suby := r.Min.Y + y*curh/h
			r32, g32, b32, a32 := m.At(subx, suby).RGBA()
			r := uint8(r32 >> 8)
			g := uint8(g32 >> 8)
			b := uint8(b32 >> 8)
			a := uint8(a32 >> 8)
			img.SetRGBA(x, y, color.RGBA{r, g, b, a})
		}
	}
	return img
}
<|endoftext|>


<commit_before>/*
 * This file is part of remco.
 * Based on code from confd.
 * https://github.com/kelseyhightower/confd/blob/30663b9822fe8e800d1f2ea78447fba0ebce8f6c/resource/template/resource.go
 * Users who have contributed to this file
 * © 2013 Kelsey Hightower
 * © 2014 Armon Dadgar
 * © 2014 Ernesto Jimenez
 * © 2014 Nathan Fritz
 * © 2014 John Engelman
 * © 2014 Joanna Solmon
 * © 2014 Chris Armstrong
 * © 2014 Chris McNabb
 * © 2015 Phil Kates
 * © 2015 Matthew Fisher
 *
 * © 2016 The Remco Authors
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

package template

import (
	"context"
	"errors"
	"path"
	"strings"
	"sync"
	"time"

	"github.com/HeavyHorst/memkv"
	berr "github.com/HeavyHorst/remco/backends/error"
	"github.com/HeavyHorst/remco/log"
	"github.com/Sirupsen/logrus"
)

// Resource is the representation of a parsed template resource.
type Resource struct {
	backends []Backend
	funcMap  map[string]interface{}
	store    memkv.Store
	sources  []*Processor
	logger   *logrus.Entry
}

// ErrEmptySrc is returned if an emty src template is passed to NewResource
var ErrEmptySrc = errors.New("empty src template")

// NewResource creates a Resource.
func NewResource(backends []Backend, sources []*Processor, name string) (*Resource, error) {
	if len(backends) == 0 {
		return nil, errors.New("A valid StoreClient is required.")
	}

	logger := log.WithFields(logrus.Fields{"resource": name})

	for _, v := range sources {
		if v.Src == "" {
			return nil, ErrEmptySrc
		}
		v.logger = logger
	}

	tr := &Resource{
		backends: backends,
		store:    memkv.New(),
		funcMap:  newFuncMap(),
		sources:  sources,
		logger:   logger,
	}

	// initialize the inidividual backend memkv Stores
	for i := range tr.backends {
		store := memkv.New()
		tr.backends[i].store = &store

		if tr.backends[i].Interval <= 0 && tr.backends[i].Onetime == false && tr.backends[i].Watch == false {
			logger.Warning("Interval needs to be > 0: setting interval to 60")
			tr.backends[i].Interval = 60
		}
	}

	addFuncs(tr.funcMap, tr.store.FuncMap)

	return tr, nil
}

// Close is calling the close method on all backends
func (t *Resource) Close() {
	for _, v := range t.backends {
		t.logger.WithFields(logrus.Fields{
			"backend": v.Name,
		}).Debug("Closing client connection")
		v.Close()
	}
}

// setVars sets the Vars for a template resource.
func (t *Resource) setVars(storeClient Backend) error {
	var err error

	t.logger.WithFields(logrus.Fields{
		"backend":    storeClient.Name,
		"key_prefix": storeClient.Prefix,
	}).Debug("Retrieving keys")

	result, err := storeClient.GetValues(appendPrefix(storeClient.Prefix, storeClient.Keys))
	if err != nil {
		return err
	}

	storeClient.store.Purge()

	for key, value := range result {
		storeClient.store.Set(path.Join("/", strings.TrimPrefix(key, storeClient.Prefix)), value)
	}

	//merge all stores
	t.store.Purge()
	for _, v := range t.backends {
		for _, kv := range v.store.GetAllKVs() {
			if t.store.Exists(kv.Key) {
				t.logger.Warning("Key collision - " + kv.Key)
			}
			t.store.Set(kv.Key, kv.Value)
		}
	}

	return nil
}

func (t *Resource) createStageFileAndSync() error {
	for _, s := range t.sources {
		err := s.createStageFile(t.funcMap)
		if err != nil {
			return err
		}
		if err = s.syncFiles(); err != nil {
			return err
		}
	}
	return nil
}

// Process is a convenience function that wraps calls to the three main tasks
// required to keep local configuration files in sync. First we gather vars
// from the store, then we stage a candidate configuration file, and finally sync
// things up.
// It returns an error if any.
func (t *Resource) process(storeClients []Backend) error {
	for _, storeClient := range storeClients {
		if err := t.setVars(storeClient); err != nil {
			return berr.BackendError{
				Message: err.Error(),
				Backend: storeClient.Name,
			}
		}
	}
	if err := t.createStageFileAndSync(); err != nil {
		return err
	}
	return nil
}

// Monitor will start to monitor all given Backends for changes.
// It will process all given tamplates on changes.
func (t *Resource) Monitor(ctx context.Context) {
	wg := &sync.WaitGroup{}

	processChan := make(chan Backend)
	defer close(processChan)
	errChan := make(chan berr.BackendError, 10)

	// try to process the template resource with all given backends
	// we wait 2 seconds and try again (with all backends - no stale data)
	// if some error occurs
retryloop:
	for {
		select {
		case <-ctx.Done():
			return
		default:
			if err := t.process(t.backends); err != nil {
				switch err.(type) {
				case berr.BackendError:
					err := err.(berr.BackendError)
					t.logger.WithFields(logrus.Fields{
						"backend": err.Backend,
					}).Error(err)
				default:
					log.Error(err)
				}
				time.Sleep(2 * time.Second)
				continue retryloop
			}
			break retryloop
		}
	}

	for _, sc := range t.backends {
		if sc.Watch {
			wg.Add(1)
			go func(s Backend) {
				defer wg.Done()
				s.watch(ctx, processChan, errChan)
			}(sc)
		}

		if sc.Interval > 0 {
			wg.Add(1)
			go func(s Backend) {
				defer wg.Done()
				s.interval(ctx, processChan)
			}(sc)
		}
	}

	done := make(chan struct{})
	go func() {
		// If there is no goroutine left - quit
		wg.Wait()
		close(done)
	}()

	for {
		select {
		case storeClient := <-processChan:
			if err := t.process([]Backend{storeClient}); err != nil {
				if _, ok := err.(berr.BackendError); ok {
					t.logger.WithFields(logrus.Fields{
						"backend": storeClient.Name,
					}).Error(err)
				} else {
					log.Error(err)
				}
			}
		case err := <-errChan:
			t.logger.WithFields(logrus.Fields{
				"backend": err.Backend,
			}).Error(err.Message)
		case <-ctx.Done():
			go func() {
				for range processChan {
				}
			}()
			wg.Wait()
			return
		case <-done:
			return
		}
	}
}
<commit_msg>resource.go: was using the wrong logger<commit_after>/*
 * This file is part of remco.
 * Based on code from confd.
 * https://github.com/kelseyhightower/confd/blob/30663b9822fe8e800d1f2ea78447fba0ebce8f6c/resource/template/resource.go
 * Users who have contributed to this file
 * © 2013 Kelsey Hightower
 * © 2014 Armon Dadgar
 * © 2014 Ernesto Jimenez
 * © 2014 Nathan Fritz
 * © 2014 John Engelman
 * © 2014 Joanna Solmon
 * © 2014 Chris Armstrong
 * © 2014 Chris McNabb
 * © 2015 Phil Kates
 * © 2015 Matthew Fisher
 *
 * © 2016 The Remco Authors
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

package template

import (
	"context"
	"errors"
	"path"
	"strings"
	"sync"
	"time"

	"github.com/HeavyHorst/memkv"
	berr "github.com/HeavyHorst/remco/backends/error"
	"github.com/HeavyHorst/remco/log"
	"github.com/Sirupsen/logrus"
)

// Resource is the representation of a parsed template resource.
type Resource struct {
	backends []Backend
	funcMap  map[string]interface{}
	store    memkv.Store
	sources  []*Processor
	logger   *logrus.Entry
}

// ErrEmptySrc is returned if an emty src template is passed to NewResource
var ErrEmptySrc = errors.New("empty src template")

// NewResource creates a Resource.
func NewResource(backends []Backend, sources []*Processor, name string) (*Resource, error) {
	if len(backends) == 0 {
		return nil, errors.New("A valid StoreClient is required.")
	}

	logger := log.WithFields(logrus.Fields{"resource": name})

	for _, v := range sources {
		if v.Src == "" {
			return nil, ErrEmptySrc
		}
		v.logger = logger
	}

	tr := &Resource{
		backends: backends,
		store:    memkv.New(),
		funcMap:  newFuncMap(),
		sources:  sources,
		logger:   logger,
	}

	// initialize the inidividual backend memkv Stores
	for i := range tr.backends {
		store := memkv.New()
		tr.backends[i].store = &store

		if tr.backends[i].Interval <= 0 && tr.backends[i].Onetime == false && tr.backends[i].Watch == false {
			logger.Warning("Interval needs to be > 0: setting interval to 60")
			tr.backends[i].Interval = 60
		}
	}

	addFuncs(tr.funcMap, tr.store.FuncMap)

	return tr, nil
}

// Close is calling the close method on all backends
func (t *Resource) Close() {
	for _, v := range t.backends {
		t.logger.WithFields(logrus.Fields{
			"backend": v.Name,
		}).Debug("Closing client connection")
		v.Close()
	}
}

// setVars sets the Vars for a template resource.
func (t *Resource) setVars(storeClient Backend) error {
	var err error

	t.logger.WithFields(logrus.Fields{
		"backend":    storeClient.Name,
		"key_prefix": storeClient.Prefix,
	}).Debug("Retrieving keys")

	result, err := storeClient.GetValues(appendPrefix(storeClient.Prefix, storeClient.Keys))
	if err != nil {
		return err
	}

	storeClient.store.Purge()

	for key, value := range result {
		storeClient.store.Set(path.Join("/", strings.TrimPrefix(key, storeClient.Prefix)), value)
	}

	//merge all stores
	t.store.Purge()
	for _, v := range t.backends {
		for _, kv := range v.store.GetAllKVs() {
			if t.store.Exists(kv.Key) {
				t.logger.Warning("Key collision - " + kv.Key)
			}
			t.store.Set(kv.Key, kv.Value)
		}
	}

	return nil
}

func (t *Resource) createStageFileAndSync() error {
	for _, s := range t.sources {
		err := s.createStageFile(t.funcMap)
		if err != nil {
			return err
		}
		if err = s.syncFiles(); err != nil {
			return err
		}
	}
	return nil
}

// Process is a convenience function that wraps calls to the three main tasks
// required to keep local configuration files in sync. First we gather vars
// from the store, then we stage a candidate configuration file, and finally sync
// things up.
// It returns an error if any.
func (t *Resource) process(storeClients []Backend) error {
	for _, storeClient := range storeClients {
		if err := t.setVars(storeClient); err != nil {
			return berr.BackendError{
				Message: err.Error(),
				Backend: storeClient.Name,
			}
		}
	}
	if err := t.createStageFileAndSync(); err != nil {
		return err
	}
	return nil
}

// Monitor will start to monitor all given Backends for changes.
// It will process all given tamplates on changes.
func (t *Resource) Monitor(ctx context.Context) {
	wg := &sync.WaitGroup{}

	processChan := make(chan Backend)
	defer close(processChan)
	errChan := make(chan berr.BackendError, 10)

	// try to process the template resource with all given backends
	// we wait 2 seconds and try again (with all backends - no stale data)
	// if some error occurs
retryloop:
	for {
		select {
		case <-ctx.Done():
			return
		default:
			if err := t.process(t.backends); err != nil {
				switch err.(type) {
				case berr.BackendError:
					err := err.(berr.BackendError)
					t.logger.WithFields(logrus.Fields{
						"backend": err.Backend,
					}).Error(err)
				default:
					t.logger.Error(err)
				}
				time.Sleep(2 * time.Second)
				continue retryloop
			}
			break retryloop
		}
	}

	for _, sc := range t.backends {
		if sc.Watch {
			wg.Add(1)
			go func(s Backend) {
				defer wg.Done()
				s.watch(ctx, processChan, errChan)
			}(sc)
		}

		if sc.Interval > 0 {
			wg.Add(1)
			go func(s Backend) {
				defer wg.Done()
				s.interval(ctx, processChan)
			}(sc)
		}
	}

	done := make(chan struct{})
	go func() {
		// If there is no goroutine left - quit
		wg.Wait()
		close(done)
	}()

	for {
		select {
		case storeClient := <-processChan:
			if err := t.process([]Backend{storeClient}); err != nil {
				if _, ok := err.(berr.BackendError); ok {
					t.logger.WithFields(logrus.Fields{
						"backend": storeClient.Name,
					}).Error(err)
				} else {
					log.Error(err)
				}
			}
		case err := <-errChan:
			t.logger.WithFields(logrus.Fields{
				"backend": err.Backend,
			}).Error(err.Message)
		case <-ctx.Done():
			go func() {
				for range processChan {
				}
			}()
			wg.Wait()
			return
		case <-done:
			return
		}
	}
}
<|endoftext|>


<commit_before>package template

import (
	"fmt"
	"github.com/hoisie/mustache"
	"github.com/russross/blackfriday"
	"monet/conf"
	"os"
	"path/filepath"
	"strings"
)

type dict map[string]interface{}

type Base struct {
	Path string
}

var templates = map[string]*mustache.Template{}
var templatePaths = map[string]string{}

func (b *Base) Render(t string, c ...interface{}) string {
	body := Render(t, c...)
	newc := append(c, dict{"body": body})
	return Render(b.Path, newc...)
}

func Render(t string, c ...interface{}) string {
	// add the Config to all of our template rendering
	c = append(c, dict{"Debug": conf.Config.Debug})

	if conf.Config.TemplatePreCompile {
		template := templates[t]
		if template == nil {
			fmt.Printf("Error: template %s not found\n", t)
			return ""
		}
		return template.Render(c...)
	}
	path := templatePaths[t]
	if len(path) < 0 {
		fmt.Printf("Error: template %s not found\n", t)
		return ""
	}
	template, err := mustache.ParseFile(path)
	if err != nil {
		fmt.Println(err)
		return ""
	}
	return template.Render(c...)
}

func LoadDir(dir string) int {
	numTemplates := 0
	walker := func(path string, info os.FileInfo, err error) error {
		name, isDir := info.Name(), info.IsDir()
		if isDir {
			return nil
		}
		if name[0] == '.' {
			return nil
		}
		tname := strings.Join(strings.Split(path, "/")[1:], "/")
		templatePaths[tname] = path
		// if template pre-compilation is on, compile these and store
		if conf.Config.TemplatePreCompile {
			templates[tname], err = mustache.ParseFile(path)
			if err != nil {
				fmt.Println(err)
			}
		}
		numTemplates++
		return nil
	}
	filepath.Walk(dir, walker)
	return numTemplates
}

func Init(templatePaths []string) {
	for _, path := range templatePaths {
		num := LoadDir(path)
		fmt.Printf("Loaded %d templates from %s\n", num, path)
	}
}

func RenderMarkdown(source string) string {
	var flags int
	var extensions int
	extensions |= blackfriday.EXTENSION_NO_INTRA_EMPHASIS
	extensions |= blackfriday.EXTENSION_TABLES
	extensions |= blackfriday.EXTENSION_FENCED_CODE
	extensions |= blackfriday.EXTENSION_AUTOLINK
	extensions |= blackfriday.EXTENSION_STRIKETHROUGH
	extensions |= blackfriday.EXTENSION_SPACE_HEADERS
	extensions |= blackfriday.EXTENSION_HARD_LINE_BREAK
	flags |= blackfriday.HTML_GITHUB_BLOCKCODE
	flags |= blackfriday.HTML_SAFELINK
	renderer := blackfriday.HtmlRenderer(flags, "", "")
	return string(blackfriday.Markdown([]byte(source), renderer, extensions))
}
<commit_msg>more fixing of import paths for monet<commit_after>package template

import (
	"fmt"
	"github.com/hoisie/mustache"
	"github.com/jmoiron/monet/conf"
	"github.com/russross/blackfriday"
	"os"
	"path/filepath"
	"strings"
)

type dict map[string]interface{}

type Base struct {
	Path string
}

var templates = map[string]*mustache.Template{}
var templatePaths = map[string]string{}

func (b *Base) Render(t string, c ...interface{}) string {
	body := Render(t, c...)
	newc := append(c, dict{"body": body})
	return Render(b.Path, newc...)
}

func Render(t string, c ...interface{}) string {
	// add the Config to all of our template rendering
	c = append(c, dict{"Debug": conf.Config.Debug})

	if conf.Config.TemplatePreCompile {
		template := templates[t]
		if template == nil {
			fmt.Printf("Error: template %s not found\n", t)
			return ""
		}
		return template.Render(c...)
	}
	path := templatePaths[t]
	if len(path) < 0 {
		fmt.Printf("Error: template %s not found\n", t)
		return ""
	}
	template, err := mustache.ParseFile(path)
	if err != nil {
		fmt.Println(err)
		return ""
	}
	return template.Render(c...)
}

func LoadDir(dir string) int {
	numTemplates := 0
	walker := func(path string, info os.FileInfo, err error) error {
		name, isDir := info.Name(), info.IsDir()
		if isDir {
			return nil
		}
		if name[0] == '.' {
			return nil
		}
		tname := strings.Join(strings.Split(path, "/")[1:], "/")
		templatePaths[tname] = path
		// if template pre-compilation is on, compile these and store
		if conf.Config.TemplatePreCompile {
			templates[tname], err = mustache.ParseFile(path)
			if err != nil {
				fmt.Println(err)
			}
		}
		numTemplates++
		return nil
	}
	filepath.Walk(dir, walker)
	return numTemplates
}

func Init(templatePaths []string) {
	for _, path := range templatePaths {
		num := LoadDir(path)
		fmt.Printf("Loaded %d templates from %s\n", num, path)
	}
}

func RenderMarkdown(source string) string {
	var flags int
	var extensions int
	extensions |= blackfriday.EXTENSION_NO_INTRA_EMPHASIS
	extensions |= blackfriday.EXTENSION_TABLES
	extensions |= blackfriday.EXTENSION_FENCED_CODE
	extensions |= blackfriday.EXTENSION_AUTOLINK
	extensions |= blackfriday.EXTENSION_STRIKETHROUGH
	extensions |= blackfriday.EXTENSION_SPACE_HEADERS
	extensions |= blackfriday.EXTENSION_HARD_LINE_BREAK
	flags |= blackfriday.HTML_GITHUB_BLOCKCODE
	flags |= blackfriday.HTML_SAFELINK
	renderer := blackfriday.HtmlRenderer(flags, "", "")
	return string(blackfriday.Markdown([]byte(source), renderer, extensions))
}
<|endoftext|>


<commit_before>package template

import (
	"errors"
	"regexp"

	"github.com/flosch/pongo2"
	"github.com/odedlaz/tpl/config"

	tplos "github.com/odedlaz/tpl/os"
)

// Filters all tpl registered filters
var Filters = []string{}

// Settings global settings
var (
	Settings             = config.Settings{}
	stripEmptyLinesRegex = regexp.MustCompile("\n\n")
)

// Must panics if err != nil. otherwise, returns the text
func Must(txt string, err error) string {
	if err != nil {
		panic(err)
	}
	return txt
}

// Read reads the template from a file,
// or if it doesn't exit, tries to read it from stdin
func Read(filename string) (string, error) {
	if filename == "" && !tplos.StdInAvailable() {
		return "", errors.New("you have to pipe text or pass a filename argument")
	}

	// if a filename was passed - use it instead of the pipe
	if filename != "" {
		return tplos.ReadFile(filename)
	}

	// fallback - read from pipe
	return tplos.ReadFromStdIn(), nil
}

// Execute transforms a template text into whatever it needed to be
func Execute(tpltxt string) (string, error) {
	tpl, err := pongo2.FromString(tpltxt)
	if err != nil {
		return "", err
	}

	txt, err := tpl.Execute(pongo2.Context{})
	if err != nil {
		return "", err
	}

	return stripEmptyLinesRegex.ReplaceAllString(txt, "\n"), nil
}

// RegisterFilter adds a new filter to pongo2
func RegisterFilter(name string, fn pongo2.FilterFunction) {
	pongo2.RegisterFilter(name, fn)
	Filters = append(Filters, name)
}

// RegisterSettings adds a global settings to pongo
func RegisterSettings(settings *config.Settings) {
	Settings = *settings
}
<commit_msg>whitepspace control: fixed double empty line removal. double empty lines are replaces by one line, even if there are spaces between them.<commit_after>package template

import (
	"errors"
	"regexp"
	"strings"

	"github.com/flosch/pongo2"
	"github.com/odedlaz/tpl/config"

	tplos "github.com/odedlaz/tpl/os"
)

// Filters all tpl registered filters
var Filters = []string{}

// Settings global settings
var (
	Settings = config.Settings{}
	// remove any empty lines (even if there are spaces between them)
	stripEmptyLinesRegex = regexp.MustCompile("\n\\s*\n\\s*\n")
)

// Must panics if err != nil. otherwise, returns the text
func Must(txt string, err error) string {
	if err != nil {
		panic(err)
	}
	return txt
}

// Read reads the template from a file,
// or if it doesn't exit, tries to read it from stdin
func Read(filename string) (string, error) {
	if filename == "" && !tplos.StdInAvailable() {
		return "", errors.New("you have to pipe text or pass a filename argument")
	}

	// if a filename was passed - use it instead of the pipe
	if filename != "" {
		return tplos.ReadFile(filename)
	}

	// fallback - read from pipe
	return tplos.ReadFromStdIn(), nil
}

// Execute transforms a template text into whatever it needed to be
func Execute(tpltxt string) (string, error) {
	tpl, err := pongo2.FromString(tpltxt)
	if err != nil {
		return "", err
	}

	txt, err := tpl.Execute(pongo2.Context{})
	if err != nil {
		return "", err
	}
	txt = stripEmptyLinesRegex.ReplaceAllString(txt, "\n")
	return strings.TrimLeft(txt, "\n"), nil
}

// RegisterFilter adds a new filter to pongo2
func RegisterFilter(name string, fn pongo2.FilterFunction) {
	pongo2.RegisterFilter(name, fn)
	Filters = append(Filters, name)
}

// RegisterSettings adds a global settings to pongo
func RegisterSettings(settings *config.Settings) {
	Settings = *settings
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/md5"
	"errors"
	"fmt"
	"github.com/BurntSushi/toml"
	"github.com/kelseyhightower/confd/log"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"syscall"
	"text/template"
)

// TemplateResourceConfig holds the parsed template resource.
type TemplateResourceConfig struct {
	TemplateResource TemplateResource `toml:"template"`
}

// TemplateResource is the representation of a parsed template resource.
type TemplateResource struct {
	Dest       string
	FileMode   os.FileMode
	Gid        int
	Keys       []string
	Mode       string
	Uid        int
	ReloadCmd  string `toml:"reload_cmd"`
	CheckCmd   string `toml:"check_cmd"`
	StageFile  *os.File
	Src        string
	Vars       map[string]interface{}
	etcdClient EtcdClient
}

// NewTemplateResourceFromPath creates a TemplateResource using a decoded file path
// and the supplied EtcdClient as input.
// It returns a TemplateResource and an error if any.
func NewTemplateResourceFromPath(path string, c EtcdClient) (*TemplateResource, error) {
	if c == nil {
		return nil, errors.New("A valid EtcdClient is required.")
	}
	var tc *TemplateResourceConfig
	_, err := toml.DecodeFile(path, &tc)
	if err != nil {
		return nil, fmt.Errorf("Cannot process template resource %s - %s", path, err.Error())
	}
	tc.TemplateResource.etcdClient = c
	return &tc.TemplateResource, nil
}

// setVars sets the Vars for template resource.
func (t *TemplateResource) setVars() error {
	var err error
	t.Vars, err = getValues(t.etcdClient, Prefix(), t.Keys)
	if err != nil {
		return err
	}
	return nil
}

// createStageFile stages the src configuration file by processing the src
// template and setting the desired owner, group, and mode. It also sets the
// StageFile for the template resource.
// It returns an error if any.
func (t *TemplateResource) createStageFile() error {
	t.Src = filepath.Join(TemplateDir(), t.Src)
	if !IsFileExist(t.Src) {
		return errors.New("Missing template: " + t.Src)
	}
	temp, err := ioutil.TempFile("", "")
	if err != nil {
		os.Remove(temp.Name())
		return err
	}
	tmpl := template.Must(template.ParseFiles(t.Src))
	if err = tmpl.Execute(temp, t.Vars); err != nil {
		return err
	}
	// Set the owner, group, and mode on the stage file now to make it easier to
	// compare against the destination configuration file later.
	os.Chmod(temp.Name(), t.FileMode)
	os.Chown(temp.Name(), t.Uid, t.Gid)
	t.StageFile = temp
	return nil
}

// sync compares the staged and dest config files and attempts to sync them
// if they differ. sync will run a config check command if set before
// overwriting the target config file. Finally, sync will run a reload command
// if set to have the application or service pick up the changes.
// It returns an error if any.
func (t *TemplateResource) sync() error {
	staged := t.StageFile.Name()
	defer os.Remove(staged)
	err, ok := sameConfig(staged, t.Dest)
	if err != nil {
		log.Error(err.Error())
	}
	if !ok {
		log.Info(t.Dest + " not in sync")
		if t.CheckCmd != "" {
			if err := t.check(); err != nil {
				return errors.New("Config check failed: " + err.Error())
			}
		}
		os.Rename(staged, t.Dest)
		if t.ReloadCmd != "" {
			if err := t.reload(); err != nil {
				return err
			}
		}
	} else {
		log.Info(t.Dest + " in sync")
	}
	return nil
}

// check executes the check command to validate the staged config file. The
// command is modified so that any references to src template are substituted
// with a string representing the full path of the staged file. This allows the
// check to be run on the staged file before overwriting the destination config
// file.
// It returns nil if the check command returns 0 and there are no other errors.
func (t *TemplateResource) check() error {
	var cmdBuffer bytes.Buffer
	data := make(map[string]string)
	data["src"] = t.StageFile.Name()
	tmpl, err := template.New("checkcmd").Parse(t.CheckCmd)
	if err != nil {
		return err
	}
	if err := tmpl.Execute(&cmdBuffer, data); err != nil {
		return err
	}
	log.Debug("Running " + cmdBuffer.String())
	c := exec.Command("/bin/sh", "-c", cmdBuffer.String())
	if err := c.Run(); err != nil {
		return err
	}
	return nil
}

// reload executes the reload command.
// It returns nil if the reload command returns 0.
func (t *TemplateResource) reload() error {
	log.Debug("Running " + t.ReloadCmd)
	c := exec.Command("/bin/sh", "-c", t.ReloadCmd)
	if err := c.Run(); err != nil {
		return err
	}
	return nil
}

// process is a convenience function that wraps calls to the three main tasks
// required to keep local configuration files in sync. First we gather vars
// from etcd, then we stage a candidate configuration file, and finally sync
// things up.
// It returns an error if any.
func (t *TemplateResource) process() error {
	if err := t.setFileMode(); err != nil {
		return err
	}
	if err := t.setVars(); err != nil {
		return err
	}
	if err := t.createStageFile(); err != nil {
		return err
	}
	if err := t.sync(); err != nil {
		return err
	}
	return nil
}

// setFileMode sets the FileMode.
// It returns an error if any.
func (t *TemplateResource) setFileMode() error {
	if t.Mode == "" {
		if !IsFileExist(t.Dest) {
			t.FileMode = 0644
		} else {
			fi, err := os.Stat(t.Dest)
			if err != nil {
				return err
			}
			t.FileMode = fi.Mode()
		}
	} else {
		mode, err := strconv.ParseUint(t.Mode, 0, 32)
		if err != nil {
			return err
		}
		t.FileMode = os.FileMode(mode)
	}
	return nil
}

// ProcessTemplateResources is a convenience function that loads all the
// template resources and processes them serially. Called from main.
// It return an error if any.
func ProcessTemplateResources(c EtcdClient) []error {
	runErrors := make([]error, 0)
	var err error
	if c == nil {
		runErrors = append(runErrors, errors.New("An etcd client is required"))
		return runErrors
	}
	paths, err := filepath.Glob(filepath.Join(ConfigDir(), "*toml"))
	if err != nil {
		runErrors = append(runErrors, err)
		return runErrors
	}
	for _, p := range paths {
		t, err := NewTemplateResourceFromPath(p, c)
		if err != nil {
			runErrors = append(runErrors, err)
			log.Error(err.Error())
			continue
		}
		if err := t.process(); err != nil {
			runErrors = append(runErrors, err)
			log.Error(err.Error())
			continue
		}
	}
	return runErrors
}

// fileStat return a fileInfo describing the named file.
func fileStat(name string) (fi fileInfo, err error) {
	if IsFileExist(name) {
		f, err := os.Open(name)
		defer f.Close()
		if err != nil {
			return fi, err
		}
		stats, _ := f.Stat()
		fi.Uid = stats.Sys().(*syscall.Stat_t).Uid
		fi.Gid = stats.Sys().(*syscall.Stat_t).Gid
		fi.Mode = stats.Sys().(*syscall.Stat_t).Mode
		h := md5.New()
		io.Copy(h, f)
		fi.Md5 = fmt.Sprintf("%x", h.Sum(nil))
		return fi, nil
	} else {
		return fi, errors.New("File not found")
	}
}

// sameConfig reports whether src and dest config files are equal.
// Two config files are equal when they have the same file contents and
// Unix permissions. The owner, group, and mode must match.
// It return false in other cases.
func sameConfig(src, dest string) (error, bool) {
	if !IsFileExist(dest) {
		return nil, false
	}
	d, err := fileStat(dest)
	if err != nil {
		return err, false
	}
	s, err := fileStat(src)
	if err != nil {
		return err, false
	}
	if d.Uid != s.Uid || d.Gid != s.Gid || d.Mode != s.Mode || d.Md5 != s.Md5 {
		return nil, false
	}
	return nil, true
}
<commit_msg>An error is logged when the target config cannot be updated.<commit_after>package main

import (
	"bytes"
	"crypto/md5"
	"errors"
	"fmt"
	"github.com/BurntSushi/toml"
	"github.com/kelseyhightower/confd/log"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"syscall"
	"text/template"
)

// TemplateResourceConfig holds the parsed template resource.
type TemplateResourceConfig struct {
	TemplateResource TemplateResource `toml:"template"`
}

// TemplateResource is the representation of a parsed template resource.
type TemplateResource struct {
	Dest       string
	FileMode   os.FileMode
	Gid        int
	Keys       []string
	Mode       string
	Uid        int
	ReloadCmd  string `toml:"reload_cmd"`
	CheckCmd   string `toml:"check_cmd"`
	StageFile  *os.File
	Src        string
	Vars       map[string]interface{}
	etcdClient EtcdClient
}

// NewTemplateResourceFromPath creates a TemplateResource using a decoded file path
// and the supplied EtcdClient as input.
// It returns a TemplateResource and an error if any.
func NewTemplateResourceFromPath(path string, c EtcdClient) (*TemplateResource, error) {
	if c == nil {
		return nil, errors.New("A valid EtcdClient is required.")
	}
	var tc *TemplateResourceConfig
	_, err := toml.DecodeFile(path, &tc)
	if err != nil {
		return nil, fmt.Errorf("Cannot process template resource %s - %s", path, err.Error())
	}
	tc.TemplateResource.etcdClient = c
	return &tc.TemplateResource, nil
}

// setVars sets the Vars for template resource.
func (t *TemplateResource) setVars() error {
	var err error
	t.Vars, err = getValues(t.etcdClient, Prefix(), t.Keys)
	if err != nil {
		return err
	}
	return nil
}

// createStageFile stages the src configuration file by processing the src
// template and setting the desired owner, group, and mode. It also sets the
// StageFile for the template resource.
// It returns an error if any.
func (t *TemplateResource) createStageFile() error {
	t.Src = filepath.Join(TemplateDir(), t.Src)
	if !IsFileExist(t.Src) {
		return errors.New("Missing template: " + t.Src)
	}
	temp, err := ioutil.TempFile("", "")
	if err != nil {
		os.Remove(temp.Name())
		return err
	}
	tmpl := template.Must(template.ParseFiles(t.Src))
	if err = tmpl.Execute(temp, t.Vars); err != nil {
		return err
	}
	// Set the owner, group, and mode on the stage file now to make it easier to
	// compare against the destination configuration file later.
	os.Chmod(temp.Name(), t.FileMode)
	os.Chown(temp.Name(), t.Uid, t.Gid)
	t.StageFile = temp
	return nil
}

// sync compares the staged and dest config files and attempts to sync them
// if they differ. sync will run a config check command if set before
// overwriting the target config file. Finally, sync will run a reload command
// if set to have the application or service pick up the changes.
// It returns an error if any.
func (t *TemplateResource) sync() error {
	staged := t.StageFile.Name()
	defer os.Remove(staged)
	err, ok := sameConfig(staged, t.Dest)
	if err != nil {
		log.Error(err.Error())
	}
	if !ok {
		log.Info(t.Dest + " not in sync")
		if t.CheckCmd != "" {
			if err := t.check(); err != nil {
				return errors.New("Config check failed: " + err.Error())
			}
		}
		if err := os.Rename(staged, t.Dest); err != nil {
			return err
		}
		if t.ReloadCmd != "" {
			if err := t.reload(); err != nil {
				return err
			}
		}
	} else {
		log.Info(t.Dest + " in sync")
	}
	return nil
}

// check executes the check command to validate the staged config file. The
// command is modified so that any references to src template are substituted
// with a string representing the full path of the staged file. This allows the
// check to be run on the staged file before overwriting the destination config
// file.
// It returns nil if the check command returns 0 and there are no other errors.
func (t *TemplateResource) check() error {
	var cmdBuffer bytes.Buffer
	data := make(map[string]string)
	data["src"] = t.StageFile.Name()
	tmpl, err := template.New("checkcmd").Parse(t.CheckCmd)
	if err != nil {
		return err
	}
	if err := tmpl.Execute(&cmdBuffer, data); err != nil {
		return err
	}
	log.Debug("Running " + cmdBuffer.String())
	c := exec.Command("/bin/sh", "-c", cmdBuffer.String())
	if err := c.Run(); err != nil {
		return err
	}
	return nil
}

// reload executes the reload command.
// It returns nil if the reload command returns 0.
func (t *TemplateResource) reload() error {
	log.Debug("Running " + t.ReloadCmd)
	c := exec.Command("/bin/sh", "-c", t.ReloadCmd)
	if err := c.Run(); err != nil {
		return err
	}
	return nil
}

// process is a convenience function that wraps calls to the three main tasks
// required to keep local configuration files in sync. First we gather vars
// from etcd, then we stage a candidate configuration file, and finally sync
// things up.
// It returns an error if any.
func (t *TemplateResource) process() error {
	if err := t.setFileMode(); err != nil {
		return err
	}
	if err := t.setVars(); err != nil {
		return err
	}
	if err := t.createStageFile(); err != nil {
		return err
	}
	if err := t.sync(); err != nil {
		return err
	}
	return nil
}

// setFileMode sets the FileMode.
// It returns an error if any.
func (t *TemplateResource) setFileMode() error {
	if t.Mode == "" {
		if !IsFileExist(t.Dest) {
			t.FileMode = 0644
		} else {
			fi, err := os.Stat(t.Dest)
			if err != nil {
				return err
			}
			t.FileMode = fi.Mode()
		}
	} else {
		mode, err := strconv.ParseUint(t.Mode, 0, 32)
		if err != nil {
			return err
		}
		t.FileMode = os.FileMode(mode)
	}
	return nil
}

// ProcessTemplateResources is a convenience function that loads all the
// template resources and processes them serially. Called from main.
// It return an error if any.
func ProcessTemplateResources(c EtcdClient) []error {
	runErrors := make([]error, 0)
	var err error
	if c == nil {
		runErrors = append(runErrors, errors.New("An etcd client is required"))
		return runErrors
	}
	paths, err := filepath.Glob(filepath.Join(ConfigDir(), "*toml"))
	if err != nil {
		runErrors = append(runErrors, err)
		return runErrors
	}
	for _, p := range paths {
		t, err := NewTemplateResourceFromPath(p, c)
		if err != nil {
			runErrors = append(runErrors, err)
			log.Error(err.Error())
			continue
		}
		if err := t.process(); err != nil {
			runErrors = append(runErrors, err)
			log.Error(err.Error())
			continue
		}
	}
	return runErrors
}

// fileStat return a fileInfo describing the named file.
func fileStat(name string) (fi fileInfo, err error) {
	if IsFileExist(name) {
		f, err := os.Open(name)
		defer f.Close()
		if err != nil {
			return fi, err
		}
		stats, _ := f.Stat()
		fi.Uid = stats.Sys().(*syscall.Stat_t).Uid
		fi.Gid = stats.Sys().(*syscall.Stat_t).Gid
		fi.Mode = stats.Sys().(*syscall.Stat_t).Mode
		h := md5.New()
		io.Copy(h, f)
		fi.Md5 = fmt.Sprintf("%x", h.Sum(nil))
		return fi, nil
	} else {
		return fi, errors.New("File not found")
	}
}

// sameConfig reports whether src and dest config files are equal.
// Two config files are equal when they have the same file contents and
// Unix permissions. The owner, group, and mode must match.
// It return false in other cases.
func sameConfig(src, dest string) (error, bool) {
	if !IsFileExist(dest) {
		return nil, false
	}
	d, err := fileStat(dest)
	if err != nil {
		return err, false
	}
	s, err := fileStat(src)
	if err != nil {
		return err, false
	}
	if d.Uid != s.Uid || d.Gid != s.Gid || d.Mode != s.Mode || d.Md5 != s.Md5 {
		return nil, false
	}
	return nil, true
}
<|endoftext|>