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"~( { "%s": { "latest": "%s" } })~",
89  name,
90  layerId).get()).get();
91 
93  path::join(imagePath, "repositories"),
94  stringify(repositories));
95 
96  if (write.isError()) {
97  return Failure("Failed to save docker test image 'repositories': " +
98  write.error());
99  }
100 
101  mkdir = os::mkdir(layerPath);
102  if (mkdir.isError()) {
103  return Failure("Failed to create docker test image layer '" +
104  layerId + "': " + mkdir.error());
105  }
106 
108  R"~( { "id": "815b809d588c80fd6ddf4d6ac244ad1c01ae4cbe0f91cc7480e306671ee9c346", "created": "2016-03-02T17:16:00.167415955Z", "container": "eb53609036555d26c39bdccfa9850426934bdfde96111d099041689b2251a377", "container_config": { "Hostname": "eb5360903655", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": null, "Cmd": [ "/bin/sh", "-c", "#(nop) ADD file:81ba6f20bdb99e6c13c434a577069860b6656908031162083b1ac9c02c71dd9f in /" ], "Image": "", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": null }, "docker_version": "1.9.1", "config": { "Hostname": "eb5360903655", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": %s, "Cmd": %s, "Image": "", "Volumes": null, "WorkingDir": "", "Entrypoint": %s, "OnBuild": null, "Labels": null }, "architecture": "amd64", "os": "linux" })~",
109  std::string(jsonify(environment)),
110  cmd,
111  entrypoint).get()).get();
112 
113  write = os::write(
114  path::join(layerPath, "json"),
115  stringify(manifest));
116 
117  if (write.isError()) {
118  return Failure("Failed to save docker test image layer '" + layerId +
119  "': " + write.error());
120  }
121 
122  const std::string rootfsDir = path::join(layerPath, "layer");
123 
124  mkdir = os::mkdir(rootfsDir);
125  if (mkdir.isError()) {
126  return Failure("Failed to create layer rootfs directory '" +
127  rootfsDir + "': " + mkdir.error());
128  }
129 
130  // Create one linux rootfs for the layer.
131  Try<process::Owned<Rootfs>> rootfs = LinuxRootfs::create(rootfsDir);
132  if (rootfs.isError()) {
133  return Failure("Failed to create docker test image rootfs: " +
134  rootfs.error());
135  }
136 
137  Future<Nothing> tarRootfs = command::tar(
138  Path("."),
139  Path(path::join(layerPath, "layer.tar")),
140  rootfsDir);
141 
142  tarRootfs.await();
143 
144  if (!tarRootfs.isReady()) {
145  return Failure(
146  "Failed to tar root filesystem: " +
147  (tarRootfs.isFailed() ? tarRootfs.failure() : "discarded"));
148  }
149 
150  Try<Nothing> rmdir = os::rmdir(rootfsDir);
151  if (rmdir.isError()) {
152  return Failure("Failed to remove layer rootfs directory: " +
153  rmdir.error());
154  }
155 
156  write = os::write(
157  path::join(layerPath, "VERSION"),
158  "1.0");
159 
160  if (write.isError()) {
161  return Failure("Failed to save layer version: " + write.error());
162  }
163 
164  Future<Nothing> tarImage = command::tar(
165  Path("."),
166  Path(path::join(directory, name + ".tar")),
167  imagePath);
168 
169  tarImage.await();
170 
171  if (!tarImage.isReady()) {
172  return Failure(
173  "Failed to tar docker test image: " +
174  (tarImage.isFailed() ? tarImage.failure() : "discarded"));
175  }
176 
177  rmdir = os::rmdir(imagePath);
178  if (rmdir.isError()) {
179  return Failure("Failed to remove image directory: " +
180  rmdir.error());
181  }
182 
183  return Nothing();
184  }
185 };
186 
187 } // namespace tests {
188 } // namespace internal {
189 } // namespace mesos {
190 
191 #endif // __TEST_DOCKER_ARCHIVE_HPP__
192 
bool isReady() const
Definition: future.hpp:1231
Try< Nothing > rmdir(const std::string &directory, bool recursive=true, bool removeRoot=true, bool continueOnError=false)
Definition: rmdir.hpp:43
Definition: nothing.hpp:16
Definition: try.hpp:34
ssize_t write(const WindowsFD &fd, const void *data, size_t size)
Definition: write.hpp:29
Definition: future.hpp:664
bool await(const Duration &duration=Seconds(-1)) const
Definition: future.hpp:1276
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:56
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
Represents a POSIX or Windows file system path and offers common path manipulations.
Definition: path.hpp:145
Try< Nothing > mkdir(const std::string &directory, bool recursive=true)
Definition: mkdir.hpp:31
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:886
static Try error(const E &e)
Definition: try.hpp:42
JSON::Proxy jsonify(const T &)
Definition: jsonify.hpp:779
Definition: docker_archive.hpp:46
Definition: json.hpp:245
bool isError() const
Definition: try.hpp:71
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
const std::string & failure() const
Definition: future.hpp:1336
std::string stringify(int flags)
constexpr const char * name
Definition: shell.hpp:41
bool isFailed() const
Definition: future.hpp:1245
Definition: future.hpp:57