mirror of
https://github.com/minio/minio.git
synced 2025-11-09 21:49:46 -05:00
Add support for CopyObject across regions and multiple Minio IPs
This PR adds CopyObject support for objects residing in buckets in different Minio instances (where Minio instances are part of a federated setup). Also, added support for multiple Minio domain IPs. This is required for distributed deployments, where one deployment may have multiple nodes, each with a different public IP.
This commit is contained in:
committed by
kannappanr
parent
f30c95a301
commit
6ce7265c8c
@@ -21,18 +21,22 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio-go/pkg/set"
|
||||
|
||||
"github.com/coredns/coredns/plugin/etcd/msg"
|
||||
etcd "github.com/coreos/etcd/client"
|
||||
)
|
||||
|
||||
// ErrNoEntriesFound - Indicates no entries were found for the given key (directory)
|
||||
var ErrNoEntriesFound = errors.New("No entries found for this key")
|
||||
|
||||
// create a new coredns service record for the bucket.
|
||||
func newCoreDNSMsg(bucket string, ip string, port int, ttl uint32) ([]byte, error) {
|
||||
func newCoreDNSMsg(bucket, ip string, port int, ttl uint32) ([]byte, error) {
|
||||
return json.Marshal(&SrvRecord{
|
||||
Host: ip,
|
||||
Port: port,
|
||||
@@ -41,12 +45,24 @@ func newCoreDNSMsg(bucket string, ip string, port int, ttl uint32) ([]byte, erro
|
||||
})
|
||||
}
|
||||
|
||||
// Retrieves list of DNS entries for a bucket.
|
||||
// Retrieves list of DNS entries for the domain.
|
||||
func (c *coreDNS) List() ([]SrvRecord, error) {
|
||||
kapi := etcd.NewKeysAPI(c.etcdClient)
|
||||
key := msg.Path(fmt.Sprintf("%s.", c.domainName), defaultPrefixPath)
|
||||
return c.list(key)
|
||||
}
|
||||
|
||||
// Retrieves DNS records for a bucket.
|
||||
func (c *coreDNS) Get(bucket string) ([]SrvRecord, error) {
|
||||
key := msg.Path(fmt.Sprintf("%s.%s.", bucket, c.domainName), defaultPrefixPath)
|
||||
return c.list(key)
|
||||
}
|
||||
|
||||
// Retrieves list of entries under the key passed.
|
||||
// Note that this method fetches entries upto only two levels deep.
|
||||
func (c *coreDNS) list(key string) ([]SrvRecord, error) {
|
||||
kapi := etcd.NewKeysAPI(c.etcdClient)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout)
|
||||
r, err := kapi.Get(ctx, key, nil)
|
||||
r, err := kapi.Get(ctx, key, &etcd.GetOptions{Recursive: true})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -54,48 +70,52 @@ func (c *coreDNS) List() ([]SrvRecord, error) {
|
||||
|
||||
var srvRecords []SrvRecord
|
||||
for _, n := range r.Node.Nodes {
|
||||
var srvRecord SrvRecord
|
||||
if err = json.Unmarshal([]byte(n.Value), &srvRecord); err != nil {
|
||||
return nil, err
|
||||
if !n.Dir {
|
||||
var srvRecord SrvRecord
|
||||
if err = json.Unmarshal([]byte(n.Value), &srvRecord); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
srvRecord.Key = strings.TrimPrefix(n.Key, key)
|
||||
srvRecords = append(srvRecords, srvRecord)
|
||||
} else {
|
||||
// As this is a directory, loop through all the nodes inside
|
||||
for _, n1 := range n.Nodes {
|
||||
var srvRecord SrvRecord
|
||||
if err = json.Unmarshal([]byte(n1.Value), &srvRecord); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
srvRecord.Key = strings.TrimPrefix(n1.Key, key)
|
||||
srvRecord.Key = strings.TrimSuffix(srvRecord.Key, srvRecord.Host)
|
||||
srvRecords = append(srvRecords, srvRecord)
|
||||
}
|
||||
}
|
||||
srvRecord.Key = strings.TrimPrefix(n.Key, key)
|
||||
srvRecords = append(srvRecords, srvRecord)
|
||||
}
|
||||
sort.Slice(srvRecords, func(i int, j int) bool {
|
||||
return srvRecords[i].Key < srvRecords[j].Key
|
||||
})
|
||||
if srvRecords != nil {
|
||||
sort.Slice(srvRecords, func(i int, j int) bool {
|
||||
return srvRecords[i].Key < srvRecords[j].Key
|
||||
})
|
||||
} else {
|
||||
return nil, ErrNoEntriesFound
|
||||
}
|
||||
return srvRecords, nil
|
||||
}
|
||||
|
||||
// Retrieves DNS record for a bucket.
|
||||
func (c *coreDNS) Get(bucket string) (SrvRecord, error) {
|
||||
kapi := etcd.NewKeysAPI(c.etcdClient)
|
||||
key := msg.Path(fmt.Sprintf("%s.%s.", bucket, c.domainName), defaultPrefixPath)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout)
|
||||
r, err := kapi.Get(ctx, key, nil)
|
||||
cancel()
|
||||
if err != nil {
|
||||
return SrvRecord{}, err
|
||||
}
|
||||
var sr SrvRecord
|
||||
if err = json.Unmarshal([]byte(r.Node.Value), &sr); err != nil {
|
||||
return SrvRecord{}, err
|
||||
}
|
||||
sr.Key = strings.TrimPrefix(r.Node.Key, key)
|
||||
return sr, nil
|
||||
}
|
||||
|
||||
// Adds DNS entries into etcd endpoint in CoreDNS etcd messae format.
|
||||
// Adds DNS entries into etcd endpoint in CoreDNS etcd message format.
|
||||
func (c *coreDNS) Put(bucket string) error {
|
||||
bucketMsg, err := newCoreDNSMsg(bucket, c.domainIP, c.domainPort, defaultTTL)
|
||||
if err != nil {
|
||||
return err
|
||||
var err error
|
||||
for ip := range c.domainIPs {
|
||||
var bucketMsg []byte
|
||||
bucketMsg, err = newCoreDNSMsg(bucket, ip, c.domainPort, defaultTTL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kapi := etcd.NewKeysAPI(c.etcdClient)
|
||||
key := msg.Path(fmt.Sprintf("%s.%s", bucket, c.domainName), defaultPrefixPath)
|
||||
key = key + "/" + ip
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout)
|
||||
_, err = kapi.Set(ctx, key, string(bucketMsg), nil)
|
||||
cancel()
|
||||
}
|
||||
kapi := etcd.NewKeysAPI(c.etcdClient)
|
||||
key := msg.Path(fmt.Sprintf("%s.%s.", bucket, c.domainName), defaultPrefixPath)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout)
|
||||
_, err = kapi.Set(ctx, key, string(bucketMsg), nil)
|
||||
cancel()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -111,18 +131,15 @@ func (c *coreDNS) Delete(bucket string) error {
|
||||
|
||||
// CoreDNS - represents dns config for coredns server.
|
||||
type coreDNS struct {
|
||||
domainName, domainIP string
|
||||
domainPort int
|
||||
etcdClient etcd.Client
|
||||
domainName string
|
||||
domainIPs set.StringSet
|
||||
domainPort int
|
||||
etcdClient etcd.Client
|
||||
}
|
||||
|
||||
// NewCoreDNS - initialize a new coreDNS set/unset values.
|
||||
func NewCoreDNS(domainName, domainIP, domainPort string, etcdClient etcd.Client) (Config, error) {
|
||||
if domainName == "" || domainIP == "" || etcdClient == nil {
|
||||
return nil, errors.New("invalid argument")
|
||||
}
|
||||
|
||||
if net.ParseIP(domainIP) == nil {
|
||||
func NewCoreDNS(domainName string, domainIPs set.StringSet, domainPort string, etcdClient etcd.Client) (Config, error) {
|
||||
if domainName == "" || domainIPs.IsEmpty() || etcdClient == nil {
|
||||
return nil, errors.New("invalid argument")
|
||||
}
|
||||
|
||||
@@ -133,7 +150,7 @@ func NewCoreDNS(domainName, domainIP, domainPort string, etcdClient etcd.Client)
|
||||
|
||||
return &coreDNS{
|
||||
domainName: domainName,
|
||||
domainIP: domainIP,
|
||||
domainIPs: domainIPs,
|
||||
domainPort: port,
|
||||
etcdClient: etcdClient,
|
||||
}, nil
|
||||
|
||||
@@ -60,6 +60,6 @@ type SrvRecord struct {
|
||||
type Config interface {
|
||||
Put(key string) error
|
||||
List() ([]SrvRecord, error)
|
||||
Get(key string) (SrvRecord, error)
|
||||
Get(key string) ([]SrvRecord, error)
|
||||
Delete(key string) error
|
||||
}
|
||||
|
||||
@@ -140,6 +140,9 @@ func saveFileConfigEtcd(filename string, clnt etcd.Client, v interface{}) error
|
||||
kapi := etcd.NewKeysAPI(clnt)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
_, err = kapi.Update(ctx, filename, string(dataBytes))
|
||||
if etcd.IsKeyNotFound(err) {
|
||||
_, err = kapi.Create(ctx, filename, string(dataBytes))
|
||||
}
|
||||
cancel()
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user