Skip to content

Commit 3551b4c

Browse files
authored
refactor: deprecate AuthToken for non-OG pyroscope cloud (#125)
* refactor: deprecate AuthToken for non-OG pyroscope cloud * chore: rewrite log message * test: add test for uploadProfile
1 parent f3fe933 commit 3551b4c

File tree

7 files changed

+189
-29
lines changed

7 files changed

+189
-29
lines changed

Diff for: api.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ type Config struct {
1414
ApplicationName string // e.g backend.purchases
1515
Tags map[string]string
1616
ServerAddress string // e.g http://pyroscope.services.internal:4040
17-
AuthToken string // specify this token when using pyroscope cloud
1817
BasicAuthUser string // http basic auth user
1918
BasicAuthPassword string // http basic auth password
2019
TenantID string // specify TenantId when using phlare multi-tenancy
@@ -24,6 +23,9 @@ type Config struct {
2423
DisableGCRuns bool // this will disable automatic runtime.GC runs between getting the heap profiles
2524
HTTPHeaders map[string]string
2625

26+
// Deprecated: the field will be removed in future releases.
27+
// Use BasicAuthUser and BasicAuthPassword instead.
28+
AuthToken string // specify this token when using pyroscope cloud
2729
// Deprecated: the field will be removed in future releases.
2830
// Use UploadRate instead.
2931
DisableAutomaticResets bool

Diff for: collector_test.go

+7-22
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
package pyroscope
22

33
import (
4-
"fmt"
54
"io"
65
"reflect"
76
"sync"
87
"testing"
98
"time"
109

10+
"github.com/grafana/pyroscope-go/internal/testutil"
1111
"github.com/grafana/pyroscope-go/upstream"
1212
)
1313

1414
func Test_StartCPUProfile_interrupts_background_profiling(t *testing.T) {
15-
logger := new(testLogger)
15+
logger := testutil.NewTestLogger()
1616
collector := new(mockCollector)
1717
c := newCPUProfileCollector(
1818
"test",
@@ -45,22 +45,22 @@ func Test_StartCPUProfile_interrupts_background_profiling(t *testing.T) {
4545

4646
c.Stop()
4747

48-
if !reflect.DeepEqual(logger.lines, []string{
48+
if !reflect.DeepEqual(logger.Lines(), []string{
4949
"starting cpu profile collector",
5050
"cpu profile collector interrupted with StartCPUProfile",
5151
"cpu profile collector restored",
5252
"stopping cpu profile collector",
5353
"stopping cpu profile collector stopped",
5454
}) {
55-
for _, line := range logger.lines {
55+
for _, line := range logger.Lines() {
5656
t.Log(line)
5757
}
5858
t.Fatal("^ unexpected even sequence")
5959
}
6060
}
6161

6262
func Test_StartCPUProfile_blocks_Stop(t *testing.T) {
63-
logger := new(testLogger)
63+
logger := testutil.NewTestLogger()
6464
collector := new(mockCollector)
6565
c := newCPUProfileCollector(
6666
"test",
@@ -86,14 +86,14 @@ func Test_StartCPUProfile_blocks_Stop(t *testing.T) {
8686
go c.StopCPUProfile()
8787
c.Stop()
8888

89-
if !reflect.DeepEqual(logger.lines, []string{
89+
if !reflect.DeepEqual(logger.Lines(), []string{
9090
"starting cpu profile collector",
9191
"cpu profile collector interrupted with StartCPUProfile",
9292
"stopping cpu profile collector",
9393
"cpu profile collector restored",
9494
"stopping cpu profile collector stopped",
9595
}) {
96-
for _, line := range logger.lines {
96+
for _, line := range logger.Lines() {
9797
t.Log(line)
9898
}
9999
t.Fatal("^ unexpected even sequence")
@@ -146,18 +146,3 @@ type mockUpstream struct{ uploaded []*upstream.UploadJob }
146146
func (m *mockUpstream) Upload(j *upstream.UploadJob) { m.uploaded = append(m.uploaded, j) }
147147

148148
func (*mockUpstream) Flush() {}
149-
150-
type testLogger struct {
151-
sync.Mutex
152-
lines []string
153-
}
154-
155-
func (t *testLogger) Debugf(format string, args ...interface{}) { t.put(format, args...) }
156-
func (t *testLogger) Infof(format string, args ...interface{}) { t.put(format, args...) }
157-
func (t *testLogger) Errorf(format string, args ...interface{}) { t.put(format, args...) }
158-
159-
func (t *testLogger) put(format string, args ...interface{}) {
160-
t.Lock()
161-
t.lines = append(t.lines, fmt.Sprintf(format, args...))
162-
t.Unlock()
163-
}

Diff for: go.mod

+11-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ go 1.17
44

55
replace github.com/grafana/pyroscope-go/godeltaprof => ./godeltaprof
66

7-
require github.com/grafana/pyroscope-go/godeltaprof v0.1.8
7+
require (
8+
github.com/grafana/pyroscope-go/godeltaprof v0.1.8
9+
github.com/stretchr/testify v1.9.0
10+
)
811

9-
require github.com/klauspost/compress v1.17.8 // indirect
12+
require (
13+
github.com/davecgh/go-spew v1.1.1 // indirect
14+
github.com/klauspost/compress v1.17.8 // indirect
15+
github.com/pmezard/go-difflib v1.0.0 // indirect
16+
github.com/stretchr/objx v0.5.2 // indirect
17+
gopkg.in/yaml.v3 v3.0.1 // indirect
18+
)

Diff for: go.sum

+20
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,22 @@
1+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
14
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
25
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
6+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
7+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
8+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
9+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
10+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
11+
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
12+
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
13+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
14+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
15+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
16+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
17+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
18+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
19+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
20+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
21+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
22+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

Diff for: internal/testutil/logging.go

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package testutil
2+
3+
import (
4+
"fmt"
5+
"sync"
6+
)
7+
8+
type TestLogger struct {
9+
sync.Mutex
10+
lines []string
11+
}
12+
13+
func NewTestLogger() *TestLogger {
14+
return &TestLogger{lines: make([]string, 0)}
15+
}
16+
17+
func (t *TestLogger) Debugf(format string, args ...interface{}) { t.put(format, args...) }
18+
func (t *TestLogger) Infof(format string, args ...interface{}) { t.put(format, args...) }
19+
func (t *TestLogger) Errorf(format string, args ...interface{}) { t.put(format, args...) }
20+
21+
func (t *TestLogger) put(format string, args ...interface{}) {
22+
t.Lock()
23+
t.lines = append(t.lines, fmt.Sprintf(format, args...))
24+
t.Unlock()
25+
}
26+
27+
// Lines returns a copy of the logged lines
28+
func (t *TestLogger) Lines() []string {
29+
t.Lock()
30+
defer t.Unlock()
31+
return append([]string(nil), t.lines...)
32+
}

Diff for: upstream/remote/remote.go

+14-4
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,13 @@ import (
2121

2222
var errCloudTokenRequired = errors.New("please provide an authentication token. You can find it here: https://pyroscope.io/cloud")
2323

24-
const cloudHostnameSuffix = "pyroscope.cloud"
24+
const (
25+
authTokenDeprecationWarning = "Authtoken is specified, but deprecated and ignored. " +
26+
"Please switch to BasicAuthUser and BasicAuthPassword. " +
27+
"If you need to use Bearer token authentication for a custom setup, " +
28+
"you can use the HTTPHeaders option to set the Authorization header manually."
29+
cloudHostnameSuffix = "pyroscope.cloud"
30+
)
2531

2632
type Remote struct {
2733
cfg Config
@@ -40,6 +46,8 @@ type HTTPClient interface {
4046
}
4147

4248
type Config struct {
49+
// Deprecated: AuthToken will be removed in future releases.
50+
// Use BasicAuthUser and BasicAuthPassword instead.
4351
AuthToken string
4452
BasicAuthUser string // http basic auth user
4553
BasicAuthPassword string // http basic auth password
@@ -90,7 +98,7 @@ func NewRemote(cfg Config) (*Remote, error) {
9098
}
9199

92100
// authorize the token first
93-
if cfg.AuthToken == "" && requiresAuthToken(u) {
101+
if cfg.AuthToken == "" && isOGPyroscopeCloud(u) {
94102
return nil, errCloudTokenRequired
95103
}
96104

@@ -188,10 +196,12 @@ func (r *Remote) uploadProfile(j *upstream.UploadJob) error {
188196
request.Header.Set("Content-Type", contentType)
189197
// request.Header.Set("Content-Type", "binary/octet-stream+"+string(j.Format))
190198

191-
if r.cfg.AuthToken != "" {
199+
if r.cfg.AuthToken != "" && isOGPyroscopeCloud(u) {
192200
request.Header.Set("Authorization", "Bearer "+r.cfg.AuthToken)
193201
} else if r.cfg.BasicAuthUser != "" && r.cfg.BasicAuthPassword != "" {
194202
request.SetBasicAuth(r.cfg.BasicAuthUser, r.cfg.BasicAuthPassword)
203+
} else if r.cfg.AuthToken != "" {
204+
r.logger.Infof(authTokenDeprecationWarning)
195205
}
196206
if r.cfg.TenantID != "" {
197207
request.Header.Set("X-Scope-OrgID", r.cfg.TenantID)
@@ -234,7 +244,7 @@ func (r *Remote) handleJobs() {
234244
}
235245
}
236246

237-
func requiresAuthToken(u *url.URL) bool {
247+
func isOGPyroscopeCloud(u *url.URL) bool {
238248
return strings.HasSuffix(u.Host, cloudHostnameSuffix)
239249
}
240250

Diff for: upstream/remote/remote_test.go

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package remote
2+
3+
import (
4+
"bytes"
5+
"io"
6+
"net/http"
7+
"testing"
8+
"time"
9+
10+
"github.com/grafana/pyroscope-go/internal/testutil"
11+
"github.com/grafana/pyroscope-go/upstream"
12+
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/mock"
14+
)
15+
16+
func TestUploadProfile(t *testing.T) {
17+
tests := []struct {
18+
name string
19+
cfg Config
20+
serverAddress string
21+
expectedAuthHeader string
22+
expectWarning bool
23+
}{
24+
{
25+
name: "OG Pyroscope Cloud with AuthToken",
26+
cfg: Config{
27+
AuthToken: "test-token",
28+
Address: "https://example.pyroscope.cloud",
29+
},
30+
expectedAuthHeader: "Bearer test-token",
31+
expectWarning: false,
32+
},
33+
{
34+
name: "Non-OG Server with BasicAuth",
35+
cfg: Config{
36+
BasicAuthUser: "user",
37+
BasicAuthPassword: "pass",
38+
Address: "https://example.com",
39+
},
40+
expectedAuthHeader: "Basic dXNlcjpwYXNz", // Base64 encoded "user:pass"
41+
expectWarning: false,
42+
},
43+
{
44+
name: "Non-OG Server with AuthToken (Deprecated)",
45+
cfg: Config{
46+
AuthToken: "deprecated-token",
47+
Address: "https://example.com",
48+
},
49+
expectedAuthHeader: "",
50+
expectWarning: true,
51+
},
52+
}
53+
54+
for _, tt := range tests {
55+
t.Run(tt.name, func(t *testing.T) {
56+
logger := testutil.NewTestLogger()
57+
mockClient := new(MockHTTPClient)
58+
59+
mockClient.On("Do", mock.Anything).Return(&http.Response{
60+
StatusCode: 200,
61+
Body: io.NopCloser(bytes.NewBufferString("OK")),
62+
}, nil)
63+
64+
r := &Remote{
65+
cfg: tt.cfg,
66+
client: mockClient,
67+
logger: logger,
68+
}
69+
70+
err := r.uploadProfile(&upstream.UploadJob{
71+
Name: "test-profile",
72+
StartTime: time.Now(),
73+
EndTime: time.Now().Add(time.Minute),
74+
SpyName: "test-spy",
75+
SampleRate: 100,
76+
Units: "samples",
77+
})
78+
assert.NoError(t, err)
79+
80+
if tt.expectWarning {
81+
assert.Contains(t, logger.Lines(), authTokenDeprecationWarning)
82+
} else {
83+
assert.NotContains(t, logger.Lines(), authTokenDeprecationWarning)
84+
}
85+
86+
mockClient.AssertCalled(t, "Do", mock.MatchedBy(func(req *http.Request) bool {
87+
return req.Header.Get("Authorization") == tt.expectedAuthHeader
88+
}))
89+
90+
mockClient.AssertExpectations(t)
91+
})
92+
}
93+
}
94+
95+
type MockHTTPClient struct {
96+
mock.Mock
97+
}
98+
99+
func (m *MockHTTPClient) Do(req *http.Request) (*http.Response, error) {
100+
args := m.Called(req)
101+
return args.Get(0).(*http.Response), args.Error(1)
102+
}

0 commit comments

Comments
 (0)