Skip to content

Commit

Permalink
add metadata providers for azure, alicloud and oracle cloud infrastru…
Browse files Browse the repository at this point in the history
…cture

Signed-off-by: Steve Fan <29133953+stevefan1999-personal@users.noreply.github.com>
  • Loading branch information
stevefan1999-personal committed Aug 22, 2020
1 parent a2869a3 commit 50ba9cb
Show file tree
Hide file tree
Showing 6 changed files with 511 additions and 6 deletions.
8 changes: 7 additions & 1 deletion pkg/metadata/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func main() {
log.SetLevel(log.DebugLevel)
}

providers := []string{"aws", "gcp", "hetzner", "openstack", "scaleway", "vultr", "digitalocean", "packet", "cdrom"}
providers := []string{"aws", "gcp", "hetzner", "openstack", "oracle", "azure", "alicloud", "scaleway", "vultr", "digitalocean", "packet", "cdrom"}
args := flag.Args()
if len(args) > 0 {
providers = args
Expand All @@ -93,6 +93,12 @@ func main() {
netProviders = append(netProviders, NewHetzner())
case p == "openstack":
netProviders = append(netProviders, NewOpenstack())
case p == "oracle":
netProviders = append(netProviders, NewOracle())
case p == "alicloud":
netProviders = append(netProviders, NewAliCloud())
case p == "azure":
netProviders = append(netProviders, NewAzure())
case p == "packet":
netProviders = append(netProviders, NewPacket())
case p == "scaleway":
Expand Down
141 changes: 141 additions & 0 deletions pkg/metadata/provider_alicloud.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package main

import (
"fmt"
"path"

"github.com/sl1pm4t/snooze"
)

const (
aliCloudMetadataBaseUrl = "http://100.100.100.200/latest/"
)

type AliCloudMetadataAPI struct {
Region func() (string, error) `method:"GET" path:"/meta-data/region-id"`
Zone func() (string, error) `method:"GET" path:"/meta-data/zone-id"`
ImageID func() (string, error) `method:"GET" path:"/meta-data/image-id"`
InstanceID func() (string, error) `method:"GET" path:"/meta-data/instance-id"`
InstanceType func() (string, error) `method:"GET" path:"/meta-data/instance/instance-type"`
InstanceMaxEgress func() (string, error) `method:"GET" path:"/meta-data/instance/max-netbw-egress"`
InstanceMaxIngress func() (string, error) `method:"GET" path:"/meta-data/instance/max-netbw-ingerss"`
ElasticPublicIP func() (string, error) `method:"GET" path:"/meta-data/eipv4"`
PublicIP func() (string, error) `method:"GET" path:"/meta-data/public-ipv4"`
PrivateIP func() (string, error) `method:"GET" path:"/meta-data/private-ipv4"`
PublicKeys func() (string, error) `method:"GET" path:"/meta-data/public-keys"`
Hostname func() (string, error) `method:"GET" path:"/meta-data/hostname"`
Userdata func() (string, error) `method:"GET" path:"/user-data"`
}

type ProviderAliCloud struct {
DefaultProviderUtil
*AliCloudMetadataAPI
}

func NewAliCloud() (provider *ProviderAliCloud) {
client := snooze.Client{Root: aliCloudMetadataBaseUrl}
api := &AliCloudMetadataAPI{}
client.Create(api)
return &ProviderAliCloud{AliCloudMetadataAPI: api}
}

func (p *ProviderAliCloud) String() string {
return "Alibaba Cloud"
}

func (p *ProviderAliCloud) ShortName() string {
return "alicloud"
}

func (p *ProviderAliCloud) Probe() bool {
res, err := p.Hostname()
return err == nil && res != ""
}

func (p *ProviderAliCloud) Extract() ([]byte, error) {
var ret string
var err error

if ret, err = p.InstanceID(); err == nil {
if err := p.WriteDataToFile("instance id", 0644, ret, path.Join(ConfigPath, "instance_id")); err != nil {
return nil, err
}
} else {
fmt.Errorf("failed to get instance id: %s", err)
}

if ret, err = p.InstanceType(); err == nil {
if err := p.WriteDataToFile("instance type", 0644, ret, path.Join(ConfigPath, "instance_type")); err != nil {
return nil, err
}
} else {
fmt.Errorf("failed to get instance type: %s", err)
}

if ret, err = p.Region(); err == nil {
if err := p.WriteDataToFile("region", 0644, ret, path.Join(ConfigPath, "region")); err != nil {
return nil, err
}
} else {
fmt.Errorf("failed to get region: %s", err)
}

if ret, err = p.Zone(); err == nil {
if err := p.WriteDataToFile("zone", 0644, ret, path.Join(ConfigPath, "zone")); err != nil {
return nil, err
}
} else {
fmt.Errorf("failed to get zone: %s", err)
}

if ret, err = p.ImageID(); err == nil {
if err := p.WriteDataToFile("instance image", 0644, ret, path.Join(ConfigPath, "image")); err != nil {
return nil, err
}
} else {
fmt.Errorf("failed to get instance image: %s", err)
}

if ret, err = p.Hostname(); err == nil {
if err := p.WriteDataToFile("host name", 0644, ret, path.Join(ConfigPath, Hostname)); err != nil {
return nil, err
}
} else {
fmt.Errorf("failed to get host name: %s", err)
}

if ret, err = p.PublicIP(); err != nil {
ret, err = p.ElasticPublicIP()
}

if err == nil {
if err := p.WriteDataToFile("public ipv4", 0644, ret, path.Join(ConfigPath, "public_ipv4")); err != nil {
return nil, err
}
} else {
fmt.Errorf("failed to get public ipv4: %s", err)
}

if ret, err = p.PrivateIP(); err == nil {
if err := p.WriteDataToFile("private ipv4", 0644, ret, path.Join(ConfigPath, "private_ipv4")); err != nil {
return nil, err
}
} else {
fmt.Errorf("failed to get private ipv4: %s", err)
}

if ret, err = p.PublicKeys(); err == nil {
if err = p.MakeFolder("ssh public keys", 0755, path.Join(ConfigPath, SSH)); err != nil {
return nil, err
}

if err = p.WriteDataToFile("ssh public keys", 0600, ret, path.Join(ConfigPath, SSH, "authorized_keys")); err != nil {
return nil, err
}
} else {
fmt.Errorf("failed to get public keys: %s", err)
}

ret, err = p.Userdata()
return []byte(ret), err
}
208 changes: 208 additions & 0 deletions pkg/metadata/provider_azure.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package main

import (
"path"
"strings"

"github.com/hashicorp/go-retryablehttp"
"github.com/sl1pm4t/snooze"
"github.com/thoas/go-funk"
)

const (
azureAPIBaseUrl = "http://169.254.169.254/metadata/instance"
azureAPIVersion = "2020-06-01"
)

type AzurePublicKeyEntry struct {
KeyData string `json:"keyData"`
Path string `json:"path"`
}

type AzureInstanceComputeMetadata struct {
AzEnvironment string `json:"azEnvironment"`
CustomData string `json:"customData"`
IsHostCompatibilityLayerVM string `json:"isHostCompatibilityLayerVm"`
Location string `json:"location"`
Name string `json:"name"`
Offer string `json:"offer"`
OsType string `json:"osType"`
PlacementGroupID string `json:"placementGroupId"`
Plan struct {
Name string `json:"name"`
Product string `json:"product"`
Publisher string `json:"publisher"`
} `json:"plan"`
PlatformFaultDomain string `json:"platformFaultDomain"`
PlatformUpdateDomain string `json:"platformUpdateDomain"`
Provider string `json:"provider"`
PublicKeys *[]AzurePublicKeyEntry `json:"publicKeys,omitempty"`
Publisher string `json:"publisher"`
ResourceGroupName string `json:"resourceGroupName"`
ResourceID string `json:"resourceId"`
SecurityProfile struct {
SecureBootEnabled string `json:"secureBootEnabled"`
VirtualTpmEnabled string `json:"virtualTpmEnabled"`
} `json:"securityProfile"`
Sku string `json:"sku"`
StorageProfile struct {
DataDisks []interface{} `json:"dataDisks"`
ImageReference struct {
ID string `json:"id"`
Offer string `json:"offer"`
Publisher string `json:"publisher"`
Sku string `json:"sku"`
Version string `json:"version"`
} `json:"imageReference"`
OsDisk struct {
Caching string `json:"caching"`
CreateOption string `json:"createOption"`
DiffDiskSettings struct {
Option string `json:"option"`
} `json:"diffDiskSettings"`
DiskSizeGB string `json:"diskSizeGB"`
EncryptionSettings struct {
Enabled string `json:"enabled"`
} `json:"encryptionSettings"`
Image struct {
URI string `json:"uri"`
} `json:"image"`
ManagedDisk struct {
ID string `json:"id"`
StorageAccountType string `json:"storageAccountType"`
} `json:"managedDisk"`
Name string `json:"name"`
OsType string `json:"osType"`
Vhd struct {
URI string `json:"uri"`
} `json:"vhd"`
WriteAcceleratorEnabled string `json:"writeAcceleratorEnabled"`
} `json:"osDisk"`
} `json:"storageProfile"`
SubscriptionID string `json:"subscriptionId"`
Tags string `json:"tags"`
TagsList []interface{} `json:"tagsList"`
Version string `json:"version"`
VMID string `json:"vmId"`
VMScaleSetName string `json:"vmScaleSetName"`
VMSize string `json:"vmSize"`
Zone string `json:"zone"`
}

type AzureInstanceNetworkMetadata struct {
Interface []struct {
Ipv4 struct {
IPAddress []struct {
PrivateIPAddress string `json:"privateIpAddress"`
PublicIPAddress *string `json:"publicIpAddress,omitempty"`
} `json:"ipAddress"`
Subnet []struct {
Address string `json:"address"`
Prefix string `json:"prefix"`
} `json:"subnet"`
} `json:"ipv4"`
Ipv6 struct {
IPAddress []interface{} `json:"ipAddress"`
} `json:"ipv6"`
MacAddress string `json:"macAddress"`
} `json:"interface"`
}

type AzureMetadataAPI struct {
ComputeMetadata func() (AzureInstanceComputeMetadata, error) `method:"GET" path:"/compute"`
NetworkMetadata func() (AzureInstanceNetworkMetadata, error) `method:"GET" path:"/network"`
}

type ProviderAzure struct {
DefaultProviderUtil
*AzureMetadataAPI
}

func NewAzure() *ProviderAzure {
client := snooze.Client{Root: azureAPIBaseUrl, Before: func(request *retryablehttp.Request, client *retryablehttp.Client) {
request.Header.Add("Metadata", "true")
q := request.URL.Query()
q.Add("api-version", azureAPIVersion)
request.URL.RawQuery = q.Encode()
}}
api := &AzureMetadataAPI{}
client.Create(api)
return &ProviderAzure{AzureMetadataAPI: api}
}

func (p *ProviderAzure) String() string {
return "Azure"
}

func (p *ProviderAzure) ShortName() string {
return "azure"
}

func (p *ProviderAzure) Probe() bool {
_, err := p.ComputeMetadata()
return err == nil
}

func (p *ProviderAzure) Extract() ([]byte, error) {

if metadata, err := p.NetworkMetadata(); err == nil {
ip := metadata.Interface[0].Ipv4.IPAddress[0]

if ip.PublicIPAddress != nil {
if err := p.WriteDataToFile("public ipv4", 0644, *ip.PublicIPAddress, path.Join(ConfigPath, "public_ipv4")); err != nil {
return nil, err
}
}

if err := p.WriteDataToFile("private ipv4", 0644, ip.PrivateIPAddress, path.Join(ConfigPath, "private_ipv4")); err != nil {
return nil, err
}
}

if metadata, err := p.ComputeMetadata(); err == nil {
if err := p.WriteDataToFile("instance id", 0644, metadata.VMID, path.Join(ConfigPath, "instance_id")); err != nil {
return nil, err
}

// there's three major shapes in oracle cloud, flexible, bare metal and VM, so it is definitely the instance type
if err := p.WriteDataToFile("instance type", 0644, metadata.VMSize, path.Join(ConfigPath, "instance_type")); err != nil {
return nil, err
}

if err := p.WriteDataToFile("region", 0644, metadata.Location, path.Join(ConfigPath, "region")); err != nil {
return nil, err
}

if err := p.WriteDataToFile("instance image", 0644, metadata.StorageProfile.OsDisk.Name, path.Join(ConfigPath, "image")); err != nil {
return nil, err
}

// unfortunately azure assumes the vm name to be the instance host name
if err := p.WriteDataToFile("host name", 0644, metadata.Name, path.Join(ConfigPath, Hostname)); err != nil {
return nil, err
}

if err := p.WriteDataToFile("availability zone", 0644, metadata.Zone, path.Join(ConfigPath, "availability_zone")); err != nil {
return nil, err
}

if publicKeys := metadata.PublicKeys; publicKeys != nil && len(*publicKeys) > 0 {
if err := p.MakeFolder("ssh public keys", 0755, path.Join(ConfigPath, SSH)); err != nil {
return nil, err
}

combinedSSHKeys := strings.Join(funk.Map(publicKeys, func(entry AzurePublicKeyEntry) string {
return entry.KeyData
}).([]string), "\n")

if err := p.WriteDataToFile("ssh public keys", 0600, combinedSSHKeys, path.Join(ConfigPath, SSH, "authorized_keys")); err != nil {
return nil, err
}
}

// TODO: this field is disabled, figure out a way to obtain user data somewhere else
return []byte(metadata.CustomData), nil
}

return nil, nil
}
Loading

0 comments on commit 50ba9cb

Please sign in to comment.