text:
package org.elasticsearch.cluster.metadata;

import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.ResourceAlreadyExistsException;
import org.elasticsearch.Version;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.create.CreateIndexClusterStateUpdateRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeType;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.ESAllocationTestCase;
import org.elasticsearch.cluster.EmptyClusterInfoService;
import org.elasticsearch.cluster.block.ClusterBlocks;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.node.DiscoveryNodeRole;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.routing.RoutingTable;
import org.elasticsearch.cluster.routing.allocation.AllocationService;
import org.elasticsearch.cluster.routing.allocation.DataTier;
import org.elasticsearch.cluster.routing.allocation.allocator.BalancedShardsAllocator;
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders;
import org.elasticsearch.cluster.routing.allocation.decider.MaxRetryAllocationDecider;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.common.settings.IndexScopedSettings;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexModule;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.query.SearchExecutionContext;
import org.elasticsearch.indices.EmptySystemIndices;
import org.elasticsearch.indices.InvalidAliasNameException;
import org.elasticsearch.indices.InvalidIndexNameException;
import org.elasticsearch.indices.ShardLimitValidator;
import org.elasticsearch.indices.SystemIndexDescriptor;
import org.elasticsearch.indices.SystemIndices;
import org.elasticsearch.snapshots.EmptySnapshotsInfoService;
import org.elasticsearch.test.ClusterServiceUtils;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.VersionUtils;
import org.elasticsearch.test.gateway.TestGatewayAllocator;
import org.elasticsearch.threadpool.TestThreadPool;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xcontent.NamedXContentRegistry;
import org.elasticsearch.xcontent.XContentFactory;
import org.hamcrest.Matchers;
import org.junit.Before;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.Collections.emptyMap;
import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_NUMBER_OF_ROUTING_SHARDS_SETTING;
import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING;
import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_READ_ONLY_BLOCK;
import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS;
import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS;
import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_READ_ONLY;
import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_VERSION_CREATED;
import static org.elasticsearch.cluster.metadata.MetadataCreateIndexService.aggregateIndexSettings;
import static org.elasticsearch.cluster.metadata.MetadataCreateIndexService.buildIndexMetadata;
import static org.elasticsearch.cluster.metadata.MetadataCreateIndexService.clusterStateCreateIndex;
import static org.elasticsearch.cluster.metadata.MetadataCreateIndexService.getIndexNumberOfRoutingShards;
import static org.elasticsearch.cluster.metadata.MetadataCreateIndexService.parseV1Mappings;
import static org.elasticsearch.cluster.metadata.MetadataCreateIndexService.resolveAndValidateAliases;
import static org.elasticsearch.index.IndexSettings.INDEX_SOFT_DELETES_SETTING;
import static org.elasticsearch.indices.ShardLimitValidatorTests.createTestShardLimitService;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.hasValue;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.startsWith;

public class MetadataCreateIndexServiceTests extends ESTestCase {

    private AliasValidator aliasValidator;
    private CreateIndexClusterStateUpdateRequest request;
    private SearchExecutionContext searchExecutionContext;
    private IndexNameExpressionResolver indexNameExpressionResolver;

