*: do not require env.sh

This removes the need to source env.{sh,fish} when working with hscloud.

This is done by:

 1. Implementing a Go library to reliably detect the location of the
    active hscloud checkout. That in turn is enabled by
    BUILD_WORKSPACE_DIRECTORY being now a thing in Bazel.
 2. Creating a tool `hscloud`, with a command `hscloud workspace` that
    returns the workspace path.
 3. Wrapping this tool to be accessible from Python and Bash.
 4. Bumping all users of hscloud_root to use either the Go library or
    one of the two implemented wrappers.

We also drive-by replace tools/install.sh to be a proper sh_binary, and
make it yell at people if it isn't being ran as `bazel run
//tools:install`.

Finally, we also drive-by delete cluster/tools/nixops.sh which was never used.

Change-Id: I7873714319bfc38bbb930b05baa605c5aa36470a
Reviewed-on: https://gerrit.hackerspace.pl/c/hscloud/+/1169
Reviewed-by: informatic <informatic@hackerspace.pl>
diff --git a/cluster/clustercfg/BUILD b/cluster/clustercfg/BUILD
index 433f79c..e08a2e3 100644
--- a/cluster/clustercfg/BUILD
+++ b/cluster/clustercfg/BUILD
@@ -15,5 +15,6 @@
         requirement("idna"),
         requirement("six"),
         "//tools:secretstore_lib",
+        "//tools/hscloud:python",
     ],
 )
diff --git a/cluster/clustercfg/clustercfg.py b/cluster/clustercfg/clustercfg.py
index d852d6a..ea15df8 100644
--- a/cluster/clustercfg/clustercfg.py
+++ b/cluster/clustercfg/clustercfg.py
@@ -16,13 +16,12 @@
 import fabric
 
 from tools import secretstore
+from tools.hscloud import lib as hscloud
 
 import ca
 
 
-local_root = os.getenv('hscloud_root')
-if local_root is None:
-    raise Exception("Please source env.sh")
+local_root = hscloud.workspace_location()
 
 
 cluster = 'k0.hswaw.net'
diff --git a/cluster/doc/user.md b/cluster/doc/user.md
index f04e7db..11ea959 100644
--- a/cluster/doc/user.md
+++ b/cluster/doc/user.md
@@ -19,7 +19,10 @@
     Enter SSO/LDAP password for q3k@hackerspace.pl: 
     Good evening professor. I see you have driven here in your Ferrari.
 
-If `prodaccess` is not on your $PATH, ensure you have sourced `env.sh` from the root of hscloud and ran `tools/install.sh`.
+If `prodaccess` is not on your $PATH:
+
+    $ bazel run //tools:install
+    $ . env.sh
 
 By default, `prodaccess` will use your local user name to authenticate as `<user>@hackerspce.pl`. If your Hackerspace SSO name is different, specify it using the `-u` flag to prodaccess, eg. `prodaccess -u informatic`.
 
diff --git a/cluster/prodaccess/BUILD.bazel b/cluster/prodaccess/BUILD.bazel
index 6c72082..4db48dd 100644
--- a/cluster/prodaccess/BUILD.bazel
+++ b/cluster/prodaccess/BUILD.bazel
@@ -13,6 +13,7 @@
         "//cluster/certs:go_default_library",
         "//cluster/prodvider/proto:go_default_library",
         "//go/pki:go_default_library",
+        "//go/workspace:go_default_library",
         "@com_github_golang_glog//:go_default_library",
         "@org_golang_google_grpc//:go_default_library",
         "@org_golang_google_grpc//credentials:go_default_library",
diff --git a/cluster/prodaccess/kubernetes.go b/cluster/prodaccess/kubernetes.go
index 7226423..c50cd07 100644
--- a/cluster/prodaccess/kubernetes.go
+++ b/cluster/prodaccess/kubernetes.go
@@ -14,17 +14,18 @@
 	"github.com/golang/glog"
 
 	pb "code.hackerspace.pl/hscloud/cluster/prodvider/proto"
