Ignition provider (#6189)

* providers: ignition, basic config, version and config

* providers: ignition, user and passwd example, general cache implementation

* vendor: Capture new dependency upstream-pkg

* providers: ignition ignition_user

* providers: ignition ignition_disk, ignition_group and ignition_raid

* providers: ignition_file and ignition_filesystem

* providers: ignition_systemd_unit and ignition_networkd_unit

* providers: ignition_config improvements

* vendor: Capture new dependency upstream-pkg

* providers: ignition main

* documentation: ignition provider

* providers: ignition minor changes

* providers: ignition, fix test

* fixing tests and latest versions
This commit is contained in:
Máximo Cuadros 2017-01-03 12:29:14 +01:00 committed by Paul Stack
parent 32030236cb
commit 85f0fba9f9
79 changed files with 6552 additions and 4 deletions

View File

@ -0,0 +1,12 @@
package main
import (
"github.com/hashicorp/terraform/builtin/providers/ignition"
"github.com/hashicorp/terraform/plugin"
)
func main() {
plugin.Serve(&plugin.ServeOpts{
ProviderFunc: ignition.Provider,
})
}

View File

@ -0,0 +1,202 @@
package ignition
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/url"
"sync"
"github.com/coreos/go-systemd/unit"
"github.com/coreos/ignition/config/types"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)
func Provider() terraform.ResourceProvider {
return &schema.Provider{
ResourcesMap: map[string]*schema.Resource{
"ignition_config": resourceConfig(),
"ignition_disk": resourceDisk(),
"ignition_raid": resourceRaid(),
"ignition_filesystem": resourceFilesystem(),
"ignition_file": resourceFile(),
"ignition_systemd_unit": resourceSystemdUnit(),
"ignition_networkd_unit": resourceNetworkdUnit(),
"ignition_user": resourceUser(),
"ignition_group": resourceGroup(),
},
ConfigureFunc: func(*schema.ResourceData) (interface{}, error) {
return &cache{
disks: make(map[string]*types.Disk, 0),
arrays: make(map[string]*types.Raid, 0),
filesystems: make(map[string]*types.Filesystem, 0),
files: make(map[string]*types.File, 0),
systemdUnits: make(map[string]*types.SystemdUnit, 0),
networkdUnits: make(map[string]*types.NetworkdUnit, 0),
users: make(map[string]*types.User, 0),
groups: make(map[string]*types.Group, 0),
}, nil
},
}
}
type cache struct {
disks map[string]*types.Disk
arrays map[string]*types.Raid
filesystems map[string]*types.Filesystem
files map[string]*types.File
systemdUnits map[string]*types.SystemdUnit
networkdUnits map[string]*types.NetworkdUnit
users map[string]*types.User
groups map[string]*types.Group
sync.Mutex
}
func (c *cache) addDisk(g *types.Disk) string {
c.Lock()
defer c.Unlock()
id := id(g)
c.disks[id] = g
return id
}
func (c *cache) addRaid(r *types.Raid) string {
c.Lock()
defer c.Unlock()
id := id(r)
c.arrays[id] = r
return id
}
func (c *cache) addFilesystem(f *types.Filesystem) string {
c.Lock()
defer c.Unlock()
id := id(f)
c.filesystems[id] = f
return id
}
func (c *cache) addFile(f *types.File) string {
c.Lock()
defer c.Unlock()
id := id(f)
c.files[id] = f
return id
}
func (c *cache) addSystemdUnit(u *types.SystemdUnit) string {
c.Lock()
defer c.Unlock()
id := id(u)
c.systemdUnits[id] = u
return id
}
func (c *cache) addNetworkdUnit(u *types.NetworkdUnit) string {
c.Lock()
defer c.Unlock()
id := id(u)
c.networkdUnits[id] = u
return id
}
func (c *cache) addUser(u *types.User) string {
c.Lock()
defer c.Unlock()
id := id(u)
c.users[id] = u
return id
}
func (c *cache) addGroup(g *types.Group) string {
c.Lock()
defer c.Unlock()
id := id(g)
c.groups[id] = g
return id
}
func id(input interface{}) string {
b, _ := json.Marshal(input)
return hash(string(b))
}
func hash(s string) string {
sha := sha256.Sum256([]byte(s))
return hex.EncodeToString(sha[:])
}
func castSliceInterface(i []interface{}) []string {
var o []string
for _, value := range i {
o = append(o, value.(string))
}
return o
}
func getUInt(d *schema.ResourceData, key string) *uint {
var uid *uint
if value, ok := d.GetOk(key); ok {
u := uint(value.(int))
uid = &u
}
return uid
}
func validateUnit(content string) error {
r := bytes.NewBuffer([]byte(content))
u, err := unit.Deserialize(r)
if len(u) == 0 {
return fmt.Errorf("invalid or empty unit content")
}
if err == nil {
return nil
}
if err == io.EOF {
return fmt.Errorf("unexpected EOF reading unit content")
}
return err
}
func buildURL(raw string) (types.Url, error) {
u, err := url.Parse(raw)
if err != nil {
return types.Url{}, err
}
return types.Url(*u), nil
}
func buildHash(raw string) (types.Hash, error) {
h := types.Hash{}
err := h.UnmarshalJSON([]byte(fmt.Sprintf("%q", raw)))
return h, err
}

View File

@ -0,0 +1,32 @@
package ignition
import (
"testing"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)
var testProviders = map[string]terraform.ResourceProvider{
"ignition": Provider(),
}
func TestProvider(t *testing.T) {
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
t.Fatalf("err: %s", err)
}
}
func TestValidateUnit(t *testing.T) {
if err := validateUnit(""); err == nil {
t.Fatalf("error not found, expected error")
}
if err := validateUnit("[foo]qux"); err == nil {
t.Fatalf("error not found, expected error")
}
if err := validateUnit("[foo]\nqux=foo\nfoo"); err == nil {
t.Fatalf("error not found, expected error")
}
}

View File

@ -0,0 +1,320 @@
package ignition
import (
"encoding/json"
"fmt"
"github.com/hashicorp/terraform/helper/schema"
"github.com/coreos/ignition/config/types"
)
var configReferenceResource = &schema.Resource{
Schema: map[string]*schema.Schema{
"source": &schema.Schema{
Type: schema.TypeString,
ForceNew: true,
Required: true,
},
"verification": &schema.Schema{
Type: schema.TypeString,
ForceNew: true,
Optional: true,
},
},
}
func resourceConfig() *schema.Resource {
return &schema.Resource{
Create: resourceIgnitionFileCreate,
Update: resourceIgnitionFileCreate,
Delete: resourceIgnitionFileDelete,
Exists: resourceIgnitionFileExists,
Read: resourceIgnitionFileRead,
Schema: map[string]*schema.Schema{
"disks": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"arrays": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"filesystems": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"files": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"systemd": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"networkd": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"users": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"groups": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"replace": &schema.Schema{
Type: schema.TypeList,
ForceNew: true,
Optional: true,
MaxItems: 1,
Elem: configReferenceResource,
},
"append": &schema.Schema{
Type: schema.TypeList,
ForceNew: true,
Optional: true,
Elem: configReferenceResource,
},
"rendered": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceIgnitionFileCreate(d *schema.ResourceData, meta interface{}) error {
rendered, err := renderConfig(d, meta.(*cache))
if err != nil {
return err
}
if err := d.Set("rendered", rendered); err != nil {
return err
}
d.SetId(hash(rendered))
return nil
}
func resourceIgnitionFileDelete(d *schema.ResourceData, meta interface{}) error {
d.SetId("")
return nil
}
func resourceIgnitionFileExists(d *schema.ResourceData, meta interface{}) (bool, error) {
rendered, err := renderConfig(d, meta.(*cache))
if err != nil {
return false, err
}
return hash(rendered) == d.Id(), nil
}
func resourceIgnitionFileRead(d *schema.ResourceData, meta interface{}) error {
return nil
}
func renderConfig(d *schema.ResourceData, c *cache) (string, error) {
i, err := buildConfig(d, c)
if err != nil {
return "", err
}
bytes, err := json.MarshalIndent(i, " ", " ")
if err != nil {
return "", err
}
return string(bytes), nil
}
func buildConfig(d *schema.ResourceData, c *cache) (*types.Config, error) {
var err error
config := &types.Config{}
config.Ignition, err = buildIgnition(d)
if err != nil {
return nil, err
}
config.Storage, err = buildStorage(d, c)
if err != nil {
return nil, err
}
config.Systemd, err = buildSystemd(d, c)
if err != nil {
return nil, err
}
config.Networkd, err = buildNetworkd(d, c)
if err != nil {
return nil, err
}
config.Passwd, err = buildPasswd(d, c)
if err != nil {
return nil, err
}
return config, nil
}
func buildIgnition(d *schema.ResourceData) (types.Ignition, error) {
var err error
i := types.Ignition{}
i.Version.UnmarshalJSON([]byte(`"2.0.0"`))
rr := d.Get("replace.0").(map[string]interface{})
if len(rr) != 0 {
i.Config.Replace, err = buildConfigReference(rr)
if err != nil {
return i, err
}
}
ar := d.Get("append").([]interface{})
if len(ar) != 0 {
for _, rr := range ar {
r, err := buildConfigReference(rr.(map[string]interface{}))
if err != nil {
return i, err
}
i.Config.Append = append(i.Config.Append, *r)
}
}
return i, nil
}
func buildConfigReference(raw map[string]interface{}) (*types.ConfigReference, error) {
r := &types.ConfigReference{}
src, err := buildURL(raw["source"].(string))
if err != nil {
return nil, err
}
r.Source = src
hash, err := buildHash(raw["verification"].(string))
if err != nil {
return nil, err
}
r.Verification.Hash = &hash
return r, nil
}
func buildStorage(d *schema.ResourceData, c *cache) (types.Storage, error) {
storage := types.Storage{}
for _, id := range d.Get("disks").([]interface{}) {
d, ok := c.disks[id.(string)]
if !ok {
return storage, fmt.Errorf("invalid disk %q, unknown disk id", id)
}
storage.Disks = append(storage.Disks, *d)
}
for _, id := range d.Get("arrays").([]interface{}) {
a, ok := c.arrays[id.(string)]
if !ok {
return storage, fmt.Errorf("invalid raid %q, unknown raid id", id)
}
storage.Arrays = append(storage.Arrays, *a)
}
for _, id := range d.Get("filesystems").([]interface{}) {
f, ok := c.filesystems[id.(string)]
if !ok {
return storage, fmt.Errorf("invalid filesystem %q, unknown filesystem id", id)
}
storage.Filesystems = append(storage.Filesystems, *f)
}
for _, id := range d.Get("files").([]interface{}) {
f, ok := c.files[id.(string)]
if !ok {
return storage, fmt.Errorf("invalid file %q, unknown file id", id)
}
storage.Files = append(storage.Files, *f)
}
return storage, nil
}
func buildSystemd(d *schema.ResourceData, c *cache) (types.Systemd, error) {
systemd := types.Systemd{}
for _, id := range d.Get("systemd").([]interface{}) {
u, ok := c.systemdUnits[id.(string)]
if !ok {
return systemd, fmt.Errorf("invalid systemd unit %q, unknown systemd unit id", id)
}
systemd.Units = append(systemd.Units, *u)
}
return systemd, nil
}
func buildNetworkd(d *schema.ResourceData, c *cache) (types.Networkd, error) {
networkd := types.Networkd{}
for _, id := range d.Get("networkd").([]interface{}) {
u, ok := c.networkdUnits[id.(string)]
if !ok {
return networkd, fmt.Errorf("invalid networkd unit %q, unknown networkd unit id", id)
}
networkd.Units = append(networkd.Units, *u)
}
return networkd, nil
}
func buildPasswd(d *schema.ResourceData, c *cache) (types.Passwd, error) {
passwd := types.Passwd{}
for _, id := range d.Get("users").([]interface{}) {
u, ok := c.users[id.(string)]
if !ok {
return passwd, fmt.Errorf("invalid user %q, unknown user id", id)
}
passwd.Users = append(passwd.Users, *u)
}
for _, id := range d.Get("groups").([]interface{}) {
g, ok := c.groups[id.(string)]
if !ok {
return passwd, fmt.Errorf("invalid group %q, unknown group id", id)
}
passwd.Groups = append(passwd.Groups, *g)
}
return passwd, nil
}

View File

@ -0,0 +1,101 @@
package ignition
import (
"encoding/json"
"fmt"
"testing"
"github.com/coreos/ignition/config/types"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestIngnitionFileReplace(t *testing.T) {
testIgnition(t, `
resource "ignition_config" "test" {
replace {
source = "foo"
verification = "sha512-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
}
`, func(c *types.Config) error {
r := c.Ignition.Config.Replace
if r == nil {
return fmt.Errorf("unable to find replace config")
}
if r.Source.String() != "foo" {
return fmt.Errorf("config.replace.source, found %q", r.Source)
}
if r.Verification.Hash.Sum != "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" {
return fmt.Errorf("config.replace.verification, found %q", r.Verification.Hash)
}
return nil
})
}
func TestIngnitionFileAppend(t *testing.T) {
testIgnition(t, `
resource "ignition_config" "test" {
append {
source = "foo"
verification = "sha512-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
append {
source = "foo"
verification = "sha512-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
}
`, func(c *types.Config) error {
a := c.Ignition.Config.Append
if len(a) != 2 {
return fmt.Errorf("unable to find append config, expected 2")
}
if a[0].Source.String() != "foo" {
return fmt.Errorf("config.replace.source, found %q", a[0].Source)
}
if a[0].Verification.Hash.Sum != "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" {
return fmt.Errorf("config.replace.verification, found %q", a[0].Verification.Hash)
}
return nil
})
}
func testIgnition(t *testing.T, input string, assert func(*types.Config) error) {
check := func(s *terraform.State) error {
got := s.RootModule().Outputs["rendered"].Value.(string)
c := &types.Config{}
err := json.Unmarshal([]byte(got), c)
if err != nil {
return err
}
return assert(c)
}
resource.Test(t, resource.TestCase{
Providers: testProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(testTemplate, input),
Check: check,
},
},
})
}
var testTemplate = `
%s
output "rendered" {
value = "${ignition_config.test.rendered}"
}
`

View File

@ -0,0 +1,110 @@
package ignition
import (
"github.com/coreos/ignition/config/types"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceDisk() *schema.Resource {
return &schema.Resource{
Create: resourceDiskCreate,
Delete: resourceDiskDelete,
Exists: resourceDiskExists,
Read: resourceDiskRead,
Schema: map[string]*schema.Schema{
"device": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"wipe_table": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
},
"partition": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"label": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"number": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
"size": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
"start": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
"type_guid": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
},
},
},
}
}
func resourceDiskCreate(d *schema.ResourceData, meta interface{}) error {
id, err := buildDisk(d, meta.(*cache))
if err != nil {
return err
}
d.SetId(id)
return nil
}
func resourceDiskDelete(d *schema.ResourceData, meta interface{}) error {
d.SetId("")
return nil
}
func resourceDiskExists(d *schema.ResourceData, meta interface{}) (bool, error) {
id, err := buildDisk(d, meta.(*cache))
if err != nil {
return false, err
}
return id == d.Id(), nil
}
func resourceDiskRead(d *schema.ResourceData, meta interface{}) error {
return nil
}
func buildDisk(d *schema.ResourceData, c *cache) (string, error) {
var partitions []types.Partition
for _, raw := range d.Get("partition").([]interface{}) {
v := raw.(map[string]interface{})
partitions = append(partitions, types.Partition{
Label: types.PartitionLabel(v["label"].(string)),
Number: v["number"].(int),
Size: types.PartitionDimension(v["size"].(int)),
Start: types.PartitionDimension(v["start"].(int)),
TypeGUID: types.PartitionTypeGUID(v["type_guid"].(string)),
})
}
return c.addDisk(&types.Disk{
Device: types.Path(d.Get("device").(string)),
WipeTable: d.Get("wipe_table").(bool),
Partitions: partitions,
}), nil
}

