Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ import (
openshift4_19 "github.com/coreos/butane/config/openshift/v4_19"
openshift4_20 "github.com/coreos/butane/config/openshift/v4_20"
openshift4_21 "github.com/coreos/butane/config/openshift/v4_21"
openshift4_22_exp "github.com/coreos/butane/config/openshift/v4_22_exp"
openshift4_22 "github.com/coreos/butane/config/openshift/v4_22"
openshift4_23_exp "github.com/coreos/butane/config/openshift/v4_23_exp"
openshift4_8 "github.com/coreos/butane/config/openshift/v4_8"
openshift4_9 "github.com/coreos/butane/config/openshift/v4_9"
r4e1_0 "github.com/coreos/butane/config/r4e/v1_0"
Expand Down Expand Up @@ -93,7 +94,8 @@ func init() {
RegisterTranslator("openshift", "4.19.0", openshift4_19.ToConfigBytes)
RegisterTranslator("openshift", "4.20.0", openshift4_20.ToConfigBytes)
RegisterTranslator("openshift", "4.21.0", openshift4_21.ToConfigBytes)
RegisterTranslator("openshift", "4.22.0-experimental", openshift4_22_exp.ToConfigBytes)
RegisterTranslator("openshift", "4.22.0", openshift4_22.ToConfigBytes)
RegisterTranslator("openshift", "4.23.0-experimental", openshift4_23_exp.ToConfigBytes)
RegisterTranslator("r4e", "1.0.0", r4e1_0.ToIgn3_3Bytes)
RegisterTranslator("r4e", "1.1.0", r4e1_1.ToIgn3_4Bytes)
RegisterTranslator("r4e", "1.2.0-experimental", r4e1_2_exp.ToIgn3_7Bytes)
Expand Down
48 changes: 48 additions & 0 deletions config/openshift/v4_22/result/schema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// 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_6/types"
)

const (
MC_API_VERSION = "machineconfiguration.openshift.io/v1"
MC_KIND = "MachineConfig"
)

// 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"`
KernelArguments []string `json:"kernelArguments,omitempty"`
Extensions []string `json:"extensions,omitempty"`
FIPS *bool `json:"fips,omitempty"`
KernelType *string `json:"kernelType,omitempty"`
}
39 changes: 39 additions & 0 deletions config/openshift/v4_22/schema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// 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 v4_22

import (
fcos "github.com/coreos/butane/config/fcos/v1_7"
)

const ROLE_LABEL_KEY = "machineconfiguration.openshift.io/role"

type Config struct {
fcos.Config `yaml:",inline"`
Metadata Metadata `yaml:"metadata"`
OpenShift OpenShift `yaml:"openshift"`
}

type Metadata struct {
Name string `yaml:"name"`
Labels map[string]string `yaml:"labels,omitempty"`
}

type OpenShift struct {
KernelArguments []string `yaml:"kernel_arguments"`
Extensions []string `yaml:"extensions"`
FIPS *bool `yaml:"fips"`
KernelType *string `yaml:"kernel_type"`
}
261 changes: 261 additions & 0 deletions config/openshift/v4_22/translate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
// 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 v4_22

import (
"net/url"

"github.com/coreos/butane/config/common"
"github.com/coreos/butane/config/openshift/v4_22/result"
cutil "github.com/coreos/butane/config/util"
"github.com/coreos/butane/translate"

"github.com/coreos/ignition/v2/config/v3_6/types"
"github.com/coreos/vcontext/path"
"github.com/coreos/vcontext/report"
)

// Error classes:
//
// UNPARSABLE - Cannot be rendered into a config by the MCC. If present in
// MC, MCC will mark the pool degraded. We reject these.
//
// FORBIDDEN - Not supported by the MCD. If present in MC, MCD will mark
// the node degraded. We reject these.
//
// REDUNDANT - Feature is also provided by a MachineConfig-specific field
// with different semantics. To reduce confusion, disable this
// implementation.
//
// IMMUTABLE - Permitted in MC, passed through to Ignition, but not
// supported by the MCD. MCD will mark the node degraded if the field
// changes after the node is provisioned. We reject these outright to
// discourage their use.
//
// TRIPWIRE - A subset of fields in the containing struct are supported by
// the MCD. If the struct contents change after the node is provisioned,
// and the struct contains unsupported fields, MCD will mark the node
// degraded, even if the change only affects supported fields. We reject
// these.

