• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kubernetes-sigs / kubebuilder / 20684013645

03 Jan 2026 10:48PM UTC coverage: 61.602% (+0.4%) from 61.226%
20684013645

Pull #5339

github

camilamacedo86
(helm/v2-alpha): add custom resources to templates/extras

Manual resources not matching the standard layout now go to
templates/extras/ with proper templating applied.

Fixes #5248

Assisted-by: Cursor
Pull Request #5339: (Blocked by #5294) ✨ (helm/v2-alpha): add custom resources to templates/extras

103 of 107 new or added lines in 7 files covered. (96.26%)

311 existing lines in 7 files now uncovered.

5984 of 9714 relevant lines covered (61.6%)

26.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/pkg/plugins/optional/helm/v2alpha/scaffolds/edit_kustomize.go
1
/*
2
Copyright 2025 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 scaffolds
18

19
import (
20
        "fmt"
21
        "log/slog"
22
        "os"
23
        "os/exec"
24
        "path/filepath"
25
        "strings"
26

27
        "sigs.k8s.io/kubebuilder/v4/pkg/config"
28
        "sigs.k8s.io/kubebuilder/v4/pkg/machinery"
29
        "sigs.k8s.io/kubebuilder/v4/pkg/plugins"
30
        "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm/v2alpha/scaffolds/internal/kustomize"
31
        "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm/v2alpha/scaffolds/internal/templates"
32
        charttemplates "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm/v2alpha/scaffolds/internal/templates/chart-templates"
33
        "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm/v2alpha/scaffolds/internal/templates/github"
34
)
35

36
const (
37
        defaultManifestsFile = "dist/install.yaml"
38
)
39

40
var _ plugins.Scaffolder = &editKustomizeScaffolder{}
41

42
type editKustomizeScaffolder struct {
43
        config        config.Config
44
        fs            machinery.Filesystem
45
        force         bool
46
        manifestsFile string
47
        outputDir     string
48
}
49

50
// NewKustomizeHelmScaffolder returns a new Scaffolder for HelmPlugin using kustomize output
51
func NewKustomizeHelmScaffolder(cfg config.Config, force bool, manifestsFile, outputDir string) plugins.Scaffolder {
×
52
        return &editKustomizeScaffolder{
×
53
                config:        cfg,
×
54
                force:         force,
×
55
                manifestsFile: manifestsFile,
×
56
                outputDir:     outputDir,
×
57
        }
×
58
}
×
59

60
// InjectFS implements cmdutil.Scaffolder
61
func (s *editKustomizeScaffolder) InjectFS(fs machinery.Filesystem) {
×
62
        s.fs = fs
×
63
}
×
64

65
// Scaffold generates the complete Helm chart from kustomize output
66
func (s *editKustomizeScaffolder) Scaffold() error {
×
67
        slog.Info("Generating Helm Chart from kustomize output")
×
68

×
69
        // Ensure chart directory structure exists
×
70
        if err := s.ensureChartDirectoryExists(); err != nil {
×
71
                return fmt.Errorf("failed to create chart directory: %w", err)
×
72
        }
×
73

74
        // Generate fresh kustomize output if using default file
75
        if s.manifestsFile == defaultManifestsFile {
×
76
                if err := s.generateKustomizeOutput(); err != nil {
×
77
                        return fmt.Errorf("failed to generate kustomize output: %w", err)
×
78
                }
×
79
        }
80

81
        // Parse the kustomize output into organized resource groups
82
        parser := kustomize.NewParser(s.manifestsFile)
×
83
        resources, err := parser.Parse()
×
84
        if err != nil {
×
85
                return fmt.Errorf("failed to parse kustomize output from %s: %w", s.manifestsFile, err)
×
86
        }
×
87

88
        // Analyze resources to determine chart features
89
        hasWebhooks := len(resources.WebhookConfigurations) > 0 || len(resources.Certificates) > 0
×
90
        // Prometheus is enabled when ServiceMonitor resources exist (../prometheus enabled)
×
91
        hasPrometheus := len(resources.ServiceMonitors) > 0
×
92
        // Metrics are enabled either when ServiceMonitor exists or when a metrics service is present
×
93
        hasMetrics := hasPrometheus
×
94
        if !hasMetrics {
×
95
                for _, svc := range resources.Services {
×
96
                        if strings.Contains(svc.GetName(), "metrics") {
×
97
                                hasMetrics = true
×
98
                                break
×
99
                        }
100
                }
101
        }
102

103
        // When Prometheus is enabled via kustomize, ensure any previously-generated
104
        // generic ServiceMonitor file is removed to avoid duplicates in the chart.
105
        if hasPrometheus {
×
106
                staleSM := filepath.Join(s.outputDir, "chart", "templates", "monitoring", "servicemonitor.yaml")
×
107
                if rmErr := s.fs.FS.Remove(staleSM); rmErr != nil && !os.IsNotExist(rmErr) {
×
108
                        // Not fatal; log and continue
×
109
                        slog.Warn("failed to remove stale generic ServiceMonitor", "path", staleSM, "error", rmErr)
×
110
                }
×
111
        }
112
        namePrefix := resources.EstimatePrefix(s.config.GetProjectName())
×
NEW
113
        chartConverter := kustomize.NewChartConverter(resources, namePrefix, s.outputDir)
×
114
        deploymentConfig := chartConverter.ExtractDeploymentConfig()
×
115

×
116
        // Create scaffold for standard Helm chart files
×
117
        scaffold := machinery.NewScaffold(s.fs, machinery.WithConfig(s.config))
×
118

×
119
        // Define the standard Helm chart files to generate
×
120
        chartFiles := []machinery.Builder{
×
121
                &github.HelmChartCI{},                        // GitHub Actions workflow for chart testing
×
122
                &templates.HelmChart{OutputDir: s.outputDir}, // Chart.yaml metadata
×
123
                &templates.HelmValuesBasic{
×
124
                        // values.yaml with dynamic config
×
125
                        HasWebhooks:      hasWebhooks,
×
126
                        HasMetrics:       hasMetrics,
×
127
                        DeploymentConfig: deploymentConfig,
×
128
                        OutputDir:        s.outputDir,
×
129
                        Force:            s.force,
×
NEW
130
                        NamePrefix:       namePrefix,
×
131
                },
×
132
                &templates.HelmIgnore{OutputDir: s.outputDir},       // .helmignore file
×
133
                &charttemplates.HelmHelpers{OutputDir: s.outputDir}, // _helpers.tpl template functions
×
134
        }
×
135

×
136
        // Only scaffold the generic ServiceMonitor when the project does NOT already
×
137
        // provide one via kustomize (../prometheus). This avoids duplicate objects
×
138
        // with the same name within the Helm chart.
×
139
        if !hasPrometheus {
×
140
                // Find the metrics service name from parsed resources
×
141
                metricsServiceName := namePrefix + "-controller-manager-metrics-service"
×
142
                for _, svc := range resources.Services {
×
143
                        if strings.Contains(svc.GetName(), "metrics-service") {
×
144
                                metricsServiceName = svc.GetName()
×
UNCOV
145
                                break
×
146
                        }
147
                }
148

149
                chartFiles = append(chartFiles, &charttemplates.ServiceMonitor{
×
150
                        OutputDir:   s.outputDir,
×
151
                        NamePrefix:  namePrefix,
×
152
                        ServiceName: metricsServiceName,
×
UNCOV
153
                })
×
154
        }
155

156
        // Generate template files from kustomize output
157
        if writeErr := chartConverter.WriteChartFiles(s.fs); writeErr != nil {
×
158
                return fmt.Errorf("failed to write chart template files: %w", writeErr)
×
UNCOV
159
        }
×
160

161
        // Generate standard Helm chart files
162
        if err = scaffold.Execute(chartFiles...); err != nil {
×
163
                return fmt.Errorf("failed to generate Helm chart files: %w", err)
×
UNCOV
164
        }
×
165

166
        slog.Info("Helm Chart generation completed successfully")
×
UNCOV
167
        return nil
×
168
}
169

170
// generateKustomizeOutput runs make build-installer to generate the manifests file
171
func (s *editKustomizeScaffolder) generateKustomizeOutput() error {
×
172
        slog.Info("Generating kustomize output with make build-installer")
×
173

×
174
        // Check if Makefile exists
×
175
        if _, err := os.Stat("Makefile"); os.IsNotExist(err) {
×
176
                return fmt.Errorf("makefile not found in current directory")
×
UNCOV
177
        }
×
178

179
        // Run make build-installer
180
        cmd := exec.Command("make", "build-installer")
×
181
        cmd.Stdout = os.Stdout
×
182
        cmd.Stderr = os.Stderr
×
183

×
184
        if err := cmd.Run(); err != nil {
×
185
                return fmt.Errorf("failed to run make build-installer: %w", err)
×
UNCOV
186
        }
×
187

188
        // Verify that the manifests file was created
189
        if _, err := os.Stat(defaultManifestsFile); os.IsNotExist(err) {
×
190
                return fmt.Errorf("%s was not generated by make build-installer", defaultManifestsFile)
×
UNCOV
191
        }
×
192

UNCOV
193
        return nil
×
194
}
195

196
// ensureChartDirectoryExists creates the chart directory structure if it doesn't exist
197
func (s *editKustomizeScaffolder) ensureChartDirectoryExists() error {
×
198
        dirs := []string{
×
199
                filepath.Join(s.outputDir, "chart"),
×
200
                filepath.Join(s.outputDir, "chart", "templates"),
×
201
        }
×
202

×
203
        for _, dir := range dirs {
×
204
                if err := os.MkdirAll(dir, 0o755); err != nil {
×
205
                        return fmt.Errorf("failed to create directory %s: %w", dir, err)
×
UNCOV
206
                }
×
207
        }
208

UNCOV
209
        return nil
×
210
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc