Module: Pvectl::ManifestSerializer

Defined in:
lib/pvectl/manifest_serializer.rb

Overview

Wraps ConfigSerializer output with a kubectl-like manifest envelope (apiVersion, kind, metadata, spec). Handles bidirectional conversion between YAML manifest files and internal config representations.

Constant Summary collapse

API_VERSION =
"pvectl/v1"
KINDS =
{
  vm: "VirtualMachine",
  container: "Container"
}.freeze
KINDS_REVERSE =
KINDS.invert.transform_keys(&:to_s).freeze

Class Method Summary collapse

Class Method Details

.from_yaml(yaml_string) ⇒ Hash

Parses a YAML manifest string into type, metadata, and spec.

Parameters:

  • yaml_string (String)

    YAML manifest

Returns:

  • (Hash)

    { type: Symbol, metadata: Hash, spec: Hash }



41
42
43
44
45
46
47
48
49
50
# File 'lib/pvectl/manifest_serializer.rb', line 41

def from_yaml(yaml_string)
  parsed = YAML.safe_load(yaml_string)
  type = KINDS_REVERSE[parsed["kind"]]

  {
    type: type,
    metadata: (parsed["metadata"] || {}),
    spec: symbolize_keys_deep(parsed["spec"] || {})
  }
end

.to_yaml(nested_config, type:, metadata:) ⇒ String

Builds a YAML manifest string from nested config and metadata.

Parameters:

  • nested_config (Hash)

    nested config from ConfigSerializer.to_nested

  • type (Symbol)

    :vm or :container

  • metadata (Hash)

    resource metadata (vmid, name, node, status, tags)

Returns:

  • (String)

    YAML manifest string



26
27
28
29
30
31
32
33
34
35
# File 'lib/pvectl/manifest_serializer.rb', line 26

def to_yaml(nested_config, type:, metadata:)
  manifest = {
    "apiVersion" => API_VERSION,
    "kind" => KINDS.fetch(type),
    "metadata" => (),
    "spec" => stringify_keys_deep(nested_config)
  }

  YAML.dump(manifest)
end

.validate(yaml_string) ⇒ Array<String>

Validates manifest structure (envelope only, not spec content).

Parameters:

  • yaml_string (String)

    YAML manifest

Returns:

  • (Array<String>)

    error messages (empty if valid)



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/pvectl/manifest_serializer.rb', line 56

def validate(yaml_string)
  errors = []

  begin
    parsed = YAML.safe_load(yaml_string)
  rescue Psych::SyntaxError => e
    return ["YAML syntax error: #{e.message}"]
  end

  unless parsed.is_a?(Hash)
    return ["Invalid manifest: expected a YAML mapping"]
  end

  unless parsed["apiVersion"]
    errors << "Missing required field 'apiVersion'"
  end

  if parsed["apiVersion"] && parsed["apiVersion"] != API_VERSION
    errors << "Unsupported apiVersion '#{parsed["apiVersion"]}'. Expected: #{API_VERSION}"
  end

  errors << "Missing required field 'kind'" unless parsed["kind"]

  if parsed["kind"] && !KINDS_REVERSE.key?(parsed["kind"])
    errors << "Unknown kind '#{parsed["kind"]}'. Valid: #{KINDS.values.join(', ')}"
  end

   = parsed["metadata"]
  if .nil? || !.is_a?(Hash)
    errors << "Missing required field 'metadata'"
  end

  errors
end