* [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities
@ 2025-12-19 19:44 Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 01/23] sectionconfig: remove unused variable in get_property_schema() Max R. Carrara
` (22 more replies)
0 siblings, 23 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
Document PVE::SectionConfig Peculiarities - v1
==============================================
This was a long overdue series that adds a bunch of unit tests and
documentation updates for PVE::SectionConfig. The main motivation for
this series is me running somewhat regularly into new discoveries when
using PVE::SectionConfig for other purposes or investigating how certain
schemas are generated.
Since there have been quite a few surprises and somewhat sharp corners,
I decided to document all of these "peculiarities" in the form of unit /
regression tests instead of lengthy, wordy, borderline incomprehensible
paragraphs in the PVE::SectionConfig POD. (Seriously, some behaviors are
quite hard to parse when written out as text.)
The additional benefit of these tests is that they (should) guard us
against any regressions, should we have to make changes to the
PVE::SectionConfig machinery at some point in the future. On top of
that, we could use them as reference for aligning the Perl and Rust
SectionConfig / JSONSchema implementations in the future.
The commits should hopefully explain everything in detail.
Note though that these tests are by no means exhaustive. The test
scripts are however designed in such a way that it should be relatively
simple to add new cases whenever a new quirk pops up.
Finally, the POD docstrings in the PVE::SectionConfig source are also
adapted / corrected / clarified etc., which was made possible by the
insights gained from writing all of these tests.
References
==========
[0]: https://lore.proxmox.com/pve-devel/20251121165858.818307-1-m.carrara@proxmox.com/
Summary of Changes
==================
Max R. Carrara (23):
sectionconfig: remove unused variable in get_property_schema()
tests: sectionconfig: add comparison test structure
tests: sectionconfig: add test case for fixed props in updateSchema
tests: sectionconfig: add case for unused properties
tests: sectionconfig: add case for unused optional default properties
tests: sectionconfig: add case for opt. default prop being req. once
tests: sectionconfig: add case for opt. default prop requirde by all
tests: sectionconfig: add case for unused required default properties
tests: sectionconfig: add case for req. default prop being req. once
tests: sectionconfig: add case for required default prop req. by all
tests: sectionconfig: add case for required default props opt. for all
tests: sectionconfig: add isolated mode test structure
tests: sectionconfig: add case for an ident. prop on two plugins
tests: sectionconfig: add case for same prop. w/ different optionality
tests: sectionconfig: add case for differing opt. default prop uses
tests: sectionconfig: add case for differing req. default prop uses
sectionconfig: correct docs regarding global props in unified mode
sectionconfig: reword docs regarding property usage in isolated mode
sectionconfig: extend / correct docstring of `private()` method
sectionconfig: note that a prop must be defined through a JSONSchema
sectionconfig: note that props added by plugins are always optional
sectionconfig: note that `createSchema()` is universal for all plugins
sectionconfig: correct example in docstring of `updateSchema()`
src/PVE/SectionConfig.pm | 62 +-
test/Makefile | 5 +-
test/SectionConfig/Helpers.pm | 114 +
test/SectionConfig/Makefile | 11 +
test/SectionConfig/schema_comparison_test.pl | 2047 ++++++++++++++++++
test/SectionConfig/schema_isolated_test.pl | 826 +++++++
6 files changed, 3047 insertions(+), 18 deletions(-)
create mode 100644 test/SectionConfig/Helpers.pm
create mode 100644 test/SectionConfig/Makefile
create mode 100755 test/SectionConfig/schema_comparison_test.pl
create mode 100755 test/SectionConfig/schema_isolated_test.pl
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 01/23] sectionconfig: remove unused variable in get_property_schema()
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 02/23] tests: sectionconfig: add comparison test structure Max R. Carrara
` (21 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
src/PVE/SectionConfig.pm | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/PVE/SectionConfig.pm b/src/PVE/SectionConfig.pm
index 200424c..069ca9e 100644
--- a/src/PVE/SectionConfig.pm
+++ b/src/PVE/SectionConfig.pm
@@ -1062,7 +1062,6 @@ sub get_property_schema {
my ($class, $type, $key) = @_;
my $pdata = $class->private();
- my $opts = $pdata->{options}->{$type};
my $schema;
if ($class->has_isolated_properties()) {
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 02/23] tests: sectionconfig: add comparison test structure
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 01/23] sectionconfig: remove unused variable in get_property_schema() Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 03/23] tests: sectionconfig: add test case for fixed props in updateSchema Max R. Carrara
` (20 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
During development, I spotted a couple interesting "peculiarities"
when it comes to the schemas that PVE::SectionConfig generates.
I was initially intending to document these cases textually (as part
of the PVE::SectionConfig docs), but quickly realized that it's a
better idea to document these cases as unit / regression tests.
Therefore, set up a new test directory specifically for
PVE::SectionConfig and add `schema_comparison_test.pl`. The latter
contains a setup that allows us to write declarative tests for
SectionConfig in the form of nested packages:
- Each "test package" is based on the top-level `TestPackage`
- Inside such a test package, a PVE::SectionConfig plugin structure
must be declared (i.e. base plugin and child plugins)
- The create and update schemas of each plugin structure inside a
test package are then compared against their expected outputs,
which are also declared inside the test package
- this is done for both unified and isolated modes without the need to
define separate packages for either mode
In other words, to add a new test case, one has to only define a test
package, add a PVE::SectionConfig plugin structure inside of it, and
then declare the expected outputs for the schemas.
To run the tests, find all of the child packages of `TestPackage`
inside `main` (the test script) and run a subroutine for it that sets
up and tears down the plugin structure for unified and isolated mode.
Should a test fail, the contents of the gotten and expected schemas
are printed to the console in a human-readable format using
Data::Dumper, with some customized parameters. This should make it
rather easy to spot any discrepancies in case a test breaks.
Finally, add a basic test case checking that properties added by child
plugins are always optional, even if they're declared as non-optional,
both to document the first "interesting fact" about SectoinConfig and
also in order to actually show how test cases are added.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
test/Makefile | 5 +-
test/SectionConfig/Helpers.pm | 114 +++++++
test/SectionConfig/Makefile | 10 +
test/SectionConfig/schema_comparison_test.pl | 322 +++++++++++++++++++
4 files changed, 450 insertions(+), 1 deletion(-)
create mode 100644 test/SectionConfig/Helpers.pm
create mode 100644 test/SectionConfig/Makefile
create mode 100755 test/SectionConfig/schema_comparison_test.pl
diff --git a/test/Makefile b/test/Makefile
index 5b690c4..9b9f81b 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -1,4 +1,7 @@
-SUBDIRS = etc_network_interfaces
+SUBDIRS = etc_network_interfaces \
+ SectionConfig \
+
+
TESTS = lock_file.test \
calendar_event_test.test \
convert_size_test.test \
diff --git a/test/SectionConfig/Helpers.pm b/test/SectionConfig/Helpers.pm
new file mode 100644
index 0000000..970e0e4
--- /dev/null
+++ b/test/SectionConfig/Helpers.pm
@@ -0,0 +1,114 @@
+package SectionConfig::Helpers;
+
+use v5.36;
+
+use Data::Dumper;
+
+$Data::Dumper::Terse = 1;
+$Data::Dumper::Indent = 1;
+$Data::Dumper::Useqq = 1;
+$Data::Dumper::Deparse = 1;
+$Data::Dumper::Quotekeys = 0;
+$Data::Dumper::Sortkeys = 1;
+$Data::Dumper::Trailingcomma = 1;
+
+use base qw(Exporter);
+
+our @EXPORT_OK = qw(
+ get_symbol_table
+ symbol_table_has
+ get_subpackages
+ get_plugin_system_within_package
+ dump_symbol_table
+);
+
+our $UPDATE_SCHEMA_DEFAULT_PROPERTIES = {
+ digest => {
+ optional => 1,
+ type => 'string',
+ description => 'Prevent changes if current configuration file has a'
+ . ' different digest. This can be used to prevent concurrent'
+ . ' modifications.',
+ maxLength => 64,
+ },
+ delete => {
+ description => 'A list of settings you want to delete.',
+ maxLength => 4096,
+ format => 'pve-configid-list',
+ optional => 1,
+ type => 'string',
+ },
+};
+
+sub get_symbol_table($package) {
+ my $symbols = eval {
+ no strict 'refs'; ## no critic (ProhibitNoStrict)
+ *package_glob = *{"${package}::"};
+ my %syms = *package_glob->%*;
+ \%syms;
+ };
+
+ return $symbols;
+}
+
+sub symbol_table_has($package, $ref) {
+ my $symbols = get_symbol_table($package);
+ return defined($symbols->{$ref});
+}
+
+sub get_subpackages($package) {
+ my $symbols = get_symbol_table($package);
+
+ my $subpackages = [];
+
+ for my $symbol (keys $symbols->%*) {
+ if ($symbol =~ m/(?<name>.*)::$/) {
+ my $name = $+{name};
+ my $subpackage = "${package}::${name}";
+
+ my $is_class = eval { $subpackage->isa('UNIVERSAL') } || '';
+
+ push($subpackages->@*, $subpackage) if $is_class;
+ }
+ }
+
+ return $subpackages;
+}
+
+sub get_plugin_system_within_package($package) {
+ my $subpackages = get_subpackages($package);
+
+ my $base = undef;
+ my $plugins = [];
+
+ for my $package ($subpackages->@*) {
+ if ($package->isa('PVE::SectionConfig') && symbol_table_has($package, 'private')) {
+ $base = $package;
+ last;
+ }
+ }
+
+ for my $package ($subpackages->@*) {
+ if ($package->isa($base) && symbol_table_has($package, 'type')) {
+ push($plugins->@*, $package);
+ }
+ }
+
+ if (!defined($base)) {
+ die "failed to get plugin system within package '$package'";
+ }
+
+ return {
+ base => $base,
+ plugins => $plugins,
+ };
+}
+
+# debug aid
+sub dump_symbol_table($package) {
+ my $symbols = get_symbol_table($package);
+ print("'$package' => ", Dumper($symbols), "\n");
+ return;
+}
+
+1;
diff --git a/test/SectionConfig/Makefile b/test/SectionConfig/Makefile
new file mode 100644
index 0000000..d1c2b1a
--- /dev/null
+++ b/test/SectionConfig/Makefile
@@ -0,0 +1,10 @@
+TESTS = \
+ schema_comparison_test.pl \
+
+
+all:
+
+.PHONY: check
+check: $(TESTS)
+ for TEST in $(TESTS); do perl $$TEST; done
+
diff --git a/test/SectionConfig/schema_comparison_test.pl b/test/SectionConfig/schema_comparison_test.pl
new file mode 100755
index 0000000..49f6bf7
--- /dev/null
+++ b/test/SectionConfig/schema_comparison_test.pl
@@ -0,0 +1,322 @@
+#!/usr/bin/perl
+
+use v5.36;
+
+use lib qw(
+ ..
+ ../../src/
+);
+
+use Data::Dumper;
+
+$Data::Dumper::Terse = 1;
+$Data::Dumper::Indent = 1;
+$Data::Dumper::Useqq = 1;
+$Data::Dumper::Deparse = 1;
+$Data::Dumper::Quotekeys = 0;
+$Data::Dumper::Sortkeys = 1;
+$Data::Dumper::Trailingcomma = 1;
+
+use Storable qw(dclone);
+
+use Test::More;
+
+use SectionConfig::Helpers qw(
+ symbol_table_has
+ get_subpackages
+ get_plugin_system_within_package
+ dump_symbol_table
+);
+
+package TestPackage {
+ use Carp qw(confess);
+
+ sub desc($class) {
+ return undef;
+ }
+
+ sub expected_unified_createSchema($class) {
+ confess "not implemented";
+ }
+
+ sub expected_unified_updateSchema($class) {
+ confess "not implemented";
+ }
+
+ sub expected_isolated_createSchema($class) {
+ confess "not implemented";
+ }
+
+ sub expected_isolated_updateSchema($class) {
+ confess "not implemented";
+ }
+};
+
+package OneOptionalNoCommon {
+ use base qw(TestPackage);
+
+ sub desc($class) {
+ return "properties added by plugins are always optional, "
+ . "even if they're marked as required";
+ }
+
+ package OneOptionalNoCommon::PluginBase {
+ use base qw(PVE::SectionConfig);
+
+ my $DEFAULT_DATA = {};
+
+ sub private($class) {
+ return $DEFAULT_DATA;
+ }
+ };
+
+ package OneOptionalNoCommon::PluginOne {
+ use base qw(OneOptionalNoCommon::PluginBase);
+
+ sub type($class) {
+ return 'one';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-one' => {
+ type => 'string',
+ optional => 0,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-one' => {
+ optional => 0,
+ },
+ };
+ }
+ };
+
+ package OneOptionalNoCommon::PluginTwo {
+ use base qw(OneOptionalNoCommon::PluginBase);
+
+ sub type($class) {
+ return 'two';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-two' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ sub expected_unified_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+ }
+
+ sub expected_unified_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+
+ sub expected_isolated_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+ }
+
+ sub expected_isolated_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+};
+
+sub test_compare_deeply($got, $expected, $test_name, $test_package) {
+ $test_name = "$test_package - $test_name";
+ my $description = $test_package->desc();
+
+ if (!is_deeply($got, $expected, $test_name)) {
+ note("\nDescription: ", $description // "(none)", "\n");
+ note("Got:");
+ note(Dumper($got));
+ note("Expected:");
+ note(Dumper($expected));
+ note("=" x 40);
+ }
+
+ return;
+}
+
+sub init_and_run_tests($package) {
+ my $system = get_plugin_system_within_package($package);
+
+ my ($base, $plugins) = $system->@{qw(base plugins)};
+
+ my $original_private_data = dclone($base->private());
+
+ # unified mode
+
+ for my $plugin ($plugins->@*) {
+ $plugin->register();
+ }
+
+ $base->init();
+
+ test_compare_deeply(
+ $base->createSchema(),
+ $package->expected_unified_createSchema(),
+ "unified - createSchema comparison",
+ $package,
+ );
+
+ test_compare_deeply(
+ $base->updateSchema(),
+ $package->expected_unified_updateSchema(),
+ "unified - updateSchema comparison",
+ $package,
+ );
+
+ # Reset private data so that we can just re-initialize the entire
+ # plugin architecture ad hoc
+ $base->private()->%* = $original_private_data->%*;
+
+ # isolated mode
+
+ for my $plugin ($plugins->@*) {
+ $plugin->register();
+ }
+
+ $base->init(property_isolation => 1);
+
+ test_compare_deeply(
+ $base->createSchema(),
+ $package->expected_isolated_createSchema(),
+ "isolated - createSchema comparison",
+ $package,
+ );
+
+ test_compare_deeply(
+ $base->updateSchema(),
+ $package->expected_isolated_updateSchema(),
+ "isolated - updateSchema comparison",
+ $package,
+ );
+
+ return;
+}
+
+sub main() {
+ my $subpackages = get_subpackages('main');
+
+ my $test_packages = [];
+
+ for my $package (sort $subpackages->@*) {
+ if ($package->isa('TestPackage') && $package !~ m/TestPackage/) {
+ push($test_packages->@*, $package);
+ }
+ }
+
+ for my $package ($test_packages->@*) {
+ init_and_run_tests($package);
+ }
+
+ done_testing();
+
+ return 0;
+}
+
+main();
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 03/23] tests: sectionconfig: add test case for fixed props in updateSchema
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 01/23] sectionconfig: remove unused variable in get_property_schema() Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 02/23] tests: sectionconfig: add comparison test structure Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 04/23] tests: sectionconfig: add case for unused properties Max R. Carrara
` (19 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
Add a PVE::SectionConfig test case that shows that fixed properties
are always excluded from the update schema, in both unified and
isolated mode.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
test/SectionConfig/schema_comparison_test.pl | 151 +++++++++++++++++++
1 file changed, 151 insertions(+)
diff --git a/test/SectionConfig/schema_comparison_test.pl b/test/SectionConfig/schema_comparison_test.pl
index 49f6bf7..8f7921f 100755
--- a/test/SectionConfig/schema_comparison_test.pl
+++ b/test/SectionConfig/schema_comparison_test.pl
@@ -225,6 +225,157 @@ package OneOptionalNoCommon {
}
};
+package OneOptionalAllFixedNoCommon {
+ use base qw(TestPackage);
+
+ sub desc($class) {
+ return "for both unified and isolated mode, fixed properties are not"
+ . " included in updateSchema";
+ }
+
+ package OneOptionalAllFixedNoCommon::PluginBase {
+ use base qw(PVE::SectionConfig);
+
+ my $DEFAULT_DATA = {};
+
+ sub private($class) {
+ return $DEFAULT_DATA;
+ }
+ };
+
+ package OneOptionalAllFixedNoCommon::PluginOne {
+ use base qw(OneOptionalAllFixedNoCommon::PluginBase);
+
+ sub type($class) {
+ return 'one';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-one' => {
+ type => 'string',
+ optional => 0,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-one' => {
+ optional => 0,
+ fixed => 1,
+ },
+ };
+ }
+ };
+
+ package OneOptionalAllFixedNoCommon::PluginTwo {
+ use base qw(OneOptionalAllFixedNoCommon::PluginBase);
+
+ sub type($class) {
+ return 'two';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-two' => {
+ optional => 1,
+ fixed => 1,
+ },
+ };
+ }
+ };
+
+ sub expected_unified_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+ }
+
+ sub expected_unified_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+
+ sub expected_isolated_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+ }
+
+ sub expected_isolated_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+};
+
sub test_compare_deeply($got, $expected, $test_name, $test_package) {
$test_name = "$test_package - $test_name";
my $description = $test_package->desc();
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 04/23] tests: sectionconfig: add case for unused properties
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (2 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 03/23] tests: sectionconfig: add test case for fixed props in updateSchema Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 05/23] tests: sectionconfig: add case for unused optional default properties Max R. Carrara
` (18 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
Add a PVE::SectionConfig test case where plugins define properties
but then don't declare them in `options()`.
In unified mode, properties added but not used in any `options()`
method anywhere are excluded from the update schema.
In isolated mode, these properties are still implicitly declared as
being used—in other words, it's not necessary to declare (non-default)
properties in `options()`, unless the plugin author needs to (e.g. in
order to make a property `fixed`).
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
test/SectionConfig/schema_comparison_test.pl | 159 +++++++++++++++++++
1 file changed, 159 insertions(+)
diff --git a/test/SectionConfig/schema_comparison_test.pl b/test/SectionConfig/schema_comparison_test.pl
index 8f7921f..861792c 100755
--- a/test/SectionConfig/schema_comparison_test.pl
+++ b/test/SectionConfig/schema_comparison_test.pl
@@ -376,6 +376,165 @@ package OneOptionalAllFixedNoCommon {
}
};
+package AllUnusedNoCommon {
+ use base qw(TestPackage);
+
+ sub desc($class) {
+ return
+ "in unified mode, properties that plugins define but"
+ . " do not declare in options() are *not* included in updateSchema"
+ . " - in isolated mode, these properties are however included";
+ }
+
+ package AllUnusedNoCommon::PluginBase {
+ use base qw(PVE::SectionConfig);
+
+ my $DEFAULT_DATA = {};
+
+ sub private($class) {
+ return $DEFAULT_DATA;
+ }
+ };
+
+ package AllUnusedNoCommon::PluginOne {
+ use base qw(AllUnusedNoCommon::PluginBase);
+
+ sub type($class) {
+ return 'one';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-one' => {
+ type => 'string',
+ optional => 0,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {};
+ }
+ };
+
+ package AllUnusedNoCommon::PluginTwo {
+ use base qw(AllUnusedNoCommon::PluginBase);
+
+ sub type($class) {
+ return 'two';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {};
+ }
+ };
+
+ sub expected_unified_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+ }
+
+ sub expected_unified_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+
+ sub expected_isolated_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+ }
+
+ sub expected_isolated_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+}
+
sub test_compare_deeply($got, $expected, $test_name, $test_package) {
$test_name = "$test_package - $test_name";
my $description = $test_package->desc();
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 05/23] tests: sectionconfig: add case for unused optional default properties
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (3 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 04/23] tests: sectionconfig: add case for unused properties Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 06/23] tests: sectionconfig: add case for opt. default prop being req. once Max R. Carrara
` (17 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
Add a PVE::SectionConfig test case for plugin systems that declare an
optional default property (in `private()->{propertyList}`) but don't
use it anywhere, i.e. not declaring it in any `options()` method.
In unified mode, such properties are included in the create schema,
but *not* in the update schema.
In isolated mode, such properties are included in both schemas.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
test/SectionConfig/schema_comparison_test.pl | 194 +++++++++++++++++++
1 file changed, 194 insertions(+)
diff --git a/test/SectionConfig/schema_comparison_test.pl b/test/SectionConfig/schema_comparison_test.pl
index 861792c..94c0e2c 100755
--- a/test/SectionConfig/schema_comparison_test.pl
+++ b/test/SectionConfig/schema_comparison_test.pl
@@ -535,6 +535,200 @@ package AllUnusedNoCommon {
}
}
+package OptionalCommonUnused {
+ use base qw(TestPackage);
+
+ sub desc($class) {
+ return
+ "in unified mode, optional default properties that plugins"
+ . " do not declare in options() are *not* included in updateSchema"
+ . " - in isolated mode, such properties are included, however";
+ }
+
+ package OptionalCommonUnused::PluginBase {
+ use base qw(PVE::SectionConfig);
+
+ my $DEFAULT_DATA = {
+ propertyList => {
+ common => {
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+
+ sub private($class) {
+ return $DEFAULT_DATA;
+ }
+ };
+
+ package OptionalCommonUnused::PluginOne {
+ use base qw(OptionalCommonUnused::PluginBase);
+
+ sub type($class) {
+ return 'one';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-one' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ package OptionalCommonUnused::PluginTwo {
+ use base qw(OptionalCommonUnused::PluginBase);
+
+ sub type($class) {
+ return 'two';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-two' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ sub expected_unified_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'common' => {
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+ }
+
+ sub expected_unified_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+
+ sub expected_isolated_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'common' => {
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+ }
+
+ sub expected_isolated_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'common' => {
+ type => 'string',
+ optional => 1,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+}
+
sub test_compare_deeply($got, $expected, $test_name, $test_package) {
$test_name = "$test_package - $test_name";
my $description = $test_package->desc();
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 06/23] tests: sectionconfig: add case for opt. default prop being req. once
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (4 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 05/23] tests: sectionconfig: add case for unused optional default properties Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 07/23] tests: sectionconfig: add case for opt. default prop requirde by all Max R. Carrara
` (16 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
Add a PVE::SectionConfig test case for plugin systems that declare an
optional default property (in `private()->{propertyList}`) and require
it on at least one, but not all child plugins.
In other words, only one child plugin uses the optional default
property in its `options()` method, and does so with `optional => 0`.
In unified mode, such properties are marked as *optional* in both
create and update schema.
In isolated mode, such properties are only available for the plugins
that use them in `options()`, and for those plugins, they are
*optional* despite being marked as required.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
test/SectionConfig/schema_comparison_test.pl | 211 +++++++++++++++++++
1 file changed, 211 insertions(+)
diff --git a/test/SectionConfig/schema_comparison_test.pl b/test/SectionConfig/schema_comparison_test.pl
index 94c0e2c..f970d30 100755
--- a/test/SectionConfig/schema_comparison_test.pl
+++ b/test/SectionConfig/schema_comparison_test.pl
@@ -729,6 +729,217 @@ package OptionalCommonUnused {
}
}
+package OptionalCommonRequiredByOne {
+ use base qw(TestPackage);
+
+ sub desc($class) {
+ return
+ "in unified mode, optional default properties declared as"
+ . " non-optional by some but not all plugins are always optional"
+ . " for both schemas"
+ . " - in isolated mode, these properties are available and optional"
+ . " only for the plugins that use them";
+ }
+
+ package OptionalCommonRequiredByOne::PluginBase {
+ use base qw(PVE::SectionConfig);
+
+ my $DEFAULT_DATA = {
+ propertyList => {
+ common => {
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+
+ sub private($class) {
+ return $DEFAULT_DATA;
+ }
+ };
+
+ package OptionalCommonRequiredByOne::PluginOne {
+ use base qw(OptionalCommonRequiredByOne::PluginBase);
+
+ sub type($class) {
+ return 'one';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ common => {
+ optional => 0,
+ },
+ 'prop-one' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ package OptionalCommonRequiredByOne::PluginTwo {
+ use base qw(OptionalCommonRequiredByOne::PluginBase);
+
+ sub type($class) {
+ return 'two';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-two' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ sub expected_unified_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'common' => {
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+ }
+
+ sub expected_unified_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ common => {
+ type => 'string',
+ optional => 1,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+
+ sub expected_isolated_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'common' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+ }
+
+ sub expected_isolated_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ common => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+}
+
sub test_compare_deeply($got, $expected, $test_name, $test_package) {
$test_name = "$test_package - $test_name";
my $description = $test_package->desc();
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 07/23] tests: sectionconfig: add case for opt. default prop requirde by all
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (5 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 06/23] tests: sectionconfig: add case for opt. default prop being req. once Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 08/23] tests: sectionconfig: add case for unused required default properties Max R. Carrara
` (15 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
Add a PVE::SectionConfig test case for plugin systems that declare an
optional default property (in `private()->{propertyList}`) and mark it
as required on all child plugins.
In other words, all child plugins use the optional default property in
their `options()` methods, and do so with `optional => 0`.
In unified mode, such properties are marked as *required* in both
create and update schema.
In isolated mode, such properties are marked as *optional* in both
create and update schema.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
test/SectionConfig/schema_comparison_test.pl | 204 +++++++++++++++++++
1 file changed, 204 insertions(+)
diff --git a/test/SectionConfig/schema_comparison_test.pl b/test/SectionConfig/schema_comparison_test.pl
index f970d30..b49abfe 100755
--- a/test/SectionConfig/schema_comparison_test.pl
+++ b/test/SectionConfig/schema_comparison_test.pl
@@ -940,6 +940,210 @@ package OptionalCommonRequiredByOne {
}
}
+package OptionalCommonRequiredByAll {
+ use base qw(TestPackage);
+
+ sub desc($class) {
+ return
+ "in unified mode, optional default properties required by all plugins"
+ . " are required in createSchema and optional in updateSchema"
+ . " - in isolated mode, these properties are optional in *both* schemas";
+ }
+
+ package OptionalCommonRequiredByAll::PluginBase {
+ use base qw(PVE::SectionConfig);
+
+ my $DEFAULT_DATA = {
+ propertyList => {
+ common => {
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+
+ sub private($class) {
+ return $DEFAULT_DATA;
+ }
+ };
+
+ package OptionalCommonRequiredByAll::PluginOne {
+ use base qw(OptionalCommonRequiredByAll::PluginBase);
+
+ sub type($class) {
+ return 'one';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ common => {
+ optional => 0,
+ },
+ 'prop-one' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ package OptionalCommonRequiredByAll::PluginTwo {
+ use base qw(OptionalCommonRequiredByAll::PluginBase);
+
+ sub type($class) {
+ return 'two';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ common => {
+ optional => 0,
+ },
+ 'prop-two' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ sub expected_unified_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'common' => {
+ type => 'string',
+ optional => 0,
+ },
+ },
+ };
+ }
+
+ sub expected_unified_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ common => {
+ type => 'string',
+ optional => 1,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+
+ sub expected_isolated_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'common' => {
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+ }
+
+ sub expected_isolated_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ common => {
+ type => 'string',
+ optional => 1,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+}
+
sub test_compare_deeply($got, $expected, $test_name, $test_package) {
$test_name = "$test_package - $test_name";
my $description = $test_package->desc();
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 08/23] tests: sectionconfig: add case for unused required default properties
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (6 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 07/23] tests: sectionconfig: add case for opt. default prop requirde by all Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 09/23] tests: sectionconfig: add case for req. default prop being req. once Max R. Carrara
` (14 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
Add a PVE::SectionConfig test case for plugin systems that declare a
required default property (in `private()->{propertyList}`), but don't
use it in any child plugin (i.e. in `options()`).
In both unified and isolated mode, such properties are required in
both create and update schema.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
test/SectionConfig/schema_comparison_test.pl | 196 +++++++++++++++++++
1 file changed, 196 insertions(+)
diff --git a/test/SectionConfig/schema_comparison_test.pl b/test/SectionConfig/schema_comparison_test.pl
index b49abfe..fd6724e 100755
--- a/test/SectionConfig/schema_comparison_test.pl
+++ b/test/SectionConfig/schema_comparison_test.pl
@@ -1144,6 +1144,202 @@ package OptionalCommonRequiredByAll {
}
}
+package RequiredCommonUnused {
+ use base qw(TestPackage);
+
+ sub desc($class) {
+ return "for both unified and isolated mode, unused required default properties"
+ . " are required in both schemas";
+ }
+
+ package RequiredCommonUnused::PluginBase {
+ use base qw(PVE::SectionConfig);
+
+ my $DEFAULT_DATA = {
+ propertyList => {
+ common => {
+ type => 'string',
+ optional => 0,
+ },
+ },
+ };
+
+ sub private($class) {
+ return $DEFAULT_DATA;
+ }
+ };
+
+ package RequiredCommonUnused::PluginOne {
+ use base qw(RequiredCommonUnused::PluginBase);
+
+ sub type($class) {
+ return 'one';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-one' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ package RequiredCommonUnused::PluginTwo {
+ use base qw(RequiredCommonUnused::PluginBase);
+
+ sub type($class) {
+ return 'two';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-two' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ sub expected_unified_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'common' => {
+ type => 'string',
+ optional => 0,
+ },
+ },
+ };
+ }
+
+ sub expected_unified_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ common => {
+ type => 'string',
+ optional => 0,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+
+ sub expected_isolated_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'common' => {
+ type => 'string',
+ optional => 0,
+ },
+ },
+ };
+ }
+
+ sub expected_isolated_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ common => {
+ type => 'string',
+ optional => 0,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+}
+
sub test_compare_deeply($got, $expected, $test_name, $test_package) {
$test_name = "$test_package - $test_name";
my $description = $test_package->desc();
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 09/23] tests: sectionconfig: add case for req. default prop being req. once
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (7 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 08/23] tests: sectionconfig: add case for unused required default properties Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 10/23] tests: sectionconfig: add case for required default prop req. by all Max R. Carrara
` (13 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
Add a PVE::SectionConfig test case for plugin systems that declare a
required default property (in `private()->{propertyList}`) and require
it on at least one, but not all child plugins.
In other words, one child plugin uses the required default property in
its `options()` method, and does so with `optional => 0`.
In unified mode, such properties are marked as *required* in both
create and update schema.
In isolated mode, such properties are only available for the plugins
that use them in `options()`, and for those plugins they are
*optional* in both create and update schema, despite being marked as
required.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
test/SectionConfig/schema_comparison_test.pl | 210 +++++++++++++++++++
1 file changed, 210 insertions(+)
diff --git a/test/SectionConfig/schema_comparison_test.pl b/test/SectionConfig/schema_comparison_test.pl
index fd6724e..1189de8 100755
--- a/test/SectionConfig/schema_comparison_test.pl
+++ b/test/SectionConfig/schema_comparison_test.pl
@@ -1340,6 +1340,216 @@ package RequiredCommonUnused {
}
}
+package RequiredCommonRequiredByOne {
+ use base qw(TestPackage);
+
+ sub desc($class) {
+ return
+ "in unified mode, optional default properties that at least one"
+ . " plugin declares as required are always required in both schemas"
+ . " - in isolated mode, the same properties are only available to the"
+ . " plugin that declares them while *also being optional* in both schemas";
+ }
+
+ package RequiredCommonRequiredByOne::PluginBase {
+ use base qw(PVE::SectionConfig);
+
+ my $DEFAULT_DATA = {
+ propertyList => {
+ common => {
+ type => 'string',
+ optional => 0,
+ },
+ },
+ };
+
+ sub private($class) {
+ return $DEFAULT_DATA;
+ }
+ };
+
+ package RequiredCommonRequiredByOne::PluginOne {
+ use base qw(RequiredCommonRequiredByOne::PluginBase);
+
+ sub type($class) {
+ return 'one';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-one' => {
+ optional => 1,
+ },
+ common => {
+ optional => 0,
+ },
+ };
+ }
+ };
+
+ package RequiredCommonRequiredByOne::PluginTwo {
+ use base qw(RequiredCommonRequiredByOne::PluginBase);
+
+ sub type($class) {
+ return 'two';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-two' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ sub expected_unified_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'common' => {
+ type => 'string',
+ optional => 0,
+ },
+ },
+ };
+ }
+
+ sub expected_unified_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ common => {
+ type => 'string',
+ optional => 0,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+
+ sub expected_isolated_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'common' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+ }
+
+ sub expected_isolated_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ common => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+}
+
sub test_compare_deeply($got, $expected, $test_name, $test_package) {
$test_name = "$test_package - $test_name";
my $description = $test_package->desc();
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 10/23] tests: sectionconfig: add case for required default prop req. by all
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (8 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 09/23] tests: sectionconfig: add case for req. default prop being req. once Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 11/23] tests: sectionconfig: add case for required default props opt. for all Max R. Carrara
` (12 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
Add a PVE::SectionConfig test case for plugin systems that declare a
required default property (in `private()->{propertyList}`) and also
mark it as required in all child plugins' `options()`.
In both unified and isolated mode, such properties are marked as
required in both create and update schema.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
test/SectionConfig/schema_comparison_test.pl | 196 +++++++++++++++++++
1 file changed, 196 insertions(+)
diff --git a/test/SectionConfig/schema_comparison_test.pl b/test/SectionConfig/schema_comparison_test.pl
index 1189de8..97f4820 100755
--- a/test/SectionConfig/schema_comparison_test.pl
+++ b/test/SectionConfig/schema_comparison_test.pl
@@ -1550,6 +1550,202 @@ package RequiredCommonRequiredByOne {
}
}
+package RequiredCommonRequiredByAll {
+ use base qw(TestPackage);
+
+ sub desc($class) {
+ return "for both unified and isolated mode, required default properties"
+ . " that all plugins declare as required remain required in both schemas";
+ }
+
+ package RequiredCommonRequiredByAll::PluginBase {
+ use base qw(PVE::SectionConfig);
+
+ my $DEFAULT_DATA = {
+ propertyList => {
+ common => {
+ type => 'string',
+ optional => 0,
+ },
+ },
+ };
+
+ sub private($class) {
+ return $DEFAULT_DATA;
+ }
+ };
+
+ package RequiredCommonRequiredByAll::PluginOne {
+ use base qw(RequiredCommonRequiredByAll::PluginBase);
+
+ sub type($class) {
+ return 'one';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-one' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ package RequiredCommonRequiredByAll::PluginTwo {
+ use base qw(RequiredCommonRequiredByAll::PluginBase);
+
+ sub type($class) {
+ return 'two';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-two' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ sub expected_unified_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'common' => {
+ type => 'string',
+ optional => 0,
+ },
+ },
+ };
+ }
+
+ sub expected_unified_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ common => {
+ type => 'string',
+ optional => 0,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+
+ sub expected_isolated_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'common' => {
+ type => 'string',
+ optional => 0,
+ },
+ },
+ };
+ }
+
+ sub expected_isolated_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ common => {
+ type => 'string',
+ optional => 0,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+}
+
sub test_compare_deeply($got, $expected, $test_name, $test_package) {
$test_name = "$test_package - $test_name";
my $description = $test_package->desc();
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 11/23] tests: sectionconfig: add case for required default props opt. for all
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (9 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 10/23] tests: sectionconfig: add case for required default prop req. by all Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 12/23] tests: sectionconfig: add isolated mode test structure Max R. Carrara
` (11 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
Add a PVE::SectionConfig test case for plugin systems that declare a
default property as required (in `private()->{propertyList}`), but
mark it as optional in all child plugins (i.e. in their `options()`
methods).
In unified mode, such properties remain marked as *required* in both
create and update schema.
In isolated mode, such properties are marked as *optional* in both
create and update schema.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
test/SectionConfig/schema_comparison_test.pl | 204 +++++++++++++++++++
1 file changed, 204 insertions(+)
diff --git a/test/SectionConfig/schema_comparison_test.pl b/test/SectionConfig/schema_comparison_test.pl
index 97f4820..3ae8551 100755
--- a/test/SectionConfig/schema_comparison_test.pl
+++ b/test/SectionConfig/schema_comparison_test.pl
@@ -1746,6 +1746,210 @@ package RequiredCommonRequiredByAll {
}
}
+package RequiredCommonOptionalForAll {
+ use base qw(TestPackage);
+
+ sub desc($class) {
+ return
+ "in unified mode, required default properties remain required in"
+ . " both schemas, even if declared as optional"
+ . " - in isolated mode, these properties are optional in both schemas";
+ }
+
+ package RequiredCommonOptionalForAll::PluginBase {
+ use base qw(PVE::SectionConfig);
+
+ my $DEFAULT_DATA = {
+ propertyList => {
+ common => {
+ type => 'string',
+ optional => 0,
+ },
+ },
+ };
+
+ sub private($class) {
+ return $DEFAULT_DATA;
+ }
+ };
+
+ package RequiredCommonOptionalForAll::PluginOne {
+ use base qw(RequiredCommonOptionalForAll::PluginBase);
+
+ sub type($class) {
+ return 'one';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ common => {
+ optional => 1,
+ },
+ 'prop-one' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ package RequiredCommonOptionalForAll::PluginTwo {
+ use base qw(RequiredCommonOptionalForAll::PluginBase);
+
+ sub type($class) {
+ return 'two';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ common => {
+ optional => 1,
+ },
+ 'prop-two' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ sub expected_unified_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'common' => {
+ type => 'string',
+ optional => 0,
+ },
+ },
+ };
+ }
+
+ sub expected_unified_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ common => {
+ type => 'string',
+ optional => 0,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+
+ sub expected_isolated_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'common' => {
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+ }
+
+ sub expected_isolated_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ common => {
+ type => 'string',
+ optional => 1,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+}
+
sub test_compare_deeply($got, $expected, $test_name, $test_package) {
$test_name = "$test_package - $test_name";
my $description = $test_package->desc();
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 12/23] tests: sectionconfig: add isolated mode test structure
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (10 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 11/23] tests: sectionconfig: add case for required default props opt. for all Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 13/23] tests: sectionconfig: add case for an ident. prop on two plugins Max R. Carrara
` (10 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
Similar to `schema_comparison_test.pl`, add `schema_isolated_test.pl`
for tests specific to PVE::SectionConfig's property isolation mode.
The purpose of these tests is to document the existing quirks of the
isolated mode of PVE::SectionConfig as well as to ensure that we catch
any regressions, should we need to make any changes to the inner
machinery of PVE::SectionConfig in the future.
This test script works similar to `schema_comparison_test.pl`, expect
that its cases are (obviously) not tested against their counterparts
in unified mode, as that simply doesn't apply for them.
Add a simple basic test case for child plugins that define identical
properties under isolated mode.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
test/SectionConfig/Makefile | 1 +
test/SectionConfig/schema_isolated_test.pl | 239 +++++++++++++++++++++
2 files changed, 240 insertions(+)
create mode 100755 test/SectionConfig/schema_isolated_test.pl
diff --git a/test/SectionConfig/Makefile b/test/SectionConfig/Makefile
index d1c2b1a..b0dfc93 100644
--- a/test/SectionConfig/Makefile
+++ b/test/SectionConfig/Makefile
@@ -1,5 +1,6 @@
TESTS = \
schema_comparison_test.pl \
+ schema_isolated_test.pl \
all:
diff --git a/test/SectionConfig/schema_isolated_test.pl b/test/SectionConfig/schema_isolated_test.pl
new file mode 100755
index 0000000..bc2c551
--- /dev/null
+++ b/test/SectionConfig/schema_isolated_test.pl
@@ -0,0 +1,239 @@
+#!/usr/bin/perl
+
+use v5.36;
+
+use lib qw(
+ ..
+ ../../src/
+);
+
+use Data::Dumper;
+
+$Data::Dumper::Terse = 1;
+$Data::Dumper::Indent = 1;
+$Data::Dumper::Useqq = 1;
+$Data::Dumper::Deparse = 1;
+$Data::Dumper::Quotekeys = 0;
+$Data::Dumper::Sortkeys = 1;
+$Data::Dumper::Trailingcomma = 1;
+
+use Test::More;
+
+use SectionConfig::Helpers qw(
+ symbol_table_has
+ get_subpackages
+ get_plugin_system_within_package
+ dump_symbol_table
+);
+
+package TestPackage {
+ use Carp qw(confess);
+
+ sub expected_isolated_createSchema($class) {
+ confess "not implemented";
+ }
+
+ sub expected_isolated_updateSchema($class) {
+ confess "not implemented";
+ }
+
+ sub desc($class) {
+ return undef;
+ }
+};
+
+package IdenticalPropertiesOnDifferentPlugins {
+ use base qw(TestPackage);
+
+ sub desc($class) {
+ return "defining identical properties on different plugins does not lead to"
+ . " 'oneOf' being used inside either createSchema or updateSchema";
+ }
+
+ package IdenticalPropertiesOnDifferentPlugins::PluginBase {
+ use base qw(PVE::SectionConfig);
+
+ my $DEFAULT_DATA = {};
+
+ sub private($class) {
+ return $DEFAULT_DATA;
+ }
+ };
+
+ package IdenticalPropertiesOnDifferentPlugins::PluginOne {
+ use base qw(IdenticalPropertiesOnDifferentPlugins::PluginBase);
+
+ sub type($class) {
+ return 'one';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-one' => {
+ optional => 1,
+ },
+ 'prop-two' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ package IdenticalPropertiesOnDifferentPlugins::PluginTwo {
+ use base qw(IdenticalPropertiesOnDifferentPlugins::PluginBase);
+
+ sub type($class) {
+ return 'two';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-one' => {
+ optional => 1,
+ },
+ 'prop-two' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ sub expected_isolated_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+ }
+
+ sub expected_isolated_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+}
+
+sub test_compare_deeply($got, $expected, $test_name, $test_package) {
+ $test_name = "$test_package - $test_name";
+ my $description = $test_package->desc();
+
+ if (!is_deeply($got, $expected, $test_name)) {
+ note("\nDescription: ", $description // "(none)", "\n");
+ note("Got:");
+ note(Dumper($got));
+ note("Expected:");
+ note(Dumper($expected));
+ note("=" x 40);
+ }
+
+ return;
+}
+
+sub init_and_run_tests($package) {
+ my $system = get_plugin_system_within_package($package);
+
+ my ($base, $plugins) = $system->@{qw(base plugins)};
+
+ for my $plugin ($plugins->@*) {
+ $plugin->register();
+ }
+
+ $base->init(property_isolation => 1);
+
+ test_compare_deeply(
+ $base->createSchema(),
+ $package->expected_isolated_createSchema(),
+ "isolated - createSchema comparison",
+ $package,
+ );
+
+ test_compare_deeply(
+ $base->updateSchema(),
+ $package->expected_isolated_updateSchema(),
+ "isolated - updateSchema comparison",
+ $package,
+ );
+
+ return;
+}
+
+sub main() {
+ my $subpackages = get_subpackages('main');
+
+ my $test_packages = [];
+
+ for my $package (sort $subpackages->@*) {
+ if ($package !~ m/TestPackage/ && $package->isa('TestPackage')) {
+ push($test_packages->@*, $package);
+ }
+ }
+
+ for my $package ($test_packages->@*) {
+ init_and_run_tests($package);
+ }
+
+ done_testing();
+
+ return 0;
+}
+
+main();
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 13/23] tests: sectionconfig: add case for an ident. prop on two plugins
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (11 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 12/23] tests: sectionconfig: add isolated mode test structure Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 14/23] tests: sectionconfig: add case for same prop. w/ different optionality Max R. Carrara
` (9 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
Add a PVE::SectionConfig test case in which one plugin defines an
identical property as another plugin.
In both create and update schema, that property does is shared between
both plugins, that is it does not have any `type-property` /
`instance-types` attributes.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
test/SectionConfig/schema_isolated_test.pl | 131 +++++++++++++++++++++
1 file changed, 131 insertions(+)
diff --git a/test/SectionConfig/schema_isolated_test.pl b/test/SectionConfig/schema_isolated_test.pl
index bc2c551..4b14d8a 100755
--- a/test/SectionConfig/schema_isolated_test.pl
+++ b/test/SectionConfig/schema_isolated_test.pl
@@ -172,6 +172,137 @@ package IdenticalPropertiesOnDifferentPlugins {
}
}
+package IdenticalPropertyOnDifferentPlugin {
+ use base qw(TestPackage);
+
+ sub desc($class) {
+ return "defining identical properties on different plugins does not lead to"
+ . " 'oneOf' being used inside either createSchema or updateSchema";
+ }
+
+ package IdenticalPropertyOnDifferentPlugin::PluginBase {
+ use base qw(PVE::SectionConfig);
+
+ my $DEFAULT_DATA = {};
+
+ sub private($class) {
+ return $DEFAULT_DATA;
+ }
+ };
+
+ package IdenticalPropertyOnDifferentPlugin::PluginOne {
+ use base qw(IdenticalPropertyOnDifferentPlugin::PluginBase);
+
+ sub type($class) {
+ return 'one';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-one' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ package IdenticalPropertyOnDifferentPlugin::PluginTwo {
+ use base qw(IdenticalPropertyOnDifferentPlugin::PluginBase);
+
+ sub type($class) {
+ return 'two';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-one' => {
+ optional => 1,
+ },
+ 'prop-two' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ sub expected_isolated_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+ }
+
+ sub expected_isolated_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+}
+
sub test_compare_deeply($got, $expected, $test_name, $test_package) {
$test_name = "$test_package - $test_name";
my $description = $test_package->desc();
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 14/23] tests: sectionconfig: add case for same prop. w/ different optionality
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (12 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 13/23] tests: sectionconfig: add case for an ident. prop on two plugins Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 15/23] tests: sectionconfig: add case for differing opt. default prop uses Max R. Carrara
` (8 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
Add a PVE::SectionConfig isolated mode test case where a property with
the same name, but differing optionality is defined on two plugins.
That is, the name is the same, but one has `optional => 1`, whereas
the other has `optional => 0`.
In both create and update schema, both properties are marked as
optional, because properties added by child plugins are always marked
as optional.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
test/SectionConfig/schema_isolated_test.pl | 133 +++++++++++++++++++++
1 file changed, 133 insertions(+)
diff --git a/test/SectionConfig/schema_isolated_test.pl b/test/SectionConfig/schema_isolated_test.pl
index 4b14d8a..a94dcb1 100755
--- a/test/SectionConfig/schema_isolated_test.pl
+++ b/test/SectionConfig/schema_isolated_test.pl
@@ -303,6 +303,139 @@ package IdenticalPropertyOnDifferentPlugin {
}
}
+package SamePropertyNamesOnDifferentPlugins {
+ use base qw(TestPackage);
+
+ sub desc($class) {
+ return
+ "defining properties with the same name but different optionality"
+ . " on different plugins does not lead to 'oneOf' being used inside"
+ . " either createSchema or updateSchema - because properties defined"
+ . " by plugins are always marked as optional";
+ }
+
+ package SamePropertyNamesOnDifferentPlugins::PluginBase {
+ use base qw(PVE::SectionConfig);
+
+ my $DEFAULT_DATA = {};
+
+ sub private($class) {
+ return $DEFAULT_DATA;
+ }
+ };
+
+ package SamePropertyNamesOnDifferentPlugins::PluginOne {
+ use base qw(SamePropertyNamesOnDifferentPlugins::PluginBase);
+
+ sub type($class) {
+ return 'one';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-one' => {
+ type => 'string',
+ optional => 0,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-one' => {
+ optional => 0,
+ },
+ 'prop-two' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ package SamePropertyNamesOnDifferentPlugins::PluginTwo {
+ use base qw(SamePropertyNamesOnDifferentPlugins::PluginBase);
+
+ sub type($class) {
+ return 'two';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 0,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ 'prop-one' => {
+ optional => 1,
+ },
+ 'prop-two' => {
+ optional => 0,
+ },
+ };
+ }
+ };
+
+ sub expected_isolated_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+ }
+
+ sub expected_isolated_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+}
+
sub test_compare_deeply($got, $expected, $test_name, $test_package) {
$test_name = "$test_package - $test_name";
my $description = $test_package->desc();
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 15/23] tests: sectionconfig: add case for differing opt. default prop uses
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (13 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 14/23] tests: sectionconfig: add case for same prop. w/ different optionality Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 16/23] tests: sectionconfig: add case for differing req. " Max R. Carrara
` (7 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
Add a PVE::SectionConfig isolated mode test case where an optional
default property (i.e. it's defined in `private()->{propertyList}`) is
marked as required by one plugin, but as optional by the other.
In both create and update schema, such a property is marked as
optional for plugins that use it, even for plugins that mark it as
required.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
test/SectionConfig/schema_isolated_test.pl | 155 +++++++++++++++++++++
1 file changed, 155 insertions(+)
diff --git a/test/SectionConfig/schema_isolated_test.pl b/test/SectionConfig/schema_isolated_test.pl
index a94dcb1..202e039 100755
--- a/test/SectionConfig/schema_isolated_test.pl
+++ b/test/SectionConfig/schema_isolated_test.pl
@@ -436,6 +436,161 @@ package SamePropertyNamesOnDifferentPlugins {
}
}
+package OptionalCommonRequiredAndOptional {
+ use base qw(TestPackage);
+
+ sub desc($class) {
+ return
+ "optional default properties not required by all plugins"
+ . " are optional in both schemas for plugins that use them,"
+ . " even if a plugin marks it as required";
+ }
+
+ package OptionalCommonRequiredAndOptional::PluginBase {
+ use base qw(PVE::SectionConfig);
+
+ my $DEFAULT_DATA = {
+ propertyList => {
+ common => {
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+
+ sub private($class) {
+ return $DEFAULT_DATA;
+ }
+ };
+
+ package OptionalCommonRequiredAndOptional::PluginOne {
+ use base qw(OptionalCommonRequiredAndOptional::PluginBase);
+
+ sub type($class) {
+ return 'one';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ common => {
+ optional => 0,
+ },
+ 'prop-one' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ package OptionalCommonRequiredAndOptional::PluginTwo {
+ use base qw(OptionalCommonRequiredAndOptional::PluginBase);
+
+ sub type($class) {
+ return 'two';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ common => {
+ optional => 1,
+ },
+ 'prop-two' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ sub expected_isolated_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'common' => {
+ type => 'string',
+ optional => 1,
+ },
+ },
+ };
+ }
+
+ sub expected_isolated_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ common => {
+ type => 'string',
+ optional => 1,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+}
+
sub test_compare_deeply($got, $expected, $test_name, $test_package) {
$test_name = "$test_package - $test_name";
my $description = $test_package->desc();
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 16/23] tests: sectionconfig: add case for differing req. default prop uses
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (14 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 15/23] tests: sectionconfig: add case for differing opt. default prop uses Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 17/23] sectionconfig: correct docs regarding global props in unified mode Max R. Carrara
` (6 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
Add a PVE::SectionConfig isolated mode test case where a required
default property (i.e. it's defined in `private()->{propertyList}`) is
marked as required by one plugin, but as optional by the other.
In the create schema, such a property uses a `oneOf` schema to differ
between the cases of both plugins. In other words, if a plugin marked
it as optional, it is marked as optional in the create schema too, and
vice versa.
In the update schema, such a property is marked as optional for both
plugins.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
test/SectionConfig/schema_isolated_test.pl | 168 +++++++++++++++++++++
1 file changed, 168 insertions(+)
diff --git a/test/SectionConfig/schema_isolated_test.pl b/test/SectionConfig/schema_isolated_test.pl
index 202e039..95980de 100755
--- a/test/SectionConfig/schema_isolated_test.pl
+++ b/test/SectionConfig/schema_isolated_test.pl
@@ -591,6 +591,174 @@ package OptionalCommonRequiredAndOptional {
}
}
+package RequiredCommonRequiredAndOptional {
+ use base qw(TestPackage);
+
+ sub desc($class) {
+ return "when a required default property is marked as both optional and required"
+ . " by different plugins, 'oneOf' is used in createSchema";
+ }
+
+ package RequiredCommonRequiredAndOptional::PluginBase {
+ use base qw(PVE::SectionConfig);
+
+ my $DEFAULT_DATA = {
+ propertyList => {
+ common => {
+ type => 'string',
+ optional => 0,
+ },
+ },
+ };
+
+ sub private($class) {
+ return $DEFAULT_DATA;
+ }
+ };
+
+ package RequiredCommonRequiredAndOptional::PluginOne {
+ use base qw(RequiredCommonRequiredAndOptional::PluginBase);
+
+ sub type($class) {
+ return 'one';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-one' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ common => {
+ optional => 0,
+ },
+ 'prop-one' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ package RequiredCommonRequiredAndOptional::PluginTwo {
+ use base qw(RequiredCommonRequiredAndOptional::PluginBase);
+
+ sub type($class) {
+ return 'two';
+ }
+
+ sub properties($class) {
+ return {
+ 'prop-two' => {
+ type => 'string',
+ optional => 1,
+ },
+ };
+ }
+
+ sub options($class) {
+ return {
+ common => {
+ optional => 1,
+ },
+ 'prop-two' => {
+ optional => 1,
+ },
+ };
+ }
+ };
+
+ sub expected_isolated_createSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'common' => {
+ oneOf => [
+ {
+ 'instance-types' => [
+ "one",
+ ],
+ optional => 0,
+ type => 'string',
+ },
+ {
+ 'instance-types' => [
+ "two",
+ ],
+ optional => 1,
+ type => 'string',
+ },
+ ],
+ 'type-property' => 'type',
+ },
+ },
+ };
+ }
+
+ sub expected_isolated_updateSchema($class) {
+ return {
+ type => 'object',
+ additionalProperties => 0,
+ properties => {
+ type => {
+ type => 'string',
+ enum => [
+ "one", "two",
+ ],
+ },
+ 'prop-one' => {
+ 'instance-types' => [
+ "one",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ 'prop-two' => {
+ 'instance-types' => [
+ "two",
+ ],
+ 'type-property' => 'type',
+ type => 'string',
+ optional => 1,
+ },
+ common => {
+ type => 'string',
+ optional => 1,
+ },
+ $SectionConfig::Helpers::UPDATE_SCHEMA_DEFAULT_PROPERTIES->%*,
+ },
+ };
+ }
+}
+
sub test_compare_deeply($got, $expected, $test_name, $test_package) {
$test_name = "$test_package - $test_name";
my $description = $test_package->desc();
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 17/23] sectionconfig: correct docs regarding global props in unified mode
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (15 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 16/23] tests: sectionconfig: add case for differing req. " Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 18/23] sectionconfig: reword docs regarding property usage in isolated mode Max R. Carrara
` (5 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
Note that default properties in unified mode are only excluded from
the update schema if they are not specified in `options()`.
Remove the paragraph at the end that suggests that this is the case
for isolated mode and both create and update schema.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
src/PVE/SectionConfig.pm | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/PVE/SectionConfig.pm b/src/PVE/SectionConfig.pm
index 069ca9e..6504399 100644
--- a/src/PVE/SectionConfig.pm
+++ b/src/PVE/SectionConfig.pm
@@ -345,15 +345,17 @@ which I<properties> are used in the method's return value. Because properties
are registered globally in this mode, any properties may be specified,
regardless of which plugin introduced them.
+B<NOTE:> Properties in the global list of properties (see C<L<< private()|/$base->private() >>>)
+are B<not> automatically added to C<L<< updateSchema()|/$base->updateSchema() >>>
+and must be explicitly declared in C<L<< options()|/$plugin->options() >>> if one
+wishes to use them.
+
In I<L<isolated mode|/MODES>>, the locally defined properties (those registered
by overriding C<L<< properties()|/$plugin->properties() >>>) are automatically
added to the plugin's schema and made C<optional> by default. Should this not be
desired, a property may still be explicitly defined, in order to make it required
or C<fixed> instead.
-Properties in the global list of properties (see C<L<< private()|/$base->private() >>>)
-are not automatically added and must be explicitly defined instead.
-
=cut
sub options {
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 18/23] sectionconfig: reword docs regarding property usage in isolated mode
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (16 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 17/23] sectionconfig: correct docs regarding global props in unified mode Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 19/23] sectionconfig: extend / correct docstring of `private()` method Max R. Carrara
` (4 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
In the MODES section, clarify that declaring a plugin's own properties
in `options()` is not necessary in isolated mode for visibility's
sake. The phrase "automatically added to its schema" might otherwise
not be completely clear to lesser experienced users.
Also reword a similar paragraph in the docstring for the
`properties()` method, making it a little less wordy overall.
Additionally, that paragraph mentions that a local property may be
marked as required in `options()`, which is incorrect, so get rid of
that passage of text too.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
src/PVE/SectionConfig.pm | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/PVE/SectionConfig.pm b/src/PVE/SectionConfig.pm
index 6504399..6ad8772 100644
--- a/src/PVE/SectionConfig.pm
+++ b/src/PVE/SectionConfig.pm
@@ -90,7 +90,9 @@ return value of the C<L<< options()|/$plugin->options() >>> method when it's eit
C<fixed> or stems from the global list of properties.
All I<locally> defined properties of a child plugin are automatically added to
-its schema.
+its schema. This means that is is not necessary to declare the usage of a
+plugin's own properties in its own C<L<< options()|/$plugin->options() >>>
+method, unless one wants to declare it as C<fixed>.
=cut
@@ -352,9 +354,8 @@ wishes to use them.
In I<L<isolated mode|/MODES>>, the locally defined properties (those registered
by overriding C<L<< properties()|/$plugin->properties() >>>) are automatically
-added to the plugin's schema and made C<optional> by default. Should this not be
-desired, a property may still be explicitly defined, in order to make it required
-or C<fixed> instead.
+added to the plugin's schema and made C<optional> by default. However, marking
+the property as C<fixed> must still be done via C<L<< options()|/$plugin->options() >>>.
=cut
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 19/23] sectionconfig: extend / correct docstring of `private()` method
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (17 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 18/23] sectionconfig: reword docs regarding property usage in isolated mode Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 20/23] sectionconfig: note that a prop must be defined through a JSONSchema Max R. Carrara
` (3 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
At the end of the docstring, note that ..
* one really should only declare default properties as required in
very special cases, such as using a property as an identifier
* and that only attributes specific to PVE::JSONSchema should be
declared on default properties
The latter case in particular was mentioned off-list a while ago;
every default property must be defined through a valid JSONSchema, any
other attributes really shouldn't be added.
Also correct the example in this docstring along the way, correctly
quoting the key and adding `optional => 1` in order not to suggest
anything that is advised against in the new paragraph further below.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
src/PVE/SectionConfig.pm | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/src/PVE/SectionConfig.pm b/src/PVE/SectionConfig.pm
index 6ad8772..71a0019 100644
--- a/src/PVE/SectionConfig.pm
+++ b/src/PVE/SectionConfig.pm
@@ -138,9 +138,10 @@ More precisely, this method returns a hash with the following structure:
optional => 1,
description => 'example property',
},
- some-property => {
- description => 'another example property',
+ 'some-property' => {
type => 'boolean'
+ optional => 1,
+ description => 'another example property',
},
},
options => {
@@ -204,6 +205,19 @@ plugin architecture upfront, for example:
Additional properties defined in I<child plugins> are stored in the
C<propertyList> key. See C<L<< properties()|/$plugin->properties() >>>.
+B<NOTE:> It is advised to mark all default properties in C<propertyList> as
+optional using C<< optional => 1 >>, with only very few exceptions. One such
+exception would be a property that you intend to use as unique identifier of a
+section. In that case, ensure that you explicitly define it with
+C<< optional => 0 >> and that it is not used in any child plugins'
+C<L<< options()|/$plugin->options() >>> method.
+
+B<NOTE:> When specifying the default properties in the C<propertyList> key, you
+MAY only add attributes related to C<L<PVE::JSONSchema>> to property
+definitions. Any other attributes, even those specific to C<PVE::SectionConfig>
+(like C<fixed>), are not considered valid. All properties in C<propertyList>
+MUST be defined through a valid JSONSchema.
+
=cut
sub private {
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 20/23] sectionconfig: note that a prop must be defined through a JSONSchema
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (18 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 19/23] sectionconfig: extend / correct docstring of `private()` method Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 21/23] sectionconfig: note that props added by plugins are always optional Max R. Carrara
` (2 subsequent siblings)
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
... in the docstring for the `properties()` method, to make things a
little more obvious for readers less acquainted with
PVE::SectionConfig.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
src/PVE/SectionConfig.pm | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/PVE/SectionConfig.pm b/src/PVE/SectionConfig.pm
index 71a0019..a584e0c 100644
--- a/src/PVE/SectionConfig.pm
+++ b/src/PVE/SectionConfig.pm
@@ -299,6 +299,9 @@ See below for details on L<the different modes|/MODES>.
This method doesn't need to be overridden if no new properties are necessary.
+A property's definition must always be a valid JSON Schema. See
+C<L<PVE::JSONSchema>> for more information on how to define such a schema.
+
sub properties() {
return {
path => {
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 21/23] sectionconfig: note that props added by plugins are always optional
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (19 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 20/23] sectionconfig: note that a prop must be defined through a JSONSchema Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 22/23] sectionconfig: note that `createSchema()` is universal for all plugins Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 23/23] sectionconfig: correct example in docstring of `updateSchema()` Max R. Carrara
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
... in the docstring for the `properties()` method. Also explain why
that is the case.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
src/PVE/SectionConfig.pm | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/PVE/SectionConfig.pm b/src/PVE/SectionConfig.pm
index a584e0c..5c83d6a 100644
--- a/src/PVE/SectionConfig.pm
+++ b/src/PVE/SectionConfig.pm
@@ -328,6 +328,11 @@ the plugin's schema and made C<optional> by default.
See the C<L<< options()|/$plugin->options() >>> method for more information.
+B<NOTE:> All properties added by child plugins are always marked as C<< optional => 1 >>,
+even if explicitly declared as C<< optional => 0 >>. If that were not the case,
+any plugin could add a new required (non-optional) property and break existing
+plugins.
+
=cut
sub properties {
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 22/23] sectionconfig: note that `createSchema()` is universal for all plugins
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (20 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 21/23] sectionconfig: note that props added by plugins are always optional Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 23/23] sectionconfig: correct example in docstring of `updateSchema()` Max R. Carrara
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
... in the docstring of the `createSchema()` method, mostly because
this might not be super obvious for readers not as familiar with
PVE::SectionConfig.
Adapt the header to reflect this.
While at it, also use an L<> tag for PVE::JSONSchema.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
src/PVE/SectionConfig.pm | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/src/PVE/SectionConfig.pm b/src/PVE/SectionConfig.pm
index 5c83d6a..7c27276 100644
--- a/src/PVE/SectionConfig.pm
+++ b/src/PVE/SectionConfig.pm
@@ -467,19 +467,23 @@ my sub add_property {
}
}
-=head3 $plugin->createSchema()
+=head3 $base->createSchema()
-=head3 $plugin->createSchema([ $skip_type, $base ])
+=head3 $base->createSchema([ $skip_type, $base ])
$schema = PVE::Example::Plugin->createSchema($skip_type, $base)
$schema = $class->createSchema($skip_type, $base)
-Returns the C<PVE::JSONSchema> used for I<creating> config entries of a
+Returns the C<L<PVE::JSONSchema>> used for I<creating> config entries of a
I<child plugin>.
This schema may then be used as desired, for example as the definition of
parameters of an API handler (C<POST>).
+B<NOTE:> This schema is universal for all registered plugins. This means that
+calling this method on different plugin instances will always return the same
+result.
+
=over
=item C<$skip_type> (optional)
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
* [pve-devel] [PATCH pve-common v1 23/23] sectionconfig: correct example in docstring of `updateSchema()`
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
` (21 preceding siblings ...)
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 22/23] sectionconfig: note that `createSchema()` is universal for all plugins Max R. Carrara
@ 2025-12-19 19:44 ` Max R. Carrara
22 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2025-12-19 19:44 UTC (permalink / raw)
To: pve-devel
... and also reword / format the description for the `$single_class`
parameter somewhat.
Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
---
src/PVE/SectionConfig.pm | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/PVE/SectionConfig.pm b/src/PVE/SectionConfig.pm
index 7c27276..77ab156 100644
--- a/src/PVE/SectionConfig.pm
+++ b/src/PVE/SectionConfig.pm
@@ -576,8 +576,8 @@ sub createSchema {
=head3 $plugin->updateSchema([ $single_class, $base ])
- $update_schema = PVE::Example::Plugin->update_schema($single_class, $base)
- $update_schema = $class->updateSchema($single_class, $base)
+ $update_schema = PVE::Example::Plugin->updateSchema($single_class, $base)
+ $update_schema = $plugin->updateSchema($single_class, $base)
Returns the C<L<PVE::JSONSchema>> used for I<updating> config entries of a
I<child plugin>.
@@ -590,9 +590,9 @@ parameters of an API handler (C<PUT>).
=item C<$single_class> (optional)
Can be set to C<1> to only include properties which are defined in the returned
-hash of C<L<< options()|/$plugin->options() >>> of the plugin C<$class>.
+hash of C<L<< options()|/$plugin->options() >>> of the C<$plugin> instance.
-This parameter is only valid for child plugins, not the base plugin.
+B<NOTE:> This parameter is only valid for child plugins, not the base plugin.
=item C<$base> (optional)
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 24+ messages in thread
end of thread, other threads:[~2025-12-19 19:47 UTC | newest]
Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-12-19 19:44 [pve-devel] [PATCH pve-common v1 00/23] Document PVE::SectionConfig Peculiarities Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 01/23] sectionconfig: remove unused variable in get_property_schema() Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 02/23] tests: sectionconfig: add comparison test structure Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 03/23] tests: sectionconfig: add test case for fixed props in updateSchema Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 04/23] tests: sectionconfig: add case for unused properties Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 05/23] tests: sectionconfig: add case for unused optional default properties Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 06/23] tests: sectionconfig: add case for opt. default prop being req. once Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 07/23] tests: sectionconfig: add case for opt. default prop requirde by all Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 08/23] tests: sectionconfig: add case for unused required default properties Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 09/23] tests: sectionconfig: add case for req. default prop being req. once Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 10/23] tests: sectionconfig: add case for required default prop req. by all Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 11/23] tests: sectionconfig: add case for required default props opt. for all Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 12/23] tests: sectionconfig: add isolated mode test structure Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 13/23] tests: sectionconfig: add case for an ident. prop on two plugins Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 14/23] tests: sectionconfig: add case for same prop. w/ different optionality Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 15/23] tests: sectionconfig: add case for differing opt. default prop uses Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 16/23] tests: sectionconfig: add case for differing req. " Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 17/23] sectionconfig: correct docs regarding global props in unified mode Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 18/23] sectionconfig: reword docs regarding property usage in isolated mode Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 19/23] sectionconfig: extend / correct docstring of `private()` method Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 20/23] sectionconfig: note that a prop must be defined through a JSONSchema Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 21/23] sectionconfig: note that props added by plugins are always optional Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 22/23] sectionconfig: note that `createSchema()` is universal for all plugins Max R. Carrara
2025-12-19 19:44 ` [pve-devel] [PATCH pve-common v1 23/23] sectionconfig: correct example in docstring of `updateSchema()` Max R. Carrara
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox