Skip to content

Commit

Permalink
feat(dev/validate): #70. Create initial LintValidation functionality …
Browse files Browse the repository at this point in the history
…and added to dev validate cmd
  • Loading branch information
mike-winberry committed Apr 2, 2024
1 parent 1c44236 commit 22aab2d
Show file tree
Hide file tree
Showing 2 changed files with 226 additions and 0 deletions.
50 changes: 50 additions & 0 deletions src/cmd/dev/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"reflect"
"strings"

"github.com/defenseunicorns/go-oscal/src/pkg/utils"
Expand Down Expand Up @@ -127,3 +128,52 @@ func writeValidation(result types.Validation, outputFile string) error {

return nil
}

// LintValidation checks if a validation has all the required fields.
func LintValidation(validation types.Validation) error {
if validation.Title == "" {
return fmt.Errorf("validation title is required")
}

// Requires a target
if reflect.DeepEqual(validation.Target, types.Target{}) {
return fmt.Errorf("validation target is required")
}

// Requires a payload
if reflect.DeepEqual(validation.Target.Payload, types.Payload{}) {
return fmt.Errorf("validation target payload is required")
}

// Requires resources
if len(validation.Target.Payload.Resources) == 0 {
return fmt.Errorf("validation target resources are required")
}

// Iterate through each resource and check if the rule has all the required fields
for _, resource := range validation.Target.Payload.Resources {
// get the resource rule
rule := resource.ResourceRule

// Requires a version
if rule.Version == "" {
return fmt.Errorf("resource %s has no version", rule.Name)
}

// Requires a resource
if rule.Resource == "" {
return fmt.Errorf("resource %s has no resource", rule.Name)
}

// Requires a namespace if the resource has a name
if rule.Name != "" && len(rule.Namespaces) == 0 {
return fmt.Errorf("resource %s has no namespaces", rule.Name)
}

// Requires a name if the resource has a field
if !reflect.DeepEqual(rule.Field, types.Field{}) && rule.Name == "" {
return fmt.Errorf("resource-rule with field must have a name")
}
}
return nil
}
176 changes: 176 additions & 0 deletions src/cmd/dev/validate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package dev

import (
"testing"

"github.com/defenseunicorns/lula/src/types"
)

func Test_lintValidation(t *testing.T) {

tests := []struct {
name string
validation types.Validation
wantErr bool
}{
// TODO: Add test cases.
{
name: "success",
validation: types.Validation{
Title: "Lula Validation",
LulaVersion: ">= v0.1.0",
Target: types.Target{
Provider: "opa",
Domain: "kubernetes",
Payload: types.Payload{
Resources: []types.Resource{
{
Name: "podsvt",
ResourceRule: types.ResourceRule{
Group: "core",
Version: "v1",
Resource: "pods",
Namespaces: []string{"validation-test"},
},
},
},
Rego: "package validate\n\nimport future.keywords.every\n\nvalidate {\n every pod in input.podsvt {\n podLabel := pod.metadata.labels.foo\npodlabel == \"bar\"\n }\n}",
},
},
},
wantErr: false,
},
{
name: "error no validation",
validation: types.Validation{},
wantErr: true,
},
{
name: "error no validation.title",
validation: types.Validation{
Title: "Lula Validation",
},
wantErr: true,
},
{
name: "error no target",
validation: types.Validation{
Title: "Lula Validation",
LulaVersion: ">= v0.1.0",
Target: types.Target{},
},
wantErr: true,
},
{
name: "error resource-rule.name no resource-rule.namespaces",
validation: types.Validation{
Title: "Lula Validation",
LulaVersion: ">= v0.1.0",
Target: types.Target{
Provider: "opa",
Domain: "kubernetes",
Payload: types.Payload{
Resources: []types.Resource{
{
Name: "podsvt",
ResourceRule: types.ResourceRule{
Name: "podsvt",
Group: "core",
Version: "v1",
Resource: "pods",
},
},
},
Rego: "package validate\n\nimport future.keywords.every\n\nvalidate {\n every pod in input.podsvt {\n podLabel := pod.metadata.labels.foo\npodlabel == \"bar\"\n }\n}",
},
},
},
wantErr: true,
},
{
name: "error resource-rule.field no resource-rule.name",
validation: types.Validation{
Title: "Lula Validation",
LulaVersion: ">= v0.1.0",
Target: types.Target{
Provider: "opa",
Domain: "kubernetes",
Payload: types.Payload{
Resources: []types.Resource{
{
Name: "podsvt",
ResourceRule: types.ResourceRule{
Group: "core",
Version: "v1",
Resource: "pods",
Field: types.Field{
Jsonpath: "metadata.labels.foo",
Type: "json",
Base64: false,
},
},
},
},
Rego: "package validate\n\nimport future.keywords.every\n\nvalidate {\n every pod in input.podsvt {\n podLabel := pod.metadata.labels.foo\npodlabel == \"bar\"\n }\n}",
},
},
},
wantErr: true,
},
{
name: "error no resource-rule.resource",
validation: types.Validation{
Title: "Lula Validation",
LulaVersion: ">= v0.1.0",
Target: types.Target{
Provider: "opa",
Domain: "kubernetes",
Payload: types.Payload{
Resources: []types.Resource{
{
Name: "podsvt",
ResourceRule: types.ResourceRule{
Group: "core",
Version: "v1",
},
},
},
Rego: "package validate\n\nimport future.keywords.every\n\nvalidate {\n every pod in input.podsvt {\n podLabel := pod.metadata.labels.foo\npodlabel == \"bar\"\n }\n}",
},
},
},
wantErr: true,
},
{
name: "error resource-rule no version",
validation: types.Validation{
Title: "Lula Validation",
LulaVersion: ">= v0.1.0",
Target: types.Target{
Provider: "opa",
Domain: "kubernetes",
Payload: types.Payload{
Resources: []types.Resource{
{
Name: "podsvt",
ResourceRule: types.ResourceRule{
Group: "core",
Resource: "pods",
},
},
},
Rego: "package validate\n\nimport future.keywords.every\n\nvalidate {\n every pod in input.podsvt {\n podLabel := pod.metadata.labels.foo\npodlabel == \"bar\"\n }\n}",
},
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := LintValidation(tt.validation); (err != nil) != tt.wantErr {
t.Errorf("lintValidation() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

0 comments on commit 22aab2d

Please sign in to comment.