fix(pkg/chart): remove pkg/chart

This is the final step in replacing pkg/chart with pkg/chartutil.

This also removes the last of the TOML code.
pull/839/head
Matt Butcher 9 years ago
parent 57a32f1df8
commit 4a6c1e308b

89
glide.lock generated

@ -1,8 +1,8 @@
hash: 2ac3dc0e19d5a688173924d35a07b4bad2454c7e6f5ff4d5a6911f33d1037586
updated: 2016-05-24T09:51:36.455233258-07:00
hash: c936008b9f7c5628e6c1bc442f64c7f858387eb2ce56337a33d6b5ef98a6b822
updated: 2016-06-16T12:47:24.36415913-06:00
imports:
- name: bitbucket.org/ww/goautoneg
version: 75cd24fc2f2c2a2088577d12123ddee5f54e0675
version: 75cd24fc2f2c
- name: github.com/aokoli/goutils
version: 9c37978a95bd5c709a15883b6242714ea6709e64
- name: github.com/beorn7/perks
@ -11,8 +11,6 @@ imports:
- quantile
- name: github.com/blang/semver
version: 31b736133b98f26d5e078ec9eb591666edfd091f
- name: github.com/BurntSushi/toml
version: bbd5bb678321a0d6e58f1099321dfa73391c1b6f
- name: github.com/davecgh/go-spew
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
subpackages:
@ -22,17 +20,6 @@ imports:
subpackages:
- digest
- reference
- name: github.com/docker/docker
version: 0f5c9d301b9b1cca66b3ea0f9dec3b5317d3686d
subpackages:
- pkg/jsonmessage
- pkg/mount
- pkg/stdcopy
- pkg/symlink
- pkg/term
- pkg/term/winconsole
- pkg/timeutils
- pkg/units
- name: github.com/docker/engine-api
version: 3d72d392d07bece8d7d7b2a3b6b2e57c2df376a2
subpackages:
@ -155,8 +142,6 @@ imports:
- util/wordwrap
- name: github.com/imdario/mergo
version: 6633656539c1639d9d78127b7d47c622b5d7b6dc
- name: github.com/inconshreveable/mousetrap
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
- name: github.com/juju/ratelimit
version: 77ed1c8a01217656d2080ad51981f6e99adaa177
- name: github.com/Masterminds/semver
@ -169,24 +154,6 @@ imports:
version: fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a
subpackages:
- pbutil
- name: github.com/opencontainers/runc
version: 7ca2aa4873aea7cb4265b1726acb24b90d8726c6
subpackages:
- libcontainer
- libcontainer/apparmor
- libcontainer/cgroups
- libcontainer/cgroups/fs
- libcontainer/cgroups/systemd
- libcontainer/configs
- libcontainer/configs/validate
- libcontainer/criurpc
- libcontainer/label
- libcontainer/seccomp
- libcontainer/selinux
- libcontainer/stacktrace
- libcontainer/system
- libcontainer/user
- libcontainer/utils
- name: github.com/pborman/uuid
version: ca53cad383cad2479bbba7f7a1a05797ec1386e4
- name: github.com/prometheus/client_golang
@ -234,18 +201,6 @@ imports:
- internal
- jws
- jwt
- name: google.golang.org/appengine
version: 12d5545dc1cfa6047a286d5e853841b6471f4c19
subpackages:
- internal
- internal/app_identity
- internal/base
- internal/datastore
- internal/log
- internal/modules
- internal/remote_api
- urlfetch
- internal/urlfetch
- name: google.golang.org/cloud
version: eb47ba841d53d93506cfbfbc03927daf9cc48f88
subpackages:
@ -280,14 +235,24 @@ imports:
- pkg/kubectl/cmd/util
- pkg/kubectl/resource
- pkg/labels
- pkg/api/resource
- pkg/api/unversioned
- pkg/auth/user
- pkg/conversion
- pkg/fields
- pkg/runtime
- pkg/runtime/serializer
- pkg/types
- pkg/util
- pkg/util/intstr
- pkg/util/rand
- pkg/util/sets
- pkg/util/validation
- pkg/client/unversioned/auth
- pkg/client/unversioned/clientcmd/api
- pkg/client/unversioned/clientcmd/api/latest
- pkg/runtime
- pkg/util/errors
- pkg/util/homedir
- pkg/util/validation
- pkg/kubelet/server/portforward
- pkg/util/httpstream
- pkg/util/runtime
@ -313,17 +278,14 @@ imports:
- pkg/util/flag
- pkg/util/strategicpatch
- pkg/watch
- pkg/util/sets
- pkg/util/yaml
- pkg/api/resource
- pkg/auth/user
- pkg/conversion
- pkg/fields
- pkg/runtime/serializer
- pkg/types
- pkg/util
- pkg/util/intstr
- pkg/util/rand
- third_party/forked/reflect
- pkg/conversion/queryparams
- pkg/util/json
- pkg/runtime/serializer/protobuf
- pkg/runtime/serializer/recognizer
- pkg/runtime/serializer/versioning
- pkg/util/wait
- pkg/api/v1
- pkg/client/metrics
- pkg/runtime/serializer/streaming
@ -333,9 +295,6 @@ imports:
- pkg/version
- pkg/watch/versioned
- pkg/client/unversioned/clientcmd/api/v1
- pkg/runtime/serializer/versioning
- pkg/conversion/queryparams
- pkg/util/json
- pkg/httplog
- pkg/util/wsstream
- third_party/golang/netutil
@ -357,7 +316,6 @@ imports:
- pkg/apis/extensions/install
- pkg/apis/metrics/install
- pkg/apis/policy/install
- pkg/util/wait
- plugin/pkg/client/auth
- pkg/client/clientset_generated/internalclientset
- pkg/client/clientset_generated/internalclientset/typed/core/unversioned
@ -380,9 +338,6 @@ imports:
- pkg/registry/generic
- pkg/util/framer
- third_party/forked/json
- third_party/forked/reflect
- pkg/runtime/serializer/protobuf
- pkg/runtime/serializer/recognizer
- pkg/util/parsers
- pkg/util/net/sets
- pkg/apis/apps/v1alpha1

