vendor: Update Ignition dependency

This commit is contained in:
Dalton Hubble
2016-05-03 14:47:00 -07:00
parent 682bb72dde
commit fb560dc79d
300 changed files with 23128 additions and 17815 deletions

88
Godeps/Godeps.json generated
View File

@@ -1,10 +1,19 @@
{
"ImportPath": "github.com/coreos/coreos-baremetal",
"GoVersion": "go1.5.3",
"GoVersion": "go1.6.1",
"Packages": [
"./..."
],
"Deps": [
{
"ImportPath": "github.com/alecthomas/units",
"Rev": "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a"
},
{
"ImportPath": "github.com/camlistore/camlistore/pkg/errorutil",
"Comment": "0.9-207-g7ec2b0a",
"Rev": "7ec2b0ac2e64f2c0e474258f0cf3a0ab0831796e"
},
{
"ImportPath": "github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/github.com/coreos/yaml",
"Comment": "v1.9.1",
@@ -15,30 +24,19 @@
"Comment": "v1.9.1",
"Rev": "b3f805dee6a4aa5ed298a1f370284df470eecf43"
},
{
"ImportPath": "github.com/coreos/go-semver/semver",
"Rev": "294930c1e79c64e7dbe360054274fdad492c8cf5"
},
{
"ImportPath": "github.com/coreos/go-systemd/journal",
"Comment": "v4-42-g7b2428f",
"Rev": "7b2428fec40033549c68f54e26e89e7ca9a9ce31"
},
{
"ImportPath": "github.com/coreos/ignition/src/config",
"Comment": "v0.3.0-6-g699adeb",
"Rev": "699adeba84898259868cef779a45293a49935500"
},
{
"ImportPath": "github.com/coreos/ignition/third_party/github.com/alecthomas/units",
"Comment": "v0.3.0-6-g699adeb",
"Rev": "699adeba84898259868cef779a45293a49935500"
},
{
"ImportPath": "github.com/coreos/ignition/third_party/github.com/camlistore/camlistore/pkg/errorutil",
"Comment": "v0.3.0-6-g699adeb",
"Rev": "699adeba84898259868cef779a45293a49935500"
},
{
"ImportPath": "github.com/coreos/ignition/third_party/github.com/go-yaml/yaml",
"Comment": "v0.3.0-6-g699adeb",
"Rev": "699adeba84898259868cef779a45293a49935500"
"ImportPath": "github.com/coreos/ignition/config",
"Comment": "v0.4.0-20-g44c274a",
"Rev": "44c274ab414294a8e34b3a940e0ec1afe6b6c610"
},
{
"ImportPath": "github.com/coreos/pkg/capnslog",
@@ -52,22 +50,14 @@
"ImportPath": "github.com/davecgh/go-spew/spew",
"Rev": "5215b55f46b2b919f50a1df0eaa5886afe4e3b3d"
},
{
"ImportPath": "github.com/golang/glog",
"Rev": "fca8c8854093a154ff1eb580aae10276ad6b1b5f"
},
{
"ImportPath": "github.com/golang/protobuf/proto",
"Rev": "552c7b9542c194800fd493123b3798ef0a832032"
"Rev": "f0a097ddac24fb00e07d2ac17f8671423f3ea47c"
},
{
"ImportPath": "github.com/pmezard/go-difflib/difflib",
"Rev": "792786c7400a136282c1664665ae0a8db921c6c2"
},
{
"ImportPath": "github.com/satori/go.uuid",
"Rev": "e673fdd4dea8a7334adbbe7f57b7e4b00bdc5502"
},
{
"ImportPath": "github.com/spf13/cobra",
"Rev": "65a708cee0a4424f4e353d031ce440643e312f92"
@@ -81,6 +71,14 @@
"Comment": "v1.1.3-4-g1f4a164",
"Rev": "1f4a1643a57e798696635ea4c126e9127adb7d3c"
},
{
"ImportPath": "github.com/vincent-petithory/dataurl",
"Rev": "9a301d65acbb728fcc3ace14f45f511a4cfeea9c"
},
{
"ImportPath": "go4.org/errorutil",
"Rev": "03efcb870d84809319ea509714dd6d19a1498483"
},
{
"ImportPath": "golang.org/x/crypto/cast5",
"Rev": "5dc8cb4b8a8eb076cbb5a06bc3b8682c15bdbbd3"
@@ -89,49 +87,25 @@
"ImportPath": "golang.org/x/crypto/openpgp",
"Rev": "5dc8cb4b8a8eb076cbb5a06bc3b8682c15bdbbd3"
},
{
"ImportPath": "golang.org/x/crypto/ripemd160",
"Rev": "5dc8cb4b8a8eb076cbb5a06bc3b8682c15bdbbd3"
},
{
"ImportPath": "golang.org/x/crypto/ssh/terminal",
"Rev": "5dc8cb4b8a8eb076cbb5a06bc3b8682c15bdbbd3"
},
{
"ImportPath": "golang.org/x/net/context",
"Rev": "6acef71eb69611914f7a30939ea9f6e194c78172"
"Rev": "fb93926129b8ec0056f2f458b1f519654814edf0"
},
{
"ImportPath": "golang.org/x/net/http2",
"Rev": "6acef71eb69611914f7a30939ea9f6e194c78172"
"Rev": "fb93926129b8ec0056f2f458b1f519654814edf0"
},
{
"ImportPath": "golang.org/x/net/internal/timeseries",
"Rev": "6acef71eb69611914f7a30939ea9f6e194c78172"
"Rev": "fb93926129b8ec0056f2f458b1f519654814edf0"
},
{
"ImportPath": "golang.org/x/net/trace",
"Rev": "6acef71eb69611914f7a30939ea9f6e194c78172"
},
{
"ImportPath": "golang.org/x/oauth2",
"Rev": "c30abeebd1a0c41f6e0d9ad6d26fd9daf8aff7d6"
},
{
"ImportPath": "google.golang.org/cloud/compute/metadata",
"Rev": "3fb76bc404703eddc53d1e407eaa665ab3881362"
},
{
"ImportPath": "google.golang.org/cloud/internal",
"Rev": "3fb76bc404703eddc53d1e407eaa665ab3881362"
"Rev": "fb93926129b8ec0056f2f458b1f519654814edf0"
},
{
"ImportPath": "google.golang.org/grpc",
"Rev": "986912c9070a060e59e0f28ac7c87f89f72b9388"
},
{
"ImportPath": "gopkg.in/check.v1",
"Rev": "4f90aeace3a26ad7021961c297b22c42160c7b25"
"Rev": "8eeecf2291de9d171d0b1392a27ff3975679f4f5"
},
{
"ImportPath": "gopkg.in/yaml.v2",

49
vendor/github.com/alecthomas/units/bytes_test.go generated vendored Normal file
View File

@@ -0,0 +1,49 @@
package units
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestBase2BytesString(t *testing.T) {
assert.Equal(t, Base2Bytes(0).String(), "0B")
assert.Equal(t, Base2Bytes(1025).String(), "1KiB1B")
assert.Equal(t, Base2Bytes(1048577).String(), "1MiB1B")
}
func TestParseBase2Bytes(t *testing.T) {
n, err := ParseBase2Bytes("0B")
assert.NoError(t, err)
assert.Equal(t, 0, int(n))
n, err = ParseBase2Bytes("1KB")
assert.NoError(t, err)
assert.Equal(t, 1024, int(n))
n, err = ParseBase2Bytes("1MB1KB25B")
assert.NoError(t, err)
assert.Equal(t, 1049625, int(n))
n, err = ParseBase2Bytes("1.5MB")
assert.NoError(t, err)
assert.Equal(t, 1572864, int(n))
}
func TestMetricBytesString(t *testing.T) {
assert.Equal(t, MetricBytes(0).String(), "0B")
assert.Equal(t, MetricBytes(1001).String(), "1KB1B")
assert.Equal(t, MetricBytes(1001025).String(), "1MB1KB25B")
}
func TestParseMetricBytes(t *testing.T) {
n, err := ParseMetricBytes("0B")
assert.NoError(t, err)
assert.Equal(t, 0, int(n))
n, err = ParseMetricBytes("1KB1B")
assert.NoError(t, err)
assert.Equal(t, 1001, int(n))
n, err = ParseMetricBytes("1MB1KB25B")
assert.NoError(t, err)
assert.Equal(t, 1001025, int(n))
n, err = ParseMetricBytes("1.5MB")
assert.NoError(t, err)
assert.Equal(t, 1500000, int(n))
}

257
vendor/github.com/coreos/go-semver/semver/semver.go generated vendored Normal file
View File

@@ -0,0 +1,257 @@
// 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 NewVersion(version string) (*Version, error) {
v := Version{}
v.Metadata = splitOff(&version, "+")
v.PreRelease = PreRelease(splitOff(&version, "-"))
dotParts := strings.SplitN(version, ".", 3)
if len(dotParts) != 3 {
return nil, errors.New(fmt.Sprintf("%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 nil, err
}
}
v.Major = parsed[0]
v.Minor = parsed[1]
v.Patch = parsed[2]
return &v, nil
}
func Must(v *Version, err error) *Version {
if err != nil {
panic(err)
}
return v
}
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
}
vv, err := NewVersion(data)
if err != nil {
return err
}
*v = *vv
return nil
}
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")
}
vv, err := NewVersion(string(data[1 : l-1]))
if err != nil {
return err
}
*v = *vv
return nil
}
func (v Version) LessThan(versionB Version) bool {
versionA := v
cmp := recursiveCompare(versionA.Slice(), versionB.Slice())
if cmp == 0 {
cmp = preReleaseCompare(versionA, versionB)
}
if cmp == -1 {
return true
}
return false
}
/* Slice converts the comparable parts of the semver into a slice of strings */
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 prelease, 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 {
// Handle slice length disparity.
if len(versionA) == 0 {
// Nothing to compare too, so we return 0
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
}
// 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 = ""
}

View File

@@ -0,0 +1,330 @@
// 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 (
"bytes"
"encoding/json"
"errors"
"math/rand"
"reflect"
"testing"
"time"
"gopkg.in/yaml.v2"
)
type fixture struct {
GreaterVersion string
LesserVersion string
}
var fixtures = []fixture{
fixture{"0.0.0", "0.0.0-foo"},
fixture{"0.0.1", "0.0.0"},
fixture{"1.0.0", "0.9.9"},
fixture{"0.10.0", "0.9.0"},
fixture{"0.99.0", "0.10.0"},
fixture{"2.0.0", "1.2.3"},
fixture{"0.0.0", "0.0.0-foo"},
fixture{"0.0.1", "0.0.0"},
fixture{"1.0.0", "0.9.9"},
fixture{"0.10.0", "0.9.0"},
fixture{"0.99.0", "0.10.0"},
fixture{"2.0.0", "1.2.3"},
fixture{"0.0.0", "0.0.0-foo"},
fixture{"0.0.1", "0.0.0"},
fixture{"1.0.0", "0.9.9"},
fixture{"0.10.0", "0.9.0"},
fixture{"0.99.0", "0.10.0"},
fixture{"2.0.0", "1.2.3"},
fixture{"1.2.3", "1.2.3-asdf"},
fixture{"1.2.3", "1.2.3-4"},
fixture{"1.2.3", "1.2.3-4-foo"},
fixture{"1.2.3-5-foo", "1.2.3-5"},
fixture{"1.2.3-5", "1.2.3-4"},
fixture{"1.2.3-5-foo", "1.2.3-5-Foo"},
fixture{"3.0.0", "2.7.2+asdf"},
fixture{"3.0.0+foobar", "2.7.2"},
fixture{"1.2.3-a.10", "1.2.3-a.5"},
fixture{"1.2.3-a.b", "1.2.3-a.5"},
fixture{"1.2.3-a.b", "1.2.3-a"},
fixture{"1.2.3-a.b.c.10.d.5", "1.2.3-a.b.c.5.d.100"},
fixture{"1.0.0", "1.0.0-rc.1"},
fixture{"1.0.0-rc.2", "1.0.0-rc.1"},
fixture{"1.0.0-rc.1", "1.0.0-beta.11"},
fixture{"1.0.0-beta.11", "1.0.0-beta.2"},
fixture{"1.0.0-beta.2", "1.0.0-beta"},
fixture{"1.0.0-beta", "1.0.0-alpha.beta"},
fixture{"1.0.0-alpha.beta", "1.0.0-alpha.1"},
fixture{"1.0.0-alpha.1", "1.0.0-alpha"},
}
func TestCompare(t *testing.T) {
for _, v := range fixtures {
gt, err := NewVersion(v.GreaterVersion)
if err != nil {
t.Error(err)
}
lt, err := NewVersion(v.LesserVersion)
if err != nil {
t.Error(err)
}
if gt.LessThan(*lt) == true {
t.Errorf("%s should not be less than %s", gt, lt)
}
}
}
func testString(t *testing.T, orig string, version *Version) {
if orig != version.String() {
t.Errorf("%s != %s", orig, version)
}
}
func TestString(t *testing.T) {
for _, v := range fixtures {
gt, err := NewVersion(v.GreaterVersion)
if err != nil {
t.Error(err)
}
testString(t, v.GreaterVersion, gt)
lt, err := NewVersion(v.LesserVersion)
if err != nil {
t.Error(err)
}
testString(t, v.LesserVersion, lt)
}
}
func shuffleStringSlice(src []string) []string {
dest := make([]string, len(src))
rand.Seed(time.Now().Unix())
perm := rand.Perm(len(src))
for i, v := range perm {
dest[v] = src[i]
}
return dest
}
func TestSort(t *testing.T) {
sortedVersions := []string{"1.0.0", "1.0.2", "1.2.0", "3.1.1"}
unsortedVersions := shuffleStringSlice(sortedVersions)
semvers := []*Version{}
for _, v := range unsortedVersions {
sv, err := NewVersion(v)
if err != nil {
t.Fatal(err)
}
semvers = append(semvers, sv)
}
Sort(semvers)
for idx, sv := range semvers {
if sv.String() != sortedVersions[idx] {
t.Fatalf("incorrect sort at index %v", idx)
}
}
}
func TestBumpMajor(t *testing.T) {
version, _ := NewVersion("1.0.0")
version.BumpMajor()
if version.Major != 2 {
t.Fatalf("bumping major on 1.0.0 resulted in %v", version)
}
version, _ = NewVersion("1.5.2")
version.BumpMajor()
if version.Minor != 0 && version.Patch != 0 {
t.Fatalf("bumping major on 1.5.2 resulted in %v", version)
}
version, _ = NewVersion("1.0.0+build.1-alpha.1")
version.BumpMajor()
if version.PreRelease != "" && version.PreRelease != "" {
t.Fatalf("bumping major on 1.0.0+build.1-alpha.1 resulted in %v", version)
}
}
func TestBumpMinor(t *testing.T) {
version, _ := NewVersion("1.0.0")
version.BumpMinor()
if version.Major != 1 {
t.Fatalf("bumping minor on 1.0.0 resulted in %v", version)
}
if version.Minor != 1 {
t.Fatalf("bumping major on 1.0.0 resulted in %v", version)
}
version, _ = NewVersion("1.0.0+build.1-alpha.1")
version.BumpMinor()
if version.PreRelease != "" && version.PreRelease != "" {
t.Fatalf("bumping major on 1.0.0+build.1-alpha.1 resulted in %v", version)
}
}
func TestBumpPatch(t *testing.T) {
version, _ := NewVersion("1.0.0")
version.BumpPatch()
if version.Major != 1 {
t.Fatalf("bumping minor on 1.0.0 resulted in %v", version)
}
if version.Minor != 0 {
t.Fatalf("bumping major on 1.0.0 resulted in %v", version)
}
if version.Patch != 1 {
t.Fatalf("bumping major on 1.0.0 resulted in %v", version)
}
version, _ = NewVersion("1.0.0+build.1-alpha.1")
version.BumpPatch()
if version.PreRelease != "" && version.PreRelease != "" {
t.Fatalf("bumping major on 1.0.0+build.1-alpha.1 resulted in %v", version)
}
}
func TestMust(t *testing.T) {
tests := []struct {
versionStr string
version *Version
recov interface{}
}{
{
versionStr: "1.0.0",
version: &Version{Major: 1},
},
{
versionStr: "version number",
recov: errors.New("version number is not in dotted-tri format"),
},
}
for _, tt := range tests {
func() {
defer func() {
recov := recover()
if !reflect.DeepEqual(tt.recov, recov) {
t.Fatalf("incorrect panic for %q: want %v, got %v", tt.versionStr, tt.recov, recov)
}
}()
version := Must(NewVersion(tt.versionStr))
if !reflect.DeepEqual(tt.version, version) {
t.Fatalf("incorrect version for %q: want %+v, got %+v", tt.versionStr, tt.version, version)
}
}()
}
}
type fixtureJSON struct {
GreaterVersion *Version
LesserVersion *Version
}
func TestJSON(t *testing.T) {
fj := make([]fixtureJSON, len(fixtures))
for i, v := range fixtures {
var err error
fj[i].GreaterVersion, err = NewVersion(v.GreaterVersion)
if err != nil {
t.Fatal(err)
}
fj[i].LesserVersion, err = NewVersion(v.LesserVersion)
if err != nil {
t.Fatal(err)
}
}
fromStrings, err := json.Marshal(fixtures)
if err != nil {
t.Fatal(err)
}
fromVersions, err := json.Marshal(fj)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(fromStrings, fromVersions) {
t.Errorf("Expected: %s", fromStrings)
t.Errorf("Unexpected: %s", fromVersions)
}
fromJson := make([]fixtureJSON, 0, len(fj))
err = json.Unmarshal(fromStrings, &fromJson)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(fromJson, fj) {
t.Error("Expected: ", fj)
t.Error("Unexpected: ", fromJson)
}
}
func TestYAML(t *testing.T) {
document, err := yaml.Marshal(fixtures)
if err != nil {
t.Fatal(err)
}
expected := make([]fixtureJSON, len(fixtures))
for i, v := range fixtures {
var err error
expected[i].GreaterVersion, err = NewVersion(v.GreaterVersion)
if err != nil {
t.Fatal(err)
}
expected[i].LesserVersion, err = NewVersion(v.LesserVersion)
if err != nil {
t.Fatal(err)
}
}
fromYAML := make([]fixtureJSON, 0, len(fixtures))
err = yaml.Unmarshal(document, &fromYAML)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(fromYAML, expected) {
t.Error("Expected: ", expected)
t.Error("Unexpected: ", fromYAML)
}
}
func TestBadInput(t *testing.T) {
bad := []string{
"1.2",
"1.2.3x",
"0x1.3.4",
"-1.2.3",
"1.2.3.4",
}
for _, b := range bad {
if _, err := NewVersion(b); err == nil {
t.Error("Improperly accepted value: ", b)
}
}
}

38
vendor/github.com/coreos/go-semver/semver/sort.go generated vendored Normal file
View File

@@ -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))
}

73
vendor/github.com/coreos/ignition/config/append.go generated vendored Normal file
View File

@@ -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 config
import (
"reflect"
"github.com/coreos/ignition/config/types"
)
// Append appends newConfig to oldConfig and returns the result. Appending one
// config to another is accomplished by iterating over every field in the
// config structure, appending slices, recursively appending structs, and
// overwriting old values with new values for all other types.
func Append(oldConfig, newConfig types.Config) types.Config {
vOld := reflect.ValueOf(oldConfig)
vNew := reflect.ValueOf(newConfig)
vResult := appendStruct(vOld, vNew)
return vResult.Interface().(types.Config)
}
// appendStruct is an internal helper function to AppendConfig. Given two values
// of structures (assumed to be the same type), recursively iterate over every
// field in the struct, appending slices, recursively appending structs, and
// overwriting old values with the new for all other types. Individual fields
// are able to override their merge strategy using the "merge" tag. Accepted
// values are "new" or "old": "new" uses the new value, "old" uses the old
// value. These are currently only used for "ignition.config" and
// "ignition.version".
func appendStruct(vOld, vNew reflect.Value) reflect.Value {
tOld := vOld.Type()
vRes := reflect.New(tOld)
for i := 0; i < tOld.NumField(); i++ {
vfOld := vOld.Field(i)
vfNew := vNew.Field(i)
vfRes := vRes.Elem().Field(i)
switch tOld.Field(i).Tag.Get("merge") {
case "old":
vfRes.Set(vfOld)
continue
case "new":
vfRes.Set(vfNew)
continue
}
switch vfOld.Type().Kind() {
case reflect.Struct:
vfRes.Set(appendStruct(vfOld, vfNew))
case reflect.Slice:
vfRes.Set(reflect.AppendSlice(vfOld, vfNew))
default:
vfRes.Set(vfNew)
}
}
return vRes.Elem()
}

197
vendor/github.com/coreos/ignition/config/append_test.go generated vendored Normal file
View File

@@ -0,0 +1,197 @@
// 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 config
import (
"reflect"
"testing"
"github.com/coreos/ignition/config/types"
)
func TestAppend(t *testing.T) {
type in struct {
oldConfig types.Config
newConfig types.Config
}
type out struct {
config types.Config
}
tests := []struct {
in in
out out
}{
// empty
{
in: in{
oldConfig: types.Config{},
newConfig: types.Config{},
},
out: out{config: types.Config{}},
},
// merge tags
{
in: in{
oldConfig: types.Config{},
newConfig: types.Config{
Ignition: types.Ignition{
Version: types.IgnitionVersion{Major: 2},
},
},
},
out: out{config: types.Config{}},
},
{
in: in{
oldConfig: types.Config{
Ignition: types.Ignition{
Version: types.IgnitionVersion{Major: 2},
},
},
newConfig: types.Config{},
},
out: out{config: types.Config{
Ignition: types.Ignition{
Version: types.IgnitionVersion{Major: 2},
},
}},
},
{
in: in{
oldConfig: types.Config{},
newConfig: types.Config{
Ignition: types.Ignition{
Config: types.IgnitionConfig{
Replace: &types.ConfigReference{},
},
},
},
},
out: out{config: types.Config{
Ignition: types.Ignition{
Config: types.IgnitionConfig{
Replace: &types.ConfigReference{},
},
},
}},
},
{
in: in{
oldConfig: types.Config{
Ignition: types.Ignition{
Config: types.IgnitionConfig{
Replace: &types.ConfigReference{},
},
},
},
newConfig: types.Config{},
},
out: out{config: types.Config{}},
},
// old
{
in: in{
oldConfig: types.Config{
Storage: types.Storage{
Disks: []types.Disk{
{
WipeTable: true,
Partitions: []types.Partition{
{Number: 1},
{Number: 2},
},
},
},
},
},
newConfig: types.Config{},
},
out: out{config: types.Config{
Storage: types.Storage{
Disks: []types.Disk{
{
WipeTable: true,
Partitions: []types.Partition{
{Number: 1},
{Number: 2},
},
},
},
},
}},
},
// new
{
in: in{
oldConfig: types.Config{},
newConfig: types.Config{
Systemd: types.Systemd{
Units: []types.SystemdUnit{
{Name: "test1.service"},
{Name: "test2.service"},
},
},
},
},
out: out{config: types.Config{
Systemd: types.Systemd{
Units: []types.SystemdUnit{
{Name: "test1.service"},
{Name: "test2.service"},
},
},
}},
},
// both
{
in: in{
oldConfig: types.Config{
Passwd: types.Passwd{
Users: []types.User{
{Name: "oldUser"},
},
},
},
newConfig: types.Config{
Passwd: types.Passwd{
Users: []types.User{
{Name: "newUser"},
},
},
},
},
out: out{config: types.Config{
Passwd: types.Passwd{
Users: []types.User{
{Name: "oldUser"},
{Name: "newUser"},
},
},
}},
},
}
for i, test := range tests {
config := Append(test.in.oldConfig, test.in.newConfig)
if !reflect.DeepEqual(test.out.config, config) {
t.Errorf("#%d: bad config: want %+v, got %+v", i, test.out.config, config)
}
}
}

View File

@@ -17,12 +17,15 @@
package config
import (
"bytes"
"compress/gzip"
"io/ioutil"
"strings"
"unicode"
)
func isCloudConfig(userdata []byte) bool {
header := strings.SplitN(string(userdata), "\n", 2)[0]
header := strings.SplitN(string(decompressIfGzipped(userdata)), "\n", 2)[0]
// Trim trailing whitespaces
header = strings.TrimRightFunc(header, unicode.IsSpace)
@@ -31,6 +34,20 @@ func isCloudConfig(userdata []byte) bool {
}
func isScript(userdata []byte) bool {
header := strings.SplitN(string(userdata), "\n", 2)[0]
header := strings.SplitN(string(decompressIfGzipped(userdata)), "\n", 2)[0]
return strings.HasPrefix(header, "#!")
}
func decompressIfGzipped(data []byte) []byte {
if reader, err := gzip.NewReader(bytes.NewReader(data)); err == nil {
uncompressedData, err := ioutil.ReadAll(reader)
reader.Close()
if err == nil {
return uncompressedData
} else {
return data
}
} else {
return data
}
}

101
vendor/github.com/coreos/ignition/config/config.go generated vendored Normal file
View File

@@ -0,0 +1,101 @@
// 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 config
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/coreos/ignition/config/types"
"github.com/coreos/ignition/config/v1"
"go4.org/errorutil"
)
var (
ErrCloudConfig = errors.New("not a config (found coreos-cloudconfig)")
ErrEmpty = errors.New("not a config (empty)")
ErrScript = errors.New("not a config (found coreos-cloudinit script)")
ErrDeprecated = errors.New("config format deprecated")
)
func Parse(rawConfig []byte) (types.Config, error) {
switch majorVersion(rawConfig) {
case 1:
config, err := ParseFromV1(rawConfig)
if err != nil {
return types.Config{}, err
}
return config, ErrDeprecated
default:
return ParseFromLatest(rawConfig)
}
}
func ParseFromLatest(rawConfig []byte) (config types.Config, err error) {
if err = json.Unmarshal(rawConfig, &config); err == nil {
err = config.Ignition.Version.AssertValid()
} else if isEmpty(rawConfig) {
err = ErrEmpty
} else if isCloudConfig(rawConfig) {
err = ErrCloudConfig
} else if isScript(rawConfig) {
err = ErrScript
}
if serr, ok := err.(*json.SyntaxError); ok {
line, col, highlight := errorutil.HighlightBytePosition(bytes.NewReader(rawConfig), serr.Offset)
err = fmt.Errorf("error at line %d, column %d\n%s%v", line, col, highlight, err)
}
return
}
func ParseFromV1(rawConfig []byte) (types.Config, error) {
config, err := v1.Parse(rawConfig)
if err != nil {
return types.Config{}, err
}
return TranslateFromV1(config)
}
func majorVersion(rawConfig []byte) int64 {
var composite struct {
Version *int `json:"ignitionVersion"`
Ignition struct {
Version *types.IgnitionVersion `json:"version"`
} `json:"ignition"`
}
if json.Unmarshal(rawConfig, &composite) != nil {
return 0
}
var major int64
if composite.Ignition.Version != nil {
major = composite.Ignition.Version.Major
} else if composite.Version != nil {
major = int64(*composite.Version)
}
return major
}
func isEmpty(userdata []byte) bool {
return len(userdata) == 0
}

102
vendor/github.com/coreos/ignition/config/config_test.go generated vendored Normal file
View File

