Skip to content

Conversation

@hardcoretime
Copy link
Contributor

@hardcoretime hardcoretime commented Jan 29, 2026

Description

Cloud-init userdata in the virtual machine specification should not be empty or exceed 2048 KB.

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

These changes are required to prevent the virtual machine from entering a pending status due to incorrect cloud-init userdata.

What is the expected result?

Webhook prevents virtual machine creation or update with incorrect cloud-init userdata and Provisioning Handler return an error if resource was created with invalidated data.

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: vm
type: feature
summary: "Cloud-init userdata is now validated during virtual machine creation and updates."

@hardcoretime hardcoretime added this to the v1.5.0 milestone Jan 29, 2026
@hardcoretime hardcoretime changed the title feat(vm): validate cloud init userdata feat(vm): validate cloud-init userdata Jan 29, 2026
@hardcoretime hardcoretime force-pushed the feat/vm/validate-cloud-init-userdata branch from d5074c3 to 150f8a7 Compare January 29, 2026 23:41
@hardcoretime hardcoretime added the e2e/run Run e2e test on cluster of PR author label Jan 29, 2026
@deckhouse-BOaTswain
Copy link
Contributor

deckhouse-BOaTswain commented Jan 29, 2026

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 Jan 30, 2026
@hardcoretime hardcoretime force-pushed the feat/vm/validate-cloud-init-userdata branch from 150f8a7 to 4c1d8d4 Compare January 30, 2026 11:49
Signed-off-by: Roman Sysoev <roman.sysoev@flant.com>
@hardcoretime hardcoretime force-pushed the feat/vm/validate-cloud-init-userdata branch 2 times, most recently from d831e4b to 298e709 Compare January 30, 2026 14:36
Signed-off-by: Roman Sysoev <roman.sysoev@flant.com>
@hardcoretime hardcoretime force-pushed the feat/vm/validate-cloud-init-userdata branch from 298e709 to c907d80 Compare January 30, 2026 15:01
@hardcoretime hardcoretime added validation/skip/no_cyrillic Skip no_cyrillic validation validation/skip/doc_changes Skip doc changes validation and removed validation/skip/no_cyrillic Skip no_cyrillic validation labels Jan 30, 2026
@hardcoretime hardcoretime marked this pull request as ready for review January 30, 2026 16:00
return nil
}

func (p *ProvisioningService) validateSysprepSecret(_ *corev1.Secret) error {
Copy link
Contributor

@Isteb4k Isteb4k Jan 30, 2026

Choose a reason for hiding this comment

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

Overengineering. Code that does nothing. Remove everything unnecessary

Keep It Simple

}

func (p *ProvisioningService) ValidateUserDataLen(userData string) error {
if userData == "" {
Copy link
Contributor

Choose a reason for hiding this comment

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

trim it before validation

validate := len(checkKeys) == 0
for _, key := range checkKeys {
if _, ok := secret.Data[key]; ok {
validate = true
Copy link
Contributor

@Isteb4k Isteb4k Jan 30, 2026

Choose a reason for hiding this comment

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

The variable is named incorrectly, and you can actually get rid of it

Keep It Simple

err := p.reader.Get(ctx, key, secret)
if err != nil {
if k8serrors.IsNotFound(err) {
return SecretNotFoundError(key.String())
Copy link
Contributor

Choose a reason for hiding this comment

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

Overengineering. The error looks cool, but you’re not using its advantages. You’re just putting it into a condition afterward. Just return a simple fmt.Errorf

Keep It Simple

provisioningService *service.ProvisioningService
}

func NewProvisioningValidator(provisioningService *service.ProvisioningService) *ProvisioningValidator {
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like you can use CEL rules instead

reader client.Reader
}

func NewProvisioningService(reader client.Reader) *ProvisioningService {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this here and not inside the vm directory?


func (p *ProvisioningService) validateCloudInitSecret(secret *corev1.Secret) error {
if !p.hasOneOfKeys(secret, cloudInitCheckKeys...) {
return fmt.Errorf("the secret should have one of data fields %v: %w", cloudInitCheckKeys, ErrSecretIsNotValid)
Copy link
Contributor

Choose a reason for hiding this comment

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

"the specified provisioning secret %q must have at least one of the following fields: %v: %w"

builder.Status(metav1.ConditionFalse).
Reason(vmcondition.ReasonProvisioningNotReady).
Message(service.CapitalizeFirstLetter(err.Error()))
return nil
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a bug. It’s not the user’s fault. Shouldn’t this be validated at the CRD level?

case errors.Is(err, service.ErrSecretIsNotValid):
builder.Status(metav1.ConditionFalse).
Reason(vmcondition.ReasonProvisioningNotReady).
Message(fmt.Sprintf("Invalid secret %q: %s", secretKey.String(), err.Error()))
Copy link
Contributor

Choose a reason for hiding this comment

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

It should already be clear from err.Error() that the secret failed validation. No need to duplicate this "Invalid secret"

} else {
err := h.provisioningService.ValidateUserDataLen(p.UserData)
if err != nil {
errMsg := fmt.Errorf("failed to validate userdata length: %w", err)
Copy link
Contributor

Choose a reason for hiding this comment

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

err = fmt.Errorf("failed to validate userdata length: %w", err)

No need to create one more variable

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

Labels

validation/skip/doc_changes Skip doc changes validation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants