Class: RKSeal::SealedSecret
- Inherits:
-
Object
- Object
- RKSeal::SealedSecret
- Defined in:
- lib/rkseal/sealed_secret.rb
Overview
Domain model for the SealedSecret resource – the encrypted, on-disk counterpart of Secret.
Unlike a Secret, a SealedSecret cannot be decrypted client-side: its ‘spec.encryptedData` values are opaque ciphertext. What is readable is the set of data keys (the map keys are plaintext), the sealing scope (a metadata annotation), and the template `type`. That readable surface is exactly what powers the offline `edit –local` flow: rkseal can show the user which keys exist and let them keep / replace / add / remove keys without ever seeing the current values.
This model is the single place that knows how to:
- parse a local `<name>.yaml` SealedSecret into that readable surface;
- render a *redacted* editor buffer -- a Secret manifest in which every
existing key is shown under `stringData` as {REDACTED_PLACEHOLDER}, so a
value left untouched means "keep the current ciphertext".
No method here shells out, touches the cluster, or decrypts anything; it is pure data transformation and trivially unit-testable.
Constant Summary collapse
- API_VERSION =
apiVersion/kind this model represents.
"bitnami.com/v1alpha1"- KIND =
"SealedSecret"- REDACTED_PLACEHOLDER =
Placeholder shown for every existing key in the ‘edit –local` buffer. Because the ciphertext cannot be decrypted, the current value is never revealed; leaving this token in place means “keep the sealed value as-is”.
"<redacted>"
Instance Attribute Summary collapse
-
#encrypted_keys ⇒ Array<String>
readonly
The data keys present in ‘spec.encryptedData` (plaintext keys; the values are ciphertext and are not held here).
-
#name ⇒ String
readonly
The SealedSecret name.
-
#namespace ⇒ String?
readonly
The namespace.
-
#scope ⇒ Symbol
readonly
Sealing scope (:strict, :namespace_wide, :cluster_wide), derived from the metadata annotation (see RKSeal::Secret.scope_from_sealed_json).
-
#type ⇒ String
readonly
The template Secret ‘type` (e.g. “Opaque”).
Class Method Summary collapse
-
.diverged?(local, cluster) ⇒ Boolean
Whether the sealed payload (‘spec.encryptedData` + `spec.template`) of two SealedSecret documents differs.
-
.parse(yaml) ⇒ RKSeal::SealedSecret
Parse a SealedSecret manifest (the local ‘<name>.yaml`) into the model.
Instance Method Summary collapse
-
#initialize(name:, namespace:, scope:, type:, encrypted_keys:) ⇒ SealedSecret
constructor
A new instance of SealedSecret.
-
#to_buffer(commented: true, string_data: false) ⇒ String
Render the redacted editor buffer for the offline local edit: a Kubernetes Secret manifest in which every existing key is shown as REDACTED_PLACEHOLDER.
Constructor Details
#initialize(name:, namespace:, scope:, type:, encrypted_keys:) ⇒ SealedSecret
Returns a new instance of SealedSecret.
149 150 151 152 153 154 155 |
# File 'lib/rkseal/sealed_secret.rb', line 149 def initialize(name:, namespace:, scope:, type:, encrypted_keys:) @name = name @namespace = namespace @scope = scope @type = type @encrypted_keys = encrypted_keys.freeze end |
Instance Attribute Details
#encrypted_keys ⇒ Array<String> (readonly)
Returns the data keys present in ‘spec.encryptedData` (plaintext keys; the values are ciphertext and are not held here).
47 48 49 |
# File 'lib/rkseal/sealed_secret.rb', line 47 def encrypted_keys @encrypted_keys end |
#name ⇒ String (readonly)
Returns the SealedSecret name.
37 38 39 |
# File 'lib/rkseal/sealed_secret.rb', line 37 def name @name end |
#namespace ⇒ String? (readonly)
Returns the namespace.
39 40 41 |
# File 'lib/rkseal/sealed_secret.rb', line 39 def namespace @namespace end |
#scope ⇒ Symbol (readonly)
Returns sealing scope (:strict, :namespace_wide, :cluster_wide), derived from the metadata annotation (see RKSeal::Secret.scope_from_sealed_json).
42 43 44 |
# File 'lib/rkseal/sealed_secret.rb', line 42 def scope @scope end |
#type ⇒ String (readonly)
Returns the template Secret ‘type` (e.g. “Opaque”).
44 45 46 |
# File 'lib/rkseal/sealed_secret.rb', line 44 def type @type end |
Class Method Details
.diverged?(local, cluster) ⇒ Boolean
Whether the sealed payload (‘spec.encryptedData` + `spec.template`) of two SealedSecret documents differs. `kubectl apply` stores the manifest verbatim, so right after a deploy the local `<name>.yaml` and the cluster object share an identical payload; an unequal payload therefore means the local file is *ahead of* (or absent from) the cluster – i.e. it carries un-deployed changes. Re-sealing is non-deterministic, so equal payload is only ever produced by the exact same applied file – there are no false “equal” verdicts that could mask drift. Tolerant of JSON (kubectl) and YAML (the local file) alike, and of malformed input (treated as drift, so the user’s local file is never silently overwritten).
86 87 88 |
# File 'lib/rkseal/sealed_secret.rb', line 86 def diverged?(local, cluster) sealed_payload(local) != sealed_payload(cluster) end |
.parse(yaml) ⇒ RKSeal::SealedSecret
Parse a SealedSecret manifest (the local ‘<name>.yaml`) into the model.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/rkseal/sealed_secret.rb', line 56 def parse(yaml) doc = yaml.is_a?(Hash) ? yaml : load_yaml(yaml) unless doc.is_a?(Hash) raise InvalidInputError, "not a SealedSecret manifest (expected a YAML mapping)" end validate_kind!(doc) new( name: fetch_name(doc), namespace: doc.dig("metadata", "namespace"), scope: Secret.scope_from_sealed_json(doc), type: doc.dig("spec", "template", "type") || Secret::DEFAULT_TYPE, encrypted_keys: encrypted_keys(doc) ) end |
Instance Method Details
#to_buffer(commented: true, string_data: false) ⇒ String
Render the redacted editor buffer for the offline local edit: a Kubernetes Secret manifest in which every existing key is shown as REDACTED_PLACEHOLDER. The operator keeps a value by leaving the placeholder, replaces it by typing a new value, adds keys by adding lines, and removes keys by deleting lines.
By default the keys sit under ‘data` (so replacements are base64, matching the rest of the tool). With `string_data: true` they sit under `stringData`, so replacements/new keys are entered as plaintext.
171 172 173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/rkseal/sealed_secret.rb', line 171 def to_buffer(commented: true, string_data: false) body = { "apiVersion" => Secret::API_VERSION, "kind" => Secret::KIND, "metadata" => { "name" => name, "namespace" => namespace }, "type" => type, (string_data ? "stringData" : "data") => encrypted_keys.to_h { |key| [key, REDACTED_PLACEHOLDER] } } yaml = YAML.dump(body).delete_prefix("---\n") commented ? "#{buffer_header(string_data)}#{yaml}" : yaml end |