whole_func_string: function joinCluster(provider, bigIp, masterIid, options) {
          const TEMP_USER_NAME_LENGHTH = 10; // these are hex bytes - user name will be 20 chars
          const TEMP_USER_PASSWORD_LENGTH = 24; // use a multiple of 6 to prevent '=' at the end
  
          const now = new Date();
  
          let managementIp;
          let tempPassword;
          let tempUser;
  
          if (!masterIid) {
              return q.reject(new Error('Must have a master ID to join'));
          }
  
          // don't send request too often - master might be in process of syncing
          this.instance.lastJoinRequest = this.instance.lastJoinRequest || new Date(1970, 0);
          const elapsedMsFromLastJoin = now - new Date(this.instance.lastJoinRequest);
          if (elapsedMsFromLastJoin < MIN_MS_BETWEEN_JOIN_REQUESTS) {
              logger.silly('Join request is too soon after last join request.', elapsedMsFromLastJoin, 'ms');
              return q();
          }
  
          logger.info('Joining cluster.');
  
          if (provider.hasFeature(CloudProvider.FEATURE_MESSAGING)) {
              this.instance.lastJoinRequest = now;
              return provider.putInstance(this.instanceId, this.instance)
                  .then((response) => {
                      logger.debug(response);
                      logger.debug('Resetting current device trust');
                      return bigIp.cluster.resetTrust();
                  })
                  .then((response) => {
                      logger.debug(response);
  
                      // Make sure we don't have a current copy of the device group
                      return bigIp.cluster.deleteDeviceGroup(options.deviceGroup);
                  })
                  .then((response) => {
                      logger.debug(response);
                      return bigIp.deviceInfo();
                  })
                  .then((response) => {
                      managementIp = response.managementAddress;
  
                      // Get a random user name to use
                      return cryptoUtil.generateRandomBytes(TEMP_USER_NAME_LENGHTH, 'hex');
                  })
                  .then((respomse) => {
                      tempUser = respomse;
  
                      // Get a random password for the user
                      return cryptoUtil.generateRandomBytes(TEMP_USER_PASSWORD_LENGTH, 'base64');
                  })
                  .then((response) => {
                      tempPassword = response;
  
                      // Create the temp user account
                      return bigIp.onboard.updateUser(tempUser, tempPassword, 'admin');
                  })
                  .then(() => {
                      logger.debug('Sending message to join cluster.');
  
                      const messageData = {
                          host: managementIp,
                          port: bigIp.port,
                          username: tempUser,
                          password: tempPassword,
                          hostname: this.instance.hostname,
                          deviceGroup: options.deviceGroup
                      };
  
                      return prepareMessageData.call(this, provider, masterIid, JSON.stringify(messageData));
                  })
                  .then((preppedData) => {
                      if (preppedData) {
                          return provider.sendMessage(
                              CloudProvider.MESSAGE_ADD_TO_CLUSTER,
                              {
                                  toInstanceId: masterIid,
                                  fromInstanceId: this.instanceId,
                                  data: preppedData
                              }
                          );
                      }
                      logger.debug('No encrypted data received');
                      return q();
                  })
                  .catch((err) => {
                      // need to bubble up nested errors
                      return q.reject(err);
                  });
          }
  
          // not using messaging, just send the request via iControl REST
          const masterInstance = this.instances[masterIid];
  
          this.instance.lastJoinRequest = now;
          return provider.putInstance(this.instanceId, this.instance)
              .then((response) => {
                  logger.debug(response);
                  logger.debug('Resetting current device trust');
                  return bigIp.cluster.resetTrust();
              })
              .then((response) => {
                  logger.debug(response);
  
                  // Make sure we don't have a current copy of the device group
                  return bigIp.cluster.deleteDeviceGroup(options.deviceGroup);
              })
              .then((response) => {
                  logger.debug(response);
                  return provider.getMasterCredentials(masterInstance.mgmtIp, options.port);
              })
              .then((credentials) => {
                  logger.debug('Sending request to join cluster.');
                  return bigIp.cluster.joinCluster(
                      options.deviceGroup,
                      masterInstance.mgmtIp,
                      credentials.username,
                      credentials.password,
                      false,
                      {
                          remotePort: options.port
                      }
                  );
              });
      }
