Merge "k0.hswaw.net: pass metallb through Calico"
diff --git a/ci_presubmit.sh b/ci_presubmit.sh
new file mode 100755
index 0000000..5abaf10
--- /dev/null
+++ b/ci_presubmit.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+# This is a presubmit (on-merge) CI script.
+# Currently no CI runs it - so be a good cyborg, and run it before submitting
+# changes to hscloud.
+
+set -e -o pipefail
+
+if [ -z "$hscloud_root" ]; then
+    echo 2>&1 "Please first source env.sh"
+    exit 1
+fi
+
+cd $hscloud_root
+
+# Test critical tools.
+tools/install.sh
+kubectl version --client=true
+kubecfg version
+prodaccess --help 2>/dev/null
+bazel run //cluster/clustercfg smoketest
diff --git a/cluster/clustercfg/clustercfg.py b/cluster/clustercfg/clustercfg.py
index c3e7bd6..1396826 100644
--- a/cluster/clustercfg/clustercfg.py
+++ b/cluster/clustercfg/clustercfg.py
@@ -225,6 +225,9 @@
         return nodestrap(sys.argv[2:], nocerts=True)
     elif mode == "admincreds":
         return admincreds(sys.argv[2:])
+    elif mode == "smoketest":
+        sys.stdout.write("Smoke test passed.")
+        return 0
     else:
         usage()
         return 1
diff --git a/devtools/hackdoc/README.md b/devtools/hackdoc/README.md
index 3226cf2..7bf556a 100644
--- a/devtools/hackdoc/README.md
+++ b/devtools/hackdoc/README.md
@@ -13,6 +13,16 @@
 - https://hackdoc.hackerspace.pl/?ref=master for the `master` branch
 - https://hackdoc.hackerspace.pl/?ref=change/249 for the the source code at change '249'
 
+Special Markdown
+----------------
+
+We should be accepting a Somewhat Standard Subset Of Markdown. For reference, we're using the [blackfriday](https://godoc.org/gopkg.in/russross/blackfriday.v2) library with [CommonExtensions](https://godoc.org/gopkg.in/russross/blackfriday.v2#CommonExtensions) enabled.
+
+In addition, we also support Table of Contents autorendering, just place the following anywhere in your document to render a TOC:
+
+    [TOC]
+
+
 Local Rendering
 ---------------
 
@@ -20,4 +30,4 @@
 
      bazel run //devtools/hackdoc  -- -hspki_disable -docroot /path/to/hscloud
 
-The output log should tell you where hackdoc just started listening at. Currently this is `127.0.0.1:8080` by default. You can change this by passing a `-listen` flag, eg. `-listen 127.0.0.1:4242`.
+The output log should tell you where hackdoc just started listening at. Currently this is `127.0.0.1:8080` by default. You can change this by passing a `-pub_listen` flag, eg. `-pub_listen 127.0.0.1:4242`.
diff --git a/devtools/hackdoc/markdown.go b/devtools/hackdoc/markdown.go
index eaed905..5004642 100644
--- a/devtools/hackdoc/markdown.go
+++ b/devtools/hackdoc/markdown.go
@@ -19,14 +19,27 @@
 		Flags: blackfriday.CommonHTMLFlags | blackfriday.TOC,
 	})
 
+	// master is the default branch - do not make special links for that, as
+	// that makes them kinda ugly.
+	if ref == "master" {
+		ref = ""
+	}
+
 	parser := blackfriday.New(blackfriday.WithRenderer(r), blackfriday.WithExtensions(blackfriday.CommonExtensions))
 	ast := parser.Parse(input)
 
+	// Render table of contents (raw HTML) into bytes.
+	var tocB bytes.Buffer
+	tocB.Write([]byte(`<div class="toc">`))
+	r.RenderHeader(&tocB, ast)
+	tocB.Write([]byte(`</div>`))
+	toc := tocB.Bytes()
+
 	var buf bytes.Buffer
-	buf.Write([]byte(`<div class="toc"><h1>Page Contents</h1>`))
-	r.RenderHeader(&buf, ast)
-	buf.Write([]byte(`</div><div class="content">`))
+	buf.Write([]byte(`<div class="content">`))
+	// Render Markdown with some custom behaviour.
 	ast.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
