Apache Mesos
docker_archive.hpp
Go to the documentation of this file.
1 // Licensed to the Apache Software Foundation (ASF) under one
2 // or more contributor license agreements. See the NOTICE file
3 // distributed with this work for additional information
4 // regarding copyright ownership. The ASF licenses this file
5 // to you under the Apache License, Version 2.0 (the
6 // "License"); you may not use this file except in compliance
7 // with the License. You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #ifndef __TEST_DOCKER_ARCHIVE_HPP__
18 #define __TEST_DOCKER_ARCHIVE_HPP__
19 
20 #include <process/collect.hpp>
21 #include <process/future.hpp>
22 #include <process/owned.hpp>
23 
24 #include <stout/error.hpp>
25 #include <stout/json.hpp>
26 #include <stout/jsonify.hpp>
27 #include <stout/nothing.hpp>
28 #include <stout/os.hpp>
29 #include <stout/path.hpp>
30 #include <stout/stringify.hpp>
31 #include <stout/try.hpp>
32 
33 #include "common/command_utils.hpp"
34 
36 
37 using process::Failure;
38 using process::Future;
39 
40 namespace mesos {
41 namespace internal {
42 namespace tests {
43 
44 // This represents a docker archive. It has the same format
45 // as that tarball generated by doing a 'docker save'.
47 {
48 public:
49  // Create a docker test image tarball in docker registry directory.
50  // Users can define own entrypoint/cmd as JSON array of JSON string
51  // (e.g., `[\"sh\", \"-c\"]`).
52  //
53  // NOTE: The default value for `environment` includes some environment
54  // variables which will cause problems if they are fed into one of Mesos'
55  // built-in executors. This is on purpose, as the environment variables
56  // of the image should not be passed into built-in executors. Tests that
57  // use a custom executor should consider overriding this default.
59  const std::string& directory,
60  const std::string& name,
61  const std::string& entrypoint = "null",
62  const std::string& cmd = "null",
63  const std::vector<std::string>& environment = {
64  {"LD_LIBRARY_PATH=invalid"},
65  {"LIBPROCESS_IP=invalid"},
66  {"LIBPROCESS_PORT=invalid"}})
67  {
68  Try<Nothing> mkdir = os::mkdir(directory, true);
69  if (mkdir.isError()) {
70  return Failure("Failed to create '" + directory + "': " + mkdir.error());
71  }
72 
73  const std::string imagePath = path::join(directory, name);
74 
75  mkdir = os::mkdir(imagePath);
76  if (mkdir.isError()) {
77  return Failure("Failed to create docker test image directory '" +
78  imagePath + "': " + mkdir.error());
79  }
80 
81  const std::string layerId =
82  "815b809d588c80fd6ddf4d6ac244ad1c01ae4cbe0f91cc7480e306671ee9c346";
83 
84  const std::string layerPath = path::join(imagePath, layerId);
85 
86  // Create docker test image `repositories`.
88  R"~(
89  {
90  "%s": {
91  "latest": "%s"
92  }
93  })~",
94  name,
95  layerId).get()).get();
96 
98  path::join(imagePath, "repositories"),
99  stringify(repositories));
100 
101  if (write.isError()) {
102  return Failure("Failed to save docker test image 'repositories': " +
103  write.error());
104  }
105 
106  mkdir = os::mkdir(layerPath);
107  if (mkdir.isError()) {
108  return Failure("Failed to create docker test image layer '" +
109  layerId + "': " + mkdir.error());
110  }
111 
113  R"~(
114  {
115  "id": "815b809d588c80fd6ddf4d6ac244ad1c01ae4cbe0f91cc7480e306671ee9c346",
116  "created": "2016-03-02T17:16:00.167415955Z",
117  "container": "eb53609036555d26c39bdccfa9850426934bdfde96111d099041689b2251a377",
118  "container_config": {
119  "Hostname": "eb5360903655",
120  "Domainname": "",
121  "User": "",
122  "AttachStdin": false,
123  "AttachStdout": false,
124  "AttachStderr": false,
125  "Tty": false,
126  "OpenStdin": false,
127  "StdinOnce": false,
128  "Env": null,
129  "Cmd": [
130  "/bin/sh",
131  "-c",
132  "#(nop) ADD file:81ba6f20bdb99e6c13c434a577069860b6656908031162083b1ac9c02c71dd9f in /"
133  ],
134  "Image": "",
135  "Volumes": null,
136  "WorkingDir": "",
137  "Entrypoint": null,
138  "OnBuild": null,
139  "Labels": null
140  },
141  "docker_version": "1.9.1",
142  "config": {
143  "Hostname": "eb5360903655",
144  "Domainname": "",
145  "User": "",
146  "AttachStdin": false,
147  "AttachStdout": false,
148  "AttachStderr": false,
149  "Tty": false,
150  "OpenStdin": false,
151  "StdinOnce": false,
152  "Env": %s,
153  "Cmd": %s,
154  "Image": "",
155  "Volumes": null,
156  "WorkingDir": "",
157  "Entrypoint": %s,
158  "OnBuild": null,
159  "Labels": null
160  },
161  "architecture": "amd64",
162  "os": "linux"
163  })~",
164  std::string(jsonify(environment)),
165  cmd,
166  entrypoint).get()).get();
167 
168  write = os::write(
169  path::join(layerPath, "json"),
170  stringify(manifest));
171 
172  if (write.isError()) {
173  return Failure("Failed to save docker test image layer '" + layerId +
174  "': " + write.error());
175  }
176 
177  const std::string rootfsDir = path::join(layerPath, "layer");
178 
179  mkdir = os::mkdir(rootfsDir);
180  if (mkdir.isError()) {
181  return Failure("Failed to create layer rootfs directory '" +
182  rootfsDir + "': " + mkdir.error());
183  }
184 
185  // Create one linux rootfs for the layer.
186  Try<process::Owned<Rootfs>> rootfs = LinuxRootfs::create(rootfsDir);
187  if (rootfs.isError()) {
188  return Failure("Failed to create docker test image rootfs: " +
189  rootfs.error());
190  }
191 
192  Future<Nothing> tarRootfs = command::tar(
193  Path("."),
194  Path(path::join(layerPath, "layer.tar")),
195  rootfsDir);
196 
197  tarRootfs.await();
198 
199  if (!tarRootfs.isReady()) {
200  return Failure(
201  "Failed to tar root filesystem: " +
202  (tarRootfs.isFailed() ? tarRootfs.failure() : "discarded"));
203  }
204 
205  Try<Nothing> rmdir = os::rmdir(rootfsDir);
206  if (rmdir.isError()) {
207  return Failure("Failed to remove layer rootfs directory: " +
208  rmdir.error());
209  }
210 
211  write = os::write(
212  path::join(layerPath, "VERSION"),
213  "1.0");
214 
215  if (write.isError()) {
216  return Failure("Failed to save layer version: " + write.error());
217  }
218 
219  Future<Nothing> tarImage = command::tar(
220  Path("."),
221  Path(path::join(directory, name + ".tar")),
222  imagePath);
223 
224  tarImage.await();
225 
226  if (!tarImage.isReady()) {
227  return Failure(
228  "Failed to tar docker test image: " +
229  (tarImage.isFailed() ? tarImage.failure() : "discarded"));
230  }
231 
232  rmdir = os::rmdir(imagePath);
233  if (rmdir.isError()) {
234  return Failure("Failed to remove image directory: " +
235  rmdir.error());
236  }
237 
238  return Nothing();
239  }
240 };
241 
242 } // namespace tests {
243 } // namespace internal {
244 } // namespace mesos {
245 
246 #endif // __TEST_DOCKER_ARCHIVE_HPP__
Try< Nothing > rmdir(const std::string &directory, bool recursive=true, bool removeRoot=true, bool continueOnError=false)
Definition: rmdir.hpp:42
Definition: nothing.hpp:16
ssize_t write(const int_fd &fd, const void *data, size_t size)
Definition: write.hpp:72
Definition: check.hpp:33
Definition: future.hpp:668
URI manifest(const std::string &repository, const std::string &reference, const std::string &registry, const Option< std::string > &scheme=None(), const Option< int > &port=None())
Definition: docker.hpp:47
std::string join(const std::string &path1, const std::string &path2, const char _separator=os::PATH_SEPARATOR)
Definition: path.hpp:116
static Future< Nothing > create(const std::string &directory, const std::string &name, const std::string &entrypoint="null", const std::string &cmd="null", const std::vector< std::string > &environment={{"LD_LIBRARY_PATH=invalid"},{"LIBPROCESS_IP=invalid"},{"LIBPROCESS_PORT=invalid"}})
Definition: docker_archive.hpp:58
process::Future< Nothing > tar(const Path &input, const Path &output, const Option< Path > &directory=None(), const Option< Compression > &compression=None())
Tar(archive) the file/directory to produce output file.
Environment * environment
Try< Nothing > mkdir(const std::string &directory, bool recursive=true, bool sync=false)
Definition: mkdir.hpp:42
Represents a POSIX or Windows file system path and offers common path manipulations.
Definition: path.hpp:212
Definition: agent.hpp:25
Try< Value > parse(const std::string &s)
Returns the OCI v1 descriptor, image index, image manifest and image configuration from the given str...
Definition: json.hpp:978
static Try error(const E &e)
Definition: try.hpp:43
JSON::Proxy jsonify(const T &)
Definition: jsonify.hpp:702
Definition: docker_archive.hpp:46
Definition: json.hpp:247
Definition: attributes.hpp:24
bool isError() const
Definition: try.hpp:78
Protocol< WriteRequest, WriteResponse > write
static Try< process::Owned< Rootfs > > create(const std::string &root)
Try< std::string > format(const std::string &s, const T &...t)
Definition: format.hpp:58
std::string stringify(int flags)
constexpr const char * name
Definition: shell.hpp:41
Definition: future.hpp:58