Skip to content

Commit

Permalink
Add generic Set type and use it (#2243)
Browse files Browse the repository at this point in the history
Based upon expanding the `StringSet` type. Note that we cannot substitute uses of `map[T]struct{}` where `T` is an interface because interface types are [not comparable](golang/go#51338), even though the `map[T]struct{}` version works.

`AreEqual` is a standalone function at the moment because making it a method [crashes the compiler](golang/go#51840).
  • Loading branch information
Porges authored Apr 19, 2022
1 parent a512f6d commit e63c20b
Show file tree
Hide file tree
Showing 23 changed files with 172 additions and 172 deletions.
1 change: 1 addition & 0 deletions v2/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.12.1
golang.org/x/crypto v0.0.0-20220214200702-86341886e292
golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65
k8s.io/api v0.24.0-beta.0
k8s.io/apiextensions-apiserver v0.23.5
Expand Down
2 changes: 2 additions & 0 deletions v2/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd h1:zVFyTKZN/Q7mNRWSs1GOYnHM9NiFSJ54YVRsD0rNWT4=
golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
Expand Down
13 changes: 7 additions & 6 deletions v2/internal/reflecthelpers/reflect_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/pkg/errors"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/Azure/azure-service-operator/v2/internal/set"
"github.com/Azure/azure-service-operator/v2/pkg/genruntime"
)

Expand Down Expand Up @@ -62,30 +63,30 @@ func FindReferences(obj interface{}, t reflect.Type) (map[interface{}]struct{},
}

// FindResourceReferences finds all the genruntime.ResourceReference's on the provided object
func FindResourceReferences(obj interface{}) (map[genruntime.ResourceReference]struct{}, error) {
func FindResourceReferences(obj interface{}) (set.Set[genruntime.ResourceReference], error) {
untypedResult, err := FindReferences(obj, reflect.TypeOf(genruntime.ResourceReference{}))
if err != nil {
return nil, err
}

result := make(map[genruntime.ResourceReference]struct{})
result := set.Make[genruntime.ResourceReference]()
for k := range untypedResult {
result[k.(genruntime.ResourceReference)] = struct{}{}
result.Add(k.(genruntime.ResourceReference))
}

return result, nil
}

// FindSecretReferences finds all of the genruntime.SecretReference's on the provided object
func FindSecretReferences(obj interface{}) (map[genruntime.SecretReference]struct{}, error) {
func FindSecretReferences(obj interface{}) (set.Set[genruntime.SecretReference], error) {
untypedResult, err := FindReferences(obj, reflect.TypeOf(genruntime.SecretReference{}))
if err != nil {
return nil, err
}

result := make(map[genruntime.SecretReference]struct{})
result := set.Make[genruntime.SecretReference]()
for k := range untypedResult {
result[k.(genruntime.SecretReference)] = struct{}{}
result.Add(k.(genruntime.SecretReference))
}

return result, nil
Expand Down
9 changes: 5 additions & 4 deletions v2/internal/resolver/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"k8s.io/apimachinery/pkg/types"

"github.com/Azure/azure-service-operator/v2/internal/reflecthelpers"
"github.com/Azure/azure-service-operator/v2/internal/set"
"github.com/Azure/azure-service-operator/v2/internal/util/kubeclient"
"github.com/Azure/azure-service-operator/v2/pkg/genruntime"
)
Expand Down Expand Up @@ -192,8 +193,8 @@ func (r *Resolver) findGVK(ref genruntime.NamespacedResourceReference) (schema.G
// ResolveSecretReferences resolves all provided secret references
func (r *Resolver) ResolveSecretReferences(
ctx context.Context,
refs map[genruntime.NamespacedSecretReference]struct{}) (genruntime.ResolvedSecrets, error) {

refs set.Set[genruntime.NamespacedSecretReference],
) (genruntime.ResolvedSecrets, error) {
return r.kubeSecretResolver.ResolveSecretReferences(ctx, refs)
}

Expand All @@ -205,9 +206,9 @@ func (r *Resolver) ResolveResourceSecretReferences(ctx context.Context, metaObje
}

// Include the namespace
namespacedSecretRefs := make(map[genruntime.NamespacedSecretReference]struct{})
namespacedSecretRefs := set.Make[genruntime.NamespacedSecretReference]()
for ref := range refs {
namespacedSecretRefs[ref.ToNamespacedRef(metaObject.GetNamespace())] = struct{}{}
namespacedSecretRefs.Add(ref.ToNamespacedRef(metaObject.GetNamespace()))
}

// resolve them
Expand Down
6 changes: 4 additions & 2 deletions v2/internal/resolver/resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import (
mysql "github.com/Azure/azure-service-operator/v2/api/dbformysql/v1beta20210501"
resources "github.com/Azure/azure-service-operator/v2/api/resources/v1beta20200601"
storage "github.com/Azure/azure-service-operator/v2/api/storage/v1beta20210401"

"github.com/Azure/azure-service-operator/v2/internal/resolver"
"github.com/Azure/azure-service-operator/v2/internal/set"
"github.com/Azure/azure-service-operator/v2/internal/testcommon"
"github.com/Azure/azure-service-operator/v2/internal/util/kubeclient"
"github.com/Azure/azure-service-operator/v2/pkg/genruntime"
Expand Down Expand Up @@ -427,7 +429,7 @@ func Test_ResolveSecrets_ReturnsExpectedSecretValue(t *testing.T) {
ref := genruntime.SecretReference{Name: secretName, Key: secretKey}
namespacedRef := ref.ToNamespacedRef(testNamespace)

resolvedSecrets, err := test.resolver.ResolveSecretReferences(ctx, map[genruntime.NamespacedSecretReference]struct{}{namespacedRef: {}})
resolvedSecrets, err := test.resolver.ResolveSecretReferences(ctx, set.Make(namespacedRef))
g.Expect(err).ToNot(HaveOccurred())

actualSecret, err := resolvedSecrets.LookupSecret(ref)
Expand All @@ -448,7 +450,7 @@ func Test_ResolveSecrets_ReturnsReferenceNotFound(t *testing.T) {
ref := genruntime.SecretReference{Name: secretName, Key: secretKey}
namespacedRef := ref.ToNamespacedRef(testNamespace)

_, err = test.resolver.ResolveSecretReferences(ctx, map[genruntime.NamespacedSecretReference]struct{}{namespacedRef: {}})
_, err = test.resolver.ResolveSecretReferences(ctx, set.Make(namespacedRef))
g.Expect(err).To(HaveOccurred())
g.Expect(errors.Unwrap(err)).To(BeAssignableToTypeOf(&resolver.SecretNotFound{}))
}
Expand Down
5 changes: 3 additions & 2 deletions v2/internal/resolver/secret_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ import (
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"

"github.com/Azure/azure-service-operator/v2/internal/set"
"github.com/Azure/azure-service-operator/v2/internal/util/kubeclient"
"github.com/Azure/azure-service-operator/v2/pkg/genruntime"
)

// SecretResolver is a secret resolver
type SecretResolver interface {
ResolveSecretReference(ctx context.Context, ref genruntime.NamespacedSecretReference) (string, error)
ResolveSecretReferences(ctx context.Context, refs map[genruntime.NamespacedSecretReference]struct{}) (genruntime.ResolvedSecrets, error)
ResolveSecretReferences(ctx context.Context, refs set.Set[genruntime.NamespacedSecretReference]) (genruntime.ResolvedSecrets, error)
}

// kubeSecretResolver resolves Kubernetes secrets
Expand Down Expand Up @@ -66,7 +67,7 @@ func (r *kubeSecretResolver) ResolveSecretReference(ctx context.Context, ref gen
}

// ResolveSecretReferences resolves all provided secret references
func (r *kubeSecretResolver) ResolveSecretReferences(ctx context.Context, refs map[genruntime.NamespacedSecretReference]struct{}) (genruntime.ResolvedSecrets, error) {
func (r *kubeSecretResolver) ResolveSecretReferences(ctx context.Context, refs set.Set[genruntime.NamespacedSecretReference]) (genruntime.ResolvedSecrets, error) {
result := make(map[genruntime.SecretReference]string)

for ref := range refs {
Expand Down
73 changes: 73 additions & 0 deletions v2/internal/set/set.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*/

package set

import (
"golang.org/x/exp/constraints"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)

// Set provides a standard way to have a set of distinct things
type Set[T comparable] map[T]struct{}

// Make creates a new set with the given values
func Make[T comparable](ts ...T) Set[T] {
result := make(Set[T], len(ts))

for _, x := range ts {
result.Add(x)
}

return result
}

// Contains does a check to see if the provided value is in the set
func (set Set[T]) Contains(x T) bool {
_, ok := set[x]
return ok
}

// Add adds the provided value into the set
// Nothing happens if the value is already present
func (set Set[T]) Add(x T) {
set[x] = struct{}{}
}

// Remove deletes the provided value from the set
// Nothing happens if the value is not present
func (set Set[T]) Remove(x T) {
delete(set, x)
}

// Copy returns an independent copy of the set
func (set Set[T]) Copy() Set[T] {
return maps.Clone(set)
}

/* compiler crashes at the moment: https://github.com/golang/go/issues/51840
func (set Set[T]) Equals(other Set[T]) bool {
return maps.Equal(set, other)
}
*/

func AreEqual[T comparable](left, right Set[T]) bool {
return maps.Equal(left, right)
}

// Values returns a slice of all the values in the set
func (set Set[T]) Values() []T {
return maps.Keys(set)
}

// AsSortedSlice returns a sorted slice of values from this set
func AsSortedSlice[T constraints.Ordered](set Set[T]) []T {
result := maps.Keys(set)
slices.Sort(result)
return result
}
4 changes: 3 additions & 1 deletion v2/pkg/genruntime/resource_reference.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (

"github.com/pkg/errors"
kerrors "k8s.io/apimachinery/pkg/util/errors"

"github.com/Azure/azure-service-operator/v2/internal/set"
)

// KnownResourceReference is a resource reference to a known type.
Expand Down Expand Up @@ -160,7 +162,7 @@ func (ref ResourceReference) Copy() ResourceReference {
}

// ValidateResourceReferences calls Validate on each ResourceReference
func ValidateResourceReferences(refs map[ResourceReference]struct{}) error {
func ValidateResourceReferences(refs set.Set[ResourceReference]) error {
var errs []error
for ref := range refs {
errs = append(errs, ref.Validate())
Expand Down
9 changes: 5 additions & 4 deletions v2/pkg/genruntime/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package genruntime
import (
"fmt"

"github.com/Azure/azure-service-operator/v2/internal/set"
"github.com/pkg/errors"
)

Expand Down Expand Up @@ -101,19 +102,19 @@ func makeKeyPair(dest *SecretDestination) secretKeyPair {
// those secrets to overwrite one another.
func ValidateSecretDestinations(destinations []*SecretDestination) error {
// Map of secret -> keys
//locations := make(map[string]map[string]struct{})
locations := make(map[secretKeyPair]struct{})
locations := set.Make[secretKeyPair]()

for _, dest := range destinations {
if dest == nil {
continue
}

pair := makeKeyPair(dest)
if _, ok := locations[pair]; ok {
if locations.Contains(pair) {
return errors.Errorf("cannot write more than one secret to destination %s", dest.String())
}

locations[pair] = struct{}{}
locations.Add(pair)
}

return nil
Expand Down
1 change: 1 addition & 0 deletions v2/tools/generator/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sergi/go-diff v1.0.0 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd // indirect
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023 // indirect
Expand Down
2 changes: 2 additions & 0 deletions v2/tools/generator/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd h1:zVFyTKZN/Q7mNRWSs1GOYnHM9NiFSJ54YVRsD0rNWT4=
golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
Expand Down
Loading

0 comments on commit e63c20b

Please sign in to comment.