Skip to content

Commit

Permalink
Rules API: Add e2e test (#198)
Browse files Browse the repository at this point in the history
* Start e2e test

Signed-off-by: Jéssica Lins <jessicaalins@gmail.com>

* Fix flag, command

Signed-off-by: Jéssica Lins <jessicaalins@gmail.com>

* Fix s3 file

Signed-off-by: Jéssica Lins <jessicaalins@gmail.com>

* Dynamically create yaml file

Signed-off-by: Jéssica Lins <jessicaalins@gmail.com>

* Start to add test

Signed-off-by: Jéssica Lins <jessicaalins@gmail.com>

* Fix endpoint path

Signed-off-by: Jéssica Lins <jessicaalins@gmail.com>

* Create rules_test, leaner yaml

Signed-off-by: Jéssica Lins <jessicaalins@gmail.com>

* Finish first test cases

Signed-off-by: Jéssica Lins <jessicaalins@gmail.com>

* Update test image, add more test cases

Signed-off-by: Jéssica Lins <jessicaalins@gmail.com>

* Code review suggestions, add startServicesForRules function

Signed-off-by: Jéssica Lins <jessicaalins@gmail.com>

* Update flag syntax

Signed-off-by: Jéssica Lins <jessicaalins@gmail.com>

* Format, rewrite test names

Signed-off-by: Jéssica Lins <jessicaalins@gmail.com>

* Check for tenant labels

Signed-off-by: Jéssica Lins <jessicaalins@gmail.com>

* Create defaultTenantName const

Signed-off-by: Jéssica Lins <jessicaalins@gmail.com>

* Rename test cases, remove write invalid rules case for now

Signed-off-by: Jéssica Lins <jessicaalins@gmail.com>

* Remove unused invalidRulesYamlTpl

Signed-off-by: Jéssica Lins <jessicaalins@gmail.com>

* Revert "Remove unused invalidRulesYamlTpl"

This reverts commit ccd9a59.

* Readd invalid rules test

Signed-off-by: Jéssica Lins <jessicaalins@gmail.com>

* Add missing new line

Signed-off-by: Jéssica Lins <jessicaalins@gmail.com>
  • Loading branch information
jessicalins committed Jan 26, 2022
1 parent cff5981 commit e486b89
Show file tree
Hide file tree
Showing 8 changed files with 321 additions and 25 deletions.
83 changes: 79 additions & 4 deletions test/e2e/configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type testType string

const (
metrics testType = "metrics"
rules testType = "rules"
logs testType = "logs"
tenants testType = "tenants"
interactive testType = "interactive"
Expand All @@ -29,13 +30,16 @@ const (
certsContainerPath = dockerLocalSharedDir + "/" + certsSharedDir
configsContainerPath = dockerLocalSharedDir + "/" + configSharedDir

envMetricsName = "e2e_metrics_read_write"
envLogsName = "e2e_logs_read_write_tail"
envTenantsName = "e2e_tenants"
envInteractive = "e2e_interactive"
envMetricsName = "e2e_metrics_read_write"
envRulesAPIName = "e2e_rules_api"
envLogsName = "e2e_logs_read_write_tail"
envTenantsName = "e2e_tenants"
envInteractive = "e2e_interactive"

defaultTenantID = "1610b0c3-c509-4592-a256-a1871353dbfa"
mtlsTenantID = "845cdfd9-f936-443c-979c-2ee7dc91f646"

defaultTenantName = "test-oidc"
)

const tenantsYamlTpl = `
Expand Down Expand Up @@ -166,3 +170,74 @@ func createDexYAML(
)
testutil.Ok(t, err)
}

const rulesYAMLTpl = `
type: S3
config:
bucket: %s
endpoint: %s
access_key: %s
insecure: true
secret_key: %s
`

func createRulesYAML(
t *testing.T,
e e2e.Environment,
bucket, endpoint, accessKey, secretKey string,
) {
yamlContent := []byte(fmt.Sprintf(
rulesYAMLTpl,
bucket,
endpoint,
accessKey,
secretKey,
))

err := ioutil.WriteFile(
filepath.Join(e.SharedDir(), configSharedDir, "rules-objstore.yaml"),
yamlContent,
os.FileMode(0755),
)
testutil.Ok(t, err)
}

const recordingRuleYamlTpl = `
groups:
- name: example
rules:
- record: job:http_inprogress_requests:sum
expr: sum by (job) (http_inprogress_requests)
`

const alertingRuleYamlTpl = `
groups:
- name: example
rules:
- alert: HighRequestLatency
expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5
for: 10m
labels:
severity: page
annotations:
summary: High request latency
`
const recordAndAlertingRulesYamlTpl = `
groups:
- name: node_rules
rules:
- record: job:up:avg
expr: avg without(instance)(up{job="node"})
- alert: ManyInstancesDown
expr: job:up:avg{job="node"} < 0.5
`

const invalidRulesYamlTpl = `
invalid:
- name: testing
invalid_rules:
- rule1: job:up:avg
expr: avg without(instance)(up{job="node"})
- rule2: ManyInstancesDown
expr: job:up:avg{job="node"} < 0.5
`
12 changes: 12 additions & 0 deletions test/e2e/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ func getContainerName(t *testing.T, tt testType, serviceName string) string {
return envLogsName + "-" + serviceName
case metrics:
return envMetricsName + "-" + serviceName
case rules:
return envRulesAPIName + "-" + serviceName
case tenants:
return envTenantsName + "-" + serviceName
case interactive:
Expand Down Expand Up @@ -116,3 +118,13 @@ func assertResponse(t *testing.T, response string, expected string) {
fmt.Sprintf("failed to assert that the response '%s' contains '%s'", response, expected),
)
}

type tokenRoundTripper struct {
rt http.RoundTripper
token string
}

func (rt *tokenRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
r.Header.Add("Authorization", "bearer "+rt.token)
return rt.rt.RoundTrip(r)
}
4 changes: 2 additions & 2 deletions test/e2e/interactive_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ func TestInteractiveSetup(t *testing.T) {

up, err := newUpRun(
e, "up-metrics-read-write", metrics,
"https://"+api.InternalEndpoint("https")+"/api/metrics/v1/test-oidc/api/v1/query",
"https://"+api.InternalEndpoint("https")+"/api/metrics/v1/test-oidc/api/v1/receive",
"https://"+api.InternalEndpoint("https")+"/api/metrics/v1/"+defaultTenantName+"/api/v1/query",
"https://"+api.InternalEndpoint("https")+"/api/metrics/v1/"+defaultTenantName+"/api/v1/receive",
withToken(token),
withRunParameters(&runParams{period: "5000ms", threshold: "1", latency: "10s", duration: "0"}),
)
Expand Down
6 changes: 3 additions & 3 deletions test/e2e/logs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ func TestLogsReadWriteAndTail(t *testing.T) {
t.Run("logs-tail", func(t *testing.T) {
up, err := newUpRun(
e, "up-logs-tail", logs,
"https://"+api.InternalEndpoint("https")+"/api/logs/v1/test-oidc/loki/api/v1/query",
"https://"+api.InternalEndpoint("https")+"/api/logs/v1/test-oidc/loki/api/v1/push",
"https://"+api.InternalEndpoint("https")+"/api/logs/v1/"+defaultTenantName+"/loki/api/v1/query",
"https://"+api.InternalEndpoint("https")+"/api/logs/v1/"+defaultTenantName+"/loki/api/v1/push",
withToken(token),
withRunParameters(&runParams{initialDelay: "0s", period: "250ms", threshold: "1", latency: "10s", duration: "0"}),
)
Expand All @@ -112,7 +112,7 @@ func TestLogsReadWriteAndTail(t *testing.T) {

d := websocket.Dialer{TLSClientConfig: getTLSClientConfig(t, e)}
conn, _, err := d.Dial(
"wss://"+api.Endpoint("https")+"/api/logs/v1/test-oidc/loki/api/v1/tail?query=%7B_id%3D%22test%22%7D",
"wss://"+api.Endpoint("https")+"/api/logs/v1/"+defaultTenantName+"/loki/api/v1/tail?query=%7B_id%3D%22test%22%7D",
http.Header{
"Authorization": []string{"Bearer " + token},
"X-Scope-OrgID": []string{defaultTenantID},
Expand Down
16 changes: 3 additions & 13 deletions test/e2e/metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ func TestMetricsReadAndWrite(t *testing.T) {
t.Run("metrics-read-write", func(t *testing.T) {
up, err := newUpRun(
e, "up-metrics-read-write", metrics,
"https://"+api.InternalEndpoint("https")+"/api/metrics/v1/test-oidc/api/v1/query",
"https://"+api.InternalEndpoint("https")+"/api/metrics/v1/test-oidc/api/v1/receive",
"https://"+api.InternalEndpoint("https")+"/api/metrics/v1/"+defaultTenantName+"/api/v1/query",
"https://"+api.InternalEndpoint("https")+"/api/metrics/v1/"+defaultTenantName+"/api/v1/receive",
withToken(token),
withRunParameters(&runParams{period: "500ms", threshold: "1", latency: "10s", duration: "0"}),
)
Expand Down Expand Up @@ -106,7 +106,7 @@ func TestMetricsReadAndWrite(t *testing.T) {
}

apiTest, err := promapi.NewClient(promapi.Config{
Address: "https://" + api.Endpoint("https") + "/api/metrics/v1/test-oidc",
Address: "https://" + api.Endpoint("https") + "/api/metrics/v1/"+defaultTenantName,
RoundTripper: &tokenRoundTripper{rt: tr, token: token},
})
testutil.Ok(t, err)
Expand Down Expand Up @@ -191,13 +191,3 @@ func TestMetricsReadAndWrite(t *testing.T) {
})
})
}

type tokenRoundTripper struct {
rt http.RoundTripper
token string
}

func (rt *tokenRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
r.Header.Add("Authorization", "bearer "+rt.token)
return rt.rt.RoundTrip(r)
}
173 changes: 173 additions & 0 deletions test/e2e/rules_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// +build integration

package e2e

import (
"bytes"
"io/ioutil"
"net/http"
"testing"

"github.com/efficientgo/e2e"
"github.com/efficientgo/tools/core/pkg/testutil"
)

func TestRulesAPI(t *testing.T) {
t.Parallel()

e, err := e2e.NewDockerEnvironment(envRulesAPIName)
testutil.Ok(t, err)
t.Cleanup(e.Close)

prepareConfigsAndCerts(t, rules, e)
_, token, rateLimiterAddr := startBaseServices(t, e, rules)
rulesEndpoint := startServicesForRules(t, e)

api, err := newObservatoriumAPIService(
e,
withRulesEndpoint("http://"+rulesEndpoint),
withRateLimiter(rateLimiterAddr),
)
testutil.Ok(t, err)
testutil.Ok(t, e2e.StartAndWaitReady(api))

rulesEndpointURL := "https://" + api.Endpoint("https") + "/api/metrics/v1/"+defaultTenantName+"/api/v1/rules/raw"
tr := &http.Transport{
TLSClientConfig: getTLSClientConfig(t, e),
}

client := &http.Client{
Transport: &tokenRoundTripper{rt: tr, token: token},
}

t.Run("write-then-read-recording-rules", func(t *testing.T) {
// Try to list rules
r, err := http.NewRequest(
http.MethodGet,
rulesEndpointURL,
nil,
)
testutil.Ok(t, err)

res, err := client.Do(r)
testutil.Ok(t, err)
testutil.Equals(t, http.StatusNotFound, res.StatusCode)

// Set a file containing a recording rule
recordingRule := []byte(recordingRuleYamlTpl)
r, err = http.NewRequest(
http.MethodPut,
rulesEndpointURL,
bytes.NewReader(recordingRule),
)
testutil.Ok(t, err)

res, err = client.Do(r)
testutil.Ok(t, err)
testutil.Equals(t, http.StatusOK, res.StatusCode)

// Check if recording rule is listed
r, err = http.NewRequest(
http.MethodGet,
rulesEndpointURL,
nil,
)
testutil.Ok(t, err)

res, err = client.Do(r)
defer res.Body.Close()

testutil.Ok(t, err)
testutil.Equals(t, http.StatusOK, res.StatusCode)

body, err := ioutil.ReadAll(res.Body)
bodyStr := string(body)

assertResponse(t, bodyStr, "sum by (job) (http_inprogress_requests)")
assertResponse(t, bodyStr, "tenant_id: "+defaultTenantID)
})

t.Run("write-then-read-alerting-rules", func(t *testing.T) {
// Set a file containing an alerting rule
alertingRule := []byte(alertingRuleYamlTpl)
r, err := http.NewRequest(
http.MethodPut,
rulesEndpointURL,
bytes.NewReader(alertingRule),
)
testutil.Ok(t, err)

res, err := client.Do(r)
testutil.Ok(t, err)
testutil.Equals(t, http.StatusOK, res.StatusCode)

// Check if the alerting rule is listed
r, err = http.NewRequest(
http.MethodGet,
rulesEndpointURL,
nil,
)
testutil.Ok(t, err)

res, err = client.Do(r)
defer res.Body.Close()

testutil.Ok(t, err)
testutil.Equals(t, http.StatusOK, res.StatusCode)

body, err := ioutil.ReadAll(res.Body)
bodyStr := string(body)
assertResponse(t, bodyStr, "alert: HighRequestLatency")
assertResponse(t, bodyStr, "tenant_id: "+defaultTenantID)
})

t.Run("write-then-read-recording-and-alerting-rules", func(t *testing.T) {
// Set a file containing both recording and alerting rules
recordAndAlertingRules := []byte(recordAndAlertingRulesYamlTpl)
r, err := http.NewRequest(
http.MethodPut,
rulesEndpointURL,
bytes.NewReader(recordAndAlertingRules),
)
testutil.Ok(t, err)

res, err := client.Do(r)
testutil.Ok(t, err)
testutil.Equals(t, http.StatusOK, res.StatusCode)

// Check if both recording and alerting rules are listed
r, err = http.NewRequest(
http.MethodGet,
rulesEndpointURL,
nil,
)
testutil.Ok(t, err)

res, err = client.Do(r)
defer res.Body.Close()

testutil.Ok(t, err)
testutil.Equals(t, http.StatusOK, res.StatusCode)

body, err := ioutil.ReadAll(res.Body)
bodyStr := string(body)
assertResponse(t, bodyStr, "record: job:up:avg")
assertResponse(t, bodyStr, "alert: ManyInstancesDown")
assertResponse(t, bodyStr, "tenant_id: "+defaultTenantID)
})
t.Run("write-invalid-rules", func(t *testing.T) {
// Set an invalid rules file
invalidRules := []byte(invalidRulesYamlTpl)
r, err := http.NewRequest(
http.MethodPut,
rulesEndpointURL,
bytes.NewReader(invalidRules),
)
testutil.Ok(t, err)

res, err := client.Do(r)
testutil.Ok(t, err)
testutil.Equals(t, http.StatusBadRequest, res.StatusCode)
})
}

Loading

0 comments on commit e486b89

Please sign in to comment.