Skip to content

Commit 055b089

Browse files
authored
Allow endpointURL to be specified in namespace annotations (#36)
related issue: aws-controllers-k8s/community#876 We want the user to have the same level of control over `endpoint_url `as the existing account and region parameters. The current implementation it does not allow the user to specify` endpoint_url` in namespace meta data annotations when they are attempting to use cross-account or cross region resource management.
1 parent 929fa47 commit 055b089

File tree

5 files changed

+88
-8
lines changed

5 files changed

+88
-8
lines changed

apis/core/v1alpha1/annotations.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,12 @@ const (
5151
// injected by POD IRSA, to decide in which region the resources should be
5252
// created.
5353
AnnotationDefaultRegion = AnnotationPrefix + "default-region"
54+
// AnnotationEndpointURL is an annotation whose value is the identifier
55+
// for the AWS endpoint in which the service controller will use to create
56+
// its resources. If this annotation is set on a namespace, the Kubernetes user
57+
// is indicating that the ACK service controller should create its resources using
58+
// that specific endpoint. If this annotation is not set, ACK service controller
59+
// will either use the default behavior of aws-sdk-go to create endpoints or
60+
// aws-endpoint-url if it is set in controller binary flags and environment variables.
61+
AnnotationEndpointURL = AnnotationPrefix + "endpoint-url"
5462
)

pkg/runtime/adoption_reconciler.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,10 @@ func (r *adoptionReconciler) reconcile(req ctrlrt.Request) error {
107107
acctID := r.getOwnerAccountID(res)
108108
region := r.getRegion(res)
109109
roleARN := r.getRoleARN(acctID)
110+
endpointURL := r.getEndpointURL(res)
110111

111112
sess, err := r.sc.NewSession(
112-
region, &r.cfg.EndpointURL, roleARN,
113+
region, &endpointURL, roleARN,
113114
targetDescriptor.EmptyRuntimeObject().GetObjectKind().GroupVersionKind(),
114115
)
115116
if err != nil {
@@ -419,6 +420,24 @@ func (r *adoptionReconciler) getOwnerAccountID(
419420
return ackv1alpha1.AWSAccountID(r.cfg.AccountID)
420421
}
421422

423+
// getEndpointURL returns the AWS account that owns the supplied resource.
424+
// We look for the namespace associated endpoint url, if that is set we use it.
425+
// Otherwise if none of these annotations are set we use the endpoint url specified
426+
// in the configuration
427+
func (r *adoptionReconciler) getEndpointURL(
428+
res *ackv1alpha1.AdoptedResource,
429+
) string {
430+
// look for endpoint url in the namespace annotations
431+
namespace := res.GetNamespace()
432+
endpointURL, ok := r.cache.Namespaces.GetEndpointURL(namespace)
433+
if ok {
434+
return endpointURL
435+
}
436+
437+
// use controller configuration
438+
return r.cfg.EndpointURL
439+
}
440+
422441
// getRoleARN return the Role ARN that should be assumed in order to manage
423442
// the resources.
424443
func (r *adoptionReconciler) getRoleARN(

pkg/runtime/cache/namespace.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ type namespaceInfo struct {
3131
defaultRegion string
3232
// services.k8s.aws/owner-account-id Annotation
3333
ownerAccountID string
34+
// services.k8s.aws/endpoint-url Annotation
35+
endpointURL string
3436
}
3537

3638
// getDefaultRegion returns the default region value
@@ -49,15 +51,23 @@ func (n *namespaceInfo) getOwnerAccountID() string {
4951
return n.ownerAccountID
5052
}
5153

54+
// getEndpointURL returns the namespace Endpoint URL
55+
func (n *namespaceInfo) getEndpointURL() string {
56+
if n == nil {
57+
return ""
58+
}
59+
return n.endpointURL
60+
}
61+
5262
// NamespaceCache is responsible of keeping track of namespaces
5363
// annotations, and caching those related to the ACK controller.
5464
type NamespaceCache struct {
5565
sync.RWMutex
5666

5767
log logr.Logger
58-
// Provide a namespace specifically to listen to.
68+
// Provide a namespace specifically to listen to.
5969
// Provide empty string to listen to all namespaces except kube-system and kube-public.
60-
watchNamespace string
70+
watchNamespace string
6171

6272
// Namespace informer
6373
informer k8scache.SharedInformer
@@ -81,12 +91,12 @@ func NewNamespaceCache(clientset kubernetes.Interface, log logr.Logger, watchNam
8191
}
8292
}
8393

84-
// Check if the provided namespace should be listened to or not
94+
// Check if the provided namespace should be listened to or not
8595
func isWatchNamespace(raw interface{}, watchNamespace string) bool {
8696
object, ok := raw.(*corev1.Namespace)
8797
if !ok {
8898
return false
89-
}
99+
}
90100

91101
if watchNamespace != "" {
92102
return watchNamespace == object.ObjectMeta.Name
@@ -143,8 +153,18 @@ func (c *NamespaceCache) GetOwnerAccountID(namespace string) (string, bool) {
143153
return "", false
144154
}
145155

156+
// GetEndpointURL returns the endpoint URL if it exists
157+
func (c *NamespaceCache) GetEndpointURL(namespace string) (string, bool) {
158+
info, ok := c.getNamespaceInfo(namespace)
159+
if ok {
160+
e := info.getEndpointURL()
161+
return e, e != ""
162+
}
163+
return "", false
164+
}
165+
146166
// getNamespaceInfo reads a namespace cached annotations and
147-
// return a given namespace default aws region and owner account id.
167+
// return a given namespace default aws region, owner account id and endpoint url.
148168
// This function is thread safe.
149169
func (c *NamespaceCache) getNamespaceInfo(ns string) (*namespaceInfo, bool) {
150170
c.RLock()
@@ -166,6 +186,10 @@ func (c *NamespaceCache) setNamespaceInfoFromK8sObject(ns *corev1.Namespace) {
166186
if ok {
167187
nsInfo.ownerAccountID = OwnerAccountID
168188
}
189+
EndpointURL, ok := nsa[ackv1alpha1.AnnotationEndpointURL]
190+
if ok {
191+
nsInfo.endpointURL = EndpointURL
192+
}
169193
c.Lock()
170194
defer c.Unlock()
171195
c.namespaceInfos[ns.ObjectMeta.Name] = nsInfo

pkg/runtime/cache/namespace_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ func TestNamespaceCache(t *testing.T) {
6363
Annotations: map[string]string{
6464
ackv1alpha1.AnnotationDefaultRegion: "us-west-2",
6565
ackv1alpha1.AnnotationOwnerAccountID: "012345678912",
66+
ackv1alpha1.AnnotationEndpointURL: "https://amazon-service.region.amazonaws.com",
6667
},
6768
},
6869
},
@@ -79,6 +80,10 @@ func TestNamespaceCache(t *testing.T) {
7980
require.True(t, ok)
8081
require.Equal(t, "012345678912", ownerAccountID)
8182

83+
endpointURL, ok := namespaceCache.GetEndpointURL("production")
84+
require.True(t, ok)
85+
require.Equal(t, "https://amazon-service.region.amazonaws.com", endpointURL)
86+
8287
// Test update events
8388
k8sClient.CoreV1().Namespaces().Update(
8489
context.Background(),
@@ -88,6 +93,7 @@ func TestNamespaceCache(t *testing.T) {
8893
Annotations: map[string]string{
8994
ackv1alpha1.AnnotationDefaultRegion: "us-est-1",
9095
ackv1alpha1.AnnotationOwnerAccountID: "21987654321",
96+
ackv1alpha1.AnnotationEndpointURL: "https://amazon-other-service.region.amazonaws.com",
9197
},
9298
},
9399
},
@@ -104,6 +110,10 @@ func TestNamespaceCache(t *testing.T) {
104110
require.True(t, ok)
105111
require.Equal(t, "21987654321", ownerAccountID)
106112

113+
endpointURL, ok = namespaceCache.GetEndpointURL("production")
114+
require.True(t, ok)
115+
require.Equal(t, "https://amazon-other-service.region.amazonaws.com", endpointURL)
116+
107117
// Test delete events
108118
k8sClient.CoreV1().Namespaces().Delete(
109119
context.Background(),

pkg/runtime/reconciler.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,9 @@ func (r *resourceReconciler) Reconcile(req ctrlrt.Request) (ctrlrt.Result, error
137137
acctID := r.getOwnerAccountID(res)
138138
region := r.getRegion(res)
139139
roleARN := r.getRoleARN(acctID)
140+
endpointURL := r.getEndpointURL(res)
140141
sess, err := r.sc.NewSession(
141-
region, &r.cfg.EndpointURL, roleARN,
142+
region, &endpointURL, roleARN,
142143
res.RuntimeObject().GetObjectKind().GroupVersionKind(),
143144
)
144145
if err != nil {
@@ -170,7 +171,6 @@ func (r *resourceReconciler) reconcile(
170171
if res.IsBeingDeleted() {
171172
return r.cleanup(ctx, rm, res)
172173
}
173-
174174
return r.Sync(ctx, rm, res)
175175
}
176176

@@ -612,6 +612,25 @@ func (r *resourceReconciler) getRegion(
612612
return ackv1alpha1.AWSRegion(r.cfg.Region)
613613
}
614614

615+
// getEndpointURL returns the AWS account that owns the supplied resource.
616+
// We look for the namespace associated endpoint url, if that is set we use it.
617+
// Otherwise if none of these annotations are set we use the endpoint url specified
618+
// in the configuration
619+
func (r *resourceReconciler) getEndpointURL(
620+
res acktypes.AWSResource,
621+
) string {
622+
623+
// look for endpoint url in the namespace annotations
624+
namespace := res.MetaObject().GetNamespace()
625+
endpointURL, ok := r.cache.Namespaces.GetEndpointURL(namespace)
626+
if ok {
627+
return endpointURL
628+
}
629+
630+
// use controller configuration EndpointURL
631+
return r.cfg.EndpointURL
632+
}
633+
615634
// NewReconciler returns a new reconciler object
616635
func NewReconciler(
617636
sc acktypes.ServiceController,

0 commit comments

Comments
 (0)