Crate datadog_agent_remote_config

Crate datadog_agent_remote_config 

Source
Expand description

Provides a client for remote configuration.

Configuration assigned to this client arrives through polling the Datadog Agent, which fetches it from the Datadog backend. A subscriber supplies a ProductDecoder that names a product and decodes its payloads, then receives typed snapshots through a Subscription. The crate keeps the client’s identity, its protocol cursor (the last targets version), the cached paths it reports to the Agent, configuration path structure, and numeric apply states private.

§Testing

With the test-util feature enabled, [TestPublisher] creates a Subscription that a test publishes into by hand, either with finished snapshots and rejections or by running a decoder over payloads exactly as the client does. A component that takes a Subscription can therefore be tested without an Agent.

§Trust

The client performs no TUF signature verification. It trusts the Agent, reached over an authenticated local IPC channel, to have verified already. It does validate that each payload matches the length and SHA-256 hash published in the accompanying targets metadata, which guards against a bug in delivery.

§Examples

A decoder for a product whose payload is a message, a consumer of its subscription, and a test of both through [TestPublisher]:

use datadog_agent_remote_config::{ConfigId, ProductDecoder, RemoteConfigurationClient, Subscription, TestPublisher};

#[derive(Debug)]
struct Example {
    message: String,
}

/// Keeps the last valid message in ascending configuration ID order.
#[derive(Default)]
struct ExampleDecoder {
    message: Option<String>,
}

impl ProductDecoder for ExampleDecoder {
    const PRODUCT: &'static str = "EXAMPLE_PRODUCT";

    type Snapshot = Example;
    type Error = String;

    fn decode(&mut self, _id: &ConfigId, payload: &[u8]) -> Result<(), Self::Error> {
        let message = std::str::from_utf8(payload).map_err(|_| "Message is not UTF-8.".to_owned())?;
        self.message = Some(message.to_owned());
        Ok(())
    }

    fn build(self) -> Result<Self::Snapshot, Self::Error> {
        let message = self.message.ok_or_else(|| "No message was assigned.".to_owned())?;
        Ok(Example { message })
    }
}

/// Subscribes once where the application is wired together, and hands the subscription to its consumer.
fn wire(rc_client: &RemoteConfigurationClient) -> datadog_agent_remote_config::Result<()> {
    let subscription = rc_client.subscribe::<ExampleDecoder>()?;
    tokio::spawn(consume(subscription));
    Ok(())
}

async fn consume(mut subscription: Subscription<Example>) {
    // A snapshot accepted before the subscription was created is not announced by `changed`, so read it first.
    if let Some(example) = subscription.current() {
        println!("{}", example.message);
    }
    loop {
        match subscription.changed().await {
            Ok(example) => println!("{}", example.message),
            // The client reports the rejection to the Agent; `current` still returns the last accepted snapshot.
            Err(error) => eprintln!("{error}"),
        }
    }
}

// In a test, `TestPublisher` runs the decoder over payloads exactly as the client does.
let (publisher, mut subscription) = TestPublisher::<Example>::new();

publisher.assign::<ExampleDecoder>([("greeting.v1", "hello"), ("greeting.v2", "hi")]);
assert_eq!(subscription.changed().await.unwrap().message, "hi");

// A rejection keeps the last accepted snapshot. This decoder rejects an empty assignment only to show that; a real
// decoder must build from one, or an expired Agent cache leaves its last snapshot live.
publisher.assign::<ExampleDecoder>(Vec::<(&str, &str)>::new());
assert_eq!(*subscription.changed().await.unwrap_err(), "No message was assigned.");
assert_eq!(subscription.current().unwrap().message, "hi");

Structs§

AgentIdentity
What an Agent client reports.
ConfigId
Identifies one configuration assigned to a product.
JsonError
A payload that decode_json could not deserialize.
RcClientConfiguration
Settings for a RemoteConfigurationClient.
RemoteConfigurationClient
A cloneable handle for subscribing to Remote Configuration products.
RemoteConfigurationWorker
Drives polling and delivery for a RemoteConfigurationClient.
Subscription
A handle that observes one product’s configuration as the client publishes it.

Enums§

ClientKind
The kind of client the Agent sees, and the details that kind reports.
Error
An error produced by the remote configuration client.

Traits§

ApplyError
Converts a subscriber’s decoding error into a rejection message for the Agent.
ProductDecoder
Decodes one product’s assigned configurations into a snapshot.

Functions§

decode_json
Deserializes a JSON payload, for a decode implementation to call.

Type Aliases§

Result
A result produced by the remote configuration client.