    @Before
    public void setupCreateIndexRequestAndAliasValidator() {
        indexNameExpressionResolver = new IndexNameExpressionResolver(new ThreadContext(Settings.EMPTY), EmptySystemIndices.INSTANCE);
        aliasValidator = new AliasValidator();
        request = new CreateIndexClusterStateUpdateRequest("create index", "test", "test");
        Settings indexSettings = Settings.builder()
            .put(SETTING_VERSION_CREATED, Version.CURRENT)
            .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
            .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1)
            .build();
        searchExecutionContext = new SearchExecutionContext(
            0,
            0,
            new IndexSettings(IndexMetadata.builder("test").settings(indexSettings).build(), indexSettings),
            null,
            null,
            null,
            null,
            null,
            null,
            xContentRegistry(),
            writableRegistry(),
            null,
            null,
            () -> randomNonNegativeLong(),
            null,
            null,
            () -> true,
            null,
            emptyMap()
        );
    }

    private ClusterState createClusterState(String name, int numShards, int numReplicas, Settings settings) {
        int numRoutingShards = settings.getAsInt(IndexMetadata.INDEX_NUMBER_OF_ROUTING_SHARDS_SETTING.getKey(), numShards);
        Metadata.Builder metaBuilder = Metadata.builder();
        IndexMetadata indexMetadata = IndexMetadata.builder(name)
            .settings(settings(Version.CURRENT).put(settings))
            .numberOfShards(numShards)
            .numberOfReplicas(numReplicas)
            .setRoutingNumShards(numRoutingShards)
            .build();
        metaBuilder.put(indexMetadata, false);
        Metadata metadata = metaBuilder.build();
        RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
        routingTableBuilder.addAsNew(metadata.index(name));

        RoutingTable routingTable = routingTableBuilder.build();
        ClusterState clusterState = ClusterState.builder(
            org.elasticsearch.cluster.ClusterName.CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY)
        ).metadata(metadata).routingTable(routingTable).blocks(ClusterBlocks.builder().addBlocks(indexMetadata)).build();
        return clusterState;
    }

    public static boolean isShrinkable(int source, int target) {
        int x = source / target;
        assert source > target : source + " <= " + target;
        return target * x == source;
    }

    public static boolean isSplitable(int source, int target) {
        int x = target / source;
        assert source < target : source + " >= " + target;
        return source * x == target;
    }

    public void testValidateShrinkIndex() {
        int numShards = randomIntBetween(2, 42);
        ClusterState state = createClusterState(
            "source",
            numShards,
            randomIntBetween(0, 10),
            Settings.builder().put("index.blocks.write", true).build()
        );

        assertEquals(
            "index [source] already exists",
            expectThrows(
                ResourceAlreadyExistsException.class,
                () -> MetadataCreateIndexService.validateShrinkIndex(state, "target", "source", Settings.EMPTY)
            ).getMessage()
        );

        assertEquals(
            "no such index [no_such_index]",
            expectThrows(
                IndexNotFoundException.class,
                () -> MetadataCreateIndexService.validateShrinkIndex(state, "no_such_index", "target", Settings.EMPTY)
            ).getMessage()
        );

        Settings targetSettings = Settings.builder().put("index.number_of_shards", 1).build();
        assertEquals(
            "can't shrink an index with only one shard",
            expectThrows(
                IllegalArgumentException.class,
                () -> MetadataCreateIndexService.validateShrinkIndex(
                    createClusterState("source", 1, 0, Settings.builder().put("index.blocks.write", true).build()),
                    "source",
                    "target",
                    targetSettings
                )
            ).getMessage()
        );

        assertEquals(
            "the number of target shards [10] must be less that the number of source shards [5]",
            expectThrows(
                IllegalArgumentException.class,
                () -> MetadataCreateIndexService.validateShrinkIndex(
                    createClusterState("source", 5, 0, Settings.builder().put("index.blocks.write", true).build()),
                    "source",
                    "target",
                    Settings.builder().put("index.number_of_shards", 10).build()
                )
            ).getMessage()
        );

        assertEquals(
            "index source must be read-only to resize index. use \"index.blocks.write=true\"",
            expectThrows(
                IllegalStateException.class,
                () -> MetadataCreateIndexService.validateShrinkIndex(
                    createClusterState("source", randomIntBetween(2, 100), randomIntBetween(0, 10), Settings.EMPTY),
                    "source",
                    "target",
                    targetSettings
                )
            ).getMessage()
        );

        assertEquals(
            "index source must have all shards allocated on the same node to shrink index",
            expectThrows(
                IllegalStateException.class,
                () -> MetadataCreateIndexService.validateShrinkIndex(state, "source", "target", targetSettings)

            ).getMessage()
        );
        assertEquals(
            "the number of source shards [8] must be a multiple of [3]",
            expectThrows(
                IllegalArgumentException.class,
                () -> MetadataCreateIndexService.validateShrinkIndex(
                    createClusterState("source", 8, randomIntBetween(0, 10), Settings.builder().put("index.blocks.write", true).build()),
                    "source",
                    "target",
                    Settings.builder().put("index.number_of_shards", 3).build()
                )
            ).getMessage()
        );

        // create one that won't fail
        ClusterState clusterState = ClusterState.builder(
            createClusterState("source", numShards, 0, Settings.builder().put("index.blocks.write", true).build())
        ).nodes(DiscoveryNodes.builder().add(newNode("node1"))).build();
        AllocationService service = new AllocationService(
            new AllocationDeciders(Collections.singleton(new MaxRetryAllocationDecider())),
            new TestGatewayAllocator(),
            new BalancedShardsAllocator(Settings.EMPTY),
            EmptyClusterInfoService.INSTANCE,
            EmptySnapshotsInfoService.INSTANCE
        );

        RoutingTable routingTable = service.reroute(clusterState, "reroute").routingTable();
        clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
        // now we start the shard
        routingTable = ESAllocationTestCase.startInitializingShardsAndReroute(service, clusterState, "source").routingTable();
        clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
        int targetShards;
        do {
            targetShards = randomIntBetween(1, numShards / 2);
        } while (isShrinkable(numShards, targetShards) == false);
        MetadataCreateIndexService.validateShrinkIndex(
            clusterState,
            "source",
            "target",
            Settings.builder().put("index.number_of_shards", targetShards).build()
        );
    }

    public void testValidateSplitIndex() {
        int numShards = randomIntBetween(1, 42);
        Settings targetSettings = Settings.builder().put("index.number_of_shards", numShards * 2).build();
        ClusterState state = createClusterState(
            "source",
            numShards,
            randomIntBetween(0, 10),
            Settings.builder().put("index.blocks.write", true).build()
        );

        assertEquals(
            "index [source] already exists",
            expectThrows(
                ResourceAlreadyExistsException.class,
                () -> MetadataCreateIndexService.validateSplitIndex(state, "target", "source", targetSettings)
            ).getMessage()
        );

        assertEquals(
            "no such index [no_such_index]",
            expectThrows(
                IndexNotFoundException.class,
                () -> MetadataCreateIndexService.validateSplitIndex(state, "no_such_index", "target", targetSettings)
            ).getMessage()
        );

        assertEquals(
            "the number of source shards [10] must be less that the number of target shards [5]",
            expectThrows(
                IllegalArgumentException.class,
                () -> MetadataCreateIndexService.validateSplitIndex(
                    createClusterState("source", 10, 0, Settings.builder().put("index.blocks.write", true).build()),
                    "source",
                    "target",
                    Settings.builder().put("index.number_of_shards", 5).build()
                )
            ).getMessage()
        );

        assertEquals(
            "index source must be read-only to resize index. use \"index.blocks.write=true\"",
            expectThrows(
                IllegalStateException.class,
                () -> MetadataCreateIndexService.validateSplitIndex(
                    createClusterState("source", randomIntBetween(2, 100), randomIntBetween(0, 10), Settings.EMPTY),
                    "source",
                    "target",
                    targetSettings
                )
            ).getMessage()
        );

        assertEquals(
            "the number of source shards [3] must be a factor of [4]",
            expectThrows(
                IllegalArgumentException.class,
                () -> MetadataCreateIndexService.validateSplitIndex(
                    createClusterState("source", 3, randomIntBetween(0, 10), Settings.builder().put("index.blocks.write", true).build()),
                    "source",
                    "target",
                    Settings.builder().put("index.number_of_shards", 4).build()
                )
            ).getMessage()
        );

        int targetShards;
        do {
            targetShards = randomIntBetween(numShards + 1, 100);
        } while (isSplitable(numShards, targetShards) == false);
        ClusterState clusterState = ClusterState.builder(
            createClusterState(
                "source",
                numShards,
                0,
                Settings.builder().put("index.blocks.write", true).put("index.number_of_routing_shards", targetShards).build()
            )
        ).nodes(DiscoveryNodes.builder().add(newNode("node1"))).build();
        AllocationService service = new AllocationService(
            new AllocationDeciders(Collections.singleton(new MaxRetryAllocationDecider())),
            new TestGatewayAllocator(),
            new BalancedShardsAllocator(Settings.EMPTY),
            EmptyClusterInfoService.INSTANCE,
            EmptySnapshotsInfoService.INSTANCE
        );

        RoutingTable routingTable = service.reroute(clusterState, "reroute").routingTable();
        clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();
        // now we start the shard
        routingTable = ESAllocationTestCase.startInitializingShardsAndReroute(service, clusterState, "source").routingTable();
        clusterState = ClusterState.builder(clusterState).routingTable(routingTable).build();

        MetadataCreateIndexService.validateSplitIndex(
            clusterState,
            "source",
            "target",
            Settings.builder().put("index.number_of_shards", targetShards).build()
        );
    }

    public void testPrepareResizeIndexSettings() {
        final List<Version> versions = Arrays.asList(VersionUtils.randomVersion(random()), VersionUtils.randomVersion(random()));
        versions.sort(Comparator.comparingLong(l -> l.id));
        final Version version = versions.get(0);
        final Version upgraded = versions.get(1);
        final Settings.Builder indexSettingsBuilder = Settings.builder()
            .put("index.version.created", version)
            .put("index.similarity.default.type", "BM25")
            .put("index.analysis.analyzer.default.tokenizer", "keyword")
            .put("index.soft_deletes.enabled", "true");
        if (randomBoolean()) {
            indexSettingsBuilder.put("index.allocation.max_retries", randomIntBetween(1, 1000));
        }
        runPrepareResizeIndexSettingsTest(
            indexSettingsBuilder.build(),
            Settings.EMPTY,
            Collections.emptyList(),
            randomBoolean(),
            settings -> {
                assertThat("similarity settings must be copied", settings.get("index.similarity.default.type"), equalTo("BM25"));
                assertThat(
                    "analysis settings must be copied",
                    settings.get("index.analysis.analyzer.default.tokenizer"),
                    equalTo("keyword")
                );
                assertThat(settings.get("index.routing.allocation.initial_recovery._id"), equalTo("node1"));
                assertThat(settings.get("index.allocation.max_retries"), nullValue());
                assertThat(settings.getAsVersion("index.version.created", null), equalTo(version));
                assertThat(settings.get("index.soft_deletes.enabled"), equalTo("true"));
            }
        );
    }

    public void testPrepareResizeIndexSettingsCopySettings() {
        final int maxMergeCount = randomIntBetween(1, 16);
        final int maxThreadCount = randomIntBetween(1, 16);
        final Setting<String> nonCopyableExistingIndexSetting = Setting.simpleString(
            "index.non_copyable.existing",
            Setting.Property.IndexScope,
            Setting.Property.NotCopyableOnResize
        );
        final Setting<String> nonCopyableRequestIndexSetting = Setting.simpleString(
            "index.non_copyable.request",
            Setting.Property.IndexScope,
            Setting.Property.NotCopyableOnResize
        );
        runPrepareResizeIndexSettingsTest(
            Settings.builder()
                .put("index.merge.scheduler.max_merge_count", maxMergeCount)
                .put("index.non_copyable.existing", "existing")
                .build(),
            Settings.builder()
                .put("index.blocks.write", (String) null)
                .put("index.merge.scheduler.max_thread_count", maxThreadCount)
                .put("index.non_copyable.request", "request")
                .build(),
            Arrays.asList(nonCopyableExistingIndexSetting, nonCopyableRequestIndexSetting),
            true,
            settings -> {
                assertNull(settings.getAsBoolean("index.blocks.write", null));
                assertThat(settings.get("index.routing.allocation.require._name"), equalTo("node1"));
                assertThat(settings.getAsInt("index.merge.scheduler.max_merge_count", null), equalTo(maxMergeCount));
                assertThat(settings.getAsInt("index.merge.scheduler.max_thread_count", null), equalTo(maxThreadCount));
                assertNull(settings.get("index.non_copyable.existing"));
                assertThat(settings.get("index.non_copyable.request"), equalTo("request"));
            }
        );
    }

    public void testPrepareResizeIndexSettingsAnalysisSettings() {
        // analysis settings from the request are not overwritten
        runPrepareResizeIndexSettingsTest(
            Settings.EMPTY,
            Settings.builder().put("index.analysis.analyzer.default.tokenizer", "whitespace").build(),
            Collections.emptyList(),
            randomBoolean(),
            settings -> assertThat(
                "analysis settings are not overwritten",
                settings.get("index.analysis.analyzer.default.tokenizer"),
                equalTo("whitespace")
            )
        );

    }

    public void testPrepareResizeIndexSettingsSimilaritySettings() {
        // similarity settings from the request are not overwritten
        runPrepareResizeIndexSettingsTest(
            Settings.EMPTY,
            Settings.builder().put("index.similarity.sim.type", "DFR").build(),
            Collections.emptyList(),
            randomBoolean(),
            settings -> assertThat("similarity settings are not overwritten", settings.get("index.similarity.sim.type"), equalTo("DFR"))
        );

    }

    public void testDoNotOverrideSoftDeletesSettingOnResize() {
        runPrepareResizeIndexSettingsTest(
            Settings.builder().put("index.soft_deletes.enabled", "false").build(),
            Settings.builder().put("index.soft_deletes.enabled", "true").build(),
            Collections.emptyList(),
            randomBoolean(),
            settings -> assertThat(settings.get("index.soft_deletes.enabled"), equalTo("true"))
        );
    }

    private void runPrepareResizeIndexSettingsTest(
        final Settings sourceSettings,
        final Settings requestSettings,
        final Collection<Setting<?>> additionalIndexScopedSettings,
        final boolean copySettings,
        final Consumer<Settings> consumer
    ) {
        final String indexName = randomAlphaOfLength(10);

        final Settings indexSettings = Settings.builder()
            .put("index.blocks.write", true)
            .put("index.routing.allocation.require._name", "node1")
            .put(sourceSettings)
            .build();

        final ClusterState initialClusterState = ClusterState.builder(
            createClusterState(indexName, randomIntBetween(2, 10), 0, indexSettings)
        ).nodes(DiscoveryNodes.builder().add(newNode("node1"))).build();

        final AllocationService service = new AllocationService(
            new AllocationDeciders(Collections.singleton(new MaxRetryAllocationDecider())),
            new TestGatewayAllocator(),
            new BalancedShardsAllocator(Settings.EMPTY),
            EmptyClusterInfoService.INSTANCE,
            EmptySnapshotsInfoService.INSTANCE
        );

        final RoutingTable initialRoutingTable = service.reroute(initialClusterState, "reroute").routingTable();
        final ClusterState routingTableClusterState = ClusterState.builder(initialClusterState).routingTable(initialRoutingTable).build();

        // now we start the shard
        final RoutingTable routingTable = ESAllocationTestCase.startInitializingShardsAndReroute(
            service,
            routingTableClusterState,
            indexName
        ).routingTable();
        final ClusterState clusterState = ClusterState.builder(routingTableClusterState).routingTable(routingTable).build();

        final Settings.Builder indexSettingsBuilder = Settings.builder().put("index.number_of_shards", 1).put(requestSettings);
        final Set<Setting<?>> settingsSet = Stream.concat(
            IndexScopedSettings.BUILT_IN_INDEX_SETTINGS.stream(),
            additionalIndexScopedSettings.stream()
        ).collect(Collectors.toSet());
        MetadataCreateIndexService.prepareResizeIndexSettings(
            clusterState,
            indexSettingsBuilder,
            clusterState.metadata().index(indexName).getIndex(),
            "target",
            ResizeType.SHRINK,
            copySettings,
            new IndexScopedSettings(Settings.EMPTY, settingsSet)
        );
        consumer.accept(indexSettingsBuilder.build());
    }

    private DiscoveryNode newNode(String nodeId) {
        return new DiscoveryNode(
            nodeId,
            buildNewFakeTransportAddress(),
            emptyMap(),
            Set.of(DiscoveryNodeRole.MASTER_ROLE, DiscoveryNodeRole.DATA_ROLE),
            Version.CURRENT
        );
    }

    public void testValidateIndexName() throws Exception {
        withTemporaryClusterService(((clusterService, threadPool) -> {
            MetadataCreateIndexService checkerService = new MetadataCreateIndexService(
                Settings.EMPTY,
                clusterService,
                null,
                null,
                null,
                createTestShardLimitService(randomIntBetween(1, 1000), clusterService),
                null,
                null,
                threadPool,
                null,
                EmptySystemIndices.INSTANCE,
                false
            );
            validateIndexName(checkerService, "index?name", "must not contain the following characters " + Strings.INVALID_FILENAME_CHARS);

            validateIndexName(checkerService, "index#name", "must not contain '#'");

            validateIndexName(checkerService, "_indexname", "must not start with '_', '-', or '+'");
            validateIndexName(checkerService, "-indexname", "must not start with '_', '-', or '+'");
            validateIndexName(checkerService, "+indexname", "must not start with '_', '-', or '+'");

            validateIndexName(checkerService, "INDEXNAME", "must be lowercase");

            validateIndexName(checkerService, "..", "must not be '.' or '..'");

            validateIndexName(checkerService, "foo:bar", "must not contain ':'");
        }));
    }

    private void validateIndexName(MetadataCreateIndexService metadataCreateIndexService, String indexName, String errorMessage) {
        InvalidIndexNameException e = expectThrows(
            InvalidIndexNameException.class,
            () -> metadataCreateIndexService.validateIndexName(
                indexName,
                ClusterState.builder(ClusterName.CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY)).build()
            )
        );
        assertThat(e.getMessage(), endsWith(errorMessage));
    }

    public void testCalculateNumRoutingShards() {
        assertEquals(1024, MetadataCreateIndexService.calculateNumRoutingShards(1, Version.CURRENT));
        assertEquals(1024, MetadataCreateIndexService.calculateNumRoutingShards(2, Version.CURRENT));
        assertEquals(768, MetadataCreateIndexService.calculateNumRoutingShards(3, Version.CURRENT));
        assertEquals(576, MetadataCreateIndexService.calculateNumRoutingShards(9, Version.CURRENT));
        assertEquals(1024, MetadataCreateIndexService.calculateNumRoutingShards(512, Version.CURRENT));
        assertEquals(2048, MetadataCreateIndexService.calculateNumRoutingShards(1024, Version.CURRENT));
        assertEquals(4096, MetadataCreateIndexService.calculateNumRoutingShards(2048, Version.CURRENT));

        for (int i = 0; i < 1000; i++) {
            int randomNumShards = randomIntBetween(1, 10000);
            int numRoutingShards = MetadataCreateIndexService.calculateNumRoutingShards(randomNumShards, Version.CURRENT);
            if (numRoutingShards <= 1024) {
                assertTrue("numShards: " + randomNumShards, randomNumShards < 513);
                assertTrue("numRoutingShards: " + numRoutingShards, numRoutingShards > 512);
            } else {
                assertEquals("numShards: " + randomNumShards, numRoutingShards / 2, randomNumShards);
            }

            double ratio = numRoutingShards / randomNumShards;
            int intRatio = (int) ratio;
            assertEquals(ratio, intRatio, 0.0d);
            assertTrue(1 < ratio);
            assertTrue(ratio <= 1024);
            assertEquals(0, intRatio % 2);
            assertEquals("ratio is not a power of two", intRatio, Integer.highestOneBit(intRatio));
        }
    }

    public void testValidateDotIndex() {
        List<SystemIndexDescriptor> systemIndexDescriptors = new ArrayList<>();
        systemIndexDescriptors.add(new SystemIndexDescriptor(".test-one*", "test"));
        systemIndexDescriptors.add(new SystemIndexDescriptor(".test-~(one*)", "test"));
        systemIndexDescriptors.add(new SystemIndexDescriptor(".pattern-test*", "test-1"));

        withTemporaryClusterService(((clusterService, threadPool) -> {
            MetadataCreateIndexService checkerService = new MetadataCreateIndexService(
                Settings.EMPTY,
                clusterService,
                null,
                null,
                null,
                createTestShardLimitService(randomIntBetween(1, 1000), clusterService),
                null,
                null,
                threadPool,
                null,
                new SystemIndices(
                    Collections.singletonMap("foo", new SystemIndices.Feature("foo", "test feature", systemIndexDescriptors))
                ),
                false
            );
            // Check deprecations
            assertFalse(checkerService.validateDotIndex(".test2", false));
            assertWarnings(
                "index name [.test2] starts with a dot '.', in the next major version, index "
                    + "names starting with a dot are reserved for hidden indices and system indices"
            );

            // Check non-system hidden indices don't trigger a warning
            assertFalse(checkerService.validateDotIndex(".test2", true));

            // Check NO deprecation warnings if we give the index name
            assertTrue(checkerService.validateDotIndex(".test-one", false));
            assertTrue(checkerService.validateDotIndex(".test-3", false));

            // Check that patterns with wildcards work
            assertTrue(checkerService.validateDotIndex(".pattern-test", false));
            assertTrue(checkerService.validateDotIndex(".pattern-test-with-suffix", false));
            assertTrue(checkerService.validateDotIndex(".pattern-test-other-suffix", false));
        }));
    }

    @SuppressWarnings("unchecked")
    public void testParseMappingsAppliesDataFromTemplateAndRequest() throws Exception {
        IndexTemplateMetadata templateMetadata = addMatchingTemplate(templateBuilder -> {
            templateBuilder.putAlias(AliasMetadata.builder("alias1"));
            templateBuilder.putMapping("_doc", createMapping("mapping_from_template", "text"));
        });
        request.mappings(createMapping("mapping_from_request", "text").string());

        Map<String, Object> parsedMappings = MetadataCreateIndexService.parseV1Mappings(
            request.mappings(),
            List.of(templateMetadata.getMappings()),
            NamedXContentRegistry.EMPTY
        );

        assertThat(parsedMappings, hasKey("_doc"));
        Map<String, Object> doc = (Map<String, Object>) parsedMappings.get("_doc");
        assertThat(doc, hasKey("properties"));
        Map<String, Object> mappingsProperties = (Map<String, Object>) doc.get("properties");
        assertThat(mappingsProperties, hasKey("mapping_from_request"));
        assertThat(mappingsProperties, hasKey("mapping_from_template"));
    }

    public void testAggregateSettingsAppliesSettingsFromTemplatesAndRequest() {
        IndexTemplateMetadata templateMetadata = addMatchingTemplate(
            builder -> { builder.settings(Settings.builder().put("template_setting", "value1")); }
        );
        ImmutableOpenMap.Builder<String, IndexTemplateMetadata> templatesBuilder = ImmutableOpenMap.builder();
        templatesBuilder.put("template_1", templateMetadata);
        Metadata metadata = new Metadata.Builder().templates(templatesBuilder.build()).build();
        ClusterState clusterState = ClusterState.builder(
            org.elasticsearch.cluster.ClusterName.CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY)
        ).metadata(metadata).build();
        request.settings(Settings.builder().put("request_setting", "value2").build());

        Settings aggregatedIndexSettings = aggregateIndexSettings(
            clusterState,
            request,
            templateMetadata.settings(),
            null,
            Settings.EMPTY,
            IndexScopedSettings.DEFAULT_SCOPED_SETTINGS,
            randomShardLimitService(),
            Collections.emptySet()
        );

        assertThat(aggregatedIndexSettings.get("template_setting"), equalTo("value1"));
        assertThat(aggregatedIndexSettings.get("request_setting"), equalTo("value2"));
    }

    public void testInvalidAliasName() {
        final String[] invalidAliasNames = new String[] { "-alias1", "+alias2", "_alias3", "a#lias", "al:ias", ".", ".." };
        String aliasName = randomFrom(invalidAliasNames);
        request.aliases(Set.of(new Alias(aliasName)));

        expectThrows(
            InvalidAliasNameException.class,
            () -> resolveAndValidateAliases(
                request.index(),
                request.aliases(),
                List.of(),
                Metadata.builder().build(),
                aliasValidator,
                xContentRegistry(),
                searchExecutionContext,
                indexNameExpressionResolver::resolveDateMathExpression,
                m -> false
            )
        );
    }

    public void testAliasNameWithMathExpression() {
        final String aliasName = "<date-math-based-{2021-01-19||/M{yyyy-MM-dd}}>";

        request.aliases(Set.of(new Alias(aliasName)));

        List<AliasMetadata> aliasMetadata = resolveAndValidateAliases(
            request.index(),
            request.aliases(),
            List.of(),
            Metadata.builder().build(),
            aliasValidator,
            xContentRegistry(),
            searchExecutionContext,
            indexNameExpressionResolver::resolveDateMathExpression,
            m -> false
        );

        assertEquals("date-math-based-2021-01-01", aliasMetadata.get(0).alias());
    }

    @SuppressWarnings("unchecked")
    public void testRequestDataHavePriorityOverTemplateData() throws Exception {
        CompressedXContent templateMapping = createMapping("test", "text");
        CompressedXContent reqMapping = createMapping("test", "keyword");

        IndexTemplateMetadata templateMetadata = addMatchingTemplate(
            builder -> builder.putAlias(AliasMetadata.builder("alias").searchRouting("fromTemplate").build())
                .putMapping("_doc", templateMapping)
                .settings(Settings.builder().put("key1", "templateValue"))
        );

        request.mappings(reqMapping.string());
        request.aliases(Set.of(new Alias("alias").searchRouting("fromRequest")));
        request.settings(Settings.builder().put("key1", "requestValue").build());

        Map<String, Object> parsedMappings = MetadataCreateIndexService.parseV1Mappings(
            request.mappings(),
            List.of(templateMetadata.mappings()),
            xContentRegistry()
        );
        List<AliasMetadata> resolvedAliases = resolveAndValidateAliases(
            request.index(),
            request.aliases(),
            MetadataIndexTemplateService.resolveAliases(List.of(templateMetadata)),
            Metadata.builder().build(),
            aliasValidator,
            xContentRegistry(),
            searchExecutionContext,
            indexNameExpressionResolver::resolveDateMathExpression,
            m -> false
        );

        Settings aggregatedIndexSettings = aggregateIndexSettings(
            ClusterState.EMPTY_STATE,
            request,
            templateMetadata.settings(),
            null,
            Settings.EMPTY,
            IndexScopedSettings.DEFAULT_SCOPED_SETTINGS,
            randomShardLimitService(),
            Collections.emptySet()
        );

        assertThat(resolvedAliases.get(0).getSearchRouting(), equalTo("fromRequest"));
        assertThat(aggregatedIndexSettings.get("key1"), equalTo("requestValue"));
        assertThat(parsedMappings, hasKey("_doc"));
        Map<String, Object> doc = (Map<String, Object>) parsedMappings.get("_doc");
        assertThat(doc, hasKey("properties"));
        Map<String, Object> mappingsProperties = (Map<String, Object>) doc.get("properties");
        assertThat(mappingsProperties, hasKey("test"));
        assertThat((Map<String, Object>) mappingsProperties.get("test"), hasValue("keyword"));
    }

    public void testDefaultSettings() {
        Settings aggregatedIndexSettings = aggregateIndexSettings(
            ClusterState.EMPTY_STATE,
            request,
            Settings.EMPTY,
            null,
            Settings.EMPTY,
            IndexScopedSettings.DEFAULT_SCOPED_SETTINGS,
            randomShardLimitService(),
            Collections.emptySet()
        );

        assertThat(aggregatedIndexSettings.get(SETTING_NUMBER_OF_SHARDS), equalTo("1"));
    }

    public void testSettingsFromClusterState() {
        Settings aggregatedIndexSettings = aggregateIndexSettings(
            ClusterState.EMPTY_STATE,
            request,
            Settings.EMPTY,
            null,
            Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 15).build(),
            IndexScopedSettings.DEFAULT_SCOPED_SETTINGS,
            randomShardLimitService(),
            Collections.emptySet()
        );

        assertThat(aggregatedIndexSettings.get(SETTING_NUMBER_OF_SHARDS), equalTo("15"));
    }

    public void testTemplateOrder() throws Exception {
        List<IndexTemplateMetadata> templates = new ArrayList<>(3);
        templates.add(
            addMatchingTemplate(
                builder -> builder.order(3)
                    .settings(Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 12))
                    .putAlias(AliasMetadata.builder("alias1").writeIndex(true).searchRouting("3").build())
            )
        );
        templates.add(
            addMatchingTemplate(
                builder -> builder.order(2)
                    .settings(Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 11))
                    .putAlias(AliasMetadata.builder("alias1").searchRouting("2").build())
            )
        );
        templates.add(
            addMatchingTemplate(
                builder -> builder.order(1)
                    .settings(Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 10))
                    .putAlias(AliasMetadata.builder("alias1").searchRouting("1").build())
            )
        );

        Settings aggregatedIndexSettings = aggregateIndexSettings(
            ClusterState.EMPTY_STATE,
            request,
            MetadataIndexTemplateService.resolveSettings(templates),
            null,
            Settings.EMPTY,
            IndexScopedSettings.DEFAULT_SCOPED_SETTINGS,
            randomShardLimitService(),
            Collections.emptySet()
        );
        List<AliasMetadata> resolvedAliases = resolveAndValidateAliases(
            request.index(),
            request.aliases(),
            MetadataIndexTemplateService.resolveAliases(templates),
            Metadata.builder().build(),
            aliasValidator,
            xContentRegistry(),
            searchExecutionContext,
            indexNameExpressionResolver::resolveDateMathExpression,
            m -> false
        );

        assertThat(aggregatedIndexSettings.get(SETTING_NUMBER_OF_SHARDS), equalTo("12"));
        AliasMetadata alias = resolvedAliases.get(0);
        assertThat(alias.getSearchRouting(), equalTo("3"));
        assertThat(alias.writeIndex(), is(true));
    }

    public void testResolvedAliasInTemplate() {
        List<IndexTemplateMetadata> templates = new ArrayList<>(3);
        templates.add(
            addMatchingTemplate(
                builder -> builder.order(3)
                    .settings(Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 1))
                    .putAlias(AliasMetadata.builder("<jan-{2021-01-07||/M{yyyy-MM-dd}}>").build())
            )
        );
        templates.add(
            addMatchingTemplate(
                builder -> builder.order(2)
                    .settings(Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 1))
                    .putAlias(AliasMetadata.builder("<feb-{2021-02-28||/M{yyyy-MM-dd}}>").build())
            )
        );
        templates.add(
            addMatchingTemplate(
                builder -> builder.order(1)
                    .settings(Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 1))
                    .putAlias(AliasMetadata.builder("<mar-{2021-03-07||/M{yyyy-MM-dd}}>").build())
            )
        );

        List<AliasMetadata> resolvedAliases = resolveAndValidateAliases(
            request.index(),
            request.aliases(),
            MetadataIndexTemplateService.resolveAliases(templates),
            Metadata.builder().build(),
            aliasValidator,
            xContentRegistry(),
            searchExecutionContext,
            indexNameExpressionResolver::resolveDateMathExpression,
            m -> false
        );

        assertThat(resolvedAliases.get(0).alias(), equalTo("jan-2021-01-01"));
        assertThat(resolvedAliases.get(1).alias(), equalTo("feb-2021-02-01"));
        assertThat(resolvedAliases.get(2).alias(), equalTo("mar-2021-03-01"));
    }

    public void testAggregateIndexSettingsIgnoresTemplatesOnCreateFromSourceIndex() throws Exception {
        CompressedXContent templateMapping = createMapping("test", "text");

        IndexTemplateMetadata templateMetadata = addMatchingTemplate(
            builder -> builder.putAlias(AliasMetadata.builder("alias").searchRouting("fromTemplate").build())
                .putMapping("_doc", templateMapping)
                .settings(Settings.builder().put("templateSetting", "templateValue"))
        );

        request.settings(Settings.builder().put("requestSetting", "requestValue").build());
        request.resizeType(ResizeType.SPLIT);
        request.recoverFrom(new Index("sourceIndex", UUID.randomUUID().toString()));
        ClusterState clusterState = createClusterState("sourceIndex", 1, 0, Settings.builder().put("index.blocks.write", true).build());

        Settings aggregatedIndexSettings = aggregateIndexSettings(
            clusterState,
            request,
            templateMetadata.settings(),
            clusterState.metadata().index("sourceIndex"),
            Settings.EMPTY,
            IndexScopedSettings.DEFAULT_SCOPED_SETTINGS,
            randomShardLimitService(),
            Collections.emptySet()
        );

        assertThat(aggregatedIndexSettings.get("templateSetting"), is(nullValue()));
        assertThat(aggregatedIndexSettings.get("requestSetting"), is("requestValue"));
    }

    public void testClusterStateCreateIndexThrowsWriteIndexValidationException() throws Exception {
        IndexMetadata existingWriteIndex = IndexMetadata.builder("test2")
            .settings(settings(Version.CURRENT))
            .putAlias(AliasMetadata.builder("alias1").writeIndex(true).build())
            .numberOfShards(1)
            .numberOfReplicas(0)
            .build();
        ClusterState currentClusterState = ClusterState.builder(ClusterState.EMPTY_STATE)
            .metadata(Metadata.builder().put(existingWriteIndex, false).build())
            .build();

        IndexMetadata newIndex = IndexMetadata.builder("test")
            .settings(settings(Version.CURRENT))
            .numberOfShards(1)
            .numberOfReplicas(0)
            .putAlias(AliasMetadata.builder("alias1").writeIndex(true).build())
            .build();

        assertThat(
            expectThrows(
                IllegalStateException.class,
                () -> clusterStateCreateIndex(currentClusterState, Set.of(), newIndex, (state, reason) -> state, null)
            ).getMessage(),
            startsWith("alias [alias1] has more than one write index [")
        );
    }

    public void testClusterStateCreateIndex() {
        ClusterState currentClusterState = ClusterState.builder(ClusterState.EMPTY_STATE).build();

        IndexMetadata newIndexMetadata = IndexMetadata.builder("test")
            .settings(settings(Version.CURRENT).put(SETTING_READ_ONLY, true))
            .numberOfShards(1)
            .numberOfReplicas(0)
            .putAlias(AliasMetadata.builder("alias1").writeIndex(true).build())
            .build();

        // used as a value container, not for the concurrency and visibility guarantees
        AtomicBoolean allocationRerouted = new AtomicBoolean(false);
        BiFunction<ClusterState, String, ClusterState> rerouteRoutingTable = (clusterState, reason) -> {
            allocationRerouted.compareAndSet(false, true);
            return clusterState;
        };

        ClusterState updatedClusterState = clusterStateCreateIndex(
            currentClusterState,
            Set.of(INDEX_READ_ONLY_BLOCK),
            newIndexMetadata,
            rerouteRoutingTable,
            null
        );
        assertThat(updatedClusterState.blocks().getIndexBlockWithId("test", INDEX_READ_ONLY_BLOCK.id()), is(INDEX_READ_ONLY_BLOCK));
        assertThat(updatedClusterState.routingTable().index("test"), is(notNullValue()));
        assertThat(allocationRerouted.get(), is(true));
    }

    public void testClusterStateCreateIndexWithMetadataTransaction() {
        ClusterState currentClusterState = ClusterState.builder(ClusterState.EMPTY_STATE)
            .metadata(
                Metadata.builder()
                    .put(
                        IndexMetadata.builder("my-index")
                            .settings(settings(Version.CURRENT).put(SETTING_READ_ONLY, true))
                            .numberOfShards(1)
                            .numberOfReplicas(0)
                    )
            )
            .build();

        IndexMetadata newIndexMetadata = IndexMetadata.builder("test")
            .settings(settings(Version.CURRENT).put(SETTING_READ_ONLY, true))
            .numberOfShards(1)
            .numberOfReplicas(0)
            .putAlias(AliasMetadata.builder("alias1").writeIndex(true).build())
            .build();

        // adds alias from new index to existing index
        BiConsumer<Metadata.Builder, IndexMetadata> metadataTransformer = (builder, indexMetadata) -> {
            AliasMetadata newAlias = indexMetadata.getAliases().iterator().next().value;
            IndexMetadata myIndex = builder.get("my-index");
            builder.put(IndexMetadata.builder(myIndex).putAlias(AliasMetadata.builder(newAlias.getAlias()).build()));
        };

        ClusterState updatedClusterState = clusterStateCreateIndex(
            currentClusterState,
            Set.of(INDEX_READ_ONLY_BLOCK),
            newIndexMetadata,
            (clusterState, y) -> clusterState,
            metadataTransformer
        );
        assertTrue(updatedClusterState.metadata().findAllAliases(new String[] { "my-index" }).containsKey("my-index"));
        assertNotNull(updatedClusterState.metadata().findAllAliases(new String[] { "my-index" }).get("my-index"));
        assertNotNull(
            updatedClusterState.metadata().findAllAliases(new String[] { "my-index" }).get("my-index").get(0).alias(),
            equalTo("alias1")
        );
    }

    public void testParseMappingsWithTypedTemplateAndTypelessIndexMapping() throws Exception {
        IndexTemplateMetadata templateMetadata = addMatchingTemplate(builder -> {
            try {
                builder.putMapping("type", "{\"type\": {}}");
            } catch (IOException e) {
                ExceptionsHelper.reThrowIfNotNull(e);
            }
        });

        Map<String, Object> mappings = parseV1Mappings("{\"_doc\":{}}", List.of(templateMetadata.mappings()), xContentRegistry());
        assertThat(mappings, Matchers.hasKey(MapperService.SINGLE_MAPPING_NAME));
    }

    public void testParseMappingsWithTypedTemplate() throws Exception {
        IndexTemplateMetadata templateMetadata = addMatchingTemplate(builder -> {
            try {
                builder.putMapping("type", """
                    {"type":{"properties":{"field":{"type":"keyword"}}}}
                    """);
            } catch (IOException e) {
                ExceptionsHelper.reThrowIfNotNull(e);
            }
        });
        Map<String, Object> mappings = parseV1Mappings("", List.of(templateMetadata.mappings()), xContentRegistry());
        assertThat(mappings, Matchers.hasKey(MapperService.SINGLE_MAPPING_NAME));
    }

    public void testParseMappingsWithTypelessTemplate() throws Exception {
        IndexTemplateMetadata templateMetadata = addMatchingTemplate(builder -> {
            try {
                builder.putMapping(MapperService.SINGLE_MAPPING_NAME, "{\"_doc\": {}}");
            } catch (IOException e) {
                ExceptionsHelper.reThrowIfNotNull(e);
            }
        });
        Map<String, Object> mappings = parseV1Mappings("", List.of(templateMetadata.mappings()), xContentRegistry());
        assertThat(mappings, Matchers.hasKey(MapperService.SINGLE_MAPPING_NAME));
    }

    public void testBuildIndexMetadata() {
        IndexMetadata sourceIndexMetadata = IndexMetadata.builder("parent")
            .settings(Settings.builder().put("index.version.created", Version.CURRENT).build())
            .numberOfShards(1)
            .numberOfReplicas(0)
            .primaryTerm(0, 3L)
            .build();

        Settings indexSettings = Settings.builder()
            .put("index.version.created", Version.CURRENT)
            .put(SETTING_NUMBER_OF_REPLICAS, 0)
            .put(SETTING_NUMBER_OF_SHARDS, 1)
            .build();
        List<AliasMetadata> aliases = List.of(AliasMetadata.builder("alias1").build());
        IndexMetadata indexMetadata = buildIndexMetadata("test", aliases, () -> null, indexSettings, 4, sourceIndexMetadata, false);

        assertThat(indexMetadata.getAliases().size(), is(1));
        assertThat(indexMetadata.getAliases().keys().iterator().next().value, is("alias1"));
        assertThat("The source index primary term must be used", indexMetadata.primaryTerm(0), is(3L));
    }

    public void testGetIndexNumberOfRoutingShardsWithNullSourceIndex() {
        Settings indexSettings = Settings.builder()
            .put("index.version.created", Version.CURRENT)
            .put(INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 3)
            .build();
        int targetRoutingNumberOfShards = getIndexNumberOfRoutingShards(indexSettings, null);
        assertThat(
            "When the target routing number of shards is not specified the expected value is the configured number of shards "
                + "multiplied by 2 at most ten times (ie. 3 * 2^8)",
            targetRoutingNumberOfShards,
            is(768)
        );
    }

    public void testGetIndexNumberOfRoutingShardsWhenExplicitlyConfigured() {
        Settings indexSettings = Settings.builder()
            .put(INDEX_NUMBER_OF_ROUTING_SHARDS_SETTING.getKey(), 9)
            .put(INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 3)
            .build();
        int targetRoutingNumberOfShards = getIndexNumberOfRoutingShards(indexSettings, null);
        assertThat(targetRoutingNumberOfShards, is(9));
    }

    public void testGetIndexNumberOfRoutingShardsNullVsNotDefined() {
        int numberOfPrimaryShards = randomIntBetween(1, 16);
        Settings indexSettings = settings(Version.CURRENT).put(INDEX_NUMBER_OF_ROUTING_SHARDS_SETTING.getKey(), (String) null)
            .put(INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), numberOfPrimaryShards)
            .build();
        int targetRoutingNumberOfShardsWithNull = getIndexNumberOfRoutingShards(indexSettings, null);
        indexSettings = settings(Version.CURRENT).put(INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), numberOfPrimaryShards).build();
        int targetRoutingNumberOfShardsWithNotDefined = getIndexNumberOfRoutingShards(indexSettings, null);
        assertThat(targetRoutingNumberOfShardsWithNull, is(targetRoutingNumberOfShardsWithNotDefined));
    }

    public void testGetIndexNumberOfRoutingShardsNull() {
        Settings indexSettings = settings(Version.CURRENT).put(INDEX_NUMBER_OF_ROUTING_SHARDS_SETTING.getKey(), (String) null)
            .put(INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 2)
            .build();
        int targetRoutingNumberOfShardsWithNull = getIndexNumberOfRoutingShards(indexSettings, null);
        assertThat(targetRoutingNumberOfShardsWithNull, is(1024));
    }

    public void testGetIndexNumberOfRoutingShardsYieldsSourceNumberOfShards() {
        Settings indexSettings = Settings.builder().put(INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 3).build();

        IndexMetadata sourceIndexMetadata = IndexMetadata.builder("parent")
            .settings(Settings.builder().put("index.version.created", Version.CURRENT).build())
            .numberOfShards(6)
            .numberOfReplicas(0)
            .build();

        int targetRoutingNumberOfShards = getIndexNumberOfRoutingShards(indexSettings, sourceIndexMetadata);
        assertThat(targetRoutingNumberOfShards, is(6));
    }

    private Optional<String> aggregatedTierPreference(Settings settings, boolean isDataStream) {
        Settings templateSettings = Settings.EMPTY;
        request.settings(Settings.EMPTY);

        if (randomBoolean()) {
            templateSettings = settings;
        } else {
            request.settings(settings);
        }

        if (isDataStream) {
            request.dataStreamName(randomAlphaOfLength(10));
        } else {
            request.dataStreamName(null);
        }
        Settings aggregatedIndexSettings = aggregateIndexSettings(
            ClusterState.EMPTY_STATE,
            request,
            templateSettings,
            null,
            Settings.EMPTY,
            IndexScopedSettings.DEFAULT_SCOPED_SETTINGS,
            randomShardLimitService(),
            Set.of(new DataTier.DefaultHotAllocationSettingProvider())
        );

        if (aggregatedIndexSettings.keySet().contains(DataTier.TIER_PREFERENCE)) {
            return Optional.of(aggregatedIndexSettings.get(DataTier.TIER_PREFERENCE));
        } else {
            return Optional.empty();
        }
    }

    public void testEnforceDefaultTierPreference() {
        Settings settings;
        Optional<String> tier;

        // empty settings gets the appropriate tier
        settings = Settings.EMPTY;
        tier = aggregatedTierPreference(settings, false);
        assertEquals(DataTier.DATA_CONTENT, tier.get());

        settings = Settings.EMPTY;
        tier = aggregatedTierPreference(settings, true);
        assertEquals(DataTier.DATA_HOT, tier.get());

        // an explicit tier is respected
        settings = Settings.builder().put(DataTier.TIER_PREFERENCE, DataTier.DATA_COLD).build();
        tier = aggregatedTierPreference(settings, randomBoolean());
        assertEquals(DataTier.DATA_COLD, tier.get());

        // any of the INDEX_ROUTING_.*_GROUP_PREFIX settings still result in a default
        String includeRoutingSetting = randomFrom(
            IndexMetadata.INDEX_ROUTING_REQUIRE_GROUP_PREFIX,
            IndexMetadata.INDEX_ROUTING_EXCLUDE_GROUP_PREFIX,
            IndexMetadata.INDEX_ROUTING_INCLUDE_GROUP_PREFIX
        ) + "." + randomAlphaOfLength(10);
        settings = Settings.builder().put(includeRoutingSetting, randomAlphaOfLength(10)).build();
        tier = aggregatedTierPreference(settings, false);
        assertEquals(DataTier.DATA_CONTENT, tier.get());

        // an explicit null gets the appropriate tier
        settings = Settings.builder().putNull(DataTier.TIER_PREFERENCE).build();
        tier = aggregatedTierPreference(settings, false);
        assertEquals(DataTier.DATA_CONTENT, tier.get());

        settings = Settings.builder().putNull(DataTier.TIER_PREFERENCE).build();
        tier = aggregatedTierPreference(settings, true);
        assertEquals(DataTier.DATA_HOT, tier.get());
    }

    public void testRejectWithSoftDeletesDisabled() {
        final IllegalArgumentException error = expectThrows(IllegalArgumentException.class, () -> {
            request = new CreateIndexClusterStateUpdateRequest("create index", "test", "test");
            request.settings(Settings.builder().put(INDEX_SOFT_DELETES_SETTING.getKey(), false).build());
            aggregateIndexSettings(
                ClusterState.EMPTY_STATE,
                request,
                Settings.EMPTY,
                null,
                Settings.EMPTY,
                IndexScopedSettings.DEFAULT_SCOPED_SETTINGS,
                randomShardLimitService(),
                Collections.emptySet()
            );
        });
        assertThat(
            error.getMessage(),
            equalTo(
                "Creating indices with soft-deletes disabled is no longer supported. "
                    + "Please do not specify a value for setting [index.soft_deletes.enabled]."
            )
        );
    }

    public void testRejectTranslogRetentionSettings() {
        request = new CreateIndexClusterStateUpdateRequest("create index", "test", "test");
        final Settings.Builder settings = Settings.builder();
        if (randomBoolean()) {
            settings.put(IndexSettings.INDEX_TRANSLOG_RETENTION_AGE_SETTING.getKey(), TimeValue.timeValueMillis(between(1, 120)));
        } else {
            settings.put(IndexSettings.INDEX_TRANSLOG_RETENTION_SIZE_SETTING.getKey(), between(1, 128) + "mb");
        }
        if (randomBoolean()) {
            settings.put(SETTING_VERSION_CREATED, VersionUtils.randomVersionBetween(random(), Version.V_8_0_0, Version.CURRENT));
        }
        request.settings(settings.build());
        IllegalArgumentException error = expectThrows(
            IllegalArgumentException.class,
            () -> aggregateIndexSettings(
                ClusterState.EMPTY_STATE,
                request,
                Settings.EMPTY,
                null,
                Settings.EMPTY,
                IndexScopedSettings.DEFAULT_SCOPED_SETTINGS,
                randomShardLimitService(),
                Collections.emptySet()
            )
        );
        assertThat(
            error.getMessage(),
            equalTo(
                "Translog retention settings [index.translog.retention.age] "
                    + "and [index.translog.retention.size] are no longer supported. Please do not specify values for these settings"
            )
        );
    }

    public void testDeprecateTranslogRetentionSettings() {
        request = new CreateIndexClusterStateUpdateRequest("create index", "test", "test");
        final Settings.Builder settings = Settings.builder();
        if (randomBoolean()) {
            settings.put(IndexSettings.INDEX_TRANSLOG_RETENTION_AGE_SETTING.getKey(), TimeValue.timeValueMillis(between(1, 120)));
        } else {
            settings.put(IndexSettings.INDEX_TRANSLOG_RETENTION_SIZE_SETTING.getKey(), between(1, 128) + "mb");
        }
        settings.put(SETTING_VERSION_CREATED, VersionUtils.randomPreviousCompatibleVersion(random(), Version.V_8_0_0));
        request.settings(settings.build());
        aggregateIndexSettings(
            ClusterState.EMPTY_STATE,
            request,
            Settings.EMPTY,
            null,
            Settings.EMPTY,
            IndexScopedSettings.DEFAULT_SCOPED_SETTINGS,
            randomShardLimitService(),
            Collections.emptySet()
        );
        assertWarnings(
            "Translog retention settings [index.translog.retention.age] "
                + "and [index.translog.retention.size] are deprecated and effectively ignored. They will be removed in a future version."
        );
    }

    public void testDeprecateSimpleFS() {
        request = new CreateIndexClusterStateUpdateRequest("create index", "test", "test");
        final Settings.Builder settings = Settings.builder();
        settings.put(IndexModule.INDEX_STORE_TYPE_SETTING.getKey(), IndexModule.Type.SIMPLEFS.getSettingsKey());

        request.settings(settings.build());
        aggregateIndexSettings(
            ClusterState.EMPTY_STATE,
            request,
            Settings.EMPTY,
            null,
            Settings.EMPTY,
            IndexScopedSettings.DEFAULT_SCOPED_SETTINGS,
            randomShardLimitService(),
            Collections.emptySet()
        );
        assertWarnings(
            "[simplefs] is deprecated and will be removed in 8.0. Use [niofs] or other file systems instead. "
                + "Elasticsearch 7.15 or later uses [niofs] for the [simplefs] store type "
                + "as it offers superior or equivalent performance to [simplefs]."
        );
    }

    private IndexTemplateMetadata addMatchingTemplate(Consumer<IndexTemplateMetadata.Builder> configurator) {
        IndexTemplateMetadata.Builder builder = templateMetadataBuilder("template1", "te*");
        configurator.accept(builder);
        return builder.build();
    }

    private IndexTemplateMetadata.Builder templateMetadataBuilder(String name, String pattern) {
        return IndexTemplateMetadata.builder(name).patterns(Collections.singletonList(pattern));
    }

    private CompressedXContent createMapping(String fieldName, String fieldType) {
        try {
            final String mapping = Strings.toString(
                XContentFactory.jsonBuilder()
                    .startObject()
                    .startObject("_doc")
                    .startObject("properties")
                    .startObject(fieldName)
                    .field("type", fieldType)
                    .endObject()
                    .endObject()
                    .endObject()
                    .endObject()
            );

            return new CompressedXContent(mapping);
        } catch (IOException e) {
            throw ExceptionsHelper.convertToRuntime(e);
        }
    }

    private ShardLimitValidator randomShardLimitService() {
        return createTestShardLimitService(randomIntBetween(10, 10000));
    }

    private void withTemporaryClusterService(BiConsumer<ClusterService, ThreadPool> consumer) {
        ThreadPool threadPool = new TestThreadPool(getTestName());
        try {
            final ClusterService clusterService = ClusterServiceUtils.createClusterService(threadPool);
            consumer.accept(clusterService, threadPool);
        } finally {
            threadPool.shutdown();
        }
    }
}


