Skip to content

Commit

Permalink
add helm plugin to distribute projects
Browse files Browse the repository at this point in the history
  • Loading branch information
camilamacedo86 committed Oct 22, 2024
1 parent eae8b21 commit 07d3c66
Show file tree
Hide file tree
Showing 89 changed files with 3,890 additions and 9 deletions.
2 changes: 2 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
deployimagev1alpha1 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang/deploy-image/v1alpha1"
golangv4 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang/v4"
grafanav1alpha1 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/grafana/v1alpha"
helmv1alpha1 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm/v1alpha"
)

func init() {
Expand Down Expand Up @@ -61,6 +62,7 @@ func main() {
&kustomizecommonv2.Plugin{},
&deployimagev1alpha1.Plugin{},
&grafanav1alpha1.Plugin{},
&helmv1alpha1.Plugin{},
),
cli.WithPlugins(externalPlugins...),
cli.WithDefaultPlugins(cfgv3.Version, gov4Bundle),
Expand Down
40 changes: 40 additions & 0 deletions pkg/plugins/optional/helm/v1alpha/commons.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha

import (
"errors"

"sigs.k8s.io/kubebuilder/v4/pkg/config"
)

func InsertPluginMetaToConfig(target config.Config, cfg pluginConfig) error {
err := target.DecodePluginConfig(pluginKey, cfg)
if !errors.As(err, &config.UnsupportedFieldError{}) {

if err != nil && !errors.As(err, &config.PluginKeyNotFoundError{}) {
return err
}

if err = target.EncodePluginConfig(pluginKey, cfg); err != nil {
return err
}

}

return nil
}
68 changes: 68 additions & 0 deletions pkg/plugins/optional/helm/v1alpha/edit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha

import (
"fmt"

"github.com/spf13/pflag"
"sigs.k8s.io/kubebuilder/v4/pkg/config"
"sigs.k8s.io/kubebuilder/v4/pkg/machinery"
"sigs.k8s.io/kubebuilder/v4/pkg/plugin"
"sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm/v1alpha/scaffolds"
)

var _ plugin.EditSubcommand = &editSubcommand{}

type editSubcommand struct {
config config.Config
force bool
}

func (p *editSubcommand) UpdateMetadata(cliMeta plugin.CLIMetadata, subcmdMeta *plugin.SubcommandMetadata) {
subcmdMeta.Description = `Initialize a helm chart to distribute the project under dist/
`
subcmdMeta.Examples = fmt.Sprintf(` # Initialize a helm chart to distribute the project under dist/
%[1]s edit --plugins helm/v1alpha
`, cliMeta.CommandName)
}

func (p *editSubcommand) BindFlags(fs *pflag.FlagSet) {
fs.BoolVar(&p.force, "force", true, "if true, run re-generate the files")
}

func (p *editSubcommand) InjectConfig(c config.Config) error {
p.config = c
return nil
}

func (p *editSubcommand) Scaffold(fs machinery.Filesystem) error {
scaffolder := scaffolds.NewInitScaffolder()
scaffolder.InjectFS(fs)
err := scaffolder.Scaffold()
if err != nil {
return err
}

// Track the resources following a declarative approach
if err := InsertPluginMetaToConfig(p.config, pluginConfig{}); err != nil {
return err
}

return nil
}
62 changes: 62 additions & 0 deletions pkg/plugins/optional/helm/v1alpha/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha

import (
"fmt"

"sigs.k8s.io/kubebuilder/v4/pkg/config"
"sigs.k8s.io/kubebuilder/v4/pkg/machinery"
"sigs.k8s.io/kubebuilder/v4/pkg/plugin"
"sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/grafana/v1alpha/scaffolds"
)

var _ plugin.InitSubcommand = &initSubcommand{}

type initSubcommand struct {
config config.Config
}

func (p *initSubcommand) UpdateMetadata(cliMeta plugin.CLIMetadata, subcmdMeta *plugin.SubcommandMetadata) {
subcmdMeta.Description = `Initialize a helm chart to distribute the project under dist/
`
subcmdMeta.Examples = fmt.Sprintf(` # Initialize a helm chart to distribute the project under dist/
%[1]s init --plugins helm/v1alpha
`, cliMeta.CommandName)
}

func (p *initSubcommand) InjectConfig(c config.Config) error {
p.config = c
return nil
}

func (p *initSubcommand) Scaffold(fs machinery.Filesystem) error {
scaffolder := scaffolds.NewInitScaffolder()
scaffolder.InjectFS(fs)
err := scaffolder.Scaffold()
if err != nil {
return err
}

// Track the resources following a declarative approach
if err := InsertPluginMetaToConfig(p.config, pluginConfig{}); err != nil {
return err
}

return nil
}
64 changes: 64 additions & 0 deletions pkg/plugins/optional/helm/v1alpha/plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha

import (
"sigs.k8s.io/kubebuilder/v4/pkg/config"
cfgv3 "sigs.k8s.io/kubebuilder/v4/pkg/config/v3"
"sigs.k8s.io/kubebuilder/v4/pkg/model/stage"
"sigs.k8s.io/kubebuilder/v4/pkg/plugin"
"sigs.k8s.io/kubebuilder/v4/pkg/plugins"
)

const pluginName = "helm." + plugins.DefaultNameQualifier

var (
pluginVersion = plugin.Version{Number: 1, Stage: stage.Alpha}
supportedProjectVersions = []config.Version{cfgv3.Version}
pluginKey = plugin.KeyFor(Plugin{})
)

// Plugin implements the plugin.Full interface
type Plugin struct {
initSubcommand
editSubcommand
}

var (
_ plugin.Init = Plugin{}
)

// Name returns the name of the plugin
func (Plugin) Name() string { return pluginName }

// Version returns the version of the grafana plugin
func (Plugin) Version() plugin.Version { return pluginVersion }

// SupportedProjectVersions returns an array with all project versions supported by the plugin
func (Plugin) SupportedProjectVersions() []config.Version { return supportedProjectVersions }

// GetInitSubcommand will return the subcommand which is responsible for initializing and scaffolding grafana manifests
func (p Plugin) GetInitSubcommand() plugin.InitSubcommand { return &p.initSubcommand }

// GetEditSubcommand will return the subcommand which is responsible for adding grafana manifests
func (p Plugin) GetEditSubcommand() plugin.EditSubcommand { return &p.editSubcommand }

type pluginConfig struct{}

func (p Plugin) DeprecationWarning() string {
return ""
}
145 changes: 145 additions & 0 deletions pkg/plugins/optional/helm/v1alpha/scaffolds/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package scaffolds

import (
"fmt"
"os"
"path/filepath"
"strings"

log "github.com/sirupsen/logrus"
"sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm/v1alpha/scaffolds/internal/templates"

"sigs.k8s.io/kubebuilder/v4/pkg/machinery"
"sigs.k8s.io/kubebuilder/v4/pkg/plugins"
)

var _ plugins.Scaffolder = &initScaffolder{}

type initScaffolder struct {
fs machinery.Filesystem
}

// NewInitScaffolder returns a new Scaffolder for project initialization operations
func NewInitScaffolder() plugins.Scaffolder {
return &initScaffolder{}
}

// InjectFS implements cmdutil.Scaffolder
func (s *initScaffolder) InjectFS(fs machinery.Filesystem) {
s.fs = fs
}

// Scaffold implements cmdutil.Scaffolder
func (s *initScaffolder) Scaffold() error {
log.Println("Generating Helm Chart to distribute project")

scaffold := machinery.NewScaffold(s.fs)

err := scaffold.Execute(
&templates.HelmChart{},
&templates.HelmValues{},
&templates.HelmIgnore{},
)
if err != nil {
return err
}

// Copy all relevant files from config/ to chart/templates/
err = copyConfigFiles()
if err != nil {
return err
}
return nil
}

// Helper function to copy files from config/ to dist/chart/templates/
func copyConfigFiles() error {
// Define the source directories and destination directories under dist/chart/templates
configDirs := []struct {
SrcDir string
DestDir string
SubDir string
}{
{"config/rbac", "dist/chart/templates/rbac", "rbac"},
{"config/manager", "dist/chart/templates/manager", "controllerManager"},
{"config/webhook", "dist/chart/templates/webhook", "webhook"},
{"config/crd/bases", "dist/chart/templates/crds", "crd"},
{"config/prometheus", "dist/chart/templates/prometheus", "prometheus"},
{"config/certmanager", "dist/chart/templates/certmanager", "certmanager"},
{"config/network-policy", "dist/chart/templates/network-policy", "networkPolicy"},
}

// Copy all YAML files in each directory
for _, dir := range configDirs {
files, err := filepath.Glob(filepath.Join(dir.SrcDir, "*.yaml"))
if err != nil {
return err
}

for _, srcFile := range files {
destFile := filepath.Join(dir.DestDir, filepath.Base(srcFile))
err := copyFileWithHelmLogic(srcFile, destFile, dir.SubDir)
if err != nil {
return err
}
}
}

return nil
}

// copyFileWithHelmLogic reads the source file, wraps it with Helm logic, and writes it to the destination
func copyFileWithHelmLogic(srcFile, destFile, subDir string) error {
// Ensure the source file exists
if _, err := os.Stat(srcFile); os.IsNotExist(err) {
log.Printf("Source file does not exist: %s", srcFile)
return err
}

// Read the source file
content, err := os.ReadFile(srcFile)
if err != nil {
log.Printf("Error reading source file: %s", srcFile)
return err
}

// Skip irrelevant files like kustomization.yaml
if strings.HasSuffix(srcFile, "kustomization.yaml") || strings.HasSuffix(srcFile, "kustomizeconfig.yaml") {
log.Printf("Skipping irrelevant file: %s", srcFile)
return nil
}

// Wrap the content with Helm conditional logic based on the subdirectory (e.g., "rbac", "manager")
wrappedContent := fmt.Sprintf("{{- if .Values.%s.create }}\n%s\n{{- end }}\n", subDir, string(content))

// Ensure the destination directory exists
if err := os.MkdirAll(filepath.Dir(destFile), os.ModePerm); err != nil {
return err
}

// Write the wrapped content to the destination file
err = os.WriteFile(destFile, []byte(wrappedContent), os.ModePerm)
if err != nil {
log.Printf("Error writing destination file: %s", destFile)
return err
}

log.Printf("Successfully copied %s to %s", srcFile, destFile)
return nil
}
Loading

0 comments on commit 07d3c66

Please sign in to comment.