-
Notifications
You must be signed in to change notification settings - Fork 1.8k
run: add hello-broken sample #981
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
00a633c
f0c0ad6
73325a9
f28d0cb
b292f2c
4e22570
411430b
c29fca9
17463cf
1691553
a6c32a0
9fafc5f
bbdbef8
d0032f5
61ff2e9
a830635
a8a6e2d
6a8711a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| vendor/ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| # 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] | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 build artifact. | ||
grayside marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| # This is based on Debian and sets the GOPATH to /go. | ||
| # https://hub.docker.com/_/golang | ||
| FROM golang:1.13 as builder | ||
|
|
||
| # Create and change to the app directory. | ||
| WORKDIR /app | ||
|
|
||
| # Retrieve application dependencies. | ||
| # This allows the container build to reuse cached dependencies. | ||
| COPY go.* ./ | ||
grayside marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| RUN go mod download | ||
|
|
||
| # Copy local code to the container image. | ||
| COPY . ./ | ||
|
|
||
| # Build the binary. | ||
| RUN CGO_ENABLED=0 GOOS=linux go build -mod=readonly -v -o server | ||
|
||
|
|
||
| # Use the official Alpine image for a lean production container. | ||
| # https://hub.docker.com/_/alpine | ||
| # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds | ||
| FROM alpine:3 | ||
| RUN apk add --no-cache ca-certificates | ||
grayside marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| # 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] | ||
| 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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| // 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-broken demonstrates a difficult to troubleshoot service. | ||
| package main | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "log" | ||
| "net/http" | ||
| "os" | ||
| ) | ||
|
|
||
| func main() { | ||
| log.Print("hello-broken: service started") | ||
|
|
||
| http.HandleFunc("/", brokenHandler) | ||
|
|
||
grayside marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // [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 brokenHandler(w http.ResponseWriter, r *http.Request) { | ||
| log.Print("hello-broken: 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") | ||
grayside marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| // [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-broken: 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) | ||
| } | ||
| 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", "") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I might be missing something, but why is this needed?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.