Skip to content

Commit

Permalink
feat: add vultr.com cloud support
Browse files Browse the repository at this point in the history
* cloud-init for vultr.com
* ipv4/v6 support
* set static IPs for private interface

Signed-off-by: Serge Logvinov <serge.logvinov@sinextra.dev>
Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
  • Loading branch information
sergelogvinov authored and smira committed Sep 14, 2021
1 parent 0ff4c7c commit 19a8ae9
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .drone.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,8 @@ local release = {
'_out/vmware-arm64.ova',
'_out/vmlinuz-amd64',
'_out/vmlinuz-arm64',
'_out/vultr-amd64.raw.xz',
'_out/vultr-arm64.raw.xz',
],
checksum: ['sha256', 'sha512'],
},
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ image-%: ## Builds the specified image. Valid options are aws, azure, digital-oc
docker run --rm -v /dev:/dev --privileged $(REGISTRY_AND_USERNAME)/installer:$(IMAGE_TAG) image --platform $* --arch $$arch --tar-to-stdout | tar xz -C $(ARTIFACTS) ; \
done

images: image-aws image-azure image-digital-ocean image-gcp image-hcloud image-metal image-openstack image-scaleway image-upcloud image-vmware ## Builds all known images (AWS, Azure, DigitalOcean, GCP, HCloud, Metal, Openstack, Scaleway, UpCloud and VMware).
images: image-aws image-azure image-digital-ocean image-gcp image-hcloud image-metal image-openstack image-scaleway image-upcloud image-vmware image-vultr ## Builds all known images (AWS, Azure, DigitalOcean, GCP, HCloud, Metal, Openstack, Scaleway, UpCloud, Vultr and VMware).