View File

@ -0,0 +1,60 @@
package ignition
import (
"fmt"
"testing"
"github.com/coreos/ignition/config/types"
)
func TestIngnitionDisk(t *testing.T) {
testIgnition(t, `
resource "ignition_disk" "foo" {
device = "/foo"
partition {
label = "qux"
size = 42
start = 2048
type_guid = "01234567-89AB-CDEF-EDCB-A98765432101"
}
}
resource "ignition_config" "test" {
disks = [
"${ignition_disk.foo.id}",
]
}
`, func(c *types.Config) error {
if len(c.Storage.Disks) != 1 {
return fmt.Errorf("disks, found %d", len(c.Storage.Disks))
}
d := c.Storage.Disks[0]
if d.Device != "/foo" {
return fmt.Errorf("name, found %q", d.Device)
}
if len(d.Partitions) != 1 {
return fmt.Errorf("parition, found %d", len(d.Partitions))
}
p := d.Partitions[0]
if p.Label != "qux" {
return fmt.Errorf("parition.0.label, found %q", p.Label)
}
if p.Size != 42 {
return fmt.Errorf("parition.0.size, found %q", p.Size)
}
if p.Start != 2048 {
return fmt.Errorf("parition.0.start, found %q", p.Start)
}
if p.TypeGUID != "01234567-89AB-CDEF-EDCB-A98765432101" {
return fmt.Errorf("parition.0.type_guid, found %q", p.TypeGUID)
}
return nil
})
}

View File

@ -0,0 +1,189 @@
package ignition
import (
"encoding/base64"
"fmt"
"github.com/coreos/ignition/config/types"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceFile() *schema.Resource {
return &schema.Resource{
Create: resourceFileCreate,
Delete: resourceFileDelete,
Exists: resourceFileExists,
Read: resourceFileRead,
Schema: map[string]*schema.Schema{
"filesystem": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"path": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"content": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"mime": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: "text/plain",
},
"content": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
},
},
},
"source": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"source": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"compression": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"verification": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
},
},
"mode": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
"uid": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
"gid": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
},
}
}
func resourceFileCreate(d *schema.ResourceData, meta interface{}) error {
id, err := buildFile(d, meta.(*cache))
if err != nil {
return err
}
d.SetId(id)
return nil
}
func resourceFileDelete(d *schema.ResourceData, meta interface{}) error {
d.SetId("")
return nil
}
func resourceFileExists(d *schema.ResourceData, meta interface{}) (bool, error) {
id, err := buildFile(d, meta.(*cache))
if err != nil {
return false, err
}
return id == d.Id(), nil
}
func resourceFileRead(d *schema.ResourceData, metacontent interface{}) error {
return nil
}
func buildFile(d *schema.ResourceData, c *cache) (string, error) {
_, hasContent := d.GetOk("content")
_, hasSource := d.GetOk("source")
if hasContent && hasSource {
return "", fmt.Errorf("content and source options are incompatible")
}
if !hasContent && !hasSource {
return "", fmt.Errorf("content or source options must be present")
}
var compression types.Compression
var source types.Url
var hash *types.Hash
var err error
if hasContent {
source, err = encodeDataURL(
d.Get("content.0.mime").(string),
d.Get("content.0.content").(string),
)
if err != nil {
return "", err
}
}
if hasSource {
source, err = buildURL(d.Get("source.0.source").(string))
if err != nil {
return "", err
}
compression = types.Compression(d.Get("source.0.compression").(string))
h, err := buildHash(d.Get("source.0.verification").(string))
if err != nil {
return "", err
}
hash = &h
}
return c.addFile(&types.File{
Filesystem: d.Get("filesystem").(string),
Path: types.Path(d.Get("path").(string)),
Contents: types.FileContents{
Compression: compression,
Source: source,
Verification: types.Verification{
Hash: hash,
},
},
User: types.FileUser{
Id: d.Get("uid").(int),
},
Group: types.FileGroup{
Id: d.Get("gid").(int),
},
Mode: types.FileMode(d.Get("mode").(int)),
}), nil
}
func encodeDataURL(mime, content string) (types.Url, error) {
base64 := base64.StdEncoding.EncodeToString([]byte(content))
return buildURL(
fmt.Sprintf("data:%s;charset=utf-8;base64,%s", mime, base64),
)
}

View File

@ -0,0 +1,92 @@
package ignition
import (
"fmt"
"testing"
"github.com/coreos/ignition/config/types"
)
func TestIngnitionFile(t *testing.T) {
testIgnition(t, `
resource "ignition_file" "foo" {
filesystem = "foo"
path = "/foo"
content {
content = "foo"
}
mode = 420
uid = 42
gid = 84
}
resource "ignition_file" "qux" {
filesystem = "qux"
path = "/qux"
source {
source = "qux"
compression = "gzip"
verification = "sha512-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
}
resource "ignition_config" "test" {
files = [
"${ignition_file.foo.id}",
"${ignition_file.qux.id}",
]
}
`, func(c *types.Config) error {
if len(c.Storage.Files) != 2 {
return fmt.Errorf("arrays, found %d", len(c.Storage.Arrays))
}
f := c.Storage.Files[0]
if f.Filesystem != "foo" {
return fmt.Errorf("filesystem, found %q", f.Filesystem)
}
if f.Path != "/foo" {
return fmt.Errorf("path, found %q", f.Path)
}
if f.Contents.Source.String() != "data:text/plain;charset=utf-8;base64,Zm9v" {
return fmt.Errorf("contents.source, found %q", f.Contents.Source)
}
if f.Mode != types.FileMode(420) {
return fmt.Errorf("mode, found %q", f.Mode)
}
if f.User.Id != 42 {
return fmt.Errorf("uid, found %q", f.User.Id)
}
if f.Group.Id != 84 {
return fmt.Errorf("gid, found %q", f.Group.Id)
}
f = c.Storage.Files[1]
if f.Filesystem != "qux" {
return fmt.Errorf("filesystem, found %q", f.Filesystem)
}
if f.Path != "/qux" {
return fmt.Errorf("path, found %q", f.Path)
}
if f.Contents.Source.String() != "qux" {
return fmt.Errorf("contents.source, found %q", f.Contents.Source)
}
if f.Contents.Compression != "gzip" {
return fmt.Errorf("contents.compression, found %q", f.Contents.Compression)
}
if f.Contents.Verification.Hash.Sum != "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" {
return fmt.Errorf("config.replace.verification, found %q", f.Contents.Verification.Hash)
}
return nil
})
}

View File

@ -0,0 +1,113 @@
package ignition
import (
"github.com/coreos/ignition/config/types"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceFilesystem() *schema.Resource {
return &schema.Resource{
Create: resourceFilesystemCreate,
Delete: resourceFilesystemDelete,
Exists: resourceFilesystemExists,
Read: resourceFilesystemRead,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"mount": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"device": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"format": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"force": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
},
"options": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
},
},
"path": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}
func resourceFilesystemCreate(d *schema.ResourceData, meta interface{}) error {
id, err := buildFilesystem(d, meta.(*cache))
if err != nil {
return err
}
d.SetId(id)
return nil
}
func resourceFilesystemDelete(d *schema.ResourceData, meta interface{}) error {
d.SetId("")
return nil
}
func resourceFilesystemExists(d *schema.ResourceData, meta interface{}) (bool, error) {
id, err := buildFilesystem(d, meta.(*cache))
if err != nil {
return false, err
}
return id == d.Id(), nil
}
func resourceFilesystemRead(d *schema.ResourceData, meta interface{}) error {
return nil
}
func buildFilesystem(d *schema.ResourceData, c *cache) (string, error) {
var mount *types.FilesystemMount
if _, ok := d.GetOk("mount"); ok {
mount = &types.FilesystemMount{
Device: types.Path(d.Get("mount.0.device").(string)),
Format: types.FilesystemFormat(d.Get("mount.0.format").(string)),
}
force, hasForce := d.GetOk("mount.0.force")
options, hasOptions := d.GetOk("mount.0.options")
if hasOptions || hasForce {
mount.Create = &types.FilesystemCreate{
Force: force.(bool),
Options: castSliceInterface(options.([]interface{})),
}
}
}
path := types.Path(d.Get("path").(string))
return c.addFilesystem(&types.Filesystem{
Name: d.Get("name").(string),
Mount: mount,
Path: &path,
}), nil
}

View File

