@ -1,5 +1,5 @@
/ *
/ *
* MinIO Cloud Storage , ( C ) 2015 , 2016 , 2017 , 2018 MinIO , Inc .
* MinIO Cloud Storage , ( C ) 2015 - 2020 MinIO , Inc .
*
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* you may not use this file except in compliance with the License .
@ -22,7 +22,6 @@ import (
"encoding/xml"
"encoding/xml"
"fmt"
"fmt"
"io"
"io"
"net"
"net/http"
"net/http"
"net/url"
"net/url"
"path"
"path"
@ -75,40 +74,45 @@ func initFederatorBackend(buckets []BucketInfo, objLayer ObjectLayer) {
return
return
}
}
bucketSet := set . NewStringSet ( )
bucketsSet := set . NewStringSet ( )
bucketsToBeUpdated := set . NewStringSet ( )
bucketsInConflict := set . NewStringSet ( )
for _ , bucket := range buckets {
bucketsSet . Add ( bucket . Name )
r , ok := dnsBuckets [ bucket . Name ]
if ! ok {
bucketsToBeUpdated . Add ( bucket . Name )
continue
}
if ! globalDomainIPs . Intersection ( set . CreateStringSet ( getHostsSlice ( r ) ... ) ) . IsEmpty ( ) {
if globalDomainIPs . Difference ( set . CreateStringSet ( getHostsSlice ( r ) ... ) ) . IsEmpty ( ) {
// No difference in terms of domainIPs and nothing
// has changed so we don't change anything on the etcd.
continue
}
// if domain IPs intersect then it won't be an empty set.
// such an intersection means that bucket exists on etcd.
// but if we do see a difference with local domain IPs with
// hostSlice from etcd then we should update with newer
// domainIPs, we proceed to do that here.
bucketsToBeUpdated . Add ( bucket . Name )
continue
}
// No IPs seem to intersect, this means that bucket exists but has
// different IP addresses perhaps from a different deployment.
// bucket names are globally unique in federation at a given
// path prefix, name collision is not allowed. We simply log
// an error and continue.
bucketsInConflict . Add ( bucket . Name )
}
// Add buckets that are not registered with the DNS
// Add/update buckets that are not registered with the DNS
g := errgroup . WithNErrs ( len ( buckets ) )
g := errgroup . WithNErrs ( len ( buckets ) )
for index := range buckets {
bucketsToBeUpdatedSlice := bucketsToBeUpdated . ToSlice ( )
bucketSet . Add ( buckets [ index ] . Name )
for index := range bucketsToBeUpdatedSlice {
index := index
index := index
g . Go ( func ( ) error {
g . Go ( func ( ) error {
r , gerr := globalDNSConfig . Get ( buckets [ index ] . Name )
return globalDNSConfig . Put ( bucketsToBeUpdatedSlice [ index ] )
if gerr != nil {
if gerr == dns . ErrNoEntriesFound {
return globalDNSConfig . Put ( buckets [ index ] . Name )
}
return gerr
}
if ! globalDomainIPs . Intersection ( set . CreateStringSet ( getHostsSlice ( r ) ... ) ) . IsEmpty ( ) {
if globalDomainIPs . Difference ( set . CreateStringSet ( getHostsSlice ( r ) ... ) ) . IsEmpty ( ) {
// No difference in terms of domainIPs and nothing
// has changed so we don't change anything on the etcd.
return nil
}
// if domain IPs intersect then it won't be an empty set.
// such an intersection means that bucket exists on etcd.
// but if we do see a difference with local domain IPs with
// hostSlice from etcd then we should update with newer
// domainIPs, we proceed to do that here.
return globalDNSConfig . Put ( buckets [ index ] . Name )
}
// No IPs seem to intersect, this means that bucket exists but has
// different IP addresses perhaps from a different deployment.
// bucket names are globally unique in federation at a given
// path prefix, name collision is not allowed. We simply log
// an error and continue.
return fmt . Errorf ( "Unable to add bucket DNS entry for bucket %s, an entry exists for the same bucket. Use one of these IP addresses %v to access the bucket" , buckets [ index ] . Name , globalDomainIPs . ToSlice ( ) )
} , index )
} , index )
}
}
@ -118,36 +122,26 @@ func initFederatorBackend(buckets []BucketInfo, objLayer ObjectLayer) {
}
}
}
}
g = errgroup . WithNErrs ( len ( dnsBuckets ) )
for _ , bucket := range bucketsInConflict . ToSlice ( ) {
logger . LogIf ( context . Background ( ) , fmt . Errorf ( "Unable to add bucket DNS entry for bucket %s, an entry exists for the same bucket. Use one of these IP addresses %v to access the bucket" , bucket , globalDomainIPs . ToSlice ( ) ) )
}
// Remove buckets that are in DNS for this server, but aren't local
// Remove buckets that are in DNS for this server, but aren't local
for index := range dnsBuckets {
for bucket , records := range dnsBuckets {
index := index
if bucketsSet . Contains ( bucket ) {
g . Go ( func ( ) error {
continue
// This is a local bucket that exists, so we can continue
}
if bucketSet . Contains ( dnsBuckets [ index ] . Key ) {
return nil
}
if globalDomainIPs . Intersection ( set . CreateStringSet ( getHostsSlice ( records ) ... ) ) . IsEmpty ( ) {
// This is not for our server, so we can continue
// This is not for our server, so we can continue
hostPort := net . JoinHostPort ( dnsBuckets [ index ] . Host , string ( dnsBuckets [ index ] . Port ) )
continue
if globalDomainIPs . Intersection ( set . CreateStringSet ( hostPort ) ) . IsEmpty ( ) {
}
return nil
}
// We go to here, so we know the bucket no longer exists,
// but is registered in DNS to this server
if err := globalDNSConfig . DeleteRecord ( dnsBuckets [ index ] ) ; err != nil {
return fmt . Errorf ( "Failed to remove DNS entry for %s due to %w" ,
dnsBuckets [ index ] . Key , err )
}
return nil
} , index )
}
for _ , err := range g . Wait ( ) {
// We go to here, so we know the bucket no longer exists,
if err != nil {
// but is registered in DNS to this server
logger . LogIf ( context . Background ( ) , err )
if err = globalDNSConfig . Delete ( bucket ) ; err != nil {
logger . LogIf ( context . Background ( ) , fmt . Errorf ( "Failed to remove DNS entry for %s due to %w" ,
bucket , err ) )
}
}
}
}
}
}
@ -285,16 +279,11 @@ func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.R
writeErrorResponse ( ctx , w , toAPIError ( ctx , err ) , r . URL , guessIsBrowserReq ( r ) )
writeErrorResponse ( ctx , w , toAPIError ( ctx , err ) , r . URL , guessIsBrowserReq ( r ) )
return
return
}
}
bucketSet := set . NewStringSet ( )
for _ , dnsRecords := range dnsBuckets {
for _ , dnsRecord := range dnsBuckets {
if bucketSet . Contains ( dnsRecord . Key ) {
continue
}
bucketsInfo = append ( bucketsInfo , BucketInfo {
bucketsInfo = append ( bucketsInfo , BucketInfo {
Name : dnsRecord . Key ,
Name : dnsRecords [ 0 ] . Key ,
Created : dnsRecord . CreationDate ,
Created : dnsRecords [ 0 ] . CreationDate ,
} )
} )
bucketSet . Add ( dnsRecord . Key )
}
}
} else {
} else {
// Invoke the list buckets.
// Invoke the list buckets.