1package baggageclaim
2
3import (
4	"context"
5	"encoding/json"
6	"io"
7
8	"code.cloudfoundry.org/lager"
9)
10
11type Encoding string
12
13const GzipEncoding Encoding = "gzip"
14const ZstdEncoding Encoding = "zstd"
15
16//go:generate counterfeiter . Client
17
18// Client represents a client connection to a BaggageClaim server.
19type Client interface {
20	// CreateVolume will create a volume on the remote server. By passing in a
21	// VolumeSpec with a different strategy you can choose the type of volume
22	// that you want to create.
23	//
24	// You are required to pass in a logger to the call to retain context across
25	// the library boundary.
26	//
27	// CreateVolume returns the volume that was created or an error as to why it
28	// could not be created.
29	CreateVolume(lager.Logger, string, VolumeSpec) (Volume, error)
30
31	// ListVolumes lists the volumes that are present on the server. A
32	// VolumeProperties object can be passed in to filter the volumes that are in
33	// the response.
34	//
35	// You are required to pass in a logger to the call to retain context across
36	// the library boundary.
37	//
38	// ListVolumes returns the volumes that were found or an error as to why they
39	// could not be listed.
40	ListVolumes(lager.Logger, VolumeProperties) (Volumes, error)
41
42	// LookupVolume finds a volume that is present on the server. It takes a
43	// string that corresponds to the Handle of the Volume.
44	//
45	// You are required to pass in a logger to the call to retain context across
46	// the library boundary.
47	//
48	// LookupVolume returns a bool if the volume is found with the matching volume
49	// or an error as to why the volume could not be found.
50	LookupVolume(lager.Logger, string) (Volume, bool, error)
51
52	// DestroyVolumes deletes the list of volumes that is present on the server. It takes
53	// a string of volumes
54	//
55	// You are required to pass in a logger to the call to retain context across
56	// the library boundary.
57	//
58	// DestroyVolumes returns an error if any of the volume deletion fails. It does not
59	// return an error if volumes were not found on the server.
60	// DestroyVolumes returns an error as to why one or more volumes could not be deleted.
61	DestroyVolumes(lager.Logger, []string) error
62
63	// DestroyVolume deletes the volume with the provided handle that is present on the server.
64	//
65	// You are required to pass in a logger to the call to retain context across
66	// the library boundary.
67	//
68	// DestroyVolume returns an error if the volume deletion fails. It does not
69	// return an error if the volume was not found on the server.
70	DestroyVolume(lager.Logger, string) error
71}
72
73//go:generate counterfeiter . Volume
74
75// Volume represents a volume in the BaggageClaim system.
76type Volume interface {
77	// Handle returns a per-server unique identifier for the volume. The URL of
78	// the server and a handle is enough to universally identify a volume.
79	Handle() string
80
81	// Path returns the filesystem path to the volume on the server. This can be
82	// supplied to other systems in order to let them use the volume.
83	Path() string
84
85	// SetProperty sets a property on the Volume. Properties can be used to
86	// filter the results in the ListVolumes call above.
87	SetProperty(key string, value string) error
88
89	// SetPrivileged namespaces or un-namespaces the UID/GID ownership of the
90	// volume's contents.
91	SetPrivileged(bool) error
92
93	// GetPrivileged returns a bool indicating if the volume is privileged.
94	GetPrivileged() (bool, error)
95
96	// StreamIn calls BaggageClaim API endpoint in order to initialize tarStream
97	// to stream the contents of the Reader into this volume at the specified path.
98	StreamIn(ctx context.Context, path string, encoding Encoding, tarStream io.Reader) error
99
100	StreamOut(ctx context.Context, path string, encoding Encoding) (io.ReadCloser, error)
101
102	// Properties returns the currently set properties for a Volume. An error is
103	// returned if these could not be retrieved.
104	Properties() (VolumeProperties, error)
105
106	// Destroy removes the volume and its contents. Note that it does not
107	// safeguard against child volumes being present.
108	Destroy() error
109}
110
111//go:generate counterfeiter . VolumeFuture
112
113type VolumeFuture interface {
114	// Wait will wait until the future has been provided with a value, which is
115	// either the volume that was created or an error as to why it could not be
116	// created.
117	Wait() (Volume, error)
118
119	// Destroy removes the future from the remote server. This can be used to
120	// either stop waiting for a value, or remove the value from the remote
121	// server after it is no longer needed.
122	Destroy() error
123}
124
125// Volumes represents a list of Volume object.
126type Volumes []Volume
127
128func (v Volumes) Handles() []string {
129	var handles []string
130	for _, vol := range v {
131		handles = append(handles, vol.Handle())
132	}
133	return handles
134}
135
136// VolumeProperties represents the properties for a particular volume.
137type VolumeProperties map[string]string
138
139// VolumeSpec is a specification representing the kind of volume that you'd
140// like from the server.
141type VolumeSpec struct {
142	// Strategy is the information that the server requires to materialise the
143	// volume. There are examples of these in this package.
144	Strategy Strategy
145
146	// Properties is the set of initial properties that the Volume should have.
147	Properties VolumeProperties
148
149	// Privileged is used to determine whether or not we need to perform a UID
150	// translation of the files in the volume so that they can be read by a
151	// non-privileged user.
152	Privileged bool
153}
154
155type Strategy interface {
156	Encode() *json.RawMessage
157}
158
159// ImportStrategy creates a volume by copying a directory from the host.
160type ImportStrategy struct {
161	// The location on the host to import. If the path is a directory, its
162	// contents will be copied in. If the path is a file, it is assumed to be a
163	// .tar.gz file, and its contents will be unpacked in to the volume.
164	Path string
165
166	// Follow symlinks and import them as files instead of links.
167	FollowSymlinks bool
168}
169
170func (strategy ImportStrategy) Encode() *json.RawMessage {
171	payload, _ := json.Marshal(struct {
172		Type           string `json:"type"`
173		Path           string `json:"path"`
174		FollowSymlinks bool   `json:"follow_symlinks"`
175	}{
176		Type:           "import",
177		Path:           strategy.Path,
178		FollowSymlinks: strategy.FollowSymlinks,
179	})
180
181	msg := json.RawMessage(payload)
182	return &msg
183}
184
185// COWStrategy creates a Copy-On-Write layer of another Volume.
186type COWStrategy struct {
187	// The parent volume that we should base the new volume on.
188	Parent Volume
189}
190
191func (strategy COWStrategy) Encode() *json.RawMessage {
192	payload, _ := json.Marshal(struct {
193		Type   string `json:"type"`
194		Volume string `json:"volume"`
195	}{
196		Type:   "cow",
197		Volume: strategy.Parent.Handle(),
198	})
199
200	msg := json.RawMessage(payload)
201	return &msg
202}
203
204// EmptyStrategy created a new empty volume.
205type EmptyStrategy struct{}
206
207func (EmptyStrategy) Encode() *json.RawMessage {
208	msg := json.RawMessage(`{"type":"empty"}`)
209	return &msg
210}
211