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
|
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.integration;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ApplicationName;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.vespa.hosted.controller.NotExistsException;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationStore;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterId;
import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.time.Instant;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import static java.util.Objects.requireNonNull;
/**
* Threadsafe.
*
* @author jonmv
*/
public class ApplicationStoreMock implements ApplicationStore {
private static final byte[] tombstone = new byte[0];
private final Map<ApplicationId, Map<RevisionId, byte[]>> store = new ConcurrentHashMap<>();
private final Map<DeploymentId, byte[]> devStore = new ConcurrentHashMap<>();
private final Map<ApplicationId, Map<Long, byte[]>> diffs = new ConcurrentHashMap<>();
private final Map<DeploymentId, Map<Long, byte[]>> devDiffs = new ConcurrentHashMap<>();
private final Map<ApplicationId, NavigableMap<Instant, byte[]>> meta = new ConcurrentHashMap<>();
private final Map<DeploymentId, NavigableMap<Instant, byte[]>> metaManual = new ConcurrentHashMap<>();
private static ApplicationId appId(TenantName tenant, ApplicationName application) {
return ApplicationId.from(tenant, application, InstanceName.defaultName());
}
private static ApplicationId testerId(TenantName tenant, ApplicationName application) {
return TesterId.of(appId(tenant, application)).id();
}
@Override
public InputStream stream(DeploymentId deploymentId, RevisionId revisionId) {
if ( ! revisionId.isProduction())
return new ByteArrayInputStream(devStore.get(deploymentId));
TenantAndApplicationId tenantAndApplicationId = TenantAndApplicationId.from(deploymentId.applicationId());
byte[] bytes = store.get(appId(tenantAndApplicationId.tenant(), tenantAndApplicationId.application())).get(revisionId);
if (bytes == null) throw new NotExistsException("No " + revisionId + " found for " + tenantAndApplicationId);
return new ByteArrayInputStream(bytes);
}
@Override
public Optional<byte[]> getDiff(TenantName tenantName, ApplicationName applicationName, long buildNumber) {
return Optional.ofNullable(diffs.get(appId(tenantName, applicationName))).map(map -> map.get(buildNumber));
}
@Override
public void pruneDiffs(TenantName tenantName, ApplicationName applicationName, long beforeBuildNumber) {
Optional.ofNullable(diffs.get(appId(tenantName, applicationName)))
.ifPresent(map -> map.keySet().removeIf(buildNumber -> buildNumber < beforeBuildNumber));
}
@Override
public Optional<byte[]> find(TenantName tenant, ApplicationName application, long buildNumber) {
return store.getOrDefault(appId(tenant, application), Map.of()).entrySet().stream()
.filter(kv -> kv.getKey().number() == buildNumber)
.map(Map.Entry::getValue)
.findFirst();
}
@Override
public void put(TenantName tenant, ApplicationName application, RevisionId revision, byte[] bytes, byte[] tests, byte[] diff) {
store.computeIfAbsent(appId(tenant, application), __ -> new ConcurrentHashMap<>()).put(revision, bytes);
store.computeIfAbsent(testerId(tenant, application), key -> new ConcurrentHashMap<>()) .put(revision, tests);
diffs.computeIfAbsent(appId(tenant, application), __ -> new ConcurrentHashMap<>()).put(revision.number(), diff);
}
@Override
public void prune(TenantName tenant, ApplicationName application, RevisionId oldestToRetain) {
store.getOrDefault(appId(tenant, application), Map.of()).keySet().removeIf(version -> version.compareTo(oldestToRetain) < 0);
store.getOrDefault(testerId(tenant, application), Map.of()).keySet().removeIf(version -> version.compareTo(oldestToRetain) < 0);
}
@Override
public void removeAll(TenantName tenant, ApplicationName application) {
store.remove(appId(tenant, application));
store.remove(testerId(tenant, application));
}
@Override
public InputStream streamTester(TenantName tenant, ApplicationName application, RevisionId revision) {
return new ByteArrayInputStream(store.get(testerId(tenant, application)).get(revision));
}
@Override
public Optional<byte[]> getDevDiff(DeploymentId deploymentId, long buildNumber) {
return Optional.ofNullable(devDiffs.get(deploymentId)).map(map -> map.get(buildNumber));
}
@Override
public void pruneDevDiffs(DeploymentId deploymentId, long beforeBuildNumber) {
Optional.ofNullable(devDiffs.get(deploymentId))
.ifPresent(map -> map.keySet().removeIf(buildNumber -> buildNumber < beforeBuildNumber));
}
@Override
public void putDev(DeploymentId deploymentId, RevisionId revision, byte[] applicationPackage, byte[] diff) {
devStore.put(deploymentId, applicationPackage);
devDiffs.computeIfAbsent(deploymentId, __ -> new ConcurrentHashMap<>()).put(revision.number(), diff);
}
@Override
public void putMeta(TenantName tenant, ApplicationName application, Instant now, byte[] metaZip) {
meta.putIfAbsent(appId(tenant, application), new ConcurrentSkipListMap<>());
meta.get(appId(tenant, application)).put(now, metaZip);
}
@Override
public void putMetaTombstone(TenantName tenant, ApplicationName application, Instant now) {
putMeta(tenant, application, now, tombstone);
}
@Override
public void putMeta(DeploymentId id, Instant now, byte[] metaZip) {
metaManual.computeIfAbsent(id, __ -> new ConcurrentSkipListMap<>()).put(now, metaZip);
}
@Override
public void putMetaTombstone(DeploymentId id, Instant now) {
putMeta(id, now, tombstone);
}
@Override
public void pruneMeta(Instant oldest) {
for (ApplicationId id : meta.keySet()) {
Instant activeAtOldest = meta.get(id).lowerKey(oldest);
if (activeAtOldest != null)
meta.get(id).headMap(activeAtOldest).clear();
if (meta.get(id).lastKey().isBefore(oldest) && meta.get(id).lastEntry().getValue() == tombstone)
meta.remove(id);
}
}
public NavigableMap<Instant, byte[]> getMeta(ApplicationId id) { return meta.get(id); }
public NavigableMap<Instant, byte[]> getMeta(DeploymentId id) { return metaManual.get(id); }
}
|