Skip to content

Commit

Permalink
Marshal supported GVKs up front
Browse files Browse the repository at this point in the history
Signed-off-by: Anlan Du <[email protected]>
  • Loading branch information
anlandu committed Nov 4, 2023
1 parent 0fc5c29 commit e798bfd
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 58 deletions.
4 changes: 2 additions & 2 deletions cmd/gator/sync/verify/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var Cmd = &cobra.Command{
var (
flagFilenames []string
flagImages []string
flagSupportedGVKs string
flagSupportedGVKs verify.SupportedGVKs
)

const (
Expand All @@ -32,7 +32,7 @@ const (
func init() {
Cmd.Flags().StringArrayVarP(&flagFilenames, flagNameFilename, "f", []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().StringVarP(&flagSupportedGVKs, flagSupportedGVKs, "d", "", "a json string listing the GVKs supported by the cluster as a nested array of groups, containing supported versions, each of which contains supported kinds. See https://open-policy-agent.github.io/gatekeeper/website/docs/gator#the-gator-sync-verify-subcommand for an example.")
Cmd.Flags().VarP(&flagSupportedGVKs, flagNameSupportedGVKs, "s", "a json string listing the GVKs supported by the cluster as a nested array of groups, containing supported versions, each of which contains supported kinds. See https://open-policy-agent.github.io/gatekeeper/website/docs/gator#the-gator-sync-verify-subcommand for an example.")
}

func run(cmd *cobra.Command, args []string) {
Expand Down
29 changes: 1 addition & 28 deletions pkg/gator/reader/read_resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"github.com/open-policy-agent/gatekeeper/v3/pkg/gator"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/yaml"
)

Expand Down Expand Up @@ -104,7 +103,7 @@ func ReadTemplate(scheme *runtime.Scheme, f fs.FS, path string) (*templates.Cons
return template, nil
}

// ToStructured converts an unstructured object into an object with the schema defined
// ToTemplate converts an unstructured object into an object with the schema defined
// by u's group, version, and kind.
func ToStructured(scheme *runtime.Scheme, u *unstructured.Unstructured) (runtime.Object, error) {
gvk := u.GroupVersionKind()
Expand Down Expand Up @@ -255,32 +254,6 @@ func ReadK8sResources(r io.Reader) ([]*unstructured.Unstructured, error) {
return objs, nil
}

type DiscoveryResults map[schema.GroupVersionKind]struct{}

func ReadDiscoveryResults(r string) (DiscoveryResults, error) {
if r == "" {
return nil, nil
}
var stringAsJSON map[string]map[string][]string
if err := json.Unmarshal([]byte(r), &stringAsJSON); err != nil {
return nil, err
}
results := DiscoveryResults{}
for group, versions := range stringAsJSON {
for version, kinds := range versions {
for _, kind := range kinds {
results[schema.GroupVersionKind{
Group: group,
Version: version,
Kind: kind,
}] = struct{}{}
}
}
}

return results, nil
}

func IsTemplate(u *unstructured.Unstructured) bool {
gvk := u.GroupVersionKind()
return gvk.Group == templatesv1.SchemeGroupVersion.Group && gvk.Kind == "ConstraintTemplate"
Expand Down
45 changes: 37 additions & 8 deletions pkg/gator/sync/verify/verify.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package verify

import (
"encoding/json"
"fmt"

cfapis "github.com/open-policy-agent/frameworks/constraint/pkg/apis"
Expand All @@ -13,6 +14,39 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
)

type SupportedGVKs map[schema.GroupVersionKind]struct{}

func (s *SupportedGVKs) String() string {
return fmt.Sprintf("%v", *s)
}

func (s *SupportedGVKs) Set(value string) error {
if value == "" {
return nil
}
var stringAsJSON map[string]map[string][]string
if err := json.Unmarshal([]byte(value), &stringAsJSON); err != nil {
return err
}
*s = SupportedGVKs{}
for group, versions := range stringAsJSON {
for version, kinds := range versions {
for _, kind := range kinds {
(*s)[schema.GroupVersionKind{
Group: group,
Version: version,
Kind: kind,
}] = struct{}{}
}
}
}
return nil
}

func (s *SupportedGVKs) Type() string {
return "SupportedGVKs"
}

var scheme *runtime.Scheme

func init() {
Expand All @@ -29,12 +63,7 @@ func init() {

// Reads a list of unstructured objects and a string containing supported GVKs and
// outputs a set of missing sync requirements per template and ingestion problems per template

Check failure on line 65 in pkg/gator/sync/verify/verify.go

View workflow job for this annotation

GitHub Actions / Lint

Comment should end in a period (godot)
func Verify(unstrucs []*unstructured.Unstructured, flagSupportedGVKs string) (map[string]parser.SyncRequirements, map[string]error, error) {
discoveryResults, err := reader.ReadDiscoveryResults(flagSupportedGVKs)
if err != nil {
return nil, nil, fmt.Errorf("reading: %w", err)
}

func Verify(unstrucs []*unstructured.Unstructured, supportedGVKs SupportedGVKs) (map[string]parser.SyncRequirements, map[string]error, error) {
templates := []*templates.ConstraintTemplate{}
syncedGVKs := map[schema.GroupVersionKind]struct{}{}
templateErrs := map[string]error{}
Expand All @@ -52,7 +81,7 @@ func Verify(unstrucs []*unstructured.Unstructured, flagSupportedGVKs string) (ma
Version: gvkEntry.Version,
Kind: gvkEntry.Kind,
}
if _, exists := discoveryResults[gvk]; exists || discoveryResults == nil {
if _, exists := supportedGVKs[gvk]; exists || supportedGVKs == nil {
syncedGVKs[gvk] = struct{}{}
}
}
Expand All @@ -71,7 +100,7 @@ func Verify(unstrucs []*unstructured.Unstructured, flagSupportedGVKs string) (ma
Version: syncOnlyEntry.Version,
Kind: syncOnlyEntry.Kind,
}
if _, exists := discoveryResults[gvk]; exists || discoveryResults == nil {
if _, exists := supportedGVKs[gvk]; exists || supportedGVKs == nil {
syncedGVKs[gvk] = struct{}{}
}
}
Expand Down
49 changes: 30 additions & 19 deletions pkg/gator/sync/verify/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ import (

func TestVerify(t *testing.T) {
tcs := []struct {
name string
inputs []string
discovery string
wantReqs map[string][]parser.GVKEquivalenceSet
wantErrs map[string]error
err error
name string
inputs []string
supportedGVKs SupportedGVKs
wantReqs map[string]parser.SyncRequirements
wantErrs map[string]error
err error
}{
{
name: "basic req unfulfilled",
inputs: []string{
fixtures.TemplateReferential,
},
wantReqs: map[string][]parser.GVKEquivalenceSet{
wantReqs: map[string]parser.SyncRequirements{
"k8suniqueserviceselector": {
parser.GVKEquivalenceSet{
{
Expand All @@ -45,7 +45,7 @@ func TestVerify(t *testing.T) {
fixtures.TemplateReferential,
fixtures.TemplateReferentialBadAnnotation,
},
wantReqs: map[string][]parser.GVKEquivalenceSet{
wantReqs: map[string]parser.SyncRequirements{
"k8suniqueserviceselector": {
parser.GVKEquivalenceSet{
{
Expand All @@ -66,7 +66,7 @@ func TestVerify(t *testing.T) {
fixtures.TemplateReferential,
fixtures.Config,
},
wantReqs: map[string][]parser.GVKEquivalenceSet{},
wantReqs: map[string]parser.SyncRequirements{},
wantErrs: map[string]error{},
},
{
Expand All @@ -75,18 +75,29 @@ func TestVerify(t *testing.T) {
fixtures.TemplateReferential,
fixtures.Config,
},
discovery: `{"": {"v1": ["Service"]}}`,
wantReqs: map[string][]parser.GVKEquivalenceSet{},
wantErrs: map[string]error{},
supportedGVKs: SupportedGVKs{
{
Group: "",
Version: "v1",
Kind: "Service",
}: struct{}{},
},
wantReqs: map[string]parser.SyncRequirements{},
wantErrs: map[string]error{},
},
{
name: "basic req fulfilled by syncset but not discoveryresults",
inputs: []string{
fixtures.TemplateReferential,
fixtures.Config,
},
discovery: `{"extensions": {"v1beta1": ["Ingress"]}}`,
wantReqs: map[string][]parser.GVKEquivalenceSet{
supportedGVKs: SupportedGVKs{
{
Group: "extensions",
Version: "v1beta1",
Kind: "Ingress",
}: struct{}{},
}, wantReqs: map[string]parser.SyncRequirements{
"k8suniqueserviceselector": {
parser.GVKEquivalenceSet{
{
Expand All @@ -105,7 +116,7 @@ func TestVerify(t *testing.T) {
fixtures.TemplateReferentialMultEquivSets,
fixtures.SyncSet,
},
wantReqs: map[string][]parser.GVKEquivalenceSet{},
wantReqs: map[string]parser.SyncRequirements{},
wantErrs: map[string]error{},
},
{
Expand All @@ -114,7 +125,7 @@ func TestVerify(t *testing.T) {
fixtures.TemplateReferentialMultReqs,
fixtures.SyncSet,
},
wantReqs: map[string][]parser.GVKEquivalenceSet{
wantReqs: map[string]parser.SyncRequirements{
"k8suniqueingresshostmultireq": {
parser.GVKEquivalenceSet{
{
Expand All @@ -136,7 +147,7 @@ func TestVerify(t *testing.T) {
fixtures.Config,
fixtures.SyncSet,
},
wantReqs: map[string][]parser.GVKEquivalenceSet{
wantReqs: map[string]parser.SyncRequirements{
"k8suniqueingresshostmultireq": {
parser.GVKEquivalenceSet{
{
Expand All @@ -152,7 +163,7 @@ func TestVerify(t *testing.T) {
{
name: "no data of any kind",
inputs: []string{},
wantReqs: map[string][]parser.GVKEquivalenceSet{},
wantReqs: map[string]parser.SyncRequirements{},
wantErrs: map[string]error{},
},
}
Expand All @@ -167,7 +178,7 @@ func TestVerify(t *testing.T) {
objs = append(objs, u)
}

gotReqs, gotErrs, err := Verify(objs, tc.discovery)
gotReqs, gotErrs, err := Verify(objs, tc.supportedGVKs)
if tc.err != nil {
require.ErrorIs(t, err, tc.err)
} else if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion website/docs/gator.md
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ Or, using an OCI Artifact containing templates as described previously:
gator sync verify --filename="config.yaml" --image=localhost:5000/gator/template-library:v1
```
Optionally, the `--discovery-results` flag can be used to pass in a list of supported GVKs discovered on a cluster. If this is nonempty, only GVKs that are both included in a SyncSet/Config and are supported will be considered to fulfill a template's requirements. Discovery results should be passed as a string representing a JSON object mapping groups to lists of versions, which contain lists of kinds, like such:
Optionally, the `--supported-gvks` flag can be used to pass in a list of supported GVKs for the applicable cluster. If this is nonempty, only GVKs that are both included in a SyncSet/Config and are supported will be considered to fulfill a template's requirements. Supported GVKs should be passed as a string representing a JSON object mapping groups to lists of versions, which contain lists of kinds, like such:
```
{
"group1": {
Expand Down

0 comments on commit e798bfd

Please sign in to comment.