@ -0,0 +1,100 @@
package ignition
import (
"fmt"
"testing"
"github.com/coreos/ignition/config/types"
)
func TestIngnitionFilesystem(t *testing.T) {
testIgnition(t, `
resource "ignition_filesystem" "foo" {
name = "foo"
path = "/foo"
}
resource "ignition_filesystem" "qux" {
name = "qux"
mount {
device = "/qux"
format = "ext4"
}
}
resource "ignition_filesystem" "bar" {
name = "bar"
mount {
device = "/bar"
format = "ext4"
force = true
options = ["rw"]
}
}
resource "ignition_config" "test" {
filesystems = [
"${ignition_filesystem.foo.id}",
"${ignition_filesystem.qux.id}",
"${ignition_filesystem.bar.id}",
]
}
`, func(c *types.Config) error {
if len(c.Storage.Filesystems) != 3 {
return fmt.Errorf("disks, found %d", len(c.Storage.Filesystems))
}
f := c.Storage.Filesystems[0]
if f.Name != "foo" {
return fmt.Errorf("name, found %q", f.Name)
}
if f.Mount != nil {
return fmt.Errorf("mount, found %q", f.Mount.Device)
}
if string(*f.Path) != "/foo" {
return fmt.Errorf("path, found %q", f.Path)
}
f = c.Storage.Filesystems[1]
if f.Name != "qux" {
return fmt.Errorf("name, found %q", f.Name)
}
if f.Mount.Device != "/qux" {
return fmt.Errorf("mount.0.device, found %q", f.Mount.Device)
}
if f.Mount.Format != "ext4" {
return fmt.Errorf("mount.0.format, found %q", f.Mount.Format)
}
if f.Mount.Create != nil {
return fmt.Errorf("mount, create was found %q", f.Mount.Create)
}
f = c.Storage.Filesystems[2]
if f.Name != "bar" {
return fmt.Errorf("name, found %q", f.Name)
}
if f.Mount.Device != "/bar" {
return fmt.Errorf("mount.0.device, found %q", f.Mount.Device)
}
if f.Mount.Format != "ext4" {
return fmt.Errorf("mount.0.format, found %q", f.Mount.Format)
}
if f.Mount.Create.Force != true {
return fmt.Errorf("mount.0.force, found %q", f.Mount.Create.Force)
}
if len(f.Mount.Create.Options) != 1 || f.Mount.Create.Options[0] != "rw" {
return fmt.Errorf("mount.0.options, found %q", f.Mount.Create.Options)
}
return nil
})
}

View File

@ -0,0 +1,68 @@
package ignition
import (
"github.com/coreos/ignition/config/types"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceGroup() *schema.Resource {
return &schema.Resource{
Create: resourceGroupCreate,
Delete: resourceGroupDelete,
Exists: resourceGroupExists,
Read: resourceGroupRead,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"gid": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
"password_hash": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}
func resourceGroupCreate(d *schema.ResourceData, meta interface{}) error {
id, err := buildGroup(d, meta.(*cache))
if err != nil {
return err
}
d.SetId(id)
return nil
}
func resourceGroupDelete(d *schema.ResourceData, meta interface{}) error {
d.SetId("")
return nil
}
func resourceGroupExists(d *schema.ResourceData, meta interface{}) (bool, error) {
id, err := buildGroup(d, meta.(*cache))
if err != nil {
return false, err
}
return id == d.Id(), nil
}
func resourceGroupRead(d *schema.ResourceData, meta interface{}) error {
return nil
}
func buildGroup(d *schema.ResourceData, c *cache) (string, error) {
return c.addGroup(&types.Group{
Name: d.Get("name").(string),
PasswordHash: d.Get("password_hash").(string),
Gid: getUInt(d, "gid"),
}), nil
}

View File

@ -0,0 +1,59 @@
package ignition
import (
"fmt"
"testing"
"github.com/coreos/ignition/config/types"
)
func TestIngnitionGroup(t *testing.T) {
testIgnition(t, `
resource "ignition_group" "foo" {
name = "foo"
password_hash = "password"
gid = 42
}
resource "ignition_group" "qux" {
name = "qux"
}
resource "ignition_config" "test" {
groups = [
"${ignition_group.foo.id}",
"${ignition_group.qux.id}",
]
}
`, func(c *types.Config) error {
if len(c.Passwd.Groups) != 2 {
return fmt.Errorf("groups, found %d", len(c.Passwd.Groups))
}
g := c.Passwd.Groups[0]
if g.Name != "foo" {
return fmt.Errorf("name, found %q", g.Name)
}
if g.PasswordHash != "password" {
return fmt.Errorf("password_hash, found %q", g.PasswordHash)
}
if g.Gid == nil || *g.Gid != uint(42) {
return fmt.Errorf("gid, found %q", *g.Gid)
}
g = c.Passwd.Groups[1]
if g.Name != "qux" {
return fmt.Errorf("name, found %q", g.Name)
}
if g.Gid != nil {
return fmt.Errorf("uid, found %d", *g.Gid)
}
return nil
})
}

View File

@ -0,0 +1,66 @@
package ignition
import (
"github.com/coreos/ignition/config/types"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceNetworkdUnit() *schema.Resource {
return &schema.Resource{
Create: resourceNetworkdUnitCreate,
Delete: resourceNetworkdUnitDelete,
Exists: resourceNetworkdUnitExists,
Read: resourceNetworkdUnitRead,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"content": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}
func resourceNetworkdUnitCreate(d *schema.ResourceData, meta interface{}) error {
id, err := buildNetworkdUnit(d, meta.(*cache))
if err != nil {
return err
}
d.SetId(id)
return nil
}
func resourceNetworkdUnitDelete(d *schema.ResourceData, meta interface{}) error {
d.SetId("")
return nil
}
func resourceNetworkdUnitExists(d *schema.ResourceData, meta interface{}) (bool, error) {
id, err := buildNetworkdUnit(d, meta.(*cache))
if err != nil {
return false, err
}
return id == d.Id(), nil
}
func resourceNetworkdUnitRead(d *schema.ResourceData, meta interface{}) error {
return nil
}
func buildNetworkdUnit(d *schema.ResourceData, c *cache) (string, error) {
if err := validateUnit(d.Get("content").(string)); err != nil {
return "", err
}
return c.addNetworkdUnit(&types.NetworkdUnit{
Name: types.NetworkdUnitName(d.Get("name").(string)),
Contents: d.Get("content").(string),
}), nil
}

View File

@ -0,0 +1,39 @@
package ignition
import (
"fmt"
"testing"
"github.com/coreos/ignition/config/types"
)
func TestIngnitionNetworkdUnit(t *testing.T) {
testIgnition(t, `
resource "ignition_networkd_unit" "foo" {
name = "foo.link"
content = "[Match]\nName=eth0\n\n[Network]\nAddress=10.0.1.7\n"
}
resource "ignition_config" "test" {
networkd = [
"${ignition_networkd_unit.foo.id}",
]
}
`, func(c *types.Config) error {
if len(c.Networkd.Units) != 1 {
return fmt.Errorf("networkd, found %d", len(c.Networkd.Units))
}
u := c.Networkd.Units[0]
if u.Name != "foo.link" {
return fmt.Errorf("name, found %q", u.Name)
}
if u.Contents != "[Match]\nName=eth0\n\n[Network]\nAddress=10.0.1.7\n" {
return fmt.Errorf("content, found %q", u.Contents)
}
return nil
})
}

View File

@ -0,0 +1,80 @@
package ignition
import (
"github.com/coreos/ignition/config/types"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceRaid() *schema.Resource {
return &schema.Resource{
Create: resourceRaidCreate,
Delete: resourceRaidDelete,
Exists: resourceRaidExists,
Read: resourceRaidRead,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"level": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"devices": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"spares": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
},
}
}
func resourceRaidCreate(d *schema.ResourceData, meta interface{}) error {
id, err := buildRaid(d, meta.(*cache))
if err != nil {
return err
}
d.SetId(id)
return nil
}
func resourceRaidDelete(d *schema.ResourceData, meta interface{}) error {
d.SetId("")
return nil
}
func resourceRaidExists(d *schema.ResourceData, meta interface{}) (bool, error) {
id, err := buildRaid(d, meta.(*cache))
if err != nil {
return false, err
}
return id == d.Id(), nil
}
func resourceRaidRead(d *schema.ResourceData, meta interface{}) error {
return nil
}
func buildRaid(d *schema.ResourceData, c *cache) (string, error) {
var devices []types.Path
for _, value := range d.Get("devices").([]interface{}) {
devices = append(devices, types.Path(value.(string)))
}
return c.addRaid(&types.Raid{
Name: d.Get("name").(string),
Level: d.Get("level").(string),
Devices: devices,
Spares: d.Get("spares").(int),
}), nil
}

View File

@ -0,0 +1,48 @@
package ignition
import (
"fmt"
"testing"
"github.com/coreos/ignition/config/types"
)
func TestIngnitionRaid(t *testing.T) {
testIgnition(t, `
resource "ignition_raid" "foo" {
name = "foo"
level = "raid10"
devices = ["/foo"]
spares = 42
}
resource "ignition_config" "test" {
arrays = [
"${ignition_raid.foo.id}",
]
}
`, func(c *types.Config) error {
if len(c.Storage.Arrays) != 1 {
return fmt.Errorf("arrays, found %d", len(c.Storage.Arrays))
}
a := c.Storage.Arrays[0]
if a.Name != "foo" {
return fmt.Errorf("name, found %q", a.Name)
}
if len(a.Devices) != 1 || a.Devices[0] != "/foo" {
return fmt.Errorf("devices, found %d", a.Devices)
}
if a.Level != "raid10" {
return fmt.Errorf("level, found %q", a.Level)
}
if a.Spares != 42 {
return fmt.Errorf("spares, found %q", a.Spares)
}
return nil
})
}

View File

@ -0,0 +1,113 @@
package ignition
import (
"github.com/coreos/ignition/config/types"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceSystemdUnit() *schema.Resource {
return &schema.Resource{
Create: resourceSystemdUnitCreate,
Delete: resourceSystemdUnitDelete,
Exists: resourceSystemdUnitExists,
Read: resourceSystemdUnitRead,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"enable": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: true,
ForceNew: true,
},
"mask": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
},
"content": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"dropin": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"content": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
},
},
},
}
}
func resourceSystemdUnitCreate(d *schema.ResourceData, meta interface{}) error {
id, err := buildSystemdUnit(d, meta.(*cache))
if err != nil {
return err
}
d.SetId(id)
return nil
}
func resourceSystemdUnitDelete(d *schema.ResourceData, meta interface{}) error {
d.SetId("")
return nil
}
func resourceSystemdUnitExists(d *schema.ResourceData, meta interface{}) (bool, error) {
id, err := buildSystemdUnit(d, meta.(*cache))
if err != nil {
return false, err
}
return id == d.Id(), nil
}
func resourceSystemdUnitRead(d *schema.ResourceData, meta interface{}) error {
return nil
}
func buildSystemdUnit(d *schema.ResourceData, c *cache) (string, error) {
if err := validateUnit(d.Get("content").(string)); err != nil {
return "", err
}
var dropins []types.SystemdUnitDropIn
for _, raw := range d.Get("dropin").([]interface{}) {
value := raw.(map[string]interface{})
if err := validateUnit(value["content"].(string)); err != nil {
return "", err
}
dropins = append(dropins, types.SystemdUnitDropIn{
Name: types.SystemdUnitDropInName(value["name"].(string)),
Contents: value["content"].(string),
})
}
return c.addSystemdUnit(&types.SystemdUnit{
Name: types.SystemdUnitName(d.Get("name").(string)),
Contents: d.Get("content").(string),
Enable: d.Get("enable").(bool),
Mask: d.Get("mask").(bool),
DropIns: dropins,
}), nil
}

View File

@ -0,0 +1,58 @@
package ignition
import (
"fmt"
"testing"
"github.com/coreos/ignition/config/types"
)
func TestIngnitionSystemdUnit(t *testing.T) {
testIgnition(t, `
resource "ignition_systemd_unit" "foo" {
name = "foo.service"
content = "[Match]\nName=eth0\n\n[Network]\nAddress=10.0.1.7\n"
enable = false
mask = true
dropin {
name = "foo.conf"
content = "[Match]\nName=eth0\n\n[Network]\nAddress=10.0.1.7\n"
}
}
resource "ignition_config" "test" {
systemd = [
"${ignition_systemd_unit.foo.id}",
]
}
`, func(c *types.Config) error {
if len(c.Systemd.Units) != 1 {
return fmt.Errorf("systemd, found %d", len(c.Systemd.Units))
}
u := c.Systemd.Units[0]
if u.Name != "foo.service" {
return fmt.Errorf("name, found %q", u.Name)
}
if u.Contents != "[Match]\nName=eth0\n\n[Network]\nAddress=10.0.1.7\n" {
return fmt.Errorf("content, found %q", u.Contents)
}
if u.Mask != true {
return fmt.Errorf("mask, found %q", u.Mask)
}
if u.Enable != false {
return fmt.Errorf("enable, found %q", u.Enable)
}
if len(u.DropIns) != 1 {
return fmt.Errorf("dropins, found %q", u.DropIns)
}
return nil
})
}

