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

Create gator root command #1403

Merged
merged 3 commits into from
Jun 29, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
75 changes: 0 additions & 75 deletions cmd/gatekeeper-test-alpha/gatekeeper-test-alpha.go

This file was deleted.

32 changes: 32 additions & 0 deletions cmd/gator/gator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package main

import (
"fmt"
"os"

"github.com/open-policy-agent/gatekeeper/cmd/gator/test"
"github.com/spf13/cobra"
)

const version = "alpha"

func init() {
rootCmd.AddCommand(test.Cmd)
}

var rootCmd = &cobra.Command{
Use: "gator subcommand",
Short: "gator is a suite of authorship tools for Gatekeeper",
Version: version,
RunE: func(cmd *cobra.Command, args []string) error {
return nil
},
}

func main() {
err := rootCmd.Execute()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
114 changes: 114 additions & 0 deletions cmd/gator/test/test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package test

import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"

"github.com/open-policy-agent/gatekeeper/pkg/gktest"
"github.com/spf13/cobra"
)

const (
examples = ` # Run all tests in label-tests.yaml
gator test label-tests.yaml

# Run all suites whose names contain "forbid-labels".
gator test tests/... --run forbid-labels//

# Run all tests whose names contain "nginx-deployment".
gator test tests/... --run //nginx-deployment

# Run all tests whose names exactly match "nginx-deployment".
gator test tests/... --run '//^nginx-deployment$'

# Run all tests that are either named "forbid-labels" or are
# in suites named "forbid-labels".
gator test tests/... --run '^forbid-labels$'`
)

var run string

func init() {
Cmd.Flags().StringVarP(&run, "run", "r", "",
`regular expression which filters tests to run by name`)
}

// Cmd is the gator test subcommand.
var Cmd = &cobra.Command{
Use: "test path [--run=name]",
Short: "test runs suites of tests on Gatekeeper Constraints",
Example: examples,
Args: cobra.ExactArgs(1),
RunE: runE,
}

func runE(_ *cobra.Command, args []string) error {
path := args[0]

// Convert path to be absolute. Allowing for relative and absolute paths
// everywhere in the code leads to unnecessary complexity, so the first
// thing we do on encountering a path is to convert it to an absolute path.
var err error
if !filepath.IsAbs(path) {
path, err = filepath.Abs(path)
if err != nil {
return fmt.Errorf("getting absolute path: %w", err)
}
}

// Create the base file system. We use fs.FS rather than direct calls to
// os.ReadFile or filepath.WalkDir to make testing easier and keep logic
// os-independent.
fileSystem := getFS(path)

suites, err := gktest.ReadSuites(fileSystem, path)
if err != nil {
return fmt.Errorf("listing test files: %w", err)
}
filter, err := gktest.NewFilter(run)
if err != nil {
return fmt.Errorf("compiling filter: %w", err)
}

return runSuites(fileSystem, suites, filter)
}

func runSuites(fileSystem fs.FS, suites []gktest.Suite, filter gktest.Filter) error {
isFailure := false
for _, s := range suites {
if !filter.MatchesSuite(s) {
continue
}

results := s.Run(fileSystem, filter)
for _, result := range results {
if result.IsFailure() {
isFailure = true
}
fmt.Println(result.String())
}
}

if isFailure {
// At least one test failed or there was a problem executing tests in at
// least one file.
return errors.New("FAIL")
}
return nil
}

func getFS(path string) fs.FS {
// TODO(#1397): Check that this produces the correct file system string on
// Windows. We may need to add a trailing `/` for fs.FS to function properly.
root := filepath.VolumeName(path)
if root == "" {
// We are running on a unix-like filesystem without volume names, so the
// file system root is `/`.
root = "/"
}

return os.DirFS(root)
}
11 changes: 0 additions & 11 deletions pkg/gktest/files.go

This file was deleted.

8 changes: 4 additions & 4 deletions pkg/gktest/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,20 @@ func NewFilter(run string) (Filter, error) {
return Filter{}, nil
}

// Suite filters the set of test suites to run by suite name and the tests
// MatchesSuite filters the set of test suites to run by suite name and the tests
// contained in the suite. Returns true if the suite should be run.
//
// If a suite regex was specified, returns true if the suite regex matches
// `suite`.
// If a suite regex was not specified but a test regex was, returns true if at
// least one test in `tests` matches the test regex.
func (f Filter) Suite(suite string, tests []string) bool {
func (f Filter) MatchesSuite(suite Suite) bool {
return true
}

// Test filters the set of tests to run by name.
// MatchesTest filters the set of tests to run by name.
//
// Returns true if the test regex matches test.
func (f Filter) Test(test string) bool {
func (f Filter) MatchesTest(testCase TestCase) bool {
return true
}
18 changes: 18 additions & 0 deletions pkg/gktest/read_suites.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package gktest

import "io/fs"

// ReadSuites returns the set of test Suites selected by path.
//
// 1) If path is a path to a Suite, parses and returns the Suite.
// 2) If the path is a directory, returns the Suites defined in that directory
// (not recursively).
// 3) If the path is a directory followed by "...", returns all Suites in that
// directory and its subdirectories.
//
// Returns an error if:
// - path is a file that does not define a Suite
// - any matched files containing Suites are not parseable
func ReadSuites(f fs.FS, path string) ([]Suite, error) {
return nil, nil
}
6 changes: 0 additions & 6 deletions pkg/gktest/run.go

This file was deleted.

31 changes: 31 additions & 0 deletions pkg/gktest/suite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package gktest

import "io/fs"

// Suite defines a set of TestCases which all use the same ConstraintTemplate
// and Constraint.
type Suite struct {
TestCases []TestCase
}

// Run executes every TestCase in the Suite. Returns the results for every
// TestCase.
func (s Suite) Run(f fs.FS, filter Filter) []Result {
results := make([]Result, len(s.TestCases))
for i, tc := range s.TestCases {
if !filter.MatchesTest(tc) {
continue
}

results[i] = tc.Run(f)
}
return results
}

// TestCase runs Constraint against a YAML object
type TestCase struct{}

// Run executes the TestCase and returns the Result of the run.
func (tc TestCase) Run(f fs.FS) Result {
return Result{}
}