Skip to content

Commit

Permalink
added tags management to OIDCProvider
Browse files Browse the repository at this point in the history
Signed-off-by: Cecilia Bernardi <cbernardi@expediagroup.com>
  • Loading branch information
cebernardi committed Feb 24, 2022
1 parent 79762cf commit 7dad1bb
Show file tree
Hide file tree
Showing 12 changed files with 857 additions and 144 deletions.
6 changes: 6 additions & 0 deletions apis/iam/v1beta1/openidconnectprovider_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ type OpenIDConnectProviderParameters struct {
// +optional
ClientIDList []string `json:"clientIDList,omitempty"`

// Tags. For more information about
// tagging, see Tagging OpenID Connect (OIDC) identity providers (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags_idps_oidc.html)
// in the IAM User Guide.
// +optional
Tags []Tag `json:"tags,omitempty"`

// A list of server certificate thumbprints for the OpenID Connect (OIDC) identity
// provider's server certificates. Typically this list includes only one entry.
// However, IAM lets you have up to five thumbprints for an OIDC provider. This
Expand Down
5 changes: 5 additions & 0 deletions apis/iam/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions package/crds/iam.aws.crossplane.io_openidconnectproviders.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,38 @@ spec:
type: string
maxItems: 100
type: array
tags:
description: Tags. For more information about tagging, see Tagging
OpenID Connect (OIDC) identity providers (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags_idps_oidc.html)
in the IAM User Guide.
items:
description: Tag represents user-provided metadata that can
be associated with a IAM role. For more information about
tagging, see Tagging IAM Identities (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html)
in the IAM User Guide.
properties:
key:
description: The key name that can be used to look up or
retrieve the associated value. For example, Department
or Cost Center are common choices.
type: string
value:
description: "The value associated with this tag. For example,
tags with a key name of Department could have values such
as Human Resources, Accounting, and Support. Tags with
a key name of Cost Center might have values that consist
of the number associated with the different cost centers
in your company. Typically, many resources have tags with
the same key name but with different values. \n AWS always
interprets the tag Value as a single string. If you need
to store an array, you can store comma-separated values
in the string. However, you must interpret the value in
your code."
type: string
required:
- key
type: object
type: array
thumbprintList:
description: "A list of server certificate thumbprints for the
OpenID Connect (OIDC) identity provider's server certificates.
Expand Down
23 changes: 23 additions & 0 deletions pkg/clients/iam/fake/openidconnectprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,24 @@ import (
// this ensures that the mock implements the client interface
var _ clientset.OpenIDConnectProviderClient = (*MockOpenIDConnectProviderClient)(nil)

// MockOpenIDConnectProviderInput holds the input structures for future inspections
type MockOpenIDConnectProviderInput struct {
CreateOIDCProviderInput *iam.CreateOpenIDConnectProviderInput
TagOpenIDConnectProviderInput *iam.TagOpenIDConnectProviderInput
UntagOpenIDConnectProviderInput *iam.UntagOpenIDConnectProviderInput
}

// MockOpenIDConnectProviderClient is a type that implements all the methods for OpenIDConnectProviderClient interface
type MockOpenIDConnectProviderClient struct {
MockOpenIDConnectProviderInput MockOpenIDConnectProviderInput
MockGetOpenIDConnectProvider func(ctx context.Context, input *iam.GetOpenIDConnectProviderInput, opts []func(*iam.Options)) (*iam.GetOpenIDConnectProviderOutput, error)
MockCreateOpenIDConnectProvider func(ctx context.Context, input *iam.CreateOpenIDConnectProviderInput, opts []func(*iam.Options)) (*iam.CreateOpenIDConnectProviderOutput, error)
MockAddClientIDToOpenIDConnectProvider func(ctx context.Context, input *iam.AddClientIDToOpenIDConnectProviderInput, opts []func(*iam.Options)) (*iam.AddClientIDToOpenIDConnectProviderOutput, error)
MockRemoveClientIDFromOpenIDConnectProvider func(ctx context.Context, input *iam.RemoveClientIDFromOpenIDConnectProviderInput, opts []func(*iam.Options)) (*iam.RemoveClientIDFromOpenIDConnectProviderOutput, error)
MockUpdateOpenIDConnectProviderThumbprint func(ctx context.Context, input *iam.UpdateOpenIDConnectProviderThumbprintInput, opts []func(*iam.Options)) (*iam.UpdateOpenIDConnectProviderThumbprintOutput, error)
MockDeleteOpenIDConnectProvider func(ctx context.Context, input *iam.DeleteOpenIDConnectProviderInput, opts []func(*iam.Options)) (*iam.DeleteOpenIDConnectProviderOutput, error)
MockTagOpenIDConnectProvider func(ctx context.Context, input *iam.TagOpenIDConnectProviderInput, opts []func(*iam.Options)) (*iam.TagOpenIDConnectProviderOutput, error)
MockUntagOpenIDConnectProvider func(ctx context.Context, input *iam.UntagOpenIDConnectProviderInput, opts []func(*iam.Options)) (*iam.UntagOpenIDConnectProviderOutput, error)
}

// GetOpenIDConnectProvider mocks client call.
Expand All @@ -44,6 +54,7 @@ func (m *MockOpenIDConnectProviderClient) GetOpenIDConnectProvider(ctx context.C

// CreateOpenIDConnectProvider mocks client call.
func (m *MockOpenIDConnectProviderClient) CreateOpenIDConnectProvider(ctx context.Context, input *iam.CreateOpenIDConnectProviderInput, opts ...func(*iam.Options)) (*iam.CreateOpenIDConnectProviderOutput, error) {
m.MockOpenIDConnectProviderInput.CreateOIDCProviderInput = input
return m.MockCreateOpenIDConnectProvider(ctx, input, opts)
}

Expand All @@ -66,3 +77,15 @@ func (m *MockOpenIDConnectProviderClient) UpdateOpenIDConnectProviderThumbprint(
func (m *MockOpenIDConnectProviderClient) DeleteOpenIDConnectProvider(ctx context.Context, input *iam.DeleteOpenIDConnectProviderInput, opts ...func(*iam.Options)) (*iam.DeleteOpenIDConnectProviderOutput, error) {
return m.MockDeleteOpenIDConnectProvider(ctx, input, opts)
}

// TagOpenIDConnectProvider mocks client call
func (m *MockOpenIDConnectProviderClient) TagOpenIDConnectProvider(ctx context.Context, input *iam.TagOpenIDConnectProviderInput, opts ...func(*iam.Options)) (*iam.TagOpenIDConnectProviderOutput, error) {
m.MockOpenIDConnectProviderInput.TagOpenIDConnectProviderInput = input
return m.MockTagOpenIDConnectProvider(ctx, input, opts)
}

// UntagOpenIDConnectProvider mocks client call
func (m *MockOpenIDConnectProviderClient) UntagOpenIDConnectProvider(ctx context.Context, input *iam.UntagOpenIDConnectProviderInput, opts ...func(*iam.Options)) (*iam.UntagOpenIDConnectProviderOutput, error) {
m.MockOpenIDConnectProviderInput.UntagOpenIDConnectProviderInput = input
return m.MockUntagOpenIDConnectProvider(ctx, input, opts)
}
16 changes: 15 additions & 1 deletion pkg/clients/iam/openidconnectprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ type OpenIDConnectProviderClient interface {
RemoveClientIDFromOpenIDConnectProvider(ctx context.Context, input *iam.RemoveClientIDFromOpenIDConnectProviderInput, opts ...func(*iam.Options)) (*iam.RemoveClientIDFromOpenIDConnectProviderOutput, error)
UpdateOpenIDConnectProviderThumbprint(ctx context.Context, input *iam.UpdateOpenIDConnectProviderThumbprintInput, opts ...func(*iam.Options)) (*iam.UpdateOpenIDConnectProviderThumbprintOutput, error)
DeleteOpenIDConnectProvider(ctx context.Context, input *iam.DeleteOpenIDConnectProviderInput, opts ...func(*iam.Options)) (*iam.DeleteOpenIDConnectProviderOutput, error)
TagOpenIDConnectProvider(ctx context.Context, input *iam.TagOpenIDConnectProviderInput, opts ...func(*iam.Options)) (*iam.TagOpenIDConnectProviderOutput, error)
UntagOpenIDConnectProvider(ctx context.Context, input *iam.UntagOpenIDConnectProviderInput, optFns ...func(*iam.Options)) (*iam.UntagOpenIDConnectProviderOutput, error)
}

// GenerateOIDCProviderObservation is used to produce v1alpha1.OpenIDConnectProvider
Expand All @@ -59,13 +61,25 @@ func IsOIDCProviderUpToDate(in svcapitypes.OpenIDConnectProviderParameters, obse
sortSlicesOpt := cmpopts.SortSlices(func(x, y string) bool {
return x < y
})
sortSliceTags := cmpopts.SortSlices(func(x, y svcapitypes.Tag) bool {
return x.Key < y.Key
})
if !cmp.Equal(in.ClientIDList, observed.ClientIDList, sortSlicesOpt, cmpopts.EquateEmpty()) {
return false
}
if !cmp.Equal(in.ThumbprintList, observed.ThumbprintList, sortSlicesOpt, cmpopts.EquateEmpty()) {
return false
}
return true

nTags := len(observed.Tags)
if nTags == 0 {
return true
}
cmpTags := make([]svcapitypes.Tag, nTags)
for i := range observed.Tags {
cmpTags[i] = svcapitypes.Tag{Key: *observed.Tags[i].Key, Value: *observed.Tags[i].Value}
}
return cmp.Equal(in.Tags, cmpTags, sortSliceTags, cmpopts.EquateEmpty())
}

// SliceDifference returns the elements to added and removed between the
Expand Down
78 changes: 77 additions & 1 deletion pkg/clients/iam/openidconnectprovider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@ package iam
import (
"testing"

"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/aws/aws-sdk-go-v2/service/iam/types"
"github.com/google/go-cmp/cmp"

"github.com/crossplane/crossplane-runtime/pkg/test"

"github.com/crossplane/provider-aws/apis/iam/v1beta1"
)

func TestSliceDiff(t *testing.T) {
Expand Down Expand Up @@ -72,3 +78,73 @@ func TestSliceDiff(t *testing.T) {
})
}
}

func TestIsOIDCProviderUpToDate(t *testing.T) {
type args struct {
input v1beta1.OpenIDConnectProviderParameters
observed iam.GetOpenIDConnectProviderOutput
}

cases := map[string]struct {
args args
want bool
}{
"DifferentClientIDList": {
args: args{
input: v1beta1.OpenIDConnectProviderParameters{ClientIDList: []string{"client1", "client3"}},
observed: iam.GetOpenIDConnectProviderOutput{ClientIDList: []string{"client2", "client3"}},
},
want: false,
},
"DifferentThumbprintList": {
args: args{
input: v1beta1.OpenIDConnectProviderParameters{ThumbprintList: []string{"thumbprint1", "thumbprint3"}},
observed: iam.GetOpenIDConnectProviderOutput{ThumbprintList: []string{"thumbprint2", "thumbprint3"}},
},
want: false,
},
"DifferentTags": {
args: args{
input: v1beta1.OpenIDConnectProviderParameters{Tags: []v1beta1.Tag{
{Key: "key1", Value: "value1"},
{Key: "key3", Value: "value3"},
}},
observed: iam.GetOpenIDConnectProviderOutput{Tags: []types.Tag{
{Key: aws.String("key2"), Value: aws.String("value2")},
{Key: aws.String("key3"), Value: aws.String("value3")},
}},
},
want: false,
},
"UpToDate": {
args: args{
input: v1beta1.OpenIDConnectProviderParameters{
ClientIDList: []string{"client1", "client2"},
ThumbprintList: []string{"thumbprint1", "thumbprint2"},
Tags: []v1beta1.Tag{
{Key: "key1", Value: "value1"},
{Key: "key2", Value: "value2"},
},
},
observed: iam.GetOpenIDConnectProviderOutput{
ClientIDList: []string{"client2", "client1"},
ThumbprintList: []string{"thumbprint2", "thumbprint1"},
Tags: []types.Tag{
{Key: aws.String("key2"), Value: aws.String("value2")},
{Key: aws.String("key1"), Value: aws.String("value1")},
},
},
},
want: true,
},
}

for name, tc := range cases {
t.Run(name, func(t *testing.T) {
o := IsOIDCProviderUpToDate(tc.args.input, tc.args.observed)
if o != tc.want {
t.Errorf("want %t, got %t", tc.want, o)
}
})
}
}
22 changes: 0 additions & 22 deletions pkg/clients/iam/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,25 +196,3 @@ func IsRoleUpToDate(in v1beta1.RoleParameters, observed iamtypes.Role) (bool, st
}
return false, diff, nil
}

// DiffIAMTags returns the lists of tags that need to be removed and added according
// to current and desired states, also returns if desired state needs to be updated
func DiffIAMTags(local map[string]string, remote []iamtypes.Tag) (add []iamtypes.Tag, remove []string, areTagsUpToDate bool) {
removeMap := map[string]struct{}{}
for _, t := range remote {
if local[aws.ToString(t.Key)] == aws.ToString(t.Value) {
delete(local, aws.ToString(t.Key))
continue
}
removeMap[aws.ToString(t.Key)] = struct{}{}
}
for k, v := range local {
add = append(add, iamtypes.Tag{Key: aws.String(k), Value: aws.String(v)})
}
for k := range removeMap {
remove = append(remove, k)
}
areTagsUpToDate = len(add) == 0 && len(remove) == 0

return add, remove, areTagsUpToDate
}
102 changes: 0 additions & 102 deletions pkg/clients/iam/role_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package iam

import (
"sort"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -344,104 +343,3 @@ observed assume role policy: %7B%22Version%22%3A%222012-10-17%22%2C%22Statement%
})
}
}

