Compare commits

..

36 Commits

Author SHA1 Message Date
Alex Crawford
fac805dc11 Merge pull request #375 from crawford/build
build: extract the version number from git
2015-08-12 11:09:29 -07:00
Alex Crawford
94ea0b99ea Merge pull request #374 from crawford/timeout
pkg/http: up the timeout to 10 seconds
2015-08-12 11:08:04 -07:00
Alex Crawford
56a80d84cf build: extract the version number from git
Update the tests as well.
2015-08-12 11:05:04 -07:00
Alex Crawford
00c9174da4 pkg/http: up the timeout to 10 seconds
Additionally, fix the units on that multiplication. This isn't
acceleration.
2015-08-11 10:42:41 -07:00
Alex Crawford
ec8742c9ba Merge pull request #371 from crawford/http-client
pkg: update HttpClient to use newer Go features
2015-08-07 20:03:01 -07:00
Alex Crawford
b3b09aeb19 pkg: update HttpClient to use newer Go features 2015-08-07 19:30:47 -07:00
Alex Crawford
481d98c0b5 Merge pull request #370 from crawford/etcd2
config: update flags for etcd 2.1
2015-08-07 17:03:43 -07:00
Alex Crawford
f30727a675 config: update flags for etcd 2.1 2015-08-07 16:20:36 -07:00
Alex Crawford
e1305937e6 Merge pull request #362 from bodgit/mkisofs
Add note for creating config-drive ISO on OS X
2015-07-23 18:42:37 -07:00
Alex Crawford
20c4653ecf Merge pull request #365 from cusspvz/flannel/add-public-ip-opt
Add PublicIP option on flannel
2015-07-23 13:41:06 -07:00
José Moreira
43c6da06a5 add public_ip opt on cloud-config.md 2015-07-23 17:04:37 +01:00
José Moreira
7ab84601c3 Add PublicIP opt 2015-07-22 22:54:03 +01:00
Alex Crawford
a24b23663c Merge pull request #364 from crawford/etcd
config: specific valid values for ETCD_PROXY
2015-07-21 22:21:25 -07:00
Alex Crawford
91fe744bd2 config: specific valid values for ETCD_PROXY 2015-07-21 14:20:10 -07:00
Matt Dainty
eb8fc045ee Add note for creating config-drive ISO on OS X 2015-07-20 10:27:02 +01:00
Alex Crawford
ba83b2871f coreos-cloudinit: bump to v1.5.0+git 2015-07-14 11:55:39 -07:00
Alex Crawford
f36821f7ce coreos-cloudinit: bump to v1.5.0 2015-07-14 11:54:56 -07:00
Alex Crawford
97fe210760 Merge pull request #356 from crawford/ignition
config: recognize Ignition configs and no-op
2015-07-10 16:34:18 -07:00
Alex Crawford
c6400f7751 config: recognize Ignition configs and no-op 2015-07-10 16:32:57 -07:00
Alex Crawford
f6647634f0 Merge pull request #352 from packethost/packet-datasource
datasource: add packethost metadata
2015-07-10 12:25:37 -07:00
Sam Tresler
837d3d3622 datasource: add packethost metadata 2015-07-10 15:13:57 -04:00
Alex Crawford
1063a4b9ee Merge pull request #351 from packethost/bond-config-options
Bond options persisted to the generated netdev file.
2015-07-10 09:08:31 -07:00
Sam Tresler
081f77a102 Persisting bond options to the netdev file, and updating the test. 2015-06-23 11:21:26 -04:00
Alex Crawford
41289286ca Merge pull request #354 from packethost/go-ci-versions
Go ci versions
2015-06-22 16:04:56 -07:00
Sam Tresler
d50a4069a6 Removing goland 1.3 and 1.2 from Travis testing 2015-06-22 18:43:27 -04:00
Alex Crawford
be0c9c56e4 Merge pull request #347 from crawford/import
doc: deprecate coreos-ssh-import-*
2015-05-28 13:45:02 -07:00
Alex Crawford
6467f06656 doc: deprecate coreos-ssh-import-* 2015-05-28 13:35:51 -07:00
Alex Crawford
7a05e63fcc coreos-cloudinit: bump to v1.4.1+git 2015-05-12 17:08:27 -07:00
Alex Crawford
ca6f97d050 coreos-cloudinit: bump to v1.4.1 2015-05-12 17:08:06 -07:00
Alex Crawford
d086bca9e4 Merge pull request #336 from crawford/doc
docs: fix typo with etcd2 env vars
2015-04-28 13:48:05 -07:00
Alex Crawford
d25f18776f docs: fix typo with etcd2 env vars 2015-04-27 18:03:13 -07:00
Alex Crawford
c583b77cdb Merge pull request #335 from crawford/github
config: deprecate fetching SSH keys from remote endpoints
2015-04-20 11:29:38 -07:00
Alex Crawford
ed4d5fac4c config: deprecate fetching SSH keys from remote endpoints 2015-04-20 11:23:35 -07:00
Rob Szumski
40429204ba Merge pull request #333 from coreos/robszumski-patch-1
docs: add info about discovery size parameter
2015-04-16 12:04:49 -07:00
Rob Szumski
d72d54be59 docs: add info about discovery size parameter 2015-04-16 12:02:37 -07:00
Alex Crawford
373c7ecbd9 coreos-cloudinit: bump to v1.4.0+git 2015-04-09 17:48:36 -07:00
19 changed files with 421 additions and 134 deletions

View File