sbc-%: ## Builds the specified SBC image. Valid options are rpi_4, rock64, bananapi_m64, libretech_all_h3_cc_h5, rockpi_4 and pine64 (e.g. sbc-rpi_4)
@docker pull $(REGISTRY_AND_USERNAME)/installer:$(IMAGE_TAG)
Expand Down
15 changes: 14 additions & 1 deletion cmd/installer/cmd/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func runImageCmd() (err error) {

if options.ConfigSource == "" {
switch p.Name() {
case "aws", "azure", "digital-ocean", "gcp", "hcloud", "scaleway", "upcloud":
case "aws", "azure", "digital-ocean", "gcp", "hcloud", "scaleway", "upcloud", "vultr":
options.ConfigSource = constants.ConfigNone
case "vmware":
options.ConfigSource = constants.ConfigGuestInfo
Expand Down Expand Up @@ -186,6 +186,19 @@ func finalize(platform runtime.Platform, img, arch string) (err error) {
if err = ova.CreateOVAFromRAW(name, img, outputArg, arch); err != nil {
return err
}
case "vultr":
file = filepath.Join(outputArg, fmt.Sprintf("vultr-%s.raw", arch))

err = os.Rename(img, file)
if err != nil {
return err
}

log.Println("compressing image")

if err = xz(file); err != nil {
return err
}
case "metal":
if options.Board != constants.BoardNone {
name := fmt.Sprintf("metal-%s-%s.img", options.Board, arch)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ require (
github.com/vmware-tanzu/sonobuoy v0.53.2
github.com/vmware/govmomi v0.26.1
github.com/vmware/vmw-guestinfo v0.0.0-20200218095840-687661b8bd8e
github.com/vultr/metadata v1.0.3
go.etcd.io/etcd/api/v3 v3.5.0
go.etcd.io/etcd/client/pkg/v3 v3.5.0
go.etcd.io/etcd/client/v3 v3.5.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,8 @@ github.com/vmware/govmomi v0.26.1/go.mod h1:daTuJEcQosNMXYJOeku0qdBJP9SOLLWB3Mqz
github.com/vmware/vmw-guestinfo v0.0.0-20170707015358-25eff159a728/go.mod h1:x9oS4Wk2s2u4tS29nEaDLdzvuHdB19CvSGJjPgkZJNk=
github.com/vmware/vmw-guestinfo v0.0.0-20200218095840-687661b8bd8e h1:In34xdQmxmPpV5YWm3D9ovtyUtkasFWYDfc3UGPdkeo=
github.com/vmware/vmw-guestinfo v0.0.0-20200218095840-687661b8bd8e/go.mod h1:/3jxIXT64LBNFMdpUk5XfFWYK40Z9+HwGH1sjilaV8Y=
github.com/vultr/metadata v1.0.3 h1:sM2KjASP7A49Kltf/fRzAgpCfxV8/HLsgZJB9QXeU1A=
github.com/vultr/metadata v1.0.3/go.mod h1:9W0P2cGZYdhwzzfeOPGSoa2jqWiBpl333rEF9ml9E4A=
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/vmware"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr"
"github.com/talos-systems/talos/pkg/machinery/constants"
)

Expand Down Expand Up @@ -78,6 +79,8 @@ func newPlatform(platform string) (p runtime.Platform, err error) {
p = &upcloud.UpCloud{}
case "vmware":
p = &vmware.VMware{}
case "vultr":
p = &vultr.Vultr{}
default:
return nil, fmt.Errorf("unknown platform: %q", platform)
}
Expand Down
167 changes: 167 additions & 0 deletions internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/vultr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package vultr

import (
"context"
"encoding/json"
"fmt"
"log"
"net"

"github.com/talos-systems/go-procfs/procfs"
"github.com/vultr/metadata"

"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors"
"github.com/talos-systems/talos/pkg/download"
"github.com/talos-systems/talos/pkg/machinery/config"
"github.com/talos-systems/talos/pkg/machinery/config/configloader"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1"
)

const (
// VultrMetadataEndpoint is the local Vultr endpoint fot the instance metadata.
VultrMetadataEndpoint = "http://169.254.169.254/v1.json"
// VultrExternalIPEndpoint is the local Vultr endpoint for the external IP.
VultrExternalIPEndpoint = "http://169.254.169.254/latest/meta-data/public-ipv4"
// VultrHostnameEndpoint is the local Vultr endpoint for the hostname.
VultrHostnameEndpoint = "http://169.254.169.254/latest/meta-data/hostname"
// VultrUserDataEndpoint is the local Vultr endpoint for the config.
VultrUserDataEndpoint = "http://169.254.169.254/latest/user-data"
)

// Vultr is the concrete type that implements the runtime.Platform interface.
type Vultr struct{}

// Name implements the runtime.Platform interface.
func (v *Vultr) Name() string {
return "vultr"
}

// ConfigurationNetwork implements the network configuration interface.
func (v *Vultr) ConfigurationNetwork(metadataConfig []byte, confProvider config.Provider) (config.Provider, error) {
var machineConfig *v1alpha1.Config

machineConfig, ok := confProvider.(*v1alpha1.Config)
if !ok {
return nil, fmt.Errorf("unable to determine machine config type")
}

meta := &metadata.MetaData{}
if err := json.Unmarshal(metadataConfig, meta); err != nil {
return nil, err
}

if machineConfig.MachineConfig == nil {
machineConfig.MachineConfig = &v1alpha1.MachineConfig{}
}

if machineConfig.MachineConfig.MachineNetwork == nil {
machineConfig.MachineConfig.MachineNetwork = &v1alpha1.NetworkConfig{}
}

if machineConfig.MachineConfig.MachineNetwork.NetworkInterfaces == nil {
for i, addr := range meta.Interfaces {
iface := &v1alpha1.Device{
DeviceInterface: fmt.Sprintf("eth%d", i),
}

if addr.IPv4.Address != "" {
iface.DeviceDHCP = true
}

if addr.NetworkType == "private" {
iface.DeviceMTU = 1450

if addr.IPv4.Address != "" {
mask, _ := net.IPMask(net.ParseIP(addr.IPv4.Netmask).To4()).Size()

iface.DeviceDHCP = false
iface.DeviceAddresses = append(iface.DeviceAddresses,
fmt.Sprintf("%s/%d", addr.IPv4.Address, mask),
)
}
}

machineConfig.MachineConfig.MachineNetwork.NetworkInterfaces = append(machineConfig.MachineConfig.MachineNetwork.NetworkInterfaces, iface)
}
}

return confProvider, nil
}

// Configuration implements the runtime.Platform interface.
func (v *Vultr) Configuration(ctx context.Context) ([]byte, error) {
log.Printf("fetching Vultr instance config from: %q ", VultrMetadataEndpoint)

metaConfigDl, err := download.Download(ctx, VultrMetadataEndpoint)
if err != nil {
return nil, errors.ErrNoConfigSource
}

log.Printf("fetching machine config from: %q", VultrUserDataEndpoint)

machineConfigDl, err := download.Download(ctx, VultrUserDataEndpoint,
download.WithErrorOnNotFound(errors.ErrNoConfigSource),
download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource))
if err != nil {
return nil, err
}

confProvider, err := configloader.NewFromBytes(machineConfigDl)
if err != nil {
return nil, err
}

confProvider, err = v.ConfigurationNetwork(metaConfigDl, confProvider)
if err != nil {
return nil, err
}

return confProvider.Bytes()
}

// Mode implements the runtime.Platform interface.
func (v *Vultr) Mode() runtime.Mode {
return runtime.ModeCloud
}

// Hostname implements the runtime.Platform interface.
func (v *Vultr) Hostname(ctx context.Context) (hostname []byte, err error) {
log.Printf("fetching hostname from: %q", VultrHostnameEndpoint)

hostname, err = download.Download(ctx, VultrHostnameEndpoint,
download.WithErrorOnNotFound(errors.ErrNoHostname),
download.WithErrorOnEmptyResponse(errors.ErrNoHostname))
if err != nil {
return nil, err
}

return hostname, nil
}

// ExternalIPs implements the runtime.Platform interface.
func (v *Vultr) ExternalIPs(ctx context.Context) (addrs []net.IP, err error) {
log.Printf("fetching external IP from: %q", VultrExternalIPEndpoint)

exIP, err := download.Download(ctx, VultrExternalIPEndpoint,
download.WithErrorOnNotFound(errors.ErrNoExternalIPs),
download.WithErrorOnEmptyResponse(errors.ErrNoExternalIPs))
if err != nil {
return nil, err
}

if addr := net.ParseIP(string(exIP)); addr != nil {
addrs = append(addrs, addr)
}

return addrs, err
}

// KernelArgs implements the runtime.Platform interface.
func (v *Vultr) KernelArgs() procfs.Parameters {
return []*procfs.Parameter{}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package vultr_test

import (
"testing"

"github.com/stretchr/testify/suite"

"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1"
)

type ConfigSuite struct {
suite.Suite
}

func (suite *ConfigSuite) TestNetworkConfig() {
//nolint:lll
cfg := []byte(`{
"bgp":{"ipv4":{"my-address":"","my-asn":"","peer-address":"","peer-asn":""},"ipv6":{"my-address":"","my-asn":"","peer-address":"","peer-asn":""}},"hostname":"talos","instance-v2-id":"91b07056-af72-4551-b15b-d57d34071be9","instanceid":"50190000","interfaces":[{"ipv4":{"additional":[],"address":"95.111.222.111","gateway":"95.111.222.1","netmask":"255.255.254.0"},"ipv6":{"additional":[],"address":"2001:19f0:5001:2095:1111:2222:3333:4444","network":"2001:19f0:5001:2095::","prefix":"64"},"mac":"56:00:03:89:53:e0","network-type":"public"},{"ipv4":{"additional":[],"address":"10.7.96.3","gateway":"","netmask":"255.255.240.0"},"ipv6":{"additional":[],"network":"","prefix":""},"mac":"5a:00:03:89:53:e0","network-type":"private","network-v2-id":"dadc2b30-0b55-4fa1-8c29-f67215bd5ac4","networkid":"net6126811851cd7"}],"public-keys":["ssh-ed25519"],"region":{"regioncode":"AMS"},"user-defined":[]
}`)

p := &vultr.Vultr{}

defaultMachineConfig := &v1alpha1.Config{}

machineConfig := &v1alpha1.Config{
MachineConfig: &v1alpha1.MachineConfig{
MachineNetwork: &v1alpha1.NetworkConfig{
NetworkInterfaces: []*v1alpha1.Device{
{
DeviceInterface: "eth0",
DeviceDHCP: true,
},
{
DeviceInterface: "eth1",
DeviceAddresses: []string{"10.7.96.3/20"},
DeviceDHCP: false,
DeviceMTU: 1450,
},
},
},
},
}

result, err := p.ConfigurationNetwork(cfg, defaultMachineConfig)

suite.Require().NoError(err)
suite.Assert().Equal(machineConfig, result)
}

func TestConfigSuite(t *testing.T) {
suite.Run(t, new(ConfigSuite))
}
6 changes: 6 additions & 0 deletions website/content/docs/v0.13/Cloud Platforms/vultr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: "Vultr"
description: "Creating a cluster via the CLI (vultr-cli) on Vultr.com."
---

Talos is known to work on Vultr.com; however, it is currently undocumented.

0 comments on commit 19a8ae9

Please sign in to comment.