var (
// See also validateRHCOSSupport() and validateMCOSupport()
fieldFilters = cutil.NewFilters(result.MachineConfig{}, cutil.FilterMap{
// UNPARSABLE, REDUNDANT
"spec.config.kernelArguments": common.ErrKernelArgumentSupport,
// IMMUTABLE
"spec.config.passwd.groups": common.ErrGroupSupport,
// TRIPWIRE
"spec.config.passwd.users.gecos": common.ErrUserFieldSupport,
// TRIPWIRE
"spec.config.passwd.users.groups": common.ErrUserFieldSupport,
// TRIPWIRE
"spec.config.passwd.users.homeDir": common.ErrUserFieldSupport,
// TRIPWIRE
"spec.config.passwd.users.noCreateHome": common.ErrUserFieldSupport,
// TRIPWIRE
"spec.config.passwd.users.noLogInit": common.ErrUserFieldSupport,
// TRIPWIRE
"spec.config.passwd.users.noUserGroup": common.ErrUserFieldSupport,
// TRIPWIRE
"spec.config.passwd.users.primaryGroup": common.ErrUserFieldSupport,
// TRIPWIRE
"spec.config.passwd.users.shell": common.ErrUserFieldSupport,
// TRIPWIRE
"spec.config.passwd.users.shouldExist": common.ErrUserFieldSupport,
// TRIPWIRE
"spec.config.passwd.users.system": common.ErrUserFieldSupport,
// TRIPWIRE
"spec.config.passwd.users.uid": common.ErrUserFieldSupport,
// IMMUTABLE
"spec.config.storage.directories": common.ErrDirectorySupport,
// FORBIDDEN
"spec.config.storage.files.append": common.ErrFileAppendSupport,
// redundant with a check from Ignition validation, but ensures we
// exclude the section from docs
"spec.config.storage.files.contents.httpHeaders": common.ErrFileHeaderSupport,
// IMMUTABLE
// If you change this to be less restrictive without adding
// link support in the MCO, consider what should happen if
// the user specifies a storage.tree that includes symlinks.
"spec.config.storage.links": common.ErrLinkSupport,
})
)

// Return FieldFilters for this spec.
func (c Config) FieldFilters() *cutil.FieldFilters {
return &fieldFilters
}

// ToMachineConfig4_22Unvalidated 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_22Unvalidated(options common.TranslateOptions) (result.MachineConfig, translate.TranslationSet, report.Report) {
cfg, ts, r := c.Config.ToIgn3_6Unvalidated(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: result.MC_API_VERSION,
Kind: result.MC_KIND,
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", "metadata", "labels"), path.New("json", "metadata", "labels"))
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))
}

// translate OpenShift fields
tr := translate.NewTranslator("yaml", "json", options)
from := &c.OpenShift
to := &mc.Spec
ts2, r2 := translate.Prefixed(tr, "extensions", &from.Extensions, &to.Extensions)
translate.MergeP(tr, ts2, &r2, "fips", &from.FIPS, &to.FIPS)
translate.MergeP2(tr, ts2, &r2, "kernel_arguments", &from.KernelArguments, "kernelArguments", &to.KernelArguments)
translate.MergeP2(tr, ts2, &r2, "kernel_type", &from.KernelType, "kernelType", &to.KernelType)
ts.MergeP2("openshift", "spec", ts2)
r.Merge(r2)

// finally, check the fully desugared config for RHCOS and MCO support
r.Merge(validateRHCOSSupport(mc))
r.Merge(validateMCOSupport(mc))

return mc, ts, r
}

