mirror of https://github.com/helm/helm
Fix for (#10597) Added 2-way TLS Support for oci pull for artifact repository which causes TLS handshake failure error. Special notes for your reviewer: Added flag for two-way authentication (--mtls-enabled) . eg: helm pull oci://nginx.testharbor.com:9443/testrepo/sslcharttest --version 0.1.0 --ca-file /etc/docker/certs.d/nginx.testharbor.com/ca.crt --cert-file /etc/docker/certs.d/nginx.testharbor.com/root_client.crt --key-file /etc/docker/certs.d/nginx.testharbor.com/root_client.key --mtls-enabledpull/11174/head
parent
9377988685
commit
04e772d801
@ -0,0 +1,136 @@
|
||||
/*
|
||||
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"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"helm.sh/helm/v3/pkg/repo/repotest"
|
||||
)
|
||||
|
||||
func TestMutualtlsPull(t *testing.T) {
|
||||
srv, err := repotest.NewTempmtlsServerWithCleanup(t, "testdata/testcharts/*.tgz*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer srv.Stopmtls()
|
||||
|
||||
ociSrv, err := repotest.NewOCImtlsServer(t, srv.Rootmtls())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ociSrv.Run(t)
|
||||
|
||||
if err := srv.LinkIndicesmtls(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
helmTestKeyOut := "Signed by: Helm Testing (This key should only be used for testing. DO NOT TRUST.) <helm-testing@helm.sh>\n" +
|
||||
"Using Key With Fingerprint: 5E615389B53CA37F0EE60BD3843BBF981FC18762\n" +
|
||||
"Chart Hash Verified: "
|
||||
|
||||
// all flags will get "-d outdir" appended.
|
||||
tests := []struct {
|
||||
name string
|
||||
args string
|
||||
existFile string
|
||||
existDir string
|
||||
wantError bool
|
||||
wantErrorMsg string
|
||||
failExpect string
|
||||
expectFile string
|
||||
expectDir bool
|
||||
expectVerify bool
|
||||
expectSha string
|
||||
}{
|
||||
{
|
||||
name: "Fetch OCI Chart",
|
||||
args: fmt.Sprintf("oci://%s/u/ocitestuser/oci-dependent-chart --version 0.1.0 --ca-file ../../testdata/rootca.crt --cert-file ../../testdata/rootca.crt --key-file ../../testdata/rootca.key --tls-enabled", ociSrv.RegistryURL),
|
||||
expectFile: "./oci-dependent-chart-0.1.0.tgz",
|
||||
},
|
||||
{
|
||||
name: "Fail fetching non-existent OCI chart with mutual tls enabled",
|
||||
args: fmt.Sprintf("oci://%s/u/ocitestuser/nosuchthing --version 0.1.0 --tls-enabled", ociSrv.RegistryURL),
|
||||
failExpect: "Failed to fetch",
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "Fail fetching OCI chart without version specified with mutual tls enabled",
|
||||
args: fmt.Sprintf("oci://%s/u/ocitestuser/nosuchthing --tls-enabled", ociSrv.RegistryURL),
|
||||
wantErrorMsg: "Error: --version flag is explicitly required for OCI registries",
|
||||
wantError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
outdir := srv.Rootmtls()
|
||||
cmd := fmt.Sprintf("fetch %s -d '%s' --repository-config %s --repository-cache %s --registry-config %s",
|
||||
tt.args,
|
||||
outdir,
|
||||
filepath.Join(outdir, "repositories.yaml"),
|
||||
outdir,
|
||||
filepath.Join(outdir, "config.json"),
|
||||
)
|
||||
// Create file or Dir before helm pull --untar, see: https://github.com/helm/helm/issues/7182
|
||||
if tt.existFile != "" {
|
||||
file := filepath.Join(outdir, tt.existFile)
|
||||
_, err := os.Create(file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if tt.existDir != "" {
|
||||
file := filepath.Join(outdir, tt.existDir)
|
||||
err := os.Mkdir(file, 0755)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
_, out, err := executeActionCommand(cmd)
|
||||
if err != nil {
|
||||
if tt.wantError {
|
||||
if tt.wantErrorMsg != "" && tt.wantErrorMsg == err.Error() {
|
||||
t.Fatalf("Actual error %s, not equal to expected error %s", err, tt.wantErrorMsg)
|
||||
}
|
||||
return
|
||||
}
|
||||
t.Fatalf("%q reported error: %s", tt.name, err)
|
||||
}
|
||||
|
||||
if tt.expectVerify {
|
||||
outString := helmTestKeyOut + tt.expectSha + "\n"
|
||||
if out != outString {
|
||||
t.Errorf("%q: expected verification output %q, got %q", tt.name, outString, out)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ef := filepath.Join(outdir, tt.expectFile)
|
||||
fi, err := os.Stat(ef)
|
||||
if err != nil {
|
||||
t.Errorf("%q: expected a file at %s. %s", tt.name, ef, err)
|
||||
}
|
||||
if fi.IsDir() != tt.expectDir {
|
||||
t.Errorf("%q: expected directory=%t, but it's not.", tt.name, tt.expectDir)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,445 @@
|
||||
/*
|
||||
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 repotest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/distribution/distribution/v3/configuration"
|
||||
"github.com/distribution/distribution/v3/registry"
|
||||
_ "github.com/distribution/distribution/v3/registry/auth/htpasswd" // used for docker test registry
|
||||
_ "github.com/distribution/distribution/v3/registry/storage/driver/inmemory" // used for docker test registry
|
||||
"github.com/phayes/freeport"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"helm.sh/helm/v3/internal/tlsutil"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
ociRegistry "helm.sh/helm/v3/pkg/registry"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
)
|
||||
|
||||
// NewTempServerWithCleanup creates a server inside of a temp dir.
|
||||
//
|
||||
// If the passed in string is not "", it will be treated as a shell glob, and files
|
||||
// will be copied from that path to the server's docroot.
|
||||
//
|
||||
// The caller is responsible for stopping the server.
|
||||
// The temp dir will be removed by testing package automatically when test finished.
|
||||
const (
|
||||
testCaCertFile = "crt.pem"
|
||||
testCertFile = "rootca.crt"
|
||||
testKeyFile = "rootca.key"
|
||||
)
|
||||
|
||||
const tlsTestDir = "../../testdata"
|
||||
|
||||
func NewTempmtlsServerWithCleanup(t *testing.T, glob string) (*mtlsServer, error) {
|
||||
srv, err := NewTempmtlsServer(glob)
|
||||
t.Cleanup(func() { os.RemoveAll(srv.docroot) })
|
||||
return srv, err
|
||||
}
|
||||
|
||||
// Set up a fake repo with basic auth enabled
|
||||
func NewTempmtlsServerWithCleanupAndBasicAuth(t *testing.T, glob string) *mtlsServer {
|
||||
srv, err := NewTempmtlsServerWithCleanup(t, glob)
|
||||
srv.Stopmtls()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
srv.WithMiddlewaremtls(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
username, password, ok := r.BasicAuth()
|
||||
if !ok || username != "username" || password != "password" {
|
||||
t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password)
|
||||
}
|
||||
}))
|
||||
srv.mtlsStart()
|
||||
return srv
|
||||
}
|
||||
|
||||
type mtlsOCIServer struct {
|
||||
*registry.Registry
|
||||
RegistryURL string
|
||||
Dir string
|
||||
TestUsername string
|
||||
TestPassword string
|
||||
Client *ociRegistry.Client
|
||||
}
|
||||
|
||||
type mtlsOCIServerRunConfig struct {
|
||||
DependingChart *chart.Chart
|
||||
}
|
||||
|
||||
type mtlsOCIServerOpt func(config *mtlsOCIServerRunConfig)
|
||||
|
||||
func NewOCImtlsServer(t *testing.T, dir string) (*mtlsOCIServer, error) {
|
||||
testHtpasswdFileBasename := "authtest.htpasswd"
|
||||
testUsername, testPassword := "username", "password"
|
||||
|
||||
pwBytes, err := bcrypt.GenerateFromPassword([]byte(testPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
t.Fatal("error generating bcrypt password for test htpasswd file")
|
||||
}
|
||||
htpasswdPath := filepath.Join(dir, testHtpasswdFileBasename)
|
||||
err = ioutil.WriteFile(htpasswdPath, []byte(fmt.Sprintf("%s:%s\n", testUsername, string(pwBytes))), 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating test htpasswd file")
|
||||
}
|
||||
|
||||
// Registry config
|
||||
config := &configuration.Configuration{}
|
||||
port, err := freeport.GetFreePort()
|
||||
if err != nil {
|
||||
t.Fatalf("error finding free port for test registry")
|
||||
}
|
||||
|
||||
config.HTTP.Addr = fmt.Sprintf(":%d", port)
|
||||
config.HTTP.DrainTimeout = time.Duration(10) * time.Second
|
||||
config.Storage = map[string]configuration.Parameters{"inmemory": map[string]interface{}{}}
|
||||
config.Auth = configuration.Auth{
|
||||
"htpasswd": configuration.Parameters{
|
||||
"realm": "localhost",
|
||||
"path": htpasswdPath,
|
||||
},
|
||||
}
|
||||
|
||||
registryURL := fmt.Sprintf("localhost:%d", port)
|
||||
|
||||
r, err := registry.NewRegistry(context.Background(), config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return &mtlsOCIServer{
|
||||
Registry: r,
|
||||
RegistryURL: registryURL,
|
||||
TestUsername: testUsername,
|
||||
TestPassword: testPassword,
|
||||
Dir: dir,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (srv *mtlsOCIServer) Run(t *testing.T, opts ...mtlsOCIServerOpt) {
|
||||
cfg := &mtlsOCIServerRunConfig{}
|
||||
for _, fn := range opts {
|
||||
fn(cfg)
|
||||
}
|
||||
|
||||
go srv.ListenAndServe()
|
||||
|
||||
credentialsFile := filepath.Join(srv.Dir, "config.json")
|
||||
|
||||
optns := tlsutil.Options{
|
||||
CaCertFile: testfile(t, testCaCertFile),
|
||||
CertFile: testfile(t, testCertFile),
|
||||
KeyFile: testfile(t, testKeyFile),
|
||||
InsecureSkipVerify: false,
|
||||
}
|
||||
|
||||
// init test client
|
||||
registryClient, err := ociRegistry.NewClient(
|
||||
ociRegistry.ClientOptDebug(true),
|
||||
ociRegistry.ClientOptEnableCache(true),
|
||||
ociRegistry.ClientOptWriter(os.Stdout),
|
||||
ociRegistry.ClientOptTwoWayTLSEnable(true),
|
||||
ociRegistry.ClientOptCredentialsFile(credentialsFile),
|
||||
ociRegistry.ClientOptWithTLSOpts(optns),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating registry client")
|
||||
}
|
||||
|
||||
err = registryClient.Login(
|
||||
srv.RegistryURL,
|
||||
ociRegistry.LoginOptBasicAuth(srv.TestUsername, srv.TestPassword),
|
||||
ociRegistry.LoginOptInsecure(false))
|
||||
if err != nil {
|
||||
t.Fatalf("error logging into registry with good credentials")
|
||||
}
|
||||
|
||||
ref := fmt.Sprintf("%s/u/ocitestuser/oci-dependent-chart:0.1.0", srv.RegistryURL)
|
||||
|
||||
err = chartutil.ExpandFile(srv.Dir, filepath.Join(srv.Dir, "oci-dependent-chart-0.1.0.tgz"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// valid chart
|
||||
ch, err := loader.LoadDir(filepath.Join(srv.Dir, "oci-dependent-chart"))
|
||||
if err != nil {
|
||||
t.Fatal("error loading chart")
|
||||
}
|
||||
|
||||
err = os.RemoveAll(filepath.Join(srv.Dir, "oci-dependent-chart"))
|
||||
if err != nil {
|
||||
t.Fatal("error removing chart before push")
|
||||
}
|
||||
|
||||
// save it back to disk..
|
||||
absPath, err := chartutil.Save(ch, srv.Dir)
|
||||
if err != nil {
|
||||
t.Fatal("could not create chart archive")
|
||||
}
|
||||
|
||||
// load it into memory...
|
||||
contentBytes, err := ioutil.ReadFile(absPath)
|
||||
if err != nil {
|
||||
t.Fatal("could not load chart into memory")
|
||||
}
|
||||
|
||||
result, err := registryClient.Push(contentBytes, ref)
|
||||
if err != nil {
|
||||
t.Fatalf("error pushing dependent chart: %s", err)
|
||||
}
|
||||
t.Logf("Manifest.Digest: %s, Manifest.Size: %d, "+
|
||||
"Config.Digest: %s, Config.Size: %d, "+
|
||||
"Chart.Digest: %s, Chart.Size: %d",
|
||||
result.Manifest.Digest, result.Manifest.Size,
|
||||
result.Config.Digest, result.Config.Size,
|
||||
result.Chart.Digest, result.Chart.Size)
|
||||
|
||||
srv.Client = registryClient
|
||||
c := cfg.DependingChart
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
|
||||
dependingRef := fmt.Sprintf("%s/u/ocitestuser/%s:%s",
|
||||
srv.RegistryURL, c.Metadata.Name, c.Metadata.Version)
|
||||
|
||||
// load it into memory...
|
||||
absPath = filepath.Join(srv.Dir,
|
||||
fmt.Sprintf("%s-%s.tgz", c.Metadata.Name, c.Metadata.Version))
|
||||
contentBytes, err = ioutil.ReadFile(absPath)
|
||||
if err != nil {
|
||||
t.Fatal("could not load chart into memory")
|
||||
}
|
||||
|
||||
result, err = registryClient.Push(contentBytes, dependingRef)
|
||||
if err != nil {
|
||||
t.Fatalf("error pushing depending chart: %s", err)
|
||||
}
|
||||
t.Logf("Manifest.Digest: %s, Manifest.Size: %d, "+
|
||||
"Config.Digest: %s, Config.Size: %d, "+
|
||||
"Chart.Digest: %s, Chart.Size: %d",
|
||||
result.Manifest.Digest, result.Manifest.Size,
|
||||
result.Config.Digest, result.Config.Size,
|
||||
result.Chart.Digest, result.Chart.Size)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// NewTempServer creates a server inside of a temp dir.
|
||||
//
|
||||
// If the passed in string is not "", it will be treated as a shell glob, and files
|
||||
// will be copied from that path to the server's docroot.
|
||||
//
|
||||
// The caller is responsible for destroying the temp directory as well as stopping
|
||||
// the server.
|
||||
//
|
||||
// Deprecated: use NewTempServerWithCleanup
|
||||
func NewTempmtlsServer(glob string) (*mtlsServer, error) {
|
||||
tdir, err := ioutil.TempDir("", "helm-repotest-")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
srv := NewmtlsServer(tdir)
|
||||
|
||||
if glob != "" {
|
||||
if _, err := srv.mtlsCopyCharts(glob); err != nil {
|
||||
srv.Stopmtls()
|
||||
return srv, err
|
||||
}
|
||||
}
|
||||
|
||||
return srv, nil
|
||||
}
|
||||
|
||||
// NewServer creates a repository server for testing.
|
||||
//
|
||||
// docroot should be a temp dir managed by the caller.
|
||||
//
|
||||
// This will start the server, serving files off of the docroot.
|
||||
//
|
||||
// Use CopyCharts to move charts into the repository and then index them
|
||||
// for service.
|
||||
func NewmtlsServer(docroot string) *mtlsServer {
|
||||
root, err := filepath.Abs(docroot)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
srv := &mtlsServer{
|
||||
docroot: root,
|
||||
}
|
||||
srv.mtlsStart()
|
||||
// Add the testing repository as the only repo.
|
||||
if err := setmtlsTestingRepository(srv.mtlsURL(), filepath.Join(root, "repositories.yaml")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return srv
|
||||
}
|
||||
|
||||
// Server is an implementation of a repository server for testing.
|
||||
type mtlsServer struct {
|
||||
docroot string
|
||||
srv *httptest.Server
|
||||
middleware http.HandlerFunc
|
||||
}
|
||||
|
||||
// WithMiddleware injects middleware in front of the server. This can be used to inject
|
||||
// additional functionality like layering in an authentication frontend.
|
||||
func (s *mtlsServer) WithMiddlewaremtls(middleware http.HandlerFunc) {
|
||||
s.middleware = middleware
|
||||
}
|
||||
|
||||
// Root gets the docroot for the server.
|
||||
func (s *mtlsServer) Rootmtls() string {
|
||||
return s.docroot
|
||||
}
|
||||
|
||||
// CopyCharts takes a glob expression and copies those charts to the server root.
|
||||
func (s *mtlsServer) mtlsCopyCharts(origin string) ([]string, error) {
|
||||
files, err := filepath.Glob(origin)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
copied := make([]string, len(files))
|
||||
for i, f := range files {
|
||||
base := filepath.Base(f)
|
||||
newname := filepath.Join(s.docroot, base)
|
||||
data, err := ioutil.ReadFile(f)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
if err := ioutil.WriteFile(newname, data, 0644); err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
copied[i] = newname
|
||||
}
|
||||
|
||||
err = s.mtlsCreateIndex()
|
||||
return copied, err
|
||||
}
|
||||
|
||||
// CreateIndex will read docroot and generate an index.yaml file.
|
||||
func (s *mtlsServer) mtlsCreateIndex() error {
|
||||
// generate the index
|
||||
index, err := repo.IndexDirectory(s.docroot, s.mtlsURL())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d, err := yaml.Marshal(index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ifile := filepath.Join(s.docroot, "index.yaml")
|
||||
return ioutil.WriteFile(ifile, d, 0644)
|
||||
}
|
||||
|
||||
func (s *mtlsServer) mtlsStart() {
|
||||
s.srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if s.middleware != nil {
|
||||
s.middleware.ServeHTTP(w, r)
|
||||
}
|
||||
http.FileServer(http.Dir(s.docroot)).ServeHTTP(w, r)
|
||||
}))
|
||||
}
|
||||
|
||||
func (s *mtlsServer) StartmTLS() {
|
||||
cd := "../../testdata"
|
||||
ca, pub, priv := filepath.Join(cd, "rootca.crt"), filepath.Join(cd, "crt.pem"), filepath.Join(cd, "key.pem")
|
||||
|
||||
s.srv = httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if s.middleware != nil {
|
||||
s.middleware.ServeHTTP(w, r)
|
||||
}
|
||||
http.FileServer(http.Dir(s.Rootmtls())).ServeHTTP(w, r)
|
||||
}))
|
||||
tlsConf, err := tlsutil.NewClientTLS(pub, priv, ca)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tlsConf.BuildNameToCertificate()
|
||||
tlsConf.ServerName = "helm.sh"
|
||||
s.srv.TLS = tlsConf
|
||||
s.srv.StartTLS()
|
||||
|
||||
// Set up repositories config with ca file
|
||||
repoConfig := filepath.Join(s.Rootmtls(), "repositories.yaml")
|
||||
|
||||
r := repo.NewFile()
|
||||
r.Add(&repo.Entry{
|
||||
Name: "test",
|
||||
URL: s.mtlsURL(),
|
||||
CAFile: filepath.Join("../../testdata", "rootca.crt"),
|
||||
})
|
||||
|
||||
if err := r.WriteFile(repoConfig, 0644); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Stop stops the server and closes all connections.
|
||||
//
|
||||
// It should be called explicitly.
|
||||
func (s *mtlsServer) Stopmtls() {
|
||||
s.srv.Close()
|
||||
}
|
||||
|
||||
// URL returns the URL of the server.
|
||||
//
|
||||
// Example:
|
||||
// http://localhost:1776
|
||||
func (s *mtlsServer) mtlsURL() string {
|
||||
return s.srv.URL
|
||||
}
|
||||
|
||||
// LinkIndices links the index created with CreateIndex and makes a symbolic link to the cache index.
|
||||
//
|
||||
// This makes it possible to simulate a local cache of a repository.
|
||||
func (s *mtlsServer) LinkIndicesmtls() error {
|
||||
lstart := filepath.Join(s.docroot, "index.yaml")
|
||||
ldest := filepath.Join(s.docroot, "test-index.yaml")
|
||||
return os.Symlink(lstart, ldest)
|
||||
}
|
||||
|
||||
// setTestingRepository sets up a testing repository.yaml with only the given URL.
|
||||
func setmtlsTestingRepository(url, fname string) error {
|
||||
r := repo.NewFile()
|
||||
r.Add(&repo.Entry{
|
||||
Name: "test",
|
||||
URL: url,
|
||||
})
|
||||
return r.WriteFile(fname, 0644)
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
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 repotest
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"helm.sh/helm/v3/internal/test/ensure"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
)
|
||||
|
||||
// Young'n, in these here parts, we test our tests.
|
||||
|
||||
func TestMtlsServer(t *testing.T) {
|
||||
defer ensure.HelmHome(t)()
|
||||
|
||||
rootDir := ensure.TempDir(t)
|
||||
defer os.RemoveAll(rootDir)
|
||||
|
||||
srv := NewServer(rootDir)
|
||||
defer srv.Stop()
|
||||
|
||||
c, err := srv.CopyCharts("testdata/*.tgz")
|
||||
if err != nil {
|
||||
// Some versions of Go don't correctly fire defer on Fatal.
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(c) != 1 {
|
||||
t.Errorf("Unexpected chart count: %d", len(c))
|
||||
}
|
||||
|
||||
if filepath.Base(c[0]) != "examplechart-0.1.0.tgz" {
|
||||
t.Errorf("Unexpected chart: %s", c[0])
|
||||
}
|
||||
|
||||
res, err := http.Get(srv.URL() + "/examplechart-0.1.0.tgz")
|
||||
res.Body.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if res.ContentLength < 500 {
|
||||
t.Errorf("Expected at least 500 bytes of data, got %d", res.ContentLength)
|
||||
}
|
||||
|
||||
res, err = http.Get(srv.URL() + "/index.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(res.Body)
|
||||
res.Body.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
m := repo.NewIndexFile()
|
||||
if err := yaml.Unmarshal(data, m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if l := len(m.Entries); l != 1 {
|
||||
t.Fatalf("Expected 1 entry, got %d", l)
|
||||
}
|
||||
|
||||
expect := "examplechart"
|
||||
if !m.Has(expect, "0.1.0") {
|
||||
t.Errorf("missing %q", expect)
|
||||
}
|
||||
|
||||
res, err = http.Get(srv.URL() + "/index.yaml-nosuchthing")
|
||||
res.Body.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if res.StatusCode != 404 {
|
||||
t.Fatalf("Expected 404, got %d", res.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewmtlsTempServer(t *testing.T) {
|
||||
defer ensure.HelmHome(t)()
|
||||
|
||||
srv, err := NewTempServerWithCleanup(t, "testdata/examplechart-0.1.0.tgz")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer srv.Stop()
|
||||
|
||||
res, err := http.Head(srv.URL() + "/examplechart-0.1.0.tgz")
|
||||
res.Body.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if res.StatusCode != 200 {
|
||||
t.Errorf("Expected 200, got %d", res.StatusCode)
|
||||
}
|
||||
}
|
Loading…
Reference in new issue