Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(config): add volumes field for VolumeConfig manifest #782

Merged
merged 2 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions docs/docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,23 @@ extensionServices:
<td markdown="1" align="center">:negative_squared_cross_mark:</td>
</tr>

<tr markdown="1">
<td markdown="1">`volumes`</td>
<td markdown="1">[][Volume](#volume)</td>
<td markdown="1">Machine volume configs specification.<details><summary>*Show example*</summary>
```yaml
volumes:
- name: EPHEMERAL
provisioning:
diskSelector:
match: disk.transport == "nvme"
maxSize: 50GiB
```
</summary></td>
<td markdown="1" align="center">`nil`</td>
<td markdown="1" align="center">:negative_squared_cross_mark:</td>
</tr>

<tr markdown="1">
<td markdown="1">`nodeLabels`</td>
<td markdown="1">map[string]string</td>
Expand Down Expand Up @@ -910,6 +927,44 @@ environment:

</table>

## Volume

`Volume` defines machine volume configuration for a node.

<table markdown="1">
<tr markdown="1">
<th markdown="1">Field</th><th>Type</th><th>Description</th><th>Default Value</th><th>Required</th>
</tr>

<tr markdown="1">
<td markdown="1">`name`</td>
<td markdown="1">`string`</td>
<td markdown="1">Name of the volume config.<details><summary>*Show example*</summary>
```yaml
name: EPHEMERAL
```
</details></td>
<td markdown="1" align="center">`nil`</td>
<td markdown="1" align="center">:white_check_mark:</td>
</tr>

<tr markdown="1">
<td markdown="1">`provisioning`</td>
<td markdown="1">[ProvisioningSpec](#provisioningspec)</td>
<td markdown="1">Provisioning spec of the volume config.<details><summary>*Show example*</summary>
```yaml
provisioning:
diskSelector:
match: disk.transport == "nvme"
maxSize: 50GiB
```
</details></td>
<td markdown="1" align="center">`nil`</td>
<td markdown="1" align="center">:white_check_mark:</td>
</tr>

</table>

## NetworkRule

`NetworkRule` defines the firewall rules to match.
Expand Down Expand Up @@ -1005,3 +1060,7 @@ ingress:
## ConfigFile

`ConfigFile` is type of upstream Talos <a href="https://www.talos.dev/v1.7/reference/configuration/extensions/extensionserviceconfig/#ExtensionServiceConfig.configFiles" target="_blank">`extensions.ConfigFile`</a>

## ProvisioningSpec

`ProvisioningSpec` is type of upstream Talos <a href="https://www.talos.dev/v1.9/reference/configuration/block/volumeconfig/#VolumeConfig.provisioning" target="_blank">`block.ProvisioningSpec`</a>
11 changes: 9 additions & 2 deletions example/talconfig.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ nodes:
mountPath: /usr/local/etc/nut/upsmon.conf
environment:
- UPS_NAME=ups
volumes:
- name: EPHEMERAL
provisioning:
diskSelector:
match: disk.transport == "nvme"
maxSize: 50GiB
ingressFirewall:
defaultAction: block
rules:
Expand Down Expand Up @@ -74,8 +80,9 @@ nodes:
installDiskSelector:
size: 4GB
model: WDC*
name: /sys/block/sda/device/name
busPath: /pci0000:00/0000:00:17.0/ata1/host0/target0:0:0/0:0:0:0
# TODO: broken since Talos 1.9 and I need to investigate
# name: /sys/block/sda/device/name
# busPath: /pci0000:00/0000:00:17.0/ata1/host0/target0:0:0/0:0:0:0
nodeLabels:
rack: rack1a
zone: us-east-1a
Expand Down
7 changes: 7 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package config

import (
"github.com/siderolabs/image-factory/pkg/schematic"
"github.com/siderolabs/talos/pkg/machinery/config/types/block"
"github.com/siderolabs/talos/pkg/machinery/config/types/network"
"github.com/siderolabs/talos/pkg/machinery/config/types/runtime/extensions"
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
Expand Down Expand Up @@ -59,6 +60,7 @@ type NodeConfigs struct {
MachineSpec MachineSpec `yaml:"machineSpec,omitempty" jsonschema:"description=Machine hardware specification"`
IngressFirewall *IngressFirewall `yaml:"ingressFirewall,omitempty" jsonschema:"description=Machine firewall specification"`
ExtensionServices []*ExtensionService `yaml:"extensionServices,omitempty" jsonschema:"description=Machine extension services specification"`
Volumes []*Volume `yaml:"volumes,omitempty" jsonschema:"description=Machine volume configs specification"`
}

type ImageFactory struct {
Expand Down Expand Up @@ -92,3 +94,8 @@ type ExtensionService struct {
ConfigFiles extensions.ConfigFileList `yaml:"configFiles,omitempty" jsonschema:"description=The config files for the extension service"`
Environment []string `yaml:"environment,omitempty" jsonschema:"description=The environment for the extension service"`
}

type Volume struct {
Name string `yaml:"name" jsonschema:"description=Name of the volume config"`
Provisioning block.ProvisioningSpec `yaml:"provisioning" jsonschema:"description=Provisioning spec of the volume config"`
}
9 changes: 9 additions & 0 deletions pkg/generate/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ func GenerateConfig(c *config.TalhelperConfig, dryRun bool, outDir, secretFile,
cfg = append(cfg, ext...)
}

if len(node.Volumes) > 0 {
slog.Debug(fmt.Sprintf("generating volume config for %s", node.Hostname))
vc, err := talos.GenerateVolumeConfigBytes(node.Volumes, mode)
if err != nil {
return err
}
cfg = append(cfg, vc...)
}

if len(node.ExtraManifests) > 0 {
slog.Debug(fmt.Sprintf("generating extra manifests for %s", node.Hostname))
content, err := combineExtraManifests(node.ExtraManifests)
Expand Down
60 changes: 60 additions & 0 deletions pkg/talos/volumeconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package talos

import (
"fmt"
"slices"

"github.com/budimanjojo/talhelper/v3/pkg/config"
"github.com/siderolabs/talos/pkg/machinery/config/types/block"
)

func GenerateVolumeConfigBytes(cfgs []*config.Volume, mode string) ([]byte, error) {
var result [][]byte

vcs, err := GenerateVolumeConfig(cfgs, mode)
if err != nil {
return nil, err
}

for _, vc := range vcs {
vcByte, err := marshalYaml(vc)
if err != nil {
return nil, err
}

result = append(result, vcByte)
}

return CombineYamlBytes(result), nil
}

func GenerateVolumeConfig(cfgs []*config.Volume, mode string) ([]*block.VolumeConfigV1Alpha1, error) {
var (
// I suppose we shouldn't allow same volume names?
names []string
result []*block.VolumeConfigV1Alpha1
)

m, err := parseMode(mode)
if err != nil {
return nil, err
}

for _, v := range cfgs {
if slices.Index(names, v.Name) != -1 {
return nil, fmt.Errorf("duplicated volume config name found: %s", v.Name)
}
names = append(names, v.Name)
vc := block.NewVolumeConfigV1Alpha1()
vc.MetaName = v.Name
vc.ProvisioningSpec = v.Provisioning

if _, err := vc.Validate(m); err != nil {
return nil, err
}

result = append(result, vc)
}

return result, nil
}
54 changes: 54 additions & 0 deletions pkg/talos/volumeconfig_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package talos

import (
"testing"

"github.com/budimanjojo/talhelper/v3/pkg/config"
"github.com/siderolabs/talos/pkg/machinery/cel"
"github.com/siderolabs/talos/pkg/machinery/cel/celenv"
"github.com/siderolabs/talos/pkg/machinery/config/types/block"
"gopkg.in/yaml.v3"
)

func TestGenerateNodeVolumeConfig(t *testing.T) {
data := []byte(`nodes:
- hostname: node1
volumes:
- name: EPHEMERAL
provisioning:
diskSelector:
match: disk.transport == "nvme"
maxSize: 50GiB
- name: IMAGECACHE
provisioning:
diskSelector:
match: disk.size > 120u * GB && disk.size < 1u * TB`)
var m config.TalhelperConfig
if err := yaml.Unmarshal(data, &m); err != nil {
t.Fatal(err)
}

expectedVolume1Name := "EPHEMERAL"
expectedVolume1Provisioning := block.ProvisioningSpec{
DiskSelectorSpec: block.DiskSelector{
Match: cel.MustExpression(cel.ParseBooleanExpression(`disk.transport == "nvme"`, celenv.DiskLocator())),
},
ProvisioningMaxSize: block.MustByteSize("50GiB"),
}
expectedVolume2Name := "IMAGECACHE"
expectedVolume2Provisioning := block.ProvisioningSpec{
DiskSelectorSpec: block.DiskSelector{
Match: cel.MustExpression(cel.ParseBooleanExpression(`disk.size > 120u * GB && disk.size < 1u * TB`, celenv.DiskLocator())),
},
}

result, err := GenerateVolumeConfig(m.Nodes[0].Volumes, "metal")
if err != nil {
t.Fatal(err)
}

compare(result[0].Name(), expectedVolume1Name, t)
compare(result[0].ProvisioningSpec, expectedVolume1Provisioning, t)
compare(result[1].Name(), expectedVolume2Name, t)
compare(result[1].ProvisioningSpec, expectedVolume2Provisioning, t)
}
Loading