-
Notifications
You must be signed in to change notification settings - Fork 169
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(cli): Charttool #367
Merged
feat(cli): Charttool #367
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1 @@ | ||
dist | ||
tk | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/go-clix/cli" | ||
"github.com/grafana/tanka/pkg/helm" | ||
"gopkg.in/yaml.v2" | ||
) | ||
|
||
func chartsCmd() *cli.Command { | ||
cmd := &cli.Command{ | ||
Use: "charts", | ||
Short: "Declarative vendoring of Helm Charts", | ||
} | ||
|
||
cmd.AddCommand( | ||
chartsInitCmd(), | ||
chartsAddCmd(), | ||
chartsVendorCmd(), | ||
chartsConfigCmd(), | ||
) | ||
|
||
return cmd | ||
} | ||
|
||
func chartsVendorCmd() *cli.Command { | ||
cmd := &cli.Command{ | ||
Use: "vendor", | ||
Short: "Download Charts to a local folder", | ||
} | ||
|
||
cmd.Run = func(cmd *cli.Command, args []string) error { | ||
c, err := loadChartfile() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return c.Vendor() | ||
} | ||
|
||
return cmd | ||
} | ||
|
||
func chartsAddCmd() *cli.Command { | ||
cmd := &cli.Command{ | ||
Use: "add [chart@version] [...]", | ||
Short: "Adds Charts to the chartfile", | ||
} | ||
|
||
cmd.Run = func(cmd *cli.Command, args []string) error { | ||
c, err := loadChartfile() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return c.Add(args) | ||
} | ||
|
||
return cmd | ||
} | ||
|
||
func chartsConfigCmd() *cli.Command { | ||
cmd := &cli.Command{ | ||
Use: "config", | ||
Short: "Displays the current manifest", | ||
} | ||
|
||
cmd.Run = func(cmd *cli.Command, args []string) error { | ||
c, err := loadChartfile() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
data, err := yaml.Marshal(c.Manifest) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fmt.Print(string(data)) | ||
|
||
return nil | ||
} | ||
|
||
return cmd | ||
} | ||
|
||
func chartsInitCmd() *cli.Command { | ||
cmd := &cli.Command{ | ||
Use: "init", | ||
Short: "Create a new Chartfile", | ||
} | ||
|
||
cmd.Run = func(cmd *cli.Command, args []string) error { | ||
wd, err := os.Getwd() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
path := filepath.Join(wd, helm.Filename) | ||
if _, err := os.Stat(path); err == nil { | ||
return fmt.Errorf("Chartfile at '%s' already exists. Aborting", path) | ||
} | ||
|
||
if _, err := helm.InitChartfile(path); err != nil { | ||
return err | ||
} | ||
|
||
log.Printf("Success! New Chartfile created at '%s'", path) | ||
return nil | ||
} | ||
|
||
return cmd | ||
} | ||
|
||
func loadChartfile() (*helm.Charts, error) { | ||
wd, err := os.Getwd() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return helm.LoadChartfile(wd) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
package helm | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io/ioutil" | ||
"log" | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
"strings" | ||
|
||
"github.com/Masterminds/semver" | ||
"sigs.k8s.io/yaml" | ||
) | ||
|
||
// LoadChartfile opens a Chartfile tree | ||
func LoadChartfile(projectRoot string) (*Charts, error) { | ||
// make sure project root is valid | ||
abs, err := filepath.Abs(projectRoot) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// open chartfile | ||
chartfile := filepath.Join(abs, Filename) | ||
data, err := ioutil.ReadFile(chartfile) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// parse it | ||
c := Chartfile{ | ||
Version: Version, | ||
Directory: DefaultDir, | ||
} | ||
if err := yaml.UnmarshalStrict(data, &c); err != nil { | ||
return nil, err | ||
} | ||
|
||
for i, r := range c.Requires { | ||
if r.Chart == "" { | ||
return nil, fmt.Errorf("requirements[%v]: 'chart' must be set", i) | ||
} | ||
} | ||
|
||
// return Charts handle | ||
charts := &Charts{ | ||
Manifest: c, | ||
projectRoot: abs, | ||
|
||
// default to ExecHelm, but allow injecting from the outside | ||
Helm: ExecHelm{}, | ||
} | ||
return charts, nil | ||
} | ||
|
||
// Charts exposes the central Chartfile management functions | ||
type Charts struct { | ||
// Manifest are the chartfile.yaml contents. It holds data about the developers intentions | ||
Manifest Chartfile | ||
|
||
// projectRoot is the enclosing directory of chartfile.yaml | ||
projectRoot string | ||
|
||
// Helm is the helm implementation underneath. ExecHelm is the default, but | ||
// any implementation of the Helm interface may be used | ||
Helm Helm | ||
} | ||
|
||
// ChartDir returns the directory pulled charts are saved in | ||
func (c Charts) ChartDir() string { | ||
return filepath.Join(c.projectRoot, c.Manifest.Directory) | ||
} | ||
|
||
// ManifestFile returns the full path to the chartfile.yaml | ||
func (c Charts) ManifestFile() string { | ||
return filepath.Join(c.projectRoot, Filename) | ||
} | ||
|
||
// Vendor pulls all Charts specified in the manifest into the local charts | ||
// directory. It fetches the repository index before doing so. | ||
func (c Charts) Vendor() error { | ||
dir := c.ChartDir() | ||
if err := os.MkdirAll(dir, os.ModePerm); err != nil { | ||
return err | ||
} | ||
|
||
log.Println("Syncing Repositories ...") | ||
if err := c.Helm.RepoUpdate(Opts{Repositories: c.Manifest.Repositories}); err != nil { | ||
return err | ||
} | ||
|
||
log.Println("Pulling Charts ...") | ||
for _, r := range c.Manifest.Requires { | ||
err := c.Helm.Pull(r.Chart, r.Version.String(), PullOpts{ | ||
Destination: dir, | ||
Opts: Opts{Repositories: c.Manifest.Repositories}, | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
log.Printf(" %s@%s", r.Chart, r.Version.String()) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Add adds every Chart in reqs to the Manifest after validation, and runs | ||
// Vendor afterwards | ||
func (c Charts) Add(reqs []string) error { | ||
log.Printf("Adding %v Charts ...", len(reqs)) | ||
sh0rez marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
skip := func(s string, err error) { | ||
log.Printf(" Skipping %s: %s.", s, err) | ||
} | ||
|
||
// parse new charts, append in memory | ||
added := 0 | ||
for _, s := range reqs { | ||
r, err := parseReq(s) | ||
if err != nil { | ||
skip(s, err) | ||
continue | ||
} | ||
|
||
if c.Manifest.Requires.Has(*r) { | ||
skip(s, fmt.Errorf("already exists")) | ||
continue | ||
} | ||
|
||
c.Manifest.Requires = append(c.Manifest.Requires, *r) | ||
added++ | ||
log.Println(" OK:", s) | ||
} | ||
|
||
// write out | ||
if err := write(c.Manifest, c.ManifestFile()); err != nil { | ||
return err | ||
} | ||
|
||
// skipped some? fail then | ||
if added != len(reqs) { | ||
return fmt.Errorf("%v Charts were skipped. Please check above logs for details", len(reqs)-added) | ||
} | ||
|
||
// worked fine? vendor it | ||
log.Printf("Added %v Charts to helmfile.yaml. Vendoring ...", added) | ||
return c.Vendor() | ||
} | ||
|
||
func InitChartfile(path string) (*Charts, error) { | ||
c := Chartfile{ | ||
Version: Version, | ||
Repositories: []Repo{{ | ||
Name: "stable", | ||
URL: "https://kubernetes-charts.storage.googleapis.com", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we want this? Being deprecated soon? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently it's the primary source of truth. Soon is not yet deprecated :) We will remove as soon as deprecated |
||
}}, | ||
Requires: make(Requirements, 0), | ||
} | ||
|
||
if err := write(c, path); err != nil { | ||
return nil, err | ||
} | ||
|
||
return LoadChartfile(filepath.Dir(path)) | ||
} | ||
|
||
// write saves a Chartfile to dest | ||
func write(c Chartfile, dest string) error { | ||
data, err := yaml.Marshal(c) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return ioutil.WriteFile(dest, data, 0644) | ||
} | ||
|
||
var chartExp = regexp.MustCompile(`\w+\/\w+@.+`) | ||
|
||
// parseReq parses a requirement from a string of the format `repo/name@version` | ||
func parseReq(s string) (*Requirement, error) { | ||
if !chartExp.MatchString(s) { | ||
return nil, fmt.Errorf("not of form 'repo/chart@version'") | ||
} | ||
|
||
elems := strings.Split(s, "@") | ||
chart := elems[0] | ||
ver, err := semver.NewVersion(elems[1]) | ||
if errors.Is(err, semver.ErrInvalidSemVer) { | ||
return nil, fmt.Errorf("version is invalid") | ||
} else if err != nil { | ||
return nil, fmt.Errorf("version is invalid: %s", err) | ||
} | ||
|
||
return &Requirement{ | ||
Chart: chart, | ||
Version: *ver, | ||
}, nil | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could we colourise this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not easily. The only yaml highlighter library I am aware of (alecthomas/chroma) doesn't handle yaml well, especially not on light schemed terminals.
We actually removed it from the project for that exact reason some time ago (were coloring diff using it)
We could look into writing a custom yaml highlighter some day