Commit f8193c25 authored by jackgr's avatar jackgr

Prepare to download charts

parent 54abaf7e
......@@ -20,6 +20,7 @@ import (
"archive/tar"
"bytes"
"compress/gzip"
"encoding/base64"
"errors"
"fmt"
"io"
......@@ -243,25 +244,7 @@ func LoadDir(chart string) (*Chart, error) {
// LoadData loads a chart from data, where data is a []byte containing a gzipped tar file.
func LoadData(data []byte) (*Chart, error) {
b := bytes.NewBuffer(data)
unzipped, err := gzip.NewReader(b)
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
return LoadDataFromReader(bytes.NewBuffer(data))
}
// Load loads a chart from a chart archive.
......@@ -281,7 +264,11 @@ func Load(archive string) (*Chart, error) {
}
defer raw.Close()
unzipped, err := gzip.NewReader(raw)
return LoadDataFromReader(raw)
}
func LoadDataFromReader(r io.Reader) (*Chart, error) {
unzipped, err := gzip.NewReader(r)
if err != nil {
return nil, err
}
......@@ -367,3 +354,65 @@ func loadTar(r *tar.Reader) (*tarChart, error) {
return c, nil
}
// ChartMember is a file in a chart.
type ChartMember 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() ([]*ChartMember, error) {
dir := c.TemplatesDir()
return c.loadDirectory(dir)
}
// loadDirectory loads the members of a directory.
func (c *Chart) loadDirectory(dir string) ([]*ChartMember, error) {
files, err := ioutil.ReadDir(dir)
if err != nil {
return nil, err
}
members := []*ChartMember{}
for _, file := range files {
filename := filepath.Join(dir, file.Name())
member, err := c.loadMember(filename)
if err != nil {
return nil, err
}
members = append(members, member)
}
return members, nil
}
// path is from the root of the chart.
func (c *Chart) LoadMember(path string) (*ChartMember, 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) (*ChartMember, error) {
dir := c.Dir()
if !strings.HasPrefix(filename, dir) {
err := fmt.Errorf("File %s is outside chart directory %s", filename, dir)
return nil, err
}
b, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
path := strings.TrimPrefix(filename, dir)
content := base64.StdEncoding.EncodeToString(b)
result := &ChartMember{
Path: path,
Content: []byte(content),
}
return result, nil
}
......@@ -17,6 +17,8 @@ limitations under the License.
package chart
import (
"encoding/base64"
"fmt"
"io/ioutil"
"path/filepath"
"testing"
......@@ -30,6 +32,7 @@ const (
testarchive = "testdata/frobnitz-0.0.1.tgz"
testill = "testdata/ill-1.2.3.tgz"
testnochart = "testdata/nochart.tgz"
testmember = "templates/wordpress.jinja"
)
// Type canaries. If these fail, they will fail at compile time.
......@@ -160,3 +163,91 @@ func TestChart(t *testing.T) {
t.Errorf("Unexpectedly, icon is in %s", i)
}
}
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 %s 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 []*ChartMember) error {
for _, member := range members {
if member.Path == path {
filename := filepath.Join(root, path)
if err := compareContent(filename, string(member.Content)); err != nil {
return err
}
return nil
}
}
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, string(member.Content)); err != nil {
t.Fatal(err)
}
}
func compareContent(filename, content string) error {
b, err := ioutil.ReadFile(filename)
if err != nil {
return fmt.Errorf("Cannot read test file %s: %s", filename, err)
}
compare := base64.StdEncoding.EncodeToString(b)
if content != compare {
return fmt.Errorf("Expected member content\n%s\ngot\n%s", compare, content)
}
return nil
}
......@@ -34,6 +34,8 @@ type Chartfile struct {
Home string `yaml:"home"`
Dependencies []*Dependency `yaml:"dependencies,omitempty"`
Environment []*EnvConstraint `yaml:"environment,omitempty"`
Expander *Expander `yaml:"expander,omitempty"`
Schema string `yaml:"schema,omitempty"`
}
// Maintainer describes a chart maintainer.
......@@ -57,6 +59,14 @@ type EnvConstraint struct {
APIGroups []string `yaml:"apiGroups,omitempty"`
}
// Expander controls how template/ is evaluated.
type Expander struct {
// Currently just Expandybird or GoTemplate
Name string `json:"name"`
// During evaluation, which file to start from.
Entrypoint string `json:"entrypoint"`
}
// LoadChartfile loads a Chart.yaml file into a *Chart.
func LoadChartfile(filename string) (*Chartfile, error) {
b, err := ioutil.ReadFile(filename)
......
......@@ -50,6 +50,23 @@ func TestLoadChartfile(t *testing.T) {
if f.Source[0] != "https://example.com/foo/bar" {
t.Errorf("Expected https://example.com/foo/bar, got %s", f.Source)
}
expander := f.Expander
if expander == nil {
t.Errorf("No expander found in %s", testfile)
} else {
if expander.Name != "Expandybird" {
t.Errorf("Expected expander name Expandybird, got %s", expander.Name)
}
if expander.Entrypoint != "templates/wordpress.jinja" {
t.Errorf("Expected expander entrypoint templates/wordpress.jinja, got %s", expander.Entrypoint)
}
}
if f.Schema != "wordpress.jinja.schema" {
t.Errorf("Expected schema wordpress.jinja.schema, got %s", f.Schema)
}
}
func TestVersionOK(t *testing.T) {
......
......@@ -26,3 +26,8 @@ environment:
- extensions/v1beta1/daemonset
apiGroups:
- 3rdParty
expander:
name: Expandybird
entrypoint: templates/wordpress.jinja
schema: wordpress.jinja.schema
\ No newline at end of file
......@@ -97,28 +97,6 @@ type Manifest struct {
Layout *Layout `json:"layout,omitempty"`
}
// Expander controls how template/ is evaluated.
type Expander struct {
// Currently just Expandybird or GoTemplate
Name string `json:"name"`
// During evaluation, which file to start from.
Entrypoint string `json:"entry_point"`
}
// ChartFile is a file in a chart that is not chart.yaml.
type ChartFile struct {
Path string `json:"path"` // Path from the root of the chart.
Content string `json:"content"` // Base64 encoded file content.
}
// Chart is our internal representation of the chart.yaml (in structured form) + all supporting files.
type Chart struct {
Name string `json:"name"`
Expander *Expander `json:"expander"`
Schema interface{} `json:"schema"`
Files []*ChartFile `json:"files"`
}
// Template describes a set of resources to be deployed.
// Manager expands a Template into a Configuration, which
// describes the set in a form that can be instantiated.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment