Define dependencies for web. stack concretely.

This commit destroys the use of AppState, which makes passing
concrete state along to various serving components onerous.
This commit is contained in:
Matt T. Proud 2013-05-05 19:32:04 +02:00
parent cfc3b1053d
commit 3b9b1c6ab4
13 changed files with 143 additions and 140 deletions

8
.gitignore vendored
View file

@ -21,5 +21,11 @@ _cgo_*
core core
*-stamp *-stamp
prometheus.build
prometheus prometheus
.#*
command-line-arguments.test
*BACKUP*
*BASE*
*LOCAL*
*REMOTE*

View file

@ -11,36 +11,19 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
.SUFFIXES: TEST_ARTIFACTS = prometheus search_index
TEST_ARTIFACTS = prometheus prometheus.build search_index
include Makefile.INCLUDE include Makefile.INCLUDE
REV := $(shell git rev-parse --short HEAD)
BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
HOSTNAME := $(shell hostname -f)
BUILD_DATE := $(shell date +%Y%m%d-%H:%M:%S)
BUILDFLAGS := -ldflags \
" -X main.buildVersion $(REV)\
-X main.buildBranch $(BRANCH)\
-X main.buildUser $(USER)@$(HOSTNAME)\
-X main.buildDate $(BUILD_DATE)\
-X main.goVersion $(GO_VERSION)\
-X main.leveldbVersion $(LEVELDB_VERSION)\
-X main.protobufVersion $(PROTOCOL_BUFFERS_VERSION)\
-X main.snappyVersion $(SNAPPY_VERSION)"
all: test all: test
advice: advice:
go tool vet . go tool vet .
binary: build binary: build
go build $(BUILDFLAGS) -o prometheus.build go build $(BUILDFLAGS) .
build: preparation config model web build: preparation config model web
go build $(BUILDFLAGS) .
clean: clean:
$(MAKE) -C build clean $(MAKE) -C build clean
@ -72,11 +55,14 @@ preparation: source_path
$(MAKE) -C build $(MAKE) -C build
run: binary run: binary
./prometheus.build $(ARGUMENTS) ./prometheus $(ARGUMENTS)
search_index: search_index:
godoc -index -write_index -index_files='search_index' godoc -index -write_index -index_files='search_index'
server: config model preparation
$(MAKE) -C server
# source_path is responsible for ensuring that the builder has not done anything # source_path is responsible for ensuring that the builder has not done anything
# stupid like working on Prometheus outside of ${GOPATH}. # stupid like working on Prometheus outside of ${GOPATH}.
source_path: source_path:
@ -84,15 +70,7 @@ source_path:
[ -d "$(FULL_GOPATH)" ] [ -d "$(FULL_GOPATH)" ]
test: build test: build
go test ./appstate/... $(GO_TEST_FLAGS) go test ./... $(GO_TEST_FLAGS)
go test ./coding/... $(GO_TEST_FLAGS)
go test ./config/... $(GO_TEST_FLAGS)
go test ./model/... $(GO_TEST_FLAGS)
go test ./retrieval/... $(GO_TEST_FLAGS)
go test ./rules/... $(GO_TEST_FLAGS)
go test ./storage/... $(GO_TEST_FLAGS)
go test ./utility/... $(GO_TEST_FLAGS)
go test ./web/... $(GO_TEST_FLAGS)
web: preparation config model web: preparation config model
$(MAKE) -C web $(MAKE) -C web

View file

@ -1,3 +1,5 @@
# -*- Mode: makefile -*-
# Copyright 2013 Prometheus Team # Copyright 2013 Prometheus Team
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -52,3 +54,17 @@ BREW_INSTALL := brew install
# By default, wget sets the creation time to match the server's, which throws # By default, wget sets the creation time to match the server's, which throws
# off Make. :-( # off Make. :-(
WGET := wget --no-use-server-timestamps -c WGET := wget --no-use-server-timestamps -c
REV := $(shell git rev-parse --short HEAD)
BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
HOSTNAME := $(shell hostname -f)
BUILD_DATE := $(shell date +%Y%m%d-%H:%M:%S)
BUILDFLAGS := -ldflags \
" -X main.buildVersion $(REV)\
-X main.buildBranch $(BRANCH)\
-X main.buildUser $(USER)@$(HOSTNAME)\
-X main.buildDate $(BUILD_DATE)\
-X main.goVersion $(GO_VERSION)\
-X main.leveldbVersion $(LEVELDB_VERSION)\
-X main.protobufVersion $(PROTOCOL_BUFFERS_VERSION)\
-X main.snappyVersion $(SNAPPY_VERSION)"

View file