+	"code.hackerspace.pl/hscloud/go/workspace"
 )
 
 func kubernetesPaths() (string, string, string) {
-	localRoot := os.Getenv("hscloud_root")
-	if localRoot == "" {
-		glog.Exitf("Please source env.sh")
+	ws, err := workspace.Get()
+	if err != nil {
+		glog.Exitf("%v", err)
 	}
 
-	localKey := path.Join(localRoot, ".kubectl", fmt.Sprintf("%s.key", flagUsername))
-	localCert := path.Join(localRoot, ".kubectl", fmt.Sprintf("%s.crt", flagUsername))
-	localCA := path.Join(localRoot, ".kubectl", fmt.Sprintf("ca.crt"))
+	localKey := path.Join(ws, ".kubectl", fmt.Sprintf("%s.key", flagUsername))
+	localCert := path.Join(ws, ".kubectl", fmt.Sprintf("%s.crt", flagUsername))
+	localCA := path.Join(ws, ".kubectl", fmt.Sprintf("ca.crt"))
 
 	return localKey, localCert, localCA
 }
diff --git a/cluster/tools/BUILD b/cluster/tools/BUILD
index 2526fd7..a63245e 100644
--- a/cluster/tools/BUILD
+++ b/cluster/tools/BUILD
@@ -22,7 +22,14 @@
 sh_binary(
     name = "calicoctl",
     srcs = ["calicoctl.sh"],
-    data = [":calicoctl.bin", "//tools:secretstore"],
+    data = [
+        ":calicoctl.bin",
+        "//tools:secretstore",
+        "//tools/hscloud",
+    ],
+    deps = [
+        "//tools/hscloud:shell",
+    ],
 )
 
 copy_go_binary(
diff --git a/cluster/tools/calicoctl.sh b/cluster/tools/calicoctl.sh
index 30fe652..86e1097 100755
--- a/cluster/tools/calicoctl.sh
+++ b/cluster/tools/calicoctl.sh
@@ -1,23 +1,22 @@
 #!/usr/bin/env bash
 
-# A wrapper around the real calicoctl to configure etcd access...
+# A wrapper around the real calicoctl to configure etcd access.
 
-if [ -z "$hscloud_root" ]; then
-   echo 2>&1 "Please source env.sh"
-   exit 1
-fi
+source tools/hscloud/lib.sh || exit 1
 
-ETCD_ENDPOINTS="https://bc01n01.hswaw.net:2379,https://bc01n01.hswaw.net:2379,https://bc01n01.hswaw.net:2379"
-ETCD_KEY_FILE="$hscloud_root/cluster/secrets/plain/etcd-calico.key"
-ETCD_CERT_FILE="$hscloud_root/cluster/certs/etcd-calico.cert"
-ETCD_CA_CERT_FILE="$hscloud_root/cluster/certs/ca-etcd.crt"
+function main() {
+    local ws=$(hscloud::workspace_location)
 
-if [ ! -f "$ETCD_KEY_FILE" ] ; then
-        secretstore decrypt "$hscloud_root/cluster/secrets/cipher/etcd-calico.key" > "$ETCD_KEY_FILE"
-fi
+    export ETCD_ENDPOINTS="https://bc01n01.hswaw.net:2379,https://bc01n01.hswaw.net:2379,https://bc01n01.hswaw.net:2379"
+    export ETCD_KEY_FILE="$ws/cluster/secrets/plain/etcd-calico.key"
+    export ETCD_CERT_FILE="$ws/cluster/certs/etcd-calico.cert"
+    export ETCD_CA_CERT_FILE="$ws/cluster/certs/ca-etcd.crt"
 
-export ETCD_ENDPOINTS
-export ETCD_KEY_FILE
-export ETCD_CERT_FILE
-export ETCD_CA_CERT_FILE
-calicoctl.bin "$@"
+    if [ ! -f "$ETCD_KEY_FILE" ] ; then
+        $(hscloud::must_rlocation hscloud/tools/secretstore) decrypt "$ws/cluster/secrets/cipher/etcd-calico.key" "$ETCD_KEY_FILE"
+    fi
+
+    "$(hscloud::must_rlocation hscloud/cluster/tools/calicoctl.bin)" "$@"
+}
+
+main "$@"
diff --git a/cluster/tools/install.sh b/cluster/tools/install.sh
deleted file mode 100755
index 6f32fbb..0000000
--- a/cluster/tools/install.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env bash
-
-set -e -o pipefail
-
-if [ -z "$hscloud_root" ]; then
-    echo 2>&1 "Please first source env.sh"
-    exit 1
-fi
-
-cd "${hscloud_root}"
-
-bazel build \
-        //cluster/tools:kubectl \
-        //cluster/tools:kubecfg \
-        //cluster/tools:calicoctl \
-        //cluster/tools:cfssl
-
diff --git a/cluster/tools/nixops.sh b/cluster/tools/nixops.sh
deleted file mode 100755
index e810972..0000000
--- a/cluster/tools/nixops.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env bash
-
-# A wrapper around real nixops to decrypt GCP secret.
-
-if [ -z "$hscloud_root" ]; then
-   echo 2>&1 "Please source env.sh"
-   exit 1
-fi
-
-for f in sa.json sa.pem; do
-    plain="$hscloud_root/gcp/secrets/plain/$f"
-    cipher="$hscloud_root/gcp/secrets/cipher/$f"
-    if [ ! -f "$plain" ]; then
-        secretstore decrypt "$cipher" > "$plain"
-    fi
-done
-
-export GCE_PROJECT="hscloud"
-export GCE_SERVICE_ACCOUNT="nixops@hscloud.iam.gserviceaccount.com"
-export ACCESS_KEYPATH="$hscloud_root/gcp/secrets/plain/sa.pem"
-
-./external/nixops/bin/nixops "$@"