Open Agent Management Protocol (OpAMP) Go Implementation

repository·main·Indexed 42 days ago

https://github.com/open-telemetry/opamp-go

A Go implementation of the Open Agent Management Protocol (OpAMP), a vendor-agnostic protocol for remotely managing, configuring, and updating large fleets of data collection agents. It provides the OpAMPClient and OpAMPServer interfaces for status reporting, configuration management, and package updates via HTTP and WebSocket connections.

Tokens
4.1K
Snippets
7
Records
13
Agent score
54%

What's inside OpAMP Go

  1. What is OpAMP?

    main

    The Open Agent Management Protocol (OpAMP) is a vendor-agnostic network protocol designed for the remote management of large fleets of data collection Agents.

    Key capabilities of OpAMP include:

    • Status Reporting: Agents can report their current status to a central Server.
    • Configuration Management: Agents can receive configuration updates from a Server.
    • Package Updates: Agents can receive agent package updates from the Server.

    Because the protocol is vendor-agnostic, a single Server can manage a heterogeneous fleet of different Agents from various vendors, provided they all implement the OpAMP protocol.

  2. Run the OpAMP example agent

    main

    The agent example provides a reference implementation of an OpAMP protocol agent. It supports both HTTP and Websocket connections.

    By default, the agent runs in a single instance mode. However, it also supports a 'scale mode' where multiple agents are started within the same process. When running in scale mode, the agent orchestrator logs to stdout, while the individual agents log to stderr.

  3. Configure and start an OpAMP Server

    main

    The StartSettings struct is used with the Start() method to launch a standalone OpAMP Server. It embeds the base Settings and adds network-specific configurations.

    • ListenEndpoint: The network address to listen on (e.g., "127.0.0.1:4320").
    • ListenPath: The URL path for OpAMP connections. Defaults to "/v1/opamp" if empty.
    • TLSConfig: Pointer to a tls.Config for secure connections.
    • HTTPMiddleware: An optional function to wrap the http.Handler. For WebSockets, this is called upon connection; for standard HTTP, it is called for every request.
    server.StartSettings{
        Settings: server.Settings{
            EnableCompression: true,
        },
        ListenEndpoint: "127.0.0.1:4320",
        ListenPath:     "/v1/opamp",
        TLSConfig:      &tls.Config{ /* ... */ },
        HTTPMiddleware: func(next http.Handler) http.Handler {
            return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                // custom middleware logic
                next.ServeHTTP(w, r)
            })
        },
    }
  4. Configure OpAMP Server settings

    main

    The Settings struct defines the operational parameters for an OpAMP Server. Use it to configure callbacks, message size limits, and custom capabilities.

    • Callbacks: types.Callbacks that the server triggers after successful Attach/Start.
    • EnableCompression: Set to true to enable compression for both directions. For WebSockets, this requires client support.
    • MaxMessageSize: The maximum size in bytes for transport messages.
      • 0: Defaults to 64 MiB.
      • < 0: No limit applied.
      • For HTTP: Applies to the complete request/response body (pre-compression/post-decompression).
      • For WebSocket: Applies to the complete OpAMP WebSocket message including headers and data.
    • CustomCapabilities: A slice of strings representing custom capabilities. Each should be a reverse FQDN with optional version information to uniquely identify the capability, matching those specified in a CustomMessage.
    server.Settings{
        EnableCompression: true,
        MaxMessageSize:    1024 * 1024 * 10, // 10 MiB
        CustomCapabilities: []string{"example.com/my-capability/v1"},
    }
  5. Configure the OpAMP example agent via CLI flags and environment variables

    main

    The example agent can be configured using command-line flags or environment variables. Below is the complete list of available configuration options:

    FlagEnvironment VariableDescription
    -endpointAGENT_ENDPOINTOpAMP server endpoint URL (default wss://127.0.0.1:4320/v1/opamp)
    -heartbeatAGENT_HEARTBEATHeartbeat duration (default 30s)
    -quite-agentAGENT_QUIETDisable agent logger
    -scale-countAGENT_SCALE_COUNTNumber of agents to start in scale mode (default 1)
    -tAGENT_TYPEAgent Type String (default io.opentelemetry.collector)
    -tls-ca_fileAGENT_TLS_CA_FILEPath to the CA cert for server certificate verification
    -tls-cert_fileAGENT_TLS_CERT_FILEPath to the TLS cert
    -tls-insecureAGENT_TLS_INSECUREDisable the client transport security
    -tls-insecure_skip_verifyAGENT_TLS_INSECURE_SKIP_VERIFYEnable TLS but skip certificate verification
    -tls-key_fileAGENT_TLS_KEY_FILEPath to the TLS key
    -vAGENT_VERSIONAgent Version String (default 1.0.0)
    Usage of agent:
      -endpoint string
        	OpAMP server endpoint URL (env var: AGENT_ENDPOINT). (default "wss://127.0.0.1:4320/v1/opamp")
      -heartbeat duration
        	Heartbeat duration (env var: AGENT_HEARTBEAT). (default 30s)
      -quite-agent
        	Disable agent logger (env var: AGENT_QUIET).
      -scale-count uint
        	The number of agents to start in scale mode (env var: AGENT_SCALE_COUNT). (default 1)
      -t string
        	Agent Type String (env var: AGENT_TYPE). (default "io.opentelemetry.collector")
      -tls-ca_file string
        	Path to the CA cert. It verifies the server certificate (env var: AGENT_TLS_CA_FILE).
      -tls-cert_file string
        	Path to the TLS cert (env var: AGENT_TLS_CERT_FILE).
      -tls-insecure
        	Disable the client transport security (env var: AGENT_TLS_INSECURE).
      -tls-insecure_skip_verify
        	Will enable TLS but not verify the certificate (env var: AGENT_TLS_INSECURE_SKIP_VERIFY).
      -tls-key_file string
        	Path to the TLS key (env var: AGENT_TLS_KEY_FILE).
      -v string
        	Agent Version String (env var: AGENT_VERSION). (default "1.0.0")
  6. Send Custom Messages

    main

    The SendCustomMessage method allows the agent to send specialized messages to the server using custom capabilities.

    Usage Pattern: Because only one custom message can be sent at a time, SendCustomMessage returns a messageSendingChannel. If a message is already in progress, subsequent calls return ErrCustomMessagePending. You should wait for the returned channel to close before attempting to send another message.

    Errors:

    • ErrCustomMessageMissing: If the provided message is nil.
    • ErrCustomCapabilityNotSupported: If the message capability was not declared in SetCustomCapabilities or StartSettings.
    • ErrCustomMessagePending: If a message is currently being sent.
    // Sending a custom message and waiting for completion
    ch, err := client.SendCustomMessage(myCustomMsg)
    if err != nil {
        // handle error
    }
    <-ch // Wait for the message to be sent
  7. Use the OpAMPClient interface

    main

    The OpAMPClient interface is the primary entry point for agents to interact with an OpAMP Server. It allows agents to report their status, health, and available components, while also enabling them to receive remote configurations and send custom messages.

    Lifecycle Management:

    1. Setup: Call SetAgentDescription() and optionally SetCustomCapabilities(), SetFlags(), or SetAvailableComponents() before calling Start() to ensure these attributes are included in the initial connection handshake.
    2. Execution: Call Start(ctx, settings) to begin connection attempts. Start returns immediately and does not block until a connection is established. Connection success or failure is communicated via callbacks (e.g., OnConnectFailed, OnRemoteConfig).
    3. Runtime: Use methods like SetHealth(), SetRemoteConfigStatus(), or SendCustomMessage() to update the server on the agent's state or perform actions during the session.
    4. Teardown: Call Stop(ctx) to gracefully shut down the client. This ensures no further callbacks are triggered and waits for in-flight callbacks to complete.
    // Example conceptual usage of OpAMPClient
    var client OpAMPClient
    
    // 1. Configure agent identity before starting
    err := client.SetAgentDescription(&protobufs.AgentDescription{...})
    
    // 2. Start the client
    err = client.Start(ctx, settings)
    if err != nil {
        // Handle immediate errors (e.g. invalid serverURL)
    }
    
    // 3. Update status during runtime
    client.SetHealth(&protobufs.ComponentHealth{...})
    
    // 4. Stop the client
    client.Stop(ctx)
  8. Configure Capabilities and Components

    main

    These methods define what the agent is capable of and what it can configure.

    • SetCapabilities(capabilities *protobufs.AgentCapabilities) error: Updates the set of capabilities the client supports. Can be called anytime. Must not be nil.
    • SetCustomCapabilities(customCapabilities *protobufs.CustomCapabilities) error: Modifies the set of custom capabilities. If called before Start(), they are sent during the initial handshake. Each capability should be a reverse FQDN.
    • SetAvailableComponents(components *protobufs.AvailableComponents) error: Modifies the set of components available for configuration.
      • If ReportsAvailableComponents capability is set in StartSettings, this must be called before Start().
      • If called after Start(), components must not be nil and components.Hash must be non-empty.
    • SetFlags(flags protobufs.AgentToServerFlags): Modifies the set of flags supported by the client.
  9. Attach an OpAMP Server to an existing http.Server

    main

    If you want to manage your own http.Server instance instead of letting the OpAMP library start its own, use the Attach() method. This returns an HTTPHandlerFunc and a ConnContext that you must integrate into your server.

    1. Call Attach(settings).
    2. Register the returned HTTPHandlerFunc to your router/mux.
    3. Assign the returned ConnContext to the ConnContext field of your http.Server (this is specifically used for plain HTTP connections).
    4. Start your http.Server.
    // 1. Attach the server
    handler, connContext, err := opampServer.Attach(server.Settings{
        EnableCompression: true,
    })
    if err != nil {
        log.Fatal(err)
    }
    
    // 2. Register the handler
    mux := http.NewServeMux()
    mux.HandleFunc("/opamp", handler)
    
    // 3. Configure and start your own http.Server
    httpSrv := &http.Server{
        Addr:         "127.0.0.1:4320",
        Handler:      mux,
        ConnContext:  connContext,
    }
    
    if err := httpSrv.ListenAndServe(); err != nil {
        log.Fatal(err)
    }
  10. OpAMPServer Interface

    main

    The OpAMPServer interface defines the core capabilities of an OpAMP server implementation.

    MethodDescription
    Attach(settings Settings) (HTTPHandlerFunc, ConnContext, error)Prepares the server to handle requests from an existing http.Server. Returns a handler and a connection context.
    Start(settings StartSettings) errorStarts its own internal http.Server using the provided settings. This call blocks until the server is ready to accept connections.
    Stop(ctx context.Context) errorStops accepting new connections and closes all current connections. This blocks until both the socket and all connections are closed.
    Addr() net.AddrReturns the network address the server is listening on. Returns nil if the server is not started. Useful for retrieving the port if 0 was used in ListenEndpoint to allocate an ephemeral port.
  11. Start and Stop an OpAMPClient

    main

    The Start and Stop methods manage the lifecycle of the OpAMP connection.

    • Start(ctx context.Context, settings types.StartSettings) error:

      • Initiates connection attempts to the Server.
      • It is non-blocking; it returns before the connection is actually established.
      • Requirement: SetAgentDescription() MUST be called before Start().
      • Guarantees: If Start returns without error, the client will eventually trigger one of OnConnectFailed, OnError, or OnRemoteConfig (unless Stop is called first).
      • Constraint: Should be called only once and not concurrently with other methods.
    • Stop(ctx context.Context) error:

      • Gracefully shuts down the client.
      • Requirement: May only be called after Start() has returned successfully.
      • Guarantees: Once Stop returns, no further callbacks will be triggered. It waits for in-flight callbacks to finish, so ensure your callbacks react to context cancellation and do not block indefinitely.
    // Start signature
    Start(ctx context.Context, settings types.StartSettings) error
    
    // Stop signature
    Stop(ctx context.Context) error
  12. Report Agent status and health

    main

    Use these methods to communicate the agent's current state to the OpAMP Server. Most of these can be called before or after Start() and are typically used within OnMessage handlers.

    • SetAgentDescription(descr *protobufs.AgentDescription) error: Sets attributes of the Agent. Must not be nil. If called after Start(), attributes are included in the next status report.
    • AgentDescription() *protobufs.AgentDescription: Returns the last successfully set description.
    • SetHealth(health *protobufs.ComponentHealth) error: Sets the health status. Must not be nil.
    • UpdateEffectiveConfig(ctx context.Context) error: Fetches the current local effective configuration (via the GetEffectiveConfig callback) and sends it to the Server.
    • SetRemoteConfigStatus(status *protobufs.RemoteConfigStatus) error: Reports the current remote configuration status. LastRemoteConfigHash must be non-nil.
    • SetPackageStatuses(statuses *protobufs.PackageStatuses) error: Reports current package statuses. ServerProvidedAllPackagesHash must be non-nil.