Skip to content

Commit b2a107d

Browse files
committed
Merge branch 'main' into feat/api/v2/identities.createIdentity
2 parents 7ec3804 + b88a058 commit b2a107d

39 files changed

Lines changed: 560 additions & 183 deletions

File tree

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: Build and Publish Agent
2+
3+
on:
4+
push:
5+
tags:
6+
- "agent/v*"
7+
8+
permissions:
9+
contents: read
10+
packages: write
11+
12+
jobs:
13+
build:
14+
name: Build Agent
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Get Version
20+
run: |
21+
VERSION=$(git describe --tags --always)
22+
VERSION=${VERSION#agent/}
23+
echo "VERSION=$VERSION" >> $GITHUB_ENV
24+
25+
- name: Get tags
26+
run: echo "TAGS=ghcr.io/unkeyed/agent:${{env.VERSION}},ghcr.io/unkeyed/agent:latest" >> $GITHUB_ENV
27+
28+
- name: Set up QEMU
29+
uses: docker/setup-qemu-action@v3
30+
- name: Set up Docker Buildx
31+
uses: docker/setup-buildx-action@v3
32+
- name: Login to image repository
33+
uses: docker/login-action@v3
34+
with:
35+
registry: ghcr.io
36+
username: ${{ github.actor }}
37+
password: ${{ secrets.GHCR_TOKEN }}
38+
39+
- name: Build and push
40+
uses: docker/build-push-action@v6
41+
with:
42+
context: apps/agent
43+
platforms: linux/amd64
44+
push: true
45+
tags: ${{ env.TAGS }}
46+
build-args: VERSION=${{env.VERSION}}

apps/agent/.goreleaser.yaml

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
2+
project_name: agent
3+
4+
before:
5+
hooks:
6+
- go mod tidy
7+
8+
builds:
9+
- id: agent
10+
main: ./cmd/main.go
11+
binary: unkey
12+
13+
ldflags:
14+
- -X 'github.com/unkeyed/unkey/apps/agent/pkg/version.Version=${VERSION}'
15+
16+
# Custom build tags templates.
17+
# For more info refer to: https://pkg.go.dev/cmd/go#hdr-Build_constraints
18+
tags:
19+
- osusergo
20+
- netgo
21+
- static_build
22+
- feature
23+
24+
env:
25+
- CGO_ENABLED=0
26+
27+
# GOOS list to build for.
28+
# For more info refer to: https://pkg.go.dev/cmd/go#hdr-Environment_variables
29+
#
30+
# Default: [ 'darwin', 'linux', 'windows' ].
31+
goos:
32+
- darwin
33+
- freebsd
34+
- windows
35+
36+
# GOARCH to build for.
37+
# For more info refer to: https://pkg.go.dev/cmd/go#hdr-Environment_variables
38+
#
39+
# Default: [ '386', 'amd64', 'arm64' ].
40+
goarch:
41+
- amd64
42+
- arm
43+
- arm64
44+
dockers:
45+
- image_templates:
46+
- "ghcr.io/unkeyed/agent:{{ .Version }}"
47+
- "ghcr.io/unkeyed/agent:latest"
48+
dockerfile: Dockerfile.goreleaser
49+
build_flag_templates:
50+
- "--label=org.opencontainers.image.created={{.Date}}"
51+
- "--label=org.opencontainers.image.title={{.ProjectName}}"
52+
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
53+
- "--label=org.opencontainers.image.version={{.Version}}"
54+
- "--platform=linux/amd64"
55+
use: docker
56+
57+
archives:
58+
- format: tar.gz
59+
name_template: >-
60+
{{ .ProjectName }}_
61+
{{- title .Os }}_
62+
{{- if eq .Arch "amd64" }}x86_64
63+
{{- else if eq .Arch "386" }}i386
64+
{{- else }}{{ .Arch }}{{ end }}
65+
files:
66+
- README.md
67+
- LICENSE*
68+
- config.*.json
69+
70+
checksum:
71+
name_template: "checksums.txt"
72+
73+
snapshot:
74+
name_template: "{{ incpatch .Version }}-next"
75+
76+
changelog:
77+
sort: asc
78+
filters:
79+
exclude:
80+
- "^docs:"
81+
- "^test:"
82+
- "^chore:"

apps/chproxy/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
chproxy
2+
coverage.out

apps/chproxy/config.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,14 @@ func LoadConfig() (*Config, error) {
2929
ListenerPort: "7123",
3030
LogDebug: false,
3131
ServiceName: "chproxy",
32-
ServiceVersion: "1.3.0",
32+
ServiceVersion: "1.3.1",
3333
TraceMaxBatchSize: 512,
3434
TraceSampleRate: 0.25, // Sample 25%
3535
}
3636

37+
// Generic logger
38+
config.Logger = slog.New(slog.NewTextHandler(os.Stdout, nil))
39+
3740
config.ClickhouseURL = os.Getenv("CLICKHOUSE_URL")
3841
if config.ClickhouseURL == "" {
3942
return nil, fmt.Errorf("CLICKHOUSE_URL must be defined")

apps/chproxy/config_test.go

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"testing"
6+
"time"
7+
)
8+
9+
func TestLoadConfig(t *testing.T) {
10+
// Setup and cleanup for all tests
11+
setupEnv := func(t *testing.T, envs map[string]string) {
12+
t.Helper()
13+
for k, v := range envs {
14+
os.Setenv(k, v)
15+
}
16+
}
17+
18+
cleanEnv := func(t *testing.T, keys []string) {
19+
t.Helper()
20+
for _, k := range keys {
21+
os.Unsetenv(k)
22+
}
23+
}
24+
25+
// Required env vars that need to be cleaned up
26+
envKeys := []string{
27+
"CLICKHOUSE_URL",
28+
"BASIC_AUTH",
29+
"PORT",
30+
"OTEL_EXPORTER_LOG_DEBUG",
31+
"OTEL_TRACE_SAMPLE_RATE",
32+
}
33+
34+
// Cleanup after all tests in this function
35+
t.Cleanup(func() {
36+
cleanEnv(t, envKeys)
37+
})
38+
39+
// Test for valid config with default values
40+
t.Run("ValidConfig", func(t *testing.T) {
41+
// Set required environment variables
42+
setupEnv(t, map[string]string{
43+
"CLICKHOUSE_URL": "http://localhost:8123",
44+
"BASIC_AUTH": "user:password",
45+
})
46+
47+
config, err := LoadConfig()
48+
if err != nil {
49+
t.Fatalf("LoadConfig() error = %v, wantErr = false", err)
50+
}
51+
52+
// Use a table-driven check for expected values
53+
tests := []struct {
54+
name string
55+
got interface{}
56+
expected interface{}
57+
}{
58+
{"FlushInterval", config.FlushInterval, time.Second * 5},
59+
{"ListenerPort", config.ListenerPort, "7123"},
60+
{"LogDebug", config.LogDebug, false},
61+
{"ServiceName", config.ServiceName, "chproxy"},
62+
{"ServiceVersion", config.ServiceVersion, "1.3.1"},
63+
{"TraceMaxBatchSize", config.TraceMaxBatchSize, 512},
64+
{"TraceSampleRate", config.TraceSampleRate, 0.25},
65+
{"ClickhouseURL", config.ClickhouseURL, "http://localhost:8123"},
66+
{"BasicAuth", config.BasicAuth, "user:password"},
67+
}
68+
69+
for _, tc := range tests {
70+
t.Run(tc.name, func(t *testing.T) {
71+
if tc.got != tc.expected {
72+
t.Errorf("%s = %v, want %v", tc.name, tc.got, tc.expected)
73+
}
74+
})
75+
}
76+
77+
if config.Logger == nil {
78+
t.Error("Logger is nil, want non-nil")
79+
}
80+
})
81+
82+
// Test for error cases
83+
t.Run("ErrorCases", func(t *testing.T) {
84+
tests := []struct {
85+
name string
86+
envs map[string]string
87+
wantErr string
88+
}{
89+
{
90+
name: "MissingClickhouseURL",
91+
envs: map[string]string{
92+
"BASIC_AUTH": "user:password",
93+
},
94+
wantErr: "CLICKHOUSE_URL must be defined",
95+
},
96+
{
97+
name: "MissingBasicAuth",
98+
envs: map[string]string{
99+
"CLICKHOUSE_URL": "http://localhost:8123",
100+
},
101+
wantErr: "BASIC_AUTH must be defined",
102+
},
103+
{
104+
name: "InvalidSampleRate",
105+
envs: map[string]string{
106+
"CLICKHOUSE_URL": "http://localhost:8123",
107+
"BASIC_AUTH": "user:password",
108+
"OTEL_TRACE_SAMPLE_RATE": "invalid",
109+
},
110+
wantErr: "invalid TRACE_SAMPLE_RATE",
111+
},
112+
{
113+
name: "SampleRateTooLow",
114+
envs: map[string]string{
115+
"CLICKHOUSE_URL": "http://localhost:8123",
116+
"BASIC_AUTH": "user:password",
117+
"OTEL_TRACE_SAMPLE_RATE": "-0.1",
118+
},
119+
wantErr: "OTEL_TRACE_SAMPLE_RATE must be between 0.0 and 1.0",
120+
},
121+
{
122+
name: "SampleRateTooHigh",
123+
envs: map[string]string{
124+
"CLICKHOUSE_URL": "http://localhost:8123",
125+
"BASIC_AUTH": "user:password",
126+
"OTEL_TRACE_SAMPLE_RATE": "1.1",
127+
},
128+
wantErr: "OTEL_TRACE_SAMPLE_RATE must be between 0.0 and 1.0",
129+
},
130+
}
131+
132+
for _, tc := range tests {
133+
t.Run(tc.name, func(t *testing.T) {
134+
// Clean environment before each test case
135+
cleanEnv(t, envKeys)
136+
137+
// Set up environment for this test case
138+
setupEnv(t, tc.envs)
139+
140+
_, err := LoadConfig()
141+
if err == nil {
142+
t.Fatalf("LoadConfig() error = nil, wantErr = true")
143+
}
144+
145+
if got := err.Error(); got != tc.wantErr && got[:len(tc.wantErr)] != tc.wantErr {
146+
t.Errorf("LoadConfig() error = %v, want to contain %v", got, tc.wantErr)
147+
}
148+
})
149+
}
150+
})
151+
152+
// Test for custom configurations
153+
t.Run("CustomConfigurations", func(t *testing.T) {
154+
// Table-driven tests for custom configurations
155+
tests := []struct {
156+
name string
157+
envs map[string]string
158+
checkFunc func(*testing.T, *Config)
159+
}{
160+
{
161+
name: "CustomPort",
162+
envs: map[string]string{
163+
"CLICKHOUSE_URL": "http://localhost:8123",
164+
"BASIC_AUTH": "user:password",
165+
"PORT": "8000",
166+
},
167+
checkFunc: func(t *testing.T, c *Config) {
168+
if c.ListenerPort != "8000" {
169+
t.Errorf("ListenerPort = %s, want 8000", c.ListenerPort)
170+
}
171+
},
172+
},
173+
{
174+
name: "DebugMode",
175+
envs: map[string]string{
176+
"CLICKHOUSE_URL": "http://localhost:8123",
177+
"BASIC_AUTH": "user:password",
178+
"OTEL_EXPORTER_LOG_DEBUG": "true",
179+
},
180+
checkFunc: func(t *testing.T, c *Config) {
181+
if !c.LogDebug {
182+
t.Errorf("LogDebug = %v, want true", c.LogDebug)
183+
}
184+
},
185+
},
186+
{
187+
name: "CustomSampleRate",
188+
envs: map[string]string{
189+
"CLICKHOUSE_URL": "http://localhost:8123",
190+
"BASIC_AUTH": "user:password",
191+
"OTEL_TRACE_SAMPLE_RATE": "0.5",
192+
},
193+
checkFunc: func(t *testing.T, c *Config) {
194+
if c.TraceSampleRate != 0.5 {
195+
t.Errorf("TraceSampleRate = %f, want 0.5", c.TraceSampleRate)
196+
}
197+
},
198+
},
199+
{
200+
name: "ZeroSampleRate",
201+
envs: map[string]string{
202+
"CLICKHOUSE_URL": "http://localhost:8123",
203+
"BASIC_AUTH": "user:password",
204+
"OTEL_TRACE_SAMPLE_RATE": "0.0",
205+
},
206+
checkFunc: func(t *testing.T, c *Config) {
207+
if c.TraceSampleRate != 0.0 {
208+
t.Errorf("TraceSampleRate = %f, want 0.0", c.TraceSampleRate)
209+
}
210+
},
211+
},
212+
{
213+
name: "OneSampleRate",
214+
envs: map[string]string{
215+
"CLICKHOUSE_URL": "http://localhost:8123",
216+
"BASIC_AUTH": "user:password",
217+
"OTEL_TRACE_SAMPLE_RATE": "1.0",
218+
},
219+
checkFunc: func(t *testing.T, c *Config) {
220+
if c.TraceSampleRate != 1.0 {
221+
t.Errorf("TraceSampleRate = %f, want 1.0", c.TraceSampleRate)
222+
}
223+
},
224+
},
225+
}
226+
227+
for _, tc := range tests {
228+
t.Run(tc.name, func(t *testing.T) {
229+
// Clean environment before each test case
230+
cleanEnv(t, envKeys)
231+
232+
// Set up environment for this test case
233+
setupEnv(t, tc.envs)
234+
235+
config, err := LoadConfig()
236+
if err != nil {
237+
t.Fatalf("LoadConfig() error = %v, wantErr = false", err)
238+
}
239+
240+
tc.checkFunc(t, config)
241+
})
242+
}
243+
})
244+
}

0 commit comments

Comments
 (0)