Skip to content

Conversation

@fl64
Copy link
Member

@fl64 fl64 commented Oct 20, 2025

Description

This PR adds the ability to safely edit or/and remove the generic VirtualMachineClass resource without it being automatically recreated by the virtualization module.

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

Previously, if users deleted the generic VirtualMachineClass (either accidentally or intentionally), the virtualization module would automatically recreate it on the next restart or update. Also, it may stuck in Terminating phase.

What is the expected result?

  • Users can safely remove generic VMClass
  • System remembers VMClass was created and won't recreate it automatically

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: module
type: feature 
summary: add ability to edit or remove generic vmclass

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Oct 20, 2025

Reviewer's Guide

Implement stateful management of the generic VirtualMachineClass by tracking its creation in a module-state Secret, updating the create hook to respect user deletions, adding a state update hook, and introducing a Helm label cleanup hook while removing legacy static defaults.

Sequence diagram for generic VirtualMachineClass creation and state tracking

sequenceDiagram
    participant Hook_create_generic_vmclass as Hook: create-generic-vmclass
    participant K8s_API as K8s API
    participant Secret_module_state as Secret: module-state
    participant Hook_update_module_state as Hook: update-module-state
    participant User
    User->>K8s_API: Delete generic VirtualMachineClass
    Hook_create_generic_vmclass->>K8s_API: Check if generic VMClass exists
    alt VMClass does not exist
        Hook_create_generic_vmclass->>Secret_module_state: Check generic-vmclass-created flag
        alt generic-vmclass-created == true
            Hook_create_generic_vmclass-->>K8s_API: Do nothing
        else generic-vmclass-created != true
            Hook_create_generic_vmclass->>K8s_API: Create generic VMClass
            Hook_update_module_state->>Secret_module_state: Set generic-vmclass-created=true
        end
    else VMClass exists
        Hook_create_generic_vmclass-->>K8s_API: Do nothing
    end
    Hook_update_module_state->>Secret_module_state: Update generic-vmclass-created flag if VMClass exists
Loading

Sequence diagram for Helm label cleanup on generic VMClass

sequenceDiagram
    participant Hook_drop_helm_labels_from_generic_vmclass as Hook: drop-helm-labels-from-generic-vmclass
    participant K8s_API as K8s API
    Hook_drop_helm_labels_from_generic_vmclass->>K8s_API: Get generic VMClass metadata
    alt VMClass has Helm labels
        Hook_drop_helm_labels_from_generic_vmclass->>K8s_API: Remove Helm labels from generic VMClass
    else VMClass does not have Helm labels
        Hook_drop_helm_labels_from_generic_vmclass-->>K8s_API: Do nothing
    end
Loading

Entity relationship diagram for module-state Secret and VirtualMachineClass

erDiagram
    MODULE_STATE_SECRET {
        string generic-vmclass-created
    }
    VIRTUAL_MACHINE_CLASS {
        string name
        map labels
        CPU cpu
        SizingPolicy[] sizingPolicies
    }
    MODULE_STATE_SECRET ||--o| VIRTUAL_MACHINE_CLASS : tracks creation
Loading

Class diagram for ModuleState and VMClassMetadata types

classDiagram
    class ModuleState {
        +bool GenericVMClassCreated
        +map<string, byte[]> ToSecretData()
        +map<string, any> ToPatchData()
    }
    class VMClassMetadata {
        +string Name
        +map<string, string> Labels
    }
Loading

File-Level Changes

Change Details Files
Track generic VMClass creation state in a module-state Secret
  • Add update-module-state hook to maintain generic-vmclass-created flag
  • Decode and encode state in Secret data on every run
  • Introduce ModuleState type with ToSecretData/ToPatchData helpers
  • Cover state transitions with comprehensive unit tests
images/hooks/pkg/hooks/update-module-state/hook.go
images/hooks/pkg/hooks/update-module-state/hook_test.go
Respect user deletions in create-generic-vmclass hook
  • Skip creating generic VMClass when state Secret marks it created
  • Retain legacy logic to create class when no state or false flag
  • Add unit tests for all state and existence combinations
