<commit_before>package web

import (
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/gorilla/mux"
	"github.com/zenoss/glog"
	"github.com/zenoss/go-json-rest"
	"github.com/zenoss/serviced"
	"github.com/zenoss/serviced/dao"

	"mime"
	"net/http"
	"net/http/httputil"
	"net/url"
)

type ServiceConfig struct {
	bindPort    string
	agentPort   string
	zookeepers  []string
	stats       bool
	hostaliases []string
}

func NewServiceConfig(bindPort string, agentPort string, zookeepers []string, stats bool, hostaliases string) *ServiceConfig {
	cfg := ServiceConfig{bindPort, agentPort, zookeepers, stats, []string{}}
	if hostaliases != "" {
		cfg.hostaliases = strings.Split(hostaliases, ":")
	}
	if len(cfg.bindPort) == 0 {
		cfg.bindPort = ":8787"
	}
	if len(cfg.agentPort) == 0 {
		cfg.agentPort = "127.0.0.1:4979"
	}
	if len(cfg.zookeepers) == 0 {
		cfg.zookeepers = []string{"127.0.0.1:2181"}
	}
	return &cfg
}

// Serve handles control plane web UI requests and virtual host requests for zenoss web based services.
// The UI server actually listens on port 7878, the uihandler defined here just reverse proxies to it.
// Virutal host routing to zenoss web based services is done by the vhosthandler function.
func (sc *ServiceConfig) Serve() {
	client, err := sc.getClient()
	if err != nil {
		glog.Errorf("Unable to get control plane client: %v", err)
		return
	}

	// Reverse proxy to the web UI server.
	uihandler := func(w http.ResponseWriter, r *http.Request) {
		uiUrl, err := url.Parse("http://127.0.0.1:7878")
		if err != nil {
			glog.Errorf("Can't parse UI URL: %v", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		ui := httputil.NewSingleHostReverseProxy(uiUrl)
		if ui == nil {
			glog.Errorf("Can't proxy UI request: %v", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		ui.ServeHTTP(w, r)
	}

	// Lookup the appropriate virtual host and forward the request to it.
	// TODO: when zookeeper registration is integrated we can be more event
	// driven and only refresh the vhost map when service states change.
	vhosthandler := func(w http.ResponseWriter, r *http.Request) {
		var empty interface{}
		services := []*dao.RunningService{}
		client.GetRunningServices(&empty, &services)

		vhosts := make(map[string][]*dao.ServiceState, 0)

		for _, s := range services {
			var svc dao.Service

			if err := client.GetService(s.ServiceId, &svc); err != nil {
				glog.Errorf("Can't get service: %s (%v)", s.Id, err)
			}

			vheps := svc.GetServiceVHosts()

			for _, vhep := range vheps {
				for _, vh := range vhep.VHosts {
					svcstates := []*dao.ServiceState{}
					if err := client.GetServiceStates(s.ServiceId, &svcstates); err != nil {
						http.Error(w, fmt.Sprintf("can't retrieve service states for %s (%v)", s.ServiceId, err), http.StatusInternalServerError)
						return
					}

					for _, ss := range svcstates {
						vhosts[vh] = append(vhosts[vh], ss)
					}
				}
			}

		}

		muxvars := mux.Vars(r)
		svcstates, ok := vhosts[muxvars["subdomain"]]
		if !ok {
			http.Error(w, fmt.Sprintf("unknown vhost: %v", muxvars["subdomain"]), http.StatusNotFound)
			return
		}

		// TODO: implement a more intelligent strategy than "always pick the first one" when more
		// than one service state is mapped to a given virtual host
		for _, svcep := range svcstates[0].Endpoints {
			for _, vh := range svcep.VHosts {
				if vh == muxvars["subdomain"] {
					rp := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: "http", Host: fmt.Sprintf("%s:%d", svcstates[0].PrivateIp, svcep.PortNumber)})
					rp.ServeHTTP(w, r)
					return
				}
			}
		}

		http.Error(w, fmt.Sprintf("unrecognized endpoint: %s", muxvars["subdomain"]), http.StatusNotImplemented)
	}

	r := mux.NewRouter()

	if hnm, err := os.Hostname(); err == nil {
		sc.hostaliases = append(sc.hostaliases, hnm)
	}

	cmd := exec.Command("hostname", "--fqdn")
	if hnm, err := cmd.CombinedOutput(); err == nil {
		sc.hostaliases = append(sc.hostaliases, string(hnm[:len(hnm)-1]))
	}

	for _, ha := range sc.hostaliases {
		r.HandleFunc("/", vhosthandler).Host(fmt.Sprintf("{subdomain}.%s", ha))
		r.HandleFunc("/{path:.*}", vhosthandler).Host(fmt.Sprintf("{subdomain}.%s", ha))
	}

	r.HandleFunc("/{path:.*}", uihandler)

	http.Handle("/", r)

	certfile, err := serviced.TempCertFile()
	if err != nil {
		glog.Fatal("Could not prepare cert.pem file.")
	}
	keyfile, err := serviced.TempKeyFile()
	if err != nil {
		glog.Fatal("Could not prepare key.pem file.")
	}
	http.ListenAndServeTLS(sc.bindPort, certfile, keyfile, nil)
}

func (this *ServiceConfig) ServeUI() {
	mime.AddExtensionType(".json", "application/json")
	mime.AddExtensionType(".woff", "application/font-woff")

	handler := rest.ResourceHandler{
		EnableRelaxedContentType: true,
	}
	routes := []rest.Route{
		rest.Route{"GET", "/", MainPage},
		rest.Route{"GET", "/test", TestPage},
		rest.Route{"GET", "/stats", this.IsCollectingStats()},
		// Hosts
		rest.Route{"GET", "/hosts", this.AuthorizedClient(RestGetHosts)},
		rest.Route{"POST", "/hosts/add", this.AuthorizedClient(RestAddHost)},
		rest.Route{"DELETE", "/hosts/:hostId", this.AuthorizedClient(RestRemoveHost)},
		rest.Route{"PUT", "/hosts/:hostId", this.AuthorizedClient(RestUpdateHost)},
		rest.Route{"GET", "/hosts/:hostId/running", this.AuthorizedClient(RestGetRunningForHost)},
		rest.Route{"DELETE", "/hosts/:hostId/:serviceStateId", this.AuthorizedClient(RestKillRunning)},
		// Pools
		rest.Route{"POST", "/pools/add", this.AuthorizedClient(RestAddPool)},
		rest.Route{"GET", "/pools/:poolId/hosts", this.AuthorizedClient(RestGetHostsForResourcePool)},
		rest.Route{"DELETE", "/pools/:poolId", this.AuthorizedClient(RestRemovePool)},
		rest.Route{"PUT", "/pools/:poolId", this.AuthorizedClient(RestUpdatePool)},
		rest.Route{"GET", "/pools", this.AuthorizedClient(RestGetPools)},
		// Services (Apps)
		rest.Route{"GET", "/services", this.AuthorizedClient(RestGetAllServices)},
		rest.Route{"GET", "/services/:serviceId", this.AuthorizedClient(RestGetService)},
		rest.Route{"GET", "/services/:serviceId/running", this.AuthorizedClient(RestGetRunningForService)},
		rest.Route{"GET", "/services/:serviceId/running/:serviceStateId", this.AuthorizedClient(RestGetRunningService)},
		rest.Route{"GET", "/services/:serviceId/:serviceStateId/logs", this.AuthorizedClient(RestGetServiceStateLogs)},
		rest.Route{"POST", "/services/add", this.AuthorizedClient(RestAddService)},
		rest.Route{"DELETE", "/services/:serviceId", this.AuthorizedClient(RestRemoveService)},
		rest.Route{"GET", "/services/:serviceId/logs", this.AuthorizedClient(RestGetServiceLogs)},
		rest.Route{"PUT", "/services/:serviceId", this.AuthorizedClient(RestUpdateService)},
		rest.Route{"GET", "/services/:serviceId/snapshot", this.AuthorizedClient(RestSnapshotService)},
		rest.Route{"PUT", "/services/:serviceId/startService", this.AuthorizedClient(RestStartService)},
		rest.Route{"PUT", "/services/:serviceId/stopService", this.AuthorizedClient(RestStopService)},

		// Services (Virtual Host)
		rest.Route{"GET", "/vhosts", this.AuthorizedClient(RestGetVirtualHosts)},
		rest.Route{"POST", "/vhosts/:serviceId/:application/:vhostName", this.AuthorizedClient(RestAddVirtualHost)},
		rest.Route{"DELETE", "/vhosts/:serviceId/:application/:vhostName", this.AuthorizedClient(RestRemoveVirtualHost)},

		// Service templates (App templates)
		rest.Route{"GET", "/templates", this.AuthorizedClient(RestGetAppTemplates)},
		rest.Route{"POST", "/templates/deploy", this.AuthorizedClient(RestDeployAppTemplate)},
		// Login
		rest.Route{"POST", "/login", this.UnAuthorizedClient(RestLogin)},
		rest.Route{"DELETE", "/login", RestLogout},
		// "Misc" stuff
		rest.Route{"GET", "/top/services", this.AuthorizedClient(RestGetTopServices)},
		rest.Route{"GET", "/running", this.AuthorizedClient(RestGetAllRunning)},
		// Generic static data
		rest.Route{"GET", "/favicon.ico", FavIcon},
		rest.Route{"GET", "/static*resource", StaticData},
	}

	// Hardcoding these target URLs for now.
	// TODO: When internal services are allowed to run on other hosts, look that up.
	routes = routeToInternalServiceProxy("/elastic", "http://127.0.0.1:9200/", routes)
	routes = routeToInternalServiceProxy("/metrics", "http://127.0.0.1:8888/", routes)

	handler.SetRoutes(routes...)

	http.ListenAndServe(":7878", &handler)
}

var methods []string = []string{"GET", "POST", "PUT", "DELETE"}

func routeToInternalServiceProxy(path string, target string, routes []rest.Route) []rest.Route {
	targetUrl, err := url.Parse(target)
	if err != nil {
		glog.Errorf("Unable to parse proxy target URL: %s", target)
		return routes
	}
	// Wrap the normal http.Handler in a rest.HandlerFunc
	handlerFunc := func(w *rest.ResponseWriter, r *rest.Request) {
		proxy := serviced.NewReverseProxy(path, targetUrl)
		proxy.ServeHTTP(w.ResponseWriter, r.Request)
	}
	// Add on a glob to match subpaths
	andsubpath := path + "*x"
	for _, method := range methods {
		routes = append(routes, rest.Route{method, path, handlerFunc})
		routes = append(routes, rest.Route{method, andsubpath, handlerFunc})
	}
	return routes
}

func (this *ServiceConfig) UnAuthorizedClient(realfunc HandlerClientFunc) HandlerFunc {
	return func(w *rest.ResponseWriter, r *rest.Request) {
		client, err := this.getClient()
		if err != nil {
			glog.Errorf("Unable to acquire client: %v", err)
			RestServerError(w)
			return
		}
		defer client.Close()
		realfunc(w, r, client)
	}
}

func (this *ServiceConfig) AuthorizedClient(realfunc HandlerClientFunc) HandlerFunc {
	return func(w *rest.ResponseWriter, r *rest.Request) {
		if !LoginOk(r) {
			RestUnauthorized(w)
			return
		}
		client, err := this.getClient()
		if err != nil {
			glog.Errorf("Unable to acquire client: %v", err)
			RestServerError(w)
			return
		}
		defer client.Close()
		realfunc(w, r, client)
	}
}

func (this *ServiceConfig) IsCollectingStats() HandlerFunc {
	if this.stats {
		return func(w *rest.ResponseWriter, r *rest.Request) {
			w.WriteHeader(http.StatusOK)
		}
	} else {
		return func(w *rest.ResponseWriter, r *rest.Request) {
			w.WriteHeader(http.StatusNotImplemented)
		}
	}
}

func (this *ServiceConfig) getClient() (c *serviced.ControlClient, err error) {
	// setup the client
	c, err = serviced.NewControlClient(this.agentPort)
	if err != nil {
		glog.Fatalf("Could not create a control plane client: %v", err)
	}
	return c, err
}
<commit_msg>error handling<commit_after>package web

import (
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/gorilla/mux"
	"github.com/zenoss/glog"
	"github.com/zenoss/go-json-rest"
	"github.com/zenoss/serviced"
	"github.com/zenoss/serviced/dao"

	"mime"
	"net/http"
	"net/http/httputil"
	"net/url"
)

type ServiceConfig struct {
	bindPort    string
	agentPort   string
	zookeepers  []string
	stats       bool
	hostaliases []string
}

func NewServiceConfig(bindPort string, agentPort string, zookeepers []string, stats bool, hostaliases string) *ServiceConfig {
	cfg := ServiceConfig{bindPort, agentPort, zookeepers, stats, []string{}}
	if hostaliases != "" {
		cfg.hostaliases = strings.Split(hostaliases, ":")
	}
	if len(cfg.bindPort) == 0 {
		cfg.bindPort = ":8787"
	}
	if len(cfg.agentPort) == 0 {
		cfg.agentPort = "127.0.0.1:4979"
	}
	if len(cfg.zookeepers) == 0 {
		cfg.zookeepers = []string{"127.0.0.1:2181"}
	}
	return &cfg
}

// Serve handles control plane web UI requests and virtual host requests for zenoss web based services.
// The UI server actually listens on port 7878, the uihandler defined here just reverse proxies to it.
// Virutal host routing to zenoss web based services is done by the vhosthandler function.
func (sc *ServiceConfig) Serve() {
	client, err := sc.getClient()
	if err != nil {
		glog.Errorf("Unable to get control plane client: %v", err)
		return
	}

	// Reverse proxy to the web UI server.
	uihandler := func(w http.ResponseWriter, r *http.Request) {
		uiUrl, err := url.Parse("http://127.0.0.1:7878")
		if err != nil {
			glog.Errorf("Can't parse UI URL: %v", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		ui := httputil.NewSingleHostReverseProxy(uiUrl)
		if ui == nil {
			glog.Errorf("Can't proxy UI request: %v", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		ui.ServeHTTP(w, r)
	}

	// Lookup the appropriate virtual host and forward the request to it.
	// TODO: when zookeeper registration is integrated we can be more event
	// driven and only refresh the vhost map when service states change.
	vhosthandler := func(w http.ResponseWriter, r *http.Request) {
		var empty interface{}
		services := []*dao.RunningService{}
		client.GetRunningServices(&empty, &services)

		vhosts := make(map[string][]*dao.ServiceState, 0)

		for _, s := range services {
			var svc dao.Service

			if err := client.GetService(s.ServiceId, &svc); err != nil {
				glog.Errorf("Can't get service: %s (%v)", s.Id, err)
			}

			vheps := svc.GetServiceVHosts()

			for _, vhep := range vheps {
				for _, vh := range vhep.VHosts {
					svcstates := []*dao.ServiceState{}
					if err := client.GetServiceStates(s.ServiceId, &svcstates); err != nil {
						http.Error(w, fmt.Sprintf("can't retrieve service states for %s (%v)", s.ServiceId, err), http.StatusInternalServerError)
						return
					}

					for _, ss := range svcstates {
						vhosts[vh] = append(vhosts[vh], ss)
					}
				}
			}

		}

		muxvars := mux.Vars(r)
		svcstates, ok := vhosts[muxvars["subdomain"]]
		if !ok {
			http.Error(w, fmt.Sprintf("unknown vhost: %v", muxvars["subdomain"]), http.StatusNotFound)
			return
		}

		// TODO: implement a more intelligent strategy than "always pick the first one" when more
		// than one service state is mapped to a given virtual host
		for _, svcep := range svcstates[0].Endpoints {
			for _, vh := range svcep.VHosts {
				if vh == muxvars["subdomain"] {
					rp := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: "http", Host: fmt.Sprintf("%s:%d", svcstates[0].PrivateIp, svcep.PortNumber)})
					rp.ServeHTTP(w, r)
					return
				}
			}
		}

		http.Error(w, fmt.Sprintf("unrecognized endpoint: %s", muxvars["subdomain"]), http.StatusNotImplemented)
	}

	r := mux.NewRouter()

	if hnm, err := os.Hostname(); err == nil {
		sc.hostaliases = append(sc.hostaliases, hnm)
	}

	cmd := exec.Command("hostname", "--fqdn")
	if hnm, err := cmd.CombinedOutput(); err == nil {
		sc.hostaliases = append(sc.hostaliases, string(hnm[:len(hnm)-1]))
	}

	for _, ha := range sc.hostaliases {
		r.HandleFunc("/", vhosthandler).Host(fmt.Sprintf("{subdomain}.%s", ha))
		r.HandleFunc("/{path:.*}", vhosthandler).Host(fmt.Sprintf("{subdomain}.%s", ha))
	}

	r.HandleFunc("/{path:.*}", uihandler)

	http.Handle("/", r)

	certfile, err := serviced.TempCertFile()
	if err != nil {
		glog.Error("Could not prepare cert.pem file.")
	}
	keyfile, err := serviced.TempKeyFile()
	if err != nil {
		glog.Error("Could not prepare key.pem file.")
	}
	http.ListenAndServeTLS(sc.bindPort, certfile, keyfile, nil)
}

func (this *ServiceConfig) ServeUI() {
	mime.AddExtensionType(".json", "application/json")
	mime.AddExtensionType(".woff", "application/font-woff")

	handler := rest.ResourceHandler{
		EnableRelaxedContentType: true,
	}
	routes := []rest.Route{
		rest.Route{"GET", "/", MainPage},
		rest.Route{"GET", "/test", TestPage},
		rest.Route{"GET", "/stats", this.IsCollectingStats()},
		// Hosts
		rest.Route{"GET", "/hosts", this.AuthorizedClient(RestGetHosts)},
		rest.Route{"POST", "/hosts/add", this.AuthorizedClient(RestAddHost)},
		rest.Route{"DELETE", "/hosts/:hostId", this.AuthorizedClient(RestRemoveHost)},
		rest.Route{"PUT", "/hosts/:hostId", this.AuthorizedClient(RestUpdateHost)},
		rest.Route{"GET", "/hosts/:hostId/running", this.AuthorizedClient(RestGetRunningForHost)},
		rest.Route{"DELETE", "/hosts/:hostId/:serviceStateId", this.AuthorizedClient(RestKillRunning)},
		// Pools
		rest.Route{"POST", "/pools/add", this.AuthorizedClient(RestAddPool)},
		rest.Route{"GET", "/pools/:poolId/hosts", this.AuthorizedClient(RestGetHostsForResourcePool)},
		rest.Route{"DELETE", "/pools/:poolId", this.AuthorizedClient(RestRemovePool)},
		rest.Route{"PUT", "/pools/:poolId", this.AuthorizedClient(RestUpdatePool)},
		rest.Route{"GET", "/pools", this.AuthorizedClient(RestGetPools)},
		// Services (Apps)
		rest.Route{"GET", "/services", this.AuthorizedClient(RestGetAllServices)},
		rest.Route{"GET", "/services/:serviceId", this.AuthorizedClient(RestGetService)},
		rest.Route{"GET", "/services/:serviceId/running", this.AuthorizedClient(RestGetRunningForService)},
		rest.Route{"GET", "/services/:serviceId/running/:serviceStateId", this.AuthorizedClient(RestGetRunningService)},
		rest.Route{"GET", "/services/:serviceId/:serviceStateId/logs", this.AuthorizedClient(RestGetServiceStateLogs)},
		rest.Route{"POST", "/services/add", this.AuthorizedClient(RestAddService)},
		rest.Route{"DELETE", "/services/:serviceId", this.AuthorizedClient(RestRemoveService)},
		rest.Route{"GET", "/services/:serviceId/logs", this.AuthorizedClient(RestGetServiceLogs)},
		rest.Route{"PUT", "/services/:serviceId", this.AuthorizedClient(RestUpdateService)},
		rest.Route{"GET", "/services/:serviceId/snapshot", this.AuthorizedClient(RestSnapshotService)},
		rest.Route{"PUT", "/services/:serviceId/startService", this.AuthorizedClient(RestStartService)},
		rest.Route{"PUT", "/services/:serviceId/stopService", this.AuthorizedClient(RestStopService)},

		// Services (Virtual Host)
		rest.Route{"GET", "/vhosts", this.AuthorizedClient(RestGetVirtualHosts)},
		rest.Route{"POST", "/vhosts/:serviceId/:application/:vhostName", this.AuthorizedClient(RestAddVirtualHost)},
		rest.Route{"DELETE", "/vhosts/:serviceId/:application/:vhostName", this.AuthorizedClient(RestRemoveVirtualHost)},

		// Service templates (App templates)
		rest.Route{"GET", "/templates", this.AuthorizedClient(RestGetAppTemplates)},
		rest.Route{"POST", "/templates/deploy", this.AuthorizedClient(RestDeployAppTemplate)},
		// Login
		rest.Route{"POST", "/login", this.UnAuthorizedClient(RestLogin)},
		rest.Route{"DELETE", "/login", RestLogout},
		// "Misc" stuff
		rest.Route{"GET", "/top/services", this.AuthorizedClient(RestGetTopServices)},
		rest.Route{"GET", "/running", this.AuthorizedClient(RestGetAllRunning)},
		// Generic static data
		rest.Route{"GET", "/favicon.ico", FavIcon},
		rest.Route{"GET", "/static*resource", StaticData},
	}

	// Hardcoding these target URLs for now.
	// TODO: When internal services are allowed to run on other hosts, look that up.
	routes = routeToInternalServiceProxy("/elastic", "http://127.0.0.1:9200/", routes)
	routes = routeToInternalServiceProxy("/metrics", "http://127.0.0.1:8888/", routes)

	handler.SetRoutes(routes...)

	http.ListenAndServe(":7878", &handler)
}

var methods []string = []string{"GET", "POST", "PUT", "DELETE"}

func routeToInternalServiceProxy(path string, target string, routes []rest.Route) []rest.Route {
	targetUrl, err := url.Parse(target)
	if err != nil {
		glog.Errorf("Unable to parse proxy target URL: %s", target)
		return routes
	}
	// Wrap the normal http.Handler in a rest.HandlerFunc
	handlerFunc := func(w *rest.ResponseWriter, r *rest.Request) {
		proxy := serviced.NewReverseProxy(path, targetUrl)
		proxy.ServeHTTP(w.ResponseWriter, r.Request)
	}
	// Add on a glob to match subpaths
	andsubpath := path + "*x"
	for _, method := range methods {
		routes = append(routes, rest.Route{method, path, handlerFunc})
		routes = append(routes, rest.Route{method, andsubpath, handlerFunc})
	}
	return routes
}

func (this *ServiceConfig) UnAuthorizedClient(realfunc HandlerClientFunc) HandlerFunc {
	return func(w *rest.ResponseWriter, r *rest.Request) {
		client, err := this.getClient()
		if err != nil {
			glog.Errorf("Unable to acquire client: %v", err)
			RestServerError(w)
			return
		}
		defer client.Close()
		realfunc(w, r, client)
	}
}

func (this *ServiceConfig) AuthorizedClient(realfunc HandlerClientFunc) HandlerFunc {
	return func(w *rest.ResponseWriter, r *rest.Request) {
		if !LoginOk(r) {
			RestUnauthorized(w)
			return
		}
		client, err := this.getClient()
		if err != nil {
			glog.Errorf("Unable to acquire client: %v", err)
			RestServerError(w)
			return
		}
		defer client.Close()
		realfunc(w, r, client)
	}
}

func (this *ServiceConfig) IsCollectingStats() HandlerFunc {
	if this.stats {
		return func(w *rest.ResponseWriter, r *rest.Request) {
			w.WriteHeader(http.StatusOK)
		}
	} else {
		return func(w *rest.ResponseWriter, r *rest.Request) {
			w.WriteHeader(http.StatusNotImplemented)
		}
	}
}

func (this *ServiceConfig) getClient() (c *serviced.ControlClient, err error) {
	// setup the client
	c, err = serviced.NewControlClient(this.agentPort)
	if err != nil {
		glog.Fatalf("Could not create a control plane client: %v", err)
	}
	return c, err
}
<|endoftext|>


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

package web

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

	"github.com/go-kit/kit/log/level"
	"github.com/gogo/protobuf/proto"
	"github.com/prometheus/client_golang/prometheus"
	dto "github.com/prometheus/client_model/go"
	"github.com/prometheus/common/expfmt"
	"github.com/prometheus/common/model"

	"github.com/prometheus/prometheus/pkg/labels"
	"github.com/prometheus/prometheus/pkg/timestamp"
	"github.com/prometheus/prometheus/pkg/value"
	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/storage"
)

var (
	federationErrors = prometheus.NewCounter(prometheus.CounterOpts{
		Name: "prometheus_web_federation_errors_total",
		Help: "Total number of errors that occurred while sending federation responses.",
	})
)

func (h *Handler) federation(w http.ResponseWriter, req *http.Request) {
	h.mtx.RLock()
	defer h.mtx.RUnlock()

	if err := req.ParseForm(); err != nil {
		http.Error(w, fmt.Sprintf("error parsing form values: %v", err), http.StatusBadRequest)
		return
	}

	var matcherSets [][]*labels.Matcher
	for _, s := range req.Form["match[]"] {
		matchers, err := promql.ParseMetricSelector(s)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		matcherSets = append(matcherSets, matchers)
	}

	var (
		mint   = timestamp.FromTime(h.now().Time().Add(-promql.LookbackDelta))
		maxt   = timestamp.FromTime(h.now().Time())
		format = expfmt.Negotiate(req.Header)
		enc    = expfmt.NewEncoder(w, format)
	)
	w.Header().Set("Content-Type", string(format))

	q, err := h.storage.Querier(req.Context(), mint, maxt)
	if err != nil {
		federationErrors.Inc()
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer q.Close()

	vec := make(promql.Vector, 0, 8000)

	var sets []storage.SeriesSet
	for _, mset := range matcherSets {
		s, err := q.Select(nil, mset...)
		if err != nil {
			federationErrors.Inc()
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		sets = append(sets, s)
	}

	set := storage.NewMergeSeriesSet(sets)
	it := storage.NewBuffer(int64(promql.LookbackDelta / 1e6))
	for set.Next() {
		s := set.At()

		// TODO(fabxc): allow fast path for most recent sample either
		// in the storage itself or caching layer in Prometheus.
		it.Reset(s.Iterator())

		var t int64
		var v float64

		ok := it.Seek(maxt)
		if ok {
			t, v = it.Values()
		} else {
			t, v, ok = it.PeekBack(1)
			if !ok {
				continue
			}
		}
		// The exposition formats do not support stale markers, so drop them. This
		// is good enough for staleness handling of federated data, as the
		// interval-based limits on staleness will do the right thing for supported
		// use cases (which is to say federating aggregated time series).
		if value.IsStaleNaN(v) {
			continue
		}

		vec = append(vec, promql.Sample{
			Metric: s.Labels(),
			Point:  promql.Point{T: t, V: v},
		})
	}
	if set.Err() != nil {
		federationErrors.Inc()
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	sort.Sort(byName(vec))

	externalLabels := h.config.GlobalConfig.ExternalLabels.Clone()
	if _, ok := externalLabels[model.InstanceLabel]; !ok {
		externalLabels[model.InstanceLabel] = ""
	}
	externalLabelNames := make(model.LabelNames, 0, len(externalLabels))
	for ln := range externalLabels {
		externalLabelNames = append(externalLabelNames, ln)
	}
	sort.Sort(externalLabelNames)

	var (
		lastMetricName string
		protMetricFam  *dto.MetricFamily
	)
	for _, s := range vec {
		nameSeen := false
		globalUsed := map[string]struct{}{}
		protMetric := &dto.Metric{
			Untyped: &dto.Untyped{},
		}

		for _, l := range s.Metric {
			if l.Value == "" {
				// No value means unset. Never consider those labels.
				// This is also important to protect against nameless metrics.
				continue
			}
			if l.Name == labels.MetricName {
				nameSeen = true
				if l.Value == lastMetricName {
					// We already have the name in the current MetricFamily,
					// and we ignore nameless metrics.
					continue
				}
				// Need to start a new MetricFamily. Ship off the old one (if any) before
				// creating the new one.
				if protMetricFam != nil {
					if err := enc.Encode(protMetricFam); err != nil {
						federationErrors.Inc()
						level.Error(h.logger).Log("msg", "federation failed", "err", err)
						return
					}
				}
				protMetricFam = &dto.MetricFamily{
					Type: dto.MetricType_UNTYPED.Enum(),
					Name: proto.String(l.Value),
				}
				lastMetricName = l.Value
				continue
			}
			protMetric.Label = append(protMetric.Label, &dto.LabelPair{
				Name:  proto.String(l.Name),
				Value: proto.String(l.Value),
			})
			if _, ok := externalLabels[model.LabelName(l.Name)]; ok {
				globalUsed[l.Name] = struct{}{}
			}
		}
		if !nameSeen {
			level.Warn(h.logger).Log("msg", "Ignoring nameless metric during federation", "metric", s.Metric)
			continue
		}
		// Attach global labels if they do not exist yet.
		for _, ln := range externalLabelNames {
			lv := externalLabels[ln]
			if _, ok := globalUsed[string(ln)]; !ok {
				protMetric.Label = append(protMetric.Label, &dto.LabelPair{
					Name:  proto.String(string(ln)),
					Value: proto.String(string(lv)),
				})
			}
		}

		protMetric.TimestampMs = proto.Int64(s.T)
		protMetric.Untyped.Value = proto.Float64(s.V)

		protMetricFam.Metric = append(protMetricFam.Metric, protMetric)
	}
	// Still have to ship off the last MetricFamily, if any.
	if protMetricFam != nil {
		if err := enc.Encode(protMetricFam); err != nil {
			federationErrors.Inc()
			level.Error(h.logger).Log("msg", "federation failed", "err", err)
		}
	}
}

// byName makes a model.Vector sortable by metric name.
type byName promql.Vector

func (vec byName) Len() int      { return len(vec) }
func (vec byName) Swap(i, j int) { vec[i], vec[j] = vec[j], vec[i] }

func (vec byName) Less(i, j int) bool {
	ni := vec[i].Metric.Get(labels.MetricName)
	nj := vec[j].Metric.Get(labels.MetricName)
	return ni < nj
}
<commit_msg>Add SelectParams to Select for federation (#4546)<commit_after>// Copyright 2015 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package web

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

	"github.com/go-kit/kit/log/level"
	"github.com/gogo/protobuf/proto"
	"github.com/prometheus/client_golang/prometheus"
	dto "github.com/prometheus/client_model/go"
	"github.com/prometheus/common/expfmt"
	"github.com/prometheus/common/model"

	"github.com/prometheus/prometheus/pkg/labels"
	"github.com/prometheus/prometheus/pkg/timestamp"
	"github.com/prometheus/prometheus/pkg/value"
	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/storage"
)

var (
	federationErrors = prometheus.NewCounter(prometheus.CounterOpts{
		Name: "prometheus_web_federation_errors_total",
		Help: "Total number of errors that occurred while sending federation responses.",
	})
)

func (h *Handler) federation(w http.ResponseWriter, req *http.Request) {
	h.mtx.RLock()
	defer h.mtx.RUnlock()

	if err := req.ParseForm(); err != nil {
		http.Error(w, fmt.Sprintf("error parsing form values: %v", err), http.StatusBadRequest)
		return
	}

	var matcherSets [][]*labels.Matcher
	for _, s := range req.Form["match[]"] {
		matchers, err := promql.ParseMetricSelector(s)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		matcherSets = append(matcherSets, matchers)
	}

	var (
		mint   = timestamp.FromTime(h.now().Time().Add(-promql.LookbackDelta))
		maxt   = timestamp.FromTime(h.now().Time())
		format = expfmt.Negotiate(req.Header)
		enc    = expfmt.NewEncoder(w, format)
	)
	w.Header().Set("Content-Type", string(format))

	q, err := h.storage.Querier(req.Context(), mint, maxt)
	if err != nil {
		federationErrors.Inc()
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer q.Close()

	vec := make(promql.Vector, 0, 8000)

	params := &storage.SelectParams{
		Start: mint,
		End:   maxt,
	}

	var sets []storage.SeriesSet
	for _, mset := range matcherSets {
		s, err := q.Select(params, mset...)
		if err != nil {
			federationErrors.Inc()
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		sets = append(sets, s)
	}

	set := storage.NewMergeSeriesSet(sets)
	it := storage.NewBuffer(int64(promql.LookbackDelta / 1e6))
	for set.Next() {
		s := set.At()

		// TODO(fabxc): allow fast path for most recent sample either
		// in the storage itself or caching layer in Prometheus.
		it.Reset(s.Iterator())

		var t int64
		var v float64

		ok := it.Seek(maxt)
		if ok {
			t, v = it.Values()
		} else {
			t, v, ok = it.PeekBack(1)
			if !ok {
				continue
			}
		}
		// The exposition formats do not support stale markers, so drop them. This
		// is good enough for staleness handling of federated data, as the
		// interval-based limits on staleness will do the right thing for supported
		// use cases (which is to say federating aggregated time series).
		if value.IsStaleNaN(v) {
			continue
		}

		vec = append(vec, promql.Sample{
			Metric: s.Labels(),
			Point:  promql.Point{T: t, V: v},
		})
	}
	if set.Err() != nil {
		federationErrors.Inc()
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	sort.Sort(byName(vec))

	externalLabels := h.config.GlobalConfig.ExternalLabels.Clone()
	if _, ok := externalLabels[model.InstanceLabel]; !ok {
		externalLabels[model.InstanceLabel] = ""
	}
	externalLabelNames := make(model.LabelNames, 0, len(externalLabels))
	for ln := range externalLabels {
		externalLabelNames = append(externalLabelNames, ln)
	}
	sort.Sort(externalLabelNames)

	var (
		lastMetricName string
		protMetricFam  *dto.MetricFamily
	)
	for _, s := range vec {
		nameSeen := false
		globalUsed := map[string]struct{}{}
		protMetric := &dto.Metric{
			Untyped: &dto.Untyped{},
		}

		for _, l := range s.Metric {
			if l.Value == "" {
				// No value means unset. Never consider those labels.
				// This is also important to protect against nameless metrics.
				continue
			}
			if l.Name == labels.MetricName {
				nameSeen = true
				if l.Value == lastMetricName {
					// We already have the name in the current MetricFamily,
					// and we ignore nameless metrics.
					continue
				}
				// Need to start a new MetricFamily. Ship off the old one (if any) before
				// creating the new one.
				if protMetricFam != nil {
					if err := enc.Encode(protMetricFam); err != nil {
						federationErrors.Inc()
						level.Error(h.logger).Log("msg", "federation failed", "err", err)
						return
					}
				}
				protMetricFam = &dto.MetricFamily{
					Type: dto.MetricType_UNTYPED.Enum(),
					Name: proto.String(l.Value),
				}
				lastMetricName = l.Value
				continue
			}
			protMetric.Label = append(protMetric.Label, &dto.LabelPair{
				Name:  proto.String(l.Name),
				Value: proto.String(l.Value),
			})
			if _, ok := externalLabels[model.LabelName(l.Name)]; ok {
				globalUsed[l.Name] = struct{}{}
			}
		}
		if !nameSeen {
			level.Warn(h.logger).Log("msg", "Ignoring nameless metric during federation", "metric", s.Metric)
			continue
		}
		// Attach global labels if they do not exist yet.
		for _, ln := range externalLabelNames {
			lv := externalLabels[ln]
			if _, ok := globalUsed[string(ln)]; !ok {
				protMetric.Label = append(protMetric.Label, &dto.LabelPair{
					Name:  proto.String(string(ln)),
					Value: proto.String(string(lv)),
				})
			}
		}

		protMetric.TimestampMs = proto.Int64(s.T)
		protMetric.Untyped.Value = proto.Float64(s.V)

		protMetricFam.Metric = append(protMetricFam.Metric, protMetric)
	}
	// Still have to ship off the last MetricFamily, if any.
	if protMetricFam != nil {
		if err := enc.Encode(protMetricFam); err != nil {
			federationErrors.Inc()
			level.Error(h.logger).Log("msg", "federation failed", "err", err)
		}
	}
}

// byName makes a model.Vector sortable by metric name.
type byName promql.Vector

func (vec byName) Len() int      { return len(vec) }
func (vec byName) Swap(i, j int) { vec[i], vec[j] = vec[j], vec[i] }

func (vec byName) Less(i, j int) bool {
	ni := vec[i].Metric.Get(labels.MetricName)
	nj := vec[j].Metric.Get(labels.MetricName)
	return ni < nj
}
<|endoftext|>


<commit_before>package matchers

var (
	TypeEpub   = newType("epub", "application/epub+zip")
	TypeZip    = newType("zip", "application/zip")
	TypeTar    = newType("tar", "application/x-tar")
	TypeRar    = newType("rar", "application/x-rar-compressed")
	TypeGz     = newType("gz", "application/gzip")
	TypeBz2    = newType("bz2", "application/x-bzip2")
	Type7z     = newType("7z", "application/x-7z-compressed")
	TypeXz     = newType("xz", "application/x-xz")
	TypePdf    = newType("pdf", "application/pdf")
	TypeExe    = newType("exe", "application/x-msdownload")
	TypeSwf    = newType("swf", "application/x-shockwave-flash")
	TypeRtf    = newType("rtf", "application/rtf")
	TypeEot    = newType("eot", "application/octet-stream")
	TypePs     = newType("ps", "application/postscript")
	TypeSqlite = newType("sqlite", "application/x-sqlite3")
	TypeNes    = newType("nes", "application/x-nintendo-nes-rom")
	TypeCrx    = newType("crx", "application/x-google-chrome-extension")
	TypeCab    = newType("cab", "application/vnd.ms-cab-compressed")
	TypeDeb    = newType("deb", "application/x-deb")
	TypeAr     = newType("ar", "application/x-unix-archive")
	TypeZ      = newType("Z", "application/x-compress")
	TypeLz     = newType("lz", "application/x-lzip")
)

var Archive = Map{
	TypeEpub:   Epub,
	TypeZip:    Zip,
	TypeTar:    Tar,
	TypeRar:    Rar,
	TypeBz2:    Bz2,
	Type7z:     SevenZ,
	TypeXz:     Xz,
	TypePdf:    Pdf,
	TypeExe:    Exe,
	TypeSwf:    Swf,
	TypeRtf:    Rtf,
	TypeEot:    Eot,
	TypePs:     Ps,
	TypeSqlite: Sqlite,
	TypeNes:    Nes,
	TypeCrx:    Crx,
	TypeCab:    Cab,
	TypeDeb:    Deb,
	TypeAr:     Ar,
	TypeZ:      Z,
	TypeLz:     Lz,
}

func Epub(buf []byte) bool {
	return len(buf) > 57 &&
		buf[0] == 0x50 && buf[1] == 0x4B && buf[2] == 0x3 && buf[3] == 0x4 &&
		buf[30] == 0x6D && buf[31] == 0x69 && buf[32] == 0x6D && buf[33] == 0x65 &&
		buf[34] == 0x74 && buf[35] == 0x79 && buf[36] == 0x70 && buf[37] == 0x65 &&
		buf[38] == 0x61 && buf[39] == 0x70 && buf[40] == 0x70 && buf[41] == 0x6C &&
		buf[42] == 0x69 && buf[43] == 0x63 && buf[44] == 0x61 && buf[45] == 0x74 &&
		buf[46] == 0x69 && buf[47] == 0x6F && buf[48] == 0x6E && buf[49] == 0x2F &&
		buf[50] == 0x65 && buf[51] == 0x70 && buf[52] == 0x75 && buf[53] == 0x62 &&
		buf[54] == 0x2B && buf[55] == 0x7A && buf[56] == 0x69 && buf[57] == 0x70
}

func Zip(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x50 && buf[1] == 0x4B &&
		(buf[2] == 0x3 || buf[2] == 0x5 || buf[2] == 0x7) &&
		(buf[3] == 0x4 || buf[3] == 0x6 || buf[3] == 0x8)
}

func Tar(buf []byte) bool {
	return len(buf) > 261 &&
		buf[257] == 0x75 && buf[258] == 0x73 &&
		buf[259] == 0x74 && buf[260] == 0x61 &&
		buf[261] == 0x72
}

func Rar(buf []byte) bool {
	return len(buf) > 6 &&
		buf[0] == 0x52 && buf[1] == 0x61 && buf[2] == 0x72 &&
		buf[3] == 0x21 && buf[4] == 0x1A && buf[5] == 0x7 &&
		(buf[6] == 0x0 || buf[6] == 0x1)
}

func Gz(buf []byte) bool {
	return len(buf) > 2 &&
		buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x8
}

func Bz2(buf []byte) bool {
	return len(buf) > 2 &&
		buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68
}

func SevenZ(buf []byte) bool {
	return len(buf) > 5 &&
		buf[0] == 0x37 && buf[1] == 0x7A && buf[2] == 0xBC &&
		buf[3] == 0xAF && buf[4] == 0x27 && buf[5] == 0x1C
}

func Pdf(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x25 && buf[1] == 0x50 &&
		buf[2] == 0x44 && buf[3] == 0x46
}

func Exe(buf []byte) bool {
	return len(buf) > 1 &&
		buf[0] == 0x4D && buf[1] == 0x5A
}

func Swf(buf []byte) bool {
	return len(buf) > 2 &&
		(buf[0] == 0x43 || buf[0] == 0x46) &&
		buf[1] == 0x57 && buf[2] == 0x53
}

func Rtf(buf []byte) bool {
	return len(buf) > 4 &&
		buf[0] == 0x7B && buf[1] == 0x5C &&
		buf[2] == 0x72 && buf[3] == 0x74 &&
		buf[4] == 0x66
}

func Nes(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x4E && buf[1] == 0x45 &&
		buf[2] == 0x53 && buf[3] == 0x1A
}

func Crx(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x43 && buf[1] == 0x72 &&
		buf[2] == 0x32 && buf[3] == 0x34
}

func Cab(buf []byte) bool {
	return len(buf) > 3 &&
		((buf[0] == 0x4D && buf[1] == 0x53 && buf[2] == 0x43 && buf[3] == 0x46) ||
			(buf[0] == 0x49 && buf[1] == 0x53 && buf[2] == 0x63 && buf[3] == 0x28))
}

func Eot(buf []byte) bool {
	return len(buf) > 35 &&
		buf[34] == 0x4C && buf[35] == 0x50 &&
		((buf[8] == 0x02 && buf[9] == 0x00 &&
			buf[10] == 0x01) || (buf[8] == 0x01 &&
			buf[9] == 0x00 && buf[10] == 0x00) ||
			(buf[8] == 0x02 && buf[9] == 0x00 &&
				buf[10] == 0x02))
}

func Ps(buf []byte) bool {
	return len(buf) > 1 &&
		buf[0] == 0x25 && buf[1] == 0x21
}

func Xz(buf []byte) bool {
	return len(buf) > 5 &&
		buf[0] == 0xFD && buf[1] == 0x37 &&
		buf[2] == 0x7A && buf[3] == 0x58 &&
		buf[4] == 0x5A && buf[5] == 0x00
}

func Sqlite(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x53 && buf[1] == 0x51 &&
		buf[2] == 0x4C && buf[3] == 0x69
}

func Deb(buf []byte) bool {
	return len(buf) > 20 &&
		buf[0] == 0x21 && buf[1] == 0x3C && buf[2] == 0x61 &&
		buf[3] == 0x72 && buf[4] == 0x63 && buf[5] == 0x68 &&
		buf[6] == 0x3E && buf[7] == 0x0A && buf[8] == 0x64 &&
		buf[9] == 0x65 && buf[10] == 0x62 && buf[11] == 0x69 &&
		buf[12] == 0x61 && buf[13] == 0x6E && buf[14] == 0x2D &&
		buf[15] == 0x62 && buf[16] == 0x69 && buf[17] == 0x6E &&
		buf[18] == 0x61 && buf[19] == 0x72 && buf[20] == 0x79
}

func Ar(buf []byte) bool {
	return len(buf) > 6 &&
		buf[0] == 0x21 && buf[1] == 0x3C &&
		buf[2] == 0x61 && buf[3] == 0x72 &&
		buf[4] == 0x63 && buf[5] == 0x68 &&
		buf[6] == 0x3E
}

func Z(buf []byte) bool {
	return len(buf) > 1 &&
		((buf[0] == 0x1F && buf[1] == 0xA0) ||
			(buf[0] == 0x1F && buf[1] == 0x9D))
}

func Lz(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x4C && buf[1] == 0x5A &&
		buf[2] == 0x49 && buf[3] == 0x50
}
<commit_msg>Registers the GZ Matcher<commit_after>package matchers

var (
	TypeEpub   = newType("epub", "application/epub+zip")
	TypeZip    = newType("zip", "application/zip")
	TypeTar    = newType("tar", "application/x-tar")
	TypeRar    = newType("rar", "application/x-rar-compressed")
	TypeGz     = newType("gz", "application/gzip")
	TypeBz2    = newType("bz2", "application/x-bzip2")
	Type7z     = newType("7z", "application/x-7z-compressed")
	TypeXz     = newType("xz", "application/x-xz")
	TypePdf    = newType("pdf", "application/pdf")
	TypeExe    = newType("exe", "application/x-msdownload")
	TypeSwf    = newType("swf", "application/x-shockwave-flash")
	TypeRtf    = newType("rtf", "application/rtf")
	TypeEot    = newType("eot", "application/octet-stream")
	TypePs     = newType("ps", "application/postscript")
	TypeSqlite = newType("sqlite", "application/x-sqlite3")
	TypeNes    = newType("nes", "application/x-nintendo-nes-rom")
	TypeCrx    = newType("crx", "application/x-google-chrome-extension")
	TypeCab    = newType("cab", "application/vnd.ms-cab-compressed")
	TypeDeb    = newType("deb", "application/x-deb")
	TypeAr     = newType("ar", "application/x-unix-archive")
	TypeZ      = newType("Z", "application/x-compress")
	TypeLz     = newType("lz", "application/x-lzip")
)

var Archive = Map{
	TypeEpub:   Epub,
	TypeZip:    Zip,
	TypeTar:    Tar,
	TypeRar:    Rar,
	TypeGz:     Gz,
	TypeBz2:    Bz2,
	Type7z:     SevenZ,
	TypeXz:     Xz,
	TypePdf:    Pdf,
	TypeExe:    Exe,
	TypeSwf:    Swf,
	TypeRtf:    Rtf,
	TypeEot:    Eot,
	TypePs:     Ps,
	TypeSqlite: Sqlite,
	TypeNes:    Nes,
	TypeCrx:    Crx,
	TypeCab:    Cab,
	TypeDeb:    Deb,
	TypeAr:     Ar,
	TypeZ:      Z,
	TypeLz:     Lz,
}

func Epub(buf []byte) bool {
	return len(buf) > 57 &&
		buf[0] == 0x50 && buf[1] == 0x4B && buf[2] == 0x3 && buf[3] == 0x4 &&
		buf[30] == 0x6D && buf[31] == 0x69 && buf[32] == 0x6D && buf[33] == 0x65 &&
		buf[34] == 0x74 && buf[35] == 0x79 && buf[36] == 0x70 && buf[37] == 0x65 &&
		buf[38] == 0x61 && buf[39] == 0x70 && buf[40] == 0x70 && buf[41] == 0x6C &&
		buf[42] == 0x69 && buf[43] == 0x63 && buf[44] == 0x61 && buf[45] == 0x74 &&
		buf[46] == 0x69 && buf[47] == 0x6F && buf[48] == 0x6E && buf[49] == 0x2F &&
		buf[50] == 0x65 && buf[51] == 0x70 && buf[52] == 0x75 && buf[53] == 0x62 &&
		buf[54] == 0x2B && buf[55] == 0x7A && buf[56] == 0x69 && buf[57] == 0x70
}

func Zip(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x50 && buf[1] == 0x4B &&
		(buf[2] == 0x3 || buf[2] == 0x5 || buf[2] == 0x7) &&
		(buf[3] == 0x4 || buf[3] == 0x6 || buf[3] == 0x8)
}

func Tar(buf []byte) bool {
	return len(buf) > 261 &&
		buf[257] == 0x75 && buf[258] == 0x73 &&
		buf[259] == 0x74 && buf[260] == 0x61 &&
		buf[261] == 0x72
}

func Rar(buf []byte) bool {
	return len(buf) > 6 &&
		buf[0] == 0x52 && buf[1] == 0x61 && buf[2] == 0x72 &&
		buf[3] == 0x21 && buf[4] == 0x1A && buf[5] == 0x7 &&
		(buf[6] == 0x0 || buf[6] == 0x1)
}

func Gz(buf []byte) bool {
	return len(buf) > 2 &&
		buf[0] == 0x1F && buf[1] == 0x8B && buf[2] == 0x8
}

func Bz2(buf []byte) bool {
	return len(buf) > 2 &&
		buf[0] == 0x42 && buf[1] == 0x5A && buf[2] == 0x68
}

func SevenZ(buf []byte) bool {
	return len(buf) > 5 &&
		buf[0] == 0x37 && buf[1] == 0x7A && buf[2] == 0xBC &&
		buf[3] == 0xAF && buf[4] == 0x27 && buf[5] == 0x1C
}

func Pdf(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x25 && buf[1] == 0x50 &&
		buf[2] == 0x44 && buf[3] == 0x46
}

func Exe(buf []byte) bool {
	return len(buf) > 1 &&
		buf[0] == 0x4D && buf[1] == 0x5A
}

func Swf(buf []byte) bool {
	return len(buf) > 2 &&
		(buf[0] == 0x43 || buf[0] == 0x46) &&
		buf[1] == 0x57 && buf[2] == 0x53
}

func Rtf(buf []byte) bool {
	return len(buf) > 4 &&
		buf[0] == 0x7B && buf[1] == 0x5C &&
		buf[2] == 0x72 && buf[3] == 0x74 &&
		buf[4] == 0x66
}

func Nes(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x4E && buf[1] == 0x45 &&
		buf[2] == 0x53 && buf[3] == 0x1A
}

func Crx(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x43 && buf[1] == 0x72 &&
		buf[2] == 0x32 && buf[3] == 0x34
}

func Cab(buf []byte) bool {
	return len(buf) > 3 &&
		((buf[0] == 0x4D && buf[1] == 0x53 && buf[2] == 0x43 && buf[3] == 0x46) ||
			(buf[0] == 0x49 && buf[1] == 0x53 && buf[2] == 0x63 && buf[3] == 0x28))
}

func Eot(buf []byte) bool {
	return len(buf) > 35 &&
		buf[34] == 0x4C && buf[35] == 0x50 &&
		((buf[8] == 0x02 && buf[9] == 0x00 &&
			buf[10] == 0x01) || (buf[8] == 0x01 &&
			buf[9] == 0x00 && buf[10] == 0x00) ||
			(buf[8] == 0x02 && buf[9] == 0x00 &&
				buf[10] == 0x02))
}

func Ps(buf []byte) bool {
	return len(buf) > 1 &&
		buf[0] == 0x25 && buf[1] == 0x21
}

func Xz(buf []byte) bool {
	return len(buf) > 5 &&
		buf[0] == 0xFD && buf[1] == 0x37 &&
		buf[2] == 0x7A && buf[3] == 0x58 &&
		buf[4] == 0x5A && buf[5] == 0x00
}

func Sqlite(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x53 && buf[1] == 0x51 &&
		buf[2] == 0x4C && buf[3] == 0x69
}

func Deb(buf []byte) bool {
	return len(buf) > 20 &&
		buf[0] == 0x21 && buf[1] == 0x3C && buf[2] == 0x61 &&
		buf[3] == 0x72 && buf[4] == 0x63 && buf[5] == 0x68 &&
		buf[6] == 0x3E && buf[7] == 0x0A && buf[8] == 0x64 &&
		buf[9] == 0x65 && buf[10] == 0x62 && buf[11] == 0x69 &&
		buf[12] == 0x61 && buf[13] == 0x6E && buf[14] == 0x2D &&
		buf[15] == 0x62 && buf[16] == 0x69 && buf[17] == 0x6E &&
		buf[18] == 0x61 && buf[19] == 0x72 && buf[20] == 0x79
}

func Ar(buf []byte) bool {
	return len(buf) > 6 &&
		buf[0] == 0x21 && buf[1] == 0x3C &&
		buf[2] == 0x61 && buf[3] == 0x72 &&
		buf[4] == 0x63 && buf[5] == 0x68 &&
		buf[6] == 0x3E
}

func Z(buf []byte) bool {
	return len(buf) > 1 &&
		((buf[0] == 0x1F && buf[1] == 0xA0) ||
			(buf[0] == 0x1F && buf[1] == 0x9D))
}

func Lz(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x4C && buf[1] == 0x5A &&
		buf[2] == 0x49 && buf[3] == 0x50
}
<|endoftext|>


<commit_before>package pis

func checkInclusion(s1, s2 string) bool {
	// time complexity O((n2-n1)*n1+n1)
	// space complexity O((n2-n1)*n1)
	n1, n2 := len(s1), len(s2)
	if n1 == 0 || n2 == 0 || n1 > n2 {
		return false
	}
	cache1 := make(map[byte]int, n1)
	for i := range s1 {
		cache1[s1[i]]++
	}
	for i := 0; i <= n2-n1; i++ {
		cache2 := make(map[byte]int, n1)
		for j := i; j < i+n1; j++ {
			cache2[s2[j]]++
		}
		if comp(cache1, cache2) {
			return true
		}
	}
	return false
}

func comp(m1, m2 map[byte]int) bool {
	for k := range m1 {
		if m1[k]-m2[k] != 0 {
			return false
		}
	}
	return true
}
<commit_msg>use []int instead<commit_after>package pis

func checkInclusion(s1, s2 string) bool {
	// return hashmap(s1, s2)
	return array(s1, s2)
}

func array(s1, s2 string) bool {
	n1, n2 := len(s1), len(s2)
	if n1 == 0 || n2 == 0 || n1 > n2 {
		return false
	}
	cache1 := make([]int, 26)
	for i := range s1 {
		cache1[int(s1[i]-'a')]++
	}
	for i := 0; i <= n2-n1; i++ {
		cache2 := make([]int, 26)
		for j := i; j < i+n1; j++ {
			cache2[int(s2[j]-'a')]++
		}
		if compArray(cache1, cache2) {
			return true
		}
	}
	return false
}

func compArray(a1, a2 []int) bool {
	for i := 0; i < len(a1); i++ {
		if a1[i] != a2[i] {
			return false
		}
	}
	return true
}

func hashmap(s1, s2 string) bool {
	// time complexity O((n2-n1)*n1+n1)
	// space complexity O((n2-n1)*n1)
	n1, n2 := len(s1), len(s2)
	if n1 == 0 || n2 == 0 || n1 > n2 {
		return false
	}
	cache1 := make(map[byte]int, n1)
	for i := range s1 {
		cache1[s1[i]]++
	}
	for i := 0; i <= n2-n1; i++ {
		cache2 := make(map[byte]int, n1)
		for j := i; j < i+n1; j++ {
			cache2[s2[j]]++
		}
		if comp(cache1, cache2) {
			return true
		}
	}
	return false
}

func comp(m1, m2 map[byte]int) bool {
	for k := range m1 {
		if m1[k]-m2[k] != 0 {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>package consul

import (
	"bytes"
	"fmt"
	"log"
	"strconv"

	"github.com/armon/consul-api"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceConsulKeys() *schema.Resource {
	return &schema.Resource{
		Create: resourceConsulKeysCreate,
		Update: resourceConsulKeysCreate,
		Read:   resourceConsulKeysRead,
		Delete: resourceConsulKeysDelete,

		Schema: map[string]*schema.Schema{
			"datacenter": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"token": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},

			"keys": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},

						"path": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},

						"value": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Computed: true,
						},

						"default": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
						},

						"delete": &schema.Schema{
							Type:     schema.TypeBool,
							Optional: true,
						},
					},
				},
				Set: resourceConsulKeysHash,
			},

			"var": &schema.Schema{
				Type:     schema.TypeMap,
				Computed: true,
			},
		},
	}
}

func resourceConsulKeysHash(v interface{}) int {
	var buf bytes.Buffer
	m := v.(map[string]interface{})
	buf.WriteString(fmt.Sprintf("%s-", m["name"].(string)))
	buf.WriteString(fmt.Sprintf("%s-", m["path"].(string)))
	return hashcode.String(buf.String())
}

func resourceConsulKeysCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*consulapi.Client)
	kv := client.KV()

	// Resolve the datacenter first, all the other keys are dependent
	// on this.
	var dc string
	if v, ok := d.GetOk("datacenter"); ok {
		dc = v.(string)
		log.Printf("[DEBUG] Consul datacenter: %s", dc)
	} else {
		log.Printf("[DEBUG] Resolving Consul datacenter...")
		var err error
		dc, err = get_dc(client)
		if err != nil {
			return err
		}
	}
	var token string
	if v, ok := d.GetOk("token"); ok {
		token = v.(string)
	}

	// Setup the operations using the datacenter
	qOpts := consulapi.QueryOptions{Datacenter: dc, Token: token}
	wOpts := consulapi.WriteOptions{Datacenter: dc, Token: token}

	// Store the computed vars
	vars := make(map[string]string)

	// Extract the keys
	keys := d.Get("keys").(*schema.Set).List()
	for _, raw := range keys {
		key, path, sub, err := parse_key(raw)
		if err != nil {
			return err
		}

		if valueRaw, ok := sub["value"]; ok {
			value, ok := valueRaw.(string)
			if !ok {
				return fmt.Errorf("Failed to get value for key '%s'", key)
			}

			log.Printf("[DEBUG] Setting key '%s' to '%v' in %s", path, value, dc)
			pair := consulapi.KVPair{Key: path, Value: []byte(value)}
			if _, err := kv.Put(&pair, &wOpts); err != nil {
				return fmt.Errorf("Failed to set Consul key '%s': %v", path, err)
			}
			vars[key] = value
			sub["value"] = value

		} else {
			log.Printf("[DEBUG] Getting key '%s' in %s", path, dc)
			pair, _, err := kv.Get(path, &qOpts)
			if err != nil {
				return fmt.Errorf("Failed to get Consul key '%s': %v", path, err)
			}
			value := attribute_value(sub, key, pair)
			vars[key] = value
			sub["value"] = value
		}
	}

	// Update the resource
	d.SetId("consul")
	d.Set("datacenter", dc)
	d.Set("keys", keys)
	d.Set("var", vars)
	return nil
}

func resourceConsulKeysRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*consulapi.Client)
	kv := client.KV()

	// Get the DC, error if not available.
	var dc string
	if v, ok := d.GetOk("datacenter"); ok {
		dc = v.(string)
		log.Printf("[DEBUG] Consul datacenter: %s", dc)
	} else {
		return fmt.Errorf("Missing datacenter configuration")
	}
	var token string
	if v, ok := d.GetOk("token"); ok {
		token = v.(string)
	}

	// Setup the operations using the datacenter
	qOpts := consulapi.QueryOptions{Datacenter: dc, Token: token}

	// Store the computed vars
	vars := make(map[string]string)

	// Extract the keys
	keys := d.Get("keys").(*schema.Set).List()
	for _, raw := range keys {
		key, path, sub, err := parse_key(raw)
		if err != nil {
			return err
		}

		log.Printf("[DEBUG] Refreshing value of key '%s' in %s", path, dc)
		pair, _, err := kv.Get(path, &qOpts)
		if err != nil {
			return fmt.Errorf("Failed to get value for path '%s' from Consul: %v", path, err)
		}

		value := attribute_value(sub, key, pair)
		vars[key] = value
		sub["value"] = value
	}

	// Update the resource
	d.Set("keys", keys)
	d.Set("var", vars)
	return nil
}

func resourceConsulKeysDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*consulapi.Client)
	kv := client.KV()

	// Get the DC, error if not available.
	var dc string
	if v, ok := d.GetOk("datacenter"); ok {
		dc = v.(string)
		log.Printf("[DEBUG] Consul datacenter: %s", dc)
	} else {
		return fmt.Errorf("Missing datacenter configuration")
	}
	var token string
	if v, ok := d.GetOk("token"); ok {
		token = v.(string)
	}

	// Setup the operations using the datacenter
	wOpts := consulapi.WriteOptions{Datacenter: dc, Token: token}

	// Extract the keys
	keys := d.Get("keys").(*schema.Set).List()
	for _, raw := range keys {
		_, path, sub, err := parse_key(raw)
		if err != nil {
			return err
		}

		// Ignore if the key is non-managed
		shouldDelete, ok := sub["delete"].(bool)
		if !ok || !shouldDelete {
			continue
		}

		log.Printf("[DEBUG] Deleting key '%s' in %s", path, dc)
		if _, err := kv.Delete(path, &wOpts); err != nil {
			return fmt.Errorf("Failed to delete Consul key '%s': %v", path, err)
		}
	}

	// Clear the ID
	d.SetId("")
	return nil
}

// parse_key is used to parse a key into a name, path, config or error
func parse_key(raw interface{}) (string, string, map[string]interface{}, error) {
	sub, ok := raw.(map[string]interface{})
	if !ok {
		return "", "", nil, fmt.Errorf("Failed to unroll: %#v", raw)
	}

	key, ok := sub["name"].(string)
	if !ok {
		return "", "", nil, fmt.Errorf("Failed to expand key '%#v'", sub)
	}

	path, ok := sub["path"].(string)
	if !ok {
		return "", "", nil, fmt.Errorf("Failed to get path for key '%s'", key)
	}
	return key, path, sub, nil
}

// attribute_value determines the value for a key, potentially
// using a default value if provided.
func attribute_value(sub map[string]interface{}, key string, pair *consulapi.KVPair) string {
	// Use the value if given
	if pair != nil {
		return string(pair.Value)
	}

	// Use a default if given
	if raw, ok := sub["default"]; ok {
		switch def := raw.(type) {
		case string:
			return def
		case bool:
			return strconv.FormatBool(def)
		}
	}

	// No value
	return ""
}

// get_dc is used to get the datacenter of the local agent
func get_dc(client *consulapi.Client) (string, error) {
	info, err := client.Agent().Self()
	if err != nil {
		return "", fmt.Errorf("Failed to get datacenter from Consul agent: %v", err)
	}
	dc := info["Config"]["Datacenter"].(string)
	return dc, nil
}
<commit_msg>provider/consul: Change keyword to key from keys<commit_after>package consul

