From 75cecb4210ad0d6ef201dafd307b4d023ccd7d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Alb=C3=B8ge?= Date: Fri, 16 Aug 2024 01:54:29 +0200 Subject: [PATCH] feat: add Apache Cloudstack support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for new platform. Signed-off-by: Andrey Smirnov Signed-off-by: Claus Albøge --- Makefile | 2 +- hack/release.toml | 6 + .../platform/cloudstack/cloudstack.go | 117 +++++++++ .../platform/cloudstack/cloudstack_test.go | 39 +++ .../v1alpha1/platform/cloudstack/metadata.go | 76 ++++++ .../cloudstack/testdata/expected.yaml | 18 ++ .../cloudstack/testdata/metadata.json | 6 + .../pkg/runtime/v1alpha1/platform/platform.go | 5 +- pkg/imager/profile/default.go | 12 + .../v1.8/introduction/support-matrix.md | 5 +- .../install/cloud-platforms/cloudstack.md | 228 ++++++++++++++++++ 11 files changed, 510 insertions(+), 4 deletions(-) create mode 100644 internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/cloudstack.go create mode 100644 internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/cloudstack_test.go create mode 100644 internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/metadata.go create mode 100644 internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/testdata/expected.yaml create mode 100644 internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/testdata/metadata.json create mode 100644 website/content/v1.8/talos-guides/install/cloud-platforms/cloudstack.md diff --git a/Makefile b/Makefile index ef56239a3c..82401105ed 100644 --- a/Makefile +++ b/Makefile @@ -395,7 +395,7 @@ image-%: ## Builds the specified image. Valid options are aws, azure, digital-oc images-essential: image-aws image-azure image-gcp image-metal secureboot-installer ## Builds only essential images used in the CI (AWS, GCP, and Metal). -images: image-akamai image-aws image-azure image-digital-ocean image-exoscale image-gcp image-hcloud image-iso image-metal image-nocloud image-opennebula image-openstack image-oracle image-scaleway image-upcloud image-vmware image-vultr ## Builds all known images (AWS, Azure, DigitalOcean, Exoscale, GCP, HCloud, Metal, NoCloud, OpenNebula, OpenStack, Oracle, Scaleway, UpCloud, Vultr and VMware). +images: image-akamai image-aws image-azure image-digital-ocean image-exoscale image-cloudstack image-gcp image-hcloud image-iso image-metal image-nocloud image-opennebula image-openstack image-oracle image-scaleway image-upcloud image-vmware image-vultr ## Builds all known images (AWS, Azure, DigitalOcean, Exoscale, Cloudstack, GCP, HCloud, Metal, NoCloud, OpenNebula, OpenStack, Oracle, Scaleway, UpCloud, Vultr and VMware). .PHONY: iso iso: image-iso ## Builds the ISO and outputs it to the artifact directory. diff --git a/hack/release.toml b/hack/release.toml index 8498fd4054..2b1052a20d 100644 --- a/hack/release.toml +++ b/hack/release.toml @@ -192,6 +192,12 @@ The Talos bundled Flannel manifest was simplified to remove the `install-cni` st title = "Device Extra Settle Timeout" description = """\ Talos Linux now supports a kernel command line argument `talos.device.settle_time=3m` to set the device extra settle timeout to workaround issues with broken drivers. +""" + + [notes.platform] + title = "Platform Support" + description = """\ +Talos Linux now supports Apache CloudStack platform. """ [make_deps] diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/cloudstack.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/cloudstack.go new file mode 100644 index 0000000000..051949d2f1 --- /dev/null +++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/cloudstack.go @@ -0,0 +1,117 @@ +// 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 cloudstack contains the Cloudstack platform implementation. +package cloudstack + +import ( + "context" + "fmt" + "log" + "net/netip" + "strings" + + "github.com/cosi-project/runtime/pkg/state" + "github.com/siderolabs/go-procfs/procfs" + + "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" + "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" + "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils" + "github.com/siderolabs/talos/pkg/download" + "github.com/siderolabs/talos/pkg/machinery/constants" + "github.com/siderolabs/talos/pkg/machinery/resources/network" + runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" +) + +// Cloudstack is the concrete type that implements the runtime.Platform interface. +type Cloudstack struct{} + +// ParseMetadata converts Cloudstack platform metadata into platform network config. +func (e *Cloudstack) ParseMetadata(metadata *MetadataConfig) (*runtime.PlatformNetworkConfig, error) { + networkConfig := &runtime.PlatformNetworkConfig{} + + if metadata.Hostname != "" { + hostnameSpec := network.HostnameSpecSpec{ + ConfigLayer: network.ConfigPlatform, + } + + if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil { + return nil, err + } + + networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) + } + + if metadata.PublicIPv4 != "" { + if ip, err := netip.ParseAddr(metadata.PublicIPv4); err == nil { + networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip) + } + } + + networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ + Platform: e.Name(), + Hostname: metadata.Hostname, + Region: metadata.Zone, + Zone: metadata.Zone, + InstanceType: strings.ToLower(strings.SplitN(metadata.InstanceType, " ", 2)[0]), + InstanceID: metadata.InstanceID, + ProviderID: fmt.Sprintf("cloudstack://%s", metadata.InstanceID), + } + + return networkConfig, nil +} + +// Name implements the runtime.Platform interface. +func (e *Cloudstack) Name() string { + return "cloudstack" +} + +// Configuration implements the runtime.Platform interface. +func (e *Cloudstack) Configuration(ctx context.Context, r state.State) ([]byte, error) { + if err := netutils.Wait(ctx, r); err != nil { + return nil, err + } + + log.Printf("fetching machine config from %q", CloudstackUserDataEndpoint) + + return download.Download(ctx, CloudstackUserDataEndpoint, + download.WithErrorOnNotFound(errors.ErrNoConfigSource), + download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource)) +} + +// Mode implements the runtime.Platform interface. +func (e *Cloudstack) Mode() runtime.Mode { + return runtime.ModeCloud +} + +// KernelArgs implements the runtime.Platform interface. +func (e *Cloudstack) KernelArgs(string) procfs.Parameters { + return []*procfs.Parameter{ + procfs.NewParameter("console").Append("tty1").Append("ttyS0"), + procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"), + } +} + +// NetworkConfiguration implements the runtime.Platform interface. +func (e *Cloudstack) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error { + log.Printf("fetching cloudstack instance config from: %q", CloudstackMetadataEndpoint) + + metadata, err := e.getMetadata(ctx) + if err != nil { + return err + } + + networkConfig, err := e.ParseMetadata(metadata) + if err != nil { + return err + } + + select { + case ch <- networkConfig: + case <-ctx.Done(): + return ctx.Err() + } + + return nil +} diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/cloudstack_test.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/cloudstack_test.go new file mode 100644 index 0000000000..a94ba55fff --- /dev/null +++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/cloudstack_test.go @@ -0,0 +1,39 @@ +// 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 cloudstack_test + +import ( + _ "embed" + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" + + "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack" +) + +//go:embed testdata/metadata.json +var rawMetadata []byte + +//go:embed testdata/expected.yaml +var expectedNetworkConfig string + +func TestEmpty(t *testing.T) { + p := &cloudstack.Cloudstack{} + + var m cloudstack.MetadataConfig + + require.NoError(t, json.Unmarshal(rawMetadata, &m)) + + networkConfig, err := p.ParseMetadata(&m) + require.NoError(t, err) + + marshaled, err := yaml.Marshal(networkConfig) + require.NoError(t, err) + + assert.Equal(t, expectedNetworkConfig, string(marshaled)) +} diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/metadata.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/metadata.go new file mode 100644 index 0000000000..0d7d1f77f8 --- /dev/null +++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/metadata.go @@ -0,0 +1,76 @@ +// 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 cloudstack + +import ( + "context" + stderrors "errors" + "fmt" + + "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" + "github.com/siderolabs/talos/pkg/download" +) + +const ( + // CloudstackMetadataEndpoint is the local Cloudstack endpoint. + CloudstackMetadataEndpoint = "http://data-server./latest/meta-data" + // CloudstackUserDataEndpoint is the local Cloudstack endpoint for the config. + CloudstackUserDataEndpoint = "http://data-server./latest/user-data" +) + +// MetadataConfig represents a metadata Cloudstack instance. +type MetadataConfig struct { + Hostname string `json:"local-hostname,omitempty"` + InstanceID string `json:"instance-id,omitempty"` + InstanceType string `json:"service-offering,omitempty"` + PublicIPv4 string `json:"public-ipv4,omitempty"` + Zone string `json:"availability-zone,omitempty"` +} + +/* +local-ipv4 +public-hostname +vm-id +public-keys +cloud-identifier +hypervisor-host-name +*/ + +func (e *Cloudstack) getMetadata(ctx context.Context) (metadata *MetadataConfig, err error) { + getMetadataKey := func(key string) (string, error) { + res, metaerr := download.Download(ctx, fmt.Sprintf("%s/%s", CloudstackMetadataEndpoint, key), + download.WithErrorOnNotFound(errors.ErrNoConfigSource), + download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource)) + if metaerr != nil && !stderrors.Is(metaerr, errors.ErrNoConfigSource) { + return "", fmt.Errorf("failed to fetch %q from IMDS: %w", key, metaerr) + } + + return string(res), nil + } + + metadata = &MetadataConfig{} + + if metadata.Hostname, err = getMetadataKey("local-hostname"); err != nil { + return nil, err + } + + if metadata.InstanceType, err = getMetadataKey("service-offering"); err != nil { + return nil, err + } + + if metadata.InstanceID, err = getMetadataKey("instance-id"); err != nil { + return nil, err + } + + if metadata.PublicIPv4, err = getMetadataKey("public-ipv4"); err != nil { + return nil, err + } + + if metadata.Zone, err = getMetadataKey("availability-zone"); err != nil { + return nil, err + } + + return metadata, nil +} diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/testdata/expected.yaml b/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/testdata/expected.yaml new file mode 100644 index 0000000000..f284b2a291 --- /dev/null +++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/testdata/expected.yaml @@ -0,0 +1,18 @@ +addresses: [] +links: [] +routes: [] +hostnames: + - hostname: talos + domainname: fqdn + layer: platform +resolvers: [] +timeServers: [] +operators: [] +externalIPs: + - 1.2.3.4 +metadata: + platform: cloudstack + hostname: talos.fqdn + instanceType: standard.tiny + instanceId: 3fe6b28a-669e-4eb2-bffd-4180c572c410 + providerId: cloudstack://3fe6b28a-669e-4eb2-bffd-4180c572c410 diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/testdata/metadata.json b/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/testdata/metadata.json new file mode 100644 index 0000000000..a1f1b87589 --- /dev/null +++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/testdata/metadata.json @@ -0,0 +1,6 @@ +{ + "local-hostname": "talos.fqdn", + "instance-id": "3fe6b28a-669e-4eb2-bffd-4180c572c410", + "public-ipv4": "1.2.3.4", + "service-offering": "standard.tiny" +} \ No newline at end of file diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/platform.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/platform.go index 6870b46ef5..1a74eed1f5 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/platform/platform.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/platform.go @@ -18,6 +18,7 @@ import ( "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/akamai" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/aws" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/azure" + "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/container" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal" @@ -88,7 +89,7 @@ func NewPlatform(platform string) (p runtime.Platform, err error) { return newPlatform(platform) } -//nolint:gocyclo +//nolint:gocyclo,cyclop func newPlatform(platform string) (p runtime.Platform, err error) { switch platform { case "akamai": @@ -97,6 +98,8 @@ func newPlatform(platform string) (p runtime.Platform, err error) { return aws.NewAWS() case "azure": p = &azure.Azure{} + case "cloudstack": + p = &cloudstack.Cloudstack{} case "container": p = &container.Container{} case "digital-ocean": diff --git a/pkg/imager/profile/default.go b/pkg/imager/profile/default.go index 227347b2a8..b780ff3ef8 100644 --- a/pkg/imager/profile/default.go +++ b/pkg/imager/profile/default.go @@ -121,6 +121,18 @@ var Default = map[string]Profile{ }, }, }, + "cloudstack": { + Platform: "cloudstack", + SecureBoot: pointer.To(false), + Output: Output{ + Kind: OutKindImage, + OutFormat: OutFormatZSTD, + ImageOptions: &ImageOptions{ + DiskSize: DefaultRAWDiskSize, + DiskFormat: DiskFormatRaw, + }, + }, + }, "digital-ocean": { Platform: "digital-ocean", SecureBoot: pointer.To(false), diff --git a/website/content/v1.8/introduction/support-matrix.md b/website/content/v1.8/introduction/support-matrix.md index 0f003d5c5d..56e432b7aa 100644 --- a/website/content/v1.8/introduction/support-matrix.md +++ b/website/content/v1.8/introduction/support-matrix.md @@ -6,14 +6,14 @@ description: "Table of supported Talos Linux versions and respective platforms." | Talos Version | 1.8 | 1.7 | | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Release Date | 2024-08-15 (TBD) | 2024-04-19 (1.7.0) | +| Release Date | 2024-09-15 (TBD) | 2024-04-19 (1.7.0) | | End of Community Support | 1.9.0 release (2024-12-15, TBD) | 1.8.0 release (2024-08-15) | | Enterprise Support | [offered by Sidero Labs Inc.](https://www.siderolabs.com/support/) | [offered by Sidero Labs Inc.](https://www.siderolabs.com/support/) | | Kubernetes | 1.31, 1.30, 1.29, 1.28, 1.27, 1.26 | 1.30, 1.29, 1.28, 1.27, 1.26, 1.25 | | NVIDIA Drivers | 550.x.x (PRODUCTION), 535.x.x (LTS) | 535.x.x (LTS) | | Architecture | amd64, arm64 | amd64, arm64 | | **Platforms** | | | -| - cloud | Akamai, AWS, GCP, Azure, Digital Ocean, Exoscale, Hetzner, OpenNebula, OpenStack, Oracle Cloud, Scaleway, Vultr, Upcloud | Akamai, AWS, GCP, Azure, Digital Ocean, Exoscale, Hetzner, OpenNebula, OpenStack, Oracle Cloud, Scaleway, Vultr, Upcloud | +| - cloud | Akamai, AWS, GCP, Azure, CloudStack, Digital Ocean, Exoscale, Hetzner, OpenNebula, OpenStack, Oracle Cloud, Scaleway, Vultr, Upcloud | Akamai, AWS, GCP, Azure, Digital Ocean, Exoscale, Hetzner, OpenNebula, OpenStack, Oracle Cloud, Scaleway, Vultr, Upcloud | | - bare metal | x86: BIOS, UEFI, SecureBoot; arm64: UEFI, SecureBoot; boot: ISO, PXE, disk image | x86: BIOS, UEFI; arm64: UEFI; boot: ISO, PXE, disk image | | - virtualized | VMware, Hyper-V, KVM, Proxmox, Xen | VMware, Hyper-V, KVM, Proxmox, Xen | | - SBCs | Banana Pi M64, Jetson Nano, Libre Computer Board ALL-H3-CC, Nano Pi R4S, Pine64, Pine64 Rock64, Radxa ROCK Pi 4c, Radxa Rock4c+, Raspberry Pi 4B, Raspberry Pi Compute Module 4 | Banana Pi M64, Jetson Nano, Libre Computer Board ALL-H3-CC, Nano Pi R4S, Orange Pi R1 Plus LTS, Pine64, Pine64 Rock64, Radxa ROCK Pi 4c, Raspberry Pi 4B, Raspberry Pi Compute Module 4 | @@ -45,6 +45,7 @@ description: "Table of supported Talos Linux versions and respective platforms." ### Tier 3 * Akamai +* CloudStack * Exoscale * Hetzner * nocloud diff --git a/website/content/v1.8/talos-guides/install/cloud-platforms/cloudstack.md b/website/content/v1.8/talos-guides/install/cloud-platforms/cloudstack.md new file mode 100644 index 0000000000..c53c65f7b4 --- /dev/null +++ b/website/content/v1.8/talos-guides/install/cloud-platforms/cloudstack.md @@ -0,0 +1,228 @@ +--- +title: "CloudStack" +description: "Creating a cluster via the CLI (cmk) on Apache CloudStack." +--- + +## Creating a Talos Linux Cluster on Apache CloudStack via the CMK CLI + +In this guide we will create an single node Kubernetes cluster in Apache CloudStack. + +We assume Apache CloudStack is already running in a basic configuration - and some familiarity with Apache CloudStack. + +We will be using the [CloudStack Cloudmonkey](https://github.com/apache/cloudstack-cloudmonkey) CLI tool. + +Please see the [official Apache CloudStack documentation](https://docs.cloudstack.apache.org/en/latest/) for information related to Apache CloudStack. + +### Obtain the Talos Image + +Download the Talos CloudStack image `cloudstack-amd64.raw.gz` from the [Image Factory](https://factory.talos.dev). + +> Note: the minimum version of Talos required to support Apache CloudStack is v1.8.0. + +Using an upload method of your choice, upload the image to a Apache CloudStack. + +You might be able to use the "Register Template from URL" to download the image directly from the Image Factory. + +> Note: CloudStack does not seem to like compressed images, so you might have to download the image to a local webserver, uncompress it and let CloudStack fetch the image from there instead. +> Alternatively, you can try to remove `.gz` from URL to fetch an uncompressed image from the Image Factory. + +### Get Required Variables + +Next we will get a number of required variables and export them for later use: + +#### Get Image Template ID + +```bash +$ cmk list templates templatefilter=self | jq -r '.template[] | [.id, .name] | @tsv' | sort -k2 +01813d29-1253-4080-8d29-d405d94148af Talos 1.8.0 +... +$ export IMAGE_ID=01813d29-1253-4080-8d29-d405d94148af +``` + +#### Get Zone ID + +Get a list of Zones and select the relevant zone + +```bash +$ cmk list zones | jq -r '.zone[] | [.id, .name] | @tsv' | sort -k2 +a8c71a6f-2e09-41ed-8754-2d4dd8783920 fsn1 +9d38497b-d810-42ab-a772-e596994d21d2 fsn2 +... +$ export ZONE_ID=a8c71a6f-2e09-41ed-8754-2d4dd8783920 +``` + +#### Get Service Offering ID + +Get a list of service offerings (instance types) and select the desired offering + +```bash +$ cmk list serviceofferings | jq -r '.serviceoffering[] | [.id, .memory, .cpunumber, .name] | @tsv' | sort -k4 +82ac8c87-22ee-4ec3-8003-c80b09efe02c 2048 2 K8S-CP-S +c7f5253e-e1f1-4e33-a45e-eb2ebbc65fd4 4096 2 K8S-WRK-S +... +$ export SERVICEOFFERING_ID=82ac8c87-22ee-4ec3-8003-c80b09efe02c +``` + +#### Get Network ID + +Get a list of networks and select the relevant network for your cluster. + +```bash +$ cmk list networks zoneid=${ZONE_ID} | jq -r '.network[] | [.id, .type, .name] | @tsv' | sort -k3 +f706984f-9dd1-4cb8-9493-3fba1f0de7e3 Isolate demo +143ed8f1-3cc5-4ba2-8717-457ad993cf25 Isolated talos +... +$ export NETWORK_ID=143ed8f1-3cc5-4ba2-8717-457ad993cf25 +``` + +#### Get next free Public IP address and ID + +To create a loadbalancer for the K8S API Endpoint, find the next available public IP address in the zone. + +(In this test environment, the 10.0.0.0/24 RFC-1918 IP range has been configured as "Public IP addresses") + +```bash +$ cmk list publicipaddresses zoneid=${ZONE_ID} state=free forvirtualnetwork=true | jq -r '.publicipaddress[] | [.id, .ipaddress] | @tsv' | sort -k2 +1901d946-3797-48aa-a113-8fb730b0770a 10.0.0.102 +fa207d0e-c8f8-4f09-80f0-d45a6aac77eb 10.0.0.103 +aa397291-f5dc-4903-b299-277161b406cb 10.0.0.104 +... +$ export PUBLIC_IPADDRESS=10.0.0.102 +$ export PUBLIC_IPADDRESS_ID=1901d946-3797-48aa-a113-8fb730b0770a +``` + +#### Acquire and Associate Public IP Address + +Acquire and associate the public IP address with the network we selected earlier. + +```bash +$ cmk associateIpAddress ipaddress=${PUBLIC_IPADDRESS} networkid=${NETWORK_ID} +{ + "ipaddress": { + ..., + "ipaddress": "10.0.0.102", + ... + } +} +``` + +#### Create LB and FW rule using the Public IP Address + +Create a Loadbalancer for the K8S API Endpoint. + +> Note: The "create loadbalancerrule" also takes care of creating a corresponding firewallrule. + +```bash +$ cmk create loadbalancerrule algorithm=roundrobin name="k8s-api" privateport=6443 publicport=6443 openfirewall=true publicipid=${PUBLIC_IPADDRESS_ID} cidrlist=0.0.0.0/0 +{ + "loadbalancer": { + ... + "name": "k8s-api", + "networkid": "143ed8f1-3cc5-4ba2-8717-457ad993cf25", + "privateport": "6443", + "publicip": "10.0.0.102", + "publicipid": "1901d946-3797-48aa-a113-8fb730b0770a", + "publicport": "6443", + ... + } +} +``` + +### Create the Talos Configuration Files + +Finally it's time to generate the Talos configuration files, using the Public IP address assigned to the loadbalancer. + +```bash +$ talosctl gen config talos-cloudstack https://${PUBLIC_IPADDRESS}:6443 --with-docs=false --with-examples=false +created controlplane.yaml +created worker.yaml +created talosconfig +``` + +Make any adjustments to the `controlplane.yaml` and/or `worker.yaml` as you like. + +> Note: Remember to validate! + +#### Create Talos VM + +Next we will create the actual VM and supply the `controlplane.yaml` as base64 encoded `userdata`. + +```bash +$ cmk deploy virtualmachine zoneid=${ZONEID} templateid=${IMAGE_ID} serviceofferingid=${SERVICEOFFERING_ID} networkIds=${NETWORK_ID} name=talosdemo usersdata=$(base64 controlplane.yaml | tr -d '\n') +{ + "virtualmachine": { + "account": "admin", + "affinitygroup": [], + "cpunumber": 2, + "cpuspeed": 2000, + "cpuused": "0.3%", + ... + } +} +``` + +#### Get Talos VM ID and Internal IP address + +Get the ID of our newly created VM. +(Also available in the full output of the above command.) + +```bash +$ cmk list virtualmachines | jq -r '.virtualmachine[] | [.id, .ipaddress, .name]|@tsv' | sort -k3 +9c119627-cb38-4b64-876b-ca2b79820b5a 10.1.1.154 srv03 +545099fc-ec2d-4f32-915d-b0c821cfb634 10.1.1.97 srv04 +d37aeca4-7d1f-45cd-9a4d-97fdbf535aa1 10.1.1.243 talosdemo +$ export VM_ID=d37aeca4-7d1f-45cd-9a4d-97fdbf535aa1 +$ export VM_IP=10.1.1.243 +``` + +#### Get Load Balancer ID + +Obtain the ID of the `loadbalancerrule` we created earlier. + +```bash +$ cmk list loadbalancerrules | jq -r '.loadbalancerrule[]| [.id, .publicip, .name] | @tsv' | sort -k2 +ede6b711-b6bc-4ade-9e48-4b3f5aa59934 10.0.0.102 k8s-api +1bad3c46-96fa-4f50-a4fc-9a46a54bc350 10.0.0.197 ac0b5d98cf6a24d55a4fb2f9e240c473-tcp-443 +$ export LB_RULE_ID=ede6b711-b6bc-4ade-9e48-4b3f5aa59934 +``` + +#### Assign Talos VM to Load Balancer + +With the ID of the VM and the load balancer, we can assign the VM to the `loadbalancerrule`, making the K8S API endpoint available via the Load Balancer + +```bash +cmk assigntoloadbalancerrule id=${LB_RULE_ID} virtualmachineids=${VM_ID} +``` + +### Bootstrap Etcd + +Once the Talos VM has booted, it time to bootstrap etcd. + +Configure `talosctl` with IP addresses of the control plane node's IP address. + +Set the `endpoints` and `nodes`: + +```bash +talosctl --talosconfig talosconfig config endpoint ${VM_IP} +talosctl --talosconfig talosconfig config node ${VM_IP} +``` + +Next, bootstrap `etcd`: + +```bash +talosctl --talosconfig talosconfig bootstrap +``` + +### Retrieve the `kubeconfig` + +At this point we can retrieve the admin `kubeconfig` by running: + +```bash +talosctl --talosconfig talosconfig kubeconfig . +``` + +We can also watch the cluster bootstrap via: + +```bash +talosctl --talosconfig talosconfig dashboard +```