Exemplo n.º 1
0
Option<Error> validateContainerId(const ContainerID& containerId)
{
  const string& id = containerId.value();

  // Check common Mesos ID rules.
  Option<Error> error = common::validation::validateID(id);
  if (error.isSome()) {
    return Error(error->message);
  }

  // Check ContainerID specific rules.
  //
  // Periods are disallowed because our string representation of
  // ContainerID uses periods: <uuid>.<child>.<grandchild>.
  // For example: <uuid>.redis.backup
  //
  // Spaces are disallowed as they can render logs confusing and
  // need escaping on terminals when dealing with paths.
  auto invalidCharacter = [](char c) {
    return  c == '.' || c == ' ';
  };

  if (std::any_of(id.begin(), id.end(), invalidCharacter)) {
    return Error("'ContainerID.value' '" + id + "'"
                 " contains invalid characters");
  }

  // TODO(bmahler): Print the invalid field nicely within the error
  // (e.g. 'parent.parent.parent.value'). For now we only have one
  // level of nesting so it's ok.
  if (containerId.has_parent()) {
    Option<Error> parentError = validateContainerId(containerId.parent());

    if (parentError.isSome()) {
      return Error("'ContainerID.parent' is invalid: " + parentError->message);
    }
  }

  return None();
}
// This test verifies that access to the '/flags' endpoint can be authorized
// without authentication if an authorization rule exists that applies to
// anyone. The authorizer will map the absence of a principal to "ANY".
TYPED_TEST(SlaveAuthorizerTest, AuthorizeFlagsEndpointWithoutPrincipal)
{
  const string endpoint = "flags";

  // Because the authenticators' lifetime is tied to libprocess's lifetime,
  // it may already be set by other tests. We have to unset it here to disable
  // HTTP authentication.
  // TODO(nfnt): Fix this behavior. The authenticator should be unset by
  // every test case that sets it, similar to how it's done for the master.
  http::authentication::unsetAuthenticator(
      slave::DEFAULT_HTTP_AUTHENTICATION_REALM);

  // Setup ACLs so that any principal can access the '/flags' endpoint.
  ACLs acls;
  acls.set_permissive(false);

  mesos::ACL::GetEndpoint* acl = acls.add_get_endpoints();
  acl->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
  acl->mutable_paths()->add_values("/" + endpoint);

  slave::Flags agentFlags = this->CreateSlaveFlags();
  agentFlags.acls = acls;
  agentFlags.authenticate_http = false;
  agentFlags.http_credentials = None();

  // Create an `Authorizer` with the ACLs.
  Try<Authorizer*> create = TypeParam::create(parameterize(acls));
  ASSERT_SOME(create);
  Owned<Authorizer> authorizer(create.get());

  StandaloneMasterDetector detector;
  Try<Owned<cluster::Slave>> agent = this->StartSlave(
      &detector, authorizer.get(), agentFlags);
  ASSERT_SOME(agent);

  Future<Response> response = http::get(agent.get()->pid, endpoint);

  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response)
    << response.get().body;
}
Exemplo n.º 3
0
void CNamingTreeCtrl::OnCopy()
{
  // TODO: Add your command handler code here
  CNamingObject* pObject = GetTreeObject();
  try
  {
    CString IOR = m_pORB->object_to_string(pObject->Object());
    // Copy to the clipboard by using the CEdit control.  This is easier
    // that doing it the right way
    CEdit Temp;
    CRect None(0,0, 1, 1);
    Temp.Create(0, None, this, 0);
    Temp.SetWindowText(IOR);
    Temp.SetSel(0, IOR.GetLength());
    Temp.Copy();
    Temp.PostMessage(WM_CLOSE);
  }
  catch(CORBA::Exception& ex)
  {
    MessageBox(ACE_TEXT_CHAR_TO_TCHAR (ex._rep_id()), ACE_TEXT ("CORBA::Exception"));
  }
}
Exemplo n.º 4
0
// Tests the archive APIs for a simple file.
TEST_F(TarTest, File)
{
  // Create a test file.
  const Path testFile("testfile");
  ASSERT_SOME(createTestFile(testFile));

  // Archive the test file.
  const Path outputTarFile("test.tar");
  AWAIT_ASSERT_READY(command::tar(testFile, outputTarFile, None()));
  ASSERT_TRUE(os::exists(outputTarFile));

  // Remove the test file to make sure untar process creates new test file.
  ASSERT_SOME(os::rm(testFile));
  ASSERT_FALSE(os::exists(testFile));

  // Untar the tarball and verify that the original file is created.
  AWAIT_ASSERT_READY(command::untar(outputTarFile));
  ASSERT_TRUE(os::exists(testFile));

  // Verify that the content is same as original file.
  EXPECT_SOME_EQ("test", os::read(testFile));
}
Exemplo n.º 5
0
namespace uri {

/**
 * Construct an URI with the given parameters. No validation will be
 * performed in this function.
 */
URI construct(
    const std::string& scheme,
    const std::string& path = "",
    const Option<std::string>& host = None(),
    const Option<int>& port = None(),
    const Option<std::string>& query = None(),
    const Option<std::string>& fragment = None(),
    const Option<std::string>& user = None(),
    const Option<std::string>& password = None());

} // namespace uri {
// Tests that an agent endpoint handler forms
// correct queries against the authorizer.
TEST_P(SlaveEndpointTest, AuthorizedRequest)
{
  const string endpoint = GetParam();

  StandaloneMasterDetector detector;

  MockAuthorizer mockAuthorizer;

  Try<Owned<cluster::Slave>> agent = StartSlave(&detector, &mockAuthorizer);
  ASSERT_SOME(agent);

  Future<authorization::Request> request;
  EXPECT_CALL(mockAuthorizer, authorized(_))
    .WillOnce(DoAll(FutureArg<0>(&request),
                    Return(true)));

  Future<Response> response = http::get(
      agent.get()->pid,
      endpoint,
      None(),
      createBasicAuthHeaders(DEFAULT_CREDENTIAL));

  AWAIT_READY(request);

  const string principal = DEFAULT_CREDENTIAL.principal();
  EXPECT_EQ(principal, request.get().subject().value());

  // TODO(bbannier): Once agent endpoint handlers use more than just
  // `GET_ENDPOINT_WITH_PATH` we should factor out the request method
  // and expected authorization action and parameterize
  // `SlaveEndpointTest` on that as well in addition to the endpoint.
  EXPECT_EQ(authorization::GET_ENDPOINT_WITH_PATH, request.get().action());

  EXPECT_EQ("/" + endpoint, request.get().object().value());

  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response)
    << response.get().body;
}
Exemplo n.º 7
0
TEST_F(ProtobufIOTest, Append)
{
  const string file = ".protobuf_io_test_append";

  const size_t writes = 10;
  for (size_t i = 0; i < writes; i++) {
    FrameworkID frameworkId;
    frameworkId.set_value(stringify(i));

    Try<Nothing> result = ::protobuf::append(file, frameworkId);
    ASSERT_SOME(result);
  }

  Try<int_fd> fd = os::open(
      file,
      O_CREAT | O_RDONLY | O_CLOEXEC,
      S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);

  ASSERT_SOME(fd);

  Result<FrameworkID> read = None();
  size_t reads = 0;
  while (true) {
    read = ::protobuf::read<FrameworkID>(fd.get());
    if (!read.isSome()) {
      break;
    }

    EXPECT_EQ(read->value(), stringify(reads++));
  }

  // Ensure we've hit the end of the file without reading a partial
  // protobuf.
  ASSERT_TRUE(read.isNone());
  ASSERT_EQ(writes, reads);

  os::close(fd.get());
}
Exemplo n.º 8
0
// Returns the netlink link object associated with a given link by its
// interface index. Returns None if the link is not found.
inline Result<Netlink<struct rtnl_link>> get(int index)
{
  Try<Netlink<struct nl_sock>> socket = routing::socket();
  if (socket.isError()) {
    return Error(socket.error());
  }

  // Dump all the netlink link objects from kernel. Note that the flag
  // AF_UNSPEC means all available families.
  struct nl_cache* c = NULL;
  int error = rtnl_link_alloc_cache(socket.get().get(), AF_UNSPEC, &c);
  if (error != 0) {
    return Error(nl_geterror(error));
  }

  Netlink<struct nl_cache> cache(c);
  struct rtnl_link* l = rtnl_link_get(cache.get(), index);
  if (l == NULL) {
    return None();
  }

  return Netlink<struct rtnl_link>(l);
}
Exemplo n.º 9
0
  // Launch 'ping' using the given capabilities and user.
  Try<Subprocess> ping(
      const Set<Capability>& capabilities,
      const Option<string>& user = None())
  {
    CapabilitiesTestHelper helper;

    helper.flags.user = user;
    helper.flags.capabilities = capabilities::convert(capabilities);

    vector<string> argv = {
      "test-helper",
      CapabilitiesTestHelper::NAME
    };

    return subprocess(
        getTestHelperPath("test-helper"),
        argv,
        Subprocess::FD(STDIN_FILENO),
        Subprocess::FD(STDOUT_FILENO),
        Subprocess::FD(STDERR_FILENO),
        NO_SETSID,
        &helper.flags);
  }