The umbrellas sharing startup, E Umbrella based in Shenzen, which only just launched in 11 Chinese citiles announced that it lost nearly all of of its 300,000 umbrella. The concept of the company is similar to bike sharing companies that have become popular across china, and a number of other cities across the world....but are there some limits to the sharing economy? One question that i had, was why not make the deposit exceed the the cost of individual umbrellas? Another interesting question from commenteer erictan was, "This is a ridiculous idea. So what prevents people from stealing them? Do they not open without a special code sent by app? How do you disable umbrellas if you don't pay?"
Mr-X on Wed, 2017-07-19 09:11.
When I first heard about this I wondered if thsi had been properly thought through. For example, what is stopping someone buying a cheap umbrella and keeping it in their bag. Or, one of those fold-up waterfroof ponchos for when you're caught out!
Sharing a bike is undersrtandable, primarily because it won't fit in your pocket.


text:
University of California, Berkeley School of Law, Public Law & Legal Theory Research Paper Series
http://www.law.berkeley.edu/index.html
https://www.ssrn.com/link/uc-berkeley-public-law.html
Rules for the Heavens: The Coming Revolution in Space and the Laws of War
University of Illinois Law Review, Vol. 2020, No. 1, 2020
University of California at Berkeley School of Law
The Failed Quest for Equal Educational Opportunity: Regulating Education the Way We Regulate Business
Stephen D. Sugarman
University of California, Berkeley - School of Law
Justice Roger J. Traynor, Pragmatism, and the Current California Supreme Court
Hastings Law Journal, Forthcoming
Athena Rising? Brief of Professors Jeffrey A. Lefstin and Peter S. Menell as Amici Curiae in Support of Petition for a Writ of Certiorari in Athena Diagnostics v. Mayo Collaborative Services
Jeffrey A. Lefstin and Peter S. Menell
University of California Hastings College of the Law and University of California, Berkeley - School of Law
The Uncopyrightability of Edicts of Government
U of Penn Law School, Public Law Research Paper No. 19-40, UC Berkeley Public Law Research Paper
Shyamkrishna Balganesh and Peter S. Menell
University of Pennsylvania Law School and University of California, Berkeley - School of Law
Global Data Privacy: The EU Way
Paul M. Schwartz, Global Data Privacy: The EU Way, 94 New York University Law Review 771 (2019)
Paul M. Schwartz
Can You Pay For Privacy? Consumer Expectations and the Behavior of Free and Paid Apps
Berkeley Technology Law Journal, Vol. 35, 2020
Kenneth A. Bamberger, Serge Egelman, Catherine Han, Amit Elazari and Irwin Reyes
University of California, Berkeley - School of Law, International Computer Science Institute (ICSI), University of California, Berkeley, Students, University of California, Berkeley - School of Information and University of California, Berkeley
Procurement As Policy: Administrative Process for Machine Learning
Number of pages: 78 Posted: 15 Oct 2019 Last Revised: 09 Dec 2019
Deirdre K. Mulligan and Kenneth A. Bamberger
University of California, Berkeley - School of Information and University of California, Berkeley - School of Law
Trademark Counterpublics
Sonia Katyal and Vicki T. Huang
University of California, Berkeley - School of Law and Deakin University, Geelong, Australia - Deakin Law School
Brands Behaving Badly
Trademark Reporter, 2019
Sonia Katyal
Democratic Norms and Governance Experimentalism in Worker Centers
82 Law & Contemporary Problems 141 (2019), UCLA School of Law, Public Law Research Paper No. 19-27
Posted: 30 Aug 2019 Last Revised: 03 Sep 2019
Sameer M. Ashar and Catherine Fisk
UCLA School of Law - UCLA School of Law and University of California, Berkeley - School of Law
Underground Pathways to Pollution: The Need for Better Guidance on Groundwater Hydrologically Connected to Surface Water
Ecology Law Quarterly, Vol. 46, No. 2, 2019
Kaela Shiigi
Berkeley Law
Revisiting and Confronting the Federal Judiciary Capacity 'Crisis': Charting a Path for Federal Judiciary Reform
California Law Review, Vol. 108, Forthcoming
Number of pages: 100 Posted: 16 Aug 2019 Last Revised: 14 Jan 2020
Peter S. Menell and Ryan G. Vacca
University of California, Berkeley - School of Law and University of New Hampshire School of Law
Mashups and Fair Use: The Bold Misadventures of the Seussian Starship Enterprise
Peter S. Menell, Shyamkrishna Balganesh and David Nimmer
University of California, Berkeley - School of Law, University of Pennsylvania Law School and Irell & Manella LLP
Asian Territorial Disputes and the 1951 San Francisco Peace Treaty: The Case of Dokdo
18 Chinese Journal of International Law ___ (2019)
Thomas Schwartz and John Yoo
Department of Political Science, Vanderbilt University and University of California at Berkeley School of Law
Beyond Cross-Examination: A Response to Cheng and Nunn
97 Texas Law Review Online 193 (2019)
Andrea L. Roth
Collaborative Law: Dispute Resolution Competencies for the 'New Advocacy'
QUT Law and Justice Journal, 4(1) 213-237 (2008)
Marilyn A Scott
University of Technology Sydney, Faculty of Law
What's Wrong with the Commissioner? Some Lessons from Downunder
Seton Hall Journal of Sports and Entertainment Law, 19(1) 105-148, 2009
Ian Dobinson and David Thorpe
University of Technology Sydney, Faculty of Law and University of Technology Sydney, Faculty of Law
Secretarial Audit with Reference to Affixation of Common Seal
Inspira - Journal of Modern Management & Entrepreneurship (2018), Vol. V No. 2, pp.239-241
Devendra Jarwal and Anoop Vyas
University of Delhi and Devi Ahilya University
The Gramm-Leach-Bliley Act
Number of pages: 21 Posted: 10 Jul 2019 Last Revised: 28 Oct 2019
Jolina C. Cuaresma
Intellectual Property in the New Technological Age: 2019 - Chapters 1 and 2
Number of pages: 176 Posted: 08 Jul 2019 Last Revised: 18 Aug 2019
Peter S. Menell, Mark A. Lemley and Robert P. Merges
University of California, Berkeley - School of Law, Stanford Law School and University of California, Berkeley - School of Law
Commissioning the Consumer Financial Protection Bureau
31 Loy. Consumer L. Rev. 426
It's Good to Have the 'Haves' on Your Side: A Defense of Repeat Players in Multidistrict Litigation
108 Georgetown Law Journal 73 (2019), U of Houston Law Center No. 2019-A-9
Andrew Bradt and D. Theodore Rave
University of California, Berkeley - School of Law and University of Houston Law Center
The Paradox of Source Code Secrecy
Cornell Law Review, Forthcoming
Reforming Patent Eligibility: Supplementary Statement of Professors Jeffrey A. Lefstin and Peter S. Menell
UC Hastings Research Paper No. 336
Peter S. Menell and Jeffrey A. Lefstin
University of California, Berkeley - School of Law and University of California Hastings College of the Law
Economic Analysis of Network Effects and Intellectual Property
Berkeley Technology Law Journal, Vol. 34, No. 1, 2019
Number of pages: 104 Posted: 04 Jun 2019
Peter S. Menell
Ben Depoorter & Peter S. Menell (eds.), Research Handbook on the Economics of Intellectual Property Law: Vol I. Theory (2018), UC Berkeley Public Law Research Paper
Transparenthood
Michigan Law Review, Forthcoming
Sonia Katyal and Ilona M. Turner
University of California, Berkeley - School of Law and Transgender Law Center
Looking for Law in All the Wrong Places: Justice Beyond and Between
Looking for Law in All the Wrong Places: Justice Beyond and Between, Fordham University Press (2019)
Marianne Constable, Leti Volpp and Bryan Wagner
University of California, Berkeley - Department of Rhetoric, University of California, Berkeley - School of Law and affiliation not provided to SSRN
Protecting the Nation from 'Honor Killings': The Construction of a Problem
34 Constitutional Commentary 133 (2019)
Leti Volpp
Polarized State Politics, Stable Mortgage Markets
University of Chicago Coase-Sandor Institute for Law & Economics Research Paper No. 882, U of Chicago, Public Law Working Paper
Number of pages: 45 Posted: 17 May 2019 Last Revised: 05 Dec 2019
Brian D. Feinstein, Chen Meng and Manisha Padi
University of Pennsylvania, The Wharton School, University of Chicago - Law School and University of California, Berkeley - School of Law
Be Careful What You Wish For: Ronald Reagan, Donald Trump, The Assault on Civil Rights, and The Surprising Story of How Title VII Got Its Private Right of Action
Berkeley Journal of Employment and Labor Law, Vol. 39, No. 1, 2018
David B. Oppenheimer, Henry Cornillie, Henry Bluestone Smith, Thao Thai and Richard Treadwell
University of California, Berkeley - School of Law, affiliation not provided to SSRN, affiliation not provided to SSRN, affiliation not provided to SSRN and affiliation not provided to SSRN
Attempted Justice: Misunderstanding and Bias in Psychological Constructions of Criminal Attempt
Stanford Law Review, Vol. 71, March 2019
Avani Mehta Sood
Healthy Data Protection
Number of pages: 32 Posted: 24 Apr 2019 Last Revised: 23 Oct 2019
Lothar Determann
Baker & McKenzie LLP
A Defense of the Electoral College in the Time of Trump
Pepperdine Law Review, Vol. 46, No. 4, 2019
Patent Showdown at the N.D. C[orr]al
Chicago-Kent Journal of Intellectual Property Law (Forthcoming)
Number of pages: 47 Posted: 02 Apr 2019 Last Revised: 16 May 2019
Google v. Oracle Amicus Certiorari Stage Brief: Vindicating IP's Channeling Principle and Restoring Jurisdictional Balance to Software Copyright Protection
Peter S. Menell and David Nimmer
University of California, Berkeley - School of Law and Irell & Manella LLP
Continuity and Transformation in Environmental Governance
Daniel A. Farber
United States Constitutional Law: Chapter 1 (Introduction)
United States Constitutional Law, 2019, ISBN: 9781640208018, Duke Law School Public Law & Legal Theory Series No. 2019-23
Daniel A. Farber and Neil Siegel
University of California, Berkeley - School of Law and Duke University School of Law
The Tethered Economy
87(4) George Washington Law Review ___ (2019 Forthcoming), Case Legal Studies Research Paper No. 2019-10
Number of pages: 92 Posted: 31 Jan 2019 Last Revised: 03 Oct 2019
Chris Jay Hoofnagle, Aniket Kesari and Aaron Perzanowski
University of California, Berkeley - School of Information, University of California, Berkeley - Jurisprudence and Social Policy Program and Case Western Reserve University School of Law
Resolution of Territorial Disputes in East Asia: The Case of Dokdo
Berkeley Journal of International Law (BJIL), Vol. 36, No. 3, 2018
Laurent Mayali and John Yoo
University of California, Berkeley - School of Law and University of California at Berkeley School of Law
Patent Mediation Guide
UC Berkeley Public Law Research Paper
Peter S. Menell, Kathi Vidal, Leeron Kalay, Matthew Powers and Sarita Venkat
University of California, Berkeley - School of Law, Winston & Strawn LLP, Fish & Richardson P.C., Tensegrity Law Group and Independent
Private Accountability in the Age of Artificial Intelligence
66 UCLA L. REV. 54 (2019) , UC Berkeley Public Law Research Paper
Refugees Welcome?
Berkeley La Raza Law Journal, Vol. 28, Article 4, 2018, UC Berkeley Public Law Research Paper
Regulatory Review in Anti-Regulatory Times
Number of pages: 53 Posted: 18 Dec 2018 Last Revised: 22 Feb 2019
The Myth of Morrison: Securities Fraud Litigation Against Foreign Issuers
UC Berkeley Public Law Research Paper, U of Penn, Inst for Law & Econ Research Paper No. 18-34
Robert P. Bartlett, Matthew D. Cain, Jill E. Fisch and Steven Davidoff Solomon
University of California, Berkeley - School of Law, Berkeley Center for Law and Business, University of Pennsylvania Law School - Institute for Law and Economics, European Corporate Governance Institute and University of California, Berkeley - School of Law
Archibald Cox and the Diversity Justification for Affirmative Action
UC Berkeley Public Law Research Paper No. 2913310, Virginia Journal of Social Policy and the Law, Vol. 25, No. 2, 2018
David B. Oppenheimer
Exaggerating the Effects of Janus: A Reply to Professors Baude and Volokh
Harvard Law Review Forum, Vol. 132, p.42, 2018, Forthcoming, UC Berkeley Public Law Research Paper
Erwin Chemerinsky and Catherine Fisk
University of California, Berkeley - School of Law and University of California, Berkeley - School of Law
The European Union General Data Protection Regulation: What It Is And What It Means
Number of pages: 35 Posted: 16 Oct 2018 Last Revised: 15 Feb 2019
Chris Jay Hoofnagle, Bart van der Sloot and Frederik Zuiderveen Borgesius
University of California, Berkeley - School of Information, Tilburg University - Tilburg Institute for Law, Technology, and Society (TILT) and University of Amsterdam - IViR Institute for Information Law (IViR)
Indian Personal Data Protection Act, 2018: Draft Bill and Its History, Compared to EU GDPR and California Privacy Law
Number of pages: 27 Posted: 01 Oct 2018 Last Revised: 26 Mar 2019
Lothar Determann and Chetan Gupta
Baker & McKenzie LLP and Baker McKenzie LLP

