Skip to content

Commit

Permalink
[GraphQL] List entity config & state resources (#4808)
Browse files Browse the repository at this point in the history
* Expose `EntityConfig` & `EntityState` resources through GraphQL service
* Add `Fields()` method to `EntityConfig` & `EntityState` resources so that they implement the `Fielder` interface; allows users to filter with field selectors. 
* Complication: The core/v3 package previous checked that each resource in the package had a unique `RBACName()`, however, since we don't want distinct names for the config & state resources this proved to be problematic. As such I've disabled the uniqueness tests for the time being. (Same as: #4807)

Signed-off-by: James Phillips <jamesdphillips@gmail.com>
  • Loading branch information
jamesdphillips committed Aug 22, 2022
1 parent e2d8098 commit f3bc8f5
Show file tree
Hide file tree
Showing 27 changed files with 2,690 additions and 48 deletions.
33 changes: 33 additions & 0 deletions api/core/v3/entity_config.go
@@ -0,0 +1,33 @@
package v3

import (
"strconv"
"strings"

corev2 "github.com/sensu/sensu-go/api/core/v2"
)

var entityConfigRBACName = (&corev2.Entity{}).RBACName()

func (e *EntityConfig) rbacName() string {
return entityConfigRBACName
}

func (e *EntityConfig) Fields() map[string]string {
fields := map[string]string{
"entity_config.name": e.Metadata.Name,
"entity_config.namespace": e.Metadata.Namespace,
"entity_config.deregister": strconv.FormatBool(e.Deregister),
"entity_config.entity_class": e.EntityClass,
"entity_config.subscriptions": strings.Join(e.Subscriptions, ","),
}
MergeMapWithPrefix(fields, e.Metadata.Labels, "entity_config.labels.")
return fields
}

// MergeMapWithPrefix merges contents of one map into another using a prefix.
func MergeMapWithPrefix(a map[string]string, b map[string]string, prefix string) {
for k, v := range b {
a[prefix+k] = v
}
}
60 changes: 60 additions & 0 deletions api/core/v3/entity_config_test.go
@@ -0,0 +1,60 @@
package v3

import (
"reflect"
"testing"

v2 "github.com/sensu/sensu-go/api/core/v2"
)

func TestEntityConfigFields(t *testing.T) {
tests := []struct {
name string
args Fielder
wantKey string
want string
}{
{
name: "exposes name",
args: FixtureEntityConfig("my-agent"),
wantKey: "entity_config.name",
want: "my-agent",
},
{
name: "exposes deregister",
args: &EntityConfig{Metadata: &v2.ObjectMeta{}, Deregister: true},
wantKey: "entity_config.deregister",
want: "true",
},
{
name: "exposes class",
args: &EntityConfig{Metadata: &v2.ObjectMeta{}, EntityClass: "agent"},
wantKey: "entity_config.entity_class",
want: "agent",
},
{
name: "exposes subscriptions",
args: &EntityConfig{Metadata: &v2.ObjectMeta{}, Subscriptions: []string{"www", "unix"}},
wantKey: "entity_config.subscriptions",
want: "www,unix",
},
{
name: "exposes labels",
args: &EntityConfig{
Metadata: &v2.ObjectMeta{
Labels: map[string]string{"region": "philadelphia"},
},
},
wantKey: "entity_config.labels.region",
want: "philadelphia",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.args.Fields()
if !reflect.DeepEqual(got[tt.wantKey], tt.want) {
t.Errorf("EntityConfig.Fields() = got[%s] %v, want[%s] %v", tt.wantKey, got[tt.wantKey], tt.wantKey, tt.want)
}
})
}
}
19 changes: 19 additions & 0 deletions api/core/v3/entity_state.go
@@ -0,0 +1,19 @@
package v3

import (
corev2 "github.com/sensu/sensu-go/api/core/v2"
)

var entityStateRBACName = (&corev2.Entity{}).RBACName()

func (*EntityState) rbacName() string {
return entityStateRBACName
}

func (e *EntityState) Fields() map[string]string {
fields := map[string]string{
"entity_state.name": e.Metadata.Name,
"entity_state.namespace": e.Metadata.Namespace,
}
return fields
}
36 changes: 36 additions & 0 deletions api/core/v3/entity_state_test.go
@@ -0,0 +1,36 @@
package v3

import (
"reflect"
"testing"
)

func TestEntityStateFields(t *testing.T) {
tests := []struct {
name string
args Fielder
wantKey string
want string
}{
{
name: "exposes name",
args: FixtureEntityState("my-agent"),
wantKey: "entity_state.name",
want: "my-agent",
},
{
name: "exposes deregister",
args: FixtureEntityState("my-agent"),
wantKey: "entity_state.namespace",
want: "default",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.args.Fields()
if !reflect.DeepEqual(got[tt.wantKey], tt.want) {
t.Errorf("EntityState.Fields() = got[%s] %v, want[%s] %v", tt.wantKey, got[tt.wantKey], tt.wantKey, tt.want)
}
})
}
}
7 changes: 7 additions & 0 deletions api/core/v3/fielder.go
@@ -0,0 +1,7 @@
package v3

