Compare commits

...

39 Commits

Author SHA1 Message Date
Alex Crawford
9999178538 coreos-cloudinit: bump to 0.10.3 2014-09-17 12:19:13 -07:00
Alex Crawford
8f766e4666 Merge pull request #235 from crawford/routes
network: add support for CIDR addresses Debian routes
2014-09-17 12:18:16 -07:00
Alex Crawford
2d28d16c92 network: add support for CIDR addresses Debian routes
OnMetal is changing their template from:
`route add -net 1.2.3.0 netmask 255.255.255.0 gw 10.1.2.1 || true`
to:
`route add -net 1.2.3.0/24 gw 10.1.2.1 || true`
2014-09-16 17:36:34 -07:00
Alex Crawford
e9cd09dd7b coreos-cloudinit: bump to 0.10.2+git 2014-09-14 08:19:57 -07:00
Alex Crawford
8370b30aa2 coreos-cloudinit: bump to 0.10.2 2014-09-14 08:19:33 -07:00
Alex Crawford
3e015cc3a1 Merge pull request #233 from crawford/configdrive
configdrive: don't fail if no network config was provided
2014-09-14 08:18:14 -07:00
Alex Crawford
a0fe6d0884 configdrive: return an empty network config when filename is empty
Additionally, don't bother checking for a network config if it isn't going to
be processed.
2014-09-13 21:51:51 -07:00
Alex Crawford
585ce5fcd9 Revert "metadata: don't fail if no network config was provided"
This reverts commit c1f373e648.
2014-09-13 21:01:42 -07:00
Alex Crawford
72445796ca coreos-cloudinit: bump to 0.10.1+git 2014-09-12 16:48:15 -07:00
Alex Crawford
7342d91a85 coreos-cloudinit: bump to 0.10.1 2014-09-12 16:47:58 -07:00
Alex Crawford
db1bc51c98 Merge pull request #231 from crawford/netconf
metadata: don't fail if no network config was provided
2014-09-12 16:35:24 -07:00
Alex Crawford
c1f373e648 metadata: don't fail if no network config was provided 2014-09-12 16:29:27 -07:00
Alex Crawford
db49a16002 coreos-cloudinit: bump to 0.10.0+git 2014-09-11 17:37:05 -07:00
Alex Crawford
a4a6c281d9 coreos-cloudinit: bump to 0.10.0 2014-09-11 17:36:38 -07:00
Alex Crawford
17f8733121 Merge pull request #228 from crawford/sub
env: add support for escaping environment substitutions
2014-09-11 15:34:03 -07:00
Alex Crawford
7dec922618 env: add support for escaping environment substitutions 2014-09-11 15:30:33 -07:00
Alex Crawford
54d3ae27af Merge pull request #226 from crawford/oem
flags: add oem flag
2014-09-11 13:25:21 -07:00
Alex Crawford
ee2416af64 flags: move the flags into their own namespace 2014-09-11 12:00:17 -07:00
Alex Crawford
cda037f9a5 flags: add oem flag
The oem flag will allow each of the OEMs to specify one flag only, acting as a
shortcut to their specific configuration. This will allow us to update which
options each OEM uses when running cloudinit.
2014-09-11 12:00:17 -07:00
Alex Crawford
549806cf64 Merge pull request #227 from crawford/ipv6
metadata: add support for IPv6 variable substitution
2014-09-11 10:45:33 -07:00
Alex Crawford
56815a6756 metadata: add support for IPv6 variable substitution 2014-09-11 10:43:02 -07:00
Alex Crawford
24a6f7c49c Merge pull request #225 from crawford/exit
userdata: change handling of bad userdata
2014-09-10 19:16:12 -07:00
Alex Crawford
98484be434 userdata: change handling of bad userdata
Don't fail after encountering bad userdata. Continue processing the metadata
and then exit. This will allow people with bad userdata to actually log in and
see the error.
2014-09-10 17:50:23 -07:00
Jonathan Boulle
9024659296 Merge pull request #217 from ecnahc515/patch-1
Fix broken link to fleet config
2014-09-09 15:19:18 -07:00
Chance Zibolski
fc6940f7ba Documentation: More specific link to fleet config.
Add an anchor tag to the url to take person directly to config section.
2014-09-09 15:15:55 -07:00
Brian Waldon
f2fd95699b Merge pull request #224 from bcwaldon/typo
docs: fix a typo
2014-09-09 12:36:42 -07:00
bdevloed
65db96cc7c docs: fix a typo 2014-09-09 12:31:54 -07:00
Alex Crawford
c17b93b5c0 Merge pull request #223 from crawford/yaml
third_party: sync third_party/gopkg.in/yaml.v1
2014-09-08 19:28:59 -07:00
Alex Crawford
d352f8ce6a Merge pull request #222 from crawford/contribute
docs: Update maintainers and contribution guide
2014-09-08 15:54:30 -07:00
Alex Crawford
78aa2c56ec yaml: replace goyaml with yaml 2014-09-08 13:25:27 -07:00
Alex Crawford
c5b3788282 third_party: sync third_party/gopkg.in/yaml.v1
Update launchpad.net/goyaml to gopkg.in/yaml.v1
2014-09-08 13:23:50 -07:00
Alex Crawford
5e98970bb5 docs: Update maintainers and contribution guide 2014-09-08 12:55:17 -07:00
Alex Crawford
cbdd446c55 Merge pull request #220 from crawford/docs
docs: Update list of platforms supporting variable substitutions
2014-09-05 11:51:45 -07:00
Alex Crawford
316cadcf44 docs: Update list of platforms supporting variable substitutions 2014-09-04 12:57:19 -07:00
Alex Crawford
5a939be21b coreos-cloudinit: bump to 0.9.6+git 2014-09-02 17:49:09 -07:00
Alex Crawford
8d76c64386 coreos-cloudinit: bump to 0.9.6 2014-09-02 17:48:45 -07:00
Alex Crawford
1b854eb51e Merge pull request #218 from crawford/units
units: Ensure that the units are executed in order
2014-09-02 17:40:37 -07:00
Alex Crawford
9fcf338bf3 units: Ensure that the units are executed in order 2014-09-02 17:15:32 -07:00
Alex Crawford
fda72bdb5c coreos-cloudinit: bump to 0.9.5+git 2014-09-02 10:10:59 -07:00
36 changed files with 715 additions and 324 deletions

View File