redpajama_set_name: RedPajamaCommonCrawl


text:
Paul Pogba's brother makes Liverpool FC Champions League and Premier League prediction
Mathias Pogba, brother of Manchester United star Paul, has given his verdict on Liverpool's chances of silverware this season.
Joe BrayManchester City writer
Mathias Pogba thinks Liverpool are the team to beat in the Champions League. (Image: 2017 Getty Images)
Liverpool will 'destroy' Atletico Madrid in the Champions League last-16, according to Paul Pogba's brother Mathais.
The Pogba brothers have attracted attention this week after Manchester United midfielder Paul was given permission to attend the wedding of Florentin in France on Friday. He returned to United over the weekend, before manager Ole Gunnar Solskjaer confirmed he had come down with illness as he continues to recover from injury.
Solskjaer was speaking after United discovered their Europa League opponents, with the Champions League draw taking place on the same day. That particular draw saw holders Liverpool paired with Atletico Madrid in the last-16, while United will travel to Club Brugge in the Europa League.
And Mathias, speaking on Spanish TV about Liverpool's draw, was blunt about Atletico's chances against Jurgen Klopp's men.
Real Madrid insider warns about Paul Pogba transfer from Manchester United
Club Brugge explain why they can defeat Manchester United in the Europa League
"Prepare yourself," he said. "Liverpool are going to destroy (Atletico) Madrid. That is it."
Picking out Mohamed Salah and Naby Keita as Liverpool's big threats, Pogba continued: "Lodi is going to stop Salah? My friend Lodi is not going to be able to send a single cross because he is going to be focused on stopping Salah. He is going to be very tired."
"My friend from Guinea [Keita]. You will see how he controls the midfield."
But Pogba insisted there was no motive behind his comments, saying he just felt Liverpool were the superior side.
"I do not care," he said when asked who will win the competition. "I am just saying what I think like everyone else here."
And when pressed on whether Liverpool can be caught in the Premier League, Mathias said if they "do not lose on Boxing Day [against Leicester], my friend, it is going to be very difficult. Prepare yourself."


