Arnavion / k8s-openapi

Rust definitions of the resource types in the Kubernetes client API
Apache License 2.0
379 stars 41 forks source link

Feature request: deserialize k8s yaml files #45

Closed andygrove closed 5 years ago

andygrove commented 5 years ago

First of all, thanks for creating this crate. It is really great. I am using it in https://github.com/andygrove/ballista and it is working well. However, I have a new requirement now that I don't think there is a solution for.

I would like to be able to parse k8s yaml templates and then do the equivalent of kubectl apply -f yamlfile. To implement this, I need the ability to deserialize yaml into the standard API structs. Is there any way to do this using this crate?

Arnavion commented 5 years ago

Every resource type impls serde::Deserialize, so you first identify what the apiVersion and kind of the document are, and then use the corresponding type as the deserialization target:

let api_version_key = serde_yaml::Value::String("apiVersion".to_owned());
let kind_key = serde_yaml::Value::String("kind".to_owned());

let file = std::fs::File::open("...")?;
let file: serde_yaml::Sequence = serde_yaml::from_reader(file)?;
for document in file {
    if let serde_yaml::Value::Mapping(mapping) = &document {
        let api_version = mapping.get(&api_version_key).and_then(serde_yaml::Value::as_str);
        let kind = mapping.get(&kind_key).and_then(serde_yaml::Value::as_str);
        match (api_version, kind) {
            (Some(api_version), Some(kind)) if
                api_version == <k8s_openapi::api::core::v1::Pod as k8s_openapi::Resource>::api_version() &&
                kind == <k8s_openapi::api::core::v1::Pod as k8s_openapi::Resource>::kind() =>
            {
                let pod: k8s_openapi::api::core::v1::Pod = serde::Deserialize::deserialize(document)?;
                ...
            },

            (Some(api_version), Some(kind)) if
                api_version == <k8s_openapi::api::apps::v1::Deployment as k8s_openapi::Resource>::api_version() &&
                kind == <k8s_openapi::api::apps::v1::Deployment as k8s_openapi::Resource>::kind() =>
            {
                let deployment: k8s_openapi::api::apps::v1::Deployment = serde::Deserialize::deserialize(document)?;
                ...
            },

            ...
        }
    }
}

A macro will help to reduce the boilerplate of the match arms.

It does require you to have a static mapping of apiVersion + kind to type. Not sure there's a way around that.

But note that kubectl apply -f / kubectl create -f do not parse the template as structured types. They parse them as unstructured types (runtime.Object, ie the equivalent of serde_yaml::Value) and POST them directly.