Skip to content

Commit

Permalink
Drop mappings (#134)
Browse files Browse the repository at this point in the history
The functionality enabled by mappings can/will be replaced with
dedicated resources that can decorate a secret exposing the
ProvisionedService duck-type which can be consumed by a ServiceBinding.

Custom type and provider fields are now managed with projected volumes
and field path env vars.

Refs servicebinding/spec#154

Signed-off-by: Scott Andrews <andrewssc@vmware.com>
  • Loading branch information
scothis authored Jul 20, 2021
1 parent b600cd1 commit 7d04196
Show file tree
Hide file tree
Showing 25 changed files with 939 additions and 1,294 deletions.
20 changes: 0 additions & 20 deletions config/300-servicebinding.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -142,26 +142,6 @@ spec:
- name
type: object
type: array
mappings:
description: Mappings is the collection of mappings from existing
Secret entries to new Secret entries
items:
description: ServiceBindingMapping defines a mapping from the existing
collection of Secret values to a new Secret entry.
properties:
name:
description: Name is the name of the mapped Secret entry
type: string
value:
description: Value is the value of the new Secret entry. Contents
may be a Go template and refer to the other secret entries
by name.
type: string
required:
- name
- value
type: object
type: array
name:
description: Name is the name of the service as projected into the
application container. Defaults to .metadata.name.
Expand Down
4 changes: 4 additions & 0 deletions config/300-servicebindingprojection.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ spec:
type: array
name:
type: string
provider:
type: string
type:
type: string
required:
- application
- binding
Expand Down
110 changes: 100 additions & 10 deletions pkg/apis/labsinternal/v1alpha1/servicebindingprojection_lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"context"
"crypto/sha1"
"fmt"
"regexp"
"sort"
"strings"

Expand Down Expand Up @@ -57,19 +58,64 @@ func (b *ServiceBindingProjection) Do(ctx context.Context, ps *duckv1.WithPod) {
b.Undo(ctx, ps)

injectedSecrets, injectedVolumes := b.injectedValues(ps)
key := b.annotationKey()

sb := b.Spec.Binding

volume := corev1.Volume{
Name: fmt.Sprintf("%s%x", bindingVolumePrefix, sha1.Sum([]byte(sb.Name))),
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: sb.Name,
Projected: &corev1.ProjectedVolumeSource{
Sources: []corev1.VolumeProjection{
{
Secret: &corev1.SecretProjection{
LocalObjectReference: corev1.LocalObjectReference{
Name: sb.Name,
},
},
},
},
},
},
}
if b.Spec.Type != "" {
typeAnnotation := fmt.Sprintf("%s-type", key)
ps.Spec.Template.Annotations[typeAnnotation] = b.Spec.Type
volume.VolumeSource.Projected.Sources = append(volume.VolumeSource.Projected.Sources,
corev1.VolumeProjection{
DownwardAPI: &corev1.DownwardAPIProjection{
Items: []corev1.DownwardAPIVolumeFile{
{
FieldRef: &corev1.ObjectFieldSelector{
FieldPath: fmt.Sprintf("metadata.annotations['%s']", typeAnnotation),
},
Path: "type",
},
},
},
},
)
}
if b.Spec.Provider != "" {
providerAnnotation := fmt.Sprintf("%s-provider", key)
ps.Spec.Template.Annotations[providerAnnotation] = b.Spec.Provider
volume.VolumeSource.Projected.Sources = append(volume.VolumeSource.Projected.Sources,
corev1.VolumeProjection{
DownwardAPI: &corev1.DownwardAPIProjection{
Items: []corev1.DownwardAPIVolumeFile{
{
FieldRef: &corev1.ObjectFieldSelector{
FieldPath: fmt.Sprintf("metadata.annotations['%s']", providerAnnotation),
},
Path: "provider",
},
},
},
},
)
}
ps.Spec.Template.Spec.Volumes = append(ps.Spec.Template.Spec.Volumes, volume)
injectedSecrets.Insert(volume.Secret.SecretName)
injectedSecrets.Insert(sb.Name)
injectedVolumes.Insert(volume.Name)
sort.SliceStable(ps.Spec.Template.Spec.Volumes, func(i, j int) bool {
iname := ps.Spec.Template.Spec.Volumes[i].Name
Expand All @@ -81,7 +127,7 @@ func (b *ServiceBindingProjection) Do(ctx context.Context, ps *duckv1.WithPod) {
return iname < jname
})
// track which secret is injected, so it can be removed when no longer used
ps.Annotations[b.annotationKey()] = volume.Secret.SecretName
ps.Annotations[key] = sb.Name

for i := range ps.Spec.Template.Spec.InitContainers {
c := &ps.Spec.Template.Spec.InitContainers[i]
Expand All @@ -98,6 +144,7 @@ func (b *ServiceBindingProjection) Do(ctx context.Context, ps *duckv1.WithPod) {
}

func (b *ServiceBindingProjection) doContainer(ctx context.Context, ps *duckv1.WithPod, c *corev1.Container, bindingVolume, secretName string, allInjectedVolumes, allInjectedSecrets sets.String) {
key := b.annotationKey()
mountPath := ""
// lookup predefined mount path
for _, e := range c.Env {
Expand Down Expand Up @@ -133,6 +180,30 @@ func (b *ServiceBindingProjection) doContainer(ctx context.Context, ps *duckv1.W

if len(b.Spec.Env) != 0 {
for _, e := range b.Spec.Env {
if e.Key == "type" && b.Spec.Type != "" {
typeAnnotation := fmt.Sprintf("%s-type", key)
c.Env = append(c.Env, corev1.EnvVar{
Name: e.Name,
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{
FieldPath: fmt.Sprintf("metadata.annotations['%s']", typeAnnotation),
},
},
})
continue
}
if e.Key == "provider" && b.Spec.Provider != "" {
providerAnnotation := fmt.Sprintf("%s-provider", key)
c.Env = append(c.Env, corev1.EnvVar{
Name: e.Name,
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{
FieldPath: fmt.Sprintf("metadata.annotations['%s']", providerAnnotation),
},
},
})
continue
}
c.Env = append(c.Env, corev1.EnvVar{
Name: e.Name,
ValueFrom: &corev1.EnvVarSource{
Expand All @@ -149,9 +220,7 @@ func (b *ServiceBindingProjection) doContainer(ctx context.Context, ps *duckv1.W
iv := c.Env[i]
jv := c.Env[j]
// only sort injected env
if iv.ValueFrom == nil || iv.ValueFrom.SecretKeyRef == nil ||
jv.ValueFrom == nil || jv.ValueFrom.SecretKeyRef == nil ||
!allInjectedSecrets.HasAll(iv.ValueFrom.SecretKeyRef.Name, jv.ValueFrom.SecretKeyRef.Name) {
if !b.isInjectedEnv(iv, allInjectedSecrets) || !b.isInjectedEnv(jv, allInjectedSecrets) {
return false
}
return iv.Name < jv.Name
Expand Down Expand Up @@ -183,15 +252,22 @@ func (b *ServiceBindingProjection) Undo(ctx context.Context, ps *duckv1.WithPod)
if ps.Annotations == nil {
ps.Annotations = map[string]string{}
}
if ps.Spec.Template.Annotations == nil {
ps.Spec.Template.Annotations = map[string]string{}
}

key := b.annotationKey()
removeSecrets := sets.NewString(ps.Annotations[key], b.Spec.Binding.Name)
removeVolumes := sets.NewString()
delete(ps.Annotations, key)
delete(ps.Spec.Template.Annotations, fmt.Sprintf("%s-type", key))
delete(ps.Spec.Template.Annotations, fmt.Sprintf("%s-provider", key))

preservedVolumes := []corev1.Volume{}
for _, v := range ps.Spec.Template.Spec.Volumes {
if v.Secret != nil && removeSecrets.Has(v.Secret.SecretName) {
if v.Projected != nil && len(v.Projected.Sources) > 0 &&
v.Projected.Sources[0].Secret != nil &&
removeSecrets.Has(v.Projected.Sources[0].Secret.Name) {
removeVolumes.Insert(v.Name)
continue
}
Expand All @@ -218,7 +294,7 @@ func (b *ServiceBindingProjection) undoContainer(ctx context.Context, ps *duckv1

preservedEnv := []corev1.EnvVar{}
for _, e := range c.Env {
if e.ValueFrom == nil || e.ValueFrom.SecretKeyRef == nil || !removeSecrets.Has(e.ValueFrom.SecretKeyRef.Name) {
if !b.isInjectedEnv(e, removeSecrets) {
preservedEnv = append(preservedEnv, e)
}
}
Expand All @@ -238,13 +314,27 @@ func (b *ServiceBindingProjection) injectedValues(ps *duckv1.WithPod) (sets.Stri
}
}
for _, v := range ps.Spec.Template.Spec.Volumes {
if v.Secret != nil && secrets.Has(v.Secret.SecretName) {
if v.Projected != nil && len(v.Projected.Sources) > 0 &&
v.Projected.Sources[0].Secret != nil &&
secrets.Has(v.Projected.Sources[0].Secret.Name) {
volumes.Insert(v.Name)
}
}
return secrets, volumes
}

var fieldPathAnnotationRe = regexp.MustCompile(fmt.Sprintf(`^%s[0-9a-f]+%s(type|provider)%s$`, regexp.QuoteMeta(fmt.Sprintf("metadata.annotations['%s-", ServiceBindingProjectionAnnotationKey)), "-", "']"))

func (b *ServiceBindingProjection) isInjectedEnv(e corev1.EnvVar, allInjectedSecrets sets.String) bool {
if e.ValueFrom != nil && e.ValueFrom.SecretKeyRef != nil && allInjectedSecrets.Has(e.ValueFrom.SecretKeyRef.Name) {
return true
}
if e.ValueFrom != nil && e.ValueFrom.FieldRef != nil && fieldPathAnnotationRe.MatchString(e.ValueFrom.FieldRef.FieldPath) {
return true
}
return false
}

func (bs *ServiceBindingProjectionStatus) InitializeConditions() {
sbpCondSet.Manage(bs).InitializeConditions()
}
Expand Down
Loading

0 comments on commit 7d04196

Please sign in to comment.