Skip to content

Commit

Permalink
Create gator root command (#1403)
Browse files Browse the repository at this point in the history
* read test files

Signed-off-by: Will Beason <willbeason@google.com>

* Make gator root command

Per decision in weekly meeting, make the "gator" root command and "test"
the subcommand in charge of running the tests. This frees us up to add
other subcommands that we want to do later.

Signed-off-by: Will Beason <willbeason@google.com>

Co-authored-by: Sertaç Özercan <852750+sozercan@users.noreply.github.com>
  • Loading branch information
Will Beason and sozercan committed Jun 29, 2021
1 parent 60c9781 commit c43fb49
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 96 deletions.
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
@@ -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
@@ -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
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
@@ -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
@@ -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{}
}

0 comments on commit c43fb49

Please sign in to comment.