Skip to content

Bug Report: Incompatibility between Secrets generation and Configuration validation (missing keys, casing mismatch) #200

@meridionaljet

Description

@meridionaljet

Summary

When using pulumiverse-talos (versions 0.5.x and 0.6.x), there is a persistent validation failure when passing machine_secrets generated by the talos:machine:Secrets resource directly into the talos:machine:getConfiguration invoke/function. The validation fails claiming required attributes are missing, even when using consistent talos_version settings.

This appears to be caused by two issues:

  1. Missing Fields: Certain fields required by getConfiguration (e.g., bootstrap_token, secretbox_encryption_secret) are not generated by Secrets for newer Talos versions (v1.11.5).
  2. Casing Mismatch: The Python SDK generates snake_case keys (e.g., k8s_serviceaccount), but the provider's validation logic seemingly expects camelCase keys (e.g., k8sServiceAccount) for nested structures like certs, causing it to reject valid secrets objects.

Environment

  • Provider Version: pulumiverse-talos v0.5.2 and v0.6.1
  • Pulumi SDK: Python
  • Talos Version: v1.11.5 (also tested with v1.9.0)

Steps to Reproduce

  1. Create a talos:machine:Secrets resource.
  2. Pass the machine_secrets output from that resource into talos:machine:get_configuration_output.
  3. Run pulumi up.

Minimal Code Example (Python)

import pulumi
import pulumiverse_talos as talos

# 1. Generate secrets (Explicit version to match config)
secrets = talos.machine.Secrets("secrets", talos_version="v1.11.5")

# 2. Generate config using those secrets
# This is expected to fail with "Missing Configuration" errors
config = talos.machine.get_configuration_output(
    cluster_name="test-cluster",
    machine_type="controlplane",
    cluster_endpoint="https://1.2.3.4:6443",
    machine_secrets=secrets.machine_secrets,
    talos_version="v1.11.5"
)

Observed Errors

The pulumi up execution fails with "Missing Configuration for Required Attribute" errors from the provider validator. We observed a sequence of missing keys:

  1. bootstrap_token: Even when using v1.11.5 (where this is deprecated), validation often requires it.
    • Error: [AttributeName("machine_secrets").AttributeName("secrets").AttributeName("bootstrap_token")] Missing Configuration...
  2. secretbox_encryption_secret: Missing from generated secrets.
  3. k8s_aggregator: Missing from certs.
  4. k8s_serviceaccount: Present in certs (snake_case) but rejected by validation.

Debugging Findings

I inspected the secrets.machine_secrets dictionary during runtime.

  1. Missing Fields: bootstrap_token and secretbox_encryption_secret were absent from the dictionary generated by Secrets(talos_version="v1.11.5").
  2. Casing Mismatch: For certificates like k8s_serviceaccount, the key existed in the dictionary as k8s_serviceaccount (snake_case). However, the validation failed with "Missing Configuration".
    • Fix: Manually injecting the camelCase alias k8sServiceAccount pointing to the same value resolved the error.
    • This suggests the Python SDK produces snake_case keys, but the underlying Go provider/Bridge expects the wire-format camelCase keys for nested structures in validation.

Workaround

I got my test cluster to deploy by implementing a manual patching function to inject missing legacy fields and alias existing fields to camelCase:

def patch_secrets(secrets):
    # Backfill missing legacy tokens
    if not secrets['secrets'].get('bootstrap_token'):
        secrets['secrets']['bootstrap_token'] = 'dummy...'
    
    # Backfill missing encryption keys
    if not secrets['secrets'].get('secretbox_encryption_secret'):
        secrets['secrets']['secretbox_encryption_secret'] = 'dummy...'

    # Fix Casing for Certs
    # Even if 'k8s_serviceaccount' exists, provider demands 'k8sServiceAccount'
    c = secrets['certs']
    
    # Inject both snake_case (if missing) and camelCase aliases
    if c.get('k8s_serviceaccount'):
        c['k8sServiceAccount'] = c['k8s_serviceaccount']
    
    # Inject k8s_aggregator aliases/dummy
    dummy_cert = 'dummy...'
    agg_val = c.get('k8s_aggregator') or dummy_cert
    c['k8s_aggregator'] = agg_val
    c['k8sAggregator'] = agg_val

    return secrets

patched_secrets = secrets.machine_secrets.apply(patch_secrets)

Expected Behavior

  1. Secrets resource should generate all fields required by getConfiguration for the same talos_version.
  2. The Provider/SDK Bridge should automatically handle case conversion (snake_case to camelCase) for nested maps like machine_secrets, or the generated Output should already match the expected schema.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions