Skip to content

Resolve the product variant once at startup - #5131

Open
caseydavenport wants to merge 9 commits into
tigera:masterfrom
caseydavenport:casey-variant-resolution
Open

Resolve the product variant once at startup#5131
caseydavenport wants to merge 9 commits into
tigera:masterfrom
caseydavenport:casey-variant-resolution

Conversation

@caseydavenport

@caseydavenport caseydavenport commented Jul 31, 2026

Copy link
Copy Markdown
Member

The operator decides which variant it is running in four places: the --variant flag, Enterprise CRD discovery, the Installation spec, and the Installation status. The one gating most controllers is CRD discovery, which is not what the user set and stays true after a downgrade because the CRDs outlive the variant change.

This resolves the variant once from the Installation before any controller starts, and restarts the operator from a single watch when it changes. Design: https://github.com/tigera/designs/pull/89 (CORE-13240).

Description

  • the variant is resolved in main from the Installation, merging the overlay, and passed to controllers as one option
  • restart-on-variant-change moves to a watch on the manager cache; the reboot in the installation controller goes away, along with its ability to fire mid-uninstall
  • the bootstrap ConfigMap watch moves onto the manager cache too, which drops a goroutine that could never be stopped
  • registration gates key on the variant rather than on whether the Enterprise CRDs exist
  • GetInstallationSpec no longer returns the status variant, so controllers cannot re-derive it
  • the --variant flag is now bootstrap-only and validated; an unrecognised value used to be treated as Calico, which installs the wrong CRDs for the bootstrap-crds path where nothing runs afterwards to correct them
  • updateCRDs installs CRDs for the overlay-merged variant. It read the spec variant before the overlay was merged in, so an overlay that set the variant got the wrong CRD set.
  • fillDefaults defaults to the process variant instead of hardcoding Calico, so it and main cannot disagree

Behaviour changes worth calling out:

  • an Enterprise install whose Installation omits the variant now comes up as Enterprise. Previously fillDefaults overrode it to Calico.
  • asking for Enterprise without the Enterprise CRDs now fails at startup with a message rather than degrading on every reconcile.

Also fixes a latent ImageSet bug. During an Enterprise install the status variant lags the spec, so controllers asked for a calico- ImageSet. GetImageSet only errors when an ImageSet with the matching prefix exists, so on a cluster carrying only enterprise-<version> it found nothing, returned no error, and the components deployed with built-in image tags. Air-gapped clusters fail to pull; everyone else silently gets floating tags instead of the digests they pinned.

Fixes an issue where components could be deployed with default image tags instead of the images from a configured ImageSet during a Calico Enterprise installation.

The operator runs as a single variant for the lifetime of the process, and
restarts when the Installation asks for a different one.
Reuses the informer the controllers already have for Installation instead of
opening a second watch with a hand-built REST client.
Drops the hand-rolled client-go informer and its unstoppable goroutine. The
shared cache isn't filtered, so the handler matches on name and namespace.
The installation controller no longer reboots the operator; the variant watch
handles that. Missing Enterprise CRDs now fail at startup with a message rather
than degrading on every reconcile.
It reports whether the cluster serves the Calico Enterprise APIs, which is no
longer the same question as whether to start those controllers.
Controller registration and reconcile decisions use the variant the process
booted as. GetInstallationSpec no longer returns the status variant, the
enterprise-CRD-exists option is gone, and fillDefaults defaults to the process
variant so main and the installation controller agree.
EnterpriseCRDsExist meant the enterprise CRDs were present, not that the
operator ran as Enterprise. The mainline and pool suites install Calico.
Comment thread cmd/main.go Outdated
Comment thread cmd/main.go Outdated
updateCRDs read the spec variant before the overlay was merged in, so an
overlay that set the variant would get the wrong CRD set.
The bootstrap flag now only ever selects which CRDs to install. Splits the
non-blocking lookup out so the variant watch shares it.
provider: opts.DetectedProvider,
clusterDomain: opts.ClusterDomain,
allowedTLSAssets: allowedAssets(opts.ClusterDomain),
enterprise: opts.Variant.IsEnterprise(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, could you rename the variable to isEnterprise just so it's apparent this is a bool when reading it / answering the question if it's enterprise or not.

tierWatchReady: &utils.ReadyFlag{},
status: status.New(mgr.GetClient(), "gatewayapi", opts.KubernetesVersion),
clusterDomain: opts.ClusterDomain,
variant: opts.Variant,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In some places you've change this to enterprise as a bool, and some you've just passed on the variant. Is there a reason to do this differently, or can we pass the variant everywhere or just pass the enterprise?

Comment thread cmd/main.go
}

// Resolve the variant now that the operator CRDs exist.
bootVariant := waitForVariant(ctx, c, setupLog)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't the boot variant now, is it? It's the real variant, or am I missing something?

Comment thread cmd/main.go
log.Error(err, "Failed to read the Installation, will retry")
case variant != "":
return variant
case !waiting:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the point of the waiting variable just so that we log that info once?

Comment thread cmd/main.go
waiting = true
}

select {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it would be more effective to add a watch instead of poll, since we require a change to happen if the variant isn't set.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants