forked from hswaw/hscloud
go/{mirko,statusz}: better status, kubernetes client
Change-Id: I66753a79eaf36529aee508d2b7782aab00de1498master
parent
7fb4acb690
commit
8fe651b569
151
WORKSPACE
151
WORKSPACE
|
@ -1,14 +1,14 @@
|
|||
|
||||
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
|
||||
# Skylib
|
||||
|
||||
skylib_version = "0.8.0"
|
||||
|
||||
http_archive(
|
||||
name = "bazel_skylib",
|
||||
type = "tar.gz",
|
||||
url = "https://github.com/bazelbuild/bazel-skylib/releases/download/{}/bazel-skylib.{}.tar.gz".format (skylib_version, skylib_version),
|
||||
url = "https://github.com/bazelbuild/bazel-skylib/releases/download/{}/bazel-skylib.{}.tar.gz".format(skylib_version, skylib_version),
|
||||
sha256 = "2ef429f5d7ce7111263289644d233707dba35e39696377ebab8b0bc701f7818e",
|
||||
)
|
||||
|
||||
|
@ -27,6 +27,7 @@ load(
|
|||
)
|
||||
|
||||
container_repositories()
|
||||
|
||||
# Nix rules
|
||||
http_archive(
|
||||
name = "io_tweag_rules_nixpkgs",
|
||||
|
@ -360,3 +361,149 @@ go_repository(
|
|||
commit = "de5bf2ad457846296e2031421a34e2568e304e35",
|
||||
importpath = "github.com/PuerkitoBio/urlesc",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_abbot_go_http_auth",
|
||||
commit = "860ed7f246ff5abfdbd5c7ce618fd37b49fd3d86",
|
||||
importpath = "github.com/abbot/go-http-auth",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_urfave_cli",
|
||||
commit = "693af58b4d51b8fcc7f9d89576da170765980581",
|
||||
importpath = "github.com/urfave/cli",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "org_golang_x_crypto",
|
||||
commit = "4def268fd1a49955bfb3dda92fe3db4f924f2285",
|
||||
importpath = "golang.org/x/crypto",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "org_golang_x_oauth2",
|
||||
commit = "0f29369cfe4552d0e4bcddc57cc75f4d7e672a33",
|
||||
importpath = "golang.org/x/oauth2",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_djherbis_atime",
|
||||
commit = "2d569978378562c466df74eda2d82900f435c5f4",
|
||||
importpath = "github.com/djherbis/atime",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_google_cloud_go",
|
||||
commit = "71971b35976fc2f904ed2772536790a5458d9996",
|
||||
importpath = "cloud.google.com/go",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "org_golang_x_net",
|
||||
commit = "da137c7871d730100384dbcf36e6f8fa493aef5b",
|
||||
importpath = "golang.org/x/net",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_stackexchange_wmi",
|
||||
commit = "cbe66965904dbe8a6cd589e2298e5d8b986bd7dd",
|
||||
importpath = "github.com/stackexchange/wmi",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_go_ole_go_ole",
|
||||
commit = "938323a72016e9cf84fa5fba7635089efb0ad87f",
|
||||
importpath = "github.com/go-ole/go-ole",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_dustin_go_humanize",
|
||||
commit = "9f541cc9db5d55bce703bd99987c9d5cb8eea45e",
|
||||
importpath = "github.com/dustin/go-humanize",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "io_k8s_client_go",
|
||||
commit = "0c47f9da00011ea9a8717671127ac21625c7a6c0",
|
||||
importpath = "k8s.io/client-go",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "io_k8s_apimachinery",
|
||||
commit = "bfcf53abc9f82bad3e534fcb1c36599d3c989ebf",
|
||||
importpath = "k8s.io/apimachinery",
|
||||
build_file_proto_mode = "disable",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "io_k8s_klog",
|
||||
commit = "6a023d6d0e0954feabd46dc2d3a6a2c3c991fe1a",
|
||||
importpath = "k8s.io/klog",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "io_k8s_utils",
|
||||
commit = "3dccf664f023863740c508fb4284e49742bedfa4",
|
||||
importpath = "k8s.io/utils",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_googleapis_gnostic",
|
||||
commit = "25d8b0b6698593f520d9d8dc5a88e6b16ca9ecc0",
|
||||
importpath = "github.com/googleapis/gnostic",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "io_k8s_api",
|
||||
commit = "3043179095b6baa0087e8735d796bd6dfa881f8e",
|
||||
importpath = "k8s.io/api",
|
||||
build_file_proto_mode = "disable",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "org_golang_x_time",
|
||||
commit = "9d24e82272b4f38b78bc8cff74fa936d31ccd8ef",
|
||||
importpath = "golang.org/x/time",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_google_gofuzz",
|
||||
commit = "f140a6486e521aad38f5917de355cbf147cc0496",
|
||||
importpath = "github.com/google/gofuzz",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "io_k8s_sigs_yaml",
|
||||
commit = "4cd0c284b15f1735b8cc247df097d262b8903f9f",
|
||||
importpath = "sigs.k8s.io/yaml",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_modern_go_reflect2",
|
||||
commit = "94122c33edd36123c84d5368cfb2b69df93a0ec8",
|
||||
importpath = "github.com/modern-go/reflect2",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_davecgh_go_spew",
|
||||
commit = "d8f796af33cc11cb798c1aaeb27a4ebc5099927d",
|
||||
importpath = "github.com/davecgh/go-spew",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_json_iterator_go",
|
||||
commit = "27518f6661eba504be5a7a9a9f6d9460d892ade3",
|
||||
importpath = "github.com/json-iterator/go",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_modern_go_concurrent",
|
||||
commit = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94",
|
||||
importpath = "github.com/modern-go/concurrent",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "in_gopkg_inf_v0",
|
||||
commit = "d2d2541c53f18d2a059457998ce2876cc8e67cbf",
|
||||
importpath = "gopkg.in/inf.v0",
|
||||
)
|
||||
|
|
|
@ -15,3 +15,6 @@ function rev() {
|
|||
}
|
||||
|
||||
echo STABLE_BUILD_GERRIT-OAUTH-PROVIDER_LABEL $(rev .)
|
||||
echo STABLE_GIT_COMMIT $(git rev-parse HEAD)
|
||||
echo STABLE_GIT_VERSION $(rev .)
|
||||
echo STABLE_BUILDER $(id -un)@$(hostname -f):$(pwd)
|
||||
|
|
|
@ -2,13 +2,18 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
|||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["mirko.go"],
|
||||
srcs = [
|
||||
"kubernetes.go",
|
||||
"mirko.go",
|
||||
],
|
||||
importpath = "code.hackerspace.pl/hscloud/go/mirko",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//go/pki:go_default_library",
|
||||
"//go/statusz:go_default_library",
|
||||
"@com_github_golang_glog//:go_default_library",
|
||||
"@io_k8s_client_go//kubernetes:go_default_library",
|
||||
"@io_k8s_client_go//rest:go_default_library",
|
||||
"@org_golang_google_grpc//:go_default_library",
|
||||
"@org_golang_google_grpc//reflection:go_default_library",
|
||||
"@org_golang_x_net//trace:go_default_library",
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package mirko
|
||||
|
||||
import (
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
func (m *Mirko) kubernetesConnect() {
|
||||
config, err := rest.InClusterConfig()
|
||||
if err != nil {
|
||||
glog.Errorf("mirko.KubernetesClientSet: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
clientset, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
glog.Errorf("kubernetes.NewForConfig: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
m.kubernetesCS = clientset
|
||||
}
|
|
@ -8,14 +8,18 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.hackerspace.pl/hscloud/go/pki"
|
||||
"code.hackerspace.pl/hscloud/go/statusz"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/trace"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/reflection"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
||||
"code.hackerspace.pl/hscloud/go/pki"
|
||||
"code.hackerspace.pl/hscloud/go/statusz"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -38,6 +42,8 @@ type Mirko struct {
|
|||
httpServer *http.Server
|
||||
httpMux *http.ServeMux
|
||||
|
||||
kubernetesCS *kubernetes.Clientset
|
||||
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
@ -113,6 +119,12 @@ func (m *Mirko) Listen() error {
|
|||
http.Redirect(w, r, "/debug/status", http.StatusSeeOther)
|
||||
})
|
||||
|
||||
m.kubernetesConnect()
|
||||
|
||||
debugParts := strings.Split(flagDebugAddress, ":")
|
||||
debugPort := debugParts[len(debugParts)-1]
|
||||
statusz.PublicAddress = fmt.Sprintf("http://%s:%s/", m.Address().String(), debugPort)
|
||||
|
||||
m.httpListen = httpLis
|
||||
m.httpServer = &http.Server{
|
||||
Addr: flagDebugAddress,
|
||||
|
@ -196,3 +208,98 @@ func (m *Mirko) Serve() error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Address returns a linkable address where this service is running, sans port.
|
||||
// If running within kubernetes, this will return the pod IP.
|
||||
// Otherwise, this will guess the main, 'external' IP address of the machine it's running on.
|
||||
// On failures, returns loopback address.
|
||||
func (m *Mirko) Address() net.IP {
|
||||
// If we're not running in Kubernetes and binding to 127.0.0.1, return loopback.
|
||||
if m.kubernetesCS == nil && strings.HasPrefix(flagListenAddress, "127.0.0.1:") {
|
||||
return net.ParseIP("127.0.0.1")
|
||||
}
|
||||
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
glog.Errorf("net.Interface(): %v", err)
|
||||
return net.ParseIP("127.0.0.1")
|
||||
}
|
||||
|
||||
addrmap := make(map[string]net.IP)
|
||||
|
||||
for _, iface := range ifaces {
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
glog.Errorf("iface(%q).Addrs(): %v", iface.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(ip.String(), "fe80:") {
|
||||
continue
|
||||
}
|
||||
addrmap[iface.Name] = ip
|
||||
}
|
||||
}
|
||||
|
||||
if m.kubernetesCS != nil {
|
||||
addr, ok := addrmap["eth0"]
|
||||
if !ok {
|
||||
glog.Errorf("Running on Kubernetes but no eth0! Available interfaces: %v", addrmap)
|
||||
return net.ParseIP("127.0.0.1")
|
||||
}
|
||||
|
||||
return addr
|
||||
}
|
||||
|
||||
if len(addrmap) == 0 {
|
||||
glog.Errorf("No interfaces found!")
|
||||
return net.ParseIP("127.0.0.1")
|
||||
}
|
||||
|
||||
// Heuristics ahoy!
|
||||
prioritized := []*ifaceWithPriority{}
|
||||
for iface, addr := range addrmap {
|
||||
prio := &ifaceWithPriority{
|
||||
iface: iface,
|
||||
addr: addr,
|
||||
}
|
||||
switch {
|
||||
case strings.HasPrefix(iface, "lo"):
|
||||
prio.priority = -10
|
||||
case strings.HasPrefix(iface, "tap"):
|
||||
prio.priority = -5
|
||||
case strings.HasPrefix(iface, "tun"):
|
||||
prio.priority = -5
|
||||
case strings.HasPrefix(iface, "veth"):
|
||||
prio.priority = 5
|
||||
case strings.HasPrefix(iface, "wl"):
|
||||
prio.priority = 5
|
||||
case strings.HasPrefix(iface, "enp"):
|
||||
prio.priority = 10
|
||||
case strings.HasPrefix(iface, "eth"):
|
||||
prio.priority = 10
|
||||
}
|
||||
|
||||
prioritized = append(prioritized, prio)
|
||||
}
|
||||
|
||||
sort.Slice(prioritized, func(i, j int) bool { return prioritized[i].priority > prioritized[j].priority })
|
||||
return prioritized[0].addr
|
||||
}
|
||||
|
||||
type ifaceWithPriority struct {
|
||||
iface string
|
||||
addr net.IP
|
||||
priority int
|
||||
}
|
||||
|
|
|
@ -12,4 +12,10 @@ go_library(
|
|||
"@com_github_golang_glog//:go_default_library",
|
||||
"@com_github_shirou_gopsutil//load:go_default_library",
|
||||
],
|
||||
x_defs = {
|
||||
"code.hackerspace.pl/hscloud/go/statusz.GitCommit": "{STABLE_GIT_COMMIT}",
|
||||
"code.hackerspace.pl/hscloud/go/statusz.GitVersion": "{STABLE_GIT_VERSION}",
|
||||
"code.hackerspace.pl/hscloud/go/statusz.Builder": "{STABLE_BUILDER}",
|
||||
"code.hackerspace.pl/hscloud/go/statusz.BuildTimestamp": "{BUILD_TIMESTAMP}",
|
||||
},
|
||||
)
|
||||
|
|
|
@ -42,6 +42,7 @@ import (
|
|||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -60,6 +61,13 @@ var (
|
|||
tmpl = template.Must(reparse(nil))
|
||||
funcs = make(template.FuncMap)
|
||||
|
||||
GitCommit = "unknown"
|
||||
GitVersion = "unknown"
|
||||
Builder = "unknown"
|
||||
BuildTimestamp = "0"
|
||||
// mirko populates this
|
||||
PublicAddress = "#"
|
||||
|
||||
DefaultMux = true
|
||||
)
|
||||
|
||||
|
@ -75,15 +83,18 @@ var statusHTML = `<!DOCTYPE html>
|
|||
<title>Status for {{.BinaryName}}</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
background: #fff;
|
||||
}
|
||||
h1 {
|
||||
font-family: sans-serif;
|
||||
clear: both;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 120%;
|
||||
padding-top: 0.3em;
|
||||
padding-bottom: 0.3em;
|
||||
background: #eeeeff;
|
||||
margin-top: 1em;
|
||||
}
|
||||
.lefthand {
|
||||
float: left;
|
||||
|
@ -97,16 +108,19 @@ text-align: right;
|
|||
<h1>Status for {{.BinaryName}}</h1>
|
||||
<div>
|
||||
<div class=lefthand>
|
||||
Started at {{.StartTime}}<br>
|
||||
Current time {{.CurrentTime}}<br>
|
||||
Started: {{.StartTime}} -- up {{.Up}}<br>
|
||||
Built on {{.BuildTime}}<br>
|
||||
Built at {{.Builder}}<br>
|
||||
Built from git checkout <a href="https://gerrit.hackerspace.pl/plugins/gitiles/hscloud/+/{{.GitCommit}}">{{.GitVersion}}</a><br>
|
||||
SHA256 {{.BinaryHash}}<br>
|
||||
</div>
|
||||
<div class=righthand>
|
||||
Running as {{.Username}} on {{.Hostname}}<br>
|
||||
Running as {{.Username}} on <a href="{{.PublicAddress}}">{{.Hostname}}</a><br>
|
||||
Load {{.LoadAvg}}<br>
|
||||
View <a href=/debug/status>status</a>,
|
||||
<a href=/debug/requests>requests</a>
|
||||
</div>
|
||||
<div style="clear: both;"> </div>
|
||||
</div>`
|
||||
|
||||
func reparse(sections []section) (*template.Template, error) {
|
||||
|
@ -133,24 +147,39 @@ func StatusHandler(w http.ResponseWriter, r *http.Request) {
|
|||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
buildTime := time.Unix(0, 0)
|
||||
if buildTimeNum, err := strconv.Atoi(BuildTimestamp); err == nil {
|
||||
buildTime = time.Unix(int64(buildTimeNum), 0)
|
||||
}
|
||||
|
||||
data := struct {
|
||||
Sections []section
|
||||
BinaryName string
|
||||
BinaryHash string
|
||||
Hostname string
|
||||
Username string
|
||||
StartTime string
|
||||
CurrentTime string
|
||||
LoadAvg string
|
||||
Sections []section
|
||||
BinaryName string
|
||||
BinaryHash string
|
||||
GitVersion string
|
||||
GitCommit string
|
||||
Builder string
|
||||
BuildTime string
|
||||
Hostname string
|
||||
Username string
|
||||
StartTime string
|
||||
Up string
|
||||
LoadAvg string
|
||||
PublicAddress string
|
||||
}{
|
||||
Sections: sections,
|
||||
BinaryName: binaryName,
|
||||
BinaryHash: binaryHash,
|
||||
Hostname: hostname,
|
||||
Username: username,
|
||||
StartTime: serverStart.Format(time.RFC1123),
|
||||
CurrentTime: time.Now().Format(time.RFC1123),
|
||||
LoadAvg: loadAverage(),
|
||||
Sections: sections,
|
||||
BinaryName: binaryName,
|
||||
BinaryHash: binaryHash,
|
||||
GitVersion: GitVersion,
|
||||
GitCommit: GitCommit,
|
||||
Builder: Builder,
|
||||
BuildTime: fmt.Sprintf("%s (%d)", buildTime.Format(time.RFC1123), buildTime.Unix()),
|
||||
Hostname: hostname,
|
||||
Username: username,
|
||||
StartTime: serverStart.Format(time.RFC1123),
|
||||
Up: time.Since(serverStart).String(),
|
||||
LoadAvg: loadAverage(),
|
||||
PublicAddress: PublicAddress,
|
||||
}
|
||||
|
||||
if err := tmpl.ExecuteTemplate(w, "status", data); err != nil {
|
||||
|
|
Loading…
Reference in New Issue