Some checks failed
go: build dev binaries / cleanup (push) Successful in 4s
Codespell / Check for spelling errors (push) Failing after 3s
docker: build dev containers / build-rust-binaries (amd64, x86_64-unknown-linux-musl) (push) Failing after 3s
docker: build dev containers / build-dev-containers (push) Has been skipped
docker: build dev containers / build-rust-binaries (arm64, true, aarch64-unknown-linux-musl) (push) Failing after 3s
End to End / FUSE Mount (push) Failing after 2s
EC Integration Tests / EC Integration Tests (push) Failing after 30s
FUSE DLM Integration Tests / FUSE DLM Integration Tests (push) Failing after 3s
FUSE Volume Server Failover Tests / FUSE Volume Server Failover (push) Failing after 3s
FUSE Integration Tests / FUSE Integration Testing (push) Failing after 2s
FUSE P2P Peer Chunk Sharing Integration Tests / FUSE P2P Peer Chunk Sharing (push) Failing after 3s
go: build binary / Go Vet (push) Failing after 2s
go: build binary / Go Vet 32-bit (push) Failing after 3s
go: build binary / Build (push) Failing after 4s
go: build binary / Build cross-platform (freebsd/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (darwin/arm64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/arm64) (push) Failing after 3s
go: build binary / Test (push) Failing after 2s
go: build binary / Test 32-bit (push) Failing after 3s
helm: lint and test charts / lint-test (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (11) (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (17) (push) Failing after 3s
go: build dev binaries / build_dev_linux_windows (amd64, linux) (push) Failing after 2s
go: build dev binaries / build_dev_linux_windows (amd64, windows) (push) Failing after 3s
go: build dev binaries / build_dev_darwin (amd64, darwin) (push) Has been skipped
go: build dev binaries / build_dev_darwin (arm64, darwin) (push) Has been skipped
Java Client Unit Tests / Java Unit Tests (11) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (17) (push) Failing after 2s
Java Client Unit Tests / Java Unit Tests (21) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (8) (push) Failing after 3s
Kafka Quick Test (Load Test with Schema Registry) / Kafka Client Load Test (Quick) (push) Failing after 3s
Kafka Gateway Tests / Kafka Unit Tests (unit-tests-1) (push) Failing after 11s
Kafka Gateway Tests / Kafka Integration Tests (Critical) (integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka End-to-End Tests (with SMQ) (e2e-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Consumer Group Tests (Highly Isolated) (consumer-group-1) (push) Failing after 2s
Kafka Gateway Tests / Kafka Client Compatibility (with SMQ) (client-compat-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka SMQ Integration Tests (Full Stack) (smq-integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Protocol Tests (Isolated) (protocol-1) (push) Failing after 3s
KMS Tests / KMS Provider Integration Tests (push) Failing after 2s
KMS Tests / S3 KMS End-to-End Tests (push) Failing after 3s
Master Cold Start Tests / Master Cold Start Tests (push) Failing after 4s
Metadata Subscribe Integration Tests / Metadata Subscribe Integration Tests (push) Failing after 3s
Multi-Master Tests / Multi-Master Failover Tests (push) Failing after 3s
Performance / CPU and Heap Profile (push) Failing after 2s
Performance / S3 Read/Write Benchmark (rust volume) (push) Failing after 3s
pjdfstest POSIX Compliance / pjdfstest (push) Failing after 2s
Plugin Worker Integration Tests / Plugin Worker: erasure_coding (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: vacuum (push) Failing after 3s
Rust Volume Server Tests / Rust Integration Tests (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 1) (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 1) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 3) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Distributed Tests (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Performance Tests (push) Failing after 3s
Performance / Throughput Benchmark (go volume) (push) Failing after 3s
Performance / Throughput Benchmark (rust volume) (push) Failing after 2s
Performance / S3 Read/Write Benchmark (go volume) (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: volume_balance (push) Failing after 3s
PostgreSQL Gateway Tests / PostgreSQL Basic Tests (push) Failing after 3s
Rust Volume Server Tests / Rust Unit Tests (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 3) (push) Failing after 3s
Rust Plugin Worker Tests / Rust Plugin Worker Build and Unit Tests (push) Failing after 3s
rust: build dev volume server binaries / cleanup (push) Successful in 3s
S3 ETag and ACL Tests / S3 ETag + ACL Integration Tests (push) Failing after 3s
S3 IAM Integration Tests / IAM Unit Tests (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (advanced) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (basic) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (group) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (policy-enforcement) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (sts) (push) Failing after 4s
S3 Keycloak Integration Tests / S3 Keycloak Integration Tests (push) Failing after 3s
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.11) (push) Failing after 3s
S3 PyArrow Parquet Tests / Go Unit Tests (Implicit Directory) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.12) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-amd64, x86_64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-arm64, aarch64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-linux (linux-amd64, x86_64-unknown-linux-gnu) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.9) (push) Has been cancelled
S3 Proxy Signature Tests / S3 Proxy Signature Verification Tests (push) Has been cancelled
S3 SDK V2 Route Disambiguation Tests / S3 SDK V2 Routing Tests (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (quick) (push) Has been cancelled
S3 SSE Tests / S3 SSE Compatibility Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Metadata Persistence Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Copy Operations Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Multipart Upload Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Performance Test (push) Has been cancelled
S3 SSE Tests / S3 Volume Encryption Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (comprehensive) (push) Has been cancelled
Spark Integration Tests / Spark Integration Tests (push) Failing after 2s
Telemetry Integration Tests / telemetry-integration-test (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 1) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 1) (push) Failing after 3s
Ceph S3 tests / Basic S3 tests (KV store) (push) Has been cancelled
Ceph S3 tests / S3 Versioning & Object Lock tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Unit Tests (push) Failing after 3s
Ceph S3 tests / S3 CORS tests (push) Has been cancelled
Ceph S3 tests / SeaweedFS Custom S3 Copy tests (push) Has been cancelled
Ceph S3 tests / Basic S3 tests (SQL store) (push) Has been cancelled
test s3 over https using aws-cli / awscli-tests (push) Has been cancelled
TLS Rotation Integration Tests / TLS Rotation Integration Tests (push) Has been cancelled
Vacuum Integration Tests / Vacuum Integration Tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Variables Integration Tests (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (advanced-policy) (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (basic-policy) (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (contextual-policy) (push) Failing after 4s
S3 Policy Integration Tests / Trusted Proxy Detection Tests (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Shell Integration Tests (push) Failing after 3s
Samba on FUSE Integration / samba-integration (push) Failing after 2s
SFTP Integration Tests / SFTP Integration Testing (push) Failing after 3s
terraform: validate and test modules / fmt, validate, plan-level tests (push) Failing after 3s
terraform: validate and test modules / local cluster smoke test (real weed) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 3) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 2) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 2) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 3) (push) Failing after 5s
mount: windows conformance / WinFsp conformance (push) Has been cancelled
mount: windows / Mount on Windows (push) Has been cancelled
336 lines
11 KiB
Go
336 lines
11 KiB
Go
// Package s3api implements the S3 API for SeaweedFS.
|
|
// This file implements the Smart Object Storage API (SOSAPI) which enables
|
|
// enterprise backup software to automatically discover storage system
|
|
// capabilities and capacity information.
|
|
package s3api
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/md5"
|
|
"encoding/hex"
|
|
"encoding/xml"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
|
|
"github.com/seaweedfs/seaweedfs/weed/util/version"
|
|
)
|
|
|
|
// SOSAPI constants
|
|
const (
|
|
// sosAPISystemFolder is the well-known folder path for SOSAPI system files.
|
|
// This UUID-based path is part of the SOSAPI specification.
|
|
sosAPISystemFolder = ".system-d26a9498-cb7c-4a87-a44a-8ae204f5ba6c"
|
|
|
|
// sosAPISystemXML is the path to the system capabilities XML file.
|
|
sosAPISystemXML = sosAPISystemFolder + "/system.xml"
|
|
|
|
// sosAPICapacityXML is the path to the capacity information XML file.
|
|
sosAPICapacityXML = sosAPISystemFolder + "/capacity.xml"
|
|
|
|
// sosAPIProtocolVersion is the SOSAPI protocol version supported.
|
|
sosAPIProtocolVersion = `"1.0"`
|
|
|
|
// sosAPIDefaultBlockSizeKB is the recommended block size in KB.
|
|
// 4096 KB (4MB) is optimal for object storage workloads.
|
|
sosAPIDefaultBlockSizeKB = 4096
|
|
)
|
|
|
|
// SystemInfo represents the system.xml response structure for SOSAPI.
|
|
// It describes the storage system's capabilities and recommendations.
|
|
type SystemInfo struct {
|
|
XMLName xml.Name `xml:"SystemInfo"`
|
|
ProtocolVersion string `xml:"ProtocolVersion"`
|
|
ModelName string `xml:"ModelName"`
|
|
ProtocolCapabilities struct {
|
|
CapacityInfo bool `xml:"CapacityInfo"`
|
|
UploadSessions bool `xml:"UploadSessions"`
|
|
IAMSTS bool `xml:"IAMSTS"`
|
|
} `xml:"ProtocolCapabilities"`
|
|
APIEndpoints *APIEndpoints `xml:"APIEndpoints,omitempty"`
|
|
SystemRecommendations *SystemRecommendations `xml:"SystemRecommendations,omitempty"`
|
|
}
|
|
|
|
// APIEndpoints contains optional IAM and STS endpoint information.
|
|
type APIEndpoints struct {
|
|
IAMEndpoint string `xml:"IAMEndpoint,omitempty"`
|
|
STSEndpoint string `xml:"STSEndpoint,omitempty"`
|
|
}
|
|
|
|
// SystemRecommendations contains storage system performance recommendations.
|
|
type SystemRecommendations struct {
|
|
S3ConcurrentTaskLimit int `xml:"S3ConcurrentTaskLimit,omitempty"`
|
|
S3MultiObjectDeleteLimit int `xml:"S3MultiObjectDeleteLimit,omitempty"`
|
|
StorageCurrentTaskLimit int `xml:"StorageCurrentTaskLimit,omitempty"`
|
|
KBBlockSize int `xml:"KbBlockSize"`
|
|
}
|
|
|
|
// CapacityInfo represents the capacity.xml response structure for SOSAPI.
|
|
// It provides real-time storage capacity information.
|
|
type CapacityInfo struct {
|
|
XMLName xml.Name `xml:"CapacityInfo"`
|
|
Capacity int64 `xml:"Capacity"`
|
|
Available int64 `xml:"Available"`
|
|
Used int64 `xml:"Used"`
|
|
}
|
|
|
|
// isSOSAPIObject checks if the given object path is a SOSAPI virtual object.
|
|
// These objects don't physically exist but are generated on-demand.
|
|
func isSOSAPIObject(object string) bool {
|
|
switch object {
|
|
case sosAPISystemXML, sosAPICapacityXML:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// generateSystemXML creates the system.xml response containing storage system
|
|
// capabilities and recommendations.
|
|
func generateSystemXML() ([]byte, error) {
|
|
si := SystemInfo{
|
|
ProtocolVersion: sosAPIProtocolVersion,
|
|
ModelName: "\"SeaweedFS " + version.VERSION_NUMBER + "\"",
|
|
}
|
|
|
|
// Enable capacity reporting capability
|
|
si.ProtocolCapabilities.CapacityInfo = true
|
|
si.ProtocolCapabilities.UploadSessions = false
|
|
si.ProtocolCapabilities.IAMSTS = false
|
|
|
|
// Set recommended block size for optimal performance
|
|
si.SystemRecommendations = &SystemRecommendations{
|
|
KBBlockSize: sosAPIDefaultBlockSizeKB,
|
|
}
|
|
|
|
return xml.Marshal(&si)
|
|
}
|
|
|
|
// generateCapacityXML creates the capacity.xml response containing real-time
|
|
// storage capacity information.
|
|
func (s3a *S3ApiServer) generateCapacityXML(ctx context.Context, bucket string) ([]byte, error) {
|
|
total, available, used, err := s3a.getCapacityInfo(ctx, bucket)
|
|
if err != nil {
|
|
glog.Warningf("SOSAPI: failed to get capacity info for bucket %s: %v, using defaults", bucket, err)
|
|
// Return zero capacity on error
|
|
total, available, used = 0, 0, 0
|
|
}
|
|
|
|
ci := CapacityInfo{
|
|
Capacity: total,
|
|
Available: available,
|
|
Used: used,
|
|
}
|
|
|
|
return xml.Marshal(&ci)
|
|
}
|
|
|
|
// getCapacityInfo retrieves capacity information for the specific bucket.
|
|
// It checks bucket quota first, then falls back to cluster topology information.
|
|
// Returns capacity, available, and used bytes.
|
|
func (s3a *S3ApiServer) getCapacityInfo(ctx context.Context, bucket string) (capacity, available, used int64, err error) {
|
|
// 1. Check if bucket has a quota
|
|
// We use s3a.getEntry which is a helper in s3api_bucket_handlers.go
|
|
var quota int64
|
|
// getEntry communicates with filer, so errors here might mean filer connectivity issues or bucket not found
|
|
// If bucket not found, we probably shouldn't be here (checked in handler), but safe to ignore
|
|
if entry, getErr := s3a.getBucketEntry(bucket); getErr == nil && entry != nil {
|
|
quota = entry.Quota
|
|
}
|
|
|
|
// 2. Get cluster topology from master
|
|
if len(s3a.option.Masters) == 0 {
|
|
return 0, 0, 0, fmt.Errorf("no master servers configured")
|
|
}
|
|
|
|
masterMap := make(map[string]pb.ServerAddress)
|
|
for _, master := range s3a.option.Masters {
|
|
masterMap[string(master)] = master
|
|
}
|
|
|
|
// Cluster capacity below is read from disk counters, which a filtered
|
|
// listing still reports in full.
|
|
collectionName := s3a.getCollectionName(bucket)
|
|
|
|
// Connect to any available master and get volume list (topology)
|
|
err = pb.WithOneOfGrpcMasterClients(false, masterMap, s3a.option.GrpcDialOption, func(client master_pb.SeaweedClient) error {
|
|
resp, vErr := client.VolumeList(ctx, &master_pb.VolumeListRequest{Collection: collectionName})
|
|
if vErr != nil {
|
|
return vErr
|
|
}
|
|
|
|
if resp.TopologyInfo == nil {
|
|
return nil
|
|
}
|
|
|
|
// Calculate used size for the bucket by summing up volumes in the collection
|
|
used = collectBucketUsageFromTopology(resp.TopologyInfo, collectionName)
|
|
|
|
// Calculate cluster capacity if no quota
|
|
if quota > 0 {
|
|
capacity = quota
|
|
available = quota - used
|
|
if available < 0 {
|
|
available = 0
|
|
}
|
|
} else {
|
|
// No quota - use cluster capacity
|
|
clusterTotal, clusterAvailable := calculateClusterCapacity(resp.TopologyInfo, resp.VolumeSizeLimitMb)
|
|
capacity = clusterTotal
|
|
available = clusterAvailable
|
|
}
|
|
return nil
|
|
})
|
|
|
|
return capacity, available, used, err
|
|
}
|
|
|
|
// collectBucketUsageFromTopology sums up the size of all volumes belonging to the specified collection.
|
|
func collectBucketUsageFromTopology(t *master_pb.TopologyInfo, collectionName string) (used int64) {
|
|
seenVolumes := make(map[uint32]bool)
|
|
for _, dc := range t.DataCenterInfos {
|
|
for _, r := range dc.RackInfos {
|
|
for _, dn := range r.DataNodeInfos {
|
|
for _, disk := range dn.DiskInfos {
|
|
for _, vi := range disk.VolumeInfos {
|
|
if vi.Collection == collectionName {
|
|
if !seenVolumes[vi.Id] {
|
|
used += int64(vi.Size)
|
|
seenVolumes[vi.Id] = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// calculateClusterCapacity sums up the total and available capacity of the entire cluster.
|
|
func calculateClusterCapacity(t *master_pb.TopologyInfo, volumeSizeLimitMb uint64) (total, available int64) {
|
|
volumeSize := int64(volumeSizeLimitMb) * 1024 * 1024
|
|
for _, dc := range t.DataCenterInfos {
|
|
for _, r := range dc.RackInfos {
|
|
for _, dn := range r.DataNodeInfos {
|
|
for _, disk := range dn.DiskInfos {
|
|
total += int64(disk.MaxVolumeCount) * volumeSize
|
|
available += int64(disk.FreeVolumeCount) * volumeSize
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// handleSOSAPIGetObject handles GET requests for SOSAPI virtual objects.
|
|
// Returns true if the request was handled, false if it should proceed normally.
|
|
func (s3a *S3ApiServer) handleSOSAPIGetObject(w http.ResponseWriter, r *http.Request, bucket, object string) bool {
|
|
if !isSOSAPIObject(object) {
|
|
return false
|
|
}
|
|
|
|
xmlData, err := s3a.generateSOSAPIContent(r.Context(), bucket, object)
|
|
if err != nil {
|
|
if errors.Is(err, filer_pb.ErrNotFound) {
|
|
s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucket)
|
|
} else {
|
|
glog.Errorf("SOSAPI: failed to generate %s: %v", object, err)
|
|
s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Calculate ETag from content
|
|
hash := md5.Sum(xmlData)
|
|
etag := hex.EncodeToString(hash[:])
|
|
|
|
// Set ETag header manually as ServeContent doesn't calculate it automatically
|
|
w.Header().Set("ETag", "\""+etag+"\"")
|
|
w.Header().Set("Content-Type", "application/xml")
|
|
|
|
// Use http.ServeContent to handle Content-Length, Range, and Last-Modified
|
|
http.ServeContent(w, r, object, time.Now().UTC(), bytes.NewReader(xmlData))
|
|
|
|
return true
|
|
}
|
|
|
|
// handleSOSAPIHeadObject handles HEAD requests for SOSAPI virtual objects.
|
|
// Returns true if the request was handled, false if it should proceed normally.
|
|
func (s3a *S3ApiServer) handleSOSAPIHeadObject(w http.ResponseWriter, r *http.Request, bucket, object string) bool {
|
|
if !isSOSAPIObject(object) {
|
|
return false
|
|
}
|
|
|
|
xmlData, err := s3a.generateSOSAPIContent(r.Context(), bucket, object)
|
|
if err != nil {
|
|
if errors.Is(err, filer_pb.ErrNotFound) {
|
|
s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucket)
|
|
} else {
|
|
glog.Errorf("SOSAPI: failed to generate %s for HEAD: %v", object, err)
|
|
s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Calculate ETag from content
|
|
hash := md5.Sum(xmlData)
|
|
etag := hex.EncodeToString(hash[:])
|
|
|
|
// Set response headers (no body for HEAD)
|
|
w.Header().Set("Content-Type", "application/xml")
|
|
w.Header().Set("ETag", "\""+etag+"\"")
|
|
w.Header().Set("Content-Length", strconv.Itoa(len(xmlData)))
|
|
w.Header().Set("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
return true
|
|
}
|
|
|
|
// generateSOSAPIContent generates the XML content for SOSAPI virtual objects.
|
|
// Returns the complete XML with declaration prepended.
|
|
func (s3a *S3ApiServer) generateSOSAPIContent(ctx context.Context, bucket, object string) ([]byte, error) {
|
|
// Verify bucket exists
|
|
if _, errCode := s3a.getBucketConfig(bucket); errCode != s3err.ErrNone {
|
|
if errCode == s3err.ErrNoSuchBucket {
|
|
return nil, filer_pb.ErrNotFound
|
|
}
|
|
return nil, fmt.Errorf("bucket config error: %v", errCode)
|
|
}
|
|
|
|
var xmlData []byte
|
|
var err error
|
|
|
|
switch object {
|
|
case sosAPISystemXML:
|
|
xmlData, err = generateSystemXML()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
glog.V(4).Infof("SOSAPI: generated system.xml for bucket %s", bucket)
|
|
|
|
case sosAPICapacityXML:
|
|
xmlData, err = s3a.generateCapacityXML(ctx, bucket)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
glog.V(4).Infof("SOSAPI: generated capacity.xml for bucket %s", bucket)
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unknown SOSAPI object: %s", object)
|
|
}
|
|
|
|
// Prepend XML declaration
|
|
xmlData = append([]byte(xml.Header), xmlData...)
|
|
|
|
return xmlData, nil
|
|
}
|