glide.yaml: Vendor Container Linux Config transpiler

* Update and vendor ct, Ignition, and deps
This commit is contained in:
Dalton Hubble
2017-04-17 16:46:25 -07:00
parent 3f43e4ecb6
commit a948a97339
69 changed files with 3630 additions and 582 deletions

24
glide.lock generated
View File

@@ -1,38 +1,42 @@
hash: 9b6db105e9175308cb3a911161d68bbc80a5ee762bb7f08c926101cc5389b115
updated: 2017-04-10T15:46:19.530989294-07:00
hash: d7d6d80b5734d981194ff7ff4cfa150cedae4549133301d1845f110e48729543
updated: 2017-04-17T15:53:14.762097698-07:00
imports:
- name: github.com/ajeddeloh/go-json
version: 73d058cf8437a1989030afe571eeab9f90eebbbd
- name: github.com/ajeddeloh/yaml
version: 1072abfea31191db507785e2e0c1b8d1440d35a5
- name: github.com/alecthomas/units
version: 2efee857e7cfd4f3d0138cc3cbb1b4966962b93a
version: 6b4e7dc5e3143b85ea77909c72caf89416fc2915
- name: github.com/camlistore/camlistore
version: 9106ce829629773474c689b34aacd7d3aaa99426
- name: github.com/coreos/container-linux-config-transpiler
version: 12554ca0a5ce8ea4a6c594242ccb23d8b9bff493
subpackages:
- config
- config/templating
- config/types
- name: github.com/coreos/coreos-cloudinit
version: 4c333e657bfbaa8f6594298b48324f45e6bf5961
subpackages:
- config
- name: github.com/coreos/fuze
version: 63c72bc1c8875f7f4ca11800a1a8de0478a69a12
subpackages:
- config
- config/types
- name: github.com/coreos/go-semver
version: 294930c1e79c64e7dbe360054274fdad492c8cf5
version: 5e3acbb5668c4c3deb4842615c4098eb61fb6b1e
subpackages:
- semver
- name: github.com/coreos/go-systemd
version: 43e4800a6165b4e02bb2a36673c54b230d6f7b26
subpackages:
- journal
- unit
- name: github.com/coreos/ignition
version: 3ffd793b1292c6b0b3519bce214bdb41f336faa7
version: d75d0aa3bf307f0954ce4ea8cac56dacec8d16ce
subpackages:
- config
- config/types
- config/v1
- config/v1/types
- config/v2_0
- config/v2_0/types
- config/validate
- config/validate/astjson
- config/validate/report

View File

@@ -17,9 +17,15 @@ import:
- naming
- peer
- transport
# Ignition and Fuze parse machine configs
# Container Linux Config Transpiler and Ignition
- package: github.com/coreos/container-linux-config-transpiler
version: v0.2.2
subpackages:
- config
- config/types
- config/templating
- package: github.com/coreos/ignition
version: 3ffd793b1292c6b0b3519bce214bdb41f336faa7
version: d75d0aa3bf307f0954ce4ea8cac56dacec8d16ce
subpackages:
- config
- config/types
@@ -28,15 +34,16 @@ import:
- config/validate
- config/validate/astjson
- config/validate/report
- package: github.com/coreos/fuze
version: 63c72bc1c8875f7f4ca11800a1a8de0478a69a12
subpackages:
- config
- config/types
- package: github.com/ajeddeloh/yaml
version: 1072abfea31191db507785e2e0c1b8d1440d35a5
- package: github.com/vincent-petithory/dataurl
version: 9a301d65acbb728fcc3ace14f45f511a4cfeea9c
- package: github.com/alecthomas/units
version: 6b4e7dc5e3143b85ea77909c72caf89416fc2915
- package: github.com/coreos/go-semver
version: 5e3acbb5668c4c3deb4842615c4098eb61fb6b1e
subpackages:
- semver
- package: github.com/coreos/coreos-cloudinit
version: v1.11.0
subpackages:
@@ -71,14 +78,8 @@ import:
version: f7716cbe52baa25d2e9b0d0da546fcf909fc16b4
- package: github.com/coreos/yaml
version: 6b16a5714269b2f70720a45406b1babd947a17ef
- package: github.com/alecthomas/units
version: 2efee857e7cfd4f3d0138cc3cbb1b4966962b93a
- package: github.com/camlistore/camlistore
version: 9106ce829629773474c689b34aacd7d3aaa99426
- package: github.com/coreos/go-semver
version: 294930c1e79c64e7dbe360054274fdad492c8cf5
subpackages:
- semver
- package: github.com/coreos/go-systemd
version: v12
subpackages:

View File

@@ -26,35 +26,35 @@ var (
ErrNotDocumentNode = errors.New("Can only convert from document node")
)
type YamlNode struct {
type yamlNode struct {
key yaml.Node
yaml.Node
}
func FromYamlDocumentNode(n yaml.Node) (YamlNode, error) {
func fromYamlDocumentNode(n yaml.Node) (yamlNode, error) {
if n.Kind != yaml.DocumentNode {
return YamlNode{}, ErrNotDocumentNode
return yamlNode{}, ErrNotDocumentNode
}
return YamlNode{
return yamlNode{
key: n,
Node: *n.Children[0],
}, nil
}
func (n YamlNode) ValueLineCol(source io.ReadSeeker) (int, int, string) {
func (n yamlNode) ValueLineCol(source io.ReadSeeker) (int, int, string) {
return n.Line, n.Column, ""
}
func (n YamlNode) KeyLineCol(source io.ReadSeeker) (int, int, string) {
func (n yamlNode) KeyLineCol(source io.ReadSeeker) (int, int, string) {
return n.key.Line, n.key.Column, ""
}
func (n YamlNode) LiteralValue() interface{} {
func (n yamlNode) LiteralValue() interface{} {
return n.Value
}
func (n YamlNode) SliceChild(index int) (validate.AstNode, bool) {
func (n yamlNode) SliceChild(index int) (validate.AstNode, bool) {
if n.Kind != yaml.SequenceNode {
return nil, false
}
@@ -62,13 +62,13 @@ func (n YamlNode) SliceChild(index int) (validate.AstNode, bool) {
return nil, false
}
return YamlNode{
return yamlNode{
key: yaml.Node{},
Node: *n.Children[index],
}, true
}
func (n YamlNode) KeyValueMap() (map[string]validate.AstNode, bool) {
func (n yamlNode) KeyValueMap() (map[string]validate.AstNode, bool) {
if n.Kind != yaml.MappingNode {
return nil, false
}
@@ -77,7 +77,7 @@ func (n YamlNode) KeyValueMap() (map[string]validate.AstNode, bool) {
for i := 0; i < len(n.Children); i += 2 {
key := *n.Children[i]
value := *n.Children[i+1]
kvmap[key.Value] = YamlNode{
kvmap[key.Value] = yamlNode{
key: key,
Node: value,
}
@@ -85,6 +85,6 @@ func (n YamlNode) KeyValueMap() (map[string]validate.AstNode, bool) {
return kvmap, true
}
func (n YamlNode) Tag() string {
func (n yamlNode) Tag() string {
return "yaml"
}

View File

@@ -18,7 +18,8 @@ import (
"reflect"
yaml "github.com/ajeddeloh/yaml"
"github.com/coreos/fuze/config/types"
"github.com/coreos/container-linux-config-transpiler/config/types"
ignTypes "github.com/coreos/ignition/config/v2_0/types"
"github.com/coreos/ignition/config/validate"
"github.com/coreos/ignition/config/validate/report"
)
@@ -39,7 +40,7 @@ func Parse(data []byte) (types.Config, report.Report) {
})
r.Merge(validate.ValidateWithoutSource(reflect.ValueOf(cfg)))
} else {
root, err := FromYamlDocumentNode(*nodes)
root, err := fromYamlDocumentNode(*nodes)
if err != nil {
return types.Config{}, report.ReportFromError(err, report.EntryError)
}
@@ -52,3 +53,7 @@ func Parse(data []byte) (types.Config, report.Report) {
}
return cfg, r
}
func ConvertAs2_0(in types.Config, platform string) (ignTypes.Config, report.Report) {
return types.ConvertAs2_0(in, platform)
}

View File

@@ -0,0 +1,135 @@
// Copyright 2017 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package templating
import (
"fmt"
"strings"
)
var (
ErrUnknownPlatform = fmt.Errorf("unsupported platform")
ErrUnknownField = fmt.Errorf("unknown field")
)
const (
PlatformAzure = "azure"
PlatformDO = "digitalocean"
PlatformEC2 = "ec2"
PlatformGCE = "gce"
PlatformPacket = "packet"
PlatformOpenStackMetadata = "openstack-metadata"
)
var Platforms = []string{
PlatformAzure,
PlatformDO,
PlatformEC2,
PlatformGCE,
PlatformPacket,
PlatformOpenStackMetadata,
}
const (
fieldHostname = "HOSTNAME"
fieldV4Private = "PRIVATE_IPV4"
fieldV4Public = "PUBLIC_IPV4"
fieldV6Private = "PRIVATE_IPV6"
fieldV6Public = "PUBLIC_IPV6"
)
var platformTemplatingMap = map[string]map[string]string{
PlatformAzure: {
// TODO: is this right?
fieldV4Private: "COREOS_AZURE_IPV4_DYNAMIC",
fieldV4Public: "COREOS_AZURE_IPV4_VIRTUAL",
},
PlatformDO: {
// TODO: unused: COREOS_DIGITALOCEAN_IPV4_ANCHOR_0
fieldHostname: "COREOS_DIGITALOCEAN_HOSTNAME",
fieldV4Private: "COREOS_DIGITALOCEAN_IPV4_PRIVATE_0",
fieldV4Public: "COREOS_DIGITALOCEAN_IPV4_PUBLIC_0",
fieldV6Private: "COREOS_DIGITALOCEAN_IPV6_PRIVATE_0",
fieldV6Public: "COREOS_DIGITALOCEAN_IPV6_PUBLIC_0",
},
PlatformEC2: {
fieldHostname: "COREOS_EC2_HOSTNAME",
fieldV4Private: "COREOS_EC2_IPV4_LOCAL",
fieldV4Public: "COREOS_EC2_IPV4_PUBLIC",
},
PlatformGCE: {
fieldHostname: "COREOS_GCE_HOSTNAME",
fieldV4Private: "COREOS_GCE_IP_EXTERNAL_0",
fieldV4Public: "COREOS_GCE_IP_LOCAL_0",
},
PlatformPacket: {
fieldHostname: "COREOS_PACKET_HOSTNAME",
fieldV4Private: "COREOS_PACKET_IPV4_PRIVATE_0",
fieldV4Public: "COREOS_PACKET_IPV4_PUBLIC_0",
fieldV6Public: "COREOS_PACKET_IPV6_PUBLIC_0",
},
PlatformOpenStackMetadata: {
fieldHostname: "COREOS_OPENSTACK_HOSTNAME",
fieldV4Private: "COREOS_OPENSTACK_IPV4_LOCAL",
fieldV4Public: "COREOS_OPENSTACK_IPV4_PUBLIC",
},
}
// HasTemplating returns whether or not any of the environment variables present
// in the passed in list use ct templating
func HasTemplating(vars []string) bool {
for _, v := range vars {
if strings.ContainsRune(v, '{') || strings.ContainsRune(v, '}') {
return true
}
}
return false
}
func PerformTemplating(platform string, vars []string) ([]string, error) {
if _, ok := platformTemplatingMap[platform]; !ok {
return nil, ErrUnknownPlatform
}
for i := range vars {
startIndex := strings.IndexRune(vars[i], '{')
endIndex := strings.IndexRune(vars[i], '}')
for startIndex != -1 && endIndex != -1 && startIndex < endIndex {
fieldName := vars[i][startIndex+1 : endIndex]
fieldVal, ok := platformTemplatingMap[platform][fieldName]
if !ok {
return nil, ErrUnknownField
}
vars[i] = strings.Replace(vars[i], "{"+fieldName+"}", "${"+fieldVal+"}", 1)
// start the search for a new start index from the old end index, or
// we'll just find the curly braces we just substituted in
startIndex = strings.IndexRune(vars[i][endIndex:], '{')
if startIndex != -1 {
startIndex += endIndex
// and start the search for a new end index from the new start
// index, or as before we'll just find the curly braces we just
// substituted in
endIndex = strings.IndexRune(vars[i][startIndex:], '}')
if endIndex != -1 {
endIndex += startIndex
}
}
}
}
return vars, nil
}

View File

@@ -0,0 +1,89 @@
// Copyright 2017 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"reflect"
"github.com/coreos/container-linux-config-transpiler/config/templating"
)
var (
ErrPlatformUnspecified = fmt.Errorf("platform must be specified to use templating")
)
func isZero(v interface{}) bool {
if v == nil {
return true
}
zv := reflect.Zero(reflect.TypeOf(v))
return reflect.DeepEqual(v, zv.Interface())
}
// assembleUnit will assemble the contents of a systemd unit dropin that will
// have the given environment variables, and call the given exec line with the
// provided args prepended to it
func assembleUnit(exec string, args, vars []string, platform string) (string, error) {
hasTemplating := templating.HasTemplating(args)
var out string
if hasTemplating {
if platform == "" {
return "", ErrPlatformUnspecified
}
out = "[Unit]\nRequires=coreos-metadata.service\nAfter=coreos-metadata.service\n\n[Service]\nEnvironmentFile=/run/metadata/coreos\n"
var err error
args, err = templating.PerformTemplating(platform, args)
if err != nil {
return "", err
}
} else {
out = "[Service]\n"
}
for _, v := range vars {
out += fmt.Sprintf("Environment=\"%s\"\n", v)
}
for _, a := range args {
exec += fmt.Sprintf(" \\\n %s", a)
}
out += "ExecStart=\n"
out += fmt.Sprintf("ExecStart=%s", exec)
return out, nil
}
// getCliArgs builds a list of --ARG=VAL from a struct with cli: tags on its members.
func getCliArgs(e interface{}) []string {
if e == nil {
return nil
}
et := reflect.TypeOf(e)
ev := reflect.ValueOf(e)
vars := []string{}
for i := 0; i < et.NumField(); i++ {
if val := ev.Field(i).Interface(); !isZero(val) {
if et.Field(i).Anonymous {
vars = append(vars, getCliArgs(val)...)
} else {
key := et.Field(i).Tag.Get("cli")
vars = append(vars, fmt.Sprintf("--%s=%q", key, val))
}
}
}
return vars
}

View File

@@ -0,0 +1,95 @@
// 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 (
"net/url"
ignTypes "github.com/coreos/ignition/config/v2_0/types"
"github.com/coreos/ignition/config/validate/report"
)
type Config struct {
Ignition Ignition `yaml:"ignition"`
Storage Storage `yaml:"storage"`
Systemd Systemd `yaml:"systemd"`
Networkd Networkd `yaml:"networkd"`
Passwd Passwd `yaml:"passwd"`
Etcd *Etcd `yaml:"etcd"`
Flannel *Flannel `yaml:"flannel"`
Update *Update `yaml:"update"`
Docker *Docker `yaml:"docker"`
Locksmith *Locksmith `yaml:"locksmith"`
}
type Ignition struct {
Config IgnitionConfig `yaml:"config"`
}
type IgnitionConfig struct {
Append []ConfigReference `yaml:"append"`
Replace *ConfigReference `yaml:"replace"`
}
type ConfigReference struct {
Source string `yaml:"source"`
Verification Verification `yaml:"verification"`
}
func init() {
register2_0(func(in Config, out ignTypes.Config, platform string) (ignTypes.Config, report.Report) {
for _, ref := range in.Ignition.Config.Append {
newRef, err := convertConfigReference(ref)
if err != nil {
return out, report.ReportFromError(err, report.EntryError)
}
out.Ignition.Config.Append = append(out.Ignition.Config.Append, newRef)
}
if in.Ignition.Config.Replace != nil {
newRef, err := convertConfigReference(*in.Ignition.Config.Replace)
if err != nil {
return out, report.ReportFromError(err, report.EntryError)
}
out.Ignition.Config.Replace = &newRef
}
return out, report.Report{}
})
}
func convertConfigReference(in ConfigReference) (ignTypes.ConfigReference, error) {
source, err := url.Parse(in.Source)
if err != nil {
return ignTypes.ConfigReference{}, err
}
return ignTypes.ConfigReference{
Source: ignTypes.Url(*source),
Verification: convertVerification(in.Verification),
}, nil
}
func convertVerification(in Verification) ignTypes.Verification {
if in.Hash.Function == "" || in.Hash.Sum == "" {
return ignTypes.Verification{}
}
return ignTypes.Verification{
&ignTypes.Hash{
Function: in.Hash.Function,
Sum: in.Hash.Sum,
},
}
}

View File

@@ -0,0 +1,58 @@
// Copyright 2017 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"reflect"
ignTypes "github.com/coreos/ignition/config/v2_0/types"
"github.com/coreos/ignition/config/validate"
"github.com/coreos/ignition/config/validate/report"
)
type converterFor2_0 func(in Config, out ignTypes.Config, platform string) (ignTypes.Config, report.Report)
var convertersFor2_0 []converterFor2_0
func register2_0(f converterFor2_0) {
convertersFor2_0 = append(convertersFor2_0, f)
}
func ConvertAs2_0(in Config, platform string) (ignTypes.Config, report.Report) {
out := ignTypes.Config{
Ignition: ignTypes.Ignition{
Version: ignTypes.IgnitionVersion{Major: 2, Minor: 0},
},
}
r := report.Report{}
for _, convert := range convertersFor2_0 {
var subReport report.Report
out, subReport = convert(in, out, platform)
r.Merge(subReport)
}
if r.IsFatal() {
return ignTypes.Config{}, r
}
validationReport := validate.ValidateWithoutSource(reflect.ValueOf(out))
r.Merge(validationReport)
if r.IsFatal() {
return ignTypes.Config{}, r
}
return out, r
}

View File

@@ -0,0 +1,95 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"github.com/alecthomas/units"
ignTypes "github.com/coreos/ignition/config/v2_0/types"
"github.com/coreos/ignition/config/validate/report"
)
const (
BYTES_PER_SECTOR = 512
)
type Disk struct {
Device string `yaml:"device"`
WipeTable bool `yaml:"wipe_table"`
Partitions []Partition `yaml:"partitions"`
}
type Partition struct {
Label string `yaml:"label"`
Number int `yaml:"number"`
Size string `yaml:"size"`
Start string `yaml:"start"`
TypeGUID string `yaml:"type_guid"`
}
func init() {
register2_0(func(in Config, out ignTypes.Config, platform string) (ignTypes.Config, report.Report) {
for _, disk := range in.Storage.Disks {
newDisk := ignTypes.Disk{
Device: ignTypes.Path(disk.Device),
WipeTable: disk.WipeTable,
}
for _, partition := range disk.Partitions {
size, err := convertPartitionDimension(partition.Size)
if err != nil {
return out, report.ReportFromError(err, report.EntryError)
}
start, err := convertPartitionDimension(partition.Start)
if err != nil {
return out, report.ReportFromError(err, report.EntryError)
}
newDisk.Partitions = append(newDisk.Partitions, ignTypes.Partition{
Label: ignTypes.PartitionLabel(partition.Label),
Number: partition.Number,
Size: size,
Start: start,
TypeGUID: ignTypes.PartitionTypeGUID(partition.TypeGUID),
})
}
out.Storage.Disks = append(out.Storage.Disks, newDisk)
}
return out, report.Report{}
})
}
func convertPartitionDimension(in string) (ignTypes.PartitionDimension, error) {
if in == "" {
return 0, nil
}
b, err := units.ParseBase2Bytes(in)
if err != nil {
return 0, err
}
if b < 0 {
return 0, fmt.Errorf("invalid dimension (negative): %q", in)
}
// Translate bytes into sectors
sectors := (b / BYTES_PER_SECTOR)
if b%BYTES_PER_SECTOR != 0 {
sectors++
}
return ignTypes.PartitionDimension(uint64(sectors)), nil
}

View File

@@ -0,0 +1,44 @@
// Copyright 2017 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"strings"
ignTypes "github.com/coreos/ignition/config/v2_0/types"
"github.com/coreos/ignition/config/validate/report"
)
type Docker struct {
Flags []string `yaml:"flags"`
}
func init() {
register2_0(func(in Config, out ignTypes.Config, platform string) (ignTypes.Config, report.Report) {
if in.Docker != nil {
contents := fmt.Sprintf("[Service]\nEnvironment=\"DOCKER_OPTS=%s\"", strings.Join(in.Docker.Flags, " "))
out.Systemd.Units = append(out.Systemd.Units, ignTypes.SystemdUnit{
Name: "docker.service",
Enable: true,
DropIns: []ignTypes.SystemdUnitDropIn{{
Name: "20-clct-docker.conf",
Contents: contents,
}},
})
}
return out, report.Report{}
})
}

View File

@@ -0,0 +1,281 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"errors"
"fmt"
"github.com/coreos/go-semver/semver"
ignTypes "github.com/coreos/ignition/config/v2_0/types"
"github.com/coreos/ignition/config/validate/report"
)
var (
EtcdVersionTooOld = errors.New("Etcd version specified is not valid (too old)")
EtcdMinorVersionTooNew = errors.New("Etcd minor version specified is too new, only options available in the previous minor version will be accepted")
EtcdMajorVersionTooNew = errors.New("Etcd version is not valid (too new)")
OldestEtcd = *semver.New("2.3.0")
EtcdDefaultVersion = *semver.New("3.0.0")
)
// Options can be the options for any Etcd version
type Options interface{}
type etcdCommon Etcd
type EtcdVersion semver.Version
func (e *EtcdVersion) UnmarshalYAML(unmarshal func(interface{}) error) error {
t := semver.Version(*e)
if err := unmarshal(&t); err != nil {
return err
}
*e = EtcdVersion(t)
return nil
}
func (e EtcdVersion) Validate() report.Report {
v := semver.Version(e)
switch {
case v.LessThan(OldestEtcd):
return report.ReportFromError(EtcdVersionTooOld, report.EntryError)
case v.Major == 2 && v.Minor > 3:
fallthrough
case v.Major == 3 && v.Minor > 1:
return report.ReportFromError(EtcdMinorVersionTooNew, report.EntryWarning)
case v.Major > 3:
return report.ReportFromError(EtcdMajorVersionTooNew, report.EntryError)
}
return report.Report{}
}
func (e EtcdVersion) String() string {
return semver.Version(e).String()
}
// Etcd is a stub for yaml unmarshalling that figures out which
// of the other Etcd structs to use and unmarshals to that. Options needs
// to be an embedded type so that the structure of the yaml tree matches the
// structure of the go config tree
type Etcd struct {
Version *EtcdVersion `yaml:"version"`
Options
}
func (etcd *Etcd) UnmarshalYAML(unmarshal func(interface{}) error) error {
t := etcdCommon(*etcd)
if err := unmarshal(&t); err != nil {
return err
}
*etcd = Etcd(t)
var version semver.Version
if etcd.Version == nil {
version = EtcdDefaultVersion
} else {
version = semver.Version(*etcd.Version)
}
if version.Major == 2 && version.Minor >= 3 {
o := Etcd2{}
if err := unmarshal(&o); err != nil {
return err
}
etcd.Options = o
} else if version.Major == 3 && version.Minor == 0 {
o := Etcd3_0{}
if err := unmarshal(&o); err != nil {
return err
}
etcd.Options = o
} else if version.Major == 3 && version.Minor >= 1 {
o := Etcd3_1{}
if err := unmarshal(&o); err != nil {
return err
}
etcd.Options = o
}
return nil
}
func init() {
register2_0(func(in Config, out ignTypes.Config, platform string) (ignTypes.Config, report.Report) {
if in.Etcd != nil {
contents, err := etcdContents(*in.Etcd, platform)
if err != nil {
return ignTypes.Config{}, report.ReportFromError(err, report.EntryError)
}
out.Systemd.Units = append(out.Systemd.Units, ignTypes.SystemdUnit{
Name: "etcd-member.service",
Enable: true,
DropIns: []ignTypes.SystemdUnitDropIn{{
Name: "20-clct-etcd-member.conf",
Contents: contents,
}},
})
}
return out, report.Report{}
})
}
// etcdContents creates the string containing the systemd drop in for etcd-member
func etcdContents(etcd Etcd, platform string) (string, error) {
args := getCliArgs(etcd.Options)
var vars []string
if etcd.Version != nil {
vars = []string{fmt.Sprintf("ETCD_IMAGE_TAG=v%s", etcd.Version)}
}
return assembleUnit("/usr/lib/coreos/etcd-wrapper $ETCD_OPTS", args, vars, platform)
}
type Etcd3_0 struct {
Name string `yaml:"name" cli:"name"`
DataDir string `yaml:"data_dir" cli:"data-dir"`
WalDir string `yaml:"wal_dir" cli:"wal-dir"`
SnapshotCount int `yaml:"snapshot_count" cli:"snapshot-count"`
HeartbeatInterval int `yaml:"heartbeat_interval" cli:"heartbeat-interval"`
ElectionTimeout int `yaml:"election_timeout" cli:"election-timeout"`
ListenPeerUrls string `yaml:"listen_peer_urls" cli:"listen-peer-urls"`
ListenClientUrls string `yaml:"listen_client_urls" cli:"listen-client-urls"`
MaxSnapshots int `yaml:"max_snapshots" cli:"max-snapshots"`
MaxWals int `yaml:"max_wals" cli:"max-wals"`
Cors string `yaml:"cors" cli:"cors"`
InitialAdvertisePeerUrls string `yaml:"initial_advertise_peer_urls" cli:"initial-advertise-peer-urls"`
InitialCluster string `yaml:"initial_cluster" cli:"initial-cluster"`
InitialClusterState string `yaml:"initial_cluster_state" cli:"initial-cluster-state"`
InitialClusterToken string `yaml:"initial_cluster_token" cli:"initial-cluster-token"`
AdvertiseClientUrls string `yaml:"advertise_client_urls" cli:"advertise-client-urls"`
Discovery string `yaml:"discovery" cli:"discovery"`
DiscoverySrv string `yaml:"discovery_srv" cli:"discovery-srv"`
DiscoveryFallback string `yaml:"discovery_fallback" cli:"discovery-fallback"`
DiscoveryProxy string `yaml:"discovery_proxy" cli:"discovery-proxy"`
StrictReconfigCheck bool `yaml:"strict_reconfig_check" cli:"strict-reconfig-check"`
AutoCompactionRetention int `yaml:"auto_compaction_retention" cli:"auto-compaction-retention"`
Proxy string `yaml:"proxy" cli:"proxy"`
ProxyFailureWait int `yaml:"proxy_failure_wait" cli:"proxy-failure-wait"`
ProxyRefreshInterval int `yaml:"proxy_refresh_interval" cli:"proxy-refresh-interval"`
ProxyDialTimeout int `yaml:"proxy_dial_timeout" cli:"proxy-dial-timeout"`
ProxyWriteTimeout int `yaml:"proxy_write_timeout" cli:"proxy-write-timeout"`
ProxyReadTimeout int `yaml:"proxy_read_timeout" cli:"proxy-read-timeout"`
CaFile string `yaml:"ca_file" cli:"ca-file" deprecated:"ca_file obsoleted by trusted_ca_file and client_cert_auth"`
CertFile string `yaml:"cert_file" cli:"cert-file"`
KeyFile string `yaml:"key_file" cli:"key-file"`
ClientCertAuth bool `yaml:"client_cert_auth" cli:"client-cert-auth"`
TrustedCaFile string `yaml:"trusted_ca_file" cli:"trusted-ca-file"`
AutoTls bool `yaml:"auto_tls" cli:"auto-tls"`
PeerCaFile string `yaml:"peer_ca_file" cli:"peer-ca-file" deprecated:"peer_ca_file obsoleted peer_trusted_ca_file and peer_client_cert_auth"`
PeerCertFile string `yaml:"peer_cert_file" cli:"peer-cert-file"`
PeerKeyFile string `yaml:"peer_key_file" cli:"peer-key-file"`
PeerClientCertAuth bool `yaml:"peer_client_cert_auth" cli:"peer-client-cert-auth"`
PeerTrustedCaFile string `yaml:"peer_trusted_ca_file" cli:"peer-trusted-ca-file"`
PeerAutoTls bool `yaml:"peer_auto_tls" cli:"peer-auto-tls"`
Debug bool `yaml:"debug" cli:"debug"`
LogPackageLevels string `yaml:"log_package_levels" cli:"log-package-levels"`
ForceNewCluster bool `yaml:"force_new_cluster" cli:"force-new-cluster"`
}
type Etcd3_1 struct {
Name string `yaml:"name" cli:"name"`
DataDir string `yaml:"data_dir" cli:"data-dir"`
WalDir string `yaml:"wal_dir" cli:"wal-dir"`
SnapshotCount int `yaml:"snapshot_count" cli:"snapshot-count"`
HeartbeatInterval int `yaml:"heartbeat_interval" cli:"heartbeat-interval"`
ElectionTimeout int `yaml:"election_timeout" cli:"election-timeout"`
ListenPeerUrls string `yaml:"listen_peer_urls" cli:"listen-peer-urls"`
ListenClientUrls string `yaml:"listen_client_urls" cli:"listen-client-urls"`
MaxSnapshots int `yaml:"max_snapshots" cli:"max-snapshots"`
MaxWals int `yaml:"max_wals" cli:"max-wals"`
Cors string `yaml:"cors" cli:"cors"`
InitialAdvertisePeerUrls string `yaml:"initial_advertise_peer_urls" cli:"initial-advertise-peer-urls"`
InitialCluster string `yaml:"initial_cluster" cli:"initial-cluster"`
InitialClusterState string `yaml:"initial_cluster_state" cli:"initial-cluster-state"`
InitialClusterToken string `yaml:"initial_cluster_token" cli:"initial-cluster-token"`
AdvertiseClientUrls string `yaml:"advertise_client_urls" cli:"advertise-client-urls"`
Discovery string `yaml:"discovery" cli:"discovery"`
DiscoverySrv string `yaml:"discovery_srv" cli:"discovery-srv"`
DiscoveryFallback string `yaml:"discovery_fallback" cli:"discovery-fallback"`
DiscoveryProxy string `yaml:"discovery_proxy" cli:"discovery-proxy"`
StrictReconfigCheck bool `yaml:"strict_reconfig_check" cli:"strict-reconfig-check"`
AutoCompactionRetention int `yaml:"auto_compaction_retention" cli:"auto-compaction-retention"`
Proxy string `yaml:"proxy" cli:"proxy"`
ProxyFailureWait int `yaml:"proxy_failure_wait" cli:"proxy-failure-wait"`
ProxyRefreshInterval int `yaml:"proxy_refresh_interval" cli:"proxy-refresh-interval"`
ProxyDialTimeout int `yaml:"proxy_dial_timeout" cli:"proxy-dial-timeout"`
ProxyWriteTimeout int `yaml:"proxy_write_timeout" cli:"proxy-write-timeout"`
ProxyReadTimeout int `yaml:"proxy_read_timeout" cli:"proxy-read-timeout"`
CaFile string `yaml:"ca_file" cli:"ca-file" deprecated:"ca_file obsoleted by trusted_ca_file and client_cert_auth"`
CertFile string `yaml:"cert_file" cli:"cert-file"`
KeyFile string `yaml:"key_file" cli:"key-file"`
ClientCertAuth bool `yaml:"client_cert_auth" cli:"client-cert-auth"`
TrustedCaFile string `yaml:"trusted_ca_file" cli:"trusted-ca-file"`
AutoTls bool `yaml:"auto_tls" cli:"auto-tls"`
PeerCaFile string `yaml:"peer_ca_file" cli:"peer-ca-file" deprecated:"peer_ca_file obsoleted peer_trusted_ca_file and peer_client_cert_auth"`
PeerCertFile string `yaml:"peer_cert_file" cli:"peer-cert-file"`
PeerKeyFile string `yaml:"peer_key_file" cli:"peer-key-file"`
PeerClientCertAuth bool `yaml:"peer_client_cert_auth" cli:"peer-client-cert-auth"`
PeerTrustedCaFile string `yaml:"peer_trusted_ca_file" cli:"peer-trusted-ca-file"`
PeerAutoTls bool `yaml:"peer_auto_tls" cli:"peer-auto-tls"`
Debug bool `yaml:"debug" cli:"debug"`
LogPackageLevels string `yaml:"log_package_levels" cli:"log-package-levels"`
ForceNewCluster bool `yaml:"force_new_cluster" cli:"force-new-cluster"`
Metrics string `yaml:"metrics" cli:"metrics"`
LogOutput string `yaml:"log_output" cli:"log-output"`
}
type Etcd2 struct {
AdvertiseClientURLs string `yaml:"advertise_client_urls" cli:"advertise-client-urls"`
CAFile string `yaml:"ca_file" cli:"ca-file" deprecated:"ca_file obsoleted by trusted_ca_file and client_cert_auth"`
CertFile string `yaml:"cert_file" cli:"cert-file"`
ClientCertAuth bool `yaml:"client_cert_auth" cli:"client-cert-auth"`
CorsOrigins string `yaml:"cors" cli:"cors"`
DataDir string `yaml:"data_dir" cli:"data-dir"`
Debug bool `yaml:"debug" cli:"debug"`
Discovery string `yaml:"discovery" cli:"discovery"`
DiscoveryFallback string `yaml:"discovery_fallback" cli:"discovery-fallback"`
DiscoverySRV string `yaml:"discovery_srv" cli:"discovery-srv"`
DiscoveryProxy string `yaml:"discovery_proxy" cli:"discovery-proxy"`
ElectionTimeout int `yaml:"election_timeout" cli:"election-timeout"`
EnablePprof bool `yaml:"enable_pprof" cli:"enable-pprof"`
ForceNewCluster bool `yaml:"force_new_cluster" cli:"force-new-cluster"`
HeartbeatInterval int `yaml:"heartbeat_interval" cli:"heartbeat-interval"`
InitialAdvertisePeerURLs string `yaml:"initial_advertise_peer_urls" cli:"initial-advertise-peer-urls"`
InitialCluster string `yaml:"initial_cluster" cli:"initial-cluster"`
InitialClusterState string `yaml:"initial_cluster_state" cli:"initial-cluster-state"`
InitialClusterToken string `yaml:"initial_cluster_token" cli:"initial-cluster-token"`
KeyFile string `yaml:"key_file" cli:"key-file"`
ListenClientURLs string `yaml:"listen_client_urls" cli:"listen-client-urls"`
ListenPeerURLs string `yaml:"listen_peer_urls" cli:"listen-peer-urls"`
LogPackageLevels string `yaml:"log_package_levels" cli:"log-package-levels"`
MaxSnapshots int `yaml:"max_snapshots" cli:"max-snapshots"`
MaxWALs int `yaml:"max_wals" cli:"max-wals"`
Name string `yaml:"name" cli:"name"`
PeerCAFile string `yaml:"peer_ca_file" cli:"peer-ca-file" deprecated:"peer_ca_file obsoleted peer_trusted_ca_file and peer_client_cert_auth"`
PeerCertFile string `yaml:"peer_cert_file" cli:"peer-cert-file"`
PeerKeyFile string `yaml:"peer_key_file" cli:"peer-key-file"`
PeerClientCertAuth bool `yaml:"peer_client_cert_auth" cli:"peer-client-cert-auth"`
PeerTrustedCAFile string `yaml:"peer_trusted_ca_file" cli:"peer-trusted-ca-file"`
Proxy string `yaml:"proxy" cli:"proxy" valid:"^(on|off|readonly)$"`
ProxyDialTimeout int `yaml:"proxy_dial_timeout" cli:"proxy-dial-timeout"`
ProxyFailureWait int `yaml:"proxy_failure_wait" cli:"proxy-failure-wait"`
ProxyReadTimeout int `yaml:"proxy_read_timeout" cli:"proxy-read-timeout"`
ProxyRefreshInterval int `yaml:"proxy_refresh_interval" cli:"proxy-refresh-interval"`
ProxyWriteTimeout int `yaml:"proxy_write_timeout" cli:"proxy-write-timeout"`
SnapshotCount int `yaml:"snapshot_count" cli:"snapshot-count"`
StrictReconfigCheck bool `yaml:"strict_reconfig_check" cli:"strict-reconfig-check"`
TrustedCAFile string `yaml:"trusted_ca_file" cli:"trusted-ca-file"`
WalDir string `yaml:"wal_dir" cli:"wal-dir"`
}

View File

@@ -0,0 +1,98 @@
// 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 (
"net/url"
ignTypes "github.com/coreos/ignition/config/v2_0/types"
"github.com/coreos/ignition/config/validate/report"
"github.com/vincent-petithory/dataurl"
)
type File struct {
Filesystem string `yaml:"filesystem"`
Path string `yaml:"path"`
Mode int `yaml:"mode"`
Contents FileContents `yaml:"contents"`
User FileUser `yaml:"user"`
Group FileGroup `yaml:"group"`
}
type FileContents struct {
Remote Remote `yaml:"remote"`
Inline string `yaml:"inline"`
}
type Remote struct {
Url string `yaml:"url"`
Compression string `yaml:"compression"`
Verification Verification `yaml:"verification"`
}
type FileUser struct {
Id int `yaml:"id"`
}
type FileGroup struct {
Id int `yaml:"id"`
}
func init() {
register2_0(func(in Config, out ignTypes.Config, platform string) (ignTypes.Config, report.Report) {
for _, file := range in.Storage.Files {
newFile := ignTypes.File{
Filesystem: file.Filesystem,
Path: ignTypes.Path(file.Path),
Mode: ignTypes.FileMode(file.Mode),
User: ignTypes.FileUser{Id: file.User.Id},
Group: ignTypes.FileGroup{Id: file.Group.Id},
}
if file.Contents.Inline != "" {
newFile.Contents = ignTypes.FileContents{
Source: ignTypes.Url{
Scheme: "data",
Opaque: "," + dataurl.EscapeString(file.Contents.Inline),
},
}
}
if file.Contents.Remote.Url != "" {
source, err := url.Parse(file.Contents.Remote.Url)
if err != nil {
return out, report.ReportFromError(err, report.EntryError)
}
newFile.Contents = ignTypes.FileContents{Source: ignTypes.Url(*source)}
}
if newFile.Contents == (ignTypes.FileContents{}) {
newFile.Contents = ignTypes.FileContents{
Source: ignTypes.Url{
Scheme: "data",
Opaque: ",",
},
}
}
newFile.Contents.Compression = ignTypes.Compression(file.Contents.Remote.Compression)
newFile.Contents.Verification = convertVerification(file.Contents.Remote.Verification)
out.Storage.Files = append(out.Storage.Files, newFile)
}
return out, report.Report{}
})
}

View File

@@ -0,0 +1,71 @@
// 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 (
ignTypes "github.com/coreos/ignition/config/v2_0/types"
"github.com/coreos/ignition/config/validate/report"
)
type Filesystem struct {
Name string `yaml:"name"`
Mount *Mount `yaml:"mount"`
Path string `yaml:"path"`
}
type Mount struct {
Device string `yaml:"device"`
Format string `yaml:"format"`
Create *Create `yaml:"create"`
}
type Create struct {
Force bool `yaml:"force"`
Options []string `yaml:"options"`
}
func init() {
register2_0(func(in Config, out ignTypes.Config, platform string) (ignTypes.Config, report.Report) {
for _, filesystem := range in.Storage.Filesystems {
newFilesystem := ignTypes.Filesystem{
Name: filesystem.Name,
Path: func(p ignTypes.Path) *ignTypes.Path {
if p == "" {
return nil
}
return &p
}(ignTypes.Path(filesystem.Path)),
}
if filesystem.Mount != nil {
newFilesystem.Mount = &ignTypes.FilesystemMount{
Device: ignTypes.Path(filesystem.Mount.Device),
Format: ignTypes.FilesystemFormat(filesystem.Mount.Format),
}
if filesystem.Mount.Create != nil {
newFilesystem.Mount.Create = &ignTypes.FilesystemCreate{
Force: filesystem.Mount.Create.Force,
Options: ignTypes.MkfsOptions(filesystem.Mount.Create.Options),
}
}
}
out.Storage.Filesystems = append(out.Storage.Filesystems, newFilesystem)
}
return out, report.Report{}
})
}

View File

@@ -0,0 +1,157 @@
package types
import (
"errors"
"fmt"
"github.com/coreos/go-semver/semver"
ignTypes "github.com/coreos/ignition/config/v2_0/types"
"github.com/coreos/ignition/config/validate/report"
)
var (
ErrFlannelTooOld = errors.New("invalid flannel version (too old)")
ErrFlannelMinorTooNew = errors.New("flannel minor version too new. Only options available in the previous minor version will be supported")
OldestFlannelVersion = *semver.New("0.5.0")
FlannelDefaultVersion = *semver.New("0.6.0")
)
type Flannel struct {
Version *FlannelVersion `yaml:"version"`
Options
}
type flannelCommon Flannel
type FlannelVersion semver.Version
func (v *FlannelVersion) UnmarshalYAML(unmarshal func(interface{}) error) error {
t := semver.Version(*v)
if err := unmarshal(&t); err != nil {
return err
}
*v = FlannelVersion(t)
return nil
}
func (fv FlannelVersion) Validate() report.Report {
v := semver.Version(fv)
switch {
case v.LessThan(OldestFlannelVersion):
return report.ReportFromError(ErrFlannelTooOld, report.EntryError)
case v.Major == 0 && fv.Minor > 7:
return report.ReportFromError(ErrFlannelMinorTooNew, report.EntryWarning)
}
return report.Report{}
}
func (fv FlannelVersion) String() string {
return semver.Version(fv).String()
}
func (flannel *Flannel) UnmarshalYAML(unmarshal func(interface{}) error) error {
t := flannelCommon(*flannel)
if err := unmarshal(&t); err != nil {
return err
}
*flannel = Flannel(t)
var v semver.Version
if flannel.Version == nil {
v = FlannelDefaultVersion
} else {
v = semver.Version(*flannel.Version)
}
if v.Major == 0 && v.Minor >= 7 {
o := Flannel0_7{}
if err := unmarshal(&o); err != nil {
return err
}
flannel.Options = o
} else if v.Major == 0 && v.Minor == 6 {
o := Flannel0_6{}
if err := unmarshal(&o); err != nil {
return err
}
flannel.Options = o
} else if v.Major == 0 && v.Minor == 5 {
o := Flannel0_5{}
if err := unmarshal(&o); err != nil {
return err
}
flannel.Options = o
}
return nil
}
func init() {
register2_0(func(in Config, out ignTypes.Config, platform string) (ignTypes.Config, report.Report) {
if in.Flannel != nil {
contents, err := flannelContents(*in.Flannel, platform)
if err != nil {
return ignTypes.Config{}, report.ReportFromError(err, report.EntryError)
}
out.Systemd.Units = append(out.Systemd.Units, ignTypes.SystemdUnit{
Name: "flanneld.service",
Enable: true,
DropIns: []ignTypes.SystemdUnitDropIn{{
Name: "20-clct-flannel.conf",
Contents: contents,
}},
})
}
return out, report.Report{}
})
}
// flannelContents creates the string containing the systemd drop in for flannel
func flannelContents(flannel Flannel, platform string) (string, error) {
args := getCliArgs(flannel.Options)
vars := []string{fmt.Sprintf("FLANNEL_IMAGE_TAG=v%s", flannel.Version)}
return assembleUnit("/usr/lib/coreos/flannel-wrapper $FLANNEL_OPTS", args, vars, platform)
}
// Flannel0_7 represents flannel options for version 0.7.x. Don't embed Flannel0_6 because
// the yaml parser doesn't handle embedded structs
type Flannel0_7 struct {
EtcdUsername string `yaml:"etcd_username" cli:"etcd-username"`
EtcdPassword string `yaml:"etcd_password" cli:"etcd-password"`
EtcdEndpoints string `yaml:"etcd_endpoints" cli:"etcd-endpoints"`
EtcdCAFile string `yaml:"etcd_cafile" cli:"etcd-cafile"`
EtcdCertFile string `yaml:"etcd_certfile" cli:"etcd-certfile"`
EtcdKeyFile string `yaml:"etcd_keyfile" cli:"etcd-keyfile"`
EtcdPrefix string `yaml:"etcd_prefix" cli:"etcd-prefix"`
IPMasq string `yaml:"ip_masq" cli:"ip-masq"`
SubnetFile string `yaml:"subnet_file" cli:"subnet-file"`
Iface string `yaml:"interface" cli:"iface"`
PublicIP string `yaml:"public_ip" cli:"public-ip"`
KubeSubnetMgr bool `yaml:"kube_subnet_mgr" cli:"kube-subnet-mgr"`
}
type Flannel0_6 struct {
EtcdUsername string `yaml:"etcd_username" cli:"etcd-username"`
EtcdPassword string `yaml:"etcd_password" cli:"etcd-password"`
EtcdEndpoints string `yaml:"etcd_endpoints" cli:"etcd-endpoints"`
EtcdCAFile string `yaml:"etcd_cafile" cli:"etcd-cafile"`
EtcdCertFile string `yaml:"etcd_certfile" cli:"etcd-certfile"`
EtcdKeyFile string `yaml:"etcd_keyfile" cli:"etcd-keyfile"`
EtcdPrefix string `yaml:"etcd_prefix" cli:"etcd-prefix"`
IPMasq string `yaml:"ip_masq" cli:"ip-masq"`
SubnetFile string `yaml:"subnet_file" cli:"subnet-file"`
Iface string `yaml:"interface" cli:"iface"`
PublicIP string `yaml:"public_ip" cli:"public-ip"`
}
type Flannel0_5 struct {
EtcdEndpoints string `yaml:"etcd_endpoints" cli:"etcd-endpoints"`
EtcdCAFile string `yaml:"etcd_cafile" cli:"etcd-cafile"`
EtcdCertFile string `yaml:"etcd_certfile" cli:"etcd-certfile"`
EtcdKeyFile string `yaml:"etcd_keyfile" cli:"etcd-keyfile"`
EtcdPrefix string `yaml:"etcd_prefix" cli:"etcd-prefix"`
IPMasq string `yaml:"ip_masq" cli:"ip-masq"`
SubnetFile string `yaml:"subnet_file" cli:"subnet-file"`
Iface string `yaml:"interface" cli:"iface"`
PublicIP string `yaml:"public_ip" cli:"public-ip"`
}

View File

@@ -0,0 +1,41 @@
// Copyright 2017 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"errors"
"strings"
"github.com/coreos/ignition/config/validate/report"
)
var (
ErrUnknownStrategy = errors.New("unknown reboot strategy")
)
type Locksmith struct {
RebootStrategy RebootStrategy `yaml:"reboot_strategy"`
}
type RebootStrategy string
func (r RebootStrategy) Validate() report.Report {
switch strings.ToLower(string(r)) {
case "reboot", "etcd-lock", "off":
return report.Report{}
default:
return report.ReportFromError(ErrUnknownStrategy, report.EntryError)
}
}

View File

@@ -0,0 +1,41 @@
// 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 (
ignTypes "github.com/coreos/ignition/config/v2_0/types"
"github.com/coreos/ignition/config/validate/report"
)
type Networkd struct {
Units []NetworkdUnit `yaml:"units"`
}
type NetworkdUnit struct {
Name string `yaml:"name"`
Contents string `yaml:"contents"`
}
func init() {
register2_0(func(in Config, out ignTypes.Config, platform string) (ignTypes.Config, report.Report) {
for _, unit := range in.Networkd.Units {
out.Networkd.Units = append(out.Networkd.Units, ignTypes.NetworkdUnit{
Name: ignTypes.NetworkdUnitName(unit.Name),
Contents: unit.Contents,
})
}
return out, report.Report{}
})
}

View File

@@ -14,6 +14,11 @@
package types
import (
ignTypes "github.com/coreos/ignition/config/v2_0/types"
"github.com/coreos/ignition/config/validate/report"
)
type Passwd struct {
Users []User `yaml:"users"`
Groups []Group `yaml:"groups"`
@@ -45,3 +50,42 @@ type Group struct {
PasswordHash string `yaml:"password_hash"`
System bool `yaml:"system"`
}
func init() {
register2_0(func(in Config, out ignTypes.Config, platform string) (ignTypes.Config, report.Report) {
for _, user := range in.Passwd.Users {
newUser := ignTypes.User{
Name: user.Name,
PasswordHash: user.PasswordHash,
SSHAuthorizedKeys: user.SSHAuthorizedKeys,
}
if user.Create != nil {
newUser.Create = &ignTypes.UserCreate{
Uid: user.Create.Uid,
GECOS: user.Create.GECOS,
Homedir: user.Create.Homedir,
NoCreateHome: user.Create.NoCreateHome,
PrimaryGroup: user.Create.PrimaryGroup,
Groups: user.Create.Groups,
NoUserGroup: user.Create.NoUserGroup,
System: user.Create.System,
NoLogInit: user.Create.NoLogInit,
Shell: user.Create.Shell,
}
}
out.Passwd.Users = append(out.Passwd.Users, newUser)
}
for _, group := range in.Passwd.Groups {
out.Passwd.Groups = append(out.Passwd.Groups, ignTypes.Group{
Name: group.Name,
Gid: group.Gid,
PasswordHash: group.PasswordHash,
System: group.System,
})
}
return out, report.Report{}
})
}

View File

@@ -0,0 +1,46 @@
// 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 (
ignTypes "github.com/coreos/ignition/config/v2_0/types"
"github.com/coreos/ignition/config/validate/report"
)
type Raid struct {
Name string `yaml:"name"`
Level string `yaml:"level"`
Devices []string `yaml:"devices"`
Spares int `yaml:"spares"`
}
func init() {
register2_0(func(in Config, out ignTypes.Config, platform string) (ignTypes.Config, report.Report) {
for _, array := range in.Storage.Arrays {
newArray := ignTypes.Raid{
Name: array.Name,
Level: array.Level,
Spares: array.Spares,
}
for _, device := range array.Devices {
newArray.Devices = append(newArray.Devices, ignTypes.Path(device))
}
out.Storage.Arrays = append(out.Storage.Arrays, newArray)
}
return out, report.Report{}
})
}

View File

@@ -0,0 +1,60 @@
// 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 (
ignTypes "github.com/coreos/ignition/config/v2_0/types"
"github.com/coreos/ignition/config/validate/report"
)
type Systemd struct {
Units []SystemdUnit `yaml:"units"`
}
type SystemdUnit struct {
Name string `yaml:"name"`
Enable bool `yaml:"enable"`
Mask bool `yaml:"mask"`
Contents string `yaml:"contents"`
DropIns []SystemdUnitDropIn `yaml:"dropins"`
}
type SystemdUnitDropIn struct {
Name string `yaml:"name"`
Contents string `yaml:"contents"`
}
func init() {
register2_0(func(in Config, out ignTypes.Config, platform string) (ignTypes.Config, report.Report) {
for _, unit := range in.Systemd.Units {
newUnit := ignTypes.SystemdUnit{
Name: ignTypes.SystemdUnitName(unit.Name),
Enable: unit.Enable,
Mask: unit.Mask,
Contents: unit.Contents,
}
for _, dropIn := range unit.DropIns {
newUnit.DropIns = append(newUnit.DropIns, ignTypes.SystemdUnitDropIn{
Name: ignTypes.SystemdUnitDropInName(dropIn.Name),
Contents: dropIn.Contents,
})
}
out.Systemd.Units = append(out.Systemd.Units, newUnit)
}
return out, report.Report{}
})
}

View File

@@ -0,0 +1,91 @@
// Copyright 2017 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"errors"
"fmt"
"net/url"
"strings"
ignTypes "github.com/coreos/ignition/config/v2_0/types"
"github.com/coreos/ignition/config/validate/report"
"github.com/vincent-petithory/dataurl"
)
var (
ErrUnknownGroup = errors.New("unknown update group")
)
type Update struct {
Group UpdateGroup `yaml:"group"`
Server UpdateServer `yaml:"server"`
}
type UpdateGroup string
type UpdateServer string
func (u Update) Validate() report.Report {
switch strings.ToLower(string(u.Group)) {
case "stable", "beta", "alpha":
return report.Report{}
default:
if u.Server == "" {
return report.ReportFromError(ErrUnknownGroup, report.EntryWarning)
}
return report.Report{}
}
}
func (s UpdateServer) Validate() report.Report {
_, err := url.Parse(string(s))
if err != nil {
return report.ReportFromError(err, report.EntryError)
}
return report.Report{}
}
func init() {
register2_0(func(in Config, out ignTypes.Config, platform string) (ignTypes.Config, report.Report) {
var contents string
if in.Update != nil {
if in.Update.Group != "" {
contents += fmt.Sprintf("GROUP=%s", strings.ToLower(string(in.Update.Group)))
}
if in.Update.Server != "" {
contents += fmt.Sprintf("\nSERVER=%s", in.Update.Server)
}
}
if in.Locksmith != nil {
if in.Locksmith.RebootStrategy != "" {
contents += fmt.Sprintf("\nREBOOT_STRATEGY=%s", strings.ToLower(string(in.Locksmith.RebootStrategy)))
}
}
if contents != "" {
out.Storage.Files = append(out.Storage.Files, ignTypes.File{
Filesystem: "root",
Path: "/etc/coreos/update.conf",
Mode: 0644,
Contents: ignTypes.FileContents{
Source: ignTypes.Url{
Scheme: "data",
Opaque: "," + dataurl.EscapeString(contents),
},
},
})
}
return out, report.Report{}
})
}