Exemplo n.º 10
0
  Try<Nothing> createTestFile(
      const Path& file,
      const Option<Path>& directory = None())
  {
    const Path testFile(
        directory.isSome() ?
        path::join(directory.get(), file.value): file);

    const string& testFileDir = testFile.dirname();
    if (!os::exists(testFileDir)) {
      Try<Nothing> mkdir = os::mkdir(testFileDir, true);
      if (mkdir.isError()) {
        return Error("Failed to create test directory: " + mkdir.error());
      }
    }

    Try<Nothing> write = os::write(testFile, "test");
    if (write.isError()) {
      return Error("Failed to create to test file: " + write.error());
    }

    return Nothing();
  }
Exemplo n.º 11
0
// Tests that unauthorized requests for an agent endpoint are properly rejected.
TEST_P(SlaveEndpointTest, UnauthorizedRequest)
{
  const string endpoint = GetParam();

  StandaloneMasterDetector detector;

  MockAuthorizer mockAuthorizer;

  Try<Owned<cluster::Slave>> agent = StartSlave(&detector, &mockAuthorizer);
  ASSERT_SOME(agent);

  EXPECT_CALL(mockAuthorizer, authorized(_))
    .WillOnce(Return(false));

  Future<Response> response = http::get(
      agent.get()->pid,
      endpoint,
      None(),
      createBasicAuthHeaders(DEFAULT_CREDENTIAL));

  AWAIT_EXPECT_RESPONSE_STATUS_EQ(Forbidden().status, response)
    << response.get().body;
}
Exemplo n.º 12
0
int main(int argc, char** argv)
{
#ifdef __WINDOWS__
  // Initialize the Windows socket stack.
  process::Winsock winsock;
#endif

  // Initialize Google Mock/Test.
  testing::InitGoogleMock(&argc, argv);

  // Initialize libprocess.
  process::initialize(None(), process::DEFAULT_HTTP_AUTHENTICATION_REALM);

  // NOTE: Windows does not support signal semantics required for these
  // handlers to be useful.
#ifndef __WINDOWS__
  // Install GLOG's signal handler.
  google::InstallFailureSignalHandler();

  // We reset the GLOG's signal handler for SIGTERM because
  // 'SubprocessTest.Status' sends SIGTERM to a subprocess which
  // results in a stack trace otherwise.
  os::signals::reset(SIGTERM);
#endif // __WINDOWS__

  // Add the libprocess test event listeners.
  ::testing::TestEventListeners& listeners =
    ::testing::UnitTest::GetInstance()->listeners();

  listeners.Append(process::ClockTestEventListener::instance());
  listeners.Append(process::FilterTestEventListener::instance());

  int result = RUN_ALL_TESTS();

  process::finalize();
  return result;
}
Exemplo n.º 13
0
// Test that the environment decorator hook adds a new environment
// variable to the executor runtime.
// Test hook adds a new environment variable "FOO" to the executor
// with a value "bar". We validate the hook by verifying the value
// of this environment variable.
TEST_F(HookTest, VerifySlaveExecutorEnvironmentDecorator)
{
  const string& directory = os::getcwd(); // We're inside a temporary sandbox.
  Fetcher fetcher;

  Try<MesosContainerizer*> containerizer =
    MesosContainerizer::create(CreateSlaveFlags(), false, &fetcher);
  ASSERT_SOME(containerizer);

  ContainerID containerId;
  containerId.set_value("test_container");

  // Test hook adds a new environment variable "FOO" to the executor
  // with a value "bar". A '0' (success) exit status for the following
  // command validates the hook.
  process::Future<bool> launch = containerizer.get()->launch(
      containerId,
      CREATE_EXECUTOR_INFO("executor", "test $FOO = 'bar'"),
      directory,
      None(),
      SlaveID(),
      process::PID<Slave>(),
      false);
  AWAIT_READY(launch);
  ASSERT_TRUE(launch.get());

  // Wait on the container.
  process::Future<containerizer::Termination> wait =
    containerizer.get()->wait(containerId);
  AWAIT_READY(wait);

  // Check the executor exited correctly.
  EXPECT_TRUE(wait.get().has_status());
  EXPECT_EQ(0, wait.get().status());

  delete containerizer.get();
}
Exemplo n.º 14
0
// Tests the archive APIs for a simple directory.
TEST_F(TarTest, Directory)
{
  const Path testDir("test_dir");
  const Path testFile(path::join(testDir, "testfile"));

  // Create a test file in the test directory.
  ASSERT_SOME(createTestFile(testFile));

  // Archive the test directory.
  const Path outputTarFile("test_dir.tar");
  AWAIT_ASSERT_READY(command::tar(testDir, outputTarFile, None()));
  ASSERT_TRUE(os::exists(outputTarFile));

  // Remove the test directory to make sure untar process creates new test file.
  ASSERT_SOME(os::rmdir(testDir));
  ASSERT_FALSE(os::exists(testDir));

  // Untar the tarball and verify that the original directory is created.
  AWAIT_ASSERT_READY(command::untar(outputTarFile));
  ASSERT_TRUE(os::exists(testDir));

  // Verify that the content is same as original file.
  EXPECT_SOME_EQ("test", os::read(testFile));
}
Exemplo n.º 15
0
    std::deque<http::Response*> decode(const char* data, size_t length)
    {
        size_t parsed = http_parser_execute(&parser, &settings, data, length);

        if (parsed != length) {
            // TODO(bmahler): joyent/http-parser exposes error reasons.
            failure = true;

            // If we're still writing the body, fail the writer!
            if (writer.isSome()) {
                http::Pipe::Writer writer_ = writer.get(); // Remove const.
                writer_.fail("failed to decode body");
                writer = None();
            }
        }

        if (!responses.empty()) {
            std::deque<http::Response*> result = responses;
            responses.clear();
            return result;
        }

        return std::deque<http::Response*>();
    }