@ -12,8 +12,6 @@ import:
- package: gopkg.in/yaml.v2
- package: github.com/Masterminds/semver
version: 1.1.0
- package: github.com/BurntSushi/toml
version: bbd5bb678321a0d6e58f1099321dfa73391c1b6f
- package: github.com/technosophos/moniker
- package: github.com/golang/protobuf
version: f0a097ddac24fb00e07d2ac17f8671423f3ea47c
@ -38,5 +36,5 @@ import:
- pkg/labels
- package: github.com/gosuri/uitable
- package: speter.net/go/exp/math/dec/inf
vcs: git
repo: https://github.com/go-inf/inf.git
vcs: git

@ -1,525 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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 chart
import (
"archive/tar"
"bytes"
"compress/gzip"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"k8s.io/helm/pkg/chartutil"
)
// ChartfileName is the default Chart file name.
const ChartfileName string = "Chart.yaml"
const (
preTemplates string = "templates/"
preValues string = "values.yaml"
preCharts string = "charts/"
)
const defaultValues = `# Default values for %s.
# This is a YAML-formatted file.
# Declare name/value pairs to be passed into your templates.
# name: value
`
var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=")
// Chart represents a complete chart.
//
// A chart consists of the following parts:
//
// - Chart.yaml: In code, we refer to this as the Chartfile
// - templates/*: The template directory
// - README.md: Optional README file
// - LICENSE: Optional license file
// - hooks/: Optional hooks registry
// - docs/: Optional docs directory
//
// Packed charts are stored in gzipped tar archives (.tgz). Unpackaged charts
// are directories where the directory name is the Chartfile.Name.
//
// Optionally, a chart might also locate a provenance (.prov) file that it
// can use for cryptographic signing.
type Chart struct {
loader chartLoader
}
// Close the chart.
//
// Charts should always be closed when no longer needed.
func (c *Chart) Close() error {
return c.loader.close()
}
// Chartfile gets the Chartfile (Chart.yaml) for this chart.
func (c *Chart) Chartfile() *Chartfile {
return c.loader.chartfile()
}
// Dir returns the directory where the charts are located.
func (c *Chart) Dir() string {
return c.loader.dir()
}
// TemplatesDir returns the directory where the templates are stored.
func (c *Chart) TemplatesDir() string {
return filepath.Join(c.loader.dir(), preTemplates)
}
// ChartsDir returns the directory where dependency charts are stored.
func (c *Chart) ChartsDir() string {
return filepath.Join(c.loader.dir(), preCharts)
}
// LoadValues loads the contents of values.yaml into a map
func (c *Chart) LoadValues() (Values, error) {
m, err := chartutil.ReadValuesFile(filepath.Join(c.loader.dir(), preValues))
if err != nil {
return map[string]interface{}{}, err
}
return m.AsMap(), nil
}
// ChartDepNames returns the list of chart names found in ChartsDir.
func (c *Chart) ChartDepNames() ([]string, error) {
files, err := ioutil.ReadDir(c.ChartsDir())
if err != nil {
return nil, err
}
var deps []string
for _, file := range files {
if file.IsDir() {
deps = append(deps, filepath.Join(c.ChartsDir(), file.Name()))
}
}
return deps, nil
}
// chartLoader provides load, close, and save implementations for a chart.
type chartLoader interface {
// Chartfile resturns a *Chartfile for this chart.
chartfile() *Chartfile
// Dir returns a directory where the chart can be accessed.
dir() string
// Close cleans up a chart.
close() error
}
type dirChart struct {
chartyaml *Chartfile
chartdir string
}
func (d *dirChart) chartfile() *Chartfile {
return d.chartyaml
}
func (d *dirChart) dir() string {
return d.chartdir
}
func (d *dirChart) close() error {
return nil
}
type tarChart struct {
chartyaml *Chartfile
tmpDir string
}
func (t *tarChart) chartfile() *Chartfile {
return t.chartyaml
}
func (t *tarChart) dir() string {
return t.tmpDir
}
func (t *tarChart) close() error {
// Remove the temp directory.
return os.RemoveAll(t.tmpDir)
}
// Create creates a new chart in a directory.
//
// Inside of dir, this will create a directory based on the name of
// chartfile.Name. It will then write the Chart.yaml into this directory and
// create the (empty) appropriate directories.
//
// The returned *Chart will point to the newly created directory.
//
// If dir does not exist, this will return an error.
// If Chart.yaml or any directories cannot be created, this will return an
// error. In such a case, this will attempt to clean up by removing the
// new chart directory.
func Create(chartfile *Chartfile, dir string) (*Chart, error) {
path, err := filepath.Abs(dir)
if err != nil {
return nil, err
}
if fi, err := os.Stat(path); err != nil {
return nil, err
} else if !fi.IsDir() {
return nil, fmt.Errorf("no such directory %s", path)
}
n := fname(chartfile.Name)
cdir := filepath.Join(path, n)
if fi, err := os.Stat(cdir); err == nil && !fi.IsDir() {
return nil, fmt.Errorf("file %s already exists and is not a directory", cdir)
}
if err := os.MkdirAll(cdir, 0755); err != nil {
return nil, err
}
if err := chartfile.Save(filepath.Join(cdir, ChartfileName)); err != nil {
return nil, err
}
val := []byte(fmt.Sprintf(defaultValues, chartfile.Name))
if err := ioutil.WriteFile(filepath.Join(cdir, preValues), val, 0644); err != nil {
return nil, err
}
for _, d := range []string{preTemplates, preCharts} {
if err := os.MkdirAll(filepath.Join(cdir, d), 0755); err != nil {
return nil, err
}
}
return &Chart{
loader: &dirChart{chartyaml: chartfile, chartdir: cdir},
}, nil
}
// Expand uncompresses and extracts a chart into the specified directory.
func Expand(dir string, r io.Reader) error {
gr, err := gzip.NewReader(r)
if err != nil {
return err
}
defer gr.Close()
tr := tar.NewReader(gr)
for {
header, err := tr.Next()
if err == io.EOF {
break
} else if err != nil {
return err
}
path := filepath.Clean(filepath.Join(dir, header.Name))
info := header.FileInfo()
if info.IsDir() {
if err = os.MkdirAll(path, info.Mode()); err != nil {
return err
}
continue
}
file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode())
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(file, tr)
if err != nil {
return err
}
}
return nil
}
// fname prepares names for the filesystem
func fname(name string) string {
// Right now, we don't do anything. Do we need to encode any particular
// characters? What characters are legal in a chart name, but not in file
// names on Windows, Linux, or OSX.
return name
}
// LoadDir loads an entire chart from a directory.
//
// This includes the Chart.yaml (*Chartfile) and all of the manifests.
//
// If you are just reading the Chart.yaml file, it is substantially more
// performant to use LoadChartfile.
func LoadDir(chart string) (*Chart, error) {
dir, err := filepath.Abs(chart)
if err != nil {
return nil, fmt.Errorf("%s is not a valid path", chart)
}
if fi, err := os.Stat(dir); err != nil {
return nil, err
} else if !fi.IsDir() {
return nil, fmt.Errorf("%s is not a directory", chart)
}
cf, err := LoadChartfile(filepath.Join(dir, "Chart.yaml"))
if err != nil {
return nil, err
}
cl := &dirChart{
chartyaml: cf,
chartdir: dir,
}
return &Chart{
loader: cl,
}, nil
}
// LoadChart loads an entire chart archive.
//
// The following are valid values for 'chfi':
//
// - relative path to the chart archive
// - absolute path to the chart archive
// - name of the chart directory
//
func LoadChart(chfi string) (*Chart, error) {
path, err := filepath.Abs(chfi)
if err != nil {
return nil, err
}
fi, err := os.Stat(path)
if err != nil {
return nil, err
}
if fi.IsDir() {
return LoadDir(path)
}
return Load(path)
}
// LoadData loads a chart from data, where data is a []byte containing a gzipped tar file.
func LoadData(data []byte) (*Chart, error) {
return LoadDataFromReader(bytes.NewBuffer(data))
}
// Load loads a chart from a chart archive.
//
// A chart archive is a gzipped tar archive that follows the Chart format
// specification.
func Load(archive string) (*Chart, error) {
if fi, err := os.Stat(archive); err != nil {
return nil, err
} else if fi.IsDir() {
return nil, errors.New("cannot load a directory with chart.Load()")
}
raw, err := os.Open(archive)
if err != nil {
return nil, err
}
defer raw.Close()
return LoadDataFromReader(raw)
}
// LoadDataFromReader loads a chart from a reader
func LoadDataFromReader(r io.Reader) (*Chart, error) {
unzipped, err := gzip.NewReader(r)
if err != nil {
return nil, err
}
defer unzipped.Close()
untarred := tar.NewReader(unzipped)
c, err := loadTar(untarred)
if err != nil {
return nil, err
}
cf, err := LoadChartfile(filepath.Join(c.tmpDir, ChartfileName))
if err != nil {
return nil, err
}
c.chartyaml = cf
return &Chart{loader: c}, nil
}
func loadTar(r *tar.Reader) (*tarChart, error) {
td, err := ioutil.TempDir("", "chart-")
if err != nil {
return nil, err
}
// ioutil.TempDir uses Getenv("TMPDIR"), so there are no guarantees
dir, err := filepath.Abs(td)
if err != nil {
return nil, fmt.Errorf("%s is not a valid path", td)
}
c := &tarChart{
chartyaml: &Chartfile{},
tmpDir: dir,
}
firstDir := ""
hdr, err := r.Next()
for err == nil {
// This is to prevent malformed tar attacks.
hdr.Name = filepath.Clean(hdr.Name)
if firstDir == "" {
fi := hdr.FileInfo()
if fi.IsDir() {
firstDir = hdr.Name
}
} else if strings.HasPrefix(hdr.Name, firstDir) {
// We know this has the prefix, so we know there won't be an error.
rel, _ := filepath.Rel(firstDir, hdr.Name)
// If tar record is a directory, create one in the tmpdir and return.
if hdr.FileInfo().IsDir() {
os.MkdirAll(filepath.Join(c.tmpDir, rel), 0755)
hdr, err = r.Next()
continue
}
//dest := filepath.Join(c.tmpDir, rel)
f, err := os.Create(filepath.Join(c.tmpDir, rel))
if err != nil {
hdr, err = r.Next()
continue
}
if _, err := io.Copy(f, r); err != nil {
}
f.Close()
}
hdr, err = r.Next()
}
if err != nil && err != io.EOF {
c.close()
return c, err
}
return c, nil
}
// Member is a file in a chart.
type Member struct {
Path string `json:"path"` // Path from the root of the chart.
Content []byte `json:"content"` // Base64 encoded content.
}
// LoadTemplates loads the members of TemplatesDir().
func (c *Chart) LoadTemplates() ([]*Member, error) {
dir := c.TemplatesDir()
return c.loadDirectory(dir)
}
// loadDirectory loads the members of a directory.
func (c *Chart) loadDirectory(dir string) ([]*Member, error) {
files, err := ioutil.ReadDir(dir)
if err != nil {
return nil, err
}
members := []*Member{}
for _, file := range files {
filename := filepath.Join(dir, file.Name())
if !file.IsDir() {
addition, err := c.loadMember(filename)
if err != nil {
return nil, err
}
members = append(members, addition)
} else {
additions, err := c.loadDirectory(filename)
if err != nil {
return nil, err
}
members = append(members, additions...)
}
}
return members, nil
}
// LoadMember loads a chart member from a given path where path is the root of the chart.
func (c *Chart) LoadMember(path string) (*Member, error) {
filename := filepath.Join(c.loader.dir(), path)
return c.loadMember(filename)
}
// loadMember loads and base 64 encodes a file.
func (c *Chart) loadMember(filename string) (*Member, error) {
dir := c.Dir()
if !strings.HasPrefix(filename, dir) {
err := fmt.Errorf("File %s is outside chart directory %s", filename, dir)
return nil, err
}
content, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
path := strings.TrimPrefix(filename, dir)
path = strings.TrimLeft(path, "/")
result := &Member{
Path: path,
Content: content,
}
return result, nil
}
// Content is abstraction for the contents of a chart.
type Content struct {
Chartfile *Chartfile `json:"chartfile"`
Members []*Member `json:"members"`
}
// LoadContent loads contents of a chart directory into Content
func (c *Chart) LoadContent() (*Content, error) {
ms, err := c.loadDirectory(c.Dir())
if err != nil {
return nil, err
}
cc := &Content{
Chartfile: c.Chartfile(),
Members: ms,
}
return cc, nil
}

@ -1,308 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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 chart
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"testing"
)
const (
testfile = "testdata/frobnitz/Chart.yaml"
testdir = "testdata/frobnitz/"
testarchive = "testdata/frobnitz-0.0.1.tgz"
testmember = "templates/template.tpl"
expectedTemplate = "Hello {{.Name | default \"world\"}}\n"
)
// Type canaries. If these fail, they will fail at compile time.
var _ chartLoader = &dirChart{}
var _ chartLoader = &tarChart{}
func TestLoadDir(t *testing.T) {
c, err := LoadDir(testdir)
if err != nil {
t.Errorf("Failed to load chart: %s", err)
}
if c.Chartfile().Name != "frobnitz" {
t.Errorf("Expected chart name to be 'frobnitz'. Got '%s'.", c.Chartfile().Name)
}
}
func TestCreate(t *testing.T) {
tdir, err := ioutil.TempDir("", "helm-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tdir)
cf := &Chartfile{Name: "foo"}
c, err := Create(cf, tdir)
if err != nil {
t.Fatal(err)
}
dir := filepath.Join(tdir, "foo")
if c.Chartfile().Name != "foo" {
t.Errorf("Expected name to be 'foo', got %q", c.Chartfile().Name)
}
for _, d := range []string{preTemplates, preCharts} {
if fi, err := os.Stat(filepath.Join(dir, d)); err != nil {
t.Errorf("Expected %s dir: %s", d, err)
} else if !fi.IsDir() {
t.Errorf("Expected %s to be a directory.", d)
}
}
for _, f := range []string{ChartfileName, preValues} {
if fi, err := os.Stat(filepath.Join(dir, f)); err != nil {
t.Errorf("Expected %s file: %s", f, err)
} else if fi.IsDir() {
t.Errorf("Expected %s to be a fle.", f)
}
}
}
func TestLoad(t *testing.T) {
c, err := Load(testarchive)
if err != nil {
t.Errorf("Failed to load chart: %s", err)
return
}
defer c.Close()
if c.Chartfile() == nil {
t.Error("No chartfile was loaded.")
return
}
if c.Chartfile().Name != "frobnitz" {
t.Errorf("Expected name to be frobnitz, got %q", c.Chartfile().Name)
}
}
func TestLoadData(t *testing.T) {
data, err := ioutil.ReadFile(testarchive)
if err != nil {
t.Errorf("Failed to read testarchive file: %s", err)
return
}
c, err := LoadData(data)
if err != nil {
t.Errorf("Failed to load chart: %s", err)
return
}
if c.Chartfile() == nil {
t.Error("No chartfile was loaded.")
return
}
if c.Chartfile().Name != "frobnitz" {
t.Errorf("Expected name to be frobnitz, got %q", c.Chartfile().Name)
}
}
func TestChart(t *testing.T) {
c, err := LoadDir(testdir)
if err != nil {
t.Errorf("Failed to load chart: %s", err)
}
defer c.Close()
if c.Dir() != c.loader.dir() {
t.Errorf("Unexpected location for directory: %s", c.Dir())
}
if c.Chartfile().Name != c.loader.chartfile().Name {
t.Errorf("Unexpected chart file name: %s", c.Chartfile().Name)
}
dir := c.Dir()
d := c.ChartsDir()
if d != filepath.Join(dir, preCharts) {
t.Errorf("Unexpectedly, charts are in %s", d)
}
d = c.TemplatesDir()
if d != filepath.Join(dir, preTemplates) {
t.Errorf("Unexpectedly, templates are in %s", d)
}
}
func TestLoadTemplates(t *testing.T) {
c, err := LoadDir(testdir)
if err != nil {
t.Errorf("Failed to load chart: %s", err)
}
members, err := c.LoadTemplates()
if members == nil {
t.Fatalf("Cannot load templates: unknown error")
}
if err != nil {
t.Fatalf("Cannot load templates: %s", err)
}
dir := c.TemplatesDir()
files, err := ioutil.ReadDir(dir)
if err != nil {
t.Fatalf("Cannot read template directory: %s", err)
}
if len(members) != len(files) {
t.Fatalf("Expected %d templates, got %d", len(files), len(members))
}
root := c.loader.dir()
for _, file := range files {
path := filepath.Join(preTemplates, file.Name())
if err := findMember(root, path, members); err != nil {
t.Fatal(err)
}
}
}
func findMember(root, path string, members []*Member) error {
for _, member := range members {
if member.Path == path {
filename := filepath.Join(root, path)
return compareContent(filename, member.Content)
}
}
return fmt.Errorf("Template not found: %s", path)
}
func TestLoadMember(t *testing.T) {
c, err := LoadDir(testdir)
if err != nil {
t.Errorf("Failed to load chart: %s", err)
}
member, err := c.LoadMember(testmember)
if member == nil {
t.Fatalf("Cannot load member %s: unknown error", testmember)
}
if err != nil {
t.Fatalf("Cannot load member %s: %s", testmember, err)
}
if member.Path != testmember {
t.Errorf("Expected member path %s, got %s", testmember, member.Path)
}
filename := filepath.Join(c.loader.dir(), testmember)
if err := compareContent(filename, member.Content); err != nil {
t.Fatal(err)
}
}
func TestLoadContent(t *testing.T) {
c, err := LoadDir(testdir)
if err != nil {
t.Errorf("Failed to load chart: %s", err)
}
content, err := c.LoadContent()
if err != nil {
t.Errorf("Failed to load chart content: %s", err)
}
want := c.Chartfile()
have := content.Chartfile
if !reflect.DeepEqual(want, have) {
t.Errorf("Unexpected chart file\nwant:\n%v\nhave:\n%v\n", want, have)
}
for _, member := range content.Members {
have := member.Content
wantMember, err := c.LoadMember(member.Path)
if err != nil {
t.Errorf("Failed to load chart member: %s", err)
}
t.Logf("%s:\n%s\n\n", member.Path, member.Content)
want := wantMember.Content
if !reflect.DeepEqual(want, have) {
t.Errorf("Unexpected chart member %s\nwant:\n%v\nhave:\n%v\n", member.Path, want, have)
}
}
}
func compareContent(filename string, content []byte) error {
compare, err := ioutil.ReadFile(filename)
if err != nil {
return fmt.Errorf("Cannot read test file %s: %s", filename, err)
}
if !reflect.DeepEqual(compare, content) {
return fmt.Errorf("Expected member content\n%v\ngot\n%v", compare, content)
}
return nil
}
func TestExpand(t *testing.T) {
r, err := os.Open(testarchive)
if err != nil {
t.Errorf("Failed to read testarchive file: %s", err)
return
}
td, err := ioutil.TempDir("", "helm-unittest-chart-")
if err != nil {
t.Errorf("Failed to create tempdir: %s", err)
return
}
err = Expand(td, r)
if err != nil {
t.Errorf("Failed to expand testarchive file: %s", err)
}
fi, err := os.Lstat(td + "/frobnitz/Chart.yaml")
if err != nil {
t.Errorf("Failed to stat Chart.yaml from expanded archive: %s", err)
}
if fi.Name() != "Chart.yaml" {
t.Errorf("Didn't get the right file name from stat, expected Chart.yaml, got: %s", fi.Name())
}
tr, err := os.Open(td + "/frobnitz/templates/template.tpl")
if err != nil {
t.Errorf("Failed to open template.tpl from expanded archive: %s", err)
}
c, err := ioutil.ReadAll(tr)
if err != nil {
t.Errorf("Failed to read contents of template.tpl from expanded archive: %s", err)
}
if string(c) != expectedTemplate {
t.Errorf("Contents of the expanded template differ, wanted '%s' got '%s'", expectedTemplate, c)
}
}

@ -1,75 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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 chart
import (
"io/ioutil"
"github.com/Masterminds/semver"
"gopkg.in/yaml.v2"
)
// Chartfile describes a Helm Chart (e.g. Chart.yaml)
type Chartfile struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
Version string `yaml:"version"`
Keywords []string `yaml:"keywords,omitempty"`
Maintainers []*Maintainer `yaml:"maintainers,omitempty"`
Source []string `yaml:"sources,omitempty"`
Home string `yaml:"home"`
}
// Maintainer describes a chart maintainer.
type Maintainer struct {
Name string `yaml:"name"`
Email string `yaml:"email,omitempty"`
}
// LoadChartfile loads a Chart.yaml file into a *Chart.
func LoadChartfile(filename string) (*Chartfile, error) {
b, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
var y Chartfile
err = yaml.Unmarshal(b, &y)
if err != nil {
return nil, err
}
// Validate that the Version is actually a valid semver version
_, err = semver.NewVersion(y.Version)
if err != nil {
return nil, err
}
return &y, nil
}
// Save saves a Chart.yaml file
func (c *Chartfile) Save(filename string) error {
b, err := c.Marshal()
if err != nil {
return err
}
return ioutil.WriteFile(filename, b, 0644)
}
// Marshal encodes the chart file into YAML.
func (c *Chartfile) Marshal() ([]byte, error) {
return yaml.Marshal(c)
}

