Skip to content

Commit

Permalink
Change Butane Configs conversion to compatible Ignition version
Browse files Browse the repository at this point in the history
* Parse Ignition bytes to the compatible Ignition version (determined
by current Ignition module's `ParseCompatibleVersion`)
* Parse v1.3 Butane Configs to Ignition v3.3
* Parse v1.2 Butane Configs to Ignition v3.3
* Parse v1.1 Butane Configs to Ignition v3.3
* Parse v1.0 Butane Configs to Ignition v3.3
* Relies on Ignition v3.x spec's future compatibility, where lesser
Ignition can be converted forward to the current Ignition (e.g. any
new fields will be empty or equivalently zero effect)
* Add support for version skew among Butane Config snippets
  * Butane Config and snippets will always convert to the Ignition
  version set by this provider

Docs: https://github.com/poseidon/terraform-provider-ct#versions
  • Loading branch information
dghubble committed Feb 16, 2022
1 parent 2e011c1 commit b3962e8
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 316 deletions.
11 changes: 11 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ Notable changes between releases.

## Latest

## v0.10.0

* Change how older (< 1.4) Butane Configs are parsed to Ignition ([#116](https://github.com/poseidon/terraform-provider-ct/pull/116))
* Parse Ignition bytes to the forward compatible Ignition version ([docs](https://github.com/poseidon/terraform-provider-ct#versions))
* Parse v1.3 Butane Configs to Ignition v3.3
* Parse v1.2 Butane Configs to Ignition v3.3
* Parse v1.1 Butane Configs to Ignition v3.3
* Parse v1.0 Butane Configs to Ignition v3.3
* Add support for verison skew among Butane Config snippets
* Butane Config and snippets will always convert to the current Ignition version

## v0.9.2

* Update butane, ignition, and Terraform SDK modules
Expand Down
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ terraform {
required_providers {
ct = {
source = "poseidon/ct"
version = "0.9.2"
version = "0.10.0"
}
}
}
Expand Down Expand Up @@ -75,12 +75,15 @@ $ terraform init
## Versions
Butane configs contain a `version` that is associated with an Ignition format version. For example, a Butane config with `version: 1.0.0` would produce an Ignition config with version `3.0.0`, across future releases.
Butane configs are converted to the current (according to this provider) stable Ignition config and merged together. For example, a Butane Config with `version: 1.2.0` would produce an Ignition config with version `v3.3.0`. This relies on Ignition's [forward compatibility](https://github.com/coreos/ignition/blob/main/config/v3_3/config.go#L61).
Container Linux Configs render a fixed Ignition version, depending on the `terraform-provider-ct` release, so updating alters the rendered Ignition version.
Before `terraform-provider-ct` v0.10.0, Butane configs contained a `version` that was associated with an Ignition format version. For example, a Butane config with `version: 1.0.0` would produce an Ignition config with version `3.0.0`.
| terraform-provider-ct | CLC to Ignition | Butane to Ignition |
|-----------------------|---------------------|--------------------|
| 0.10.x | Renders 2.3.0 | Butane (1.0, 1.1, 1.2, 1.3, 1.4) -> Ignition 3.3 |
| 0.9.x | Renders 2.3.0 | Butane (1.0, 1.1, 1.2, 1.3, 1.4) -> Ignition (3.0, 3.1, 3.2, 3.2, 3.3)
| 0.8.x | Renders 2.3.0 | Butane (1.0, 1.1, 1.2, 1.3) -> Ignition (3.0, 3.1, 3.2, 3.2)
| 0.7.x | Renders 2.3.0 | Butane (1.0, 1.1, 1.2) -> Ignition (3.0, 3.1, 3.2) |
Expand All @@ -90,10 +93,6 @@ Container Linux Configs render a fixed Ignition version, depending on the `terra
| 0.3.x | Renders 2.2.0 | NA |
| 0.2.x | Renders 2.0.0 | NA |
Notes:
* Butane config `snippets` must match the version set in the content. Version skew among snippets is **not** supported.
## Development
### Binary
Expand Down
157 changes: 12 additions & 145 deletions ct/datasource_ct_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,7 @@ import (

ignition "github.com/coreos/ignition/config/v2_3"
ignitionTypes "github.com/coreos/ignition/config/v2_3/types"
ignition30 "github.com/coreos/ignition/v2/config/v3_0"
ignition30Types "github.com/coreos/ignition/v2/config/v3_0/types"
ignition31 "github.com/coreos/ignition/v2/config/v3_1"
ignition31Types "github.com/coreos/ignition/v2/config/v3_1/types"
ignition32 "github.com/coreos/ignition/v2/config/v3_2"
ignition32Types "github.com/coreos/ignition/v2/config/v3_2/types"
ignition33 "github.com/coreos/ignition/v2/config/v3_3"
ignition33Types "github.com/coreos/ignition/v2/config/v3_3/types"
)

func dataSourceCTConfig() *schema.Resource {
Expand Down Expand Up @@ -97,8 +90,8 @@ func renderConfig(d *schema.ResourceData) (string, error) {
}
}

// Fedora CoreOS Config
ign, err := fccToIgnition([]byte(content), pretty, strict, snippets)
// Butane Config
ign, err := butaneToIgnition([]byte(content), pretty, strict, snippets)
if err == common.ErrNoVariant {
// consider as Container Linux Config
ign, err = renderCLC([]byte(content), platform, pretty, strict, snippets)
Expand All @@ -107,7 +100,7 @@ func renderConfig(d *schema.ResourceData) (string, error) {
}

// Translate Fedora CoreOS config to Ignition v3.X.Y
func fccToIgnition(data []byte, pretty, strict bool, snippets []string) ([]byte, error) {
func butaneToIgnition(data []byte, pretty, strict bool, snippets []string) ([]byte, error) {
ignBytes, report, err := butane.TranslateBytes(data, common.TranslateBytesOptions{
Pretty: pretty,
})
Expand All @@ -119,166 +112,40 @@ func fccToIgnition(data []byte, pretty, strict bool, snippets []string) ([]byte,
return nil, fmt.Errorf("strict parsing error: %v", report.String())
}

if len(snippets) == 0 {
return ignBytes, nil
}

// merge FCC snippets into main Ignition config
return mergeFCCSnippets(ignBytes, pretty, strict, snippets)
}

// Manually parse main Fedora CoreOS Config's Ignition using fallback Ignition
// versions. Then translate and parse FCC snippets as the chosen Ignition
// version to merge.
// version
// Upstream might later handle: https://github.com/coreos/butane/issues/118
// Note: This means snippets version must match the main config version.
// Parse Fedora CoreOS Ignition and Butane snippets into Ignition Config.
func mergeFCCSnippets(ignBytes []byte, pretty, strict bool, snippets []string) ([]byte, error) {
ign33, _, err := ignition33.Parse(ignBytes)
if err == nil {
// FCC config v1.4.0
ign33, err = mergeFCC14(ign33, snippets, pretty, strict)
if err != nil {
return nil, fmt.Errorf("FCC v1.4.0 merge error: %v", err)
}
return marshalJSON(ign33, pretty)
}

ign32, _, err := ignition32.Parse(ignBytes)
if err == nil {
// FCC config v1.2.0
ign32, err = mergeFCC12(ign32, snippets, pretty, strict)
if err != nil {
return nil, fmt.Errorf("FCC v1.2.0 merge error: %v", err)
}
return marshalJSON(ign32, pretty)
}

ign31, _, err := ignition31.Parse(ignBytes)
if err == nil {
// FCC config v1.1.0
ign31, err = mergeFCC11(ign31, snippets, pretty, strict)
if err != nil {
return nil, fmt.Errorf("FCC v1.1.0 merge error: %v", err)
}
return marshalJSON(ign31, pretty)
}

var ign30 ignition30Types.Config
ign30, _, err = ignition30.Parse(ignBytes)
ign, _, err := ignition33.ParseCompatibleVersion(ignBytes)
if err != nil {
return nil, fmt.Errorf("FCC v1.0.0 parse error: %v", err)
return nil, fmt.Errorf("%v", err)
}
// FCC config v1.0.0
ign30, err = mergeFCCV10(ign30, snippets, pretty, strict)
if err != nil {
return nil, fmt.Errorf("FCC v1.0.0 merge error: %v", err)
}
return marshalJSON(ign30, pretty)
}

// merge FCC v1.4.0 snippets
func mergeFCC14(ign ignition33Types.Config, snippets []string, pretty, strict bool) (ignition33Types.Config, error) {
for _, snippet := range snippets {
ignextBytes, report, err := butane.TranslateBytes([]byte(snippet), common.TranslateBytesOptions{
Pretty: pretty,
})
if err != nil {
// For FCC, require snippets be FCCs (don't fall-through to CLC)
if err == common.ErrNoVariant {
return ign, fmt.Errorf("Fedora CoreOS snippets require `variant`: %v", err)
return nil, fmt.Errorf("Butane snippets require `variant`: %v", err)
}
return ign, fmt.Errorf("snippet v1.4.0 translate error: %v", err)
return nil, fmt.Errorf("Butane translate error: %v", err)
}
if strict && len(report.Entries) > 0 {
return ign, fmt.Errorf("strict parsing error: %v", report.String())
return nil, fmt.Errorf("strict parsing error: %v", report.String())
}

ignext, _, err := ignition33.Parse(ignextBytes)
ignext, _, err := ignition33.ParseCompatibleVersion(ignextBytes)
if err != nil {
return ign, fmt.Errorf("snippet parse error: %v, expect v1.4.0", err)
return nil, fmt.Errorf("snippet parse error: %v, expect v1.4.0", err)
}
ign = ignition33.Merge(ign, ignext)
}
return ign, nil
}

// merge FCC v1.2.0 snippets
func mergeFCC12(ign ignition32Types.Config, snippets []string, pretty, strict bool) (ignition32Types.Config, error) {
for _, snippet := range snippets {
ignextBytes, report, err := butane.TranslateBytes([]byte(snippet), common.TranslateBytesOptions{
Pretty: pretty,
})
if err != nil {
// For FCC, require snippets be FCCs (don't fall-through to CLC)
if err == common.ErrNoVariant {
return ign, fmt.Errorf("Fedora CoreOS snippets require `variant`: %v", err)
}
return ign, fmt.Errorf("snippet v1.2.0 translate error: %v", err)
}
if strict && len(report.Entries) > 0 {
return ign, fmt.Errorf("strict parsing error: %v", report.String())
}

ignext, _, err := ignition32.Parse(ignextBytes)
if err != nil {
return ign, fmt.Errorf("snippet parse error: %v, expect v1.2.0", err)
}
ign = ignition32.Merge(ign, ignext)
}
return ign, nil
}

// merge FCC v1.1.0 snippets
func mergeFCC11(ign ignition31Types.Config, snippets []string, pretty, strict bool) (ignition31Types.Config, error) {
for _, snippet := range snippets {
ignextBytes, report, err := butane.TranslateBytes([]byte(snippet), common.TranslateBytesOptions{
Pretty: pretty,
})
if err != nil {
// For FCC, require snippets be FCCs (don't fall-through to CLC)
if err == common.ErrNoVariant {
return ign, fmt.Errorf("Fedora CoreOS snippets require `variant`: %v", err)
}
return ign, fmt.Errorf("snippet v1.1.0 translate error: %v", err)
}
if strict && len(report.Entries) > 0 {
return ign, fmt.Errorf("strict parsing error: %v", report.String())
}

ignext, _, err := ignition31.Parse(ignextBytes)
if err != nil {
return ign, fmt.Errorf("snippet parse error: %v, expect v1.1.0", err)
}
ign = ignition31.Merge(ign, ignext)
}
return ign, nil
}

// merge FCC v1.0.0 snippets
func mergeFCCV10(ign ignition30Types.Config, snippets []string, pretty, strict bool) (ignition30Types.Config, error) {
for _, snippet := range snippets {
ignextBytes, report, err := butane.TranslateBytes([]byte(snippet), common.TranslateBytesOptions{
Pretty: pretty,
})
if err != nil {
// For FCC, require snippets be FCCs (don't fall-through to CLC)
if err == common.ErrNoVariant {
return ign, fmt.Errorf("Fedora CoreOS snippets require `variant`: %v", err)
}
return ign, fmt.Errorf("snippet v1.0.0 translate error: %v", err)
}
if strict && len(report.Entries) > 0 {
return ign, fmt.Errorf("strict parsing error: %v", report.String())
}

ignext, _, err := ignition30.Parse(ignextBytes)
if err != nil {
return ign, fmt.Errorf("snippet parse error: %v, expect v1.0.0", err)
}
ign = ignition30.Merge(ign, ignext)
}
return ign, nil
return marshalJSON(ign, pretty)
}

// Translate Container Linux Config as Ignition JSON.
Expand Down
Loading

0 comments on commit b3962e8

Please sign in to comment.