mirror of
https://github.com/grafana/grafana.git
synced 2026-01-15 05:35:41 +00:00
Compare commits
4 Commits
ash/react-
...
iam-team-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f211a7f7f | ||
|
|
7b47fc814e | ||
|
|
18e2fb8c3e | ||
|
|
c6a7312b57 |
@@ -46,6 +46,8 @@ replace github.com/grafana/grafana/apps/annotation => ../annotation
|
||||
|
||||
replace github.com/grafana/grafana/apps/collections => ../collections
|
||||
|
||||
replace github.com/grafana/grafana/pkg/semconv => ../../pkg/semconv
|
||||
|
||||
replace github.com/prometheus/alertmanager => github.com/grafana/prometheus-alertmanager v0.25.1-0.20250911094103-5456b6e45604
|
||||
|
||||
require (
|
||||
@@ -94,7 +96,7 @@ require (
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
|
||||
github.com/Masterminds/squirrel v1.5.4 // indirect
|
||||
github.com/NYTimes/gziphandler v1.1.1 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.6 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.3.0 // indirect
|
||||
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f // indirect
|
||||
github.com/Yiling-J/theine-go v0.6.2 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect
|
||||
@@ -235,8 +237,9 @@ require (
|
||||
github.com/grafana/grafana/apps/secret v0.0.0 // indirect
|
||||
github.com/grafana/grafana/pkg/aggregator v0.0.0 // indirect
|
||||
github.com/grafana/grafana/pkg/apiserver v0.0.0 // indirect
|
||||
github.com/grafana/grafana/pkg/plugins v0.0.0-20260113153209-1d3f09d5193a // indirect
|
||||
github.com/grafana/grafana/pkg/promlib v0.0.8 // indirect
|
||||
github.com/grafana/grafana/pkg/semconv v0.0.0-20250804150913-990f1c69ecc2 // indirect
|
||||
github.com/grafana/grafana/pkg/semconv v0.0.0 // indirect
|
||||
github.com/grafana/otel-profiling-go v0.5.1 // indirect
|
||||
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect
|
||||
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
|
||||
|
||||
@@ -122,8 +122,8 @@ github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1/go.mod h1:8cl44BDmi+
|
||||
github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk=
|
||||
github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw=
|
||||
@@ -186,8 +186,8 @@ github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cq
|
||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
|
||||
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
|
||||
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
|
||||
github.com/RoaringBitmap/roaring v1.9.3 h1:t4EbC5qQwnisr5PrP9nt0IRhRTb9gMUgQF4t4S2OByM=
|
||||
github.com/RoaringBitmap/roaring v1.9.3/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
|
||||
github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg=
|
||||
@@ -871,10 +871,10 @@ github.com/grafana/grafana/apps/example v0.0.0-20251027162426-edef69fdc82b h1:6B
|
||||
github.com/grafana/grafana/apps/example v0.0.0-20251027162426-edef69fdc82b/go.mod h1:6+wASOCN8LWt6FJ8dc0oODUBIEY5XHaE6ABi8g0mR+k=
|
||||
github.com/grafana/grafana/apps/quotas v0.0.0-20251209183543-1013d74f13f2 h1:rDPMdshj3QMvpXn+wK4T8awF9n2sd8i4YRiGqX2xTvg=
|
||||
github.com/grafana/grafana/apps/quotas v0.0.0-20251209183543-1013d74f13f2/go.mod h1:M7bV60iRB61y0ISPG1HX/oNLZtlh0ZF22rUYwNkAKjo=
|
||||
github.com/grafana/grafana/pkg/plugins v0.0.0-20260113153209-1d3f09d5193a h1:UmIA+Amg0Kgm56vSyqCUQQ5MBD4svQ+smCzIbAXYzyo=
|
||||
github.com/grafana/grafana/pkg/plugins v0.0.0-20260113153209-1d3f09d5193a/go.mod h1:b9WxBFbMdf6pDxy90WRFHMyBl1/o8xY86SqnLLLN/yQ=
|
||||
github.com/grafana/grafana/pkg/promlib v0.0.8 h1:VUWsqttdf0wMI4j9OX9oNrykguQpZcruudDAFpJJVw0=
|
||||
github.com/grafana/grafana/pkg/promlib v0.0.8/go.mod h1:U1ezG/MGaEPoThqsr3lymMPN5yIPdVTJnDZ+wcXT+ao=
|
||||
github.com/grafana/grafana/pkg/semconv v0.0.0-20250804150913-990f1c69ecc2 h1:A65jWgLk4Re28gIuZcpC0aTh71JZ0ey89hKGE9h543s=
|
||||
github.com/grafana/grafana/pkg/semconv v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:2HRzUK/xQEYc+8d5If/XSusMcaYq9IptnBSHACiQcOQ=
|
||||
github.com/grafana/jsonparser v0.0.0-20240425183733-ea80629e1a32 h1:NznuPwItog+rwdVg8hAuGKP29ndRSzJAwhxKldkP8oQ=
|
||||
github.com/grafana/jsonparser v0.0.0-20240425183733-ea80629e1a32/go.mod h1:796sq+UcONnSlzA3RtlBZ+b/hrerkZXiEmO8oMjyRwY=
|
||||
github.com/grafana/loki/pkg/push v0.0.0-20250823105456-332df2b20000 h1:/5LKSYgLmAhwA4m6iGUD4w1YkydEWWjazn9qxCFT8W0=
|
||||
|
||||
@@ -17,4 +17,8 @@ teambindingv0alpha1: teambindingKind & {
|
||||
schema: {
|
||||
spec: v0alpha1.TeamBindingSpec
|
||||
}
|
||||
SelectableFields: [
|
||||
"spec.teamRef.name",
|
||||
"spec.subject.name",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -334,6 +334,22 @@ func AddAuthNKnownTypes(scheme *runtime.Scheme) error {
|
||||
&metav1.PartialObjectMetadata{},
|
||||
&metav1.PartialObjectMetadataList{},
|
||||
)
|
||||
|
||||
// Enable field selectors for TeamBinding
|
||||
err := scheme.AddFieldLabelConversionFunc(
|
||||
TeamBindingResourceInfo.GroupVersionKind(),
|
||||
func(label, value string) (string, string, error) {
|
||||
switch label {
|
||||
case "metadata.name", "metadata.namespace", "spec.teamRef.name", "spec.subject.name":
|
||||
return label, value, nil
|
||||
default:
|
||||
return "", "", fmt.Errorf("field label not supported for TeamBinding: %s", label)
|
||||
}
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,37 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
)
|
||||
|
||||
// schema is unexported to prevent accidental overwrites
|
||||
var (
|
||||
schemaTeamBinding = resource.NewSimpleSchema("iam.grafana.app", "v0alpha1", NewTeamBinding(), &TeamBindingList{}, resource.WithKind("TeamBinding"),
|
||||
resource.WithPlural("teambindings"), resource.WithScope(resource.NamespacedScope))
|
||||
resource.WithPlural("teambindings"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{{
|
||||
FieldSelector: "spec.teamRef.name",
|
||||
FieldValueFunc: func(o resource.Object) (string, error) {
|
||||
cast, ok := o.(*TeamBinding)
|
||||
if !ok {
|
||||
return "", errors.New("provided object must be of type *TeamBinding")
|
||||
}
|
||||
|
||||
return cast.Spec.TeamRef.Name, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
FieldSelector: "spec.subject.name",
|
||||
FieldValueFunc: func(o resource.Object) (string, error) {
|
||||
cast, ok := o.(*TeamBinding)
|
||||
if !ok {
|
||||
return "", errors.New("provided object must be of type *TeamBinding")
|
||||
}
|
||||
|
||||
return cast.Spec.Subject.Name, nil
|
||||
},
|
||||
},
|
||||
}))
|
||||
kindTeamBinding = resource.Kind{
|
||||
Schema: schemaTeamBinding,
|
||||
Codecs: map[resource.KindEncoding]resource.Codec{
|
||||
|
||||
4
apps/iam/pkg/apis/iam_manifest.go
generated
4
apps/iam/pkg/apis/iam_manifest.go
generated
@@ -147,6 +147,10 @@ var appManifestData = app.ManifestData{
|
||||
Plural: "TeamBindings",
|
||||
Scope: "Namespaced",
|
||||
Conversion: false,
|
||||
SelectableFields: []string{
|
||||
"spec.teamRef.name",
|
||||
"spec.subject.name",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
type ListTeamBindingsQuery struct {
|
||||
UID string
|
||||
OrgID int64
|
||||
TeamUID string
|
||||
UserUID string
|
||||
Pagination common.Pagination
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,12 @@ WHERE
|
||||
{{ if .Query.UID }}
|
||||
AND tm.uid = {{ .Arg .Query.UID }}
|
||||
{{ end }}
|
||||
{{ if .Query.TeamUID }}
|
||||
AND t.uid = {{ .Arg .Query.TeamUID }}
|
||||
{{ end }}
|
||||
{{ if .Query.UserUID }}
|
||||
AND u.uid = {{ .Arg .Query.UserUID }}
|
||||
{{ end }}
|
||||
{{- if .Query.Pagination.Continue }}
|
||||
AND tm.id >= {{ .Arg .Query.Pagination.Continue }}
|
||||
{{- end }}
|
||||
|
||||
@@ -357,7 +357,9 @@ func (b *IdentityAccessManagementAPIBuilder) UpdateTeamsAPIGroup(opts builder.AP
|
||||
|
||||
func (b *IdentityAccessManagementAPIBuilder) UpdateTeamBindingsAPIGroup(opts builder.APIGroupOptions, storage map[string]rest.Storage, enableZanzanaSync bool) error {
|
||||
teamBindingResource := iamv0.TeamBindingResourceInfo
|
||||
teamBindingUniStore, err := grafanaregistry.NewRegistryStore(opts.Scheme, teamBindingResource, opts.OptsGetter)
|
||||
teamBindingUniStore, err := grafanaregistry.NewRegistryStoreWithSelectableFields(opts.Scheme, teamBindingResource, opts.OptsGetter, grafanaregistry.SelectableFieldsOptions{
|
||||
GetAttrs: teambinding.GetAttrs,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
41
pkg/registry/apis/iam/teambinding/fields.go
Normal file
41
pkg/registry/apis/iam/teambinding/fields.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package teambinding
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
iamv0alpha1 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
)
|
||||
|
||||
// GetTeamBindingSelectableFields returns a field set that can be used for field selectors.
|
||||
func GetTeamBindingSelectableFields(obj *iamv0alpha1.TeamBinding) fields.Set {
|
||||
objectMetaFields := generic.ObjectMetaFieldsSet(&obj.ObjectMeta, true)
|
||||
|
||||
// Using the generated schema to get selectable fields dynamically
|
||||
schema := iamv0alpha1.TeamBindingSchema()
|
||||
specificFields := fields.Set{}
|
||||
|
||||
for _, selectableField := range schema.SelectableFields() {
|
||||
val, err := selectableField.FieldValueFunc(obj)
|
||||
if err != nil {
|
||||
val = ""
|
||||
}
|
||||
specificFields[selectableField.FieldSelector] = val
|
||||
}
|
||||
|
||||
return generic.MergeFieldsSets(objectMetaFields, specificFields)
|
||||
}
|
||||
|
||||
// GetAttrs returns labels and fields of a TeamBinding object.
|
||||
// This is used by the storage layer for filtering.
|
||||
func GetAttrs(o runtime.Object) (labels.Set, fields.Set, error) {
|
||||
tb, ok := o.(*iamv0alpha1.TeamBinding)
|
||||
if !ok {
|
||||
return nil, nil, errors.New("provided object must be of type *TeamBinding")
|
||||
}
|
||||
|
||||
return labels.Set(tb.Labels), GetTeamBindingSelectableFields(tb), nil
|
||||
}
|
||||
@@ -276,9 +276,20 @@ func (l *LegacyBindingStore) List(ctx context.Context, options *internalversion.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := l.store.ListTeamBindings(ctx, ns, legacy.ListTeamBindingsQuery{
|
||||
query := legacy.ListTeamBindingsQuery{
|
||||
Pagination: common.PaginationFromListOptions(options),
|
||||
})
|
||||
}
|
||||
|
||||
if options.FieldSelector != nil {
|
||||
if name, ok := options.FieldSelector.RequiresExactMatch("spec.teamRef.name"); ok {
|
||||
query.TeamUID = name
|
||||
}
|
||||
if name, ok := options.FieldSelector.RequiresExactMatch("spec.subject.name"); ok {
|
||||
query.UserUID = name
|
||||
}
|
||||
}
|
||||
|
||||
res, err := l.store.ListTeamBindings(ctx, ns, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ import (
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
iamv0alpha1 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
@@ -65,6 +67,7 @@ func TestIntegrationTeamBindings(t *testing.T) {
|
||||
require.NotNil(t, user)
|
||||
|
||||
doTeamBindingCRUDTestsUsingTheNewAPIs(t, helper, team, user)
|
||||
doTeamBindingFieldSelectionTests(t, helper)
|
||||
|
||||
if mode < 3 {
|
||||
doTeamBindingCRUDTestsUsingTheLegacyAPIs(t, helper, mode)
|
||||
@@ -101,16 +104,17 @@ func doTeamBindingCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHel
|
||||
require.NotEmpty(t, createdUID)
|
||||
|
||||
// Get the team binding
|
||||
fetched, err := teamBindingClient.Resource.Get(ctx, createdUID, metav1.GetOptions{})
|
||||
response, err := teamBindingClient.Resource.Get(ctx, createdUID, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, fetched)
|
||||
require.NotNil(t, response)
|
||||
|
||||
fetchedSpec := fetched.Object["spec"].(map[string]interface{})
|
||||
require.Equal(t, user.GetName(), fetchedSpec["subject"].(map[string]interface{})["name"])
|
||||
require.Equal(t, team.GetName(), fetchedSpec["teamRef"].(map[string]interface{})["name"])
|
||||
require.Equal(t, "admin", fetchedSpec["permission"])
|
||||
require.Equal(t, false, fetchedSpec["external"])
|
||||
require.Equal(t, createdUID, fetched.GetName())
|
||||
var actual iamv0alpha1.TeamBinding
|
||||
require.NoError(t, runtime.DefaultUnstructuredConverter.FromUnstructured(response.Object, &actual))
|
||||
require.Equal(t, user.GetName(), actual.Spec.Subject.Name)
|
||||
require.Equal(t, team.GetName(), actual.Spec.TeamRef.Name)
|
||||
require.Equal(t, iamv0alpha1.TeamBindingTeamPermissionAdmin, actual.Spec.Permission)
|
||||
require.False(t, actual.Spec.External)
|
||||
require.Equal(t, createdUID, actual.Name)
|
||||
|
||||
// Update the team binding
|
||||
toUpdate := toCreate.DeepCopy()
|
||||
@@ -127,16 +131,16 @@ func doTeamBindingCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHel
|
||||
require.Equal(t, false, updatedSpec["external"])
|
||||
|
||||
// Get the team binding
|
||||
fetched, err = teamBindingClient.Resource.Get(ctx, createdUID, metav1.GetOptions{})
|
||||
response, err = teamBindingClient.Resource.Get(ctx, createdUID, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, fetched)
|
||||
require.NotNil(t, response)
|
||||
|
||||
fetchedSpec = fetched.Object["spec"].(map[string]interface{})
|
||||
require.Equal(t, user.GetName(), fetchedSpec["subject"].(map[string]interface{})["name"])
|
||||
require.Equal(t, team.GetName(), fetchedSpec["teamRef"].(map[string]interface{})["name"])
|
||||
require.Equal(t, "member", fetchedSpec["permission"])
|
||||
require.Equal(t, false, fetchedSpec["external"])
|
||||
require.Equal(t, createdUID, fetched.GetName())
|
||||
require.NoError(t, runtime.DefaultUnstructuredConverter.FromUnstructured(response.Object, &actual))
|
||||
require.Equal(t, user.GetName(), actual.Spec.Subject.Name)
|
||||
require.Equal(t, team.GetName(), actual.Spec.TeamRef.Name)
|
||||
require.Equal(t, iamv0alpha1.TeamBindingTeamPermissionMember, actual.Spec.Permission)
|
||||
require.False(t, actual.Spec.External)
|
||||
require.Equal(t, createdUID, actual.Name)
|
||||
|
||||
// Delete the team binding
|
||||
err = teamBindingClient.Resource.Delete(ctx, createdUID, metav1.DeleteOptions{})
|
||||
@@ -488,14 +492,136 @@ func doTeamBindingCRUDTestsUsingTheLegacyAPIs(t *testing.T, helper *apis.K8sTest
|
||||
GVR: gvrTeamBindings,
|
||||
})
|
||||
|
||||
teamBinding, err := teamBindingClient.Resource.Get(ctx, teamBindingName, metav1.GetOptions{})
|
||||
response, err := teamBindingClient.Resource.Get(ctx, teamBindingName, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, teamBinding)
|
||||
require.NotNil(t, response)
|
||||
|
||||
teamBindingSpec := teamBinding.Object["spec"].(map[string]interface{})
|
||||
require.Equal(t, "member", teamBindingSpec["permission"])
|
||||
require.Equal(t, userRsp.Result.UID, teamBindingSpec["subject"].(map[string]interface{})["name"])
|
||||
require.Equal(t, teamRsp.Result.UID, teamBindingSpec["teamRef"].(map[string]interface{})["name"])
|
||||
require.Equal(t, teamBindingName, teamBinding.GetName())
|
||||
var actual iamv0alpha1.TeamBinding
|
||||
require.NoError(t, runtime.DefaultUnstructuredConverter.FromUnstructured(response.Object, &actual))
|
||||
require.Equal(t, iamv0alpha1.TeamBindingTeamPermissionMember, actual.Spec.Permission)
|
||||
require.Equal(t, userRsp.Result.UID, actual.Spec.Subject.Name)
|
||||
require.Equal(t, teamRsp.Result.UID, actual.Spec.TeamRef.Name)
|
||||
require.Equal(t, teamBindingName, actual.Name)
|
||||
})
|
||||
}
|
||||
|
||||
func doTeamBindingFieldSelectionTests(t *testing.T, helper *apis.K8sTestHelper) {
|
||||
t.Run("should list team bindings using field selectors", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
var teamNames []string
|
||||
var userNames []string
|
||||
var bindingNames []string
|
||||
|
||||
teamBindingClient := helper.GetResourceClient(apis.ResourceClientArgs{
|
||||
User: helper.Org1.Admin,
|
||||
Namespace: helper.Namespacer(helper.Org1.Admin.Identity.GetOrgID()),
|
||||
GVR: gvrTeamBindings,
|
||||
})
|
||||
teamClient := helper.GetResourceClient(apis.ResourceClientArgs{
|
||||
User: helper.Org1.Admin,
|
||||
Namespace: helper.Namespacer(helper.Org1.Admin.Identity.GetOrgID()),
|
||||
GVR: gvrTeams,
|
||||
})
|
||||
userClient := helper.GetResourceClient(apis.ResourceClientArgs{
|
||||
User: helper.Org1.Admin,
|
||||
Namespace: helper.Namespacer(helper.Org1.Admin.Identity.GetOrgID()),
|
||||
GVR: gvrUsers,
|
||||
})
|
||||
|
||||
// Helper to create teams
|
||||
createTeam := func(name string, email string) *unstructured.Unstructured {
|
||||
obj := helper.LoadYAMLOrJSONFile("testdata/team-test-create-v0.yaml")
|
||||
obj.SetName(name)
|
||||
if email != "" {
|
||||
obj.Object["spec"].(map[string]interface{})["email"] = email
|
||||
}
|
||||
|
||||
created, err := teamClient.Resource.Create(ctx, obj, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
teamNames = append(teamNames, created.GetName())
|
||||
return created
|
||||
}
|
||||
|
||||
// Helper to create users
|
||||
createUser := func(name string, email string, login string) *unstructured.Unstructured {
|
||||
obj := helper.LoadYAMLOrJSONFile("testdata/user-test-create-v0.yaml")
|
||||
obj.SetName(name)
|
||||
|
||||
spec := obj.Object["spec"].(map[string]interface{})
|
||||
spec["email"] = email
|
||||
spec["login"] = login
|
||||
|
||||
created, err := userClient.Resource.Create(ctx, obj, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
userNames = append(userNames, created.GetName())
|
||||
return created
|
||||
}
|
||||
|
||||
teamA := createTeam("team-a", "teama@example.com")
|
||||
teamB := createTeam("team-b", "teamb@example.com")
|
||||
user1 := createUser("user-1", "user1@example.com", "user1")
|
||||
user2 := createUser("user-2", "user2@example.com", "user2")
|
||||
|
||||
createBinding := func(user *unstructured.Unstructured, team *unstructured.Unstructured) {
|
||||
toCreate := helper.LoadYAMLOrJSONFile("testdata/teambinding-test-create-v0.yaml")
|
||||
toCreate.SetName("")
|
||||
toCreate.SetGenerateName("binding-")
|
||||
toCreate.Object["spec"].(map[string]interface{})["subject"].(map[string]interface{})["name"] = user.GetName()
|
||||
toCreate.Object["spec"].(map[string]interface{})["teamRef"].(map[string]interface{})["name"] = team.GetName()
|
||||
|
||||
created, err := teamBindingClient.Resource.Create(ctx, toCreate, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
bindingNames = append(bindingNames, created.GetName())
|
||||
}
|
||||
|
||||
// Create 4 bindings
|
||||
createBinding(user1, teamA)
|
||||
createBinding(user2, teamA)
|
||||
createBinding(user1, teamB)
|
||||
createBinding(user2, teamB)
|
||||
|
||||
t.Cleanup(func() {
|
||||
cleanupCtx := context.Background()
|
||||
|
||||
for _, name := range bindingNames {
|
||||
_ = teamBindingClient.Resource.Delete(cleanupCtx, name, metav1.DeleteOptions{})
|
||||
}
|
||||
for _, name := range teamNames {
|
||||
_ = teamClient.Resource.Delete(cleanupCtx, name, metav1.DeleteOptions{})
|
||||
}
|
||||
for _, name := range userNames {
|
||||
_ = userClient.Resource.Delete(cleanupCtx, name, metav1.DeleteOptions{})
|
||||
}
|
||||
})
|
||||
|
||||
// Verify we have at least 4 bindings overall
|
||||
all, err := teamBindingClient.Resource.List(ctx, metav1.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
require.GreaterOrEqual(t, len(all.Items), 4)
|
||||
|
||||
// Query 1: select by teamRef.name, should return 2 of the 4
|
||||
listByTeam, err := teamBindingClient.Resource.List(ctx, metav1.ListOptions{
|
||||
FieldSelector: fmt.Sprintf("spec.teamRef.name=%s", teamA.GetName()),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, listByTeam.Items, 2)
|
||||
for _, item := range listByTeam.Items {
|
||||
var actual iamv0alpha1.TeamBinding
|
||||
require.NoError(t, runtime.DefaultUnstructuredConverter.FromUnstructured(item.Object, &actual))
|
||||
require.Equal(t, teamA.GetName(), actual.Spec.TeamRef.Name)
|
||||
}
|
||||
|
||||
// Query 2: select by subject.name, should return 2 of the 4
|
||||
listByUser, err := teamBindingClient.Resource.List(ctx, metav1.ListOptions{
|
||||
FieldSelector: fmt.Sprintf("spec.subject.name=%s", user1.GetName()),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, listByUser.Items, 2)
|
||||
for _, item := range listByUser.Items {
|
||||
var actual iamv0alpha1.TeamBinding
|
||||
require.NoError(t, runtime.DefaultUnstructuredConverter.FromUnstructured(item.Object, &actual))
|
||||
require.Equal(t, user1.GetName(), actual.Spec.Subject.Name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user