diff --git a/README.md b/README.md index 11a05a7f..7d2684d3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -hscloud -======= +![](doc/img/hscloud-smol.png) `hscloud` is the main monorepo of the Warsaw Hackerspace infrastructure code. @@ -14,7 +13,14 @@ For a pleaseant web viewing experience, [see this documentation in hackdoc](http Getting started --------------- -You will need Bash and Bazel (1.2.0+). Clone this repo, cd into it and: +You will need Bash and Bazel (1.2.0+). + +First, clone the repository: + + git clone https://gerrit.hackerspace.pl/hscloud + cd hscloud + +Then, set up everything: . ./env.sh # setup PATH and hscloud_root tools/install.sh # build tools @@ -26,10 +32,12 @@ This does not pollute your system, and you can work on multiple hscloud checkout What now? --------- -If you want to use our Kubernetes cluster to run some stuff, see [//cluster/doc/user](cluster/doc/user). +If you want to use our Kubernetes cluster to run some stuff, see [//cluster/doc/user.md](cluster/doc/user.md). -If you're looking for administrative docs about cluster maintenance, see [//cluster/doc/admin](cluster/doc/admin). +If you're looking for administrative docs about cluster maintenance, see [//cluster/doc/admin.md](cluster/doc/admin.md). If you want to browse the source of `hscloud` in a web browser, use [gerrit's gitiles](https://gerrit.hackerspace.pl/plugins/gitiles/hscloud/+/refs/heads/master/). If you want to learn how to contribute to this repository, see [//doc/codelab/gerrit](doc/codelab/gerrit). + +If you want help, talk to q3k, informatic or your therapist. diff --git a/WORKSPACE b/WORKSPACE index bf25c0c2..75f3901d 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -197,6 +197,7 @@ http_archive( ], sha256 = "d8c45ee70ec39a57e7a05e5027c32b1576cc7f16d9dd37135b0eddde45cf1b10", ) + load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") go_repository( @@ -207,8 +208,11 @@ go_repository( # Invoke go_rules_dependencies depending on host platform. load("//tools:go_sdk.bzl", "gen_imports") + gen_imports(name = "go_sdk_imports") + load("@go_sdk_imports//:imports.bzl", "load_go_sdk") + load_go_sdk() gazelle_dependencies() @@ -2012,3 +2016,9 @@ go_repository( commit = "6a3e2ff9e7c564f36873c2e36413f634534f1c44", importpath = "github.com/xanzy/ssh-agent", ) + +go_repository( + name = "com_github_gabriel_vasile_mimetype", + commit = "06500030e7d26826f68caa5ca7d98c315c4caa28", + importpath = "github.com/gabriel-vasile/mimetype", +) diff --git a/cluster/README.md b/cluster/doc/admin.md similarity index 78% rename from cluster/README.md rename to cluster/doc/admin.md index 01208592..27b30ca1 100644 --- a/cluster/README.md +++ b/cluster/doc/admin.md @@ -1,23 +1,10 @@ HSCloud Clusters ================ +Admin documentation. For user documentation, see [//cluster/doc/user.md](/cluster/doc/user.md). + Current cluster: `k0.hswaw.net` -Accessing via kubectl ---------------------- - - prodaccess # get a short-lived certificate for your use via SSO - # if youre local username is not the same as your HSWAW SSO - # username, pass `-username foo` - kubectl version - kubectl top nodes - -Every user gets a `personal-$username` namespace. Feel free to use it for your own purposes, but watch out for resource usage! - - kubectl run -n personal-$username run --image=alpine:latest -it foo - -To proceed further you should be somewhat familiar with Kubernetes. Otherwise the rest of terminology might not make sense. We recommend going through the original Kubernetes tutorials. - Persistent Storage (waw2) ------------------------- @@ -64,9 +51,10 @@ Ceph - Debugging We run Ceph via Rook. The Rook operator is running in the `ceph-rook-system` namespace. To debug Ceph issues, start by looking at its logs. -A dashboard is available at https://ceph-waw2.hswaw.net/, to get the admin password run: +A dashboard is available at https://ceph-waw2.hswaw.net/ and https://ceph-waw3.hswaw.net, to get the admin password run: kubectl -n ceph-waw2 get secret rook-ceph-dashboard-password -o yaml | grep "password:" | awk '{print $2}' | base64 --decode ; echo + kubectl -n ceph-waw2 get secret rook-ceph-dashboard-password -o yaml | grep "password:" | awk '{print $2}' | base64 --decode ; echo Ceph - Backups @@ -75,6 +63,7 @@ Ceph - Backups Kubernetes PVs backed in Ceph RBDs get backed up using Benji. An hourly cronjob runs in every Ceph cluster. You can also manually trigger a run by doing: kubectl -n ceph-waw2 create job --from=cronjob/ceph-waw2-benji ceph-waw2-benji-manual-$(date +%s) + kubectl -n ceph-waw3 create job --from=cronjob/ceph-waw3-benji ceph-waw3-benji-manual-$(date +%s) Ceph ObjectStorage pools (RADOSGW) are _not_ backed up yet! @@ -83,8 +72,7 @@ Ceph - Object Storage To create an object store user consult rook.io manual (https://rook.io/docs/rook/v0.9/ceph-object-store-user-crd.html) User authentication secret is generated in ceph cluster namespace (`ceph-waw2`), -thus may need to be manually copied into application namespace. (see -`app/registry/prod.jsonnet` comment) +thus may need to be manually copied into application namespace. (see `app/registry/prod.jsonnet` comment) `tools/rook-s3cmd-config` can be used to generate test configuration file for s3cmd. Remember to append `:default-placement` to your region name (ie. `waw-hdd-redundant-1-object:default-placement`) diff --git a/cluster/doc/index.md b/cluster/doc/index.md new file mode 100644 index 00000000..afd04e43 --- /dev/null +++ b/cluster/doc/index.md @@ -0,0 +1,6 @@ +Warsaw Hackerspace Kubernetes Cluster +===================================== + +**User documentation**: [user.md](user.md). + +**Admin documentation**: [admin.md](admin.md). diff --git a/cluster/doc/user.md b/cluster/doc/user.md new file mode 100644 index 00000000..6dd6938a --- /dev/null +++ b/cluster/doc/user.md @@ -0,0 +1,58 @@ +Warsaw Hackerspace Kubernetes Clusters +====================================== + +End-user^Whacker documentation. + +Intro +----- + +We run Kubernetes, a cluster system on our production machines. This allows you to schedule software to run without having to worry about traditional deployment, or where your particular piece of code is actually running. This document will not teach you how to use Kubernetes, but will give you a short hands-on example on how to access it, and then point you in the right direction for general documentation to follow. + +Accessing Kubernetes +-------------------- + +Kubernetes is accessed fully via an API, for which there exists a standard command line tool: `kubectl`. If you've check out hscloud and followed the instructions in [//README.md]("/README.md"), you should have that tool built and available for you to use. + +Before you can use `kubectl`, however, you will need to authenticate yourself. To do that, run `prodaccess`. This will issue you short-term (~hours) credentials that `kubectl` can then pass on to Kubernetes to authenticate itself. + + $ prodaccess + 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`. + +By default, `prodaccess` will use your local user name to authenticate as `@hackerspce.pl`. If your Hackerspace SSO name is different, specify it using the `-u` flag to prodaccess, eg. `prodaccess -u informatic`. + +You can now check that you indeed have access to Kubernetes: + + $ kubectl version # show version of Kubernetes + $ kubectl top nodes # show node (machine/server) statistics + +You are now fully set up to schedule your own jobs on `k0.hswaw.net`, our currently only Kubernetes cluster. + +Running Stuff +------------- + +We have a fairly extensive role-based access control system set up to provide a level of multi-tenancy of our Kubernetes cluster. What this means is that you will not be able to modify other people's stuff. Indeed, by default, you barely have any access. So as you can experiment with Kubernetes, we automatically provision you a personal namespace (`personal-$USER`) in Kubernetes. This acts as your own playground, where you can run anything you want, as long as it doesn't eat into our resources too much. + +For example, to run an Alpine Linux Docker image in your own namespace: + + kubectl -n personal-$USER run --image=alpine:latest -it foo + +This will create a Kubernetes deployment named foo, running the `alpine:latest` Docker image, and drop you in an interactive shell in it. Naturally, replace `$USER` with your SSO username if it's different from your system username. + +Once you're done, delete the Deployment: + + kubectl -n personal-$USER delete deployment foo + +Pod Security +------------ + +Apart from the RBAC (role based access control) that prevents you from poling at things that you shouldn't over the API, we have one more security measure in place. Throught a Kubernetes mechanism called 'PodSecurityPolicy' we limit what pods (ie. containers) can do. Notably, pods will by default not be able to access any host data, run in privileged mode, or even setuid to a different uid. The most notable side effect of this is that some basic system tools within pods will not work: ie., apt on Ubuntu. + +More Kubernetes +--------------- + +We highly recommend following the [Kubernetes Basics](https://kubernetes.io/docs/tutorials/kubernetes-basics/) tutorial as a first step in using Kubernetes for real world applications. + +For defining production jobs, we use a language called `Jsonnet` via a tool called `kubecfg`. This is to replace some more popular tools that other Kubernetes systems use, eg. Helm. For more information about that, ping q3k so that he writes a codelab about it :). diff --git a/devtools/depotview/BUILD.bazel b/devtools/depotview/BUILD.bazel index 223f64cc..908a6298 100644 --- a/devtools/depotview/BUILD.bazel +++ b/devtools/depotview/BUILD.bazel @@ -1,3 +1,4 @@ +load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_layer", "container_push") load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") go_library( @@ -18,3 +19,28 @@ go_binary( embed = [":go_default_library"], visibility = ["//visibility:public"], ) + +container_layer( + name = "layer_bin", + files = [ + ":depotview", + ], + directory = "/devtools/", +) + +container_image( + name = "runtime", + base = "@prodimage-bionic//image", + layers = [ + ":layer_bin", + ], +) + +container_push( + name = "push", + image = ":runtime", + format = "Docker", + registry = "registry.k0.hswaw.net", + repository = "devtools/depotview", + tag = "{BUILD_TIMESTAMP}-{STABLE_GIT_COMMIT}", +) diff --git a/devtools/depotview/README.md b/devtools/depotview/README.md index 9b806a66..e1faca40 100644 --- a/devtools/depotview/README.md +++ b/devtools/depotview/README.md @@ -3,6 +3,11 @@ depotview Git-as-a-service over gRPC. Useful to get read-only access to hscloud. +Production +---------- + +There's a prod instance running at depotview.devtools-prod.svc.cluster.local. + Development ----------- diff --git a/devtools/hackdoc/BUILD.bazel b/devtools/hackdoc/BUILD.bazel index 3988dfe5..5536760c 100644 --- a/devtools/hackdoc/BUILD.bazel +++ b/devtools/hackdoc/BUILD.bazel @@ -1,3 +1,4 @@ +load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_layer", "container_push") load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") go_library( @@ -15,6 +16,7 @@ go_library( "//devtools/hackdoc/source:go_default_library", "//go/mirko:go_default_library", "//go/pki:go_default_library", + "@com_github_gabriel_vasile_mimetype//:go_default_library", "@com_github_golang_glog//:go_default_library", "@in_gopkg_russross_blackfriday_v2//:go_default_library", "@org_golang_google_grpc//:go_default_library", @@ -26,3 +28,28 @@ go_binary( embed = [":go_default_library"], visibility = ["//visibility:public"], ) + +container_layer( + name = "layer_bin", + files = [ + ":hackdoc", + ], + directory = "/devtools/", +) + +container_image( + name = "runtime", + base = "@prodimage-bionic//image", + layers = [ + ":layer_bin", + ], +) + +container_push( + name = "push", + image = ":runtime", + format = "Docker", + registry = "registry.k0.hswaw.net", + repository = "devtools/hackdoc", + tag = "{BUILD_TIMESTAMP}-{STABLE_GIT_COMMIT}", +) diff --git a/devtools/hackdoc/README.md b/devtools/hackdoc/README.md index 45d4486d..3226cf2b 100644 --- a/devtools/hackdoc/README.md +++ b/devtools/hackdoc/README.md @@ -18,6 +18,6 @@ Local Rendering To run hackdoc locally on a filesystem checkout (ie. when working on docs, templates, or hackdoc itself), run: - bazel run //devtools/hackdoc:local + 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`. diff --git a/devtools/hackdoc/config/BUILD.bazel b/devtools/hackdoc/config/BUILD.bazel index fecf6381..c5052c7b 100644 --- a/devtools/hackdoc/config/BUILD.bazel +++ b/devtools/hackdoc/config/BUILD.bazel @@ -8,7 +8,6 @@ go_library( deps = [ "//devtools/hackdoc/source:go_default_library", "@com_github_burntsushi_toml//:go_default_library", - "@com_github_golang_glog//:go_default_library", ], ) diff --git a/devtools/hackdoc/main.go b/devtools/hackdoc/main.go index 558268b4..aae850f7 100644 --- a/devtools/hackdoc/main.go +++ b/devtools/hackdoc/main.go @@ -111,7 +111,6 @@ func (s *service) handler(w http.ResponseWriter, r *http.Request) { return } - glog.Infof("%+v", r.URL.Query()) ref := r.URL.Query().Get("ref") if ref == "" { ref = flagGitwebDefaultBranch @@ -206,7 +205,7 @@ func (r *request) handlePageAuto(dirpath string) { } if file { - r.handleMarkdown(fpath, cfg) + http.Redirect(r.w, r.r, "/"+fpath, 302) return } } @@ -238,13 +237,18 @@ func (r *request) handlePage(page string) { if file { parts := strings.Split(r.rpath, "/") dirpath := strings.Join(parts[:(len(parts)-1)], "/") + // TODO(q3k): figure out this hack, hopefully by implementing a real path type + if dirpath == "/" { + dirpath = "//" + } + cfg, err := config.ForPath(r.ctx, r.source, dirpath) if err != nil { glog.Errorf("could not get config for path %q: %w", dirpath, err) r.handle500() return } - r.handleMarkdown(r.rpath, cfg) + r.handleFile(r.rpath, cfg) return } diff --git a/devtools/hackdoc/markdown.go b/devtools/hackdoc/markdown.go index 911c2c0b..fb46c5d2 100644 --- a/devtools/hackdoc/markdown.go +++ b/devtools/hackdoc/markdown.go @@ -8,6 +8,7 @@ import ( "code.hackerspace.pl/hscloud/devtools/hackdoc/config" + "github.com/gabriel-vasile/mimetype" "github.com/golang/glog" "gopkg.in/russross/blackfriday.v2" ) @@ -31,6 +32,7 @@ func renderMarkdown(input []byte, ref string) []byte { q["ref"] = []string{ref} u.RawQuery = q.Encode() node.Destination = []byte(u.String()) + glog.Infof("link fix %q -> %q", dest, u.String()) } } return r.RenderNode(&buf, node, entering) @@ -38,7 +40,12 @@ func renderMarkdown(input []byte, ref string) []byte { return buf.Bytes() } -func (r *request) handleMarkdown(path string, cfg *config.Config) { +type pathPart struct { + Label string + Path string +} + +func (r *request) handleFile(path string, cfg *config.Config) { data, err := r.source.ReadFile(r.ctx, path) if err != nil { glog.Errorf("ReadFile(%q): %w", err) @@ -46,30 +53,55 @@ func (r *request) handleMarkdown(path string, cfg *config.Config) { return } - rendered := renderMarkdown([]byte(data), r.ref) + // TODO(q3k): do MIME detection instead. + if strings.HasSuffix(path, ".md") { + rendered := renderMarkdown([]byte(data), r.ref) - r.logRequest("serving markdown at %s, cfg %+v", path, cfg) + r.logRequest("serving markdown at %s, cfg %+v", path, cfg) + + // TODO(q3k): allow markdown files to override which template to load + tmpl, ok := cfg.Templates["default"] + if !ok { + glog.Errorf("No default template found for %s", path) + // TODO(q3k): implement fallback template + r.w.Write(rendered) + return + } + + pathInDepot := strings.TrimPrefix(path, "//") + pathParts := []pathPart{ + {Label: "//", Path: "/"}, + } + parts := strings.Split(pathInDepot, "/") + fullPath := "" + for i, p := range parts { + label := p + if i != len(parts)-1 { + label = label + "/" + } + fullPath += "/" + p + pathParts = append(pathParts, pathPart{Label: label, Path: fullPath}) + } + + vars := map[string]interface{}{ + "Rendered": template.HTML(rendered), + "Title": path, + "Path": path, + "PathInDepot": pathInDepot, + "PathParts": pathParts, + "HackdocURL": flagHackdocURL, + "WebLinks": r.source.WebLinks(pathInDepot), + } + err = tmpl.Execute(r.w, vars) + if err != nil { + glog.Errorf("Could not execute template for %s: %v", err) + } - // TODO(q3k): allow markdown files to override which template to load - tmpl, ok := cfg.Templates["default"] - if !ok { - glog.Errorf("No default template found for %s", path) - // TODO(q3k): implement fallback template - r.w.Write(rendered) return } - pathInDepot := strings.TrimPrefix(path, "//") - vars := map[string]interface{}{ - "Rendered": template.HTML(rendered), - "Title": path, - "Path": path, - "PathInDepot": pathInDepot, - "HackdocURL": flagHackdocURL, - "WebLinks": r.source.WebLinks(pathInDepot), - } - err = tmpl.Execute(r.w, vars) - if err != nil { - glog.Errorf("Could not execute template for %s: %v", err) - } + // Just serve the file. + mime := mimetype.Detect(data) + r.w.Header().Set("Content-Type", mime.String()) + r.w.Write(data) } diff --git a/devtools/hackdoc/tpl/default.html b/devtools/hackdoc/tpl/default.html index 71209627..a6c1d428 100644 --- a/devtools/hackdoc/tpl/default.html +++ b/devtools/hackdoc/tpl/default.html @@ -58,7 +58,7 @@ body { } .column { - max-width: 80em; + width: 80em; padding: 1rem 0 1rem 0; } @@ -85,8 +85,16 @@ body { color: #b30014; } -.header span.muted { +.header span.part { color: #666; + padding-left: 0.2em; +} + +.header span.part a { + color: rgb(27, 106, 203); +} +.header span.part a:visited { + color: rgb(27, 106, 203); } .footer { @@ -98,6 +106,14 @@ body { text-align: right; } +.footer .left { + float: left; +} + +.footer .right { + float: right; +} + .footer a { color: #bbb; } @@ -164,15 +180,20 @@ ul li::before {
- hackdoc:{{ .Path }} - {{ range .WebLinks }} - [{{ .Kind }} {{ .LinkLabel }}] - {{ end }} + hackdoc: + {{ range .PathParts }}{{ .Label }}{{ end }} + shortcuts: root, cluster docs, codelabs
{{ .Rendered }}
diff --git a/doc/img/hscloud-smol.png b/doc/img/hscloud-smol.png new file mode 100644 index 00000000..3aa3126c Binary files /dev/null and b/doc/img/hscloud-smol.png differ diff --git a/doc/img/hscloud.png b/doc/img/hscloud.png new file mode 100644 index 00000000..9e3c1a19 Binary files /dev/null and b/doc/img/hscloud.png differ