Skip to content
Snippets Groups Projects
Commit db11c6fb authored by Bruce Flynn's avatar Bruce Flynn
Browse files

Update chart index on interval rather than on upload

parent 9bdb8747
Branches main
No related tags found
No related merge requests found
Pipeline #31505 failed with stage
......@@ -8,6 +8,7 @@ import (
"os"
"os/exec"
"path"
"time"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
......@@ -20,7 +21,7 @@ func httpFail(w http.ResponseWriter, r *http.Request, msg string, err error) {
http.Error(w, msg, http.StatusInternalServerError)
}
func updateIndex() error {
func updateIndex(url, dir string) error {
cmd := exec.Command("helm", "repo", "index", "--url", url, dir)
cmd.Stderr = os.Stderr
err := cmd.Run()
......@@ -30,92 +31,32 @@ func updateIndex() error {
return nil
}
func handleUpload(w http.ResponseWriter, r *http.Request) {
p := path.Join(dir, r.URL.Path)
st, err := os.Stat(p)
// got an error and the file exists... something interesting perhaps?
if err != nil && !os.IsNotExist(err) {
http.Error(w, "InternalServerError", http.StatusInternalServerError)
return
}
// got a stat result, file must exist already and we should not overwrite
if st != nil {
http.Error(w, "File exists", http.StatusForbidden)
return
}
// file does not exist, proceed with upload
f, err := os.Create(p)
if err != nil {
httpFail(w, r, "Could not complete upload", err)
return
}
if _, err := io.Copy(f, r.Body); err != nil {
httpFail(w, r, "Could not complete upload", err)
return
}
err = updateIndex()
if err != nil {
httpFail(w, r, "Could not update index", err)
// best-effort to remove the file if we could not update the index
os.Remove(p)
return
}
log.Printf("successfully handled chart %s", path.Base(p))
}
func handleGetHome(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(fmt.Sprintf(`
<div>
<h1>Chart Repository</h1>
<p>
To add this repostory to Helm, run:
<code>
<pre>
helm repo add &lt;name&gt; %s
</pre>
</code>
</p>
<h3>API</h3>
<dl>
<dt>GET /index.yaml</dt>
<dd>Get chart index file used by Helm</dd>
<dt>GET /<version>-<version>.tgz</dt>
<dd>Get a Heml chart package</dd>
<dt>PUT /<version>-<version>.tgz</dt>
<dd>Upload a new chart package to the index</dd>
<dt>GET /files</dt>
<dd>Basic file listing of chart directory</code></dd>
<dt>GET /status</dt>
<dd>Health check that simply returns <code>OK!</code></dd>
</dl>
</div>
`, url)))
}
func createUploadHandler(dir string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
p := path.Join(dir, r.URL.Path)
st, err := os.Stat(p)
// got an error and the file exists... something interesting perhaps?
if err != nil && !os.IsNotExist(err) {
http.Error(w, "InternalServerError", http.StatusInternalServerError)
return
}
// got a stat result, file must exist already and we should not overwrite
if st != nil {
http.Error(w, "File exists", http.StatusForbidden)
return
}
func handleGetFile(w http.ResponseWriter, r *http.Request) {
p := path.Join(dir, r.URL.Path)
st, err := os.Stat(p)
if err != nil && os.IsNotExist(err) || st != nil && st.IsDir() {
http.Error(w, "Not Found", http.StatusNotFound)
return
}
f, err := os.Open(p)
if err != nil {
httpFail(w, r, "InternalServerError", err)
return
}
if _, err := io.Copy(w, f); err != nil {
httpFail(w, r, "InternalServerError", err)
return
// file does not exist, proceed with upload
f, err := os.Create(p)
if err != nil {
httpFail(w, r, "Could not complete upload", err)
return
}
if _, err := io.Copy(f, r.Body); err != nil {
httpFail(w, r, "Could not complete upload", err)
return
}
log.Printf("successfully handled chart %s", path.Base(p))
}
}
......@@ -132,9 +73,6 @@ func getEnvDefault(name, def string) string {
}
var (
dir string
host string
url string
buildStr string
)
......@@ -157,6 +95,10 @@ Options can also be specified as environment variables $CHART_SVR_<name>
fmt.Fprintf(os.Stderr, "\n%s\n", buildStr)
}
var (
dir, host, url string
)
pflag.StringVar(&dir, "dir", getEnvDefault("CHART_SVR_DIR", "."), "Chart report dir")
pflag.StringVar(&host, "host", getEnvDefault("CHART_SVR_HOST", ":8080"), "Host to bind to [<host>]:<port>")
pflag.StringVar(&url, "url", getEnvDefault("CHART_SVR_URL", ""), "Externally accessible URL to the chart repository")
......@@ -178,11 +120,21 @@ Options can also be specified as environment variables $CHART_SVR_<name>
r := mux.NewRouter()
r.HandleFunc("/status", handleStatus).Methods("GET")
// Limit to only index.yaml and chart (.tgz) files
r.HandleFunc(`/{chart:.*-.*.tgz}`, handleUpload).Methods("PUT")
r.HandleFunc(`/{chart:.*-.*.tgz}`, createUploadHandler(dir)).Methods("PUT")
r.PathPrefix("/").Handler(http.FileServer(http.Dir(dir)))
h := handlers.CombinedLoggingHandler(os.Stdout, handlers.CORS()(r))
go func() {
tick := time.NewTicker(time.Minute)
for {
<-tick.C
if err := updateIndex(url, dir); err != nil {
log.Printf("failed to update chart index at %s: %s", dir, err)
}
}
}()
log.Printf("serving charts from %s on %s", dir, host)
log.Fatal(http.ListenAndServe(host, h))
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment