feat: add oraclecloud.com platform support

* cloud-init for oraclecloud (IMDSv2)
* amd64/arm64 arch
* set DHCPv6 on if IPv6 subnet allocated

Signed-off-by: Serge Logvinov <serge.logvinov@sinextra.dev>
Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
This commit is contained in:
Serge Logvinov 2021-12-17 06:18:32 +00:00 committed by Andrey Smirnov
parent f3ec24bebf
commit 2869b5eeac
No known key found for this signature in database
GPG Key ID: 7B26396447AB6DFD
9 changed files with 358 additions and 3 deletions

View File

@ -556,6 +556,8 @@ local release = {
'_out/nocloud-arm64.raw.xz',
'_out/openstack-amd64.tar.gz',
'_out/openstack-arm64.tar.gz',
'_out/oracle-amd64.qcow2.xz',
'_out/oracle-arm64.qcow2.xz',
'_out/scaleway-amd64.raw.xz',
'_out/scaleway-arm64.raw.xz',
'_out/talos-amd64.iso',

View File

@ -228,7 +228,7 @@ image-%: ## Builds the specified image. Valid options are aws, azure, digital-oc
images-essential: image-aws image-gcp image-metal ## Builds only essential images used in the CI (AWS, GCP, and Metal).
images: image-aws image-azure image-digital-ocean image-gcp image-hcloud image-metal image-nocloud image-openstack image-scaleway image-upcloud image-vmware image-vultr ## Builds all known images (AWS, Azure, DigitalOcean, GCP, HCloud, Metal, NoCloud, Openstack, Scaleway, UpCloud, Vultr and VMware).
images: image-aws image-azure image-digital-ocean image-gcp image-hcloud image-metal image-nocloud image-openstack image-oracle image-scaleway image-upcloud image-vmware image-vultr ## Builds all known images (AWS, Azure, DigitalOcean, GCP, HCloud, Metal, NoCloud, Openstack, Oracle, 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)/imager:$(IMAGE_TAG)

View File

@ -84,7 +84,7 @@ func runImageCmd() (err error) {
if options.ConfigSource == "" {
switch p.Name() {
case "aws", "azure", "digital-ocean", "gcp", "hcloud", "nocloud", "scaleway", "upcloud", "vultr":
case "aws", "azure", "digital-ocean", "gcp", "hcloud", "nocloud", "oracle", "scaleway", "upcloud", "vultr":
options.ConfigSource = constants.ConfigNone
case "vmware":
options.ConfigSource = constants.ConfigGuestInfo
@ -169,6 +169,19 @@ func finalize(platform runtime.Platform, img, arch string) (err error) {
if err = tar(fmt.Sprintf("openstack-%s.tar.gz", arch), file, dir); err != nil {
return err
}
case "oracle":
name = fmt.Sprintf("oracle-%s.qcow2", arch)
file = filepath.Join(outputArg, name)
if err = qemuimg.Convert("raw", "qcow2", "cluster_size=8k", img, file); err != nil {
return err
}
log.Println("compressing image")
if err = xz(file); err != nil {
return err
}
case "scaleway":
file = filepath.Join(outputArg, fmt.Sprintf("scaleway-%s.raw", arch))

View File

@ -52,6 +52,13 @@ Talos now preserves machine configuration as it was submitted to the node.
There is some work still going on various cloud platforms to stop modifying machine configuration on the fly.
"""
[notes.platforms]
title = "Platform Support"
description="""\
Talos now supports Oracle Cloud.
"""
[make_deps]
[make_deps.tools]

View File

@ -0,0 +1,164 @@
// 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 oracle
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"log"
"net"
"github.com/AlekSi/pointer"
"github.com/talos-systems/go-procfs/procfs"
"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"
)
// Ref: https://docs.oracle.com/en-us/iaas/Content/Compute/Tasks/gettingmetadata.htm
const (
// OracleHostnameEndpoint is the local metadata endpoint for the hostname.
OracleHostnameEndpoint = "http://169.254.169.254/opc/v2/instance/hostname"
// OracleUserDataEndpoint is the local metadata endpoint inside of Oracle Cloud.
OracleUserDataEndpoint = "http://169.254.169.254/opc/v2/instance/metadata/user_data"
// OracleNetworkEndpoint is the local network metadata endpoint inside of Oracle Cloud.
OracleNetworkEndpoint = "http://169.254.169.254/opc/v2/vnics/"
)
// NetworkConfig holds network interface meta config.
type NetworkConfig struct {
HWAddr string `json:"macAddr"`
PrivateIP string `json:"privateIp"`
VirtualRouterIP string `json:"virtualRouterIp"`
SubnetCidrBlock string `json:"subnetCidrBlock"`
Ipv6SubnetCidrBlock string `json:"ipv6SubnetCidrBlock,omitempty"`
Ipv6VirtualRouterIP string `json:"ipv6VirtualRouterIp,omitempty"`
}
// Oracle is the concrete type that implements the platform.Platform interface.
type Oracle struct{}
// Name implements the platform.Platform interface.
func (o *Oracle) Name() string {
return "oracle"
}
// ConfigurationNetwork implements the network configuration interface.
func (o *Oracle) ConfigurationNetwork(metadataNetworkConfig []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")
}
if machineConfig.MachineConfig == nil {
machineConfig.MachineConfig = &v1alpha1.MachineConfig{}
}
if machineConfig.MachineConfig.MachineNetwork == nil {
machineConfig.MachineConfig.MachineNetwork = &v1alpha1.NetworkConfig{}
}
var interfaceAddresses []NetworkConfig
if err := json.Unmarshal(metadataNetworkConfig, &interfaceAddresses); err != nil {
return nil, err
}
if machineConfig.MachineConfig.MachineNetwork.NetworkInterfaces == nil {
for idx, iface := range interfaceAddresses {
ipv6 := iface.Ipv6SubnetCidrBlock != "" && iface.Ipv6VirtualRouterIP != ""
if ipv6 {
device := &v1alpha1.Device{
DeviceInterface: fmt.Sprintf("eth%d", idx),
DeviceDHCP: true,
DeviceDHCPOptions: &v1alpha1.DHCPOptions{DHCPIPv6: pointer.ToBool(true)},
}
machineConfig.MachineConfig.MachineNetwork.NetworkInterfaces = append(machineConfig.MachineConfig.MachineNetwork.NetworkInterfaces, device)
}
}
}
return confProvider, nil
}
// Configuration implements the platform.Platform interface.
func (o *Oracle) Configuration(ctx context.Context) ([]byte, error) {
log.Printf("fetching network config from %q", OracleNetworkEndpoint)
metadataNetworkConfig, err := download.Download(ctx, OracleNetworkEndpoint,
download.WithHeaders(map[string]string{"Authorization": "Bearer Oracle"}))
if err != nil {
return nil, fmt.Errorf("failed to fetch network config from metadata service")
}
log.Printf("fetching machine config from: %q", OracleUserDataEndpoint)
machineConfigDl, err := download.Download(ctx, OracleUserDataEndpoint,
download.WithHeaders(map[string]string{"Authorization": "Bearer Oracle"}),
download.WithErrorOnNotFound(errors.ErrNoConfigSource),
download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource))
if err != nil {
return nil, err
}
machineConfig, err := base64.StdEncoding.DecodeString(string(machineConfigDl))
if err != nil {
return nil, errors.ErrNoConfigSource
}
confProvider, err := configloader.NewFromBytes(machineConfig)
if err != nil {
return nil, fmt.Errorf("error parsing machine config: %w", err)
}
confProvider, err = o.ConfigurationNetwork(metadataNetworkConfig, confProvider)
if err != nil {
return nil, err
}
return confProvider.Bytes()
}
// Hostname implements the platform.Platform interface.
func (o *Oracle) Hostname(ctx context.Context) (hostname []byte, err error) {
log.Printf("fetching hostname from: %q", OracleHostnameEndpoint)
hostname, err = download.Download(ctx, OracleHostnameEndpoint,
download.WithHeaders(map[string]string{"Authorization": "Bearer Oracle"}),
download.WithErrorOnNotFound(errors.ErrNoHostname),
download.WithErrorOnEmptyResponse(errors.ErrNoHostname))
if err != nil {
return nil, err
}
return hostname, nil
}
// Mode implements the platform.Platform interface.
func (o *Oracle) Mode() runtime.Mode {
return runtime.ModeCloud
}
// ExternalIPs implements the runtime.Platform interface.
func (o *Oracle) ExternalIPs(ctx context.Context) (addrs []net.IP, err error) {
return nil, nil
}
// KernelArgs implements the runtime.Platform interface.
func (o *Oracle) KernelArgs() procfs.Parameters {
return []*procfs.Parameter{
procfs.NewParameter("console").Append("tty1").Append("ttyS0"),
}
}

View File

@ -0,0 +1,60 @@
// 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 oracle_test
import (
"testing"
"github.com/AlekSi/pointer"
"github.com/stretchr/testify/suite"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1"
)
type ConfigSuite struct {
suite.Suite
}
func (suite *ConfigSuite) TestNetworkConfig() {
cfg := []byte(`
[ {
"vnicId" : "ocid1.vnic.oc1.eu-amsterdam-1.asdasd",
"privateIp" : "172.16.1.11",
"vlanTag" : 1,
"macAddr" : "02:00:17:00:00:00",
"virtualRouterIp" : "172.16.1.1",
"subnetCidrBlock" : "172.16.1.0/24",
"ipv6SubnetCidrBlock" : "2603:a:b:c::/64",
"ipv6VirtualRouterIp" : "fe80::a:b:c:d"
} ]
`)
a := &oracle.Oracle{}
defaultMachineConfig := &v1alpha1.Config{}
machineConfig := &v1alpha1.Config{
MachineConfig: &v1alpha1.MachineConfig{
MachineNetwork: &v1alpha1.NetworkConfig{
NetworkInterfaces: []*v1alpha1.Device{
{
DeviceInterface: "eth0",
DeviceDHCP: true,
DeviceDHCPOptions: &v1alpha1.DHCPOptions{DHCPIPv6: pointer.ToBool(true)},
},
},
},
},
}
result, err := a.ConfigurationNetwork(cfg, defaultMachineConfig)
suite.Require().NoError(err)
suite.Assert().Equal(machineConfig, result)
}
func TestConfigSuite(t *testing.T) {
suite.Run(t, new(ConfigSuite))
}

View File

@ -21,6 +21,7 @@ import (
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/metal"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/packet"
"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"
@ -72,6 +73,8 @@ func newPlatform(platform string) (p runtime.Platform, err error) {
p = &metal.Metal{}
case "openstack":
p = &openstack.Openstack{}
case "oracle":
p = &oracle.Oracle{}
case "nocloud":
p = &nocloud.Nocloud{}
case "packet":

View File

@ -0,0 +1,105 @@
---
title: "Oracle"
description: "Creating a cluster via the CLI (oci) on OracleCloud.com."
---
## Upload image
Oracle Cloud at the moment does not have a Talos official image.
So you can use [Bring Your Own Image (BYOI)](https://docs.oracle.com/en-us/iaas/Content/Compute/References/bringyourownimage.htm) approach.
Once the image is uploaded, set the ```Boot volume type``` to ``Paravirtualized`` mode.
OracleCloud has highly available NTP service, it can be enabled in Talos machine config with:
```yaml
machine:
time:
servers:
- 169.254.169.254
```
## Creating a Cluster via the CLI
```bash
```
### Create a Load Balancer
Create a load balancer by issuing the commands shown below.
Save the IP/DNS name, as this info will be used in the next step.
```bash
```
### Create the Machine Configuration Files
#### Generating Base Configurations
Using the IP/DNS name of the loadbalancer created earlier, generate the base configuration files for the Talos machines by issuing:
```bash
$ talosctl gen config talos-k8s-oracle-tutorial https://<load balancer IP or DNS>:6443
created controlplane.yaml
created worker.yaml
created talosconfig
```
At this point, you can modify the generated configs to your liking.
Optionally, you can specify `--config-patch` with RFC6902 jsonpatches which will be applied during the config generation.
#### Validate the Configuration Files
Validate any edited machine configs with:
```bash
$ talosctl validate --config controlplane.yaml --mode cloud
controlplane.yaml is valid for cloud mode
$ talosctl validate --config worker.yaml --mode cloud
worker.yaml is valid for cloud mode
```
### Create the Servers
#### Create the Control Plane Nodes
Create the control plane nodes with:
```bash
```
#### Create the Worker Nodes
Create the worker nodes with the following command, repeating (and incrementing the name counter) as many times as desired.
```bash
```
### Bootstrap Etcd
To configure `talosctl` we will need the first control plane node's IP.
This can be found by issuing:
```bash
```
Set the `endpoints` and `nodes` for your talosconfig with:
```bash
talosctl --talosconfig talosconfig config endpoint <control-plane-1-IP>
talosctl --talosconfig talosconfig config node <control-plane-1-IP>
```
Bootstrap `etcd` on the first control plane node with:
```bash
talosctl --talosconfig talosconfig bootstrap
```
### Retrieve the `kubeconfig`
At this point we can retrieve the admin `kubeconfig` by running:
```bash
talosctl --talosconfig talosconfig kubeconfig .
```

View File

@ -11,7 +11,7 @@ weight: 6
| Kubernetes | 1.23, 1.22, 1.21 | 1.23, 1.22, 1.21 |
| Architecture | amd64, arm64 |
| **Platforms** | | |
| - cloud | AWS, GCP, Azure, Digital Ocean, Hetzner, OpenStack, Scaleway, Vultr, Upcloud |
| - cloud | AWS, GCP, Azure, Digital Ocean, Hetzner, OpenStack, Oracle Cloud, Scaleway, Vultr, Upcloud | AWS, GCP, Azure, Digital Ocean, Hetzner, OpenStack, Scaleway, Vultr, Upcloud |
| - bare metal | x86: BIOS, UEFI; arm64: UEFI; boot: ISO, PXE, disk image |
| - virtualized | VMware, Hyper-V, KVM, Proxmox, Xen |
| - SBCs | Raspberry Pi4, Banana Pi M64, Pine64, and other |
@ -46,6 +46,7 @@ Tier 3: Not tested by core Talos team, community tested.
* Hetzner
* nocloud
* Oracle Cloud
* Scaleway
* Vultr
* Upcloud