Exemplo n.º 16
0
  OK(const JSON::Value& value, const Option<std::string>& jsonp = None())
  {
    type = BODY;

    status = "200 OK";

    std::ostringstream out;

    if (jsonp.isSome()) {
      out << jsonp.get() << "(";
    }

    out << value;

    if (jsonp.isSome()) {
      out << ");";
      headers["Content-Type"] = "text/javascript";
    } else {
      headers["Content-Type"] = "application/json";
    }

    headers["Content-Length"] = stringify(out.str().size());
    body = out.str().data();
  }
Exemplo n.º 17
0
const std::string Logging::TOGGLE_HELP()
{
  return HELP(
    TLDR(
        "Sets the logging verbosity level for a specified duration."),
    DESCRIPTION(
        "The libprocess library uses [glog][glog] for logging. The library",
        "only uses verbose logging which means nothing will be output unless",
        "the verbosity level is set (by default it's 0, libprocess uses levels"
        " 1, 2, and 3).",
        "",
        "**NOTE:** If your application uses glog this will also affect",
        "your verbose logging.",
        "",
        "Query parameters:",
        "",
        ">        level=VALUE          Verbosity level (e.g., 1, 2, 3)",
        ">        duration=VALUE       Duration to keep verbosity level",
        ">                             toggled (e.g., 10secs, 15mins, etc.)"),
    AUTHENTICATION(true),
    None(),
    REFERENCES(
        "[glog]: https://code.google.com/p/google-glog"));
}
Exemplo n.º 18
0
void FlagsBase::add(
    Option<T> Flags::*option,
    const Name& name,
    const Option<Name>& alias,
    const std::string& help,
    F validate)
{
  // Don't bother adding anything if the pointer is NULL.
  if (option == NULL) {
    return;
  }

  Flags* flags = dynamic_cast<Flags*>(this);
  if (flags == NULL) {
    ABORT("Attempted to add flag '" + name.value +
          "' with incompatible type");
  }

  Flag flag;
  flag.name = name;
  flag.alias = alias;
  flag.help = help;
  flag.boolean = typeid(T) == typeid(bool);

  // NOTE: See comment above in Flags::T* overload of FLagsBase::add
  // for why we need to pass FlagsBase* (or const FlagsBase&) as a
  // parameter.

  flag.load =
    [option](FlagsBase* base, const std::string& value) -> Try<Nothing> {
    Flags* flags = dynamic_cast<Flags*>(base);
    if (flags != NULL) {
      // NOTE: 'fetch' "retrieves" the value if necessary and then
      // invokes 'parse'. See 'fetch' for more details.
      Try<T> t = fetch<T>(value);
      if (t.isSome()) {
        flags->*option = Some(t.get());
      } else {
        return Error("Failed to load value '" + value + "': " + t.error());
      }
    }

    return Nothing();
  };

  flag.stringify = [option](const FlagsBase& base) -> Option<std::string> {
    const Flags* flags = dynamic_cast<const Flags*>(&base);
    if (flags != NULL) {
      if ((flags->*option).isSome()) {
        return stringify((flags->*option).get());
      }
    }
    return None();
  };

  flag.validate = [option, validate](const FlagsBase& base) -> Option<Error> {
    const Flags* flags = dynamic_cast<const Flags*>(&base);
    if (flags != NULL) {
      return validate(flags->*option);
    }
    return None();
  };

  add(flag);
}
Exemplo n.º 19
0
void FlagsBase::add(
    T1 Flags::*t1,
    const Name& name,
    const Option<Name>& alias,
    const std::string& help,
    const T2& t2,
    F validate)
{
  // Don't bother adding anything if the pointer is NULL.
  if (t1 == NULL) {
    return;
  }

  Flags* flags = dynamic_cast<Flags*>(this);
  if (flags == NULL) {
    ABORT("Attempted to add flag '" + name.value +
          "' with incompatible type");
  } else {
    flags->*t1 = t2; // Set the default.
  }

  Flag flag;
  flag.name = name;
  flag.alias = alias;
  flag.help = help;
  flag.boolean = typeid(T1) == typeid(bool);

  // NOTE: We need to take FlagsBase* (or const FlagsBase&) as the
  // first argument to 'load', 'stringify', and 'validate' so that we
  // use the correct instance of FlagsBase. In other words, we can't
  // capture 'this' here because it's possible that the FlagsBase
  // object that we're working with when we invoke FlagsBase::add is
  // not the same instance as 'this' when these lambdas get invoked.

  flag.load = [t1](FlagsBase* base, const std::string& value) -> Try<Nothing> {
    Flags* flags = dynamic_cast<Flags*>(base);
    if (base != NULL) {
      // NOTE: 'fetch' "retrieves" the value if necessary and then
      // invokes 'parse'. See 'fetch' for more details.
      Try<T1> t = fetch<T1>(value);
      if (t.isSome()) {
        flags->*t1 = t.get();
      } else {
        return Error("Failed to load value '" + value + "': " + t.error());
      }
    }

    return Nothing();
  };

  flag.stringify = [t1](const FlagsBase& base) -> Option<std::string> {
    const Flags* flags = dynamic_cast<const Flags*>(&base);
    if (flags != NULL) {
      return stringify(flags->*t1);
    }
    return None();
  };

  flag.validate = [t1, validate](const FlagsBase& base) -> Option<Error> {
    const Flags* flags = dynamic_cast<const Flags*>(&base);
    if (flags != NULL) {
      return validate(flags->*t1);
    }
    return None();
  };

  // Update the help string to include the default value.
  flag.help += help.size() > 0 && help.find_last_of("\n\r") != help.size() - 1
    ? " (default: " // On same line, add space.
    : "(default: "; // On newline.
  flag.help += stringify(t2);
  flag.help += ")";

  add(flag);
}
Exemplo n.º 20
0
Try<TaskState> TaskState::recover(
    const string& rootDir,
    const SlaveID& slaveId,
    const FrameworkID& frameworkId,
    const ExecutorID& executorId,
    const UUID& uuid,
    const TaskID& taskId,
    bool strict)
{
  TaskState state;
  state.id = taskId;
  string message;

  // Read the task info.
  string path = paths::getTaskInfoPath(
      rootDir, slaveId, frameworkId, executorId, uuid, taskId);
  if (!os::exists(path)) {
    // This could happen if the slave died after creating the task
    // directory but before it checkpointed the task info.
    LOG(WARNING) << "Failed to find task info file '" << path << "'";
    return state;
  }

  const Result<Task>& task = ::protobuf::read<Task>(path);

  if (task.isError()) {
    message = "Failed to read task info from '" + path + "': " + task.error();

    if (strict) {
      return Error(message);
    } else {
      LOG(WARNING) << message;
      state.errors++;
      return state;
    }
  }

  if (task.isNone()) {
    // This could happen if the slave died after opening the file for
    // writing but before it checkpointed anything.
    LOG(WARNING) << "Found empty task info file '" << path << "'";
    return state;
  }

  state.info = task.get();

  // Read the status updates.
  path = paths::getTaskUpdatesPath(
      rootDir, slaveId, frameworkId, executorId, uuid, taskId);
  if (!os::exists(path)) {
    // This could happen if the slave died before it checkpointed
    // any status updates for this task.
    LOG(WARNING) << "Failed to find status updates file '" << path << "'";
    return state;
  }

  // Open the status updates file for reading and writing (for truncating).
  const Try<int>& fd = os::open(path, O_RDWR);

  if (fd.isError()) {
    message = "Failed to open status updates file '" + path +
              "': " + fd.error();

    if (strict) {
      return Error(message);
    } else {
      LOG(WARNING) << message;
      state.errors++;
      return state;
    }
  }

  // Now, read the updates.
  Result<StatusUpdateRecord> record = None();
  while (true) {
    // Ignore errors due to partial protobuf read.
    record = ::protobuf::read<StatusUpdateRecord>(fd.get(), true);

    if (!record.isSome()) {
      break;
    }

    if (record.get().type() == StatusUpdateRecord::UPDATE) {
      state.updates.push_back(record.get().update());
    } else {
      state.acks.insert(UUID::fromBytes(record.get().uuid()));
    }
  }

  // Always truncate the file to contain only valid updates.
  // NOTE: This is safe even though we ignore partial protobuf
  // read errors above, because the 'fd' is properly set to the
  // end of the last valid update by 'protobuf::read()'.
  if (ftruncate(fd.get(), lseek(fd.get(), 0, SEEK_CUR)) != 0) {
    return ErrnoError(
        "Failed to truncate status updates file '" + path + "'");
  }

  // After reading a non-corrupted updates file, 'record' should be 'none'.
  if (record.isError()) {
    message = "Failed to read status updates file  '" + path +
              "': " + record.error();

    if (strict) {
      return Error(message);
    } else {
      LOG(WARNING) << message;
      state.errors++;
      return state;
    }
  }

  // Close the updates file.
  const Try<Nothing>& close = os::close(fd.get());

  if (close.isError()) {
    message = "Failed to close status updates file '" + path +
              "': " + close.error();

    if (strict) {
      return Error(message);
    } else {
      LOG(WARNING) << message;
      state.errors++;
      return state;
    }
  }

  return state;
}
// This test verifies that the image specified in the volume will be
// properly provisioned and mounted into the container if container
// root filesystem is not specified.
TEST_P(VolumeImageIsolatorTest, ROOT_ImageInVolumeWithoutRootFilesystem)
{
  string registry = path::join(sandbox.get(), "registry");
  AWAIT_READY(DockerArchive::create(registry, "test_image"));

  slave::Flags flags = CreateSlaveFlags();
  flags.isolation = "filesystem/linux,volume/image,docker/runtime";
  flags.docker_registry = registry;
  flags.docker_store_dir = path::join(sandbox.get(), "store");
  flags.image_providers = "docker";

  Fetcher fetcher(flags);

  Try<MesosContainerizer*> create =
    MesosContainerizer::create(flags, true, &fetcher);

  ASSERT_SOME(create);

  Owned<Containerizer> containerizer(create.get());

  ContainerID containerId;
  containerId.set_value(id::UUID::random().toString());

  ContainerInfo container = createContainerInfo(
      None(),
      {createVolumeFromDockerImage("rootfs", "test_image", Volume::RW)});

  CommandInfo command = createCommandInfo("test -d rootfs/bin");

  ExecutorInfo executor = createExecutorInfo(
      "test_executor",
      nesting ? createCommandInfo("sleep 1000") : command);

  if (!nesting) {
    executor.mutable_container()->CopyFrom(container);
  }

  string directory = path::join(flags.work_dir, "sandbox");
  ASSERT_SOME(os::mkdir(directory));

  Future<Containerizer::LaunchResult> launch = containerizer->launch(
      containerId,
      createContainerConfig(None(), executor, directory),
      map<string, string>(),
      None());

  AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch);

  Future<Option<ContainerTermination>> wait = containerizer->wait(containerId);

  if (nesting) {
    ContainerID nestedContainerId;
    nestedContainerId.mutable_parent()->CopyFrom(containerId);
    nestedContainerId.set_value(id::UUID::random().toString());

    launch = containerizer->launch(
        nestedContainerId,
        createContainerConfig(command, container),
        map<string, string>(),
        None());

    AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch);

    wait = containerizer->wait(nestedContainerId);
  }

  AWAIT_READY(wait);
  ASSERT_SOME(wait.get());
  ASSERT_TRUE(wait->get().has_status());
  EXPECT_WEXITSTATUS_EQ(0, wait->get().status());

  if (nesting) {
    Future<Option<ContainerTermination>> termination =
      containerizer->destroy(containerId);

    AWAIT_READY(termination);
    ASSERT_SOME(termination.get());
    ASSERT_TRUE(termination->get().has_status());
    EXPECT_WTERMSIG_EQ(SIGKILL, termination.get()->status());
  }
}
Exemplo n.º 22
0
Option<Error> validate(
    const mesos::agent::Call& call,
    const Option<string>& principal)
{
  if (!call.IsInitialized()) {
    return Error("Not initialized: " + call.InitializationErrorString());
  }

  if (!call.has_type()) {
    return Error("Expecting 'type' to be present");
  }

  switch (call.type()) {
    case mesos::agent::Call::UNKNOWN:
      return None();

    case mesos::agent::Call::GET_HEALTH:
      return None();

    case mesos::agent::Call::GET_FLAGS:
      return None();

    case mesos::agent::Call::GET_VERSION:
      return None();

    case mesos::agent::Call::GET_METRICS:
      if (!call.has_get_metrics()) {
        return Error("Expecting 'get_metrics' to be present");
      }
      return None();

    case mesos::agent::Call::GET_LOGGING_LEVEL:
      return None();

    case mesos::agent::Call::SET_LOGGING_LEVEL:
      if (!call.has_set_logging_level()) {
        return Error("Expecting 'set_logging_level' to be present");
      }
      return None();

    case mesos::agent::Call::LIST_FILES:
      if (!call.has_list_files()) {
        return Error("Expecting 'list_files' to be present");
      }
      return None();

    case mesos::agent::Call::READ_FILE:
      if (!call.has_read_file()) {
        return Error("Expecting 'read_file' to be present");
      }
      return None();

    case mesos::agent::Call::GET_STATE:
      return None();

    case mesos::agent::Call::GET_CONTAINERS:
      return None();

    case mesos::agent::Call::GET_FRAMEWORKS:
      return None();

    case mesos::agent::Call::GET_EXECUTORS:
      return None();

    case mesos::agent::Call::GET_TASKS:
      return None();

    case mesos::agent::Call::GET_AGENT:
      return None();

    case mesos::agent::Call::LAUNCH_NESTED_CONTAINER: {
      if (!call.has_launch_nested_container()) {
        return Error("Expecting 'launch_nested_container' to be present");
      }

      Option<Error> error = validation::container::validateContainerId(
          call.launch_nested_container().container_id());

      if (error.isSome()) {
        return Error("'launch_nested_container.container_id' is invalid"
                     ": " + error->message);
      }

      // The parent `ContainerID` is required, so that we know
      // which container to place it underneath.
      if (!call.launch_nested_container().container_id().has_parent()) {
        return Error("Expecting 'launch_nested_container.container_id.parent'"
                     " to be present");
      }

      if (call.launch_nested_container().has_command()) {
        error = common::validation::validateCommandInfo(
            call.launch_nested_container().command());
        if (error.isSome()) {
          return Error("'launch_nested_container.command' is invalid"
                       ": " + error->message);
        }
      }

      return None();
    }

    case mesos::agent::Call::WAIT_NESTED_CONTAINER: {
      if (!call.has_wait_nested_container()) {
        return Error("Expecting 'wait_nested_container' to be present");
      }

      Option<Error> error = validation::container::validateContainerId(
          call.wait_nested_container().container_id());

      if (error.isSome()) {
        return Error("'wait_nested_container.container_id' is invalid"
                     ": " + error->message);
      }

      // Nested containers always have at least one parent.
      if (!call.wait_nested_container().container_id().has_parent()) {
        return Error("Expecting 'wait_nested_container.container_id.parent'"
                     " to be present");
      }

      return None();
    }

    case mesos::agent::Call::KILL_NESTED_CONTAINER: {
      if (!call.has_kill_nested_container()) {
        return Error("Expecting 'kill_nested_container' to be present");
      }

      Option<Error> error = validation::container::validateContainerId(
          call.kill_nested_container().container_id());

      if (error.isSome()) {
        return Error("'kill_nested_container.container_id' is invalid"
                     ": " + error->message);
      }

      // Nested containers always have at least one parent.
      if (!call.kill_nested_container().container_id().has_parent()) {
        return Error("Expecting 'kill_nested_container.container_id.parent'"
                     " to be present");
      }

      return None();
    }

    case mesos::agent::Call::REMOVE_NESTED_CONTAINER: {
      if (!call.has_remove_nested_container()) {
        return Error("Expecting 'remove_nested_container' to be present");
      }

      Option<Error> error = validation::container::validateContainerId(
          call.remove_nested_container().container_id());

      if (error.isSome()) {
        return Error("'remove_nested_container.container_id' is invalid"
                     ": " + error->message);
      }

      // Nested containers always have at least one parent.
      if (!call.remove_nested_container().container_id().has_parent()) {
        return Error("Expecting 'remove_nested_container.container_id.parent'"
                     " to be present");
      }

      return None();
    }

    case mesos::agent::Call::LAUNCH_NESTED_CONTAINER_SESSION: {
      if (!call.has_launch_nested_container_session()) {
        return Error(
            "Expecting 'launch_nested_container_session' to be present");
      }

      Option<Error> error = validation::container::validateContainerId(
          call.launch_nested_container_session().container_id());

      if (error.isSome()) {
        return Error("'launch_nested_container_session.container_id' is invalid"
                     ": " + error->message);
      }

      // The parent `ContainerID` is required, so that we know
      // which container to place it underneath.
      if (!call.launch_nested_container_session().container_id().has_parent()) {
        return Error(
            "Expecting 'launch_nested_container_session.container_id.parent'"
            " to be present");
      }

      if (call.launch_nested_container_session().has_command()) {
        error = common::validation::validateCommandInfo(
            call.launch_nested_container_session().command());
        if (error.isSome()) {
          return Error("'launch_nested_container_session.command' is invalid"
                       ": " + error->message);
        }
      }

      return None();
    }

    case mesos::agent::Call::ATTACH_CONTAINER_INPUT: {
      if (!call.has_attach_container_input()) {
        return Error("Expecting 'attach_container_input' to be present");
      }

      if (!call.attach_container_input().has_type()) {
        return Error("Expecting 'attach_container_input.type' to be present");
      }

      switch (call.attach_container_input().type()) {
        case mesos::agent::Call::AttachContainerInput::UNKNOWN:
          return Error("'attach_container_input.type' is unknown");

        case mesos::agent::Call::AttachContainerInput::CONTAINER_ID: {
          Option<Error> error = validation::container::validateContainerId(
              call.attach_container_input().container_id());

          if (error.isSome()) {
            return Error("'attach_container_input.container_id' is invalid"
                ": " + error->message);
          }
        }

        case mesos::agent::Call::AttachContainerInput::PROCESS_IO:
          return None();
      }

      UNREACHABLE();
    }

    case mesos::agent::Call::ATTACH_CONTAINER_OUTPUT: {
      if (!call.has_attach_container_output()) {
        return Error("Expecting 'attach_container_output' to be present");
      }

      Option<Error> error = validation::container::validateContainerId(
          call.attach_container_output().container_id());

      if (error.isSome()) {
        return Error("'attach_container_output.container_id' is invalid"
                     ": " + error->message);
      }

      return None();
    }
  }

  UNREACHABLE();
}
Exemplo n.º 23
0
Option<Error> validate(const mesos::executor::Call& call)
{
  if (!call.IsInitialized()) {
    return Error("Not initialized: " + call.InitializationErrorString());
  }

  if (!call.has_type()) {
    return Error("Expecting 'type' to be present");
  }

  // All calls should have executor id set.
  if (!call.has_executor_id()) {
    return Error("Expecting 'executor_id' to be present");
  }

  // All calls should have framework id set.
  if (!call.has_framework_id()) {
    return Error("Expecting 'framework_id' to be present");
  }

  switch (call.type()) {
    case mesos::executor::Call::SUBSCRIBE: {
      if (!call.has_subscribe()) {
        return Error("Expecting 'subscribe' to be present");
      }
      return None();
    }

    case mesos::executor::Call::UPDATE: {
      if (!call.has_update()) {
        return Error("Expecting 'update' to be present");
      }

      const TaskStatus& status = call.update().status();

      if (!status.has_uuid()) {
        return Error("Expecting 'uuid' to be present");
      }

      Try<UUID> uuid = UUID::fromBytes(status.uuid());
      if (uuid.isError()) {
        return uuid.error();
      }

      if (status.has_executor_id() &&
          status.executor_id().value()
          != call.executor_id().value()) {
        return Error("ExecutorID in Call: " +
                     call.executor_id().value() +
                     " does not match ExecutorID in TaskStatus: " +
                     call.update().status().executor_id().value()
                     );
      }

      if (status.source() != TaskStatus::SOURCE_EXECUTOR) {
        return Error("Received Call from executor " +
                     call.executor_id().value() +
                     " of framework " +
                     call.framework_id().value() +
                     " with invalid source, expecting 'SOURCE_EXECUTOR'"
                     );
      }

      if (status.state() == TASK_STAGING) {
        return Error("Received TASK_STAGING from executor " +
                     call.executor_id().value() +
                     " of framework " +
                     call.framework_id().value() +
                     " which is not allowed"
                     );
      }

      // TODO(alexr): Validate `check_status` is present if
      // the corresponding `TaskInfo.check` has been defined.

      if (status.has_check_status()) {
        Option<Error> validate =
          checks::validation::checkStatusInfo(status.check_status());

        if (validate.isSome()) {
          return validate.get();
        }
      }

      return None();
    }

    case mesos::executor::Call::MESSAGE: {
      if (!call.has_message()) {
        return Error("Expecting 'message' to be present");
      }
      return None();
    }

    case mesos::executor::Call::UNKNOWN: {
      return None();
    }
  }
  UNREACHABLE();
}
Exemplo n.º 24
0
// Generate a `Process` object for the process associated with `pid`. If
// process is not found, we return `None`; error is reserved for the case where
// something went wrong.
inline Result<Process> process(pid_t pid)
{
  // Find process with pid.
  Result<PROCESSENTRY32> entry = process_entry(pid);

  if (entry.isError()) {
    return WindowsError(entry.error());
  } else if (entry.isNone()) {
    return None();
  }

  HANDLE process_handle = ::OpenProcess(
      PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ,
      false,
      pid);

  if (process_handle == INVALID_HANDLE_VALUE) {
    return WindowsError("os::process: Call to `OpenProcess` failed");
  }

  SharedHandle safe_process_handle(process_handle, ::CloseHandle);

  // Get Windows Working set size (Resident set size in linux).
  PROCESS_MEMORY_COUNTERS proc_mem_counters;
  BOOL get_process_memory_info = ::GetProcessMemoryInfo(
      safe_process_handle.get(),
      &proc_mem_counters,
      sizeof(proc_mem_counters));

  if (!get_process_memory_info) {
    return WindowsError("os::process: Call to `GetProcessMemoryInfo` failed");
  }

  // Get session Id.
  pid_t session_id;
  BOOL process_id_to_session_id = ::ProcessIdToSessionId(pid, &session_id);

  if (!process_id_to_session_id) {
    return WindowsError("os::process: Call to `ProcessIdToSessionId` failed");
  }

  // Get Process CPU time.
  FILETIME create_filetime, exit_filetime, kernel_filetime, user_filetime;
  BOOL get_process_times = ::GetProcessTimes(
      safe_process_handle.get(),
      &create_filetime,
      &exit_filetime,
      &kernel_filetime,
      &user_filetime);

  if (!get_process_times) {
    return WindowsError("os::process: Call to `GetProcessTimes` failed");
  }

  // Get utime and stime.
  ULARGE_INTEGER lKernelTime, lUserTime; // In 100 nanoseconds.
  lKernelTime.HighPart = kernel_filetime.dwHighDateTime;
  lKernelTime.LowPart = kernel_filetime.dwLowDateTime;
  lUserTime.HighPart = user_filetime.dwHighDateTime;
  lUserTime.LowPart = user_filetime.dwLowDateTime;

  Try<Duration> utime = Nanoseconds(lKernelTime.QuadPart * 100);
  Try<Duration> stime = Nanoseconds(lUserTime.QuadPart * 100);

  return Process(
      pid,
      entry.get().th32ParentProcessID,         // Parent process id.
      0,                                       // Group id.
      session_id,
      Bytes(proc_mem_counters.WorkingSetSize),
      utime.isSome() ? utime.get() : Option<Duration>::none(),
      stime.isSome() ? stime.get() : Option<Duration>::none(),
      entry.get().szExeFile,                   // Executable filename.
      false);                                  // Is not zombie process.
}
Exemplo n.º 25
0
// Suspends execution of the calling process until a child specified by `pid`
// has changed state. Unlike the POSIX standard function `::waitpid`, this
// function does not use -1 and 0 to signify errors and nonblocking return.
// Instead, we return `Result<pid_t>`:
//   * In case of error, we return `Error` rather than -1. For example, we
//     would return an `Error` in case of `EINVAL`.
//   * In case of nonblocking return, we return `None` rather than 0. For
//     example, if we pass `WNOHANG` in the `options`, we would expect 0 to be
//     returned in the case that children specified by `pid` exist, but have
//     not changed state yet. In this case we return `None` instead.
//
// NOTE: There are important differences between the POSIX and Windows
// implementations of this function:
//   * On POSIX, `pid_t` is a signed number, but on Windows, PIDs are `DWORD`,
//     which is `unsigned long`. Thus, if we use `DWORD` to represent the `pid`
//     argument, passing -1 as the `pid` would (on most modern servers)
//     silently convert to a really large `pid`. This is undesirable.
//   * Since it is important to be able to detect -1 has been passed to
//     `os::waitpid`, as a matter of practicality, we choose to:
//     (1) Use `long` to represent the `pid` argument.
//     (2) Disable using any value <= 0 for `pid` on Windows.
//   * This decision is pragmatic. The reasoning is:
//     (1) The Windows code paths call `os::waitpid` in only a handful of
//         places, and in none of these conditions do we need `-1` as a value.
//     (2) Since PIDs virtually never take on values outside the range of
//         vanilla signed `long` it is likely that an accidental conversion
//         will never happen.
//     (3) Even though it is not formalized in the C specification, the
//         implementation of `long` on the vast majority of production servers
//         is 2's complement, so we expect that when we accidentally do
//         implicitly convert from `unsigned long` to `long`, we will "wrap
//         around" to negative values. And since we've disabled the negative
//         `pid` in the Windows implementation, we should error out.
//   * Finally, on Windows, we currently do not check that the process we are
//     attempting to await is a child process.
inline Result<pid_t> waitpid(long pid, int* status, int options)
{
  const bool wait_for_child = (options & WNOHANG) == 0;

  // NOTE: Windows does not implement pids <= 0.
  if (pid <= 0) {
    errno = ENOSYS;
    return ErrnoError(
        "os::waitpid: Value of pid is '" + stringify(pid) +
        "'; the Windows implementation currently does not allow values <= 0");
  } else if (options != 0 && options != WNOHANG) {
    // NOTE: We only support `options == 0` or `options == WNOHANG`. On Windows
    // no flags other than `WNOHANG` are supported.
    errno = ENOSYS;
    return ErrnoError(
        "os::waitpid: Only flag `WNOHANG` is implemented on Windows");
  }

  // TODO(hausdorff): Check that `pid` is one of the child processes. If not,
  // set `errno` to `ECHILD` and return -1.

  // Open the child process as a safe `SharedHandle`.
  const HANDLE process = ::OpenProcess(
      PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
      FALSE,
      static_cast<DWORD>(pid));

  if (process == NULL) {
    return WindowsError("os::waitpid: Failed to open process for pid '" +
                        stringify(pid) + "'");
  }

  SharedHandle scoped_process(process, ::CloseHandle);

  // If `WNOHANG` flag is set, don't wait. Otherwise, wait for child to
  // terminate.
  const DWORD wait_time = wait_for_child ? INFINITE : 0;
  const DWORD wait_results = ::WaitForSingleObject(
      scoped_process.get(),
      wait_time);

  // Verify our wait exited correctly.
  const bool state_signaled = wait_results == WAIT_OBJECT_0;
  if (options == 0 && !state_signaled) {
    // If `WNOHANG` is not set, then we should have stopped waiting only for a
    // state change in `scoped_process`.
    errno = ECHILD;
    return WindowsError(
        "os::waitpid: Failed to wait for pid '" + stringify(pid) +
        "'. `::WaitForSingleObject` should have waited for child process to " +
        "exit, but returned code '" + stringify(wait_results) +
        "' instead");
  } else if (wait_for_child && !state_signaled &&
             wait_results != WAIT_TIMEOUT) {
    // If `WNOHANG` is set, then a successful wait should report either a
    // timeout (since we set the time to wait to `0`), or a successful state
    // change of `scoped_process`. Anything else is an error.
    errno = ECHILD;
    return WindowsError(
        "os::waitpid: Failed to wait for pid '" + stringify(pid) +
        "'. `ENOHANG` flag was passed in, so `::WaitForSingleObject` should " +
        "have either returned `WAIT_OBJECT_0` or `WAIT_TIMEOUT` (the " +
        "timeout was set to 0, because we are not waiting for the child), " +
        "but instead returned code '" + stringify(wait_results) + "'");
  }

  if (!wait_for_child && wait_results == WAIT_TIMEOUT) {
    // Success. `ENOHANG` was set and we got a timeout, so return `None` (POSIX
    // `::waitpid` would return 0 here).
    return None();
  }

  // Attempt to retrieve exit code from child process. Store that exit code in
  // the `status` variable if it's `NULL`.
  DWORD child_exit_code = 0;
  if (!::GetExitCodeProcess(scoped_process.get(), &child_exit_code)) {
    errno = ECHILD;
    return WindowsError(
        "os::waitpid: Successfully waited on child process with pid '" +
        std::to_string(pid) + "', but could not retrieve exit code");
  }

  if (status != NULL) {
    *status = child_exit_code;
  }

  // Success. Return pid of the child process for which the status is reported.
  return pid;
}
Exemplo n.º 26
0
inline Try<std::set<pid_t>> pids()
{
  return pids(None(), None());
}
Exemplo n.º 27
0
 Info (const hashmap<std::string, ContainerNetwork>& _containerNetworks,
       const Option<std::string> _rootfs = None())
   : containerNetworks (_containerNetworks),
     rootfs(_rootfs) {}
Exemplo n.º 28
0
 // 'name' is the unique name for the instance of Gauge being constructed.
 // It will be the key exposed in the JSON endpoint.
 //
 // 'f' is the function that is called when the Metric value is requested.
 // The user of `Gauge` must ensure that `f` is safe to execute up until
 // the removal of the `Gauge` (via `process::metrics::remove(...)`) is
 // complete.
 explicit PushGauge(const std::string& name)
   : Metric(name, None()), data(new Data()) {}
Exemplo n.º 29
0
LevelDBStorage::LevelDBStorage()
  : db(nullptr), first(None())
{
  // Nothing to see here.
}
Exemplo n.º 30
0
 TestHttpServer() : ProcessBase("TestHttpServer")
 {
   route("/test", None(), &TestHttpServer::test);
 }