Handle signals, code cleanup, easier labels

This commit is contained in:
Christopher LaPointe
2023-05-26 19:52:17 -04:00
parent af94d29dd3
commit ebb45b55c3
10 changed files with 123 additions and 124 deletions

View File

@@ -1,5 +1,5 @@
README.md
docker-compose.yml
*compose.yml
Dockerfile
.gitignore
traefik-lazyload

4
.gitignore vendored
View File

@@ -1 +1,5 @@
# Binary
traefik-lazyload
# Various testing/compose files
*compose.yml

View File

@@ -14,10 +14,8 @@ var httpAssets embed.FS
const httpAssetPrefix = "/__llassets/"
type SplashModel struct {
Name string
CID string
WaitForCode int
WaitForPath string
*service.ContainerState
Name string
}
var splashTemplate = template.Must(template.ParseFS(httpAssets, path.Join("assets", config.Model.Splash)))

View File

@@ -23,13 +23,13 @@
</div>
<div class="message">
<h2>Starting {{.Name}}</h2>
<h3>{{.CID}}</h3>
<h3>{{.Name}}</h3>
</div>
</div>
<script>
async function testForOk(url) {
const response = await fetch(url, {
method: "HEAD",
method: "{{.WaitForMethod}}",
});
console.log(`Got ${response.status}`);
return response.status === {{.WaitForCode}};

View File

@@ -1,57 +0,0 @@
version: '3.5'
services:
# reverse-proxy:
# image: traefik:v2.4
# command:
# - --api.insecure
# - --providers.docker
# - --providers.docker.defaultRule=Host(`{{.Name}}.d0.lan`)
# - --entryPoints.web.address=:85
# - --entryPoints.web.forwardedHeaders.insecure
# - --providers.docker.exposedByDefault=false
# - --providers.docker.constraints=Label(`my.zone`,`zone1`)
# restart: always
# ports:
# - "85:85" # The HTTP port
# - "8086:8080" # The Web UI (enabled by --api)
# volumes:
# - /var/run/docker.sock:/var/run/docker.sock # So that Traefik can listen to the Docker events
monitor:
build: .
labels:
- traefik.enable=true
- "traefik.http.routers.lazyload.priority=-100"
- "traefik.http.routers.lazyload.rule=Host(`whoami.d.lan`, `whoami2.d.lan`, `pdf.d.lan`, `noexist.d.lan`, `lazyloader.d.lan`)"
environment:
TLL_STOPATBOOT: true
TLL_STATUSHOST: lazyloader.d.lan
networks:
- traefik-bridge
volumes:
- /var/run/docker.sock:/var/run/docker.sock
whoami:
image: containous/whoami
networks:
- traefik-bridge
labels:
- traefik.enable=true
- "traefik.http.routers.lazywhoami.rule=Host(`whoami.d.lan`)"
- lazyloader=true
whoami2:
image: containous/whoami
networks:
- traefik-bridge
labels:
- traefik.enable=true
- "traefik.http.routers.lazywhoami2.rule=Host(`whoami2.d.lan`)"
- lazyloader=true
- lazyloader.stopdelay=1m
networks:
traefik-bridge:
external: true
name: traefik-bridge

32
main.go
View File

@@ -1,11 +1,14 @@
package main
import (
"context"
"errors"
"fmt"
"io"
"io/fs"
"net/http"
"os"
"os/signal"
"runtime"
"traefik-lazyload/pkg/config"
"traefik-lazyload/pkg/service"
@@ -44,14 +47,31 @@ func main() {
// Set up http server
subFs, _ := fs.Sub(httpAssets, "assets")
http.Handle(httpAssetPrefix, http.StripPrefix(httpAssetPrefix, http.FileServer(http.FS(subFs))))
http.HandleFunc("/", ContainerHandler)
router := http.NewServeMux()
router.Handle(httpAssetPrefix, http.StripPrefix(httpAssetPrefix, http.FileServer(http.FS(subFs))))
router.HandleFunc("/", ContainerHandler)
srv := &http.Server{
Addr: config.Model.Listen,
Handler: router,
}
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt)
go func() {
<-sigChan
logrus.Info("Shutting down...")
srv.Shutdown(context.Background())
}()
logrus.Infof("Listening on %s...", config.Model.Listen)
if config.Model.StatusHost != "" {
logrus.Infof("Status host set to %s", config.Model.StatusHost)
}
http.ListenAndServe(config.Model.Listen, nil)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
logrus.Fatal(err)
}
}
func ContainerHandler(w http.ResponseWriter, r *http.Request) {
@@ -77,10 +97,8 @@ func ContainerHandler(w http.ResponseWriter, r *http.Request) {
} else {
w.WriteHeader(http.StatusAccepted)
renderErr := splashTemplate.Execute(w, SplashModel{
Name: host,
CID: sOpts.ContainerName,
WaitForCode: sOpts.WaitForCode,
WaitForPath: sOpts.WaitForPath,
Name: host,
ContainerState: sOpts,
})
if renderErr != nil {
logrus.Error(renderErr)

View File

@@ -1,19 +1,19 @@
package service
import (
"strconv"
"strings"
"time"
"traefik-lazyload/pkg/config"
"github.com/docker/docker/api/types"
"github.com/sirupsen/logrus"
)
type containerSettings struct {
stopDelay time.Duration
waitForCode int
waitForPath string
stopDelay time.Duration
waitForCode int
waitForPath string
waitForMethod string
needs []string
}
type ContainerState struct {
@@ -34,26 +34,11 @@ func newStateFromContainer(ct *types.Container) *ContainerState {
}
func extractContainerLabels(ct *types.Container) (target containerSettings) {
{ // Parse stop delay
stopDelay, _ := labelOrDefault(ct, "stopdelay", config.Model.StopDelay.String())
if dur, stopErr := time.ParseDuration(stopDelay); stopErr != nil {
target.stopDelay = config.Model.StopDelay
logrus.Warnf("Unable to parse stopdelay for %s of %s, defaulting to %s", containerShort(ct), stopDelay, target.stopDelay.String())
} else {
target.stopDelay = dur
}
}
{ // WaitForCode
codeStr, _ := labelOrDefault(ct, "waitforcode", "200")
if code, err := strconv.Atoi(codeStr); err != nil {
target.waitForCode = 200
logrus.Warnf("Unable to parse WaitForCode of %s, defaulting to %d", containerShort(ct), target.waitForCode)
} else {
target.waitForCode = code
}
}
target.stopDelay, _ = labelOrDefaultDuration(ct, "stopdelay", config.Model.StopDelay)
target.waitForCode, _ = labelOrDefaultInt(ct, "waitforcode", 200)
target.waitForPath, _ = labelOrDefault(ct, "waitforpath", "/")
target.waitForMethod, _ = labelOrDefault(ct, "waitformethod", "HEAD")
target.needs, _ = labelOrDefaultArr(ct, "needs")
return
}
@@ -85,6 +70,19 @@ func (s *containerSettings) StopDelay() string {
return s.stopDelay.String()
}
func (s *ContainerState) WaitForCode() int {
return s.waitForCode
}
func (s *ContainerState) WaitForPath() string {
return s.waitForPath
}
func (s *ContainerState) WaitForMethod() string {
return s.waitForMethod
}
// Wrapper for container results that opaques and adds some methods to that data
type ContainerWrapper struct {
types.Container
}

53
pkg/service/labels.go Normal file
View File

@@ -0,0 +1,53 @@
package service
import (
"strconv"
"strings"
"time"
"traefik-lazyload/pkg/config"
"github.com/docker/docker/api/types"
"github.com/sirupsen/logrus"
)
func labelOrDefault(ct *types.Container, sublabel, dflt string) (string, bool) {
if val, ok := ct.Labels[config.SubLabel(sublabel)]; ok {
return val, true
}
return dflt, false
}
func labelOrDefaultArr(ct *types.Container, sublabel string) ([]string, bool) {
if val, ok := ct.Labels[config.SubLabel(sublabel)]; ok {
return strings.Split(val, ","), true
}
return []string{}, false
}
func labelOrDefaultInt(ct *types.Container, sublabel string, dflt int) (int, bool) {
s, ok := labelOrDefault(ct, sublabel, "")
if !ok {
return dflt, false
}
if val, err := strconv.Atoi(s); err != nil {
logrus.Warnf("Unable to parse %s on %s: %v. Using default of %d", sublabel, containerShort(ct), err, dflt)
return dflt, false
} else {
return val, true
}
}
func labelOrDefaultDuration(ct *types.Container, sublabel string, dflt time.Duration) (time.Duration, bool) {
s, ok := labelOrDefault(ct, sublabel, "")
if !ok {
return dflt, false
}
if val, err := time.ParseDuration(s); err != nil {
logrus.Warnf("Unable to parse %s on %s: %v. Using default of %s", sublabel, containerShort(ct), err, dflt.String())
return dflt, false
} else {
return val, true
}
}

View File

@@ -55,13 +55,7 @@ func (s *Core) Close() error {
return s.client.Close()
}
type StartResult struct {
WaitForCode int
WaitForPath string
ContainerName string
}
func (s *Core) StartHost(hostname string) (*StartResult, error) {
func (s *Core) StartHost(hostname string) (*ContainerState, error) {
s.mux.Lock()
defer s.mux.Unlock()
@@ -75,11 +69,7 @@ func (s *Core) StartHost(hostname string) (*StartResult, error) {
if ets, exists := s.active[ct.ID]; exists {
logrus.Debugf("Asked to start host, but we already think it's started: %s", ets.name)
return &StartResult{
WaitForCode: ets.waitForCode,
WaitForPath: ets.waitForPath,
ContainerName: containerShort(ct),
}, nil
return ets, nil
}
go s.startContainer(ctx, ct)
@@ -88,10 +78,7 @@ func (s *Core) StartHost(hostname string) (*StartResult, error) {
ets := newStateFromContainer(ct)
s.active[ct.ID] = ets
return &StartResult{
WaitForCode: ets.waitForCode,
WaitForPath: ets.waitForPath,
}, nil
return ets, nil
}
func (s *Core) StopAll() {
@@ -229,6 +216,15 @@ func (s *Core) checkContainerForInactivity(ctx context.Context, cid string, ct *
return false, nil
}
func (s *Core) findContainersByDepProvider(ctx context.Context, name string) ([]types.Container, error) {
filters := filters.NewArgs()
filters.Add("label", config.SubLabel("providers")+"="+name)
return s.client.ContainerList(ctx, types.ContainerListOptions{
Filters: filters,
All: true,
})
}
func (s *Core) findContainerByHostname(ctx context.Context, hostname string) (*types.Container, error) {
containers, err := s.findAllLazyloadContainers(ctx, true)
if err != nil {

View File

@@ -3,7 +3,6 @@ package service
import (
"fmt"
"strings"
"traefik-lazyload/pkg/config"
"github.com/docker/docker/api/types"
)
@@ -16,13 +15,6 @@ func sumNetworkBytes(networks map[string]types.NetworkStats) (recv int64, send i
return
}
func labelOrDefault(ct *types.Container, sublabel, dflt string) (string, bool) {
if val, ok := ct.Labels[config.SubLabel(sublabel)]; ok {
return val, true
}
return dflt, false
}
func shortId(id string) string {
const SLEN = 8
if len(id) <= SLEN {
@@ -42,10 +34,7 @@ func containerShort(c *types.Container) string {
}
func trimRootPath(s string) string {
if strings.HasPrefix(s, "/") {
return s[1:]
}
return s
return strings.TrimPrefix(s, "/")
}
func isRunning(c *types.Container) bool {