text:
FESTIVAL REVIEW: Super Dark Times
Owen Campbell, Charlie Tahan
September 29th, 2017 (LIMITED)
October 3rd, 2017 (VOD)
Kevin Phillips
*DISCLAIMER: This review was written in April of 2017 during the 2017 Tribeca Film Festival*
It's a rarity when movies have a character with the name "Zach." It's even more of a rarity when "Zach" is the name of a film's lead character. When I was reading the synopsis for "Super Dark Times" and saw my name as the central protagonist, immediately I took interest. As it turns out, there's a pretty great movie surrounding my majestic, underused name. Set in, as the press notes state, "mid-90s suburbia," this movie revolves around two best friends, Zach and Josh. The two spend a lot of time together shooting the shit, swearing, and talking about girls. One girl in particular, named Allison, has caught the eyes of both guys, with Josh being more vocal about his crush. One day, the two boys are hanging out with a couple of other friends of theirs, and all is going just hunky dory. And then, shit hits the fan. Tragedy occurs! Blood is on thy hands! So the rest of the film deals with the aftermath of what happens on that fatal afternoon.
What I admired so much about "Super Dark Times" is how it portrays the different psychological effects it has on Zach and Josh. The two respond to what happened that day in ways that are both understandable and incredibly believable. The two actors playing Zach and Josh, Owen Campbell and Charlie Tahan, give genuinely human performances that, being the central core of the story, elevates the entire film from good to great. Elizabeth Cappuccino delivers strong work as well as Erica, with her chemistry with the boys, again, being incredibly believable. Their relationship together is very similar to a romantic subplot in the 2013 indie "The Kings of Summer," yet this movie manages to take that type of subplot and make it feel fresh once again. The film wouldn't work as well as it does, though, if it wasn't for Ben Collins and Luke Piotrowski's dark and intense screenplay, as well as Kevin Phillips' eye for getting the best performances out of these actors and telling such a powerful story.
It's not a perfect movie by any means, with the lack of intelligence from other characters when trying to put two and two together being questionable, as well as how the opening scene doesn't feel necessary to the story when thinking back on it. That being said, I was both on the edge of and pressing back into my seat throughout this movie. "Super Dark Times" is, as the title suggests, super dark. The performances are stellar, the script sharply written, and the film well-directed as a whole. For his feature film debut, director Kevin Phillips has crafted an intense, relatable, and fascinating look into the teen psyche from multiple subjects. Elements of "Donnie Darko," "Stand By Me," and "The Dirties" are scattered throughout, but I can see this becoming something of a new cult classic along the lines of those films. If you're attending the Tribeca Film Festival currently, attending another festival this is playing at, or if it ends up coming to a town near you when it's eventually released, I'd highly recommend going and checking this out. You won't come out of the film feeling good, but you will come out feeling incredibly satisfied, tense, and maybe even horrified.
Charlize Theron, James McAvoy
At this year's SXSW Film Festival in Austin, Texas, two action films duked it out for the love of those attending the world-famous festival. The first of those films was Edgar Wright's "Baby Driver," which I found to be a fun movie with a decent chunk of flaws that kept me from loving it. The second film was "Atomic Blonde," the Charlize Theron-starring flick based on the graphic novel "The Coldest City." Both films are different in story and tone, and both have their fair share of flaws. Contrary to popular opinion, though, I have to say that I enjoyed "Atomic Blonde" a whole lot more. For those who don't know, "Atomic Blonde" takes place at the tail-end of the Cold War, just as the Berlin Wall is coming down. Charlize Theron plays MI6 Agent Lorraine Broughton, and Broughton is sent undercover to Berlin, Germany to both investigate the murder of a fellow agent, as well as obtain a list containing all the names of double agents. Because this is an action movie, expect a lot of blood and combat between Lorraine and everyone who gets in her way.
Before anyone's panties get twisted and accuse me of being crazy and wrong for liking "Blonde" more than "Baby," let me point out again, I don't think either are perfect movies. In fact, there are points where "Blonde" is a much inferior film to "Baby." For example, even though I found the story and characters to be more interesting in the former, the intelligence of some of the characters, particularly our lead, is very questionable at times. Without spoiling too much, Broughton is made out to be an intelligent badass, and yet the film makes it pretty clear in telling you who the real double agent is early on and why you shouldn't trust him or her. And yet, Broughton still manages to trust them despite having been deceived by them at least once already. For a character who is otherwise very intelligent, she could be very naive and dumb as well. It's because of this that the film also suffers from being pretty predictable at certain points. Not too predictable where it irritated me or anything, but enough where it became noticeable and slightly glaring at times.
So yeah, that's all I got. That's everything negative I can say about the movie. I had a blast with "Atomic Blonde," plain and simple. If you thought Charlize Theron was a badass as Furiosa in "Mad Max: Fury Road," she somehow managed to surpass herself on the badass meter. The dedication to her physical performance is quite admirable, and even if you might have problems with her British accent's consistency, I was able to let go of that because of the amount of commitment she had to the role, among other reasons. James McAvoy is just as fun as he always is playing the charismatic David Percival, someone who consistently keeps you asking yourself whether or not you can trust him. Everyone else in the supporting cast, from John Goodman to Eddie Marsen to Sofia Boutella all have their moments to shine, and all overall deliver solid performances.
The strongest tool the movie has is director David Leitch. Having co-directed the wickedly awesome "John Wick" previously, Leitch manages to tell a captivatingly interesting and entertaining story, as well as incorporate some impressive-as-hell action sequences into the mix. There's an action sequence that happens at the end of the second act that is meant to simulate a single take. While it's obvious where the cuts are in the scene, the fact that it doesn't take away from the sheer brilliance of the sequence (which lasts about 10-15 minutes, by the way) really says something about Leitch as a director. The choices of colors in certain set pieces, as well as the stellar music choices, add to the cyberpunk neo-noir vibe Leitch is attempting, and successfully, pulling off. If anyone had any worries about Leitch being the director of the upcoming "Deadpool 2," "Atomic Blonde" will throw all of them away.
Like I said before, "Atomic Blonde" isn't without its flaws. I know some who had more problems with it than me, but hey, it's all about taste, I guess. While the soundtrack and the action sequences were pretty good in "Baby Driver," I found the soundtrack and action sequences in this movie to be much more captivating, inventive, fun, and exciting. The editing in "Baby" is infinitely better than the editing in "Blonde," but as I said, "Blonde" kept my interest and adrenaline flowing more so than "Baby." It might not be fair to compare the two films to one another, but considering they're both R-rated action thrillers with unique soundtracks and talented directors that happened to both play at this year's SXSW Film Festival, I'd say it's fair enough.
So overall, I found "Atomic Blonde" to be a thrilling and fun action/spy thriller that kept my eyes glued to the screen all the way through. Charlize Theron kicks ass, the soundtrack rocks, the action itself ranged from awesome to jaw-dropping, and the story kept me invested enough to want to continue following Lorraine throughout her endeavors. With some of the action sequences that take place during the film, particularly the aforementioned 15-minute single take scene, make this flick something that deserves to be seen on the big screen. I cannot wait to see this movie again, and I really hope I'm able to see it on the biggest screen possible with the loudest sound out there. Whether you think this is better or worse than "Baby Driver" is up to you, but regardless, both are worth seeing on the big screen. This one just grabbed me more, I guess.
Labels: 1989, atomic blonde, berlin, charlize theron, focus features, james mcavoy, the coldest city, universal pictures


