Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: move validate to lint #2839

Merged
merged 46 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
2d57404
switching json description to go comments
AustinAbro321 Jul 29, 2024
e466577
switching json description to go comments
AustinAbro321 Jul 29, 2024
a54b4d6
switching json description to go comments
AustinAbro321 Jul 29, 2024
d746067
switching json description to go comments
AustinAbro321 Jul 29, 2024
7e16860
make gen types work again
AustinAbro321 Jul 29, 2024
8dfe1a5
wording
AustinAbro321 Jul 29, 2024
be388f8
add json tags back
AustinAbro321 Jul 29, 2024
59bc21f
add back json tags
AustinAbro321 Jul 29, 2024
cde411a
inline
AustinAbro321 Jul 29, 2024
8f2d3ec
Merge branch 'main' into utilize-invopop-features
AustinAbro321 Jul 30, 2024
fd680fc
back to omitempty
AustinAbro321 Jul 30, 2024
cbc25f2
add back test
AustinAbro321 Jul 30, 2024
da9ea48
removing json options
AustinAbro321 Jul 30, 2024
e8a11ac
add back json data
AustinAbro321 Jul 30, 2024
de0bd7c
bb required
AustinAbro321 Jul 30, 2024
96d703d
de-dup
AustinAbro321 Jul 30, 2024
380d2e7
add period to json descriptions
AustinAbro321 Jul 30, 2024
5e91b84
use more invopop features
AustinAbro321 Jul 30, 2024
4623784
merge
AustinAbro321 Jul 31, 2024
457a23e
move v1alpha1 to it's own package
AustinAbro321 Jul 31, 2024
4094292
Merge remote-tracking branch 'origin/use-more-invopop-features' into …
AustinAbro321 Jul 31, 2024
8efabbb
Merge branch 'main' into add-api-version
AustinAbro321 Jul 31, 2024
cd3f497
adding go mod to api package
AustinAbro321 Jul 31, 2024
d16a7d7
move bigbang over
AustinAbro321 Jul 31, 2024
2716d41
avoid introducing dependencies
AustinAbro321 Jul 31, 2024
648bbf0
header title
AustinAbro321 Jul 31, 2024
df9c182
package
AustinAbro321 Jul 31, 2024
d1a934d
Merge branch 'main' into add-api-version
AustinAbro321 Jul 31, 2024
2bddac1
update go mod
AustinAbro321 Aug 1, 2024
c955d6f
move validate to lint
AustinAbro321 Aug 1, 2024
9b4fbe9
moving validate to lint and separating out packages
AustinAbro321 Aug 5, 2024
35a1dfc
merge
AustinAbro321 Aug 7, 2024
3a9f928
merge
AustinAbro321 Aug 7, 2024
b1ce576
move validate to lint
AustinAbro321 Aug 7, 2024
7d017ab
merge
AustinAbro321 Aug 8, 2024
1d307e2
move lint back
AustinAbro321 Aug 8, 2024
16dead0
call validate component
AustinAbro321 Aug 8, 2024
9d62adf
unexport validate functions
AustinAbro321 Aug 8, 2024
9fface6
merge
AustinAbro321 Aug 12, 2024
9c4d24b
re-delete file
AustinAbro321 Aug 12, 2024
759414e
static check
AustinAbro321 Aug 12, 2024
a881426
remove unnecessary lint checks
AustinAbro321 Aug 12, 2024
265df7c
move validate compose code to compose
AustinAbro321 Aug 12, 2024
e831b01
move compose check to compose
AustinAbro321 Aug 12, 2024
e704281
Merge branch 'main' into move-validate-to-lint
AustinAbro321 Aug 12, 2024
89fe7cd
Merge branch 'main' into move-validate-to-lint
schristoff Aug 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/api/v1alpha1/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ var (
IsUppercaseNumberUnderscore = regexp.MustCompile(`^[A-Z0-9_]+$`).MatchString
)

// Zarf looks for these strings in zarf.yaml to make dynamic changes
const (
ZarfPackageTemplatePrefix = "###ZARF_PKG_TMPL_"
ZarfPackageVariablePrefix = "###ZARF_PKG_VAR_"
ZarfPackageArch = "###ZARF_PKG_ARCH###"
ZarfComponentName = "###ZARF_COMPONENT_NAME###"
)

// ZarfPackageKind is an enum of the different kinds of Zarf packages.
type ZarfPackageKind string

Expand Down
193 changes: 54 additions & 139 deletions src/api/v1alpha1/validate.go → src/pkg/lint/validate.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,19 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors

// Package v1alpha1 holds the definition of the v1alpha1 Zarf Package
package v1alpha1
// Package lint contains functions for verifying zarf yaml files are valid
package lint

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