// ToMachineConfig4_22 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_22(options common.TranslateOptions) (result.MachineConfig, report.Report, error) {
cfg, r, err := cutil.Translate(c, "ToMachineConfig4_22Unvalidated", options)
return cfg.(result.MachineConfig), r, err
}

// ToIgn3_6Unvalidated 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_6Unvalidated(options common.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) {
mc, ts, r := c.ToMachineConfig4_22Unvalidated(options)
cfg := mc.Spec.Config

// report warnings if there are any non-empty fields in Spec (other
// than the Ignition config itself) that we're ignoring
mc.Spec.Config = types.Config{}
warnings := translate.PrefixReport(cutil.CheckForElidedFields(mc.Spec), "spec")
// translate from json space into yaml space, since the caller won't
// have enough info to do it
r.Merge(cutil.TranslateReportPaths(warnings, ts))

ts = ts.Descend(path.New("json", "spec", "config"))
return cfg, ts, r
}

// ToIgn3_6 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_6(options common.TranslateOptions) (types.Config, report.Report, error) {
cfg, r, err := cutil.Translate(c, "ToIgn3_6Unvalidated", options)
return cfg.(types.Config), r, err
}

// ToConfigBytes translates from a v4.22 Butane config to a v4.22 MachineConfig or a v3.6.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 ToConfigBytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) {
if options.Raw {
return cutil.TranslateBytes(input, &Config{}, "ToIgn3_6", options)
} else {
return cutil.TranslateBytesYAML(input, &Config{}, "ToMachineConfig4_22", options)
}
}

// Error on fields that are rejected by RHCOS.
//
// Some of these fields may have been generated by sugar (e.g.
// boot_device.luks), so we work in JSON (output) space and then translate
// paths back to YAML (input) space. That's also the reason we do these
// checks after translation, rather than during validation.
func validateRHCOSSupport(mc result.MachineConfig) report.Report {
var r report.Report
for i, fs := range mc.Spec.Config.Storage.Filesystems {
if fs.Format != nil && *fs.Format == "btrfs" {
// we don't ship mkfs.btrfs
r.AddOnError(path.New("json", "spec", "config", "storage", "filesystems", i, "format"), common.ErrBtrfsSupport)
}
}
return r
}

// Error on fields that are rejected outright by the MCO, or that are
// unsupported by the MCO and we want to discourage.
//
// https://github.com/openshift/machine-config-operator/blob/d6dabadeca05/MachineConfigDaemon.md#supported-vs-unsupported-ignition-config-changes
//
// Some of these fields may have been generated by sugar (e.g. storage.trees),
// so we work in JSON (output) space and then translate paths back to YAML
// (input) space. That's also the reason we do these checks after
// translation, rather than during validation.
func validateMCOSupport(mc result.MachineConfig) report.Report {
// See also fieldFilters at the top of this file.

var r report.Report
for i, fs := range mc.Spec.Config.Storage.Filesystems {
if fs.Format != nil && *fs.Format == "none" {
// UNPARSABLE
r.AddOnError(path.New("json", "spec", "config", "storage", "filesystems", i, "format"), common.ErrFilesystemNoneSupport)
}
}
for i, file := range mc.Spec.Config.Storage.Files {
if file.Contents.Source != nil {
fileSource, err := url.Parse(*file.Contents.Source)
// parse errors will be caught by normal config validation
if err == nil && fileSource.Scheme != "data" {
// FORBIDDEN
r.AddOnError(path.New("json", "spec", "config", "storage", "files", i, "contents", "source"), common.ErrFileSchemeSupport)
}
}
if file.Mode != nil && *file.Mode & ^0777 != 0 {
// UNPARSABLE
r.AddOnError(path.New("json", "spec", "config", "storage", "files", i, "mode"), common.ErrFileSpecialModeSupport)
}
}
for i, user := range mc.Spec.Config.Passwd.Users {
if user.Name != "core" {
// TRIPWIRE
r.AddOnError(path.New("json", "spec", "config", "passwd", "users", i, "name"), common.ErrUserNameSupport)
}
}
return r
}
Loading
Loading