import (
	"bytes"
	"fmt"
	"log"
	"strconv"

	"github.com/armon/consul-api"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceConsulKeys() *schema.Resource {
	return &schema.Resource{
		Create: resourceConsulKeysCreate,
		Update: resourceConsulKeysCreate,
		Read:   resourceConsulKeysRead,
		Delete: resourceConsulKeysDelete,

		Schema: map[string]*schema.Schema{
			"datacenter": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"token": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},

			"key": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},

						"path": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},

						"value": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Computed: true,
						},

						"default": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
						},

						"delete": &schema.Schema{
							Type:     schema.TypeBool,
							Optional: true,
						},
					},
				},
				Set: resourceConsulKeysHash,
			},

			"var": &schema.Schema{
				Type:     schema.TypeMap,
				Computed: true,
			},
		},
	}
}

func resourceConsulKeysHash(v interface{}) int {
	var buf bytes.Buffer
	m := v.(map[string]interface{})
	buf.WriteString(fmt.Sprintf("%s-", m["name"].(string)))
	buf.WriteString(fmt.Sprintf("%s-", m["path"].(string)))
	return hashcode.String(buf.String())
}

func resourceConsulKeysCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*consulapi.Client)
	kv := client.KV()

	// Resolve the datacenter first, all the other keys are dependent
	// on this.
	var dc string
	if v, ok := d.GetOk("datacenter"); ok {
		dc = v.(string)
		log.Printf("[DEBUG] Consul datacenter: %s", dc)
	} else {
		log.Printf("[DEBUG] Resolving Consul datacenter...")
		var err error
		dc, err = get_dc(client)
		if err != nil {
			return err
		}
	}
	var token string
	if v, ok := d.GetOk("token"); ok {
		token = v.(string)
	}

	// Setup the operations using the datacenter
	qOpts := consulapi.QueryOptions{Datacenter: dc, Token: token}
	wOpts := consulapi.WriteOptions{Datacenter: dc, Token: token}

	// Store the computed vars
	vars := make(map[string]string)

	// Extract the keys
	keys := d.Get("key").(*schema.Set).List()
	for _, raw := range keys {
		key, path, sub, err := parse_key(raw)
		if err != nil {
			return err
		}

		if valueRaw, ok := sub["value"]; ok {
			value, ok := valueRaw.(string)
			if !ok {
				return fmt.Errorf("Failed to get value for key '%s'", key)
			}

			log.Printf("[DEBUG] Setting key '%s' to '%v' in %s", path, value, dc)
			pair := consulapi.KVPair{Key: path, Value: []byte(value)}
			if _, err := kv.Put(&pair, &wOpts); err != nil {
				return fmt.Errorf("Failed to set Consul key '%s': %v", path, err)
			}
			vars[key] = value
			sub["value"] = value

		} else {
			log.Printf("[DEBUG] Getting key '%s' in %s", path, dc)
			pair, _, err := kv.Get(path, &qOpts)
			if err != nil {
				return fmt.Errorf("Failed to get Consul key '%s': %v", path, err)
			}
			value := attribute_value(sub, key, pair)
			vars[key] = value
			sub["value"] = value
		}
	}

	// Update the resource
	d.SetId("consul")
	d.Set("datacenter", dc)
	d.Set("key", keys)
	d.Set("var", vars)
	return nil
}

func resourceConsulKeysRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*consulapi.Client)
	kv := client.KV()

	// Get the DC, error if not available.
	var dc string
	if v, ok := d.GetOk("datacenter"); ok {
		dc = v.(string)
		log.Printf("[DEBUG] Consul datacenter: %s", dc)
	} else {
		return fmt.Errorf("Missing datacenter configuration")
	}
	var token string
	if v, ok := d.GetOk("token"); ok {
		token = v.(string)
	}

	// Setup the operations using the datacenter
	qOpts := consulapi.QueryOptions{Datacenter: dc, Token: token}

	// Store the computed vars
	vars := make(map[string]string)

	// Extract the keys
	keys := d.Get("key").(*schema.Set).List()
	for _, raw := range keys {
		key, path, sub, err := parse_key(raw)
		if err != nil {
			return err
		}

		log.Printf("[DEBUG] Refreshing value of key '%s' in %s", path, dc)
		pair, _, err := kv.Get(path, &qOpts)
		if err != nil {
			return fmt.Errorf("Failed to get value for path '%s' from Consul: %v", path, err)
		}

		value := attribute_value(sub, key, pair)
		vars[key] = value
		sub["value"] = value
	}

	// Update the resource
	d.Set("key", keys)
	d.Set("var", vars)
	return nil
}

func resourceConsulKeysDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*consulapi.Client)
	kv := client.KV()

	// Get the DC, error if not available.
	var dc string
	if v, ok := d.GetOk("datacenter"); ok {
		dc = v.(string)
		log.Printf("[DEBUG] Consul datacenter: %s", dc)
	} else {
		return fmt.Errorf("Missing datacenter configuration")
	}
	var token string
	if v, ok := d.GetOk("token"); ok {
		token = v.(string)
	}

	// Setup the operations using the datacenter
	wOpts := consulapi.WriteOptions{Datacenter: dc, Token: token}

	// Extract the keys
	keys := d.Get("key").(*schema.Set).List()
	for _, raw := range keys {
		_, path, sub, err := parse_key(raw)
		if err != nil {
			return err
		}

		// Ignore if the key is non-managed
		shouldDelete, ok := sub["delete"].(bool)
		if !ok || !shouldDelete {
			continue
		}

		log.Printf("[DEBUG] Deleting key '%s' in %s", path, dc)
		if _, err := kv.Delete(path, &wOpts); err != nil {
			return fmt.Errorf("Failed to delete Consul key '%s': %v", path, err)
		}
	}

	// Clear the ID
	d.SetId("")
	return nil
}

// parse_key is used to parse a key into a name, path, config or error
func parse_key(raw interface{}) (string, string, map[string]interface{}, error) {
	sub, ok := raw.(map[string]interface{})
	if !ok {
		return "", "", nil, fmt.Errorf("Failed to unroll: %#v", raw)
	}

	key, ok := sub["name"].(string)
	if !ok {
		return "", "", nil, fmt.Errorf("Failed to expand key '%#v'", sub)
	}

	path, ok := sub["path"].(string)
	if !ok {
		return "", "", nil, fmt.Errorf("Failed to get path for key '%s'", key)
	}
	return key, path, sub, nil
}

