Repository: bottlerocket-os/bottlerocket
File: sources/models/src/lib.rs (Rust)

Code before:
    cluster_domain: DNSDomain,
    node_ip: IpAddr,
    pod_infra_container_image: SingleLineString,
}

// ECS settings.
#[model]
struct ECSSettings {
    cluster: String,
    instance_attributes: HashMap<ECSAttributeKey, ECSAttributeValue>,
    allow_privileged_containers: bool,
    logging_drivers: Vec<SingleLineString>,
    loglevel: ECSAgentLogLevel,
    enable_spot_instance_draining: bool,
    image_pull_behavior: ECSAgentImagePullBehavior,
}

#[model]
struct RegistryMirror {
    registry: SingleLineString,
    endpoint: Vec<Url>,
}

#[model]
struct RegistryCredential {
    registry: SingleLineString,
    username: SingleLineString,
    password: SingleLineString,
    // This is the base64 encoding of "username:password"
    auth: ValidBase64,
    identitytoken: SingleLineString,
}

// Image registry settings for the container runtimes.
#[model]
struct RegistrySettings {
    #[serde(
        default,
        skip_serializing_if = "Option::is_none",
        deserialize_with = "deserialize_mirrors"
    )]
    mirrors: Vec<RegistryMirror>,
    #[serde(alias = "creds", default, skip_serializing_if = "Option::is_none")]
    credentials: Vec<RegistryCredential>,
}

// Update settings. Taken from userdata. The 'seed' setting is generated
// by the "Bork" settings generator at runtime.
#[model]
struct UpdatesSettings {
    metadata_base_url: Url,

Review comment (suggestion):
> Even though both values must be positive integers, I used i64 instead of u64 for the configurations because the [datastore serializer](https://github.com/bottlerocket-os/bottlerocket/blob/2a202f877fefbbfc54e689b164460ced6d66dacc/sources/api/datastore/src/serialization/pairs.rs#L172) doesn't support u64. I decided against of having a new PositiveIntegerValue custom type to perform validations at the API level for two reasons: a) I don't think we should have (at least not for this PR) a custom type to perform validations for positive integers, instead we should figure out if there is a way to support u64 serialization and b) the ECS agent will provide a [useful message](https://github.com/aws/amazon-ecs-agent/blob/5fb8e801e9d11630c3470abb778291913ebb9d7f/agent/config/config.go#L361) if any of the provided values isn't a positive integer.

I am pretty unhappy with this rationale and frankly just tired of accepting all the recent justifications along these lines. (See also #2463, #2257.)

Especially for the datastore serializer, `i64` seems like it would have the same problems as `u64` would for JSON. Is it actually problematic or does `serde_json` do the right thing in both cases?

I am actually fine with accepting negative values here if `ecs-agent` also tolerates them, which it appears to. What numeric type does it use? It's clearly a signed type. Can we just use that?

(Note that if it's actually an `i64`, then using a `u64` here would be an error that could expose the agent to invalid input.)

Code after:
    cluster_domain: DNSDomain,
    node_ip: IpAddr,
    pod_infra_container_image: SingleLineString,
}

// ECS settings.
#[model]
struct ECSSettings {
    cluster: String,
    instance_attributes: HashMap<ECSAttributeKey, ECSAttributeValue>,
    allow_privileged_containers: bool,
    logging_drivers: Vec<SingleLineString>,
    loglevel: ECSAgentLogLevel,
    enable_spot_instance_draining: bool,
    image_pull_behavior: ECSAgentImagePullBehavior,
    container_stop_timeout: ECSDurationValue,
    task_cleanup_wait: ECSDurationValue,
    metadata_service_rps: i64,
    metadata_service_burst: i64,
    reserved_memory: u16,
}

#[model]
struct RegistryMirror {
    registry: SingleLineString,
    endpoint: Vec<Url>,
}

#[model]
struct RegistryCredential {
    registry: SingleLineString,
    username: SingleLineString,
    password: SingleLineString,
    // This is the base64 encoding of "username:password"
    auth: ValidBase64,
    identitytoken: SingleLineString,
}

// Image registry settings for the container runtimes.
#[model]
struct RegistrySettings {
    #[serde(
        default,
        skip_serializing_if = "Option::is_none",
        deserialize_with = "deserialize_mirrors"
    )]
    mirrors: Vec<RegistryMirror>,
    #[serde(alias = "creds", default, skip_serializing_if = "Option::is_none")]
    credentials: Vec<RegistryCredential>,
}