"github.com/defenseunicorns/pkg/helpers/v2"
"github.com/zarf-dev/zarf/src/api/v1alpha1"
"k8s.io/apimachinery/pkg/util/validation"
)

// Zarf looks for these strings in zarf.yaml to make dynamic changes
const (
ZarfPackageTemplatePrefix = "###ZARF_PKG_TMPL_"
ZarfPackageVariablePrefix = "###ZARF_PKG_VAR_"
ZarfPackageArch = "###ZARF_PKG_ARCH###"
ZarfComponentName = "###ZARF_COMPONENT_NAME###"
)

var (
// IsLowercaseNumberHyphenNoStartHyphen is a regex for lowercase, numbers and hyphens that cannot start with a hyphen.
// https://regex101.com/r/FLdG9G/2
Expand All @@ -47,105 +38,71 @@ const (
errChartReleaseNameEmpty = "release name empty, unable to fallback to chart name"
)

// Package errors found during validation.
schristoff marked this conversation as resolved.
Show resolved Hide resolved
const (
//nolint:revive //ignore
PkgValidateErrInitNoYOLO = "sorry, you can't YOLO an init package"
//nolint:revive //ignore
PkgValidateErrConstant = "invalid package constant: %w"
//nolint:revive //ignore
PkgValidateErrYOLONoOCI = "OCI images not allowed in YOLO"
//nolint:revive //ignore
PkgValidateErrYOLONoGit = "git repos not allowed in YOLO"
//nolint:revive //ignore
PkgValidateErrYOLONoArch = "cluster architecture not allowed in YOLO"
//nolint:revive //ignore
PkgValidateErrYOLONoDistro = "cluster distros not allowed in YOLO"
//nolint:revive //ignore
PkgValidateErrComponentNameNotUnique = "component name %q is not unique"
//nolint:revive //ignore
PkgValidateErrComponentReqDefault = "component %q cannot be both required and default"
//nolint:revive //ignore
PkgValidateErrComponentReqGrouped = "component %q cannot be both required and grouped"
//nolint:revive //ignore
PkgValidateErrChartNameNotUnique = "chart name %q is not unique"
//nolint:revive //ignore
PkgValidateErrChart = "invalid chart definition: %w"
//nolint:revive //ignore
PkgValidateErrManifestNameNotUnique = "manifest name %q is not unique"
//nolint:revive //ignore
PkgValidateErrManifest = "invalid manifest definition: %w"
//nolint:revive //ignore
PkgValidateErrGroupMultipleDefaults = "group %q has multiple defaults (%q, %q)"
//nolint:revive //ignore
PkgValidateErrGroupOneComponent = "group %q only has one component (%q)"
//nolint:revive //ignore
PkgValidateErrAction = "invalid action: %w"
//nolint:revive //ignore
PkgValidateErrActionCmdWait = "action %q cannot be both a command and wait action"
//nolint:revive //ignore
PkgValidateErrActionClusterNetwork = "a single wait action must contain only one of cluster or network"
//nolint:revive //ignore
PkgValidateErrChartName = "chart %q exceed the maximum length of %d characters"
//nolint:revive //ignore
PkgValidateErrChartNamespaceMissing = "chart %q must include a namespace"
//nolint:revive //ignore
PkgValidateErrChartURLOrPath = "chart %q must have either a url or localPath"
//nolint:revive //ignore
PkgValidateErrChartVersion = "chart %q must include a chart version"
//nolint:revive //ignore
PkgValidateErrImportDefinition = "invalid imported definition for %s: %s"
//nolint:revive //ignore
PkgValidateErrInitNoYOLO = "sorry, you can't YOLO an init package"
PkgValidateErrConstant = "invalid package constant: %w"
PkgValidateErrYOLONoOCI = "OCI images not allowed in YOLO"
PkgValidateErrYOLONoGit = "git repos not allowed in YOLO"
PkgValidateErrYOLONoArch = "cluster architecture not allowed in YOLO"
PkgValidateErrYOLONoDistro = "cluster distros not allowed in YOLO"
PkgValidateErrComponentNameNotUnique = "component name %q is not unique"
PkgValidateErrComponentReqDefault = "component %q cannot be both required and default"
PkgValidateErrComponentReqGrouped = "component %q cannot be both required and grouped"
PkgValidateErrChartNameNotUnique = "chart name %q is not unique"
PkgValidateErrChart = "invalid chart definition: %w"
PkgValidateErrManifestNameNotUnique = "manifest name %q is not unique"
PkgValidateErrManifest = "invalid manifest definition: %w"
PkgValidateErrGroupMultipleDefaults = "group %q has multiple defaults (%q, %q)"
PkgValidateErrGroupOneComponent = "group %q only has one component (%q)"
PkgValidateErrAction = "invalid action: %w"
PkgValidateErrActionCmdWait = "action %q cannot be both a command and wait action"
PkgValidateErrActionClusterNetwork = "a single wait action must contain only one of cluster or network"
PkgValidateErrChartName = "chart %q exceed the maximum length of %d characters"
PkgValidateErrChartNamespaceMissing = "chart %q must include a namespace"
PkgValidateErrChartURLOrPath = "chart %q must have either a url or localPath"
PkgValidateErrChartVersion = "chart %q must include a chart version"
PkgValidateErrManifestFileOrKustomize = "manifest %q must have at least one file or kustomization"
//nolint:revive //ignore
PkgValidateErrManifestNameLength = "manifest %q exceed the maximum length of %d characters"
//nolint:revive //ignore
PkgValidateErrVariable = "invalid package variable: %w"
PkgValidateErrManifestNameLength = "manifest %q exceed the maximum length of %d characters"
PkgValidateErrVariable = "invalid package variable: %w"
)

// Validate runs all validation checks on the package.
func (pkg ZarfPackage) Validate() error {
// ValidatePackage runs all validation checks on the package.
func ValidatePackage(pkg v1alpha1.ZarfPackage) error {
var err error
if pkg.Kind == ZarfInitConfig && pkg.Metadata.YOLO {
if pkg.Kind == v1alpha1.ZarfInitConfig && pkg.Metadata.YOLO {
err = errors.Join(err, fmt.Errorf(PkgValidateErrInitNoYOLO))
}

for _, constant := range pkg.Constants {
if varErr := constant.Validate(); varErr != nil {
err = errors.Join(err, fmt.Errorf(PkgValidateErrConstant, varErr))
}
}

uniqueComponentNames := make(map[string]bool)
groupDefault := make(map[string]string)
groupedComponents := make(map[string][]string)

if pkg.Metadata.YOLO {
for _, component := range pkg.Components {
if len(component.Images) > 0 {
err = errors.Join(err, fmt.Errorf(PkgValidateErrYOLONoOCI))
}

if len(component.Repos) > 0 {
err = errors.Join(err, fmt.Errorf(PkgValidateErrYOLONoGit))
}

if component.Only.Cluster.Architecture != "" {
err = errors.Join(err, fmt.Errorf(PkgValidateErrYOLONoArch))
}

if len(component.Only.Cluster.Distros) > 0 {
err = errors.Join(err, fmt.Errorf(PkgValidateErrYOLONoDistro))
}
}
}

for _, component := range pkg.Components {
// ensure component name is unique
if _, ok := uniqueComponentNames[component.Name]; ok {
err = errors.Join(err, fmt.Errorf(PkgValidateErrComponentNameNotUnique, component.Name))
}
uniqueComponentNames[component.Name] = true

if component.IsRequired() {
if component.Default {
err = errors.Join(err, fmt.Errorf(PkgValidateErrComponentReqDefault, component.Name))
Expand All @@ -154,37 +111,31 @@ func (pkg ZarfPackage) Validate() error {
err = errors.Join(err, fmt.Errorf(PkgValidateErrComponentReqGrouped, component.Name))
}
}

uniqueChartNames := make(map[string]bool)
for _, chart := range component.Charts {
// ensure chart name is unique
if _, ok := uniqueChartNames[chart.Name]; ok {
err = errors.Join(err, fmt.Errorf(PkgValidateErrChartNameNotUnique, chart.Name))
}
uniqueChartNames[chart.Name] = true

if chartErr := chart.Validate(); chartErr != nil {
if chartErr := validateChart(chart); chartErr != nil {
err = errors.Join(err, fmt.Errorf(PkgValidateErrChart, chartErr))
}
}

uniqueManifestNames := make(map[string]bool)
for _, manifest := range component.Manifests {
// ensure manifest name is unique
if _, ok := uniqueManifestNames[manifest.Name]; ok {
err = errors.Join(err, fmt.Errorf(PkgValidateErrManifestNameNotUnique, manifest.Name))
}
uniqueManifestNames[manifest.Name] = true

if manifestErr := manifest.Validate(); manifestErr != nil {
if manifestErr := validateManifest(manifest); manifestErr != nil {
err = errors.Join(err, fmt.Errorf(PkgValidateErrManifest, manifestErr))
}
}

if actionsErr := component.Actions.validate(); actionsErr != nil {
if actionsErr := validateActions(component.Actions); actionsErr != nil {
err = errors.Join(err, fmt.Errorf("%q: %w", component.Name, actionsErr))
}

// ensure groups don't have multiple defaults or only one component
if component.DeprecatedGroup != "" {
if component.Default {
Expand All @@ -196,74 +147,38 @@ func (pkg ZarfPackage) Validate() error {
groupedComponents[component.DeprecatedGroup] = append(groupedComponents[component.DeprecatedGroup], component.Name)
}
}

for groupKey, componentNames := range groupedComponents {
if len(componentNames) == 1 {
err = errors.Join(err, fmt.Errorf(PkgValidateErrGroupOneComponent, groupKey, componentNames[0]))
}
}

return err
}

func (a ZarfComponentActions) validate() error {
// validateActions validates the actions of a component.
func validateActions(a v1alpha1.ZarfComponentActions) error {
var err error

err = errors.Join(err, a.OnCreate.Validate())
err = errors.Join(err, validateActionSet(a.OnCreate))

if a.OnCreate.HasSetVariables() {
if hasSetVariables(a.OnCreate) {
err = errors.Join(err, fmt.Errorf("cannot contain setVariables outside of onDeploy in actions"))
}

err = errors.Join(err, a.OnDeploy.Validate())
err = errors.Join(err, validateActionSet(a.OnDeploy))

if a.OnRemove.HasSetVariables() {
if hasSetVariables(a.OnRemove) {
err = errors.Join(err, fmt.Errorf("cannot contain setVariables outside of onDeploy in actions"))
}

err = errors.Join(err, a.OnRemove.Validate())

return err
}

// Validate validates the component trying to be imported.
func (c ZarfComponent) Validate() error {
var err error
path := c.Import.Path
url := c.Import.URL

// ensure path or url is provided
if path == "" && url == "" {
err = errors.Join(err, fmt.Errorf(PkgValidateErrImportDefinition, c.Name, "neither a path nor a URL was provided"))
}

// ensure path and url are not both provided
if path != "" && url != "" {
err = errors.Join(err, fmt.Errorf(PkgValidateErrImportDefinition, c.Name, "both a path and a URL were provided"))
}

// validation for path
if url == "" && path != "" {
// ensure path is not an absolute path
if filepath.IsAbs(path) {
err = errors.Join(err, fmt.Errorf(PkgValidateErrImportDefinition, c.Name, "path cannot be an absolute path"))
}
}

// validation for url
if url != "" && path == "" {
ok := helpers.IsOCIURL(url)
if !ok {
err = errors.Join(err, fmt.Errorf(PkgValidateErrImportDefinition, c.Name, "URL is not a valid OCI URL"))
}
}
err = errors.Join(err, validateActionSet(a.OnRemove))

return err
}

// HasSetVariables returns true if any of the actions contain setVariables.
func (as ZarfComponentActionSet) HasSetVariables() bool {
check := func(actions []ZarfComponentAction) bool {
// hasSetVariables returns true if any of the actions contain setVariables.
func hasSetVariables(as v1alpha1.ZarfComponentActionSet) bool {
check := func(actions []v1alpha1.ZarfComponentAction) bool {
for _, action := range actions {
if len(action.SetVariables) > 0 {
return true
Expand All @@ -275,12 +190,12 @@ func (as ZarfComponentActionSet) HasSetVariables() bool {
return check(as.Before) || check(as.After) || check(as.OnSuccess) || check(as.OnFailure)
}

// Validate runs all validation checks on component action sets.
func (as ZarfComponentActionSet) Validate() error {
// validateActionSet runs all validation checks on component action sets.
func validateActionSet(as v1alpha1.ZarfComponentActionSet) error {
var err error
validate := func(actions []ZarfComponentAction) {
validate := func(actions []v1alpha1.ZarfComponentAction) {
for _, action := range actions {
if actionErr := action.Validate(); actionErr != nil {
if actionErr := validateAction(action); actionErr != nil {
err = errors.Join(err, fmt.Errorf(PkgValidateErrAction, actionErr))
}
}
Expand All @@ -293,8 +208,8 @@ func (as ZarfComponentActionSet) Validate() error {
return err
}

// Validate runs all validation checks on an action.
func (action ZarfComponentAction) Validate() error {
// validateAction runs all validation checks on an action.
func validateAction(action v1alpha1.ZarfComponentAction) error {
var err error

if action.Wait != nil {
Expand Down Expand Up @@ -340,8 +255,8 @@ func validateReleaseName(chartName, releaseName string) (err error) {
return
}

// Validate runs all validation checks on a chart.
func (chart ZarfChart) Validate() error {
// validateChart runs all validation checks on a chart.
func validateChart(chart v1alpha1.ZarfChart) error {
var err error

if len(chart.Name) > ZarfMaxChartNameLength {
Expand Down Expand Up @@ -372,8 +287,8 @@ func (chart ZarfChart) Validate() error {
return err
}

// Validate runs all validation checks on a manifest.
func (manifest ZarfManifest) Validate() error {
// validateManifest runs all validation checks on a manifest.
func validateManifest(manifest v1alpha1.ZarfManifest) error {
var err error

if len(manifest.Name) > ZarfMaxChartNameLength {
Expand Down
Loading