aboutsummaryrefslogtreecommitdiffstats
path: root/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java
blob: 54fe575a3651bd4c2ba47f34975b0f9a7f814d3a (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
162
163
164
165
166
167
// 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.restapi;

import com.yahoo.application.container.JDisc;
import com.yahoo.application.container.handler.Request;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.athenz.api.AthenzDomain;
import com.yahoo.vespa.athenz.api.AthenzPrincipal;
import com.yahoo.vespa.athenz.api.AthenzUser;
import com.yahoo.vespa.athenz.api.OktaAccessToken;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions;
import com.yahoo.vespa.hosted.controller.api.identifiers.Property;
import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId;
import com.yahoo.vespa.hosted.controller.api.identifiers.ScrewdriverId;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockBuildService;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.DeploymentJobs;
import com.yahoo.vespa.hosted.controller.athenz.ApplicationAction;
import com.yahoo.vespa.hosted.controller.athenz.HostedAthenzIdentities;
import com.yahoo.vespa.hosted.controller.athenz.mock.AthenzClientFactoryMock;
import com.yahoo.vespa.hosted.controller.athenz.mock.AthenzDbMock;
import com.yahoo.vespa.hosted.controller.deployment.BuildJob;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentSteps;
import com.yahoo.vespa.hosted.controller.integration.ArtifactRepositoryMock;
import com.yahoo.vespa.hosted.controller.maintenance.JobControl;
import com.yahoo.vespa.hosted.controller.maintenance.Upgrader;
import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
import com.yahoo.vespa.hosted.controller.security.AthenzCredentials;
import com.yahoo.vespa.hosted.controller.security.AthenzTenantSpec;

import java.io.File;
import java.time.Duration;
import java.util.Optional;

import static com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType.component;
import static org.junit.Assert.assertFalse;

/**
 * Provides testing of controller functionality accessed through the container
 * 
 * @author bratseth
 */
public class ContainerControllerTester {

    private final ContainerTester containerTester;
    private final Upgrader upgrader;

    public ContainerControllerTester(JDisc container, String responseFilePath) {
        containerTester = new ContainerTester(container, responseFilePath);
        CuratorDb curatorDb = controller().curator();
        curatorDb.writeUpgradesPerMinute(100);
        upgrader = new Upgrader(controller(), Duration.ofDays(1), new JobControl(curatorDb), curatorDb);
    }

    public Controller controller() { return containerTester.controller(); }

    public ArtifactRepositoryMock artifactRepository() {
        return (ArtifactRepositoryMock) containerTester.container().components()
                                                       .getComponent(ArtifactRepositoryMock.class.getName());
    }

    public Upgrader upgrader() { return upgrader; }

    /** Returns the wrapped generic container tester */
    public ContainerTester containerTester() { return containerTester; }

    public Application createApplication() {
        return createApplication("domain1","tenant1", "application1", "default");
    }

    public Application createApplication(String athensDomain, String tenant, String application, String instance) {
        AthenzDomain domain1 = addTenantAthenzDomain(athensDomain, "user");
        AthenzPrincipal user = new AthenzPrincipal(new AthenzUser("user"));
        AthenzCredentials credentials = new AthenzCredentials(user, domain1, new OktaAccessToken("okta-token"));
        AthenzTenantSpec tenantSpec = new AthenzTenantSpec(TenantName.from(tenant),
                                                           domain1,
                                                           new Property("property1"),
                                                           Optional.of(new PropertyId("1234")));
        controller().tenants().create(tenantSpec, credentials);

        ApplicationId app = ApplicationId.from(tenant, application, instance);
        return controller().applications().createApplication(app, Optional.of(credentials));
    }

    public Application deploy(Application application, ApplicationPackage applicationPackage, ZoneId zone) {
        controller().applications().deploy(application.id(), zone, Optional.of(applicationPackage),
                                           new DeployOptions(false, Optional.empty(), false, false));
        return application;
    }

    public void deployCompletely(Application application, ApplicationPackage applicationPackage, long projectId,
                                 boolean failStaging) {
        jobCompletion(JobType.component).application(application)
                                        .projectId(projectId)
                                        .uploadArtifact(applicationPackage)
                                        .submit();
        DeploymentSteps steps = controller().applications().deploymentTrigger().steps(applicationPackage.deploymentSpec());
        boolean succeeding = true;
        for (var job : steps.jobs()) {
            if (!succeeding) return;
            var zone = job.zone(controller().system());
            deploy(application, applicationPackage, zone);
            if (failStaging && zone.environment() == Environment.staging) {
                succeeding = false;
            }
            if (zone.environment().isTest()) {
                controller().applications().deactivate(application.id(), zone);
            }
            jobCompletion(job).application(application).success(succeeding).projectId(projectId).submit();
        }
    }

    /** Notify the controller about a job completing */
    public BuildJob jobCompletion(JobType job) {
        return new BuildJob(this::notifyJobCompletion, artifactRepository()).type(job);
    }

    // ---- Delegators:
    
    public void assertResponse(Request request, File expectedResponse) {
        containerTester.assertResponse(request, expectedResponse);
    }

    public void assertResponse(Request request, String expectedResponse, int expectedStatusCode) {
        containerTester.assertResponse(request, expectedResponse, expectedStatusCode);
    }

    /*
     * Authorize action on tenantDomain/application for a given screwdriverId
     */
    public void authorize(AthenzDomain tenantDomain, ScrewdriverId screwdriverId, ApplicationAction action, Application application) {
        AthenzClientFactoryMock mock = (AthenzClientFactoryMock) containerTester.container().components()
                .getComponent(AthenzClientFactoryMock.class.getName());

        mock.getSetup()
                .domains.get(tenantDomain)
                .applications.get(new com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId(application.id().application().value()))
                .addRoleMember(action, HostedAthenzIdentities.from(screwdriverId));
    }

    private void notifyJobCompletion(DeploymentJobs.JobReport report) {
        MockBuildService buildService = (MockBuildService) containerTester.container().components().getComponent(MockBuildService.class.getName());
        if (report.jobType() != component && ! buildService.remove(report.buildJob()))
            throw new IllegalArgumentException(report.jobType() + " is not running for " + report.applicationId());
        assertFalse("Unexpected entry '" + report.jobType() + "@" + report.projectId() + " in: " + buildService.jobs(),
                    buildService.remove(report.buildJob()));
        controller().applications().deploymentTrigger().notifyOfCompletion(report);
        controller().applications().deploymentTrigger().triggerReadyJobs();
    }

    private AthenzDomain addTenantAthenzDomain(String domainName, String userName) {
        AthenzClientFactoryMock mock = (AthenzClientFactoryMock) containerTester.container().components()
                                                                                .getComponent(AthenzClientFactoryMock.class.getName());
        AthenzDomain athensDomain = new AthenzDomain(domainName);
        AthenzDbMock.Domain domain = mock.getSetup().getOrCreateDomain(athensDomain);
        domain.markAsVespaTenant();
        domain.admin(new AthenzUser(userName));
        return athensDomain;
    }

}