From 06ff31b6dc0dd1b505fd4c03ab287f324d14ec79 Mon Sep 17 00:00:00 2001 From: Joshua Dolitsky Date: Sun, 8 Oct 2017 13:54:56 -0500 Subject: [PATCH] Add --disable-api and --chart-url flags to CLI --- cmd/chartmuseum/main.go | 12 +++++ pkg/chartmuseum/routes.go | 16 +++--- pkg/chartmuseum/server.go | 12 +++-- pkg/chartmuseum/server_test.go | 96 ++++++++++++++++++++++------------ pkg/repo/index.go | 17 ++++-- pkg/repo/index_test.go | 18 ++++++- 6 files changed, 123 insertions(+), 48 deletions(-) diff --git a/cmd/chartmuseum/main.go b/cmd/chartmuseum/main.go index c91a3a9d..9083ce88 100644 --- a/cmd/chartmuseum/main.go +++ b/cmd/chartmuseum/main.go @@ -39,6 +39,8 @@ func cliHandler(c *cli.Context) { options := chartmuseum.ServerOptions{ Debug: c.Bool("debug"), LogJSON: c.Bool("log-json"), + EnableAPI: !c.Bool("disable-api"), + ChartURL: c.String("chart-url"), StorageBackend: backend, } @@ -117,12 +119,22 @@ var cliFlags = []cli.Flag{ Usage: "output structured logs as json", EnvVar: "LOG_JSON", }, + cli.BoolFlag{ + Name: "disable-api", + Usage: "disable all routes prefixed with /api", + EnvVar: "DISABLE_API", + }, cli.IntFlag{ Name: "port", Value: 8080, Usage: "port to listen on", EnvVar: "PORT", }, + cli.StringFlag{ + Name: "chart-url", + Usage: "absolute url for .tgzs in index.yaml", + EnvVar: "CHART_URL", + }, cli.StringFlag{ Name: "storage", Usage: "storage backend, can be one of: local, amazon, google", diff --git a/pkg/chartmuseum/routes.go b/pkg/chartmuseum/routes.go index 2434e380..9198a8c7 100644 --- a/pkg/chartmuseum/routes.go +++ b/pkg/chartmuseum/routes.go @@ -1,15 +1,17 @@ package chartmuseum -func (server *Server) setRoutes() { +func (server *Server) setRoutes(enableAPI bool) { // Helm Chart Repository server.Router.GET("/index.yaml", server.getIndexFileRequestHandler) server.Router.GET("/charts/:filename", server.getStorageObjectRequestHandler) // Chart Manipulation - server.Router.GET("/api/charts", server.getAllChartsRequestHandler) - server.Router.POST("/api/charts", server.postPackageRequestHandler) - server.Router.POST("/api/prov", server.postProvenanceFileRequestHandler) - server.Router.GET("/api/charts/:name", server.getChartRequestHandler) - server.Router.GET("/api/charts/:name/:version", server.getChartVersionRequestHandler) - server.Router.DELETE("/api/charts/:name/:version", server.deleteChartVersionRequestHandler) + if enableAPI { + server.Router.GET("/api/charts", server.getAllChartsRequestHandler) + server.Router.POST("/api/charts", server.postPackageRequestHandler) + server.Router.POST("/api/prov", server.postProvenanceFileRequestHandler) + server.Router.GET("/api/charts/:name", server.getChartRequestHandler) + server.Router.GET("/api/charts/:name/:version", server.getChartVersionRequestHandler) + server.Router.DELETE("/api/charts/:name/:version", server.deleteChartVersionRequestHandler) + } } diff --git a/pkg/chartmuseum/server.go b/pkg/chartmuseum/server.go index 3425846b..04fc9b6f 100644 --- a/pkg/chartmuseum/server.go +++ b/pkg/chartmuseum/server.go @@ -40,6 +40,8 @@ type ( StorageBackend storage.Backend LogJSON bool Debug bool + EnableAPI bool + ChartURL string } ) @@ -84,13 +86,13 @@ func NewServer(options ServerOptions) (*Server, error) { server := &Server{ Logger: logger, Router: router, - RepositoryIndex: repo.NewIndex(), + RepositoryIndex: repo.NewIndex(options.ChartURL), StorageBackend: options.StorageBackend, StorageCache: []storage.Object{}, StorageCacheLock: &sync.Mutex{}, } - server.setRoutes() + server.setRoutes(options.EnableAPI) err = server.regenerateRepositoryIndex() return server, err @@ -176,7 +178,11 @@ func (server *Server) regenerateRepositoryIndex() error { return err } - index := &repo.Index{IndexFile: server.RepositoryIndex.IndexFile, Raw: server.RepositoryIndex.Raw} + index := &repo.Index{ + IndexFile: server.RepositoryIndex.IndexFile, + Raw: server.RepositoryIndex.Raw, + ChartURL: server.RepositoryIndex.ChartURL, + } for _, object := range diff.Removed { err := server.removeIndexObject(index, object) diff --git a/pkg/chartmuseum/server_test.go b/pkg/chartmuseum/server_test.go index 329ca7fc..fde90839 100644 --- a/pkg/chartmuseum/server_test.go +++ b/pkg/chartmuseum/server_test.go @@ -24,6 +24,7 @@ var testProvfilePath = "../../testdata/charts/mychart/mychart-0.1.0.tgz.prov" type ServerTestSuite struct { suite.Suite Server *Server + DisabledAPIServer *Server BrokenServer *Server TempDirectory string BrokenTempDirectory string @@ -31,11 +32,14 @@ type ServerTestSuite struct { TestProvfileFilename string } -func (suite *ServerTestSuite) doRequest(broken bool, method string, urlStr string, body io.Reader) gin.ResponseWriter { +func (suite *ServerTestSuite) doRequest(broken bool, disabled bool, method string, urlStr string, body io.Reader) gin.ResponseWriter { c, _ := gin.CreateTestContext(httptest.NewRecorder()) c.Request, _ = http.NewRequest(method, urlStr, body) if broken { suite.BrokenServer.Router.HandleContext(c) + + } else if disabled { + suite.DisabledAPIServer.Router.HandleContext(c) } else { suite.Server.Router.HandleContext(c) } @@ -56,19 +60,24 @@ func (suite *ServerTestSuite) SetupSuite() { backend := storage.Backend(storage.NewLocalFilesystemBackend(suite.TempDirectory)) - server, err := NewServer(ServerOptions{backend, false, false}) + server, err := NewServer(ServerOptions{backend, false, false, true, ""}) suite.NotNil(server) - suite.Nil(err, "no error creating new server, logJson=false, debug=false") + suite.Nil(err, "no error creating new server, logJson=false, debug=false, disabled=false") - server, err = NewServer(ServerOptions{backend, true, true}) + server, err = NewServer(ServerOptions{backend, true, true, true, ""}) suite.NotNil(server) - suite.Nil(err, "no error creating new server, logJson=true, debug=true") + suite.Nil(err, "no error creating new server, logJson=true, debug=true, disabled=false") - server, err = NewServer(ServerOptions{backend, false, true}) - suite.Nil(err, "no error creating new server, logJson=false, debug=true") + server, err = NewServer(ServerOptions{backend, false, true, true, ""}) + suite.Nil(err, "no error creating new server, logJson=false, debug=true, disabled=false") suite.Server = server + disabledAPIServer, err := NewServer(ServerOptions{backend, false, true, false, ""}) + suite.Nil(err, "no error creating new server, logJson=false, debug=true, disabled=true") + + suite.DisabledAPIServer = disabledAPIServer + suite.TestTarballFilename = pathutil.Join(suite.TempDirectory, "mychart-0.1.0.tgz") destFileTarball, err := os.Create(suite.TestTarballFilename) suite.Nil(err, "no error creating new tarball in temp dir") @@ -95,7 +104,7 @@ func (suite *ServerTestSuite) SetupSuite() { defer os.RemoveAll(suite.BrokenTempDirectory) brokenBackend := storage.Backend(storage.NewLocalFilesystemBackend(suite.BrokenTempDirectory)) - brokenServer, err := NewServer(ServerOptions{brokenBackend, false, true}) + brokenServer, err := NewServer(ServerOptions{brokenBackend, false, true, true, ""}) suite.Nil(err, "no error creating new server, logJson=false, debug=true") suite.BrokenServer = brokenServer @@ -139,76 +148,76 @@ func (suite *ServerTestSuite) TestRoutes() { var res gin.ResponseWriter // GET /charts/ - res = suite.doRequest(false, "GET", "/charts/mychart-0.1.0.tgz", nil) + res = suite.doRequest(false, false, "GET", "/charts/mychart-0.1.0.tgz", nil) suite.Equal(200, res.Status(), "200 GET /charts/mychart-0.1.0.tgz") - res = suite.doRequest(false, "GET", "/charts/mychart-0.1.0.tgz.prov", nil) + res = suite.doRequest(false, false, "GET", "/charts/mychart-0.1.0.tgz.prov", nil) suite.Equal(200, res.Status(), "200 GET /charts/mychart-0.1.0.tgz.prov") - res = suite.doRequest(false, "GET", "/charts/fakechart-0.1.0.tgz", nil) + res = suite.doRequest(false, false, "GET", "/charts/fakechart-0.1.0.tgz", nil) suite.Equal(404, res.Status(), "404 GET /charts/fakechart-0.1.0.tgz") - res = suite.doRequest(false, "GET", "/charts/fakechart-0.1.0.tgz.prov", nil) + res = suite.doRequest(false, false, "GET", "/charts/fakechart-0.1.0.tgz.prov", nil) suite.Equal(404, res.Status(), "404 GET /charts/fakechart-0.1.0.tgz.prov") - res = suite.doRequest(false, "GET", "/charts/fakechart-0.1.0.bad", nil) + res = suite.doRequest(false, false, "GET", "/charts/fakechart-0.1.0.bad", nil) suite.Equal(500, res.Status(), "500 GET /charts/fakechart-0.1.0.bad") // GET /api/charts - res = suite.doRequest(false, "GET", "/api/charts", nil) + res = suite.doRequest(false, false, "GET", "/api/charts", nil) suite.Equal(200, res.Status(), "200 GET /api/charts") - res = suite.doRequest(true, "GET", "/api/charts", nil) + res = suite.doRequest(true, false, "GET", "/api/charts", nil) suite.Equal(500, res.Status(), "500 GET /api/charts") // GET /api/charts/ - res = suite.doRequest(false, "GET", "/api/charts/mychart", nil) + res = suite.doRequest(false, false, "GET", "/api/charts/mychart", nil) suite.Equal(200, res.Status(), "200 GET /api/charts/mychart") - res = suite.doRequest(false, "GET", "/api/charts/fakechart", nil) + res = suite.doRequest(false, false, "GET", "/api/charts/fakechart", nil) suite.Equal(404, res.Status(), "404 GET /api/charts/fakechart") - res = suite.doRequest(true, "GET", "/api/charts/mychart", nil) + res = suite.doRequest(true, false, "GET", "/api/charts/mychart", nil) suite.Equal(500, res.Status(), "500 GET /api/charts/mychart") // GET /api/charts// - res = suite.doRequest(false, "GET", "/api/charts/mychart/0.1.0", nil) + res = suite.doRequest(false, false, "GET", "/api/charts/mychart/0.1.0", nil) suite.Equal(200, res.Status(), "200 GET /api/charts/mychart/0.1.0") - res = suite.doRequest(false, "GET", "/api/charts/mychart/latest", nil) + res = suite.doRequest(false, false, "GET", "/api/charts/mychart/latest", nil) suite.Equal(200, res.Status(), "200 GET /api/charts/mychart/latest") - res = suite.doRequest(false, "GET", "/api/charts/mychart/0.0.0", nil) + res = suite.doRequest(false, false, "GET", "/api/charts/mychart/0.0.0", nil) suite.Equal(404, res.Status(), "404 GET /api/charts/mychart/0.0.0") - res = suite.doRequest(false, "GET", "/api/charts/fakechart/0.1.0", nil) + res = suite.doRequest(false, false, "GET", "/api/charts/fakechart/0.1.0", nil) suite.Equal(404, res.Status(), "404 GET /api/charts/fakechart/0.1.0") - res = suite.doRequest(true, "GET", "/api/charts/mychart/0.1.0", nil) + res = suite.doRequest(true, false, "GET", "/api/charts/mychart/0.1.0", nil) suite.Equal(500, res.Status(), "500 GET /api/charts/mychart/0.1.0") // DELETE /api/charts// - res = suite.doRequest(false, "DELETE", "/api/charts/mychart/0.1.0", nil) + res = suite.doRequest(false, false, "DELETE", "/api/charts/mychart/0.1.0", nil) suite.Equal(200, res.Status(), "200 DELETE /api/charts/mychart/0.1.0") - res = suite.doRequest(false, "DELETE", "/api/charts/mychart/0.1.0", nil) + res = suite.doRequest(false, false, "DELETE", "/api/charts/mychart/0.1.0", nil) suite.Equal(404, res.Status(), "404 DELETE /api/charts/mychart/0.1.0") // GET /index.yaml - res = suite.doRequest(false, "GET", "/index.yaml", nil) + res = suite.doRequest(false, false, "GET", "/index.yaml", nil) suite.Equal(200, res.Status(), "200 GET /index.yaml") - res = suite.doRequest(true, "GET", "/index.yaml", nil) + res = suite.doRequest(true, false, "GET", "/index.yaml", nil) suite.Equal(500, res.Status(), "500 GET /index.yaml") // POST /api/charts body = bytes.NewBuffer([]byte{}) - res = suite.doRequest(false, "POST", "/api/charts", body) + res = suite.doRequest(false, false, "POST", "/api/charts", body) suite.Equal(500, res.Status(), "500 POST /api/charts") // POST /api/prov body = bytes.NewBuffer([]byte{}) - res = suite.doRequest(false, "POST", "/api/prov", body) + res = suite.doRequest(false, false, "POST", "/api/prov", body) suite.Equal(500, res.Status(), "500 POST /api/prov") // POST /api/charts @@ -216,11 +225,11 @@ func (suite *ServerTestSuite) TestRoutes() { suite.Nil(err, "no error opening test tarball") body = bytes.NewBuffer(content) - res = suite.doRequest(false, "POST", "/api/charts", body) + res = suite.doRequest(false, false, "POST", "/api/charts", body) suite.Equal(201, res.Status(), "201 POST /api/charts") body = bytes.NewBuffer(content) - res = suite.doRequest(false, "POST", "/api/charts", body) + res = suite.doRequest(false, false, "POST", "/api/charts", body) suite.Equal(500, res.Status(), "500 POST /api/charts") // POST /api/prov @@ -228,12 +237,33 @@ func (suite *ServerTestSuite) TestRoutes() { suite.Nil(err, "no error opening test provenance file") body = bytes.NewBuffer(content) - res = suite.doRequest(false, "POST", "/api/prov", body) + res = suite.doRequest(false, false, "POST", "/api/prov", body) suite.Equal(201, res.Status(), "201 POST /api/prov") body = bytes.NewBuffer(content) - res = suite.doRequest(false, "POST", "/api/prov", body) + res = suite.doRequest(false, false, "POST", "/api/prov", body) suite.Equal(500, res.Status(), "500 POST /api/prov") + + // Test that all /api routes disabled if EnableAPI=false + res = suite.doRequest(false, true, "GET", "/api/charts", nil) + suite.Equal(404, res.Status(), "404 GET /api/charts") + + res = suite.doRequest(false, true, "GET", "/api/charts/mychart", nil) + suite.Equal(404, res.Status(), "404 GET /api/charts") + + res = suite.doRequest(false, true, "GET", "/api/charts/mychart/0.1.0", nil) + suite.Equal(404, res.Status(), "404 GET /api/charts") + + body = bytes.NewBuffer([]byte{}) + res = suite.doRequest(false, true, "POST", "/api/charts", body) + suite.Equal(404, res.Status(), "404 POST /api/charts") + + body = bytes.NewBuffer([]byte{}) + res = suite.doRequest(false, true, "POST", "/api/prov", body) + suite.Equal(404, res.Status(), "404 POST /api/prov") + + res = suite.doRequest(false, true, "DELETE", "/api/charts/mychart/0.1.0", nil) + suite.Equal(404, res.Status(), "404 DELETE /api/charts/mychart/0.1.0") } func TestServerTestSuite(t *testing.T) { diff --git a/pkg/repo/index.go b/pkg/repo/index.go index 6ca28a39..4d67ba73 100644 --- a/pkg/repo/index.go +++ b/pkg/repo/index.go @@ -1,6 +1,7 @@ package repo import ( + "strings" "time" "github.com/ghodss/yaml" @@ -16,12 +17,14 @@ var ( // Index represents the repository index (index.yaml) type Index struct { *helm_repo.IndexFile - Raw []byte + Raw []byte + ChartURL string } // NewIndex creates a new instance of Index -func NewIndex() *Index { - index := Index{&helm_repo.IndexFile{}, []byte{}} +func NewIndex(chartURL string) *Index { + chartURL = strings.TrimSuffix(chartURL, "/") + index := Index{&helm_repo.IndexFile{}, []byte{}, chartURL} index.Entries = map[string]helm_repo.ChartVersions{} index.APIVersion = helm_repo.APIVersionV1 return &index @@ -63,6 +66,7 @@ func (index *Index) AddEntry(chartVersion *helm_repo.ChartVersion) { if _, ok := index.Entries[chartVersion.Name]; !ok { index.Entries[chartVersion.Name] = helm_repo.ChartVersions{} } + index.setChartURL(chartVersion) index.Entries[chartVersion.Name] = append(index.Entries[chartVersion.Name], chartVersion) } @@ -72,6 +76,7 @@ func (index *Index) UpdateEntry(chartVersion *helm_repo.ChartVersion) { if k == chartVersion.Name { for i, cv := range index.Entries[chartVersion.Name] { if cv.Version == chartVersion.Version { + index.setChartURL(chartVersion) index.Entries[chartVersion.Name][i] = chartVersion break } @@ -80,3 +85,9 @@ func (index *Index) UpdateEntry(chartVersion *helm_repo.ChartVersion) { } } } + +func (index *Index) setChartURL(chartVersion *helm_repo.ChartVersion) { + if index.ChartURL != "" { + chartVersion.URLs[0] = strings.Join([]string{index.ChartURL, chartVersion.URLs[0]}, "/") + } +} diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go index b2247c4d..ad8806df 100644 --- a/pkg/repo/index_test.go +++ b/pkg/repo/index_test.go @@ -23,7 +23,7 @@ func getChartVersion(name string, patch int, created time.Time) *helm_repo.Chart } chartVersion := helm_repo.ChartVersion{ Metadata: &metadata, - URLs: []string{}, + URLs: []string{fmt.Sprintf("charts/%s-%s.tgz", name, version)}, Created: created, Removed: false, Digest: "", @@ -32,7 +32,7 @@ func getChartVersion(name string, patch int, created time.Time) *helm_repo.Chart } func (suite *IndexTestSuite) SetupSuite() { - suite.Index = NewIndex() + suite.Index = NewIndex("") now := time.Now() for _, name := range []string{"a", "b", "c"} { for i := 0; i < 10; i++ { @@ -71,6 +71,20 @@ func (suite *IndexTestSuite) TestRemove() { suite.Index.RemoveEntry(chartVersion) } +func (suite *IndexTestSuite) TestChartURLs() { + index := NewIndex("") + chartVersion := getChartVersion("a", 0, time.Now()) + index.AddEntry(chartVersion) + suite.Equal("charts/a-1.0.0.tgz", + index.Entries["a"][0].URLs[0], "relative chart url") + + index = NewIndex("http://mysite.com:8080/") + chartVersion = getChartVersion("a", 0, time.Now()) + index.AddEntry(chartVersion) + suite.Equal("http://mysite.com:8080/charts/a-1.0.0.tgz", + index.Entries["a"][0].URLs[0], "absolute chart url") +} + func TestIndexTestSuite(t *testing.T) { suite.Run(t, new(IndexTestSuite)) }