Compare commits
3 Commits
burble.dn4
...
enhancemen
Author | SHA1 | Date | |
---|---|---|---|
600bafe08d | |||
66e63c66a1 | |||
166234fa89 |
5
.gitignore
vendored
5
.gitignore
vendored
@ -16,4 +16,7 @@
|
||||
|
||||
.DS_Store
|
||||
frontend/frontend
|
||||
proxy/proxy
|
||||
proxy/proxy
|
||||
|
||||
# don't include generated bindata file
|
||||
frontend/bindata.go
|
||||
|
@ -31,8 +31,9 @@ script:
|
||||
- |
|
||||
# Build image
|
||||
docker build \
|
||||
--build-arg IMAGE_ARCH=$IMAGE_ARCH \
|
||||
-t $DOCKER_USERNAME/$IMAGE_NAME:$IMAGE_ARCH \
|
||||
-f $PROGRAM/Dockerfile.$IMAGE_ARCH \
|
||||
-f $PROGRAM/Dockerfile \
|
||||
$PROGRAM
|
||||
|
||||
# Tag image :{arch} and :{arch}-build{build number}
|
||||
|
19
frontend/Dockerfile
Normal file
19
frontend/Dockerfile
Normal file
@ -0,0 +1,19 @@
|
||||
FROM golang:buster AS step_0
|
||||
#
|
||||
# IMAGE_ARCH is the binary format of the final output
|
||||
# BUILD_ARCH is the binaary format of the build host
|
||||
#
|
||||
ARG IMAGE_ARCH=amd64
|
||||
ARG BUILD_ARCH=amd64
|
||||
#
|
||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=$IMAGE_ARCH GO111MODULE=on
|
||||
WORKDIR /root
|
||||
COPY . .
|
||||
# go-bindata is run on the build host as part of the go generate step
|
||||
RUN GOARCH=$BUILD_ARCH go get -u github.com/kevinburke/go-bindata/...
|
||||
RUN go generate
|
||||
RUN go build -ldflags "-w -s" -o /frontend
|
||||
|
||||
FROM scratch AS step_1
|
||||
COPY --from=step_0 /frontend /
|
||||
ENTRYPOINT ["/frontend"]
|
@ -1,9 +0,0 @@
|
||||
FROM golang:buster AS step_0
|
||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on
|
||||
WORKDIR /root
|
||||
COPY . .
|
||||
RUN go build -o /frontend
|
||||
|
||||
FROM scratch AS step_1
|
||||
COPY --from=step_0 /frontend /
|
||||
ENTRYPOINT ["/frontend"]
|
@ -1,9 +0,0 @@
|
||||
FROM golang:buster AS step_0
|
||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=arm GO111MODULE=on
|
||||
WORKDIR /root
|
||||
COPY . .
|
||||
RUN go build -o /frontend
|
||||
|
||||
FROM scratch AS step_1
|
||||
COPY --from=step_0 /frontend /
|
||||
ENTRYPOINT ["/frontend"]
|
@ -1,9 +0,0 @@
|
||||
FROM golang:buster AS step_0
|
||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GO111MODULE=on
|
||||
WORKDIR /root
|
||||
COPY . .
|
||||
RUN go build -o /frontend
|
||||
|
||||
FROM scratch AS step_1
|
||||
COPY --from=step_0 /frontend /
|
||||
ENTRYPOINT ["/frontend"]
|
@ -1,9 +0,0 @@
|
||||
FROM golang:buster AS step_0
|
||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=386 GO111MODULE=on
|
||||
WORKDIR /root
|
||||
COPY . .
|
||||
RUN go build -o /frontend
|
||||
|
||||
FROM scratch AS step_1
|
||||
COPY --from=step_0 /frontend /
|
||||
ENTRYPOINT ["/frontend"]
|
@ -1,9 +0,0 @@
|
||||
FROM golang:buster AS step_0
|
||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=ppc64le GO111MODULE=on
|
||||
WORKDIR /root
|
||||
COPY . .
|
||||
RUN go build -o /frontend
|
||||
|
||||
FROM scratch AS step_1
|
||||
COPY --from=step_0 /frontend /
|
||||
ENTRYPOINT ["/frontend"]
|
@ -1,9 +0,0 @@
|
||||
FROM golang:buster AS step_0
|
||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=s390x GO111MODULE=on
|
||||
WORKDIR /root
|
||||
COPY . .
|
||||
RUN go build -o /frontend
|
||||
|
||||
FROM scratch AS step_1
|
||||
COPY --from=step_0 /frontend /
|
||||
ENTRYPOINT ["/frontend"]
|
2
frontend/bindata/robots.txt
Normal file
2
frontend/bindata/robots.txt
Normal file
@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow: /
|
14
frontend/bindata/templates/bgpmap.tpl
Normal file
14
frontend/bindata/templates/bgpmap.tpl
Normal file
@ -0,0 +1,14 @@
|
||||
<h2>BGPmap: {{ html .Target }}</h2>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/viz.js@2.1.2/viz.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/viz.js@2.1.2/lite.render.js" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
var viz = new Viz();
|
||||
viz.renderSVGElement(`{{ .Result }}`)
|
||||
.then(element => {
|
||||
document.body.appendChild(element);
|
||||
})
|
||||
.catch(error => {
|
||||
document.body.innerHTML = "<pre>"+error+"</pre>"
|
||||
});
|
||||
</script>
|
2
frontend/bindata/templates/bird.tpl
Normal file
2
frontend/bindata/templates/bird.tpl
Normal file
@ -0,0 +1,2 @@
|
||||
<h2>{{ html .ServerName }}: {{ html .Target }}</h2>
|
||||
{{ .Result }}
|
68
frontend/bindata/templates/page.tpl
Normal file
68
frontend/bindata/templates/page.tpl
Normal file
@ -0,0 +1,68 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<meta name="renderer" content="webkit">
|
||||
<title>{{ .Title }}</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.1/dist/css/bootstrap.min.css" integrity="sha256-VoFZSlmyTXsegReQCNmbXrS4hBBUl/cexZvPmPWoJsY=" crossorigin="anonymous">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<a class="navbar-brand" href="/">{{ .Brand }}</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
{{ $option := .URLOption }}
|
||||
{{ $server := .URLServer }}
|
||||
{{ $target := .URLCommand }}
|
||||
{{ if .IsWhois }}
|
||||
{{ $option = "summary" }}
|
||||
{{ $server = .AllServersURL }}
|
||||
{{ $target = "" }}
|
||||
{{ end }}
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link{{ if .AllServersLinkActive }} active{{ end }}"
|
||||
href="/{{ $option }}/{{ .AllServersURL }}/{{ $target }}"> All Servers </a>
|
||||
</li>
|
||||
{{ range $k, $v := .Servers }}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link{{ if eq $server $v }} active{{ end }}"
|
||||
href="/{{ $option }}/{{ $v }}/{{ $target }}">{{ $v }}</a>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ if .IsWhois }}
|
||||
{{ $target = .WhoisTarget }}
|
||||
{{ end }}
|
||||
<form class="form-inline" action="/redir" method="GET">
|
||||
<div class="input-group">
|
||||
<select name="action" class="form-control">
|
||||
{{ range $k, $v := .Options }}
|
||||
<option value="{{ $k }}"{{ if eq $k $.URLOption }} selected{{end}}>{{ $v }}</option>
|
||||
{{ end }}
|
||||
</select>
|
||||
<input name="server" class="d-none" value="{{ $server }}">
|
||||
<input name="target" class="form-control" placeholder="Target" aria-label="Target" value="{{ $target }}">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-outline-success" type="submit">»</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.1/dist/js/bootstrap.min.js" integrity="sha256-0IiaoZCI++9oAAvmCb5Y0r93XkuhvJpRalZLffQXLok=" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
26
frontend/bindata/templates/summary.tpl
Normal file
26
frontend/bindata/templates/summary.tpl
Normal file
@ -0,0 +1,26 @@
|
||||
{{ $ServerName := urlquery .ServerName }}
|
||||
|
||||
<table class="table table-striped table-bordered table-sm">
|
||||
<thead>
|
||||
{{ range .Header }}
|
||||
<th scope="col">{{ html . }}</th>
|
||||
{{ end }}
|
||||
</thead>
|
||||
<tbody>
|
||||
{{ range .Rows }}
|
||||
<tr class="table-{{ .MappedState }}">
|
||||
<td><a href="/detail/{{ $ServerName }}/{{ urlquery .Name }}">{{ html .Name }}</a></td>
|
||||
<td>{{ .Proto }}</td>
|
||||
<td>{{ .Table }}</td>
|
||||
<td>{{ .State }}</td>
|
||||
<td>{{ .Since }}</td>
|
||||
<td>{{ .Info }}</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!--
|
||||
{{ .Raw }}
|
||||
-->
|
||||
|
2
frontend/bindata/templates/whois.tpl
Normal file
2
frontend/bindata/templates/whois.tpl
Normal file
@ -0,0 +1,2 @@
|
||||
<h2>whois {{ html .Target }}</h2>
|
||||
{{ .Result }}
|
@ -2,4 +2,7 @@ module github.com/xddxdd/bird-lg-go/frontend
|
||||
|
||||
go 1.15
|
||||
|
||||
require github.com/gorilla/handlers v1.5.1
|
||||
require (
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1
|
||||
github.com/gorilla/handlers v1.5.1
|
||||
)
|
||||
|
@ -1,3 +1,5 @@
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
||||
|
@ -7,6 +7,9 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// binary data
|
||||
//go:generate go-bindata -prefix bindata -o bindata.go bindata/...
|
||||
|
||||
type settingType struct {
|
||||
servers []string
|
||||
domain string
|
||||
@ -93,5 +96,6 @@ func main() {
|
||||
*navBarBrandPtr,
|
||||
}
|
||||
|
||||
ImportTemplates()
|
||||
webServerStart()
|
||||
}
|
||||
|
@ -1,13 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func renderTemplate(w http.ResponseWriter, r *http.Request, title string, content string) {
|
||||
// static options map
|
||||
var optionsMap = map[string]string{
|
||||
"summary": "show protocols",
|
||||
"detail": "show protocols all",
|
||||
"route": "show route for ...",
|
||||
"route_all": "show route for ... all",
|
||||
"route_bgpmap": "show route for ... (bgpmap)",
|
||||
"route_where": "show route where net ~ [ ... ]",
|
||||
"route_where_all": "show route where net ~ [ ... ] all",
|
||||
"route_where_bgpmap": "show route where net ~ [ ... ] (bgpmap)",
|
||||
"route_generic": "show route ...",
|
||||
"generic": "show ...",
|
||||
"whois": "whois ...",
|
||||
"traceroute": "traceroute ...",
|
||||
}
|
||||
|
||||
// pre-compiled regexp and constant statemap for summary rendering
|
||||
var splitSummaryLine = regexp.MustCompile(`(\w+)(\s+)(\w+)(\s+)([\w-]+)(\s+)(\w+)(\s+)([0-9\-\. :]+)(.*)`)
|
||||
var summaryStateMap = map[string]string{
|
||||
"up": "success",
|
||||
"down": "secondary",
|
||||
"start": "danger",
|
||||
"passive": "info",
|
||||
}
|
||||
|
||||
// render the page template
|
||||
func renderPageTemplate(w http.ResponseWriter, r *http.Request, title string, content string) {
|
||||
path := r.URL.Path[1:]
|
||||
split := strings.SplitN(path, "/", 3)
|
||||
|
||||
@ -24,36 +52,28 @@ func renderTemplate(w http.ResponseWriter, r *http.Request, title string, conten
|
||||
|
||||
split = strings.SplitN(path, "/", 3)
|
||||
|
||||
var args tmplArguments
|
||||
args.Options = map[string]string{
|
||||
"summary": "show protocols",
|
||||
"detail": "show protocols all",
|
||||
"route": "show route for ...",
|
||||
"route_all": "show route for ... all",
|
||||
"route_bgpmap": "show route for ... (bgpmap)",
|
||||
"route_where": "show route where net ~ [ ... ]",
|
||||
"route_where_all": "show route where net ~ [ ... ] all",
|
||||
"route_where_bgpmap": "show route where net ~ [ ... ] (bgpmap)",
|
||||
"route_generic": "show route ...",
|
||||
"generic": "show ...",
|
||||
"whois": "whois ...",
|
||||
"traceroute": "traceroute ...",
|
||||
args := TemplatePage{
|
||||
Options: optionsMap,
|
||||
Servers: setting.servers,
|
||||
AllServersLinkActive: strings.ToLower(split[1]) == strings.ToLower(strings.Join(setting.servers, "+")),
|
||||
AllServersURL: strings.Join(setting.servers, "+"),
|
||||
IsWhois: isWhois,
|
||||
WhoisTarget: whoisTarget,
|
||||
|
||||
URLOption: strings.ToLower(split[0]),
|
||||
URLServer: strings.ToLower(split[1]),
|
||||
URLCommand: split[2],
|
||||
Title: setting.titleBrand + title,
|
||||
Brand: setting.navBarBrand,
|
||||
Content: content,
|
||||
}
|
||||
args.Servers = setting.servers
|
||||
args.AllServersLinkActive = strings.ToLower(split[1]) == strings.ToLower(strings.Join(setting.servers, "+"))
|
||||
args.AllServersURL = strings.Join(setting.servers, "+")
|
||||
args.IsWhois = isWhois
|
||||
args.WhoisTarget = whoisTarget
|
||||
|
||||
args.URLOption = strings.ToLower(split[0])
|
||||
args.URLServer = strings.ToLower(split[1])
|
||||
args.URLCommand = split[2]
|
||||
tmpl := TemplateLibrary["page"]
|
||||
err := tmpl.Execute(w, args)
|
||||
if err != nil {
|
||||
fmt.Println("Error rendering page:", err.Error())
|
||||
}
|
||||
|
||||
args.Title = setting.titleBrand + title
|
||||
args.Brand = setting.navBarBrand
|
||||
args.Content = content
|
||||
|
||||
tmpl.Execute(w, args)
|
||||
}
|
||||
|
||||
// Write the given text to http response, and add whois links for
|
||||
@ -77,87 +97,81 @@ func smartFormatter(s string) string {
|
||||
return result
|
||||
}
|
||||
|
||||
type summaryTableArguments struct {
|
||||
Headers []string
|
||||
Lines [][]string
|
||||
}
|
||||
|
||||
// Output a table for the summary page
|
||||
func summaryTable(data string, serverName string) string {
|
||||
var result string
|
||||
|
||||
// Sort the table, excluding title row
|
||||
stringsSplitted := strings.Split(strings.TrimSpace(data), "\n")
|
||||
if len(stringsSplitted) <= 1 {
|
||||
lines := strings.Split(strings.TrimSpace(data), "\n")
|
||||
if len(lines) <= 1 {
|
||||
// Likely backend returned an error message
|
||||
result = "<pre>" + strings.TrimSpace(data) + "</pre>"
|
||||
} else {
|
||||
// Draw the table head
|
||||
result += `<table class="table table-striped table-bordered table-sm">`
|
||||
result += `<thead>`
|
||||
for _, col := range strings.Split(stringsSplitted[0], " ") {
|
||||
colTrimmed := strings.TrimSpace(col)
|
||||
if len(colTrimmed) == 0 {
|
||||
continue
|
||||
}
|
||||
result += `<th scope="col">` + colTrimmed + `</th>`
|
||||
}
|
||||
result += `</thead><tbody>`
|
||||
|
||||
stringsWithoutTitle := stringsSplitted[1:]
|
||||
sort.Strings(stringsWithoutTitle)
|
||||
|
||||
for _, line := range stringsWithoutTitle {
|
||||
// Ignore empty lines
|
||||
line = strings.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse a total of 6 columns from bird summary
|
||||
lineSplitted := regexp.MustCompile(`(\w+)(\s+)(\w+)(\s+)([\w-]+)(\s+)(\w+)(\s+)([0-9\-\. :]+)(.*)`).FindStringSubmatch(line)
|
||||
if lineSplitted == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var row [6]string
|
||||
if len(lineSplitted) >= 2 {
|
||||
row[0] = strings.TrimSpace(lineSplitted[1])
|
||||
}
|
||||
if len(lineSplitted) >= 4 {
|
||||
row[1] = strings.TrimSpace(lineSplitted[3])
|
||||
}
|
||||
if len(lineSplitted) >= 6 {
|
||||
row[2] = strings.TrimSpace(lineSplitted[5])
|
||||
}
|
||||
if len(lineSplitted) >= 8 {
|
||||
row[3] = strings.TrimSpace(lineSplitted[7])
|
||||
}
|
||||
if len(lineSplitted) >= 10 {
|
||||
row[4] = strings.TrimSpace(lineSplitted[9])
|
||||
}
|
||||
if len(lineSplitted) >= 11 {
|
||||
row[5] = strings.TrimSpace(lineSplitted[10])
|
||||
}
|
||||
|
||||
// Draw the row in red if the link isn't up
|
||||
result += `<tr class="` + (map[string]string{
|
||||
"up": "table-success",
|
||||
"down": "table-secondary",
|
||||
"start": "table-danger",
|
||||
"passive": "table-info",
|
||||
})[row[3]] + `">`
|
||||
// Add link to detail for first column
|
||||
result += `<td><a href="/detail/` + serverName + `/` + row[0] + `">` + row[0] + `</a></td>`
|
||||
// Draw the other cells
|
||||
for i := 1; i < 6; i++ {
|
||||
result += "<td>" + row[i] + "</td>"
|
||||
}
|
||||
result += "</tr>"
|
||||
}
|
||||
result += "</tbody></table>"
|
||||
result += "<!--" + data + "-->"
|
||||
return "<pre>" + strings.TrimSpace(data) + "</pre>"
|
||||
}
|
||||
|
||||
return result
|
||||
args := TemplateSummary{
|
||||
ServerName: serverName,
|
||||
Raw: data,
|
||||
}
|
||||
|
||||
// extract the table header
|
||||
for _, col := range strings.Split(lines[0], " ") {
|
||||
colTrimmed := strings.TrimSpace(col)
|
||||
if len(colTrimmed) == 0 {
|
||||
continue
|
||||
}
|
||||
args.Header = append(args.Header, col)
|
||||
}
|
||||
|
||||
// sort the remaining rows
|
||||
rows := lines[1:]
|
||||
sort.Strings(rows)
|
||||
|
||||
// parse each line
|
||||
for _, line := range rows {
|
||||
|
||||
// Ignore empty lines
|
||||
line = strings.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse a total of 6 columns from bird summary
|
||||
lineSplitted := splitSummaryLine.FindStringSubmatch(line)
|
||||
if lineSplitted == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var row SummaryRowData
|
||||
|
||||
if len(lineSplitted) >= 2 {
|
||||
row.Name = strings.TrimSpace(lineSplitted[1])
|
||||
}
|
||||
if len(lineSplitted) >= 4 {
|
||||
row.Proto = strings.TrimSpace(lineSplitted[3])
|
||||
}
|
||||
if len(lineSplitted) >= 6 {
|
||||
row.Table = strings.TrimSpace(lineSplitted[5])
|
||||
}
|
||||
if len(lineSplitted) >= 8 {
|
||||
row.State = strings.TrimSpace(lineSplitted[7])
|
||||
row.MappedState = summaryStateMap[row.State]
|
||||
}
|
||||
if len(lineSplitted) >= 10 {
|
||||
row.Since = strings.TrimSpace(lineSplitted[9])
|
||||
}
|
||||
if len(lineSplitted) >= 11 {
|
||||
row.Info = strings.TrimSpace(lineSplitted[10])
|
||||
}
|
||||
|
||||
// add to the result
|
||||
args.Rows = append(args.Rows, row)
|
||||
}
|
||||
|
||||
// finally, render the summary template
|
||||
tmpl := TemplateLibrary["summary"]
|
||||
var buffer bytes.Buffer
|
||||
err := tmpl.Execute(&buffer, args)
|
||||
if err != nil {
|
||||
fmt.Println("Error rendering summary:", err.Error())
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type tmplArguments struct {
|
||||
// template argument structures
|
||||
|
||||
// page
|
||||
type TemplatePage struct {
|
||||
// Global options
|
||||
Options map[string]string
|
||||
Servers []string
|
||||
@ -27,73 +31,90 @@ type tmplArguments struct {
|
||||
Content string
|
||||
}
|
||||
|
||||
var tmpl = template.Must(template.New("tmpl").Parse(`
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<meta name="renderer" content="webkit">
|
||||
<title>{{ .Title }}</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.1/dist/css/bootstrap.min.css" integrity="sha256-VoFZSlmyTXsegReQCNmbXrS4hBBUl/cexZvPmPWoJsY=" crossorigin="anonymous">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
</head>
|
||||
<body>
|
||||
// summary
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<a class="navbar-brand" href="/">{{ .Brand }}</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
type SummaryRowData struct {
|
||||
Name string
|
||||
Proto string
|
||||
Table string
|
||||
State string
|
||||
MappedState string
|
||||
Since string
|
||||
Info string
|
||||
}
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
{{ $option := .URLOption }}
|
||||
{{ $server := .URLServer }}
|
||||
{{ $target := .URLCommand }}
|
||||
{{ if .IsWhois }}
|
||||
{{ $option = "summary" }}
|
||||
{{ $server = .AllServersURL }}
|
||||
{{ $target = "" }}
|
||||
{{ end }}
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link{{ if .AllServersLinkActive }} active{{ end }}"
|
||||
href="/{{ $option }}/{{ .AllServersURL }}/{{ $target }}"> All Servers </a>
|
||||
</li>
|
||||
{{ range $k, $v := .Servers }}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link{{ if eq $server $v }} active{{ end }}"
|
||||
href="/{{ $option }}/{{ $v }}/{{ $target }}">{{ $v }}</a>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ if .IsWhois }}
|
||||
{{ $target = .WhoisTarget }}
|
||||
{{ end }}
|
||||
<form class="form-inline" action="/redir" method="GET">
|
||||
<div class="input-group">
|
||||
<select name="action" class="form-control">
|
||||
{{ range $k, $v := .Options }}
|
||||
<option value="{{ $k }}"{{ if eq $k $.URLOption }} selected{{end}}>{{ $v }}</option>
|
||||
{{ end }}
|
||||
</select>
|
||||
<input name="server" class="d-none" value="{{ $server }}">
|
||||
<input name="target" class="form-control" placeholder="Target" aria-label="Target" value="{{ $target }}">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-outline-success" type="submit">»</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
// utility functions to allow filtering of results in the template
|
||||
|
||||
<div class="container">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
func (r SummaryRowData) NameHasPrefix(prefix string) bool {
|
||||
return strings.HasPrefix(r.Name, prefix)
|
||||
}
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.1/dist/js/bootstrap.min.js" integrity="sha256-0IiaoZCI++9oAAvmCb5Y0r93XkuhvJpRalZLffQXLok=" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
`))
|
||||
func (r SummaryRowData) NameContains(prefix string) bool {
|
||||
return strings.Contains(r.Name, prefix)
|
||||
}
|
||||
|
||||
type TemplateSummary struct {
|
||||
ServerName string
|
||||
Raw string
|
||||
Header []string
|
||||
Rows []SummaryRowData
|
||||
}
|
||||
|
||||
// whois
|
||||
type TemplateWhois struct {
|
||||
Target string
|
||||
Result string
|
||||
}
|
||||
|
||||
// bgpmap
|
||||
type TemplateBGPmap struct {
|
||||
Servers []string
|
||||
Target string
|
||||
Result string
|
||||
}
|
||||
|
||||
// bird
|
||||
type TemplateBird struct {
|
||||
ServerName string
|
||||
Target string
|
||||
Result string
|
||||
}
|
||||
|
||||
// global variable to hold the templates
|
||||
|
||||
var TemplateLibrary map[string]*template.Template
|
||||
|
||||
// list of required templates
|
||||
|
||||
var requiredTemplates = [...]string{
|
||||
"page",
|
||||
"summary",
|
||||
"whois",
|
||||
"bgpmap",
|
||||
"bird",
|
||||
}
|
||||
|
||||
// import templates from bindata
|
||||
|
||||
func ImportTemplates() {
|
||||
|
||||
// create a new (blank) initial template
|
||||
TemplateLibrary = make(map[string]*template.Template)
|
||||
|
||||
// for each template that is needed
|
||||
for _, tmpl := range requiredTemplates {
|
||||
|
||||
// extract the template definition from the bindata
|
||||
def := MustAssetString("templates/" + tmpl + ".tpl")
|
||||
|
||||
// and add it to the template library
|
||||
template, err := template.New(tmpl).Parse(def)
|
||||
if err != nil {
|
||||
panic("Unable to parse template (templates/" + tmpl + ": " + err.Error())
|
||||
}
|
||||
|
||||
// store in the library
|
||||
TemplateLibrary[tmpl] = template
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,47 +1,82 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/elazarl/go-bindata-assetfs"
|
||||
"github.com/gorilla/handlers"
|
||||
)
|
||||
|
||||
func webHandlerWhois(w http.ResponseWriter, r *http.Request) {
|
||||
var target string = r.URL.Path[len("/whois/"):]
|
||||
var primitiveMap = map[string]string{
|
||||
"summary": "show protocols",
|
||||
"detail": "show protocols all %s",
|
||||
"route": "show route for %s",
|
||||
"route_all": "show route for %s all",
|
||||
"route_where": "show route where net ~ [ %s ]",
|
||||
"route_where_all": "show route where net ~ [ %s ] all",
|
||||
"route_generic": "show route %s",
|
||||
"generic": "show %s",
|
||||
"traceroute": "%s",
|
||||
}
|
||||
|
||||
renderTemplate(
|
||||
// serve up a generic error
|
||||
func serverError(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte("500 Internal Server Error"))
|
||||
}
|
||||
|
||||
// WHOIS pages
|
||||
func webHandlerWhois(w http.ResponseWriter, r *http.Request) {
|
||||
target, err := url.PathUnescape(r.URL.Path[len("/whois/"):])
|
||||
if err != nil {
|
||||
serverError(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// render the whois template
|
||||
args := TemplateWhois{
|
||||
Target: target,
|
||||
Result: smartFormatter(whois(target)),
|
||||
}
|
||||
|
||||
tmpl := TemplateLibrary["whois"]
|
||||
var buffer bytes.Buffer
|
||||
err = tmpl.Execute(&buffer, args)
|
||||
if err != nil {
|
||||
fmt.Println("Error rendering whois template:", err.Error())
|
||||
}
|
||||
|
||||
renderPageTemplate(
|
||||
w, r,
|
||||
" - whois "+html.EscapeString(target),
|
||||
"<h2>whois "+html.EscapeString(target)+"</h2>"+smartFormatter(whois(target)),
|
||||
buffer.String(),
|
||||
)
|
||||
}
|
||||
|
||||
// serve up results from bird
|
||||
func webBackendCommunicator(endpoint string, command string) func(w http.ResponseWriter, r *http.Request) {
|
||||
backendCommandPrimitive, commandPresent := (map[string]string{
|
||||
"summary": "show protocols",
|
||||
"detail": "show protocols all %s",
|
||||
"route": "show route for %s",
|
||||
"route_all": "show route for %s all",
|
||||
"route_where": "show route where net ~ [ %s ]",
|
||||
"route_where_all": "show route where net ~ [ %s ] all",
|
||||
"route_generic": "show route %s",
|
||||
"generic": "show %s",
|
||||
"traceroute": "%s",
|
||||
})[command]
|
||||
|
||||
backendCommandPrimitive, commandPresent := primitiveMap[command]
|
||||
if !commandPresent {
|
||||
panic("invalid command: " + command)
|
||||
}
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
split := strings.SplitN(r.URL.Path[1:], "/", 4)
|
||||
split := strings.SplitN(r.URL.Path[1:], "/", 3)
|
||||
var urlCommands string
|
||||
if len(split) >= 3 {
|
||||
urlCommands = split[2]
|
||||
tmp, err := url.PathUnescape(split[2])
|
||||
if err != nil {
|
||||
serverError(w, r)
|
||||
return
|
||||
}
|
||||
urlCommands = tmp
|
||||
}
|
||||
|
||||
var backendCommand string
|
||||
@ -52,26 +87,50 @@ func webBackendCommunicator(endpoint string, command string) func(w http.Respons
|
||||
}
|
||||
backendCommand = strings.TrimSpace(backendCommand)
|
||||
|
||||
var servers []string = strings.Split(split[1], "+")
|
||||
escapedServers, err := url.PathUnescape(split[1])
|
||||
if err != nil {
|
||||
serverError(w, r)
|
||||
return
|
||||
}
|
||||
servers := strings.Split(escapedServers, "+")
|
||||
|
||||
var responses []string = batchRequest(servers, endpoint, backendCommand)
|
||||
var result string
|
||||
var content string
|
||||
for i, response := range responses {
|
||||
result += "<h2>" + html.EscapeString(servers[i]) + ": " + html.EscapeString(backendCommand) + "</h2>"
|
||||
|
||||
var result string
|
||||
if (endpoint == "bird") && backendCommand == "show protocols" && len(response) > 4 && strings.ToLower(response[0:4]) == "name" {
|
||||
result += summaryTable(response, servers[i])
|
||||
result = summaryTable(response, servers[i])
|
||||
} else {
|
||||
result += smartFormatter(response)
|
||||
result = smartFormatter(response)
|
||||
}
|
||||
|
||||
// render the bird result template
|
||||
args := TemplateBird{
|
||||
ServerName: servers[i],
|
||||
Target: backendCommand,
|
||||
Result: result,
|
||||
}
|
||||
|
||||
tmpl := TemplateLibrary["bird"]
|
||||
var buffer bytes.Buffer
|
||||
err := tmpl.Execute(&buffer, args)
|
||||
if err != nil {
|
||||
fmt.Println("Error rendering bird template:", err.Error())
|
||||
}
|
||||
|
||||
content += buffer.String()
|
||||
}
|
||||
|
||||
renderTemplate(
|
||||
renderPageTemplate(
|
||||
w, r,
|
||||
" - "+html.EscapeString(endpoint+" "+backendCommand),
|
||||
result,
|
||||
content,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// bgpmap result
|
||||
func webHandlerBGPMap(endpoint string, command string) func(w http.ResponseWriter, r *http.Request) {
|
||||
backendCommandPrimitive, commandPresent := (map[string]string{
|
||||
"route_bgpmap": "show route for %s all",
|
||||
@ -95,51 +154,70 @@ func webHandlerBGPMap(endpoint string, command string) func(w http.ResponseWrite
|
||||
|
||||
var servers []string = strings.Split(split[1], "+")
|
||||
var responses []string = batchRequest(servers, endpoint, backendCommand)
|
||||
renderTemplate(
|
||||
|
||||
// render the bgpmap result template
|
||||
args := TemplateBGPmap{
|
||||
Servers: servers,
|
||||
Target: backendCommand,
|
||||
Result: birdRouteToGraphviz(servers, responses, urlCommands),
|
||||
}
|
||||
|
||||
tmpl := TemplateLibrary["bgpmap"]
|
||||
var buffer bytes.Buffer
|
||||
err := tmpl.Execute(&buffer, args)
|
||||
if err != nil {
|
||||
fmt.Println("Error rendering bgpmap template:", err.Error())
|
||||
}
|
||||
|
||||
renderPageTemplate(
|
||||
w, r,
|
||||
" - "+html.EscapeString(endpoint+" "+backendCommand),
|
||||
`
|
||||
<script src="https://cdn.jsdelivr.net/npm/viz.js@2.1.2/viz.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/viz.js@2.1.2/lite.render.js" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
var viz = new Viz();
|
||||
viz.renderSVGElement(`+"`"+birdRouteToGraphviz(servers, responses, urlCommands)+"`"+`)
|
||||
.then(element => {
|
||||
document.body.appendChild(element);
|
||||
})
|
||||
.catch(error => {
|
||||
document.body.innerHTML = "<pre>"+error+"</pre>"
|
||||
});
|
||||
</script>`,
|
||||
buffer.String(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// redirect from the form input to a path style query
|
||||
func webHandlerNavbarFormRedirect(w http.ResponseWriter, r *http.Request) {
|
||||
query := r.URL.Query()
|
||||
if query.Get("action") == "whois" {
|
||||
http.Redirect(w, r, "/"+query.Get("action")+"/"+query.Get("target"), 302)
|
||||
} else if query.Get("action") == "summary" {
|
||||
http.Redirect(w, r, "/"+query.Get("action")+"/"+query.Get("server")+"/", 302)
|
||||
} else {
|
||||
http.Redirect(w, r, "/"+query.Get("action")+"/"+query.Get("server")+"/"+query.Get("target"), 302)
|
||||
|
||||
action := query.Get("action")
|
||||
|
||||
switch action {
|
||||
case "whois":
|
||||
target := url.PathEscape(query.Get("target"))
|
||||
http.Redirect(w, r, "/"+action+"/"+target, 302)
|
||||
case "summary":
|
||||
server := url.PathEscape(query.Get("server"))
|
||||
http.Redirect(w, r, "/"+action+"/"+server+"/", 302)
|
||||
default:
|
||||
server := url.PathEscape(query.Get("server"))
|
||||
target := url.PathEscape(query.Get("target"))
|
||||
http.Redirect(w, r, "/"+action+"/"+server+"/"+target, 302)
|
||||
}
|
||||
}
|
||||
|
||||
func webHandlerRobotsTxt(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("User-agent: *\nDisallow: /\n"))
|
||||
}
|
||||
|
||||
func webHandler404(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte("404 not found\n"))
|
||||
}
|
||||
|
||||
// set up routing paths and start webserver
|
||||
func webServerStart() {
|
||||
// Start HTTP server
|
||||
|
||||
// redirect main page to all server summary
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/summary/"+strings.Join(setting.servers, "+"), 302)
|
||||
})
|
||||
|
||||
// serve static pages using the AssetFS and bindata
|
||||
fs := http.FileServer(&assetfs.AssetFS{
|
||||
Asset: Asset,
|
||||
AssetDir: AssetDir,
|
||||
AssetInfo: AssetInfo,
|
||||
Prefix: "",
|
||||
})
|
||||
|
||||
http.Handle("/static/", fs)
|
||||
http.Handle("/robots.txt", fs)
|
||||
http.Handle("/favicon.ico", fs)
|
||||
|
||||
// backend routes
|
||||
http.HandleFunc("/summary/", webBackendCommunicator("bird", "summary"))
|
||||
http.HandleFunc("/detail/", webBackendCommunicator("bird", "detail"))
|
||||
http.HandleFunc("/route/", webBackendCommunicator("bird", "route"))
|
||||
@ -154,7 +232,7 @@ func webServerStart() {
|
||||
http.HandleFunc("/whois/", webHandlerWhois)
|
||||
http.HandleFunc("/redir", webHandlerNavbarFormRedirect)
|
||||
http.HandleFunc("/telegram/", webHandlerTelegramBot)
|
||||
http.HandleFunc("/robots.txt", webHandlerRobotsTxt)
|
||||
http.HandleFunc("/favicon.ico", webHandler404)
|
||||
|
||||
// Start HTTP server
|
||||
http.ListenAndServe(setting.listen, handlers.LoggingHandler(os.Stdout, http.DefaultServeMux))
|
||||
}
|
||||
|
@ -1,8 +1,13 @@
|
||||
FROM golang:buster AS step_0
|
||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on
|
||||
#
|
||||
# IMAGE_ARCH is the binary format of the final output
|
||||
#
|
||||
ARG IMAGE_ARCH=amd64
|
||||
#
|
||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=$IMAGE_ARCH GO111MODULE=on
|
||||
WORKDIR /root
|
||||
COPY . .
|
||||
RUN go build -o /proxy
|
||||
RUN go build -ldflags "-w -s" -o /proxy
|
||||
|
||||
FROM amd64/debian AS step_1
|
||||
ENV TARGET_ARCH=x86_64
|
@ -1,23 +0,0 @@
|
||||
FROM golang:buster AS step_0
|
||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=arm GO111MODULE=on
|
||||
WORKDIR /root
|
||||
COPY . .
|
||||
RUN go build -o /proxy
|
||||
|
||||
FROM arm32v7/debian AS step_1
|
||||
ENV TARGET_ARCH=arm
|
||||
WORKDIR /root
|
||||
RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get -qq install -y \
|
||||
build-essential musl-dev musl-tools tar wget git
|
||||
RUN git clone https://github.com/sabotage-linux/kernel-headers.git
|
||||
RUN wget https://sourceforge.net/projects/traceroute/files/traceroute/traceroute-2.1.0/traceroute-2.1.0.tar.gz/download \
|
||||
-O traceroute-2.1.0.tar.gz
|
||||
RUN tar xvf traceroute-2.1.0.tar.gz \
|
||||
&& cd traceroute-2.1.0 \
|
||||
&& make -j4 CC=musl-gcc CFLAGS="-I/root/kernel-headers/${TARGET_ARCH}/include" LDFLAGS="-static"
|
||||
|
||||
FROM scratch AS step_2
|
||||
ENV PATH=/
|
||||
COPY --from=step_0 /proxy /
|
||||
COPY --from=step_1 /root/traceroute-2.1.0/traceroute/traceroute /
|
||||
ENTRYPOINT ["/proxy"]
|
@ -1,23 +0,0 @@
|
||||
FROM golang:buster AS step_0
|
||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GO111MODULE=on
|
||||
WORKDIR /root
|
||||
COPY . .
|
||||
RUN go build -o /proxy
|
||||
|
||||
FROM arm64v8/debian AS step_1
|
||||
ENV TARGET_ARCH=arm64
|
||||
WORKDIR /root
|
||||
RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get -qq install -y \
|
||||
build-essential musl-dev musl-tools tar wget git
|
||||
RUN git clone https://github.com/sabotage-linux/kernel-headers.git
|
||||
RUN wget https://sourceforge.net/projects/traceroute/files/traceroute/traceroute-2.1.0/traceroute-2.1.0.tar.gz/download \
|
||||
-O traceroute-2.1.0.tar.gz
|
||||
RUN tar xvf traceroute-2.1.0.tar.gz \
|
||||
&& cd traceroute-2.1.0 \
|
||||
&& make -j4 CC=musl-gcc CFLAGS="-I/root/kernel-headers/${TARGET_ARCH}/include" LDFLAGS="-static"
|
||||
|
||||
FROM scratch AS step_2
|
||||
ENV PATH=/
|
||||
COPY --from=step_0 /proxy /
|
||||
COPY --from=step_1 /root/traceroute-2.1.0/traceroute/traceroute /
|
||||
ENTRYPOINT ["/proxy"]
|
@ -1,23 +0,0 @@
|
||||
FROM golang:buster AS step_0
|
||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=386 GO111MODULE=on
|
||||
WORKDIR /root
|
||||
COPY . .
|
||||
RUN go build -o /proxy
|
||||
|
||||
FROM i386/debian AS step_1
|
||||
ENV TARGET_ARCH=x86
|
||||
WORKDIR /root
|
||||
RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get -qq install -y \
|
||||
build-essential musl-dev musl-tools tar wget git
|
||||
RUN git clone https://github.com/sabotage-linux/kernel-headers.git
|
||||
RUN wget https://sourceforge.net/projects/traceroute/files/traceroute/traceroute-2.1.0/traceroute-2.1.0.tar.gz/download \
|
||||
-O traceroute-2.1.0.tar.gz
|
||||
RUN tar xvf traceroute-2.1.0.tar.gz \
|
||||
&& cd traceroute-2.1.0 \
|
||||
&& make -j4 CC=musl-gcc CFLAGS="-I/root/kernel-headers/${TARGET_ARCH}/include" LDFLAGS="-static"
|
||||
|
||||
FROM scratch AS step_2
|
||||
ENV PATH=/
|
||||
COPY --from=step_0 /proxy /
|
||||
COPY --from=step_1 /root/traceroute-2.1.0/traceroute/traceroute /
|
||||
ENTRYPOINT ["/proxy"]
|
@ -1,23 +0,0 @@
|
||||
FROM golang:buster AS step_0
|
||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=ppc64le GO111MODULE=on
|
||||
WORKDIR /root
|
||||
COPY . .
|
||||
RUN go build -o /proxy
|
||||
|
||||
FROM ppc64le/debian AS step_1
|
||||
ENV TARGET_ARCH=ppc64le
|
||||
WORKDIR /root
|
||||
RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get -qq install -y \
|
||||
build-essential musl-dev musl-tools tar wget git
|
||||
RUN git clone https://github.com/sabotage-linux/kernel-headers.git
|
||||
RUN wget https://sourceforge.net/projects/traceroute/files/traceroute/traceroute-2.1.0/traceroute-2.1.0.tar.gz/download \
|
||||
-O traceroute-2.1.0.tar.gz
|
||||
RUN tar xvf traceroute-2.1.0.tar.gz \
|
||||
&& cd traceroute-2.1.0 \
|
||||
&& make -j4 CC=musl-gcc CFLAGS="-I/root/kernel-headers/${TARGET_ARCH}/include" LDFLAGS="-static"
|
||||
|
||||
FROM scratch AS step_2
|
||||
ENV PATH=/
|
||||
COPY --from=step_0 /proxy /
|
||||
COPY --from=step_1 /root/traceroute-2.1.0/traceroute/traceroute /
|
||||
ENTRYPOINT ["/proxy"]
|
@ -1,23 +0,0 @@
|
||||
FROM golang:buster AS step_0
|
||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=s390x GO111MODULE=on
|
||||
WORKDIR /root
|
||||
COPY . .
|
||||
RUN go build -o /proxy
|
||||
|
||||
FROM s390x/debian AS step_1
|
||||
ENV TARGET_ARCH=s390
|
||||
WORKDIR /root
|
||||
RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get -qq install -y \
|
||||
build-essential musl-dev musl-tools tar wget git
|
||||
RUN git clone https://github.com/sabotage-linux/kernel-headers.git
|
||||
RUN wget https://sourceforge.net/projects/traceroute/files/traceroute/traceroute-2.1.0/traceroute-2.1.0.tar.gz/download \
|
||||
-O traceroute-2.1.0.tar.gz
|
||||
RUN tar xvf traceroute-2.1.0.tar.gz \
|
||||
&& cd traceroute-2.1.0 \
|
||||
&& make -j4 CC=musl-gcc CFLAGS="-I/root/kernel-headers/${TARGET_ARCH}/include" LDFLAGS="-static"
|
||||
|
||||
FROM scratch AS step_2
|
||||
ENV PATH=/
|
||||
COPY --from=step_0 /proxy /
|
||||
COPY --from=step_1 /root/traceroute-2.1.0/traceroute/traceroute /
|
||||
ENTRYPOINT ["/proxy"]
|
Loading…
x
Reference in New Issue
Block a user