Repository: bottlerocket-os/bottlerocket
File: sources/settings-migrations/v1.40.0/kubelet-device-plugins-cdi-settings/src/main.rs (Rust)

Code before:
use migration_helpers::{error, migrate, Migration, MigrationData, Result};
use snafu::OptionExt;

const DEVICE_LIST_STRATEGY_SETTING: &str =
    "settings.kubelet-device-plugins.nvidia.device-list-strategy";

#[snafu::report]
fn main() -> Result<()> {
    migrate(ReplaceDeviceListStrategy)
}

/// We changed the type of the device-list-strategy in the NVIDIA Kubernetes Device
/// plugin API from a string to a list, and accept "cdi-cri" as a valid value
pub struct ReplaceDeviceListStrategy;

impl Migration for ReplaceDeviceListStrategy {
    /// New versions must either have a default for the settings or generate them; we don't need to
    /// do anything.
    fn forward(&mut self, input: MigrationData) -> Result<MigrationData> {
        println!("ReplaceDeviceListStrategy has no work to do on upgrade.",);
        Ok(input)
    }

    /// Older versions don't know about the settings; we remove them so that old versions don't see
    /// them and fail deserialization.  (The settings must be defaulted or generated in new versions,
    /// and safe to remove.)
    fn backward(&mut self, mut input: MigrationData) -> Result<MigrationData> {
        let setting = DEVICE_LIST_STRATEGY_SETTING;
        if let Some(data) = input.data.get_mut(setting) {
            match data {
                serde_json::Value::Array(arr) => {
                    let list: Vec<&str> = arr
                        .iter()
                        .map(|v| v.as_str())
                        .collect::<Option<Vec<&str>>>()
                        .context(error::ReplaceListContentsSnafu {
                            setting,
                            data: arr.clone(),

Review comment (suggestion):
Changing it to list is not quite correct here right, now it supports both string and list. Maybe "from string to a more flexible type that supports both string and list"? This explains why the `forward` can be a no-op.

Code after:
use migration_helpers::{error, migrate, Migration, MigrationData, Result};
use snafu::OptionExt;

const DEVICE_LIST_STRATEGY_SETTING: &str =
    "settings.kubelet-device-plugins.nvidia.device-list-strategy";

#[snafu::report]
fn main() -> Result<()> {
    migrate(ReplaceDeviceListStrategy)
}

/// We changed the type of the device-list-strategy in the NVIDIA Kubernetes Device
/// plugin API from a string to a list, and accept "cdi-cri" as a valid value
pub struct ReplaceDeviceListStrategy;

impl Migration for ReplaceDeviceListStrategy {
    /// New versions must either have a default for the settings or generate them; we don't need to
    /// do anything.
    fn forward(&mut self, input: MigrationData) -> Result<MigrationData> {
        println!("ReplaceDeviceListStrategy has no work to do on upgrade.");
        Ok(input)
    }

    /// Older versions don't know about the setting now accepting both a string, a list and a new accepted value "cdi-cri";
    /// we remove the list option and the "cdi-cri" value so that old versions don't see them and fail deserialization.
    /// (The settings must be defaulted or generated in new versions, and safe to remove.)
    fn backward(&mut self, mut input: MigrationData) -> Result<MigrationData> {
        let setting = DEVICE_LIST_STRATEGY_SETTING;
        if let Some(data) = input.data.get_mut(setting) {
            match data {
                serde_json::Value::Array(arr) => {
                    let list: Vec<&str> = arr
                        .iter()
                        .map(|v| v.as_str())
                        .collect::<Option<Vec<&str>>>()
                        .context(error::ReplaceListContentsSnafu {
                            setting,
                            data: arr.clone(),