add extra chart command output

Signed-off-by: Josh Dolitsky <jdolitsky@gmail.com>
pull/5243/head
Josh Dolitsky 7 years ago
parent acdd17fb4b
commit 084476b2c5

@ -16,6 +16,8 @@ limitations under the License.
package main
import (
"io"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@ -30,19 +32,19 @@ Example usage:
$ helm chart pull [URL]
`
func newChartCmd(cfg *action.Configuration) *cobra.Command {
func newChartCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "chart",
Short: "push, pull, tag, or remove Helm charts",
Long: chartHelp,
}
cmd.AddCommand(
newChartListCmd(cfg),
newChartExportCmd(cfg),
newChartPullCmd(cfg),
newChartPushCmd(cfg),
newChartRemoveCmd(cfg),
newChartSaveCmd(cfg),
newChartListCmd(cfg, out),
newChartExportCmd(cfg, out),
newChartPullCmd(cfg, out),
newChartPushCmd(cfg, out),
newChartRemoveCmd(cfg, out),
newChartSaveCmd(cfg, out),
)
return cmd
}

@ -17,6 +17,8 @@ limitations under the License.
package main
import (
"io"
"github.com/spf13/cobra"
"k8s.io/helm/cmd/helm/require"
@ -27,7 +29,7 @@ const chartExportDesc = `
TODO
`
func newChartExportCmd(cfg *action.Configuration) *cobra.Command {
func newChartExportCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return &cobra.Command{
Use: "export [ref]",
Short: "export a chart to directory",
@ -35,7 +37,7 @@ func newChartExportCmd(cfg *action.Configuration) *cobra.Command {
Args: require.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ref := args[0]
return action.NewChartExport(cfg).Run(ref)
return action.NewChartExport(cfg).Run(out, ref)
},
}
}

@ -17,6 +17,8 @@ limitations under the License.
package main
import (
"io"
"github.com/spf13/cobra"
"k8s.io/helm/pkg/action"
@ -26,14 +28,14 @@ const chartListDesc = `
TODO
`
func newChartListCmd(cfg *action.Configuration) *cobra.Command {
func newChartListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return &cobra.Command{
Use: "list",
Aliases: []string{"ls"},
Short: "list all saved charts",
Long: chartListDesc,
RunE: func(cmd *cobra.Command, args []string) error {
return action.NewChartList(cfg).Run()
return action.NewChartList(cfg).Run(out)
},
}
}

@ -17,6 +17,8 @@ limitations under the License.
package main
import (
"io"
"github.com/spf13/cobra"
"k8s.io/helm/cmd/helm/require"
@ -27,7 +29,7 @@ const chartPullDesc = `
TODO
`
func newChartPullCmd(cfg *action.Configuration) *cobra.Command {
func newChartPullCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return &cobra.Command{
Use: "pull [ref]",
Short: "pull a chart from remote",
@ -35,7 +37,7 @@ func newChartPullCmd(cfg *action.Configuration) *cobra.Command {
Args: require.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ref := args[0]
return action.NewChartPull(cfg).Run(ref)
return action.NewChartPull(cfg).Run(out, ref)
},
}
}

@ -17,6 +17,8 @@ limitations under the License.
package main
import (
"io"
"github.com/spf13/cobra"
"k8s.io/helm/cmd/helm/require"
@ -27,7 +29,7 @@ const chartPushDesc = `
TODO
`
func newChartPushCmd(cfg *action.Configuration) *cobra.Command {
func newChartPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return &cobra.Command{
Use: "push [ref]",
Short: "push a chart to remote",
@ -35,7 +37,7 @@ func newChartPushCmd(cfg *action.Configuration) *cobra.Command {
Args: require.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ref := args[0]
return action.NewChartPush(cfg).Run(ref)
return action.NewChartPush(cfg).Run(out, ref)
},
}
}

@ -17,6 +17,8 @@ limitations under the License.
package main
import (
"io"
"github.com/spf13/cobra"
"k8s.io/helm/cmd/helm/require"
@ -27,7 +29,7 @@ const chartRemoveDesc = `
TODO
`
func newChartRemoveCmd(cfg *action.Configuration) *cobra.Command {
func newChartRemoveCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return &cobra.Command{
Use: "remove [ref]",
Aliases: []string{"rm"},
@ -36,7 +38,7 @@ func newChartRemoveCmd(cfg *action.Configuration) *cobra.Command {
Args: require.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ref := args[0]
return action.NewChartRemove(cfg).Run(ref)
return action.NewChartRemove(cfg).Run(out, ref)
},
}
}

@ -17,6 +17,8 @@ limitations under the License.
package main
import (
"io"
"github.com/spf13/cobra"
"k8s.io/helm/cmd/helm/require"
@ -27,7 +29,7 @@ const chartSaveDesc = `
TODO
`
func newChartSaveCmd(cfg *action.Configuration) *cobra.Command {
func newChartSaveCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return &cobra.Command{
Use: "save [path] [ref]",
Short: "save a chart directory",
@ -36,7 +38,7 @@ func newChartSaveCmd(cfg *action.Configuration) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
path := args[0]
ref := args[1]
return action.NewChartSave(cfg).Run(path, ref)
return action.NewChartSave(cfg).Run(out, path, ref)
},
}
}

@ -89,7 +89,7 @@ func newRootCmd(c helm.Interface, actionConfig *action.Configuration, out io.Wri
newRepoCmd(out),
newSearchCmd(out),
newVerifyCmd(out),
newChartCmd(actionConfig),
newChartCmd(actionConfig, out),
// release commands
newGetCmd(c, out),

@ -17,6 +17,8 @@ limitations under the License.
package action
import (
"fmt"
"io"
"io/ioutil"
"os"
@ -37,7 +39,7 @@ func NewChartExport(cfg *Configuration) *ChartExport {
}
// Run executes the chart export operation
func (a *ChartExport) Run(ref string) error {
func (a *ChartExport) Run(out io.Writer, ref string) error {
r, err := registry.ParseReference(ref)
if err != nil {
return err
@ -66,5 +68,6 @@ func (a *ChartExport) Run(ref string) error {
return err
}
fmt.Fprintf(out, "Exported to %s/\n", ch.Metadata.Name)
return nil
}

@ -16,6 +16,10 @@ limitations under the License.
package action
import (
"io"
)
// ChartList performs a chart list operation.
type ChartList struct {
cfg *Configuration
@ -29,6 +33,6 @@ func NewChartList(cfg *Configuration) *ChartList {
}
// Run executes the chart list operation
func (a *ChartList) Run() error {
func (a *ChartList) Run(out io.Writer) error {
return a.cfg.RegistryClient.PrintChartTable()
}

@ -17,6 +17,8 @@ limitations under the License.
package action
import (
"io"
"k8s.io/helm/pkg/registry"
)
@ -33,7 +35,7 @@ func NewChartPull(cfg *Configuration) *ChartPull {
}
// Run executes the chart pull operation
func (a *ChartPull) Run(ref string) error {
func (a *ChartPull) Run(out io.Writer, ref string) error {
r, err := registry.ParseReference(ref)
if err != nil {
return err

@ -17,6 +17,8 @@ limitations under the License.
package action
import (
"io"
"k8s.io/helm/pkg/registry"
)
@ -33,7 +35,7 @@ func NewChartPush(cfg *Configuration) *ChartPush {
}
// Run executes the chart push operation
func (a *ChartPush) Run(ref string) error {
func (a *ChartPush) Run(out io.Writer, ref string) error {
r, err := registry.ParseReference(ref)
if err != nil {
return err

@ -17,6 +17,8 @@ limitations under the License.
package action
import (
"io"
"k8s.io/helm/pkg/registry"
)
@ -33,7 +35,7 @@ func NewChartRemove(cfg *Configuration) *ChartRemove {
}
// Run executes the chart remove operation
func (a *ChartRemove) Run(ref string) error {
func (a *ChartRemove) Run(out io.Writer, ref string) error {
r, err := registry.ParseReference(ref)
if err != nil {
return err

@ -17,6 +17,7 @@ limitations under the License.
package action
import (
"io"
"path/filepath"
"k8s.io/helm/pkg/chart/loader"
@ -36,7 +37,7 @@ func NewChartSave(cfg *Configuration) *ChartSave {
}
// Run executes the chart save operation
func (a *ChartSave) Run(path string, ref string) error {
func (a *ChartSave) Run(out io.Writer, path string, ref string) error {
path, err := filepath.Abs(path)
if err != nil {
return err

@ -153,65 +153,83 @@ func (cache *filesystemCache) LoadReference(ref *Reference) ([]ocispec.Descripto
return nil, err
}
printChartSummary(cache.out, metaLayer, contentLayer)
layers := []ocispec.Descriptor{metaLayer, contentLayer}
return layers, nil
}
func (cache *filesystemCache) StoreReference(ref *Reference, layers []ocispec.Descriptor) error {
tagDir := mkdir(filepath.Join(cache.rootDir, "refs", ref.Locator, "tags", tagOrDefault(ref.Object)))
func (cache *filesystemCache) StoreReference(ref *Reference, layers []ocispec.Descriptor) (bool, error) {
tag := tagOrDefault(ref.Object)
tagDir := mkdir(filepath.Join(cache.rootDir, "refs", ref.Locator, "tags", tag))
// Retrieve just the meta and content layers
metaLayer, contentLayer, err := extractLayers(layers)
if err != nil {
return err
return false, err
}
// Extract chart name and version
name, version, err := extractChartNameVersionFromLayer(contentLayer)
if err != nil {
return err
return false, err
}
// Create chart file
chartPath, err := createChartFile(filepath.Join(cache.rootDir, "charts"), name, version)
if err != nil {
return err
return false, err
}
// Create chart symlink
err = createSymlink(chartPath, filepath.Join(tagDir, "chart"))
if err != nil {
return err
return false, err
}
// Save meta blob
metaExists, metaPath := digestPath(filepath.Join(cache.rootDir, "blobs", "meta"), metaLayer.Digest)
if !metaExists {
fmt.Fprintf(cache.out, "%s: Saving meta (%s)\n",
shortDigest(metaLayer.Digest.Hex()), byteCountBinary(metaLayer.Size))
_, metaJSONRaw, ok := cache.store.Get(metaLayer)
if !ok {
return errors.New("error retrieving meta layer")
return false, errors.New("error retrieving meta layer")
}
metaPath, err := createDigestFile(filepath.Join(cache.rootDir, "blobs", "meta"), metaJSONRaw)
err = writeFile(metaPath, metaJSONRaw)
if err != nil {
return err
return false, err
}
}
// Create meta symlink
err = createSymlink(metaPath, filepath.Join(tagDir, "meta"))
if err != nil {
return err
return false, err
}
// Save content blob
contentExists, contentPath := digestPath(filepath.Join(cache.rootDir, "blobs", "content"), contentLayer.Digest)
if !contentExists {
fmt.Fprintf(cache.out, "%s: Saving content (%s)\n",
shortDigest(contentLayer.Digest.Hex()), byteCountBinary(contentLayer.Size))
_, contentRaw, ok := cache.store.Get(contentLayer)
if !ok {
return errors.New("error retrieving content layer")
return false, errors.New("error retrieving content layer")
}
contentPath, err := createDigestFile(filepath.Join(cache.rootDir, "blobs", "content"), contentRaw)
err = writeFile(contentPath, contentRaw)
if err != nil {
return err
return false, err
}
}
// Create content symlink
return createSymlink(contentPath, filepath.Join(tagDir, "content"))
err = createSymlink(contentPath, filepath.Join(tagDir, "content"))
if err != nil {
return false, err
}
printChartSummary(cache.out, metaLayer, contentLayer)
return metaExists && contentExists, nil
}
func (cache *filesystemCache) DeleteReference(ref *Reference) error {
@ -226,6 +244,22 @@ func (cache *filesystemCache) TableRows() ([][]string, error) {
return getRefsSorted(filepath.Join(cache.rootDir, "refs"))
}
// printChartSummary prints details about a chart layers
func printChartSummary(out io.Writer, metaLayer ocispec.Descriptor, contentLayer ocispec.Descriptor) {
fmt.Fprintf(out, "Name: %s\n", contentLayer.Annotations[HelmChartNameAnnotation])
fmt.Fprintf(out, "Version: %s\n", contentLayer.Annotations[HelmChartVersionAnnotation])
fmt.Fprintf(out, "Meta: %s\n", metaLayer.Digest)
fmt.Fprintf(out, "Content: %s\n", contentLayer.Digest)
}
// fileExists determines if a file exists
func fileExists(path string) bool {
if _, err := os.Stat(path); os.IsNotExist(err) {
return false
}
return true
}
// mkdir will create a directory (no error check) and return the path
func mkdir(dir string) string {
os.MkdirAll(dir, 0755)
@ -318,20 +352,18 @@ func createChartFile(chartsRootDir string, name string, version string) (string,
return chartPath, nil
}
// createDigestFile calcultaes the sha256 digest of some content and creates a file, returning the path
func createDigestFile(rootDir string, c []byte) (string, error) {
digest := checksum.FromBytes(c).String()
digestLeft, digestRight := splitDigest(digest)
pathDir := filepath.Join(rootDir, "sha256", digestLeft)
path := filepath.Join(pathDir, digestRight)
if _, err := os.Stat(path); err != nil && os.IsNotExist(err) {
os.MkdirAll(pathDir, 0755)
err := ioutil.WriteFile(path, c, 0644)
if err != nil {
return "", err
// digestPath returns the path to addressable content, and whether the file exists
func digestPath(rootDir string, digest checksum.Digest) (bool, string) {
digestLeft, digestRight := splitDigest(digest.Hex())
path := filepath.Join(rootDir, "sha256", digestLeft, digestRight)
exists := fileExists(path)
return exists, path
}
}
return path, nil
// writeFile creates a path, ensuring parent directory
func writeFile(path string, c []byte) error {
os.MkdirAll(filepath.Dir(path), 0755)
return ioutil.WriteFile(path, c, 0644)
}
// splitDigest returns a sha256 digest in two parts, on with first 2 chars and one with second 62 chars
@ -367,6 +399,14 @@ func tagOrDefault(tag string) string {
return HelmChartDefaultTag
}
// shortDigest returns first 7 characters of a sha256 digest
func shortDigest(digest string) string {
if len(digest) == 64 {
return digest[:7]
}
return digest
}
// getRefsSorted returns a map of all refs stored in a refsRootDir
func getRefsSorted(refsRootDir string) ([][]string, error) {
refsMap := map[string]map[string]string{}
@ -404,7 +444,7 @@ func getRefsSorted(refsRootDir string) ([][]string, error) {
// Make sure the filename looks like a sha256 digest (64 chars)
if len(digest) == 64 {
refsMap[ref]["digest"] = digest[:7]
refsMap[ref]["digest"] = shortDigest(digest)
refsMap[ref]["size"] = byteCountBinary(destFileInfo.Size())
refsMap[ref]["created"] = units.HumanDuration(time.Now().UTC().Sub(destFileInfo.ModTime()))
}

@ -59,36 +59,63 @@ func NewClient(options *ClientOptions) *Client {
// PushChart uploads a chart to a registry
func (c *Client) PushChart(ref *Reference) error {
c.setDefaultTag(ref)
fmt.Fprintf(c.out, "The push refers to repository [%s]\n", ref.Locator)
layers, err := c.cache.LoadReference(ref)
if err != nil {
return err
}
err = oras.Push(context.Background(), c.resolver, ref.String(), c.cache.store, layers)
if err != nil {
return err
}
var totalSize int64
for _, layer := range layers {
totalSize += layer.Size
}
fmt.Fprintf(c.out,
"%s: pushed to remote (%d layers, %s total)\n", ref.Object, len(layers), byteCountBinary(totalSize))
return nil
}
// PullChart downloads a chart from a registry
func (c *Client) PullChart(ref *Reference) error {
c.setDefaultTag(ref)
fmt.Fprintf(c.out, "%s: Pulling from %s\n", ref.Object, ref.Locator)
layers, err := oras.Pull(context.Background(), c.resolver, ref.String(), c.cache.store, KnownMediaTypes()...)
if err != nil {
return err
}
err = c.cache.StoreReference(ref, layers)
exists, err := c.cache.StoreReference(ref, layers)
if err != nil {
return err
}
if !exists {
fmt.Fprintf(c.out, "Status: Downloaded newer chart for %s:%s\n", ref.Locator, ref.Object)
} else {
fmt.Fprintf(c.out, "Status: Chart is up to date for %s:%s\n", ref.Locator, ref.Object)
}
return nil
}
// SaveChart stores a copy of chart in local cache
func (c *Client) SaveChart(ch *chart.Chart, ref *Reference) error {
c.setDefaultTag(ref)
layers, err := c.cache.ChartToLayers(ch)
if err != nil {
return err
}
err = c.cache.StoreReference(ref, layers)
_, err = c.cache.StoreReference(ref, layers)
if err != nil {
return err
}
fmt.Fprintf(c.out, "%s: saved\n", ref.Object)
return nil
}
// LoadChart retrieves a chart object by reference
func (c *Client) LoadChart(ref *Reference) (*chart.Chart, error) {
c.setDefaultTag(ref)
layers, err := c.cache.LoadReference(ref)
if err != nil {
return nil, err
@ -99,7 +126,12 @@ func (c *Client) LoadChart(ref *Reference) (*chart.Chart, error) {
// RemoveChart deletes a locally saved chart
func (c *Client) RemoveChart(ref *Reference) error {
c.setDefaultTag(ref)
err := c.cache.DeleteReference(ref)
if err != nil {
return err
}
fmt.Fprintf(c.out, "%s: removed\n", ref.Object)
return err
}
@ -120,3 +152,10 @@ func (c *Client) PrintChartTable() error {
fmt.Fprintln(c.out, table.String())
return nil
}
func (c *Client) setDefaultTag(ref *Reference) {
if ref.Object == "" {
ref.Object = HelmChartDefaultTag
fmt.Fprintf(c.out, "Using default tag: %s\n", HelmChartDefaultTag)
}
}

Loading…
Cancel
Save