datasource: replace metadata map with struct

The loosely-typed metadata map is a load of crap. Make it a struct and
let the compiler help us out.
This commit is contained in:
Alex Crawford
2015-01-22 17:35:39 -08:00
parent d4c617fc23
commit 3e47c09b41
17 changed files with 248 additions and 276 deletions

View File

@@ -24,6 +24,8 @@ import (
"os"
"strings"
"github.com/coreos/coreos-cloudinit/datasource"
"github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/github.com/cloudsigma/cepgo"
)
@@ -69,7 +71,7 @@ func (_ *serverContextService) Type() string {
return "server-context"
}
func (scs *serverContextService) FetchMetadata() ([]byte, error) {
func (scs *serverContextService) FetchMetadata() (metadata datasource.Metadata, err error) {
var (
inputMetadata struct {
Name string `json:"name"`
@@ -88,48 +90,41 @@ func (scs *serverContextService) FetchMetadata() ([]byte, error) {
} `json:"vlan"`
} `json:"nics"`
}
outputMetadata struct {
Hostname string `json:"name"`
PublicKeys map[string]string `json:"public_keys"`
LocalIPv4 string `json:"local-ipv4"`
PublicIPv4 string `json:"public-ipv4"`
}
rawMetadata []byte
)
rawMetadata, err := scs.client.FetchRaw("")
if err != nil {
return []byte{}, err
if rawMetadata, err = scs.client.FetchRaw(""); err != nil {
return
}
err = json.Unmarshal(rawMetadata, &inputMetadata)
if err != nil {
return []byte{}, err
if err = json.Unmarshal(rawMetadata, &inputMetadata); err != nil {
return
}
if inputMetadata.Name != "" {
outputMetadata.Hostname = inputMetadata.Name
metadata.Hostname = inputMetadata.Name
} else {
outputMetadata.Hostname = inputMetadata.UUID
metadata.Hostname = inputMetadata.UUID
}
metadata.SSHPublicKeys = map[string]string{}
if key, ok := inputMetadata.Meta["ssh_public_key"]; ok {
splitted := strings.Split(key, " ")
outputMetadata.PublicKeys = make(map[string]string)
outputMetadata.PublicKeys[splitted[len(splitted)-1]] = key
metadata.SSHPublicKeys[splitted[len(splitted)-1]] = key
}
for _, nic := range inputMetadata.Nics {
if nic.IPv4Conf.IP.UUID != "" {
outputMetadata.PublicIPv4 = nic.IPv4Conf.IP.UUID
metadata.PublicIPv4 = net.ParseIP(nic.IPv4Conf.IP.UUID)
}
if nic.VLAN.UUID != "" {
if localIP, err := scs.findLocalIP(nic.Mac); err == nil {
outputMetadata.LocalIPv4 = localIP
metadata.PrivateIPv4 = localIP
}
}
}
return json.Marshal(outputMetadata)
return
}
func (scs *serverContextService) FetchUserdata() ([]byte, error) {
@@ -154,14 +149,14 @@ func (scs *serverContextService) FetchNetworkConfig(a string) ([]byte, error) {
return nil, nil
}
func (scs *serverContextService) findLocalIP(mac string) (string, error) {
func (scs *serverContextService) findLocalIP(mac string) (net.IP, error) {
ifaces, err := net.Interfaces()
if err != nil {
return "", err
return nil, err
}
ifaceMac, err := net.ParseMAC(mac)
if err != nil {
return "", err
return nil, err
}
for _, iface := range ifaces {
if !bytes.Equal(iface.HardwareAddr, ifaceMac) {
@@ -176,12 +171,12 @@ func (scs *serverContextService) findLocalIP(mac string) (string, error) {
switch ip := addr.(type) {
case *net.IPNet:
if ip.IP.To4() != nil {
return ip.IP.To4().String(), nil
return ip.IP.To4(), nil
}
}
}
}
return "", errors.New("Local IP not found")
return nil, errors.New("Local IP not found")
}
func isBase64Encoded(field string, userdata map[string]string) bool {

View File

@@ -15,7 +15,7 @@
package cloudsigma
import (
"encoding/json"
"net"
"reflect"
"testing"
)
@@ -44,12 +44,6 @@ func (f *fakeCepgoClient) FetchRaw(key string) ([]byte, error) {
}
func TestServerContextFetchMetadata(t *testing.T) {
var metadata struct {
Hostname string `json:"name"`
PublicKeys map[string]string `json:"public_keys"`
LocalIPv4 string `json:"local-ipv4"`
PublicIPv4 string `json:"public-ipv4"`
}
client := new(fakeCepgoClient)
scs := NewServerContextService()
scs.client = client
@@ -114,24 +108,20 @@ func TestServerContextFetchMetadata(t *testing.T) {
"uuid": "20a0059b-041e-4d0c-bcc6-9b2852de48b3"
}`)
metadataBytes, err := scs.FetchMetadata()
metadata, err := scs.FetchMetadata()
if err != nil {
t.Error(err.Error())
}
if err := json.Unmarshal(metadataBytes, &metadata); err != nil {
t.Error(err.Error())
}
if metadata.Hostname != "coreos" {
t.Errorf("Hostname is not 'coreos' but %s instead", metadata.Hostname)
}
if metadata.PublicKeys["john@doe"] != "ssh-rsa AAAAB3NzaC1yc2E.../hQ5D5 john@doe" {
if metadata.SSHPublicKeys["john@doe"] != "ssh-rsa AAAAB3NzaC1yc2E.../hQ5D5 john@doe" {
t.Error("Public SSH Keys are not being read properly")
}
if metadata.PublicIPv4 != "31.171.251.74" {
if !metadata.PublicIPv4.Equal(net.ParseIP("31.171.251.74")) {
t.Errorf("Public IP is not 31.171.251.74 but %s instead", metadata.PublicIPv4)
}
}

View File

@@ -16,8 +16,10 @@ package digitalocean
import (
"encoding/json"
"net"
"strconv"
"github.com/coreos/coreos-cloudinit/datasource"
"github.com/coreos/coreos-cloudinit/datasource/metadata"
)
@@ -68,45 +70,43 @@ func NewDatasource(root string) *metadataService {
return &metadataService{MetadataService: metadata.NewDatasource(root, apiVersion, userdataUrl, metadataPath)}
}
func (ms *metadataService) FetchMetadata() ([]byte, error) {
data, err := ms.FetchData(ms.MetadataUrl())
if err != nil || len(data) == 0 {
return []byte{}, err
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
}
var metadata Metadata
if err := json.Unmarshal(data, &metadata); err != nil {
return []byte{}, err
ms.interfaces = m.Interfaces
ms.dns = m.DNS
if len(m.Interfaces.Public) > 0 {
if m.Interfaces.Public[0].IPv4 != nil {
metadata.PublicIPv4 = net.ParseIP(m.Interfaces.Public[0].IPv4.IPAddress)
}
if m.Interfaces.Public[0].IPv6 != nil {
metadata.PublicIPv6 = net.ParseIP(m.Interfaces.Public[0].IPv6.IPAddress)
}
}
if len(m.Interfaces.Private) > 0 {
if m.Interfaces.Private[0].IPv4 != nil {
metadata.PrivateIPv4 = net.ParseIP(m.Interfaces.Private[0].IPv4.IPAddress)
}
if m.Interfaces.Private[0].IPv6 != nil {
metadata.PrivateIPv6 = net.ParseIP(m.Interfaces.Private[0].IPv6.IPAddress)
}
}
metadata.Hostname = m.Hostname
metadata.SSHPublicKeys = map[string]string{}
for i, key := range m.PublicKeys {
metadata.SSHPublicKeys[strconv.Itoa(i)] = key
}
ms.interfaces = metadata.Interfaces
ms.dns = metadata.DNS
attrs := make(map[string]interface{})
if len(metadata.Interfaces.Public) > 0 {
if metadata.Interfaces.Public[0].IPv4 != nil {
attrs["public-ipv4"] = metadata.Interfaces.Public[0].IPv4.IPAddress
}
if metadata.Interfaces.Public[0].IPv6 != nil {
attrs["public-ipv6"] = metadata.Interfaces.Public[0].IPv6.IPAddress
}
}
if len(metadata.Interfaces.Private) > 0 {
if metadata.Interfaces.Private[0].IPv4 != nil {
attrs["local-ipv4"] = metadata.Interfaces.Private[0].IPv4.IPAddress
}
if metadata.Interfaces.Private[0].IPv6 != nil {
attrs["local-ipv6"] = metadata.Interfaces.Private[0].IPv6.IPAddress
}
}
attrs["hostname"] = metadata.Hostname
keys := make(map[string]string)
for i, key := range metadata.PublicKeys {
keys[strconv.Itoa(i)] = key
}
attrs["public_keys"] = keys
return json.Marshal(attrs)
return
}
func (ms metadataService) FetchNetworkConfig(filename string) ([]byte, error) {

View File

@@ -15,10 +15,12 @@
package digitalocean
import (
"bytes"
"fmt"
"net"
"reflect"
"testing"
"github.com/coreos/coreos-cloudinit/datasource"
"github.com/coreos/coreos-cloudinit/datasource/metadata"
"github.com/coreos/coreos-cloudinit/datasource/metadata/test"
"github.com/coreos/coreos-cloudinit/pkg"
@@ -36,7 +38,7 @@ func TestFetchMetadata(t *testing.T) {
root string
metadataPath string
resources map[string]string
expect []byte
expect datasource.Metadata
clientErr error
expectErr error
}{
@@ -81,7 +83,14 @@ func TestFetchMetadata(t *testing.T) {
}
}`,
},
expect: []byte(`{"hostname":"","public-ipv4":"192.168.1.2","public-ipv6":"fe00::","public_keys":{"0":"publickey1","1":"publickey2"}}`),
expect: datasource.Metadata{
PublicIPv4: net.ParseIP("192.168.1.2"),
PublicIPv6: net.ParseIP("fe00::"),
SSHPublicKeys: map[string]string{
"0": "publickey1",
"1": "publickey2",
},
},
},
{
clientErr: pkg.ErrTimeout{Err: fmt.Errorf("test error")},
@@ -99,8 +108,8 @@ func TestFetchMetadata(t *testing.T) {
if Error(err) != Error(tt.expectErr) {
t.Fatalf("bad error (%q): want %q, got %q", tt.resources, tt.expectErr, err)
}
if !bytes.Equal(metadata, tt.expect) {
t.Fatalf("bad fetch (%q): want %q, got %q", tt.resources, tt.expect, metadata)
if !reflect.DeepEqual(tt.expect, metadata) {
t.Fatalf("bad fetch (%q): want %#q, got %#q", tt.resources, tt.expect, metadata)
}
}
}

View File

@@ -17,10 +17,11 @@ package ec2
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"net"
"strings"
"github.com/coreos/coreos-cloudinit/datasource"
"github.com/coreos/coreos-cloudinit/datasource/metadata"
"github.com/coreos/coreos-cloudinit/pkg"
)
@@ -40,59 +41,57 @@ func NewDatasource(root string) *metadataService {
return &metadataService{metadata.NewDatasource(root, apiVersion, userdataPath, metadataPath)}
}
func (ms metadataService) FetchMetadata() ([]byte, error) {
attrs := make(map[string]interface{})
func (ms metadataService) FetchMetadata() (datasource.Metadata, error) {
metadata := datasource.Metadata{}
if keynames, err := ms.fetchAttributes(fmt.Sprintf("%s/public-keys", ms.MetadataUrl())); err == nil {
keyIDs := make(map[string]string)
for _, keyname := range keynames {
tokens := strings.SplitN(keyname, "=", 2)
if len(tokens) != 2 {
return nil, fmt.Errorf("malformed public key: %q", keyname)
return metadata, fmt.Errorf("malformed public key: %q", keyname)
}
keyIDs[tokens[1]] = tokens[0]
}
keys := make(map[string]string)
metadata.SSHPublicKeys = map[string]string{}
for name, id := range keyIDs {
sshkey, err := ms.fetchAttribute(fmt.Sprintf("%s/public-keys/%s/openssh-key", ms.MetadataUrl(), id))
if err != nil {
return nil, err
return metadata, err
}
keys[name] = sshkey
metadata.SSHPublicKeys[name] = sshkey
fmt.Printf("Found SSH key for %q\n", name)
}
attrs["public_keys"] = keys
} else if _, ok := err.(pkg.ErrNotFound); !ok {
return nil, err
return metadata, err
}
if hostname, err := ms.fetchAttribute(fmt.Sprintf("%s/hostname", ms.MetadataUrl())); err == nil {
attrs["hostname"] = strings.Split(hostname, " ")[0]
metadata.Hostname = strings.Split(hostname, " ")[0]
} else if _, ok := err.(pkg.ErrNotFound); !ok {
return nil, err
return metadata, err
}
if localAddr, err := ms.fetchAttribute(fmt.Sprintf("%s/local-ipv4", ms.MetadataUrl())); err == nil {
attrs["local-ipv4"] = localAddr
metadata.PrivateIPv4 = net.ParseIP(localAddr)
} else if _, ok := err.(pkg.ErrNotFound); !ok {
return nil, err
return metadata, err
}
if publicAddr, err := ms.fetchAttribute(fmt.Sprintf("%s/public-ipv4", ms.MetadataUrl())); err == nil {
attrs["public-ipv4"] = publicAddr
metadata.PublicIPv4 = net.ParseIP(publicAddr)
} else if _, ok := err.(pkg.ErrNotFound); !ok {
return nil, err
return metadata, err
}
if content_path, err := ms.fetchAttribute(fmt.Sprintf("%s/network_config/content_path", ms.MetadataUrl())); err == nil {
attrs["network_config"] = map[string]string{
"content_path": content_path,
}
if contentPath, err := ms.fetchAttribute(fmt.Sprintf("%s/network_config/content_path", ms.MetadataUrl())); err == nil {
metadata.NetworkConfigPath = contentPath
} else if _, ok := err.(pkg.ErrNotFound); !ok {
return nil, err
return metadata, err
}
return json.Marshal(attrs)
return metadata, nil
}
func (ms metadataService) Type() string {

View File

@@ -15,11 +15,12 @@
package ec2
import (
"bytes"
"fmt"
"net"
"reflect"
"testing"
"github.com/coreos/coreos-cloudinit/datasource"
"github.com/coreos/coreos-cloudinit/datasource/metadata"
"github.com/coreos/coreos-cloudinit/datasource/metadata/test"
"github.com/coreos/coreos-cloudinit/pkg"
@@ -145,7 +146,7 @@ func TestFetchMetadata(t *testing.T) {
root string
metadataPath string
resources map[string]string
expect []byte
expect datasource.Metadata
clientErr error
expectErr error
}{
@@ -169,7 +170,13 @@ func TestFetchMetadata(t *testing.T) {
"/2009-04-04/meta-data/public-keys/0/openssh-key": "key",
"/2009-04-04/meta-data/network_config/content_path": "path",
},
expect: []byte(`{"hostname":"host","local-ipv4":"1.2.3.4","network_config":{"content_path":"path"},"public-ipv4":"5.6.7.8","public_keys":{"test1":"key"}}`),
expect: datasource.Metadata{
Hostname: "host",
PrivateIPv4: net.ParseIP("1.2.3.4"),
PublicIPv4: net.ParseIP("5.6.7.8"),
SSHPublicKeys: map[string]string{"test1": "key"},
NetworkConfigPath: "path",
},
},
{
root: "/",
@@ -183,7 +190,13 @@ func TestFetchMetadata(t *testing.T) {
"/2009-04-04/meta-data/public-keys/0/openssh-key": "key",
"/2009-04-04/meta-data/network_config/content_path": "path",
},
expect: []byte(`{"hostname":"host","local-ipv4":"1.2.3.4","network_config":{"content_path":"path"},"public-ipv4":"5.6.7.8","public_keys":{"test1":"key"}}`),
expect: datasource.Metadata{
Hostname: "host",
PrivateIPv4: net.ParseIP("1.2.3.4"),
PublicIPv4: net.ParseIP("5.6.7.8"),
SSHPublicKeys: map[string]string{"test1": "key"},
NetworkConfigPath: "path",
},
},
{
clientErr: pkg.ErrTimeout{Err: fmt.Errorf("test error")},
@@ -199,8 +212,8 @@ func TestFetchMetadata(t *testing.T) {
if Error(err) != Error(tt.expectErr) {
t.Fatalf("bad error (%q): want %q, got %q", tt.resources, tt.expectErr, err)
}
if !bytes.Equal(metadata, tt.expect) {
t.Fatalf("bad fetch (%q): want %q, got %q", tt.resources, tt.expect, metadata)
if !reflect.DeepEqual(tt.expect, metadata) {
t.Fatalf("bad fetch (%q): want %#v, got %#v", tt.resources, tt.expect, metadata)
}
}
}