Skip to content

Commit

Permalink
feat: Gator sync test support (#3098)
Browse files Browse the repository at this point in the history
Signed-off-by: Anlan Du <[email protected]>
Co-authored-by: alex <[email protected]>
Co-authored-by: Sertaç Özercan <[email protected]>
Co-authored-by: Rita Zhang <[email protected]>
  • Loading branch information
4 people authored Oct 29, 2024
1 parent 57ef7d4 commit 3cc730e
Show file tree
Hide file tree
Showing 21 changed files with 1,173 additions and 69 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ generate: __conversion-gen __controller-gen
$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths="./apis/..." paths="./pkg/..."
$(CONVERSION_GEN) \
--output-base=/gatekeeper \
--input-dirs=./apis/mutations/v1,./apis/mutations/v1beta1,./apis/mutations/v1alpha1,./apis/expansion/v1alpha1,./apis/syncset/v1alpha1 \
--input-dirs=./apis/mutations/v1,./apis/mutations/v1beta1,./apis/mutations/v1alpha1,./apis/expansion/v1alpha1,./apis/syncset/v1alpha1,./apis/gvkmanifest/v1alpha1 \
--go-header-file=./hack/boilerplate.go.txt \
--output-file-base=zz_generated.conversion

Expand Down
10 changes: 10 additions & 0 deletions apis/addtoscheme_gvkmanifest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package apis

import (
"github.com/open-policy-agent/gatekeeper/v3/apis/gvkmanifest/v1alpha1"
)

func init() {
// Register the types with the Scheme so the components can map objects to GroupVersionKinds and back
AddToSchemes = append(AddToSchemes, v1alpha1.AddToScheme)
}
20 changes: 20 additions & 0 deletions apis/gvkmanifest/v1alpha1/groupversion_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Package v1alpha1 contains API Schema definitions for the GVKManifest v1alpha1 API group
// +kubebuilder:object:generate=true
// +groupName=gvkmanifest.gatekeeper.sh
package v1alpha1

import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)

var (
// GroupVersion is group version used to register these objects.
GroupVersion = schema.GroupVersion{Group: "gvkmanifest.gatekeeper.sh", Version: "v1alpha1"}

// SchemeBuilder is used to add go types to the GroupVersionKind scheme.
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}

// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)
42 changes: 42 additions & 0 deletions apis/gvkmanifest/v1alpha1/gvkmanifest_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type GVKManifestSpec struct {
Groups map[string]Versions `json:"groups,omitempty"`
}

type Versions map[string]Kinds

type Kinds []string

type Version struct {
Name string `json:"name,omitempty"`
Kinds []string `json:"kinds,omitempty"`
}

// +kubebuilder:resource:scope=Cluster
// +kubebuilder:object:root=true

// GVKManifest is the Schema for the GVKManifest API.
type GVKManifest struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec GVKManifestSpec `json:"spec,omitempty"`
}

// +kubebuilder:object:root=true

// GVKManifestList contains a list of GVKManifests.
type GVKManifestList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []GVKManifest `json:"items"`
}

func init() {
SchemeBuilder.Register(&GVKManifest{}, &GVKManifestList{})
}
193 changes: 193 additions & 0 deletions apis/gvkmanifest/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions cmd/gator/gator.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"os"

"github.com/open-policy-agent/gatekeeper/v3/cmd/gator/expand"
"github.com/open-policy-agent/gatekeeper/v3/cmd/gator/sync"
"github.com/open-policy-agent/gatekeeper/v3/cmd/gator/test"
"github.com/open-policy-agent/gatekeeper/v3/cmd/gator/verify"
"github.com/open-policy-agent/gatekeeper/v3/pkg/version"
Expand All @@ -15,6 +16,7 @@ var commands = []*cobra.Command{
verify.Cmd,
test.Cmd,
expand.Cmd,
sync.Cmd,
k8sVersion.WithFont("alligator2"),
}

Expand Down
24 changes: 24 additions & 0 deletions cmd/gator/sync/sync.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package sync

import (
"fmt"

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

var commands = []*cobra.Command{
synctest.Cmd,
}

var Cmd = &cobra.Command{
Use: "sync",
Short: "Manage SyncSets and Config",
Run: func(_ *cobra.Command, _ []string) {
fmt.Println("Usage: gator sync test")
},
}

func init() {
Cmd.AddCommand(commands...)
}
73 changes: 73 additions & 0 deletions cmd/gator/sync/test/test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package test

import (
"fmt"
"os"
"strings"

cmdutils "github.com/open-policy-agent/gatekeeper/v3/cmd/gator/util"
"github.com/open-policy-agent/gatekeeper/v3/pkg/gator/reader"
"github.com/open-policy-agent/gatekeeper/v3/pkg/gator/sync/test"
"github.com/spf13/cobra"
)

var Cmd = &cobra.Command{
Use: "test",
Short: "Test that the provided SyncSet(s) and/or Config contain the GVKs required by the input templates.",
Run: run,
}

var (
flagFilenames []string
flagImages []string
flagOmitGVKManifest bool
flagTempDir string
)

const (
flagNameFilename = "filename"
flagNameImage = "image"
flagNameForce = "force-omit-gvk-manifest"
flagNameTempDir = "tempdir"
)

func init() {
Cmd.Flags().StringArrayVarP(&flagFilenames, flagNameFilename, "n", []string{}, "a file or directory containing Kubernetes resources. Can be specified multiple times.")
Cmd.Flags().StringArrayVarP(&flagImages, flagNameImage, "i", []string{}, "a URL to an OCI image containing policies. Can be specified multiple times.")
Cmd.Flags().BoolVarP(&flagOmitGVKManifest, flagNameForce, "f", false, "Do not require a GVK manifest; if one is not provided, assume all GVKs listed in the requirements "+
"and configs are supported by the cluster under test. If this assumption isn't true, the given config may cause errors or templates may not be enforced correctly even after passing this test.")
Cmd.Flags().StringVarP(&flagTempDir, flagNameTempDir, "d", "", fmt.Sprintf("Specifies the temporary directory to download and unpack images to, if using the --%s flag. Optional.", flagNameImage))
}

func run(_ *cobra.Command, _ []string) {
unstrucs, err := reader.ReadSources(flagFilenames, flagImages, flagTempDir)
if err != nil {
cmdutils.ErrFatalf("reading: %v", err)
}
if len(unstrucs) == 0 {
cmdutils.ErrFatalf("no input data identified")
}

missingRequirements, templateErrors, err := test.Test(unstrucs, flagOmitGVKManifest)
if err != nil {
cmdutils.ErrFatalf("checking: %v", err)
}

if len(missingRequirements) > 0 {
cmdutils.ErrFatalf("the following requirements were not met: \n%v", resultsToString(missingRequirements))
}

if len(templateErrors) > 0 {
cmdutils.ErrFatalf("encountered errors parsing the following templates: \n%v", resultsToString(templateErrors))
}

os.Exit(0)
}

func resultsToString[T any](results map[string]T) string {
var sb strings.Builder
for template, vals := range results {
sb.WriteString(fmt.Sprintf("%s:\n%v\n", template, vals))
}
return sb.String()
}
Loading

0 comments on commit 3cc730e

Please sign in to comment.