Exemplo n.º 1
0
bool operator == (const ExecutorInfo& left, const ExecutorInfo& right)
{
    return left.executor_id() == right.executor_id() &&
           left.data() == right.data() &&
           Resources(left.resources()) == Resources(right.resources()) &&
           left.command() == right.command() &&
           left.framework_id() == right.framework_id() &&
           left.name() == right.name() &&
           left.source() == right.source() &&
           left.container() == right.container() &&
           left.discovery() == right.discovery();
}
Exemplo n.º 2
0
inline bool operator == (const ExecutorInfo& left, const ExecutorInfo& right)
{
  return left.executor_id() == right.executor_id() &&
    left.has_framework_id() == right.has_framework_id() &&
    (!left.has_framework_id() ||
    (left.framework_id() == right.framework_id())) &&
    left.command() == right.command() &&
    Resources(left.resources()) == Resources(right.resources()) &&
    left.has_name() == right.has_name() &&
    (!left.has_name() || (left.name() == right.name())) &&
    left.has_source() == right.has_source() &&
    (!left.has_source() || (left.source() == right.source())) &&
    left.has_data() == right.has_data() &&
    (!left.has_data() || (left.data() == right.data()));
}
Exemplo n.º 3
0
  // In this hook, we create a new environment variable "FOO" and set
  // it's value to "bar".
  virtual Result<Environment> slaveExecutorEnvironmentDecorator(
      const ExecutorInfo& executorInfo)
  {
    LOG(INFO) << "Executing 'slaveExecutorEnvironmentDecorator' hook";

    Environment environment;

    if (executorInfo.command().has_environment()) {
      environment.CopyFrom(executorInfo.command().environment());
    }

    Environment::Variable* variable = environment.add_variables();
    variable->set_name("FOO");
    variable->set_value("bar");

    return environment;
  }
Exemplo n.º 4
0
process::Future<Option<ContainerPrepareInfo>> CalicoIsolatorProcess::prepare(
            const ContainerID& containerId,
            const ExecutorInfo& executorInfo,
            const std::string& directory,
            const Option<std::string>& user)
{
    LOG(INFO) << "CalicoIsolator::prepare";

    std::string ipAddress = "auto";
    std::string profile = "none";
    foreach (const Environment_Variable& var,
             executorInfo.command().environment().variables()) {
        LOG(INFO) << "ENV: " << var.name() << "=" << var.value();
        if (var.name() == "CALICO_IP") {
            ipAddress = var.value();
        }
        else if (var.name() == "CALICO_PROFILE") {
            profile = var.value();
        }
    }

    if (ipAddress == "auto") {
        std::vector<std::string> argv(3);
        argv[0] = "python";
        argv[1] = ipamPath;
        argv[2] = "assign_ipv4";
        Try<process::Subprocess> child = process::subprocess(
                                             pythonPath,
                                             argv,
                                             process::Subprocess::PIPE(),
                                             process::Subprocess::PIPE(),
                                             process::Subprocess::PIPE());
        CHECK_SOME(child);
        waitpid(child.get().pid(), NULL, 0);
        ipAddress = process::io::read(child.get().out().get()).get();
        LOG(INFO) << "Got IP " << ipAddress << " from IPAM.";
    }

    LOG(INFO) << "LIBPROCESS_IP=" << ipAddress;

    ContainerPrepareInfo prepareInfo;

    Environment::Variable* variable =
        prepareInfo.mutable_environment()->add_variables();
    variable->set_name("LIBPROCESS_IP");
    variable->set_value(ipAddress);

    foreach (const Parameter& parameter, parameters.parameter()) {
        if (parameter.key() == initializationKey) {
            prepareInfo.add_commands()->set_value(parameter.value());
        }
    }

    (*infos)[containerId] = new Info(ipAddress, profile);
    (*executors)[executorInfo.executor_id()] = containerId;

    return prepareInfo;
}
Exemplo n.º 5
0
  // This hook locates the file created by environment decorator hook
  // and deletes it.
  virtual Try<Nothing> slaveRemoveExecutorHook(
      const FrameworkInfo& frameworkInfo,
      const ExecutorInfo& executorInfo)
  {
    LOG(INFO) << "Executing 'slaveRemoveExecutorHook'";

    foreach (const Environment::Variable& variable,
        executorInfo.command().environment().variables()) {
      if (variable.name() == testEnvironmentVariableName) {
        string path = variable.value();
        // The removeExecutor hook may be called multiple times; thus
        // we ignore the subsequent calls.
        if (os::isfile(path)) {
          CHECK_SOME(os::rm(path));
        }
        break;
      }
    }
    return Nothing();
  }
