Skip to content

Commit 90858c1

Browse files
stubbiclaude
andauthored
feat: automatic image updates via OCI registry digest polling (#8)
* feat: add automatic image update polling via OCI registry digest checks When spec.image.autoUpdate.enabled is true, the operator periodically queries the container registry for the current digest of the configured tag. When a new digest is detected, it injects a pod annotation that triggers a rolling StatefulSet update. - AutoUpdateSpec on ImageSpec with enabled/interval fields - AutoUpdateStatus tracking lastCheckTime, resolvedDigest, lastUpdateTime - OCI registry client (internal/registry) with bearer token auth flow - Supports GHCR, Docker Hub, and private registries via imagePullSecrets - Pod annotation-based rollout (same mechanism as kubectl rollout restart) - Requeue-based polling (idiomatic controller-runtime, leader-election safe) - 11 new tests (registry client + StatefulSet annotation injection) Usage: spec: image: tag: latest autoUpdate: enabled: true interval: 5m Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove unused nolint directives flagged by nolintlint CI uses golangci-lint v2.1.0 which doesn't flag gosec G704 on these lines, making the nolint:gosec directives unused. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add #nosec G704 for standalone gosec SSRF findings The registry client makes HTTP requests to operator-configured registry URLs (not untrusted user input). Add #nosec G704 directives for the standalone gosec scanner used in CI security scan. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 34add3f commit 90858c1

10 files changed

Lines changed: 760 additions & 17 deletions

File tree

api/v1alpha1/paperclipinstance_types.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,24 @@ type ImageSpec struct {
155155
// PullSecrets specifies image pull secrets.
156156
// +optional
157157
PullSecrets []corev1.LocalObjectReference `json:"pullSecrets,omitempty"`
158+
159+
// AutoUpdate enables automatic image updates by polling the registry for new digests.
160+
// +optional
161+
AutoUpdate *AutoUpdateSpec `json:"autoUpdate,omitempty"`
162+
}
163+
164+
// AutoUpdateSpec configures automatic image update polling.
165+
type AutoUpdateSpec struct {
166+
// Enabled controls whether auto-update polling is active.
167+
// +kubebuilder:default=false
168+
// +optional
169+
Enabled bool `json:"enabled,omitempty"`
170+
171+
// Interval is the polling interval (e.g. "5m", "1h"). Minimum is 1m.
172+
// +kubebuilder:default="5m"
173+
// +kubebuilder:validation:Pattern=`^\d+(s|m|h)$`
174+
// +optional
175+
Interval string `json:"interval,omitempty"`
158176
}
159177

160178
// DeploymentSpec controls deployment mode and exposure.
@@ -727,6 +745,10 @@ type InstanceStatus struct {
727745
// Restore tracks the state of the latest restore operation.
728746
// +optional
729747
Restore *RestoreStatus `json:"restore,omitempty"`
748+
749+
// AutoUpdate tracks the state of automatic image update checks.
750+
// +optional
751+
AutoUpdate *AutoUpdateStatus `json:"autoUpdate,omitempty"`
730752
}
731753

732754
// ManagedResources tracks the names of managed Kubernetes resources.
@@ -775,6 +797,25 @@ type RestoreStatus struct {
775797
Result string `json:"result,omitempty"`
776798
}
777799

800+
// AutoUpdateStatus tracks the state of automatic image update checks.
801+
type AutoUpdateStatus struct {
802+
// LastCheckTime is when the operator last queried the registry.
803+
// +optional
804+
LastCheckTime *metav1.Time `json:"lastCheckTime,omitempty"`
805+
806+
// ResolvedDigest is the most recently observed digest for the configured tag.
807+
// +optional
808+
ResolvedDigest string `json:"resolvedDigest,omitempty"`
809+
810+
// LastUpdateTime is when the digest last changed and a rollout was triggered.
811+
// +optional
812+
LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"`
813+
814+
// LastError records the most recent error from a registry check, if any.
815+
// +optional
816+
LastError string `json:"lastError,omitempty"`
817+
}
818+
778819
// +kubebuilder:object:root=true
779820
// +kubebuilder:subresource:status
780821
// +kubebuilder:resource:shortName=pci

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

charts/paperclip-operator/templates/crds/paperclip.inc_instances.yaml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3636,6 +3636,22 @@ spec:
36363636
image:
36373637
description: Image specifies the Paperclip container image to deploy.
36383638
properties:
3639+
autoUpdate:
3640+
description: AutoUpdate enables automatic image updates by polling
3641+
the registry for new digests.
3642+
properties:
3643+
enabled:
3644+
default: false
3645+
description: Enabled controls whether auto-update polling
3646+
is active.
3647+
type: boolean
3648+
interval:
3649+
default: 5m
3650+
description: Interval is the polling interval (e.g. "5m",
3651+
"1h"). Minimum is 1m.
3652+
pattern: ^\d+(s|m|h)$
3653+
type: string
3654+
type: object
36393655
digest:
36403656
description: Digest overrides the tag with an image digest (e.g.
36413657
sha256:abc...).
@@ -7410,6 +7426,29 @@ spec:
74107426
status:
74117427
description: InstanceStatus defines the observed state of Instance.
74127428
properties:
7429+
autoUpdate:
7430+
description: AutoUpdate tracks the state of automatic image update
7431+
checks.
7432+
properties:
7433+
lastCheckTime:
7434+
description: LastCheckTime is when the operator last queried the
7435+
registry.
7436+
format: date-time
7437+
type: string
7438+
lastError:
7439+
description: LastError records the most recent error from a registry
7440+
check, if any.
7441+
type: string
7442+
lastUpdateTime:
7443+
description: LastUpdateTime is when the digest last changed and
7444+
a rollout was triggered.
7445+
format: date-time
7446+
type: string
7447+
resolvedDigest:
7448+
description: ResolvedDigest is the most recently observed digest
7449+
for the configured tag.
7450+
type: string
7451+
type: object
74137452
backup:
74147453
description: Backup tracks the state of the latest backup operation.
74157454
properties:

cmd/main.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939

4040
paperclipv1alpha1 "github.com/paperclipinc/paperclip-operator/api/v1alpha1"
4141
"github.com/paperclipinc/paperclip-operator/internal/controller"
42+
"github.com/paperclipinc/paperclip-operator/internal/registry"
4243
// +kubebuilder:scaffold:imports
4344
)
4445

