adapt ui to new assets

This commit is contained in:
Mikaël Cluseau
2026-05-08 15:01:44 +02:00
parent 7a6310c93e
commit b0e84f6aa8
12 changed files with 97 additions and 41 deletions
+17 -4
View File
@@ -262,14 +262,17 @@ func wsDownloadSet(req *restful.Request, resp *restful.Response) {
for _, item := range set.Items { for _, item := range set.Items {
names := make([]string, 0) names := make([]string, 0)
section := ""
switch item.Kind { switch item.Kind {
case "cluster": case "cluster":
section = "Cluster"
for _, c := range cfg.Clusters { for _, c := range cfg.Clusters {
if globMatch(item.Name, c.Name) { if globMatch(item.Name, c.Name) {
names = append(names, c.Name) names = append(names, c.Name)
} }
} }
case "host": case "host":
section = "Host"
for _, h := range cfg.Hosts { for _, h := range cfg.Hosts {
if globMatch(item.Name, h.Name) { if globMatch(item.Name, h.Name) {
names = append(names, h.Name) names = append(names, h.Name)
@@ -278,12 +281,22 @@ func wsDownloadSet(req *restful.Request, resp *restful.Response) {
} }
for _, name := range names { for _, name := range names {
fmt.Fprintf(buf, "<h2>%s %s</h2>", strings.Title(item.Kind), name) buf.WriteString("<p>")
fmt.Fprintf(buf, "<p class=\"download-links\">\n") fmt.Fprintf(buf, "<strong>%s %s:</strong>", section, name)
buf.WriteString(" <span class=\"download-links\">")
for _, asset := range item.Assets { for _, asset := range item.Assets {
fmt.Fprintf(buf, " <a href=\"/public/download-set/%s/%s/%s?set=%s\" download>%s</a>\n", item.Kind, name, asset, setStr, asset) for _, v := range hostAssetVariants(asset) {
url := fmt.Sprintf("/public/download-set/%s/%s/%s", item.Kind, name, v.url)
if strings.Contains(url, "?") {
url += "&"
} else {
url += "?"
} }
fmt.Fprintf(buf, `</p>`) url += "set=" + setStr
fmt.Fprintf(buf, " <a href=\"%s\" download>%s</a>\n", url, v.name)
}
}
buf.WriteString("</span></p>\n")
} }
} }
+7 -3
View File
@@ -188,11 +188,15 @@ func wsDownloadPage(req *restful.Request, resp *restful.Response) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
buf.WriteString(htmlHeader(fmt.Sprintf("Token assets: %s %s", spec.Kind, spec.Name))) buf.WriteString(htmlHeader(fmt.Sprintf("Token assets: %s %s", spec.Kind, spec.Name)))
buf.WriteString("<ul>") buf.WriteString("<table class=\"download-table\">")
for _, asset := range spec.Assets { for _, asset := range spec.Assets {
fmt.Fprintf(buf, "<li><a href=\"%s\" download>%s</a></li>\n", asset, asset) buf.WriteString("<tr>")
for _, v := range hostAssetVariants(asset) {
fmt.Fprintf(buf, "<td><a href=\"%s\" download>%s</a></td>", v.url, v.name)
} }
buf.WriteString("</ul>") buf.WriteString("</tr>\n")
}
buf.WriteString("</table>")
buf.WriteString(htmlFooter) buf.WriteString(htmlFooter)
buf.WriteTo(resp) buf.WriteTo(resp)
+39 -16
View File
@@ -1,7 +1,6 @@
package main package main
import ( import (
"flag"
"io" "io"
"log" "log"
"net/http" "net/http"
@@ -14,10 +13,36 @@ import (
"novit.tech/direktil/local-server/pkg/mime" "novit.tech/direktil/local-server/pkg/mime"
) )
var ( type AssetVariant struct {
allowDetectedHost = flag.Bool("allow-detected-host", false, "Allow access to host assets from its IP (insecure but enables unattended netboot)") name string
trustXFF = flag.Bool("trust-xff", false, "Trust the X-Forwarded-For header") url string
) }
func hostAssetVariants(asset string) []AssetVariant {
switch asset {
case "uki":
return []AssetVariant{
{asset, asset},
{asset + " (serial)", asset + "?serial=0"},
}
case "boot.img",
"boot.img.gz",
"boot.qcow2",
"boot.qed",
"boot.vdi",
"boot.vmdk",
"boot.vpc",
"boot.iso",
"boot.tar":
return []AssetVariant{
{asset, asset},
{asset + " (UKI)", asset + "?uki"},
{asset + " (UKI+serial)", asset + "?uki&serial=0"},
}
default:
return []AssetVariant{{asset, asset}}
}
}
type wsHost struct { type wsHost struct {
hostDoc string hostDoc string
@@ -52,9 +77,6 @@ func (ws wsHost) register(rws *restful.WebService, alterRB func(*restful.RouteBu
b("boot.img.gz"). b("boot.img.gz").
Produces(mime.DISK + "+gzip"). Produces(mime.DISK + "+gzip").
Doc("Get the " + ws.hostDoc + "'s boot disk image, gzip compressed"), Doc("Get the " + ws.hostDoc + "'s boot disk image, gzip compressed"),
b("boot.img.lz4").
Produces(mime.DISK + "+lz4").
Doc("Get the " + ws.hostDoc + "'s boot disk image, lz4 compressed"),
// - other formats // - other formats
b("boot.qcow2"). b("boot.qcow2").
@@ -188,19 +210,12 @@ func renderHost(w http.ResponseWriter, r *http.Request, what string, host *local
} }
switch what { switch what {
case "config":
err = renderConfig(w, r, ctx, false)
case "config.json":
err = renderConfig(w, r, ctx, true)
case "ipxe":
err = renderIPXE(w, ctx)
case "kernel": case "kernel":
err = renderKernel(w, r, ctx) err = renderKernel(w, r, ctx)
case "initrd": case "initrd":
err = renderCtx(w, r, ctx, what, buildInitrd) err = renderCtx(w, r, ctx, what, buildInitrd)
case "uki": case "uki":
uki = true
err = renderCtx(w, r, ctx, what, withUki(buildUki)) err = renderCtx(w, r, ctx, what, withUki(buildUki))
case "bootstrap.tar": case "bootstrap.tar":
@@ -230,6 +245,14 @@ func renderHost(w http.ResponseWriter, r *http.Request, what string, host *local
case "bootstrap-config": case "bootstrap-config":
err = renderBootstrapConfig(w, ctx) err = renderBootstrapConfig(w, ctx)
case "config":
err = renderConfig(w, r, ctx, false)
case "config.json":
err = renderConfig(w, r, ctx, true)
case "ipxe":
err = renderIPXE(w, ctx)
default: default:
http.NotFound(w, r) http.NotFound(w, r)
} }
+1 -1
View File
@@ -25,7 +25,7 @@ require (
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
k8s.io/apimachinery v0.33.2 k8s.io/apimachinery v0.33.2
m.cluseau.fr/go v0.0.0-20230809064045-12c5a121c766 m.cluseau.fr/go v0.0.0-20230809064045-12c5a121c766
novit.tech/direktil/pkg v0.0.0-20260221072850-b72bed72bb51 novit.tech/direktil/pkg v0.0.0-20260508111456-5a75785cfbbf
) )
replace github.com/zmap/zlint/v3 => github.com/zmap/zlint/v3 v3.3.1 replace github.com/zmap/zlint/v3 => github.com/zmap/zlint/v3 v3.3.1
+2
View File
@@ -350,3 +350,5 @@ novit.tech/direktil/pkg v0.0.0-20260210141740-4d5661fa8ecd h1:proGf8Cid9tzJzoRbq
novit.tech/direktil/pkg v0.0.0-20260210141740-4d5661fa8ecd/go.mod h1:zjezU6tELE880oYHs/WAauGBupKIEQQ7KqWTB69RW10= novit.tech/direktil/pkg v0.0.0-20260210141740-4d5661fa8ecd/go.mod h1:zjezU6tELE880oYHs/WAauGBupKIEQQ7KqWTB69RW10=
novit.tech/direktil/pkg v0.0.0-20260221072850-b72bed72bb51 h1:NBcpvWcTBMzFos0pkuLsbVCQ+mHf8KqNOdVywMX6FFk= novit.tech/direktil/pkg v0.0.0-20260221072850-b72bed72bb51 h1:NBcpvWcTBMzFos0pkuLsbVCQ+mHf8KqNOdVywMX6FFk=
novit.tech/direktil/pkg v0.0.0-20260221072850-b72bed72bb51/go.mod h1:zjezU6tELE880oYHs/WAauGBupKIEQQ7KqWTB69RW10= novit.tech/direktil/pkg v0.0.0-20260221072850-b72bed72bb51/go.mod h1:zjezU6tELE880oYHs/WAauGBupKIEQQ7KqWTB69RW10=
novit.tech/direktil/pkg v0.0.0-20260508111456-5a75785cfbbf h1:Qt4aaB1R/BjQGEAhhNoIhzQwzaycxPgxHiA9M7aEujk=
novit.tech/direktil/pkg v0.0.0-20260508111456-5a75785cfbbf/go.mod h1:zjezU6tELE880oYHs/WAauGBupKIEQQ7KqWTB69RW10=
@@ -10,17 +10,17 @@ const Downloads = {
host: [ host: [
"kernel", "kernel",
"initrd", "initrd",
"uki",
"bootstrap.tar", "bootstrap.tar",
"boot.img.lz4",
"boot.img.gz", "boot.img.gz",
"boot.img",
"boot.qcow2", "boot.qcow2",
"boot.vmdk", "boot.vmdk",
"boot.img",
"boot.iso", "boot.iso",
"boot.tar", "boot.tar",
"boot-efi.tar",
"config",
"bootstrap-config", "bootstrap-config",
"config",
"config.json",
"ipxe", "ipxe",
], ],
}[this.kind] }[this.kind]
+7 -2
View File
@@ -10,18 +10,23 @@
cursor: pointer; cursor: pointer;
} }
.downloads, .download-links { .downloads, .download-links, .download-table td {
& > * { & > * {
display: inline-block; display: inline-block;
margin-right: 1ex; margin-right: 1ex;
margin-bottom: 1ex; margin-bottom: 1ex;
padding: 0.5ex; padding: 0.5ex;
border: 1px solid; border: 1px solid !important;
border-radius: 1ex; border-radius: 1ex;
cursor: pointer; cursor: pointer;
} }
} }
.download-table th,
.download-table td {
border: none !important;
}
.downloads, .view-links { .downloads, .view-links {
& > .selected { & > .selected {
color: var(--link); color: var(--link);
+1 -1
View File
@@ -11,7 +11,7 @@
<style>@import url('/ui/style.css');@import url('/ui/app.css');</style> <style>@import url('/ui/style.css');@import url('/ui/app.css');</style>
<script src="/ui/jsonpatch.min-942279a1c4209cc2.js" integrity="sha384-GcPrkRS12jtrElEkbJcrZ8asvvb9s3mc+CUq9UW/8bL4L0bpkmh9M+oFnWN6qLq2"></script> <script src="/ui/jsonpatch.min-942279a1c4209cc2.js" integrity="sha384-GcPrkRS12jtrElEkbJcrZ8asvvb9s3mc+CUq9UW/8bL4L0bpkmh9M+oFnWN6qLq2"></script>
<script src="/ui/app-e7fb26679b9aa0f2.js" defer integrity="sha384-4oRQalb7IIBcqQzfDkeCj53qYOP6dLsTwqcjnm3EiBa92oNDD3chUw38W2gEC+3p" type="module"></script> <script src="/ui/app-e7fb26679b9aa0f2.js" defer integrity="sha384-4oRQalb7IIBcqQzfDkeCj53qYOP6dLsTwqcjnm3EiBa92oNDD3chUw38W2gEC+3p" type="module"></script>
<script src="/ui/Downloads-c9374f19f52c46d.js" integrity="sha384-WQIkCSxlUkvu4jFIWwS3bESMaazGwwnBn9dyvB7nItz3L8BmusRMsJACzfJa7sC4"></script> <script src="/ui/Downloads-29497c61f1fe9bf0.js" integrity="sha384-xwn49JflUBaZlQCHCn55Q9qSGqsw01Job+TXk53HLhvNhKAALN18+gNCdF0bUJW0"></script>
<script src="/ui/GetCopy-7e3c9678f9647d40.js" integrity="sha384-LzxUXylxE/t25HyTch8y2qvKcehvP2nqCo37swIBGEKZZUzHVJVQrS5UJDWNskTA"></script> <script src="/ui/GetCopy-7e3c9678f9647d40.js" integrity="sha384-LzxUXylxE/t25HyTch8y2qvKcehvP2nqCo37swIBGEKZZUzHVJVQrS5UJDWNskTA"></script>
<script src="/ui/Cluster-703dcdca97841304.js" integrity="sha384-ifGpq/GB1nDfqczm5clTRtcfnc9UMkT+SptMyS3UG9Di3xoKORtOGGjmK5RnJx+v"></script> <script src="/ui/Cluster-703dcdca97841304.js" integrity="sha384-ifGpq/GB1nDfqczm5clTRtcfnc9UMkT+SptMyS3UG9Di3xoKORtOGGjmK5RnJx+v"></script>
<script src="/ui/Host-61916516a854adff.js" integrity="sha384-/wh3KrC0sb4MT7ekO2U84rswxI42WSH/0jB4dbDaaGaGhX60xTEZHFsdQAf7UgTG"></script> <script src="/ui/Host-61916516a854adff.js" integrity="sha384-/wh3KrC0sb4MT7ekO2U84rswxI42WSH/0jB4dbDaaGaGhX60xTEZHFsdQAf7UgTG"></script>
+3 -1
View File
@@ -48,8 +48,10 @@ th, td {
border-bottom: dotted 1pt; border-bottom: dotted 1pt;
padding: 2pt 4pt; padding: 2pt 4pt;
} }
tr:first-child > th { tr:first-child {
th, td {
border-top: dotted 1pt; border-top: dotted 1pt;
}
} }
th, tr:last-child > td { th, tr:last-child > td {
border-bottom: solid 1pt; border-bottom: solid 1pt;
+7 -2
View File
@@ -10,18 +10,23 @@
cursor: pointer; cursor: pointer;
} }
.downloads, .download-links { .downloads, .download-links, .download-table td {
& > * { & > * {
display: inline-block; display: inline-block;
margin-right: 1ex; margin-right: 1ex;
margin-bottom: 1ex; margin-bottom: 1ex;
padding: 0.5ex; padding: 0.5ex;
border: 1px solid; border: 1px solid !important;
border-radius: 1ex; border-radius: 1ex;
cursor: pointer; cursor: pointer;
} }
} }
.download-table th,
.download-table td {
border: none !important;
}
.downloads, .view-links { .downloads, .view-links {
& > .selected { & > .selected {
color: var(--link); color: var(--link);
+4 -4
View File
@@ -10,17 +10,17 @@ const Downloads = {
host: [ host: [
"kernel", "kernel",
"initrd", "initrd",
"uki",
"bootstrap.tar", "bootstrap.tar",
"boot.img.lz4",
"boot.img.gz", "boot.img.gz",
"boot.img",
"boot.qcow2", "boot.qcow2",
"boot.vmdk", "boot.vmdk",
"boot.img",
"boot.iso", "boot.iso",
"boot.tar", "boot.tar",
"boot-efi.tar",
"config",
"bootstrap-config", "bootstrap-config",
"config",
"config.json",
"ipxe", "ipxe",
], ],
}[this.kind] }[this.kind]
+3 -1
View File
@@ -48,8 +48,10 @@ th, td {
border-bottom: dotted 1pt; border-bottom: dotted 1pt;
padding: 2pt 4pt; padding: 2pt 4pt;
} }
tr:first-child > th { tr:first-child {
th, td {
border-top: dotted 1pt; border-top: dotted 1pt;
}
} }
th, tr:last-child > td { th, tr:last-child > td {
border-bottom: solid 1pt; border-bottom: solid 1pt;