View File

@ -0,0 +1,126 @@
package ignition
import (
"github.com/coreos/ignition/config/types"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceUser() *schema.Resource {
return &schema.Resource{
Create: resourceUserCreate,
Delete: resourceUserDelete,
Exists: resourceUserExists,
Read: resourceUserRead,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"password_hash": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"ssh_authorized_keys": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"uid": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
"gecos": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"home_dir": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"no_create_home": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
},
"primary_group": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"groups": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"no_user_group": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
},
"no_log_init": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
},
"shell": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}
func resourceUserCreate(d *schema.ResourceData, meta interface{}) error {
id, err := buildUser(d, meta.(*cache))
if err != nil {
return err
}
d.SetId(id)
return nil
}
func resourceUserDelete(d *schema.ResourceData, meta interface{}) error {
d.SetId("")
return nil
}
func resourceUserExists(d *schema.ResourceData, meta interface{}) (bool, error) {
id, err := buildUser(d, meta.(*cache))
if err != nil {
return false, err
}
return id == d.Id(), nil
}
func resourceUserRead(d *schema.ResourceData, meta interface{}) error {
return nil
}
func buildUser(d *schema.ResourceData, c *cache) (string, error) {
return c.addUser(&types.User{
Name: d.Get("name").(string),
PasswordHash: d.Get("password_hash").(string),
SSHAuthorizedKeys: castSliceInterface(d.Get("ssh_authorized_keys").([]interface{})),
Create: &types.UserCreate{
Uid: getUInt(d, "uid"),
GECOS: d.Get("gecos").(string),
Homedir: d.Get("home_dir").(string),
NoCreateHome: d.Get("no_create_home").(bool),
PrimaryGroup: d.Get("primary_group").(string),
Groups: castSliceInterface(d.Get("groups").([]interface{})),
NoUserGroup: d.Get("no_user_group").(bool),
NoLogInit: d.Get("no_log_init").(bool),
Shell: d.Get("shell").(string),
},
}), nil
}

View File

@ -0,0 +1,104 @@
package ignition
import (
"fmt"
"testing"
"github.com/coreos/ignition/config/types"
)
func TestIngnitionUser(t *testing.T) {
testIgnition(t, `
resource "ignition_user" "foo" {
name = "foo"
password_hash = "password"
ssh_authorized_keys = ["keys"]
uid = 42
gecos = "gecos"
home_dir = "home"
no_create_home = true
primary_group = "primary_group"
groups = ["group"]
no_user_group = true
no_log_init = true
shell = "shell"
}
resource "ignition_user" "qux" {
name = "qux"
}
resource "ignition_config" "test" {
users = [
"${ignition_user.foo.id}",
"${ignition_user.qux.id}",
]
}
`, func(c *types.Config) error {
if len(c.Passwd.Users) != 2 {
return fmt.Errorf("users, found %d", len(c.Passwd.Users))
}
u := c.Passwd.Users[0]
if u.Name != "foo" {
return fmt.Errorf("name, found %q", u.Name)
}
if u.PasswordHash != "password" {
return fmt.Errorf("password_hash, found %q", u.PasswordHash)
}
if len(u.SSHAuthorizedKeys) != 1 || u.SSHAuthorizedKeys[0] != "keys" {
return fmt.Errorf("ssh_authorized_keys, found %q", u.SSHAuthorizedKeys)
}
if *u.Create.Uid != uint(42) {
return fmt.Errorf("uid, found %q", *u.Create.Uid)
}
if u.Create.GECOS != "gecos" {
return fmt.Errorf("gecos, found %q", u.Create.GECOS)
}
if u.Create.Homedir != "home" {
return fmt.Errorf("home_dir, found %q", u.Create.Homedir)
}
if u.Create.NoCreateHome != true {
return fmt.Errorf("no_create_home, found %q", u.Create.NoCreateHome)
}
if u.Create.PrimaryGroup != "primary_group" {
return fmt.Errorf("primary_group, found %q", u.Create.PrimaryGroup)
}
if len(u.Create.Groups) != 1 || u.Create.Groups[0] != "group" {
return fmt.Errorf("groups, found %q", u.Create.Groups)
}
if u.Create.NoUserGroup != true {
return fmt.Errorf("no_create_home, found %q", u.Create.NoCreateHome)
}
if u.Create.NoLogInit != true {
return fmt.Errorf("no_log_init, found %q", u.Create.NoLogInit)
}
if u.Create.Shell != "shell" {
return fmt.Errorf("shell, found %q", u.Create.Shell)
}
u = c.Passwd.Users[1]
if u.Name != "qux" {
return fmt.Errorf("name, found %q", u.Name)
}
if u.Create.Uid != nil {
return fmt.Errorf("uid, found %d", *u.Create.Uid)
}
return nil
})
}

View File

@ -31,6 +31,7 @@ import (
grafanaprovider "github.com/hashicorp/terraform/builtin/providers/grafana"
herokuprovider "github.com/hashicorp/terraform/builtin/providers/heroku"
icinga2provider "github.com/hashicorp/terraform/builtin/providers/icinga2"
ignitionprovider "github.com/hashicorp/terraform/builtin/providers/ignition"
influxdbprovider "github.com/hashicorp/terraform/builtin/providers/influxdb"
libratoprovider "github.com/hashicorp/terraform/builtin/providers/librato"
logentriesprovider "github.com/hashicorp/terraform/builtin/providers/logentries"
@ -95,6 +96,7 @@ var InternalProviders = map[string]plugin.ProviderFunc{
"grafana": grafanaprovider.Provider,
"heroku": herokuprovider.Provider,
"icinga2": icinga2provider.Provider,
"ignition": ignitionprovider.Provider,
"influxdb": influxdbprovider.Provider,
"librato": libratoprovider.Provider,
"logentries": logentriesprovider.Provider,

19
vendor/github.com/alecthomas/units/COPYING generated vendored Normal file
View File

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

11
vendor/github.com/alecthomas/units/README.md generated vendored Normal file
View File

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

83
vendor/github.com/alecthomas/units/bytes.go generated vendored Normal file
View File

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

13
vendor/github.com/alecthomas/units/doc.go generated vendored Normal file
View File

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

26
vendor/github.com/alecthomas/units/si.go generated vendored Normal file
View File

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

138
vendor/github.com/alecthomas/units/util.go generated vendored Normal file
View File

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

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

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

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

@ -0,0 +1,244 @@
// Copyright 2013-2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Semantic Versions http://semver.org
package semver
import (
"bytes"
"errors"
"fmt"
"strconv"
"strings"
)
type Version struct {
Major int64
Minor int64
Patch int64
PreRelease PreRelease
Metadata string
}
type PreRelease string
func splitOff(input *string, delim string) (val string) {
parts := strings.SplitN(*input, delim, 2)
if len(parts) == 2 {
*input = parts[0]
val = parts[1]
}
return val
}
func NewVersion(version string) (*Version, error) {
v := Version{}
v.Metadata = splitOff(&version, "+")
v.PreRelease = PreRelease(splitOff(&version, "-"))
dotParts := strings.SplitN(version, ".", 3)
if len(dotParts) != 3 {
return nil, errors.New(fmt.Sprintf("%s is not in dotted-tri format", version))
}
parsed := make([]int64, 3, 3)
for i, v := range dotParts[:3] {
val, err := strconv.ParseInt(v, 10, 64)
parsed[i] = val
if err != nil {
return nil, err
}
}
v.Major = parsed[0]
v.Minor = parsed[1]
v.Patch = parsed[2]
return &v, nil
}
func Must(v *Version, err error) *Version {
if err != nil {
panic(err)
}
return v
}
func (v Version) String() string {
var buffer bytes.Buffer
fmt.Fprintf(&buffer, "%d.%d.%d", v.Major, v.Minor, v.Patch)
if v.PreRelease != "" {
fmt.Fprintf(&buffer, "-%s", v.PreRelease)
}
if v.Metadata != "" {
fmt.Fprintf(&buffer, "+%s", v.Metadata)
}
return buffer.String()
}
func (v Version) MarshalJSON() ([]byte, error) {
return []byte(`"` + v.String() + `"`), nil
}
func (v *Version) UnmarshalJSON(data []byte) error {
l := len(data)
if l == 0 || string(data) == `""` {
return nil
}
if l < 2 || data[0] != '"' || data[l-1] != '"' {
return errors.New("invalid semver string")
}
vv, err := NewVersion(string(data[1 : l-1]))
if err != nil {
return err
}
*v = *vv
return nil
}
func (v Version) LessThan(versionB Version) bool {
versionA := v
cmp := recursiveCompare(versionA.Slice(), versionB.Slice())
if cmp == 0 {
cmp = preReleaseCompare(versionA, versionB)
}
if cmp == -1 {
return true
}
return false
}
/* Slice converts the comparable parts of the semver into a slice of strings */
func (v Version) Slice() []int64 {
return []int64{v.Major, v.Minor, v.Patch}
}
func (p PreRelease) Slice() []string {
preRelease := string(p)
return strings.Split(preRelease, ".")
}
func preReleaseCompare(versionA Version, versionB Version) int {
a := versionA.PreRelease
b := versionB.PreRelease
/* Handle the case where if two versions are otherwise equal it is the
* one without a PreRelease that is greater */
if len(a) == 0 && (len(b) > 0) {
return 1
} else if len(b) == 0 && (len(a) > 0) {
return -1
}
// If there is a prelease, check and compare each part.
return recursivePreReleaseCompare(a.Slice(), b.Slice())
}
func recursiveCompare(versionA []int64, versionB []int64) int {
if len(versionA) == 0 {
return 0
}
a := versionA[0]
b := versionB[0]
if a > b {
return 1
} else if a < b {
return -1
}
return recursiveCompare(versionA[1:], versionB[1:])
}
func recursivePreReleaseCompare(versionA []string, versionB []string) int {
// Handle slice length disparity.
if len(versionA) == 0 {
// Nothing to compare too, so we return 0
return 0
} else if len(versionB) == 0 {
// We're longer than versionB so return 1.
return 1
}
a := versionA[0]
b := versionB[0]
aInt := false
bInt := false
aI, err := strconv.Atoi(versionA[0])
if err == nil {
aInt = true
}
bI, err := strconv.Atoi(versionB[0])
if err == nil {
bInt = true
}
// Handle Integer Comparison
if aInt && bInt {
if aI > bI {
return 1
} else if aI < bI {
return -1
}
}
// Handle String Comparison
if a > b {
return 1
} else if a < b {
return -1
}
return recursivePreReleaseCompare(versionA[1:], versionB[1:])
}
// BumpMajor increments the Major field by 1 and resets all other fields to their default values
func (v *Version) BumpMajor() {
v.Major += 1
v.Minor = 0
v.Patch = 0
v.PreRelease = PreRelease("")
v.Metadata = ""
}
// BumpMinor increments the Minor field by 1 and resets all other fields to their default values
func (v *Version) BumpMinor() {
v.Minor += 1
v.Patch = 0
v.PreRelease = PreRelease("")
v.Metadata = ""
}
// BumpPatch increments the Patch field by 1 and resets all other fields to their default values
func (v *Version) BumpPatch() {
v.Patch += 1
v.PreRelease = PreRelease("")
v.Metadata = ""
}

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

@ -0,0 +1,38 @@
// Copyright 2013-2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package semver
import (
"sort"
)
type Versions []*Version
func (s Versions) Len() int {
return len(s)
}
func (s Versions) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s Versions) Less(i, j int) bool {
return s[i].LessThan(*s[j])
}
// Sort sorts the given slice of Version
func Sort(versions []*Version) {
sort.Sort(Versions(versions))
}

191
vendor/github.com/coreos/go-systemd/LICENSE generated vendored Normal file
View File

@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of Copyright License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
3. Grant of Patent License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
4. Redistribution.
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of
this License; and
You must cause any modified files to carry prominent notices stating that You
changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
5. Submission of Contributions.
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
6. Trademarks.
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty.
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
8. Limitation of Liability.
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability.
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "[]" replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same "printed page" as the copyright notice for easier identification within
third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

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

202
vendor/github.com/coreos/ignition/LICENSE generated vendored Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

5
vendor/github.com/coreos/ignition/NOTICE generated vendored Normal file
View File

@ -0,0 +1,5 @@
CoreOS Project
Copyright 2015 CoreOS, Inc
This product includes software developed at CoreOS, Inc.
(http://www.coreos.com/).

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

124
vendor/github.com/coreos/ignition/config/types/disk.go generated vendored Normal file
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
}

66
vendor/github.com/coreos/ignition/config/types/file.go generated vendored Normal file
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

@ -0,0 +1,22 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
type Group struct {
Name string `json:"name,omitempty"`
Gid *uint `json:"gid,omitempty"`
PasswordHash string `json:"passwordHash,omitempty"`
System bool `json:"system,omitempty"`
}

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

@ -0,0 +1,73 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package 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

@ -0,0 +1,19 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
type Networkd struct {
Units []NetworkdUnit `json:"units,omitempty"`
}

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

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

@ -0,0 +1,39 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"errors"
"path/filepath"
"github.com/coreos/ignition/config/validate/report"
)
var (
ErrPathRelative = errors.New("path not absolute")
)
type Path string
func (p Path) MarshalJSON() ([]byte, error) {
return []byte(`"` + string(p) + `"`), nil
}
func (p Path) Validate() report.Report {
if !filepath.IsAbs(string(p)) {
return report.ReportFromError(ErrPathRelative, report.EntryError)
}
return report.Report{}
}

