Merge "changelog-webapp: Added find build controllers"
diff --git a/src/cmd/changelog-webapp/README.md b/src/cmd/changelog-webapp/README.md
index 5917052..4b1a7d1 100644
--- a/src/cmd/changelog-webapp/README.md
+++ b/src/cmd/changelog-webapp/README.md
@@ -6,7 +6,7 @@
Create a new App Engine project to host this application. Detailed instructions are located [here](https://cloud.google.com/appengine/docs/standard/nodejs/building-app/creating-project).
### Secret Manager
-This application queries secret manager for any information that should not be publicly accessible, such as client secrets and internal instance URLs. Ensure this service is enabled in Google Cloud, and that the App Engine service account has the `Secret Manager Secret Accessor` role in Google Cloud IAM.
+This application queries secret manager for any information that should not be publicly accessible, such as client secrets and internal instance URLs. Ensure this service is enabled in Google Cloud, and that the App Engine service account has the `Secret Manager Secret Accessor` role in Google Cloud IAM. See [here](https://cloud.google.com/secret-manager/docs/configuring-secret-manager) for information on enabling the API and managing permissions.
## Configuration
`app.yaml` stores public environment variables used to run the service. For private environment variables, the variable name is stored in Secret Manager instead. This is indicated by the `_NAME` suffix for any environment variable.
diff --git a/src/cmd/changelog-webapp/app.yaml b/src/cmd/changelog-webapp/app.yaml
index 4f005fd..5044ddb 100644
--- a/src/cmd/changelog-webapp/app.yaml
+++ b/src/cmd/changelog-webapp/app.yaml
@@ -11,13 +11,16 @@
# Webpage configuration
STATIC_BASE_PATH: "src/cmd/changelog-webapp/static/"
CHANGELOG_QUERY_SIZE: "50"
+ COS_FALLBACK_REPO_PREFIX: "mirrors/cros/"
# External sources
- COS_EXTERNAL_GERRIT_INSTANCE: "cos-review.googlesource.com"
+ COS_EXTERNAL_GERRIT_INSTANCE: "https://cos-review.googlesource.com"
+ COS_EXTERNAL_FALLBACK_GERRIT_INSTANCE: "https://chromium-review.googlesource.com"
COS_EXTERNAL_GOB_INSTANCE: "cos.googlesource.com"
COS_EXTERNAL_MANIFEST_REPO: "cos/manifest-snapshots"
# Internal source names (values are retrieved from secret manager)
COS_INTERNAL_GERRIT_INSTANCE_NAME: "cos-internal-gerrit-instance"
+ COS_INTERNAL_FALLBACK_GERRIT_INSTANCE_NAME: "cos-internal-fallback-gerrit-instance"
COS_INTERNAL_GOB_INSTANCE_NAME: "cos-internal-gob-instance"
- COS_INTERNAL_MANIFEST_REPO_NAME: "cos-internal-manifest-repo"
\ No newline at end of file
+ COS_INTERNAL_MANIFEST_REPO_NAME: "cos-internal-manifest-repo"
diff --git a/src/cmd/changelog-webapp/controllers/authHandlers.go b/src/cmd/changelog-webapp/controllers/authHandlers.go
index deb71ac..aa159ee 100644
--- a/src/cmd/changelog-webapp/controllers/authHandlers.go
+++ b/src/cmd/changelog-webapp/controllers/authHandlers.go
@@ -171,7 +171,7 @@
return
}
if val, ok := session.Values["oauthState"]; !ok || val == nil {
- http.Redirect(w, r, "/", http.StatusPermanentRedirect)
+ http.Error(w, err.Error(), http.StatusBadRequest)
return
}
sessionState := session.Values["oauthState"].(string)
diff --git a/src/cmd/changelog-webapp/controllers/pageHandlers.go b/src/cmd/changelog-webapp/controllers/pageHandlers.go
index c2d24f6..edcec32 100644
--- a/src/cmd/changelog-webapp/controllers/pageHandlers.go
+++ b/src/cmd/changelog-webapp/controllers/pageHandlers.go
@@ -20,12 +20,14 @@
"fmt"
"net/http"
"os"
+ "regexp"
"strconv"
"strings"
"text/template"
secretmanager "cloud.google.com/go/secretmanager/apiv1"
"cos.googlesource.com/cos/tools/src/pkg/changelog"
+ "cos.googlesource.com/cos/tools/src/pkg/findbuild"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
@@ -37,17 +39,22 @@
)
var (
- internalInstance string
- internalManifestRepo string
- externalInstance string
- externalManifestRepo string
- envQuerySize string
+ internalGerritInstance string
+ internalFallbackGerritInstance string
+ internalGoBInstance string
+ internalManifestRepo string
+ externalGerritInstance string
+ externalFallbackGerritInstance string
+ externalGoBInstance string
+ externalManifestRepo string
+ fallbackRepoPrefix string
+ envQuerySize string
staticBasePath string
indexTemplate *template.Template
changelogTemplate *template.Template
promptLoginTemplate *template.Template
- locateCLTemplate *template.Template
+ locateBuildTemplate *template.Template
statusForbiddenTemplate *template.Template
basicTextTemplate *template.Template
@@ -68,7 +75,8 @@
codes.Unavailable.String(): "503 Service Unavailable",
codes.DataLoss.String(): "500 Internal Server Error",
}
- gitiles403Desc = "unexpected HTTP 403 from Gitiles"
+ gitiles403Desc = "unexpected HTTP 403 from Gitiles"
+ gerritErrCodeRe = regexp.MustCompile("status code\\s*(\\d+)")
)
func init() {
@@ -77,7 +85,15 @@
if err != nil {
log.Fatalf("Failed to setup client: %v", err)
}
- internalInstance, err = getSecret(client, os.Getenv("COS_INTERNAL_GOB_INSTANCE_NAME"))
+ internalGerritInstance, err = getSecret(client, os.Getenv("COS_INTERNAL_GERRIT_INSTANCE_NAME"))
+ if err != nil {
+ log.Fatalf("Failed to retrieve secret for COS_INTERNAL_GERRIT_INSTANCE_NAME with key name %s\n%v", os.Getenv("COS_INTERNAL_GERRIT_INSTANCE_NAME"), err)
+ }
+ internalFallbackGerritInstance, err = getSecret(client, os.Getenv("COS_INTERNAL_FALLBACK_GERRIT_INSTANCE_NAME"))
+ if err != nil {
+ log.Fatalf("Failed to retrieve secret for COS_INTERNAL_FALLBACK_GERRIT_INSTANCE_NAME with key name %s\n%v", os.Getenv("COS_INTERNAL_FALLBACK_GERRIT_INSTANCE_NAME"), err)
+ }
+ internalGoBInstance, err = getSecret(client, os.Getenv("COS_INTERNAL_GOB_INSTANCE_NAME"))
if err != nil {
log.Fatalf("Failed to retrieve secret for COS_INTERNAL_GOB_INSTANCE_NAME with key name %s\n%v", os.Getenv("COS_INTERNAL_GOB_INSTANCE_NAME"), err)
}
@@ -85,12 +101,16 @@
if err != nil {
log.Fatalf("Failed to retrieve secret for COS_INTERNAL_MANIFEST_REPO_NAME with key name %s\n%v", os.Getenv("COS_INTERNAL_MANIFEST_REPO_NAME"), err)
}
- externalInstance = os.Getenv("COS_EXTERNAL_GOB_INSTANCE")
+ externalGerritInstance = os.Getenv("COS_EXTERNAL_GERRIT_INSTANCE")
+ externalFallbackGerritInstance = os.Getenv("COS_EXTERNAL_FALLBACK_GERRIT_INSTANCE")
+ externalGoBInstance = os.Getenv("COS_EXTERNAL_GOB_INSTANCE")
externalManifestRepo = os.Getenv("COS_EXTERNAL_MANIFEST_REPO")
+ fallbackRepoPrefix = os.Getenv("COS_FALLBACK_REPO_PREFIX")
envQuerySize = getIntVerifiedEnv("CHANGELOG_QUERY_SIZE")
staticBasePath = os.Getenv("STATIC_BASE_PATH")
indexTemplate = template.Must(template.ParseFiles(staticBasePath + "templates/index.html"))
changelogTemplate = template.Must(template.ParseFiles(staticBasePath + "templates/changelog.html"))
+ locateBuildTemplate = template.Must(template.ParseFiles(staticBasePath + "templates/locateBuild.html"))
promptLoginTemplate = template.Must(template.ParseFiles(staticBasePath + "templates/promptLogin.html"))
basicTextTemplate = template.Must(template.ParseFiles(staticBasePath + "templates/error.html"))
}
@@ -112,6 +132,14 @@
Internal bool
}
+type locateBuildPage struct {
+ CL string
+ CLNum string
+ BuildNum string
+ GerritLink string
+ Internal bool
+}
+
type statusPage struct {
ActivePage string
}
@@ -162,6 +190,14 @@
return output
}
+func unwrappedError(err error) error {
+ innerErr := err
+ for errors.Unwrap(innerErr) != nil {
+ innerErr = errors.Unwrap(innerErr)
+ }
+ return innerErr
+}
+
func gobCommitLink(instance, repo, SHA string) string {
return fmt.Sprintf("https://%s/%s/+/%s", instance, repo, SHA)
}
@@ -230,6 +266,34 @@
return page
}
+func findBuildWithFallback(httpClient *http.Client, gerrit, fallbackGerrit, gob, repo, cl string, internal bool) (*findbuild.BuildResponse, bool, error) {
+ didFallback := false
+ request := &findbuild.BuildRequest{
+ HTTPClient: httpClient,
+ GerritHost: gerrit,
+ GitilesHost: gob,
+ ManifestRepo: repo,
+ RepoPrefix: "",
+ CL: cl,
+ }
+ buildData, err := findbuild.FindBuild(request)
+ innerErr := unwrappedError(err)
+ if innerErr == findbuild.ErrorCLNotFound {
+ log.Debugf("Cl %s not found in Gerrit instance, using fallback", cl)
+ fallbackRequest := &findbuild.BuildRequest{
+ HTTPClient: httpClient,
+ GerritHost: fallbackGerrit,
+ GitilesHost: gob,
+ ManifestRepo: repo,
+ RepoPrefix: fallbackRepoPrefix,
+ CL: cl,
+ }
+ buildData, err = findbuild.FindBuild(fallbackRequest)
+ didFallback = true
+ }
+ return buildData, didFallback, err
+}
+
// handleError creates the error page for a given error
func handleError(w http.ResponseWriter, inputErr error, currPage string) {
var header, text string
@@ -280,28 +344,34 @@
return
}
if err := r.ParseForm(); err != nil {
- changelogTemplate.Execute(w, &changelogPage{QuerySize: envQuerySize})
+ err = changelogTemplate.Execute(w, &changelogPage{QuerySize: envQuerySize})
+ if err != nil {
+ log.Errorf("HandleChangelog: error executing locatebuild template: %v", err)
+ }
return
}
source := r.FormValue("source")
target := r.FormValue("target")
// If no source/target values specified in request, display empty changelog page
if source == "" || target == "" {
- changelogTemplate.Execute(w, &changelogPage{QuerySize: envQuerySize, Internal: true})
+ err = changelogTemplate.Execute(w, &changelogPage{QuerySize: envQuerySize, Internal: true})
+ if err != nil {
+ log.Errorf("HandleChangelog: error executing locatebuild template: %v", err)
+ }
return
}
querySize, err := strconv.Atoi(r.FormValue("n"))
if err != nil {
querySize, _ = strconv.Atoi(envQuerySize)
}
- internal, instance, manifestRepo := false, externalInstance, externalManifestRepo
+ internal, instance, manifestRepo := false, externalGoBInstance, externalManifestRepo
if r.FormValue("internal") == "true" {
- internal, instance, manifestRepo = true, internalInstance, internalManifestRepo
+ internal, instance, manifestRepo = true, internalGoBInstance, internalManifestRepo
}
added, removed, err := changelog.Changelog(httpClient, source, target, instance, manifestRepo, querySize)
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)
+ source, target, externalGoBInstance, externalManifestRepo, err)
handleError(w, err, "changelog")
return
}
@@ -318,3 +388,60 @@
log.Errorf("HandleChangelog: error executing changelog template: %v", err)
}
}
+
+// HandleLocateBuild serves the Locate CL page
+func HandleLocateBuild(w http.ResponseWriter, r *http.Request) {
+ httpClient, err := HTTPClient(w, r, "/locatebuild/")
+ // Require login to access if no session found
+ if err != nil {
+ log.Debug(err)
+ err = promptLoginTemplate.Execute(w, &statusPage{ActivePage: "locatebuild"})
+ if err != nil {
+ log.Errorf("HandleLocateBuild: error executing promptLogin template: %v", err)
+ }
+ return
+ }
+ if err := r.ParseForm(); err != nil {
+ err = locateBuildTemplate.Execute(w, &locateBuildPage{Internal: true})
+ if err != nil {
+ log.Errorf("HandleLocateBuild: error executing locatebuild template: %v", err)
+ }
+ return
+ }
+ cl := r.FormValue("cl")
+ // If no CL value specified in request, display empty CL form
+ if cl == "" {
+ err = locateBuildTemplate.Execute(w, &locateBuildPage{Internal: true})
+ if err != nil {
+ log.Errorf("HandleLocateBuild: error executing locatebuild template: %v", err)
+ }
+ return
+ }
+ internal, gerrit, fallbackGerrit, gob, repo := false, externalGerritInstance, externalFallbackGerritInstance, externalGoBInstance, externalManifestRepo
+ if r.FormValue("internal") == "true" {
+ internal, gerrit, fallbackGerrit, gob, repo = true, internalGerritInstance, internalFallbackGerritInstance, internalGoBInstance, internalManifestRepo
+ }
+ buildData, didFallback, err := findBuildWithFallback(httpClient, gerrit, fallbackGerrit, gob, repo, cl, internal)
+ if err != nil {
+ log.Errorf("HandleLocateBuild: error retrieving build for CL %s with internal set to %t\n%v", cl, internal, err)
+ handleError(w, err, "locatebuild")
+ return
+ }
+ var gerritLink string
+ if didFallback {
+ gerritLink = fallbackGerrit + "/q/" + buildData.CLNum
+ } else {
+ gerritLink = gerrit + "/q/" + buildData.CLNum
+ }
+ page := &locateBuildPage{
+ CL: cl,
+ CLNum: buildData.CLNum,
+ BuildNum: buildData.BuildNum,
+ Internal: internal,
+ GerritLink: gerritLink,
+ }
+ err = locateBuildTemplate.Execute(w, page)
+ if err != nil {
+ log.Errorf("HandleLocateBuild: error executing locatebuild template: %v", err)
+ }
+}
diff --git a/src/cmd/changelog-webapp/main.go b/src/cmd/changelog-webapp/main.go
index a0f6fff..3813591 100644
--- a/src/cmd/changelog-webapp/main.go
+++ b/src/cmd/changelog-webapp/main.go
@@ -38,10 +38,11 @@
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(staticBasePath))))
http.HandleFunc("/", controllers.HandleIndex)
+ http.HandleFunc("/changelog/", controllers.HandleChangelog)
+ http.HandleFunc("/locatebuild/", controllers.HandleLocateBuild)
http.HandleFunc("/login/", func(w http.ResponseWriter, r *http.Request) {
controllers.HandleLogin(w, r, "/")
})
- http.HandleFunc("/changelog/", controllers.HandleChangelog)
http.HandleFunc("/oauth2callback/", controllers.HandleCallback)
if port == "" {