diff --git a/cmd/cluster-bootstrap/bootstrapinplace.go b/cmd/cluster-bootstrap/bootstrapinplace.go new file mode 100644 index 000000000..472a47b72 --- /dev/null +++ b/cmd/cluster-bootstrap/bootstrapinplace.go @@ -0,0 +1,65 @@ +package main + +import ( + "errors" + + "github.com/openshift/cluster-bootstrap/pkg/bootstrapinplace" + + "github.com/spf13/cobra" +) + +var ( + CmdBootstrapInPlace = &cobra.Command{ + Use: "bootstrap-in-place", + Short: "Create Ignition based on Fedora CoreOS Config", + Long: "", + PreRunE: validateBootstrapInPlaceOpts, + RunE: runCmdBootstrapInPlace, + SilenceUsage: true, + } + + bootstrapInPlaceOpts struct { + assetDir string + ignitionPath string + input string + Strict bool + Pretty bool + } +) + +func init() { + cmdRoot.AddCommand(CmdBootstrapInPlace) + CmdBootstrapInPlace.Flags().BoolVarP(&bootstrapInPlaceOpts.Strict, "strict", "s", true, "fail on any warning") + CmdBootstrapInPlace.Flags().BoolVarP(&bootstrapInPlaceOpts.Pretty, "pretty", "p", true, "output formatted json") + CmdBootstrapInPlace.Flags().StringVar(&bootstrapInPlaceOpts.input, "input", "", "fcc input file path") + CmdBootstrapInPlace.Flags().StringVar(&bootstrapInPlaceOpts.ignitionPath, "output", "o", "Ignition output file path") + CmdBootstrapInPlace.Flags().StringVarP(&bootstrapInPlaceOpts.assetDir, "asset-dir", "d", "", "allow embedding local files from this directory") +} + +func runCmdBootstrapInPlace(cmd *cobra.Command, args []string) error { + bip, err := bootstrapinplace.NewBootstrapInPlaceCommand(bootstrapinplace.BootstrapInPlaceConfig{ + AssetDir: bootstrapInPlaceOpts.assetDir, + IgnitionPath: bootstrapInPlaceOpts.ignitionPath, + Input: bootstrapInPlaceOpts.input, + Strict: bootstrapInPlaceOpts.Strict, + Pretty: bootstrapInPlaceOpts.Pretty, + }) + if err != nil { + return err + } + + return bip.Create() +} + +func validateBootstrapInPlaceOpts(cmd *cobra.Command, args []string) error { + if bootstrapInPlaceOpts.ignitionPath == "" { + return errors.New("missing required flag: --output") + } + if bootstrapInPlaceOpts.assetDir == "" { + return errors.New("missing required flag: --asset-dir") + } + if bootstrapInPlaceOpts.input == "" { + return errors.New("missing required flag: --input") + } + return nil +} diff --git a/cmd/cluster-bootstrap/start.go b/cmd/cluster-bootstrap/start.go index 3934f7e65..ce611fcae 100644 --- a/cmd/cluster-bootstrap/start.go +++ b/cmd/cluster-bootstrap/start.go @@ -3,6 +3,7 @@ package main import ( "errors" "strings" + "time" "github.com/spf13/cobra" @@ -26,6 +27,7 @@ var ( requiredPodClauses []string waitForTearDownEvent string earlyTearDown bool + assetsCreatedTimeout time.Duration } ) @@ -44,6 +46,7 @@ func init() { cmdStart.Flags().StringSliceVar(&startOpts.requiredPodClauses, "required-pods", defaultRequiredPods, "List of pods name prefixes with their namespace (written as /) that are required to be running and ready before the start command does the pivot, or alternatively a list of or'ed pod prefixes with a description (written as :/|/|...).") cmdStart.Flags().StringVar(&startOpts.waitForTearDownEvent, "tear-down-event", "", "if this optional event name of the form / is given, the event is waited for before tearing down the bootstrap control plane") cmdStart.Flags().BoolVar(&startOpts.earlyTearDown, "tear-down-early", true, "tear down immediate after the non-bootstrap control plane is up and bootstrap-success event is created.") + cmdStart.Flags().DurationVar(&startOpts.assetsCreatedTimeout, "assets-create-timeout", time.Duration(60)*time.Minute, "how long to wait for all the assets be created.") } func runCmdStart(cmd *cobra.Command, args []string) error { @@ -59,6 +62,7 @@ func runCmdStart(cmd *cobra.Command, args []string) error { RequiredPodPrefixes: podPrefixes, WaitForTearDownEvent: startOpts.waitForTearDownEvent, EarlyTearDown: startOpts.earlyTearDown, + AssetsCreatedTimeout: startOpts.assetsCreatedTimeout, }) if err != nil { return err diff --git a/glide.diff b/glide.diff new file mode 100644 index 000000000..fdc59e880 --- /dev/null +++ b/glide.diff @@ -0,0 +1,71044 @@ +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/.github/ISSUE_TEMPLATE/release-checklist.md updated/vendor/github.com/coreos/fcct/.github/ISSUE_TEMPLATE/release-checklist.md +1,48d0 +< Release checklist: +< +< Tagging: +< - [ ] Write release notes in NEWS. Get them reviewed and merged +< - [ ] If doing a branched release, also include a PR to merge the NEWS changes into master +< - [ ] Ensure your local copy is up to date with the upstream master branch (`git@github.com:coreos/fcct.git`) +< - [ ] Ensure your working directory is clean (`git clean -fdx`) +< - [ ] Ensure you can sign commits and any yubikeys/smartcards are plugged in +< - [ ] Run `./tag_release.sh ` +< - [ ] Push that tag to GitHub +< +< Packaging: +< - [ ] Update the FCCT spec file in [Fedora](https://src.fedoraproject.org/rpms/fedora-coreos-config-transpiler): +< - Bump the `Version` +< - Switch the `Release` back to `1.git%{shortcommit}%{?dist}` +< - Remove any patches obsoleted by the new release +< - Run `go-mods-to-bundled-provides.py | sort` while inside of the fcct directory you ran `./tag_release` from & copy output into spec file in `# Main package provides` section +< - Update change log +< - [ ] Run `spectool -g -S fedora-coreos-config-transpiler.spec` +< - [ ] Run `kinit your_fas_account@FEDORAPROJECT.ORG` +< - [ ] Run `fedpkg new-sources tarball-name` +< - [ ] PR the changes in [Fedora](https://src.fedoraproject.org/rpms/fedora-coreos-config-transpiler) +< - [ ] Once the PR merges to master, merge master into the other relevant branches (e.g. f30) then push those +< - [ ] On each of those branches run `fedpkg build` +< - [ ] Once the builds have finished, submit them to [bodhi](https://bodhi.fedoraproject.org/updates/new), filling in: +< - `fedora-coreos-config-transpiler` for `Packages` +< - Selecting the build(s) that just completed, except for the rawhide one (which gets submitted automatically) +< - Writing brief release notes like "New upstream release. See release notes at `link to NEWS on GH tag`" +< - Leave `Update name` blank +< - `Type`, `Severity` and `Suggestion` can be left as `unspecified` unless it is a security release. In that case select `security` which the appropriate severity. +< - `Stable karma` and `Unstable` karma can be set to `2` and `-1`, respectively. +< +< GitHub release: +< - [ ] Wait until the Bodhi update shows "Signed :heavy_check_mark:" in the Metadata box. +< - [ ] [File a releng ticket](https://pagure.io/releng/new_issue) based on [prior signing tickets](https://pagure.io/releng/issue/9489). +< - [ ] Update the script and test it locally by dropping the `sigul` lines. +< - [ ] Ping `mboddu` in Freenode `#fedora-coreos`, linking to the ticket +< - [ ] Wait for the ticket to be closed +< - [ ] Download the artifacts and signatures +< - [ ] Verify the signatures +< - [ ] Find the new tag in the [GitHub tag list](https://github.com/coreos/fcct/tags) and click the triple dots menu, and create a draft release for it. +< - [ ] Upload all the release artifacts and their signatures. Copy and paste the release notes from NEWS here as well. +< - [ ] Publish the release +< +< Quay release: +< - [ ] Visit the [Quay tags page](https://quay.io/repository/coreos/fcct?tab=tags) and wait for a versioned tag to appear +< - [ ] Click the gear next to the tag, select "Add New Tag", enter `release`, and confirm +< +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/.gitignore updated/vendor/github.com/coreos/fcct/.gitignore +1,2d0 +< /bin +< /tmpdocs +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/.travis.yml updated/vendor/github.com/coreos/fcct/.travis.yml +1,12d0 +< language: go +< go: +< - "1.12.x" +< +< services: +< - docker +< +< install: echo nop +< +< script: +< - ./test +< - docker build . +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/Dockerfile updated/vendor/github.com/coreos/fcct/Dockerfile +1,9d0 +< FROM golang:latest AS builder +< RUN mkdir /fcct +< COPY . /fcct +< WORKDIR /fcct +< RUN ./build_releases +< +< FROM scratch +< COPY --from=builder /fcct/bin/releases/fcct-x86_64-unknown-linux-gnu /usr/local/bin/fcct +< ENTRYPOINT ["/usr/local/bin/fcct"] +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/LICENSE updated/vendor/github.com/coreos/fcct/LICENSE +1,202d0 +< Apache License +< Version 2.0, January 2004 +< http://www.apache.org/licenses/ +< +< TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +< +< 1. Definitions. +< +< "License" shall mean the terms and conditions for use, reproduction, +< and distribution as defined by Sections 1 through 9 of this document. +< +< "Licensor" shall mean the copyright owner or entity authorized by +< the copyright owner that is granting the License. +< +< "Legal Entity" shall mean the union of the acting entity and all +< other entities that control, are controlled by, or are under common +< control with that entity. For the purposes of this definition, +< "control" means (i) the power, direct or indirect, to cause the +< direction or management of such entity, whether by contract or +< otherwise, or (ii) ownership of fifty percent (50%) or more of the +< outstanding shares, or (iii) beneficial ownership of such entity. +< +< "You" (or "Your") shall mean an individual or Legal Entity +< exercising permissions granted by this License. +< +< "Source" form shall mean the preferred form for making modifications, +< including but not limited to software source code, documentation +< source, and configuration files. +< +< "Object" form shall mean any form resulting from mechanical +< transformation or translation of a Source form, including but +< not limited to compiled object code, generated documentation, +< and conversions to other media types. +< +< "Work" shall mean the work of authorship, whether in Source or +< Object form, made available under the License, as indicated by a +< copyright notice that is included in or attached to the work +< (an example is provided in the Appendix below). +< +< "Derivative Works" shall mean any work, whether in Source or Object +< form, that is based on (or derived from) the Work and for which the +< editorial revisions, annotations, elaborations, or other modifications +< represent, as a whole, an original work of authorship. For the purposes +< of this License, Derivative Works shall not include works that remain +< separable from, or merely link (or bind by name) to the interfaces of, +< the Work and Derivative Works thereof. +< +< "Contribution" shall mean any work of authorship, including +< the original version of the Work and any modifications or additions +< to that Work or Derivative Works thereof, that is intentionally +< submitted to Licensor for inclusion in the Work by the copyright owner +< or by an individual or Legal Entity authorized to submit on behalf of +< the copyright owner. For the purposes of this definition, "submitted" +< means any form of electronic, verbal, or written communication sent +< to the Licensor or its representatives, including but not limited to +< communication on electronic mailing lists, source code control systems, +< and issue tracking systems that are managed by, or on behalf of, the +< Licensor for the purpose of discussing and improving the Work, but +< excluding communication that is conspicuously marked or otherwise +< designated in writing by the copyright owner as "Not a Contribution." +< +< "Contributor" shall mean Licensor and any individual or Legal Entity +< on behalf of whom a Contribution has been received by Licensor and +< subsequently incorporated within the Work. +< +< 2. Grant of Copyright License. Subject to the terms and conditions of +< this License, each Contributor hereby grants to You a perpetual, +< worldwide, non-exclusive, no-charge, royalty-free, irrevocable +< copyright license to reproduce, prepare Derivative Works of, +< publicly display, publicly perform, sublicense, and distribute the +< Work and such Derivative Works in Source or Object form. +< +< 3. Grant of Patent License. Subject to the terms and conditions of +< this License, each Contributor hereby grants to You a perpetual, +< worldwide, non-exclusive, no-charge, royalty-free, irrevocable +< (except as stated in this section) patent license to make, have made, +< use, offer to sell, sell, import, and otherwise transfer the Work, +< where such license applies only to those patent claims licensable +< by such Contributor that are necessarily infringed by their +< Contribution(s) alone or by combination of their Contribution(s) +< with the Work to which such Contribution(s) was submitted. If You +< institute patent litigation against any entity (including a +< cross-claim or counterclaim in a lawsuit) alleging that the Work +< or a Contribution incorporated within the Work constitutes direct +< or contributory patent infringement, then any patent licenses +< granted to You under this License for that Work shall terminate +< as of the date such litigation is filed. +< +< 4. Redistribution. You may reproduce and distribute copies of the +< Work or Derivative Works thereof in any medium, with or without +< modifications, and in Source or Object form, provided that You +< meet the following conditions: +< +< (a) You must give any other recipients of the Work or +< Derivative Works a copy of this License; and +< +< (b) You must cause any modified files to carry prominent notices +< stating that You changed the files; and +< +< (c) You must retain, in the Source form of any Derivative Works +< that You distribute, all copyright, patent, trademark, and +< attribution notices from the Source form of the Work, +< excluding those notices that do not pertain to any part of +< the Derivative Works; and +< +< (d) If the Work includes a "NOTICE" text file as part of its +< distribution, then any Derivative Works that You distribute must +< include a readable copy of the attribution notices contained +< within such NOTICE file, excluding those notices that do not +< pertain to any part of the Derivative Works, in at least one +< of the following places: within a NOTICE text file distributed +< as part of the Derivative Works; within the Source form or +< documentation, if provided along with the Derivative Works; or, +< within a display generated by the Derivative Works, if and +< wherever such third-party notices normally appear. The contents +< of the NOTICE file are for informational purposes only and +< do not modify the License. You may add Your own attribution +< notices within Derivative Works that You distribute, alongside +< or as an addendum to the NOTICE text from the Work, provided +< that such additional attribution notices cannot be construed +< as modifying the License. +< +< You may add Your own copyright statement to Your modifications and +< may provide additional or different license terms and conditions +< for use, reproduction, or distribution of Your modifications, or +< for any such Derivative Works as a whole, provided Your use, +< reproduction, and distribution of the Work otherwise complies with +< the conditions stated in this License. +< +< 5. Submission of Contributions. Unless You explicitly state otherwise, +< any Contribution intentionally submitted for inclusion in the Work +< by You to the Licensor shall be under the terms and conditions of +< this License, without any additional terms or conditions. +< Notwithstanding the above, nothing herein shall supersede or modify +< the terms of any separate license agreement you may have executed +< with Licensor regarding such Contributions. +< +< 6. Trademarks. This License does not grant permission to use the trade +< names, trademarks, service marks, or product names of the Licensor, +< except as required for reasonable and customary use in describing the +< origin of the Work and reproducing the content of the NOTICE file. +< +< 7. Disclaimer of Warranty. Unless required by applicable law or +< agreed to in writing, Licensor provides the Work (and each +< Contributor provides its Contributions) on an "AS IS" BASIS, +< WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +< implied, including, without limitation, any warranties or conditions +< of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +< PARTICULAR PURPOSE. You are solely responsible for determining the +< appropriateness of using or redistributing the Work and assume any +< risks associated with Your exercise of permissions under this License. +< +< 8. Limitation of Liability. In no event and under no legal theory, +< whether in tort (including negligence), contract, or otherwise, +< unless required by applicable law (such as deliberate and grossly +< negligent acts) or agreed to in writing, shall any Contributor be +< liable to You for damages, including any direct, indirect, special, +< incidental, or consequential damages of any character arising as a +< result of this License or out of the use or inability to use the +< Work (including but not limited to damages for loss of goodwill, +< work stoppage, computer failure or malfunction, or any and all +< other commercial damages or losses), even if such Contributor +< has been advised of the possibility of such damages. +< +< 9. Accepting Warranty or Additional Liability. While redistributing +< the Work or Derivative Works thereof, You may choose to offer, +< and charge a fee for, acceptance of support, warranty, indemnity, +< or other liability obligations and/or rights consistent with this +< License. However, in accepting such obligations, You may act only +< on Your own behalf and on Your sole responsibility, not on behalf +< of any other Contributor, and only if You agree to indemnify, +< defend, and hold each Contributor harmless for any liability +< incurred by, or claims asserted against, such Contributor by reason +< of your accepting any such warranty or additional liability. +< +< END OF TERMS AND CONDITIONS +< +< APPENDIX: How to apply the Apache License to your work. +< +< To apply the Apache License to your work, attach the following +< boilerplate notice, with the fields enclosed by brackets "{}" +< replaced with your own identifying information. (Don't include +< the brackets!) The text should be enclosed in the appropriate +< comment syntax for the file format. We also recommend that a +< file or class name and description of purpose be included on the +< same "printed page" as the copyright notice for easier +< identification within third-party archives. +< +< Copyright {yyyy} {name of copyright owner} +< +< 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. +< +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/NEWS updated/vendor/github.com/coreos/fcct/NEWS +1,158d0 +< 2020-12-04 FCCT 0.8.0 +< +< Breaking Changes: +< +< - Restructure Go API +< +< Features: +< +< - Stabilize Fedora CoreOS spec 1.3.0, targeting Ignition spec 3.2.0 +< - Add Fedora CoreOS spec 1.4.0-experimental, targeting Ignition spec +< 3.3.0-experimental +< - Add RHEL CoreOS spec 0.1.0, targeting Ignition spec 3.2.0 +< - Add RHEL CoreOS spec 0.2.0-experimental, targeting Ignition spec +< 3.3.0-experimental +< - Add boot_device section for configuring boot device LUKS and mirroring +< (fcos 1.3.0, rhcos 0.1.0) +< +< Bug Fixes: +< +< - Fix systemd-fsck@.service dependencies in generated mount units +< +< Misc Changes: +< +< - Warn if file/dir modes appear to have been specified in decimal +< - Validate input in translation functions taking Go structs (Go API) +< - Allow registering external translators (Go API) +< - Allow specs to derive from other specs (Go API) +< +< Docs Changes: +< +< - Document Clevis custom and LUKS wipe_volume fields +< - Add LUKS and mirroring examples +< - Add password authentication example +< +< 2020-10-23 FCCT 0.7.0 +< +< Features: +< +< - Stabilize FCC spec 1.2.0, targeting Ignition spec 3.2.0 +< - Add FCC spec 1.3.0-experimental, targeting Ignition spec +< 3.3.0-experimental +< - Add storage.luks section for creating LUKS2 encrypted volumes +< (1.2.0) +< - Add resize field for modifying partition size (1.2.0) +< - Add should_exist field for deleting users & groups (1.2.0) +< - Add NoResourceAutoCompression translate option to skip +< automatic compression (Go API) +< +< Docs Changes: +< +< - Switch to GitHub Pages +< +< +< 2020-05-28 FCCT 0.6.0 +< +< Features: +< +< - Stabilize FCC spec 1.1.0, targeting Ignition spec 3.1.0 +< - Add FCC spec 1.2.0-experimental, targeting Ignition spec +< 3.2.0-experimental +< - Add inline field to TLS certificate authorities and config merge and +< replace (1.1.0) +< - Add local field for embedding contents from local file (1.1.0) +< - Add storage.trees section for embedding local directory trees (1.1.0) +< - Auto-select smallest encoding for inline or local contents (1.1.0) +< - Add http_headers field for specifying HTTP headers on fetch (1.1.0) +< +< Bug Fixes: +< +< - Include mount options in generated mount units (1.1.0) +< - Validate uniqueness constraints within FCC sections +< - Omit empty values from output JSON +< - Append newline to output +< +< Docs Changes: +< +< - Document support for CA bundles in Ignition >= 2.3.0 +< - Document support for sha256 resource verification (1.1.0) +< - Clarify semantics of overwrite and mode fields +< +< +< 2020-03-23 FCCT 0.5.0 +< +< Breaking Changes: +< +< - Previously, command-line options could be preceded by a single dash +< (-strict) or double dash (--strict). Accept only the double-dash form. +< +< Features: +< +< - Accept input filename directly on command line, without --input +< - Add short equivalents of command-line options +< +< Bug Fixes: +< +< - Fail if unexpected non-option arguments are specified +< +< Misc Changes: +< +< - Deprecate --input and hide it from --help +< - Document files[].append[].inline property +< - Update docs for switch to Fedora signing keys +< +< +< 2020-01-24 FCCT 0.4.0 +< +< Features: +< +< - Add mount_options field to filesystem entry +< +< Misc Changes: +< +< - Add "release" tag to container of latest release +< - Vendor dependencies +< +< +< 2020-01-23 FCCT 0.3.0 +< +< Features: +< +< - Add v1.1.0-experimental spec +< - Add with_mount_unit field to generate mount unit from filesystem entry +< +< Bug Fixes: +< +< - Report warnings and errors to stderr, not stdout +< - Truncate output file before writing +< - Fix line and column reporting +< +< Misc Changes: +< +< - Document syntax of inline file contents +< - Document usage of published container image +< +< +< 2019-07-24 FCCT 0.2.0 +< +< Features: +< +< - Add --version flag +< - Add Dockerfile and build containers automatically on quay.io +< +< Bug Fixes: +< +< - Fix validation of paths for files and directories +< - Fix --output flag handling +< +< Misc Changes: +< +< - Add tests for the examples in the docs +< - Add travis integration +< +< +< 2019-07-10 FCCT 0.1.0 +< +< Initial Release of FCCT. While the golang API is not stable, the Fedora CoreOS +< Configuration language is. Configs written with version 1.0.0 will continue to +< work with future releases of FCCT. +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/README.md updated/vendor/github.com/coreos/fcct/README.md +1,55d0 +< # Fedora CoreOS Config Transpiler +< +< The Fedora CoreOS Config Transpiler (FCCT) translates human readable Fedora CoreOS Configs (FCCs) +< into machine readable [Ignition](https://github.com/coreos/ignition) Configs. See the [getting +< started](docs/getting-started.md) guide for how to use FCCT and the [configuration +< specifications](docs/specs.md) for everything FCCs support. +< +< ### Project Layout +< +< Internally, FCCT has a versioned `base` component which contains support for +< a particular Ignition spec version, plus distro-independent sugar. New base +< functionality is added only to the experimental base package. Eventually the +< experimental base package is stabilized and a new experimental package +< created. The base component is versioned independently of any particular +< distro, and its versions are not exposed to the user. Client code should +< not need to import anything from `base`. +< +< Each FCC variant/version pair corresponds to a `config` package, which +< derives either from a `base` package or from another `config` package. New +< functionality is similarly added only to an experimental config version, +< which is eventually stabilized and a new experimental version created. +< (This will often happen when the underlying package is stabilized.) A +< `config` package can contain sugar or validation logic specific to a distro +< (for example, additional properties for configuring etcd). +< +< Packages outside the FCCT repository can implement additional FCC versions +< by deriving from a `base` or `config` package and registering their +< variant/version pair with `config`. +< +< `config/` +< Top-level `TranslateBytes()` function that determines which config version +< to parse and emit. Clients should typically use this to translate FCCs. +< +< `config/common/` +< Common definitions for all spec versions, including translate options +< structs and error definitions. +< +< `config/*/vX_Y/` +< User facing definitions of the spec. Each is derived from another config +< package or from a base package. Each one defines its own translate +< functions to be registered in the `config` package. Clients can use +< these directly if they want to translate a specific spec version. +< +< `config/util/` +< Utility code for implementing config packages, including the +< (un)marshaling helpers. Clients don't need to import this unless they're +< implementing an out-of-tree config version. +< +< `base/` +< Distro-agnostic code targeting individual Ignition spec versions. Clients +< don't need to import this unless they're implementing an out-of-tree +< config version. +< +< `internal/` +< `main`, non-exported code. +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/util/file.go updated/vendor/github.com/coreos/fcct/base/util/file.go +1,146d0 +< // 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 util +< +< import ( +< "path/filepath" +< "strings" +< +< "github.com/coreos/fcct/config/common" +< ) +< +< 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 common.ErrFilesDirEscape +< } +< return nil +< } +< +< /// CheckForDecimalMode fails if the specified mode appears to have been +< /// incorrectly specified in decimal instead of octal. +< func CheckForDecimalMode(mode int, directory bool) error { +< correctedMode, ok := decimalModeToOctal(mode) +< if !ok { +< return nil +< } +< if !isTypicalMode(mode, directory) && isTypicalMode(correctedMode, directory) { +< return common.ErrDecimalMode +< } +< return nil +< } +< +< /// isTypicalMode returns true if the specified mode is unsurprising. +< /// It returns false for some modes that are unusual but valid in limited +< /// cases. +< func isTypicalMode(mode int, directory bool) bool { +< // no permissions is always reasonable (root ignores mode bits) +< if mode == 0 { +< return true +< } +< +< // test user/group/other in reverse order +< perms := []int{mode & 0007, (mode & 0070) >> 3, (mode & 0700) >> 6} +< hadR := false +< hadW := false +< hadX := false +< for _, perm := range perms { +< r := perm&4 != 0 +< w := perm&2 != 0 +< x := perm&1 != 0 +< // more-specific perm must have all the bits of less-specific +< // perm (r--rw----) +< if !r && hadR || !w && hadW || !x && hadX { +< return false +< } +< // if we have executable permission, it's weird for a +< // less-specific perm to have read but not execute (rwxr-----) +< if x && hadR && !hadX { +< return false +< } +< // -w- and --x are reasonable in special cases but they're +< // uncommon +< if (w || x) && !r { +< return false +< } +< hadR = hadR || r +< hadW = hadW || w +< hadX = hadX || x +< } +< +< // must be readable by someone +< if !hadR { +< return false +< } +< +< if directory { +< // must be executable by someone +< if !hadX { +< return false +< } +< // setuid forbidden +< if mode&04000 != 0 { +< return false +< } +< // setgid or sticky must be writable to someone +< if mode&03000 != 0 && !hadW { +< return false +< } +< } else { +< // setuid or setgid +< if mode&06000 != 0 { +< // must be executable to someone +< if !hadX { +< return false +< } +< // world-writable permission is a bad idea +< if mode&2 != 0 { +< return false +< } +< } +< // sticky forbidden +< if mode&01000 != 0 { +< return false +< } +< } +< +< return true +< } +< +< /// decimalModeToOctal takes a mode written in decimal and converts it to +< /// octal, returning (0, false) on failure. +< func decimalModeToOctal(mode int) (int, bool) { +< if mode < 0 || mode > 7777 { +< // out of range +< return 0, false +< } +< ret := 0 +< for divisor := 1000; divisor > 0; divisor /= 10 { +< digit := (mode / divisor) % 10 +< if digit > 7 { +< // digit not available in octal +< return 0, false +< } +< ret = (ret << 3) | digit +< } +< return ret, true +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/util/file_test.go updated/vendor/github.com/coreos/fcct/base/util/file_test.go +1,125d0 +< // 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 util +< +< import ( +< "fmt" +< "strings" +< "testing" +< +< "github.com/stretchr/testify/assert" +< ) +< +< var ( +< expectedBadDirModes = []int{ +< 500, // 0764 +< 550, // 01046 +< 555, // 01053 +< 700, // 01274 +< 750, // 01356 +< 755, // 01363 +< 770, // 01402 +< 775, // 01407 +< 777, // 01411 +< 1700, // 03244 +< 1750, // 03326 +< 1755, // 03333 +< 1770, // 03352 +< 1775, // 03357 +< 1777, // 03361 +< 2700, // 05214 +< 2750, // 05276 +< 2755, // 05303 +< 2770, // 05322 +< 2775, // 05327 +< 2777, // 05331 +< 3700, // 07164 +< 3750, // 07246 +< 3755, // 07253 +< 3770, // 07272 +< 3775, // 07277 +< 3777, // 07301 +< } +< expectedBadFileModes = []int{ +< 400, // 0620 +< 440, // 0670 +< 444, // 0674 +< 500, // 0764 +< 550, // 01046 +< 555, // 01053 +< 600, // 01130 +< 640, // 01200 +< 644, // 01204 +< 660, // 01224 +< 664, // 01230 +< 666, // 01232 +< 700, // 01274 +< 750, // 01356 +< 755, // 01363 +< 770, // 01402 +< 775, // 01407 +< 777, // 01411 +< 2500, // 04704 +< 2550, // 04766 +< 2555, // 04773 +< 2700, // 05214 +< 2750, // 05276 +< 2755, // 05303 +< 2770, // 05322 +< 2775, // 05327 +< 4500, // 010624 +< 4550, // 010706 +< 4555, // 010713 +< 4700, // 011134 +< 4750, // 011216 +< 4755, // 011223 +< 4770, // 011242 +< 4775, // 011247 +< 6500, // 014544 +< 6550, // 014626 +< 6555, // 014633 +< 6700, // 015054 +< 6750, // 015136 +< 6755, // 015143 +< 6770, // 015162 +< 6775, // 015167 +< } +< ) +< +< func TestCheckForDecimalMode(t *testing.T) { +< // test decimal to octal conversion +< for i := -1; i < 10001; i++ { +< iStr := fmt.Sprintf("%d", i) +< result, ok := decimalModeToOctal(i) +< assert.Equal(t, i >= 0 && i <= 7777 && !strings.ContainsAny(iStr, "89"), ok, "converting %d to octal returned incorrect ok", i) +< if ok { +< assert.Equal(t, iStr, fmt.Sprintf("%o", result), "converting %d to octal failed", i) +< } +< } +< +< // check the checker against a hardcoded list +< var badDirModes []int +< var badFileModes []int +< for i := -1; i <= 10000; i++ { +< if CheckForDecimalMode(i, true) != nil { +< badDirModes = append(badDirModes, i) +< } +< if CheckForDecimalMode(i, false) != nil { +< badFileModes = append(badFileModes, i) +< } +< } +< assert.Equal(t, expectedBadDirModes, badDirModes, "bad set of decimal directory modes") +< assert.Equal(t, expectedBadFileModes, badFileModes, "bad set of decimal file modes") +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/util/merge.go updated/vendor/github.com/coreos/fcct/base/util/merge.go +1,61d0 +< // 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 util +< +< import ( +< "fmt" +< +< "github.com/coreos/fcct/translate" +< +< "github.com/coreos/ignition/v2/config/merge" +< ) +< +< // MergeTranslatedConfigs merges a parent and child config and returns the +< // result. It also generates and returns the merged TranslationSet by +< // mapping the parent/child TranslationSets through the merge transcript. +< func MergeTranslatedConfigs(parent interface{}, parentTranslations translate.TranslationSet, child interface{}, childTranslations translate.TranslationSet) (interface{}, translate.TranslationSet) { +< // mappings: +< // left: parent or child translate.TranslationSet +< // right: merge.Transcript +< +< // merge configs +< result, right := merge.MergeStructTranscribe(parent, child) +< +< // merge left and right mappings into new TranslationSet +< if parentTranslations.FromTag != childTranslations.FromTag || parentTranslations.ToTag != childTranslations.ToTag { +< panic(fmt.Sprintf("mismatched translation tags, %s != %s || %s != %s", parentTranslations.FromTag, childTranslations.FromTag, parentTranslations.ToTag, childTranslations.ToTag)) +< } +< ts := translate.NewTranslationSet(parentTranslations.FromTag, parentTranslations.ToTag) +< for _, rightEntry := range right.Mappings { +< var left *translate.TranslationSet +< switch rightEntry.From.Tag { +< case merge.TAG_PARENT: +< left = &parentTranslations +< case merge.TAG_CHILD: +< left = &childTranslations +< default: +< panic("unexpected mapping tag " + rightEntry.From.Tag) +< } +< leftEntry, ok := left.Set[rightEntry.From.String()] +< if !ok { +< // the right mapping is more comprehensive than the +< // left mapping +< continue +< } +< rightEntry.To.Tag = leftEntry.To.Tag +< ts.AddTranslation(leftEntry.From, rightEntry.To) +< } +< return result, ts +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/util/merge_test.go updated/vendor/github.com/coreos/fcct/base/util/merge_test.go +1,156d0 +< // 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 util +< +< import ( +< "testing" +< +< "github.com/coreos/fcct/translate" +< +< "github.com/coreos/ignition/v2/config/util" +< // config version doesn't matter; just pick one +< "github.com/coreos/ignition/v2/config/v3_0/types" +< "github.com/coreos/vcontext/path" +< "github.com/stretchr/testify/assert" +< ) +< +< // TestMergeTranslatedConfigs tests merging two Ignition configs and their +< // corresponding translations. +< func TestMergeTranslatedConfigs(t *testing.T) { +< tests := []struct { +< parent types.Config +< parentTranslations translate.TranslationSet +< child types.Config +< childTranslations translate.TranslationSet +< merged types.Config +< mergedTranslations translate.TranslationSet +< }{ +< { +< parent: types.Config{ +< Ignition: types.Ignition{ +< Version: "3.0.0", +< }, +< Systemd: types.Systemd{ +< Units: []types.Unit{ +< { +< Name: "aardvark.service", +< Enabled: util.BoolToPtr(true), +< Contents: util.StrToPtr("antelope"), +< }, +< { +< Name: "caribou.service", +< Contents: util.StrToPtr("caribou"), +< }, +< { +< Name: "elephant.service", +< Contents: util.StrToPtr("elephant"), +< }, +< }, +< }, +< }, +< parentTranslations: makeTranslationSet([]translate.Translation{ +< // parent key duplicated in child, should be clobbered +< {path.New("in", "bad", 1), path.New("out", "systemd", "units", 0, "name")}, +< // parent field overridden in child, should be clobbered +< {path.New("in", "bad", 2), path.New("out", "systemd", "units", 0, "contents")}, +< // parent field not overridden in child +< {path.New("in", "good", 1), path.New("out", "systemd", "units", 0, "enabled")}, +< // parent key not specified in child +< {path.New("in", "good", 2), path.New("out", "systemd", "units", 1, "name")}, +< // parent field not specified in child +< {path.New("in", "good", 3), path.New("out", "systemd", "units", 1, "contents")}, +< // other fields omitted from translation set +< }), +< child: types.Config{ +< Ignition: types.Ignition{ +< Version: "3.0.0", +< }, +< Systemd: types.Systemd{ +< Units: []types.Unit{ +< { +< Name: "bear.service", +< Enabled: util.BoolToPtr(true), +< Contents: util.StrToPtr("bear"), +< }, +< { +< Name: "aardvark.service", +< Contents: util.StrToPtr("aardvark"), +< }, +< }, +< }, +< }, +< childTranslations: makeTranslationSet([]translate.Translation{ +< // child key not mentioned in parent +< {path.New("in", "good", 11), path.New("out", "systemd", "units", 0, "name")}, +< // child field not mentioned in parent +< {path.New("in", "good", 12), path.New("out", "systemd", "units", 0, "contents")}, +< // parent key duplicated in child +< {path.New("in", "good", 13), path.New("out", "systemd", "units", 1, "name")}, +< // parent field overridden in child +< {path.New("in", "good", 14), path.New("out", "systemd", "units", 1, "contents")}, +< // other fields omitted from translation set +< }), +< merged: types.Config{ +< Ignition: types.Ignition{ +< Version: "3.0.0", +< }, +< Systemd: types.Systemd{ +< Units: []types.Unit{ +< { +< Name: "aardvark.service", +< Enabled: util.BoolToPtr(true), +< Contents: util.StrToPtr("aardvark"), +< }, +< { +< Name: "caribou.service", +< Contents: util.StrToPtr("caribou"), +< }, +< { +< Name: "elephant.service", +< Contents: util.StrToPtr("elephant"), +< }, +< { +< Name: "bear.service", +< Enabled: util.BoolToPtr(true), +< Contents: util.StrToPtr("bear"), +< }, +< }, +< }, +< }, +< mergedTranslations: makeTranslationSet([]translate.Translation{ +< {path.New("in", "good", 13), path.New("out", "systemd", "units", 0, "name")}, +< {path.New("in", "good", 1), path.New("out", "systemd", "units", 0, "enabled")}, +< {path.New("in", "good", 14), path.New("out", "systemd", "units", 0, "contents")}, +< {path.New("in", "good", 2), path.New("out", "systemd", "units", 1, "name")}, +< {path.New("in", "good", 3), path.New("out", "systemd", "units", 1, "contents")}, +< {path.New("in", "good", 11), path.New("out", "systemd", "units", 3, "name")}, +< {path.New("in", "good", 12), path.New("out", "systemd", "units", 3, "contents")}, +< }), +< }, +< } +< for i, test := range tests { +< c, ts := MergeTranslatedConfigs(test.parent, test.parentTranslations, test.child, test.childTranslations) +< assert.Equal(t, test.merged, c, "#%d: bad config", i) +< assert.Equal(t, test.mergedTranslations, ts, "#%d: bad translations", i) +< } +< } +< +< func makeTranslationSet(translations []translate.Translation) translate.TranslationSet { +< ts := translate.NewTranslationSet(translations[0].From.Tag, translations[0].To.Tag) +< for _, t := range translations { +< ts.AddTranslation(t.From, t.To) +< } +< return ts +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/util/test.go updated/vendor/github.com/coreos/fcct/base/util/test.go +1,45d0 +< // 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 util +< +< import ( +< "fmt" +< "testing" +< +< "github.com/coreos/fcct/translate" +< "github.com/stretchr/testify/assert" +< ) +< +< // helper functions for writing tests +< +< // VerifyTranslations ensures all the translations are identity, unless they +< // match a listed one, and verifies that all the listed ones exist. +< func VerifyTranslations(t *testing.T, set translate.TranslationSet, exceptions []translate.Translation, format string, args ...interface{}) { +< message := fmt.Sprintf(format, args...) +< exceptionSet := translate.NewTranslationSet(set.FromTag, set.ToTag) +< for _, ex := range exceptions { +< exceptionSet.AddTranslation(ex.From, ex.To) +< if tr, ok := set.Set[ex.To.String()]; ok { +< assert.Equal(t, ex, tr, "%s: non-identity translation with unexpected From", message) +< } else { +< t.Errorf("%s: missing non-identity translation %v", message, ex) +< } +< } +< for key, translation := range set.Set { +< if _, ok := exceptionSet.Set[key]; !ok { +< assert.Equal(t, translation.From.Path, translation.To.Path, "%s: translation is not identity", message) +< } +< } +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/util/url.go updated/vendor/github.com/coreos/fcct/base/util/url.go +1,67d0 +< // 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 util +< +< import ( +< "bytes" +< "compress/gzip" +< "encoding/base64" +< "net/url" +< +< "github.com/vincent-petithory/dataurl" +< ) +< +< func MakeDataURL(contents []byte, currentCompression *string, allowCompression 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 == "") && allowCompression { +< 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 +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_1/schema.go updated/vendor/github.com/coreos/fcct/base/v0_1/schema.go +1,205d0 +< // 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 +< +< type CaReference struct { +< Source string `yaml:"source"` +< Verification Verification `yaml:"verification"` +< } +< +< type Config struct { +< Version string `yaml:"version"` +< Variant string `yaml:"variant"` +< Ignition Ignition `yaml:"ignition"` +< Passwd Passwd `yaml:"passwd"` +< Storage Storage `yaml:"storage"` +< Systemd Systemd `yaml:"systemd"` +< } +< +< type ConfigReference struct { +< Source *string `yaml:"source"` +< Verification Verification `yaml:"verification"` +< } +< +< type Device string +< +< type Directory struct { +< Group NodeGroup `yaml:"group"` +< Overwrite *bool `yaml:"overwrite"` +< Path string `yaml:"path"` +< User NodeUser `yaml:"user"` +< Mode *int `yaml:"mode"` +< } +< +< type Disk struct { +< Device string `yaml:"device"` +< Partitions []Partition `yaml:"partitions"` +< WipeTable *bool `yaml:"wipe_table"` +< } +< +< type Dropin struct { +< Contents *string `yaml:"contents"` +< Name string `yaml:"name"` +< } +< +< 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"` +< } +< +< 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"` +< } +< +< type FilesystemOption string +< +< type Group string +< +< type Ignition struct { +< Config IgnitionConfig `yaml:"config"` +< Security Security `yaml:"security"` +< Timeouts Timeouts `yaml:"timeouts"` +< } +< +< type IgnitionConfig struct { +< Merge []ConfigReference `yaml:"merge"` +< Replace ConfigReference `yaml:"replace"` +< } +< +< type Link struct { +< Group NodeGroup `yaml:"group"` +< Overwrite *bool `yaml:"overwrite"` +< Path string `yaml:"path"` +< User NodeUser `yaml:"user"` +< Hard *bool `yaml:"hard"` +< Target string `yaml:"target"` +< } +< +< type NodeGroup struct { +< ID *int `yaml:"id"` +< Name *string `yaml:"name"` +< } +< +< type NodeUser struct { +< ID *int `yaml:"id"` +< Name *string `yaml:"name"` +< } +< +< type Partition struct { +< GUID *string `yaml:"guid"` +< Label *string `yaml:"label"` +< Number int `yaml:"number"` +< ShouldExist *bool `yaml:"should_exist"` +< SizeMiB *int `yaml:"size_mib"` +< StartMiB *int `yaml:"start_mib"` +< TypeGUID *string `yaml:"type_guid"` +< WipePartitionEntry *bool `yaml:"wipe_partition_entry"` +< } +< +< type Passwd struct { +< Groups []PasswdGroup `yaml:"groups"` +< Users []PasswdUser `yaml:"users"` +< } +< +< type PasswdGroup struct { +< Gid *int `yaml:"gid"` +< Name string `yaml:"name"` +< PasswordHash *string `yaml:"password_hash"` +< System *bool `yaml:"system"` +< } +< +< type PasswdUser struct { +< Gecos *string `yaml:"gecos"` +< Groups []Group `yaml:"groups"` +< HomeDir *string `yaml:"home_dir"` +< Name string `yaml:"name"` +< NoCreateHome *bool `yaml:"no_create_home"` +< NoLogInit *bool `yaml:"no_log_init"` +< NoUserGroup *bool `yaml:"no_user_group"` +< PasswordHash *string `yaml:"password_hash"` +< PrimaryGroup *string `yaml:"primary_group"` +< SSHAuthorizedKeys []SSHAuthorizedKey `yaml:"ssh_authorized_keys"` +< Shell *string `yaml:"shell"` +< System *bool `yaml:"system"` +< UID *int `yaml:"uid"` +< } +< +< type Raid struct { +< Devices []Device `yaml:"devices"` +< Level string `yaml:"level"` +< Name string `yaml:"name"` +< Options []RaidOption `yaml:"options"` +< Spares *int `yaml:"spares"` +< } +< +< type RaidOption string +< +< type SSHAuthorizedKey string +< +< type Security struct { +< TLS TLS `yaml:"tls"` +< } +< +< type Storage struct { +< Directories []Directory `yaml:"directories"` +< Disks []Disk `yaml:"disks"` +< Files []File `yaml:"files"` +< Filesystems []Filesystem `yaml:"filesystems"` +< Links []Link `yaml:"links"` +< Raid []Raid `yaml:"raid"` +< } +< +< type Systemd struct { +< Units []Unit `yaml:"units"` +< } +< +< type TLS struct { +< CertificateAuthorities []CaReference `yaml:"certificate_authorities"` +< } +< +< type Timeouts struct { +< HTTPResponseHeaders *int `yaml:"http_response_headers"` +< HTTPTotal *int `yaml:"http_total"` +< } +< +< type Unit struct { +< Contents *string `yaml:"contents"` +< Dropins []Dropin `yaml:"dropins"` +< Enabled *bool `yaml:"enabled"` +< Mask *bool `yaml:"mask"` +< Name string `yaml:"name"` +< } +< +< type Verification struct { +< Hash *string `yaml:"hash"` +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_1/translate.go updated/vendor/github.com/coreos/fcct/base/v0_1/translate.go +1,113d0 +< // 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/config/common" +< "github.com/coreos/fcct/translate" +< +< "github.com/coreos/ignition/v2/config/v3_0/types" +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< "github.com/vincent-petithory/dataurl" +< ) +< +< // ToIgn3_0Unvalidated translates the config to an Ignition config. It also returns the set of translations +< // it did so paths in the resultant config can be tracked back to their source in the source config. +< // No config validation is performed on input or output. +< func (c Config) ToIgn3_0Unvalidated(options common.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) +< +< tm, r := translate.Prefixed(tr, "ignition", &c.Ignition, &ret.Ignition) +< translate.MergeP(tr, tm, &r, "passwd", &c.Passwd, &ret.Passwd) +< translate.MergeP(tr, tm, &r, "storage", &c.Storage, &ret.Storage) +< translate.MergeP(tr, tm, &r, "systemd", &c.Systemd, &ret.Systemd) +< +< if r.IsFatal() { +< return types.Config{}, translate.TranslationSet{}, r +< } +< return ret, tm, r +< } +< +< func translateIgnition(from Ignition, options common.TranslateOptions) (to types.Ignition, tm translate.TranslationSet, r report.Report) { +< tr := translate.NewTranslator("yaml", "json", options) +< to.Version = types.MaxVersion.String() +< tm, r = translate.Prefixed(tr, "config", &from.Config, &to.Config) +< 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 common.TranslateOptions) (to types.File, tm translate.TranslationSet, r report.Report) { +< tr := translate.NewTranslator("yaml", "json", options) +< tr.AddCustomTranslator(translateFileContents) +< 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 translateFileContents(from FileContents, options common.TranslateOptions) (to types.FileContents, tm translate.TranslationSet, r report.Report) { +< tr := translate.NewTranslator("yaml", "json", options) +< tm, r = translate.Prefixed(tr, "verification", &from.Verification, &to.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, options common.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 common.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 +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_1/translate_test.go updated/vendor/github.com/coreos/fcct/base/v0_1/translate_test.go +1,291d0 +< // 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 ( +< "testing" +< +< baseutil "github.com/coreos/fcct/base/util" +< "github.com/coreos/fcct/config/common" +< "github.com/coreos/fcct/translate" +< +< "github.com/coreos/ignition/v2/config/util" +< "github.com/coreos/ignition/v2/config/v3_0/types" +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< "github.com/stretchr/testify/assert" +< ) +< +< // Most of this is covered by the Ignition translator generic tests, so just test the custom bits +< +< // TestTranslateFile tests translating the ct storage.files.[i] entries to ignition storage.files.[i] entries. +< func TestTranslateFile(t *testing.T) { +< tests := []struct { +< in File +< out types.File +< exceptions []translate.Translation +< }{ +< { +< File{}, +< types.File{}, +< nil, +< }, +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< File{ +< Path: "/foo", +< Group: NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Mode: util.IntToPtr(420), +< Append: []FileContents{ +< { +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< { +< Inline: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< }, +< Overwrite: util.BoolToPtr(true), +< Contents: FileContents{ +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< Group: types.NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: types.NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Overwrite: util.BoolToPtr(true), +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Mode: util.IntToPtr(420), +< Append: []types.FileContents{ +< { +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< Verification: types.Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< { +< Source: util.StrToPtr("data:,hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: types.Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< }, +< Contents: types.FileContents{ +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< Verification: types.Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< { +< From: path.New("yaml", "append", 1, "inline"), +< To: path.New("json", "append", 1, "source"), +< }, +< }, +< }, +< } +< +< for i, test := range tests { +< actual, translations, r := translateFile(test.in, common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< baseutil.VerifyTranslations(t, translations, test.exceptions, "#%d", i) +< } +< } +< +< // TestTranslateDirectory tests translating the ct storage.directories.[i] entries to ignition storage.directories.[i] entires. +< func TestTranslateDirectory(t *testing.T) { +< tests := []struct { +< in Directory +< out types.Directory +< }{ +< { +< Directory{}, +< types.Directory{}, +< }, +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< Directory{ +< Path: "/foo", +< Group: NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Mode: util.IntToPtr(420), +< Overwrite: util.BoolToPtr(true), +< }, +< types.Directory{ +< Node: types.Node{ +< Path: "/foo", +< Group: types.NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: types.NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Overwrite: util.BoolToPtr(true), +< }, +< DirectoryEmbedded1: types.DirectoryEmbedded1{ +< Mode: util.IntToPtr(420), +< }, +< }, +< }, +< } +< +< for i, test := range tests { +< actual, _, r := translateDirectory(test.in, common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +< +< // TestTranslateLink tests translating the ct storage.links.[i] entries to ignition storage.links.[i] entires. +< func TestTranslateLink(t *testing.T) { +< tests := []struct { +< in Link +< out types.Link +< }{ +< { +< Link{}, +< types.Link{}, +< }, +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< Link{ +< Path: "/foo", +< Group: NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Overwrite: util.BoolToPtr(true), +< Target: "/bar", +< Hard: util.BoolToPtr(false), +< }, +< types.Link{ +< Node: types.Node{ +< Path: "/foo", +< Group: types.NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: types.NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Overwrite: util.BoolToPtr(true), +< }, +< LinkEmbedded1: types.LinkEmbedded1{ +< Target: "/bar", +< Hard: util.BoolToPtr(false), +< }, +< }, +< }, +< } +< +< for i, test := range tests { +< actual, _, r := translateLink(test.in, common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +< +< // TestTranslateIgnition tests translating the ct config.ignition to the ignition config.ignition section. +< // It ensure that the version is set as well. +< func TestTranslateIgnition(t *testing.T) { +< tests := []struct { +< in Ignition +< out types.Ignition +< }{ +< { +< Ignition{}, +< types.Ignition{ +< Version: "3.0.0", +< }, +< }, +< } +< for i, test := range tests { +< actual, _, r := translateIgnition(test.in, common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +< +< // TestToIgn3_0 tests the config.ToIgn3_0 function ensuring it will generate a valid config even when empty. Not much else is +< // tested since it uses the Ignition translation code which has it's own set of tests. +< func TestToIgn3_0(t *testing.T) { +< tests := []struct { +< in Config +< out types.Config +< }{ +< { +< Config{}, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.0.0", +< }, +< }, +< }, +< } +< for i, test := range tests { +< actual, _, r := test.in.ToIgn3_0Unvalidated(common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_1/validate.go updated/vendor/github.com/coreos/fcct/base/v0_1/validate.go +1,44d0 +< // 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 ( +< baseutil "github.com/coreos/fcct/base/util" +< "github.com/coreos/fcct/config/common" +< +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< ) +< +< func (f FileContents) Validate(c path.ContextPath) (r report.Report) { +< if f.Inline != nil && f.Source != nil { +< r.AddOnError(c.Append("inline"), common.ErrTooManyResourceSources) +< } +< return +< } +< +< func (d Directory) Validate(c path.ContextPath) (r report.Report) { +< if d.Mode != nil { +< r.AddOnWarn(c.Append("mode"), baseutil.CheckForDecimalMode(*d.Mode, true)) +< } +< return +< } +< +< func (f File) Validate(c path.ContextPath) (r report.Report) { +< if f.Mode != nil { +< r.AddOnWarn(c.Append("mode"), baseutil.CheckForDecimalMode(*f.Mode, false)) +< } +< return +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_1/validate_test.go updated/vendor/github.com/coreos/fcct/base/v0_1/validate_test.go +1,138d0 +< // 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 ( +< "testing" +< +< "github.com/coreos/fcct/config/common" +< +< "github.com/coreos/ignition/v2/config/util" +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< "github.com/stretchr/testify/assert" +< ) +< +< // TestValidateFileContents tests that multiple sources (i.e. urls and inline) are not allowed but zero or one sources are +< func TestValidateFileContents(t *testing.T) { +< tests := []struct { +< in FileContents +< out error +< }{ +< {}, +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< FileContents{ +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< nil, +< }, +< { +< FileContents{ +< Inline: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< nil, +< }, +< { +< FileContents{ +< Source: util.StrToPtr("data:,hello"), +< Inline: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< common.ErrTooManyResourceSources, +< }, +< } +< +< for i, test := range tests { +< actual := test.in.Validate(path.New("yaml")) +< expected := report.Report{} +< // hardcode inline for now since that's the only place errors occur. Move into the +< // test struct once there's more than one place +< expected.AddOnError(path.New("yaml", "inline"), test.out) +< assert.Equal(t, expected, actual, "#%d: bad report", i) +< } +< } +< +< func TestValidateMode(t *testing.T) { +< fileTests := []struct { +< in File +< out error +< }{ +< { +< in: File{}, +< out: nil, +< }, +< { +< in: File{ +< Mode: util.IntToPtr(0600), +< }, +< out: nil, +< }, +< { +< in: File{ +< Mode: util.IntToPtr(600), +< }, +< out: common.ErrDecimalMode, +< }, +< } +< +< for i, test := range fileTests { +< actual := test.in.Validate(path.New("yaml")) +< expected := report.Report{} +< expected.AddOnWarn(path.New("yaml", "mode"), test.out) +< assert.Equal(t, expected, actual, "#%d: bad report", i) +< } +< +< dirTests := []struct { +< in Directory +< out error +< }{ +< { +< in: Directory{}, +< out: nil, +< }, +< { +< in: Directory{ +< Mode: util.IntToPtr(01770), +< }, +< out: nil, +< }, +< { +< in: Directory{ +< Mode: util.IntToPtr(1770), +< }, +< out: common.ErrDecimalMode, +< }, +< } +< +< for i, test := range dirTests { +< actual := test.in.Validate(path.New("yaml")) +< expected := report.Report{} +< expected.AddOnWarn(path.New("yaml", "mode"), test.out) +< assert.Equal(t, expected, actual, "#%d: bad report", i) +< } +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_2/schema.go updated/vendor/github.com/coreos/fcct/base/v0_2/schema.go +1,219d0 +< // 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 +< +< type Config struct { +< Version string `yaml:"version"` +< Variant string `yaml:"variant"` +< Ignition Ignition `yaml:"ignition"` +< Passwd Passwd `yaml:"passwd"` +< Storage Storage `yaml:"storage"` +< Systemd Systemd `yaml:"systemd"` +< } +< +< type Device string +< +< type Directory struct { +< Group NodeGroup `yaml:"group"` +< Overwrite *bool `yaml:"overwrite"` +< Path string `yaml:"path"` +< User NodeUser `yaml:"user"` +< Mode *int `yaml:"mode"` +< } +< +< type Disk struct { +< Device string `yaml:"device"` +< Partitions []Partition `yaml:"partitions"` +< WipeTable *bool `yaml:"wipe_table"` +< } +< +< type Dropin struct { +< Contents *string `yaml:"contents"` +< Name string `yaml:"name"` +< } +< +< type File struct { +< 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"` +< 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 []Resource `yaml:"merge"` +< Replace Resource `yaml:"replace"` +< } +< +< type Link struct { +< Group NodeGroup `yaml:"group"` +< Overwrite *bool `yaml:"overwrite"` +< Path string `yaml:"path"` +< User NodeUser `yaml:"user"` +< Hard *bool `yaml:"hard"` +< Target string `yaml:"target"` +< } +< +< type NodeGroup struct { +< ID *int `yaml:"id"` +< Name *string `yaml:"name"` +< } +< +< type NodeUser struct { +< ID *int `yaml:"id"` +< Name *string `yaml:"name"` +< } +< +< type Partition struct { +< GUID *string `yaml:"guid"` +< Label *string `yaml:"label"` +< Number int `yaml:"number"` +< ShouldExist *bool `yaml:"should_exist"` +< SizeMiB *int `yaml:"size_mib"` +< StartMiB *int `yaml:"start_mib"` +< TypeGUID *string `yaml:"type_guid"` +< WipePartitionEntry *bool `yaml:"wipe_partition_entry"` +< } +< +< type Passwd struct { +< Groups []PasswdGroup `yaml:"groups"` +< Users []PasswdUser `yaml:"users"` +< } +< +< type PasswdGroup struct { +< Gid *int `yaml:"gid"` +< Name string `yaml:"name"` +< PasswordHash *string `yaml:"password_hash"` +< System *bool `yaml:"system"` +< } +< +< type PasswdUser struct { +< Gecos *string `yaml:"gecos"` +< Groups []Group `yaml:"groups"` +< HomeDir *string `yaml:"home_dir"` +< Name string `yaml:"name"` +< NoCreateHome *bool `yaml:"no_create_home"` +< NoLogInit *bool `yaml:"no_log_init"` +< NoUserGroup *bool `yaml:"no_user_group"` +< PasswordHash *string `yaml:"password_hash"` +< PrimaryGroup *string `yaml:"primary_group"` +< SSHAuthorizedKeys []SSHAuthorizedKey `yaml:"ssh_authorized_keys"` +< Shell *string `yaml:"shell"` +< System *bool `yaml:"system"` +< 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"` +< Name string `yaml:"name"` +< Options []RaidOption `yaml:"options"` +< Spares *int `yaml:"spares"` +< } +< +< 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 { +< TLS TLS `yaml:"tls"` +< } +< +< type Storage struct { +< Directories []Directory `yaml:"directories"` +< Disks []Disk `yaml:"disks"` +< Files []File `yaml:"files"` +< 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 { +< Units []Unit `yaml:"units"` +< } +< +< type TLS struct { +< CertificateAuthorities []Resource `yaml:"certificate_authorities"` +< } +< +< type Timeouts struct { +< HTTPResponseHeaders *int `yaml:"http_response_headers"` +< 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"` +< Enabled *bool `yaml:"enabled"` +< Mask *bool `yaml:"mask"` +< Name string `yaml:"name"` +< } +< +< type Verification struct { +< Hash *string `yaml:"hash"` +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_2/translate.go updated/vendor/github.com/coreos/fcct/base/v0_2/translate.go +1,373d0 +< // 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 ( +< "io/ioutil" +< "os" +< "path/filepath" +< "strings" +< "text/template" +< +< baseutil "github.com/coreos/fcct/base/util" +< "github.com/coreos/fcct/config/common" +< "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" +< ) +< +< var ( +< mountUnitTemplate = template.Must(template.New("unit").Parse(`# Generated by FCCT +< [Unit] +< Before=local-fs.target +< Requires=systemd-fsck@{{.EscapedDevice}}.service +< After=systemd-fsck@{{.EscapedDevice}}.service +< +< [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_1Unvalidated translates the config to an Ignition config. It also returns the set of translations +< // it did so paths in the resultant config can be tracked back to their source in the source config. +< // No config validation is performed on input or output. +< func (c Config) ToIgn3_1Unvalidated(options common.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) +< +< tm, r := translate.Prefixed(tr, "ignition", &c.Ignition, &ret.Ignition) +< translate.MergeP(tr, tm, &r, "passwd", &c.Passwd, &ret.Passwd) +< translate.MergeP(tr, tm, &r, "storage", &c.Storage, &ret.Storage) +< translate.MergeP(tr, tm, &r, "systemd", &c.Systemd, &ret.Systemd) +< +< c.addMountUnits(&ret, &tm) +< +< tm2, r2 := c.processTrees(&ret, options) +< tm.Merge(tm2) +< r.Merge(r2) +< +< if r.IsFatal() { +< return types.Config{}, translate.TranslationSet{}, r +< } +< return ret, tm, r +< } +< +< func translateIgnition(from Ignition, options common.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 common.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 common.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, common.ErrNoFilesDir) +< return +< } +< +< // calculate file path within FilesDir and check for +< // path traversal +< filePath := filepath.Join(options.FilesDir, *from.Local) +< if err := baseutil.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 := baseutil.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 := baseutil.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 translateDirectory(from Directory, options common.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 common.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 common.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, common.ErrNoFilesDir) +< return ts, r +< } +< +< // calculate base path within FilesDir and check for +< // path traversal +< srcBaseDir := filepath.Join(options.FilesDir, tree.Local) +< if err := baseutil.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, common.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 common.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, common.ErrNodeExists) +< return nil +< } +< } else { +< if t.Exists(destPath) { +< r.AddOnError(yamlPath, common.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 := baseutil.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, common.ErrNodeExists) +< return nil +< } +< } else { +< if t.Exists(destPath) { +< r.AddOnError(yamlPath, common.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, common.ErrFileType) +< return nil +< } +< return nil +< }) +< r.AddOnError(yamlPath, err) +< } +< +< func (c Config) addMountUnits(config *types.Config, ts *translate.TranslationSet) { +< if len(c.Storage.Filesystems) == 0 { +< return +< } +< var rendered types.Config +< renderedTranslations := translate.NewTranslationSet("yaml", "json") +< 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) +< unitPath := path.New("json", "systemd", "units", len(rendered.Systemd.Units)) +< rendered.Systemd.Units = append(rendered.Systemd.Units, newUnit) +< renderedTranslations.AddFromCommonSource(fromPath, unitPath, newUnit) +< } +< retConfig, retTranslations := baseutil.MergeTranslatedConfigs(rendered, renderedTranslations, *config, *ts) +< *config = retConfig.(types.Config) +< *ts = retTranslations +< } +< +< func mountUnitFromFS(fs Filesystem) types.Unit { +< context := struct { +< *Filesystem +< EscapedDevice string +< }{ +< Filesystem: &fs, +< EscapedDevice: unit.UnitNamePathEscape(fs.Device), +< } +< contents := strings.Builder{} +< err := mountUnitTemplate.Execute(&contents, context) +< 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()), +< } +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_2/translate_test.go updated/vendor/github.com/coreos/fcct/base/v0_2/translate_test.go +1,1358d0 +< // 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 ( +< "io/ioutil" +< "net" +< "os" +< "path/filepath" +< "strings" +< "testing" +< +< baseutil "github.com/coreos/fcct/base/util" +< "github.com/coreos/fcct/config/common" +< "github.com/coreos/fcct/translate" +< +< "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/stretchr/testify/assert" +< ) +< +< // Most of this is covered by the Ignition translator generic tests, so just test the custom bits +< +< // TestTranslateFile tests translating the ct storage.files.[i] entries to ignition storage.files.[i] entries. +< func TestTranslateFile(t *testing.T) { +< zzz := "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" +< zzz_gz := "data:;base64,H4sIAAAAAAAC/6oajAAQAAD//5tA8d+VAAAA" +< random := "\xc0\x9cl\x01\x89i\xa5\xbfW\xe4\x1b\xf4J_\xb79P\xa3#\xa7" +< random_b64 := "data:;base64,wJxsAYlppb9X5Bv0Sl+3OVCjI6c=" +< +< filesDir, err := ioutil.TempDir("", "translate-test-") +< if err != nil { +< t.Error(err) +< return +< } +< defer os.RemoveAll(filesDir) +< fileContents := map[string]string{ +< "file-1": "file contents\n", +< "file-2": zzz, +< "file-3": random, +< } +< for name, contents := range fileContents { +< err := ioutil.WriteFile(filepath.Join(filesDir, name), []byte(contents), 0644) +< if err != nil { +< t.Error(err) +< return +< } +< } +< +< tests := []struct { +< in File +< out types.File +< exceptions []translate.Translation +< report string +< options common.TranslateOptions +< }{ +< { +< File{}, +< types.File{}, +< nil, +< "", +< common.TranslateOptions{}, +< }, +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< File{ +< Path: "/foo", +< Group: NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Mode: util.IntToPtr(420), +< Append: []Resource{ +< { +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< HTTPHeaders: HTTPHeaders{ +< HTTPHeader{ +< Name: "Header", +< Value: util.StrToPtr("this isn't validated"), +< }, +< }, +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< { +< Inline: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< HTTPHeaders: HTTPHeaders{ +< HTTPHeader{ +< Name: "Header", +< Value: util.StrToPtr("this isn't validated"), +< }, +< }, +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< { +< Local: util.StrToPtr("file-1"), +< }, +< }, +< Overwrite: util.BoolToPtr(true), +< Contents: Resource{ +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< HTTPHeaders: HTTPHeaders{ +< HTTPHeader{ +< Name: "Header", +< Value: util.StrToPtr("this isn't validated"), +< }, +< }, +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< Group: types.NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: types.NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Overwrite: util.BoolToPtr(true), +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Mode: util.IntToPtr(420), +< Append: []types.Resource{ +< { +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< HTTPHeaders: types.HTTPHeaders{ +< types.HTTPHeader{ +< Name: "Header", +< Value: util.StrToPtr("this isn't validated"), +< }, +< }, +< Verification: types.Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< { +< Source: util.StrToPtr("data:,hello"), +< Compression: util.StrToPtr("gzip"), +< HTTPHeaders: types.HTTPHeaders{ +< types.HTTPHeader{ +< Name: "Header", +< Value: util.StrToPtr("this isn't validated"), +< }, +< }, +< Verification: types.Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< { +< Source: util.StrToPtr("data:,file%20contents%0A"), +< }, +< }, +< Contents: types.Resource{ +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< HTTPHeaders: types.HTTPHeaders{ +< types.HTTPHeader{ +< Name: "Header", +< Value: util.StrToPtr("this isn't validated"), +< }, +< }, +< Verification: types.Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< { +< From: path.New("yaml", "append", 1, "inline"), +< To: path.New("json", "append", 1, "source"), +< }, +< { +< From: path.New("yaml", "append", 2, "local"), +< To: path.New("json", "append", 2, "source"), +< }, +< }, +< "", +< common.TranslateOptions{ +< FilesDir: filesDir, +< }, +< }, +< // inline file contents +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< // String is too short for auto gzip compression +< Inline: util.StrToPtr("xyzzy"), +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,xyzzy"), +< }, +< }, +< }, +< []translate.Translation{ +< { +< From: path.New("yaml", "contents", "inline"), +< To: path.New("json", "contents", "source"), +< }, +< }, +< "", +< common.TranslateOptions{}, +< }, +< // local file contents +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< Local: util.StrToPtr("file-1"), +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,file%20contents%0A"), +< }, +< }, +< }, +< []translate.Translation{ +< { +< From: path.New("yaml", "contents", "local"), +< To: path.New("json", "contents", "source"), +< }, +< }, +< "", +< common.TranslateOptions{ +< FilesDir: filesDir, +< }, +< }, +< // filesDir not specified +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< Local: util.StrToPtr("file-1"), +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< }, +< []translate.Translation{}, +< "error at $.contents.local: " + common.ErrNoFilesDir.Error() + "\n", +< common.TranslateOptions{}, +< }, +< // attempted directory traversal +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< Local: util.StrToPtr("../file-1"), +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< }, +< []translate.Translation{}, +< "error at $.contents.local: " + common.ErrFilesDirEscape.Error() + "\n", +< common.TranslateOptions{ +< FilesDir: filesDir, +< }, +< }, +< // attempted inclusion of nonexistent file +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< Local: util.StrToPtr("file-missing"), +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< }, +< []translate.Translation{}, +< "error at $.contents.local: open " + filepath.Join(filesDir, "file-missing") + ": no such file or directory\n", +< common.TranslateOptions{ +< FilesDir: filesDir, +< }, +< }, +< // inline and local automatic file encoding +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< // gzip +< Inline: util.StrToPtr(zzz), +< }, +< Append: []Resource{ +< { +< // gzip +< Local: util.StrToPtr("file-2"), +< }, +< { +< // base64 +< Inline: util.StrToPtr(random), +< }, +< { +< // base64 +< Local: util.StrToPtr("file-3"), +< }, +< { +< // URL-escaped +< Inline: util.StrToPtr(zzz), +< Compression: util.StrToPtr("invalid"), +< }, +< { +< // URL-escaped +< Local: util.StrToPtr("file-2"), +< Compression: util.StrToPtr("invalid"), +< }, +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr(zzz_gz), +< Compression: util.StrToPtr("gzip"), +< }, +< Append: []types.Resource{ +< { +< Source: util.StrToPtr(zzz_gz), +< Compression: util.StrToPtr("gzip"), +< }, +< { +< Source: util.StrToPtr(random_b64), +< }, +< { +< Source: util.StrToPtr(random_b64), +< }, +< { +< Source: util.StrToPtr("data:," + zzz), +< Compression: util.StrToPtr("invalid"), +< }, +< { +< Source: util.StrToPtr("data:," + zzz), +< Compression: util.StrToPtr("invalid"), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< { +< From: path.New("yaml", "contents", "inline"), +< To: path.New("json", "contents", "source"), +< }, +< { +< From: path.New("yaml", "contents", "inline"), +< To: path.New("json", "contents", "compression"), +< }, +< { +< From: path.New("yaml", "append", 0, "local"), +< To: path.New("json", "append", 0, "source"), +< }, +< { +< From: path.New("yaml", "append", 0, "local"), +< To: path.New("json", "append", 0, "compression"), +< }, +< { +< From: path.New("yaml", "append", 1, "inline"), +< To: path.New("json", "append", 1, "source"), +< }, +< { +< From: path.New("yaml", "append", 2, "local"), +< To: path.New("json", "append", 2, "source"), +< }, +< { +< From: path.New("yaml", "append", 3, "inline"), +< To: path.New("json", "append", 3, "source"), +< }, +< { +< From: path.New("yaml", "append", 4, "local"), +< To: path.New("json", "append", 4, "source"), +< }, +< }, +< "", +< common.TranslateOptions{ +< FilesDir: filesDir, +< }, +< }, +< // Test disable automatic gzip compression +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< Inline: util.StrToPtr(zzz), +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:," + zzz), +< }, +< }, +< }, +< []translate.Translation{ +< { +< From: path.New("yaml", "contents", "inline"), +< To: path.New("json", "contents", "source"), +< }, +< }, +< "", +< common.TranslateOptions{ +< NoResourceAutoCompression: true, +< }, +< }, +< } +< +< for i, test := range tests { +< actual, translations, r := translateFile(test.in, test.options) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, test.report, r.String(), "#%d: bad report", i) +< baseutil.VerifyTranslations(t, translations, test.exceptions, "#%d", i) +< } +< } +< +< // TestTranslateDirectory tests translating the ct storage.directories.[i] entries to ignition storage.directories.[i] entires. +< func TestTranslateDirectory(t *testing.T) { +< tests := []struct { +< in Directory +< out types.Directory +< }{ +< { +< Directory{}, +< types.Directory{}, +< }, +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< Directory{ +< Path: "/foo", +< Group: NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Mode: util.IntToPtr(420), +< Overwrite: util.BoolToPtr(true), +< }, +< types.Directory{ +< Node: types.Node{ +< Path: "/foo", +< Group: types.NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: types.NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Overwrite: util.BoolToPtr(true), +< }, +< DirectoryEmbedded1: types.DirectoryEmbedded1{ +< Mode: util.IntToPtr(420), +< }, +< }, +< }, +< } +< +< for i, test := range tests { +< actual, _, r := translateDirectory(test.in, common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +< +< // TestTranslateLink tests translating the ct storage.links.[i] entries to ignition storage.links.[i] entires. +< func TestTranslateLink(t *testing.T) { +< tests := []struct { +< in Link +< out types.Link +< }{ +< { +< Link{}, +< types.Link{}, +< }, +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< Link{ +< Path: "/foo", +< Group: NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Overwrite: util.BoolToPtr(true), +< Target: "/bar", +< Hard: util.BoolToPtr(false), +< }, +< types.Link{ +< Node: types.Node{ +< Path: "/foo", +< Group: types.NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: types.NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Overwrite: util.BoolToPtr(true), +< }, +< LinkEmbedded1: types.LinkEmbedded1{ +< Target: "/bar", +< Hard: util.BoolToPtr(false), +< }, +< }, +< }, +< } +< +< for i, test := range tests { +< actual, _, r := translateLink(test.in, common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +< +< // TestTranslateFilesystem tests translating the fcct storage.filesystems.[i] entries to ignition storage.filesystems.[i] entries. +< func TestTranslateFilesystem(t *testing.T) { +< tests := []struct { +< in Filesystem +< out types.Filesystem +< }{ +< { +< Filesystem{}, +< types.Filesystem{}, +< }, +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< Filesystem{ +< Device: "/foo", +< Format: util.StrToPtr("/bar"), +< Label: util.StrToPtr("/baz"), +< MountOptions: []string{"yes", "no", "maybe"}, +< Options: []string{"foo", "foo", "bar"}, +< Path: util.StrToPtr("/quux"), +< UUID: util.StrToPtr("1234"), +< WipeFilesystem: util.BoolToPtr(true), +< WithMountUnit: util.BoolToPtr(true), +< }, +< types.Filesystem{ +< Device: "/foo", +< Format: util.StrToPtr("/bar"), +< Label: util.StrToPtr("/baz"), +< MountOptions: []types.MountOption{"yes", "no", "maybe"}, +< Options: []types.FilesystemOption{"foo", "foo", "bar"}, +< Path: util.StrToPtr("/quux"), +< UUID: util.StrToPtr("1234"), +< WipeFilesystem: util.BoolToPtr(true), +< }, +< }, +< } +< +< for i, test := range tests { +< // Filesystem doesn't have a custom translator, so embed in a +< // complete config +< in := Config{ +< Storage: Storage{ +< Filesystems: []Filesystem{test.in}, +< }, +< } +< expected := []types.Filesystem{test.out} +< actual, _, r := in.ToIgn3_1Unvalidated(common.TranslateOptions{}) +< assert.Equal(t, expected, actual.Storage.Filesystems, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +< +< // TestTranslateMountUnit tests the FCCT storage.filesystems.[i].with_mount_unit flag. +< func TestTranslateMountUnit(t *testing.T) { +< tests := []struct { +< in Config +< out types.Config +< }{ +< // local mount with options, overridden enabled flag +< { +< Config{ +< Storage: Storage{ +< Filesystems: []Filesystem{ +< { +< Device: "/dev/disk/by-label/foo", +< Format: util.StrToPtr("ext4"), +< MountOptions: []string{"ro", "noatime"}, +< Path: util.StrToPtr("/var/lib/containers"), +< WithMountUnit: util.BoolToPtr(true), +< }, +< }, +< }, +< Systemd: Systemd{ +< Units: []Unit{ +< { +< Name: "var-lib-containers.mount", +< Enabled: util.BoolToPtr(false), +< }, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.1.0", +< }, +< Storage: types.Storage{ +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/disk/by-label/foo", +< Format: util.StrToPtr("ext4"), +< MountOptions: []types.MountOption{"ro", "noatime"}, +< Path: util.StrToPtr("/var/lib/containers"), +< }, +< }, +< }, +< Systemd: types.Systemd{ +< Units: []types.Unit{ +< { +< Enabled: util.BoolToPtr(false), +< Contents: util.StrToPtr(`# Generated by FCCT +< [Unit] +< Before=local-fs.target +< Requires=systemd-fsck@dev-disk-by\x2dlabel-foo.service +< After=systemd-fsck@dev-disk-by\x2dlabel-foo.service +< +< [Mount] +< Where=/var/lib/containers +< What=/dev/disk/by-label/foo +< Type=ext4 +< Options=ro,noatime +< +< [Install] +< RequiredBy=local-fs.target`), +< Name: "var-lib-containers.mount", +< }, +< }, +< }, +< }, +< }, +< // local mount, no options +< { +< Config{ +< Storage: Storage{ +< Filesystems: []Filesystem{ +< { +< Device: "/dev/disk/by-label/foo", +< Format: util.StrToPtr("ext4"), +< Path: util.StrToPtr("/var/lib/containers"), +< WithMountUnit: util.BoolToPtr(true), +< }, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.1.0", +< }, +< Storage: types.Storage{ +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/disk/by-label/foo", +< Format: util.StrToPtr("ext4"), +< Path: util.StrToPtr("/var/lib/containers"), +< }, +< }, +< }, +< Systemd: types.Systemd{ +< Units: []types.Unit{ +< { +< Enabled: util.BoolToPtr(true), +< Contents: util.StrToPtr(`# Generated by FCCT +< [Unit] +< Before=local-fs.target +< Requires=systemd-fsck@dev-disk-by\x2dlabel-foo.service +< After=systemd-fsck@dev-disk-by\x2dlabel-foo.service +< +< [Mount] +< Where=/var/lib/containers +< What=/dev/disk/by-label/foo +< Type=ext4 +< +< [Install] +< RequiredBy=local-fs.target`), +< Name: "var-lib-containers.mount", +< }, +< }, +< }, +< }, +< }, +< // overridden mount unit +< { +< Config{ +< Storage: Storage{ +< Filesystems: []Filesystem{ +< { +< Device: "/dev/disk/by-label/foo", +< Format: util.StrToPtr("ext4"), +< Path: util.StrToPtr("/var/lib/containers"), +< WithMountUnit: util.BoolToPtr(true), +< }, +< }, +< }, +< Systemd: Systemd{ +< Units: []Unit{ +< { +< Name: "var-lib-containers.mount", +< Contents: util.StrToPtr("[Service]\nExecStart=/bin/false\n"), +< }, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.1.0", +< }, +< Storage: types.Storage{ +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/disk/by-label/foo", +< Format: util.StrToPtr("ext4"), +< Path: util.StrToPtr("/var/lib/containers"), +< }, +< }, +< }, +< Systemd: types.Systemd{ +< Units: []types.Unit{ +< { +< Enabled: util.BoolToPtr(true), +< Contents: util.StrToPtr("[Service]\nExecStart=/bin/false\n"), +< Name: "var-lib-containers.mount", +< }, +< }, +< }, +< }, +< }, +< } +< +< for i, test := range tests { +< out, _, r := test.in.ToIgn3_1Unvalidated(common.TranslateOptions{}) +< assert.Equal(t, test.out, out, "#%d: bad output", i) +< assert.Equal(t, report.Report{}, r, "#%d: expected empty report", i) +< } +< } +< +< // TestTranslateTree tests translating the FCC storage.trees.[i] entries to ignition storage.files.[i] entries. +< func TestTranslateTree(t *testing.T) { +< tests := []struct { +< options *common.TranslateOptions // defaulted if not specified +< dirDirs map[string]os.FileMode // relative path -> mode +< dirFiles map[string]os.FileMode // relative path -> mode +< dirLinks map[string]string // relative path -> target +< dirSockets []string // relative path +< inTrees []Tree +< inFiles []File +< inDirs []Directory +< inLinks []Link +< outFiles []types.File +< outLinks []types.Link +< report string +< }{ +< // smoke test +< {}, +< // basic functionality +< { +< dirFiles: map[string]os.FileMode{ +< "tree/executable": 0700, +< "tree/file": 0600, +< "tree/overridden": 0644, +< "tree/overridden-executable": 0700, +< "tree/subdir/file": 0644, +< // compressed contents +< "tree/subdir/subdir/subdir/subdir/subdir/subdir/subdir/subdir/subdir/file": 0644, +< "tree2/file": 0600, +< }, +< dirLinks: map[string]string{ +< "tree/subdir/bad-link": "../nonexistent", +< "tree/subdir/link": "../file", +< "tree/subdir/overridden-link": "../file", +< }, +< inTrees: []Tree{ +< { +< Local: "tree", +< }, +< { +< Local: "tree2", +< Path: util.StrToPtr("/etc"), +< }, +< }, +< inFiles: []File{ +< { +< Path: "/overridden", +< Mode: util.IntToPtr(0600), +< User: NodeUser{ +< Name: util.StrToPtr("bovik"), +< }, +< }, +< { +< Path: "/overridden-executable", +< Mode: util.IntToPtr(0600), +< User: NodeUser{ +< Name: util.StrToPtr("bovik"), +< }, +< }, +< }, +< inLinks: []Link{ +< { +< Path: "/subdir/overridden-link", +< User: NodeUser{ +< Name: util.StrToPtr("bovik"), +< }, +< }, +< }, +< outFiles: []types.File{ +< { +< Node: types.Node{ +< Path: "/overridden", +< User: types.NodeUser{ +< Name: util.StrToPtr("bovik"), +< }, +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,tree%2Foverridden"), +< }, +< Mode: util.IntToPtr(0600), +< }, +< }, +< { +< Node: types.Node{ +< Path: "/overridden-executable", +< User: types.NodeUser{ +< Name: util.StrToPtr("bovik"), +< }, +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,tree%2Foverridden-executable"), +< }, +< Mode: util.IntToPtr(0600), +< }, +< }, +< { +< Node: types.Node{ +< Path: "/executable", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,tree%2Fexecutable"), +< }, +< Mode: util.IntToPtr(0755), +< }, +< }, +< { +< Node: types.Node{ +< Path: "/file", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,tree%2Ffile"), +< }, +< Mode: util.IntToPtr(0644), +< }, +< }, +< { +< Node: types.Node{ +< Path: "/subdir/file", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,tree%2Fsubdir%2Ffile"), +< }, +< Mode: util.IntToPtr(0644), +< }, +< }, +< { +< Node: types.Node{ +< Path: "/subdir/subdir/subdir/subdir/subdir/subdir/subdir/subdir/subdir/file", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:;base64,H4sIAAAAAAAC/yopSk3VLy5NSsksIptKy8xJBQQAAP//gkRzjkgAAAA="), +< Compression: util.StrToPtr("gzip"), +< }, +< Mode: util.IntToPtr(0644), +< }, +< }, +< { +< Node: types.Node{ +< Path: "/etc/file", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,tree2%2Ffile"), +< }, +< Mode: util.IntToPtr(0644), +< }, +< }, +< }, +< outLinks: []types.Link{ +< { +< Node: types.Node{ +< Path: "/subdir/overridden-link", +< User: types.NodeUser{ +< Name: util.StrToPtr("bovik"), +< }, +< }, +< LinkEmbedded1: types.LinkEmbedded1{ +< Target: "../file", +< }, +< }, +< { +< Node: types.Node{ +< Path: "/subdir/bad-link", +< }, +< LinkEmbedded1: types.LinkEmbedded1{ +< Target: "../nonexistent", +< }, +< }, +< { +< Node: types.Node{ +< Path: "/subdir/link", +< }, +< LinkEmbedded1: types.LinkEmbedded1{ +< Target: "../file", +< }, +< }, +< }, +< }, +< // collisions +< { +< dirFiles: map[string]os.FileMode{ +< "tree0/file": 0600, +< "tree1/directory": 0600, +< "tree2/link": 0600, +< "tree3/file-partial": 0600, // should be okay +< "tree4/link-partial": 0600, +< "tree5/tree-file": 0600, // set up for tree/tree collision +< "tree6/tree-file": 0600, +< "tree15/tree-link": 0600, +< }, +< dirLinks: map[string]string{ +< "tree7/file": "file", +< "tree8/directory": "file", +< "tree9/link": "file", +< "tree10/file-partial": "file", +< "tree11/link-partial": "file", // should be okay +< "tree12/tree-file": "file", +< "tree13/tree-link": "file", // set up for tree/tree collision +< "tree14/tree-link": "file", +< }, +< inTrees: []Tree{ +< { +< Local: "tree0", +< }, +< { +< Local: "tree1", +< }, +< { +< Local: "tree2", +< }, +< { +< Local: "tree3", +< }, +< { +< Local: "tree4", +< }, +< { +< Local: "tree5", +< }, +< { +< Local: "tree6", +< }, +< { +< Local: "tree7", +< }, +< { +< Local: "tree8", +< }, +< { +< Local: "tree9", +< }, +< { +< Local: "tree10", +< }, +< { +< Local: "tree11", +< }, +< { +< Local: "tree12", +< }, +< { +< Local: "tree13", +< }, +< { +< Local: "tree14", +< }, +< { +< Local: "tree15", +< }, +< }, +< inFiles: []File{ +< { +< Path: "/file", +< Contents: Resource{ +< Source: util.StrToPtr("data:,foo"), +< }, +< }, +< { +< Path: "/file-partial", +< }, +< }, +< inDirs: []Directory{ +< { +< Path: "/directory", +< }, +< }, +< inLinks: []Link{ +< { +< Path: "/link", +< Target: "file", +< }, +< { +< Path: "/link-partial", +< }, +< }, +< report: "error at $.storage.trees.0: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.1: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.2: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.4: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.6: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.7: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.8: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.9: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.10: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.12: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.14: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.15: " + common.ErrNodeExists.Error() + "\n", +< }, +< // files-dir escape +< { +< inTrees: []Tree{ +< { +< Local: "../escape", +< }, +< }, +< report: "error at $.storage.trees.0: " + common.ErrFilesDirEscape.Error() + "\n", +< }, +< // no files-dir +< { +< options: &common.TranslateOptions{}, +< inTrees: []Tree{ +< { +< Local: "tree", +< }, +< }, +< report: "error at $.storage.trees.0: " + common.ErrNoFilesDir.Error() + "\n", +< }, +< // non-file/dir/symlink in directory tree +< { +< dirSockets: []string{ +< "tree/socket", +< }, +< inTrees: []Tree{ +< { +< Local: "tree", +< }, +< }, +< report: "error at $.storage.trees.0: " + common.ErrFileType.Error() + "\n", +< }, +< // unreadable file +< { +< dirDirs: map[string]os.FileMode{ +< "tree/subdir": 0000, +< "tree2": 0000, +< }, +< dirFiles: map[string]os.FileMode{ +< "tree/file": 0000, +< }, +< inTrees: []Tree{ +< { +< Local: "tree", +< }, +< { +< Local: "tree2", +< }, +< }, +< report: "error at $.storage.trees.0: open %FilesDir%/tree/file: permission denied\n" + +< "error at $.storage.trees.0: open %FilesDir%/tree/subdir: permission denied\n" + +< "error at $.storage.trees.1: open %FilesDir%/tree2: permission denied\n", +< }, +< // local is not a directory +< { +< dirFiles: map[string]os.FileMode{ +< "tree": 0600, +< }, +< inTrees: []Tree{ +< { +< Local: "tree", +< }, +< { +< Local: "nonexistent", +< }, +< }, +< report: "error at $.storage.trees.0: " + common.ErrTreeNotDirectory.Error() + "\n" + +< "error at $.storage.trees.1: stat %FilesDir%/nonexistent: no such file or directory\n", +< }, +< } +< +< for i, test := range tests { +< filesDir, err := ioutil.TempDir("", "translate-test-") +< if err != nil { +< t.Error(err) +< return +< } +< defer os.RemoveAll(filesDir) +< for path, mode := range test.dirDirs { +< absPath := filepath.Join(filesDir, path) +< if err := os.MkdirAll(absPath, 0755); err != nil { +< t.Error(err) +< return +< } +< if err := os.Chmod(absPath, mode); err != nil { +< t.Error(err) +< return +< } +< } +< for path, mode := range test.dirFiles { +< absPath := filepath.Join(filesDir, path) +< if err := os.MkdirAll(filepath.Dir(absPath), 0755); err != nil { +< t.Error(err) +< return +< } +< if err := ioutil.WriteFile(absPath, []byte(path), mode); err != nil { +< t.Error(err) +< return +< } +< } +< for path, target := range test.dirLinks { +< absPath := filepath.Join(filesDir, path) +< if err := os.MkdirAll(filepath.Dir(absPath), 0755); err != nil { +< t.Error(err) +< return +< } +< if err := os.Symlink(target, absPath); err != nil { +< t.Error(err) +< return +< } +< } +< for _, path := range test.dirSockets { +< absPath := filepath.Join(filesDir, path) +< if err := os.MkdirAll(filepath.Dir(absPath), 0755); err != nil { +< t.Error(err) +< return +< } +< listener, err := net.ListenUnix("unix", &net.UnixAddr{ +< Name: absPath, +< Net: "unix", +< }) +< if err != nil { +< t.Error(err) +< return +< } +< defer listener.Close() +< } +< +< config := Config{ +< Storage: Storage{ +< Files: test.inFiles, +< Directories: test.inDirs, +< Links: test.inLinks, +< Trees: test.inTrees, +< }, +< } +< options := common.TranslateOptions{ +< FilesDir: filesDir, +< } +< if test.options != nil { +< options = *test.options +< } +< actual, _, r := config.ToIgn3_1Unvalidated(options) +< +< expectedReport := strings.ReplaceAll(test.report, "%FilesDir%", filesDir) +< assert.Equal(t, expectedReport, r.String(), "#%d: bad report", i) +< if expectedReport != "" { +< continue +< } +< +< assert.Equal(t, test.outFiles, actual.Storage.Files, "#%d: files mismatch", i) +< assert.Equal(t, []types.Directory(nil), actual.Storage.Directories, "#%d: directories mismatch", i) +< assert.Equal(t, test.outLinks, actual.Storage.Links, "#%d: links mismatch", i) +< } +< } +< +< // TestTranslateIgnition tests translating the ct config.ignition to the ignition config.ignition section. +< // It ensure that the version is set as well. +< func TestTranslateIgnition(t *testing.T) { +< tests := []struct { +< in Ignition +< out types.Ignition +< }{ +< { +< Ignition{}, +< types.Ignition{ +< Version: "3.1.0", +< }, +< }, +< { +< Ignition{ +< Config: IgnitionConfig{ +< Merge: []Resource{ +< { +< Inline: util.StrToPtr("xyzzy"), +< }, +< }, +< Replace: Resource{ +< Inline: util.StrToPtr("xyzzy"), +< }, +< }, +< }, +< types.Ignition{ +< Version: "3.1.0", +< Config: types.IgnitionConfig{ +< Merge: []types.Resource{ +< { +< Source: util.StrToPtr("data:,xyzzy"), +< }, +< }, +< Replace: types.Resource{ +< Source: util.StrToPtr("data:,xyzzy"), +< }, +< }, +< }, +< }, +< { +< Ignition{ +< Proxy: Proxy{ +< HTTPProxy: util.StrToPtr("https://example.com:8080"), +< NoProxy: []string{"example.com"}, +< }, +< }, +< types.Ignition{ +< Version: "3.1.0", +< Proxy: types.Proxy{ +< HTTPProxy: util.StrToPtr("https://example.com:8080"), +< NoProxy: []types.NoProxyItem{types.NoProxyItem("example.com")}, +< }, +< }, +< }, +< { +< Ignition{ +< Security: Security{ +< TLS: TLS{ +< CertificateAuthorities: []Resource{ +< { +< Inline: util.StrToPtr("xyzzy"), +< }, +< }, +< }, +< }, +< }, +< types.Ignition{ +< Version: "3.1.0", +< Security: types.Security{ +< TLS: types.TLS{ +< CertificateAuthorities: []types.Resource{ +< { +< Source: util.StrToPtr("data:,xyzzy"), +< }, +< }, +< }, +< }, +< }, +< }, +< } +< for i, test := range tests { +< actual, _, r := translateIgnition(test.in, common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +< +< // TestToIgn3_1 tests the config.ToIgn3_1 function ensuring it will generate a valid config even when empty. Not much else is +< // tested since it uses the Ignition translation code which has it's own set of tests. +< func TestToIgn3_1(t *testing.T) { +< tests := []struct { +< in Config +< out types.Config +< }{ +< { +< Config{}, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.1.0", +< }, +< }, +< }, +< } +< for i, test := range tests { +< actual, _, r := test.in.ToIgn3_1Unvalidated(common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_2/util.go updated/vendor/github.com/coreos/fcct/base/v0_2/util.go +1,125d0 +< // 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 --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_2/validate.go updated/vendor/github.com/coreos/fcct/base/v0_2/validate.go +1,78d0 +< // 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 ( +< baseutil "github.com/coreos/fcct/base/util" +< "github.com/coreos/fcct/config/common" +< +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< ) +< +< 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), common.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"), common.ErrMountUnitNoPath) +< } +< if fs.Format == nil || *fs.Format == "" { +< r.AddOnError(c.Append("format"), common.ErrMountUnitNoFormat) +< } +< return +< } +< +< func (d Directory) Validate(c path.ContextPath) (r report.Report) { +< if d.Mode != nil { +< r.AddOnWarn(c.Append("mode"), baseutil.CheckForDecimalMode(*d.Mode, true)) +< } +< return +< } +< +< func (f File) Validate(c path.ContextPath) (r report.Report) { +< if f.Mode != nil { +< r.AddOnWarn(c.Append("mode"), baseutil.CheckForDecimalMode(*f.Mode, false)) +< } +< return +< } +< +< func (t Tree) Validate(c path.ContextPath) (r report.Report) { +< if t.Local == "" { +< r.AddOnError(c, common.ErrTreeNoLocal) +< } +< return +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_2/validate_test.go updated/vendor/github.com/coreos/fcct/base/v0_2/validate_test.go +1,214d0 +< // 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 ( +< "testing" +< +< "github.com/coreos/fcct/config/common" +< +< "github.com/coreos/ignition/v2/config/util" +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< "github.com/stretchr/testify/assert" +< ) +< +< // TestValidateResource tests that multiple sources (i.e. urls and inline) are not allowed but zero or one sources are +< func TestValidateResource(t *testing.T) { +< tests := []struct { +< in Resource +< out error +< errPath path.ContextPath +< }{ +< {}, +< // source specified +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< Resource{ +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< nil, +< path.New("yaml"), +< }, +< // inline specified +< { +< Resource{ +< Inline: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< nil, +< path.New("yaml"), +< }, +< // local specified +< { +< Resource{ +< Local: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< nil, +< path.New("yaml"), +< }, +< // source + inline, invalid +< { +< Resource{ +< Source: util.StrToPtr("data:,hello"), +< Inline: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< common.ErrTooManyResourceSources, +< path.New("yaml", "source"), +< }, +< // source + local, invalid +< { +< Resource{ +< Source: util.StrToPtr("data:,hello"), +< Local: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< common.ErrTooManyResourceSources, +< path.New("yaml", "source"), +< }, +< // inline + local, invalid +< { +< Resource{ +< Inline: util.StrToPtr("hello"), +< Local: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< common.ErrTooManyResourceSources, +< path.New("yaml", "inline"), +< }, +< // source + inline + local, invalid +< { +< Resource{ +< Source: util.StrToPtr("data:,hello"), +< Inline: util.StrToPtr("hello"), +< Local: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< common.ErrTooManyResourceSources, +< path.New("yaml", "source"), +< }, +< } +< +< for i, test := range tests { +< actual := test.in.Validate(path.New("yaml")) +< expected := report.Report{} +< expected.AddOnError(test.errPath, test.out) +< assert.Equal(t, expected, actual, "#%d: bad report", i) +< } +< } +< +< func TestValidateTree(t *testing.T) { +< tests := []struct { +< in Tree +< out error +< }{ +< { +< in: Tree{}, +< out: common.ErrTreeNoLocal, +< }, +< } +< +< for i, test := range tests { +< actual := test.in.Validate(path.New("yaml")) +< expected := report.Report{} +< expected.AddOnError(path.New("yaml"), test.out) +< assert.Equal(t, expected, actual, "#%d: bad report", i) +< } +< } +< +< func TestValidateMode(t *testing.T) { +< fileTests := []struct { +< in File +< out error +< }{ +< { +< in: File{}, +< out: nil, +< }, +< { +< in: File{ +< Mode: util.IntToPtr(0600), +< }, +< out: nil, +< }, +< { +< in: File{ +< Mode: util.IntToPtr(600), +< }, +< out: common.ErrDecimalMode, +< }, +< } +< +< for i, test := range fileTests { +< actual := test.in.Validate(path.New("yaml")) +< expected := report.Report{} +< expected.AddOnWarn(path.New("yaml", "mode"), test.out) +< assert.Equal(t, expected, actual, "#%d: bad report", i) +< } +< +< dirTests := []struct { +< in Directory +< out error +< }{ +< { +< in: Directory{}, +< out: nil, +< }, +< { +< in: Directory{ +< Mode: util.IntToPtr(01770), +< }, +< out: nil, +< }, +< { +< in: Directory{ +< Mode: util.IntToPtr(1770), +< }, +< out: common.ErrDecimalMode, +< }, +< } +< +< for i, test := range dirTests { +< actual := test.in.Validate(path.New("yaml")) +< expected := report.Report{} +< expected.AddOnWarn(path.New("yaml", "mode"), test.out) +< assert.Equal(t, expected, actual, "#%d: bad report", i) +< } +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_3/schema.go updated/vendor/github.com/coreos/fcct/base/v0_3/schema.go +1,254d0 +< // 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_3 +< +< type Clevis struct { +< Custom *Custom `yaml:"custom"` +< Tang []Tang `yaml:"tang"` +< Threshold *int `yaml:"threshold"` +< Tpm2 *bool `yaml:"tpm2"` +< } +< +< type Config struct { +< Version string `yaml:"version"` +< Variant string `yaml:"variant"` +< Ignition Ignition `yaml:"ignition"` +< Passwd Passwd `yaml:"passwd"` +< Storage Storage `yaml:"storage"` +< Systemd Systemd `yaml:"systemd"` +< } +< +< type Custom struct { +< Config string `yaml:"config"` +< NeedsNetwork *bool `yaml:"needs_network"` +< Pin string `yaml:"pin"` +< } +< +< type Device string +< +< type Directory struct { +< Group NodeGroup `yaml:"group"` +< Overwrite *bool `yaml:"overwrite"` +< Path string `yaml:"path"` +< User NodeUser `yaml:"user"` +< Mode *int `yaml:"mode"` +< } +< +< type Disk struct { +< Device string `yaml:"device"` +< Partitions []Partition `yaml:"partitions"` +< WipeTable *bool `yaml:"wipe_table"` +< } +< +< type Dropin struct { +< Contents *string `yaml:"contents"` +< Name string `yaml:"name"` +< } +< +< type File struct { +< 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"` +< 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 []Resource `yaml:"merge"` +< Replace Resource `yaml:"replace"` +< } +< +< type Link struct { +< Group NodeGroup `yaml:"group"` +< Overwrite *bool `yaml:"overwrite"` +< Path string `yaml:"path"` +< User NodeUser `yaml:"user"` +< Hard *bool `yaml:"hard"` +< Target string `yaml:"target"` +< } +< +< type Luks struct { +< Clevis *Clevis `yaml:"clevis"` +< Device *string `yaml:"device"` +< KeyFile Resource `yaml:"key_file"` +< Label *string `yaml:"label"` +< Name string `yaml:"name"` +< Options []LuksOption `yaml:"options"` +< UUID *string `yaml:"uuid"` +< WipeVolume *bool `yaml:"wipe_volume"` +< } +< +< type LuksOption string +< +< type NodeGroup struct { +< ID *int `yaml:"id"` +< Name *string `yaml:"name"` +< } +< +< type NodeUser struct { +< ID *int `yaml:"id"` +< Name *string `yaml:"name"` +< } +< +< type Partition struct { +< GUID *string `yaml:"guid"` +< Label *string `yaml:"label"` +< Number int `yaml:"number"` +< Resize *bool `yaml:"resize"` +< ShouldExist *bool `yaml:"should_exist"` +< SizeMiB *int `yaml:"size_mib"` +< StartMiB *int `yaml:"start_mib"` +< TypeGUID *string `yaml:"type_guid"` +< WipePartitionEntry *bool `yaml:"wipe_partition_entry"` +< } +< +< type Passwd struct { +< Groups []PasswdGroup `yaml:"groups"` +< Users []PasswdUser `yaml:"users"` +< } +< +< type PasswdGroup struct { +< Gid *int `yaml:"gid"` +< Name string `yaml:"name"` +< PasswordHash *string `yaml:"password_hash"` +< ShouldExist *bool `yaml:"should_exist"` +< System *bool `yaml:"system"` +< } +< +< type PasswdUser struct { +< Gecos *string `yaml:"gecos"` +< Groups []Group `yaml:"groups"` +< HomeDir *string `yaml:"home_dir"` +< Name string `yaml:"name"` +< NoCreateHome *bool `yaml:"no_create_home"` +< NoLogInit *bool `yaml:"no_log_init"` +< NoUserGroup *bool `yaml:"no_user_group"` +< PasswordHash *string `yaml:"password_hash"` +< PrimaryGroup *string `yaml:"primary_group"` +< ShouldExist *bool `yaml:"should_exist"` +< SSHAuthorizedKeys []SSHAuthorizedKey `yaml:"ssh_authorized_keys"` +< Shell *string `yaml:"shell"` +< System *bool `yaml:"system"` +< 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"` +< Name string `yaml:"name"` +< Options []RaidOption `yaml:"options"` +< Spares *int `yaml:"spares"` +< } +< +< 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 { +< TLS TLS `yaml:"tls"` +< } +< +< type Storage struct { +< Directories []Directory `yaml:"directories"` +< Disks []Disk `yaml:"disks"` +< Files []File `yaml:"files"` +< Filesystems []Filesystem `yaml:"filesystems"` +< Links []Link `yaml:"links"` +< Luks []Luks `yaml:"luks"` +< Raid []Raid `yaml:"raid"` +< Trees []Tree `yaml:"trees" fcct:"auto_skip"` // Added, not in ignition spec +< } +< +< type Systemd struct { +< Units []Unit `yaml:"units"` +< } +< +< type Tang struct { +< Thumbprint *string `yaml:"thumbprint"` +< URL string `yaml:"url"` +< } +< +< type TLS struct { +< CertificateAuthorities []Resource `yaml:"certificate_authorities"` +< } +< +< type Timeouts struct { +< HTTPResponseHeaders *int `yaml:"http_response_headers"` +< 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"` +< Enabled *bool `yaml:"enabled"` +< Mask *bool `yaml:"mask"` +< Name string `yaml:"name"` +< } +< +< type Verification struct { +< Hash *string `yaml:"hash"` +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_3/translate.go updated/vendor/github.com/coreos/fcct/base/v0_3/translate.go +1,400d0 +< // 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_3 +< +< import ( +< "fmt" +< "io/ioutil" +< "os" +< "path/filepath" +< "strings" +< "text/template" +< +< baseutil "github.com/coreos/fcct/base/util" +< "github.com/coreos/fcct/config/common" +< "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_2/types" +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< ) +< +< var ( +< mountUnitTemplate = template.Must(template.New("unit").Parse(`# Generated by FCCT +< [Unit] +< {{- if .Remote }} +< Before=remote-fs.target +< DefaultDependencies=no +< {{- else }} +< Before=local-fs.target +< {{- end }} +< Requires=systemd-fsck@{{.EscapedDevice}}.service +< After=systemd-fsck@{{.EscapedDevice}}.service +< +< [Mount] +< Where={{.Path}} +< What={{.Device}} +< Type={{.Format}} +< {{- if .MountOptions }} +< Options= +< {{- range $i, $opt := .MountOptions }} +< {{- if $i }},{{ end }} +< {{- $opt }} +< {{- end }} +< {{- end }} +< +< [Install] +< {{- if .Remote }} +< RequiredBy=remote-fs.target +< {{- else }} +< RequiredBy=local-fs.target +< {{- end }}`)) +< ) +< +< // ToIgn3_2Unvalidated translates the config to an Ignition config. It also returns the set of translations +< // it did so paths in the resultant config can be tracked back to their source in the source config. +< // No config validation is performed on input or output. +< func (c Config) ToIgn3_2Unvalidated(options common.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) { +< ret := types.Config{} +< +< tr := translate.NewTranslator("yaml", "json", options) +< tr.AddCustomTranslator(translateIgnition) +< tr.AddCustomTranslator(translateFile) +< tr.AddCustomTranslator(translateDirectory) +< tr.AddCustomTranslator(translateLink) +< tr.AddCustomTranslator(translateResource) +< +< tm, r := translate.Prefixed(tr, "ignition", &c.Ignition, &ret.Ignition) +< translate.MergeP(tr, tm, &r, "passwd", &c.Passwd, &ret.Passwd) +< translate.MergeP(tr, tm, &r, "storage", &c.Storage, &ret.Storage) +< translate.MergeP(tr, tm, &r, "systemd", &c.Systemd, &ret.Systemd) +< +< c.addMountUnits(&ret, &tm) +< +< tm2, r2 := c.processTrees(&ret, options) +< tm.Merge(tm2) +< r.Merge(r2) +< +< if r.IsFatal() { +< return types.Config{}, translate.TranslationSet{}, r +< } +< return ret, tm, r +< } +< +< func translateIgnition(from Ignition, options common.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 common.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 common.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, common.ErrNoFilesDir) +< return +< } +< +< // calculate file path within FilesDir and check for +< // path traversal +< filePath := filepath.Join(options.FilesDir, *from.Local) +< if err := baseutil.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 := baseutil.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 := baseutil.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 translateDirectory(from Directory, options common.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 common.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 common.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, common.ErrNoFilesDir) +< return ts, r +< } +< +< // calculate base path within FilesDir and check for +< // path traversal +< srcBaseDir := filepath.Join(options.FilesDir, tree.Local) +< if err := baseutil.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, common.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 common.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, common.ErrNodeExists) +< return nil +< } +< } else { +< if t.Exists(destPath) { +< r.AddOnError(yamlPath, common.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 := baseutil.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, common.ErrNodeExists) +< return nil +< } +< } else { +< if t.Exists(destPath) { +< r.AddOnError(yamlPath, common.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, common.ErrFileType) +< return nil +< } +< return nil +< }) +< r.AddOnError(yamlPath, err) +< } +< +< func (c Config) addMountUnits(config *types.Config, ts *translate.TranslationSet) { +< if len(c.Storage.Filesystems) == 0 { +< return +< } +< var rendered types.Config +< renderedTranslations := translate.NewTranslationSet("yaml", "json") +< for i, fs := range c.Storage.Filesystems { +< if fs.WithMountUnit == nil || !*fs.WithMountUnit { +< continue +< } +< fromPath := path.New("yaml", "storage", "filesystems", i, "with_mount_unit") +< remote := false +< // check filesystems targeting /dev/mapper devices against LUKS to determine if a +< // remote mount is needed +< if strings.HasPrefix(fs.Device, "/dev/mapper/") || strings.HasPrefix(fs.Device, "/dev/disk/by-id/dm-name-") { +< for _, luks := range c.Storage.Luks { +< // LUKS devices are opened with their name specified +< if fs.Device == fmt.Sprintf("/dev/mapper/%s", luks.Name) || fs.Device == fmt.Sprintf("/dev/disk/by-id/dm-name-%s", luks.Name) { +< if luks.Clevis != nil && len(luks.Clevis.Tang) > 0 { +< remote = true +< break +< } +< } +< } +< } +< newUnit := mountUnitFromFS(fs, remote) +< unitPath := path.New("json", "systemd", "units", len(rendered.Systemd.Units)) +< rendered.Systemd.Units = append(rendered.Systemd.Units, newUnit) +< renderedTranslations.AddFromCommonSource(fromPath, unitPath, newUnit) +< } +< retConfig, retTranslations := baseutil.MergeTranslatedConfigs(rendered, renderedTranslations, *config, *ts) +< *config = retConfig.(types.Config) +< *ts = retTranslations +< } +< +< func mountUnitFromFS(fs Filesystem, remote bool) types.Unit { +< context := struct { +< *Filesystem +< EscapedDevice string +< Remote bool +< }{ +< Filesystem: &fs, +< EscapedDevice: unit.UnitNamePathEscape(fs.Device), +< Remote: remote, +< } +< contents := strings.Builder{} +< err := mountUnitTemplate.Execute(&contents, context) +< 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()), +< } +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_3/translate_test.go updated/vendor/github.com/coreos/fcct/base/v0_3/translate_test.go +1,1438d0 +< // 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_3 +< +< import ( +< "io/ioutil" +< "net" +< "os" +< "path/filepath" +< "strings" +< "testing" +< +< baseutil "github.com/coreos/fcct/base/util" +< "github.com/coreos/fcct/config/common" +< "github.com/coreos/fcct/translate" +< +< "github.com/coreos/ignition/v2/config/util" +< "github.com/coreos/ignition/v2/config/v3_2/types" +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< "github.com/stretchr/testify/assert" +< ) +< +< // Most of this is covered by the Ignition translator generic tests, so just test the custom bits +< +< // TestTranslateFile tests translating the ct storage.files.[i] entries to ignition storage.files.[i] entries. +< func TestTranslateFile(t *testing.T) { +< zzz := "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" +< zzz_gz := "data:;base64,H4sIAAAAAAAC/6oajAAQAAD//5tA8d+VAAAA" +< random := "\xc0\x9cl\x01\x89i\xa5\xbfW\xe4\x1b\xf4J_\xb79P\xa3#\xa7" +< random_b64 := "data:;base64,wJxsAYlppb9X5Bv0Sl+3OVCjI6c=" +< +< filesDir, err := ioutil.TempDir("", "translate-test-") +< if err != nil { +< t.Error(err) +< return +< } +< defer os.RemoveAll(filesDir) +< fileContents := map[string]string{ +< "file-1": "file contents\n", +< "file-2": zzz, +< "file-3": random, +< } +< for name, contents := range fileContents { +< err := ioutil.WriteFile(filepath.Join(filesDir, name), []byte(contents), 0644) +< if err != nil { +< t.Error(err) +< return +< } +< } +< +< tests := []struct { +< in File +< out types.File +< exceptions []translate.Translation +< report string +< options common.TranslateOptions +< }{ +< { +< File{}, +< types.File{}, +< nil, +< "", +< common.TranslateOptions{}, +< }, +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< File{ +< Path: "/foo", +< Group: NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Mode: util.IntToPtr(420), +< Append: []Resource{ +< { +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< HTTPHeaders: HTTPHeaders{ +< HTTPHeader{ +< Name: "Header", +< Value: util.StrToPtr("this isn't validated"), +< }, +< }, +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< { +< Inline: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< HTTPHeaders: HTTPHeaders{ +< HTTPHeader{ +< Name: "Header", +< Value: util.StrToPtr("this isn't validated"), +< }, +< }, +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< { +< Local: util.StrToPtr("file-1"), +< }, +< }, +< Overwrite: util.BoolToPtr(true), +< Contents: Resource{ +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< HTTPHeaders: HTTPHeaders{ +< HTTPHeader{ +< Name: "Header", +< Value: util.StrToPtr("this isn't validated"), +< }, +< }, +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< Group: types.NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: types.NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Overwrite: util.BoolToPtr(true), +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Mode: util.IntToPtr(420), +< Append: []types.Resource{ +< { +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< HTTPHeaders: types.HTTPHeaders{ +< types.HTTPHeader{ +< Name: "Header", +< Value: util.StrToPtr("this isn't validated"), +< }, +< }, +< Verification: types.Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< { +< Source: util.StrToPtr("data:,hello"), +< Compression: util.StrToPtr("gzip"), +< HTTPHeaders: types.HTTPHeaders{ +< types.HTTPHeader{ +< Name: "Header", +< Value: util.StrToPtr("this isn't validated"), +< }, +< }, +< Verification: types.Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< { +< Source: util.StrToPtr("data:,file%20contents%0A"), +< }, +< }, +< Contents: types.Resource{ +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< HTTPHeaders: types.HTTPHeaders{ +< types.HTTPHeader{ +< Name: "Header", +< Value: util.StrToPtr("this isn't validated"), +< }, +< }, +< Verification: types.Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< { +< From: path.New("yaml", "append", 1, "inline"), +< To: path.New("json", "append", 1, "source"), +< }, +< { +< From: path.New("yaml", "append", 2, "local"), +< To: path.New("json", "append", 2, "source"), +< }, +< }, +< "", +< common.TranslateOptions{ +< FilesDir: filesDir, +< }, +< }, +< // inline file contents +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< // String is too short for auto gzip compression +< Inline: util.StrToPtr("xyzzy"), +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,xyzzy"), +< }, +< }, +< }, +< []translate.Translation{ +< { +< From: path.New("yaml", "contents", "inline"), +< To: path.New("json", "contents", "source"), +< }, +< }, +< "", +< common.TranslateOptions{}, +< }, +< // local file contents +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< Local: util.StrToPtr("file-1"), +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,file%20contents%0A"), +< }, +< }, +< }, +< []translate.Translation{ +< { +< From: path.New("yaml", "contents", "local"), +< To: path.New("json", "contents", "source"), +< }, +< }, +< "", +< common.TranslateOptions{ +< FilesDir: filesDir, +< }, +< }, +< // filesDir not specified +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< Local: util.StrToPtr("file-1"), +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< }, +< []translate.Translation{}, +< "error at $.contents.local: " + common.ErrNoFilesDir.Error() + "\n", +< common.TranslateOptions{}, +< }, +< // attempted directory traversal +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< Local: util.StrToPtr("../file-1"), +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< }, +< []translate.Translation{}, +< "error at $.contents.local: " + common.ErrFilesDirEscape.Error() + "\n", +< common.TranslateOptions{ +< FilesDir: filesDir, +< }, +< }, +< // attempted inclusion of nonexistent file +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< Local: util.StrToPtr("file-missing"), +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< }, +< []translate.Translation{}, +< "error at $.contents.local: open " + filepath.Join(filesDir, "file-missing") + ": no such file or directory\n", +< common.TranslateOptions{ +< FilesDir: filesDir, +< }, +< }, +< // inline and local automatic file encoding +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< // gzip +< Inline: util.StrToPtr(zzz), +< }, +< Append: []Resource{ +< { +< // gzip +< Local: util.StrToPtr("file-2"), +< }, +< { +< // base64 +< Inline: util.StrToPtr(random), +< }, +< { +< // base64 +< Local: util.StrToPtr("file-3"), +< }, +< { +< // URL-escaped +< Inline: util.StrToPtr(zzz), +< Compression: util.StrToPtr("invalid"), +< }, +< { +< // URL-escaped +< Local: util.StrToPtr("file-2"), +< Compression: util.StrToPtr("invalid"), +< }, +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr(zzz_gz), +< Compression: util.StrToPtr("gzip"), +< }, +< Append: []types.Resource{ +< { +< Source: util.StrToPtr(zzz_gz), +< Compression: util.StrToPtr("gzip"), +< }, +< { +< Source: util.StrToPtr(random_b64), +< }, +< { +< Source: util.StrToPtr(random_b64), +< }, +< { +< Source: util.StrToPtr("data:," + zzz), +< Compression: util.StrToPtr("invalid"), +< }, +< { +< Source: util.StrToPtr("data:," + zzz), +< Compression: util.StrToPtr("invalid"), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< { +< From: path.New("yaml", "contents", "inline"), +< To: path.New("json", "contents", "source"), +< }, +< { +< From: path.New("yaml", "contents", "inline"), +< To: path.New("json", "contents", "compression"), +< }, +< { +< From: path.New("yaml", "append", 0, "local"), +< To: path.New("json", "append", 0, "source"), +< }, +< { +< From: path.New("yaml", "append", 0, "local"), +< To: path.New("json", "append", 0, "compression"), +< }, +< { +< From: path.New("yaml", "append", 1, "inline"), +< To: path.New("json", "append", 1, "source"), +< }, +< { +< From: path.New("yaml", "append", 2, "local"), +< To: path.New("json", "append", 2, "source"), +< }, +< { +< From: path.New("yaml", "append", 3, "inline"), +< To: path.New("json", "append", 3, "source"), +< }, +< { +< From: path.New("yaml", "append", 4, "local"), +< To: path.New("json", "append", 4, "source"), +< }, +< }, +< "", +< common.TranslateOptions{ +< FilesDir: filesDir, +< }, +< }, +< // Test disable automatic gzip compression +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< Inline: util.StrToPtr(zzz), +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:," + zzz), +< }, +< }, +< }, +< []translate.Translation{ +< { +< From: path.New("yaml", "contents", "inline"), +< To: path.New("json", "contents", "source"), +< }, +< }, +< "", +< common.TranslateOptions{ +< NoResourceAutoCompression: true, +< }, +< }, +< } +< +< for i, test := range tests { +< actual, translations, r := translateFile(test.in, test.options) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, test.report, r.String(), "#%d: bad report", i) +< baseutil.VerifyTranslations(t, translations, test.exceptions, "#%d", i) +< } +< } +< +< // TestTranslateDirectory tests translating the ct storage.directories.[i] entries to ignition storage.directories.[i] entires. +< func TestTranslateDirectory(t *testing.T) { +< tests := []struct { +< in Directory +< out types.Directory +< }{ +< { +< Directory{}, +< types.Directory{}, +< }, +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< Directory{ +< Path: "/foo", +< Group: NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Mode: util.IntToPtr(420), +< Overwrite: util.BoolToPtr(true), +< }, +< types.Directory{ +< Node: types.Node{ +< Path: "/foo", +< Group: types.NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: types.NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Overwrite: util.BoolToPtr(true), +< }, +< DirectoryEmbedded1: types.DirectoryEmbedded1{ +< Mode: util.IntToPtr(420), +< }, +< }, +< }, +< } +< +< for i, test := range tests { +< actual, _, r := translateDirectory(test.in, common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +< +< // TestTranslateLink tests translating the ct storage.links.[i] entries to ignition storage.links.[i] entires. +< func TestTranslateLink(t *testing.T) { +< tests := []struct { +< in Link +< out types.Link +< }{ +< { +< Link{}, +< types.Link{}, +< }, +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< Link{ +< Path: "/foo", +< Group: NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Overwrite: util.BoolToPtr(true), +< Target: "/bar", +< Hard: util.BoolToPtr(false), +< }, +< types.Link{ +< Node: types.Node{ +< Path: "/foo", +< Group: types.NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: types.NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Overwrite: util.BoolToPtr(true), +< }, +< LinkEmbedded1: types.LinkEmbedded1{ +< Target: "/bar", +< Hard: util.BoolToPtr(false), +< }, +< }, +< }, +< } +< +< for i, test := range tests { +< actual, _, r := translateLink(test.in, common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +< +< // TestTranslateFilesystem tests translating the fcct storage.filesystems.[i] entries to ignition storage.filesystems.[i] entries. +< func TestTranslateFilesystem(t *testing.T) { +< tests := []struct { +< in Filesystem +< out types.Filesystem +< }{ +< { +< Filesystem{}, +< types.Filesystem{}, +< }, +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< Filesystem{ +< Device: "/foo", +< Format: util.StrToPtr("/bar"), +< Label: util.StrToPtr("/baz"), +< MountOptions: []string{"yes", "no", "maybe"}, +< Options: []string{"foo", "foo", "bar"}, +< Path: util.StrToPtr("/quux"), +< UUID: util.StrToPtr("1234"), +< WipeFilesystem: util.BoolToPtr(true), +< WithMountUnit: util.BoolToPtr(true), +< }, +< types.Filesystem{ +< Device: "/foo", +< Format: util.StrToPtr("/bar"), +< Label: util.StrToPtr("/baz"), +< MountOptions: []types.MountOption{"yes", "no", "maybe"}, +< Options: []types.FilesystemOption{"foo", "foo", "bar"}, +< Path: util.StrToPtr("/quux"), +< UUID: util.StrToPtr("1234"), +< WipeFilesystem: util.BoolToPtr(true), +< }, +< }, +< } +< +< for i, test := range tests { +< // Filesystem doesn't have a custom translator, so embed in a +< // complete config +< in := Config{ +< Storage: Storage{ +< Filesystems: []Filesystem{test.in}, +< }, +< } +< expected := []types.Filesystem{test.out} +< actual, _, r := in.ToIgn3_2Unvalidated(common.TranslateOptions{}) +< assert.Equal(t, expected, actual.Storage.Filesystems, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +< +< // TestTranslateMountUnit tests the FCCT storage.filesystems.[i].with_mount_unit flag. +< func TestTranslateMountUnit(t *testing.T) { +< tests := []struct { +< in Config +< out types.Config +< }{ +< // local mount with options, overridden enabled flag +< { +< Config{ +< Storage: Storage{ +< Filesystems: []Filesystem{ +< { +< Device: "/dev/disk/by-label/foo", +< Format: util.StrToPtr("ext4"), +< MountOptions: []string{"ro", "noatime"}, +< Path: util.StrToPtr("/var/lib/containers"), +< WithMountUnit: util.BoolToPtr(true), +< }, +< }, +< }, +< Systemd: Systemd{ +< Units: []Unit{ +< { +< Name: "var-lib-containers.mount", +< Enabled: util.BoolToPtr(false), +< }, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.2.0", +< }, +< Storage: types.Storage{ +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/disk/by-label/foo", +< Format: util.StrToPtr("ext4"), +< MountOptions: []types.MountOption{"ro", "noatime"}, +< Path: util.StrToPtr("/var/lib/containers"), +< }, +< }, +< }, +< Systemd: types.Systemd{ +< Units: []types.Unit{ +< { +< Enabled: util.BoolToPtr(false), +< Contents: util.StrToPtr(`# Generated by FCCT +< [Unit] +< Before=local-fs.target +< Requires=systemd-fsck@dev-disk-by\x2dlabel-foo.service +< After=systemd-fsck@dev-disk-by\x2dlabel-foo.service +< +< [Mount] +< Where=/var/lib/containers +< What=/dev/disk/by-label/foo +< Type=ext4 +< Options=ro,noatime +< +< [Install] +< RequiredBy=local-fs.target`), +< Name: "var-lib-containers.mount", +< }, +< }, +< }, +< }, +< }, +< // remote mount with options +< { +< Config{ +< Storage: Storage{ +< Filesystems: []Filesystem{ +< { +< Device: "/dev/mapper/foo-bar", +< Format: util.StrToPtr("ext4"), +< MountOptions: []string{"ro", "noatime"}, +< Path: util.StrToPtr("/var/lib/containers"), +< WithMountUnit: util.BoolToPtr(true), +< }, +< }, +< Luks: []Luks{ +< { +< Name: "foo-bar", +< Device: util.StrToPtr("/dev/bar"), +< Clevis: &Clevis{ +< Tang: []Tang{ +< { +< URL: "http://example.com", +< }, +< }, +< }, +< }, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.2.0", +< }, +< Storage: types.Storage{ +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/mapper/foo-bar", +< Format: util.StrToPtr("ext4"), +< MountOptions: []types.MountOption{"ro", "noatime"}, +< Path: util.StrToPtr("/var/lib/containers"), +< }, +< }, +< Luks: []types.Luks{ +< { +< Name: "foo-bar", +< Device: util.StrToPtr("/dev/bar"), +< Clevis: &types.Clevis{ +< Tang: []types.Tang{ +< { +< URL: "http://example.com", +< }, +< }, +< }, +< }, +< }, +< }, +< Systemd: types.Systemd{ +< Units: []types.Unit{ +< { +< Enabled: util.BoolToPtr(true), +< Contents: util.StrToPtr(`# Generated by FCCT +< [Unit] +< Before=remote-fs.target +< DefaultDependencies=no +< Requires=systemd-fsck@dev-mapper-foo\x2dbar.service +< After=systemd-fsck@dev-mapper-foo\x2dbar.service +< +< [Mount] +< Where=/var/lib/containers +< What=/dev/mapper/foo-bar +< Type=ext4 +< Options=ro,noatime +< +< [Install] +< RequiredBy=remote-fs.target`), +< Name: "var-lib-containers.mount", +< }, +< }, +< }, +< }, +< }, +< // local mount, no options +< { +< Config{ +< Storage: Storage{ +< Filesystems: []Filesystem{ +< { +< Device: "/dev/disk/by-label/foo", +< Format: util.StrToPtr("ext4"), +< Path: util.StrToPtr("/var/lib/containers"), +< WithMountUnit: util.BoolToPtr(true), +< }, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.2.0", +< }, +< Storage: types.Storage{ +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/disk/by-label/foo", +< Format: util.StrToPtr("ext4"), +< Path: util.StrToPtr("/var/lib/containers"), +< }, +< }, +< }, +< Systemd: types.Systemd{ +< Units: []types.Unit{ +< { +< Enabled: util.BoolToPtr(true), +< Contents: util.StrToPtr(`# Generated by FCCT +< [Unit] +< Before=local-fs.target +< Requires=systemd-fsck@dev-disk-by\x2dlabel-foo.service +< After=systemd-fsck@dev-disk-by\x2dlabel-foo.service +< +< [Mount] +< Where=/var/lib/containers +< What=/dev/disk/by-label/foo +< Type=ext4 +< +< [Install] +< RequiredBy=local-fs.target`), +< Name: "var-lib-containers.mount", +< }, +< }, +< }, +< }, +< }, +< // overridden mount unit +< { +< Config{ +< Storage: Storage{ +< Filesystems: []Filesystem{ +< { +< Device: "/dev/disk/by-label/foo", +< Format: util.StrToPtr("ext4"), +< Path: util.StrToPtr("/var/lib/containers"), +< WithMountUnit: util.BoolToPtr(true), +< }, +< }, +< }, +< Systemd: Systemd{ +< Units: []Unit{ +< { +< Name: "var-lib-containers.mount", +< Contents: util.StrToPtr("[Service]\nExecStart=/bin/false\n"), +< }, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.2.0", +< }, +< Storage: types.Storage{ +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/disk/by-label/foo", +< Format: util.StrToPtr("ext4"), +< Path: util.StrToPtr("/var/lib/containers"), +< }, +< }, +< }, +< Systemd: types.Systemd{ +< Units: []types.Unit{ +< { +< Enabled: util.BoolToPtr(true), +< Contents: util.StrToPtr("[Service]\nExecStart=/bin/false\n"), +< Name: "var-lib-containers.mount", +< }, +< }, +< }, +< }, +< }, +< } +< +< for i, test := range tests { +< out, _, r := test.in.ToIgn3_2Unvalidated(common.TranslateOptions{}) +< assert.Equal(t, test.out, out, "#%d: bad output", i) +< assert.Equal(t, report.Report{}, r, "#%d: expected empty report", i) +< } +< } +< +< // TestTranslateTree tests translating the FCC storage.trees.[i] entries to ignition storage.files.[i] entries. +< func TestTranslateTree(t *testing.T) { +< tests := []struct { +< options *common.TranslateOptions // defaulted if not specified +< dirDirs map[string]os.FileMode // relative path -> mode +< dirFiles map[string]os.FileMode // relative path -> mode +< dirLinks map[string]string // relative path -> target +< dirSockets []string // relative path +< inTrees []Tree +< inFiles []File +< inDirs []Directory +< inLinks []Link +< outFiles []types.File +< outLinks []types.Link +< report string +< }{ +< // smoke test +< {}, +< // basic functionality +< { +< dirFiles: map[string]os.FileMode{ +< "tree/executable": 0700, +< "tree/file": 0600, +< "tree/overridden": 0644, +< "tree/overridden-executable": 0700, +< "tree/subdir/file": 0644, +< // compressed contents +< "tree/subdir/subdir/subdir/subdir/subdir/subdir/subdir/subdir/subdir/file": 0644, +< "tree2/file": 0600, +< }, +< dirLinks: map[string]string{ +< "tree/subdir/bad-link": "../nonexistent", +< "tree/subdir/link": "../file", +< "tree/subdir/overridden-link": "../file", +< }, +< inTrees: []Tree{ +< { +< Local: "tree", +< }, +< { +< Local: "tree2", +< Path: util.StrToPtr("/etc"), +< }, +< }, +< inFiles: []File{ +< { +< Path: "/overridden", +< Mode: util.IntToPtr(0600), +< User: NodeUser{ +< Name: util.StrToPtr("bovik"), +< }, +< }, +< { +< Path: "/overridden-executable", +< Mode: util.IntToPtr(0600), +< User: NodeUser{ +< Name: util.StrToPtr("bovik"), +< }, +< }, +< }, +< inLinks: []Link{ +< { +< Path: "/subdir/overridden-link", +< User: NodeUser{ +< Name: util.StrToPtr("bovik"), +< }, +< }, +< }, +< outFiles: []types.File{ +< { +< Node: types.Node{ +< Path: "/overridden", +< User: types.NodeUser{ +< Name: util.StrToPtr("bovik"), +< }, +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,tree%2Foverridden"), +< }, +< Mode: util.IntToPtr(0600), +< }, +< }, +< { +< Node: types.Node{ +< Path: "/overridden-executable", +< User: types.NodeUser{ +< Name: util.StrToPtr("bovik"), +< }, +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,tree%2Foverridden-executable"), +< }, +< Mode: util.IntToPtr(0600), +< }, +< }, +< { +< Node: types.Node{ +< Path: "/executable", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,tree%2Fexecutable"), +< }, +< Mode: util.IntToPtr(0755), +< }, +< }, +< { +< Node: types.Node{ +< Path: "/file", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,tree%2Ffile"), +< }, +< Mode: util.IntToPtr(0644), +< }, +< }, +< { +< Node: types.Node{ +< Path: "/subdir/file", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,tree%2Fsubdir%2Ffile"), +< }, +< Mode: util.IntToPtr(0644), +< }, +< }, +< { +< Node: types.Node{ +< Path: "/subdir/subdir/subdir/subdir/subdir/subdir/subdir/subdir/subdir/file", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:;base64,H4sIAAAAAAAC/yopSk3VLy5NSsksIptKy8xJBQQAAP//gkRzjkgAAAA="), +< Compression: util.StrToPtr("gzip"), +< }, +< Mode: util.IntToPtr(0644), +< }, +< }, +< { +< Node: types.Node{ +< Path: "/etc/file", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,tree2%2Ffile"), +< }, +< Mode: util.IntToPtr(0644), +< }, +< }, +< }, +< outLinks: []types.Link{ +< { +< Node: types.Node{ +< Path: "/subdir/overridden-link", +< User: types.NodeUser{ +< Name: util.StrToPtr("bovik"), +< }, +< }, +< LinkEmbedded1: types.LinkEmbedded1{ +< Target: "../file", +< }, +< }, +< { +< Node: types.Node{ +< Path: "/subdir/bad-link", +< }, +< LinkEmbedded1: types.LinkEmbedded1{ +< Target: "../nonexistent", +< }, +< }, +< { +< Node: types.Node{ +< Path: "/subdir/link", +< }, +< LinkEmbedded1: types.LinkEmbedded1{ +< Target: "../file", +< }, +< }, +< }, +< }, +< // collisions +< { +< dirFiles: map[string]os.FileMode{ +< "tree0/file": 0600, +< "tree1/directory": 0600, +< "tree2/link": 0600, +< "tree3/file-partial": 0600, // should be okay +< "tree4/link-partial": 0600, +< "tree5/tree-file": 0600, // set up for tree/tree collision +< "tree6/tree-file": 0600, +< "tree15/tree-link": 0600, +< }, +< dirLinks: map[string]string{ +< "tree7/file": "file", +< "tree8/directory": "file", +< "tree9/link": "file", +< "tree10/file-partial": "file", +< "tree11/link-partial": "file", // should be okay +< "tree12/tree-file": "file", +< "tree13/tree-link": "file", // set up for tree/tree collision +< "tree14/tree-link": "file", +< }, +< inTrees: []Tree{ +< { +< Local: "tree0", +< }, +< { +< Local: "tree1", +< }, +< { +< Local: "tree2", +< }, +< { +< Local: "tree3", +< }, +< { +< Local: "tree4", +< }, +< { +< Local: "tree5", +< }, +< { +< Local: "tree6", +< }, +< { +< Local: "tree7", +< }, +< { +< Local: "tree8", +< }, +< { +< Local: "tree9", +< }, +< { +< Local: "tree10", +< }, +< { +< Local: "tree11", +< }, +< { +< Local: "tree12", +< }, +< { +< Local: "tree13", +< }, +< { +< Local: "tree14", +< }, +< { +< Local: "tree15", +< }, +< }, +< inFiles: []File{ +< { +< Path: "/file", +< Contents: Resource{ +< Source: util.StrToPtr("data:,foo"), +< }, +< }, +< { +< Path: "/file-partial", +< }, +< }, +< inDirs: []Directory{ +< { +< Path: "/directory", +< }, +< }, +< inLinks: []Link{ +< { +< Path: "/link", +< Target: "file", +< }, +< { +< Path: "/link-partial", +< }, +< }, +< report: "error at $.storage.trees.0: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.1: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.2: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.4: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.6: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.7: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.8: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.9: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.10: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.12: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.14: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.15: " + common.ErrNodeExists.Error() + "\n", +< }, +< // files-dir escape +< { +< inTrees: []Tree{ +< { +< Local: "../escape", +< }, +< }, +< report: "error at $.storage.trees.0: " + common.ErrFilesDirEscape.Error() + "\n", +< }, +< // no files-dir +< { +< options: &common.TranslateOptions{}, +< inTrees: []Tree{ +< { +< Local: "tree", +< }, +< }, +< report: "error at $.storage.trees.0: " + common.ErrNoFilesDir.Error() + "\n", +< }, +< // non-file/dir/symlink in directory tree +< { +< dirSockets: []string{ +< "tree/socket", +< }, +< inTrees: []Tree{ +< { +< Local: "tree", +< }, +< }, +< report: "error at $.storage.trees.0: " + common.ErrFileType.Error() + "\n", +< }, +< // unreadable file +< { +< dirDirs: map[string]os.FileMode{ +< "tree/subdir": 0000, +< "tree2": 0000, +< }, +< dirFiles: map[string]os.FileMode{ +< "tree/file": 0000, +< }, +< inTrees: []Tree{ +< { +< Local: "tree", +< }, +< { +< Local: "tree2", +< }, +< }, +< report: "error at $.storage.trees.0: open %FilesDir%/tree/file: permission denied\n" + +< "error at $.storage.trees.0: open %FilesDir%/tree/subdir: permission denied\n" + +< "error at $.storage.trees.1: open %FilesDir%/tree2: permission denied\n", +< }, +< // local is not a directory +< { +< dirFiles: map[string]os.FileMode{ +< "tree": 0600, +< }, +< inTrees: []Tree{ +< { +< Local: "tree", +< }, +< { +< Local: "nonexistent", +< }, +< }, +< report: "error at $.storage.trees.0: " + common.ErrTreeNotDirectory.Error() + "\n" + +< "error at $.storage.trees.1: stat %FilesDir%/nonexistent: no such file or directory\n", +< }, +< } +< +< for i, test := range tests { +< filesDir, err := ioutil.TempDir("", "translate-test-") +< if err != nil { +< t.Error(err) +< return +< } +< defer os.RemoveAll(filesDir) +< for path, mode := range test.dirDirs { +< absPath := filepath.Join(filesDir, path) +< if err := os.MkdirAll(absPath, 0755); err != nil { +< t.Error(err) +< return +< } +< if err := os.Chmod(absPath, mode); err != nil { +< t.Error(err) +< return +< } +< } +< for path, mode := range test.dirFiles { +< absPath := filepath.Join(filesDir, path) +< if err := os.MkdirAll(filepath.Dir(absPath), 0755); err != nil { +< t.Error(err) +< return +< } +< if err := ioutil.WriteFile(absPath, []byte(path), mode); err != nil { +< t.Error(err) +< return +< } +< } +< for path, target := range test.dirLinks { +< absPath := filepath.Join(filesDir, path) +< if err := os.MkdirAll(filepath.Dir(absPath), 0755); err != nil { +< t.Error(err) +< return +< } +< if err := os.Symlink(target, absPath); err != nil { +< t.Error(err) +< return +< } +< } +< for _, path := range test.dirSockets { +< absPath := filepath.Join(filesDir, path) +< if err := os.MkdirAll(filepath.Dir(absPath), 0755); err != nil { +< t.Error(err) +< return +< } +< listener, err := net.ListenUnix("unix", &net.UnixAddr{ +< Name: absPath, +< Net: "unix", +< }) +< if err != nil { +< t.Error(err) +< return +< } +< defer listener.Close() +< } +< +< config := Config{ +< Storage: Storage{ +< Files: test.inFiles, +< Directories: test.inDirs, +< Links: test.inLinks, +< Trees: test.inTrees, +< }, +< } +< options := common.TranslateOptions{ +< FilesDir: filesDir, +< } +< if test.options != nil { +< options = *test.options +< } +< actual, _, r := config.ToIgn3_2Unvalidated(options) +< +< expectedReport := strings.ReplaceAll(test.report, "%FilesDir%", filesDir) +< assert.Equal(t, expectedReport, r.String(), "#%d: bad report", i) +< if expectedReport != "" { +< continue +< } +< +< assert.Equal(t, test.outFiles, actual.Storage.Files, "#%d: files mismatch", i) +< assert.Equal(t, []types.Directory(nil), actual.Storage.Directories, "#%d: directories mismatch", i) +< assert.Equal(t, test.outLinks, actual.Storage.Links, "#%d: links mismatch", i) +< } +< } +< +< // TestTranslateIgnition tests translating the ct config.ignition to the ignition config.ignition section. +< // It ensure that the version is set as well. +< func TestTranslateIgnition(t *testing.T) { +< tests := []struct { +< in Ignition +< out types.Ignition +< }{ +< { +< Ignition{}, +< types.Ignition{ +< Version: "3.2.0", +< }, +< }, +< { +< Ignition{ +< Config: IgnitionConfig{ +< Merge: []Resource{ +< { +< Inline: util.StrToPtr("xyzzy"), +< }, +< }, +< Replace: Resource{ +< Inline: util.StrToPtr("xyzzy"), +< }, +< }, +< }, +< types.Ignition{ +< Version: "3.2.0", +< Config: types.IgnitionConfig{ +< Merge: []types.Resource{ +< { +< Source: util.StrToPtr("data:,xyzzy"), +< }, +< }, +< Replace: types.Resource{ +< Source: util.StrToPtr("data:,xyzzy"), +< }, +< }, +< }, +< }, +< { +< Ignition{ +< Proxy: Proxy{ +< HTTPProxy: util.StrToPtr("https://example.com:8080"), +< NoProxy: []string{"example.com"}, +< }, +< }, +< types.Ignition{ +< Version: "3.2.0", +< Proxy: types.Proxy{ +< HTTPProxy: util.StrToPtr("https://example.com:8080"), +< NoProxy: []types.NoProxyItem{types.NoProxyItem("example.com")}, +< }, +< }, +< }, +< { +< Ignition{ +< Security: Security{ +< TLS: TLS{ +< CertificateAuthorities: []Resource{ +< { +< Inline: util.StrToPtr("xyzzy"), +< }, +< }, +< }, +< }, +< }, +< types.Ignition{ +< Version: "3.2.0", +< Security: types.Security{ +< TLS: types.TLS{ +< CertificateAuthorities: []types.Resource{ +< { +< Source: util.StrToPtr("data:,xyzzy"), +< }, +< }, +< }, +< }, +< }, +< }, +< } +< for i, test := range tests { +< actual, _, r := translateIgnition(test.in, common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +< +< // TestToIgn3_2 tests the config.ToIgn3_2 function ensuring it will generate a valid config even when empty. Not much else is +< // tested since it uses the Ignition translation code which has it's own set of tests. +< func TestToIgn3_2(t *testing.T) { +< tests := []struct { +< in Config +< out types.Config +< }{ +< { +< Config{}, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.2.0", +< }, +< }, +< }, +< } +< for i, test := range tests { +< actual, _, r := test.in.ToIgn3_2Unvalidated(common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_3/util.go updated/vendor/github.com/coreos/fcct/base/v0_3/util.go +1,125d0 +< // 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_3 +< +< import ( +< "github.com/coreos/ignition/v2/config/v3_2/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 --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_3/validate.go updated/vendor/github.com/coreos/fcct/base/v0_3/validate.go +1,78d0 +< // 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_3 +< +< import ( +< baseutil "github.com/coreos/fcct/base/util" +< "github.com/coreos/fcct/config/common" +< +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< ) +< +< 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), common.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"), common.ErrMountUnitNoPath) +< } +< if fs.Format == nil || *fs.Format == "" { +< r.AddOnError(c.Append("format"), common.ErrMountUnitNoFormat) +< } +< return +< } +< +< func (d Directory) Validate(c path.ContextPath) (r report.Report) { +< if d.Mode != nil { +< r.AddOnWarn(c.Append("mode"), baseutil.CheckForDecimalMode(*d.Mode, true)) +< } +< return +< } +< +< func (f File) Validate(c path.ContextPath) (r report.Report) { +< if f.Mode != nil { +< r.AddOnWarn(c.Append("mode"), baseutil.CheckForDecimalMode(*f.Mode, false)) +< } +< return +< } +< +< func (t Tree) Validate(c path.ContextPath) (r report.Report) { +< if t.Local == "" { +< r.AddOnError(c, common.ErrTreeNoLocal) +< } +< return +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_3/validate_test.go updated/vendor/github.com/coreos/fcct/base/v0_3/validate_test.go +1,214d0 +< // 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_3 +< +< import ( +< "testing" +< +< "github.com/coreos/fcct/config/common" +< +< "github.com/coreos/ignition/v2/config/util" +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< "github.com/stretchr/testify/assert" +< ) +< +< // TestValidateResource tests that multiple sources (i.e. urls and inline) are not allowed but zero or one sources are +< func TestValidateResource(t *testing.T) { +< tests := []struct { +< in Resource +< out error +< errPath path.ContextPath +< }{ +< {}, +< // source specified +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< Resource{ +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< nil, +< path.New("yaml"), +< }, +< // inline specified +< { +< Resource{ +< Inline: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< nil, +< path.New("yaml"), +< }, +< // local specified +< { +< Resource{ +< Local: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< nil, +< path.New("yaml"), +< }, +< // source + inline, invalid +< { +< Resource{ +< Source: util.StrToPtr("data:,hello"), +< Inline: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< common.ErrTooManyResourceSources, +< path.New("yaml", "source"), +< }, +< // source + local, invalid +< { +< Resource{ +< Source: util.StrToPtr("data:,hello"), +< Local: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< common.ErrTooManyResourceSources, +< path.New("yaml", "source"), +< }, +< // inline + local, invalid +< { +< Resource{ +< Inline: util.StrToPtr("hello"), +< Local: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< common.ErrTooManyResourceSources, +< path.New("yaml", "inline"), +< }, +< // source + inline + local, invalid +< { +< Resource{ +< Source: util.StrToPtr("data:,hello"), +< Inline: util.StrToPtr("hello"), +< Local: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< common.ErrTooManyResourceSources, +< path.New("yaml", "source"), +< }, +< } +< +< for i, test := range tests { +< actual := test.in.Validate(path.New("yaml")) +< expected := report.Report{} +< expected.AddOnError(test.errPath, test.out) +< assert.Equal(t, expected, actual, "#%d: bad report", i) +< } +< } +< +< func TestValidateTree(t *testing.T) { +< tests := []struct { +< in Tree +< out error +< }{ +< { +< in: Tree{}, +< out: common.ErrTreeNoLocal, +< }, +< } +< +< for i, test := range tests { +< actual := test.in.Validate(path.New("yaml")) +< expected := report.Report{} +< expected.AddOnError(path.New("yaml"), test.out) +< assert.Equal(t, expected, actual, "#%d: bad report", i) +< } +< } +< +< func TestValidateMode(t *testing.T) { +< fileTests := []struct { +< in File +< out error +< }{ +< { +< in: File{}, +< out: nil, +< }, +< { +< in: File{ +< Mode: util.IntToPtr(0600), +< }, +< out: nil, +< }, +< { +< in: File{ +< Mode: util.IntToPtr(600), +< }, +< out: common.ErrDecimalMode, +< }, +< } +< +< for i, test := range fileTests { +< actual := test.in.Validate(path.New("yaml")) +< expected := report.Report{} +< expected.AddOnWarn(path.New("yaml", "mode"), test.out) +< assert.Equal(t, expected, actual, "#%d: bad report", i) +< } +< +< dirTests := []struct { +< in Directory +< out error +< }{ +< { +< in: Directory{}, +< out: nil, +< }, +< { +< in: Directory{ +< Mode: util.IntToPtr(01770), +< }, +< out: nil, +< }, +< { +< in: Directory{ +< Mode: util.IntToPtr(1770), +< }, +< out: common.ErrDecimalMode, +< }, +< } +< +< for i, test := range dirTests { +< actual := test.in.Validate(path.New("yaml")) +< expected := report.Report{} +< expected.AddOnWarn(path.New("yaml", "mode"), test.out) +< assert.Equal(t, expected, actual, "#%d: bad report", i) +< } +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_4_exp/schema.go updated/vendor/github.com/coreos/fcct/base/v0_4_exp/schema.go +1,254d0 +< // 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_4_exp +< +< type Clevis struct { +< Custom *Custom `yaml:"custom"` +< Tang []Tang `yaml:"tang"` +< Threshold *int `yaml:"threshold"` +< Tpm2 *bool `yaml:"tpm2"` +< } +< +< type Config struct { +< Version string `yaml:"version"` +< Variant string `yaml:"variant"` +< Ignition Ignition `yaml:"ignition"` +< Passwd Passwd `yaml:"passwd"` +< Storage Storage `yaml:"storage"` +< Systemd Systemd `yaml:"systemd"` +< } +< +< type Custom struct { +< Config string `yaml:"config"` +< NeedsNetwork *bool `yaml:"needs_network"` +< Pin string `yaml:"pin"` +< } +< +< type Device string +< +< type Directory struct { +< Group NodeGroup `yaml:"group"` +< Overwrite *bool `yaml:"overwrite"` +< Path string `yaml:"path"` +< User NodeUser `yaml:"user"` +< Mode *int `yaml:"mode"` +< } +< +< type Disk struct { +< Device string `yaml:"device"` +< Partitions []Partition `yaml:"partitions"` +< WipeTable *bool `yaml:"wipe_table"` +< } +< +< type Dropin struct { +< Contents *string `yaml:"contents"` +< Name string `yaml:"name"` +< } +< +< type File struct { +< 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"` +< 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 []Resource `yaml:"merge"` +< Replace Resource `yaml:"replace"` +< } +< +< type Link struct { +< Group NodeGroup `yaml:"group"` +< Overwrite *bool `yaml:"overwrite"` +< Path string `yaml:"path"` +< User NodeUser `yaml:"user"` +< Hard *bool `yaml:"hard"` +< Target string `yaml:"target"` +< } +< +< type Luks struct { +< Clevis *Clevis `yaml:"clevis"` +< Device *string `yaml:"device"` +< KeyFile Resource `yaml:"key_file"` +< Label *string `yaml:"label"` +< Name string `yaml:"name"` +< Options []LuksOption `yaml:"options"` +< UUID *string `yaml:"uuid"` +< WipeVolume *bool `yaml:"wipe_volume"` +< } +< +< type LuksOption string +< +< type NodeGroup struct { +< ID *int `yaml:"id"` +< Name *string `yaml:"name"` +< } +< +< type NodeUser struct { +< ID *int `yaml:"id"` +< Name *string `yaml:"name"` +< } +< +< type Partition struct { +< GUID *string `yaml:"guid"` +< Label *string `yaml:"label"` +< Number int `yaml:"number"` +< Resize *bool `yaml:"resize"` +< ShouldExist *bool `yaml:"should_exist"` +< SizeMiB *int `yaml:"size_mib"` +< StartMiB *int `yaml:"start_mib"` +< TypeGUID *string `yaml:"type_guid"` +< WipePartitionEntry *bool `yaml:"wipe_partition_entry"` +< } +< +< type Passwd struct { +< Groups []PasswdGroup `yaml:"groups"` +< Users []PasswdUser `yaml:"users"` +< } +< +< type PasswdGroup struct { +< Gid *int `yaml:"gid"` +< Name string `yaml:"name"` +< PasswordHash *string `yaml:"password_hash"` +< ShouldExist *bool `yaml:"should_exist"` +< System *bool `yaml:"system"` +< } +< +< type PasswdUser struct { +< Gecos *string `yaml:"gecos"` +< Groups []Group `yaml:"groups"` +< HomeDir *string `yaml:"home_dir"` +< Name string `yaml:"name"` +< NoCreateHome *bool `yaml:"no_create_home"` +< NoLogInit *bool `yaml:"no_log_init"` +< NoUserGroup *bool `yaml:"no_user_group"` +< PasswordHash *string `yaml:"password_hash"` +< PrimaryGroup *string `yaml:"primary_group"` +< ShouldExist *bool `yaml:"should_exist"` +< SSHAuthorizedKeys []SSHAuthorizedKey `yaml:"ssh_authorized_keys"` +< Shell *string `yaml:"shell"` +< System *bool `yaml:"system"` +< 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"` +< Name string `yaml:"name"` +< Options []RaidOption `yaml:"options"` +< Spares *int `yaml:"spares"` +< } +< +< 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 { +< TLS TLS `yaml:"tls"` +< } +< +< type Storage struct { +< Directories []Directory `yaml:"directories"` +< Disks []Disk `yaml:"disks"` +< Files []File `yaml:"files"` +< Filesystems []Filesystem `yaml:"filesystems"` +< Links []Link `yaml:"links"` +< Luks []Luks `yaml:"luks"` +< Raid []Raid `yaml:"raid"` +< Trees []Tree `yaml:"trees" fcct:"auto_skip"` // Added, not in ignition spec +< } +< +< type Systemd struct { +< Units []Unit `yaml:"units"` +< } +< +< type Tang struct { +< Thumbprint *string `yaml:"thumbprint"` +< URL string `yaml:"url"` +< } +< +< type TLS struct { +< CertificateAuthorities []Resource `yaml:"certificate_authorities"` +< } +< +< type Timeouts struct { +< HTTPResponseHeaders *int `yaml:"http_response_headers"` +< 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"` +< Enabled *bool `yaml:"enabled"` +< Mask *bool `yaml:"mask"` +< Name string `yaml:"name"` +< } +< +< type Verification struct { +< Hash *string `yaml:"hash"` +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_4_exp/translate.go updated/vendor/github.com/coreos/fcct/base/v0_4_exp/translate.go +1,400d0 +< // 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_4_exp +< +< import ( +< "fmt" +< "io/ioutil" +< "os" +< "path/filepath" +< "strings" +< "text/template" +< +< baseutil "github.com/coreos/fcct/base/util" +< "github.com/coreos/fcct/config/common" +< "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_3_experimental/types" +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< ) +< +< var ( +< mountUnitTemplate = template.Must(template.New("unit").Parse(`# Generated by FCCT +< [Unit] +< {{- if .Remote }} +< Before=remote-fs.target +< DefaultDependencies=no +< {{- else }} +< Before=local-fs.target +< {{- end }} +< Requires=systemd-fsck@{{.EscapedDevice}}.service +< After=systemd-fsck@{{.EscapedDevice}}.service +< +< [Mount] +< Where={{.Path}} +< What={{.Device}} +< Type={{.Format}} +< {{- if .MountOptions }} +< Options= +< {{- range $i, $opt := .MountOptions }} +< {{- if $i }},{{ end }} +< {{- $opt }} +< {{- end }} +< {{- end }} +< +< [Install] +< {{- if .Remote }} +< RequiredBy=remote-fs.target +< {{- else }} +< RequiredBy=local-fs.target +< {{- end }}`)) +< ) +< +< // ToIgn3_3Unvalidated translates the config to an Ignition config. It also returns the set of translations +< // it did so paths in the resultant config can be tracked back to their source in the source config. +< // No config validation is performed on input or output. +< func (c Config) ToIgn3_3Unvalidated(options common.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) +< tr.AddCustomTranslator(translateResource) +< +< tm, r := translate.Prefixed(tr, "ignition", &c.Ignition, &ret.Ignition) +< translate.MergeP(tr, tm, &r, "passwd", &c.Passwd, &ret.Passwd) +< translate.MergeP(tr, tm, &r, "storage", &c.Storage, &ret.Storage) +< translate.MergeP(tr, tm, &r, "systemd", &c.Systemd, &ret.Systemd) +< +< c.addMountUnits(&ret, &tm) +< +< tm2, r2 := c.processTrees(&ret, options) +< tm.Merge(tm2) +< r.Merge(r2) +< +< if r.IsFatal() { +< return types.Config{}, translate.TranslationSet{}, r +< } +< return ret, tm, r +< } +< +< func translateIgnition(from Ignition, options common.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 common.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 common.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, common.ErrNoFilesDir) +< return +< } +< +< // calculate file path within FilesDir and check for +< // path traversal +< filePath := filepath.Join(options.FilesDir, *from.Local) +< if err := baseutil.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 := baseutil.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 := baseutil.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 translateDirectory(from Directory, options common.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 common.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 common.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, common.ErrNoFilesDir) +< return ts, r +< } +< +< // calculate base path within FilesDir and check for +< // path traversal +< srcBaseDir := filepath.Join(options.FilesDir, tree.Local) +< if err := baseutil.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, common.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 common.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, common.ErrNodeExists) +< return nil +< } +< } else { +< if t.Exists(destPath) { +< r.AddOnError(yamlPath, common.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 := baseutil.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, common.ErrNodeExists) +< return nil +< } +< } else { +< if t.Exists(destPath) { +< r.AddOnError(yamlPath, common.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, common.ErrFileType) +< return nil +< } +< return nil +< }) +< r.AddOnError(yamlPath, err) +< } +< +< func (c Config) addMountUnits(config *types.Config, ts *translate.TranslationSet) { +< if len(c.Storage.Filesystems) == 0 { +< return +< } +< var rendered types.Config +< renderedTranslations := translate.NewTranslationSet("yaml", "json") +< for i, fs := range c.Storage.Filesystems { +< if fs.WithMountUnit == nil || !*fs.WithMountUnit { +< continue +< } +< fromPath := path.New("yaml", "storage", "filesystems", i, "with_mount_unit") +< remote := false +< // check filesystems targeting /dev/mapper devices against LUKS to determine if a +< // remote mount is needed +< if strings.HasPrefix(fs.Device, "/dev/mapper/") || strings.HasPrefix(fs.Device, "/dev/disk/by-id/dm-name-") { +< for _, luks := range c.Storage.Luks { +< // LUKS devices are opened with their name specified +< if fs.Device == fmt.Sprintf("/dev/mapper/%s", luks.Name) || fs.Device == fmt.Sprintf("/dev/disk/by-id/dm-name-%s", luks.Name) { +< if luks.Clevis != nil && len(luks.Clevis.Tang) > 0 { +< remote = true +< break +< } +< } +< } +< } +< newUnit := mountUnitFromFS(fs, remote) +< unitPath := path.New("json", "systemd", "units", len(rendered.Systemd.Units)) +< rendered.Systemd.Units = append(rendered.Systemd.Units, newUnit) +< renderedTranslations.AddFromCommonSource(fromPath, unitPath, newUnit) +< } +< retConfig, retTranslations := baseutil.MergeTranslatedConfigs(rendered, renderedTranslations, *config, *ts) +< *config = retConfig.(types.Config) +< *ts = retTranslations +< } +< +< func mountUnitFromFS(fs Filesystem, remote bool) types.Unit { +< context := struct { +< *Filesystem +< EscapedDevice string +< Remote bool +< }{ +< Filesystem: &fs, +< EscapedDevice: unit.UnitNamePathEscape(fs.Device), +< Remote: remote, +< } +< contents := strings.Builder{} +< err := mountUnitTemplate.Execute(&contents, context) +< 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()), +< } +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_4_exp/translate_test.go updated/vendor/github.com/coreos/fcct/base/v0_4_exp/translate_test.go +1,1438d0 +< // 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_4_exp +< +< import ( +< "io/ioutil" +< "net" +< "os" +< "path/filepath" +< "strings" +< "testing" +< +< baseutil "github.com/coreos/fcct/base/util" +< "github.com/coreos/fcct/config/common" +< "github.com/coreos/fcct/translate" +< +< "github.com/coreos/ignition/v2/config/util" +< "github.com/coreos/ignition/v2/config/v3_3_experimental/types" +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< "github.com/stretchr/testify/assert" +< ) +< +< // Most of this is covered by the Ignition translator generic tests, so just test the custom bits +< +< // TestTranslateFile tests translating the ct storage.files.[i] entries to ignition storage.files.[i] entries. +< func TestTranslateFile(t *testing.T) { +< zzz := "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" +< zzz_gz := "data:;base64,H4sIAAAAAAAC/6oajAAQAAD//5tA8d+VAAAA" +< random := "\xc0\x9cl\x01\x89i\xa5\xbfW\xe4\x1b\xf4J_\xb79P\xa3#\xa7" +< random_b64 := "data:;base64,wJxsAYlppb9X5Bv0Sl+3OVCjI6c=" +< +< filesDir, err := ioutil.TempDir("", "translate-test-") +< if err != nil { +< t.Error(err) +< return +< } +< defer os.RemoveAll(filesDir) +< fileContents := map[string]string{ +< "file-1": "file contents\n", +< "file-2": zzz, +< "file-3": random, +< } +< for name, contents := range fileContents { +< err := ioutil.WriteFile(filepath.Join(filesDir, name), []byte(contents), 0644) +< if err != nil { +< t.Error(err) +< return +< } +< } +< +< tests := []struct { +< in File +< out types.File +< exceptions []translate.Translation +< report string +< options common.TranslateOptions +< }{ +< { +< File{}, +< types.File{}, +< nil, +< "", +< common.TranslateOptions{}, +< }, +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< File{ +< Path: "/foo", +< Group: NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Mode: util.IntToPtr(420), +< Append: []Resource{ +< { +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< HTTPHeaders: HTTPHeaders{ +< HTTPHeader{ +< Name: "Header", +< Value: util.StrToPtr("this isn't validated"), +< }, +< }, +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< { +< Inline: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< HTTPHeaders: HTTPHeaders{ +< HTTPHeader{ +< Name: "Header", +< Value: util.StrToPtr("this isn't validated"), +< }, +< }, +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< { +< Local: util.StrToPtr("file-1"), +< }, +< }, +< Overwrite: util.BoolToPtr(true), +< Contents: Resource{ +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< HTTPHeaders: HTTPHeaders{ +< HTTPHeader{ +< Name: "Header", +< Value: util.StrToPtr("this isn't validated"), +< }, +< }, +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< Group: types.NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: types.NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Overwrite: util.BoolToPtr(true), +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Mode: util.IntToPtr(420), +< Append: []types.Resource{ +< { +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< HTTPHeaders: types.HTTPHeaders{ +< types.HTTPHeader{ +< Name: "Header", +< Value: util.StrToPtr("this isn't validated"), +< }, +< }, +< Verification: types.Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< { +< Source: util.StrToPtr("data:,hello"), +< Compression: util.StrToPtr("gzip"), +< HTTPHeaders: types.HTTPHeaders{ +< types.HTTPHeader{ +< Name: "Header", +< Value: util.StrToPtr("this isn't validated"), +< }, +< }, +< Verification: types.Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< { +< Source: util.StrToPtr("data:,file%20contents%0A"), +< }, +< }, +< Contents: types.Resource{ +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< HTTPHeaders: types.HTTPHeaders{ +< types.HTTPHeader{ +< Name: "Header", +< Value: util.StrToPtr("this isn't validated"), +< }, +< }, +< Verification: types.Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< { +< From: path.New("yaml", "append", 1, "inline"), +< To: path.New("json", "append", 1, "source"), +< }, +< { +< From: path.New("yaml", "append", 2, "local"), +< To: path.New("json", "append", 2, "source"), +< }, +< }, +< "", +< common.TranslateOptions{ +< FilesDir: filesDir, +< }, +< }, +< // inline file contents +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< // String is too short for auto gzip compression +< Inline: util.StrToPtr("xyzzy"), +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,xyzzy"), +< }, +< }, +< }, +< []translate.Translation{ +< { +< From: path.New("yaml", "contents", "inline"), +< To: path.New("json", "contents", "source"), +< }, +< }, +< "", +< common.TranslateOptions{}, +< }, +< // local file contents +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< Local: util.StrToPtr("file-1"), +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,file%20contents%0A"), +< }, +< }, +< }, +< []translate.Translation{ +< { +< From: path.New("yaml", "contents", "local"), +< To: path.New("json", "contents", "source"), +< }, +< }, +< "", +< common.TranslateOptions{ +< FilesDir: filesDir, +< }, +< }, +< // filesDir not specified +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< Local: util.StrToPtr("file-1"), +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< }, +< []translate.Translation{}, +< "error at $.contents.local: " + common.ErrNoFilesDir.Error() + "\n", +< common.TranslateOptions{}, +< }, +< // attempted directory traversal +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< Local: util.StrToPtr("../file-1"), +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< }, +< []translate.Translation{}, +< "error at $.contents.local: " + common.ErrFilesDirEscape.Error() + "\n", +< common.TranslateOptions{ +< FilesDir: filesDir, +< }, +< }, +< // attempted inclusion of nonexistent file +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< Local: util.StrToPtr("file-missing"), +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< }, +< []translate.Translation{}, +< "error at $.contents.local: open " + filepath.Join(filesDir, "file-missing") + ": no such file or directory\n", +< common.TranslateOptions{ +< FilesDir: filesDir, +< }, +< }, +< // inline and local automatic file encoding +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< // gzip +< Inline: util.StrToPtr(zzz), +< }, +< Append: []Resource{ +< { +< // gzip +< Local: util.StrToPtr("file-2"), +< }, +< { +< // base64 +< Inline: util.StrToPtr(random), +< }, +< { +< // base64 +< Local: util.StrToPtr("file-3"), +< }, +< { +< // URL-escaped +< Inline: util.StrToPtr(zzz), +< Compression: util.StrToPtr("invalid"), +< }, +< { +< // URL-escaped +< Local: util.StrToPtr("file-2"), +< Compression: util.StrToPtr("invalid"), +< }, +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr(zzz_gz), +< Compression: util.StrToPtr("gzip"), +< }, +< Append: []types.Resource{ +< { +< Source: util.StrToPtr(zzz_gz), +< Compression: util.StrToPtr("gzip"), +< }, +< { +< Source: util.StrToPtr(random_b64), +< }, +< { +< Source: util.StrToPtr(random_b64), +< }, +< { +< Source: util.StrToPtr("data:," + zzz), +< Compression: util.StrToPtr("invalid"), +< }, +< { +< Source: util.StrToPtr("data:," + zzz), +< Compression: util.StrToPtr("invalid"), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< { +< From: path.New("yaml", "contents", "inline"), +< To: path.New("json", "contents", "source"), +< }, +< { +< From: path.New("yaml", "contents", "inline"), +< To: path.New("json", "contents", "compression"), +< }, +< { +< From: path.New("yaml", "append", 0, "local"), +< To: path.New("json", "append", 0, "source"), +< }, +< { +< From: path.New("yaml", "append", 0, "local"), +< To: path.New("json", "append", 0, "compression"), +< }, +< { +< From: path.New("yaml", "append", 1, "inline"), +< To: path.New("json", "append", 1, "source"), +< }, +< { +< From: path.New("yaml", "append", 2, "local"), +< To: path.New("json", "append", 2, "source"), +< }, +< { +< From: path.New("yaml", "append", 3, "inline"), +< To: path.New("json", "append", 3, "source"), +< }, +< { +< From: path.New("yaml", "append", 4, "local"), +< To: path.New("json", "append", 4, "source"), +< }, +< }, +< "", +< common.TranslateOptions{ +< FilesDir: filesDir, +< }, +< }, +< // Test disable automatic gzip compression +< { +< File{ +< Path: "/foo", +< Contents: Resource{ +< Inline: util.StrToPtr(zzz), +< }, +< }, +< types.File{ +< Node: types.Node{ +< Path: "/foo", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:," + zzz), +< }, +< }, +< }, +< []translate.Translation{ +< { +< From: path.New("yaml", "contents", "inline"), +< To: path.New("json", "contents", "source"), +< }, +< }, +< "", +< common.TranslateOptions{ +< NoResourceAutoCompression: true, +< }, +< }, +< } +< +< for i, test := range tests { +< actual, translations, r := translateFile(test.in, test.options) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, test.report, r.String(), "#%d: bad report", i) +< baseutil.VerifyTranslations(t, translations, test.exceptions, "#%d", i) +< } +< } +< +< // TestTranslateDirectory tests translating the ct storage.directories.[i] entries to ignition storage.directories.[i] entires. +< func TestTranslateDirectory(t *testing.T) { +< tests := []struct { +< in Directory +< out types.Directory +< }{ +< { +< Directory{}, +< types.Directory{}, +< }, +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< Directory{ +< Path: "/foo", +< Group: NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Mode: util.IntToPtr(420), +< Overwrite: util.BoolToPtr(true), +< }, +< types.Directory{ +< Node: types.Node{ +< Path: "/foo", +< Group: types.NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: types.NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Overwrite: util.BoolToPtr(true), +< }, +< DirectoryEmbedded1: types.DirectoryEmbedded1{ +< Mode: util.IntToPtr(420), +< }, +< }, +< }, +< } +< +< for i, test := range tests { +< actual, _, r := translateDirectory(test.in, common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +< +< // TestTranslateLink tests translating the ct storage.links.[i] entries to ignition storage.links.[i] entires. +< func TestTranslateLink(t *testing.T) { +< tests := []struct { +< in Link +< out types.Link +< }{ +< { +< Link{}, +< types.Link{}, +< }, +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< Link{ +< Path: "/foo", +< Group: NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Overwrite: util.BoolToPtr(true), +< Target: "/bar", +< Hard: util.BoolToPtr(false), +< }, +< types.Link{ +< Node: types.Node{ +< Path: "/foo", +< Group: types.NodeGroup{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("foobar"), +< }, +< User: types.NodeUser{ +< ID: util.IntToPtr(1), +< Name: util.StrToPtr("bazquux"), +< }, +< Overwrite: util.BoolToPtr(true), +< }, +< LinkEmbedded1: types.LinkEmbedded1{ +< Target: "/bar", +< Hard: util.BoolToPtr(false), +< }, +< }, +< }, +< } +< +< for i, test := range tests { +< actual, _, r := translateLink(test.in, common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +< +< // TestTranslateFilesystem tests translating the fcct storage.filesystems.[i] entries to ignition storage.filesystems.[i] entries. +< func TestTranslateFilesystem(t *testing.T) { +< tests := []struct { +< in Filesystem +< out types.Filesystem +< }{ +< { +< Filesystem{}, +< types.Filesystem{}, +< }, +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< Filesystem{ +< Device: "/foo", +< Format: util.StrToPtr("/bar"), +< Label: util.StrToPtr("/baz"), +< MountOptions: []string{"yes", "no", "maybe"}, +< Options: []string{"foo", "foo", "bar"}, +< Path: util.StrToPtr("/quux"), +< UUID: util.StrToPtr("1234"), +< WipeFilesystem: util.BoolToPtr(true), +< WithMountUnit: util.BoolToPtr(true), +< }, +< types.Filesystem{ +< Device: "/foo", +< Format: util.StrToPtr("/bar"), +< Label: util.StrToPtr("/baz"), +< MountOptions: []types.MountOption{"yes", "no", "maybe"}, +< Options: []types.FilesystemOption{"foo", "foo", "bar"}, +< Path: util.StrToPtr("/quux"), +< UUID: util.StrToPtr("1234"), +< WipeFilesystem: util.BoolToPtr(true), +< }, +< }, +< } +< +< for i, test := range tests { +< // Filesystem doesn't have a custom translator, so embed in a +< // complete config +< in := Config{ +< Storage: Storage{ +< Filesystems: []Filesystem{test.in}, +< }, +< } +< expected := []types.Filesystem{test.out} +< actual, _, r := in.ToIgn3_3Unvalidated(common.TranslateOptions{}) +< assert.Equal(t, expected, actual.Storage.Filesystems, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +< +< // TestTranslateMountUnit tests the FCCT storage.filesystems.[i].with_mount_unit flag. +< func TestTranslateMountUnit(t *testing.T) { +< tests := []struct { +< in Config +< out types.Config +< }{ +< // local mount with options, overridden enabled flag +< { +< Config{ +< Storage: Storage{ +< Filesystems: []Filesystem{ +< { +< Device: "/dev/disk/by-label/foo", +< Format: util.StrToPtr("ext4"), +< MountOptions: []string{"ro", "noatime"}, +< Path: util.StrToPtr("/var/lib/containers"), +< WithMountUnit: util.BoolToPtr(true), +< }, +< }, +< }, +< Systemd: Systemd{ +< Units: []Unit{ +< { +< Name: "var-lib-containers.mount", +< Enabled: util.BoolToPtr(false), +< }, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.3.0-experimental", +< }, +< Storage: types.Storage{ +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/disk/by-label/foo", +< Format: util.StrToPtr("ext4"), +< MountOptions: []types.MountOption{"ro", "noatime"}, +< Path: util.StrToPtr("/var/lib/containers"), +< }, +< }, +< }, +< Systemd: types.Systemd{ +< Units: []types.Unit{ +< { +< Enabled: util.BoolToPtr(false), +< Contents: util.StrToPtr(`# Generated by FCCT +< [Unit] +< Before=local-fs.target +< Requires=systemd-fsck@dev-disk-by\x2dlabel-foo.service +< After=systemd-fsck@dev-disk-by\x2dlabel-foo.service +< +< [Mount] +< Where=/var/lib/containers +< What=/dev/disk/by-label/foo +< Type=ext4 +< Options=ro,noatime +< +< [Install] +< RequiredBy=local-fs.target`), +< Name: "var-lib-containers.mount", +< }, +< }, +< }, +< }, +< }, +< // remote mount with options +< { +< Config{ +< Storage: Storage{ +< Filesystems: []Filesystem{ +< { +< Device: "/dev/mapper/foo-bar", +< Format: util.StrToPtr("ext4"), +< MountOptions: []string{"ro", "noatime"}, +< Path: util.StrToPtr("/var/lib/containers"), +< WithMountUnit: util.BoolToPtr(true), +< }, +< }, +< Luks: []Luks{ +< { +< Name: "foo-bar", +< Device: util.StrToPtr("/dev/bar"), +< Clevis: &Clevis{ +< Tang: []Tang{ +< { +< URL: "http://example.com", +< }, +< }, +< }, +< }, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.3.0-experimental", +< }, +< Storage: types.Storage{ +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/mapper/foo-bar", +< Format: util.StrToPtr("ext4"), +< MountOptions: []types.MountOption{"ro", "noatime"}, +< Path: util.StrToPtr("/var/lib/containers"), +< }, +< }, +< Luks: []types.Luks{ +< { +< Name: "foo-bar", +< Device: util.StrToPtr("/dev/bar"), +< Clevis: &types.Clevis{ +< Tang: []types.Tang{ +< { +< URL: "http://example.com", +< }, +< }, +< }, +< }, +< }, +< }, +< Systemd: types.Systemd{ +< Units: []types.Unit{ +< { +< Enabled: util.BoolToPtr(true), +< Contents: util.StrToPtr(`# Generated by FCCT +< [Unit] +< Before=remote-fs.target +< DefaultDependencies=no +< Requires=systemd-fsck@dev-mapper-foo\x2dbar.service +< After=systemd-fsck@dev-mapper-foo\x2dbar.service +< +< [Mount] +< Where=/var/lib/containers +< What=/dev/mapper/foo-bar +< Type=ext4 +< Options=ro,noatime +< +< [Install] +< RequiredBy=remote-fs.target`), +< Name: "var-lib-containers.mount", +< }, +< }, +< }, +< }, +< }, +< // local mount, no options +< { +< Config{ +< Storage: Storage{ +< Filesystems: []Filesystem{ +< { +< Device: "/dev/disk/by-label/foo", +< Format: util.StrToPtr("ext4"), +< Path: util.StrToPtr("/var/lib/containers"), +< WithMountUnit: util.BoolToPtr(true), +< }, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.3.0-experimental", +< }, +< Storage: types.Storage{ +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/disk/by-label/foo", +< Format: util.StrToPtr("ext4"), +< Path: util.StrToPtr("/var/lib/containers"), +< }, +< }, +< }, +< Systemd: types.Systemd{ +< Units: []types.Unit{ +< { +< Enabled: util.BoolToPtr(true), +< Contents: util.StrToPtr(`# Generated by FCCT +< [Unit] +< Before=local-fs.target +< Requires=systemd-fsck@dev-disk-by\x2dlabel-foo.service +< After=systemd-fsck@dev-disk-by\x2dlabel-foo.service +< +< [Mount] +< Where=/var/lib/containers +< What=/dev/disk/by-label/foo +< Type=ext4 +< +< [Install] +< RequiredBy=local-fs.target`), +< Name: "var-lib-containers.mount", +< }, +< }, +< }, +< }, +< }, +< // overridden mount unit +< { +< Config{ +< Storage: Storage{ +< Filesystems: []Filesystem{ +< { +< Device: "/dev/disk/by-label/foo", +< Format: util.StrToPtr("ext4"), +< Path: util.StrToPtr("/var/lib/containers"), +< WithMountUnit: util.BoolToPtr(true), +< }, +< }, +< }, +< Systemd: Systemd{ +< Units: []Unit{ +< { +< Name: "var-lib-containers.mount", +< Contents: util.StrToPtr("[Service]\nExecStart=/bin/false\n"), +< }, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.3.0-experimental", +< }, +< Storage: types.Storage{ +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/disk/by-label/foo", +< Format: util.StrToPtr("ext4"), +< Path: util.StrToPtr("/var/lib/containers"), +< }, +< }, +< }, +< Systemd: types.Systemd{ +< Units: []types.Unit{ +< { +< Enabled: util.BoolToPtr(true), +< Contents: util.StrToPtr("[Service]\nExecStart=/bin/false\n"), +< Name: "var-lib-containers.mount", +< }, +< }, +< }, +< }, +< }, +< } +< +< for i, test := range tests { +< out, _, r := test.in.ToIgn3_3Unvalidated(common.TranslateOptions{}) +< assert.Equal(t, test.out, out, "#%d: bad output", i) +< assert.Equal(t, report.Report{}, r, "#%d: expected empty report", i) +< } +< } +< +< // TestTranslateTree tests translating the FCC storage.trees.[i] entries to ignition storage.files.[i] entries. +< func TestTranslateTree(t *testing.T) { +< tests := []struct { +< options *common.TranslateOptions // defaulted if not specified +< dirDirs map[string]os.FileMode // relative path -> mode +< dirFiles map[string]os.FileMode // relative path -> mode +< dirLinks map[string]string // relative path -> target +< dirSockets []string // relative path +< inTrees []Tree +< inFiles []File +< inDirs []Directory +< inLinks []Link +< outFiles []types.File +< outLinks []types.Link +< report string +< }{ +< // smoke test +< {}, +< // basic functionality +< { +< dirFiles: map[string]os.FileMode{ +< "tree/executable": 0700, +< "tree/file": 0600, +< "tree/overridden": 0644, +< "tree/overridden-executable": 0700, +< "tree/subdir/file": 0644, +< // compressed contents +< "tree/subdir/subdir/subdir/subdir/subdir/subdir/subdir/subdir/subdir/file": 0644, +< "tree2/file": 0600, +< }, +< dirLinks: map[string]string{ +< "tree/subdir/bad-link": "../nonexistent", +< "tree/subdir/link": "../file", +< "tree/subdir/overridden-link": "../file", +< }, +< inTrees: []Tree{ +< { +< Local: "tree", +< }, +< { +< Local: "tree2", +< Path: util.StrToPtr("/etc"), +< }, +< }, +< inFiles: []File{ +< { +< Path: "/overridden", +< Mode: util.IntToPtr(0600), +< User: NodeUser{ +< Name: util.StrToPtr("bovik"), +< }, +< }, +< { +< Path: "/overridden-executable", +< Mode: util.IntToPtr(0600), +< User: NodeUser{ +< Name: util.StrToPtr("bovik"), +< }, +< }, +< }, +< inLinks: []Link{ +< { +< Path: "/subdir/overridden-link", +< User: NodeUser{ +< Name: util.StrToPtr("bovik"), +< }, +< }, +< }, +< outFiles: []types.File{ +< { +< Node: types.Node{ +< Path: "/overridden", +< User: types.NodeUser{ +< Name: util.StrToPtr("bovik"), +< }, +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,tree%2Foverridden"), +< }, +< Mode: util.IntToPtr(0600), +< }, +< }, +< { +< Node: types.Node{ +< Path: "/overridden-executable", +< User: types.NodeUser{ +< Name: util.StrToPtr("bovik"), +< }, +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,tree%2Foverridden-executable"), +< }, +< Mode: util.IntToPtr(0600), +< }, +< }, +< { +< Node: types.Node{ +< Path: "/executable", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,tree%2Fexecutable"), +< }, +< Mode: util.IntToPtr(0755), +< }, +< }, +< { +< Node: types.Node{ +< Path: "/file", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,tree%2Ffile"), +< }, +< Mode: util.IntToPtr(0644), +< }, +< }, +< { +< Node: types.Node{ +< Path: "/subdir/file", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,tree%2Fsubdir%2Ffile"), +< }, +< Mode: util.IntToPtr(0644), +< }, +< }, +< { +< Node: types.Node{ +< Path: "/subdir/subdir/subdir/subdir/subdir/subdir/subdir/subdir/subdir/file", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:;base64,H4sIAAAAAAAC/yopSk3VLy5NSsksIptKy8xJBQQAAP//gkRzjkgAAAA="), +< Compression: util.StrToPtr("gzip"), +< }, +< Mode: util.IntToPtr(0644), +< }, +< }, +< { +< Node: types.Node{ +< Path: "/etc/file", +< }, +< FileEmbedded1: types.FileEmbedded1{ +< Contents: types.Resource{ +< Source: util.StrToPtr("data:,tree2%2Ffile"), +< }, +< Mode: util.IntToPtr(0644), +< }, +< }, +< }, +< outLinks: []types.Link{ +< { +< Node: types.Node{ +< Path: "/subdir/overridden-link", +< User: types.NodeUser{ +< Name: util.StrToPtr("bovik"), +< }, +< }, +< LinkEmbedded1: types.LinkEmbedded1{ +< Target: "../file", +< }, +< }, +< { +< Node: types.Node{ +< Path: "/subdir/bad-link", +< }, +< LinkEmbedded1: types.LinkEmbedded1{ +< Target: "../nonexistent", +< }, +< }, +< { +< Node: types.Node{ +< Path: "/subdir/link", +< }, +< LinkEmbedded1: types.LinkEmbedded1{ +< Target: "../file", +< }, +< }, +< }, +< }, +< // collisions +< { +< dirFiles: map[string]os.FileMode{ +< "tree0/file": 0600, +< "tree1/directory": 0600, +< "tree2/link": 0600, +< "tree3/file-partial": 0600, // should be okay +< "tree4/link-partial": 0600, +< "tree5/tree-file": 0600, // set up for tree/tree collision +< "tree6/tree-file": 0600, +< "tree15/tree-link": 0600, +< }, +< dirLinks: map[string]string{ +< "tree7/file": "file", +< "tree8/directory": "file", +< "tree9/link": "file", +< "tree10/file-partial": "file", +< "tree11/link-partial": "file", // should be okay +< "tree12/tree-file": "file", +< "tree13/tree-link": "file", // set up for tree/tree collision +< "tree14/tree-link": "file", +< }, +< inTrees: []Tree{ +< { +< Local: "tree0", +< }, +< { +< Local: "tree1", +< }, +< { +< Local: "tree2", +< }, +< { +< Local: "tree3", +< }, +< { +< Local: "tree4", +< }, +< { +< Local: "tree5", +< }, +< { +< Local: "tree6", +< }, +< { +< Local: "tree7", +< }, +< { +< Local: "tree8", +< }, +< { +< Local: "tree9", +< }, +< { +< Local: "tree10", +< }, +< { +< Local: "tree11", +< }, +< { +< Local: "tree12", +< }, +< { +< Local: "tree13", +< }, +< { +< Local: "tree14", +< }, +< { +< Local: "tree15", +< }, +< }, +< inFiles: []File{ +< { +< Path: "/file", +< Contents: Resource{ +< Source: util.StrToPtr("data:,foo"), +< }, +< }, +< { +< Path: "/file-partial", +< }, +< }, +< inDirs: []Directory{ +< { +< Path: "/directory", +< }, +< }, +< inLinks: []Link{ +< { +< Path: "/link", +< Target: "file", +< }, +< { +< Path: "/link-partial", +< }, +< }, +< report: "error at $.storage.trees.0: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.1: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.2: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.4: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.6: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.7: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.8: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.9: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.10: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.12: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.14: " + common.ErrNodeExists.Error() + "\n" + +< "error at $.storage.trees.15: " + common.ErrNodeExists.Error() + "\n", +< }, +< // files-dir escape +< { +< inTrees: []Tree{ +< { +< Local: "../escape", +< }, +< }, +< report: "error at $.storage.trees.0: " + common.ErrFilesDirEscape.Error() + "\n", +< }, +< // no files-dir +< { +< options: &common.TranslateOptions{}, +< inTrees: []Tree{ +< { +< Local: "tree", +< }, +< }, +< report: "error at $.storage.trees.0: " + common.ErrNoFilesDir.Error() + "\n", +< }, +< // non-file/dir/symlink in directory tree +< { +< dirSockets: []string{ +< "tree/socket", +< }, +< inTrees: []Tree{ +< { +< Local: "tree", +< }, +< }, +< report: "error at $.storage.trees.0: " + common.ErrFileType.Error() + "\n", +< }, +< // unreadable file +< { +< dirDirs: map[string]os.FileMode{ +< "tree/subdir": 0000, +< "tree2": 0000, +< }, +< dirFiles: map[string]os.FileMode{ +< "tree/file": 0000, +< }, +< inTrees: []Tree{ +< { +< Local: "tree", +< }, +< { +< Local: "tree2", +< }, +< }, +< report: "error at $.storage.trees.0: open %FilesDir%/tree/file: permission denied\n" + +< "error at $.storage.trees.0: open %FilesDir%/tree/subdir: permission denied\n" + +< "error at $.storage.trees.1: open %FilesDir%/tree2: permission denied\n", +< }, +< // local is not a directory +< { +< dirFiles: map[string]os.FileMode{ +< "tree": 0600, +< }, +< inTrees: []Tree{ +< { +< Local: "tree", +< }, +< { +< Local: "nonexistent", +< }, +< }, +< report: "error at $.storage.trees.0: " + common.ErrTreeNotDirectory.Error() + "\n" + +< "error at $.storage.trees.1: stat %FilesDir%/nonexistent: no such file or directory\n", +< }, +< } +< +< for i, test := range tests { +< filesDir, err := ioutil.TempDir("", "translate-test-") +< if err != nil { +< t.Error(err) +< return +< } +< defer os.RemoveAll(filesDir) +< for path, mode := range test.dirDirs { +< absPath := filepath.Join(filesDir, path) +< if err := os.MkdirAll(absPath, 0755); err != nil { +< t.Error(err) +< return +< } +< if err := os.Chmod(absPath, mode); err != nil { +< t.Error(err) +< return +< } +< } +< for path, mode := range test.dirFiles { +< absPath := filepath.Join(filesDir, path) +< if err := os.MkdirAll(filepath.Dir(absPath), 0755); err != nil { +< t.Error(err) +< return +< } +< if err := ioutil.WriteFile(absPath, []byte(path), mode); err != nil { +< t.Error(err) +< return +< } +< } +< for path, target := range test.dirLinks { +< absPath := filepath.Join(filesDir, path) +< if err := os.MkdirAll(filepath.Dir(absPath), 0755); err != nil { +< t.Error(err) +< return +< } +< if err := os.Symlink(target, absPath); err != nil { +< t.Error(err) +< return +< } +< } +< for _, path := range test.dirSockets { +< absPath := filepath.Join(filesDir, path) +< if err := os.MkdirAll(filepath.Dir(absPath), 0755); err != nil { +< t.Error(err) +< return +< } +< listener, err := net.ListenUnix("unix", &net.UnixAddr{ +< Name: absPath, +< Net: "unix", +< }) +< if err != nil { +< t.Error(err) +< return +< } +< defer listener.Close() +< } +< +< config := Config{ +< Storage: Storage{ +< Files: test.inFiles, +< Directories: test.inDirs, +< Links: test.inLinks, +< Trees: test.inTrees, +< }, +< } +< options := common.TranslateOptions{ +< FilesDir: filesDir, +< } +< if test.options != nil { +< options = *test.options +< } +< actual, _, r := config.ToIgn3_3Unvalidated(options) +< +< expectedReport := strings.ReplaceAll(test.report, "%FilesDir%", filesDir) +< assert.Equal(t, expectedReport, r.String(), "#%d: bad report", i) +< if expectedReport != "" { +< continue +< } +< +< assert.Equal(t, test.outFiles, actual.Storage.Files, "#%d: files mismatch", i) +< assert.Equal(t, []types.Directory(nil), actual.Storage.Directories, "#%d: directories mismatch", i) +< assert.Equal(t, test.outLinks, actual.Storage.Links, "#%d: links mismatch", i) +< } +< } +< +< // TestTranslateIgnition tests translating the ct config.ignition to the ignition config.ignition section. +< // It ensure that the version is set as well. +< func TestTranslateIgnition(t *testing.T) { +< tests := []struct { +< in Ignition +< out types.Ignition +< }{ +< { +< Ignition{}, +< types.Ignition{ +< Version: "3.3.0-experimental", +< }, +< }, +< { +< Ignition{ +< Config: IgnitionConfig{ +< Merge: []Resource{ +< { +< Inline: util.StrToPtr("xyzzy"), +< }, +< }, +< Replace: Resource{ +< Inline: util.StrToPtr("xyzzy"), +< }, +< }, +< }, +< types.Ignition{ +< Version: "3.3.0-experimental", +< Config: types.IgnitionConfig{ +< Merge: []types.Resource{ +< { +< Source: util.StrToPtr("data:,xyzzy"), +< }, +< }, +< Replace: types.Resource{ +< Source: util.StrToPtr("data:,xyzzy"), +< }, +< }, +< }, +< }, +< { +< Ignition{ +< Proxy: Proxy{ +< HTTPProxy: util.StrToPtr("https://example.com:8080"), +< NoProxy: []string{"example.com"}, +< }, +< }, +< types.Ignition{ +< Version: "3.3.0-experimental", +< Proxy: types.Proxy{ +< HTTPProxy: util.StrToPtr("https://example.com:8080"), +< NoProxy: []types.NoProxyItem{types.NoProxyItem("example.com")}, +< }, +< }, +< }, +< { +< Ignition{ +< Security: Security{ +< TLS: TLS{ +< CertificateAuthorities: []Resource{ +< { +< Inline: util.StrToPtr("xyzzy"), +< }, +< }, +< }, +< }, +< }, +< types.Ignition{ +< Version: "3.3.0-experimental", +< Security: types.Security{ +< TLS: types.TLS{ +< CertificateAuthorities: []types.Resource{ +< { +< Source: util.StrToPtr("data:,xyzzy"), +< }, +< }, +< }, +< }, +< }, +< }, +< } +< for i, test := range tests { +< actual, _, r := translateIgnition(test.in, common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +< +< // TestToIgn3_3 tests the config.ToIgn3_3 function ensuring it will generate a valid config even when empty. Not much else is +< // tested since it uses the Ignition translation code which has it's own set of tests. +< func TestToIgn3_3(t *testing.T) { +< tests := []struct { +< in Config +< out types.Config +< }{ +< { +< Config{}, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.3.0-experimental", +< }, +< }, +< }, +< } +< for i, test := range tests { +< actual, _, r := test.in.ToIgn3_3Unvalidated(common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< } +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_4_exp/util.go updated/vendor/github.com/coreos/fcct/base/v0_4_exp/util.go +1,125d0 +< // 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_4_exp +< +< import ( +< "github.com/coreos/ignition/v2/config/v3_3_experimental/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 --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_4_exp/validate.go updated/vendor/github.com/coreos/fcct/base/v0_4_exp/validate.go +1,78d0 +< // 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_4_exp +< +< import ( +< baseutil "github.com/coreos/fcct/base/util" +< "github.com/coreos/fcct/config/common" +< +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< ) +< +< 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), common.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"), common.ErrMountUnitNoPath) +< } +< if fs.Format == nil || *fs.Format == "" { +< r.AddOnError(c.Append("format"), common.ErrMountUnitNoFormat) +< } +< return +< } +< +< func (d Directory) Validate(c path.ContextPath) (r report.Report) { +< if d.Mode != nil { +< r.AddOnWarn(c.Append("mode"), baseutil.CheckForDecimalMode(*d.Mode, true)) +< } +< return +< } +< +< func (f File) Validate(c path.ContextPath) (r report.Report) { +< if f.Mode != nil { +< r.AddOnWarn(c.Append("mode"), baseutil.CheckForDecimalMode(*f.Mode, false)) +< } +< return +< } +< +< func (t Tree) Validate(c path.ContextPath) (r report.Report) { +< if t.Local == "" { +< r.AddOnError(c, common.ErrTreeNoLocal) +< } +< return +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/base/v0_4_exp/validate_test.go updated/vendor/github.com/coreos/fcct/base/v0_4_exp/validate_test.go +1,214d0 +< // 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_4_exp +< +< import ( +< "testing" +< +< "github.com/coreos/fcct/config/common" +< +< "github.com/coreos/ignition/v2/config/util" +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< "github.com/stretchr/testify/assert" +< ) +< +< // TestValidateResource tests that multiple sources (i.e. urls and inline) are not allowed but zero or one sources are +< func TestValidateResource(t *testing.T) { +< tests := []struct { +< in Resource +< out error +< errPath path.ContextPath +< }{ +< {}, +< // source specified +< { +< // contains invalid (by the validator's definition) combinations of fields, +< // but the translator doesn't care and we can check they all get translated at once +< Resource{ +< Source: util.StrToPtr("http://example/com"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< nil, +< path.New("yaml"), +< }, +< // inline specified +< { +< Resource{ +< Inline: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< nil, +< path.New("yaml"), +< }, +< // local specified +< { +< Resource{ +< Local: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< nil, +< path.New("yaml"), +< }, +< // source + inline, invalid +< { +< Resource{ +< Source: util.StrToPtr("data:,hello"), +< Inline: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< common.ErrTooManyResourceSources, +< path.New("yaml", "source"), +< }, +< // source + local, invalid +< { +< Resource{ +< Source: util.StrToPtr("data:,hello"), +< Local: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< common.ErrTooManyResourceSources, +< path.New("yaml", "source"), +< }, +< // inline + local, invalid +< { +< Resource{ +< Inline: util.StrToPtr("hello"), +< Local: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< common.ErrTooManyResourceSources, +< path.New("yaml", "inline"), +< }, +< // source + inline + local, invalid +< { +< Resource{ +< Source: util.StrToPtr("data:,hello"), +< Inline: util.StrToPtr("hello"), +< Local: util.StrToPtr("hello"), +< Compression: util.StrToPtr("gzip"), +< Verification: Verification{ +< Hash: util.StrToPtr("this isn't validated"), +< }, +< }, +< common.ErrTooManyResourceSources, +< path.New("yaml", "source"), +< }, +< } +< +< for i, test := range tests { +< actual := test.in.Validate(path.New("yaml")) +< expected := report.Report{} +< expected.AddOnError(test.errPath, test.out) +< assert.Equal(t, expected, actual, "#%d: bad report", i) +< } +< } +< +< func TestValidateTree(t *testing.T) { +< tests := []struct { +< in Tree +< out error +< }{ +< { +< in: Tree{}, +< out: common.ErrTreeNoLocal, +< }, +< } +< +< for i, test := range tests { +< actual := test.in.Validate(path.New("yaml")) +< expected := report.Report{} +< expected.AddOnError(path.New("yaml"), test.out) +< assert.Equal(t, expected, actual, "#%d: bad report", i) +< } +< } +< +< func TestValidateMode(t *testing.T) { +< fileTests := []struct { +< in File +< out error +< }{ +< { +< in: File{}, +< out: nil, +< }, +< { +< in: File{ +< Mode: util.IntToPtr(0600), +< }, +< out: nil, +< }, +< { +< in: File{ +< Mode: util.IntToPtr(600), +< }, +< out: common.ErrDecimalMode, +< }, +< } +< +< for i, test := range fileTests { +< actual := test.in.Validate(path.New("yaml")) +< expected := report.Report{} +< expected.AddOnWarn(path.New("yaml", "mode"), test.out) +< assert.Equal(t, expected, actual, "#%d: bad report", i) +< } +< +< dirTests := []struct { +< in Directory +< out error +< }{ +< { +< in: Directory{}, +< out: nil, +< }, +< { +< in: Directory{ +< Mode: util.IntToPtr(01770), +< }, +< out: nil, +< }, +< { +< in: Directory{ +< Mode: util.IntToPtr(1770), +< }, +< out: common.ErrDecimalMode, +< }, +< } +< +< for i, test := range dirTests { +< actual := test.in.Validate(path.New("yaml")) +< expected := report.Report{} +< expected.AddOnWarn(path.New("yaml", "mode"), test.out) +< assert.Equal(t, expected, actual, "#%d: bad report", i) +< } +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/build updated/vendor/github.com/coreos/fcct/build +1,19d0 +< #!/usr/bin/env bash +< +< set -eu +< +< export GO111MODULE=on +< export GOFLAGS=-mod=vendor +< export CGO_ENABLED=0 +< version=$(git describe --dirty --always) +< LDFLAGS="-w -X github.com/coreos/fcct/internal/version.Raw=$version" +< +< NAME=fcct +< +< eval $(go env) +< if [ -z ${BIN_PATH+a} ]; then +< BIN_PATH=${PWD}/bin/${GOARCH} +< fi +< +< echo "Building $NAME..." +< go build -o ${BIN_PATH}/${NAME} -ldflags "$LDFLAGS" internal/main.go +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/build_releases updated/vendor/github.com/coreos/fcct/build_releases +1,36d0 +< #!/usr/bin/env bash +< +< set -euo pipefail +< +< export GO111MODULE=on +< export GOFLAGS=-mod=vendor +< export CGO_ENABLED=0 +< version=$(git describe --dirty --always) +< LDFLAGS="-w -X github.com/coreos/fcct/internal/version.Raw=$version" +< +< eval $(go env) +< if [ -z ${BIN_PATH+a} ]; then +< export BIN_PATH=${PWD}/bin/releases/ +< fi +< +< build_release() { +< export NAME="fcct-${1}" +< echo "building ${NAME}" +< go build -o ${BIN_PATH}/${NAME} -ldflags "$LDFLAGS" internal/main.go +< } +< +< export GOOS=linux +< export GOARCH=amd64 +< build_release x86_64-unknown-linux-gnu +< +< export GOOS=darwin +< export GOARCH=amd64 +< build_release x86_64-apple-darwin +< +< export GOOS=windows +< export GOARCH=amd64 +< build_release x86_64-pc-windows-gnu.exe +< +< export GOOS=linux +< export GOARCH=arm64 +< build_release aarch64-unknown-linux-gnu +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/common/common.go updated/vendor/github.com/coreos/fcct/config/common/common.go +1,26d0 +< // 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 +< +< type TranslateOptions struct { +< FilesDir string // allow embedding local files relative to this directory +< NoResourceAutoCompression bool // skip automatic compression of inline/local resources +< } +< +< type TranslateBytesOptions struct { +< TranslateOptions +< Pretty bool +< Strict bool +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/common/errors.go updated/vendor/github.com/coreos/fcct/config/common/errors.go +1,49d0 +< // 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 ( +< // common field parsing +< ErrNoVariant = errors.New("error parsing variant; must be specified") +< ErrInvalidVersion = errors.New("error parsing version; must be a valid semver") +< +< // high-level errors for fatal reports +< ErrInvalidSourceConfig = errors.New("source config is invalid") +< ErrInvalidGeneratedConfig = errors.New("config generated was invalid") +< +< // resources and trees +< ErrTooManyResourceSources = errors.New("only one of the following can be set: inline, local, source") +< 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") +< ErrTreeNoLocal = errors.New("local is required") +< +< // filesystem nodes +< ErrDecimalMode = errors.New("unreasonable mode would be reasonable if specified in octal; remember to add a leading zero") +< +< // mount units +< ErrMountUnitNoPath = errors.New("path is required if with_mount_unit is true") +< ErrMountUnitNoFormat = errors.New("format is required if with_mount_unit is true") +< +< // boot device +< ErrUnknownBootDeviceLayout = errors.New("layout must be one of: aarch64, ppc64le, x86_64") +< ErrTooFewMirrorDevices = errors.New("mirroring requires at least two devices") +< ) +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/config.go updated/vendor/github.com/coreos/fcct/config/config.go +1,103d0 +< // 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 config +< +< import ( +< "fmt" +< +< "github.com/coreos/fcct/config/common" +< fcos1_0 "github.com/coreos/fcct/config/fcos/v1_0" +< fcos1_1 "github.com/coreos/fcct/config/fcos/v1_1" +< fcos1_2 "github.com/coreos/fcct/config/fcos/v1_2" +< fcos1_3 "github.com/coreos/fcct/config/fcos/v1_3" +< fcos1_4_exp "github.com/coreos/fcct/config/fcos/v1_4_exp" +< rhcos0_1 "github.com/coreos/fcct/config/rhcos/v0_1" +< rhcos0_2_exp "github.com/coreos/fcct/config/rhcos/v0_2_exp" +< +< "github.com/coreos/go-semver/semver" +< "github.com/coreos/vcontext/report" +< "gopkg.in/yaml.v3" +< ) +< +< var ( +< registry = map[string]translator{} +< ) +< +< /// Fields that must be included in the root struct of every spec version. +< type commonFields struct { +< Version string `yaml:"version"` +< Variant string `yaml:"variant"` +< } +< +< func init() { +< RegisterTranslator("fcos", "1.0.0", fcos1_0.ToIgn3_0Bytes) +< RegisterTranslator("fcos", "1.1.0", fcos1_1.ToIgn3_1Bytes) +< RegisterTranslator("fcos", "1.2.0", fcos1_2.ToIgn3_2Bytes) +< RegisterTranslator("fcos", "1.3.0", fcos1_3.ToIgn3_2Bytes) +< RegisterTranslator("fcos", "1.4.0-experimental", fcos1_4_exp.ToIgn3_3Bytes) +< RegisterTranslator("rhcos", "0.1.0", rhcos0_1.ToIgn3_2Bytes) +< RegisterTranslator("rhcos", "0.2.0-experimental", rhcos0_2_exp.ToIgn3_3Bytes) +< } +< +< /// RegisterTranslator registers a translator for the specified variant and +< /// version to be available for use by TranslateBytes. This is only needed +< /// by users implementing their own translators outside the FCCT package. +< func RegisterTranslator(variant, version string, trans translator) { +< key := fmt.Sprintf("%s+%s", variant, version) +< if _, ok := registry[key]; ok { +< panic("tried to reregister existing translator") +< } +< registry[key] = trans +< } +< +< func getTranslator(variant string, version semver.Version) (translator, error) { +< t, ok := registry[fmt.Sprintf("%s+%s", variant, version.String())] +< if !ok { +< return nil, fmt.Errorf("No translator exists for variant %s with version %s", variant, version.String()) +< } +< return t, nil +< } +< +< // translators take a raw config and translate it to a raw Ignition config. The report returned should include any +< // errors, warnings, etc and may or may not be fatal. If report is fatal, or other errors are encountered while translating +< // translators should return an error. +< type translator func([]byte, common.TranslateBytesOptions) ([]byte, report.Report, error) +< +< // TranslateBytes wraps all of the individual TranslateBytes functions in a switch that determines the correct one to call. +< // TranslateBytes returns an error if the report had fatal errors or if other errors occured during translation. +< func TranslateBytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { +< // first determine version. This will ignore most fields, so don't use strict +< ver := commonFields{} +< if err := yaml.Unmarshal(input, &ver); err != nil { +< return nil, report.Report{}, fmt.Errorf("Error unmarshaling yaml: %v", err) +< } +< +< if ver.Variant == "" { +< return nil, report.Report{}, common.ErrNoVariant +< } +< +< tmp, err := semver.NewVersion(ver.Version) +< if err != nil { +< return nil, report.Report{}, common.ErrInvalidVersion +< } +< version := *tmp +< +< translator, err := getTranslator(ver.Variant, version) +< if err != nil { +< return nil, report.Report{}, err +< } +< +< return translator(input, options) +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/fcos/v1_0/schema.go updated/vendor/github.com/coreos/fcct/config/fcos/v1_0/schema.go +1,23d0 +< // 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 v1_0 +< +< import ( +< base "github.com/coreos/fcct/base/v0_1" +< ) +< +< type Config struct { +< base.Config `yaml:",inline"` +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/fcos/v1_0/translate.go updated/vendor/github.com/coreos/fcct/config/fcos/v1_0/translate.go +1,39d0 +< // 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_0 +< +< import ( +< "github.com/coreos/fcct/config/common" +< cutil "github.com/coreos/fcct/config/util" +< +< "github.com/coreos/ignition/v2/config/v3_0/types" +< "github.com/coreos/vcontext/report" +< ) +< +< // ToIgn3_0 translates the config to an Ignition config. It returns a +< // report of any errors or warnings in the source and resultant config. If +< // the report has fatal errors or it encounters other problems translating, +< // an error is returned. +< func (c Config) ToIgn3_0(options common.TranslateOptions) (types.Config, report.Report, error) { +< cfg, r, err := cutil.Translate(c, "ToIgn3_0Unvalidated", options) +< return cfg.(types.Config), r, err +< } +< +< // ToIgn3_0Bytes translates from a v1.0 fcc to a v3.0.0 Ignition config. It returns a report of any errors or +< // warnings in the source and resultant config. If the report has fatal errors or it encounters other problems +< // translating, an error is returned. +< func ToIgn3_0Bytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { +< return cutil.TranslateBytes(input, &Config{}, "ToIgn3_0", options) +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/fcos/v1_1/schema.go updated/vendor/github.com/coreos/fcct/config/fcos/v1_1/schema.go +1,23d0 +< // 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 v1_1 +< +< import ( +< base "github.com/coreos/fcct/base/v0_2" +< ) +< +< type Config struct { +< base.Config `yaml:",inline"` +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/fcos/v1_1/translate.go updated/vendor/github.com/coreos/fcct/config/fcos/v1_1/translate.go +1,39d0 +< // 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 ( +< "github.com/coreos/fcct/config/common" +< cutil "github.com/coreos/fcct/config/util" +< +< "github.com/coreos/ignition/v2/config/v3_1/types" +< "github.com/coreos/vcontext/report" +< ) +< +< // ToIgn3_1 translates the config to an Ignition config. It returns a +< // report of any errors or warnings in the source and resultant config. If +< // the report has fatal errors or it encounters other problems translating, +< // an error is returned. +< func (c Config) ToIgn3_1(options common.TranslateOptions) (types.Config, report.Report, error) { +< cfg, r, err := cutil.Translate(c, "ToIgn3_1Unvalidated", options) +< return cfg.(types.Config), r, err +< } +< +< // ToIgn3_1Bytes 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 ToIgn3_1Bytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { +< return cutil.TranslateBytes(input, &Config{}, "ToIgn3_1", options) +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/fcos/v1_2/schema.go updated/vendor/github.com/coreos/fcct/config/fcos/v1_2/schema.go +1,23d0 +< // 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 v1_2 +< +< import ( +< base "github.com/coreos/fcct/base/v0_3" +< ) +< +< type Config struct { +< base.Config `yaml:",inline"` +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/fcos/v1_2/translate.go updated/vendor/github.com/coreos/fcct/config/fcos/v1_2/translate.go +1,39d0 +< // 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_2 +< +< import ( +< "github.com/coreos/fcct/config/common" +< cutil "github.com/coreos/fcct/config/util" +< +< "github.com/coreos/ignition/v2/config/v3_2/types" +< "github.com/coreos/vcontext/report" +< ) +< +< // ToIgn3_2 translates the config to an Ignition config. It returns a +< // report of any errors or warnings in the source and resultant config. If +< // the report has fatal errors or it encounters other problems translating, +< // an error is returned. +< func (c Config) ToIgn3_2(options common.TranslateOptions) (types.Config, report.Report, error) { +< cfg, r, err := cutil.Translate(c, "ToIgn3_2Unvalidated", options) +< return cfg.(types.Config), r, err +< } +< +< // ToIgn3_2Bytes translates from a v1.2 fcc to a v3.2.0 Ignition config. It returns a report of any errors or +< // warnings in the source and resultant config. If the report has fatal errors or it encounters other problems +< // translating, an error is returned. +< func ToIgn3_2Bytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { +< return cutil.TranslateBytes(input, &Config{}, "ToIgn3_2", options) +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/fcos/v1_3/schema.go updated/vendor/github.com/coreos/fcct/config/fcos/v1_3/schema.go +1,40d0 +< // 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 v1_3 +< +< import ( +< base "github.com/coreos/fcct/base/v0_3" +< ) +< +< type Config struct { +< base.Config `yaml:",inline"` +< BootDevice BootDevice `yaml:"boot_device"` +< } +< +< type BootDevice struct { +< Layout *string `yaml:"layout"` +< Luks BootDeviceLuks `yaml:"luks"` +< Mirror BootDeviceMirror `yaml:"mirror"` +< } +< +< type BootDeviceLuks struct { +< Tang []base.Tang `yaml:"tang"` +< Threshold *int `yaml:"threshold"` +< Tpm2 *bool `yaml:"tpm2"` +< } +< +< type BootDeviceMirror struct { +< Devices []string `yaml:"devices"` +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/fcos/v1_3/translate.go updated/vendor/github.com/coreos/fcct/config/fcos/v1_3/translate.go +1,254d0 +< // 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 v1_3 +< +< import ( +< "fmt" +< +< baseutil "github.com/coreos/fcct/base/util" +< "github.com/coreos/fcct/config/common" +< cutil "github.com/coreos/fcct/config/util" +< "github.com/coreos/fcct/translate" +< +< "github.com/coreos/ignition/v2/config/util" +< "github.com/coreos/ignition/v2/config/v3_2/types" +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< ) +< +< const ( +< biosTypeGuid = "21686148-6449-6E6F-744E-656564454649" +< prepTypeGuid = "9E1A2D38-C612-4316-AA26-8B49521E5A8B" +< espTypeGuid = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" +< +< // The partition layout implemented in this file replicates +< // the layout of the OS image defined in: +< // https://github.com/coreos/coreos-assembler/blob/master/src/create_disk.sh +< // +< // Exception: we don't try to skip unused partition numbers, +< // because specifying a partition number would prevent child +< // configs from overriding partition fields using the partition +< // label as the lookup key. +< // +< // It's not critical that we match that layout exactly; the hard +< // constraints are: +< // - The desugared partition cannot be smaller than the one it +< // replicates +< // - The new BIOS-BOOT partition (and maybe the PReP one?) must be +< // at the same offset as the original +< // +< // Do not change these constants! New partition layouts must be +< // encoded into new layout templates. +< biosV1SizeMiB = 1 +< prepV1SizeMiB = 4 +< espV1SizeMiB = 127 +< bootV1SizeMiB = 384 +< ) +< +< // ToIgn3_2Unvalidated translates the config to an Ignition config. It also +< // returns the set of translations it did so paths in the resultant config +< // can be tracked back to their source in the source config. No config +< // validation is performed on input or output. +< func (c Config) ToIgn3_2Unvalidated(options common.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) { +< ret, ts, r := c.Config.ToIgn3_2Unvalidated(options) +< r.Merge(c.processBootDevice(&ret, &ts, options)) +< return ret, ts, r +< } +< +< // ToIgn3_2 translates the config to an Ignition config. It returns a +< // report of any errors or warnings in the source and resultant config. If +< // the report has fatal errors or it encounters other problems translating, +< // an error is returned. +< func (c Config) ToIgn3_2(options common.TranslateOptions) (types.Config, report.Report, error) { +< cfg, r, err := cutil.Translate(c, "ToIgn3_2Unvalidated", options) +< return cfg.(types.Config), r, err +< } +< +< // ToIgn3_2Bytes translates from a v1.3 fcc to a v3.2.0 Ignition config. It returns a report of any errors or +< // warnings in the source and resultant config. If the report has fatal errors or it encounters other problems +< // translating, an error is returned. +< func ToIgn3_2Bytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { +< return cutil.TranslateBytes(input, &Config{}, "ToIgn3_2", options) +< } +< +< func (c Config) processBootDevice(config *types.Config, ts *translate.TranslationSet, options common.TranslateOptions) report.Report { +< var rendered types.Config +< renderedTranslations := translate.NewTranslationSet("yaml", "json") +< var r report.Report +< +< // check for high-level features +< wantLuks := (c.BootDevice.Luks.Tpm2 != nil && *c.BootDevice.Luks.Tpm2) || len(c.BootDevice.Luks.Tang) > 0 +< wantMirror := len(c.BootDevice.Mirror.Devices) > 0 +< if !wantLuks && !wantMirror { +< return r +< } +< +< // compute layout rendering options +< var wantBIOSPart bool +< var wantPRePPart bool +< layout := c.BootDevice.Layout +< switch { +< case layout == nil || *layout == "x86_64": +< wantBIOSPart = true +< case *layout == "aarch64": +< // neither BIOS or PReP +< case *layout == "ppc64le": +< wantPRePPart = true +< default: +< // should have failed validation +< panic("unknown layout") +< } +< +< // mirrored root disk +< if wantMirror { +< // partition disks +< for i, device := range c.BootDevice.Mirror.Devices { +< labelIndex := len(rendered.Storage.Disks) + 1 +< disk := types.Disk{ +< Device: device, +< WipeTable: util.BoolToPtr(true), +< } +< if wantBIOSPart { +< disk.Partitions = append(disk.Partitions, types.Partition{ +< Label: util.StrToPtr(fmt.Sprintf("bios-%d", labelIndex)), +< SizeMiB: util.IntToPtr(biosV1SizeMiB), +< TypeGUID: util.StrToPtr(biosTypeGuid), +< }) +< } +< if wantPRePPart { +< disk.Partitions = append(disk.Partitions, types.Partition{ +< Label: util.StrToPtr(fmt.Sprintf("prep-%d", labelIndex)), +< SizeMiB: util.IntToPtr(prepV1SizeMiB), +< TypeGUID: util.StrToPtr(prepTypeGuid), +< }) +< } +< disk.Partitions = append(disk.Partitions, types.Partition{ +< Label: util.StrToPtr(fmt.Sprintf("esp-%d", labelIndex)), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, types.Partition{ +< Label: util.StrToPtr(fmt.Sprintf("boot-%d", labelIndex)), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, types.Partition{ +< Label: util.StrToPtr(fmt.Sprintf("root-%d", labelIndex)), +< }) +< renderedTranslations.AddFromCommonSource(path.New("yaml", "boot_device", "mirror", "devices", i), path.New("json", "storage", "disks", len(rendered.Storage.Disks)), disk) +< rendered.Storage.Disks = append(rendered.Storage.Disks, disk) +< } +< +< // create RAIDs +< raidDevices := func(labelPrefix string) []types.Device { +< count := len(rendered.Storage.Disks) +< ret := make([]types.Device, count) +< for i := 0; i < count; i++ { +< ret[i] = types.Device(fmt.Sprintf("/dev/disk/by-partlabel/%s-%d", labelPrefix, i+1)) +< } +< return ret +< } +< rendered.Storage.Raid = []types.Raid{{ +< Devices: raidDevices("esp"), +< Level: "raid1", +< Name: "md-esp", +< // put the RAID superblock at the end of the +< // partition to avoid confusing UEFI +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, { +< Devices: raidDevices("boot"), +< Level: "raid1", +< Name: "md-boot", +< // put the RAID superblock at the end of the +< // partition so BIOS GRUB doesn't need to +< // understand RAID +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, { +< Devices: raidDevices("root"), +< Level: "raid1", +< Name: "md-root", +< }} +< renderedTranslations.AddFromCommonSource(path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid"), rendered.Storage.Raid) +< +< // create filesystems, except for root +< rendered.Storage.Filesystems = []types.Filesystem{{ +< Device: "/dev/md/md-esp", +< Format: util.StrToPtr("vfat"), +< Label: util.StrToPtr("EFI-SYSTEM"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/md/md-boot", +< Format: util.StrToPtr("ext4"), +< Label: util.StrToPtr("boot"), +< WipeFilesystem: util.BoolToPtr(true), +< }} +< renderedTranslations.AddFromCommonSource(path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems"), rendered.Storage.Filesystems) +< } +< +< // encrypted root partition +< if wantLuks { +< luksDevice := "/dev/disk/by-partlabel/root" +< if wantMirror { +< luksDevice = "/dev/md/md-root" +< } +< clevis, ts2, r2 := translateBootDeviceLuks(c.BootDevice.Luks, options) +< rendered.Storage.Luks = []types.Luks{{ +< Clevis: &clevis, +< Device: &luksDevice, +< Label: util.StrToPtr("luks-root"), +< Name: "root", +< WipeVolume: util.BoolToPtr(true), +< }} +< lpath := path.New("yaml", "boot_device", "luks") +< rpath := path.New("json", "storage", "luks", 0) +< renderedTranslations.Merge(ts2.PrefixPaths(lpath, rpath.Append("clevis"))) +< for _, f := range []string{"device", "label", "name", "wipeVolume"} { +< renderedTranslations.AddTranslation(lpath, rpath.Append(f)) +< } +< r.Merge(r2) +< } +< +< // create root filesystem +< var rootDevice string +< switch { +< case wantLuks: +< // LUKS, or LUKS on RAID +< rootDevice = "/dev/mapper/root" +< case wantMirror: +< // RAID without LUKS +< rootDevice = "/dev/md/md-root" +< default: +< panic("can't happen") +< } +< rootFilesystem := types.Filesystem{ +< Device: rootDevice, +< Format: util.StrToPtr("xfs"), +< Label: util.StrToPtr("root"), +< WipeFilesystem: util.BoolToPtr(true), +< } +< renderedTranslations.AddFromCommonSource(path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", len(rendered.Storage.Filesystems)), rootFilesystem) +< rendered.Storage.Filesystems = append(rendered.Storage.Filesystems, rootFilesystem) +< +< // merge with translated config +< retConfig, retTranslations := baseutil.MergeTranslatedConfigs(rendered, renderedTranslations, *config, *ts) +< *config = retConfig.(types.Config) +< *ts = retTranslations +< return r +< } +< +< func translateBootDeviceLuks(from BootDeviceLuks, options common.TranslateOptions) (to types.Clevis, tm translate.TranslationSet, r report.Report) { +< tr := translate.NewTranslator("yaml", "json", options) +< tm, r = translate.Prefixed(tr, "tang", &from.Tang, &to.Tang) +< translate.MergeP(tr, tm, &r, "threshold", &from.Threshold, &to.Threshold) +< translate.MergeP(tr, tm, &r, "tpm2", &from.Tpm2, &to.Tpm2) +< return +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/fcos/v1_3/translate_test.go updated/vendor/github.com/coreos/fcct/config/fcos/v1_3/translate_test.go +1,1159d0 +< // 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 v1_3 +< +< import ( +< "testing" +< +< baseutil "github.com/coreos/fcct/base/util" +< base "github.com/coreos/fcct/base/v0_3" +< "github.com/coreos/fcct/config/common" +< "github.com/coreos/fcct/translate" +< +< "github.com/coreos/ignition/v2/config/util" +< "github.com/coreos/ignition/v2/config/v3_2/types" +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< "github.com/stretchr/testify/assert" +< ) +< +< // Most of this is covered by the Ignition translator generic tests, so just test the custom bits +< +< // TestTranslateBootDevice tests translating the FCC boot_device section. +< func TestTranslateBootDevice(t *testing.T) { +< tests := []struct { +< in Config +< out types.Config +< exceptions []translate.Translation +< }{ +< // empty config +< { +< Config{}, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.2.0", +< }, +< }, +< []translate.Translation{}, +< }, +< // LUKS, x86_64 +< { +< Config{ +< BootDevice: BootDevice{ +< Luks: BootDeviceLuks{ +< Tang: []base.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.2.0", +< }, +< Storage: types.Storage{ +< Luks: []types.Luks{ +< { +< Clevis: &types.Clevis{ +< Tang: []types.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Device: util.StrToPtr("/dev/disk/by-partlabel/root"), +< Label: util.StrToPtr("luks-root"), +< Name: "root", +< WipeVolume: util.BoolToPtr(true), +< }, +< }, +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/mapper/root", +< Format: util.StrToPtr("xfs"), +< Label: util.StrToPtr("root"), +< WipeFilesystem: util.BoolToPtr(true), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< {path.New("yaml", "boot_device", "luks", "tang", 0, "url"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "url")}, +< {path.New("yaml", "boot_device", "luks", "tang", 0, "thumbprint"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "thumbprint")}, +< {path.New("yaml", "boot_device", "luks", "threshold"), path.New("json", "storage", "luks", 0, "clevis", "threshold")}, +< {path.New("yaml", "boot_device", "luks", "tpm2"), path.New("json", "storage", "luks", 0, "clevis", "tpm2")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "device")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "label")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "name")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "wipeVolume")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 0, "device")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 0, "format")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 0, "label")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 0, "wipeFilesystem")}, +< }, +< }, +< // 3-disk mirror, x86_64 +< { +< Config{ +< BootDevice: BootDevice{ +< Mirror: BootDeviceMirror{ +< Devices: []string{"/dev/vda", "/dev/vdb", "/dev/vdc"}, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.2.0", +< }, +< Storage: types.Storage{ +< Disks: []types.Disk{ +< { +< Device: "/dev/vda", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("bios-1"), +< SizeMiB: util.IntToPtr(biosV1SizeMiB), +< TypeGUID: util.StrToPtr(biosTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-1"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-1"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-1"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< { +< Device: "/dev/vdb", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("bios-2"), +< SizeMiB: util.IntToPtr(biosV1SizeMiB), +< TypeGUID: util.StrToPtr(biosTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-2"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-2"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-2"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< { +< Device: "/dev/vdc", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("bios-3"), +< SizeMiB: util.IntToPtr(biosV1SizeMiB), +< TypeGUID: util.StrToPtr(biosTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-3"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-3"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-3"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< }, +< Raid: []types.Raid{ +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/esp-1", +< "/dev/disk/by-partlabel/esp-2", +< "/dev/disk/by-partlabel/esp-3", +< }, +< Level: "raid1", +< Name: "md-esp", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/boot-1", +< "/dev/disk/by-partlabel/boot-2", +< "/dev/disk/by-partlabel/boot-3", +< }, +< Level: "raid1", +< Name: "md-boot", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/root-1", +< "/dev/disk/by-partlabel/root-2", +< "/dev/disk/by-partlabel/root-3", +< }, +< Level: "raid1", +< Name: "md-root", +< }, +< }, +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/md/md-esp", +< Format: util.StrToPtr("vfat"), +< Label: util.StrToPtr("EFI-SYSTEM"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/md/md-boot", +< Format: util.StrToPtr("ext4"), +< Label: util.StrToPtr("boot"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/md/md-root", +< Format: util.StrToPtr("xfs"), +< Label: util.StrToPtr("root"), +< WipeFilesystem: util.BoolToPtr(true), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 3, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 3, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 3, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 2)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 2)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 2)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "wipeFilesystem")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "wipeFilesystem")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "device")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "format")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "label")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "wipeFilesystem")}, +< }, +< }, +< // 3-disk mirror + LUKS, x86_64 +< { +< Config{ +< BootDevice: BootDevice{ +< Luks: BootDeviceLuks{ +< Tang: []base.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Mirror: BootDeviceMirror{ +< Devices: []string{"/dev/vda", "/dev/vdb", "/dev/vdc"}, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.2.0", +< }, +< Storage: types.Storage{ +< Disks: []types.Disk{ +< { +< Device: "/dev/vda", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("bios-1"), +< SizeMiB: util.IntToPtr(biosV1SizeMiB), +< TypeGUID: util.StrToPtr(biosTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-1"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-1"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-1"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< { +< Device: "/dev/vdb", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("bios-2"), +< SizeMiB: util.IntToPtr(biosV1SizeMiB), +< TypeGUID: util.StrToPtr(biosTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-2"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-2"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-2"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< { +< Device: "/dev/vdc", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("bios-3"), +< SizeMiB: util.IntToPtr(biosV1SizeMiB), +< TypeGUID: util.StrToPtr(biosTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-3"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-3"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-3"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< }, +< Raid: []types.Raid{ +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/esp-1", +< "/dev/disk/by-partlabel/esp-2", +< "/dev/disk/by-partlabel/esp-3", +< }, +< Level: "raid1", +< Name: "md-esp", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/boot-1", +< "/dev/disk/by-partlabel/boot-2", +< "/dev/disk/by-partlabel/boot-3", +< }, +< Level: "raid1", +< Name: "md-boot", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/root-1", +< "/dev/disk/by-partlabel/root-2", +< "/dev/disk/by-partlabel/root-3", +< }, +< Level: "raid1", +< Name: "md-root", +< }, +< }, +< Luks: []types.Luks{ +< { +< Clevis: &types.Clevis{ +< Tang: []types.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Device: util.StrToPtr("/dev/md/md-root"), +< Label: util.StrToPtr("luks-root"), +< Name: "root", +< WipeVolume: util.BoolToPtr(true), +< }, +< }, +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/md/md-esp", +< Format: util.StrToPtr("vfat"), +< Label: util.StrToPtr("EFI-SYSTEM"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/md/md-boot", +< Format: util.StrToPtr("ext4"), +< Label: util.StrToPtr("boot"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/mapper/root", +< Format: util.StrToPtr("xfs"), +< Label: util.StrToPtr("root"), +< WipeFilesystem: util.BoolToPtr(true), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 3, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 3, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 3, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 2)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 2)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 2)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "name")}, +< {path.New("yaml", "boot_device", "luks", "tang", 0, "url"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "url")}, +< {path.New("yaml", "boot_device", "luks", "tang", 0, "thumbprint"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "thumbprint")}, +< {path.New("yaml", "boot_device", "luks", "threshold"), path.New("json", "storage", "luks", 0, "clevis", "threshold")}, +< {path.New("yaml", "boot_device", "luks", "tpm2"), path.New("json", "storage", "luks", 0, "clevis", "tpm2")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "device")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "label")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "name")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "wipeVolume")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "wipeFilesystem")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "wipeFilesystem")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "device")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "format")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "label")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "wipeFilesystem")}, +< }, +< }, +< // 2-disk mirror + LUKS, aarch64 +< { +< Config{ +< BootDevice: BootDevice{ +< Layout: util.StrToPtr("aarch64"), +< Luks: BootDeviceLuks{ +< Tang: []base.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Mirror: BootDeviceMirror{ +< Devices: []string{"/dev/vda", "/dev/vdb"}, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.2.0", +< }, +< Storage: types.Storage{ +< Disks: []types.Disk{ +< { +< Device: "/dev/vda", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("esp-1"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-1"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-1"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< { +< Device: "/dev/vdb", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("esp-2"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-2"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-2"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< }, +< Raid: []types.Raid{ +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/esp-1", +< "/dev/disk/by-partlabel/esp-2", +< }, +< Level: "raid1", +< Name: "md-esp", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/boot-1", +< "/dev/disk/by-partlabel/boot-2", +< }, +< Level: "raid1", +< Name: "md-boot", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/root-1", +< "/dev/disk/by-partlabel/root-2", +< }, +< Level: "raid1", +< Name: "md-root", +< }, +< }, +< Luks: []types.Luks{ +< { +< Clevis: &types.Clevis{ +< Tang: []types.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Device: util.StrToPtr("/dev/md/md-root"), +< Label: util.StrToPtr("luks-root"), +< Name: "root", +< WipeVolume: util.BoolToPtr(true), +< }, +< }, +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/md/md-esp", +< Format: util.StrToPtr("vfat"), +< Label: util.StrToPtr("EFI-SYSTEM"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/md/md-boot", +< Format: util.StrToPtr("ext4"), +< Label: util.StrToPtr("boot"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/mapper/root", +< Format: util.StrToPtr("xfs"), +< Label: util.StrToPtr("root"), +< WipeFilesystem: util.BoolToPtr(true), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "name")}, +< {path.New("yaml", "boot_device", "luks", "tang", 0, "url"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "url")}, +< {path.New("yaml", "boot_device", "luks", "tang", 0, "thumbprint"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "thumbprint")}, +< {path.New("yaml", "boot_device", "luks", "threshold"), path.New("json", "storage", "luks", 0, "clevis", "threshold")}, +< {path.New("yaml", "boot_device", "luks", "tpm2"), path.New("json", "storage", "luks", 0, "clevis", "tpm2")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "device")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "label")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "name")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "wipeVolume")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "wipeFilesystem")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "wipeFilesystem")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "device")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "format")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "label")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "wipeFilesystem")}, +< }, +< }, +< // 2-disk mirror + LUKS, ppc64le +< { +< Config{ +< BootDevice: BootDevice{ +< Layout: util.StrToPtr("ppc64le"), +< Luks: BootDeviceLuks{ +< Tang: []base.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Mirror: BootDeviceMirror{ +< Devices: []string{"/dev/vda", "/dev/vdb"}, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.2.0", +< }, +< Storage: types.Storage{ +< Disks: []types.Disk{ +< { +< Device: "/dev/vda", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("prep-1"), +< SizeMiB: util.IntToPtr(prepV1SizeMiB), +< TypeGUID: util.StrToPtr(prepTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-1"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-1"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-1"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< { +< Device: "/dev/vdb", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("prep-2"), +< SizeMiB: util.IntToPtr(prepV1SizeMiB), +< TypeGUID: util.StrToPtr(prepTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-2"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-2"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-2"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< }, +< Raid: []types.Raid{ +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/esp-1", +< "/dev/disk/by-partlabel/esp-2", +< }, +< Level: "raid1", +< Name: "md-esp", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/boot-1", +< "/dev/disk/by-partlabel/boot-2", +< }, +< Level: "raid1", +< Name: "md-boot", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/root-1", +< "/dev/disk/by-partlabel/root-2", +< }, +< Level: "raid1", +< Name: "md-root", +< }, +< }, +< Luks: []types.Luks{ +< { +< Clevis: &types.Clevis{ +< Tang: []types.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Device: util.StrToPtr("/dev/md/md-root"), +< Label: util.StrToPtr("luks-root"), +< Name: "root", +< WipeVolume: util.BoolToPtr(true), +< }, +< }, +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/md/md-esp", +< Format: util.StrToPtr("vfat"), +< Label: util.StrToPtr("EFI-SYSTEM"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/md/md-boot", +< Format: util.StrToPtr("ext4"), +< Label: util.StrToPtr("boot"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/mapper/root", +< Format: util.StrToPtr("xfs"), +< Label: util.StrToPtr("root"), +< WipeFilesystem: util.BoolToPtr(true), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 3, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 3, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "name")}, +< {path.New("yaml", "boot_device", "luks", "tang", 0, "url"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "url")}, +< {path.New("yaml", "boot_device", "luks", "tang", 0, "thumbprint"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "thumbprint")}, +< {path.New("yaml", "boot_device", "luks", "threshold"), path.New("json", "storage", "luks", 0, "clevis", "threshold")}, +< {path.New("yaml", "boot_device", "luks", "tpm2"), path.New("json", "storage", "luks", 0, "clevis", "tpm2")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "device")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "label")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "name")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "wipeVolume")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "wipeFilesystem")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "wipeFilesystem")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "device")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "format")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "label")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "wipeFilesystem")}, +< }, +< }, +< // 2-disk mirror + LUKS with overridden root partition size +< // and filesystem type, x86_64 +< { +< Config{ +< Config: base.Config{ +< Storage: base.Storage{ +< Disks: []base.Disk{ +< { +< Device: "/dev/vda", +< Partitions: []base.Partition{ +< { +< Label: util.StrToPtr("root-1"), +< SizeMiB: util.IntToPtr(8192), +< }, +< }, +< }, +< { +< Device: "/dev/vdb", +< Partitions: []base.Partition{ +< { +< Label: util.StrToPtr("root-2"), +< SizeMiB: util.IntToPtr(8192), +< }, +< }, +< }, +< }, +< Filesystems: []base.Filesystem{ +< { +< Device: "/dev/mapper/root", +< Format: util.StrToPtr("ext4"), +< }, +< }, +< }, +< }, +< BootDevice: BootDevice{ +< Luks: BootDeviceLuks{ +< Tang: []base.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Mirror: BootDeviceMirror{ +< Devices: []string{"/dev/vda", "/dev/vdb"}, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.2.0", +< }, +< Storage: types.Storage{ +< Disks: []types.Disk{ +< { +< Device: "/dev/vda", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("bios-1"), +< SizeMiB: util.IntToPtr(biosV1SizeMiB), +< TypeGUID: util.StrToPtr(biosTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-1"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-1"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-1"), +< SizeMiB: util.IntToPtr(8192), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< { +< Device: "/dev/vdb", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("bios-2"), +< SizeMiB: util.IntToPtr(biosV1SizeMiB), +< TypeGUID: util.StrToPtr(biosTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-2"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-2"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-2"), +< SizeMiB: util.IntToPtr(8192), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< }, +< Raid: []types.Raid{ +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/esp-1", +< "/dev/disk/by-partlabel/esp-2", +< }, +< Level: "raid1", +< Name: "md-esp", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/boot-1", +< "/dev/disk/by-partlabel/boot-2", +< }, +< Level: "raid1", +< Name: "md-boot", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/root-1", +< "/dev/disk/by-partlabel/root-2", +< }, +< Level: "raid1", +< Name: "md-root", +< }, +< }, +< Luks: []types.Luks{ +< { +< Clevis: &types.Clevis{ +< Tang: []types.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Device: util.StrToPtr("/dev/md/md-root"), +< Label: util.StrToPtr("luks-root"), +< Name: "root", +< WipeVolume: util.BoolToPtr(true), +< }, +< }, +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/md/md-esp", +< Format: util.StrToPtr("vfat"), +< Label: util.StrToPtr("EFI-SYSTEM"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/md/md-boot", +< Format: util.StrToPtr("ext4"), +< Label: util.StrToPtr("boot"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/mapper/root", +< Format: util.StrToPtr("ext4"), +< Label: util.StrToPtr("root"), +< WipeFilesystem: util.BoolToPtr(true), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "storage", "disks", 0, "partitions", 0, "label"), path.New("json", "storage", "disks", 0, "partitions", 3, "label")}, +< {path.New("yaml", "storage", "disks", 0, "partitions", 0, "size_mib"), path.New("json", "storage", "disks", 0, "partitions", 3, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "storage", "disks", 1, "partitions", 0, "label"), path.New("json", "storage", "disks", 1, "partitions", 3, "label")}, +< {path.New("yaml", "storage", "disks", 1, "partitions", 0, "size_mib"), path.New("json", "storage", "disks", 1, "partitions", 3, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "name")}, +< {path.New("yaml", "boot_device", "luks", "tang", 0, "url"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "url")}, +< {path.New("yaml", "boot_device", "luks", "tang", 0, "thumbprint"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "thumbprint")}, +< {path.New("yaml", "boot_device", "luks", "threshold"), path.New("json", "storage", "luks", 0, "clevis", "threshold")}, +< {path.New("yaml", "boot_device", "luks", "tpm2"), path.New("json", "storage", "luks", 0, "clevis", "tpm2")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "device")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "label")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "name")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "wipeVolume")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "wipeFilesystem")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "wipeFilesystem")}, +< {path.New("yaml", "storage", "filesystems", 0, "device"), path.New("json", "storage", "filesystems", 2, "device")}, +< {path.New("yaml", "storage", "filesystems", 0, "format"), path.New("json", "storage", "filesystems", 2, "format")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "label")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "wipeFilesystem")}, +< }, +< }, +< } +< +< // The partition sizes of existing layouts must never change, but +< // we use the constants in tests for clarity. Ensure no one has +< // changed them. +< assert.Equal(t, biosV1SizeMiB, 1) +< assert.Equal(t, prepV1SizeMiB, 4) +< assert.Equal(t, espV1SizeMiB, 127) +< assert.Equal(t, bootV1SizeMiB, 384) +< +< for i, test := range tests { +< actual, translations, r := test.in.ToIgn3_2Unvalidated(common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< baseutil.VerifyTranslations(t, translations, test.exceptions, "#%d", i) +< } +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/fcos/v1_3/validate.go updated/vendor/github.com/coreos/fcct/config/fcos/v1_3/validate.go +1,41d0 +< // 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 v1_3 +< +< import ( +< "github.com/coreos/fcct/config/common" +< +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< ) +< +< func (d BootDevice) Validate(c path.ContextPath) (r report.Report) { +< if d.Layout != nil { +< switch *d.Layout { +< case "aarch64", "ppc64le", "x86_64": +< default: +< r.AddOnError(c.Append("layout"), common.ErrUnknownBootDeviceLayout) +< } +< } +< r.Merge(d.Mirror.Validate(c.Append("mirror"))) +< return +< } +< +< func (m BootDeviceMirror) Validate(c path.ContextPath) (r report.Report) { +< if len(m.Devices) == 1 { +< r.AddOnError(c.Append("devices"), common.ErrTooFewMirrorDevices) +< } +< return +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/fcos/v1_3/validate_test.go updated/vendor/github.com/coreos/fcct/config/fcos/v1_3/validate_test.go +1,87d0 +< // 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 v1_3 +< +< import ( +< "testing" +< +< base "github.com/coreos/fcct/base/v0_3" +< "github.com/coreos/fcct/config/common" +< +< "github.com/coreos/ignition/v2/config/util" +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< "github.com/stretchr/testify/assert" +< ) +< +< // TestValidateBootDevice tests boot device validation +< func TestValidateBootDevice(t *testing.T) { +< tests := []struct { +< in BootDevice +< out error +< errPath path.ContextPath +< }{ +< // empty config +< { +< BootDevice{}, +< nil, +< path.New("yaml"), +< }, +< // complete config +< { +< BootDevice{ +< Layout: util.StrToPtr("x86_64"), +< Luks: BootDeviceLuks{ +< Tang: []base.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("x"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Mirror: BootDeviceMirror{ +< Devices: []string{"/dev/vda", "/dev/vdb"}, +< }, +< }, +< nil, +< path.New("yaml"), +< }, +< // invalid layout +< { +< BootDevice{ +< Layout: util.StrToPtr("sparc"), +< }, +< common.ErrUnknownBootDeviceLayout, +< path.New("yaml", "layout"), +< }, +< // only one mirror device +< { +< BootDevice{ +< Mirror: BootDeviceMirror{ +< Devices: []string{"/dev/vda"}, +< }, +< }, +< common.ErrTooFewMirrorDevices, +< path.New("yaml", "mirror", "devices"), +< }, +< } +< +< for i, test := range tests { +< actual := test.in.Validate(path.New("yaml")) +< expected := report.Report{} +< expected.AddOnError(test.errPath, test.out) +< assert.Equal(t, expected, actual, "#%d: bad validation report", i) +< } +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/fcos/v1_4_exp/schema.go updated/vendor/github.com/coreos/fcct/config/fcos/v1_4_exp/schema.go +1,40d0 +< // 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 v1_4_exp +< +< import ( +< base "github.com/coreos/fcct/base/v0_4_exp" +< ) +< +< type Config struct { +< base.Config `yaml:",inline"` +< BootDevice BootDevice `yaml:"boot_device"` +< } +< +< type BootDevice struct { +< Layout *string `yaml:"layout"` +< Luks BootDeviceLuks `yaml:"luks"` +< Mirror BootDeviceMirror `yaml:"mirror"` +< } +< +< type BootDeviceLuks struct { +< Tang []base.Tang `yaml:"tang"` +< Threshold *int `yaml:"threshold"` +< Tpm2 *bool `yaml:"tpm2"` +< } +< +< type BootDeviceMirror struct { +< Devices []string `yaml:"devices"` +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/fcos/v1_4_exp/translate.go updated/vendor/github.com/coreos/fcct/config/fcos/v1_4_exp/translate.go +1,254d0 +< // 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 v1_4_exp +< +< import ( +< "fmt" +< +< baseutil "github.com/coreos/fcct/base/util" +< "github.com/coreos/fcct/config/common" +< cutil "github.com/coreos/fcct/config/util" +< "github.com/coreos/fcct/translate" +< +< "github.com/coreos/ignition/v2/config/util" +< "github.com/coreos/ignition/v2/config/v3_3_experimental/types" +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< ) +< +< const ( +< biosTypeGuid = "21686148-6449-6E6F-744E-656564454649" +< prepTypeGuid = "9E1A2D38-C612-4316-AA26-8B49521E5A8B" +< espTypeGuid = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" +< +< // The partition layout implemented in this file replicates +< // the layout of the OS image defined in: +< // https://github.com/coreos/coreos-assembler/blob/master/src/create_disk.sh +< // +< // Exception: we don't try to skip unused partition numbers, +< // because specifying a partition number would prevent child +< // configs from overriding partition fields using the partition +< // label as the lookup key. +< // +< // It's not critical that we match that layout exactly; the hard +< // constraints are: +< // - The desugared partition cannot be smaller than the one it +< // replicates +< // - The new BIOS-BOOT partition (and maybe the PReP one?) must be +< // at the same offset as the original +< // +< // Do not change these constants! New partition layouts must be +< // encoded into new layout templates. +< biosV1SizeMiB = 1 +< prepV1SizeMiB = 4 +< espV1SizeMiB = 127 +< bootV1SizeMiB = 384 +< ) +< +< // ToIgn3_3Unvalidated translates the config to an Ignition config. It also +< // returns the set of translations it did so paths in the resultant config +< // can be tracked back to their source in the source config. No config +< // validation is performed on input or output. +< func (c Config) ToIgn3_3Unvalidated(options common.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) { +< ret, ts, r := c.Config.ToIgn3_3Unvalidated(options) +< r.Merge(c.processBootDevice(&ret, &ts, options)) +< return ret, ts, r +< } +< +< // ToIgn3_3 translates the config to an Ignition config. It returns a +< // report of any errors or warnings in the source and resultant config. If +< // the report has fatal errors or it encounters other problems translating, +< // an error is returned. +< func (c Config) ToIgn3_3(options common.TranslateOptions) (types.Config, report.Report, error) { +< cfg, r, err := cutil.Translate(c, "ToIgn3_3Unvalidated", options) +< return cfg.(types.Config), r, err +< } +< +< // ToIgn3_3Bytes translates from a v1.4 fcc to a v3.3.0 Ignition config. It returns a report of any errors or +< // warnings in the source and resultant config. If the report has fatal errors or it encounters other problems +< // translating, an error is returned. +< func ToIgn3_3Bytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { +< return cutil.TranslateBytes(input, &Config{}, "ToIgn3_3", options) +< } +< +< func (c Config) processBootDevice(config *types.Config, ts *translate.TranslationSet, options common.TranslateOptions) report.Report { +< var rendered types.Config +< renderedTranslations := translate.NewTranslationSet("yaml", "json") +< var r report.Report +< +< // check for high-level features +< wantLuks := (c.BootDevice.Luks.Tpm2 != nil && *c.BootDevice.Luks.Tpm2) || len(c.BootDevice.Luks.Tang) > 0 +< wantMirror := len(c.BootDevice.Mirror.Devices) > 0 +< if !wantLuks && !wantMirror { +< return r +< } +< +< // compute layout rendering options +< var wantBIOSPart bool +< var wantPRePPart bool +< layout := c.BootDevice.Layout +< switch { +< case layout == nil || *layout == "x86_64": +< wantBIOSPart = true +< case *layout == "aarch64": +< // neither BIOS or PReP +< case *layout == "ppc64le": +< wantPRePPart = true +< default: +< // should have failed validation +< panic("unknown layout") +< } +< +< // mirrored root disk +< if wantMirror { +< // partition disks +< for i, device := range c.BootDevice.Mirror.Devices { +< labelIndex := len(rendered.Storage.Disks) + 1 +< disk := types.Disk{ +< Device: device, +< WipeTable: util.BoolToPtr(true), +< } +< if wantBIOSPart { +< disk.Partitions = append(disk.Partitions, types.Partition{ +< Label: util.StrToPtr(fmt.Sprintf("bios-%d", labelIndex)), +< SizeMiB: util.IntToPtr(biosV1SizeMiB), +< TypeGUID: util.StrToPtr(biosTypeGuid), +< }) +< } +< if wantPRePPart { +< disk.Partitions = append(disk.Partitions, types.Partition{ +< Label: util.StrToPtr(fmt.Sprintf("prep-%d", labelIndex)), +< SizeMiB: util.IntToPtr(prepV1SizeMiB), +< TypeGUID: util.StrToPtr(prepTypeGuid), +< }) +< } +< disk.Partitions = append(disk.Partitions, types.Partition{ +< Label: util.StrToPtr(fmt.Sprintf("esp-%d", labelIndex)), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, types.Partition{ +< Label: util.StrToPtr(fmt.Sprintf("boot-%d", labelIndex)), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, types.Partition{ +< Label: util.StrToPtr(fmt.Sprintf("root-%d", labelIndex)), +< }) +< renderedTranslations.AddFromCommonSource(path.New("yaml", "boot_device", "mirror", "devices", i), path.New("json", "storage", "disks", len(rendered.Storage.Disks)), disk) +< rendered.Storage.Disks = append(rendered.Storage.Disks, disk) +< } +< +< // create RAIDs +< raidDevices := func(labelPrefix string) []types.Device { +< count := len(rendered.Storage.Disks) +< ret := make([]types.Device, count) +< for i := 0; i < count; i++ { +< ret[i] = types.Device(fmt.Sprintf("/dev/disk/by-partlabel/%s-%d", labelPrefix, i+1)) +< } +< return ret +< } +< rendered.Storage.Raid = []types.Raid{{ +< Devices: raidDevices("esp"), +< Level: "raid1", +< Name: "md-esp", +< // put the RAID superblock at the end of the +< // partition to avoid confusing UEFI +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, { +< Devices: raidDevices("boot"), +< Level: "raid1", +< Name: "md-boot", +< // put the RAID superblock at the end of the +< // partition so BIOS GRUB doesn't need to +< // understand RAID +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, { +< Devices: raidDevices("root"), +< Level: "raid1", +< Name: "md-root", +< }} +< renderedTranslations.AddFromCommonSource(path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid"), rendered.Storage.Raid) +< +< // create filesystems, except for root +< rendered.Storage.Filesystems = []types.Filesystem{{ +< Device: "/dev/md/md-esp", +< Format: util.StrToPtr("vfat"), +< Label: util.StrToPtr("EFI-SYSTEM"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/md/md-boot", +< Format: util.StrToPtr("ext4"), +< Label: util.StrToPtr("boot"), +< WipeFilesystem: util.BoolToPtr(true), +< }} +< renderedTranslations.AddFromCommonSource(path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems"), rendered.Storage.Filesystems) +< } +< +< // encrypted root partition +< if wantLuks { +< luksDevice := "/dev/disk/by-partlabel/root" +< if wantMirror { +< luksDevice = "/dev/md/md-root" +< } +< clevis, ts2, r2 := translateBootDeviceLuks(c.BootDevice.Luks, options) +< rendered.Storage.Luks = []types.Luks{{ +< Clevis: &clevis, +< Device: &luksDevice, +< Label: util.StrToPtr("luks-root"), +< Name: "root", +< WipeVolume: util.BoolToPtr(true), +< }} +< lpath := path.New("yaml", "boot_device", "luks") +< rpath := path.New("json", "storage", "luks", 0) +< renderedTranslations.Merge(ts2.PrefixPaths(lpath, rpath.Append("clevis"))) +< for _, f := range []string{"device", "label", "name", "wipeVolume"} { +< renderedTranslations.AddTranslation(lpath, rpath.Append(f)) +< } +< r.Merge(r2) +< } +< +< // create root filesystem +< var rootDevice string +< switch { +< case wantLuks: +< // LUKS, or LUKS on RAID +< rootDevice = "/dev/mapper/root" +< case wantMirror: +< // RAID without LUKS +< rootDevice = "/dev/md/md-root" +< default: +< panic("can't happen") +< } +< rootFilesystem := types.Filesystem{ +< Device: rootDevice, +< Format: util.StrToPtr("xfs"), +< Label: util.StrToPtr("root"), +< WipeFilesystem: util.BoolToPtr(true), +< } +< renderedTranslations.AddFromCommonSource(path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", len(rendered.Storage.Filesystems)), rootFilesystem) +< rendered.Storage.Filesystems = append(rendered.Storage.Filesystems, rootFilesystem) +< +< // merge with translated config +< retConfig, retTranslations := baseutil.MergeTranslatedConfigs(rendered, renderedTranslations, *config, *ts) +< *config = retConfig.(types.Config) +< *ts = retTranslations +< return r +< } +< +< func translateBootDeviceLuks(from BootDeviceLuks, options common.TranslateOptions) (to types.Clevis, tm translate.TranslationSet, r report.Report) { +< tr := translate.NewTranslator("yaml", "json", options) +< tm, r = translate.Prefixed(tr, "tang", &from.Tang, &to.Tang) +< translate.MergeP(tr, tm, &r, "threshold", &from.Threshold, &to.Threshold) +< translate.MergeP(tr, tm, &r, "tpm2", &from.Tpm2, &to.Tpm2) +< return +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/fcos/v1_4_exp/translate_test.go updated/vendor/github.com/coreos/fcct/config/fcos/v1_4_exp/translate_test.go +1,1159d0 +< // 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 v1_4_exp +< +< import ( +< "testing" +< +< baseutil "github.com/coreos/fcct/base/util" +< base "github.com/coreos/fcct/base/v0_4_exp" +< "github.com/coreos/fcct/config/common" +< "github.com/coreos/fcct/translate" +< +< "github.com/coreos/ignition/v2/config/util" +< "github.com/coreos/ignition/v2/config/v3_3_experimental/types" +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< "github.com/stretchr/testify/assert" +< ) +< +< // Most of this is covered by the Ignition translator generic tests, so just test the custom bits +< +< // TestTranslateBootDevice tests translating the FCC boot_device section. +< func TestTranslateBootDevice(t *testing.T) { +< tests := []struct { +< in Config +< out types.Config +< exceptions []translate.Translation +< }{ +< // empty config +< { +< Config{}, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.3.0-experimental", +< }, +< }, +< []translate.Translation{}, +< }, +< // LUKS, x86_64 +< { +< Config{ +< BootDevice: BootDevice{ +< Luks: BootDeviceLuks{ +< Tang: []base.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.3.0-experimental", +< }, +< Storage: types.Storage{ +< Luks: []types.Luks{ +< { +< Clevis: &types.Clevis{ +< Tang: []types.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Device: util.StrToPtr("/dev/disk/by-partlabel/root"), +< Label: util.StrToPtr("luks-root"), +< Name: "root", +< WipeVolume: util.BoolToPtr(true), +< }, +< }, +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/mapper/root", +< Format: util.StrToPtr("xfs"), +< Label: util.StrToPtr("root"), +< WipeFilesystem: util.BoolToPtr(true), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< {path.New("yaml", "boot_device", "luks", "tang", 0, "url"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "url")}, +< {path.New("yaml", "boot_device", "luks", "tang", 0, "thumbprint"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "thumbprint")}, +< {path.New("yaml", "boot_device", "luks", "threshold"), path.New("json", "storage", "luks", 0, "clevis", "threshold")}, +< {path.New("yaml", "boot_device", "luks", "tpm2"), path.New("json", "storage", "luks", 0, "clevis", "tpm2")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "device")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "label")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "name")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "wipeVolume")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 0, "device")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 0, "format")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 0, "label")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 0, "wipeFilesystem")}, +< }, +< }, +< // 3-disk mirror, x86_64 +< { +< Config{ +< BootDevice: BootDevice{ +< Mirror: BootDeviceMirror{ +< Devices: []string{"/dev/vda", "/dev/vdb", "/dev/vdc"}, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.3.0-experimental", +< }, +< Storage: types.Storage{ +< Disks: []types.Disk{ +< { +< Device: "/dev/vda", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("bios-1"), +< SizeMiB: util.IntToPtr(biosV1SizeMiB), +< TypeGUID: util.StrToPtr(biosTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-1"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-1"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-1"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< { +< Device: "/dev/vdb", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("bios-2"), +< SizeMiB: util.IntToPtr(biosV1SizeMiB), +< TypeGUID: util.StrToPtr(biosTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-2"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-2"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-2"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< { +< Device: "/dev/vdc", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("bios-3"), +< SizeMiB: util.IntToPtr(biosV1SizeMiB), +< TypeGUID: util.StrToPtr(biosTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-3"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-3"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-3"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< }, +< Raid: []types.Raid{ +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/esp-1", +< "/dev/disk/by-partlabel/esp-2", +< "/dev/disk/by-partlabel/esp-3", +< }, +< Level: "raid1", +< Name: "md-esp", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/boot-1", +< "/dev/disk/by-partlabel/boot-2", +< "/dev/disk/by-partlabel/boot-3", +< }, +< Level: "raid1", +< Name: "md-boot", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/root-1", +< "/dev/disk/by-partlabel/root-2", +< "/dev/disk/by-partlabel/root-3", +< }, +< Level: "raid1", +< Name: "md-root", +< }, +< }, +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/md/md-esp", +< Format: util.StrToPtr("vfat"), +< Label: util.StrToPtr("EFI-SYSTEM"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/md/md-boot", +< Format: util.StrToPtr("ext4"), +< Label: util.StrToPtr("boot"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/md/md-root", +< Format: util.StrToPtr("xfs"), +< Label: util.StrToPtr("root"), +< WipeFilesystem: util.BoolToPtr(true), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 3, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 3, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 3, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 2)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 2)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 2)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "wipeFilesystem")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "wipeFilesystem")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "device")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "format")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "label")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "wipeFilesystem")}, +< }, +< }, +< // 3-disk mirror + LUKS, x86_64 +< { +< Config{ +< BootDevice: BootDevice{ +< Luks: BootDeviceLuks{ +< Tang: []base.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Mirror: BootDeviceMirror{ +< Devices: []string{"/dev/vda", "/dev/vdb", "/dev/vdc"}, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.3.0-experimental", +< }, +< Storage: types.Storage{ +< Disks: []types.Disk{ +< { +< Device: "/dev/vda", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("bios-1"), +< SizeMiB: util.IntToPtr(biosV1SizeMiB), +< TypeGUID: util.StrToPtr(biosTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-1"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-1"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-1"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< { +< Device: "/dev/vdb", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("bios-2"), +< SizeMiB: util.IntToPtr(biosV1SizeMiB), +< TypeGUID: util.StrToPtr(biosTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-2"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-2"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-2"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< { +< Device: "/dev/vdc", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("bios-3"), +< SizeMiB: util.IntToPtr(biosV1SizeMiB), +< TypeGUID: util.StrToPtr(biosTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-3"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-3"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-3"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< }, +< Raid: []types.Raid{ +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/esp-1", +< "/dev/disk/by-partlabel/esp-2", +< "/dev/disk/by-partlabel/esp-3", +< }, +< Level: "raid1", +< Name: "md-esp", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/boot-1", +< "/dev/disk/by-partlabel/boot-2", +< "/dev/disk/by-partlabel/boot-3", +< }, +< Level: "raid1", +< Name: "md-boot", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/root-1", +< "/dev/disk/by-partlabel/root-2", +< "/dev/disk/by-partlabel/root-3", +< }, +< Level: "raid1", +< Name: "md-root", +< }, +< }, +< Luks: []types.Luks{ +< { +< Clevis: &types.Clevis{ +< Tang: []types.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Device: util.StrToPtr("/dev/md/md-root"), +< Label: util.StrToPtr("luks-root"), +< Name: "root", +< WipeVolume: util.BoolToPtr(true), +< }, +< }, +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/md/md-esp", +< Format: util.StrToPtr("vfat"), +< Label: util.StrToPtr("EFI-SYSTEM"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/md/md-boot", +< Format: util.StrToPtr("ext4"), +< Label: util.StrToPtr("boot"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/mapper/root", +< Format: util.StrToPtr("xfs"), +< Label: util.StrToPtr("root"), +< WipeFilesystem: util.BoolToPtr(true), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 3, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 3, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "partitions", 3, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 2), path.New("json", "storage", "disks", 2, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 2)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 2)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 2)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "name")}, +< {path.New("yaml", "boot_device", "luks", "tang", 0, "url"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "url")}, +< {path.New("yaml", "boot_device", "luks", "tang", 0, "thumbprint"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "thumbprint")}, +< {path.New("yaml", "boot_device", "luks", "threshold"), path.New("json", "storage", "luks", 0, "clevis", "threshold")}, +< {path.New("yaml", "boot_device", "luks", "tpm2"), path.New("json", "storage", "luks", 0, "clevis", "tpm2")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "device")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "label")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "name")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "wipeVolume")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "wipeFilesystem")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "wipeFilesystem")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "device")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "format")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "label")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "wipeFilesystem")}, +< }, +< }, +< // 2-disk mirror + LUKS, aarch64 +< { +< Config{ +< BootDevice: BootDevice{ +< Layout: util.StrToPtr("aarch64"), +< Luks: BootDeviceLuks{ +< Tang: []base.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Mirror: BootDeviceMirror{ +< Devices: []string{"/dev/vda", "/dev/vdb"}, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.3.0-experimental", +< }, +< Storage: types.Storage{ +< Disks: []types.Disk{ +< { +< Device: "/dev/vda", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("esp-1"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-1"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-1"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< { +< Device: "/dev/vdb", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("esp-2"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-2"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-2"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< }, +< Raid: []types.Raid{ +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/esp-1", +< "/dev/disk/by-partlabel/esp-2", +< }, +< Level: "raid1", +< Name: "md-esp", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/boot-1", +< "/dev/disk/by-partlabel/boot-2", +< }, +< Level: "raid1", +< Name: "md-boot", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/root-1", +< "/dev/disk/by-partlabel/root-2", +< }, +< Level: "raid1", +< Name: "md-root", +< }, +< }, +< Luks: []types.Luks{ +< { +< Clevis: &types.Clevis{ +< Tang: []types.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Device: util.StrToPtr("/dev/md/md-root"), +< Label: util.StrToPtr("luks-root"), +< Name: "root", +< WipeVolume: util.BoolToPtr(true), +< }, +< }, +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/md/md-esp", +< Format: util.StrToPtr("vfat"), +< Label: util.StrToPtr("EFI-SYSTEM"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/md/md-boot", +< Format: util.StrToPtr("ext4"), +< Label: util.StrToPtr("boot"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/mapper/root", +< Format: util.StrToPtr("xfs"), +< Label: util.StrToPtr("root"), +< WipeFilesystem: util.BoolToPtr(true), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "name")}, +< {path.New("yaml", "boot_device", "luks", "tang", 0, "url"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "url")}, +< {path.New("yaml", "boot_device", "luks", "tang", 0, "thumbprint"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "thumbprint")}, +< {path.New("yaml", "boot_device", "luks", "threshold"), path.New("json", "storage", "luks", 0, "clevis", "threshold")}, +< {path.New("yaml", "boot_device", "luks", "tpm2"), path.New("json", "storage", "luks", 0, "clevis", "tpm2")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "device")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "label")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "name")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "wipeVolume")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "wipeFilesystem")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "wipeFilesystem")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "device")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "format")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "label")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "wipeFilesystem")}, +< }, +< }, +< // 2-disk mirror + LUKS, ppc64le +< { +< Config{ +< BootDevice: BootDevice{ +< Layout: util.StrToPtr("ppc64le"), +< Luks: BootDeviceLuks{ +< Tang: []base.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Mirror: BootDeviceMirror{ +< Devices: []string{"/dev/vda", "/dev/vdb"}, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.3.0-experimental", +< }, +< Storage: types.Storage{ +< Disks: []types.Disk{ +< { +< Device: "/dev/vda", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("prep-1"), +< SizeMiB: util.IntToPtr(prepV1SizeMiB), +< TypeGUID: util.StrToPtr(prepTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-1"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-1"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-1"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< { +< Device: "/dev/vdb", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("prep-2"), +< SizeMiB: util.IntToPtr(prepV1SizeMiB), +< TypeGUID: util.StrToPtr(prepTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-2"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-2"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-2"), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< }, +< Raid: []types.Raid{ +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/esp-1", +< "/dev/disk/by-partlabel/esp-2", +< }, +< Level: "raid1", +< Name: "md-esp", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/boot-1", +< "/dev/disk/by-partlabel/boot-2", +< }, +< Level: "raid1", +< Name: "md-boot", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/root-1", +< "/dev/disk/by-partlabel/root-2", +< }, +< Level: "raid1", +< Name: "md-root", +< }, +< }, +< Luks: []types.Luks{ +< { +< Clevis: &types.Clevis{ +< Tang: []types.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Device: util.StrToPtr("/dev/md/md-root"), +< Label: util.StrToPtr("luks-root"), +< Name: "root", +< WipeVolume: util.BoolToPtr(true), +< }, +< }, +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/md/md-esp", +< Format: util.StrToPtr("vfat"), +< Label: util.StrToPtr("EFI-SYSTEM"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/md/md-boot", +< Format: util.StrToPtr("ext4"), +< Label: util.StrToPtr("boot"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/mapper/root", +< Format: util.StrToPtr("xfs"), +< Label: util.StrToPtr("root"), +< WipeFilesystem: util.BoolToPtr(true), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 3, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "device")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 3, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "name")}, +< {path.New("yaml", "boot_device", "luks", "tang", 0, "url"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "url")}, +< {path.New("yaml", "boot_device", "luks", "tang", 0, "thumbprint"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "thumbprint")}, +< {path.New("yaml", "boot_device", "luks", "threshold"), path.New("json", "storage", "luks", 0, "clevis", "threshold")}, +< {path.New("yaml", "boot_device", "luks", "tpm2"), path.New("json", "storage", "luks", 0, "clevis", "tpm2")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "device")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "label")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "name")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "wipeVolume")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "wipeFilesystem")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "wipeFilesystem")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "device")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "format")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "label")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "wipeFilesystem")}, +< }, +< }, +< // 2-disk mirror + LUKS with overridden root partition size +< // and filesystem type, x86_64 +< { +< Config{ +< Config: base.Config{ +< Storage: base.Storage{ +< Disks: []base.Disk{ +< { +< Device: "/dev/vda", +< Partitions: []base.Partition{ +< { +< Label: util.StrToPtr("root-1"), +< SizeMiB: util.IntToPtr(8192), +< }, +< }, +< }, +< { +< Device: "/dev/vdb", +< Partitions: []base.Partition{ +< { +< Label: util.StrToPtr("root-2"), +< SizeMiB: util.IntToPtr(8192), +< }, +< }, +< }, +< }, +< Filesystems: []base.Filesystem{ +< { +< Device: "/dev/mapper/root", +< Format: util.StrToPtr("ext4"), +< }, +< }, +< }, +< }, +< BootDevice: BootDevice{ +< Luks: BootDeviceLuks{ +< Tang: []base.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Mirror: BootDeviceMirror{ +< Devices: []string{"/dev/vda", "/dev/vdb"}, +< }, +< }, +< }, +< types.Config{ +< Ignition: types.Ignition{ +< Version: "3.3.0-experimental", +< }, +< Storage: types.Storage{ +< Disks: []types.Disk{ +< { +< Device: "/dev/vda", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("bios-1"), +< SizeMiB: util.IntToPtr(biosV1SizeMiB), +< TypeGUID: util.StrToPtr(biosTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-1"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-1"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-1"), +< SizeMiB: util.IntToPtr(8192), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< { +< Device: "/dev/vdb", +< Partitions: []types.Partition{ +< { +< Label: util.StrToPtr("bios-2"), +< SizeMiB: util.IntToPtr(biosV1SizeMiB), +< TypeGUID: util.StrToPtr(biosTypeGuid), +< }, +< { +< Label: util.StrToPtr("esp-2"), +< SizeMiB: util.IntToPtr(espV1SizeMiB), +< TypeGUID: util.StrToPtr(espTypeGuid), +< }, +< { +< Label: util.StrToPtr("boot-2"), +< SizeMiB: util.IntToPtr(bootV1SizeMiB), +< }, +< { +< Label: util.StrToPtr("root-2"), +< SizeMiB: util.IntToPtr(8192), +< }, +< }, +< WipeTable: util.BoolToPtr(true), +< }, +< }, +< Raid: []types.Raid{ +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/esp-1", +< "/dev/disk/by-partlabel/esp-2", +< }, +< Level: "raid1", +< Name: "md-esp", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/boot-1", +< "/dev/disk/by-partlabel/boot-2", +< }, +< Level: "raid1", +< Name: "md-boot", +< Options: []types.RaidOption{"--metadata=1.0"}, +< }, +< { +< Devices: []types.Device{ +< "/dev/disk/by-partlabel/root-1", +< "/dev/disk/by-partlabel/root-2", +< }, +< Level: "raid1", +< Name: "md-root", +< }, +< }, +< Luks: []types.Luks{ +< { +< Clevis: &types.Clevis{ +< Tang: []types.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("z"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Device: util.StrToPtr("/dev/md/md-root"), +< Label: util.StrToPtr("luks-root"), +< Name: "root", +< WipeVolume: util.BoolToPtr(true), +< }, +< }, +< Filesystems: []types.Filesystem{ +< { +< Device: "/dev/md/md-esp", +< Format: util.StrToPtr("vfat"), +< Label: util.StrToPtr("EFI-SYSTEM"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/md/md-boot", +< Format: util.StrToPtr("ext4"), +< Label: util.StrToPtr("boot"), +< WipeFilesystem: util.BoolToPtr(true), +< }, { +< Device: "/dev/mapper/root", +< Format: util.StrToPtr("ext4"), +< Label: util.StrToPtr("root"), +< WipeFilesystem: util.BoolToPtr(true), +< }, +< }, +< }, +< }, +< []translate.Translation{ +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "storage", "disks", 0, "partitions", 0, "label"), path.New("json", "storage", "disks", 0, "partitions", 3, "label")}, +< {path.New("yaml", "storage", "disks", 0, "partitions", 0, "size_mib"), path.New("json", "storage", "disks", 0, "partitions", 3, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 0), path.New("json", "storage", "disks", 0, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 0, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 1, "typeGuid")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 2, "label")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "partitions", 2, "sizeMiB")}, +< {path.New("yaml", "storage", "disks", 1, "partitions", 0, "label"), path.New("json", "storage", "disks", 1, "partitions", 3, "label")}, +< {path.New("yaml", "storage", "disks", 1, "partitions", 0, "size_mib"), path.New("json", "storage", "disks", 1, "partitions", 3, "sizeMiB")}, +< {path.New("yaml", "boot_device", "mirror", "devices", 1), path.New("json", "storage", "disks", 1, "wipeTable")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 0, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "name")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 1, "options", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 0)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "devices", 1)}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "level")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid", 2, "name")}, +< {path.New("yaml", "boot_device", "luks", "tang", 0, "url"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "url")}, +< {path.New("yaml", "boot_device", "luks", "tang", 0, "thumbprint"), path.New("json", "storage", "luks", 0, "clevis", "tang", 0, "thumbprint")}, +< {path.New("yaml", "boot_device", "luks", "threshold"), path.New("json", "storage", "luks", 0, "clevis", "threshold")}, +< {path.New("yaml", "boot_device", "luks", "tpm2"), path.New("json", "storage", "luks", 0, "clevis", "tpm2")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "device")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "label")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "name")}, +< {path.New("yaml", "boot_device", "luks"), path.New("json", "storage", "luks", 0, "wipeVolume")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 0, "wipeFilesystem")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "device")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "format")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "label")}, +< {path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", 1, "wipeFilesystem")}, +< {path.New("yaml", "storage", "filesystems", 0, "device"), path.New("json", "storage", "filesystems", 2, "device")}, +< {path.New("yaml", "storage", "filesystems", 0, "format"), path.New("json", "storage", "filesystems", 2, "format")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "label")}, +< {path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", 2, "wipeFilesystem")}, +< }, +< }, +< } +< +< // The partition sizes of existing layouts must never change, but +< // we use the constants in tests for clarity. Ensure no one has +< // changed them. +< assert.Equal(t, biosV1SizeMiB, 1) +< assert.Equal(t, prepV1SizeMiB, 4) +< assert.Equal(t, espV1SizeMiB, 127) +< assert.Equal(t, bootV1SizeMiB, 384) +< +< for i, test := range tests { +< actual, translations, r := test.in.ToIgn3_3Unvalidated(common.TranslateOptions{}) +< assert.Equal(t, test.out, actual, "#%d: translation mismatch", i) +< assert.Equal(t, report.Report{}, r, "#%d: non-empty report", i) +< baseutil.VerifyTranslations(t, translations, test.exceptions, "#%d", i) +< } +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/fcos/v1_4_exp/validate.go updated/vendor/github.com/coreos/fcct/config/fcos/v1_4_exp/validate.go +1,41d0 +< // 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 v1_4_exp +< +< import ( +< "github.com/coreos/fcct/config/common" +< +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< ) +< +< func (d BootDevice) Validate(c path.ContextPath) (r report.Report) { +< if d.Layout != nil { +< switch *d.Layout { +< case "aarch64", "ppc64le", "x86_64": +< default: +< r.AddOnError(c.Append("layout"), common.ErrUnknownBootDeviceLayout) +< } +< } +< r.Merge(d.Mirror.Validate(c.Append("mirror"))) +< return +< } +< +< func (m BootDeviceMirror) Validate(c path.ContextPath) (r report.Report) { +< if len(m.Devices) == 1 { +< r.AddOnError(c.Append("devices"), common.ErrTooFewMirrorDevices) +< } +< return +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/fcos/v1_4_exp/validate_test.go updated/vendor/github.com/coreos/fcct/config/fcos/v1_4_exp/validate_test.go +1,87d0 +< // 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 v1_4_exp +< +< import ( +< "testing" +< +< base "github.com/coreos/fcct/base/v0_4_exp" +< "github.com/coreos/fcct/config/common" +< +< "github.com/coreos/ignition/v2/config/util" +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< "github.com/stretchr/testify/assert" +< ) +< +< // TestValidateBootDevice tests boot device validation +< func TestValidateBootDevice(t *testing.T) { +< tests := []struct { +< in BootDevice +< out error +< errPath path.ContextPath +< }{ +< // empty config +< { +< BootDevice{}, +< nil, +< path.New("yaml"), +< }, +< // complete config +< { +< BootDevice{ +< Layout: util.StrToPtr("x86_64"), +< Luks: BootDeviceLuks{ +< Tang: []base.Tang{{ +< URL: "https://example.com/", +< Thumbprint: util.StrToPtr("x"), +< }}, +< Threshold: util.IntToPtr(2), +< Tpm2: util.BoolToPtr(true), +< }, +< Mirror: BootDeviceMirror{ +< Devices: []string{"/dev/vda", "/dev/vdb"}, +< }, +< }, +< nil, +< path.New("yaml"), +< }, +< // invalid layout +< { +< BootDevice{ +< Layout: util.StrToPtr("sparc"), +< }, +< common.ErrUnknownBootDeviceLayout, +< path.New("yaml", "layout"), +< }, +< // only one mirror device +< { +< BootDevice{ +< Mirror: BootDeviceMirror{ +< Devices: []string{"/dev/vda"}, +< }, +< }, +< common.ErrTooFewMirrorDevices, +< path.New("yaml", "mirror", "devices"), +< }, +< } +< +< for i, test := range tests { +< actual := test.in.Validate(path.New("yaml")) +< expected := report.Report{} +< expected.AddOnError(test.errPath, test.out) +< assert.Equal(t, expected, actual, "#%d: bad validation report", i) +< } +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/rhcos/v0_1/schema.go updated/vendor/github.com/coreos/fcct/config/rhcos/v0_1/schema.go +1,23d0 +< // 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_1 +< +< import ( +< fcos "github.com/coreos/fcct/config/fcos/v1_3" +< ) +< +< type Config struct { +< fcos.Config `yaml:",inline"` +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/rhcos/v0_1/translate.go updated/vendor/github.com/coreos/fcct/config/rhcos/v0_1/translate.go +1,39d0 +< // 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_1 +< +< import ( +< "github.com/coreos/fcct/config/common" +< cutil "github.com/coreos/fcct/config/util" +< +< "github.com/coreos/ignition/v2/config/v3_2/types" +< "github.com/coreos/vcontext/report" +< ) +< +< // ToIgn3_2 translates the config to an Ignition config. It returns a +< // report of any errors or warnings in the source and resultant config. If +< // the report has fatal errors or it encounters other problems translating, +< // an error is returned. +< func (c Config) ToIgn3_2(options common.TranslateOptions) (types.Config, report.Report, error) { +< cfg, r, err := cutil.Translate(c, "ToIgn3_2Unvalidated", options) +< return cfg.(types.Config), r, err +< } +< +< // ToIgn3_2Bytes translates from a v0.1 rcc to a v3.2.0 Ignition config. It returns a report of any errors or +< // warnings in the source and resultant config. If the report has fatal errors or it encounters other problems +< // translating, an error is returned. +< func ToIgn3_2Bytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { +< return cutil.TranslateBytes(input, &Config{}, "ToIgn3_2", options) +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/rhcos/v0_2_exp/schema.go updated/vendor/github.com/coreos/fcct/config/rhcos/v0_2_exp/schema.go +1,23d0 +< // 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_exp +< +< import ( +< fcos "github.com/coreos/fcct/config/fcos/v1_4_exp" +< ) +< +< type Config struct { +< fcos.Config `yaml:",inline"` +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/rhcos/v0_2_exp/translate.go updated/vendor/github.com/coreos/fcct/config/rhcos/v0_2_exp/translate.go +1,39d0 +< // 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_exp +< +< import ( +< "github.com/coreos/fcct/config/common" +< cutil "github.com/coreos/fcct/config/util" +< +< "github.com/coreos/ignition/v2/config/v3_3_experimental/types" +< "github.com/coreos/vcontext/report" +< ) +< +< // ToIgn3_3 translates the config to an Ignition config. It returns a +< // report of any errors or warnings in the source and resultant config. If +< // the report has fatal errors or it encounters other problems translating, +< // an error is returned. +< func (c Config) ToIgn3_3(options common.TranslateOptions) (types.Config, report.Report, error) { +< cfg, r, err := cutil.Translate(c, "ToIgn3_3Unvalidated", options) +< return cfg.(types.Config), r, err +< } +< +< // ToIgn3_3Bytes translates from a v0.2 rcc to a v3.3.0 Ignition config. It returns a report of any errors or +< // warnings in the source and resultant config. If the report has fatal errors or it encounters other problems +< // translating, an error is returned. +< func ToIgn3_3Bytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { +< return cutil.TranslateBytes(input, &Config{}, "ToIgn3_3", options) +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/util/util.go updated/vendor/github.com/coreos/fcct/config/util/util.go +1,177d0 +< // 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 util +< +< import ( +< "bytes" +< "reflect" +< "regexp" +< "strings" +< +< "github.com/coreos/fcct/config/common" +< "github.com/coreos/fcct/translate" +< +< "github.com/clarketm/json" +< ignvalidate "github.com/coreos/ignition/v2/config/validate" +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< "github.com/coreos/vcontext/tree" +< "github.com/coreos/vcontext/validate" +< vyaml "github.com/coreos/vcontext/yaml" +< "gopkg.in/yaml.v3" +< ) +< +< var ( +< snakeRe = regexp.MustCompile("([A-Z])") +< ) +< +< // Misc helpers +< +< // Translate translates cfg to the corresponding Ignition config version +< // using the named translation method on cfg, and returns the marshaled +< // 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 Translate(cfg interface{}, translateMethod string, options common.TranslateOptions) (interface{}, report.Report, error) { +< // Get method, and zero return value for error returns. +< method := reflect.ValueOf(cfg).MethodByName(translateMethod) +< zeroValue := reflect.Zero(method.Type().Out(0)).Interface() +< +< // Validate the input. +< r := validate.Validate(cfg, "yaml") +< if r.IsFatal() { +< return zeroValue, r, common.ErrInvalidSourceConfig +< } +< +< // Perform the translation. +< translateRet := method.Call([]reflect.Value{reflect.ValueOf(options)}) +< final := translateRet[0].Interface() +< translations := translateRet[1].Interface().(translate.TranslationSet) +< translateReport := translateRet[2].Interface().(report.Report) +< r.Merge(translateReport) +< if r.IsFatal() { +< return zeroValue, r, common.ErrInvalidSourceConfig +< } +< +< // Check for invalid duplicated keys. +< dupsReport := validate.ValidateCustom(final, "json", ignvalidate.ValidateDups) +< translateReportPaths(&dupsReport, translations) +< r.Merge(dupsReport) +< +< // Validate JSON semantics. +< jsonReport := validate.Validate(final, "json") +< translateReportPaths(&jsonReport, translations) +< r.Merge(jsonReport) +< +< if r.IsFatal() { +< return zeroValue, r, common.ErrInvalidGeneratedConfig +< } +< return final, r, nil +< } +< +< // TranslateBytes unmarshals the FCC specified in input into the struct +< // pointed to by container, translates it to the corresponding Ignition +< // config version using the named translation method, and returns the +< // marshaled 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, container interface{}, translateMethod string, options common.TranslateBytesOptions) ([]byte, report.Report, error) { +< cfg := container +< +< // Unmarshal the YAML. +< contextTree, err := unmarshal(input, cfg, options.Strict) +< if err != nil { +< return nil, report.Report{}, err +< } +< +< // Check for unused keys. +< unusedKeyCheck := func(v reflect.Value, c path.ContextPath) report.Report { +< return ignvalidate.ValidateUnusedKeys(v, c, contextTree) +< } +< r := validate.ValidateCustom(cfg, "yaml", unusedKeyCheck) +< r.Correlate(contextTree) +< if r.IsFatal() { +< return nil, r, common.ErrInvalidSourceConfig +< } +< +< // Perform the translation. +< translateRet := reflect.ValueOf(cfg).MethodByName(translateMethod).Call([]reflect.Value{reflect.ValueOf(options.TranslateOptions)}) +< final := translateRet[0].Interface() +< translateReport := translateRet[1].Interface().(report.Report) +< errVal := translateRet[2] +< translateReport.Correlate(contextTree) +< r.Merge(translateReport) +< if !errVal.IsNil() { +< return nil, r, errVal.Interface().(error) +< } +< if r.IsFatal() { +< return nil, r, common.ErrInvalidSourceConfig +< } +< +< // Marshal the JSON. +< outbytes, err := marshal(final, options.Pretty) +< return outbytes, r, err +< } +< +< // unmarshal unmarshals the data to "to" and also returns a context tree for the source. If strict +< // is set it errors out on unused keys. +< func unmarshal(data []byte, to interface{}, strict bool) (tree.Node, error) { +< 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 --no-dereference -N -r current/vendor/github.com/coreos/fcct/config/util/util_test.go updated/vendor/github.com/coreos/fcct/config/util/util_test.go +1,50d0 +< // 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 util +< +< import ( +< "testing" +< ) +< +< func TestSnake(t *testing.T) { +< tests := []struct { +< in string +< out string +< }{ +< {}, +< { +< "foo", +< "foo", +< }, +< { +< "snakeCase", +< "snake_case", +< }, +< { +< "longSnakeCase", +< "long_snake_case", +< }, +< { +< "snake_already", +< "snake_already", +< }, +< } +< +< for i, test := range tests { +< if snake(test.in) != test.out { +< t.Errorf("#%d: expected %q got %q", i, test.out, snake(test.in)) +< } +< } +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/docs/_config.yml updated/vendor/github.com/coreos/fcct/docs/_config.yml +1,39d0 +< title: coreos/fcct +< description: Fedora CoreOS Config Transpiler documentation +< baseurl: "/fcct" +< url: "https://coreos.github.io" +< # Comment above and use below for local development +< # url: "http://localhost:4000" +< permalink: /:title/ +< +< remote_theme: coreos/just-the-docs +< plugins: +< - jekyll-remote-theme +< +< color_scheme: coreos +< +< # Aux links for the upper right navigation +< aux_links: +< "FCCT on GitHub": +< - "https://github.com/coreos/fcct" +< +< footer_content: "Copyright © Red Hat, Inc. and others." +< +< # Footer last edited timestamp +< last_edit_timestamp: true +< last_edit_time_format: "%b %e %Y at %I:%M %p" +< +< # Footer "Edit this page on GitHub" link text +< gh_edit_link: true +< gh_edit_link_text: "Edit this page on GitHub" +< gh_edit_repository: "https://github.com/coreos/fcct" +< gh_edit_branch: "master" +< gh_edit_view_mode: "tree" +< +< compress_html: +< clippings: all +< comments: all +< endings: all +< startings: [] +< blanklines: false +< profile: false +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/docs/_sass/color_schemes/coreos.scss updated/vendor/github.com/coreos/fcct/docs/_sass/color_schemes/coreos.scss +1d0 +< $link-color: #53a3da; +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/docs/config-fcos-v1_0.md updated/vendor/github.com/coreos/fcct/docs/config-fcos-v1_0.md +1,134d0 +< --- +< layout: default +< title: Fedora CoreOS v1.0.0 +< parent: Configuration specifications +< nav_order: 49 +< --- +< +< # Fedora CoreOS Specification v1.0.0 +< +< The Fedora CoreOS configuration is a YAML document conforming to the following specification, with **_italicized_** entries being optional: +< +< * **variant** (string): used to differentiate configs for different operating systems. Must be `fcos` for this specification. +< * **version** (string): the semantic version of the spec for this document. This document is for version `1.0.0` and generates Ignition configs with version `3.0.0`. +< * **ignition** (object): metadata about the configuration itself. +< * **_config_** (objects): options related to the configuration. +< * **_merge_** (list of objects): a list of the configs to be merged to the current config. +< * **source** (string): the URL of the config. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. +< * **_verification_** (object): options related to the verification of the config. +< * **_hash_** (string): the hash of the config, in the form `-` where type is `sha512`. +< * **_replace_** (object): the config that will replace the current. +< * **source** (string): the URL of the config. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. +< * **_verification_** (object): options related to the verification of the config. +< * **_hash_** (string): the hash of the config, in the form `-` where type is `sha512`. +< * **_timeouts_** (object): options relating to `http` timeouts when fetching files over `http` or `https`. +< * **_http_response_headers_** (integer) the time to wait (in seconds) for the server's response headers (but not the body) after making a request. 0 indicates no timeout. Default is 10 seconds. +< * **_http_total_** (integer) the time limit (in seconds) for the operation (connection, request, and response), including retries. 0 indicates no timeout. Default is 0. +< * **_security_** (object): options relating to network security. +< * **_tls_** (object): options relating to TLS when fetching resources over `https`. +< * **_certificate_authorities_** (list of objects): the list of additional certificate authorities (in addition to the system authorities) to be used for TLS verification when fetching over `https`. All certificate authorities must have a unique `source`. +< * **source** (string): the URL of the certificate bundle (in PEM format). With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. +< * **_verification_** (object): options related to the verification of the certificate. +< * **_hash_** (string): the hash of the certificate, in the form `-` where type is sha512. +< * **_storage_** (object): describes the desired state of the system's storage devices. +< * **_disks_** (list of objects): the list of disks to be configured and their options. Every entry must have a unique `device`. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **_wipe_table_** (boolean): whether or not the partition tables shall be wiped. When true, the partition tables are erased before any further manipulation. Otherwise, the existing entries are left intact. +< * **_partitions_** (list of objects): the list of partitions and their configuration for this particular disk. Every partition must have a unique `number`, or if 0 is specified, a unique `label`. +< * **_label_** (string): the PARTLABEL for the partition. +< * **_number_** (integer): the partition number, which dictates it's position in the partition table (one-indexed). If zero, use the next available partition slot. +< * **_size_mib_** (integer): the size of the partition (in mebibytes). If zero, the partition will be made as large as possible. +< * **_start_mib_** (integer): the start of the partition (in mebibytes). If zero, the partition will be positioned at the start of the largest block available. +< * **_type_guid_** (string): the GPT [partition type GUID][part-types]. If omitted, the default will be 0FC63DAF-8483-4772-8E79-3D69D8477DE4 (Linux filesystem data). +< * **_guid_** (string): the GPT unique partition GUID. +< * **_wipe_partition_entry_** (boolean) if true, Ignition will clobber an existing partition if it does not match the config. If false (default), Ignition will fail instead. +< * **_should_exist_** (boolean) whether or not the partition with the specified `number` should exist. If omitted, it defaults to true. If false Ignition will either delete the specified partition or fail, depending on `wipePartitionEntry`. If false `number` must be specified and non-zero and `label`, `start`, `size`, `guid`, and `typeGuid` must all be omitted. +< * **_raid_** (list of objects): the list of RAID arrays to be configured. Every RAID array must have a unique `name`. +< * **name** (string): the name to use for the resulting md device. +< * **level** (string): the redundancy level of the array (e.g. linear, raid1, raid5, etc.). +< * **devices** (list of strings): the list of devices (referenced by their absolute path) in the array. +< * **_spares_** (integer): the number of spares (if applicable) in the array. +< * **_options_** (list of strings): any additional options to be passed to mdadm. +< * **_filesystems_** (list of objects): the list of filesystems to be configured. `path`, `device`, and `format` all need to be specified. Every filesystem must have a unique `device`. +< * **path** (string): the mount-point of the filesystem while Ignition is running relative to where the root filesystem will be mounted. This is not necessarily the same as where it should be mounted in the real root, but it is encouraged to make it the same. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **format** (string): the filesystem format (ext4, btrfs, xfs, vfat, or swap). +< * **_wipe_filesystem_** (boolean): whether or not to wipe the device before filesystem creation, see [the documentation on filesystems](https://coreos.github.io/ignition/operator-notes/#filesystem-reuse-semantics) for more information. +< * **_label_** (string): the label of the filesystem. +< * **_uuid_** (string): the uuid of the filesystem. +< * **_options_** (list of strings): any additional options to be passed to the format-specific mkfs utility. +< * **_files_** (list of objects): the list of files to be written. Every file, directory and link must have a unique `path`. +< * **path** (string): the absolute path to the file. +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. `contents` must be specified if `overwrite` is true. Defaults to false. +< * **_contents_** (object): options related to the contents of the file. +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the file contents. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. If source is omitted and a regular file already exists at the path, Ignition will do nothing. If source is omitted and no file exists, an empty file will be created. Mutually exclusive with `inline`. +< * **_inline_** (string): the contents of the file. Mutually exclusive with `source`. +< * **_verification_** (object): options related to the verification of the file contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is `sha512`. +< * **_append_** (list of objects): list of contents to be appended to the file. Follows the same stucture as `contents` +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the contents to append. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline`. +< * **_inline_** (string): the contents to append. Mutually exclusive with `source`. +< * **_verification_** (object): options related to the verification of the appended contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is `sha512`. +< * **_mode_** (integer): the file's permission mode. If not specified, the permission mode for files defaults to 0644 or the existing file's permissions if `overwrite` is false, `contents` is unspecified, and a file already exists at the path. +< * **_user_** (object): specifies the file's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **_directories_** (list of objects): the list of directories to be created. Every file, directory, and link must have a unique `path`. +< * **path** (string): the absolute path to the directory. +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. If false and a directory already exists at the path, Ignition will only set its permissions. If false and a non-directory exists at that path, Ignition will fail. Defaults to false. +< * **_mode_** (integer): the directory's permission mode. If not specified, the permission mode for directories defaults to 0755 or the mode of an existing directory if `overwrite` is false and a directory already exists at the path. +< * **_user_** (object): specifies the directory's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **_links_** (list of objects): the list of links to be created. Every file, directory, and link must have a unique `path`. +< * **path** (string): the absolute path to the link +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. If overwrite is false and a matching link exists at the path, Ignition will only set the owner and group. Defaults to false. +< * **_user_** (object): specifies the symbolic link's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **target** (string): the target path of the link +< * **_hard_** (boolean): a symbolic link is created if this is false, a hard one if this is true. +< * **_systemd_** (object): describes the desired state of the systemd units. +< * **_units_** (list of objects): the list of systemd units. +< * **name** (string): the name of the unit. This must be suffixed with a valid unit type (e.g. "thing.service"). Every unit must have a unique `name`. +< * **_enabled_** (boolean): whether or not the service shall be enabled. When true, the service is enabled. When false, the service is disabled. When omitted, the service is unmodified. In order for this to have any effect, the unit must have an install section. +< * **_mask_** (boolean): whether or not the service shall be masked. When true, the service is masked by symlinking it to `/dev/null`. +< * **_contents_** (string): the contents of the unit. +< * **_dropins_** (list of objects): the list of drop-ins for the unit. Every drop-in must have a unique `name`. +< * **name** (string): the name of the drop-in. This must be suffixed with ".conf". +< * **_contents_** (string): the contents of the drop-in. +< * **_passwd_** (object): describes the desired additions to the passwd database. +< * **_users_** (list of objects): the list of accounts that shall exist. All users must have a unique `name`. +< * **name** (string): the username for the account. +< * **_password_hash_** (string): the hashed password for the account. +< * **_ssh_authorized_keys_** (list of strings): a list of SSH keys to be added as an SSH key fragment at `.ssh/authorized_keys.d/ignition` in the user's home directory. All SSH keys must be unique. +< * **_uid_** (integer): the user ID of the account. +< * **_gecos_** (string): the GECOS field of the account. +< * **_home_dir_** (string): the home directory of the account. +< * **_no_create_home_** (boolean): whether or not to create the user's home directory. This only has an effect if the account doesn't exist yet. +< * **_primary_group_** (string): the name of the primary group of the account. +< * **_groups_** (list of strings): the list of supplementary groups of the account. +< * **_no_user_group_** (boolean): whether or not to create a group with the same name as the user. This only has an effect if the account doesn't exist yet. +< * **_no_log_init_** (boolean): whether or not to add the user to the lastlog and faillog databases. This only has an effect if the account doesn't exist yet. +< * **_shell_** (string): the login shell of the new account. +< * **_system_** (bool): whether or not this account should be a system account. This only has an effect if the account doesn't exist yet. +< * **_groups_** (list of objects): the list of groups to be added. All groups must have a unique `name`. +< * **name** (string): the name of the group. +< * **_gid_** (integer): the group ID of the new group. +< * **_password_hash_** (string): the hashed password of the new group. +< * **_system_** (bool): whether or not the group should be a system group. This only has an effect if the group doesn't exist yet. +< +< [part-types]: http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs +< [rfc2397]: https://tools.ietf.org/html/rfc2397 +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/docs/config-fcos-v1_1.md updated/vendor/github.com/coreos/fcct/docs/config-fcos-v1_1.md +1,167d0 +< --- +< layout: default +< title: Fedora CoreOS v1.1.0 +< parent: Configuration specifications +< nav_order: 48 +< --- +< +< # Fedora CoreOS Specification v1.1.0 +< +< The Fedora CoreOS configuration is a YAML document conforming to the following specification, with **_italicized_** entries being optional: +< +< * **variant** (string): used to differentiate configs for different operating systems. Must be `fcos` for this specification. +< * **version** (string): the semantic version of the spec for this document. This document is for version `1.1.0` and generates Ignition configs with version `3.1.0`. +< * **ignition** (object): metadata about the configuration itself. +< * **_config_** (objects): options related to the configuration. +< * **_merge_** (list of objects): a list of the configs to be merged to the current config. +< * **_source_** (string): the URL of the config. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the config. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the config, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the config. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_replace_** (object): the config that will replace the current. +< * **_source_** (string): the URL of the config. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the config. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the config, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the config. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_timeouts_** (object): options relating to `http` timeouts when fetching files over `http` or `https`. +< * **_http_response_headers_** (integer) the time to wait (in seconds) for the server's response headers (but not the body) after making a request. 0 indicates no timeout. Default is 10 seconds. +< * **_http_total_** (integer) the time limit (in seconds) for the operation (connection, request, and response), including retries. 0 indicates no timeout. Default is 0. +< * **_security_** (object): options relating to network security. +< * **_tls_** (object): options relating to TLS when fetching resources over `https`. +< * **_certificate_authorities_** (list of objects): the list of additional certificate authorities (in addition to the system authorities) to be used for TLS verification when fetching over `https`. All certificate authorities must have a unique `source`, `inline`, or `local`. +< * **_source_** (string): the URL of the certificate bundle (in PEM format). With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the certificate bundle (in PEM format). With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the certificate bundle (in PEM format), relative to the directory specified by the `--files-dir` command-line argument. With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the certificate. +< * **_hash_** (string): the hash of the certificate, in the form `-` where type is either `sha512` or `sha256`. +< * **_proxy_** (object): options relating to setting an `HTTP(S)` proxy when fetching resources. +< * **_httpProxy_** (string): will be used as the proxy URL for HTTP requests and HTTPS requests unless overridden by `httpsProxy` or `noProxy`. +< * **_httpsProxy_** (string): will be used as the proxy URL for HTTPS requests unless overridden by `noProxy`. +< * **_noProxy_** (list of strings): specifies a list of strings to hosts that should be excluded from proxying. Each value is represented by an `IP address prefix (1.2.3.4)`, `an IP address prefix in CIDR notation (1.2.3.4/8)`, `a domain name`, or `a special DNS label (*)`. An IP address prefix and domain name can also include a literal port number `(1.2.3.4:80)`. A domain name matches that name and all subdomains. A domain name with a leading `.` matches subdomains only. For example `foo.com` matches `foo.com` and `bar.foo.com`; `.y.com` matches `x.y.com` but not `y.com`. A single asterisk `(*)` indicates that no proxying should be done. +< * **_storage_** (object): describes the desired state of the system's storage devices. +< * **_disks_** (list of objects): the list of disks to be configured and their options. Every entry must have a unique `device`. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **_wipe_table_** (boolean): whether or not the partition tables shall be wiped. When true, the partition tables are erased before any further manipulation. Otherwise, the existing entries are left intact. +< * **_partitions_** (list of objects): the list of partitions and their configuration for this particular disk. Every partition must have a unique `number`, or if 0 is specified, a unique `label`. +< * **_label_** (string): the PARTLABEL for the partition. +< * **_number_** (integer): the partition number, which dictates it's position in the partition table (one-indexed). If zero, use the next available partition slot. +< * **_size_mib_** (integer): the size of the partition (in mebibytes). If zero, the partition will be made as large as possible. +< * **_start_mib_** (integer): the start of the partition (in mebibytes). If zero, the partition will be positioned at the start of the largest block available. +< * **_type_guid_** (string): the GPT [partition type GUID][part-types]. If omitted, the default will be 0FC63DAF-8483-4772-8E79-3D69D8477DE4 (Linux filesystem data). +< * **_guid_** (string): the GPT unique partition GUID. +< * **_wipe_partition_entry_** (boolean) if true, Ignition will clobber an existing partition if it does not match the config. If false (default), Ignition will fail instead. +< * **_should_exist_** (boolean) whether or not the partition with the specified `number` should exist. If omitted, it defaults to true. If false Ignition will either delete the specified partition or fail, depending on `wipePartitionEntry`. If false `number` must be specified and non-zero and `label`, `start`, `size`, `guid`, and `typeGuid` must all be omitted. +< * **_raid_** (list of objects): the list of RAID arrays to be configured. Every RAID array must have a unique `name`. +< * **name** (string): the name to use for the resulting md device. +< * **level** (string): the redundancy level of the array (e.g. linear, raid1, raid5, etc.). +< * **devices** (list of strings): the list of devices (referenced by their absolute path) in the array. +< * **_spares_** (integer): the number of spares (if applicable) in the array. +< * **_options_** (list of strings): any additional options to be passed to mdadm. +< * **_filesystems_** (list of objects): the list of filesystems to be configured. `path`, `device`, and `format` all need to be specified. Every filesystem must have a unique `device`. +< * **path** (string): the mount-point of the filesystem while Ignition is running relative to where the root filesystem will be mounted. This is not necessarily the same as where it should be mounted in the real root, but it is encouraged to make it the same. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **format** (string): the filesystem format (ext4, btrfs, xfs, vfat, or swap). +< * **_wipe_filesystem_** (boolean): whether or not to wipe the device before filesystem creation, see [the documentation on filesystems](https://coreos.github.io/ignition/operator-notes/#filesystem-reuse-semantics) for more information. +< * **_label_** (string): the label of the filesystem. +< * **_uuid_** (string): the uuid of the filesystem. +< * **_options_** (list of strings): any additional options to be passed to the format-specific mkfs utility. +< * **_mount_options_** (list of strings): any special options to be passed to the mount command. +< * **_with_mount_unit_** (bool): Whether to generate a generic mount unit for this filesystem as well. If a more specific unit is needed, a custom one can be specified in the `systemd.units` section. The unit will be named with the [escaped][systemd-escape] version of the `path`. +< * **_files_** (list of objects): the list of files to be written. Every file, directory and link must have a unique `path`. +< * **path** (string): the absolute path to the file. +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. `contents` must be specified if `overwrite` is true. Defaults to false. +< * **_contents_** (object): options related to the contents of the file. +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the file contents. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. If source is omitted and a regular file already exists at the path, Ignition will do nothing. If source is omitted and no file exists, an empty file will be created. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the file. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the file, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the file contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_append_** (list of objects): list of contents to be appended to the file. Follows the same stucture as `contents` +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the contents to append. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents to append. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents to append, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the appended contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_mode_** (integer): the file's permission mode. If not specified, the permission mode for files defaults to 0644 or the existing file's permissions if `overwrite` is false, `contents` is unspecified, and a file already exists at the path. +< * **_user_** (object): specifies the file's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **_directories_** (list of objects): the list of directories to be created. Every file, directory, and link must have a unique `path`. +< * **path** (string): the absolute path to the directory. +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. If false and a directory already exists at the path, Ignition will only set its permissions. If false and a non-directory exists at that path, Ignition will fail. Defaults to false. +< * **_mode_** (integer): the directory's permission mode. If not specified, the permission mode for directories defaults to 0755 or the mode of an existing directory if `overwrite` is false and a directory already exists at the path. +< * **_user_** (object): specifies the directory's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **_links_** (list of objects): the list of links to be created. Every file, directory, and link must have a unique `path`. +< * **path** (string): the absolute path to the link +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. If overwrite is false and a matching link exists at the path, Ignition will only set the owner and group. Defaults to false. +< * **_user_** (object): specifies the symbolic link's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **target** (string): the target path of the link +< * **_hard_** (boolean): a symbolic link is created if this is false, a hard one if this is true. +< * **_trees_** (list of objects): a list of local directory trees to be embedded in the config. Ownership is not preserved. File modes are set to 0755 if the local file is executable or 0644 otherwise. Attributes of files, directories, and symlinks can be overridden by creating a corresponding entry in the `files`, `directories`, or `links` section; such `files` entries must omit `contents` and such `links` entries must omit `target`. +< * **local** (string): the base of the local directory tree, relative to the directory specified by the `--files-dir` command-line argument. +< * **_path_** (string): the path of the tree within the target system. Defaults to `/`. +< * **_systemd_** (object): describes the desired state of the systemd units. +< * **_units_** (list of objects): the list of systemd units. +< * **name** (string): the name of the unit. This must be suffixed with a valid unit type (e.g. "thing.service"). Every unit must have a unique `name`. +< * **_enabled_** (boolean): whether or not the service shall be enabled. When true, the service is enabled. When false, the service is disabled. When omitted, the service is unmodified. In order for this to have any effect, the unit must have an install section. +< * **_mask_** (boolean): whether or not the service shall be masked. When true, the service is masked by symlinking it to `/dev/null`. +< * **_contents_** (string): the contents of the unit. +< * **_dropins_** (list of objects): the list of drop-ins for the unit. Every drop-in must have a unique `name`. +< * **name** (string): the name of the drop-in. This must be suffixed with ".conf". +< * **_contents_** (string): the contents of the drop-in. +< * **_passwd_** (object): describes the desired additions to the passwd database. +< * **_users_** (list of objects): the list of accounts that shall exist. All users must have a unique `name`. +< * **name** (string): the username for the account. +< * **_password_hash_** (string): the hashed password for the account. +< * **_ssh_authorized_keys_** (list of strings): a list of SSH keys to be added as an SSH key fragment at `.ssh/authorized_keys.d/ignition` in the user's home directory. All SSH keys must be unique. +< * **_uid_** (integer): the user ID of the account. +< * **_gecos_** (string): the GECOS field of the account. +< * **_home_dir_** (string): the home directory of the account. +< * **_no_create_home_** (boolean): whether or not to create the user's home directory. This only has an effect if the account doesn't exist yet. +< * **_primary_group_** (string): the name of the primary group of the account. +< * **_groups_** (list of strings): the list of supplementary groups of the account. +< * **_no_user_group_** (boolean): whether or not to create a group with the same name as the user. This only has an effect if the account doesn't exist yet. +< * **_no_log_init_** (boolean): whether or not to add the user to the lastlog and faillog databases. This only has an effect if the account doesn't exist yet. +< * **_shell_** (string): the login shell of the new account. +< * **_system_** (bool): whether or not this account should be a system account. This only has an effect if the account doesn't exist yet. +< * **_groups_** (list of objects): the list of groups to be added. All groups must have a unique `name`. +< * **name** (string): the name of the group. +< * **_gid_** (integer): the group ID of the new group. +< * **_password_hash_** (string): the hashed password of the new group. +< * **_system_** (bool): whether or not the group should be a system group. This only has an effect if the group doesn't exist yet. +< +< [part-types]: http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs +< [rfc2397]: https://tools.ietf.org/html/rfc2397 +< [systemd-escape]: https://www.freedesktop.org/software/systemd/man/systemd-escape.html +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/docs/config-fcos-v1_2.md updated/vendor/github.com/coreos/fcct/docs/config-fcos-v1_2.md +1,194d0 +< --- +< layout: default +< title: Fedora CoreOS v1.2.0 +< parent: Configuration specifications +< nav_order: 47 +< --- +< +< # Fedora CoreOS Specification v1.2.0 +< +< The Fedora CoreOS configuration is a YAML document conforming to the following specification, with **_italicized_** entries being optional: +< +< * **variant** (string): used to differentiate configs for different operating systems. Must be `fcos` for this specification. +< * **version** (string): the semantic version of the spec for this document. This document is for version `1.2.0` and generates Ignition configs with version `3.2.0`. +< * **ignition** (object): metadata about the configuration itself. +< * **_config_** (objects): options related to the configuration. +< * **_merge_** (list of objects): a list of the configs to be merged to the current config. +< * **_source_** (string): the URL of the config. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the config. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the config, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the config. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_replace_** (object): the config that will replace the current. +< * **_source_** (string): the URL of the config. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the config. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the config, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the config. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_timeouts_** (object): options relating to `http` timeouts when fetching files over `http` or `https`. +< * **_http_response_headers_** (integer) the time to wait (in seconds) for the server's response headers (but not the body) after making a request. 0 indicates no timeout. Default is 10 seconds. +< * **_http_total_** (integer) the time limit (in seconds) for the operation (connection, request, and response), including retries. 0 indicates no timeout. Default is 0. +< * **_security_** (object): options relating to network security. +< * **_tls_** (object): options relating to TLS when fetching resources over `https`. +< * **_certificate_authorities_** (list of objects): the list of additional certificate authorities (in addition to the system authorities) to be used for TLS verification when fetching over `https`. All certificate authorities must have a unique `source`, `inline`, or `local`. +< * **_source_** (string): the URL of the certificate bundle (in PEM format). With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the certificate bundle (in PEM format). With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the certificate bundle (in PEM format), relative to the directory specified by the `--files-dir` command-line argument. With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the certificate. +< * **_hash_** (string): the hash of the certificate, in the form `-` where type is either `sha512` or `sha256`. +< * **_proxy_** (object): options relating to setting an `HTTP(S)` proxy when fetching resources. +< * **_httpProxy_** (string): will be used as the proxy URL for HTTP requests and HTTPS requests unless overridden by `httpsProxy` or `noProxy`. +< * **_httpsProxy_** (string): will be used as the proxy URL for HTTPS requests unless overridden by `noProxy`. +< * **_noProxy_** (list of strings): specifies a list of strings to hosts that should be excluded from proxying. Each value is represented by an `IP address prefix (1.2.3.4)`, `an IP address prefix in CIDR notation (1.2.3.4/8)`, `a domain name`, or `a special DNS label (*)`. An IP address prefix and domain name can also include a literal port number `(1.2.3.4:80)`. A domain name matches that name and all subdomains. A domain name with a leading `.` matches subdomains only. For example `foo.com` matches `foo.com` and `bar.foo.com`; `.y.com` matches `x.y.com` but not `y.com`. A single asterisk `(*)` indicates that no proxying should be done. +< * **_storage_** (object): describes the desired state of the system's storage devices. +< * **_disks_** (list of objects): the list of disks to be configured and their options. Every entry must have a unique `device`. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **_wipe_table_** (boolean): whether or not the partition tables shall be wiped. When true, the partition tables are erased before any further manipulation. Otherwise, the existing entries are left intact. +< * **_partitions_** (list of objects): the list of partitions and their configuration for this particular disk. Every partition must have a unique `number`, or if 0 is specified, a unique `label`. +< * **_label_** (string): the PARTLABEL for the partition. +< * **_number_** (integer): the partition number, which dictates it's position in the partition table (one-indexed). If zero, use the next available partition slot. +< * **_size_mib_** (integer): the size of the partition (in mebibytes). If zero, the partition will be made as large as possible. +< * **_start_mib_** (integer): the start of the partition (in mebibytes). If zero, the partition will be positioned at the start of the largest block available. +< * **_type_guid_** (string): the GPT [partition type GUID][part-types]. If omitted, the default will be 0FC63DAF-8483-4772-8E79-3D69D8477DE4 (Linux filesystem data). +< * **_guid_** (string): the GPT unique partition GUID. +< * **_wipe_partition_entry_** (boolean) if true, Ignition will clobber an existing partition if it does not match the config. If false (default), Ignition will fail instead. +< * **_should_exist_** (boolean) whether or not the partition with the specified `number` should exist. If omitted, it defaults to true. If false Ignition will either delete the specified partition or fail, depending on `wipePartitionEntry`. If false `number` must be specified and non-zero and `label`, `start`, `size`, `guid`, and `typeGuid` must all be omitted. +< * **_raid_** (list of objects): the list of RAID arrays to be configured. Every RAID array must have a unique `name`. +< * **name** (string): the name to use for the resulting md device. +< * **level** (string): the redundancy level of the array (e.g. linear, raid1, raid5, etc.). +< * **devices** (list of strings): the list of devices (referenced by their absolute path) in the array. +< * **_spares_** (integer): the number of spares (if applicable) in the array. +< * **_options_** (list of strings): any additional options to be passed to mdadm. +< * **_filesystems_** (list of objects): the list of filesystems to be configured. `path`, `device`, and `format` all need to be specified. Every filesystem must have a unique `device`. +< * **path** (string): the mount-point of the filesystem while Ignition is running relative to where the root filesystem will be mounted. This is not necessarily the same as where it should be mounted in the real root, but it is encouraged to make it the same. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **format** (string): the filesystem format (ext4, btrfs, xfs, vfat, or swap). +< * **_wipe_filesystem_** (boolean): whether or not to wipe the device before filesystem creation, see [the documentation on filesystems](https://coreos.github.io/ignition/operator-notes/#filesystem-reuse-semantics) for more information. +< * **_label_** (string): the label of the filesystem. +< * **_uuid_** (string): the uuid of the filesystem. +< * **_options_** (list of strings): any additional options to be passed to the format-specific mkfs utility. +< * **_mount_options_** (list of strings): any special options to be passed to the mount command. +< * **_with_mount_unit_** (bool): Whether to generate a generic mount unit for this filesystem as well. If a more specific unit is needed, a custom one can be specified in the `systemd.units` section. The unit will be named with the [escaped][systemd-escape] version of the `path`. If your filesystem is located on a Tang-backed LUKS device, the unit will automatically require network access if you specify the device as `/dev/mapper/` or `/dev/disk/by-id/dm-name-`. +< * **_files_** (list of objects): the list of files to be written. Every file, directory and link must have a unique `path`. +< * **path** (string): the absolute path to the file. +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. `contents` must be specified if `overwrite` is true. Defaults to false. +< * **_contents_** (object): options related to the contents of the file. +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the file contents. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. If source is omitted and a regular file already exists at the path, Ignition will do nothing. If source is omitted and no file exists, an empty file will be created. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the file. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the file, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the file contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_append_** (list of objects): list of contents to be appended to the file. Follows the same stucture as `contents` +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the contents to append. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents to append. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents to append, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the appended contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_mode_** (integer): the file's permission mode. If not specified, the permission mode for files defaults to 0644 or the existing file's permissions if `overwrite` is false, `contents` is unspecified, and a file already exists at the path. +< * **_user_** (object): specifies the file's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **_directories_** (list of objects): the list of directories to be created. Every file, directory, and link must have a unique `path`. +< * **path** (string): the absolute path to the directory. +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. If false and a directory already exists at the path, Ignition will only set its permissions. If false and a non-directory exists at that path, Ignition will fail. Defaults to false. +< * **_mode_** (integer): the directory's permission mode. If not specified, the permission mode for directories defaults to 0755 or the mode of an existing directory if `overwrite` is false and a directory already exists at the path. +< * **_user_** (object): specifies the directory's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **_links_** (list of objects): the list of links to be created. Every file, directory, and link must have a unique `path`. +< * **path** (string): the absolute path to the link +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. If overwrite is false and a matching link exists at the path, Ignition will only set the owner and group. Defaults to false. +< * **_user_** (object): specifies the symbolic link's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **target** (string): the target path of the link +< * **_hard_** (boolean): a symbolic link is created if this is false, a hard one if this is true. +< * **_luks_** (list of objects): the list of luks devices to be created. Every device must have a unique `name`. +< * **name** (string): the name of the luks device. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **_key_file_** (string): options related to the contents of the key file. +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the key file contents. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the key file. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the key file, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the file contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_label_** (string): the label of the luks device. +< * **_uuid_** (string): the uuid of the luks device. +< * **_options_** (list of strings): any additional options to be passed to the cryptsetup utility. +< * **_wipe_volume_** (boolean): whether or not to wipe the device before volume creation, see [the Ignition documentation on filesystems](https://coreos.github.io/ignition/operator-notes/#filesystem-reuse-semantics) for more information. +< * **_clevis_** (object): describes the clevis configuration for the luks device. +< * **_tang_** (list of objects): describes a tang server. Every server must have a unique `url`. +< * **url** (string): url of the tang server. +< * **thumbprint** (string): thumbprint of a trusted signing key. +< * **_tpm2_** (bool): whether or not to use a tpm2 device. +< * **_threshold_** (int): sets the minimum number of pieces required to decrypt the device. +< * **_custom_** (object): overrides the clevis configuration. The `pin` & `config` will be passed directly to `clevis luks bind`. If specified, all other clevis options must be omitted. +< * **pin** (string): the clevis pin. +< * **config** (string): the clevis configuration JSON. +< * **_needs_network_** (bool): whether or not the device requires networking. +< * **_trees_** (list of objects): a list of local directory trees to be embedded in the config. Ownership is not preserved. File modes are set to 0755 if the local file is executable or 0644 otherwise. Attributes of files, directories, and symlinks can be overridden by creating a corresponding entry in the `files`, `directories`, or `links` section; such `files` entries must omit `contents` and such `links` entries must omit `target`. +< * **local** (string): the base of the local directory tree, relative to the directory specified by the `--files-dir` command-line argument. +< * **_path_** (string): the path of the tree within the target system. Defaults to `/`. +< * **_systemd_** (object): describes the desired state of the systemd units. +< * **_units_** (list of objects): the list of systemd units. +< * **name** (string): the name of the unit. This must be suffixed with a valid unit type (e.g. "thing.service"). Every unit must have a unique `name`. +< * **_enabled_** (boolean): whether or not the service shall be enabled. When true, the service is enabled. When false, the service is disabled. When omitted, the service is unmodified. In order for this to have any effect, the unit must have an install section. +< * **_mask_** (boolean): whether or not the service shall be masked. When true, the service is masked by symlinking it to `/dev/null`. +< * **_contents_** (string): the contents of the unit. +< * **_dropins_** (list of objects): the list of drop-ins for the unit. Every drop-in must have a unique `name`. +< * **name** (string): the name of the drop-in. This must be suffixed with ".conf". +< * **_contents_** (string): the contents of the drop-in. +< * **_passwd_** (object): describes the desired additions to the passwd database. +< * **_users_** (list of objects): the list of accounts that shall exist. All users must have a unique `name`. +< * **name** (string): the username for the account. +< * **_password_hash_** (string): the hashed password for the account. +< * **_ssh_authorized_keys_** (list of strings): a list of SSH keys to be added as an SSH key fragment at `.ssh/authorized_keys.d/ignition` in the user's home directory. All SSH keys must be unique. +< * **_uid_** (integer): the user ID of the account. +< * **_gecos_** (string): the GECOS field of the account. +< * **_home_dir_** (string): the home directory of the account. +< * **_no_create_home_** (boolean): whether or not to create the user's home directory. This only has an effect if the account doesn't exist yet. +< * **_primary_group_** (string): the name of the primary group of the account. +< * **_groups_** (list of strings): the list of supplementary groups of the account. +< * **_no_user_group_** (boolean): whether or not to create a group with the same name as the user. This only has an effect if the account doesn't exist yet. +< * **_no_log_init_** (boolean): whether or not to add the user to the lastlog and faillog databases. This only has an effect if the account doesn't exist yet. +< * **_shell_** (string): the login shell of the new account. +< * **_system_** (bool): whether or not this account should be a system account. This only has an effect if the account doesn't exist yet. +< * **_groups_** (list of objects): the list of groups to be added. All groups must have a unique `name`. +< * **name** (string): the name of the group. +< * **_gid_** (integer): the group ID of the new group. +< * **_password_hash_** (string): the hashed password of the new group. +< * **_system_** (bool): whether or not the group should be a system group. This only has an effect if the group doesn't exist yet. +< +< [part-types]: http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs +< [rfc2397]: https://tools.ietf.org/html/rfc2397 +< [systemd-escape]: https://www.freedesktop.org/software/systemd/man/systemd-escape.html +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/docs/config-fcos-v1_3.md updated/vendor/github.com/coreos/fcct/docs/config-fcos-v1_3.md +1,204d0 +< --- +< layout: default +< title: Fedora CoreOS v1.3.0 +< parent: Configuration specifications +< nav_order: 46 +< --- +< +< # Fedora CoreOS Specification v1.3.0 +< +< The Fedora CoreOS configuration is a YAML document conforming to the following specification, with **_italicized_** entries being optional: +< +< * **variant** (string): used to differentiate configs for different operating systems. Must be `fcos` for this specification. +< * **version** (string): the semantic version of the spec for this document. This document is for version `1.3.0` and generates Ignition configs with version `3.2.0`. +< * **ignition** (object): metadata about the configuration itself. +< * **_config_** (objects): options related to the configuration. +< * **_merge_** (list of objects): a list of the configs to be merged to the current config. +< * **_source_** (string): the URL of the config. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the config. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the config, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the config. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_replace_** (object): the config that will replace the current. +< * **_source_** (string): the URL of the config. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the config. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the config, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the config. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_timeouts_** (object): options relating to `http` timeouts when fetching files over `http` or `https`. +< * **_http_response_headers_** (integer) the time to wait (in seconds) for the server's response headers (but not the body) after making a request. 0 indicates no timeout. Default is 10 seconds. +< * **_http_total_** (integer) the time limit (in seconds) for the operation (connection, request, and response), including retries. 0 indicates no timeout. Default is 0. +< * **_security_** (object): options relating to network security. +< * **_tls_** (object): options relating to TLS when fetching resources over `https`. +< * **_certificate_authorities_** (list of objects): the list of additional certificate authorities (in addition to the system authorities) to be used for TLS verification when fetching over `https`. All certificate authorities must have a unique `source`, `inline`, or `local`. +< * **_source_** (string): the URL of the certificate bundle (in PEM format). With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the certificate bundle (in PEM format). With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the certificate bundle (in PEM format), relative to the directory specified by the `--files-dir` command-line argument. With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the certificate. +< * **_hash_** (string): the hash of the certificate, in the form `-` where type is either `sha512` or `sha256`. +< * **_proxy_** (object): options relating to setting an `HTTP(S)` proxy when fetching resources. +< * **_httpProxy_** (string): will be used as the proxy URL for HTTP requests and HTTPS requests unless overridden by `httpsProxy` or `noProxy`. +< * **_httpsProxy_** (string): will be used as the proxy URL for HTTPS requests unless overridden by `noProxy`. +< * **_noProxy_** (list of strings): specifies a list of strings to hosts that should be excluded from proxying. Each value is represented by an `IP address prefix (1.2.3.4)`, `an IP address prefix in CIDR notation (1.2.3.4/8)`, `a domain name`, or `a special DNS label (*)`. An IP address prefix and domain name can also include a literal port number `(1.2.3.4:80)`. A domain name matches that name and all subdomains. A domain name with a leading `.` matches subdomains only. For example `foo.com` matches `foo.com` and `bar.foo.com`; `.y.com` matches `x.y.com` but not `y.com`. A single asterisk `(*)` indicates that no proxying should be done. +< * **_storage_** (object): describes the desired state of the system's storage devices. +< * **_disks_** (list of objects): the list of disks to be configured and their options. Every entry must have a unique `device`. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **_wipe_table_** (boolean): whether or not the partition tables shall be wiped. When true, the partition tables are erased before any further manipulation. Otherwise, the existing entries are left intact. +< * **_partitions_** (list of objects): the list of partitions and their configuration for this particular disk. Every partition must have a unique `number`, or if 0 is specified, a unique `label`. +< * **_label_** (string): the PARTLABEL for the partition. +< * **_number_** (integer): the partition number, which dictates it's position in the partition table (one-indexed). If zero, use the next available partition slot. +< * **_size_mib_** (integer): the size of the partition (in mebibytes). If zero, the partition will be made as large as possible. +< * **_start_mib_** (integer): the start of the partition (in mebibytes). If zero, the partition will be positioned at the start of the largest block available. +< * **_type_guid_** (string): the GPT [partition type GUID][part-types]. If omitted, the default will be 0FC63DAF-8483-4772-8E79-3D69D8477DE4 (Linux filesystem data). +< * **_guid_** (string): the GPT unique partition GUID. +< * **_wipe_partition_entry_** (boolean) if true, Ignition will clobber an existing partition if it does not match the config. If false (default), Ignition will fail instead. +< * **_should_exist_** (boolean) whether or not the partition with the specified `number` should exist. If omitted, it defaults to true. If false Ignition will either delete the specified partition or fail, depending on `wipePartitionEntry`. If false `number` must be specified and non-zero and `label`, `start`, `size`, `guid`, and `typeGuid` must all be omitted. +< * **_raid_** (list of objects): the list of RAID arrays to be configured. Every RAID array must have a unique `name`. +< * **name** (string): the name to use for the resulting md device. +< * **level** (string): the redundancy level of the array (e.g. linear, raid1, raid5, etc.). +< * **devices** (list of strings): the list of devices (referenced by their absolute path) in the array. +< * **_spares_** (integer): the number of spares (if applicable) in the array. +< * **_options_** (list of strings): any additional options to be passed to mdadm. +< * **_filesystems_** (list of objects): the list of filesystems to be configured. `path`, `device`, and `format` all need to be specified. Every filesystem must have a unique `device`. +< * **path** (string): the mount-point of the filesystem while Ignition is running relative to where the root filesystem will be mounted. This is not necessarily the same as where it should be mounted in the real root, but it is encouraged to make it the same. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **format** (string): the filesystem format (ext4, btrfs, xfs, vfat, or swap). +< * **_wipe_filesystem_** (boolean): whether or not to wipe the device before filesystem creation, see [the documentation on filesystems](https://coreos.github.io/ignition/operator-notes/#filesystem-reuse-semantics) for more information. +< * **_label_** (string): the label of the filesystem. +< * **_uuid_** (string): the uuid of the filesystem. +< * **_options_** (list of strings): any additional options to be passed to the format-specific mkfs utility. +< * **_mount_options_** (list of strings): any special options to be passed to the mount command. +< * **_with_mount_unit_** (bool): Whether to generate a generic mount unit for this filesystem as well. If a more specific unit is needed, a custom one can be specified in the `systemd.units` section. The unit will be named with the [escaped][systemd-escape] version of the `path`. If your filesystem is located on a Tang-backed LUKS device, the unit will automatically require network access if you specify the device as `/dev/mapper/` or `/dev/disk/by-id/dm-name-`. +< * **_files_** (list of objects): the list of files to be written. Every file, directory and link must have a unique `path`. +< * **path** (string): the absolute path to the file. +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. `contents` must be specified if `overwrite` is true. Defaults to false. +< * **_contents_** (object): options related to the contents of the file. +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the file contents. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. If source is omitted and a regular file already exists at the path, Ignition will do nothing. If source is omitted and no file exists, an empty file will be created. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the file. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the file, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the file contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_append_** (list of objects): list of contents to be appended to the file. Follows the same stucture as `contents` +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the contents to append. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents to append. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents to append, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the appended contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_mode_** (integer): the file's permission mode. If not specified, the permission mode for files defaults to 0644 or the existing file's permissions if `overwrite` is false, `contents` is unspecified, and a file already exists at the path. +< * **_user_** (object): specifies the file's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **_directories_** (list of objects): the list of directories to be created. Every file, directory, and link must have a unique `path`. +< * **path** (string): the absolute path to the directory. +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. If false and a directory already exists at the path, Ignition will only set its permissions. If false and a non-directory exists at that path, Ignition will fail. Defaults to false. +< * **_mode_** (integer): the directory's permission mode. If not specified, the permission mode for directories defaults to 0755 or the mode of an existing directory if `overwrite` is false and a directory already exists at the path. +< * **_user_** (object): specifies the directory's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **_links_** (list of objects): the list of links to be created. Every file, directory, and link must have a unique `path`. +< * **path** (string): the absolute path to the link +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. If overwrite is false and a matching link exists at the path, Ignition will only set the owner and group. Defaults to false. +< * **_user_** (object): specifies the symbolic link's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **target** (string): the target path of the link +< * **_hard_** (boolean): a symbolic link is created if this is false, a hard one if this is true. +< * **_luks_** (list of objects): the list of luks devices to be created. Every device must have a unique `name`. +< * **name** (string): the name of the luks device. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **_key_file_** (string): options related to the contents of the key file. +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the key file contents. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the key file. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the key file, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the file contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_label_** (string): the label of the luks device. +< * **_uuid_** (string): the uuid of the luks device. +< * **_options_** (list of strings): any additional options to be passed to the cryptsetup utility. +< * **_wipe_volume_** (boolean): whether or not to wipe the device before volume creation, see [the Ignition documentation on filesystems](https://coreos.github.io/ignition/operator-notes/#filesystem-reuse-semantics) for more information. +< * **_clevis_** (object): describes the clevis configuration for the luks device. +< * **_tang_** (list of objects): describes a tang server. Every server must have a unique `url`. +< * **url** (string): url of the tang server. +< * **thumbprint** (string): thumbprint of a trusted signing key. +< * **_tpm2_** (bool): whether or not to use a tpm2 device. +< * **_threshold_** (int): sets the minimum number of pieces required to decrypt the device. +< * **_custom_** (object): overrides the clevis configuration. The `pin` & `config` will be passed directly to `clevis luks bind`. If specified, all other clevis options must be omitted. +< * **pin** (string): the clevis pin. +< * **config** (string): the clevis configuration JSON. +< * **_needs_network_** (bool): whether or not the device requires networking. +< * **_trees_** (list of objects): a list of local directory trees to be embedded in the config. Ownership is not preserved. File modes are set to 0755 if the local file is executable or 0644 otherwise. Attributes of files, directories, and symlinks can be overridden by creating a corresponding entry in the `files`, `directories`, or `links` section; such `files` entries must omit `contents` and such `links` entries must omit `target`. +< * **local** (string): the base of the local directory tree, relative to the directory specified by the `--files-dir` command-line argument. +< * **_path_** (string): the path of the tree within the target system. Defaults to `/`. +< * **_systemd_** (object): describes the desired state of the systemd units. +< * **_units_** (list of objects): the list of systemd units. +< * **name** (string): the name of the unit. This must be suffixed with a valid unit type (e.g. "thing.service"). Every unit must have a unique `name`. +< * **_enabled_** (boolean): whether or not the service shall be enabled. When true, the service is enabled. When false, the service is disabled. When omitted, the service is unmodified. In order for this to have any effect, the unit must have an install section. +< * **_mask_** (boolean): whether or not the service shall be masked. When true, the service is masked by symlinking it to `/dev/null`. +< * **_contents_** (string): the contents of the unit. +< * **_dropins_** (list of objects): the list of drop-ins for the unit. Every drop-in must have a unique `name`. +< * **name** (string): the name of the drop-in. This must be suffixed with ".conf". +< * **_contents_** (string): the contents of the drop-in. +< * **_passwd_** (object): describes the desired additions to the passwd database. +< * **_users_** (list of objects): the list of accounts that shall exist. All users must have a unique `name`. +< * **name** (string): the username for the account. +< * **_password_hash_** (string): the hashed password for the account. +< * **_ssh_authorized_keys_** (list of strings): a list of SSH keys to be added as an SSH key fragment at `.ssh/authorized_keys.d/ignition` in the user's home directory. All SSH keys must be unique. +< * **_uid_** (integer): the user ID of the account. +< * **_gecos_** (string): the GECOS field of the account. +< * **_home_dir_** (string): the home directory of the account. +< * **_no_create_home_** (boolean): whether or not to create the user's home directory. This only has an effect if the account doesn't exist yet. +< * **_primary_group_** (string): the name of the primary group of the account. +< * **_groups_** (list of strings): the list of supplementary groups of the account. +< * **_no_user_group_** (boolean): whether or not to create a group with the same name as the user. This only has an effect if the account doesn't exist yet. +< * **_no_log_init_** (boolean): whether or not to add the user to the lastlog and faillog databases. This only has an effect if the account doesn't exist yet. +< * **_shell_** (string): the login shell of the new account. +< * **_system_** (bool): whether or not this account should be a system account. This only has an effect if the account doesn't exist yet. +< * **_groups_** (list of objects): the list of groups to be added. All groups must have a unique `name`. +< * **name** (string): the name of the group. +< * **_gid_** (integer): the group ID of the new group. +< * **_password_hash_** (string): the hashed password of the new group. +< * **_system_** (bool): whether or not the group should be a system group. This only has an effect if the group doesn't exist yet. +< * **_boot_device_** (object): describes the desired boot device configuration. At least one of `luks` or `mirror` must be specified. +< * **_layout_** (string): the disk layout of the target OS image. Supported values are `aarch64`, `ppc64le`, and `x86_64`. Defaults to `x86_64`. +< * **_luks_** (object): describes the clevis configuration for encrypting the root filesystem. +< * **_tang_** (list of objects): describes a tang server. Every server must have a unique `url`. +< * **url** (string): url of the tang server. +< * **thumbprint** (string): thumbprint of a trusted signing key. +< * **_tpm2_** (bool): whether or not to use a tpm2 device. +< * **_threshold_** (int): sets the minimum number of pieces required to decrypt the device. +< * **_mirror_** (object): describes mirroring of the boot disk for fault tolerance. +< * **_devices_** (list of strings): the list of whole-disk devices (not partitions) to include in the disk array, referenced by their absolute path. At least two devices must be specified. +< +< [part-types]: http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs +< [rfc2397]: https://tools.ietf.org/html/rfc2397 +< [systemd-escape]: https://www.freedesktop.org/software/systemd/man/systemd-escape.html +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/docs/config-fcos-v1_4-exp.md updated/vendor/github.com/coreos/fcct/docs/config-fcos-v1_4-exp.md +1,206d0 +< --- +< layout: default +< title: Fedora CoreOS v1.4.0-experimental +< parent: Configuration specifications +< nav_order: 50 +< --- +< +< # Fedora CoreOS Specification v1.4.0-experimental +< +< **Note: This configuration is experimental and has not been stabilized. It is subject to change without warning or announcement.** +< +< The Fedora CoreOS configuration is a YAML document conforming to the following specification, with **_italicized_** entries being optional: +< +< * **variant** (string): used to differentiate configs for different operating systems. Must be `fcos` for this specification. +< * **version** (string): the semantic version of the spec for this document. This document is for version `1.4.0-experimental` and generates Ignition configs with version `3.3.0-experimental`. +< * **ignition** (object): metadata about the configuration itself. +< * **_config_** (objects): options related to the configuration. +< * **_merge_** (list of objects): a list of the configs to be merged to the current config. +< * **_source_** (string): the URL of the config. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the config. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the config, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the config. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_replace_** (object): the config that will replace the current. +< * **_source_** (string): the URL of the config. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the config. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the config, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the config. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_timeouts_** (object): options relating to `http` timeouts when fetching files over `http` or `https`. +< * **_http_response_headers_** (integer) the time to wait (in seconds) for the server's response headers (but not the body) after making a request. 0 indicates no timeout. Default is 10 seconds. +< * **_http_total_** (integer) the time limit (in seconds) for the operation (connection, request, and response), including retries. 0 indicates no timeout. Default is 0. +< * **_security_** (object): options relating to network security. +< * **_tls_** (object): options relating to TLS when fetching resources over `https`. +< * **_certificate_authorities_** (list of objects): the list of additional certificate authorities (in addition to the system authorities) to be used for TLS verification when fetching over `https`. All certificate authorities must have a unique `source`, `inline`, or `local`. +< * **_source_** (string): the URL of the certificate bundle (in PEM format). With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the certificate bundle (in PEM format). With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the certificate bundle (in PEM format), relative to the directory specified by the `--files-dir` command-line argument. With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the certificate. +< * **_hash_** (string): the hash of the certificate, in the form `-` where type is either `sha512` or `sha256`. +< * **_proxy_** (object): options relating to setting an `HTTP(S)` proxy when fetching resources. +< * **_httpProxy_** (string): will be used as the proxy URL for HTTP requests and HTTPS requests unless overridden by `httpsProxy` or `noProxy`. +< * **_httpsProxy_** (string): will be used as the proxy URL for HTTPS requests unless overridden by `noProxy`. +< * **_noProxy_** (list of strings): specifies a list of strings to hosts that should be excluded from proxying. Each value is represented by an `IP address prefix (1.2.3.4)`, `an IP address prefix in CIDR notation (1.2.3.4/8)`, `a domain name`, or `a special DNS label (*)`. An IP address prefix and domain name can also include a literal port number `(1.2.3.4:80)`. A domain name matches that name and all subdomains. A domain name with a leading `.` matches subdomains only. For example `foo.com` matches `foo.com` and `bar.foo.com`; `.y.com` matches `x.y.com` but not `y.com`. A single asterisk `(*)` indicates that no proxying should be done. +< * **_storage_** (object): describes the desired state of the system's storage devices. +< * **_disks_** (list of objects): the list of disks to be configured and their options. Every entry must have a unique `device`. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **_wipe_table_** (boolean): whether or not the partition tables shall be wiped. When true, the partition tables are erased before any further manipulation. Otherwise, the existing entries are left intact. +< * **_partitions_** (list of objects): the list of partitions and their configuration for this particular disk. Every partition must have a unique `number`, or if 0 is specified, a unique `label`. +< * **_label_** (string): the PARTLABEL for the partition. +< * **_number_** (integer): the partition number, which dictates it's position in the partition table (one-indexed). If zero, use the next available partition slot. +< * **_size_mib_** (integer): the size of the partition (in mebibytes). If zero, the partition will be made as large as possible. +< * **_start_mib_** (integer): the start of the partition (in mebibytes). If zero, the partition will be positioned at the start of the largest block available. +< * **_type_guid_** (string): the GPT [partition type GUID][part-types]. If omitted, the default will be 0FC63DAF-8483-4772-8E79-3D69D8477DE4 (Linux filesystem data). +< * **_guid_** (string): the GPT unique partition GUID. +< * **_wipe_partition_entry_** (boolean) if true, Ignition will clobber an existing partition if it does not match the config. If false (default), Ignition will fail instead. +< * **_should_exist_** (boolean) whether or not the partition with the specified `number` should exist. If omitted, it defaults to true. If false Ignition will either delete the specified partition or fail, depending on `wipePartitionEntry`. If false `number` must be specified and non-zero and `label`, `start`, `size`, `guid`, and `typeGuid` must all be omitted. +< * **_raid_** (list of objects): the list of RAID arrays to be configured. Every RAID array must have a unique `name`. +< * **name** (string): the name to use for the resulting md device. +< * **level** (string): the redundancy level of the array (e.g. linear, raid1, raid5, etc.). +< * **devices** (list of strings): the list of devices (referenced by their absolute path) in the array. +< * **_spares_** (integer): the number of spares (if applicable) in the array. +< * **_options_** (list of strings): any additional options to be passed to mdadm. +< * **_filesystems_** (list of objects): the list of filesystems to be configured. `path`, `device`, and `format` all need to be specified. Every filesystem must have a unique `device`. +< * **path** (string): the mount-point of the filesystem while Ignition is running relative to where the root filesystem will be mounted. This is not necessarily the same as where it should be mounted in the real root, but it is encouraged to make it the same. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **format** (string): the filesystem format (ext4, btrfs, xfs, vfat, or swap). +< * **_wipe_filesystem_** (boolean): whether or not to wipe the device before filesystem creation, see [the documentation on filesystems](https://coreos.github.io/ignition/operator-notes/#filesystem-reuse-semantics) for more information. +< * **_label_** (string): the label of the filesystem. +< * **_uuid_** (string): the uuid of the filesystem. +< * **_options_** (list of strings): any additional options to be passed to the format-specific mkfs utility. +< * **_mount_options_** (list of strings): any special options to be passed to the mount command. +< * **_with_mount_unit_** (bool): Whether to generate a generic mount unit for this filesystem as well. If a more specific unit is needed, a custom one can be specified in the `systemd.units` section. The unit will be named with the [escaped][systemd-escape] version of the `path`. If your filesystem is located on a Tang-backed LUKS device, the unit will automatically require network access if you specify the device as `/dev/mapper/` or `/dev/disk/by-id/dm-name-`. +< * **_files_** (list of objects): the list of files to be written. Every file, directory and link must have a unique `path`. +< * **path** (string): the absolute path to the file. +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. `contents` must be specified if `overwrite` is true. Defaults to false. +< * **_contents_** (object): options related to the contents of the file. +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the file contents. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. If source is omitted and a regular file already exists at the path, Ignition will do nothing. If source is omitted and no file exists, an empty file will be created. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the file. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the file, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the file contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_append_** (list of objects): list of contents to be appended to the file. Follows the same stucture as `contents` +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the contents to append. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents to append. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents to append, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the appended contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_mode_** (integer): the file's permission mode. If not specified, the permission mode for files defaults to 0644 or the existing file's permissions if `overwrite` is false, `contents` is unspecified, and a file already exists at the path. +< * **_user_** (object): specifies the file's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **_directories_** (list of objects): the list of directories to be created. Every file, directory, and link must have a unique `path`. +< * **path** (string): the absolute path to the directory. +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. If false and a directory already exists at the path, Ignition will only set its permissions. If false and a non-directory exists at that path, Ignition will fail. Defaults to false. +< * **_mode_** (integer): the directory's permission mode. If not specified, the permission mode for directories defaults to 0755 or the mode of an existing directory if `overwrite` is false and a directory already exists at the path. +< * **_user_** (object): specifies the directory's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **_links_** (list of objects): the list of links to be created. Every file, directory, and link must have a unique `path`. +< * **path** (string): the absolute path to the link +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. If overwrite is false and a matching link exists at the path, Ignition will only set the owner and group. Defaults to false. +< * **_user_** (object): specifies the symbolic link's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **target** (string): the target path of the link +< * **_hard_** (boolean): a symbolic link is created if this is false, a hard one if this is true. +< * **_luks_** (list of objects): the list of luks devices to be created. Every device must have a unique `name`. +< * **name** (string): the name of the luks device. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **_key_file_** (string): options related to the contents of the key file. +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the key file contents. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the key file. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the key file, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the file contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_label_** (string): the label of the luks device. +< * **_uuid_** (string): the uuid of the luks device. +< * **_options_** (list of strings): any additional options to be passed to the cryptsetup utility. +< * **_wipe_volume_** (boolean): whether or not to wipe the device before volume creation, see [the Ignition documentation on filesystems](https://coreos.github.io/ignition/operator-notes/#filesystem-reuse-semantics) for more information. +< * **_clevis_** (object): describes the clevis configuration for the luks device. +< * **_tang_** (list of objects): describes a tang server. Every server must have a unique `url`. +< * **url** (string): url of the tang server. +< * **thumbprint** (string): thumbprint of a trusted signing key. +< * **_tpm2_** (bool): whether or not to use a tpm2 device. +< * **_threshold_** (int): sets the minimum number of pieces required to decrypt the device. +< * **_custom_** (object): overrides the clevis configuration. The `pin` & `config` will be passed directly to `clevis luks bind`. If specified, all other clevis options must be omitted. +< * **pin** (string): the clevis pin. +< * **config** (string): the clevis configuration JSON. +< * **_needs_network_** (bool): whether or not the device requires networking. +< * **_trees_** (list of objects): a list of local directory trees to be embedded in the config. Ownership is not preserved. File modes are set to 0755 if the local file is executable or 0644 otherwise. Attributes of files, directories, and symlinks can be overridden by creating a corresponding entry in the `files`, `directories`, or `links` section; such `files` entries must omit `contents` and such `links` entries must omit `target`. +< * **local** (string): the base of the local directory tree, relative to the directory specified by the `--files-dir` command-line argument. +< * **_path_** (string): the path of the tree within the target system. Defaults to `/`. +< * **_systemd_** (object): describes the desired state of the systemd units. +< * **_units_** (list of objects): the list of systemd units. +< * **name** (string): the name of the unit. This must be suffixed with a valid unit type (e.g. "thing.service"). Every unit must have a unique `name`. +< * **_enabled_** (boolean): whether or not the service shall be enabled. When true, the service is enabled. When false, the service is disabled. When omitted, the service is unmodified. In order for this to have any effect, the unit must have an install section. +< * **_mask_** (boolean): whether or not the service shall be masked. When true, the service is masked by symlinking it to `/dev/null`. +< * **_contents_** (string): the contents of the unit. +< * **_dropins_** (list of objects): the list of drop-ins for the unit. Every drop-in must have a unique `name`. +< * **name** (string): the name of the drop-in. This must be suffixed with ".conf". +< * **_contents_** (string): the contents of the drop-in. +< * **_passwd_** (object): describes the desired additions to the passwd database. +< * **_users_** (list of objects): the list of accounts that shall exist. All users must have a unique `name`. +< * **name** (string): the username for the account. +< * **_password_hash_** (string): the hashed password for the account. +< * **_ssh_authorized_keys_** (list of strings): a list of SSH keys to be added as an SSH key fragment at `.ssh/authorized_keys.d/ignition` in the user's home directory. All SSH keys must be unique. +< * **_uid_** (integer): the user ID of the account. +< * **_gecos_** (string): the GECOS field of the account. +< * **_home_dir_** (string): the home directory of the account. +< * **_no_create_home_** (boolean): whether or not to create the user's home directory. This only has an effect if the account doesn't exist yet. +< * **_primary_group_** (string): the name of the primary group of the account. +< * **_groups_** (list of strings): the list of supplementary groups of the account. +< * **_no_user_group_** (boolean): whether or not to create a group with the same name as the user. This only has an effect if the account doesn't exist yet. +< * **_no_log_init_** (boolean): whether or not to add the user to the lastlog and faillog databases. This only has an effect if the account doesn't exist yet. +< * **_shell_** (string): the login shell of the new account. +< * **_system_** (bool): whether or not this account should be a system account. This only has an effect if the account doesn't exist yet. +< * **_groups_** (list of objects): the list of groups to be added. All groups must have a unique `name`. +< * **name** (string): the name of the group. +< * **_gid_** (integer): the group ID of the new group. +< * **_password_hash_** (string): the hashed password of the new group. +< * **_system_** (bool): whether or not the group should be a system group. This only has an effect if the group doesn't exist yet. +< * **_boot_device_** (object): describes the desired boot device configuration. At least one of `luks` or `mirror` must be specified. +< * **_layout_** (string): the disk layout of the target OS image. Supported values are `aarch64`, `ppc64le`, and `x86_64`. Defaults to `x86_64`. +< * **_luks_** (object): describes the clevis configuration for encrypting the root filesystem. +< * **_tang_** (list of objects): describes a tang server. Every server must have a unique `url`. +< * **url** (string): url of the tang server. +< * **thumbprint** (string): thumbprint of a trusted signing key. +< * **_tpm2_** (bool): whether or not to use a tpm2 device. +< * **_threshold_** (int): sets the minimum number of pieces required to decrypt the device. +< * **_mirror_** (object): describes mirroring of the boot disk for fault tolerance. +< * **_devices_** (list of strings): the list of whole-disk devices (not partitions) to include in the disk array, referenced by their absolute path. At least two devices must be specified. +< +< [part-types]: http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs +< [rfc2397]: https://tools.ietf.org/html/rfc2397 +< [systemd-escape]: https://www.freedesktop.org/software/systemd/man/systemd-escape.html +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/docs/config-rhcos-v0_1.md updated/vendor/github.com/coreos/fcct/docs/config-rhcos-v0_1.md +1,204d0 +< --- +< layout: default +< title: RHEL CoreOS v0.1.0 +< parent: Configuration specifications +< nav_order: 99 +< --- +< +< # RHEL CoreOS Specification v0.1.0 +< +< The RHEL CoreOS configuration is a YAML document conforming to the following specification, with **_italicized_** entries being optional: +< +< * **variant** (string): used to differentiate configs for different operating systems. Must be `rhcos` for this specification. +< * **version** (string): the semantic version of the spec for this document. This document is for version `0.1.0` and generates Ignition configs with version `3.2.0`. +< * **ignition** (object): metadata about the configuration itself. +< * **_config_** (objects): options related to the configuration. +< * **_merge_** (list of objects): a list of the configs to be merged to the current config. +< * **_source_** (string): the URL of the config. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the config. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the config, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the config. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_replace_** (object): the config that will replace the current. +< * **_source_** (string): the URL of the config. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the config. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the config, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the config. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_timeouts_** (object): options relating to `http` timeouts when fetching files over `http` or `https`. +< * **_http_response_headers_** (integer) the time to wait (in seconds) for the server's response headers (but not the body) after making a request. 0 indicates no timeout. Default is 10 seconds. +< * **_http_total_** (integer) the time limit (in seconds) for the operation (connection, request, and response), including retries. 0 indicates no timeout. Default is 0. +< * **_security_** (object): options relating to network security. +< * **_tls_** (object): options relating to TLS when fetching resources over `https`. +< * **_certificate_authorities_** (list of objects): the list of additional certificate authorities (in addition to the system authorities) to be used for TLS verification when fetching over `https`. All certificate authorities must have a unique `source`, `inline`, or `local`. +< * **_source_** (string): the URL of the certificate bundle (in PEM format). With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the certificate bundle (in PEM format). With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the certificate bundle (in PEM format), relative to the directory specified by the `--files-dir` command-line argument. With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the certificate. +< * **_hash_** (string): the hash of the certificate, in the form `-` where type is either `sha512` or `sha256`. +< * **_proxy_** (object): options relating to setting an `HTTP(S)` proxy when fetching resources. +< * **_httpProxy_** (string): will be used as the proxy URL for HTTP requests and HTTPS requests unless overridden by `httpsProxy` or `noProxy`. +< * **_httpsProxy_** (string): will be used as the proxy URL for HTTPS requests unless overridden by `noProxy`. +< * **_noProxy_** (list of strings): specifies a list of strings to hosts that should be excluded from proxying. Each value is represented by an `IP address prefix (1.2.3.4)`, `an IP address prefix in CIDR notation (1.2.3.4/8)`, `a domain name`, or `a special DNS label (*)`. An IP address prefix and domain name can also include a literal port number `(1.2.3.4:80)`. A domain name matches that name and all subdomains. A domain name with a leading `.` matches subdomains only. For example `foo.com` matches `foo.com` and `bar.foo.com`; `.y.com` matches `x.y.com` but not `y.com`. A single asterisk `(*)` indicates that no proxying should be done. +< * **_storage_** (object): describes the desired state of the system's storage devices. +< * **_disks_** (list of objects): the list of disks to be configured and their options. Every entry must have a unique `device`. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **_wipe_table_** (boolean): whether or not the partition tables shall be wiped. When true, the partition tables are erased before any further manipulation. Otherwise, the existing entries are left intact. +< * **_partitions_** (list of objects): the list of partitions and their configuration for this particular disk. Every partition must have a unique `number`, or if 0 is specified, a unique `label`. +< * **_label_** (string): the PARTLABEL for the partition. +< * **_number_** (integer): the partition number, which dictates it's position in the partition table (one-indexed). If zero, use the next available partition slot. +< * **_size_mib_** (integer): the size of the partition (in mebibytes). If zero, the partition will be made as large as possible. +< * **_start_mib_** (integer): the start of the partition (in mebibytes). If zero, the partition will be positioned at the start of the largest block available. +< * **_type_guid_** (string): the GPT [partition type GUID][part-types]. If omitted, the default will be 0FC63DAF-8483-4772-8E79-3D69D8477DE4 (Linux filesystem data). +< * **_guid_** (string): the GPT unique partition GUID. +< * **_wipe_partition_entry_** (boolean) if true, Ignition will clobber an existing partition if it does not match the config. If false (default), Ignition will fail instead. +< * **_should_exist_** (boolean) whether or not the partition with the specified `number` should exist. If omitted, it defaults to true. If false Ignition will either delete the specified partition or fail, depending on `wipePartitionEntry`. If false `number` must be specified and non-zero and `label`, `start`, `size`, `guid`, and `typeGuid` must all be omitted. +< * **_raid_** (list of objects): the list of RAID arrays to be configured. Every RAID array must have a unique `name`. +< * **name** (string): the name to use for the resulting md device. +< * **level** (string): the redundancy level of the array (e.g. linear, raid1, raid5, etc.). +< * **devices** (list of strings): the list of devices (referenced by their absolute path) in the array. +< * **_spares_** (integer): the number of spares (if applicable) in the array. +< * **_options_** (list of strings): any additional options to be passed to mdadm. +< * **_filesystems_** (list of objects): the list of filesystems to be configured. `path`, `device`, and `format` all need to be specified. Every filesystem must have a unique `device`. +< * **path** (string): the mount-point of the filesystem while Ignition is running relative to where the root filesystem will be mounted. This is not necessarily the same as where it should be mounted in the real root, but it is encouraged to make it the same. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **format** (string): the filesystem format (ext4, btrfs, xfs, vfat, or swap). +< * **_wipe_filesystem_** (boolean): whether or not to wipe the device before filesystem creation, see [the documentation on filesystems](https://coreos.github.io/ignition/operator-notes/#filesystem-reuse-semantics) for more information. +< * **_label_** (string): the label of the filesystem. +< * **_uuid_** (string): the uuid of the filesystem. +< * **_options_** (list of strings): any additional options to be passed to the format-specific mkfs utility. +< * **_mount_options_** (list of strings): any special options to be passed to the mount command. +< * **_with_mount_unit_** (bool): Whether to generate a generic mount unit for this filesystem as well. If a more specific unit is needed, a custom one can be specified in the `systemd.units` section. The unit will be named with the [escaped][systemd-escape] version of the `path`. If your filesystem is located on a Tang-backed LUKS device, the unit will automatically require network access if you specify the device as `/dev/mapper/` or `/dev/disk/by-id/dm-name-`. +< * **_files_** (list of objects): the list of files to be written. Every file, directory and link must have a unique `path`. +< * **path** (string): the absolute path to the file. +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. `contents` must be specified if `overwrite` is true. Defaults to false. +< * **_contents_** (object): options related to the contents of the file. +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the file contents. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. If source is omitted and a regular file already exists at the path, Ignition will do nothing. If source is omitted and no file exists, an empty file will be created. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the file. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the file, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the file contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_append_** (list of objects): list of contents to be appended to the file. Follows the same stucture as `contents` +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the contents to append. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents to append. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents to append, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the appended contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_mode_** (integer): the file's permission mode. If not specified, the permission mode for files defaults to 0644 or the existing file's permissions if `overwrite` is false, `contents` is unspecified, and a file already exists at the path. +< * **_user_** (object): specifies the file's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **_directories_** (list of objects): the list of directories to be created. Every file, directory, and link must have a unique `path`. +< * **path** (string): the absolute path to the directory. +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. If false and a directory already exists at the path, Ignition will only set its permissions. If false and a non-directory exists at that path, Ignition will fail. Defaults to false. +< * **_mode_** (integer): the directory's permission mode. If not specified, the permission mode for directories defaults to 0755 or the mode of an existing directory if `overwrite` is false and a directory already exists at the path. +< * **_user_** (object): specifies the directory's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **_links_** (list of objects): the list of links to be created. Every file, directory, and link must have a unique `path`. +< * **path** (string): the absolute path to the link +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. If overwrite is false and a matching link exists at the path, Ignition will only set the owner and group. Defaults to false. +< * **_user_** (object): specifies the symbolic link's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **target** (string): the target path of the link +< * **_hard_** (boolean): a symbolic link is created if this is false, a hard one if this is true. +< * **_luks_** (list of objects): the list of luks devices to be created. Every device must have a unique `name`. +< * **name** (string): the name of the luks device. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **_key_file_** (string): options related to the contents of the key file. +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the key file contents. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the key file. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the key file, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the file contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_label_** (string): the label of the luks device. +< * **_uuid_** (string): the uuid of the luks device. +< * **_options_** (list of strings): any additional options to be passed to the cryptsetup utility. +< * **_wipe_volume_** (boolean): whether or not to wipe the device before volume creation, see [the Ignition documentation on filesystems](https://coreos.github.io/ignition/operator-notes/#filesystem-reuse-semantics) for more information. +< * **_clevis_** (object): describes the clevis configuration for the luks device. +< * **_tang_** (list of objects): describes a tang server. Every server must have a unique `url`. +< * **url** (string): url of the tang server. +< * **thumbprint** (string): thumbprint of a trusted signing key. +< * **_tpm2_** (bool): whether or not to use a tpm2 device. +< * **_threshold_** (int): sets the minimum number of pieces required to decrypt the device. +< * **_custom_** (object): overrides the clevis configuration. The `pin` & `config` will be passed directly to `clevis luks bind`. If specified, all other clevis options must be omitted. +< * **pin** (string): the clevis pin. +< * **config** (string): the clevis configuration JSON. +< * **_needs_network_** (bool): whether or not the device requires networking. +< * **_trees_** (list of objects): a list of local directory trees to be embedded in the config. Ownership is not preserved. File modes are set to 0755 if the local file is executable or 0644 otherwise. Attributes of files, directories, and symlinks can be overridden by creating a corresponding entry in the `files`, `directories`, or `links` section; such `files` entries must omit `contents` and such `links` entries must omit `target`. +< * **local** (string): the base of the local directory tree, relative to the directory specified by the `--files-dir` command-line argument. +< * **_path_** (string): the path of the tree within the target system. Defaults to `/`. +< * **_systemd_** (object): describes the desired state of the systemd units. +< * **_units_** (list of objects): the list of systemd units. +< * **name** (string): the name of the unit. This must be suffixed with a valid unit type (e.g. "thing.service"). Every unit must have a unique `name`. +< * **_enabled_** (boolean): whether or not the service shall be enabled. When true, the service is enabled. When false, the service is disabled. When omitted, the service is unmodified. In order for this to have any effect, the unit must have an install section. +< * **_mask_** (boolean): whether or not the service shall be masked. When true, the service is masked by symlinking it to `/dev/null`. +< * **_contents_** (string): the contents of the unit. +< * **_dropins_** (list of objects): the list of drop-ins for the unit. Every drop-in must have a unique `name`. +< * **name** (string): the name of the drop-in. This must be suffixed with ".conf". +< * **_contents_** (string): the contents of the drop-in. +< * **_passwd_** (object): describes the desired additions to the passwd database. +< * **_users_** (list of objects): the list of accounts that shall exist. All users must have a unique `name`. +< * **name** (string): the username for the account. +< * **_password_hash_** (string): the hashed password for the account. +< * **_ssh_authorized_keys_** (list of strings): a list of SSH keys to be added as an SSH key fragment at `.ssh/authorized_keys.d/ignition` in the user's home directory. All SSH keys must be unique. +< * **_uid_** (integer): the user ID of the account. +< * **_gecos_** (string): the GECOS field of the account. +< * **_home_dir_** (string): the home directory of the account. +< * **_no_create_home_** (boolean): whether or not to create the user's home directory. This only has an effect if the account doesn't exist yet. +< * **_primary_group_** (string): the name of the primary group of the account. +< * **_groups_** (list of strings): the list of supplementary groups of the account. +< * **_no_user_group_** (boolean): whether or not to create a group with the same name as the user. This only has an effect if the account doesn't exist yet. +< * **_no_log_init_** (boolean): whether or not to add the user to the lastlog and faillog databases. This only has an effect if the account doesn't exist yet. +< * **_shell_** (string): the login shell of the new account. +< * **_system_** (bool): whether or not this account should be a system account. This only has an effect if the account doesn't exist yet. +< * **_groups_** (list of objects): the list of groups to be added. All groups must have a unique `name`. +< * **name** (string): the name of the group. +< * **_gid_** (integer): the group ID of the new group. +< * **_password_hash_** (string): the hashed password of the new group. +< * **_system_** (bool): whether or not the group should be a system group. This only has an effect if the group doesn't exist yet. +< * **_boot_device_** (object): describes the desired boot device configuration. At least one of `luks` or `mirror` must be specified. +< * **_layout_** (string): the disk layout of the target OS image. Supported values are `aarch64`, `ppc64le`, and `x86_64`. Defaults to `x86_64`. +< * **_luks_** (object): describes the clevis configuration for encrypting the root filesystem. +< * **_tang_** (list of objects): describes a tang server. Every server must have a unique `url`. +< * **url** (string): url of the tang server. +< * **thumbprint** (string): thumbprint of a trusted signing key. +< * **_tpm2_** (bool): whether or not to use a tpm2 device. +< * **_threshold_** (int): sets the minimum number of pieces required to decrypt the device. +< * **_mirror_** (object): describes mirroring of the boot disk for fault tolerance. +< * **_devices_** (list of strings): the list of whole-disk devices (not partitions) to include in the disk array, referenced by their absolute path. At least two devices must be specified. +< +< [part-types]: http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs +< [rfc2397]: https://tools.ietf.org/html/rfc2397 +< [systemd-escape]: https://www.freedesktop.org/software/systemd/man/systemd-escape.html +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/docs/config-rhcos-v0_2-exp.md updated/vendor/github.com/coreos/fcct/docs/config-rhcos-v0_2-exp.md +1,206d0 +< --- +< layout: default +< title: RHEL CoreOS v0.2.0-experimental +< parent: Configuration specifications +< nav_order: 100 +< --- +< +< # RHEL CoreOS Specification v0.2.0-experimental +< +< **Note: This configuration is experimental and has not been stabilized. It is subject to change without warning or announcement.** +< +< The RHEL CoreOS configuration is a YAML document conforming to the following specification, with **_italicized_** entries being optional: +< +< * **variant** (string): used to differentiate configs for different operating systems. Must be `rhcos` for this specification. +< * **version** (string): the semantic version of the spec for this document. This document is for version `0.2.0-experimental` and generates Ignition configs with version `3.3.0-experimental`. +< * **ignition** (object): metadata about the configuration itself. +< * **_config_** (objects): options related to the configuration. +< * **_merge_** (list of objects): a list of the configs to be merged to the current config. +< * **_source_** (string): the URL of the config. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the config. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the config, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the config. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_replace_** (object): the config that will replace the current. +< * **_source_** (string): the URL of the config. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the config. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the config, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the config. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_timeouts_** (object): options relating to `http` timeouts when fetching files over `http` or `https`. +< * **_http_response_headers_** (integer) the time to wait (in seconds) for the server's response headers (but not the body) after making a request. 0 indicates no timeout. Default is 10 seconds. +< * **_http_total_** (integer) the time limit (in seconds) for the operation (connection, request, and response), including retries. 0 indicates no timeout. Default is 0. +< * **_security_** (object): options relating to network security. +< * **_tls_** (object): options relating to TLS when fetching resources over `https`. +< * **_certificate_authorities_** (list of objects): the list of additional certificate authorities (in addition to the system authorities) to be used for TLS verification when fetching over `https`. All certificate authorities must have a unique `source`, `inline`, or `local`. +< * **_source_** (string): the URL of the certificate bundle (in PEM format). With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Supported schemes are `http`, `https`, `s3`, `tftp`, and [`data`][rfc2397]. Note: When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the certificate bundle (in PEM format). With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the certificate bundle (in PEM format), relative to the directory specified by the `--files-dir` command-line argument. With Ignition ≥ 2.4.0, the bundle can contain multiple concatenated certificates. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the certificate. +< * **_hash_** (string): the hash of the certificate, in the form `-` where type is either `sha512` or `sha256`. +< * **_proxy_** (object): options relating to setting an `HTTP(S)` proxy when fetching resources. +< * **_httpProxy_** (string): will be used as the proxy URL for HTTP requests and HTTPS requests unless overridden by `httpsProxy` or `noProxy`. +< * **_httpsProxy_** (string): will be used as the proxy URL for HTTPS requests unless overridden by `noProxy`. +< * **_noProxy_** (list of strings): specifies a list of strings to hosts that should be excluded from proxying. Each value is represented by an `IP address prefix (1.2.3.4)`, `an IP address prefix in CIDR notation (1.2.3.4/8)`, `a domain name`, or `a special DNS label (*)`. An IP address prefix and domain name can also include a literal port number `(1.2.3.4:80)`. A domain name matches that name and all subdomains. A domain name with a leading `.` matches subdomains only. For example `foo.com` matches `foo.com` and `bar.foo.com`; `.y.com` matches `x.y.com` but not `y.com`. A single asterisk `(*)` indicates that no proxying should be done. +< * **_storage_** (object): describes the desired state of the system's storage devices. +< * **_disks_** (list of objects): the list of disks to be configured and their options. Every entry must have a unique `device`. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **_wipe_table_** (boolean): whether or not the partition tables shall be wiped. When true, the partition tables are erased before any further manipulation. Otherwise, the existing entries are left intact. +< * **_partitions_** (list of objects): the list of partitions and their configuration for this particular disk. Every partition must have a unique `number`, or if 0 is specified, a unique `label`. +< * **_label_** (string): the PARTLABEL for the partition. +< * **_number_** (integer): the partition number, which dictates it's position in the partition table (one-indexed). If zero, use the next available partition slot. +< * **_size_mib_** (integer): the size of the partition (in mebibytes). If zero, the partition will be made as large as possible. +< * **_start_mib_** (integer): the start of the partition (in mebibytes). If zero, the partition will be positioned at the start of the largest block available. +< * **_type_guid_** (string): the GPT [partition type GUID][part-types]. If omitted, the default will be 0FC63DAF-8483-4772-8E79-3D69D8477DE4 (Linux filesystem data). +< * **_guid_** (string): the GPT unique partition GUID. +< * **_wipe_partition_entry_** (boolean) if true, Ignition will clobber an existing partition if it does not match the config. If false (default), Ignition will fail instead. +< * **_should_exist_** (boolean) whether or not the partition with the specified `number` should exist. If omitted, it defaults to true. If false Ignition will either delete the specified partition or fail, depending on `wipePartitionEntry`. If false `number` must be specified and non-zero and `label`, `start`, `size`, `guid`, and `typeGuid` must all be omitted. +< * **_raid_** (list of objects): the list of RAID arrays to be configured. Every RAID array must have a unique `name`. +< * **name** (string): the name to use for the resulting md device. +< * **level** (string): the redundancy level of the array (e.g. linear, raid1, raid5, etc.). +< * **devices** (list of strings): the list of devices (referenced by their absolute path) in the array. +< * **_spares_** (integer): the number of spares (if applicable) in the array. +< * **_options_** (list of strings): any additional options to be passed to mdadm. +< * **_filesystems_** (list of objects): the list of filesystems to be configured. `path`, `device`, and `format` all need to be specified. Every filesystem must have a unique `device`. +< * **path** (string): the mount-point of the filesystem while Ignition is running relative to where the root filesystem will be mounted. This is not necessarily the same as where it should be mounted in the real root, but it is encouraged to make it the same. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **format** (string): the filesystem format (ext4, btrfs, xfs, vfat, or swap). +< * **_wipe_filesystem_** (boolean): whether or not to wipe the device before filesystem creation, see [the documentation on filesystems](https://coreos.github.io/ignition/operator-notes/#filesystem-reuse-semantics) for more information. +< * **_label_** (string): the label of the filesystem. +< * **_uuid_** (string): the uuid of the filesystem. +< * **_options_** (list of strings): any additional options to be passed to the format-specific mkfs utility. +< * **_mount_options_** (list of strings): any special options to be passed to the mount command. +< * **_with_mount_unit_** (bool): Whether to generate a generic mount unit for this filesystem as well. If a more specific unit is needed, a custom one can be specified in the `systemd.units` section. The unit will be named with the [escaped][systemd-escape] version of the `path`. If your filesystem is located on a Tang-backed LUKS device, the unit will automatically require network access if you specify the device as `/dev/mapper/` or `/dev/disk/by-id/dm-name-`. +< * **_files_** (list of objects): the list of files to be written. Every file, directory and link must have a unique `path`. +< * **path** (string): the absolute path to the file. +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. `contents` must be specified if `overwrite` is true. Defaults to false. +< * **_contents_** (object): options related to the contents of the file. +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the file contents. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. If source is omitted and a regular file already exists at the path, Ignition will do nothing. If source is omitted and no file exists, an empty file will be created. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the file. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the file, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the file contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_append_** (list of objects): list of contents to be appended to the file. Follows the same stucture as `contents` +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the contents to append. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents to append. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents to append, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the appended contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_mode_** (integer): the file's permission mode. If not specified, the permission mode for files defaults to 0644 or the existing file's permissions if `overwrite` is false, `contents` is unspecified, and a file already exists at the path. +< * **_user_** (object): specifies the file's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **_directories_** (list of objects): the list of directories to be created. Every file, directory, and link must have a unique `path`. +< * **path** (string): the absolute path to the directory. +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. If false and a directory already exists at the path, Ignition will only set its permissions. If false and a non-directory exists at that path, Ignition will fail. Defaults to false. +< * **_mode_** (integer): the directory's permission mode. If not specified, the permission mode for directories defaults to 0755 or the mode of an existing directory if `overwrite` is false and a directory already exists at the path. +< * **_user_** (object): specifies the directory's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **_links_** (list of objects): the list of links to be created. Every file, directory, and link must have a unique `path`. +< * **path** (string): the absolute path to the link +< * **_overwrite_** (boolean): whether to delete preexisting nodes at the path. If overwrite is false and a matching link exists at the path, Ignition will only set the owner and group. Defaults to false. +< * **_user_** (object): specifies the symbolic link's owner. +< * **_id_** (integer): the user ID of the owner. +< * **_name_** (string): the user name of the owner. +< * **_group_** (object): specifies the group of the owner. +< * **_id_** (integer): the group ID of the owner. +< * **_name_** (string): the group name of the owner. +< * **target** (string): the target path of the link +< * **_hard_** (boolean): a symbolic link is created if this is false, a hard one if this is true. +< * **_luks_** (list of objects): the list of luks devices to be created. Every device must have a unique `name`. +< * **name** (string): the name of the luks device. +< * **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks. +< * **_key_file_** (string): options related to the contents of the key file. +< * **_compression_** (string): the type of compression used on the contents (null or gzip). Compression cannot be used with S3. +< * **_source_** (string): the URL of the key file contents. Supported schemes are `http`, `https`, `tftp`, `s3`, and [`data`][rfc2397]. When using `http`, it is advisable to use the verification option to ensure the contents haven't been modified. Mutually exclusive with `inline` and `local`. +< * **_inline_** (string): the contents of the key file. Mutually exclusive with `source` and `local`. +< * **_local_** (string): a local path to the contents of the key file, relative to the directory specified by the `--files-dir` command-line argument. Mutually exclusive with `source` and `inline`. +< * **_http_headers_** (list of objects): a list of HTTP headers to be added to the request. Available for `http` and `https` source schemes only. +< * **name** (string): the header name. +< * **_value_** (string): the header contents. +< * **_verification_** (object): options related to the verification of the file contents. +< * **_hash_** (string): the hash of the config, in the form `-` where type is either `sha512` or `sha256`. +< * **_label_** (string): the label of the luks device. +< * **_uuid_** (string): the uuid of the luks device. +< * **_options_** (list of strings): any additional options to be passed to the cryptsetup utility. +< * **_wipe_volume_** (boolean): whether or not to wipe the device before volume creation, see [the Ignition documentation on filesystems](https://coreos.github.io/ignition/operator-notes/#filesystem-reuse-semantics) for more information. +< * **_clevis_** (object): describes the clevis configuration for the luks device. +< * **_tang_** (list of objects): describes a tang server. Every server must have a unique `url`. +< * **url** (string): url of the tang server. +< * **thumbprint** (string): thumbprint of a trusted signing key. +< * **_tpm2_** (bool): whether or not to use a tpm2 device. +< * **_threshold_** (int): sets the minimum number of pieces required to decrypt the device. +< * **_custom_** (object): overrides the clevis configuration. The `pin` & `config` will be passed directly to `clevis luks bind`. If specified, all other clevis options must be omitted. +< * **pin** (string): the clevis pin. +< * **config** (string): the clevis configuration JSON. +< * **_needs_network_** (bool): whether or not the device requires networking. +< * **_trees_** (list of objects): a list of local directory trees to be embedded in the config. Ownership is not preserved. File modes are set to 0755 if the local file is executable or 0644 otherwise. Attributes of files, directories, and symlinks can be overridden by creating a corresponding entry in the `files`, `directories`, or `links` section; such `files` entries must omit `contents` and such `links` entries must omit `target`. +< * **local** (string): the base of the local directory tree, relative to the directory specified by the `--files-dir` command-line argument. +< * **_path_** (string): the path of the tree within the target system. Defaults to `/`. +< * **_systemd_** (object): describes the desired state of the systemd units. +< * **_units_** (list of objects): the list of systemd units. +< * **name** (string): the name of the unit. This must be suffixed with a valid unit type (e.g. "thing.service"). Every unit must have a unique `name`. +< * **_enabled_** (boolean): whether or not the service shall be enabled. When true, the service is enabled. When false, the service is disabled. When omitted, the service is unmodified. In order for this to have any effect, the unit must have an install section. +< * **_mask_** (boolean): whether or not the service shall be masked. When true, the service is masked by symlinking it to `/dev/null`. +< * **_contents_** (string): the contents of the unit. +< * **_dropins_** (list of objects): the list of drop-ins for the unit. Every drop-in must have a unique `name`. +< * **name** (string): the name of the drop-in. This must be suffixed with ".conf". +< * **_contents_** (string): the contents of the drop-in. +< * **_passwd_** (object): describes the desired additions to the passwd database. +< * **_users_** (list of objects): the list of accounts that shall exist. All users must have a unique `name`. +< * **name** (string): the username for the account. +< * **_password_hash_** (string): the hashed password for the account. +< * **_ssh_authorized_keys_** (list of strings): a list of SSH keys to be added as an SSH key fragment at `.ssh/authorized_keys.d/ignition` in the user's home directory. All SSH keys must be unique. +< * **_uid_** (integer): the user ID of the account. +< * **_gecos_** (string): the GECOS field of the account. +< * **_home_dir_** (string): the home directory of the account. +< * **_no_create_home_** (boolean): whether or not to create the user's home directory. This only has an effect if the account doesn't exist yet. +< * **_primary_group_** (string): the name of the primary group of the account. +< * **_groups_** (list of strings): the list of supplementary groups of the account. +< * **_no_user_group_** (boolean): whether or not to create a group with the same name as the user. This only has an effect if the account doesn't exist yet. +< * **_no_log_init_** (boolean): whether or not to add the user to the lastlog and faillog databases. This only has an effect if the account doesn't exist yet. +< * **_shell_** (string): the login shell of the new account. +< * **_system_** (bool): whether or not this account should be a system account. This only has an effect if the account doesn't exist yet. +< * **_groups_** (list of objects): the list of groups to be added. All groups must have a unique `name`. +< * **name** (string): the name of the group. +< * **_gid_** (integer): the group ID of the new group. +< * **_password_hash_** (string): the hashed password of the new group. +< * **_system_** (bool): whether or not the group should be a system group. This only has an effect if the group doesn't exist yet. +< * **_boot_device_** (object): describes the desired boot device configuration. At least one of `luks` or `mirror` must be specified. +< * **_layout_** (string): the disk layout of the target OS image. Supported values are `aarch64`, `ppc64le`, and `x86_64`. Defaults to `x86_64`. +< * **_luks_** (object): describes the clevis configuration for encrypting the root filesystem. +< * **_tang_** (list of objects): describes a tang server. Every server must have a unique `url`. +< * **url** (string): url of the tang server. +< * **thumbprint** (string): thumbprint of a trusted signing key. +< * **_tpm2_** (bool): whether or not to use a tpm2 device. +< * **_threshold_** (int): sets the minimum number of pieces required to decrypt the device. +< * **_mirror_** (object): describes mirroring of the boot disk for fault tolerance. +< * **_devices_** (list of strings): the list of whole-disk devices (not partitions) to include in the disk array, referenced by their absolute path. At least two devices must be specified. +< +< [part-types]: http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs +< [rfc2397]: https://tools.ietf.org/html/rfc2397 +< [systemd-escape]: https://www.freedesktop.org/software/systemd/man/systemd-escape.html +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/docs/development.md updated/vendor/github.com/coreos/fcct/docs/development.md +1,50d0 +< --- +< layout: default +< nav_order: 9 +< --- +< +< # Developing FCCT +< {: .no_toc } +< +< 1. TOC +< {:toc} +< +< ## Creating a release +< +< Create a [release checklist](https://github.com/coreos/fcct/issues/new?template=release-checklist.md) and follow those steps. +< +< ## Bumping spec versions +< +< This checklist describes bumping the Ignition spec version, `base` version, and `config` version. If your scenario is different, modify to taste. +< +< ### Stabilize Ignition spec version +< +< - Bump `go.mod` for new Ignition release and update vendor. +< - Update imports. Drop `-experimental` from Ignition spec versions in `base/vB_exp/translate_test.go`. +< +< ### Bump base version +< +< - Rename `base/vB_exp` to `base/vB` and update `package` statements. Update imports. +< - Copy `base/vB` to `base/vB+1_exp`. +< - Update `package` statements in `base/vB+1_exp`. +< +< ### Bump config version +< +< - Rename `config/fcos/vC_exp` to `config/fcos/vC` and update `package` statements. Update imports. +< - Drop `-experimental` from `init()` in `config/config.go`. +< - Drop `-experimental` from examples in `docs/`. +< - Copy `config/fcos/vC` to `config/fcos/vC+1_exp`. +< - Update `package` statements in `config/fcos/vC+1_exp`. Bump its base dependency to `base/vB+1_exp`. +< - Import `config/vC+1_exp` in `config/config.go` and add `fcos` `C+1-experimental` to `init()`. +< +< ### Bump Ignition spec version +< +< - Bump Ignition types imports and rename `ToIgnI` and `TestToIgnI` functions in `base/vB+1_exp`. Bump Ignition spec versions in `base/vB+1_exp/translate_test.go`. +< - Bump Ignition types imports in `config/fcos/vC+1_exp`. Update `ToIgnI` function names, `util` calls, and header comments to `ToIgnI+1`. +< +< ### Update docs +< +< - Copy the `C-exp` spec doc to `C+1-exp`. Update the header and the version numbers in the description of the `version` field. +< - Rename the `C-exp` spec doc to `C`. Update the header, delete the experimental config warning, and update the version numbers in the description of the `version` field. Update the `nav_order` to one less than the previous stable release. +< - Update `docs/specs.md`. +< - Update `docs/migrating-configs.md` for the new spec version. Copy the relevant section from Ignition's `doc/migrating-configs.md`, convert the configs to FCCs, convert field names to snake case, and update wording as needed. Add subsections for any new FCC-specific features. +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/docs/examples.md updated/vendor/github.com/coreos/fcct/docs/examples.md +1,357d0 +< --- +< layout: default +< nav_order: 3 +< --- +< +< # Examples +< {: .no_toc } +< +< 1. TOC +< {:toc} +< +< Here you can find a bunch of simple examples for using `fcct`, with some explanations about what they do. The examples here are in no way comprehensive, for a full list of all the options present in `fcct` check out the [configuration specification][spec]. +< +< ## Users and groups +< +< This example modifies the existing `core` user and sets its ssh key. +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< passwd: +< users: +< - name: core +< ssh_authorized_keys: +< - key1 +< ``` +< +< This example creates one user, `user1` and sets up one ssh public key for the user. The user is also given the home directory `/home/user1`, but it's not created, the user is added to the `wheel` and `plugdev` groups, and the user's shell is set to `/bin/bash`. +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< passwd: +< users: +< - name: user1 +< ssh_authorized_keys: +< - key1 +< home_dir: /home/user1 +< no_create_home: true +< groups: +< - wheel +< - plugdev +< shell: /bin/bash +< ``` +< +< ### Using password authentication +< +< You can use a Fedora CoreOS Config to set a password for a local user. Building on the previous example, we can configure the `password_hash` for one or more users: +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< passwd: +< users: +< - name: user1 +< ssh_authorized_keys: +< - key1 +< password_hash: $y$j9T$aUmgEDoFIDPhGxEe2FUjc/$C5A... +< home_dir: /home/user1 +< no_create_home: true +< groups: +< - wheel +< - plugdev +< shell: /bin/bash +< ``` +< +< To generate a secure password hash, use the `mkpasswd` command: +< +< ``` +< $ mkpasswd --method=yescrypt +< Password: +< $y$j9T$A0Y3wwVOKP69S.1K/zYGN.$S596l11UGH3XjN... +< ``` +< +< The `yescrypt` hashing method is recommended for new passwords. For more details on hashing methods, see `man 5 crypt`. +< +< For more information, see the Fedora CoreOS documentation on [Authentication][fcos-auth-docs]. +< +< ## Storage and files +< +< ### Files +< +< This example creates a file at `/opt/file` with the contents `Hello, world!`, permissions 0644 (so readable and writable by the owner, and only readable by everyone else), and the file is owned by user uid 500 and gid 501. +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< storage: +< files: +< - path: /opt/file +< contents: +< inline: Hello, world! +< mode: 0644 +< user: +< id: 500 +< group: +< id: 501 +< ``` +< +< This example fetches a gzip-compressed file from `http://example.com/file2`, makes sure that the _uncompressed_ contents match the provided sha512 hash, and writes it to `/opt/file2`. +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< storage: +< files: +< - path: /opt/file2 +< contents: +< source: http://example.com/file2 +< compression: gzip +< verification: +< hash: sha512-4ee6a9d20cc0e6c7ee187daffa6822bdef7f4cebe109eff44b235f97e45dc3d7a5bb932efc841192e46618f48a6f4f5bc0d15fd74b1038abf46bf4b4fd409f2e +< mode: 0644 +< ``` +< +< This example creates a file at `/opt/file3` whose contents are read from a local file `local-file3` on the system running FCCT. The path of the local file is relative to a _files-dir_ which must be specified via the `-d`/`--files-dir` option to FCCT. +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< storage: +< files: +< - path: /opt/file3 +< contents: +< local: local-file3 +< mode: 0644 +< ``` +< +< ### Directory trees +< +< Consider a directory tree at `~/fcc/tree` on the system running FCCT: +< +< ``` +< file +< overridden-file +< directory/file +< directory/symlink -> ../file +< ``` +< +< This example copies that directory tree to `/etc/files` on the target system. The ownership and mode for `overridden-file` are explicitly set by the config. All other filesystem objects are owned by `root:root`, directory modes are set to 0755, and file modes are set to 0755 if the source file is executable or 0644 otherwise. The example must be transpiled with `--files-dir ~/fcc`. +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< storage: +< trees: +< - local: tree +< path: /etc/files +< files: +< - path: /etc/files/overridden-file +< mode: 0600 +< user: +< id: 500 +< group: +< id: 501 +< ``` +< +< ### Filesystems and partitions +< +< This example creates a single partition spanning all of the sdb device then creates a btrfs filesystem on it to use as /var. Finally it creates the mount unit for systemd so it gets mounted on boot. +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< storage: +< disks: +< - device: /dev/sdb +< wipe_table: true +< partitions: +< - number: 1 +< label: var +< filesystems: +< - path: /var +< device: /dev/disk/by-partlabel/var +< format: btrfs +< wipe_filesystem: true +< label: var +< with_mount_unit: true +< ``` +< +< ### LUKS encrypted storage +< +< This example creates three LUKS2 encrypted storage volumes: one unlocked with a static key file, one with a TPM2 device via Clevis, and one with a network Tang server via Clevis. Volumes can be unlocked with any combination of these methods, or with a custom Clevis PIN and CFG. If a key file is not specified for a device, an ephemeral one will be created. +< +< +< ```yaml +< variant: fcos +< version: 1.2.0 +< storage: +< luks: +< - name: static-key-example +< device: /dev/sdb +< key_file: +< inline: REPLACE-THIS-WITH-YOUR-KEY-MATERIAL +< - name: tpm-example +< device: /dev/sdc +< clevis: +< tpm2: true +< - name: tang-example +< device: /dev/sdd +< clevis: +< tang: +< - url: https://tang.example.com +< thumbprint: REPLACE-THIS-WITH-YOUR-TANG-THUMBPRINT +< filesystems: +< - path: /var/lib/static_key_example +< device: /dev/disk/by-id/dm-name-static-key-example +< format: ext4 +< label: STATIC-EXAMPLE +< with_mount_unit: true +< - path: /var/lib/tpm_example +< device: /dev/disk/by-id/dm-name-tpm-example +< format: ext4 +< label: TPM-EXAMPLE +< with_mount_unit: true +< - path: /var/lib/tang_example +< device: /dev/disk/by-id/dm-name-tang-example +< format: ext4 +< label: TANG-EXAMPLE +< with_mount_unit: true +< ``` +< +< This example uses the shortcut `boot_device` syntax to configure an encrypted root filesystem unlocked with a combination of a TPM2 device and a network Tang server. +< +< +< ```yaml +< variant: fcos +< version: 1.3.0 +< boot_device: +< luks: +< tpm2: true +< tang: +< - url: https://tang.example.com +< thumbprint: REPLACE-THIS-WITH-YOUR-TANG-THUMBPRINT +< ``` +< +< This example combines `boot_device` with a manually-specified filesystem `format` to create an encrypted root filesystem formatted with `ext4` instead of the default `xfs`. +< +< +< ```yaml +< variant: fcos +< version: 1.3.0 +< boot_device: +< luks: +< tpm2: true +< storage: +< filesystems: +< - device: /dev/mapper/root +< format: ext4 +< ``` +< +< ### Mirrored boot disk +< +< This example replicates all default partitions on the boot disk across multiple disks, allowing the system to survive disk failure. +< +< +< ```yaml +< variant: fcos +< version: 1.3.0 +< boot_device: +< mirror: +< devices: +< - /dev/sda +< - /dev/sdb +< ``` +< +< This example configures a mirrored boot disk with a TPM2-encrypted root filesystem, overrides the size of the root partition replicas, and adds a mirrored `/var` partition which consumes the remainder of the disks. +< +< +< ```yaml +< variant: fcos +< version: 1.3.0 +< boot_device: +< luks: +< tpm2: true +< mirror: +< devices: +< - /dev/sda +< - /dev/sdb +< storage: +< disks: +< - device: /dev/sda +< partitions: +< - label: root-1 +< size_mib: 8192 +< - label: var-1 +< - device: /dev/sdb +< partitions: +< - label: root-2 +< size_mib: 8192 +< - label: var-2 +< raid: +< - name: md-var +< level: raid1 +< devices: +< - /dev/disk/by-partlabel/var-1 +< - /dev/disk/by-partlabel/var-2 +< filesystems: +< - device: /dev/md/md-var +< path: /var +< format: xfs +< wipe_filesystem: true +< with_mount_unit: true +< ``` +< +< ## systemd units +< +< This example adds a drop-in for the `serial-getty@ttyS0` unit, turning on autologin on `ttyS0` by overriding the `ExecStart=` defined in the default unit. More information on systemd dropins can be found in [the systemd docs][dropins]. +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< systemd: +< units: +< - name: serial-getty@ttyS0.service +< dropins: +< - name: autologin.conf +< contents: | +< [Service] +< TTYVTDisallocate=no +< ExecStart= +< ExecStart=-/usr/sbin/agetty --autologin core --noclear %I $TERM +< ``` +< +< This example creates a new systemd unit called hello.service, enables it so it will run on boot, and defines the contents to simply echo `"Hello, World!"`. +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< systemd: +< units: +< - name: hello.service +< enabled: true +< contents: | +< [Unit] +< Description=A hello world unit! +< [Service] +< Type=oneshot +< RemainAfterExit=yes +< ExecStart=/usr/bin/echo "Hello, World!" +< [Install] +< WantedBy=multi-user.target +< ``` +< +< [spec]: specs.md +< [dropins]: https://www.freedesktop.org/software/systemd/man/systemd.unit.html#Description +< [fcos-auth-docs]: https://docs.fedoraproject.org/en-US/fedora-coreos/authentication +Binary files current/vendor/github.com/coreos/fcct/docs/favicon.ico and updated/vendor/github.com/coreos/fcct/docs/favicon.ico differ +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/docs/getting-started.md updated/vendor/github.com/coreos/fcct/docs/getting-started.md +1,76d0 +< --- +< layout: default +< nav_order: 2 +< --- +< +< # Getting started +< +< `fcct`, the Fedora CoreOS Config Transpiler, is a tool that consumes a Fedora CoreOS Config and produces an Ignition config, which is a JSON document that can be given to a Fedora CoreOS machine when it first boots. Using this config, a machine can be told to create users, create filesystems, set up the network, install systemd units, and more. +< +< Fedora CoreOS Configs are YAML files conforming to `fcct`'s schema. For more information on the schema, take a look at the [configuration specifications][spec]. +< +< ### Getting FCCT +< +< `fcct` can be downloaded as a standalone binary or run as a container with docker or podman. +< +< #### Standalone binary +< +< Download the latest version of `fcct` and the detached signature from the [releases page](https://github.com/coreos/fcct/releases). Verify it with gpg: +< +< ``` +< gpg --verify +< ``` +< You may need to download the [Fedora signing keys](https://getfedora.org/static/fedora.gpg) and import them with `gpg --import ` if you have not already done so. +< +< New releases of `fcct` are backwards compatible with old releases unless otherwise noted. +< +< #### Container +< +< This example uses podman, but docker can also be used. +< +< ```bash +< # Pull the latest release +< podman pull quay.io/coreos/fcct:release +< +< # Run fcct using standard in and standard out +< podman run -i --rm quay.io/coreos/fcct:release --pretty --strict < your_config.fcc > transpiled_config.ign +< +< # Run fcct using files. +< podman run --rm -v /path/to/your_config.fcc:/config.fcc:z quay.io/coreos/fcct:release --pretty --strict /config.fcc > transpiled_config.ign +< ``` +< +< ### Writing and using Fedora CoreOS Configs +< +< As a simple example, let's use `fcct` to set the authorized ssh key for the `core` user on a Fedora CoreOS machine. +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< passwd: +< users: +< - name: core +< ssh_authorized_keys: +< - ssh-rsa AAAAB3NzaC1yc... +< ``` +< +< In this above file, you'll want to set the `ssh-rsa AAAAB3NzaC1yc...` line to be your ssh public key (which is probably the contents of `~/.ssh/id_rsa.pub`, if you're on Linux). +< +< If we take this file and give it to `fcct`: +< +< ``` +< $ ./bin/amd64/fcct example.yaml +< +< {"ignition":{"config":{"replace":{"source":null,"verification":{}}},"security":{"tls":{}},"timeouts":{},"version":"3.0.0"},"passwd":{"users":[{"name":"core","sshAuthorizedKeys":["ssh-rsa ssh-rsa AAAAB3NzaC1yc..."]}]},"storage":{},"systemd":{}} +< ``` +< +< We can see that it produces a JSON file. This file isn't intended to be human-friendly, and will definitely be a pain to read/edit (especially if you have multi-line things like systemd units). Luckily, you shouldn't have to care about this file! Just provide it to a booting Fedora CoreOS machine and [Ignition][ignition], the utility inside of Fedora CoreOS that receives this file, will know what to do with it. +< +< The method by which this file is provided to a Fedora CoreOS machine depends on the environment in which the machine is running. For instructions on a given provider, head over to the [list of supported platforms for Ignition][supported-platforms]. +< +< To see some examples for what else `fcct` can do, head over to the [examples][examples]. +< +< [spec]: specs.md +< [ignition]: https://coreos.github.io/ignition/ +< [supported-platforms]: https://coreos.github.io/ignition/supported-platforms/ +< [examples]: examples.md +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/docs/index.md updated/vendor/github.com/coreos/fcct/docs/index.md +1,11d0 +< --- +< layout: default +< nav_order: 1 +< --- +< +< # Fedora CoreOS Config Transpiler +< +< The Fedora CoreOS Config Transpiler (FCCT) translates human readable Fedora CoreOS Configs (FCCs) +< into machine readable [Ignition](https://coreos.github.io/ignition/) Configs. See the [getting +< started](getting-started) guide for how to use FCCT and the [configuration specifications](specs.md) +< for everything FCCs support. +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/docs/migrating-configs.md updated/vendor/github.com/coreos/fcct/docs/migrating-configs.md +1,359d0 +< --- +< layout: default +< nav_order: 5 +< --- +< +< # Upgrading Configs +< +< Occasionally, there are changes made to Fedora CoreOS configuration that break backward compatibility. While this is not a concern for running machines (since Ignition only runs one time during first boot), it is a concern for those who maintain configuration files. This document serves to detail each of the breaking changes and tries to provide some reasoning for the change. This does not cover all of the changes to the spec - just those that need to be considered when migrating from one version to the next. +< +< {: .no_toc } +< +< 1. TOC +< {:toc} +< +< ## From Version 1.2.0 to 1.3.0 +< +< There are no breaking changes between versions 1.2.0 and 1.3.0 of the configuration specification. Any valid 1.2.0 configuration can be updated to a 1.3.0 configuration by changing the version string in the config. +< +< The following is a list of notable new features, deprecations, and changes. +< +< ### Boot disk mirroring and LUKS +< +< The config gained a new top-level `boot_device` section with `luks` and `mirror` subsections, which provide a simple way to configure encryption and/or mirroring for the boot disk. When `luks` is specified, the root filesystem is encrypted and can be unlocked with any combination of a TPM2 device and network Tang servers. When `mirror` is specified, all default partitions are replicated across multiple disks, allowing the system to survive disk failure. On aarch64 or ppc64le systems, the `layout` field must be set to `aarch64` or `ppc64le` to select the correct partition layout. +< +< +< ```yaml +< variant: fcos +< version: 1.3.0 +< boot_device: +< layout: ppc64le +< mirror: +< devices: +< - /dev/sda +< - /dev/sdb +< luks: +< tang: +< - url: https://tang.example.com +< thumbprint: REPLACE-THIS-WITH-YOUR-TANG-THUMBPRINT +< tpm2: true +< threshold: 2 +< ``` +< +< ## From Version 1.1.0 to 1.2.0 +< +< There are no breaking changes between versions 1.1.0 and 1.2.0 of the configuration specification. Any valid 1.1.0 configuration can be updated to a 1.2.0 configuration by changing the version string in the config. +< +< The following is a list of notable new features, deprecations, and changes. +< +< ### Partition resizing +< +< The `partition` section gained a new `resize` field. When true, Ignition will resize an existing partition if it matches the config in all respects except the partition size. +< +< +< ```yaml +< variant: fcos +< version: 1.2.0 +< storage: +< disks: +< - device: /dev/sda +< partitions: +< - label: root +< size_mib: 16384 +< resize: true +< ``` +< +< ### LUKS encrypted storage +< +< Ignition now supports creating LUKS2 encrypted storage volumes. Volumes can be configured to allow unlocking with any combination of a TPM2 device via Clevis, network Tang servers via Clevis, and static key files. Alternatively, the Clevis configuration can be manually specified with a custom PIN and CFG. If a key file is not specified for a device, an ephemeral one will be created. +< +< +< ```yaml +< variant: fcos +< version: 1.2.0 +< storage: +< luks: +< - name: static-key-example +< device: /dev/sdb +< key_file: +< inline: REPLACE-THIS-WITH-YOUR-KEY-MATERIAL +< - name: tpm-example +< device: /dev/sdc +< clevis: +< tpm2: true +< - name: tang-example +< device: /dev/sdd +< clevis: +< tang: +< - url: https://tang.example.com +< thumbprint: REPLACE-THIS-WITH-YOUR-TANG-THUMBPRINT +< filesystems: +< - path: /var/lib/static_key_example +< device: /dev/disk/by-id/dm-name-static-key-example +< format: ext4 +< label: STATIC-EXAMPLE +< with_mount_unit: true +< - path: /var/lib/tpm_example +< device: /dev/disk/by-id/dm-name-tpm-example +< format: ext4 +< label: TPM-EXAMPLE +< with_mount_unit: true +< - path: /var/lib/tang_example +< device: /dev/disk/by-id/dm-name-tang-example +< format: ext4 +< label: TANG-EXAMPLE +< with_mount_unit: true +< ``` +< +< ### User/group deletion +< +< The `passwd` `users` and `groups` sections have a new field `should_exist`. If specified and false, Ignition will delete the specified user or group if it exists. +< +< +< ```yaml +< variant: fcos +< version: 1.2.0 +< passwd: +< users: +< - name: core +< should_exist: false +< groups: +< - name: core +< should_exist: false +< ``` +< +< ### Google Cloud Storage URL support +< +< The sections which allow fetching a remote URL now accept Google Cloud Storage (`gs://`) URLs in the `source` field. +< +< +< ```yaml +< variant: fcos +< version: 1.2.0 +< storage: +< files: +< - path: /etc/example +< mode: 0644 +< contents: +< source: gs://bucket/object +< ``` +< +< ## From Version 1.0.0 to 1.1.0 +< +< There are no breaking changes between versions 1.0.0 and 1.1.0 of the configuration specification. Any valid 1.0.0 configuration can be updated to a 1.1.0 configuration by changing the version string in the config. +< +< The following is a list of notable new features, deprecations, and changes. +< +< ### Embedding local files in configs +< +< The config `merge` and `replace` sections, the `certificate_authorities` section, and the files `contents` and `append` sections gained a new field called `local`, which is mutually exclusive with the `source` and `inline` fields. It causes the contents of a file from the system running FCCT to be embedded in the config. The specified path is relative to a local _files-dir_, specified with the `-d`/`--files-dir` option to FCCT. If no _files-dir_ is specified, this functionality is unavailable. +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< ignition: +< config: +< merge: +< - local: config.ign +< security: +< tls: +< certificate_authorities: +< - local: ca.pem +< storage: +< files: +< - path: /opt/file +< contents: +< local: file +< append: +< - local: file-epilogue +< mode: 0644 +< ``` +< +< ### Embedding directory trees in configs +< +< The `storage` section gained a new subsection called `trees`. It is a list of directory trees on the system running FCCT whose files, directories, and symlinks will be embedded in the config. By default, the resulting filesystem objects are owned by `root:root`, directory modes are set to 0755, and file modes are set to 0755 if the source file is executable or 0644 otherwise. Attributes of files, directories, and symlinks can be overridden by creating an entry in the `files`, `directories`, or `links` section; such `files` entries must omit `contents` and such `links` entries must omit `target`. +< +< Tree paths are relative to a local _files-dir_, specified with the `-d`/`--files-dir` option to FCCT. If no _files-dir_ is specified, this functionality is unavailable. +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< storage: +< trees: +< - local: tree +< path: /etc/files +< files: +< - path: /etc/files/overridden-file +< mode: 0600 +< user: +< id: 500 +< group: +< id: 501 +< ``` +< +< ### Inline contents on certificate authorities and merged configs +< +< The `certificate_authorities` section now supports inline contents via the `inline` field. The config `merge` and `replace` sections also now support `inline`, but using this functionality is not recommended. +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< ignition: +< config: +< merge: +< - inline: | +< {"ignition": {"version": "3.1.0"}} +< security: +< tls: +< certificate_authorities: +< - inline: | +< -----BEGIN CERTIFICATE----- +< MIICzTCCAlKgAwIBAgIJALTP0pfNBMzGMAoGCCqGSM49BAMCMIGZMQswCQYDVQQG +< EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNj +< bzETMBEGA1UECgwKQ29yZU9TIEluYzEUMBIGA1UECwwLRW5naW5lZXJpbmcxEzAR +< BgNVBAMMCmNvcmVvcy5jb20xHTAbBgkqhkiG9w0BCQEWDm9lbUBjb3Jlb3MuY29t +< MB4XDTE4MDEyNTAwMDczOVoXDTI4MDEyMzAwMDczOVowgZkxCzAJBgNVBAYTAlVT +< MRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRMw +< EQYDVQQKDApDb3JlT1MgSW5jMRQwEgYDVQQLDAtFbmdpbmVlcmluZzETMBEGA1UE +< AwwKY29yZW9zLmNvbTEdMBsGCSqGSIb3DQEJARYOb2VtQGNvcmVvcy5jb20wdjAQ +< BgcqhkjOPQIBBgUrgQQAIgNiAAQDEhfHEulYKlANw9eR5l455gwzAIQuraa049Rh +< vM7PPywaiD8DobteQmE8wn7cJSzOYw6GLvrL4Q1BO5EFUXknkW50t8lfnUeHveCN +< sqvm82F1NVevVoExAUhDYmMREa6jZDBiMA8GA1UdEQQIMAaHBH8AAAEwHQYDVR0O +< BBYEFEbFy0SPiF1YXt+9T3Jig2rNmBtpMB8GA1UdIwQYMBaAFEbFy0SPiF1YXt+9 +< T3Jig2rNmBtpMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDaQAwZgIxAOul +< t3MhI02IONjTDusl2YuCxMgpy2uy0MPkEGUHnUOsxmPSG0gEBCNHyeKVeTaPUwIx +< AKbyaAqbChEy9CvDgyv6qxTYU+eeBImLKS3PH2uW5etc/69V/sDojqpH3hEffsOt +< 9g== +< -----END CERTIFICATE----- +< ``` +< +< ### Compression support for certificate authorities and merged configs +< +< The config `merge` and `replace` sections and the `certificate_authorities` section now support gzip-compressed resources via the `compression` field. `gzip` compression is supported for all URL schemes except `s3`. +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< ignition: +< config: +< merge: +< - source: https://secure.example.com/example.ign.gz +< compression: gzip +< security: +< tls: +< certificate_authorities: +< - source: https://example.com/ca.pem.gz +< compression: gzip +< ``` +< +< ### SHA-256 resource verification +< +< All `verification.hash` fields now support the `sha256` hash type. +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< storage: +< files: +< - path: /etc/hosts +< mode: 0644 +< contents: +< source: https://example.com/etc/hosts +< verification: +< hash: sha256-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +< ``` +< +< ### Automatic generation of mount units +< +< The `filesystems` section gained a new `with_mount_unit` field. If `true`, a generic mount unit will be automatically generated for the specified filesystem. +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< storage: +< filesystems: +< - path: /var/data +< device: /dev/vdb1 +< format: ext4 +< with_mount_unit: true +< ``` +< +< ### Filesystem mount options +< +< The `filesystems` section gained a new `mount_options` field. It is a list of options Ignition should pass to `mount -o` when mounting the specified filesystem. This is useful for mounting btrfs subvolumes. If the `with_mount_unit` field is `true`, this field also affects mount options used by the provisioned system when mounting the filesystem. +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< storage: +< filesystems: +< - path: /var/data +< device: /dev/vdb1 +< wipe_filesystem: false +< format: btrfs +< mount_options: +< - subvolid=5 +< with_mount_unit: true +< ``` +< +< ### Custom HTTP headers +< +< The sections which allow fetching a remote URL — config `merge` and `replace`, `certificate_authorities`, and file `contents` and `append` — gained a new field called `http_headers`. This field can be set to an array of HTTP headers which will be added to an HTTP or HTTPS request. Custom headers can override Ignition's default headers, and will not be retained across HTTP redirects. +< +< During config merging, if a child config specifies a header `name` but not a corresponding `value`, any header with that `name` in the parent config will be removed. +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< storage: +< files: +< - path: /etc/hosts +< mode: 0644 +< contents: +< source: https://example.com/etc/hosts +< http_headers: +< - name: Authorization +< value: Basic YWxhZGRpbjpvcGVuc2VzYW1l +< - name: User-Agent +< value: Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 5.1) +< ``` +< +< ### HTTP proxies +< +< The `ignition` section gained a new field called `proxy`. It allows configuring proxies for HTTP and HTTPS requests, as well as exempting certain hosts from proxying. +< +< The `https_proxy` field specifies the proxy URL for HTTPS requests. The `http_proxy` field specifies the proxy URL for HTTP requests, and also for HTTPS requests if `https_proxy` is not specified. The `no_proxy` field lists specifiers of hosts that should not be proxied, in any of several formats: +< +< - An IP address prefix (`1.2.3.4`) +< - An IP address prefix in CIDR notation (`1.2.3.4/8`) +< - A domain name, matching the domain and its subdomains (`example.com`) +< - A domain name, matching subdomains only (`.example.com`) +< - A wildcard matching all hosts (`*`) +< +< IP addresses and domain names can also include a port number (`1.2.3.4:80`). +< +< +< ```yaml +< variant: fcos +< version: 1.1.0 +< ignition: +< proxy: +< http_proxy: https://proxy.example.net/ +< https_proxy: https://secure.proxy.example.net/ +< no_proxy: +< - www.example.net +< storage: +< files: +< - path: /etc/hosts +< mode: 0644 +< contents: +< source: https://example.com/etc/hosts +< ``` +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/docs/specs.md updated/vendor/github.com/coreos/fcct/docs/specs.md +1,47d0 +< --- +< layout: default +< has_children: true +< nav_order: 4 +< has_toc: false +< --- +< +< # Configuration specifications +< +< CoreOS Configs must conform to a specific variant and version of the `fcct` schema, specified with the `variant` and `version` fields in the configuration. +< +< See the [Upgrading Configs](migrating-configs.md) page for instructions to update a configuration to the latest specification. +< +< ## Stable specification versions +< +< We recommend that you always use the latest **stable** specification for your operating system to benefit from new features and bug fixes. The following **stable** specification versions are currently supported in `fcct`: +< +< - Fedora CoreOS (`fcos`) +< - [v1.3.0](config-fcos-v1_3.md) +< - [v1.2.0](config-fcos-v1_2.md) +< - [v1.1.0](config-fcos-v1_1.md) +< - [v1.0.0](config-fcos-v1_0.md) +< - RHEL CoreOS (`rhcos`) +< - [v0.1.0](config-rhcos-v0_1.md) +< +< ## Experimental specification versions +< +< Do not use **experimental** specifications for anything beyond **development and testing** as they are subject to change **without warning or announcement**. The following **experimental** specification versions are currently available in `fcct`: +< +< - Fedora CoreOS (`fcos`) +< - [v1.4.0-experimental](config-fcos-v1_4-exp.md) +< - RHEL CoreOS (`rhcos`) +< - [v0.2.0-experimental](config-rhcos-v0_2-exp.md) +< +< ## FCC specifications and Ignition specifications +< +< Each version of the FCC specification corresponds to a version of the Ignition specification: +< +< | FCC variant | FCC version | Ignition spec | +< |-------------|--------------------|--------------------| +< | `fcos` | 1.0.0 | 3.0.0 | +< | `fcos` | 1.1.0 | 3.1.0 | +< | `fcos` | 1.2.0 | 3.2.0 | +< | `fcos` | 1.3.0 | 3.2.0 | +< | `fcos` | 1.4.0-experimental | 3.3.0-experimental | +< | `rhcos` | 0.1.0 | 3.2.0 | +< | `rhcos` | 0.2.0-experimental | 3.3.0-experimental | +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/go.mod updated/vendor/github.com/coreos/fcct/go.mod +1,17d0 +< module github.com/coreos/fcct +< +< go 1.12 +< +< require ( +< github.com/clarketm/json v1.14.1 +< github.com/coreos/go-semver v0.3.0 +< github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e +< github.com/coreos/ignition/v2 v2.8.1 +< github.com/coreos/vcontext v0.0.0-20201120045928-b0e13dab675c +< github.com/davecgh/go-spew v1.1.1 // indirect +< github.com/spf13/pflag v1.0.5 +< github.com/stretchr/testify v1.5.1 +< github.com/vincent-petithory/dataurl v0.0.0-20160330182126-9a301d65acbb +< gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect +< gopkg.in/yaml.v3 v3.0.0-20191010095647-fc94e3f71652 +< ) +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/go.sum updated/vendor/github.com/coreos/fcct/go.sum +1,383d0 +< cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +< cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +< cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +< cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +< cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +< cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +< cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +< cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +< cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +< cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +< cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +< cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +< cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +< cloud.google.com/go v0.58.0/go.mod h1:W+9FnSUw6nhVwXlFcp1eL+krq5+HQUJeUogSeJZZiWg= +< cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +< cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +< cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +< cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +< cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +< cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +< cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +< cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +< cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +< cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +< cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +< cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +< cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +< cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +< cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +< cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +< cloud.google.com/go/storage v1.9.0/go.mod h1:m+/etGaqZbylxaNT876QGXqEHp4PR2Rq5GMqICWb9bU= +< dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +< github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +< github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +< github.com/aws/aws-sdk-go v1.30.28/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +< github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +< github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +< github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +< github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +< github.com/clarketm/json v1.14.1 h1:43bkbTTKKdDx7crs3WHzkrnH6S1EvAF1VZrdFGMmmz4= +< github.com/clarketm/json v1.14.1/go.mod h1:ynr2LRfb0fQU34l07csRNBTcivjySLLiY1YzQqKVfdo= +< github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +< github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +< github.com/coreos/go-json v0.0.0-20170920214419-6a2fe990e083 h1:iLYct0QOZLUuTbFBf+PDiKvpG1xPicwkcgnKaGCeTgc= +< github.com/coreos/go-json v0.0.0-20170920214419-6a2fe990e083/go.mod h1:FmxyHfvrCFfCsXRylD4QQRlQmvzl+DG6iTHyEEykPfU= +< github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +< github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +< github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= +< github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +< github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28= +< github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +< github.com/coreos/ignition/v2 v2.8.1 h1:gKCX6NwGGFh866MvpJlq2ZCqppVbWK0DA/uflL34tDU= +< github.com/coreos/ignition/v2 v2.8.1/go.mod h1:A5lFFzA2/zvZQPVEvI1lR5WPLWRb7KZ7Q1QOeUMtcAc= +< github.com/coreos/vcontext v0.0.0-20201120045928-b0e13dab675c h1:jA28WeORitsxGFVWhyWB06sAG2HbLHPQuHwDydhU2CQ= +< github.com/coreos/vcontext v0.0.0-20201120045928-b0e13dab675c/go.mod h1:z4pMVvaUrxs98RROlIYdAQCKhEicjnTirOaVyDRH5h8= +< github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +< github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +< github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +< github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +< github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +< github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +< github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +< github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +< github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +< github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +< github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +< github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +< github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +< github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +< github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +< github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +< github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +< github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +< github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +< github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +< github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +< github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +< github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +< github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +< github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +< github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +< github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +< github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +< github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +< github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +< github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +< github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +< github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +< github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +< github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +< github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +< github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +< github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +< github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +< github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +< github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +< github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +< github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +< github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +< github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +< github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +< github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +< github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +< github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +< github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +< github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +< github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +< github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +< github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +< github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +< github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +< github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +< github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +< github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +< github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +< github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +< github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +< github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +< github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +< github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +< github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +< github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +< github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +< github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +< github.com/pin/tftp v2.1.0+incompatible/go.mod h1:xVpZOMCXTy+A5QMjEVN0Glwa1sUvaJhFXbr/aAxuxGY= +< github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +< github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +< github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +< github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +< github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +< github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +< github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa/go.mod h1:2RVY1rIf+2J2o/IM9+vPq9RzmHDSseB7FoXiSNIUsoU= +< github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +< github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +< github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +< github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +< github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +< github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +< github.com/vincent-petithory/dataurl v0.0.0-20160330182126-9a301d65acbb h1:lyL3z7vYwTWXf4/bI+A01+cCSnfhKIBhy+SQ46Z/ml8= +< github.com/vincent-petithory/dataurl v0.0.0-20160330182126-9a301d65acbb/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U= +< github.com/vmware/vmw-guestinfo v0.0.0-20170707015358-25eff159a728/go.mod h1:x9oS4Wk2s2u4tS29nEaDLdzvuHdB19CvSGJjPgkZJNk= +< github.com/vmware/vmw-ovflib v0.0.0-20170608004843-1f217b9dc714/go.mod h1:jiPk45kn7klhByRvUq5i2vo1RtHKBHj+iWGFpxbXuuI= +< github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +< github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +< go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +< go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +< go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +< go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +< go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +< golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +< golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +< golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +< golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +< golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +< golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +< golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +< golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +< golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +< golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +< golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +< golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +< golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +< golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +< golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +< golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +< golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +< golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +< golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +< golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +< golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +< golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +< golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +< golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +< golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +< golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +< golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +< golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +< golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +< golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +< golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +< golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +< golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +< golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +< golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +< golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +< golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +< golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +< golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +< golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +< golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +< golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +< golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +< golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +< golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +< golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +< golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +< golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +< golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +< golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +< golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +< golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +< golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +< golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +< golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +< golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +< golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +< golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +< golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +< golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +< golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +< golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +< golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +< golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +< golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +< golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +< golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +< golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +< golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +< golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +< golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +< golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +< golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +< golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +< golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +< golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +< golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +< golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +< golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +< golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +< golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +< golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +< golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +< golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +< golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +< golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +< golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +< golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +< golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +< golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +< golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +< golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +< golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +< golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +< golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +< golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +< golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +< golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +< golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +< golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +< golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +< golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +< golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +< golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +< golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +< golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +< golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +< golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +< golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +< golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +< golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +< golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +< golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +< golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +< golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +< golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +< golang.org/x/tools v0.0.0-20200601175630-2caf76543d99/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +< golang.org/x/tools v0.0.0-20200606014950-c42cb6316fb6/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +< golang.org/x/tools v0.0.0-20200610160956-3e83d1e96d0e/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +< golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +< golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +< golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +< google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +< google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +< google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +< google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +< google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +< google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +< google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +< google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +< google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +< google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +< google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +< google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +< google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +< google.golang.org/api v0.26.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +< google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +< google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +< google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +< google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +< google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +< google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +< google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +< google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +< google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +< google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +< google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +< google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +< google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +< google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +< google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +< google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +< google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +< google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +< google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +< google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +< google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +< google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +< google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +< google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +< google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +< google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +< google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +< google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +< google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +< google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +< google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +< google.golang.org/genproto v0.0.0-20200603110839-e855014d5736/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +< google.golang.org/genproto v0.0.0-20200608115520-7c474a2e3482/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +< google.golang.org/genproto v0.0.0-20200610104632-a5b850bcf112/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +< google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +< google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +< google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +< google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +< google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +< google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +< google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +< google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +< google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +< google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +< google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +< google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +< google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +< google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +< google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +< google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +< google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +< google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +< google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +< gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +< gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +< gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +< gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +< gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +< gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +< gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +< gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +< gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +< gopkg.in/yaml.v3 v3.0.0-20190502103701-55513cacd4ae h1:ehhBuCxzgQEGk38YjhFv/97fMIc2JGHZAhAWMmEjmu0= +< gopkg.in/yaml.v3 v3.0.0-20190502103701-55513cacd4ae/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +< gopkg.in/yaml.v3 v3.0.0-20191010095647-fc94e3f71652 h1:VKvJ/mQ4BgCjZUDggYFxTe0qv9jPMHsZPD4Xt91Y5H4= +< gopkg.in/yaml.v3 v3.0.0-20191010095647-fc94e3f71652/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +< honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +< honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +< honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +< honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +< honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +< honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +< honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +< rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +< rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +< rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/internal/main.go updated/vendor/github.com/coreos/fcct/internal/main.go +1,111d0 +< // 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 main +< +< import ( +< "fmt" +< "io/ioutil" +< "os" +< +< "github.com/spf13/pflag" +< +< "github.com/coreos/fcct/config" +< "github.com/coreos/fcct/config/common" +< "github.com/coreos/fcct/internal/version" +< ) +< +< func fail(format string, args ...interface{}) { +< fmt.Fprintf(os.Stderr, format, args...) +< os.Exit(1) +< } +< +< func main() { +< var ( +< input string +< output string +< helpFlag bool +< versionFlag bool +< ) +< options := common.TranslateBytesOptions{} +< pflag.BoolVarP(&helpFlag, "help", "h", false, "show usage and exit") +< pflag.BoolVarP(&versionFlag, "version", "V", false, "print the version and exit") +< pflag.BoolVarP(&options.Strict, "strict", "s", false, "fail on any warning") +< pflag.BoolVarP(&options.Pretty, "pretty", "p", false, "output formatted json") +< pflag.StringVar(&input, "input", "", "read from input file instead of stdin") +< pflag.Lookup("input").Deprecated = "specify filename directly on command line" +< pflag.Lookup("input").Hidden = true +< pflag.StringVarP(&output, "output", "o", "", "write to output file instead of stdout") +< pflag.StringVarP(&options.FilesDir, "files-dir", "d", "", "allow embedding local files from this directory") +< +< pflag.Usage = func() { +< fmt.Fprintf(os.Stderr, "Usage: %s [options] [input-file]\n", os.Args[0]) +< fmt.Fprintf(os.Stderr, "Options:\n") +< pflag.PrintDefaults() +< } +< pflag.Parse() +< +< args := pflag.Args() +< if len(args) == 1 && input == "" { +< input = args[0] +< } else if len(args) > 0 { +< pflag.Usage() +< os.Exit(2) +< } +< +< if helpFlag { +< pflag.Usage() +< os.Exit(0) +< } +< +< if versionFlag { +< fmt.Println(version.String) +< os.Exit(0) +< } +< +< var infile *os.File = os.Stdin +< var outfile *os.File = os.Stdout +< if input != "" { +< var err error +< infile, err = os.Open(input) +< if err != nil { +< fail("failed to open %s: %v\n", input, err) +< } +< defer infile.Close() +< } +< +< dataIn, err := ioutil.ReadAll(infile) +< if err != nil { +< fail("failed to read %s: %v\n", infile.Name(), err) +< } +< +< dataOut, r, err := config.TranslateBytes(dataIn, options) +< fmt.Fprintf(os.Stderr, "%s", r.String()) +< if err != nil { +< fail("Error translating config: %v\n", err) +< } +< +< if output != "" { +< var err error +< outfile, err = os.OpenFile(output, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) +< if err != nil { +< fail("failed to open %s: %v\n", output, err) +< } +< defer outfile.Close() +< } +< +< if _, err := outfile.Write(append(dataOut, '\n')); err != nil { +< fail("Failed to write config to %s: %v\n", outfile.Name(), err) +< } +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/internal/version/version.go updated/vendor/github.com/coreos/fcct/internal/version/version.go +1,24d0 +< // 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 version +< +< import ( +< "fmt" +< ) +< +< var ( +< Raw = "was not built properly" +< String = fmt.Sprintf("Fedora CoreOS Config Transpiler %s", Raw) +< ) +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/tag_release.sh updated/vendor/github.com/coreos/fcct/tag_release.sh +1,23d0 +< #!/usr/bin/env bash +< +< set -e +< +< [ $# == 2 ] || { echo "usage: $0 " && exit 1; } +< +< VER=$1 +< COMMIT=$2 +< +< [[ "${VER}" =~ ^v[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+(-.+)?$ ]] || { +< echo "malformed version: \"${VER}\"" +< exit 2 +< } +< +< [[ "${COMMIT}" =~ ^[[:xdigit:]]+$ ]] || { +< echo "malformed commit id: \"${COMMIT}\"" +< exit 3 +< } +< +< source ./build +< +< git tag --sign --message "Fedora CoreOS Config Transpiler ${VER}" "${VER}" "${COMMIT}" +< git verify-tag --verbose "${VER}" +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/test updated/vendor/github.com/coreos/fcct/test +1,42d0 +< #!/bin/bash +< set -euo pipefail +< +< export GO111MODULE=on +< export GOFLAGS=-mod=vendor +< +< SRC=$(find . -name '*.go' -not -path "./vendor/*") +< +< echo "checking gofmt" +< res=$(gofmt -d $SRC) +< echo "$res" +< test -z "$res" +< +< echo "Running tests" +< go test ./... -cover +< +< echo "Checking docs" +< shopt -s nullglob +< mkdir tmpdocs +< trap 'rm -r tmpdocs' EXIT +< # Create files-dir contents expected by configs +< mkdir -p tmpdocs/files-dir/tree +< touch tmpdocs/files-dir/{config.ign,ca.pem,file,file-epilogue,local-file3} +< +< for doc in docs/*md +< do +< echo "Checking $doc" +< # split each doc into a bunch of tmpfiles then run fcct on them +< sed -n '/^/,/^```$/ p' < ${doc} \ +< | csplit - '//' '{*}' -z --prefix "tmpdocs/fcc_$(basename ${doc%.*})_" -q +< +< for i in tmpdocs/fcc_* +< do +< echo "Checking $i" +< cat "$i" | tail -n +3 | head -n -1 \ +< | go run internal/main.go --strict --files-dir tmpdocs/files-dir > /dev/null \ +< || (cat -n "$i" && false) +< done +< rm -f tmpdocs/fcc_* +< done +< +< echo ok +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/translate/set.go updated/vendor/github.com/coreos/fcct/translate/set.go +1,117d0 +< // 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 translate +< +< import ( +< "fmt" +< "reflect" +< +< "github.com/coreos/vcontext/path" +< ) +< +< // Translation represents how a path changes when translating. If something at $yaml.storage.filesystems.4 +< // generates content at $json.systemd.units.3 a translation can represent that. This allows validation errors +< // in Ignition structs to be tracked back to their source in the yaml. +< type Translation struct { +< From path.ContextPath +< To path.ContextPath +< } +< +< // TranslationSet represents all of the translations that occurred. They're stored in a map from a string representation +< // of the destination path to the translation struct. The map is purely an optimization to allow fast lookups. Ideally the +< // map would just be from the destination path.ContextPath to the source path.ContextPath, but ContextPath contains a slice +< // which are not comparable and thus cannot be used as keys in maps. +< type TranslationSet struct { +< FromTag string +< ToTag string +< Set map[string]Translation +< } +< +< func NewTranslationSet(fromTag, toTag string) TranslationSet { +< return TranslationSet{ +< FromTag: fromTag, +< ToTag: toTag, +< Set: map[string]Translation{}, +< } +< } +< +< func (ts TranslationSet) String() string { +< str := fmt.Sprintf("from: %v\nto: %v\n", ts.FromTag, ts.ToTag) +< for k, v := range ts.Set { +< str += fmt.Sprintf("%s: %v -> %v\n", k, v.From.String(), v.To.String()) +< } +< return str +< } +< +< // AddTranslation adds a translation to the set +< func (ts TranslationSet) AddTranslation(from, to path.ContextPath) { +< // create copies of the paths so if someone else changes from.Path the added translation does not change. +< from = from.Copy() +< to = to.Copy() +< translation := Translation{ +< From: from, +< To: to, +< } +< toString := translation.To.String() +< ts.Set[toString] = translation +< } +< +< // Shortcut for AddTranslation for identity translations +< func (ts TranslationSet) AddIdentity(paths ...string) { +< for _, p := range paths { +< from := path.New(ts.FromTag, p) +< to := path.New(ts.ToTag, p) +< ts.AddTranslation(from, to) +< } +< } +< +< // AddFromCommonSource adds translations for all of the paths in to from a single common path. This is useful +< // if one part of a config generates a large struct and all of the large struct should map to one path in the +< // config being translated. +< func (ts TranslationSet) AddFromCommonSource(common path.ContextPath, toPrefix path.ContextPath, to interface{}) { +< v := reflect.ValueOf(to) +< vPaths := prefixPaths(getAllPaths(v, ts.ToTag), toPrefix.Path...) +< for _, path := range vPaths { +< ts.AddTranslation(common, path) +< } +< } +< +< // Merge adds all the entries to the set. It mutates the Set in place. +< func (ts TranslationSet) Merge(from TranslationSet) { +< for _, t := range from.Set { +< ts.AddTranslation(t.From, t.To) +< } +< } +< +< // MergeP is like Merge, but first it calls Prefix on the set being merged in. +< func (ts TranslationSet) MergeP(prefix interface{}, from TranslationSet) { +< from = from.Prefix(prefix) +< ts.Merge(from) +< } +< +< // Prefix returns a TranslationSet with all translation paths prefixed by prefix. +< func (ts TranslationSet) Prefix(prefix interface{}) TranslationSet { +< return ts.PrefixPaths(path.New(ts.FromTag, prefix), path.New(ts.ToTag, prefix)) +< } +< +< // PrefixPaths returns a TranslationSet with from translation paths prefixed by +< // fromPrefix and to translation paths prefixed by toPrefix. +< func (ts TranslationSet) PrefixPaths(fromPrefix, toPrefix path.ContextPath) TranslationSet { +< ret := NewTranslationSet(ts.FromTag, ts.ToTag) +< for _, tr := range ts.Set { +< ret.AddTranslation(fromPrefix.Append(tr.From.Path...), toPrefix.Append(tr.To.Path...)) +< } +< return ret +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/translate/tests/pkga/types.go updated/vendor/github.com/coreos/fcct/translate/tests/pkga/types.go +1,42d0 +< // 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 pkga +< +< type Trivial struct { +< A string +< B int +< C bool +< } +< +< type Nested struct { +< D string +< Trivial +< } +< +< type TrivialReordered struct { +< B int +< A string +< C bool +< } +< +< type HasList struct { +< L []Trivial +< } +< +< type TrivialSkip struct { +< A string `fcct:"auto_skip"` +< B int +< C bool +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/translate/tests/pkgb/types.go updated/vendor/github.com/coreos/fcct/translate/tests/pkgb/types.go +1,42d0 +< // 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 pkgb +< +< type Trivial struct { +< A string +< B int +< C bool +< } +< +< type Nested struct { +< D string +< Trivial +< } +< +< // note: struct ordering is different from pkga +< type TrivialReordered struct { +< A string +< B int +< C bool +< } +< +< type HasList struct { +< L []Nested +< } +< +< type TrivialSkip struct { +< B int +< C bool +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/translate/tests/readme.txt updated/vendor/github.com/coreos/fcct/translate/tests/readme.txt +1,3d0 +< Tests for this translator are in their own package since it needs to test +< translating from one package to another. This pattern should not be replicated +< in other places in the codebase +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/translate/translate.go updated/vendor/github.com/coreos/fcct/translate/translate.go +1,273d0 +< // 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 translate +< +< import ( +< "fmt" +< "reflect" +< +< "github.com/coreos/ignition/v2/config/util" +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< ) +< +< /* +< * This is an automatic translator that replace boilerplate code to copy one +< * struct into a nearly identical struct in another package. To use it first +< * 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(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 +< // ancestors might require custom translators however +< func (t translator) translatable(t1, t2 reflect.Type) bool { +< k1 := t1.Kind() +< k2 := t2.Kind() +< if k1 != k2 { +< return false +< } +< switch { +< case util.IsPrimitive(k1): +< return true +< case util.IsInvalidInConfig(k1): +< panic(fmt.Sprintf("Encountered invalid kind %s in config. This is a bug, please file a report", k1)) +< case k1 == reflect.Ptr || k1 == reflect.Slice: +< return t.translatable(t1.Elem(), t2.Elem()) || t.hasTranslator(t1.Elem(), t2.Elem()) +< case k1 == reflect.Struct: +< return t.translatableStruct(t1, t2) +< default: +< panic(fmt.Sprintf("Encountered unknown kind %s in config. This is a bug, please file a report", k1)) +< } +< } +< +< // precondition: t1, t2 are both of Kind 'struct' +< func (t translator) translatableStruct(t1, t2 reflect.Type) bool { +< if t1.Name() != t2.Name() { +< return false +< } +< t1Fields := 0 +< for i := 0; i < t1.NumField(); i++ { +< t1f := t1.Field(i) +< 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 +< } +< if !t.translatable(t1f.Type, t2f.Type) && !t.hasTranslator(t1f.Type, t2f.Type) { +< return false +< } +< } +< return t2.NumField() == t1Fields +< } +< +< // checks that t could reasonably be the type of a translator function +< func (t translator) couldBeValidTranslator(tr reflect.Type) bool { +< if tr.Kind() != reflect.Func { +< return false +< } +< if tr.NumIn() != 2 || tr.NumOut() != 3 { +< return false +< } +< 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 +< } +< +< // translate from one type to another, but deep copy all data +< // precondition: vFrom and vTo are the same type as defined by translatable() +< // precondition: vTo is addressable and settable +< func (t translator) translateSameType(vFrom, vTo reflect.Value, fromPath, toPath path.ContextPath) { +< k := vFrom.Kind() +< switch { +< case util.IsPrimitive(k): +< // Use convert, even if not needed; type alias to primitives are not +< // directly assignable and calling Convert on primitives does no harm +< vTo.Set(vFrom.Convert(vTo.Type())) +< t.translations.AddTranslation(fromPath, toPath) +< case k == reflect.Ptr: +< if vFrom.IsNil() { +< return +< } +< vTo.Set(reflect.New(vTo.Type().Elem())) +< t.translate(vFrom.Elem(), vTo.Elem(), fromPath, toPath) +< case k == reflect.Slice: +< if vFrom.IsNil() { +< return +< } +< vTo.Set(reflect.MakeSlice(vTo.Type(), vFrom.Len(), vFrom.Len())) +< for i := 0; i < vFrom.Len(); i++ { +< t.translate(vFrom.Index(i), vTo.Index(i), fromPath.Append(i), toPath.Append(i)) +< } +< 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 { +< panic("vTo did not have a matching type. This is a bug; please file a report") +< } +< toFieldIndex := toStructField.Index[0] +< vToField := vTo.FieldByName(fieldGoName) +< +< from := fromPath.Append(fieldName(vFrom, i, fromPath.Tag)) +< to := toPath.Append(fieldName(vTo, toFieldIndex, toPath.Tag)) +< if vFrom.Type().Field(i).Anonymous { +< from = fromPath +< to = toPath +< } +< t.translate(vFrom.Field(i), vToField, from, to) +< } +< default: +< panic("Encountered types that are not the same when they should be. This is a bug, please file a report") +< } +< } +< +< // helper to return if a custom translator was defined +< func (t translator) hasTranslator(tFrom, tTo reflect.Type) bool { +< return t.getTranslator(tFrom, tTo).IsValid() +< } +< +< // vTo must be addressable, should be acquired by calling reflect.ValueOf() on a variable of the correct type +< func (t translator) translate(vFrom, vTo reflect.Value, fromPath, toPath path.ContextPath) { +< tFrom := vFrom.Type() +< tTo := vTo.Type() +< if fnv := t.getTranslator(tFrom, tTo); fnv.IsValid() { +< 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 +< retSet := returns[1].Interface().(TranslationSet) +< for _, trans := range retSet.Set { +< from := fromPath.Append(trans.From.Path...) +< 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) { +< t.translateSameType(vFrom, vTo, fromPath, toPath) +< return +< } +< +< panic(fmt.Sprintf("Translator not defined for %v to %v", tFrom, tTo)) +< } +< +< type Translator interface { +< // Adds a custom translator for cases where the structs are not identical. Must be of type +< // 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, 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, options interface{}) Translator { +< return &translator{ +< options: options, +< translations: TranslationSet{ +< FromTag: fromTag, +< ToTag: toTag, +< Set: map[string]Translation{}, +< }, +< } +< } +< +< 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, optionsType) -> (toType, TranslationSet, report.Report) +< func (t *translator) AddCustomTranslator(fn interface{}) { +< fnv := reflect.ValueOf(fn) +< if !t.couldBeValidTranslator(fnv.Type()) { +< panic("Tried to register invalid translator function") +< } +< t.translators = append(t.translators, fnv) +< } +< +< func (t translator) getTranslator(from, to reflect.Type) reflect.Value { +< for _, fn := range t.translators { +< if fn.Type().In(0) == from && fn.Type().Out(0) == to { +< return fn +< } +< } +< return 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, report.Report) { +< fv := reflect.ValueOf(from) +< tv := reflect.ValueOf(to) +< if fv.Kind() != reflect.Ptr || tv.Kind() != reflect.Ptr { +< panic("Translate needs to be called on pointers") +< } +< fv = fv.Elem() +< tv = tv.Elem() +< // 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, *t.report +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/translate/translate_test.go updated/vendor/github.com/coreos/fcct/translate/translate_test.go +1,346d0 +< // 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 translate +< +< import ( +< "errors" +< "testing" +< +< "github.com/coreos/fcct/translate/tests/pkga" +< "github.com/coreos/fcct/translate/tests/pkgb" +< +< "github.com/coreos/vcontext/path" +< "github.com/coreos/vcontext/report" +< "github.com/stretchr/testify/assert" +< ) +< +< type testOptions struct{} +< +< // Note: we need different input and output types which unfortunately means a lot of tests +< +< // mkTrans makes a TranslationSet with no tag in the paths consuming pairs of args. i.e: +< // mkTrans(from1, to1, from2, to2) -> a set wiht from1->to1, from2->to2 +< // This is just a shorthand for making writing tests easier +< func mkTrans(paths ...path.ContextPath) TranslationSet { +< ret := TranslationSet{Set: map[string]Translation{}} +< if len(paths)%2 == 1 { +< panic("Odd number of args to mkTrans") +< } +< for i := 0; i < len(paths); i += 2 { +< ret.AddTranslation(paths[i], paths[i+1]) +< } +< return ret +< } +< +< // fp means "fastpath"; super shorthand, we'll use it a lot +< func fp(parts ...interface{}) path.ContextPath { +< return path.New("", parts...) +< } +< +< func TestTranslateTrivial(t *testing.T) { +< in := pkga.Trivial{ +< A: "asdf", +< B: 5, +< C: true, +< } +< +< expected := pkgb.Trivial{ +< A: "asdf", +< B: 5, +< C: true, +< } +< exTrans := mkTrans( +< fp("A"), fp("A"), +< fp("B"), fp("B"), +< fp("C"), fp("C"), +< ) +< +< got := pkgb.Trivial{} +< +< trans := NewTranslator("", "", testOptions{}) +< +< ts, r := trans.Translate(&in, &got) +< assert.Equal(t, got, expected, "bad translation") +< assert.Equal(t, ts, exTrans, "bad translation") +< assert.Equal(t, r.String(), "", "non-empty report") +< } +< +< func TestTranslateNested(t *testing.T) { +< in := pkga.Nested{ +< D: "foobar", +< Trivial: pkga.Trivial{ +< A: "asdf", +< B: 5, +< C: true, +< }, +< } +< +< expected := pkgb.Nested{ +< D: "foobar", +< Trivial: pkgb.Trivial{ +< A: "asdf", +< B: 5, +< C: true, +< }, +< } +< exTrans := mkTrans( +< fp("A"), fp("A"), +< fp("B"), fp("B"), +< fp("C"), fp("C"), +< fp("D"), fp("D"), +< ) +< +< got := pkgb.Nested{} +< +< trans := NewTranslator("", "", testOptions{}) +< +< ts, r := trans.Translate(&in, &got) +< assert.Equal(t, got, expected, "bad translation") +< assert.Equal(t, ts, exTrans, "bad translation") +< assert.Equal(t, r.String(), "", "non-empty report") +< } +< +< func TestTranslateTrivialReordered(t *testing.T) { +< in := pkga.TrivialReordered{ +< A: "asdf", +< B: 5, +< C: true, +< } +< +< expected := pkgb.TrivialReordered{ +< A: "asdf", +< B: 5, +< C: true, +< } +< exTrans := mkTrans( +< fp("A"), fp("A"), +< fp("B"), fp("B"), +< fp("C"), fp("C"), +< ) +< +< got := pkgb.TrivialReordered{} +< +< trans := NewTranslator("", "", testOptions{}) +< +< ts, r := trans.Translate(&in, &got) +< assert.Equal(t, got, expected, "bad translation") +< assert.Equal(t, ts, exTrans, "bad translation") +< assert.Equal(t, r.String(), "", "non-empty report") +< } +< +< func TestTranslateTrivialSkip(t *testing.T) { +< in := pkga.TrivialSkip{ +< A: "asdf", +< B: 5, +< C: true, +< } +< +< expected := pkgb.TrivialSkip{ +< B: 5, +< C: true, +< } +< exTrans := mkTrans( +< fp("B"), fp("B"), +< fp("C"), fp("C"), +< ) +< +< got := pkgb.TrivialSkip{} +< +< trans := NewTranslator("", "", testOptions{}) +< +< ts, r := trans.Translate(&in, &got) +< assert.Equal(t, got, expected, "bad translation") +< assert.Equal(t, ts, exTrans, "bad translation") +< assert.Equal(t, r.String(), "", "non-empty report") +< } +< +< func TestCustomTranslatorTrivial(t *testing.T) { +< tr := func(a pkga.Trivial, options testOptions) (pkgb.Nested, TranslationSet, report.Report) { +< ts := mkTrans(fp("A"), fp("A"), +< fp("B"), fp("B"), +< fp("C"), fp("C"), +< fp("C"), fp("D"), +< ) +< var r report.Report +< r.AddOnInfo(fp("A"), errors.New("info")) +< return pkgb.Nested{ +< Trivial: pkgb.Trivial{ +< A: a.A, +< B: a.B, +< C: a.C, +< }, +< D: "abc", +< }, ts, r +< } +< in := pkga.Trivial{ +< A: "asdf", +< B: 5, +< C: true, +< } +< +< expected := pkgb.Nested{ +< D: "abc", +< Trivial: pkgb.Trivial{ +< A: "asdf", +< B: 5, +< C: true, +< }, +< } +< exTrans := mkTrans( +< fp("A"), fp("A"), +< fp("B"), fp("B"), +< fp("C"), fp("C"), +< fp("C"), fp("D"), +< ) +< +< got := pkgb.Nested{} +< +< trans := NewTranslator("", "", testOptions{}) +< trans.AddCustomTranslator(tr) +< +< ts, r := trans.Translate(&in, &got) +< assert.Equal(t, got, expected, "bad translation") +< assert.Equal(t, ts, exTrans, "bad translation") +< assert.Equal(t, r.String(), "info at $.A: info\n", "bad report") +< } +< +< func TestCustomTranslatorTrivialWithAutomaticResume(t *testing.T) { +< trans := NewTranslator("", "", testOptions{}) +< tr := func(a pkga.Trivial, options testOptions) (pkgb.Nested, TranslationSet, report.Report) { +< ret := pkgb.Nested{ +< D: "abc", +< } +< ts, r := trans.Translate(&a, &ret.Trivial) +< ts.AddTranslation(fp("C"), fp("D")) +< return ret, ts, r +< } +< in := pkga.Trivial{ +< A: "asdf", +< B: 5, +< C: true, +< } +< exTrans := mkTrans( +< fp("A"), fp("A"), +< fp("B"), fp("B"), +< fp("C"), fp("C"), +< fp("C"), fp("D"), +< ) +< +< expected := pkgb.Nested{ +< D: "abc", +< Trivial: pkgb.Trivial{ +< A: "asdf", +< B: 5, +< C: true, +< }, +< } +< +< got := pkgb.Nested{} +< +< trans.AddCustomTranslator(tr) +< +< ts, r := trans.Translate(&in, &got) +< assert.Equal(t, got, expected, "bad translation") +< assert.Equal(t, ts, exTrans, "bad translation") +< assert.Equal(t, r.String(), "", "non-empty report") +< } +< +< func TestCustomTranslatorList(t *testing.T) { +< tr := func(a pkga.Trivial, options testOptions) (pkgb.Nested, TranslationSet, report.Report) { +< ts := mkTrans(fp("A"), fp("A"), +< fp("B"), fp("B"), +< fp("C"), fp("C"), +< fp("C"), fp("D"), +< ) +< return pkgb.Nested{ +< Trivial: pkgb.Trivial{ +< A: a.A, +< B: a.B, +< C: a.C, +< }, +< D: "abc", +< }, ts, report.Report{} +< } +< in := pkga.HasList{ +< L: []pkga.Trivial{ +< { +< A: "asdf", +< B: 5, +< C: true, +< }, +< }, +< } +< +< expected := pkgb.HasList{ +< L: []pkgb.Nested{ +< { +< D: "abc", +< Trivial: pkgb.Trivial{ +< A: "asdf", +< B: 5, +< C: true, +< }, +< }, +< }, +< } +< exTrans := mkTrans( +< fp("L", 0, "A"), fp("L", 0, "A"), +< fp("L", 0, "B"), fp("L", 0, "B"), +< fp("L", 0, "C"), fp("L", 0, "C"), +< fp("L", 0, "C"), fp("L", 0, "D"), +< ) +< +< got := pkgb.HasList{} +< +< trans := NewTranslator("", "", testOptions{}) +< trans.AddCustomTranslator(tr) +< +< ts, r := trans.Translate(&in, &got) +< assert.Equal(t, got, expected, "bad translation") +< assert.Equal(t, ts, exTrans, "bad translation") +< assert.Equal(t, r.String(), "", "non-empty report") +< } +< +< func TestAddIdentity(t *testing.T) { +< ts := NewTranslationSet("1", "2") +< ts.AddIdentity("foo", "bar") +< expectedFoo := Translation{ +< From: path.New("1", "foo"), +< To: path.New("2", "foo"), +< } +< expectedBar := Translation{ +< From: path.New("1", "bar"), +< To: path.New("2", "bar"), +< } +< expectedFoo2 := Translation{ +< From: path.New("1", "pre", "foo"), +< To: path.New("2", "pre", "foo"), +< } +< expectedBar2 := Translation{ +< From: path.New("1", "pre", "bar"), +< To: path.New("2", "pre", "bar"), +< } +< ts2 := NewTranslationSet("1", "2") +< ts2.MergeP("pre", ts) +< ts3 := NewTranslationSet("1", "2") +< ts3.Merge(ts.Prefix("pre")) +< +< assert.Equal(t, ts.Set["$.foo"], expectedFoo, "foo not added correctly") +< assert.Equal(t, ts.Set["$.bar"], expectedBar, "bar not added correctly") +< assert.Equal(t, ts2.Set["$.pre.foo"], expectedFoo2, "foo not added correctly") +< assert.Equal(t, ts3.Set["$.pre.bar"], expectedBar2, "bar not added correctly") +< assert.Equal(t, ts3.Set["$.pre.foo"], expectedFoo2, "foo not added correctly") +< assert.Equal(t, ts2.Set["$.pre.bar"], expectedBar2, "bar not added correctly") +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/translate/util.go updated/vendor/github.com/coreos/fcct/translate/util.go +1,114d0 +< // 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 translate +< +< import ( +< "reflect" +< "strings" +< +< "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, +< // index is the field index, and tag is the struct tag used when (un)marshalling (e.g. "json" or "yaml") +< func fieldName(t reflect.Value, index int, tag string) string { +< f := t.Type().Field(index) +< if tag == "" { +< return f.Name +< } +< return strings.Split(f.Tag.Get(tag), ",")[0] +< } +< +< func prefixPath(p path.ContextPath, prefix ...interface{}) path.ContextPath { +< return path.New(p.Tag, prefix...).Append(p.Path...) +< } +< +< func prefixPaths(ps []path.ContextPath, prefix ...interface{}) []path.ContextPath { +< ret := []path.ContextPath{} +< for _, p := range ps { +< ret = append(ret, prefixPath(p, prefix...)) +< } +< return ret +< } +< +< func getAllPaths(v reflect.Value, tag string) []path.ContextPath { +< k := v.Kind() +< t := v.Type() +< switch { +< case util.IsPrimitive(k): +< return nil +< case k == reflect.Ptr: +< if v.IsNil() { +< return nil +< } +< return getAllPaths(v.Elem(), tag) +< case k == reflect.Slice: +< ret := []path.ContextPath{} +< for i := 0; i < v.Len(); i++ { +< paths := getAllPaths(v.Index(i), tag) +< if len(paths) > 0 { +< // struct, pointer to struct, etc.; add children +< ret = append(ret, prefixPaths(paths, i)...) +< } else { +< // primitive type; add slice entry +< ret = append(ret, path.New(tag, i)) +< } +< } +< return ret +< case k == reflect.Struct: +< ret := []path.ContextPath{} +< for i := 0; i < t.NumField(); i++ { +< name := fieldName(v, i, tag) +< field := v.Field(i) +< if t.Field(i).Anonymous { +< ret = append(ret, getAllPaths(field, tag)...) +< } else { +< ret = append(ret, prefixPaths(getAllPaths(field, tag), name)...) +< ret = append(ret, path.New(tag, name)) +< } +< } +< return ret +< default: +< 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 --no-dereference -N -r current/vendor/github.com/coreos/fcct/vendor/github.com/clarketm/json/LICENSE updated/vendor/github.com/coreos/fcct/vendor/github.com/clarketm/json/LICENSE +1,27d0 +< Copyright (c) 2009 The Go Authors. All rights reserved. +< +< Redistribution and use in source and binary forms, with or without +< modification, are permitted provided that the following conditions are +< met: +< +< * Redistributions of source code must retain the above copyright +< notice, this list of conditions and the following disclaimer. +< * Redistributions in binary form must reproduce the above +< copyright notice, this list of conditions and the following disclaimer +< in the documentation and/or other materials provided with the +< distribution. +< * Neither the name of Google Inc. nor the names of its +< contributors may be used to endorse or promote products derived from +< this software without specific prior written permission. +< +< THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +< "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +< LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +< A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +< OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +< SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +< LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +< DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +< THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +< (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +< OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/vendor/github.com/clarketm/json/README.md updated/vendor/github.com/coreos/fcct/vendor/github.com/clarketm/json/README.md +1,19d0 +< # [json](https://godoc.org/github.com/clarketm/json) +< > Mirrors [golang/go](https://github.com/golang/go) [![Golang version](https://img.shields.io/badge/go-1.12.7-green)](https://github.com/golang/go/releases/tag/go1.12.7) +< +< Drop-in replacement for Golang [`encoding/json`](https://golang.org/pkg/encoding/json/) with additional features. +< +< ## Installation +< ```shell +< $ go get -u github.com/clarketm/json +< ``` +< +< ## Usage +< Same usage as Golang [`encoding/json`](https://golang.org/pkg/encoding/json/). +< +< ## Features +< - Support zero values of structs with `omitempty`: [golang/go#11939](https://github.com/golang/go/issues/11939). +< > If `omitempty` is applied to a struct and all the children of the struct are *empty*, then on marshalling it will be **omitted** from the encoded json. +< +< ## License +< Refer to the [Golang](https://github.com/golang/go/blob/master/LICENSE) license. See [LICENSE](LICENSE) for more information. +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/vendor/github.com/clarketm/json/decode.go updated/vendor/github.com/coreos/fcct/vendor/github.com/clarketm/json/decode.go +1,1304d0 +< // Copyright 2010 The Go Authors. All rights reserved. +< // Use of this source code is governed by a BSD-style +< // license that can be found in the LICENSE file. +< +< // Represents JSON data structure using native Go types: booleans, floats, +< // strings, arrays, and maps. +< +< package json +< +< import ( +< "encoding" +< "encoding/base64" +< "fmt" +< "reflect" +< "strconv" +< "strings" +< "unicode" +< "unicode/utf16" +< "unicode/utf8" +< ) +< +< // Unmarshal parses the JSON-encoded data and stores the result +< // in the value pointed to by v. If v is nil or not a pointer, +< // Unmarshal returns an InvalidUnmarshalError. +< // +< // Unmarshal uses the inverse of the encodings that +< // Marshal uses, allocating maps, slices, and pointers as necessary, +< // with the following additional rules: +< // +< // To unmarshal JSON into a pointer, Unmarshal first handles the case of +< // the JSON being the JSON literal null. In that case, Unmarshal sets +< // the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into +< // the value pointed at by the pointer. If the pointer is nil, Unmarshal +< // allocates a new value for it to point to. +< // +< // To unmarshal JSON into a value implementing the Unmarshaler interface, +< // Unmarshal calls that value's UnmarshalJSON method, including +< // when the input is a JSON null. +< // Otherwise, if the value implements encoding.TextUnmarshaler +< // and the input is a JSON quoted string, Unmarshal calls that value's +< // UnmarshalText method with the unquoted form of the string. +< // +< // To unmarshal JSON into a struct, Unmarshal matches incoming object +< // keys to the keys used by Marshal (either the struct field name or its tag), +< // preferring an exact match but also accepting a case-insensitive match. By +< // default, object keys which don't have a corresponding struct field are +< // ignored (see Decoder.DisallowUnknownFields for an alternative). +< // +< // To unmarshal JSON into an interface value, +< // Unmarshal stores one of these in the interface value: +< // +< // bool, for JSON booleans +< // float64, for JSON numbers +< // string, for JSON strings +< // []interface{}, for JSON arrays +< // map[string]interface{}, for JSON objects +< // nil for JSON null +< // +< // To unmarshal a JSON array into a slice, Unmarshal resets the slice length +< // to zero and then appends each element to the slice. +< // As a special case, to unmarshal an empty JSON array into a slice, +< // Unmarshal replaces the slice with a new empty slice. +< // +< // To unmarshal a JSON array into a Go array, Unmarshal decodes +< // JSON array elements into corresponding Go array elements. +< // If the Go array is smaller than the JSON array, +< // the additional JSON array elements are discarded. +< // If the JSON array is smaller than the Go array, +< // the additional Go array elements are set to zero values. +< // +< // To unmarshal a JSON object into a map, Unmarshal first establishes a map to +< // use. If the map is nil, Unmarshal allocates a new map. Otherwise Unmarshal +< // reuses the existing map, keeping existing entries. Unmarshal then stores +< // key-value pairs from the JSON object into the map. The map's key type must +< // either be any string type, an integer, implement json.Unmarshaler, or +< // implement encoding.TextUnmarshaler. +< // +< // If a JSON value is not appropriate for a given target type, +< // or if a JSON number overflows the target type, Unmarshal +< // skips that field and completes the unmarshaling as best it can. +< // If no more serious errors are encountered, Unmarshal returns +< // an UnmarshalTypeError describing the earliest such error. In any +< // case, it's not guaranteed that all the remaining fields following +< // the problematic one will be unmarshaled into the target object. +< // +< // The JSON null value unmarshals into an interface, map, pointer, or slice +< // by setting that Go value to nil. Because null is often used in JSON to mean +< // ``not present,'' unmarshaling a JSON null into any other Go type has no effect +< // on the value and produces no error. +< // +< // When unmarshaling quoted strings, invalid UTF-8 or +< // invalid UTF-16 surrogate pairs are not treated as an error. +< // Instead, they are replaced by the Unicode replacement +< // character U+FFFD. +< // +< func Unmarshal(data []byte, v interface{}) error { +< // Check for well-formedness. +< // Avoids filling out half a data structure +< // before discovering a JSON syntax error. +< var d decodeState +< err := checkValid(data, &d.scan) +< if err != nil { +< return err +< } +< +< d.init(data) +< return d.unmarshal(v) +< } +< +< // Unmarshaler is the interface implemented by types +< // that can unmarshal a JSON description of themselves. +< // The input can be assumed to be a valid encoding of +< // a JSON value. UnmarshalJSON must copy the JSON data +< // if it wishes to retain the data after returning. +< // +< // By convention, to approximate the behavior of Unmarshal itself, +< // Unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op. +< type Unmarshaler interface { +< UnmarshalJSON([]byte) error +< } +< +< // An UnmarshalTypeError describes a JSON value that was +< // not appropriate for a value of a specific Go type. +< type UnmarshalTypeError struct { +< Value string // description of JSON value - "bool", "array", "number -5" +< Type reflect.Type // type of Go value it could not be assigned to +< Offset int64 // error occurred after reading Offset bytes +< Struct string // name of the struct type containing the field +< Field string // the full path from root node to the field +< } +< +< func (e *UnmarshalTypeError) Error() string { +< if e.Struct != "" || e.Field != "" { +< return "json: cannot unmarshal " + e.Value + " into Go struct field " + e.Struct + "." + e.Field + " of type " + e.Type.String() +< } +< return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() +< } +< +< // An UnmarshalFieldError describes a JSON object key that +< // led to an unexported (and therefore unwritable) struct field. +< // +< // Deprecated: No longer used; kept for compatibility. +< type UnmarshalFieldError struct { +< Key string +< Type reflect.Type +< Field reflect.StructField +< } +< +< func (e *UnmarshalFieldError) Error() string { +< return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String() +< } +< +< // An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. +< // (The argument to Unmarshal must be a non-nil pointer.) +< type InvalidUnmarshalError struct { +< Type reflect.Type +< } +< +< func (e *InvalidUnmarshalError) Error() string { +< if e.Type == nil { +< return "json: Unmarshal(nil)" +< } +< +< if e.Type.Kind() != reflect.Ptr { +< return "json: Unmarshal(non-pointer " + e.Type.String() + ")" +< } +< return "json: Unmarshal(nil " + e.Type.String() + ")" +< } +< +< func (d *decodeState) unmarshal(v interface{}) error { +< rv := reflect.ValueOf(v) +< if rv.Kind() != reflect.Ptr || rv.IsNil() { +< return &InvalidUnmarshalError{reflect.TypeOf(v)} +< } +< +< d.scan.reset() +< d.scanWhile(scanSkipSpace) +< // We decode rv not rv.Elem because the Unmarshaler interface +< // test must be applied at the top level of the value. +< err := d.value(rv) +< if err != nil { +< return d.addErrorContext(err) +< } +< return d.savedError +< } +< +< // A Number represents a JSON number literal. +< type Number string +< +< // String returns the literal text of the number. +< func (n Number) String() string { return string(n) } +< +< // Float64 returns the number as a float64. +< func (n Number) Float64() (float64, error) { +< return strconv.ParseFloat(string(n), 64) +< } +< +< // Int64 returns the number as an int64. +< func (n Number) Int64() (int64, error) { +< return strconv.ParseInt(string(n), 10, 64) +< } +< +< // decodeState represents the state while decoding a JSON value. +< type decodeState struct { +< data []byte +< off int // next read offset in data +< opcode int // last read result +< scan scanner +< errorContext struct { // provides context for type errors +< Struct reflect.Type +< FieldStack []string +< } +< savedError error +< useNumber bool +< disallowUnknownFields bool +< // safeUnquote is the number of current string literal bytes that don't +< // need to be unquoted. When negative, no bytes need unquoting. +< safeUnquote int +< } +< +< // readIndex returns the position of the last byte read. +< func (d *decodeState) readIndex() int { +< return d.off - 1 +< } +< +< // phasePanicMsg is used as a panic message when we end up with something that +< // shouldn't happen. It can indicate a bug in the JSON decoder, or that +< // something is editing the data slice while the decoder executes. +< const phasePanicMsg = "JSON decoder out of sync - data changing underfoot?" +< +< func (d *decodeState) init(data []byte) *decodeState { +< d.data = data +< d.off = 0 +< d.savedError = nil +< d.errorContext.Struct = nil +< +< // Reuse the allocated space for the FieldStack slice. +< d.errorContext.FieldStack = d.errorContext.FieldStack[:0] +< return d +< } +< +< // saveError saves the first err it is called with, +< // for reporting at the end of the unmarshal. +< func (d *decodeState) saveError(err error) { +< if d.savedError == nil { +< d.savedError = d.addErrorContext(err) +< } +< } +< +< // addErrorContext returns a new error enhanced with information from d.errorContext +< func (d *decodeState) addErrorContext(err error) error { +< if d.errorContext.Struct != nil || len(d.errorContext.FieldStack) > 0 { +< switch err := err.(type) { +< case *UnmarshalTypeError: +< err.Struct = d.errorContext.Struct.Name() +< err.Field = strings.Join(d.errorContext.FieldStack, ".") +< return err +< } +< } +< return err +< } +< +< // skip scans to the end of what was started. +< func (d *decodeState) skip() { +< s, data, i := &d.scan, d.data, d.off +< depth := len(s.parseState) +< for { +< op := s.step(s, data[i]) +< i++ +< if len(s.parseState) < depth { +< d.off = i +< d.opcode = op +< return +< } +< } +< } +< +< // scanNext processes the byte at d.data[d.off]. +< func (d *decodeState) scanNext() { +< if d.off < len(d.data) { +< d.opcode = d.scan.step(&d.scan, d.data[d.off]) +< d.off++ +< } else { +< d.opcode = d.scan.eof() +< d.off = len(d.data) + 1 // mark processed EOF with len+1 +< } +< } +< +< // scanWhile processes bytes in d.data[d.off:] until it +< // receives a scan code not equal to op. +< func (d *decodeState) scanWhile(op int) { +< s, data, i := &d.scan, d.data, d.off +< for i < len(data) { +< newOp := s.step(s, data[i]) +< i++ +< if newOp != op { +< d.opcode = newOp +< d.off = i +< return +< } +< } +< +< d.off = len(data) + 1 // mark processed EOF with len+1 +< d.opcode = d.scan.eof() +< } +< +< // rescanLiteral is similar to scanWhile(scanContinue), but it specialises the +< // common case where we're decoding a literal. The decoder scans the input +< // twice, once for syntax errors and to check the length of the value, and the +< // second to perform the decoding. +< // +< // Only in the second step do we use decodeState to tokenize literals, so we +< // know there aren't any syntax errors. We can take advantage of that knowledge, +< // and scan a literal's bytes much more quickly. +< func (d *decodeState) rescanLiteral() { +< data, i := d.data, d.off +< Switch: +< switch data[i-1] { +< case '"': // string +< // safeUnquote is initialized at -1, which means that all bytes +< // checked so far can be unquoted at a later time with no work +< // at all. When reaching the closing '"', if safeUnquote is +< // still -1, all bytes can be unquoted with no work. Otherwise, +< // only those bytes up until the first '\\' or non-ascii rune +< // can be safely unquoted. +< safeUnquote := -1 +< for ; i < len(data); i++ { +< if c := data[i]; c == '\\' { +< if safeUnquote < 0 { // first unsafe byte +< safeUnquote = int(i - d.off) +< } +< i++ // escaped char +< } else if c == '"' { +< d.safeUnquote = safeUnquote +< i++ // tokenize the closing quote too +< break Switch +< } else if c >= utf8.RuneSelf { +< if safeUnquote < 0 { // first unsafe byte +< safeUnquote = int(i - d.off) +< } +< } +< } +< case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': // number +< for ; i < len(data); i++ { +< switch data[i] { +< case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', +< '.', 'e', 'E', '+', '-': +< default: +< break Switch +< } +< } +< case 't': // true +< i += len("rue") +< case 'f': // false +< i += len("alse") +< case 'n': // null +< i += len("ull") +< } +< if i < len(data) { +< d.opcode = stateEndValue(&d.scan, data[i]) +< } else { +< d.opcode = scanEnd +< } +< d.off = i + 1 +< } +< +< // value consumes a JSON value from d.data[d.off-1:], decoding into v, and +< // reads the following byte ahead. If v is invalid, the value is discarded. +< // The first byte of the value has been read already. +< func (d *decodeState) value(v reflect.Value) error { +< switch d.opcode { +< default: +< panic(phasePanicMsg) +< +< case scanBeginArray: +< if v.IsValid() { +< if err := d.array(v); err != nil { +< return err +< } +< } else { +< d.skip() +< } +< d.scanNext() +< +< case scanBeginObject: +< if v.IsValid() { +< if err := d.object(v); err != nil { +< return err +< } +< } else { +< d.skip() +< } +< d.scanNext() +< +< case scanBeginLiteral: +< // All bytes inside literal return scanContinue op code. +< start := d.readIndex() +< d.rescanLiteral() +< +< if v.IsValid() { +< if err := d.literalStore(d.data[start:d.readIndex()], v, false); err != nil { +< return err +< } +< } +< } +< return nil +< } +< +< type unquotedValue struct{} +< +< // valueQuoted is like value but decodes a +< // quoted string literal or literal null into an interface value. +< // If it finds anything other than a quoted string literal or null, +< // valueQuoted returns unquotedValue{}. +< func (d *decodeState) valueQuoted() interface{} { +< switch d.opcode { +< default: +< panic(phasePanicMsg) +< +< case scanBeginArray, scanBeginObject: +< d.skip() +< d.scanNext() +< +< case scanBeginLiteral: +< v := d.literalInterface() +< switch v.(type) { +< case nil, string: +< return v +< } +< } +< return unquotedValue{} +< } +< +< // indirect walks down v allocating pointers as needed, +< // until it gets to a non-pointer. +< // If it encounters an Unmarshaler, indirect stops and returns that. +< // If decodingNull is true, indirect stops at the first settable pointer so it +< // can be set to nil. +< func indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value) { +< // Issue #24153 indicates that it is generally not a guaranteed property +< // that you may round-trip a reflect.Value by calling Value.Addr().Elem() +< // and expect the value to still be settable for values derived from +< // unexported embedded struct fields. +< // +< // The logic below effectively does this when it first addresses the value +< // (to satisfy possible pointer methods) and continues to dereference +< // subsequent pointers as necessary. +< // +< // After the first round-trip, we set v back to the original value to +< // preserve the original RW flags contained in reflect.Value. +< v0 := v +< haveAddr := false +< +< // If v is a named type and is addressable, +< // start with its address, so that if the type has pointer methods, +< // we find them. +< if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() { +< haveAddr = true +< v = v.Addr() +< } +< for { +< // Load value from interface, but only if the result will be +< // usefully addressable. +< if v.Kind() == reflect.Interface && !v.IsNil() { +< e := v.Elem() +< if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) { +< haveAddr = false +< v = e +< continue +< } +< } +< +< if v.Kind() != reflect.Ptr { +< break +< } +< +< if decodingNull && v.CanSet() { +< break +< } +< +< // Prevent infinite loop if v is an interface pointing to its own address: +< // var v interface{} +< // v = &v +< if v.Elem().Kind() == reflect.Interface && v.Elem().Elem() == v { +< v = v.Elem() +< break +< } +< if v.IsNil() { +< v.Set(reflect.New(v.Type().Elem())) +< } +< if v.Type().NumMethod() > 0 && v.CanInterface() { +< if u, ok := v.Interface().(Unmarshaler); ok { +< return u, nil, reflect.Value{} +< } +< if !decodingNull { +< if u, ok := v.Interface().(encoding.TextUnmarshaler); ok { +< return nil, u, reflect.Value{} +< } +< } +< } +< +< if haveAddr { +< v = v0 // restore original value after round-trip Value.Addr().Elem() +< haveAddr = false +< } else { +< v = v.Elem() +< } +< } +< return nil, nil, v +< } +< +< // array consumes an array from d.data[d.off-1:], decoding into v. +< // The first byte of the array ('[') has been read already. +< func (d *decodeState) array(v reflect.Value) error { +< // Check for unmarshaler. +< u, ut, pv := indirect(v, false) +< if u != nil { +< start := d.readIndex() +< d.skip() +< return u.UnmarshalJSON(d.data[start:d.off]) +< } +< if ut != nil { +< d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)}) +< d.skip() +< return nil +< } +< v = pv +< +< // Check type of target. +< switch v.Kind() { +< case reflect.Interface: +< if v.NumMethod() == 0 { +< // Decoding into nil interface? Switch to non-reflect code. +< ai := d.arrayInterface() +< v.Set(reflect.ValueOf(ai)) +< return nil +< } +< // Otherwise it's invalid. +< fallthrough +< default: +< d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)}) +< d.skip() +< return nil +< case reflect.Array, reflect.Slice: +< break +< } +< +< i := 0 +< for { +< // Look ahead for ] - can only happen on first iteration. +< d.scanWhile(scanSkipSpace) +< if d.opcode == scanEndArray { +< break +< } +< +< // Get element of array, growing if necessary. +< if v.Kind() == reflect.Slice { +< // Grow slice if necessary +< if i >= v.Cap() { +< newcap := v.Cap() + v.Cap()/2 +< if newcap < 4 { +< newcap = 4 +< } +< newv := reflect.MakeSlice(v.Type(), v.Len(), newcap) +< reflect.Copy(newv, v) +< v.Set(newv) +< } +< if i >= v.Len() { +< v.SetLen(i + 1) +< } +< } +< +< if i < v.Len() { +< // Decode into element. +< if err := d.value(v.Index(i)); err != nil { +< return err +< } +< } else { +< // Ran out of fixed array: skip. +< if err := d.value(reflect.Value{}); err != nil { +< return err +< } +< } +< i++ +< +< // Next token must be , or ]. +< if d.opcode == scanSkipSpace { +< d.scanWhile(scanSkipSpace) +< } +< if d.opcode == scanEndArray { +< break +< } +< if d.opcode != scanArrayValue { +< panic(phasePanicMsg) +< } +< } +< +< if i < v.Len() { +< if v.Kind() == reflect.Array { +< // Array. Zero the rest. +< z := reflect.Zero(v.Type().Elem()) +< for ; i < v.Len(); i++ { +< v.Index(i).Set(z) +< } +< } else { +< v.SetLen(i) +< } +< } +< if i == 0 && v.Kind() == reflect.Slice { +< v.Set(reflect.MakeSlice(v.Type(), 0, 0)) +< } +< return nil +< } +< +< var nullLiteral = []byte("null") +< var textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() +< +< // object consumes an object from d.data[d.off-1:], decoding into v. +< // The first byte ('{') of the object has been read already. +< func (d *decodeState) object(v reflect.Value) error { +< // Check for unmarshaler. +< u, ut, pv := indirect(v, false) +< if u != nil { +< start := d.readIndex() +< d.skip() +< return u.UnmarshalJSON(d.data[start:d.off]) +< } +< if ut != nil { +< d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)}) +< d.skip() +< return nil +< } +< v = pv +< t := v.Type() +< +< // Decoding into nil interface? Switch to non-reflect code. +< if v.Kind() == reflect.Interface && v.NumMethod() == 0 { +< oi := d.objectInterface() +< v.Set(reflect.ValueOf(oi)) +< return nil +< } +< +< var fields structFields +< +< // Check type of target: +< // struct or +< // map[T1]T2 where T1 is string, an integer type, +< // or an encoding.TextUnmarshaler +< switch v.Kind() { +< case reflect.Map: +< // Map key must either have string kind, have an integer kind, +< // or be an encoding.TextUnmarshaler. +< switch t.Key().Kind() { +< case reflect.String, +< reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, +< reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: +< default: +< if !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) { +< d.saveError(&UnmarshalTypeError{Value: "object", Type: t, Offset: int64(d.off)}) +< d.skip() +< return nil +< } +< } +< if v.IsNil() { +< v.Set(reflect.MakeMap(t)) +< } +< case reflect.Struct: +< fields = cachedTypeFields(t) +< // ok +< default: +< d.saveError(&UnmarshalTypeError{Value: "object", Type: t, Offset: int64(d.off)}) +< d.skip() +< return nil +< } +< +< var mapElem reflect.Value +< origErrorContext := d.errorContext +< +< for { +< // Read opening " of string key or closing }. +< d.scanWhile(scanSkipSpace) +< if d.opcode == scanEndObject { +< // closing } - can only happen on first iteration. +< break +< } +< if d.opcode != scanBeginLiteral { +< panic(phasePanicMsg) +< } +< +< // Read key. +< start := d.readIndex() +< d.rescanLiteral() +< item := d.data[start:d.readIndex()] +< key, ok := d.unquoteBytes(item) +< if !ok { +< panic(phasePanicMsg) +< } +< +< // Figure out field corresponding to key. +< var subv reflect.Value +< destring := false // whether the value is wrapped in a string to be decoded first +< +< if v.Kind() == reflect.Map { +< elemType := t.Elem() +< if !mapElem.IsValid() { +< mapElem = reflect.New(elemType).Elem() +< } else { +< mapElem.Set(reflect.Zero(elemType)) +< } +< subv = mapElem +< } else { +< var f *field +< if i, ok := fields.nameIndex[string(key)]; ok { +< // Found an exact name match. +< f = &fields.list[i] +< } else { +< // Fall back to the expensive case-insensitive +< // linear search. +< for i := range fields.list { +< ff := &fields.list[i] +< if ff.equalFold(ff.nameBytes, key) { +< f = ff +< break +< } +< } +< } +< if f != nil { +< subv = v +< destring = f.quoted +< for _, i := range f.index { +< if subv.Kind() == reflect.Ptr { +< if subv.IsNil() { +< // If a struct embeds a pointer to an unexported type, +< // it is not possible to set a newly allocated value +< // since the field is unexported. +< // +< // See https://golang.org/issue/21357 +< if !subv.CanSet() { +< d.saveError(fmt.Errorf("json: cannot set embedded pointer to unexported struct: %v", subv.Type().Elem())) +< // Invalidate subv to ensure d.value(subv) skips over +< // the JSON value without assigning it to subv. +< subv = reflect.Value{} +< destring = false +< break +< } +< subv.Set(reflect.New(subv.Type().Elem())) +< } +< subv = subv.Elem() +< } +< subv = subv.Field(i) +< } +< d.errorContext.FieldStack = append(d.errorContext.FieldStack, f.name) +< d.errorContext.Struct = t +< } else if d.disallowUnknownFields { +< d.saveError(fmt.Errorf("json: unknown field %q", key)) +< } +< } +< +< // Read : before value. +< if d.opcode == scanSkipSpace { +< d.scanWhile(scanSkipSpace) +< } +< if d.opcode != scanObjectKey { +< panic(phasePanicMsg) +< } +< d.scanWhile(scanSkipSpace) +< +< if destring { +< switch qv := d.valueQuoted().(type) { +< case nil: +< if err := d.literalStore(nullLiteral, subv, false); err != nil { +< return err +< } +< case string: +< if err := d.literalStore([]byte(qv), subv, true); err != nil { +< return err +< } +< default: +< d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal unquoted value into %v", subv.Type())) +< } +< } else { +< if err := d.value(subv); err != nil { +< return err +< } +< } +< +< // Write value back to map; +< // if using struct, subv points into struct already. +< if v.Kind() == reflect.Map { +< kt := t.Key() +< var kv reflect.Value +< switch { +< case reflect.PtrTo(kt).Implements(textUnmarshalerType): +< kv = reflect.New(kt) +< if err := d.literalStore(item, kv, true); err != nil { +< return err +< } +< kv = kv.Elem() +< case kt.Kind() == reflect.String: +< kv = reflect.ValueOf(key).Convert(kt) +< default: +< switch kt.Kind() { +< case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: +< s := string(key) +< n, err := strconv.ParseInt(s, 10, 64) +< if err != nil || reflect.Zero(kt).OverflowInt(n) { +< d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: kt, Offset: int64(start + 1)}) +< break +< } +< kv = reflect.ValueOf(n).Convert(kt) +< case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: +< s := string(key) +< n, err := strconv.ParseUint(s, 10, 64) +< if err != nil || reflect.Zero(kt).OverflowUint(n) { +< d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: kt, Offset: int64(start + 1)}) +< break +< } +< kv = reflect.ValueOf(n).Convert(kt) +< default: +< panic("json: Unexpected key type") // should never occur +< } +< } +< if kv.IsValid() { +< v.SetMapIndex(kv, subv) +< } +< } +< +< // Next token must be , or }. +< if d.opcode == scanSkipSpace { +< d.scanWhile(scanSkipSpace) +< } +< // Reset errorContext to its original state. +< // Keep the same underlying array for FieldStack, to reuse the +< // space and avoid unnecessary allocs. +< d.errorContext.FieldStack = d.errorContext.FieldStack[:len(origErrorContext.FieldStack)] +< d.errorContext.Struct = origErrorContext.Struct +< if d.opcode == scanEndObject { +< break +< } +< if d.opcode != scanObjectValue { +< panic(phasePanicMsg) +< } +< } +< return nil +< } +< +< // convertNumber converts the number literal s to a float64 or a Number +< // depending on the setting of d.useNumber. +< func (d *decodeState) convertNumber(s string) (interface{}, error) { +< if d.useNumber { +< return Number(s), nil +< } +< f, err := strconv.ParseFloat(s, 64) +< if err != nil { +< return nil, &UnmarshalTypeError{Value: "number " + s, Type: reflect.TypeOf(0.0), Offset: int64(d.off)} +< } +< return f, nil +< } +< +< var numberType = reflect.TypeOf(Number("")) +< +< // literalStore decodes a literal stored in item into v. +< // +< // fromQuoted indicates whether this literal came from unwrapping a +< // string from the ",string" struct tag option. this is used only to +< // produce more helpful error messages. +< func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) error { +< // Check for unmarshaler. +< if len(item) == 0 { +< //Empty string given +< d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) +< return nil +< } +< isNull := item[0] == 'n' // null +< u, ut, pv := indirect(v, isNull) +< if u != nil { +< return u.UnmarshalJSON(item) +< } +< if ut != nil { +< if item[0] != '"' { +< if fromQuoted { +< d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) +< return nil +< } +< val := "number" +< switch item[0] { +< case 'n': +< val = "null" +< case 't', 'f': +< val = "bool" +< } +< d.saveError(&UnmarshalTypeError{Value: val, Type: v.Type(), Offset: int64(d.readIndex())}) +< return nil +< } +< s, ok := d.unquoteBytes(item) +< if !ok { +< if fromQuoted { +< return fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()) +< } +< panic(phasePanicMsg) +< } +< return ut.UnmarshalText(s) +< } +< +< v = pv +< +< switch c := item[0]; c { +< case 'n': // null +< // The main parser checks that only true and false can reach here, +< // but if this was a quoted string input, it could be anything. +< if fromQuoted && string(item) != "null" { +< d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) +< break +< } +< switch v.Kind() { +< case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: +< v.Set(reflect.Zero(v.Type())) +< // otherwise, ignore null for primitives/string +< } +< case 't', 'f': // true, false +< value := item[0] == 't' +< // The main parser checks that only true and false can reach here, +< // but if this was a quoted string input, it could be anything. +< if fromQuoted && string(item) != "true" && string(item) != "false" { +< d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) +< break +< } +< switch v.Kind() { +< default: +< if fromQuoted { +< d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) +< } else { +< d.saveError(&UnmarshalTypeError{Value: "bool", Type: v.Type(), Offset: int64(d.readIndex())}) +< } +< case reflect.Bool: +< v.SetBool(value) +< case reflect.Interface: +< if v.NumMethod() == 0 { +< v.Set(reflect.ValueOf(value)) +< } else { +< d.saveError(&UnmarshalTypeError{Value: "bool", Type: v.Type(), Offset: int64(d.readIndex())}) +< } +< } +< +< case '"': // string +< s, ok := d.unquoteBytes(item) +< if !ok { +< if fromQuoted { +< return fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()) +< } +< panic(phasePanicMsg) +< } +< switch v.Kind() { +< default: +< d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.readIndex())}) +< case reflect.Slice: +< if v.Type().Elem().Kind() != reflect.Uint8 { +< d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.readIndex())}) +< break +< } +< b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) +< n, err := base64.StdEncoding.Decode(b, s) +< if err != nil { +< d.saveError(err) +< break +< } +< v.SetBytes(b[:n]) +< case reflect.String: +< if v.Type() == numberType && !isValidNumber(string(s)) { +< return fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", item) +< } +< v.SetString(string(s)) +< case reflect.Interface: +< if v.NumMethod() == 0 { +< v.Set(reflect.ValueOf(string(s))) +< } else { +< d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.readIndex())}) +< } +< } +< +< default: // number +< if c != '-' && (c < '0' || c > '9') { +< if fromQuoted { +< return fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()) +< } +< panic(phasePanicMsg) +< } +< s := string(item) +< switch v.Kind() { +< default: +< if v.Kind() == reflect.String && v.Type() == numberType { +< // s must be a valid number, because it's +< // already been tokenized. +< v.SetString(s) +< break +< } +< if fromQuoted { +< return fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()) +< } +< d.saveError(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.readIndex())}) +< case reflect.Interface: +< n, err := d.convertNumber(s) +< if err != nil { +< d.saveError(err) +< break +< } +< if v.NumMethod() != 0 { +< d.saveError(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.readIndex())}) +< break +< } +< v.Set(reflect.ValueOf(n)) +< +< case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: +< n, err := strconv.ParseInt(s, 10, 64) +< if err != nil || v.OverflowInt(n) { +< d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.readIndex())}) +< break +< } +< v.SetInt(n) +< +< case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: +< n, err := strconv.ParseUint(s, 10, 64) +< if err != nil || v.OverflowUint(n) { +< d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.readIndex())}) +< break +< } +< v.SetUint(n) +< +< case reflect.Float32, reflect.Float64: +< n, err := strconv.ParseFloat(s, v.Type().Bits()) +< if err != nil || v.OverflowFloat(n) { +< d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.readIndex())}) +< break +< } +< v.SetFloat(n) +< } +< } +< return nil +< } +< +< // The xxxInterface routines build up a value to be stored +< // in an empty interface. They are not strictly necessary, +< // but they avoid the weight of reflection in this common case. +< +< // valueInterface is like value but returns interface{} +< func (d *decodeState) valueInterface() (val interface{}) { +< switch d.opcode { +< default: +< panic(phasePanicMsg) +< case scanBeginArray: +< val = d.arrayInterface() +< d.scanNext() +< case scanBeginObject: +< val = d.objectInterface() +< d.scanNext() +< case scanBeginLiteral: +< val = d.literalInterface() +< } +< return +< } +< +< // arrayInterface is like array but returns []interface{}. +< func (d *decodeState) arrayInterface() []interface{} { +< var v = make([]interface{}, 0) +< for { +< // Look ahead for ] - can only happen on first iteration. +< d.scanWhile(scanSkipSpace) +< if d.opcode == scanEndArray { +< break +< } +< +< v = append(v, d.valueInterface()) +< +< // Next token must be , or ]. +< if d.opcode == scanSkipSpace { +< d.scanWhile(scanSkipSpace) +< } +< if d.opcode == scanEndArray { +< break +< } +< if d.opcode != scanArrayValue { +< panic(phasePanicMsg) +< } +< } +< return v +< } +< +< // objectInterface is like object but returns map[string]interface{}. +< func (d *decodeState) objectInterface() map[string]interface{} { +< m := make(map[string]interface{}) +< for { +< // Read opening " of string key or closing }. +< d.scanWhile(scanSkipSpace) +< if d.opcode == scanEndObject { +< // closing } - can only happen on first iteration. +< break +< } +< if d.opcode != scanBeginLiteral { +< panic(phasePanicMsg) +< } +< +< // Read string key. +< start := d.readIndex() +< d.rescanLiteral() +< item := d.data[start:d.readIndex()] +< key, ok := d.unquote(item) +< if !ok { +< panic(phasePanicMsg) +< } +< +< // Read : before value. +< if d.opcode == scanSkipSpace { +< d.scanWhile(scanSkipSpace) +< } +< if d.opcode != scanObjectKey { +< panic(phasePanicMsg) +< } +< d.scanWhile(scanSkipSpace) +< +< // Read value. +< m[key] = d.valueInterface() +< +< // Next token must be , or }. +< if d.opcode == scanSkipSpace { +< d.scanWhile(scanSkipSpace) +< } +< if d.opcode == scanEndObject { +< break +< } +< if d.opcode != scanObjectValue { +< panic(phasePanicMsg) +< } +< } +< return m +< } +< +< // literalInterface consumes and returns a literal from d.data[d.off-1:] and +< // it reads the following byte ahead. The first byte of the literal has been +< // read already (that's how the caller knows it's a literal). +< func (d *decodeState) literalInterface() interface{} { +< // All bytes inside literal return scanContinue op code. +< start := d.readIndex() +< d.rescanLiteral() +< +< item := d.data[start:d.readIndex()] +< +< switch c := item[0]; c { +< case 'n': // null +< return nil +< +< case 't', 'f': // true, false +< return c == 't' +< +< case '"': // string +< s, ok := d.unquote(item) +< if !ok { +< panic(phasePanicMsg) +< } +< return s +< +< default: // number +< if c != '-' && (c < '0' || c > '9') { +< panic(phasePanicMsg) +< } +< n, err := d.convertNumber(string(item)) +< if err != nil { +< d.saveError(err) +< } +< return n +< } +< } +< +< // getu4 decodes \uXXXX from the beginning of s, returning the hex value, +< // or it returns -1. +< func getu4(s []byte) rune { +< if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { +< return -1 +< } +< var r rune +< for _, c := range s[2:6] { +< switch { +< case '0' <= c && c <= '9': +< c = c - '0' +< case 'a' <= c && c <= 'f': +< c = c - 'a' + 10 +< case 'A' <= c && c <= 'F': +< c = c - 'A' + 10 +< default: +< return -1 +< } +< r = r*16 + rune(c) +< } +< return r +< } +< +< // unquote converts a quoted JSON string literal s into an actual string t. +< // The rules are different than for Go, so cannot use strconv.Unquote. +< // The first byte in s must be '"'. +< func (d *decodeState) unquote(s []byte) (t string, ok bool) { +< s, ok = d.unquoteBytes(s) +< t = string(s) +< return +< } +< +< func (d *decodeState) unquoteBytes(s []byte) (t []byte, ok bool) { +< // We already know that s[0] == '"'. However, we don't know that the +< // closing quote exists in all cases, such as when the string is nested +< // via the ",string" option. +< if len(s) < 2 || s[len(s)-1] != '"' { +< return +< } +< s = s[1 : len(s)-1] +< +< // If there are no unusual characters, no unquoting is needed, so return +< // a slice of the original bytes. +< r := d.safeUnquote +< if r == -1 { +< return s, true +< } +< +< b := make([]byte, len(s)+2*utf8.UTFMax) +< w := copy(b, s[0:r]) +< for r < len(s) { +< // Out of room? Can only happen if s is full of +< // malformed UTF-8 and we're replacing each +< // byte with RuneError. +< if w >= len(b)-2*utf8.UTFMax { +< nb := make([]byte, (len(b)+utf8.UTFMax)*2) +< copy(nb, b[0:w]) +< b = nb +< } +< switch c := s[r]; { +< case c == '\\': +< r++ +< if r >= len(s) { +< return +< } +< switch s[r] { +< default: +< return +< case '"', '\\', '/', '\'': +< b[w] = s[r] +< r++ +< w++ +< case 'b': +< b[w] = '\b' +< r++ +< w++ +< case 'f': +< b[w] = '\f' +< r++ +< w++ +< case 'n': +< b[w] = '\n' +< r++ +< w++ +< case 'r': +< b[w] = '\r' +< r++ +< w++ +< case 't': +< b[w] = '\t' +< r++ +< w++ +< case 'u': +< r-- +< rr := getu4(s[r:]) +< if rr < 0 { +< return +< } +< r += 6 +< if utf16.IsSurrogate(rr) { +< rr1 := getu4(s[r:]) +< if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { +< // A valid pair; consume. +< r += 6 +< w += utf8.EncodeRune(b[w:], dec) +< break +< } +< // Invalid surrogate; fall back to replacement rune. +< rr = unicode.ReplacementChar +< } +< w += utf8.EncodeRune(b[w:], rr) +< } +< +< // Quote, control characters are invalid. +< case c == '"', c < ' ': +< return +< +< // ASCII +< case c < utf8.RuneSelf: +< b[w] = c +< r++ +< w++ +< +< // Coerce to well-formed UTF-8. +< default: +< rr, size := utf8.DecodeRune(s[r:]) +< r += size +< w += utf8.EncodeRune(b[w:], rr) +< } +< } +< return b[0:w], true +< } +diff --no-dereference -N -r current/vendor/github.com/coreos/fcct/vendor/github.com/clarketm/json/encode.go updated/vendor/github.com/coreos/fcct/vendor/github.com/clarketm/json/encode.go +1,1374d0 +< // Copyright 2010 The Go Authors. All rights reserved. +< // Use of this source code is governed by a BSD-style +< // license that can be found in the LICENSE file. +< +< // Package json implements encoding and decoding of JSON as defined in +< // RFC 7159. The mapping between JSON and Go values is described +< // in the documentation for the Marshal and Unmarshal functions. +< // +< // See "JSON and Go" for an introduction to this package: +< // https://golang.org/doc/articles/json_and_go.html +< package json +< +< import ( +< "bytes" +< "encoding" +< "encoding/base64" +< "fmt" +< "math" +< "reflect" +< "sort" +< "strconv" +< "strings" +< "sync" +< "unicode" +< "unicode/utf8" +< ) +< +< // Marshal returns the JSON encoding of v. +< // +< // Marshal traverses the value v recursively. +< // If an encountered value implements the Marshaler interface +< // and is not a nil pointer, Marshal calls its MarshalJSON method +< // to produce JSON. If no MarshalJSON method is present but the +< // value implements encoding.TextMarshaler instead, Marshal calls +< // its MarshalText method and encodes the result as a JSON string. +< // The nil pointer exception is not strictly necessary +< // but mimics a similar, necessary exception in the behavior of +< // UnmarshalJSON. +< // +< // Otherwise, Marshal uses the following type-dependent default encodings: +< // +< // Boolean values encode as JSON booleans. +< // +< // Floating point, integer, and Number values encode as JSON numbers. +< // +< // String values encode as JSON strings coerced to valid UTF-8, +< // replacing invalid bytes with the Unicode replacement rune. +< // So that the JSON will be safe to embed inside HTML