@@ -0,0 +1,102 @@
// 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 config
import (
"reflect"
"testing"
"github.com/coreos/ignition/config/types"
v1 "github.com/coreos/ignition/config/v1"
)
func TestParse(t *testing.T) {
type in struct {
config []byte
}
type out struct {
config types.Config
err error
}
tests := []struct {
in in
out out
}{
{
in: in{config: []byte(`{"ignitionVersion": 1}`)},
out: out{err: ErrDeprecated},
},
{
in: in{config: []byte(`{"ignition": {"version": "1.0.0"}}`)},
out: out{err: v1.ErrVersion},
},
{
in: in{config: []byte(`{"ignition": {"version": "2.0.0"}}`)},
out: out{config: types.Config{Ignition: types.Ignition{Version: types.IgnitionVersion{Major: 2, Minor: 0}}}},
},
{
in: in{config: []byte(`{"ignition": {"version": "2.1.0"}}`)},
out: out{err: types.ErrNewVersion},
},
{
in: in{config: []byte(`{}`)},
out: out{err: types.ErrOldVersion},
},
{
in: in{config: []byte{}},
out: out{err: ErrEmpty},
},
{
in: in{config: []byte("#cloud-config")},
out: out{err: ErrCloudConfig},
},
{
in: in{config: []byte("#cloud-config ")},
out: out{err: ErrCloudConfig},
},
{
in: in{config: []byte("#cloud-config\n\r")},
out: out{err: ErrCloudConfig},
},
{
in: in{config: []byte{0x1f, 0x8b, 0x08, 0x00, 0x03, 0xd6, 0x79, 0x56,
0x00, 0x03, 0x53, 0x4e, 0xce, 0xc9, 0x2f, 0x4d, 0xd1, 0x4d, 0xce,
0xcf, 0x4b, 0xcb, 0x4c, 0xe7, 0x02, 0x00, 0x05, 0x56, 0xb3, 0xb8,
0x0e, 0x00, 0x00, 0x00}},
out: out{err: ErrCloudConfig},
},
{
in: in{config: []byte("#!/bin/sh")},
out: out{err: ErrScript},
},
{
in: in{config: []byte{0x1f, 0x8b, 0x08, 0x00, 0x48, 0xda, 0x79, 0x56,
0x00, 0x03, 0x53, 0x56, 0xd4, 0x4f, 0xca, 0xcc, 0xd3, 0x2f, 0xce,
0xe0, 0x02, 0x00, 0x1d, 0x9d, 0xfb, 0x04, 0x0a, 0x00, 0x00, 0x00}},
out: out{err: ErrScript},
},
}
for i, test := range tests {
config, err := Parse(test.in.config)
if test.out.err != err {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if test.out.err == nil && !reflect.DeepEqual(test.out.config, config) {
t.Errorf("#%d: bad config: want %+v, got %+v", i, test.out.config, config)
}
}
}

163
vendor/github.com/coreos/ignition/config/translate.go generated vendored Normal file
View File

@@ -0,0 +1,163 @@
// 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 config
import (
"fmt"
"github.com/coreos/ignition/config/types"
v1 "github.com/coreos/ignition/config/v1/types"
"github.com/vincent-petithory/dataurl"
)
func TranslateFromV1(old v1.Config) (types.Config, error) {
config := types.Config{
Ignition: types.Ignition{
Version: types.IgnitionVersion{Major: 2},
},
}
for _, oldDisk := range old.Storage.Disks {
disk := types.Disk{
Device: types.Path(oldDisk.Device),
WipeTable: oldDisk.WipeTable,
}
for _, oldPartition := range oldDisk.Partitions {
disk.Partitions = append(disk.Partitions, types.Partition{
Label: types.PartitionLabel(oldPartition.Label),
Number: oldPartition.Number,
Size: types.PartitionDimension(oldPartition.Size),
Start: types.PartitionDimension(oldPartition.Start),
TypeGUID: types.PartitionTypeGUID(oldPartition.TypeGUID),
})
}
config.Storage.Disks = append(config.Storage.Disks, disk)
}
for _, oldArray := range old.Storage.Arrays {
array := types.Raid{
Name: oldArray.Name,
Level: oldArray.Level,
Spares: oldArray.Spares,
}
for _, oldDevice := range oldArray.Devices {
array.Devices = append(array.Devices, types.Path(oldDevice))
}
config.Storage.Arrays = append(config.Storage.Arrays, array)
}
for i, oldFilesystem := range old.Storage.Filesystems {
filesystem := types.Filesystem{
Name: fmt.Sprintf("_translate-filesystem-%d", i),
Mount: &types.FilesystemMount{
Device: types.Path(oldFilesystem.Device),
Format: types.FilesystemFormat(oldFilesystem.Format),
},
}
if oldFilesystem.Create != nil {
filesystem.Mount.Create = &types.FilesystemCreate{
Force: oldFilesystem.Create.Force,
Options: types.MkfsOptions(oldFilesystem.Create.Options),
}
}
config.Storage.Filesystems = append(config.Storage.Filesystems, filesystem)
for _, oldFile := range oldFilesystem.Files {
file := types.File{
Filesystem: filesystem.Name,
Path: types.Path(oldFile.Path),
Contents: types.FileContents{
Source: types.Url{
Scheme: "data",
Opaque: "," + dataurl.EscapeString(oldFile.Contents),
},
},
Mode: types.FileMode(oldFile.Mode),
User: types.FileUser{Id: oldFile.Uid},
Group: types.FileGroup{Id: oldFile.Gid},
}
config.Storage.Files = append(config.Storage.Files, file)
}
}
for _, oldUnit := range old.Systemd.Units {
unit := types.SystemdUnit{
Name: types.SystemdUnitName(oldUnit.Name),
Enable: oldUnit.Enable,
Mask: oldUnit.Mask,
Contents: oldUnit.Contents,
}
for _, oldDropIn := range oldUnit.DropIns {
unit.DropIns = append(unit.DropIns, types.SystemdUnitDropIn{
Name: types.SystemdUnitDropInName(oldDropIn.Name),
Contents: oldDropIn.Contents,
})
}
config.Systemd.Units = append(config.Systemd.Units, unit)
}
for _, oldUnit := range old.Networkd.Units {
config.Networkd.Units = append(config.Networkd.Units, types.NetworkdUnit{
Name: types.NetworkdUnitName(oldUnit.Name),
Contents: oldUnit.Contents,
})
}
for _, oldUser := range old.Passwd.Users {
user := types.User{
Name: oldUser.Name,
PasswordHash: oldUser.PasswordHash,
SSHAuthorizedKeys: oldUser.SSHAuthorizedKeys,
}
if oldUser.Create != nil {
user.Create = &types.UserCreate{
Uid: oldUser.Create.Uid,
GECOS: oldUser.Create.GECOS,
Homedir: oldUser.Create.Homedir,
NoCreateHome: oldUser.Create.NoCreateHome,
PrimaryGroup: oldUser.Create.PrimaryGroup,
Groups: oldUser.Create.Groups,
NoUserGroup: oldUser.Create.NoUserGroup,
System: oldUser.Create.System,
NoLogInit: oldUser.Create.NoLogInit,
Shell: oldUser.Create.Shell,
}
}
config.Passwd.Users = append(config.Passwd.Users, user)
}
for _, oldGroup := range old.Passwd.Groups {
config.Passwd.Groups = append(config.Passwd.Groups, types.Group{
Name: oldGroup.Name,
Gid: oldGroup.Gid,
PasswordHash: oldGroup.PasswordHash,
System: oldGroup.System,
})
}
return config, nil
}

View File

@@ -0,0 +1,425 @@
// 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 config
import (
"reflect"
"testing"
"github.com/coreos/ignition/config/types"
v1 "github.com/coreos/ignition/config/v1/types"
)
func TestTranslateFromV1(t *testing.T) {
type in struct {
config v1.Config
}
type out struct {
config types.Config
err error
}
tests := []struct {
in in
out out
}{
{
in: in{},
out: out{config: types.Config{Ignition: types.Ignition{Version: types.IgnitionVersion{Major: 2}}}},
},
{
in: in{config: v1.Config{
Storage: v1.Storage{
Disks: []v1.Disk{
{
Device: v1.Path("/dev/sda"),
WipeTable: true,
Partitions: []v1.Partition{
{
Label: v1.PartitionLabel("ROOT"),
Number: 7,
Size: v1.PartitionDimension(100),
Start: v1.PartitionDimension(50),
TypeGUID: "HI",
},
{
Label: v1.PartitionLabel("DATA"),
Number: 12,
Size: v1.PartitionDimension(1000),
Start: v1.PartitionDimension(300),
TypeGUID: "LO",
},
},
},
{
Device: v1.Path("/dev/sdb"),
WipeTable: true,
},
},
Arrays: []v1.Raid{
{
Name: "fast",
Level: "raid0",
Devices: []v1.Path{v1.Path("/dev/sdc"), v1.Path("/dev/sdd")},
Spares: 2,
},
{
Name: "durable",
Level: "raid1",
Devices: []v1.Path{v1.Path("/dev/sde"), v1.Path("/dev/sdf")},
Spares: 3,
},
},
Filesystems: []v1.Filesystem{
{
Device: v1.Path("/dev/disk/by-partlabel/ROOT"),
Format: v1.FilesystemFormat("btrfs"),
Create: &v1.FilesystemCreate{
Force: true,
Options: v1.MkfsOptions([]string{"-L", "ROOT"}),
},
Files: []v1.File{
{
Path: v1.Path("/opt/file1"),
Contents: "file1",
Mode: v1.FileMode(0664),
Uid: 500,
Gid: 501,
},
{
Path: v1.Path("/opt/file2"),
Contents: "file2",
Mode: v1.FileMode(0644),
Uid: 502,
Gid: 503,
},
},
},
{
Device: v1.Path("/dev/disk/by-partlabel/DATA"),
Format: v1.FilesystemFormat("ext4"),
Files: []v1.File{
{
Path: v1.Path("/opt/file3"),
Contents: "file3",
Mode: v1.FileMode(0400),
Uid: 1000,
Gid: 1001,
},
},
},
},
},
}},
out: out{config: types.Config{
Ignition: types.Ignition{Version: types.IgnitionVersion{Major: 2}},
Storage: types.Storage{
Disks: []types.Disk{
{
Device: types.Path("/dev/sda"),
WipeTable: true,
Partitions: []types.Partition{
{
Label: types.PartitionLabel("ROOT"),
Number: 7,
Size: types.PartitionDimension(100),
Start: types.PartitionDimension(50),
TypeGUID: "HI",
},
{
Label: types.PartitionLabel("DATA"),
Number: 12,
Size: types.PartitionDimension(1000),
Start: types.PartitionDimension(300),
TypeGUID: "LO",
},
},
},
{
Device: types.Path("/dev/sdb"),
WipeTable: true,
},
},
Arrays: []types.Raid{
{
Name: "fast",
Level: "raid0",
Devices: []types.Path{types.Path("/dev/sdc"), types.Path("/dev/sdd")},
Spares: 2,
},
{
Name: "durable",
Level: "raid1",
Devices: []types.Path{types.Path("/dev/sde"), types.Path("/dev/sdf")},
Spares: 3,
},
},
Filesystems: []types.Filesystem{
{
Name: "_translate-filesystem-0",
Mount: &types.FilesystemMount{
Device: types.Path("/dev/disk/by-partlabel/ROOT"),
Format: types.FilesystemFormat("btrfs"),
Create: &types.FilesystemCreate{
Force: true,
Options: types.MkfsOptions([]string{"-L", "ROOT"}),
},
},
},
{
Name: "_translate-filesystem-1",
Mount: &types.FilesystemMount{
Device: types.Path("/dev/disk/by-partlabel/DATA"),
Format: types.FilesystemFormat("ext4"),
},
},
},
Files: []types.File{
{
Filesystem: "_translate-filesystem-0",
Path: types.Path("/opt/file1"),
Contents: types.FileContents{
Source: types.Url{
Scheme: "data",
Opaque: ",file1",
},
},
Mode: types.FileMode(0664),
User: types.FileUser{Id: 500},
Group: types.FileGroup{Id: 501},
},
{
Filesystem: "_translate-filesystem-0",
Path: types.Path("/opt/file2"),
Contents: types.FileContents{
Source: types.Url{
Scheme: "data",
Opaque: ",file2",
},
},
Mode: types.FileMode(0644),
User: types.FileUser{Id: 502},
Group: types.FileGroup{Id: 503},
},
{
Filesystem: "_translate-filesystem-1",
Path: types.Path("/opt/file3"),
Contents: types.FileContents{
Source: types.Url{
Scheme: "data",
Opaque: ",file3",
},
},
Mode: types.FileMode(0400),
User: types.FileUser{Id: 1000},
Group: types.FileGroup{Id: 1001},
},
},
},
}},
},
{
in: in{v1.Config{
Systemd: v1.Systemd{
Units: []v1.SystemdUnit{
{
Name: "test1.service",
Enable: true,
Contents: "test1 contents",
DropIns: []v1.SystemdUnitDropIn{
{
Name: "conf1.conf",
Contents: "conf1 contents",
},
{
Name: "conf2.conf",
Contents: "conf2 contents",
},
},
},
{
Name: "test2.service",
Mask: true,
Contents: "test2 contents",
},
},
},
}},
out: out{config: types.Config{
Ignition: types.Ignition{Version: types.IgnitionVersion{Major: 2}},
Systemd: types.Systemd{
Units: []types.SystemdUnit{
{
Name: "test1.service",
Enable: true,
Contents: "test1 contents",
DropIns: []types.SystemdUnitDropIn{
{
Name: "conf1.conf",
Contents: "conf1 contents",
},
{
Name: "conf2.conf",
Contents: "conf2 contents",
},
},
},
{
Name: "test2.service",
Mask: true,
Contents: "test2 contents",
},
},
},
}},
},
{
in: in{v1.Config{
Networkd: v1.Networkd{
Units: []v1.NetworkdUnit{
{
Name: "test1.network",
Contents: "test1 contents",
},
{
Name: "test2.network",
Contents: "test2 contents",
},
},
},
}},
out: out{config: types.Config{
Ignition: types.Ignition{Version: types.IgnitionVersion{Major: 2}},
Networkd: types.Networkd{
Units: []types.NetworkdUnit{
{
Name: "test1.network",
Contents: "test1 contents",
},
{
Name: "test2.network",
Contents: "test2 contents",
},
},
},
}},
},
{
in: in{v1.Config{
Passwd: v1.Passwd{
Users: []v1.User{
{
Name: "user 1",
PasswordHash: "password 1",
SSHAuthorizedKeys: []string{"key1", "key2"},
},
{
Name: "user 2",
PasswordHash: "password 2",
SSHAuthorizedKeys: []string{"key3", "key4"},
Create: &v1.UserCreate{
Uid: func(i uint) *uint { return &i }(123),
GECOS: "gecos",
Homedir: "/home/user 2",
NoCreateHome: true,
PrimaryGroup: "wheel",
Groups: []string{"wheel", "plugdev"},
NoUserGroup: true,
System: true,
NoLogInit: true,
Shell: "/bin/zsh",
},
},
{
Name: "user 3",
PasswordHash: "password 3",
SSHAuthorizedKeys: []string{"key5", "key6"},
Create: &v1.UserCreate{},
},
},
Groups: []v1.Group{
{
Name: "group 1",
Gid: func(i uint) *uint { return &i }(1000),
PasswordHash: "password 1",
System: true,
},
{
Name: "group 2",
PasswordHash: "password 2",
},
},
},
}},
out: out{config: types.Config{
Ignition: types.Ignition{Version: types.IgnitionVersion{Major: 2}},
Passwd: types.Passwd{
Users: []types.User{
{
Name: "user 1",
PasswordHash: "password 1",
SSHAuthorizedKeys: []string{"key1", "key2"},
},
{
Name: "user 2",
PasswordHash: "password 2",
SSHAuthorizedKeys: []string{"key3", "key4"},
Create: &types.UserCreate{
Uid: func(i uint) *uint { return &i }(123),
GECOS: "gecos",
Homedir: "/home/user 2",
NoCreateHome: true,
PrimaryGroup: "wheel",
Groups: []string{"wheel", "plugdev"},
NoUserGroup: true,
System: true,
NoLogInit: true,
Shell: "/bin/zsh",
},
},
{
Name: "user 3",
PasswordHash: "password 3",
SSHAuthorizedKeys: []string{"key5", "key6"},
Create: &types.UserCreate{},
},
},
Groups: []types.Group{
{
Name: "group 1",
Gid: func(i uint) *uint { return &i }(1000),
PasswordHash: "password 1",
System: true,
},
{
Name: "group 2",
PasswordHash: "password 2",
},
},
},
}},
},
}
for i, test := range tests {
config, err := TranslateFromV1(test.in.config)
if test.out.err != err {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if !reflect.DeepEqual(test.out.config, config) {
t.Errorf("#%d: bad config: want %+v, got %+v", i, test.out.config, config)
}
}
}

View File

@@ -0,0 +1,54 @@
// 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 (
"encoding/json"
"errors"
)
var (
ErrCompressionInvalid = errors.New("invalid compression method")
)
type Compression string
func (c *Compression) UnmarshalYAML(unmarshal func(interface{}) error) error {
return c.unmarshal(unmarshal)
}
func (c *Compression) UnmarshalJSON(data []byte) error {
return c.unmarshal(func(tc interface{}) error {
return json.Unmarshal(data, tc)
})
}
func (c *Compression) unmarshal(unmarshal func(interface{}) error) error {
var tc string
if err := unmarshal(&tc); err != nil {
return err
}
*c = Compression(tc)
return c.assertValid()
}
func (c Compression) assertValid() error {
switch c {
case "gzip":
default:
return ErrCompressionInvalid
}
return nil
}

View File

@@ -0,0 +1,34 @@
// 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 (
"github.com/coreos/go-semver/semver"
)
var (
MaxVersion = semver.Version{
Major: 2,
Minor: 0,
}
)
type Config struct {
Ignition Ignition `json:"ignition" yaml:"ignition"`
Storage Storage `json:"storage,omitempty" yaml:"storage"`
Systemd Systemd `json:"systemd,omitempty" yaml:"systemd"`
Networkd Networkd `json:"networkd,omitempty" yaml:"networkd"`
Passwd Passwd `json:"passwd,omitempty" yaml:"passwd"`
}