@ -1,33 +0,0 @@
// Copyright 2013 Prometheus Team
// 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 appstate
import (
"github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/retrieval"
"github.com/prometheus/prometheus/rules"
"github.com/prometheus/prometheus/storage/metric"
)
// ApplicationState is an encapsulation of all relevant Prometheus application
// runtime state. It enables simpler passing of this state to components that
// require it.
type ApplicationState struct {
Config config.Config
RuleManager rules.RuleManager
Storage metric.TieredStorage
TargetManager retrieval.TargetManager
BuildInfo map[string]string
CurationState chan metric.CurationState
}

65
main.go
View file

@ -15,7 +15,6 @@ package main
import ( import (
"flag" "flag"
"github.com/prometheus/prometheus/appstate"
"github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/retrieval" "github.com/prometheus/prometheus/retrieval"
"github.com/prometheus/prometheus/retrieval/format" "github.com/prometheus/prometheus/retrieval/format"
@ -23,6 +22,7 @@ import (
"github.com/prometheus/prometheus/rules/ast" "github.com/prometheus/prometheus/rules/ast"
"github.com/prometheus/prometheus/storage/metric" "github.com/prometheus/prometheus/storage/metric"
"github.com/prometheus/prometheus/web" "github.com/prometheus/prometheus/web"
"github.com/prometheus/prometheus/web/api"
"log" "log"
"os" "os"
"os/signal" "os/signal"
@ -42,10 +42,10 @@ var (
) )
type prometheus struct { type prometheus struct {
storage metric.TieredStorage curationState chan metric.CurationState
// TODO: Refactor channels to work with arrays of results for better chunking.
scrapeResults chan format.Result
ruleResults chan *rules.Result ruleResults chan *rules.Result
storage metric.TieredStorage
scrapeResults chan format.Result
} }
func (p prometheus) interruptHandler() { func (p prometheus) interruptHandler() {
@ -60,9 +60,8 @@ func (p prometheus) interruptHandler() {
} }
func (p prometheus) close() { func (p prometheus) close() {
close(p.curationState)
p.storage.Close() p.storage.Close()
close(p.scrapeResults)
close(p.ruleResults)
} }
func main() { func main() {
@ -92,22 +91,46 @@ func main() {
scrapeResults := make(chan format.Result, *scrapeResultsQueueCapacity) scrapeResults := make(chan format.Result, *scrapeResultsQueueCapacity)
ruleResults := make(chan *rules.Result, *ruleResultsQueueCapacity) ruleResults := make(chan *rules.Result, *ruleResultsQueueCapacity)
curationState := make(chan metric.CurationState, 1)
// Queue depth will need to be exposed
targetManager := retrieval.NewTargetManager(scrapeResults, *concurrentRetrievalAllowance)
targetManager.AddTargetsFromConfig(conf)
statusHandler := &web.StatusHandler{
BuildInfo: BuildInfo,
Config: &conf,
CurationState: curationState,
// Furnish the default status.
PrometheusStatus: &web.PrometheusStatus{},
TargetManager: targetManager,
}
// The closing of curationState implicitly closes this routine.
go statusHandler.ServeRequestsForever()
metricsService := &api.MetricsService{
Config: &conf,
TargetManager: targetManager,
Storage: ts,
}
webService := &web.WebService{
StatusHandler: statusHandler,
MetricsHandler: metricsService,
}
prometheus := prometheus{ prometheus := prometheus{
storage: *ts, curationState: curationState,
scrapeResults: scrapeResults,
ruleResults: ruleResults, ruleResults: ruleResults,
scrapeResults: scrapeResults,
storage: *ts,
} }
defer prometheus.close() defer prometheus.close()
go ts.Serve() go ts.Serve()
go prometheus.interruptHandler() go prometheus.interruptHandler()
// Queue depth will need to be exposed
targetManager := retrieval.NewTargetManager(scrapeResults, *concurrentRetrievalAllowance)
targetManager.AddTargetsFromConfig(conf)
ast.SetStorage(*ts) ast.SetStorage(*ts)
ruleManager := rules.NewRuleManager(ruleResults, conf.EvaluationInterval()) ruleManager := rules.NewRuleManager(ruleResults, conf.EvaluationInterval())
@ -116,16 +139,12 @@ func main() {
log.Fatalf("Error loading rule files: %v", err) log.Fatalf("Error loading rule files: %v", err)
} }
appState := &appstate.ApplicationState{ go func() {
BuildInfo: BuildInfo, err := webService.ServeForever()
Config: conf, if err != nil {
CurationState: make(chan metric.CurationState), log.Fatal(err)
RuleManager: ruleManager, }
Storage: *ts, }()
TargetManager: targetManager,
}
web.StartServing(appState)
// TODO(all): Migrate this into prometheus.serve(). // TODO(all): Migrate this into prometheus.serve().
for { for {

View file

@ -29,4 +29,4 @@ clean:
rm -rf generated/* rm -rf generated/*
-rm -f $(MAKE_ARTIFACTS) -rm -f $(MAKE_ARTIFACTS)
.PHONY: generated .PHONY: clean generated

View file

@ -11,13 +11,15 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
all: blob all: blob-stamp
blob: static/generated/protocol_buffer.descriptor blob-stamp: static/generated/protocol_buffer.descriptor
$(MAKE) -C blob $(MAKE) -C blob
touch $@
clean: clean:
$(MAKE) -C blob clean $(MAKE) -C blob clean
-rm -f *-stamp
static/generated: static/generated:
mkdir -vp static/generated mkdir -vp static/generated
@ -25,4 +27,4 @@ static/generated:
static/generated/protocol_buffer.descriptor: static/generated ../model/generated/descriptor.blob static/generated/protocol_buffer.descriptor: static/generated ../model/generated/descriptor.blob
cp -f ../model/generated/descriptor.blob $@ cp -f ../model/generated/descriptor.blob $@
.PHONY: blob clean .PHONY: clean

View file

@ -15,7 +15,9 @@ package api
import ( import (
"code.google.com/p/gorest" "code.google.com/p/gorest"
"github.com/prometheus/prometheus/appstate" "github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/retrieval"
"github.com/prometheus/prometheus/storage/metric"
"github.com/prometheus/prometheus/utility" "github.com/prometheus/prometheus/utility"
) )
@ -27,13 +29,9 @@ type MetricsService struct {
metrics gorest.EndPoint `method:"GET" path:"/metrics" output:"string"` metrics gorest.EndPoint `method:"GET" path:"/metrics" output:"string"`
setTargets gorest.EndPoint `method:"PUT" path:"/jobs/{jobName:string}/targets" postdata:"[]TargetGroup"` setTargets gorest.EndPoint `method:"PUT" path:"/jobs/{jobName:string}/targets" postdata:"[]TargetGroup"`
time utility.Time
appState *appstate.ApplicationState Config *config.Config
time utility.Time TargetManager retrieval.TargetManager
} Storage *metric.TieredStorage
func NewMetricsService(appState *appstate.ApplicationState) *MetricsService {
return &MetricsService{
appState: appState,
}
} }

View file

@ -96,7 +96,7 @@ func (serv MetricsService) QueryRange(expr string, end int64, duration int64, st
} }
func (serv MetricsService) Metrics() string { func (serv MetricsService) Metrics() string {
metricNames, err := serv.appState.Storage.GetAllValuesForLabel(model.MetricNameLabel) metricNames, err := serv.Storage.GetAllValuesForLabel(model.MetricNameLabel)
rb := serv.ResponseBuilder() rb := serv.ResponseBuilder()
serv.setAccessControlHeaders(rb) serv.setAccessControlHeaders(rb)
rb.SetContentType(gorest.Application_Json) rb.SetContentType(gorest.Application_Json)

View file

@ -26,25 +26,31 @@ type TargetGroup struct {
} }
func (serv MetricsService) SetTargets(targetGroups []TargetGroup, jobName string) { func (serv MetricsService) SetTargets(targetGroups []TargetGroup, jobName string) {
if job := serv.appState.Config.GetJobByName(jobName); job == nil { job := serv.Config.GetJobByName(jobName)
if job == nil {
rb := serv.ResponseBuilder() rb := serv.ResponseBuilder()
rb.SetResponseCode(http.StatusNotFound) rb.SetResponseCode(http.StatusNotFound)
} else { return
newTargets := []retrieval.Target{}
for _, targetGroup := range targetGroups {
// Do mandatory map type conversion due to Go shortcomings.
baseLabels := model.LabelSet{
model.JobLabel: model.LabelValue(job.GetName()),
}
for label, value := range targetGroup.BaseLabels {
baseLabels[model.LabelName(label)] = model.LabelValue(value)
}
for _, endpoint := range targetGroup.Endpoints {
newTarget := retrieval.NewTarget(endpoint, time.Second*5, baseLabels)
newTargets = append(newTargets, newTarget)
}
}
serv.appState.TargetManager.ReplaceTargets(*job, newTargets, serv.appState.Config.ScrapeInterval())
} }
newTargets := []retrieval.Target{}
for _, targetGroup := range targetGroups {
// Do mandatory map type conversion due to Go shortcomings.
baseLabels := model.LabelSet{
model.JobLabel: model.LabelValue(job.GetName()),
}
for label, value := range targetGroup.BaseLabels {
baseLabels[model.LabelName(label)] = model.LabelValue(value)
}
for _, endpoint := range targetGroup.Endpoints {
newTarget := retrieval.NewTarget(endpoint, time.Second*5, baseLabels)
newTargets = append(newTargets, newTarget)
}
}
// BUG(julius): Validate that this ScrapeInterval is in fact the proper one
// for the job.
serv.TargetManager.ReplaceTargets(*job, newTargets, serv.Config.ScrapeInterval())
} }

3
web/blob/.gitignore vendored
View file

@ -1 +1,2 @@
files.go files.go
protocol_buffer.descriptor

View file

@ -15,27 +15,32 @@ package web
import ( import (
"flag" "flag"
"github.com/prometheus/prometheus/appstate" "github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/retrieval" "github.com/prometheus/prometheus/retrieval"
"github.com/prometheus/prometheus/storage/metric" "github.com/prometheus/prometheus/storage/metric"
"net/http" "net/http"
"sync"
) )
type PrometheusStatus struct { type PrometheusStatus struct {
BuildInfo map[string]string
Config string Config string
Curation metric.CurationState
Flags map[string]string
Rules string Rules string
TargetPools map[string]*retrieval.TargetPool TargetPools map[string]*retrieval.TargetPool
BuildInfo map[string]string
Flags map[string]string
Curation metric.CurationState
} }
type StatusHandler struct { type StatusHandler struct {
appState *appstate.ApplicationState BuildInfo map[string]string
Config *config.Config
CurationState chan metric.CurationState
PrometheusStatus *PrometheusStatus PrometheusStatus *PrometheusStatus
TargetManager retrieval.TargetManager
mutex sync.Mutex
} }
func (h *StatusHandler) Run() { func (h *StatusHandler) ServeRequestsForever() {
flags := map[string]string{} flags := map[string]string{}
flag.VisitAll(func(f *flag.Flag) { flag.VisitAll(func(f *flag.Flag) {
@ -43,19 +48,22 @@ func (h *StatusHandler) Run() {
}) })
h.PrometheusStatus = &PrometheusStatus{ h.PrometheusStatus = &PrometheusStatus{
Config: h.appState.Config.String(), BuildInfo: h.BuildInfo,
Rules: "TODO: list rules here", Config: h.Config.String(),
TargetPools: h.appState.TargetManager.Pools(),
BuildInfo: h.appState.BuildInfo,
Flags: flags, Flags: flags,
Rules: "TODO: list rules here",
TargetPools: h.TargetManager.Pools(),
} }
// Law of Demeter :-( for state := range h.CurationState {
for state := range h.appState.CurationState { h.Lock()
h.PrometheusStatus.Curation = state h.PrometheusStatus.Curation = state
h.Unlock()
} }
} }
func (h StatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *StatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.Lock()
defer h.Unlock()
executeTemplate(w, "status", h.PrometheusStatus) executeTemplate(w, "status", h.PrometheusStatus)
} }

View file

@ -19,7 +19,6 @@ import (
"fmt" "fmt"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/exp" "github.com/prometheus/client_golang/prometheus/exp"
"github.com/prometheus/prometheus/appstate"
"github.com/prometheus/prometheus/web/api" "github.com/prometheus/prometheus/web/api"
"github.com/prometheus/prometheus/web/blob" "github.com/prometheus/prometheus/web/blob"
"html/template" "html/template"
@ -34,8 +33,13 @@ var (
useLocalAssets = flag.Bool("useLocalAssets", false, "Read assets/templates from file instead of binary.") useLocalAssets = flag.Bool("useLocalAssets", false, "Read assets/templates from file instead of binary.")
) )
func StartServing(appState *appstate.ApplicationState) { type WebService struct {
gorest.RegisterService(api.NewMetricsService(appState)) StatusHandler *StatusHandler
MetricsHandler *api.MetricsService
}
func (w WebService) ServeForever() error {
gorest.RegisterService(w.MetricsHandler)
// TODO(julius): This will need to be rewritten once the exp package provides // TODO(julius): This will need to be rewritten once the exp package provides
// the coarse mux behaviors via a wrapper function. // the coarse mux behaviors via a wrapper function.
@ -44,10 +48,7 @@ func StartServing(appState *appstate.ApplicationState) {
exp.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile)) exp.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
exp.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol)) exp.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
statusHandler := &StatusHandler{appState: appState} exp.Handle("/", w.StatusHandler)
go statusHandler.Run()
exp.Handle("/", statusHandler)
exp.HandleFunc("/graph", graphHandler) exp.HandleFunc("/graph", graphHandler)
exp.Handle("/api/", gorest.Handle()) exp.Handle("/api/", gorest.Handle())
@ -59,7 +60,8 @@ func StartServing(appState *appstate.ApplicationState) {
} }
log.Printf("listening on %s", *listenAddress) log.Printf("listening on %s", *listenAddress)
go http.ListenAndServe(*listenAddress, exp.DefaultCoarseMux)
return http.ListenAndServe(*listenAddress, exp.DefaultCoarseMux)
} }
func getTemplate(name string) (t *template.Template, err error) { func getTemplate(name string) (t *template.Template, err error) {