Skip to content

Commit

Permalink
Support for indexing and selecting fields and overriding TriggerFunc.
Browse files Browse the repository at this point in the history
Added test for making sure field selection works
  • Loading branch information
pwittrock committed Jan 22, 2018
1 parent e52fe6d commit c99ca7e
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 21 deletions.
28 changes: 14 additions & 14 deletions cmd/apiregister-gen/generators/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -704,13 +704,13 @@ func (apigroup *APIGroup) DoType(t *types.Type) (*Struct, []*types.Type) {

uType = prefix + uImportName + "." + name

fmt.Printf("\nDifferent Parent Package: %s\nChild Package: %s\nKind: %s (Kind.String() %s)\nImport stmt: %s\nType: %s\n\n",
pkg,
member.Type.Name.Package,
member.Type.Kind,
member.Type.String(),
uImport,
uType)
//fmt.Printf("\nDifferent Parent Package: %s\nChild Package: %s\nKind: %s (Kind.String() %s)\nImport stmt: %s\nType: %s\n\n",
// pkg,
// member.Type.Name.Package,
// member.Type.Kind,
// member.Type.String(),
// uImport,
// uType)
} else {
// Handle non- Pointer, Maps, Slices
pkg := t.Name.Package
Expand All @@ -727,13 +727,13 @@ func (apigroup *APIGroup) DoType(t *types.Type) (*Struct, []*types.Type) {
// Create the field type name - should be <pkgalias>.<TypeName>
uType = uImportName + "." + name

fmt.Printf("\nDifferent Parent Package: %s\nChild Package: %s\nKind: %s (Kind.String() %s)\nImport stmt: %s\nType: %s\n\n",
pkg,
member.Type.Name.Package,
member.Type.Kind,
member.Type.String(),
uImport,
uType)
//fmt.Printf("\nDifferent Parent Package: %s\nChild Package: %s\nKind: %s (Kind.String() %s)\nImport stmt: %s\nType: %s\n\n",
// pkg,
// member.Type.Name.Package,
// member.Type.Kind,
// member.Type.String(),
// uImport,
// uType)
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion cmd/apiregister-gen/generators/unversioned_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,19 @@ var (
{{ range $api := .UnversionedResources -}}
Internal{{ $api.Kind }} = builders.NewInternalResource(
"{{ $api.Resource }}",
"{{ $api.Kind }}",
func() runtime.Object { return &{{ $api.Kind }}{} },
func() runtime.Object { return &{{ $api.Kind }}List{} },
)
Internal{{ $api.Kind }}Status = builders.NewInternalResourceStatus(
"{{ $api.Resource }}",
"{{ $api.Kind }}Status",
func() runtime.Object { return &{{ $api.Kind }}{} },
func() runtime.Object { return &{{ $api.Kind }}List{} },
)
{{ range $subresource := .Subresources -}}
Internal{{$subresource.REST}} = builders.NewInternalSubresource(
"{{$subresource.Resource}}", "{{$subresource.Path}}",
"{{$subresource.Resource}}", "{{$subresource.Request}}", "{{$subresource.Path}}",
func() runtime.Object { return &{{$subresource.Request}}{} },
)
{{ end -}}
Expand Down
46 changes: 46 additions & 0 deletions example/pkg/apis/olympus/v1beta1/poseidon_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package v1beta1

import (
"fmt"
"log"

"k8s.io/api/core/v1"
Expand All @@ -28,6 +29,10 @@ import (

"github.com/kubernetes-incubator/apiserver-builder/example/pkg/apis/olympus"
"k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/storage"
)

// +genclient
Expand Down Expand Up @@ -69,3 +74,44 @@ func (PoseidonSchemeFns) DefaultingFunction(o interface{}) {
// set default field values here
log.Printf("Defaulting fields for Poseidon %s\n", obj.Name)
}

func (b PoseidonStrategy) TriggerFunc(obj runtime.Object) []storage.MatchValue {
// Change this function to override the trigger fn that is used
value := b.DefaultStorageStrategy.TriggerFunc(obj)
return value
}

// The following functions allow spec.deployment.name to be selected when listing
// or watching resources
func (b PoseidonStrategy) GetAttrs(o runtime.Object) (labels.Set, fields.Set, bool, error) {
// Change this function to override the attributes that are matched
l, _, uninit, e := b.DefaultStorageStrategy.GetAttrs(o)
obj := o.(*olympus.Poseidon)

fs := fields.Set{"spec.deployment.name": obj.Spec.Deployment.Name}
fs = generic.AddObjectMetaFieldsSet(fs, &obj.ObjectMeta, true)
return l, fs, uninit, e
}

func (b PoseidonStrategy) BasicMatch(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: b.GetAttrs,
IndexFields: []string{"spec.deployment.name"},
}
}

// All field selector fields must appear in this function
func (b PoseidonSchemeFns) FieldSelectorConversion(label, value string) (string, string, error) {
switch label {
case "metadata.name":
return label, value, nil
case "metadata.namespace":
return label, value, nil
case "spec.deployment.name":
return label, value, nil
default:
return "", "", fmt.Errorf("%q is not a known field selector: only %q, %q, %q", label, "metadata.name", "metadata.namespace", "spec.deployment.name")
}
}
45 changes: 45 additions & 0 deletions example/pkg/apis/olympus/v1beta1/poseidon_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,49 @@ var _ = Describe("Poseidon", func() {
})
})
})

Describe("when listing a resource", func() {
Context("using labels", func() {
It("shouldn't find the matchings objects because the functions are overriden", func() {
client = cs.OlympusV1beta1().Poseidons("poseidon-test-valid")

instance1 := Poseidon{}
instance1.Name = "instance-1"
instance1.Spec.Deployment.Name = "i1"
instance1.Labels = map[string]string{"foo": "1"}
expected1 := instance1

instance2 := Poseidon{}
instance2.Name = "instance-2"
instance2.Spec.Deployment.Name = "i2"
instance2.Labels = map[string]string{"foo": "2"}
expected2 := instance2

By("returning success from the create request")
_, err := client.Create(&instance1)
Expect(err).ShouldNot(HaveOccurred())

By("returning success from the create request")
_, err = client.Create(&instance2)
Expect(err).ShouldNot(HaveOccurred())

By("returning the item for list requests")
result, err := client.List(metav1.ListOptions{FieldSelector: "spec.deployment.name=i1"})
Expect(err).ShouldNot(HaveOccurred())
Expect(result.Items).To(HaveLen(1))
Expect(result.Items[0].Name).To(Equal(expected1.Name))

By("returning the item for list requests")
result, err = client.List(metav1.ListOptions{FieldSelector: "spec.deployment.name=i2"})
Expect(err).ShouldNot(HaveOccurred())
Expect(result.Items).To(HaveLen(1))
Expect(result.Items[0].Name).To(Equal(expected2.Name))

By("returning the item for list requests")
result, err = client.List(metav1.ListOptions{})
Expect(err).ShouldNot(HaveOccurred())
Expect(result.Items).To(HaveLen(2))
})
})
})
})
19 changes: 14 additions & 5 deletions pkg/builders/api_unversioned_resource_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,18 @@ import (
// name - name of the resource - e.g. "deployments"
// new - function for creating new empty UNVERSIONED instances - e.g. func() runtime.Object { return &Deployment{} }
// newList - function for creating an empty list of UNVERSIONED instances - e.g. func() runtime.Object { return &DeploymentList{} }
func NewInternalResource(name string, new, newList func() runtime.Object) UnversionedResourceBuilder {
return NewBuilder(name, "", new, newList, true)
func NewInternalResource(name, kind string, new, newList func() runtime.Object) UnversionedResourceBuilder {
return NewBuilder(name, kind, "", new, newList, true)
}

// NewInternalResourceStatus returns a new strategy for the status subresource of an object
// name - name of the resource - e.g. "deployments"
// new - function for creating new empty UNVERSIONED instances - e.g. func() runtime.Object { return &Deployment{} }
// newList - function for creating an empty list of UNVERSIONED instances - e.g. func() runtime.Object { return &DeploymentList{} }
func NewInternalResourceStatus(name string, new, newList func() runtime.Object) UnversionedResourceBuilder {
func NewInternalResourceStatus(name, kind string, new, newList func() runtime.Object) UnversionedResourceBuilder {
return NewBuilder(
name,
kind,
"status",
new, newList,
true)
Expand All @@ -44,9 +45,10 @@ func NewInternalResourceStatus(name string, new, newList func() runtime.Object)
// name - name of the resource - e.g. "deployments"
// path - path to the subresource - e.g. "scale"
// new - function for creating new empty UNVERSIONED instances - e.g. func() runtime.Object { return &Deployment{} }
func NewInternalSubresource(name, path string, new func() runtime.Object) UnversionedResourceBuilder {
func NewInternalSubresource(name, kind, path string, new func() runtime.Object) UnversionedResourceBuilder {
return NewBuilder(
name,
kind,
path,
new,
nil, // Don't provide a list function
Expand All @@ -55,13 +57,14 @@ func NewInternalSubresource(name, path string, new func() runtime.Object) Unvers
}

func NewBuilder(
name, path string,
name, kind, path string,
new, newList func() runtime.Object,
useRegistryStore bool) UnversionedResourceBuilder {

return &UnversionedResourceBuilderImpl{
path,
name,
kind,
new,
newList,
useRegistryStore,
Expand All @@ -78,12 +81,14 @@ type UnversionedResourceBuilder interface {

GetPath() string
GetName() string
GetKind() string
ShouldUseRegistryStore() bool
}

type UnversionedResourceBuilderImpl struct {
Path string
Name string
Kind string
NewFunc func() runtime.Object
NewListFunc func() runtime.Object
UseRegistryStore bool
Expand All @@ -97,6 +102,10 @@ func (b *UnversionedResourceBuilderImpl) GetName() string {
return b.Name
}

func (b *UnversionedResourceBuilderImpl) GetKind() string {
return b.Kind
}

func (b *UnversionedResourceBuilderImpl) ShouldUseRegistryStore() bool {
return b.UseRegistryStore
}
Expand Down
20 changes: 19 additions & 1 deletion pkg/builders/api_version_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ func NewApiVersion(group, version string) *VersionedApiBuilder {
b := &VersionedApiBuilder{
GroupVersion: schema.GroupVersion{group, version},
}
b.SchemaBuilder = runtime.NewSchemeBuilder(b.registerTypes, b.registerDefaults, b.registerConversions)
b.SchemaBuilder = runtime.NewSchemeBuilder(
b.registerTypes,
b.registerDefaults,
b.registerConversions,
b.registerSelectorConversions)
return b
}

Expand Down Expand Up @@ -92,6 +96,20 @@ func (s *VersionedApiBuilder) registerConversions(scheme *runtime.Scheme) error
return nil
}

func (s *VersionedApiBuilder) registerSelectorConversions(scheme *runtime.Scheme) error {
for _, k := range s.Kinds {
err := scheme.AddFieldLabelConversionFunc(
s.GroupVersion.String(),
k.Unversioned.GetKind(),
k.SchemeFns.FieldSelectorConversion)
if err != nil {
glog.Errorf("Failed to add conversion functions %v", err)
return err
}
}
return nil
}

// registerEndpoints registers the REST endpoints for all resources in this API group version
// group is the group to register the resources under
// optionsGetter is the RESTOptionsGetter provided by a server.Config
Expand Down
4 changes: 4 additions & 0 deletions pkg/builders/default_scheme_fns.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ func (DefaultSchemeFns) DefaultingFunction(interface{}) {}
func (DefaultSchemeFns) GetConversionFunctions() []interface{} { return []interface{}{} }

func (DefaultSchemeFns) Register(scheme *runtime.Scheme) error { return nil }

func (DefaultSchemeFns) FieldSelectorConversion(label, value string) (string, string, error) {
return runtime.DefaultMetaV1FieldSelectorConversion(label, value)
}
1 change: 1 addition & 0 deletions pkg/builders/resource_interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ type SchemeFns interface {
DefaultingFunction(obj interface{})
GetConversionFunctions() []interface{}
Register(scheme *runtime.Scheme) error
FieldSelectorConversion(label, value string) (string, string, error)
}

type StandardStorageProvider interface {
Expand Down

0 comments on commit c99ca7e

Please sign in to comment.