mirror of
https://github.com/lukaszraczylo/oss-telemetry.git
synced 2026-10-15 01:07:33 +00:00
fix: make internals yaegi-interpretable for traefik plugin consumers
Swap atomic.Pointer[string] for a mutex-guarded string, atomic.Bool for the function-based sync/atomic int32 API, two range-over-int loops for classic index loops, and drop the init() that seeded the endpoint pointer. No public API or runtime behaviour change; go test, go vet, and golangci-lint all pass. Rationale: the traefikoidc Traefik plugin vendors and interprets dependency source via Yaegi, which on several Traefik runtimes cannot interpret generic stdlib types or range-over-int. This keeps the package importable from that plugin without altering what it does.
This commit is contained in:
+28
-14
@@ -49,21 +49,35 @@ const (
|
|||||||
maxVersionLen = 32
|
maxVersionLen = 32
|
||||||
)
|
)
|
||||||
|
|
||||||
// endpoint holds the ingest URL. Production code never mutates it; it is
|
// Yaegi note: this package is consumed by the traefikoidc Traefik plugin, which
|
||||||
// atomic only so the package's own test suite can safely retarget it at
|
// Traefik interprets with Yaegi (it vendors and interprets dependency source).
|
||||||
// httptest servers while goroutines started by Send are still in flight.
|
// It therefore avoids generic stdlib types (atomic.Pointer[T], atomic.Bool) and
|
||||||
var endpoint atomic.Pointer[string]
|
// range-over-int (Go 1.22), which some Traefik/Yaegi runtimes cannot interpret.
|
||||||
|
// Endpoint mutation uses a mutex-guarded string; the disabled flag uses the
|
||||||
|
// function-based sync/atomic int32 API (atomic.LoadInt32/StoreInt32).
|
||||||
var (
|
var (
|
||||||
disabled atomic.Bool
|
// endpointURL holds the ingest URL. Production code never mutates it; the
|
||||||
|
// setter exists only so the test suite can retarget it at httptest servers
|
||||||
|
// while goroutines started by Send are still in flight.
|
||||||
|
endpointMu sync.RWMutex
|
||||||
|
endpointURL = defaultEndpoint
|
||||||
|
|
||||||
|
disabled int32 // 0 = enabled, 1 = disabled; accessed via sync/atomic only
|
||||||
inflight sync.WaitGroup
|
inflight sync.WaitGroup
|
||||||
|
|
||||||
client = &http.Client{Timeout: httpTimeout}
|
client = &http.Client{Timeout: httpTimeout}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func currentEndpoint() string {
|
||||||
s := defaultEndpoint
|
endpointMu.RLock()
|
||||||
endpoint.Store(&s)
|
defer endpointMu.RUnlock()
|
||||||
|
return endpointURL
|
||||||
|
}
|
||||||
|
|
||||||
|
func setEndpointURL(u string) {
|
||||||
|
endpointMu.Lock()
|
||||||
|
endpointURL = u
|
||||||
|
endpointMu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send fires a single anonymous telemetry ping in the background and returns
|
// Send fires a single anonymous telemetry ping in the background and returns
|
||||||
@@ -78,7 +92,7 @@ func init() {
|
|||||||
// Call once at program startup. Calling repeatedly will send repeated pings;
|
// Call once at program startup. Calling repeatedly will send repeated pings;
|
||||||
// the server is responsible for deduplication.
|
// the server is responsible for deduplication.
|
||||||
func Send(project, version string) {
|
func Send(project, version string) {
|
||||||
if disabled.Load() {
|
if atomic.LoadInt32(&disabled) != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if isDisabledByEnv(project) {
|
if isDisabledByEnv(project) {
|
||||||
@@ -144,7 +158,7 @@ func isUsableBuildVersion(v string) bool {
|
|||||||
// Disable suppresses all subsequent Send calls in this process.
|
// Disable suppresses all subsequent Send calls in this process.
|
||||||
// Idempotent and safe to call from any goroutine.
|
// Idempotent and safe to call from any goroutine.
|
||||||
func Disable() {
|
func Disable() {
|
||||||
disabled.Store(true)
|
atomic.StoreInt32(&disabled, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait blocks until all in-flight pings have completed, or until timeout
|
// Wait blocks until all in-flight pings have completed, or until timeout
|
||||||
@@ -173,7 +187,7 @@ func dispatch(project, version string) {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), httpTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), httpTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, *endpoint.Load(), bytes.NewReader(body))
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, currentEndpoint(), bytes.NewReader(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -208,7 +222,7 @@ func validProject(p string) bool {
|
|||||||
if n == 0 || n > maxProjectLen {
|
if n == 0 || n > maxProjectLen {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for i := range n {
|
for i := 0; i < n; i++ {
|
||||||
c := p[i]
|
c := p[i]
|
||||||
switch {
|
switch {
|
||||||
case c >= 'a' && c <= 'z',
|
case c >= 'a' && c <= 'z',
|
||||||
@@ -330,7 +344,7 @@ func isDisabledByEnv(project string) bool {
|
|||||||
func projectEnvKey(project string) string {
|
func projectEnvKey(project string) string {
|
||||||
const suffix = "_DISABLE_TELEMETRY"
|
const suffix = "_DISABLE_TELEMETRY"
|
||||||
buf := make([]byte, 0, len(project)+len(suffix))
|
buf := make([]byte, 0, len(project)+len(suffix))
|
||||||
for i := range len(project) {
|
for i := 0; i < len(project); i++ {
|
||||||
c := project[i]
|
c := project[i]
|
||||||
switch {
|
switch {
|
||||||
case c == '-':
|
case c == '-':
|
||||||
|
|||||||
+8
-9
@@ -25,22 +25,21 @@ type pingPayload struct {
|
|||||||
// each other. Tests share `disabled`, `endpoint`, and `inflight`.
|
// each other. Tests share `disabled`, `endpoint`, and `inflight`.
|
||||||
func resetState(t *testing.T) {
|
func resetState(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
disabled.Store(false)
|
atomic.StoreInt32(&disabled, 0)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
disabled.Store(false)
|
atomic.StoreInt32(&disabled, 0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// setEndpoint atomically retargets the ingest URL for the duration of a test
|
// setEndpoint retargets the ingest URL for the duration of a test and restores
|
||||||
// and restores it on cleanup. Safe even if Send goroutines outlive the test.
|
// it on cleanup. Safe even if Send goroutines outlive the test (the setter is
|
||||||
|
// mutex-guarded).
|
||||||
func setEndpoint(t *testing.T, url string) {
|
func setEndpoint(t *testing.T, url string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
prev := *endpoint.Load()
|
prev := currentEndpoint()
|
||||||
s := url
|
setEndpointURL(url)
|
||||||
endpoint.Store(&s)
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
p := prev
|
setEndpointURL(prev)
|
||||||
endpoint.Store(&p)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user