github.com/doitroot/helm@v3.0.0-beta.3+incompatible/pkg/repo/repotest/server.go (about)

     1  /*
     2  Copyright The Helm Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package repotest
    17  
    18  import (
    19  	"io/ioutil"
    20  	"net/http"
    21  	"net/http/httptest"
    22  	"os"
    23  	"path/filepath"
    24  
    25  	"sigs.k8s.io/yaml"
    26  
    27  	"helm.sh/helm/pkg/repo"
    28  )
    29  
    30  // NewTempServer creates a server inside of a temp dir.
    31  //
    32  // If the passed in string is not "", it will be treated as a shell glob, and files
    33  // will be copied from that path to the server's docroot.
    34  //
    35  // The caller is responsible for destroying the temp directory as well as stopping
    36  // the server.
    37  func NewTempServer(glob string) (*Server, error) {
    38  	tdir, err := ioutil.TempDir("", "helm-repotest-")
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  	srv := NewServer(tdir)
    43  
    44  	if glob != "" {
    45  		if _, err := srv.CopyCharts(glob); err != nil {
    46  			srv.Stop()
    47  			return srv, err
    48  		}
    49  	}
    50  
    51  	return srv, nil
    52  }
    53  
    54  // NewServer creates a repository server for testing.
    55  //
    56  // docroot should be a temp dir managed by the caller.
    57  //
    58  // This will start the server, serving files off of the docroot.
    59  //
    60  // Use CopyCharts to move charts into the repository and then index them
    61  // for service.
    62  func NewServer(docroot string) *Server {
    63  	root, err := filepath.Abs(docroot)
    64  	if err != nil {
    65  		panic(err)
    66  	}
    67  	srv := &Server{
    68  		docroot: root,
    69  	}
    70  	srv.Start()
    71  	// Add the testing repository as the only repo.
    72  	if err := setTestingRepository(srv.URL(), filepath.Join(root, "repositories.yaml")); err != nil {
    73  		panic(err)
    74  	}
    75  	return srv
    76  }
    77  
    78  // Server is an implementation of a repository server for testing.
    79  type Server struct {
    80  	docroot    string
    81  	srv        *httptest.Server
    82  	middleware http.HandlerFunc
    83  }
    84  
    85  // WithMiddleware injects middleware in front of the server. This can be used to inject
    86  // additional functionality like layering in an authentication frontend.
    87  func (s *Server) WithMiddleware(middleware http.HandlerFunc) {
    88  	s.middleware = middleware
    89  }
    90  
    91  // Root gets the docroot for the server.
    92  func (s *Server) Root() string {
    93  	return s.docroot
    94  }
    95  
    96  // CopyCharts takes a glob expression and copies those charts to the server root.
    97  func (s *Server) CopyCharts(origin string) ([]string, error) {
    98  	files, err := filepath.Glob(origin)
    99  	if err != nil {
   100  		return []string{}, err
   101  	}
   102  	copied := make([]string, len(files))
   103  	for i, f := range files {
   104  		base := filepath.Base(f)
   105  		newname := filepath.Join(s.docroot, base)
   106  		data, err := ioutil.ReadFile(f)
   107  		if err != nil {
   108  			return []string{}, err
   109  		}
   110  		if err := ioutil.WriteFile(newname, data, 0644); err != nil {
   111  			return []string{}, err
   112  		}
   113  		copied[i] = newname
   114  	}
   115  
   116  	err = s.CreateIndex()
   117  	return copied, err
   118  }
   119  
   120  // CreateIndex will read docroot and generate an index.yaml file.
   121  func (s *Server) CreateIndex() error {
   122  	// generate the index
   123  	index, err := repo.IndexDirectory(s.docroot, s.URL())
   124  	if err != nil {
   125  		return err
   126  	}
   127  
   128  	d, err := yaml.Marshal(index)
   129  	if err != nil {
   130  		return err
   131  	}
   132  
   133  	ifile := filepath.Join(s.docroot, "index.yaml")
   134  	return ioutil.WriteFile(ifile, d, 0644)
   135  }
   136  
   137  func (s *Server) Start() {
   138  	s.srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   139  		if s.middleware != nil {
   140  			s.middleware.ServeHTTP(w, r)
   141  		}
   142  		http.FileServer(http.Dir(s.docroot)).ServeHTTP(w, r)
   143  	}))
   144  }
   145  
   146  // Stop stops the server and closes all connections.
   147  //
   148  // It should be called explicitly.
   149  func (s *Server) Stop() {
   150  	s.srv.Close()
   151  }
   152  
   153  // URL returns the URL of the server.
   154  //
   155  // Example:
   156  //	http://localhost:1776
   157  func (s *Server) URL() string {
   158  	return s.srv.URL
   159  }
   160  
   161  // LinkIndices links the index created with CreateIndex and makes a symbolic link to the cache index.
   162  //
   163  // This makes it possible to simulate a local cache of a repository.
   164  func (s *Server) LinkIndices() error {
   165  	lstart := filepath.Join(s.docroot, "index.yaml")
   166  	ldest := filepath.Join(s.docroot, "test-index.yaml")
   167  	return os.Symlink(lstart, ldest)
   168  }
   169  
   170  // setTestingRepository sets up a testing repository.yaml with only the given URL.
   171  func setTestingRepository(url, fname string) error {
   172  	r := repo.NewFile()
   173  	r.Add(&repo.Entry{
   174  		Name: "test",
   175  		URL:  url,
   176  	})
   177  	return r.WriteFile(fname, 0644)
   178  }