diff --git a/WORKSPACE b/WORKSPACE index 5bf90e8c..7b6561d1 100644 --- a/WORKSPACE +++ b/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", +) diff --git a/bzl/workspace-status.sh b/bzl/workspace-status.sh index dcc90c5d..54500172 100755 --- a/bzl/workspace-status.sh +++ b/bzl/workspace-status.sh @@ -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) diff --git a/go/mirko/BUILD.bazel b/go/mirko/BUILD.bazel index 59ed755f..89b40ed3 100644 --- a/go/mirko/BUILD.bazel +++ b/go/mirko/BUILD.bazel @@ -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", diff --git a/go/mirko/kubernetes.go b/go/mirko/kubernetes.go new file mode 100644 index 00000000..c3a0bb3e --- /dev/null +++ b/go/mirko/kubernetes.go @@ -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 +} diff --git a/go/mirko/mirko.go b/go/mirko/mirko.go index 01766db1..78dbda1a 100644 --- a/go/mirko/mirko.go +++ b/go/mirko/mirko.go @@ -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 +} diff --git a/go/statusz/BUILD.bazel b/go/statusz/BUILD.bazel index f051d720..4434b95a 100644 --- a/go/statusz/BUILD.bazel +++ b/go/statusz/BUILD.bazel @@ -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}", + }, ) diff --git a/go/statusz/statusz.go b/go/statusz/statusz.go index 2aa76e40..20d51510 100644 --- a/go/statusz/statusz.go +++ b/go/statusz/statusz.go @@ -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 = ` Status for {{.BinaryName}}