Skip to content

Commit

Permalink
Merge pull request #116 from nocturnalastro/warnAPIFail
Browse files Browse the repository at this point in the history
CNF-13910: Add warning if any api resources are missing a group
  • Loading branch information
openshift-merge-bot[bot] authored Oct 9, 2024
2 parents 54ea9d7 + 02ceef6 commit c163696
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 11 deletions.
53 changes: 45 additions & 8 deletions pkg/compare/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/cli-runtime/pkg/genericiooptions"
"k8s.io/cli-runtime/pkg/resource"
"k8s.io/client-go/discovery"
Expand Down Expand Up @@ -429,37 +430,73 @@ func (o *Options) setLiveSearchTypes(f kcmdutil.Factory) error {

// getSupportedResourceTypes retrieves a set of resource types that are supported by the cluster. For each supported
// resource type it will specify a list of groups where it exists.
func getSupportedResourceTypes(client discovery.CachedDiscoveryInterface) (map[string][]string, error) {
resources := make(map[string][]string)
lists, err := client.ServerPreferredResources()
func getSupportedResourceTypes(client discovery.CachedDiscoveryInterface) (map[string][]schema.GroupVersion, error) {
resources := make(map[string][]schema.GroupVersion)
_, lists, err := client.ServerGroupsAndResources()
if err != nil {
return resources, fmt.Errorf("failed to get clusters resource types: %w", err)
}
for _, list := range lists {
if len(list.APIResources) != 0 {
for _, res := range list.APIResources {
resources[res.Kind] = append(resources[res.Kind], res.Group)
gv := schema.GroupVersion{Group: res.Group, Version: res.Version}
if !slices.Contains(resources[res.Kind], gv) {
resources[res.Kind] = append(resources[res.Kind], gv)
}
}
}
}
return resources, nil
}

func getExpectedGroups(templates []ReferenceTemplate) []schema.GroupVersion {
groups := make([]schema.GroupVersion, 0)
for _, t := range templates {
gvk := t.GetMetadata().GroupVersionKind()
gv := schema.GroupVersion{Group: gvk.Group, Version: gvk.Version}
if gvk.Group != "" && !slices.Contains(groups, gv) {
groups = append(groups, gv)
}
}
return groups
}

// findAllRequestedSupportedTypes divides the requested types in to two groups: supported types and unsupported types based on if they are specified as supported.
// The list of supported types will include the types in the form of {kind}.{group}.
func findAllRequestedSupportedTypes(supportedTypesWithGroups map[string][]string, requestedTypes map[string][]ReferenceTemplate) ([]string, []string) {
func findAllRequestedSupportedTypes(supportedTypesWithGroups map[string][]schema.GroupVersion, requestedTypes map[string][]ReferenceTemplate) ([]string, []string) {
var typesIncludingGroup []string
var notSupportedTypes []string
for kind := range requestedTypes {
var badAPI []string
for kind, templates := range requestedTypes {
if _, ok := supportedTypesWithGroups[kind]; ok {
for _, group := range supportedTypesWithGroups[kind] {
typesIncludingGroup = append(typesIncludingGroup, strings.Join([]string{kind, group}, "."))
expectedGroups := getExpectedGroups(templates)
for _, gv := range supportedTypesWithGroups[kind] {
index := slices.Index(expectedGroups, gv)
if index > -1 {
expectedGroups = slices.Delete(expectedGroups, index, index+1)
}
var supported string
if gv.Group == "" {
supported = kind
} else {
supported = strings.Join([]string{kind, gv.Version, gv.Group}, ".")
}

typesIncludingGroup = append(typesIncludingGroup, supported)
}
for _, gv := range expectedGroups {
badAPI = append(badAPI, strings.Join([]string{kind, gv.Group + "/" + gv.Version}, "."))
}
} else {
notSupportedTypes = append(notSupportedTypes, kind)
}
}
if len(badAPI) > 0 {
slices.Sort(badAPI)
klog.Warningf(
"There may be an issue with the API resources exposed by the cluster. Found kind but missing group/version for %s ",
strings.Join(badAPI, ", "))
}
return typesIncludingGroup, notSupportedTypes
}

Expand Down
24 changes: 21 additions & 3 deletions pkg/compare/compare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ type Test struct {
outputFormat string
checks Checks
verboseOutput bool
badAPIResources bool

userOverridePath string
templToGenPatchFor []string
Expand Down Expand Up @@ -208,6 +209,7 @@ func (test Test) Clone() Test {
templToGenPatchFor: slices.Clone(test.templToGenPatchFor),
overrideGenReason: test.overrideGenReason,
referenceFileName: test.referenceFileName,
badAPIResources: test.badAPIResources,
}
}

Expand Down Expand Up @@ -283,6 +285,12 @@ func (test Test) withMetadataFile(referenceFileName string) Test {
return newTest
}

func (test Test) withBadAPIResources() Test {
newTest := test.Clone()
newTest.badAPIResources = true
return newTest
}

func (test *Test) subTestName(mode Mode) string {
name := test.name
if test.subTestSuffix != "" {
Expand Down Expand Up @@ -479,6 +487,12 @@ func TestCompareRun(t *testing.T) {
withSubTestSuffix("One Of").
withMetadataFile("metadata-one-of.yaml").
withChecks(defaultChecks.withPrefixedSuffix("oneOf")),

defaultTest("All Required Templates Exist And There Are No Diffs").
withSubTestSuffix("Bad API Resources").
withBadAPIResources().
withModes([]Mode{{Live, LocalRef}}).
withChecks(defaultChecks.withPrefixedSuffix("badAPI")),
}

tf := cmdtesting.NewTestFactory()
Expand Down Expand Up @@ -539,7 +553,7 @@ func getCommand(t *testing.T, test *Test, modeIndex int, tf *cmdtesting.TestFact
require.NoError(t, cmd.Flags().Set("filename", resourcesDir))
require.NoError(t, cmd.Flags().Set("recursive", "true"))
case Live:
discoveryResources, resources := getResources(t, resourcesDir)
discoveryResources, resources := getResources(t, *test, resourcesDir)
updateTestDiscoveryClient(tf, discoveryResources)
setClient(t, resources, tf)
}
Expand Down Expand Up @@ -612,7 +626,7 @@ func setClient(t *testing.T, resources []*unstructured.Unstructured, tf *cmdtest
}
}

func getResources(t *testing.T, resourcesDir string) ([]v1.APIResource, []*unstructured.Unstructured) {
func getResources(t *testing.T, test Test, resourcesDir string) ([]v1.APIResource, []*unstructured.Unstructured) {
var resources []*unstructured.Unstructured
var rL []v1.APIResource
require.NoError(t, filepath.Walk(resourcesDir,
Expand All @@ -634,7 +648,11 @@ func getResources(t *testing.T, resourcesDir string) ([]v1.APIResource, []*unstr
}
r := unstructured.Unstructured{Object: data}
resources = append(resources, &r)
rL = append(rL, v1.APIResource{Name: r.GetName(), Kind: r.GetKind(), Version: r.GetAPIVersion()})
res := v1.APIResource{Name: r.GetName(), Kind: r.GetKind(), Version: r.GroupVersionKind().Version, Group: r.GroupVersionKind().Group}
if test.badAPIResources {
res.Group = ""
}
rL = append(rL, res)
return nil
}))
return rL, resources
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
There may be an issue with the API resources exposed by the cluster. Found kind but missing group/version for ClusterRole.rbac.authorization.k8s.io/v1, ClusterRoleBinding.rbac.authorization.k8s.io/v1, Deployment.apps/v1, RoleBinding.rbac.authorization.k8s.io/v1
Summary
CRs with diffs: 0/14
No validation issues with the cluster
No CRs are unmatched to reference CRs
Metadata Hash: 933892b7ae8a4f5232734acc34f6c93fc223844d836b37af390cfeaecf0b7a99
No patched CRs

0 comments on commit c163696

Please sign in to comment.