mirror of
https://github.com/niusmallnan/steve.git
synced 2025-07-04 18:26:18 +00:00
Adding tests
Adds tests for: - Cache clearing functionality - Very basic schema generation
This commit is contained in:
parent
115eb31f57
commit
b449b93643
168
pkg/schema/factory_test.go
Normal file
168
pkg/schema/factory_test.go
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/rancher/apiserver/pkg/types"
|
||||||
|
"github.com/rancher/wrangler/pkg/schemas"
|
||||||
|
k8sSchema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testGroup = "test.k8s.io"
|
||||||
|
testVersion = "v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type schemaTestConfig struct {
|
||||||
|
permissionVerbs []string
|
||||||
|
desiredResourceVerbs []string
|
||||||
|
desiredCollectionVerbs []string
|
||||||
|
errDesired bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSchemas(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
config schemaTestConfig
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "basic get schema test",
|
||||||
|
config: schemaTestConfig{
|
||||||
|
permissionVerbs: []string{"get"},
|
||||||
|
desiredResourceVerbs: []string{"GET"},
|
||||||
|
desiredCollectionVerbs: []string{"GET"},
|
||||||
|
errDesired: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
test := test
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
// test caching functionality
|
||||||
|
mockLookup := newMockAccessSetLookup()
|
||||||
|
userName := "testUser"
|
||||||
|
testUser := user.DefaultInfo{
|
||||||
|
Name: userName,
|
||||||
|
UID: userName,
|
||||||
|
Groups: []string{},
|
||||||
|
Extra: map[string][]string{},
|
||||||
|
}
|
||||||
|
|
||||||
|
collection := NewCollection(context.TODO(), types.EmptyAPISchemas(), mockLookup)
|
||||||
|
collection.schemas = map[string]*types.APISchema{"testCRD": makeSchema("testCRD")}
|
||||||
|
runSchemaTest(t, test.config, mockLookup, collection, &testUser)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestSchemaCache(t *testing.T) {
|
||||||
|
// Schemas are a frequently used resource. It's important that the cache doesn't have a leak given size/frequency of resource
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
before schemaTestConfig
|
||||||
|
after schemaTestConfig
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "permissions increase, cache size same",
|
||||||
|
before: schemaTestConfig{
|
||||||
|
permissionVerbs: []string{"get"},
|
||||||
|
desiredResourceVerbs: []string{"GET"},
|
||||||
|
desiredCollectionVerbs: []string{"GET"},
|
||||||
|
errDesired: false,
|
||||||
|
},
|
||||||
|
after: schemaTestConfig{
|
||||||
|
permissionVerbs: []string{"get", "create", "delete"},
|
||||||
|
desiredResourceVerbs: []string{"GET", "DELETE"},
|
||||||
|
desiredCollectionVerbs: []string{"GET", "POST"},
|
||||||
|
errDesired: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "permissions decrease, cache size same",
|
||||||
|
before: schemaTestConfig{
|
||||||
|
permissionVerbs: []string{"get", "create", "delete"},
|
||||||
|
desiredResourceVerbs: []string{"GET", "DELETE"},
|
||||||
|
desiredCollectionVerbs: []string{"GET", "POST"},
|
||||||
|
errDesired: false,
|
||||||
|
},
|
||||||
|
after: schemaTestConfig{
|
||||||
|
permissionVerbs: []string{"get"},
|
||||||
|
desiredResourceVerbs: []string{"GET"},
|
||||||
|
desiredCollectionVerbs: []string{"GET"},
|
||||||
|
errDesired: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
test := test
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
// test caching functionality
|
||||||
|
mockLookup := newMockAccessSetLookup()
|
||||||
|
userName := "testUser"
|
||||||
|
testUser := user.DefaultInfo{
|
||||||
|
Name: userName,
|
||||||
|
UID: userName,
|
||||||
|
Groups: []string{},
|
||||||
|
Extra: map[string][]string{},
|
||||||
|
}
|
||||||
|
collection := NewCollection(context.TODO(), types.EmptyAPISchemas(), mockLookup)
|
||||||
|
collection.schemas = map[string]*types.APISchema{"testCRD": makeSchema("testCRD")}
|
||||||
|
runSchemaTest(t, test.before, mockLookup, collection, &testUser)
|
||||||
|
assert.Len(t, collection.cache.Keys(), 1, "expected cache to be size 1")
|
||||||
|
mockLookup.Clear()
|
||||||
|
runSchemaTest(t, test.after, mockLookup, collection, &testUser)
|
||||||
|
assert.Len(t, collection.cache.Keys(), 1, "expected cache to be size 1")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runSchemaTest(t *testing.T, config schemaTestConfig, lookup *mockAccessSetLookup, collection *Collection, testUser user.Info) {
|
||||||
|
for _, verb := range config.permissionVerbs {
|
||||||
|
lookup.AddAccessForUser(testUser, verb, k8sSchema.GroupResource{Group: testGroup, Resource: "testCRD"}, "*", "*")
|
||||||
|
}
|
||||||
|
|
||||||
|
collection.schemas = map[string]*types.APISchema{"testCRD": makeSchema("testCRD")}
|
||||||
|
userSchemas, err := collection.Schemas(testUser)
|
||||||
|
if config.errDesired {
|
||||||
|
assert.Error(t, err, "expected error but none was found")
|
||||||
|
}
|
||||||
|
var testSchema *types.APISchema
|
||||||
|
for schemaName, userSchema := range userSchemas.Schemas {
|
||||||
|
if schemaName == "testCRD" {
|
||||||
|
testSchema = userSchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.NotNil(t, testSchema, "expected a test schema, but was nil")
|
||||||
|
assert.Len(t, testSchema.ResourceMethods, len(config.desiredResourceVerbs), "did not get as many verbs as expected for resource methods")
|
||||||
|
assert.Len(t, testSchema.CollectionMethods, len(config.desiredCollectionVerbs), "did not get as many verbs as expected for resource methods")
|
||||||
|
for _, verb := range config.desiredResourceVerbs {
|
||||||
|
assert.Contains(t, testSchema.ResourceMethods, verb, "did not find %s in resource methods %v", verb, testSchema.ResourceMethods)
|
||||||
|
}
|
||||||
|
for _, verb := range config.desiredCollectionVerbs {
|
||||||
|
assert.Contains(t, testSchema.CollectionMethods, verb, "did not find %s in resource methods %v", verb, testSchema.CollectionMethods)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeSchema(resourceType string) *types.APISchema {
|
||||||
|
return &types.APISchema{
|
||||||
|
Schema: &schemas.Schema{
|
||||||
|
ID: resourceType,
|
||||||
|
CollectionMethods: []string{},
|
||||||
|
ResourceMethods: []string{},
|
||||||
|
ResourceFields: map[string]schemas.Field{
|
||||||
|
"name": {Type: "string"},
|
||||||
|
"value": {Type: "string"},
|
||||||
|
},
|
||||||
|
Attributes: map[string]interface{}{
|
||||||
|
"group": testGroup,
|
||||||
|
"version": testVersion,
|
||||||
|
"resource": resourceType,
|
||||||
|
"verbs": []string{"get", "list", "watch", "delete", "update", "create"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
75
pkg/schema/mock_test.go
Normal file
75
pkg/schema/mock_test.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"hash"
|
||||||
|
|
||||||
|
"github.com/rancher/steve/pkg/accesscontrol"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
insideSeparator = "&"
|
||||||
|
outsideSeparator = "%"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mockAccessSetLookup struct {
|
||||||
|
accessSets map[string]*accesscontrol.AccessSet
|
||||||
|
currentHash map[string]hash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMockAccessSetLookup() *mockAccessSetLookup {
|
||||||
|
return &mockAccessSetLookup{
|
||||||
|
accessSets: map[string]*accesscontrol.AccessSet{},
|
||||||
|
currentHash: map[string]hash.Hash{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockAccessSetLookup) AccessFor(user user.Info) *accesscontrol.AccessSet {
|
||||||
|
if set, ok := m.accessSets[user.GetName()]; ok {
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockAccessSetLookup) PurgeUserData(id string) {
|
||||||
|
var foundKey string
|
||||||
|
for key, value := range m.accessSets {
|
||||||
|
if value.ID == id {
|
||||||
|
foundKey = key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if foundKey != "" {
|
||||||
|
delete(m.accessSets, foundKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockAccessSetLookup) AddAccessForUser(user user.Info, verb string, gr schema.GroupResource, namespace string, name string) {
|
||||||
|
currentAccessSet, ok := m.accessSets[user.GetName()]
|
||||||
|
var currentHash hash.Hash
|
||||||
|
if !ok {
|
||||||
|
currentAccessSet = &accesscontrol.AccessSet{}
|
||||||
|
currentHash = sha256.New()
|
||||||
|
} else {
|
||||||
|
currentHash = m.currentHash[currentAccessSet.ID]
|
||||||
|
}
|
||||||
|
currentAccessSet.Add(verb, gr, accesscontrol.Access{Namespace: namespace, ResourceName: name})
|
||||||
|
calculateAccessSetID(currentHash, verb, gr, namespace, name)
|
||||||
|
currentAccessSet.ID = hex.EncodeToString(currentHash.Sum(nil))
|
||||||
|
m.accessSets[user.GetName()] = currentAccessSet
|
||||||
|
m.currentHash[currentAccessSet.ID] = currentHash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockAccessSetLookup) Clear() {
|
||||||
|
m.accessSets = map[string]*accesscontrol.AccessSet{}
|
||||||
|
m.currentHash = map[string]hash.Hash{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateAccessSetID(digest hash.Hash, verb string, gr schema.GroupResource, namespace string, name string) {
|
||||||
|
digest.Write([]byte(verb + insideSeparator))
|
||||||
|
digest.Write([]byte(gr.String() + insideSeparator))
|
||||||
|
digest.Write([]byte(namespace + insideSeparator))
|
||||||
|
digest.Write([]byte(name + outsideSeparator))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user