Skip to content

Commit 9438c44

Browse files
committed
addons: render addons as tasks
Defer addon manifest rendering and bootstrap-channel assembly into AddonManifest and BootstrapChannel fi-tasks so addon templates can reference the finalized task graph. Signed-off-by: Ciprian Hacman <ciprian@hakman.dev>
1 parent daad3ce commit 9438c44

13 files changed

Lines changed: 1023 additions & 264 deletions

pkg/templates/templates.go

Lines changed: 15 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,25 @@ limitations under the License.
1717
package templates
1818

1919
import (
20-
"bytes"
2120
"context"
2221
"fmt"
23-
"io"
2422
"os"
2523
"strings"
26-
"text/template"
2724

2825
"k8s.io/klog/v2"
29-
"k8s.io/kops/pkg/apis/kops"
3026
"k8s.io/kops/upup/pkg/fi"
3127
"k8s.io/kops/util/pkg/vfs"
3228
)
3329

3430
type Templates struct {
35-
cluster *kops.Cluster
36-
resources map[string]fi.Resource
37-
TemplateFunctions template.FuncMap
31+
resources map[string]fi.Resource
32+
isTemplate map[string]bool
3833
}
3934

40-
func LoadTemplates(ctx context.Context, cluster *kops.Cluster, base vfs.Path) (*Templates, error) {
35+
func LoadTemplates(ctx context.Context, base vfs.Path) (*Templates, error) {
4136
t := &Templates{
42-
cluster: cluster,
43-
resources: make(map[string]fi.Resource),
44-
TemplateFunctions: make(template.FuncMap),
37+
resources: make(map[string]fi.Resource),
38+
isTemplate: make(map[string]bool),
4539
}
4640
err := t.loadFrom(ctx, base)
4741
if err != nil {
@@ -54,6 +48,11 @@ func (t *Templates) Find(key string) fi.Resource {
5448
return t.resources[key]
5549
}
5650

51+
// IsTemplate reports whether key was loaded from a file ending in .template.
52+
func (t *Templates) IsTemplate(key string) bool {
53+
return t.isTemplate[key]
54+
}
55+
5756
func (t *Templates) loadFrom(ctx context.Context, base vfs.Path) error {
5857
files, err := base.ReadTree(ctx)
5958
if err != nil {
@@ -75,74 +74,11 @@ func (t *Templates) loadFrom(ctx context.Context, base vfs.Path) error {
7574
return fmt.Errorf("error getting relative path for %s", f)
7675
}
7776

78-
var resource fi.Resource
79-
if strings.HasSuffix(key, ".template") {
80-
key = strings.TrimSuffix(key, ".template")
81-
klog.V(6).Infof("loading (templated) resource %q", key)
82-
83-
resource = &templateResource{
84-
template: string(contents),
85-
loader: t,
86-
key: key,
87-
}
88-
} else {
89-
klog.V(6).Infof("loading resource %q", key)
90-
resource = fi.NewBytesResource(contents)
91-
92-
}
93-
94-
t.resources[key] = resource
77+
isTemplate := strings.HasSuffix(key, ".template")
78+
key = strings.TrimSuffix(key, ".template")
79+
klog.V(6).Infof("loading resource %q", key)
80+
t.resources[key] = fi.NewBytesResource(contents)
81+
t.isTemplate[key] = isTemplate
9582
}
9683
return nil
9784
}
98-
99-
func (l *Templates) executeTemplate(key string, d string) (string, error) {
100-
t := template.New(key)
101-
102-
funcMap := make(template.FuncMap)
103-
// funcMap["Args"] = func() []string {
104-
// return args
105-
//}
106-
//funcMap["RenderResource"] = func(resourceName string, args []string) (string, error) {
107-
// return l.renderResource(resourceName, args)
108-
//}
109-
for k, fn := range l.TemplateFunctions {
110-
funcMap[k] = fn
111-
}
112-
t.Funcs(funcMap)
113-
114-
t.Option("missingkey=zero")
115-
116-
spec := l.cluster.Spec
117-
118-
_, err := t.Parse(d)
119-
if err != nil {
120-
return "", fmt.Errorf("error parsing template %q: %v", key, err)
121-
}
122-
123-
var buffer bytes.Buffer
124-
err = t.ExecuteTemplate(&buffer, key, spec)
125-
if err != nil {
126-
return "", fmt.Errorf("error executing template %q: %v", key, err)
127-
}
128-
129-
return buffer.String(), nil
130-
}
131-
132-
type templateResource struct {
133-
key string
134-
loader *Templates
135-
template string
136-
}
137-
138-
var _ fi.Resource = &templateResource{}
139-
140-
func (a *templateResource) Open() (io.Reader, error) {
141-
var err error
142-
result, err := a.loader.executeTemplate(a.key, a.template)
143-
if err != nil {
144-
return nil, fmt.Errorf("error executing resource template %q: %v", a.key, err)
145-
}
146-
reader := bytes.NewReader([]byte(result))
147-
return reader, nil
148-
}

pkg/templates/templates_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
Copyright 2026 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package templates
18+
19+
import (
20+
"context"
21+
"os"
22+
"path/filepath"
23+
"testing"
24+
25+
"k8s.io/kops/util/pkg/vfs"
26+
)
27+
28+
func TestLoadTemplatesTracksTemplateSources(t *testing.T) {
29+
dir := t.TempDir()
30+
if err := os.MkdirAll(filepath.Join(dir, "addons", "example"), 0755); err != nil {
31+
t.Fatalf("creating addon dir: %v", err)
32+
}
33+
if err := os.WriteFile(filepath.Join(dir, "addons", "example", "plain.yaml"), []byte("plain"), 0644); err != nil {
34+
t.Fatalf("writing plain resource: %v", err)
35+
}
36+
if err := os.WriteFile(filepath.Join(dir, "addons", "example", "rendered.yaml.template"), []byte("template"), 0644); err != nil {
37+
t.Fatalf("writing template resource: %v", err)
38+
}
39+
40+
templates, err := LoadTemplates(context.Background(), vfs.NewFSPath(dir))
41+
if err != nil {
42+
t.Fatalf("loading templates: %v", err)
43+
}
44+
45+
if templates.Find("addons/example/plain.yaml") == nil {
46+
t.Fatalf("plain resource was not loaded")
47+
}
48+
if templates.IsTemplate("addons/example/plain.yaml") {
49+
t.Fatalf("plain resource reported as template")
50+
}
51+
52+
if templates.Find("addons/example/rendered.yaml") == nil {
53+
t.Fatalf("template resource was not loaded under trimmed key")
54+
}
55+
if !templates.IsTemplate("addons/example/rendered.yaml") {
56+
t.Fatalf("template resource did not report template source")
57+
}
58+
}

upup/pkg/fi/cloudup/apply_cluster.go

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -513,9 +513,10 @@ func (c *ApplyClusterCmd) Run(ctx context.Context) (*ApplyResults, error) {
513513
}
514514
}
515515

516-
tf := &TemplateFunctions{
517-
KopsModelContext: *modelContext,
518-
cloud: cloud,
516+
addonRenderer := &addonTemplateRenderer{
517+
modelContext: modelContext,
518+
cloud: cloud,
519+
secretStore: secretStore,
519520
}
520521

521522
nodeUpAssets, err := nodemodel.BuildNodeUpAssets(ctx, assetBuilder)
@@ -534,22 +535,18 @@ func (c *ApplyClusterCmd) Run(ctx context.Context) (*ApplyResults, error) {
534535
}
535536

536537
{
537-
templates, err := templates.LoadTemplates(ctx, cluster, models.NewAssetPath("cloudup/resources"))
538+
templates, err := templates.LoadTemplates(ctx, models.NewAssetPath("cloudup/resources"))
538539
if err != nil {
539540
return nil, fmt.Errorf("error loading templates: %v", err)
540541
}
541542

542-
err = tf.AddTo(templates.TemplateFunctions, secretStore)
543-
if err != nil {
544-
return nil, err
545-
}
546-
547543
bcb := bootstrapchannelbuilder.NewBootstrapChannelBuilder(
548544
modelContext,
549545
clusterLifecycle,
550546
assetBuilder,
551547
templates,
552548
addons,
549+
addonRenderer,
553550
)
554551

555552
l.Builders = append(l.Builders,

0 commit comments

Comments
 (0)