@@ -39,22 +39,25 @@ Thanks for your contributions!
### Format of the Commit Message ### Format of the Commit Message
We follow a rough convention for commit messages borrowed from AngularJS. This We follow a rough convention for commit messages that is designed to answer two
is an example of a commit: questions: what changed and why. The subject line should feature the what and
the body of the commit should describe the why.
``` ```
feat(scripts/test-cluster): add a cluster test command environment: write new keys in consistent order
this uses tmux to setup a test cluster that you can easily kill and Go 1.3 randomizes the ordering of keys when iterating over a map.
start for debugging. Sort the keys to make this ordering consistent.
Fixes #38
``` ```
The format can be described more formally as follows: The format can be described more formally as follows:
``` ```
<type>(<scope>): <subject> <subsystem>: <what changed>
<BLANK LINE> <BLANK LINE>
<body> <why this change was made>
<BLANK LINE> <BLANK LINE>
<footer> <footer>
``` ```
@@ -63,25 +66,3 @@ The first line is the subject and should be no longer than 70 characters, the
second line is always blank, and other lines should be wrapped at 80 characters. second line is always blank, and other lines should be wrapped at 80 characters.
This allows the message to be easier to read on GitHub as well as in various This allows the message to be easier to read on GitHub as well as in various
git tools. git tools.
#### Subject Line
The subject line contains a succinct description of the change.
#### Allowed `<type>`s
- *feat* (feature)
- *fix* (bug fix)
- *docs* (documentation)
- *style* (formatting, missing semi colons, …)
- *refactor*
- *test* (when adding missing tests)
- *chore* (maintain)
#### Allowed `<scope>`s
Scopes can anything specifying the place of the commit change in the code base -
for example, "api", "store", etc.
For more details on the commit format, see the [AngularJS commit style
guide](https://docs.google.com/a/coreos.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#).

View File

@@ -4,7 +4,7 @@ CoreOS allows you to declaratively customize various OS-level items, such as net
## Configuration File ## Configuration File
The file used by this system initialization program is called a "cloud-config" file. It is inspired by the [cloud-init][cloud-init] project's [cloud-config][cloud-config] file. which is "the defacto multi-distribution package that handles early initialization of a cloud instance" ([cloud-init docs][cloud-init-docs]). Because the cloud-init project includes tools which aren't used by CoreOS, only the relevant subset of its configuration items will be implemented in our cloud-config file. In addition to those, we added a few CoreOS-specific items, such as etcd configuration, OEM definition, and systemd units. The file used by this system initialization program is called a "cloud-config" file. It is inspired by the [cloud-init][cloud-init] project's [cloud-config][cloud-config] file, which is "the defacto multi-distribution package that handles early initialization of a cloud instance" ([cloud-init docs][cloud-init-docs]). Because the cloud-init project includes tools which aren't used by CoreOS, only the relevant subset of its configuration items will be implemented in our cloud-config file. In addition to those, we added a few CoreOS-specific items, such as etcd configuration, OEM definition, and systemd units.
We've designed our implementation to allow the same cloud-config file to work across all of our supported platforms. We've designed our implementation to allow the same cloud-config file to work across all of our supported platforms.
@@ -68,7 +68,7 @@ Environment="ETCD_PEER_ADDR=192.0.2.13:7001"
For more information about the available configuration parameters, see the [etcd documentation][etcd-config]. For more information about the available configuration parameters, see the [etcd documentation][etcd-config].
Note that hyphens in the coreos.etcd.* keys are mapped to underscores. Note that hyphens in the coreos.etcd.* keys are mapped to underscores.
_Note: The `$private_ipv4` and `$public_ipv4` substitution variables referenced in other documents are only supported on Amazon EC2, Google Compute Engine, OpenStack, Rackspace, and Vagrant._ _Note: The `$private_ipv4` and `$public_ipv4` substitution variables referenced in other documents are only supported on Amazon EC2, Google Compute Engine, OpenStack, Rackspace, DigitalOcean, and Vagrant._
[etcd-config]: https://github.com/coreos/etcd/blob/master/Documentation/configuration.md [etcd-config]: https://github.com/coreos/etcd/blob/master/Documentation/configuration.md
@@ -95,7 +95,7 @@ Environment="FLEET_METADATA=region=us-west"
For more information on fleet configuration, see the [fleet documentation][fleet-config]. For more information on fleet configuration, see the [fleet documentation][fleet-config].
[fleet-config]: https://github.com/coreos/fleet/blob/master/Documentation/deployment-and-configuration.md [fleet-config]: https://github.com/coreos/fleet/blob/master/Documentation/deployment-and-configuration.md#configuration
#### update #### update

3
MAINTAINERS Normal file
View File

@@ -0,0 +1,3 @@
Alex Crawford <alex.crawford@coreos.com> (@crawford)
Jonathan Boulle <jonathan.boulle@coreos.com> (@jonboulle)
Brian Waldon <brian.waldon@coreos.com> (@bcwaldon)

View File

@@ -21,94 +21,125 @@ import (
) )
const ( const (
version = "0.9.5" version = "0.10.3"
datasourceInterval = 100 * time.Millisecond datasourceInterval = 100 * time.Millisecond
datasourceMaxInterval = 30 * time.Second datasourceMaxInterval = 30 * time.Second
datasourceTimeout = 5 * time.Minute datasourceTimeout = 5 * time.Minute
) )
var ( var (
printVersion bool flags = struct {
ignoreFailure bool printVersion bool
sources struct { ignoreFailure bool
file string sources struct {
configDrive string file string
metadataService bool configDrive string
ec2MetadataService string metadataService bool
cloudSigmaMetadataService bool ec2MetadataService string
digitalOceanMetadataService string cloudSigmaMetadataService bool
url string digitalOceanMetadataService string
procCmdLine bool url string
} procCmdLine bool
convertNetconf string }
workspace string convertNetconf string
sshKeyName string workspace string
sshKeyName string
oem string
}{}
) )
func init() { func init() {
flag.BoolVar(&printVersion, "version", false, "Print the version and exit") flag.BoolVar(&flags.printVersion, "version", false, "Print the version and exit")
flag.BoolVar(&ignoreFailure, "ignore-failure", false, "Exits with 0 status in the event of malformed input from user-data") flag.BoolVar(&flags.ignoreFailure, "ignore-failure", false, "Exits with 0 status in the event of malformed input from user-data")
flag.StringVar(&sources.file, "from-file", "", "Read user-data from provided file") flag.StringVar(&flags.sources.file, "from-file", "", "Read user-data from provided file")
flag.StringVar(&sources.configDrive, "from-configdrive", "", "Read data from provided cloud-drive directory") flag.StringVar(&flags.sources.configDrive, "from-configdrive", "", "Read data from provided cloud-drive directory")
flag.BoolVar(&sources.metadataService, "from-metadata-service", false, "[DEPRECATED - Use -from-ec2-metadata] Download data from metadata service") flag.BoolVar(&flags.sources.metadataService, "from-metadata-service", false, "[DEPRECATED - Use -from-ec2-metadata] Download data from metadata service")
flag.StringVar(&sources.ec2MetadataService, "from-ec2-metadata", "", "Download EC2 data from the provided url") flag.StringVar(&flags.sources.ec2MetadataService, "from-ec2-metadata", "", "Download EC2 data from the provided url")
flag.BoolVar(&sources.cloudSigmaMetadataService, "from-cloudsigma-metadata", false, "Download data from CloudSigma server context") flag.BoolVar(&flags.sources.cloudSigmaMetadataService, "from-cloudsigma-metadata", false, "Download data from CloudSigma server context")
flag.StringVar(&sources.digitalOceanMetadataService, "from-digitalocean-metadata", "", "Download DigitalOcean data from the provided url") flag.StringVar(&flags.sources.digitalOceanMetadataService, "from-digitalocean-metadata", "", "Download DigitalOcean data from the provided url")
flag.StringVar(&sources.url, "from-url", "", "Download user-data from provided url") flag.StringVar(&flags.sources.url, "from-url", "", "Download user-data from provided url")
flag.BoolVar(&sources.procCmdLine, "from-proc-cmdline", false, fmt.Sprintf("Parse %s for '%s=<url>', using the cloud-config served by an HTTP GET to <url>", proc_cmdline.ProcCmdlineLocation, proc_cmdline.ProcCmdlineCloudConfigFlag)) flag.BoolVar(&flags.sources.procCmdLine, "from-proc-cmdline", false, fmt.Sprintf("Parse %s for '%s=<url>', using the cloud-config served by an HTTP GET to <url>", proc_cmdline.ProcCmdlineLocation, proc_cmdline.ProcCmdlineCloudConfigFlag))
flag.StringVar(&convertNetconf, "convert-netconf", "", "Read the network config provided in cloud-drive and translate it from the specified format into networkd unit files") flag.StringVar(&flags.oem, "oem", "", "Use the settings specific to the provided OEM")
flag.StringVar(&workspace, "workspace", "/var/lib/coreos-cloudinit", "Base directory coreos-cloudinit should use to store data") flag.StringVar(&flags.convertNetconf, "convert-netconf", "", "Read the network config provided in cloud-drive and translate it from the specified format into networkd unit files")
flag.StringVar(&sshKeyName, "ssh-key-name", initialize.DefaultSSHKeyName, "Add SSH keys to the system with the given name") flag.StringVar(&flags.workspace, "workspace", "/var/lib/coreos-cloudinit", "Base directory coreos-cloudinit should use to store data")
flag.StringVar(&flags.sshKeyName, "ssh-key-name", initialize.DefaultSSHKeyName, "Add SSH keys to the system with the given name")
} }
type oemConfig map[string]string
var (
oemConfigs = map[string]oemConfig{
"digitalocean": oemConfig{
"from-digitalocean-metadata": "http://169.254.169.254/",
"convert-netconf": "digitalocean",
},
"ec2-compat": oemConfig{
"from-ec2-metadata": "http://169.254.169.254/",
"from-configdrive": "/media/configdrive",
},
"rackspace-onmetal": oemConfig{
"from-configdrive": "/media/configdrive",
"convert-netconf": "debian",
},
}
)
func main() { func main() {
failure := false
flag.Parse() flag.Parse()
die := func() { if c, ok := oemConfigs[flags.oem]; ok {
if ignoreFailure { for k, v := range c {
os.Exit(0) flag.Set(k, v)
} }
os.Exit(1) } else if flags.oem != "" {
oems := make([]string, 0, len(oemConfigs))
for k := range oemConfigs {
oems = append(oems, k)
}
fmt.Printf("Invalid option to --oem: %q. Supported options: %q\n", flags.oem, oems)
os.Exit(2)
} }
if printVersion == true { if flags.printVersion == true {
fmt.Printf("coreos-cloudinit version %s\n", version) fmt.Printf("coreos-cloudinit version %s\n", version)
os.Exit(0) os.Exit(0)
} }
switch convertNetconf { switch flags.convertNetconf {
case "": case "":
case "debian": case "debian":
case "digitalocean": case "digitalocean":
default: default:
fmt.Printf("Invalid option to -convert-netconf: '%s'. Supported options: 'debian, digitalocean'\n", convertNetconf) fmt.Printf("Invalid option to -convert-netconf: '%s'. Supported options: 'debian, digitalocean'\n", flags.convertNetconf)
os.Exit(1) os.Exit(2)
} }
dss := getDatasources() dss := getDatasources()
if len(dss) == 0 { if len(dss) == 0 {
fmt.Println("Provide at least one of --from-file, --from-configdrive, --from-ec2-metadata, --from-cloudsigma-metadata, --from-url or --from-proc-cmdline") fmt.Println("Provide at least one of --from-file, --from-configdrive, --from-ec2-metadata, --from-cloudsigma-metadata, --from-url or --from-proc-cmdline")
os.Exit(1) os.Exit(2)
} }
ds := selectDatasource(dss) ds := selectDatasource(dss)
if ds == nil { if ds == nil {
fmt.Println("No datasources available in time") fmt.Println("No datasources available in time")
die() os.Exit(1)
} }
fmt.Printf("Fetching user-data from datasource of type %q\n", ds.Type()) fmt.Printf("Fetching user-data from datasource of type %q\n", ds.Type())
userdataBytes, err := ds.FetchUserdata() userdataBytes, err := ds.FetchUserdata()
if err != nil { if err != nil {
fmt.Printf("Failed fetching user-data from datasource: %v\n", err) fmt.Printf("Failed fetching user-data from datasource: %v\nContinuing...\n", err)
die() failure = true
} }
fmt.Printf("Fetching meta-data from datasource of type %q\n", ds.Type()) fmt.Printf("Fetching meta-data from datasource of type %q\n", ds.Type())
metadataBytes, err := ds.FetchMetadata() metadataBytes, err := ds.FetchMetadata()
if err != nil { if err != nil {
fmt.Printf("Failed fetching meta-data from datasource: %v\n", err) fmt.Printf("Failed fetching meta-data from datasource: %v\n", err)
die() os.Exit(1)
} }
// Extract IPv4 addresses from metadata if possible // Extract IPv4 addresses from metadata if possible
@@ -117,34 +148,34 @@ func main() {
subs, err = initialize.ExtractIPsFromMetadata(metadataBytes) subs, err = initialize.ExtractIPsFromMetadata(metadataBytes)
if err != nil { if err != nil {
fmt.Printf("Failed extracting IPs from meta-data: %v\n", err) fmt.Printf("Failed extracting IPs from meta-data: %v\n", err)
die() os.Exit(1)
} }
} }
// Apply environment to user-data // Apply environment to user-data
env := initialize.NewEnvironment("/", ds.ConfigRoot(), workspace, convertNetconf, sshKeyName, subs) env := initialize.NewEnvironment("/", ds.ConfigRoot(), flags.workspace, flags.convertNetconf, flags.sshKeyName, subs)
userdata := env.Apply(string(userdataBytes)) userdata := env.Apply(string(userdataBytes))
var ccm, ccu *initialize.CloudConfig var ccm, ccu *initialize.CloudConfig
var script *system.Script var script *system.Script
if ccm, err = initialize.ParseMetaData(string(metadataBytes)); err != nil { if ccm, err = initialize.ParseMetaData(string(metadataBytes)); err != nil {
fmt.Printf("Failed to parse meta-data: %v\n", err) fmt.Printf("Failed to parse meta-data: %v\n", err)
die() os.Exit(1)
} }
if ccm != nil { if ccm != nil && flags.convertNetconf != "" {
fmt.Printf("Fetching network config from datasource of type %q\n", ds.Type()) fmt.Printf("Fetching network config from datasource of type %q\n", ds.Type())
netconfBytes, err := ds.FetchNetworkConfig(ccm.NetworkConfigPath) netconfBytes, err := ds.FetchNetworkConfig(ccm.NetworkConfigPath)
if err != nil { if err != nil {
fmt.Printf("Failed fetching network config from datasource: %v\n", err) fmt.Printf("Failed fetching network config from datasource: %v\n", err)
die() os.Exit(1)
} }
ccm.NetworkConfig = string(netconfBytes) ccm.NetworkConfig = string(netconfBytes)
} }
if ud, err := initialize.ParseUserData(userdata); err != nil { if ud, err := initialize.ParseUserData(userdata); err != nil {
fmt.Printf("Failed to parse user-data: %v\n", err) fmt.Printf("Failed to parse user-data: %v\nContinuing...\n", err)
die() failure = true
} else { } else {
switch t := ud.(type) { switch t := ud.(type) {
case *initialize.CloudConfig: case *initialize.CloudConfig:
@@ -172,16 +203,20 @@ func main() {
if cc != nil { if cc != nil {
if err = initialize.Apply(*cc, env); err != nil { if err = initialize.Apply(*cc, env); err != nil {
fmt.Printf("Failed to apply cloud-config: %v\n", err) fmt.Printf("Failed to apply cloud-config: %v\n", err)
die() os.Exit(1)
} }
} }
if script != nil { if script != nil {
if err = runScript(*script, env); err != nil { if err = runScript(*script, env); err != nil {
fmt.Printf("Failed to run script: %v\n", err) fmt.Printf("Failed to run script: %v\n", err)
die() os.Exit(1)
} }
} }
if failure && !flags.ignoreFailure {
os.Exit(1)
}
} }
// mergeCloudConfig merges certain options from mdcc (a CloudConfig derived from // mergeCloudConfig merges certain options from mdcc (a CloudConfig derived from
@@ -222,28 +257,28 @@ func mergeCloudConfig(mdcc, udcc initialize.CloudConfig) (cc initialize.CloudCon
// on the different source command-line flags. // on the different source command-line flags.
func getDatasources() []datasource.Datasource { func getDatasources() []datasource.Datasource {
dss := make([]datasource.Datasource, 0, 5) dss := make([]datasource.Datasource, 0, 5)
if sources.file != "" { if flags.sources.file != "" {
dss = append(dss, file.NewDatasource(sources.file)) dss = append(dss, file.NewDatasource(flags.sources.file))
} }
if sources.url != "" { if flags.sources.url != "" {
dss = append(dss, url.NewDatasource(sources.url)) dss = append(dss, url.NewDatasource(flags.sources.url))
} }
if sources.configDrive != "" { if flags.sources.configDrive != "" {
dss = append(dss, configdrive.NewDatasource(sources.configDrive)) dss = append(dss, configdrive.NewDatasource(flags.sources.configDrive))
} }
if sources.metadataService { if flags.sources.metadataService {
dss = append(dss, ec2.NewDatasource(ec2.DefaultAddress)) dss = append(dss, ec2.NewDatasource(ec2.DefaultAddress))
} }
if sources.ec2MetadataService != "" { if flags.sources.ec2MetadataService != "" {
dss = append(dss, ec2.NewDatasource(sources.ec2MetadataService)) dss = append(dss, ec2.NewDatasource(flags.sources.ec2MetadataService))
} }
if sources.cloudSigmaMetadataService { if flags.sources.cloudSigmaMetadataService {
dss = append(dss, cloudsigma.NewServerContextService()) dss = append(dss, cloudsigma.NewServerContextService())
} }
if sources.digitalOceanMetadataService != "" { if flags.sources.digitalOceanMetadataService != "" {
dss = append(dss, digitalocean.NewDatasource(sources.digitalOceanMetadataService)) dss = append(dss, digitalocean.NewDatasource(flags.sources.digitalOceanMetadataService))
} }
if sources.procCmdLine { if flags.sources.procCmdLine {
dss = append(dss, proc_cmdline.NewDatasource()) dss = append(dss, proc_cmdline.NewDatasource())
} }
return dss return dss

View File

@@ -42,6 +42,9 @@ func (cd *configDrive) FetchUserdata() ([]byte, error) {
} }
func (cd *configDrive) FetchNetworkConfig(filename string) ([]byte, error) { func (cd *configDrive) FetchNetworkConfig(filename string) ([]byte, error) {
if filename == "" {
return []byte{}, nil
}
return cd.tryReadFile(path.Join(cd.openstackRoot(), filename)) return cd.tryReadFile(path.Join(cd.openstackRoot(), filename))
} }

View File

@@ -6,7 +6,7 @@ import (
"log" "log"
"path" "path"
"github.com/coreos/coreos-cloudinit/third_party/launchpad.net/goyaml" "github.com/coreos/coreos-cloudinit/third_party/gopkg.in/yaml.v1"
"github.com/coreos/coreos-cloudinit/network" "github.com/coreos/coreos-cloudinit/network"
"github.com/coreos/coreos-cloudinit/system" "github.com/coreos/coreos-cloudinit/system"
@@ -51,12 +51,12 @@ type warner func(format string, v ...interface{})
func warnOnUnrecognizedKeys(contents string, warn warner) { func warnOnUnrecognizedKeys(contents string, warn warner) {
// Generate a map of all understood cloud config options // Generate a map of all understood cloud config options
var cc map[string]interface{} var cc map[string]interface{}
b, _ := goyaml.Marshal(&CloudConfig{}) b, _ := yaml.Marshal(&CloudConfig{})
goyaml.Unmarshal(b, &cc) yaml.Unmarshal(b, &cc)
// Now unmarshal the entire provided contents // Now unmarshal the entire provided contents
var c map[string]interface{} var c map[string]interface{}
goyaml.Unmarshal([]byte(contents), &c) yaml.Unmarshal([]byte(contents), &c)
// Check that every key in the contents exists in the cloud config // Check that every key in the contents exists in the cloud config
for k, _ := range c { for k, _ := range c {
@@ -84,8 +84,8 @@ func warnOnUnrecognizedKeys(contents string, warn warner) {
// Check for any badly-specified users, if any are set // Check for any badly-specified users, if any are set
if users, ok := c["users"]; ok { if users, ok := c["users"]; ok {
var known map[string]interface{} var known map[string]interface{}
b, _ := goyaml.Marshal(&system.User{}) b, _ := yaml.Marshal(&system.User{})
goyaml.Unmarshal(b, &known) yaml.Unmarshal(b, &known)
if set, ok := users.([]interface{}); ok { if set, ok := users.([]interface{}); ok {
for _, u := range set { for _, u := range set {
@@ -107,8 +107,8 @@ func warnOnUnrecognizedKeys(contents string, warn warner) {
// Check for any badly-specified files, if any are set // Check for any badly-specified files, if any are set
if files, ok := c["write_files"]; ok { if files, ok := c["write_files"]; ok {
var known map[string]interface{} var known map[string]interface{}
b, _ := goyaml.Marshal(&system.File{}) b, _ := yaml.Marshal(&system.File{})
goyaml.Unmarshal(b, &known) yaml.Unmarshal(b, &known)
if set, ok := files.([]interface{}); ok { if set, ok := files.([]interface{}); ok {
for _, f := range set { for _, f := range set {
@@ -133,7 +133,7 @@ func warnOnUnrecognizedKeys(contents string, warn warner) {
// fields but log encountering them. // fields but log encountering them.
func NewCloudConfig(contents string) (*CloudConfig, error) { func NewCloudConfig(contents string) (*CloudConfig, error) {
var cfg CloudConfig var cfg CloudConfig
err := goyaml.Unmarshal([]byte(contents), &cfg) err := yaml.Unmarshal([]byte(contents), &cfg)
if err != nil { if err != nil {
return &cfg, err return &cfg, err
} }
@@ -142,7 +142,7 @@ func NewCloudConfig(contents string) (*CloudConfig, error) {
} }
func (cc CloudConfig) String() string { func (cc CloudConfig) String() string {
bytes, err := goyaml.Marshal(cc) bytes, err := yaml.Marshal(cc)
if err != nil { if err != nil {
return "" return ""
} }
@@ -291,7 +291,11 @@ func Apply(cfg CloudConfig, env *Environment) error {
// disk, masking/unmasking units, or invoking systemd // disk, masking/unmasking units, or invoking systemd
// commands against units. It returns any error encountered. // commands against units. It returns any error encountered.
func processUnits(units []system.Unit, root string, um system.UnitManager) error { func processUnits(units []system.Unit, root string, um system.UnitManager) error {
commands := make(map[string]string, 0) type action struct {
unit string
command string
}
actions := make([]action, 0, len(units))
reload := false reload := false
for _, unit := range units { for _, unit := range units {
dst := unit.Destination(root) dst := unit.Destination(root)
@@ -329,9 +333,9 @@ func processUnits(units []system.Unit, root string, um system.UnitManager) error
} }
if unit.Group() == "network" { if unit.Group() == "network" {
commands["systemd-networkd.service"] = "restart" actions = append(actions, action{"systemd-networkd.service", "restart"})
} else if unit.Command != "" { } else if unit.Command != "" {
commands[unit.Name] = unit.Command actions = append(actions, action{unit.Name, unit.Command})
} }
} }
@@ -341,13 +345,13 @@ func processUnits(units []system.Unit, root string, um system.UnitManager) error
} }
} }
for unit, command := range commands { for _, action := range actions {
log.Printf("Calling unit command '%s %s'", command, unit) log.Printf("Calling unit command '%s %s'", action.command, action.unit)
res, err := um.RunUnitCommand(command, unit) res, err := um.RunUnitCommand(action.command, action.unit)
if err != nil { if err != nil {
return err return err
} }
log.Printf("Result of '%s %s': %s", command, unit, res) log.Printf("Result of '%s %s': %s", action.command, action.unit, res)
} }
return nil return nil

View File

@@ -3,6 +3,7 @@ package initialize
import ( import (
"os" "os"
"path" "path"
"regexp"
"strings" "strings"
"github.com/coreos/coreos-cloudinit/system" "github.com/coreos/coreos-cloudinit/system"
@@ -28,6 +29,8 @@ func NewEnvironment(root, configRoot, workspace, netconfType, sshKeyName string,
for k, v := range map[string]string{ for k, v := range map[string]string{
"$public_ipv4": os.Getenv("COREOS_PUBLIC_IPV4"), "$public_ipv4": os.Getenv("COREOS_PUBLIC_IPV4"),
"$private_ipv4": os.Getenv("COREOS_PRIVATE_IPV4"), "$private_ipv4": os.Getenv("COREOS_PRIVATE_IPV4"),
"$public_ipv6": os.Getenv("COREOS_PUBLIC_IPV6"),
"$private_ipv6": os.Getenv("COREOS_PRIVATE_IPV6"),
} { } {
if _, ok := substitutions[k]; !ok { if _, ok := substitutions[k]; !ok {
substitutions[k] = v substitutions[k] = v
@@ -60,9 +63,18 @@ func (e *Environment) SetSSHKeyName(name string) {
e.sshKeyName = name e.sshKeyName = name
} }
// Apply goes through the map of substitutions and replaces all instances of
// the keys with their respective values. It supports escaping substitutions
// with a leading '\'.
func (e *Environment) Apply(data string) string { func (e *Environment) Apply(data string) string {
for key, val := range e.substitutions { for key, val := range e.substitutions {
data = strings.Replace(data, key, val, -1) matchKey := strings.Replace(key, `$`, `\$`, -1)
replKey := strings.Replace(key, `$`, `$$`, -1)
// "key" -> "val"
data = regexp.MustCompile(`([^\\]|^)`+matchKey).ReplaceAllString(data, `${1}`+val)
// "\key" -> "key"
data = regexp.MustCompile(`\\`+matchKey).ReplaceAllString(data, replKey)
} }
return data return data
} }
@@ -80,6 +92,12 @@ func (e *Environment) DefaultEnvironmentFile() *system.EnvFile {
if ip, ok := e.substitutions["$private_ipv4"]; ok && len(ip) > 0 { if ip, ok := e.substitutions["$private_ipv4"]; ok && len(ip) > 0 {
ef.Vars["COREOS_PRIVATE_IPV4"] = ip ef.Vars["COREOS_PRIVATE_IPV4"] = ip
} }
if ip, ok := e.substitutions["$public_ipv6"]; ok && len(ip) > 0 {
ef.Vars["COREOS_PUBLIC_IPV6"] = ip
}
if ip, ok := e.substitutions["$private_ipv6"]; ok && len(ip) > 0 {
ef.Vars["COREOS_PRIVATE_IPV6"] = ip
}
if len(ef.Vars) == 0 { if len(ef.Vars) == 0 {
return nil return nil
} else { } else {

View File

@@ -12,6 +12,8 @@ import (
func TestEnvironmentApply(t *testing.T) { func TestEnvironmentApply(t *testing.T) {
os.Setenv("COREOS_PUBLIC_IPV4", "1.2.3.4") os.Setenv("COREOS_PUBLIC_IPV4", "1.2.3.4")
os.Setenv("COREOS_PRIVATE_IPV4", "5.6.7.8") os.Setenv("COREOS_PRIVATE_IPV4", "5.6.7.8")
os.Setenv("COREOS_PUBLIC_IPV6", "1234::")
os.Setenv("COREOS_PRIVATE_IPV6", "5678::")
for _, tt := range []struct { for _, tt := range []struct {
subs map[string]string subs map[string]string
input string input string
@@ -23,14 +25,16 @@ func TestEnvironmentApply(t *testing.T) {
map[string]string{ map[string]string{
"$public_ipv4": "192.0.2.3", "$public_ipv4": "192.0.2.3",
"$private_ipv4": "192.0.2.203", "$private_ipv4": "192.0.2.203",
"$public_ipv6": "fe00:1234::",
"$private_ipv6": "fe00:5678::",
}, },
`[Service] `[Service]
ExecStart=/usr/bin/echo "$public_ipv4" ExecStart=/usr/bin/echo "$public_ipv4 $public_ipv6"
ExecStop=/usr/bin/echo $private_ipv4 ExecStop=/usr/bin/echo $private_ipv4 $private_ipv6
ExecStop=/usr/bin/echo $unknown`, ExecStop=/usr/bin/echo $unknown`,
`[Service] `[Service]
ExecStart=/usr/bin/echo "192.0.2.3" ExecStart=/usr/bin/echo "192.0.2.3 fe00:1234::"
ExecStop=/usr/bin/echo 192.0.2.203 ExecStop=/usr/bin/echo 192.0.2.203 fe00:5678::
ExecStop=/usr/bin/echo $unknown`, ExecStop=/usr/bin/echo $unknown`,
}, },
{ {
@@ -51,6 +55,24 @@ ExecStop=/usr/bin/echo $unknown`,
"$private_ipv4\nfoobar", "$private_ipv4\nfoobar",
"5.6.7.8\nfoobar", "5.6.7.8\nfoobar",
}, },
{
// Escaping substitutions
map[string]string{"$private_ipv4": "127.0.0.1"},
`\$private_ipv4
$private_ipv4
addr: \$private_ipv4
\\$private_ipv4`,
`$private_ipv4
127.0.0.1
addr: $private_ipv4
\$private_ipv4`,
},
{
// No substitutions with escaping
nil,
"\\$test\n$test",
"\\$test\n$test",
},
} { } {
env := NewEnvironment("./", "./", "./", "", "", tt.subs) env := NewEnvironment("./", "./", "./", "", "", tt.subs)
@@ -65,8 +87,10 @@ func TestEnvironmentFile(t *testing.T) {
subs := map[string]string{ subs := map[string]string{
"$public_ipv4": "1.2.3.4", "$public_ipv4": "1.2.3.4",
"$private_ipv4": "5.6.7.8", "$private_ipv4": "5.6.7.8",
"$public_ipv6": "1234::",
"$private_ipv6": "5678::",
} }
expect := "COREOS_PRIVATE_IPV4=5.6.7.8\nCOREOS_PUBLIC_IPV4=1.2.3.4\n" expect := "COREOS_PRIVATE_IPV4=5.6.7.8\nCOREOS_PRIVATE_IPV6=5678::\nCOREOS_PUBLIC_IPV4=1.2.3.4\nCOREOS_PUBLIC_IPV6=1234::\n"
dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-") dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-")
if err != nil { if err != nil {
@@ -96,6 +120,8 @@ func TestEnvironmentFileNil(t *testing.T) {
subs := map[string]string{ subs := map[string]string{
"$public_ipv4": "", "$public_ipv4": "",
"$private_ipv4": "", "$private_ipv4": "",
"$public_ipv6": "",
"$private_ipv6": "",
} }
env := NewEnvironment("./", "./", "./", "", "", subs) env := NewEnvironment("./", "./", "./", "", "", subs)

View File

@@ -5,8 +5,8 @@ import (
"sort" "sort"
) )
// ParseMetaData parses a JSON blob in the OpenStack metadata service format, and // ParseMetaData parses a JSON blob in the OpenStack metadata service format,
// converts it to a partially hydrated CloudConfig // and converts it to a partially hydrated CloudConfig.
func ParseMetaData(contents string) (*CloudConfig, error) { func ParseMetaData(contents string) (*CloudConfig, error) {
if len(contents) == 0 { if len(contents) == 0 {
return nil, nil return nil, nil
@@ -34,22 +34,31 @@ func ParseMetaData(contents string) (*CloudConfig, error) {
return &cfg, nil return &cfg, nil
} }
// ExtractIPsFromMetaData parses a JSON blob in the OpenStack metadata service format, // ExtractIPsFromMetaData parses a JSON blob in the OpenStack metadata service
// and returns a substitution map possibly containing private_ipv4 and public_ipv4 addresses // format and returns a substitution map possibly containing private_ipv4,
// public_ipv4, private_ipv6, and public_ipv6 addresses.
func ExtractIPsFromMetadata(contents []byte) (map[string]string, error) { func ExtractIPsFromMetadata(contents []byte) (map[string]string, error) {
var ips struct { var ips struct {
Public string `json:"public-ipv4"` PublicIPv4 string `json:"public-ipv4"`
Private string `json:"local-ipv4"` PrivateIPv4 string `json:"local-ipv4"`
PublicIPv6 string `json:"public-ipv6"`
PrivateIPv6 string `json:"local-ipv6"`
} }
if err := json.Unmarshal(contents, &ips); err != nil { if err := json.Unmarshal(contents, &ips); err != nil {
return nil, err return nil, err
} }
m := make(map[string]string) m := make(map[string]string)
if ips.Private != "" { if ips.PrivateIPv4 != "" {
m["$private_ipv4"] = ips.Private m["$private_ipv4"] = ips.PrivateIPv4
} }
if ips.Public != "" { if ips.PublicIPv4 != "" {
m["$public_ipv4"] = ips.Public m["$public_ipv4"] = ips.PublicIPv4
}
if ips.PrivateIPv6 != "" {
m["$private_ipv6"] = ips.PrivateIPv6
}
if ips.PublicIPv6 != "" {
m["$public_ipv6"] = ips.PublicIPv6
} }
return m, nil return m, nil
} }

View File

@@ -43,9 +43,9 @@ func TestExtractIPsFromMetadata(t *testing.T) {
out map[string]string out map[string]string
}{ }{
{ {
[]byte(`{"public-ipv4": "12.34.56.78", "local-ipv4": "1.2.3.4"}`), []byte(`{"public-ipv4": "12.34.56.78", "local-ipv4": "1.2.3.4", "public-ipv6": "1234::", "local-ipv6": "5678::"}`),
false, false,
map[string]string{"$public_ipv4": "12.34.56.78", "$private_ipv4": "1.2.3.4"}, map[string]string{"$public_ipv4": "12.34.56.78", "$private_ipv4": "1.2.3.4", "$public_ipv6": "1234::", "$private_ipv6": "5678::"},
}, },
{ {
[]byte(`{"local-ipv4": "127.0.0.1", "something_else": "don't care"}`), []byte(`{"local-ipv4": "127.0.0.1", "something_else": "don't care"}`),

View File

@@ -16,7 +16,7 @@ func ParseUserData(contents string) (interface{}, error) {
// Explicitly trim the header so we can handle user-data from // Explicitly trim the header so we can handle user-data from
// non-unix operating systems. The rest of the file is parsed // non-unix operating systems. The rest of the file is parsed
// by goyaml, which correctly handles CRLF. // by yaml, which correctly handles CRLF.
header = strings.TrimSpace(header) header = strings.TrimSpace(header)
if strings.HasPrefix(header, "#!") { if strings.HasPrefix(header, "#!") {

View File

@@ -236,7 +236,11 @@ func parseInterfaceStanza(attributes []string, options []string) (*stanzaInterfa
for i, field := range fields[:len(fields)-1] { for i, field := range fields[:len(fields)-1] {
switch field { switch field {
case "-net": case "-net":
route.destination.IP = net.ParseIP(fields[i+1]) if _, dst, err := net.ParseCIDR(fields[i+1]); err == nil {
route.destination = *dst
} else {
route.destination.IP = net.ParseIP(fields[i+1])
}
case "netmask": case "netmask":
route.destination.Mask = net.IPMask(net.ParseIP(fields[i+1]).To4()) route.destination.Mask = net.IPMask(net.ParseIP(fields[i+1]).To4())
case "gw": case "gw":

View File

@@ -285,31 +285,57 @@ func TestBadParseInterfaceStanzasStaticPostUp(t *testing.T) {
} }
func TestParseInterfaceStanzaStaticPostUp(t *testing.T) { func TestParseInterfaceStanzaStaticPostUp(t *testing.T) {
options := []string{ for _, tt := range []struct {
"address 192.168.1.100", options []string
"netmask 255.255.255.0", expect []route
"post-up route add gw 192.168.1.1 -net 192.168.1.0 netmask 255.255.255.0", }{
}
expect := []route{
{ {
destination: net.IPNet{ options: []string{
IP: net.IPv4(192, 168, 1, 0), "address 192.168.1.100",
Mask: net.IPv4Mask(255, 255, 255, 0), "netmask 255.255.255.0",
"post-up route add gw 192.168.1.1 -net 192.168.1.0 netmask 255.255.255.0",
},
expect: []route{
{
destination: net.IPNet{
IP: net.IPv4(192, 168, 1, 0),
Mask: net.IPv4Mask(255, 255, 255, 0),
},
gateway: net.IPv4(192, 168, 1, 1),
},
}, },
gateway: net.IPv4(192, 168, 1, 1),
}, },
} {
options: []string{
iface, err := parseInterfaceStanza([]string{"eth", "inet", "static"}, options) "address 192.168.1.100",
if err != nil { "netmask 255.255.255.0",
t.FailNow() "post-up route add gw 192.168.1.1 -net 192.168.1.0/24 || true",
} },
static, ok := iface.configMethod.(configMethodStatic) expect: []route{
if !ok { {
t.FailNow() destination: func() net.IPNet {
} if _, net, err := net.ParseCIDR("192.168.1.0/24"); err == nil {
if !reflect.DeepEqual(static.routes, expect) { return *net
t.FailNow() } else {
panic(err)
}
}(),
gateway: net.IPv4(192, 168, 1, 1),
},
},
},
} {
iface, err := parseInterfaceStanza([]string{"eth", "inet", "static"}, tt.options)
if err != nil {
t.Fatalf("bad error (%+v): want nil, got %s\n", tt, err)
}
static, ok := iface.configMethod.(configMethodStatic)
if !ok {
t.Fatalf("bad config method (%+v): want configMethodStatic, got %T\n", tt, iface.configMethod)
}
if !reflect.DeepEqual(static.routes, tt.expect) {
t.Fatalf("bad routes (%+v): want %#v, got %#v\n", tt, tt.expect, static.routes)
}
} }
} }

View File

@@ -1,3 +1,15 @@
The following files were ported to Go from C files of libyaml, and thus
are still covered by their original copyright and license:
apic.go
emitterc.go
parserc.go
readerc.go
scannerc.go
writerc.go
yamlh.go
yamlprivateh.go
Copyright (c) 2006 Kirill Simonov Copyright (c) 2006 Kirill Simonov
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of

128
third_party/gopkg.in/yaml.v1/README.md vendored Normal file
View File

@@ -0,0 +1,128 @@
# YAML support for the Go language
Introduction
------------
The yaml package enables Go programs to comfortably encode and decode YAML
values. It was developed within [Canonical](https://www.canonical.com) as
part of the [juju](https://juju.ubuntu.com) project, and is based on a
pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML)
C library to parse and generate YAML data quickly and reliably.
Compatibility
-------------
The yaml package is almost compatible with YAML 1.1, including support for
anchors, tags, etc. There are still a few missing bits, such as document
merging, base-60 floats (huh?), and multi-document unmarshalling. These
features are not hard to add, and will be introduced as necessary.
Installation and usage
----------------------
The import path for the package is *gopkg.in/yaml.v1*.
To install it, run:
go get gopkg.in/yaml.v1
API documentation
-----------------
If opened in a browser, the import path itself leads to the API documentation:
* [https://gopkg.in/yaml.v1](https://gopkg.in/yaml.v1)
API stability
-------------
The package API for yaml v1 will remain stable as described in [gopkg.in](https://gopkg.in).
License
-------
The yaml package is licensed under the LGPL with an exception that allows it to be linked statically. Please see the LICENSE file for details.
Example
-------
```Go
package main
import (
"fmt"
"log"
"gopkg.in/yaml.v1"
)
var data = `
a: Easy!
b:
c: 2
d: [3, 4]
`
type T struct {
A string
B struct{C int; D []int ",flow"}
}
func main() {
t := T{}
err := yaml.Unmarshal([]byte(data), &t)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- t:\n%v\n\n", t)
d, err := yaml.Marshal(&t)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- t dump:\n%s\n\n", string(d))
m := make(map[interface{}]interface{})
err = yaml.Unmarshal([]byte(data), &m)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- m:\n%v\n\n", m)
d, err = yaml.Marshal(&m)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- m dump:\n%s\n\n", string(d))
}
```
This example will generate the following output:
```
--- t:
{Easy! {2 [3 4]}}
--- t dump:
a: Easy!
b:
c: 2
d: [3, 4]
--- m:
map[a:Easy! b:map[c:2 d:[3 4]]]
--- m dump:
a: Easy!
b:
c: 2
d:
- 3
- 4
```

View File

@@ -1,4 +1,4 @@
package goyaml package yaml
import ( import (
"io" "io"

View File

@@ -1,8 +1,9 @@
package goyaml package yaml
import ( import (
"reflect" "reflect"
"strconv" "strconv"
"time"
) )
const ( const (
@@ -211,6 +212,16 @@ func newDecoder() *decoder {
// returned to call SetYAML() with the value of *out once it's defined. // returned to call SetYAML() with the value of *out once it's defined.
// //
func (d *decoder) setter(tag string, out *reflect.Value, good *bool) (set func()) { func (d *decoder) setter(tag string, out *reflect.Value, good *bool) (set func()) {
if (*out).Kind() != reflect.Ptr && (*out).CanAddr() {
setter, _ := (*out).Addr().Interface().(Setter)
if setter != nil {
var arg interface{}
*out = reflect.ValueOf(&arg).Elem()
return func() {
*good = setter.SetYAML(tag, arg)
}
}
}
again := true again := true
for again { for again {
again = false again = false
@@ -279,16 +290,19 @@ func (d *decoder) alias(n *node, out reflect.Value) (good bool) {
return good return good
} }
var durationType = reflect.TypeOf(time.Duration(0))
func (d *decoder) scalar(n *node, out reflect.Value) (good bool) { func (d *decoder) scalar(n *node, out reflect.Value) (good bool) {
var tag string var tag string
var resolved interface{} var resolved interface{}
if n.tag == "" && !n.implicit { if n.tag == "" && !n.implicit {
tag = "!!str"
resolved = n.value resolved = n.value
} else { } else {
tag, resolved = resolve(n.tag, n.value) tag, resolved = resolve(n.tag, n.value)
if set := d.setter(tag, &out, &good); set != nil { }
defer set() if set := d.setter(tag, &out, &good); set != nil {
} defer set()
} }
switch out.Kind() { switch out.Kind() {
case reflect.String: case reflect.String:
@@ -320,6 +334,14 @@ func (d *decoder) scalar(n *node, out reflect.Value) (good bool) {
out.SetInt(int64(resolved)) out.SetInt(int64(resolved))
good = true good = true
} }
case string:
if out.Type() == durationType {
d, err := time.ParseDuration(resolved)
if err == nil {
out.SetInt(int64(d))
good = true
}
}
} }
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
switch resolved := resolved.(type) { switch resolved := resolved.(type) {
@@ -437,6 +459,10 @@ func (d *decoder) mapping(n *node, out reflect.Value) (good bool) {
} }
l := len(n.children) l := len(n.children)
for i := 0; i < l; i += 2 { for i := 0; i < l; i += 2 {
if isMerge(n.children[i]) {
d.merge(n.children[i+1], out)
continue
}
k := reflect.New(kt).Elem() k := reflect.New(kt).Elem()
if d.unmarshal(n.children[i], k) { if d.unmarshal(n.children[i], k) {
e := reflect.New(et).Elem() e := reflect.New(et).Elem()
@@ -456,7 +482,12 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
name := settableValueOf("") name := settableValueOf("")
l := len(n.children) l := len(n.children)
for i := 0; i < l; i += 2 { for i := 0; i < l; i += 2 {
if !d.unmarshal(n.children[i], name) { ni := n.children[i]
if isMerge(ni) {
d.merge(n.children[i+1], out)
continue
}
if !d.unmarshal(ni, name) {
continue continue
} }
if info, ok := sinfo.FieldsMap[name.String()]; ok { if info, ok := sinfo.FieldsMap[name.String()]; ok {
@@ -471,3 +502,37 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
} }
return true return true
} }
func (d *decoder) merge(n *node, out reflect.Value) {
const wantMap = "map merge requires map or sequence of maps as the value"
switch n.kind {
case mappingNode:
d.unmarshal(n, out)
case aliasNode:
an, ok := d.doc.anchors[n.value]
if ok && an.kind != mappingNode {
panic(wantMap)
}
d.unmarshal(n, out)
case sequenceNode:
// Step backwards as earlier nodes take precedence.
for i := len(n.children)-1; i >= 0; i-- {
ni := n.children[i]
if ni.kind == aliasNode {
an, ok := d.doc.anchors[ni.value]
if ok && an.kind != mappingNode {
panic(wantMap)
}
} else if ni.kind != mappingNode {
panic(wantMap)
}
d.unmarshal(ni, out)
}
default:
panic(wantMap)
}
}
func isMerge(n *node) bool {
return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == "!!merge" || n.tag == "tag:yaml.org,2002:merge")
}

View File

@@ -1,10 +1,11 @@
package goyaml_test package yaml_test
import ( import (
. "launchpad.net/gocheck" . "gopkg.in/check.v1"
"github.com/coreos/coreos-cloudinit/third_party/launchpad.net/goyaml" "gopkg.in/yaml.v1"
"math" "math"
"reflect" "reflect"
"time"
) )
var unmarshalIntTest = 123 var unmarshalIntTest = 123
@@ -350,6 +351,32 @@ var unmarshalTests = []struct {
C inlineB `yaml:",inline"` C inlineB `yaml:",inline"`
}{1, inlineB{2, inlineC{3}}}, }{1, inlineB{2, inlineC{3}}},
}, },
// bug 1243827
{
"a: -b_c",
map[string]interface{}{"a": "-b_c"},
},
{
"a: +b_c",
map[string]interface{}{"a": "+b_c"},
},
{
"a: 50cent_of_dollar",
map[string]interface{}{"a": "50cent_of_dollar"},
},
// Duration
{
"a: 3s",
map[string]time.Duration{"a": 3 * time.Second},
},
// Issue #24.
{
"a: <foo>",
map[string]string{"a": "<foo>"},
},
} }
type inlineB struct { type inlineB struct {
@@ -377,7 +404,7 @@ func (s *S) TestUnmarshal(c *C) {
pv := reflect.New(pt.Elem()) pv := reflect.New(pt.Elem())
value = pv.Interface() value = pv.Interface()
} }
err := goyaml.Unmarshal([]byte(item.data), value) err := yaml.Unmarshal([]byte(item.data), value)
c.Assert(err, IsNil, Commentf("Item #%d", i)) c.Assert(err, IsNil, Commentf("Item #%d", i))
if t.Kind() == reflect.String { if t.Kind() == reflect.String {
c.Assert(*value.(*string), Equals, item.value, Commentf("Item #%d", i)) c.Assert(*value.(*string), Equals, item.value, Commentf("Item #%d", i))
@@ -389,7 +416,7 @@ func (s *S) TestUnmarshal(c *C) {
func (s *S) TestUnmarshalNaN(c *C) { func (s *S) TestUnmarshalNaN(c *C) {
value := map[string]interface{}{} value := map[string]interface{}{}
err := goyaml.Unmarshal([]byte("notanum: .NaN"), &value) err := yaml.Unmarshal([]byte("notanum: .NaN"), &value)
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(math.IsNaN(value["notanum"].(float64)), Equals, true) c.Assert(math.IsNaN(value["notanum"].(float64)), Equals, true)
} }
@@ -408,7 +435,7 @@ var unmarshalErrorTests = []struct {
func (s *S) TestUnmarshalErrors(c *C) { func (s *S) TestUnmarshalErrors(c *C) {
for _, item := range unmarshalErrorTests { for _, item := range unmarshalErrorTests {
var value interface{} var value interface{}
err := goyaml.Unmarshal([]byte(item.data), &value) err := yaml.Unmarshal([]byte(item.data), &value)
c.Assert(err, ErrorMatches, item.error, Commentf("Partial unmarshal: %#v", value)) c.Assert(err, ErrorMatches, item.error, Commentf("Partial unmarshal: %#v", value))
} }
} }
@@ -421,6 +448,8 @@ var setterTests = []struct {
{"_: [1,A]", "!!seq", []interface{}{1, "A"}}, {"_: [1,A]", "!!seq", []interface{}{1, "A"}},
{"_: 10", "!!int", 10}, {"_: 10", "!!int", 10},
{"_: null", "!!null", nil}, {"_: null", "!!null", nil},
{`_: BAR!`, "!!str", "BAR!"},
{`_: "BAR!"`, "!!str", "BAR!"},
{"_: !!foo 'BAR!'", "!!foo", "BAR!"}, {"_: !!foo 'BAR!'", "!!foo", "BAR!"},
} }
@@ -442,17 +471,31 @@ func (o *typeWithSetter) SetYAML(tag string, value interface{}) (ok bool) {
return true return true
} }
type typeWithSetterField struct { type setterPointerType struct {
Field *typeWithSetter "_" Field *typeWithSetter "_"
} }
func (s *S) TestUnmarshalWithSetter(c *C) { type setterValueType struct {
Field typeWithSetter "_"
}
func (s *S) TestUnmarshalWithPointerSetter(c *C) {
for _, item := range setterTests { for _, item := range setterTests {
obj := &typeWithSetterField{} obj := &setterPointerType{}
err := goyaml.Unmarshal([]byte(item.data), obj) err := yaml.Unmarshal([]byte(item.data), obj)
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(obj.Field, NotNil, c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value))
Commentf("Pointer not initialized (%#v)", item.value)) c.Assert(obj.Field.tag, Equals, item.tag)
c.Assert(obj.Field.value, DeepEquals, item.value)
}
}
func (s *S) TestUnmarshalWithValueSetter(c *C) {
for _, item := range setterTests {
obj := &setterValueType{}
err := yaml.Unmarshal([]byte(item.data), obj)
c.Assert(err, IsNil)
c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value))
c.Assert(obj.Field.tag, Equals, item.tag) c.Assert(obj.Field.tag, Equals, item.tag)
c.Assert(obj.Field.value, DeepEquals, item.value) c.Assert(obj.Field.value, DeepEquals, item.value)
} }
@@ -460,7 +503,7 @@ func (s *S) TestUnmarshalWithSetter(c *C) {
func (s *S) TestUnmarshalWholeDocumentWithSetter(c *C) { func (s *S) TestUnmarshalWholeDocumentWithSetter(c *C) {
obj := &typeWithSetter{} obj := &typeWithSetter{}
err := goyaml.Unmarshal([]byte(setterTests[0].data), obj) err := yaml.Unmarshal([]byte(setterTests[0].data), obj)
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(obj.tag, Equals, setterTests[0].tag) c.Assert(obj.tag, Equals, setterTests[0].tag)
value, ok := obj.value.(map[interface{}]interface{}) value, ok := obj.value.(map[interface{}]interface{})
@@ -477,8 +520,8 @@ func (s *S) TestUnmarshalWithFalseSetterIgnoresValue(c *C) {
}() }()
m := map[string]*typeWithSetter{} m := map[string]*typeWithSetter{}
data := "{abc: 1, def: 2, ghi: 3, jkl: 4}" data := `{abc: 1, def: 2, ghi: 3, jkl: 4}`
err := goyaml.Unmarshal([]byte(data), m) err := yaml.Unmarshal([]byte(data), m)
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(m["abc"], NotNil) c.Assert(m["abc"], NotNil)
c.Assert(m["def"], IsNil) c.Assert(m["def"], IsNil)
@@ -489,6 +532,98 @@ func (s *S) TestUnmarshalWithFalseSetterIgnoresValue(c *C) {
c.Assert(m["ghi"].value, Equals, 3) c.Assert(m["ghi"].value, Equals, 3)
} }
// From http://yaml.org/type/merge.html
var mergeTests = `
anchors:
- &CENTER { "x": 1, "y": 2 }
- &LEFT { "x": 0, "y": 2 }
- &BIG { "r": 10 }
- &SMALL { "r": 1 }
# All the following maps are equal:
plain:
# Explicit keys
"x": 1
"y": 2
"r": 10
label: center/big
mergeOne:
# Merge one map
<< : *CENTER
"r": 10
label: center/big
mergeMultiple:
# Merge multiple maps
<< : [ *CENTER, *BIG ]
label: center/big
override:
# Override
<< : [ *BIG, *LEFT, *SMALL ]
"x": 1
label: center/big
shortTag:
# Explicit short merge tag
!!merge "<<" : [ *CENTER, *BIG ]
label: center/big
longTag:
# Explicit merge long tag
!<tag:yaml.org,2002:merge> "<<" : [ *CENTER, *BIG ]
label: center/big
inlineMap:
# Inlined map
<< : {"x": 1, "y": 2, "r": 10}
label: center/big
inlineSequenceMap:
# Inlined map in sequence
<< : [ *CENTER, {"r": 10} ]
label: center/big
`
func (s *S) TestMerge(c *C) {
var want = map[interface{}]interface{}{
"x": 1,
"y": 2,
"r": 10,
"label": "center/big",
}
var m map[string]interface{}
err := yaml.Unmarshal([]byte(mergeTests), &m)
c.Assert(err, IsNil)
for name, test := range m {
if name == "anchors" {
continue
}
c.Assert(test, DeepEquals, want, Commentf("test %q failed", name))
}
}
func (s *S) TestMergeStruct(c *C) {
type Data struct {
X, Y, R int
Label string
}
want := Data{1, 2, 10, "center/big"}
var m map[string]Data
err := yaml.Unmarshal([]byte(mergeTests), &m)
c.Assert(err, IsNil)
for name, test := range m {
if name == "anchors" {
continue
}
c.Assert(test, Equals, want, Commentf("test %q failed", name))
}
}
//var data []byte //var data []byte
//func init() { //func init() {
// var err error // var err error
@@ -502,7 +637,7 @@ func (s *S) TestUnmarshalWithFalseSetterIgnoresValue(c *C) {
// var err error // var err error
// for i := 0; i < c.N; i++ { // for i := 0; i < c.N; i++ {
// var v map[string]interface{} // var v map[string]interface{}
// err = goyaml.Unmarshal(data, &v) // err = yaml.Unmarshal(data, &v)
// } // }
// if err != nil { // if err != nil {
// panic(err) // panic(err)
@@ -511,9 +646,9 @@ func (s *S) TestUnmarshalWithFalseSetterIgnoresValue(c *C) {
// //
//func (s *S) BenchmarkMarshal(c *C) { //func (s *S) BenchmarkMarshal(c *C) {
// var v map[string]interface{} // var v map[string]interface{}
// goyaml.Unmarshal(data, &v) // yaml.Unmarshal(data, &v)
// c.ResetTimer() // c.ResetTimer()
// for i := 0; i < c.N; i++ { // for i := 0; i < c.N; i++ {
// goyaml.Marshal(&v) // yaml.Marshal(&v)
// } // }
//} //}

View File

@@ -1,4 +1,4 @@
package goyaml package yaml
import ( import (
"bytes" "bytes"

View File

@@ -1,9 +1,10 @@
package goyaml package yaml
import ( import (
"reflect" "reflect"
"sort" "sort"
"strconv" "strconv"
"time"
) )
type encoder struct { type encoder struct {
@@ -85,7 +86,11 @@ func (e *encoder) marshal(tag string, in reflect.Value) {
case reflect.String: case reflect.String:
e.stringv(tag, in) e.stringv(tag, in)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
e.intv(tag, in) if in.Type() == durationType {
e.stringv(tag, reflect.ValueOf(in.Interface().(time.Duration).String()))
} else {
e.intv(tag, in)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
e.uintv(tag, in) e.uintv(tag, in)
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:

View File

@@ -1,12 +1,13 @@
package goyaml_test package yaml_test
import ( import (
"fmt" "fmt"
. "launchpad.net/gocheck" "gopkg.in/yaml.v1"
"github.com/coreos/coreos-cloudinit/third_party/launchpad.net/goyaml" . "gopkg.in/check.v1"
"math" "math"
"strconv" "strconv"
"strings" "strings"
"time"
) )
var marshalIntTest = 123 var marshalIntTest = 123
@@ -212,11 +213,23 @@ var marshalTests = []struct {
}{1, inlineB{2, inlineC{3}}}, }{1, inlineB{2, inlineC{3}}},
"a: 1\nb: 2\nc: 3\n", "a: 1\nb: 2\nc: 3\n",
}, },
// Duration
{
map[string]time.Duration{"a": 3 * time.Second},
"a: 3s\n",
},
// Issue #24.
{
map[string]string{"a": "<foo>"},
"a: <foo>\n",
},
} }
func (s *S) TestMarshal(c *C) { func (s *S) TestMarshal(c *C) {
for _, item := range marshalTests { for _, item := range marshalTests {
data, err := goyaml.Marshal(item.value) data, err := yaml.Marshal(item.value)
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(string(data), Equals, item.data) c.Assert(string(data), Equals, item.data)
} }
@@ -237,7 +250,7 @@ var marshalErrorTests = []struct {
func (s *S) TestMarshalErrors(c *C) { func (s *S) TestMarshalErrors(c *C) {
for _, item := range marshalErrorTests { for _, item := range marshalErrorTests {
_, err := goyaml.Marshal(item.value) _, err := yaml.Marshal(item.value)
c.Assert(err, ErrorMatches, item.error) c.Assert(err, ErrorMatches, item.error)
} }
} }
@@ -269,12 +282,12 @@ func (s *S) TestMarshalTypeCache(c *C) {
var err error var err error
func() { func() {
type T struct{ A int } type T struct{ A int }
data, err = goyaml.Marshal(&T{}) data, err = yaml.Marshal(&T{})
c.Assert(err, IsNil) c.Assert(err, IsNil)
}() }()
func() { func() {
type T struct{ B int } type T struct{ B int }
data, err = goyaml.Marshal(&T{}) data, err = yaml.Marshal(&T{})
c.Assert(err, IsNil) c.Assert(err, IsNil)
}() }()
c.Assert(string(data), Equals, "b: 0\n") c.Assert(string(data), Equals, "b: 0\n")
@@ -298,7 +311,7 @@ func (s *S) TestMashalWithGetter(c *C) {
obj := &typeWithGetterField{} obj := &typeWithGetterField{}
obj.Field.tag = item.tag obj.Field.tag = item.tag
obj.Field.value = item.value obj.Field.value = item.value
data, err := goyaml.Marshal(obj) data, err := yaml.Marshal(obj)
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(string(data), Equals, string(item.data)) c.Assert(string(data), Equals, string(item.data))
} }
@@ -308,7 +321,7 @@ func (s *S) TestUnmarshalWholeDocumentWithGetter(c *C) {
obj := &typeWithGetter{} obj := &typeWithGetter{}
obj.tag = "" obj.tag = ""
obj.value = map[string]string{"hello": "world!"} obj.value = map[string]string{"hello": "world!"}
data, err := goyaml.Marshal(obj) data, err := yaml.Marshal(obj)
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(string(data), Equals, "hello: world!\n") c.Assert(string(data), Equals, "hello: world!\n")
} }
@@ -356,7 +369,7 @@ func (s *S) TestSortedOutput(c *C) {
for _, k := range order { for _, k := range order {
m[k] = 1 m[k] = 1
} }
data, err := goyaml.Marshal(m) data, err := yaml.Marshal(m)
c.Assert(err, IsNil) c.Assert(err, IsNil)
out := "\n" + string(data) out := "\n" + string(data)
last := 0 last := 0

View File

@@ -1,4 +1,4 @@
package goyaml package yaml
import ( import (
"bytes" "bytes"

View File

@@ -1,4 +1,4 @@
package goyaml package yaml
import ( import (
"io" "io"

View File

@@ -1,4 +1,4 @@
package goyaml package yaml
import ( import (
"math" "math"
@@ -27,7 +27,6 @@ func init() {
t[int(c)] = 'M' // In map t[int(c)] = 'M' // In map
} }
t[int('.')] = '.' // Float (potentially in map) t[int('.')] = '.' // Float (potentially in map)
t[int('<')] = '<' // Merge
var resolveMapList = []struct { var resolveMapList = []struct {
v interface{} v interface{}
@@ -45,6 +44,7 @@ func init() {
{math.Inf(+1), "!!float", []string{".inf", ".Inf", ".INF"}}, {math.Inf(+1), "!!float", []string{".inf", ".Inf", ".INF"}},
{math.Inf(+1), "!!float", []string{"+.inf", "+.Inf", "+.INF"}}, {math.Inf(+1), "!!float", []string{"+.inf", "+.Inf", "+.INF"}},
{math.Inf(-1), "!!float", []string{"-.inf", "-.Inf", "-.INF"}}, {math.Inf(-1), "!!float", []string{"-.inf", "-.Inf", "-.INF"}},
{"<<", "!!merge", []string{"<<"}},
} }
m := resolveMap m := resolveMap
@@ -113,13 +113,8 @@ func resolve(tag string, in string) (rtag string, out interface{}) {
case 'D', 'S': case 'D', 'S':
// Int, float, or timestamp. // Int, float, or timestamp.
for i := 0; i != len(in); i++ { plain := strings.Replace(in, "_", "", -1)
if in[i] == '_' { intv, err := strconv.ParseInt(plain, 0, 64)
in = strings.Replace(in, "_", "", -1)
break
}
}
intv, err := strconv.ParseInt(in, 0, 64)
if err == nil { if err == nil {
if intv == int64(int(intv)) { if intv == int64(int(intv)) {
return "!!int", int(intv) return "!!int", int(intv)
@@ -127,26 +122,23 @@ func resolve(tag string, in string) (rtag string, out interface{}) {
return "!!int", intv return "!!int", intv
} }
} }
floatv, err := strconv.ParseFloat(in, 64) floatv, err := strconv.ParseFloat(plain, 64)
if err == nil { if err == nil {
return "!!float", floatv return "!!float", floatv
} }
if strings.HasPrefix(in, "0b") { if strings.HasPrefix(plain, "0b") {
intv, err := strconv.ParseInt(in[2:], 2, 64) intv, err := strconv.ParseInt(plain[2:], 2, 64)
if err == nil { if err == nil {
return "!!int", int(intv) return "!!int", int(intv)
} }
} else if strings.HasPrefix(in, "-0b") { } else if strings.HasPrefix(plain, "-0b") {
intv, err := strconv.ParseInt(in[3:], 2, 64) intv, err := strconv.ParseInt(plain[3:], 2, 64)
if err == nil { if err == nil {
return "!!int", -int(intv) return "!!int", -int(intv)
} }
} }
// XXX Handle timestamps here. // XXX Handle timestamps here.
case '<':
// XXX Handle merge (<<) here.
default: default:
panic("resolveTable item not yet handled: " + panic("resolveTable item not yet handled: " +
string([]byte{c}) + " (with " + in + ")") string([]byte{c}) + " (with " + in + ")")

View File

@@ -1,4 +1,4 @@
package goyaml package yaml
import ( import (
"bytes" "bytes"

View File

@@ -1,4 +1,4 @@
package goyaml package yaml
import ( import (
"reflect" "reflect"

View File

@@ -1,7 +1,7 @@
package goyaml_test package yaml_test
import ( import (
. "launchpad.net/gocheck" . "gopkg.in/check.v1"
"testing" "testing"
) )

View File

@@ -1,4 +1,4 @@
package goyaml package yaml
// Set the writer error and return false. // Set the writer error and return false.
func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool {

View File

@@ -1,5 +1,10 @@
// Package goyaml implements YAML support for the Go language. // Package yaml implements YAML support for the Go language.
package goyaml //
// Source code and other details for the project are available at GitHub:
//
// https://github.com/go-yaml/yaml
//
package yaml
import ( import (
"errors" "errors"
@@ -28,32 +33,31 @@ func handleErr(err *error) {
} }
} }
// Objects implementing the goyaml.Setter interface will receive the YAML // The Setter interface may be implemented by types to do their own custom
// tag and value via the SetYAML method during unmarshaling, rather than // unmarshalling of YAML values, rather than being implicitly assigned by
// being implicitly assigned by the goyaml machinery. If setting the value // the yaml package machinery. If setting the value works, the method should
// works, the method should return true. If it returns false, the given // return true. If it returns false, the value is considered unsupported
// value will be omitted from maps and slices. // and is omitted from maps and slices.
type Setter interface { type Setter interface {
SetYAML(tag string, value interface{}) bool SetYAML(tag string, value interface{}) bool
} }
// Objects implementing the goyaml.Getter interface will get the GetYAML() // The Getter interface is implemented by types to do their own custom
// method called when goyaml is requested to marshal the given value, and // marshalling into a YAML tag and value.
// the result of this method will be marshaled in place of the actual object.
type Getter interface { type Getter interface {
GetYAML() (tag string, value interface{}) GetYAML() (tag string, value interface{})
} }
// Unmarshal decodes the first document found within the in byte slice // Unmarshal decodes the first document found within the in byte slice
// and assigns decoded values into the object pointed by out. // and assigns decoded values into the out value.
// //
// Maps, pointers to structs and ints, etc, may all be used as out values. // Maps and pointers (to a struct, string, int, etc) are accepted as out
// If an internal pointer within a struct is not initialized, goyaml // values. If an internal pointer within a struct is not initialized,
// will initialize it if necessary for unmarshalling the provided data, // the yaml package will initialize it if necessary for unmarshalling
// but the struct provided as out must not be a nil pointer. // the provided data. The out parameter must not be nil.
// //
// The type of the decoded values and the type of out will be considered, // The type of the decoded values and the type of out will be considered,
// and Unmarshal() will do the best possible job to unmarshal values // and Unmarshal will do the best possible job to unmarshal values
// appropriately. It is NOT considered an error, though, to skip values // appropriately. It is NOT considered an error, though, to skip values
// because they are not available in the decoded YAML, or if they are not // because they are not available in the decoded YAML, or if they are not
// compatible with the out value. To ensure something was properly // compatible with the out value. To ensure something was properly
@@ -61,11 +65,11 @@ type Getter interface {
// field (usually the zero value). // field (usually the zero value).
// //
// Struct fields are only unmarshalled if they are exported (have an // Struct fields are only unmarshalled if they are exported (have an
// upper case first letter), and will be unmarshalled using the field // upper case first letter), and are unmarshalled using the field name
// name lowercased by default. When custom field names are desired, the // lowercased as the default key. Custom keys may be defined via the
// tag value may be used to tweak the name. Everything before the first // "yaml" name in the field tag: the content preceding the first comma
// comma in the field tag will be used as the name. The values following // is used as the key, and the following comma-separated options are
// the comma are used to tweak the marshalling process (see Marshal). // used to tweak the marshalling process (see Marshal).
// Conflicting names result in a runtime error. // Conflicting names result in a runtime error.
// //
// For example: // For example:
@@ -75,7 +79,7 @@ type Getter interface {
// B int // B int
// } // }
// var T t // var T t
// goyaml.Unmarshal([]byte("a: 1\nb: 2"), &t) // yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
// //
// See the documentation of Marshal for the format of tags and a list of // See the documentation of Marshal for the format of tags and a list of
// supported tag options. // supported tag options.
@@ -94,14 +98,16 @@ func Unmarshal(in []byte, out interface{}) (err error) {
// Marshal serializes the value provided into a YAML document. The structure // Marshal serializes the value provided into a YAML document. The structure
// of the generated document will reflect the structure of the value itself. // of the generated document will reflect the structure of the value itself.
// Maps, pointers to structs and ints, etc, may all be used as the in value. // Maps and pointers (to struct, string, int, etc) are accepted as the in value.
// //
// In the case of struct values, only exported fields will be serialized. // Struct fields are only unmarshalled if they are exported (have an upper case
// The lowercased field name is used as the key for each exported field, // first letter), and are unmarshalled using the field name lowercased as the
// but this behavior may be changed using the respective field tag. // default key. Custom keys may be defined via the "yaml" name in the field
// The tag may also contain flags to tweak the marshalling behavior for // tag: the content preceding the first comma is used as the key, and the
// the field. Conflicting names result in a runtime error. The tag format // following comma-separated options are used to tweak the marshalling process.
// accepted is: // Conflicting names result in a runtime error.
//
// The field tag format accepted is:
// //
// `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)` // `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
// //
@@ -126,8 +132,8 @@ func Unmarshal(in []byte, out interface{}) (err error) {
// F int "a,omitempty" // F int "a,omitempty"
// B int // B int
// } // }
// goyaml.Marshal(&T{B: 2}) // Returns "b: 2\n" // yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
// goyaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" // yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
// //
func Marshal(in interface{}) (out []byte, err error) { func Marshal(in interface{}) (out []byte, err error) {
defer handleErr(&err) defer handleErr(&err)
@@ -142,7 +148,7 @@ func Marshal(in interface{}) (out []byte, err error) {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// Maintain a mapping of keys to structure field indexes // Maintain a mapping of keys to structure field indexes
// The code in this section was copied from gobson. // The code in this section was copied from mgo/bson.
// structInfo holds details for the serialization of fields of // structInfo holds details for the serialization of fields of
// a given struct. // a given struct.

View File

@@ -1,4 +1,4 @@
package goyaml package yaml
import ( import (
"io" "io"

View File

@@ -1,4 +1,4 @@
package goyaml package yaml
const ( const (
// The size of the input raw buffer. // The size of the input raw buffer.

View File

@@ -1,14 +0,0 @@
[568].out
_*
*.cgo*.*
yaml-*/stamp-h1
yaml-*/Makefile
yaml-*/*/Makefile
yaml-*/libtool
yaml-*/config*
yaml-*/*/*.lo
yaml-*/*/*.la
yaml-*/*/.libs
yaml-*/*/.deps
yaml-*/tests/*

View File

@@ -1 +0,0 @@
propose -cr -for=lp:goyaml

View File

@@ -1,20 +0,0 @@
#!/bin/sh
set -e
BADFMT=`find * -name '*.go' | xargs gofmt -l`
if [ -n "$BADFMT" ]; then
BADFMT=`echo "$BADFMT" | sed "s/^/ /"`
echo -e "gofmt is sad:\n\n$BADFMT"
exit 1
fi
VERSION=`go version | awk '{print $3}'`
if [ $VERSION == 'devel' ]; then
go tool vet \
-methods \
-printf \
-rangeloops \
-printfuncs 'ErrorContextf:1,notFoundf:0,badReqErrorf:0,Commitf:0,Snapshotf:0,Debugf:0' \
.
fi

View File

@@ -1,39 +0,0 @@
include $(GOROOT)/src/Make.inc
YAML=yaml-0.1.3
LIBYAML=$(PWD)/$(YAML)/src/.libs/libyaml.a
TARG=launchpad.net/goyaml
GOFILES=\
goyaml.go\
resolve.go\
CGOFILES=\
decode.go\
encode.go\
CGO_OFILES+=\
helpers.o\
api.o\
scanner.o\
reader.o\
parser.o\
writer.o\
emitter.o\
GOFMT=gofmt
BADFMT:=$(shell $(GOFMT) -l $(GOFILES) $(CGOFILES) $(wildcard *_test.go))
all: package
gofmt: $(BADFMT)
@for F in $(BADFMT); do $(GOFMT) -w $$F && echo $$F; done
include $(GOROOT)/src/Make.pkg
ifneq ($(BADFMT),)
ifneq ($(MAKECMDGOALS),gofmt)
$(warning WARNING: make gofmt: $(BADFMT))
endif
endif