mirror of https://github.com/helm/helm
Signed-off-by: Matthew Fisher <matt.fisher@microsoft.com>pull/5635/head
commit
d0e046549f
@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The Helm Authors.
|
|
||||||
|
|
||||||
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"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
"helm.sh/helm/cmd/helm/require"
|
|
||||||
"helm.sh/helm/pkg/action"
|
|
||||||
)
|
|
||||||
|
|
||||||
const chartPullDesc = `
|
|
||||||
Download a chart from a remote registry.
|
|
||||||
|
|
||||||
This will store the chart in the local registry cache to be used later.
|
|
||||||
`
|
|
||||||
|
|
||||||
func newChartPullCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|
||||||
return &cobra.Command{
|
|
||||||
Use: "pull [ref]",
|
|
||||||
Short: "pull a chart from remote",
|
|
||||||
Long: chartPullDesc,
|
|
||||||
Args: require.MinimumNArgs(1),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
ref := args[0]
|
|
||||||
return action.NewChartPull(cfg).Run(out, ref)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The Helm Authors.
|
|
||||||
|
|
||||||
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"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
"helm.sh/helm/cmd/helm/require"
|
|
||||||
"helm.sh/helm/pkg/action"
|
|
||||||
)
|
|
||||||
|
|
||||||
const chartPushDesc = `
|
|
||||||
Upload a chart to a remote registry.
|
|
||||||
|
|
||||||
Note: the ref must already exist in the local registry cache.
|
|
||||||
|
|
||||||
Must first run "helm chart save" or "helm chart pull".
|
|
||||||
`
|
|
||||||
|
|
||||||
func newChartPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|
||||||
return &cobra.Command{
|
|
||||||
Use: "push [ref]",
|
|
||||||
Short: "push a chart to remote",
|
|
||||||
Long: chartPushDesc,
|
|
||||||
Args: require.MinimumNArgs(1),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
ref := args[0]
|
|
||||||
return action.NewChartPush(cfg).Run(out, ref)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The Helm Authors.
|
|
||||||
|
|
||||||
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"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
"helm.sh/helm/cmd/helm/require"
|
|
||||||
"helm.sh/helm/pkg/action"
|
|
||||||
)
|
|
||||||
|
|
||||||
const chartSaveDesc = `
|
|
||||||
Store a copy of chart in local registry cache.
|
|
||||||
|
|
||||||
Note: modifying the chart after this operation will
|
|
||||||
not change the item as it exists in the cache.
|
|
||||||
`
|
|
||||||
|
|
||||||
func newChartSaveCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|
||||||
return &cobra.Command{
|
|
||||||
Use: "save [path] [ref]",
|
|
||||||
Short: "save a chart directory",
|
|
||||||
Long: chartSaveDesc,
|
|
||||||
Args: require.MinimumNArgs(2),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
path := args[0]
|
|
||||||
ref := args[1]
|
|
||||||
return action.NewChartSave(cfg).Run(out, path, ref)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
Copyright The Helm Authors.
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"helm.sh/helm/cmd/helm/require"
|
||||||
|
"helm.sh/helm/pkg/action"
|
||||||
|
)
|
||||||
|
|
||||||
|
const pushDesc = `
|
||||||
|
Upload a chart to a registry.
|
||||||
|
|
||||||
|
This command will take a distributable, compressed chart and upload it to a registry.
|
||||||
|
|
||||||
|
The chart must already exist in the local registry cache, which can be created with either "helm package" or "helm pull".
|
||||||
|
|
||||||
|
Some registries require you to be authenticated with "helm login" before uploading a chart.
|
||||||
|
`
|
||||||
|
|
||||||
|
func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "push [...]",
|
||||||
|
Short: "upload a chart to a registry",
|
||||||
|
Long: pushDesc,
|
||||||
|
Args: require.MinimumNArgs(1),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) == 0 {
|
||||||
|
return errors.Errorf("need at least one argument: the name of the chart to push")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(args); i++ {
|
||||||
|
ref := args[i]
|
||||||
|
if err := action.NewChartPush(cfg).Run(out, ref); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The Helm Authors.
|
|
||||||
|
|
||||||
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"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
"helm.sh/helm/cmd/helm/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
var repoHelm = `
|
|
||||||
This command consists of multiple subcommands to interact with chart repositories.
|
|
||||||
|
|
||||||
It can be used to add, remove, list, and index chart repositories.
|
|
||||||
Example usage:
|
|
||||||
$ helm repo add [NAME] [REPO_URL]
|
|
||||||
`
|
|
||||||
|
|
||||||
func newRepoCmd(out io.Writer) *cobra.Command {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "repo add|remove|list|index|update [ARGS]",
|
|
||||||
Short: "add, list, remove, update, and index chart repositories",
|
|
||||||
Long: repoHelm,
|
|
||||||
Args: require.NoArgs,
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.AddCommand(newRepoAddCmd(out))
|
|
||||||
cmd.AddCommand(newRepoListCmd(out))
|
|
||||||
cmd.AddCommand(newRepoRemoveCmd(out))
|
|
||||||
cmd.AddCommand(newRepoIndexCmd(out))
|
|
||||||
cmd.AddCommand(newRepoUpdateCmd(out))
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
@ -1,114 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The Helm Authors.
|
|
||||||
|
|
||||||
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 (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
"helm.sh/helm/cmd/helm/require"
|
|
||||||
"helm.sh/helm/pkg/getter"
|
|
||||||
"helm.sh/helm/pkg/helmpath"
|
|
||||||
"helm.sh/helm/pkg/repo"
|
|
||||||
)
|
|
||||||
|
|
||||||
type repoAddOptions struct {
|
|
||||||
name string
|
|
||||||
url string
|
|
||||||
username string
|
|
||||||
password string
|
|
||||||
home helmpath.Home
|
|
||||||
noupdate bool
|
|
||||||
|
|
||||||
certFile string
|
|
||||||
keyFile string
|
|
||||||
caFile string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRepoAddCmd(out io.Writer) *cobra.Command {
|
|
||||||
o := &repoAddOptions{}
|
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "add [NAME] [URL]",
|
|
||||||
Short: "add a chart repository",
|
|
||||||
Args: require.ExactArgs(2),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
o.name = args[0]
|
|
||||||
o.url = args[1]
|
|
||||||
o.home = settings.Home
|
|
||||||
|
|
||||||
return o.run(out)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
f := cmd.Flags()
|
|
||||||
f.StringVar(&o.username, "username", "", "chart repository username")
|
|
||||||
f.StringVar(&o.password, "password", "", "chart repository password")
|
|
||||||
f.BoolVar(&o.noupdate, "no-update", false, "raise error if repo is already registered")
|
|
||||||
f.StringVar(&o.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
|
|
||||||
f.StringVar(&o.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
|
|
||||||
f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *repoAddOptions) run(out io.Writer) error {
|
|
||||||
if err := addRepository(o.name, o.url, o.username, o.password, o.home, o.certFile, o.keyFile, o.caFile, o.noupdate); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(out, "%q has been added to your repositories\n", o.name)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func addRepository(name, url, username, password string, home helmpath.Home, certFile, keyFile, caFile string, noUpdate bool) error {
|
|
||||||
f, err := repo.LoadFile(home.RepositoryFile())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if noUpdate && f.Has(name) {
|
|
||||||
return errors.Errorf("repository name (%s) already exists, please specify a different name", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
cif := home.CacheIndex(name)
|
|
||||||
c := repo.Entry{
|
|
||||||
Name: name,
|
|
||||||
Cache: cif,
|
|
||||||
URL: url,
|
|
||||||
Username: username,
|
|
||||||
Password: password,
|
|
||||||
CertFile: certFile,
|
|
||||||
KeyFile: keyFile,
|
|
||||||
CAFile: caFile,
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := repo.NewChartRepository(&c, getter.All(settings))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := r.DownloadIndexFile(home.Cache()); err != nil {
|
|
||||||
return errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", url)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Update(&c)
|
|
||||||
|
|
||||||
return f.WriteFile(home.RepositoryFile(), 0644)
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The Helm Authors.
|
|
||||||
|
|
||||||
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 (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"helm.sh/helm/pkg/repo"
|
|
||||||
"helm.sh/helm/pkg/repo/repotest"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRepoAddCmd(t *testing.T) {
|
|
||||||
defer resetEnv()()
|
|
||||||
|
|
||||||
srv, hh, err := repotest.NewTempServer("testdata/testserver/*.*")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
srv.Stop()
|
|
||||||
os.RemoveAll(hh.String())
|
|
||||||
}()
|
|
||||||
ensureTestHome(t, hh)
|
|
||||||
settings.Home = hh
|
|
||||||
|
|
||||||
tests := []cmdTestCase{{
|
|
||||||
name: "add a repository",
|
|
||||||
cmd: fmt.Sprintf("repo add test-name %s --home '%s'", srv.URL(), hh),
|
|
||||||
golden: "output/repo-add.txt",
|
|
||||||
}}
|
|
||||||
|
|
||||||
runTestCmd(t, tests)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRepoAdd(t *testing.T) {
|
|
||||||
defer resetEnv()()
|
|
||||||
|
|
||||||
ts, hh, err := repotest.NewTempServer("testdata/testserver/*.*")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
ts.Stop()
|
|
||||||
os.RemoveAll(hh.String())
|
|
||||||
}()
|
|
||||||
ensureTestHome(t, hh)
|
|
||||||
settings.Home = hh
|
|
||||||
|
|
||||||
const testRepoName = "test-name"
|
|
||||||
|
|
||||||
if err := addRepository(testRepoName, ts.URL(), "", "", hh, "", "", "", true); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := repo.LoadFile(hh.RepositoryFile())
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !f.Has(testRepoName) {
|
|
||||||
t.Errorf("%s was not successfully inserted into %s", testRepoName, hh.RepositoryFile())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := addRepository(testRepoName, ts.URL(), "", "", hh, "", "", "", false); err != nil {
|
|
||||||
t.Errorf("Repository was not updated: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := addRepository(testRepoName, ts.URL(), "", "", hh, "", "", "", false); err != nil {
|
|
||||||
t.Errorf("Duplicate repository name was added")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,101 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The Helm Authors.
|
|
||||||
|
|
||||||
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"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
"helm.sh/helm/cmd/helm/require"
|
|
||||||
"helm.sh/helm/pkg/repo"
|
|
||||||
)
|
|
||||||
|
|
||||||
const repoIndexDesc = `
|
|
||||||
Read the current directory and generate an index file based on the charts found.
|
|
||||||
|
|
||||||
This tool is used for creating an 'index.yaml' file for a chart repository. To
|
|
||||||
set an absolute URL to the charts, use '--url' flag.
|
|
||||||
|
|
||||||
To merge the generated index with an existing index file, use the '--merge'
|
|
||||||
flag. In this case, the charts found in the current directory will be merged
|
|
||||||
into the existing index, with local charts taking priority over existing charts.
|
|
||||||
`
|
|
||||||
|
|
||||||
type repoIndexOptions struct {
|
|
||||||
dir string
|
|
||||||
url string
|
|
||||||
merge string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRepoIndexCmd(out io.Writer) *cobra.Command {
|
|
||||||
o := &repoIndexOptions{}
|
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "index [DIR]",
|
|
||||||
Short: "generate an index file given a directory containing packaged charts",
|
|
||||||
Long: repoIndexDesc,
|
|
||||||
Args: require.ExactArgs(1),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
o.dir = args[0]
|
|
||||||
return o.run(out)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
f := cmd.Flags()
|
|
||||||
f.StringVar(&o.url, "url", "", "url of chart repository")
|
|
||||||
f.StringVar(&o.merge, "merge", "", "merge the generated index into the given index")
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *repoIndexOptions) run(out io.Writer) error {
|
|
||||||
path, err := filepath.Abs(i.dir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return index(path, i.url, i.merge)
|
|
||||||
}
|
|
||||||
|
|
||||||
func index(dir, url, mergeTo string) error {
|
|
||||||
out := filepath.Join(dir, "index.yaml")
|
|
||||||
|
|
||||||
i, err := repo.IndexDirectory(dir, url)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if mergeTo != "" {
|
|
||||||
// if index.yaml is missing then create an empty one to merge into
|
|
||||||
var i2 *repo.IndexFile
|
|
||||||
if _, err := os.Stat(mergeTo); os.IsNotExist(err) {
|
|
||||||
i2 = repo.NewIndexFile()
|
|
||||||
i2.WriteFile(mergeTo, 0755)
|
|
||||||
} else {
|
|
||||||
i2, err = repo.LoadIndexFile(mergeTo)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "merge failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i.Merge(i2)
|
|
||||||
}
|
|
||||||
i.SortEntries()
|
|
||||||
return i.WriteFile(out, 0755)
|
|
||||||
}
|
|
@ -1,166 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The Helm Authors.
|
|
||||||
|
|
||||||
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"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"helm.sh/helm/pkg/repo"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRepoIndexCmd(t *testing.T) {
|
|
||||||
|
|
||||||
dir := testTempDir(t)
|
|
||||||
|
|
||||||
comp := filepath.Join(dir, "compressedchart-0.1.0.tgz")
|
|
||||||
if err := linkOrCopy("testdata/testcharts/compressedchart-0.1.0.tgz", comp); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
comp2 := filepath.Join(dir, "compressedchart-0.2.0.tgz")
|
|
||||||
if err := linkOrCopy("testdata/testcharts/compressedchart-0.2.0.tgz", comp2); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
c := newRepoIndexCmd(buf)
|
|
||||||
|
|
||||||
if err := c.RunE(c, []string{dir}); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
destIndex := filepath.Join(dir, "index.yaml")
|
|
||||||
|
|
||||||
index, err := repo.LoadIndexFile(destIndex)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(index.Entries) != 1 {
|
|
||||||
t.Errorf("expected 1 entry, got %d: %#v", len(index.Entries), index.Entries)
|
|
||||||
}
|
|
||||||
|
|
||||||
vs := index.Entries["compressedchart"]
|
|
||||||
if len(vs) != 2 {
|
|
||||||
t.Errorf("expected 2 versions, got %d: %#v", len(vs), vs)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedVersion := "0.2.0"
|
|
||||||
if vs[0].Version != expectedVersion {
|
|
||||||
t.Errorf("expected %q, got %q", expectedVersion, vs[0].Version)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test with `--merge`
|
|
||||||
|
|
||||||
// Remove first two charts.
|
|
||||||
if err := os.Remove(comp); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := os.Remove(comp2); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// Add a new chart and a new version of an existing chart
|
|
||||||
if err := linkOrCopy("testdata/testcharts/reqtest-0.1.0.tgz", filepath.Join(dir, "reqtest-0.1.0.tgz")); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := linkOrCopy("testdata/testcharts/compressedchart-0.3.0.tgz", filepath.Join(dir, "compressedchart-0.3.0.tgz")); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.ParseFlags([]string{"--merge", destIndex})
|
|
||||||
if err := c.RunE(c, []string{dir}); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
index, err = repo.LoadIndexFile(destIndex)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(index.Entries) != 2 {
|
|
||||||
t.Errorf("expected 2 entries, got %d: %#v", len(index.Entries), index.Entries)
|
|
||||||
}
|
|
||||||
|
|
||||||
vs = index.Entries["compressedchart"]
|
|
||||||
if len(vs) != 3 {
|
|
||||||
t.Errorf("expected 3 versions, got %d: %#v", len(vs), vs)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedVersion = "0.3.0"
|
|
||||||
if vs[0].Version != expectedVersion {
|
|
||||||
t.Errorf("expected %q, got %q", expectedVersion, vs[0].Version)
|
|
||||||
}
|
|
||||||
|
|
||||||
// test that index.yaml gets generated on merge even when it doesn't exist
|
|
||||||
if err := os.Remove(destIndex); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.ParseFlags([]string{"--merge", destIndex})
|
|
||||||
if err := c.RunE(c, []string{dir}); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = repo.LoadIndexFile(destIndex)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify it didn't create an empty index.yaml and the merged happened
|
|
||||||
if len(index.Entries) != 2 {
|
|
||||||
t.Errorf("expected 2 entries, got %d: %#v", len(index.Entries), index.Entries)
|
|
||||||
}
|
|
||||||
|
|
||||||
vs = index.Entries["compressedchart"]
|
|
||||||
if len(vs) != 3 {
|
|
||||||
t.Errorf("expected 3 versions, got %d: %#v", len(vs), vs)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedVersion = "0.3.0"
|
|
||||||
if vs[0].Version != expectedVersion {
|
|
||||||
t.Errorf("expected %q, got %q", expectedVersion, vs[0].Version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func linkOrCopy(old, new string) error {
|
|
||||||
if err := os.Link(old, new); err != nil {
|
|
||||||
return copyFile(old, new)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyFile(dst, src string) error {
|
|
||||||
i, err := os.Open(dst)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer i.Close()
|
|
||||||
|
|
||||||
o, err := os.Create(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer o.Close()
|
|
||||||
|
|
||||||
_, err = io.Copy(o, i)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The Helm Authors.
|
|
||||||
|
|
||||||
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 (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/gosuri/uitable"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
"helm.sh/helm/cmd/helm/require"
|
|
||||||
"helm.sh/helm/pkg/helmpath"
|
|
||||||
"helm.sh/helm/pkg/repo"
|
|
||||||
)
|
|
||||||
|
|
||||||
type repoListOptions struct {
|
|
||||||
home helmpath.Home
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRepoListCmd(out io.Writer) *cobra.Command {
|
|
||||||
o := &repoListOptions{}
|
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "list",
|
|
||||||
Short: "list chart repositories",
|
|
||||||
Args: require.NoArgs,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
o.home = settings.Home
|
|
||||||
return o.run(out)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *repoListOptions) run(out io.Writer) error {
|
|
||||||
f, err := repo.LoadFile(o.home.RepositoryFile())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(f.Repositories) == 0 {
|
|
||||||
return errors.New("no repositories to show")
|
|
||||||
}
|
|
||||||
table := uitable.New()
|
|
||||||
table.AddRow("NAME", "URL")
|
|
||||||
for _, re := range f.Repositories {
|
|
||||||
table.AddRow(re.Name, re.URL)
|
|
||||||
}
|
|
||||||
fmt.Fprintln(out, table)
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The Helm Authors.
|
|
||||||
|
|
||||||
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 (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
"helm.sh/helm/cmd/helm/require"
|
|
||||||
"helm.sh/helm/pkg/helmpath"
|
|
||||||
"helm.sh/helm/pkg/repo"
|
|
||||||
)
|
|
||||||
|
|
||||||
type repoRemoveOptions struct {
|
|
||||||
name string
|
|
||||||
home helmpath.Home
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRepoRemoveCmd(out io.Writer) *cobra.Command {
|
|
||||||
o := &repoRemoveOptions{}
|
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "remove [NAME]",
|
|
||||||
Aliases: []string{"rm"},
|
|
||||||
Short: "remove a chart repository",
|
|
||||||
Args: require.ExactArgs(1),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
o.name = args[0]
|
|
||||||
o.home = settings.Home
|
|
||||||
|
|
||||||
return o.run(out)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *repoRemoveOptions) run(out io.Writer) error {
|
|
||||||
return removeRepoLine(out, r.name, r.home)
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeRepoLine(out io.Writer, name string, home helmpath.Home) error {
|
|
||||||
repoFile := home.RepositoryFile()
|
|
||||||
r, err := repo.LoadFile(repoFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !r.Remove(name) {
|
|
||||||
return errors.Errorf("no repo named %q found", name)
|
|
||||||
}
|
|
||||||
if err := r.WriteFile(repoFile, 0644); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := removeRepoCache(name, home); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(out, "%q has been removed from your repositories\n", name)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeRepoCache(name string, home helmpath.Home) error {
|
|
||||||
if _, err := os.Stat(home.CacheIndex(name)); err == nil {
|
|
||||||
err = os.Remove(home.CacheIndex(name))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The Helm Authors.
|
|
||||||
|
|
||||||
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"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"helm.sh/helm/pkg/repo"
|
|
||||||
"helm.sh/helm/pkg/repo/repotest"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRepoRemove(t *testing.T) {
|
|
||||||
defer resetEnv()()
|
|
||||||
|
|
||||||
ts, hh, err := repotest.NewTempServer("testdata/testserver/*.*")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
ts.Stop()
|
|
||||||
os.RemoveAll(hh.String())
|
|
||||||
}()
|
|
||||||
ensureTestHome(t, hh)
|
|
||||||
settings.Home = hh
|
|
||||||
|
|
||||||
const testRepoName = "test-name"
|
|
||||||
|
|
||||||
b := bytes.NewBuffer(nil)
|
|
||||||
if err := removeRepoLine(b, testRepoName, hh); err == nil {
|
|
||||||
t.Errorf("Expected error removing %s, but did not get one.", testRepoName)
|
|
||||||
}
|
|
||||||
if err := addRepository(testRepoName, ts.URL(), "", "", hh, "", "", "", true); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
mf, _ := os.Create(hh.CacheIndex(testRepoName))
|
|
||||||
mf.Close()
|
|
||||||
|
|
||||||
b.Reset()
|
|
||||||
if err := removeRepoLine(b, testRepoName, hh); err != nil {
|
|
||||||
t.Errorf("Error removing %s from repositories", testRepoName)
|
|
||||||
}
|
|
||||||
if !strings.Contains(b.String(), "has been removed") {
|
|
||||||
t.Errorf("Unexpected output: %s", b.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := os.Stat(hh.CacheIndex(testRepoName)); err == nil {
|
|
||||||
t.Errorf("Error cache file was not removed for repository %s", testRepoName)
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := repo.LoadFile(hh.RepositoryFile())
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Has(testRepoName) {
|
|
||||||
t.Errorf("%s was not successfully removed from repositories list", testRepoName)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,103 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The Helm Authors.
|
|
||||||
|
|
||||||
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 (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
"helm.sh/helm/cmd/helm/require"
|
|
||||||
"helm.sh/helm/pkg/getter"
|
|
||||||
"helm.sh/helm/pkg/helmpath"
|
|
||||||
"helm.sh/helm/pkg/repo"
|
|
||||||
)
|
|
||||||
|
|
||||||
const updateDesc = `
|
|
||||||
Update gets the latest information about charts from the respective chart repositories.
|
|
||||||
Information is cached locally, where it is used by commands like 'helm search'.
|
|
||||||
|
|
||||||
'helm update' is the deprecated form of 'helm repo update'. It will be removed in
|
|
||||||
future releases.
|
|
||||||
`
|
|
||||||
|
|
||||||
var errNoRepositories = errors.New("no repositories found. You must add one before updating")
|
|
||||||
|
|
||||||
type repoUpdateOptions struct {
|
|
||||||
update func([]*repo.ChartRepository, io.Writer, helmpath.Home)
|
|
||||||
home helmpath.Home
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRepoUpdateCmd(out io.Writer) *cobra.Command {
|
|
||||||
o := &repoUpdateOptions{update: updateCharts}
|
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "update",
|
|
||||||
Aliases: []string{"up"},
|
|
||||||
Short: "update information of available charts locally from chart repositories",
|
|
||||||
Long: updateDesc,
|
|
||||||
Args: require.NoArgs,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
o.home = settings.Home
|
|
||||||
return o.run(out)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *repoUpdateOptions) run(out io.Writer) error {
|
|
||||||
f, err := repo.LoadFile(o.home.RepositoryFile())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(f.Repositories) == 0 {
|
|
||||||
return errNoRepositories
|
|
||||||
}
|
|
||||||
var repos []*repo.ChartRepository
|
|
||||||
for _, cfg := range f.Repositories {
|
|
||||||
r, err := repo.NewChartRepository(cfg, getter.All(settings))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
repos = append(repos, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
o.update(repos, out, o.home)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateCharts(repos []*repo.ChartRepository, out io.Writer, home helmpath.Home) {
|
|
||||||
fmt.Fprintln(out, "Hang tight while we grab the latest from your chart repositories...")
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
for _, re := range repos {
|
|
||||||
wg.Add(1)
|
|
||||||
go func(re *repo.ChartRepository) {
|
|
||||||
defer wg.Done()
|
|
||||||
if err := re.DownloadIndexFile(home.Cache()); err != nil {
|
|
||||||
fmt.Fprintf(out, "...Unable to get an update from the %q chart repository (%s):\n\t%s\n", re.Config.Name, re.Config.URL, err)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(out, "...Successfully got an update from the %q chart repository\n", re.Config.Name)
|
|
||||||
}
|
|
||||||
}(re)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
fmt.Fprintln(out, "Update Complete. ⎈ Happy Helming!⎈ ")
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The Helm Authors.
|
|
||||||
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"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"helm.sh/helm/pkg/getter"
|
|
||||||
"helm.sh/helm/pkg/helmpath"
|
|
||||||
"helm.sh/helm/pkg/repo"
|
|
||||||
"helm.sh/helm/pkg/repo/repotest"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestUpdateCmd(t *testing.T) {
|
|
||||||
defer resetEnv()()
|
|
||||||
|
|
||||||
hh := testHelmHome(t)
|
|
||||||
settings.Home = hh
|
|
||||||
|
|
||||||
out := bytes.NewBuffer(nil)
|
|
||||||
// Instead of using the HTTP updater, we provide our own for this test.
|
|
||||||
// The TestUpdateCharts test verifies the HTTP behavior independently.
|
|
||||||
updater := func(repos []*repo.ChartRepository, out io.Writer, hh helmpath.Home) {
|
|
||||||
for _, re := range repos {
|
|
||||||
fmt.Fprintln(out, re.Config.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
o := &repoUpdateOptions{
|
|
||||||
update: updater,
|
|
||||||
home: hh,
|
|
||||||
}
|
|
||||||
if err := o.run(out); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if got := out.String(); !strings.Contains(got, "charts") {
|
|
||||||
t.Errorf("Expected 'charts' got %q", got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdateCharts(t *testing.T) {
|
|
||||||
defer resetEnv()()
|
|
||||||
|
|
||||||
ts, hh, err := repotest.NewTempServer("testdata/testserver/*.*")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
ts.Stop()
|
|
||||||
os.RemoveAll(hh.String())
|
|
||||||
}()
|
|
||||||
ensureTestHome(t, hh)
|
|
||||||
settings.Home = hh
|
|
||||||
|
|
||||||
r, err := repo.NewChartRepository(&repo.Entry{
|
|
||||||
Name: "charts",
|
|
||||||
URL: ts.URL(),
|
|
||||||
Cache: hh.CacheIndex("charts"),
|
|
||||||
}, getter.All(settings))
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
b := bytes.NewBuffer(nil)
|
|
||||||
updateCharts([]*repo.ChartRepository{r}, b, hh)
|
|
||||||
|
|
||||||
got := b.String()
|
|
||||||
if strings.Contains(got, "Unable to get an update") {
|
|
||||||
t.Errorf("Failed to get a repo: %q", got)
|
|
||||||
}
|
|
||||||
if !strings.Contains(got, "Update Complete.") {
|
|
||||||
t.Error("Update was not successful")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,163 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The Helm Authors.
|
|
||||||
|
|
||||||
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 (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Masterminds/semver"
|
|
||||||
"github.com/gosuri/uitable"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
"helm.sh/helm/cmd/helm/search"
|
|
||||||
"helm.sh/helm/pkg/helmpath"
|
|
||||||
"helm.sh/helm/pkg/repo"
|
|
||||||
)
|
|
||||||
|
|
||||||
const searchDesc = `
|
|
||||||
Search reads through all of the repositories configured on the system, and
|
|
||||||
looks for matches.
|
|
||||||
|
|
||||||
Repositories are managed with 'helm repo' commands.
|
|
||||||
`
|
|
||||||
|
|
||||||
// searchMaxScore suggests that any score higher than this is not considered a match.
|
|
||||||
const searchMaxScore = 25
|
|
||||||
|
|
||||||
type searchOptions struct {
|
|
||||||
helmhome helmpath.Home
|
|
||||||
|
|
||||||
versions bool
|
|
||||||
regexp bool
|
|
||||||
version string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSearchCmd(out io.Writer) *cobra.Command {
|
|
||||||
o := &searchOptions{}
|
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "search [keyword]",
|
|
||||||
Short: "search for a keyword in charts",
|
|
||||||
Long: searchDesc,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
o.helmhome = settings.Home
|
|
||||||
return o.run(out, args)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
f := cmd.Flags()
|
|
||||||
f.BoolVarP(&o.regexp, "regexp", "r", false, "use regular expressions for searching")
|
|
||||||
f.BoolVarP(&o.versions, "versions", "l", false, "show the long listing, with each version of each chart on its own line")
|
|
||||||
f.StringVarP(&o.version, "version", "v", "", "search using semantic versioning constraints")
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *searchOptions) run(out io.Writer, args []string) error {
|
|
||||||
index, err := o.buildIndex(out)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var res []*search.Result
|
|
||||||
if len(args) == 0 {
|
|
||||||
res = index.All()
|
|
||||||
} else {
|
|
||||||
q := strings.Join(args, " ")
|
|
||||||
res, err = index.Search(q, searchMaxScore, o.regexp)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
search.SortScore(res)
|
|
||||||
data, err := o.applyConstraint(res)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(out, o.formatSearchResults(data))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *searchOptions) applyConstraint(res []*search.Result) ([]*search.Result, error) {
|
|
||||||
if len(o.version) == 0 {
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
constraint, err := semver.NewConstraint(o.version)
|
|
||||||
if err != nil {
|
|
||||||
return res, errors.Wrap(err, "an invalid version/constraint format")
|
|
||||||
}
|
|
||||||
|
|
||||||
data := res[:0]
|
|
||||||
foundNames := map[string]bool{}
|
|
||||||
for _, r := range res {
|
|
||||||
if _, found := foundNames[r.Name]; found {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
v, err := semver.NewVersion(r.Chart.Version)
|
|
||||||
if err != nil || constraint.Check(v) {
|
|
||||||
data = append(data, r)
|
|
||||||
if !o.versions {
|
|
||||||
foundNames[r.Name] = true // If user hasn't requested all versions, only show the latest that matches
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *searchOptions) formatSearchResults(res []*search.Result) string {
|
|
||||||
if len(res) == 0 {
|
|
||||||
return "No results found"
|
|
||||||
}
|
|
||||||
table := uitable.New()
|
|
||||||
table.MaxColWidth = 50
|
|
||||||
table.AddRow("NAME", "CHART VERSION", "APP VERSION", "DESCRIPTION")
|
|
||||||
for _, r := range res {
|
|
||||||
table.AddRow(r.Name, r.Chart.Version, r.Chart.AppVersion, r.Chart.Description)
|
|
||||||
}
|
|
||||||
return table.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *searchOptions) buildIndex(out io.Writer) (*search.Index, error) {
|
|
||||||
// Load the repositories.yaml
|
|
||||||
rf, err := repo.LoadFile(o.helmhome.RepositoryFile())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
i := search.NewIndex()
|
|
||||||
for _, re := range rf.Repositories {
|
|
||||||
n := re.Name
|
|
||||||
f := o.helmhome.CacheIndex(n)
|
|
||||||
ind, err := repo.LoadIndexFile(f)
|
|
||||||
if err != nil {
|
|
||||||
// TODO should print to stderr
|
|
||||||
fmt.Fprintf(out, "WARNING: Repo %q is corrupt or missing. Try 'helm repo update'.", n)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
i.AddRepo(n, ind, o.versions || len(o.version) > 0)
|
|
||||||
}
|
|
||||||
return i, nil
|
|
||||||
}
|
|
@ -1,237 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The Helm Authors.
|
|
||||||
|
|
||||||
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 search provides client-side repository searching.
|
|
||||||
|
|
||||||
This supports building an in-memory search index based on the contents of
|
|
||||||
multiple repositories, and then using string matching or regular expressions
|
|
||||||
to find matches.
|
|
||||||
*/
|
|
||||||
package search
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path"
|
|
||||||
"regexp"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Masterminds/semver"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
"helm.sh/helm/pkg/repo"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Result is a search result.
|
|
||||||
//
|
|
||||||
// Score indicates how close it is to match. The higher the score, the longer
|
|
||||||
// the distance.
|
|
||||||
type Result struct {
|
|
||||||
Name string
|
|
||||||
Score int
|
|
||||||
Chart *repo.ChartVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
// Index is a searchable index of chart information.
|
|
||||||
type Index struct {
|
|
||||||
lines map[string]string
|
|
||||||
charts map[string]*repo.ChartVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
const sep = "\v"
|
|
||||||
|
|
||||||
// NewIndex creats a new Index.
|
|
||||||
func NewIndex() *Index {
|
|
||||||
return &Index{lines: map[string]string{}, charts: map[string]*repo.ChartVersion{}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// verSep is a separator for version fields in map keys.
|
|
||||||
const verSep = "$$"
|
|
||||||
|
|
||||||
// AddRepo adds a repository index to the search index.
|
|
||||||
func (i *Index) AddRepo(rname string, ind *repo.IndexFile, all bool) {
|
|
||||||
ind.SortEntries()
|
|
||||||
for name, ref := range ind.Entries {
|
|
||||||
if len(ref) == 0 {
|
|
||||||
// Skip chart names that have zero releases.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// By convention, an index file is supposed to have the newest at the
|
|
||||||
// 0 slot, so our best bet is to grab the 0 entry and build the index
|
|
||||||
// entry off of that.
|
|
||||||
// Note: Do not use filePath.Join since on Windows it will return \
|
|
||||||
// which results in a repo name that cannot be understood.
|
|
||||||
fname := path.Join(rname, name)
|
|
||||||
if !all {
|
|
||||||
i.lines[fname] = indstr(rname, ref[0])
|
|
||||||
i.charts[fname] = ref[0]
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// If 'all' is set, then we go through all of the refs, and add them all
|
|
||||||
// to the index. This will generate a lot of near-duplicate entries.
|
|
||||||
for _, rr := range ref {
|
|
||||||
versionedName := fname + verSep + rr.Version
|
|
||||||
i.lines[versionedName] = indstr(rname, rr)
|
|
||||||
i.charts[versionedName] = rr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All returns all charts in the index as if they were search results.
|
|
||||||
//
|
|
||||||
// Each will be given a score of 0.
|
|
||||||
func (i *Index) All() []*Result {
|
|
||||||
res := make([]*Result, len(i.charts))
|
|
||||||
j := 0
|
|
||||||
for name, ch := range i.charts {
|
|
||||||
parts := strings.Split(name, verSep)
|
|
||||||
res[j] = &Result{
|
|
||||||
Name: parts[0],
|
|
||||||
Chart: ch,
|
|
||||||
}
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search searches an index for the given term.
|
|
||||||
//
|
|
||||||
// Threshold indicates the maximum score a term may have before being marked
|
|
||||||
// irrelevant. (Low score means higher relevance. Golf, not bowling.)
|
|
||||||
//
|
|
||||||
// If regexp is true, the term is treated as a regular expression. Otherwise,
|
|
||||||
// term is treated as a literal string.
|
|
||||||
func (i *Index) Search(term string, threshold int, regexp bool) ([]*Result, error) {
|
|
||||||
if regexp {
|
|
||||||
return i.SearchRegexp(term, threshold)
|
|
||||||
}
|
|
||||||
return i.SearchLiteral(term, threshold), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// calcScore calculates a score for a match.
|
|
||||||
func (i *Index) calcScore(index int, matchline string) int {
|
|
||||||
|
|
||||||
// This is currently tied to the fact that sep is a single char.
|
|
||||||
splits := []int{}
|
|
||||||
s := rune(sep[0])
|
|
||||||
for i, ch := range matchline {
|
|
||||||
if ch == s {
|
|
||||||
splits = append(splits, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, pos := range splits {
|
|
||||||
if index > pos {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
return len(splits)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SearchLiteral does a literal string search (no regexp).
|
|
||||||
func (i *Index) SearchLiteral(term string, threshold int) []*Result {
|
|
||||||
term = strings.ToLower(term)
|
|
||||||
buf := []*Result{}
|
|
||||||
for k, v := range i.lines {
|
|
||||||
lk := strings.ToLower(k)
|
|
||||||
lv := strings.ToLower(v)
|
|
||||||
res := strings.Index(lv, term)
|
|
||||||
if score := i.calcScore(res, lv); res != -1 && score < threshold {
|
|
||||||
parts := strings.Split(lk, verSep) // Remove version, if it is there.
|
|
||||||
buf = append(buf, &Result{Name: parts[0], Score: score, Chart: i.charts[k]})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
// SearchRegexp searches using a regular expression.
|
|
||||||
func (i *Index) SearchRegexp(re string, threshold int) ([]*Result, error) {
|
|
||||||
matcher, err := regexp.Compile(re)
|
|
||||||
if err != nil {
|
|
||||||
return []*Result{}, err
|
|
||||||
}
|
|
||||||
buf := []*Result{}
|
|
||||||
for k, v := range i.lines {
|
|
||||||
ind := matcher.FindStringIndex(v)
|
|
||||||
if len(ind) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if score := i.calcScore(ind[0], v); ind[0] >= 0 && score < threshold {
|
|
||||||
parts := strings.Split(k, verSep) // Remove version, if it is there.
|
|
||||||
buf = append(buf, &Result{Name: parts[0], Score: score, Chart: i.charts[k]})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chart returns the ChartVersion for a particular name.
|
|
||||||
func (i *Index) Chart(name string) (*repo.ChartVersion, error) {
|
|
||||||
c, ok := i.charts[name]
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("no such chart")
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SortScore does an in-place sort of the results.
|
|
||||||
//
|
|
||||||
// Lowest scores are highest on the list. Matching scores are subsorted alphabetically.
|
|
||||||
func SortScore(r []*Result) {
|
|
||||||
sort.Sort(scoreSorter(r))
|
|
||||||
}
|
|
||||||
|
|
||||||
// scoreSorter sorts results by score, and subsorts by alpha Name.
|
|
||||||
type scoreSorter []*Result
|
|
||||||
|
|
||||||
// Len returns the length of this scoreSorter.
|
|
||||||
func (s scoreSorter) Len() int { return len(s) }
|
|
||||||
|
|
||||||
// Swap performs an in-place swap.
|
|
||||||
func (s scoreSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
||||||
|
|
||||||
// Less compares a to b, and returns true if a is less than b.
|
|
||||||
func (s scoreSorter) Less(a, b int) bool {
|
|
||||||
first := s[a]
|
|
||||||
second := s[b]
|
|
||||||
|
|
||||||
if first.Score > second.Score {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if first.Score < second.Score {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if first.Name == second.Name {
|
|
||||||
v1, err := semver.NewVersion(first.Chart.Version)
|
|
||||||
if err != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
v2, err := semver.NewVersion(second.Chart.Version)
|
|
||||||
if err != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// Sort so that the newest chart is higher than the oldest chart. This is
|
|
||||||
// the opposite of what you'd expect in a function called Less.
|
|
||||||
return v1.GreaterThan(v2)
|
|
||||||
}
|
|
||||||
return first.Name < second.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func indstr(name string, ref *repo.ChartVersion) string {
|
|
||||||
i := ref.Name + sep + name + "/" + ref.Name + sep +
|
|
||||||
ref.Description + sep + strings.Join(ref.Keywords, " ")
|
|
||||||
return i
|
|
||||||
}
|
|
@ -1,303 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The Helm Authors.
|
|
||||||
|
|
||||||
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 search
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"helm.sh/helm/pkg/chart"
|
|
||||||
"helm.sh/helm/pkg/repo"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSortScore(t *testing.T) {
|
|
||||||
in := []*Result{
|
|
||||||
{Name: "bbb", Score: 0, Chart: &repo.ChartVersion{Metadata: &chart.Metadata{Version: "1.2.3"}}},
|
|
||||||
{Name: "aaa", Score: 5},
|
|
||||||
{Name: "abb", Score: 5},
|
|
||||||
{Name: "aab", Score: 0},
|
|
||||||
{Name: "bab", Score: 5},
|
|
||||||
{Name: "ver", Score: 5, Chart: &repo.ChartVersion{Metadata: &chart.Metadata{Version: "1.2.4"}}},
|
|
||||||
{Name: "ver", Score: 5, Chart: &repo.ChartVersion{Metadata: &chart.Metadata{Version: "1.2.3"}}},
|
|
||||||
}
|
|
||||||
expect := []string{"aab", "bbb", "aaa", "abb", "bab", "ver", "ver"}
|
|
||||||
expectScore := []int{0, 0, 5, 5, 5, 5, 5}
|
|
||||||
SortScore(in)
|
|
||||||
|
|
||||||
// Test Score
|
|
||||||
for i := 0; i < len(expectScore); i++ {
|
|
||||||
if expectScore[i] != in[i].Score {
|
|
||||||
t.Errorf("Sort error on index %d: expected %d, got %d", i, expectScore[i], in[i].Score)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Test Name
|
|
||||||
for i := 0; i < len(expect); i++ {
|
|
||||||
if expect[i] != in[i].Name {
|
|
||||||
t.Errorf("Sort error: expected %s, got %s", expect[i], in[i].Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test version of last two items
|
|
||||||
if in[5].Chart.Version != "1.2.4" {
|
|
||||||
t.Errorf("Expected 1.2.4, got %s", in[5].Chart.Version)
|
|
||||||
}
|
|
||||||
if in[6].Chart.Version != "1.2.3" {
|
|
||||||
t.Error("Expected 1.2.3 to be last")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var indexfileEntries = map[string]repo.ChartVersions{
|
|
||||||
"niña": {
|
|
||||||
{
|
|
||||||
URLs: []string{"http://example.com/charts/nina-0.1.0.tgz"},
|
|
||||||
Metadata: &chart.Metadata{
|
|
||||||
Name: "niña",
|
|
||||||
Version: "0.1.0",
|
|
||||||
Description: "One boat",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"pinta": {
|
|
||||||
{
|
|
||||||
URLs: []string{"http://example.com/charts/pinta-0.1.0.tgz"},
|
|
||||||
Metadata: &chart.Metadata{
|
|
||||||
Name: "pinta",
|
|
||||||
Version: "0.1.0",
|
|
||||||
Description: "Two ship",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"santa-maria": {
|
|
||||||
{
|
|
||||||
URLs: []string{"http://example.com/charts/santa-maria-1.2.3.tgz"},
|
|
||||||
Metadata: &chart.Metadata{
|
|
||||||
Name: "santa-maria",
|
|
||||||
Version: "1.2.3",
|
|
||||||
Description: "Three boat",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
URLs: []string{"http://example.com/charts/santa-maria-1.2.2-rc-1.tgz"},
|
|
||||||
Metadata: &chart.Metadata{
|
|
||||||
Name: "santa-maria",
|
|
||||||
Version: "1.2.2-RC-1",
|
|
||||||
Description: "Three boat",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadTestIndex(t *testing.T, all bool) *Index {
|
|
||||||
i := NewIndex()
|
|
||||||
i.AddRepo("testing", &repo.IndexFile{Entries: indexfileEntries}, all)
|
|
||||||
i.AddRepo("ztesting", &repo.IndexFile{Entries: map[string]repo.ChartVersions{
|
|
||||||
"pinta": {
|
|
||||||
{
|
|
||||||
URLs: []string{"http://example.com/charts/pinta-2.0.0.tgz"},
|
|
||||||
Metadata: &chart.Metadata{
|
|
||||||
Name: "pinta",
|
|
||||||
Version: "2.0.0",
|
|
||||||
Description: "Two ship, version two",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}, all)
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAll(t *testing.T) {
|
|
||||||
i := loadTestIndex(t, false)
|
|
||||||
all := i.All()
|
|
||||||
if len(all) != 4 {
|
|
||||||
t.Errorf("Expected 4 entries, got %d", len(all))
|
|
||||||
}
|
|
||||||
|
|
||||||
i = loadTestIndex(t, true)
|
|
||||||
all = i.All()
|
|
||||||
if len(all) != 5 {
|
|
||||||
t.Errorf("Expected 5 entries, got %d", len(all))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddRepo_Sort(t *testing.T) {
|
|
||||||
i := loadTestIndex(t, true)
|
|
||||||
sr, err := i.Search("TESTING/SANTA-MARIA", 100, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
SortScore(sr)
|
|
||||||
|
|
||||||
ch := sr[0]
|
|
||||||
expect := "1.2.3"
|
|
||||||
if ch.Chart.Version != expect {
|
|
||||||
t.Errorf("Expected %q, got %q", expect, ch.Chart.Version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSearchByName(t *testing.T) {
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
query string
|
|
||||||
expect []*Result
|
|
||||||
regexp bool
|
|
||||||
fail bool
|
|
||||||
failMsg string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "basic search for one result",
|
|
||||||
query: "santa-maria",
|
|
||||||
expect: []*Result{
|
|
||||||
{Name: "testing/santa-maria"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "basic search for two results",
|
|
||||||
query: "pinta",
|
|
||||||
expect: []*Result{
|
|
||||||
{Name: "testing/pinta"},
|
|
||||||
{Name: "ztesting/pinta"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "repo-specific search for one result",
|
|
||||||
query: "ztesting/pinta",
|
|
||||||
expect: []*Result{
|
|
||||||
{Name: "ztesting/pinta"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "partial name search",
|
|
||||||
query: "santa",
|
|
||||||
expect: []*Result{
|
|
||||||
{Name: "testing/santa-maria"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "description search, one result",
|
|
||||||
query: "Three",
|
|
||||||
expect: []*Result{
|
|
||||||
{Name: "testing/santa-maria"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "description search, two results",
|
|
||||||
query: "two",
|
|
||||||
expect: []*Result{
|
|
||||||
{Name: "testing/pinta"},
|
|
||||||
{Name: "ztesting/pinta"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "description upper search, two results",
|
|
||||||
query: "TWO",
|
|
||||||
expect: []*Result{
|
|
||||||
{Name: "testing/pinta"},
|
|
||||||
{Name: "ztesting/pinta"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "nothing found",
|
|
||||||
query: "mayflower",
|
|
||||||
expect: []*Result{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "regexp, one result",
|
|
||||||
query: "Th[ref]*",
|
|
||||||
expect: []*Result{
|
|
||||||
{Name: "testing/santa-maria"},
|
|
||||||
},
|
|
||||||
regexp: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "regexp, fail compile",
|
|
||||||
query: "th[",
|
|
||||||
expect: []*Result{},
|
|
||||||
regexp: true,
|
|
||||||
fail: true,
|
|
||||||
failMsg: "error parsing regexp:",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
i := loadTestIndex(t, false)
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
|
|
||||||
charts, err := i.Search(tt.query, 100, tt.regexp)
|
|
||||||
if err != nil {
|
|
||||||
if tt.fail {
|
|
||||||
if !strings.Contains(err.Error(), tt.failMsg) {
|
|
||||||
t.Fatalf("Unexpected error message: %s", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.Fatalf("%s: %s", tt.name, err)
|
|
||||||
}
|
|
||||||
// Give us predictably ordered results.
|
|
||||||
SortScore(charts)
|
|
||||||
|
|
||||||
l := len(charts)
|
|
||||||
if l != len(tt.expect) {
|
|
||||||
t.Fatalf("Expected %d result, got %d", len(tt.expect), l)
|
|
||||||
}
|
|
||||||
// For empty result sets, just keep going.
|
|
||||||
if l == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, got := range charts {
|
|
||||||
ex := tt.expect[i]
|
|
||||||
if got.Name != ex.Name {
|
|
||||||
t.Errorf("[%d]: Expected name %q, got %q", i, ex.Name, got.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSearchByNameAll(t *testing.T) {
|
|
||||||
// Test with the All bit turned on.
|
|
||||||
i := loadTestIndex(t, true)
|
|
||||||
cs, err := i.Search("santa-maria", 100, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(cs) != 2 {
|
|
||||||
t.Errorf("expected 2 charts, got %d", len(cs))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCalcScore(t *testing.T) {
|
|
||||||
i := NewIndex()
|
|
||||||
|
|
||||||
fields := []string{"aaa", "bbb", "ccc", "ddd"}
|
|
||||||
matchline := strings.Join(fields, sep)
|
|
||||||
if r := i.calcScore(2, matchline); r != 0 {
|
|
||||||
t.Errorf("Expected 0, got %d", r)
|
|
||||||
}
|
|
||||||
if r := i.calcScore(5, matchline); r != 1 {
|
|
||||||
t.Errorf("Expected 1, got %d", r)
|
|
||||||
}
|
|
||||||
if r := i.calcScore(10, matchline); r != 2 {
|
|
||||||
t.Errorf("Expected 2, got %d", r)
|
|
||||||
}
|
|
||||||
if r := i.calcScore(14, matchline); r != 3 {
|
|
||||||
t.Errorf("Expected 3, got %d", r)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The Helm Authors.
|
|
||||||
|
|
||||||
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 (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSearchCmd(t *testing.T) {
|
|
||||||
defer resetEnv()()
|
|
||||||
|
|
||||||
setHome := func(cmd string) string {
|
|
||||||
return cmd + " --home=testdata/helmhome"
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []cmdTestCase{{
|
|
||||||
name: "search for 'maria', expect one match",
|
|
||||||
cmd: setHome("search maria"),
|
|
||||||
golden: "output/search-single.txt",
|
|
||||||
}, {
|
|
||||||
name: "search for 'alpine', expect two matches",
|
|
||||||
cmd: setHome("search alpine"),
|
|
||||||
golden: "output/search-multiple.txt",
|
|
||||||
}, {
|
|
||||||
name: "search for 'alpine' with versions, expect three matches",
|
|
||||||
cmd: setHome("search alpine --versions"),
|
|
||||||
golden: "output/search-multiple-versions.txt",
|
|
||||||
}, {
|
|
||||||
name: "search for 'alpine' with version constraint, expect one match with version 0.1.0",
|
|
||||||
cmd: setHome("search alpine --version '>= 0.1, < 0.2'"),
|
|
||||||
golden: "output/search-constraint.txt",
|
|
||||||
}, {
|
|
||||||
name: "search for 'alpine' with version constraint, expect one match with version 0.1.0",
|
|
||||||
cmd: setHome("search alpine --versions --version '>= 0.1, < 0.2'"),
|
|
||||||
golden: "output/search-versions-constraint.txt",
|
|
||||||
}, {
|
|
||||||
name: "search for 'alpine' with version constraint, expect one match with version 0.2.0",
|
|
||||||
cmd: setHome("search alpine --version '>= 0.1'"),
|
|
||||||
golden: "output/search-constraint-single.txt",
|
|
||||||
}, {
|
|
||||||
name: "search for 'alpine' with version constraint and --versions, expect two matches",
|
|
||||||
cmd: setHome("search alpine --versions --version '>= 0.1'"),
|
|
||||||
golden: "output/search-multiple-versions-constraints.txt",
|
|
||||||
}, {
|
|
||||||
name: "search for 'syzygy', expect no matches",
|
|
||||||
cmd: setHome("search syzygy"),
|
|
||||||
golden: "output/search-not-found.txt",
|
|
||||||
}, {
|
|
||||||
name: "search for 'alp[a-z]+', expect two matches",
|
|
||||||
cmd: setHome("search alp[a-z]+ --regexp"),
|
|
||||||
golden: "output/search-regex.txt",
|
|
||||||
}, {
|
|
||||||
name: "search for 'alp[', expect failure to compile regexp",
|
|
||||||
cmd: setHome("search alp[ --regexp"),
|
|
||||||
wantError: true,
|
|
||||||
}}
|
|
||||||
runTestCmd(t, tests)
|
|
||||||
}
|
|
@ -1 +1 @@
|
|||||||
Error: cannot load Chart.yaml: error converting YAML to JSON: yaml: line 5: did not find expected '-' indicator
|
Error: cannot load Chart.yaml: error converting YAML to JSON: yaml: line 6: did not find expected '-' indicator
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
apiVersion: v1
|
||||||
description: A Helm chart for Kubernetes
|
description: A Helm chart for Kubernetes
|
||||||
name: reqsubchart
|
name: reqsubchart
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
apiVersion: v1
|
||||||
description: A Helm chart for Kubernetes
|
description: A Helm chart for Kubernetes
|
||||||
name: reqsubchart
|
name: reqsubchart
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,3 +1,4 @@
|
|||||||
|
apiVersion: v1
|
||||||
description: A Helm chart for Kubernetes
|
description: A Helm chart for Kubernetes
|
||||||
name: decompressedchart
|
name: decompressedchart
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
|
Binary file not shown.
@ -1,3 +1,4 @@
|
|||||||
|
apiVersion: v1
|
||||||
description: A Helm chart for Kubernetes
|
description: A Helm chart for Kubernetes
|
||||||
name: reqsubchart
|
name: reqsubchart
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
apiVersion: v1
|
||||||
description: A Helm chart for Kubernetes
|
description: A Helm chart for Kubernetes
|
||||||
name: reqsubchart2
|
name: reqsubchart2
|
||||||
version: 0.2.0
|
version: 0.2.0
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -1,20 +1,21 @@
|
|||||||
-----BEGIN PGP SIGNED MESSAGE-----
|
-----BEGIN PGP SIGNED MESSAGE-----
|
||||||
Hash: SHA512
|
Hash: SHA512
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
description: A Helm chart for Kubernetes
|
description: A Helm chart for Kubernetes
|
||||||
name: signtest
|
name: signtest
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
|
|
||||||
...
|
...
|
||||||
files:
|
files:
|
||||||
signtest-0.1.0.tgz: sha256:dee72947753628425b82814516bdaa37aef49f25e8820dd2a6e15a33a007823b
|
signtest-0.1.0.tgz: sha256:e5ef611620fb97704d8751c16bab17fedb68883bfb0edc76f78a70e9173f9b55
|
||||||
-----BEGIN PGP SIGNATURE-----
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
wsBcBAEBCgAQBQJXomNHCRCEO7+YH8GHYgAALywIAG1Me852Fpn1GYu8Q1GCcw4g
|
wsBcBAEBCgAQBQJcoosfCRCEO7+YH8GHYgAA220IALAs8T8NPgkcLvHu+5109cAN
|
||||||
l2k7vOFchdDwDhdSVbkh4YyvTaIO3iE2Jtk1rxw+RIJiUr0eLO/rnIJuxZS8WKki
|
BOCNPSZDNsqLZW/2Dc9cKoBG7Jen4Qad+i5l9351kqn3D9Gm6eRfAWcjfggRobV/
|
||||||
DR1LI9J1VD4dxN3uDETtWDWq7ScoPsRY5mJvYZXC8whrWEt/H2kfqmoA9LloRPWp
|
9daZ19h0nl4O1muQNAkjvdgZt8MOP3+PB3I3/Tu2QCYjI579SLUmuXlcZR5BCFPR
|
||||||
flOE0iktA4UciZOblTj6nAk3iDyjh/4HYL4a6tT0LjjKI7OTw4YyHfjHad1ywVCz
|
PJy+e3QpV2PcdeU2KZLG4tjtlrq+3QC9ZHHEJLs+BVN9d46Dwo6CxJdHJrrrAkTw
|
||||||
9dMUc1rPgTnl+fnRiSPSrlZIWKOt1mcQ4fVrU3nwtRUwTId2k8FtygL0G6M+Y6t0
|
M8MhA92vbiTTPRSCZI9x5qDAwJYhoq0oxLflpuL2tIlo3qVoCsaTSURwMESEHO32
|
||||||
S6yaU7qfk9uTxkdkUF7Bf1X3ukxfe+cNBC32vf4m8LY4NkcYfSqK2fGtQsnVr6s=
|
XwYG7BaVDMELWhAorBAGBGBwWFbJ1677qQ2gd9CN0COiVhekWlFRcnn60800r84=
|
||||||
=NyOM
|
=k9Y9
|
||||||
-----END PGP SIGNATURE-----
|
-----END PGP SIGNATURE-----
|
@ -1,3 +1,4 @@
|
|||||||
|
apiVersion: v1
|
||||||
description: A Helm chart for Kubernetes
|
description: A Helm chart for Kubernetes
|
||||||
name: signtest
|
name: signtest
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The Helm Authors.
|
|
||||||
|
|
||||||
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 action
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"helm.sh/helm/pkg/chart/loader"
|
|
||||||
"helm.sh/helm/pkg/registry"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ChartSave performs a chart save operation.
|
|
||||||
type ChartSave struct {
|
|
||||||
cfg *Configuration
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewChartSave creates a new ChartSave object with the given configuration.
|
|
||||||
func NewChartSave(cfg *Configuration) *ChartSave {
|
|
||||||
return &ChartSave{
|
|
||||||
cfg: cfg,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run executes the chart save operation
|
|
||||||
func (a *ChartSave) Run(out io.Writer, path, ref string) error {
|
|
||||||
path, err := filepath.Abs(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ch, err := loader.LoadDir(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := registry.ParseReference(ref)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.cfg.RegistryClient.SaveChart(ch, r)
|
|
||||||
}
|
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
Copyright The Helm Authors.
|
||||||
|
|
||||||
|
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 action
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestVerifyChart(t *testing.T) {
|
||||||
|
v, err := VerifyChart("testdata/signtest-0.1.0.tgz", "testdata/helm-test-key.pub")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// The verification is tested at length in the provenance package. Here,
|
||||||
|
// we just want a quick sanity check that the v is not empty.
|
||||||
|
if len(v.FileHash) == 0 {
|
||||||
|
t.Error("Digest missing")
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue