Skip to content

Conversation

@loktev-d
Copy link
Contributor

@loktev-d loktev-d commented Oct 24, 2025

Description

Implements periodic monitoring of DVCR (Deckhouse Virtualization Container Registry) image presence for VirtualImage and ClusterVirtualImage resources.

Key components:

  • ImageChecker: New component (pkg/dvcr/image_checker.go) that verifies image existence in DVCR registry using go-containerregistry
    • Performs lightweight HEAD requests to check image manifests
  • Periodic Reconciliation: Uses controller-runtime's CronSource to trigger reconciliation on a configurable schedule
    • Default: hourly (0 * * * *)
    • Configurable via module config: dvcr.imageMonitorSchedule
    • Can be disabled by setting empty string
  • Phase Management: New ImageLost phase added to track when images are deleted from DVCR
    • ImageLost: Image manifest not found in DVCR registry
    • ImagePVCLost: PVC backing the image was deleted (existing, renamed from PVCLost)
  • Optimizations:
    • Custom field indexes (IndexVIByPhaseAndStorage, IndexCVIByPhase) to filter resources at API level

Why do we need it, and what problem does it solve?

When images are deleted from DVCR (either manually or due to storage issues), the VirtualImage/ClusterVirtualImage resources continue to show Ready phase even though the underlying image data is gone. This creates a misleading state where users think images are available but VM creation fails.

This feature provides automated detection of missing images and updates resource status accordingly, giving users accurate visibility into image availability.

What is the expected result?

Checklist

  • The code is covered by unit tests.
  • e2e tests passed.
  • Documentation updated according to the changes.
  • Changes were tested in the Kubernetes cluster manually.

Changelog entries

section: images
type: feature
summary:  Added monitoring of DVCR image presence for VirtualImage and ClusterVirtualImage

Signed-off-by: Daniil Loktev <[email protected]>
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Oct 24, 2025

Reviewer's Guide

Introduces a periodic DVCR image presence monitor for VirtualImage and ClusterVirtualImage using the existing GC framework and minimal API surface changes.

Sequence diagram for periodic DVCR image presence monitoring

sequenceDiagram
  participant CronScheduler
  participant ImageMonitorManager
  participant DVCRRegistry
  participant K8sAPI
  CronScheduler->>ImageMonitorManager: Trigger ListForDelete()
  ImageMonitorManager->>K8sAPI: List VirtualImage/ClusterVirtualImage
  loop For each Ready image
    ImageMonitorManager->>DVCRRegistry: CheckImageExists(registryURL)
    alt Image exists
      ImageMonitorManager-->>CronScheduler: No action
    else Image missing
      ImageMonitorManager->>K8sAPI: Update Status.Phase to ImageLost
      ImageMonitorManager->>K8sAPI: Set Condition Reason=ImageLost
    end
  end
Loading

ER diagram for updated VirtualImage and ClusterVirtualImage status phases

erDiagram
  VIRTUALIMAGE {
    string id
    ImagePhase phase
    string progress
    string registry_url
  }
  CLUSTERVIRTUALIMAGE {
    string id
    ImagePhase phase
    string progress
    string registry_url
  }
  IMAGEPHASE {
    string name
  }
  VIRTUALIMAGE ||--o| IMAGEPHASE : "has phase"
  CLUSTERVIRTUALIMAGE ||--o| IMAGEPHASE : "has phase"
  IMAGEPHASE {
    Pending
    Provisioning
    WaitForUserUpload
    Ready
    Failed
    Terminating
    ImageLost
    PVCLost
  }
Loading

Class diagram for DVCR image presence monitoring

classDiagram
  class VirtualImage {
    +Status: VirtualImageStatus
    +Spec: VirtualImageSpec
  }
  class ClusterVirtualImage {
    +Status: ClusterVirtualImageStatus
    +Spec: ClusterVirtualImageSpec
  }
  class VirtualImageStatus {
    +Phase: ImagePhase
    +Progress: string
    +Conditions: []Condition
    +Target: TargetInfo
  }
  class ClusterVirtualImageStatus {
    +Phase: ImagePhase
    +Progress: string
    +Conditions: []Condition
    +Target: TargetInfo
  }
  class ImagePhase {
    <<enum>>
    Pending
    Provisioning
    WaitForUserUpload
    Ready
    Failed
    Terminating
    ImageLost
    PVCLost
  }
  class ImageMonitorManager {
    +client: Client
    +dvcrSettings: DVCRSettings
    +New()
    +ShouldBeDeleted(obj)
    +ListForDelete(ctx, now)
    +checkImage(ctx, registryURL)
    +markImageLost(ctx, obj, registryURL)
    +getAuthCredentials(ctx)
  }
  class DVCRSettings {
    +AuthSecret: string
    +AuthSecretNamespace: string
    +InsecureTLS: string
  }
  class ImageChecker {
    <<interface>>
    +CheckImageExists(ctx, imageURL)
  }
  class DefaultImageChecker {
    +username: string
    +password: string
    +insecure: bool
    +CheckImageExists(ctx, imageURL)
  }
  VirtualImageStatus --> ImagePhase
  ClusterVirtualImageStatus --> ImagePhase
  ImageMonitorManager --> DVCRSettings
  ImageMonitorManager --> ImageChecker
  DefaultImageChecker ..|> ImageChecker
Loading

File-Level Changes

Change Details Files
Extend GC settings to include image monitor schedule
  • Add GcImageMonitorScheduleVar
  • Implement GetImageMonitorSettingsFromEnv
  • Add NewDefaultImageMonitorSettings
pkg/config/load_gc_settings.go
Implement image-monitor controllers for VI and CVI
  • Setup GC cron source
  • List ready CRs and check DVCR via ImageChecker
  • Update status to ImageLost on absence
pkg/controller/vi/image_monitor.go
pkg/controller/cvi/image_monitor.go
Provide DVCR ImageChecker
  • Use go-containerregistry HEAD request
  • Support basic auth and TLS options
  • Error handling for not-found vs other failures
pkg/dvcr/checker.go
Enhance CRDs with ImageLost/PVCLost semantics
  • Add ImageLost and PVCLost reasons and phases
  • Update enum validation to include new values
  • Deprecate Lost alias
api/core/v1alpha2/virtual_image.go
api/core/v1alpha2/cluster_virtual_image.go
api/core/v1alpha2/image_status.go
api/core/v1alpha2/vicondition/condition.go
api/core/v1alpha2/cvicondition/condition.go
Align PVC source controller with PVCLost
  • Rename Lost reason to PVCLost
  • Use ImagePVCLost phase for PVC errors
  • Update tests accordingly
pkg/controller/vi/internal/source/step/ready_pvc_step.go
pkg/controller/vi/internal/source/sources.go
pkg/controller/vi/internal/source/object_ref_vdsnapshot_pvc_test.go
Report new phases in metrics
  • Include ImageLost and ImagePVCLost in scraper phases
pkg/monitoring/metrics/vi/scraper.go
pkg/monitoring/metrics/cvi/scraper.go
Bump dependencies for image checking
  • Add google/go-containerregistry
  • Update docker/cli version
go.mod

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@loktev-d loktev-d marked this pull request as draft October 24, 2025 13:15
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `images/virtualization-artifact/pkg/dvcr/checker.go:126-127` </location>
<code_context>
+		contains(errStr, "404")
+}
+
+// contains checks if a string contains a substring (case-insensitive helper).
+func contains(s, substr string) bool {
+	return len(s) >= len(substr) && (s == substr || containsHelper(s, substr))
+}
</code_context>

<issue_to_address>
**suggestion:** Use strings.Contains for substring checks instead of custom implementation.

Refactor the 'contains' and 'containsHelper' functions to use 'strings.Contains' for improved readability and reliability.

Suggested implementation:

```golang
import "strings"

// contains checks if a string contains a substring.
func contains(s, substr string) bool {
	return strings.Contains(s, substr)
}

```

If there is a `containsHelper` function elsewhere in the file, it should be removed as it is no longer needed.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Signed-off-by: Daniil Loktev <[email protected]>
@loktev-d loktev-d added this to the v1.1.2 milestone Nov 1, 2025
loktev-d and others added 5 commits November 1, 2025 16:15
Signed-off-by: Daniil Loktev <[email protected]>
Signed-off-by: Daniil Loktev <[email protected]>
Signed-off-by: Daniil Loktev <[email protected]>
Signed-off-by: Daniil Loktev <[email protected]>
@universal-itengineer universal-itengineer removed this from the v1.1.2 milestone Nov 5, 2025
@Isteb4k Isteb4k added this to the v1.2.0 milestone Nov 5, 2025
@loktev-d loktev-d added the e2e/run Run e2e test on cluster of PR author label Nov 18, 2025
@deckhouse-BOaTswain
Copy link
Contributor

deckhouse-BOaTswain commented Nov 18, 2025

Workflow has started.
Follow the progress here: Workflow Run

The target step completed with status: failure.

@deckhouse-BOaTswain deckhouse-BOaTswain removed the e2e/run Run e2e test on cluster of PR author label Nov 18, 2025
@loktev-d loktev-d added the e2e/run Run e2e test on cluster of PR author label Nov 20, 2025
@deckhouse-BOaTswain
Copy link
Contributor

deckhouse-BOaTswain commented Nov 20, 2025

Workflow has started.
Follow the progress here: Workflow Run

The target step completed with status: failure.

@deckhouse-BOaTswain deckhouse-BOaTswain removed the e2e/run Run e2e test on cluster of PR author label Nov 20, 2025
@Isteb4k Isteb4k modified the milestones: v1.2.0, v1.3.0 Nov 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants