Skip to content

Commit

Permalink
add krel validation cmd
Browse files Browse the repository at this point in the history
  • Loading branch information
npolshakova committed Sep 17, 2024
1 parent dbda6e5 commit 92cdea1
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 0 deletions.
10 changes: 10 additions & 0 deletions cmd/krel/cmd/testdata/validation-data/invalid-indent.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pr: 126108
releasenote:
text: |-
Reduced state change noise when volume expansion fails. Also mark certain failures as infeasible.
ACTION REQUIRED: If you are using the `RecoverVolumeExpansionFailure` alpha feature gate
then after upgrading to this release, you need to update some objects.
For any existing PersistentVolumeClaimss with `status.allocatedResourceStatus` set to either
"ControllerResizeFailed" or "NodeResizeFailed", clear the `status.allocatedResourceStatus`.
pr_body: ""
5 changes: 5 additions & 0 deletions cmd/krel/cmd/testdata/validation-data/invalid-multi-line.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 125163
releasenote:
text: 'ACTION REQUIRED: The Dynamic Resource Allocation (DRA) driver's DaemonSet must be deployed
with a service account that enables writing ResourceSlice and reading ResourceClaim
objects.'
3 changes: 3 additions & 0 deletions cmd/krel/cmd/testdata/validation-data/invalid-yaml-start.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pr: 125157
releasenote:
text: `kubeadm`: The `NodeSwap` check that kubeadm performs during preflight, has a new warning to verify if swap has been configured correctly.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pr: 125157
releasenote:
text: "`kubeadm`: The `NodeSwap` check that kubeadm performs during preflight, has a new warning to verify if swap has been configured correctly"
pr_body: ""
4 changes: 4 additions & 0 deletions cmd/krel/cmd/testdata/validation-data/valid.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pr: 125157
releasenote:
text: "`kubeadm`: The `NodeSwap` check that kubeadm performs during preflight, has a new warning to verify if swap has been configured correctly."
pr_body: ""
119 changes: 119 additions & 0 deletions cmd/krel/cmd/validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package cmd

import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"

"github.com/spf13/cobra"
"k8s.io/release/pkg/notes"
"sigs.k8s.io/yaml"
)

func init() {
// Add the validation subcommand to the root command
rootCmd.AddCommand(validateCmd)
}

// validate represents the subcommand for `krel validate`.
var validateCmd = &cobra.Command{
Use: "validate",
Short: "The subcommand for validating release notes for the Release Notes subteam of SIG Release",
Long: `krel validate <path-to-release-notes-maps>
The 'validate' subcommand of krel has been developed to:
1. Check release notes maps for valid yaml.
2. Check release notes maps for valid punctuation.`,
SilenceUsage: true,
SilenceErrors: true,
RunE: func(cmd *cobra.Command, args []string) error {
// Extract the release notes path from args
releaseNotesPath := args[0]

// Run the PR creation function
return runValidateReleaseNotes(releaseNotesPath)
},
}

func runValidateReleaseNotes(releaseNotesPath string) (err error) {
// Check if the directory exists
if _, err := os.Stat(releaseNotesPath); os.IsNotExist(err) {
return fmt.Errorf("release notes path %s does not exist", releaseNotesPath)
}

// Validate the YAML files in the directory
err = filepath.Walk(releaseNotesPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// Only process YAML files
if filepath.Ext(path) == ".yaml" || filepath.Ext(path) == ".yml" {
fmt.Printf("Validating YAML file: %s\n", path)

// Validate YAML
if err := ValidateYamlMap(path); err != nil {
return fmt.Errorf("YAML validation failed for %s: %v", path, err)
}

// (Optional) You can add custom punctuation validation here
// For example, you could check for missing periods at the end of lines

fmt.Printf("YAML file %s is valid.\n", path)
}
return nil
})

if err != nil {
return fmt.Errorf("failed to validate release notes: %v", err)
}

fmt.Println("All release notes are valid.")
return nil
}

// ValidateYamlMap reads a YAML map file, unmarshals it into a map, and then re-marshals it
// to validate the correctness of the content.
func ValidateYamlMap(filePath string) error {
// Read the YAML file
data, err := os.ReadFile(filePath)
if err != nil {
return fmt.Errorf("failed to read file %s: %w", filePath, err)
}

// Unmarshal the YAML data into a map for manipulation and validation
var testMap notes.ReleaseNotesMap
if err := yaml.Unmarshal(data, &testMap); err != nil {
return fmt.Errorf("YAML unmarshal failed for %s:%w", filePath, err)
}

// Check the map for valid punctuation in the "text" field
if err := validateTextFieldPunctuation(&testMap); err != nil {
return fmt.Errorf("punctuation check failed for file %s: %w", filePath, err)
}

// Re-marshall the YAML to check if it can be successfully serialized again
_, err = yaml.Marshal(testMap)
if err != nil {
return fmt.Errorf("while re-marshaling map for file %s:%w", filePath, err)
}

fmt.Printf("File %s is valid YAML.\n", filePath)
return nil
}

// validateTextFieldPunctuation checks if the "text" field in a YAML map
// ends with valid punctuation (., !, ?).
func validateTextFieldPunctuation(data *notes.ReleaseNotesMap) error {
validPunctuation := regexp.MustCompile(`[.!?]$`)

text := *data.ReleaseNote.Text
if !validPunctuation.MatchString(strings.TrimSpace(text)) {
return fmt.Errorf("the 'text' field does not end with valid punctuation: '%s'", text)
}

return nil
}
41 changes: 41 additions & 0 deletions cmd/krel/cmd/validation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package cmd

import (
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
)

func TestRunValidateReleaseNotes(t *testing.T) {
testDataPath := "testdata/validation-data"

// Valid YAML returns no error
err := runValidateReleaseNotes(filepath.Join(testDataPath, "valid.yaml"))
assert.NoError(t, err, "Expected no error for valid YAML file")

// Try a non-existent path
err = runValidateReleaseNotes("nonexistent/path")
assert.Error(t, err, "Expected error for non-existent path")
assert.Contains(t, err.Error(), "does not exist", "Error should be about non-existent path")

// Missing punctuation YAML returns error
err = runValidateReleaseNotes(filepath.Join(testDataPath, "missing-punctuation.yaml"))
assert.Error(t, err, "Expected error for missing punctuation YAML file")
assert.Contains(t, err.Error(), "field does not end with valid punctuation", "Error should be about missing punctuation")

// Try invalid yaml starting with "`"
err = runValidateReleaseNotes(filepath.Join(testDataPath, "invalid-yaml-start.yaml"))
assert.Error(t, err, "Expected error for invalid yaml")
assert.Contains(t, err.Error(), "validation failed for testdata/validation-data/invalid-yaml-start", "Error should be about invalid yaml")

// Try invalid multi line yaml
err = runValidateReleaseNotes(filepath.Join(testDataPath, "invalid-multi-line.yaml"))
assert.Error(t, err, "Expected error for invalid yaml")
assert.Contains(t, err.Error(), "YAML validation failed for testdata/validation-data/invalid-multi-line.yaml", "Error should be about invalid yaml")

// Try invalid indent
err = runValidateReleaseNotes(filepath.Join(testDataPath, "invalid-indent.yaml"))
assert.Error(t, err, "Expected error for invalid yaml")
assert.Contains(t, err.Error(), "YAML validation failed for testdata/validation-data/invalid-indent.yaml", "Error should be about invalid yaml")
}

0 comments on commit 92cdea1

Please sign in to comment.