I think this code might be clearer if you do something like

    case DriverStateChanged(driverId, state, exception) => {
      state match {
        case DriverState.FAILED | DriverState.FINISHED | DriverState.KILLED => {
          drivers.find(_.id == driverId) match {
            case Some(driver) => {
              drivers -= driver
              completedDrivers += driver
              persistenceEngine.removeDriver(driver)
              driver.state = state
              driver.exception = exception
              driver.worker.foreach(w => w.removeDriver(driver))
            }
            case None =>
              logWarning(s"Got driver update for unknown driver $driverId")
          }
        }

        case _ =>
          throw new Exception(s"Received unexpected state update for driver $driverId: $state")
      }
    }

it does have more nesting, but it clarifies unambiguously why we're removing the driver.

It might be even cleaner to extract the actual removal code to a removeDriver method like we have for workers and applications. This might be useful for killing waiting drivers too.