### Description

It is impossible to create a follower index with a `$` character in its name (please note, it is possible to create a regular index with `$` in its name).

This could be reproduced by rest request:
```
PUT /my-index/_ccr/follow
{
    "remote_cluster": "my-remote-cluster",
    "leader_index": "my-index$follower"
}
```

or `CcrIntegTestCase`:

```
    public void testFollowIndex() throws Exception {
        assertAcked(leaderClient().admin().indices().prepareCreate("my$index"));
        setupRemoteCluster();
        final PutFollowAction.Request followRequest = putFollow("my$index", "my$index");
        followerClient().execute(PutFollowAction.INSTANCE, followRequest).get();
    }
```

This is happening as during initial follow phase we restore the leader cluster from the remote using a snapshot restore.
Corresponding request is created with index name added to `RestoreSnapshotRequest#renamePattern` (regexp) without escaping `$`.

x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/IndexFollowingIT.java
waitForDocs(firstBatchNumDocs + secondBatchNumDocs, indexer);
            final Map<ShardId, Long> secondBatchNumDocsPerShard = new HashMap<>();
            final ShardStats[] secondBatchShardStats = leaderClient().admin()
                .indices()
                .prepareStats("index1")
                .get()
                .getIndex("index1")
                .getShards();
            for (final ShardStats shardStats : secondBatchShardStats) {
                if (shardStats.getShardRouting().primary()) {
                    final long value = shardStats.getStats().getIndexing().getTotal().getIndexCount() - 1;
                    secondBatchNumDocsPerShard.put(shardStats.getShardRouting().shardId(), value);
                }
            }
            assertBusy(assertTask(numberOfPrimaryShards, secondBatchNumDocsPerShard));
            for (String docId : indexer.getIds()) {
                assertBusy(() -> {
                    final GetResponse getResponse = followerClient().prepareGet("index2", docId).get();
                    assertTrue("Doc with id [" + docId + "] is missing", getResponse.isExists());
                });
            }
            pauseFollow("index2");
            assertMaxSeqNoOfUpdatesIsTransferred(resolveLeaderIndex("index1"), resolveFollowerIndex("index2"), numberOfPrimaryShards);
        }
    }
    public void testFollowIndexWithConcurrentMappingChanges() throws Exception {
        final int numberOfPrimaryShards = randomIntBetween(1, 3);
        final String leaderIndexSettings = getIndexSettings(numberOfPrimaryShards, between(0, 1));
        assertAcked(leaderClient().admin().indices().prepareCreate("index1").setSource(leaderIndexSettings, XContentType.JSON));
        ensureLeaderYellow("index1");