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
|
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.application.pkg;
import com.yahoo.config.application.api.DeploymentSpec;
import com.yahoo.config.application.api.ValidationId;
import org.junit.jupiter.api.Test;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
/**
* @author valerijf
* @author jonmv
*/
public class ApplicationPackageTest {
static final String deploymentXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<deployment version=\"1.0\">\n" +
" <test />\n" +
" <prod>\n" +
" <parallel>\n" +
" <region active=\"true\">us-central-1</region>\n" +
" </parallel>\n" +
" </prod>\n" +
"</deployment>\n";
static final String servicesXml = "<services version='1.0' xmlns:deploy=\"vespa\" xmlns:preprocess=\"properties\">\n" +
" <preprocess:include file='jdisc.xml' />\n" +
" <content version='1.0' if='foo' />\n" +
" <content version='1.0' id='foo' deploy:environment='staging prod' deploy:region='us-east-3 us-central-1'>\n" +
" <preprocess:include file='content/content.xml' />\n" +
" </content>\n" +
" <preprocess:include file='not_found.xml' required='false' />\n" +
"</services>\n";
private static final String jdiscXml = "<container id='stateless' version='1.0' />\n";
private static final String contentXml = "<documents>\n" +
" <document type=\"music.sd\" mode=\"index\" />\n" +
"</documents>\n" +
"<preprocess:include file=\"nodes.xml\" />";
private static final String nodesXml = "<nodes>\n" +
" <node hostalias=\"node0\" distribution-key=\"0\" />\n" +
"</nodes>";
@Test
void test_createEmptyForDeploymentRemoval() {
ApplicationPackage app = ApplicationPackage.deploymentRemoval();
assertEquals(DeploymentSpec.empty, app.deploymentSpec());
assertEquals(List.of(), app.trustedCertificates());
for (ValidationId validationId : ValidationId.values()) {
assertTrue(app.validationOverrides().allows(validationId, Instant.now()));
}
}
@Test
void testMetaData() {
byte[] zip = ApplicationPackage.filesZip(Map.of("services.xml", servicesXml.getBytes(UTF_8),
"jdisc.xml", jdiscXml.getBytes(UTF_8),
"content/content.xml", contentXml.getBytes(UTF_8),
"content/nodes.xml", nodesXml.getBytes(UTF_8),
"gurba", "gurba".getBytes(UTF_8)));
assertEquals(Map.of("services.xml", servicesXml,
"jdisc.xml", jdiscXml,
"content/content.xml", contentXml,
"content/nodes.xml", nodesXml),
unzip(new ApplicationPackage(zip, false).metaDataZip()));
}
@Test
void testMetaDataWithMissingFiles() {
byte[] zip = ApplicationPackage.filesZip(Map.of("services.xml", servicesXml.getBytes(UTF_8)));
try {
new ApplicationPackage(zip, false).metaDataZip();
fail("Should fail on missing include file");
}
catch (RuntimeException e) {
assertEquals("./jdisc.xml", e.getCause().getMessage());
}
}
@Test
void testAbsoluteInclude() throws Exception {
try {
getApplicationZip("include-absolute.zip");
fail("Should fail on include file outside zip");
}
catch (RuntimeException e) {
assertEquals(IllegalArgumentException.class, e.getClass());
}
}
@Test
void testParentInclude() throws Exception {
try {
getApplicationZip("include-parent.zip");
fail("Should fail on include file outside zip");
}
catch (RuntimeException e) {
assertEquals("./../not_found.xml is not a descendant of .", e.getMessage());
}
}
@Test
void testBundleHashesAreSameWithDifferentDeploymentXml() throws Exception {
var originalPackage = getApplicationZip("original.zip");
var changedServices = getApplicationZip("changed-services-xml.zip");
var changedDeploymentXml = getApplicationZip("changed-deployment-xml.zip");
var similarDeploymentXml = getApplicationZip("similar-deployment-xml.zip");
// services.xml is changed -> different bundle hash
assertNotEquals(originalPackage.bundleHash(), changedServices.bundleHash());
// deployment.xml is changed, with real changes -> different bundle hash
assertNotEquals(originalPackage.bundleHash(), changedDeploymentXml.bundleHash());
// deployment.xml is changed, but only deployment orchestration settings -> same bundle hash
assertEquals(originalPackage.bundleHash(), similarDeploymentXml.bundleHash());
}
private static Map<String, String> unzip(byte[] zip) {
return ZipEntries.from(zip, __ -> true, 1 << 10, true)
.asList().stream()
.collect(Collectors.toMap(ZipEntries.ZipEntryWithContent::name,
entry -> new String(entry.contentOrThrow(), UTF_8)));
}
private ApplicationPackage getApplicationZip(String path) throws Exception {
return new ApplicationPackage(Files.readAllBytes(Path.of("src/test/resources/application-packages/" + path)), true);
}
}
|