// attribute_value determines the value for a key, potentially
// using a default value if provided.
func attribute_value(sub map[string]interface{}, key string, pair *consulapi.KVPair) string {
	// Use the value if given
	if pair != nil {
		return string(pair.Value)
	}

	// Use a default if given
	if raw, ok := sub["default"]; ok {
		switch def := raw.(type) {
		case string:
			return def
		case bool:
			return strconv.FormatBool(def)
		}
	}

	// No value
	return ""
}

// get_dc is used to get the datacenter of the local agent
func get_dc(client *consulapi.Client) (string, error) {
	info, err := client.Agent().Self()
	if err != nil {
		return "", fmt.Errorf("Failed to get datacenter from Consul agent: %v", err)
	}
	dc := info["Config"]["Datacenter"].(string)
	return dc, nil
}
<|endoftext|>


<commit_before>package activity

import (
	"errors"
	"fmt"
	"github.com/antonienko/goandroid/device"
	"strings"
	"time"
)

// Activity struct represents activity subsystem for associated android device.
type Activity struct {
	dev device.Device // Device instance
}

// NewActivity method gives an initial
// ized instance of activity manager.
// It takes device.Device is a parameter and returns an instance of activity.Activity
// struct.
func NewActivity(dev device.Device) Activity {
	return Activity{dev: dev}
}

// StartActivity method launches a activity on device. See "am start" for more
// more details regarding this command. It takes canonilcal class name as its
// first parameter which defines the package and class name of the activity to
// be launched and a list of accepable options by am start command. This method
// requires root access on device and returns error if root access can not be
// granted. It also returns other adb related errors if something goes wrong.
//
// TODO Check if activity can not be launched or not found or other command
// related outputs.
func (am Activity) StartActivity(canonicalClass string, options ...string) error {
	err := am.dev.Root()
	if err != nil {
		return err
	}
	if len(options) > 0 {
		cmd := []string{}
		cmd = append(cmd, "start")
		cmd = append(cmd, options...)
		cmd = append(cmd, canonicalClass)
		_, err := am.dev.Shell("am", cmd...)
		if err != nil {
			return err
		}
	} else {
		_, err := am.dev.Shell("am", "start", canonicalClass)
		if err != nil {
			return err
		}
	}
	return nil
}

func (am Activity) StopPackage(packageName string) error {
	err := am.dev.Root()
	if err != nil {
		return err
	}
	_, err = am.dev.Shell("am", "force-stop", packageName)
	if err != nil {
		return err
	}
	return nil
}

// GetFocusedActivity method provides currently focused activity on device screen.
// It uses adb command "dumpsys acyivity" command to determine focused activity
// and extracts package name from the line "mFocusedActivity".
// It returens error if something went wrong on adb side or the method is unable to
// determine the activity canonical package name.
func (am Activity) GetFocusedActivity() (string, error) {
	ret, err := am.dev.Shell("dumpsys", "activity", "|", "grep", "mFocusedActivity:")
	if err != nil {
		return "", err
	}
	if !strings.Contains(ret, "mFocusedActivity:") {
		return "", errors.New("Unable to detect focused activity")
	}
	trimmed := strings.TrimSpace(ret)
	parts := strings.Split(trimmed, " ")
	if len(parts) < 3 {
		return "", errors.New("Unable to determine activity canonical package name from line : " + trimmed)
	}
	return parts[3], nil
}

// IsActivityFocused method checks if given activity is focused on device
// screen or not. If the currently focused activity canonical package contains
// given name then it returns true, and its not case sensitive in nature.
// It returns error if activity information can not be parsed or somethis else
// went wront in between.
func (am Activity) IsActivityFocused(name string) (bool, error) {
	activity, err := am.GetFocusedActivity()
	if err != nil {
		return false, err
	}
	if strings.Contains(strings.ToLower(activity), strings.ToLower(name)) {
		return true, nil
	}
	return false, nil
}

// WaitForActivityToFocus method waits for given activity name to be focused on
// device screen in given timeout period. If activity is not focused within timeout
// then error is returned. Also error is returned if something else went wrong such as
// adb error or current focused activity can not be determined. This method returns
// instantly when given activity is found to be focused on screen.
func (am Activity) WaitForActivityToFocus(name string, timeout int) error {
	startTime := time.Now()
	for {
		currentTime := time.Now()
		delta := currentTime.Sub(startTime)
		if delta.Seconds() >= float64(timeout) {
			break
		}
		stat, err := am.IsActivityFocused(name)
		if err != nil {
			return err
		}
		if stat {
			return nil
		}
	}
	return errors.New(fmt.Sprintf("Activity %s is not focused on screen within timeout of %d seconds", name, timeout))
}

func (am Activity) WaitForeverForActivity(name string, sampleRate time.Duration, control chan bool) error {
	for {
		select {
		case <-control:
			fmt.Println("Stop signal on waitForActivity")
			return errors.New("STOP SIGNAL")
		default:
			fmt.Printf("Waiting forever %s",sampleRate)
			stat, err := am.IsActivityFocused(name)
			if err != nil {
				return err
			}
			if stat {
				return nil
			}
			time.Sleep(sampleRate)
		}

	}
}

