Commit abf1ddc3 authored by Matt Butcher's avatar Matt Butcher

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
parent fcdb79da
...@@ -18,6 +18,7 @@ package repo ...@@ -18,6 +18,7 @@ package repo
import ( import (
"fmt" "fmt"
htemplate "html/template"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"path/filepath" "path/filepath"
...@@ -30,37 +31,74 @@ import ( ...@@ -30,37 +31,74 @@ import (
"k8s.io/helm/pkg/provenance" "k8s.io/helm/pkg/provenance"
) )
var localRepoPath string const indexHTMLTemplate = `
<html>
<head>
<title>Helm Repository</title>
</head>
<h1>Helm Charts Repository</h1>
<ul>
{{range $name, $ver := .Index.Entries}}
<li>{{$name}}<ul>{{range $ver}}
<li><a href="{{index .URLs 0}}">{{.Name}}-{{.Version}}</a></li>
{{end}}</ul>
</li>
{{end}}
</ul>
<body>
<p>Last Generated: {{.Index.Generated}}</p>
</body>
</html>
`
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 // StartLocalRepo starts a web server and serves files from the given path
func StartLocalRepo(path, address string) error { func StartLocalRepo(path, address string) error {
if address == "" { if address == "" {
address = ":8879" address = ":8879"
} }
localRepoPath = path s := &RepositoryServer{RepoPath: path}
http.HandleFunc("/", rootHandler) return http.ListenAndServe(address, s)
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")
}
} }
func serveFile(w http.ResponseWriter, r *http.Request, file string) { func (s *RepositoryServer) htmlIndex(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, filepath.Join(localRepoPath, file)) 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 // AddChartToLocalRepo saves a chart in the given path and then reindexes the index file
......
/*
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", "<html>"},
{"charts root", "/charts/", "<html>"},
{"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))
}
}
}
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"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment