mirror of
https://github.com/helm/helm.git
synced 2025-02-06 11:00:38 +00:00
Compare commits
17 Commits
99d496ac56
...
95bf507887
Author | SHA1 | Date | |
---|---|---|---|
|
95bf507887 | ||
|
3253059438 | ||
|
2d0091c278 | ||
|
547f49abf6 | ||
|
ee67cf0608 | ||
|
52384a5d12 | ||
|
56b40ad40f | ||
|
be7ab45c93 | ||
|
54753d64dc | ||
|
5f64fe6663 | ||
|
d637595735 | ||
|
0ce267d907 | ||
|
7ea1d1df66 | ||
|
e3e84343d2 | ||
|
80ae32622b | ||
|
ca7415b731 | ||
|
7f4c6ae643 |
2
.github/workflows/golangci-lint.yml
vendored
2
.github/workflows/golangci-lint.yml
vendored
@ -21,6 +21,6 @@ jobs:
|
||||
go-version: '1.23'
|
||||
check-latest: true
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@ec5d18412c0aeab7936cb16880d708ba2a64e1ae #pin@6.2.0
|
||||
uses: golangci/golangci-lint-action@e60da84bfae8c7920a47be973d75e15710aa8bd7 #pin@6.3.0
|
||||
with:
|
||||
version: v1.62
|
||||
|
@ -45,6 +45,7 @@ func newRepoCmd(out io.Writer) *cobra.Command {
|
||||
cmd.AddCommand(newRepoRemoveCmd(out))
|
||||
cmd.AddCommand(newRepoIndexCmd(out))
|
||||
cmd.AddCommand(newRepoUpdateCmd(out))
|
||||
cmd.AddCommand(newRepoImportCmd(out))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
98
cmd/helm/repo_import.go
Normal file
98
cmd/helm/repo_import.go
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
|
||||
"helm.sh/helm/v3/cmd/helm/require"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
)
|
||||
|
||||
type repoImportOptions struct {
|
||||
importedFilePath string
|
||||
repoFile string
|
||||
repoCache string
|
||||
}
|
||||
|
||||
func newRepoImportCmd(out io.Writer) *cobra.Command {
|
||||
o := &repoImportOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "import [PATH]",
|
||||
Short: "import chart repositories",
|
||||
Args: require.ExactArgs(1),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) == 0 {
|
||||
// Allow file completion when completing the argument for the directory
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
}
|
||||
// No more completions, so disable file completion
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o.importedFilePath = args[0]
|
||||
o.repoFile = settings.RepositoryConfig
|
||||
o.repoCache = settings.RepositoryCache
|
||||
return o.run(out)
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.StringVar(&o.importedFilePath, "file", "", "path to the file to import")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *repoImportOptions) run(out io.Writer) error {
|
||||
var helmRepoEntries []repo.Entry
|
||||
fileContent, err := os.ReadFile(o.importedFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = yaml.UnmarshalStrict(fileContent, &helmRepoEntries)
|
||||
if err != nil {
|
||||
return errors.Errorf("%s is an invalid YAML file", o.importedFilePath)
|
||||
}
|
||||
|
||||
for i := range helmRepoEntries {
|
||||
helmRepoEntry := helmRepoEntries[i]
|
||||
info := repoAddOptions{
|
||||
name: helmRepoEntry.Name,
|
||||
url: helmRepoEntry.URL,
|
||||
username: helmRepoEntry.Username,
|
||||
password: helmRepoEntry.Password,
|
||||
passCredentialsAll: helmRepoEntry.PassCredentialsAll,
|
||||
certFile: helmRepoEntry.CertFile,
|
||||
keyFile: helmRepoEntry.KeyFile,
|
||||
caFile: helmRepoEntry.CAFile,
|
||||
insecureSkipTLSverify: helmRepoEntry.InsecureSkipTLSverify,
|
||||
repoFile: o.repoFile,
|
||||
repoCache: o.repoCache}
|
||||
err = info.run(out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
189
cmd/helm/repo_import_test.go
Normal file
189
cmd/helm/repo_import_test.go
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
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"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"io/fs"
|
||||
|
||||
"helm.sh/helm/v3/pkg/repo/repotest"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
func TestRepoImportCmd(t *testing.T) {
|
||||
srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer srv.Stop()
|
||||
|
||||
// A second test server is setup to verify URL changing
|
||||
srv2, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer srv2.Stop()
|
||||
|
||||
tmpdir := filepath.Join(t.TempDir(), "path-component.yaml/data")
|
||||
err = os.MkdirAll(tmpdir, 0777)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
repoFile := filepath.Join(tmpdir, "repositories.yaml")
|
||||
repoImportFile := filepath.Join(tmpdir, "repositories-import.yaml")
|
||||
|
||||
var repositories = []repositoryElement{
|
||||
{Name: "repo1", URL: srv.URL()},
|
||||
{Name: "repo2", URL: srv2.URL()},
|
||||
}
|
||||
|
||||
data, err := yaml.Marshal(&repositories)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(repoImportFile, data, fs.FileMode(0644))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tests := []cmdTestCase{
|
||||
{
|
||||
name: "import repositories",
|
||||
cmd: fmt.Sprintf("repo import %s --repository-config %s --repository-cache %s", repoImportFile, repoFile, tmpdir),
|
||||
golden: "output/repo-import.txt",
|
||||
},
|
||||
{
|
||||
name: "import repositories second time",
|
||||
cmd: fmt.Sprintf("repo import %s --repository-config %s --repository-cache %s", repoImportFile, repoFile, tmpdir),
|
||||
golden: "output/repo-import2.txt",
|
||||
},
|
||||
}
|
||||
|
||||
runTestCmd(t, tests)
|
||||
}
|
||||
|
||||
func TestRepoImportFileCompletion(t *testing.T) {
|
||||
checkFileCompletion(t, "repo import", true)
|
||||
checkFileCompletion(t, "repo import file", false)
|
||||
}
|
||||
|
||||
func TestRepoImportOnNonExistingFile(t *testing.T) {
|
||||
ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ts.Stop()
|
||||
tmpdir := filepath.Join(t.TempDir(), "path-component.yaml/data")
|
||||
err = os.MkdirAll(tmpdir, 0777)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
repoFile := filepath.Join(tmpdir, "repositories.yaml")
|
||||
nonExistingFileName := "non-existing-file"
|
||||
|
||||
o := &repoImportOptions{
|
||||
importedFilePath: nonExistingFileName,
|
||||
repoFile: repoFile,
|
||||
repoCache: tmpdir,
|
||||
}
|
||||
|
||||
wantErrorMsg := fmt.Sprintf("open %s: no such file or directory", nonExistingFileName)
|
||||
|
||||
if err := o.run(io.Discard); err != nil {
|
||||
if wantErrorMsg != err.Error() {
|
||||
t.Fatalf("Actual error %s, not equal to expected error %s", err, wantErrorMsg)
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("expect reported an error.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoImportOnNonYamlFile(t *testing.T) {
|
||||
ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ts.Stop()
|
||||
tmpdir := filepath.Join(t.TempDir(), "path-component.yaml/data")
|
||||
err = os.MkdirAll(tmpdir, 0777)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
repoFile := filepath.Join(tmpdir, "repositories.yaml")
|
||||
repoImportFile := filepath.Join(tmpdir, "repositories-import.txt")
|
||||
err = os.WriteFile(repoImportFile, []byte("This is not a yaml file"), fs.FileMode(0644))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
o := &repoImportOptions{
|
||||
importedFilePath: repoImportFile,
|
||||
repoFile: repoFile,
|
||||
repoCache: tmpdir,
|
||||
}
|
||||
|
||||
wantErrorMsg := fmt.Sprintf("%s is an invalid YAML file", repoImportFile)
|
||||
|
||||
if err := o.run(io.Discard); err != nil {
|
||||
if wantErrorMsg != err.Error() {
|
||||
t.Fatalf("Actual error %s, not equal to expected error %s", err, wantErrorMsg)
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("expect reported an error.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoImportOnYamlFileWithInvalidStructure(t *testing.T) {
|
||||
ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ts.Stop()
|
||||
tmpdir := filepath.Join(t.TempDir(), "path-component.yaml/data")
|
||||
err = os.MkdirAll(tmpdir, 0777)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
repoFile := filepath.Join(tmpdir, "repositories.yaml")
|
||||
repoImportFile := filepath.Join(tmpdir, "repositories-import.txt")
|
||||
err = os.WriteFile(repoImportFile, []byte("- firstKey: firstValue\n secondKey: secondValue"), fs.FileMode(0644))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
o := &repoImportOptions{
|
||||
importedFilePath: repoImportFile,
|
||||
repoFile: repoFile,
|
||||
repoCache: tmpdir,
|
||||
}
|
||||
|
||||
wantErrorMsg := fmt.Sprintf("%s is an invalid YAML file", repoImportFile)
|
||||
|
||||
if err := o.run(io.Discard); err != nil {
|
||||
if wantErrorMsg != err.Error() {
|
||||
t.Fatalf("Actual error %s, not equal to expected error %s", err, wantErrorMsg)
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("expect reported an error.")
|
||||
}
|
||||
}
|
@ -297,7 +297,12 @@ func newDefaultRegistryClient(plainHTTP bool, username, password string) (*regis
|
||||
func newRegistryClientWithTLS(
|
||||
certFile, keyFile, caFile string, insecureSkipTLSverify bool, username, password string,
|
||||
) (*registry.Client, error) {
|
||||
tlsConf, err := tlsutil.NewClientTLS(certFile, keyFile, caFile, insecureSkipTLSverify)
|
||||
tlsConf, err := tlsutil.NewTLSConfig(
|
||||
tlsutil.WithInsecureSkipVerify(insecureSkipTLSverify),
|
||||
tlsutil.WithCertKeyPairFiles(certFile, keyFile),
|
||||
tlsutil.WithCAFile(caFile),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't create TLS config for client: %w", err)
|
||||
}
|
||||
|
2
cmd/helm/testdata/output/repo-import.txt
vendored
Normal file
2
cmd/helm/testdata/output/repo-import.txt
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
"repo1" has been added to your repositories
|
||||
"repo2" has been added to your repositories
|
2
cmd/helm/testdata/output/repo-import2.txt
vendored
Normal file
2
cmd/helm/testdata/output/repo-import2.txt
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
"repo1" already exists with the same configuration, skipping
|
||||
"repo2" already exists with the same configuration, skipping
|
8
go.mod
8
go.mod
@ -34,8 +34,8 @@ require (
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
golang.org/x/crypto v0.32.0
|
||||
golang.org/x/term v0.28.0
|
||||
golang.org/x/text v0.21.0
|
||||
golang.org/x/term v0.29.0
|
||||
golang.org/x/text v0.22.0
|
||||
k8s.io/api v0.32.1
|
||||
k8s.io/apiextensions-apiserver v0.32.1
|
||||
k8s.io/apimachinery v0.32.1
|
||||
@ -162,8 +162,8 @@ require (
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/oauth2 v0.23.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
golang.org/x/tools v0.26.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect
|
||||
|
16
go.sum
16
go.sum
@ -438,8 +438,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -461,8 +461,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@ -470,8 +470,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
|
||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
@ -479,8 +479,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
@ -1,58 +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 tlsutil
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Options represents configurable options used to create client and server TLS configurations.
|
||||
type Options struct {
|
||||
CaCertFile string
|
||||
// If either the KeyFile or CertFile is empty, ClientConfig() will not load them.
|
||||
KeyFile string
|
||||
CertFile string
|
||||
// Client-only options
|
||||
InsecureSkipVerify bool
|
||||
}
|
||||
|
||||
// ClientConfig returns a TLS configuration for use by a Helm client.
|
||||
func ClientConfig(opts Options) (cfg *tls.Config, err error) {
|
||||
var cert *tls.Certificate
|
||||
var pool *x509.CertPool
|
||||
|
||||
if opts.CertFile != "" || opts.KeyFile != "" {
|
||||
if cert, err = CertFromFilePair(opts.CertFile, opts.KeyFile); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, errors.Wrapf(err, "could not load x509 key pair (cert: %q, key: %q)", opts.CertFile, opts.KeyFile)
|
||||
}
|
||||
return nil, errors.Wrapf(err, "could not read x509 key pair (cert: %q, key: %q)", opts.CertFile, opts.KeyFile)
|
||||
}
|
||||
}
|
||||
if !opts.InsecureSkipVerify && opts.CaCertFile != "" {
|
||||
if pool, err = CertPoolFromFile(opts.CaCertFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
cfg = &tls.Config{InsecureSkipVerify: opts.InsecureSkipVerify, Certificates: []tls.Certificate{*cert}, RootCAs: pool}
|
||||
return cfg, nil
|
||||
}
|
@ -19,60 +19,104 @@ package tlsutil
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// NewClientTLS returns tls.Config appropriate for client auth.
|
||||
func NewClientTLS(certFile, keyFile, caFile string, insecureSkipTLSverify bool) (*tls.Config, error) {
|
||||
type TLSConfigOptions struct {
|
||||
insecureSkipTLSverify bool
|
||||
certPEMBlock, keyPEMBlock []byte
|
||||
caPEMBlock []byte
|
||||
}
|
||||
|
||||
type TLSConfigOption func(options *TLSConfigOptions) error
|
||||
|
||||
func WithInsecureSkipVerify(insecureSkipTLSverify bool) TLSConfigOption {
|
||||
return func(options *TLSConfigOptions) error {
|
||||
options.insecureSkipTLSverify = insecureSkipTLSverify
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithCertKeyPairFiles(certFile, keyFile string) TLSConfigOption {
|
||||
return func(options *TLSConfigOptions) error {
|
||||
if certFile == "" && keyFile == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
certPEMBlock, err := os.ReadFile(certFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read cert file: %q: %w", certFile, err)
|
||||
}
|
||||
|
||||
keyPEMBlock, err := os.ReadFile(keyFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read key file: %q: %w", keyFile, err)
|
||||
}
|
||||
|
||||
options.certPEMBlock = certPEMBlock
|
||||
options.keyPEMBlock = keyPEMBlock
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithCAFile(caFile string) TLSConfigOption {
|
||||
return func(options *TLSConfigOptions) error {
|
||||
if caFile == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
caPEMBlock, err := os.ReadFile(caFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't read CA file: %q: %w", caFile, err)
|
||||
}
|
||||
|
||||
options.caPEMBlock = caPEMBlock
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewTLSConfig(options ...TLSConfigOption) (*tls.Config, error) {
|
||||
to := TLSConfigOptions{}
|
||||
|
||||
errs := []error{}
|
||||
for _, option := range options {
|
||||
err := option(&to)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
|
||||
config := tls.Config{
|
||||
InsecureSkipVerify: insecureSkipTLSverify,
|
||||
InsecureSkipVerify: to.insecureSkipTLSverify,
|
||||
}
|
||||
|
||||
if certFile != "" && keyFile != "" {
|
||||
cert, err := CertFromFilePair(certFile, keyFile)
|
||||
if len(to.certPEMBlock) > 0 && len(to.keyPEMBlock) > 0 {
|
||||
cert, err := tls.X509KeyPair(to.certPEMBlock, to.keyPEMBlock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to load cert from key pair: %w", err)
|
||||
}
|
||||
config.Certificates = []tls.Certificate{*cert}
|
||||
|
||||
config.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
|
||||
if caFile != "" {
|
||||
cp, err := CertPoolFromFile(caFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if len(to.caPEMBlock) > 0 {
|
||||
cp := x509.NewCertPool()
|
||||
if !cp.AppendCertsFromPEM(to.caPEMBlock) {
|
||||
return nil, fmt.Errorf("failed to append certificates from pem block")
|
||||
}
|
||||
|
||||
config.RootCAs = cp
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// CertPoolFromFile returns an x509.CertPool containing the certificates
|
||||
// in the given PEM-encoded file.
|
||||
// Returns an error if the file could not be read, a certificate could not
|
||||
// be parsed, or if the file does not contain any certificates
|
||||
func CertPoolFromFile(filename string) (*x509.CertPool, error) {
|
||||
b, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("can't read CA file: %v", filename)
|
||||
}
|
||||
cp := x509.NewCertPool()
|
||||
if !cp.AppendCertsFromPEM(b) {
|
||||
return nil, errors.Errorf("failed to append certificates from file: %s", filename)
|
||||
}
|
||||
return cp, nil
|
||||
}
|
||||
|
||||
// CertFromFilePair returns a tls.Certificate containing the
|
||||
// certificates public/private key pair from a pair of given PEM-encoded files.
|
||||
// Returns an error if the file could not be read, a certificate could not
|
||||
// be parsed, or if the file does not contain any certificates
|
||||
func CertFromFilePair(certFile, keyFile string) (*tls.Certificate, error) {
|
||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "can't load key pair from cert %s and key %s", certFile, keyFile)
|
||||
}
|
||||
return &cert, err
|
||||
}
|
||||
|
105
internal/tlsutil/tls_test.go
Normal file
105
internal/tlsutil/tls_test.go
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
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 tlsutil
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const tlsTestDir = "../../testdata"
|
||||
|
||||
const (
|
||||
testCaCertFile = "rootca.crt"
|
||||
testCertFile = "crt.pem"
|
||||
testKeyFile = "key.pem"
|
||||
)
|
||||
|
||||
func testfile(t *testing.T, file string) (path string) {
|
||||
var err error
|
||||
if path, err = filepath.Abs(filepath.Join(tlsTestDir, file)); err != nil {
|
||||
t.Fatalf("error getting absolute path to test file %q: %v", file, err)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func TestNewTLSConfig(t *testing.T) {
|
||||
certFile := testfile(t, testCertFile)
|
||||
keyFile := testfile(t, testKeyFile)
|
||||
caCertFile := testfile(t, testCaCertFile)
|
||||
insecureSkipTLSverify := false
|
||||
|
||||
{
|
||||
cfg, err := NewTLSConfig(
|
||||
WithInsecureSkipVerify(insecureSkipTLSverify),
|
||||
WithCertKeyPairFiles(certFile, keyFile),
|
||||
WithCAFile(caCertFile),
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if got := len(cfg.Certificates); got != 1 {
|
||||
t.Fatalf("expecting 1 client certificates, got %d", got)
|
||||
}
|
||||
if cfg.InsecureSkipVerify {
|
||||
t.Fatalf("insecure skip verify mismatch, expecting false")
|
||||
}
|
||||
if cfg.RootCAs == nil {
|
||||
t.Fatalf("mismatch tls RootCAs, expecting non-nil")
|
||||
}
|
||||
}
|
||||
{
|
||||
cfg, err := NewTLSConfig(
|
||||
WithInsecureSkipVerify(insecureSkipTLSverify),
|
||||
WithCAFile(caCertFile),
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if got := len(cfg.Certificates); got != 0 {
|
||||
t.Fatalf("expecting 0 client certificates, got %d", got)
|
||||
}
|
||||
if cfg.InsecureSkipVerify {
|
||||
t.Fatalf("insecure skip verify mismatch, expecting false")
|
||||
}
|
||||
if cfg.RootCAs == nil {
|
||||
t.Fatalf("mismatch tls RootCAs, expecting non-nil")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
cfg, err := NewTLSConfig(
|
||||
WithInsecureSkipVerify(insecureSkipTLSverify),
|
||||
WithCertKeyPairFiles(certFile, keyFile),
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if got := len(cfg.Certificates); got != 1 {
|
||||
t.Fatalf("expecting 1 client certificates, got %d", got)
|
||||
}
|
||||
if cfg.InsecureSkipVerify {
|
||||
t.Fatalf("insecure skip verify mismatch, expecting false")
|
||||
}
|
||||
if cfg.RootCAs != nil {
|
||||
t.Fatalf("mismatch tls RootCAs, expecting nil")
|
||||
}
|
||||
}
|
||||
}
|
@ -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 tlsutil
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const tlsTestDir = "../../testdata"
|
||||
|
||||
const (
|
||||
testCaCertFile = "rootca.crt"
|
||||
testCertFile = "crt.pem"
|
||||
testKeyFile = "key.pem"
|
||||
)
|
||||
|
||||
func TestClientConfig(t *testing.T) {
|
||||
opts := Options{
|
||||
CaCertFile: testfile(t, testCaCertFile),
|
||||
CertFile: testfile(t, testCertFile),
|
||||
KeyFile: testfile(t, testKeyFile),
|
||||
InsecureSkipVerify: false,
|
||||
}
|
||||
|
||||
cfg, err := ClientConfig(opts)
|
||||
if err != nil {
|
||||
t.Fatalf("error building tls client config: %v", err)
|
||||
}
|
||||
|
||||
if got := len(cfg.Certificates); got != 1 {
|
||||
t.Fatalf("expecting 1 client certificates, got %d", got)
|
||||
}
|
||||
if cfg.InsecureSkipVerify {
|
||||
t.Fatalf("insecure skip verify mismatch, expecting false")
|
||||
}
|
||||
if cfg.RootCAs == nil {
|
||||
t.Fatalf("mismatch tls RootCAs, expecting non-nil")
|
||||
}
|
||||
}
|
||||
|
||||
func testfile(t *testing.T, file string) (path string) {
|
||||
var err error
|
||||
if path, err = filepath.Abs(filepath.Join(tlsTestDir, file)); err != nil {
|
||||
t.Fatalf("error getting absolute path to test file %q: %v", file, err)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func TestNewClientTLS(t *testing.T) {
|
||||
certFile := testfile(t, testCertFile)
|
||||
keyFile := testfile(t, testKeyFile)
|
||||
caCertFile := testfile(t, testCaCertFile)
|
||||
insecureSkipTLSverify := false
|
||||
|
||||
cfg, err := NewClientTLS(certFile, keyFile, caCertFile, insecureSkipTLSverify)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if got := len(cfg.Certificates); got != 1 {
|
||||
t.Fatalf("expecting 1 client certificates, got %d", got)
|
||||
}
|
||||
if cfg.InsecureSkipVerify {
|
||||
t.Fatalf("insecure skip verify mismatch, expecting false")
|
||||
}
|
||||
if cfg.RootCAs == nil {
|
||||
t.Fatalf("mismatch tls RootCAs, expecting non-nil")
|
||||
}
|
||||
|
||||
cfg, err = NewClientTLS("", "", caCertFile, insecureSkipTLSverify)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if got := len(cfg.Certificates); got != 0 {
|
||||
t.Fatalf("expecting 0 client certificates, got %d", got)
|
||||
}
|
||||
if cfg.InsecureSkipVerify {
|
||||
t.Fatalf("insecure skip verify mismatch, expecting false")
|
||||
}
|
||||
if cfg.RootCAs == nil {
|
||||
t.Fatalf("mismatch tls RootCAs, expecting non-nil")
|
||||
}
|
||||
|
||||
cfg, err = NewClientTLS(certFile, keyFile, "", insecureSkipTLSverify)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if got := len(cfg.Certificates); got != 1 {
|
||||
t.Fatalf("expecting 1 client certificates, got %d", got)
|
||||
}
|
||||
if cfg.InsecureSkipVerify {
|
||||
t.Fatalf("insecure skip verify mismatch, expecting false")
|
||||
}
|
||||
if cfg.RootCAs != nil {
|
||||
t.Fatalf("mismatch tls RootCAs, expecting nil")
|
||||
}
|
||||
}
|
@ -789,8 +789,16 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (
|
||||
dl.Verify = downloader.VerifyAlways
|
||||
}
|
||||
if c.RepoURL != "" {
|
||||
chartURL, err := repo.FindChartInAuthAndTLSAndPassRepoURL(c.RepoURL, c.Username, c.Password, name, version,
|
||||
c.CertFile, c.KeyFile, c.CaFile, c.InsecureSkipTLSverify, c.PassCredentialsAll, getter.All(settings))
|
||||
chartURL, err := repo.FindChartInRepoURL(
|
||||
c.RepoURL,
|
||||
name,
|
||||
getter.All(settings),
|
||||
repo.WithChartVersion(version),
|
||||
repo.WithClientTLS(c.CertFile, c.KeyFile, c.CaFile),
|
||||
repo.WithUsernamePassword(c.Username, c.Password),
|
||||
repo.WithInsecureSkipTLSverify(c.InsecureSkipTLSverify),
|
||||
repo.WithPassCredentialsAll(c.PassCredentialsAll),
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -117,7 +117,16 @@ func (p *Pull) Run(chartRef string) (string, error) {
|
||||
}
|
||||
|
||||
if p.RepoURL != "" {
|
||||
chartURL, err := repo.FindChartInAuthAndTLSAndPassRepoURL(p.RepoURL, p.Username, p.Password, chartRef, p.Version, p.CertFile, p.KeyFile, p.CaFile, p.InsecureSkipTLSverify, p.PassCredentialsAll, getter.All(p.Settings))
|
||||
chartURL, err := repo.FindChartInRepoURL(
|
||||
p.RepoURL,
|
||||
chartRef,
|
||||
getter.All(p.Settings),
|
||||
repo.WithChartVersion(p.Version),
|
||||
repo.WithClientTLS(p.CertFile, p.KeyFile, p.CaFile),
|
||||
repo.WithUsernamePassword(p.Username, p.Password),
|
||||
repo.WithInsecureSkipTLSverify(p.InsecureSkipTLSverify),
|
||||
repo.WithPassCredentialsAll(p.PassCredentialsAll),
|
||||
)
|
||||
if err != nil {
|
||||
return out.String(), err
|
||||
}
|
||||
|
@ -742,7 +742,7 @@ func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*
|
||||
return
|
||||
}
|
||||
}
|
||||
url, err = repo.FindChartInRepoURL(repoURL, name, version, certFile, keyFile, caFile, m.Getters)
|
||||
url, err = repo.FindChartInRepoURL(repoURL, name, m.Getters, repo.WithChartVersion(version), repo.WithClientTLS(certFile, keyFile, caFile))
|
||||
if err == nil {
|
||||
return url, username, password, false, false, "", "", "", err
|
||||
}
|
||||
|
@ -128,7 +128,11 @@ func (g *HTTPGetter) httpClient() (*http.Client, error) {
|
||||
})
|
||||
|
||||
if (g.opts.certFile != "" && g.opts.keyFile != "") || g.opts.caFile != "" || g.opts.insecureSkipVerifyTLS {
|
||||
tlsConf, err := tlsutil.NewClientTLS(g.opts.certFile, g.opts.keyFile, g.opts.caFile, g.opts.insecureSkipVerifyTLS)
|
||||
tlsConf, err := tlsutil.NewTLSConfig(
|
||||
tlsutil.WithInsecureSkipVerify(g.opts.insecureSkipVerifyTLS),
|
||||
tlsutil.WithCertKeyPairFiles(g.opts.certFile, g.opts.keyFile),
|
||||
tlsutil.WithCAFile(g.opts.caFile),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "can't create TLS config for client")
|
||||
}
|
||||
|
@ -311,7 +311,11 @@ func TestDownloadTLS(t *testing.T) {
|
||||
insecureSkipTLSverify := false
|
||||
|
||||
tlsSrv := httptest.NewUnstartedServer(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))
|
||||
tlsConf, err := tlsutil.NewClientTLS(pub, priv, ca, insecureSkipTLSverify)
|
||||
tlsConf, err := tlsutil.NewTLSConfig(
|
||||
tlsutil.WithInsecureSkipVerify(insecureSkipTLSverify),
|
||||
tlsutil.WithCertKeyPairFiles(pub, priv),
|
||||
tlsutil.WithCAFile(ca),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(errors.Wrap(err, "can't create TLS config for client"))
|
||||
}
|
||||
|
@ -128,7 +128,11 @@ func (g *OCIGetter) newRegistryClient() (*registry.Client, error) {
|
||||
})
|
||||
|
||||
if (g.opts.certFile != "" && g.opts.keyFile != "") || g.opts.caFile != "" || g.opts.insecureSkipVerifyTLS {
|
||||
tlsConf, err := tlsutil.NewClientTLS(g.opts.certFile, g.opts.keyFile, g.opts.caFile, g.opts.insecureSkipVerifyTLS)
|
||||
tlsConf, err := tlsutil.NewTLSConfig(
|
||||
tlsutil.WithInsecureSkipVerify(g.opts.insecureSkipVerifyTLS),
|
||||
tlsutil.WithCertKeyPairFiles(g.opts.certFile, g.opts.keyFile),
|
||||
tlsutil.WithCAFile(g.opts.caFile),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't create TLS config for client: %w", err)
|
||||
}
|
||||
|
@ -824,35 +824,3 @@ func scrubValidationError(err error) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase
|
||||
// and returns said phase (PodSucceeded or PodFailed qualify).
|
||||
func (c *Client) WaitAndGetCompletedPodPhase(name string, timeout time.Duration) (v1.PodPhase, error) {
|
||||
client, err := c.getKubeClient()
|
||||
if err != nil {
|
||||
return v1.PodUnknown, err
|
||||
}
|
||||
to := int64(timeout)
|
||||
watcher, err := client.CoreV1().Pods(c.namespace()).Watch(context.Background(), metav1.ListOptions{
|
||||
FieldSelector: fmt.Sprintf("metadata.name=%s", name),
|
||||
TimeoutSeconds: &to,
|
||||
})
|
||||
if err != nil {
|
||||
return v1.PodUnknown, err
|
||||
}
|
||||
|
||||
for event := range watcher.ResultChan() {
|
||||
p, ok := event.Object.(*v1.Pod)
|
||||
if !ok {
|
||||
return v1.PodUnknown, fmt.Errorf("%s not a pod", name)
|
||||
}
|
||||
switch p.Status.Phase {
|
||||
case v1.PodFailed:
|
||||
return v1.PodFailed, nil
|
||||
case v1.PodSucceeded:
|
||||
return v1.PodSucceeded, nil
|
||||
}
|
||||
}
|
||||
|
||||
return v1.PodUnknown, err
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
@ -34,19 +33,18 @@ import (
|
||||
// delegates all its calls to `PrintingKubeClient`
|
||||
type FailingKubeClient struct {
|
||||
PrintingKubeClient
|
||||
CreateError error
|
||||
GetError error
|
||||
WaitError error
|
||||
DeleteError error
|
||||
DeleteWithPropagationError error
|
||||
WatchUntilReadyError error
|
||||
UpdateError error
|
||||
BuildError error
|
||||
BuildTableError error
|
||||
BuildDummy bool
|
||||
BuildUnstructuredError error
|
||||
WaitAndGetCompletedPodPhaseError error
|
||||
WaitDuration time.Duration
|
||||
CreateError error
|
||||
GetError error
|
||||
WaitError error
|
||||
DeleteError error
|
||||
DeleteWithPropagationError error
|
||||
WatchUntilReadyError error
|
||||
UpdateError error
|
||||
BuildError error
|
||||
BuildTableError error
|
||||
BuildDummy bool
|
||||
BuildUnstructuredError error
|
||||
WaitDuration time.Duration
|
||||
}
|
||||
|
||||
// Create returns the configured error if set or prints
|
||||
@ -133,14 +131,6 @@ func (f *FailingKubeClient) BuildTable(r io.Reader, _ bool) (kube.ResourceList,
|
||||
return f.PrintingKubeClient.BuildTable(r, false)
|
||||
}
|
||||
|
||||
// WaitAndGetCompletedPodPhase returns the configured error if set or prints
|
||||
func (f *FailingKubeClient) WaitAndGetCompletedPodPhase(s string, d time.Duration) (v1.PodPhase, error) {
|
||||
if f.WaitAndGetCompletedPodPhaseError != nil {
|
||||
return v1.PodSucceeded, f.WaitAndGetCompletedPodPhaseError
|
||||
}
|
||||
return f.PrintingKubeClient.WaitAndGetCompletedPodPhase(s, d)
|
||||
}
|
||||
|
||||
// DeleteWithPropagationPolicy returns the configured error if set or prints
|
||||
func (f *FailingKubeClient) DeleteWithPropagationPolicy(resources kube.ResourceList, policy metav1.DeletionPropagation) (*kube.Result, []error) {
|
||||
if f.DeleteWithPropagationError != nil {
|
||||
|
@ -21,7 +21,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
@ -111,11 +110,6 @@ func (p *PrintingKubeClient) BuildTable(_ io.Reader, _ bool) (kube.ResourceList,
|
||||
return []*resource.Info{}, nil
|
||||
}
|
||||
|
||||
// WaitAndGetCompletedPodPhase implements KubeClient WaitAndGetCompletedPodPhase.
|
||||
func (p *PrintingKubeClient) WaitAndGetCompletedPodPhase(_ string, _ time.Duration) (v1.PodPhase, error) {
|
||||
return v1.PodSucceeded, nil
|
||||
}
|
||||
|
||||
// DeleteWithPropagationPolicy implements KubeClient delete.
|
||||
//
|
||||
// It only prints out the content to be deleted.
|
||||
|
@ -20,7 +20,6 @@ import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
@ -64,10 +63,6 @@ type Interface interface {
|
||||
// Validates against OpenAPI schema if validate is true.
|
||||
Build(reader io.Reader, validate bool) (ResourceList, error)
|
||||
|
||||
// WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase
|
||||
// and returns said phase (PodSucceeded or PodFailed qualify).
|
||||
WaitAndGetCompletedPodPhase(name string, timeout time.Duration) (v1.PodPhase, error)
|
||||
|
||||
// IsReachable checks whether the client is able to connect to the cluster.
|
||||
IsReachable() error
|
||||
}
|
||||
|
@ -111,7 +111,11 @@ func NewOCIPusher(ops ...Option) (Pusher, error) {
|
||||
|
||||
func (pusher *OCIPusher) newRegistryClient() (*registry.Client, error) {
|
||||
if (pusher.opts.certFile != "" && pusher.opts.keyFile != "") || pusher.opts.caFile != "" || pusher.opts.insecureSkipTLSverify {
|
||||
tlsConf, err := tlsutil.NewClientTLS(pusher.opts.certFile, pusher.opts.keyFile, pusher.opts.caFile, pusher.opts.insecureSkipTLSverify)
|
||||
tlsConf, err := tlsutil.NewTLSConfig(
|
||||
tlsutil.WithInsecureSkipVerify(pusher.opts.insecureSkipTLSverify),
|
||||
tlsutil.WithCertKeyPairFiles(pusher.opts.certFile, pusher.opts.keyFile),
|
||||
tlsutil.WithCAFile(pusher.opts.caFile),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "can't create TLS config for client")
|
||||
}
|
||||
|
@ -101,7 +101,11 @@ func extractChartMeta(chartData []byte) (*chart.Metadata, error) {
|
||||
|
||||
// NewRegistryClientWithTLS is a helper function to create a new registry client with TLS enabled.
|
||||
func NewRegistryClientWithTLS(out io.Writer, certFile, keyFile, caFile string, insecureSkipTLSverify bool, registryConfig string, debug bool) (*Client, error) {
|
||||
tlsConf, err := tlsutil.NewClientTLS(certFile, keyFile, caFile, insecureSkipTLSverify)
|
||||
tlsConf, err := tlsutil.NewTLSConfig(
|
||||
tlsutil.WithInsecureSkipVerify(insecureSkipTLSverify),
|
||||
tlsutil.WithCertKeyPairFiles(certFile, keyFile),
|
||||
tlsutil.WithCAFile(caFile),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't create TLS config for client: %s", err)
|
||||
}
|
||||
|
@ -94,9 +94,14 @@ func setup(suite *TestSuite, tlsEnabled, insecure bool) *registry.Registry {
|
||||
if tlsEnabled {
|
||||
var tlsConf *tls.Config
|
||||
if insecure {
|
||||
tlsConf, err = tlsutil.NewClientTLS("", "", "", true)
|
||||
tlsConf, err = tlsutil.NewTLSConfig(
|
||||
tlsutil.WithInsecureSkipVerify(true),
|
||||
)
|
||||
} else {
|
||||
tlsConf, err = tlsutil.NewClientTLS(tlsCert, tlsKey, tlsCA, false)
|
||||
tlsConf, err = tlsutil.NewTLSConfig(
|
||||
tlsutil.WithCertKeyPairFiles(tlsCert, tlsKey),
|
||||
tlsutil.WithCAFile(tlsCA),
|
||||
)
|
||||
}
|
||||
httpClient := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
|
@ -196,33 +196,65 @@ func (r *ChartRepository) generateIndex() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type findChartInRepoURLOptions struct {
|
||||
Username string
|
||||
Password string
|
||||
PassCredentialsAll bool
|
||||
InsecureSkipTLSverify bool
|
||||
CertFile string
|
||||
KeyFile string
|
||||
CAFile string
|
||||
ChartVersion string
|
||||
}
|
||||
|
||||
type FindChartInRepoURLOption func(*findChartInRepoURLOptions)
|
||||
|
||||
// WithChartVersion specifies the chart version to find
|
||||
func WithChartVersion(chartVersion string) FindChartInRepoURLOption {
|
||||
return func(options *findChartInRepoURLOptions) {
|
||||
options.ChartVersion = chartVersion
|
||||
}
|
||||
}
|
||||
|
||||
// WithUsernamePassword specifies the username/password credntials for the repository
|
||||
func WithUsernamePassword(username, password string) FindChartInRepoURLOption {
|
||||
return func(options *findChartInRepoURLOptions) {
|
||||
options.Username = username
|
||||
options.Password = password
|
||||
}
|
||||
}
|
||||
|
||||
// WithPassCredentialsAll flags whether credentials should be passed on to other domains
|
||||
func WithPassCredentialsAll(passCredentialsAll bool) FindChartInRepoURLOption {
|
||||
return func(options *findChartInRepoURLOptions) {
|
||||
options.PassCredentialsAll = passCredentialsAll
|
||||
}
|
||||
}
|
||||
|
||||
// WithClientTLS species the cert, key, and CA files for client mTLS
|
||||
func WithClientTLS(certFile, keyFile, caFile string) FindChartInRepoURLOption {
|
||||
return func(options *findChartInRepoURLOptions) {
|
||||
options.CertFile = certFile
|
||||
options.KeyFile = keyFile
|
||||
options.CAFile = caFile
|
||||
}
|
||||
}
|
||||
|
||||
// WithInsecureSkipTLSverify skips TLS verification for repostory communication
|
||||
func WithInsecureSkipTLSverify(insecureSkipTLSverify bool) FindChartInRepoURLOption {
|
||||
return func(options *findChartInRepoURLOptions) {
|
||||
options.InsecureSkipTLSverify = insecureSkipTLSverify
|
||||
}
|
||||
}
|
||||
|
||||
// FindChartInRepoURL finds chart in chart repository pointed by repoURL
|
||||
// without adding repo to repositories
|
||||
func FindChartInRepoURL(repoURL, chartName, chartVersion, certFile, keyFile, caFile string, getters getter.Providers) (string, error) {
|
||||
return FindChartInAuthRepoURL(repoURL, "", "", chartName, chartVersion, certFile, keyFile, caFile, getters)
|
||||
}
|
||||
func FindChartInRepoURL(repoURL string, chartName string, getters getter.Providers, options ...FindChartInRepoURLOption) (string, error) {
|
||||
|
||||
// FindChartInAuthRepoURL finds chart in chart repository pointed by repoURL
|
||||
// without adding repo to repositories, like FindChartInRepoURL,
|
||||
// but it also receives credentials for the chart repository.
|
||||
func FindChartInAuthRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, getters getter.Providers) (string, error) {
|
||||
return FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile, false, getters)
|
||||
}
|
||||
|
||||
// FindChartInAuthAndTLSRepoURL finds chart in chart repository pointed by repoURL
|
||||
// without adding repo to repositories, like FindChartInRepoURL,
|
||||
// but it also receives credentials and TLS verify flag for the chart repository.
|
||||
// TODO Helm 4, FindChartInAuthAndTLSRepoURL should be integrated into FindChartInAuthRepoURL.
|
||||
func FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, insecureSkipTLSverify bool, getters getter.Providers) (string, error) {
|
||||
return FindChartInAuthAndTLSAndPassRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile, insecureSkipTLSverify, false, getters)
|
||||
}
|
||||
|
||||
// FindChartInAuthAndTLSAndPassRepoURL finds chart in chart repository pointed by repoURL
|
||||
// without adding repo to repositories, like FindChartInRepoURL,
|
||||
// but it also receives credentials, TLS verify flag, and if credentials should
|
||||
// be passed on to other domains.
|
||||
// TODO Helm 4, FindChartInAuthAndTLSAndPassRepoURL should be integrated into FindChartInAuthRepoURL.
|
||||
func FindChartInAuthAndTLSAndPassRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, insecureSkipTLSverify, passCredentialsAll bool, getters getter.Providers) (string, error) {
|
||||
opts := findChartInRepoURLOptions{}
|
||||
for _, option := range options {
|
||||
option(&opts)
|
||||
}
|
||||
|
||||
// Download and write the index file to a temporary location
|
||||
buf := make([]byte, 20)
|
||||
@ -231,14 +263,14 @@ func FindChartInAuthAndTLSAndPassRepoURL(repoURL, username, password, chartName,
|
||||
|
||||
c := Entry{
|
||||
URL: repoURL,
|
||||
Username: username,
|
||||
Password: password,
|
||||
PassCredentialsAll: passCredentialsAll,
|
||||
CertFile: certFile,
|
||||
KeyFile: keyFile,
|
||||
CAFile: caFile,
|
||||
Username: opts.Username,
|
||||
Password: opts.Password,
|
||||
PassCredentialsAll: opts.PassCredentialsAll,
|
||||
CertFile: opts.CertFile,
|
||||
KeyFile: opts.KeyFile,
|
||||
CAFile: opts.CAFile,
|
||||
Name: name,
|
||||
InsecureSkipTLSverify: insecureSkipTLSverify,
|
||||
InsecureSkipTLSverify: opts.InsecureSkipTLSverify,
|
||||
}
|
||||
r, err := NewChartRepository(&c, getters)
|
||||
if err != nil {
|
||||
@ -260,10 +292,10 @@ func FindChartInAuthAndTLSAndPassRepoURL(repoURL, username, password, chartName,
|
||||
}
|
||||
|
||||
errMsg := fmt.Sprintf("chart %q", chartName)
|
||||
if chartVersion != "" {
|
||||
errMsg = fmt.Sprintf("%s version %q", errMsg, chartVersion)
|
||||
if opts.ChartVersion != "" {
|
||||
errMsg = fmt.Sprintf("%s version %q", errMsg, opts.ChartVersion)
|
||||
}
|
||||
cv, err := repoIndex.Get(chartName, chartVersion)
|
||||
cv, err := repoIndex.Get(chartName, opts.ChartVersion)
|
||||
if err != nil {
|
||||
return "", errors.Errorf("%s not found in %s repository", errMsg, repoURL)
|
||||
}
|
||||
|
@ -297,7 +297,12 @@ func TestFindChartInAuthAndTLSAndPassRepoURL(t *testing.T) {
|
||||
}
|
||||
defer srv.Close()
|
||||
|
||||
chartURL, err := FindChartInAuthAndTLSAndPassRepoURL(srv.URL, "", "", "nginx", "", "", "", "", true, false, getter.All(&cli.EnvSettings{}))
|
||||
chartURL, err := FindChartInRepoURL(
|
||||
srv.URL,
|
||||
"nginx",
|
||||
getter.All(&cli.EnvSettings{}),
|
||||
WithInsecureSkipTLSverify(true),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
@ -306,7 +311,7 @@ func TestFindChartInAuthAndTLSAndPassRepoURL(t *testing.T) {
|
||||
}
|
||||
|
||||
// If the insecureSkipTLSVerify is false, it will return an error that contains "x509: certificate signed by unknown authority".
|
||||
_, err = FindChartInAuthAndTLSAndPassRepoURL(srv.URL, "", "", "nginx", "0.1.0", "", "", "", false, false, getter.All(&cli.EnvSettings{}))
|
||||
_, err = FindChartInRepoURL(srv.URL, "nginx", getter.All(&cli.EnvSettings{}), WithChartVersion("0.1.0"))
|
||||
// Go communicates with the platform and different platforms return different messages. Go itself tests darwin
|
||||
// differently for its message. On newer versions of Darwin the message includes the "Acme Co" portion while older
|
||||
// versions of Darwin do not. As there are people developing Helm using both old and new versions of Darwin we test
|
||||
@ -327,7 +332,7 @@ func TestFindChartInRepoURL(t *testing.T) {
|
||||
}
|
||||
defer srv.Close()
|
||||
|
||||
chartURL, err := FindChartInRepoURL(srv.URL, "nginx", "", "", "", "", getter.All(&cli.EnvSettings{}))
|
||||
chartURL, err := FindChartInRepoURL(srv.URL, "nginx", getter.All(&cli.EnvSettings{}))
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
@ -335,7 +340,7 @@ func TestFindChartInRepoURL(t *testing.T) {
|
||||
t.Errorf("%s is not the valid URL", chartURL)
|
||||
}
|
||||
|
||||
chartURL, err = FindChartInRepoURL(srv.URL, "nginx", "0.1.0", "", "", "", getter.All(&cli.EnvSettings{}))
|
||||
chartURL, err = FindChartInRepoURL(srv.URL, "nginx", getter.All(&cli.EnvSettings{}), WithChartVersion("0.1.0"))
|
||||
if err != nil {
|
||||
t.Errorf("%s", err)
|
||||
}
|
||||
@ -350,7 +355,7 @@ func TestErrorFindChartInRepoURL(t *testing.T) {
|
||||
RepositoryCache: t.TempDir(),
|
||||
})
|
||||
|
||||
if _, err := FindChartInRepoURL("http://someserver/something", "nginx", "", "", "", "", g); err == nil {
|
||||
if _, err := FindChartInRepoURL("http://someserver/something", "nginx", g); err == nil {
|
||||
t.Errorf("Expected error for bad chart URL, but did not get any errors")
|
||||
} else if !strings.Contains(err.Error(), `looks like "http://someserver/something" is not a valid chart repository or cannot be reached`) {
|
||||
t.Errorf("Expected error for bad chart URL, but got a different error (%v)", err)
|
||||
@ -362,19 +367,19 @@ func TestErrorFindChartInRepoURL(t *testing.T) {
|
||||
}
|
||||
defer srv.Close()
|
||||
|
||||
if _, err = FindChartInRepoURL(srv.URL, "nginx1", "", "", "", "", g); err == nil {
|
||||
if _, err = FindChartInRepoURL(srv.URL, "nginx1", g); err == nil {
|
||||
t.Errorf("Expected error for chart not found, but did not get any errors")
|
||||
} else if err.Error() != `chart "nginx1" not found in `+srv.URL+` repository` {
|
||||
t.Errorf("Expected error for chart not found, but got a different error (%v)", err)
|
||||
}
|
||||
|
||||
if _, err = FindChartInRepoURL(srv.URL, "nginx1", "0.1.0", "", "", "", g); err == nil {
|
||||
if _, err = FindChartInRepoURL(srv.URL, "nginx1", g, WithChartVersion("0.1.0")); err == nil {
|
||||
t.Errorf("Expected error for chart not found, but did not get any errors")
|
||||
} else if err.Error() != `chart "nginx1" version "0.1.0" not found in `+srv.URL+` repository` {
|
||||
t.Errorf("Expected error for chart not found, but got a different error (%v)", err)
|
||||
}
|
||||
|
||||
if _, err = FindChartInRepoURL(srv.URL, "chartWithNoURL", "", "", "", "", g); err == nil {
|
||||
if _, err = FindChartInRepoURL(srv.URL, "chartWithNoURL", g); err == nil {
|
||||
t.Errorf("Expected error for no chart URLs available, but did not get any errors")
|
||||
} else if err.Error() != `chart "chartWithNoURL" has no downloadable URLs` {
|
||||
t.Errorf("Expected error for chart not found, but got a different error (%v)", err)
|
||||
|
@ -368,7 +368,11 @@ func (s *Server) StartTLS() {
|
||||
}
|
||||
http.FileServer(http.Dir(s.Root())).ServeHTTP(w, r)
|
||||
}))
|
||||
tlsConf, err := tlsutil.NewClientTLS(pub, priv, ca, insecure)
|
||||
tlsConf, err := tlsutil.NewTLSConfig(
|
||||
tlsutil.WithInsecureSkipVerify(insecure),
|
||||
tlsutil.WithCertKeyPairFiles(pub, priv),
|
||||
tlsutil.WithCAFile(ca),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user