diff --git a/swift/public.go b/swift/public.go index cbb91bf2..9e40d327 100644 --- a/swift/public.go +++ b/swift/public.go @@ -2,21 +2,26 @@ package swift import ( "fmt" - "github.com/ncw/swift" + "github.com/ncw/swift" "github.com/smira/aptly/aptly" "github.com/smira/aptly/files" - "time" + "time" "os" "path/filepath" + "net/http" + "encoding/json" ) // PublishedStorage abstract file system with published files (actually hosted on Swift) type PublishedStorage struct { - conn swift.Connection - container string - prefix string + conn swift.Connection + container string + prefix string + support_bulk_delete bool } +type SwiftInfo map[string]interface{} + // Check interface var ( _ aptly.PublishedStorage = (*PublishedStorage)(nil) @@ -25,44 +30,56 @@ var ( // NewPublishedStorage creates new instance of PublishedStorage with specified Swift access // keys, tenant and tenantId func NewPublishedStorage(username string, password string, authUrl string, tenant string, tenantId string, container string, prefix string) (*PublishedStorage, error) { - if username == "" { - username = os.Getenv("OS_USERNAME") - } - if password == "" { - password = os.Getenv("OS_PASSWORD") - } - if authUrl == "" { - authUrl = os.Getenv("OS_AUTH_URL") - } - if tenant == "" { - tenant = os.Getenv("OS_TENANT_NAME") - } - if tenantId == "" { - tenantId = os.Getenv("OS_TENANT_ID") - } + if username == "" { + username = os.Getenv("OS_USERNAME") + } + if password == "" { + password = os.Getenv("OS_PASSWORD") + } + if authUrl == "" { + authUrl = os.Getenv("OS_AUTH_URL") + } + if tenant == "" { + tenant = os.Getenv("OS_TENANT_NAME") + } + if tenantId == "" { + tenantId = os.Getenv("OS_TENANT_ID") + } - ct := swift.Connection{ - UserName: username, - ApiKey: password, - AuthUrl: authUrl, - UserAgent: "aptly/" + aptly.Version, - Tenant: tenant, - TenantId: tenantId, - ConnectTimeout: 60 * time.Second, - Timeout: 60 * time.Second, - } - err := ct.Authenticate() - if err != nil { - return nil, fmt.Errorf("Swift authentication failed: %s", err) - } + ct := swift.Connection{ + UserName: username, + ApiKey: password, + AuthUrl: authUrl, + UserAgent: "aptly/" + aptly.Version, + Tenant: tenant, + TenantId: tenantId, + ConnectTimeout: 60 * time.Second, + Timeout: 60 * time.Second, + } + err := ct.Authenticate() + if err != nil { + return nil, fmt.Errorf("Swift authentication failed: %s", err) + } - result := &PublishedStorage{ - conn: ct, - container: container, - prefix: prefix, - } + var bulk_delete bool + resp, err := http.Get(filepath.Join(ct.StorageUrl, "..", "..") + "/info") + if err == nil { + defer resp.Body.Close() + decoder := json.NewDecoder(resp.Body) + var infos SwiftInfo + if decoder.Decode(&infos) == nil { + _, bulk_delete = infos["bulk_delete"] + } + } - return result, nil + result := &PublishedStorage{ + conn: ct, + container: container, + prefix: prefix, + support_bulk_delete: bulk_delete, + } + + return result, nil } // String @@ -72,8 +89,8 @@ func (storage *PublishedStorage) String() string { // MkDir creates directory recursively under public path func (storage *PublishedStorage) MkDir(path string) error { - // no op for Swift - return nil + // no op for Swift + return nil } // PutFile puts file into published storage at specified path @@ -88,7 +105,7 @@ func (storage *PublishedStorage) PutFile(path string, sourceFilename string) err } defer source.Close() - _, err = storage.conn.ObjectPut(storage.container, filepath.Join(storage.prefix, path), source, false, "", "", nil) + _, err = storage.conn.ObjectPut(storage.container, filepath.Join(storage.prefix, path), source, false, "", "", nil) if err != nil { return fmt.Errorf("error uploading %s to %s: %s", sourceFilename, storage, err) @@ -108,25 +125,28 @@ func (storage *PublishedStorage) Remove(path string) error { // RemoveDirs removes directory structure under public path func (storage *PublishedStorage) RemoveDirs(path string, progress aptly.Progress) error { - path = filepath.Join(storage.prefix, path) - opts := swift.ObjectsOpts{ - Prefix: storage.prefix, - } - if objects, err := storage.conn.ObjectNamesAll(storage.container, &opts); err != nil { + path = filepath.Join(storage.prefix, path) + opts := swift.ObjectsOpts{ + Prefix: storage.prefix, + } + if objects, err := storage.conn.ObjectNamesAll(storage.container, &opts); err != nil { return fmt.Errorf("error removing dir %s from %s: %s", path, storage, err) - } else { - if _, err := storage.conn.BulkDelete(storage.container, objects); err != nil { - if err == swift.Forbidden { - for _, name := range objects { - if storage.conn.ObjectDelete(storage.container, name) != nil { - return nil - } - } - } - } - } + } else { + var multi_delete bool = true + if storage.support_bulk_delete { + _, err := storage.conn.BulkDelete(storage.container, objects) + multi_delete = err != nil + } + if multi_delete { + for _, name := range objects { + if err := storage.conn.ObjectDelete(storage.container, name); err != nil { + return err + } + } + } + } - return nil + return nil } // LinkFromPool links package file from pool to dist's pool location @@ -171,9 +191,9 @@ func (storage *PublishedStorage) Filelist(prefix string) ([]string, error) { if prefix != "" { prefix += "/" } - opts := swift.ObjectsOpts{ - Prefix: prefix, - } + opts := swift.ObjectsOpts{ + Prefix: prefix, + } contents, err := storage.conn.ObjectNamesAll(storage.container, &opts) if err != nil { return nil, fmt.Errorf("error listing under prefix %s in %s: %s", prefix, storage, err) @@ -189,5 +209,5 @@ func (storage *PublishedStorage) RenameFile(oldName, newName string) error { return fmt.Errorf("error copying %s -> %s in %s: %s", oldName, newName, storage, err) } - return nil + return nil }