Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion charts/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ apiVersion: v2
name: nais-api
description: The all mighty Nais API
type: application
version: v0.1.0
version: v0.2.0
sources:
- https://github.com/nais/api/tree/main/charts
4 changes: 4 additions & 0 deletions charts/templates/networkpolicy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ spec:
podSelector:
matchLabels:
k8s-app: kube-dns
- to:
- podSelector:
matchLabels:
app.kubernetes.io/name: mimir
- to:
- podSelector:
matchLabels:
Expand Down
97 changes: 40 additions & 57 deletions internal/thirdparty/promclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package promclient
import (
"context"
"fmt"
"net/http"
"strings"
"time"

Expand All @@ -16,7 +17,7 @@ import (

type QueryClient interface {
Query(ctx context.Context, environment string, query string, opts ...QueryOption) (prom.Vector, error)
QueryAll(ctx context.Context, query string, opts ...QueryOption) (map[string]prom.Vector, error)
QueryAll(ctx context.Context, query string, opts ...QueryOption) (prom.Vector, error)
QueryRange(ctx context.Context, environment string, query string, promRange promv1.Range) (prom.Value, promv1.Warnings, error)
}

Expand All @@ -43,64 +44,44 @@ func WithTime(t time.Time) QueryOption {
}

type RealClient struct {
prometheuses map[string]promv1.API
mimirMetrics promv1.API
mimirRules promv1.API
log logrus.FieldLogger
}

func New(clusters []string, tenant string, log logrus.FieldLogger) (*RealClient, error) {
proms := map[string]promv1.API{}

for _, cluster := range clusters {
client, err := api.NewClient(api.Config{Address: fmt.Sprintf("https://prometheus.%s.%s.cloud.nais.io", cluster, tenant)})
if err != nil {
return nil, err
}

proms[cluster] = promv1.NewAPI(client)
}

return &RealClient{
prometheuses: proms,
log: log,
}, nil
type mimirRoundTrip struct {
HeaderValue string
}

func (c *RealClient) QueryAll(ctx context.Context, query string, opts ...QueryOption) (map[string]prom.Vector, error) {
type result struct {
env string
vec prom.Vector
}
wg := pool.NewWithResults[*result]().WithContext(ctx)
func (r mimirRoundTrip) RoundTrip(req *http.Request) (*http.Response, error) {
req.Header.Set("X-Scope-OrgID", r.HeaderValue)
return http.DefaultTransport.RoundTrip(req)
}

for env := range c.prometheuses {
wg.Go(func(ctx context.Context) (*result, error) {
v, err := c.Query(ctx, env, query, opts...)
if err != nil {
c.log.WithError(err).Errorf("failed to query prometheus in %s", env)
return nil, err
}
return &result{env: env, vec: v}, nil
})
func New(clusters []string, tenant string, log logrus.FieldLogger) (*RealClient, error) {
mimirMetrics, err := api.NewClient(api.Config{Address: "http://mimir-query-frontend:8080/prometheus", RoundTripper: mimirRoundTrip{HeaderValue: "nais"}})
if err != nil {
return nil, err
}

results, err := wg.Wait()
mimirAlerts, err := api.NewClient(api.Config{Address: "http://mimir-ruler:8080/prometheus", RoundTripper: mimirRoundTrip{HeaderValue: "tenant"}})
if err != nil {
return nil, err
}

ret := map[string]prom.Vector{}
for _, res := range results {
ret[res.env] = res.vec
}
return &RealClient{
mimirMetrics: promv1.NewAPI(mimirMetrics),
mimirRules: promv1.NewAPI(mimirAlerts),
log: log,
}, nil
}

return ret, nil
func (c *RealClient) QueryAll(ctx context.Context, query string, opts ...QueryOption) (prom.Vector, error) {
return c.Query(ctx, "query-all", query, opts...)
}

func (c *RealClient) Query(ctx context.Context, environmentName string, query string, opts ...QueryOption) (prom.Vector, error) {
client, ok := c.prometheuses[environmentName]
if !ok {
return nil, fmt.Errorf("no prometheus client for environment %s", environmentName)
}
client := c.mimirMetrics

opt := &QueryOpts{
Time: time.Now().Add(-5 * time.Minute),
Expand Down Expand Up @@ -130,19 +111,13 @@ func (c *RealClient) Query(ctx context.Context, environmentName string, query st
}

func (c *RealClient) QueryRange(ctx context.Context, environment string, query string, promRange promv1.Range) (prom.Value, promv1.Warnings, error) {
client, ok := c.prometheuses[environment]
if !ok {
return nil, nil, fmt.Errorf("no prometheus client for environment %s", environment)
}

client := c.mimirMetrics
return client.QueryRange(ctx, query, promRange)
}

func (c *RealClient) Rules(ctx context.Context, environment string, teamSlug slug.Slug) (promv1.RulesResult, error) {
api, ok := c.prometheuses[environment]
if !ok {
return promv1.RulesResult{}, fmt.Errorf("no prometheus client for environment %s", environment)
}
api := c.mimirRules

res, err := api.Rules(ctx)
if err != nil {
return promv1.RulesResult{}, err
Expand All @@ -160,7 +135,7 @@ func (c *RealClient) RulesAll(ctx context.Context, teamSlug slug.Slug) (map[stri
}
wg := pool.NewWithResults[*item]().WithContext(ctx)

for env := range c.prometheuses {
for _, env := range []string{"dev"} {
wg.Go(func(ctx context.Context) (*item, error) {
res, err := c.Rules(ctx, env, teamSlug)
if err != nil {
Expand All @@ -186,15 +161,22 @@ func filterRulesByTeam(in promv1.RulesResult, teamSlug slug.Slug) promv1.RulesRe
out.Groups = make([]promv1.RuleGroup, 0, len(in.Groups))

for _, g := range in.Groups {
splittedFile := strings.Split(g.File, "/")
if len(splittedFile) < 2 {
continue
}

namespace := splittedFile[1]

var filtered promv1.Rules
for _, r := range g.Rules {
if ar, ok := r.(promv1.AlertingRule); ok {
if string(ar.Labels["namespace"]) == teamSlug.String() ||
string(ar.Labels["team"]) == teamSlug.String() {
if namespace == teamSlug.String() {
for _, r := range g.Rules {
if ar, ok := r.(promv1.AlertingRule); ok {
filtered = append(filtered, ar)
}
}
}

if len(filtered) > 0 {
out.Groups = append(out.Groups, promv1.RuleGroup{
Name: g.Name,
Expand All @@ -204,5 +186,6 @@ func filterRulesByTeam(in promv1.RulesResult, teamSlug slug.Slug) promv1.RulesRe
})
}
}

return out
}
31 changes: 18 additions & 13 deletions internal/thirdparty/promclient/fake/fake.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,18 @@ func NewFakeClient(environments []string, random *rand.Rand, nowFunc func() prom
}
}

func (c *FakeClient) QueryAll(ctx context.Context, query string, opts ...promclient.QueryOption) (map[string]prom.Vector, error) {
ret := map[string]prom.Vector{}
func (c *FakeClient) QueryAll(ctx context.Context, query string, opts ...promclient.QueryOption) (prom.Vector, error) {
var vectors prom.Vector
for _, env := range c.environments {
v, err := c.Query(ctx, env, query, opts...)
vec, err := c.Query(ctx, env, query, opts...)
if err != nil {
return nil, err
return vec, err
}
ret[env] = v

vectors = append(vec, vectors...)
}
return ret, nil

return vectors, nil
}

func (c *FakeClient) Query(ctx context.Context, environment string, query string, opts ...promclient.QueryOption) (prom.Vector, error) {
Expand Down Expand Up @@ -86,6 +88,7 @@ func (c *FakeClient) Query(ctx context.Context, environment string, query string
for _, matcher := range expr.LabelMatchers {
labelsToCreate = append(labelsToCreate, matcher.Name)
}
labelsToCreate = append(labelsToCreate, "k8s_cluster_name")
teamSlug, workload, unit, err = c.selector(expr)

case *parser.Call:
Expand All @@ -103,7 +106,7 @@ func (c *FakeClient) Query(ctx context.Context, environment string, query string
for _, matcher := range vectorSelector.LabelMatchers {
labelsToCreate = append(labelsToCreate, matcher.Name)
}
labelsToCreate = append(labelsToCreate, "pod")
labelsToCreate = append(labelsToCreate, []string{"pod", "k8s_cluster_name"}...)
teamSlug, workload, unit, err = c.selector(expr)

default:
Expand All @@ -114,20 +117,22 @@ func (c *FakeClient) Query(ctx context.Context, environment string, query string
}

makeLabels := func() prom.Metric {
lbls := prom.Metric{}
labels := prom.Metric{}
for _, label := range labelsToCreate {
switch label {
case "namespace":
lbls["namespace"] = prom.LabelValue(teamSlug)
labels["namespace"] = prom.LabelValue(teamSlug)
case "workload_namespace":
lbls["workload_namespace"] = prom.LabelValue(teamSlug)
labels["workload_namespace"] = prom.LabelValue(teamSlug)
case "container":
lbls["container"] = prom.LabelValue(workload)
labels["container"] = prom.LabelValue(workload)
case "pod":
lbls["pod"] = prom.LabelValue(fmt.Sprintf("%s-%s", workload, "1"))
labels["pod"] = prom.LabelValue(fmt.Sprintf("%s-%s", workload, "1"))
}
}
return lbls
// TODO: Støtte de spørringene som har et miljø satt, og QueryAll som svarer med flere miljøer
labels["k8s_cluster_name"] = prom.LabelValue(environment)
return labels
}

value := func() prom.SampleValue {
Expand Down
Loading