From 81415839ba7df2aef1e5dd7e9e89f24d70c7eda1 Mon Sep 17 00:00:00 2001 From: kkHAIKE Date: Sun, 14 Apr 2024 04:35:25 +0800 Subject: [PATCH 1/4] Fix an issue where contexts are not copied correctly (#1941) Fix an issue in the Stasher where contexts between the request and log entry are not copied correctly. Co-authored-by: ouyangxu --- pkg/download/protocol.go | 4 ++-- pkg/download/protocol_test.go | 36 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/pkg/download/protocol.go b/pkg/download/protocol.go index b9d5945ee..a6942a9c7 100644 --- a/pkg/download/protocol.go +++ b/pkg/download/protocol.go @@ -298,7 +298,7 @@ func union(list1, list2 []string) []string { func copyContextWithCustomTimeout(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc) { ctxCopy, cancel := context.WithTimeout(context.Background(), timeout) - requestid.SetInContext(ctxCopy, requestid.FromContext(ctx)) - log.SetEntryInContext(ctxCopy, log.EntryFromContext(ctx)) + ctxCopy = requestid.SetInContext(ctxCopy, requestid.FromContext(ctx)) + ctxCopy = log.SetEntryInContext(ctxCopy, log.EntryFromContext(ctx)) return ctxCopy, cancel } diff --git a/pkg/download/protocol_test.go b/pkg/download/protocol_test.go index 40134a497..d2f910684 100644 --- a/pkg/download/protocol_test.go +++ b/pkg/download/protocol_test.go @@ -17,6 +17,7 @@ import ( "github.com/gomods/athens/pkg/download/mode" "github.com/gomods/athens/pkg/errors" "github.com/gomods/athens/pkg/index/nop" + "github.com/gomods/athens/pkg/log" "github.com/gomods/athens/pkg/module" "github.com/gomods/athens/pkg/stash" "github.com/gomods/athens/pkg/storage" @@ -495,3 +496,38 @@ func (ml *mockLister) List(ctx context.Context, mod string) (*storage.RevInfo, [ ml.called = true return nil, ml.list, ml.err } + +type testEntry struct { + msg string +} + +var _ log.Entry = &testEntry{} + +func (e *testEntry) Debugf(format string, args ...any) { + e.msg = format +} +func (*testEntry) Infof(format string, args ...any) {} +func (*testEntry) Warnf(format string, args ...any) {} +func (*testEntry) Errorf(format string, args ...any) {} +func (*testEntry) WithFields(fields map[string]any) log.Entry { return nil } +func (*testEntry) SystemErr(err error) {} + +func Test_copyContextWithCustomTimeout(t *testing.T) { + testEntry := &testEntry{} + + // create a context with a logger entry + logctx := log.SetEntryInContext(context.Background(), testEntry) + + // check the log work as expected + log.EntryFromContext(logctx).Debugf("first test") + require.Equal(t, "first test", testEntry.msg) + + // use copyContextWithCustomTimeout to create a new context with a custom timeout, + // and the returned context should have the same logger entry + newCtx, cancel := copyContextWithCustomTimeout(logctx, 10*time.Second) + defer cancel() + + // check the log work as expected + log.EntryFromContext(newCtx).Debugf("second test") + require.Equal(t, "second test", testEntry.msg) +} From f969e03904d682dfbcaccb3acd1831134a853037 Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 13 Apr 2024 23:42:47 +0200 Subject: [PATCH 2/4] Remove hard coded values from SystemD Unit and installation (#1874) Removes hardcoded values form our supplied SystemD Unit file and the installation script. Users, prior to installation, will need to ensure that their user, group, and permissions settings of where Athens stores data are correct. Co-authored-by: Manu Gupta Co-authored-by: Matt --- scripts/service/athens.service | 2 -- scripts/systemd.sh | 9 +++------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/scripts/service/athens.service b/scripts/service/athens.service index 06de46a39..7d39bb264 100644 --- a/scripts/service/athens.service +++ b/scripts/service/athens.service @@ -14,8 +14,6 @@ Nice=5 User=www-data Group=www-data -Environment=ATHENS_DISK_STORAGE_ROOT=/var/run/athens - ; The full path and the arguments of the command to be executed to start the process. ExecStart=/usr/local/bin/athens -config_file=/etc/athens/config.toml diff --git a/scripts/systemd.sh b/scripts/systemd.sh index 7bb4c2dd4..a877825e5 100755 --- a/scripts/systemd.sh +++ b/scripts/systemd.sh @@ -41,12 +41,6 @@ function doInstallConfig fi sudo mkdir -p /etc/athens sudo install -v -o root -g root -m 644 config.toml /etc/athens - - # if storage is on disk, this is where the database goes (see scripts/service/athens.service) - ATHENS_DISK_STORAGE_ROOT=/var/run/athens - sudo mkdir -p $ATHENS_DISK_STORAGE_ROOT - sudo chown www-data $ATHENS_DISK_STORAGE_ROOT - sudo chgrp www-data $ATHENS_DISK_STORAGE_ROOT } # doInstallBinary copies the Athens binary to /usr/local/bin with the necessary settings. @@ -69,6 +63,9 @@ function doInstallBinary # doInstallSystemd sets up the SystemD service unit. function doInstallSystemd { + local rootPath=$(sed -nr 's/(RootPath) = (".*")/\2/p' /etc/athens/config.toml | xargs) + sed -i "/ReadWritePaths/ s|=.*|=$rootPath|" scripts/service/athens.service + sudo install -v -o root -g root -m 644 scripts/service/athens.service /etc/systemd/system sudo systemctl daemon-reload sudo systemctl enable athens From 8d9b7676fd135aae8f18866275d6f5c0bef697ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 19:24:54 -0700 Subject: [PATCH 3/4] update-go-pkg(deps): bump github.com/aws/aws-sdk-go (#1942) Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.44.220 to 1.51.21. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.44.220...v1.51.21) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 11 ++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 36c0c7de6..10d8d0e99 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/Azure/azure-storage-blob-go v0.10.0 github.com/BurntSushi/toml v1.0.0 github.com/DataDog/opencensus-go-exporter-datadog v0.0.0-20180917103902-e6c7f767dc57 - github.com/aws/aws-sdk-go v1.44.220 + github.com/aws/aws-sdk-go v1.51.21 github.com/bsm/redislock v0.7.2 github.com/fatih/color v1.13.0 github.com/go-playground/validator/v10 v10.19.0 diff --git a/go.sum b/go.sum index cea8aeeac..178eb2a76 100644 --- a/go.sum +++ b/go.sum @@ -97,8 +97,8 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0= github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= -github.com/aws/aws-sdk-go v1.44.220 h1:yAj99qAt0Htjle9Up3DglgHfOP77lmFPrElA4jKnrBo= -github.com/aws/aws-sdk-go v1.44.220/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.51.21 h1:UrT6JC9R9PkYYXDZBV0qDKTualMr+bfK2eboTknMgbs= +github.com/aws/aws-sdk-go v1.51.21/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -441,8 +441,6 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= @@ -651,7 +649,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -696,7 +693,6 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -736,12 +732,10 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -751,7 +745,6 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 359c119441ec75baea05161932d66cc20822bae0 Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 20 Apr 2024 00:19:34 -0700 Subject: [PATCH 4/4] Add an introduction page as the home page (#1945) A default homepage is baked into the server that uses the request host address, or in HTTP 2 the authority. This includes ports. It also checks for schema. The values are used to indicate to users how to configure their go env Of course, this won't work on all installations - especially enterprise ones. For that, we've introduced ATHENS_HOME_TEMPLATE_PATH as an environment variable along with HomeTemplatePath in the config. This value defaults to /var/lib/athens/home.html but can be configured to any location that Athens can reliably read from. This is a Go HTML template so it should use Go HTML template formatting and logic. --- cmd/proxy/actions/app_proxy.go | 2 +- cmd/proxy/actions/app_proxy_test.go | 44 +++++-- cmd/proxy/actions/home.go | 132 +++++++++++++++++++- config.dev.toml | 4 + docs/content/configuration/home-template.md | 101 +++++++++++++++ pkg/config/config.go | 2 + pkg/config/config_test.go | 37 +++--- 7 files changed, 291 insertions(+), 31 deletions(-) create mode 100644 docs/content/configuration/home-template.md diff --git a/cmd/proxy/actions/app_proxy.go b/cmd/proxy/actions/app_proxy.go index 0b9cd00db..777ad4ca6 100644 --- a/cmd/proxy/actions/app_proxy.go +++ b/cmd/proxy/actions/app_proxy.go @@ -32,7 +32,7 @@ func addProxyRoutes( l *log.Logger, c *config.Config, ) error { - r.HandleFunc("/", proxyHomeHandler) + r.HandleFunc("/", proxyHomeHandler(c)) r.HandleFunc("/healthz", healthHandler) r.HandleFunc("/readyz", getReadinessHandler(s)) r.HandleFunc("/version", versionHandler) diff --git a/cmd/proxy/actions/app_proxy_test.go b/cmd/proxy/actions/app_proxy_test.go index 7b2b37415..735aea4dd 100644 --- a/cmd/proxy/actions/app_proxy_test.go +++ b/cmd/proxy/actions/app_proxy_test.go @@ -7,6 +7,7 @@ import ( "net/http/httptest" "strings" "testing" + "text/template" "github.com/gomods/athens/pkg/build" "github.com/gomods/athens/pkg/config" @@ -21,7 +22,7 @@ type routeTest struct { method string path string body string - test func(t *testing.T, resp *http.Response) + test func(t *testing.T, req *http.Request, resp *http.Response) } func TestProxyRoutes(t *testing.T) { @@ -40,22 +41,43 @@ func TestProxyRoutes(t *testing.T) { baseURL := "https://athens.azurefd.net" + c.PathPrefix testCases := []routeTest{ - {"GET", "/", "", func(t *testing.T, resp *http.Response) { + {"GET", "/", "", func(t *testing.T, req *http.Request, resp *http.Response) { assert.Equal(t, http.StatusOK, resp.StatusCode) body, err := io.ReadAll(resp.Body) require.NoError(t, err) - assert.Equal(t, `"Welcome to The Athens Proxy"`, string(body)) + tmp, err := template.New("home").Parse(homepage) + assert.NoError(t, err) + + var templateData = make(map[string]string) + + templateData["Host"] = req.Host + + if !strings.HasPrefix(templateData["Host"], "http://") && !strings.HasPrefix(templateData["Host"], "https://") { + if req.TLS != nil { + templateData["Host"] = "https://" + templateData["Host"] + } else { + templateData["Host"] = "http://" + templateData["Host"] + } + } + + templateData["NoSumPatterns"] = strings.Join(c.NoSumPatterns, ",") + + var expected strings.Builder + err = tmp.ExecuteTemplate(&expected, "home", templateData) + require.NoError(t, err) + + assert.Equal(t, expected.String(), string(body)) }}, - {"GET", "/badz", "", func(t *testing.T, resp *http.Response) { + {"GET", "/badz", "", func(t *testing.T, req *http.Request, resp *http.Response) { assert.Equal(t, http.StatusNotFound, resp.StatusCode) }}, - {"GET", "/healthz", "", func(t *testing.T, resp *http.Response) { + {"GET", "/healthz", "", func(t *testing.T, req *http.Request, resp *http.Response) { assert.Equal(t, http.StatusOK, resp.StatusCode) }}, - {"GET", "/readyz", "", func(t *testing.T, resp *http.Response) { + {"GET", "/readyz", "", func(t *testing.T, req *http.Request, resp *http.Response) { assert.Equal(t, http.StatusOK, resp.StatusCode) }}, - {"GET", "/version", "", func(t *testing.T, resp *http.Response) { + {"GET", "/version", "", func(t *testing.T, req *http.Request, resp *http.Response) { assert.Equal(t, http.StatusOK, resp.StatusCode) details := build.Details{} err := json.NewDecoder(resp.Body).Decode(&details) @@ -64,13 +86,13 @@ func TestProxyRoutes(t *testing.T) { }}, // Default sumdb is sum.golang.org - {"GET", "/sumdb/sum.golang.org/supported", "", func(t *testing.T, resp *http.Response) { + {"GET", "/sumdb/sum.golang.org/supported", "", func(t *testing.T, req *http.Request, resp *http.Response) { assert.Equal(t, http.StatusOK, resp.StatusCode) }}, - {"GET", "/sumdb/sum.rust-lang.org/supported", "", func(t *testing.T, resp *http.Response) { + {"GET", "/sumdb/sum.rust-lang.org/supported", "", func(t *testing.T, req *http.Request, resp *http.Response) { assert.Equal(t, http.StatusNotFound, resp.StatusCode) }}, - {"GET", "/sumdb/sum.golang.org/lookup/github.com/gomods/athens", "", func(t *testing.T, resp *http.Response) { + {"GET", "/sumdb/sum.golang.org/lookup/github.com/gomods/athens", "", func(t *testing.T, req *http.Request, resp *http.Response) { assert.Equal(t, http.StatusForbidden, resp.StatusCode) }}, } @@ -84,7 +106,7 @@ func TestProxyRoutes(t *testing.T) { t.Run(req.RequestURI, func(t *testing.T) { w := httptest.NewRecorder() r.ServeHTTP(w, req) - tc.test(t, w.Result()) + tc.test(t, req, w.Result()) }) } diff --git a/cmd/proxy/actions/home.go b/cmd/proxy/actions/home.go index 94fb545f2..8f41b979e 100644 --- a/cmd/proxy/actions/home.go +++ b/cmd/proxy/actions/home.go @@ -1,9 +1,137 @@ package actions import ( + "errors" + "html/template" "net/http" + "os" + "strings" + + "github.com/gomods/athens/pkg/config" + "github.com/gomods/athens/pkg/log" ) -func proxyHomeHandler(w http.ResponseWriter, r *http.Request) { - _, _ = w.Write([]byte(`"Welcome to The Athens Proxy"`)) +const homepage = ` + + + Athens + + + + +

Welcome to Athens

+ +

Configuring your client

+
GOPROXY={{ .Host }},direct
+ {{ if .NoSumPatterns }} +

Excluding checksum database

+

Use the following GONOSUM environment variable to exclude checksum database:

+
GONOSUM={{ .NoSumPatterns }}
+ {{ end }} + +

How to use the Athens API

+

Use the catalog endpoint to get a list of all modules in the proxy

+ +

List of versions

+

This endpoint returns a list of versions that Athens knows about for acidburn/htp:

+
GET {{ .Host }}/github.com/acidburn/htp/@v/list
+ +

Version info

+

This endpoint returns information about a specific version of a module:

+
GET {{ .Host }}/github.com/acidburn/htp/@v/v1.0.0.info
+

This returns JSON with information about v1.0.0. It looks like this: +

{
+	"Name": "v1.0.0",
+	"Short": "v1.0.0",
+	"Version": "v1.0.0",
+	"Time": "1972-07-18T12:34:56Z"
+}
+ +

go.mod file

+

This endpoint returns the go.mod file for a specific version of a module:

+
GET {{ .Host }}/github.com/acidburn/htp/@v/v1.0.0.mod
+

This returns the go.mod file for version v1.0.0. If {{ .Host }}/github.com/acidburn/htp version v1.0.0 has no dependencies, the response body would look like this:

+
module github.com/acidburn/htp
+ +

Module sources

+
GET {{ .Host }}/github.com/acidburn/htp/@v/v1.0.0.zip
+

This is what it sounds like — it sends back a zip file with the source code for the module in version v1.0.0.

+ +

Latest

+
GET {{ .Host }}/github.com/acidburn/htp/@latest
+

This endpoint returns the latest version of the module. If the version does not exist it should retrieve the hash of latest commit.

+ + + +` + +func proxyHomeHandler(c *config.Config) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + lggr := log.EntryFromContext(r.Context()) + + templateData := make(map[string]string) + + templateContents := homepage + + // load the template from the file system if it exists, otherwise revert to default + rawTemplateFileContents, err := os.ReadFile(c.HomeTemplatePath) + if err != nil { + if !errors.Is(err, os.ErrNotExist) { + // this is some other error, log it and revert to default + lggr.SystemErr(err) + } + } else { + templateContents = string(rawTemplateFileContents) + } + + // This should be correct in most cases. If it is not, users can supply their own template + templateData["Host"] = r.Host + + // if the host does not have a scheme, add one based on the request + if !strings.HasPrefix(templateData["Host"], "http://") && !strings.HasPrefix(templateData["Host"], "https://") { + if r.TLS != nil { + templateData["Host"] = "https://" + templateData["Host"] + } else { + templateData["Host"] = "http://" + templateData["Host"] + } + } + + templateData["NoSumPatterns"] = strings.Join(c.NoSumPatterns, ",") + + tmp, err := template.New("home").Parse(templateContents) + if err != nil { + lggr.SystemErr(err) + w.WriteHeader(http.StatusInternalServerError) + } + + w.Header().Add("Content-Type", "text/html") + w.WriteHeader(http.StatusOK) + + err = tmp.ExecuteTemplate(w, "home", templateData) + if err != nil { + lggr.SystemErr(err) + w.WriteHeader(http.StatusInternalServerError) + } + } } diff --git a/config.dev.toml b/config.dev.toml index 16f11779c..d04df2fea 100755 --- a/config.dev.toml +++ b/config.dev.toml @@ -169,6 +169,10 @@ BasicAuthUser = "" # Env override: BASIC_AUTH_PASS BasicAuthPass = "" +# A path on disk to a Go HTML template to be used on the homepage +# Env override: ATHENS_HOME_TEMPLATE_PATH +HomeTemplatePath = "/var/lib/athens/home.html" + # Set to true to force an SSL redirect # Env override: PROXY_FORCE_SSL ForceSSL = false diff --git a/docs/content/configuration/home-template.md b/docs/content/configuration/home-template.md new file mode 100644 index 000000000..cbb55a2a8 --- /dev/null +++ b/docs/content/configuration/home-template.md @@ -0,0 +1,101 @@ +--- +title: Home template configuration +description: How to customize the home template +weight: 8 +--- + +As of v0.14.0 Athens ships with a default, minimal HTML home page that advises users on how to connect to the proxy. It factors in whether `GoNoSumPatterns` is configured, and attempts +to build configuration for `GO_PROXY`. It relies on the users request Host header (on HTTP 1.1) or the Authority header (on HTTP 2) as well as whether the request was over TLS to advise +on configuring `GO_PROXY`. Lastly, the homepage provides a quick guide on how users can leverage the Athens API. + +Of course, not all instructions will be this simple. Some installations may be reachable at different addresses in CI than for desktop users. In this case, and others where the default +home page does not make sense it is possible to override the template. + +Do so by configuring `HomeTemplatePath` via the config or `ATHENS_HOME_TEMPLATE_PATH` to a location on disk with a Go HTML template or placing a template file at `/var/lib/athens/home.html`. + +Athens automatically injects the following variables in templates: + +| Setting | Source | +| :------ | :----- | +| `Host` | Built from the request Host (HTTP1) or Authority (HTTP2) header and presence of TLS. Includes ports. | +| `NoSumPatterns` | Comes directly from the configuration. | + +Using these values is done by wrapping them in bracers with a dot prepended. Example: `{{ .Host }}` + +For more advanced formatting read more about [Go HTML templates](https://pkg.go.dev/html/template). + +```html + + + + Athens + + + + +

Welcome to Athens

+ +

Configuring your client

+
GOPROXY={{ .Host }},direct
+ {{ if .NoSumPatterns }} +

Excluding checksum database

+

Use the following GONOSUM environment variable to exclude checksum database:

+
GONOSUM={{ .NoSumPatterns }}
+ {{ end }} + +

How to use the Athens API

+

Use the catalog endpoint to get a list of all modules in the proxy

+ +

List of versions

+

This endpoint returns a list of versions that Athens knows about for acidburn/htp:

+
GET {{ .Host }}/github.com/acidburn/htp/@v/list
+ +

Version info

+

This endpoint returns information about a specific version of a module:

+
GET {{ .Host }}/github.com/acidburn/htp/@v/v1.0.0.info
+

This returns JSON with information about v1.0.0. It looks like this: +

{
+	"Name": "v1.0.0",
+	"Short": "v1.0.0",
+	"Version": "v1.0.0",
+	"Time": "1972-07-18T12:34:56Z"
+}
+ +

go.mod file

+

This endpoint returns the go.mod file for a specific version of a module:

+
GET {{ .Host }}/github.com/acidburn/htp/@v/v1.0.0.mod
+

This returns the go.mod file for version v1.0.0. If {{ .Host }}/github.com/acidburn/htp version v1.0.0 has no dependencies, the response body would look like this:

+
module github.com/acidburn/htp
+ +

Module sources

+
GET {{ .Host }}/github.com/acidburn/htp/@v/v1.0.0.zip
+

This is what it sounds like — it sends back a zip file with the source code for the module in version v1.0.0.

+ +

Latest

+
GET {{ .Host }}/github.com/acidburn/htp/@latest
+

This endpoint returns the latest version of the module. If the version does not exist it should retrieve the hash of latest commit.

+ + + +``` \ No newline at end of file diff --git a/pkg/config/config.go b/pkg/config/config.go index 35aaccc1d..9f070e5b1 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -42,6 +42,7 @@ type Config struct { UnixSocket string `envconfig:"ATHENS_UNIX_SOCKET"` BasicAuthUser string `envconfig:"BASIC_AUTH_USER"` BasicAuthPass string `envconfig:"BASIC_AUTH_PASS"` + HomeTemplatePath string `envconfig:"ATHENS_HOME_TEMPLATE_PATH"` ForceSSL bool `envconfig:"PROXY_FORCE_SSL"` ValidatorHook string `envconfig:"ATHENS_PROXY_VALIDATOR"` PathPrefix string `envconfig:"ATHENS_PATH_PREFIX"` @@ -157,6 +158,7 @@ func defaultConfig() *Config { PprofPort: ":3001", StatsExporter: "prometheus", TimeoutConf: TimeoutConf{Timeout: 300}, + HomeTemplatePath: "/var/lib/athens/home.html", StorageType: "memory", Port: ":3000", SingleFlightType: "memory", diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index e10a2afbe..c2897b568 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -79,23 +79,24 @@ func TestEnvOverrides(t *testing.T) { TimeoutConf: TimeoutConf{ Timeout: 30, }, - StorageType: "minio", - GlobalEndpoint: "mytikas.gomods.io", - Port: ":7000", - EnablePprof: false, - PprofPort: ":3001", - BasicAuthUser: "testuser", - BasicAuthPass: "testpass", - ForceSSL: true, - ValidatorHook: "testhook.io", - PathPrefix: "prefix", - NETRCPath: "/test/path/.netrc", - HGRCPath: "/test/path/.hgrc", - Storage: &Storage{}, - GoBinaryEnvVars: []string{"GOPROXY=direct"}, - SingleFlight: &SingleFlight{}, - RobotsFile: "robots.txt", - Index: &Index{}, + StorageType: "minio", + GlobalEndpoint: "mytikas.gomods.io", + HomeTemplatePath: "/tmp/athens/home.html", + Port: ":7000", + EnablePprof: false, + PprofPort: ":3001", + BasicAuthUser: "testuser", + BasicAuthPass: "testpass", + ForceSSL: true, + ValidatorHook: "testhook.io", + PathPrefix: "prefix", + NETRCPath: "/test/path/.netrc", + HGRCPath: "/test/path/.hgrc", + Storage: &Storage{}, + GoBinaryEnvVars: []string{"GOPROXY=direct"}, + SingleFlight: &SingleFlight{}, + RobotsFile: "robots.txt", + Index: &Index{}, } envVars := getEnvMap(expConf) @@ -269,6 +270,7 @@ func TestParseExampleConfig(t *testing.T) { StorageType: "memory", NetworkMode: "strict", GlobalEndpoint: "http://localhost:3001", + HomeTemplatePath: "/var/lib/athens/home.html", Port: ":3000", EnablePprof: false, PprofPort: ":3001", @@ -322,6 +324,7 @@ func getEnvMap(config *Config) map[string]string { envVars["BASIC_AUTH_USER"] = config.BasicAuthUser envVars["BASIC_AUTH_PASS"] = config.BasicAuthPass envVars["PROXY_FORCE_SSL"] = strconv.FormatBool(config.ForceSSL) + envVars["ATHENS_HOME_TEMPLATE_PATH"] = config.HomeTemplatePath envVars["ATHENS_PROXY_VALIDATOR"] = config.ValidatorHook envVars["ATHENS_PATH_PREFIX"] = config.PathPrefix envVars["ATHENS_NETRC_PATH"] = config.NETRCPath