images/hooks/pkg/hooks/create-generic-vmclass/hook.go
images/hooks/pkg/hooks/create-generic-vmclass/hook_test.go
Remove Helm-injected labels from generic VMClass post-Helm
  • Add drop-helm-labels-from-generic-vmclass hook to strip managed-by and heritage labels
  • Use JSON patch to remove labels if present
  • Write tests covering label presence/absence and selector mismatches
images/hooks/pkg/hooks/drop-helm-labels-from-generic-vmclass/hook.go
images/hooks/pkg/hooks/drop-helm-labels-from-generic-vmclass/hook_test.go
Remove legacy default VMClass manifests and prevent-default hook
  • Delete prevent-default-vmclasses-deletion hook
  • Remove static vmclasses-default.yaml template
images/hooks/pkg/hooks/prevent-default-vmclasses-deletion/hook.go
templates/virtualization-controller/vmclasses-default.yaml
Add virtualization-module-hooks registration entry
  • Create register.go stub for hook wiring
  • Bootstrap hooks under images/hooks/cmd/virtualization-module-hooks
images/hooks/cmd/virtualization-module-hooks/register.go

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

@fl64
Copy link
Member Author

fl64 commented Oct 20, 2025

@sourcery-ai review

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 - here's some feedback:

  • In create-generic-vmclass/hook.go the constant createGenericVMClassHookName is both typo’d (“steate”) and unused—either correct it with a meaningful name or remove it to avoid confusion.
  • The default VMClass spec (CPU model and sizing policies) is hard-coded in Reconcile; consider extracting these defaults into shared constants or config to simplify future adjustments.
  • Base64 encode/decode logic is duplicated across both hooks—extract a small utility function to centralize this and reduce repetition.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In create-generic-vmclass/hook.go the constant createGenericVMClassHookName is both typo’d (“steate”) and unused—either correct it with a meaningful name or remove it to avoid confusion.
- The default VMClass spec (CPU model and sizing policies) is hard-coded in Reconcile; consider extracting these defaults into shared constants or config to simplify future adjustments.
- Base64 encode/decode logic is duplicated across both hooks—extract a small utility function to centralize this and reduce repetition.

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.

@fl64 fl64 force-pushed the chore/module/add-abbility-to-remove-vmclass branch from e683936 to dddb2c4 Compare October 20, 2025 06:33
@fl64
Copy link
Member Author

fl64 commented Oct 20, 2025

@sourcery-ai summary

@fl64 fl64 force-pushed the chore/module/add-abbility-to-remove-vmclass branch 4 times, most recently from 09a7345 to 7ac9050 Compare October 21, 2025 05:22
@fl64 fl64 added this to the v1.2.0 milestone Oct 21, 2025
@fl64 fl64 force-pushed the chore/module/add-abbility-to-remove-vmclass branch 4 times, most recently from d10961f to 28b219e Compare October 21, 2025 10:11
fl64 added 8 commits October 22, 2025 08:06
Signed-off-by: Pavel Tishkov <[email protected]>
Signed-off-by: Pavel Tishkov <[email protected]>
Signed-off-by: Pavel Tishkov <[email protected]>
Signed-off-by: Pavel Tishkov <[email protected]>
Signed-off-by: Pavel Tishkov <[email protected]>
Signed-off-by: Pavel Tishkov <[email protected]>
@fl64 fl64 force-pushed the chore/module/add-abbility-to-remove-vmclass branch from 28b219e to 576270a Compare October 22, 2025 05:06
fl64 added 6 commits October 22, 2025 08:20
Signed-off-by: Pavel Tishkov <[email protected]>
Signed-off-by: Pavel Tishkov <[email protected]>
Signed-off-by: Pavel Tishkov <[email protected]>
Signed-off-by: Pavel Tishkov <[email protected]>
Signed-off-by: Pavel Tishkov <[email protected]>
Signed-off-by: Pavel Tishkov <[email protected]>
@fl64 fl64 changed the title chore(module): add ability to remove generic vmclass feat(module): add ability to edit or remove generic vmclass Oct 22, 2025
@fl64 fl64 marked this pull request as ready for review October 23, 2025 13:11
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 - here's some feedback:

  • In the update-module-state hook you always patch the secret even when the state hasn't changed; consider skipping the patch when newState equals currentState to reduce unnecessary API calls.
  • The default VMClass spec is fully hardcoded in the create hook; extracting the sizing policies and CPU model into constants or making them configurable would improve maintainability.
  • There's repeated base64 encoding/decoding and secret data handling logic across hooks; consider extracting these operations into a shared utility to avoid duplication.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the update-module-state hook you always patch the secret even when the state hasn't changed; consider skipping the patch when newState equals currentState to reduce unnecessary API calls.
