Skip to content

Commit a5a6e11

Browse files
author
Hannes Hörl
committed
Annotations for ytt & helm per template step
This allows to control an app's individual template steps via annotations. It is modelled after the fetch annotations, considering the template step's index when pulling information from the app's annotations, e.g. `ext.packaging.carvel.dev/ytt-0-paths-from-secret-name`. The original annotations, without the index, are still used, for the first occurrence of the relevant template step class. The annotations are not as "strict" as the data values from the pkgi. E.g. when there is an annotation for a template step that either does not exist or is not of the desired class, it does not error, the annotation is just & silently ignored. After all, with annotations we don't have as strict of an API. Signed-off-by: Hannes Hörl <[email protected]>
1 parent 1aa9ebf commit a5a6e11

File tree

2 files changed

+287
-357
lines changed

2 files changed

+287
-357
lines changed

pkg/packageinstall/app.go

Lines changed: 108 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,20 @@ import (
2121
const (
2222
ManuallyControlledAnnKey = "ext.packaging.carvel.dev/manually-controlled"
2323

24-
HelmTemplateOverlayNameKey = "ext.packaging.carvel.dev/helm-template-name"
25-
HelmTemplateOverlayNameSpaceKey = "ext.packaging.carvel.dev/helm-template-namespace"
24+
HelmTemplateOverlayNameKey = "ext.packaging.carvel.dev/helm-template-name"
25+
HelmTemplateOverlayNameKeyFmt = "ext.packaging.carvel.dev/helm-%d-template-name"
26+
HelmTemplateOverlayNameSpaceKey = "ext.packaging.carvel.dev/helm-template-namespace"
27+
HelmTemplateOverlayNameSpaceKeyFmt = "ext.packaging.carvel.dev/helm-%d-template-namespace"
2628

2729
// Resulting secret names are sorted deterministically by suffix
28-
ExtYttPathsFromSecretNameAnnKey = "ext.packaging.carvel.dev/ytt-paths-from-secret-name"
29-
ExtHelmPathsFromSecretNameAnnKey = "ext.packaging.carvel.dev/helm-template-values-from-secret-name"
30+
ExtYttPathsFromSecretNameAnnKey = "ext.packaging.carvel.dev/ytt-paths-from-secret-name"
31+
ExtYttPathsFromSecretNameAnnKeyFmt = "ext.packaging.carvel.dev/ytt-%d-paths-from-secret-name"
32+
ExtHelmPathsFromSecretNameAnnKey = "ext.packaging.carvel.dev/helm-template-values-from-secret-name"
33+
ExtHelmPathsFromSecretNameAnnKeyFmt = "ext.packaging.carvel.dev/helm-%d-template-values-from-secret-name"
3034

3135
// ExtYttDataValuesOverlaysAnnKey if set, adds the pkgi's values secrets as overlays/paths, not as values, to the app
32-
ExtYttDataValuesOverlaysAnnKey = "ext.packaging.carvel.dev/ytt-data-values-overlays"
36+
ExtYttDataValuesOverlaysAnnKey = "ext.packaging.carvel.dev/ytt-data-values-overlays"
37+
ExtYttDataValuesOverlaysAnnKeyFmt = "ext.packaging.carvel.dev/ytt-%d-data-values-overlays"
3338

3439
ExtFetchSecretNameAnnKeyFmt = "ext.packaging.carvel.dev/fetch-%d-secret-name"
3540
)
@@ -101,21 +106,10 @@ func NewApp(existingApp *v1alpha1.App, pkgInstall *pkgingv1alpha1.PackageInstall
101106
}
102107

103108
templatesPatcher := templateStepsPatcher{
104-
yttPatcher: &yttStepPatcher{
105-
addValuesAsInlinePaths: pkgiHasAnnotation(pkgInstall, ExtYttDataValuesOverlaysAnnKey),
106-
additionalPaths: secretNamesFromAnn(pkgInstall, ExtYttPathsFromSecretNameAnnKey),
107-
},
108-
helmPatcher: &helmStepPatcher{
109-
additionalPaths: secretNamesFromAnn(pkgInstall, ExtHelmPathsFromSecretNameAnnKey),
110-
name: pkgiAnnotationValue(pkgInstall, HelmTemplateOverlayNameKey),
111-
namespace: pkgiAnnotationValue(pkgInstall, HelmTemplateOverlayNameSpaceKey),
112-
},
113-
cuePatcher: &cueStepPatcher{},
114-
115109
templateSteps: desiredApp.Spec.Template,
116110
values: pkgInstall.Spec.Values,
111+
annotations: pkgInstall.Annotations,
117112
}
118-
119113
if err := templatesPatcher.patch(); err != nil {
120114
return &v1alpha1.App{}, err
121115
}
@@ -136,63 +130,10 @@ const (
136130
stepClassCue stepClass = "cue"
137131
)
138132

139-
type yttStepPatcher struct {
140-
addValuesAsInlinePaths bool // TODO: support multiple ytt steps
141-
additionalPaths []string // TODO: support multiple ytt steps
142-
}
143-
144-
func (yp *yttStepPatcher) addValues(yttStep *kcv1alpha1.AppTemplateYtt, value pkgingv1alpha1.PackageInstallValues) {
145-
if yp.addValuesAsInlinePaths {
146-
addSecretAsInlinePath(&yttStep.Inline, value.SecretRef.Name)
147-
} else {
148-
addSecretAsValueSource(&yttStep.ValuesFrom, value.SecretRef.Name)
149-
}
150-
}
151-
152-
func (yp *yttStepPatcher) addPaths(yttStep *kcv1alpha1.AppTemplateYtt) {
153-
for _, secretName := range yp.additionalPaths {
154-
addSecretAsInlinePath(&yttStep.Inline, secretName)
155-
}
156-
}
157-
158-
type helmStepPatcher struct {
159-
additionalPaths []string
160-
name string // TODO: support multiple helm steps
161-
namespace string // TODO: support multiple helm steps
162-
}
163-
164-
func (hp *helmStepPatcher) addValues(helmStep *kcv1alpha1.AppTemplateHelmTemplate, value pkgingv1alpha1.PackageInstallValues) {
165-
addSecretAsValueSource(&helmStep.ValuesFrom, value.SecretRef.Name)
166-
}
167-
168-
func (hp *helmStepPatcher) addPaths(helmStep *kcv1alpha1.AppTemplateHelmTemplate) {
169-
for _, secretName := range hp.additionalPaths {
170-
addSecretAsValueSource(&helmStep.ValuesFrom, secretName)
171-
}
172-
}
173-
174-
func (hp *helmStepPatcher) setNameAndNamespace(helmStep *kcv1alpha1.AppTemplateHelmTemplate) {
175-
if hp.name != "" {
176-
helmStep.Name = hp.name
177-
}
178-
if hp.namespace != "" {
179-
helmStep.Namespace = hp.namespace
180-
}
181-
}
182-
183-
type cueStepPatcher struct{}
184-
185-
func (cp *cueStepPatcher) addValues(cueStep *kcv1alpha1.AppTemplateCue, value pkgingv1alpha1.PackageInstallValues) {
186-
addSecretAsValueSource(&cueStep.ValuesFrom, value.SecretRef.Name)
187-
}
188-
189133
type templateStepsPatcher struct {
190134
templateSteps []kcv1alpha1.AppTemplate
191135
values []pkgingv1alpha1.PackageInstallValues
192-
193-
yttPatcher *yttStepPatcher
194-
helmPatcher *helmStepPatcher
195-
cuePatcher *cueStepPatcher
136+
annotations map[string]string
196137

197138
classifiedSteps [][]stepClass
198139
once sync.Once
@@ -266,6 +207,18 @@ func (p *templateStepsPatcher) defaultStepIdxs(stepIdxs []int, class stepClass)
266207
}
267208

268209
func (p *templateStepsPatcher) patch() error {
210+
if err := p.patchFromValues(); err != nil {
211+
return err
212+
}
213+
p.patchFromYttAnnotations()
214+
p.patchFromHelmAnnotations()
215+
216+
return nil
217+
}
218+
219+
// patchFromValues patches all 'valueable' template steps with values from the
220+
// packageInstall
221+
func (p *templateStepsPatcher) patchFromValues() error {
269222
for _, values := range p.values {
270223
stepIdxs, err := p.defaultStepIdxs(values.TemplateSteps, stepClassValueable)
271224
if err != nil {
@@ -284,36 +237,108 @@ func (p *templateStepsPatcher) patch() error {
284237

285238
switch {
286239
case p.stepHasClass(stepIdx, stepClassYtt):
287-
p.yttPatcher.addValues(templateStep.Ytt, values)
240+
// ytt is a bit special: when we find the indexed annotation (or the
241+
// naked one for the first ytt step) to apply the values as inline
242+
// paths on the app, we do so; else, by default, we apply the pkgi's
243+
// values as values on the app.
244+
valuesAsPath := false
245+
if firstYttStepIdx, ok := p.firstOf(stepClassYtt); ok && stepIdx == firstYttStepIdx {
246+
if _, ok := p.annotations[ExtYttDataValuesOverlaysAnnKey]; ok {
247+
valuesAsPath = true
248+
}
249+
}
250+
if _, ok := p.annotations[fmt.Sprintf(ExtYttDataValuesOverlaysAnnKeyFmt, stepIdx)]; ok {
251+
valuesAsPath = true
252+
}
253+
if valuesAsPath {
254+
addSecretAsInlinePath(&templateStep.Ytt.Inline, values.SecretRef.Name)
255+
} else {
256+
addSecretAsValueSource(&templateStep.Ytt.ValuesFrom, values.SecretRef.Name)
257+
}
288258

289259
case p.stepHasClass(stepIdx, stepClassHelm):
290-
p.helmPatcher.addValues(templateStep.HelmTemplate, values)
260+
addSecretAsValueSource(&templateStep.HelmTemplate.ValuesFrom, values.SecretRef.Name)
291261

292262
case p.stepHasClass(stepIdx, stepClassCue):
293-
p.cuePatcher.addValues(templateStep.Cue, values)
263+
addSecretAsValueSource(&templateStep.Cue.ValuesFrom, values.SecretRef.Name)
294264
}
295265
}
296266
}
297267

268+
return nil
269+
}
270+
271+
// patchFromYttAnnotations patches ytt template steps with values from
272+
// annotations from the packageInstall
273+
func (p *templateStepsPatcher) patchFromYttAnnotations() {
274+
firstYttIdx, hasYtt := p.firstOf(stepClassYtt)
275+
276+
if !hasYtt {
277+
return
278+
}
279+
280+
patcher := func(ts *kcv1alpha1.AppTemplateYtt, pathsAnno string) {
281+
for _, secretName := range secretNamesFromAnn(p.annotations, pathsAnno) {
282+
addSecretAsInlinePath(&ts.Inline, secretName)
283+
}
284+
}
285+
298286
for _, stepIdx := range p.getClassifiedSteps(stepClassYtt) {
299-
p.yttPatcher.addPaths(p.templateSteps[stepIdx].Ytt)
300-
break // TODO: support multiple ytt steps
287+
ts := p.templateSteps[stepIdx].Ytt
288+
289+
if stepIdx == firstYttIdx {
290+
// annotations that are not indexed are only applied to the first ytt
291+
// step, so that we are backwards compatible
292+
patcher(ts, ExtYttPathsFromSecretNameAnnKey)
293+
}
294+
295+
patcher(ts, fmt.Sprintf(ExtYttPathsFromSecretNameAnnKeyFmt, stepIdx))
296+
}
297+
}
298+
299+
// patchFromHelmAnnotations patches helm template steps with values from
300+
// annotations from the packageInstall
301+
func (p *templateStepsPatcher) patchFromHelmAnnotations() {
302+
firstHelmIdx, hasHelm := p.firstOf(stepClassHelm)
303+
304+
if !hasHelm {
305+
return
301306
}
307+
308+
patcher := func(ts *kcv1alpha1.AppTemplateHelmTemplate, nameAnno, namespaceAnno, pathsAnno string) {
309+
if name, ok := p.annotations[nameAnno]; ok && name != "" {
310+
ts.Name = name
311+
}
312+
if namespace, ok := p.annotations[namespaceAnno]; ok && namespace != "" {
313+
ts.Namespace = namespace
314+
}
315+
for _, secretName := range secretNamesFromAnn(p.annotations, pathsAnno) {
316+
addSecretAsValueSource(&ts.ValuesFrom, secretName)
317+
}
318+
}
319+
302320
for _, stepIdx := range p.getClassifiedSteps(stepClassHelm) {
303321
ts := p.templateSteps[stepIdx].HelmTemplate
304-
p.helmPatcher.addPaths(ts)
305-
p.helmPatcher.setNameAndNamespace(ts)
306-
break // TODO: support multiple helm steps
307-
}
308322

309-
return nil
323+
if stepIdx == firstHelmIdx {
324+
// annotations that are not indexed are only applied to the first helm
325+
// step, so that we are backwards compatible
326+
patcher(ts, HelmTemplateOverlayNameKey, HelmTemplateOverlayNameSpaceKey, ExtHelmPathsFromSecretNameAnnKey)
327+
}
328+
329+
patcher(ts,
330+
fmt.Sprintf(HelmTemplateOverlayNameKeyFmt, stepIdx),
331+
fmt.Sprintf(HelmTemplateOverlayNameSpaceKeyFmt, stepIdx),
332+
fmt.Sprintf(ExtHelmPathsFromSecretNameAnnKeyFmt, stepIdx),
333+
)
334+
}
310335
}
311336

312-
func secretNamesFromAnn(installedPkg *pkgingv1alpha1.PackageInstall, annKey string) []string {
337+
func secretNamesFromAnn(annotations map[string]string, annKey string) []string {
313338
var suffixes []string
314339
suffixToSecretName := map[string]string{}
315340

316-
for ann, secretName := range installedPkg.Annotations {
341+
for ann, secretName := range annotations {
317342
if ann == annKey {
318343
suffix := ""
319344
suffixToSecretName[suffix] = secretName
@@ -334,18 +359,6 @@ func secretNamesFromAnn(installedPkg *pkgingv1alpha1.PackageInstall, annKey stri
334359
return result
335360
}
336361

337-
func pkgiAnnotationValue(pkgi *pkgingv1alpha1.PackageInstall, key string) string {
338-
if anno, found := pkgi.Annotations[key]; found {
339-
return anno
340-
}
341-
return ""
342-
}
343-
344-
func pkgiHasAnnotation(pkgi *pkgingv1alpha1.PackageInstall, key string) bool {
345-
_, found := pkgi.Annotations[key]
346-
return found
347-
}
348-
349362
// addSecretAsInlinePath adds a secret as an inline path to the provided inline
350363
// fetches. If the inline fetch is nil, it is initialized.
351364
func addSecretAsInlinePath(inline **kcv1alpha1.AppFetchInline, secretName string) {

0 commit comments

Comments
 (0)