1package swift
2
3import (
4	"os"
5)
6
7// DynamicLargeObjectCreateFile represents an open static large object
8type DynamicLargeObjectCreateFile struct {
9	largeObjectCreateFile
10}
11
12// DynamicLargeObjectCreateFile creates a dynamic large object
13// returning an object which satisfies io.Writer, io.Seeker, io.Closer
14// and io.ReaderFrom.  The flags are as passes to the
15// largeObjectCreate method.
16func (c *Connection) DynamicLargeObjectCreateFile(opts *LargeObjectOpts) (LargeObjectFile, error) {
17	lo, err := c.largeObjectCreate(opts)
18	if err != nil {
19		return nil, err
20	}
21
22	return withBuffer(opts, &DynamicLargeObjectCreateFile{
23		largeObjectCreateFile: *lo,
24	}), nil
25}
26
27// DynamicLargeObjectCreate creates or truncates an existing dynamic
28// large object returning a writeable object.  This sets opts.Flags to
29// an appropriate value before calling DynamicLargeObjectCreateFile
30func (c *Connection) DynamicLargeObjectCreate(opts *LargeObjectOpts) (LargeObjectFile, error) {
31	opts.Flags = os.O_TRUNC | os.O_CREATE
32	return c.DynamicLargeObjectCreateFile(opts)
33}
34
35// DynamicLargeObjectDelete deletes a dynamic large object and all of its segments.
36func (c *Connection) DynamicLargeObjectDelete(container string, path string) error {
37	return c.LargeObjectDelete(container, path)
38}
39
40// DynamicLargeObjectMove moves a dynamic large object from srcContainer, srcObjectName to dstContainer, dstObjectName
41func (c *Connection) DynamicLargeObjectMove(srcContainer string, srcObjectName string, dstContainer string, dstObjectName string) error {
42	info, headers, err := c.Object(dstContainer, srcObjectName)
43	if err != nil {
44		return err
45	}
46
47	segmentContainer, segmentPath := parseFullPath(headers["X-Object-Manifest"])
48	if err := c.createDLOManifest(dstContainer, dstObjectName, segmentContainer+"/"+segmentPath, info.ContentType); err != nil {
49		return err
50	}
51
52	if err := c.ObjectDelete(srcContainer, srcObjectName); err != nil {
53		return err
54	}
55
56	return nil
57}
58
59// createDLOManifest creates a dynamic large object manifest
60func (c *Connection) createDLOManifest(container string, objectName string, prefix string, contentType string) error {
61	headers := make(Headers)
62	headers["X-Object-Manifest"] = prefix
63	manifest, err := c.ObjectCreate(container, objectName, false, "", contentType, headers)
64	if err != nil {
65		return err
66	}
67
68	if err := manifest.Close(); err != nil {
69		return err
70	}
71
72	return nil
73}
74
75// Close satisfies the io.Closer interface
76func (file *DynamicLargeObjectCreateFile) Close() error {
77	return file.Flush()
78}
79
80func (file *DynamicLargeObjectCreateFile) Flush() error {
81	err := file.conn.createDLOManifest(file.container, file.objectName, file.segmentContainer+"/"+file.prefix, file.contentType)
82	if err != nil {
83		return err
84	}
85	return file.conn.waitForSegmentsToShowUp(file.container, file.objectName, file.Size())
86}
87
88func (c *Connection) getAllDLOSegments(segmentContainer, segmentPath string) ([]Object, error) {
89	//a simple container listing works 99.9% of the time
90	segments, err := c.ObjectsAll(segmentContainer, &ObjectsOpts{Prefix: segmentPath})
91	if err != nil {
92		return nil, err
93	}
94
95	hasObjectName := make(map[string]struct{})
96	for _, segment := range segments {
97		hasObjectName[segment.Name] = struct{}{}
98	}
99
100	//The container listing might be outdated (i.e. not contain all existing
101	//segment objects yet) because of temporary inconsistency (Swift is only
102	//eventually consistent!). Check its completeness.
103	segmentNumber := 0
104	for {
105		segmentNumber++
106		segmentName := getSegment(segmentPath, segmentNumber)
107		if _, seen := hasObjectName[segmentName]; seen {
108			continue
109		}
110
111		//This segment is missing in the container listing. Use a more reliable
112		//request to check its existence. (HEAD requests on segments are
113		//guaranteed to return the correct metadata, except for the pathological
114		//case of an outage of large parts of the Swift cluster or its network,
115		//since every segment is only written once.)
116		segment, _, err := c.Object(segmentContainer, segmentName)
117		switch err {
118		case nil:
119			//found new segment -> add it in the correct position and keep
120			//going, more might be missing
121			if segmentNumber <= len(segments) {
122				segments = append(segments[:segmentNumber], segments[segmentNumber-1:]...)
123				segments[segmentNumber-1] = segment
124			} else {
125				segments = append(segments, segment)
126			}
127			continue
128		case ObjectNotFound:
129			//This segment is missing. Since we upload segments sequentially,
130			//there won't be any more segments after it.
131			return segments, nil
132		default:
133			return nil, err //unexpected error
134		}
135	}
136}
137