Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions api/v1alpha1/multigrescluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ type MultigresClusterSpec struct {
// +optional
Images ClusterImages `json:"images,omitempty"`

// ImageUpdatePolicy controls when the cluster adopts a changed
// operator-default image set. Explicit spec.images values are unaffected.
// +optional
ImageUpdatePolicy *ImageUpdatePolicy `json:"imageUpdatePolicy,omitempty"`

// LogLevels configures the --log-level flag for each multigres data-plane component.
// All fields default to "info" if not specified.
// +optional
Expand Down Expand Up @@ -192,6 +197,106 @@ type ClusterImages struct {
Postgres ImageRef `json:"postgres,omitempty"`
}

// ImageUpdatePolicy controls when a cluster adopts a changed operator-default
// image set.
type ImageUpdatePolicy struct {
// Strategy overrides the operator's --image-update-strategy for this
// cluster: "immediate" adopts a changed default image set on the next
// reconcile; "lazy" holds the current set until acknowledgedRevision
// names the new revision. Empty inherits the operator's flag. This lets
// individual clusters be frozen or advanced independently of the fleet.
// +optional
// +kubebuilder:validation:Enum=immediate;lazy
Strategy string `json:"strategy,omitempty"`

// AcknowledgedRevision authorizes the cluster to adopt the default image
// set with this revision. Under the lazy update strategy, a cluster keeps
// the set it is running until this field names the revision published in
// status.images.availableRevision. A value matching no known revision has
// no effect and is reported through the ImageRolloutPending condition.
// +optional
// +kubebuilder:validation:MaxLength=64
// +kubebuilder:validation:Pattern="^[0-9a-f]*$"
AcknowledgedRevision string `json:"acknowledgedRevision,omitempty"`
}

// ComponentImages is a concrete image for every Multigres component. It
// describes a fully resolved image set.
type ComponentImages struct {
// +optional
Multigateway ImageRef `json:"multigateway,omitempty"`
// +optional
Multiorch ImageRef `json:"multiorch,omitempty"`
// +optional
Multipooler ImageRef `json:"multipooler,omitempty"`
// +optional
Multiadmin ImageRef `json:"multiadmin,omitempty"`
// +optional
MultiadminWeb ImageRef `json:"multiadminWeb,omitempty"`
// +optional
Postgres ImageRef `json:"postgres,omitempty"`
}

// ImageSource describes how the effective component images were selected.
// +kubebuilder:validation:Enum=explicit;defaults;mixed
type ImageSource string

const (
// ImageSourceExplicit means every component image is explicitly pinned in
// spec.images, so operator defaults do not participate.
ImageSourceExplicit ImageSource = "explicit"

// ImageSourceDefaults means every component image comes from the
// operator-managed default set.
ImageSourceDefaults ImageSource = "defaults"

// ImageSourceMixed means spec.images pins some components while the
// operator-managed default set supplies the rest.
ImageSourceMixed ImageSource = "mixed"
)

// ImagesStatus reports the effective component images and, when defaults
// participate, the operator-default rollout state.
type ImagesStatus struct {
// UpdateStrategy is the strategy in effect for this cluster:
// spec.imageUpdatePolicy.strategy when set, otherwise the operator's
// --image-update-strategy flag. It determines whether
// spec.imageUpdatePolicy.acknowledgedRevision has any effect: "lazy"
// consults it, "immediate" ignores it.
// +optional
UpdateStrategy string `json:"updateStrategy,omitempty"`

// Effective is the complete image set currently selected for child
// resources after explicit pins and operator defaults are combined.
Effective ComponentImages `json:"effective"`

// Source identifies whether effective images come from explicit spec pins,
// operator defaults, or a mixture of both.
Source ImageSource `json:"source"`

// Applied is the operator-default image set committed for components that
// are not explicitly set in spec.images. It is empty when source is
// explicit because defaults do not participate.
// +optional
Applied ComponentImages `json:"applied,omitempty"`

// AppliedRevision identifies the applied default image set.
// +optional
AppliedRevision string `json:"appliedRevision,omitempty"`

// Available is the operator's current default image set. Under the lazy
// update strategy, this is the set that AvailableRevision authorizes.
// +optional
Available ComponentImages `json:"available,omitempty"`

// AvailableRevision identifies the operator's current default image set.
// When it differs from appliedRevision, a new set is available but has not
// been adopted yet; with the lazy update strategy, adoption waits for
// spec.imageUpdatePolicy.acknowledgedRevision to be set to this value.
// +optional
AvailableRevision string `json:"availableRevision,omitempty"`
}

// ============================================================================
// Template Defaults Config Section Specs
// ============================================================================
Expand Down Expand Up @@ -519,6 +624,27 @@ const (

// ReasonEndpointReady indicates the external endpoint is serving traffic.
ReasonEndpointReady = "EndpointReady"

// ConditionImageRolloutPending indicates whether a newer operator-default
// image set is available that this cluster has not adopted yet.
ConditionImageRolloutPending = "ImageRolloutPending"

// ReasonAwaitingAcknowledgement indicates a new default image set is
// available and the cluster is holding its current set until
// spec.imageUpdatePolicy.acknowledgedRevision names the new revision.
ReasonAwaitingAcknowledgement = "AwaitingAcknowledgement"

// ReasonRevisionMismatch indicates the acknowledged revision does not
// match the available revision, so it has no effect.
ReasonRevisionMismatch = "RevisionMismatch"

// ReasonImagesUpToDate indicates the cluster runs the operator's current
// default image set.
ReasonImagesUpToDate = "UpToDate"

// ReasonFullyPinned indicates every component image is explicitly set and
// operator-default rollout state does not apply.
ReasonFullyPinned = "FullyPinned"
)

// ============================================================================
Expand Down Expand Up @@ -615,6 +741,11 @@ type MultigresClusterStatus struct {
// successful reconciliation. Used for targeted template-change enqueuing.
// +optional
ResolvedTemplates *ResolvedTemplates `json:"resolvedTemplates,omitempty"`

// Images reports the effective component images and operator-default
// rollout state when defaults participate.
// +optional
Images *ImagesStatus `json:"images,omitempty"`
}

// ResolvedTemplates tracks the template names resolved during reconciliation.
Expand Down Expand Up @@ -656,6 +787,7 @@ type DatabaseStatusSummary struct {
// MultigresCluster represents a distributed database cluster managed by the operator.
// +kubebuilder:resource:shortName=mgc
// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase"
// +kubebuilder:printcolumn:name="Images",type="string",JSONPath=".status.images.appliedRevision",priority=1
// +kubebuilder:validation:XValidation:rule="self.metadata.name.size() <= 25",message="MultigresCluster name must be at most 25 characters"
type MultigresCluster struct {
metav1.TypeMeta `json:",inline"`
Expand Down
58 changes: 58 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions cmd/multigres-operator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import (
multigresv1alpha1 "github.com/multigres/multigres-operator/api/v1alpha1"
multigresclustercontroller "github.com/multigres/multigres-operator/pkg/cluster-handler/controller/multigrescluster"
tablegroupcontroller "github.com/multigres/multigres-operator/pkg/cluster-handler/controller/tablegroup"
"github.com/multigres/multigres-operator/pkg/images"
"github.com/multigres/multigres-operator/pkg/resolver"
cellcontroller "github.com/multigres/multigres-operator/pkg/resource-handler/controller/cell"
shardcontroller "github.com/multigres/multigres-operator/pkg/resource-handler/controller/shard"
Expand Down Expand Up @@ -147,6 +148,17 @@ func main() {
"If set, HTTP/2 will be enabled for the metrics and webhook servers",
)

var imageUpdateStrategy string
flag.StringVar(
&imageUpdateStrategy,
"image-update-strategy",
string(images.UpdateImmediate),
"When clusters adopt a changed default image set: 'immediate' rolls all "+
"clusters as soon as operator configuration changes; 'lazy' holds each "+
"cluster on its current set until spec.imageUpdatePolicy."+
"acknowledgedRevision names the new set's revision.",
)

// Webhook Flag Configuration
flag.IntVar(&webhookPort, "webhook-port", 9443, " The port that the webhook server serves at.")
flag.BoolVar(&webhookEnabled, "webhook-enable", true, "Enable the admission webhook server")
Expand Down Expand Up @@ -181,6 +193,32 @@ func main() {

ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))

if imageUpdateStrategy != string(images.UpdateImmediate) &&
imageUpdateStrategy != string(images.UpdateLazy) {
setupLog.Error(
errors.New("--image-update-strategy must be 'immediate' or 'lazy'"),
"invalid configuration", "value", imageUpdateStrategy,
)
os.Exit(1)
}

defaultImages, imageOverrides := images.DefaultsFromEnv()
imagesConfig := images.Config{
Defaults: defaultImages,
Strategy: images.UpdateStrategy(imageUpdateStrategy),
}
setupLog.Info("resolved default component images",
"revision", images.Revision(defaultImages),
"updateStrategy", imageUpdateStrategy,
"postgres", defaultImages.Postgres,
"multiadmin", defaultImages.Multiadmin,
"multiadminWeb", defaultImages.MultiadminWeb,
"multiorch", defaultImages.Multiorch,
"multipooler", defaultImages.Multipooler,
"multigateway", defaultImages.Multigateway,
"envOverrides", imageOverrides,
)

// Initialize distributed tracing (noop if OTEL_EXPORTER_OTLP_ENDPOINT is unset).
shutdownTracing, err := monitoring.InitTracing(
context.Background(),
Expand Down Expand Up @@ -415,6 +453,7 @@ func main() {
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Recorder: mgr.GetEventRecorderFor("multigrescluster-controller"),
Images: imagesConfig,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "MultigresCluster")
os.Exit(1)
Expand Down
Loading
Loading