func TestDiffIAMTags(t *testing.T) {
type args struct {
local []v1beta1.Tag
remote []iamtypes.Tag
}
type want struct {
add []iamtypes.Tag
remove []string
}
cases := map[string]struct {
args args
want want
}{
"AllNew": {
args: args{
local: []v1beta1.Tag{
{Key: "key", Value: "val"},
},
},
want: want{
add: []iamtypes.Tag{
{Key: aws.String("key"), Value: aws.String("val")},
},
},
},
"SomeNew": {
args: args{
local: []v1beta1.Tag{
{Key: "key", Value: "val"},
{Key: "key1", Value: "val1"},
{Key: "key2", Value: "val2"},
},
remote: []iamtypes.Tag{
{Key: aws.String("key"), Value: aws.String("val")},
},
},
want: want{
add: []iamtypes.Tag{
{Key: aws.String("key1"), Value: aws.String("val1")},
{Key: aws.String("key2"), Value: aws.String("val2")},
},
},
},
"Update": {
args: args{
local: []v1beta1.Tag{
{Key: "key", Value: "different"},
{Key: "key1", Value: "val1"},
{Key: "key2", Value: "val2"},
},
remote: []iamtypes.Tag{
{Key: aws.String("key"), Value: aws.String("val")},
{Key: aws.String("key1"), Value: aws.String("val1")},
{Key: aws.String("key2"), Value: aws.String("val2")},
},
},
want: want{
add: []iamtypes.Tag{
{Key: aws.String("key"), Value: aws.String("different")},
},
remove: []string{"key"},
},
},
"RemoveAll": {
args: args{
remote: []iamtypes.Tag{
{Key: aws.String("key"), Value: aws.String("val")},
{Key: aws.String("key1"), Value: aws.String("val1")},
{Key: aws.String("key2"), Value: aws.String("val2")},
},
},
want: want{
remove: []string{"key", "key1", "key2"},
},
},
}

for name, tc := range cases {
t.Run(name, func(t *testing.T) {
tagCmp := cmpopts.SortSlices(func(i, j iamtypes.Tag) bool {
return aws.StringValue(i.Key) < aws.StringValue(j.Key)
})

crTagMap := make(map[string]string, len(tc.args.local))
for _, v := range tc.args.local {
crTagMap[v.Key] = v.Value
}

add, remove, _ := DiffIAMTags(crTagMap, tc.args.remote)
if diff := cmp.Diff(tc.want.add, add, tagCmp, cmpopts.IgnoreTypes(document.NoSerde{})); diff != "" {
t.Errorf("r: -want, +got:\n%s", diff)
}
sort.Strings(tc.want.remove)
sort.Strings(remove)
if diff := cmp.Diff(tc.want.remove, remove, tagCmp, cmpopts.IgnoreTypes(document.NoSerde{})); diff != "" {
t.Errorf("r: -want, +got:\n%s", diff)
}
})
}
}

0 comments on commit 7dad1bb

Please sign in to comment.