@ -1,59 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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 chart
import (
"testing"
)
const badChart = "testdata/badchartversion/Chart.yaml"
func TestLoadChartfile(t *testing.T) {
f, err := LoadChartfile(testfile)
if err != nil {
t.Errorf("Failed to open %s: %s", testfile, err)
return
}
if f.Name != "frobnitz" {
t.Errorf("Expected frobnitz, got %s", f.Name)
}
if len(f.Maintainers) != 2 {
t.Errorf("Expected 2 maintainers, got %d", len(f.Maintainers))
}
if f.Source[0] != "https://example.com/foo/bar" {
t.Errorf("Expected https://example.com/foo/bar, got %s", f.Source)
}
}
func TestLoadChartfileFailsWithInvalidVersion(t *testing.T) {
f, err := LoadChartfile(badChart)
if err == nil {
t.Errorf("LoadChartFile didn't fail with invalid version")
return
}
if err.Error() != "Invalid Semantic Version" {
t.Errorf("LoadChartFile didn't return the expected error")
return
}
if f != nil {
t.Errorf("LoadChartFile returned a chart despite error")
return
}
}

@ -1,23 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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 chart implements the Chart format.
This package provides tools for working with the Chart format, including the
Chartfile (chart.yaml) and compressed chart archives.
*/
package chart

