aboutsummaryrefslogtreecommitdiffstats
path: root/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java
blob: 110aaf2b1a614f5de6346399f6daeada5881702f (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
// 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.athenz.api.OktaIdentityToken;
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.api.integration.athenz.ApplicationAction;
import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
import com.yahoo.vespa.hosted.controller.athenz.HostedAthenzIdentities;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactoryMock;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzDbMock;
import com.yahoo.vespa.hosted.controller.deployment.BuildJob;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentSteps;
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();
        upgrader = new Upgrader(controller(), Duration.ofDays(1), new JobControl(curatorDb), curatorDb);
        upgrader.setUpgradesPerMinute(100); // Anything to make it more than one per maintenance interval.
    }

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

    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 OktaIdentityToken("okta-identity-token"), new OktaAccessToken("okta-access-token"));
        AthenzTenantSpec tenantSpec = new AthenzTenantSpec(TenantName.from(tenant),
                                                           domain1,
                                                           new Property("property1"),
                                                           Optional.of(new PropertyId("1234")));
        controller().tenants().create(tenantSpec, credentials);

        TenantAndApplicationId id = TenantAndApplicationId.from(tenant, application);
        controller().applications().createApplication(id, Optional.of(credentials));
        controller().applications().createInstance(id.instance(instance));
        return controller().applications().requireApplication(id);
    }

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

    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());
        // TODO jonmv: Connect instances from deployment spec to deployments below.
        boolean succeeding = true;
        for (var job : steps.jobs()) {
            if (!succeeding) return;
            var zone = job.zone(controller().system());
            deploy(application.id().defaultInstance(), applicationPackage, zone);
            if (failStaging && zone.environment() == Environment.staging) {
                succeeding = false;
            }
            if (zone.environment().isTest()) {
                controller().applications().deactivate(application.id().defaultInstance(), 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, containerTester.serviceRegistry().artifactRepositoryMock()).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, TenantAndApplicationId id) {
        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(id.application().value()))
                .addRoleMember(action, HostedAthenzIdentities.from(screwdriverId));
    }

    private void notifyJobCompletion(DeploymentJobs.JobReport report) {
        MockBuildService buildService = containerTester.serviceRegistry().buildServiceMock();
        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;
    }

}