summaryrefslogtreecommitdiffhomepage
path: root/website/cmd
diff options
context:
space:
mode:
authorAdin Scannell <ascannell@google.com>2019-11-18 13:40:27 -0800
committerAdin Scannell <ascannell@google.com>2020-04-21 12:00:59 -0700
commit957e26a6f30d40e2bff042d76a327d0a2cfbabae (patch)
tree3e95d46355585ae4661de5cef30cdca72a7c94bb /website/cmd
parentdc2f198866c5fd8162a79978eb3633975d3ba11f (diff)
Move website to a simpler jekyll-based template
This will allow us to merge the site into the main repository. This merge allows the documentation to be kept up-to-date and synchronized with the main project. Builds will be triggered on any update, removing the need for the cron-based reploy.
Diffstat (limited to 'website/cmd')
-rw-r--r--website/cmd/generate-syscall-docs/main.go208
-rw-r--r--website/cmd/gvisor-website/main.go210
2 files changed, 418 insertions, 0 deletions
diff --git a/website/cmd/generate-syscall-docs/main.go b/website/cmd/generate-syscall-docs/main.go
new file mode 100644
index 000000000..d0faed128
--- /dev/null
+++ b/website/cmd/generate-syscall-docs/main.go
@@ -0,0 +1,208 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "bufio"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+ "text/template"
+)
+
+type CompatibilityInfo map[string]map[string]ArchInfo
+
+// ArchInfo is compatbility doc for an architecture.
+type ArchInfo struct {
+ // Syscalls maps syscall number for the architecture to the doc.
+ Syscalls map[uintptr]SyscallDoc `json:"syscalls"`
+}
+
+// SyscallDoc represents a single item of syscall documentation.
+type SyscallDoc struct {
+ Name string `json:"name"`
+ Support string `json:"support"`
+ Note string `json:"note,omitempty"`
+ URLs []string `json:"urls,omitempty"`
+}
+
+var mdTemplate = template.Must(template.New("out").Parse(`---
+title: {{.OS}}/{{.Arch}}
+description: Syscall Compatibility Reference Documentation for {{.OS}}/{{.Arch}}
+layout: docs
+category: Compatibility
+weight: 50
+permalink: /docs/user_guide/compatibility/{{.OS}}/{{.Arch}}/
+noedit: true
+---
+
+This table is a reference of {{.OS}} syscalls for the {{.Arch}} architecture and
+their compatibility status in gVisor. gVisor does not support all syscalls and
+some syscalls may have a partial implementation.
+
+This page is automatically generated from the source code.
+
+Of {{.Total}} syscalls, {{.Supported}} syscalls have a full or partial
+implementation. There are currently {{.Unsupported}} unsupported
+syscalls. {{if .Undocumented}}{{.Undocumented}} syscalls are not yet documented.{{end}}
+
+<table>
+ <thead>
+ <tr>
+ <th>#</th>
+ <th>Name</th>
+ <th>Support</th>
+ <th>Notes</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{range $i, $syscall := .Syscalls}}
+ <tr>
+ <td><a class="doc-table-anchor" id="{{.Name}}"></a>{{.Number}}</td>
+ <td><a href="http://man7.org/linux/man-pages/man2/{{.Name}}.2.html" target="_blank" rel="noopener">{{.Name}}</a></td>
+ <td>{{.Support}}</td>
+ <td>{{.Note}} {{range $i, $url := .URLs}}<br/>See: <a href="{{.}}">{{.}}</a>{{end}}</td>
+ </tr>
+ {{end}}
+ </tbody>
+</table>
+`))
+
+// Fatalf writes a message to stderr and exits with error code 1
+func Fatalf(format string, a ...interface{}) {
+ fmt.Fprintf(os.Stderr, format, a...)
+ os.Exit(1)
+}
+
+func main() {
+ inputFlag := flag.String("in", "-", "File to input ('-' for stdin)")
+ outputDir := flag.String("out", ".", "Directory to output files.")
+
+ flag.Parse()
+
+ var input io.Reader
+ if *inputFlag == "-" {
+ input = os.Stdin
+ } else {
+ i, err := os.Open(*inputFlag)
+ if err != nil {
+ Fatalf("Error opening %q: %v", *inputFlag, err)
+ }
+ input = i
+ }
+ input = bufio.NewReader(input)
+
+ var info CompatibilityInfo
+ d := json.NewDecoder(input)
+ if err := d.Decode(&info); err != nil {
+ Fatalf("Error reading json: %v", err)
+ }
+
+ weight := 0
+ for osName, osInfo := range info {
+ for archName, archInfo := range osInfo {
+ outDir := filepath.Join(*outputDir, osName)
+ outFile := filepath.Join(outDir, archName+".md")
+
+ if err := os.MkdirAll(outDir, 0755); err != nil {
+ Fatalf("Error creating directory %q: %v", *outputDir, err)
+ }
+
+ f, err := os.OpenFile(outFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
+ if err != nil {
+ Fatalf("Error opening file %q: %v", outFile, err)
+ }
+ defer f.Close()
+
+ weight += 10
+ data := struct {
+ OS string
+ Arch string
+ Weight int
+ Total int
+ Supported int
+ Unsupported int
+ Undocumented int
+ Syscalls []struct {
+ Name string
+ Number uintptr
+ Support string
+ Note string
+ URLs []string
+ }
+ }{
+ OS: strings.Title(osName),
+ Arch: archName,
+ Weight: weight,
+ Total: 0,
+ Supported: 0,
+ Unsupported: 0,
+ Undocumented: 0,
+ Syscalls: []struct {
+ Name string
+ Number uintptr
+ Support string
+ Note string
+ URLs []string
+ }{},
+ }
+
+ for num, s := range archInfo.Syscalls {
+ switch s.Support {
+ case "Full Support", "Partial Support":
+ data.Supported++
+ case "Unimplemented":
+ data.Unsupported++
+ default:
+ data.Undocumented++
+ }
+ data.Total++
+
+ for i := range s.URLs {
+ if !strings.HasPrefix(s.URLs[i], "http://") && !strings.HasPrefix(s.URLs[i], "https://") {
+ s.URLs[i] = "https://" + s.URLs[i]
+ }
+ }
+
+ data.Syscalls = append(data.Syscalls, struct {
+ Name string
+ Number uintptr
+ Support string
+ Note string
+ URLs []string
+ }{
+ Name: s.Name,
+ Number: num,
+ Support: s.Support,
+ Note: s.Note, // TODO urls
+ URLs: s.URLs,
+ })
+ }
+
+ sort.Slice(data.Syscalls, func(i, j int) bool {
+ return data.Syscalls[i].Number < data.Syscalls[j].Number
+ })
+
+ if err := mdTemplate.Execute(f, data); err != nil {
+ Fatalf("Error writing file %q: %v", outFile, err)
+ }
+ }
+ }
+}
diff --git a/website/cmd/gvisor-website/main.go b/website/cmd/gvisor-website/main.go
new file mode 100644
index 000000000..d86d7c735
--- /dev/null
+++ b/website/cmd/gvisor-website/main.go
@@ -0,0 +1,210 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "net/http"
+ "os"
+ "regexp"
+ "strings"
+)
+
+var redirects = map[string]string{
+ // Github redirects
+ "/change": "https://github.com/google/gvisor",
+ "/issue": "https://github.com/google/gvisor/issues",
+ "/issue/new": "https://github.com/google/gvisor/issues/new",
+ "/pr": "https://github.com/google/gvisor/pulls",
+
+ // For links
+ "/faq": "/docs/user_guide/faq/",
+
+ // Redirects to compatibility docs.
+ "/c": "/docs/user_guide/compatibility/",
+ "/c/linux/amd64": "/docs/user_guide/compatibility/linux/amd64/",
+
+ // Redirect for old urls
+ "/docs/user_guide/compatibility/amd64/": "/docs/user_guide/compatibility/linux/amd64/",
+ "/docs/user_guide/compatibility/amd64": "/docs/user_guide/compatibility/linux/amd64/",
+ "/docs/user_guide/kubernetes/": "/docs/user_guide/quick_start/kubernetes/",
+ "/docs/user_guide/kubernetes": "/docs/user_guide/quick_start/kubernetes/",
+ "/docs/user_guide/oci/": "/docs/user_guide/quick_start/oci/",
+ "/docs/user_guide/oci": "/docs/user_guide/quick_start/oci/",
+ "/docs/user_guide/docker/": "/docs/user_guide/quick_start/docker/",
+ "/docs/user_guide/docker": "/docs/user_guide/quick_start/docker/",
+
+ // Deprecated, but links continue to work.
+ "/cl": "https://gvisor-review.googlesource.com",
+}
+
+var prefixHelpers = map[string]string{
+ "change": "https://github.com/google/gvisor/commit/%s",
+ "issue": "https://github.com/google/gvisor/issues/%s",
+ "pr": "https://github.com/google/gvisor/pull/%s",
+
+ // Redirects to compatibility docs.
+ "c/linux/amd64": "/docs/user_guide/compatibility/linux/amd64/#%s",
+
+ // Deprecated, but links continue to work.
+ "cl": "https://gvisor-review.googlesource.com/c/gvisor/+/%s",
+}
+
+var (
+ validId = regexp.MustCompile(`^[A-Za-z0-9-]*/?$`)
+ goGetHTML5 = `<!doctype html><html><head><meta charset=utf-8>
+<meta name="go-import" content="gvisor.dev/gvisor git https://github.com/google/gvisor">
+<meta name="go-import" content="gvisor.dev/website git https://github.com/google/gvisor-website">
+<title>Go-get</title></head><body></html>`
+)
+
+// cronHandler wraps an http.Handler to check that the request is from the App
+// Engine Cron service.
+// See: https://cloud.google.com/appengine/docs/standard/go112/scheduling-jobs-with-cron-yaml#validating_cron_requests
+func cronHandler(h http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.Header.Get("X-Appengine-Cron") != "true" {
+ http.NotFound(w, r)
+ return
+ }
+ // Fallthrough.
+ h.ServeHTTP(w, r)
+ })
+}
+
+// wrappedHandler wraps an http.Handler.
+//
+// If the query parameters include go-get=1, then we redirect to a single
+// static page that allows us to serve arbitrary Go packages.
+func wrappedHandler(h http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ gg, ok := r.URL.Query()["go-get"]
+ if ok && len(gg) == 1 && gg[0] == "1" {
+ // Serve a trivial html page.
+ w.Write([]byte(goGetHTML5))
+ return
+ }
+ // Fallthrough.
+ h.ServeHTTP(w, r)
+ })
+}
+
+// redirectWithQuery redirects to the given target url preserving query parameters.
+func redirectWithQuery(w http.ResponseWriter, r *http.Request, target string) {
+ url := target
+ if qs := r.URL.RawQuery; qs != "" {
+ url += "?" + qs
+ }
+ http.Redirect(w, r, url, http.StatusFound)
+}
+
+// hostRedirectHandler redirects the www. domain to the naked domain.
+func hostRedirectHandler(h http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if strings.HasPrefix(r.Host, "www.") {
+ // Redirect to the naked domain.
+ r.URL.Scheme = "https" // Assume https.
+ r.URL.Host = r.Host[4:] // Remove the 'www.'
+ http.Redirect(w, r, r.URL.String(), http.StatusMovedPermanently)
+ return
+ }
+
+ if *projectId != "" && r.Host == *projectId+".appspot.com" && *customHost != "" {
+ // Redirect to the custom domain.
+ r.URL.Scheme = "https" // Assume https.
+ r.URL.Host = *customHost
+ http.Redirect(w, r, r.URL.String(), http.StatusMovedPermanently)
+ return
+ }
+ h.ServeHTTP(w, r)
+ })
+}
+
+// prefixRedirectHandler returns a handler that redirects to the given formated url.
+func prefixRedirectHandler(prefix, baseURL string) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if p := r.URL.Path; p == prefix {
+ // Redirect /prefix/ to /prefix.
+ http.Redirect(w, r, p[:len(p)-1], http.StatusFound)
+ return
+ }
+ id := r.URL.Path[len(prefix):]
+ if !validId.MatchString(id) {
+ http.Error(w, "Not found", http.StatusNotFound)
+ return
+ }
+ target := fmt.Sprintf(baseURL, id)
+ redirectWithQuery(w, r, target)
+ })
+}
+
+// redirectHandler returns a handler that redirects to the given url.
+func redirectHandler(target string) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ redirectWithQuery(w, r, target)
+ })
+}
+
+// redirectRedirects registers redirect http handlers.
+func registerRedirects(mux *http.ServeMux) {
+ if mux == nil {
+ mux = http.DefaultServeMux
+ }
+
+ for prefix, baseURL := range prefixHelpers {
+ p := "/" + prefix + "/"
+ mux.Handle(p, hostRedirectHandler(wrappedHandler(prefixRedirectHandler(p, baseURL))))
+ }
+
+ for path, redirect := range redirects {
+ mux.Handle(path, hostRedirectHandler(wrappedHandler(redirectHandler(redirect))))
+ }
+}
+
+// registerStatic registers static file handlers
+func registerStatic(mux *http.ServeMux, staticDir string) {
+ if mux == nil {
+ mux = http.DefaultServeMux
+ }
+ mux.Handle("/", hostRedirectHandler(wrappedHandler(http.FileServer(http.Dir(staticDir)))))
+}
+
+func envFlagString(name, def string) string {
+ if val := os.Getenv(name); val != "" {
+ return val
+ }
+ return def
+}
+
+var (
+ addr = flag.String("http", envFlagString("HTTP", ":8080"), "HTTP service address")
+ staticDir = flag.String("static-dir", envFlagString("STATIC_DIR", "_site"), "static files directory")
+
+ // Uses the standard GOOGLE_CLOUD_PROJECT environment variable set by App Engine.
+ projectId = flag.String("project-id", envFlagString("GOOGLE_CLOUD_PROJECT", ""), "The App Engine project ID.")
+ customHost = flag.String("custom-domain", envFlagString("CUSTOM_DOMAIN", "gvisor.dev"), "The application's custom domain.")
+)
+
+func main() {
+ flag.Parse()
+
+ registerRedirects(nil)
+ registerStatic(nil, *staticDir)
+
+ log.Printf("Listening on %s...", *addr)
+ log.Fatal(http.ListenAndServe(*addr, nil))
+}