From 9930bdf0e7ae41c7da0f58f055768ffb7e62cc87 Mon Sep 17 00:00:00 2001 From: David-Jaeyoon-Lee Date: Fri, 11 Oct 2024 17:47:31 +0000 Subject: [PATCH 1/8] Add expansion template support for gator verify Signed-off-by: David-Jaeyoon-Lee --- pkg/gator/errors.go | 3 ++ pkg/gator/reader/read_resources.go | 14 ++++++ pkg/gator/verify/runner.go | 74 +++++++++++++++++++++++++++--- pkg/gator/verify/suite.go | 4 ++ 4 files changed, 88 insertions(+), 7 deletions(-) diff --git a/pkg/gator/errors.go b/pkg/gator/errors.go index 11d828b8c2a..82d14e5f617 100644 --- a/pkg/gator/errors.go +++ b/pkg/gator/errors.go @@ -18,6 +18,9 @@ var ( // ErrNotASyncSet indicates the user-indicated file does not contain a // SyncSet. ErrNotAGVKManifest = errors.New("not a GVKManifest") + // ErrNotAnExpansion indicates the user-indicated file does not contain a + // Constraint. + ErrNotAnExpansion = errors.New("not an Expansion Template") // ErrAddingTemplate indicates a problem instantiating a Suite's ConstraintTemplate. ErrAddingTemplate = errors.New("adding template") // ErrAddingConstraint indicates a problem instantiating a Suite's Constraint. diff --git a/pkg/gator/reader/read_resources.go b/pkg/gator/reader/read_resources.go index b5ec0260f5b..3244f09618f 100644 --- a/pkg/gator/reader/read_resources.go +++ b/pkg/gator/reader/read_resources.go @@ -243,6 +243,20 @@ func ReadConstraint(f fs.FS, path string) (*unstructured.Unstructured, error) { return u, nil } +func ReadExpansion(f fs.FS, path string) (*unstructured.Unstructured, error) { + u, err := ReadObject(f, path) + if err != nil { + return nil, err + } + + gvk := u.GroupVersionKind() + if gvk.Group != "expansion.gatekeeper.sh" { + return nil, gator.ErrNotAnExpansion + } + + return u, nil +} + // ReadK8sResources reads JSON or YAML k8s resources from an io.Reader, // decoding them into Unstructured objects and returning those objects as a // slice. diff --git a/pkg/gator/verify/runner.go b/pkg/gator/verify/runner.go index 87557030c1d..cc33e367534 100644 --- a/pkg/gator/verify/runner.go +++ b/pkg/gator/verify/runner.go @@ -12,7 +12,9 @@ import ( "github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews" "github.com/open-policy-agent/frameworks/constraint/pkg/types" "github.com/open-policy-agent/gatekeeper/v3/apis" + "github.com/open-policy-agent/gatekeeper/v3/pkg/expansion" "github.com/open-policy-agent/gatekeeper/v3/pkg/gator" + "github.com/open-policy-agent/gatekeeper/v3/pkg/gator/expand" "github.com/open-policy-agent/gatekeeper/v3/pkg/gator/reader" mutationtypes "github.com/open-policy-agent/gatekeeper/v3/pkg/mutation/types" "github.com/open-policy-agent/gatekeeper/v3/pkg/target" @@ -123,7 +125,9 @@ func (r *Runner) runTest(ctx context.Context, suiteDir string, filter Filter, t start := time.Now() err := r.tryAddConstraint(ctx, suiteDir, t) + err = r.tryAddExpansion(suiteDir, t) var results []CaseResult + // What is this Invalid and where does it get set? I didn't see it get set in tryAddConstraints if t.Invalid { if errors.Is(err, constraints.ErrSchema) { err = nil @@ -142,6 +146,11 @@ func (r *Runner) runTest(ctx context.Context, suiteDir string, filter Filter, t } } +func (r *Runner) tryAddExpansion(suiteDir string, t *Test) error { + _, err := r.makeTestExpander(suiteDir, t) + return err +} + func (r *Runner) tryAddConstraint(ctx context.Context, suiteDir string, t *Test) error { client, err := r.newClient(r.includeTrace, r.useK8sCEL) if err != nil { @@ -179,6 +188,15 @@ func (r *Runner) runCases(ctx context.Context, suiteDir string, filter Filter, t return c, nil } + newExpander := func() (*expand.Expander, error) { + e, err := r.makeTestExpander(suiteDir, t) + if err != nil { + return nil, err + } + + return e, nil + } + results := make([]CaseResult, len(t.Cases)) for i, c := range t.Cases { @@ -187,7 +205,7 @@ func (r *Runner) runCases(ctx context.Context, suiteDir string, filter Filter, t continue } - results[i] = r.runCase(ctx, newClient, suiteDir, c) + results[i] = r.runCase(ctx, newClient, newExpander, suiteDir, c) } return results, nil @@ -216,6 +234,18 @@ func (r *Runner) makeTestClient(ctx context.Context, suiteDir string, t *Test) ( return client, nil } +func (r *Runner) makeTestExpander(suiteDir string, t *Test) (*expand.Expander, error) { + // Support Mutator logic? Then we need to add support for mutators as well or do we just ignore them? + expansionPath := t.Expansion + if expansionPath == "" { + return nil, nil + } + + et, err := reader.ReadExpansion(r.filesystem, path.Join(suiteDir, expansionPath)) + er, err := expand.NewExpander([]*unstructured.Unstructured{et}) + return er, err +} + func (r *Runner) addConstraint(ctx context.Context, suiteDir, constraintPath string, client gator.Client) error { if constraintPath == "" { return fmt.Errorf("%w: missing constraint", gator.ErrInvalidSuite) @@ -252,9 +282,9 @@ func (r *Runner) addTemplate(suiteDir, templatePath string, client gator.Client) } // RunCase executes a Case and returns the result of the run. -func (r *Runner) runCase(ctx context.Context, newClient func() (gator.Client, error), suiteDir string, tc *Case) CaseResult { +func (r *Runner) runCase(ctx context.Context, newClient func() (gator.Client, error), newExpander func() (*expand.Expander, error), suiteDir string, tc *Case) CaseResult { start := time.Now() - trace, err := r.checkCase(ctx, newClient, suiteDir, tc) + trace, err := r.checkCase(ctx, newClient, newExpander, suiteDir, tc) return CaseResult{ Name: tc.Name, @@ -264,7 +294,7 @@ func (r *Runner) runCase(ctx context.Context, newClient func() (gator.Client, er } } -func (r *Runner) checkCase(ctx context.Context, newClient func() (gator.Client, error), suiteDir string, tc *Case) (trace *string, err error) { +func (r *Runner) checkCase(ctx context.Context, newClient func() (gator.Client, error), newExpander func() (*expand.Expander, error), suiteDir string, tc *Case) (trace *string, err error) { if tc.Object == "" { return nil, fmt.Errorf("%w: must define object", gator.ErrInvalidCase) } @@ -274,7 +304,7 @@ func (r *Runner) checkCase(ctx context.Context, newClient func() (gator.Client, return nil, fmt.Errorf("%w: assertions must be non-empty", gator.ErrInvalidCase) } - review, err := r.runReview(ctx, newClient, suiteDir, tc) + review, err := r.runReview(ctx, newClient, newExpander, suiteDir, tc) if err != nil { return nil, err } @@ -293,12 +323,17 @@ func (r *Runner) checkCase(ctx context.Context, newClient func() (gator.Client, return trace, nil } -func (r *Runner) runReview(ctx context.Context, newClient func() (gator.Client, error), suiteDir string, tc *Case) (*types.Responses, error) { +func (r *Runner) runReview(ctx context.Context, newClient func() (gator.Client, error), newExpander func() (*expand.Expander, error), suiteDir string, tc *Case) (*types.Responses, error) { c, err := newClient() if err != nil { return nil, err } + e, err := newExpander() + if err != nil { + return nil, err + } + toReviewPath := path.Join(suiteDir, tc.Object) toReviewObjs, err := readObjects(r.filesystem, toReviewPath) if err != nil { @@ -327,7 +362,32 @@ func (r *Runner) runReview(ctx context.Context, newClient func() (gator.Client, Object: *toReview, Source: mutationtypes.SourceTypeOriginal, } - return c.Review(ctx, au, reviews.EnforcementPoint(util.GatorEnforcementPoint)) + + review, err := c.Review(ctx, au, reviews.EnforcementPoint(util.GatorEnforcementPoint)) + + if e != nil { + resultants, err := e.Expand(toReview) + if err != nil { + return nil, fmt.Errorf("expanding resource %s: %w", toReview.GetName(), err) + } + + for _, resultant := range resultants { + au := target.AugmentedUnstructured{ + Object: *resultant.Obj, + Source: mutationtypes.SourceTypeGenerated, + } + resultantReview, err := c.Review(ctx, au, reviews.EnforcementPoint(util.GatorEnforcementPoint)) + if err != nil { + return nil, fmt.Errorf("reviewing expanded resource %v %s/%s: %w", + resultant.Obj.GroupVersionKind(), resultant.Obj.GetNamespace(), resultant.Obj.GetName(), err) + } + expansion.OverrideEnforcementAction(resultant.EnforcementAction, resultantReview) + expansion.AggregateResponses(resultant.TemplateName, review, resultantReview) + expansion.AggregateStats(resultant.TemplateName, review, resultantReview) + } + } + + return review, err } func (r *Runner) validateAndReviewAdmissionReviewRequest(ctx context.Context, c gator.Client, toReview *unstructured.Unstructured) (*types.Responses, error) { diff --git a/pkg/gator/verify/suite.go b/pkg/gator/verify/suite.go index 24cc51e5eb9..d454d7ec439 100644 --- a/pkg/gator/verify/suite.go +++ b/pkg/gator/verify/suite.go @@ -35,6 +35,10 @@ type Test struct { // the Suite. Must be an instance of Template. Constraint string `json:"constraint"` + // Expansion is the path to the Expansion, relative to the file defining + // the Suite. + Expansion string `json:"expansion"` + // Cases are the test cases to run on the instantiated Constraint. // Mutually exclusive with Invalid. Cases []*Case `json:"cases,omitempty"` From 9aa60fbc5638c8dffa2316e55051ca6cdcce7090 Mon Sep 17 00:00:00 2001 From: David-Jaeyoon-Lee Date: Wed, 16 Oct 2024 20:43:54 +0000 Subject: [PATCH 2/8] fix error handling Signed-off-by: David-Jaeyoon-Lee --- pkg/gator/verify/runner.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/gator/verify/runner.go b/pkg/gator/verify/runner.go index cc33e367534..2608b043273 100644 --- a/pkg/gator/verify/runner.go +++ b/pkg/gator/verify/runner.go @@ -125,9 +125,7 @@ func (r *Runner) runTest(ctx context.Context, suiteDir string, filter Filter, t start := time.Now() err := r.tryAddConstraint(ctx, suiteDir, t) - err = r.tryAddExpansion(suiteDir, t) var results []CaseResult - // What is this Invalid and where does it get set? I didn't see it get set in tryAddConstraints if t.Invalid { if errors.Is(err, constraints.ErrSchema) { err = nil @@ -197,6 +195,11 @@ func (r *Runner) runCases(ctx context.Context, suiteDir string, filter Filter, t return e, nil } + _, err := newExpander() + if err != nil { + return nil, err + } + results := make([]CaseResult, len(t.Cases)) for i, c := range t.Cases { From 4c16047fdf2e5b45362fc583e7ce4b881f6a003b Mon Sep 17 00:00:00 2001 From: David-Jaeyoon-Lee Date: Thu, 24 Oct 2024 00:33:31 +0000 Subject: [PATCH 3/8] fix lint errors, error handling, and unused functions Signed-off-by: David-Jaeyoon-Lee --- pkg/gator/errors.go | 2 +- pkg/gator/verify/runner.go | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pkg/gator/errors.go b/pkg/gator/errors.go index 82d14e5f617..ccf9076e76e 100644 --- a/pkg/gator/errors.go +++ b/pkg/gator/errors.go @@ -19,7 +19,7 @@ var ( // SyncSet. ErrNotAGVKManifest = errors.New("not a GVKManifest") // ErrNotAnExpansion indicates the user-indicated file does not contain a - // Constraint. + // ExpansionTemplate. ErrNotAnExpansion = errors.New("not an Expansion Template") // ErrAddingTemplate indicates a problem instantiating a Suite's ConstraintTemplate. ErrAddingTemplate = errors.New("adding template") diff --git a/pkg/gator/verify/runner.go b/pkg/gator/verify/runner.go index 2608b043273..9fcc4b29fc9 100644 --- a/pkg/gator/verify/runner.go +++ b/pkg/gator/verify/runner.go @@ -144,11 +144,6 @@ func (r *Runner) runTest(ctx context.Context, suiteDir string, filter Filter, t } } -func (r *Runner) tryAddExpansion(suiteDir string, t *Test) error { - _, err := r.makeTestExpander(suiteDir, t) - return err -} - func (r *Runner) tryAddConstraint(ctx context.Context, suiteDir string, t *Test) error { client, err := r.newClient(r.includeTrace, r.useK8sCEL) if err != nil { @@ -245,6 +240,10 @@ func (r *Runner) makeTestExpander(suiteDir string, t *Test) (*expand.Expander, e } et, err := reader.ReadExpansion(r.filesystem, path.Join(suiteDir, expansionPath)) + if err != nil { + return nil, err + } + er, err := expand.NewExpander([]*unstructured.Unstructured{et}) return er, err } From 1215c2393b82fcd34ffbae8e2a48c5d30f202217 Mon Sep 17 00:00:00 2001 From: David-Jaeyoon-Lee Date: Thu, 24 Oct 2024 22:55:20 +0000 Subject: [PATCH 4/8] add testing for gator verify expansion template support Signed-off-by: David-Jaeyoon-Lee --- pkg/gator/fixtures/fixtures.go | 72 +++++++++++++++++++++++++++++++++ pkg/gator/verify/runner_test.go | 62 ++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) diff --git a/pkg/gator/fixtures/fixtures.go b/pkg/gator/fixtures/fixtures.go index 220bb63487d..bf7e3007f3b 100644 --- a/pkg/gator/fixtures/fixtures.go +++ b/pkg/gator/fixtures/fixtures.go @@ -158,6 +158,34 @@ spec: } ` + TemplateRestrictCustomField = ` +kind: ConstraintTemplate +apiVersion: templates.gatekeeper.sh/v1beta1 +metadata: + name: restrictedcustomfield +spec: + crd: + spec: + names: + kind: RestrictedCustomField + validation: + openAPIV3Schema: + type: object + properties: + expectedCustomField: + type: boolean + targets: + - target: admission.k8s.gatekeeper.sh + rego: | + package restrictedcustomfield + violation[{"msg": msg}] { + got := input.review.object.spec.customField + expected := input.parameters.expectedCustomField + got == expected + msg := sprintf("foo object has restricted custom field value of %v", [expected]) + } +` + ConstraintAlwaysValidate = ` kind: AlwaysValidate apiVersion: constraints.gatekeeper.sh/v1beta1 @@ -262,6 +290,22 @@ metadata: name: other ` + ConstraintRestrictCustomField = ` +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: RestrictedCustomField +metadata: + name: restrict-foo-custom-field +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Foo"] + namespaces: + - "default" + parameters: + expectedCustomField: true +` + Object = ` kind: Object apiVersion: group.sh/v1 @@ -328,6 +372,17 @@ apiVersion: group.sh/v1 metadata: name: object` + ObjectFooTemplate = ` +apiVersion: apps/v1 +kind: FooTemplate +metadata: + name: foo-template +spec: + template: + spec: + customField: true +` + NamespaceSelected = ` kind: Namespace apiVersion: /v1 @@ -682,4 +737,21 @@ spec: - apiGroups: ["*"] kinds: ["*"] ` + + ExpansionRestrictCustomField = ` +apiVersion: expansion.gatekeeper.sh/v1alpha1 +kind: ExpansionTemplate +metadata: + name: expand-foo +spec: + applyTo: + - groups: [ "apps" ] + kinds: [ "FooTemplate" ] + versions: [ "v1" ] + templateSource: "spec.template" + generatedGVK: + kind: "Foo" + group: "" + version: "v1" +` ) diff --git a/pkg/gator/verify/runner_test.go b/pkg/gator/verify/runner_test.go index 2c901f8acc4..9dad0480753 100644 --- a/pkg/gator/verify/runner_test.go +++ b/pkg/gator/verify/runner_test.go @@ -1170,6 +1170,68 @@ func TestRunner_Run(t *testing.T) { }, }, }, + { + name: "expansion system", + suite: Suite{ + Tests: []Test{ + { + Name: "check custom field with expansion system", + Template: "template.yaml", + Constraint: "constraint.yaml", + Expansion: "expansion.yaml", + Cases: []*Case{ + { + Name: "Foo Template object", + Object: "foo-template.yaml", + Assertions: []Assertion{{Message: ptr.To[string]("foo object has restricted custom field")}}, + }, + }, + }, + { + Name: "check custom field without expansion system", + Template: "template.yaml", + Constraint: "constraint.yaml", + Cases: []*Case{ + { + Name: "Foo Template object", + Object: "foo-template.yaml", + Assertions: []Assertion{{Violations: gator.IntStrFromStr("no")}}, + }, + }, + }, + }, + }, + f: fstest.MapFS{ + "template.yaml": &fstest.MapFile{ + Data: []byte(fixtures.TemplateRestrictCustomField), + }, + "constraint.yaml": &fstest.MapFile{ + Data: []byte(fixtures.ConstraintRestrictCustomField), + }, + "foo-template.yaml": &fstest.MapFile{ + Data: []byte(fixtures.ObjectFooTemplate), + }, + "expansion.yaml": &fstest.MapFile{ + Data: []byte(fixtures.ExpansionRestrictCustomField), + }, + }, + want: SuiteResult{ + TestResults: []TestResult{ + { + Name: "check custom field with expansion system", + CaseResults: []CaseResult{ + {Name: "Foo Template object"}, + }, + }, + { + Name: "check custom field without expansion system", + CaseResults: []CaseResult{ + {Name: "Foo Template object"}, + }, + }, + }, + }, + }, } for _, tc := range testCases { From be6e9f66bdc0165830d735831da4b091f7b93862 Mon Sep 17 00:00:00 2001 From: David-Jaeyoon-Lee Date: Wed, 30 Oct 2024 19:56:08 +0000 Subject: [PATCH 5/8] Rebase to master & fix merge conflicts Signed-off-by: David-Jaeyoon-Lee --- pkg/gator/errors.go | 1 + pkg/gator/reader/read_resources.go | 2 +- pkg/gator/verify/runner.go | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/gator/errors.go b/pkg/gator/errors.go index ccf9076e76e..a5e504ddeb9 100644 --- a/pkg/gator/errors.go +++ b/pkg/gator/errors.go @@ -19,6 +19,7 @@ var ( // SyncSet. ErrNotAGVKManifest = errors.New("not a GVKManifest") // ErrNotAnExpansion indicates the user-indicated file does not contain a + // ErrNotAnExpansion indicates the user-indicated file does not contain an // ExpansionTemplate. ErrNotAnExpansion = errors.New("not an Expansion Template") // ErrAddingTemplate indicates a problem instantiating a Suite's ConstraintTemplate. diff --git a/pkg/gator/reader/read_resources.go b/pkg/gator/reader/read_resources.go index 3244f09618f..64b3f3483a5 100644 --- a/pkg/gator/reader/read_resources.go +++ b/pkg/gator/reader/read_resources.go @@ -250,7 +250,7 @@ func ReadExpansion(f fs.FS, path string) (*unstructured.Unstructured, error) { } gvk := u.GroupVersionKind() - if gvk.Group != "expansion.gatekeeper.sh" { + if gvk.Group != "expansion.gatekeeper.sh" || gvk.Kind != "ExpansionTemplate" { return nil, gator.ErrNotAnExpansion } diff --git a/pkg/gator/verify/runner.go b/pkg/gator/verify/runner.go index 9fcc4b29fc9..2f995124c24 100644 --- a/pkg/gator/verify/runner.go +++ b/pkg/gator/verify/runner.go @@ -366,6 +366,10 @@ func (r *Runner) runReview(ctx context.Context, newClient func() (gator.Client, } review, err := c.Review(ctx, au, reviews.EnforcementPoint(util.GatorEnforcementPoint)) + if err != nil { + return nil, fmt.Errorf("reviewing %v %s/%s: %w", + toReview.GroupVersionKind(), toReview.GetNamespace(), toReview.GetName(), err) + } if e != nil { resultants, err := e.Expand(toReview) From cb5cab8ac3029b50e7e86629196d521d2d0c0afe Mon Sep 17 00:00:00 2001 From: David-Jaeyoon-Lee Date: Mon, 4 Nov 2024 19:56:48 +0000 Subject: [PATCH 6/8] Add/update resources for testing expansion under the test directory for gator verify Signed-off-by: David-Jaeyoon-Lee --- test/gator/verify/allow_expansion.yaml | 8 ++++++++ test/gator/verify/deny_expansion.yaml | 8 ++++++++ test/gator/verify/expansion.yaml | 15 +++++++++++++++ test/gator/verify/suite.yaml | 16 +++++++++++++++- 4 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 test/gator/verify/allow_expansion.yaml create mode 100644 test/gator/verify/deny_expansion.yaml create mode 100644 test/gator/verify/expansion.yaml diff --git a/test/gator/verify/allow_expansion.yaml b/test/gator/verify/allow_expansion.yaml new file mode 100644 index 00000000000..3f9e8dfd86b --- /dev/null +++ b/test/gator/verify/allow_expansion.yaml @@ -0,0 +1,8 @@ +apiVersion: apps/v1 +kind: FooTemplate +metadata: + name: foo-template +spec: + template: + foo: bar + diff --git a/test/gator/verify/deny_expansion.yaml b/test/gator/verify/deny_expansion.yaml new file mode 100644 index 00000000000..45c107adf6e --- /dev/null +++ b/test/gator/verify/deny_expansion.yaml @@ -0,0 +1,8 @@ +apiVersion: apps/v1 +kind: FooTemplate +metadata: + name: foo-template +spec: + template: + foo: qux + diff --git a/test/gator/verify/expansion.yaml b/test/gator/verify/expansion.yaml new file mode 100644 index 00000000000..08a1b4a27ad --- /dev/null +++ b/test/gator/verify/expansion.yaml @@ -0,0 +1,15 @@ +apiVersion: expansion.gatekeeper.sh/v1alpha1 +kind: ExpansionTemplate +metadata: + name: expand-foo +spec: + applyTo: + - groups: [ "apps" ] + kinds: [ "FooTemplate" ] + versions: [ "v1" ] + templateSource: "spec.template" + generatedGVK: + kind: "FooIsBar" + group: "" + version: "v1" + diff --git a/test/gator/verify/suite.yaml b/test/gator/verify/suite.yaml index 1b769ccc193..61782b5a839 100644 --- a/test/gator/verify/suite.yaml +++ b/test/gator/verify/suite.yaml @@ -36,4 +36,18 @@ tests: - name: foo-not-bar object: deny.yaml assertions: - - violations: no \ No newline at end of file + - violations: no +- name: foo-is-bar-expansion + template: template.yaml + constraint: constraint.yaml + expansion: expansion.yaml + cases: + - name: foo-bar + object: allow_expansion.yaml + assertions: + - violations: no + - name: foo-not-bar + object: deny_expansion.yaml + assertions: + - violations: yes + From 104fafc1a06aedc4fd7df10735f1bf06343e2b53 Mon Sep 17 00:00:00 2001 From: David-Jaeyoon-Lee Date: Wed, 6 Nov 2024 04:38:54 +0000 Subject: [PATCH 7/8] Fix comments on errors.go Signed-off-by: David-Jaeyoon-Lee --- pkg/gator/errors.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/gator/errors.go b/pkg/gator/errors.go index a5e504ddeb9..bef2dbf9d01 100644 --- a/pkg/gator/errors.go +++ b/pkg/gator/errors.go @@ -15,10 +15,9 @@ var ( // ErrNotASyncSet indicates the user-indicated file does not contain a // SyncSet. ErrNotASyncSet = errors.New("not a SyncSet") - // ErrNotASyncSet indicates the user-indicated file does not contain a - // SyncSet. + // ErrNotAGVKManifest indicates the user-indicated file does not contain a + // GVK Manifest. ErrNotAGVKManifest = errors.New("not a GVKManifest") - // ErrNotAnExpansion indicates the user-indicated file does not contain a // ErrNotAnExpansion indicates the user-indicated file does not contain an // ExpansionTemplate. ErrNotAnExpansion = errors.New("not an Expansion Template") From 6ac162821d4850111ff5dc3e73f55900e9f09e7b Mon Sep 17 00:00:00 2001 From: David-Jaeyoon-Lee Date: Wed, 6 Nov 2024 22:30:17 +0000 Subject: [PATCH 8/8] Add gator verify expansion support details in website docs Signed-off-by: David-Jaeyoon-Lee --- website/docs/gator.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/website/docs/gator.md b/website/docs/gator.md index 6a11425c4e1..924aed02749 100644 --- a/website/docs/gator.md +++ b/website/docs/gator.md @@ -128,7 +128,7 @@ gator test --filename=manifests-and-policies/ --output=json `gator verify` organizes tests into three levels: Suites, Tests, and Cases: - A Suite is a file which defines Tests. -- A Test declares a ConstraintTemplate, a Constraint, and Cases to test the +- A Test declares a ConstraintTemplate, a Constraint, an ExpansionTemplate (optional), and Cases to test the Constraint. - A Case defines an object to validate and whether the object is expected to pass validation. @@ -162,6 +162,8 @@ ConstraintTemplate. It is an error for the Constraint to have a different type than that defined in the ConstraintTemplate spec.crd.spec.names.kind, or for the ConstraintTemplate to not compile. +A Test can also optionally compile an ExpansionTemplate. + ### Cases Each Test contains a list of Cases under the `cases` field. @@ -264,6 +266,25 @@ the `run` flag: gator verify path/to/suites/... --run "disallowed" ``` +### Validating Generated Resources with ExpansionTemplates +`gator verify` may be used along with expansion templates to validate generated resources. The expansion template is optionally declared at the test level. If an expansion template is set for a test, gator will attempt to expand each object under the test. The violations for the parent object & its expanded resources will be aggregated. + +Example for declaring an expansion template in a Gator Suite: +```yaml +apiVersion: test.gatekeeper.sh/v1alpha1 +kind: Suite +tests: +- name: expansion + template: template.yaml + constraint: constraint.yaml + expansion: expansion.yaml + cases: + - name: example-expand + object: deployment.yaml + assertions: + - violations: yes +``` + ### Validating Metadata-Based Constraint Templates `gator verify` may be used with an [`AdmissionReview`](https://pkg.go.dev/k8s.io/kubernetes/pkg/apis/admission#AdmissionReview)