feat: add custom loading page (#15)

* add option for custom loading and error pages
* document custom landing pages
* link to example templates
* update loading page template to match template actually in-use
This commit is contained in:
Jenn
2021-12-09 16:34:17 -05:00
committed by GitHub
parent 124e619f08
commit 3178fdcc57
5 changed files with 66 additions and 28 deletions

View File

@@ -23,18 +23,26 @@ Traefik middleware to start containers on demand.
### Plugin configuration
**Custom loading/error pages**
The `loadingpage` and `errorpage` keys in the plugin configuration can be used to override the default loading and error pages. The value should be a path where a template that can be parsed by Go's [html/template](https://pkg.go.dev/html/template) package can be found in the Traefik container. An example of both a loading page and an error page template can be found in the [pkg/pages/](pkg/pages/) directory in [loading.html](pkg/pages/loading.html) and [error.html](pkg/pages/error.html) respectively. The plugin will default to the built-in loading and error pages if these fields are omitted.
**Example Configuration**
```yml
testData:
serviceUrl: http://ondemand:10000
name: TRAEFIK_HACKATHON_whoami
timeout: 1m
loadingpage: /opt/on-demand/loading.html
errorpage: /opt/on-demand/error.html
```
| Parameter | Type | Example | Description |
| ------------ | --------------- | -------------------------- | ----------------------------------------------------------------------- |
| `serviceUrl` | `string` | `http://ondemand:10000` | The docker container name, or the swarm service name |
| `name` | `string` | `TRAEFIK_HACKATHON_whoami` | The container/service to be stopped (docker ps | docker service ls) |
| `timeout` | `time.Duration` | `1m30s` | The duration after which the container/service will be scaled down to 0 |
| Parameter | Type | Example | Description |
| ------------ | --------------- | -------------------------- | ----------------------------------------------------------------------- |
| `serviceUrl` | `string` | `http://ondemand:10000` | The docker container name, or the swarm service name |
| `name` | `string` | `TRAEFIK_HACKATHON_whoami` | The container/service to be stopped (docker ps | docker service ls) |
| `timeout` | `time.Duration` | `1m30s` | The duration after which the container/service will be scaled down to 0 |
| `loadingpage`| `string` | `/opt/on-demand/loading.html` | The path in the traefik container for the loading page template |
| `errorpage` | `string` | `/opt/on-demand/error.html` | The path in the traefik container for the error page template |
### Traefik-Ondemand-Service

View File

@@ -19,9 +19,11 @@ var netClient = &http.Client{
// Config the plugin configuration
type Config struct {
Name string `yaml:"name"`
ServiceUrl string `yaml:"serviceurl"`
Timeout string `yaml:"timeout"`
Name string `yaml:"name"`
ServiceUrl string `yaml:"serviceurl"`
Timeout string `yaml:"timeout"`
ErrorPage string `yaml:"errorpage"`
LoadingPage string `yaml:"loadingpage"`
}
// CreateConfig creates a config with its default values
@@ -33,10 +35,12 @@ func CreateConfig() *Config {
// Ondemand holds the request for the on demand service
type Ondemand struct {
request string
name string
next http.Handler
timeout time.Duration
request string
name string
next http.Handler
timeout time.Duration
errorpage string
loadingpage string
}
func buildRequest(url string, name string, timeout time.Duration) (string, error) {
@@ -67,10 +71,12 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h
}
return &Ondemand{
next: next,
name: config.Name,
request: request,
timeout: timeout,
next: next,
name: config.Name,
request: request,
timeout: timeout,
errorpage: config.ErrorPage,
loadingpage: config.LoadingPage,
}, nil
}
@@ -83,7 +89,7 @@ func (e *Ondemand) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if err != nil {
rw.WriteHeader(http.StatusInternalServerError)
rw.Write([]byte(pages.GetErrorPage(e.name, err.Error())))
rw.Write([]byte(pages.GetErrorPage(e.errorpage, e.name, err.Error())))
}
if status == "started" {
@@ -93,11 +99,11 @@ func (e *Ondemand) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
} else if status == "starting" {
// Service starting, notify client
rw.WriteHeader(http.StatusAccepted)
rw.Write([]byte(pages.GetLoadingPage(e.name, e.timeout)))
rw.Write([]byte(pages.GetLoadingPage(e.loadingpage, e.name, e.timeout)))
} else {
// Error
rw.WriteHeader(http.StatusInternalServerError)
rw.Write([]byte(pages.GetErrorPage(e.name, status)))
rw.Write([]byte(pages.GetErrorPage(e.errorpage, e.name, status)))
}
}

View File

@@ -3,6 +3,7 @@ package pages
import (
"bytes"
"html/template"
"path"
)
var errorPage = `<!doctype html>
@@ -175,15 +176,26 @@ type ErrorData struct {
Error string
}
func GetErrorPage(name string, e string) string {
tpl, err := template.New("error").Parse(errorPage)
func GetErrorPage(template_path string, name string, e string) string {
var tpl *template.Template
var err error
if template_path != "" {
tpl, err = template.New(path.Base(template_path)).ParseFiles(template_path)
} else {
tpl, err = template.New("loading").Parse(loadingPage)
}
if err != nil {
return err.Error()
}
b := bytes.Buffer{}
tpl.Execute(&b, ErrorData{
err = tpl.Execute(&b, ErrorData{
Name: name,
Error: e,
})
if err != nil {
return err.Error()
}
return b.String()
}

View File

@@ -2,6 +2,7 @@ package pages
import (
"bytes"
"path"
"fmt"
"html/template"
@@ -251,16 +252,27 @@ type LoadingData struct {
Timeout string
}
func GetLoadingPage(name string, timeout time.Duration) string {
tpl, err := template.New("loading").Parse(loadingPage)
func GetLoadingPage(template_path string, name string, timeout time.Duration) string {
var tpl *template.Template
var err error
if template_path != "" {
tpl, err = template.New(path.Base(template_path)).ParseFiles(template_path)
} else {
tpl, err = template.New("loading").Parse(loadingPage)
}
if err != nil {
return err.Error()
}
b := bytes.Buffer{}
tpl.Execute(&b, LoadingData{
err = tpl.Execute(&b, LoadingData{
Name: name,
Timeout: humanizeDuration(timeout),
})
if err != nil {
return err.Error()
}
return b.String()
}

View File

@@ -214,7 +214,7 @@
</header>
<section class="panel">
<h2 class="headline" id="headline">{{name}} is loading...</h2>
<h2 class="headline" id="headline">{{ .Name }} is loading...</h2>
<div class="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
<p class="message text" id="message">Your instance is loading, and will be
@@ -222,7 +222,7 @@
<div class="support text">
Your instance will shutdown automatically after {{duration}} of
Your instance will shutdown automatically after {{ .Timeout }} of
inactivity.
</div>
</section>
@@ -233,4 +233,4 @@
</footer>
</body>
</html>
</html>