The Enterprise Spring Boot Governance Starter governance-starter
library supports adding and validating custom specifications. This feature allows you to incorporate organization-specific regulations, industry guidelines, or proprietary standards into your validation processes.
To run validation against custom specifications, follow these steps:
- Add custom governance specs by implementing the
GovernanceSpecProvider
bean. - Create a class in which to store your collected information.
- Implement a
GovernanceDetailsScanner
bean to gather details and populate the class created in the previous step. - Implement a
GovernanceValidator
bean to validate against the collected data.
A spec id can only be validated by a single validator. A validator can only validate a single spec id.
That is, a spec id has a 1:1 mapping to a GovernanceValidator
bean.
Define a GovernanceSpecProvider bean to add custom specs
To add custom governance specs, see the following example:
package com.example.compliance;
import com.vmware.tanzu.spring.governance.spec.GovernanceSpec;
import com.vmware.tanzu.spring.governance.spec.GovernanceSpecProvider;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class ExampleOrgGovernanceSpecProvider implements GovernanceSpecProvider {
@Override
public List<GovernanceSpec> getSpecs() {
return List.of(getSpec());
}
private static GovernanceSpec getSpec() {
var spec = new GovernanceSpec();
spec.setId("MY-ORG-0001");
spec.setTitle("TLS must be enabled");
spec.setDescription("As per org rules, all apps must have TLS enabled");
spec.setOrg("com.example");
spec.setTags(List.of("MY-ORG"));
return spec;
}
}
Create a custom class to store application details
To create a class in which to store your collected information, see the following example of a custom class:
public record ExampleOrgComplianceDetails(
boolean serverTlsEnabled,
boolean managementTlsEnabled) { }
Define a GovernanceDetailsScanner bean
Gather application details and populate the class created earlier. Implement the interface methods, where:
getKey()
is the key for the details object. In the validator bean, a map will be provided. Use this key to fetch the details object from the map.scan()
returns an instance of the collected details object.
package com.example.compliance;
import com.vmware.tanzu.spring.governance.GovernanceDetailsScanner;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.stereotype.Component;
@Component
public class ExampleOrgDetailsScanner implements GovernanceDetailsScanner {
static final String KEY = "exampleDetails";
private final ServerProperties serverProperties;
private final ManagementServerProperties managementServerProperties;
ExampleOrgDetailsScanner(ServerProperties serverProperties,
ManagementServerProperties managementServerProperties) {
this.serverProperties = serverProperties;
this.managementServerProperties = managementServerProperties;
}
@Override
public String getKey() {
return KEY;
}
@Override
public Object scan() {
var serverTlsEnabled = serverProperties.getSsl() != null
&& serverProperties.getSsl().isEnabled();
var actuatorIsUsingSameTlsConfig = serverTlsEnabled
&& managementServerProperties.getSsl() == null;
var actuatorHasSeparateTlsConfig = managementServerProperties.getSsl() != null
&& managementServerProperties.getSsl().isEnabled();
var managementTlsEnabled = actuatorIsUsingSameTlsConfig || actuatorHasSeparateTlsConfig;
return new ExampleOrgComplianceDetails(serverTlsEnabled, managementTlsEnabled);
}
}
Create a GovernanceValidator bean to run your validation rules
Implement the interface methods, where:
requiresKey()
is the key defined in the scanner where this validator can find the details.appliesToSpec()
where the validator returns theGovernanceSpec
it validates.validate()
where the validator returns aList<ValidationTestRun>
after validating the details.
package com.example.compliance;
import com.vmware.tanzu.spring.governance.GovernanceValidator;
import com.vmware.tanzu.governance.ValidationState;
import com.vmware.tanzu.spring.governance.ValidationTestRun;
import com.vmware.tanzu.spring.governance.spec.GovernanceSpec;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
@Component
public class ExampleOrgTlsEnabledValidator implements GovernanceValidator {
private static final String ID = "MY-ORG-0001";
@Override
public String requiresKey() {
return ExampleOrgDetailsScanner.KEY;
}
@Override
public GovernanceSpec appliesToSpec(List<GovernanceSpec> list) {
return list.stream()
.filter(spec -> ID.equals(spec.getId()))
.findFirst()
.orElse(null);
}
@Override
public List<ValidationTestRun> validate(Map<String, Object> appDetails) {
ExampleOrgComplianceDetails exampleDetails = (ExampleOrgComplianceDetails) getDetails(appDetails);
var serverTlsEnabledTest = new ValidationTestRun();
var passedTest1 = exampleDetails.serverTlsEnabled() ? ValidationState.PASS : ValidationState.FAIL;
serverTlsEnabledTest.setState(passedTest1);
serverTlsEnabledTest.setDescriptionFormat("Observed server tls enabled: %tlsEnabled");
serverTlsEnabledTest.setParameters(Map.of("tlsEnabled", passedTest1));
var managementTlsEnabledTest = new ValidationTestRun();
var passedTest2 = exampleDetails.managementTlsEnabled() ? ValidationState.PASS : ValidationState.FAIL;
managementTlsEnabledTest.setState(passedTest2);
managementTlsEnabledTest.setDescriptionFormat("Observed management tls enabled: %tlsEnabled");
managementTlsEnabledTest.setParameters(Map.of("tlsEnabled", passedTest2));
return List.of(serverTlsEnabledTest, managementTlsEnabledTest);
}
}
Also note:
-
The interface method
getDetails()
returns the details from theappDetails
Map using the key. -
The default interface method
appliesToDetails(Map<String, Object> appDetails)
(not shown) can be overridden to configure whether the validator should be run against the details. When it returnsfalse
,validate()
is not executed. The total test cases do not include this test. -
The string in
ValidationTestRun.setDescriptionFormat()
can include parameters; for example,"Observed management tls enabled: %tlsEnabled"
. These parameters must be present as keys in the Map forValidationTestRun.setParameters()
. In the output JSON, the value will be used; for example,“Description”: “Observed server tls enabled: true”
Validation State
The test result is represented by the ValidationState
:
PASS
- the test succeededFAIL
- the test failedUNKNOWN
- an issue occurred while collecting data or running the test.
For example, the library uses reflection while collecting the configuration of the application. In the case where reflection fails, the result is treated asUNKNOWN
. If you encounter this result when running the pre-defined tests, contact Broadcom Support with your use case.
If there are failing or unknown tests, the library forces the application to shut down. However, you can bypass the shutdown by deactivating the specific test, or by deactivating the exit-on-failure
flag.
See Library Configuration Options for further instructions.
Run the application
Run the app and access the Governance Endpoint.
Filter by the tag for your spec: https://localhost:8443/actuator/governance?tag=MY-ORG
.
Observe the details field in the JSON that includes a field, exampleDetails
, from the ExampleOrgDetailsScanner
. You will see the test run result shows failure.
Set the management port on a separate port,; then the test will pass.
management:
server:
port: 9443
Content feedback and comments