Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions run/hello-broken/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
vendor/
51 changes: 51 additions & 0 deletions run/hello-broken/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# 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.

# [START run_broken_dockerfile]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Dockerfile is using the version workshopped in knative/docs#1774, let's finalize for this repo here and I'll send a follow-up to port the other Cloud Run samples.


# Use the offical golang image to create a binary.
# This is based on Debian and sets the GOPATH to /go.
# https://hub.docker.com/_/golang
FROM golang:1.13-buster as builder

# Create and change to the app directory.
WORKDIR /app

# Retrieve application dependencies.
# This allows the container build to reuse cached dependencies.
# Expecting to copy go.mod and if present go.sum.
COPY go.* ./
RUN go mod download

# Copy local code to the container image.
COPY . ./

# Build the binary.
RUN go build -mod=readonly -v -o server

# Use the official Debian slim image for a lean production container.
# https://hub.docker.com/_/debian
# https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
FROM debian:buster-slim
RUN set -e && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
ca-certificates && \
rm -rf /var/lib/apt/lists/*

# Copy the binary to the production image from the builder stage.
COPY --from=builder /app/server /server

# Run the web service on container startup.
CMD ["/server"]

# [END run_broken_dockerfile]
3 changes: 3 additions & 0 deletions run/hello-broken/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/GoogleCloudPlatform/golang-samples/run/hello-broken

go 1.13
75 changes: 75 additions & 0 deletions run/hello-broken/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// 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.

// [START run_broken_service]

// Sample hello demonstrates a difficult to troubleshoot service.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More comment would be nice here. What is troublesome about it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sample is used in a tutorial where the reader is eventually given this answer, but a comment in the first code snippet would be counter-productive. Is there somewhere else I might add this detail?

package main

import (
"fmt"
"log"
"net/http"
"os"
)

func main() {
log.Print("hello: service started")

http.HandleFunc("/", helloHandler)

// [END run_broken_service]
http.HandleFunc("/improved", improvedHandler)
// [START run_broken_service]

port := os.Getenv("PORT")
if port == "" {
port = "8080"
log.Printf("Defaulting to port %s", port)
}

log.Printf("Listening on port %s", port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
}

func helloHandler(w http.ResponseWriter, r *http.Request) {
log.Print("hello: received request")

// [START run_broken_service_problem]
name := os.Getenv("NAME")
if name == "" {
log.Printf("Missing required server parameter")
// The panic stack trace appears in Stackdriver Error Reporting.
panic("Missing required server parameter")
}
// [END run_broken_service_problem]

fmt.Fprintf(w, "Hello %s!\n", name)
}

// [END run_broken_service]

func improvedHandler(w http.ResponseWriter, r *http.Request) {
log.Print("hello: received request")

// [START run_broken_service_upgrade]
name := os.Getenv("NAME")
if name == "" {
name = "World"
log.Printf("warning: NAME not set, default to %s", name)
}
// [END run_broken_service_upgrade]

fmt.Fprintf(w, "Hello %s!\n", name)
}
103 changes: 103 additions & 0 deletions run/hello-broken/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// 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 (
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
)

func TestBrokenErrors(t *testing.T) {
payload := strings.NewReader("")
req := httptest.NewRequest("GET", "/", payload)
rr := httptest.NewRecorder()

os.Setenv("NAME", "")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be missing something, but why is this needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sample demonstrates a brittle, low-visibility handling of an unset value, and a sane-defaults high visibility approach. I am testing the cases around both.


defer func() {
if r := recover(); r == nil {
t.Errorf("brokenHandler: got (no panic), want (panic)")
}
}()
brokenHandler(rr, req)
}

func TestBrokenHandler(t *testing.T) {
tests := []struct {
label string
name string
want string
}{
{
label: "<SET>",
name: "Testers",
want: "Hello Testers!\n",
},
}

for _, test := range tests {
req := httptest.NewRequest("GET", "/", strings.NewReader(""))
rr := httptest.NewRecorder()

os.Setenv("NAME", test.name)
improvedHandler(rr, req)

if code := rr.Result().StatusCode; code != http.StatusOK {
t.Errorf("brokenHandler(%s): got (%q), want (%q)", test.label, code, http.StatusOK)
}

if got := rr.Body.String(); test.want != got {
t.Errorf("brokenHandler(%s): got (%q), want (%q)", test.label, got, test.want)
}
}
}

func TestImprovedHandler(t *testing.T) {
tests := []struct {
label string
name string
want string
}{
{
label: "<EMPTY>",
name: "",
want: "Hello World!\n",
},
{
label: "<SET>",
name: "Testers",
want: "Hello Testers!\n",
},
}

for _, test := range tests {
req := httptest.NewRequest("GET", "/", strings.NewReader(""))
rr := httptest.NewRecorder()

os.Setenv("NAME", test.name)
improvedHandler(rr, req)

if code := rr.Result().StatusCode; code != http.StatusOK {
t.Errorf("brokenHandler(%s): got (%q), want (%q)", test.label, code, http.StatusOK)
}

if got := rr.Body.String(); test.want != got {
t.Errorf("brokenHandler(%s): got (%q), want (%q)", test.label, got, test.want)
}
}
}