diff --git a/cmd/apiregister-gen/generators/parser.go b/cmd/apiregister-gen/generators/parser.go index 91712ec63a..fec74111c9 100644 --- a/cmd/apiregister-gen/generators/parser.go +++ b/cmd/apiregister-gen/generators/parser.go @@ -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 @@ -727,13 +727,13 @@ func (apigroup *APIGroup) DoType(t *types.Type) (*Struct, []*types.Type) { // Create the field type name - should be . 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) } } } diff --git a/cmd/apiregister-gen/generators/unversioned_generator.go b/cmd/apiregister-gen/generators/unversioned_generator.go index ea8a0f4d5c..d188b41eba 100644 --- a/cmd/apiregister-gen/generators/unversioned_generator.go +++ b/cmd/apiregister-gen/generators/unversioned_generator.go @@ -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 -}} diff --git a/example/pkg/apis/olympus/v1beta1/poseidon_types.go b/example/pkg/apis/olympus/v1beta1/poseidon_types.go index f00283d0bc..0025fa7077 100644 --- a/example/pkg/apis/olympus/v1beta1/poseidon_types.go +++ b/example/pkg/apis/olympus/v1beta1/poseidon_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1beta1 import ( + "fmt" "log" "k8s.io/api/core/v1" @@ -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 @@ -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") + } +} diff --git a/example/pkg/apis/olympus/v1beta1/poseidon_types_test.go b/example/pkg/apis/olympus/v1beta1/poseidon_types_test.go index e027b29c61..b10c969391 100644 --- a/example/pkg/apis/olympus/v1beta1/poseidon_types_test.go +++ b/example/pkg/apis/olympus/v1beta1/poseidon_types_test.go @@ -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)) + }) + }) + }) }) diff --git a/pkg/builders/api_unversioned_resource_builder.go b/pkg/builders/api_unversioned_resource_builder.go index d9c181e6de..74046b21d1 100644 --- a/pkg/builders/api_unversioned_resource_builder.go +++ b/pkg/builders/api_unversioned_resource_builder.go @@ -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) @@ -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 @@ -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, @@ -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 @@ -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 } diff --git a/pkg/builders/api_version_builder.go b/pkg/builders/api_version_builder.go index 53f4161c24..a3e2f9c968 100644 --- a/pkg/builders/api_version_builder.go +++ b/pkg/builders/api_version_builder.go @@ -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 } @@ -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 diff --git a/pkg/builders/default_scheme_fns.go b/pkg/builders/default_scheme_fns.go index 8aed5c862a..2bd6e39466 100644 --- a/pkg/builders/default_scheme_fns.go +++ b/pkg/builders/default_scheme_fns.go @@ -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) +} diff --git a/pkg/builders/resource_interfaces.go b/pkg/builders/resource_interfaces.go index fa50dd8a7e..68cb505263 100644 --- a/pkg/builders/resource_interfaces.go +++ b/pkg/builders/resource_interfaces.go @@ -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 {