45
vendor/github.com/coreos/ignition/config/types/raid.go generated vendored Normal file
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"`
}

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

@ -0,0 +1,73 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"errors"
"path/filepath"
"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"`
}
type SystemdUnitDropIn struct {
Name SystemdUnitDropInName `json:"name,omitempty"`
Contents string `json:"contents,omitempty"`
}
type SystemdUnitName string
func (n SystemdUnitName) Validate() report.Report {
switch filepath.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 filepath.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"`
}
type NetworkdUnitName string
func (n NetworkdUnitName) Validate() report.Report {
switch filepath.Ext(string(n)) {
case ".link", ".netdev", ".network":
return report.Report{}
default:
return report.ReportFromError(errors.New("invalid networkd unit extension"), report.EntryError)
}
}

72
vendor/github.com/coreos/ignition/config/types/url.go generated vendored Normal file
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)
}
}

35
vendor/github.com/coreos/ignition/config/types/user.go generated vendored Normal file
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

@ -0,0 +1,158 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package report
import (
"bytes"
"encoding/json"
"fmt"
"sort"
)
type Report struct {
Entries []Entry
}
func (into *Report) Merge(from Report) {
into.Entries = append(into.Entries, from.Entries...)
}
func ReportFromError(err error, severity entryKind) Report {
if err == nil {
return Report{}
}
return Report{
Entries: []Entry{
{
Kind: severity,
Message: err.Error(),
},
},
}
}
// Sort sorts the entries by line number, then column number
func (r *Report) Sort() {
sort.Sort(entries(r.Entries))
}
type entries []Entry
func (e entries) Len() int {
return len(e)
}
func (e entries) Swap(i, j int) {
e[i], e[j] = e[j], e[i]
}
func (e entries) Less(i, j int) bool {
if e[i].Line != e[j].Line {
return e[i].Line < e[j].Line
}
return e[i].Column < e[j].Column
}
const (
EntryError entryKind = iota
EntryWarning
EntryInfo
EntryDeprecated
)
// AddPosition updates all the entries with Line equal to 0 and sets the Line/Column fields to line/column. This is useful for
// when a type has a custom unmarshaller and thus can't determine an exact offset of the error with the type. In this case
// the offset for the entire chunk of json that got unmarshalled to the type can be used instead, which is still pretty good.
func (r *Report) AddPosition(line, col int, highlight string) {
for i, e := range r.Entries {
if e.Line == 0 {
r.Entries[i].Line = line
r.Entries[i].Column = col
r.Entries[i].Highlight = highlight
}
}
}
func (r *Report) Add(e Entry) {
r.Entries = append(r.Entries, e)
}
func (r Report) String() string {
var errs bytes.Buffer
for i, entry := range r.Entries {
if i != 0 {
// Only add line breaks on multiline reports
errs.WriteString("\n")
}
errs.WriteString(entry.String())
}
return errs.String()
}
// IsFatal returns if there were any errors that make the config invalid
func (r Report) IsFatal() bool {
for _, entry := range r.Entries {
if entry.Kind == EntryError {
return true
}
}
return false
}
// IsDeprecated returns if the report has deprecations
func (r Report) IsDeprecated() bool {
for _, entry := range r.Entries {
if entry.Kind == EntryDeprecated {
return true
}
}
return false
}
type Entry struct {
Kind entryKind `json:"kind"`
Message string `json:"message"`
Line int `json:"line,omitempty"`
Column int `json:"column,omitempty"`
Highlight string `json:"-"`
}
func (e Entry) String() string {
if e.Line != 0 {
return fmt.Sprintf("%s at line %d, column %d\n%s%v", e.Kind.String(), e.Line, e.Column, e.Highlight, e.Message)
}
return fmt.Sprintf("%s: %v", e.Kind.String(), e.Message)
}
type entryKind int
func (e entryKind) String() string {
switch e {
case EntryError:
return "error"
case EntryWarning:
return "warning"
case EntryInfo:
return "info"
case EntryDeprecated:
return "deprecated"
default:
return "unknown error"
}
}
func (e entryKind) MarshalJSON() ([]byte, error) {
return json.Marshal(e.String())
}

20
vendor/github.com/vincent-petithory/dataurl/LICENSE generated vendored Normal file
View File

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

81
vendor/github.com/vincent-petithory/dataurl/README.md generated vendored Normal file
View File