@ -1,113 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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 chart
import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"os"
"path/filepath"
)
// Save creates an archived chart to the given directory.
//
// This takes an existing chart and a destination directory.
//
// If the directory is /foo, and the chart is named bar, with version 1.0.0, this
// will generate /foo/bar-1.0.0.tgz.
//
// This returns the absolute path to the chart archive file.
func Save(c *Chart, outDir string) (string, error) {
// Create archive
if fi, err := os.Stat(outDir); err != nil {
return "", err
} else if !fi.IsDir() {
return "", fmt.Errorf("location %s is not a directory", outDir)
}
cfile := c.Chartfile()
dir := c.Dir()
pdir := filepath.Dir(dir)
filename := fmt.Sprintf("%s-%s.tgz", fname(cfile.Name), cfile.Version)
filename = filepath.Join(outDir, filename)
// Fail early if the YAML is borked.
if err := cfile.Save(filepath.Join(dir, ChartfileName)); err != nil {
return "", err
}
// Create file.
f, err := os.Create(filename)
if err != nil {
return "", err
}
// Wrap in gzip writer
zipper := gzip.NewWriter(f)
zipper.Header.Extra = headerBytes
zipper.Header.Comment = "Helm"
// Wrap in tar writer
twriter := tar.NewWriter(zipper)
rollback := false
defer func() {
twriter.Close()
zipper.Close()
f.Close()
if rollback {
os.Remove(filename)
}
}()
err = filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
hdr, err := tar.FileInfoHeader(fi, ".")
if err != nil {
return err
}
relpath, err := filepath.Rel(pdir, path)
if err != nil {
return err
}
hdr.Name = relpath
twriter.WriteHeader(hdr)
// Skip directories.
if fi.IsDir() {
return nil
}
in, err := os.Open(path)
if err != nil {
return err
}
_, err = io.Copy(twriter, in)
in.Close()
return err
})
if err != nil {
rollback = true
return filename, err
}
return filename, nil
}

