Skip to content

Commit 950b2f3

Browse files
committed
#100 support filesystem as a source
1 parent 7701f30 commit 950b2f3

File tree

15 files changed

+419
-27
lines changed

15 files changed

+419
-27
lines changed

README.md

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,35 @@ Submit issue if some features missing for your use-case.
1919

2020
## Usage
2121

22-
1) From file: `cat my-app.yaml | helmify mychart`
23-
24-
Will create 'mychart' directory with Helm chart from yaml file with k8s objects.
22+
1) As pipe:
2523

24+
```shell
25+
cat my-app.yaml | helmify mychart
26+
```
27+
Will create 'mychart' directory with Helm chart from yaml file with k8s objects.
2628

27-
2) From directory with yamls:
2829
```shell
2930
awk 'FNR==1 && NR!=1 {print "---"}{print}' /<my_directory>/*.yaml | helmify mychart
3031
```
32+
Will create 'mychart' directory with Helm chart from all yaml files in `<my_directory> `directory.
33+
34+
2) From filesystem:
35+
```shell
36+
helmify -f /my_directory/my-app.yaml mychart
37+
```
38+
Will create 'mychart' directory with Helm chart from `my_directory/my-app.yaml`.
39+
```shell
40+
helmify -f /my_directory mychart
41+
```
3142
Will create 'mychart' directory with Helm chart from all yaml files in `<my_directory> `directory.
43+
```shell
44+
helmify -f /my_directory -r mychart
45+
```
46+
Will create 'mychart' directory with Helm chart from all yaml files in `<my_directory> `directory recursively.
47+
```shell
48+
helmify -f ./first_dir -f ./second_dir/my_deployment.yaml -f ./third_dir mychart
49+
```
50+
Will create 'mychart' directory with Helm chart from multiple directories and files.
3251

3352

