pull/2041/merge
Larry Rensing 9 years ago committed by GitHub
commit 5798dcdc2b

@ -75,7 +75,7 @@ if it cannot find a requirements.yaml.
func newDependencyCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "dependency update|build|list",
Use: "dependency update|build|list|create",
Aliases: []string{"dep", "dependencies"},
Short: "manage a chart's dependencies",
Long: dependencyDesc,
@ -84,6 +84,7 @@ func newDependencyCmd(out io.Writer) *cobra.Command {
cmd.AddCommand(newDependencyListCmd(out))
cmd.AddCommand(newDependencyUpdateCmd(out))
cmd.AddCommand(newDependencyBuildCmd(out))
cmd.AddCommand(newDependencyCreateCmd(out))
return cmd
}

@ -0,0 +1,86 @@
/*
Copyright 2016 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 main
import (
"io"
"path/filepath"
"github.com/spf13/cobra"
"k8s.io/helm/cmd/helm/helmpath"
"k8s.io/helm/pkg/downloader"
)
const dependencyCreateDesc = `
Update the requirements.yaml file within a given chart.
If no requirements.yaml exists in the chart directory, this command will create
a new requirements.yaml and add the provided dependency.
`
type dependencyCreateCmd struct {
out io.Writer
name string
chartpath string
repository string
version string
helmhome helmpath.Home
}
// newDependencyCreateCmd creates a new dependency create command.
func newDependencyCreateCmd(out io.Writer) *cobra.Command {
dcc := &dependencyCreateCmd{
out: out,
}
cmd := &cobra.Command{
Use: "create DEPENDENCY REPOSITORY [flags]",
Aliases: []string{"create"},
Short: "add dependencies to the contents of requirements.yaml",
Long: dependencyCreateDesc,
RunE: func(cmd *cobra.Command, args []string) error {
dcc.name = args[0]
dcc.repository = args[1]
dcc.helmhome = helmpath.Home(homePath())
return dcc.run()
},
}
f := cmd.Flags()
f.StringVarP(&dcc.version, "version", "", "0.1.0", "set the version")
f.StringVarP(&dcc.chartpath, "chartpath", "c", ".", "directory of chart to add dependency to ")
return cmd
}
// run runs the full dependency create process.
func (d *dependencyCreateCmd) run() error {
var err error
d.chartpath, err = filepath.Abs(d.chartpath)
if err != nil {
return err
}
man := &downloader.Manager{
Out: d.out,
ChartPath: d.chartpath,
HelmHome: d.helmhome,
}
return man.Create(d.name, d.repository, d.version)
}

@ -0,0 +1,156 @@
/*
Copyright 2016 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 main
import (
"bytes"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
"k8s.io/helm/cmd/helm/helmpath"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/proto/hapi/chart"
)
func TestDependencyCreateCmd(t *testing.T) {
// Set up a testing helm home
oldhome := helmHome
hh, err := tempHelmHome(t)
if err != nil {
t.Fatal(err)
}
helmHome = hh
defer func() {
os.RemoveAll(hh)
helmHome = oldhome
}()
chartname := "depcreate"
if err := createChartWithoutDeps(hh, chartname); err != nil {
t.Fatal(err)
}
out := bytes.NewBuffer(nil)
dcc := &dependencyCreateCmd{out: out}
dcc.name = "dep1"
dcc.repository = "repo1"
dcc.helmhome = helmpath.Home(hh)
dcc.chartpath = filepath.Join(hh, chartname)
dcc.version = "0.1.0"
doesNotExist := filepath.Join(hh, chartname, "requirements.yaml")
if _, err := os.Stat(doesNotExist); err == nil {
t.Fatalf("Unexpected %q", doesNotExist)
}
// no requirements.yaml exists
if err := dcc.run(); err != nil {
output := out.String()
t.Logf("Output: %s", output)
t.Fatal(err)
}
output := out.String()
if !strings.Contains(output, `charts, creating new requirements file`) {
t.Errorf("New requirements.yaml did not get created")
}
expect := filepath.Join(hh, chartname, "requirements.yaml")
if _, err := os.Stat(expect); err != nil {
t.Fatal(err)
}
// add a dependency to an existing requirements.yaml
dcc.name = "dep2"
dcc.repository = "repo2"
dcc.version = "1.0.0"
if err := dcc.run(); err != nil {
output := out.String()
t.Logf("Output: %s", output)
t.Fatal(err)
}
c, err := chartutil.LoadDir(dcc.chartpath)
if err != nil {
t.Fatal(err)
}
reqs, err := chartutil.LoadRequirements(c)
if err != nil {
t.Fatal(err)
}
// compare
expectedReqCount := 2
if len(reqs.Dependencies) != expectedReqCount {
t.Errorf("Expected %d total requirements, actual count: %d", expectedReqCount, len(reqs.Dependencies))
}
expectedDeps := []chartutil.Dependency{
{Name: "dep1", Version: "0.1.0", Repository: "repo1"},
{Name: "dep2", Version: "1.0.0", Repository: "repo2"},
}
for i := 0; i < len(reqs.Dependencies); i++ {
if !reflect.DeepEqual(expectedDeps[i], *reqs.Dependencies[i]) {
t.Errorf("Expected deps: %+v\n Actual deps: %+v\n", expectedDeps[i], *reqs.Dependencies[i])
}
}
// dep already exists
dcc.name = "dep2"
dcc.repository = "modifiedrepo"
dcc.version = "1.0.1"
if err := dcc.run(); err != nil {
output := out.String()
t.Logf("Output: %s", output)
t.Fatal(err)
}
c, err = chartutil.LoadDir(dcc.chartpath)
if err != nil {
t.Fatal(err)
}
reqs, err = chartutil.LoadRequirements(c)
if err != nil {
t.Fatal(err)
}
expectedDeps[1] = chartutil.Dependency{
Name: "dep2", Version: "1.0.1", Repository: "modifiedrepo",
}
for i := 0; i < len(reqs.Dependencies); i++ {
if !reflect.DeepEqual(expectedDeps[i], *reqs.Dependencies[i]) {
t.Errorf("Expected deps: %+v\n Actual deps: %+v\n", expectedDeps[i], *reqs.Dependencies[i])
}
}
}
func createChartWithoutDeps(dest, name string) error {
cfile := &chart.Metadata{
Name: name,
Version: "1.2.3",
}
_, err := chartutil.Create(cfile, dest)
return err
}

@ -57,11 +57,11 @@ type Dependency struct {
// used to fetch the repository index.
Repository string `json:"repository"`
// A yaml path that resolves to a boolean, used for enabling/disabling charts (e.g. subchart1.enabled )
Condition string `json:"condition"`
Condition string `json:"condition,omitempty"`
// Tags can be used to group charts for enabling/disabling together
Tags []string `json:"tags"`
Tags []string `json:"tags,omitempty"`
// Enabled bool determines if chart should be loaded
Enabled bool `json:"enabled"`
Enabled bool `json:"enabled,omitempty"`
}
// ErrNoRequirementsFile to detect error condition

@ -160,6 +160,51 @@ func (m *Manager) Update() error {
return writeLock(m.ChartPath, lock)
}
// Create adds a new dependency for a chart.
//
// It first attempts to read the requirements.yaml file. If
// the file does not exist, it creates a new requirements.yaml
// with the provided dependency.
func (m *Manager) Create(name string, repo string, version string) error {
c, err := m.loadChartDir()
if err != nil {
return err
}
// If no requirements file is found, we will create the file and
// add the dependency.
req, err := chartutil.LoadRequirements(c)
if err != nil {
if err == chartutil.ErrRequirementsNotFound {
fmt.Fprintf(m.Out, "No requirements found in %s/charts, creating new requirements file\n", m.ChartPath)
} else {
return err
}
}
if req != nil {
deps := req.Dependencies
d := requirementsHasDependency(deps, name)
// If dep already exists, edit it. Otherwise add to end of file
if d != nil {
d.Repository = repo
d.Version = version
} else {
req.Dependencies = append(req.Dependencies, &chartutil.Dependency{
Name: name, Version: version, Repository: repo})
}
} else {
req = &chartutil.Requirements{
Dependencies: []*chartutil.Dependency{
{Name: name, Version: version, Repository: repo},
},
}
}
return writeRequirements(m.ChartPath, req)
}
func (m *Manager) loadChartDir() (*chart.Chart, error) {
if fi, err := os.Stat(m.ChartPath); err != nil {
return nil, fmt.Errorf("could not find %s: %s", m.ChartPath, err)
@ -519,6 +564,16 @@ func (m *Manager) loadChartRepositories() (map[string]*repo.ChartRepository, err
return indices, nil
}
// writeRequirements writes a requirements.yaml
func writeRequirements(chartpath string, reqs *chartutil.Requirements) error {
data, err := yaml.Marshal(reqs)
if err != nil {
return err
}
dest := filepath.Join(chartpath, "requirements.yaml")
return ioutil.WriteFile(dest, data, 0664)
}
// writeLock writes a lockfile to disk
func writeLock(chartpath string, lock *chartutil.RequirementsLock) error {
data, err := yaml.Marshal(lock)
@ -570,3 +625,12 @@ func tarFromLocalDir(chartpath string, name string, repo string, version string)
return "", fmt.Errorf("Can't get a valid version for dependency %s.", name)
}
func requirementsHasDependency(deps []*chartutil.Dependency, newDepName string) *chartutil.Dependency {
for _, dep := range deps {
if dep.Name == newDepName {
return dep
}
}
return nil
}

@ -74,7 +74,7 @@ type Metadata struct {
// The condition to check to enable chart
Condition string `protobuf:"bytes,11,opt,name=condition" json:"condition,omitempty"`
// The tags to check to enable chart
Tags []string `protobuf:"bytes,12,opt,name=tags" json:"tags,omitempty"`
Tags string `protobuf:"bytes,12,opt,name=tags" json:"tags,omitempty"`
}
func (m *Metadata) Reset() { *m = Metadata{} }

@ -141,7 +141,7 @@ func TestResolve(t *testing.T) {
}
func TestHashReq(t *testing.T) {
expect := "sha256:c8250374210bd909cef274be64f871bd4e376d4ecd34a1589b5abf90b68866ba"
expect := "sha256:e70e41f8922e19558a8bf62f591a8b70c8e4622e3c03e5415f09aba881f13885"
req := &chartutil.Requirements{
Dependencies: []*chartutil.Dependency{
{Name: "alpine", Version: "0.1.0", Repository: "http://localhost:8879/charts"},

Loading…
Cancel
Save