View File

@@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc.
// 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.
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package config
package types
import (
"encoding/json"
@@ -20,7 +20,7 @@ import (
)
type Disk struct {
Device DevicePath `json:"device,omitempty" yaml:"device"`
Device Path `json:"device,omitempty" yaml:"device"`
WipeTable bool `json:"wipeTable,omitempty" yaml:"wipe_table"`
Partitions []Partition `json:"partitions,omitempty" yaml:"partitions"`
}

78
vendor/github.com/coreos/ignition/config/types/file.go generated vendored Normal file
View File

@@ -0,0 +1,78 @@
// 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 (
"encoding/json"
"errors"
"os"
)
var (
ErrFileIllegalMode = errors.New("illegal file mode")
)
type File struct {
Filesystem string `json:"filesystem,omitempty" yaml:"filesystem"`
Path Path `json:"path,omitempty" yaml:"path"`
Contents FileContents `json:"contents,omitempty" yaml:"contents"`
Mode FileMode `json:"mode,omitempty" yaml:"mode"`
User FileUser `json:"user,omitempty" yaml:"uid"`
Group FileGroup `json:"group,omitempty" yaml:"gid"`
}
type FileUser struct {
Id int `json:"id,omitempty" yaml:"id"`
}
type FileGroup struct {
Id int `json:"id,omitempty" yaml:"id"`
}
type FileContents struct {
Compression Compression `json:"compression,omitempty" yaml:"compression"`
Source Url `json:"source,omitempty" yaml:"source"`
Verification Verification `json:"verification,omitempty" yaml:"verification"`
}
type FileMode os.FileMode
func (m *FileMode) UnmarshalYAML(unmarshal func(interface{}) error) error {
return m.unmarshal(unmarshal)
}
func (m *FileMode) UnmarshalJSON(data []byte) error {
return m.unmarshal(func(tm interface{}) error {
return json.Unmarshal(data, tm)
})
}
type fileMode FileMode
func (m *FileMode) unmarshal(unmarshal func(interface{}) error) error {
tm := fileMode(*m)
if err := unmarshal(&tm); err != nil {
return err
}
*m = FileMode(tm)
return m.assertValid()
}
func (m FileMode) assertValid() error {
if (m &^ 07777) != 0 {
return ErrFileIllegalMode
}
return nil
}

View File

@@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc.
// 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.
@@ -12,14 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package config
package types
import (
"encoding/json"
"reflect"
"testing"
"github.com/coreos/ignition/third_party/github.com/go-yaml/yaml"
"github.com/go-yaml/yaml"
)
func TestFileModeUnmarshalJSON(t *testing.T) {

View File

@@ -0,0 +1,181 @@
// 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 (
"encoding/json"
"errors"
)
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")
)
type Filesystem struct {
Name string `json:"name,omitempty" yaml:"name"`
Mount *FilesystemMount `json:"mount,omitempty" yaml:"mount"`
Path Path `json:"path,omitempty" yaml:"path"`
}
type FilesystemMount struct {
Device Path `json:"device,omitempty" yaml:"device"`
Format FilesystemFormat `json:"format,omitempty" yaml:"format"`
Create *FilesystemCreate `json:"create,omitempty" yaml:"create"`
}
type FilesystemCreate struct {
Force bool `json:"force,omitempty" yaml:"force"`
Options MkfsOptions `json:"options,omitempty" yaml:"options"`
}
func (f *Filesystem) UnmarshalYAML(unmarshal func(interface{}) error) error {
return f.unmarshal(unmarshal)
}
func (f *Filesystem) UnmarshalJSON(data []byte) error {
return f.unmarshal(func(tf interface{}) error {
return json.Unmarshal(data, tf)
})
}
type filesystem Filesystem
func (f *Filesystem) unmarshal(unmarshal func(interface{}) error) error {
tf := filesystem(*f)
if err := unmarshal(&tf); err != nil {
return err
}
*f = Filesystem(tf)
return f.assertValid()
}
func (f Filesystem) assertValid() error {
hasMount := false
hasPath := false
if f.Mount != nil {
hasMount = true
if err := f.Mount.assertValid(); err != nil {
return err
}
}
if len(f.Path) != 0 {
hasPath = true
if err := f.Path.assertValid(); err != nil {
return err
}
}
if !hasMount && !hasPath {
return ErrFilesystemNoMountPath
} else if hasMount && hasPath {
return ErrFilesystemMountAndPath
}
return nil
}
func (f *FilesystemMount) UnmarshalYAML(unmarshal func(interface{}) error) error {
return f.unmarshal(unmarshal)
}
func (f *FilesystemMount) UnmarshalJSON(data []byte) error {
return f.unmarshal(func(tf interface{}) error {
return json.Unmarshal(data, tf)
})
}
type filesystemMount FilesystemMount
func (f *FilesystemMount) unmarshal(unmarshal func(interface{}) error) error {
tf := filesystemMount(*f)
if err := unmarshal(&tf); err != nil {
return err
}
*f = FilesystemMount(tf)
return f.assertValid()
}
func (f FilesystemMount) assertValid() error {
if err := f.Device.assertValid(); err != nil {
return err
}
if err := f.Format.assertValid(); err != nil {
return err
}
return nil
}
type FilesystemFormat string
func (f *FilesystemFormat) UnmarshalYAML(unmarshal func(interface{}) error) error {
return f.unmarshal(unmarshal)
}
func (f *FilesystemFormat) UnmarshalJSON(data []byte) error {
return f.unmarshal(func(tf interface{}) error {
return json.Unmarshal(data, tf)
})
}
type filesystemFormat FilesystemFormat
func (f *FilesystemFormat) unmarshal(unmarshal func(interface{}) error) error {
tf := filesystemFormat(*f)
if err := unmarshal(&tf); err != nil {
return err
}
*f = FilesystemFormat(tf)
return f.assertValid()
}
func (f FilesystemFormat) assertValid() error {
switch f {
case "ext4", "btrfs", "xfs":
return nil
default:
return ErrFilesystemInvalidFormat
}
}
type MkfsOptions []string
func (o *MkfsOptions) UnmarshalYAML(unmarshal func(interface{}) error) error {
return o.unmarshal(unmarshal)
}
func (o *MkfsOptions) UnmarshalJSON(data []byte) error {
return o.unmarshal(func(to interface{}) error {
return json.Unmarshal(data, to)
})
}
type mkfsOptions MkfsOptions
func (o *MkfsOptions) unmarshal(unmarshal func(interface{}) error) error {
to := mkfsOptions(*o)
if err := unmarshal(&to); err != nil {
return err
}
*o = MkfsOptions(to)
return o.assertValid()
}
func (o MkfsOptions) assertValid() error {
return nil
}

View File

@@ -0,0 +1,309 @@
// 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 (
"encoding/json"
"reflect"
"testing"
"github.com/go-yaml/yaml"
)
func TestFilesystemFormatUnmarshalJSON(t *testing.T) {
type in struct {
data string
}
type out struct {
format FilesystemFormat
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `"ext4"`},
out: out{format: FilesystemFormat("ext4")},
},
{
in: in{data: `"bad"`},
out: out{format: FilesystemFormat("bad"), err: ErrFilesystemInvalidFormat},
},
}
for i, test := range tests {
var format FilesystemFormat
err := json.Unmarshal([]byte(test.in.data), &format)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if !reflect.DeepEqual(test.out.format, format) {
t.Errorf("#%d: bad format: want %#v, got %#v", i, test.out.format, format)
}
}
}
func TestFilesystemFormatUnmarshalYAML(t *testing.T) {
type in struct {
data string
}
type out struct {
format FilesystemFormat
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `"ext4"`},
out: out{format: FilesystemFormat("ext4")},
},
{
in: in{data: `"bad"`},
out: out{format: FilesystemFormat("bad"), err: ErrFilesystemInvalidFormat},
},
}
for i, test := range tests {
var format FilesystemFormat
err := yaml.Unmarshal([]byte(test.in.data), &format)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if !reflect.DeepEqual(test.out.format, format) {
t.Errorf("#%d: bad format: want %#v, got %#v", i, test.out.format, format)
}
}
}
func TestFilesystemFormatAssertValid(t *testing.T) {
type in struct {
format FilesystemFormat
}
type out struct {
err error
}
tests := []struct {
in in
out out
}{
{
in: in{format: FilesystemFormat("ext4")},
out: out{},
},
{
in: in{format: FilesystemFormat("btrfs")},
out: out{},
},
{
in: in{format: FilesystemFormat("")},
out: out{err: ErrFilesystemInvalidFormat},
},
}
for i, test := range tests {
err := test.in.format.assertValid()
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
}
}
func TestMkfsOptionsUnmarshalJSON(t *testing.T) {
type in struct {
data string
}
type out struct {
options MkfsOptions
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `["--label=ROOT"]`},
out: out{options: MkfsOptions([]string{"--label=ROOT"})},
},
}
for i, test := range tests {
var options MkfsOptions
err := json.Unmarshal([]byte(test.in.data), &options)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if !reflect.DeepEqual(test.out.options, options) {
t.Errorf("#%d: bad format: want %#v, got %#v", i, test.out.options, options)
}
}
}
func TestMkfsOptionsUnmarshalYAML(t *testing.T) {
type in struct {
data string
}
type out struct {
options MkfsOptions
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `["--label=ROOT"]`},
out: out{options: MkfsOptions([]string{"--label=ROOT"})},
},
}
for i, test := range tests {
var options MkfsOptions
err := yaml.Unmarshal([]byte(test.in.data), &options)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if !reflect.DeepEqual(test.out.options, options) {
t.Errorf("#%d: bad device: want %#v, got %#v", i, test.out.options, options)
}
}
}
func TestFilesystemUnmarshalJSON(t *testing.T) {
type in struct {
data string
}
type out struct {
filesystem Filesystem
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `{"mount": {"device": "/foo", "format": "ext4"}}`},
out: out{filesystem: Filesystem{Mount: &FilesystemMount{Device: "/foo", Format: "ext4"}}},
},
{
in: in{data: `{"mount": {"format": "ext4"}}`},
out: out{err: ErrPathRelative},
},
}
for i, test := range tests {
var filesystem Filesystem
err := json.Unmarshal([]byte(test.in.data), &filesystem)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if !reflect.DeepEqual(test.out.filesystem, filesystem) {
t.Errorf("#%d: bad filesystem: want %#v, got %#v", i, test.out.filesystem, filesystem)
}
}
}
func TestFilesystemUnmarshalYAML(t *testing.T) {
type in struct {
data string
}
type out struct {
filesystem Filesystem
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: "mount:\n device: /foo\n format: ext4"},
out: out{filesystem: Filesystem{Mount: &FilesystemMount{Device: "/foo", Format: "ext4"}}},
},
{
in: in{data: "mount:\n format: ext4"},
out: out{err: ErrPathRelative},
},
}
for i, test := range tests {
var filesystem Filesystem
err := yaml.Unmarshal([]byte(test.in.data), &filesystem)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if !reflect.DeepEqual(test.out.filesystem, filesystem) {
t.Errorf("#%d: bad filesystem: want %#v, got %#v", i, test.out.filesystem, filesystem)
}
}
}
func TestFilesystemAssertValid(t *testing.T) {
type in struct {
filesystem Filesystem
}
type out struct {
err error
}
tests := []struct {
in in
out out
}{
{
in: in{filesystem: Filesystem{Mount: &FilesystemMount{Device: "/foo", Format: "ext4"}}},
out: out{},
},
{
in: in{filesystem: Filesystem{Mount: &FilesystemMount{Device: "/foo"}}},
out: out{err: ErrFilesystemInvalidFormat},
},
{
in: in{filesystem: Filesystem{Mount: &FilesystemMount{Format: "ext4"}}},
out: out{err: ErrPathRelative},
},
{
in: in{filesystem: Filesystem{Path: Path("/mount")}},
out: out{},
},
{
in: in{filesystem: Filesystem{Path: Path("mount")}},
out: out{err: ErrPathRelative},
},
{
in: in{filesystem: Filesystem{Path: Path("/mount"), Mount: &FilesystemMount{Device: "/foo", Format: "ext4"}}},
out: out{err: ErrFilesystemMountAndPath},
},
{
in: in{filesystem: Filesystem{}},
out: out{err: ErrFilesystemNoMountPath},
},
}
for i, test := range tests {
err := test.in.filesystem.assertValid()
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc.
// 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.
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package config
package types
type Group struct {
Name string `json:"name,omitempty" yaml:"name"`

81
vendor/github.com/coreos/ignition/config/types/hash.go generated vendored Normal file
View File

@@ -0,0 +1,81 @@
// 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"
"encoding/json"
"errors"
"strings"
)
var (
ErrHashMalformed = errors.New("malformed hash specifier")
ErrHashWrongSize = errors.New("incorrect size for hash sum")
ErrHashUnrecognized = errors.New("unrecognized hash function")
)
type Hash struct {
Function string
Sum string
}
func (h *Hash) UnmarshalYAML(unmarshal func(interface{}) error) error {
return h.unmarshal(unmarshal)
}
func (h *Hash) UnmarshalJSON(data []byte) error {
return h.unmarshal(func(th interface{}) error {
return json.Unmarshal(data, th)
})
}
func (h Hash) MarshalJSON() ([]byte, error) {
return []byte(`"` + h.Function + "-" + h.Sum + `"`), nil
}
func (h *Hash) unmarshal(unmarshal func(interface{}) error) error {
var th string
if err := unmarshal(&th); err != nil {
return err
}
parts := strings.SplitN(th, "-", 2)
if len(parts) != 2 {
return ErrHashMalformed
}
h.Function = parts[0]
h.Sum = parts[1]
return h.assertValid()
}
func (h Hash) assertValid() error {
var hash crypto.Hash
switch h.Function {
case "sha512":
hash = crypto.SHA512
default:
return ErrHashUnrecognized
}
if len(h.Sum) != hex.EncodedLen(hash.Size()) {
return ErrHashWrongSize
}
return nil
}

View File

@@ -0,0 +1,131 @@
// 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 (
"encoding/json"
"reflect"
"testing"
"github.com/go-yaml/yaml"
)
func TestHashUnmarshalJSON(t *testing.T) {
type in struct {
data string
}
type out struct {
hash Hash
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `"sha512-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"`},
out: out{hash: Hash{Function: "sha512", Sum: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"}},
},
{
in: in{data: `"xor01234567"`},
out: out{err: ErrHashMalformed},
},
}
for i, test := range tests {
var hash Hash
err := json.Unmarshal([]byte(test.in.data), &hash)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if !reflect.DeepEqual(test.out.hash, hash) {
t.Errorf("#%d: bad hash: want %+v, got %+v", i, test.out.hash, hash)
}
}
}
func TestHashUnmarshalYAML(t *testing.T) {
type in struct {
data string
}
type out struct {
hash Hash
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `sha512-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef`},
out: out{hash: Hash{Function: "sha512", Sum: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"}},
},
{
in: in{data: `xor01234567`},
out: out{err: ErrHashMalformed},
},
}
for i, test := range tests {
var hash Hash
err := yaml.Unmarshal([]byte(test.in.data), &hash)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if !reflect.DeepEqual(test.out.hash, hash) {
t.Errorf("#%d: bad hash: want %+v, got %+v", i, test.out.hash, hash)
}
}
}
func TestHashAssertValid(t *testing.T) {
type in struct {
hash Hash
}
type out struct {
err error
}
tests := []struct {
in in
out out
}{
{
in: in{hash: Hash{}},
out: out{err: ErrHashUnrecognized},
},
{
in: in{hash: Hash{Function: "xor"}},
out: out{err: ErrHashUnrecognized},
},
{
in: in{hash: Hash{Function: "sha512", Sum: "123"}},
out: out{err: ErrHashWrongSize},
},
{
in: in{hash: Hash{Function: "sha512", Sum: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"}},
out: out{},
},
}
for i, test := range tests {
err := test.in.hash.assertValid()
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
}
}

View File

@@ -0,0 +1,77 @@
// 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 (
"encoding/json"
"errors"
"github.com/coreos/go-semver/semver"
)
var (
ErrOldVersion = errors.New("incorrect config version (too old)")
ErrNewVersion = errors.New("incorrect config version (too new)")
)
type Ignition struct {
Version IgnitionVersion `json:"version,omitempty" yaml:"version" merge:"old"`
Config IgnitionConfig `json:"config,omitempty" yaml:"config" merge:"new"`
}
type IgnitionConfig struct {
Append []ConfigReference `json:"append,omitempty" yaml:"append"`
Replace *ConfigReference `json:"replace,omitempty" yaml:"replace"`
}
type ConfigReference struct {
Source Url `json:"source,omitempty" yaml:"source"`
Verification Verification `json:"verification,omitempty" yaml:"verification"`
}
type IgnitionVersion semver.Version
func (v *IgnitionVersion) UnmarshalYAML(unmarshal func(interface{}) error) error {
return v.unmarshal(unmarshal)
}
func (v *IgnitionVersion) UnmarshalJSON(data []byte) error {
return v.unmarshal(func(tv interface{}) error {
return json.Unmarshal(data, tv)
})
}
func (v IgnitionVersion) MarshalJSON() ([]byte, error) {
return semver.Version(v).MarshalJSON()
}
func (v *IgnitionVersion) unmarshal(unmarshal func(interface{}) error) error {
tv := semver.Version(*v)
if err := unmarshal(&tv); err != nil {
return err
}
*v = IgnitionVersion(tv)
return nil
}
func (v IgnitionVersion) AssertValid() error {
if MaxVersion.Major > v.Major {
return ErrOldVersion
}
if MaxVersion.LessThan(semver.Version(v)) {
return ErrNewVersion
}
return nil
}

View File

@@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc.
// 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.
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package config
package types
type Networkd struct {
Units []NetworkdUnit `json:"units,omitempty" yaml:"units"`

View File

@@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc.
// 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.
@@ -12,14 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package config
package types
import (
"encoding/json"
"fmt"
"regexp"
"github.com/coreos/ignition/third_party/github.com/alecthomas/units"
"github.com/alecthomas/units"
)
type Partition struct {

View File

@@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc.
// 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.
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package config
package types
type Passwd struct {
Users []User `json:"users,omitempty" yaml:"users"`

59
vendor/github.com/coreos/ignition/config/types/path.go generated vendored Normal file
View File

@@ -0,0 +1,59 @@
// 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 (
"encoding/json"
"errors"
"path/filepath"
)
var (
ErrPathRelative = errors.New("path not absolute")
)
type Path string
func (p *Path) UnmarshalYAML(unmarshal func(interface{}) error) error {
return p.unmarshal(unmarshal)
}
func (p *Path) UnmarshalJSON(data []byte) error {
return p.unmarshal(func(td interface{}) error {
return json.Unmarshal(data, td)
})
}
func (p Path) MarshalJSON() ([]byte, error) {
return []byte(`"` + string(p) + `"`), nil
}
type path Path
func (p *Path) unmarshal(unmarshal func(interface{}) error) error {
td := path(*p)
if err := unmarshal(&td); err != nil {
return err
}
*p = Path(td)
return p.assertValid()
}
func (p Path) assertValid() error {
if !filepath.IsAbs(string(p)) {
return ErrPathRelative
}
return nil
}

View File

@@ -0,0 +1,135 @@
// 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 (
"encoding/json"
"reflect"
"testing"
"github.com/go-yaml/yaml"
)
func TestPathUnmarshalJSON(t *testing.T) {
type in struct {
data string
}
type out struct {
device Path
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `"/path"`},
out: out{device: Path("/path")},
},
{
in: in{data: `"bad"`},
out: out{device: Path("bad"), err: ErrPathRelative},
},
}
for i, test := range tests {
var device Path
err := json.Unmarshal([]byte(test.in.data), &device)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if !reflect.DeepEqual(test.out.device, device) {
t.Errorf("#%d: bad device: want %#v, got %#v", i, test.out.device, device)
}
}
}
func TestPathUnmarshalYAML(t *testing.T) {
type in struct {
data string
}
type out struct {
device Path
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `"/path"`},
out: out{device: Path("/path")},
},
{
in: in{data: `"bad"`},
out: out{device: Path("bad"), err: ErrPathRelative},
},
}
for i, test := range tests {
var device Path
err := yaml.Unmarshal([]byte(test.in.data), &device)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if !reflect.DeepEqual(test.out.device, device) {
t.Errorf("#%d: bad device: want %#v, got %#v", i, test.out.device, device)
}
}
}
func TestPathAssertValid(t *testing.T) {
type in struct {
device Path
}
type out struct {
err error
}
tests := []struct {
in in
out out
}{
{
in: in{device: Path("/good/path")},
out: out{},
},
{
in: in{device: Path("/name")},
out: out{},
},
{
in: in{device: Path("/this/is/a/fairly/long/path/to/a/device.")},
out: out{},
},
{
in: in{device: Path("/this one has spaces")},
out: out{},
},
{
in: in{device: Path("relative/path")},
out: out{err: ErrPathRelative},
},
}
for i, test := range tests {
err := test.in.device.assertValid()
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc.
// 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.
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package config
package types
import (
"encoding/json"
@@ -20,10 +20,10 @@ import (
)
type Raid struct {
Name string `json:"name" yaml:"name"`
Level string `json:"level" yaml:"level"`
Devices []DevicePath `json:"devices,omitempty" yaml:"devices"`
Spares int `json:"spares,omitempty" yaml:"spares"`
Name string `json:"name" yaml:"name"`
Level string `json:"level" yaml:"level"`
Devices []Path `json:"devices,omitempty" yaml:"devices"`
Spares int `json:"spares,omitempty" yaml:"spares"`
}
func (n *Raid) UnmarshalYAML(unmarshal func(interface{}) error) error {

View File

@@ -0,0 +1,22 @@
// 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
type Storage struct {
Disks []Disk `json:"disks,omitempty" yaml:"disks"`
Arrays []Raid `json:"raid,omitempty" yaml:"raid"`
Filesystems []Filesystem `json:"filesystems,omitempty" yaml:"filesystems"`
Files []File `json:"files,omitempty" yaml:"files"`
}

View File

@@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc.
// 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.
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package config
package types
type Systemd struct {
Units []SystemdUnit `json:"units,omitempty" yaml:"units"`

View File

@@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc.
// 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.
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package config
package types
import (
"encoding/json"

View File

@@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc.
// 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.
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package config
package types
import (
"encoding/json"
@@ -20,7 +20,7 @@ import (
"reflect"
"testing"
"github.com/coreos/ignition/third_party/github.com/go-yaml/yaml"
"github.com/go-yaml/yaml"
)
func TestSystemdUnitNameUnmarshalJSON(t *testing.T) {

52
vendor/github.com/coreos/ignition/config/types/url.go generated vendored Normal file
View File

@@ -0,0 +1,52 @@
// 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 (
"encoding/json"
"net/url"
)
type Url url.URL
func (u *Url) UnmarshalYAML(unmarshal func(interface{}) error) error {
return u.unmarshal(unmarshal)
}
func (u *Url) UnmarshalJSON(data []byte) error {
return u.unmarshal(func(tu interface{}) error {
return json.Unmarshal(data, tu)
})
}
func (u Url) MarshalJSON() ([]byte, error) {
return []byte(`"` + u.String() + `"`), nil
}
func (u *Url) unmarshal(unmarshal func(interface{}) error) error {
var tu string
if err := unmarshal(&tu); err != nil {
return err
}
pu, err := url.Parse(tu)
*u = Url(*pu)
return err
}
func (u Url) String() string {
tu := url.URL(u)
return (&tu).String()
}

View File

@@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc.
// 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.
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package config
package types
type User struct {
Name string `json:"name,omitempty" yaml:"name"`

View File

@@ -0,0 +1,19 @@
// 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
type Verification struct {
Hash *Hash `json:"hash,omitempty" yaml:"hash"`
}

View File

@@ -0,0 +1,53 @@
// 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.
// These functions are copied from github.com/coreos/coreos-cloudinit/config.
package v1
import (
"bytes"
"compress/gzip"
"io/ioutil"
"strings"
"unicode"
)
func isCloudConfig(userdata []byte) bool {
header := strings.SplitN(string(decompressIfGzipped(userdata)), "\n", 2)[0]
// Trim trailing whitespaces
header = strings.TrimRightFunc(header, unicode.IsSpace)
return (header == "#cloud-config")
}
func isScript(userdata []byte) bool {
header := strings.SplitN(string(decompressIfGzipped(userdata)), "\n", 2)[0]
return strings.HasPrefix(header, "#!")
}
func decompressIfGzipped(data []byte) []byte {
if reader, err := gzip.NewReader(bytes.NewReader(data)); err == nil {
uncompressedData, err := ioutil.ReadAll(reader)
reader.Close()
if err == nil {
return uncompressedData
} else {
return data
}
} else {
return data
}
}

View File

@@ -12,52 +12,40 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package config
package v1
import (
"bytes"
"compress/gzip"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"github.com/coreos/ignition/third_party/github.com/camlistore/camlistore/pkg/errorutil"
)
"github.com/coreos/ignition/config/v1/types"
type Config struct {
Version int `json:"ignitionVersion" yaml:"ignition_version"`
Storage Storage `json:"storage,omitempty" yaml:"storage"`
Systemd Systemd `json:"systemd,omitempty" yaml:"systemd"`
Networkd Networkd `json:"networkd,omitempty" yaml:"networkd"`
Passwd Passwd `json:"passwd,omitempty" yaml:"passwd"`
}
const (
Version = 1
"github.com/camlistore/camlistore/pkg/errorutil"
)
var (
ErrVersion = errors.New("incorrect config version")
ErrCloudConfig = errors.New("not a config (found coreos-cloudconfig)")
ErrScript = errors.New("not a config (found coreos-cloudinit script)")
ErrEmpty = errors.New("not a config (empty)")
ErrScript = errors.New("not a config (found coreos-cloudinit script)")
)
func Parse(config []byte) (cfg Config, err error) {
if err = json.Unmarshal(config, &cfg); err == nil {
if cfg.Version != Version {
func Parse(rawConfig []byte) (config types.Config, err error) {
if err = json.Unmarshal(rawConfig, &config); err == nil {
if config.Version != types.Version {
err = ErrVersion
}
} else if isCloudConfig(decompressIfGzipped(config)) {
err = ErrCloudConfig
} else if isScript(decompressIfGzipped(config)) {
err = ErrScript
} else if isEmpty(config) {
} else if isEmpty(rawConfig) {
err = ErrEmpty
} else if isCloudConfig(rawConfig) {
err = ErrCloudConfig
} else if isScript(rawConfig) {
err = ErrScript
}
if serr, ok := err.(*json.SyntaxError); ok {
line, col, highlight := errorutil.HighlightBytePosition(bytes.NewReader(config), serr.Offset)
line, col, highlight := errorutil.HighlightBytePosition(bytes.NewReader(rawConfig), serr.Offset)
err = fmt.Errorf("error at line %d, column %d\n%s%v", line, col, highlight, err)
}
@@ -67,17 +55,3 @@ func Parse(config []byte) (cfg Config, err error) {
func isEmpty(userdata []byte) bool {
return len(userdata) == 0
}
func decompressIfGzipped(data []byte) []byte {
if reader, err := gzip.NewReader(bytes.NewReader(data)); err == nil {
uncompressedData, err := ioutil.ReadAll(reader)
reader.Close()
if err == nil {
return uncompressedData
} else {
return data
}
} else {
return data
}
}

View File

@@ -12,11 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package config
package v1
import (
"reflect"
"testing"
"github.com/coreos/ignition/config/v1/types"
)
func TestParse(t *testing.T) {
@@ -24,7 +26,7 @@ func TestParse(t *testing.T) {
config []byte
}
type out struct {
config Config
config types.Config
err error
}
@@ -34,7 +36,7 @@ func TestParse(t *testing.T) {
}{
{
in: in{config: []byte(`{"ignitionVersion": 1}`)},
out: out{config: Config{Version: 1}},
out: out{config: types.Config{Version: 1}},
},
{
in: in{config: []byte(`{}`)},

View File

@@ -0,0 +1,27 @@
// 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
const (
Version = 1
)
type Config struct {
Version int `json:"ignitionVersion" yaml:"ignition_version"`
Storage Storage `json:"storage,omitempty" yaml:"storage"`
Systemd Systemd `json:"systemd,omitempty" yaml:"systemd"`
Networkd Networkd `json:"networkd,omitempty" yaml:"networkd"`
Passwd Passwd `json:"passwd,omitempty" yaml:"passwd"`
}

View File

@@ -0,0 +1,167 @@
// 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 (
"encoding/json"
"fmt"
)
type Disk struct {
Device Path `json:"device,omitempty" yaml:"device"`
WipeTable bool `json:"wipeTable,omitempty" yaml:"wipe_table"`
Partitions []Partition `json:"partitions,omitempty" yaml:"partitions"`
}
func (n *Disk) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := n.unmarshal(unmarshal); err != nil {
return err
}
if err := n.preparePartitions(); err != nil {
return err
}
return n.assertValid()
}
func (n *Disk) UnmarshalJSON(data []byte) error {
err := n.unmarshal(func(tn interface{}) error {
return json.Unmarshal(data, tn)
})
if err != nil {
return err
}
return n.assertValid()
}
type disk Disk
func (n *Disk) unmarshal(unmarshal func(interface{}) error) error {
tn := disk(*n)
if err := unmarshal(&tn); err != nil {
return err
}
*n = Disk(tn)
return nil
}
func (n Disk) assertValid() error {
// This applies to YAML (post-prepare) and JSON unmarshals equally:
if len(n.Device) == 0 {
return fmt.Errorf("disk device is required")
}
if n.partitionNumbersCollide() {
return fmt.Errorf("disk %q: partition numbers collide", n.Device)
}
if n.partitionsOverlap() {
return fmt.Errorf("disk %q: partitions overlap", n.Device)
}
if n.partitionsMisaligned() {
return fmt.Errorf("disk %q: partitions misaligned", n.Device)
}
// Disks which get to this point will likely succeed in sgdisk
return nil
}
// 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() PartitionDimension {
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
}
// preparePartitions performs some checks and potentially adjusts the partitions for alignment.
// This is only invoked when unmarshalling YAML, since there we parse human-friendly units.
func (n *Disk) preparePartitions() error {
// On the YAML side we accept human-friendly units which may require massaging.
// align partition starts
for i := range n.Partitions {
// skip automatically placed partitions
if n.Partitions[i].Start == 0 {
continue
}
// keep partitions out of the first 2048 sectors
if n.Partitions[i].Start < 2048 {
n.Partitions[i].Start = 2048
}
// toss the bottom 11 bits
n.Partitions[i].Start &= ^PartitionDimension(2048 - 1)
}
// TODO(vc): may be interesting to do something about potentially overlapping partitions
return nil
}

View File

@@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc.
// 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.
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package config
package types
import (
"encoding/json"
@@ -27,7 +27,7 @@ var (
type FileMode os.FileMode
type File struct {
Path string `json:"path,omitempty" yaml:"path"`
Path Path `json:"path,omitempty" yaml:"path"`
Contents string `json:"contents,omitempty" yaml:"contents"`
Mode FileMode `json:"mode,omitempty" yaml:"mode"`
Uid int `json:"uid,omitempty" yaml:"uid"`

View File

@@ -0,0 +1,139 @@
// 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 (
"encoding/json"
"reflect"
"testing"
"github.com/go-yaml/yaml"
)
func TestFileModeUnmarshalJSON(t *testing.T) {
type in struct {
data string
}
type out struct {
mode FileMode
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `420`},
out: out{mode: FileMode(420)},
},
{
in: in{data: `9999`},
out: out{mode: FileMode(9999), err: ErrFileIllegalMode},
},
}
for i, test := range tests {
var mode FileMode
err := json.Unmarshal([]byte(test.in.data), &mode)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if !reflect.DeepEqual(test.out.mode, mode) {
t.Errorf("#%d: bad mode: want %#o, got %#o", i, test.out.mode, mode)
}
}
}
func TestFileModeUnmarshalYAML(t *testing.T) {
type in struct {
data string
}
type out struct {
mode FileMode
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `0644`},
out: out{mode: FileMode(0644)},
},
{
in: in{data: `0420`},
out: out{mode: FileMode(0420)},
},
{
in: in{data: `017777`},
out: out{mode: FileMode(017777), err: ErrFileIllegalMode},
},
}
for i, test := range tests {
var mode FileMode
err := yaml.Unmarshal([]byte(test.in.data), &mode)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if !reflect.DeepEqual(test.out.mode, mode) {
t.Errorf("#%d: bad mode: want %#o, got %#o", i, test.out.mode, mode)
}
}
}
func TestFileAssertValid(t *testing.T) {
type in struct {
mode FileMode
}
type out struct {
err error
}
tests := []struct {
in in
out out
}{
{
in: in{mode: FileMode(0)},
out: out{},
},
{
in: in{mode: FileMode(0644)},
out: out{},
},
{
in: in{mode: FileMode(01755)},
out: out{},
},
{
in: in{mode: FileMode(07777)},
out: out{},
},
{
in: in{mode: FileMode(010000)},
out: out{err: ErrFileIllegalMode},
},
}
for i, test := range tests {
err := test.in.mode.assertValid()
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc.
// 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.
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package config
package types
import (
"encoding/json"
@@ -20,12 +20,11 @@ import (
)
var (
ErrFilesystemRelativePath = errors.New("device path not absolute")
ErrFilesystemInvalidFormat = errors.New("invalid filesystem format")
)
type Filesystem struct {
Device DevicePath `json:"device,omitempty" yaml:"device"`
Device Path `json:"device,omitempty" yaml:"device"`
Format FilesystemFormat `json:"format,omitempty" yaml:"format"`
Create *FilesystemCreate `json:"create,omitempty" yaml:"create"`
Files []File `json:"files,omitempty" yaml:"files"`

View File

@@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc.
// 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.
@@ -12,128 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package config
package types
import (
"encoding/json"
"reflect"
"testing"
"github.com/coreos/ignition/third_party/github.com/go-yaml/yaml"
"github.com/go-yaml/yaml"
)
func TestDevicePathUnmarshalJSON(t *testing.T) {
type in struct {
data string
}
type out struct {
device DevicePath
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `"/path"`},
out: out{device: DevicePath("/path")},
},
{
in: in{data: `"bad"`},
out: out{device: DevicePath("bad"), err: ErrFilesystemRelativePath},
},
}
for i, test := range tests {
var device DevicePath
err := json.Unmarshal([]byte(test.in.data), &device)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if !reflect.DeepEqual(test.out.device, device) {
t.Errorf("#%d: bad device: want %#v, got %#v", i, test.out.device, device)
}
}
}
func TestDevicePathUnmarshalYAML(t *testing.T) {
type in struct {
data string
}
type out struct {
device DevicePath
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `"/path"`},
out: out{device: DevicePath("/path")},
},
{
in: in{data: `"bad"`},
out: out{device: DevicePath("bad"), err: ErrFilesystemRelativePath},
},
}
for i, test := range tests {
var device DevicePath
err := yaml.Unmarshal([]byte(test.in.data), &device)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if !reflect.DeepEqual(test.out.device, device) {
t.Errorf("#%d: bad device: want %#v, got %#v", i, test.out.device, device)
}
}
}
func TestDevicePathAssertValid(t *testing.T) {
type in struct {
device DevicePath
}
type out struct {
err error
}
tests := []struct {
in in
out out
}{
{
in: in{device: DevicePath("/good/path")},
out: out{},
},
{
in: in{device: DevicePath("/name")},
out: out{},
},
{
in: in{device: DevicePath("/this/is/a/fairly/long/path/to/a/device.")},
out: out{},
},
{
in: in{device: DevicePath("/this one has spaces")},
out: out{},
},
{
in: in{device: DevicePath("relative/path")},
out: out{err: ErrFilesystemRelativePath},
},
}
for i, test := range tests {
err := test.in.device.assertValid()
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
}
}
func TestFilesystemFormatUnmarshalJSON(t *testing.T) {
type in struct {
data string
@@ -319,7 +207,7 @@ func TestFilesystemUnmarshalJSON(t *testing.T) {
},
{
in: in{data: `{"format": "ext4"}`},
out: out{filesystem: Filesystem{Format: "ext4"}, err: ErrFilesystemRelativePath},
out: out{filesystem: Filesystem{Format: "ext4"}, err: ErrPathRelative},
},
}
@@ -354,7 +242,7 @@ func TestFilesystemUnmarshalYAML(t *testing.T) {
},
{
in: in{data: "format: ext4"},
out: out{filesystem: Filesystem{Format: "ext4"}, err: ErrFilesystemRelativePath},
out: out{filesystem: Filesystem{Format: "ext4"}, err: ErrPathRelative},
},
}
@@ -392,11 +280,11 @@ func TestFilesystemAssertValid(t *testing.T) {
},
{
in: in{filesystem: Filesystem{Format: "ext4"}},
out: out{err: ErrFilesystemRelativePath},
out: out{err: ErrPathRelative},
},
{
in: in{filesystem: Filesystem{}},
out: out{err: ErrFilesystemRelativePath},
out: out{err: ErrPathRelative},
},
}

View File

@@ -0,0 +1,22 @@
// 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
type Group struct {
Name string `json:"name,omitempty" yaml:"name"`
Gid *uint `json:"gid,omitempty" yaml:"gid"`
PasswordHash string `json:"passwordHash,omitempty" yaml:"password_hash"`
System bool `json:"system,omitempty" yaml:"system"`
}

View File

@@ -0,0 +1,19 @@
// 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
type Networkd struct {
Units []NetworkdUnit `json:"units,omitempty" yaml:"units"`
}

View File

@@ -0,0 +1,137 @@
// 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 (
"encoding/json"
"fmt"
"regexp"
"github.com/alecthomas/units"
)
type Partition struct {
Label PartitionLabel `json:"label,omitempty" yaml:"label"`
Number int `json:"number" yaml:"number"`
Size PartitionDimension `json:"size" yaml:"size"`
Start PartitionDimension `json:"start" yaml:"start"`
TypeGUID PartitionTypeGUID `json:"typeGuid,omitempty" yaml:"type_guid"`
}
type PartitionLabel string
func (n *PartitionLabel) UnmarshalYAML(unmarshal func(interface{}) error) error {
return n.unmarshal(unmarshal)
}
func (n *PartitionLabel) UnmarshalJSON(data []byte) error {
return n.unmarshal(func(tn interface{}) error {
return json.Unmarshal(data, tn)
})
}
type partitionLabel PartitionLabel
func (n *PartitionLabel) unmarshal(unmarshal func(interface{}) error) error {
tn := partitionLabel(*n)
if err := unmarshal(&tn); err != nil {
return err
}
*n = PartitionLabel(tn)
return n.assertValid()
}
func (n PartitionLabel) assertValid() error {
// 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(string(n)) > 36 {
return fmt.Errorf("partition labels may not exceed 36 characters")
}
return nil
}
type PartitionDimension uint64
func (n *PartitionDimension) UnmarshalYAML(unmarshal func(interface{}) error) error {
// In YAML we allow human-readable dimensions like GiB/TiB etc.
var str string
if err := unmarshal(&str); err != nil {
return err
}
b2b, err := units.ParseBase2Bytes(str) // TODO(vc): replace the units package
if err != nil {
return err
}
if b2b < 0 {
return fmt.Errorf("negative value inappropriate: %q", str)
}
// Translate bytes into sectors
sectors := (b2b / 512)
if b2b%512 != 0 {
sectors++
}
*n = PartitionDimension(uint64(sectors))
return nil
}
func (n *PartitionDimension) UnmarshalJSON(data []byte) error {
// In JSON we expect plain integral sectors.
// The YAML->JSON conversion is responsible for normalizing human units to sectors.
var pd uint64
if err := json.Unmarshal(data, &pd); err != nil {
return err
}
*n = PartitionDimension(pd)
return nil
}
type PartitionTypeGUID string
func (d *PartitionTypeGUID) UnmarshalYAML(unmarshal func(interface{}) error) error {
return d.unmarshal(unmarshal)
}
func (d *PartitionTypeGUID) UnmarshalJSON(data []byte) error {
return d.unmarshal(func(td interface{}) error {
return json.Unmarshal(data, td)
})
}
type partitionTypeGUID PartitionTypeGUID
func (d *PartitionTypeGUID) unmarshal(unmarshal func(interface{}) error) error {
td := partitionTypeGUID(*d)
if err := unmarshal(&td); err != nil {
return err
}
*d = PartitionTypeGUID(td)
return d.assertValid()
}
func (d PartitionTypeGUID) assertValid() error {
ok, err := regexp.MatchString("[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}", string(d))
if err != nil {
return fmt.Errorf("error matching type-guid regexp: %v", err)
}
if !ok {
return fmt.Errorf(`partition type-guid must have the form "01234567-89AB-CDEF-EDCB-A98765432101", got: %q`, string(d))
}
return nil
}

View File

@@ -0,0 +1,20 @@
// 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
type Passwd struct {
Users []User `json:"users,omitempty" yaml:"users"`
Groups []Group `json:"groups,omitempty" yaml:"groups"`
}

View File

@@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc.
// 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.
@@ -12,39 +12,44 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package config
package types
import (
"encoding/json"
"errors"
"path/filepath"
)
type DevicePath string
var (
ErrPathRelative = errors.New("path not absolute")
)
func (d *DevicePath) UnmarshalYAML(unmarshal func(interface{}) error) error {
type Path string
func (d *Path) UnmarshalYAML(unmarshal func(interface{}) error) error {
return d.unmarshal(unmarshal)
}
func (d *DevicePath) UnmarshalJSON(data []byte) error {
func (d *Path) UnmarshalJSON(data []byte) error {
return d.unmarshal(func(td interface{}) error {
return json.Unmarshal(data, td)
})
}
type devicePath DevicePath
type path Path
func (d *DevicePath) unmarshal(unmarshal func(interface{}) error) error {
td := devicePath(*d)
func (d *Path) unmarshal(unmarshal func(interface{}) error) error {
td := path(*d)
if err := unmarshal(&td); err != nil {
return err
}
*d = DevicePath(td)
*d = Path(td)
return d.assertValid()
}
func (d DevicePath) assertValid() error {
func (d Path) assertValid() error {
if !filepath.IsAbs(string(d)) {
return ErrFilesystemRelativePath
return ErrPathRelative
}
return nil
}

View File

@@ -0,0 +1,135 @@
// 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 (
"encoding/json"
"reflect"
"testing"
"github.com/go-yaml/yaml"
)
func TestPathUnmarshalJSON(t *testing.T) {
type in struct {
data string
}
type out struct {
device Path
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `"/path"`},
out: out{device: Path("/path")},
},
{
in: in{data: `"bad"`},
out: out{device: Path("bad"), err: ErrPathRelative},
},
}
for i, test := range tests {
var device Path
err := json.Unmarshal([]byte(test.in.data), &device)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if !reflect.DeepEqual(test.out.device, device) {
t.Errorf("#%d: bad device: want %#v, got %#v", i, test.out.device, device)
}
}
}
func TestPathUnmarshalYAML(t *testing.T) {
type in struct {
data string
}
type out struct {
device Path
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `"/path"`},
out: out{device: Path("/path")},
},
{
in: in{data: `"bad"`},
out: out{device: Path("bad"), err: ErrPathRelative},
},
}
for i, test := range tests {
var device Path
err := yaml.Unmarshal([]byte(test.in.data), &device)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if !reflect.DeepEqual(test.out.device, device) {
t.Errorf("#%d: bad device: want %#v, got %#v", i, test.out.device, device)
}
}
}
func TestPathAssertValid(t *testing.T) {
type in struct {
device Path
}
type out struct {
err error
}
tests := []struct {
in in
out out
}{
{
in: in{device: Path("/good/path")},
out: out{},
},
{
in: in{device: Path("/name")},
out: out{},
},
{
in: in{device: Path("/this/is/a/fairly/long/path/to/a/device.")},
out: out{},
},
{
in: in{device: Path("/this one has spaces")},
out: out{},
},
{
in: in{device: Path("relative/path")},
out: out{err: ErrPathRelative},
},
}
for i, test := range tests {
err := test.in.device.assertValid()
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
}
}

View File

@@ -0,0 +1,65 @@
// 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 (
"encoding/json"
"fmt"
)
type Raid struct {
Name string `json:"name" yaml:"name"`
Level string `json:"level" yaml:"level"`
Devices []Path `json:"devices,omitempty" yaml:"devices"`
Spares int `json:"spares,omitempty" yaml:"spares"`
}
func (n *Raid) UnmarshalYAML(unmarshal func(interface{}) error) error {
return n.unmarshal(unmarshal)
}
func (n *Raid) UnmarshalJSON(data []byte) error {
return n.unmarshal(func(tn interface{}) error {
return json.Unmarshal(data, tn)
})
}
type raid Raid
func (n *Raid) unmarshal(unmarshal func(interface{}) error) error {
tn := raid(*n)
if err := unmarshal(&tn); err != nil {
return err
}
*n = Raid(tn)
return n.assertValid()
}
func (n Raid) assertValid() error {
switch n.Level {
case "linear", "raid0", "0", "stripe":
if n.Spares != 0 {
return fmt.Errorf("spares unsupported for %q arrays", n.Level)
}
case "raid1", "1", "mirror":
case "raid4", "4":
case "raid5", "5":
case "raid6", "6":
case "raid10", "10":
default:
return fmt.Errorf("unrecognized raid level: %q", n.Level)
}
return nil
}

View File

@@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc.
// 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.
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package config
package types
type Storage struct {
Disks []Disk `json:"disks,omitempty" yaml:"disks"`

View File

@@ -0,0 +1,19 @@
// 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
type Systemd struct {
Units []SystemdUnit `json:"units,omitempty" yaml:"units"`
}

View File

@@ -0,0 +1,135 @@
// 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 (
"encoding/json"
"errors"
"path/filepath"
)
type SystemdUnit struct {
Name SystemdUnitName `json:"name,omitempty" yaml:"name"`
Enable bool `json:"enable,omitempty" yaml:"enable"`
Mask bool `json:"mask,omitempty" yaml:"mask"`
Contents string `json:"contents,omitempty" yaml:"contents"`
DropIns []SystemdUnitDropIn `json:"dropins,omitempty" yaml:"dropins"`
}
type SystemdUnitDropIn struct {
Name SystemdUnitDropInName `json:"name,omitempty" yaml:"name"`
Contents string `json:"contents,omitempty" yaml:"contents"`
}
type SystemdUnitName string
func (n *SystemdUnitName) UnmarshalYAML(unmarshal func(interface{}) error) error {
return n.unmarshal(unmarshal)
}
func (n *SystemdUnitName) UnmarshalJSON(data []byte) error {
return n.unmarshal(func(tn interface{}) error {
return json.Unmarshal(data, tn)
})
}
type systemdUnitName SystemdUnitName
func (n *SystemdUnitName) unmarshal(unmarshal func(interface{}) error) error {
tn := systemdUnitName(*n)
if err := unmarshal(&tn); err != nil {
return err
}
*n = SystemdUnitName(tn)
return n.assertValid()
}
func (n SystemdUnitName) assertValid() error {
switch filepath.Ext(string(n)) {
case ".service", ".socket", ".device", ".mount", ".automount", ".swap", ".target", ".path", ".timer", ".snapshot", ".slice", ".scope":
return nil
default:
return errors.New("invalid systemd unit extension")
}
}
type SystemdUnitDropInName string
func (n *SystemdUnitDropInName) UnmarshalYAML(unmarshal func(interface{}) error) error {
return n.unmarshal(unmarshal)
}
func (n *SystemdUnitDropInName) UnmarshalJSON(data []byte) error {
return n.unmarshal(func(tn interface{}) error {
return json.Unmarshal(data, tn)
})
}
type systemdUnitDropInName SystemdUnitDropInName
func (n *SystemdUnitDropInName) unmarshal(unmarshal func(interface{}) error) error {
tn := systemdUnitDropInName(*n)
if err := unmarshal(&tn); err != nil {
return err
}
*n = SystemdUnitDropInName(tn)
return n.assertValid()
}
func (n SystemdUnitDropInName) assertValid() error {
switch filepath.Ext(string(n)) {
case ".conf":
return nil
default:
return errors.New("invalid systemd unit drop-in extension")
}
}
type NetworkdUnit struct {
Name NetworkdUnitName `json:"name,omitempty" yaml:"name"`
Contents string `json:"contents,omitempty" yaml:"contents"`
}
type NetworkdUnitName string
func (n *NetworkdUnitName) UnmarshalYAML(unmarshal func(interface{}) error) error {
return n.unmarshal(unmarshal)
}
func (n *NetworkdUnitName) UnmarshalJSON(data []byte) error {
return n.unmarshal(func(tn interface{}) error {
return json.Unmarshal(data, tn)
})
}
type networkdUnitName NetworkdUnitName
func (n *NetworkdUnitName) unmarshal(unmarshal func(interface{}) error) error {
tn := networkdUnitName(*n)
if err := unmarshal(&tn); err != nil {
return err
}
*n = NetworkdUnitName(tn)
return n.assertValid()
}
func (n NetworkdUnitName) assertValid() error {
switch filepath.Ext(string(n)) {
case ".link", ".netdev", ".network":
return nil
default:
return errors.New("invalid networkd unit extension")
}
}

View File

@@ -0,0 +1,204 @@
// 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 (
"encoding/json"
"errors"
"reflect"
"testing"
"github.com/go-yaml/yaml"
)
func TestSystemdUnitNameUnmarshalJSON(t *testing.T) {
type in struct {
data string
}
type out struct {
unit SystemdUnitName
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `"test.service"`},
out: out{unit: SystemdUnitName("test.service")},
},
{
in: in{data: `"test.socket"`},
out: out{unit: SystemdUnitName("test.socket")},
},
{
in: in{data: `"test.blah"`},
out: out{err: errors.New("invalid systemd unit extension")},
},
}
for i, test := range tests {
var unit SystemdUnitName
err := json.Unmarshal([]byte(test.in.data), &unit)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if err != nil {
continue
}
if !reflect.DeepEqual(test.out.unit, unit) {
t.Errorf("#%d: bad unit: want %#v, got %#v", i, test.out.unit, unit)
}
}
}
func TestSystemdUnitNameUnmarshalYAML(t *testing.T) {
type in struct {
data string
}
type out struct {
unit SystemdUnitName
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `"test.service"`},
out: out{unit: SystemdUnitName("test.service")},
},
{
in: in{data: `"test.socket"`},
out: out{unit: SystemdUnitName("test.socket")},
},
{
in: in{data: `"test.blah"`},
out: out{err: errors.New("invalid systemd unit extension")},
},
}
for i, test := range tests {
var unit SystemdUnitName
err := yaml.Unmarshal([]byte(test.in.data), &unit)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if err != nil {
continue
}
if !reflect.DeepEqual(test.out.unit, unit) {
t.Errorf("#%d: bad unit: want %#v, got %#v", i, test.out.unit, unit)
}
}
}
func TestNetworkdUnitNameUnmarshalJSON(t *testing.T) {
type in struct {
data string
}
type out struct {
unit NetworkdUnitName
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `"test.network"`},
out: out{unit: NetworkdUnitName("test.network")},
},
{
in: in{data: `"test.link"`},
out: out{unit: NetworkdUnitName("test.link")},
},
{
in: in{data: `"test.netdev"`},
out: out{unit: NetworkdUnitName("test.netdev")},
},
{
in: in{data: `"test.blah"`},
out: out{err: errors.New("invalid networkd unit extension")},
},
}
for i, test := range tests {
var unit NetworkdUnitName
err := json.Unmarshal([]byte(test.in.data), &unit)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if err != nil {
continue
}
if !reflect.DeepEqual(test.out.unit, unit) {
t.Errorf("#%d: bad unit: want %#v, got %#v", i, test.out.unit, unit)
}
}
}
func TestNetworkdUnitNameUnmarshalYAML(t *testing.T) {
type in struct {
data string
}
type out struct {
unit NetworkdUnitName
err error
}
tests := []struct {
in in
out out
}{
{
in: in{data: `"test.network"`},
out: out{unit: NetworkdUnitName("test.network")},
},
{
in: in{data: `"test.link"`},
out: out{unit: NetworkdUnitName("test.link")},
},
{
in: in{data: `"test.netdev"`},
out: out{unit: NetworkdUnitName("test.netdev")},
},
{
in: in{data: `"test.blah"`},
out: out{err: errors.New("invalid networkd unit extension")},
},
}
for i, test := range tests {
var unit NetworkdUnitName
err := yaml.Unmarshal([]byte(test.in.data), &unit)
if !reflect.DeepEqual(test.out.err, err) {
t.Errorf("#%d: bad error: want %v, got %v", i, test.out.err, err)
}
if err != nil {
continue
}
if !reflect.DeepEqual(test.out.unit, unit) {
t.Errorf("#%d: bad unit: want %#v, got %#v", i, test.out.unit, unit)
}
}
}