3453
3) From [kustomize](https://kustomize.io/) output:
@@ -78,15 +97,17 @@ Usage:
7897

7998
```helmify [flags] CHART_NAME``` - `CHART_NAME` is optional. Default is 'chart'. Can be a directory, e.g. 'deploy/charts/mychart'.
8099

81-
| flag | description | sample |
82-
| --- | --- | --- |
83-
| -h -help | Prints help | `helmify -h`|
84-
| -v | Enable verbose output. Prints WARN and INFO. | `helmify -v`|
85-
| -vv | Enable very verbose output. Also prints DEBUG. | `helmify -vv`|
86-
| -version | Print helmify version. | `helmify -version`|
87-
| -crd-dir | Place crds in their own folder per Helm 3 [docs](https://helm.sh/docs/chart_best_practices/custom_resource_definitions/#method-1-let-helm-do-it-for-you). Caveat: CRDs templating is not supported by Helm. | `helmify -crd-dir`|
88-
| -image-pull-secrets| Allows the user to use existing secrets as imagePullSecrets | `helmify -image-pull-secrets`|
89-
| -cert-manager-as-subchart | Allows the user to install cert-manager as a subchart | `helmify -cert-manager-as-subchart`|
100+
| flag | description | sample |
101+
|---------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------|
102+
| -h -help | Prints help | `helmify -h` |
103+
| -f | File source for k8s manifests (directory or file), multiple sources supported | `helmify -f ./test_data` |
104+
| -r | Scan file directory recursively. Used only if -f provided | `helmify -f ./test_data -r` |
105+
| -v | Enable verbose output. Prints WARN and INFO. | `helmify -v` |
106+
| -vv | Enable very verbose output. Also prints DEBUG. | `helmify -vv` |
107+
| -version | Print helmify version. | `helmify -version` |
108+
| -crd-dir | Place crds in their own folder per Helm 3 [docs](https://helm.sh/docs/chart_best_practices/custom_resource_definitions/#method-1-let-helm-do-it-for-you). Caveat: CRDs templating is not supported by Helm. | `helmify -crd-dir` |
109+
| -image-pull-secrets | Allows the user to use existing secrets as imagePullSecrets | `helmify -image-pull-secrets` |
110+
| -cert-manager-as-subchart | Allows the user to install cert-manager as a subchart | `helmify -cert-manager-as-subchart` |
90111
## Status
91112
Supported k8s resources:
92113
- Deployment, DaemonSet, StatefulSet

cmd/helmify/flags.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"os"
77
"path/filepath"
8+
"strings"
89

910
"github.com/arttor/helmify/pkg/config"
1011
)
@@ -17,7 +18,16 @@ Example 1: 'kustomize build <kustomize_dir> | helmify mychart'
1718
Example 2: 'cat my-app.yaml | helmify mychart'
1819
- will create 'mychart' directory with Helm chart from yaml file.
1920
20-
Example 3: 'awk 'FNR==1 && NR!=1 {print "---"}{print}' /my_directory/*.yaml | helmify mychart'
21+
Example 3: 'helmify -f ./test_data/dir mychart'
22+
- will scan directory ./test_data/dir for files with k8s manifests and create 'mychart' directory with Helm chart.
23+
24+
Example 4: 'helmify -f ./test_data/dir -r mychart'
25+
- will scan directory ./test_data/dir recursively and create 'mychart' directory with Helm chart.
26+
27+
Example 5: 'helmify -f ./test_data/dir -f ./test_data/sample-app.yaml -f ./test_data/dir/another_dir mychart'
28+
- will scan provided multiple files and directories and create 'mychart' directory with Helm chart.
29+
30+
Example 6: 'awk 'FNR==1 && NR!=1 {print "---"}{print}' /my_directory/*.yaml | helmify mychart'
2131
- will create 'mychart' directory with Helm chart from all yaml files in my_directory directory.
2232
2333
Usage:
@@ -26,8 +36,23 @@ Usage:
2636
Flags:
2737
`
2838

39+
type arrayFlags []string
40+
41+
func (i *arrayFlags) String() string {
42+
if i == nil || len(*i) == 0 {
43+
return ""
44+
}
45+
return strings.Join(*i, ", ")
46+
}
47+
48+
func (i *arrayFlags) Set(value string) error {
49+
*i = append(*i, value)
50+
return nil
51+
}
52+
2953
// ReadFlags command-line flags into app config.
3054
func ReadFlags() config.Config {
55+
files := arrayFlags{}
3156
result := config.Config{}
3257
var h, help, version, crd bool
3358
flag.BoolVar(&h, "h", false, "Print help. Example: helmify -h")
@@ -39,6 +64,8 @@ func ReadFlags() config.Config {
3964
flag.BoolVar(&result.ImagePullSecrets, "image-pull-secrets", false, "Allows the user to use existing secrets as imagePullSecrets in values.yaml")
4065
flag.BoolVar(&result.GenerateDefaults, "generate-defaults", false, "Allows the user to add empty placeholders for tipical customization options in values.yaml. Currently covers: topology constraints, node selectors, tolerances")
4166
flag.BoolVar(&result.CertManagerAsSubchart, "cert-manager-as-subchart", false, "Allows the user to add cert-manager as a subchart")
67+
flag.BoolVar(&result.FilesRecursively, "r", false, "Scan dirs from -f option recursively")
68+
flag.Var(&files, "f", "File or directory containing k8s manifests")
4269

4370
flag.Parse()
4471
if h || help {
@@ -58,5 +85,6 @@ func ReadFlags() config.Config {
5885
if crd {
5986
result.Crd = crd
6087
}
88+
result.Files = files
6189
return result
6290
}

cmd/helmify/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ func main() {
1414
logrus.WithError(err).Error("stdin error")
1515
os.Exit(1)
1616
}
17-
if (stat.Mode() & os.ModeCharDevice) != 0 {
17+
if len(conf.Files) == 0 && (stat.Mode()&os.ModeCharDevice) != 0 {
1818
logrus.Error("no data piped in stdin")
1919
os.Exit(1)
2020
}

pkg/app/app.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package app
22

33
import (
44
"context"
5+
"github.com/arttor/helmify/pkg/file"
56
"github.com/arttor/helmify/pkg/processor/job"
67
"github.com/arttor/helmify/pkg/processor/statefulset"
78
"io"
@@ -27,7 +28,7 @@ import (
2728
)
2829

2930
// Start - application entrypoint for processing input to a Helm chart.
30-
func Start(input io.Reader, config config.Config) error {
31+
func Start(stdin io.Reader, config config.Config) error {
3132
err := config.Validate()
3233
if err != nil {
3334
return err
@@ -42,7 +43,6 @@ func Start(input io.Reader, config config.Config) error {
4243
logrus.Debug("Received termination, signaling shutdown")
4344
cancelFunc()
4445
}()
45-
objects := decoder.Decode(ctx.Done(), input)
4646
appCtx := New(config, helm.NewOutput())
4747
appCtx = appCtx.WithProcessors(
4848
configmap.New(),
@@ -65,9 +65,20 @@ func Start(input io.Reader, config config.Config) error {
6565
job.NewCron(),
6666
job.NewJob(),
6767
).WithDefaultProcessor(processor.Default())
68-
for obj := range objects {
69-
appCtx.Add(obj)
68+
if len(config.Files) != 0 {
69+
file.Walk(config.Files, config.FilesRecursively, func(filename string, fileReader io.Reader) {
70+
objects := decoder.Decode(ctx.Done(), fileReader)
71+
for obj := range objects {
72+
appCtx.Add(obj, filename)
73+
}
74+
})
75+
} else {
76+
objects := decoder.Decode(ctx.Done(), stdin)
77+
for obj := range objects {
78+
appCtx.Add(obj, "")
79+
}
7080
}
81+
7182
return appCtx.CreateHelm(ctx.Done())
7283
}
7384

pkg/app/context.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type appContext struct {
1616
config config.Config
1717
appMeta *metadata.Service
1818
objects []*unstructured.Unstructured
19+
fileNames []string
1920
}
2021

2122
// New returns context with config set.
@@ -40,10 +41,11 @@ func (c *appContext) WithDefaultProcessor(processor helmify.Processor) *appConte
4041
}
4142

4243
// Add k8s object to app context.
43-
func (c *appContext) Add(obj *unstructured.Unstructured) {
44+
func (c *appContext) Add(obj *unstructured.Unstructured, filename string) {
4445
// we need to add all objects before start processing only to define app metadata.
4546
c.appMeta.Load(obj)
4647
c.objects = append(c.objects, obj)
48+
c.fileNames = append(c.fileNames, filename)
4749
}
4850

4951
// CreateHelm creates helm chart from context k8s objects.
@@ -53,21 +55,27 @@ func (c *appContext) CreateHelm(stop <-chan struct{}) error {
5355
"Namespace": c.appMeta.Namespace(),
5456
}).Info("creating a chart")
5557
var templates []helmify.Template
56-
for _, obj := range c.objects {
58+
var filenames []string
59+
for i, obj := range c.objects {
5760
template, err := c.process(obj)
5861
if err != nil {
5962
return err
6063
}
6164
if template != nil {
6265
templates = append(templates, template)
66+
filename := template.Filename()
67+
if c.fileNames[i] != "" {
68+
filename = c.fileNames[i]
69+
}
70+
filenames = append(filenames, filename)
6371
}
6472
select {
6573
case <-stop:
6674
return nil
6775
default:
6876
}
6977
}
70-
return c.output.Create(c.config.ChartDir, c.config.ChartName, c.config.Crd, c.config.CertManagerAsSubchart, templates)
78+
return c.output.Create(c.config.ChartDir, c.config.ChartName, c.config.Crd, c.config.CertManagerAsSubchart, templates, filenames)
7179
}
7280

7381
func (c *appContext) process(obj *unstructured.Unstructured) (helmify.Template, error) {

pkg/config/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ type Config struct {
2828
GenerateDefaults bool
2929
// CertManagerAsSubchart enables the generation of a subchart for cert-manager
3030
CertManagerAsSubchart bool
31+
// Files - directories or files with k8s manifests
32+
Files []string
33+
// FilesRecursively read Files recursively
34+
FilesRecursively bool
3135
}
3236

3337
func (c *Config) Validate() error {

pkg/file/reader.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package file
2+
3+
import (
4+
"github.com/sirupsen/logrus"
5+
"io"
6+
"io/fs"
7+
"os"
8+
"path/filepath"
9+
)
10+
11+
func Walk(paths []string, recursively bool, walkFunc func(filename string, r io.Reader)) {
12+
13+
for _, path := range paths {
14+
info, err := os.Stat(path)
15+
if err != nil {
16+
logrus.Warnf("no such file or directory %q: %v", path, err)
17+
continue
18+
}
19+
// handle single file file:
20+
if !info.IsDir() {
21+
file, err := os.Open(path)
22+
if err != nil {
23+
logrus.Warnf("unable to open file %q: %v", file.Name(), err)
24+
continue
25+
}
26+
walkFunc(info.Name(), file)
27+
err = file.Close()
28+
if err != nil {
29+
logrus.Warnf("unable to close file %q: %v", file.Name(), err)
30+
}
31+
continue
32+
}
33+
// handle directory non-recursively:
34+
if !recursively {
35+
dir, err := os.Open(path)
36+
if err != nil {
37+
logrus.Warnf("unable to open directory %q: %v", dir.Name(), err)
38+
continue
39+
}
40+
files, err := dir.ReadDir(0)
41+
if err != nil {
42+
logrus.Warnf("unable to read directory %q: %v", dir.Name(), err)
43+
continue
44+
}
45+
for _, f := range files {
46+
if f.IsDir() {
47+
continue
48+
}
49+
file, err := os.Open(filepath.Join(path, f.Name()))
50+
if err != nil {
51+
logrus.Warnf("unable to open file %q: %v", file.Name(), err)
52+
continue
53+
}
54+
walkFunc(f.Name(), file)
55+
err = file.Close()
56+
if err != nil {
57+
logrus.Warnf("unable to close file %q: %v", file.Name(), err)
58+
}
59+
continue
60+
}
61+
continue
62+
}
63+
// handle directory recursively:
64+
err = filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
65+
if err != nil {
66+
return err
67+
}
68+
if d.IsDir() {
69+
return nil
70+
}
71+
file, err := os.Open(path)
72+
if err != nil {
73+
return err
74+
}
75+
walkFunc(d.Name(), file)
76+
err = file.Close()
77+
if err != nil {
78+
logrus.Warnf("unable to close file %q: %v", file.Name(), err)
79+
}
80+
return nil
81+
})
82+
if err != nil {
83+
logrus.Warnf("unable to open %q: %v", info.Name(), err)
84+
continue
85+
}
86+
}
87+
}

pkg/helm/chart.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ type output struct{}
3131
// └── _helpers.tp # Helm default template partials
3232
//
3333
// Overwrites existing values.yaml and templates in templates dir on every run.
34-
func (o output) Create(chartDir, chartName string, crd bool, certManagerAsSubchart bool, templates []helmify.Template) error {
34+
func (o output) Create(chartDir, chartName string, crd bool, certManagerAsSubchart bool, templates []helmify.Template, filenames []string) error {
3535
err := initChartDir(chartDir, chartName, crd, certManagerAsSubchart)
3636
if err != nil {
3737
return err
@@ -40,10 +40,10 @@ func (o output) Create(chartDir, chartName string, crd bool, certManagerAsSubcha
4040
files := map[string][]helmify.Template{}
4141
values := helmify.Values{}
4242
values[cluster.DomainKey] = cluster.DefaultDomain
43-
for _, template := range templates {
44-
file := files[template.Filename()]
43+
for i, template := range templates {
44+
file := files[filenames[i]]
4545
file = append(file, template)
46-
files[template.Filename()] = file
46+
files[filenames[i]] = file
4747
err = values.Merge(template.Values())
4848
if err != nil {
4949
return err

pkg/helmify/model.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type Template interface {
2828

2929
// Output - converts Template into helm chart on disk.
3030
type Output interface {
31-
Create(chartName, chartDir string, Crd bool, certManagerAsSubchart bool, templates []Template) error
31+
Create(chartName, chartDir string, Crd bool, certManagerAsSubchart bool, templates []Template, filenames []string) error
3232
}
3333

3434
// AppMetadata handle common information about K8s objects in the chart.

0 commit comments

Comments
 (0)