Add an option which builds recursively the dependent chart

Signed-off-by: Cosmin Cojocar <cosmin.cojocar@gmx.ch>
pull/4468/head
Cosmin Cojocar 7 years ago
parent 9689b02321
commit 3ca69e7b50

@ -41,6 +41,7 @@ type dependencyBuildCmd struct {
chartpath string
verify bool
keyring string
recursive bool
helmhome helmpath.Home
}
@ -65,6 +66,7 @@ func newDependencyBuildCmd(out io.Writer) *cobra.Command {
f := cmd.Flags()
f.BoolVar(&dbc.verify, "verify", false, "verify the packages against signatures")
f.StringVar(&dbc.keyring, "keyring", defaultKeyring(), "keyring containing public keys")
f.BoolVar(&dbc.recursive, "recursive", false, "run build recursively for the dependent charts")
return cmd
}
@ -75,11 +77,16 @@ func (d *dependencyBuildCmd) run() error {
ChartPath: d.chartpath,
HelmHome: d.helmhome,
Keyring: d.keyring,
Recursive: d.recursive,
Getters: getter.All(settings),
}
if d.verify {
man.Verify = downloader.VerifyIfPossible
}
return man.Build()
if d.recursive {
return man.BuildRecursively()
}
_, err := man.Build()
return err
}

@ -16,13 +16,19 @@ limitations under the License.
package main
import (
"archive/tar"
"bytes"
"compress/gzip"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"testing"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/helm/helmpath"
"k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/provenance"
"k8s.io/helm/pkg/repo"
"k8s.io/helm/pkg/repo/repotest"
@ -116,5 +122,132 @@ func TestDependencyBuildCmd(t *testing.T) {
if v := reqver.Version; v != "0.1.0" {
t.Errorf("mismatched versions. Expected %q, got %q", "0.1.0", v)
}
}
func TestDependencyRecursiveBuildCmd(t *testing.T) {
hh, err := tempHelmHome(t)
if err != nil {
t.Fatal(err)
}
cleanup := resetEnv()
defer func() {
os.RemoveAll(hh.String())
cleanup()
}()
settings.Home = hh
srv := repotest.NewServer(hh.String())
defer srv.Stop()
chartname := "rectest"
if err := createTestingChartWithRecursiveDep(hh.String(), chartname, srv); err != nil {
t.Fatal(err)
}
out := bytes.NewBuffer(nil)
dbc := &dependencyBuildCmd{out: out}
dbc.helmhome = helmpath.Home(hh)
dbc.chartpath = filepath.Join(hh.String(), chartname)
dbc.recursive = true
if err := dbc.run(); err != nil {
output := out.String()
t.Logf("Output: %s", output)
t.Fatal(err)
}
expect := filepath.Join(hh.String(), chartname, "charts/rectest1-0.1.0.tgz")
if _, err := os.Stat(expect); err != nil {
t.Fatal(err)
}
expect = filepath.Join(hh.String(), chartname, "charts/rectest1/charts/rectest2-0.1.0.tgz")
if _, err := os.Stat(expect); err != nil {
t.Fatal(err)
}
}
func createTestingChartWithRecursiveDep(dest string, name string, srv *repotest.Server) error {
// Create the deepest chart without any dependency
rectestChart2 := "rectest2"
rectestChart2Version := "0.1.0"
err := createAndSaveTestingChart(dest, rectestChart2, rectestChart2Version, nil)
if err != nil {
return err
}
rectestChart1 := "rectest1"
rectestChart1Version := "0.1.0"
err = createAndSaveTestingChart(dest, rectestChart1, rectestChart1Version,
[]*chartutil.Dependency{
{Name: rectestChart2, Version: rectestChart2Version, Repository: srv.URL()}},
)
if err != nil {
return err
}
_, err = srv.CopyCharts(filepath.Join(dest, "/*.tgz"))
if err != nil {
return err
}
return createAndSaveTestingChart(dest, name, "0.1.0",
[]*chartutil.Dependency{
{Name: rectestChart1, Version: rectestChart1Version, Repository: srv.URL()}},
)
}
func createAndSaveTestingChart(dest string, name, version string, deps []*chartutil.Dependency) error {
cfile := &chart.Metadata{
Name: name,
Version: version,
}
dir := filepath.Join(dest, name)
_, err := chartutil.Create(cfile, dest)
if err != nil {
return err
}
if len(deps) > 0 {
req := &chartutil.Requirements{
Dependencies: deps,
}
err := writeRequirements(dir, req)
if err != nil {
return err
}
}
archiveFile := filepath.Join(dest, fmt.Sprintf("%s-%s.tgz", name, version))
f, err := os.Create(archiveFile)
if err != nil {
return err
}
defer f.Close()
zipper := gzip.NewWriter(f)
defer zipper.Close()
tarball := tar.NewWriter(zipper)
defer tarball.Close()
return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
header, err := tar.FileInfoHeader(info, info.Name())
if err != nil {
return err
}
header.Name = filepath.Join(filepath.Base(dir), strings.TrimPrefix(path, dir))
if err := tarball.WriteHeader(header); err != nil {
return err
}
if info.IsDir() {
return nil
}
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(tarball, file)
return err
})
}

@ -24,6 +24,7 @@ helm dependency build [flags] CHART
```
-h, --help help for build
--keyring string keyring containing public keys (default "~/.gnupg/pubring.gpg")
--recursive run build recursively for the dependent charts
--verify verify the packages against signatures
```

@ -32,6 +32,9 @@ of 'helm dependency update'.
\fB\-\-keyring\fP="~/.gnupg/pubring.gpg"
keyring containing public keys
\fB\-\-recursive\fP[=false]
run build recursively for the dependent charts
.PP
\fB\-\-verify\fP[=false]
verify the packages against signatures

@ -55,6 +55,8 @@ type Manager struct {
Keyring string
// SkipUpdate indicates that the repository should not be updated first.
SkipUpdate bool
// Recursive indicates that there is a recursive build
Recursive bool
// Getter collection for the operation
Getters []getter.Provider
}
@ -64,42 +66,102 @@ type Manager struct {
// If the lockfile is not present, this will run a Manager.Update()
//
// If SkipUpdate is set, this will not update the repository.
func (m *Manager) Build() error {
func (m *Manager) Build() ([]*chartutil.Dependency, error) {
c, err := m.loadChartDir()
if err != nil {
return err
return nil, err
}
// If a lock file is found, run a build from that. Otherwise, just do
// an update.
lock, err := chartutil.LoadRequirementsLock(c)
if err != nil {
return m.Update()
err = m.Update()
if err != nil {
return nil, err
}
req, err := chartutil.LoadRequirements(c)
if err != nil {
if err == chartutil.ErrRequirementsNotFound {
return nil, nil
}
return nil, err
}
return req.Dependencies, nil
}
// A lock must accompany a requirements.yaml file.
req, err := chartutil.LoadRequirements(c)
if err != nil {
return fmt.Errorf("requirements.yaml cannot be opened: %s", err)
return nil, fmt.Errorf("requirements.yaml cannot be opened: %s", err)
}
if sum, err := resolver.HashReq(req); err != nil || sum != lock.Digest {
return fmt.Errorf("requirements.lock is out of sync with requirements.yaml")
return nil, fmt.Errorf("requirements.lock is out of sync with requirements.yaml")
}
// Check that all of the repos we're dependent on actually exist.
if err := m.hasAllRepos(lock.Dependencies); err != nil {
return err
return nil, err
}
if !m.SkipUpdate {
// For each repo in the file, update the cached copy of that repo
if err := m.UpdateRepositories(); err != nil {
return err
return nil, err
}
}
// Now we need to fetch every package here into charts/
return m.downloadAll(lock.Dependencies)
return req.Dependencies, m.downloadAll(lock.Dependencies)
}
// BuildRecursively rebuilds recursively a local charts directory from a lockfile.
// If the lockfile is not present, this will run a Manager.Update()
//
// If SkipUpdate is set, this will not update the repository.
func (m *Manager) BuildRecursively() error {
// Build the main chart
dependencies, err := m.Build()
if err != nil {
return err
}
m.SkipUpdate = true
baseChartPath := filepath.Join(m.ChartPath, "charts")
// Do a Breadth-first traversal over the chart dependencies and
// build them on by one
type chartDep struct {
path string
deps []*chartutil.Dependency
}
depQueue := []chartDep{{
path: baseChartPath,
deps: dependencies,
}}
for {
if len(depQueue) == 0 {
break
}
currChartDep := depQueue[0]
depQueue = depQueue[1:]
for _, dep := range currChartDep.deps {
chartPath := filepath.Join(currChartDep.path, dep.Name)
m.ChartPath = chartPath
newDeps, err := m.Build()
if err != nil {
return err
}
if len(newDeps) > 0 {
depQueue = append(depQueue, chartDep{
path: filepath.Join(chartPath, "charts"),
deps: newDeps,
})
}
}
}
return nil
}
// Update updates a local charts directory.
@ -253,6 +315,15 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error {
saveError = fmt.Errorf("could not download %s: %s", churl, err)
break
}
if m.Recursive {
tarPath := filepath.Join(destPath, filepath.Base(churl))
fmt.Fprintln(m.Out, "Unpacking:", tarPath)
err := chartutil.ExpandFile(destPath, tarPath)
if err != nil {
return fmt.Errorf("could not unpack %s: %s", tarPath, err)
}
}
}
if saveError == nil {
@ -647,6 +718,12 @@ func move(tmpPath, destPath string) error {
filename := file.Name()
tmpfile := filepath.Join(tmpPath, filename)
destfile := filepath.Join(destPath, filename)
if _, err := os.Stat(destfile); err == nil {
err := os.RemoveAll(destfile)
if err != nil {
return err
}
}
if err := os.Rename(tmpfile, destfile); err != nil {
return fmt.Errorf("Unable to move local charts to charts dir: %v", err)
}

@ -327,6 +327,8 @@ _helm_dependency_build()
flags+=("--keyring=")
local_nonpersistent_flags+=("--keyring=")
flags+=("--recursive")
local_nonpersistent_flags+=("--recursive")
flags+=("--verify")
local_nonpersistent_flags+=("--verify")
flags+=("--debug")

Loading…
Cancel
Save