View File

@@ -1,281 +0,0 @@
// 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"
"net/url"
"reflect"
"github.com/alecthomas/units"
fuzeTypes "github.com/coreos/fuze/config/types"
"github.com/coreos/ignition/config/types"
"github.com/coreos/ignition/config/validate"
"github.com/coreos/ignition/config/validate/report"
"github.com/vincent-petithory/dataurl"
)
const (
BYTES_PER_SECTOR = 512
)
func ConvertAs2_0_0(in fuzeTypes.Config) (types.Config, report.Report) {
out := types.Config{
Ignition: types.Ignition{
Version: types.IgnitionVersion{Major: 2, Minor: 0},
},
}
for _, ref := range in.Ignition.Config.Append {
newRef, err := convertConfigReference(ref)
if err != nil {
return types.Config{}, report.ReportFromError(err, report.EntryError)
}
out.Ignition.Config.Append = append(out.Ignition.Config.Append, newRef)
}
if in.Ignition.Config.Replace != nil {
newRef, err := convertConfigReference(*in.Ignition.Config.Replace)
if err != nil {
return types.Config{}, report.ReportFromError(err, report.EntryError)
}
out.Ignition.Config.Replace = &newRef
}
for _, disk := range in.Storage.Disks {
newDisk := types.Disk{
Device: types.Path(disk.Device),
WipeTable: disk.WipeTable,
}
for _, partition := range disk.Partitions {
size, err := convertPartitionDimension(partition.Size)
if err != nil {
return types.Config{}, report.ReportFromError(err, report.EntryError)
}
start, err := convertPartitionDimension(partition.Start)
if err != nil {
return types.Config{}, report.ReportFromError(err, report.EntryError)
}
newDisk.Partitions = append(newDisk.Partitions, types.Partition{
Label: types.PartitionLabel(partition.Label),
Number: partition.Number,
Size: size,
Start: start,
TypeGUID: types.PartitionTypeGUID(partition.TypeGUID),
})
}
out.Storage.Disks = append(out.Storage.Disks, newDisk)
}
for _, array := range in.Storage.Arrays {
newArray := types.Raid{
Name: array.Name,
Level: array.Level,
Spares: array.Spares,
}
for _, device := range array.Devices {
newArray.Devices = append(newArray.Devices, types.Path(device))
}
out.Storage.Arrays = append(out.Storage.Arrays, newArray)
}
for _, filesystem := range in.Storage.Filesystems {
newFilesystem := types.Filesystem{
Name: filesystem.Name,
Path: func(p types.Path) *types.Path {
if p == "" {
return nil
}
return &p
}(types.Path(filesystem.Path)),
}
if filesystem.Mount != nil {
newFilesystem.Mount = &types.FilesystemMount{
Device: types.Path(filesystem.Mount.Device),
Format: types.FilesystemFormat(filesystem.Mount.Format),
}
if filesystem.Mount.Create != nil {
newFilesystem.Mount.Create = &types.FilesystemCreate{
Force: filesystem.Mount.Create.Force,
Options: types.MkfsOptions(filesystem.Mount.Create.Options),
}
}
}
out.Storage.Filesystems = append(out.Storage.Filesystems, newFilesystem)
}
for _, file := range in.Storage.Files {
newFile := types.File{
Filesystem: file.Filesystem,
Path: types.Path(file.Path),
Mode: types.FileMode(file.Mode),
User: types.FileUser{Id: file.User.Id},
Group: types.FileGroup{Id: file.Group.Id},
}
if file.Contents.Inline != "" {
newFile.Contents = types.FileContents{
Source: types.Url{
Scheme: "data",
Opaque: "," + dataurl.EscapeString(file.Contents.Inline),
},
}
}
if file.Contents.Remote.Url != "" {
source, err := url.Parse(file.Contents.Remote.Url)
if err != nil {
return types.Config{}, report.ReportFromError(err, report.EntryError)
}
newFile.Contents = types.FileContents{Source: types.Url(*source)}
}
if newFile.Contents == (types.FileContents{}) {
newFile.Contents = types.FileContents{
Source: types.Url{
Scheme: "data",
Opaque: ",",
},
}
}
newFile.Contents.Compression = types.Compression(file.Contents.Remote.Compression)
newFile.Contents.Verification = convertVerification(file.Contents.Remote.Verification)
out.Storage.Files = append(out.Storage.Files, newFile)
}
for _, unit := range in.Systemd.Units {
newUnit := types.SystemdUnit{
Name: types.SystemdUnitName(unit.Name),
Enable: unit.Enable,
Mask: unit.Mask,
Contents: unit.Contents,
}
for _, dropIn := range unit.DropIns {
newUnit.DropIns = append(newUnit.DropIns, types.SystemdUnitDropIn{
Name: types.SystemdUnitDropInName(dropIn.Name),
Contents: dropIn.Contents,
})
}
out.Systemd.Units = append(out.Systemd.Units, newUnit)
}
for _, unit := range in.Networkd.Units {
out.Networkd.Units = append(out.Networkd.Units, types.NetworkdUnit{
Name: types.NetworkdUnitName(unit.Name),
Contents: unit.Contents,
})
}
for _, user := range in.Passwd.Users {
newUser := types.User{
Name: user.Name,
PasswordHash: user.PasswordHash,
SSHAuthorizedKeys: user.SSHAuthorizedKeys,
}
if user.Create != nil {
newUser.Create = &types.UserCreate{
Uid: user.Create.Uid,
GECOS: user.Create.GECOS,
Homedir: user.Create.Homedir,
NoCreateHome: user.Create.NoCreateHome,
PrimaryGroup: user.Create.PrimaryGroup,
Groups: user.Create.Groups,
NoUserGroup: user.Create.NoUserGroup,
System: user.Create.System,
NoLogInit: user.Create.NoLogInit,
Shell: user.Create.Shell,
}
}
out.Passwd.Users = append(out.Passwd.Users, newUser)
}
for _, group := range in.Passwd.Groups {
out.Passwd.Groups = append(out.Passwd.Groups, types.Group{
Name: group.Name,
Gid: group.Gid,
PasswordHash: group.PasswordHash,
System: group.System,
})
}
r := validate.ValidateWithoutSource(reflect.ValueOf(out))
if r.IsFatal() {
return types.Config{}, r
}
return out, r
}
func convertConfigReference(in fuzeTypes.ConfigReference) (types.ConfigReference, error) {
source, err := url.Parse(in.Source)
if err != nil {
return types.ConfigReference{}, err
}
return types.ConfigReference{
Source: types.Url(*source),
Verification: convertVerification(in.Verification),
}, nil
}
func convertVerification(in fuzeTypes.Verification) types.Verification {
if in.Hash.Function == "" || in.Hash.Sum == "" {
return types.Verification{}
}
return types.Verification{
&types.Hash{
Function: in.Hash.Function,
Sum: in.Hash.Sum,
},
}
}
func convertPartitionDimension(in string) (types.PartitionDimension, error) {
if in == "" {
return 0, nil
}
b, err := units.ParseBase2Bytes(in)
if err != nil {
return 0, err
}
if b < 0 {
return 0, fmt.Errorf("invalid dimension (negative): %q", in)
}
// Translate bytes into sectors
sectors := (b / BYTES_PER_SECTOR)
if b%BYTES_PER_SECTOR != 0 {
sectors++
}
return types.PartitionDimension(uint64(sectors)), nil
}

