summaryrefslogtreecommitdiff
path: root/vendor/github.com/coreos
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/coreos')
-rw-r--r--vendor/github.com/coreos/go-semver/LICENSE202
-rw-r--r--vendor/github.com/coreos/go-semver/README.md28
-rw-r--r--vendor/github.com/coreos/go-semver/semver/semver.go275
-rw-r--r--vendor/github.com/coreos/go-semver/semver/sort.go38
-rw-r--r--vendor/github.com/coreos/go-systemd/LICENSE191
-rw-r--r--vendor/github.com/coreos/go-systemd/README.md54
-rw-r--r--vendor/github.com/coreos/go-systemd/unit/deserialize.go276
-rw-r--r--vendor/github.com/coreos/go-systemd/unit/escape.go116
-rw-r--r--vendor/github.com/coreos/go-systemd/unit/option.go54
-rw-r--r--vendor/github.com/coreos/go-systemd/unit/serialize.go75
-rw-r--r--vendor/github.com/coreos/ignition/LICENSE202
-rw-r--r--vendor/github.com/coreos/ignition/NOTICE5
-rw-r--r--vendor/github.com/coreos/ignition/README.md12
-rw-r--r--vendor/github.com/coreos/ignition/config/types/config.go80
-rw-r--r--vendor/github.com/coreos/ignition/config/types/directory.go30
-rw-r--r--vendor/github.com/coreos/ignition/config/types/disk.go126
-rw-r--r--vendor/github.com/coreos/ignition/config/types/file.go62
-rw-r--r--vendor/github.com/coreos/ignition/config/types/filesystem.go74
-rw-r--r--vendor/github.com/coreos/ignition/config/types/ignition.go59
-rw-r--r--vendor/github.com/coreos/ignition/config/types/link.go35
-rw-r--r--vendor/github.com/coreos/ignition/config/types/mode.go30
-rw-r--r--vendor/github.com/coreos/ignition/config/types/node.go56
-rw-r--r--vendor/github.com/coreos/ignition/config/types/partition.go73
-rw-r--r--vendor/github.com/coreos/ignition/config/types/path.go31
-rw-r--r--vendor/github.com/coreos/ignition/config/types/raid.go58
-rw-r--r--vendor/github.com/coreos/ignition/config/types/schema.go200
-rw-r--r--vendor/github.com/coreos/ignition/config/types/unit.go109
-rw-r--r--vendor/github.com/coreos/ignition/config/types/url.go49
-rw-r--r--vendor/github.com/coreos/ignition/config/types/verification.go83
-rw-r--r--vendor/github.com/coreos/ignition/config/validate/report/report.go158
-rw-r--r--vendor/github.com/coreos/ignition/internal/exec/util/device_alias.go55
-rw-r--r--vendor/github.com/coreos/ignition/internal/exec/util/file.go235
-rw-r--r--vendor/github.com/coreos/ignition/internal/exec/util/passwd.go191
-rw-r--r--vendor/github.com/coreos/ignition/internal/exec/util/path.go31
-rw-r--r--vendor/github.com/coreos/ignition/internal/exec/util/unit.go80
-rw-r--r--vendor/github.com/coreos/ignition/internal/exec/util/user_lookup.c139
-rw-r--r--vendor/github.com/coreos/ignition/internal/exec/util/user_lookup.go50
-rw-r--r--vendor/github.com/coreos/ignition/internal/exec/util/user_lookup.h23
-rw-r--r--vendor/github.com/coreos/ignition/internal/exec/util/util.go32
-rw-r--r--vendor/github.com/coreos/ignition/internal/exec/util/verification.go81
40 files changed, 3758 insertions, 0 deletions
diff --git a/vendor/github.com/coreos/go-semver/LICENSE b/vendor/github.com/coreos/go-semver/LICENSE
new file mode 100644
index 00000000..d6456956
--- /dev/null
+++ b/vendor/github.com/coreos/go-semver/LICENSE
@@ -0,0 +1,202 @@
+
+ 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 --git a/vendor/github.com/coreos/go-semver/README.md b/vendor/github.com/coreos/go-semver/README.md
new file mode 100644
index 00000000..5bc9263c
--- /dev/null
+++ b/vendor/github.com/coreos/go-semver/README.md
@@ -0,0 +1,28 @@
+# go-semver - Semantic Versioning Library
+
+[![Build Status](https://travis-ci.org/coreos/go-semver.svg?branch=master)](https://travis-ci.org/coreos/go-semver)
+[![GoDoc](https://godoc.org/github.com/coreos/go-semver/semver?status.svg)](https://godoc.org/github.com/coreos/go-semver/semver)
+
+go-semver is a [semantic versioning][semver] library for Go. It lets you parse
+and compare two semantic version strings.
+
+[semver]: http://semver.org/
+
+## Usage
+
+```go
+vA := semver.New("1.2.3")
+vB := semver.New("3.2.1")
+
+fmt.Printf("%s < %s == %t\n", vA, vB, vA.LessThan(*vB))
+```
+
+## Example Application
+
+```
+$ go run example.go 1.2.3 3.2.1
+1.2.3 < 3.2.1 == true
+
+$ go run example.go 5.2.3 3.2.1
+5.2.3 < 3.2.1 == false
+```
diff --git a/vendor/github.com/coreos/go-semver/semver/semver.go b/vendor/github.com/coreos/go-semver/semver/semver.go
new file mode 100644
index 00000000..9c8072d1
--- /dev/null
+++ b/vendor/github.com/coreos/go-semver/semver/semver.go
@@ -0,0 +1,275 @@
+// Copyright 2013-2015 CoreOS, 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.
+
+// Semantic Versions http://semver.org
+package semver
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+type Version struct {
+ Major int64
+ Minor int64
+ Patch int64
+ PreRelease PreRelease
+ Metadata string
+}
+
+type PreRelease string
+
+func splitOff(input *string, delim string) (val string) {
+ parts := strings.SplitN(*input, delim, 2)
+
+ if len(parts) == 2 {
+ *input = parts[0]
+ val = parts[1]
+ }
+
+ return val
+}
+
+func New(version string) *Version {
+ return Must(NewVersion(version))
+}
+
+func NewVersion(version string) (*Version, error) {
+ v := Version{}
+
+ if err := v.Set(version); err != nil {
+ return nil, err
+ }
+
+ return &v, nil
+}
+
+// Must is a helper for wrapping NewVersion and will panic if err is not nil.
+func Must(v *Version, err error) *Version {
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// Set parses and updates v from the given version string. Implements flag.Value
+func (v *Version) Set(version string) error {
+ metadata := splitOff(&version, "+")
+ preRelease := PreRelease(splitOff(&version, "-"))
+ dotParts := strings.SplitN(version, ".", 3)
+
+ if len(dotParts) != 3 {
+ return fmt.Errorf("%s is not in dotted-tri format", version)
+ }
+
+ parsed := make([]int64, 3, 3)
+
+ for i, v := range dotParts[:3] {
+ val, err := strconv.ParseInt(v, 10, 64)
+ parsed[i] = val
+ if err != nil {
+ return err
+ }
+ }
+
+ v.Metadata = metadata
+ v.PreRelease = preRelease
+ v.Major = parsed[0]
+ v.Minor = parsed[1]
+ v.Patch = parsed[2]
+ return nil
+}
+
+func (v Version) String() string {
+ var buffer bytes.Buffer
+
+ fmt.Fprintf(&buffer, "%d.%d.%d", v.Major, v.Minor, v.Patch)
+
+ if v.PreRelease != "" {
+ fmt.Fprintf(&buffer, "-%s", v.PreRelease)
+ }
+
+ if v.Metadata != "" {
+ fmt.Fprintf(&buffer, "+%s", v.Metadata)
+ }
+
+ return buffer.String()
+}
+
+func (v *Version) UnmarshalYAML(unmarshal func(interface{}) error) error {
+ var data string
+ if err := unmarshal(&data); err != nil {
+ return err
+ }
+ return v.Set(data)
+}
+
+func (v Version) MarshalJSON() ([]byte, error) {
+ return []byte(`"` + v.String() + `"`), nil
+}
+
+func (v *Version) UnmarshalJSON(data []byte) error {
+ l := len(data)
+ if l == 0 || string(data) == `""` {
+ return nil
+ }
+ if l < 2 || data[0] != '"' || data[l-1] != '"' {
+ return errors.New("invalid semver string")
+ }
+ return v.Set(string(data[1 : l-1]))
+}
+
+// Compare tests if v is less than, equal to, or greater than versionB,
+// returning -1, 0, or +1 respectively.
+func (v Version) Compare(versionB Version) int {
+ if cmp := recursiveCompare(v.Slice(), versionB.Slice()); cmp != 0 {
+ return cmp
+ }
+ return preReleaseCompare(v, versionB)
+}
+
+// Equal tests if v is equal to versionB.
+func (v Version) Equal(versionB Version) bool {
+ return v.Compare(versionB) == 0
+}
+
+// LessThan tests if v is less than versionB.
+func (v Version) LessThan(versionB Version) bool {
+ return v.Compare(versionB) < 0
+}
+
+// Slice converts the comparable parts of the semver into a slice of integers.
+func (v Version) Slice() []int64 {
+ return []int64{v.Major, v.Minor, v.Patch}
+}
+
+func (p PreRelease) Slice() []string {
+ preRelease := string(p)
+ return strings.Split(preRelease, ".")
+}
+
+func preReleaseCompare(versionA Version, versionB Version) int {
+ a := versionA.PreRelease
+ b := versionB.PreRelease
+
+ /* Handle the case where if two versions are otherwise equal it is the
+ * one without a PreRelease that is greater */
+ if len(a) == 0 && (len(b) > 0) {
+ return 1
+ } else if len(b) == 0 && (len(a) > 0) {
+ return -1
+ }
+
+ // If there is a prerelease, check and compare each part.
+ return recursivePreReleaseCompare(a.Slice(), b.Slice())
+}
+
+func recursiveCompare(versionA []int64, versionB []int64) int {
+ if len(versionA) == 0 {
+ return 0
+ }
+
+ a := versionA[0]
+ b := versionB[0]
+
+ if a > b {
+ return 1
+ } else if a < b {
+ return -1
+ }
+
+ return recursiveCompare(versionA[1:], versionB[1:])
+}
+
+func recursivePreReleaseCompare(versionA []string, versionB []string) int {
+ // A larger set of pre-release fields has a higher precedence than a smaller set,
+ // if all of the preceding identifiers are equal.
+ if len(versionA) == 0 {
+ if len(versionB) > 0 {
+ return -1
+ }
+ return 0
+ } else if len(versionB) == 0 {
+ // We're longer than versionB so return 1.
+ return 1
+ }
+
+ a := versionA[0]
+ b := versionB[0]
+
+ aInt := false
+ bInt := false
+
+ aI, err := strconv.Atoi(versionA[0])
+ if err == nil {
+ aInt = true
+ }
+
+ bI, err := strconv.Atoi(versionB[0])
+ if err == nil {
+ bInt = true
+ }
+
+ // Numeric identifiers always have lower precedence than non-numeric identifiers.
+ if aInt && !bInt {
+ return -1
+ } else if !aInt && bInt {
+ return 1
+ }
+
+ // Handle Integer Comparison
+ if aInt && bInt {
+ if aI > bI {
+ return 1
+ } else if aI < bI {
+ return -1
+ }
+ }
+
+ // Handle String Comparison
+ if a > b {
+ return 1
+ } else if a < b {
+ return -1
+ }
+
+ return recursivePreReleaseCompare(versionA[1:], versionB[1:])
+}
+
+// BumpMajor increments the Major field by 1 and resets all other fields to their default values
+func (v *Version) BumpMajor() {
+ v.Major += 1
+ v.Minor = 0
+ v.Patch = 0
+ v.PreRelease = PreRelease("")
+ v.Metadata = ""
+}
+
+// BumpMinor increments the Minor field by 1 and resets all other fields to their default values
+func (v *Version) BumpMinor() {
+ v.Minor += 1
+ v.Patch = 0
+ v.PreRelease = PreRelease("")
+ v.Metadata = ""
+}
+
+// BumpPatch increments the Patch field by 1 and resets all other fields to their default values
+func (v *Version) BumpPatch() {
+ v.Patch += 1
+ v.PreRelease = PreRelease("")
+ v.Metadata = ""
+}
diff --git a/vendor/github.com/coreos/go-semver/semver/sort.go b/vendor/github.com/coreos/go-semver/semver/sort.go
new file mode 100644
index 00000000..e256b41a
--- /dev/null
+++ b/vendor/github.com/coreos/go-semver/semver/sort.go
@@ -0,0 +1,38 @@
+// Copyright 2013-2015 CoreOS, 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 semver
+
+import (
+ "sort"
+)
+
+type Versions []*Version
+
+func (s Versions) Len() int {
+ return len(s)
+}
+
+func (s Versions) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+}
+
+func (s Versions) Less(i, j int) bool {
+ return s[i].LessThan(*s[j])
+}
+
+// Sort sorts the given slice of Version
+func Sort(versions []*Version) {
+ sort.Sort(Versions(versions))
+}
diff --git a/vendor/github.com/coreos/go-systemd/LICENSE b/vendor/github.com/coreos/go-systemd/LICENSE
new file mode 100644
index 00000000..37ec93a1
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/LICENSE
@@ -0,0 +1,191 @@
+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:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+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
+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 --git a/vendor/github.com/coreos/go-systemd/README.md b/vendor/github.com/coreos/go-systemd/README.md
new file mode 100644
index 00000000..cb87a112
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/README.md
@@ -0,0 +1,54 @@
+# go-systemd
+
+[![Build Status](https://travis-ci.org/coreos/go-systemd.png?branch=master)](https://travis-ci.org/coreos/go-systemd)
+[![godoc](https://godoc.org/github.com/coreos/go-systemd?status.svg)](http://godoc.org/github.com/coreos/go-systemd)
+
+Go bindings to systemd. The project has several packages:
+
+- `activation` - for writing and using socket activation from Go
+- `dbus` - for starting/stopping/inspecting running services and units
+- `journal` - for writing to systemd's logging service, journald
+- `sdjournal` - for reading from journald by wrapping its C API
+- `machine1` - for registering machines/containers with systemd
+- `unit` - for (de)serialization and comparison of unit files
+
+## Socket Activation
+
+An example HTTP server using socket activation can be quickly set up by following this README on a Linux machine running systemd:
+
+https://github.com/coreos/go-systemd/tree/master/examples/activation/httpserver
+
+## Journal
+
+Using the pure-Go `journal` package you can submit journal entries directly to systemd's journal, taking advantage of features like indexed key/value pairs for each log entry.
+The `sdjournal` package provides read access to the journal by wrapping around journald's native C API; consequently it requires cgo and the journal headers to be available.
+
+## D-Bus
+
+The `dbus` package connects to the [systemd D-Bus API](http://www.freedesktop.org/wiki/Software/systemd/dbus/) and lets you start, stop and introspect systemd units. The API docs are here:
+
+http://godoc.org/github.com/coreos/go-systemd/dbus
+
+### Debugging
+
+Create `/etc/dbus-1/system-local.conf` that looks like this:
+
+```
+<!DOCTYPE busconfig PUBLIC
+"-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <policy user="root">
+ <allow eavesdrop="true"/>
+ <allow eavesdrop="true" send_destination="*"/>
+ </policy>
+</busconfig>
+```
+
+## machined
+
+The `machine1` package allows interaction with the [systemd machined D-Bus API](http://www.freedesktop.org/wiki/Software/systemd/machined/).
+
+## Units
+
+The `unit` package provides various functions for working with [systemd unit files](http://www.freedesktop.org/software/systemd/man/systemd.unit.html).
diff --git a/vendor/github.com/coreos/go-systemd/unit/deserialize.go b/vendor/github.com/coreos/go-systemd/unit/deserialize.go
new file mode 100644
index 00000000..8a88162f
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/unit/deserialize.go
@@ -0,0 +1,276 @@
+// Copyright 2015 CoreOS, 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 unit
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "strings"
+ "unicode"
+)
+
+const (
+ // SYSTEMD_LINE_MAX mimics the maximum line length that systemd can use.
+ // On typical systemd platforms (i.e. modern Linux), this will most
+ // commonly be 2048, so let's use that as a sanity check.
+ // Technically, we should probably pull this at runtime:
+ // SYSTEMD_LINE_MAX = int(C.sysconf(C.__SC_LINE_MAX))
+ // but this would introduce an (unfortunate) dependency on cgo
+ SYSTEMD_LINE_MAX = 2048
+
+ // characters that systemd considers indicate a newline
+ SYSTEMD_NEWLINE = "\r\n"
+)
+
+var (
+ ErrLineTooLong = fmt.Errorf("line too long (max %d bytes)", SYSTEMD_LINE_MAX)
+)
+
+// Deserialize parses a systemd unit file into a list of UnitOption objects.
+func Deserialize(f io.Reader) (opts []*UnitOption, err error) {
+ lexer, optchan, errchan := newLexer(f)
+ go lexer.lex()
+
+ for opt := range optchan {
+ opts = append(opts, &(*opt))
+ }
+
+ err = <-errchan
+ return opts, err
+}
+
+func newLexer(f io.Reader) (*lexer, <-chan *UnitOption, <-chan error) {
+ optchan := make(chan *UnitOption)
+ errchan := make(chan error, 1)
+ buf := bufio.NewReader(f)
+
+ return &lexer{buf, optchan, errchan, ""}, optchan, errchan
+}
+
+type lexer struct {
+ buf *bufio.Reader
+ optchan chan *UnitOption
+ errchan chan error
+ section string
+}
+
+func (l *lexer) lex() {
+ var err error
+ defer func() {
+ close(l.optchan)
+ close(l.errchan)
+ }()
+ next := l.lexNextSection
+ for next != nil {
+ if l.buf.Buffered() >= SYSTEMD_LINE_MAX {
+ // systemd truncates lines longer than LINE_MAX
+ // https://bugs.freedesktop.org/show_bug.cgi?id=85308
+ // Rather than allowing this to pass silently, let's
+ // explicitly gate people from encountering this
+ line, err := l.buf.Peek(SYSTEMD_LINE_MAX)
+ if err != nil {
+ l.errchan <- err
+ return
+ }
+ if bytes.IndexAny(line, SYSTEMD_NEWLINE) == -1 {
+ l.errchan <- ErrLineTooLong
+ return
+ }
+ }
+
+ next, err = next()
+ if err != nil {
+ l.errchan <- err
+ return
+ }
+ }
+}
+
+type lexStep func() (lexStep, error)
+
+func (l *lexer) lexSectionName() (lexStep, error) {
+ sec, err := l.buf.ReadBytes(']')
+ if err != nil {
+ return nil, errors.New("unable to find end of section")
+ }
+
+ return l.lexSectionSuffixFunc(string(sec[:len(sec)-1])), nil
+}
+
+func (l *lexer) lexSectionSuffixFunc(section string) lexStep {
+ return func() (lexStep, error) {
+ garbage, _, err := l.toEOL()
+ if err != nil {
+ return nil, err
+ }
+
+ garbage = bytes.TrimSpace(garbage)
+ if len(garbage) > 0 {
+ return nil, fmt.Errorf("found garbage after section name %s: %v", l.section, garbage)
+ }
+
+ return l.lexNextSectionOrOptionFunc(section), nil
+ }
+}
+
+func (l *lexer) ignoreLineFunc(next lexStep) lexStep {
+ return func() (lexStep, error) {
+ for {
+ line, _, err := l.toEOL()
+ if err != nil {
+ return nil, err
+ }
+
+ line = bytes.TrimSuffix(line, []byte{' '})
+
+ // lack of continuation means this line has been exhausted
+ if !bytes.HasSuffix(line, []byte{'\\'}) {
+ break
+ }
+ }
+
+ // reached end of buffer, safe to exit
+ return next, nil
+ }
+}
+
+func (l *lexer) lexNextSection() (lexStep, error) {
+ r, _, err := l.buf.ReadRune()
+ if err != nil {
+ if err == io.EOF {
+ err = nil
+ }
+ return nil, err
+ }
+
+ if r == '[' {
+ return l.lexSectionName, nil
+ } else if isComment(r) {
+ return l.ignoreLineFunc(l.lexNextSection), nil
+ }
+
+ return l.lexNextSection, nil
+}
+
+func (l *lexer) lexNextSectionOrOptionFunc(section string) lexStep {
+ return func() (lexStep, error) {
+ r, _, err := l.buf.ReadRune()
+ if err != nil {
+ if err == io.EOF {
+ err = nil
+ }
+ return nil, err
+ }
+
+ if unicode.IsSpace(r) {
+ return l.lexNextSectionOrOptionFunc(section), nil
+ } else if r == '[' {
+ return l.lexSectionName, nil
+ } else if isComment(r) {
+ return l.ignoreLineFunc(l.lexNextSectionOrOptionFunc(section)), nil
+ }
+
+ l.buf.UnreadRune()
+ return l.lexOptionNameFunc(section), nil
+ }
+}
+
+func (l *lexer) lexOptionNameFunc(section string) lexStep {
+ return func() (lexStep, error) {
+ var partial bytes.Buffer
+ for {
+ r, _, err := l.buf.ReadRune()
+ if err != nil {
+ return nil, err
+ }
+
+ if r == '\n' || r == '\r' {
+ return nil, errors.New("unexpected newline encountered while parsing option name")
+ }
+
+ if r == '=' {
+ break
+ }
+
+ partial.WriteRune(r)
+ }
+
+ name := strings.TrimSpace(partial.String())
+ return l.lexOptionValueFunc(section, name, bytes.Buffer{}), nil
+ }
+}
+
+func (l *lexer) lexOptionValueFunc(section, name string, partial bytes.Buffer) lexStep {
+ return func() (lexStep, error) {
+ for {
+ line, eof, err := l.toEOL()
+ if err != nil {
+ return nil, err
+ }
+
+ if len(bytes.TrimSpace(line)) == 0 {
+ break
+ }
+
+ partial.Write(line)
+
+ // lack of continuation means this value has been exhausted
+ idx := bytes.LastIndex(line, []byte{'\\'})
+ if idx == -1 || idx != (len(line)-1) {
+ break
+ }
+
+ if !eof {
+ partial.WriteRune('\n')
+ }
+
+ return l.lexOptionValueFunc(section, name, partial), nil
+ }
+
+ val := partial.String()
+ if strings.HasSuffix(val, "\n") {
+ // A newline was added to the end, so the file didn't end with a backslash.
+ // => Keep the newline
+ val = strings.TrimSpace(val) + "\n"
+ } else {
+ val = strings.TrimSpace(val)
+ }
+ l.optchan <- &UnitOption{Section: section, Name: name, Value: val}
+
+ return l.lexNextSectionOrOptionFunc(section), nil
+ }
+}
+
+// toEOL reads until the end-of-line or end-of-file.
+// Returns (data, EOFfound, error)
+func (l *lexer) toEOL() ([]byte, bool, error) {
+ line, err := l.buf.ReadBytes('\n')
+ // ignore EOF here since it's roughly equivalent to EOL
+ if err != nil && err != io.EOF {
+ return nil, false, err
+ }
+
+ line = bytes.TrimSuffix(line, []byte{'\r'})
+ line = bytes.TrimSuffix(line, []byte{'\n'})
+
+ return line, err == io.EOF, nil
+}
+
+func isComment(r rune) bool {
+ return r == '#' || r == ';'
+}
diff --git a/vendor/github.com/coreos/go-systemd/unit/escape.go b/vendor/github.com/coreos/go-systemd/unit/escape.go
new file mode 100644
index 00000000..63b11726
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/unit/escape.go
@@ -0,0 +1,116 @@
+// Copyright 2015 CoreOS, 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.
+
+// Implements systemd-escape [--unescape] [--path]
+
+package unit
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+const (
+ allowed = `:_.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`
+)
+
+// If isPath is true:
+// We remove redundant '/'s, the leading '/', and trailing '/'.
+// If the result is empty, a '/' is inserted.
+//
+// We always:
+// Replace the following characters with `\x%x`:
+// Leading `.`
+// `-`, `\`, and anything not in this set: `:-_.\[0-9a-zA-Z]`
+// Replace '/' with '-'.
+func escape(unescaped string, isPath bool) string {
+ e := []byte{}
+ inSlashes := false
+ start := true
+ for i := 0; i < len(unescaped); i++ {
+ c := unescaped[i]
+ if isPath {
+ if c == '/' {
+ inSlashes = true
+ continue
+ } else if inSlashes {
+ inSlashes = false
+ if !start {
+ e = append(e, '-')
+ }
+ }
+ }
+
+ if c == '/' {
+ e = append(e, '-')
+ } else if start && c == '.' || strings.IndexByte(allowed, c) == -1 {
+ e = append(e, []byte(fmt.Sprintf(`\x%x`, c))...)
+ } else {
+ e = append(e, c)
+ }
+ start = false
+ }
+ if isPath && len(e) == 0 {
+ e = append(e, '-')
+ }
+ return string(e)
+}
+
+// If isPath is true:
+// We always return a string beginning with '/'.
+//
+// We always:
+// Replace '-' with '/'.
+// Replace `\x%x` with the value represented in hex.
+func unescape(escaped string, isPath bool) string {
+ u := []byte{}
+ for i := 0; i < len(escaped); i++ {
+ c := escaped[i]
+ if c == '-' {
+ c = '/'
+ } else if c == '\\' && len(escaped)-i >= 4 && escaped[i+1] == 'x' {
+ n, err := strconv.ParseInt(escaped[i+2:i+4], 16, 8)
+ if err == nil {
+ c = byte(n)
+ i += 3
+ }
+ }
+ u = append(u, c)
+ }
+ if isPath && (len(u) == 0 || u[0] != '/') {
+ u = append([]byte("/"), u...)
+ }
+ return string(u)
+}
+
+// UnitNameEscape escapes a string as `systemd-escape` would
+func UnitNameEscape(unescaped string) string {
+ return escape(unescaped, false)
+}
+
+// UnitNameUnescape unescapes a string as `systemd-escape --unescape` would
+func UnitNameUnescape(escaped string) string {
+ return unescape(escaped, false)
+}
+
+// UnitNamePathEscape escapes a string as `systemd-escape --path` would
+func UnitNamePathEscape(unescaped string) string {
+ return escape(unescaped, true)
+}
+
+// UnitNamePathUnescape unescapes a string as `systemd-escape --path --unescape` would
+func UnitNamePathUnescape(escaped string) string {
+ return unescape(escaped, true)
+}
diff --git a/vendor/github.com/coreos/go-systemd/unit/option.go b/vendor/github.com/coreos/go-systemd/unit/option.go
new file mode 100644
index 00000000..e5d21e19
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/unit/option.go
@@ -0,0 +1,54 @@
+// Copyright 2015 CoreOS, 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 unit
+
+import (
+ "fmt"
+)
+
+type UnitOption struct {
+ Section string
+ Name string
+ Value string
+}
+
+func NewUnitOption(section, name, value string) *UnitOption {
+ return &UnitOption{Section: section, Name: name, Value: value}
+}
+
+func (uo *UnitOption) String() string {
+ return fmt.Sprintf("{Section: %q, Name: %q, Value: %q}", uo.Section, uo.Name, uo.Value)
+}
+
+func (uo *UnitOption) Match(other *UnitOption) bool {
+ return uo.Section == other.Section &&
+ uo.Name == other.Name &&
+ uo.Value == other.Value
+}
+
+func AllMatch(u1 []*UnitOption, u2 []*UnitOption) bool {
+ length := len(u1)
+ if length != len(u2) {
+ return false
+ }
+
+ for i := 0; i < length; i++ {
+ if !u1[i].Match(u2[i]) {
+ return false
+ }
+ }
+
+ return true
+}
diff --git a/vendor/github.com/coreos/go-systemd/unit/serialize.go b/vendor/github.com/coreos/go-systemd/unit/serialize.go
new file mode 100644
index 00000000..e07799ca
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/unit/serialize.go
@@ -0,0 +1,75 @@
+// Copyright 2015 CoreOS, 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 unit
+
+import (
+ "bytes"
+ "io"
+)
+
+// Serialize encodes all of the given UnitOption objects into a
+// unit file. When serialized the options are sorted in their
+// supplied order but grouped by section.
+func Serialize(opts []*UnitOption) io.Reader {
+ var buf bytes.Buffer
+
+ if len(opts) == 0 {
+ return &buf
+ }
+
+ // Index of sections -> ordered options
+ idx := map[string][]*UnitOption{}
+ // Separately preserve order in which sections were seen
+ sections := []string{}
+ for _, opt := range opts {
+ sec := opt.Section
+ if _, ok := idx[sec]; !ok {
+ sections = append(sections, sec)
+ }
+ idx[sec] = append(idx[sec], opt)
+ }
+
+ for i, sect := range sections {
+ writeSectionHeader(&buf, sect)
+ writeNewline(&buf)
+
+ opts := idx[sect]
+ for _, opt := range opts {
+ writeOption(&buf, opt)
+ writeNewline(&buf)
+ }
+ if i < len(sections)-1 {
+ writeNewline(&buf)
+ }
+ }
+
+ return &buf
+}
+
+func writeNewline(buf *bytes.Buffer) {
+ buf.WriteRune('\n')
+}
+
+func writeSectionHeader(buf *bytes.Buffer, section string) {
+ buf.WriteRune('[')
+ buf.WriteString(section)
+ buf.WriteRune(']')
+}
+
+func writeOption(buf *bytes.Buffer, opt *UnitOption) {
+ buf.WriteString(opt.Name)
+ buf.WriteRune('=')
+ buf.WriteString(opt.Value)
+}
diff --git a/vendor/github.com/coreos/ignition/LICENSE b/vendor/github.com/coreos/ignition/LICENSE
new file mode 100644
index 00000000..e06d2081
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/LICENSE
@@ -0,0 +1,202 @@
+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 --git a/vendor/github.com/coreos/ignition/NOTICE b/vendor/github.com/coreos/ignition/NOTICE
new file mode 100644
index 00000000..e520005c
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/NOTICE
@@ -0,0 +1,5 @@
+CoreOS Project
+Copyright 2015 CoreOS, Inc
+
+This product includes software developed at CoreOS, Inc.
+(http://www.coreos.com/).
diff --git a/vendor/github.com/coreos/ignition/README.md b/vendor/github.com/coreos/ignition/README.md
new file mode 100644
index 00000000..1c96889d
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/README.md
@@ -0,0 +1,12 @@
+# Ignition #
+
+Ignition is the utility used by CoreOS Container Linux to manipulate disks during the initramfs. This includes partitioning disks, formatting partitions, writing files (regular files, systemd units, networkd units, etc.), and configuring users. On first boot, Ignition reads its configuration from a source of truth (remote URL, network metadata service, hypervisor bridge, etc.) and applies the configuration.
+
+## Usage ##
+
+Odds are good that you don't want to invoke Ignition directly. In fact, it isn't even present in the Container Linux root filesystem. Take a look at the [Getting Started Guide][getting started] for details on providing Ignition with a runtime configuration.
+
+Use the [bug tracker][issues] to report bugs.
+
+[getting started]: doc/getting-started.md
+[issues]: https://github.com/coreos/bugs/issues/new?labels=component/ignition
diff --git a/vendor/github.com/coreos/ignition/config/types/config.go b/vendor/github.com/coreos/ignition/config/types/config.go
new file mode 100644
index 00000000..dc1fe3b3
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/config/types/config.go
@@ -0,0 +1,80 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+ "fmt"
+
+ "github.com/coreos/go-semver/semver"
+
+ "github.com/coreos/ignition/config/validate/report"
+)
+
+var (
+ MaxVersion = semver.Version{
+ Major: 2,
+ Minor: 1,
+ PreRelease: "experimental",
+ }
+)
+
+func (c Config) Validate() report.Report {
+ r := report.Report{}
+ rules := []rule{
+ checkFilesFilesystems,
+ checkDuplicateFilesystems,
+ }
+
+ for _, rule := range rules {
+ rule(c, &r)
+ }
+ return r
+}
+
+type rule func(cfg Config, report *report.Report)
+
+func checkFilesFilesystems(cfg Config, r *report.Report) {
+ filesystems := map[string]struct{}{"root": {}}
+ for _, filesystem := range cfg.Storage.Filesystems {
+ filesystems[filesystem.Name] = struct{}{}
+ }
+ for _, file := range cfg.Storage.Files {
+ if file.Filesystem == "" {
+ // Filesystem was not specified. This is an error, but its handled in types.File's Validate, not here
+ continue
+ }
+ _, ok := filesystems[file.Filesystem]
+ if !ok {
+ r.Add(report.Entry{
+ Kind: report.EntryWarning,
+ Message: fmt.Sprintf("File %q references nonexistent filesystem %q. (This is ok if it is defined in a referenced config)",
+ file.Path, file.Filesystem),
+ })
+ }
+ }
+}
+
+func checkDuplicateFilesystems(cfg Config, r *report.Report) {
+ filesystems := map[string]struct{}{"root": {}}
+ for _, filesystem := range cfg.Storage.Filesystems {
+ if _, ok := filesystems[filesystem.Name]; ok {
+ r.Add(report.Entry{
+ Kind: report.EntryWarning,
+ Message: fmt.Sprintf("Filesystem %q shadows exising filesystem definition", filesystem.Name),
+ })
+ }
+ filesystems[filesystem.Name] = struct{}{}
+ }
+}
diff --git a/vendor/github.com/coreos/ignition/config/types/directory.go b/vendor/github.com/coreos/ignition/config/types/directory.go
new file mode 100644
index 00000000..16adad05
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/config/types/directory.go
@@ -0,0 +1,30 @@
+// Copyright 2017 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+ "github.com/coreos/ignition/config/validate/report"
+)
+
+func (d Directory) ValidateMode() report.Report {
+ r := report.Report{}
+ if err := validateMode(d.Mode); err != nil {
+ r.Add(report.Entry{
+ Message: err.Error(),
+ Kind: report.EntryError,
+ })
+ }
+ return r
+}
diff --git a/vendor/github.com/coreos/ignition/config/types/disk.go b/vendor/github.com/coreos/ignition/config/types/disk.go
new file mode 100644
index 00000000..21273a1c
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/config/types/disk.go
@@ -0,0 +1,126 @@
+// Copyright 2016 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+ "fmt"
+
+ "github.com/coreos/ignition/config/validate/report"
+)
+
+func (n Disk) Validate() report.Report {
+ return report.Report{}
+}
+
+func (n Disk) ValidateDevice() report.Report {
+ if len(n.Device) == 0 {
+ return report.ReportFromError(fmt.Errorf("disk device is required"), report.EntryError)
+ }
+ if err := validatePath(string(n.Device)); err != nil {
+ return report.ReportFromError(err, report.EntryError)
+ }
+ return report.Report{}
+}
+
+func (n Disk) ValidatePartitions() report.Report {
+ r := report.Report{}
+ if n.partitionNumbersCollide() {
+ r.Add(report.Entry{
+ Message: fmt.Sprintf("disk %q: partition numbers collide", n.Device),
+ Kind: report.EntryError,
+ })
+ }
+ if n.partitionsOverlap() {
+ r.Add(report.Entry{
+ Message: fmt.Sprintf("disk %q: partitions overlap", n.Device),
+ Kind: report.EntryError,
+ })
+ }
+ if n.partitionsMisaligned() {
+ r.Add(report.Entry{
+ Message: fmt.Sprintf("disk %q: partitions misaligned", n.Device),
+ Kind: report.EntryError,
+ })
+ }
+ // Disks which have no errors at this point will likely succeed in sgdisk
+ return r
+}
+
+// partitionNumbersCollide returns true if partition numbers in n.Partitions are not unique.
+func (n Disk) partitionNumbersCollide() bool {
+ m := map[int][]Partition{}
+ for _, p := range n.Partitions {
+ m[p.Number] = append(m[p.Number], p)
+ }
+ for _, n := range m {
+ if len(n) > 1 {
+ // TODO(vc): return information describing the collision for logging
+ return true
+ }
+ }
+ return false
+}
+
+// end returns the last sector of a partition.
+func (p Partition) end() int {
+ if p.Size == 0 {
+ // a size of 0 means "fill available", just return the start as the end for those.
+ return p.Start
+ }
+ return p.Start + p.Size - 1
+}
+
+// partitionsOverlap returns true if any explicitly dimensioned partitions overlap
+func (n Disk) partitionsOverlap() bool {
+ for _, p := range n.Partitions {
+ // Starts of 0 are placed by sgdisk into the "largest available block" at that time.
+ // We aren't going to check those for overlap since we don't have the disk geometry.
+ if p.Start == 0 {
+ continue
+ }
+
+ for _, o := range n.Partitions {
+ if p == o || o.Start == 0 {
+ continue
+ }
+
+ // is p.Start within o?
+ if p.Start >= o.Start && p.Start <= o.end() {
+ return true
+ }
+
+ // is p.end() within o?
+ if p.end() >= o.Start && p.end() <= o.end() {
+ return true
+ }
+
+ // do p.Start and p.end() straddle o?
+ if p.Start < o.Start && p.end() > o.end() {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// partitionsMisaligned returns true if any of the partitions don't start on a 2048-sector (1MiB) boundary.
+func (n Disk) partitionsMisaligned() bool {
+ for _, p := range n.Partitions {
+ if (p.Start & (2048 - 1)) != 0 {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/coreos/ignition/config/types/file.go b/vendor/github.com/coreos/ignition/config/types/file.go
new file mode 100644
index 00000000..866fc054
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/config/types/file.go
@@ -0,0 +1,62 @@
+// Copyright 2016 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/coreos/ignition/config/validate/report"
+)
+
+var (
+ ErrCompressionInvalid = errors.New("invalid compression method")
+)
+
+func (f File) ValidateMode() report.Report {
+ r := report.Report{}
+ if err := validateMode(f.Mode); err != nil {
+ r.Add(report.Entry{
+ Message: err.Error(),
+ Kind: report.EntryError,
+ })
+ }
+ return r
+}
+
+func (fc FileContents) ValidateCompression() report.Report {
+ r := report.Report{}
+ switch fc.Compression {
+ case "", "gzip":
+ default:
+ r.Add(report.Entry{
+ Message: ErrCompressionInvalid.Error(),
+ Kind: report.EntryError,
+ })
+ }
+ return r
+}
+
+func (fc FileContents) ValidateSource() report.Report {
+ r := report.Report{}
+ err := validateURL(fc.Source)
+ if err != nil {
+ r.Add(report.Entry{
+ Message: fmt.Sprintf("invalid url %q: %v", fc.Source, err),
+ Kind: report.EntryError,
+ })
+ }
+ return r
+}
diff --git a/vendor/github.com/coreos/ignition/config/types/filesystem.go b/vendor/github.com/coreos/ignition/config/types/filesystem.go
new file mode 100644
index 00000000..0b17121a
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/config/types/filesystem.go
@@ -0,0 +1,74 @@
+// Copyright 2016 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/coreos/ignition/config/validate/report"
+)
+
+var (
+ ErrFilesystemInvalidFormat = errors.New("invalid filesystem format")
+ ErrFilesystemNoMountPath = errors.New("filesystem is missing mount or path")
+ ErrFilesystemMountAndPath = errors.New("filesystem has both mount and path defined")
+)
+
+func (f Filesystem) Validate() report.Report {
+ r := report.Report{}
+ if f.Mount == nil && f.Path == nil {
+ return report.ReportFromError(ErrFilesystemNoMountPath, report.EntryError)
+ }
+ if f.Mount != nil && f.Path != nil {
+ return report.ReportFromError(ErrFilesystemMountAndPath, report.EntryError)
+ }
+ return r
+}
+
+func (f Filesystem) ValidatePath() report.Report {
+ r := report.Report{}
+ if f.Path != nil && validatePath(*f.Path) != nil {
+ r.Add(report.Entry{
+ Message: fmt.Sprintf("filesystem %q: path not absolute", f.Name),
+ Kind: report.EntryError,
+ })
+ }
+ return r
+}
+
+func (m Mount) Validate() report.Report {
+ r := report.Report{}
+ switch m.Format {
+ case "ext4", "btrfs", "xfs", "swap":
+ default:
+ r.Add(report.Entry{
+ Message: ErrFilesystemInvalidFormat.Error(),
+ Kind: report.EntryError,
+ })
+ }
+ return r
+}
+
+func (m Mount) ValidateDevice() report.Report {
+ r := report.Report{}
+ if err := validatePath(m.Device); err != nil {
+ r.Add(report.Entry{
+ Message: err.Error(),
+ Kind: report.EntryError,
+ })
+ }
+ return r
+}
diff --git a/vendor/github.com/coreos/ignition/config/types/ignition.go b/vendor/github.com/coreos/ignition/config/types/ignition.go
new file mode 100644
index 00000000..661b898d
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/config/types/ignition.go
@@ -0,0 +1,59 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+ "errors"
+
+ "github.com/coreos/go-semver/semver"
+
+ "github.com/coreos/ignition/config/validate/report"
+)
+
+var (
+ ErrOldVersion = errors.New("incorrect config version (too old)")
+ ErrNewVersion = errors.New("incorrect config version (too new)")
+ ErrInvalidVersion = errors.New("invalid config version (couldn't parse)")
+)
+
+func (c ConfigReference) ValidateSource() report.Report {
+ r := report.Report{}
+ err := validateURL(c.Source)
+ if err != nil {
+ r.Add(report.Entry{
+ Message: err.Error(),
+ Kind: report.EntryError,
+ })
+ }
+ return r
+}
+
+func (v Ignition) Semver() (*semver.Version, error) {
+ return semver.NewVersion(v.Version)
+}
+
+func (v Ignition) Validate() report.Report {
+ tv, err := v.Semver()
+ if err != nil {
+ return report.ReportFromError(ErrInvalidVersion, report.EntryError)
+ }
+ if MaxVersion.Major > tv.Major {
+ return report.ReportFromError(ErrOldVersion, report.EntryError)
+ }
+ if MaxVersion.LessThan(*tv) {
+ return report.ReportFromError(ErrNewVersion, report.EntryError)
+ }
+ return report.Report{}
+}
diff --git a/vendor/github.com/coreos/ignition/config/types/link.go b/vendor/github.com/coreos/ignition/config/types/link.go
new file mode 100644
index 00000000..1b7794c0
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/config/types/link.go
@@ -0,0 +1,35 @@
+// Copyright 2017 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+ "fmt"
+
+ "github.com/coreos/ignition/config/validate/report"
+)
+
+func (s Link) Validate() report.Report {
+ r := report.Report{}
+ if !s.Hard {
+ err := validatePath(s.Target)
+ if err != nil {
+ r.Add(report.Entry{
+ Message: fmt.Sprintf("problem with target path %q: %v", s.Target, err),
+ Kind: report.EntryError,
+ })
+ }
+ }
+ return r
+}
diff --git a/vendor/github.com/coreos/ignition/config/types/mode.go b/vendor/github.com/coreos/ignition/config/types/mode.go
new file mode 100644
index 00000000..09608b51
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/config/types/mode.go
@@ -0,0 +1,30 @@
+// Copyright 2017 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+ "errors"
+)
+
+var (
+ ErrFileIllegalMode = errors.New("illegal file mode")
+)
+
+func validateMode(m int) error {
+ if m < 0 || m > 07777 {
+ return ErrFileIllegalMode
+ }
+ return nil
+}
diff --git a/vendor/github.com/coreos/ignition/config/types/node.go b/vendor/github.com/coreos/ignition/config/types/node.go
new file mode 100644
index 00000000..88ffca8a
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/config/types/node.go
@@ -0,0 +1,56 @@
+// Copyright 2016 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+ "errors"
+ "path/filepath"
+
+ "github.com/coreos/ignition/config/validate/report"
+)
+
+var (
+ ErrNoFilesystem = errors.New("no filesystem specified")
+)
+
+func (n Node) ValidateFilesystem() report.Report {
+ r := report.Report{}
+ if n.Filesystem == "" {
+ r.Add(report.Entry{
+ Message: ErrNoFilesystem.Error(),
+ Kind: report.EntryError,
+ })
+ }
+ return r
+}
+
+func (n Node) ValidatePath() report.Report {
+ r := report.Report{}
+ if err := validatePath(n.Path); err != nil {
+ r.Add(report.Entry{
+ Message: err.Error(),
+ Kind: report.EntryError,
+ })
+ }
+ return r
+}
+
+func (n Node) Depth() int {
+ count := 0
+ for p := filepath.Clean(string(n.Path)); p != "/"; count++ {
+ p = filepath.Dir(p)
+ }
+ return count
+}
diff --git a/vendor/github.com/coreos/ignition/config/types/partition.go b/vendor/github.com/coreos/ignition/config/types/partition.go
new file mode 100644
index 00000000..4473b667
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/config/types/partition.go
@@ -0,0 +1,73 @@
+// Copyright 2016 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+ "errors"
+ "fmt"
+ "regexp"
+
+ "github.com/coreos/ignition/config/validate/report"
+)
+
+const (
+ guidRegexStr = "^(|[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12})$"
+)
+
+var (
+ ErrLabelTooLong = errors.New("partition labels may not exceed 36 characters")
+ ErrDoesntMatchGUIDRegex = errors.New("doesn't match the form \"01234567-89AB-CDEF-EDCB-A98765432101\"")
+)
+
+func (p Partition) ValidateLabel() report.Report {
+ r := report.Report{}
+ // http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_entries:
+ // 56 (0x38) 72 bytes Partition name (36 UTF-16LE code units)
+
+ // XXX(vc): note GPT calls it a name, we're using label for consistency
+ // with udev naming /dev/disk/by-partlabel/*.
+ if len(p.Label) > 36 {
+ r.Add(report.Entry{
+ Message: ErrLabelTooLong.Error(),
+ Kind: report.EntryError,
+ })
+ }
+ return r
+}
+
+func (p Partition) ValidateTypeGUID() report.Report {
+ return validateGUID(p.TypeGUID)
+}
+
+func (p Partition) ValidateGUID() report.Report {
+ return validateGUID(p.GUID)
+}
+
+func validateGUID(guid string) report.Report {
+ r := report.Report{}
+ ok, err := regexp.MatchString(guidRegexStr, guid)
+ if err != nil {
+ r.Add(report.Entry{
+ Message: fmt.Sprintf("error matching guid regexp: %v", err),
+ Kind: report.EntryError,
+ })
+ } else if !ok {
+ r.Add(report.Entry{
+ Message: ErrDoesntMatchGUIDRegex.Error(),
+ Kind: report.EntryError,
+ })
+ }
+ return r
+}
diff --git a/vendor/github.com/coreos/ignition/config/types/path.go b/vendor/github.com/coreos/ignition/config/types/path.go
new file mode 100644
index 00000000..0bdbdcb0
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/config/types/path.go
@@ -0,0 +1,31 @@
+// Copyright 2016 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+ "errors"
+ "path"
+)
+
+var (
+ ErrPathRelative = errors.New("path not absolute")
+)
+
+func validatePath(p string) error {
+ if !path.IsAbs(p) {
+ return ErrPathRelative
+ }
+ return nil
+}
diff --git a/vendor/github.com/coreos/ignition/config/types/raid.go b/vendor/github.com/coreos/ignition/config/types/raid.go
new file mode 100644
index 00000000..4dffa296
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/config/types/raid.go
@@ -0,0 +1,58 @@
+// Copyright 2016 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+ "fmt"
+
+ "github.com/coreos/ignition/config/validate/report"
+)
+
+func (n Raid) ValidateLevel() report.Report {
+ r := report.Report{}
+ switch n.Level {
+ case "linear", "raid0", "0", "stripe":
+ if n.Spares != 0 {
+ r.Add(report.Entry{
+ Message: fmt.Sprintf("spares unsupported for %q arrays", n.Level),
+ Kind: report.EntryError,
+ })
+ }
+ case "raid1", "1", "mirror":
+ case "raid4", "4":
+ case "raid5", "5":
+ case "raid6", "6":
+ case "raid10", "10":
+ default:
+ r.Add(report.Entry{
+ Message: fmt.Sprintf("unrecognized raid level: %q", n.Level),
+ Kind: report.EntryError,
+ })
+ }
+ return r
+}
+
+func (n Raid) ValidateDevices() report.Report {
+ r := report.Report{}
+ for d := range n.Devices {
+ if err := validatePath(string(d)); err != nil {
+ r.Add(report.Entry{
+ Message: fmt.Sprintf("array %q: device path not absolute: %q", n.Name, d),
+ Kind: report.EntryError,
+ })
+ }
+ }
+ return r
+}
diff --git a/vendor/github.com/coreos/ignition/config/types/schema.go b/vendor/github.com/coreos/ignition/config/types/schema.go
new file mode 100644
index 00000000..265ae7ce
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/config/types/schema.go
@@ -0,0 +1,200 @@
+package types
+
+// generated by "schematyper --package=types schema/ignition.json -o config/types/schema.go --root-type=Config" -- DO NOT EDIT
+
+type Config struct {
+ Ignition Ignition `json:"ignition"`
+ Networkd Networkd `json:"networkd,omitempty"`
+ Passwd Passwd `json:"passwd,omitempty"`
+ Storage Storage `json:"storage,omitempty"`
+ Systemd Systemd `json:"systemd,omitempty"`
+}
+
+type ConfigReference struct {
+ Source string `json:"source,omitempty"`
+ Verification Verification `json:"verification,omitempty"`
+}
+
+type Create struct {
+ Force bool `json:"force,omitempty"`
+ Options []Option `json:"options,omitempty"`
+}
+
+type Device string
+
+type Directory struct {
+ Node
+ DirectoryEmbedded1
+}
+
+type DirectoryEmbedded1 struct {
+ Mode int `json:"mode,omitempty"`
+}
+
+type Disk struct {
+ Device string `json:"device,omitempty"`
+ Partitions []Partition `json:"partitions,omitempty"`
+ WipeTable bool `json:"wipeTable,omitempty"`
+}
+
+type Dropin struct {
+ Contents string `json:"contents,omitempty"`
+ Name string `json:"name,omitempty"`
+}
+
+type File struct {
+ Node
+ FileEmbedded1
+}
+
+type FileContents struct {
+ Compression string `json:"compression,omitempty"`
+ Source string `json:"source,omitempty"`
+ Verification Verification `json:"verification,omitempty"`
+}
+
+type FileEmbedded1 struct {
+ Contents FileContents `json:"contents,omitempty"`
+ Mode int `json:"mode,omitempty"`
+}
+
+type Filesystem struct {
+ Mount *Mount `json:"mount,omitempty"`
+ Name string `json:"name,omitempty"`
+ Path *string `json:"path,omitempty"`
+}
+
+type Ignition struct {
+ Config IgnitionConfig `json:"config,omitempty"`
+ Timeouts Timeouts `json:"timeouts,omitempty"`
+ Version string `json:"version,omitempty"`
+}
+
+type IgnitionConfig struct {
+ Append []ConfigReference `json:"append,omitempty"`
+ Replace *ConfigReference `json:"replace,omitempty"`
+}
+
+type Link struct {
+ Node
+ LinkEmbedded1
+}
+
+type LinkEmbedded1 struct {
+ Hard bool `json:"hard,omitempty"`
+ Target string `json:"target,omitempty"`
+}
+
+type Mount struct {
+ Create *Create `json:"create,omitempty"`
+ Device string `json:"device,omitempty"`
+ Format string `json:"format,omitempty"`
+}
+
+type Networkd struct {
+ Units []Networkdunit `json:"units,omitempty"`
+}
+
+type Networkdunit struct {
+ Contents string `json:"contents,omitempty"`
+ Name string `json:"name,omitempty"`
+}
+
+type Node struct {
+ Filesystem string `json:"filesystem,omitempty"`
+ Group NodeGroup `json:"group,omitempty"`
+ Path string `json:"path,omitempty"`
+ User NodeUser `json:"user,omitempty"`
+}
+
+type NodeGroup struct {
+ ID int `json:"id,omitempty"`
+}
+
+type NodeUser struct {
+ ID int `json:"id,omitempty"`
+}
+
+type Option string
+
+type Partition struct {
+ GUID string `json:"guid,omitempty"`
+ Label string `json:"label,omitempty"`
+ Number int `json:"number,omitempty"`
+ Size int `json:"size,omitempty"`
+ Start int `json:"start,omitempty"`
+ TypeGUID string `json:"typeGuid,omitempty"`
+}
+
+type Passwd struct {
+ Groups []PasswdGroup `json:"groups,omitempty"`
+ Users []PasswdUser `json:"users,omitempty"`
+}
+
+type PasswdGroup struct {
+ Gid *int `json:"gid,omitempty"`
+ Name string `json:"name,omitempty"`
+ PasswordHash string `json:"passwordHash,omitempty"`
+ System bool `json:"system,omitempty"`
+}
+
+type PasswdUser struct {
+ Create *Usercreate `json:"create,omitempty"`
+ Name string `json:"name,omitempty"`
+ PasswordHash string `json:"passwordHash,omitempty"`
+ SSHAuthorizedKeys []SSHAuthorizedKey `json:"sshAuthorizedKeys,omitempty"`
+}
+
+type Raid struct {
+ Devices []Device `json:"devices,omitempty"`
+ Level string `json:"level,omitempty"`
+ Name string `json:"name,omitempty"`
+ Spares int `json:"spares,omitempty"`
+}
+
+type SSHAuthorizedKey string
+
+type Storage struct {
+ Directories []Directory `json:"directories,omitempty"`
+ Disks []Disk `json:"disks,omitempty"`
+ Files []File `json:"files,omitempty"`
+ Filesystems []Filesystem `json:"filesystems,omitempty"`
+ Links []Link `json:"links,omitempty"`
+ Raid []Raid `json:"raid,omitempty"`
+}
+
+type Systemd struct {
+ Units []Unit `json:"units,omitempty"`
+}
+
+type Timeouts struct {
+ HTTPResponseHeaders *int `json:"httpResponseHeaders,omitempty"`
+ HTTPTotal *int `json:"httpTotal,omitempty"`
+}
+
+type Unit struct {
+ Contents string `json:"contents,omitempty"`
+ Dropins []Dropin `json:"dropins,omitempty"`
+ Enable bool `json:"enable,omitempty"`
+ Mask bool `json:"mask,omitempty"`
+ Name string `json:"name,omitempty"`
+}
+
+type Usercreate struct {
+ Gecos string `json:"gecos,omitempty"`
+ Groups []UsercreateGroup `json:"groups,omitempty"`
+ HomeDir string `json:"homeDir,omitempty"`
+ NoCreateHome bool `json:"noCreateHome,omitempty"`
+ NoLogInit bool `json:"noLogInit,omitempty"`
+ NoUserGroup bool `json:"noUserGroup,omitempty"`
+ PrimaryGroup string `json:"primaryGroup,omitempty"`
+ Shell string `json:"shell,omitempty"`
+ System bool `json:"system,omitempty"`
+ UID *int `json:"uid,omitempty"`
+}
+
+type UsercreateGroup string
+
+type Verification struct {
+ Hash *string `json:"hash,omitempty"`
+}
diff --git a/vendor/github.com/coreos/ignition/config/types/unit.go b/vendor/github.com/coreos/ignition/config/types/unit.go
new file mode 100644
index 00000000..3d3fc8db
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/config/types/unit.go
@@ -0,0 +1,109 @@
+// Copyright 2016 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "path"
+
+ "github.com/coreos/go-systemd/unit"
+
+ "github.com/coreos/ignition/config/validate/report"
+)
+
+var (
+ ErrInvalidSystemdExt = errors.New("invalid systemd unit extension")
+ ErrInvalidNetworkdExt = errors.New("invalid networkd unit extension")
+)
+
+func (u Unit) ValidateContents() report.Report {
+ r := report.Report{}
+ if err := validateUnitContent(u.Contents); err != nil {
+ r.Add(report.Entry{
+ Message: err.Error(),
+ Kind: report.EntryError,
+ })
+ }
+ return r
+}
+
+func (u Unit) ValidateName() report.Report {
+ r := report.Report{}
+ switch path.Ext(u.Name) {
+ case ".service", ".socket", ".device", ".mount", ".automount", ".swap", ".target", ".path", ".timer", ".snapshot", ".slice", ".scope":
+ default:
+ r.Add(report.Entry{
+ Message: ErrInvalidSystemdExt.Error(),
+ Kind: report.EntryError,
+ })
+ }
+ return r
+}
+
+func (d Dropin) Validate() report.Report {
+ r := report.Report{}
+
+ if err := validateUnitContent(d.Contents); err != nil {
+ r.Add(report.Entry{
+ Message: err.Error(),
+ Kind: report.EntryError,
+ })
+ }
+
+ switch path.Ext(d.Name) {
+ case ".conf":
+ default:
+ r.Add(report.Entry{
+ Message: fmt.Sprintf("invalid systemd unit drop-in extension: %q", path.Ext(d.Name)),
+ Kind: report.EntryError,
+ })
+ }
+
+ return r
+}
+
+func (u Networkdunit) Validate() report.Report {
+ r := report.Report{}
+
+ if err := validateUnitContent(u.Contents); err != nil {
+ r.Add(report.Entry{
+ Message: err.Error(),
+ Kind: report.EntryError,
+ })
+ }
+
+ switch path.Ext(u.Name) {
+ case ".link", ".netdev", ".network":
+ default:
+ r.Add(report.Entry{
+ Message: ErrInvalidNetworkdExt.Error(),
+ Kind: report.EntryError,
+ })
+ }
+
+ return r
+}
+
+func validateUnitContent(content string) error {
+ c := bytes.NewBufferString(content)
+ _, err := unit.Deserialize(c)
+ if err != nil {
+ return fmt.Errorf("invalid unit content: %s", err)
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/coreos/ignition/config/types/url.go b/vendor/github.com/coreos/ignition/config/types/url.go
new file mode 100644
index 00000000..e80bf939
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/config/types/url.go
@@ -0,0 +1,49 @@
+// Copyright 2016 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+ "errors"
+ "net/url"
+
+ "github.com/vincent-petithory/dataurl"
+)
+
+var (
+ ErrInvalidScheme = errors.New("invalid url scheme")
+)
+
+func validateURL(s string) error {
+ // Empty url is valid, indicates an empty file
+ if s == "" {
+ return nil
+ }
+ u, err := url.Parse(s)
+ if err != nil {
+ return err
+ }
+
+ switch u.Scheme {
+ case "http", "https", "oem":
+ return nil
+ case "data":
+ if _, err := dataurl.DecodeString(s); err != nil {
+ return err
+ }
+ return nil
+ default:
+ return ErrInvalidScheme
+ }
+}
diff --git a/vendor/github.com/coreos/ignition/config/types/verification.go b/vendor/github.com/coreos/ignition/config/types/verification.go
new file mode 100644
index 00000000..d2158b80
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/config/types/verification.go
@@ -0,0 +1,83 @@
+// Copyright 2016 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package types
+
+import (
+ "crypto"
+ "encoding/hex"
+ "errors"
+ "strings"
+
+ "github.com/coreos/ignition/config/validate/report"
+)
+
+var (
+ ErrHashMalformed = errors.New("malformed hash specifier")
+ ErrHashWrongSize = errors.New("incorrect size for hash sum")
+ ErrHashUnrecognized = errors.New("unrecognized hash function")
+)
+
+// HashParts will return the sum and function (in that order) of the hash stored
+// in this Verification, or an error if there is an issue during parsing.
+func (v Verification) HashParts() (string, string, error) {
+ if v.Hash == nil {
+ // The hash can be nil
+ return "", "", nil
+ }
+ parts := strings.SplitN(*v.Hash, "-", 2)
+ if len(parts) != 2 {
+ return "", "", ErrHashMalformed
+ }
+
+ return parts[0], parts[1], nil
+}
+
+func (v Verification) Validate() report.Report {
+ r := report.Report{}
+
+ if v.Hash == nil {
+ // The hash can be nil
+ return r
+ }
+
+ function, sum, err := v.HashParts()
+ if err != nil {
+ r.Add(report.Entry{
+ Message: err.Error(),
+ Kind: report.EntryError,
+ })
+ return r
+ }
+ var hash crypto.Hash
+ switch function {
+ case "sha512":
+ hash = crypto.SHA512
+ default:
+ r.Add(report.Entry{
+ Message: ErrHashUnrecognized.Error(),
+ Kind: report.EntryError,
+ })
+ return r
+ }
+
+ if len(sum) != hex.EncodedLen(hash.Size()) {
+ r.Add(report.Entry{
+ Message: ErrHashWrongSize.Error(),
+ Kind: report.EntryError,
+ })
+ }
+
+ return r
+}
diff --git a/vendor/github.com/coreos/ignition/config/validate/report/report.go b/vendor/github.com/coreos/ignition/config/validate/report/report.go
new file mode 100644
index 00000000..e0d4fed8
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/config/validate/report/report.go
@@ -0,0 +1,158 @@
+// Copyright 2016 CoreOS, 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 report
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "sort"
+)
+
+type Report struct {
+ Entries []Entry
+}
+
+func (into *Report) Merge(from Report) {
+ into.Entries = append(into.Entries, from.Entries...)
+}
+
+func ReportFromError(err error, severity entryKind) Report {
+ if err == nil {
+ return Report{}
+ }
+ return Report{
+ Entries: []Entry{
+ {
+ Kind: severity,
+ Message: err.Error(),
+ },
+ },
+ }
+}
+
+// Sort sorts the entries by line number, then column number
+func (r *Report) Sort() {
+ sort.Sort(entries(r.Entries))
+}
+
+type entries []Entry
+
+func (e entries) Len() int {
+ return len(e)
+}
+
+func (e entries) Swap(i, j int) {
+ e[i], e[j] = e[j], e[i]
+}
+
+func (e entries) Less(i, j int) bool {
+ if e[i].Line != e[j].Line {
+ return e[i].Line < e[j].Line
+ }
+ return e[i].Column < e[j].Column
+}
+
+const (
+ EntryError entryKind = iota
+ EntryWarning
+ EntryInfo
+ EntryDeprecated
+)
+
+// AddPosition updates all the entries with Line equal to 0 and sets the Line/Column fields to line/column. This is useful for
+// when a type has a custom unmarshaller and thus can't determine an exact offset of the error with the type. In this case
+// the offset for the entire chunk of json that got unmarshalled to the type can be used instead, which is still pretty good.
+func (r *Report) AddPosition(line, col int, highlight string) {
+ for i, e := range r.Entries {
+ if e.Line == 0 {
+ r.Entries[i].Line = line
+ r.Entries[i].Column = col
+ r.Entries[i].Highlight = highlight
+ }
+ }
+}
+
+func (r *Report) Add(e Entry) {
+ r.Entries = append(r.Entries, e)
+}
+
+func (r Report) String() string {
+ var errs bytes.Buffer
+ for i, entry := range r.Entries {
+ if i != 0 {
+ // Only add line breaks on multiline reports
+ errs.WriteString("\n")
+ }
+ errs.WriteString(entry.String())
+ }
+ return errs.String()
+}
+
+// IsFatal returns if there were any errors that make the config invalid
+func (r Report) IsFatal() bool {
+ for _, entry := range r.Entries {
+ if entry.Kind == EntryError {
+ return true
+ }
+ }
+ return false
+}
+
+// IsDeprecated returns if the report has deprecations
+func (r Report) IsDeprecated() bool {
+ for _, entry := range r.Entries {
+ if entry.Kind == EntryDeprecated {
+ return true
+ }
+ }
+ return false
+}
+
+type Entry struct {
+ Kind entryKind `json:"kind"`
+ Message string `json:"message"`
+ Line int `json:"line,omitempty"`
+ Column int `json:"column,omitempty"`
+ Highlight string `json:"-"`
+}
+
+func (e Entry) String() string {
+ if e.Line != 0 {
+ return fmt.Sprintf("%s at line %d, column %d\n%s%v", e.Kind.String(), e.Line, e.Column, e.Highlight, e.Message)
+ }
+ return fmt.Sprintf("%s: %v", e.Kind.String(), e.Message)
+}
+
+type entryKind int
+
+func (e entryKind) String() string {
+ switch e {
+ case EntryError:
+ return "error"
+ case EntryWarning:
+ return "warning"
+ case EntryInfo:
+ return "info"
+ case EntryDeprecated:
+ return "deprecated"
+ default:
+ return "unknown error"
+ }
+}
+
+func (e entryKind) MarshalJSON() ([]byte, error) {
+ return json.Marshal(e.String())
+}
diff --git a/vendor/github.com/coreos/ignition/internal/exec/util/device_alias.go b/vendor/github.com/coreos/ignition/internal/exec/util/device_alias.go
new file mode 100644
index 00000000..0b4b4474
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/internal/exec/util/device_alias.go
@@ -0,0 +1,55 @@
+// Copyright 2016 CoreOS, 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 (
+ "os"
+ "path/filepath"
+)
+
+const deviceAliasDir = "/dev_aliases"
+
+// DeviceAlias returns the aliased form of the supplied path.
+// Note device paths in ignition are always absolute.
+func DeviceAlias(path string) string {
+ return filepath.Join(deviceAliasDir, filepath.Clean(path))
+}
+
+// CreateDeviceAlias creates a device alias for the supplied path.
+// On success the canonicalized path used as the alias target is returned.
+func CreateDeviceAlias(path string) (string, error) {
+ target, err := filepath.EvalSymlinks(path)
+ if err != nil {
+ return "", err
+ }
+
+ alias := DeviceAlias(path)
+
+ if err := os.Remove(alias); err != nil {
+ if !os.IsNotExist(err) {
+ return "", err
+ }
+
+ if err = os.MkdirAll(filepath.Dir(alias), 0750); err != nil {
+ return "", err
+ }
+ }
+
+ if err = os.Symlink(target, alias); err != nil {
+ return "", err
+ }
+
+ return target, nil
+}
diff --git a/vendor/github.com/coreos/ignition/internal/exec/util/file.go b/vendor/github.com/coreos/ignition/internal/exec/util/file.go
new file mode 100644
index 00000000..b51a9dee
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/internal/exec/util/file.go
@@ -0,0 +1,235 @@
+// Copyright 2015 CoreOS, 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 (
+ "bufio"
+ "compress/gzip"
+ "encoding/hex"
+ "hash"
+ "io"
+ "io/ioutil"
+ "net/url"
+ "os"
+ "path/filepath"
+
+ "github.com/coreos/ignition/config/types"
+ "github.com/coreos/ignition/internal/log"
+ "github.com/coreos/ignition/internal/resource"
+
+ "golang.org/x/net/context"
+)
+
+const (
+ DefaultDirectoryPermissions os.FileMode = 0755
+ DefaultFilePermissions os.FileMode = 0644
+)
+
+type File struct {
+ io.ReadCloser
+ hash.Hash
+ Path string
+ Mode os.FileMode
+ Uid int
+ Gid int
+ expectedSum string
+}
+
+func (f File) Verify() error {
+ if f.Hash == nil {
+ return nil
+ }
+ sum := f.Sum(nil)
+ encodedSum := make([]byte, hex.EncodedLen(len(sum)))
+ hex.Encode(encodedSum, sum)
+
+ if string(encodedSum) != f.expectedSum {
+ return ErrHashMismatch{
+ Calculated: string(encodedSum),
+ Expected: f.expectedSum,
+ }
+ }
+ return nil
+}
+
+// newHashedReader returns a new ReadCloser that also writes to the provided hash.
+func newHashedReader(reader io.ReadCloser, hasher hash.Hash) io.ReadCloser {
+ return struct {
+ io.Reader
+ io.Closer
+ }{
+ Reader: io.TeeReader(reader, hasher),
+ Closer: reader,
+ }
+}
+
+// RenderFile returns a *File with a Reader that downloads, hashes, and decompresses the incoming data.
+// It returns nil if f had invalid options. Errors reading/verifying/decompressing the file will
+// present themselves when the Reader is actually read from.
+func RenderFile(l *log.Logger, c *resource.HttpClient, f types.File) *File {
+ var reader io.ReadCloser
+ var err error
+ var expectedSum string
+
+ // explicitly ignoring the error here because the config should already be
+ // validated by this point
+ u, _ := url.Parse(f.Contents.Source)
+
+ reader, err = resource.FetchAsReader(l, c, context.Background(), *u)
+ if err != nil {
+ l.Crit("Error fetching file %q: %v", f.Path, err)
+ return nil
+ }
+
+ fileHash, err := GetHasher(f.Contents.Verification)
+ if err != nil {
+ l.Crit("Error verifying file %q: %v", f.Path, err)
+ return nil
+ }
+
+ if fileHash != nil {
+ reader = newHashedReader(reader, fileHash)
+ // explicitly ignoring the error here because the config should already
+ // be validated by this point
+ _, expectedSum, _ = f.Contents.Verification.HashParts()
+ }
+
+ reader, err = decompressFileStream(l, f, reader)
+ if err != nil {
+ l.Crit("Error decompressing file %q: %v", f.Path, err)
+ return nil
+ }
+
+ return &File{
+ Path: f.Path,
+ ReadCloser: reader,
+ Hash: fileHash,
+ Mode: os.FileMode(f.Mode),
+ Uid: f.User.ID,
+ Gid: f.Group.ID,
+ expectedSum: expectedSum,
+ }
+}
+
+// gzipReader is a wrapper for gzip's reader that closes the stream it wraps as well
+// as itself when Close() is called.
+type gzipReader struct {
+ *gzip.Reader //actually a ReadCloser
+ source io.Closer
+}
+
+func newGzipReader(reader io.ReadCloser) (io.ReadCloser, error) {
+ gzReader, err := gzip.NewReader(reader)
+ if err != nil {
+ return nil, err
+ }
+ return gzipReader{
+ Reader: gzReader,
+ source: reader,
+ }, nil
+}
+
+func (gz gzipReader) Close() error {
+ if err := gz.Reader.Close(); err != nil {
+ return err
+ }
+ if err := gz.source.Close(); err != nil {
+ return err
+ }
+ return nil
+}
+
+func decompressFileStream(l *log.Logger, f types.File, contents io.ReadCloser) (io.ReadCloser, error) {
+ switch f.Contents.Compression {
+ case "":
+ return contents, nil
+ case "gzip":
+ return newGzipReader(contents)
+ default:
+ return nil, types.ErrCompressionInvalid
+ }
+}
+
+func (u Util) WriteLink(s types.Link) error {
+ path := u.JoinPath(s.Path)
+
+ if err := MkdirForFile(path); err != nil {
+ return err
+ }
+
+ if s.Hard {
+ return os.Link(s.Target, path)
+ }
+ return os.Symlink(s.Target, path)
+}
+
+// WriteFile creates and writes the file described by f using the provided context.
+func (u Util) WriteFile(f *File) error {
+ defer f.Close()
+ var err error
+
+ path := u.JoinPath(string(f.Path))
+
+ if err := MkdirForFile(path); err != nil {
+ return err
+ }
+
+ // Create a temporary file in the same directory to ensure it's on the same filesystem
+ var tmp *os.File
+ if tmp, err = ioutil.TempFile(filepath.Dir(path), "tmp"); err != nil {
+ return err
+ }
+
+ defer func() {
+ tmp.Close()
+ if err != nil {
+ os.Remove(tmp.Name())
+ }
+ }()
+
+ fileWriter := bufio.NewWriter(tmp)
+
+ if _, err = io.Copy(fileWriter, f); err != nil {
+ return err
+ }
+ fileWriter.Flush()
+
+ if err = f.Verify(); err != nil {
+ return err
+ }
+
+ // XXX(vc): Note that we assume to be operating on the file we just wrote, this is only guaranteed
+ // by using syscall.Fchown() and syscall.Fchmod()
+
+ // Ensure the ownership and mode are as requested (since WriteFile can be affected by sticky bit)
+ if err = os.Chown(tmp.Name(), f.Uid, f.Gid); err != nil {
+ return err
+ }
+
+ if err = os.Chmod(tmp.Name(), f.Mode); err != nil {
+ return err
+ }
+
+ if err = os.Rename(tmp.Name(), path); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// MkdirForFile helper creates the directory components of path.
+func MkdirForFile(path string) error {
+ return os.MkdirAll(filepath.Dir(path), DefaultDirectoryPermissions)
+}
diff --git a/vendor/github.com/coreos/ignition/internal/exec/util/passwd.go b/vendor/github.com/coreos/ignition/internal/exec/util/passwd.go
new file mode 100644
index 00000000..4252de35
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/internal/exec/util/passwd.go
@@ -0,0 +1,191 @@
+// Copyright 2015 CoreOS, 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"
+ "os/exec"
+ "strconv"
+ "strings"
+
+ "github.com/coreos/ignition/config/types"
+
+ keys "github.com/coreos/update-ssh-keys/authorized_keys_d"
+)
+
+// CreateUser creates the user as described.
+func (u Util) CreateUser(c types.PasswdUser) error {
+ if c.Create == nil {
+ return nil
+ }
+
+ cu := c.Create
+ args := []string{"--root", u.DestDir}
+
+ if c.PasswordHash != "" {
+ args = append(args, "--password", c.PasswordHash)
+ } else {
+ args = append(args, "--password", "*")
+ }
+
+ if cu.UID != nil {
+ args = append(args, "--uid",
+ strconv.FormatUint(uint64(*cu.UID), 10))
+ }
+
+ if cu.Gecos != "" {
+ args = append(args, "--comment", fmt.Sprintf("%q", cu.Gecos))
+ }
+
+ if cu.HomeDir != "" {
+ args = append(args, "--home-dir", cu.HomeDir)
+ }
+
+ if cu.NoCreateHome {
+ args = append(args, "--no-create-home")
+ } else {
+ args = append(args, "--create-home")
+ }
+
+ if cu.PrimaryGroup != "" {
+ args = append(args, "--gid", cu.PrimaryGroup)
+ }
+
+ if len(cu.Groups) > 0 {
+ args = append(args, "--groups", strings.Join(translateV2_1UsercreateGroupSliceToStringSlice(cu.Groups), ","))
+ }
+
+ if cu.NoUserGroup {
+ args = append(args, "--no-user-group")
+ }
+
+ if cu.System {
+ args = append(args, "--system")
+ }
+
+ if cu.NoLogInit {
+ args = append(args, "--no-log-init")
+ }
+
+ if cu.Shell != "" {
+ args = append(args, "--shell", cu.Shell)
+ }
+
+ args = append(args, c.Name)
+
+ return u.LogCmd(exec.Command("useradd", args...),
+ "creating user %q", c.Name)
+}
+
+// golang--
+func translateV2_1UsercreateGroupSliceToStringSlice(groups []types.UsercreateGroup) []string {
+ newGroups := make([]string, len(groups))
+ for i, g := range groups {
+ newGroups[i] = string(g)
+ }
+ return newGroups
+}
+
+// Add the provided SSH public keys to the user's authorized keys.
+func (u Util) AuthorizeSSHKeys(c types.PasswdUser) error {
+ if len(c.SSHAuthorizedKeys) == 0 {
+ return nil
+ }
+
+ return u.LogOp(func() error {
+ usr, err := u.userLookup(c.Name)
+ if err != nil {
+ return fmt.Errorf("unable to lookup user %q", c.Name)
+ }
+
+ akd, err := keys.Open(usr, true)
+ if err != nil {
+ return err
+ }
+ defer akd.Close()
+
+ // TODO(vc): introduce key names to config?
+ // TODO(vc): validate c.SSHAuthorizedKeys well-formedness.
+ ks := strings.Join(translateV2_1SSHAuthorizedKeySliceToStringSlice(c.SSHAuthorizedKeys), "\n")
+ // XXX(vc): for now ensure the addition is always
+ // newline-terminated. A future version of akd will handle this
+ // for us in addition to validating the ssh keys for
+ // well-formedness.
+ if !strings.HasSuffix(ks, "\n") {
+ ks = ks + "\n"
+ }
+
+ if err := akd.Add("coreos-ignition", []byte(ks), true, true); err != nil {
+ return err
+ }
+
+ if err := akd.Sync(); err != nil {
+ return err
+ }
+
+ return nil
+ }, "adding ssh keys to user %q", c.Name)
+}
+
+// golang--
+func translateV2_1SSHAuthorizedKeySliceToStringSlice(keys []types.SSHAuthorizedKey) []string {
+ newKeys := make([]string, len(keys))
+ for i, k := range keys {
+ newKeys[i] = string(k)
+ }
+ return newKeys
+}
+
+// SetPasswordHash sets the password hash of the specified user.
+func (u Util) SetPasswordHash(c types.PasswdUser) error {
+ if c.PasswordHash == "" {
+ return nil
+ }
+
+ args := []string{
+ "--root", u.DestDir,
+ "--password", c.PasswordHash,
+ }
+
+ args = append(args, c.Name)
+
+ return u.LogCmd(exec.Command("usermod", args...),
+ "setting password for %q", c.Name)
+}
+
+// CreateGroup creates the group as described.
+func (u Util) CreateGroup(g types.PasswdGroup) error {
+ args := []string{"--root", u.DestDir}
+
+ if g.Gid != nil {
+ args = append(args, "--gid",
+ strconv.FormatUint(uint64(*g.Gid), 10))
+ }
+
+ if g.PasswordHash != "" {
+ args = append(args, "--password", g.PasswordHash)
+ } else {
+ args = append(args, "--password", "*")
+ }
+
+ if g.System {
+ args = append(args, "--system")
+ }
+
+ args = append(args, g.Name)
+
+ return u.LogCmd(exec.Command("groupadd", args...),
+ "adding group %q", g.Name)
+}
diff --git a/vendor/github.com/coreos/ignition/internal/exec/util/path.go b/vendor/github.com/coreos/ignition/internal/exec/util/path.go
new file mode 100644
index 00000000..ec68dd96
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/internal/exec/util/path.go
@@ -0,0 +1,31 @@
+// Copyright 2015 CoreOS, 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"
+)
+
+func SystemdUnitsPath() string {
+ return filepath.Join("etc", "systemd", "system")
+}
+
+func NetworkdUnitsPath() string {
+ return filepath.Join("etc", "systemd", "network")
+}
+
+func SystemdDropinsPath(unitName string) string {
+ return filepath.Join("etc", "systemd", "system", unitName+".d")
+}
diff --git a/vendor/github.com/coreos/ignition/internal/exec/util/unit.go b/vendor/github.com/coreos/ignition/internal/exec/util/unit.go
new file mode 100644
index 00000000..0f383bbb
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/internal/exec/util/unit.go
@@ -0,0 +1,80 @@
+// Copyright 2015 CoreOS, 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"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ "github.com/coreos/ignition/config/types"
+)
+
+const (
+ presetPath string = "/etc/systemd/system-preset/20-ignition.preset"
+ DefaultPresetPermissions os.FileMode = 0644
+)
+
+func FileFromSystemdUnit(unit types.Unit) *File {
+ return &File{
+ Path: filepath.Join(SystemdUnitsPath(), string(unit.Name)),
+ ReadCloser: ioutil.NopCloser(bytes.NewReader([]byte(unit.Contents))),
+ Mode: DefaultFilePermissions,
+ }
+}
+
+func FileFromNetworkdUnit(unit types.Networkdunit) *File {
+ return &File{
+ Path: filepath.Join(NetworkdUnitsPath(), string(unit.Name)),
+ ReadCloser: ioutil.NopCloser(bytes.NewReader([]byte(unit.Contents))),
+ Mode: DefaultFilePermissions,
+ }
+}
+
+func FileFromUnitDropin(unit types.Unit, dropin types.Dropin) *File {
+ return &File{
+ Path: filepath.Join(SystemdDropinsPath(string(unit.Name)), string(dropin.Name)),
+ ReadCloser: ioutil.NopCloser(bytes.NewReader([]byte(dropin.Contents))),
+ Mode: DefaultFilePermissions,
+ }
+}
+
+func (u Util) MaskUnit(unit types.Unit) error {
+ path := u.JoinPath(SystemdUnitsPath(), string(unit.Name))
+ if err := MkdirForFile(path); err != nil {
+ return err
+ }
+ if err := os.RemoveAll(path); err != nil {
+ return err
+ }
+ return os.Symlink("/dev/null", path)
+}
+
+func (u Util) EnableUnit(unit types.Unit) error {
+ path := u.JoinPath(presetPath)
+ if err := MkdirForFile(path); err != nil {
+ return err
+ }
+ file, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, DefaultPresetPermissions)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ _, err = file.WriteString(fmt.Sprintf("enable %s\n", unit.Name))
+ return err
+}
diff --git a/vendor/github.com/coreos/ignition/internal/exec/util/user_lookup.c b/vendor/github.com/coreos/ignition/internal/exec/util/user_lookup.c
new file mode 100644
index 00000000..a63da3f6
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/internal/exec/util/user_lookup.c
@@ -0,0 +1,139 @@
+// Copyright 2015 CoreOS, 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.
+#define _GNU_SOURCE
+#include <errno.h>
+#include <pwd.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "user_lookup.h"
+
+#define STACK_SIZE (64 * 1024)
+
+/* This is all a bit copy-and-pasty from update-ssh-keys/authorized_keys_d,
+ * TODO(vc): refactor authorized_keys_d a bit so external packages can reuse
+ * the pieces duplicated here.
+ */
+typedef struct user_lookup_ctxt {
+ void *stack;
+
+ const char *name;
+ const char *root;
+
+ user_lookup_res_t *res;
+ int ret;
+ int err;
+} user_lookup_ctxt_t;
+
+
+static int user_lookup_fn(user_lookup_ctxt_t *ctxt) {
+ char buf[16 * 1024];
+ struct passwd p, *pptr;
+
+ if(chroot(ctxt->root) == -1) {
+ goto out_err;
+ }
+
+ if(getpwnam_r(ctxt->name, &p, buf, sizeof(buf), &pptr) != 0 || !pptr) {
+ goto out_err;
+ }
+
+ if(!(ctxt->res->name = strdup(p.pw_name))) {
+ goto out_err;
+ }
+
+ if(!(ctxt->res->home = strdup(p.pw_dir))) {
+ free(ctxt->res->name);
+ goto out_err;
+ }
+
+ ctxt->res->uid = p.pw_uid;
+ ctxt->res->gid = p.pw_gid;
+
+ return 0;
+
+out_err:
+ ctxt->err = errno;
+ ctxt->ret = -1;
+ return 0;
+}
+
+/* user_lookup() looks up a user in a chroot.
+ * returns 0 and the results in res on success,
+ * res->name will be NULL if user doesn't exist.
+ * returns -1 on error.
+ */
+int user_lookup(const char *root, const char *name, user_lookup_res_t *res) {
+ user_lookup_ctxt_t ctxt = {
+ .root = root,
+ .name = name,
+ .res = res,
+ .ret = 0
+ };
+ int pid, ret = 0;
+ sigset_t allsigs, orig;
+
+ if(!(ctxt.stack = malloc(STACK_SIZE))) {
+ ret = -1;
+ goto out;
+ }
+
+ /* It's necessary to block all signals before cloning, so the child
+ * doesn't run any of the Go runtime's signal handlers.
+ */
+ if((ret = sigemptyset(&orig)) == -1 ||
+ (ret = sigfillset(&allsigs)) == -1)
+ goto out_stack;
+
+ if((ret = sigprocmask(SIG_BLOCK, &allsigs, &orig)) == -1)
+ goto out_stack;
+
+ pid = clone((int(*)(void *))user_lookup_fn, ctxt.stack + STACK_SIZE,
+ CLONE_VM, &ctxt);
+
+ ret = sigprocmask(SIG_SETMASK, &orig, NULL);
+
+ if(pid != -1) {
+ if(waitpid(pid, NULL, __WCLONE) == -1 && errno != ECHILD) {
+ ret = -1;
+ goto out_stack;
+ }
+ } else {
+ ret = -1;
+ }
+
+ if(ret != -1) {
+ errno = ctxt.err;
+ ret = ctxt.ret;
+ }
+
+out_stack:
+ free(ctxt.stack);
+
+out:
+ return ret;
+}
+
+/* user_lookup_res_free() frees any memory allocated by a successful user_lookup(). */
+void user_lookup_res_free(user_lookup_res_t *res) {
+ free(res->home);
+ free(res->name);
+}
diff --git a/vendor/github.com/coreos/ignition/internal/exec/util/user_lookup.go b/vendor/github.com/coreos/ignition/internal/exec/util/user_lookup.go
new file mode 100644
index 00000000..85d34919
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/internal/exec/util/user_lookup.go
@@ -0,0 +1,50 @@
+// Copyright 2015 CoreOS, 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.
+
+// +build linux
+
+package util
+
+// #include "user_lookup.h"
+import "C"
+
+import (
+ "fmt"
+ "os/user"
+)
+
+// userLookup looks up the user in u.DestDir.
+func (u Util) userLookup(name string) (*user.User, error) {
+ res := &C.user_lookup_res_t{}
+
+ if ret, err := C.user_lookup(C.CString(u.DestDir),
+ C.CString(name), res); ret < 0 {
+ return nil, fmt.Errorf("lookup failed: %v", err)
+ }
+
+ if res.name == nil {
+ return nil, fmt.Errorf("user %q not found", name)
+ }
+
+ usr := &user.User{
+ Name: C.GoString(res.name),
+ Uid: fmt.Sprintf("%d", int(res.uid)),
+ Gid: fmt.Sprintf("%d", int(res.gid)),
+ HomeDir: u.JoinPath(C.GoString(res.home)),
+ }
+
+ C.user_lookup_res_free(res)
+
+ return usr, nil
+}
diff --git a/vendor/github.com/coreos/ignition/internal/exec/util/user_lookup.h b/vendor/github.com/coreos/ignition/internal/exec/util/user_lookup.h
new file mode 100644
index 00000000..8178ee41
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/internal/exec/util/user_lookup.h
@@ -0,0 +1,23 @@
+// Copyright 2015 CoreOS, 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.
+
+typedef struct user_lookup_res {
+ int uid;
+ int gid;
+ char *home;
+ char *name;
+} user_lookup_res_t;
+
+int user_lookup(const char *, const char *, user_lookup_res_t *);
+void user_lookup_res_free(user_lookup_res_t *);
diff --git a/vendor/github.com/coreos/ignition/internal/exec/util/util.go b/vendor/github.com/coreos/ignition/internal/exec/util/util.go
new file mode 100644
index 00000000..31fd33e9
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/internal/exec/util/util.go
@@ -0,0 +1,32 @@
+// Copyright 2015 CoreOS, 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"
+
+ "github.com/coreos/ignition/internal/log"
+)
+
+// Util encapsulates logging and destdir indirection for the util methods.
+type Util struct {
+ DestDir string // directory prefix to use in applying fs paths.
+ *log.Logger
+}
+
+// JoinPath returns a path into the context ala filepath.Join(d, args)
+func (u Util) JoinPath(path ...string) string {
+ return filepath.Join(u.DestDir, filepath.Join(path...))
+}
diff --git a/vendor/github.com/coreos/ignition/internal/exec/util/verification.go b/vendor/github.com/coreos/ignition/internal/exec/util/verification.go
new file mode 100644
index 00000000..77a74603
--- /dev/null
+++ b/vendor/github.com/coreos/ignition/internal/exec/util/verification.go
@@ -0,0 +1,81 @@
+// Copyright 2015 CoreOS, 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 (
+ "crypto/sha512"
+ "encoding/hex"
+ "fmt"
+ "hash"
+
+ "github.com/coreos/ignition/config/types"
+)
+
+type ErrHashMismatch struct {
+ Calculated string
+ Expected string
+}
+
+func (e ErrHashMismatch) Error() string {
+ return fmt.Sprintf("hash verification failed (calculated %s but expected %s)",
+ e.Calculated, e.Expected)
+}
+
+func AssertValid(verify types.Verification, data []byte) error {
+ if hash := verify.Hash; hash != nil {
+ hashFunc, hashSum, err := verify.HashParts()
+ if err != nil {
+ return err
+ }
+
+ var sum []byte
+ switch hashFunc {
+ case "sha512":
+ rawSum := sha512.Sum512(data)
+ sum = rawSum[:]
+ default:
+ return types.ErrHashUnrecognized
+ }
+
+ encodedSum := make([]byte, hex.EncodedLen(len(sum)))
+ hex.Encode(encodedSum, sum)
+ if string(encodedSum) != hashSum {
+ return ErrHashMismatch{
+ Calculated: string(encodedSum),
+ Expected: hashSum,
+ }
+ }
+ }
+
+ return nil
+}
+
+func GetHasher(verify types.Verification) (hash.Hash, error) {
+ if verify.Hash == nil {
+ return nil, nil
+ }
+
+ function, _, err := verify.HashParts()
+ if err != nil {
+ return nil, err
+ }
+
+ switch function {
+ case "sha512":
+ return sha512.New(), nil
+ default:
+ return nil, types.ErrHashUnrecognized
+ }
+}