@@ -4,10 +4,6 @@ matrix:
include: include:
- go: 1.4 - go: 1.4
env: TOOLS_CMD=golang.org/x/tools/cmd env: TOOLS_CMD=golang.org/x/tools/cmd
- go: 1.3
env: TOOLS_CMD=code.google.com/p/go.tools/cmd
- go: 1.2
env: TOOLS_CMD=code.google.com/p/go.tools/cmd
install: install:
- go get ${TOOLS_CMD}/cover - go get ${TOOLS_CMD}/cover

View File

@@ -0,0 +1,38 @@
# Deprecated Cloud-Config Features
## Retrieving SSH Authorized Keys
### From a GitHub User
Using the `coreos-ssh-import-github` field, we can import public SSH keys from a GitHub user to use as authorized keys to a server.
```yaml
#cloud-config
users:
- name: elroy
coreos-ssh-import-github: elroy
```
### From an HTTP Endpoint
We can also pull public SSH keys from any HTTP endpoint which matches [GitHub's API response format](https://developer.github.com/v3/users/keys/#list-public-keys-for-a-user).
For example, if you have an installation of GitHub Enterprise, you can provide a complete URL with an authentication token:
```yaml
#cloud-config
users:
- name: elroy
coreos-ssh-import-url: https://github-enterprise.example.com/api/v3/users/elroy/keys?access_token=<TOKEN>
```
You can also specify any URL whose response matches the JSON format for public keys:
```yaml
#cloud-config
users:
- name: elroy
coreos-ssh-import-url: https://example.com/public-keys
```

View File

@@ -76,14 +76,16 @@ _Note: The `$private_ipv4` and `$public_ipv4` substitution variables referenced
#### etcd2 #### etcd2
The `coreos.etcd2.*` parameters will be translated to a partial systemd unit acting as an etcd configuration file. The `coreos.etcd2.*` parameters will be translated to a partial systemd unit acting as an etcd configuration file.
If the platform environment supports the templating feature of coreos-cloudinit it is possible to automate etcd configuration with the `$private_ipv4` and `$public_ipv4` fields. For example, the following cloud-config document... If the platform environment supports the templating feature of coreos-cloudinit it is possible to automate etcd configuration with the `$private_ipv4` and `$public_ipv4` fields. When generating a [discovery token](https://discovery.etcd.io/new?size=3), set the `size` parameter, since etcd uses this to determine if all members have joined the cluster. After the cluster is bootstrapped, it can grow or shrink from this configured size.
For example, the following cloud-config document...
```yaml ```yaml
#cloud-config #cloud-config
coreos: coreos:
etcd2: etcd2:
# generate a new token for each unique cluster from https://discovery.etcd.io/new # generate a new token for each unique cluster from https://discovery.etcd.io/new?size=3
discovery: https://discovery.etcd.io/<token> discovery: https://discovery.etcd.io/<token>
# multi-region and multi-cloud deployments need to use $public_ipv4 # multi-region and multi-cloud deployments need to use $public_ipv4
advertise-client-urls: http://$public_ipv4:2379 advertise-client-urls: http://$public_ipv4:2379
@@ -102,7 +104,7 @@ Environment="ETCD_DISCOVERY=https://discovery.etcd.io/<token>"
Environment="ETCD_ADVERTISE_CLIENT_URLS=http://203.0.113.29:2379" Environment="ETCD_ADVERTISE_CLIENT_URLS=http://203.0.113.29:2379"
Environment="ETCD_INITIAL_ADVERTISE_PEER_URLS=http://192.0.2.13:2380" Environment="ETCD_INITIAL_ADVERTISE_PEER_URLS=http://192.0.2.13:2380"
Environment="ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379,http://0.0.0.0:4001" Environment="ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379,http://0.0.0.0:4001"
Environment="ETCD_LISTEN_PEERS_URLS=http://192.0.2.13:2380,http://192.0.2.13:7001" Environment="ETCD_LISTEN_PEER_URLS=http://192.0.2.13:2380,http://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].
@@ -167,6 +169,7 @@ List of flannel configuration parameters:
- **ip_masq**: Install IP masquerade rules for traffic outside of flannel subnet - **ip_masq**: Install IP masquerade rules for traffic outside of flannel subnet
- **subnet_file**: Path to flannel subnet file to write out - **subnet_file**: Path to flannel subnet file to write out
- **interface**: Interface (name or IP) that should be used for inter-host communication - **interface**: Interface (name or IP) that should be used for inter-host communication
- **public_ip**: IP accessible by other nodes for inter-host communication
[flannel-readme]: https://github.com/coreos/flannel/blob/master/README.md [flannel-readme]: https://github.com/coreos/flannel/blob/master/README.md
@@ -332,9 +335,9 @@ All but the `passwd` and `ssh-authorized-keys` fields will be ignored if the use
- **groups**: Add user to these additional groups - **groups**: Add user to these additional groups
- **no-user-group**: Boolean. Skip default group creation. - **no-user-group**: Boolean. Skip default group creation.
- **ssh-authorized-keys**: List of public SSH keys to authorize for this user - **ssh-authorized-keys**: List of public SSH keys to authorize for this user
- **coreos-ssh-import-github**: Authorize SSH keys from GitHub user - **coreos-ssh-import-github** [DEPRECATED]: Authorize SSH keys from GitHub user
- **coreos-ssh-import-github-users**: Authorize SSH keys from a list of GitHub users - **coreos-ssh-import-github-users** [DEPRECATED]: Authorize SSH keys from a list of GitHub users
- **coreos-ssh-import-url**: Authorize SSH keys imported from a url endpoint. - **coreos-ssh-import-url** [DEPRECATED]: Authorize SSH keys imported from a url endpoint.
- **system**: Create the user as a system user. No home directory will be created. - **system**: Create the user as a system user. No home directory will be created.
- **no-log-init**: Boolean. Skip initialization of lastlog and faillog databases. - **no-log-init**: Boolean. Skip initialization of lastlog and faillog databases.
- **shell**: User's login shell. - **shell**: User's login shell.
@@ -380,43 +383,6 @@ perl -e 'print crypt("password","\$6\$SALT\$") . "\n"'
Using a higher number of rounds will help create more secure passwords, but given enough time, password hashes can be reversed. On most RPM based distributions there is a tool called mkpasswd available in the `expect` package, but this does not handle "rounds" nor advanced hashing algorithms. Using a higher number of rounds will help create more secure passwords, but given enough time, password hashes can be reversed. On most RPM based distributions there is a tool called mkpasswd available in the `expect` package, but this does not handle "rounds" nor advanced hashing algorithms.
#### Retrieving SSH Authorized Keys
##### From a GitHub User
Using the `coreos-ssh-import-github` field, we can import public SSH keys from a GitHub user to use as authorized keys to a server.
```yaml
#cloud-config
users:
- name: elroy
coreos-ssh-import-github: elroy
```
##### From an HTTP Endpoint
We can also pull public SSH keys from any HTTP endpoint which matches [GitHub's API response format](https://developer.github.com/v3/users/keys/#list-public-keys-for-a-user).
For example, if you have an installation of GitHub Enterprise, you can provide a complete URL with an authentication token:
```yaml
#cloud-config
users:
- name: elroy
coreos-ssh-import-url: https://github-enterprise.example.com/api/v3/users/elroy/keys?access_token=<TOKEN>
```
You can also specify any URL whose response matches the JSON format for public keys:
```yaml
#cloud-config
users:
- name: elroy
coreos-ssh-import-url: https://example.com/public-keys
```
### write_files ### write_files
The `write_files` directive defines a set of files to create on the local filesystem. The `write_files` directive defines a set of files to create on the local filesystem.

View File

@@ -21,6 +21,12 @@ mkisofs -R -V config-2 -o configdrive.iso /tmp/new-drive
rm -r /tmp/new-drive rm -r /tmp/new-drive
``` ```
If on OS X, replace the `mkisofs` invocation with:
```sh
hdiutil makehybrid -iso -joliet -default-volume-name config-2 -o configdrive.iso /tmp/new-drive
```
## QEMU virtfs ## QEMU virtfs
One exception to the above, when using QEMU it is possible to skip creating an One exception to the above, when using QEMU it is possible to skip creating an

7
build
View File

@@ -1,7 +1,10 @@
#!/bin/bash -e #!/bin/bash -e
NAME="coreos-cloudinit"
ORG_PATH="github.com/coreos" ORG_PATH="github.com/coreos"
REPO_PATH="${ORG_PATH}/coreos-cloudinit" REPO_PATH="${ORG_PATH}/${NAME}"
VERSION=$(git describe --dirty --tags)
GLDFLAGS="-X main.version \"${VERSION}\""
if [ ! -h gopath/src/${REPO_PATH} ]; then if [ ! -h gopath/src/${REPO_PATH} ]; then
mkdir -p gopath/src/${ORG_PATH} mkdir -p gopath/src/${ORG_PATH}
@@ -11,4 +14,4 @@ fi
export GOBIN=${PWD}/bin export GOBIN=${PWD}/bin
export GOPATH=${PWD}/gopath export GOPATH=${PWD}/gopath
go build -o bin/coreos-cloudinit ${REPO_PATH} go build -ldflags "${GLDFLAGS}" -o ${GOBIN}/${NAME} ${REPO_PATH}

View File

@@ -16,15 +16,18 @@ package config
type Etcd2 struct { type Etcd2 struct {
AdvertiseClientURLs string `yaml:"advertise_client_urls" env:"ETCD_ADVERTISE_CLIENT_URLS"` AdvertiseClientURLs string `yaml:"advertise_client_urls" env:"ETCD_ADVERTISE_CLIENT_URLS"`
CAFile string `yaml:"ca_file" env:"ETCD_CA_FILE"` CAFile string `yaml:"ca_file" env:"ETCD_CA_FILE" deprecated:"ca_file obsoleted by trusted_ca_file and client_cert_auth"`
CertFile string `yaml:"cert_file" env:"ETCD_CERT_FILE"` CertFile string `yaml:"cert_file" env:"ETCD_CERT_FILE"`
ClientCertAuth bool `yaml:"client_cert_auth" env:"ETCD_CLIENT_CERT_AUTH"`
CorsOrigins string `yaml:"cors" env:"ETCD_CORS"` CorsOrigins string `yaml:"cors" env:"ETCD_CORS"`
DataDir string `yaml:"data_dir" env:"ETCD_DATA_DIR"` DataDir string `yaml:"data_dir" env:"ETCD_DATA_DIR"`
Debug bool `yaml:"debug" env:"ETCD_DEBUG"`
Discovery string `yaml:"discovery" env:"ETCD_DISCOVERY"` Discovery string `yaml:"discovery" env:"ETCD_DISCOVERY"`
DiscoveryFallback string `yaml:"discovery_fallback" env:"ETCD_DISCOVERY_FALLBACK"` DiscoveryFallback string `yaml:"discovery_fallback" env:"ETCD_DISCOVERY_FALLBACK"`
DiscoverySRV string `yaml:"discovery_srv" env:"ETCD_DISCOVERY_SRV"` DiscoverySRV string `yaml:"discovery_srv" env:"ETCD_DISCOVERY_SRV"`
DiscoveryProxy string `yaml:"discovery_proxy" env:"ETCD_DISCOVERY_PROXY"` DiscoveryProxy string `yaml:"discovery_proxy" env:"ETCD_DISCOVERY_PROXY"`
ElectionTimeout int `yaml:"election_timeout" env:"ETCD_ELECTION_TIMEOUT"` ElectionTimeout int `yaml:"election_timeout" env:"ETCD_ELECTION_TIMEOUT"`
ForceNewCluster bool `yaml:"force_new_cluster" env:"ETCD_FORCE_NEW_CLUSTER"`
HeartbeatInterval int `yaml:"heartbeat_interval" env:"ETCD_HEARTBEAT_INTERVAL"` HeartbeatInterval int `yaml:"heartbeat_interval" env:"ETCD_HEARTBEAT_INTERVAL"`
InitialAdvertisePeerURLs string `yaml:"initial_advertise_peer_urls" env:"ETCD_INITIAL_ADVERTISE_PEER_URLS"` InitialAdvertisePeerURLs string `yaml:"initial_advertise_peer_urls" env:"ETCD_INITIAL_ADVERTISE_PEER_URLS"`
InitialCluster string `yaml:"initial_cluster" env:"ETCD_INITIAL_CLUSTER"` InitialCluster string `yaml:"initial_cluster" env:"ETCD_INITIAL_CLUSTER"`
@@ -33,12 +36,21 @@ type Etcd2 struct {
KeyFile string `yaml:"key_file" env:"ETCD_KEY_FILE"` KeyFile string `yaml:"key_file" env:"ETCD_KEY_FILE"`
ListenClientURLs string `yaml:"listen_client_urls" env:"ETCD_LISTEN_CLIENT_URLS"` ListenClientURLs string `yaml:"listen_client_urls" env:"ETCD_LISTEN_CLIENT_URLS"`
ListenPeerURLs string `yaml:"listen_peer_urls" env:"ETCD_LISTEN_PEER_URLS"` ListenPeerURLs string `yaml:"listen_peer_urls" env:"ETCD_LISTEN_PEER_URLS"`
LogPackageLevels string `yaml:"log_package_levels" env:"ETCD_LOG_PACKAGE_LEVELS"`
MaxSnapshots int `yaml:"max_snapshots" env:"ETCD_MAX_SNAPSHOTS"` MaxSnapshots int `yaml:"max_snapshots" env:"ETCD_MAX_SNAPSHOTS"`
MaxWALs int `yaml:"max_wals" env:"ETCD_MAX_WALS"` MaxWALs int `yaml:"max_wals" env:"ETCD_MAX_WALS"`
Name string `yaml:"name" env:"ETCD_NAME"` Name string `yaml:"name" env:"ETCD_NAME"`
PeerCAFile string `yaml:"peer_ca_file" env:"ETCD_PEER_CA_FILE"` PeerCAFile string `yaml:"peer_ca_file" env:"ETCD_PEER_CA_FILE" deprecated:"peer_ca_file obsoleted peer_trusted_ca_file and peer_client_cert_auth"`
PeerCertFile string `yaml:"peer_cert_file" env:"ETCD_PEER_CERT_FILE"` PeerCertFile string `yaml:"peer_cert_file" env:"ETCD_PEER_CERT_FILE"`
PeerKeyFile string `yaml:"peer_key_file" env:"ETCD_PEER_KEY_FILE"` PeerKeyFile string `yaml:"peer_key_file" env:"ETCD_PEER_KEY_FILE"`
Proxy string `yaml:"proxy" env:"ETCD_PROXY"` PeerClientCertAuth bool `yaml:"peer_client_cert_auth" env:"ETCD_PEER_CLIENT_CERT_AUTH"`
SnapshotCount int `yaml:"snapshot_count" env:"ETCD_SNAPSHOTCOUNT"` PeerTrustedCAFile string `yaml:"peer_trusted_ca_file" env:"ETCD_PEER_TRUSTED_CA_FILE"`
Proxy string `yaml:"proxy" env:"ETCD_PROXY" valid:"^(on|off|readonly)$"`
ProxyDialTimeout int `yaml:"proxy_dial_timeout" env:"ETCD_PROXY_DIAL_TIMEOUT"`
ProxyFailureWait int `yaml:"proxy_failure_wait" env:"ETCD_PROXY_FAILURE_WAIT"`
ProxyReadTimeout int `yaml:"proxy_read_timeout" env:"ETCD_PROXY_READ_TIMEOUT"`
ProxyRefreshInterval int `yaml:"proxy_refresh_interval" env:"ETCD_PROXY_REFRESH_INTERVAL"`
ProxyWriteTimeout int `yaml:"proxy_write_timeout" env:"ETCD_PROXY_WRITE_TIMEOUT"`
SnapshotCount int `yaml:"snapshot_count" env:"ETCD_SNAPSHOT_COUNT"`
TrustedCAFile string `yaml:"trusted_ca_file" env:"ETCD_TRUSTED_CA_FILE"`
} }

View File

@@ -23,4 +23,5 @@ type Flannel struct {
IPMasq string `yaml:"ip_masq" env:"FLANNELD_IP_MASQ"` IPMasq string `yaml:"ip_masq" env:"FLANNELD_IP_MASQ"`
SubnetFile string `yaml:"subnet_file" env:"FLANNELD_SUBNET_FILE"` SubnetFile string `yaml:"subnet_file" env:"FLANNELD_SUBNET_FILE"`
Iface string `yaml:"interface" env:"FLANNELD_IFACE"` Iface string `yaml:"interface" env:"FLANNELD_IFACE"`
PublicIP string `yaml:"public_ip" env:"FLANNELD_PUBLIC_IP"`
} }

26
config/ignition.go Normal file
View File

@@ -0,0 +1,26 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"encoding/json"
)
func IsIgnitionConfig(userdata string) bool {
var cfg struct {
Version *int `json:"ignitionVersion" yaml:"ignition_version"`
}
return (json.Unmarshal([]byte(userdata), &cfg) == nil && cfg.Version != nil)
}

View File

@@ -18,9 +18,9 @@ type User struct {
Name string `yaml:"name"` Name string `yaml:"name"`
PasswordHash string `yaml:"passwd"` PasswordHash string `yaml:"passwd"`
SSHAuthorizedKeys []string `yaml:"ssh_authorized_keys"` SSHAuthorizedKeys []string `yaml:"ssh_authorized_keys"`
SSHImportGithubUser string `yaml:"coreos_ssh_import_github"` SSHImportGithubUser string `yaml:"coreos_ssh_import_github" deprecated:"trying to fetch from a remote endpoint introduces too many intermittent errors"`
SSHImportGithubUsers []string `yaml:"coreos_ssh_import_github_users"` SSHImportGithubUsers []string `yaml:"coreos_ssh_import_github_users" deprecated:"trying to fetch from a remote endpoint introduces too many intermittent errors"`
SSHImportURL string `yaml:"coreos_ssh_import_url"` SSHImportURL string `yaml:"coreos_ssh_import_url" deprecated:"trying to fetch from a remote endpoint introduces too many intermittent errors"`
GECOS string `yaml:"gecos"` GECOS string `yaml:"gecos"`
Homedir string `yaml:"homedir"` Homedir string `yaml:"homedir"`
NoCreateHome bool `yaml:"no_create_home"` NoCreateHome bool `yaml:"no_create_home"`

View File

@@ -40,6 +40,8 @@ func Validate(userdataBytes []byte) (Report, error) {
return Report{}, nil return Report{}, nil
case config.IsScript(string(userdataBytes)): case config.IsScript(string(userdataBytes)):
return Report{}, nil return Report{}, nil
case config.IsIgnitionConfig(string(userdataBytes)):
return Report{}, nil
case config.IsCloudConfig(string(userdataBytes)): case config.IsCloudConfig(string(userdataBytes)):
return validateCloudConfig(userdataBytes, Rules) return validateCloudConfig(userdataBytes, Rules)
default: default:

View File

@@ -111,6 +111,16 @@ func TestValidate(t *testing.T) {
{ {
config: "#!/bin/bash\necho hey", config: "#!/bin/bash\necho hey",
}, },
{
config: "{}",
report: Report{entries: []Entry{{entryError, `must be "#cloud-config" or begin with "#!"`, 1}}},
},
{
config: `{"ignitionVersion":0}`,
},
{
config: `{"ignitionVersion":1}`,
},
} }
for i, tt := range tests { for i, tt := range tests {

View File

@@ -29,6 +29,7 @@ import (
"github.com/coreos/coreos-cloudinit/datasource/metadata/cloudsigma" "github.com/coreos/coreos-cloudinit/datasource/metadata/cloudsigma"
"github.com/coreos/coreos-cloudinit/datasource/metadata/digitalocean" "github.com/coreos/coreos-cloudinit/datasource/metadata/digitalocean"
"github.com/coreos/coreos-cloudinit/datasource/metadata/ec2" "github.com/coreos/coreos-cloudinit/datasource/metadata/ec2"
"github.com/coreos/coreos-cloudinit/datasource/metadata/packet"
"github.com/coreos/coreos-cloudinit/datasource/proc_cmdline" "github.com/coreos/coreos-cloudinit/datasource/proc_cmdline"
"github.com/coreos/coreos-cloudinit/datasource/url" "github.com/coreos/coreos-cloudinit/datasource/url"
"github.com/coreos/coreos-cloudinit/datasource/waagent" "github.com/coreos/coreos-cloudinit/datasource/waagent"
@@ -39,7 +40,6 @@ import (
) )
const ( const (
version = "1.4.0"
datasourceInterval = 100 * time.Millisecond datasourceInterval = 100 * time.Millisecond
datasourceMaxInterval = 30 * time.Second datasourceMaxInterval = 30 * time.Second
datasourceTimeout = 5 * time.Minute datasourceTimeout = 5 * time.Minute
@@ -57,6 +57,7 @@ var (
ec2MetadataService string ec2MetadataService string
cloudSigmaMetadataService bool cloudSigmaMetadataService bool
digitalOceanMetadataService string digitalOceanMetadataService string
packetMetadataService string
url string url string
procCmdLine bool procCmdLine bool
} }
@@ -66,6 +67,7 @@ var (
oem string oem string
validate bool validate bool
}{} }{}
version = "was not built properly"
) )
func init() { func init() {
@@ -78,6 +80,7 @@ func init() {
flag.StringVar(&flags.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(&flags.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(&flags.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(&flags.sources.packetMetadataService, "from-packet-metadata", "", "Download Packet data from metadata service")
flag.StringVar(&flags.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(&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.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(&flags.oem, "oem", "", "Use the settings specific to the provided OEM") flag.StringVar(&flags.oem, "oem", "", "Use the settings specific to the provided OEM")
@@ -109,6 +112,10 @@ var (
"cloudsigma": oemConfig{ "cloudsigma": oemConfig{
"from-cloudsigma-metadata": "true", "from-cloudsigma-metadata": "true",
}, },
"packet": oemConfig{
"from-packet-metadata": "https://metadata.packet.net/",
"convert-netconf": "packet",
},
} }
) )
@@ -131,7 +138,7 @@ func main() {
} }
if flags.printVersion == true { if flags.printVersion == true {
fmt.Printf("coreos-cloudinit version %s\n", version) fmt.Printf("coreos-cloudinit %s\n", version)
os.Exit(0) os.Exit(0)
} }
@@ -139,14 +146,15 @@ func main() {
case "": case "":
case "debian": case "debian":
case "digitalocean": case "digitalocean":
case "packet":
default: default:
fmt.Printf("Invalid option to -convert-netconf: '%s'. Supported options: 'debian, digitalocean'\n", flags.convertNetconf) fmt.Printf("Invalid option to -convert-netconf: '%s'. Supported options: 'debian, digitalocean, packet'\n", flags.convertNetconf)
os.Exit(2) 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-packet-metadata, --from-url or --from-proc-cmdline")
os.Exit(2) os.Exit(2)
} }
@@ -192,16 +200,20 @@ func main() {
var ccu *config.CloudConfig var ccu *config.CloudConfig
var script *config.Script var script *config.Script
if ud, err := initialize.ParseUserData(userdata); err != nil { switch ud, err := initialize.ParseUserData(userdata); err {
fmt.Printf("Failed to parse user-data: %v\nContinuing...\n", err) case initialize.ErrIgnitionConfig:
failure = true fmt.Printf("Detected an Ignition config. Exiting...")
} else { os.Exit(0)
case nil:
switch t := ud.(type) { switch t := ud.(type) {
case *config.CloudConfig: case *config.CloudConfig:
ccu = t ccu = t
case *config.Script: case *config.Script:
script = t script = t
} }
default:
fmt.Printf("Failed to parse user-data: %v\nContinuing...\n", err)
failure = true
} }
fmt.Println("Merging cloud-config from meta-data and user-data") fmt.Println("Merging cloud-config from meta-data and user-data")
@@ -215,6 +227,8 @@ func main() {
ifaces, err = network.ProcessDebianNetconf(metadata.NetworkConfig) ifaces, err = network.ProcessDebianNetconf(metadata.NetworkConfig)
case "digitalocean": case "digitalocean":
ifaces, err = network.ProcessDigitalOceanNetconf(metadata.NetworkConfig) ifaces, err = network.ProcessDigitalOceanNetconf(metadata.NetworkConfig)
case "packet":
ifaces, err = network.ProcessPacketNetconf(metadata.NetworkConfig)
default: default:
err = fmt.Errorf("Unsupported network config format %q", flags.convertNetconf) err = fmt.Errorf("Unsupported network config format %q", flags.convertNetconf)
} }
@@ -290,6 +304,9 @@ func getDatasources() []datasource.Datasource {
if flags.sources.waagent != "" { if flags.sources.waagent != "" {
dss = append(dss, waagent.NewDatasource(flags.sources.waagent)) dss = append(dss, waagent.NewDatasource(flags.sources.waagent))
} }
if flags.sources.packetMetadataService != "" {
dss = append(dss, packet.NewDatasource(flags.sources.packetMetadataService))
}
if flags.sources.procCmdLine { if flags.sources.procCmdLine {
dss = append(dss, proc_cmdline.NewDatasource()) dss = append(dss, proc_cmdline.NewDatasource())
} }

View File

@@ -0,0 +1,106 @@
// 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 packet
import (
"encoding/json"
"net"
"strconv"
"github.com/coreos/coreos-cloudinit/datasource"
"github.com/coreos/coreos-cloudinit/datasource/metadata"
)
const (
DefaultAddress = "https://metadata.packet.net/"
apiVersion = ""
userdataUrl = "userdata"
metadataPath = "metadata"
)
type Netblock struct {
Address net.IP `json:"address"`
Cidr int `json:"cidr"`
Netmask net.IP `json:"netmask"`
Gateway net.IP `json:"gateway"`
AddressFamily int `json:"address_family"`
Public bool `json:"public"`
}
type Nic struct {
Name string `json:"name"`
Mac string `json:"mac"`
}
type NetworkData struct {
Interfaces []Nic `json:"interfaces"`
Netblocks []Netblock `json:"addresses"`
DNS []net.IP `json:"dns"`
}
// Metadata that will be pulled from the https://metadata.packet.net/metadata only. We have the opportunity to add more later.
type Metadata struct {
Hostname string `json:"hostname"`
SSHKeys []string `json:"ssh_keys"`
NetworkData NetworkData `json:"network"`
}
type metadataService struct {
metadata.MetadataService
}
func NewDatasource(root string) *metadataService {
return &metadataService{MetadataService: metadata.NewDatasource(root, apiVersion, userdataUrl, metadataPath)}
}
func (ms *metadataService) FetchMetadata() (metadata datasource.Metadata, err error) {
var data []byte
var m Metadata
if data, err = ms.FetchData(ms.MetadataUrl()); err != nil || len(data) == 0 {
return
}
if err = json.Unmarshal(data, &m); err != nil {
return
}
if len(m.NetworkData.Netblocks) > 0 {
for _, Netblock := range m.NetworkData.Netblocks {
if Netblock.AddressFamily == 4 {
if Netblock.Public == true {
metadata.PublicIPv4 = Netblock.Address
} else {
metadata.PrivateIPv4 = Netblock.Address
}
} else {
metadata.PublicIPv6 = Netblock.Address
}
}
}
metadata.Hostname = m.Hostname
metadata.SSHPublicKeys = map[string]string{}
for i, key := range m.SSHKeys {
metadata.SSHPublicKeys[strconv.Itoa(i)] = key
}
metadata.NetworkConfig, err = json.Marshal(m.NetworkData)
return
}
func (ms metadataService) Type() string {
return "packet-metadata-service"
}

View File

@@ -21,6 +21,10 @@ import (
"github.com/coreos/coreos-cloudinit/config" "github.com/coreos/coreos-cloudinit/config"
) )
var (
ErrIgnitionConfig = errors.New("not a config (found Ignition)")
)
func ParseUserData(contents string) (interface{}, error) { func ParseUserData(contents string) (interface{}, error) {
if len(contents) == 0 { if len(contents) == 0 {
return nil, nil return nil, nil
@@ -33,6 +37,8 @@ func ParseUserData(contents string) (interface{}, error) {
case config.IsCloudConfig(contents): case config.IsCloudConfig(contents):
log.Printf("Parsing user-data as cloud-config") log.Printf("Parsing user-data as cloud-config")
return config.NewCloudConfig(contents) return config.NewCloudConfig(contents)
case config.IsIgnitionConfig(contents):
return nil, ErrIgnitionConfig
default: default:
return nil, errors.New("Unrecognized user-data format") return nil, errors.New("Unrecognized user-data format")
} }

View File

@@ -130,7 +130,17 @@ type bondInterface struct {
} }
func (b *bondInterface) Netdev() string { func (b *bondInterface) Netdev() string {
return fmt.Sprintf("[NetDev]\nKind=bond\nName=%s\n", b.name) config := fmt.Sprintf("[NetDev]\nKind=bond\nName=%s\n", b.name)
if b.hwaddr != nil {
config += fmt.Sprintf("MACAddress=%s\n", b.hwaddr.String())
}
config += fmt.Sprintf("\n[Bond]\n")
for _, name := range sortedKeys(b.options) {
config += fmt.Sprintf("%s=%s\n", name, b.options[name])
}
return config
} }
func (b *bondInterface) Type() string { func (b *bondInterface) Type() string {

View File

@@ -52,7 +52,7 @@ func TestInterfaceGenerators(t *testing.T) {
}, },
{ {
name: "testname", name: "testname",
netdev: "[NetDev]\nKind=bond\nName=testname\n", netdev: "[NetDev]\nKind=bond\nName=testname\n\n[Bond]\n",
network: "[Match]\nName=testname\n\n[Network]\nBond=testbond1\nVLAN=testvlan1\nVLAN=testvlan2\nDHCP=true\n", network: "[Match]\nName=testname\n\n[Network]\nBond=testbond1\nVLAN=testvlan1\nVLAN=testvlan2\nDHCP=true\n",
kind: "bond", kind: "bond",
iface: &bondInterface{logicalInterface: logicalInterface{ iface: &bondInterface{logicalInterface: logicalInterface{

133
network/packet.go Normal file
View File

@@ -0,0 +1,133 @@
// 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 network
import (
"encoding/json"
"net"
"github.com/coreos/coreos-cloudinit/datasource/metadata/packet"
)
func ProcessPacketNetconf(config []byte) ([]InterfaceGenerator, error) {
var netdata packet.NetworkData
if err := json.Unmarshal(config, &netdata); err != nil {
return nil, err
}
var nameservers []net.IP
if netdata.DNS != nil {
nameservers = netdata.DNS
} else {
nameservers = append(nameservers, net.ParseIP("8.8.8.8"), net.ParseIP("8.8.4.4"))
}
generators, err := parseNetwork(netdata, nameservers)
if err != nil {
return nil, err
}
return generators, nil
}
func parseNetwork(netdata packet.NetworkData, nameservers []net.IP) ([]InterfaceGenerator, error) {
var interfaces []InterfaceGenerator
var addresses []net.IPNet
var routes []route
for _, netblock := range netdata.Netblocks {
addresses = append(addresses, net.IPNet{
IP: netblock.Address,
Mask: net.IPMask(netblock.Netmask),
})
if netblock.Public == false {
routes = append(routes, route{
destination: net.IPNet{
IP: net.IPv4(10, 0, 0, 0),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
gateway: netblock.Gateway,
})
} else {
if netblock.AddressFamily == 4 {
routes = append(routes, route{
destination: net.IPNet{
IP: net.IPv4zero,
Mask: net.IPMask(net.IPv4zero),
},
gateway: netblock.Gateway,
})
} else {
routes = append(routes, route{
destination: net.IPNet{
IP: net.IPv6zero,
Mask: net.IPMask(net.IPv6zero),
},
gateway: netblock.Gateway,
})
}
}
}
bond := bondInterface{
logicalInterface: logicalInterface{
name: "bond0",
config: configMethodStatic{
addresses: addresses,
nameservers: nameservers,
routes: routes,
},
},
options: map[string]string{
"Mode": "802.3ad",
"LACPTransmitRate": "fast",
"MIIMonitorSec": ".2",
"UpDelaySec": ".2",
"DownDelaySec": ".2",
},
}
for _, iface := range netdata.Interfaces {
if iface.Name != "chassis0" && iface.Name != "ipmi0" {
bond.slaves = append(bond.slaves, iface.Name)
if iface.Name == "enp1s0f0" {
bond.hwaddr, _ = net.ParseMAC(iface.Mac)
}
}
}
for _, iface := range netdata.Interfaces {
if iface.Name != "chassis0" && iface.Name != "ipmi0" {
p := physicalInterface{
logicalInterface: logicalInterface{
name: iface.Name,
config: configMethodStatic{
nameservers: nameservers,
},
children: []networkInterface{&bond},
},
}
if iface.Name == "enp1s0f0" {
p.configDepth = 20
}
interfaces = append(interfaces, &p)
}
}
interfaces = append(interfaces, &bond)
return interfaces, nil
}

View File

@@ -15,12 +15,10 @@
package pkg package pkg
import ( import (
"crypto/tls"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"net"
"net/http" "net/http"
neturl "net/url" neturl "net/url"
"strings" "strings"
@@ -55,16 +53,15 @@ type ErrNetwork struct {
} }
type HttpClient struct { type HttpClient struct {
// Initial backoff duration. Defaults to 50 milliseconds
InitialBackoff time.Duration
// Maximum exp backoff duration. Defaults to 5 seconds // Maximum exp backoff duration. Defaults to 5 seconds
MaxBackoff time.Duration MaxBackoff time.Duration
// Maximum number of connection retries. Defaults to 15 // Maximum number of connection retries. Defaults to 15
MaxRetries int MaxRetries int
// HTTP client timeout, this is suggested to be low since exponential
// backoff will kick off too. Defaults to 2 seconds
Timeout time.Duration
// Whether or not to skip TLS verification. Defaults to false // Whether or not to skip TLS verification. Defaults to false
SkipTLS bool SkipTLS bool
@@ -78,29 +75,12 @@ type Getter interface {
func NewHttpClient() *HttpClient { func NewHttpClient() *HttpClient {
hc := &HttpClient{ hc := &HttpClient{
InitialBackoff: 50 * time.Millisecond,
MaxBackoff: time.Second * 5, MaxBackoff: time.Second * 5,
MaxRetries: 15, MaxRetries: 15,
Timeout: time.Duration(2) * time.Second,
SkipTLS: false, SkipTLS: false,
} client: &http.Client{
Timeout: 10 * time.Second,
// We need to create our own client in order to add timeout support.
// TODO(c4milo) Replace it once Go 1.3 is officially used by CoreOS
// More info: https://code.google.com/p/go/source/detail?r=ada6f2d5f99f
hc.client = &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: hc.SkipTLS,
},
Dial: func(network, addr string) (net.Conn, error) {
deadline := time.Now().Add(hc.Timeout)
c, err := net.DialTimeout(network, addr, hc.Timeout)
if err != nil {
return nil, err
}
c.SetDeadline(deadline)
return c, nil
},
}, },
} }
@@ -134,7 +114,7 @@ func (h *HttpClient) GetRetry(rawurl string) ([]byte, error) {
dataURL := url.String() dataURL := url.String()
duration := 50 * time.Millisecond duration := h.InitialBackoff
for retry := 1; retry <= h.MaxRetries; retry++ { for retry := 1; retry <= h.MaxRetries; retry++ {
log.Printf("Fetching data from %s. Attempt #%d", dataURL, retry) log.Printf("Fetching data from %s. Attempt #%d", dataURL, retry)

51
test
View File

@@ -1,19 +1,8 @@
#!/bin/bash -e #!/bin/bash -e
#
# Run all coreos-cloudinit tests
# ./test
# ./test -v
#
# Run tests for one package
# PKG=initialize ./test
#
# Invoke ./cover for HTML output
COVER=${COVER:-"-cover"}
source ./build source ./build
declare -a TESTPKGS=( SRC="
config config
config/validate config/validate
datasource datasource
@@ -31,36 +20,22 @@ declare -a TESTPKGS=(
network network
pkg pkg
system system
) "
if [ -z "$PKG" ]; then echo "Checking gofix..."
GOFMTPATH="${TESTPKGS[*]} *.go" go tool fix -diff $SRC
# prepend repo path to each package
TESTPKGS="${TESTPKGS[*]/#/${REPO_PATH}/} ./"
else
GOFMTPATH="$TESTPKGS"
# strip out slashes and dots from PKG=./foo/
TESTPKGS=${PKG//\//}
TESTPKGS=${TESTPKGS//./}
TESTPKGS=${TESTPKGS/#/${REPO_PATH}/}
fi
echo "Running tests..."
go test -i ${TESTPKGS}
go test ${COVER} $@ ${TESTPKGS}
echo "Checking gofmt..." echo "Checking gofmt..."
fmtRes=$(gofmt -l $GOFMTPATH) gofmt -d -e $SRC
if [ -n "$fmtRes" ]; then
echo "$fmtRes" # split SRC into an array and prepend REPO_PATH to each local package for go vet
exit 1 split_vet=(${SRC// / })
fi VET_TEST=${split_vet[@]/#/${REPO_PATH}/}
echo "Checking govet..." echo "Checking govet..."
vetRes=$(go vet $TESTPKGS) go vet $VET_TEST
if [ -n "${vetRes}" ]; then
echo -e "govet checking failed:\n${vetRes}" echo "Running tests..."
exit 255 go test -timeout 60s -cover $@ ${VET_TEST} --race
fi
echo "Success" echo "Success"