Skip to content

Commit 1ccf0bc

Browse files
authored
feat: properly output dependent native APIs (#619)
1 parent 4ba8141 commit 1ccf0bc

File tree

7 files changed

+80
-37
lines changed

7 files changed

+80
-37
lines changed

bundle-generator/deployment/src/main/java/io/quarkiverse/operatorsdk/bundle/deployment/BundleProcessor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,12 +189,12 @@ void generateBundle(ApplicationInfoBuildItem configuration,
189189
Path.of(BUNDLE).resolve(manifestBuilder.getName()).resolve(fileName).toString(),
190190
manifestBuilder.getManifestData(serviceAccounts, clusterRoleBindings, clusterRoles,
191191
roleBindings, roles, deployments)));
192-
log.infov("Generating {0} for {1} controller -> {2}",
192+
log.infov("Generating {0} for ''{1}'' controller -> {2}",
193193
manifestBuilder.getManifestType(),
194194
manifestBuilder.getName(),
195195
outputDir.resolve(manifestBuilder.getName()).resolve(fileName));
196196
} catch (IOException e) {
197-
log.errorv("Cannot generate {0} for {1}: {2}",
197+
log.errorv("Cannot generate {0} for ''{1}'' controller: {2}",
198198
manifestBuilder.getManifestType(), manifestBuilder.getName(), e.getMessage());
199199
}
200200
});

bundle-generator/deployment/src/main/java/io/quarkiverse/operatorsdk/bundle/deployment/builders/CsvManifestsBuilder.java

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
import java.nio.file.Path;
88
import java.util.*;
99
import java.util.function.Predicate;
10+
import java.util.stream.Collectors;
1011

1112
import org.jboss.logging.Logger;
1213

