Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions .github/workflows/ci-build-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,19 @@ jobs:
path-to-profile: .coverprofile
flag-name: Go-${{ matrix.go }}
parallel: true

integration:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
- run: make developer-start integration-test
finish:
needs:
- build
- integration
runs-on: ubuntu-latest
steps:
- uses: shogo82148/actions-goveralls@v1
Expand Down
11 changes: 8 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,10 @@ GO_TEST_FLAGS ?= -coverprofile=.coverprofile
.PHONY: test
test: check-license-headers check-codegen gotest check-fmtprints check-todos

GO_TEST_PATH ?= $(shell $(GO) list ./... | grep -v v2/integration)
.PHONY: gotest
gotest:
go test -timeout=5m -v ${GO_TEST_FLAGS} ./...
$(GO) test -timeout=5m -v ${GO_TEST_FLAGS} $(GO_TEST_PATH)

.PHONY: data-race-test
data-race-test:
Expand All @@ -134,6 +135,10 @@ data-race-test:
data-race-test-inspect:
./hack/inspect-race-output.sh race-output.log

.PHONY: integration-test
integration-test:
$(MAKE) -C integration test

.PHONY: bench
bench:
bash -c "$(GO) test -v -coverprofile=.coverprofile ./... -run=nonthingplease -bench=. | grep -v ' app=trickster '; exit ${PIPESTATUS[0]}"
Expand All @@ -151,11 +156,11 @@ generate: perform-generate insert-license-headers

.PHONY: perform-generate
perform-generate:
$(GO) generate ./pkg/... ./cmd/...
$(GO) generate ./pkg/... ./cmd/... ./integration/...

.PHONY: insert-license-headers
insert-license-headers:
@for file in $$(find ./pkg ./cmd -name '*.go') ; \
@for file in $$(find ./pkg ./cmd ./integration -name '*.go') ; \
do \
output=$$(grep 'Licensed under the Apache License' $$file) ; \
if [ "$$?" != "0" ]; then \
Expand Down
5 changes: 4 additions & 1 deletion cmd/trickster/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
package main

