Skip to content

v2.3.0

Latest

Choose a tag to compare

@sergenyalcin sergenyalcin released this 22 Jun 15:24
b02902e

New Features

Conversion Webhook Decoupled from Leader Election (#678)

⚠️ Action required for providers using conversion webhooks and safe-start feature (gated setup). The webhook registration path has moved out of the controller Setup function. Providers that set StartWebhooks: true need to update their entry point. Providers that do not use conversion webhooks are unaffected.

Conversion webhook registration was gated behind leader election through the SetupGated/gate mechanism. SetupGated stores a closure that only fires on the elected leader, so follower pods never called ctrl.NewWebhookManagedBy and their webhook servers returned 404 for every conversion request. With two replicas and --leader-election, some conversion webhook calls fail.

The fix separates webhook registration from reconciler setup:

  • controller.go.tmpl: adds SetupWebhookWithManager — a standalone function that registers the conversion webhook for a single resource kind. Removes the if o.StartWebhooks { ... } block from Setup; webhook registration is no longer the reconciler's responsibility.

  • setup.go.tmpl: adds the SetupWebhookWithManager{{ .Group }} aggregator, following the same pattern as Setup{{ .Group }} and SetupGated{{ .Group }}, so providers can register all webhooks for a group in a single call.

Providers call SetupWebhookWithManager_ once before mgr.Start() on every pod, independent of the gate and leader election. Reconciler setup remains behind the gate and runs only on the leader.

// OLD generated code (v2.2.0 and earlier) — inside Setup()
if o.StartWebhooks {
    if err := ctrl.NewWebhookManagedBy(mgr, &v1beta1.MyResource{}).
        Complete(); err != nil {
        return errors.Wrap(err, "cannot register webhook for the kind v1beta1.MyResource")
    }
}

What changed

The if o.StartWebhooks { ... } block has been removed from the generated Setup() function entirely. In its place, each generated resource controller file now exposes a standalone function:

// NEW generated code (v2.3.0) — standalone function, not inside Setup()
func SetupWebhookWithManager(mgr ctrl.Manager) error {
    if err := ctrl.NewWebhookManagedBy(mgr, &v1beta1.MyResource{}).
        Complete(); err != nil {
        return errors.Wrap(err, "cannot register webhook for the kind v1beta1.MyResource")
    }
    return nil
}

The group-level zz_setup.go file also gains a corresponding aggregator:

func SetupWebhookWithManagerMyGroup(mgr ctrl.Manager) error {
    for _, setup := range []func(ctrl.Manager) error{
        myresourceSetupWebhookWithManager,
        // ... all resources in this group
    } {
        if err := setup(mgr); err != nil {
            return err
        }
    }
    return nil
}

The StartWebhooks bool field remains on tjcontroller.Options for now but has no effect on the generated code and will be removed in a future release.

Provider side:

// Webhooks are registered eagerly on all pods before mgr.Start() so that
// every replica (leader and followers alike) can serve conversion requests.
// Reconciler setup is deferred to the gate and only runs on the leader.
startWebhooks := *certsDir != ""
if startWebhooks {
    kingpin.FatalIfError(clustercontroller.SetupWebhookWithManager_accesscontextmanager(mgr), "Cannot setup cluster-scoped webhooks")
    kingpin.FatalIfError(namespacedcontroller.SetupWebhookWithManager_accesscontextmanager(mgr), "Cannot setup namespaced webhooks")
}

MR API Lifecycle Versioning & CRD Version Management (#539, #563, #567, #585)

Upjet now includes a comprehensive framework for managing Managed Resource API versioning across CRD versions. This introduces:

  • New configuration APIs (LifecycleConfiguration, storage/hub version hooks) in pkg/config/resource.go and pkg/pipeline/ for generating multi-version CRDs
  • A pkg/config/conversion package with built-in converters (optional field, field type change) and helpers for registering conversion functions automatically
  • A new cmd/schemadiff CLI tool for schema change detection, integrated with the crddiff tool for detecting breaking changes between CRD versions
  • Documentation guide for managing CRD versions (docs/breaking-change-detection.md)

API Roundtrip Testing Library (#636)

A new testing library (pkg/test/roundtrip) enables API roundtrip tests for generated provider types:

  • Fuzzer-based serialization roundtrip testing with configurable fuzz functions
  • EquateNilAndZeroValuePtr comparison helper for comparing nil pointers and zero-value pointers

ReconciliationPolicy Support (#665, #668)

A new ReconciliationPolicy API type (apis/v1alpha1/reconciliation_policy.go) allows per-resource control over reconciliation rate limiting:

  • Configurable exponential failure rate limiter with per-resource baseDelay and maxDelay
  • New reconciliationpolicy reconciler and finalizer implementations in pkg/reconciler/reconciliationpolicy/
  • handler.WithDefaultRateLimiter option to make the default rate limiter for handler.EventHandler configurable (#668)

Resource Identity Support for TF Plugin Framework (#623)

The Terraform Plugin Framework integration now supports resource identity:

  • Identity is passed to the framework only when the resource supports it
  • Missing Resource Identity diagnostics are now treated as resource-not-found, enabling proper observe behavior

Customizable Controller Setup Template (#655)

Providers can now override the controller setup template used by pipeline.ControllerGenerator via config.Provider. New documentation has been added for main and controller template variables (docs/controller-template-variables.md, docs/main-template-variables.md).

Provider LocalName Support (#566)

Added an optional LocalName field to ProviderRequirement. This allows explicit override of the derived provider local name in Terraform configuration — useful for providers where the resource prefix doesn't match the source path (e.g., port-labs/port-labs).

Bug Fixes

  • Race condition in async MR status (#670): Fixed a race condition on MR status between async callbacks and the managed reconciler. Async Plugin SDKv2 and Framework external clients now use deep copies for Create/Delete operations.
  • Dotted map keys with correct nesting (#635): Fixed incorrect nesting when setting dotted map keys for sensitive values.
  • Panic on null prior in proposedNewAttributes (#641): Fixed a panic in the TF Plugin Framework client when the prior state is null.
  • Panic on nested attributes in collection types (#606): Fixed a panic while diffing TF framework resources with nested attribute collection types.
  • Nested attributes inside blocks (#558): Handle nested attributes inside Terraform blocks correctly during schema traversal.
  • Reconstructed state flush after observation failures (#555, #564): Both the Framework and TF SDK external clients now flush reconstructed states for recalculation after observation failures, fixing stale state issues.
  • Runtime compatibility (#602): Fixed compatibility issues with controller-runtime and apimachinery (required JSON tags), and bumped crossplane-runtime to v2.2.0.

Security

  • go.opentelemetry.io/otel updated to v1.41.0
  • github.com/antchfx/xpath updated to v1.3.6
  • google.golang.org/grpc updated to v1.79.3

Dependency & CI Updates

  • Go version bumped to 1.25.8
  • Migrated to golangci-lint v2 (v2.10.1)
  • Updated GitHub Actions: actions/checkout@v6, actions/cache@v5, actions/setup-go@v6, codecov/codecov-action@v6, github/codeql-action@v4, zeebe-io/backport-action@v4, fsfe/reuse-action@v6
  • Added Renovate configuration for automated dependency management
  • Added test.race make target and parallel CI job for race detection

What's Changed

  • Move mergenci to emeritus maintainers by @sergenyalcin in #556
  • Add doc on managing CRD versions by @jeanduplessis in #539
  • ci: Bump golangci-lint version to v2.6.1 by @erhancagirici in #557
  • framework-external-client: Flush reconstructed states for recalculation after Observation failures by @erhancagirici in #555
  • Handle nested attributes inside blocks by @kangasta in #558
  • Adding new conversion functions and handling their runtime aspects by @sergenyalcin in #563
  • tfsdk external client: Flush reconstructed states for recalculation after Observation failures by @erhancagirici in #564
  • Configure Renovate by @renovate[bot] in #287
  • Update zeebe-io/backport-action action to v4 by @renovate[bot] in #575
  • Update actions/checkout action to v6 by @renovate[bot] in #572
  • Update actions/cache action to v5 by @renovate[bot] in #571
  • Update actions/github-script action to v8 by @renovate[bot] in #573
  • Update codecov/codecov-action action to v5 by @renovate[bot] in #574
  • Update fsfe/reuse-action action to v6 by @renovate[bot] in #577
  • Update github/codeql-action action to v4 by @renovate[bot] in #582
  • Update actions/setup-go action to v6 by @renovate[bot] in #580
  • Update dependency ubuntu to v24 by @renovate[bot] in #581
  • Update golangci/golangci-lint-action action to v9.2.0 by @renovate[bot] in #578
  • Add CodeRabbit AI codereview agent configuration by @jeanduplessis in #540
  • Add one pager to discuss Upjet MR API Lifecycle Versioning Policy by @sergenyalcin in #560
  • feat(terraform): add LocalName field to ProviderRequirement by @acarter-wl in #566
  • docs: Update README with up-to-date information by @jeanduplessis in #587
  • Add new configuration APIs/mechanisms for starting to introduce new MR API version policy by @sergenyalcin in #585
  • Update all non-major github action by @renovate[bot] in #586
  • Update dependency golangci/golangci-lint to v2.10.1 by @renovate[bot] in #576
  • Update all non-major github action by @renovate[bot] in #608
  • Fix panic while diffing tf framework resources with nested attributes collection types by @ravilr in #606
  • Update all non-major github action by @renovate[bot] in #613
  • bump crossplane-runtime to v2.2.0 by @fernandezcuesta in #602
  • Update module google.golang.org/grpc to v1.79.3 [SECURITY] by @renovate[bot] in #616
  • feat: add resource identity support to TF Plugin Framework integration by @erikmiller-gusto in #623
  • Update module github.com/antchfx/xpath to v1.3.6 [SECURITY] by @renovate[bot] in #626
  • Update all non-major github action by @renovate[bot] in #620
  • Update codecov/codecov-action action to v6 by @renovate[bot] in #622
  • Update kubernetes patches by @renovate[bot] in #619
  • fix panic when null prior in proposedNewAttributes by @stevendborrelli in #641
  • ci: add codecov token by @erhancagirici in #643
  • fix: set dotted map keys with correct nesting by @erhancagirici in #635
  • feat: API roundtrip testing library by @erhancagirici in #636
  • Update module go.opentelemetry.io/otel to v1.41.0 [SECURITY] by @renovate[bot] in #637
  • Update all non-major github action by @renovate[bot] in #630
  • Allow custom controller template via config.Provider by @ulucinar in #655
  • Update all non-major github action by @renovate[bot] in #650
  • Integrate schema change detection and add some helpers for registering the conversion functions automatically by @sergenyalcin in #567
  • Add Support for ReconciliationPolicy Configurations by @ulucinar in #665
  • Make the Default Rate Limiter for the Event Handler Configurable by @ulucinar in #668
  • Fix Race on MR Status Between Async Callbacks & Managed Reconciler by @ulucinar in #670
  • feat: add Description field to SSA InjectedKey by @fernandezcuesta in #672
  • Decouple conversion webhook registration from leader election by @sergenyalcin in #678

New Contributors

Full Changelog: v2.2.0...v2.3.0