aboutsummaryrefslogtreecommitdiffstats
path: root/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java
blob: 70775bc9c0088dd352c43a3d2dffc9ca550a5191 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.rotation;

import com.yahoo.config.provision.SystemName;
import com.yahoo.vespa.hosted.controller.ControllerTester;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.AssignedRotation;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext;
import com.yahoo.vespa.hosted.controller.deployment.InternalDeploymentTester;
import com.yahoo.vespa.hosted.rotation.config.RotationsConfig;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import java.net.URI;
import java.util.List;
import java.util.stream.Collectors;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

/**
 * @author Oyvind Gronnesby
 * @author mpolden
 */
public class RotationRepositoryTest {

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    private final RotationsConfig rotationsConfig = new RotationsConfig(
        new RotationsConfig.Builder()
            .rotations("foo-1", "foo-1.com")
            .rotations("foo-2", "foo-2.com")
    );

    private final RotationsConfig rotationsConfigWhitespaces = new RotationsConfig(
            new RotationsConfig.Builder()
                .rotations("foo-1", "\n  \t     foo-1.com      \n")
                .rotations("foo-2", "foo-2.com")
        );

    private final ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
            .globalServiceId("foo")
            .region("us-east-3")
            .region("us-west-1")
            .build();

    private InternalDeploymentTester tester;
    private RotationRepository repository;
    private DeploymentContext application;

    @Before
    public void before() {
        tester = new InternalDeploymentTester(new ControllerTester(rotationsConfig));
        repository = tester.applications().rotationRepository();
        application = tester.newDeploymentContext("tenant1", "app1", "default");
    }

    @Test
    public void assigns_and_reuses_rotation() {
        // Submitting assigns a rotation
        application.submit(applicationPackage);
        Rotation expected = new Rotation(new RotationId("foo-1"), "foo-1.com");

        assertEquals(List.of(expected.id()), rotationIds(application.instance().rotations()));
        assertEquals(URI.create("https://app1--tenant1.global.vespa.oath.cloud:4443/"),
                     application.instance().endpointsIn(SystemName.main).main().get().url());
        try (RotationLock lock = repository.lock()) {
            List<AssignedRotation> rotations = repository.getOrAssignRotations(application.application().deploymentSpec(),
                                                                               application.instance(),
                                                                              lock);
            assertSingleRotation(expected, rotations, repository);
        }

        // Submitting once more assigns same rotation
        application.submit(applicationPackage);
        assertEquals(List.of(expected.id()), rotationIds(application.instance().rotations()));
    }
    
    @Test
    public void strips_whitespace_in_rotation_fqdn() {
        tester = new InternalDeploymentTester(new ControllerTester(rotationsConfigWhitespaces));
        RotationRepository repository = tester.controller().applications().rotationRepository();
        var application2 = tester.newDeploymentContext("tenant1", "app2", "default");

        application2.submit(applicationPackage);

        try (RotationLock lock = repository.lock()) {
            List<AssignedRotation> rotations = repository.getOrAssignRotations(application2.application().deploymentSpec(), application2.instance(), lock);
            Rotation assignedRotation = new Rotation(new RotationId("foo-1"), "foo-1.com");
            assertSingleRotation(assignedRotation, rotations, repository);
        }
    }

    @Test
    public void out_of_rotations() {
        // Assigns 1 rotation
        application.submit(applicationPackage);

        // Assigns 1 more
        var application2 = tester.newDeploymentContext("tenant2", "app2", "default");
        application2.submit(applicationPackage);

        // We're now out of rotations
        thrown.expect(IllegalStateException.class);
        thrown.expectMessage("no rotations available");
        var application3 = tester.newDeploymentContext("tenant3", "app3", "default");
        application3.submit(applicationPackage);
    }

    @Test
    public void too_few_zones() {
        ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
                .globalServiceId("foo")
                .region("us-east-3")
                .build();
        thrown.expect(RuntimeException.class);
        thrown.expectMessage("less than 2 prod zones are defined");
        application.submit(applicationPackage);
    }

    @Test
    public void no_rotation_assigned_for_application_without_service_id() {
        ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
                .region("us-east-3")
                .region("us-west-1")
                .build();
        application.submit(applicationPackage);
        assertTrue(application.instance().rotations().isEmpty());
    }

    @Test
    public void prefixes_system_when_not_main() {
        ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
                .globalServiceId("foo")
                .region("us-east-3")
                .region("us-west-1")
                .build();
        var application2 = tester.newDeploymentContext("tenant2", "app2", "default");
        application2.submit(applicationPackage);
        assertEquals(List.of(new RotationId("foo-1")), rotationIds(application2.instance().rotations()));
        assertEquals("https://cd--app2--tenant2.global.vespa.oath.cloud:4443/",
                     application2.instance().endpointsIn(SystemName.cd).main().get().url().toString());
    }

    private void assertSingleRotation(Rotation expected, List<AssignedRotation> assignedRotations, RotationRepository repository) {
        assertEquals(1, assignedRotations.size());
        var rotationId = assignedRotations.get(0).rotationId();
        var rotation = repository.getRotation(rotationId);
        assertTrue(rotationId + " exists", rotation.isPresent());
        assertEquals(expected, rotation.get());
    }

    private static List<RotationId> rotationIds(List<AssignedRotation> assignedRotations) {
        return assignedRotations.stream().map(AssignedRotation::rotationId).collect(Collectors.toUnmodifiableList());
    }

}