v1.0.3 — ревью Соннета: SSRF fix, go.sum, readyz, sort, graceful shutdown, gofmt
This commit is contained in:
+3
-1
@@ -6,8 +6,10 @@
|
|||||||
FROM golang:1.24-alpine AS builder
|
FROM golang:1.24-alpine AS builder
|
||||||
ENV GOTOOLCHAIN=auto
|
ENV GOTOOLCHAIN=auto
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
COPY server/go.mod server/go.sum ./
|
||||||
|
RUN go mod download
|
||||||
COPY server/ .
|
COPY server/ .
|
||||||
RUN go mod init tf-registry && go mod tidy && CGO_ENABLED=0 go build -o registry .
|
RUN CGO_ENABLED=0 go build -o registry .
|
||||||
|
|
||||||
FROM alpine:3.21
|
FROM alpine:3.21
|
||||||
RUN apk --no-cache add ca-certificates
|
RUN apk --no-cache add ca-certificates
|
||||||
|
|||||||
+77
-77
@@ -16,101 +16,101 @@ import (
|
|||||||
// parseDocsRequestPath parses paths like:
|
// parseDocsRequestPath parses paths like:
|
||||||
// /docs/<namespace>/<name>/<version>/... (rest may be empty)
|
// /docs/<namespace>/<name>/<version>/... (rest may be empty)
|
||||||
func parseDocsRequestPath(p string) (namespace, name, version, rest string, err error) {
|
func parseDocsRequestPath(p string) (namespace, name, version, rest string, err error) {
|
||||||
p = strings.TrimPrefix(p, "/")
|
p = strings.TrimPrefix(p, "/")
|
||||||
p = strings.TrimPrefix(p, "docs/")
|
p = strings.TrimPrefix(p, "docs/")
|
||||||
parts := strings.SplitN(p, "/", 4)
|
parts := strings.SplitN(p, "/", 4)
|
||||||
if len(parts) < 3 {
|
if len(parts) < 3 {
|
||||||
err = fmt.Errorf("invalid docs path: %s", p)
|
err = fmt.Errorf("invalid docs path: %s", p)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
namespace = parts[0]
|
namespace = parts[0]
|
||||||
name = parts[1]
|
name = parts[1]
|
||||||
version = parts[2]
|
version = parts[2]
|
||||||
if len(parts) == 3 {
|
if len(parts) == 3 {
|
||||||
rest = ""
|
rest = ""
|
||||||
} else {
|
} else {
|
||||||
rest = parts[3]
|
rest = parts[3]
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// docsObjectKey builds the S3 key for a docs object given parsed parts.
|
// docsObjectKey builds the S3 key for a docs object given parsed parts.
|
||||||
func docsObjectKey(namespace, name, version, pathPart string) string {
|
func docsObjectKey(namespace, name, version, pathPart string) string {
|
||||||
clean := strings.TrimPrefix(pathPart, "/")
|
clean := strings.TrimPrefix(pathPart, "/")
|
||||||
if clean == "" {
|
if clean == "" {
|
||||||
return fmt.Sprintf("docs/%s/%s/%s/index.html", namespace, name, version)
|
return fmt.Sprintf("docs/%s/%s/%s/index.html", namespace, name, version)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("docs/%s/%s/%s/%s", namespace, name, version, clean)
|
return fmt.Sprintf("docs/%s/%s/%s/%s", namespace, name, version, clean)
|
||||||
}
|
}
|
||||||
|
|
||||||
// tryCandidateKeys returns a list of keys to attempt for a given request path.
|
// tryCandidateKeys returns a list of keys to attempt for a given request path.
|
||||||
// Order matters: exact path first, then <path>/index.html, then top-level index.
|
// Order matters: exact path first, then <path>/index.html, then top-level index.
|
||||||
func tryCandidateKeys(namespace, name, version, rest string) []string {
|
func tryCandidateKeys(namespace, name, version, rest string) []string {
|
||||||
keys := []string{}
|
keys := []string{}
|
||||||
if rest == "" {
|
if rest == "" {
|
||||||
keys = append(keys, docsObjectKey(namespace, name, version, "index.html"))
|
keys = append(keys, docsObjectKey(namespace, name, version, "index.html"))
|
||||||
return keys
|
return keys
|
||||||
}
|
}
|
||||||
// exact
|
// exact
|
||||||
keys = append(keys, docsObjectKey(namespace, name, version, rest))
|
keys = append(keys, docsObjectKey(namespace, name, version, rest))
|
||||||
// if it looks like a directory or has no extension, try index under it
|
// if it looks like a directory or has no extension, try index under it
|
||||||
if strings.HasSuffix(rest, "/") || filepath.Ext(rest) == "" {
|
if strings.HasSuffix(rest, "/") || filepath.Ext(rest) == "" {
|
||||||
keys = append(keys, docsObjectKey(namespace, name, version, strings.TrimSuffix(rest, "/")+"/index.html"))
|
keys = append(keys, docsObjectKey(namespace, name, version, strings.TrimSuffix(rest, "/")+"/index.html"))
|
||||||
}
|
}
|
||||||
// finally, try root index
|
// finally, try root index
|
||||||
keys = append(keys, docsObjectKey(namespace, name, version, "index.html"))
|
keys = append(keys, docsObjectKey(namespace, name, version, "index.html"))
|
||||||
return keys
|
return keys
|
||||||
}
|
}
|
||||||
|
|
||||||
// docsHandler serves static documentation files from S3 (public-facing via Ingress).
|
// docsHandler serves static documentation files from S3 (public-facing via Ingress).
|
||||||
func docsHandler(w http.ResponseWriter, r *http.Request) {
|
func docsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ns, name, ver, rest, err := parseDocsRequestPath(r.URL.Path)
|
ns, name, ver, rest, err := parseDocsRequestPath(r.URL.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Bad docs path", http.StatusBadRequest)
|
http.Error(w, "Bad docs path", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
candidates := tryCandidateKeys(ns, name, ver, rest)
|
candidates := tryCandidateKeys(ns, name, ver, rest)
|
||||||
log.Printf("Docs candidates (host=%s): %v", hostname, candidates)
|
log.Printf("Docs candidates (host=%s): %v", hostname, candidates)
|
||||||
|
|
||||||
var lastErr error
|
var lastErr error
|
||||||
for _, key := range candidates {
|
for _, key := range candidates {
|
||||||
obj, err := s3Client.GetObject(ctx, bucketName, key, s3.GetObjectOptions{})
|
obj, err := s3Client.GetObject(ctx, bucketName, key, s3.GetObjectOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lastErr = err
|
lastErr = err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
stat, err := obj.Stat()
|
stat, err := obj.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lastErr = err
|
lastErr = err
|
||||||
_ = obj.Close()
|
_ = obj.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine content-type
|
// Determine content-type
|
||||||
ext := filepath.Ext(key)
|
ext := filepath.Ext(key)
|
||||||
ctype := mime.TypeByExtension(ext)
|
ctype := mime.TypeByExtension(ext)
|
||||||
if ctype == "" {
|
if ctype == "" {
|
||||||
// fallback for HTML
|
// fallback for HTML
|
||||||
if ext == ".html" || strings.HasSuffix(key, "index.html") {
|
if ext == ".html" || strings.HasSuffix(key, "index.html") {
|
||||||
ctype = "text/html; charset=utf-8"
|
ctype = "text/html; charset=utf-8"
|
||||||
} else {
|
} else {
|
||||||
ctype = "application/octet-stream"
|
ctype = "application/octet-stream"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", ctype)
|
w.Header().Set("Content-Type", ctype)
|
||||||
w.Header().Set("Content-Length", fmt.Sprintf("%d", stat.Size))
|
w.Header().Set("Content-Length", fmt.Sprintf("%d", stat.Size))
|
||||||
w.Header().Set("Last-Modified", stat.LastModified.Format(http.TimeFormat))
|
w.Header().Set("Last-Modified", stat.LastModified.Format(http.TimeFormat))
|
||||||
|
|
||||||
if _, err := io.Copy(w, obj); err != nil {
|
if _, err := io.Copy(w, obj); err != nil {
|
||||||
log.Printf("Error streaming object %s: %v", key, err)
|
log.Printf("Error streaming object %s: %v", key, err)
|
||||||
}
|
}
|
||||||
_ = obj.Close()
|
_ = obj.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Docs not found in candidates: %v, lastErr: %v", candidates, lastErr)
|
log.Printf("Docs not found in candidates: %v, lastErr: %v", candidates, lastErr)
|
||||||
http.Error(w, "Documentation not found", http.StatusNotFound)
|
http.Error(w, "Documentation not found", http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
module tf-registry
|
||||||
|
|
||||||
|
go 1.25.0
|
||||||
|
|
||||||
|
require github.com/minio/minio-go/v7 v7.2.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.18.6 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.11 // indirect
|
||||||
|
github.com/klauspost/crc32 v1.3.0 // indirect
|
||||||
|
github.com/minio/crc64nvme v1.1.1 // indirect
|
||||||
|
github.com/minio/md5-simd v1.1.2 // indirect
|
||||||
|
github.com/philhofer/fwd v1.2.0 // indirect
|
||||||
|
github.com/rs/xid v1.6.0 // indirect
|
||||||
|
github.com/tinylib/msgp v1.6.1 // indirect
|
||||||
|
github.com/zeebo/xxh3 v1.1.0 // indirect
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
|
golang.org/x/crypto v0.51.0 // indirect
|
||||||
|
golang.org/x/net v0.53.0 // indirect
|
||||||
|
golang.org/x/sys v0.44.0 // indirect
|
||||||
|
golang.org/x/text v0.37.0 // indirect
|
||||||
|
gopkg.in/ini.v1 v1.67.2 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
|
||||||
|
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
|
github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM=
|
||||||
|
github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw=
|
||||||
|
github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI=
|
||||||
|
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
||||||
|
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||||
|
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||||
|
github.com/minio/minio-go/v7 v7.2.1 h1:PfBfwvKB/MmqyN8Vb1G9voWisaM9OrLv+WwOvMwS9Dw=
|
||||||
|
github.com/minio/minio-go/v7 v7.2.1/go.mod h1:EU9hENAStx/xXduNdrGO5e4X5vk19NtgB+RIPjZO8o0=
|
||||||
|
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
||||||
|
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||||
|
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
github.com/tinylib/msgp v1.6.1 h1:ESRv8eL3u+DNHUoSAAQRE50Hm162zqAnBoGv9PzScPY=
|
||||||
|
github.com/tinylib/msgp v1.6.1/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
|
||||||
|
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||||
|
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||||
|
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
|
||||||
|
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
|
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
|
||||||
|
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
|
||||||
|
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
|
||||||
|
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
|
||||||
|
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
||||||
|
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
|
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||||
|
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/ini.v1 v1.67.2 h1:JtOSMb9OuaCZKr7h5D/h6iii14sK0hLbplTc6frx4Ss=
|
||||||
|
gopkg.in/ini.v1 v1.67.2/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
+40
-8
@@ -10,7 +10,11 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
s3 "github.com/minio/minio-go/v7"
|
s3 "github.com/minio/minio-go/v7"
|
||||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||||
@@ -23,7 +27,7 @@ var (
|
|||||||
s3Prefix = os.Getenv("S3_PREFIX") // S3 key prefix (may differ from hostname)
|
s3Prefix = os.Getenv("S3_PREFIX") // S3 key prefix (may differ from hostname)
|
||||||
)
|
)
|
||||||
|
|
||||||
const VERSION = "1.0.2"
|
const VERSION = "1.0.3"
|
||||||
|
|
||||||
// Terraform Registry Protocol Structs
|
// Terraform Registry Protocol Structs
|
||||||
type Discovery struct {
|
type Discovery struct {
|
||||||
@@ -108,8 +112,27 @@ func main() {
|
|||||||
if port == "" {
|
if port == "" {
|
||||||
port = "5000"
|
port = "5000"
|
||||||
}
|
}
|
||||||
log.Printf("Starting Registry Service on :%s (Bucket: %s, Endpoint: %s)\n", port, bucketName, endpoint)
|
|
||||||
log.Fatal(http.ListenAndServe(":"+port, nil))
|
srv := &http.Server{Addr: ":" + port}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
log.Printf("Starting Registry Service on :%s (Bucket: %s, Endpoint: %s)\n", port, bucketName, endpoint)
|
||||||
|
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
|
||||||
|
log.Fatalf("HTTP server error: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
quit := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
<-quit
|
||||||
|
log.Println("Shutting down server...")
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
if err := srv.Shutdown(ctx); err != nil {
|
||||||
|
log.Fatalf("Server forced to shutdown: %v", err)
|
||||||
|
}
|
||||||
|
log.Println("Server stopped")
|
||||||
}
|
}
|
||||||
|
|
||||||
func rootHandler(w http.ResponseWriter, r *http.Request) {
|
func rootHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -133,15 +156,14 @@ func rootHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func proxyHandler(w http.ResponseWriter, r *http.Request) {
|
func proxyHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
bucket := r.URL.Query().Get("bucket")
|
|
||||||
key := r.URL.Query().Get("key")
|
key := r.URL.Query().Get("key")
|
||||||
|
|
||||||
if bucket == "" || key == "" {
|
if key == "" {
|
||||||
http.Error(w, "Missing bucket or key params", http.StatusBadRequest)
|
http.Error(w, "Missing key param", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
obj, err := s3Client.GetObject(context.Background(), bucket, key, s3.GetObjectOptions{})
|
obj, err := s3Client.GetObject(context.Background(), bucketName, key, s3.GetObjectOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error getting object %s/%s: %v", bucket, key, err)
|
log.Printf("Error getting object %s/%s: %v", bucket, key, err)
|
||||||
http.Error(w, "File not found", http.StatusNotFound)
|
http.Error(w, "File not found", http.StatusNotFound)
|
||||||
@@ -232,6 +254,10 @@ func listVersions(w http.ResponseWriter, r *http.Request, namespace, pType strin
|
|||||||
versions = append(versions, *v)
|
versions = append(versions, *v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sort.Slice(versions, func(i, j int) bool {
|
||||||
|
return versions[i].Version < versions[j].Version
|
||||||
|
})
|
||||||
|
|
||||||
resp := VersionList{
|
resp := VersionList{
|
||||||
ID: fmt.Sprintf("%s/%s", namespace, pType),
|
ID: fmt.Sprintf("%s/%s", namespace, pType),
|
||||||
Versions: versions,
|
Versions: versions,
|
||||||
@@ -294,7 +320,13 @@ func healthzHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func readyzHandler(w http.ResponseWriter, r *http.Request) {
|
func readyzHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
// Проверить доступность S3
|
_, err := s3Client.BucketExists(context.Background(), bucketName)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("readyz: S3 bucket check failed: %v", err)
|
||||||
|
w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
|
w.Write([]byte("s3 unreachable"))
|
||||||
|
return
|
||||||
|
}
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
w.Write([]byte("ok"))
|
w.Write([]byte("ok"))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user