package com.yahoo.config.application.api; import com.yahoo.config.provision.RegionName; import com.yahoo.yolean.Exceptions; import org.junit.Test; import java.io.StringReader; import java.time.Duration; import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; /** * @author bratseth */ public class DeploymentSpecWithBcpTest { @Test public void minimalProductionSpecWithExplicitBcp() { StringReader r = new StringReader(""" us-east1 us-west1 us-east1 us-west1 """); assertTwoRegions(DeploymentSpec.fromXml(r)); } @Test public void specWithoutInstanceWithBcp() { StringReader r = new StringReader(""" us-east1 us-west1 us-east1 us-west1 """); assertTwoRegions(DeploymentSpec.fromXml(r)); } @Test public void complexBcpSetup() { StringReader r = new StringReader(""" us-east1 us-east2 us-east1 us-east2 us-east1 us-east2 us-central1 us-west1 us-west2 eu-east1 eu-west1 us-east1 us-east2 us-central1 us-west1 us-west2 us-central1 eu-east1 eu-west1 """); var spec = DeploymentSpec.fromXml(r); var betaBcp = spec.requireInstance("beta").bcp(); assertEquals(1, betaBcp.groups().size()); var betaGroup = betaBcp.groups().get(0); assertEquals(2, betaGroup.members().size()); assertEquals(Duration.ofMinutes(60), betaGroup.deadline()); assertEquals(new Bcp.RegionMember(RegionName.from("us-east1"), 1.0), betaGroup.members().get(0)); assertEquals(new Bcp.RegionMember(RegionName.from("us-east2"), 1.0), betaGroup.members().get(1)); var mainBcp = spec.requireInstance("main").bcp(); assertEquals(7, mainBcp.regions().size()); assertEquals(3, mainBcp.groups().size()); var usEast = mainBcp.groups().get(0); assertEquals(3, usEast.members().size()); assertEquals(Duration.ofMinutes(0), usEast.deadline()); assertEquals(new Bcp.RegionMember(RegionName.from("us-east1"), 1.0), usEast.members().get(0)); assertEquals(new Bcp.RegionMember(RegionName.from("us-east2"), 1.0), usEast.members().get(1)); assertEquals(new Bcp.RegionMember(RegionName.from("us-central1"), 0.3), usEast.members().get(2)); var usWest = mainBcp.groups().get(1); assertEquals(3, usWest.members().size()); assertEquals(Duration.ofMinutes(0), usWest.deadline()); assertEquals(new Bcp.RegionMember(RegionName.from("us-west1"), 1.0), usWest.members().get(0)); assertEquals(new Bcp.RegionMember(RegionName.from("us-west2"), 1.0), usWest.members().get(1)); assertEquals(new Bcp.RegionMember(RegionName.from("us-central1"), 0.7), usWest.members().get(2)); var eu = mainBcp.groups().get(2); assertEquals(2, eu.members().size()); assertEquals(Duration.ofMinutes(30), eu.deadline()); assertEquals(new Bcp.RegionMember(RegionName.from("eu-east1"), 1.0), eu.members().get(0)); assertEquals(new Bcp.RegionMember(RegionName.from("eu-west1"), 1.0), eu.members().get(1)); } @Test public void regionMembershipMatchValidation1() { try { StringReader r = new StringReader(""" us-east1 us-west1 us-west1 """); DeploymentSpec.fromXml(r); fail(); } catch (IllegalArgumentException e) { assertEquals("BCP and deployment mismatch in instance 'default': " + "A element must place all deployed production regions in at least one group, " + "and declare no extra regions. " + "Deployed regions: [us-east1, us-west1]. BCP regions: [us-west1]", Exceptions.toMessageString(e)); } } @Test public void regionMembershipMatchValidation2() { try { StringReader r = new StringReader(""" us-west1 us-east1 us-west1 """); DeploymentSpec.fromXml(r); fail(); } catch (IllegalArgumentException e) { assertEquals("BCP and deployment mismatch in instance 'default': " + "A element must place all deployed production regions in at least one group, " + "and declare no extra regions. " + "Deployed regions: [us-west1]. BCP regions: [us-east1, us-west1]", Exceptions.toMessageString(e)); } } @Test public void deadlineValidation() { try { StringReader r = new StringReader(""" us-east1 us-west1 us-east1 us-west1 """); DeploymentSpec.fromXml(r); fail(); } catch (IllegalArgumentException e) { assertEquals("Illegal deadline 'fast': Must be an integer followed by 'm', 'h' or 'd'", Exceptions.toMessageString(e)); } } @Test public void fractionalMembershipValidation() { try { StringReader r = new StringReader(""" us-east1 us-west1 us-east1 us-west1 """); DeploymentSpec.fromXml(r); fail(); } catch (IllegalArgumentException e) { assertEquals("Illegal BCP spec: All regions must have total membership fractions summing to 1.0, but us-east1 sums to 0.9", Exceptions.toMessageString(e)); } } @Test public void endpointsDefinedInBcp() { StringReader r = new StringReader(""" us-east1 us-east2 us-east1 us-east2 us-east1 us-east2 us-central1 us-west1 us-west2 us-east1 us-east2 us-central1 us-west1 us-west2 us-central1 """); var spec = DeploymentSpec.fromXml(r); var betaEndpoints = spec.requireInstance("beta").endpoints(); assertEquals(1, betaEndpoints.size()); assertEquals("foo", betaEndpoints.get(0).endpointId()); assertEquals("bar", betaEndpoints.get(0).containerId()); assertEquals(List.of(RegionName.from("us-east1"), RegionName.from("us-east2")), betaEndpoints.get(0).regions()); var mainEndpoints = spec.requireInstance("main").endpoints(); assertEquals(2, mainEndpoints.size()); assertEquals("east", mainEndpoints.get(0).endpointId()); assertEquals(List.of(RegionName.from("us-east1"), RegionName.from("us-east2"), RegionName.from("us-central1")), mainEndpoints.get(0).regions()); assertEquals("west", mainEndpoints.get(1).endpointId()); assertEquals(List.of(RegionName.from("us-west1"), RegionName.from("us-west2"), RegionName.from("us-central1")), mainEndpoints.get(1).regions()); } @Test public void endpointsDefinedInBcpImplicitInstance() { StringReader r = new StringReader(""" us-east1 us-east2 us-central1 us-west1 us-west2 us-east1 us-east2 us-central1 us-west1 us-west2 us-central1 """); var spec = DeploymentSpec.fromXml(r); var mainEndpoints = spec.requireInstance("default").endpoints(); assertEquals(2, mainEndpoints.size()); assertEquals("east", mainEndpoints.get(0).endpointId()); assertEquals(List.of(RegionName.from("us-east1"), RegionName.from("us-east2"), RegionName.from("us-central1")), mainEndpoints.get(0).regions()); assertEquals("west", mainEndpoints.get(1).endpointId()); assertEquals(List.of(RegionName.from("us-west1"), RegionName.from("us-west2"), RegionName.from("us-central1")), mainEndpoints.get(1).regions()); } @Test public void endpointsDefinedInBcpValidation1() { StringReader r = new StringReader(""" us-east1 us-east2 us-east1 us-east2 """); try { DeploymentSpec.fromXml(r); } catch (IllegalArgumentException e) { assertEquals("The default element at the root cannot define endpoints", Exceptions.toMessageString(e)); } } @Test public void endpointsDefinedInBcpValidation2() { StringReader r = new StringReader(""" us-east1 us-east2 us-east1 us-east2 us-east1 """); try { DeploymentSpec.fromXml(r); } catch (IllegalArgumentException e) { assertEquals("Endpoints in cannot contain children", Exceptions.toMessageString(e)); } } private void assertTwoRegions(DeploymentSpec spec) { var bcp = spec.requireInstance("default").bcp(); assertEquals(1, bcp.groups().size()); var group = bcp.groups().get(0); assertEquals(2, group.members().size()); assertEquals(Duration.ZERO, group.deadline()); assertEquals(new Bcp.RegionMember(RegionName.from("us-east1"), 1.0), group.members().get(0)); assertEquals(new Bcp.RegionMember(RegionName.from("us-west1"), 1.0), group.members().get(1)); } }