From 1dd8d2eaf752c98bc8fcb786d3fe335f739c1ef8 Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Fri, 5 Mar 2021 23:16:53 -0500 Subject: [PATCH 1/6] translate: fix incorrect copypasted panic message --- translate/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translate/util.go b/translate/util.go index 58facb47..cae42098 100644 --- a/translate/util.go +++ b/translate/util.go @@ -82,7 +82,7 @@ func getAllPaths(v reflect.Value, tag string, includeZeroFields bool) []path.Con } return ret default: - panic("Encountered types that are not the same when they should be. This is a bug, please file a report") + panic("Encountered unexpected type. This is a bug, please file a report") } } From abc3cd9c399597ff0f3f667290871ee86bd76b28 Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Fri, 12 Feb 2021 14:24:11 -0500 Subject: [PATCH 2/6] rhcos/v0_2_exp: rename to openshift/v4_8_exp We want to support both OCP and OKD. --- config/config.go | 4 ++-- config/{rhcos/v0_2_exp => openshift/v4_8_exp}/schema.go | 2 +- .../{rhcos/v0_2_exp => openshift/v4_8_exp}/translate.go | 2 +- ...fig-rhcos-v0_2-exp.md => config-openshift-v4_8-exp.md} | 8 ++++---- docs/specs.md | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) rename config/{rhcos/v0_2_exp => openshift/v4_8_exp}/schema.go (97%) rename config/{rhcos/v0_2_exp => openshift/v4_8_exp}/translate.go (98%) rename docs/{config-rhcos-v0_2-exp.md => config-openshift-v4_8-exp.md} (98%) diff --git a/config/config.go b/config/config.go index 0758c0d8..90bd302f 100644 --- a/config/config.go +++ b/config/config.go @@ -23,8 +23,8 @@ import ( fcos1_2 "github.com/coreos/fcct/config/fcos/v1_2" fcos1_3 "github.com/coreos/fcct/config/fcos/v1_3" fcos1_4_exp "github.com/coreos/fcct/config/fcos/v1_4_exp" + openshift4_8_exp "github.com/coreos/fcct/config/openshift/v4_8_exp" rhcos0_1 "github.com/coreos/fcct/config/rhcos/v0_1" - rhcos0_2_exp "github.com/coreos/fcct/config/rhcos/v0_2_exp" "github.com/coreos/go-semver/semver" "github.com/coreos/vcontext/report" @@ -47,8 +47,8 @@ func init() { RegisterTranslator("fcos", "1.2.0", fcos1_2.ToIgn3_2Bytes) RegisterTranslator("fcos", "1.3.0", fcos1_3.ToIgn3_2Bytes) RegisterTranslator("fcos", "1.4.0-experimental", fcos1_4_exp.ToIgn3_3Bytes) + RegisterTranslator("openshift", "4.8.0-experimental", openshift4_8_exp.ToIgn3_3Bytes) RegisterTranslator("rhcos", "0.1.0", rhcos0_1.ToIgn3_2Bytes) - RegisterTranslator("rhcos", "0.2.0-experimental", rhcos0_2_exp.ToIgn3_3Bytes) } /// RegisterTranslator registers a translator for the specified variant and diff --git a/config/rhcos/v0_2_exp/schema.go b/config/openshift/v4_8_exp/schema.go similarity index 97% rename from config/rhcos/v0_2_exp/schema.go rename to config/openshift/v4_8_exp/schema.go index 50a8a6cf..b6b635f7 100644 --- a/config/rhcos/v0_2_exp/schema.go +++ b/config/openshift/v4_8_exp/schema.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v0_2_exp +package v4_8_exp import ( fcos "github.com/coreos/fcct/config/fcos/v1_4_exp" diff --git a/config/rhcos/v0_2_exp/translate.go b/config/openshift/v4_8_exp/translate.go similarity index 98% rename from config/rhcos/v0_2_exp/translate.go rename to config/openshift/v4_8_exp/translate.go index 33d34a86..66c60635 100644 --- a/config/rhcos/v0_2_exp/translate.go +++ b/config/openshift/v4_8_exp/translate.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v0_2_exp +package v4_8_exp import ( "github.com/coreos/fcct/config/common" diff --git a/docs/config-rhcos-v0_2-exp.md b/docs/config-openshift-v4_8-exp.md similarity index 98% rename from docs/config-rhcos-v0_2-exp.md rename to docs/config-openshift-v4_8-exp.md index 0c0be11c..0d813a40 100644 --- a/docs/config-rhcos-v0_2-exp.md +++ b/docs/config-openshift-v4_8-exp.md @@ -1,18 +1,18 @@ --- layout: default -title: RHEL CoreOS v0.2.0-experimental +title: OpenShift v4.8.0-experimental parent: Configuration specifications nav_order: 100 --- -# RHEL CoreOS Specification v0.2.0-experimental +# OpenShift Specification v4.8.0-experimental **Note: This configuration is experimental and has not been stabilized. It is subject to change without warning or announcement.** -The RHEL CoreOS configuration is a YAML document conforming to the following specification, with **_italicized_** entries being optional: +The OpenShift configuration is a YAML document conforming to the following specification, with **_italicized_** entries being optional: * **variant** (string): used to differentiate configs for different operating systems. Must be `rhcos` for this specification. -* **version** (string): the semantic version of the spec for this document. This document is for version `0.2.0-experimental` and generates Ignition configs with version `3.3.0-experimental`. +* **version** (string): the semantic version of the spec for this document. This document is for version `4.8.0-experimental` and generates Ignition configs with version `3.3.0-experimental`. * **ignition** (object): metadata about the configuration itself. * **_config_** (objects): options related to the configuration. * **_merge_** (list of objects): a list of the configs to be merged to the current config. diff --git a/docs/specs.md b/docs/specs.md index 22379bd3..7346e5c2 100644 --- a/docs/specs.md +++ b/docs/specs.md @@ -29,8 +29,8 @@ Do not use **experimental** specifications for anything beyond **development and - Fedora CoreOS (`fcos`) - [v1.4.0-experimental](config-fcos-v1_4-exp.md) -- RHEL CoreOS (`rhcos`) - - [v0.2.0-experimental](config-rhcos-v0_2-exp.md) +- OpenShift (`openshift`) + - [v4.8.0-experimental](config-openshift-v4_8-exp.md) ## FCC specifications and Ignition specifications @@ -43,5 +43,5 @@ Each version of the FCC specification corresponds to a version of the Ignition s | `fcos` | 1.2.0 | 3.2.0 | | `fcos` | 1.3.0 | 3.2.0 | | `fcos` | 1.4.0-experimental | 3.3.0-experimental | +| `openshift` | 4.8.0-experimental | 3.3.0-experimental | | `rhcos` | 0.1.0 | 3.2.0 | -| `rhcos` | 0.2.0-experimental | 3.3.0-experimental | From 16a8e38bdb064e86c8721ac2da450b47e8ffe9d8 Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Fri, 12 Feb 2021 14:27:48 -0500 Subject: [PATCH 3/6] openshift/v4_8_exp: switch back to Ignition spec 3.2.0 We're not going to switch to spec 3.3.0 in this cycle. --- config/config.go | 2 +- config/openshift/v4_8_exp/schema.go | 2 +- config/openshift/v4_8_exp/translate.go | 14 +++++++------- docs/config-openshift-v4_8-exp.md | 2 +- docs/specs.md | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/config/config.go b/config/config.go index 90bd302f..46dddeff 100644 --- a/config/config.go +++ b/config/config.go @@ -47,7 +47,7 @@ func init() { RegisterTranslator("fcos", "1.2.0", fcos1_2.ToIgn3_2Bytes) RegisterTranslator("fcos", "1.3.0", fcos1_3.ToIgn3_2Bytes) RegisterTranslator("fcos", "1.4.0-experimental", fcos1_4_exp.ToIgn3_3Bytes) - RegisterTranslator("openshift", "4.8.0-experimental", openshift4_8_exp.ToIgn3_3Bytes) + RegisterTranslator("openshift", "4.8.0-experimental", openshift4_8_exp.ToIgn3_2Bytes) RegisterTranslator("rhcos", "0.1.0", rhcos0_1.ToIgn3_2Bytes) } diff --git a/config/openshift/v4_8_exp/schema.go b/config/openshift/v4_8_exp/schema.go index b6b635f7..3c4fb382 100644 --- a/config/openshift/v4_8_exp/schema.go +++ b/config/openshift/v4_8_exp/schema.go @@ -15,7 +15,7 @@ package v4_8_exp import ( - fcos "github.com/coreos/fcct/config/fcos/v1_4_exp" + fcos "github.com/coreos/fcct/config/fcos/v1_3" ) type Config struct { diff --git a/config/openshift/v4_8_exp/translate.go b/config/openshift/v4_8_exp/translate.go index 66c60635..f7069dda 100644 --- a/config/openshift/v4_8_exp/translate.go +++ b/config/openshift/v4_8_exp/translate.go @@ -18,22 +18,22 @@ import ( "github.com/coreos/fcct/config/common" cutil "github.com/coreos/fcct/config/util" - "github.com/coreos/ignition/v2/config/v3_3_experimental/types" + "github.com/coreos/ignition/v2/config/v3_2/types" "github.com/coreos/vcontext/report" ) -// ToIgn3_3 translates the config to an Ignition config. It returns a +// ToIgn3_2 translates the config to an Ignition config. It returns a // report of any errors or warnings in the source and resultant config. If // the report has fatal errors or it encounters other problems translating, // an error is returned. -func (c Config) ToIgn3_3(options common.TranslateOptions) (types.Config, report.Report, error) { - cfg, r, err := cutil.Translate(c, "ToIgn3_3Unvalidated", options) +func (c Config) ToIgn3_2(options common.TranslateOptions) (types.Config, report.Report, error) { + cfg, r, err := cutil.Translate(c, "ToIgn3_2Unvalidated", options) return cfg.(types.Config), r, err } -// ToIgn3_3Bytes translates from a v0.2 rcc to a v3.3.0 Ignition config. It returns a report of any errors or +// ToIgn3_2Bytes translates from a v4.8 occ to a v3.2.0 Ignition config. It returns a report of any errors or // warnings in the source and resultant config. If the report has fatal errors or it encounters other problems // translating, an error is returned. -func ToIgn3_3Bytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { - return cutil.TranslateBytes(input, &Config{}, "ToIgn3_3", options) +func ToIgn3_2Bytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { + return cutil.TranslateBytes(input, &Config{}, "ToIgn3_2", options) } diff --git a/docs/config-openshift-v4_8-exp.md b/docs/config-openshift-v4_8-exp.md index 0d813a40..fd07d766 100644 --- a/docs/config-openshift-v4_8-exp.md +++ b/docs/config-openshift-v4_8-exp.md @@ -12,7 +12,7 @@ nav_order: 100 The OpenShift configuration is a YAML document conforming to the following specification, with **_italicized_** entries being optional: * **variant** (string): used to differentiate configs for different operating systems. Must be `rhcos` for this specification. -* **version** (string): the semantic version of the spec for this document. This document is for version `4.8.0-experimental` and generates Ignition configs with version `3.3.0-experimental`. +* **version** (string): the semantic version of the spec for this document. This document is for version `4.8.0-experimental` and generates Ignition configs with version `3.2.0`. * **ignition** (object): metadata about the configuration itself. * **_config_** (objects): options related to the configuration. * **_merge_** (list of objects): a list of the configs to be merged to the current config. diff --git a/docs/specs.md b/docs/specs.md index 7346e5c2..ab9c5f0e 100644 --- a/docs/specs.md +++ b/docs/specs.md @@ -43,5 +43,5 @@ Each version of the FCC specification corresponds to a version of the Ignition s | `fcos` | 1.2.0 | 3.2.0 | | `fcos` | 1.3.0 | 3.2.0 | | `fcos` | 1.4.0-experimental | 3.3.0-experimental | -| `openshift` | 4.8.0-experimental | 3.3.0-experimental | +| `openshift` | 4.8.0-experimental | 3.2.0 | | `rhcos` | 0.1.0 | 3.2.0 | From dc35a4295e320c5bdf2684905feb1c70271a8795 Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Fri, 12 Feb 2021 19:16:21 -0500 Subject: [PATCH 4/6] openshift/v4_8_exp: add report correlation test from fcos We'll need to generalize it. --- config/openshift/v4_8_exp/validate_test.go | 120 +++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 config/openshift/v4_8_exp/validate_test.go diff --git a/config/openshift/v4_8_exp/validate_test.go b/config/openshift/v4_8_exp/validate_test.go new file mode 100644 index 00000000..dbcbe957 --- /dev/null +++ b/config/openshift/v4_8_exp/validate_test.go @@ -0,0 +1,120 @@ +// 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 v4_8_exp + +import ( + "testing" + + "github.com/coreos/fcct/config/common" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/stretchr/testify/assert" +) + +// TestReportCorrelation tests that errors are correctly correlated to their source lines +func TestReportCorrelation(t *testing.T) { + tests := []struct { + in string + message string + line int64 + }{ + // FCCT unused key check + { + `storage: + files: + - path: /z + q: z`, + "Unused key q", + 4, + }, + // FCCT YAML validation error + { + `storage: + files: + - path: /z + contents: + source: https://example.com + inline: z`, + common.ErrTooManyResourceSources.Error(), + 5, + }, + // FCCT YAML validation warning + { + `storage: + files: + - path: /z + mode: 644`, + common.ErrDecimalMode.Error(), + 4, + }, + // FCCT translation error + { + `storage: + files: + - path: /z + contents: + local: z`, + common.ErrNoFilesDir.Error(), + 5, + }, + // Ignition validation error, leaf node + { + `storage: + files: + - path: z`, + errors.ErrPathRelative.Error(), + 3, + }, + // Ignition validation error, partition + { + `storage: + disks: + - device: /dev/z + partitions: + - start_mib: 5`, + errors.ErrNeedLabelOrNumber.Error(), + 5, + }, + // Ignition validation error, partition list + { + `storage: + disks: + - device: /dev/z + partitions: + - number: 1 + should_exist: false + - label: z`, + errors.ErrZeroesWithShouldNotExist.Error(), + 5, + }, + // Ignition duplicate key check, paths + { + `storage: + files: + - path: /z + - path: /z`, + errors.ErrDuplicate.Error(), + 4, + }, + } + + for i, test := range tests { + _, r, _ := ToIgn3_2Bytes([]byte(test.in), common.TranslateBytesOptions{}) + assert.Len(t, r.Entries, 1, "#%d: unexpected report length", i) + assert.Equal(t, test.message, r.Entries[0].Message, "#%d: bad error", i) + assert.NotNil(t, r.Entries[0].Marker.StartP, "#%d: marker start is nil", i) + assert.Equal(t, test.line, r.Entries[0].Marker.StartP.Line, "#%d: incorrect error line", i) + } +} From 2911ba1c5b9b2d93971bbed3b5447ad632907a1b Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Fri, 5 Mar 2021 22:53:19 -0500 Subject: [PATCH 5/6] openshift/v4_8_exp: add metadata struct Don't use it for anything yet, but require that metadata.name is specified. --- config/common/errors.go | 3 + config/openshift/v4_8_exp/schema.go | 6 ++ config/openshift/v4_8_exp/validate.go | 29 ++++++++ config/openshift/v4_8_exp/validate_test.go | 80 +++++++++++++++++----- 4 files changed, 102 insertions(+), 16 deletions(-) create mode 100644 config/openshift/v4_8_exp/validate.go diff --git a/config/common/errors.go b/config/common/errors.go index 5a544c55..fdcc9545 100644 --- a/config/common/errors.go +++ b/config/common/errors.go @@ -46,4 +46,7 @@ var ( // boot device ErrUnknownBootDeviceLayout = errors.New("layout must be one of: aarch64, ppc64le, x86_64") ErrTooFewMirrorDevices = errors.New("mirroring requires at least two devices") + + // MachineConfigs + ErrNameRequired = errors.New("metadata.name is required") ) diff --git a/config/openshift/v4_8_exp/schema.go b/config/openshift/v4_8_exp/schema.go index 3c4fb382..fd19e073 100644 --- a/config/openshift/v4_8_exp/schema.go +++ b/config/openshift/v4_8_exp/schema.go @@ -20,4 +20,10 @@ import ( type Config struct { fcos.Config `yaml:",inline"` + Metadata Metadata `yaml:"metadata"` +} + +type Metadata struct { + Name string `yaml:"name"` + Labels map[string]string `yaml:"labels,omitempty"` } diff --git a/config/openshift/v4_8_exp/validate.go b/config/openshift/v4_8_exp/validate.go new file mode 100644 index 00000000..33a79c61 --- /dev/null +++ b/config/openshift/v4_8_exp/validate.go @@ -0,0 +1,29 @@ +// 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 v4_8_exp + +import ( + "github.com/coreos/fcct/config/common" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (m Metadata) Validate(c path.ContextPath) (r report.Report) { + if m.Name == "" { + r.AddOnError(c.Append("name"), common.ErrNameRequired) + } + return +} diff --git a/config/openshift/v4_8_exp/validate_test.go b/config/openshift/v4_8_exp/validate_test.go index dbcbe957..3aa92098 100644 --- a/config/openshift/v4_8_exp/validate_test.go +++ b/config/openshift/v4_8_exp/validate_test.go @@ -20,9 +20,33 @@ import ( "github.com/coreos/fcct/config/common" "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" "github.com/stretchr/testify/assert" ) +func TestValidateMetadata(t *testing.T) { + tests := []struct { + in Metadata + out error + errPath path.ContextPath + }{ + // missing name + { + Metadata{}, + common.ErrNameRequired, + path.New("yaml", "name"), + }, + } + + for i, test := range tests { + actual := test.in.Validate(path.New("yaml")) + expected := report.Report{} + expected.AddOnError(test.errPath, test.out) + assert.Equal(t, expected, actual, "#%d: bad report", i) + } +} + // TestReportCorrelation tests that errors are correctly correlated to their source lines func TestReportCorrelation(t *testing.T) { tests := []struct { @@ -32,64 +56,85 @@ func TestReportCorrelation(t *testing.T) { }{ // FCCT unused key check { - `storage: + ` + metadata: + name: something + storage: files: - path: /z q: z`, "Unused key q", - 4, + 7, }, // FCCT YAML validation error { - `storage: + ` + metadata: + name: something + storage: files: - path: /z contents: source: https://example.com inline: z`, common.ErrTooManyResourceSources.Error(), - 5, + 8, }, // FCCT YAML validation warning { - `storage: + ` + metadata: + name: something + storage: files: - path: /z mode: 644`, common.ErrDecimalMode.Error(), - 4, + 7, }, // FCCT translation error { - `storage: + ` + metadata: + name: something + storage: files: - path: /z contents: local: z`, common.ErrNoFilesDir.Error(), - 5, + 8, }, // Ignition validation error, leaf node { - `storage: + ` + metadata: + name: something + storage: files: - path: z`, errors.ErrPathRelative.Error(), - 3, + 6, }, // Ignition validation error, partition { - `storage: + ` + metadata: + name: something + storage: disks: - device: /dev/z partitions: - start_mib: 5`, errors.ErrNeedLabelOrNumber.Error(), - 5, + 8, }, // Ignition validation error, partition list { - `storage: + ` + metadata: + name: something + storage: disks: - device: /dev/z partitions: @@ -97,16 +142,19 @@ func TestReportCorrelation(t *testing.T) { should_exist: false - label: z`, errors.ErrZeroesWithShouldNotExist.Error(), - 5, + 8, }, // Ignition duplicate key check, paths { - `storage: + ` + metadata: + name: something + storage: files: - path: /z - path: /z`, errors.ErrDuplicate.Error(), - 4, + 7, }, } From 9a950e9291666eaf8d5f777d92963470aa1e4036 Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Fri, 5 Mar 2021 23:16:17 -0500 Subject: [PATCH 6/6] openshift/v4_8_exp: generate either MachineConfig or Ignition config Generate a MachineConfig by default, but produce an Ignition config if the Raw/-r/--raw translation option is specified. We host our own reimplementation of MachineConfig structs to avoid pulling in a massive (and circular) set of dependencies from openshift/machine-config-operator. --- config/common/common.go | 1 + config/config.go | 2 +- config/openshift/v4_8_exp/result/schema.go | 39 ++++++++++++ config/openshift/v4_8_exp/translate.go | 73 +++++++++++++++++++++- config/openshift/v4_8_exp/validate_test.go | 14 +++-- config/util/util.go | 27 ++++++++ docs/config-openshift-v4_8-exp.md | 5 ++ internal/main.go | 1 + translate/set.go | 21 +++++++ translate/util.go | 13 ++++ 10 files changed, 187 insertions(+), 9 deletions(-) create mode 100644 config/openshift/v4_8_exp/result/schema.go diff --git a/config/common/common.go b/config/common/common.go index c46148b9..3b6afea7 100644 --- a/config/common/common.go +++ b/config/common/common.go @@ -23,5 +23,6 @@ type TranslateOptions struct { type TranslateBytesOptions struct { TranslateOptions Pretty bool + Raw bool // encode only the Ignition config, not any wrapper Strict bool } diff --git a/config/config.go b/config/config.go index 46dddeff..599f235f 100644 --- a/config/config.go +++ b/config/config.go @@ -47,7 +47,7 @@ func init() { RegisterTranslator("fcos", "1.2.0", fcos1_2.ToIgn3_2Bytes) RegisterTranslator("fcos", "1.3.0", fcos1_3.ToIgn3_2Bytes) RegisterTranslator("fcos", "1.4.0-experimental", fcos1_4_exp.ToIgn3_3Bytes) - RegisterTranslator("openshift", "4.8.0-experimental", openshift4_8_exp.ToIgn3_2Bytes) + RegisterTranslator("openshift", "4.8.0-experimental", openshift4_8_exp.ToConfigBytes) RegisterTranslator("rhcos", "0.1.0", rhcos0_1.ToIgn3_2Bytes) } diff --git a/config/openshift/v4_8_exp/result/schema.go b/config/openshift/v4_8_exp/result/schema.go new file mode 100644 index 00000000..91f9c733 --- /dev/null +++ b/config/openshift/v4_8_exp/result/schema.go @@ -0,0 +1,39 @@ +// 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 result + +import ( + "github.com/coreos/ignition/v2/config/v3_2/types" +) + +// We round-trip through JSON because Ignition uses `json` struct tags, +// so all struct tags need to be `json` even though we're ultimately +// writing YAML. + +type MachineConfig struct { + ApiVersion string `json:"apiVersion"` + Kind string `json:"kind"` + Metadata Metadata `json:"metadata"` + Spec Spec `json:"spec"` +} + +type Metadata struct { + Name string `json:"name"` + Labels map[string]string `json:"labels,omitempty"` +} + +type Spec struct { + Config types.Config `json:"config"` +} diff --git a/config/openshift/v4_8_exp/translate.go b/config/openshift/v4_8_exp/translate.go index f7069dda..53d904b3 100644 --- a/config/openshift/v4_8_exp/translate.go +++ b/config/openshift/v4_8_exp/translate.go @@ -16,12 +16,75 @@ package v4_8_exp import ( "github.com/coreos/fcct/config/common" + "github.com/coreos/fcct/config/openshift/v4_8_exp/result" cutil "github.com/coreos/fcct/config/util" + "github.com/coreos/fcct/translate" "github.com/coreos/ignition/v2/config/v3_2/types" + "github.com/coreos/vcontext/path" "github.com/coreos/vcontext/report" ) +// ToMachineConfig4_8Unvalidated translates the config to a MachineConfig. It also +// returns the set of translations it did so paths in the resultant config +// can be tracked back to their source in the source config. No config +// validation is performed on input or output. +func (c Config) ToMachineConfig4_8Unvalidated(options common.TranslateOptions) (result.MachineConfig, translate.TranslationSet, report.Report) { + cfg, ts, r := c.Config.ToIgn3_2Unvalidated(options) + if r.IsFatal() { + return result.MachineConfig{}, ts, r + } + + // wrap + ts = ts.PrefixPaths(path.New("yaml"), path.New("json", "spec", "config")) + mc := result.MachineConfig{ + ApiVersion: "machineconfiguration.openshift.io/v1", + Kind: "MachineConfig", + Metadata: result.Metadata{ + Name: c.Metadata.Name, + Labels: make(map[string]string), + }, + Spec: result.Spec{ + Config: cfg, + }, + } + ts.AddTranslation(path.New("yaml", "version"), path.New("json", "apiVersion")) + ts.AddTranslation(path.New("yaml", "version"), path.New("json", "kind")) + ts.AddTranslation(path.New("yaml", "metadata"), path.New("json", "metadata")) + ts.AddTranslation(path.New("yaml", "metadata", "name"), path.New("json", "metadata", "name")) + ts.AddTranslation(path.New("yaml", "version"), path.New("json", "spec")) + ts.AddTranslation(path.New("yaml"), path.New("json", "spec", "config")) + for k, v := range c.Metadata.Labels { + mc.Metadata.Labels[k] = v + ts.AddTranslation(path.New("yaml", "metadata", "labels", k), path.New("json", "metadata", "labels", k)) + } + if len(mc.Metadata.Labels) > 0 { + ts.AddTranslation(path.New("yaml", "metadata", "labels"), path.New("json", "metadata", "labels")) + } + + return mc, ts, r +} + +// ToMachineConfig4_8 translates the config to a MachineConfig. It returns a +// report of any errors or warnings in the source and resultant config. If +// the report has fatal errors or it encounters other problems translating, +// an error is returned. +func (c Config) ToMachineConfig4_8(options common.TranslateOptions) (result.MachineConfig, report.Report, error) { + cfg, r, err := cutil.Translate(c, "ToMachineConfig4_8Unvalidated", options) + return cfg.(result.MachineConfig), r, err +} + +// ToIgn3_2Unvalidated translates the config to an Ignition config. It also +// returns the set of translations it did so paths in the resultant config +// can be tracked back to their source in the source config. No config +// validation is performed on input or output. +func (c Config) ToIgn3_2Unvalidated(options common.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) { + mc, ts, r := c.ToMachineConfig4_8Unvalidated(options) + cfg := mc.Spec.Config + ts = ts.Descend(path.New("json", "spec", "config")) + return cfg, ts, r +} + // ToIgn3_2 translates the config to an Ignition config. It returns a // report of any errors or warnings in the source and resultant config. If // the report has fatal errors or it encounters other problems translating, @@ -31,9 +94,13 @@ func (c Config) ToIgn3_2(options common.TranslateOptions) (types.Config, report. return cfg.(types.Config), r, err } -// ToIgn3_2Bytes translates from a v4.8 occ to a v3.2.0 Ignition config. It returns a report of any errors or +// ToConfigBytes translates from a v4.8 occ to a v4.8 MachineConfig or a v3.2.0 Ignition config. It returns a report of any errors or // warnings in the source and resultant config. If the report has fatal errors or it encounters other problems // translating, an error is returned. -func ToIgn3_2Bytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { - return cutil.TranslateBytes(input, &Config{}, "ToIgn3_2", options) +func ToConfigBytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { + if options.Raw { + return cutil.TranslateBytes(input, &Config{}, "ToIgn3_2", options) + } else { + return cutil.TranslateBytesYAML(input, &Config{}, "ToMachineConfig4_8", options) + } } diff --git a/config/openshift/v4_8_exp/validate_test.go b/config/openshift/v4_8_exp/validate_test.go index 3aa92098..3cfe24ad 100644 --- a/config/openshift/v4_8_exp/validate_test.go +++ b/config/openshift/v4_8_exp/validate_test.go @@ -159,10 +159,14 @@ func TestReportCorrelation(t *testing.T) { } for i, test := range tests { - _, r, _ := ToIgn3_2Bytes([]byte(test.in), common.TranslateBytesOptions{}) - assert.Len(t, r.Entries, 1, "#%d: unexpected report length", i) - assert.Equal(t, test.message, r.Entries[0].Message, "#%d: bad error", i) - assert.NotNil(t, r.Entries[0].Marker.StartP, "#%d: marker start is nil", i) - assert.Equal(t, test.line, r.Entries[0].Marker.StartP.Line, "#%d: incorrect error line", i) + for _, raw := range []bool{false, true} { + _, r, _ := ToConfigBytes([]byte(test.in), common.TranslateBytesOptions{ + Raw: raw, + }) + assert.Len(t, r.Entries, 1, "#%d: unexpected report length, raw %v", i, raw) + assert.Equal(t, test.message, r.Entries[0].Message, "#%d: bad error, raw %v", i, raw) + assert.NotNil(t, r.Entries[0].Marker.StartP, "#%d: marker start is nil, raw %v", i, raw) + assert.Equal(t, test.line, r.Entries[0].Marker.StartP.Line, "#%d: incorrect error line, raw %v", i, raw) + } } } diff --git a/config/util/util.go b/config/util/util.go index 1d31055b..84d95522 100644 --- a/config/util/util.go +++ b/config/util/util.go @@ -133,6 +133,33 @@ func TranslateBytes(input []byte, container interface{}, translateMethod string, return outbytes, r, err } +func TranslateBytesYAML(input []byte, container interface{}, translateMethod string, options common.TranslateBytesOptions) ([]byte, report.Report, error) { + // marshal to JSON, unmarshal, remarshal to YAML. there's no other + // good way to respect the `json` struct tags. + // https://github.com/go-yaml/yaml/issues/424 + jsonCfg, r, err := TranslateBytes(input, container, translateMethod, options) + if err != nil { + return jsonCfg, r, err + } + + var ifaceCfg interface{} + if err := json.Unmarshal(jsonCfg, &ifaceCfg); err != nil { + return []byte{}, r, err + } + + var yamlCfgBuf bytes.Buffer + encoder := yaml.NewEncoder(&yamlCfgBuf) + encoder.SetIndent(2) + if err := encoder.Encode(ifaceCfg); err != nil { + return []byte{}, r, err + } + if err := encoder.Close(); err != nil { + return []byte{}, r, err + } + yamlCfg := bytes.Trim(yamlCfgBuf.Bytes(), "\n") + return yamlCfg, r, err +} + // unmarshal unmarshals the data to "to" and also returns a context tree for the source. If strict // is set it errors out on unused keys. func unmarshal(data []byte, to interface{}, strict bool) (tree.Node, error) { diff --git a/docs/config-openshift-v4_8-exp.md b/docs/config-openshift-v4_8-exp.md index fd07d766..266689e0 100644 --- a/docs/config-openshift-v4_8-exp.md +++ b/docs/config-openshift-v4_8-exp.md @@ -13,6 +13,9 @@ The OpenShift configuration is a YAML document conforming to the following speci * **variant** (string): used to differentiate configs for different operating systems. Must be `rhcos` for this specification. * **version** (string): the semantic version of the spec for this document. This document is for version `4.8.0-experimental` and generates Ignition configs with version `3.2.0`. +* **metadata** (object): metadata about the generated MachineConfig resource. Respected when rendering to a MachineConfig, ignored when rendering directly to an Ignition config. + * **name** (string): a unique [name][k8s-names] for this MachineConfig resource. + * **_labels_** (object): string key/value pairs to apply as [Kubernetes labels][k8s-labels] to this MachineConfig resource. * **ignition** (object): metadata about the configuration itself. * **_config_** (objects): options related to the configuration. * **_merge_** (list of objects): a list of the configs to be merged to the current config. @@ -202,6 +205,8 @@ The OpenShift configuration is a YAML document conforming to the following speci * **_mirror_** (object): describes mirroring of the boot disk for fault tolerance. * **_devices_** (list of strings): the list of whole-disk devices (not partitions) to include in the disk array, referenced by their absolute path. At least two devices must be specified. +[k8s-names]: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names +[k8s-labels]: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ [part-types]: http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs [rfc2397]: https://tools.ietf.org/html/rfc2397 [systemd-escape]: https://www.freedesktop.org/software/systemd/man/systemd-escape.html diff --git a/internal/main.go b/internal/main.go index 4deaa444..86272fc6 100644 --- a/internal/main.go +++ b/internal/main.go @@ -45,6 +45,7 @@ func main() { pflag.Lookup("debug").Hidden = true pflag.BoolVarP(&options.Strict, "strict", "s", false, "fail on any warning") pflag.BoolVarP(&options.Pretty, "pretty", "p", false, "output formatted json") + pflag.BoolVarP(&options.Raw, "raw", "r", false, "never wrap in a MachineConfig; force Ignition output") pflag.StringVar(&input, "input", "", "read from input file instead of stdin") pflag.Lookup("input").Deprecated = "specify filename directly on command line" pflag.Lookup("input").Hidden = true diff --git a/translate/set.go b/translate/set.go index c1a89d6f..c8e60a8b 100644 --- a/translate/set.go +++ b/translate/set.go @@ -142,6 +142,27 @@ func (ts TranslationSet) PrefixPaths(fromPrefix, toPrefix path.ContextPath) Tran return ret } +// Descend returns the subtree of translations rooted at the specified To path. +func (ts TranslationSet) Descend(to path.ContextPath) TranslationSet { + ret := NewTranslationSet(ts.FromTag, ts.ToTag) +OUTER: + for _, tr := range ts.Set { + if len(tr.To.Path) < len(to.Path) { + // can't be in the requested subtree; skip + continue + } + for i, e := range to.Path { + if tr.To.Path[i] != e { + // not in the requested subtree; skip + continue OUTER + } + } + subtreePath := path.New(tr.To.Tag, tr.To.Path[len(to.Path):]...) + ret.AddTranslation(tr.From, subtreePath) + } + return ret +} + // DebugVerifyCoverage recursively checks whether every non-zero field in v // has a translation. If translations are missing, it returns a multi-line // error listing them. diff --git a/translate/util.go b/translate/util.go index cae42098..112a1e4f 100644 --- a/translate/util.go +++ b/translate/util.go @@ -81,6 +81,19 @@ func getAllPaths(v reflect.Value, tag string, includeZeroFields bool) []path.Con } } return ret + case k == reflect.Map: + // we don't have these in FCCs or Ignition configs, but + // we need to support validating translations of + // metadata.labels in MachineConfig output + ret := []path.ContextPath{} + iter := v.MapRange() + for iter.Next() { + // for struct, pointer to struct, etc., add any children + ret = append(ret, prefixPaths(getAllPaths(iter.Value(), tag, includeZeroFields), iter.Key())...) + // add map entry + ret = append(ret, path.New(tag, iter.Key())) + } + return ret default: panic("Encountered unexpected type. This is a bug, please file a report") }