From 57dd1cca461ef52be40e838c4ca4c175a474e35c Mon Sep 17 00:00:00 2001 From: Paulo Janotti Date: Thu, 1 Nov 2018 13:19:30 -0700 Subject: [PATCH] Add docker scratch for agent and collector (#156) * Add docker from scratch linux for agent and collector * Fix flaky test that depended on timing --- .gitignore | 5 ++++ Makefile | 28 ++++++++++++++---- cmd/ocagent/Dockerfile | 9 ++++++ cmd/ocagent/config.go | 2 +- cmd/ocagent/main.go | 18 ++++++------ cmd/occollector/Dockerfile | 4 +++ cmd/occollector/main.go | 9 ++++-- exporter/exporterparser/zipkin_test.go | 40 +++++--------------------- 8 files changed, 66 insertions(+), 49 deletions(-) create mode 100644 cmd/ocagent/Dockerfile create mode 100644 cmd/occollector/Dockerfile diff --git a/.gitignore b/.gitignore index 67ab17349d..f0e166e095 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,8 @@ bin/ # Miscellaneous files *.sw[op] *.DS_Store + +# Binaries are copied (as needed) to the same location as their respective Dockerfile in +# order to simplify docker build commands. Ignore these files if proper clean up fails. +cmd/ocagent/ocagent_linux +cmd/occollector/occollector_linux diff --git a/Makefile b/Makefile index 33ad6d40a2..06bf3db252 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ ALL_SRC := $(shell find . -type f -name '*.go' -not -path "./vendor/*") -GOTEST_OPT=-v -race +GOTEST_OPT?=-v -race GOTEST=go test GOFMT=gofmt GOOS=$(shell go env GOOS) @@ -14,10 +14,6 @@ BUILD_INFO=-ldflags "-X $(BUILD_INFO_IMPORT_PATH).GitHash=$(GIT_SHA)" .PHONY: default_goal default_goal: fmt test -.PHONY: clean -clean: - rm -rf bin/ - .PHONY: test test: $(GOTEST) $(GOTEST_OPT) ./... @@ -39,6 +35,28 @@ agent: collector: CGO_ENABLED=0 go build -o ./bin/occollector_$(GOOS) $(BUILD_INFO) ./cmd/occollector +.PHONY: docker-component # Not intended to be used directly +docker-component: check-component + GOOS=linux $(MAKE) $(COMPONENT) + cp ./bin/oc$(COMPONENT)_linux ./cmd/oc$(COMPONENT)/ + docker build -t oc$(COMPONENT) ./cmd/oc$(COMPONENT)/ + rm ./cmd/oc$(COMPONENT)/oc$(COMPONENT)_linux + +.PHONY: check-component +check-component: +ifndef COMPONENT + $(error COMPONENT variable was not defined) +endif + +.PHONY: docker-agent +docker-agent: + COMPONENT=agent $(MAKE) docker-component + +.PHONY: docker-collector +docker-collector: + COMPONENT=collector $(MAKE) docker-component + + .PHONY: binaries binaries: agent collector diff --git a/cmd/ocagent/Dockerfile b/cmd/ocagent/Dockerfile new file mode 100644 index 0000000000..0a8efab72b --- /dev/null +++ b/cmd/ocagent/Dockerfile @@ -0,0 +1,9 @@ +# Use a helper to create an emtpy configuration since the agent requires such file +FROM alpine:3.7 as helper +RUN touch ./config.yaml + +FROM scratch +COPY ocagent_linux / +COPY --from=helper ./config.yaml config.yaml +ENTRYPOINT ["/ocagent_linux"] +EXPOSE 55678 55679 diff --git a/cmd/ocagent/config.go b/cmd/ocagent/config.go index c21e2eea2e..16625b9fbe 100644 --- a/cmd/ocagent/config.go +++ b/cmd/ocagent/config.go @@ -47,7 +47,7 @@ import ( // port: 55679 const ( - defaultOCInterceptorAddress = "localhost:55678" + defaultOCInterceptorAddress = ":55678" defaultZPagesPort = 55679 ) diff --git a/cmd/ocagent/main.go b/cmd/ocagent/main.go index f45aaf3517..71d4fc6e1c 100644 --- a/cmd/ocagent/main.go +++ b/cmd/ocagent/main.go @@ -121,14 +121,15 @@ func runZPages(port int) func() error { log.Fatalf("Failed to bind to run zPages on %q: %v", addr, err) } + srv := http.Server{Handler: zPagesMux} go func() { log.Printf("Running zPages at %q", addr) - if err := http.Serve(ln, zPagesMux); err != nil { + if err := srv.Serve(ln); err != nil && err != http.ErrServerClosed { log.Fatalf("Failed to serve zPages: %v", err) } }() - return ln.Close + return srv.Close } func runOCInterceptor(addr string, sr spanreceiver.SpanReceiver) (doneFn func() error, err error) { @@ -152,13 +153,14 @@ func runOCInterceptor(addr string, sr spanreceiver.SpanReceiver) (doneFn func() agenttracepb.RegisterTraceServiceServer(srv, oci) go func() { log.Printf("Running OpenCensus interceptor as a gRPC service at %q", addr) - - // Not using log.Fatalf(srv.Serve(ln)) because CTRL-C will close the listener as the user requested - // and that log.Fatalf will produce an unrelated but misleading error: - // "Failed to run OpenCensus interceptor: accept tcp 127.0.0.1:55678: use of closed network connection" - _ = srv.Serve(ln) + if err := srv.Serve(ln); err != nil { + log.Fatalf("Failed to run OpenCensus interceptor: %v", err) + } }() - doneFn = ln.Close + doneFn = func() error { + srv.Stop() + return nil + } return doneFn, nil } diff --git a/cmd/occollector/Dockerfile b/cmd/occollector/Dockerfile new file mode 100644 index 0000000000..1eeaa29034 --- /dev/null +++ b/cmd/occollector/Dockerfile @@ -0,0 +1,4 @@ +FROM scratch +COPY occollector_linux / +ENTRYPOINT ["/occollector_linux"] +EXPOSE 55678 diff --git a/cmd/occollector/main.go b/cmd/occollector/main.go index 3287995c43..3e8cbe5d0a 100644 --- a/cmd/occollector/main.go +++ b/cmd/occollector/main.go @@ -42,7 +42,7 @@ import ( ) const ( - defaultOCAddress = "localhost:55678" + defaultOCAddress = ":55678" ) func main() { @@ -103,7 +103,12 @@ func runOCServerWithInterceptor(addr string, logger *zap.Logger) (func() error, } }() - return lis.Close, nil + closeFn := func() error { + grpcSrv.Stop() + return nil + } + + return closeFn, nil } type fakeSpanReceiver struct { diff --git a/exporter/exporterparser/zipkin_test.go b/exporter/exporterparser/zipkin_test.go index 6c5687d708..80a5f0a174 100644 --- a/exporter/exporterparser/zipkin_test.go +++ b/exporter/exporterparser/zipkin_test.go @@ -21,9 +21,7 @@ import ( "net/http" "net/http/httptest" "strings" - "sync" "testing" - "time" "github.com/census-instrumentation/opencensus-service/exporter" "github.com/census-instrumentation/opencensus-service/exporter/exporterparser" @@ -41,10 +39,12 @@ import ( // // The rest of the fields should match up exactly func TestZipkinExportersFromYAML_roundtripJSON(t *testing.T) { - cb := newConcurrentBuffer() + responseReady := make(chan bool) + buf := new(bytes.Buffer) cst := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - io.Copy(cb, r.Body) + io.Copy(buf, r.Body) r.Body.Close() + responseReady <- true })) defer cst.Close() @@ -79,9 +79,8 @@ exporters: req, _ := http.NewRequest("POST", "https://tld.org/", strings.NewReader(zipkinSpansJSONJavaLibrary)) responseWriter := httptest.NewRecorder() zi.ServeHTTP(responseWriter, req) - // Just for 100X longer than the Zipkin reporting period - // because -race slows things down a lot. - <-time.After(100 * time.Millisecond) + // Wait for the server to write the response. + <-responseReady // We expect back the exact JSON that was intercepted want := testutils.GenerateNormalizedJSON(` @@ -111,38 +110,13 @@ exporters: }]`) // Finally we need to inspect the output - gotBytes, _ := ioutil.ReadAll(cb) + gotBytes, _ := ioutil.ReadAll(buf) got := testutils.GenerateNormalizedJSON(string(gotBytes)) if got != want { t.Errorf("RoundTrip result do not match:\nGot\n %s\n\nWant\n: %s\n", got, want) } } -type concurrentBuffer struct { - mu sync.Mutex - buf *bytes.Buffer -} - -func newConcurrentBuffer() *concurrentBuffer { - return &concurrentBuffer{ - buf: new(bytes.Buffer), - } -} - -func (cb *concurrentBuffer) Write(b []byte) (int, error) { - cb.mu.Lock() - defer cb.mu.Unlock() - - return cb.buf.Write(b) -} - -func (cb *concurrentBuffer) Read(b []byte) (int, error) { - cb.mu.Lock() - defer cb.mu.Unlock() - - return cb.buf.Read(b) -} - const zipkinSpansJSONJavaLibrary = ` [{ "traceId": "4d1e00c0db9010db86154a4ba6e91385",