View File

@@ -0,0 +1,35 @@
// 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
type User struct {
Name string `json:"name,omitempty" yaml:"name"`
PasswordHash string `json:"passwordHash,omitempty" yaml:"password_hash"`
SSHAuthorizedKeys []string `json:"sshAuthorizedKeys,omitempty" yaml:"ssh_authorized_keys"`
Create *UserCreate `json:"create,omitempty" yaml:"create"`
}
type UserCreate struct {
Uid *uint `json:"uid,omitempty" yaml:"uid"`
GECOS string `json:"gecos,omitempty" yaml:"gecos"`
Homedir string `json:"homeDir,omitempty" yaml:"home_dir"`
NoCreateHome bool `json:"noCreateHome,omitempty" yaml:"no_create_home"`
PrimaryGroup string `json:"primaryGroup,omitempty" yaml:"primary_group"`
Groups []string `json:"groups,omitempty" yaml:"groups"`
NoUserGroup bool `json:"noUserGroup,omitempty" yaml:"no_user_group"`
System bool `json:"system,omitempty" yaml:"system"`
NoLogInit bool `json:"noLogInit,omitempty" yaml:"no_log_init"`
Shell string `json:"shell,omitempty" yaml:"shell"`
}

View File

@@ -0,0 +1,5 @@
# If you manipulate the contents of third_party/, amend this accordingly.
# pkg version
github.com/alecthomas/units 6b4e7dc5e3143b85ea77909c72caf89416fc2915
github.com/camlistore/camlistore/pkg/errorutil 9106ce829629773474c689b34aacd7d3aaa99426
github.com/go-yaml/yaml 49c95bdc21843256fb6c4e0d370a05f24a0bf213

