2015-01-25 06:32:33 +03:00
|
|
|
// 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.
|
2014-10-18 02:36:22 +04:00
|
|
|
|
2014-08-05 13:50:21 +04:00
|
|
|
package cloudsigma
|
|
|
|
|
|
|
|
import (
|
2014-10-23 15:12:09 +04:00
|
|
|
"bytes"
|
2014-08-05 13:50:21 +04:00
|
|
|
"encoding/base64"
|
|
|
|
"encoding/json"
|
2014-10-23 15:12:09 +04:00
|
|
|
"errors"
|
2014-11-17 16:27:26 +03:00
|
|
|
"io/ioutil"
|
2014-10-23 15:12:09 +04:00
|
|
|
"net"
|
2014-08-05 13:50:21 +04:00
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
|
2015-01-23 04:35:39 +03:00
|
|
|
"github.com/coreos/coreos-cloudinit/datasource"
|
|
|
|
|
2014-10-20 22:30:50 +04:00
|
|
|
"github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/github.com/cloudsigma/cepgo"
|
2014-08-05 13:50:21 +04:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
userDataFieldName = "cloudinit-user-data"
|
|
|
|
)
|
|
|
|
|
|
|
|
type serverContextService struct {
|
|
|
|
client interface {
|
|
|
|
All() (interface{}, error)
|
|
|
|
Key(string) (interface{}, error)
|
|
|
|
Meta() (map[string]string, error)
|
|
|
|
FetchRaw(string) ([]byte, error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewServerContextService() *serverContextService {
|
|
|
|
return &serverContextService{
|
|
|
|
client: cepgo.NewCepgo(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (_ *serverContextService) IsAvailable() bool {
|
|
|
|
productNameFile, err := os.Open("/sys/class/dmi/id/product_name")
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
productName := make([]byte, 10)
|
|
|
|
_, err = productNameFile.Read(productName)
|
2014-11-17 16:27:26 +03:00
|
|
|
|
|
|
|
return err == nil && string(productName) == "CloudSigma" && hasDHCPLeases()
|
2014-08-05 13:50:21 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (_ *serverContextService) AvailabilityChanges() bool {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (_ *serverContextService) ConfigRoot() string {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func (_ *serverContextService) Type() string {
|
|
|
|
return "server-context"
|
|
|
|
}
|
|
|
|
|
2015-01-23 04:35:39 +03:00
|
|
|
func (scs *serverContextService) FetchMetadata() (metadata datasource.Metadata, err error) {
|
2014-08-05 13:50:21 +04:00
|
|
|
var (
|
|
|
|
inputMetadata struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
UUID string `json:"uuid"`
|
|
|
|
Meta map[string]string `json:"meta"`
|
|
|
|
Nics []struct {
|
2014-10-23 14:21:49 +04:00
|
|
|
Mac string `json:"mac"`
|
|
|
|
IPv4Conf struct {
|
2014-08-05 13:50:21 +04:00
|
|
|
InterfaceType string `json:"interface_type"`
|
2014-10-23 14:21:49 +04:00
|
|
|
IP struct {
|
|
|
|
UUID string `json:"uuid"`
|
|
|
|
} `json:"ip"`
|
|
|
|
} `json:"ip_v4_conf"`
|
2014-10-23 15:12:09 +04:00
|
|
|
VLAN struct {
|
|
|
|
UUID string `json:"uuid"`
|
2014-11-17 16:27:26 +03:00
|
|
|
} `json:"vlan"`
|
2014-08-05 13:50:21 +04:00
|
|
|
} `json:"nics"`
|
|
|
|
}
|
2015-01-23 04:35:39 +03:00
|
|
|
rawMetadata []byte
|
2014-08-05 13:50:21 +04:00
|
|
|
)
|
|
|
|
|
2015-01-23 04:35:39 +03:00
|
|
|
if rawMetadata, err = scs.client.FetchRaw(""); err != nil {
|
|
|
|
return
|
2014-08-05 13:50:21 +04:00
|
|
|
}
|
|
|
|
|
2015-01-23 04:35:39 +03:00
|
|
|
if err = json.Unmarshal(rawMetadata, &inputMetadata); err != nil {
|
|
|
|
return
|
2014-08-05 13:50:21 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if inputMetadata.Name != "" {
|
2015-01-23 04:35:39 +03:00
|
|
|
metadata.Hostname = inputMetadata.Name
|
2014-08-05 13:50:21 +04:00
|
|
|
} else {
|
2015-01-23 04:35:39 +03:00
|
|
|
metadata.Hostname = inputMetadata.UUID
|
2014-08-05 13:50:21 +04:00
|
|
|
}
|
|
|
|
|
2015-01-23 04:35:39 +03:00
|
|
|
metadata.SSHPublicKeys = map[string]string{}
|
2015-02-22 06:31:38 +03:00
|
|
|
// CloudSigma uses an empty string, rather than no string,
|
|
|
|
// to represent the lack of a SSH key
|
2015-02-21 20:22:16 +03:00
|
|
|
if key, _ := inputMetadata.Meta["ssh_public_key"]; len(key) > 0 {
|
2014-08-05 13:50:21 +04:00
|
|
|
splitted := strings.Split(key, " ")
|
2015-01-23 04:35:39 +03:00
|
|
|
metadata.SSHPublicKeys[splitted[len(splitted)-1]] = key
|
2014-08-05 13:50:21 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, nic := range inputMetadata.Nics {
|
2014-10-23 14:21:49 +04:00
|
|
|
if nic.IPv4Conf.IP.UUID != "" {
|
2015-01-23 04:35:39 +03:00
|
|
|
metadata.PublicIPv4 = net.ParseIP(nic.IPv4Conf.IP.UUID)
|
2014-10-23 15:12:09 +04:00
|
|
|
}
|
|
|
|
if nic.VLAN.UUID != "" {
|
|
|
|
if localIP, err := scs.findLocalIP(nic.Mac); err == nil {
|
2015-01-23 04:35:39 +03:00
|
|
|
metadata.PrivateIPv4 = localIP
|
2014-10-23 15:12:09 +04:00
|
|
|
}
|
2014-08-05 13:50:21 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-23 04:35:39 +03:00
|
|
|
return
|
2014-08-05 13:50:21 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (scs *serverContextService) FetchUserdata() ([]byte, error) {
|
|
|
|
metadata, err := scs.client.Meta()
|
|
|
|
if err != nil {
|
|
|
|
return []byte{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
userData, ok := metadata[userDataFieldName]
|
|
|
|
if ok && isBase64Encoded(userDataFieldName, metadata) {
|
|
|
|
if decodedUserData, err := base64.StdEncoding.DecodeString(userData); err == nil {
|
|
|
|
return decodedUserData, nil
|
|
|
|
} else {
|
|
|
|
return []byte{}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return []byte(userData), nil
|
|
|
|
}
|
|
|
|
|
2015-01-23 04:35:39 +03:00
|
|
|
func (scs *serverContextService) findLocalIP(mac string) (net.IP, error) {
|
2014-10-23 15:12:09 +04:00
|
|
|
ifaces, err := net.Interfaces()
|
|
|
|
if err != nil {
|
2015-01-23 04:35:39 +03:00
|
|
|
return nil, err
|
2014-10-23 15:12:09 +04:00
|
|
|
}
|
|
|
|
ifaceMac, err := net.ParseMAC(mac)
|
|
|
|
if err != nil {
|
2015-01-23 04:35:39 +03:00
|
|
|
return nil, err
|
2014-10-23 15:12:09 +04:00
|
|
|
}
|
|
|
|
for _, iface := range ifaces {
|
|
|
|
if !bytes.Equal(iface.HardwareAddr, ifaceMac) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
addrs, err := iface.Addrs()
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, addr := range addrs {
|
|
|
|
switch ip := addr.(type) {
|
|
|
|
case *net.IPNet:
|
|
|
|
if ip.IP.To4() != nil {
|
2015-01-23 04:35:39 +03:00
|
|
|
return ip.IP.To4(), nil
|
2014-10-23 15:12:09 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-01-23 04:35:39 +03:00
|
|
|
return nil, errors.New("Local IP not found")
|
2014-10-23 15:12:09 +04:00
|
|
|
}
|
|
|
|
|
2014-08-05 13:50:21 +04:00
|
|
|
func isBase64Encoded(field string, userdata map[string]string) bool {
|
|
|
|
base64Fields, ok := userdata["base64_fields"]
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, base64Field := range strings.Split(base64Fields, ",") {
|
|
|
|
if field == base64Field {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2014-11-17 16:27:26 +03:00
|
|
|
|
|
|
|
func hasDHCPLeases() bool {
|
|
|
|
files, err := ioutil.ReadDir("/run/systemd/netif/leases/")
|
|
|
|
return err == nil && len(files) > 0
|
|
|
|
}
|