Merge "changelog-webapp: Added error pages"
diff --git a/go.mod b/go.mod
index a147d73..b915599 100644
--- a/go.mod
+++ b/go.mod
@@ -21,5 +21,6 @@
google.golang.org/api v0.28.0
google.golang.org/grpc v1.29.1
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790
+ google.golang.org/grpc v1.29.1
google.golang.org/protobuf v1.25.0
)
diff --git a/src/cmd/changelog-webapp/controllers/pageHandlers.go b/src/cmd/changelog-webapp/controllers/pageHandlers.go
index abc425e..c2d24f6 100644
--- a/src/cmd/changelog-webapp/controllers/pageHandlers.go
+++ b/src/cmd/changelog-webapp/controllers/pageHandlers.go
@@ -16,6 +16,7 @@
import (
"context"
+ "errors"
"fmt"
"net/http"
"os"
@@ -25,6 +26,8 @@
secretmanager "cloud.google.com/go/secretmanager/apiv1"
"cos.googlesource.com/cos/tools/src/pkg/changelog"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
log "github.com/sirupsen/logrus"
)
@@ -39,10 +42,33 @@
externalInstance string
externalManifestRepo string
envQuerySize string
- staticBasePath string
- indexTemplate *template.Template
- changelogTemplate *template.Template
- promptLoginTemplate *template.Template
+
+ staticBasePath string
+ indexTemplate *template.Template
+ changelogTemplate *template.Template
+ promptLoginTemplate *template.Template
+ locateCLTemplate *template.Template
+ statusForbiddenTemplate *template.Template
+ basicTextTemplate *template.Template
+
+ grpcCodeToHeader = map[string]string{
+ codes.Canceled.String(): "499 Client Closed Request",
+ codes.Unknown.String(): "500 Internal Server Error",
+ codes.InvalidArgument.String(): "400 Bad Request",
+ codes.DeadlineExceeded.String(): "504 Gateway Timeout",
+ codes.NotFound.String(): "404 Not Found",
+ codes.PermissionDenied.String(): "403 Forbidden",
+ codes.Unauthenticated.String(): "401 Unauthorized",
+ codes.ResourceExhausted.String(): "429 Too Many Requests",
+ codes.FailedPrecondition.String(): "400 Bad Request",
+ codes.Aborted.String(): "409 Conflict",
+ codes.OutOfRange.String(): "400 Bad Request",
+ codes.Unimplemented.String(): "501 Not Implemented",
+ codes.Internal.String(): "500 Internal Server Error",
+ codes.Unavailable.String(): "503 Service Unavailable",
+ codes.DataLoss.String(): "500 Internal Server Error",
+ }
+ gitiles403Desc = "unexpected HTTP 403 from Gitiles"
)
func init() {
@@ -66,6 +92,7 @@
indexTemplate = template.Must(template.ParseFiles(staticBasePath + "templates/index.html"))
changelogTemplate = template.Must(template.ParseFiles(staticBasePath + "templates/changelog.html"))
promptLoginTemplate = template.Must(template.ParseFiles(staticBasePath + "templates/promptLogin.html"))
+ basicTextTemplate = template.Must(template.ParseFiles(staticBasePath + "templates/error.html"))
}
type changelogData struct {
@@ -85,6 +112,16 @@
Internal bool
}
+type statusPage struct {
+ ActivePage string
+}
+
+type basicTextPage struct {
+ Header string
+ Body string
+ ActivePage string
+}
+
type repoTable struct {
Name string
Additions []*repoTableEntry
@@ -114,10 +151,6 @@
URL string
}
-type promptLoginPage struct {
- ActivePage string
-}
-
// getIntVerifiedEnv retrieves an environment variable but checks that it can be
// converted to int first
func getIntVerifiedEnv(envName string) string {
@@ -197,6 +230,39 @@
return page
}
+// handleError creates the error page for a given error
+func handleError(w http.ResponseWriter, inputErr error, currPage string) {
+ var header, text string
+ innerErr := inputErr
+ for errors.Unwrap(innerErr) != nil {
+ innerErr = errors.Unwrap(innerErr)
+ }
+ rpcStatus, ok := status.FromError(innerErr)
+ // Error is not a status code, display generic header
+ if !ok {
+ basicTextTemplate.Execute(w, &basicTextPage{
+ Header: "An error occurred while fulfilling your request",
+ Body: innerErr.Error(),
+ ActivePage: currPage,
+ })
+ return
+ }
+ code, text := rpcStatus.Code(), rpcStatus.Message()
+ // RPC status code misclassifies 403 error as internal for Gitiles requests
+ if text == gitiles403Desc {
+ code = codes.PermissionDenied
+ }
+ if _, ok := grpcCodeToHeader[code.String()]; !ok {
+ header = "An error occurred while fulfilling your request"
+ }
+ header = grpcCodeToHeader[code.String()]
+ basicTextTemplate.Execute(w, &basicTextPage{
+ Header: header,
+ Body: text,
+ ActivePage: currPage,
+ })
+}
+
// HandleIndex serves the home page
func HandleIndex(w http.ResponseWriter, r *http.Request) {
indexTemplate.Execute(w, nil)
@@ -204,19 +270,19 @@
// HandleChangelog serves the changelog page
func HandleChangelog(w http.ResponseWriter, r *http.Request) {
- if err := r.ParseForm(); err != nil {
- changelogTemplate.Execute(w, &changelogPage{QuerySize: envQuerySize})
- return
- }
httpClient, err := HTTPClient(w, r, "/changelog/")
if err != nil {
log.Debug(err)
- err = promptLoginTemplate.Execute(w, &promptLoginPage{ActivePage: "changelog"})
+ err = promptLoginTemplate.Execute(w, &statusPage{ActivePage: "changelog"})
if err != nil {
log.Errorf("HandleChangelog: error executing promptLogin template: %v", err)
}
return
}
+ if err := r.ParseForm(); err != nil {
+ changelogTemplate.Execute(w, &changelogPage{QuerySize: envQuerySize})
+ return
+ }
source := r.FormValue("source")
target := r.FormValue("target")
// If no source/target values specified in request, display empty changelog page
@@ -236,7 +302,7 @@
if err != nil {
log.Errorf("HandleChangelog: error retrieving changelog between builds %s and %s on GoB instance: %s with manifest repository: %s\n%v\n",
source, target, externalInstance, externalManifestRepo, err)
- http.Error(w, err.Error(), http.StatusInternalServerError)
+ handleError(w, err, "changelog")
return
}
page := createChangelogPage(changelogData{
diff --git a/src/cmd/changelog-webapp/static/templates/error.html b/src/cmd/changelog-webapp/static/templates/error.html
new file mode 100644
index 0000000..b57a938
--- /dev/null
+++ b/src/cmd/changelog-webapp/static/templates/error.html
@@ -0,0 +1,35 @@
+<html>
+<head>
+ <meta name="description" content="Google COS build information">
+ <link rel="stylesheet" href="/static/css/base.css">
+</head>
+<body>
+ <div class="navbar">
+ <p class="navbar-title">Container Optimized OS</p>
+ </div>
+ <div class="sidenav">
+ {{if (eq .ActivePage "home")}}
+ <a class="active" href="/">Home</a>
+ {{else}}
+ <a href="/">Home</a>
+ {{end}}
+ {{if (eq .ActivePage "changelog")}}
+ <a class="active" href="/changelog/">Changelog</a>
+ {{else}}
+ <a href="/changelog/">Changelog</a>
+ {{end}}
+ {{if (eq .ActivePage "locateCl")}}
+ <a class="active" href="/locatecl/">Locate CL</a>
+ {{else}}
+ <a href="/locatecl/">Locate CL</a>
+ {{end}}
+ <a href="/login/">Login</a>
+ </div>
+ <div class="main">
+ <h1>{{ .Header }}</h1>
+ <p>{{ .Body }}</a>
+ </p>
+ </div>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/src/pkg/changelog/changelog.go b/src/pkg/changelog/changelog.go
index 1f26545..098138a 100644
--- a/src/pkg/changelog/changelog.go
+++ b/src/pkg/changelog/changelog.go
@@ -85,7 +85,7 @@
}
client, err := gerritClient(httpClient, remoteURL)
if err != nil {
- return fmt.Errorf("createClients: error creating client mapping:\n%v", err)
+ return fmt.Errorf("createClients: error creating client mapping:\n%w", err)
}
clients[remoteURL] = client
}
@@ -102,7 +102,7 @@
}
doc := etree.NewDocument()
if err := doc.ReadFromString(manifest); err != nil {
- return nil, fmt.Errorf("repoMap: error parsing manifest xml:\n%v", err)
+ return nil, fmt.Errorf("repoMap: error parsing manifest xml:\n%w", err)
}
root := doc.SelectElement("manifest")
@@ -136,12 +136,12 @@
log.Debugf("Retrieving manifest file for build %s\n", buildNum)
response, err := utils.DownloadManifest(client, repo, buildNum)
if err != nil {
- return nil, fmt.Errorf("mappedManifest: error downloading manifest file from repo %s:\n%v",
+ return nil, fmt.Errorf("mappedManifest: error downloading manifest file from repo %s:\n%w",
repo, err)
}
mappedManifest, err := repoMap(response.Contents)
if err != nil {
- return nil, fmt.Errorf("mappedManifest: error parsing manifest contents from repo %s:\n%v",
+ return nil, fmt.Errorf("mappedManifest: error parsing manifest contents from repo %s:\n%w",
repo, err)
}
return mappedManifest, nil
@@ -239,27 +239,27 @@
// so that client knows what URL to use
manifestClient, err := gerritClient(httpClient, host)
if err != nil {
- return nil, nil, fmt.Errorf("Changelog: error creating client for GoB instance: %s:\n%v", host, err)
+ return nil, nil, fmt.Errorf("Changelog: error creating client for GoB instance: %s:\n%w", host, err)
}
sourceRepos, err := mappedManifest(manifestClient, repo, sourceBuildNum)
if err != nil {
- return nil, nil, fmt.Errorf("Changelog: error retrieving mapped manifest for source build number: %s using manifest repository: %s:\n%v",
+ return nil, nil, fmt.Errorf("Changelog: error retrieving mapped manifest for source build number: %s using manifest repository: %s:\n%w",
sourceBuildNum, repo, err)
}
targetRepos, err := mappedManifest(manifestClient, repo, targetBuildNum)
if err != nil {
- return nil, nil, fmt.Errorf("Changelog: error retrieving mapped manifest for target build number: %s using manifest repository: %s:\n%v",
+ return nil, nil, fmt.Errorf("Changelog: error retrieving mapped manifest for target build number: %s using manifest repository: %s:\n%w",
targetBuildNum, repo, err)
}
clients[host] = manifestClient
err = createGerritClients(clients, httpClient, sourceRepos)
if err != nil {
- return nil, nil, fmt.Errorf("Changelog: error creating source clients:\n%v", err)
+ return nil, nil, fmt.Errorf("Changelog: error creating source clients:\n%w", err)
}
err = createGerritClients(clients, httpClient, targetRepos)
if err != nil {
- return nil, nil, fmt.Errorf("Changelog: error creating target clients:\n%v", err)
+ return nil, nil, fmt.Errorf("Changelog: error creating target clients:\n%w", err)
}
addChan := make(chan additionsResult, 1)
@@ -268,11 +268,11 @@
go additions(clients, targetRepos, sourceRepos, querySize, missChan)
missRes := <-missChan
if missRes.Err != nil {
- return nil, nil, fmt.Errorf("Changelog: failure when retrieving missed commits:\n%v", missRes.Err)
+ return nil, nil, fmt.Errorf("Changelog: failure when retrieving missed commits:\n%w", missRes.Err)
}
addRes := <-addChan
if addRes.Err != nil {
- return nil, nil, fmt.Errorf("Changelog: failure when retrieving commit additions:\n%v", addRes.Err)
+ return nil, nil, fmt.Errorf("Changelog: failure when retrieving commit additions:\n%w", addRes.Err)
}
return addRes.Additions, missRes.Additions, nil