@@ -203,9 +204,10 @@ func main() {
203204
}
204205

205206
if err := (&controller.InstanceReconciler{
206-
Client: mgr.GetClient(),
207-
Scheme: mgr.GetScheme(),
208-
Recorder: mgr.GetEventRecorderFor("paperclip-operator"),
207+
Client: mgr.GetClient(),
208+
Scheme: mgr.GetScheme(),
209+
Recorder: mgr.GetEventRecorderFor("paperclip-operator"),
210+
RegistryClient: registry.NewClient(nil),
209211
}).SetupWithManager(mgr); err != nil {
210212
setupLog.Error(err, "unable to create controller", "controller", "Instance")
211213
os.Exit(1)

config/crd/bases/paperclip.inc_instances.yaml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3630,6 +3630,22 @@ spec:
36303630
image:
36313631
description: Image specifies the Paperclip container image to deploy.
36323632
properties:
3633+
autoUpdate:
3634+
description: AutoUpdate enables automatic image updates by polling
3635+
the registry for new digests.
3636+
properties:
3637+
enabled:
3638+
default: false
3639+
description: Enabled controls whether auto-update polling
3640+
is active.
3641+
type: boolean
3642+
interval:
3643+
default: 5m
3644+
description: Interval is the polling interval (e.g. "5m",
3645+
"1h"). Minimum is 1m.
3646+
pattern: ^\d+(s|m|h)$
3647+
type: string
3648+
type: object
36333649
digest:
36343650
description: Digest overrides the tag with an image digest (e.g.
36353651
sha256:abc...).
@@ -7404,6 +7420,29 @@ spec:
74047420
status:
74057421
description: InstanceStatus defines the observed state of Instance.
74067422
properties:
7423+
autoUpdate:
7424+
description: AutoUpdate tracks the state of automatic image update
7425+
checks.
7426+
properties:
7427+
lastCheckTime:
7428+
description: LastCheckTime is when the operator last queried the
7429+
registry.
7430+
format: date-time
7431+
type: string
7432+
lastError:
7433+
description: LastError records the most recent error from a registry
7434+
check, if any.
7435+
type: string
7436+
lastUpdateTime:
7437+
description: LastUpdateTime is when the digest last changed and
7438+
a rollout was triggered.
7439+
format: date-time
7440+
type: string
7441+
resolvedDigest:
7442+
description: ResolvedDigest is the most recently observed digest
7443+
for the configured tag.
7444+
type: string
7445+
type: object
74077446
backup:
74087447
description: Backup tracks the state of the latest backup operation.
74097448
properties:

0 commit comments

Comments
 (0)