From abf1ddc3244373153dc4098ef0e978fa2303ae2a Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Tue, 18 Oct 2016 17:04:33 -0600 Subject: [PATCH] fix(helm): finish repo index.html Previous versions of Helm had placeholder text in the index.yaml file. This generates an HTML index for 'helm serve'. It also has a refactoring of the server so that the server can be tested. Closes #1397 --- pkg/repo/local.go | 84 +++++++++++++++++++++-------- pkg/repo/local_test.go | 61 +++++++++++++++++++++ pkg/repo/testdata/server/index.yaml | 40 ++++++++++++++ pkg/repo/testdata/server/test.txt | 1 + 4 files changed, 163 insertions(+), 23 deletions(-) create mode 100644 pkg/repo/local_test.go create mode 100644 pkg/repo/testdata/server/index.yaml create mode 100644 pkg/repo/testdata/server/test.txt diff --git a/pkg/repo/local.go b/pkg/repo/local.go index b3105706a..9ef8a5cad 100644 --- a/pkg/repo/local.go +++ b/pkg/repo/local.go @@ -18,6 +18,7 @@ package repo import ( "fmt" + htemplate "html/template" "io/ioutil" "net/http" "path/filepath" @@ -30,37 +31,74 @@ import ( "k8s.io/helm/pkg/provenance" ) -var localRepoPath string +const indexHTMLTemplate = ` + + + Helm Repository + +

Helm Charts Repository

+ + +

Last Generated: {{.Index.Generated}}

+ + +` + +const indexFile = ` +Welcome to the Kubernetes Package manager!\nBrowse charts on localhost:8879/charts! +` + +// RepositoryServer is an HTTP handler for serving a chart repository. +type RepositoryServer struct { + RepoPath string +} + +// ServeHTTP implements the http.Handler interface. +func (s *RepositoryServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { + uri := r.URL.Path + switch uri { + case "/": + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + fmt.Fprintf(w, indexFile) + case "/charts/", "/charts/index.html", "/charts/index": + s.htmlIndex(w, r) + default: + file := strings.TrimPrefix(uri, "/charts/") + http.ServeFile(w, r, filepath.Join(s.RepoPath, file)) + } +} // StartLocalRepo starts a web server and serves files from the given path func StartLocalRepo(path, address string) error { if address == "" { address = ":8879" } - localRepoPath = path - http.HandleFunc("/", rootHandler) - http.HandleFunc("/charts/", indexHandler) - return http.ListenAndServe(address, nil) -} -func rootHandler(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - fmt.Fprintf(w, "Welcome to the Kubernetes Package manager!\nBrowse charts on localhost:8879/charts!") -} -func indexHandler(w http.ResponseWriter, r *http.Request) { - file := r.URL.Path[len("/charts/"):] - if len(strings.Split(file, ".")) > 1 { - serveFile(w, r, file) - } else if file == "" { - fmt.Fprintf(w, "list of charts should be here at some point") - } else if file == "index" { - fmt.Fprintf(w, "index file data should be here at some point") - } else { - fmt.Fprintf(w, "Ummm... Nothing to see here folks") - } + s := &RepositoryServer{RepoPath: path} + return http.ListenAndServe(address, s) } -func serveFile(w http.ResponseWriter, r *http.Request, file string) { - http.ServeFile(w, r, filepath.Join(localRepoPath, file)) +func (s *RepositoryServer) htmlIndex(w http.ResponseWriter, r *http.Request) { + t := htemplate.Must(htemplate.New("index.html").Parse(indexHTMLTemplate)) + // load index + lrp := filepath.Join(s.RepoPath, "index.yaml") + i, err := LoadIndexFile(lrp) + if err != nil { + http.Error(w, err.Error(), 500) + return + } + data := map[string]interface{}{ + "Index": i, + } + if err := t.Execute(w, data); err != nil { + fmt.Fprintf(w, "Template error: %s", err) + } } // AddChartToLocalRepo saves a chart in the given path and then reindexes the index file diff --git a/pkg/repo/local_test.go b/pkg/repo/local_test.go new file mode 100644 index 000000000..e6fdb4f4d --- /dev/null +++ b/pkg/repo/local_test.go @@ -0,0 +1,61 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 repo + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +func TestRepositoryServer(t *testing.T) { + + tests := []struct { + name string + path string + expect string + }{ + {"index YAML", "/charts/index.yaml", "apiVersion: v1"}, + {"index HTML", "/charts/index.html", ""}, + {"charts root", "/charts/", ""}, + {"root", "/", "Welcome"}, + {"file", "/test.txt", "Hello World"}, + } + + s := &RepositoryServer{RepoPath: "testdata/server"} + srv := httptest.NewServer(s) + defer srv.Close() + + for _, tt := range tests { + res, err := http.Get(srv.URL + tt.path) + if err != nil { + t.Errorf("%s: error getting %s: %s", tt.name, tt.path, err) + continue + } + body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Errorf("%s: error reading %s: %s", tt.name, tt.path, err) + } + res.Body.Close() + if !strings.Contains(string(body), tt.expect) { + t.Errorf("%s: expected to find %q in %q", tt.name, tt.expect, string(body)) + } + } + +} diff --git a/pkg/repo/testdata/server/index.yaml b/pkg/repo/testdata/server/index.yaml new file mode 100644 index 000000000..ae29dfd8f --- /dev/null +++ b/pkg/repo/testdata/server/index.yaml @@ -0,0 +1,40 @@ +apiVersion: v1 +entries: + nginx: + - urls: + - http://storage.googleapis.com/kubernetes-charts/nginx-0.1.0.tgz + name: nginx + description: string + version: 0.1.0 + home: https://github.com/something + digest: "sha256:1234567890abcdef" + keywords: + - popular + - web server + - proxy + - urls: + - http://storage.googleapis.com/kubernetes-charts/nginx-0.2.0.tgz + name: nginx + description: string + version: 0.2.0 + home: https://github.com/something/else + digest: "sha256:1234567890abcdef" + keywords: + - popular + - web server + - proxy + alpine: + - urls: + - http://storage.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz + - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz + name: alpine + description: string + version: 1.0.0 + home: https://github.com/something + keywords: + - linux + - alpine + - small + - sumtin + digest: "sha256:1234567890abcdef" + diff --git a/pkg/repo/testdata/server/test.txt b/pkg/repo/testdata/server/test.txt new file mode 100644 index 000000000..557db03de --- /dev/null +++ b/pkg/repo/testdata/server/test.txt @@ -0,0 +1 @@ +Hello World