Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
import com.devshawn.kafka.gitops.MainCommand;
import com.devshawn.kafka.gitops.StateManager;
import com.devshawn.kafka.gitops.config.ManagerConfig;
import com.devshawn.kafka.gitops.exception.*;
import com.devshawn.kafka.gitops.exception.ConfluentCloudException;
import com.devshawn.kafka.gitops.exception.KafkaExecutionException;
import com.devshawn.kafka.gitops.exception.MissingConfigurationException;
import com.devshawn.kafka.gitops.exception.ValidationException;
import com.devshawn.kafka.gitops.exception.WritePlanOutputException;
import com.devshawn.kafka.gitops.service.ParserService;
import com.devshawn.kafka.gitops.util.LogUtil;
import picocli.CommandLine;
Expand Down Expand Up @@ -39,6 +43,7 @@ private ManagerConfig generateStateManagerConfig() {
return new ManagerConfig.Builder()
.setVerboseRequested(parent.isVerboseRequested())
.setDeleteDisabled(parent.isDeleteDisabled())
.setIncludeUnchangedEnabled(false)
.setStateFile(parent.getFile())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ private ManagerConfig generateStateManagerConfig() {
return new ManagerConfig.Builder()
.setVerboseRequested(parent.isVerboseRequested())
.setDeleteDisabled(parent.isDeleteDisabled())
.setIncludeUnchangedEnabled(false)
.setStateFile(parent.getFile())
.setNullablePlanFile(planFile)
.build();
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/devshawn/kafka/gitops/cli/PlanCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ public class PlanCommand implements Callable<Integer> {
description = "Specify the output file for the plan.")
private File outputFile;

@CommandLine.Option(names = {"--include-unchanged"}, description = "Include unchanged resources in the plan file.")
private boolean includeUnchanged = false;

@CommandLine.ParentCommand
private MainCommand parent;

Expand Down Expand Up @@ -54,6 +57,7 @@ private ManagerConfig generateStateManagerConfig() {
return new ManagerConfig.Builder()
.setVerboseRequested(parent.isVerboseRequested())
.setDeleteDisabled(parent.isDeleteDisabled())
.setIncludeUnchangedEnabled(includeUnchanged)
.setStateFile(parent.getFile())
.setNullablePlanFile(outputFile)
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ private ManagerConfig generateStateManagerConfig() {
return new ManagerConfig.Builder()
.setVerboseRequested(parent.isVerboseRequested())
.setDeleteDisabled(parent.isDeleteDisabled())
.setIncludeUnchangedEnabled(false)
.setStateFile(parent.getFile())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public interface ManagerConfig {

boolean isDeleteDisabled();

boolean isIncludeUnchangedEnabled();

File getStateFile();

Optional<File> getPlanFile();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.devshawn.kafka.gitops.domain.plan;

import com.devshawn.kafka.gitops.enums.PlanAction;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.inferred.freebuilder.FreeBuilder;

Expand All @@ -13,6 +14,13 @@ public interface DesiredPlan {

List<AclPlan> getAclPlans();

default DesiredPlan toChangesOnlyPlan() {
DesiredPlan.Builder builder = new DesiredPlan.Builder();
getTopicPlans().stream().filter(it -> !it.getAction().equals(PlanAction.NO_CHANGE)).map(TopicPlan::toChangesOnlyPlan).forEach(builder::addTopicPlans);
getAclPlans().stream().filter(it -> !it.getAction().equals(PlanAction.NO_CHANGE)).forEach(builder::addAclPlans);
return builder.build();
}

class Builder extends DesiredPlan_Builder {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ public interface TopicPlan {

List<TopicConfigPlan> getTopicConfigPlans();

default TopicPlan toChangesOnlyPlan() {
TopicPlan.Builder builder = new TopicPlan.Builder().setName(getName()).setAction(getAction()).setTopicDetails(getTopicDetails());
getTopicConfigPlans().stream().filter(it -> !it.getAction().equals(PlanAction.NO_CHANGE)).forEach(builder::addTopicConfigPlans);
return builder.build();
}

class Builder extends TopicPlan_Builder {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,9 @@ public void writePlanToFile(DesiredPlan desiredPlan) {
if (managerConfig.getPlanFile().isPresent()) {
try {
managerConfig.getPlanFile().get().createNewFile();
DesiredPlan outputPlan = managerConfig.isIncludeUnchangedEnabled() ? desiredPlan : desiredPlan.toChangesOnlyPlan();
FileWriter writer = new FileWriter(managerConfig.getPlanFile().get());
writer.write(objectMapper.writeValueAsString(desiredPlan));
writer.write(objectMapper.writeValueAsString(outputPlan));
writer.close();
} catch (IOException ex) {
throw new WritePlanOutputException(ex.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,14 @@ class ApplyCommandIntegrationSpec extends Specification {
System.setOut(oldOut)

where:
planFile | deleteDisabled
"seed-topic-modification" | true
"seed-topic-modification" | false
"seed-topic-modification-3" | true
"seed-topic-modification-3" | false
"seed-acl-exists" | false
"no-changes" | false
planFile | deleteDisabled
"seed-topic-modification" | true
"seed-topic-modification" | false
"seed-topic-modification-3" | true
"seed-topic-modification-3" | false
"seed-acl-exists" | false
"no-changes" | false
"no-changes-include-unchanged" | false
}

void 'test reading missing file throws ReadPlanInputException'() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,40 @@ class PlanCommandIntegrationSpec extends Specification {
"seed-blacklist-topics" | false
}

void 'test include unchanged flag - #planNam #includeUnchanged'() {
setup:
TestUtils.cleanUpCluster()
TestUtils.seedCluster()
String planOutputFile = "/tmp/plan.json"
String file = TestUtils.getResourceFilePath("plans/${planName}.yaml")
MainCommand mainCommand = new MainCommand()
CommandLine cmd = new CommandLine(mainCommand)

when:
int exitCode = -1
if (includeUnchanged) {
exitCode = cmd.execute("-f", file, "plan", "--include-unchanged", "-o", planOutputFile)
} else {
exitCode = cmd.execute("-f", file, "plan", "-o", planOutputFile)
}

then:
exitCode == 0

when:
String expected = includeUnchanged ? "${planName}-include-unchanged" : planName
String actualPlan = TestUtils.getFileContent(planOutputFile)
String expectedPlan = TestUtils.getResourceFileContent("plans/${expected}-plan.json")

then:
JSONAssert.assertEquals(expectedPlan, actualPlan, true)

where:
planName | includeUnchanged
"seed-basic" | false
"seed-basic" | true
}

void 'test invalid plans - #planName'() {
setup:
ByteArrayOutputStream err = new ByteArrayOutputStream()
Expand Down Expand Up @@ -187,7 +221,7 @@ class PlanCommandIntegrationSpec extends Specification {
System.setOut(oldOut)
}

void 'test plan that has no changes'() {
void 'test plan that has no changes - #includeUnchanged'() {
setup:
TestUtils.cleanUpCluster()
TestUtils.seedCluster()
Expand All @@ -200,15 +234,21 @@ class PlanCommandIntegrationSpec extends Specification {
CommandLine cmd = new CommandLine(mainCommand)

when:
int exitCode = cmd.execute("-f", file, "plan", "-o", planOutputFile)
int exitCode = -1
if (includeUnchanged) {
exitCode = cmd.execute("-f", file, "plan", "--include-unchanged", "-o", planOutputFile)
} else {
exitCode = cmd.execute("-f", file, "plan", "-o", planOutputFile)
}

then:
exitCode == 0
out.toString() == TestUtils.getResourceFileContent("plans/no-changes-output.txt")

when:
String expected = includeUnchanged ? "${planName}-include-unchanged" : planName
String actualPlan = TestUtils.getFileContent(planOutputFile)
String expectedPlan = TestUtils.getResourceFileContent("plans/${planName}-plan.json")
String expectedPlan = TestUtils.getResourceFileContent("plans/${expected}-plan.json")

then:
JSONAssert.assertEquals(expectedPlan, actualPlan, true)
Expand All @@ -217,6 +257,8 @@ class PlanCommandIntegrationSpec extends Specification {
System.setOut(oldOut)

where:
planName << ["no-changes"]
planName | includeUnchanged
"no-changes" | false
"no-changes" | true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Executing apply...

[SUCCESS] There are no necessary changes; the actual state matches the desired state.
81 changes: 81 additions & 0 deletions src/test/resources/plans/no-changes-include-unchanged-plan.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"topicPlans": [
{
"name": "delete-topic",
"action": "NO_CHANGE",
"topicDetails": {
"partitions": 1,
"replication": 1,
"configs": {}
},
"topicConfigPlans": []
},
{
"name": "test-topic",
"action": "NO_CHANGE",
"topicDetails": {
"partitions": 1,
"replication": 1,
"configs": {}
},
"topicConfigPlans": []
},
{
"name": "topic-with-configs-1",
"action": "NO_CHANGE",
"topicDetails": {
"partitions": 3,
"replication": 1,
"configs": {
"cleanup.policy": "compact",
"segment.bytes": "100000"
}
},
"topicConfigPlans": [
{
"key": "cleanup.policy",
"value": "compact",
"action": "NO_CHANGE"
},
{
"key": "segment.bytes",
"value": "100000",
"action": "NO_CHANGE"
}
]
},
{
"name": "topic-with-configs-2",
"action": "NO_CHANGE",
"topicDetails": {
"partitions": 6,
"replication": 1,
"configs": {
"retention.ms": "60000"
}
},
"topicConfigPlans": [
{
"key": "retention.ms",
"value": "60000",
"action": "NO_CHANGE"
}
]
}
],
"aclPlans": [
{
"name": "test-service-0",
"aclDetails": {
"name": "test-topic",
"type": "TOPIC",
"pattern": "LITERAL",
"principal": "User:test",
"host": "*",
"operation": "READ",
"permission": "ALLOW"
},
"action": "NO_CHANGE"
}
]
}
81 changes: 2 additions & 79 deletions src/test/resources/plans/no-changes-plan.json
Original file line number Diff line number Diff line change
@@ -1,81 +1,4 @@
{
"topicPlans": [
{
"name": "delete-topic",
"action": "NO_CHANGE",
"topicDetails": {
"partitions": 1,
"replication": 1,
"configs": {}
},
"topicConfigPlans": []
},
{
"name": "test-topic",
"action": "NO_CHANGE",
"topicDetails": {
"partitions": 1,
"replication": 1,
"configs": {}
},
"topicConfigPlans": []
},
{
"name": "topic-with-configs-1",
"action": "NO_CHANGE",
"topicDetails": {
"partitions": 3,
"replication": 1,
"configs": {
"cleanup.policy": "compact",
"segment.bytes": "100000"
}
},
"topicConfigPlans": [
{
"key": "cleanup.policy",
"value": "compact",
"action": "NO_CHANGE"
},
{
"key": "segment.bytes",
"value": "100000",
"action": "NO_CHANGE"
}
]
},
{
"name": "topic-with-configs-2",
"action": "NO_CHANGE",
"topicDetails": {
"partitions": 6,
"replication": 1,
"configs": {
"retention.ms": "60000"
}
},
"topicConfigPlans": [
{
"key": "retention.ms",
"value": "60000",
"action": "NO_CHANGE"
}
]
}
],
"aclPlans": [
{
"name": "test-service-0",
"aclDetails": {
"name": "test-topic",
"type": "TOPIC",
"pattern": "LITERAL",
"principal": "User:test",
"host": "*",
"operation": "READ",
"permission": "ALLOW"
},
"action": "NO_CHANGE"
}
]
"topicPlans": [],
"aclPlans": []
}
Loading