@ -0,0 +1,81 @@
# Data URL Schemes for Go [![wercker status](https://app.wercker.com/status/6f9a2e144dfcc59e862c52459b452928/s "wercker status")](https://app.wercker.com/project/bykey/6f9a2e144dfcc59e862c52459b452928) [![GoDoc](https://godoc.org/github.com/vincent-petithory/dataurl?status.png)](https://godoc.org/github.com/vincent-petithory/dataurl)
This package parses and generates Data URL Schemes for the Go language, according to [RFC 2397](http://tools.ietf.org/html/rfc2397).
Data URLs are small chunks of data commonly used in browsers to display inline data,
typically like small images, or when you use the FileReader API of the browser.
Common use-cases:
* generate a data URL out of a `string`, `[]byte`, `io.Reader` for inclusion in HTML templates,
* parse a data URL sent by a browser in a http.Handler, and do something with the data (save to disk, etc.)
* ...
Install the package with:
~~~
go get github.com/vincent-petithory/dataurl
~~~
## Usage
~~~ go
package main
import (
"github.com/vincent-petithory/dataurl"
"fmt"
)
func main() {
dataURL, err := dataurl.DecodeString(`data:text/plain;charset=utf-8;base64,aGV5YQ==`)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("content type: %s, data: %s\n", dataURL.MediaType.ContentType(), string(dataURL.Data))
// Output: content type: text/plain, data: heya
}
~~~
From a `http.Handler`:
~~~ go
func handleDataURLUpload(w http.ResponseWriter, r *http.Request) {
dataURL, err := dataurl.Decode(r.Body)
defer r.Body.Close()
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if dataURL.ContentType() == "image/png" {
ioutil.WriteFile("image.png", dataURL.Data, 0644)
} else {
http.Error(w, "not a png", http.StatusBadRequest)
}
}
~~~
## Command
For convenience, a `dataurl` command is provided to encode/decode dataurl streams.
~~~
dataurl - Encode or decode dataurl data and print to standard output
Usage: dataurl [OPTION]... [FILE]
dataurl encodes or decodes FILE or standard input if FILE is - or omitted, and prints to standard output.
Unless -mimetype is used, when FILE is specified, dataurl will attempt to detect its mimetype using Go's mime.TypeByExtension (http://golang.org/pkg/mime/#TypeByExtension). If this fails or data is read from STDIN, the mimetype will default to application/octet-stream.
Options:
-a=false: encode data using ascii instead of base64
-ascii=false: encode data using ascii instead of base64
-d=false: decode data instead of encoding
-decode=false: decode data instead of encoding
-m="": force the mimetype of the data to encode to this value
-mimetype="": force the mimetype of the data to encode to this value
~~~
## Contributing
Feel free to file an issue/make a pull request if you find any bug, or want to suggest enhancements.

280
vendor/github.com/vincent-petithory/dataurl/dataurl.go generated vendored Normal file
View File

@ -0,0 +1,280 @@
package dataurl
import (
"bytes"
"encoding/base64"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"strconv"
"strings"
)
const (
// EncodingBase64 is base64 encoding for the data url
EncodingBase64 = "base64"
// EncodingASCII is ascii encoding for the data url
EncodingASCII = "ascii"
)
func defaultMediaType() MediaType {
return MediaType{
"text",
"plain",
map[string]string{"charset": "US-ASCII"},
}
}
// MediaType is the combination of a media type, a media subtype
// and optional parameters.
type MediaType struct {
Type string
Subtype string
Params map[string]string
}
// ContentType returns the content type of the dataurl's data, in the form type/subtype.
func (mt *MediaType) ContentType() string {
return fmt.Sprintf("%s/%s", mt.Type, mt.Subtype)
}
// String implements the Stringer interface.
//
// Params values are escaped with the Escape function, rather than in a quoted string.
func (mt *MediaType) String() string {
var buf bytes.Buffer
for k, v := range mt.Params {
fmt.Fprintf(&buf, ";%s=%s", k, EscapeString(v))
}
return mt.ContentType() + (&buf).String()
}
// DataURL is the combination of a MediaType describing the type of its Data.
type DataURL struct {
MediaType
Encoding string
Data []byte
}
// New returns a new DataURL initialized with data and
// a MediaType parsed from mediatype and paramPairs.
// mediatype must be of the form "type/subtype" or it will panic.
// paramPairs must have an even number of elements or it will panic.
// For more complex DataURL, initialize a DataURL struct.
// The DataURL is initialized with base64 encoding.
func New(data []byte, mediatype string, paramPairs ...string) *DataURL {
parts := strings.Split(mediatype, "/")
if len(parts) != 2 {
panic("dataurl: invalid mediatype")
}
nParams := len(paramPairs)
if nParams%2 != 0 {
panic("dataurl: requires an even number of param pairs")
}
params := make(map[string]string)
for i := 0; i < nParams; i += 2 {
params[paramPairs[i]] = paramPairs[i+1]
}
mt := MediaType{
parts[0],
parts[1],
params,
}
return &DataURL{
MediaType: mt,
Encoding: EncodingBase64,
Data: data,
}
}
// String implements the Stringer interface.
//
// Note: it doesn't guarantee the returned string is equal to
// the initial source string that was used to create this DataURL.
// The reasons for that are:
// * Insertion of default values for MediaType that were maybe not in the initial string,
// * Various ways to encode the MediaType parameters (quoted string or url encoded string, the latter is used),
func (du *DataURL) String() string {
var buf bytes.Buffer
du.WriteTo(&buf)
return (&buf).String()
}
// WriteTo implements the WriterTo interface.
// See the note about String().
func (du *DataURL) WriteTo(w io.Writer) (n int64, err error) {
var ni int
ni, _ = fmt.Fprint(w, "data:")
n += int64(ni)
ni, _ = fmt.Fprint(w, du.MediaType.String())
n += int64(ni)
if du.Encoding == EncodingBase64 {
ni, _ = fmt.Fprint(w, ";base64")
n += int64(ni)
}
ni, _ = fmt.Fprint(w, ",")
n += int64(ni)
if du.Encoding == EncodingBase64 {
encoder := base64.NewEncoder(base64.StdEncoding, w)
ni, err = encoder.Write(du.Data)
if err != nil {
return
}
encoder.Close()
} else if du.Encoding == EncodingASCII {
ni, _ = fmt.Fprint(w, Escape(du.Data))
n += int64(ni)
} else {
err = fmt.Errorf("dataurl: invalid encoding %s", du.Encoding)
return
}
return
}
// UnmarshalText decodes a Data URL string and sets it to *du
func (du *DataURL) UnmarshalText(text []byte) error {
decoded, err := DecodeString(string(text))
if err != nil {
return err
}
*du = *decoded
return nil
}
// MarshalText writes du as a Data URL
func (du *DataURL) MarshalText() ([]byte, error) {
buf := bytes.NewBuffer(nil)
if _, err := du.WriteTo(buf); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
type encodedDataReader func(string) ([]byte, error)
var asciiDataReader encodedDataReader = func(s string) ([]byte, error) {
us, err := Unescape(s)
if err != nil {
return nil, err
}
return []byte(us), nil
}
var base64DataReader encodedDataReader = func(s string) ([]byte, error) {
data, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return nil, err
}
return []byte(data), nil
}
type parser struct {
du *DataURL
l *lexer
currentAttr string
unquoteParamVal bool
encodedDataReaderFn encodedDataReader
}
func (p *parser) parse() error {
for item := range p.l.items {
switch item.t {
case itemError:
return errors.New(item.String())
case itemMediaType:
p.du.MediaType.Type = item.val
// Should we clear the default
// "charset" parameter at this point?
delete(p.du.MediaType.Params, "charset")
case itemMediaSubType:
p.du.MediaType.Subtype = item.val
case itemParamAttr:
p.currentAttr = item.val
case itemLeftStringQuote:
p.unquoteParamVal = true
case itemParamVal:
val := item.val
if p.unquoteParamVal {
p.unquoteParamVal = false
us, err := strconv.Unquote("\"" + val + "\"")
if err != nil {
return err
}
val = us
} else {
us, err := UnescapeToString(val)
if err != nil {
return err
}
val = us
}
p.du.MediaType.Params[p.currentAttr] = val
case itemBase64Enc:
p.du.Encoding = EncodingBase64
p.encodedDataReaderFn = base64DataReader
case itemDataComma:
if p.encodedDataReaderFn == nil {
p.encodedDataReaderFn = asciiDataReader
}
case itemData:
reader, err := p.encodedDataReaderFn(item.val)
if err != nil {
return err
}
p.du.Data = reader
case itemEOF:
if p.du.Data == nil {
p.du.Data = []byte("")
}
return nil
}
}
panic("EOF not found")
}
// DecodeString decodes a Data URL scheme string.
func DecodeString(s string) (*DataURL, error) {
du := &DataURL{
MediaType: defaultMediaType(),
Encoding: EncodingASCII,
}
parser := &parser{
du: du,
l: lex(s),
}
if err := parser.parse(); err != nil {
return nil, err
}
return du, nil
}
// Decode decodes a Data URL scheme from a io.Reader.
func Decode(r io.Reader) (*DataURL, error) {
data, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
return DecodeString(string(data))
}
// EncodeBytes encodes the data bytes into a Data URL string, using base 64 encoding.
//
// The media type of data is detected using http.DetectContentType.
func EncodeBytes(data []byte) string {
mt := http.DetectContentType(data)
// http.DetectContentType may add spurious spaces between ; and a parameter.
// The canonical way is to not have them.
cleanedMt := strings.Replace(mt, "; ", ";", -1)
return New(data, cleanedMt).String()
}

28
vendor/github.com/vincent-petithory/dataurl/doc.go generated vendored Normal file
View File

@ -0,0 +1,28 @@
/*
Package dataurl parses Data URL Schemes
according to RFC 2397
(http://tools.ietf.org/html/rfc2397).
Data URLs are small chunks of data commonly used in browsers to display inline data,
typically like small images, or when you use the FileReader API of the browser.
A dataurl looks like:
data:text/plain;charset=utf-8,A%20brief%20note
Or, with base64 encoding:
data:image/vnd.microsoft.icon;name=golang%20favicon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAD///8AVE44//7hdv/+4Xb//uF2//7hdv/+4Xb//uF2//7hdv/+4Xb//uF2//7hdv/+4Xb/
/uF2/1ROOP////8A////AFROOP/+4Xb//uF2//7hdv/+4Xb//uF2//7hdv/+4Xb//uF2//7hdv/+
...
/6CcjP97c07/e3NO/1dOMf9BOiX/TkUn/2VXLf97c07/e3NO/6CcjP/h4uX/////AP///wD///8A
////AP///wD///8A////AP///wDq6/H/3N/j/9fZ3f/q6/H/////AP///wD///8A////AP///wD/
//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAA==
Common functions are Decode and DecodeString to obtain a DataURL,
and DataURL.String() and DataURL.WriteTo to generate a Data URL string.
*/
package dataurl

521
vendor/github.com/vincent-petithory/dataurl/lex.go generated vendored Normal file
View File

@ -0,0 +1,521 @@
package dataurl
import (
"fmt"
"strings"
"unicode"
"unicode/utf8"
)
type item struct {
t itemType
val string
}
func (i item) String() string {
switch i.t {
case itemEOF:
return "EOF"
case itemError:
return i.val
}
if len(i.val) > 10 {
return fmt.Sprintf("%.10q...", i.val)
}
return fmt.Sprintf("%q", i.val)
}
type itemType int
const (
itemError itemType = iota
itemEOF
itemDataPrefix
itemMediaType
itemMediaSep
itemMediaSubType
itemParamSemicolon
itemParamAttr
itemParamEqual
itemLeftStringQuote
itemRightStringQuote
itemParamVal
itemBase64Enc
itemDataComma
itemData
)
const eof rune = -1
func isTokenRune(r rune) bool {
return r <= unicode.MaxASCII &&
!unicode.IsControl(r) &&
!unicode.IsSpace(r) &&
!isTSpecialRune(r)
}
func isTSpecialRune(r rune) bool {
return r == '(' ||
r == ')' ||
r == '<' ||
r == '>' ||
r == '@' ||
r == ',' ||
r == ';' ||
r == ':' ||
r == '\\' ||
r == '"' ||
r == '/' ||
r == '[' ||
r == ']' ||
r == '?' ||
r == '='
}
// See http://tools.ietf.org/html/rfc2045
// This doesn't include extension-token case
// as it's handled separatly
func isDiscreteType(s string) bool {
if strings.HasPrefix(s, "text") ||
strings.HasPrefix(s, "image") ||
strings.HasPrefix(s, "audio") ||
strings.HasPrefix(s, "video") ||
strings.HasPrefix(s, "application") {
return true
}
return false
}
// See http://tools.ietf.org/html/rfc2045
// This doesn't include extension-token case
// as it's handled separatly
func isCompositeType(s string) bool {
if strings.HasPrefix(s, "message") ||
strings.HasPrefix(s, "multipart") {
return true
}
return false
}
func isURLCharRune(r rune) bool {
// We're a bit permissive here,
// by not including '%' in delims
// This is okay, since url unescaping will validate
// that later in the parser.
return r <= unicode.MaxASCII &&
!(r >= 0x00 && r <= 0x1F) && r != 0x7F && /* control */
// delims
r != ' ' &&
r != '<' &&
r != '>' &&
r != '#' &&
r != '"' &&
// unwise
r != '{' &&
r != '}' &&
r != '|' &&
r != '\\' &&
r != '^' &&
r != '[' &&
r != ']' &&
r != '`'
}
func isBase64Rune(r rune) bool {
return (r >= 'a' && r <= 'z') ||
(r >= 'A' && r <= 'Z') ||
(r >= '0' && r <= '9') ||
r == '+' ||
r == '/' ||
r == '=' ||
r == '\n'
}
type stateFn func(*lexer) stateFn
// lexer lexes the data URL scheme input string.
// The implementation is from the text/template/parser package.
type lexer struct {
input string
start int
pos int
width int
seenBase64Item bool
items chan item
}
func (l *lexer) run() {
for state := lexBeforeDataPrefix; state != nil; {
state = state(l)
}
close(l.items)
}
func (l *lexer) emit(t itemType) {
l.items <- item{t, l.input[l.start:l.pos]}
l.start = l.pos
}
func (l *lexer) next() (r rune) {
if l.pos >= len(l.input) {
l.width = 0
return eof
}
r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
l.pos += l.width
return r
}
func (l *lexer) backup() {
l.pos -= l.width
}
func (l *lexer) ignore() {
l.start = l.pos
}
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
l.items <- item{itemError, fmt.Sprintf(format, args...)}
return nil
}
func lex(input string) *lexer {
l := &lexer{
input: input,
items: make(chan item),
}
go l.run() // Concurrently run state machine.
return l
}
const (
dataPrefix = "data:"
mediaSep = '/'
paramSemicolon = ';'
paramEqual = '='
dataComma = ','
)
// start lexing by detecting data prefix
func lexBeforeDataPrefix(l *lexer) stateFn {
if strings.HasPrefix(l.input[l.pos:], dataPrefix) {
return lexDataPrefix
}
return l.errorf("missing data prefix")
}
// lex data prefix
func lexDataPrefix(l *lexer) stateFn {
l.pos += len(dataPrefix)
l.emit(itemDataPrefix)
return lexAfterDataPrefix
}
// lex what's after data prefix.
// it can be the media type/subtype separator,
// the base64 encoding, or the comma preceding the data
func lexAfterDataPrefix(l *lexer) stateFn {
switch r := l.next(); {
case r == paramSemicolon:
l.backup()
return lexParamSemicolon
case r == dataComma:
l.backup()
return lexDataComma
case r == eof:
return l.errorf("missing comma before data")
case r == 'x' || r == 'X':
if l.next() == '-' {
return lexXTokenMediaType
}
return lexInDiscreteMediaType
case isTokenRune(r):
return lexInDiscreteMediaType
default:
return l.errorf("invalid character after data prefix")
}
}
func lexXTokenMediaType(l *lexer) stateFn {
for {
switch r := l.next(); {
case r == mediaSep:
l.backup()
return lexMediaType
case r == eof:
return l.errorf("missing media type slash")
case isTokenRune(r):
default:
return l.errorf("invalid character for media type")
}
}
}
func lexInDiscreteMediaType(l *lexer) stateFn {
for {
switch r := l.next(); {
case r == mediaSep:
l.backup()
// check it's valid discrete type
if !isDiscreteType(l.input[l.start:l.pos]) &&
!isCompositeType(l.input[l.start:l.pos]) {
return l.errorf("invalid media type")
}
return lexMediaType
case r == eof:
return l.errorf("missing media type slash")
case isTokenRune(r):
default:
return l.errorf("invalid character for media type")
}
}
}
func lexMediaType(l *lexer) stateFn {
if l.pos > l.start {
l.emit(itemMediaType)
}
return lexMediaSep
}
func lexMediaSep(l *lexer) stateFn {
l.next()
l.emit(itemMediaSep)
return lexAfterMediaSep
}
func lexAfterMediaSep(l *lexer) stateFn {
for {
switch r := l.next(); {
case r == paramSemicolon || r == dataComma:
l.backup()
return lexMediaSubType
case r == eof:
return l.errorf("incomplete media type")
case isTokenRune(r):
default:
return l.errorf("invalid character for media subtype")
}
}
}
func lexMediaSubType(l *lexer) stateFn {
if l.pos > l.start {
l.emit(itemMediaSubType)
}
return lexAfterMediaSubType
}
func lexAfterMediaSubType(l *lexer) stateFn {
switch r := l.next(); {
case r == paramSemicolon:
l.backup()
return lexParamSemicolon
case r == dataComma:
l.backup()
return lexDataComma
case r == eof:
return l.errorf("missing comma before data")
default:
return l.errorf("expected semicolon or comma")
}
}
func lexParamSemicolon(l *lexer) stateFn {
l.next()
l.emit(itemParamSemicolon)
return lexAfterParamSemicolon
}
func lexAfterParamSemicolon(l *lexer) stateFn {
switch r := l.next(); {
case r == eof:
return l.errorf("unterminated parameter sequence")
case r == paramEqual || r == dataComma:
return l.errorf("unterminated parameter sequence")
case isTokenRune(r):
l.backup()
return lexInParamAttr
default:
return l.errorf("invalid character for parameter attribute")
}
}
func lexBase64Enc(l *lexer) stateFn {
if l.pos > l.start {
if v := l.input[l.start:l.pos]; v != "base64" {
return l.errorf("expected base64, got %s", v)
}
l.seenBase64Item = true
l.emit(itemBase64Enc)
}
return lexDataComma
}
func lexInParamAttr(l *lexer) stateFn {
for {
switch r := l.next(); {
case r == paramEqual:
l.backup()
return lexParamAttr
case r == dataComma:
l.backup()
return lexBase64Enc
case r == eof:
return l.errorf("unterminated parameter sequence")
case isTokenRune(r):
default:
return l.errorf("invalid character for parameter attribute")
}
}
}
func lexParamAttr(l *lexer) stateFn {
if l.pos > l.start {
l.emit(itemParamAttr)
}
return lexParamEqual
}
func lexParamEqual(l *lexer) stateFn {
l.next()
l.emit(itemParamEqual)
return lexAfterParamEqual
}
func lexAfterParamEqual(l *lexer) stateFn {
switch r := l.next(); {
case r == '"':
l.emit(itemLeftStringQuote)
return lexInQuotedStringParamVal
case r == eof:
return l.errorf("missing comma before data")
case isTokenRune(r):
return lexInParamVal
default:
return l.errorf("invalid character for parameter value")
}
}
func lexInQuotedStringParamVal(l *lexer) stateFn {
for {
switch r := l.next(); {
case r == eof:
return l.errorf("unclosed quoted string")
case r == '\\':
return lexEscapedChar
case r == '"':
l.backup()
return lexQuotedStringParamVal
case r <= unicode.MaxASCII:
default:
return l.errorf("invalid character for parameter value")
}
}
}
func lexEscapedChar(l *lexer) stateFn {
switch r := l.next(); {
case r <= unicode.MaxASCII:
return lexInQuotedStringParamVal
case r == eof:
return l.errorf("unexpected eof")
default:
return l.errorf("invalid escaped character")
}
}
func lexInParamVal(l *lexer) stateFn {
for {
switch r := l.next(); {
case r == paramSemicolon || r == dataComma:
l.backup()
return lexParamVal
case r == eof:
return l.errorf("missing comma before data")
case isTokenRune(r):
default:
return l.errorf("invalid character for parameter value")
}
}
}
func lexQuotedStringParamVal(l *lexer) stateFn {
if l.pos > l.start {
l.emit(itemParamVal)
}
l.next()
l.emit(itemRightStringQuote)
return lexAfterParamVal
}
func lexParamVal(l *lexer) stateFn {
if l.pos > l.start {
l.emit(itemParamVal)
}
return lexAfterParamVal
}
func lexAfterParamVal(l *lexer) stateFn {
switch r := l.next(); {
case r == paramSemicolon:
l.backup()
return lexParamSemicolon
case r == dataComma:
l.backup()
return lexDataComma
case r == eof:
return l.errorf("missing comma before data")
default:
return l.errorf("expected semicolon or comma")
}
}
func lexDataComma(l *lexer) stateFn {
l.next()
l.emit(itemDataComma)
if l.seenBase64Item {
return lexBase64Data
}
return lexData
}
func lexData(l *lexer) stateFn {
Loop:
for {
switch r := l.next(); {
case r == eof:
break Loop
case isURLCharRune(r):
default:
return l.errorf("invalid data character")
}
}
if l.pos > l.start {
l.emit(itemData)
}
l.emit(itemEOF)
return nil
}
func lexBase64Data(l *lexer) stateFn {
Loop:
for {
switch r := l.next(); {
case r == eof:
break Loop
case isBase64Rune(r):
default:
return l.errorf("invalid data character")
}
}
if l.pos > l.start {
l.emit(itemData)
}
l.emit(itemEOF)
return nil
}