func_code_string: function joinCluster(provider, bigIp, masterIid, options) {
          const TEMP_USER_NAME_LENGHTH = 10; // these are hex bytes - user name will be 20 chars
          const TEMP_USER_PASSWORD_LENGTH = 24; // use a multiple of 6 to prevent '=' at the end
  
          const now = new Date();
  
          let managementIp;
          let tempPassword;
          let tempUser;
  
          if (!masterIid) {
              return q.reject(new Error('Must have a master ID to join'));
          }
  
          // don't send request too often - master might be in process of syncing
          this.instance.lastJoinRequest = this.instance.lastJoinRequest || new Date(1970, 0);
          const elapsedMsFromLastJoin = now - new Date(this.instance.lastJoinRequest);
          if (elapsedMsFromLastJoin < MIN_MS_BETWEEN_JOIN_REQUESTS) {
              logger.silly('Join request is too soon after last join request.', elapsedMsFromLastJoin, 'ms');
              return q();
          }
  
          logger.info('Joining cluster.');
  
          if (provider.hasFeature(CloudProvider.FEATURE_MESSAGING)) {
              this.instance.lastJoinRequest = now;
              return provider.putInstance(this.instanceId, this.instance)
                  .then((response) => {
                      logger.debug(response);
                      logger.debug('Resetting current device trust');
                      return bigIp.cluster.resetTrust();
                  })
                  .then((response) => {
                      logger.debug(response);
  
                      // Make sure we don't have a current copy of the device group
                      return bigIp.cluster.deleteDeviceGroup(options.deviceGroup);
                  })
                  .then((response) => {
                      logger.debug(response);
                      return bigIp.deviceInfo();
                  })
                  .then((response) => {
                      managementIp = response.managementAddress;
  
                      // Get a random user name to use
                      return cryptoUtil.generateRandomBytes(TEMP_USER_NAME_LENGHTH, 'hex');
                  })
                  .then((respomse) => {
                      tempUser = respomse;
  
                      // Get a random password for the user
                      return cryptoUtil.generateRandomBytes(TEMP_USER_PASSWORD_LENGTH, 'base64');
                  })
                  .then((response) => {
                      tempPassword = response;
  
                      // Create the temp user account
                      return bigIp.onboard.updateUser(tempUser, tempPassword, 'admin');
                  })
                  .then(() => {
                      logger.debug('Sending message to join cluster.');
  
                      const messageData = {
                          host: managementIp,
                          port: bigIp.port,
                          username: tempUser,
                          password: tempPassword,
                          hostname: this.instance.hostname,
                          deviceGroup: options.deviceGroup
                      };
  
                      return prepareMessageData.call(this, provider, masterIid, JSON.stringify(messageData));
                  })
                  .then((preppedData) => {
                      if (preppedData) {
                          return provider.sendMessage(
                              CloudProvider.MESSAGE_ADD_TO_CLUSTER,
                              {
                                  toInstanceId: masterIid,
                                  fromInstanceId: this.instanceId,
                                  data: preppedData
                              }
                          );
                      }
                      logger.debug('No encrypted data received');
                      return q();
                  })
                  .catch((err) => {
                      // need to bubble up nested errors
                      return q.reject(err);
                  });
          }
  
          // not using messaging, just send the request via iControl REST
          const masterInstance = this.instances[masterIid];
  
          this.instance.lastJoinRequest = now;
          return provider.putInstance(this.instanceId, this.instance)
              .then((response) => {
                  logger.debug(response);
                  logger.debug('Resetting current device trust');
                  return bigIp.cluster.resetTrust();
              })
              .then((response) => {
                  logger.debug(response);
  
                  // Make sure we don't have a current copy of the device group
                  return bigIp.cluster.deleteDeviceGroup(options.deviceGroup);
              })
              .then((response) => {
                  logger.debug(response);
                  return provider.getMasterCredentials(masterInstance.mgmtIp, options.port);
              })
              .then((credentials) => {
                  logger.debug('Sending request to join cluster.');
                  return bigIp.cluster.joinCluster(
                      options.deviceGroup,
                      masterInstance.mgmtIp,
                      credentials.username,
                      credentials.password,
                      false,
                      {
                          remotePort: options.port
                      }
                  );
              });
      }
func_documentation_string: Called with this bound to the caller