Exemplo n.º 6
0
Environment HookManager::slaveExecutorEnvironmentDecorator(
    ExecutorInfo executorInfo)
{
  Lock lock(&mutex);

  foreachpair (const string& name, Hook* hook, availableHooks) {
    const Result<Environment>& result =
      hook->slaveExecutorEnvironmentDecorator(executorInfo);
    if (result.isSome()) {
      // Update executorInfo to include newer environment variables
      // so that the next hook module can extend the environment
      // variables instead of simply overwriting them.
      executorInfo.mutable_command()->mutable_environment()->MergeFrom(
          result.get());
    } else if (result.isError()) {
      LOG(WARNING) << "Slave environment decorator hook failed for module '"
                   << name << "': " << result.error();
    }
  }

  return executorInfo.command().environment();
}
Exemplo n.º 7
0
    Future<bool> launch(
        const ContainerID& containerId,
        const Option<TaskInfo>& taskInfo,
        const ExecutorInfo& executorInfo,
        const string& directory,
        const Option<string>& user,
        const SlaveID& slaveId,
        const map<string, string>& environment,
        bool checkpoint)
    {
        CHECK(!terminatedContainers.contains(containerId))
                << "Failed to launch nested container " << containerId
                << " for executor '" << executorInfo.executor_id() << "'"
                << " of framework " << executorInfo.framework_id()
                << " because this ContainerID is being re-used with"
                << " a previously terminated container";

        CHECK(!containers_.contains(containerId))
                << "Failed to launch container " << containerId
                << " for executor '" << executorInfo.executor_id() << "'"
                << " of framework " << executorInfo.framework_id()
                << " because it is already launched";

        CHECK(executors.contains(executorInfo.executor_id()))
                << "Failed to launch executor '" << executorInfo.executor_id() << "'"
                << " of framework " << executorInfo.framework_id()
                << " because it is unknown to the containerizer";

        containers_[containerId] = Owned<ContainerData>(new ContainerData());
        containers_.at(containerId)->executorId = executorInfo.executor_id();
        containers_.at(containerId)->frameworkId = executorInfo.framework_id();

        // We need to synchronize all reads and writes to the environment
        // as this is global state.
        //
        // TODO(jmlvanre): Even this is not sufficient, as other aspects
        // of the code may read an environment variable while we are
        // manipulating it. The better solution is to pass the environment
        // variables into the fork, or to set them on the command line.
        // See MESOS-3475.
        static std::mutex mutex;

        synchronized(mutex) {
            // Since the constructor for `MesosExecutorDriver` reads
            // environment variables to load flags, even it needs to
            // be within this synchronization section.
            //
            // Prepare additional environment variables for the executor.
            // TODO(benh): Need to get flags passed into the TestContainerizer
            // in order to properly use here.
            slave::Flags flags;
            flags.recovery_timeout = Duration::zero();

            // We need to save the original set of environment variables so we
            // can reset the environment after calling 'driver->start()' below.
            hashmap<string, string> original = os::environment();

            foreachpair (const string& name, const string variable, environment) {
                os::setenv(name, variable);
            }

            // TODO(benh): Can this be removed and done exclusively in the
            // 'executorEnvironment()' function? There are other places in the
            // code where we do this as well and it's likely we can do this once
            // in 'executorEnvironment()'.
            foreach (const Environment::Variable& variable,
                     executorInfo.command().environment().variables()) {
                os::setenv(variable.name(), variable.value());
            }

            os::setenv("MESOS_LOCAL", "1");

            const Owned<ExecutorData>& executorData =
                executors.at(executorInfo.executor_id());

            if (executorData->executor != nullptr) {
                executorData->driver = Owned<MesosExecutorDriver>(
                                           new MesosExecutorDriver(executorData->executor));
                executorData->driver->start();
            } else {
                shared_ptr<v1::MockHTTPExecutor> executor =
                    executorData->v1ExecutorMock;
                executorData->v1Library = Owned<v1::executor::TestMesos>(
                                              new v1::executor::TestMesos(ContentType::PROTOBUF, executor));
            }

            os::unsetenv("MESOS_LOCAL");

            // Unset the environment variables we set by resetting them to their
            // original values and also removing any that were not part of the
            // original environment.
            foreachpair (const string& name, const string& value, original) {
                os::setenv(name, value);
            }
// Prepare runs BEFORE a task is started
// will check if the volume is already mounted and if not,
// will mount the volume.
// A container can ask for multiple mounts, but if
// there are any problems parsing or mounting even one
// mount, we want to exit with an error and no new
// mounted volumes. Goal: make all mounts or none.
Future<Option<ContainerPrepareInfo>> DockerVolumeDriverIsolator::prepare(
  const ContainerID& containerId,
  const ExecutorInfo& executorInfo,
  const string& directory,
  const Option<string>& user)
{
  LOG(INFO) << "Preparing external storage for container: "
            << stringify(containerId);

  // Get things we need from task's environment in ExecutoInfo.
  if (!executorInfo.command().has_environment()) {
    // No environment means no external volume specification.
    // Not an error, just nothing to do, so return None.
    LOG(INFO) << "No environment specified for container ";
    return None();
  }

  // In the future we aspire to accepting a json mount list
  // some un-used "scaffolding" is in place now for this
  JSON::Object environment;
  JSON::Array jsonVariables;

  // We accept <environment-var-name>#, where # can be 1-9, saved in array[#].
  // We also accept <environment-var-name>, saved in array[0].
  static constexpr size_t ARRAY_SIZE = 10;
  std::array<std::string, ARRAY_SIZE> deviceDriverNames;
  std::array<std::string, ARRAY_SIZE> volumeNames;
  std::array<std::string, ARRAY_SIZE> mountOptions;

  // Iterate through the environment variables,
  // looking for the ones we need.
  foreach (const auto &variable,
           executorInfo.command().environment().variables()) {
    JSON::Object variableObject;
    variableObject.values["name"] = variable.name();
    variableObject.values["value"] = variable.value();
    jsonVariables.values.push_back(variableObject);

    if (strings::startsWith(variable.name(), VOL_NAME_ENV_VAR_NAME)) {

      if (containsProhibitedChars(variable.value())) {
        LOG(ERROR) << "Environment variable " << variable.name()
                   << " rejected because it's value contains "
                   << "prohibited characters";
        return Failure("prepare() failed due to illegal environment variable");
      }

      const size_t prefixLength = strlen(VOL_NAME_ENV_VAR_NAME);

      if (variable.name().length() == prefixLength) {
        volumeNames[0] = variable.value();
      } else if (variable.name().length() == (prefixLength+1)) {
        char digit = variable.name().data()[prefixLength];

        if (isdigit(digit)) {
          size_t index =
              std::atoi(variable.name().substr(prefixLength).c_str());

          if (index !=0) {
            volumeNames[index] = variable.value();
          }
        }
      }
      LOG(INFO) << "External volume name (" << variable.value()
                << ") parsed from environment";
    } else if (strings::startsWith(variable.name(), VOL_DRIVER_ENV_VAR_NAME)) {

      if (containsProhibitedChars(variable.value())) {
        LOG(ERROR) << "Environment variable " << variable.name()
            << " rejected because it's value contains prohibited characters";
        return Failure("prepare() failed due to illegal environment variable");
      }

      const size_t prefixLength = strlen(VOL_DRIVER_ENV_VAR_NAME);

      if (variable.name().length() == prefixLength) {
        deviceDriverNames[0] = variable.value();
      } else if (variable.name().length() == (prefixLength+1)) {

        char digit = variable.name().data()[prefixLength];

        if (isdigit(digit)) {
          size_t index =
              std::atoi(variable.name().substr(prefixLength).c_str());
          if (index !=0) {
            deviceDriverNames[index] = variable.value();
          }
        }
      }
    } else if (strings::startsWith(variable.name(), VOL_OPTS_ENV_VAR_NAME)) {

      if (containsProhibitedChars(variable.value())) {
        LOG(ERROR) << "Environment variable " << variable.name()
            << " rejected because it's value contains prohibited characters";
        return Failure("prepare() failed due to illegal environment variable");
      }

      const size_t prefixLength = strlen(VOL_OPTS_ENV_VAR_NAME);

      if (variable.name().length() == prefixLength) {
        mountOptions[0] = variable.value();
      } else if (variable.name().length() == (prefixLength+1)) {

        char digit = variable.name().data()[prefixLength];

        if (isdigit(digit)) {

          size_t index =
              std::atoi(variable.name().substr(prefixLength).c_str());

          if (index !=0) {
            mountOptions[index] = variable.value();
          }
        }
      }
    } else if (variable.name() == JSON_VOLS_ENV_VAR_NAME) {
      //JSON::Value jsonVolArray = JSON::parse(variable.value());
    }
  }
  // TODO: json environment is not used yet
  environment.values["variables"] = jsonVariables;

  // requestedExternalMounts is all mounts requested by container.
  std::vector<process::Owned<ExternalMount>> requestedExternalMounts;
  // unconnectedExternalMounts is the subset of those not already
  // in use by another container.
  std::vector<process::Owned<ExternalMount>> unconnectedExternalMounts;
  // prevConnectedExternalMounts is the subset of those that are
  // in use by another container.
  std::vector<process::Owned<ExternalMount>> prevConnectedExternalMounts;

  // Not using iterator because we access all 3 arrays using common index.
  for (size_t i = 0; i < volumeNames.size(); i++) {

    if (volumeNames[i].empty()) {
      continue;
    }

    LOG(INFO) << "Validating mount name " << volumeNames[i];

    if (deviceDriverNames[i].empty()) {
      deviceDriverNames[i] = VOL_DRIVER_DEFAULT;
    }

    process::Owned<ExternalMount> mount(
        new ExternalMount(deviceDriverNames[i],
            volumeNames[i],
            mountOptions[i]));

    // Check for duplicates in environment.
    bool duplicateInEnv = false;
    for (const auto &ent : requestedExternalMounts) {

      if (ent.get()->getExternalMountId() ==
             mount.get()->getExternalMountId()) {
        duplicateInEnv = true;
        break;
      }
    }

    if (duplicateInEnv) {
      LOG(INFO) << "Duplicate mount request(" << *mount
                << ") in environment will be ignored";
      continue;
    }

    requestedExternalMounts.push_back(mount);

    // Now check if another container is already using this same mount.
    bool mountInUse = false;
    for (const auto &ent : infos) {

      if (ent.second.get()->getExternalMountId() ==
             mount.get()->getExternalMountId()) {
        mountInUse = true;
        prevConnectedExternalMounts.push_back(ent.second);
        LOG(INFO) << "Requested mount(" << *mount
                  << ") is already mounted by another container";
        break;
      }
    }

    if (!mountInUse) {
      unconnectedExternalMounts.push_back(mount);
    }
  }

  // As we connect mounts we will build a list of successful mounts.
  // We need this because, if there is a failure, we need to unmount these.
  // The goal is we mount either ALL or NONE.
  std::vector<process::Owned<ExternalMount>> successfulExternalMounts;
  for (const auto &iter : unconnectedExternalMounts) {
    std::string mountpoint = mount(*iter, "prepare()");

    if (!mountpoint.empty()) {
      // Need to construct a newExternalMount because we just
      // learned the mountpoint.
      process::Owned<ExternalMount> newmount(
            new ExternalMount(iter->deviceDriverName,
                              iter->volumeName,
                iter->mountOptions,
                mountpoint));
      successfulExternalMounts.push_back(newmount);
    } else {
      // Once any mount attempt fails, give up on whole list
      // and attempt to undo the mounts we already made.
      LOG(ERROR) << "Mount failed during prepare()";

      for (const auto &unmountme : successfulExternalMounts) {

        if (unmount(*unmountme, "prepare()-reverting mounts after failure")) {
          LOG(ERROR) << "During prepare() of a container requesting multiple "
                     << "mounts, a mount failure occurred after making "
                     << "at least one mount and a second failure occurred "
                     << "while attempting to remove the earlier mount(s)";
          break;
        }
      }
      return Failure("prepare() failed during mount attempt");
    }
  }

  // Note: infos has a record for each mount associated with this container
  // even if the mount is also used by another container.
  for (const auto &iter : prevConnectedExternalMounts) {
    infos.put(containerId, iter);
  }

  for (const auto &iter : successfulExternalMounts) {
    infos.put(containerId, iter);
  }

  //checkpoint the dvdi mounts for persistence
  std::string myinfosout;
  dumpInfos(myinfosout);
  mesos::internal::slave::state::checkpoint(mountJsonFilename, myinfosout);

  return None();
}
Exemplo n.º 9
0
Future<bool> TestContainerizer::_launch(
    const ContainerID& containerId,
    const ExecutorInfo& executorInfo,
    const string& directory,
    const Option<string>& user,
    const SlaveID& slaveId,
    const PID<slave::Slave>& slavePid,
    bool checkpoint)
{
    CHECK(!drivers.contains(containerId))
            << "Failed to launch executor " << executorInfo.executor_id()
            << " of framework " << executorInfo.framework_id()
            << " because it is already launched";

    CHECK(executors.contains(executorInfo.executor_id()))
            << "Failed to launch executor " << executorInfo.executor_id()
            << " of framework " << executorInfo.framework_id()
            << " because it is unknown to the containerizer";

    // Store mapping from (frameworkId, executorId) -> containerId to facilitate
    // easy destroy from tests.
    std::pair<FrameworkID, ExecutorID> key(executorInfo.framework_id(),
                                           executorInfo.executor_id());
    containers_[key] = containerId;

    Executor* executor = executors[executorInfo.executor_id()];

    // We need to synchronize all reads and writes to the environment as this is
    // global state.
    // TODO(jmlvanre): Even this is not sufficient, as other aspects of the code
    // may read an environment variable while we are manipulating it. The better
    // solution is to pass the environment variables into the fork, or to set them
    // on the command line. See MESOS-3475.
    static std::mutex mutex;

    synchronized(mutex) {
        // Since the constructor for `MesosExecutorDriver` reads environment
        // variables to load flags, even it needs to be within this synchronization
        // section.
        Owned<MesosExecutorDriver> driver(new MesosExecutorDriver(executor));
        drivers[containerId] = driver;

        // Prepare additional environment variables for the executor.
        // TODO(benh): Need to get flags passed into the TestContainerizer
        // in order to properly use here.
        slave::Flags flags;
        flags.recovery_timeout = Duration::zero();

        // We need to save the original set of environment variables so we
        // can reset the environment after calling 'driver->start()' below.
        hashmap<string, string> original = os::environment();

        const map<string, string> environment = executorEnvironment(
                executorInfo,
                directory,
                slaveId,
                slavePid,
                checkpoint,
                flags);

        foreachpair (const string& name, const string variable, environment) {
            os::setenv(name, variable);
        }

        // TODO(benh): Can this be removed and done exlusively in the
        // 'executorEnvironment()' function? There are other places in the
        // code where we do this as well and it's likely we can do this once
        // in 'executorEnvironment()'.
        foreach (const Environment::Variable& variable,
                 executorInfo.command().environment().variables()) {
            os::setenv(variable.name(), variable.value());
        }

        os::setenv("MESOS_LOCAL", "1");

        driver->start();

        os::unsetenv("MESOS_LOCAL");

        // Unset the environment variables we set by resetting them to their
        // original values and also removing any that were not part of the
        // original environment.
        foreachpair (const string& name, const string& value, original) {
            os::setenv(name, value);
        }