@@ -18,12 +18,15 @@ package projector
1818
1919import (
2020 "context"
21+ "encoding/json"
2122 "fmt"
2223 "path"
2324 "sort"
2425 "strings"
2526
2627 corev1 "k8s.io/api/core/v1"
28+ "k8s.io/apimachinery/pkg/api/meta"
29+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2730 "k8s.io/apimachinery/pkg/runtime"
2831 "k8s.io/apimachinery/pkg/util/sets"
2932
@@ -37,6 +40,7 @@ const (
3740 SecretAnnotationPrefix = Group + "/secret-"
3841 TypeAnnotationPrefix = Group + "/type-"
3942 ProviderAnnotationPrefix = Group + "/provider-"
43+ MappingAnnotationPrefix = Group + "/mapping-"
4044)
4145
4246var _ ServiceBindingProjector = (* serviceBindingProjector )(nil )
@@ -54,35 +58,95 @@ func New(mappingSource MappingSource) ServiceBindingProjector {
5458}
5559
5660func (p * serviceBindingProjector ) Project (ctx context.Context , binding * servicebindingv1beta1.ServiceBinding , workload runtime.Object ) error {
57- mapping , err := p .mappingSource . LookupMapping (ctx , workload )
61+ ctx , resourceMapping , version , err := p .lookupClusterMapping (ctx , workload )
5862 if err != nil {
5963 return err
6064 }
61- mpt , err := NewMetaPodTemplate (ctx , workload , mapping )
65+
66+ // rather than attempt to merge an existing binding, unproject it
67+ if err := p .Unproject (ctx , binding , workload ); err != nil {
68+ return err
69+ }
70+
71+ versionMapping := MappingVersion (version , resourceMapping )
72+ mpt , err := NewMetaPodTemplate (ctx , workload , versionMapping )
6273 if err != nil {
6374 return err
6475 }
6576 p .project (binding , mpt )
66- return mpt .WriteToWorkload (ctx )
77+
78+ if p .secretName (binding ) != "" {
79+ if err := p .stashLocalMapping (binding , mpt , resourceMapping ); err != nil {
80+ return err
81+ }
82+ }
83+ if err := mpt .WriteToWorkload (ctx ); err != nil {
84+ return err
85+ }
86+
87+ return nil
6788}
6889
6990func (p * serviceBindingProjector ) Unproject (ctx context.Context , binding * servicebindingv1beta1.ServiceBinding , workload runtime.Object ) error {
70- mapping , err := p .mappingSource .LookupMapping (ctx , workload )
91+ resourceMapping , err := p .retrieveLocalMapping (binding , workload )
92+ if err != nil {
93+ return err
94+ }
95+ ctx , m , version , err := p .lookupClusterMapping (ctx , workload )
7196 if err != nil {
7297 return err
7398 }
74- mpt , err := NewMetaPodTemplate (ctx , workload , mapping )
99+ if resourceMapping == nil {
100+ // fall back to using the remote mappings, this isn't ideal as the mapping may have changed after the binding was originally projected
101+ resourceMapping = m
102+ }
103+ versionMapping := MappingVersion (version , resourceMapping )
104+ mpt , err := NewMetaPodTemplate (ctx , workload , versionMapping )
75105 if err != nil {
76106 return err
77107 }
78108 p .unproject (binding , mpt )
79- return mpt .WriteToWorkload (ctx )
109+
110+ if err := p .stashLocalMapping (binding , mpt , nil ); err != nil {
111+ return err
112+ }
113+ if err := mpt .WriteToWorkload (ctx ); err != nil {
114+ return err
115+ }
116+
117+ return nil
80118}
81119
82- func (p * serviceBindingProjector ) project (binding * servicebindingv1beta1.ServiceBinding , mpt * metaPodTemplate ) {
83- // rather than attempt to merge an existing binding, unproject it
84- p .unproject (binding , mpt )
120+ type mappingValue struct {
121+ WorkloadMapping * servicebindingv1beta1.ClusterWorkloadResourceMappingSpec
122+ RESTMapping * meta.RESTMapping
123+ }
124+
125+ // lookupClusterMapping resolves the mapping from the context or from the cluster. This
126+ // avoids redundant calls to the mappingSource for the same workload call when Unproject
127+ // is called from Project. When the lookup is from the cluster, the value is stashed into
128+ // the context for future lookups in this turn.
129+ func (p * serviceBindingProjector ) lookupClusterMapping (ctx context.Context , workload runtime.Object ) (context.Context , * servicebindingv1beta1.ClusterWorkloadResourceMappingSpec , string , error ) {
130+ raw := ctx .Value (mappingValue {})
131+ if value , ok := raw .(mappingValue ); ok {
132+ return ctx , value .WorkloadMapping , value .RESTMapping .Resource .Version , nil
133+ }
134+ rm , err := p .mappingSource .LookupRESTMapping (ctx , workload )
135+ if err != nil {
136+ return ctx , nil , "" , err
137+ }
138+ wm , err := p .mappingSource .LookupWorkloadMapping (ctx , rm .Resource )
139+ if err != nil {
140+ return ctx , nil , "" , err
141+ }
142+ ctx = context .WithValue (ctx , mappingValue {}, mappingValue {
143+ WorkloadMapping : wm ,
144+ RESTMapping : rm ,
145+ })
146+ return ctx , wm , rm .Resource .Version , nil
147+ }
85148
149+ func (p * serviceBindingProjector ) project (binding * servicebindingv1beta1.ServiceBinding , mpt * metaPodTemplate ) {
86150 if p .secretName (binding ) == "" {
87151 // no secret to bind
88152 return
@@ -100,9 +164,9 @@ func (p *serviceBindingProjector) unproject(binding *servicebindingv1beta1.Servi
100164 }
101165
102166 // cleanup annotations
103- delete (mpt .Annotations , p .secretAnnotationName (binding ))
104- delete (mpt .Annotations , p .typeAnnotationName (binding ))
105- delete (mpt .Annotations , p .providerAnnotationName (binding ))
167+ delete (mpt .PodTemplateAnnotations , p .secretAnnotationName (binding ))
168+ delete (mpt .PodTemplateAnnotations , p .typeAnnotationName (binding ))
169+ delete (mpt .PodTemplateAnnotations , p .providerAnnotationName (binding ))
106170}
107171
108172func (p * serviceBindingProjector ) projectVolume (binding * servicebindingv1beta1.ServiceBinding , mpt * metaPodTemplate ) {
@@ -296,7 +360,7 @@ func (p *serviceBindingProjector) projectEnv(binding *servicebindingv1beta1.Serv
296360
297361func (p * serviceBindingProjector ) unprojectEnv (binding * servicebindingv1beta1.ServiceBinding , mpt * metaPodTemplate , mc * metaContainer ) {
298362 env := []corev1.EnvVar {}
299- secret := mpt .Annotations [p .secretAnnotationName (binding )]
363+ secret := mpt .PodTemplateAnnotations [p .secretAnnotationName (binding )]
300364 typeFieldPath := fmt .Sprintf ("metadata.annotations['%s']" , p .typeAnnotationName (binding ))
301365 providerFieldPath := fmt .Sprintf ("metadata.annotations['%s']" , p .providerAnnotationName (binding ))
302366 for _ , e := range mc .Env {
@@ -364,7 +428,7 @@ func (p *serviceBindingProjector) isProjectedEnv(e corev1.EnvVar, secrets sets.S
364428
365429func (p * serviceBindingProjector ) knownProjectedSecrets (mpt * metaPodTemplate ) sets.String {
366430 secrets := sets .NewString ()
367- for k , v := range mpt .Annotations {
431+ for k , v := range mpt .PodTemplateAnnotations {
368432 if strings .HasPrefix (k , SecretAnnotationPrefix ) {
369433 secrets .Insert (v )
370434 }
@@ -385,7 +449,7 @@ func (p *serviceBindingProjector) secretAnnotation(binding *servicebindingv1beta
385449 if secret == "" {
386450 return ""
387451 }
388- mpt .Annotations [key ] = secret
452+ mpt .PodTemplateAnnotations [key ] = secret
389453 return secret
390454}
391455
@@ -399,7 +463,7 @@ func (p *serviceBindingProjector) volumeName(binding *servicebindingv1beta1.Serv
399463
400464func (p * serviceBindingProjector ) typeAnnotation (binding * servicebindingv1beta1.ServiceBinding , mpt * metaPodTemplate ) string {
401465 key := p .typeAnnotationName (binding )
402- mpt .Annotations [key ] = binding .Spec .Type
466+ mpt .PodTemplateAnnotations [key ] = binding .Spec .Type
403467 return key
404468}
405469
@@ -409,10 +473,43 @@ func (p *serviceBindingProjector) typeAnnotationName(binding *servicebindingv1be
409473
410474func (p * serviceBindingProjector ) providerAnnotation (binding * servicebindingv1beta1.ServiceBinding , mpt * metaPodTemplate ) string {
411475 key := p .providerAnnotationName (binding )
412- mpt .Annotations [key ] = binding .Spec .Provider
476+ mpt .PodTemplateAnnotations [key ] = binding .Spec .Provider
413477 return key
414478}
415479
416480func (p * serviceBindingProjector ) providerAnnotationName (binding * servicebindingv1beta1.ServiceBinding ) string {
417481 return fmt .Sprintf ("%s%s" , ProviderAnnotationPrefix , binding .UID )
418482}
483+
484+ func (p * serviceBindingProjector ) retrieveLocalMapping (binding * servicebindingv1beta1.ServiceBinding , workload runtime.Object ) (* servicebindingv1beta1.ClusterWorkloadResourceMappingSpec , error ) {
485+ annoations := workload .(metav1.Object ).GetAnnotations ()
486+ if annoations == nil {
487+ return nil , nil
488+ }
489+ data , ok := annoations [p .mappingAnnotationName (binding )]
490+ if ! ok {
491+ return nil , nil
492+ }
493+ var mapping servicebindingv1beta1.ClusterWorkloadResourceMappingSpec
494+ if err := json .Unmarshal ([]byte (data ), & mapping ); err != nil {
495+ return nil , err
496+ }
497+ return & mapping , nil
498+ }
499+
500+ func (p * serviceBindingProjector ) stashLocalMapping (binding * servicebindingv1beta1.ServiceBinding , mpt * metaPodTemplate , mapping * servicebindingv1beta1.ClusterWorkloadResourceMappingSpec ) error {
501+ if mapping == nil {
502+ delete (mpt .WorkloadAnnotations , p .mappingAnnotationName (binding ))
503+ return nil
504+ }
505+ data , err := json .Marshal (mapping )
506+ if err != nil {
507+ return err
508+ }
509+ mpt .WorkloadAnnotations [p .mappingAnnotationName (binding )] = string (data )
510+ return nil
511+ }
512+
513+ func (p * serviceBindingProjector ) mappingAnnotationName (binding * servicebindingv1beta1.ServiceBinding ) string {
514+ return fmt .Sprintf ("%s%s" , MappingAnnotationPrefix , binding .UID )
515+ }
0 commit comments