My wife and I welcomed our daughter back in February of this year. I knew having a baby would be tough, but whoa. We were out of our depth. Even though we're both involved parents, thanks to the exhaustion and the general chaos that comes with having an infant, we still had a hard time keeping up.
Being a parent isn't always stressful, but in our experience, those moments can leave you feeling helpless and unimaginably frustrated. We needed a tool to help reduce our stress and exhaustion, so we could spend more time enjoying our little monster.
So I'm building Newbee to help make life a little easier for new parents like us.
Newbee is based on a foundation of logging and tracking events that occur on a daily basis in your infant's life, like eating, sleeping, diaper changes, play time, trips, and more.
You can use the data you collect to identify patterns in your infant's life, useful for narrowing down the causes of sleep problems or watching for allergies or illness, and to track growth and development over time. To keep you and your family on the same page, all your events are securely synced with Newbee's servers—"the cloud," if you're into the whole brevity thing. If your little teething darling drools all over your phone or accidentally throws your tablet in the sink, your data is safe and sound.
Thoughtfully designed for busy, tired parents.
Parents already have enough going on without having to bother with constant data entry demands, but a service like Newbee is most helpful when it has as more information to work with. I'm focusing on making Newbee quick and easy to use so you can find what you're looking for and do what you're trying to do as fast as possible. Everything you'll want to do is only a tap away, in the Actions menu.
Every family that uses Newbee gets their own (aptly named) Family account, which can include as many family members as you want at no additional cost. No need to pay every time you download the app, no awkward pairing process to share data.
Beyond parents and children, you can also add Helpers with limited access to your Family account. Grandparents, babysitters, nannies, and anyone else caring for your little one can accept your invitation to join your Family as a Helper, and be adding events in seconds. You can even restrict Helpers to a time period you specify without providing access to any events or account information he or she doesn't need.
There are more additional features than I have room to cover right now. I'll be sharing details about many of these over the next few weeks, but until then, a short list: Night Mode keeps your phone dim for you and your baby during nighttime wakings; a noise generator to help everybody relax; Nightstand Mode for tablets, with the current time, feeding details, and sleep timers; optional push notifications for instant updates when you need them; and, you can export your data at any time.
Blended families, rejoice: Newbee works on phones and tablets running iOS, Android, Windows Phone, and Firefox OS!
And that's just for starters. Newbee is built using web technologies, which means it's flexible enough to work on any device with a modern web browser. Besides most phones and tablets, it also works on desktop and laptop computers, and even some crazy places like smart TVs, video game consoles, automobiles, and beyond!
I'm designing my own icon set to use in Newbee, and I'll be uploading the entire set of icons to The Noun Project with a Creative Commons CC-BY license so they're available for everyone to use!
I'm using the Cooper Hewitt typeface in Newbee, and I've made a few slight modifications to make it easier to read at small sizes. Your groggy eyes will thank you, and I'll be making the resulting fonts available under the Open Font License once I've had a chance to finish and test all the tweaks.
Your name will be listed on Newbee's Special Thanks page for as long as Newbee exists, as well as access to double-top-secret backer-only updates. And, not to mention, my sincere thanks for helping make this a reality.
Note: the final colors may vary slightly.
You'll get a 1-year subscription to Newbee for 33% off the standard annual rate. Move quick, only 500 are available! Plus, you'll receive the $5 and $1 rewards.
A 1-year subscription to Newbee, plus early access to Newbee's beta testing phase in November. You'll be one of the very first to try it out, and your feedback will help guide its growth and development. And, of course, you'll receive the $5 and $1 rewards too.
You'll get a fantastic Newbee t-shirt, printed with the help of the good folks at Cotton Bureau in Pennsylvania.
This is just a mockup, the final shirt design will vary slightly.
It's a soft, tri-blend shirt in heather black to hide those inevitable stains, featuring the Newbee logo printed using water-based inks to make sure it's even softer. Feel free to snuggle away.
And if that isn't enough, you'll also get the $24 rewards, including a 1-year Newbee subscription and early access in November!
For those of you without children: imagine how good your life could be if all your friends, family, and coworkers with babies were better off thanks to Newbee? Put some of that disposable income to good use and help bring some much-needed relief to the new parents in your world. You'll get a Newbee sticker and your name on the "special thanks" page. Now go enjoy getting a good night's sleep, you lucky bum.
Features for expecting mothers like stat tracking and contraction timers.
I've been working on Newbee since May and have made solid progress on its foundation, but I need to focus on building it full-time if it's going to be as awesome and useful as it can be. Rather than seeking out some kind of investment to provide the funding I need, I'd prefer to look to the families who could use something like Newbee.
Beyond financial assistance, connecting with a large number of families through a fundraiser like this means I'll be able to get a wider variety of feedback as Newbee is developed. I'm trying to make Newbee useful for as many families and situations as possible, so the more voices I hear from throughout the process, the better it'll be for everyone!
Since I'm solely responsible for nearly every aspect of Newbee, it's possible that an expected injury or personal tragedy could impede development. However, this is statistically unlikely as I'm currently of good physical and mental health (just had my annual physical; you should see my glomerular filtration rate!) and lead a low-risk (read: dull) lifestyle.
As with any software development, it's difficult to predict which features, or aspects of a feature, will end up being more time-consuming than expected. However, from the work I've already completed, I don't anticipate any major problems finishing Newbee's core features that would cause me to miss the estimated v.1.0 release timeframe of December 2014.


Based on the acclaimed book 'History on Trial: My Day in Court with a Holocaust Denier', Denial recounts Deborah E. Lipstadt's (Rachel Weisz) legal battle for historical truth against David Irving (Timothy Spall), who accused her of libel when she declared him a Holocaust denier. In the English legal system, the burden of proof is on the accused, therefore it was up to Lipstadt and her legal team to prove the essential truth that the Holocaust occurred.
The movie soundtrack of Denial will be released on September 30, 2016 by Howe Records.


text:
Mosquitoes Capture Crook
I can validate this. The mosquitoes are nuts around here.
Authorities reportedly followed Wilson as part of a high-speed chase, before Wilson got out of the car and fled on foot.
The area was flooded with mosquitoes — so much so that Chief Thomas Dornbrook said when sheriff's office officials arrived, he immediately asked for bug spray.
"That's the first thing I asked for, if he could give me some spray because it was crazy," Dornbrook told the news station.
Nearly an hour later, Wilson turned himself in to police because he could no longer endure the mosquitoes nipping at his skin.
"When we handcuffed him he asked us to wipe his forehead because he had 15-20 mosquitoes on his forehead at that time," the police chief said.