Add website integration tests and remove NotImplemented stubs

Replace PutBucketWebsite, GetBucketWebsite, DeleteBucketWebsite
NotImplemented test stubs with comprehensive integration tests covering:
- non-existing bucket errors
- validation (empty suffix, suffix with slash, invalid protocol, mutual
  exclusion of RedirectAllRequestsTo and IndexDocument)
- successful put/get round-trips for both index+error and redirect-all configs
- delete idempotency and verification

Signed-off-by: Marc Singer <marc@singer.gg>

Add error document serving, routing rules, and integration tests

Implement Features 1 and 2 of S3 static website hosting:

- WebsiteErrorDocument controller wrapper intercepts 4xx errors on
  website-enabled buckets and serves the configured error document or
  evaluates post-request routing rules (error code match redirects)
- ResolveWebsiteIndex middleware now caches parsed WebsiteConfiguration
  in context, handles RedirectAllRequestsTo, evaluates pre-request
  routing rules (key prefix match redirects), and rewrites directory
  keys for index document
- MatchPreRequestRule and MatchPostRequestRule methods on
  WebsiteConfiguration for routing rule evaluation
- 14 unit tests for routing rule matching
- 7 integration tests covering error document, routing rules,
  redirect-all, and index document behavior

Signed-off-by: Marc Singer <marc@singer.gg>

Add separate website hosting endpoint with virtual-host routing

Signed-off-by: Marc Singer <marc@singer.gg>

Support catch-all mode for website endpoint when --website-domain is omitted

Signed-off-by: Marc Singer <marc@singer.gg>
This commit is contained in:
Marc Singer
2026-06-10 12:41:51 +04:00
committed by niksis02
parent ac5c3b4a86
commit 375c2764d5
26 changed files with 1833 additions and 175 deletions
+37
View File
@@ -91,6 +91,10 @@ var (
webuiAdminGateways []string
webuiPathPrefix string
webuiS3Prefix string
websitePorts []string
websiteDomain string
websiteCertFile, websiteKeyFile string
websiteNoTLS bool
disableACLs bool
mpMaxParts int
copyObjectThreshold int64
@@ -152,6 +156,7 @@ documentation can be found in the GitHub wiki.`,
webuiGateways = ctx.StringSlice("webui-gateways")
webuiAdminGateways = ctx.StringSlice("webui-admin-gateways")
webuiPathPrefix = ctx.String("webui-path-prefix")
websitePorts = ctx.StringSlice("website")
// Resolve relative UNIX socket paths to absolute before any backend
// (e.g. posix) can change the working directory via os.Chdir.
@@ -165,6 +170,9 @@ documentation can be found in the GitHub wiki.`,
if webuiPorts, err = utils.AbsSocketPaths(webuiPorts); err != nil {
return err
}
if websitePorts, err = utils.AbsSocketPaths(websitePorts); err != nil {
return err
}
return nil
},
Action: func(ctx *cli.Context) error {
@@ -240,6 +248,35 @@ func initFlags() []cli.Flag {
EnvVars: []string{"VGW_WEBUI_S3_PREFIX"},
Destination: &webuiS3Prefix,
},
&cli.StringSliceFlag{
Name: "website",
Usage: "enable static website hosting endpoint on the specified listen address (e.g. ':8080'; same forms as --port; can be specified multiple times; requires --website-domain)",
EnvVars: []string{"VGW_WEBSITE_PORT"},
},
&cli.StringFlag{
Name: "website-domain",
Usage: "base domain for website virtual-host routing (e.g. 'example.com'); host 'blog.example.com' serves bucket 'blog', host 'example.com' serves bucket 'example.com'; when omitted the full hostname is used as the bucket name (catch-all mode, buckets named as FQDNs)",
EnvVars: []string{"VGW_WEBSITE_DOMAIN"},
Destination: &websiteDomain,
},
&cli.StringFlag{
Name: "website-cert",
Usage: "TLS cert file for website endpoint (defaults to --cert value when website is enabled)",
EnvVars: []string{"VGW_WEBSITE_CERT"},
Destination: &websiteCertFile,
},
&cli.StringFlag{
Name: "website-key",
Usage: "TLS key file for website endpoint (defaults to --key value when website is enabled)",
EnvVars: []string{"VGW_WEBSITE_KEY"},
Destination: &websiteKeyFile,
},
&cli.BoolFlag{
Name: "website-no-tls",
Usage: "disable TLS for website endpoint even if TLS is configured for the gateway",
EnvVars: []string{"VGW_WEBSITE_NO_TLS"},
Destination: &websiteNoTLS,
},
&cli.StringFlag{
Name: "access",
Usage: "root user access key",
+33 -19
View File
@@ -22,25 +22,26 @@ import (
)
var (
awsID string
awsSecret string
endpoint string
prefix string
dstBucket string
partSize int64
objSize int64
concurrency int
files int
totalReqs int
upload bool
download bool
hostStyle bool
checksumDisable bool
versioningEnabled bool
azureTests bool
sidecarTests bool
tlsStatus bool
parallel bool
awsID string
awsSecret string
endpoint string
websiteEndpointTest string
prefix string
dstBucket string
partSize int64
objSize int64
concurrency int
files int
totalReqs int
upload bool
download bool
hostStyle bool
checksumDisable bool
versioningEnabled bool
azureTests bool
tlsStatus bool
parallel bool
sidecarTests bool
)
func testCommand() *cli.Command {
@@ -76,6 +77,13 @@ func initTestFlags() []cli.Flag {
Destination: &endpoint,
Aliases: []string{"e"},
},
&cli.StringFlag{
Name: "website-endpoint",
Usage: "dedicated website hosting endpoint (e.g. 'http://localhost:8080'); required for WebsiteHosting tests",
EnvVars: []string{"VGW_TEST_WEBSITE_ENDPOINT"},
Destination: &websiteEndpointTest,
Aliases: []string{"we"},
},
&cli.BoolFlag{
Name: "host-style",
Usage: "Use host-style bucket addressing",
@@ -334,6 +342,9 @@ func getAction(tf testFunc) func(ctx *cli.Context) error {
integration.WithEndpoint(endpoint),
integration.WithTLSStatus(tlsStatus),
}
if websiteEndpointTest != "" {
opts = append(opts, integration.WithWebsiteEndpoint(websiteEndpointTest))
}
if debug {
opts = append(opts, integration.WithDebug())
}
@@ -380,6 +391,9 @@ func extractIntTests() (commands []*cli.Command) {
integration.WithEndpoint(endpoint),
integration.WithTLSStatus(tlsStatus),
}
if websiteEndpointTest != "" {
opts = append(opts, integration.WithWebsiteEndpoint(websiteEndpointTest))
}
if debug {
opts = append(opts, integration.WithDebug())
}