Skip to content

Commit

Permalink
Add vet command for modules and debug values
Browse files Browse the repository at this point in the history
Signed-off-by: Stefan Prodan <[email protected]>
  • Loading branch information
stefanprodan committed Oct 20, 2023
1 parent 6f4dec9 commit 89acc34
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 41 deletions.
2 changes: 1 addition & 1 deletion cmd/timoni/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ func runApplyCmd(cmd *cobra.Command, args []string) error {

buildResult, err := builder.Build()
if err != nil {
return describeErr(fetcher.GetModuleRoot(), "failed to build instance", err)
return describeErr(fetcher.GetModuleRoot(), "build failed", err)
}

finalValues, err := builder.GetDefaultValues()
Expand Down
2 changes: 1 addition & 1 deletion cmd/timoni/bundle_apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ func applyBundleInstance(ctx context.Context, cuectx *cue.Context, instance *eng

buildResult, err := builder.Build()
if err != nil {
return describeErr(modDir, "failed to build instance", err)
return describeErr(modDir, "build failed for "+instance.Name, err)
}

finalValues, err := builder.GetDefaultValues()
Expand Down
2 changes: 1 addition & 1 deletion cmd/timoni/bundle_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ func buildBundleInstance(cuectx *cue.Context, instance *engine.BundleInstance, r

buildResult, err := builder.Build()
if err != nil {
return "", describeErr(modDir, "failed to build instance", err)
return "", describeErr(modDir, "build failed for "+instance.Name, err)
}

bundleBuildSets, err := builder.GetApplySets(buildResult)
Expand Down
2 changes: 1 addition & 1 deletion cmd/timoni/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func resetCmdArgs() {
inspectModuleArgs = inspectModuleFlags{}
inspectResourcesArgs = inspectResourcesFlags{}
inspectValuesArgs = inspectValuesFlags{}
lintModArgs = lintModFlags{}
vetModArgs = vetModFlags{}
listArgs = listFlags{}
pullModArgs = pullModFlags{}
pushModArgs = pushModFlags{}
Expand Down
82 changes: 54 additions & 28 deletions cmd/timoni/mod_lint.go → cmd/timoni/mod_vet.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ import (
"context"
"fmt"
"os"
"path"

"cuelang.org/go/cue/cuecontext"
"github.com/fluxcd/pkg/ssa"
cp "github.com/otiai10/copy"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

Expand All @@ -30,37 +33,44 @@ import (
"github.com/stefanprodan/timoni/internal/flags"
)

var lintModCmd = &cobra.Command{
Use: "lint [MODULE PATH]",
Short: "Validate a local module",
Long: `The lint command builds the local module and validates the resulting Kubernetes objects.`,
Example: ` # lint a local module
timoni mod lint ./path/to/module
var vetModCmd = &cobra.Command{
Use: "vet [MODULE PATH]",
Aliases: []string{"lint"},
Short: "Validate a local module",
Long: `The vet command builds the local module and validates the resulting Kubernetes objects.`,
Example: ` # validate module in the current path
timoni mod vet
# validate module using default values instead of debug_values.cue
timoni mod vet ./path/to/module --debug=false
`,
RunE: runLintModCmd,
RunE: runVetModCmd,
}

type lintModFlags struct {
path string
pkg flags.Package
type vetModFlags struct {
path string
pkg flags.Package
debug bool
}

var lintModArgs lintModFlags
var vetModArgs vetModFlags

func init() {
lintModCmd.Flags().VarP(&lintModArgs.pkg, lintModArgs.pkg.Type(), lintModArgs.pkg.Shorthand(), lintModArgs.pkg.Description())

modCmd.AddCommand(lintModCmd)
vetModCmd.Flags().VarP(&vetModArgs.pkg, vetModArgs.pkg.Type(), vetModArgs.pkg.Shorthand(), vetModArgs.pkg.Description())
vetModCmd.Flags().BoolVar(&vetModArgs.debug, "debug", true,
"Use debug_values.cue if found in the module root instead of the default values.")
modCmd.AddCommand(vetModCmd)
}

func runLintModCmd(cmd *cobra.Command, args []string) error {
func runVetModCmd(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("module path is required")
vetModArgs.path = "."
} else {
vetModArgs.path = args[0]
}

lintModArgs.path = args[0]
if fs, err := os.Stat(lintModArgs.path); err != nil || !fs.IsDir() {
return fmt.Errorf("module not found at path %s", lintModArgs.path)
if fs, err := os.Stat(vetModArgs.path); err != nil || !fs.IsDir() {
return fmt.Errorf("module not found at path %s", vetModArgs.path)
}

log := LoggerFrom(cmd.Context())
Expand All @@ -77,7 +87,7 @@ func runLintModCmd(cmd *cobra.Command, args []string) error {

fetcher := engine.NewFetcher(
ctxPull,
lintModArgs.path,
vetModArgs.path,
apiv1.LatestVersion,
tmpDir,
"",
Expand All @@ -87,12 +97,28 @@ func runLintModCmd(cmd *cobra.Command, args []string) error {
return err
}

var tags []string
if vetModArgs.debug {
dv := path.Join(vetModArgs.path, "debug_values.cue")
if _, err := os.Stat(dv); err == nil {
if cpErr := cp.Copy(dv, path.Join(tmpDir, "module", "debug_values.cue")); cpErr != nil {
return cpErr
}
tags = append(tags, "debug")
log.Info("vetting with debug values")
} else {
log.Info("vetting with default values (debug values not found)")
}
} else {
log.Info("vetting with default values")
}

builder := engine.NewModuleBuilder(
cuectx,
"default",
*kubeconfigArgs.Namespace,
fetcher.GetModuleRoot(),
lintModArgs.pkg.String(),
vetModArgs.pkg.String(),
)

if err := builder.WriteSchemaFile(); err != nil {
Expand All @@ -104,13 +130,9 @@ func runLintModCmd(cmd *cobra.Command, args []string) error {
return fmt.Errorf("build failed: %w", err)
}

buildResult, err := builder.Build()
buildResult, err := builder.Build(tags...)
if err != nil {
return describeErr(fetcher.GetModuleRoot(), "build failed", err)
}

if _, err := builder.GetConfigValues(buildResult); err != nil {
return fmt.Errorf("failed to extract values: %w", err)
return describeErr(fetcher.GetModuleRoot(), "validation failed", err)
}

applySets, err := builder.GetApplySets(buildResult)
Expand All @@ -131,7 +153,11 @@ func runLintModCmd(cmd *cobra.Command, args []string) error {
return fmt.Errorf("build failed, no objects to apply")
}

log.Info(fmt.Sprintf("%s linted", mod.Name))
for _, object := range objects {
log.Info(fmt.Sprintf("%s valid resource", colorizeSubject(ssa.FmtUnstructured(object))))
}

log.Info(fmt.Sprintf("%s valid module", colorizeSubject(mod.Name)))

return nil
}
15 changes: 7 additions & 8 deletions cmd/timoni/mod_lint_test.go → cmd/timoni/mod_vet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,26 @@ import (
. "github.com/onsi/gomega"
)

func TestLint(t *testing.T) {
func TestModVet(t *testing.T) {
modPath := "testdata/module"

t.Run("lints module with default values", func(t *testing.T) {
t.Run("vets module with default values", func(t *testing.T) {
g := NewWithT(t)
output, err := executeCommand(fmt.Sprintf(
"mod lint %s -p main",
"mod vet %s -p main",
modPath,
))
g.Expect(err).ToNot(HaveOccurred())

g.Expect(output).To(ContainSubstring("linted"))
g.Expect(output).To(ContainSubstring("valid"))
})

t.Run("fails to lint with undefined package", func(t *testing.T) {
t.Run("fails to vet with undefined package", func(t *testing.T) {
g := NewWithT(t)
output, err := executeCommand(fmt.Sprintf(
"mod lint %s -p test",
_, err := executeCommand(fmt.Sprintf(
"mod vet %s -p test",
modPath,
))
g.Expect(output).To(BeEmpty())
g.Expect(err).To(HaveOccurred())
g.Expect(err.Error()).To(ContainSubstring("cannot find package"))
})
Expand Down
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ nav:
- Command Reference:
- Modules:
- Init: cmd/timoni_mod_init.md
- Lint: cmd/timoni_mod_lint.md
- Push: cmd/timoni_mod_push.md
- Pull: cmd/timoni_mod_pull.md
- List: cmd/timoni_mod_list.md
- Vet: cmd/timoni_mod_vet.md
- Vendor Kubernetes APIs: cmd/timoni_mod_vendor_k8s.md
- Vendor Kubernetes CRDs: cmd/timoni_mod_vendor_crd.md
- Instances:
Expand Down

0 comments on commit 89acc34

Please sign in to comment.