+		// Fix intra-hackdoc links to contain ?ref=
 		if ref != "" && entering && node.Type == blackfriday.Link || node.Type == blackfriday.Image {
 			dest := string(node.Destination)
 			u, err := url.Parse(dest)
@@ -35,9 +48,14 @@
 				q["ref"] = []string{ref}
 				u.RawQuery = q.Encode()
 				node.Destination = []byte(u.String())
-				glog.Infof("link fix %q -> %q", dest, u.String())
+				glog.V(10).Infof("link fix %q -> %q", dest, u.String())
 			}
 		}
+		// Replace [TOC] anchor with a rendered TOC.
+		if entering && node.Type == blackfriday.Text && string(node.Literal) == "[TOC]" {
+			buf.Write(toc)
+			return blackfriday.GoToNext
+		}
 		return r.RenderNode(&buf, node, entering)
 	})
 	buf.Write([]byte(`</div>`))
diff --git a/devtools/hackdoc/tpl/default.html b/devtools/hackdoc/tpl/default.html
index e6280db..6211e38 100644
--- a/devtools/hackdoc/tpl/default.html
+++ b/devtools/hackdoc/tpl/default.html
@@ -169,7 +169,7 @@
     line-height: 1.5em;
 }
 
-.content ul {
+.content :not(li) > ul {
     padding-top: 0.5em;
     line-height: 1.5em;
 }
@@ -178,12 +178,20 @@
     padding-left: 1em;
 }
 
-.content ul li::before {
+.content :not(li) > ul > li::before {
     content: "•";
     color: #333;;
     display: inline-block;
     width: 1em;
-    margin-left: -1em;
+    margin-left: -0.5em;
+}
+
+.content li > ul > li::before {
+    content: "◦";
+    color: #333;;
+    display: inline-block;
+    width: 1em;
+    margin-left: -0.5em;
 }
 
 .content img {
@@ -193,37 +201,19 @@
 }
 
 .toc {
-    float: right;
-    padding: 1em 1em 1em 1em;
+    padding: .5em;
     border: 1px solid #ddd;
     background-color: #f8f8f8;
     margin: 2em;
     max-width: 30%;
-}
-
-.toc h1 {
-    font-size: 1.2em;
-    padding-bottom: 0.5em;
+    font-size: 1em;
+    font-family: sans-serif;
 }
 
 .toc a {
     text-decoration: none;
 }
 
-.toc li {
-    padding-left: 0.5em;
-}
-
-.toc ul {
-    list-style-type: disc;
-    padding-left: 1em;
-}
-
-.toc ul ul {
-    list-style-type: circle;
-}
-
-
 </style>
 {{ end }}
 {{ define "body" }}
diff --git a/devtools/kube/hackdoc.libsonnet b/devtools/kube/hackdoc.libsonnet
index 82d70d7..d849ebb 100644
--- a/devtools/kube/hackdoc.libsonnet
+++ b/devtools/kube/hackdoc.libsonnet
@@ -3,7 +3,7 @@
 
 {
     cfg:: {
-        image: "registry.k0.hswaw.net/q3k/hackdoc:1597078177-b6554a0dcef89157a79814f3febcfd9418c9b747",
+        image: "registry.k0.hswaw.net/q3k/hackdoc:1600885335-2b8f3c4af735f193604ee2f9240e063071a62de6",
         publicFQDN: error "public FQDN must be set",
     },
 
diff --git a/doc/codelabs/getting-started/your-first-change.md b/doc/codelabs/getting-started/your-first-change.md
index ef94ff8..93990d0 100644
--- a/doc/codelabs/getting-started/your-first-change.md
+++ b/doc/codelabs/getting-started/your-first-change.md
@@ -3,6 +3,8 @@
 
 This codelab will teach you how to send your first change to hscloud. We'll be modifying some files in your personal directory, so you won't bother anyone or break anything.
 
+[TOC]
+
 Prerequisites
 -------------
 
@@ -190,3 +192,4 @@
  - If you need a **Git refresher** - we highly recommend the [Git Visual Reference](https://marklodato.github.io/visual-git-guide/index-en.html)
  - While this codelab showed you how to create and submit CRs, you didn't see anything about code review. Watch this space for a codelab about that.
  - You should now be able to commit and change code. Watch this space for a link to a codelab on using Bazel or writing a simple microservice in Go.
+
diff --git a/personal/q3k/djtest/BUILD b/personal/q3k/djtest/BUILD
index d4ab6cc..8e82b25 100644
--- a/personal/q3k/djtest/BUILD
+++ b/personal/q3k/djtest/BUILD
@@ -5,6 +5,7 @@
     srcs = glob(["djtest/**/*.py"]),
     deps = [
         requirement("django"),
+        requirement("pytz"),
         requirement("sqlparse"),
     ],
 )
@@ -24,5 +25,6 @@
        ":app",
        "@bazel_tools//tools/python/runfiles",
         requirement("uwsgi"),
+        requirement("pyelftools"),
     ],
 )
diff --git a/personal/q3k/djtest/uwsgi-start.py b/personal/q3k/djtest/uwsgi-start.py
index eb22d55..a5c325e 100644
--- a/personal/q3k/djtest/uwsgi-start.py
+++ b/personal/q3k/djtest/uwsgi-start.py
@@ -6,13 +6,24 @@
 import bazel_tools
 import bazel_tools.tools.python
 
+from elftools.elf.elffile import ELFFile
+from elftools.elf.segments import InterpSegment
+
 from bazel_tools.tools.python.runfiles import runfiles
 r = runfiles.Create()
 
 uwsgi = r.Rlocation("pydeps_pypi__uWSGI_2_0_18/uWSGI-2.0.18.data/scripts/uwsgi")
-print(uwsgi)
 settings = r.Rlocation("hscloud/personal/q3k/djtest/djtest/settings.py")
-print(settings)
+
+# uwsgi from runfiles is non-chmodded, we have to run it through its interpreter
+ld = None
+with open(uwsgi, 'rb') as f:
+    elffile = ELFFile(f)
+    for segment in elffile.iter_segments():
+        if isinstance(segment, InterpSegment):
+            ld = segment.get_interp_name()
+if ld is None:
+    raise Exception("could not find interpreter/ld.so path in uwsgi - failing")
 
 apppath = os.path.dirname(settings)
 sitepath = os.path.dirname(apppath)
@@ -35,8 +46,7 @@
 cfgf.close()
 
 args = [
-    # uwsgi from runfiles is non-chmodded, run through interpreter
-    '/lib64/ld-linux-x86-64.so.2',
+    ld,
     uwsgi,
     '--ini', cfgf.name,
 ]
diff --git a/third_party/py/requirements.txt b/third_party/py/requirements.txt
index 875712e..94765d7 100644
--- a/third_party/py/requirements.txt
+++ b/third_party/py/requirements.txt
@@ -33,6 +33,7 @@
 pyasn1==0.4.5
 pycparser==2.19
 PyNaCl==1.3.0
+pyelftools==0.26
 python-dateutil==2.8.0
 pytz==2019.1
 requests==2.22.0
diff --git a/tools/go_sdk.bzl b/tools/go_sdk.bzl
deleted file mode 100644
index 3513b75..0000000
--- a/tools/go_sdk.bzl
+++ /dev/null
@@ -1,31 +0,0 @@
-def _gen_imports_impl(ctx):
-    ctx.file("BUILD", "")
-
-    if "hscloud_root" not in ctx.os.environ:
-        fail("Please souce env.sh")
-
-    is_nixos = "hscloud_nixos" in ctx.os.environ
-    bzl_file_content = """
-load(
-    "@io_bazel_rules_go//go:deps.bzl",
-    "go_register_toolchains",
-    "go_rules_dependencies",
-)
-def load_go_sdk():
-    go_rules_dependencies()
-    go_register_toolchains({go_version})
-    """.format(
-        go_version = 'go_version = "host"' if is_nixos else "",
-    )
-
-    ctx.file("imports.bzl", bzl_file_content)
-
-_gen_imports = repository_rule(
-    implementation = _gen_imports_impl,
-    attrs = dict(),
-)
-
-def gen_imports(name):
-    _gen_imports(
-        name = name,
-    )