@ -1,120 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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 chart
import (
"archive/tar"
"compress/gzip"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"testing"
)
const sprocketdir = "testdata/sprocket"
func TestSave(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "helm-")
if err != nil {
t.Fatal("Could not create temp directory")
}
t.Logf("Temp: %s", tmpdir)
// Because of the defer, don't call t.Fatal in the remainder of this
// function.
defer os.RemoveAll(tmpdir)
c, err := LoadDir(sprocketdir)
if err != nil {
t.Errorf("Failed to load %s: %s", sprocketdir, err)
return
}
tfile, err := Save(c, tmpdir)
if err != nil {
t.Errorf("Failed to save %s to %s: %s", c.Chartfile().Name, tmpdir, err)
return
}
b := filepath.Base(tfile)
expectname := "sprocket-1.2.3-alpha.1+12345.tgz"
if b != expectname {
t.Errorf("Expected %q, got %q", expectname, b)
}
files, err := getAllFiles(tfile)
if err != nil {
t.Errorf("Could not extract files: %s", err)
}
// Files should come back in order.
expect := []string{
"sprocket",
"sprocket/Chart.yaml",
"sprocket/values.toml",
"sprocket/templates",
"sprocket/templates/template.tpl",
}
if len(expect) != len(files) {
t.Errorf("Expected %d files, found %d", len(expect), len(files))
return
}
sort.Strings(files)
sort.Strings(expect)
for i := 0; i < len(expect); i++ {
if expect[i] != files[i] {
t.Errorf("Expected file %q, got %q", expect[i], files[i])
}
}
}
func getAllFiles(tfile string) ([]string, error) {
f1, err := os.Open(tfile)
if err != nil {
return []string{}, err
}
f2, err := gzip.NewReader(f1)
if err != nil {
f1.Close()
return []string{}, err
}
if f2.Header.Comment != "Helm" {
return []string{}, fmt.Errorf("Expected header Helm. Got %s", f2.Header.Comment)
}
if string(f2.Header.Extra) != string(headerBytes) {
return []string{}, fmt.Errorf("Expected header signature. Got %v", f2.Header.Extra)
}
f3 := tar.NewReader(f2)
files := []string{}
var e error
var hdr *tar.Header
for e == nil {
hdr, e = f3.Next()
if e == nil {
files = append(files, hdr.Name)
}
}
f2.Close()
f1.Close()
return files, nil
}

@ -1 +0,0 @@
This directory houses charts used in testing.

@ -1,13 +0,0 @@
name: frobnitz
description: This is a frobniz.
version: "garbage"
keywords:
- bad bad chart file
maintainers:
- name: The Helm Team
email: helm@example.com
- name: Someone Else
email: nobody@example.com
sources:
- https://example.com/foo/bar
home: http://example.com

@ -1,11 +0,0 @@
poet = "Coleridge"
title = "Rime of the Ancient Mariner"
stanza = ["at", "length", "did", "cross", "an", "Albatross"]
[mariner]
with = "crossbow"
shot = "ALBATROSS"
[water.water]
where = "everywhere"
nor = "any drop to drink"

Binary file not shown.

@ -1,15 +0,0 @@
name = "frobnitz"
description = "This is a frobniz."
version = "1.2.3-alpha.1+12345"
keywords = ["frobnitz", "sprocket", "dodad"]
home = "http://example.com"
source = [
"https://example.com/foo/bar",
"https://github.com/example/foo"
]
[[maintainer]]
name = "The Helm Team"
email = "helm@example.com"
[[maintainer]]
name = "Someone Else"
email = "nobody@example.com"

@ -1,15 +0,0 @@
name: frobnitz
description: This is a frobniz.
version: "1.2.3-alpha.1+12345"
keywords:
- frobnitz
- sprocket
- dodad
maintainers:
- name: The Helm Team
email: helm@example.com
- name: Someone Else
email: nobody@example.com
sources:
- https://example.com/foo/bar
home: http://example.com

@ -1 +0,0 @@
This is an install document. The client may display this.

@ -1 +0,0 @@
LICENSE placeholder.

@ -1,11 +0,0 @@
# Frobnitz
This is an example chart.
## Usage
This is an example. It has no usage.
## Development
For developer info, see the top-level repository.

@ -1 +0,0 @@
This is a placeholder for documentation.

@ -1,8 +0,0 @@
<?xml version="1.0"?>
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.0" width="256" height="256" id="test">
<desc>Example icon</desc>
<rect id="first" x="2" y="2" width="40" height="60" fill="navy"/>
<rect id="second" x="15" y="4" width="40" height="60" fill="red"/>
</svg>

Before

Width:  |  Height:  |  Size: 374 B

@ -1 +0,0 @@
Hello {{.Name | default "world"}}

@ -1,6 +0,0 @@
# A values file contains configuration.
name = "Some Name"
[section]
name = "Name in a section"

@ -1,13 +0,0 @@
name: sprocket
description: This is a sprocket"
version: 1.2.3-alpha.1+12345
keywords:
- frobnitz
- sprocket
- dodad
maintainers:
- name: The Helm Team
email: helm@example.com
- name: Someone Else
email: nobody@example.com
home: http://example.com

@ -1 +0,0 @@
Hello {{.Name | default "world"}}

