diff --git a/go.mod b/go.mod index 4ad4fce70e..dbc7a3bc3d 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/containers/image v3.0.2+incompatible github.com/containers/image/v5 v5.5.1 github.com/containers/storage v1.20.2 - github.com/coreos/fcct v0.5.0 + github.com/coreos/fcct v0.7.0 github.com/coreos/go-semver v0.3.0 github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect github.com/coreos/ign-converter v0.0.0-20200629171308-e40a44f244c5 @@ -53,6 +53,7 @@ require ( github.com/vincent-petithory/dataurl v0.0.0-20160330182126-9a301d65acbb github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 + gopkg.in/yaml.v2 v2.3.0 k8s.io/api v0.19.2 k8s.io/apiextensions-apiserver v0.19.0 k8s.io/apimachinery v0.19.2 diff --git a/go.sum b/go.sum index e3818e841f..142824b3b9 100644 --- a/go.sum +++ b/go.sum @@ -155,8 +155,8 @@ github.com/containers/storage v1.20.2 h1:tw/uKRPDnmVrluIzer3dawTFG/bTJLP8IEUyHFh github.com/containers/storage v1.20.2/go.mod h1:oOB9Ie8OVPojvoaKWEGSEtHbXUAs+tSyr7RO7ZGteMc= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/fcct v0.5.0 h1:f/z+MCoR2vULes+MyoPEApQ6iluy/JbXoRi6dahPItQ= -github.com/coreos/fcct v0.5.0/go.mod h1:cbE+j77YSQwFB2fozWVB3qsI2Pi3YiVEbDz/b6Yywdo= +github.com/coreos/fcct v0.7.0 h1:g2RmLSxURkD9xqO3ZUIumeCsHCxUe9+oLbbTuai2FeI= +github.com/coreos/fcct v0.7.0/go.mod h1:boOA3u8OgiHWoiSOC9ikl4u8/3GuO7uQco/9bdwMeFQ= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= @@ -172,7 +172,6 @@ github.com/coreos/ign-converter v0.0.0-20200629171308-e40a44f244c5 h1:rBga8xIJ7M github.com/coreos/ign-converter v0.0.0-20200629171308-e40a44f244c5/go.mod h1:LNu0WTt8iVH/WJH15R/SjZw7AdyY2qAyf9ILZTCBvho= github.com/coreos/ignition v0.35.0 h1:UFodoYq1mOPrbEjtxIsZbThcDyQwAI1owczRDqWmKkQ= github.com/coreos/ignition v0.35.0/go.mod h1:WJQapxzEn9DE0ryxsGvm8QnBajm/XsS/PkrDqSpz+bA= -github.com/coreos/ignition/v2 v2.1.1/go.mod h1:RqmqU64zxarUJa3l4cHtbhcSwfQLpUhv0WVziZwoXvE= github.com/coreos/ignition/v2 v2.3.0 h1:TK+STbzVe6KZp4tQ2IaNSRMiWX4/diNngep1F7tP7Zk= github.com/coreos/ignition/v2 v2.3.0/go.mod h1:85dmM/CERMZXNrJsXqtNLIxR/dn8G9qlL1CmEjCugp0= github.com/coreos/ignition/v2 v2.7.0 h1:JCKxJllVtnk1lQY1uisxrtFSHG5L2NI1LRzc8wBEk84= diff --git a/pkg/controller/common/helpers.go b/pkg/controller/common/helpers.go index 4f0c0b3aed..5551a49bd0 100644 --- a/pkg/controller/common/helpers.go +++ b/pkg/controller/common/helpers.go @@ -8,7 +8,10 @@ import ( "sort" "github.com/clarketm/json" - fcctbase "github.com/coreos/fcct/base/v0_1" + fcctBase "github.com/coreos/fcct/base" + fcctBase0_2 "github.com/coreos/fcct/base/v0_2" + fcctCfgCommon "github.com/coreos/fcct/config/common" + fcctCfg1_1 "github.com/coreos/fcct/config/v1_1" "github.com/coreos/ign-converter/translate/v23tov30" "github.com/coreos/ign-converter/translate/v31tov22" ign2error "github.com/coreos/ignition/config/shared/errors" @@ -33,6 +36,10 @@ import ( mcfgclientset "github.com/openshift/machine-config-operator/pkg/generated/clientset/versioned" ) +var fcctTranslateOptions fcctBase.TranslateOptions = fcctBase.TranslateOptions{ + NoResourceAutoCompression: true, +} + // MergeMachineConfigs combines multiple machineconfig objects into one object. // It sorts all the configs in increasing order of their name. // It uses the Ignition config from first object as base and appends all the rest. @@ -284,9 +291,27 @@ func ValidateMachineConfig(cfg mcfgv1.MachineConfigSpec) error { return nil } -// IgnParseWrapper parses rawIgn for both V2 and V3 ignition configs and returns +func isJSON(data []byte) bool { + var js json.RawMessage + return json.Unmarshal(data, &js) == nil +} + +// IgnParseWrapper parses rawIgn for CoreOS (FCCT) configs as well as V2 and V3 ignition configs and returns // a V2 or V3 Config or an error. This wrapper is necessary since V2 and V3 use different parsers. func IgnParseWrapper(rawIgn []byte) (interface{}, error) { + if isJSON := isJSON(rawIgn); !isJSON { + // try transpiling FCCT to Ign spec v3.1 + rawFcctIgnCfgV3_1, rptFcct, errFcct := fcctCfg1_1.TranslateBytes(rawIgn, fcctCfgCommon.TranslateOptions{BaseOptions: fcctTranslateOptions}) + if errFcct != nil || rptFcct.IsFatal() { + return ign3types.Config{}, errors.Errorf("transpiling FCCT config failed with error: %v\nReport: %v", errFcct, rptFcct) + } + fcctIgnCfgV3_1, rptFcctIgnV3_1, errFcctIgnV3_1 := ign3.Parse(rawFcctIgnCfgV3_1) + if errFcctIgnV3_1 != nil || rptFcctIgnV3_1.IsFatal() { + return ign3types.Config{}, errors.Errorf("parsing Ignition config spec v3.1 transpiled from FCCT failed with error: %v\nReport: %v", errFcctIgnV3_1, rptFcctIgnV3_1) + } + return fcctIgnCfgV3_1, nil + } + ignCfgV3_1, rptV3_1, errV3_1 := ign3.Parse(rawIgn) if errV3_1 == nil && !rptV3_1.IsFatal() { return ignCfgV3_1, nil @@ -439,37 +464,35 @@ func TranspileCoreOSConfigToIgn(files, units []string) (*ign3types.Config, error outConfig := ign3types.Config{} // Convert data to Ignition resources for _, d := range files { - f := new(fcctbase.File) + f := new(fcctBase0_2.File) if err := yaml.Unmarshal([]byte(d), f); err != nil { return nil, fmt.Errorf("failed to unmarshal file into struct: %v", err) } f.Overwrite = &overwrite // Add the file to the config - var ctCfg fcctbase.Config + var ctCfg fcctBase0_2.Config ctCfg.Storage.Files = append(ctCfg.Storage.Files, *f) - ign3_0config, tSet, err := ctCfg.ToIgn3_0() - if err != nil { - return nil, fmt.Errorf("failed to transpile config to Ignition config %s\nTranslation set: %v", err, tSet) + ign3_1config, tSet, report := ctCfg.ToIgn3_1(fcctTranslateOptions) + if report.IsFatal() { + return nil, fmt.Errorf("failed to transpile config to Ignition config.\nReport: %v\nTranslation set: %v", report, tSet) } - ign3_1config := translate3.Translate(ign3_0config) outConfig = ign3.Merge(outConfig, ign3_1config) } for _, d := range units { - u := new(fcctbase.Unit) + u := new(fcctBase0_2.Unit) if err := yaml.Unmarshal([]byte(d), u); err != nil { return nil, fmt.Errorf("failed to unmarshal systemd unit into struct: %v", err) } // Add the unit to the config - var ctCfg fcctbase.Config + var ctCfg fcctBase0_2.Config ctCfg.Systemd.Units = append(ctCfg.Systemd.Units, *u) - ign3_0config, tSet, err := ctCfg.ToIgn3_0() - if err != nil { - return nil, fmt.Errorf("failed to transpile config to Ignition config %s\nTranslation set: %v", err, tSet) + ign3_1config, tSet, report := ctCfg.ToIgn3_1(fcctTranslateOptions) + if report.IsFatal() { + return nil, fmt.Errorf("failed to transpile config to Ignition config.\nReport: %v\nTranslation set: %v", report, tSet) } - ign3_1config := translate3.Translate(ign3_0config) outConfig = ign3.Merge(outConfig, ign3_1config) } diff --git a/pkg/controller/common/helpers_test.go b/pkg/controller/common/helpers_test.go index c7055293a4..54744b8174 100644 --- a/pkg/controller/common/helpers_test.go +++ b/pkg/controller/common/helpers_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/clarketm/json" + fcctBase0_2 "github.com/coreos/fcct/base/v0_2" + fcctCfg1_1 "github.com/coreos/fcct/config/v1_1" ign2types "github.com/coreos/ignition/config/v2_2/types" ign3types "github.com/coreos/ignition/v2/config/v3_1/types" "github.com/stretchr/testify/assert" @@ -144,6 +146,20 @@ func TestParseAndConvert(t *testing.T) { convertedIgn, err = ParseAndConvertConfig(rawIgn) require.NotNil(t, err) assert.Equal(t, ign3types.Config{}, convertedIgn) + + // Make a FCCT comp config + testFcctConfig := fcctCfg1_1.Config{} + testFcctConfig.Version = "1.1.0" + tempFcctUser := fcctBase0_2.PasswdUser{Name: "core", SSHAuthorizedKeys: []fcctBase0_2.SSHAuthorizedKey{"5678", "abc"}} + testFcctConfig.Passwd.Users = []fcctBase0_2.PasswdUser{tempFcctUser} + + // turn FCCT config into a raw []byte + rawFcct := helpers.YamlMarshalOrDie(testFcctConfig) + // check that it was parsed successfully + convertedFcct, err := ParseAndConvertConfig(rawFcct) + require.Nil(t, err) + testIgn3Config.Ignition.Version = "3.1.0" + assert.Equal(t, testIgn3Config, convertedFcct) } func TestMergeMachineConfigs(t *testing.T) { diff --git a/test/helpers/helpers.go b/test/helpers/helpers.go index 4975a16a17..faa8fc90b8 100644 --- a/test/helpers/helpers.go +++ b/test/helpers/helpers.go @@ -5,6 +5,7 @@ import ( "github.com/clarketm/json" ign3types "github.com/coreos/ignition/v2/config/v3_1/types" + "gopkg.in/yaml.v2" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -128,3 +129,12 @@ func MarshalOrDie(input interface{}) []byte { } return bytes } + +// YamlMarshalOrDie returns a marshalled interface or panics +func YamlMarshalOrDie(input interface{}) []byte { + bytes, err := yaml.Marshal(input) + if err != nil { + panic(err) + } + return bytes +} diff --git a/vendor/github.com/coreos/fcct/base/options.go b/vendor/github.com/coreos/fcct/base/options.go new file mode 100644 index 0000000000..453cc34432 --- /dev/null +++ b/vendor/github.com/coreos/fcct/base/options.go @@ -0,0 +1,20 @@ +// 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 base + +type TranslateOptions struct { + FilesDir string // allow embedding local files relative to this directory + NoResourceAutoCompression bool // skip automatic compression of inline/local resources +} diff --git a/vendor/github.com/coreos/fcct/base/v0_1/translate.go b/vendor/github.com/coreos/fcct/base/v0_1/translate.go deleted file mode 100644 index dba8c7aee5..0000000000 --- a/vendor/github.com/coreos/fcct/base/v0_1/translate.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2019 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 v0_1 - -import ( - "net/url" - - "github.com/coreos/fcct/translate" - - "github.com/coreos/ignition/v2/config/v3_0/types" - "github.com/coreos/vcontext/path" - "github.com/vincent-petithory/dataurl" -) - -// ToIgn3_0 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. -func (c Config) ToIgn3_0() (types.Config, translate.TranslationSet, error) { - ret := types.Config{} - tr := translate.NewTranslator("yaml", "json") - tr.AddCustomTranslator(translateIgnition) - tr.AddCustomTranslator(translateFile) - tr.AddCustomTranslator(translateDirectory) - tr.AddCustomTranslator(translateLink) - translations := tr.Translate(&c, &ret) - return ret, translations, nil -} - -func translateIgnition(from Ignition) (to types.Ignition, tm translate.TranslationSet) { - tr := translate.NewTranslator("yaml", "json") - to.Version = types.MaxVersion.String() - tm = tr.Translate(&from.Config, &to.Config).Prefix("config") - tm.MergeP("security", tr.Translate(&from.Security, &to.Security)) - tm.MergeP("timeouts", tr.Translate(&from.Timeouts, &to.Timeouts)) - return -} - -func translateFile(from File) (to types.File, tm translate.TranslationSet) { - tr := translate.NewTranslator("yaml", "json") - tr.AddCustomTranslator(translateFileContents) - tm = tr.Translate(&from.Group, &to.Group).Prefix("group") - tm.MergeP("user", tr.Translate(&from.User, &to.User)) - tm.MergeP("append", tr.Translate(&from.Append, &to.Append)) - tm.MergeP("contents", tr.Translate(&from.Contents, &to.Contents)) - to.Overwrite = from.Overwrite - to.Path = from.Path - to.Mode = from.Mode - tm.AddIdentity("overwrite", "path", "mode") - return -} - -func translateFileContents(from FileContents) (to types.FileContents, tm translate.TranslationSet) { - tr := translate.NewTranslator("yaml", "json") - tm = tr.Translate(&from.Verification, &to.Verification).Prefix("verification") - to.Source = from.Source - to.Compression = from.Compression - tm.AddIdentity("source", "compression") - if from.Inline != nil { - src := (&url.URL{ - Scheme: "data", - Opaque: "," + dataurl.EscapeString(*from.Inline), - }).String() - to.Source = &src - tm.AddTranslation(path.New("yaml", "inline"), path.New("json", "source")) - } - return -} - -func translateDirectory(from Directory) (to types.Directory, tm translate.TranslationSet) { - tr := translate.NewTranslator("yaml", "json") - tm = tr.Translate(&from.Group, &to.Group).Prefix("group") - tm.MergeP("user", tr.Translate(&from.User, &to.User)) - to.Overwrite = from.Overwrite - to.Path = from.Path - to.Mode = from.Mode - tm.AddIdentity("overwrite", "path", "mode") - return -} - -func translateLink(from Link) (to types.Link, tm translate.TranslationSet) { - tr := translate.NewTranslator("yaml", "json") - tm = tr.Translate(&from.Group, &to.Group).Prefix("group") - tm.MergeP("user", tr.Translate(&from.User, &to.User)) - to.Target = from.Target - to.Hard = from.Hard - to.Overwrite = from.Overwrite - to.Path = from.Path - tm.AddIdentity("target", "hard", "overwrite", "path") - return -} diff --git a/vendor/github.com/coreos/fcct/base/v0_1/schema.go b/vendor/github.com/coreos/fcct/base/v0_2/schema.go similarity index 76% rename from vendor/github.com/coreos/fcct/base/v0_1/schema.go rename to vendor/github.com/coreos/fcct/base/v0_2/schema.go index c1a70bcb98..b5f747f182 100644 --- a/vendor/github.com/coreos/fcct/base/v0_1/schema.go +++ b/vendor/github.com/coreos/fcct/base/v0_2/schema.go @@ -12,12 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v0_1 - -type CaReference struct { - Source string `yaml:"source"` - Verification Verification `yaml:"verification"` -} +package v0_2 type Config struct { Ignition Ignition `yaml:"ignition"` @@ -26,11 +21,6 @@ type Config struct { Systemd Systemd `yaml:"systemd"` } -type ConfigReference struct { - Source *string `yaml:"source"` - Verification Verification `yaml:"verification"` -} - type Device string type Directory struct { @@ -53,45 +43,48 @@ type Dropin struct { } type File struct { - Group NodeGroup `yaml:"group"` - Overwrite *bool `yaml:"overwrite"` - Path string `yaml:"path"` - User NodeUser `yaml:"user"` - Append []FileContents `yaml:"append"` - Contents FileContents `yaml:"contents"` - Mode *int `yaml:"mode"` -} - -type FileContents struct { - Compression *string `yaml:"compression"` - Source *string `yaml:"source"` - Inline *string `yaml:"inline"` // Added, not in ignition spec - Verification Verification `yaml:"verification"` + Group NodeGroup `yaml:"group"` + Overwrite *bool `yaml:"overwrite"` + Path string `yaml:"path"` + User NodeUser `yaml:"user"` + Append []Resource `yaml:"append"` + Contents Resource `yaml:"contents"` + Mode *int `yaml:"mode"` } type Filesystem struct { - Device string `yaml:"device"` - Format *string `yaml:"format"` - Label *string `yaml:"label"` - Options []FilesystemOption `yaml:"options"` - Path *string `yaml:"path"` - UUID *string `yaml:"uuid"` - WipeFilesystem *bool `yaml:"wipe_filesystem"` + Device string `yaml:"device"` + Format *string `yaml:"format"` + Label *string `yaml:"label"` + MountOptions []string `yaml:"mount_options"` + Options []string `yaml:"options"` + Path *string `yaml:"path"` + UUID *string `yaml:"uuid"` + WipeFilesystem *bool `yaml:"wipe_filesystem"` + WithMountUnit *bool `yaml:"with_mount_unit" fcct:"auto_skip"` // Added, not in Ignition spec } type FilesystemOption string type Group string +type HTTPHeader struct { + Name string `yaml:"name"` + Value *string `yaml:"value"` +} + +type HTTPHeaders []HTTPHeader + type Ignition struct { Config IgnitionConfig `yaml:"config"` + Proxy Proxy `yaml:"proxy"` Security Security `yaml:"security"` Timeouts Timeouts `yaml:"timeouts"` } type IgnitionConfig struct { - Merge []ConfigReference `yaml:"merge"` - Replace ConfigReference `yaml:"replace"` + Merge []Resource `yaml:"merge"` + Replace Resource `yaml:"replace"` } type Link struct { @@ -152,6 +145,12 @@ type PasswdUser struct { UID *int `yaml:"uid"` } +type Proxy struct { + HTTPProxy *string `yaml:"http_proxy"` + HTTPSProxy *string `yaml:"https_proxy"` + NoProxy []string `yaml:"no_proxy"` +} + type Raid struct { Devices []Device `yaml:"devices"` Level string `yaml:"level"` @@ -162,6 +161,15 @@ type Raid struct { type RaidOption string +type Resource struct { + Compression *string `yaml:"compression"` + HTTPHeaders HTTPHeaders `yaml:"http_headers"` + Source *string `yaml:"source"` + Inline *string `yaml:"inline"` // Added, not in ignition spec + Local *string `yaml:"local"` // Added, not in ignition spec + Verification Verification `yaml:"verification"` +} + type SSHAuthorizedKey string type Security struct { @@ -175,6 +183,7 @@ type Storage struct { Filesystems []Filesystem `yaml:"filesystems"` Links []Link `yaml:"links"` Raid []Raid `yaml:"raid"` + Trees []Tree `yaml:"trees" fcct:"auto_skip"` // Added, not in ignition spec } type Systemd struct { @@ -182,7 +191,7 @@ type Systemd struct { } type TLS struct { - CertificateAuthorities []CaReference `yaml:"certificate_authorities"` + CertificateAuthorities []Resource `yaml:"certificate_authorities"` } type Timeouts struct { @@ -190,6 +199,11 @@ type Timeouts struct { HTTPTotal *int `yaml:"http_total"` } +type Tree struct { + Local string `yaml:"local"` + Path *string `yaml:"path"` +} + type Unit struct { Contents *string `yaml:"contents"` Dropins []Dropin `yaml:"dropins"` diff --git a/vendor/github.com/coreos/fcct/base/v0_2/translate.go b/vendor/github.com/coreos/fcct/base/v0_2/translate.go new file mode 100644 index 0000000000..0959f2e16e --- /dev/null +++ b/vendor/github.com/coreos/fcct/base/v0_2/translate.go @@ -0,0 +1,441 @@ +// Copyright 2019 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 v0_2 + +import ( + "bytes" + "compress/gzip" + "encoding/base64" + "errors" + "io/ioutil" + "net/url" + "os" + "path/filepath" + "strings" + "text/template" + + "github.com/coreos/fcct/base" + "github.com/coreos/fcct/translate" + + "github.com/coreos/go-systemd/unit" + "github.com/coreos/ignition/v2/config/util" + "github.com/coreos/ignition/v2/config/v3_1/types" + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" + "github.com/vincent-petithory/dataurl" +) + +var ( + ErrFilesDirEscape = errors.New("local file path traverses outside the files directory") + ErrFileType = errors.New("trees may only contain files, directories, and symlinks") + ErrNodeExists = errors.New("matching filesystem node has existing contents or different type") + ErrNoFilesDir = errors.New("local file paths are relative to a files directory that must be specified with -d/--files-dir") + ErrTreeNotDirectory = errors.New("root of tree must be a directory") + + mountUnitTemplate = template.Must(template.New("unit").Parse(`# Generated by FCCT +[Unit] +Before=local-fs.target +Requires=systemd-fsck@{{.Device}} +After=systemd-fsck@{{.Device}} + +[Mount] +Where={{.Path}} +What={{.Device}} +Type={{.Format}} +{{- if .MountOptions }} +Options= + {{- range $i, $opt := .MountOptions }} + {{- if $i }},{{ end }} + {{- $opt }} + {{- end }} +{{- end }} + +[Install] +RequiredBy=local-fs.target`)) +) + +// ToIgn3_1 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. +func (c Config) ToIgn3_1(options base.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) { + ret := types.Config{} + tr := translate.NewTranslator("yaml", "json", options) + tr.AddCustomTranslator(translateIgnition) + tr.AddCustomTranslator(translateFile) + tr.AddCustomTranslator(translateDirectory) + tr.AddCustomTranslator(translateLink) + translations, report := tr.Translate(&c, &ret) + translations.Merge(c.addMountUnits(&ret)) + + ts, r := c.processTrees(&ret, options) + translations.Merge(ts) + report.Merge(r) + + return ret, translations, report +} + +func translateIgnition(from Ignition, options base.TranslateOptions) (to types.Ignition, tm translate.TranslationSet, r report.Report) { + tr := translate.NewTranslator("yaml", "json", options) + tr.AddCustomTranslator(translateResource) + to.Version = types.MaxVersion.String() + tm, r = translate.Prefixed(tr, "config", &from.Config, &to.Config) + translate.MergeP(tr, tm, &r, "proxy", &from.Proxy, &to.Proxy) + translate.MergeP(tr, tm, &r, "security", &from.Security, &to.Security) + translate.MergeP(tr, tm, &r, "timeouts", &from.Timeouts, &to.Timeouts) + return +} + +func translateFile(from File, options base.TranslateOptions) (to types.File, tm translate.TranslationSet, r report.Report) { + tr := translate.NewTranslator("yaml", "json", options) + tr.AddCustomTranslator(translateResource) + tm, r = translate.Prefixed(tr, "group", &from.Group, &to.Group) + translate.MergeP(tr, tm, &r, "user", &from.User, &to.User) + translate.MergeP(tr, tm, &r, "append", &from.Append, &to.Append) + translate.MergeP(tr, tm, &r, "contents", &from.Contents, &to.Contents) + to.Overwrite = from.Overwrite + to.Path = from.Path + to.Mode = from.Mode + tm.AddIdentity("overwrite", "path", "mode") + return +} + +func translateResource(from Resource, options base.TranslateOptions) (to types.Resource, tm translate.TranslationSet, r report.Report) { + tr := translate.NewTranslator("yaml", "json", options) + tm, r = translate.Prefixed(tr, "verification", &from.Verification, &to.Verification) + translate.MergeP(tr, tm, &r, "httpHeaders", &from.HTTPHeaders, &to.HTTPHeaders) + to.Source = from.Source + to.Compression = from.Compression + tm.AddIdentity("source", "compression") + + if from.Local != nil { + c := path.New("yaml", "local") + + if options.FilesDir == "" { + r.AddOnError(c, ErrNoFilesDir) + return + } + + // calculate file path within FilesDir and check for + // path traversal + filePath := filepath.Join(options.FilesDir, *from.Local) + if err := ensurePathWithinFilesDir(filePath, options.FilesDir); err != nil { + r.AddOnError(c, err) + return + } + + contents, err := ioutil.ReadFile(filePath) + if err != nil { + r.AddOnError(c, err) + return + } + + src, gzipped, err := makeDataURL(contents, to.Compression, options.NoResourceAutoCompression) + if err != nil { + r.AddOnError(c, err) + return + } + to.Source = &src + tm.AddTranslation(c, path.New("json", "source")) + if gzipped { + to.Compression = util.StrToPtr("gzip") + tm.AddTranslation(c, path.New("json", "compression")) + } + } + + if from.Inline != nil { + c := path.New("yaml", "inline") + + src, gzipped, err := makeDataURL([]byte(*from.Inline), to.Compression, options.NoResourceAutoCompression) + if err != nil { + r.AddOnError(c, err) + return + } + to.Source = &src + tm.AddTranslation(c, path.New("json", "source")) + if gzipped { + to.Compression = util.StrToPtr("gzip") + tm.AddTranslation(c, path.New("json", "compression")) + } + } + return +} + +func makeDataURL(contents []byte, currentCompression *string, noResourceAutoCompression bool) (uri string, gzipped bool, err error) { + // try three different encodings, and select the smallest one + + // URL-escaped, useful for ASCII text + opaque := "," + dataurl.Escape(contents) + + // Base64-encoded, useful for small or incompressible binary data + b64 := ";base64," + base64.StdEncoding.EncodeToString(contents) + if len(b64) < len(opaque) { + opaque = b64 + } + + // Base64-encoded gzipped, useful for compressible data. If the + // user already enabled compression, don't compress again. + // We don't try base64-encoded URL-escaped because gzipped data is + // binary and URL escaping is unlikely to be efficient. + if (currentCompression == nil || *currentCompression == "") && !noResourceAutoCompression { + var buf bytes.Buffer + var compressor *gzip.Writer + if compressor, err = gzip.NewWriterLevel(&buf, gzip.BestCompression); err != nil { + return + } + if _, err = compressor.Write(contents); err != nil { + return + } + if err = compressor.Close(); err != nil { + return + } + gz := ";base64," + base64.StdEncoding.EncodeToString(buf.Bytes()) + // Account for space needed by "compression": "gzip". + if len(gz)+25 < len(opaque) { + opaque = gz + gzipped = true + } + } + + uri = (&url.URL{ + Scheme: "data", + Opaque: opaque, + }).String() + return +} + +func translateDirectory(from Directory, options base.TranslateOptions) (to types.Directory, tm translate.TranslationSet, r report.Report) { + tr := translate.NewTranslator("yaml", "json", options) + tm, r = translate.Prefixed(tr, "group", &from.Group, &to.Group) + translate.MergeP(tr, tm, &r, "user", &from.User, &to.User) + to.Overwrite = from.Overwrite + to.Path = from.Path + to.Mode = from.Mode + tm.AddIdentity("overwrite", "path", "mode") + return +} + +func translateLink(from Link, options base.TranslateOptions) (to types.Link, tm translate.TranslationSet, r report.Report) { + tr := translate.NewTranslator("yaml", "json", options) + tm, r = translate.Prefixed(tr, "group", &from.Group, &to.Group) + translate.MergeP(tr, tm, &r, "user", &from.User, &to.User) + to.Target = from.Target + to.Hard = from.Hard + to.Overwrite = from.Overwrite + to.Path = from.Path + tm.AddIdentity("target", "hard", "overwrite", "path") + return +} + +func (c Config) processTrees(ret *types.Config, options base.TranslateOptions) (translate.TranslationSet, report.Report) { + ts := translate.NewTranslationSet("yaml", "json") + var r report.Report + if len(c.Storage.Trees) == 0 { + return ts, r + } + t := newNodeTracker(ret) + + for i, tree := range c.Storage.Trees { + yamlPath := path.New("yaml", "storage", "trees", i) + if options.FilesDir == "" { + r.AddOnError(yamlPath, ErrNoFilesDir) + return ts, r + } + + // calculate base path within FilesDir and check for + // path traversal + srcBaseDir := filepath.Join(options.FilesDir, tree.Local) + if err := ensurePathWithinFilesDir(srcBaseDir, options.FilesDir); err != nil { + r.AddOnError(yamlPath, err) + continue + } + info, err := os.Stat(srcBaseDir) + if err != nil { + r.AddOnError(yamlPath, err) + continue + } + if !info.IsDir() { + r.AddOnError(yamlPath, ErrTreeNotDirectory) + continue + } + destBaseDir := "/" + if tree.Path != nil && *tree.Path != "" { + destBaseDir = *tree.Path + } + + walkTree(yamlPath, tree, &ts, &r, t, srcBaseDir, destBaseDir, options) + } + return ts, r +} + +func walkTree(yamlPath path.ContextPath, tree Tree, ts *translate.TranslationSet, r *report.Report, t *nodeTracker, srcBaseDir, destBaseDir string, options base.TranslateOptions) { + // The strategy for errors within WalkFunc is to add an error to + // the report and return nil, so walking continues but translation + // will fail afterward. + err := filepath.Walk(srcBaseDir, func(srcPath string, info os.FileInfo, err error) error { + if err != nil { + r.AddOnError(yamlPath, err) + return nil + } + relPath, err := filepath.Rel(srcBaseDir, srcPath) + if err != nil { + r.AddOnError(yamlPath, err) + return nil + } + destPath := filepath.Join(destBaseDir, relPath) + + if info.Mode().IsDir() { + return nil + } else if info.Mode().IsRegular() { + i, file := t.GetFile(destPath) + if file != nil { + if file.Contents.Source != nil && *file.Contents.Source != "" { + r.AddOnError(yamlPath, ErrNodeExists) + return nil + } + } else { + if t.Exists(destPath) { + r.AddOnError(yamlPath, ErrNodeExists) + return nil + } + i, file = t.AddFile(types.File{ + Node: types.Node{ + Path: destPath, + }, + }) + ts.AddFromCommonSource(yamlPath, path.New("json", "storage", "files", i), file) + } + contents, err := ioutil.ReadFile(srcPath) + if err != nil { + r.AddOnError(yamlPath, err) + return nil + } + url, gzipped, err := makeDataURL(contents, file.Contents.Compression, options.NoResourceAutoCompression) + if err != nil { + r.AddOnError(yamlPath, err) + return nil + } + file.Contents.Source = util.StrToPtr(url) + ts.AddTranslation(yamlPath, path.New("json", "storage", "files", i, "contents", "source")) + if gzipped { + file.Contents.Compression = util.StrToPtr("gzip") + ts.AddTranslation(yamlPath, path.New("json", "storage", "files", i, "contents", "compression")) + } + if file.Mode == nil { + mode := 0644 + if info.Mode()&0111 != 0 { + mode = 0755 + } + file.Mode = &mode + ts.AddTranslation(yamlPath, path.New("json", "storage", "files", i, "mode")) + } + } else if info.Mode()&os.ModeType == os.ModeSymlink { + i, link := t.GetLink(destPath) + if link != nil { + if link.Target != "" { + r.AddOnError(yamlPath, ErrNodeExists) + return nil + } + } else { + if t.Exists(destPath) { + r.AddOnError(yamlPath, ErrNodeExists) + return nil + } + i, link = t.AddLink(types.Link{ + Node: types.Node{ + Path: destPath, + }, + }) + ts.AddFromCommonSource(yamlPath, path.New("json", "storage", "links", i), link) + } + link.Target, err = os.Readlink(srcPath) + if err != nil { + r.AddOnError(yamlPath, err) + return nil + } + ts.AddTranslation(yamlPath, path.New("json", "storage", "links", i, "target")) + } else { + r.AddOnError(yamlPath, ErrFileType) + return nil + } + return nil + }) + r.AddOnError(yamlPath, err) +} + +func (c Config) addMountUnits(ret *types.Config) translate.TranslationSet { + ts := translate.NewTranslationSet("yaml", "json") + if len(c.Storage.Filesystems) == 0 { + return ts + } + unitMap := make(map[string]int, len(ret.Systemd.Units)) + for i, u := range ret.Systemd.Units { + unitMap[u.Name] = i + } + for i, fs := range c.Storage.Filesystems { + if fs.WithMountUnit == nil || !*fs.WithMountUnit { + continue + } + fromPath := path.New("yaml", "storage", "filesystems", i, "with_mount_unit") + newUnit := mountUnitFromFS(fs) + if i, ok := unitMap[unit.UnitNamePathEscape(*fs.Path)+".mount"]; ok { + // user also specified a unit, only set contents and enabled if the existing unit + // is unspecified + u := &ret.Systemd.Units[i] + unitPath := path.New("json", "systemd", "units", i) + if u.Contents == nil { + (*u).Contents = newUnit.Contents + ts.AddTranslation(fromPath, unitPath.Append("contents")) + } + if u.Enabled == nil { + (*u).Enabled = newUnit.Enabled + ts.AddTranslation(fromPath, unitPath.Append("enabled")) + } + } else { + unitPath := path.New("json", "systemd", "units", len(ret.Systemd.Units)) + ret.Systemd.Units = append(ret.Systemd.Units, newUnit) + ts.AddFromCommonSource(fromPath, unitPath, newUnit) + } + } + return ts +} + +func mountUnitFromFS(fs Filesystem) types.Unit { + contents := strings.Builder{} + err := mountUnitTemplate.Execute(&contents, fs) + if err != nil { + panic(err) + } + // unchecked deref of path ok, fs would fail validation otherwise + unitName := unit.UnitNamePathEscape(*fs.Path) + ".mount" + return types.Unit{ + Name: unitName, + Enabled: util.BoolToPtr(true), + Contents: util.StrToPtr(contents.String()), + } +} + +func ensurePathWithinFilesDir(path, filesDir string) error { + absBase, err := filepath.Abs(filesDir) + if err != nil { + return err + } + absPath, err := filepath.Abs(path) + if err != nil { + return err + } + if !strings.HasPrefix(absPath, absBase+string(filepath.Separator)) { + return ErrFilesDirEscape + } + return nil +} diff --git a/vendor/github.com/coreos/fcct/base/v0_2/util.go b/vendor/github.com/coreos/fcct/base/v0_2/util.go new file mode 100644 index 0000000000..4478255305 --- /dev/null +++ b/vendor/github.com/coreos/fcct/base/v0_2/util.go @@ -0,0 +1,125 @@ +// 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 v0_2 + +import ( + "github.com/coreos/ignition/v2/config/v3_1/types" +) + +type nodeTracker struct { + files *[]types.File + fileMap map[string]int + + dirs *[]types.Directory + dirMap map[string]int + + links *[]types.Link + linkMap map[string]int +} + +func newNodeTracker(c *types.Config) *nodeTracker { + t := nodeTracker{ + files: &c.Storage.Files, + fileMap: make(map[string]int, len(c.Storage.Files)), + + dirs: &c.Storage.Directories, + dirMap: make(map[string]int, len(c.Storage.Directories)), + + links: &c.Storage.Links, + linkMap: make(map[string]int, len(c.Storage.Links)), + } + for i, n := range *t.files { + t.fileMap[n.Path] = i + } + for i, n := range *t.dirs { + t.dirMap[n.Path] = i + } + for i, n := range *t.links { + t.linkMap[n.Path] = i + } + return &t +} + +func (t *nodeTracker) Exists(path string) bool { + for _, m := range []map[string]int{t.fileMap, t.dirMap, t.linkMap} { + if _, ok := m[path]; ok { + return true + } + } + return false +} + +func (t *nodeTracker) GetFile(path string) (int, *types.File) { + if i, ok := t.fileMap[path]; ok { + return i, &(*t.files)[i] + } else { + return 0, nil + } +} + +func (t *nodeTracker) AddFile(f types.File) (int, *types.File) { + if f.Path == "" { + panic("File path missing") + } + if _, ok := t.fileMap[f.Path]; ok { + panic("Adding already existing file") + } + i := len(*t.files) + *t.files = append(*t.files, f) + t.fileMap[f.Path] = i + return i, &(*t.files)[i] +} + +func (t *nodeTracker) GetDir(path string) (int, *types.Directory) { + if i, ok := t.dirMap[path]; ok { + return i, &(*t.dirs)[i] + } else { + return 0, nil + } +} + +func (t *nodeTracker) AddDir(d types.Directory) (int, *types.Directory) { + if d.Path == "" { + panic("Directory path missing") + } + if _, ok := t.dirMap[d.Path]; ok { + panic("Adding already existing directory") + } + i := len(*t.dirs) + *t.dirs = append(*t.dirs, d) + t.dirMap[d.Path] = i + return i, &(*t.dirs)[i] +} + +func (t *nodeTracker) GetLink(path string) (int, *types.Link) { + if i, ok := t.linkMap[path]; ok { + return i, &(*t.links)[i] + } else { + return 0, nil + } +} + +func (t *nodeTracker) AddLink(l types.Link) (int, *types.Link) { + if l.Path == "" { + panic("Link path missing") + } + if _, ok := t.linkMap[l.Path]; ok { + panic("Adding already existing link") + } + i := len(*t.links) + *t.links = append(*t.links, l) + t.linkMap[l.Path] = i + return i, &(*t.links)[i] +} diff --git a/vendor/github.com/coreos/fcct/base/v0_2/validate.go b/vendor/github.com/coreos/fcct/base/v0_2/validate.go new file mode 100644 index 0000000000..0c7d3cfa96 --- /dev/null +++ b/vendor/github.com/coreos/fcct/base/v0_2/validate.go @@ -0,0 +1,70 @@ +// Copyright 2019 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 v0_2 + +import ( + "errors" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +var ( + ErrTooManyResourceSources = errors.New("only one of the following can be set: inline, local, source") + ErrMountUnitNoPath = errors.New("path is required if with_mount_unit is true") + ErrMountUnitNoFormat = errors.New("format is required if with_mount_unit is true") + ErrTreeNoLocal = errors.New("local is required") +) + +func (rs Resource) Validate(c path.ContextPath) (r report.Report) { + var field string + sources := 0 + if rs.Local != nil { + sources++ + field = "local" + } + if rs.Inline != nil { + sources++ + field = "inline" + } + if rs.Source != nil { + sources++ + field = "source" + } + if sources > 1 { + r.AddOnError(c.Append(field), ErrTooManyResourceSources) + } + return +} + +func (fs Filesystem) Validate(c path.ContextPath) (r report.Report) { + if fs.WithMountUnit == nil || !*fs.WithMountUnit { + return + } + if fs.Path == nil || *fs.Path == "" { + r.AddOnError(c.Append("path"), ErrMountUnitNoPath) + } + if fs.Format == nil || *fs.Format == "" { + r.AddOnError(c.Append("format"), ErrMountUnitNoFormat) + } + return +} + +func (t Tree) Validate(c path.ContextPath) (r report.Report) { + if t.Local == "" { + r.AddOnError(c, ErrTreeNoLocal) + } + return +} diff --git a/vendor/github.com/coreos/fcct/config/common/common.go b/vendor/github.com/coreos/fcct/config/common/common.go new file mode 100644 index 0000000000..ca7f8a5543 --- /dev/null +++ b/vendor/github.com/coreos/fcct/config/common/common.go @@ -0,0 +1,101 @@ +// Copyright 2019 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 common + +import ( + "bytes" + "regexp" + "strings" + + "github.com/coreos/fcct/base" + "github.com/coreos/fcct/translate" + + "github.com/clarketm/json" + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" + "github.com/coreos/vcontext/tree" + vyaml "github.com/coreos/vcontext/yaml" + "gopkg.in/yaml.v3" +) + +var ( + snakeRe = regexp.MustCompile("([A-Z])") +) + +type BaseOptions = base.TranslateOptions + +type TranslateOptions struct { + Pretty bool + Strict bool + BaseOptions +} + +type Common struct { + Version string `yaml:"version"` + Variant string `yaml:"variant"` +} + +// Misc helpers + +// 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) { + dec := yaml.NewDecoder(bytes.NewReader(data)) + dec.KnownFields(strict) + if err := dec.Decode(to); err != nil { + return nil, err + } + return vyaml.UnmarshalToContext(data) +} + +// Marshal is a wrapper for marshaling to json with or without pretty-printing the output +func Marshal(from interface{}, pretty bool) ([]byte, error) { + if pretty { + return json.MarshalIndent(from, "", " ") + } + return json.Marshal(from) +} + +// snakePath converts a path.ContextPath with camelCase elements and returns the +// same path but with snake_case elements instead +func snakePath(p path.ContextPath) path.ContextPath { + ret := path.New(p.Tag) + for _, part := range p.Path { + if str, ok := part.(string); ok { + ret = ret.Append(snake(str)) + } else { + ret = ret.Append(part) + } + } + return ret +} + +// snake converts from camelCase (not CamelCase) to snake_case +func snake(in string) string { + return strings.ToLower(snakeRe.ReplaceAllString(in, "_$1")) +} + +// TranslateReportPaths takes a report from a camelCase json document and a set of translations rules, +// applies those rules and converts all camelCase to snake_case. +func TranslateReportPaths(r *report.Report, ts translate.TranslationSet) { + for i, ent := range r.Entries { + context := ent.Context + if t, ok := ts.Set[context.String()]; ok { + context = t.From + } + context = snakePath(context) + r.Entries[i].Context = context + } +} diff --git a/vendor/github.com/coreos/fcct/config/common/errors.go b/vendor/github.com/coreos/fcct/config/common/errors.go new file mode 100644 index 0000000000..4bb669aaa3 --- /dev/null +++ b/vendor/github.com/coreos/fcct/config/common/errors.go @@ -0,0 +1,24 @@ +// Copyright 2019 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 common + +import ( + "errors" +) + +var ( + ErrInvalidSourceConfig = errors.New("source config is invalid") + ErrInvalidGeneratedConfig = errors.New("config generated was invalid") +) diff --git a/vendor/github.com/coreos/fcct/config/v1_1/fcos.go b/vendor/github.com/coreos/fcct/config/v1_1/fcos.go new file mode 100644 index 0000000000..b28a6e3966 --- /dev/null +++ b/vendor/github.com/coreos/fcct/config/v1_1/fcos.go @@ -0,0 +1,101 @@ +// Copyright 2019 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 v1_1 + +import ( + "reflect" + + base_0_2 "github.com/coreos/fcct/base/v0_2" + "github.com/coreos/fcct/config/common" + fcos_0_1 "github.com/coreos/fcct/distro/fcos/v0_1" + "github.com/coreos/fcct/translate" + + "github.com/coreos/ignition/v2/config/v3_1/types" + ignvalidate "github.com/coreos/ignition/v2/config/validate" + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" + "github.com/coreos/vcontext/validate" +) + +type Config struct { + common.Common `yaml:",inline"` + base_0_2.Config `yaml:",inline"` + fcos_0_1.Fcos `yaml:",inline"` +} + +func (c Config) Translate(options common.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) { + cfg, baseTranslations, baseReport := c.Config.ToIgn3_1(options.BaseOptions) + if baseReport.IsFatal() { + return types.Config{}, translate.TranslationSet{}, baseReport + } + + finalcfg, distroTranslations, distroReport := c.Fcos.ToIgn3_1(cfg, options.BaseOptions) + baseReport.Merge(distroReport) + if baseReport.IsFatal() { + return types.Config{}, translate.TranslationSet{}, baseReport + } + + baseTranslations.Merge(distroTranslations) + + return finalcfg, baseTranslations, baseReport +} + +// TranslateBytes translates from a v1.1 fcc to a v3.1.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 TranslateBytes(input []byte, options common.TranslateOptions) ([]byte, report.Report, error) { + cfg := Config{} + + contextTree, err := common.Unmarshal(input, &cfg, options.Strict) + if err != nil { + return nil, report.Report{}, err + } + + r := validate.Validate(cfg, "yaml") + unusedKeyCheck := func(v reflect.Value, c path.ContextPath) report.Report { + return ignvalidate.ValidateUnusedKeys(v, c, contextTree) + } + r.Merge(validate.ValidateCustom(cfg, "yaml", unusedKeyCheck)) + r.Correlate(contextTree) + if r.IsFatal() { + return nil, r, common.ErrInvalidSourceConfig + } + + final, translations, translateReport := cfg.Translate(options) + translateReport.Correlate(contextTree) + r.Merge(translateReport) + if r.IsFatal() { + return nil, r, common.ErrInvalidSourceConfig + } + + // Check for invalid duplicated keys. + dupsReport := validate.ValidateCustom(final, "json", ignvalidate.ValidateDups) + common.TranslateReportPaths(&dupsReport, translations) + dupsReport.Correlate(contextTree) + r.Merge(dupsReport) + + // Validate JSON semantics. + jsonReport := validate.Validate(final, "json") + common.TranslateReportPaths(&jsonReport, translations) + jsonReport.Correlate(contextTree) + r.Merge(jsonReport) + + if r.IsFatal() { + return nil, r, common.ErrInvalidGeneratedConfig + } + + outbytes, err := common.Marshal(final, options.Pretty) + return outbytes, r, err +} diff --git a/vendor/github.com/coreos/fcct/distro/fcos/v0_1/schema.go b/vendor/github.com/coreos/fcct/distro/fcos/v0_1/schema.go new file mode 100644 index 0000000000..ed0e0946dc --- /dev/null +++ b/vendor/github.com/coreos/fcct/distro/fcos/v0_1/schema.go @@ -0,0 +1,18 @@ +// Copyright 2019 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 fcos_0_1 + +type Fcos struct { +} diff --git a/vendor/github.com/coreos/fcct/distro/fcos/v0_1/translate.go b/vendor/github.com/coreos/fcct/distro/fcos/v0_1/translate.go new file mode 100644 index 0000000000..51dfe76f40 --- /dev/null +++ b/vendor/github.com/coreos/fcct/distro/fcos/v0_1/translate.go @@ -0,0 +1,43 @@ +// Copyright 2019 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 fcos_0_1 + +import ( + "github.com/coreos/fcct/base" + "github.com/coreos/fcct/translate" + + types3_0 "github.com/coreos/ignition/v2/config/v3_0/types" + types3_1 "github.com/coreos/ignition/v2/config/v3_1/types" + types3_2 "github.com/coreos/ignition/v2/config/v3_2/types" + types3_3 "github.com/coreos/ignition/v2/config/v3_3_experimental/types" + "github.com/coreos/vcontext/report" +) + +// ToIgn3_0 takes a config and merges in the distro specific bits. +func (f Fcos) ToIgn3_0(in types3_0.Config, options base.TranslateOptions) (types3_0.Config, translate.TranslationSet, report.Report) { + return in, translate.TranslationSet{}, report.Report{} +} + +func (f Fcos) ToIgn3_1(in types3_1.Config, options base.TranslateOptions) (types3_1.Config, translate.TranslationSet, report.Report) { + return in, translate.TranslationSet{}, report.Report{} +} + +func (f Fcos) ToIgn3_2(in types3_2.Config, options base.TranslateOptions) (types3_2.Config, translate.TranslationSet, report.Report) { + return in, translate.TranslationSet{}, report.Report{} +} + +func (f Fcos) ToIgn3_3(in types3_3.Config, options base.TranslateOptions) (types3_3.Config, translate.TranslationSet, report.Report) { + return in, translate.TranslationSet{}, report.Report{} +} diff --git a/vendor/github.com/coreos/fcct/translate/set.go b/vendor/github.com/coreos/fcct/translate/set.go index 6140fbf1b4..aec086973d 100644 --- a/vendor/github.com/coreos/fcct/translate/set.go +++ b/vendor/github.com/coreos/fcct/translate/set.go @@ -107,7 +107,7 @@ func (ts TranslationSet) Prefix(prefix interface{}) TranslationSet { from := path.New(ts.FromTag, prefix) to := path.New(ts.ToTag, prefix) for _, tr := range ts.Set { - ret.AddTranslation(from.Append(tr.From.Path...), to.Append(tr.From.Path...)) + ret.AddTranslation(from.Append(tr.From.Path...), to.Append(tr.To.Path...)) } return ret } diff --git a/vendor/github.com/coreos/fcct/translate/translate.go b/vendor/github.com/coreos/fcct/translate/translate.go index 3cbfbf4594..ee5c5df4ba 100644 --- a/vendor/github.com/coreos/fcct/translate/translate.go +++ b/vendor/github.com/coreos/fcct/translate/translate.go @@ -20,6 +20,7 @@ import ( "github.com/coreos/ignition/v2/config/util" "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" ) /* @@ -28,14 +29,20 @@ import ( * call NewTranslator() to get a translator instance. This can then have * additional translation rules (in the form of functions) to translate from * types in one struct to the other. Those functions are in the form: - * func(typeFromInputStruct) -> typeFromOutputStruct + * func(fromType, optionsType) -> (toType, TranslationSet, report.Report) * These can be closures that reference the translator as well. This allows for * manually translating some fields but resuming automatic translation on the * other fields through the Translator.Translate() function. */ +const ( + TAG_KEY = "fcct" + TAG_AUTO_SKIP = "auto_skip" +) + var ( translationsType = reflect.TypeOf(TranslationSet{}) + reportType = reflect.TypeOf(report.Report{}) ) // Returns if this type can be translated without a custom translator. Children or other @@ -62,13 +69,19 @@ func (t translator) translatable(t1, t2 reflect.Type) bool { // precondition: t1, t2 are both of Kind 'struct' func (t translator) translatableStruct(t1, t2 reflect.Type) bool { - if t1.NumField() != t2.NumField() || t1.Name() != t2.Name() { + if t1.Name() != t2.Name() { return false } + t1Fields := 0 for i := 0; i < t1.NumField(); i++ { t1f := t1.Field(i) - t2f, ok := t2.FieldByName(t1f.Name) + if t1f.Tag.Get(TAG_KEY) == TAG_AUTO_SKIP { + // ignore this input field + continue + } + t1Fields++ + t2f, ok := t2.FieldByName(t1f.Name) if !ok { return false } @@ -76,20 +89,22 @@ func (t translator) translatableStruct(t1, t2 reflect.Type) bool { return false } } - return true + return t2.NumField() == t1Fields } // checks that t could reasonably be the type of a translator function -func couldBeValidTranslator(t reflect.Type) bool { - if t.Kind() != reflect.Func { +func (t translator) couldBeValidTranslator(tr reflect.Type) bool { + if tr.Kind() != reflect.Func { return false } - if t.NumIn() != 1 || t.NumOut() != 2 { + if tr.NumIn() != 2 || tr.NumOut() != 3 { return false } - if util.IsInvalidInConfig(t.In(0).Kind()) || - util.IsInvalidInConfig(t.Out(0).Kind()) || - t.Out(1) != translationsType { + if util.IsInvalidInConfig(tr.In(0).Kind()) || + util.IsInvalidInConfig(tr.Out(0).Kind()) || + tr.In(1) != reflect.TypeOf(t.options) || + tr.Out(1) != translationsType || + tr.Out(2) != reportType { return false } return true @@ -122,6 +137,10 @@ func (t translator) translateSameType(vFrom, vTo reflect.Value, fromPath, toPath } case k == reflect.Struct: for i := 0; i < vFrom.NumField(); i++ { + if vFrom.Type().Field(i).Tag.Get(TAG_KEY) == TAG_AUTO_SKIP { + // ignore this input field + continue + } fieldGoName := vFrom.Type().Field(i).Name toStructField, ok := vTo.Type().FieldByName(fieldGoName) if !ok { @@ -153,7 +172,7 @@ func (t translator) translate(vFrom, vTo reflect.Value, fromPath, toPath path.Co tFrom := vFrom.Type() tTo := vTo.Type() if fnv := t.getTranslator(tFrom, tTo); fnv.IsValid() { - returns := fnv.Call([]reflect.Value{vFrom}) + returns := fnv.Call([]reflect.Value{vFrom, reflect.ValueOf(t.options)}) vTo.Set(returns[0]) // handle all the translations and "rebase" them to our current place @@ -163,6 +182,14 @@ func (t translator) translate(vFrom, vTo reflect.Value, fromPath, toPath path.Co to := toPath.Append(trans.To.Path...) t.translations.AddTranslation(from, to) } + + // likewise for the report entries + retReport := returns[2].Interface().(report.Report) + for i := range retReport.Entries { + entry := &retReport.Entries[i] + entry.Context = fromPath.Append(entry.Context.Path...) + } + t.report.Merge(retReport) return } if t.translatable(tFrom, tTo) { @@ -175,18 +202,19 @@ func (t translator) translate(vFrom, vTo reflect.Value, fromPath, toPath path.Co type Translator interface { // Adds a custom translator for cases where the structs are not identical. Must be of type - // func(fromType) -> (toType, TranslationSet). The translator should return the set of all - // translations it did. + // func(fromType, optionsType) -> (toType, TranslationSet, report.Report). + // The translator should return the set of all translations it did. AddCustomTranslator(t interface{}) // Also returns a list of source and dest paths, autocompleted by fromTag and toTag - Translate(from, to interface{}) TranslationSet + Translate(from, to interface{}) (TranslationSet, report.Report) } // NewTranslator creates a new Translator for translating from types with fromTag struct tags (e.g. "yaml") // to types with toTag struct tages (e.g. "json"). These tags are used when determining paths when generating // the TranslationSet returned by Translator.Translate() -func NewTranslator(fromTag, toTag string) Translator { +func NewTranslator(fromTag, toTag string, options interface{}) Translator { return &translator{ + options: options, translations: TranslationSet{ FromTag: fromTag, ToTag: toTag, @@ -196,18 +224,20 @@ func NewTranslator(fromTag, toTag string) Translator { } type translator struct { + options interface{} // List of custom translation funcs, must pass couldBeValidTranslator // This is only for fields that cannot or should not be trivially translated, // All trivially translated fields use the default behavior. translators []reflect.Value translations TranslationSet + report *report.Report } -// fn should be of the form func(fromType, translationsMap) -> toType -// fn should mutate translationsMap to add all the translations it did +// fn should be of the form +// func(fromType, optionsType) -> (toType, TranslationSet, report.Report) func (t *translator) AddCustomTranslator(fn interface{}) { fnv := reflect.ValueOf(fn) - if !couldBeValidTranslator(fnv.Type()) { + if !t.couldBeValidTranslator(fnv.Type()) { panic("Tried to register invalid translator function") } t.translators = append(t.translators, fnv) @@ -223,7 +253,7 @@ func (t translator) getTranslator(from, to reflect.Type) reflect.Value { } // Translate translates from into to and returns a set of all the path changes it performed. -func (t translator) Translate(from, to interface{}) TranslationSet { +func (t translator) Translate(from, to interface{}) (TranslationSet, report.Report) { fv := reflect.ValueOf(from) tv := reflect.ValueOf(to) if fv.Kind() != reflect.Ptr || tv.Kind() != reflect.Ptr { @@ -231,12 +261,13 @@ func (t translator) Translate(from, to interface{}) TranslationSet { } fv = fv.Elem() tv = tv.Elem() - // Make sure to clear this every time` + // Make sure to clear these every time t.translations = TranslationSet{ FromTag: t.translations.FromTag, ToTag: t.translations.ToTag, Set: map[string]Translation{}, } + t.report = &report.Report{} t.translate(fv, tv, path.New(t.translations.FromTag), path.New(t.translations.ToTag)) - return t.translations + return t.translations, *t.report } diff --git a/vendor/github.com/coreos/fcct/translate/util.go b/vendor/github.com/coreos/fcct/translate/util.go index 6f2b6dd504..a7b2f9c5c6 100644 --- a/vendor/github.com/coreos/fcct/translate/util.go +++ b/vendor/github.com/coreos/fcct/translate/util.go @@ -20,6 +20,7 @@ import ( "github.com/coreos/ignition/v2/config/util" "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" ) // fieldName returns the name uses when (un)marshalling a field. t should be a reflect.Value of a struct, @@ -78,3 +79,29 @@ func getAllPaths(v reflect.Value, tag string) []path.ContextPath { panic("Encountered types that are not the same when they should be. This is a bug, please file a report") } } + +// Return a copy of the report, with the context paths prefixed by prefix. +func prefixReport(r report.Report, prefix interface{}) report.Report { + var ret report.Report + ret.Merge(r) + for i := range ret.Entries { + entry := &ret.Entries[i] + entry.Context = path.New(entry.Context.Tag, prefix).Append(entry.Context.Path...) + } + return ret +} + +// Utility function to run a translation and prefix the resulting +// TranslationSet and Report. +func Prefixed(tr Translator, prefix interface{}, from interface{}, to interface{}) (TranslationSet, report.Report) { + tm, r := tr.Translate(from, to) + return tm.Prefix(prefix), prefixReport(r, prefix) +} + +// Utility function to run a translation and merge the result, with the +// specified prefix, into the specified TranslationSet and Report. +func MergeP(tr Translator, tm TranslationSet, r *report.Report, prefix interface{}, from interface{}, to interface{}) { + translations, report := tr.Translate(from, to) + tm.MergeP(prefix, translations) + r.Merge(prefixReport(report, prefix)) +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_2/types/config.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/config.go new file mode 100644 index 0000000000..4b18d53782 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/config.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/go-semver/semver" +) + +var ( + MaxVersion = semver.Version{ + Major: 3, + Minor: 2, + } +) diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_2/types/custom.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/custom.go new file mode 100644 index 0000000000..1b0c77b8b1 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/custom.go @@ -0,0 +1,41 @@ +// 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/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (cu Custom) Key() string { + return cu.Pin +} + +func (cu Custom) Validate(c path.ContextPath) (r report.Report) { + if cu.Pin == "" && cu.Config == "" { + return + } + switch cu.Pin { + case "tpm2", "tang", "sss": + default: + r.AddOnError(c.Append("pin"), errors.ErrUnknownClevisPin) + } + if cu.Config == "" { + r.AddOnError(c.Append("config"), errors.ErrClevisConfigRequired) + } + return +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_2/types/device.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/device.go new file mode 100644 index 0000000000..a10ce97b05 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/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/vendor/github.com/coreos/ignition/v2/config/v3_2/types/directory.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/directory.go new file mode 100644 index 0000000000..f6f0684557 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/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/vendor/github.com/coreos/ignition/v2/config/v3_2/types/disk.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/disk.go new file mode 100644 index 0000000000..17856e07a6 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/disk.go @@ -0,0 +1,134 @@ +// 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/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 || (p.ShouldExist != nil && !*p.ShouldExist) + hasZero = hasZero || (p.Number == 0) + } + return hasZero && hasShouldNotExist +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_2/types/file.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/file.go new file mode 100644 index 0000000000..04b14288d9 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/file.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 ( + "github.com/coreos/ignition/v2/config/shared/errors" + + "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 f.Overwrite != nil && *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/vendor/github.com/coreos/ignition/v2/config/v3_2/types/filesystem.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/filesystem.go new file mode 100644 index 0000000000..486aea86ac --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/filesystem.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 ( + "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) || + len(f.Options) != 0 { + return errors.ErrFormatNilWithOthers + } + } else { + switch *f.Format { + case "ext4", "btrfs", "xfs", "swap", "vfat": + 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/vendor/github.com/coreos/ignition/v2/config/v3_2/types/headers.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/headers.go new file mode 100644 index 0000000000..be1aadad9a --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/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/vendor/github.com/coreos/ignition/v2/config/v3_2/types/ignition.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/ignition.go new file mode 100644 index 0000000000..190445bda7 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/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/vendor/github.com/coreos/ignition/v2/config/v3_2/types/luks.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/luks.go new file mode 100644 index 0000000000..d7cd29bac2 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/luks.go @@ -0,0 +1,73 @@ +// 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 l.Clevis != nil { + if l.Clevis.Custom != nil && (len(l.Clevis.Tang) > 0 || (l.Clevis.Tpm2 != nil && *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) + } + 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/vendor/github.com/coreos/ignition/v2/config/v3_2/types/mode.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/mode.go new file mode 100644 index 0000000000..9eb7573d8b --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/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/vendor/github.com/coreos/ignition/v2/config/v3_2/types/node.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/node.go new file mode 100644 index 0000000000..52576a924a --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/node.go @@ -0,0 +1,58 @@ +// 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" + + 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 && (name != nil && *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/vendor/github.com/coreos/ignition/v2/config/v3_2/types/partition.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/partition.go new file mode 100644 index 0000000000..4bb60b2b81 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/partition.go @@ -0,0 +1,88 @@ +// 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/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 { + return fmt.Sprintf("label:%s", *p.Label) + } +} + +func (p Partition) Validate(c path.ContextPath) (r report.Report) { + if p.ShouldExist != nil && !*p.ShouldExist && + (p.Label != nil || (p.TypeGUID != nil && *p.TypeGUID != "") || (p.GUID != nil && *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/vendor/github.com/coreos/ignition/v2/config/v3_2/types/passwd.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/passwd.go new file mode 100644 index 0000000000..4060a2a6f1 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/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/vendor/github.com/coreos/ignition/v2/config/v3_2/types/path.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/path.go new file mode 100644 index 0000000000..131e300c1b --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/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/vendor/github.com/coreos/ignition/v2/config/v3_2/types/proxy.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/proxy.go new file mode 100644 index 0000000000..d48d210a0f --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/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/vendor/github.com/coreos/ignition/v2/config/v3_2/types/raid.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/raid.go new file mode 100644 index 0000000000..039e54e666 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/raid.go @@ -0,0 +1,55 @@ +// 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/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()) + return +} + +func (r Raid) validateLevel() error { + 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/vendor/github.com/coreos/ignition/v2/config/v3_2/types/resource.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/resource.go new file mode 100644 index 0000000000..68da6c7b78 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/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/vendor/github.com/coreos/ignition/v2/config/v3_2/types/schema.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/schema.go new file mode 100644 index 0000000000..b02c5a48e4 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/schema.go @@ -0,0 +1,246 @@ +package types + +// generated by "schematyper --package=types config/v3_2/schema/ignition.json -o config/v3_2/types/schema.go --root-type=Config" -- DO NOT EDIT + +type Clevis struct { + Custom *Custom `json:"custom,omitempty"` + Tang []Tang `json:"tang,omitempty"` + Threshold *int `json:"threshold,omitempty"` + Tpm2 *bool `json:"tpm2,omitempty"` +} + +type Config struct { + Ignition Ignition `json:"ignition"` + Passwd Passwd `json:"passwd,omitempty"` + Storage Storage `json:"storage,omitempty"` + Systemd Systemd `json:"systemd,omitempty"` +} + +type Custom struct { + Config string `json:"config"` + NeedsNetwork *bool `json:"needsNetwork,omitempty"` + Pin string `json:"pin"` +} + +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,omitempty"` +} + +type IgnitionConfig struct { + Merge []Resource `json:"merge,omitempty"` + Replace Resource `json:"replace,omitempty"` +} + +type Link struct { + Node + LinkEmbedded1 +} + +type LinkEmbedded1 struct { + Hard *bool `json:"hard,omitempty"` + Target string `json:"target"` +} + +type Luks struct { + Clevis *Clevis `json:"clevis,omitempty"` + Device *string `json:"device,omitempty"` + KeyFile Resource `json:"keyFile,omitempty"` + Label *string `json:"label,omitempty"` + Name string `json:"name"` + 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 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"` + Level string `json:"level"` + 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 { + 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/vendor/github.com/coreos/ignition/v2/config/v3_2/types/storage.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/storage.go new file mode 100644 index 0000000000..db7e44acd4 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/storage.go @@ -0,0 +1,70 @@ +// 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" + + 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) { + 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) + } + } + } + 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) + } + } + } + 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 l1.Hard == nil || !*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) + } + } + } + return +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_2/types/tang.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/tang.go new file mode 100644 index 0000000000..86ab79c9e6 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/tang.go @@ -0,0 +1,51 @@ +// 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 (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) + } + 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 + } +} diff --git a/vendor/github.com/coreos/fcct/base/v0_1/validate.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/tls.go similarity index 63% rename from vendor/github.com/coreos/fcct/base/v0_1/validate.go rename to vendor/github.com/coreos/ignition/v2/config/v3_2/types/tls.go index c8e6942aa4..8890e397e8 100644 --- a/vendor/github.com/coreos/fcct/base/v0_1/validate.go +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/tls.go @@ -1,4 +1,4 @@ -// Copyright 2019 Red Hat, Inc +// 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. @@ -10,24 +10,18 @@ // 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.) +// limitations under the License. -package v0_1 +package types import ( - "errors" - "github.com/coreos/vcontext/path" "github.com/coreos/vcontext/report" ) -var ( - ErrInlineAndSource = errors.New("inline cannot be specified if source is specified") -) - -func (f FileContents) Validate(c path.ContextPath) (r report.Report) { - if f.Inline != nil && f.Source != nil { - r.AddOnError(c.Append("inline"), ErrInlineAndSource) +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/vendor/github.com/coreos/ignition/v2/config/v3_2/types/unit.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/unit.go new file mode 100644 index 0000000000..8d1a5f00ff --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/unit.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 ( + "fmt" + "path" + "strings" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/shared/validations" + + "github.com/coreos/go-systemd/v22/unit" + 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 := validateUnitContent(u.Contents) + r.AddOnError(c, err) + + isEnabled := u.Enabled != nil && *u.Enabled + r.AddOnWarn(c, validations.ValidateInstallSection(u.Name, isEnabled, (u.Contents == nil || *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 := validateUnitContent(d.Contents) + r.AddOnError(c.Append("contents"), err) + + switch path.Ext(d.Name) { + case ".conf": + default: + r.AddOnError(c.Append("name"), errors.ErrInvalidSystemdDropinExt) + } + + return +} + +func validateUnitContent(content *string) ([]*unit.UnitOption, error) { + if content == nil { + return []*unit.UnitOption{}, nil + } + c := strings.NewReader(*content) + opts, err := unit.Deserialize(c) + if err != nil { + return nil, fmt.Errorf("invalid unit content: %s", err) + } + return opts, nil +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_2/types/url.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/url.go new file mode 100644 index 0000000000..0d8771bf6d --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/url.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 ( + "net/url" + + "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 "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/vendor/github.com/coreos/ignition/v2/config/v3_2/types/verification.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/types/verification.go new file mode 100644 index 0000000000..5def6f04b0 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/config.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/config.go new file mode 100644 index 0000000000..2d43f1cab3 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/config.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/go-semver/semver" +) + +var ( + MaxVersion = semver.Version{ + Major: 3, + Minor: 3, + PreRelease: "experimental", + } +) diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/custom.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/custom.go new file mode 100644 index 0000000000..1b0c77b8b1 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/custom.go @@ -0,0 +1,41 @@ +// 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/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (cu Custom) Key() string { + return cu.Pin +} + +func (cu Custom) Validate(c path.ContextPath) (r report.Report) { + if cu.Pin == "" && cu.Config == "" { + return + } + switch cu.Pin { + case "tpm2", "tang", "sss": + default: + r.AddOnError(c.Append("pin"), errors.ErrUnknownClevisPin) + } + if cu.Config == "" { + r.AddOnError(c.Append("config"), errors.ErrClevisConfigRequired) + } + return +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/device.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/device.go new file mode 100644 index 0000000000..a10ce97b05 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/directory.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/directory.go new file mode 100644 index 0000000000..f6f0684557 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/disk.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/disk.go new file mode 100644 index 0000000000..17856e07a6 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/disk.go @@ -0,0 +1,134 @@ +// 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/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 || (p.ShouldExist != nil && !*p.ShouldExist) + hasZero = hasZero || (p.Number == 0) + } + return hasZero && hasShouldNotExist +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/file.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/file.go new file mode 100644 index 0000000000..04b14288d9 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/file.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 ( + "github.com/coreos/ignition/v2/config/shared/errors" + + "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 f.Overwrite != nil && *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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/filesystem.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/filesystem.go new file mode 100644 index 0000000000..486aea86ac --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/filesystem.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 ( + "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) || + len(f.Options) != 0 { + return errors.ErrFormatNilWithOthers + } + } else { + switch *f.Format { + case "ext4", "btrfs", "xfs", "swap", "vfat": + 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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/headers.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/headers.go new file mode 100644 index 0000000000..be1aadad9a --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/ignition.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/ignition.go new file mode 100644 index 0000000000..190445bda7 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/luks.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/luks.go new file mode 100644 index 0000000000..d7cd29bac2 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/luks.go @@ -0,0 +1,73 @@ +// 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 l.Clevis != nil { + if l.Clevis.Custom != nil && (len(l.Clevis.Tang) > 0 || (l.Clevis.Tpm2 != nil && *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) + } + 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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/mode.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/mode.go new file mode 100644 index 0000000000..9eb7573d8b --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/node.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/node.go new file mode 100644 index 0000000000..52576a924a --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/node.go @@ -0,0 +1,58 @@ +// 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" + + 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 && (name != nil && *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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/partition.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/partition.go new file mode 100644 index 0000000000..4bb60b2b81 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/partition.go @@ -0,0 +1,88 @@ +// 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/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 { + return fmt.Sprintf("label:%s", *p.Label) + } +} + +func (p Partition) Validate(c path.ContextPath) (r report.Report) { + if p.ShouldExist != nil && !*p.ShouldExist && + (p.Label != nil || (p.TypeGUID != nil && *p.TypeGUID != "") || (p.GUID != nil && *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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/passwd.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/passwd.go new file mode 100644 index 0000000000..4060a2a6f1 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/path.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/path.go new file mode 100644 index 0000000000..131e300c1b --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/proxy.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/proxy.go new file mode 100644 index 0000000000..d48d210a0f --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/raid.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/raid.go new file mode 100644 index 0000000000..039e54e666 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/raid.go @@ -0,0 +1,55 @@ +// 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/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()) + return +} + +func (r Raid) validateLevel() error { + 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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/resource.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/resource.go new file mode 100644 index 0000000000..68da6c7b78 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/schema.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/schema.go new file mode 100644 index 0000000000..47fd427e58 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/schema.go @@ -0,0 +1,246 @@ +package types + +// generated by "schematyper --package=types config/v3_3_experimental/schema/ignition.json -o config/v3_3_experimental/types/schema.go --root-type=Config" -- DO NOT EDIT + +type Clevis struct { + Custom *Custom `json:"custom,omitempty"` + Tang []Tang `json:"tang,omitempty"` + Threshold *int `json:"threshold,omitempty"` + Tpm2 *bool `json:"tpm2,omitempty"` +} + +type Config struct { + Ignition Ignition `json:"ignition"` + Passwd Passwd `json:"passwd,omitempty"` + Storage Storage `json:"storage,omitempty"` + Systemd Systemd `json:"systemd,omitempty"` +} + +type Custom struct { + Config string `json:"config"` + NeedsNetwork *bool `json:"needsNetwork,omitempty"` + Pin string `json:"pin"` +} + +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,omitempty"` +} + +type IgnitionConfig struct { + Merge []Resource `json:"merge,omitempty"` + Replace Resource `json:"replace,omitempty"` +} + +type Link struct { + Node + LinkEmbedded1 +} + +type LinkEmbedded1 struct { + Hard *bool `json:"hard,omitempty"` + Target string `json:"target"` +} + +type Luks struct { + Clevis *Clevis `json:"clevis,omitempty"` + Device *string `json:"device,omitempty"` + KeyFile Resource `json:"keyFile,omitempty"` + Label *string `json:"label,omitempty"` + Name string `json:"name"` + 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 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"` + Level string `json:"level"` + 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 { + 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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/storage.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/storage.go new file mode 100644 index 0000000000..db7e44acd4 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/storage.go @@ -0,0 +1,70 @@ +// 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" + + 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) { + 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) + } + } + } + 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) + } + } + } + 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 l1.Hard == nil || !*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) + } + } + } + return +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/tang.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/tang.go new file mode 100644 index 0000000000..86ab79c9e6 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/tang.go @@ -0,0 +1,51 @@ +// 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 (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) + } + 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 + } +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/tls.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/tls.go new file mode 100644 index 0000000000..8890e397e8 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/unit.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/unit.go new file mode 100644 index 0000000000..8d1a5f00ff --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/unit.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 ( + "fmt" + "path" + "strings" + + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/shared/validations" + + "github.com/coreos/go-systemd/v22/unit" + 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 := validateUnitContent(u.Contents) + r.AddOnError(c, err) + + isEnabled := u.Enabled != nil && *u.Enabled + r.AddOnWarn(c, validations.ValidateInstallSection(u.Name, isEnabled, (u.Contents == nil || *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 := validateUnitContent(d.Contents) + r.AddOnError(c.Append("contents"), err) + + switch path.Ext(d.Name) { + case ".conf": + default: + r.AddOnError(c.Append("name"), errors.ErrInvalidSystemdDropinExt) + } + + return +} + +func validateUnitContent(content *string) ([]*unit.UnitOption, error) { + if content == nil { + return []*unit.UnitOption{}, nil + } + c := strings.NewReader(*content) + opts, err := unit.Deserialize(c) + if err != nil { + return nil, fmt.Errorf("invalid unit content: %s", err) + } + return opts, nil +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/url.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/url.go new file mode 100644 index 0000000000..0d8771bf6d --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/url.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 ( + "net/url" + + "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 "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/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/verification.go b/vendor/github.com/coreos/ignition/v2/config/v3_3_experimental/types/verification.go new file mode 100644 index 0000000000..5def6f04b0 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3_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/vendor/github.com/coreos/vcontext/yaml/yaml.go b/vendor/github.com/coreos/vcontext/yaml/yaml.go new file mode 100644 index 0000000000..7e9048ada3 --- /dev/null +++ b/vendor/github.com/coreos/vcontext/yaml/yaml.go @@ -0,0 +1,82 @@ +// Copyright 2019 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 json + +import ( + "github.com/coreos/vcontext/tree" + + "gopkg.in/yaml.v3" +) + +func UnmarshalToContext(raw []byte) (tree.Node, error) { + var ast yaml.Node + if err := yaml.Unmarshal(raw, &ast); err != nil { + return nil, err + } + return fromYamlNode(ast), nil +} + +func fromYamlNode(n yaml.Node) tree.Node { + m := tree.Marker{ + StartP: &tree.Pos{ + Line: int64(n.Line), + Column: int64(n.Column), + }, + } + switch n.Kind { + case 0: + // empty + return nil + case yaml.DocumentNode: + if len(n.Content) == 0 { + return nil + } + return fromYamlNode(*n.Content[0]) + case yaml.MappingNode: + ret := tree.MapNode{ + Marker: m, + Children: make(map[string]tree.Node, len(n.Content)/2), + Keys: make(map[string]tree.Leaf, len(n.Content)/2), + } + // MappingNodes list keys and values like [k, v, k, v...] + for i := 0; i < len(n.Content); i += 2 { + key := *n.Content[i] + value := *n.Content[i+1] + ret.Keys[key.Value] = tree.Leaf{ + Marker: tree.Marker{ + StartP: &tree.Pos{ + Line: int64(key.Line), + Column: int64(key.Column), + }, + }, + } + ret.Children[key.Value] = fromYamlNode(value) + } + return ret + case yaml.SequenceNode: + ret := tree.SliceNode{ + Marker: m, + Children: make([]tree.Node, 0, len(n.Content)), + } + for _, child := range n.Content { + ret.Children = append(ret.Children, fromYamlNode(*child)) + } + return ret + default: // scalars and aliases + return tree.Leaf{ + Marker: m, + } + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 0b37827d48..7222f93862 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -73,8 +73,12 @@ github.com/containers/ocicrypt/spec # github.com/containers/storage v1.20.2 github.com/containers/storage/pkg/config github.com/containers/storage/pkg/homedir -# github.com/coreos/fcct v0.5.0 -github.com/coreos/fcct/base/v0_1 +# github.com/coreos/fcct v0.7.0 +github.com/coreos/fcct/base +github.com/coreos/fcct/base/v0_2 +github.com/coreos/fcct/config/common +github.com/coreos/fcct/config/v1_1 +github.com/coreos/fcct/distro/fcos/v0_1 github.com/coreos/fcct/translate # github.com/coreos/go-semver v0.3.0 github.com/coreos/go-semver/semver @@ -116,6 +120,8 @@ github.com/coreos/ignition/v2/config/v3_0/types github.com/coreos/ignition/v2/config/v3_1 github.com/coreos/ignition/v2/config/v3_1/translate github.com/coreos/ignition/v2/config/v3_1/types +github.com/coreos/ignition/v2/config/v3_2/types +github.com/coreos/ignition/v2/config/v3_3_experimental/types github.com/coreos/ignition/v2/config/validate # github.com/coreos/vcontext v0.0.0-20191017033345-260217907eb5 github.com/coreos/vcontext/json @@ -123,6 +129,7 @@ github.com/coreos/vcontext/path github.com/coreos/vcontext/report github.com/coreos/vcontext/tree github.com/coreos/vcontext/validate +github.com/coreos/vcontext/yaml # github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew/spew # github.com/docker/distribution v2.7.1+incompatible