public ClusterStateBundle.FeedBlock inferContentClusterFeedBlockOrNull(ContentCluster cluster) {
        if (!feedBlockEnabled) {
            return null;
        }
        var nodeInfos = cluster.getNodeInfos();
        var exhaustions = enumerateNodeResourceExhaustionsAcrossAllNodes(nodeInfos);
        if (exhaustions.isEmpty()) {
            return null;
        }
        int maxDescriptions = 3;
        String description = exhaustions.stream()
                .limit(maxDescriptions)
                .map(NodeResourceExhaustion::toExhaustionAddedDescription)
                .collect(Collectors.joining(", "));
        if (exhaustions.size() > maxDescriptions) {
            description += String.format(" (... and %d more)", exhaustions.size() - maxDescriptions);
        }
        description = decoratedMessage(cluster, description);
        // FIXME we currently will trigger a cluster state recomputation even if the number of
        // exhaustions is greater than what is returned as part of the description. Though at
        // that point, cluster state recomputations will be the least of your worries...!
        return ClusterStateBundle.FeedBlock.blockedWith(description, exhaustions);
    }
@Test
    void node_must_be_available_in_reported_state_to_trigger_feed_block() {
        var calc = new ResourceExhaustionCalculator(true, mapOf(usage("disk", 0.5), usage("memory", 0.8)));
        var cf = createFixtureWithReportedUsages(forNode(1, usage("disk", 0.51), usage("memory", 0.79)),
                forNode(2, usage("disk", 0.6), usage("memory", 0.6)));
        cf.reportStorageNodeState(1, State.DOWN);
        cf.reportStorageNodeState(2, State.DOWN);
        var feedBlock = calc.inferContentClusterFeedBlockOrNull(cf.cluster());
        assertNull(feedBlock);
    }