14+
import io.fabric8.kubernetes.api.model.GroupVersionKind;
1315
import io.fabric8.kubernetes.api.model.ServiceAccount;
1416
import io.fabric8.kubernetes.api.model.apps.Deployment;
1517
import io.fabric8.kubernetes.api.model.rbac.*;
@@ -31,12 +33,10 @@ public class CsvManifestsBuilder extends ManifestsBuilder {
3133
private static final String ROLE_KIND = "Role";
3234
private static final String NO_SERVICE_ACCOUNT = "";
3335
private static final Logger LOGGER = Logger.getLogger(CsvManifestsBuilder.class.getName());
34-
3536
private static final String IMAGE_PNG = "image/png";
36-
3737
private ClusterServiceVersionBuilder csvBuilder;
38-
private final SortedSet<String> ownedCRs = new TreeSet<>();
39-
private final SortedSet<String> requiredCRs = new TreeSet<>();
38+
private final Set<CRDDescription> ownedCRs = new HashSet<>();
39+
private final Set<CRDDescription> requiredCRs = new HashSet<>();
4040
private final Path kubernetesResources;
4141

4242
public CsvManifestsBuilder(CSVMetadataHolder metadata, List<ReconcilerAugmentedClassInfo> controllers,
@@ -137,62 +137,81 @@ public CsvManifestsBuilder(CSVMetadataHolder metadata, List<ReconcilerAugmentedC
137137
}
138138

139139
// add owned and required CRD, also collect them
140-
final var crdsBuilder = csvSpecBuilder.editOrNewCustomresourcedefinitions();
140+
final var nativeApis = new ArrayList<GroupVersionKind>();
141141
controllers.forEach(raci -> {
142142
// add owned CRD
143143
final var resourceInfo = raci.associatedResourceInfo();
144144
if (resourceInfo.isCR()) {
145145
final var asResource = resourceInfo.asResourceTargeting();
146146
final var fullResourceName = asResource.fullResourceName();
147-
ownedCRs.add(fullResourceName);
148-
crdsBuilder
149-
.addNewOwned()
147+
ownedCRs.add(new CRDDescriptionBuilder()
150148
.withName(fullResourceName)
151149
.withVersion(asResource.version())
152150
.withKind(asResource.kind())
153-
.endOwned();
151+
.build());
154152
}
155153

156154
// add required CRD for each dependent that targets a CR
157155
final var dependents = raci.getDependentResourceInfos();
158156
if (dependents != null && !dependents.isEmpty()) {
159157
dependents.stream()
160158
.map(ResourceAssociatedAugmentedClassInfo::associatedResourceInfo)
161-
.filter(ReconciledAugmentedClassInfo::isCR)
162159
.map(ReconciledAugmentedClassInfo::asResourceTargeting)
163160
.forEach(secondaryResource -> {
164-
final var fullResourceName = secondaryResource.fullResourceName();
165-
requiredCRs.add(fullResourceName);
166-
crdsBuilder.addNewRequired()
167-
.withName(fullResourceName)
168-
.withVersion(secondaryResource.version())
169-
.withKind(secondaryResource.kind())
170-
.endRequired();
161+
if (secondaryResource.isCR()) {
162+
final var fullResourceName = secondaryResource.fullResourceName();
163+
requiredCRs.add(new CRDDescriptionBuilder()
164+
.withName(fullResourceName)
165+
.withVersion(secondaryResource.version())
166+
.withKind(secondaryResource.kind())
167+
.build());
168+
} else {
169+
nativeApis.add(new GroupVersionKind(secondaryResource.group(), secondaryResource.kind(),
170+
secondaryResource.version()));
171+
}
171172
});
172173
}
174+
});
173175

174-
// add required CRDs from CSV metadata
175-
if (metadata.requiredCRDs != null) {
176-
for (RequiredCRD requiredCRD : metadata.requiredCRDs) {
177-
requiredCRs.add(requiredCRD.name);
178-
crdsBuilder.addNewRequired()
179-
.withKind(requiredCRD.kind)
180-
.withName(requiredCRD.name)
181-
.withVersion(requiredCRD.version)
182-
.endRequired();
183-
}
176+
// add required CRDs from CSV metadata
177+
if (metadata.requiredCRDs != null) {
178+
for (RequiredCRD requiredCRD : metadata.requiredCRDs) {
179+
requiredCRs.add(new CRDDescriptionBuilder()
180+
.withKind(requiredCRD.kind)
181+
.withName(requiredCRD.name)
182+
.withVersion(requiredCRD.version)
183+
.build());
184184
}
185+
}
185186

186-
});
187-
crdsBuilder.endCustomresourcedefinitions().endSpec();
187+
// add sorted native APIs
188+
csvSpecBuilder.addAllToNativeAPIs(nativeApis.stream()
189+
.sorted(Comparator.comparing(CsvManifestsBuilder::asString))
190+
.collect(Collectors.toList()));
191+
192+
csvSpecBuilder.editOrNewCustomresourcedefinitions()
193+
.addAllToOwned(ownedCRs)
194+
.addAllToRequired(requiredCRs)
195+
.endCustomresourcedefinitions()
196+
.endSpec();
197+
}
198+
199+
private static String asString(GroupVersionKind gvk) {
200+
return gvk.getGroup() + " " + gvk.getKind() + gvk.getVersion();
188201
}
189202

190203
public Set<String> getOwnedCRs() {
191-
return Collections.unmodifiableSet(ownedCRs);
204+
return ownedCRs.stream()
205+
.map(CRDDescription::getName)
206+
.sorted()
207+
.collect(Collectors.toCollection(LinkedHashSet::new));
192208
}
193209

194210
public Set<String> getRequiredCRs() {
195-
return Collections.unmodifiableSet(requiredCRs);
211+
return requiredCRs.stream()
212+
.map(CRDDescription::getName)
213+
.sorted()
214+
.collect(Collectors.toCollection(LinkedHashSet::new));
196215
}
197216

198217
@Override

bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/DefaultBundleWhenNoCsvMetadataTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.quarkiverse.operatorsdk.bundle;
22

3+
import static io.quarkiverse.operatorsdk.bundle.Utils.BUNDLE;
4+
35
import java.nio.file.Files;
46

57
import org.junit.jupiter.api.Assertions;
@@ -14,8 +16,6 @@
1416

