From 8a07163550959b5866d7245b119c3625853a33a2 Mon Sep 17 00:00:00 2001 From: Steven Presti Date: Wed, 11 Feb 2026 15:19:42 -0500 Subject: [PATCH 1/8] config: rename v3_6_experimental to v3_6 --- config/{v3_6_experimental => v3_6}/config.go | 6 +++--- config/{v3_6_experimental => v3_6}/config_test.go | 4 ++-- config/{v3_6_experimental => v3_6}/schema/ignition.json | 0 config/{v3_6_experimental => v3_6}/translate/translate.go | 2 +- .../{v3_6_experimental => v3_6}/translate/translate_test.go | 0 config/{v3_6_experimental => v3_6}/types/cex.go | 0 config/{v3_6_experimental => v3_6}/types/cex_test.go | 0 config/{v3_6_experimental => v3_6}/types/clevis.go | 0 config/{v3_6_experimental => v3_6}/types/clevis_test.go | 0 config/{v3_6_experimental => v3_6}/types/config.go | 0 config/{v3_6_experimental => v3_6}/types/config_test.go | 0 config/{v3_6_experimental => v3_6}/types/device.go | 0 config/{v3_6_experimental => v3_6}/types/directory.go | 0 config/{v3_6_experimental => v3_6}/types/disk.go | 0 config/{v3_6_experimental => v3_6}/types/file.go | 0 config/{v3_6_experimental => v3_6}/types/file_test.go | 0 config/{v3_6_experimental => v3_6}/types/filesystem.go | 0 config/{v3_6_experimental => v3_6}/types/filesystem_test.go | 0 config/{v3_6_experimental => v3_6}/types/headers.go | 0 config/{v3_6_experimental => v3_6}/types/headers_test.go | 0 config/{v3_6_experimental => v3_6}/types/ignition.go | 0 config/{v3_6_experimental => v3_6}/types/ignition_test.go | 0 config/{v3_6_experimental => v3_6}/types/kargs.go | 0 config/{v3_6_experimental => v3_6}/types/kargs_test.go | 0 config/{v3_6_experimental => v3_6}/types/luks.go | 0 config/{v3_6_experimental => v3_6}/types/mode.go | 0 config/{v3_6_experimental => v3_6}/types/mode_test.go | 0 config/{v3_6_experimental => v3_6}/types/node.go | 0 config/{v3_6_experimental => v3_6}/types/node_test.go | 0 config/{v3_6_experimental => v3_6}/types/partition.go | 0 config/{v3_6_experimental => v3_6}/types/partition_test.go | 0 config/{v3_6_experimental => v3_6}/types/passwd.go | 0 config/{v3_6_experimental => v3_6}/types/path.go | 0 config/{v3_6_experimental => v3_6}/types/path_test.go | 0 config/{v3_6_experimental => v3_6}/types/proxy.go | 0 config/{v3_6_experimental => v3_6}/types/proxy_test.go | 0 config/{v3_6_experimental => v3_6}/types/raid.go | 0 config/{v3_6_experimental => v3_6}/types/raid_test.go | 0 config/{v3_6_experimental => v3_6}/types/resource.go | 0 config/{v3_6_experimental => v3_6}/types/schema.go | 2 +- config/{v3_6_experimental => v3_6}/types/storage.go | 0 config/{v3_6_experimental => v3_6}/types/storage_test.go | 0 config/{v3_6_experimental => v3_6}/types/systemd.go | 0 config/{v3_6_experimental => v3_6}/types/systemd_test.go | 0 config/{v3_6_experimental => v3_6}/types/tang.go | 0 config/{v3_6_experimental => v3_6}/types/tang_test.go | 0 config/{v3_6_experimental => v3_6}/types/tls.go | 0 config/{v3_6_experimental => v3_6}/types/tls_test.go | 0 config/{v3_6_experimental => v3_6}/types/unit.go | 0 config/{v3_6_experimental => v3_6}/types/unit_test.go | 0 config/{v3_6_experimental => v3_6}/types/url.go | 0 config/{v3_6_experimental => v3_6}/types/url_test.go | 0 config/{v3_6_experimental => v3_6}/types/verification.go | 0 .../{v3_6_experimental => v3_6}/types/verification_test.go | 0 54 files changed, 7 insertions(+), 7 deletions(-) rename config/{v3_6_experimental => v3_6}/config.go (93%) rename config/{v3_6_experimental => v3_6}/config_test.go (98%) rename config/{v3_6_experimental => v3_6}/schema/ignition.json (100%) rename config/{v3_6_experimental => v3_6}/translate/translate.go (97%) rename config/{v3_6_experimental => v3_6}/translate/translate_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/cex.go (100%) rename config/{v3_6_experimental => v3_6}/types/cex_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/clevis.go (100%) rename config/{v3_6_experimental => v3_6}/types/clevis_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/config.go (100%) rename config/{v3_6_experimental => v3_6}/types/config_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/device.go (100%) rename config/{v3_6_experimental => v3_6}/types/directory.go (100%) rename config/{v3_6_experimental => v3_6}/types/disk.go (100%) rename config/{v3_6_experimental => v3_6}/types/file.go (100%) rename config/{v3_6_experimental => v3_6}/types/file_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/filesystem.go (100%) rename config/{v3_6_experimental => v3_6}/types/filesystem_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/headers.go (100%) rename config/{v3_6_experimental => v3_6}/types/headers_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/ignition.go (100%) rename config/{v3_6_experimental => v3_6}/types/ignition_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/kargs.go (100%) rename config/{v3_6_experimental => v3_6}/types/kargs_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/luks.go (100%) rename config/{v3_6_experimental => v3_6}/types/mode.go (100%) rename config/{v3_6_experimental => v3_6}/types/mode_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/node.go (100%) rename config/{v3_6_experimental => v3_6}/types/node_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/partition.go (100%) rename config/{v3_6_experimental => v3_6}/types/partition_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/passwd.go (100%) rename config/{v3_6_experimental => v3_6}/types/path.go (100%) rename config/{v3_6_experimental => v3_6}/types/path_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/proxy.go (100%) rename config/{v3_6_experimental => v3_6}/types/proxy_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/raid.go (100%) rename config/{v3_6_experimental => v3_6}/types/raid_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/resource.go (100%) rename config/{v3_6_experimental => v3_6}/types/schema.go (97%) rename config/{v3_6_experimental => v3_6}/types/storage.go (100%) rename config/{v3_6_experimental => v3_6}/types/storage_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/systemd.go (100%) rename config/{v3_6_experimental => v3_6}/types/systemd_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/tang.go (100%) rename config/{v3_6_experimental => v3_6}/types/tang_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/tls.go (100%) rename config/{v3_6_experimental => v3_6}/types/tls_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/unit.go (100%) rename config/{v3_6_experimental => v3_6}/types/unit_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/url.go (100%) rename config/{v3_6_experimental => v3_6}/types/url_test.go (100%) rename config/{v3_6_experimental => v3_6}/types/verification.go (100%) rename config/{v3_6_experimental => v3_6}/types/verification_test.go (100%) diff --git a/config/v3_6_experimental/config.go b/config/v3_6/config.go similarity index 93% rename from config/v3_6_experimental/config.go rename to config/v3_6/config.go index e67b08bab..2542df78b 100644 --- a/config/v3_6_experimental/config.go +++ b/config/v3_6/config.go @@ -12,15 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -package v3_6_experimental +package v3_6 import ( "github.com/coreos/ignition/v2/config/merge" "github.com/coreos/ignition/v2/config/shared/errors" "github.com/coreos/ignition/v2/config/util" prev "github.com/coreos/ignition/v2/config/v3_5" - "github.com/coreos/ignition/v2/config/v3_6_experimental/translate" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_6/translate" + "github.com/coreos/ignition/v2/config/v3_6/types" "github.com/coreos/ignition/v2/config/validate" "github.com/coreos/go-semver/semver" diff --git a/config/v3_6_experimental/config_test.go b/config/v3_6/config_test.go similarity index 98% rename from config/v3_6_experimental/config_test.go rename to config/v3_6/config_test.go index 8ba561631..7d864e882 100644 --- a/config/v3_6_experimental/config_test.go +++ b/config/v3_6/config_test.go @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -package v3_6_experimental +package v3_6 import ( "testing" "github.com/coreos/ignition/v2/config/shared/errors" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_6/types" "github.com/stretchr/testify/assert" ) diff --git a/config/v3_6_experimental/schema/ignition.json b/config/v3_6/schema/ignition.json similarity index 100% rename from config/v3_6_experimental/schema/ignition.json rename to config/v3_6/schema/ignition.json diff --git a/config/v3_6_experimental/translate/translate.go b/config/v3_6/translate/translate.go similarity index 97% rename from config/v3_6_experimental/translate/translate.go rename to config/v3_6/translate/translate.go index 8fa256740..ae16eef36 100644 --- a/config/v3_6_experimental/translate/translate.go +++ b/config/v3_6/translate/translate.go @@ -18,7 +18,7 @@ import ( "github.com/coreos/ignition/v2/config/translate" "github.com/coreos/ignition/v2/config/util" old_types "github.com/coreos/ignition/v2/config/v3_5/types" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_6/types" ) func translateFileEmbedded1(old old_types.FileEmbedded1) (ret types.FileEmbedded1) { diff --git a/config/v3_6_experimental/translate/translate_test.go b/config/v3_6/translate/translate_test.go similarity index 100% rename from config/v3_6_experimental/translate/translate_test.go rename to config/v3_6/translate/translate_test.go diff --git a/config/v3_6_experimental/types/cex.go b/config/v3_6/types/cex.go similarity index 100% rename from config/v3_6_experimental/types/cex.go rename to config/v3_6/types/cex.go diff --git a/config/v3_6_experimental/types/cex_test.go b/config/v3_6/types/cex_test.go similarity index 100% rename from config/v3_6_experimental/types/cex_test.go rename to config/v3_6/types/cex_test.go diff --git a/config/v3_6_experimental/types/clevis.go b/config/v3_6/types/clevis.go similarity index 100% rename from config/v3_6_experimental/types/clevis.go rename to config/v3_6/types/clevis.go diff --git a/config/v3_6_experimental/types/clevis_test.go b/config/v3_6/types/clevis_test.go similarity index 100% rename from config/v3_6_experimental/types/clevis_test.go rename to config/v3_6/types/clevis_test.go diff --git a/config/v3_6_experimental/types/config.go b/config/v3_6/types/config.go similarity index 100% rename from config/v3_6_experimental/types/config.go rename to config/v3_6/types/config.go diff --git a/config/v3_6_experimental/types/config_test.go b/config/v3_6/types/config_test.go similarity index 100% rename from config/v3_6_experimental/types/config_test.go rename to config/v3_6/types/config_test.go diff --git a/config/v3_6_experimental/types/device.go b/config/v3_6/types/device.go similarity index 100% rename from config/v3_6_experimental/types/device.go rename to config/v3_6/types/device.go diff --git a/config/v3_6_experimental/types/directory.go b/config/v3_6/types/directory.go similarity index 100% rename from config/v3_6_experimental/types/directory.go rename to config/v3_6/types/directory.go diff --git a/config/v3_6_experimental/types/disk.go b/config/v3_6/types/disk.go similarity index 100% rename from config/v3_6_experimental/types/disk.go rename to config/v3_6/types/disk.go diff --git a/config/v3_6_experimental/types/file.go b/config/v3_6/types/file.go similarity index 100% rename from config/v3_6_experimental/types/file.go rename to config/v3_6/types/file.go diff --git a/config/v3_6_experimental/types/file_test.go b/config/v3_6/types/file_test.go similarity index 100% rename from config/v3_6_experimental/types/file_test.go rename to config/v3_6/types/file_test.go diff --git a/config/v3_6_experimental/types/filesystem.go b/config/v3_6/types/filesystem.go similarity index 100% rename from config/v3_6_experimental/types/filesystem.go rename to config/v3_6/types/filesystem.go diff --git a/config/v3_6_experimental/types/filesystem_test.go b/config/v3_6/types/filesystem_test.go similarity index 100% rename from config/v3_6_experimental/types/filesystem_test.go rename to config/v3_6/types/filesystem_test.go diff --git a/config/v3_6_experimental/types/headers.go b/config/v3_6/types/headers.go similarity index 100% rename from config/v3_6_experimental/types/headers.go rename to config/v3_6/types/headers.go diff --git a/config/v3_6_experimental/types/headers_test.go b/config/v3_6/types/headers_test.go similarity index 100% rename from config/v3_6_experimental/types/headers_test.go rename to config/v3_6/types/headers_test.go diff --git a/config/v3_6_experimental/types/ignition.go b/config/v3_6/types/ignition.go similarity index 100% rename from config/v3_6_experimental/types/ignition.go rename to config/v3_6/types/ignition.go diff --git a/config/v3_6_experimental/types/ignition_test.go b/config/v3_6/types/ignition_test.go similarity index 100% rename from config/v3_6_experimental/types/ignition_test.go rename to config/v3_6/types/ignition_test.go diff --git a/config/v3_6_experimental/types/kargs.go b/config/v3_6/types/kargs.go similarity index 100% rename from config/v3_6_experimental/types/kargs.go rename to config/v3_6/types/kargs.go diff --git a/config/v3_6_experimental/types/kargs_test.go b/config/v3_6/types/kargs_test.go similarity index 100% rename from config/v3_6_experimental/types/kargs_test.go rename to config/v3_6/types/kargs_test.go diff --git a/config/v3_6_experimental/types/luks.go b/config/v3_6/types/luks.go similarity index 100% rename from config/v3_6_experimental/types/luks.go rename to config/v3_6/types/luks.go diff --git a/config/v3_6_experimental/types/mode.go b/config/v3_6/types/mode.go similarity index 100% rename from config/v3_6_experimental/types/mode.go rename to config/v3_6/types/mode.go diff --git a/config/v3_6_experimental/types/mode_test.go b/config/v3_6/types/mode_test.go similarity index 100% rename from config/v3_6_experimental/types/mode_test.go rename to config/v3_6/types/mode_test.go diff --git a/config/v3_6_experimental/types/node.go b/config/v3_6/types/node.go similarity index 100% rename from config/v3_6_experimental/types/node.go rename to config/v3_6/types/node.go diff --git a/config/v3_6_experimental/types/node_test.go b/config/v3_6/types/node_test.go similarity index 100% rename from config/v3_6_experimental/types/node_test.go rename to config/v3_6/types/node_test.go diff --git a/config/v3_6_experimental/types/partition.go b/config/v3_6/types/partition.go similarity index 100% rename from config/v3_6_experimental/types/partition.go rename to config/v3_6/types/partition.go diff --git a/config/v3_6_experimental/types/partition_test.go b/config/v3_6/types/partition_test.go similarity index 100% rename from config/v3_6_experimental/types/partition_test.go rename to config/v3_6/types/partition_test.go diff --git a/config/v3_6_experimental/types/passwd.go b/config/v3_6/types/passwd.go similarity index 100% rename from config/v3_6_experimental/types/passwd.go rename to config/v3_6/types/passwd.go diff --git a/config/v3_6_experimental/types/path.go b/config/v3_6/types/path.go similarity index 100% rename from config/v3_6_experimental/types/path.go rename to config/v3_6/types/path.go diff --git a/config/v3_6_experimental/types/path_test.go b/config/v3_6/types/path_test.go similarity index 100% rename from config/v3_6_experimental/types/path_test.go rename to config/v3_6/types/path_test.go diff --git a/config/v3_6_experimental/types/proxy.go b/config/v3_6/types/proxy.go similarity index 100% rename from config/v3_6_experimental/types/proxy.go rename to config/v3_6/types/proxy.go diff --git a/config/v3_6_experimental/types/proxy_test.go b/config/v3_6/types/proxy_test.go similarity index 100% rename from config/v3_6_experimental/types/proxy_test.go rename to config/v3_6/types/proxy_test.go diff --git a/config/v3_6_experimental/types/raid.go b/config/v3_6/types/raid.go similarity index 100% rename from config/v3_6_experimental/types/raid.go rename to config/v3_6/types/raid.go diff --git a/config/v3_6_experimental/types/raid_test.go b/config/v3_6/types/raid_test.go similarity index 100% rename from config/v3_6_experimental/types/raid_test.go rename to config/v3_6/types/raid_test.go diff --git a/config/v3_6_experimental/types/resource.go b/config/v3_6/types/resource.go similarity index 100% rename from config/v3_6_experimental/types/resource.go rename to config/v3_6/types/resource.go diff --git a/config/v3_6_experimental/types/schema.go b/config/v3_6/types/schema.go similarity index 97% rename from config/v3_6_experimental/types/schema.go rename to config/v3_6/types/schema.go index c652d666d..9bc976679 100644 --- a/config/v3_6_experimental/types/schema.go +++ b/config/v3_6/types/schema.go @@ -1,6 +1,6 @@ package types -// generated by "schematyper --package=types config/v3_6_experimental/schema/ignition.json -o config/v3_6_experimental/types/schema.go --root-type=Config" -- DO NOT EDIT +// generated by "schematyper --package=types config/v3_6/schema/ignition.json -o config/v3_6/types/schema.go --root-type=Config" -- DO NOT EDIT type Cex struct { Enabled *bool `json:"enabled,omitempty"` diff --git a/config/v3_6_experimental/types/storage.go b/config/v3_6/types/storage.go similarity index 100% rename from config/v3_6_experimental/types/storage.go rename to config/v3_6/types/storage.go diff --git a/config/v3_6_experimental/types/storage_test.go b/config/v3_6/types/storage_test.go similarity index 100% rename from config/v3_6_experimental/types/storage_test.go rename to config/v3_6/types/storage_test.go diff --git a/config/v3_6_experimental/types/systemd.go b/config/v3_6/types/systemd.go similarity index 100% rename from config/v3_6_experimental/types/systemd.go rename to config/v3_6/types/systemd.go diff --git a/config/v3_6_experimental/types/systemd_test.go b/config/v3_6/types/systemd_test.go similarity index 100% rename from config/v3_6_experimental/types/systemd_test.go rename to config/v3_6/types/systemd_test.go diff --git a/config/v3_6_experimental/types/tang.go b/config/v3_6/types/tang.go similarity index 100% rename from config/v3_6_experimental/types/tang.go rename to config/v3_6/types/tang.go diff --git a/config/v3_6_experimental/types/tang_test.go b/config/v3_6/types/tang_test.go similarity index 100% rename from config/v3_6_experimental/types/tang_test.go rename to config/v3_6/types/tang_test.go diff --git a/config/v3_6_experimental/types/tls.go b/config/v3_6/types/tls.go similarity index 100% rename from config/v3_6_experimental/types/tls.go rename to config/v3_6/types/tls.go diff --git a/config/v3_6_experimental/types/tls_test.go b/config/v3_6/types/tls_test.go similarity index 100% rename from config/v3_6_experimental/types/tls_test.go rename to config/v3_6/types/tls_test.go diff --git a/config/v3_6_experimental/types/unit.go b/config/v3_6/types/unit.go similarity index 100% rename from config/v3_6_experimental/types/unit.go rename to config/v3_6/types/unit.go diff --git a/config/v3_6_experimental/types/unit_test.go b/config/v3_6/types/unit_test.go similarity index 100% rename from config/v3_6_experimental/types/unit_test.go rename to config/v3_6/types/unit_test.go diff --git a/config/v3_6_experimental/types/url.go b/config/v3_6/types/url.go similarity index 100% rename from config/v3_6_experimental/types/url.go rename to config/v3_6/types/url.go diff --git a/config/v3_6_experimental/types/url_test.go b/config/v3_6/types/url_test.go similarity index 100% rename from config/v3_6_experimental/types/url_test.go rename to config/v3_6/types/url_test.go diff --git a/config/v3_6_experimental/types/verification.go b/config/v3_6/types/verification.go similarity index 100% rename from config/v3_6_experimental/types/verification.go rename to config/v3_6/types/verification.go diff --git a/config/v3_6_experimental/types/verification_test.go b/config/v3_6/types/verification_test.go similarity index 100% rename from config/v3_6_experimental/types/verification_test.go rename to config/v3_6/types/verification_test.go From 57d4d86e8e8ed59648a95cd9c3d40576ddca058c Mon Sep 17 00:00:00 2001 From: Steven Presti Date: Wed, 11 Feb 2026 15:22:44 -0500 Subject: [PATCH 2/8] config/v3_6: stabilize --- config/v3_6/config.go | 4 ++-- config/v3_6/config_test.go | 12 ++++++++---- config/v3_6/types/config.go | 5 ++--- internal/resource/url.go | 2 +- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/config/v3_6/config.go b/config/v3_6/config.go index 2542df78b..3d4b1d7fb 100644 --- a/config/v3_6/config.go +++ b/config/v3_6/config.go @@ -58,8 +58,8 @@ func Parse(rawConfig []byte) (types.Config, report.Report, error) { return config, rpt, nil } -// ParseCompatibleVersion parses the raw config of version 3.5.0-experimental or -// lesser into a 3.5-exp types.Config struct and generates a report of any errors, +// ParseCompatibleVersion parses the raw config of version 3.6.0 or +// lesser into a 3.6 types.Config struct and generates a report of any errors, // warnings, info, and deprecations it encountered func ParseCompatibleVersion(raw []byte) (types.Config, report.Report, error) { version, rpt, err := util.GetConfigVersion(raw) diff --git a/config/v3_6/config_test.go b/config/v3_6/config_test.go index 7d864e882..fe61553fb 100644 --- a/config/v3_6/config_test.go +++ b/config/v3_6/config_test.go @@ -137,6 +137,10 @@ func TestParse(t *testing.T) { }, { in: in{config: []byte(`{"ignition": {"version": "3.6.0-experimental"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.6.0"}}`)}, out: out{config: types.Config{Ignition: types.Ignition{Version: types.MaxVersion.String()}}}, }, { @@ -156,7 +160,7 @@ func TestParse(t *testing.T) { out: out{err: errors.ErrEmpty}, }, { - in: in{config: []byte(`{"ignition": {"version": "3.6.0-experimental"}, "storage": {"filesystems": [{"format": "ext4", "label": "zzzzzzzzzzzzzzzzzzzzzzzzzzz"}]}}`)}, + in: in{config: []byte(`{"ignition": {"version": "3.6.0"}, "storage": {"filesystems": [{"format": "ext4", "label": "zzzzzzzzzzzzzzzzzzzzzzzzzzz"}]}}`)}, out: out{err: errors.ErrInvalid}, }, } @@ -191,11 +195,11 @@ func TestParse(t *testing.T) { }, { in: in{config: []byte(`{"ignition": {"version": "3.6.0-experimental"}}`)}, - out: out{config: types.Config{Ignition: types.Ignition{Version: types.MaxVersion.String()}}}, + out: out{err: errors.ErrUnknownVersion}, }, { in: in{config: []byte(`{"ignition": {"version": "3.6.0"}}`)}, - out: out{err: errors.ErrUnknownVersion}, + out: out{config: types.Config{Ignition: types.Ignition{Version: types.MaxVersion.String()}}}, }, { in: in{config: []byte(`{"ignition": {"version": "3.7.0"}}`)}, @@ -210,7 +214,7 @@ func TestParse(t *testing.T) { out: out{err: errors.ErrInvalid}, }, { - in: in{config: []byte(`{"ignition": {"version": "3.6.0-experimental"}, "storage": {"filesystems": [{"format": "ext4", "label": "zzzzzzzzzzzzzzzzzzzzzzzzzzz"}]}}`)}, + in: in{config: []byte(`{"ignition": {"version": "3.6.0"}, "storage": {"filesystems": [{"format": "ext4", "label": "zzzzzzzzzzzzzzzzzzzzzzzzzzz"}]}}`)}, out: out{err: errors.ErrInvalid}, }, } diff --git a/config/v3_6/types/config.go b/config/v3_6/types/config.go index 9428b0bb2..02ef7b4ae 100644 --- a/config/v3_6/types/config.go +++ b/config/v3_6/types/config.go @@ -25,9 +25,8 @@ import ( var ( MaxVersion = semver.Version{ - Major: 3, - Minor: 6, - PreRelease: "experimental", + Major: 3, + Minor: 6, } ) diff --git a/internal/resource/url.go b/internal/resource/url.go index 61f57a2c3..ce0abe2cd 100644 --- a/internal/resource/url.go +++ b/internal/resource/url.go @@ -72,7 +72,7 @@ var ( // config is being fetched configHeaders = http.Header{ "Accept-Encoding": []string{"identity"}, - "Accept": []string{"application/vnd.coreos.ignition+json;version=3.5.0, */*;q=0.1"}, + "Accept": []string{"application/vnd.coreos.ignition+json;version=3.6.0, */*;q=0.1"}, } // We could derive this info from aws-sdk-go/aws/endpoints/defaults.go, From 21d5140791bc2a0f2d037a325b3b6d5658c4cc5e Mon Sep 17 00:00:00 2001 From: Steven Presti Date: Wed, 11 Feb 2026 15:23:20 -0500 Subject: [PATCH 3/8] config: copy v3_6 to v3_7_experimental --- config/v3_7_experimental/config.go | 78 ++ config/v3_7_experimental/config_test.go | 242 +++++++ config/v3_7_experimental/schema/ignition.json | 685 ++++++++++++++++++ .../v3_7_experimental/translate/translate.go | 68 ++ .../translate/translate_test.go | 32 + config/v3_7_experimental/types/cex.go | 33 + config/v3_7_experimental/types/cex_test.go | 53 ++ config/v3_7_experimental/types/clevis.go | 43 ++ config/v3_7_experimental/types/clevis_test.go | 78 ++ config/v3_7_experimental/types/config.go | 64 ++ config/v3_7_experimental/types/config_test.go | 260 +++++++ config/v3_7_experimental/types/device.go | 25 + config/v3_7_experimental/types/directory.go | 26 + config/v3_7_experimental/types/disk.go | 135 ++++ config/v3_7_experimental/types/file.go | 43 ++ config/v3_7_experimental/types/file_test.go | 117 +++ config/v3_7_experimental/types/filesystem.go | 106 +++ .../types/filesystem_test.go | 194 +++++ config/v3_7_experimental/types/headers.go | 65 ++ .../v3_7_experimental/types/headers_test.go | 159 ++++ config/v3_7_experimental/types/ignition.go | 49 ++ .../v3_7_experimental/types/ignition_test.go | 48 ++ config/v3_7_experimental/types/kargs.go | 22 + config/v3_7_experimental/types/kargs_test.go | 51 ++ config/v3_7_experimental/types/luks.go | 82 +++ config/v3_7_experimental/types/mode.go | 26 + config/v3_7_experimental/types/mode_test.go | 62 ++ config/v3_7_experimental/types/node.go | 59 ++ config/v3_7_experimental/types/node_test.go | 101 +++ config/v3_7_experimental/types/partition.go | 91 +++ .../v3_7_experimental/types/partition_test.go | 94 +++ config/v3_7_experimental/types/passwd.go | 23 + config/v3_7_experimental/types/path.go | 42 ++ config/v3_7_experimental/types/path_test.go | 57 ++ config/v3_7_experimental/types/proxy.go | 49 ++ config/v3_7_experimental/types/proxy_test.go | 112 +++ config/v3_7_experimental/types/raid.go | 62 ++ config/v3_7_experimental/types/raid_test.go | 88 +++ config/v3_7_experimental/types/resource.go | 91 +++ config/v3_7_experimental/types/schema.go | 264 +++++++ config/v3_7_experimental/types/storage.go | 115 +++ .../v3_7_experimental/types/storage_test.go | 359 +++++++++ config/v3_7_experimental/types/systemd.go | 61 ++ .../v3_7_experimental/types/systemd_test.go | 105 +++ config/v3_7_experimental/types/tang.go | 65 ++ config/v3_7_experimental/types/tang_test.go | 104 +++ config/v3_7_experimental/types/tls.go | 27 + config/v3_7_experimental/types/tls_test.go | 42 ++ config/v3_7_experimental/types/unit.go | 68 ++ config/v3_7_experimental/types/unit_test.go | 108 +++ config/v3_7_experimental/types/url.go | 83 +++ config/v3_7_experimental/types/url_test.go | 137 ++++ .../v3_7_experimental/types/verification.go | 71 ++ .../types/verification_test.go | 102 +++ 54 files changed, 5426 insertions(+) create mode 100644 config/v3_7_experimental/config.go create mode 100644 config/v3_7_experimental/config_test.go create mode 100644 config/v3_7_experimental/schema/ignition.json create mode 100644 config/v3_7_experimental/translate/translate.go create mode 100644 config/v3_7_experimental/translate/translate_test.go create mode 100644 config/v3_7_experimental/types/cex.go create mode 100644 config/v3_7_experimental/types/cex_test.go create mode 100644 config/v3_7_experimental/types/clevis.go create mode 100644 config/v3_7_experimental/types/clevis_test.go create mode 100644 config/v3_7_experimental/types/config.go create mode 100644 config/v3_7_experimental/types/config_test.go create mode 100644 config/v3_7_experimental/types/device.go create mode 100644 config/v3_7_experimental/types/directory.go create mode 100644 config/v3_7_experimental/types/disk.go create mode 100644 config/v3_7_experimental/types/file.go create mode 100644 config/v3_7_experimental/types/file_test.go create mode 100644 config/v3_7_experimental/types/filesystem.go create mode 100644 config/v3_7_experimental/types/filesystem_test.go create mode 100644 config/v3_7_experimental/types/headers.go create mode 100644 config/v3_7_experimental/types/headers_test.go create mode 100644 config/v3_7_experimental/types/ignition.go create mode 100644 config/v3_7_experimental/types/ignition_test.go create mode 100644 config/v3_7_experimental/types/kargs.go create mode 100644 config/v3_7_experimental/types/kargs_test.go create mode 100644 config/v3_7_experimental/types/luks.go create mode 100644 config/v3_7_experimental/types/mode.go create mode 100644 config/v3_7_experimental/types/mode_test.go create mode 100644 config/v3_7_experimental/types/node.go create mode 100644 config/v3_7_experimental/types/node_test.go create mode 100644 config/v3_7_experimental/types/partition.go create mode 100644 config/v3_7_experimental/types/partition_test.go create mode 100644 config/v3_7_experimental/types/passwd.go create mode 100644 config/v3_7_experimental/types/path.go create mode 100644 config/v3_7_experimental/types/path_test.go create mode 100644 config/v3_7_experimental/types/proxy.go create mode 100644 config/v3_7_experimental/types/proxy_test.go create mode 100644 config/v3_7_experimental/types/raid.go create mode 100644 config/v3_7_experimental/types/raid_test.go create mode 100644 config/v3_7_experimental/types/resource.go create mode 100644 config/v3_7_experimental/types/schema.go create mode 100644 config/v3_7_experimental/types/storage.go create mode 100644 config/v3_7_experimental/types/storage_test.go create mode 100644 config/v3_7_experimental/types/systemd.go create mode 100644 config/v3_7_experimental/types/systemd_test.go create mode 100644 config/v3_7_experimental/types/tang.go create mode 100644 config/v3_7_experimental/types/tang_test.go create mode 100644 config/v3_7_experimental/types/tls.go create mode 100644 config/v3_7_experimental/types/tls_test.go create mode 100644 config/v3_7_experimental/types/unit.go create mode 100644 config/v3_7_experimental/types/unit_test.go create mode 100644 config/v3_7_experimental/types/url.go create mode 100644 config/v3_7_experimental/types/url_test.go create mode 100644 config/v3_7_experimental/types/verification.go create mode 100644 config/v3_7_experimental/types/verification_test.go diff --git a/config/v3_7_experimental/config.go b/config/v3_7_experimental/config.go new file mode 100644 index 000000000..a41e4a4da --- /dev/null +++ b/config/v3_7_experimental/config.go @@ -0,0 +1,78 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v3_7_experimental + +import ( + "github.com/coreos/ignition/v2/config/merge" + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + prev "github.com/coreos/ignition/v2/config/v3_5" + "github.com/coreos/ignition/v2/config/v3_7_experimental/translate" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" + "github.com/coreos/ignition/v2/config/validate" + + "github.com/coreos/go-semver/semver" + "github.com/coreos/vcontext/report" +) + +func Merge(parent, child types.Config) types.Config { + res, _ := merge.MergeStructTranscribe(parent, child) + return res.(types.Config) +} + +// Parse parses the raw config into a types.Config struct and generates a report of any +// errors, warnings, info, and deprecations it encountered +func Parse(rawConfig []byte) (types.Config, report.Report, error) { + if len(rawConfig) == 0 { + return types.Config{}, report.Report{}, errors.ErrEmpty + } + + var config types.Config + if rpt, err := util.HandleParseErrors(rawConfig, &config); err != nil { + return types.Config{}, rpt, err + } + + version, err := semver.NewVersion(config.Ignition.Version) + + if err != nil || *version != types.MaxVersion { + return types.Config{}, report.Report{}, errors.ErrUnknownVersion + } + + rpt := validate.ValidateWithContext(config, rawConfig) + if rpt.IsFatal() { + return types.Config{}, rpt, errors.ErrInvalid + } + + return config, rpt, nil +} + +// ParseCompatibleVersion parses the raw config of version 3.6.0 or +// lesser into a 3.6 types.Config struct and generates a report of any errors, +// warnings, info, and deprecations it encountered +func ParseCompatibleVersion(raw []byte) (types.Config, report.Report, error) { + version, rpt, err := util.GetConfigVersion(raw) + if err != nil { + return types.Config{}, rpt, err + } + + if version == types.MaxVersion { + return Parse(raw) + } + prevCfg, r, err := prev.ParseCompatibleVersion(raw) + if err != nil { + return types.Config{}, r, err + } + return translate.Translate(prevCfg), r, nil +} diff --git a/config/v3_7_experimental/config_test.go b/config/v3_7_experimental/config_test.go new file mode 100644 index 000000000..fc44b9a61 --- /dev/null +++ b/config/v3_7_experimental/config_test.go @@ -0,0 +1,242 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v3_7_experimental + +import ( + "testing" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" + "github.com/stretchr/testify/assert" +) + +func TestParse(t *testing.T) { + type in struct { + config []byte + } + type out struct { + config types.Config + err error + } + + tests := []struct { + in in + out out + }{ + { + in: in{config: []byte(`{"ignitionVersion": 1}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "1.0.0"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "2.0.0"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "2.1.0"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "2.2.0"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "2.3.0"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "2.4.0"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.0.0"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.1.0"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.2.0"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.3.0"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.4.0"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.5.0"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "2.0.0-experimental"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "2.1.0-experimental"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "2.2.0-experimental"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "2.3.0-experimental"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "2.4.0-experimental"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "2.5.0-experimental"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.0.0-experimental"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.1.0-experimental"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.2.0-experimental"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.3.0-experimental"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.4.0-experimental"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.5.0-experimental"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.6.0-experimental"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.6.0"}}`)}, + out: out{config: types.Config{Ignition: types.Ignition{Version: types.MaxVersion.String()}}}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "2.0.0"},}`)}, + out: out{err: errors.ErrInvalid}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "invalid.semver"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte{}}, + out: out{err: errors.ErrEmpty}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.6.0-experimental"}, "storage": {"filesystems": [{"format": "ext4", "label": "zzzzzzzzzzzzzzzzzzzzzzzzzzz"}]}}`)}, + out: out{err: errors.ErrInvalid}, + }, + } + + testsCompt := []struct { + in in + out out + }{ + { + in: in{config: []byte(`{"ignition": {"version": "3.0.0"}}`)}, + out: out{config: types.Config{Ignition: types.Ignition{Version: types.MaxVersion.String()}}}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.1.0"}}`)}, + out: out{config: types.Config{Ignition: types.Ignition{Version: types.MaxVersion.String()}}}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.2.0"}}`)}, + out: out{config: types.Config{Ignition: types.Ignition{Version: types.MaxVersion.String()}}}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.3.0"}}`)}, + out: out{config: types.Config{Ignition: types.Ignition{Version: types.MaxVersion.String()}}}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.4.0"}}`)}, + out: out{config: types.Config{Ignition: types.Ignition{Version: types.MaxVersion.String()}}}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.5.0"}}`)}, + out: out{config: types.Config{Ignition: types.Ignition{Version: types.MaxVersion.String()}}}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.6.0-experimental"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.6.0"}}`)}, + out: out{config: types.Config{Ignition: types.Ignition{Version: types.MaxVersion.String()}}}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.7.0"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte{}}, + out: out{err: errors.ErrEmpty}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.0.0"}, "storage": {"filesystems": [{"format": "ext4", "label": "zzzzzzzzzzzzzzzzzzzzzzzzzzz"}]}}`)}, + out: out{err: errors.ErrInvalid}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.6.0-experimental"}, "storage": {"filesystems": [{"format": "ext4", "label": "zzzzzzzzzzzzzzzzzzzzzzzzzzz"}]}}`)}, + out: out{err: errors.ErrInvalid}, + }, + } + + for i, test := range tests { + config, report, err := Parse(test.in.config) + if test.out.err != err { + t.Errorf("#%d: bad error: want %v, got %v, report: %+v", i, test.out.err, err, report) + } + if test.out.err == errors.ErrInvalid && len(report.Entries) == 0 { + t.Errorf("#%d: expected report, got none", i) + } + assert.Equal(t, test.out.config, config, "#%d: bad config, report: %+v", i, report) + } + for i, test := range testsCompt { + config, report, err := ParseCompatibleVersion(test.in.config) + if test.out.err != err { + t.Errorf("#%d: bad error: want %v, got %v, report: %+v", i, test.out.err, err, report) + } + if test.out.err == errors.ErrInvalid && len(report.Entries) == 0 { + t.Errorf("#%d: expected report, got none", i) + } + assert.Equal(t, test.out.config, config, "#%d: bad config, report: %+v", i, report) + } +} diff --git a/config/v3_7_experimental/schema/ignition.json b/config/v3_7_experimental/schema/ignition.json new file mode 100644 index 000000000..344d06749 --- /dev/null +++ b/config/v3_7_experimental/schema/ignition.json @@ -0,0 +1,685 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "ignition", + "type": "object", + "properties": { + "ignition": { + "$ref": "#/definitions/ignition" + }, + "storage": { + "$ref": "#/definitions/storage" + }, + "systemd": { + "$ref": "#/definitions/systemd" + }, + "passwd": { + "$ref": "#/definitions/passwd" + }, + "kernelArguments": { + "$ref": "#/definitions/kernelArguments" + } + }, + "required": [ + "ignition" + ], + "definitions": { + "resource": { + "type": "object", + "properties": { + "source": { + "type": ["string", "null"] + }, + "compression": { + "type": ["string", "null"] + }, + "httpHeaders": { + "$ref": "#/definitions/httpHeaders" + }, + "verification": { + "$ref": "#/definitions/verification" + } + } + }, + "verification": { + "type": "object", + "properties": { + "hash": { "type": ["string", "null"] } + } + }, + "httpHeaders": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": ["string", "null"] + } + }, + "required": [ + "name" + ] + } + }, + "ignition": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "config": { + "$ref": "#/definitions/ignition/definitions/ignition-config" + }, + "timeouts": { + "$ref": "#/definitions/ignition/definitions/timeouts" + }, + "security": { + "$ref": "#/definitions/ignition/definitions/security" + }, + "proxy": { + "$ref": "#/definitions/ignition/definitions/proxy" + } + }, + "definitions": { + "ignition-config": { + "type": "object", + "properties": { + "merge": { + "type": "array", + "items": { + "$ref": "#/definitions/resource" + } + }, + "replace": { + "$ref": "#/definitions/resource" + } + } + }, + "security": { + "type": "object", + "properties": { + "tls": { + "type": "object", + "properties": { + "certificateAuthorities": { + "type": "array", + "items": { + "$ref": "#/definitions/resource" + } + } + } + } + } + }, + "proxy": { + "type": "object", + "properties": { + "httpProxy": { + "type": ["string", "null"] + }, + "httpsProxy": { + "type": ["string", "null"] + }, + "noProxy": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "timeouts": { + "type": "object", + "properties": { + "httpResponseHeaders": { + "type": ["integer", "null"] + }, + "httpTotal": { + "type": ["integer", "null"] + } + } + } + }, + "required": [ + "version" + ] + }, + "storage": { + "type": "object", + "properties": { + "disks": { + "type": "array", + "items": { + "$ref": "#/definitions/storage/definitions/disk" + } + }, + "raid": { + "type": "array", + "items": { + "$ref": "#/definitions/storage/definitions/raid" + } + }, + "luks": { + "type": "array", + "items": { + "$ref": "#/definitions/storage/definitions/luks" + } + }, + "filesystems": { + "type": "array", + "items": { + "$ref": "#/definitions/storage/definitions/filesystem" + } + }, + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/storage/definitions/file" + } + }, + "directories": { + "type": "array", + "items": { + "$ref": "#/definitions/storage/definitions/directory" + } + }, + "links": { + "type": "array", + "items": { + "$ref": "#/definitions/storage/definitions/link" + } + } + }, + "definitions": { + "disk": { + "type": "object", + "properties": { + "device": { + "type": "string" + }, + "wipeTable": { + "type": ["boolean", "null"] + }, + "partitions": { + "type": "array", + "items": { + "$ref": "#/definitions/storage/definitions/partition" + } + } + }, + "required": [ + "device" + ] + }, + "raid": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "level": { + "type": ["string", "null"] + }, + "spares": { + "type": ["integer", "null"] + }, + "devices": { + "type": "array", + "items": { + "type": "string" + } + }, + "options": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "name" + ] + }, + "luks": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "label": { + "type": ["string", "null"] + }, + "uuid": { + "type": ["string", "null"] + }, + "device": { + "type": ["string", "null"] + }, + "keyFile": { + "$ref": "#/definitions/resource" + }, + "wipeVolume": { + "type": ["boolean", "null"] + }, + "clevis": { + "$ref": "#/definitions/storage/definitions/clevis" + }, + "cex": { + "$ref": "#/definitions/storage/definitions/cex" + }, + "options": { + "type": "array", + "items": { + "type": "string" + } + }, + "discard": { + "type": ["boolean", "null"] + }, + "openOptions": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "name" + ] + }, + "clevis": { + "type": "object", + "properties": { + "custom": { + "$ref": "#/definitions/storage/definitions/clevisCustom" + }, + "tpm2": { + "type": ["boolean", "null"] + }, + "tang": { + "type": "array", + "items": { + "$ref": "#/definitions/storage/definitions/tang" + } + }, + "threshold": { + "type": ["integer", "null"] + } + } + }, + "clevisCustom": { + "type": "object", + "properties": { + "pin": { + "type": ["string", "null"] + }, + "config": { + "type": ["string", "null"] + }, + "needsNetwork": { + "type": ["boolean", "null"] + } + } + }, + "tang": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "thumbprint": { + "type": ["string", "null"] + }, + "advertisement": { + "type": ["string", "null"] + } + } + }, + "cex": { + "type": "object", + "properties": { + "enabled": { + "type": ["boolean", "null"] + } + } + }, + "filesystem": { + "type": "object", + "properties": { + "path": { + "type": ["string", "null"] + }, + "device": { + "type": "string" + }, + "format": { + "type": ["string", "null"] + }, + "options": { + "type": "array", + "items": { + "type": "string" + } + }, + "mountOptions": { + "type": "array", + "items": { + "type": "string" + } + }, + "wipeFilesystem": { + "type": ["boolean", "null"] + }, + "label": { + "type": ["string", "null"] + }, + "uuid": { + "type": ["string", "null"] + } + }, + "required": [ + "device" + ] + }, + "file": { + "allOf": [ + { + "$ref": "#/definitions/storage/definitions/node" + }, + { + "type": "object", + "properties": { + "mode": { + "type": ["integer", "null"] + }, + "contents": { + "$ref": "#/definitions/resource" + }, + "append": { + "type": "array", + "items": { + "$ref": "#/definitions/resource" + } + } + } + } + ] + }, + "directory": { + "allOf": [ + { + "$ref": "#/definitions/storage/definitions/node" + }, + { + "type": "object", + "properties": { + "mode": { + "type": ["integer", "null"] + } + } + } + ] + }, + "link": { + "allOf": [ + { + "$ref": "#/definitions/storage/definitions/node" + }, + { + "type": "object", + "properties": { + "target": { + "type": ["string", "null"] + }, + "hard": { + "type": ["boolean", "null"] + } + } + } + ] + }, + "partition": { + "type": "object", + "properties": { + "label": { + "type": ["string", "null"] + }, + "number": { + "type": "integer" + }, + "sizeMiB": { + "type": ["integer", "null"] + }, + "startMiB": { + "type": ["integer", "null"] + }, + "typeGuid": { + "type": ["string", "null"] + }, + "guid": { + "type": ["string", "null"] + }, + "wipePartitionEntry": { + "type": ["boolean", "null"] + }, + "shouldExist": { + "type": ["boolean", "null"] + }, + "resize": { + "type": ["boolean", "null"] + } + } + }, + "node": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "overwrite": { + "type": ["boolean", "null"] + }, + "user": { + "type": "object", + "properties": { + "id": { + "type": ["integer", "null"] + }, + "name": { + "type": ["string", "null"] + } + } + }, + "group": { + "type": "object", + "properties": { + "id": { + "type": ["integer", "null"] + }, + "name": { + "type": ["string", "null"] + } + } + } + }, + "required": [ + "path" + ] + } + } + }, + "systemd": { + "type": "object", + "properties": { + "units": { + "type": "array", + "items": { + "$ref": "#/definitions/systemd/definitions/unit" + } + } + }, + "definitions": { + "unit": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "enabled": { + "type": ["boolean", "null"] + }, + "mask": { + "type": ["boolean", "null"] + }, + "contents": { + "type": ["string", "null"] + }, + "dropins": { + "type": "array", + "items": { + "$ref": "#/definitions/systemd/definitions/dropin" + } + } + }, + "required": [ + "name" + ] + }, + "dropin": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "contents": { + "type": ["string", "null"] + } + }, + "required": [ + "name" + ] + } + } + }, + "kernelArguments": { + "type": "object", + "properties": { + "shouldExist": { + "type": "array", + "items": { + "$ref": "#/definitions/kernelArgument" + } + }, + "shouldNotExist": { + "type": "array", + "items": { + "$ref": "#/definitions/kernelArgument" + } + } + } + }, + "kernelArgument": { + "type": "string" + }, + "passwd": { + "type": "object", + "properties": { + "users": { + "type": "array", + "items": { + "$ref": "#/definitions/passwd/definitions/user" + } + }, + "groups": { + "type": "array", + "items": { + "$ref": "#/definitions/passwd/definitions/group" + } + } + }, + "definitions": { + "user": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "passwordHash": { + "type": ["string", "null"] + }, + "sshAuthorizedKeys": { + "type": "array", + "items": { + "type": "string" + } + }, + "uid": { + "type": ["integer", "null"] + }, + "gecos": { + "type": ["string", "null"] + }, + "homeDir": { + "type": ["string", "null"] + }, + "noCreateHome": { + "type": ["boolean", "null"] + }, + "primaryGroup": { + "type": ["string", "null"] + }, + "groups": { + "type": "array", + "items": { + "type": "string" + } + }, + "noUserGroup": { + "type": ["boolean", "null"] + }, + "system": { + "type": ["boolean", "null"] + }, + "noLogInit": { + "type": ["boolean", "null"] + }, + "shell": { + "type": ["string", "null"] + }, + "shouldExist": { + "type": ["boolean", "null"] + } + }, + "required": [ + "name" + ] + }, + "group": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "gid": { + "type": ["integer", "null"] + }, + "passwordHash": { + "type": ["string", "null"] + }, + "system": { + "type": ["boolean", "null"] + }, + "shouldExist": { + "type": ["boolean", "null"] + } + }, + "required": [ + "name" + ] + } + } + } + } +} diff --git a/config/v3_7_experimental/translate/translate.go b/config/v3_7_experimental/translate/translate.go new file mode 100644 index 000000000..a885877e7 --- /dev/null +++ b/config/v3_7_experimental/translate/translate.go @@ -0,0 +1,68 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package translate + +import ( + "github.com/coreos/ignition/v2/config/translate" + "github.com/coreos/ignition/v2/config/util" + old_types "github.com/coreos/ignition/v2/config/v3_5/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" +) + +func translateFileEmbedded1(old old_types.FileEmbedded1) (ret types.FileEmbedded1) { + tr := translate.NewTranslator() + tr.Translate(&old.Append, &ret.Append) + tr.Translate(&old.Contents, &ret.Contents) + if old.Mode != nil { + // Since fixing #2024 we now have to mask for the stabilized specs + // to reduce security risks of applying permissions that were not applied + // before the fix was implemented. + // We support the special mode bits for specs >=3.6.0, so if + // the user provides special mode bits in an Ignition config + // with the version < 3.6.0, then we need to explicitly mask + // those bits out during translation. + ret.Mode = util.IntToPtr(*old.Mode & ^07000) + } + return +} + +func translateDirectoryEmbedded1(old old_types.DirectoryEmbedded1) (ret types.DirectoryEmbedded1) { + if old.Mode != nil { + // Since fixing #2024 we now have to mask for the stabilized specs + // to reduce security risks of applying permissions that were not applied + // before the fix was implemented. + // We support the special mode bits for specs >=3.6.0, so if + // the user provides special mode bits in an Ignition config + // with the version < 3.6.0, then we need to explicitly mask + // those bits out during translation. + ret.Mode = util.IntToPtr(*old.Mode & ^07000) + } + return +} +func translateIgnition(old old_types.Ignition) (ret types.Ignition) { + // use a new translator so we don't recurse infinitely + translate.NewTranslator().Translate(&old, &ret) + ret.Version = types.MaxVersion.String() + return +} + +func Translate(old old_types.Config) (ret types.Config) { + tr := translate.NewTranslator() + tr.AddCustomTranslator(translateIgnition) + tr.AddCustomTranslator(translateDirectoryEmbedded1) + tr.AddCustomTranslator(translateFileEmbedded1) + tr.Translate(&old, &ret) + return +} diff --git a/config/v3_7_experimental/translate/translate_test.go b/config/v3_7_experimental/translate/translate_test.go new file mode 100644 index 000000000..fc9e3de55 --- /dev/null +++ b/config/v3_7_experimental/translate/translate_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package translate + +import ( + "reflect" + "testing" + + "github.com/coreos/ignition/v2/config/util" + old "github.com/coreos/ignition/v2/config/v3_5/types" +) + +// Check that we have valid translators for the complete config struct +// hierarchy; Translate will panic if not. We need to use a deeply non-zero +// struct to ensure translation descends into every type. +func TestTranslate(t *testing.T) { + typ := reflect.TypeOf(old.Config{}) + config := util.NonZeroValue(typ).Interface().(old.Config) + Translate(config) +} diff --git a/config/v3_7_experimental/types/cex.go b/config/v3_7_experimental/types/cex.go new file mode 100644 index 000000000..b34f5f527 --- /dev/null +++ b/config/v3_7_experimental/types/cex.go @@ -0,0 +1,33 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (cm Cex) IsPresent() bool { + return util.IsTrue(cm.Enabled) +} + +func (cx Cex) Validate(c path.ContextPath) (r report.Report) { + if !util.IsTrue(cx.Enabled) { + return + } + return +} diff --git a/config/v3_7_experimental/types/cex_test.go b/config/v3_7_experimental/types/cex_test.go new file mode 100644 index 000000000..35d562d8f --- /dev/null +++ b/config/v3_7_experimental/types/cex_test.go @@ -0,0 +1,53 @@ +// Copyright 2021 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "reflect" + "testing" + + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func TestCexValidate(t *testing.T) { + tests := []struct { + in Cex + at path.ContextPath + out error + }{ + { + in: Cex{}, + out: nil, + }, + { + in: Cex{ + Enabled: util.BoolToPtr(true), + }, + out: nil, + }, + } + + for i, test := range tests { + r := test.in.Validate(path.ContextPath{}) + expected := report.Report{} + expected.AddOnError(test.at, test.out) + if !reflect.DeepEqual(expected, r) { + t.Errorf("#%d: bad report: want %v, got %v", i, expected, r) + } + } +} diff --git a/config/v3_7_experimental/types/clevis.go b/config/v3_7_experimental/types/clevis.go new file mode 100644 index 000000000..3742633dd --- /dev/null +++ b/config/v3_7_experimental/types/clevis.go @@ -0,0 +1,43 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (c Clevis) IsPresent() bool { + return util.NotEmpty(c.Custom.Pin) || + len(c.Tang) > 0 || + util.IsTrue(c.Tpm2) || + c.Threshold != nil && *c.Threshold != 0 +} + +func (cu ClevisCustom) Validate(c path.ContextPath) (r report.Report) { + if util.NilOrEmpty(cu.Pin) && util.NilOrEmpty(cu.Config) && !util.IsTrue(cu.NeedsNetwork) { + return + } + if util.NilOrEmpty(cu.Pin) { + r.AddOnError(c.Append("pin"), errors.ErrClevisPinRequired) + } + if util.NilOrEmpty(cu.Config) { + r.AddOnError(c.Append("config"), errors.ErrClevisConfigRequired) + } + return +} diff --git a/config/v3_7_experimental/types/clevis_test.go b/config/v3_7_experimental/types/clevis_test.go new file mode 100644 index 000000000..81fb3b26e --- /dev/null +++ b/config/v3_7_experimental/types/clevis_test.go @@ -0,0 +1,78 @@ +// Copyright 2021 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "reflect" + "testing" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func TestClevisCustomValidate(t *testing.T) { + tests := []struct { + in ClevisCustom + at path.ContextPath + out error + }{ + { + in: ClevisCustom{}, + out: nil, + }, + { + in: ClevisCustom{ + Config: util.StrToPtr("z"), + NeedsNetwork: util.BoolToPtr(true), + Pin: util.StrToPtr("sss"), + }, + out: nil, + }, + { + in: ClevisCustom{ + Config: util.StrToPtr("z"), + }, + at: path.New("", "pin"), + out: errors.ErrClevisPinRequired, + }, + { + in: ClevisCustom{ + Config: util.StrToPtr("z"), + Pin: util.StrToPtr("z"), + }, + at: path.New("", "pin"), + out: nil, + }, + { + in: ClevisCustom{ + Pin: util.StrToPtr("tpm2"), + }, + at: path.New("", "config"), + out: errors.ErrClevisConfigRequired, + }, + } + + for i, test := range tests { + r := test.in.Validate(path.ContextPath{}) + expected := report.Report{} + expected.AddOnError(test.at, test.out) + if !reflect.DeepEqual(expected, r) { + t.Errorf("#%d: bad report: want %v, got %v", i, expected, r) + } + } +} diff --git a/config/v3_7_experimental/types/config.go b/config/v3_7_experimental/types/config.go new file mode 100644 index 000000000..02ef7b4ae --- /dev/null +++ b/config/v3_7_experimental/types/config.go @@ -0,0 +1,64 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/go-semver/semver" + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +var ( + MaxVersion = semver.Version{ + Major: 3, + Minor: 6, + } +) + +func (cfg Config) Validate(c path.ContextPath) (r report.Report) { + systemdPath := "/etc/systemd/system/" + unitPaths := map[string]struct{}{} + for _, unit := range cfg.Systemd.Units { + if !util.NilOrEmpty(unit.Contents) { + pathString := systemdPath + unit.Name + unitPaths[pathString] = struct{}{} + } + for _, dropin := range unit.Dropins { + if !util.NilOrEmpty(dropin.Contents) { + pathString := systemdPath + unit.Name + ".d/" + dropin.Name + unitPaths[pathString] = struct{}{} + } + } + } + for i, f := range cfg.Storage.Files { + if _, exists := unitPaths[f.Path]; exists { + r.AddOnError(c.Append("storage", "files", i, "path"), errors.ErrPathConflictsSystemd) + } + } + for i, d := range cfg.Storage.Directories { + if _, exists := unitPaths[d.Path]; exists { + r.AddOnError(c.Append("storage", "directories", i, "path"), errors.ErrPathConflictsSystemd) + } + } + for i, l := range cfg.Storage.Links { + if _, exists := unitPaths[l.Path]; exists { + r.AddOnError(c.Append("storage", "links", i, "path"), errors.ErrPathConflictsSystemd) + } + } + return +} diff --git a/config/v3_7_experimental/types/config_test.go b/config/v3_7_experimental/types/config_test.go new file mode 100644 index 000000000..3d82627b2 --- /dev/null +++ b/config/v3_7_experimental/types/config_test.go @@ -0,0 +1,260 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "reflect" + "testing" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func TestConfigValidation(t *testing.T) { + tests := []struct { + in Config + out error + at path.ContextPath + }{ + // test 0: file conflicts with systemd dropin file, error + { + in: Config{ + Storage: Storage{ + Files: []File{ + { + Node: Node{Path: "/etc/systemd/system/foo.service.d/bar.conf"}, + }, + }, + }, + Systemd: Systemd{ + Units: []Unit{ + { + Name: "foo.service", + Dropins: []Dropin{ + { + Name: "bar.conf", + Contents: util.StrToPtr("[Foo]\nQux=Bar"), + }, + }, + }, + }, + }, + }, + out: errors.ErrPathConflictsSystemd, + at: path.New("json", "storage", "files", 0, "path"), + }, + // test 1: file conflicts with systemd unit, error + { + in: Config{ + Storage: Storage{ + Files: []File{ + { + Node: Node{Path: "/etc/systemd/system/foo.service"}, + }, + }, + }, + Systemd: Systemd{ + Units: []Unit{ + { + Name: "foo.service", + Contents: util.StrToPtr("[Foo]\nQux=Bar"), + Enabled: util.BoolToPtr(true), + }, + }, + }, + }, + out: errors.ErrPathConflictsSystemd, + at: path.New("json", "storage", "files", 0, "path"), + }, + // test 2: directory conflicts with systemd dropin file, error + { + in: Config{ + Storage: Storage{ + Directories: []Directory{ + { + Node: Node{Path: "/etc/systemd/system/foo.service.d/bar.conf"}, + }, + }, + }, + Systemd: Systemd{ + []Unit{ + { + Name: "foo.service", + Dropins: []Dropin{ + { + Name: "bar.conf", + Contents: util.StrToPtr("[Foo]\nQux=Bar"), + }, + }, + }, + }, + }, + }, + out: errors.ErrPathConflictsSystemd, + at: path.New("json", "storage", "directories", 0, "path"), + }, + // test 3: directory conflicts with systemd unit, error + { + in: Config{ + Storage: Storage{ + Directories: []Directory{ + { + Node: Node{Path: "/etc/systemd/system/foo.service"}, + }, + }, + }, + Systemd: Systemd{ + []Unit{ + { + Name: "foo.service", + Contents: util.StrToPtr("[foo]\nQux=Baz"), + Enabled: util.BoolToPtr(true), + }, + }, + }, + }, + out: errors.ErrPathConflictsSystemd, + at: path.New("json", "storage", "directories", 0, "path"), + }, + // test 4: link conflicts with systemd dropin file, error + { + in: Config{ + Storage: Storage{ + Links: []Link{ + { + Node: Node{Path: "/etc/systemd/system/foo.service.d/bar.conf"}, + LinkEmbedded1: LinkEmbedded1{Target: util.StrToPtr("/qux.conf")}, + }, + }, + }, + Systemd: Systemd{ + []Unit{ + { + Name: "foo.service", + Dropins: []Dropin{ + { + Name: "bar.conf", + Contents: util.StrToPtr("[Foo]\nQux=Bar"), + }, + }, + }, + }, + }, + }, + out: errors.ErrPathConflictsSystemd, + at: path.New("json", "storage", "links", 0, "path"), + }, + // test 5: link conflicts with systemd unit, error + { + in: Config{ + Storage: Storage{ + Links: []Link{ + { + Node: Node{Path: "/etc/systemd/system/foo.service"}, + LinkEmbedded1: LinkEmbedded1{Target: util.StrToPtr("/qux.conf")}, + }, + }, + }, + Systemd: Systemd{ + []Unit{ + { + Name: "foo.service", + Contents: util.StrToPtr("[foo]\nQux=Baz"), + Enabled: util.BoolToPtr(true), + }, + }, + }, + }, + out: errors.ErrPathConflictsSystemd, + at: path.New("json", "storage", "links", 0, "path"), + }, + // test 6: non-conflicting scenarios + { + in: Config{ + Storage: Storage{ + Files: []File{ + { + Node: Node{Path: "/etc/systemd/system/bar.service.d/baz.conf"}, + }, + { + Node: Node{Path: "/etc/systemd/system/bar.service"}, + }, + { + Node: Node{Path: "/etc/systemd/system/foo.service.d/qux.conf"}, + }, + }, + Links: []Link{ + { + Node: Node{Path: "/etc/systemd/system/qux.service"}, + LinkEmbedded1: LinkEmbedded1{Target: util.StrToPtr("/qux.conf")}, + }, + { + Node: Node{Path: "/etc/systemd/system/quux.service.d/foo.conf"}, + LinkEmbedded1: LinkEmbedded1{Target: util.StrToPtr("/foo.conf")}, + }, + }, + Directories: []Directory{ + { + Node: Node{Path: "/etc/systemd/system/quux.service.d"}, + }, + }, + }, + Systemd: Systemd{ + Units: []Unit{ + { + Name: "foo.service", + Contents: util.StrToPtr("[Foo]\nQux=Baz"), + Enabled: util.BoolToPtr(true), + }, + { + Name: "bar.service", + Dropins: []Dropin{ + { + Name: "baz.conf", + }, + }, + Enabled: util.BoolToPtr(true), + }, + { + Name: "qux.service", + Dropins: []Dropin{ + { + Name: "bar.conf", + Contents: util.StrToPtr("[Foo]\nQux=Baz"), + }, + }, + }, + { + Name: "quux.service", + Contents: util.StrToPtr("[Foo]\nQux=Baz"), + Enabled: util.BoolToPtr(true), + }, + }, + }, + }, + }, + } + for i, test := range tests { + r := test.in.Validate(path.New("json")) + expected := report.Report{} + expected.AddOnError(test.at, test.out) + if !reflect.DeepEqual(expected, r) { + t.Errorf("#%d: bad error: expected : %v, got %v", i, expected, r) + } + } +} diff --git a/config/v3_7_experimental/types/device.go b/config/v3_7_experimental/types/device.go new file mode 100644 index 000000000..a10ce97b0 --- /dev/null +++ b/config/v3_7_experimental/types/device.go @@ -0,0 +1,25 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (d Device) Validate(c path.ContextPath) (r report.Report) { + r.AddOnError(c, validatePath(string(d))) + return +} diff --git a/config/v3_7_experimental/types/directory.go b/config/v3_7_experimental/types/directory.go new file mode 100644 index 000000000..f6f068455 --- /dev/null +++ b/config/v3_7_experimental/types/directory.go @@ -0,0 +1,26 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (d Directory) Validate(c path.ContextPath) (r report.Report) { + r.Merge(d.Node.Validate(c)) + r.AddOnError(c.Append("mode"), validateMode(d.Mode)) + return +} diff --git a/config/v3_7_experimental/types/disk.go b/config/v3_7_experimental/types/disk.go new file mode 100644 index 000000000..8caf8499d --- /dev/null +++ b/config/v3_7_experimental/types/disk.go @@ -0,0 +1,135 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (d Disk) Key() string { + return d.Device +} + +func (n Disk) Validate(c path.ContextPath) (r report.Report) { + if len(n.Device) == 0 { + r.AddOnError(c.Append("device"), errors.ErrDiskDeviceRequired) + return + } + r.AddOnError(c.Append("device"), validatePath(n.Device)) + + if collides, p := n.partitionNumbersCollide(); collides { + r.AddOnError(c.Append("partitions", p), errors.ErrPartitionNumbersCollide) + } + if overlaps, p := n.partitionsOverlap(); overlaps { + r.AddOnError(c.Append("partitions", p), errors.ErrPartitionsOverlap) + } + if n.partitionsMixZeroesAndNonexistence() { + r.AddOnError(c.Append("partitions"), errors.ErrZeroesWithShouldNotExist) + } + if collides, p := n.partitionLabelsCollide(); collides { + r.AddOnError(c.Append("partitions", p), errors.ErrDuplicateLabels) + } + return +} + +// partitionNumbersCollide returns true if partition numbers in n.Partitions are not unique. It also returns the +// index of the colliding partition +func (n Disk) partitionNumbersCollide() (bool, int) { + m := map[int][]int{} // from partition number to index into array + for i, p := range n.Partitions { + if p.Number != 0 { + // a number of 0 means next available number, multiple devices can specify this + m[p.Number] = append(m[p.Number], i) + } + } + for _, n := range m { + if len(n) > 1 { + // TODO(vc): return information describing the collision for logging + return true, n[1] + } + } + return false, 0 +} + +func (d Disk) partitionLabelsCollide() (bool, int) { + m := map[string]struct{}{} + for i, p := range d.Partitions { + if p.Label != nil { + // a number of 0 means next available number, multiple devices can specify this + if _, exists := m[*p.Label]; exists { + return true, i + } + m[*p.Label] = struct{}{} + } + } + return false, 0 +} + +// end returns the last sector of a partition. Only used by partitionsOverlap. Requires non-nil Start and Size. +func (p Partition) end() int { + if *p.SizeMiB == 0 { + // a size of 0 means "fill available", just return the start as the end for those. + return *p.StartMiB + } + return *p.StartMiB + *p.SizeMiB - 1 +} + +// partitionsOverlap returns true if any explicitly dimensioned partitions overlap. It also returns the index of +// the overlapping partition +func (n Disk) partitionsOverlap() (bool, int) { + for _, p := range n.Partitions { + // Starts of 0 are placed by sgdisk into the "largest available block" at that time. + // We aren't going to check those for overlap since we don't have the disk geometry. + if p.StartMiB == nil || p.SizeMiB == nil || *p.StartMiB == 0 { + continue + } + + for i, o := range n.Partitions { + if o.StartMiB == nil || o.SizeMiB == nil || p == o || *o.StartMiB == 0 { + continue + } + + // is p.StartMiB within o? + if *p.StartMiB >= *o.StartMiB && *p.StartMiB <= o.end() { + return true, i + } + + // is p.end() within o? + if p.end() >= *o.StartMiB && p.end() <= o.end() { + return true, i + } + + // do p.StartMiB and p.end() straddle o? + if *p.StartMiB < *o.StartMiB && p.end() > o.end() { + return true, i + } + } + } + return false, 0 +} + +func (n Disk) partitionsMixZeroesAndNonexistence() bool { + hasZero := false + hasShouldNotExist := false + for _, p := range n.Partitions { + hasShouldNotExist = hasShouldNotExist || util.IsFalse(p.ShouldExist) + hasZero = hasZero || (p.Number == 0) + } + return hasZero && hasShouldNotExist +} diff --git a/config/v3_7_experimental/types/file.go b/config/v3_7_experimental/types/file.go new file mode 100644 index 000000000..9b71bb26a --- /dev/null +++ b/config/v3_7_experimental/types/file.go @@ -0,0 +1,43 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (f File) Validate(c path.ContextPath) (r report.Report) { + r.Merge(f.Node.Validate(c)) + r.AddOnError(c.Append("mode"), validateMode(f.Mode)) + r.AddOnError(c.Append("overwrite"), f.validateOverwrite()) + return +} + +func (f File) validateOverwrite() error { + if util.IsTrue(f.Overwrite) && f.Contents.Source == nil { + return errors.ErrOverwriteAndNilSource + } + return nil +} + +func (f FileEmbedded1) IgnoreDuplicates() map[string]struct{} { + return map[string]struct{}{ + "Append": {}, + } +} diff --git a/config/v3_7_experimental/types/file_test.go b/config/v3_7_experimental/types/file_test.go new file mode 100644 index 000000000..449e07009 --- /dev/null +++ b/config/v3_7_experimental/types/file_test.go @@ -0,0 +1,117 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "testing" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" +) + +func TestFileValidateOverwrite(t *testing.T) { + tests := []struct { + in File + out error + }{ + { + File{}, + nil, + }, + { + File{ + Node: Node{ + Overwrite: util.BoolToPtr(true), + }, + }, + errors.ErrOverwriteAndNilSource, + }, + { + File{ + Node: Node{ + Overwrite: util.BoolToPtr(true), + }, + FileEmbedded1: FileEmbedded1{ + Contents: Resource{ + Source: util.StrToPtr(""), + }, + }, + }, + nil, + }, + { + File{ + Node: Node{ + Overwrite: util.BoolToPtr(true), + }, + FileEmbedded1: FileEmbedded1{ + Contents: Resource{ + Source: util.StrToPtr("http://example.com"), + }, + }, + }, + nil, + }, + } + + for i, test := range tests { + err := test.in.validateOverwrite() + if test.out != err { + t.Errorf("#%d: bad error: want %v, got %v", i, test.out, err) + } + } +} + +func TestFileContentsValidate(t *testing.T) { + tests := []struct { + in Resource + out error + }{ + { + Resource{}, + nil, + }, + { + Resource{ + Source: util.StrToPtr(""), + }, + nil, + }, + { + Resource{ + Source: util.StrToPtr(""), + Verification: Verification{ + Hash: util.StrToPtr(""), + }, + }, + nil, + }, + { + Resource{ + Verification: Verification{ + Hash: util.StrToPtr(""), + }, + }, + errors.ErrVerificationAndNilSource, + }, + } + + for i, test := range tests { + err := test.in.validateVerification() + if test.out != err { + t.Errorf("#%d: bad error: want %v, got %v", i, test.out, err) + } + } +} diff --git a/config/v3_7_experimental/types/filesystem.go b/config/v3_7_experimental/types/filesystem.go new file mode 100644 index 000000000..c722b3633 --- /dev/null +++ b/config/v3_7_experimental/types/filesystem.go @@ -0,0 +1,106 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (f Filesystem) Key() string { + return f.Device +} + +func (f Filesystem) IgnoreDuplicates() map[string]struct{} { + return map[string]struct{}{ + "Options": {}, + "MountOptions": {}, + } +} + +func (f Filesystem) Validate(c path.ContextPath) (r report.Report) { + r.AddOnError(c.Append("path"), f.validatePath()) + r.AddOnError(c.Append("device"), validatePath(f.Device)) + r.AddOnError(c.Append("format"), f.validateFormat()) + r.AddOnError(c.Append("label"), f.validateLabel()) + return +} + +func (f Filesystem) validatePath() error { + return validatePathNilOK(f.Path) +} + +func (f Filesystem) validateFormat() error { + if util.NilOrEmpty(f.Format) { + if util.NotEmpty(f.Path) || + util.NotEmpty(f.Label) || + util.NotEmpty(f.UUID) || + util.IsTrue(f.WipeFilesystem) || + len(f.MountOptions) != 0 || + len(f.Options) != 0 { + return errors.ErrFormatNilWithOthers + } + } else { + switch *f.Format { + case "ext4", "btrfs", "xfs", "swap", "vfat", "none": + default: + return errors.ErrFilesystemInvalidFormat + } + } + return nil +} + +func (f Filesystem) validateLabel() error { + if util.NilOrEmpty(f.Label) { + return nil + } + if util.NilOrEmpty(f.Format) { + return errors.ErrLabelNeedsFormat + } + + switch *f.Format { + case "ext4": + if len(*f.Label) > 16 { + // source: man mkfs.ext4 + return errors.ErrExt4LabelTooLong + } + case "btrfs": + if len(*f.Label) > 256 { + // source: man mkfs.btrfs + return errors.ErrBtrfsLabelTooLong + } + case "xfs": + if len(*f.Label) > 12 { + // source: man mkfs.xfs + return errors.ErrXfsLabelTooLong + } + case "swap": + // mkswap's man page does not state a limit on label size, but through + // experimentation it appears that mkswap will truncate long labels to + // 15 characters, so let's enforce that. + if len(*f.Label) > 15 { + return errors.ErrSwapLabelTooLong + } + case "vfat": + if len(*f.Label) > 11 { + // source: man mkfs.fat + return errors.ErrVfatLabelTooLong + } + } + return nil +} diff --git a/config/v3_7_experimental/types/filesystem_test.go b/config/v3_7_experimental/types/filesystem_test.go new file mode 100644 index 000000000..1f8db9037 --- /dev/null +++ b/config/v3_7_experimental/types/filesystem_test.go @@ -0,0 +1,194 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "testing" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" +) + +func TestFilesystemValidateFormat(t *testing.T) { + tests := []struct { + in Filesystem + out error + }{ + { + Filesystem{Format: util.StrToPtr("ext4")}, + nil, + }, + { + Filesystem{Format: util.StrToPtr("btrfs")}, + nil, + }, + { + Filesystem{Format: util.StrToPtr("")}, + nil, + }, + { + Filesystem{Format: nil}, + nil, + }, + { + Filesystem{Label: util.StrToPtr("z")}, + errors.ErrFormatNilWithOthers, + }, + { + Filesystem{MountOptions: []MountOption{MountOption("z")}}, + errors.ErrFormatNilWithOthers, + }, + { + Filesystem{Options: []FilesystemOption{FilesystemOption("z")}}, + errors.ErrFormatNilWithOthers, + }, + { + Filesystem{Format: util.StrToPtr(""), Path: util.StrToPtr("/")}, + errors.ErrFormatNilWithOthers, + }, + { + Filesystem{Format: nil, Path: util.StrToPtr("/")}, + errors.ErrFormatNilWithOthers, + }, + { + Filesystem{UUID: util.StrToPtr("z")}, + errors.ErrFormatNilWithOthers, + }, + { + Filesystem{WipeFilesystem: util.BoolToPtr(true)}, + errors.ErrFormatNilWithOthers, + }, + } + + for i, test := range tests { + err := test.in.validateFormat() + if test.out != err { + t.Errorf("#%d: bad error: want %v, got %v", i, test.out, err) + } + } +} + +func TestFilesystemValidatePath(t *testing.T) { + tests := []struct { + in Filesystem + out error + }{ + { + Filesystem{Path: util.StrToPtr("/foo")}, + nil, + }, + { + Filesystem{Path: util.StrToPtr("")}, + nil, + }, + { + Filesystem{Path: nil}, + nil, + }, + { + Filesystem{Path: util.StrToPtr("foo")}, + errors.ErrPathRelative, + }, + } + + for i, test := range tests { + err := test.in.validatePath() + if test.out != err { + t.Errorf("#%d: bad error: want %v, got %v", i, test.out, err) + } + } +} + +func TestLabelValidate(t *testing.T) { + type in struct { + filesystem Filesystem + } + type out struct { + err error + } + + tests := []struct { + in in + out out + }{ + { + in: in{filesystem: Filesystem{Format: util.StrToPtr("ext4"), Label: nil}}, + out: out{}, + }, + { + in: in{filesystem: Filesystem{Format: util.StrToPtr("ext4"), Label: util.StrToPtr("data")}}, + out: out{}, + }, + { + in: in{filesystem: Filesystem{Format: util.StrToPtr("ext4"), Label: util.StrToPtr("thislabelistoolong")}}, + out: out{err: errors.ErrExt4LabelTooLong}, + }, + { + in: in{filesystem: Filesystem{Format: util.StrToPtr("btrfs"), Label: nil}}, + out: out{}, + }, + { + in: in{filesystem: Filesystem{Format: util.StrToPtr("btrfs"), Label: util.StrToPtr("thislabelisnottoolong")}}, + out: out{}, + }, + { + in: in{filesystem: Filesystem{Format: util.StrToPtr("btrfs"), Label: util.StrToPtr("thislabelistoolongthislabelistoolongthislabelistoolongthislabelistoolongthislabelistoolongthislabelistoolongthislabelistoolongthislabelistoolongthislabelistoolongthislabelistoolongthislabelistoolongthislabelistoolongthislabelistoolongthislabelistoolongthislabelistoolong")}}, + out: out{err: errors.ErrBtrfsLabelTooLong}, + }, + { + in: in{filesystem: Filesystem{Format: util.StrToPtr("xfs"), Label: nil}}, + out: out{}, + }, + { + in: in{filesystem: Filesystem{Format: util.StrToPtr("xfs"), Label: util.StrToPtr("data")}}, + out: out{}, + }, + { + in: in{filesystem: Filesystem{Format: util.StrToPtr("xfs"), Label: util.StrToPtr("thislabelistoolong")}}, + out: out{err: errors.ErrXfsLabelTooLong}, + }, + { + in: in{filesystem: Filesystem{Format: util.StrToPtr("swap"), Label: nil}}, + out: out{}, + }, + { + in: in{filesystem: Filesystem{Format: util.StrToPtr("swap"), Label: util.StrToPtr("data")}}, + out: out{}, + }, + { + in: in{filesystem: Filesystem{Format: util.StrToPtr("swap"), Label: util.StrToPtr("thislabelistoolong")}}, + out: out{err: errors.ErrSwapLabelTooLong}, + }, + { + in: in{filesystem: Filesystem{Format: util.StrToPtr("vfat"), Label: nil}}, + out: out{}, + }, + { + in: in{filesystem: Filesystem{Format: util.StrToPtr("vfat"), Label: util.StrToPtr("data")}}, + out: out{}, + }, + { + in: in{filesystem: Filesystem{Format: util.StrToPtr("vfat"), Label: util.StrToPtr("thislabelistoolong")}}, + out: out{err: errors.ErrVfatLabelTooLong}, + }, + } + + for i, test := range tests { + err := test.in.filesystem.validateLabel() + if test.out.err != err { + t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err) + } + } +} diff --git a/config/v3_7_experimental/types/headers.go b/config/v3_7_experimental/types/headers.go new file mode 100644 index 000000000..be1aadad9 --- /dev/null +++ b/config/v3_7_experimental/types/headers.go @@ -0,0 +1,65 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "net/http" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +// Parse generates standard net/http headers from the data in HTTPHeaders +func (hs HTTPHeaders) Parse() (http.Header, error) { + headers := http.Header{} + for _, header := range hs { + if header.Name == "" { + return nil, errors.ErrEmptyHTTPHeaderName + } + if header.Value == nil || string(*header.Value) == "" { + return nil, errors.ErrInvalidHTTPHeader + } + headers.Add(header.Name, string(*header.Value)) + } + return headers, nil +} + +func (h HTTPHeader) Validate(c path.ContextPath) (r report.Report) { + r.AddOnError(c.Append("name"), h.validateName()) + r.AddOnError(c.Append("value"), h.validateValue()) + return +} + +func (h HTTPHeader) validateName() error { + if h.Name == "" { + return errors.ErrEmptyHTTPHeaderName + } + return nil +} + +func (h HTTPHeader) validateValue() error { + if h.Value == nil { + return nil + } + if string(*h.Value) == "" { + return errors.ErrInvalidHTTPHeader + } + return nil +} + +func (h HTTPHeader) Key() string { + return h.Name +} diff --git a/config/v3_7_experimental/types/headers_test.go b/config/v3_7_experimental/types/headers_test.go new file mode 100644 index 000000000..40380b684 --- /dev/null +++ b/config/v3_7_experimental/types/headers_test.go @@ -0,0 +1,159 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "testing" + + "github.com/coreos/ignition/v2/config/shared/errors" +) + +func toPointer(val string) *string { + return &val +} + +func equal(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true +} + +func TestHeadersParse(t *testing.T) { + tests := []struct { + in HTTPHeaders + out error + }{ + { + // Valid headers + HTTPHeaders{ + HTTPHeader{ + Name: "header1", + Value: toPointer("header1value"), + }, + HTTPHeader{ + Name: "header2", + Value: toPointer("header2value"), + }, + }, + nil, + }, + { + // Duplicate headers + HTTPHeaders{ + HTTPHeader{ + Name: "header1", + Value: toPointer("header1value"), + }, + HTTPHeader{ + Name: "header1", + Value: toPointer("header2value"), + }, + }, + nil, + }, + { + // No header name + HTTPHeaders{ + HTTPHeader{ + Name: "header1", + Value: toPointer("header1value"), + }, + HTTPHeader{ + Value: toPointer("emptyheadervalue"), + }, + }, + errors.ErrEmptyHTTPHeaderName, + }, + { + // No header value + HTTPHeaders{ + HTTPHeader{ + Name: "header1", + Value: toPointer("header1value"), + }, + HTTPHeader{ + Name: "emptyheadername", + }, + }, + errors.ErrInvalidHTTPHeader, + }, + { + // Invalid header without elements + HTTPHeaders{ + HTTPHeader{ + Name: "header1", + Value: toPointer("header1value"), + }, + HTTPHeader{}, + }, + errors.ErrEmptyHTTPHeaderName, + }, + } + + for i, test := range tests { + _, err := test.in.Parse() + if test.out != err { + t.Errorf("#%d: bad error: want %v, got %v", i, test.out, err) + } + } +} + +func TestValidHeadersParse(t *testing.T) { + // Valid headers + headers := HTTPHeaders{ + HTTPHeader{ + Name: "header1", + Value: toPointer("header1value"), + }, + HTTPHeader{ + Name: "header2", + Value: toPointer("header2value"), + }, + } + parseHeaders, err := headers.Parse() + if err != nil { + t.Errorf("error during parsing valid headers: %v", err) + } + if !equal(parseHeaders["Header1"], []string{"header1value"}) || !equal(parseHeaders["Header2"], []string{"header2value"}) { + t.Errorf("parsed HTTP headers values are wrong") + } +} + +func TestDuplicateHeadersParse(t *testing.T) { + // Duplicate headers + headers := HTTPHeaders{ + HTTPHeader{ + Name: "header1", + Value: toPointer("header1value"), + }, + HTTPHeader{ + Name: "header1", + Value: toPointer("header2value"), + }, + } + parseHeaders, err := headers.Parse() + if err != nil { + t.Errorf("error during parsing valid headers: %v", err) + } + if !equal(parseHeaders["Header1"], []string{"header1value", "header2value"}) { + t.Errorf("parsed HTTP headers values are wrong") + } +} diff --git a/config/v3_7_experimental/types/ignition.go b/config/v3_7_experimental/types/ignition.go new file mode 100644 index 000000000..190445bda --- /dev/null +++ b/config/v3_7_experimental/types/ignition.go @@ -0,0 +1,49 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "github.com/coreos/go-semver/semver" + + "github.com/coreos/ignition/v2/config/shared/errors" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (v Ignition) Semver() (*semver.Version, error) { + return semver.NewVersion(v.Version) +} + +func (ic IgnitionConfig) Validate(c path.ContextPath) (r report.Report) { + for i, res := range ic.Merge { + r.AddOnError(c.Append("merge", i), res.validateRequiredSource()) + } + return +} + +func (v Ignition) Validate(c path.ContextPath) (r report.Report) { + c = c.Append("version") + tv, err := v.Semver() + if err != nil { + r.AddOnError(c, errors.ErrInvalidVersion) + return + } + + if MaxVersion != *tv { + r.AddOnError(c, errors.ErrUnknownVersion) + } + return +} diff --git a/config/v3_7_experimental/types/ignition_test.go b/config/v3_7_experimental/types/ignition_test.go new file mode 100644 index 000000000..ac7d462d3 --- /dev/null +++ b/config/v3_7_experimental/types/ignition_test.go @@ -0,0 +1,48 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "testing" + + "github.com/coreos/vcontext/validate" +) + +func TestIgnitionConfigValidate(t *testing.T) { + tests := []struct { + in IgnitionConfig + out string + }{ + { + IgnitionConfig{ + Merge: []Resource{{}}, + }, + "error at $.merge.0: source is required\n", + }, + { + IgnitionConfig{ + Replace: Resource{}, + }, + "", + }, + } + + for i, test := range tests { + r := validate.Validate(test.in, "test") + if test.out != r.String() { + t.Errorf("#%d: bad error: want %q, got %q", i, test.out, r.String()) + } + } +} diff --git a/config/v3_7_experimental/types/kargs.go b/config/v3_7_experimental/types/kargs.go new file mode 100644 index 000000000..42c29408e --- /dev/null +++ b/config/v3_7_experimental/types/kargs.go @@ -0,0 +1,22 @@ +// Copyright 2021 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +func (k KernelArguments) MergedKeys() map[string]string { + return map[string]string{ + "ShouldExist": "KernelArgument", + "ShouldNotExist": "KernelArgument", + } +} diff --git a/config/v3_7_experimental/types/kargs_test.go b/config/v3_7_experimental/types/kargs_test.go new file mode 100644 index 000000000..1a258727e --- /dev/null +++ b/config/v3_7_experimental/types/kargs_test.go @@ -0,0 +1,51 @@ +// Copyright 2021 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "testing" + + "github.com/coreos/ignition/v2/config/validate" +) + +func TestKernelArgumentsValidate(t *testing.T) { + tests := []struct { + in KernelArguments + out string + }{ + // Ensure that ValidateWithContext prevents duplicate entries + // in ShouldExist & ShouldNotExist + { + KernelArguments{ + ShouldExist: []KernelArgument{ + "foo", + "bar", + }, + ShouldNotExist: []KernelArgument{ + "baz", + "foo", + }, + }, + "error at $.shouldNotExist.1: duplicate entry defined\n", + }, + } + + for i, test := range tests { + r := validate.ValidateWithContext(test.in, nil) + if test.out != r.String() { + t.Errorf("#%d: bad error: want %q, got %q", i, test.out, r.String()) + } + } +} diff --git a/config/v3_7_experimental/types/luks.go b/config/v3_7_experimental/types/luks.go new file mode 100644 index 000000000..e4c1d6815 --- /dev/null +++ b/config/v3_7_experimental/types/luks.go @@ -0,0 +1,82 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "strings" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (l Luks) Key() string { + return l.Name +} + +func (l Luks) IgnoreDuplicates() map[string]struct{} { + return map[string]struct{}{ + "Options": {}, + } +} + +func (l Luks) Validate(c path.ContextPath) (r report.Report) { + if strings.Contains(l.Name, "/") { + r.AddOnError(c.Append("name"), errors.ErrLuksNameContainsSlash) + } + r.AddOnError(c.Append("label"), l.validateLabel()) + if util.NilOrEmpty(l.Device) { + r.AddOnError(c.Append("device"), errors.ErrDiskDeviceRequired) + } else { + r.AddOnError(c.Append("device"), validatePath(*l.Device)) + } + + if util.NotEmpty(l.Clevis.Custom.Pin) && (len(l.Clevis.Tang) > 0 || util.IsTrue(l.Clevis.Tpm2) || (l.Clevis.Threshold != nil && *l.Clevis.Threshold != 0)) { + r.AddOnError(c.Append("clevis"), errors.ErrClevisCustomWithOthers) + } + + // fail if a key file is provided and is not valid + if err := validateURLNilOK(l.KeyFile.Source); err != nil { + r.AddOnError(c.Append("keys"), errors.ErrInvalidLuksKeyFile) + } + + // fail if Cex use with Clevis + if l.Clevis.IsPresent() && l.Cex.IsPresent() { + r.AddOnError(c.Append("cex"), errors.ErrCexWithClevis) + } + + // fail if key file is provided along with Cex + if l.Cex.IsPresent() && util.NotEmpty(l.KeyFile.Source) { + r.AddOnError(c.Append("cex"), errors.ErrCexWithKeyFile) + } + + return +} + +func (l Luks) validateLabel() error { + if util.NilOrEmpty(l.Label) { + return nil + } + + if len(*l.Label) > 47 { + // LUKS2_LABEL_L has a maximum length of 48 (including the null terminator) + // https://gitlab.com/cryptsetup/cryptsetup/-/blob/1633f030e89ad2f11ae649ba9600997a41abd3fc/lib/luks2/luks2.h#L86 + return errors.ErrLuksLabelTooLong + } + + return nil +} diff --git a/config/v3_7_experimental/types/mode.go b/config/v3_7_experimental/types/mode.go new file mode 100644 index 000000000..9eb7573d8 --- /dev/null +++ b/config/v3_7_experimental/types/mode.go @@ -0,0 +1,26 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "github.com/coreos/ignition/v2/config/shared/errors" +) + +func validateMode(m *int) error { + if m != nil && (*m < 0 || *m > 07777) { + return errors.ErrFileIllegalMode + } + return nil +} diff --git a/config/v3_7_experimental/types/mode_test.go b/config/v3_7_experimental/types/mode_test.go new file mode 100644 index 000000000..8966e25d8 --- /dev/null +++ b/config/v3_7_experimental/types/mode_test.go @@ -0,0 +1,62 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "reflect" + "testing" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" +) + +func TestModeValidate(t *testing.T) { + tests := []struct { + in *int + out error + }{ + { + nil, + nil, + }, + { + util.IntToPtr(0), + nil, + }, + { + util.IntToPtr(0644), + nil, + }, + { + util.IntToPtr(01755), + nil, + }, + { + util.IntToPtr(07777), + nil, + }, + { + util.IntToPtr(010000), + errors.ErrFileIllegalMode, + }, + } + + for i, test := range tests { + err := validateMode(test.in) + if !reflect.DeepEqual(test.out, err) { + t.Errorf("#%d: bad err: want %v, got %v", i, test.out, err) + } + } +} diff --git a/config/v3_7_experimental/types/node.go b/config/v3_7_experimental/types/node.go new file mode 100644 index 000000000..248276e73 --- /dev/null +++ b/config/v3_7_experimental/types/node.go @@ -0,0 +1,59 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "path" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + vpath "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (n Node) Key() string { + return n.Path +} + +func (n Node) Validate(c vpath.ContextPath) (r report.Report) { + r.AddOnError(c.Append("path"), validatePath(n.Path)) + return +} + +func (n Node) Depth() int { + count := 0 + for p := path.Clean(string(n.Path)); p != "/"; count++ { + p = path.Dir(p) + } + return count +} + +func validateIDorName(id *int, name *string) error { + if id != nil && util.NotEmpty(name) { + return errors.ErrBothIDAndNameSet + } + return nil +} + +func (nu NodeUser) Validate(c vpath.ContextPath) (r report.Report) { + r.AddOnError(c, validateIDorName(nu.ID, nu.Name)) + return +} + +func (ng NodeGroup) Validate(c vpath.ContextPath) (r report.Report) { + r.AddOnError(c, validateIDorName(ng.ID, ng.Name)) + return +} diff --git a/config/v3_7_experimental/types/node_test.go b/config/v3_7_experimental/types/node_test.go new file mode 100644 index 000000000..20ba4eb67 --- /dev/null +++ b/config/v3_7_experimental/types/node_test.go @@ -0,0 +1,101 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "reflect" + "testing" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func TestNodeValidatePath(t *testing.T) { + node := Node{Path: "not/absolute"} + rep := report.Report{} + rep.AddOnError(path.ContextPath{}.Append("path"), errors.ErrPathRelative) + if receivedRep := node.Validate(path.ContextPath{}); !reflect.DeepEqual(rep, receivedRep) { + t.Errorf("bad error: want %v, got %v", rep, receivedRep) + } +} + +func TestNodeValidateUser(t *testing.T) { + tests := []struct { + in NodeUser + out error + }{ + { + NodeUser{util.IntToPtr(0), util.StrToPtr("")}, + nil, + }, + { + NodeUser{util.IntToPtr(1000), util.StrToPtr("")}, + nil, + }, + { + NodeUser{nil, util.StrToPtr("core")}, + nil, + }, + { + NodeUser{util.IntToPtr(1000), util.StrToPtr("core")}, + errors.ErrBothIDAndNameSet, + }, + } + + for i, test := range tests { + r := test.in.Validate(path.ContextPath{}) + expected := report.Report{} + expected.AddOnError(path.New(""), test.out) + if !reflect.DeepEqual(expected, r) { + t.Errorf("#%d: bad report: want %v got %v", i, test.out, r) + } + } +} + +func TestNodeValidateGroup(t *testing.T) { + tests := []struct { + in NodeGroup + out error + }{ + { + NodeGroup{util.IntToPtr(0), util.StrToPtr("")}, + nil, + }, + { + NodeGroup{util.IntToPtr(1000), util.StrToPtr("")}, + nil, + }, + { + NodeGroup{nil, util.StrToPtr("core")}, + nil, + }, + { + NodeGroup{util.IntToPtr(1000), util.StrToPtr("core")}, + errors.ErrBothIDAndNameSet, + }, + } + + for i, test := range tests { + r := test.in.Validate(path.ContextPath{}) + expected := report.Report{} + expected.AddOnError(path.New(""), test.out) + if !reflect.DeepEqual(expected, r) { + t.Errorf("#%d: bad report: want %v got %v", i, test.out, r) + } + } +} diff --git a/config/v3_7_experimental/types/partition.go b/config/v3_7_experimental/types/partition.go new file mode 100644 index 000000000..1b2d97edf --- /dev/null +++ b/config/v3_7_experimental/types/partition.go @@ -0,0 +1,91 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "fmt" + "regexp" + "strings" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +const ( + guidRegexStr = "^(|[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12})$" +) + +var ( + guidRegex = regexp.MustCompile(guidRegexStr) +) + +func (p Partition) Key() string { + if p.Number != 0 { + return fmt.Sprintf("number:%d", p.Number) + } else if p.Label != nil { + return fmt.Sprintf("label:%s", *p.Label) + } else { + return "" + } +} + +func (p Partition) Validate(c path.ContextPath) (r report.Report) { + if util.IsFalse(p.ShouldExist) && + (p.Label != nil || util.NotEmpty(p.TypeGUID) || util.NotEmpty(p.GUID) || p.StartMiB != nil || p.SizeMiB != nil) { + r.AddOnError(c, errors.ErrShouldNotExistWithOthers) + } + if p.Number == 0 && p.Label == nil { + r.AddOnError(c, errors.ErrNeedLabelOrNumber) + } + + r.AddOnError(c.Append("label"), p.validateLabel()) + r.AddOnError(c.Append("guid"), validateGUID(p.GUID)) + r.AddOnError(c.Append("typeGuid"), validateGUID(p.TypeGUID)) + return +} + +func (p Partition) validateLabel() error { + if p.Label == nil { + return nil + } + // http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_entries: + // 56 (0x38) 72 bytes Partition name (36 UTF-16LE code units) + + // XXX(vc): note GPT calls it a name, we're using label for consistency + // with udev naming /dev/disk/by-partlabel/*. + if len(*p.Label) > 36 { + return errors.ErrLabelTooLong + } + + // sgdisk uses colons for delimitting compound arguments and does not allow escaping them. + if strings.Contains(*p.Label, ":") { + return errors.ErrLabelContainsColon + } + return nil +} + +func validateGUID(guidPointer *string) error { + if guidPointer == nil { + return nil + } + guid := *guidPointer + if ok := guidRegex.MatchString(guid); !ok { + return errors.ErrDoesntMatchGUIDRegex + } + return nil +} diff --git a/config/v3_7_experimental/types/partition_test.go b/config/v3_7_experimental/types/partition_test.go new file mode 100644 index 000000000..a1e1623f4 --- /dev/null +++ b/config/v3_7_experimental/types/partition_test.go @@ -0,0 +1,94 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "testing" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" +) + +func TestValidateLabel(t *testing.T) { + tests := []struct { + in *string + out error + }{ + { + util.StrToPtr("root"), + nil, + }, + { + util.StrToPtr(""), + nil, + }, + { + nil, + nil, + }, + { + util.StrToPtr("111111111111111111111111111111111111"), + nil, + }, + { + util.StrToPtr("1111111111111111111111111111111111111"), + errors.ErrLabelTooLong, + }, + { + util.StrToPtr("test:"), + errors.ErrLabelContainsColon, + }, + } + for i, test := range tests { + err := Partition{Label: test.in}.validateLabel() + if err != test.out { + t.Errorf("#%d: wanted %v, got %v", i, test.out, err) + } + } +} + +func TestValidateGUID(t *testing.T) { + tests := []struct { + in *string + out error + }{ + { + util.StrToPtr("5DFBF5F4-2848-4BAC-AA5E-0D9A20B745A6"), + nil, + }, + { + util.StrToPtr("5dfbf5f4-2848-4bac-aa5e-0d9a20b745a6"), + nil, + }, + { + util.StrToPtr(""), + nil, + }, + { + nil, + nil, + }, + { + util.StrToPtr("not-a-valid-typeguid"), + errors.ErrDoesntMatchGUIDRegex, + }, + } + for i, test := range tests { + err := validateGUID(test.in) + if err != test.out { + t.Errorf("#%d: wanted %v, got %v", i, test.out, err) + } + } +} diff --git a/config/v3_7_experimental/types/passwd.go b/config/v3_7_experimental/types/passwd.go new file mode 100644 index 000000000..4060a2a6f --- /dev/null +++ b/config/v3_7_experimental/types/passwd.go @@ -0,0 +1,23 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +func (p PasswdUser) Key() string { + return p.Name +} + +func (g PasswdGroup) Key() string { + return g.Name +} diff --git a/config/v3_7_experimental/types/path.go b/config/v3_7_experimental/types/path.go new file mode 100644 index 000000000..131e300c1 --- /dev/null +++ b/config/v3_7_experimental/types/path.go @@ -0,0 +1,42 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "path" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" +) + +func validatePath(p string) error { + if p == "" { + return errors.ErrNoPath + } + if !path.IsAbs(p) { + return errors.ErrPathRelative + } + if path.Clean(p) != p { + return errors.ErrDirtyPath + } + return nil +} + +func validatePathNilOK(p *string) error { + if util.NilOrEmpty(p) { + return nil + } + return validatePath(*p) +} diff --git a/config/v3_7_experimental/types/path_test.go b/config/v3_7_experimental/types/path_test.go new file mode 100644 index 000000000..3d199cbdf --- /dev/null +++ b/config/v3_7_experimental/types/path_test.go @@ -0,0 +1,57 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "reflect" + "testing" + + "github.com/coreos/ignition/v2/config/shared/errors" +) + +func TestPathValidate(t *testing.T) { + tests := []struct { + in string + out error + }{ + { + "/good/path", + nil, + }, + { + "/name", + nil, + }, + { + "/this/is/a/fairly/long/path/to/a/device.", + nil, + }, + { + "/this one has spaces", + nil, + }, + { + "relative/path", + errors.ErrPathRelative, + }, + } + + for i, test := range tests { + err := validatePath(test.in) + if !reflect.DeepEqual(test.out, err) { + t.Errorf("#%d: bad error: want %v, got %v", i, test.out, err) + } + } +} diff --git a/config/v3_7_experimental/types/proxy.go b/config/v3_7_experimental/types/proxy.go new file mode 100644 index 000000000..d48d210a0 --- /dev/null +++ b/config/v3_7_experimental/types/proxy.go @@ -0,0 +1,49 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "net/url" + + "github.com/coreos/ignition/v2/config/shared/errors" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (p Proxy) Validate(c path.ContextPath) (r report.Report) { + validateProxyURL(p.HTTPProxy, c.Append("httpProxy"), &r, true) + validateProxyURL(p.HTTPSProxy, c.Append("httpsProxy"), &r, false) + return +} + +func validateProxyURL(s *string, p path.ContextPath, r *report.Report, httpOk bool) { + if s == nil { + return + } + u, err := url.Parse(*s) + if err != nil { + r.AddOnError(p, errors.ErrInvalidUrl) + return + } + + if u.Scheme != "https" && u.Scheme != "http" { + r.AddOnError(p, errors.ErrInvalidProxy) + return + } + if u.Scheme == "http" && !httpOk { + r.AddOnWarn(p, errors.ErrInsecureProxy) + } +} diff --git a/config/v3_7_experimental/types/proxy_test.go b/config/v3_7_experimental/types/proxy_test.go new file mode 100644 index 000000000..269f4fb22 --- /dev/null +++ b/config/v3_7_experimental/types/proxy_test.go @@ -0,0 +1,112 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "reflect" + "testing" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func TestValidateProxyURL(t *testing.T) { + tests := []struct { + in *string + httpOk bool + out report.Entry + }{ + { + nil, + false, + report.Entry{}, + }, + { + nil, + true, + report.Entry{}, + }, + { + util.StrToPtr("https://example.com"), + false, + report.Entry{}, + }, + { + util.StrToPtr("https://example.com"), + true, + report.Entry{}, + }, + { + util.StrToPtr("http://example.com"), + false, + report.Entry{ + Kind: report.Warn, + Message: errors.ErrInsecureProxy.Error(), + }, + }, + { + util.StrToPtr("http://example.com"), + true, + report.Entry{}, + }, + { + util.StrToPtr("ftp://example.com"), + false, + report.Entry{ + Kind: report.Error, + Message: errors.ErrInvalidProxy.Error(), + }, + }, + { + util.StrToPtr("ftp://example.com"), + true, + report.Entry{ + Kind: report.Error, + Message: errors.ErrInvalidProxy.Error(), + }, + }, + { + util.StrToPtr("http://[::1]a"), + false, + report.Entry{ + Kind: report.Error, + Message: errors.ErrInvalidUrl.Error(), + }, + }, + { + util.StrToPtr("http://[::1]a"), + true, + report.Entry{ + Kind: report.Error, + Message: errors.ErrInvalidUrl.Error(), + }, + }, + } + + for i, test := range tests { + r := report.Report{} + validateProxyURL(test.in, path.New(""), &r, test.httpOk) + e := report.Entry{} + if len(r.Entries) > 0 { + e = r.Entries[0] + } + if !reflect.DeepEqual(test.out, e) { + t.Errorf("#%d: bad error: want %v, got %v", i, test.out, e) + } + } +} diff --git a/config/v3_7_experimental/types/raid.go b/config/v3_7_experimental/types/raid.go new file mode 100644 index 000000000..9d69aa366 --- /dev/null +++ b/config/v3_7_experimental/types/raid.go @@ -0,0 +1,62 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (r Raid) Key() string { + return r.Name +} + +func (r Raid) IgnoreDuplicates() map[string]struct{} { + return map[string]struct{}{ + "Options": {}, + } +} + +func (ra Raid) Validate(c path.ContextPath) (r report.Report) { + r.AddOnError(c.Append("level"), ra.validateLevel()) + if len(ra.Devices) == 0 { + r.AddOnError(c.Append("devices"), errors.ErrRaidDevicesRequired) + } + return +} + +func (r Raid) validateLevel() error { + if util.NilOrEmpty(r.Level) { + return errors.ErrRaidLevelRequired + } + switch *r.Level { + case "linear", "raid0", "0", "stripe": + if r.Spares != nil && *r.Spares != 0 { + return errors.ErrSparesUnsupportedForLevel + } + case "raid1", "1", "mirror": + case "raid4", "4": + case "raid5", "5": + case "raid6", "6": + case "raid10", "10": + default: + return errors.ErrUnrecognizedRaidLevel + } + + return nil +} diff --git a/config/v3_7_experimental/types/raid_test.go b/config/v3_7_experimental/types/raid_test.go new file mode 100644 index 000000000..7323b26fb --- /dev/null +++ b/config/v3_7_experimental/types/raid_test.go @@ -0,0 +1,88 @@ +// Copyright 2021 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "reflect" + "testing" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func TestRaidValidate(t *testing.T) { + tests := []struct { + in Raid + at path.ContextPath + out error + }{ + { + in: Raid{ + Name: "name", + Level: util.StrToPtr("0"), + Devices: []Device{"/dev/fd0"}, + Spares: util.IntToPtr(0), + }, + out: nil, + }, + { + in: Raid{ + Name: "name", + Devices: []Device{"/dev/fd0"}, + }, + at: path.New("", "level"), + out: errors.ErrRaidLevelRequired, + }, + { + in: Raid{ + Name: "name", + Level: util.StrToPtr("0"), + Devices: []Device{"/dev/fd0"}, + Spares: util.IntToPtr(1), + }, + at: path.New("", "level"), + out: errors.ErrSparesUnsupportedForLevel, + }, + { + in: Raid{ + Name: "name", + Devices: []Device{"/dev/fd0"}, + Level: util.StrToPtr("zzz"), + }, + at: path.New("", "level"), + out: errors.ErrUnrecognizedRaidLevel, + }, + { + in: Raid{ + Name: "name", + Level: util.StrToPtr("0"), + }, + at: path.New("", "devices"), + out: errors.ErrRaidDevicesRequired, + }, + } + + for i, test := range tests { + r := test.in.Validate(path.ContextPath{}) + expected := report.Report{} + expected.AddOnError(test.at, test.out) + if !reflect.DeepEqual(expected, r) { + t.Errorf("#%d: bad report: want %v, got %v", i, expected, r) + } + } +} diff --git a/config/v3_7_experimental/types/resource.go b/config/v3_7_experimental/types/resource.go new file mode 100644 index 000000000..68da6c7b7 --- /dev/null +++ b/config/v3_7_experimental/types/resource.go @@ -0,0 +1,91 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "net/url" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (res Resource) Key() string { + if res.Source == nil { + return "" + } + return *res.Source +} + +func (res Resource) Validate(c path.ContextPath) (r report.Report) { + r.AddOnError(c.Append("compression"), res.validateCompression()) + r.AddOnError(c.Append("verification", "hash"), res.validateVerification()) + r.AddOnError(c.Append("source"), validateURLNilOK(res.Source)) + r.AddOnError(c.Append("httpHeaders"), res.validateSchemeForHTTPHeaders()) + return +} + +func (res Resource) validateCompression() error { + if res.Compression != nil { + switch *res.Compression { + case "", "gzip": + default: + return errors.ErrCompressionInvalid + } + } + return nil +} + +func (res Resource) validateVerification() error { + if res.Verification.Hash != nil && res.Source == nil { + return errors.ErrVerificationAndNilSource + } + return nil +} + +func (res Resource) validateSchemeForHTTPHeaders() error { + if len(res.HTTPHeaders) < 1 { + return nil + } + + if util.NilOrEmpty(res.Source) { + return errors.ErrInvalidUrl + } + + u, err := url.Parse(*res.Source) + if err != nil { + return errors.ErrInvalidUrl + } + + switch u.Scheme { + case "http", "https": + return nil + default: + return errors.ErrUnsupportedSchemeForHTTPHeaders + } +} + +// Ensure that the Source is specified and valid. This is not called by +// Resource.Validate() because some structs that embed Resource don't +// require Source to be specified. Containing structs that require Source +// should call this function from their Validate(). +func (res Resource) validateRequiredSource() error { + if util.NilOrEmpty(res.Source) { + return errors.ErrSourceRequired + } + return validateURL(*res.Source) +} diff --git a/config/v3_7_experimental/types/schema.go b/config/v3_7_experimental/types/schema.go new file mode 100644 index 000000000..b3220155d --- /dev/null +++ b/config/v3_7_experimental/types/schema.go @@ -0,0 +1,264 @@ +package types + +// generated by "schematyper --package=types config/v3_7_experimental/schema/ignition.json -o config/v3_7_experimental/types/schema.go --root-type=Config" -- DO NOT EDIT + +type Cex struct { + Enabled *bool `json:"enabled,omitempty"` +} + +type Clevis struct { + Custom ClevisCustom `json:"custom,omitempty"` + Tang []Tang `json:"tang,omitempty"` + Threshold *int `json:"threshold,omitempty"` + Tpm2 *bool `json:"tpm2,omitempty"` +} + +type ClevisCustom struct { + Config *string `json:"config,omitempty"` + NeedsNetwork *bool `json:"needsNetwork,omitempty"` + Pin *string `json:"pin,omitempty"` +} + +type Config struct { + Ignition Ignition `json:"ignition"` + KernelArguments KernelArguments `json:"kernelArguments,omitempty"` + Passwd Passwd `json:"passwd,omitempty"` + Storage Storage `json:"storage,omitempty"` + Systemd Systemd `json:"systemd,omitempty"` +} + +type Device string + +type Directory struct { + Node + DirectoryEmbedded1 +} + +type DirectoryEmbedded1 struct { + Mode *int `json:"mode,omitempty"` +} + +type Disk struct { + Device string `json:"device"` + Partitions []Partition `json:"partitions,omitempty"` + WipeTable *bool `json:"wipeTable,omitempty"` +} + +type Dropin struct { + Contents *string `json:"contents,omitempty"` + Name string `json:"name"` +} + +type File struct { + Node + FileEmbedded1 +} + +type FileEmbedded1 struct { + Append []Resource `json:"append,omitempty"` + Contents Resource `json:"contents,omitempty"` + Mode *int `json:"mode,omitempty"` +} + +type Filesystem struct { + Device string `json:"device"` + Format *string `json:"format,omitempty"` + Label *string `json:"label,omitempty"` + MountOptions []MountOption `json:"mountOptions,omitempty"` + Options []FilesystemOption `json:"options,omitempty"` + Path *string `json:"path,omitempty"` + UUID *string `json:"uuid,omitempty"` + WipeFilesystem *bool `json:"wipeFilesystem,omitempty"` +} + +type FilesystemOption string + +type Group string + +type HTTPHeader struct { + Name string `json:"name"` + Value *string `json:"value,omitempty"` +} + +type HTTPHeaders []HTTPHeader + +type Ignition struct { + Config IgnitionConfig `json:"config,omitempty"` + Proxy Proxy `json:"proxy,omitempty"` + Security Security `json:"security,omitempty"` + Timeouts Timeouts `json:"timeouts,omitempty"` + Version string `json:"version"` +} + +type IgnitionConfig struct { + Merge []Resource `json:"merge,omitempty"` + Replace Resource `json:"replace,omitempty"` +} + +type KernelArgument string + +type KernelArguments struct { + ShouldExist []KernelArgument `json:"shouldExist,omitempty"` + ShouldNotExist []KernelArgument `json:"shouldNotExist,omitempty"` +} + +type Link struct { + Node + LinkEmbedded1 +} + +type LinkEmbedded1 struct { + Hard *bool `json:"hard,omitempty"` + Target *string `json:"target,omitempty"` +} + +type Luks struct { + Cex Cex `json:"cex,omitempty"` + Clevis Clevis `json:"clevis,omitempty"` + Device *string `json:"device,omitempty"` + Discard *bool `json:"discard,omitempty"` + KeyFile Resource `json:"keyFile,omitempty"` + Label *string `json:"label,omitempty"` + Name string `json:"name"` + OpenOptions []OpenOption `json:"openOptions,omitempty"` + Options []LuksOption `json:"options,omitempty"` + UUID *string `json:"uuid,omitempty"` + WipeVolume *bool `json:"wipeVolume,omitempty"` +} + +type LuksOption string + +type MountOption string + +type NoProxyItem string + +type Node struct { + Group NodeGroup `json:"group,omitempty"` + Overwrite *bool `json:"overwrite,omitempty"` + Path string `json:"path"` + User NodeUser `json:"user,omitempty"` +} + +type NodeGroup struct { + ID *int `json:"id,omitempty"` + Name *string `json:"name,omitempty"` +} + +type NodeUser struct { + ID *int `json:"id,omitempty"` + Name *string `json:"name,omitempty"` +} + +type OpenOption string + +type Partition struct { + GUID *string `json:"guid,omitempty"` + Label *string `json:"label,omitempty"` + Number int `json:"number,omitempty"` + Resize *bool `json:"resize,omitempty"` + ShouldExist *bool `json:"shouldExist,omitempty"` + SizeMiB *int `json:"sizeMiB,omitempty"` + StartMiB *int `json:"startMiB,omitempty"` + TypeGUID *string `json:"typeGuid,omitempty"` + WipePartitionEntry *bool `json:"wipePartitionEntry,omitempty"` +} + +type Passwd struct { + Groups []PasswdGroup `json:"groups,omitempty"` + Users []PasswdUser `json:"users,omitempty"` +} + +type PasswdGroup struct { + Gid *int `json:"gid,omitempty"` + Name string `json:"name"` + PasswordHash *string `json:"passwordHash,omitempty"` + ShouldExist *bool `json:"shouldExist,omitempty"` + System *bool `json:"system,omitempty"` +} + +type PasswdUser struct { + Gecos *string `json:"gecos,omitempty"` + Groups []Group `json:"groups,omitempty"` + HomeDir *string `json:"homeDir,omitempty"` + Name string `json:"name"` + NoCreateHome *bool `json:"noCreateHome,omitempty"` + NoLogInit *bool `json:"noLogInit,omitempty"` + NoUserGroup *bool `json:"noUserGroup,omitempty"` + PasswordHash *string `json:"passwordHash,omitempty"` + PrimaryGroup *string `json:"primaryGroup,omitempty"` + SSHAuthorizedKeys []SSHAuthorizedKey `json:"sshAuthorizedKeys,omitempty"` + Shell *string `json:"shell,omitempty"` + ShouldExist *bool `json:"shouldExist,omitempty"` + System *bool `json:"system,omitempty"` + UID *int `json:"uid,omitempty"` +} + +type Proxy struct { + HTTPProxy *string `json:"httpProxy,omitempty"` + HTTPSProxy *string `json:"httpsProxy,omitempty"` + NoProxy []NoProxyItem `json:"noProxy,omitempty"` +} + +type Raid struct { + Devices []Device `json:"devices,omitempty"` + Level *string `json:"level,omitempty"` + Name string `json:"name"` + Options []RaidOption `json:"options,omitempty"` + Spares *int `json:"spares,omitempty"` +} + +type RaidOption string + +type Resource struct { + Compression *string `json:"compression,omitempty"` + HTTPHeaders HTTPHeaders `json:"httpHeaders,omitempty"` + Source *string `json:"source,omitempty"` + Verification Verification `json:"verification,omitempty"` +} + +type SSHAuthorizedKey string + +type Security struct { + TLS TLS `json:"tls,omitempty"` +} + +type Storage struct { + Directories []Directory `json:"directories,omitempty"` + Disks []Disk `json:"disks,omitempty"` + Files []File `json:"files,omitempty"` + Filesystems []Filesystem `json:"filesystems,omitempty"` + Links []Link `json:"links,omitempty"` + Luks []Luks `json:"luks,omitempty"` + Raid []Raid `json:"raid,omitempty"` +} + +type Systemd struct { + Units []Unit `json:"units,omitempty"` +} + +type TLS struct { + CertificateAuthorities []Resource `json:"certificateAuthorities,omitempty"` +} + +type Tang struct { + Advertisement *string `json:"advertisement,omitempty"` + Thumbprint *string `json:"thumbprint,omitempty"` + URL string `json:"url,omitempty"` +} + +type Timeouts struct { + HTTPResponseHeaders *int `json:"httpResponseHeaders,omitempty"` + HTTPTotal *int `json:"httpTotal,omitempty"` +} + +type Unit struct { + Contents *string `json:"contents,omitempty"` + Dropins []Dropin `json:"dropins,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + Mask *bool `json:"mask,omitempty"` + Name string `json:"name"` +} + +type Verification struct { + Hash *string `json:"hash,omitempty"` +} diff --git a/config/v3_7_experimental/types/storage.go b/config/v3_7_experimental/types/storage.go new file mode 100644 index 000000000..20cb73048 --- /dev/null +++ b/config/v3_7_experimental/types/storage.go @@ -0,0 +1,115 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "path" + "strings" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + vpath "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (s Storage) MergedKeys() map[string]string { + return map[string]string{ + "Directories": "Node", + "Files": "Node", + "Links": "Node", + } +} + +func (s Storage) Validate(c vpath.ContextPath) (r report.Report) { + s.validateDirectories(c, &r) + s.validateFiles(c, &r) + s.validateLinks(c, &r) + s.validateFilesystems(c, &r) + return +} + +func (s Storage) validateDirectories(c vpath.ContextPath, r *report.Report) { + for i, d := range s.Directories { + for _, l := range s.Links { + if strings.HasPrefix(d.Path, l.Path+"/") { + r.AddOnError(c.Append("directories", i), errors.ErrDirectoryUsedSymlink) + } + } + } +} + +func (s Storage) validateFiles(c vpath.ContextPath, r *report.Report) { + for i, f := range s.Files { + for _, l := range s.Links { + if strings.HasPrefix(f.Path, l.Path+"/") { + r.AddOnError(c.Append("files", i), errors.ErrFileUsedSymlink) + } + } + } +} + +func (s Storage) validateLinks(c vpath.ContextPath, r *report.Report) { + for i, l1 := range s.Links { + for _, l2 := range s.Links { + if strings.HasPrefix(l1.Path, l2.Path+"/") { + r.AddOnError(c.Append("links", i), errors.ErrLinkUsedSymlink) + } + } + if util.NilOrEmpty(l1.Target) { + r.AddOnError(c.Append("links", i, "target"), errors.ErrLinkTargetRequired) + continue + } + if !util.IsTrue(l1.Hard) { + continue + } + target := path.Clean(*l1.Target) + if !path.IsAbs(target) { + target = path.Join(l1.Path, *l1.Target) + } + for _, d := range s.Directories { + if target == d.Path { + r.AddOnError(c.Append("links", i), errors.ErrHardLinkToDirectory) + } + } + ownerCheck := func(ok bool, path vpath.ContextPath) { + if !ok { + r.AddOnWarn(path, errors.ErrHardLinkSpecifiesOwner) + } + } + ownerCheck(l1.User.ID == nil, c.Append("links", i, "user", "id")) + ownerCheck(l1.User.Name == nil, c.Append("links", i, "user", "name")) + ownerCheck(l1.Group.ID == nil, c.Append("links", i, "group", "id")) + ownerCheck(l1.Group.Name == nil, c.Append("links", i, "group", "name")) + } +} + +func (s Storage) validateFilesystems(c vpath.ContextPath, r *report.Report) { + disks := make(map[string]Disk) + for _, d := range s.Disks { + disks[d.Device] = d + } + + for i, f := range s.Filesystems { + disk, exist := disks[f.Device] + if exist { + if len(disk.Partitions) > 0 { + r.AddOnWarn(c.Append("filesystems", i, "device"), errors.ErrPartitionsOverwritten) + } else if !util.IsTrue(f.WipeFilesystem) && util.IsTrue(disk.WipeTable) { + r.AddOnWarn(c.Append("filesystems", i, "device"), errors.ErrFilesystemImplicitWipe) + } + } + } +} diff --git a/config/v3_7_experimental/types/storage_test.go b/config/v3_7_experimental/types/storage_test.go new file mode 100644 index 000000000..551688a27 --- /dev/null +++ b/config/v3_7_experimental/types/storage_test.go @@ -0,0 +1,359 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "reflect" + "testing" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func TestStorageValidateErrors(t *testing.T) { + tests := []struct { + in Storage + at path.ContextPath + err error + warn error + }{ + // test empty storage config returns nil + { + in: Storage{}, + }, + // test a storage config with no conflicting paths returns nil + { + in: Storage{ + Links: []Link{ + { + Node: Node{Path: "/foo"}, + LinkEmbedded1: LinkEmbedded1{Target: util.StrToPtr("/foo-t")}, + }, + { + Node: Node{Path: "/quux"}, + LinkEmbedded1: LinkEmbedded1{Target: util.StrToPtr("/quux-t")}, + }, + }, + Files: []File{ + { + Node: Node{Path: "/bar"}, + }, + }, + Directories: []Directory{ + { + Node: Node{Path: "/baz"}, + }, + }, + }, + }, + // test when a file uses a configured symlink path returns ErrFileUsedSymlink + { + in: Storage{ + Links: []Link{ + { + Node: Node{Path: "/foo"}, + LinkEmbedded1: LinkEmbedded1{Target: util.StrToPtr("/foo-t")}, + }, + }, + Files: []File{ + { + Node: Node{Path: "/foo/bar"}, + }, + }, + }, + err: errors.ErrFileUsedSymlink, + at: path.New("", "files", 0), + }, + // test when a directory uses a configured symlink path returns ErrDirectoryUsedSymlink + { + in: Storage{ + Links: []Link{ + { + Node: Node{Path: "/foo"}, + LinkEmbedded1: LinkEmbedded1{Target: util.StrToPtr("/foo-t")}, + }, + }, + Directories: []Directory{ + { + Node: Node{Path: "/foo/bar"}, + }, + }, + }, + err: errors.ErrDirectoryUsedSymlink, + at: path.New("", "directories", 0), + }, + // test the same path listed for two separate symlinks returns ErrLinkUsedSymlink + { + in: Storage{ + Links: []Link{ + { + Node: Node{Path: "/foo"}, + LinkEmbedded1: LinkEmbedded1{Target: util.StrToPtr("/foo-t")}, + }, + { + Node: Node{Path: "/foo/bar"}, + LinkEmbedded1: LinkEmbedded1{Target: util.StrToPtr("/foo-bar-t")}, + }, + }, + }, + err: errors.ErrLinkUsedSymlink, + at: path.New("", "links", 1), + }, + // test a configured symlink with no target returns ErrLinkTargetRequired + { + in: Storage{ + Links: []Link{ + { + Node: Node{Path: "/foo"}, + LinkEmbedded1: LinkEmbedded1{Hard: util.BoolToPtr(true)}, + }, + }, + }, + err: errors.ErrLinkTargetRequired, + at: path.New("", "links", 0, "target"), + }, + // test a configured symlink with a nil target returns ErrLinkTargetRequired + { + in: Storage{ + Links: []Link{ + { + Node: Node{Path: "/foo"}, + LinkEmbedded1: LinkEmbedded1{Target: util.StrToPtr("")}, + }, + }, + }, + err: errors.ErrLinkTargetRequired, + at: path.New("", "links", 0, "target"), + }, + // test that two symlinks can be configured at a time + { + in: Storage{ + Links: []Link{ + { + Node: Node{Path: "/foo"}, + LinkEmbedded1: LinkEmbedded1{Target: util.StrToPtr("/foo-t")}, + }, + { + Node: Node{Path: "/foob/bar"}, + LinkEmbedded1: LinkEmbedded1{Target: util.StrToPtr("/foob-bar-t")}, + }, + }, + }, + }, + // test when a directory uses a configured symlink with the 'Hard:= true' returns ErrHardLinkToDirectory + { + in: Storage{ + Links: []Link{ + { + Node: Node{Path: "/quux"}, + LinkEmbedded1: LinkEmbedded1{ + Target: util.StrToPtr("/foo/bar"), + Hard: util.BoolToPtr(true), + }, + }, + }, + Directories: []Directory{ + { + Node: Node{Path: "/foo/bar"}, + }, + }, + }, + err: errors.ErrHardLinkToDirectory, + at: path.New("", "links", 0), + }, + { + in: Storage{ + Links: []Link{ + { + Node: Node{ + Path: "/quux", + User: NodeUser{ + ID: util.IntToPtr(10), + }, + }, + LinkEmbedded1: LinkEmbedded1{ + Target: util.StrToPtr("/foo/bar"), + Hard: util.BoolToPtr(true), + }, + }, + }, + }, + warn: errors.ErrHardLinkSpecifiesOwner, + at: path.New("", "links", 0, "user", "id"), + }, + { + in: Storage{ + Links: []Link{ + { + Node: Node{ + Path: "/quux", + User: NodeUser{ + Name: util.StrToPtr("bovik"), + }, + }, + LinkEmbedded1: LinkEmbedded1{ + Target: util.StrToPtr("/foo/bar"), + Hard: util.BoolToPtr(true), + }, + }, + }, + }, + warn: errors.ErrHardLinkSpecifiesOwner, + at: path.New("", "links", 0, "user", "name"), + }, + { + in: Storage{ + Links: []Link{ + { + Node: Node{ + Path: "/quux", + Group: NodeGroup{ + ID: util.IntToPtr(10), + }, + }, + LinkEmbedded1: LinkEmbedded1{ + Target: util.StrToPtr("/foo/bar"), + Hard: util.BoolToPtr(true), + }, + }, + }, + }, + warn: errors.ErrHardLinkSpecifiesOwner, + at: path.New("", "links", 0, "group", "id"), + }, + { + in: Storage{ + Links: []Link{ + { + Node: Node{ + Path: "/quux", + Group: NodeGroup{ + Name: util.StrToPtr("bovik"), + }, + }, + LinkEmbedded1: LinkEmbedded1{ + Target: util.StrToPtr("/foo/bar"), + Hard: util.BoolToPtr(true), + }, + }, + }, + }, + warn: errors.ErrHardLinkSpecifiesOwner, + at: path.New("", "links", 0, "group", "name"), + }, + } + + for i, test := range tests { + r := test.in.Validate(path.ContextPath{}) + expected := report.Report{} + expected.AddOnError(test.at, test.err) + expected.AddOnWarn(test.at, test.warn) + if !reflect.DeepEqual(expected, r) { + t.Errorf("#%d: bad report: want %v, got %v", i, expected, r) + } + } +} + +func TestStorageValidateWarnings(t *testing.T) { + tests := []struct { + in Storage + at path.ContextPath + out error + }{ + // test a disk with partitions with the same 'device' as a filesystem returns ErrPartitionsOverwritten + { + in: Storage{ + Disks: []Disk{ + { + Device: "/dev/sda", + Partitions: []Partition{ + {}, {}, + }, + }, + }, + Filesystems: []Filesystem{ + { + Device: "/dev/sda", + }, + }, + }, + out: errors.ErrPartitionsOverwritten, + at: path.New("", "filesystems", 0, "device"), + }, + // test a disk with the same 'device' and 'WipeTable:=true' as a configured filesystem returns ErrFilesystemImplicitWipe + { + in: Storage{ + Disks: []Disk{ + { + Device: "/dev/sda", + WipeTable: util.BoolToPtr(true), + }, + }, + Filesystems: []Filesystem{ + { + Device: "/dev/sda", + }, + }, + }, + out: errors.ErrFilesystemImplicitWipe, + at: path.New("", "filesystems", 0, "device"), + }, + // test a disk with the same 'device' and 'WipeTable:=false' as a configured filesystem returns nil + { + in: Storage{ + Disks: []Disk{ + { + Device: "/dev/sda", + WipeTable: util.BoolToPtr(false), + }, + }, + Filesystems: []Filesystem{ + { + Device: "/dev/sda", + }, + }, + }, + out: nil, + }, + // test a disk with no partitions with the same 'device' as a filesystem returns nil + { + in: Storage{ + Disks: []Disk{ + { + Device: "/dev/sdb", + }, + }, + Filesystems: []Filesystem{ + { + Device: "/dev/sdb", + }, + }, + }, + out: nil, + }, + } + + for i, test := range tests { + r := test.in.Validate(path.ContextPath{}) + expected := report.Report{} + expected.AddOnWarn(test.at, test.out) + if !reflect.DeepEqual(expected, r) { + t.Errorf("#%d: bad report: want %v, got %v", i, expected, r) + } + } +} diff --git a/config/v3_7_experimental/types/systemd.go b/config/v3_7_experimental/types/systemd.go new file mode 100644 index 000000000..ac521ba73 --- /dev/null +++ b/config/v3_7_experimental/types/systemd.go @@ -0,0 +1,61 @@ +// Copyright 2022 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "regexp" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/shared/parse" + "github.com/coreos/ignition/v2/config/util" + + vpath "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (s Systemd) Validate(c vpath.ContextPath) (r report.Report) { + units := make(map[string]Unit) + checkInstanceUnit := regexp.MustCompile(`^(.+?)@(.+?)\.service$`) + for _, d := range s.Units { + units[d.Name] = d + } + for index, unit := range s.Units { + if checkInstanceUnit.MatchString(unit.Name) && util.IsTrue(unit.Enabled) { + instUnitSlice := checkInstanceUnit.FindSubmatch([]byte(unit.Name)) + instantiableUnit := string(instUnitSlice[1]) + "@.service" + if _, ok := units[instantiableUnit]; ok && util.NotEmpty(units[instantiableUnit].Contents) { + foundInstallSection := false + // we're doing a separate validation pass on each unit to identify + // if an instantiable unit has the install section. So logging an + // `AddOnError` will produce duplicate errors on bad unit contents + // because we're already doing that while validating a unit separately. + opts, err := parse.ParseUnitContents(units[instantiableUnit].Contents) + if err != nil { + continue + } + for _, section := range opts { + if section.Section == "Install" { + foundInstallSection = true + break + } + } + if !foundInstallSection { + r.AddOnWarn(c.Append("units", index, "contents"), errors.NewNoInstallSectionForInstantiableUnitError(instantiableUnit, unit.Name)) + } + } + } + } + return +} diff --git a/config/v3_7_experimental/types/systemd_test.go b/config/v3_7_experimental/types/systemd_test.go new file mode 100644 index 000000000..798eb10f4 --- /dev/null +++ b/config/v3_7_experimental/types/systemd_test.go @@ -0,0 +1,105 @@ +// Copyright 2022 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "fmt" + "testing" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" + "github.com/stretchr/testify/assert" +) + +func TestSystemdValidate(t *testing.T) { + tests := []struct { + in Systemd + out error + }{ + { + Systemd{ + []Unit{ + {Name: "test@.service", Contents: util.StrToPtr("[Foo]\nQux=Bar")}, + {Name: "test@foo.service", Enabled: util.BoolToPtr(true)}, + }, + }, + errors.NewNoInstallSectionForInstantiableUnitError("test@.service", "test@foo.service"), + }, + { + Systemd{ + []Unit{ + {Name: "test2@.service", Contents: util.StrToPtr("[Foo]\nQux=Bar")}, + }, + }, + nil, + }, + { + Systemd{ + []Unit{ + {Name: "test@.service", Contents: util.StrToPtr("[Foo]\nQux=Bar")}, + {Name: "test@foo.service", Enabled: util.BoolToPtr(false)}, + }, + }, + nil, + }, + { + Systemd{ + []Unit{ + {Name: "test2@.service", Contents: util.StrToPtr("[Unit]\nDescription=echo service template\n[Service]\nType=oneshot\nExecStart=/bin/echo %i\n[Install]\nWantedBy=multi-user.target\n")}, + {Name: "test2@foo.service", Enabled: util.BoolToPtr(false)}, + }, + }, + nil, + }, + { + Systemd{ + []Unit{ + {Name: "test2@.service", Contents: util.StrToPtr("[Unit]\nDescription=echo service template\n[Service]\nType=oneshot\nExecStart=/bin/echo %i\n[Install]\nWantedBy=multi-user.target\n")}, + {Name: "test2@bar.service", Enabled: util.BoolToPtr(true)}, + }, + }, + nil, + }, + { + Systemd{ + []Unit{ + {Name: "test@.service", Contents: util.StrToPtr("[Unit]\nDescription=echo service template\n[Service]\nType=oneshot\nExecStart=/bin/echo %i\n[Install]\nWantedBy=multi-user.target\n")}, + {Name: "test2@foo.service", Enabled: util.BoolToPtr(true)}, + }, + }, + nil, + }, + { + Systemd{ + []Unit{ + {Name: "test@.service"}, + {Name: "test@bar.service", Enabled: util.BoolToPtr(true)}, + }, + }, + nil, + }, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("validate %d", i), func(t *testing.T) { + actual := test.in.Validate(path.ContextPath{}) + expected := report.Report{} + expected.AddOnWarn(path.ContextPath{}.Append("units", 1, "contents"), test.out) + assert.Equal(t, expected, actual, "bad report") + }) + } +} diff --git a/config/v3_7_experimental/types/tang.go b/config/v3_7_experimental/types/tang.go new file mode 100644 index 000000000..1839d6cc3 --- /dev/null +++ b/config/v3_7_experimental/types/tang.go @@ -0,0 +1,65 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "encoding/json" + "net/url" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (t Tang) Key() string { + return t.URL +} + +func (t Tang) Validate(c path.ContextPath) (r report.Report) { + r.AddOnError(c.Append("url"), validateTangURL(t.URL)) + if util.NilOrEmpty(t.Thumbprint) { + r.AddOnError(c.Append("thumbprint"), errors.ErrTangThumbprintRequired) + } + r.AddOnError(c.Append("advertisement"), validateTangAdvertisement(t.Advertisement)) + return +} + +func validateTangURL(s string) error { + u, err := url.Parse(s) + if err != nil { + return errors.ErrInvalidUrl + } + + switch u.Scheme { + case "http", "https": + return nil + default: + return errors.ErrInvalidScheme + } +} + +func validateTangAdvertisement(s *string) error { + if util.NotEmpty(s) { + var adv any + err := json.Unmarshal([]byte(*s), &adv) + if err != nil { + return errors.ErrInvalidTangAdvertisement + } + } + + return nil +} diff --git a/config/v3_7_experimental/types/tang_test.go b/config/v3_7_experimental/types/tang_test.go new file mode 100644 index 000000000..fb57b1e4a --- /dev/null +++ b/config/v3_7_experimental/types/tang_test.go @@ -0,0 +1,104 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "reflect" + "testing" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func TestTangValidation(t *testing.T) { + tests := []struct { + in Tang + out error + at path.ContextPath + }{ + // happy path with no advertisement and healthy url + { + in: Tang{ + URL: "http://example.com", + Thumbprint: util.StrToPtr("abc"), + }, + out: nil, + }, + // test 1: invalid url scheme + { + in: Tang{ + URL: "httasfdsafadsf", + Thumbprint: util.StrToPtr("abc"), + }, + out: errors.ErrInvalidScheme, + at: path.New("foo", "url"), + }, + // null url + { + in: Tang{ + Thumbprint: util.StrToPtr("abc"), + }, + out: errors.ErrInvalidScheme, + at: path.New("foo", "url"), + }, + // null thumbprint + { + in: Tang{ + URL: "http://example.com", + Thumbprint: nil, + }, + out: errors.ErrTangThumbprintRequired, + at: path.New("foo", "thumbprint"), + }, + // Advertisement is valid json + { + in: Tang{ + URL: "http://example.com", + Thumbprint: util.StrToPtr("abc"), + Advertisement: util.StrToPtr("{\"payload\": \"eyJrZXlzIjogW3siYWxnIjogIkVTNTEyIiwgImt0eSI6ICJFQyIsICJjcnYiOiAiUC01MjEiLCAieCI6ICJBRGFNajJmazNob21CWTF5WElSQ21uRk92cmUzOFZjdHMwTnNHeDZ6RWNxdEVXcjh5ekhUMkhfa2hjNGpSa19FQWFLdjNrd2RjZ05sOTBLcGhfMGYyQ190IiwgInkiOiAiQUZ2d1UyeGJ5T1RydWo0V1NtcVlqN2wtcUVTZmhWakdCNTI1Q2d6d0NoZUZRRTBvb1o3STYyamt3NkRKQ05yS3VPUDRsSEhicm8tYXhoUk9MSXNJVExvNCIsICJrZXlfb3BzIjogWyJ2ZXJpZnkiXX0sIHsiYWxnIjogIkVDTVIiLCAia3R5IjogIkVDIiwgImNydiI6ICJQLTUyMSIsICJ4IjogIkFOZDVYcTFvZklUbTdNWG16OUY0VVRSYmRNZFNIMl9XNXczTDVWZ0w3b3hwdmpyM0hkLXNLNUVqd3A1V2swMnJMb3NXVUJjYkZyZEhjZFJTTVJoZlVFTFIiLCAieSI6ICJBRVVaVlVZWkFBY2hVcmdoX3poaTV3SUUzeTEycGwzeWhqUk5LcGpSdW9tUFhKaDhRaFhXRmRWZEtMUlEwX1lwUjNOMjNSUk1pU1lvWlg0Qm42QnlrQVBMIiwgImtleV9vcHMiOiBbImRlcml2ZUtleSJdfV19\", \"protected\": \"eyJhbGciOiJFUzUxMiIsImN0eSI6Imp3ay1zZXQranNvbiJ9\", \"signature\": \"APHfSyVzLwELwG0pMJyIP74gWvhHUvDtv0SESBxA2uOdSXq76IdWHW2xvCZDdlNan8pnqUvEedPZjf_vdKBw9MTXAPMkRxVnu64HepKwlrzzm_zG2R4CHpoCOsGgjH9-acYxg-Vha63oMojv3_bV0VHg-NbzNLaxietgYplstvcNIwkv\"}"), + }, + out: nil, + }, + // Advertisement is empty string + { + in: Tang{ + URL: "http://example.com", + Thumbprint: util.StrToPtr("abc"), + Advertisement: util.StrToPtr(""), + }, + out: nil, + }, + // Advertisement is not valid json + { + in: Tang{ + URL: "http://example.com", + Thumbprint: util.StrToPtr("abc"), + Advertisement: util.StrToPtr("{{"), + }, + out: errors.ErrInvalidTangAdvertisement, + at: path.New("foo", "advertisement"), + }, + } + for i, test := range tests { + r := test.in.Validate(path.New("foo")) + expected := report.Report{} + expected.AddOnError(test.at, test.out) + if !reflect.DeepEqual(expected, r) { + t.Errorf("#%d: bad error: expected : %v, got %v", i, expected, r) + } + } +} diff --git a/config/v3_7_experimental/types/tls.go b/config/v3_7_experimental/types/tls.go new file mode 100644 index 000000000..8890e397e --- /dev/null +++ b/config/v3_7_experimental/types/tls.go @@ -0,0 +1,27 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (tls TLS) Validate(c path.ContextPath) (r report.Report) { + for i, ca := range tls.CertificateAuthorities { + r.AddOnError(c.Append("certificateAuthorities", i), ca.validateRequiredSource()) + } + return +} diff --git a/config/v3_7_experimental/types/tls_test.go b/config/v3_7_experimental/types/tls_test.go new file mode 100644 index 000000000..8f3b2e351 --- /dev/null +++ b/config/v3_7_experimental/types/tls_test.go @@ -0,0 +1,42 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "testing" + + "github.com/coreos/vcontext/validate" +) + +func TestTLSValidate(t *testing.T) { + tests := []struct { + in TLS + out string + }{ + { + TLS{ + CertificateAuthorities: []Resource{{}}, + }, + "error at $.certificateAuthorities.0: source is required\n", + }, + } + + for i, test := range tests { + r := validate.Validate(test.in, "test") + if test.out != r.String() { + t.Errorf("#%d: bad error: want %q, got %q", i, test.out, r.String()) + } + } +} diff --git a/config/v3_7_experimental/types/unit.go b/config/v3_7_experimental/types/unit.go new file mode 100644 index 000000000..c5ee1e8e3 --- /dev/null +++ b/config/v3_7_experimental/types/unit.go @@ -0,0 +1,68 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "path" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/shared/parse" + "github.com/coreos/ignition/v2/config/shared/validations" + "github.com/coreos/ignition/v2/config/util" + + cpath "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (u Unit) Key() string { + return u.Name +} + +func (d Dropin) Key() string { + return d.Name +} + +func (u Unit) Validate(c cpath.ContextPath) (r report.Report) { + r.AddOnError(c.Append("name"), validateName(u.Name)) + c = c.Append("contents") + opts, err := parse.ParseUnitContents(u.Contents) + r.AddOnError(c, err) + + r.AddOnWarn(c, validations.ValidateInstallSection(u.Name, util.IsTrue(u.Enabled), util.NilOrEmpty(u.Contents), opts)) + + return +} + +func validateName(name string) error { + switch path.Ext(name) { + case ".service", ".socket", ".device", ".mount", ".automount", ".swap", ".target", ".path", ".timer", ".snapshot", ".slice", ".scope": + default: + return errors.ErrInvalidSystemdExt + } + return nil +} + +func (d Dropin) Validate(c cpath.ContextPath) (r report.Report) { + _, err := parse.ParseUnitContents(d.Contents) + r.AddOnError(c.Append("contents"), err) + + switch path.Ext(d.Name) { + case ".conf": + default: + r.AddOnError(c.Append("name"), errors.ErrInvalidSystemdDropinExt) + } + + return +} diff --git a/config/v3_7_experimental/types/unit_test.go b/config/v3_7_experimental/types/unit_test.go new file mode 100644 index 000000000..3dc905407 --- /dev/null +++ b/config/v3_7_experimental/types/unit_test.go @@ -0,0 +1,108 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "fmt" + "reflect" + "testing" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func TestSystemdUnitValidateContents(t *testing.T) { + tests := []struct { + in Unit + out error + }{ + { + Unit{Name: "test.service", Contents: util.StrToPtr("[Foo]\nQux=Bar")}, + nil, + }, + { + Unit{Name: "test.service", Contents: util.StrToPtr("[Foo")}, + fmt.Errorf("invalid unit content: unable to find end of section"), + }, + { + Unit{Name: "test.service", Contents: util.StrToPtr(""), Dropins: []Dropin{{}}}, + nil, + }, + } + + for i, test := range tests { + err := test.in.Validate(path.ContextPath{}) + expected := report.Report{} + expected.AddOnError(path.New("", "contents"), test.out) + if !reflect.DeepEqual(expected, err) { + t.Errorf("#%d: bad error: want %v, got %v", i, expected, err) + } + } +} + +func TestSystemdUnitValidateName(t *testing.T) { + tests := []struct { + in string + out error + }{ + { + "test.service", + nil, + }, + { + "test.socket", + nil, + }, + { + "test.blah", + errors.ErrInvalidSystemdExt, + }, + } + + for i, test := range tests { + err := validateName(test.in) + if test.out != err { + t.Errorf("#%d: bad error: want %v, got %v", i, test.out, err) + } + } +} + +func TestSystemdUnitDropInValidate(t *testing.T) { + tests := []struct { + in Dropin + out error + }{ + { + Dropin{Name: "test.conf", Contents: util.StrToPtr("[Foo]\nQux=Bar")}, + nil, + }, + { + Dropin{Name: "test.conf", Contents: util.StrToPtr("[Foo")}, + fmt.Errorf("invalid unit content: unable to find end of section"), + }, + } + + for i, test := range tests { + err := test.in.Validate(path.ContextPath{}) + expected := report.Report{} + expected.AddOnError(path.New("", "contents"), test.out) + if !reflect.DeepEqual(expected, err) { + t.Errorf("#%d: bad error: want %v, got %v", i, expected, err) + } + } +} diff --git a/config/v3_7_experimental/types/url.go b/config/v3_7_experimental/types/url.go new file mode 100644 index 000000000..97a4c9ae2 --- /dev/null +++ b/config/v3_7_experimental/types/url.go @@ -0,0 +1,83 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "net/url" + "strings" + + "github.com/aws/aws-sdk-go-v2/aws/arn" + "github.com/vincent-petithory/dataurl" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" +) + +func validateURL(s string) error { + u, err := url.Parse(s) + if err != nil { + return errors.ErrInvalidUrl + } + + switch u.Scheme { + case "http", "https", "tftp", "gs": + return nil + case "s3": + if v, ok := u.Query()["versionId"]; ok { + if len(v) == 0 || v[0] == "" { + return errors.ErrInvalidS3ObjectVersionId + } + } + return nil + case "arn": + fullURL := u.Scheme + ":" + u.Opaque + if !arn.IsARN(fullURL) { + return errors.ErrInvalidS3ARN + } + s3arn, err := arn.Parse(fullURL) + if err != nil { + return err + } + if s3arn.Service != "s3" { + return errors.ErrInvalidS3ARN + } + urlSplit := strings.Split(fullURL, "/") + if strings.HasPrefix(s3arn.Resource, "accesspoint/") && len(urlSplit) < 3 { + return errors.ErrInvalidS3ARN + } else if len(urlSplit) < 2 { + return errors.ErrInvalidS3ARN + } + if v, ok := u.Query()["versionId"]; ok { + if len(v) == 0 || v[0] == "" { + return errors.ErrInvalidS3ObjectVersionId + } + } + return nil + case "data": + if _, err := dataurl.DecodeString(s); err != nil { + return err + } + return nil + default: + return errors.ErrInvalidScheme + } +} + +func validateURLNilOK(s *string) error { + if util.NilOrEmpty(s) { + return nil + } + return validateURL(*s) +} diff --git a/config/v3_7_experimental/types/url_test.go b/config/v3_7_experimental/types/url_test.go new file mode 100644 index 000000000..1901b7aff --- /dev/null +++ b/config/v3_7_experimental/types/url_test.go @@ -0,0 +1,137 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "testing" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" +) + +func TestURLValidate(t *testing.T) { + tests := []struct { + in *string + out error + }{ + { + nil, + nil, + }, + { + util.StrToPtr(""), + nil, + }, + { + util.StrToPtr("http://example.com"), + nil, + }, + { + util.StrToPtr("https://example.com"), + nil, + }, + { + util.StrToPtr("tftp://example.com:69/foobar.txt"), + nil, + }, + { + util.StrToPtr("data:,example%20file%0A"), + nil, + }, + { + util.StrToPtr("bad://"), + errors.ErrInvalidScheme, + }, + { + util.StrToPtr("s3://bucket/key"), + nil, + }, + { + util.StrToPtr("s3://bucket/key?versionId="), + errors.ErrInvalidS3ObjectVersionId, + }, + { + util.StrToPtr("s3://bucket/key?versionId=aVersionHash"), + nil, + }, + { + util.StrToPtr("Arn:"), + errors.ErrInvalidS3ARN, + }, + { + util.StrToPtr("arn:aws:iam:us-west-2:123456789012:resource"), + errors.ErrInvalidS3ARN, + }, + { + util.StrToPtr("arn:aws:s3:::bucket-name-but-no-key"), + errors.ErrInvalidS3ARN, + }, + { + util.StrToPtr("arn:aws:s3:us-west-2:123456789012:accesspoint"), + errors.ErrInvalidS3ARN, + }, + { + util.StrToPtr("arn:aws:s3:us-west-2:123456789012:accesspoint/"), + errors.ErrInvalidS3ARN, + }, + { + util.StrToPtr("arn:aws:s3:us-west-2:123456789012:accesspoint/accesspoint-name-but-no-bucket"), + errors.ErrInvalidS3ARN, + }, + { + util.StrToPtr("arn:aws:s3:us-west-2:123456789012:bucket-name/object-key"), + nil, + }, + { + util.StrToPtr("arn:aws:s3:us-west-2:123456789012:bucket-name/object-key?versionId=aVersionHash"), + nil, + }, + { + util.StrToPtr("arn:aws:s3:us-west-2:123456789012:accesspoint/accesspoint-name/object"), + nil, + }, + { + util.StrToPtr("arn:aws:s3:us-west-2:123456789012:accesspoint/accesspoint-name/some/nested/object"), + nil, + }, + { + util.StrToPtr("arn:aws:s3:us-west-2:123456789012:accesspoint/accesspoint-name/object?versionId=aVersionHash"), + nil, + }, + { + util.StrToPtr("arn:aws:s3:::bucket-name/object-key"), + nil, + }, + { + util.StrToPtr("arn:aws:s3:::bucket-name/some/nested/object"), + nil, + }, + { + util.StrToPtr("arn:aws:s3:::bucket-name/object-key?versionId=aVersionHash"), + nil, + }, + { + util.StrToPtr("gs://bucket/object"), + nil, + }, + } + + for i, test := range tests { + err := validateURLNilOK(test.in) + if test.out != err { + t.Errorf("#%d: bad error: want %v, got %v", i, test.out, err) + } + } +} diff --git a/config/v3_7_experimental/types/verification.go b/config/v3_7_experimental/types/verification.go new file mode 100644 index 000000000..5def6f04b --- /dev/null +++ b/config/v3_7_experimental/types/verification.go @@ -0,0 +1,71 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "crypto" + "encoding/hex" + "strings" + + "github.com/coreos/ignition/v2/config/shared/errors" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +// HashParts will return the sum and function (in that order) of the hash stored +// in this Verification, or an error if there is an issue during parsing. +func (v Verification) HashParts() (string, string, error) { + if v.Hash == nil { + // The hash can be nil + return "", "", nil + } + parts := strings.SplitN(*v.Hash, "-", 2) + if len(parts) != 2 { + return "", "", errors.ErrHashMalformed + } + + return parts[0], parts[1], nil +} + +func (v Verification) Validate(c path.ContextPath) (r report.Report) { + c = c.Append("hash") + if v.Hash == nil { + // The hash can be nil + return + } + + function, sum, err := v.HashParts() + if err != nil { + r.AddOnError(c, err) + return + } + var hash crypto.Hash + switch function { + case "sha512": + hash = crypto.SHA512 + case "sha256": + hash = crypto.SHA256 + default: + r.AddOnError(c, errors.ErrHashUnrecognized) + return + } + + if len(sum) != hex.EncodedLen(hash.Size()) { + r.AddOnError(c, errors.ErrHashWrongSize) + } + + return +} diff --git a/config/v3_7_experimental/types/verification_test.go b/config/v3_7_experimental/types/verification_test.go new file mode 100644 index 000000000..9a0914d08 --- /dev/null +++ b/config/v3_7_experimental/types/verification_test.go @@ -0,0 +1,102 @@ +// Copyright 2020 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "reflect" + "testing" + + "github.com/coreos/ignition/v2/config/shared/errors" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func TestHashParts(t *testing.T) { + tests := []struct { + in string + out error + }{ + { + `"sha512-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"`, + nil, + }, + { + `"sha256-0519a9826023338828942b081814355d55301b9bc82042390f9afaf75cd3a707"`, + nil, + }, + { + `"sha512:01234567"`, + errors.ErrHashMalformed, + }, + { + `"sha256:12345678"`, + errors.ErrHashMalformed, + }, + } + + for i, test := range tests { + fun, sum, err := Verification{Hash: &test.in}.HashParts() + if err != test.out { + t.Fatalf("#%d: bad error: want %+v, got %+v", i, test.out, err) + } + if err == nil && fun+"-"+sum != test.in { + t.Fatalf("#%d: bad hash: want %+v, got %+v", i, test.in, fun+"-"+sum) + } + } +} + +func TestHashValidate(t *testing.T) { + h1 := "xor-abcdef" + h2 := "sha512-123" + h3 := "sha512-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" + h4 := "sha256-0519a9826023338828942b081814355d55301b9bc82042390f9afaf75cd3a707" + h5 := "sha256-345" + + tests := []struct { + in Verification + out error + }{ + { + Verification{Hash: &h1}, + errors.ErrHashUnrecognized, + }, + { + Verification{Hash: &h2}, + errors.ErrHashWrongSize, + }, + { + Verification{Hash: &h3}, + nil, + }, + { + Verification{Hash: &h4}, + nil, + }, + { + Verification{Hash: &h5}, + errors.ErrHashWrongSize, + }, + } + + for i, test := range tests { + err := test.in.Validate(path.ContextPath{}) + expected := report.Report{} + expected.AddOnError(path.New("", "hash"), test.out) + if !reflect.DeepEqual(expected, err) { + t.Errorf("#%d: bad error: want %v, got %v", i, expected, err) + } + } +} From 0e5a3297c98884b0fbe3d86b7836d2d511453b13 Mon Sep 17 00:00:00 2001 From: Steven Presti Date: Wed, 11 Feb 2026 15:25:11 -0500 Subject: [PATCH 4/8] config/v3_7_experimental: adapt for new experimental spec --- config/v3_7_experimental/config.go | 6 ++-- config/v3_7_experimental/config_test.go | 18 +++++++--- .../v3_7_experimental/translate/translate.go | 35 +------------------ .../translate/translate_test.go | 2 +- config/v3_7_experimental/types/config.go | 5 +-- 5 files changed, 21 insertions(+), 45 deletions(-) diff --git a/config/v3_7_experimental/config.go b/config/v3_7_experimental/config.go index a41e4a4da..c82ca703a 100644 --- a/config/v3_7_experimental/config.go +++ b/config/v3_7_experimental/config.go @@ -18,7 +18,7 @@ import ( "github.com/coreos/ignition/v2/config/merge" "github.com/coreos/ignition/v2/config/shared/errors" "github.com/coreos/ignition/v2/config/util" - prev "github.com/coreos/ignition/v2/config/v3_5" + prev "github.com/coreos/ignition/v2/config/v3_6" "github.com/coreos/ignition/v2/config/v3_7_experimental/translate" "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/config/validate" @@ -58,8 +58,8 @@ func Parse(rawConfig []byte) (types.Config, report.Report, error) { return config, rpt, nil } -// ParseCompatibleVersion parses the raw config of version 3.6.0 or -// lesser into a 3.6 types.Config struct and generates a report of any errors, +// ParseCompatibleVersion parses the raw config of version 3.7.0-experimental or +// lesser into a 3.7-exp types.Config struct and generates a report of any errors, // warnings, info, and deprecations it encountered func ParseCompatibleVersion(raw []byte) (types.Config, report.Report, error) { version, rpt, err := util.GetConfigVersion(raw) diff --git a/config/v3_7_experimental/config_test.go b/config/v3_7_experimental/config_test.go index fc44b9a61..d9dff3f56 100644 --- a/config/v3_7_experimental/config_test.go +++ b/config/v3_7_experimental/config_test.go @@ -141,6 +141,10 @@ func TestParse(t *testing.T) { }, { in: in{config: []byte(`{"ignition": {"version": "3.6.0"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, + { + in: in{config: []byte(`{"ignition": {"version": "3.7.0-experimental"}}`)}, out: out{config: types.Config{Ignition: types.Ignition{Version: types.MaxVersion.String()}}}, }, { @@ -160,7 +164,7 @@ func TestParse(t *testing.T) { out: out{err: errors.ErrEmpty}, }, { - in: in{config: []byte(`{"ignition": {"version": "3.6.0-experimental"}, "storage": {"filesystems": [{"format": "ext4", "label": "zzzzzzzzzzzzzzzzzzzzzzzzzzz"}]}}`)}, + in: in{config: []byte(`{"ignition": {"version": "3.7.0-experimental"}, "storage": {"filesystems": [{"format": "ext4", "label": "zzzzzzzzzzzzzzzzzzzzzzzzzzz"}]}}`)}, out: out{err: errors.ErrInvalid}, }, } @@ -194,17 +198,21 @@ func TestParse(t *testing.T) { out: out{config: types.Config{Ignition: types.Ignition{Version: types.MaxVersion.String()}}}, }, { - in: in{config: []byte(`{"ignition": {"version": "3.6.0-experimental"}}`)}, - out: out{err: errors.ErrUnknownVersion}, + in: in{config: []byte(`{"ignition": {"version": "3.6.0"}}`)}, + out: out{config: types.Config{Ignition: types.Ignition{Version: types.MaxVersion.String()}}}, }, { - in: in{config: []byte(`{"ignition": {"version": "3.6.0"}}`)}, + in: in{config: []byte(`{"ignition": {"version": "3.7.0-experimental"}}`)}, out: out{config: types.Config{Ignition: types.Ignition{Version: types.MaxVersion.String()}}}, }, { in: in{config: []byte(`{"ignition": {"version": "3.7.0"}}`)}, out: out{err: errors.ErrUnknownVersion}, }, + { + in: in{config: []byte(`{"ignition": {"version": "3.8.0"}}`)}, + out: out{err: errors.ErrUnknownVersion}, + }, { in: in{config: []byte{}}, out: out{err: errors.ErrEmpty}, @@ -214,7 +222,7 @@ func TestParse(t *testing.T) { out: out{err: errors.ErrInvalid}, }, { - in: in{config: []byte(`{"ignition": {"version": "3.6.0-experimental"}, "storage": {"filesystems": [{"format": "ext4", "label": "zzzzzzzzzzzzzzzzzzzzzzzzzzz"}]}}`)}, + in: in{config: []byte(`{"ignition": {"version": "3.7.0-experimental"}, "storage": {"filesystems": [{"format": "ext4", "label": "zzzzzzzzzzzzzzzzzzzzzzzzzzz"}]}}`)}, out: out{err: errors.ErrInvalid}, }, } diff --git a/config/v3_7_experimental/translate/translate.go b/config/v3_7_experimental/translate/translate.go index a885877e7..3946b1fea 100644 --- a/config/v3_7_experimental/translate/translate.go +++ b/config/v3_7_experimental/translate/translate.go @@ -16,41 +16,10 @@ package translate import ( "github.com/coreos/ignition/v2/config/translate" - "github.com/coreos/ignition/v2/config/util" - old_types "github.com/coreos/ignition/v2/config/v3_5/types" + old_types "github.com/coreos/ignition/v2/config/v3_6/types" "github.com/coreos/ignition/v2/config/v3_7_experimental/types" ) -func translateFileEmbedded1(old old_types.FileEmbedded1) (ret types.FileEmbedded1) { - tr := translate.NewTranslator() - tr.Translate(&old.Append, &ret.Append) - tr.Translate(&old.Contents, &ret.Contents) - if old.Mode != nil { - // Since fixing #2024 we now have to mask for the stabilized specs - // to reduce security risks of applying permissions that were not applied - // before the fix was implemented. - // We support the special mode bits for specs >=3.6.0, so if - // the user provides special mode bits in an Ignition config - // with the version < 3.6.0, then we need to explicitly mask - // those bits out during translation. - ret.Mode = util.IntToPtr(*old.Mode & ^07000) - } - return -} - -func translateDirectoryEmbedded1(old old_types.DirectoryEmbedded1) (ret types.DirectoryEmbedded1) { - if old.Mode != nil { - // Since fixing #2024 we now have to mask for the stabilized specs - // to reduce security risks of applying permissions that were not applied - // before the fix was implemented. - // We support the special mode bits for specs >=3.6.0, so if - // the user provides special mode bits in an Ignition config - // with the version < 3.6.0, then we need to explicitly mask - // those bits out during translation. - ret.Mode = util.IntToPtr(*old.Mode & ^07000) - } - return -} func translateIgnition(old old_types.Ignition) (ret types.Ignition) { // use a new translator so we don't recurse infinitely translate.NewTranslator().Translate(&old, &ret) @@ -61,8 +30,6 @@ func translateIgnition(old old_types.Ignition) (ret types.Ignition) { func Translate(old old_types.Config) (ret types.Config) { tr := translate.NewTranslator() tr.AddCustomTranslator(translateIgnition) - tr.AddCustomTranslator(translateDirectoryEmbedded1) - tr.AddCustomTranslator(translateFileEmbedded1) tr.Translate(&old, &ret) return } diff --git a/config/v3_7_experimental/translate/translate_test.go b/config/v3_7_experimental/translate/translate_test.go index fc9e3de55..043e4cbfb 100644 --- a/config/v3_7_experimental/translate/translate_test.go +++ b/config/v3_7_experimental/translate/translate_test.go @@ -19,7 +19,7 @@ import ( "testing" "github.com/coreos/ignition/v2/config/util" - old "github.com/coreos/ignition/v2/config/v3_5/types" + old "github.com/coreos/ignition/v2/config/v3_6/types" ) // Check that we have valid translators for the complete config struct diff --git a/config/v3_7_experimental/types/config.go b/config/v3_7_experimental/types/config.go index 02ef7b4ae..a3baabf05 100644 --- a/config/v3_7_experimental/types/config.go +++ b/config/v3_7_experimental/types/config.go @@ -25,8 +25,9 @@ import ( var ( MaxVersion = semver.Version{ - Major: 3, - Minor: 6, + Major: 3, + Minor: 7, + PreRelease: "experimental", } ) From 3252aa509db1bd7f89da34f7a7d8f66974b33053 Mon Sep 17 00:00:00 2001 From: Steven Presti Date: Wed, 11 Feb 2026 15:26:32 -0500 Subject: [PATCH 5/8] *: update to v3_7_experimental spec --- config/config.go | 4 ++-- config/config_test.go | 4 +++- config/merge/merge_test.go | 2 +- internal/apply/apply.go | 2 +- internal/doc/main.go | 2 +- internal/exec/config_fetcher.go | 4 ++-- internal/exec/engine.go | 4 ++-- internal/exec/stages/disks/disks.go | 2 +- internal/exec/stages/disks/filesystems.go | 2 +- internal/exec/stages/disks/luks.go | 2 +- internal/exec/stages/disks/partitions.go | 2 +- internal/exec/stages/disks/raid.go | 2 +- internal/exec/stages/fetch/fetch.go | 2 +- internal/exec/stages/fetch_offline/fetch-offline.go | 2 +- internal/exec/stages/fetch_offline/fetch_offline_test.go | 2 +- internal/exec/stages/files/files.go | 2 +- internal/exec/stages/files/files_test.go | 2 +- internal/exec/stages/files/filesystemEntries.go | 2 +- internal/exec/stages/files/passwd.go | 2 +- internal/exec/stages/files/units.go | 2 +- internal/exec/stages/files/units_test.go | 2 +- internal/exec/stages/kargs/kargs.go | 2 +- internal/exec/stages/mount/mount.go | 2 +- internal/exec/stages/stages.go | 2 +- internal/exec/stages/umount/umount.go | 2 +- internal/exec/util/file.go | 2 +- internal/exec/util/passwd.go | 2 +- internal/exec/util/unit.go | 2 +- internal/platform/platform.go | 2 +- internal/providers/akamai/akamai.go | 2 +- internal/providers/aliyun/aliyun.go | 2 +- internal/providers/applehv/applehv.go | 2 +- internal/providers/aws/aws.go | 2 +- internal/providers/azure/azure.go | 2 +- internal/providers/azurestack/azurestack.go | 2 +- internal/providers/cloudstack/cloudstack.go | 2 +- internal/providers/cmdline/cmdline.go | 2 +- internal/providers/digitalocean/digitalocean.go | 2 +- internal/providers/exoscale/exoscale.go | 2 +- internal/providers/file/file.go | 2 +- internal/providers/gcp/gcp.go | 2 +- internal/providers/hetzner/hetzner.go | 2 +- internal/providers/hyperv/kvp.go | 2 +- internal/providers/ibmcloud/ibmcloud.go | 2 +- internal/providers/kubevirt/kubevirt.go | 2 +- internal/providers/metal/metal.go | 2 +- internal/providers/nutanix/nutanix.go | 2 +- internal/providers/nvidiabluefield/nvidiabluefield.go | 2 +- internal/providers/openstack/openstack.go | 2 +- internal/providers/oraclecloud/oraclecloud.go | 2 +- internal/providers/packet/packet.go | 2 +- internal/providers/powervs/powervs.go | 2 +- internal/providers/proxmoxve/proxmoxve.go | 2 +- internal/providers/qemu/qemu_blockdev.go | 2 +- internal/providers/qemu/qemu_fwcfg.go | 2 +- internal/providers/scaleway/scaleway.go | 2 +- internal/providers/system/system.go | 4 ++-- internal/providers/upcloud/upcloud.go | 2 +- internal/providers/util/config.go | 2 +- internal/providers/util/file.go | 2 +- internal/providers/virtualbox/virtualbox.go | 2 +- internal/providers/vmware/vmware_amd64.go | 2 +- internal/providers/vmware/vmware_unsupported.go | 2 +- internal/providers/vultr/vultr.go | 2 +- internal/providers/zvm/zvm.go | 2 +- internal/resource/http.go | 2 +- internal/resource/url.go | 2 +- internal/sgdisk/sgdisk.go | 2 +- internal/state/state.go | 2 +- internal/util/verification.go | 2 +- internal/util/verification_test.go | 2 +- tests/register/register.go | 5 +++-- 72 files changed, 80 insertions(+), 77 deletions(-) diff --git a/config/config.go b/config/config.go index 47e822146..66b602c48 100644 --- a/config/config.go +++ b/config/config.go @@ -15,8 +15,8 @@ package config import ( - exp "github.com/coreos/ignition/v2/config/v3_6_experimental" - types_exp "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + exp "github.com/coreos/ignition/v2/config/v3_7_experimental" + types_exp "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/vcontext/report" ) diff --git a/config/config_test.go b/config/config_test.go index a38159deb..64d970d31 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -26,7 +26,8 @@ import ( v3_3 "github.com/coreos/ignition/v2/config/v3_3/types" v3_4 "github.com/coreos/ignition/v2/config/v3_4/types" v3_5 "github.com/coreos/ignition/v2/config/v3_5/types" - v3_6 "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + v3_6 "github.com/coreos/ignition/v2/config/v3_6/types" + v3_7 "github.com/coreos/ignition/v2/config/v3_7_experimental/types" ) type typeSet map[reflect.Type]struct{} @@ -276,6 +277,7 @@ func TestConfigStructure(t *testing.T) { reflect.TypeOf(v3_4.Config{}), reflect.TypeOf(v3_5.Config{}), reflect.TypeOf(v3_6.Config{}), + reflect.TypeOf(v3_7.Config{}), } for _, configType := range configs { diff --git a/config/merge/merge_test.go b/config/merge/merge_test.go index ab1221717..d0c1d7f2f 100644 --- a/config/merge/merge_test.go +++ b/config/merge/merge_test.go @@ -19,7 +19,7 @@ import ( "github.com/coreos/ignition/v2/config/util" v3_2 "github.com/coreos/ignition/v2/config/v3_2/types" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/vcontext/path" "github.com/stretchr/testify/assert" diff --git a/internal/apply/apply.go b/internal/apply/apply.go index db48b8dc7..1e55b937b 100644 --- a/internal/apply/apply.go +++ b/internal/apply/apply.go @@ -34,7 +34,7 @@ import ( "github.com/coreos/ignition/v2/internal/state" "github.com/coreos/ignition/v2/internal/util" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" ) type Flags struct { diff --git a/internal/doc/main.go b/internal/doc/main.go index 6cee7a70d..c81cb36a4 100644 --- a/internal/doc/main.go +++ b/internal/doc/main.go @@ -32,7 +32,7 @@ import ( v33 "github.com/coreos/ignition/v2/config/v3_3/types" v34 "github.com/coreos/ignition/v2/config/v3_4/types" v35 "github.com/coreos/ignition/v2/config/v3_5/types" - v36 "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + v36 "github.com/coreos/ignition/v2/config/v3_7_experimental/types" ) var ( diff --git a/internal/exec/config_fetcher.go b/internal/exec/config_fetcher.go index 0769d983b..db25ad054 100644 --- a/internal/exec/config_fetcher.go +++ b/internal/exec/config_fetcher.go @@ -27,8 +27,8 @@ import ( "github.com/coreos/ignition/v2/internal/state" "github.com/coreos/ignition/v2/internal/util" - latest "github.com/coreos/ignition/v2/config/v3_6_experimental" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + latest "github.com/coreos/ignition/v2/config/v3_7_experimental" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" ) type ConfigFetcher struct { diff --git a/internal/exec/engine.go b/internal/exec/engine.go index c2e61f7eb..7e458f4f8 100644 --- a/internal/exec/engine.go +++ b/internal/exec/engine.go @@ -24,8 +24,8 @@ import ( "github.com/coreos/go-systemd/v22/journal" "github.com/coreos/ignition/v2/config/shared/errors" - latest "github.com/coreos/ignition/v2/config/v3_6_experimental" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + latest "github.com/coreos/ignition/v2/config/v3_7_experimental" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/exec/stages" executil "github.com/coreos/ignition/v2/internal/exec/util" "github.com/coreos/ignition/v2/internal/log" diff --git a/internal/exec/stages/disks/disks.go b/internal/exec/stages/disks/disks.go index 2d06aaf3c..858c17b5e 100644 --- a/internal/exec/stages/disks/disks.go +++ b/internal/exec/stages/disks/disks.go @@ -25,7 +25,7 @@ import ( "os/exec" "path/filepath" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/exec/stages" "github.com/coreos/ignition/v2/internal/exec/util" diff --git a/internal/exec/stages/disks/filesystems.go b/internal/exec/stages/disks/filesystems.go index a915ca576..cfee68d67 100644 --- a/internal/exec/stages/disks/filesystems.go +++ b/internal/exec/stages/disks/filesystems.go @@ -26,7 +26,7 @@ import ( "strings" cutil "github.com/coreos/ignition/v2/config/util" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/exec/util" ) diff --git a/internal/exec/stages/disks/luks.go b/internal/exec/stages/disks/luks.go index 9dfe9ea3b..72fde098b 100644 --- a/internal/exec/stages/disks/luks.go +++ b/internal/exec/stages/disks/luks.go @@ -30,7 +30,7 @@ import ( "strings" "github.com/coreos/ignition/v2/config/util" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" execUtil "github.com/coreos/ignition/v2/internal/exec/util" "github.com/coreos/ignition/v2/internal/log" diff --git a/internal/exec/stages/disks/partitions.go b/internal/exec/stages/disks/partitions.go index 6285fd5c4..9a7ae9cad 100644 --- a/internal/exec/stages/disks/partitions.go +++ b/internal/exec/stages/disks/partitions.go @@ -31,7 +31,7 @@ import ( "strings" cutil "github.com/coreos/ignition/v2/config/util" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/exec/util" "github.com/coreos/ignition/v2/internal/sgdisk" diff --git a/internal/exec/stages/disks/raid.go b/internal/exec/stages/disks/raid.go index d46efd820..f209efaa6 100644 --- a/internal/exec/stages/disks/raid.go +++ b/internal/exec/stages/disks/raid.go @@ -24,7 +24,7 @@ import ( "os/exec" "strings" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/exec/util" ) diff --git a/internal/exec/stages/fetch/fetch.go b/internal/exec/stages/fetch/fetch.go index ce50c360f..0d03c9870 100644 --- a/internal/exec/stages/fetch/fetch.go +++ b/internal/exec/stages/fetch/fetch.go @@ -19,7 +19,7 @@ package fetch import ( - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/exec/stages" "github.com/coreos/ignition/v2/internal/exec/util" "github.com/coreos/ignition/v2/internal/log" diff --git a/internal/exec/stages/fetch_offline/fetch-offline.go b/internal/exec/stages/fetch_offline/fetch-offline.go index e87e853d0..46dc146fd 100644 --- a/internal/exec/stages/fetch_offline/fetch-offline.go +++ b/internal/exec/stages/fetch_offline/fetch-offline.go @@ -23,7 +23,7 @@ import ( "reflect" cfgutil "github.com/coreos/ignition/v2/config/util" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/exec/stages" executil "github.com/coreos/ignition/v2/internal/exec/util" "github.com/coreos/ignition/v2/internal/log" diff --git a/internal/exec/stages/fetch_offline/fetch_offline_test.go b/internal/exec/stages/fetch_offline/fetch_offline_test.go index 04237ecba..df72415b7 100644 --- a/internal/exec/stages/fetch_offline/fetch_offline_test.go +++ b/internal/exec/stages/fetch_offline/fetch_offline_test.go @@ -18,7 +18,7 @@ import ( "testing" "github.com/coreos/ignition/v2/config/util" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/stretchr/testify/assert" ) diff --git a/internal/exec/stages/files/files.go b/internal/exec/stages/files/files.go index d2f86bc8e..13f0fe116 100644 --- a/internal/exec/stages/files/files.go +++ b/internal/exec/stages/files/files.go @@ -20,7 +20,7 @@ import ( "os" "path/filepath" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/exec/stages" "github.com/coreos/ignition/v2/internal/exec/util" diff --git a/internal/exec/stages/files/files_test.go b/internal/exec/stages/files/files_test.go index 8555be1ce..1d9683e67 100644 --- a/internal/exec/stages/files/files_test.go +++ b/internal/exec/stages/files/files_test.go @@ -19,7 +19,7 @@ import ( "sort" "testing" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/exec/util" ) diff --git a/internal/exec/stages/files/filesystemEntries.go b/internal/exec/stages/files/filesystemEntries.go index e977b10ef..d01554a16 100644 --- a/internal/exec/stages/files/filesystemEntries.go +++ b/internal/exec/stages/files/filesystemEntries.go @@ -25,7 +25,7 @@ import ( "time" cutil "github.com/coreos/ignition/v2/config/util" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/exec/util" "github.com/coreos/ignition/v2/internal/log" diff --git a/internal/exec/stages/files/passwd.go b/internal/exec/stages/files/passwd.go index fa3ffd7de..737e9cced 100644 --- a/internal/exec/stages/files/passwd.go +++ b/internal/exec/stages/files/passwd.go @@ -19,7 +19,7 @@ import ( "path/filepath" "github.com/coreos/ignition/v2/config/util" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" ) func (s *stage) expandGlobList(globs ...string) ([]string, error) { diff --git a/internal/exec/stages/files/units.go b/internal/exec/stages/files/units.go index 097452bfb..d690f0547 100644 --- a/internal/exec/stages/files/units.go +++ b/internal/exec/stages/files/units.go @@ -23,7 +23,7 @@ import ( "github.com/coreos/ignition/v2/config/shared/errors" cutil "github.com/coreos/ignition/v2/config/util" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/exec/util" "github.com/coreos/ignition/v2/internal/systemd" ) diff --git a/internal/exec/stages/files/units_test.go b/internal/exec/stages/files/units_test.go index 96f53888f..e435d4c3a 100644 --- a/internal/exec/stages/files/units_test.go +++ b/internal/exec/stages/files/units_test.go @@ -19,7 +19,7 @@ import ( "testing" "github.com/coreos/ignition/v2/config/shared/errors" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" ) func TestParseInstanceUnit(t *testing.T) { diff --git a/internal/exec/stages/kargs/kargs.go b/internal/exec/stages/kargs/kargs.go index 57c709ddb..d03b0aaef 100644 --- a/internal/exec/stages/kargs/kargs.go +++ b/internal/exec/stages/kargs/kargs.go @@ -19,7 +19,7 @@ import ( "fmt" "os/exec" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/exec/stages" "github.com/coreos/ignition/v2/internal/exec/util" diff --git a/internal/exec/stages/mount/mount.go b/internal/exec/stages/mount/mount.go index 11492e0aa..a5aa1f8b7 100644 --- a/internal/exec/stages/mount/mount.go +++ b/internal/exec/stages/mount/mount.go @@ -28,7 +28,7 @@ import ( "strings" cutil "github.com/coreos/ignition/v2/config/util" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/exec/stages" "github.com/coreos/ignition/v2/internal/exec/util" diff --git a/internal/exec/stages/stages.go b/internal/exec/stages/stages.go index a9a6fe197..ef32b5f0f 100644 --- a/internal/exec/stages/stages.go +++ b/internal/exec/stages/stages.go @@ -15,7 +15,7 @@ package stages import ( - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/ignition/v2/internal/registry" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/exec/stages/umount/umount.go b/internal/exec/stages/umount/umount.go index e167a1786..9e002d5fe 100644 --- a/internal/exec/stages/umount/umount.go +++ b/internal/exec/stages/umount/umount.go @@ -23,7 +23,7 @@ import ( "sort" cutil "github.com/coreos/ignition/v2/config/util" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/exec/stages" "github.com/coreos/ignition/v2/internal/exec/util" "github.com/coreos/ignition/v2/internal/log" diff --git a/internal/exec/util/file.go b/internal/exec/util/file.go index 1fb9fbbba..d646cf381 100644 --- a/internal/exec/util/file.go +++ b/internal/exec/util/file.go @@ -27,7 +27,7 @@ import ( "syscall" cutil "github.com/coreos/ignition/v2/config/util" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/ignition/v2/internal/resource" "github.com/coreos/ignition/v2/internal/util" diff --git a/internal/exec/util/passwd.go b/internal/exec/util/passwd.go index 5279d332f..049a26dd0 100644 --- a/internal/exec/util/passwd.go +++ b/internal/exec/util/passwd.go @@ -25,7 +25,7 @@ import ( "github.com/coreos/go-systemd/v22/journal" "github.com/coreos/ignition/v2/config/util" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/as_user" "github.com/coreos/ignition/v2/internal/distro" "golang.org/x/sys/unix" diff --git a/internal/exec/util/unit.go b/internal/exec/util/unit.go index e945db667..d70c31991 100644 --- a/internal/exec/util/unit.go +++ b/internal/exec/util/unit.go @@ -22,7 +22,7 @@ import ( "path/filepath" "syscall" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/vincent-petithory/dataurl" diff --git a/internal/platform/platform.go b/internal/platform/platform.go index 9b01df0cc..a4815261b 100644 --- a/internal/platform/platform.go +++ b/internal/platform/platform.go @@ -18,7 +18,7 @@ import ( "errors" "fmt" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/ignition/v2/internal/registry" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/providers/akamai/akamai.go b/internal/providers/akamai/akamai.go index 6af75dc4a..6003acb1d 100644 --- a/internal/providers/akamai/akamai.go +++ b/internal/providers/akamai/akamai.go @@ -24,7 +24,7 @@ import ( "net/http" "net/url" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/providers/aliyun/aliyun.go b/internal/providers/aliyun/aliyun.go index d5089c456..ff956a4db 100644 --- a/internal/providers/aliyun/aliyun.go +++ b/internal/providers/aliyun/aliyun.go @@ -20,7 +20,7 @@ package aliyun import ( "net/url" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/providers/applehv/applehv.go b/internal/providers/applehv/applehv.go index 2dfcc8aa9..a89d36875 100644 --- a/internal/providers/applehv/applehv.go +++ b/internal/providers/applehv/applehv.go @@ -23,7 +23,7 @@ import ( "os" "os/exec" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" diff --git a/internal/providers/aws/aws.go b/internal/providers/aws/aws.go index 742005edf..ed6e6763b 100644 --- a/internal/providers/aws/aws.go +++ b/internal/providers/aws/aws.go @@ -24,7 +24,7 @@ import ( "net/url" "time" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" diff --git a/internal/providers/azure/azure.go b/internal/providers/azure/azure.go index 09de9591f..610db4e06 100644 --- a/internal/providers/azure/azure.go +++ b/internal/providers/azure/azure.go @@ -27,7 +27,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/coreos/ignition/v2/config/shared/errors" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" execUtil "github.com/coreos/ignition/v2/internal/exec/util" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/ignition/v2/internal/platform" diff --git a/internal/providers/azurestack/azurestack.go b/internal/providers/azurestack/azurestack.go index 5468bad32..06e2705bf 100644 --- a/internal/providers/azurestack/azurestack.go +++ b/internal/providers/azurestack/azurestack.go @@ -18,7 +18,7 @@ package azurestack import ( - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/azure" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/providers/cloudstack/cloudstack.go b/internal/providers/cloudstack/cloudstack.go index f91fea823..0a27874ca 100644 --- a/internal/providers/cloudstack/cloudstack.go +++ b/internal/providers/cloudstack/cloudstack.go @@ -30,7 +30,7 @@ import ( "strings" "time" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/ignition/v2/internal/platform" diff --git a/internal/providers/cmdline/cmdline.go b/internal/providers/cmdline/cmdline.go index 745f06878..b0d774fff 100644 --- a/internal/providers/cmdline/cmdline.go +++ b/internal/providers/cmdline/cmdline.go @@ -22,7 +22,7 @@ import ( "os" "strings" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/ignition/v2/internal/platform" diff --git a/internal/providers/digitalocean/digitalocean.go b/internal/providers/digitalocean/digitalocean.go index 00684dc55..bbf4f757a 100644 --- a/internal/providers/digitalocean/digitalocean.go +++ b/internal/providers/digitalocean/digitalocean.go @@ -20,7 +20,7 @@ package digitalocean import ( "net/url" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/providers/exoscale/exoscale.go b/internal/providers/exoscale/exoscale.go index cb8afd541..59d351077 100644 --- a/internal/providers/exoscale/exoscale.go +++ b/internal/providers/exoscale/exoscale.go @@ -20,7 +20,7 @@ package exoscale import ( "net/url" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/providers/file/file.go b/internal/providers/file/file.go index 7ec28b2ad..224ae6f94 100644 --- a/internal/providers/file/file.go +++ b/internal/providers/file/file.go @@ -17,7 +17,7 @@ package file import ( "os" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/providers/gcp/gcp.go b/internal/providers/gcp/gcp.go index 36627fbf8..b1fa5b5d4 100644 --- a/internal/providers/gcp/gcp.go +++ b/internal/providers/gcp/gcp.go @@ -21,7 +21,7 @@ import ( "net/http" "net/url" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/providers/hetzner/hetzner.go b/internal/providers/hetzner/hetzner.go index dca9c0f9d..2faecbc91 100644 --- a/internal/providers/hetzner/hetzner.go +++ b/internal/providers/hetzner/hetzner.go @@ -20,7 +20,7 @@ package hetzner import ( "net/url" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/providers/hyperv/kvp.go b/internal/providers/hyperv/kvp.go index 3b2c49c02..df71a214a 100644 --- a/internal/providers/hyperv/kvp.go +++ b/internal/providers/hyperv/kvp.go @@ -22,7 +22,7 @@ import ( "github.com/containers/libhvee/pkg/kvp" "github.com/coreos/ignition/v2/config/shared/errors" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" diff --git a/internal/providers/ibmcloud/ibmcloud.go b/internal/providers/ibmcloud/ibmcloud.go index df43ba989..8faec1f6e 100644 --- a/internal/providers/ibmcloud/ibmcloud.go +++ b/internal/providers/ibmcloud/ibmcloud.go @@ -26,7 +26,7 @@ import ( "path/filepath" "time" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/ignition/v2/internal/platform" diff --git a/internal/providers/kubevirt/kubevirt.go b/internal/providers/kubevirt/kubevirt.go index d3a19fbcd..dc77bcc33 100644 --- a/internal/providers/kubevirt/kubevirt.go +++ b/internal/providers/kubevirt/kubevirt.go @@ -27,7 +27,7 @@ import ( "sync" "time" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/ignition/v2/internal/platform" diff --git a/internal/providers/metal/metal.go b/internal/providers/metal/metal.go index 1870cbdb7..831588666 100644 --- a/internal/providers/metal/metal.go +++ b/internal/providers/metal/metal.go @@ -18,7 +18,7 @@ package metal import ( "github.com/coreos/ignition/v2/config/shared/errors" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/providers/nutanix/nutanix.go b/internal/providers/nutanix/nutanix.go index 75fc8f7f6..32b18f84b 100644 --- a/internal/providers/nutanix/nutanix.go +++ b/internal/providers/nutanix/nutanix.go @@ -25,7 +25,7 @@ import ( "path/filepath" "time" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/ignition/v2/internal/platform" diff --git a/internal/providers/nvidiabluefield/nvidiabluefield.go b/internal/providers/nvidiabluefield/nvidiabluefield.go index 5215ec795..5afc67523 100644 --- a/internal/providers/nvidiabluefield/nvidiabluefield.go +++ b/internal/providers/nvidiabluefield/nvidiabluefield.go @@ -22,7 +22,7 @@ import ( "os" "os/exec" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" diff --git a/internal/providers/openstack/openstack.go b/internal/providers/openstack/openstack.go index 6473939bc..803c6f396 100644 --- a/internal/providers/openstack/openstack.go +++ b/internal/providers/openstack/openstack.go @@ -31,7 +31,7 @@ import ( "sync" "time" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/ignition/v2/internal/platform" diff --git a/internal/providers/oraclecloud/oraclecloud.go b/internal/providers/oraclecloud/oraclecloud.go index fb06caf5b..4ad382523 100644 --- a/internal/providers/oraclecloud/oraclecloud.go +++ b/internal/providers/oraclecloud/oraclecloud.go @@ -24,7 +24,7 @@ import ( "net/http" "net/url" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/providers/packet/packet.go b/internal/providers/packet/packet.go index e497b659d..fe4636d22 100644 --- a/internal/providers/packet/packet.go +++ b/internal/providers/packet/packet.go @@ -25,7 +25,7 @@ import ( "net/url" "strings" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/providers/powervs/powervs.go b/internal/providers/powervs/powervs.go index 0d8a40522..cdbb5e0c0 100644 --- a/internal/providers/powervs/powervs.go +++ b/internal/providers/powervs/powervs.go @@ -24,7 +24,7 @@ import ( "path/filepath" "time" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/ignition/v2/internal/platform" diff --git a/internal/providers/proxmoxve/proxmoxve.go b/internal/providers/proxmoxve/proxmoxve.go index 37b715a24..ca603e9c5 100644 --- a/internal/providers/proxmoxve/proxmoxve.go +++ b/internal/providers/proxmoxve/proxmoxve.go @@ -28,7 +28,7 @@ import ( "path/filepath" "time" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/ignition/v2/internal/platform" diff --git a/internal/providers/qemu/qemu_blockdev.go b/internal/providers/qemu/qemu_blockdev.go index f32ec2df6..3fff4d0a7 100644 --- a/internal/providers/qemu/qemu_blockdev.go +++ b/internal/providers/qemu/qemu_blockdev.go @@ -26,7 +26,7 @@ import ( "os/exec" "time" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/ignition/v2/internal/platform" diff --git a/internal/providers/qemu/qemu_fwcfg.go b/internal/providers/qemu/qemu_fwcfg.go index 946b66584..4014876d5 100644 --- a/internal/providers/qemu/qemu_fwcfg.go +++ b/internal/providers/qemu/qemu_fwcfg.go @@ -28,7 +28,7 @@ import ( "strings" "time" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" diff --git a/internal/providers/scaleway/scaleway.go b/internal/providers/scaleway/scaleway.go index d25b9aab9..855d65474 100644 --- a/internal/providers/scaleway/scaleway.go +++ b/internal/providers/scaleway/scaleway.go @@ -22,7 +22,7 @@ import ( "math/rand" "net/url" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/providers/system/system.go b/internal/providers/system/system.go index b676e6834..319b736ce 100644 --- a/internal/providers/system/system.go +++ b/internal/providers/system/system.go @@ -18,8 +18,8 @@ import ( "os" "path/filepath" - latest "github.com/coreos/ignition/v2/config/v3_6_experimental" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + latest "github.com/coreos/ignition/v2/config/v3_7_experimental" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/ignition/v2/internal/platform" diff --git a/internal/providers/upcloud/upcloud.go b/internal/providers/upcloud/upcloud.go index bec73a7b6..5f78cda59 100644 --- a/internal/providers/upcloud/upcloud.go +++ b/internal/providers/upcloud/upcloud.go @@ -21,7 +21,7 @@ package upcloud import ( "net/url" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/providers/util/config.go b/internal/providers/util/config.go index 52ea51b00..2ee8e51f3 100644 --- a/internal/providers/util/config.go +++ b/internal/providers/util/config.go @@ -19,7 +19,7 @@ import ( "encoding/hex" "github.com/coreos/ignition/v2/config" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/vcontext/report" diff --git a/internal/providers/util/file.go b/internal/providers/util/file.go index 26f018704..5e48c56b3 100644 --- a/internal/providers/util/file.go +++ b/internal/providers/util/file.go @@ -16,7 +16,7 @@ package util import ( "github.com/coreos/ignition/v2/config/util" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/vincent-petithory/dataurl" ) diff --git a/internal/providers/virtualbox/virtualbox.go b/internal/providers/virtualbox/virtualbox.go index bfc424cc2..ccf9f4331 100644 --- a/internal/providers/virtualbox/virtualbox.go +++ b/internal/providers/virtualbox/virtualbox.go @@ -32,7 +32,7 @@ import ( "unsafe" "github.com/coreos/ignition/v2/config/shared/errors" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/providers/vmware/vmware_amd64.go b/internal/providers/vmware/vmware_amd64.go index cab0aa408..085692491 100644 --- a/internal/providers/vmware/vmware_amd64.go +++ b/internal/providers/vmware/vmware_amd64.go @@ -20,7 +20,7 @@ package vmware import ( "fmt" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/providers/vmware/vmware_unsupported.go b/internal/providers/vmware/vmware_unsupported.go index c8173323f..ea3684a68 100644 --- a/internal/providers/vmware/vmware_unsupported.go +++ b/internal/providers/vmware/vmware_unsupported.go @@ -22,7 +22,7 @@ package vmware import ( "errors" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/providers/vultr/vultr.go b/internal/providers/vultr/vultr.go index e5111c6ec..5f96588ea 100644 --- a/internal/providers/vultr/vultr.go +++ b/internal/providers/vultr/vultr.go @@ -21,7 +21,7 @@ package vultr import ( "net/url" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/platform" "github.com/coreos/ignition/v2/internal/providers/util" "github.com/coreos/ignition/v2/internal/resource" diff --git a/internal/providers/zvm/zvm.go b/internal/providers/zvm/zvm.go index 7f57fb54c..c85091250 100644 --- a/internal/providers/zvm/zvm.go +++ b/internal/providers/zvm/zvm.go @@ -25,7 +25,7 @@ import ( "strings" "github.com/coreos/ignition/v2/config/shared/errors" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/ignition/v2/internal/platform" diff --git a/internal/resource/http.go b/internal/resource/http.go index aae40e905..da1541fd0 100644 --- a/internal/resource/http.go +++ b/internal/resource/http.go @@ -30,7 +30,7 @@ import ( "time" ignerrors "github.com/coreos/ignition/v2/config/shared/errors" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/earlyrand" "github.com/coreos/ignition/v2/internal/log" "github.com/coreos/ignition/v2/internal/util" diff --git a/internal/resource/url.go b/internal/resource/url.go index ce0abe2cd..18b6d7465 100644 --- a/internal/resource/url.go +++ b/internal/resource/url.go @@ -42,7 +42,7 @@ import ( "golang.org/x/oauth2/google" "google.golang.org/api/option" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" providersUtil "github.com/coreos/ignition/v2/internal/providers/util" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" diff --git a/internal/sgdisk/sgdisk.go b/internal/sgdisk/sgdisk.go index 1b276704a..ffd3d9caa 100644 --- a/internal/sgdisk/sgdisk.go +++ b/internal/sgdisk/sgdisk.go @@ -20,7 +20,7 @@ import ( "os/exec" "github.com/coreos/ignition/v2/config/util" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/log" ) diff --git a/internal/state/state.go b/internal/state/state.go index 30fb38735..8aa0ec903 100644 --- a/internal/state/state.go +++ b/internal/state/state.go @@ -20,7 +20,7 @@ import ( "os" "path/filepath" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" ) type State struct { diff --git a/internal/util/verification.go b/internal/util/verification.go index 453120cd4..070003dbd 100644 --- a/internal/util/verification.go +++ b/internal/util/verification.go @@ -23,7 +23,7 @@ import ( "hash" "strings" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" ) var ( diff --git a/internal/util/verification_test.go b/internal/util/verification_test.go index e4ad12eea..dd919cc0f 100644 --- a/internal/util/verification_test.go +++ b/internal/util/verification_test.go @@ -18,7 +18,7 @@ import ( "reflect" "testing" - "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + "github.com/coreos/ignition/v2/config/v3_7_experimental/types" ) func TestAssertValid(t *testing.T) { diff --git a/tests/register/register.go b/tests/register/register.go index 576a57ca3..6d047abd4 100644 --- a/tests/register/register.go +++ b/tests/register/register.go @@ -22,7 +22,8 @@ import ( types33 "github.com/coreos/ignition/v2/config/v3_3/types" types34 "github.com/coreos/ignition/v2/config/v3_4/types" types35 "github.com/coreos/ignition/v2/config/v3_5/types" - types_exp "github.com/coreos/ignition/v2/config/v3_6_experimental/types" + types36 "github.com/coreos/ignition/v2/config/v3_6/types" + types_exp "github.com/coreos/ignition/v2/config/v3_7_experimental/types" "github.com/coreos/ignition/v2/tests/types" ) @@ -51,7 +52,7 @@ func Register(tType TestType, t types.Test) { {semver.Version{}}, // place holder 0 {semver.Version{}}, // place holder 1 {semver.Version{}}, // place holder 2 - {types30.MaxVersion, types31.MaxVersion, types32.MaxVersion, types33.MaxVersion, types34.MaxVersion, types35.MaxVersion, types_exp.MaxVersion}, + {types30.MaxVersion, types31.MaxVersion, types32.MaxVersion, types33.MaxVersion, types34.MaxVersion, types35.MaxVersion, types36.MaxVersion, types_exp.MaxVersion}, } test := types.DeepCopy(t) From 41d23b191dfe11d1de3ad4167ec745132e3f9d6b Mon Sep 17 00:00:00 2001 From: Steven Presti Date: Wed, 11 Feb 2026 15:27:24 -0500 Subject: [PATCH 6/8] tests: update for new experimental spec --- tests/negative/general/config.go | 4 ++-- tests/positive/files/file.go | 2 +- tests/servers/servers.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/negative/general/config.go b/tests/negative/general/config.go index 9c1960be0..91ab153fb 100644 --- a/tests/negative/general/config.go +++ b/tests/negative/general/config.go @@ -281,12 +281,12 @@ func VersionOnlyConfig25() types.Test { } func VersionOnlyConfig36() types.Test { - name := "config.versions.3.7.0-exp" + name := "config.versions.3.8.0-exp" in := types.GetBaseDisk() out := in config := `{ "ignition": { - "version": "3.7.0-experimental" + "version": "3.8.0-experimental" } }` diff --git a/tests/positive/files/file.go b/tests/positive/files/file.go index d99944a0e..bd2d952ac 100644 --- a/tests/positive/files/file.go +++ b/tests/positive/files/file.go @@ -545,7 +545,7 @@ func ApplyCustomFilePermissions() types.Test { Mode: 02755, }, }) - configMinVersion := "3.6.0-experimental" + configMinVersion := "3.6.0" return types.Test{ Name: name, diff --git a/tests/servers/servers.go b/tests/servers/servers.go index 0230cd16e..eccb61220 100644 --- a/tests/servers/servers.go +++ b/tests/servers/servers.go @@ -110,7 +110,7 @@ func headerCheck(w http.ResponseWriter, r *http.Request) { headers := map[string]string{ "X-Auth": "r8ewap98gfh4d8", "Keep-Alive": "300", - "Accept": "application/vnd.coreos.ignition+json;version=3.5.0, */*;q=0.1", + "Accept": "application/vnd.coreos.ignition+json;version=3.6.0, */*;q=0.1", "Accept-Encoding": "identity", } From c7031fbc267500301637073d8de9132a356f4cde Mon Sep 17 00:00:00 2001 From: Steven Presti Date: Wed, 11 Feb 2026 15:28:01 -0500 Subject: [PATCH 7/8] docs: shuffle for spec stabilization --- internal/doc/main.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/doc/main.go b/internal/doc/main.go index c81cb36a4..11ad19e3d 100644 --- a/internal/doc/main.go +++ b/internal/doc/main.go @@ -32,7 +32,8 @@ import ( v33 "github.com/coreos/ignition/v2/config/v3_3/types" v34 "github.com/coreos/ignition/v2/config/v3_4/types" v35 "github.com/coreos/ignition/v2/config/v3_5/types" - v36 "github.com/coreos/ignition/v2/config/v3_7_experimental/types" + v36 "github.com/coreos/ignition/v2/config/v3_6/types" + v37 "github.com/coreos/ignition/v2/config/v3_7_experimental/types" ) var ( @@ -47,13 +48,14 @@ func generate(dir string) error { config any }{ // generate in inverse order of website navbar - {"3.6.0-experimental", v36.Config{}}, + {"3.7.0-experimental", v37.Config{}}, {"3.0.0", v30.Config{}}, {"3.1.0", v31.Config{}}, {"3.2.0", v32.Config{}}, {"3.3.0", v33.Config{}}, {"3.4.0", v34.Config{}}, {"3.5.0", v35.Config{}}, + {"3.6.0", v36.Config{}}, } if err := os.MkdirAll(dir, 0755); err != nil { From 8ab8a64f83fd6e3d29b2bb2aff10a9913798d5e4 Mon Sep 17 00:00:00 2001 From: Steven Presti Date: Wed, 11 Feb 2026 15:31:28 -0500 Subject: [PATCH 8/8] docs: update for spec stabilization --- docs/configuration-v3_6.md | 190 ++++++++++++++++++ ....md => configuration-v3_7_experimental.md} | 6 +- docs/migrating-configs.md | 8 +- docs/release-notes.md | 4 + docs/specs.md | 6 +- generate | 2 +- 6 files changed, 208 insertions(+), 8 deletions(-) create mode 100644 docs/configuration-v3_6.md rename docs/{configuration-v3_6_experimental.md => configuration-v3_7_experimental.md} (99%) diff --git a/docs/configuration-v3_6.md b/docs/configuration-v3_6.md new file mode 100644 index 000000000..633ccf96b --- /dev/null +++ b/docs/configuration-v3_6.md @@ -0,0 +1,190 @@ +--- +# This file is automatically generated from config/doc and internal/doc. +# Do not edit. +title: Config Spec v3.6.0 +parent: Configuration specifications +nav_order: 43 +--- + +# Configuration Specification v3.6.0 + +The Ignition configuration is a JSON document conforming to the following specification, with **_italicized_** entries being optional: + +
+ +* **ignition** (object): metadata about the configuration itself. + * **version** (string): the semantic version number of the spec. The spec version must be compatible with the latest version (`3.6.0`). Compatibility requires the major versions to match and the spec version be less than or equal to the latest version. `-experimental` versions compare less than the final version with the same number, and previous experimental versions are not accepted. + * **_config_** (object): options related to the configuration. + * **_merge_** (list of objects): a list of the configs to be merged to the current config. + * **source** (string): the URL of the config. Supported schemes are `http`, `https`, `tftp`, `s3`, `arn`, `gs`, and [`data`](https://tools.ietf.org/html/rfc2397). When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. + * **_compression_** (string): the type of compression used on the config (null or gzip). Compression cannot be used with S3. + * **_httpHeaders_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. + * **name** (string): the header name. + * **_value_** (string): the header contents. + * **_verification_** (object): options related to the verification of the config. + * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. If `compression` is specified, the hash describes the decompressed config. + * **_replace_** (object): the config that will replace the current. + * **source** (string): the URL of the config. Supported schemes are `http`, `https`, `tftp`, `s3`, `arn`, `gs`, and [`data`](https://tools.ietf.org/html/rfc2397). When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. + * **_compression_** (string): the type of compression used on the config (null or gzip). Compression cannot be used with S3. + * **_httpHeaders_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. + * **name** (string): the header name. + * **_value_** (string): the header contents. + * **_verification_** (object): options related to the verification of the config. + * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. If `compression` is specified, the hash describes the decompressed config. + * **_timeouts_** (object): options relating to `http` timeouts when fetching files over `http` or `https`. + * **_httpResponseHeaders_** (integer): the time to wait (in seconds) for the server's response headers (but not the body) after making a request. 0 indicates no timeout. Default is 10 seconds. + * **_httpTotal_** (integer): the time limit (in seconds) for the operation (connection, request, and response), including retries. 0 indicates no timeout. Default is 0. + * **_security_** (object): options relating to network security. + * **_tls_** (object): options relating to TLS when fetching resources over `https`. + * **_certificateAuthorities_** (list of objects): the list of additional certificate authorities (in addition to the system authorities) to be used for TLS verification when fetching over `https`. All certificate authorities must have a unique `source`. + * **source** (string): the URL of the certificate bundle (in PEM format). The bundle can contain multiple concatenated certificates. Supported schemes are `http`, `https`, `tftp`, `s3`, `arn`, `gs`, and [`data`](https://tools.ietf.org/html/rfc2397). When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. + * **_compression_** (string): the type of compression used on the certificate bundle (null or gzip). Compression cannot be used with S3. + * **_httpHeaders_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. + * **name** (string): the header name. + * **_value_** (string): the header contents. + * **_verification_** (object): options related to the verification of the certificate bundle. + * **_hash_** (string): the hash of the certificate bundle, in the form `-` where type is either `sha512` or `sha256`. If `compression` is specified, the hash describes the decompressed certificate bundle. + * **_proxy_** (object): options relating to setting an `HTTP(S)` proxy when fetching resources. + * **_httpProxy_** (string): will be used as the proxy URL for HTTP requests and HTTPS requests unless overridden by `httpsProxy` or `noProxy`. + * **_httpsProxy_** (string): will be used as the proxy URL for HTTPS requests unless overridden by `noProxy`. + * **_noProxy_** (list of strings): specifies a list of strings to hosts that should be excluded from proxying. Each value is represented by an `IP address prefix (1.2.3.4)`, `an IP address prefix in CIDR notation (1.2.3.4/8)`, `a domain name`, or `a special DNS label (*)`. An IP address prefix and domain name can also include a literal port number `(1.2.3.4:80)`. A domain name matches that name and all subdomains. A domain name with a leading `.` matches subdomains only. For example `foo.com` matches `foo.com` and `bar.foo.com`; `.y.com` matches `x.y.com` but not `y.com`. A single asterisk `(*)` indicates that no proxying should be done. +* **_storage_** (object): describes the desired state of the system's storage devices. + * **_disks_** (list of objects): the list of disks to be configured and their options. Every entry must have a unique `device`. + * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. + * **_wipeTable_** (boolean): whether or not the partition tables shall be wiped. When true, the partition tables are erased before any further manipulation. Otherwise, the existing entries are left intact. + * **_partitions_** (list of objects): the list of partitions and their configuration for this particular disk. Every partition must have a unique `number`, or if 0 is specified, a unique `label`. + * **_label_** (string): the PARTLABEL for the partition. + * **_number_** (integer): the partition number, which dictates its position in the partition table (one-indexed). If zero, use the next available partition slot. + * **_sizeMiB_** (integer): the size of the partition (in mebibytes). If zero, the partition will be made as large as possible. + * **_startMiB_** (integer): the start of the partition (in mebibytes). If zero, the partition will be positioned at the start of the largest block available. + * **_typeGuid_** (string): the GPT [partition type GUID](https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs). If omitted, the default will be 0FC63DAF-8483-4772-8E79-3D69D8477DE4 (Linux filesystem data). + * **_guid_** (string): the GPT unique partition GUID. + * **_wipePartitionEntry_** (boolean): if true, Ignition will clobber an existing partition if it does not match the config. If false (default), Ignition will fail instead. + * **_shouldExist_** (boolean): whether or not the partition with the specified `number` should exist. If omitted, it defaults to true. If false Ignition will either delete the specified partition or fail, depending on `wipePartitionEntry`. If false `number` must be specified and non-zero and `label`, `start`, `size`, `guid`, and `typeGuid` must all be omitted. + * **_resize_** (boolean): whether or not the existing partition should be resized. If omitted, it defaults to false. If true, Ignition will resize an existing partition if it matches the config in all respects except the partition size. + * **_raid_** (list of objects): the list of RAID arrays to be configured. Every RAID array must have a unique `name`. + * **name** (string): the name to use for the resulting md device. + * **level** (string): the redundancy level of the array (e.g. linear, raid1, raid5, etc.). + * **devices** (list of strings): the list of devices (referenced by their absolute path) in the array. + * **_spares_** (integer): the number of spares (if applicable) in the array. + * **_options_** (list of strings): any additional options to be passed to mdadm. + * **_filesystems_** (list of objects): the list of filesystems to be configured. `device` and `format` need to be specified. Every filesystem must have a unique `device`. + * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. + * **format** (string): the filesystem format (ext4, btrfs, xfs, vfat, swap, or none). + * **_path_** (string): the mount-point of the filesystem while Ignition is running relative to where the root filesystem will be mounted. This is not necessarily the same as where it should be mounted in the real root, but it is encouraged to make it the same. + * **_wipeFilesystem_** (boolean): whether or not to wipe the device before filesystem creation, see [Ignition's documentation on filesystems](https://coreos.github.io/ignition/operator-notes/#filesystem-reuse-semantics) for more information. Defaults to false. + * **_label_** (string): the label of the filesystem. + * **_uuid_** (string): the uuid of the filesystem. + * **_options_** (list of strings): any additional options to be passed to the format-specific mkfs utility. + * **_mountOptions_** (list of strings): any special options to be passed to the mount command. + * **_files_** (list of objects): the list of files to be written. Every file, directory and link must have a unique `path`. + * **path** (string): the absolute path to the file. + * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. `contents` must be specified if `overwrite` is true. Defaults to false. + * **_contents_** (object): options related to the contents of the file. + * **_source_** (string): the URL of the file. Supported schemes are `http`, `https`, `tftp`, `s3`, `arn`, `gs`, and [`data`](https://tools.ietf.org/html/rfc2397). When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. If source is omitted and a regular file already exists at the path, Ignition will do nothing. If source is omitted and no file exists, an empty file will be created. + * **_compression_** (string): the type of compression used on the file (null or gzip). Compression cannot be used with S3. + * **_httpHeaders_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. + * **name** (string): the header name. + * **_value_** (string): the header contents. + * **_verification_** (object): options related to the verification of the file. + * **_hash_** (string): the hash of the file, in the form `-` where type is either `sha512` or `sha256`. If `compression` is specified, the hash describes the decompressed file. + * **_append_** (list of objects): list of fragments to be appended to the file. Follows the same structure as `contents`. + * **_source_** (string): the URL of the fragment. Supported schemes are `http`, `https`, `tftp`, `s3`, `arn`, `gs`, and [`data`](https://tools.ietf.org/html/rfc2397). When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. + * **_compression_** (string): the type of compression used on the fragment (null or gzip). Compression cannot be used with S3. + * **_httpHeaders_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. + * **name** (string): the header name. + * **_value_** (string): the header contents. + * **_verification_** (object): options related to the verification of the fragment. + * **_hash_** (string): the hash of the fragment, in the form `-` where type is either `sha512` or `sha256`. If `compression` is specified, the hash describes the decompressed fragment. + * **_mode_** (integer): the file's permission mode. Note that the mode must be properly specified as a **decimal** value (i.e. 0644 -> 420). Setuid/setgid/sticky bits are supported. If not specified, the permission mode for files defaults to 0644 or the existing file's permissions if `overwrite` is false, `contents` is unspecified, and a file already exists at the path. + * **_user_** (object): specifies the file's owner. + * **_id_** (integer): the user ID of the owner. + * **_name_** (string): the user name of the owner. + * **_group_** (object): specifies the file's group. + * **_id_** (integer): the group ID of the group. + * **_name_** (string): the group name of the group. + * **_directories_** (list of objects): the list of directories to be created. Every file, directory, and link must have a unique `path`. + * **path** (string): the absolute path to the directory. + * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. If false and a directory already exists at the path, Ignition will only set its permissions. If false and a non-directory exists at that path, Ignition will fail. Defaults to false. + * **_mode_** (integer): the directory's permission mode. Note that the mode must be properly specified as a **decimal** value (i.e. 0755 -> 493). Setuid/setgid/sticky bits are supported. If not specified, the permission mode for directories defaults to 0755 or the mode of an existing directory if `overwrite` is false and a directory already exists at the path. + * **_user_** (object): specifies the directory's owner. + * **_id_** (integer): the user ID of the owner. + * **_name_** (string): the user name of the owner. + * **_group_** (object): specifies the directory's group. + * **_id_** (integer): the group ID of the group. + * **_name_** (string): the group name of the group. + * **_links_** (list of objects): the list of links to be created. Every file, directory, and link must have a unique `path`. + * **path** (string): the absolute path to the link + * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. If overwrite is false and a matching link exists at the path, Ignition will only set the owner and group. Defaults to false. + * **_user_** (object): specifies the owner for a symbolic link. Ignored for hard links. + * **_id_** (integer): the user ID of the owner. + * **_name_** (string): the user name of the owner. + * **_group_** (object): specifies the group for a symbolic link. Ignored for hard links. + * **_id_** (integer): the group ID of the group. + * **_name_** (string): the group name of the group. + * **target** (string): the target path of the link + * **_hard_** (boolean): a symbolic link is created if this is false, a hard one if this is true. + * **_luks_** (list of objects): the list of luks devices to be created. Every device must have a unique `name`. + * **name** (string): the name of the luks device. + * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. + * **_keyFile_** (object): options related to the contents of the key file. + * **_source_** (string): the URL of the key file. Supported schemes are `http`, `https`, `tftp`, `s3`, `arn`, `gs`, and [`data`](https://tools.ietf.org/html/rfc2397). When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. + * **_compression_** (string): the type of compression used on the key file (null or gzip). Compression cannot be used with S3. + * **_httpHeaders_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. + * **name** (string): the header name. + * **_value_** (string): the header contents. + * **_verification_** (object): options related to the verification of the key file. + * **_hash_** (string): the hash of the key file, in the form `-` where type is either `sha512` or `sha256`. If `compression` is specified, the hash describes the decompressed key file. + * **_label_** (string): the label of the luks device. + * **_uuid_** (string): the uuid of the luks device. + * **_options_** (list of strings): any additional options to be passed to `cryptsetup luksFormat`. + * **_discard_** (boolean): whether to issue discard commands to the underlying block device when blocks are freed. Enabling this improves performance and device longevity on SSDs and space utilization on thinly provisioned SAN devices, but leaks information about which disk blocks contain data. If omitted, it defaults to false. + * **_openOptions_** (list of strings): any additional options to be passed to `cryptsetup luksOpen`. Supported options will be persistently written to the luks volume. + * **_wipeVolume_** (boolean): whether or not to wipe the device before volume creation, see [Ignition's documentation on filesystems](https://coreos.github.io/ignition/operator-notes/#filesystem-reuse-semantics) for more information. + * **_clevis_** (object): describes the clevis configuration for the luks device. + * **_tang_** (list of objects): describes a tang server. Every server must have a unique `url`. + * **url** (string): url of the tang server. + * **thumbprint** (string): thumbprint of a trusted signing key. + * **_advertisement_** (string): the advertisement JSON. If not specified, the advertisement is fetched from the tang server during provisioning. + * **_tpm2_** (boolean): whether or not to use a tpm2 device. + * **_threshold_** (integer): sets the minimum number of pieces required to decrypt the device. Default is 1. + * **_custom_** (object): overrides the clevis configuration. The `pin` & `config` will be passed directly to `clevis luks bind`. If specified, all other clevis options must be omitted. + * **pin** (string): the clevis pin. + * **config** (string): the clevis configuration JSON. + * **_needsNetwork_** (boolean): whether or not the device requires networking. + * **_cex_** (object): describes the IBM Crypto Express (CEX) card configuration for the luks device. + * **_enabled_** (boolean): whether or not to enable cex compatibility for luks. If omitted, defaults to false. +* **_systemd_** (object): describes the desired state of the systemd units. + * **_units_** (list of objects): the list of systemd units. Every unit must have a unique `name`. + * **name** (string): the name of the unit. This must be suffixed with a valid unit type (e.g. "thing.service"). + * **_enabled_** (boolean): whether or not the service shall be enabled. When true, the service is enabled. When false, the service is disabled. When omitted, the service is unmodified. In order for this to have any effect, the unit must have an install section. + * **_mask_** (boolean): whether or not the service shall be masked. When true, the service is masked by symlinking it to `/dev/null`. When false, the service is unmasked by deleting the symlink to `/dev/null` if it exists. + * **_contents_** (string): the contents of the unit. + * **_dropins_** (list of objects): the list of drop-ins for the unit. Every drop-in must have a unique `name`. + * **name** (string): the name of the drop-in. This must be suffixed with ".conf". + * **_contents_** (string): the contents of the drop-in. +* **_passwd_** (object): describes the desired additions to the passwd database. + * **_users_** (list of objects): the list of accounts that shall exist. All users must have a unique `name`. + * **name** (string): the username for the account. + * **_passwordHash_** (string): the hashed password for the account. + * **_sshAuthorizedKeys_** (list of strings): a list of SSH keys to be added as an SSH key fragment at `.ssh/authorized_keys.d/ignition` in the user's home directory. All SSH keys must be unique. + * **_uid_** (integer): the user ID of the account. + * **_gecos_** (string): the GECOS field of the account. + * **_homeDir_** (string): the home directory of the account. + * **_noCreateHome_** (boolean): whether or not to create the user's home directory. This only has an effect if the account doesn't exist yet. + * **_primaryGroup_** (string): the name of the primary group of the account. + * **_groups_** (list of strings): the list of supplementary groups of the account. + * **_noUserGroup_** (boolean): whether or not to create a group with the same name as the user. This only has an effect if the account doesn't exist yet. + * **_noLogInit_** (boolean): whether or not to add the user to the lastlog and faillog databases. This only has an effect if the account doesn't exist yet. + * **_shell_** (string): the login shell of the new account. + * **_shouldExist_** (boolean): whether or not the user with the specified `name` should exist. If omitted, it defaults to true. If false, then Ignition will delete the specified user. + * **_system_** (boolean): whether or not this account should be a system account. This only has an effect if the account doesn't exist yet. + * **_groups_** (list of objects): the list of groups to be added. All groups must have a unique `name`. + * **name** (string): the name of the group. + * **_gid_** (integer): the group ID of the new group. + * **_passwordHash_** (string): the hashed password of the new group. + * **_shouldExist_** (boolean): whether or not the group with the specified `name` should exist. If omitted, it defaults to true. If false, then Ignition will delete the specified group. + * **_system_** (boolean): whether or not the group should be a system group. This only has an effect if the group doesn't exist yet. +* **_kernelArguments_** (object): describes the desired kernel arguments. + * **_shouldExist_** (list of strings): the list of kernel arguments that should exist. + * **_shouldNotExist_** (list of strings): the list of kernel arguments that should not exist. diff --git a/docs/configuration-v3_6_experimental.md b/docs/configuration-v3_7_experimental.md similarity index 99% rename from docs/configuration-v3_6_experimental.md rename to docs/configuration-v3_7_experimental.md index 965f293f9..eec6468cb 100644 --- a/docs/configuration-v3_6_experimental.md +++ b/docs/configuration-v3_7_experimental.md @@ -1,12 +1,12 @@ --- # This file is automatically generated from config/doc and internal/doc. # Do not edit. -title: Config Spec v3.6.0-experimental +title: Config Spec v3.7.0-experimental parent: Configuration specifications nav_order: 50 --- -# Configuration Specification v3.6.0-experimental +# Configuration Specification v3.7.0-experimental _NOTE_: This pre-release version of the specification is experimental and is subject to change without notice or regard to backward compatibility. @@ -15,7 +15,7 @@ The Ignition configuration is a JSON document conforming to the following specif
* **ignition** (object): metadata about the configuration itself. - * **version** (string): the semantic version number of the spec. The spec version must be compatible with the latest version (`3.6.0-experimental`). Compatibility requires the major versions to match and the spec version be less than or equal to the latest version. `-experimental` versions compare less than the final version with the same number, and previous experimental versions are not accepted. + * **version** (string): the semantic version number of the spec. The spec version must be compatible with the latest version (`3.7.0-experimental`). Compatibility requires the major versions to match and the spec version be less than or equal to the latest version. `-experimental` versions compare less than the final version with the same number, and previous experimental versions are not accepted. * **_config_** (object): options related to the configuration. * **_merge_** (list of objects): a list of the configs to be merged to the current config. * **source** (string): the URL of the config. Supported schemes are `http`, `https`, `tftp`, `s3`, `arn`, `gs`, and [`data`](https://tools.ietf.org/html/rfc2397). When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. diff --git a/docs/migrating-configs.md b/docs/migrating-configs.md index e6d82932e..80b7f02ef 100644 --- a/docs/migrating-configs.md +++ b/docs/migrating-configs.md @@ -11,7 +11,11 @@ Occasionally, there are changes made to Ignition's configuration that break back {:toc} -## From Version 3.5.0 to 3.6.0-experimental +## From Version 3.6.0 to 3.7.0-experimental + +There are not any breaking changes between versions 3.6.0 and 3.7.0-experimental of the configuration specification. Any valid 3.6.0 configuration can be updated to a 3.7.0-experimental configuration by simply changing the version string in the config. + +## From Version 3.5.0 to 3.6.0 ### Special mode bits supported @@ -21,7 +25,7 @@ The `mode` field of the `files` and `directories` sections now respects the setu ```json { "ignition": { - "version": "3.6.0-experimental" + "version": "3.6.0" }, "storage": { "files": [{ diff --git a/docs/release-notes.md b/docs/release-notes.md index fdbf3dac4..ee405ff47 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -10,6 +10,10 @@ nav_order: 9 ### Features +- Mark the 3.6.0 config spec as stable +- No longer accept configs with version 3.6.0-experimental +- Create new 3.7.0-experimental config spec from 3.6.0 + ### Changes ### Bug fixes diff --git a/docs/specs.md b/docs/specs.md index 89021b3c6..9b8fd5c48 100644 --- a/docs/specs.md +++ b/docs/specs.md @@ -19,6 +19,7 @@ We recommend that you always use the latest **stable** specification to benefit from new features and bug fixes. The following **stable** specification versions are currently supported in Ignition: +- [v3.6.0](configuration-v3_6.md) - [v3.5.0](configuration-v3_5.md) - [v3.4.0](configuration-v3_4.md) - [v3.3.0](configuration-v3_3.md) @@ -33,7 +34,7 @@ and testing** as it is subject to change **without warning or announcement**. The following **experimental** specification version is currently available in Ignition: -- [v3.6.0-experimental](configuration-v3_6_experimental.md) +- [v3.7.0-experimental](configuration-v3_7_experimental.md) ## Legacy spec 2.x configuration specifications @@ -55,4 +56,5 @@ recommended to use the latest Ignition release. | 3.2.0 | 2.7.0 | | 3.3.0 | 2.11.0 | | 3.4.0 | 2.15.0 | -| 3.5.0 | 2.20.0 | \ No newline at end of file +| 3.5.0 | 2.20.0 | +| 3.6.0 | 2.26.0 | \ No newline at end of file diff --git a/generate b/generate index 951f49ffa..6a225d5d1 100755 --- a/generate +++ b/generate @@ -12,7 +12,7 @@ hash schematyper 2>/dev/null || { exit 1 } -specs="v3_0 v3_1 v3_2 v3_3 v3_4 v3_5 v3_6_experimental" +specs="v3_0 v3_1 v3_2 v3_3 v3_4 v3_5 v3_6 v3_7_experimental" for spec in $specs do