func (am Activity) WaitForActivityToFocusInterruptable(name string, timeout int, control chan bool) error {
	startTime := time.Now()
	for {
		select {
		case <-control:
			fmt.Println("Stop signal on WaitForActivityToFocusInterruptable")
			return errors.New(fmt.Sprintf("Couldn't see Activity %s before the stop signal", name))
		default:
			currentTime := time.Now()
			delta := currentTime.Sub(startTime)
			if delta.Seconds() >= float64(timeout) {
				return errors.New(fmt.Sprintf("Activity %s is not focused on screen within timeout of %d seconds", name, timeout))
			}
			stat, err := am.IsActivityFocused(name)
			if err != nil {
				return err
			}
			if stat {
				return nil
			}
		}
	}
}<commit_msg>Remove prints<commit_after>package activity

import (
	"errors"
	"fmt"
	"github.com/antonienko/goandroid/device"
	"strings"
	"time"
)

// Activity struct represents activity subsystem for associated android device.
type Activity struct {
	dev device.Device // Device instance
}

// NewActivity method gives an initial
// ized instance of activity manager.
// It takes device.Device is a parameter and returns an instance of activity.Activity
// struct.
func NewActivity(dev device.Device) Activity {
	return Activity{dev: dev}
}

// StartActivity method launches a activity on device. See "am start" for more
// more details regarding this command. It takes canonilcal class name as its
// first parameter which defines the package and class name of the activity to
// be launched and a list of accepable options by am start command. This method
// requires root access on device and returns error if root access can not be
// granted. It also returns other adb related errors if something goes wrong.
//
// TODO Check if activity can not be launched or not found or other command
// related outputs.
func (am Activity) StartActivity(canonicalClass string, options ...string) error {
	err := am.dev.Root()
	if err != nil {
		return err
	}
	if len(options) > 0 {
		cmd := []string{}
		cmd = append(cmd, "start")
		cmd = append(cmd, options...)
		cmd = append(cmd, canonicalClass)
		_, err := am.dev.Shell("am", cmd...)
		if err != nil {
			return err
		}
	} else {
		_, err := am.dev.Shell("am", "start", canonicalClass)
		if err != nil {
			return err
		}
	}
	return nil
}

func (am Activity) StopPackage(packageName string) error {
	err := am.dev.Root()
	if err != nil {
		return err
	}
	_, err = am.dev.Shell("am", "force-stop", packageName)
	if err != nil {
		return err
	}
	return nil
}

// GetFocusedActivity method provides currently focused activity on device screen.
// It uses adb command "dumpsys acyivity" command to determine focused activity
// and extracts package name from the line "mFocusedActivity".
// It returens error if something went wrong on adb side or the method is unable to
// determine the activity canonical package name.
func (am Activity) GetFocusedActivity() (string, error) {
	ret, err := am.dev.Shell("dumpsys", "activity", "|", "grep", "mFocusedActivity:")
	if err != nil {
		return "", err
	}
	if !strings.Contains(ret, "mFocusedActivity:") {
		return "", errors.New("Unable to detect focused activity")
	}
	trimmed := strings.TrimSpace(ret)
	parts := strings.Split(trimmed, " ")
	if len(parts) < 3 {
		return "", errors.New("Unable to determine activity canonical package name from line : " + trimmed)
	}
	return parts[3], nil
}

// IsActivityFocused method checks if given activity is focused on device
// screen or not. If the currently focused activity canonical package contains
// given name then it returns true, and its not case sensitive in nature.
// It returns error if activity information can not be parsed or somethis else
// went wront in between.
func (am Activity) IsActivityFocused(name string) (bool, error) {
	activity, err := am.GetFocusedActivity()
	if err != nil {
		return false, err
	}
	if strings.Contains(strings.ToLower(activity), strings.ToLower(name)) {
		return true, nil
	}
	return false, nil
}

// WaitForActivityToFocus method waits for given activity name to be focused on
// device screen in given timeout period. If activity is not focused within timeout
// then error is returned. Also error is returned if something else went wrong such as
// adb error or current focused activity can not be determined. This method returns
// instantly when given activity is found to be focused on screen.
func (am Activity) WaitForActivityToFocus(name string, timeout int) error {
	startTime := time.Now()
	for {
		currentTime := time.Now()
		delta := currentTime.Sub(startTime)
		if delta.Seconds() >= float64(timeout) {
			break
		}
		stat, err := am.IsActivityFocused(name)
		if err != nil {
			return err
		}
		if stat {
			return nil
		}
	}
	return errors.New(fmt.Sprintf("Activity %s is not focused on screen within timeout of %d seconds", name, timeout))
}

func (am Activity) WaitForeverForActivity(name string, sampleRate time.Duration, control chan bool) error {
	for {
		select {
		case <-control:
			return errors.New("STOP SIGNAL")
		default:
			stat, err := am.IsActivityFocused(name)
			if err != nil {
				return err
			}
			if stat {
				return nil
			}
			time.Sleep(sampleRate)
		}

	}
}

func (am Activity) WaitForActivityToFocusInterruptable(name string, timeout int, control chan bool) error {
	startTime := time.Now()
	for {
		select {
		case <-control:
			fmt.Println("Stop signal on WaitForActivityToFocusInterruptable")
			return errors.New(fmt.Sprintf("Couldn't see Activity %s before the stop signal", name))
		default:
			currentTime := time.Now()
			delta := currentTime.Sub(startTime)
			if delta.Seconds() >= float64(timeout) {
				return errors.New(fmt.Sprintf("Activity %s is not focused on screen within timeout of %d seconds", name, timeout))
			}
			stat, err := am.IsActivityFocused(name)
			if err != nil {
				return err
			}
			if stat {
				return nil
			}
		}
	}
}<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccDataSourceAwsRoute53Zone(t *testing.T) {
	rInt := acctest.RandInt()
	publicResourceName := "aws_route53_zone.test"
	publicDomain := fmt.Sprintf("terraformtestacchz-%d.com.", rInt)
	privateResourceName := "aws_route53_zone.test_private"
	privateDomain := fmt.Sprintf("test.acc-%d.", rInt)

	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccDataSourceAwsRoute53ZoneConfig(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccDataSourceAwsRoute53ZoneCheck(
						publicResourceName, "data.aws_route53_zone.by_zone_id", publicDomain),
					testAccDataSourceAwsRoute53ZoneCheck(
						publicResourceName, "data.aws_route53_zone.by_name", publicDomain),
					testAccDataSourceAwsRoute53ZoneCheck(
						privateResourceName, "data.aws_route53_zone.by_vpc", privateDomain),
					testAccDataSourceAwsRoute53ZoneCheck(
						privateResourceName, "data.aws_route53_zone.by_tag", privateDomain),
				),
			},
		},
	})
}

// rsName for the name of the created resource
// dsName for the name of the created data source
// zName for the name of the domain
func testAccDataSourceAwsRoute53ZoneCheck(rsName, dsName, zName string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[rsName]
		if !ok {
			return fmt.Errorf("root module has no resource called %s", rsName)
		}

		hostedZone, ok := s.RootModule().Resources[dsName]
		if !ok {
			return fmt.Errorf("can't find zone %q in state", dsName)
		}

		attr := rs.Primary.Attributes
		if attr["id"] != hostedZone.Primary.Attributes["id"] {
			return fmt.Errorf(
				"id is %s; want %s",
				attr["id"],
				hostedZone.Primary.Attributes["id"],
			)
		}

		if attr["name"] != zName {
			return fmt.Errorf("Route53 Zone name is %q; want %q", attr["name"], zName)
		}

		return nil
	}
}

func testAccDataSourceAwsRoute53ZoneConfig(rInt int) string {
	return fmt.Sprintf(`
	provider "aws" {
		region = "us-east-2"
	}

	resource "aws_vpc" "test" {
		cidr_block = "172.16.0.0/16"
	}

	resource "aws_route53_zone" "test_private" {
		name = "test.acc-%d."
		vpc_id = "${aws_vpc.test.id}"
		tags {
			Environment = "dev-%d"
		}
	}

	data "aws_route53_zone" "by_vpc" {
	 name = "${aws_route53_zone.test_private.name}"
	 vpc_id = "${aws_vpc.test.id}"
	}

	data "aws_route53_zone" "by_tag" {
	 name = "${aws_route53_zone.test_private.name}"
	 private_zone = true
	 tags {
		 Environment = "dev-%d"
	 }
	}

	resource "aws_route53_zone" "test" {
		name = "terraformtestacchz-%d.com."
	}

	data "aws_route53_zone" "by_zone_id" {
		zone_id = "${aws_route53_zone.test.zone_id}"
	}

	data "aws_route53_zone" "by_name" {
		name = "${data.aws_route53_zone.by_zone_id.name}"
	}`, rInt, rInt, rInt, rInt)
}
<commit_msg>move this test to new region<commit_after>package aws

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccDataSourceAwsRoute53Zone(t *testing.T) {
	rInt := acctest.RandInt()
	publicResourceName := "aws_route53_zone.test"
	publicDomain := fmt.Sprintf("terraformtestacchz-%d.com.", rInt)
	privateResourceName := "aws_route53_zone.test_private"
	privateDomain := fmt.Sprintf("test.acc-%d.", rInt)

	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccDataSourceAwsRoute53ZoneConfig(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccDataSourceAwsRoute53ZoneCheck(
						publicResourceName, "data.aws_route53_zone.by_zone_id", publicDomain),
					testAccDataSourceAwsRoute53ZoneCheck(
						publicResourceName, "data.aws_route53_zone.by_name", publicDomain),
					testAccDataSourceAwsRoute53ZoneCheck(
						privateResourceName, "data.aws_route53_zone.by_vpc", privateDomain),
					testAccDataSourceAwsRoute53ZoneCheck(
						privateResourceName, "data.aws_route53_zone.by_tag", privateDomain),
				),
			},
		},
	})
}

// rsName for the name of the created resource
// dsName for the name of the created data source
// zName for the name of the domain
func testAccDataSourceAwsRoute53ZoneCheck(rsName, dsName, zName string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[rsName]
		if !ok {
			return fmt.Errorf("root module has no resource called %s", rsName)
		}

		hostedZone, ok := s.RootModule().Resources[dsName]
		if !ok {
			return fmt.Errorf("can't find zone %q in state", dsName)
		}

		attr := rs.Primary.Attributes
		if attr["id"] != hostedZone.Primary.Attributes["id"] {
			return fmt.Errorf(
				"id is %s; want %s",
				attr["id"],
				hostedZone.Primary.Attributes["id"],
			)
		}

		if attr["name"] != zName {
			return fmt.Errorf("Route53 Zone name is %q; want %q", attr["name"], zName)
		}

		return nil
	}
}

func testAccDataSourceAwsRoute53ZoneConfig(rInt int) string {
	return fmt.Sprintf(`
	provider "aws" {
		region = "us-east-1"
	}

	resource "aws_vpc" "test" {
		cidr_block = "172.16.0.0/16"
	}

	resource "aws_route53_zone" "test_private" {
		name = "test.acc-%d."
		vpc_id = "${aws_vpc.test.id}"
		tags {
			Environment = "dev-%d"
		}
	}

	data "aws_route53_zone" "by_vpc" {
	 name = "${aws_route53_zone.test_private.name}"
	 vpc_id = "${aws_vpc.test.id}"
	}

	data "aws_route53_zone" "by_tag" {
	 name = "${aws_route53_zone.test_private.name}"
	 private_zone = true
	 tags {
		 Environment = "dev-%d"
	 }
	}

	resource "aws_route53_zone" "test" {
		name = "terraformtestacchz-%d.com."
	}

	data "aws_route53_zone" "by_zone_id" {
		zone_id = "${aws_route53_zone.test.zone_id}"
	}

	data "aws_route53_zone" "by_name" {
		name = "${data.aws_route53_zone.by_zone_id.name}"
	}`, rInt, rInt, rInt, rInt)
}
<|endoftext|>


<commit_before>package webapp

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

	"appengine"
	"appengine/datastore"
	"appengine/user"

	"model"
)

var (
	index          = template.Must(template.ParseFiles("templates/admin/base.html", "templates/admin/index.html"))
	noEndDateFixup = template.Must(template.ParseFiles("templates/admin/fixup-no-end-date.html"))
)

type adminUser struct {
	*model.UserAccount
	LogoutURL string
	Token     *model.AdminXSRFToken
}

