This commit is contained in:
commit
fc4efb086b
@ -169,6 +169,7 @@ List of flannel configuration parameters:
|
||||
- **ip_masq**: Install IP masquerade rules for traffic outside of flannel subnet
|
||||
- **subnet_file**: Path to flannel subnet file to write out
|
||||
- **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
|
||||
|
||||
|
@ -21,6 +21,12 @@ mkisofs -R -V config-2 -o configdrive.iso /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
|
||||
|
||||
One exception to the above, when using QEMU it is possible to skip creating an
|
||||
|
@ -39,6 +39,6 @@ type Etcd2 struct {
|
||||
PeerCAFile string `yaml:"peer_ca_file" env:"ETCD_PEER_CA_FILE"`
|
||||
PeerCertFile string `yaml:"peer_cert_file" env:"ETCD_PEER_CERT_FILE"`
|
||||
PeerKeyFile string `yaml:"peer_key_file" env:"ETCD_PEER_KEY_FILE"`
|
||||
Proxy string `yaml:"proxy" env:"ETCD_PROXY"`
|
||||
Proxy string `yaml:"proxy" env:"ETCD_PROXY" valid:"^(on|off|readonly)$"`
|
||||
SnapshotCount int `yaml:"snapshot_count" env:"ETCD_SNAPSHOTCOUNT"`
|
||||
}
|
||||
|
@ -23,4 +23,5 @@ type Flannel struct {
|
||||
IPMasq string `yaml:"ip_masq" env:"FLANNELD_IP_MASQ"`
|
||||
SubnetFile string `yaml:"subnet_file" env:"FLANNELD_SUBNET_FILE"`
|
||||
Iface string `yaml:"interface" env:"FLANNELD_IFACE"`
|
||||
PublicIP string `yaml:"public_ip" env:"FLANNELD_PUBLIC_IP"`
|
||||
}
|
||||
|
26
config/ignition.go
Normal file
26
config/ignition.go
Normal 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)
|
||||
}
|
@ -40,6 +40,8 @@ func Validate(userdataBytes []byte) (Report, error) {
|
||||
return Report{}, nil
|
||||
case config.IsScript(string(userdataBytes)):
|
||||
return Report{}, nil
|
||||
case config.IsIgnitionConfig(string(userdataBytes)):
|
||||
return Report{}, nil
|
||||
case config.IsCloudConfig(string(userdataBytes)):
|
||||
return validateCloudConfig(userdataBytes, Rules)
|
||||
default:
|
||||
|
@ -111,6 +111,16 @@ func TestValidate(t *testing.T) {
|
||||
{
|
||||
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 {
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
"github.com/coreos/coreos-cloudinit/datasource/metadata/cloudsigma"
|
||||
"github.com/coreos/coreos-cloudinit/datasource/metadata/digitalocean"
|
||||
"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/url"
|
||||
"github.com/coreos/coreos-cloudinit/datasource/waagent"
|
||||
@ -39,7 +40,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
version = "1.4.1+git"
|
||||
version = "1.5.0+git"
|
||||
datasourceInterval = 100 * time.Millisecond
|
||||
datasourceMaxInterval = 30 * time.Second
|
||||
datasourceTimeout = 5 * time.Minute
|
||||
@ -57,6 +58,7 @@ var (
|
||||
ec2MetadataService string
|
||||
cloudSigmaMetadataService bool
|
||||
digitalOceanMetadataService string
|
||||
packetMetadataService string
|
||||
url string
|
||||
procCmdLine bool
|
||||
}
|
||||
@ -78,6 +80,7 @@ func init() {
|
||||
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.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.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")
|
||||
@ -109,6 +112,10 @@ var (
|
||||
"cloudsigma": oemConfig{
|
||||
"from-cloudsigma-metadata": "true",
|
||||
},
|
||||
"packet": oemConfig{
|
||||
"from-packet-metadata": "https://metadata.packet.net/",
|
||||
"convert-netconf": "packet",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@ -139,14 +146,15 @@ func main() {
|
||||
case "":
|
||||
case "debian":
|
||||
case "digitalocean":
|
||||
case "packet":
|
||||
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)
|
||||
}
|
||||
|
||||
dss := getDatasources()
|
||||
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)
|
||||
}
|
||||
|
||||
@ -192,16 +200,20 @@ func main() {
|
||||
|
||||
var ccu *config.CloudConfig
|
||||
var script *config.Script
|
||||
if ud, err := initialize.ParseUserData(userdata); err != nil {
|
||||
fmt.Printf("Failed to parse user-data: %v\nContinuing...\n", err)
|
||||
failure = true
|
||||
} else {
|
||||
switch ud, err := initialize.ParseUserData(userdata); err {
|
||||
case initialize.ErrIgnitionConfig:
|
||||
fmt.Printf("Detected an Ignition config. Exiting...")
|
||||
os.Exit(0)
|
||||
case nil:
|
||||
switch t := ud.(type) {
|
||||
case *config.CloudConfig:
|
||||
ccu = t
|
||||
case *config.Script:
|
||||
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")
|
||||
@ -215,6 +227,8 @@ func main() {
|
||||
ifaces, err = network.ProcessDebianNetconf(metadata.NetworkConfig)
|
||||
case "digitalocean":
|
||||
ifaces, err = network.ProcessDigitalOceanNetconf(metadata.NetworkConfig)
|
||||
case "packet":
|
||||
ifaces, err = network.ProcessPacketNetconf(metadata.NetworkConfig)
|
||||
default:
|
||||
err = fmt.Errorf("Unsupported network config format %q", flags.convertNetconf)
|
||||
}
|
||||
@ -290,6 +304,9 @@ func getDatasources() []datasource.Datasource {
|
||||
if 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 {
|
||||
dss = append(dss, proc_cmdline.NewDatasource())
|
||||
}
|
||||
|
106
datasource/metadata/packet/metadata.go
Normal file
106
datasource/metadata/packet/metadata.go
Normal 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"
|
||||
}
|
@ -21,6 +21,10 @@ import (
|
||||
"github.com/coreos/coreos-cloudinit/config"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrIgnitionConfig = errors.New("not a config (found Ignition)")
|
||||
)
|
||||
|
||||
func ParseUserData(contents string) (interface{}, error) {
|
||||
if len(contents) == 0 {
|
||||
return nil, nil
|
||||
@ -33,6 +37,8 @@ func ParseUserData(contents string) (interface{}, error) {
|
||||
case config.IsCloudConfig(contents):
|
||||
log.Printf("Parsing user-data as cloud-config")
|
||||
return config.NewCloudConfig(contents)
|
||||
case config.IsIgnitionConfig(contents):
|
||||
return nil, ErrIgnitionConfig
|
||||
default:
|
||||
return nil, errors.New("Unrecognized user-data format")
|
||||
}
|
||||
|
@ -130,7 +130,17 @@ type bondInterface struct {
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -52,7 +52,7 @@ func TestInterfaceGenerators(t *testing.T) {
|
||||
},
|
||||
{
|
||||
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",
|
||||
kind: "bond",
|
||||
iface: &bondInterface{logicalInterface: logicalInterface{
|
||||
|
133
network/packet.go
Normal file
133
network/packet.go
Normal 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
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user