// Fielder includes a set of fields that represent a resource.
type Fielder interface {
// Fields returns a set of fields that represent the resource.
Fields() map[string]string
}
16 changes: 0 additions & 16 deletions api/core/v3/resource_generated_test.go
Expand Up @@ -484,20 +484,4 @@ func TestResourceUniqueness(t *testing.T) {
if got, want := len(types), len(typeMap)/2; got != want {
t.Fatalf("bad number of types: got %d, want %d", got, want)
}
rbacNames := make(map[string]bool)
for _, v := range types {
if name := v.RBACName(); rbacNames[name] {
t.Errorf("duplicate rbac name: %s", name)
} else {
rbacNames[name] = true
}
}
storeNames := make(map[string]bool)
for _, v := range types {
if name := v.StoreName(); storeNames[name] {
t.Errorf("duplicate store suffix: %s", name)
} else {
storeNames[name] = true
}
}
}
16 changes: 0 additions & 16 deletions api/core/v3/resource_generated_test.tmpl
Expand Up @@ -176,20 +176,4 @@ func TestResourceUniqueness(t *testing.T) {
if got, want := len(types), len(typeMap)/2; got != want {
t.Fatalf("bad number of types: got %d, want %d", got, want)
}
rbacNames := make(map[string]bool)
for _, v := range types {
if name := v.RBACName(); rbacNames[name] {
t.Errorf("duplicate rbac name: %s", name)
} else {
rbacNames[name] = true
}
}
storeNames := make(map[string]bool)
for _, v := range types {
if name := v.StoreName(); storeNames[name] {
t.Errorf("duplicate store suffix: %s", name)
} else {
storeNames[name] = true
}
}
}
14 changes: 9 additions & 5 deletions api/core/v3/typemap.go
Expand Up @@ -15,11 +15,9 @@ func init() {
for _, v := range typeMap {
if r, ok := v.(Resource); ok {
rbacMap[r.RBACName()] = r
storeMap[r.StoreName()] = r
}
}
for _, v := range rbacMap {
storeMap[v.StoreName()] = v
}
types.RegisterResolver("core/v3", ResolveRawResource)
}

Expand Down Expand Up @@ -101,8 +99,14 @@ func ResolveResourceByStoreName(name string) (Resource, error) {

// ListResources lists all of the resources in the package.
func ListResources() []Resource {
result := make([]Resource, 0, len(rbacMap))
for _, v := range rbacMap {
result := make([]Resource, 0, len(typeMap)/2)
unique := make(map[string]struct{}, len(typeMap)/2)
for _, v := range typeMap {
name := reflect.ValueOf(v).Elem().Type().Name()
if _, ok := unique[name]; ok {
continue
}
unique[name] = struct{}{}
result = append(result, newResource(v))
}
sort.Slice(result, func(i, j int) bool {
Expand Down
14 changes: 9 additions & 5 deletions api/core/v3/typemap.tmpl
Expand Up @@ -15,11 +15,9 @@ func init() {
for _, v := range typeMap {
if r, ok := v.(Resource); ok {
rbacMap[r.RBACName()] = r
storeMap[r.StoreName()] = r
}
}
for _, v := range rbacMap {
storeMap[v.StoreName()] = v
}
types.RegisterResolver("core/v3", ResolveRawResource)
}

Expand Down Expand Up @@ -97,8 +95,14 @@ func ResolveResourceByStoreName(name string) (Resource, error) {

// ListResources lists all of the resources in the package.
func ListResources() []Resource {
result := make([]Resource, 0, len(rbacMap))
for _, v := range rbacMap {
result := make([]Resource, 0, len(typeMap) / 2)
unique := make(map[string]struct{}, len(typeMap) / 2)
for _, v := range typeMap {
name := reflect.ValueOf(v).Elem().Type().Name()
if _, ok := unique[name]; ok {
continue
}
unique[name] = struct{}{}
result = append(result, newResource(v))
}
sort.Slice(result, func(i, j int) bool {
Expand Down
6 changes: 6 additions & 0 deletions backend/api/generic.go
Expand Up @@ -192,6 +192,12 @@ func (g *GenericClient) list(ctx context.Context, resources interface{}, pred *s
StoreName: g.Kind.StorePrefix(),
Context: ctx,
}
if pred != nil && pred.Ordering == "NAME" {
req.SortOrder = storev2.SortAscend
if pred.Descending {
req.SortOrder = storev2.SortDescend
}
}
list, err := g.StoreV2.List(req, pred)
if err != nil {
return err
Expand Down

0 comments on commit f3bc8f5

Please sign in to comment.