func newAdminUser(r *http.Request) *adminUser {
	c := appengine.NewContext(r)
	account, err := model.GetCurrentUserAccount(c)
	if account == nil {
		c.Errorf("Couldn't look up current user: %s", err)
		return nil
	}
	token, err := model.GetXSRFToken(c, account.AccountID)
	if err != nil {
		c.Errorf("Error looking up XSRF token: %s", err)
		return nil
	}
	SetXSRFToken(r, token)
	if !account.Role.IsStaff() {
		c.Errorf("User %s is not staff", account.Email)
		return nil
	}
	logout, err := user.LogoutURL(c, "/registration")
	if err != nil {
		c.Errorf("Error creating logout url: %s", err)
		return nil
	}
	return &adminUser{account, logout, GetXSRFToken(r)}
}

type adminHandler func(w http.ResponseWriter, r *http.Request, u *adminUser) *Error

func (fn adminHandler) Serve(w http.ResponseWriter, r *http.Request) *Error {
	u := newAdminUser(r)
	if u == nil {
		return UnauthorizedError(fmt.Errorf("No authorized staff member found"))
	}
	return fn(w, r, u)
}

func handle(path string, h adminHandler) {
	AppHandle("/admin"+path, h)
}

func init() {
	handle("", admin)
	handle("/fixup/no-end-date", fixupNoEndDate)
}

func regsWithNoDate(c appengine.Context) []*datastore.Key {
	q := datastore.NewQuery("Registration").
		Filter("Date =", time.Time{}).
		KeysOnly()
	keys, err := q.GetAll(c, nil)
	if err != nil {
		c.Errorf("Error looking up regs: %s", err)
		return nil
	}
	return keys
}

func admin(w http.ResponseWriter, r *http.Request, u *adminUser) *Error {
	c := appengine.NewContext(r)
	keys := regsWithNoDate(c)
	if err := index.Execute(w, map[string]interface{}{
		"Admin":     u,
		"Email":     "rwsims@gmail.com",
		"NoEndDate": keys,
	}); err != nil {
		return InternalError(err)
	}
	return nil
}

func fixupNoEndDate(w http.ResponseWriter, r *http.Request, u *adminUser) *Error {
	if r.Method != "POST" {
		if err := noEndDateFixup.Execute(w, map[string]interface{}{
			"XSRFToken": u.Token.Token,
			"Key":       r.FormValue("key"),
		}); err != nil {
			return InternalError(err)
		}
		return nil
	}
	if !u.Token.Validate(r.FormValue("xsrf_token")) {
		return UnauthorizedError(fmt.Errorf("XSRF token failed validation"))
	}
	c := appengine.NewContext(r)
	key, err := datastore.DecodeKey(r.FormValue("key"))
	if err != nil {
		return InternalError(fmt.Errorf("Error decoding key %s: %s", r.FormValue("key")))
	}
	reg := &model.Registration{}
	if err := datastore.Get(c, key, reg); err != nil {
		return InternalError(fmt.Errorf("Error getting registration: %s", err))
	}
	class := &model.Class{}
	if err := datastore.Get(c, key.Parent(), class); err != nil {
		return InternalError(fmt.Errorf("Error looking up class: %s", err))
	}
	reg.Date = class.GetExpirationTime()
	if _, err := datastore.Put(c, key, reg); err != nil {
		return InternalError(fmt.Errorf("Error writing registration: %s", err))
	}
	http.Redirect(w, r, "/admin", http.StatusFound)
	return nil
}
<commit_msg>Changed query to use 2013-01-01 as earliest time.<commit_after>package webapp

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

	"appengine"
	"appengine/datastore"
	"appengine/user"

	"model"
)

var (
	index          = template.Must(template.ParseFiles("templates/admin/base.html", "templates/admin/index.html"))
	noEndDateFixup = template.Must(template.ParseFiles("templates/admin/fixup-no-end-date.html"))
)

type adminUser struct {
	*model.UserAccount
	LogoutURL string
	Token     *model.AdminXSRFToken
}

func newAdminUser(r *http.Request) *adminUser {
	c := appengine.NewContext(r)
	account, err := model.GetCurrentUserAccount(c)
	if account == nil {
		c.Errorf("Couldn't look up current user: %s", err)
		return nil
	}
	token, err := model.GetXSRFToken(c, account.AccountID)
	if err != nil {
		c.Errorf("Error looking up XSRF token: %s", err)
		return nil
	}
	SetXSRFToken(r, token)
	if !account.Role.IsStaff() {
		c.Errorf("User %s is not staff", account.Email)
		return nil
	}
	logout, err := user.LogoutURL(c, "/registration")
	if err != nil {
		c.Errorf("Error creating logout url: %s", err)
		return nil
	}
	return &adminUser{account, logout, GetXSRFToken(r)}
}

type adminHandler func(w http.ResponseWriter, r *http.Request, u *adminUser) *Error

func (fn adminHandler) Serve(w http.ResponseWriter, r *http.Request) *Error {
	u := newAdminUser(r)
	if u == nil {
		return UnauthorizedError(fmt.Errorf("No authorized staff member found"))
	}
	return fn(w, r, u)
}

func handle(path string, h adminHandler) {
	AppHandle("/admin"+path, h)
}

func init() {
	handle("", admin)
	handle("/fixup/no-end-date", fixupNoEndDate)
}

func regsWithNoDate(c appengine.Context) []*datastore.Key {
	t, err := time.Parse("2006-01-02", "2013-01-01")
	if err != nil {
		panic(err)
	}
	q := datastore.NewQuery("Registration").
		Filter("Date <", t).
		KeysOnly()
	keys, err := q.GetAll(c, nil)
	if err != nil {
		c.Errorf("Error looking up regs: %s", err)
		return nil
	}
	return keys
}

func admin(w http.ResponseWriter, r *http.Request, u *adminUser) *Error {
	c := appengine.NewContext(r)
	keys := regsWithNoDate(c)
	if err := index.Execute(w, map[string]interface{}{
		"Admin":     u,
		"Email":     "rwsims@gmail.com",
		"NoEndDate": keys,
	}); err != nil {
		return InternalError(err)
	}
	return nil
}

func fixupNoEndDate(w http.ResponseWriter, r *http.Request, u *adminUser) *Error {
	if r.Method != "POST" {
		if err := noEndDateFixup.Execute(w, map[string]interface{}{
			"XSRFToken": u.Token.Token,
			"Key":       r.FormValue("key"),
		}); err != nil {
			return InternalError(err)
		}
		return nil
	}
	if !u.Token.Validate(r.FormValue("xsrf_token")) {
		return UnauthorizedError(fmt.Errorf("XSRF token failed validation"))
	}
	c := appengine.NewContext(r)
	key, err := datastore.DecodeKey(r.FormValue("key"))
	if err != nil {
		return InternalError(fmt.Errorf("Error decoding key %s: %s", r.FormValue("key")))
	}
	reg := &model.Registration{}
	if err := datastore.Get(c, key, reg); err != nil {
		return InternalError(fmt.Errorf("Error getting registration: %s", err))
	}
	class := &model.Class{}
	if err := datastore.Get(c, key.Parent(), class); err != nil {
		return InternalError(fmt.Errorf("Error looking up class: %s", err))
	}
	reg.Date = class.GetExpirationTime()
	if _, err := datastore.Put(c, key, reg); err != nil {
		return InternalError(fmt.Errorf("Error writing registration: %s", err))
	}
	http.Redirect(w, r, "/admin", http.StatusFound)
	return nil
}
<|endoftext|>


<commit_before>package migrations

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"io"

	"github.com/BurntSushi/migration"
)

func AddBuildEvents(tx migration.LimitedTx) error {
	_, err := tx.Exec(`
    CREATE TABLE build_events (
      id serial PRIMARY KEY,
      build_id integer REFERENCES builds (id),
			type varchar(32) NOT NULL,
      payload text NOT NULL
    )
  `)
	if err != nil {
		return err
	}

	cursor := 0

	for {
		var id int
		var buildLog sql.NullString

		err := tx.QueryRow(`
			SELECT id, log
			FROM builds
			WHERE id > $1
			LIMIT 1
		`, cursor).Scan(&id, &buildLog)
		if err != nil {
			if err == sql.ErrNoRows {
				break
			}

			return err
		}

		cursor = id

		if !buildLog.Valid {
			continue
		}

		logBuf := bytes.NewBufferString(buildLog.String)
		decoder := json.NewDecoder(logBuf)

		for {
			var entry logEntry

			err := decoder.Decode(&entry)
			if err != nil {
				if err != io.EOF {
					// non-JSON log; assume v0.0

					_, err = tx.Exec(`
						INSERT INTO build_events (build_id, type, payload)
						VALUES ($1, $2, $3)
					`, id, "version", "0.0")
					if err != nil {
						return err
					}

					_, err = tx.Exec(`
							INSERT INTO build_events (build_id, type, payload)
							VALUES ($1, $2, $3)
						`, id, "log", buildLog.String)
					if err != nil {
						return err
					}
				}

				break
			}

			if entry.Type != "" && entry.EventPayload != nil {
				_, err = tx.Exec(`
						INSERT INTO build_events (build_id, type, payload)
						VALUES ($1, $2, $3)
					`, id, entry.Type, []byte(*entry.EventPayload))
				if err != nil {
					return err
				}

				continue
			}

			if entry.Version != "" {
				versionEnc, err := json.Marshal(entry.Version)
				if err != nil {
					return err
				}

				_, err = tx.Exec(`
					INSERT INTO build_events (build_id, type, payload)
					VALUES ($1, $2, $3)
				`, id, "version", versionEnc)
				if err != nil {
					return err
				}

				continue
			}

			return fmt.Errorf("malformed event stream; got stuck at %s", logBuf.String())
		}
	}

	_, err = tx.Exec(`
		ALTER TABLE builds
		DROP COLUMN log
	`)
	if err != nil {
		return err
	}

	return nil
}

type logEntry struct {
	// either an event...
	Type         string           `json:"type"`
	EventPayload *json.RawMessage `json:"event"`

	// ...or a version
	Version string `json:"version"`
}
<commit_msg>correct build events migration cursor use<commit_after>package migrations

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"io"

	"github.com/BurntSushi/migration"
)

func AddBuildEvents(tx migration.LimitedTx) error {
	_, err := tx.Exec(`
    CREATE TABLE build_events (
      id serial PRIMARY KEY,
      build_id integer REFERENCES builds (id),
			type varchar(32) NOT NULL,
      payload text NOT NULL
    )
  `)
	if err != nil {
		return err
	}

	cursor := 0

	for {
		var id int
		var buildLog sql.NullString

		err := tx.QueryRow(`
			SELECT id, log
			FROM builds
			WHERE id > $1
			ORDER BY id ASC
			LIMIT 1
		`, cursor).Scan(&id, &buildLog)
		if err != nil {
			if err == sql.ErrNoRows {
				break
			}

			return err
		}

		cursor = id

		if !buildLog.Valid {
			continue
		}

		logBuf := bytes.NewBufferString(buildLog.String)
		decoder := json.NewDecoder(logBuf)

		for {
			var entry logEntry

			err := decoder.Decode(&entry)
			if err != nil {
				if err != io.EOF {
					// non-JSON log; assume v0.0

					_, err = tx.Exec(`
						INSERT INTO build_events (build_id, type, payload)
						VALUES ($1, $2, $3)
					`, id, "version", "0.0")
					if err != nil {
						return err
					}

					_, err = tx.Exec(`
							INSERT INTO build_events (build_id, type, payload)
							VALUES ($1, $2, $3)
						`, id, "log", buildLog.String)
					if err != nil {
						return err
					}
				}

				break
			}

			if entry.Type != "" && entry.EventPayload != nil {
				_, err = tx.Exec(`
						INSERT INTO build_events (build_id, type, payload)
						VALUES ($1, $2, $3)
					`, id, entry.Type, []byte(*entry.EventPayload))
				if err != nil {
					return err
				}

				continue
			}

			if entry.Version != "" {
				versionEnc, err := json.Marshal(entry.Version)
				if err != nil {
					return err
				}

				_, err = tx.Exec(`
					INSERT INTO build_events (build_id, type, payload)
					VALUES ($1, $2, $3)
				`, id, "version", versionEnc)
				if err != nil {
					return err
				}

				continue
			}

			return fmt.Errorf("malformed event stream; got stuck at %s", logBuf.String())
		}
	}

	_, err = tx.Exec(`
		ALTER TABLE builds
		DROP COLUMN log
	`)
	if err != nil {
		return err
	}

	return nil
}

type logEntry struct {
	// either an event...
	Type         string           `json:"type"`
	EventPayload *json.RawMessage `json:"event"`

	// ...or a version
	Version string `json:"version"`
}
<|endoftext|>