package erasure import ( "fmt" "strings" ) type S3ShardConfig struct { ShardBucket string `json:"aws_s3_shard_bucket"` ShardRegion string `json:"aws_s3_shard_region"` CRRDestBucket string `json:"aws_s3_crr_dest_bucket"` CRRDestRegion string `json:"aws_s3_crr_dest_region"` CloudFrontDomain string `json:"aws_cloudfront_domain,omitempty"` ReplicationRoleARN string `json:"aws_s3_replication_role_arn,omitempty"` ReplicationAccountID string `json:"aws_account_id,omitempty"` } func S3ShardKey(token, region string, shardIndex int, shardHash string) string { token = strings.TrimSpace(token) region = strings.TrimSpace(region) shardHash = strings.TrimSpace(strings.ToLower(shardHash)) if token == "" || region == "" || shardHash == "" { return "" } return fmt.Sprintf("shards/%s/%s/%d-%s.bin", token, region, shardIndex, shardHash) } func S3ShardMetadata(region string, shardIndex int, token, shardHash string) map[string]string { return map[string]string{ "region": strings.TrimSpace(region), "shard-index": fmt.Sprintf("%d", shardIndex), "token": strings.TrimSpace(token), "shard-hash": strings.TrimSpace(strings.ToLower(shardHash)), "shard-advert": FormatShardAdvert(region, shardIndex), } } func FormatShardAdvert(region string, shardIndex int) string { return fmt.Sprintf("%s:%d", strings.TrimSpace(region), shardIndex) } func ParseShardAdvert(advert string) (region string, index int, ok bool) { advert = strings.TrimSpace(advert) colon := strings.LastIndex(advert, ":") if colon <= 0 { return "", 0, false } region = strings.TrimSpace(advert[:colon]) if _, err := fmt.Sscanf(strings.TrimSpace(advert[colon+1:]), "%d", &index); err != nil { return "", 0, false } return region, index, region != "" } func BuildS3CRRRule(cfg S3ShardConfig) (map[string]interface{}, error) { if cfg.ShardBucket == "" || cfg.ShardRegion == "" || cfg.CRRDestBucket == "" || cfg.CRRDestRegion == "" { return nil, fmt.Errorf("aws s3 shard source/dest bucket and regions required") } role := strings.TrimSpace(cfg.ReplicationRoleARN) if role == "" { acct := strings.TrimSpace(cfg.ReplicationAccountID) if acct == "" { acct = "ACCOUNT_ID" } role = fmt.Sprintf("arn:aws:iam::%s:role/aetherforge-s3-shard-replication", acct) } dest := strings.TrimSpace(cfg.CRRDestBucket) if !strings.HasPrefix(dest, "arn:") { dest = "arn:aws:s3:::" + dest } return map[string]interface{}{ "Role": role, "Rules": []map[string]interface{}{{ "ID": "aetherforge-erasure-shard-crr", "Status": "Enabled", "Priority": 1, "Filter": map[string]interface{}{"Prefix": "shards/"}, "Destination": map[string]interface{}{"Bucket": dest}, }}, }, nil }