From 2c0aaf6f5bde2bee6924dc11ffeb090d19d2e1cb Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 5 Jan 2026 14:30:13 +0530 Subject: [PATCH 01/59] Implement API boilerplate for POST /v1/opamp endpoint --- internal/pkg/api/api.go | 17 ++++++ internal/pkg/api/handleOpAMP.go | 39 ++++++++++++ internal/pkg/api/metrics.go | 2 + internal/pkg/api/openapi.gen.go | 32 ++++++++++ internal/pkg/api/router.go | 2 + internal/pkg/config/env_defaults.go | 12 ++++ internal/pkg/config/limits.go | 2 + internal/pkg/server/fleet.go | 2 + model/openapi.yml | 8 +++ pkg/api/client.gen.go | 93 +++++++++++++++++++++++++++++ 10 files changed, 209 insertions(+) create mode 100644 internal/pkg/api/handleOpAMP.go diff --git a/internal/pkg/api/api.go b/internal/pkg/api/api.go index f46a03f423..f983d73c03 100644 --- a/internal/pkg/api/api.go +++ b/internal/pkg/api/api.go @@ -46,6 +46,12 @@ func WithStatus(st *StatusT) APIOpt { } } +func WithOpAMP(oa *OpAMPT) APIOpt { + return func(a *apiServer) { + a.oa = oa + } +} + func WithUpload(ut *UploadT) APIOpt { return func(a *apiServer) { a.ut = ut @@ -84,6 +90,7 @@ type apiServer struct { at *ArtifactT ack *AckT st *StatusT + oa *OpAMPT ut *UploadT ft *FileDeliveryT pt *PGPRetrieverT @@ -230,3 +237,13 @@ func (a *apiServer) Status(w http.ResponseWriter, r *http.Request, params Status ErrorResp(w, r, err) } } +func (a *apiServer) OpAMP(w http.ResponseWriter, r *http.Request) { + zlog := hlog.FromRequest(r).With(). + Str("mod", kOpAMPMod). + Logger() + w.Header().Set("Content-Type", "application/x-protobuf") + if err := a.oa.handleOpAMP(zlog, r, w); err != nil { + cntOpAMP.IncError(err) + ErrorResp(w, r, err) + } +} diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go new file mode 100644 index 0000000000..560b735be4 --- /dev/null +++ b/internal/pkg/api/handleOpAMP.go @@ -0,0 +1,39 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package api + +import ( + "net/http" + + "github.com/elastic/fleet-server/v7/internal/pkg/bulk" + "github.com/elastic/fleet-server/v7/internal/pkg/cache" + "github.com/rs/zerolog" +) + +const ( + kOpAMPMod = "opAMP" +) + +type OpAMPT struct { + bulk bulk.Bulk + cache cache.Cache +} + +func NewOpAMPT(bulker bulk.Bulk, cache cache.Cache) *OpAMPT { + oa := &OpAMPT{ + bulk: bulker, + cache: cache, + } + return oa +} + +func (oa OpAMPT) handleOpAMP(zlog zerolog.Logger, r *http.Request, w http.ResponseWriter) error { + if _, err := authAPIKey(r, oa.bulk, oa.cache); err != nil { + zlog.Debug().Err(err).Msg("unauthenticated opamp request") + return err + } + + return nil +} diff --git a/internal/pkg/api/metrics.go b/internal/pkg/api/metrics.go index 1f3fb37999..0c445493f0 100644 --- a/internal/pkg/api/metrics.go +++ b/internal/pkg/api/metrics.go @@ -40,6 +40,7 @@ var ( cntEnroll routeStats cntAcks routeStats cntStatus routeStats + cntOpAMP routeStats cntUploadStart routeStats cntUploadChunk routeStats cntUploadEnd routeStats @@ -67,6 +68,7 @@ func init() { cntArtifacts.Register(routesRegistry.newRegistry("artifacts")) cntAcks.Register(routesRegistry.newRegistry("acks")) cntStatus.Register(routesRegistry.newRegistry("status")) + cntOpAMP.Register(routesRegistry.newRegistry("opAMP")) cntUploadStart.Register(routesRegistry.newRegistry("uploadStart")) cntUploadChunk.Register(routesRegistry.newRegistry("uploadChunk")) cntUploadEnd.Register(routesRegistry.newRegistry("uploadEnd")) diff --git a/internal/pkg/api/openapi.gen.go b/internal/pkg/api/openapi.gen.go index bd1b73088d..6900b85918 100644 --- a/internal/pkg/api/openapi.gen.go +++ b/internal/pkg/api/openapi.gen.go @@ -1843,6 +1843,9 @@ type ServerInterface interface { // (GET /api/status) Status(w http.ResponseWriter, r *http.Request, params StatusParams) + // Receive OpAMP AgentToServer messages + // (POST /v1/opamp) + OpAMP(w http.ResponseWriter, r *http.Request) } // Unimplemented server implementation that returns http.StatusNotImplemented for each endpoint. @@ -1910,6 +1913,12 @@ func (_ Unimplemented) Status(w http.ResponseWriter, r *http.Request, params Sta w.WriteHeader(http.StatusNotImplemented) } +// Receive OpAMP AgentToServer messages +// (POST /v1/opamp) +func (_ Unimplemented) OpAMP(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + // ServerInterfaceWrapper converts contexts to parameters. type ServerInterfaceWrapper struct { Handler ServerInterface @@ -2824,6 +2833,26 @@ func (siw *ServerInterfaceWrapper) Status(w http.ResponseWriter, r *http.Request handler.ServeHTTP(w, r) } +// OpAMP operation middleware +func (siw *ServerInterfaceWrapper) OpAMP(w http.ResponseWriter, r *http.Request) { + + ctx := r.Context() + + ctx = context.WithValue(ctx, AgentApiKeyScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.OpAMP(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + type UnescapedCookieParamError struct { ParamName string Err error @@ -2970,6 +2999,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/api/status", wrapper.Status) }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/v1/opamp", wrapper.OpAMP) + }) return r } diff --git a/internal/pkg/api/router.go b/internal/pkg/api/router.go index 7e60d5c3a2..d2b5a6f867 100644 --- a/internal/pkg/api/router.go +++ b/internal/pkg/api/router.go @@ -48,6 +48,7 @@ type limiter struct { enroll *limit.Limiter ack *limit.Limiter status *limit.Limiter + opAMP *limit.Limiter uploadBegin *limit.Limiter uploadChunk *limit.Limiter uploadComplete *limit.Limiter @@ -63,6 +64,7 @@ func Limiter(cfg *config.ServerLimits) *limiter { enroll: limit.NewLimiter(&cfg.EnrollLimit), ack: limit.NewLimiter(&cfg.AckLimit), status: limit.NewLimiter(&cfg.StatusLimit), + opAMP: limit.NewLimiter(&cfg.OpAMPLimit), uploadBegin: limit.NewLimiter(&cfg.UploadStartLimit), uploadChunk: limit.NewLimiter(&cfg.UploadChunkLimit), uploadComplete: limit.NewLimiter(&cfg.UploadEndLimit), diff --git a/internal/pkg/config/env_defaults.go b/internal/pkg/config/env_defaults.go index 7374d7dcd9..19d6e9ee4c 100644 --- a/internal/pkg/config/env_defaults.go +++ b/internal/pkg/config/env_defaults.go @@ -57,6 +57,11 @@ const ( defaultStatusMax = 50 defaultStatusMaxBody = 0 + defaultOpAMPInterval = time.Millisecond + defaultOpAMPBurst = 1000 + defaultOpAMPMax = 0 + defaultOpAMPMaxBody = 0 + defaultUploadStartInterval = time.Second * 2 defaultUploadStartBurst = 5 defaultUploadStartMax = 10 @@ -152,6 +157,7 @@ type serverLimitDefaults struct { EnrollLimit limit `config:"enroll_limit"` AckLimit limit `config:"ack_limit"` StatusLimit limit `config:"status_limit"` + OpAMPLimit limit `config:"opamp_limit"` UploadStartLimit limit `config:"upload_start_limit"` UploadEndLimit limit `config:"upload_end_limit"` UploadChunkLimit limit `config:"upload_chunk_limit"` @@ -201,6 +207,12 @@ func defaultserverLimitDefaults() *serverLimitDefaults { Max: defaultStatusMax, MaxBody: defaultStatusMaxBody, }, + OpAMPLimit: limit{ + Interval: defaultOpAMPInterval, + Burst: defaultOpAMPBurst, + Max: defaultOpAMPMax, + MaxBody: defaultOpAMPMaxBody, + }, UploadStartLimit: limit{ Interval: defaultUploadStartInterval, Burst: defaultUploadStartBurst, diff --git a/internal/pkg/config/limits.go b/internal/pkg/config/limits.go index db3baaa202..9c767d0b71 100644 --- a/internal/pkg/config/limits.go +++ b/internal/pkg/config/limits.go @@ -28,6 +28,7 @@ type ServerLimits struct { EnrollLimit Limit `config:"enroll_limit"` AckLimit Limit `config:"ack_limit"` StatusLimit Limit `config:"status_limit"` + OpAMPLimit Limit `config:"opamp_limit"` UploadStartLimit Limit `config:"upload_start_limit"` UploadEndLimit Limit `config:"upload_end_limit"` UploadChunkLimit Limit `config:"upload_chunk_limit"` @@ -57,6 +58,7 @@ func (c *ServerLimits) LoadLimits(limits *envLimits) { c.EnrollLimit = mergeEnvLimit(c.EnrollLimit, l.EnrollLimit) c.AckLimit = mergeEnvLimit(c.AckLimit, l.AckLimit) c.StatusLimit = mergeEnvLimit(c.StatusLimit, l.StatusLimit) + c.OpAMPLimit = mergeEnvLimit(c.OpAMPLimit, l.OpAMPLimit) c.UploadStartLimit = mergeEnvLimit(c.UploadStartLimit, l.UploadStartLimit) c.UploadEndLimit = mergeEnvLimit(c.UploadEndLimit, l.UploadEndLimit) c.UploadChunkLimit = mergeEnvLimit(c.UploadChunkLimit, l.UploadChunkLimit) diff --git a/internal/pkg/server/fleet.go b/internal/pkg/server/fleet.go index ef8aeb29b8..b961d3ae6a 100644 --- a/internal/pkg/server/fleet.go +++ b/internal/pkg/server/fleet.go @@ -536,6 +536,7 @@ func (f *Fleet) runSubsystems(ctx context.Context, cfg *config.Config, g *errgro at := api.NewArtifactT(&cfg.Inputs[0].Server, bulker, f.cache) ack := api.NewAckT(&cfg.Inputs[0].Server, bulker, f.cache) st := api.NewStatusT(&cfg.Inputs[0].Server, bulker, f.cache, api.WithSelfMonitor(sm), api.WithBuildInfo(f.bi)) + oa := api.NewOpAMPT(bulker, f.cache) ut := api.NewUploadT(&cfg.Inputs[0].Server, bulker, monCli, f.cache) // uses no-retry client for bufferless chunk upload ft := api.NewFileDeliveryT(&cfg.Inputs[0].Server, bulker, monCli, f.cache) pt := api.NewPGPRetrieverT(&cfg.Inputs[0].Server, bulker, f.cache) @@ -548,6 +549,7 @@ func (f *Fleet) runSubsystems(ctx context.Context, cfg *config.Config, g *errgro api.WithArtifact(at), api.WithAck(ack), api.WithStatus(st), + api.WithOpAMP(oa), api.WithUpload(ut), api.WithFileDelivery(ft), api.WithPGP(pt), diff --git a/model/openapi.yml b/model/openapi.yml index e74c822e41..d5ef81bcd0 100644 --- a/model/openapi.yml +++ b/model/openapi.yml @@ -2054,3 +2054,11 @@ paths: $ref: "#/components/responses/internalServerError" "503": $ref: "#/components/responses/unavailable" + /v1/opamp: + post: + operationId: opAMP + summary: Receive OpAMP AgentToServer messages + security: + - agentApiKey: [] + requestBody: + required: true \ No newline at end of file diff --git a/pkg/api/client.gen.go b/pkg/api/client.gen.go index d0c44e036d..337f1247ad 100644 --- a/pkg/api/client.gen.go +++ b/pkg/api/client.gen.go @@ -137,6 +137,9 @@ type ClientInterface interface { // Status request Status(ctx context.Context, params *StatusParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // OpAMPWithBody request with any body + OpAMPWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) } func (c *Client) GetPGPKey(ctx context.Context, major int, minor int, patch int, params *GetPGPKeyParams, reqEditors ...RequestEditorFn) (*http.Response, error) { @@ -343,6 +346,18 @@ func (c *Client) Status(ctx context.Context, params *StatusParams, reqEditors .. return c.Client.Do(req) } +func (c *Client) OpAMPWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewOpAMPRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + // NewGetPGPKeyRequest generates requests for GetPGPKey func NewGetPGPKeyRequest(server string, major int, minor int, patch int, params *GetPGPKeyParams) (*http.Request, error) { var err error @@ -1128,6 +1143,35 @@ func NewStatusRequest(server string, params *StatusParams) (*http.Request, error return req, nil } +// NewOpAMPRequestWithBody generates requests for OpAMP with any type of body +func NewOpAMPRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/v1/opamp") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { for _, r := range c.RequestEditors { if err := r(ctx, req); err != nil { @@ -1215,6 +1259,9 @@ type ClientWithResponsesInterface interface { // StatusWithResponse request StatusWithResponse(ctx context.Context, params *StatusParams, reqEditors ...RequestEditorFn) (*StatusResponse, error) + + // OpAMPWithBodyWithResponse request with any body + OpAMPWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*OpAMPResponse, error) } type GetPGPKeyResponse struct { @@ -1513,6 +1560,27 @@ func (r StatusResponse) StatusCode() int { return 0 } +type OpAMPResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// Status returns HTTPResponse.Status +func (r OpAMPResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r OpAMPResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + // GetPGPKeyWithResponse request returning *GetPGPKeyResponse func (c *ClientWithResponses) GetPGPKeyWithResponse(ctx context.Context, major int, minor int, patch int, params *GetPGPKeyParams, reqEditors ...RequestEditorFn) (*GetPGPKeyResponse, error) { rsp, err := c.GetPGPKey(ctx, major, minor, patch, params, reqEditors...) @@ -1660,6 +1728,15 @@ func (c *ClientWithResponses) StatusWithResponse(ctx context.Context, params *St return ParseStatusResponse(rsp) } +// OpAMPWithBodyWithResponse request with arbitrary body returning *OpAMPResponse +func (c *ClientWithResponses) OpAMPWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*OpAMPResponse, error) { + rsp, err := c.OpAMPWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseOpAMPResponse(rsp) +} + // ParseGetPGPKeyResponse parses an HTTP response from a GetPGPKeyWithResponse call func ParseGetPGPKeyResponse(rsp *http.Response) (*GetPGPKeyResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -2314,3 +2391,19 @@ func ParseStatusResponse(rsp *http.Response) (*StatusResponse, error) { return response, nil } + +// ParseOpAMPResponse parses an HTTP response from a OpAMPWithResponse call +func ParseOpAMPResponse(rsp *http.Response) (*OpAMPResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &OpAMPResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} From a85ecd16b2eef6345e6bd006cd5cdd4830126889 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 5 Jan 2026 15:09:50 +0530 Subject: [PATCH 02/59] Add OpAMP section to dev doc --- docs/developers-guide.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/developers-guide.md b/docs/developers-guide.md index b1d85768ec..50dd0caeff 100644 --- a/docs/developers-guide.md +++ b/docs/developers-guide.md @@ -420,3 +420,31 @@ For more advanced scenario you can build a custom docker image that you could us make -C dev-tools/cloud build-and-push-cloud-image ``` +## OpAMP + +This section describes how to connect a OpenTelemetry Collector instance to Fleet Server over OpAMP. + +1. Create a deployment in Elastic Cloud. Integrations Server is not needed as we will instead be + using the Fleet Server instance built from this repository so it can "speak" OpAMP to the OpenTelemetry + Collector. +2. Create an Elasticsearch service account token using Kibana > Dev Tools > Console. + ``` + POST /_security/service/elastic/fleet-server/credential/token/opamp + ``` +3. Create a `fleet-server.dev.yml` configuration file as described in https://github.com/elastic/kibana/blob/main/x-pack/platform/plugins/shared/fleet/dev_docs/local_setup/developing_kibana_and_fleet_server.md. +4. Build the Fleet Server binary for your platform. + ``` + PLATFORMS=darwin/arm64 mage build:binary + ``` +5. Run the Fleet Server binary with the above configuration. + ``` + ./build/binaries/fleet-server-9.4.0-darwin-aarch64/fleet-server -c fleet-server.dev.yml + ``` +6. Create OpenTelemetry Collector configuration for connecting to the Fleet Server instance and save it as `otel-opamp.yaml`. + ```yaml + ``` +7. Download and extract an OpenTelemetry Collector Contrib release for your platform from https://github.com/open-telemetry/opentelemetry-collector-releases/releases +8. Run the OpenTelemetry Collector with the above configuration. + ``` + ./otelcol-contrib --config ./otel-opamp.yaml + ``` \ No newline at end of file From c6c87407f52e48ef270cd27132bfd2dc5b6dcd92 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 5 Jan 2026 15:35:48 +0530 Subject: [PATCH 03/59] Flesh out dev doc --- docs/developers-guide.md | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/docs/developers-guide.md b/docs/developers-guide.md index 50dd0caeff..0ec5ae284a 100644 --- a/docs/developers-guide.md +++ b/docs/developers-guide.md @@ -440,11 +440,47 @@ This section describes how to connect a OpenTelemetry Collector instance to Flee ``` ./build/binaries/fleet-server-9.4.0-darwin-aarch64/fleet-server -c fleet-server.dev.yml ``` -6. Create OpenTelemetry Collector configuration for connecting to the Fleet Server instance and save it as `otel-opamp.yaml`. +6. Create an Elasticsearch API key using Kibana > Console > Dev Tools. Copy the value of the `encoded` field from + the response. + ``` + POST /_security/api_key + { + "name": "opamp-api-key", + "expiration": "30d" + } + ``` +7. Create OpenTelemetry Collector configuration for connecting to the Fleet Server instance and save it as `otel-opamp.yaml`. ```yaml + receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + + exporters: + debug: + verbosity: detailed + + extensions: + opamp: + server: + http: + endpoint: http://localhost:8220/v1/opamp + tls: + insecure: true + headers: + Authorization: ApiKey ${env:API_KEY} + instance_uid: "019b8d7a-2da8-7657-b52d-492a9de33319" + + service: + pipelines: + logs: + receivers: [otlp] + exporters: [debug] + extensions: [opamp] ``` 7. Download and extract an OpenTelemetry Collector Contrib release for your platform from https://github.com/open-telemetry/opentelemetry-collector-releases/releases 8. Run the OpenTelemetry Collector with the above configuration. ``` - ./otelcol-contrib --config ./otel-opamp.yaml + API_KEY= ./otelcol-contrib --config ./otel-opamp.yaml ``` \ No newline at end of file From 4aaeffa0170ffa9c4b73d5c128b0237c0e08930e Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 5 Jan 2026 16:05:11 +0530 Subject: [PATCH 04/59] Implement basic AgentToServer and ServerToAgent ping pong --- go.mod | 1 + go.sum | 2 ++ internal/pkg/api/handleOpAMP.go | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/go.mod b/go.mod index 6bf6bb0980..65d49a164c 100644 --- a/go.mod +++ b/go.mod @@ -69,6 +69,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/open-telemetry/opamp-go v0.22.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect diff --git a/go.sum b/go.sum index 4151efafc3..327c00e341 100644 --- a/go.sum +++ b/go.sum @@ -123,6 +123,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQgXIyI= github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= +github.com/open-telemetry/opamp-go v0.22.0 h1:7UnsQgFFS7ffM09JQk+9aGVBAAlsLfcooZ9xvSYwxWM= +github.com/open-telemetry/opamp-go v0.22.0/go.mod h1:339N71soCPrhHywbAcKUZJDODod581ZOxCpTkrl3zYQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 560b735be4..68d47e47cc 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -5,11 +5,16 @@ package api import ( + "fmt" + "io" "net/http" "github.com/elastic/fleet-server/v7/internal/pkg/bulk" "github.com/elastic/fleet-server/v7/internal/pkg/cache" + "github.com/gofrs/uuid/v5" + "github.com/open-telemetry/opamp-go/protobufs" "github.com/rs/zerolog" + "google.golang.org/protobuf/proto" ) const ( @@ -35,5 +40,32 @@ func (oa OpAMPT) handleOpAMP(zlog zerolog.Logger, r *http.Request, w http.Respon return err } + body, err := io.ReadAll(r.Body) + if err != nil { + return &BadRequestErr{msg: "failed to read AgentToServer request body"} + } + defer r.Body.Close() + + var aToS protobufs.AgentToServer + if err := proto.Unmarshal(body, &aToS); err != nil { + return &BadRequestErr{msg: "failed to unmarshal AgentToServer message"} + } + + instanceUID, err := uuid.FromBytes(aToS.InstanceUid) + if err != nil { + return &BadRequestErr{msg: "failed to parse instance_uid from AgentToServer message"} + } + zlog.Debug(). + Str("instance_uid", instanceUID.String()). + Msg("received AgentToServer message from agent") + + sToA := protobufs.ServerToAgent{} + resp, err := proto.Marshal(&sToA) + if err != nil { + return fmt.Errorf("failed to marshal ServerToAgent response body: %w", err) + } + + w.Write(resp) + return nil } From 3b461de63e7cefc79fcc394e7ed473e39487d754 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 6 Jan 2026 13:43:08 +0530 Subject: [PATCH 05/59] Implement enrollment --- internal/pkg/api/handleOpAMP.go | 136 +++++++++++++++++++++++++++++++- internal/pkg/server/fleet.go | 2 +- 2 files changed, 134 insertions(+), 4 deletions(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 68d47e47cc..0a9b01a471 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -5,15 +5,25 @@ package api import ( + "context" + "encoding/json" + "errors" "fmt" "io" "net/http" + "time" + "github.com/elastic/fleet-server/v7/internal/pkg/apikey" "github.com/elastic/fleet-server/v7/internal/pkg/bulk" "github.com/elastic/fleet-server/v7/internal/pkg/cache" + "github.com/elastic/fleet-server/v7/internal/pkg/checkin" + "github.com/elastic/fleet-server/v7/internal/pkg/dl" + "github.com/elastic/fleet-server/v7/internal/pkg/model" + "github.com/elastic/fleet-server/v7/internal/pkg/sqn" "github.com/gofrs/uuid/v5" "github.com/open-telemetry/opamp-go/protobufs" "github.com/rs/zerolog" + semconv "go.opentelemetry.io/otel/semconv/v1.17.0" "google.golang.org/protobuf/proto" ) @@ -24,18 +34,25 @@ const ( type OpAMPT struct { bulk bulk.Bulk cache cache.Cache + bc *checkin.Bulk } -func NewOpAMPT(bulker bulk.Bulk, cache cache.Cache) *OpAMPT { +func NewOpAMPT( + bulker bulk.Bulk, + cache cache.Cache, + bc *checkin.Bulk, +) *OpAMPT { oa := &OpAMPT{ bulk: bulker, cache: cache, + bc: bc, } return oa } func (oa OpAMPT) handleOpAMP(zlog zerolog.Logger, r *http.Request, w http.ResponseWriter) error { - if _, err := authAPIKey(r, oa.bulk, oa.cache); err != nil { + apiKey, err := authAPIKey(r, oa.bulk, oa.cache) + if err != nil { zlog.Debug().Err(err).Msg("unauthenticated opamp request") return err } @@ -59,13 +76,126 @@ func (oa OpAMPT) handleOpAMP(zlog zerolog.Logger, r *http.Request, w http.Respon Str("instance_uid", instanceUID.String()). Msg("received AgentToServer message from agent") + // Check if Agent is "enrolled"; if it is, update it; otherwise, enroll it. + isEnrolled, err := oa.isAgentEnrolled(zlog, instanceUID.String()) + if err != nil { + return fmt.Errorf("failed to check if agent is enrolled: %w", err) + } + + zlog.Debug(). + Bool("is_enrolled", isEnrolled). + Str("agent_id", instanceUID.String()). + Msg("agent enrollment status") + if isEnrolled { + if err := oa.updateAgent(zlog, instanceUID.String(), aToS); err != nil { + return fmt.Errorf("failed to update persisted Agent information: %w", err) + } + } else { + if err := oa.enrollAgent(zlog, instanceUID.String(), aToS, apiKey); err != nil { + return fmt.Errorf("failed to enroll agent: %w", err) + } + } + sToA := protobufs.ServerToAgent{} resp, err := proto.Marshal(&sToA) if err != nil { return fmt.Errorf("failed to marshal ServerToAgent response body: %w", err) } - w.Write(resp) + _, err = w.Write(resp) + return err +} + +func (oa OpAMPT) isAgentEnrolled(zlog zerolog.Logger, agentID string) (bool, error) { + ctx := context.TODO() + agent, err := dl.FindAgent(ctx, oa.bulk, dl.QueryAgentByID, dl.FieldID, agentID) + if errors.Is(err, dl.ErrNotFound) { + return false, nil + } + + if err != nil { + return false, fmt.Errorf("failed to find agent: %w", err) + } + + if agent.Id == "" { + return false, nil + } + + return true, nil +} + +func (oa OpAMPT) enrollAgent(zlog zerolog.Logger, agentID string, aToS protobufs.AgentToServer, apiKey *apikey.APIKey) error { + zlog.Debug(). + Str("agentID", agentID). + Msg("enrolling agent") + ctx := context.TODO() + rec, err := dl.FindEnrollmentAPIKey(ctx, oa.bulk, dl.QueryEnrollmentAPIKeyByID, dl.FieldAPIKeyID, apiKey.ID) + if err != nil { + return fmt.Errorf("failed to find enrollment API key: %w", err) + } + + now := time.Now() + agent := model.Agent{ + ESDocument: model.ESDocument{Id: agentID}, + Active: true, + EnrolledAt: now.UTC().Format(time.RFC3339), + PolicyID: rec.PolicyID, + Agent: &model.AgentMetadata{ + ID: agentID, + }, + } + + data, err := json.Marshal(agent) + if err != nil { + return err + } + + zlog.Debug(). + Str("agent document", string(data)). + Msg("creating .fleet-agents doc") + if _, err = oa.bulk.Create(ctx, dl.FleetAgents, agentID, data, bulk.WithRefresh()); err != nil { + return err + } return nil } + +func (oa OpAMPT) updateAgent(zlog zerolog.Logger, agentID string, aToS protobufs.AgentToServer) error { + zlog.Debug(). + Str("aToS", aToS.String()). + Msg("updating fleet-agents doc") + + initialOpts := []checkin.Option{ + checkin.WithSeqNo(sqn.SeqNo{int64(aToS.SequenceNum)}), + //checkin.WithMessage(), + //checkin.WithMeta(rawMeta), + //checkin.WithComponents(rawComponents), + //checkin.WithDeleteAudit(agent.AuditUnenrolledReason != "" || agent.UnenrolledAt != ""), + } + + // Extract the agent version from the identifying attributes. However, agent description is + // only sent if any of its fields, including identifying attributes, change. + if aToS.AgentDescription != nil { + var agentVersion string + for _, ia := range aToS.AgentDescription.IdentifyingAttributes { + switch ia.Key { + case string(semconv.ServiceVersionKey): + agentVersion = ia.String() + } + } + initialOpts = append(initialOpts, checkin.WithVer(agentVersion)) + } + + // Extract the health status from the health message if it exists. + if aToS.Health != nil { + initialOpts = append(initialOpts, checkin.WithStatus(aToS.Health.Status)) + } + + // Extract the unhealthy reason from the health message if it exists. + if aToS.Health != nil && aToS.Health.LastError != "" { + unhealthyReason := []string{aToS.Health.LastError} + initialOpts = append(initialOpts, checkin.WithUnhealthyReason(&unhealthyReason)) + } + + return oa.bc.CheckIn(agentID, initialOpts...) +} diff --git a/internal/pkg/server/fleet.go b/internal/pkg/server/fleet.go index b961d3ae6a..f83a900fd0 100644 --- a/internal/pkg/server/fleet.go +++ b/internal/pkg/server/fleet.go @@ -536,7 +536,7 @@ func (f *Fleet) runSubsystems(ctx context.Context, cfg *config.Config, g *errgro at := api.NewArtifactT(&cfg.Inputs[0].Server, bulker, f.cache) ack := api.NewAckT(&cfg.Inputs[0].Server, bulker, f.cache) st := api.NewStatusT(&cfg.Inputs[0].Server, bulker, f.cache, api.WithSelfMonitor(sm), api.WithBuildInfo(f.bi)) - oa := api.NewOpAMPT(bulker, f.cache) + oa := api.NewOpAMPT(bulker, f.cache, bc) ut := api.NewUploadT(&cfg.Inputs[0].Server, bulker, monCli, f.cache) // uses no-retry client for bufferless chunk upload ft := api.NewFileDeliveryT(&cfg.Inputs[0].Server, bulker, monCli, f.cache) pt := api.NewPGPRetrieverT(&cfg.Inputs[0].Server, bulker, f.cache) From 2ca7b0f779b3ee74ad07e6e631a08e1ea0f55770 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 6 Jan 2026 13:45:48 +0530 Subject: [PATCH 06/59] Update dev doc to use Fleet enrollment token --- docs/developers-guide.md | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/docs/developers-guide.md b/docs/developers-guide.md index 0ec5ae284a..ae56be6104 100644 --- a/docs/developers-guide.md +++ b/docs/developers-guide.md @@ -440,15 +440,7 @@ This section describes how to connect a OpenTelemetry Collector instance to Flee ``` ./build/binaries/fleet-server-9.4.0-darwin-aarch64/fleet-server -c fleet-server.dev.yml ``` -6. Create an Elasticsearch API key using Kibana > Console > Dev Tools. Copy the value of the `encoded` field from - the response. - ``` - POST /_security/api_key - { - "name": "opamp-api-key", - "expiration": "30d" - } - ``` +6. Create a new policy in Fleet. Copy the enrollment token for that policy. 7. Create OpenTelemetry Collector configuration for connecting to the Fleet Server instance and save it as `otel-opamp.yaml`. ```yaml receivers: @@ -469,7 +461,7 @@ This section describes how to connect a OpenTelemetry Collector instance to Flee tls: insecure: true headers: - Authorization: ApiKey ${env:API_KEY} + Authorization: ApiKey ${env:FLEET_ENROLLMENT_TOKEN} instance_uid: "019b8d7a-2da8-7657-b52d-492a9de33319" service: @@ -482,5 +474,5 @@ This section describes how to connect a OpenTelemetry Collector instance to Flee 7. Download and extract an OpenTelemetry Collector Contrib release for your platform from https://github.com/open-telemetry/opentelemetry-collector-releases/releases 8. Run the OpenTelemetry Collector with the above configuration. ``` - API_KEY= ./otelcol-contrib --config ./otel-opamp.yaml - ``` \ No newline at end of file + FLEET_ENROLLMENT_TOKEN= ./otelcol-contrib --config ./otel-opamp.yaml + ``` From 1452233aba8e5f919f3edb95521609f53c6a91cc Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 6 Jan 2026 15:02:23 +0530 Subject: [PATCH 07/59] Recording local_metadata --- go.mod | 4 +- internal/pkg/api/handleOpAMP.go | 98 ++++++++++++++++++++++----------- 2 files changed, 68 insertions(+), 34 deletions(-) diff --git a/go.mod b/go.mod index 65d49a164c..f28567f143 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/mailru/easyjson v0.9.1 github.com/miolini/datacounter v1.0.3 github.com/oapi-codegen/runtime v1.1.2 + github.com/open-telemetry/opamp-go v0.22.0 github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 github.com/prometheus/client_golang v1.23.2 github.com/rs/xid v1.6.0 @@ -36,6 +37,7 @@ require ( go.elastic.co/apm/module/apmzerolog/v2 v2.7.3 go.elastic.co/apm/v2 v2.7.3 go.elastic.co/ecszerolog v0.2.0 + go.opentelemetry.io/otel v1.39.0 go.uber.org/zap v1.27.1 golang.org/x/net v0.50.0 golang.org/x/sync v0.19.0 @@ -69,7 +71,6 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/open-telemetry/opamp-go v0.22.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect @@ -88,7 +89,6 @@ require ( go.elastic.co/ecszap v1.0.3 // indirect go.elastic.co/fastjson v1.5.1 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/otel v1.39.0 // indirect go.opentelemetry.io/otel/metric v1.39.0 // indirect go.opentelemetry.io/otel/trace v1.39.0 // indirect go.uber.org/multierr v1.11.0 // indirect diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 0a9b01a471..683b83b733 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -19,11 +19,11 @@ import ( "github.com/elastic/fleet-server/v7/internal/pkg/checkin" "github.com/elastic/fleet-server/v7/internal/pkg/dl" "github.com/elastic/fleet-server/v7/internal/pkg/model" - "github.com/elastic/fleet-server/v7/internal/pkg/sqn" "github.com/gofrs/uuid/v5" "github.com/open-telemetry/opamp-go/protobufs" "github.com/rs/zerolog" - semconv "go.opentelemetry.io/otel/semconv/v1.17.0" + "go.opentelemetry.io/otel/attribute" + semconv "go.opentelemetry.io/otel/semconv/v1.37.0" "google.golang.org/protobuf/proto" ) @@ -35,6 +35,8 @@ type OpAMPT struct { bulk bulk.Bulk cache cache.Cache bc *checkin.Bulk + + agentMetas map[string]localMetadata } func NewOpAMPT( @@ -43,9 +45,10 @@ func NewOpAMPT( bc *checkin.Bulk, ) *OpAMPT { oa := &OpAMPT{ - bulk: bulker, - cache: cache, - bc: bc, + bulk: bulker, + cache: cache, + bc: bc, + agentMetas: map[string]localMetadata{}, } return oa } @@ -86,16 +89,16 @@ func (oa OpAMPT) handleOpAMP(zlog zerolog.Logger, r *http.Request, w http.Respon Bool("is_enrolled", isEnrolled). Str("agent_id", instanceUID.String()). Msg("agent enrollment status") - if isEnrolled { - if err := oa.updateAgent(zlog, instanceUID.String(), aToS); err != nil { - return fmt.Errorf("failed to update persisted Agent information: %w", err) - } - } else { + if !isEnrolled { if err := oa.enrollAgent(zlog, instanceUID.String(), aToS, apiKey); err != nil { return fmt.Errorf("failed to enroll agent: %w", err) } } + if err := oa.updateAgent(zlog, instanceUID.String(), aToS); err != nil { + return fmt.Errorf("failed to update persisted Agent information: %w", err) + } + sToA := protobufs.ServerToAgent{} resp, err := proto.Marshal(&sToA) if err != nil { @@ -135,6 +138,42 @@ func (oa OpAMPT) enrollAgent(zlog zerolog.Logger, agentID string, aToS protobufs } now := time.Now() + + // Extract the agent version from the agent description's identifying attributes. Also, extract + // the hostname from the agent description's non-identifying attributes. However, the agent + // description is only sent if any of its fields change. + meta := localMetadata{} + meta.Elastic.Agent.ID = agentID + if aToS.AgentDescription != nil { + // Extract agent version + for _, ia := range aToS.AgentDescription.IdentifyingAttributes { + switch attribute.Key(ia.Key) { + case semconv.ServiceVersionKey: + meta.Elastic.Agent.Version = ia.GetValue().GetStringValue() + } + } + zlog.Debug().Str("agent_version", meta.Elastic.Agent.Version).Msg("extracted agent version") + + // Extract hostname + for _, nia := range aToS.AgentDescription.NonIdentifyingAttributes { + switch attribute.Key(nia.Key) { + case semconv.HostNameKey: + hostname := nia.GetValue().GetStringValue() + meta.Host.Name = hostname + meta.Host.Hostname = hostname + } + } + zlog.Debug().Str("hostname", meta.Host.Hostname).Msg("extracted hostname") + } + + // Update local metadata if something has changed + data, err := json.Marshal(meta) + if err != nil { + return fmt.Errorf("failed to marshal local metadata: %w", err) + } + + zlog.Debug().RawJSON("meta", data).Msg("updating local metadata") + agent := model.Agent{ ESDocument: model.ESDocument{Id: agentID}, Active: true, @@ -143,9 +182,10 @@ func (oa OpAMPT) enrollAgent(zlog zerolog.Logger, agentID string, aToS protobufs Agent: &model.AgentMetadata{ ID: agentID, }, + LocalMetadata: data, } - data, err := json.Marshal(agent) + data, err = json.Marshal(agent) if err != nil { return err } @@ -163,28 +203,9 @@ func (oa OpAMPT) enrollAgent(zlog zerolog.Logger, agentID string, aToS protobufs func (oa OpAMPT) updateAgent(zlog zerolog.Logger, agentID string, aToS protobufs.AgentToServer) error { zlog.Debug(). Str("aToS", aToS.String()). - Msg("updating fleet-agents doc") + Msg("updating .fleet-agents doc") - initialOpts := []checkin.Option{ - checkin.WithSeqNo(sqn.SeqNo{int64(aToS.SequenceNum)}), - //checkin.WithMessage(), - //checkin.WithMeta(rawMeta), - //checkin.WithComponents(rawComponents), - //checkin.WithDeleteAudit(agent.AuditUnenrolledReason != "" || agent.UnenrolledAt != ""), - } - - // Extract the agent version from the identifying attributes. However, agent description is - // only sent if any of its fields, including identifying attributes, change. - if aToS.AgentDescription != nil { - var agentVersion string - for _, ia := range aToS.AgentDescription.IdentifyingAttributes { - switch ia.Key { - case string(semconv.ServiceVersionKey): - agentVersion = ia.String() - } - } - initialOpts = append(initialOpts, checkin.WithVer(agentVersion)) - } + initialOpts := make([]checkin.Option, 0) // Extract the health status from the health message if it exists. if aToS.Health != nil { @@ -199,3 +220,16 @@ func (oa OpAMPT) updateAgent(zlog zerolog.Logger, agentID string, aToS protobufs return oa.bc.CheckIn(agentID, initialOpts...) } + +type localMetadata struct { + Elastic struct { + Agent struct { + ID string `json:"id,omitempty"` + Version string `json:"version,omitempty"` + } `json:"agent,omitempty"` + } `json:"elastic,omitempty"` + Host struct { + Hostname string `json:"hostname,omitempty"` + Name string `json:"name,omitempty"` + } `json:"host,omitempty"` +} From 4ea7827197731240f062e6d8b110c4bb22bfc975 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 7 Jan 2026 15:51:14 +0530 Subject: [PATCH 08/59] Request components from agents every minute --- internal/pkg/api/handleOpAMP.go | 37 +++++++++++++++++++++++++++++++-- internal/pkg/server/fleet.go | 2 +- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 683b83b733..65a17b31ac 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -37,9 +37,12 @@ type OpAMPT struct { bc *checkin.Bulk agentMetas map[string]localMetadata + + flags uint64 } func NewOpAMPT( + ctx context.Context, bulker bulk.Bulk, cache cache.Cache, bc *checkin.Bulk, @@ -49,11 +52,31 @@ func NewOpAMPT( cache: cache, bc: bc, agentMetas: map[string]localMetadata{}, + flags: uint64(protobufs.ServerToAgentFlags_ServerToAgentFlags_ReportAvailableComponents), } + + go oa.startTimers(ctx) return oa } -func (oa OpAMPT) handleOpAMP(zlog zerolog.Logger, r *http.Request, w http.ResponseWriter) error { +func (oa *OpAMPT) startTimers(ctx context.Context) { + zerolog.Ctx(ctx).Debug().Msg("starting opAMP timers") + ticker := time.NewTicker(time.Minute) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + zerolog.Ctx(ctx).Debug().Msg("stopping opAMP timers") + return + case <-ticker.C: + zerolog.Ctx(ctx).Debug().Msg("opAMP timer tick; setting flags") + oa.flags = uint64(protobufs.ServerToAgentFlags_ServerToAgentFlags_ReportAvailableComponents) + } + } +} + +func (oa *OpAMPT) handleOpAMP(zlog zerolog.Logger, r *http.Request, w http.ResponseWriter) error { apiKey, err := authAPIKey(r, oa.bulk, oa.cache) if err != nil { zlog.Debug().Err(err).Msg("unauthenticated opamp request") @@ -77,6 +100,7 @@ func (oa OpAMPT) handleOpAMP(zlog zerolog.Logger, r *http.Request, w http.Respon } zlog.Debug(). Str("instance_uid", instanceUID.String()). + Str("aToS", aToS.String()). Msg("received AgentToServer message from agent") // Check if Agent is "enrolled"; if it is, update it; otherwise, enroll it. @@ -99,13 +123,22 @@ func (oa OpAMPT) handleOpAMP(zlog zerolog.Logger, r *http.Request, w http.Respon return fmt.Errorf("failed to update persisted Agent information: %w", err) } - sToA := protobufs.ServerToAgent{} + sToA := protobufs.ServerToAgent{ + Flags: oa.flags, + } + resp, err := proto.Marshal(&sToA) if err != nil { return fmt.Errorf("failed to marshal ServerToAgent response body: %w", err) } + zlog.Debug().Str("resp", sToA.String()).Msg("sending ServerToAgent response") _, err = w.Write(resp) + + // Reset flags; timer will set them again + zlog.Debug().Msg("resetting flags") + oa.flags = 0 + return err } diff --git a/internal/pkg/server/fleet.go b/internal/pkg/server/fleet.go index f83a900fd0..e8a89ecc38 100644 --- a/internal/pkg/server/fleet.go +++ b/internal/pkg/server/fleet.go @@ -536,7 +536,7 @@ func (f *Fleet) runSubsystems(ctx context.Context, cfg *config.Config, g *errgro at := api.NewArtifactT(&cfg.Inputs[0].Server, bulker, f.cache) ack := api.NewAckT(&cfg.Inputs[0].Server, bulker, f.cache) st := api.NewStatusT(&cfg.Inputs[0].Server, bulker, f.cache, api.WithSelfMonitor(sm), api.WithBuildInfo(f.bi)) - oa := api.NewOpAMPT(bulker, f.cache, bc) + oa := api.NewOpAMPT(ctx, bulker, f.cache, bc) ut := api.NewUploadT(&cfg.Inputs[0].Server, bulker, monCli, f.cache) // uses no-retry client for bufferless chunk upload ft := api.NewFileDeliveryT(&cfg.Inputs[0].Server, bulker, monCli, f.cache) pt := api.NewPGPRetrieverT(&cfg.Inputs[0].Server, bulker, f.cache) From 6b7fac84144f3bb335091e1418eac1cf8cb143a7 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 7 Jan 2026 15:51:36 +0530 Subject: [PATCH 09/59] Use agent doc --- internal/pkg/api/handleOpAMP.go | 40 ++++++++++++++++----------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 65a17b31ac..5a15306df9 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -104,22 +104,22 @@ func (oa *OpAMPT) handleOpAMP(zlog zerolog.Logger, r *http.Request, w http.Respo Msg("received AgentToServer message from agent") // Check if Agent is "enrolled"; if it is, update it; otherwise, enroll it. - isEnrolled, err := oa.isAgentEnrolled(zlog, instanceUID.String()) + agent, err := oa.findEnrolledAgent(zlog, instanceUID.String()) if err != nil { return fmt.Errorf("failed to check if agent is enrolled: %w", err) } zlog.Debug(). - Bool("is_enrolled", isEnrolled). + Bool("is_enrolled", agent != nil). Str("agent_id", instanceUID.String()). Msg("agent enrollment status") - if !isEnrolled { - if err := oa.enrollAgent(zlog, instanceUID.String(), aToS, apiKey); err != nil { + if agent == nil { + if agent, err = oa.enrollAgent(zlog, instanceUID.String(), aToS, apiKey); err != nil { return fmt.Errorf("failed to enroll agent: %w", err) } } - if err := oa.updateAgent(zlog, instanceUID.String(), aToS); err != nil { + if err := oa.updateAgent(zlog, agent, aToS); err != nil { return fmt.Errorf("failed to update persisted Agent information: %w", err) } @@ -142,32 +142,32 @@ func (oa *OpAMPT) handleOpAMP(zlog zerolog.Logger, r *http.Request, w http.Respo return err } -func (oa OpAMPT) isAgentEnrolled(zlog zerolog.Logger, agentID string) (bool, error) { +func (oa *OpAMPT) findEnrolledAgent(zlog zerolog.Logger, agentID string) (*model.Agent, error) { ctx := context.TODO() agent, err := dl.FindAgent(ctx, oa.bulk, dl.QueryAgentByID, dl.FieldID, agentID) if errors.Is(err, dl.ErrNotFound) { - return false, nil + return nil, nil } if err != nil { - return false, fmt.Errorf("failed to find agent: %w", err) + return nil, fmt.Errorf("failed to find agent: %w", err) } if agent.Id == "" { - return false, nil + return nil, nil } - return true, nil + return &agent, nil } -func (oa OpAMPT) enrollAgent(zlog zerolog.Logger, agentID string, aToS protobufs.AgentToServer, apiKey *apikey.APIKey) error { +func (oa *OpAMPT) enrollAgent(zlog zerolog.Logger, agentID string, aToS protobufs.AgentToServer, apiKey *apikey.APIKey) (*model.Agent, error) { zlog.Debug(). Str("agentID", agentID). Msg("enrolling agent") ctx := context.TODO() rec, err := dl.FindEnrollmentAPIKey(ctx, oa.bulk, dl.QueryEnrollmentAPIKeyByID, dl.FieldAPIKeyID, apiKey.ID) if err != nil { - return fmt.Errorf("failed to find enrollment API key: %w", err) + return nil, fmt.Errorf("failed to find enrollment API key: %w", err) } now := time.Now() @@ -202,7 +202,7 @@ func (oa OpAMPT) enrollAgent(zlog zerolog.Logger, agentID string, aToS protobufs // Update local metadata if something has changed data, err := json.Marshal(meta) if err != nil { - return fmt.Errorf("failed to marshal local metadata: %w", err) + return nil, fmt.Errorf("failed to marshal local metadata: %w", err) } zlog.Debug().RawJSON("meta", data).Msg("updating local metadata") @@ -220,23 +220,21 @@ func (oa OpAMPT) enrollAgent(zlog zerolog.Logger, agentID string, aToS protobufs data, err = json.Marshal(agent) if err != nil { - return err + return nil, err } zlog.Debug(). Str("agent document", string(data)). Msg("creating .fleet-agents doc") if _, err = oa.bulk.Create(ctx, dl.FleetAgents, agentID, data, bulk.WithRefresh()); err != nil { - return err + return nil, err } - return nil + return &agent, nil } -func (oa OpAMPT) updateAgent(zlog zerolog.Logger, agentID string, aToS protobufs.AgentToServer) error { - zlog.Debug(). - Str("aToS", aToS.String()). - Msg("updating .fleet-agents doc") +func (oa *OpAMPT) updateAgent(zlog zerolog.Logger, agent *model.Agent, aToS protobufs.AgentToServer) error { + zlog.Debug().Msg("updating .fleet-agents doc") initialOpts := make([]checkin.Option, 0) @@ -251,7 +249,7 @@ func (oa OpAMPT) updateAgent(zlog zerolog.Logger, agentID string, aToS protobufs initialOpts = append(initialOpts, checkin.WithUnhealthyReason(&unhealthyReason)) } - return oa.bc.CheckIn(agentID, initialOpts...) + return oa.bc.CheckIn(agent.Id, initialOpts...) } type localMetadata struct { From 6b4407475eab88926786ec8e8a69d17e001e7a42 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 7 Jan 2026 16:02:06 +0530 Subject: [PATCH 10/59] Consolidate logic --- internal/pkg/api/handleOpAMP.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 5a15306df9..cf3700d32c 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -241,12 +241,12 @@ func (oa *OpAMPT) updateAgent(zlog zerolog.Logger, agent *model.Agent, aToS prot // Extract the health status from the health message if it exists. if aToS.Health != nil { initialOpts = append(initialOpts, checkin.WithStatus(aToS.Health.Status)) - } - // Extract the unhealthy reason from the health message if it exists. - if aToS.Health != nil && aToS.Health.LastError != "" { - unhealthyReason := []string{aToS.Health.LastError} - initialOpts = append(initialOpts, checkin.WithUnhealthyReason(&unhealthyReason)) + // Extract the unhealthy reason from the health message if it exists. + if aToS.Health.LastError != "" { + unhealthyReason := []string{aToS.Health.LastError} + initialOpts = append(initialOpts, checkin.WithUnhealthyReason(&unhealthyReason)) + } } return oa.bc.CheckIn(agent.Id, initialOpts...) From 44d6030d23e12f9a347dbcde22555c5b9ac0a7ab Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 2 Feb 2026 16:02:57 -0800 Subject: [PATCH 11/59] Add feature flag for enabling OpAMP endpoint --- internal/pkg/config/input.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/pkg/config/input.go b/internal/pkg/config/input.go index 64c341ac54..0ce4d2d93e 100644 --- a/internal/pkg/config/input.go +++ b/internal/pkg/config/input.go @@ -98,6 +98,9 @@ type ( // IgnoreCheckinPolicyID when true will ignore the agent_policy_id and policy_revision_idx attributes in checkin request bodies. // This setting restores previous behaviour where all POLICY_CHANGE actions need an explicit ack. IgnoreCheckinPolicyID bool `config:"ignore_checkin_policy_id"` + + // OpAMPEnabled when true will enable the OpAMP endpoint. + OpAMPEnabled bool `config:"opamp_enabled"` } ) From 43ed72b5e5f45d0503ae95f9620045baeddd99fd Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 2 Feb 2026 16:06:57 -0800 Subject: [PATCH 12/59] Add feature flag to reference config --- fleet-server.reference.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fleet-server.reference.yml b/fleet-server.reference.yml index a5c7c01f5d..8888cc882e 100644 --- a/fleet-server.reference.yml +++ b/fleet-server.reference.yml @@ -277,6 +277,9 @@ fleet: # // POLICY_CHANGE actions need an explicit ack if this is set. # ignore_checkin_policy_id: false # +# // enable OpAMP endpoint +# opamp_enabled: false +# # # monitor options are advanced configuration and should not be adjusted is most cases # monitor: # fetch_size: 1000 # The number of documents that each monitor may fetch at once From 81e190462d61a55b42d04a341ea49652160dd00f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 2 Feb 2026 16:13:42 -0800 Subject: [PATCH 13/59] Check feature flag before handing OpAMP requests --- internal/pkg/api/error.go | 9 +++++++++ internal/pkg/api/handleOpAMP.go | 14 ++++++++++++++ internal/pkg/server/fleet.go | 2 +- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/internal/pkg/api/error.go b/internal/pkg/api/error.go index 6ea0b8b279..d692deb9b6 100644 --- a/internal/pkg/api/error.go +++ b/internal/pkg/api/error.go @@ -514,6 +514,15 @@ func NewHTTPErrResp(err error) HTTPErrResp { Level: zerolog.InfoLevel, }, }, + { + target: ErrOpAMPDisabled, + meta: HTTPErrResp{ + StatusCode: http.StatusNotImplemented, + Error: "ErrOpAMPDisabled", + Message: "OpAMP is disabled in Fleet Server configuration", + Level: zerolog.WarnLevel, + }, + }, } for _, e := range errTable { diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index cf3700d32c..78a9cdace4 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -17,6 +17,7 @@ import ( "github.com/elastic/fleet-server/v7/internal/pkg/bulk" "github.com/elastic/fleet-server/v7/internal/pkg/cache" "github.com/elastic/fleet-server/v7/internal/pkg/checkin" + "github.com/elastic/fleet-server/v7/internal/pkg/config" "github.com/elastic/fleet-server/v7/internal/pkg/dl" "github.com/elastic/fleet-server/v7/internal/pkg/model" "github.com/gofrs/uuid/v5" @@ -31,7 +32,12 @@ const ( kOpAMPMod = "opAMP" ) +var ( + ErrOpAMPDisabled = errors.New("OpAMP endpoint is disabled") +) + type OpAMPT struct { + cfg *config.Server bulk bulk.Bulk cache cache.Cache bc *checkin.Bulk @@ -43,11 +49,13 @@ type OpAMPT struct { func NewOpAMPT( ctx context.Context, + cfg *config.Server, bulker bulk.Bulk, cache cache.Cache, bc *checkin.Bulk, ) *OpAMPT { oa := &OpAMPT{ + cfg: cfg, bulk: bulker, cache: cache, bc: bc, @@ -77,6 +85,12 @@ func (oa *OpAMPT) startTimers(ctx context.Context) { } func (oa *OpAMPT) handleOpAMP(zlog zerolog.Logger, r *http.Request, w http.ResponseWriter) error { + // Check if feature flag enabling the OpAMP endpoint is enabled. + if !oa.cfg.Features.OpAMPEnabled { + zlog.Debug().Msg("opAMP endpoint is disabled") + return ErrOpAMPDisabled + } + apiKey, err := authAPIKey(r, oa.bulk, oa.cache) if err != nil { zlog.Debug().Err(err).Msg("unauthenticated opamp request") diff --git a/internal/pkg/server/fleet.go b/internal/pkg/server/fleet.go index e8a89ecc38..2426a72b48 100644 --- a/internal/pkg/server/fleet.go +++ b/internal/pkg/server/fleet.go @@ -536,7 +536,7 @@ func (f *Fleet) runSubsystems(ctx context.Context, cfg *config.Config, g *errgro at := api.NewArtifactT(&cfg.Inputs[0].Server, bulker, f.cache) ack := api.NewAckT(&cfg.Inputs[0].Server, bulker, f.cache) st := api.NewStatusT(&cfg.Inputs[0].Server, bulker, f.cache, api.WithSelfMonitor(sm), api.WithBuildInfo(f.bi)) - oa := api.NewOpAMPT(ctx, bulker, f.cache, bc) + oa := api.NewOpAMPT(ctx, &cfg.Inputs[0].Server, bulker, f.cache, bc) ut := api.NewUploadT(&cfg.Inputs[0].Server, bulker, monCli, f.cache) // uses no-retry client for bufferless chunk upload ft := api.NewFileDeliveryT(&cfg.Inputs[0].Server, bulker, monCli, f.cache) pt := api.NewPGPRetrieverT(&cfg.Inputs[0].Server, bulker, f.cache) From f38a1bc14268d6e5df9c454401c2733bc19efece Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 2 Feb 2026 16:17:24 -0800 Subject: [PATCH 14/59] Rename feature flag --- fleet-server.reference.yml | 2 +- internal/pkg/api/handleOpAMP.go | 2 +- internal/pkg/config/input.go | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fleet-server.reference.yml b/fleet-server.reference.yml index 8888cc882e..5b0d38873e 100644 --- a/fleet-server.reference.yml +++ b/fleet-server.reference.yml @@ -278,7 +278,7 @@ fleet: # ignore_checkin_policy_id: false # # // enable OpAMP endpoint -# opamp_enabled: false +# enable_opamp: false # # # monitor options are advanced configuration and should not be adjusted is most cases # monitor: diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 78a9cdace4..0da93cd366 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -86,7 +86,7 @@ func (oa *OpAMPT) startTimers(ctx context.Context) { func (oa *OpAMPT) handleOpAMP(zlog zerolog.Logger, r *http.Request, w http.ResponseWriter) error { // Check if feature flag enabling the OpAMP endpoint is enabled. - if !oa.cfg.Features.OpAMPEnabled { + if !oa.cfg.Features.EnableOpAMP { zlog.Debug().Msg("opAMP endpoint is disabled") return ErrOpAMPDisabled } diff --git a/internal/pkg/config/input.go b/internal/pkg/config/input.go index 0ce4d2d93e..29d2debfb8 100644 --- a/internal/pkg/config/input.go +++ b/internal/pkg/config/input.go @@ -99,8 +99,8 @@ type ( // This setting restores previous behaviour where all POLICY_CHANGE actions need an explicit ack. IgnoreCheckinPolicyID bool `config:"ignore_checkin_policy_id"` - // OpAMPEnabled when true will enable the OpAMP endpoint. - OpAMPEnabled bool `config:"opamp_enabled"` + // EnableOpAMP when true will enable the OpAMP endpoint. + EnableOpAMP bool `config:"enable_opamp"` } ) From 0cf3762544480b4a46a56f4fb7a1e1ad1b6b82f4 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 3 Feb 2026 15:37:01 -0800 Subject: [PATCH 15/59] Adding unit test for feature flag --- internal/pkg/api/handleOpAMP_test.go | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 internal/pkg/api/handleOpAMP_test.go diff --git a/internal/pkg/api/handleOpAMP_test.go b/internal/pkg/api/handleOpAMP_test.go new file mode 100644 index 0000000000..8bec05ca67 --- /dev/null +++ b/internal/pkg/api/handleOpAMP_test.go @@ -0,0 +1,44 @@ +package api + +import ( + "net/http" + "testing" + + "github.com/elastic/fleet-server/v7/internal/pkg/apikey" + "github.com/elastic/fleet-server/v7/internal/pkg/config" + testlog "github.com/elastic/fleet-server/v7/internal/pkg/testing/log" + "github.com/stretchr/testify/require" +) + +func TestFeatureFlag(t *testing.T) { + cases := map[string]struct { + FeatureFlagEnabled bool + WantError error + }{ + "feature flag is disabled": { + FeatureFlagEnabled: false, + WantError: ErrOpAMPDisabled, + }, + "feature flag is enabled": { + FeatureFlagEnabled: true, + WantError: apikey.ErrNoAuthHeader, + }, + } + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + cfg := &config.Server{ + Features: config.FeatureFlags{ + EnableOpAMP: tc.FeatureFlagEnabled, + }, + } + + logger := testlog.SetLogger(t) + req := http.Request{} + var resp http.ResponseWriter + + oa := OpAMPT{cfg: cfg} + err := oa.handleOpAMP(logger, &req, resp) + require.Equal(t, tc.WantError, err) + }) + } +} From 98a9578c5501e419754ea9ec8b6c4d41fa9e93f4 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 3 Feb 2026 16:27:22 -0800 Subject: [PATCH 16/59] Address linter errors --- internal/pkg/api/handleOpAMP.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 0da93cd366..e88f79f2d6 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -128,12 +128,12 @@ func (oa *OpAMPT) handleOpAMP(zlog zerolog.Logger, r *http.Request, w http.Respo Str("agent_id", instanceUID.String()). Msg("agent enrollment status") if agent == nil { - if agent, err = oa.enrollAgent(zlog, instanceUID.String(), aToS, apiKey); err != nil { + if agent, err = oa.enrollAgent(zlog, instanceUID.String(), &aToS, apiKey); err != nil { return fmt.Errorf("failed to enroll agent: %w", err) } } - if err := oa.updateAgent(zlog, agent, aToS); err != nil { + if err := oa.updateAgent(zlog, agent, &aToS); err != nil { return fmt.Errorf("failed to update persisted Agent information: %w", err) } @@ -156,7 +156,7 @@ func (oa *OpAMPT) handleOpAMP(zlog zerolog.Logger, r *http.Request, w http.Respo return err } -func (oa *OpAMPT) findEnrolledAgent(zlog zerolog.Logger, agentID string) (*model.Agent, error) { +func (oa *OpAMPT) findEnrolledAgent(_ zerolog.Logger, agentID string) (*model.Agent, error) { ctx := context.TODO() agent, err := dl.FindAgent(ctx, oa.bulk, dl.QueryAgentByID, dl.FieldID, agentID) if errors.Is(err, dl.ErrNotFound) { @@ -174,7 +174,7 @@ func (oa *OpAMPT) findEnrolledAgent(zlog zerolog.Logger, agentID string) (*model return &agent, nil } -func (oa *OpAMPT) enrollAgent(zlog zerolog.Logger, agentID string, aToS protobufs.AgentToServer, apiKey *apikey.APIKey) (*model.Agent, error) { +func (oa *OpAMPT) enrollAgent(zlog zerolog.Logger, agentID string, aToS *protobufs.AgentToServer, apiKey *apikey.APIKey) (*model.Agent, error) { zlog.Debug(). Str("agentID", agentID). Msg("enrolling agent") @@ -247,7 +247,7 @@ func (oa *OpAMPT) enrollAgent(zlog zerolog.Logger, agentID string, aToS protobuf return &agent, nil } -func (oa *OpAMPT) updateAgent(zlog zerolog.Logger, agent *model.Agent, aToS protobufs.AgentToServer) error { +func (oa *OpAMPT) updateAgent(zlog zerolog.Logger, agent *model.Agent, aToS *protobufs.AgentToServer) error { zlog.Debug().Msg("updating .fleet-agents doc") initialOpts := make([]checkin.Option, 0) From a15d749cf7c5722c590420ed222bb6b392adbbae Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Feb 2026 08:48:07 -0800 Subject: [PATCH 17/59] Allow running specific tests with TEST_RUN env var --- testing/e2e/testdata/stand-alone-opamp.tpl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 testing/e2e/testdata/stand-alone-opamp.tpl diff --git a/testing/e2e/testdata/stand-alone-opamp.tpl b/testing/e2e/testdata/stand-alone-opamp.tpl new file mode 100644 index 0000000000..234f0c1438 --- /dev/null +++ b/testing/e2e/testdata/stand-alone-opamp.tpl @@ -0,0 +1,16 @@ +output: + elasticsearch: + hosts: {{ .Hosts }} + service_token: {{ .ServiceToken }} + +fleet.agent.id: e2e-test-id + +inputs: +- type: fleet-server + server: + feature_flags: + enable_opamp: true + +logging: + to_stderr: true + From 2c9dd2610e18f77582d0ec56eb8ecd51955d1022 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Feb 2026 12:21:19 -0800 Subject: [PATCH 18/59] Running mage check:notice --- NOTICE-fips.txt | 692 +++++++++++++++++++++++++++++++----------------- NOTICE.txt | 692 +++++++++++++++++++++++++++++++----------------- 2 files changed, 904 insertions(+), 480 deletions(-) diff --git a/NOTICE-fips.txt b/NOTICE-fips.txt index 630c415806..91736aa84a 100644 --- a/NOTICE-fips.txt +++ b/NOTICE-fips.txt @@ -2593,6 +2593,218 @@ Contents of probable licence file $GOMODCACHE/github.com/oapi-codegen/runtime@v1 limitations under the License. +-------------------------------------------------------------------------------- +Dependency : github.com/open-telemetry/opamp-go +Version: v0.22.0 +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/open-telemetry/opamp-go@v0.22.0/LICENSE: + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + -------------------------------------------------------------------------------- Dependency : github.com/pbnjay/memory Version: v0.0.0-20210728143218-7b4eea64cf58 @@ -4565,6 +4777,246 @@ Contents of probable licence file $GOMODCACHE/go.elastic.co/ecszerolog@v0.2.0/LI limitations under the License. +-------------------------------------------------------------------------------- +Dependency : go.opentelemetry.io/otel +Version: v1.39.0 +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/go.opentelemetry.io/otel@v1.39.0/LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + +-------------------------------------------------------------------------------- + +Copyright 2009 The Go Authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google LLC nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + -------------------------------------------------------------------------------- Dependency : go.uber.org/zap Version: v1.27.1 @@ -9031,246 +9483,6 @@ Contents of probable licence file $GOMODCACHE/go.opentelemetry.io/auto/sdk@v1.2. limitations under the License. --------------------------------------------------------------------------------- -Dependency : go.opentelemetry.io/otel -Version: v1.39.0 -Licence type (autodetected): Apache-2.0 --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/go.opentelemetry.io/otel@v1.39.0/LICENSE: - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. - --------------------------------------------------------------------------------- - -Copyright 2009 The Go Authors. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google LLC nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -------------------------------------------------------------------------------- Dependency : go.opentelemetry.io/otel/metric Version: v1.39.0 diff --git a/NOTICE.txt b/NOTICE.txt index cc1c4fa0c3..a3c8e2e947 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -2593,6 +2593,218 @@ Contents of probable licence file $GOMODCACHE/github.com/oapi-codegen/runtime@v1 limitations under the License. +-------------------------------------------------------------------------------- +Dependency : github.com/open-telemetry/opamp-go +Version: v0.22.0 +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/open-telemetry/opamp-go@v0.22.0/LICENSE: + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + -------------------------------------------------------------------------------- Dependency : github.com/pbnjay/memory Version: v0.0.0-20210728143218-7b4eea64cf58 @@ -4565,6 +4777,246 @@ Contents of probable licence file $GOMODCACHE/go.elastic.co/ecszerolog@v0.2.0/LI limitations under the License. +-------------------------------------------------------------------------------- +Dependency : go.opentelemetry.io/otel +Version: v1.39.0 +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/go.opentelemetry.io/otel@v1.39.0/LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + +-------------------------------------------------------------------------------- + +Copyright 2009 The Go Authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google LLC nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + -------------------------------------------------------------------------------- Dependency : go.uber.org/zap Version: v1.27.1 @@ -9061,246 +9513,6 @@ Contents of probable licence file $GOMODCACHE/go.opentelemetry.io/auto/sdk@v1.2. limitations under the License. --------------------------------------------------------------------------------- -Dependency : go.opentelemetry.io/otel -Version: v1.39.0 -Licence type (autodetected): Apache-2.0 --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/go.opentelemetry.io/otel@v1.39.0/LICENSE: - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. - --------------------------------------------------------------------------------- - -Copyright 2009 The Go Authors. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google LLC nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -------------------------------------------------------------------------------- Dependency : go.opentelemetry.io/otel/metric Version: v1.39.0 From f5a0b4a47b0d2c899f66eab0f307605f82cfa11a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Feb 2026 12:45:08 -0800 Subject: [PATCH 19/59] Running mage check:headers --- internal/pkg/api/handleOpAMP_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/pkg/api/handleOpAMP_test.go b/internal/pkg/api/handleOpAMP_test.go index 8bec05ca67..457f7e8a57 100644 --- a/internal/pkg/api/handleOpAMP_test.go +++ b/internal/pkg/api/handleOpAMP_test.go @@ -1,3 +1,7 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + package api import ( From 0c43b1bbb57e8b08229fe104c52e01bf99c1aff4 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Feb 2026 16:02:05 -0800 Subject: [PATCH 20/59] Removing irrelevant file --- testing/e2e/testdata/stand-alone-opamp.tpl | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 testing/e2e/testdata/stand-alone-opamp.tpl diff --git a/testing/e2e/testdata/stand-alone-opamp.tpl b/testing/e2e/testdata/stand-alone-opamp.tpl deleted file mode 100644 index 234f0c1438..0000000000 --- a/testing/e2e/testdata/stand-alone-opamp.tpl +++ /dev/null @@ -1,16 +0,0 @@ -output: - elasticsearch: - hosts: {{ .Hosts }} - service_token: {{ .ServiceToken }} - -fleet.agent.id: e2e-test-id - -inputs: -- type: fleet-server - server: - feature_flags: - enable_opamp: true - -logging: - to_stderr: true - From 51f3dff0bed60582a55ac79480b2d20cbba50d7d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Feb 2026 18:14:26 -0800 Subject: [PATCH 21/59] WIP: Reimplement using opamp-go server package --- NOTICE-fips.txt | 32 ++++++ NOTICE.txt | 32 ++++++ go.mod | 1 + go.sum | 2 + internal/pkg/api/api.go | 10 -- internal/pkg/api/handleOpAMP.go | 172 ++++++++++++++++++++------------ internal/pkg/api/openapi.gen.go | 32 ------ internal/pkg/api/server.go | 22 +++- internal/pkg/server/fleet.go | 4 + model/openapi.yml | 16 +-- pkg/api/client.gen.go | 93 ----------------- 11 files changed, 207 insertions(+), 209 deletions(-) diff --git a/NOTICE-fips.txt b/NOTICE-fips.txt index 91736aa84a..2f1260e545 100644 --- a/NOTICE-fips.txt +++ b/NOTICE-fips.txt @@ -7235,6 +7235,38 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- +Dependency : github.com/gorilla/websocket +Version: v1.5.3 +Licence type (autodetected): BSD-2-Clause +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/gorilla/websocket@v1.5.3/LICENSE: + +Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + -------------------------------------------------------------------------------- Dependency : github.com/inconshreveable/mousetrap Version: v1.1.0 diff --git a/NOTICE.txt b/NOTICE.txt index a3c8e2e947..51a06aef87 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -7235,6 +7235,38 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- +Dependency : github.com/gorilla/websocket +Version: v1.5.3 +Licence type (autodetected): BSD-2-Clause +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/gorilla/websocket@v1.5.3/LICENSE: + +Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + -------------------------------------------------------------------------------- Dependency : github.com/inconshreveable/mousetrap Version: v1.1.0 diff --git a/go.mod b/go.mod index f28567f143..25bbaae284 100644 --- a/go.mod +++ b/go.mod @@ -65,6 +65,7 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect diff --git a/go.sum b/go.sum index 327c00e341..d50b67ff23 100644 --- a/go.sum +++ b/go.sum @@ -83,6 +83,8 @@ github.com/google/pprof v0.0.0-20230426061923-93006964c1fc h1:AGDHt781oIcL4EFk7c github.com/google/pprof v0.0.0-20230426061923-93006964c1fc/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= diff --git a/internal/pkg/api/api.go b/internal/pkg/api/api.go index f983d73c03..998c60f5f8 100644 --- a/internal/pkg/api/api.go +++ b/internal/pkg/api/api.go @@ -237,13 +237,3 @@ func (a *apiServer) Status(w http.ResponseWriter, r *http.Request, params Status ErrorResp(w, r, err) } } -func (a *apiServer) OpAMP(w http.ResponseWriter, r *http.Request) { - zlog := hlog.FromRequest(r).With(). - Str("mod", kOpAMPMod). - Logger() - w.Header().Set("Content-Type", "application/x-protobuf") - if err := a.oa.handleOpAMP(zlog, r, w); err != nil { - cntOpAMP.IncError(err) - ErrorResp(w, r, err) - } -} diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index e88f79f2d6..c13ea38cf6 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -9,7 +9,6 @@ import ( "encoding/json" "errors" "fmt" - "io" "net/http" "time" @@ -22,10 +21,12 @@ import ( "github.com/elastic/fleet-server/v7/internal/pkg/model" "github.com/gofrs/uuid/v5" "github.com/open-telemetry/opamp-go/protobufs" + oaServer "github.com/open-telemetry/opamp-go/server" + "github.com/open-telemetry/opamp-go/server/types" "github.com/rs/zerolog" + "github.com/rs/zerolog/hlog" "go.opentelemetry.io/otel/attribute" semconv "go.opentelemetry.io/otel/semconv/v1.37.0" - "google.golang.org/protobuf/proto" ) const ( @@ -42,9 +43,12 @@ type OpAMPT struct { cache cache.Cache bc *checkin.Bulk - agentMetas map[string]localMetadata + srv oaServer.OpAMPServer + handler oaServer.HTTPHandlerFunc + connCtx oaServer.ConnContext - flags uint64 + agentMetas map[string]localMetadata + flags uint64 } func NewOpAMPT( @@ -59,6 +63,7 @@ func NewOpAMPT( bulk: bulker, cache: cache, bc: bc, + srv: oaServer.New(nil), agentMetas: map[string]localMetadata{}, flags: uint64(protobufs.ServerToAgentFlags_ServerToAgentFlags_ReportAvailableComponents), } @@ -67,6 +72,45 @@ func NewOpAMPT( return oa } +func (oa *OpAMPT) Init() error { + settings := oaServer.Settings{ + Callbacks: types.Callbacks{ + OnConnecting: func(request *http.Request) types.ConnectionResponse { + zlog := hlog.FromRequest(request).With(). + Str("mod", kOpAMPMod). + Logger() + + apiKey, err := authAPIKey(request, oa.bulk, oa.cache) + if err != nil { + zlog.Warn().Err(err).Msg("unauthenticated opamp request") + return types.ConnectionResponse{ + Accept: false, + HTTPStatusCode: http.StatusUnauthorized, + HTTPResponseHeader: map[string]string{ + "Content-Type": "application/x-protobuf", + }, + } + } + + return types.ConnectionResponse{ + ConnectionCallbacks: types.ConnectionCallbacks{ + OnMessage: oa.handleMessage(zlog, apiKey), + }, + } + }, + }, + } + + handler, connCtx, err := oa.srv.Attach(settings) + if err != nil { + return fmt.Errorf("failed to attach opAMP server: %w", err) + } + + oa.handler = handler + oa.connCtx = connCtx + return nil +} + func (oa *OpAMPT) startTimers(ctx context.Context) { zerolog.Ctx(ctx).Debug().Msg("starting opAMP timers") ticker := time.NewTicker(time.Minute) @@ -84,76 +128,74 @@ func (oa *OpAMPT) startTimers(ctx context.Context) { } } -func (oa *OpAMPT) handleOpAMP(zlog zerolog.Logger, r *http.Request, w http.ResponseWriter) error { - // Check if feature flag enabling the OpAMP endpoint is enabled. - if !oa.cfg.Features.EnableOpAMP { - zlog.Debug().Msg("opAMP endpoint is disabled") - return ErrOpAMPDisabled - } - - apiKey, err := authAPIKey(r, oa.bulk, oa.cache) - if err != nil { - zlog.Debug().Err(err).Msg("unauthenticated opamp request") - return err - } - - body, err := io.ReadAll(r.Body) - if err != nil { - return &BadRequestErr{msg: "failed to read AgentToServer request body"} - } - defer r.Body.Close() +func (oa *OpAMPT) Enabled() bool { + return oa.cfg.Features.EnableOpAMP +} - var aToS protobufs.AgentToServer - if err := proto.Unmarshal(body, &aToS); err != nil { - return &BadRequestErr{msg: "failed to unmarshal AgentToServer message"} - } +func (oa *OpAMPT) handleMessage(zlog zerolog.Logger, apiKey *apikey.APIKey) func(ctx context.Context, conn types.Connection, message *protobufs.AgentToServer) *protobufs.ServerToAgent { + return func(ctx context.Context, conn types.Connection, message *protobufs.AgentToServer) *protobufs.ServerToAgent { + instanceUID, err := uuid.FromBytes(message.InstanceUid) + if err != nil { + return &protobufs.ServerToAgent{ + ErrorResponse: &protobufs.ServerErrorResponse{ + Type: protobufs.ServerErrorResponseType_ServerErrorResponseType_BadRequest, + ErrorMessage: "failed to parse instance_uid from AgentToServer message", + }, + } + } - instanceUID, err := uuid.FromBytes(aToS.InstanceUid) - if err != nil { - return &BadRequestErr{msg: "failed to parse instance_uid from AgentToServer message"} - } - zlog.Debug(). - Str("instance_uid", instanceUID.String()). - Str("aToS", aToS.String()). - Msg("received AgentToServer message from agent") + zlog.Debug(). + Str("instance_uid", instanceUID.String()). + Str("aToS", message.String()). + Msg("received AgentToServer message from agent") + + // Check if Agent is "enrolled"; if it is, update it; otherwise, enroll it. + agent, err := oa.findEnrolledAgent(zlog, instanceUID.String()) + if err != nil { + return &protobufs.ServerToAgent{ + ErrorResponse: &protobufs.ServerErrorResponse{ + Type: protobufs.ServerErrorResponseType_ServerErrorResponseType_Unavailable, + ErrorMessage: fmt.Errorf("failed to check if agent is enrolled: %w", err).Error(), + }, + } + } - // Check if Agent is "enrolled"; if it is, update it; otherwise, enroll it. - agent, err := oa.findEnrolledAgent(zlog, instanceUID.String()) - if err != nil { - return fmt.Errorf("failed to check if agent is enrolled: %w", err) - } + zlog.Debug(). + Bool("is_enrolled", agent != nil). + Str("agent_id", instanceUID.String()). + Msg("agent enrollment status") + + if agent == nil { + if agent, err = oa.enrollAgent(zlog, instanceUID.String(), message, apiKey); err != nil { + return &protobufs.ServerToAgent{ + ErrorResponse: &protobufs.ServerErrorResponse{ + Type: protobufs.ServerErrorResponseType_ServerErrorResponseType_Unavailable, + ErrorMessage: fmt.Errorf("failed to enroll agent: %w", err).Error(), + }, + } + } + } - zlog.Debug(). - Bool("is_enrolled", agent != nil). - Str("agent_id", instanceUID.String()). - Msg("agent enrollment status") - if agent == nil { - if agent, err = oa.enrollAgent(zlog, instanceUID.String(), &aToS, apiKey); err != nil { - return fmt.Errorf("failed to enroll agent: %w", err) + if err := oa.updateAgent(zlog, agent, message); err != nil { + return &protobufs.ServerToAgent{ + ErrorResponse: &protobufs.ServerErrorResponse{ + Type: protobufs.ServerErrorResponseType_ServerErrorResponseType_Unavailable, + ErrorMessage: fmt.Errorf("failed to update persisted Agent information: %w", err).Error(), + }, + } } - } - if err := oa.updateAgent(zlog, agent, &aToS); err != nil { - return fmt.Errorf("failed to update persisted Agent information: %w", err) - } + sToA := protobufs.ServerToAgent{ + Flags: oa.flags, + } - sToA := protobufs.ServerToAgent{ - Flags: oa.flags, - } + // Reset flags; timer will set them again + zlog.Debug().Msg("resetting flags") + oa.flags = 0 - resp, err := proto.Marshal(&sToA) - if err != nil { - return fmt.Errorf("failed to marshal ServerToAgent response body: %w", err) + zlog.Debug().Str("resp", sToA.String()).Msg("sending ServerToAgent response") + return &sToA } - - zlog.Debug().Str("resp", sToA.String()).Msg("sending ServerToAgent response") - _, err = w.Write(resp) - - // Reset flags; timer will set them again - zlog.Debug().Msg("resetting flags") - oa.flags = 0 - - return err } func (oa *OpAMPT) findEnrolledAgent(_ zerolog.Logger, agentID string) (*model.Agent, error) { diff --git a/internal/pkg/api/openapi.gen.go b/internal/pkg/api/openapi.gen.go index 6900b85918..bd1b73088d 100644 --- a/internal/pkg/api/openapi.gen.go +++ b/internal/pkg/api/openapi.gen.go @@ -1843,9 +1843,6 @@ type ServerInterface interface { // (GET /api/status) Status(w http.ResponseWriter, r *http.Request, params StatusParams) - // Receive OpAMP AgentToServer messages - // (POST /v1/opamp) - OpAMP(w http.ResponseWriter, r *http.Request) } // Unimplemented server implementation that returns http.StatusNotImplemented for each endpoint. @@ -1913,12 +1910,6 @@ func (_ Unimplemented) Status(w http.ResponseWriter, r *http.Request, params Sta w.WriteHeader(http.StatusNotImplemented) } -// Receive OpAMP AgentToServer messages -// (POST /v1/opamp) -func (_ Unimplemented) OpAMP(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - // ServerInterfaceWrapper converts contexts to parameters. type ServerInterfaceWrapper struct { Handler ServerInterface @@ -2833,26 +2824,6 @@ func (siw *ServerInterfaceWrapper) Status(w http.ResponseWriter, r *http.Request handler.ServeHTTP(w, r) } -// OpAMP operation middleware -func (siw *ServerInterfaceWrapper) OpAMP(w http.ResponseWriter, r *http.Request) { - - ctx := r.Context() - - ctx = context.WithValue(ctx, AgentApiKeyScopes, []string{}) - - r = r.WithContext(ctx) - - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.OpAMP(w, r) - })) - - for _, middleware := range siw.HandlerMiddlewares { - handler = middleware(handler) - } - - handler.ServeHTTP(w, r) -} - type UnescapedCookieParamError struct { ParamName string Err error @@ -2999,9 +2970,6 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/api/status", wrapper.Status) }) - r.Group(func(r chi.Router) { - r.Post(options.BaseURL+"/v1/opamp", wrapper.OpAMP) - }) return r } diff --git a/internal/pkg/api/server.go b/internal/pkg/api/server.go index 2c10d33415..e3f8df8a74 100644 --- a/internal/pkg/api/server.go +++ b/internal/pkg/api/server.go @@ -15,6 +15,7 @@ import ( "github.com/elastic/elastic-agent-libs/logp" "github.com/elastic/elastic-agent-libs/transport/tlscommon" + "github.com/go-chi/chi/v5" "github.com/rs/zerolog" @@ -41,10 +42,17 @@ func NewServer(addr string, cfg *config.Server, opts ...APIOpt) *server { for _, opt := range opts { opt(a) } + + handler := newRouter(&cfg.Limits, a, a.tracer) + + // Add OpAMP route handler to router if OpAMP feature is enabled. + if a.oa != nil && a.oa.Enabled() { + handler = addOpAMPRouteHandler(handler, a.oa, &cfg.Limits) + } return &server{ addr: addr, cfg: cfg, - handler: newRouter(&cfg.Limits, a, a.tracer), + handler: handler, logger: zap.NewStub("api-server"), } } @@ -134,6 +142,18 @@ func (s *server) Run(ctx context.Context) error { return nil } +func addOpAMPRouteHandler(existingHandler http.Handler, oa *OpAMPT, limits *config.ServerLimits) http.Handler { + r := chi.NewRouter() + + opAMPHandler := oa.handler + r.HandleFunc("/v1/opamp", http.HandlerFunc(opAMPHandler)) + + // Handle existing routes + r.Mount("/", existingHandler) + + return r +} + func getDiagConnFunc(ctx context.Context) func(c net.Conn, s http.ConnState) { return func(c net.Conn, s http.ConnState) { if c == nil { diff --git a/internal/pkg/server/fleet.go b/internal/pkg/server/fleet.go index 2426a72b48..186d89a71e 100644 --- a/internal/pkg/server/fleet.go +++ b/internal/pkg/server/fleet.go @@ -542,6 +542,10 @@ func (f *Fleet) runSubsystems(ctx context.Context, cfg *config.Config, g *errgro pt := api.NewPGPRetrieverT(&cfg.Inputs[0].Server, bulker, f.cache) auditT := api.NewAuditT(&cfg.Inputs[0].Server, bulker, f.cache) + if err := oa.Init(); err != nil { + return fmt.Errorf("failed to initialize opamp: %w", err) + } + for _, endpoint := range (&cfg.Inputs[0].Server).BindEndpoints() { apiServer := api.NewServer(endpoint, &cfg.Inputs[0].Server, api.WithCheckin(ct), diff --git a/model/openapi.yml b/model/openapi.yml index d5ef81bcd0..a8a0d2930d 100644 --- a/model/openapi.yml +++ b/model/openapi.yml @@ -2054,11 +2054,11 @@ paths: $ref: "#/components/responses/internalServerError" "503": $ref: "#/components/responses/unavailable" - /v1/opamp: - post: - operationId: opAMP - summary: Receive OpAMP AgentToServer messages - security: - - agentApiKey: [] - requestBody: - required: true \ No newline at end of file +# /v1/opamp: +# post: +# x-oapi-codegen-skip: true # Only document the API; don't generate code for it. +# summary: Receive OpAMP AgentToServer messages +# security: +# - agentApiKey: [] +# requestBody: +# required: true diff --git a/pkg/api/client.gen.go b/pkg/api/client.gen.go index 337f1247ad..d0c44e036d 100644 --- a/pkg/api/client.gen.go +++ b/pkg/api/client.gen.go @@ -137,9 +137,6 @@ type ClientInterface interface { // Status request Status(ctx context.Context, params *StatusParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // OpAMPWithBody request with any body - OpAMPWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) } func (c *Client) GetPGPKey(ctx context.Context, major int, minor int, patch int, params *GetPGPKeyParams, reqEditors ...RequestEditorFn) (*http.Response, error) { @@ -346,18 +343,6 @@ func (c *Client) Status(ctx context.Context, params *StatusParams, reqEditors .. return c.Client.Do(req) } -func (c *Client) OpAMPWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewOpAMPRequestWithBody(c.Server, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - // NewGetPGPKeyRequest generates requests for GetPGPKey func NewGetPGPKeyRequest(server string, major int, minor int, patch int, params *GetPGPKeyParams) (*http.Request, error) { var err error @@ -1143,35 +1128,6 @@ func NewStatusRequest(server string, params *StatusParams) (*http.Request, error return req, nil } -// NewOpAMPRequestWithBody generates requests for OpAMP with any type of body -func NewOpAMPRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/opamp") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("POST", queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { for _, r := range c.RequestEditors { if err := r(ctx, req); err != nil { @@ -1259,9 +1215,6 @@ type ClientWithResponsesInterface interface { // StatusWithResponse request StatusWithResponse(ctx context.Context, params *StatusParams, reqEditors ...RequestEditorFn) (*StatusResponse, error) - - // OpAMPWithBodyWithResponse request with any body - OpAMPWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*OpAMPResponse, error) } type GetPGPKeyResponse struct { @@ -1560,27 +1513,6 @@ func (r StatusResponse) StatusCode() int { return 0 } -type OpAMPResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r OpAMPResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r OpAMPResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - // GetPGPKeyWithResponse request returning *GetPGPKeyResponse func (c *ClientWithResponses) GetPGPKeyWithResponse(ctx context.Context, major int, minor int, patch int, params *GetPGPKeyParams, reqEditors ...RequestEditorFn) (*GetPGPKeyResponse, error) { rsp, err := c.GetPGPKey(ctx, major, minor, patch, params, reqEditors...) @@ -1728,15 +1660,6 @@ func (c *ClientWithResponses) StatusWithResponse(ctx context.Context, params *St return ParseStatusResponse(rsp) } -// OpAMPWithBodyWithResponse request with arbitrary body returning *OpAMPResponse -func (c *ClientWithResponses) OpAMPWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*OpAMPResponse, error) { - rsp, err := c.OpAMPWithBody(ctx, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseOpAMPResponse(rsp) -} - // ParseGetPGPKeyResponse parses an HTTP response from a GetPGPKeyWithResponse call func ParseGetPGPKeyResponse(rsp *http.Response) (*GetPGPKeyResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -2391,19 +2314,3 @@ func ParseStatusResponse(rsp *http.Response) (*StatusResponse, error) { return response, nil } - -// ParseOpAMPResponse parses an HTTP response from a OpAMPWithResponse call -func ParseOpAMPResponse(rsp *http.Response) (*OpAMPResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &OpAMPResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} From 41da74bb693357e58483de66d7e95d60d27f8692 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Feb 2026 18:24:28 -0800 Subject: [PATCH 22/59] Update spec --- model/oapi-cfg.yml | 3 +++ model/openapi.yml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/model/oapi-cfg.yml b/model/oapi-cfg.yml index c877e4c994..cdce661ff8 100644 --- a/model/oapi-cfg.yml +++ b/model/oapi-cfg.yml @@ -5,3 +5,6 @@ generate: chi-server: true # strict-server: true # We need to figure out where/how to handle auth if we want to use strict generation output: ./internal/pkg/api/openapi.gen.go +output-options: + exclude-operation-ids: + - opamp \ No newline at end of file diff --git a/model/openapi.yml b/model/openapi.yml index a8a0d2930d..2de7c44704 100644 --- a/model/openapi.yml +++ b/model/openapi.yml @@ -2056,7 +2056,7 @@ paths: $ref: "#/components/responses/unavailable" # /v1/opamp: # post: -# x-oapi-codegen-skip: true # Only document the API; don't generate code for it. +# operationId: opamp # summary: Receive OpAMP AgentToServer messages # security: # - agentApiKey: [] From 4b858d5f980916e98613d8d9e63e8560be7c5e24 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Feb 2026 10:26:51 -0800 Subject: [PATCH 23/59] Add link to OpAMP spec --- model/openapi.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/model/openapi.yml b/model/openapi.yml index 2de7c44704..c7a7e4bc8f 100644 --- a/model/openapi.yml +++ b/model/openapi.yml @@ -2058,6 +2058,9 @@ paths: # post: # operationId: opamp # summary: Receive OpAMP AgentToServer messages +# description: | +# Receive OpAMP AgentToServer messages and respond with ServerToAgent message, +# as described in https://opentelemetry.io/docs/specs/opamp/ # security: # - agentApiKey: [] # requestBody: From 3715cfe73eb085155b908efae8e0cd2744718e1d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Feb 2026 10:30:14 -0800 Subject: [PATCH 24/59] Move OpAMP documentation to separate file --- docs/developers-guide.md | 56 ---------------------------------------- docs/opamp.md | 56 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 56 deletions(-) create mode 100644 docs/opamp.md diff --git a/docs/developers-guide.md b/docs/developers-guide.md index ae56be6104..b1d85768ec 100644 --- a/docs/developers-guide.md +++ b/docs/developers-guide.md @@ -420,59 +420,3 @@ For more advanced scenario you can build a custom docker image that you could us make -C dev-tools/cloud build-and-push-cloud-image ``` -## OpAMP - -This section describes how to connect a OpenTelemetry Collector instance to Fleet Server over OpAMP. - -1. Create a deployment in Elastic Cloud. Integrations Server is not needed as we will instead be - using the Fleet Server instance built from this repository so it can "speak" OpAMP to the OpenTelemetry - Collector. -2. Create an Elasticsearch service account token using Kibana > Dev Tools > Console. - ``` - POST /_security/service/elastic/fleet-server/credential/token/opamp - ``` -3. Create a `fleet-server.dev.yml` configuration file as described in https://github.com/elastic/kibana/blob/main/x-pack/platform/plugins/shared/fleet/dev_docs/local_setup/developing_kibana_and_fleet_server.md. -4. Build the Fleet Server binary for your platform. - ``` - PLATFORMS=darwin/arm64 mage build:binary - ``` -5. Run the Fleet Server binary with the above configuration. - ``` - ./build/binaries/fleet-server-9.4.0-darwin-aarch64/fleet-server -c fleet-server.dev.yml - ``` -6. Create a new policy in Fleet. Copy the enrollment token for that policy. -7. Create OpenTelemetry Collector configuration for connecting to the Fleet Server instance and save it as `otel-opamp.yaml`. - ```yaml - receivers: - otlp: - protocols: - grpc: - endpoint: 0.0.0.0:4317 - - exporters: - debug: - verbosity: detailed - - extensions: - opamp: - server: - http: - endpoint: http://localhost:8220/v1/opamp - tls: - insecure: true - headers: - Authorization: ApiKey ${env:FLEET_ENROLLMENT_TOKEN} - instance_uid: "019b8d7a-2da8-7657-b52d-492a9de33319" - - service: - pipelines: - logs: - receivers: [otlp] - exporters: [debug] - extensions: [opamp] - ``` -7. Download and extract an OpenTelemetry Collector Contrib release for your platform from https://github.com/open-telemetry/opentelemetry-collector-releases/releases -8. Run the OpenTelemetry Collector with the above configuration. - ``` - FLEET_ENROLLMENT_TOKEN= ./otelcol-contrib --config ./otel-opamp.yaml - ``` diff --git a/docs/opamp.md b/docs/opamp.md new file mode 100644 index 0000000000..4e6f2e471c --- /dev/null +++ b/docs/opamp.md @@ -0,0 +1,56 @@ +# OpAMP + +This section describes how to connect a OpenTelemetry Collector instance to Fleet Server over OpAMP. + +1. Create a deployment in Elastic Cloud. Integrations Server is not needed as we will instead be + using the Fleet Server instance built from this repository so it can "speak" OpAMP to the OpenTelemetry + Collector. +2. Create an Elasticsearch service account token using Kibana > Dev Tools > Console. + ``` + POST /_security/service/elastic/fleet-server/credential/token/opamp + ``` +3. Create a `fleet-server.dev.yml` configuration file as described in https://github.com/elastic/kibana/blob/main/x-pack/platform/plugins/shared/fleet/dev_docs/local_setup/developing_kibana_and_fleet_server.md. +4. Build the Fleet Server binary for your platform. + ``` + PLATFORMS=darwin/arm64 mage build:binary + ``` +5. Run the Fleet Server binary with the above configuration. + ``` + ./build/binaries/fleet-server-9.4.0-darwin-aarch64/fleet-server -c fleet-server.dev.yml + ``` +6. Create a new policy in Fleet. Copy the enrollment token for that policy. +7. Create OpenTelemetry Collector configuration for connecting to the Fleet Server instance and save it as `otel-opamp.yaml`. + ```yaml + receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + + exporters: + debug: + verbosity: detailed + + extensions: + opamp: + server: + http: + endpoint: http://localhost:8220/v1/opamp + tls: + insecure: true + headers: + Authorization: ApiKey ${env:FLEET_ENROLLMENT_TOKEN} + instance_uid: "019b8d7a-2da8-7657-b52d-492a9de33319" + + service: + pipelines: + logs: + receivers: [otlp] + exporters: [debug] + extensions: [opamp] + ``` +7. Download and extract an OpenTelemetry Collector Contrib release for your platform from https://github.com/open-telemetry/opentelemetry-collector-releases/releases +8. Run the OpenTelemetry Collector with the above configuration. + ``` + FLEET_ENROLLMENT_TOKEN= ./otelcol-contrib --config ./otel-opamp.yaml + ``` From 1e767567f812502cbf5d5b8f01ab7c0fba63b45d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Feb 2026 10:39:20 -0800 Subject: [PATCH 25/59] Remove timer and flags --- internal/pkg/api/handleOpAMP.go | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index c13ea38cf6..b961fd75bd 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -48,7 +48,6 @@ type OpAMPT struct { connCtx oaServer.ConnContext agentMetas map[string]localMetadata - flags uint64 } func NewOpAMPT( @@ -65,10 +64,8 @@ func NewOpAMPT( bc: bc, srv: oaServer.New(nil), agentMetas: map[string]localMetadata{}, - flags: uint64(protobufs.ServerToAgentFlags_ServerToAgentFlags_ReportAvailableComponents), } - go oa.startTimers(ctx) return oa } @@ -111,23 +108,6 @@ func (oa *OpAMPT) Init() error { return nil } -func (oa *OpAMPT) startTimers(ctx context.Context) { - zerolog.Ctx(ctx).Debug().Msg("starting opAMP timers") - ticker := time.NewTicker(time.Minute) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - zerolog.Ctx(ctx).Debug().Msg("stopping opAMP timers") - return - case <-ticker.C: - zerolog.Ctx(ctx).Debug().Msg("opAMP timer tick; setting flags") - oa.flags = uint64(protobufs.ServerToAgentFlags_ServerToAgentFlags_ReportAvailableComponents) - } - } -} - func (oa *OpAMPT) Enabled() bool { return oa.cfg.Features.EnableOpAMP } @@ -185,13 +165,8 @@ func (oa *OpAMPT) handleMessage(zlog zerolog.Logger, apiKey *apikey.APIKey) func } } - sToA := protobufs.ServerToAgent{ - Flags: oa.flags, - } - - // Reset flags; timer will set them again - zlog.Debug().Msg("resetting flags") - oa.flags = 0 + // Empty message for now since we're only using OpAMP for monitoring. + sToA := protobufs.ServerToAgent{} zlog.Debug().Str("resp", sToA.String()).Msg("sending ServerToAgent response") return &sToA From f06da19a58715aaa5522a7c6c04a474dccd1e58f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Feb 2026 10:46:22 -0800 Subject: [PATCH 26/59] Return instanceUID in response --- internal/pkg/api/handleOpAMP.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index b961fd75bd..4df689f42e 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -166,7 +166,9 @@ func (oa *OpAMPT) handleMessage(zlog zerolog.Logger, apiKey *apikey.APIKey) func } // Empty message for now since we're only using OpAMP for monitoring. - sToA := protobufs.ServerToAgent{} + sToA := protobufs.ServerToAgent{ + InstanceUid: instanceUID.Bytes(), + } zlog.Debug().Str("resp", sToA.String()).Msg("sending ServerToAgent response") return &sToA From a05182ad558202896787d13b44449644f92fac85 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Feb 2026 10:48:30 -0800 Subject: [PATCH 27/59] Pass context associated with request --- internal/pkg/api/handleOpAMP.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 4df689f42e..b6e40d88dd 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -130,7 +130,7 @@ func (oa *OpAMPT) handleMessage(zlog zerolog.Logger, apiKey *apikey.APIKey) func Msg("received AgentToServer message from agent") // Check if Agent is "enrolled"; if it is, update it; otherwise, enroll it. - agent, err := oa.findEnrolledAgent(zlog, instanceUID.String()) + agent, err := oa.findEnrolledAgent(ctx, zlog, instanceUID.String()) if err != nil { return &protobufs.ServerToAgent{ ErrorResponse: &protobufs.ServerErrorResponse{ @@ -175,8 +175,7 @@ func (oa *OpAMPT) handleMessage(zlog zerolog.Logger, apiKey *apikey.APIKey) func } } -func (oa *OpAMPT) findEnrolledAgent(_ zerolog.Logger, agentID string) (*model.Agent, error) { - ctx := context.TODO() +func (oa *OpAMPT) findEnrolledAgent(ctx context.Context, _ zerolog.Logger, agentID string) (*model.Agent, error) { agent, err := dl.FindAgent(ctx, oa.bulk, dl.QueryAgentByID, dl.FieldID, agentID) if errors.Is(err, dl.ErrNotFound) { return nil, nil From c87b075b18cd7381b0a564eff28b47dfcbea6b2e Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Feb 2026 10:56:44 -0800 Subject: [PATCH 28/59] Fix env var name in doc --- docs/opamp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/opamp.md b/docs/opamp.md index 4e6f2e471c..adf7ff0e8d 100644 --- a/docs/opamp.md +++ b/docs/opamp.md @@ -52,5 +52,5 @@ This section describes how to connect a OpenTelemetry Collector instance to Flee 7. Download and extract an OpenTelemetry Collector Contrib release for your platform from https://github.com/open-telemetry/opentelemetry-collector-releases/releases 8. Run the OpenTelemetry Collector with the above configuration. ``` - FLEET_ENROLLMENT_TOKEN= ./otelcol-contrib --config ./otel-opamp.yaml + API_KEY= ./otelcol-contrib --config ./otel-opamp.yaml ``` From eda38547f038af12994c792ffd626b5b2157a197 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Feb 2026 10:58:14 -0800 Subject: [PATCH 29/59] Remove error that's no longer needed --- internal/pkg/api/error.go | 9 --------- internal/pkg/api/handleOpAMP.go | 4 ---- internal/pkg/api/handleOpAMP_test.go | 2 ++ 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/internal/pkg/api/error.go b/internal/pkg/api/error.go index d692deb9b6..6ea0b8b279 100644 --- a/internal/pkg/api/error.go +++ b/internal/pkg/api/error.go @@ -514,15 +514,6 @@ func NewHTTPErrResp(err error) HTTPErrResp { Level: zerolog.InfoLevel, }, }, - { - target: ErrOpAMPDisabled, - meta: HTTPErrResp{ - StatusCode: http.StatusNotImplemented, - Error: "ErrOpAMPDisabled", - Message: "OpAMP is disabled in Fleet Server configuration", - Level: zerolog.WarnLevel, - }, - }, } for _, e := range errTable { diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index b6e40d88dd..20c63e3fe0 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -33,10 +33,6 @@ const ( kOpAMPMod = "opAMP" ) -var ( - ErrOpAMPDisabled = errors.New("OpAMP endpoint is disabled") -) - type OpAMPT struct { cfg *config.Server bulk bulk.Bulk diff --git a/internal/pkg/api/handleOpAMP_test.go b/internal/pkg/api/handleOpAMP_test.go index 457f7e8a57..21b467702e 100644 --- a/internal/pkg/api/handleOpAMP_test.go +++ b/internal/pkg/api/handleOpAMP_test.go @@ -14,6 +14,8 @@ import ( "github.com/stretchr/testify/require" ) +// TODO: move to route setup where route itself is not created when feature +// flag is disabled. func TestFeatureFlag(t *testing.T) { cases := map[string]struct { FeatureFlagEnabled bool From 84ad4943d41accc7ae964e4099fb8a91b06886a6 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Feb 2026 11:06:19 -0800 Subject: [PATCH 30/59] Set Accept: true on authenticated request --- internal/pkg/api/handleOpAMP.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 20c63e3fe0..0ca8a96cd9 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -86,6 +86,7 @@ func (oa *OpAMPT) Init() error { } return types.ConnectionResponse{ + Accept: true, ConnectionCallbacks: types.ConnectionCallbacks{ OnMessage: oa.handleMessage(zlog, apiKey), }, From da6c8949c22f852ada378b91724ff3ff274e1506 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Feb 2026 11:27:43 -0800 Subject: [PATCH 31/59] Setup some more connection callbacks for debugging --- internal/pkg/api/handleOpAMP.go | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 0ca8a96cd9..e8f120264e 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -85,11 +85,23 @@ func (oa *OpAMPT) Init() error { } } - return types.ConnectionResponse{ - Accept: true, - ConnectionCallbacks: types.ConnectionCallbacks{ - OnMessage: oa.handleMessage(zlog, apiKey), + zlog.Debug().Msg("authenticated opamp request") + + // Setup connection callbacks. + connectionCallbacks := types.ConnectionCallbacks{ + OnConnected: func(ctx context.Context, conn types.Connection) { + zlog.Debug().Msg("opAMP client connected") + }, + OnConnectionClose: func(conn types.Connection) { + zlog.Debug().Msg("opAMP client disconnected") }, + OnMessage: oa.handleMessage(zlog, apiKey), + } + connectionCallbacks.SetDefaults() // set defaults for other callbacks + + return types.ConnectionResponse{ + Accept: true, + ConnectionCallbacks: connectionCallbacks, } }, }, From 3896ea022734ac33d8a66c4d1a6a1ddb2c11d70b Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Feb 2026 11:27:55 -0800 Subject: [PATCH 32/59] Always return instanceUID in ServerToAgent messages --- internal/pkg/api/handleOpAMP.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index e8f120264e..0fb4f3e82b 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -142,6 +142,7 @@ func (oa *OpAMPT) handleMessage(zlog zerolog.Logger, apiKey *apikey.APIKey) func agent, err := oa.findEnrolledAgent(ctx, zlog, instanceUID.String()) if err != nil { return &protobufs.ServerToAgent{ + InstanceUid: instanceUID.Bytes(), ErrorResponse: &protobufs.ServerErrorResponse{ Type: protobufs.ServerErrorResponseType_ServerErrorResponseType_Unavailable, ErrorMessage: fmt.Errorf("failed to check if agent is enrolled: %w", err).Error(), @@ -157,6 +158,7 @@ func (oa *OpAMPT) handleMessage(zlog zerolog.Logger, apiKey *apikey.APIKey) func if agent == nil { if agent, err = oa.enrollAgent(zlog, instanceUID.String(), message, apiKey); err != nil { return &protobufs.ServerToAgent{ + InstanceUid: instanceUID.Bytes(), ErrorResponse: &protobufs.ServerErrorResponse{ Type: protobufs.ServerErrorResponseType_ServerErrorResponseType_Unavailable, ErrorMessage: fmt.Errorf("failed to enroll agent: %w", err).Error(), @@ -167,6 +169,7 @@ func (oa *OpAMPT) handleMessage(zlog zerolog.Logger, apiKey *apikey.APIKey) func if err := oa.updateAgent(zlog, agent, message); err != nil { return &protobufs.ServerToAgent{ + InstanceUid: instanceUID.Bytes(), ErrorResponse: &protobufs.ServerErrorResponse{ Type: protobufs.ServerErrorResponseType_ServerErrorResponseType_Unavailable, ErrorMessage: fmt.Errorf("failed to update persisted Agent information: %w", err).Error(), From 041f7de8b7f84049ade4b6f9381b4af0745f70c2 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Feb 2026 11:28:09 -0800 Subject: [PATCH 33/59] Delegate connection context handling to OpAMP server --- internal/pkg/api/server.go | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/internal/pkg/api/server.go b/internal/pkg/api/server.go index e3f8df8a74..6e97e06684 100644 --- a/internal/pkg/api/server.go +++ b/internal/pkg/api/server.go @@ -30,6 +30,8 @@ type server struct { addr string handler http.Handler logger *logp.Logger + + connContext func(ctx context.Context, c net.Conn) context.Context // used by OpAMP, if feature is enabled } // NewServer creates a new HTTP api for the passed addr. @@ -43,18 +45,24 @@ func NewServer(addr string, cfg *config.Server, opts ...APIOpt) *server { opt(a) } - handler := newRouter(&cfg.Limits, a, a.tracer) + s := server{ + addr: addr, + cfg: cfg, + logger: zap.NewStub("api-server"), + } - // Add OpAMP route handler to router if OpAMP feature is enabled. + handler := newRouter(&cfg.Limits, a, a.tracer) + // If OpAMP feature is enabled, add OpAMP route handler to router and + // let OpAMP server modify connection context (setup later when HTTP server + // object is constructed). if a.oa != nil && a.oa.Enabled() { handler = addOpAMPRouteHandler(handler, a.oa, &cfg.Limits) + s.connContext = a.oa.connCtx } - return &server{ - addr: addr, - cfg: cfg, - handler: handler, - logger: zap.NewStub("api-server"), - } + + s.handler = handler + return &s + } func (s *server) Run(ctx context.Context) error { @@ -75,6 +83,7 @@ func (s *server) Run(ctx context.Context) error { BaseContext: func(net.Listener) context.Context { return ctx }, ErrorLog: errLogger(ctx), ConnState: getDiagConnFunc(ctx), + ConnContext: s.connContext, } var listenCfg net.ListenConfig From 614bc27e9071c670697e3a993aca3fe383f9eaf9 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 9 Feb 2026 10:12:27 -0800 Subject: [PATCH 34/59] Update OpAMP feature flag test to use Enabled() method The test previously referenced ErrOpAMPDisabled and handleOpAMP which no longer exist. The feature flag check now happens at route registration time, so test the Enabled() method directly instead. Co-Authored-By: Claude Opus 4.6 --- internal/pkg/api/handleOpAMP_test.go | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/internal/pkg/api/handleOpAMP_test.go b/internal/pkg/api/handleOpAMP_test.go index 21b467702e..ebadac236f 100644 --- a/internal/pkg/api/handleOpAMP_test.go +++ b/internal/pkg/api/handleOpAMP_test.go @@ -5,29 +5,24 @@ package api import ( - "net/http" "testing" - "github.com/elastic/fleet-server/v7/internal/pkg/apikey" "github.com/elastic/fleet-server/v7/internal/pkg/config" - testlog "github.com/elastic/fleet-server/v7/internal/pkg/testing/log" "github.com/stretchr/testify/require" ) -// TODO: move to route setup where route itself is not created when feature -// flag is disabled. func TestFeatureFlag(t *testing.T) { cases := map[string]struct { FeatureFlagEnabled bool - WantError error + WantEnabled bool }{ "feature flag is disabled": { FeatureFlagEnabled: false, - WantError: ErrOpAMPDisabled, + WantEnabled: false, }, "feature flag is enabled": { FeatureFlagEnabled: true, - WantError: apikey.ErrNoAuthHeader, + WantEnabled: true, }, } for name, tc := range cases { @@ -38,13 +33,8 @@ func TestFeatureFlag(t *testing.T) { }, } - logger := testlog.SetLogger(t) - req := http.Request{} - var resp http.ResponseWriter - oa := OpAMPT{cfg: cfg} - err := oa.handleOpAMP(logger, &req, resp) - require.Equal(t, tc.WantError, err) + require.Equal(t, tc.WantEnabled, oa.Enabled()) }) } } From 804bac7ba168a4d09ba143c5b3374ea4b52e405e Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 9 Feb 2026 10:46:47 -0800 Subject: [PATCH 35/59] Add rate limiting and metrics for OpAMP route Wire up pathToOperation to recognize /v1/opamp and add the opamp case to the limiter middleware. Also apply the limiter middleware to the OpAMP route handler in server.go. Co-Authored-By: Claude Opus 4.6 --- internal/pkg/api/router.go | 6 ++++++ internal/pkg/api/router_test.go | 2 ++ internal/pkg/api/server.go | 3 ++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/internal/pkg/api/router.go b/internal/pkg/api/router.go index d2b5a6f867..827238604d 100644 --- a/internal/pkg/api/router.go +++ b/internal/pkg/api/router.go @@ -92,6 +92,10 @@ func pathToOperation(path string) string { return "getPGPKey" } + if path == "/v1/opamp" { + return "opamp" + } + if strings.HasPrefix(path, "/api/fleet/") { pp := strings.Split(strings.TrimPrefix(path, "/"), "/") if len(pp) == 4 { @@ -146,6 +150,8 @@ func (l *limiter) middleware(next http.Handler) http.Handler { l.status.Wrap("status", &cntStatus, zerolog.DebugLevel)(next).ServeHTTP(w, r) case "audit-unenroll": l.auditUnenroll.Wrap("audit-unenroll", &cntAuditUnenroll, zerolog.DebugLevel)(next).ServeHTTP(w, r) + case "opamp": + l.opAMP.Wrap("opamp", &cntOpAMP, zerolog.DebugLevel)(next).ServeHTTP(w, r) default: // no tracking or limits next.ServeHTTP(w, r) diff --git a/internal/pkg/api/router_test.go b/internal/pkg/api/router_test.go index 009d691eaa..bafd8a4227 100644 --- a/internal/pkg/api/router_test.go +++ b/internal/pkg/api/router_test.go @@ -35,6 +35,8 @@ func TestPathToOperation(t *testing.T) { {"/api/fleet/file/abc", "deliverFile"}, {"/api/fleet/artifacts/some-id/hash", "artifact"}, {"/api/fleet/agents/some-id/audit/unenroll", "audit-unenroll"}, + {"/v1/opamp", "opamp"}, + {"/v1/opamp/", "opamp"}, {"/api/fleet/agents/some-id/other/unenroll", ""}, {"/api/fleet/unimplemented/some-id", ""}, {"/api/flet/agents/some-id/acks", ""}, diff --git a/internal/pkg/api/server.go b/internal/pkg/api/server.go index 6e97e06684..ff7638375d 100644 --- a/internal/pkg/api/server.go +++ b/internal/pkg/api/server.go @@ -151,8 +151,9 @@ func (s *server) Run(ctx context.Context) error { return nil } -func addOpAMPRouteHandler(existingHandler http.Handler, oa *OpAMPT, limits *config.ServerLimits) http.Handler { +func addOpAMPRouteHandler(existingHandler http.Handler, oa *OpAMPT, cfg *config.ServerLimits) http.Handler { r := chi.NewRouter() + r.Use(Limiter(cfg).middleware) opAMPHandler := oa.handler r.HandleFunc("/v1/opamp", http.HandlerFunc(opAMPHandler)) From d991ef8bd398bbedf2fbcf20f245b8ab3621268f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 11 Feb 2026 15:53:38 -0800 Subject: [PATCH 36/59] Update internal/pkg/api/handleOpAMP.go Co-authored-by: Michel Laterman <82832767+michel-laterman@users.noreply.github.com> --- internal/pkg/api/handleOpAMP.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 0fb4f3e82b..3f695399b0 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -172,7 +172,7 @@ func (oa *OpAMPT) handleMessage(zlog zerolog.Logger, apiKey *apikey.APIKey) func InstanceUid: instanceUID.Bytes(), ErrorResponse: &protobufs.ServerErrorResponse{ Type: protobufs.ServerErrorResponseType_ServerErrorResponseType_Unavailable, - ErrorMessage: fmt.Errorf("failed to update persisted Agent information: %w", err).Error(), + ErrorMessage: fmt.Sprintf("failed to update persisted Agent information: %v", err), }, } } From 1421ba67848cb99304e3a048209d126e9e1ccd78 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 11 Feb 2026 15:53:45 -0800 Subject: [PATCH 37/59] Update internal/pkg/api/handleOpAMP.go Co-authored-by: Michel Laterman <82832767+michel-laterman@users.noreply.github.com> --- internal/pkg/api/handleOpAMP.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 3f695399b0..939796cd19 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -145,7 +145,7 @@ func (oa *OpAMPT) handleMessage(zlog zerolog.Logger, apiKey *apikey.APIKey) func InstanceUid: instanceUID.Bytes(), ErrorResponse: &protobufs.ServerErrorResponse{ Type: protobufs.ServerErrorResponseType_ServerErrorResponseType_Unavailable, - ErrorMessage: fmt.Errorf("failed to check if agent is enrolled: %w", err).Error(), + ErrorMessage: fmt.Sprintf("failed to check if agent is enrolled: %v", err) }, } } From 862ef33b496881678c6e470a566b949c9e95923f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 11 Feb 2026 15:54:56 -0800 Subject: [PATCH 38/59] Update internal/pkg/api/handleOpAMP.go Co-authored-by: Michel Laterman <82832767+michel-laterman@users.noreply.github.com> --- internal/pkg/api/handleOpAMP.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 939796cd19..a8ed4a694b 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -161,7 +161,7 @@ func (oa *OpAMPT) handleMessage(zlog zerolog.Logger, apiKey *apikey.APIKey) func InstanceUid: instanceUID.Bytes(), ErrorResponse: &protobufs.ServerErrorResponse{ Type: protobufs.ServerErrorResponseType_ServerErrorResponseType_Unavailable, - ErrorMessage: fmt.Errorf("failed to enroll agent: %w", err).Error(), + ErrorMessage: fmt.Sprintf("failed to enroll agent: %v", err), }, } } From df9cb16d08a0563e8074bca47f8bcedd00197726 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 11 Feb 2026 16:17:07 -0800 Subject: [PATCH 39/59] Log when enabling OpAMP endpoint --- internal/pkg/api/server.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/pkg/api/server.go b/internal/pkg/api/server.go index ff7638375d..5f0fa98370 100644 --- a/internal/pkg/api/server.go +++ b/internal/pkg/api/server.go @@ -56,6 +56,7 @@ func NewServer(addr string, cfg *config.Server, opts ...APIOpt) *server { // let OpAMP server modify connection context (setup later when HTTP server // object is constructed). if a.oa != nil && a.oa.Enabled() { + zerolog.Log().Info().Msg("enabling OpAMP endpoint") handler = addOpAMPRouteHandler(handler, a.oa, &cfg.Limits) s.connContext = a.oa.connCtx } From 362a23b0b7bc81d6cca3e951179b87780d14da16 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 11 Feb 2026 16:22:42 -0800 Subject: [PATCH 40/59] Enable compression on OpAMP server --- internal/pkg/api/handleOpAMP.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index a8ed4a694b..4b774be6ea 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -105,6 +105,7 @@ func (oa *OpAMPT) Init() error { } }, }, + EnableCompression: true, } handler, connCtx, err := oa.srv.Attach(settings) From a838139fe6d46022b433bae75f8272a8167f381f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 11 Feb 2026 16:30:07 -0800 Subject: [PATCH 41/59] Allow up to 3 MB body for OpAMP requests --- internal/pkg/config/env_defaults.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pkg/config/env_defaults.go b/internal/pkg/config/env_defaults.go index 19d6e9ee4c..fdeebe7c58 100644 --- a/internal/pkg/config/env_defaults.go +++ b/internal/pkg/config/env_defaults.go @@ -60,7 +60,7 @@ const ( defaultOpAMPInterval = time.Millisecond defaultOpAMPBurst = 1000 defaultOpAMPMax = 0 - defaultOpAMPMaxBody = 0 + defaultOpAMPMaxBody = 3 * 1024 * 1024 defaultUploadStartInterval = time.Second * 2 defaultUploadStartBurst = 5 From 1f96ce5c78fa98556daba1812d24782526222ed9 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 11 Feb 2026 22:39:54 -0800 Subject: [PATCH 42/59] Fix missing trailing comma in composite literal Co-Authored-By: Claude Opus 4.6 --- internal/pkg/api/handleOpAMP.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 4b774be6ea..90a7e9e0d1 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -146,7 +146,7 @@ func (oa *OpAMPT) handleMessage(zlog zerolog.Logger, apiKey *apikey.APIKey) func InstanceUid: instanceUID.Bytes(), ErrorResponse: &protobufs.ServerErrorResponse{ Type: protobufs.ServerErrorResponseType_ServerErrorResponseType_Unavailable, - ErrorMessage: fmt.Sprintf("failed to check if agent is enrolled: %v", err) + ErrorMessage: fmt.Sprintf("failed to check if agent is enrolled: %v", err), }, } } From 0a75991594c6275770d4f339875a7ccc92f5e13a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 11 Feb 2026 22:40:03 -0800 Subject: [PATCH 43/59] Add error logging callbacks to OpAMP connection Add OnReadMessageError and OnMessageResponseError callbacks to log errors when reading/deserializing messages or sending responses. Co-Authored-By: Claude Opus 4.6 --- internal/pkg/api/handleOpAMP.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 90a7e9e0d1..4271f0b098 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -96,6 +96,12 @@ func (oa *OpAMPT) Init() error { zlog.Debug().Msg("opAMP client disconnected") }, OnMessage: oa.handleMessage(zlog, apiKey), + OnReadMessageError: func(conn types.Connection, mt int, msgByte []byte, err error) { + zlog.Error().Err(err).Int("message_type", mt).Msg("failed to read opAMP message") + }, + OnMessageResponseError: func(conn types.Connection, message *protobufs.ServerToAgent, err error) { + zlog.Error().Err(err).Msg("failed to send opAMP response") + }, } connectionCallbacks.SetDefaults() // set defaults for other callbacks From 048033594b4580c421d94a224e8fcba1fd57c9eb Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 11 Feb 2026 22:45:08 -0800 Subject: [PATCH 44/59] Fix OpAMP enabled log to use zerolog/log package zerolog.Log() doesn't exist; use the zerolog/log sub-package instead. Co-Authored-By: Claude Opus 4.6 --- internal/pkg/api/server.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/pkg/api/server.go b/internal/pkg/api/server.go index 5f0fa98370..71933ecd4b 100644 --- a/internal/pkg/api/server.go +++ b/internal/pkg/api/server.go @@ -18,6 +18,7 @@ import ( "github.com/go-chi/chi/v5" "github.com/rs/zerolog" + zlog "github.com/rs/zerolog/log" "github.com/elastic/fleet-server/v7/internal/pkg/config" "github.com/elastic/fleet-server/v7/internal/pkg/limit" @@ -56,7 +57,7 @@ func NewServer(addr string, cfg *config.Server, opts ...APIOpt) *server { // let OpAMP server modify connection context (setup later when HTTP server // object is constructed). if a.oa != nil && a.oa.Enabled() { - zerolog.Log().Info().Msg("enabling OpAMP endpoint") + zlog.Info().Msg("enabling OpAMP endpoint") handler = addOpAMPRouteHandler(handler, a.oa, &cfg.Limits) s.connContext = a.oa.connCtx } From a6abeb2d5d0714cbc516114acc49d62a3963eb12 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 11 Feb 2026 23:05:29 -0800 Subject: [PATCH 45/59] Disable HTTP keep-alive for OpAMP requests to fix EOF errors The server's IdleTimeout (30s) matches the OTel Collector's polling interval (~30s), causing a race where the server closes the idle connection just as the client tries to reuse it. Setting Connection: close on OpAMP responses forces a fresh connection per poll, eliminating the race with negligible overhead given the 30s polling interval. Co-Authored-By: Claude Opus 4.6 --- internal/pkg/api/server.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/pkg/api/server.go b/internal/pkg/api/server.go index 71933ecd4b..8f88fdc8d9 100644 --- a/internal/pkg/api/server.go +++ b/internal/pkg/api/server.go @@ -158,7 +158,14 @@ func addOpAMPRouteHandler(existingHandler http.Handler, oa *OpAMPT, cfg *config. r.Use(Limiter(cfg).middleware) opAMPHandler := oa.handler - r.HandleFunc("/v1/opamp", http.HandlerFunc(opAMPHandler)) + r.HandleFunc("/v1/opamp", func(w http.ResponseWriter, r *http.Request) { + // Disable HTTP keep-alive for OpAMP requests. The OTel Collector polls + // every ~30s and the server's IdleTimeout is also 30s, creating a race + // where the server closes the idle connection just as the client tries + // to reuse it, resulting in EOF errors on the client side. + w.Header().Set("Connection", "close") + opAMPHandler(w, r) + }) // Handle existing routes r.Mount("/", existingHandler) From e0554c479c717d6dd9a1e98afd8fdfc9412f69ec Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 13 Feb 2026 13:01:29 -0800 Subject: [PATCH 46/59] Handle Elasticsearch 429 rate limit in OpAMP auth Return HTTP 429 instead of 401 when Elasticsearch returns a rate limit error during API key authentication for OpAMP requests. Co-Authored-By: Claude Opus 4.6 --- internal/pkg/api/handleOpAMP.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 4271f0b098..b60f1a788f 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -74,6 +74,16 @@ func (oa *OpAMPT) Init() error { Logger() apiKey, err := authAPIKey(request, oa.bulk, oa.cache) + if errors.Is(err, apikey.ErrElasticsearchAuthLimit) { + zlog.Warn().Err(err).Msg("elasticsearch rate limit on opamp request") + return types.ConnectionResponse{ + Accept: false, + HTTPStatusCode: http.StatusTooManyRequests, + HTTPResponseHeader: map[string]string{ + "Content-Type": "application/x-protobuf", + }, + } + } if err != nil { zlog.Warn().Err(err).Msg("unauthenticated opamp request") return types.ConnectionResponse{ From b492246c32c6ce1a58655b0e9b1358bd13ff353c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 17 Feb 2026 13:56:37 -0800 Subject: [PATCH 47/59] Increase server idle timeout to 35s for OpAMP compatibility The OTel Collector polls the OpAMP endpoint every ~30s by default. With the idle timeout also at 30s, there is a race where the server closes an idle connection just as the client tries to reuse it, resulting in EOF errors. Bumping the idle timeout to 35s gives enough headroom to avoid this race. Co-Authored-By: Claude Opus 4.6 --- internal/pkg/config/timeouts.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/pkg/config/timeouts.go b/internal/pkg/config/timeouts.go index 86d75f7d52..a85d9b95c1 100644 --- a/internal/pkg/config/timeouts.go +++ b/internal/pkg/config/timeouts.go @@ -44,7 +44,9 @@ func (c *ServerTimeouts) InitDefaults() { // IdleTimeout is the maximum amount of time to wait for the // next request when keep-alives are enabled. Because TLS handshakes are expensive // for the server, avoid aggressive connection close with generous idle timeout. - c.Idle = 30 * time.Second + // Set to 35s (slightly above the OTel Collector's default ~30s OpAMP polling interval) + // to ensure idle connections are not closed just as a new OpAMP request comes in. + c.Idle = 35 * time.Second // The write timeout for HTTPS covers the time from ACCEPT to the end of the response write; // so in that case it covers the TLS handshake. If the connection is reused, the write timeout From 00f51ada44ad0c59518bf57ad336f820ce2899aa Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 17 Feb 2026 17:13:20 -0800 Subject: [PATCH 48/59] Use opamp.agent.version instead of agent_version --- internal/pkg/api/handleOpAMP.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index b60f1a788f..e91b7e3cda 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -246,7 +246,7 @@ func (oa *OpAMPT) enrollAgent(zlog zerolog.Logger, agentID string, aToS *protobu meta.Elastic.Agent.Version = ia.GetValue().GetStringValue() } } - zlog.Debug().Str("agent_version", meta.Elastic.Agent.Version).Msg("extracted agent version") + zlog.Debug().Str("opamp.agent.version", meta.Elastic.Agent.Version).Msg("extracted agent version") // Extract hostname for _, nia := range aToS.AgentDescription.NonIdentifyingAttributes { From e7138a456c9ac2da9e50059dec19b2f9447e8c4a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 17 Feb 2026 17:14:08 -0800 Subject: [PATCH 49/59] Consistently use opamp.agent.uid --- internal/pkg/api/handleOpAMP.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index e91b7e3cda..8d170214ee 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -169,7 +169,7 @@ func (oa *OpAMPT) handleMessage(zlog zerolog.Logger, apiKey *apikey.APIKey) func zlog.Debug(). Bool("is_enrolled", agent != nil). - Str("agent_id", instanceUID.String()). + Str("opamp.agent.uid", instanceUID.String()). Msg("agent enrollment status") if agent == nil { @@ -223,7 +223,7 @@ func (oa *OpAMPT) findEnrolledAgent(ctx context.Context, _ zerolog.Logger, agent func (oa *OpAMPT) enrollAgent(zlog zerolog.Logger, agentID string, aToS *protobufs.AgentToServer, apiKey *apikey.APIKey) (*model.Agent, error) { zlog.Debug(). - Str("agentID", agentID). + Str("opamp.agent.uid", agentID). Msg("enrolling agent") ctx := context.TODO() rec, err := dl.FindEnrollmentAPIKey(ctx, oa.bulk, dl.QueryEnrollmentAPIKeyByID, dl.FieldAPIKeyID, apiKey.ID) From b1075928150ac9287f1059c9d6eb5079e481ef93 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 17 Feb 2026 17:14:49 -0800 Subject: [PATCH 50/59] Don't log complete AgentToServer or ServerToAgent messages --- internal/pkg/api/handleOpAMP.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 8d170214ee..a22c87d703 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -152,7 +152,6 @@ func (oa *OpAMPT) handleMessage(zlog zerolog.Logger, apiKey *apikey.APIKey) func zlog.Debug(). Str("instance_uid", instanceUID.String()). - Str("aToS", message.String()). Msg("received AgentToServer message from agent") // Check if Agent is "enrolled"; if it is, update it; otherwise, enroll it. @@ -199,7 +198,6 @@ func (oa *OpAMPT) handleMessage(zlog zerolog.Logger, apiKey *apikey.APIKey) func InstanceUid: instanceUID.Bytes(), } - zlog.Debug().Str("resp", sToA.String()).Msg("sending ServerToAgent response") return &sToA } } From ab2404fe0b008c09d691f68bc10cbe635491b130 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 17 Feb 2026 17:16:14 -0800 Subject: [PATCH 51/59] Remove unused agentMetas field --- internal/pkg/api/handleOpAMP.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index a22c87d703..7f31950d91 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -42,8 +42,6 @@ type OpAMPT struct { srv oaServer.OpAMPServer handler oaServer.HTTPHandlerFunc connCtx oaServer.ConnContext - - agentMetas map[string]localMetadata } func NewOpAMPT( @@ -54,12 +52,11 @@ func NewOpAMPT( bc *checkin.Bulk, ) *OpAMPT { oa := &OpAMPT{ - cfg: cfg, - bulk: bulker, - cache: cache, - bc: bc, - srv: oaServer.New(nil), - agentMetas: map[string]localMetadata{}, + cfg: cfg, + bulk: bulker, + cache: cache, + bc: bc, + srv: oaServer.New(nil), } return oa From f587c10bc57605546beca3d29ec0890b8acee6ad Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 17 Feb 2026 17:18:12 -0800 Subject: [PATCH 52/59] Group imports in handleOpAMP.go Co-Authored-By: Claude Sonnet 4.6 --- internal/pkg/api/handleOpAMP.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 7f31950d91..91e88b7ff7 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -12,13 +12,6 @@ import ( "net/http" "time" - "github.com/elastic/fleet-server/v7/internal/pkg/apikey" - "github.com/elastic/fleet-server/v7/internal/pkg/bulk" - "github.com/elastic/fleet-server/v7/internal/pkg/cache" - "github.com/elastic/fleet-server/v7/internal/pkg/checkin" - "github.com/elastic/fleet-server/v7/internal/pkg/config" - "github.com/elastic/fleet-server/v7/internal/pkg/dl" - "github.com/elastic/fleet-server/v7/internal/pkg/model" "github.com/gofrs/uuid/v5" "github.com/open-telemetry/opamp-go/protobufs" oaServer "github.com/open-telemetry/opamp-go/server" @@ -27,6 +20,14 @@ import ( "github.com/rs/zerolog/hlog" "go.opentelemetry.io/otel/attribute" semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + + "github.com/elastic/fleet-server/v7/internal/pkg/apikey" + "github.com/elastic/fleet-server/v7/internal/pkg/bulk" + "github.com/elastic/fleet-server/v7/internal/pkg/cache" + "github.com/elastic/fleet-server/v7/internal/pkg/checkin" + "github.com/elastic/fleet-server/v7/internal/pkg/config" + "github.com/elastic/fleet-server/v7/internal/pkg/dl" + "github.com/elastic/fleet-server/v7/internal/pkg/model" ) const ( From 9e72aed2a74be031e9f8d669a7d532c70f7a8587 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 17 Feb 2026 17:24:43 -0800 Subject: [PATCH 53/59] Update doc --- docs/opamp.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/opamp.md b/docs/opamp.md index adf7ff0e8d..2c66d1f673 100644 --- a/docs/opamp.md +++ b/docs/opamp.md @@ -9,14 +9,21 @@ This section describes how to connect a OpenTelemetry Collector instance to Flee ``` POST /_security/service/elastic/fleet-server/credential/token/opamp ``` -3. Create a `fleet-server.dev.yml` configuration file as described in https://github.com/elastic/kibana/blob/main/x-pack/platform/plugins/shared/fleet/dev_docs/local_setup/developing_kibana_and_fleet_server.md. +3. Enable the OpAMP feature flag in the `fleet-server.yml` file by adding the following snippet to the `fleet-server` input + section, as a sibling of the `policy.id` key: + ```yml + server: + feature_flags: + enable_opamp: true + ``` + 4. Build the Fleet Server binary for your platform. ``` - PLATFORMS=darwin/arm64 mage build:binary + mage build:local ``` 5. Run the Fleet Server binary with the above configuration. ``` - ./build/binaries/fleet-server-9.4.0-darwin-aarch64/fleet-server -c fleet-server.dev.yml + ./bin/fleet-server ``` 6. Create a new policy in Fleet. Copy the enrollment token for that policy. 7. Create OpenTelemetry Collector configuration for connecting to the Fleet Server instance and save it as `otel-opamp.yaml`. From 58e84287f82dea91033c00c3798bf3e1cb617406 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 18 Feb 2026 09:23:32 -0800 Subject: [PATCH 54/59] Use opamp.agent.uid --- internal/pkg/api/handleOpAMP.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 91e88b7ff7..548f6dbd27 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -149,7 +149,7 @@ func (oa *OpAMPT) handleMessage(zlog zerolog.Logger, apiKey *apikey.APIKey) func } zlog.Debug(). - Str("instance_uid", instanceUID.String()). + Str("opamp.agent.id", instanceUID.String()). Msg("received AgentToServer message from agent") // Check if Agent is "enrolled"; if it is, update it; otherwise, enroll it. From 43e8ecff1d361dd02daea933857af8ce2773e8c6 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 18 Feb 2026 09:24:15 -0800 Subject: [PATCH 55/59] Don't log raw data --- internal/pkg/api/handleOpAMP.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index 548f6dbd27..fe1df122be 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -262,8 +262,6 @@ func (oa *OpAMPT) enrollAgent(zlog zerolog.Logger, agentID string, aToS *protobu return nil, fmt.Errorf("failed to marshal local metadata: %w", err) } - zlog.Debug().RawJSON("meta", data).Msg("updating local metadata") - agent := model.Agent{ ESDocument: model.ESDocument{Id: agentID}, Active: true, @@ -281,7 +279,6 @@ func (oa *OpAMPT) enrollAgent(zlog zerolog.Logger, agentID string, aToS *protobu } zlog.Debug(). - Str("agent document", string(data)). Msg("creating .fleet-agents doc") if _, err = oa.bulk.Create(ctx, dl.FleetAgents, agentID, data, bulk.WithRefresh()); err != nil { return nil, err From 0f070325f65f1400aa25797e248623226582db9d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 18 Feb 2026 13:36:22 -0800 Subject: [PATCH 56/59] Update idle timeout in unit tests --- internal/pkg/config/config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pkg/config/config_test.go b/internal/pkg/config/config_test.go index 57f337ecbc..6f5c7bd553 100644 --- a/internal/pkg/config/config_test.go +++ b/internal/pkg/config/config_test.go @@ -117,7 +117,7 @@ func TestConfig(t *testing.T) { Timeouts: ServerTimeouts{ Read: 20 * time.Second, ReadHeader: 5 * time.Second, - Idle: 30 * time.Second, + Idle: 35 * time.Second, Write: 5 * time.Second, CheckinTimestamp: 30 * time.Second, CheckinLongPoll: 5 * time.Minute, From ea839065e01ccf2a4872079a4fb43e7df4b3c367 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 18 Feb 2026 16:48:10 -0800 Subject: [PATCH 57/59] Attach opamp.agent.uid to all logs within handleMessage --- internal/pkg/api/handleOpAMP.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/pkg/api/handleOpAMP.go b/internal/pkg/api/handleOpAMP.go index fe1df122be..fabbafba42 100644 --- a/internal/pkg/api/handleOpAMP.go +++ b/internal/pkg/api/handleOpAMP.go @@ -148,8 +148,9 @@ func (oa *OpAMPT) handleMessage(zlog zerolog.Logger, apiKey *apikey.APIKey) func } } + zlog = zlog.With().Str("opamp.agent.uid", instanceUID.String()).Logger() + zlog.Debug(). - Str("opamp.agent.id", instanceUID.String()). Msg("received AgentToServer message from agent") // Check if Agent is "enrolled"; if it is, update it; otherwise, enroll it. @@ -166,7 +167,6 @@ func (oa *OpAMPT) handleMessage(zlog zerolog.Logger, apiKey *apikey.APIKey) func zlog.Debug(). Bool("is_enrolled", agent != nil). - Str("opamp.agent.uid", instanceUID.String()). Msg("agent enrollment status") if agent == nil { @@ -219,7 +219,6 @@ func (oa *OpAMPT) findEnrolledAgent(ctx context.Context, _ zerolog.Logger, agent func (oa *OpAMPT) enrollAgent(zlog zerolog.Logger, agentID string, aToS *protobufs.AgentToServer, apiKey *apikey.APIKey) (*model.Agent, error) { zlog.Debug(). - Str("opamp.agent.uid", agentID). Msg("enrolling agent") ctx := context.TODO() rec, err := dl.FindEnrollmentAPIKey(ctx, oa.bulk, dl.QueryEnrollmentAPIKeyByID, dl.FieldAPIKeyID, apiKey.ID) From 854e369eca894df700c49060f4d94978862dcb9b Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 19 Feb 2026 12:24:04 -0800 Subject: [PATCH 58/59] Re-enable keepalives --- internal/pkg/api/server.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/internal/pkg/api/server.go b/internal/pkg/api/server.go index 8f88fdc8d9..71933ecd4b 100644 --- a/internal/pkg/api/server.go +++ b/internal/pkg/api/server.go @@ -158,14 +158,7 @@ func addOpAMPRouteHandler(existingHandler http.Handler, oa *OpAMPT, cfg *config. r.Use(Limiter(cfg).middleware) opAMPHandler := oa.handler - r.HandleFunc("/v1/opamp", func(w http.ResponseWriter, r *http.Request) { - // Disable HTTP keep-alive for OpAMP requests. The OTel Collector polls - // every ~30s and the server's IdleTimeout is also 30s, creating a race - // where the server closes the idle connection just as the client tries - // to reuse it, resulting in EOF errors on the client side. - w.Header().Set("Connection", "close") - opAMPHandler(w, r) - }) + r.HandleFunc("/v1/opamp", http.HandlerFunc(opAMPHandler)) // Handle existing routes r.Mount("/", existingHandler) From e6cce32c3752410b9cb7e91677a05c0ccfbfd285 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 19 Feb 2026 12:26:37 -0800 Subject: [PATCH 59/59] Undo OpenAPI spec changes --- model/oapi-cfg.yml | 3 --- model/openapi.yml | 13 +------------ 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/model/oapi-cfg.yml b/model/oapi-cfg.yml index cdce661ff8..c877e4c994 100644 --- a/model/oapi-cfg.yml +++ b/model/oapi-cfg.yml @@ -5,6 +5,3 @@ generate: chi-server: true # strict-server: true # We need to figure out where/how to handle auth if we want to use strict generation output: ./internal/pkg/api/openapi.gen.go -output-options: - exclude-operation-ids: - - opamp \ No newline at end of file diff --git a/model/openapi.yml b/model/openapi.yml index c7a7e4bc8f..6db051d012 100644 --- a/model/openapi.yml +++ b/model/openapi.yml @@ -2053,15 +2053,4 @@ paths: "500": $ref: "#/components/responses/internalServerError" "503": - $ref: "#/components/responses/unavailable" -# /v1/opamp: -# post: -# operationId: opamp -# summary: Receive OpAMP AgentToServer messages -# description: | -# Receive OpAMP AgentToServer messages and respond with ServerToAgent message, -# as described in https://opentelemetry.io/docs/specs/opamp/ -# security: -# - agentApiKey: [] -# requestBody: -# required: true + $ref: "#/components/responses/unavailable" \ No newline at end of file