130
vendor/github.com/vincent-petithory/dataurl/rfc2396.go generated vendored Normal file
View File

@ -0,0 +1,130 @@
package dataurl
import (
"bytes"
"fmt"
"io"
"strings"
)
// Escape implements URL escaping, as defined in RFC 2397 (http://tools.ietf.org/html/rfc2397).
// It differs a bit from net/url's QueryEscape and QueryUnescape, e.g how spaces are treated (+ instead of %20):
//
// Only ASCII chars are allowed. Reserved chars are escaped to their %xx form.
// Unreserved chars are [a-z], [A-Z], [0-9], and -_.!~*\().
func Escape(data []byte) string {
var buf = new(bytes.Buffer)
for _, b := range data {
switch {
case isUnreserved(b):
buf.WriteByte(b)
default:
fmt.Fprintf(buf, "%%%02X", b)
}
}
return buf.String()
}
// EscapeString is like Escape, but taking
// a string as argument.
func EscapeString(s string) string {
return Escape([]byte(s))
}
// isUnreserved return true
// if the byte c is an unreserved char,
// as defined in RFC 2396.
func isUnreserved(c byte) bool {
return (c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
c == '-' ||
c == '_' ||
c == '.' ||
c == '!' ||
c == '~' ||
c == '*' ||
c == '\'' ||
c == '(' ||
c == ')'
}
func isHex(c byte) bool {
switch {
case c >= 'a' && c <= 'f':
return true
case c >= 'A' && c <= 'F':
return true
case c >= '0' && c <= '9':
return true
}
return false
}
// borrowed from net/url/url.go
func unhex(c byte) byte {
switch {
case '0' <= c && c <= '9':
return c - '0'
case 'a' <= c && c <= 'f':
return c - 'a' + 10
case 'A' <= c && c <= 'F':
return c - 'A' + 10
}
return 0
}
// Unescape unescapes a character sequence
// escaped with Escape(String?).
func Unescape(s string) ([]byte, error) {
var buf = new(bytes.Buffer)
reader := strings.NewReader(s)
for {
r, size, err := reader.ReadRune()
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
if size > 1 {
return nil, fmt.Errorf("rfc2396: non-ASCII char detected")
}
switch r {
case '%':
eb1, err := reader.ReadByte()
if err == io.EOF {
return nil, fmt.Errorf("rfc2396: unexpected end of unescape sequence")
}
if err != nil {
return nil, err
}
if !isHex(eb1) {
return nil, fmt.Errorf("rfc2396: invalid char 0x%x in unescape sequence", r)
}
eb0, err := reader.ReadByte()
if err == io.EOF {
return nil, fmt.Errorf("rfc2396: unexpected end of unescape sequence")
}
if err != nil {
return nil, err
}
if !isHex(eb0) {
return nil, fmt.Errorf("rfc2396: invalid char 0x%x in unescape sequence", r)
}
buf.WriteByte(unhex(eb0) + unhex(eb1)*16)
default:
buf.WriteByte(byte(r))
}
}
return buf.Bytes(), nil
}
// UnescapeToString is like Unescape, but returning
// a string.
func UnescapeToString(s string) (string, error) {
b, err := Unescape(s)
return string(b), err
}

View File

@ -0,0 +1 @@
box: wercker/default

18
vendor/vendor.json vendored
View File

@ -1037,6 +1037,18 @@
"path": "github.com/coreos/etcd/pkg/types",
"revision": "e5527914aa42cae3063f52892e1ca4518da0e4ae"
},
{
"checksumSHA1": "0sYnSIFxkXCVRYj3afRu8EyIl0g=",
"path": "github.com/coreos/ignition/config/types",
"revision": "211ab9d5a30be883c39a47642d05fdc6ce32b76e",
"revisionTime": "2016-12-23T13:37:58Z"
},
{
"checksumSHA1": "ViLVCc7CzzrGdJINAg8hv98CVkg=",
"path": "github.com/coreos/ignition/config/validate/report",
"revision": "211ab9d5a30be883c39a47642d05fdc6ce32b76e",
"revisionTime": "2016-12-23T13:37:58Z"
},
{
"path": "github.com/cyberdelia/heroku-go/v3",
"revision": "81c5afa1abcf69cc18ccc24fa3716b5a455c9208"
@ -2312,6 +2324,12 @@
"revision": "faddd6128c66c4708f45fdc007f575f75e592a3c",
"revisionTime": "2016-09-28T01:52:44Z"
},
{
"checksumSHA1": "CdE9OUEGLn2pAv1UMuM5rSlaUpM=",
"path": "github.com/vincent-petithory/dataurl",
"revision": "9a301d65acbb728fcc3ace14f45f511a4cfeea9c",
"revisionTime": "2016-03-30T18:21:26Z"
},
{
"comment": "v0.6.2",
"path": "github.com/vmware/govmomi",

View File

@ -31,6 +31,7 @@ body.layout-grafana,
body.layout-fastly,
body.layout-google,
body.layout-heroku,
body.layout-ignition,
body.layout-icinga2,
body.layout-influxdb,
body.layout-librato,

View File

@ -0,0 +1,41 @@
---
layout: "ignition"
page_title: "Provider: Ignition"
sidebar_current: "docs-ignition-index"
description: |-
The Ignition provider is used to generate Ignition configuration files used by CoreOS Linux.
---
# Ignition Provider
The Ignition provider is used to generate [Ignition](https://coreos.com/ignition/docs/latest/) configuration files. _Ignition_ is the provisioning utility used by [CoreOS](https://coreos.com/) Linux.
The ignition provider is what we call a _logical provider_ and doesn't manage any _physical_ resources. It generates configurations files to be used by other resources.
Use the navigation to the left to read about the available resources.
## Example Usage
This config will write a single service unit (shown below) with the contents of an example service. This unit will be enabled as a dependency of multi-user.target and therefore start on boot
```
# Systemd unit resource containing the unit definition
resource "ignition_systemd_unit" "example" {
name = "example.service"
content = "[Service]\nType=oneshot\nExecStart=/usr/bin/echo Hello World\n\n[Install]\nWantedBy=multi-user.target"
}
# Ingnition config include the previous defined systemd unit resource
resource "ignition_config" "example" {
systemd = [
"${ignition_systemd_unit.example.id}",
]
}
# Create a CoreOS server using the Igntion config.
resource "aws_instance" "web" {
# ...
user_data = "${ignition_config.example.rendered}"
}
```

View File

@ -0,0 +1,58 @@
---
layout: "ignition"
page_title: "Ignition: ignition_config"
sidebar_current: "docs-ignition-resource-config"
description: |-
Renders an ignition configuration as JSON
---
# ignition\_config
Renders an ignition configuration as JSON. It contains all the disks, partitions, arrays, filesystems, files, users, groups and units.
## Example Usage
```
resource "ignition_config" "example" {
systemd = [
"${ignition_systemd_unit.example.id}",
]
}
```
## Argument Reference
The following arguments are supported:
* `disks` - (Optional) The list of disks to be configured and their options.
* `arrays` - (Optional) The list of RAID arrays to be configured.
* `filesystems` - (Optional) The list of filesystems to be configured and/or used in the _ignition_file_ resource.
* `files` - (Optional) The list of files, rooted in this particular filesystem, to be written.
* `systemd` - (Optional) The list of systemd units. Describes the desired state of the systemd units.
* `networkd` - (Optional) The list of networkd units. Describes the desired state of the networkd files.
* `users` - (Optional) The list of accounts to be added.
* `groups` - (Optional) The list of groups to be added.
* `append` - (Optional) A block with config that will replace the current.
* `replace` - (Optional) Any number of blocks with the configs to be appended to the current config.
The `append` and `replace` blocks supports:
* `source` - (Required) The URL of the config. Supported schemes are http. Note: When using http, it is advisable to use the verification option to ensure the contents havent been modified.
* `verification` - (Optional) The hash of the config, in the form _\<type\>-\<value\>_ where type is sha512.
## Attributes Reference
The following attributes are exported:
* `rendered` - The final rendered template.

View File

@ -0,0 +1,54 @@
---
layout: "ignition"
page_title: "Ignition: ignition_disk"
sidebar_current: "docs-ignition-resource-disk"
description: |-
Describes the desired state of a systems disk.
---
# ignition\_disk
Describes the desired state of a systems disk.
## Example Usage
```
resource "ignition_disk" "foo" {
device = "/dev/sda"
partition {
start = 2048
size = 196037632
}
}
```
## Argument Reference
The following arguments are supported:
* `device` - (Required) The absolute path to the device. Devices are typically referenced by the _/dev/disk/by-*_ symlinks.
* `wipe_table` - (Optional) Whether or not the partition tables shall be wiped. When true, the partition tables are erased before any further manipulation. Otherwise, the existing entries are left intact.
* `partition` - (Optional) The list of partitions and their configuration for this particular disk..
The `partition` block supports:
* `label` - (Optional) The PARTLABEL for the partition.
* `number` - (Optional) The partition number, which dictates its position in the partition table (one-indexed). If zero, use the next available partition slot.
* `size` - (Optional) The size of the partition (in sectors). If zero, the partition will fill the remainder of the disk.
* `start` - (Optional) The start of the partition (in sectors). If zero, the partition will be positioned at the earliest available part of the disk.
* `type_guid` - (Optional) The GPT [partition type GUID](http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs). If omitted, the default will be _0FC63DAF-8483-4772-8E79-3D69D8477DE4_ (Linux filesystem data).
## Attributes Reference
The following attributes are exported:
* `id` - ID used to reference this resource in _ignition_config_.

View File

@ -0,0 +1,81 @@
---
layout: "ignition"
page_title: "Ignition: ignition_file"
sidebar_current: "docs-ignition-resource-file"
description: |-
Describes a file to be written in a particular filesystem.
---
# ignition\_file
Describes a file to be written in a particular filesystem.
## Example Usage
File with inline content:
```
resource "ignition_file" "hello" {
filesystem = "foo"
path = "/hello.txt"
content {
content = "Hello World!"
}
}
```
File with remote content:
```
resource "ignition_file" "hello" {
filesystem = "qux"
path = "/hello.txt"
source {
source = "http://example.com/hello.txt.gz"
compression = "gzip"
verification = "sha512-0123456789abcdef0123456789...456789abcdef"
}
}
```
## Argument Reference
The following arguments are supported:
* `filesystem` - (Optional) The internal identifier of the filesystem. This matches the last filesystem with the given identifier. This should be a valid name from a _ignition\_filesystem_ resource.
* `path` - (Optional) The absolute path to the file.
* `content` - (Optional) Block to provide the file content inline.
* `source` - (Optional) Block to retrieve the file content from a remote location.
__Note__: `content` and `source` are mutually exclusive
* `mode` - (Optional) The list of partitions and their configuration for
this particular disk..
* `uid` - (Optional) The user ID of the owner.
* `gid` - (Optional) The group ID of the owner.
The `content` block supports:
* `mime` - (Required) MIME format of the content (default _text/plain_).
* `content` - (Required) Content of the file.
The `source` block supports:
* `source` - (Required) The URL of the file contents. When using http, it is advisable to use the verification option to ensure the contents havent been modified.
* `compression` - (Optional) The type of compression used on the contents (null or gzip).
* `verification` - (Optional) The hash of the config, in the form _\<type\>-\<value\>_ where type is sha512.
## Attributes Reference
The following attributes are exported:
* `id` - ID used to reference this resource in _ignition_config_.

View File

@ -0,0 +1,52 @@
---
layout: "ignition"
page_title: "Ignition: ignition_filesystem"
sidebar_current: "docs-ignition-resource-filesystem"
description: |-
Describes the desired state of a systems filesystem.
---
# ignition\_filesystem
Describes the desired state of a the systems filesystems to be configured and/or used with the _ignition\_file_ resource.
## Example Usage
```
resource "ignition_filesystem" "foo" {
name = "root"
mount {
device = "/dev/disk/by-label/ROOT"
format = "xfs"
force = true
options = ["-L", "ROOT"]
}
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Optional) The identifier for the filesystem, internal to Ignition. This is only required if the filesystem needs to be referenced in the a _ignition\_files_ resource.
* `mount` - (Optional) Contains the set of mount and formatting options for the filesystem. A non-null entry indicates that the filesystem should be mounted before it is used by Ignition.
* `path` - (Optional) The mount-point of the filesystem. A non-null entry indicates that the filesystem has already been mounted by the system at the specified path. This is really only useful for _/sysroot_.
The `mount` block supports:
* `device` - (Required) The absolute path to the device. Devices are typically referenced by the _/dev/disk/by-*_ symlinks.
* `format` - (Required) The filesystem format (ext4, btrfs, or xfs).
* `force` - (Optional) Whether or not the create operation shall overwrite an existing filesystem.
* `options` - (Optional) Any additional options to be passed to the format-specific mkfs utility.
## Attributes Reference
The following attributes are exported:
* `id` - ID used to reference this resource in _ignition_config_.

View File

@ -0,0 +1,35 @@
---
layout: "ignition"
page_title: "Ignition: ignition_group"
sidebar_current: "docs-ignition-resource-group"
description: |-
Describes the desired group additions to the passwd database.
---
# ignition\_group
Describes the desired group additions to the passwd database.
## Example Usage
```
resource "ignition_group" "foo" {
name = "foo"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The groupname for the account.
* `password_hash` - (Optional) The encrypted password for the account.
* `gid` - (Optional) The group ID of the new account.
## Attributes Reference
The following attributes are exported:
* `id` - ID used to reference this resource in _ignition_config_.

View File

@ -0,0 +1,34 @@
---
layout: "ignition"
page_title: "Ignition: ignition_networkd_unit"
sidebar_current: "docs-ignition-resource-networkd-unit"
description: |-
Describes the desired state of the networkd units.
---
# ignition\_networkd\_unit
Describes the desired state of the networkd units.
## Example Usage
```
resource "ignition_networkd_unit" "example" {
name = "00-eth0.network"
content = "[Match]\nName=eth0\n\n[Network]\nAddress=10.0.1.7"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the file. This must be suffixed with a valid unit type (e.g. _00-eth0.network_).
* `content` - (Required) The contents of the networkd file.
## Attributes Reference
The following attributes are exported:
* `id` - ID used to reference this resource in _ignition_config_.

View File

@ -0,0 +1,64 @@
---
layout: "ignition"
page_title: "Ignition: ignition_raid"
sidebar_current: "docs-ignition-resource-raid"
description: |-
Describes the desired state of the systems RAID.
---
# ignition\_raid
Describes the desired state of the systems RAID.
## Example Usage
```
resource "ignition_raid" "md" {
name = "data"
level = "stripe"
devices = [
"/dev/disk/by-partlabel/raid.1.1",
"/dev/disk/by-partlabel/raid.1.2"
]
}
resource "ignition_disk" "disk1" {
device = "/dev/sdb"
wipe_table = true
partition {
label = "raid.1.1"
number = 1
size = 20480
start = 0
}
}
resource "ignition_disk" "disk2" {
device = "/dev/sdc"
wipe_table = true
partition {
label = "raid.1.2"
number = 1
size = 20480
start = 0
}
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name to use for the resulting md device.
* `level` - (Required) The redundancy level of the array (e.g. linear, raid1, raid5, etc.).
* `devices` - (Required) The list of devices (referenced by their absolute path) in the array.
* `spares` - (Optional) The number of spares (if applicable) in the array.
## Attributes Reference
The following attributes are exported:
* `id` - ID used to reference this resource in _ignition_config_

View File

@ -0,0 +1,46 @@
---
layout: "ignition"
page_title: "Ignition: ignition_systemd_unit"
sidebar_current: "docs-ignition-resource-systemd-unit"
description: |-
Describes the desired state of the systemd units.
---
# ignition\_systemd\_unit
Describes the desired state of the systemd units.
## Example Usage
```
resource "ignition_systemd_unit" "example" {
name = "example.service"
content = "[Service]\nType=oneshot\nExecStart=/usr/bin/echo Hello World\n\n[Install]\nWantedBy=multi-user.target"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) Tthe name of the unit. This must be suffixed with a valid unit type (e.g. _thing.service_).
* `enable` - (Optional) Whether or not the service shall be enabled. When true, the service is enabled. In order for this to have any effect, the unit must have an install section. (default true)
* `mask` - (Optional) Whether or not the service shall be masked. When true, the service is masked by symlinking it to _/dev/null_.
* `content` - (Required) The contents of the unit.
* `dropin` - (Optional) The list of drop-ins for the unit.
The `dropin` block supports:
* `name` - (Required) The name of the drop-in. This must be suffixed with _.conf_.
* `content` - (Optional) The contents of the drop-in.
## Attributes Reference
The following attributes are exported:
* `id` - ID used to reference this resource in _ignition_config_.

View File

@ -0,0 +1,55 @@
---
layout: "ignition"
page_title: "Ignition: ignition_user"
sidebar_current: "docs-ignition-resource-user"
description: |-
Describes the desired user additions to the passwd database.
---
# ignition\_user
Describes the desired user additions to the passwd database.
## Example Usage
```
resource "ignition_user" "foo" {
name = "foo"
home_dir = "/home/foo/"
shell = "/bin/bash"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The username for the account.
* `password_hash` - (Optional) The encrypted password for the account.
* `ssh_authorized_keys` - (Optional) A list of SSH keys to be added to the users authorized_keys.
* `uid` - (Optional) The user ID of the new account.
* `gecos` - (Optional) The GECOS field of the new account.
* `home_dir` - (Optional) The home directory of the new account.
* `no_create_home` - (Optional) Whether or not to create the users home directory.
* `primary_group` - (Optional) The name or ID of the primary group of the new account.
* `groups` - (Optional) The list of supplementary groups of the new account.
* `no_user_group` - (Optional) Whether or not to create a group with the same name as the user.
* `no_log_init` - (Optional) Whether or not to add the user to the lastlog and faillog databases.
* `shell` - (Optional) The login shell of the new account.
## Attributes Reference
The following attributes are exported:
* `id` - ID used to reference this resource in _ignition_config_.

View File

@ -262,13 +262,17 @@
<a href="/docs/providers/heroku/index.html">Heroku</a>
</li>
<li<%= sidebar_current("docs-providers-icinga2") %>>
<a href="/docs/providers/icinga2/index.html">Icinga2</a>
</li>
<li<%= sidebar_current("docs-providers-ignition") %>>
<a href="/docs/providers/ignition/index.html">Ignition</a>
</li>
<li<%= sidebar_current("docs-providers-icinga2") %>>
<a href="/docs/providers/icinga2/index.html">Icinga2</a>
</li>
<li<%= sidebar_current("docs-providers-influxdb") %>>
<a href="/docs/providers/influxdb/index.html">InfluxDB</a>
</li>
</li>
<li<%= sidebar_current("docs-providers-librato") %>>
<a href="/docs/providers/librato/index.html">Librato</a>

View File

@ -0,0 +1,33 @@
<% wrap_layout :inner do %>
<% content_for :sidebar do %>
<div class="docs-sidebar hidden-print affix-top" role="complementary">
<ul class="nav docs-sidenav">
<li<%= sidebar_current("docs-home") %>>
<a href="/docs/providers/index.html">&laquo; Documentation Home</a>
</li>
<li<%= sidebar_current("docs-ignition-index") %>>
<a href="/docs/providers/ignition/index.html">Ignition Provider</a>
</li>
<li<%= sidebar_current(/^docs-ignition-resource/) %>>
<a href="#">Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-ignition-resource-config") %>><a href="/docs/providers/ignition/r/config.html">ignition_config</a></li>
<li<%= sidebar_current("docs-ignition-resource-disk") %>><a href="/docs/providers/ignition/r/disk.html">ignition_disk</a></li>
<li<%= sidebar_current("docs-ignition-resource-raid") %>><a href="/docs/providers/ignition/r/raid.html">ignition_raid</a></li>
<li<%= sidebar_current("docs-ignition-resource-filesystem") %>><a href="/docs/providers/ignition/r/filesystem.html">ignition_filesystem</a></li>
<li<%= sidebar_current("docs-ignition-resource-file") %>><a href="/docs/providers/ignition/r/file.html">ignition_file</a></li>
<li<%= sidebar_current("docs-ignition-resource-systemd-unit") %>><a href="/docs/providers/ignition/r/systemd_unit.html">ignition_systemd_unit</a></li>
<li<%= sidebar_current("docs-ignition-resource-networkd-unit") %>><a href="/docs/providers/ignition/r/networkd_unit.html">ignition_networkd_unit</a></li>
<li<%= sidebar_current("docs-ignition-resource-user") %>><a href="/docs/providers/ignition/r/user.html">ignition_user</a></li>
<li<%= sidebar_current("docs-ignition-resource-group") %>><a href="/docs/providers/ignition/r/group.html">ignition_group</a></li>
</li>
</ul>
</li>
</ul>
</div>
<% end %>
<%= yield %>
<% end %>