hello.go (152 lines of code) (raw):

// 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 ( "context" "encoding/base64" "encoding/json" "fmt" "html/template" "io" "log" "net/http" "os" "regexp" cloudevents "github.com/cloudevents/sdk-go/v2" cloudeventsClient "github.com/cloudevents/sdk-go/v2/client" ) type Data struct { Service string Revision string Project string Region string AuthenticatedEmail string Color string } func handleReceivedEvent(ctx context.Context, event cloudevents.Event) { type LoggedEvent struct { Severity string `json:"severity"` EventType string `json:"eventType"` Message string `json:"message"` Event cloudevents.Event `json:"event"` } type PubSubMessage struct { Data string `json:"data"` } type PubSubEvent struct { Message PubSubMessage `json:"message"` } dataMessage := event.Data() // In case of PubSub event, decode its payload to be printed in the message as-is. if event.Type() == "google.cloud.pubsub.topic.v1.messagePublished" { obj := &PubSubEvent{} if err := event.DataAs(obj); err != nil { fmt.Printf("Unable to decode PubSub data: %s\n", err.Error()) } decodedMessage, decodingError := base64.StdEncoding.DecodeString(obj.Message.Data) if decodingError != nil { fmt.Printf("Unable to decode PubSub message: %s\n", decodingError.Error()) } else { dataMessage = decodedMessage } } loggedEvent := LoggedEvent{ Severity: "INFO", EventType: event.Type(), Message: fmt.Sprintf("Received event of type %s. Event data: %s", event.Type(), dataMessage), Event: event, // Always log full event data } jsonLog, err := json.Marshal(loggedEvent) if err != nil { fmt.Printf("Unable to log event to JSON: %s\n", err.Error()) } else { fmt.Printf("%s\n", jsonLog) } } func getEventsHandler() *cloudeventsClient.EventReceiver { ctx := context.Background() p, err := cloudevents.NewHTTP() if err != nil { log.Fatalf("failed to create http listener for receiving events: %s", err.Error()) } h, err := cloudevents.NewHTTPReceiveHandler(ctx, p, handleReceivedEvent) if err != nil { log.Fatalf("failed to create handler for receiving events: %s", err.Error()) } return h } func main() { tmpl := template.Must(template.ParseFiles("./index.html")) // Get project ID from metadata server project := "" client := &http.Client{} req, _ := http.NewRequest("GET", "http://metadata.google.internal/computeMetadata/v1/project/project-id", nil) req.Header.Set("Metadata-Flavor", "Google") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode == 200 { responseBody, err := io.ReadAll(res.Body) if err != nil { log.Fatal(err) } project = string(responseBody) } } // Get region from metadata server region := "" req, _ = http.NewRequest("GET", "http://metadata.google.internal/computeMetadata/v1/instance/region", nil) req.Header.Set("Metadata-Flavor", "Google") res, err = client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode == 200 { responseBody, err := io.ReadAll(res.Body) if err != nil { log.Fatal(err) } region = regexp.MustCompile(`projects/[^/]*/regions/`).ReplaceAllString(string(responseBody), "") } } if region == "" { // Fallback: get "zone" from metadata server (running on VM e.g. Cloud Run for Anthos) req, _ = http.NewRequest("GET", "http://metadata.google.internal/computeMetadata/v1/instance/zone", nil) req.Header.Set("Metadata-Flavor", "Google") res, err = client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode == 200 { responseBody, err := io.ReadAll(res.Body) if err != nil { log.Fatal(err) } region = regexp.MustCompile(`projects/[^/]*/zones/`).ReplaceAllString(string(responseBody), "") } } } service := os.Getenv("K_SERVICE") revision := os.Getenv("K_REVISION") color := os.Getenv("COLOR") data := Data{ Service: service, Revision: revision, Project: project, Region: region, Color: color, } eventsHandler := getEventsHandler() http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodPost && r.Header.Get("ce-type") != "" { // Handle cloud events. eventsHandler.ServeHTTP(w, r) return } // Default handler (hello page). data.AuthenticatedEmail = r.Header.Get("X-Goog-Authenticated-User-Email") // set when behind IAP tmpl.Execute(w, data) }) http.HandleFunc("/robots.txt", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "User-agent: *\nDisallow: /\n") }) fs := http.FileServer(http.Dir("./assets")) http.Handle("/assets/", http.StripPrefix("/assets/", fs)) port := os.Getenv("PORT") if port == "" { port = "8080" } address := fmt.Sprintf(":%s", port) log.Printf("Hello from Cloud Run! The container started successfully and is listening for HTTP requests on port %s", port) log.Fatal(http.ListenAndServe(address, nil)) }