Skip to content

Commit a4a6daf

Browse files
committed
fix(core): add missing provider type for CoreExtensionProvider in reference.conf (#693)
The provider.core section in reference.conf was missing the type field. Since v0.37.0's ProviderApiConfigurator matches providers by type (not name). CoreExtensionProvider was never registered, making ResourceListConverter unavailable and causing KafkaTopicList resources to fail with "Cannot find controller for resource type". Add unit and e2e tests to cover KafkaTopicList expansion. Fixes: #693
1 parent c66115b commit a4a6daf

4 files changed

Lines changed: 193 additions & 0 deletions

File tree

cli/src/main/resources/reference.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ jikkou {
1818
# Core
1919
provider.core {
2020
enabled = true
21+
type = io.streamthoughts.jikkou.core.CoreExtensionProvider
2122
}
2223
# Apache Kafka Provider
2324
provider.kafka {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
apiVersion: "kafka.jikkou.io/v1"
3+
kind: "KafkaTopicList"
4+
items:
5+
- metadata:
6+
name: 'e2e-topic-list-1'
7+
labels:
8+
environment: e2e
9+
spec:
10+
partitions: 1
11+
replicas: 1
12+
- metadata:
13+
name: 'e2e-topic-list-2'
14+
labels:
15+
environment: e2e
16+
spec:
17+
partitions: 2
18+
replicas: 1

e2e/run-tests.sh

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,59 @@ EOF
313313
assert_output_not_contains "e2e-topic-to-delete" || return 1
314314
}
315315

316+
# ── Kafka TopicList (apply + delete via KafkaTopicList kind) ──────────────────
317+
318+
test_kafka_topic_list_apply() {
319+
run_jikkou_capture apply --files "${E2E_RESOURCES}/kafka-topic-list.yaml"
320+
assert_exit_code 0 || return 1
321+
322+
# Verify both topics from the list were created
323+
run_jikkou_capture get kafkatopics --name 'e2e-topic-list-1'
324+
assert_exit_code 0 || return 1
325+
assert_output_contains "e2e-topic-list-1" || return 1
326+
327+
run_jikkou_capture get kafkatopics --name 'e2e-topic-list-2'
328+
assert_exit_code 0 || return 1
329+
assert_output_contains "e2e-topic-list-2" || return 1
330+
}
331+
332+
test_kafka_topic_list_delete() {
333+
local tmpfile
334+
tmpfile=$(mktemp /tmp/e2e-topic-list-delete-XXXXXX.yaml)
335+
trap "rm -f ${tmpfile}" RETURN
336+
337+
cat > "${tmpfile}" <<'EOF'
338+
---
339+
apiVersion: "kafka.jikkou.io/v1"
340+
kind: "KafkaTopicList"
341+
items:
342+
- metadata:
343+
name: 'e2e-topic-list-1'
344+
annotations:
345+
jikkou.io/delete: true
346+
spec:
347+
partitions: 1
348+
replicas: 1
349+
- metadata:
350+
name: 'e2e-topic-list-2'
351+
annotations:
352+
jikkou.io/delete: true
353+
spec:
354+
partitions: 2
355+
replicas: 1
356+
EOF
357+
358+
run_jikkou_capture apply --files "${tmpfile}"
359+
assert_exit_code 0 || return 1
360+
361+
# Verify both topics are gone
362+
run_jikkou_capture get kafkatopics --name 'e2e-topic-list-1'
363+
assert_output_not_contains "e2e-topic-list-1" || return 1
364+
365+
run_jikkou_capture get kafkatopics --name 'e2e-topic-list-2'
366+
assert_output_not_contains "e2e-topic-list-2" || return 1
367+
}
368+
316369
# ── Kafka ACLs (create, read, delete — no update for ACLs) ──────────────────
317370

318371
test_kafka_acls_create() {
@@ -566,6 +619,10 @@ main() {
566619
run_test "Kafka Topics: diff" test_kafka_topics_diff
567620
run_test "Kafka Topics: delete" test_kafka_topics_delete
568621

622+
# ── Kafka TopicList ──
623+
run_test "Kafka TopicList: apply" test_kafka_topic_list_apply
624+
run_test "Kafka TopicList: delete" test_kafka_topic_list_delete
625+
569626
# ── Kafka ACLs CRD (no update — ACLs are atomic) ──
570627
run_test "Kafka ACLs: create" test_kafka_acls_create
571628
run_test "Kafka ACLs: read" test_kafka_acls_read
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright (c) The original authors
4+
*
5+
* Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
6+
*/
7+
package io.streamthoughts.jikkou.kafka;
8+
9+
import io.streamthoughts.jikkou.core.CoreExtensionProvider;
10+
import io.streamthoughts.jikkou.core.JikkouApi;
11+
import io.streamthoughts.jikkou.core.ReconciliationContext;
12+
import io.streamthoughts.jikkou.core.models.HasItems;
13+
import io.streamthoughts.jikkou.core.models.HasMetadata;
14+
import io.streamthoughts.jikkou.core.models.ObjectMeta;
15+
import io.streamthoughts.jikkou.core.models.ResourceList;
16+
import io.streamthoughts.jikkou.kafka.collections.V1KafkaTopicList;
17+
import io.streamthoughts.jikkou.kafka.models.V1KafkaTopic;
18+
import io.streamthoughts.jikkou.kafka.models.V1KafkaTopicSpec;
19+
import io.streamthoughts.jikkou.runtime.JikkouContext;
20+
import java.util.List;
21+
import org.junit.jupiter.api.Assertions;
22+
import org.junit.jupiter.api.Test;
23+
24+
class KafkaTopicListExpansionTest {
25+
26+
@Test
27+
void shouldExpandKafkaTopicListIntoIndividualTopics() {
28+
// Given
29+
JikkouApi api = JikkouContext.defaultContext()
30+
.newApiBuilder()
31+
.register(new CoreExtensionProvider())
32+
.register(new KafkaExtensionProvider())
33+
.build()
34+
.enableBuiltInAnnotations(false);
35+
36+
V1KafkaTopicList topicList = new V1KafkaTopicList.Builder()
37+
.withItems(List.of(
38+
V1KafkaTopic.builder()
39+
.withMetadata(ObjectMeta.builder()
40+
.withName("topic-1")
41+
.build())
42+
.withSpec(V1KafkaTopicSpec.builder()
43+
.withPartitions(1)
44+
.withReplicas((short) 1)
45+
.build())
46+
.build(),
47+
V1KafkaTopic.builder()
48+
.withMetadata(ObjectMeta.builder()
49+
.withName("topic-2")
50+
.build())
51+
.withSpec(V1KafkaTopicSpec.builder()
52+
.withPartitions(2)
53+
.withReplicas((short) 1)
54+
.build())
55+
.build()))
56+
.build();
57+
58+
ReconciliationContext context = ReconciliationContext.builder()
59+
.dryRun(true)
60+
.build();
61+
62+
// When
63+
HasItems result = api.prepare(ResourceList.of(topicList), context);
64+
65+
// Then
66+
List<? extends HasMetadata> items = result.getItems();
67+
Assertions.assertEquals(2, items.size());
68+
Assertions.assertTrue(items.stream().allMatch(item -> "KafkaTopic".equals(item.getKind())));
69+
Assertions.assertTrue(items.stream().noneMatch(item -> "KafkaTopicList".equals(item.getKind())));
70+
Assertions.assertTrue(result.findByName("topic-1").isPresent());
71+
Assertions.assertTrue(result.findByName("topic-2").isPresent());
72+
}
73+
74+
@Test
75+
void shouldNotExpandKafkaTopicListWhenCoreProviderNotRegistered() {
76+
// Given
77+
JikkouApi api = JikkouContext.defaultContext()
78+
.newApiBuilder()
79+
.register(new KafkaExtensionProvider())
80+
.build()
81+
.enableBuiltInAnnotations(false);
82+
83+
V1KafkaTopicList topicList = new V1KafkaTopicList.Builder()
84+
.withItems(List.of(
85+
V1KafkaTopic.builder()
86+
.withMetadata(ObjectMeta.builder()
87+
.withName("topic-1")
88+
.build())
89+
.withSpec(V1KafkaTopicSpec.builder()
90+
.withPartitions(1)
91+
.withReplicas((short) 1)
92+
.build())
93+
.build(),
94+
V1KafkaTopic.builder()
95+
.withMetadata(ObjectMeta.builder()
96+
.withName("topic-2")
97+
.build())
98+
.withSpec(V1KafkaTopicSpec.builder()
99+
.withPartitions(2)
100+
.withReplicas((short) 1)
101+
.build())
102+
.build()))
103+
.build();
104+
105+
ReconciliationContext context = ReconciliationContext.builder()
106+
.dryRun(true)
107+
.build();
108+
109+
// When
110+
HasItems result = api.prepare(ResourceList.of(topicList), context);
111+
112+
// Then - without CoreExtensionProvider, the list is not expanded
113+
List<? extends HasMetadata> items = result.getItems();
114+
Assertions.assertEquals(1, items.size());
115+
Assertions.assertEquals("KafkaTopicList", items.getFirst().getKind());
116+
}
117+
}

0 commit comments

Comments
 (0)