1517
public class DefaultBundleWhenNoCsvMetadataTest {
1618

17-
private static final String BUNDLE = "bundle";
18-
1919
@RegisterExtension
2020
static final QuarkusProdModeTest config = new QuarkusProdModeTest()
2121
.setApplicationName("reconciler-with-no-csv-metadata")

bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/MultipleOperatorsBundleTest.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.junit.jupiter.api.extension.RegisterExtension;
1313

1414
import io.fabric8.kubernetes.api.model.HasMetadata;
15+
import io.fabric8.kubernetes.api.model.Pod;
1516
import io.fabric8.kubernetes.client.utils.Serialization;
1617
import io.fabric8.openshift.api.model.operatorhub.v1alpha1.ClusterServiceVersion;
1718
import io.quarkiverse.operatorsdk.bundle.sources.*;
@@ -27,7 +28,7 @@ public class MultipleOperatorsBundleTest {
2728
.addClasses(First.class, FirstReconciler.class,
2829
Second.class, SecondReconciler.class,
2930
Third.class, External.class, SecondExternal.class, ThirdReconciler.class,
30-
ExternalDependentResource.class))
31+
ExternalDependentResource.class, PodDependentResource.class))
3132
.overrideConfigKey("quarkus.operator-sdk.crd.generate-all", "true");
3233

3334
@SuppressWarnings("unused")
@@ -50,6 +51,11 @@ public void shouldWriteBundleForTheOperators() throws IOException {
5051
// CRDs should be alphabetically ordered
5152
assertEquals(HasMetadata.getFullResourceName(External.class), crds.getRequired().get(0).getName());
5253
assertEquals(HasMetadata.getFullResourceName(SecondExternal.class), crds.getRequired().get(1).getName());
54+
// should list native APIs as well
55+
final var podGVK = csv.getSpec().getNativeAPIs().get(0);
56+
assertEquals(HasMetadata.getGroup(Pod.class), podGVK.getGroup());
57+
assertEquals(HasMetadata.getKind(Pod.class), podGVK.getKind());
58+
assertEquals(HasMetadata.getVersion(Pod.class), podGVK.getVersion());
5359
assertEquals("1.0.0", csv.getSpec().getReplaces());
5460
assertEquals(">=1.0.0 <1.0.3", csv.getMetadata().getAnnotations().get("olm.skipRange"));
5561
assertEquals("Test", csv.getMetadata().getAnnotations().get("capabilities"));
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.quarkiverse.operatorsdk.bundle.sources;
2+
3+
import io.fabric8.kubernetes.api.model.Pod;
4+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource;
5+
6+
public class PodDependentResource extends KubernetesDependentResource<Pod, Third> {
7+
8+
public PodDependentResource() {
9+
super(Pod.class);
10+
}
11+
}

bundle-generator/deployment/src/test/java/io/quarkiverse/operatorsdk/bundle/sources/ThirdReconciler.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212

1313
@CSVMetadata(name = "third-operator", requiredCRDs = @RequiredCRD(kind = SecondExternal.KIND, name = "externalagains."
1414
+ SecondExternal.GROUP, version = SecondExternal.VERSION), replaces = "1.0.0", annotations = @Annotations(skipRange = ">=1.0.0 <1.0.3", capabilities = "Test", others = @Annotation(name = "foo", value = "bar")))
15-
@ControllerConfiguration(dependents = @Dependent(type = ExternalDependentResource.class))
15+
@ControllerConfiguration(dependents = {
16+
@Dependent(type = ExternalDependentResource.class),
17+
@Dependent(type = PodDependentResource.class)
18+
})
1619
public class ThirdReconciler implements Reconciler<Third> {
1720

1821
@Override

common-deployment/src/main/java/io/quarkiverse/operatorsdk/common/ReconciledResourceAugmentedClassInfo.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ public String version() {
2626
return HasMetadataUtils.getVersion(classInfo());
2727
}
2828

29+
public String group() {
30+
return HasMetadataUtils.getGroup(classInfo());
31+
}
32+
2933
@Override
3034
public boolean isResource() {
3135
return true;

0 commit comments

Comments
 (0)