@ -1,6 +0,0 @@
# A values file contains configuration.
name = "Some Name"
[section]
name = "Name in a section"

@ -1,75 +0,0 @@
package chart
import (
"errors"
"io"
"io/ioutil"
"strings"
"github.com/BurntSushi/toml"
)
// ErrNoTable indicates that a chart does not have a matching table.
var ErrNoTable = errors.New("no table")
// Values represents a collection of chart values.
type Values map[string]interface{}
// Table gets a table (TOML subsection) from a Values object.
//
// The table is returned as a Values.
//
// Compound table names may be specified with dots:
//
// foo.bar
//
// The above will be evaluated as "The table bar inside the table
// foo".
//
// An ErrNoTable is returned if the table does not exist.
func (v Values) Table(name string) (Values, error) {
names := strings.Split(name, ".")
table := v
var err error
for _, n := range names {
table, err = tableLookup(table, n)
if err != nil {
return table, err
}
}
return table, err
}
// Encode writes serialized Values information to the given io.Writer.
func (v Values) Encode(w io.Writer) error {
return toml.NewEncoder(w).Encode(v)
}
func tableLookup(v Values, simple string) (Values, error) {
v2, ok := v[simple]
if !ok {
return v, ErrNoTable
}
vv, ok := v2.(map[string]interface{})
if !ok {
return vv, ErrNoTable
}
return vv, nil
}
// ReadValues will parse TOML byte data into a Values.
func ReadValues(data []byte) (Values, error) {
out := map[string]interface{}{}
err := toml.Unmarshal(data, out)
return out, err
}
// ReadValuesFile will parse a TOML file into a Values.
func ReadValuesFile(filename string) (Values, error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
return map[string]interface{}{}, err
}
return ReadValues(data)
}

@ -1,134 +0,0 @@
package chart
import (
"bytes"
"fmt"
"testing"
"text/template"
)
func TestReadValues(t *testing.T) {
doc := `# Test TOML parse
poet = "Coleridge"
title = "Rime of the Ancient Mariner"
stanza = ["at", "length", "did", "cross", "an", "Albatross"]
[mariner]
with = "crossbow"
shot = "ALBATROSS"
[water.water]
where = "everywhere"
nor = "any drop to drink"
`
data, err := ReadValues([]byte(doc))
if err != nil {
t.Fatalf("Error parsing bytes: %s", err)
}
matchValues(t, data)
}
func TestReadValuesFile(t *testing.T) {
data, err := ReadValuesFile("./testdata/coleridge.toml")
if err != nil {
t.Fatalf("Error reading TOML file: %s", err)
}
matchValues(t, data)
}
func ExampleValues() {
doc := `title="Moby Dick"
[chapter.one]
title = "Loomings"
[chapter.two]
title = "The Carpet-Bag"
[chapter.three]
title = "The Spouter Inn"
`
d, err := ReadValues([]byte(doc))
if err != nil {
panic(err)
}
ch1, err := d.Table("chapter.one")
if err != nil {
panic("could not find chapter one")
}
fmt.Print(ch1["title"])
// Output:
// Loomings
}
func TestTable(t *testing.T) {
doc := `title="Moby Dick"
[chapter.one]
title = "Loomings"
[chapter.two]
title = "The Carpet-Bag"
[chapter.three]
title = "The Spouter Inn"
`
d, err := ReadValues([]byte(doc))
if err != nil {
t.Fatalf("Failed to parse the White Whale: %s", err)
}
if _, err := d.Table("title"); err == nil {
t.Fatalf("Title is not a table.")
}
if _, err := d.Table("chapter"); err != nil {
t.Fatalf("Failed to get the chapter table: %s\n%v", err, d)
}
if v, err := d.Table("chapter.one"); err != nil {
t.Errorf("Failed to get chapter.one: %s", err)
} else if v["title"] != "Loomings" {
t.Errorf("Unexpected title: %s", v["title"])
}
if _, err := d.Table("chapter.three"); err != nil {
t.Errorf("Chapter three is missing: %s\n%v", err, d)
}
if _, err := d.Table("chapter.OneHundredThirtySix"); err == nil {
t.Errorf("I think you mean 'Epilogue'")
}
}
func matchValues(t *testing.T, data map[string]interface{}) {
if data["poet"] != "Coleridge" {
t.Errorf("Unexpected poet: %s", data["poet"])
}
if o, err := ttpl("{{len .stanza}}", data); err != nil {
t.Errorf("len stanza: %s", err)
} else if o != "6" {
t.Errorf("Expected 6, got %s", o)
}
if o, err := ttpl("{{.mariner.shot}}", data); err != nil {
t.Errorf(".mariner.shot: %s", err)
} else if o != "ALBATROSS" {
t.Errorf("Expected that mariner shot ALBATROSS")
}
if o, err := ttpl("{{.water.water.where}}", data); err != nil {
t.Errorf(".water.water.where: %s", err)
} else if o != "everywhere" {
t.Errorf("Expected water water everywhere")
}
}
func ttpl(tpl string, v map[string]interface{}) (string, error) {
var b bytes.Buffer
tt := template.Must(template.New("t").Parse(tpl))
if err := tt.Execute(&b, v); err != nil {
return "", err
}
return b.String(), nil
}
Loading…
Cancel
Save