View File

@@ -1,37 +0,0 @@
// 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 Config struct {
Ignition Ignition `yaml:"ignition"`
Storage Storage `yaml:"storage"`
Systemd Systemd `yaml:"systemd"`
Networkd Networkd `yaml:"networkd"`
Passwd Passwd `yaml:"passwd"`
}
type Ignition struct {
Config IgnitionConfig `yaml:"config"`
}
type IgnitionConfig struct {
Append []ConfigReference `yaml:"append"`
Replace *ConfigReference `yaml:"replace"`
}
type ConfigReference struct {
Source string `yaml:"source"`
Verification Verification `yaml:"verification"`
}

View File

@@ -1,43 +0,0 @@
// 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 File struct {
Filesystem string `yaml:"filesystem"`
Path string `yaml:"path"`
Mode int `yaml:"mode"`
Contents FileContents `yaml:"contents"`
User FileUser `yaml:"user"`
Group FileGroup `yaml:"group"`
}
type FileContents struct {
Remote Remote `yaml:"remote"`
Inline string `yaml:"inline"`
}
type Remote struct {
Url string `yaml:"url"`
Compression string `yaml:"compression"`
Verification Verification `yaml:"verification"`
}
type FileUser struct {
Id int `yaml:"id"`
}
type FileGroup struct {
Id int `yaml:"id"`
}