View File

@@ -0,0 +1,19 @@
Copyright (C) 2014 Alec Thomas
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,11 @@
# Units - Helpful unit multipliers and functions for Go
The goal of this package is to have functionality similar to the [time](http://golang.org/pkg/time/) package.
It allows for code like this:
```go
n, err := ParseBase2Bytes("1KB")
// n == 1024
n = units.Mebibyte * 512
```

View File

@@ -0,0 +1,83 @@
package units
// Base2Bytes is the old non-SI power-of-2 byte scale (1024 bytes in a kilobyte,
// etc.).
type Base2Bytes int64
// Base-2 byte units.
const (
Kibibyte Base2Bytes = 1024
KiB = Kibibyte
Mebibyte = Kibibyte * 1024
MiB = Mebibyte
Gibibyte = Mebibyte * 1024
GiB = Gibibyte
Tebibyte = Gibibyte * 1024
TiB = Tebibyte
Pebibyte = Tebibyte * 1024
PiB = Pebibyte
Exbibyte = Pebibyte * 1024
EiB = Exbibyte
)
var (
bytesUnitMap = MakeUnitMap("iB", "B", 1024)
oldBytesUnitMap = MakeUnitMap("B", "B", 1024)
)
// ParseBase2Bytes supports both iB and B in base-2 multipliers. That is, KB
// and KiB are both 1024.
func ParseBase2Bytes(s string) (Base2Bytes, error) {
n, err := ParseUnit(s, bytesUnitMap)
if err != nil {
n, err = ParseUnit(s, oldBytesUnitMap)
}
return Base2Bytes(n), err
}
func (b Base2Bytes) String() string {
return ToString(int64(b), 1024, "iB", "B")
}
var (
metricBytesUnitMap = MakeUnitMap("B", "B", 1000)
)
// MetricBytes are SI byte units (1000 bytes in a kilobyte).
type MetricBytes SI
// SI base-10 byte units.
const (
Kilobyte MetricBytes = 1000
KB = Kilobyte
Megabyte = Kilobyte * 1000
MB = Megabyte
Gigabyte = Megabyte * 1000
GB = Gigabyte
Terabyte = Gigabyte * 1000
TB = Terabyte
Petabyte = Terabyte * 1000
PB = Petabyte
Exabyte = Petabyte * 1000
EB = Exabyte
)
// ParseMetricBytes parses base-10 metric byte units. That is, KB is 1000 bytes.
func ParseMetricBytes(s string) (MetricBytes, error) {
n, err := ParseUnit(s, metricBytesUnitMap)
return MetricBytes(n), err
}
func (m MetricBytes) String() string {
return ToString(int64(m), 1000, "B", "B")
}
// ParseStrictBytes supports both iB and B suffixes for base 2 and metric,
// respectively. That is, KiB represents 1024 and KB represents 1000.
func ParseStrictBytes(s string) (int64, error) {
n, err := ParseUnit(s, bytesUnitMap)
if err != nil {
n, err = ParseUnit(s, metricBytesUnitMap)
}
return int64(n), err
}

View File

@@ -0,0 +1,13 @@
// Package units provides helpful unit multipliers and functions for Go.
//
// The goal of this package is to have functionality similar to the time [1] package.
//
//
// [1] http://golang.org/pkg/time/
//
// It allows for code like this:
//
// n, err := ParseBase2Bytes("1KB")
// // n == 1024
// n = units.Mebibyte * 512
package units

View File

@@ -0,0 +1,26 @@
package units
// SI units.
type SI int64
// SI unit multiples.
const (
Kilo SI = 1000
Mega = Kilo * 1000
Giga = Mega * 1000
Tera = Giga * 1000
Peta = Tera * 1000
Exa = Peta * 1000
)
func MakeUnitMap(suffix, shortSuffix string, scale int64) map[string]float64 {
return map[string]float64{
shortSuffix: 1,
"K" + suffix: float64(scale),
"M" + suffix: float64(scale * scale),
"G" + suffix: float64(scale * scale * scale),
"T" + suffix: float64(scale * scale * scale * scale),
"P" + suffix: float64(scale * scale * scale * scale * scale),
"E" + suffix: float64(scale * scale * scale * scale * scale * scale),
}
}

View File

@@ -0,0 +1,138 @@
package units
import (
"errors"
"fmt"
"strings"
)
var (
siUnits = []string{"", "K", "M", "G", "T", "P", "E"}
)
func ToString(n int64, scale int64, suffix, baseSuffix string) string {
mn := len(siUnits)
out := make([]string, mn)
for i, m := range siUnits {
if n%scale != 0 || i == 0 && n == 0 {
s := suffix
if i == 0 {
s = baseSuffix
}
out[mn-1-i] = fmt.Sprintf("%d%s%s", n%scale, m, s)
}
n /= scale
if n == 0 {
break
}
}
return strings.Join(out, "")
}
// Below code ripped straight from http://golang.org/src/pkg/time/format.go?s=33392:33438#L1123
var errLeadingInt = errors.New("units: bad [0-9]*") // never printed
// leadingInt consumes the leading [0-9]* from s.
func leadingInt(s string) (x int64, rem string, err error) {
i := 0
for ; i < len(s); i++ {
c := s[i]
if c < '0' || c > '9' {
break
}
if x >= (1<<63-10)/10 {
// overflow
return 0, "", errLeadingInt
}
x = x*10 + int64(c) - '0'
}
return x, s[i:], nil
}
func ParseUnit(s string, unitMap map[string]float64) (int64, error) {
// [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
orig := s
f := float64(0)
neg := false
// Consume [-+]?
if s != "" {
c := s[0]
if c == '-' || c == '+' {
neg = c == '-'
s = s[1:]
}
}
// Special case: if all that is left is "0", this is zero.
if s == "0" {
return 0, nil
}
if s == "" {
return 0, errors.New("units: invalid " + orig)
}
for s != "" {
g := float64(0) // this element of the sequence
var x int64
var err error
// The next character must be [0-9.]
if !(s[0] == '.' || ('0' <= s[0] && s[0] <= '9')) {
return 0, errors.New("units: invalid " + orig)
}
// Consume [0-9]*
pl := len(s)
x, s, err = leadingInt(s)
if err != nil {
return 0, errors.New("units: invalid " + orig)
}
g = float64(x)
pre := pl != len(s) // whether we consumed anything before a period
// Consume (\.[0-9]*)?
post := false
if s != "" && s[0] == '.' {
s = s[1:]
pl := len(s)
x, s, err = leadingInt(s)
if err != nil {
return 0, errors.New("units: invalid " + orig)
}
scale := 1.0
for n := pl - len(s); n > 0; n-- {
scale *= 10
}
g += float64(x) / scale
post = pl != len(s)
}
if !pre && !post {
// no digits (e.g. ".s" or "-.s")
return 0, errors.New("units: invalid " + orig)
}
// Consume unit.
i := 0
for ; i < len(s); i++ {
c := s[i]
if c == '.' || ('0' <= c && c <= '9') {
break
}
}
u := s[:i]
s = s[i:]
unit, ok := unitMap[u]
if !ok {
return 0, errors.New("units: unknown unit " + u + " in " + orig)
}
f += g * unit
}
if neg {
f = -f
}
if f < float64(-1<<63) || f > float64(1<<63-1) {
return 0, errors.New("units: overflow parsing unit")
}
return int64(f), nil
}

View File

@@ -0,0 +1,58 @@
/*
Copyright 2011 Google 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 errorutil helps make better error messages.
package errorutil
import (
"bufio"
"bytes"
"fmt"
"io"
"strings"
)
// HighlightBytePosition takes a reader and the location in bytes of a parse
// error (for instance, from json.SyntaxError.Offset) and returns the line, column,
// and pretty-printed context around the error with an arrow indicating the exact
// position of the syntax error.
func HighlightBytePosition(f io.Reader, pos int64) (line, col int, highlight string) {
line = 1
br := bufio.NewReader(f)
lastLine := ""
thisLine := new(bytes.Buffer)
for n := int64(0); n < pos; n++ {
b, err := br.ReadByte()
if err != nil {
break
}
if b == '\n' {
lastLine = thisLine.String()
thisLine.Reset()
line++
col = 1
} else {
col++
thisLine.WriteByte(b)
}
}
if line > 1 {
highlight += fmt.Sprintf("%5d: %s\n", line-1, lastLine)
}
highlight += fmt.Sprintf("%5d: %s\n", line, thisLine.String())
highlight += fmt.Sprintf("%s^\n", strings.Repeat(" ", col+5))
return
}

View File

@@ -0,0 +1,7 @@
# If you manipulate the contents of vendor/, amend this accordingly.
# pkg version
github.com/alecthomas/units 6b4e7dc5e3143b85ea77909c72caf89416fc2915
github.com/coreos/go-semver 294930c1e79c64e7dbe360054274fdad492c8cf5
github.com/go-yaml/yaml 49c95bdc21843256fb6c4e0d370a05f24a0bf213
github.com/vincent-petithory/dataurl 9a301d65acbb728fcc3ace14f45f511a4cfeea9c
go4.org/errorutil 03efcb870d84809319ea509714dd6d19a1498483

View File

@@ -0,0 +1,19 @@
Copyright (C) 2014 Alec Thomas
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,11 @@
# Units - Helpful unit multipliers and functions for Go
The goal of this package is to have functionality similar to the [time](http://golang.org/pkg/time/) package.
It allows for code like this:
```go
n, err := ParseBase2Bytes("1KB")
// n == 1024
n = units.Mebibyte * 512
```

View File

@@ -0,0 +1,83 @@
package units
// Base2Bytes is the old non-SI power-of-2 byte scale (1024 bytes in a kilobyte,
// etc.).
type Base2Bytes int64
// Base-2 byte units.
const (
Kibibyte Base2Bytes = 1024
KiB = Kibibyte
Mebibyte = Kibibyte * 1024
MiB = Mebibyte
Gibibyte = Mebibyte * 1024
GiB = Gibibyte
Tebibyte = Gibibyte * 1024
TiB = Tebibyte
Pebibyte = Tebibyte * 1024
PiB = Pebibyte
Exbibyte = Pebibyte * 1024
EiB = Exbibyte
)
var (
bytesUnitMap = MakeUnitMap("iB", "B", 1024)
oldBytesUnitMap = MakeUnitMap("B", "B", 1024)
)
// ParseBase2Bytes supports both iB and B in base-2 multipliers. That is, KB
// and KiB are both 1024.
func ParseBase2Bytes(s string) (Base2Bytes, error) {
n, err := ParseUnit(s, bytesUnitMap)
if err != nil {
n, err = ParseUnit(s, oldBytesUnitMap)
}
return Base2Bytes(n), err
}
func (b Base2Bytes) String() string {
return ToString(int64(b), 1024, "iB", "B")
}
var (
metricBytesUnitMap = MakeUnitMap("B", "B", 1000)
)
// MetricBytes are SI byte units (1000 bytes in a kilobyte).
type MetricBytes SI
// SI base-10 byte units.
const (
Kilobyte MetricBytes = 1000
KB = Kilobyte
Megabyte = Kilobyte * 1000
MB = Megabyte
Gigabyte = Megabyte * 1000
GB = Gigabyte
Terabyte = Gigabyte * 1000
TB = Terabyte
Petabyte = Terabyte * 1000
PB = Petabyte
Exabyte = Petabyte * 1000
EB = Exabyte
)
// ParseMetricBytes parses base-10 metric byte units. That is, KB is 1000 bytes.
func ParseMetricBytes(s string) (MetricBytes, error) {
n, err := ParseUnit(s, metricBytesUnitMap)
return MetricBytes(n), err
}
func (m MetricBytes) String() string {
return ToString(int64(m), 1000, "B", "B")
}
// ParseStrictBytes supports both iB and B suffixes for base 2 and metric,
// respectively. That is, KiB represents 1024 and KB represents 1000.
func ParseStrictBytes(s string) (int64, error) {
n, err := ParseUnit(s, bytesUnitMap)
if err != nil {
n, err = ParseUnit(s, metricBytesUnitMap)
}
return int64(n), err
}

View File

@@ -0,0 +1,49 @@
package units
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestBase2BytesString(t *testing.T) {
assert.Equal(t, Base2Bytes(0).String(), "0B")
assert.Equal(t, Base2Bytes(1025).String(), "1KiB1B")
assert.Equal(t, Base2Bytes(1048577).String(), "1MiB1B")
}
func TestParseBase2Bytes(t *testing.T) {
n, err := ParseBase2Bytes("0B")
assert.NoError(t, err)
assert.Equal(t, 0, n)
n, err = ParseBase2Bytes("1KB")
assert.NoError(t, err)
assert.Equal(t, 1024, n)
n, err = ParseBase2Bytes("1MB1KB25B")
assert.NoError(t, err)
assert.Equal(t, 1049625, n)
n, err = ParseBase2Bytes("1.5MB")
assert.NoError(t, err)
assert.Equal(t, 1572864, n)
}
func TestMetricBytesString(t *testing.T) {
assert.Equal(t, MetricBytes(0).String(), "0B")
assert.Equal(t, MetricBytes(1001).String(), "1KB1B")
assert.Equal(t, MetricBytes(1001025).String(), "1MB1KB25B")
}
func TestParseMetricBytes(t *testing.T) {
n, err := ParseMetricBytes("0B")
assert.NoError(t, err)
assert.Equal(t, 0, n)
n, err = ParseMetricBytes("1KB1B")
assert.NoError(t, err)
assert.Equal(t, 1001, n)
n, err = ParseMetricBytes("1MB1KB25B")
assert.NoError(t, err)
assert.Equal(t, 1001025, n)
n, err = ParseMetricBytes("1.5MB")
assert.NoError(t, err)
assert.Equal(t, 1500000, n)
}

View File

@@ -0,0 +1,13 @@
// Package units provides helpful unit multipliers and functions for Go.
//
// The goal of this package is to have functionality similar to the time [1] package.
//
//
// [1] http://golang.org/pkg/time/
//
// It allows for code like this:
//
// n, err := ParseBase2Bytes("1KB")
// // n == 1024
// n = units.Mebibyte * 512
package units

View File

@@ -0,0 +1,26 @@
package units
// SI units.
type SI int64
// SI unit multiples.
const (
Kilo SI = 1000
Mega = Kilo * 1000
Giga = Mega * 1000
Tera = Giga * 1000
Peta = Tera * 1000
Exa = Peta * 1000
)
func MakeUnitMap(suffix, shortSuffix string, scale int64) map[string]float64 {
return map[string]float64{
shortSuffix: 1,
"K" + suffix: float64(scale),
"M" + suffix: float64(scale * scale),
"G" + suffix: float64(scale * scale * scale),
"T" + suffix: float64(scale * scale * scale * scale),
"P" + suffix: float64(scale * scale * scale * scale * scale),
"E" + suffix: float64(scale * scale * scale * scale * scale * scale),
}
}

View File

@@ -0,0 +1,138 @@
package units
import (
"errors"
"fmt"
"strings"
)
var (
siUnits = []string{"", "K", "M", "G", "T", "P", "E"}
)
func ToString(n int64, scale int64, suffix, baseSuffix string) string {
mn := len(siUnits)
out := make([]string, mn)
for i, m := range siUnits {
if n%scale != 0 || i == 0 && n == 0 {
s := suffix
if i == 0 {
s = baseSuffix
}
out[mn-1-i] = fmt.Sprintf("%d%s%s", n%scale, m, s)
}
n /= scale
if n == 0 {
break
}
}
return strings.Join(out, "")
}
// Below code ripped straight from http://golang.org/src/pkg/time/format.go?s=33392:33438#L1123
var errLeadingInt = errors.New("units: bad [0-9]*") // never printed
// leadingInt consumes the leading [0-9]* from s.
func leadingInt(s string) (x int64, rem string, err error) {
i := 0
for ; i < len(s); i++ {
c := s[i]
if c < '0' || c > '9' {
break
}
if x >= (1<<63-10)/10 {
// overflow
return 0, "", errLeadingInt
}
x = x*10 + int64(c) - '0'
}
return x, s[i:], nil
}
func ParseUnit(s string, unitMap map[string]float64) (int64, error) {
// [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
orig := s
f := float64(0)
neg := false
// Consume [-+]?
if s != "" {
c := s[0]
if c == '-' || c == '+' {
neg = c == '-'
s = s[1:]
}
}
// Special case: if all that is left is "0", this is zero.
if s == "0" {
return 0, nil
}
if s == "" {
return 0, errors.New("units: invalid " + orig)
}
for s != "" {
g := float64(0) // this element of the sequence
var x int64
var err error
// The next character must be [0-9.]
if !(s[0] == '.' || ('0' <= s[0] && s[0] <= '9')) {
return 0, errors.New("units: invalid " + orig)
}
// Consume [0-9]*
pl := len(s)
x, s, err = leadingInt(s)
if err != nil {
return 0, errors.New("units: invalid " + orig)
}
g = float64(x)
pre := pl != len(s) // whether we consumed anything before a period
// Consume (\.[0-9]*)?
post := false
if s != "" && s[0] == '.' {
s = s[1:]
pl := len(s)
x, s, err = leadingInt(s)
if err != nil {
return 0, errors.New("units: invalid " + orig)
}
scale := 1.0
for n := pl - len(s); n > 0; n-- {
scale *= 10
}
g += float64(x) / scale
post = pl != len(s)
}
if !pre && !post {
// no digits (e.g. ".s" or "-.s")
return 0, errors.New("units: invalid " + orig)
}
// Consume unit.
i := 0
for ; i < len(s); i++ {
c := s[i]
if c == '.' || ('0' <= c && c <= '9') {
break
}
}
u := s[:i]
s = s[i:]
unit, ok := unitMap[u]
if !ok {
return 0, errors.New("units: unknown unit " + u + " in " + orig)
}
f += g * unit
}
if neg {
f = -f
}
if f < float64(-1<<63) || f > float64(1<<63-1) {
return 0, errors.New("units: overflow parsing unit")
}
return int64(f), nil
}

Some files were not shown because too many files have changed in this diff Show More