From d840dd1f525094dfa1b9ade2aa5bb3f35022dd97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Mart=C3=ADnez=20Trivi=C3=B1o?= Date: Thu, 8 Nov 2018 17:33:30 -0800 Subject: [PATCH] Improve user agent and version handling in chart-repo command (#558) --- .circleci/config.yml | 8 ++++++- cmd/chart-repo/Dockerfile | 4 +++- cmd/chart-repo/Makefile | 4 +++- cmd/chart-repo/chart_repo.go | 5 ++++- cmd/chart-repo/utils.go | 8 ++++--- cmd/chart-repo/utils_test.go | 43 ++++++++++++++++++++++++++++++++---- cmd/chart-repo/version.go | 34 +++++++++++++++++++++++++--- 7 files changed, 92 insertions(+), 14 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e5e625cd8..fe9454044 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -110,7 +110,13 @@ jobs: - run: | CMDS=(chart-repo chartsvc) for CMD in ${CMDS[@]}; do - make -C cmd/${CMD} docker-build + # If we are in a release we pass the version to the build process to bake the version + # By default, if no passed it will use the commit sha + if [[ -n "${CIRCLE_TAG}" ]]; then + makeArgs="VERSION=${CIRCLE_TAG}" + fi + + make -C cmd/${CMD} $makeArgs docker-build done if [[ -z "${CIRCLE_PULL_REQUEST}" && -n "${DOCKER_USERNAME}" && -n "${DOCKER_PASSWORD}" ]] && [[ "${CIRCLE_BRANCH}" == "master" || -n "${CIRCLE_TAG}" ]]; then diff --git a/cmd/chart-repo/Dockerfile b/cmd/chart-repo/Dockerfile index b429cd117..b48b8bdf9 100644 --- a/cmd/chart-repo/Dockerfile +++ b/cmd/chart-repo/Dockerfile @@ -1,7 +1,9 @@ FROM golang:1.11 as builder COPY . /go/src/github.com/helm/monocular WORKDIR /go/src/github.com/helm/monocular -RUN CGO_ENABLED=0 go build -a -installsuffix cgo ./cmd/chart-repo + +ARG VERSION +RUN CGO_ENABLED=0 go build -a -installsuffix cgo -ldflags "-X main.version=$VERSION" ./cmd/chart-repo FROM scratch COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ diff --git a/cmd/chart-repo/Makefile b/cmd/chart-repo/Makefile index 83a8c8af1..34ec0f9d1 100644 --- a/cmd/chart-repo/Makefile +++ b/cmd/chart-repo/Makefile @@ -1,6 +1,8 @@ IMAGE_REPO ?= quay.io/helmpack/chart-repo IMAGE_TAG ?= latest +# Version of the binary to be produced +VERSION ?= $$(git rev-parse HEAD) docker-build: # We use the context of the root dir - docker build --pull --rm -t ${IMAGE_REPO}:${IMAGE_TAG} -f Dockerfile ../../ + docker build --pull --rm -t ${IMAGE_REPO}:${IMAGE_TAG} --build-arg "VERSION=${VERSION}" -f Dockerfile ../../ diff --git a/cmd/chart-repo/chart_repo.go b/cmd/chart-repo/chart_repo.go index 8887f26ce..a61b31784 100644 --- a/cmd/chart-repo/chart_repo.go +++ b/cmd/chart-repo/chart_repo.go @@ -24,7 +24,7 @@ import ( var rootCmd = &cobra.Command{ Use: "chart-repo", - Short: "Kubeapps Chart Repository utility", + Short: "Chart Repository utility", Run: func(cmd *cobra.Command, args []string) { cmd.Help() }, @@ -45,6 +45,9 @@ func init() { cmd.Flags().String("mongo-url", "localhost", "MongoDB URL (see https://godoc.org/github.com/globalsign/mgo#Dial for format)") cmd.Flags().String("mongo-database", "charts", "MongoDB database") cmd.Flags().String("mongo-user", "", "MongoDB user") + // see version.go + cmd.Flags().StringVarP(&userAgentComment, "user-agent-comment", "", "", "UserAgent comment used during outbound requests") cmd.Flags().Bool("debug", false, "verbose logging") } + rootCmd.AddCommand(versionCmd) } diff --git a/cmd/chart-repo/utils.go b/cmd/chart-repo/utils.go index 9bacd4f22..57faaf55b 100644 --- a/cmd/chart-repo/utils.go +++ b/cmd/chart-repo/utils.go @@ -166,10 +166,12 @@ func fetchRepoIndex(r repo) (*helmrepo.IndexFile, error) { log.WithFields(log.Fields{"url": req.URL.String()}).WithError(err).Error("could not build repo index request") return nil, err } - req.Header.Set("User-Agent", userAgent) + + req.Header.Set("User-Agent", userAgent()) if len(r.AuthorizationHeader) > 0 { req.Header.Set("Authorization", r.AuthorizationHeader) } + res, err := netClient.Do(req) if res != nil { defer res.Body.Close() @@ -278,7 +280,7 @@ func fetchAndImportIcon(dbSession datastore.Session, c chart) error { if err != nil { return err } - req.Header.Set("User-Agent", userAgent) + req.Header.Set("User-Agent", userAgent()) if len(c.Repo.AuthorizationHeader) > 0 { req.Header.Set("Authorization", c.Repo.AuthorizationHeader) } @@ -328,7 +330,7 @@ func fetchAndImportFiles(dbSession datastore.Session, name string, r repo, cv ch if err != nil { return err } - req.Header.Set("User-Agent", userAgent) + req.Header.Set("User-Agent", userAgent()) if len(r.AuthorizationHeader) > 0 { req.Header.Set("Authorization", r.AuthorizationHeader) } diff --git a/cmd/chart-repo/utils_test.go b/cmd/chart-repo/utils_test.go index 66c9a3b81..8495a533d 100644 --- a/cmd/chart-repo/utils_test.go +++ b/cmd/chart-repo/utils_test.go @@ -66,10 +66,7 @@ func (h *goodHTTPClient) Do(req *http.Request) (*http.Response, error) { if req.URL.Host == "subpath.test" && req.URL.Path != "/subpath/index.yaml" { w.WriteHeader(500) } - // Ensure we're sending the right User-Agent - if !strings.Contains(req.Header.Get("User-Agent"), "chart-repo-sync") { - w.WriteHeader(500) - } + w.Write([]byte(validRepoIndexYAML)) return w.Result(), nil } @@ -206,6 +203,44 @@ func Test_fetchRepoIndex(t *testing.T) { }) } +func Test_fetchRepoIndexUserAgent(t *testing.T) { + tests := []struct { + name string + version string + userAgentComment string + expectedUserAgent string + }{ + {"default user agent", "", "", "chart-repo/devel"}, + {"custom version no app", "1.0", "", "chart-repo/1.0"}, + {"custom version and app", "1.0", "monocular/1.2", "chart-repo/1.0 (monocular/1.2)"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Override global variables used to generate the userAgent + if tt.version != "" { + version = tt.version + } + + if tt.userAgentComment != "" { + userAgentComment = tt.userAgentComment + } + + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + assert.Equal(t, tt.expectedUserAgent, req.Header.Get("User-Agent"), "expected user agent") + rw.Write([]byte(validRepoIndexYAML)) + })) + // Close the server when test finishes + defer server.Close() + + netClient = server.Client() + + _, err := fetchRepoIndex(repo{URL: server.URL}) + assert.NoErr(t, err) + }) + } +} + func Test_parseRepoIndex(t *testing.T) { tests := []struct { name string diff --git a/cmd/chart-repo/version.go b/cmd/chart-repo/version.go index c26242c56..52c38d76d 100644 --- a/cmd/chart-repo/version.go +++ b/cmd/chart-repo/version.go @@ -16,7 +16,35 @@ limitations under the License. package main -const ( - version = "0.3.0" - userAgent = "chart-repo-sync/" + version +import ( + "fmt" + + "github.com/spf13/cobra" +) + +var ( + version = "devel" + userAgentComment string ) + +// Returns the user agent to be used during calls to the chart repositories +// Examples: +// chart-repo/devel +// chart-repo/1.0 +// chart-repo/1.0 (monocular v1.0-beta4) +// More info here https://github.com/kubeapps/kubeapps/issues/767#issuecomment-436835938 +func userAgent() string { + ua := "chart-repo/" + version + if userAgentComment != "" { + ua = fmt.Sprintf("%s (%s)", ua, userAgentComment) + } + return ua +} + +var versionCmd = &cobra.Command{ + Use: "version", + Short: "returns version information", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println(version) + }, +}