View File

@@ -44,16 +44,36 @@ func splitOff(input *string, delim string) (val string) {
return val
}
func New(version string) *Version {
return Must(NewVersion(version))
}
func NewVersion(version string) (*Version, error) {
v := Version{}
v.Metadata = splitOff(&version, "+")
v.PreRelease = PreRelease(splitOff(&version, "-"))
if err := v.Set(version); err != nil {
return nil, err
}
return &v, nil
}
// Must is a helper for wrapping NewVersion and will panic if err is not nil.
func Must(v *Version, err error) *Version {
if err != nil {
panic(err)
}
return v
}
// Set parses and updates v from the given version string. Implements flag.Value
func (v *Version) Set(version string) error {
metadata := splitOff(&version, "+")
preRelease := PreRelease(splitOff(&version, "-"))
dotParts := strings.SplitN(version, ".", 3)
if len(dotParts) != 3 {
return nil, errors.New(fmt.Sprintf("%s is not in dotted-tri format", version))
return fmt.Errorf("%s is not in dotted-tri format", version)
}
parsed := make([]int64, 3, 3)
@@ -62,22 +82,16 @@ func NewVersion(version string) (*Version, error) {
val, err := strconv.ParseInt(v, 10, 64)
parsed[i] = val
if err != nil {
return nil, err
return err
}
}
v.Metadata = metadata
v.PreRelease = preRelease
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
return nil
}
func (v Version) String() string {
@@ -101,12 +115,7 @@ func (v *Version) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&data); err != nil {
return err
}
vv, err := NewVersion(data)
if err != nil {
return err
}
*v = *vv
return nil
return v.Set(data)
}
func (v Version) MarshalJSON() ([]byte, error) {
@@ -121,30 +130,29 @@ func (v *Version) UnmarshalJSON(data []byte) error {
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
return v.Set(string(data[1 : l-1]))
}
// Compare tests if v is less than, equal to, or greater than versionB,
// returning -1, 0, or +1 respectively.
func (v Version) Compare(versionB Version) int {
if cmp := recursiveCompare(v.Slice(), versionB.Slice()); cmp != 0 {
return cmp
}
return preReleaseCompare(v, versionB)
}
// Equal tests if v is equal to versionB.
func (v Version) Equal(versionB Version) bool {
return v.Compare(versionB) == 0
}
// LessThan tests if v is less than versionB.
func (v Version) LessThan(versionB Version) bool {
versionA := v
cmp := recursiveCompare(versionA.Slice(), versionB.Slice())
if cmp == 0 {
cmp = preReleaseCompare(versionA, versionB)
}
if cmp == -1 {
return true
}
return false
return v.Compare(versionB) < 0
}
/* Slice converts the comparable parts of the semver into a slice of strings */
// Slice converts the comparable parts of the semver into a slice of integers.
func (v Version) Slice() []int64 {
return []int64{v.Major, v.Minor, v.Patch}
}
@@ -166,7 +174,7 @@ func preReleaseCompare(versionA Version, versionB Version) int {
return -1
}
// If there is a prelease, check and compare each part.
// If there is a prerelease, check and compare each part.
return recursivePreReleaseCompare(a.Slice(), b.Slice())
}
@@ -188,9 +196,12 @@ func recursiveCompare(versionA []int64, versionB []int64) int {
}
func recursivePreReleaseCompare(versionA []string, versionB []string) int {
// Handle slice length disparity.
// A larger set of pre-release fields has a higher precedence than a smaller set,
// if all of the preceding identifiers are equal.
if len(versionA) == 0 {
// Nothing to compare too, so we return 0
if len(versionB) > 0 {
return -1
}
return 0
} else if len(versionB) == 0 {
// We're longer than versionB so return 1.
@@ -213,6 +224,13 @@ func recursivePreReleaseCompare(versionA []string, versionB []string) int {
bInt = true
}
// Numeric identifiers always have lower precedence than non-numeric identifiers.
if aInt && !bInt {
return -1
} else if !aInt && bInt {
return 1
}
// Handle Integer Comparison
if aInt && bInt {
if aI > bI {

276
vendor/github.com/coreos/go-systemd/unit/deserialize.go generated vendored Normal file
View File

@@ -0,0 +1,276 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package unit
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"strings"
"unicode"
)
const (
// SYSTEMD_LINE_MAX mimics the maximum line length that systemd can use.
// On typical systemd platforms (i.e. modern Linux), this will most
// commonly be 2048, so let's use that as a sanity check.
// Technically, we should probably pull this at runtime:
// SYSTEMD_LINE_MAX = int(C.sysconf(C.__SC_LINE_MAX))
// but this would introduce an (unfortunate) dependency on cgo
SYSTEMD_LINE_MAX = 2048
// characters that systemd considers indicate a newline
SYSTEMD_NEWLINE = "\r\n"
)
var (
ErrLineTooLong = fmt.Errorf("line too long (max %d bytes)", SYSTEMD_LINE_MAX)
)
// Deserialize parses a systemd unit file into a list of UnitOption objects.
func Deserialize(f io.Reader) (opts []*UnitOption, err error) {
lexer, optchan, errchan := newLexer(f)
go lexer.lex()
for opt := range optchan {
opts = append(opts, &(*opt))
}
err = <-errchan
return opts, err
}
func newLexer(f io.Reader) (*lexer, <-chan *UnitOption, <-chan error) {
optchan := make(chan *UnitOption)
errchan := make(chan error, 1)
buf := bufio.NewReader(f)
return &lexer{buf, optchan, errchan, ""}, optchan, errchan
}
type lexer struct {
buf *bufio.Reader
optchan chan *UnitOption
errchan chan error
section string
}
func (l *lexer) lex() {
var err error
defer func() {
close(l.optchan)
close(l.errchan)
}()
next := l.lexNextSection
for next != nil {
if l.buf.Buffered() >= SYSTEMD_LINE_MAX {
// systemd truncates lines longer than LINE_MAX
// https://bugs.freedesktop.org/show_bug.cgi?id=85308
// Rather than allowing this to pass silently, let's
// explicitly gate people from encountering this
line, err := l.buf.Peek(SYSTEMD_LINE_MAX)
if err != nil {
l.errchan <- err
return
}
if bytes.IndexAny(line, SYSTEMD_NEWLINE) == -1 {
l.errchan <- ErrLineTooLong
return
}
}
next, err = next()
if err != nil {
l.errchan <- err
return
}
}
}
type lexStep func() (lexStep, error)
func (l *lexer) lexSectionName() (lexStep, error) {
sec, err := l.buf.ReadBytes(']')
if err != nil {
return nil, errors.New("unable to find end of section")
}
return l.lexSectionSuffixFunc(string(sec[:len(sec)-1])), nil
}
func (l *lexer) lexSectionSuffixFunc(section string) lexStep {
return func() (lexStep, error) {
garbage, _, err := l.toEOL()
if err != nil {
return nil, err
}
garbage = bytes.TrimSpace(garbage)
if len(garbage) > 0 {
return nil, fmt.Errorf("found garbage after section name %s: %v", l.section, garbage)
}
return l.lexNextSectionOrOptionFunc(section), nil
}
}
func (l *lexer) ignoreLineFunc(next lexStep) lexStep {
return func() (lexStep, error) {
for {
line, _, err := l.toEOL()
if err != nil {
return nil, err
}
line = bytes.TrimSuffix(line, []byte{' '})
// lack of continuation means this line has been exhausted
if !bytes.HasSuffix(line, []byte{'\\'}) {
break
}
}
// reached end of buffer, safe to exit
return next, nil
}
}
func (l *lexer) lexNextSection() (lexStep, error) {
r, _, err := l.buf.ReadRune()
if err != nil {
if err == io.EOF {
err = nil
}
return nil, err
}
if r == '[' {
return l.lexSectionName, nil
} else if isComment(r) {
return l.ignoreLineFunc(l.lexNextSection), nil
}
return l.lexNextSection, nil
}
func (l *lexer) lexNextSectionOrOptionFunc(section string) lexStep {
return func() (lexStep, error) {
r, _, err := l.buf.ReadRune()
if err != nil {
if err == io.EOF {
err = nil
}
return nil, err
}
if unicode.IsSpace(r) {
return l.lexNextSectionOrOptionFunc(section), nil
} else if r == '[' {
return l.lexSectionName, nil
} else if isComment(r) {
return l.ignoreLineFunc(l.lexNextSectionOrOptionFunc(section)), nil
}
l.buf.UnreadRune()
return l.lexOptionNameFunc(section), nil
}
}
func (l *lexer) lexOptionNameFunc(section string) lexStep {
return func() (lexStep, error) {
var partial bytes.Buffer
for {
r, _, err := l.buf.ReadRune()
if err != nil {
return nil, err
}
if r == '\n' || r == '\r' {
return nil, errors.New("unexpected newline encountered while parsing option name")
}
if r == '=' {
break
}
partial.WriteRune(r)
}
name := strings.TrimSpace(partial.String())
return l.lexOptionValueFunc(section, name, bytes.Buffer{}), nil
}
}
func (l *lexer) lexOptionValueFunc(section, name string, partial bytes.Buffer) lexStep {
return func() (lexStep, error) {
for {
line, eof, err := l.toEOL()
if err != nil {
return nil, err
}
if len(bytes.TrimSpace(line)) == 0 {
break
}
partial.Write(line)
// lack of continuation means this value has been exhausted
idx := bytes.LastIndex(line, []byte{'\\'})
if idx == -1 || idx != (len(line)-1) {
break
}
if !eof {
partial.WriteRune('\n')
}
return l.lexOptionValueFunc(section, name, partial), nil
}
val := partial.String()
if strings.HasSuffix(val, "\n") {
// A newline was added to the end, so the file didn't end with a backslash.
// => Keep the newline
val = strings.TrimSpace(val) + "\n"
} else {
val = strings.TrimSpace(val)
}
l.optchan <- &UnitOption{Section: section, Name: name, Value: val}
return l.lexNextSectionOrOptionFunc(section), nil
}
}
// toEOL reads until the end-of-line or end-of-file.
// Returns (data, EOFfound, error)
func (l *lexer) toEOL() ([]byte, bool, error) {
line, err := l.buf.ReadBytes('\n')
// ignore EOF here since it's roughly equivalent to EOL
if err != nil && err != io.EOF {
return nil, false, err
}
line = bytes.TrimSuffix(line, []byte{'\r'})
line = bytes.TrimSuffix(line, []byte{'\n'})
return line, err == io.EOF, nil
}
func isComment(r rune) bool {
return r == '#' || r == ';'
}

116
vendor/github.com/coreos/go-systemd/unit/escape.go generated vendored Normal file
View File

@@ -0,0 +1,116 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Implements systemd-escape [--unescape] [--path]
package unit
import (
"fmt"
"strconv"
"strings"
)
const (
allowed = `:_.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`
)
// If isPath is true:
// We remove redundant '/'s, the leading '/', and trailing '/'.
// If the result is empty, a '/' is inserted.
//
// We always:
// Replace the following characters with `\x%x`:
// Leading `.`
// `-`, `\`, and anything not in this set: `:-_.\[0-9a-zA-Z]`
// Replace '/' with '-'.
func escape(unescaped string, isPath bool) string {
e := []byte{}
inSlashes := false
start := true
for i := 0; i < len(unescaped); i++ {
c := unescaped[i]
if isPath {
if c == '/' {
inSlashes = true
continue
} else if inSlashes {
inSlashes = false
if !start {
e = append(e, '-')
}
}
}
if c == '/' {
e = append(e, '-')
} else if start && c == '.' || strings.IndexByte(allowed, c) == -1 {
e = append(e, []byte(fmt.Sprintf(`\x%x`, c))...)
} else {
e = append(e, c)
}
start = false
}
if isPath && len(e) == 0 {
e = append(e, '-')
}
return string(e)
}
// If isPath is true:
// We always return a string beginning with '/'.
//
// We always:
// Replace '-' with '/'.
// Replace `\x%x` with the value represented in hex.
func unescape(escaped string, isPath bool) string {
u := []byte{}
for i := 0; i < len(escaped); i++ {
c := escaped[i]
if c == '-' {
c = '/'
} else if c == '\\' && len(escaped)-i >= 4 && escaped[i+1] == 'x' {
n, err := strconv.ParseInt(escaped[i+2:i+4], 16, 8)
if err == nil {
c = byte(n)
i += 3
}
}
u = append(u, c)
}
if isPath && (len(u) == 0 || u[0] != '/') {
u = append([]byte("/"), u...)
}
return string(u)
}
// UnitNameEscape escapes a string as `systemd-escape` would
func UnitNameEscape(unescaped string) string {
return escape(unescaped, false)
}
// UnitNameUnescape unescapes a string as `systemd-escape --unescape` would
func UnitNameUnescape(escaped string) string {
return unescape(escaped, false)
}
// UnitNamePathEscape escapes a string as `systemd-escape --path` would
func UnitNamePathEscape(unescaped string) string {
return escape(unescaped, true)
}
// UnitNamePathUnescape unescapes a string as `systemd-escape --path --unescape` would
func UnitNamePathUnescape(escaped string) string {
return unescape(escaped, true)
}

54
vendor/github.com/coreos/go-systemd/unit/option.go generated vendored Normal file
View File

@@ -0,0 +1,54 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package unit
import (
"fmt"
)
type UnitOption struct {
Section string
Name string
Value string
}
func NewUnitOption(section, name, value string) *UnitOption {
return &UnitOption{Section: section, Name: name, Value: value}
}
func (uo *UnitOption) String() string {
return fmt.Sprintf("{Section: %q, Name: %q, Value: %q}", uo.Section, uo.Name, uo.Value)
}
func (uo *UnitOption) Match(other *UnitOption) bool {
return uo.Section == other.Section &&
uo.Name == other.Name &&
uo.Value == other.Value
}
func AllMatch(u1 []*UnitOption, u2 []*UnitOption) bool {
length := len(u1)
if length != len(u2) {
return false
}
for i := 0; i < length; i++ {
if !u1[i].Match(u2[i]) {
return false
}
}
return true
}

75
vendor/github.com/coreos/go-systemd/unit/serialize.go generated vendored Normal file
View File

@@ -0,0 +1,75 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package unit
import (
"bytes"
"io"
)
// Serialize encodes all of the given UnitOption objects into a
// unit file. When serialized the options are sorted in their
// supplied order but grouped by section.
func Serialize(opts []*UnitOption) io.Reader {
var buf bytes.Buffer
if len(opts) == 0 {
return &buf
}
// Index of sections -> ordered options
idx := map[string][]*UnitOption{}
// Separately preserve order in which sections were seen
sections := []string{}
for _, opt := range opts {
sec := opt.Section
if _, ok := idx[sec]; !ok {
sections = append(sections, sec)
}
idx[sec] = append(idx[sec], opt)
}
for i, sect := range sections {
writeSectionHeader(&buf, sect)
writeNewline(&buf)
opts := idx[sect]
for _, opt := range opts {
writeOption(&buf, opt)
writeNewline(&buf)
}
if i < len(sections)-1 {
writeNewline(&buf)
}
}
return &buf
}
func writeNewline(buf *bytes.Buffer) {
buf.WriteRune('\n')
}
func writeSectionHeader(buf *bytes.Buffer, section string) {
buf.WriteRune('[')
buf.WriteString(section)
buf.WriteRune(']')
}
func writeOption(buf *bytes.Buffer, opt *UnitOption) {
buf.WriteString(opt.Name)
buf.WriteRune('=')
buf.WriteString(opt.Value)
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/coreos/ignition/config/types"
"github.com/coreos/ignition/config/v1"
"github.com/coreos/ignition/config/v2_0"
"github.com/coreos/ignition/config/validate"
astjson "github.com/coreos/ignition/config/validate/astjson"
"github.com/coreos/ignition/config/validate/report"
@@ -40,14 +41,16 @@ var (
// Parse parses the raw config into a types.Config struct and generates a report of any
// errors, warnings, info, and deprecations it encountered
func Parse(rawConfig []byte) (types.Config, report.Report, error) {
switch majorVersion(rawConfig) {
case 1:
switch version(rawConfig) {
case types.IgnitionVersion{Major: 1}:
config, err := ParseFromV1(rawConfig)
if err != nil {
return types.Config{}, report.ReportFromError(err, report.EntryError), err
}
return config, report.ReportFromError(ErrDeprecated, report.EntryDeprecated), nil
case types.IgnitionVersion{Major: 2, Minor: 0}:
return ParseFromV2_0(rawConfig)
default:
return ParseFromLatest(rawConfig)
}
@@ -136,10 +139,19 @@ func ParseFromV1(rawConfig []byte) (types.Config, error) {
return types.Config{}, err
}
return TranslateFromV1(config)
return TranslateFromV1(config), nil
}
func majorVersion(rawConfig []byte) int64 {
func ParseFromV2_0(rawConfig []byte) (types.Config, report.Report, error) {
cfg, report, err := v2_0.Parse(rawConfig)
if err != nil {
return types.Config{}, report, err
}
return TranslateFromV2_0(cfg), report, err
}
func version(rawConfig []byte) types.IgnitionVersion {
var composite struct {
Version *int `json:"ignitionVersion"`
Ignition struct {
@@ -147,18 +159,15 @@ func majorVersion(rawConfig []byte) int64 {
} `json:"ignition"`
}
if json.Unmarshal(rawConfig, &composite) != nil {
return 0
if json.Unmarshal(rawConfig, &composite) == nil {
if composite.Ignition.Version != nil {
return *composite.Ignition.Version
} else if composite.Version != nil {
return types.IgnitionVersion{Major: int64(*composite.Version)}
}
}
var major int64
if composite.Ignition.Version != nil {
major = composite.Ignition.Version.Major
} else if composite.Version != nil {
major = int64(*composite.Version)
}
return major
return types.IgnitionVersion{}
}
func isEmpty(userdata []byte) bool {

View File

@@ -19,14 +19,15 @@ import (
"github.com/coreos/ignition/config/types"
v1 "github.com/coreos/ignition/config/v1/types"
v2_0 "github.com/coreos/ignition/config/v2_0/types"
"github.com/vincent-petithory/dataurl"
)
func TranslateFromV1(old v1.Config) (types.Config, error) {
func TranslateFromV1(old v1.Config) types.Config {
config := types.Config{
Ignition: types.Ignition{
Version: types.IgnitionVersion{Major: 2},
Version: types.IgnitionVersion(v2_0.MaxVersion),
},
}
@@ -83,17 +84,19 @@ func TranslateFromV1(old v1.Config) (types.Config, error) {
for _, oldFile := range oldFilesystem.Files {
file := types.File{
Filesystem: filesystem.Name,
Path: types.Path(oldFile.Path),
Node: types.Node{
Filesystem: filesystem.Name,
Path: types.Path(oldFile.Path),
Mode: types.NodeMode(oldFile.Mode),
User: types.NodeUser{Id: oldFile.Uid},
Group: types.NodeGroup{Id: oldFile.Gid},
},
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)
@@ -159,5 +162,178 @@ func TranslateFromV1(old v1.Config) (types.Config, error) {
})
}
return config, nil
return config
}
func TranslateFromV2_0(old v2_0.Config) types.Config {
translateVerification := func(old v2_0.Verification) types.Verification {
var ver types.Verification
if old.Hash != nil {
h := types.Hash(*old.Hash)
ver.Hash = &h
}
return ver
}
translateConfigReference := func(old v2_0.ConfigReference) types.ConfigReference {
return types.ConfigReference{
Source: types.Url(old.Source),
Verification: translateVerification(old.Verification),
}
}
config := types.Config{
Ignition: types.Ignition{
Version: types.IgnitionVersion(types.MaxVersion),
},
}
if old.Ignition.Config.Replace != nil {
ref := translateConfigReference(*old.Ignition.Config.Replace)
config.Ignition.Config.Replace = &ref
}
for _, oldAppend := range old.Ignition.Config.Append {
config.Ignition.Config.Append =
append(config.Ignition.Config.Append, translateConfigReference(oldAppend))
}
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 _, oldFilesystem := range old.Storage.Filesystems {
filesystem := types.Filesystem{
Name: oldFilesystem.Name,
}
if oldFilesystem.Mount != nil {
filesystem.Mount = &types.FilesystemMount{
Device: types.Path(oldFilesystem.Mount.Device),
Format: types.FilesystemFormat(oldFilesystem.Mount.Format),
}
if oldFilesystem.Mount.Create != nil {
filesystem.Mount.Create = &types.FilesystemCreate{
Force: oldFilesystem.Mount.Create.Force,
Options: types.MkfsOptions(oldFilesystem.Mount.Create.Options),
}
}
}
if oldFilesystem.Path != nil {
path := types.Path(*oldFilesystem.Path)
filesystem.Path = &path
}
config.Storage.Filesystems = append(config.Storage.Filesystems, filesystem)
}
for _, oldFile := range old.Storage.Files {
file := types.File{
Node: types.Node{
Filesystem: oldFile.Filesystem,
Path: types.Path(oldFile.Path),
Mode: types.NodeMode(oldFile.Mode),
User: types.NodeUser{Id: oldFile.User.Id},
Group: types.NodeGroup{Id: oldFile.Group.Id},
},
Contents: types.FileContents{
Compression: types.Compression(oldFile.Contents.Compression),
Source: types.Url(oldFile.Contents.Source),
Verification: translateVerification(oldFile.Contents.Verification),
},
}
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
}

View File

@@ -24,8 +24,9 @@ import (
var (
MaxVersion = semver.Version{
Major: 2,
Minor: 0,
Major: 2,
Minor: 1,
PreRelease: "experimental",
}
)

View File

@@ -14,19 +14,16 @@
package types
type Filesystem struct {
Name string `yaml:"name"`
Mount *Mount `yaml:"mount"`
Path string `yaml:"path"`
}
import (
"path"
)
type Mount struct {
Device string `yaml:"device"`
Format string `yaml:"format"`
Create *Create `yaml:"create"`
}
type Directory Node
type Create struct {
Force bool `yaml:"force"`
Options []string `yaml:"options"`
func (d *Directory) Depth() int {
count := 0
for p := path.Clean(string(d.Path)); p != "/"; count++ {
p = path.Dir(p)
}
return count
}

View File

@@ -14,40 +14,10 @@
package types
import (
"errors"
"os"
"github.com/coreos/ignition/config/validate/report"
)
var (
ErrFileIllegalMode = errors.New("illegal file mode")
ErrNoFilesystem = errors.New("no filesystem specified")
)
// File represents regular files
type File struct {
Filesystem string `json:"filesystem,omitempty"`
Path Path `json:"path,omitempty"`
Contents FileContents `json:"contents,omitempty"`
Mode FileMode `json:"mode,omitempty"`
User FileUser `json:"user,omitempty"`
Group FileGroup `json:"group,omitempty"`
}
func (f File) Validate() report.Report {
if f.Filesystem == "" {
return report.ReportFromError(ErrNoFilesystem, report.EntryError)
}
return report.Report{}
}
type FileUser struct {
Id int `json:"id,omitempty"`
}
type FileGroup struct {
Id int `json:"id,omitempty"`
Node
Contents FileContents `json:"contents,omitempty"`
}
type FileContents struct {
@@ -55,12 +25,3 @@ type FileContents struct {
Source Url `json:"source,omitempty"`
Verification Verification `json:"verification,omitempty"`
}
type FileMode os.FileMode
func (m FileMode) Validate() report.Report {
if (m &^ 07777) != 0 {
return report.ReportFromError(ErrFileIllegalMode, report.EntryError)
}
return report.Report{}
}

View File

@@ -29,8 +29,9 @@ var (
)
type Ignition struct {
Version IgnitionVersion `json:"version,omitempty" merge:"old"`
Config IgnitionConfig `json:"config,omitempty" merge:"new"`
Version IgnitionVersion `json:"version,omitempty" merge:"old"`
Config IgnitionConfig `json:"config,omitempty" merge:"new"`
Timeouts Timeouts `json:"timeouts,omitempty" merge:"new"`
}
type IgnitionConfig struct {

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

@@ -0,0 +1,60 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"errors"
"os"
"github.com/coreos/ignition/config/validate/report"
)
var (
ErrNoFilesystem = errors.New("no filesystem specified")
ErrFileIllegalMode = errors.New("illegal file mode")
)
// Node represents all common info for files (special types, e.g. directories, included).
type Node struct {
Filesystem string `json:"filesystem,omitempty"`
Path Path `json:"path,omitempty"`
Mode NodeMode `json:"mode,omitempty"`
User NodeUser `json:"user,omitempty"`
Group NodeGroup `json:"group,omitempty"`
}
type NodeUser struct {
Id int `json:"id,omitempty"`
}
type NodeGroup struct {
Id int `json:"id,omitempty"`
}
func (n Node) Validate() report.Report {
if n.Filesystem == "" {
return report.ReportFromError(ErrNoFilesystem, report.EntryError)
}
return report.Report{}
}
type NodeMode os.FileMode
func (m NodeMode) Validate() report.Report {
if (m &^ 07777) != 0 {
return report.ReportFromError(ErrFileIllegalMode, report.EntryError)
}
return report.Report{}
}

View File

@@ -16,7 +16,7 @@ package types
import (
"errors"
"path/filepath"
"path"
"github.com/coreos/ignition/config/validate/report"
)
@@ -32,7 +32,7 @@ func (p Path) MarshalJSON() ([]byte, error) {
}
func (p Path) Validate() report.Report {
if !filepath.IsAbs(string(p)) {
if !path.IsAbs(string(p)) {
return report.ReportFromError(ErrPathRelative, report.EntryError)
}
return report.Report{}

View File

@@ -19,4 +19,5 @@ type Storage struct {
Arrays []Raid `json:"raid,omitempty"`
Filesystems []Filesystem `json:"filesystems,omitempty"`
Files []File `json:"files,omitempty"`
Directories []Directory `json:"directories,omitempty"`
}

View File

@@ -14,9 +14,7 @@
package types
type Raid struct {
Name string `yaml:"name"`
Level string `yaml:"level"`
Devices []string `yaml:"devices"`
Spares int `yaml:"spares"`
type Timeouts struct {
HttpResponseHeaders *int `json:"httpResponseHeaders,omitempty"`
HttpTotal *int `json:"httpTotal,omitempty"`
}

View File

@@ -15,8 +15,12 @@
package types
import (
"bytes"
"errors"
"path/filepath"
"fmt"
"path"
"github.com/coreos/go-systemd/unit"
"github.com/coreos/ignition/config/validate/report"
)
@@ -29,15 +33,31 @@ type SystemdUnit struct {
DropIns []SystemdUnitDropIn `json:"dropins,omitempty"`
}
func (u SystemdUnit) Validate() report.Report {
if err := validateUnitContent(u.Contents); err != nil {
return report.ReportFromError(err, report.EntryError)
}
return report.Report{}
}
type SystemdUnitDropIn struct {
Name SystemdUnitDropInName `json:"name,omitempty"`
Contents string `json:"contents,omitempty"`
}
func (u SystemdUnitDropIn) Validate() report.Report {
if err := validateUnitContent(u.Contents); err != nil {
return report.ReportFromError(err, report.EntryError)
}
return report.Report{}
}
type SystemdUnitName string
func (n SystemdUnitName) Validate() report.Report {
switch filepath.Ext(string(n)) {
switch path.Ext(string(n)) {
case ".service", ".socket", ".device", ".mount", ".automount", ".swap", ".target", ".path", ".timer", ".snapshot", ".slice", ".scope":
return report.Report{}
default:
@@ -48,7 +68,7 @@ func (n SystemdUnitName) Validate() report.Report {
type SystemdUnitDropInName string
func (n SystemdUnitDropInName) Validate() report.Report {
switch filepath.Ext(string(n)) {
switch path.Ext(string(n)) {
case ".conf":
return report.Report{}
default:
@@ -61,13 +81,31 @@ type NetworkdUnit struct {
Contents string `json:"contents,omitempty"`
}
func (u NetworkdUnit) Validate() report.Report {
if err := validateUnitContent(u.Contents); err != nil {
return report.ReportFromError(err, report.EntryError)
}
return report.Report{}
}
type NetworkdUnitName string
func (n NetworkdUnitName) Validate() report.Report {
switch filepath.Ext(string(n)) {
switch path.Ext(string(n)) {
case ".link", ".netdev", ".network":
return report.Report{}
default:
return report.ReportFromError(errors.New("invalid networkd unit extension"), report.EntryError)
}
}
func validateUnitContent(content string) error {
c := bytes.NewBufferString(content)
_, err := unit.Deserialize(c)
if err != nil {
return fmt.Errorf("invalid unit content: %s", err)
}
return nil
}

View File

@@ -20,6 +20,7 @@ import (
"net/url"
"github.com/coreos/ignition/config/validate/report"
"github.com/vincent-petithory/dataurl"
)
var (
@@ -58,9 +59,14 @@ func (u Url) Validate() report.Report {
return report.Report{}
}
switch url.URL(u).Scheme {
case "http", "https", "oem", "data":
case "http", "https", "oem":
return report.Report{}
case "data":
if _, err := dataurl.DecodeString(u.String()); err != nil {
return report.ReportFromError(err, report.EntryError)
}
return report.Report{}
default:
return report.ReportFromError(ErrInvalidScheme, report.EntryError)
}
return report.ReportFromError(ErrInvalidScheme, report.EntryError)
}

View File

@@ -17,7 +17,7 @@ package types
import (
"encoding/json"
"errors"
"path/filepath"
"path"
)
var (
@@ -25,19 +25,18 @@ var (
)
type Path string
type path Path
func (d *Path) UnmarshalJSON(data []byte) error {
td := path(*d)
if err := json.Unmarshal(data, &td); err != nil {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
*d = Path(td)
*d = Path(s)
return d.AssertValid()
}
func (d Path) AssertValid() error {
if !filepath.IsAbs(string(d)) {
if !path.IsAbs(string(d)) {
return ErrPathRelative
}
return nil

View File

@@ -17,7 +17,7 @@ package types
import (
"encoding/json"
"errors"
"path/filepath"
"path"
)
type SystemdUnit struct {
@@ -46,7 +46,7 @@ func (n *SystemdUnitName) UnmarshalJSON(data []byte) error {
}
func (n SystemdUnitName) AssertValid() error {
switch filepath.Ext(string(n)) {
switch path.Ext(string(n)) {
case ".service", ".socket", ".device", ".mount", ".automount", ".swap", ".target", ".path", ".timer", ".snapshot", ".slice", ".scope":
return nil
default:
@@ -67,7 +67,7 @@ func (n *SystemdUnitDropInName) UnmarshalJSON(data []byte) error {
}
func (n SystemdUnitDropInName) AssertValid() error {
switch filepath.Ext(string(n)) {
switch path.Ext(string(n)) {
case ".conf":
return nil
default:
@@ -93,7 +93,7 @@ func (n *NetworkdUnitName) UnmarshalJSON(data []byte) error {
}
func (n NetworkdUnitName) AssertValid() error {
switch filepath.Ext(string(n)) {
switch path.Ext(string(n)) {
case ".link", ".netdev", ".network":
return nil
default:

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 v2_0
import (
"reflect"
"github.com/coreos/ignition/config/v2_0/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()
}

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 v2_0
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
}
}

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

@@ -0,0 +1,120 @@
// 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 v2_0
import (
"bytes"
"errors"
"reflect"
"github.com/coreos/ignition/config/v2_0/types"
"github.com/coreos/ignition/config/validate"
astjson "github.com/coreos/ignition/config/validate/astjson"
"github.com/coreos/ignition/config/validate/report"
json "github.com/ajeddeloh/go-json"
"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")
ErrInvalid = errors.New("config is not valid")
)
// Parse parses the raw config into a types.Config struct and generates a report of any
// errors, warnings, info, and deprecations it encountered
func Parse(rawConfig []byte) (types.Config, report.Report, error) {
if isEmpty(rawConfig) {
return types.Config{}, report.Report{}, ErrEmpty
} else if isCloudConfig(rawConfig) {
return types.Config{}, report.Report{}, ErrCloudConfig
} else if isScript(rawConfig) {
return types.Config{}, report.Report{}, ErrScript
}
var err error
var config types.Config
// These errors are fatal and the config should not be further validated
if err = json.Unmarshal(rawConfig, &config); err == nil {
versionReport := config.Ignition.Version.Validate()
if versionReport.IsFatal() {
return types.Config{}, versionReport, ErrInvalid
}
}
// Handle json syntax and type errors first, since they are fatal but have offset info
if serr, ok := err.(*json.SyntaxError); ok {
line, col, highlight := errorutil.HighlightBytePosition(bytes.NewReader(rawConfig), serr.Offset)
return types.Config{},
report.Report{
Entries: []report.Entry{{
Kind: report.EntryError,
Message: serr.Error(),
Line: line,
Column: col,
Highlight: highlight,
}},
},
ErrInvalid
}
if terr, ok := err.(*json.UnmarshalTypeError); ok {
line, col, highlight := errorutil.HighlightBytePosition(bytes.NewReader(rawConfig), terr.Offset)
return types.Config{},
report.Report{
Entries: []report.Entry{{
Kind: report.EntryError,
Message: terr.Error(),
Line: line,
Column: col,
Highlight: highlight,
}},
},
ErrInvalid
}
// Handle other fatal errors (i.e. invalid version)
if err != nil {
return types.Config{}, report.ReportFromError(err, report.EntryError), err
}
// Unmarshal again to a json.Node to get offset information for building a report
var ast json.Node
var r report.Report
configValue := reflect.ValueOf(config)
if err := json.Unmarshal(rawConfig, &ast); err != nil {
r.Add(report.Entry{
Kind: report.EntryWarning,
Message: "Ignition could not unmarshal your config for reporting line numbers. This should never happen. Please file a bug.",
})
r.Merge(validate.ValidateWithoutSource(configValue))
} else {
r.Merge(validate.Validate(configValue, astjson.FromJsonRoot(ast), bytes.NewReader(rawConfig)))
}
if r.IsFatal() {
return types.Config{}, r, ErrInvalid
}
return config, r, nil
}
func isEmpty(userdata []byte) bool {
return len(userdata) == 0
}

View File

@@ -0,0 +1,36 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"errors"
"github.com/coreos/ignition/config/validate/report"
)
var (
ErrCompressionInvalid = errors.New("invalid compression method")
)
type Compression string
func (c Compression) Validate() report.Report {
switch c {
case "", "gzip":
default:
return report.ReportFromError(ErrCompressionInvalid, report.EntryError)
}
return report.Report{}
}

View File

@@ -0,0 +1,87 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"github.com/coreos/go-semver/semver"
"github.com/coreos/ignition/config/validate/report"
)
var (
MaxVersion = semver.Version{
Major: 2,
Minor: 0,
}
)
type Config struct {
Ignition Ignition `json:"ignition"`
Storage Storage `json:"storage,omitempty"`
Systemd Systemd `json:"systemd,omitempty"`
Networkd Networkd `json:"networkd,omitempty"`
Passwd Passwd `json:"passwd,omitempty"`
}
func (c Config) Validate() report.Report {
r := report.Report{}
rules := []rule{
checkFilesFilesystems,
checkDuplicateFilesystems,
}
for _, rule := range rules {
rule(c, &r)
}
return r
}
type rule func(cfg Config, report *report.Report)
func checkFilesFilesystems(cfg Config, r *report.Report) {
filesystems := map[string]struct{}{"root": {}}
for _, filesystem := range cfg.Storage.Filesystems {
filesystems[filesystem.Name] = struct{}{}
}
for _, file := range cfg.Storage.Files {
if file.Filesystem == "" {
// Filesystem was not specified. This is an error, but its handled in types.File's Validate, not here
continue
}
_, ok := filesystems[file.Filesystem]
if !ok {
r.Add(report.Entry{
Kind: report.EntryWarning,
Message: fmt.Sprintf("File %q references nonexistent filesystem %q. (This is ok if it is defined in a referenced config)",
file.Path, file.Filesystem),
})
}
}
}
func checkDuplicateFilesystems(cfg Config, r *report.Report) {
filesystems := map[string]struct{}{"root": {}}
for _, filesystem := range cfg.Storage.Filesystems {
if _, ok := filesystems[filesystem.Name]; ok {
r.Add(report.Entry{
Kind: report.EntryWarning,
Message: fmt.Sprintf("Filesystem %q shadows exising filesystem definition", filesystem.Name),
})
}
filesystems[filesystem.Name] = struct{}{}
}
}

View File

@@ -0,0 +1,124 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"github.com/coreos/ignition/config/validate/report"
)
type Disk struct {
Device Path `json:"device,omitempty"`
WipeTable bool `json:"wipeTable,omitempty"`
Partitions []Partition `json:"partitions,omitempty"`
}
func (n Disk) Validate() report.Report {
r := report.Report{}
if len(n.Device) == 0 {
r.Add(report.Entry{
Message: "disk device is required",
Kind: report.EntryError,
})
}
if n.partitionNumbersCollide() {
r.Add(report.Entry{
Message: fmt.Sprintf("disk %q: partition numbers collide", n.Device),
Kind: report.EntryError,
})
}
if n.partitionsOverlap() {
r.Add(report.Entry{
Message: fmt.Sprintf("disk %q: partitions overlap", n.Device),
Kind: report.EntryError,
})
}
if n.partitionsMisaligned() {
r.Add(report.Entry{
Message: fmt.Sprintf("disk %q: partitions misaligned", n.Device),
Kind: report.EntryError,
})
}
// Disks which have no errors at this point will likely succeed in sgdisk
return r
}
// partitionNumbersCollide returns true if partition numbers in n.Partitions are not unique.
func (n Disk) partitionNumbersCollide() bool {
m := map[int][]Partition{}
for _, p := range n.Partitions {
m[p.Number] = append(m[p.Number], p)
}
for _, n := range m {
if len(n) > 1 {
// TODO(vc): return information describing the collision for logging
return true
}
}
return false
}
// end returns the last sector of a partition.
func (p Partition) end() 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
}

View File

@@ -0,0 +1,66 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"errors"
"os"
"github.com/coreos/ignition/config/validate/report"
)
var (
ErrFileIllegalMode = errors.New("illegal file mode")
ErrNoFilesystem = errors.New("no filesystem specified")
)
type File struct {
Filesystem string `json:"filesystem,omitempty"`
Path Path `json:"path,omitempty"`
Contents FileContents `json:"contents,omitempty"`
Mode FileMode `json:"mode,omitempty"`
User FileUser `json:"user,omitempty"`
Group FileGroup `json:"group,omitempty"`
}
func (f File) Validate() report.Report {
if f.Filesystem == "" {
return report.ReportFromError(ErrNoFilesystem, report.EntryError)
}
return report.Report{}
}
type FileUser struct {
Id int `json:"id,omitempty"`
}
type FileGroup struct {
Id int `json:"id,omitempty"`
}
type FileContents struct {
Compression Compression `json:"compression,omitempty"`
Source Url `json:"source,omitempty"`
Verification Verification `json:"verification,omitempty"`
}
type FileMode os.FileMode
func (m FileMode) Validate() report.Report {
if (m &^ 07777) != 0 {
return report.ReportFromError(ErrFileIllegalMode, report.EntryError)
}
return report.Report{}
}

View File

@@ -0,0 +1,67 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"errors"
"github.com/coreos/ignition/config/validate/report"
)
var (
ErrFilesystemInvalidFormat = errors.New("invalid filesystem format")
ErrFilesystemNoMountPath = errors.New("filesystem is missing mount or path")
ErrFilesystemMountAndPath = errors.New("filesystem has both mount and path defined")
)
type Filesystem struct {
Name string `json:"name,omitempty"`
Mount *FilesystemMount `json:"mount,omitempty"`
Path *Path `json:"path,omitempty"`
}
type FilesystemMount struct {
Device Path `json:"device,omitempty"`
Format FilesystemFormat `json:"format,omitempty"`
Create *FilesystemCreate `json:"create,omitempty"`
}
type FilesystemCreate struct {
Force bool `json:"force,omitempty"`
Options MkfsOptions `json:"options,omitempty"`
}
func (f Filesystem) Validate() report.Report {
if f.Mount == nil && f.Path == nil {
return report.ReportFromError(ErrFilesystemNoMountPath, report.EntryError)
}
if f.Mount != nil && f.Path != nil {
return report.ReportFromError(ErrFilesystemMountAndPath, report.EntryError)
}
return report.Report{}
}
type FilesystemFormat string
func (f FilesystemFormat) Validate() report.Report {
switch f {
case "ext4", "btrfs", "xfs":
return report.Report{}
default:
return report.ReportFromError(ErrFilesystemInvalidFormat, report.EntryError)
}
}
type MkfsOptions []string

View File

@@ -14,16 +14,9 @@
package types
type Disk struct {
Device string `yaml:"device"`
WipeTable bool `yaml:"wipe_table"`
Partitions []Partition `yaml:"partitions"`
}
type Partition struct {
Label string `yaml:"label"`
Number int `yaml:"number"`
Size string `yaml:"size"`
Start string `yaml:"start"`
TypeGUID string `yaml:"type_guid"`
type Group struct {
Name string `json:"name,omitempty"`
Gid *uint `json:"gid,omitempty"`
PasswordHash string `json:"passwordHash,omitempty"`
System bool `json:"system,omitempty"`
}

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 types
import (
"crypto"
"encoding/hex"
"encoding/json"
"errors"
"strings"
"github.com/coreos/ignition/config/validate/report"
)
var (
ErrHashMalformed = errors.New("malformed hash specifier")
ErrHashWrongSize = errors.New("incorrect size for hash sum")
ErrHashUnrecognized = errors.New("unrecognized hash function")
)
type Hash struct {
Function string
Sum string
}
func (h *Hash) UnmarshalJSON(data []byte) error {
var th string
if err := json.Unmarshal(data, &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 nil
}
func (h Hash) MarshalJSON() ([]byte, error) {
return []byte(`"` + h.Function + "-" + h.Sum + `"`), nil
}
func (h Hash) Validate() report.Report {
var hash crypto.Hash
switch h.Function {
case "sha512":
hash = crypto.SHA512
default:
return report.ReportFromError(ErrHashUnrecognized, report.EntryError)
}
if len(h.Sum) != hex.EncodedLen(hash.Size()) {
return report.ReportFromError(ErrHashWrongSize, report.EntryError)
}
return report.Report{}
}

View File

@@ -0,0 +1,69 @@
// 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"
"github.com/coreos/ignition/config/validate/report"
)
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" merge:"old"`
Config IgnitionConfig `json:"config,omitempty" merge:"new"`
}
type IgnitionConfig struct {
Append []ConfigReference `json:"append,omitempty"`
Replace *ConfigReference `json:"replace,omitempty"`
}
type ConfigReference struct {
Source Url `json:"source,omitempty"`
Verification Verification `json:"verification,omitempty"`
}
type IgnitionVersion semver.Version
func (v *IgnitionVersion) UnmarshalJSON(data []byte) error {
tv := semver.Version(*v)
if err := json.Unmarshal(data, &tv); err != nil {
return err
}
*v = IgnitionVersion(tv)
return nil
}
func (v IgnitionVersion) MarshalJSON() ([]byte, error) {
return semver.Version(v).MarshalJSON()
}
func (v IgnitionVersion) Validate() report.Report {
if MaxVersion.Major > v.Major {
return report.ReportFromError(ErrOldVersion, report.EntryError)
}
if MaxVersion.LessThan(semver.Version(v)) {
return report.ReportFromError(ErrNewVersion, report.EntryError)
}
return report.Report{}
}

View File

@@ -15,10 +15,5 @@
package types
type Networkd struct {
Units []NetworkdUnit `yaml:"units"`
}
type NetworkdUnit struct {
Name string `yaml:"name"`
Contents string `yaml:"contents"`
Units []NetworkdUnit `json:"units,omitempty"`
}

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 (
"fmt"
"regexp"
"github.com/coreos/ignition/config/validate/report"
)
type Partition struct {
Label PartitionLabel `json:"label,omitempty"`
Number int `json:"number"`
Size PartitionDimension `json:"size"`
Start PartitionDimension `json:"start"`
TypeGUID PartitionTypeGUID `json:"typeGuid,omitempty"`
}
type PartitionLabel string
func (n PartitionLabel) Validate() report.Report {
// http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_entries:
// 56 (0x38) 72 bytes Partition name (36 UTF-16LE code units)
// XXX(vc): note GPT calls it a name, we're using label for consistency
// with udev naming /dev/disk/by-partlabel/*.
if len(string(n)) > 36 {
return report.ReportFromError(fmt.Errorf("partition labels may not exceed 36 characters"), report.EntryError)
}
return report.Report{}
}
type PartitionDimension uint64
type PartitionTypeGUID string
func (d PartitionTypeGUID) Validate() report.Report {
ok, err := regexp.MatchString("^(|[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12})$", string(d))
if err != nil {
return report.ReportFromError(fmt.Errorf("error matching type-guid regexp: %v", err), report.EntryError)
}
if !ok {
return report.ReportFromError(fmt.Errorf(`partition type-guid must have the form "01234567-89AB-CDEF-EDCB-A98765432101", got: %q`, string(d)), report.EntryError)
}
return report.Report{}
}

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"`
Groups []Group `json:"groups,omitempty"`
}

View File

@@ -14,19 +14,26 @@
package types
type Systemd struct {
Units []SystemdUnit `yaml:"units"`
import (
"errors"
"path"
"github.com/coreos/ignition/config/validate/report"
)
var (
ErrPathRelative = errors.New("path not absolute")
)
type Path string
func (p Path) MarshalJSON() ([]byte, error) {
return []byte(`"` + string(p) + `"`), nil
}
type SystemdUnit struct {
Name string `yaml:"name"`
Enable bool `yaml:"enable"`
Mask bool `yaml:"mask"`
Contents string `yaml:"contents"`
DropIns []SystemdUnitDropIn `yaml:"dropins"`
}
type SystemdUnitDropIn struct {
Name string `yaml:"name"`
Contents string `yaml:"contents"`
func (p Path) Validate() report.Report {
if !path.IsAbs(string(p)) {
return report.ReportFromError(ErrPathRelative, report.EntryError)
}
return report.Report{}
}

View File

@@ -0,0 +1,45 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"github.com/coreos/ignition/config/validate/report"
)
type Raid struct {
Name string `json:"name"`
Level string `json:"level"`
Devices []Path `json:"devices,omitempty"`
Spares int `json:"spares,omitempty"`
}
func (n Raid) Validate() report.Report {
switch n.Level {
case "linear", "raid0", "0", "stripe":
if n.Spares != 0 {
return report.ReportFromError(fmt.Errorf("spares unsupported for %q arrays", n.Level), report.EntryError)
}
case "raid1", "1", "mirror":
case "raid4", "4":
case "raid5", "5":
case "raid6", "6":
case "raid10", "10":
default:
return report.ReportFromError(fmt.Errorf("unrecognized raid level: %q", n.Level), report.EntryError)
}
return report.Report{}
}

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"`
Arrays []Raid `json:"raid,omitempty"`
Filesystems []Filesystem `json:"filesystems,omitempty"`
Files []File `json:"files,omitempty"`
}

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"`
}

View File

@@ -0,0 +1,111 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"bytes"
"errors"
"fmt"
"path"
"github.com/coreos/go-systemd/unit"
"github.com/coreos/ignition/config/validate/report"
)
type SystemdUnit struct {
Name SystemdUnitName `json:"name,omitempty"`
Enable bool `json:"enable,omitempty"`
Mask bool `json:"mask,omitempty"`
Contents string `json:"contents,omitempty"`
DropIns []SystemdUnitDropIn `json:"dropins,omitempty"`
}
func (u SystemdUnit) Validate() report.Report {
if err := validateUnitContent(u.Contents); err != nil {
return report.ReportFromError(err, report.EntryError)
}
return report.Report{}
}
type SystemdUnitDropIn struct {
Name SystemdUnitDropInName `json:"name,omitempty"`
Contents string `json:"contents,omitempty"`
}
func (u SystemdUnitDropIn) Validate() report.Report {
if err := validateUnitContent(u.Contents); err != nil {
return report.ReportFromError(err, report.EntryError)
}
return report.Report{}
}
type SystemdUnitName string
func (n SystemdUnitName) Validate() report.Report {
switch path.Ext(string(n)) {
case ".service", ".socket", ".device", ".mount", ".automount", ".swap", ".target", ".path", ".timer", ".snapshot", ".slice", ".scope":
return report.Report{}
default:
return report.ReportFromError(errors.New("invalid systemd unit extension"), report.EntryError)
}
}
type SystemdUnitDropInName string
func (n SystemdUnitDropInName) Validate() report.Report {
switch path.Ext(string(n)) {
case ".conf":
return report.Report{}
default:
return report.ReportFromError(errors.New("invalid systemd unit drop-in extension"), report.EntryError)
}
}
type NetworkdUnit struct {
Name NetworkdUnitName `json:"name,omitempty"`
Contents string `json:"contents,omitempty"`
}
func (u NetworkdUnit) Validate() report.Report {
if err := validateUnitContent(u.Contents); err != nil {
return report.ReportFromError(err, report.EntryError)
}
return report.Report{}
}
type NetworkdUnitName string
func (n NetworkdUnitName) Validate() report.Report {
switch path.Ext(string(n)) {
case ".link", ".netdev", ".network":
return report.Report{}
default:
return report.ReportFromError(errors.New("invalid networkd unit extension"), report.EntryError)
}
}
func validateUnitContent(content string) error {
c := bytes.NewBufferString(content)
_, err := unit.Deserialize(c)
if err != nil {
return fmt.Errorf("invalid unit content: %s", err)
}
return nil
}

View File

@@ -0,0 +1,72 @@
// 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"
"net/url"
"github.com/coreos/ignition/config/validate/report"
"github.com/vincent-petithory/dataurl"
)
var (
ErrInvalidScheme = errors.New("invalid url scheme")
)
type Url url.URL
func (u *Url) UnmarshalJSON(data []byte) error {
var tu string
if err := json.Unmarshal(data, &tu); err != nil {
return err
}
pu, err := url.Parse(tu)
if err != nil {
return err
}
*u = Url(*pu)
return nil
}
func (u Url) MarshalJSON() ([]byte, error) {
return []byte(`"` + u.String() + `"`), nil
}
func (u Url) String() string {
tu := url.URL(u)
return (&tu).String()
}
func (u Url) Validate() report.Report {
// Empty url is valid, indicates an empty file
if u.String() == "" {
return report.Report{}
}
switch url.URL(u).Scheme {
case "http", "https", "oem":
return report.Report{}
case "data":
if _, err := dataurl.DecodeString(u.String()); err != nil {
return report.ReportFromError(err, report.EntryError)
}
return report.Report{}
default:
return report.ReportFromError(ErrInvalidScheme, report.EntryError)
}
}

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"`
PasswordHash string `json:"passwordHash,omitempty"`
SSHAuthorizedKeys []string `json:"sshAuthorizedKeys,omitempty"`
Create *UserCreate `json:"create,omitempty"`
}
type UserCreate struct {
Uid *uint `json:"uid,omitempty"`
GECOS string `json:"gecos,omitempty"`
Homedir string `json:"homeDir,omitempty"`
NoCreateHome bool `json:"noCreateHome,omitempty"`
PrimaryGroup string `json:"primaryGroup,omitempty"`
Groups []string `json:"groups,omitempty"`
NoUserGroup bool `json:"noUserGroup,omitempty"`
System bool `json:"system,omitempty"`
NoLogInit bool `json:"noLogInit,omitempty"`
Shell string `json:"shell,omitempty"`
}

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"`
}

View File

@@ -20,7 +20,6 @@ import (
"reflect"
"strings"
"github.com/coreos/ignition/config/types"
"github.com/coreos/ignition/config/validate/report"
)
@@ -75,10 +74,7 @@ func Validate(vObj reflect.Value, ast AstNode, source io.ReadSeeker) (r report.R
((vObj.Kind() != reflect.Ptr) ||
(!vObj.IsNil() && !vObj.Elem().Type().Implements(reflect.TypeOf((*validator)(nil)).Elem()))) {
sub_r := obj.Validate()
if vObj.Type() != reflect.TypeOf(types.Config{}) {
// Config checks are done on the config as a whole and shouldn't get line numbers
sub_r.AddPosition(line, col, highlight)
}
sub_r.AddPosition(line, col, highlight)
r.Merge(sub_r)
// Dont recurse on invalid inner nodes, it mostly leads to bogus messages
@@ -122,12 +118,18 @@ type field struct {
}
// getFields returns a field of all the fields in the struct, including the fields of
// embedded structs.
// embedded structs and structs inside interface{}'s
func getFields(vObj reflect.Value) []field {
if vObj.Kind() != reflect.Struct {
return nil
}
ret := []field{}
for i := 0; i < vObj.Type().NumField(); i++ {
if vObj.Type().Field(i).Anonymous {
ret = append(ret, getFields(vObj.Field(i))...)
// in the case of an embedded type that is an alias to interface, extract the
// real type contained by the interface
realObj := reflect.ValueOf(vObj.Field(i).Interface())
ret = append(ret, getFields(realObj)...)
} else {
ret = append(ret, field{Type: vObj.Type().Field(i), Value: vObj.Field(i)})
}