main.go (80 lines of code) (raw):
// Copyright 2021 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
//
// http://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"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"time"
"cloud.google.com/go/logging"
"example.com/micro/metadata"
"github.com/gorilla/mux"
"google.golang.org/api/option"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
type App struct {
*http.Server
projectID string
log *logging.Logger
}
func main() {
ctx := context.Background()
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
log.Printf("listening on port %s", port)
projectID := os.Getenv("GOOGLE_CLOUD_PROJECT")
app, err := newApp(ctx, port, projectID)
if err != nil {
log.Fatalf("unable to initialize application: %v", err)
}
log.Println("starting HTTP server")
go func() {
if err := app.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("Server closed: %v", err)
}
}()
// Listen for SIGINT to gracefully shutdown.
nctx, stop := signal.NotifyContext(ctx, os.Interrupt, os.Kill)
defer stop()
<-nctx.Done()
log.Println("shutdown initiated")
// Cloud Run gives apps 10 seconds to shutdown. See
// https://cloud.google.com/blog/topics/developers-practitioners/graceful-shutdowns-cloud-run-deep-dive
// for more details.
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
app.Shutdown(ctx)
log.Println("shutdown")
}
func newApp(ctx context.Context, port, projectID string) (*App, error) {
app := &App{
Server: &http.Server{
Addr: ":" + port,
// Add some defaults, should be changed to suit your use case.
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
},
}
if projectID == "" {
projID, err := metadata.ProjectID()
if err != nil {
return nil, fmt.Errorf("unable to detect Project ID from GOOGLE_CLOUD_PROJECT or metadata server: %w", err)
}
projectID = projID
}
app.projectID = projectID
client, err := logging.NewClient(ctx, fmt.Sprintf("projects/%s", app.projectID),
// We don't need to make any requests when logging to stderr.
option.WithoutAuthentication(),
option.WithGRPCDialOption(
grpc.WithTransportCredentials(insecure.NewCredentials()),
))
if err != nil {
return nil, fmt.Errorf("unable to initialize logging client: %v", err)
}
app.log = client.Logger("test-log", logging.RedirectAsJSON(os.Stderr))
// Setup request router.
r := mux.NewRouter()
r.HandleFunc("/", app.Handler).
Methods("GET")
app.Server.Handler = r
return app, nil
}