A Kubernetes manifest often holds several documents separated by ---. Read each one into a struct, or edit one of them and write the whole file back with its comments.
The file:
# Web frontend apiVersion: apps/v1 kind: Deployment metadata: name: web spec: template: spec: containers: - name: web image: registry.example.com/web:1.4.2 # pinned by release tooling --- apiVersion: v1 kind: Service metadata: name: web spec: ports: - port: 80 targetPort: 8080
The program:
use oneyaml::{Deserializer, Stream}; use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] struct Object { api_version: String, kind: String, metadata: Metadata, } #[derive(Debug, Deserialize)] struct Metadata { name: String, } fn main() -> Result<(), Box<dyn std::error::Error>> { let src = std::fs::read_to_string("app.yaml")?; // Typed: one Deserializer per document. for doc in Deserializer::from_str(&src) { let obj = Object::deserialize(doc)?; println!("{} {} ({})", obj.kind, obj.metadata.name, obj.api_version); } // Lossless: edit the Deployment, keep everything else byte for byte. let mut stream = Stream::parse(src)?; let i = stream .documents() .position(|d| d.get_path("kind").and_then(|n| n.as_str()).as_deref() == Some("Deployment")) .ok_or("no Deployment")?; let mut deploy = stream.document_mut(i).ok_or("no document")?; deploy.set_path("spec.template.spec.containers[0].image", "registry.example.com/web:1.5.0")?; print!("{stream}"); Ok(()) }
Deployment web (apps/v1) Service web (v1) # Web frontend apiVersion: apps/v1 kind: Deployment metadata: name: web spec: template: spec: containers: - name: web image: registry.example.com/web:1.5.0 # pinned by release tooling --- apiVersion: v1 kind: Service metadata: name: web spec: ports: - port: 80 targetPort: 8080
Good to know
- Iterating a
Deserializeryields one deserializer per document, in order. A document you don’t deserialize before asking for the next is skipped. oneyaml::from_strreads a single document. Given a stream with more than one, it’s an error, not a silent read of the first.Stream::parsekeeps every document with its comments.stream.documents()gives a read view of each, anddocument_mut(i)edits one in place. Each document writes its own region of the file, so the Service’s text is untouched.- The default limits include a cap on documents per stream. For manifests from outside, use
Limits::untrusted()(read untrusted YAML safely).
The crate repository has a fuller example that edits a Deployment and a Service and checks every comment’s owner: k8s_manifest.rs.