Actions `cluster:admin/repository/analyze/blob` and `cluster:admin/repository/analyze/register` and so on are only invoked via the transport service, they do not need to register as client actions so we can drop the `ActionType` instance. Instead the root `RepositoryAnalyzeAction` should create and register all these transport actions itself. But even the root `RepositoryAnalyzeAction` should not be an `ActionType` derivative, we should just declare an `ActionType` instance directly. Moreover we can use `ActionType#localOnly` since the client action does not run on remote clusters.
x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/BlobAnalyzeAction.java
boolean failIfExists,
            Runnable onLastRead,
            ListenableFuture<WriteDetails> stepListener
        ) {
            assert atomic == false || request.targetLength <= MAX_ATOMIC_WRITE_SIZE : "oversized atomic write";
            final RandomBlobContent content = new RandomBlobContent(
                request.getRepositoryName(),
                random.nextLong(),
                task::isCancelled,
                onLastRead
            );
            final AtomicLong throttledNanos = new AtomicLong();
            if (logger.isTraceEnabled()) {
                logger.trace("writing blob [atomic={}, failIfExists={}] for [{}]", atomic, failIfExists, request.getDescription());
            }
            final long startNanos = System.nanoTime();
            ActionListener.completeWith(stepListener, () -> {
                // TODO push some of this writing logic down into the blob container implementation.
                // E.g. for S3 blob containers we would like to choose somewhat more randomly between single-part and multi-part uploads,
                // rather than relying on the usual distinction based on the size of the blob.
                if (atomic || (request.targetLength <= MAX_ATOMIC_WRITE_SIZE && random.nextBoolean())) {
                    final RandomBlobContentBytesReference bytesReference = new RandomBlobContentBytesReference(
                        content,
                        Math.toIntExact(request.getTargetLength())
                    ) {
                        @Override
                        public StreamInput streamInput() throws IOException {
                            return new InputStreamStreamInput(
                                repository.maybeRateLimitSnapshots(super.streamInput(), throttledNanos::addAndGet)
                            );