Skip to content

Commit 981084f

Browse files
authored
Merge pull request #15975 from cdapio/simplified-upgrades-list-API
Added Upgrade manager interface and implementation.
2 parents 5c077de + 0276381 commit 981084f

File tree

6 files changed

+485
-20
lines changed

6 files changed

+485
-20
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright © 2025 Cask Data, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
17+
package io.cdap.cdap.app.upgrade;
18+
19+
import io.cdap.cdap.proto.id.NamespaceId;
20+
import io.cdap.cdap.proto.upgrade.ApplicationUpgradeDetail;
21+
import java.util.List;
22+
23+
/**
24+
* Manager for all upgrade related operations.
25+
*/
26+
public interface UpgradeManager {
27+
28+
/**
29+
* Lists upgrade details for applications in a namespace.
30+
*
31+
* @param namespace the namespace in which applications are searched.
32+
* @return A list of {@link ApplicationUpgradeDetail} containing application and current/latest
33+
* version details for the application artifact and the plugins.
34+
*/
35+
List<ApplicationUpgradeDetail> listUpgrades(NamespaceId namespace) throws Exception;
36+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright © 2025 Cask Data, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
17+
package io.cdap.cdap.internal.app.upgrade;
18+
19+
import io.cdap.cdap.api.artifact.ArtifactId;
20+
import io.cdap.cdap.api.artifact.ArtifactSummary;
21+
import io.cdap.cdap.api.artifact.ArtifactVersion;
22+
import io.cdap.cdap.app.store.ScanApplicationsRequest;
23+
import io.cdap.cdap.app.store.Store;
24+
import io.cdap.cdap.app.upgrade.UpgradeManager;
25+
import io.cdap.cdap.common.ApplicationNotFoundException;
26+
import io.cdap.cdap.internal.app.runtime.artifact.ArtifactRepository;
27+
import io.cdap.cdap.proto.id.ApplicationId;
28+
import io.cdap.cdap.proto.id.NamespaceId;
29+
import io.cdap.cdap.proto.id.PluginId;
30+
import io.cdap.cdap.proto.upgrade.ApplicationUpgradeDetail;
31+
import io.cdap.cdap.proto.upgrade.ArtifactUpgradeDetail;
32+
import io.cdap.cdap.proto.upgrade.PluginUpgradeDetail;
33+
import java.util.ArrayList;
34+
import java.util.Comparator;
35+
import java.util.HashMap;
36+
import java.util.List;
37+
import java.util.Map;
38+
import java.util.Map.Entry;
39+
import java.util.Optional;
40+
import java.util.stream.Collectors;
41+
import javax.inject.Inject;
42+
43+
/**
44+
* Default implementation of {@link UpgradeManager}.
45+
*/
46+
public class DefaultUpgradeManager implements UpgradeManager {
47+
48+
private final ApplicationPluginMappingFetcher mappingFetcher;
49+
private final ArtifactRepository artifactRepository;
50+
private final Store store;
51+
52+
@Inject
53+
public DefaultUpgradeManager(ApplicationPluginMappingFetcher mappingFetcher,
54+
ArtifactRepository artifactRepository, Store store) {
55+
this.mappingFetcher = mappingFetcher;
56+
this.artifactRepository = artifactRepository;
57+
this.store = store;
58+
}
59+
60+
@Override
61+
public List<ApplicationUpgradeDetail> listUpgrades(NamespaceId namespace) throws Exception {
62+
List<ApplicationPluginMapping> applicationPluginMappings =
63+
mappingFetcher.fetchApplicationPluginMapping(namespace);
64+
Map<String, List<PluginId>> applicationToPluginListMap = applicationPluginMappings.stream()
65+
.collect(Collectors.groupingBy(x -> x.getApplicationId().getApplication(),
66+
Collectors.mapping(ApplicationPluginMapping::getPluginId, Collectors.toList())));
67+
Map<String, ArtifactId> applicationIdArtifactIdMap = fetchApplicationArtifactMap(
68+
namespace);
69+
Map<String, Optional<ArtifactId>> artifactToLatestVersionMap = artifactToLatestVersionMap(
70+
namespace);
71+
return toApplicationUpgradeDetails(namespace, applicationToPluginListMap,
72+
artifactToLatestVersionMap, applicationIdArtifactIdMap);
73+
}
74+
75+
private Map<String, ArtifactId> fetchApplicationArtifactMap(NamespaceId namespace) {
76+
ScanApplicationsRequest scanAppRequest = ScanApplicationsRequest.builder()
77+
.setNamespaceId(namespace).setLatestOnly(true).build();
78+
Map<String, ArtifactId> results = new HashMap<>();
79+
store.scanApplications(scanAppRequest, Integer.MAX_VALUE, (app, meta) -> {
80+
results.put(app.getApplication(), meta.getSpec().getArtifactId());
81+
});
82+
return results;
83+
}
84+
85+
private Map<String, Optional<ArtifactId>> artifactToLatestVersionMap(NamespaceId namespace)
86+
throws Exception {
87+
List<ArtifactSummary> artifactSummaries = artifactRepository.getArtifactSummaries(
88+
namespace, true);
89+
// Get max version for each artifactID.
90+
return artifactSummaries.stream().
91+
map(x -> new ArtifactId(x.getName(), new ArtifactVersion(x.getVersion()), x.getScope())).
92+
collect(Collectors.groupingBy(ArtifactId::getName,
93+
Collectors.maxBy(Comparator.comparing(ArtifactId::getVersion))));
94+
}
95+
96+
private List<ApplicationUpgradeDetail> toApplicationUpgradeDetails(NamespaceId namespaceId,
97+
Map<String, List<PluginId>> appToPluginsMap,
98+
Map<String, Optional<ArtifactId>> artifactToLatestVersionMap,
99+
Map<String, ArtifactId> applicationIdArtifactIdMap) throws ApplicationNotFoundException {
100+
List<ApplicationUpgradeDetail> results = new ArrayList<>();
101+
for (Entry<String, List<PluginId>> entry : appToPluginsMap.entrySet()) {
102+
String appName = entry.getKey();
103+
List<PluginUpgradeDetail> pluginUpgradeDetails = toPluginUpgradeDetails(entry.getValue(),
104+
artifactToLatestVersionMap);
105+
ArtifactId currentApplicationArtifact = applicationIdArtifactIdMap.get(appName);
106+
if (currentApplicationArtifact == null) {
107+
throw new ApplicationNotFoundException(
108+
new ApplicationId(namespaceId.getNamespace(), appName));
109+
}
110+
results.add(
111+
new ApplicationUpgradeDetail(appName,
112+
toArtifactUpgradeDetails(currentApplicationArtifact, artifactToLatestVersionMap),
113+
pluginUpgradeDetails));
114+
}
115+
return results;
116+
}
117+
118+
private ArtifactUpgradeDetail toArtifactUpgradeDetails(ArtifactId current,
119+
Map<String, Optional<ArtifactId>> artifactToLatestVersionMap) {
120+
ArtifactId latest = artifactToLatestVersionMap.getOrDefault(current.getName(), Optional.empty())
121+
.orElse(current);
122+
return new ArtifactUpgradeDetail(current.getName(), current.getVersion().getVersion(),
123+
latest.getVersion().getVersion());
124+
}
125+
126+
private List<PluginUpgradeDetail> toPluginUpgradeDetails(List<PluginId> pluginIdList,
127+
Map<String, Optional<ArtifactId>> artifactToLatestVersionMap) {
128+
return pluginIdList.stream().map(
129+
x -> {
130+
ArtifactUpgradeDetail artifactUpgradeDetail = toArtifactUpgradeDetails(
131+
x.getParent().toApiArtifactId(),
132+
artifactToLatestVersionMap);
133+
return new PluginUpgradeDetail(artifactUpgradeDetail, x.getPlugin(), x.getType());
134+
}).collect(
135+
Collectors.toList());
136+
}
137+
138+
}

0 commit comments

Comments
 (0)