import (
"context"
"os"

"github.com/trickstercache/trickster/v2/pkg/appinfo"
"github.com/trickstercache/trickster/v2/pkg/daemon"
"github.com/trickstercache/trickster/v2/pkg/observability/logging"
Expand All @@ -38,7 +41,7 @@ const (
func main() {
appinfo.SetAppInfo(applicationName, applicationVersion,
applicationBuildTime, applicationGitCommitID)
err := daemon.Start()
err := daemon.Start(context.Background(), os.Args[1:]...)
if err != nil {
logger.Fatal(1, "trickster daemon failed to start",
logging.Pairs{"error": err})
Expand Down
14 changes: 14 additions & 0 deletions integration/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
GO ?= go

# Start/Stop the docker-compose environment for integration testing
developer-start:
@$(MAKE) -C .. developer-start

developer-stop:
@$(MAKE) -C .. developer-stop

developer-delete:
@$(MAKE) -C .. developer-delete

test:
$(GO) test -v --failfast
84 changes: 84 additions & 0 deletions integration/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright 2018 The Trickster 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 integration

import (
"context"
"io"
"net/http"
"os"
"path/filepath"
"slices"
"strings"
"testing"

"github.com/stretchr/testify/require"
"github.com/trickstercache/trickster/v2/pkg/daemon"
)

func TestMain(m *testing.M) {
os.Exit(m.Run())
}

// the expected error for Trickster's 'Start' to return
type expectedStartError struct {
ErrorContains *string
Error *error
}

// start a trickster instance with the provided context (for cancellation), and any args to pass to the daemon.
func startTrickster(t *testing.T, ctx context.Context, expected expectedStartError, args ...string) {
err := daemon.Start(ctx, args...)
if expected.Error != nil {
require.ErrorIs(t, err, *expected.Error)
} else if expected.ErrorContains != nil {
require.ErrorContains(t, err, *expected.ErrorContains)
} else {
require.NoError(t, err)
}
}

// query for prometheus metrics from a Trickster server at the given address.
func checkTricksterMetrics(t *testing.T, address string) []string {
url := "http://" + filepath.Join(address, "metrics")
t.Log("Checking Trickster metrics at", url)
resp, err := http.Get(url)
require.NoError(t, err)
defer resp.Body.Close()
require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from Trickster metrics endpoint")
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
lines := strings.Split(string(b), "\n")
// Filter out comments and empty lines
return slices.DeleteFunc(lines, func(s string) bool {
if strings.HasPrefix(s, "#") || s == "" {
return true
}
return false
})
}

// query trickster at the provided address/path.
func checkTrickster(t *testing.T, address string, path string, expectedStatus int) (string, http.Header) {
resp, err := http.Get("http://" + filepath.Join(address, path))
require.NoError(t, err)
defer resp.Body.Close()
require.Equal(t, expectedStatus, resp.StatusCode, "Expected status code %d from Trickster at %s", expectedStatus, path)
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
return string(body), resp.Header.Clone()
}
205 changes: 205 additions & 0 deletions integration/testdata/issue-856.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
frontend:
listen_port: 8500

negative_caches:
default:
'400': 3s
'404': 3s
'500': 3s
'502': 3s

caches:
mem1:
cache_type: memory
provider: memory
index:
max_size_objects: 512
max_size_backoff_objects: 128
mem2:
cache_type: memory
provider: memory
index:
max_size_objects: 1024
max_size_backoff_objects: 128
fs1:
cache_type: filesystem
provider: filesystem

request_rewriters:

remove-accept-encoding:
instructions:
- [ header, delete, Accept-Encoding ]

range-to-instant:
instructions:
- [ path , set , /api/v1/query ]
- [ param , delete , start ]
- [ param , delete , end ]
- [ param , delete , step ]
- [ chain , exec , remove-accept-encoding ]

rules:
example:
input_source: header
input_key: Authorization
input_type: string
input_encoding: base64
input_index: 1
input_delimiter: ' '
operation: prefix
next_route: rpc1
cases:
'1':
matches:
- 'trickster:'
next_route: sim1

tracing:
jc1:
provider: otlp
endpoint: 'http://127.0.0.1:14268/api/traces'
tags:
testTag: testTagValue
testTag2: testTag2Value
ja1:
provider: otlp
endpoint: '127.0.0.1:6831'
omit_tags:
- http.url

backends:
example:
provider: rule
rule_name: example
alb1:
provider: alb
alb:
mechanism: fgr
pool:
- prom1
- sim1
alb2:
provider: alb
alb:
mechanism: nlm
pool:
- lm1
- lm2
alb3:
latency_min: 300ms
provider: alb
alb:
mechanism: tsm
pool:
- prom1
- sim1
alb4:
provider: alb
alb:
mechanism: rr
pool:
- prom1
- prom1
- prom1
- sim1
click1:
provider: clickhouse
origin_url: 'http://127.0.0.1:8123'
cache_name: mem1
backfill_tolerance: 60s
timeseries_retention_factor: 2048
rp1:
provider: proxy
origin_url: 'http://127.0.0.1:9090'
rpc3:
provider: rpc
origin_url: 'http://127.0.0.1:9090'
prom1:
latency_max: 150ms
latency_min: 50ms
is_default: true
provider: prometheus
origin_url: 'http://127.0.0.1:9090'
cache_name: mem1
prom2:
provider: prometheus
origin_url: 'http://127.0.0.1:9090'
cache_name: fs1
flux1:
provider: influxdb
origin_url: 'http://127.0.0.1:8186/'
cache_name: mem1
backfill_tolerance: 30s
timeseries_retention_factor: 5184000
flux2:
provider: influxdb
origin_url: 'http://127.0.0.1:8086/'
cache_name: mem1
backfill_tolerance: 30s
timeseries_retention_factor: 5184000
sim1:
provider: prometheus
origin_url: 'http://127.0.0.1:8482/prometheus'
cache_name: mem1
max_idle_conns: 600
timeseries_retention_factor: 5184000
timeseries_ttl: 72h
max_ttl: 24h
max_object_size_bytes: 67108864
healthcheck:
interval: 300ms
timeout: 500ms
# uncomment and set certs to test TLS
# tls:
# full_chain_cert_path: >-
# /example/certs/127.0.0.1.pem
# private_key_path: >-
# /example/certs/127.0.0.1-key.pem
# insecure_skip_verify: true
sim2:
provider: prometheus
origin_url: 'http://127.0.0.1:8482/prometheus'
cache_name: mem1
flux-sim-1:
provider: influxdb
origin_url: 'http://127.0.0.1:8482/influxdb'
cache_name: mem1
max_idle_conns: 600
timeseries_retention_factor: 5184000
timeseries_ttl: 72h
max_ttl: 24h
max_object_size_bytes: 67108864
lm1:
provider: reverseproxycache
origin_url: 'http://127.0.0.1:8482/static/testing/test1'
lm2:
provider: reverseproxycache
origin_url: 'http://127.0.0.1:8482/static/testing/test2'
rpc1:
provider: reverseproxycache
origin_url: 'http://127.0.0.1:8482/byterange'
cache_name: fs1
paths:
root:
path: /
match_type: prefix
handler: proxycache
rpc2:
provider: reverseproxycache
origin_url: 'http://127.0.0.1:8482/byterange'
cache_name: mem1
paths:
root:
path: /
match_type: prefix
handler: proxycache

logging:
log_level: info

metrics:
listen_port: 8501

mgmt:
listen_port: 8502
Loading
Loading