- The default VMClass spec is fully hardcoded in the create hook; extracting the sizing policies and CPU model into constants or making them configurable would improve maintainability.
- There's repeated base64 encoding/decoding and secret data handling logic across hooks; consider extracting these operations into a shared utility to avoid duplication.

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.

Comment on lines 39 to 40
helmManagedByLabel = "app.kubernetes.io/managed-by"
helmHeritageLabel = "heritage"
Copy link
Member

Choose a reason for hiding this comment

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

There are also 2 annotations to remove:

meta.helm.sh/release-name: <module name>
meta.helm.sh/release-namespace: <module namespace>

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

Comment on lines 102 to 111
if genericCreatedEncoded, exists := moduleStateData["generic-vmclass-created"]; exists {
if encodedStr, ok := genericCreatedEncoded.(string); ok {
if decodedBytes, err := base64.StdEncoding.DecodeString(encodedStr); err == nil {
genericCreated := string(decodedBytes) == "true"
if genericCreated {
return nil
}
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

There is a simpler solution: unmarshal into struct with []byte field, so encode/json package will automatically decode base64.

Or unmarshal into corev1.Secret.

JqFilter:   `{"metadata": .metadata, "data": .data}`,

...

var moduleStateSecret corev1.Secret

if err := moduleStateSecrets[0].UnmarshalTo[&moduleStateSecret]; err != nil {
 ...
...

// Selected block of 10 lines will be 3 lines only:
if string(moduleStateSecret.Data["generic-vmclass-created"]) == "true" {
   return nil
}

Copy link
Member Author

Choose a reason for hiding this comment

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

done

}
}

func Reconcile(_ context.Context, input *pkg.HookInput) error {
Copy link
Member

Choose a reason for hiding this comment

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

It seems this hook can be combined with create-generic-vmclass. Same snapshots, same trigger.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think we will use this state in the future to preserve another state.

Comment on lines +123 to +126
Labels: map[string]string{
"app": "virtualization-controller",
"module": settings.ModuleName,
},
Copy link
Member

Choose a reason for hiding this comment

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

May be add a label with some kind of "spec-version" for this vmclass?

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't understand, but why?

return nil
}

// if module-state secret exists and contains generic-vmclass-created=true, nothing to do
Copy link
Member

Choose a reason for hiding this comment

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

Strange name for the flag. vmclass created, but there is no vmclass =) May be something like "vmclass-generic-was-initiated"?

Copy link
Member Author

Choose a reason for hiding this comment

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

generic-vmclass-was-ever-created?)

@fl64 fl64 requested a review from diafour November 1, 2025 06:15

var _ = registry.RegisterFunc(configDropHelmLabels, handlerDropHelmLabels)

var configDropHelmLabels = &pkg.HookConfig{
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
var configDropHelmLabels = &pkg.HookConfig{
// This hook runs at start (ExecuteOnSynchronization: true) to drop helm labels before helm execution.
var configDropHelmLabels = &pkg.HookConfig{

"module": settings.ModuleName,
},
},
ExecuteHookOnEvents: ptr.To(false),
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
ExecuteHookOnEvents: ptr.To(false),
ExecuteHookOnEvents: ptr.To(false),
ExecuteHookOnSynchronization: ptr.To(true),

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.

3 participants