|
|
|
@ -17,6 +17,7 @@ package repotest
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"crypto/tls"
|
|
|
|
|
"fmt"
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/http/httptest"
|
|
|
|
@ -33,7 +34,6 @@ import (
|
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
|
"sigs.k8s.io/yaml"
|
|
|
|
|
|
|
|
|
|
"helm.sh/helm/v4/internal/tlsutil"
|
|
|
|
|
"helm.sh/helm/v4/pkg/chart"
|
|
|
|
|
"helm.sh/helm/v4/pkg/chart/loader"
|
|
|
|
|
"helm.sh/helm/v4/pkg/chartutil"
|
|
|
|
@ -41,34 +41,103 @@ import (
|
|
|
|
|
"helm.sh/helm/v4/pkg/repo"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// NewTempServerWithCleanup creates a server inside of a temp dir.
|
|
|
|
|
func BasicAuthMiddleware(t *testing.T) http.HandlerFunc {
|
|
|
|
|
return http.HandlerFunc(func(_ 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)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ServerOption func(*testing.T, *Server)
|
|
|
|
|
|
|
|
|
|
func WithTLSConfig(tlsConfig *tls.Config) ServerOption {
|
|
|
|
|
return func(_ *testing.T, server *Server) {
|
|
|
|
|
server.tlsConfig = tlsConfig
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func WithMiddleware(middleware http.HandlerFunc) ServerOption {
|
|
|
|
|
return func(_ *testing.T, server *Server) {
|
|
|
|
|
server.middleware = middleware
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func WithChartSourceGlob(glob string) ServerOption {
|
|
|
|
|
return func(_ *testing.T, server *Server) {
|
|
|
|
|
server.chartSourceGlob = glob
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Server is an implementation of a repository server for testing.
|
|
|
|
|
type Server struct {
|
|
|
|
|
docroot string
|
|
|
|
|
srv *httptest.Server
|
|
|
|
|
middleware http.HandlerFunc
|
|
|
|
|
tlsConfig *tls.Config
|
|
|
|
|
chartSourceGlob string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 stopping the server.
|
|
|
|
|
// The server is started automatically. The caller is responsible for stopping
|
|
|
|
|
// the server.
|
|
|
|
|
//
|
|
|
|
|
// The temp dir will be removed by testing package automatically when test finished.
|
|
|
|
|
func NewTempServerWithCleanup(t *testing.T, glob string) (*Server, error) {
|
|
|
|
|
srv, err := NewTempServer(glob)
|
|
|
|
|
func NewTempServer(t *testing.T, options ...ServerOption) *Server {
|
|
|
|
|
|
|
|
|
|
docrootTempDir, err := os.MkdirTemp("", "helm-repotest-")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srv := newServer(t, docrootTempDir, options...)
|
|
|
|
|
|
|
|
|
|
t.Cleanup(func() { os.RemoveAll(srv.docroot) })
|
|
|
|
|
return srv, err
|
|
|
|
|
|
|
|
|
|
if srv.chartSourceGlob != "" {
|
|
|
|
|
if _, err := srv.CopyCharts(srv.chartSourceGlob); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return srv
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set up a fake repo with basic auth enabled
|
|
|
|
|
func NewTempServerWithCleanupAndBasicAuth(t *testing.T, glob string) *Server {
|
|
|
|
|
srv, err := NewTempServerWithCleanup(t, glob)
|
|
|
|
|
srv.Stop()
|
|
|
|
|
// Create the server, but don't yet start it
|
|
|
|
|
func newServer(t *testing.T, docroot string, options ...ServerOption) *Server {
|
|
|
|
|
absdocroot, err := filepath.Abs(docroot)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
srv.WithMiddleware(http.HandlerFunc(func(_ 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)
|
|
|
|
|
|
|
|
|
|
s := &Server{
|
|
|
|
|
docroot: absdocroot,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, option := range options {
|
|
|
|
|
option(t, s)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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.Root())).ServeHTTP(w, r)
|
|
|
|
|
}))
|
|
|
|
|
srv.Start()
|
|
|
|
|
return srv
|
|
|
|
|
|
|
|
|
|
s.start()
|
|
|
|
|
|
|
|
|
|
// Add the testing repository as the only repo. Server must be started for the server's URL to be valid
|
|
|
|
|
if err := setTestingRepository(s.URL(), filepath.Join(s.docroot, "repositories.yaml")); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type OCIServer struct {
|
|
|
|
@ -239,69 +308,6 @@ func (srv *OCIServer) Run(t *testing.T, opts ...OCIServerOpt) {
|
|
|
|
|
result.Chart.Digest, result.Chart.Size)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 NewTempServer(glob string) (*Server, error) {
|
|
|
|
|
tdir, err := os.MkdirTemp("", "helm-repotest-")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
srv := NewServer(tdir)
|
|
|
|
|
|
|
|
|
|
if glob != "" {
|
|
|
|
|
if _, err := srv.CopyCharts(glob); err != nil {
|
|
|
|
|
srv.Stop()
|
|
|
|
|
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 NewServer(docroot string) *Server {
|
|
|
|
|
root, err := filepath.Abs(docroot)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
srv := &Server{
|
|
|
|
|
docroot: root,
|
|
|
|
|
}
|
|
|
|
|
srv.Start()
|
|
|
|
|
// Add the testing repository as the only repo.
|
|
|
|
|
if err := setTestingRepository(srv.URL(), filepath.Join(root, "repositories.yaml")); err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
return srv
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Server is an implementation of a repository server for testing.
|
|
|
|
|
type Server 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 *Server) WithMiddleware(middleware http.HandlerFunc) {
|
|
|
|
|
s.middleware = middleware
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Root gets the docroot for the server.
|
|
|
|
|
func (s *Server) Root() string {
|
|
|
|
|
return s.docroot
|
|
|
|
@ -348,50 +354,12 @@ func (s *Server) CreateIndex() error {
|
|
|
|
|
return os.WriteFile(ifile, d, 0644)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Server) Start() {
|
|
|
|
|
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 *Server) StartTLS() {
|
|
|
|
|
cd := "../../testdata"
|
|
|
|
|
ca, pub, priv := filepath.Join(cd, "rootca.crt"), filepath.Join(cd, "crt.pem"), filepath.Join(cd, "key.pem")
|
|
|
|
|
insecure := false
|
|
|
|
|
|
|
|
|
|
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.Root())).ServeHTTP(w, r)
|
|
|
|
|
}))
|
|
|
|
|
tlsConf, err := tlsutil.NewTLSConfig(
|
|
|
|
|
tlsutil.WithInsecureSkipVerify(insecure),
|
|
|
|
|
tlsutil.WithCertKeyPairFiles(pub, priv),
|
|
|
|
|
tlsutil.WithCAFile(ca),
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
tlsConf.ServerName = "helm.sh"
|
|
|
|
|
s.srv.TLS = tlsConf
|
|
|
|
|
s.srv.StartTLS()
|
|
|
|
|
|
|
|
|
|
// Set up repositories config with ca file
|
|
|
|
|
repoConfig := filepath.Join(s.Root(), "repositories.yaml")
|
|
|
|
|
|
|
|
|
|
r := repo.NewFile()
|
|
|
|
|
r.Add(&repo.Entry{
|
|
|
|
|
Name: "test",
|
|
|
|
|
URL: s.URL(),
|
|
|
|
|
CAFile: filepath.Join("../../testdata", "rootca.crt"),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if err := r.WriteFile(repoConfig, 0600); err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
func (s *Server) start() {
|
|
|
|
|
if s.tlsConfig != nil {
|
|
|
|
|
s.srv.TLS = s.tlsConfig
|
|
|
|
|
s.srv.StartTLS()
|
|
|
|
|
} else {
|
|
|
|
|
s.srv.Start()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -411,6 +379,10 @@ func (s *Server) URL() string {
|
|
|
|
|
return s.srv.URL
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Server) Client() *http.Client {
|
|
|
|
|
return s.srv.Client()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
@ -422,6 +394,10 @@ func (s *Server) LinkIndices() error {
|
|
|
|
|
|
|
|
|
|
// setTestingRepository sets up a testing repository.yaml with only the given URL.
|
|
|
|
|
func setTestingRepository(url, fname string) error {
|
|
|
|
|
if url == "" {
|
|
|
|
|
panic("no url")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r := repo.NewFile()
|
|
|
|
|
r.Add(&repo.Entry{
|
|
|
|
|
Name: "test",
|
|
|
|
|