Esempio n. 1
0
func TestTierInitialisation(t *testing.T) {
	// Setup tiers
	tierConfig := make(map[string]coco.TierConfig)
	tierConfig["a"] = coco.TierConfig{Targets: []string{"127.0.0.1:29000", "a.example:29000", "b.example:29000", "c.example:29000"}}

	var tiers []coco.Tier
	for k, v := range tierConfig {
		tier := coco.Tier{Name: k, Targets: v.Targets}
		tiers = append(tiers, tier)
	}
	coco.BuildTiers(&tiers)

	// Test
	for _, tier := range tiers {
		expected := len(tier.Targets)
		actual := len(tier.Hash.Members())
		if actual != expected {
			t.Errorf("Expected %d hash members, got %d\n", expected, actual)
			t.Logf("Connections: %+v\n", tier.Connections)
			t.Logf("Mappings: %+v\n", tier.Mappings)
			t.Logf("Shadows: %+v\n", tier.Shadows)
			t.Logf("Hash.Members(): %+v\n", tier.Hash.Members())
		}
	}
}
Esempio n. 2
0
func Fetch(config coco.FetchConfig, tiers *[]coco.Tier) {
	// Initialise the error counts
	errorCounts.Add("fetch.con.get", 0)
	errorCounts.Add("fetch.http.get", 0)
	errorCounts.Add("fetch.ioutil.readall", 0)

	if len(config.Bind) == 0 {
		log.Fatal("[fatal] Fetch: No address configured to bind web server.")
	}

	coco.BuildTiers(tiers)

	m := martini.Classic()
	m.Get("/data/:hostname/(.+)", func(params martini.Params, req *http.Request) []byte {
		for _, tier := range *tiers {
			// Lookup the hostname in the tier's hash. Work out where we should proxy to.
			target, err := tier.Lookup(params["hostname"])
			if err != nil {
				log.Printf("[info] Fetch: couldn't lookup target: %s\n", err)
				defer func() { errorCounts.Add("fetch.con.get", 1) }()
				return errorJSON(err)
			}

			// Construct the URL, and do the GET
			var host string
			if len(config.RemotePort) > 0 {
				// FIXME(lindsay) look up fetch port per-target?
				host = strings.Split(target, ":")[0] + ":" + config.RemotePort
			} else {
				host = strings.Split(target, ":")[0]
			}
			url := "http://" + host + req.RequestURI
			client := &http.Client{Timeout: config.Timeout()}
			resp, err := client.Get(url)
			defer resp.Body.Close()
			if err != nil {
				log.Printf("[info] Fetch: couldn't perform GET to target: %s\n", err)
				defer func() { errorCounts.Add("fetch.http.get", 1) }()
				return errorJSON(err)
			}

			// TODO(lindsay): count successful requests to each tier
			// TODO(lindsay): count failed requests to each tier

			// Read the body, check for any errors
			body, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				log.Printf("[info] Fetch: couldn't read response from target: %s\n", err)
				defer func() { errorCounts.Add("fetch.ioutil.readall", 1) }()
				return errorJSON(err)
			}

			// Stuff in metadata about the proxied request
			meta := map[string]string{
				"host":   host,
				"target": target,
				"url":    url,
			}
			var data map[string]interface{}
			err = json.Unmarshal(body, &data)
			if err != nil {
				log.Printf("[info] Fetch: couldn't unmarshal JSON from target: %s\n", err)
				defer func() { errorCounts.Add("fetch.json.unmarshal", 1) }()
				return errorJSON(err)
			}
			data["_meta"] = meta
			bm, err := json.Marshal(data)
			if err != nil {
				log.Printf("[info] Fetch: couldn't re-marshal target JSON for client: %s\n", err)
				defer func() { errorCounts.Add("fetch.json.marshal", 1) }()
				return errorJSON(err)
			}

			// Track metrics for a successful proxy request
			defer func() {
				reqCounts.Add(target, 1) // the target in the hash we proxied to
				reqCounts.Add("total", 1)
				respCounts.Add(strconv.Itoa(resp.StatusCode), 1)
				bytesProxied.Add(resp.ContentLength)
				tierCounts.Add(tier.Name, 1)
			}()

			// return the body with metadata
			return bm
		}

		// TODO(lindsay): Provide a fallback response if there is no data available
		// return the body
		return []byte("oops")
	})
	// Implement expvars.expvarHandler in Martini.
	m.Get("/debug/vars", func(w http.ResponseWriter, r *http.Request) {
		coco.ExpvarHandler(w, r)
	})
	m.Get("/lookup", func(params martini.Params, req *http.Request) []byte {
		return coco.TierLookup(params, req, tiers)
	})

	log.Printf("[info] Fetch: binding web server to %s", config.Bind)
	log.Fatalf("[fatal] Fetch: HTTP handler crashed: %s", http.ListenAndServe(config.Bind, m))
}