mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-25 00:51:28 +02:00
[management,client] Plumb anonymize level and upload URL through remote debug bundle jobs
PR #7102 added an anonymization level to debug bundles and the anonymize_level proto field, but nothing on the management side ever set it: the remote-job builder dropped the field and the REST schema never exposed it, so a remotely triggered bundle always ran at the default level regardless of what an operator asked for. The upload destination for remote jobs was likewise fixed to the default upload server, with no way to direct a bundle to a self-hosted one. Expose anonymize_level and a new upload_url on the REST BundleParameters and the management proto, and map both onto the job request streamed to the client. Both are optional: an omitted value crosses the wire as the empty string, which the client resolves to its own defaults — the default anonymization level and the default upload server — matching how the netbird CLI defaults the same inputs.
This commit is contained in:
@@ -1393,7 +1393,7 @@ func (e *Engine) handleBundle(params *mgmProto.BundleParameters) (*mgmProto.JobR
|
||||
|
||||
waitFor := time.Duration(params.BundleForTime) * time.Minute
|
||||
|
||||
uploadKey, err := e.jobExecutor.BundleJob(e.ctx, bundleDeps, bundleJobParams, waitFor, e.config.ProfileConfig.ManagementURL.String())
|
||||
uploadKey, err := e.jobExecutor.BundleJob(e.ctx, bundleDeps, bundleJobParams, waitFor, e.config.ProfileConfig.ManagementURL.String(), params.GetUploadUrl())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -28,7 +28,11 @@ func NewExecutor() *Executor {
|
||||
return &Executor{}
|
||||
}
|
||||
|
||||
func (e *Executor) BundleJob(ctx context.Context, debugBundleDependencies debug.GeneratorDependencies, params debug.BundleConfig, waitForDuration time.Duration, mgmURL string) (string, error) {
|
||||
func (e *Executor) BundleJob(ctx context.Context, debugBundleDependencies debug.GeneratorDependencies, params debug.BundleConfig, waitForDuration time.Duration, mgmURL, uploadURL string) (string, error) {
|
||||
if uploadURL == "" {
|
||||
uploadURL = types.DefaultBundleURL
|
||||
}
|
||||
|
||||
if waitForDuration > MaxBundleWaitTime {
|
||||
log.Warnf("bundle wait time %v exceeds maximum %v, capping to maximum", waitForDuration, MaxBundleWaitTime)
|
||||
waitForDuration = MaxBundleWaitTime
|
||||
@@ -54,7 +58,7 @@ func (e *Executor) BundleJob(ctx context.Context, debugBundleDependencies debug.
|
||||
}
|
||||
}()
|
||||
|
||||
key, err := debug.UploadDebugBundle(ctx, types.DefaultBundleURL, mgmURL, path, false)
|
||||
key, err := debug.UploadDebugBundle(ctx, uploadURL, mgmURL, path, false)
|
||||
if err != nil {
|
||||
log.Errorf("failed to upload debug bundle: %v", err)
|
||||
return "", fmt.Errorf("upload debug bundle: %w", err)
|
||||
|
||||
@@ -209,6 +209,17 @@ func (j *Job) ToStreamJobRequest() (*proto.JobRequest, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// derefString returns the pointed-to string, or "" when the pointer is nil.
|
||||
// The bundle parameters carry anonymize_level and upload_url as optional
|
||||
// fields; an absent value maps to the empty proto string, which the client
|
||||
// resolves to its default.
|
||||
func derefString(s *string) string {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
return *s
|
||||
}
|
||||
|
||||
func (j *Job) buildStreamBundleResponse() (*proto.JobRequest, error) {
|
||||
var p api.BundleParameters
|
||||
if err := json.Unmarshal(j.Workload.Parameters, &p); err != nil {
|
||||
@@ -218,10 +229,12 @@ func (j *Job) buildStreamBundleResponse() (*proto.JobRequest, error) {
|
||||
ID: []byte(j.ID),
|
||||
WorkloadParameters: &proto.JobRequest_Bundle{
|
||||
Bundle: &proto.BundleParameters{
|
||||
BundleFor: p.BundleFor,
|
||||
BundleForTime: int64(p.BundleForTime),
|
||||
LogFileCount: int32(p.LogFileCount),
|
||||
Anonymize: p.Anonymize,
|
||||
BundleFor: p.BundleFor,
|
||||
BundleForTime: int64(p.BundleForTime),
|
||||
LogFileCount: int32(p.LogFileCount),
|
||||
Anonymize: p.Anonymize,
|
||||
AnonymizeLevel: derefString(p.AnonymizeLevel),
|
||||
UploadUrl: derefString(p.UploadUrl),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
||||
74
management/server/types/job_test.go
Normal file
74
management/server/types/job_test.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/http/api"
|
||||
)
|
||||
|
||||
func strPtr(s string) *string { return &s }
|
||||
|
||||
// bundleJobFromParams builds a bundle Job whose stored workload parameters are
|
||||
// the marshalled REST BundleParameters, mirroring what NewJob persists.
|
||||
func bundleJobFromParams(t *testing.T, p api.BundleParameters) *Job {
|
||||
t.Helper()
|
||||
raw, err := json.Marshal(p)
|
||||
require.NoError(t, err, "marshal bundle parameters")
|
||||
return &Job{
|
||||
ID: "job-1",
|
||||
Workload: Workload{
|
||||
Type: JobTypeBundle,
|
||||
Parameters: raw,
|
||||
Result: []byte("{}"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// TestBuildStreamBundleResponse_CarriesIdentityAndUploadFields verifies the
|
||||
// anonymize_level and upload_url REST fields are mapped onto the proto request
|
||||
// the client receives.
|
||||
func TestBuildStreamBundleResponse_CarriesIdentityAndUploadFields(t *testing.T) {
|
||||
job := bundleJobFromParams(t, api.BundleParameters{
|
||||
BundleFor: true,
|
||||
BundleForTime: 2,
|
||||
LogFileCount: 100,
|
||||
Anonymize: true,
|
||||
AnonymizeLevel: strPtr("strict"),
|
||||
UploadUrl: strPtr("https://upload.example.com"),
|
||||
})
|
||||
|
||||
req, err := job.ToStreamJobRequest()
|
||||
require.NoError(t, err, "ToStreamJobRequest must succeed")
|
||||
|
||||
bundle := req.GetBundle()
|
||||
require.NotNil(t, bundle, "the request must carry bundle parameters")
|
||||
assert.Equal(t, "strict", bundle.GetAnonymizeLevel(), "anonymize_level must reach the client")
|
||||
assert.Equal(t, "https://upload.example.com", bundle.GetUploadUrl(), "upload_url must reach the client")
|
||||
assert.True(t, bundle.GetAnonymize(), "existing fields must still map")
|
||||
assert.Equal(t, int32(100), bundle.GetLogFileCount(), "existing fields must still map")
|
||||
}
|
||||
|
||||
// TestBuildStreamBundleResponse_OmittedFieldsMapToEmpty verifies that omitted
|
||||
// optional fields map to the empty proto string, which the client resolves to
|
||||
// its defaults (default anonymization level, default upload server).
|
||||
func TestBuildStreamBundleResponse_OmittedFieldsMapToEmpty(t *testing.T) {
|
||||
job := bundleJobFromParams(t, api.BundleParameters{
|
||||
BundleFor: false,
|
||||
BundleForTime: 1,
|
||||
LogFileCount: 50,
|
||||
Anonymize: false,
|
||||
// AnonymizeLevel and UploadUrl intentionally nil.
|
||||
})
|
||||
|
||||
req, err := job.ToStreamJobRequest()
|
||||
require.NoError(t, err, "ToStreamJobRequest must succeed")
|
||||
|
||||
bundle := req.GetBundle()
|
||||
require.NotNil(t, bundle, "the request must carry bundle parameters")
|
||||
assert.Empty(t, bundle.GetAnonymizeLevel(), "an omitted anonymize_level must map to empty so the client defaults it")
|
||||
assert.Empty(t, bundle.GetUploadUrl(), "an omitted upload_url must map to empty so the client defaults it")
|
||||
}
|
||||
@@ -154,6 +154,14 @@ components:
|
||||
type: boolean
|
||||
description: Whether sensitive data should be anonymized in the bundle.
|
||||
example: false
|
||||
anonymize_level:
|
||||
type: string
|
||||
description: How much the anonymizer redacts. "default" (or empty) keeps internal IP ranges, "strict" also anonymizes them.
|
||||
example: strict
|
||||
upload_url:
|
||||
type: string
|
||||
description: Service URL the client requests an upload URL from before uploading the bundle. Empty selects the default upload server.
|
||||
example: https://upload.debug.netbird.io
|
||||
required:
|
||||
- bundle_for
|
||||
- bundle_for_time
|
||||
|
||||
@@ -2527,6 +2527,9 @@ type BundleParameters struct {
|
||||
// Anonymize Whether sensitive data should be anonymized in the bundle.
|
||||
Anonymize bool `json:"anonymize"`
|
||||
|
||||
// AnonymizeLevel How much the anonymizer redacts. "default" (or empty) keeps internal IP ranges, "strict" also anonymizes them.
|
||||
AnonymizeLevel *string `json:"anonymize_level,omitempty"`
|
||||
|
||||
// BundleFor Whether to generate a bundle for the given timeframe.
|
||||
BundleFor bool `json:"bundle_for"`
|
||||
|
||||
@@ -2535,6 +2538,9 @@ type BundleParameters struct {
|
||||
|
||||
// LogFileCount Maximum number of log files to include in the bundle.
|
||||
LogFileCount int `json:"log_file_count"`
|
||||
|
||||
// UploadUrl Service URL the client requests an upload URL from before uploading the bundle. Empty selects the default upload server.
|
||||
UploadUrl *string `json:"upload_url,omitempty"`
|
||||
}
|
||||
|
||||
// BundleResult defines model for BundleResult.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -114,6 +114,9 @@ message BundleParameters {
|
||||
// (or empty) keeps internal IP ranges, "strict" also anonymizes them.
|
||||
// Unknown values are treated as "strict".
|
||||
string anonymize_level = 5;
|
||||
// upload_url is the service URL the client requests an upload URL from
|
||||
// before uploading the bundle. Empty selects the default upload server.
|
||||
string upload_url = 6;
|
||||
}
|
||||
|
||||
message BundleResult {
|
||||
|
||||
Reference in New Issue
Block a user