docs: def reshard(stream_name, desired_size, force=False,
              region=None, key=None, keyid=None, profile=None):
      """
      Reshard a kinesis stream.  Each call to this function will wait until the stream is ACTIVE,
      then make a single split or merge operation. This function decides where to split or merge
      with the assumption that the ultimate goal is a balanced partition space.
  
      For safety, user must past in force=True; otherwise, the function will dry run.
  
      CLI example::
  
          salt myminion boto_kinesis.reshard my_stream N True region=us-east-1
  
      :return: True if a split or merge was found/performed, False if nothing is needed
      """
      conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
      r = {}
  
      stream_response = get_stream_when_active(stream_name, region, key, keyid, profile)
      if 'error' in stream_response:
          return stream_response
  
      stream_details = stream_response['result']["StreamDescription"]
      min_hash_key, max_hash_key, stream_details = get_info_for_reshard(stream_details)
  
      log.debug("found %s open shards, min_hash_key %s max_hash_key %s",
                len(stream_details["OpenShards"]), min_hash_key, max_hash_key)
  
      # find the first open shard that doesn't match the desired pattern. When we find it,
      # either split or merge (depending on if it's too big or too small), and then return.
      for shard_num, shard in enumerate(stream_details["OpenShards"]):
          shard_id = shard["ShardId"]
          if "EndingSequenceNumber" in shard["SequenceNumberRange"]:
              # something went wrong, there's a closed shard in our open shard list
              log.debug("this should never happen! closed shard %s", shard_id)
              continue
  
          starting_hash_key = shard["HashKeyRange"]["StartingHashKey"]
          ending_hash_key = shard["HashKeyRange"]["EndingHashKey"]
          # this weird math matches what AWS does when you create a kinesis stream
          # with an initial number of shards.
          expected_starting_hash_key = (
              max_hash_key - min_hash_key) / desired_size * shard_num + shard_num
          expected_ending_hash_key = (
              max_hash_key - min_hash_key) / desired_size * (shard_num + 1) + shard_num
          # fix an off-by-one at the end
          if expected_ending_hash_key > max_hash_key:
              expected_ending_hash_key = max_hash_key
  
          log.debug(
              "Shard %s (%s) should start at %s: %s",
              shard_num, shard_id, expected_starting_hash_key,
              starting_hash_key == expected_starting_hash_key
          )
          log.debug(
              "Shard %s (%s) should end at %s: %s",
              shard_num, shard_id, expected_ending_hash_key,
              ending_hash_key == expected_ending_hash_key
          )
  
          if starting_hash_key != expected_starting_hash_key:
              r['error'] = "starting hash keys mismatch, don't know what to do!"
              return r
  
          if ending_hash_key == expected_ending_hash_key:
              continue
  
          if ending_hash_key > expected_ending_hash_key + 1:
              # split at expected_ending_hash_key
              if force:
                  log.debug("%s should end at %s, actual %s, splitting",
                            shard_id, expected_ending_hash_key, ending_hash_key)
                  r = _execute_with_retries(conn,
                                            "split_shard",
                                            StreamName=stream_name,
                                            ShardToSplit=shard_id,
                                            NewStartingHashKey=str(expected_ending_hash_key + 1))  # future lint: disable=blacklisted-function
              else:
                  log.debug("%s should end at %s, actual %s would split",
                            shard_id, expected_ending_hash_key, ending_hash_key)
  
              if 'error' not in r:
                  r['result'] = True
              return r
          else:
              # merge
              next_shard_id = _get_next_open_shard(stream_details, shard_id)
              if not next_shard_id:
                  r['error'] = "failed to find next shard after {0}".format(shard_id)
                  return r
              if force:
                  log.debug("%s should continue past %s, merging with %s",
                            shard_id, ending_hash_key, next_shard_id)
                  r = _execute_with_retries(conn,
                                            "merge_shards",
                                            StreamName=stream_name,
                                            ShardToMerge=shard_id,
                                            AdjacentShardToMerge=next_shard_id)
              else:
                  log.debug("%s should continue past %s, would merge with %s",
                            shard_id, ending_hash_key, next_shard_id)
  
              if 'error' not in r:
                  r['result'] = True
              return r
  
      log.debug("No split or merge action necessary")
      r['result'] = False
      return r