mirror of
https://github.com/outbackdingo/talos-cloud-controller-manager.git
synced 2026-01-28 10:20:23 +00:00
feat: add system information for transformer
Add SystemInformation resource values to transformer templater. Signed-off-by: Serge Logvinov <serge.logvinov@sinextra.dev>
This commit is contained in:
@@ -114,6 +114,10 @@ transformations:
|
|||||||
platformMetadata:
|
platformMetadata:
|
||||||
Region: "{{ .Region }}-on-metal"
|
Region: "{{ .Region }}-on-metal"
|
||||||
Zone: "us-west-1f"
|
Zone: "us-west-1f"
|
||||||
|
# SKUNumber is a system information variable "t2.micro"
|
||||||
|
InstanceType: "{{ .SKUNumber }}"
|
||||||
|
# UUID is a system information variable "e8e8c388-5812-4db0-87e2-ad1fee51a1c1"
|
||||||
|
ProviderID: "someproviderID:///{{ .UUID }}"
|
||||||
|
|
||||||
# Features flags for nodes that match the transformation
|
# Features flags for nodes that match the transformation
|
||||||
features:
|
features:
|
||||||
@@ -182,7 +186,32 @@ type PlatformMetadataSpec struct {
|
|||||||
You can use the following command to get the platform metadata:
|
You can use the following command to get the platform metadata:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
talosctl get PlatformMetadatas.talos.dev -oyaml
|
talosctl get PlatformMetadatas -oyaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### System information variables
|
||||||
|
|
||||||
|
Additionally you can use the system information variables in the transformations rules.
|
||||||
|
|
||||||
|
Go struct for system information,
|
||||||
|
original code: [system_information.go](https://github.com/siderolabs/talos/blob/main/pkg/machinery/resources/hardware/system_information.go)
|
||||||
|
|
||||||
|
```go
|
||||||
|
type SystemInformationSpec struct {
|
||||||
|
Manufacturer string `yaml:"manufacturer,omitempty" protobuf:"1"`
|
||||||
|
ProductName string `yaml:"productName,omitempty" protobuf:"2"`
|
||||||
|
Version string `yaml:"version,omitempty" protobuf:"3"`
|
||||||
|
SerialNumber string `yaml:"serialnumber,omitempty" protobuf:"4"`
|
||||||
|
UUID string `yaml:"uuid,omitempty" protobuf:"5"`
|
||||||
|
WakeUpType string `yaml:"wakeUpType,omitempty" protobuf:"6"`
|
||||||
|
SKUNumber string `yaml:"skuNumber,omitempty" protobuf:"7"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can use the following command to get the system information:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
talosctl get SystemInformation -oyaml
|
||||||
```
|
```
|
||||||
|
|
||||||
### Transformations functions
|
### Transformations functions
|
||||||
@@ -277,3 +306,11 @@ You can use the following functions in the Go template:
|
|||||||
```yaml
|
```yaml
|
||||||
{{ b64dec "aGVsbG8=" }} -> hello
|
{{ b64dec "aGVsbG8=" }} -> hello
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### String slice functions
|
||||||
|
|
||||||
|
* `getValue` - the function to get the value from the map by key.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
{{ getValue "ds=nocloud;i=1234" "i" }} -> 1234
|
||||||
|
```
|
||||||
|
|||||||
@@ -16,3 +16,14 @@ transformations:
|
|||||||
node-role.kubernetes.io/web: ""
|
node-role.kubernetes.io/web: ""
|
||||||
taints:
|
taints:
|
||||||
node.cloudprovider.kubernetes.io/storage-type: "NoSchedule"
|
node.cloudprovider.kubernetes.io/storage-type: "NoSchedule"
|
||||||
|
|
||||||
|
- name: nocloud
|
||||||
|
nodeSelector:
|
||||||
|
- matchExpressions:
|
||||||
|
- key: platform
|
||||||
|
operator: In
|
||||||
|
values:
|
||||||
|
- nocloud
|
||||||
|
platformMetadata:
|
||||||
|
InstanceType: "{{ .SKUNumber }}"
|
||||||
|
ProviderID: proxmox://region-1/{{ getValue .SerialNumber "i" }}
|
||||||
|
|||||||
@@ -357,7 +357,7 @@ func TestSyncNodeLabels(t *testing.T) {
|
|||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
nodeSpec, err := transformer.TransformNode(client.config.Transformations, tt.meta)
|
nodeSpec, err := transformer.TransformNode(client.config.Transformations, tt.meta, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
labels := setTalosNodeLabels(client, tt.meta)
|
labels := setTalosNodeLabels(client, tt.meta)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/siderolabs/talos-cloud-controller-manager/pkg/transformer"
|
"github.com/siderolabs/talos-cloud-controller-manager/pkg/transformer"
|
||||||
"github.com/siderolabs/talos-cloud-controller-manager/pkg/utils/net"
|
"github.com/siderolabs/talos-cloud-controller-manager/pkg/utils/net"
|
||||||
"github.com/siderolabs/talos-cloud-controller-manager/pkg/utils/platform"
|
"github.com/siderolabs/talos-cloud-controller-manager/pkg/utils/platform"
|
||||||
|
"github.com/siderolabs/talos/pkg/machinery/resources/hardware"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/resources/runtime"
|
"github.com/siderolabs/talos/pkg/machinery/resources/runtime"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
@@ -138,9 +139,20 @@ func (i *instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloud
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mct := metrics.NewMetricContext("metadata")
|
var sysInfo *hardware.SystemInformationSpec
|
||||||
|
|
||||||
nodeSpec, err := transformer.TransformNode(i.c.config.Transformations, meta)
|
if len(i.c.config.Transformations) > 0 {
|
||||||
|
msys := metrics.NewMetricContext(hardware.SystemInformationID)
|
||||||
|
|
||||||
|
sysInfo, err = i.c.talos.GetNodeSystemInfo(ctx, nodeIP)
|
||||||
|
if msys.ObserveRequest(err) != nil {
|
||||||
|
return nil, fmt.Errorf("error getting system info from the node %s: %w", node.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mct := metrics.NewMetricContext("transformer")
|
||||||
|
|
||||||
|
nodeSpec, err := transformer.TransformNode(i.c.config.Transformations, meta, sysInfo)
|
||||||
if mct.ObserveTransformer(err) != nil {
|
if mct.ObserveTransformer(err) != nil {
|
||||||
return nil, fmt.Errorf("error transforming node: %w", err)
|
return nil, fmt.Errorf("error transforming node: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import (
|
|||||||
talos "github.com/siderolabs/talos/pkg/machinery/client"
|
talos "github.com/siderolabs/talos/pkg/machinery/client"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
|
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
|
||||||
|
"github.com/siderolabs/talos/pkg/machinery/resources/hardware"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/resources/k8s"
|
"github.com/siderolabs/talos/pkg/machinery/resources/k8s"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/resources/network"
|
"github.com/siderolabs/talos/pkg/machinery/resources/network"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/resources/runtime"
|
"github.com/siderolabs/talos/pkg/machinery/resources/runtime"
|
||||||
@@ -133,6 +134,8 @@ func (c *Client) GetNodeIfaces(ctx context.Context, nodeIP string) ([]network.Ad
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetNodeMetadata returns the metadata of the node.
|
// GetNodeMetadata returns the metadata of the node.
|
||||||
|
//
|
||||||
|
//nolint:dupl
|
||||||
func (c *Client) GetNodeMetadata(ctx context.Context, nodeIP string) (*runtime.PlatformMetadataSpec, error) {
|
func (c *Client) GetNodeMetadata(ctx context.Context, nodeIP string) (*runtime.PlatformMetadataSpec, error) {
|
||||||
nodeCtx := talos.WithNode(ctx, nodeIP)
|
nodeCtx := talos.WithNode(ctx, nodeIP)
|
||||||
|
|
||||||
@@ -162,6 +165,38 @@ func (c *Client) GetNodeMetadata(ctx context.Context, nodeIP string) (*runtime.P
|
|||||||
return &meta, nil
|
return &meta, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetNodeSystemInfo returns the system information of the node.
|
||||||
|
//
|
||||||
|
//nolint:dupl
|
||||||
|
func (c *Client) GetNodeSystemInfo(ctx context.Context, nodeIP string) (*hardware.SystemInformationSpec, error) {
|
||||||
|
nodeCtx := talos.WithNode(ctx, nodeIP)
|
||||||
|
|
||||||
|
var resources resource.Resource
|
||||||
|
|
||||||
|
err := retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(func() error {
|
||||||
|
var getErr error
|
||||||
|
|
||||||
|
resources, getErr = c.talos.COSI.Get(nodeCtx, resource.NewMetadata(hardware.NamespaceName, hardware.SystemInformationType, hardware.SystemInformationID, resource.VersionUndefined))
|
||||||
|
if getErr != nil {
|
||||||
|
err := c.refreshTalosClient(ctx) //nolint:errcheck
|
||||||
|
if err != nil {
|
||||||
|
return retry.ExpectedError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return getErr
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error get resources: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
meta := resources.Spec().(*hardware.SystemInformationSpec).DeepCopy() //nolint:errcheck
|
||||||
|
|
||||||
|
return &meta, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetClusterName returns cluster name.
|
// GetClusterName returns cluster name.
|
||||||
func (c *Client) GetClusterName() string {
|
func (c *Client) GetClusterName() string {
|
||||||
return c.talos.GetClusterName()
|
return c.talos.GetClusterName()
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ var genericMap = map[string]interface{}{
|
|||||||
// Encoding functions:
|
// Encoding functions:
|
||||||
"b64enc": base64encode,
|
"b64enc": base64encode,
|
||||||
"b64dec": base64decode,
|
"b64dec": base64decode,
|
||||||
|
|
||||||
|
// String slice functions:
|
||||||
|
"getValue": getValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenericFuncMap returns a copy of the basic function map as a map[string]interface{}.
|
// GenericFuncMap returns a copy of the basic function map as a map[string]interface{}.
|
||||||
@@ -83,3 +86,15 @@ func base64decode(v string) (string, error) {
|
|||||||
|
|
||||||
return string(data), nil
|
return string(data), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getValue(source string, key string) string {
|
||||||
|
parts := strings.Split(source, ";")
|
||||||
|
for _, part := range parts {
|
||||||
|
kv := strings.Split(part, "=")
|
||||||
|
if kv[0] == key {
|
||||||
|
return kv[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/siderolabs/talos-cloud-controller-manager/pkg/nodeselector"
|
"github.com/siderolabs/talos-cloud-controller-manager/pkg/nodeselector"
|
||||||
|
"github.com/siderolabs/talos/pkg/machinery/resources/hardware"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/resources/runtime"
|
"github.com/siderolabs/talos/pkg/machinery/resources/runtime"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
@@ -36,6 +37,11 @@ type NodeSpec struct {
|
|||||||
Features NodeFeaturesFlagSpec
|
Features NodeFeaturesFlagSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type nodeTransformationValues struct {
|
||||||
|
runtime.PlatformMetadataSpec
|
||||||
|
hardware.SystemInformationSpec
|
||||||
|
}
|
||||||
|
|
||||||
// NodeFeaturesFlagSpec represents the node features flags.
|
// NodeFeaturesFlagSpec represents the node features flags.
|
||||||
type NodeFeaturesFlagSpec struct {
|
type NodeFeaturesFlagSpec struct {
|
||||||
// PublicIPDiscovery try to find public IP on the node
|
// PublicIPDiscovery try to find public IP on the node
|
||||||
@@ -47,7 +53,7 @@ var prohibitedPlatformMetadataKeys = []string{"hostname", "platform"}
|
|||||||
// TransformNode transforms the node metadata based on the node transformation rules.
|
// TransformNode transforms the node metadata based on the node transformation rules.
|
||||||
//
|
//
|
||||||
//nolint:gocyclo,cyclop
|
//nolint:gocyclo,cyclop
|
||||||
func TransformNode(terms []NodeTerm, platformMetadata *runtime.PlatformMetadataSpec) (*NodeSpec, error) {
|
func TransformNode(terms []NodeTerm, platformMetadata *runtime.PlatformMetadataSpec, sysinfo *hardware.SystemInformationSpec) (*NodeSpec, error) {
|
||||||
node := &NodeSpec{
|
node := &NodeSpec{
|
||||||
Annotations: make(map[string]string),
|
Annotations: make(map[string]string),
|
||||||
Labels: make(map[string]string),
|
Labels: make(map[string]string),
|
||||||
@@ -58,7 +64,12 @@ func TransformNode(terms []NodeTerm, platformMetadata *runtime.PlatformMetadataS
|
|||||||
return node, nil
|
return node, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata := metadataFromStruct(platformMetadata)
|
values := nodeTransformationValues{PlatformMetadataSpec: *platformMetadata}
|
||||||
|
if sysinfo != nil {
|
||||||
|
values.SystemInformationSpec = *sysinfo
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata := mapFromStruct(platformMetadata)
|
||||||
|
|
||||||
for _, term := range terms {
|
for _, term := range terms {
|
||||||
match, err := nodeselector.Match(term.NodeSelector, metadata)
|
match, err := nodeselector.Match(term.NodeSelector, metadata)
|
||||||
@@ -69,7 +80,7 @@ func TransformNode(terms []NodeTerm, platformMetadata *runtime.PlatformMetadataS
|
|||||||
if match {
|
if match {
|
||||||
if term.Annotations != nil {
|
if term.Annotations != nil {
|
||||||
for k, v := range term.Annotations {
|
for k, v := range term.Annotations {
|
||||||
t, err := executeTemplate(v, platformMetadata)
|
t, err := executeTemplate(v, values)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to transformer annotation %q: %w", k, err)
|
return nil, fmt.Errorf("failed to transformer annotation %q: %w", k, err)
|
||||||
}
|
}
|
||||||
@@ -84,7 +95,7 @@ func TransformNode(terms []NodeTerm, platformMetadata *runtime.PlatformMetadataS
|
|||||||
|
|
||||||
if term.Labels != nil {
|
if term.Labels != nil {
|
||||||
for k, v := range term.Labels {
|
for k, v := range term.Labels {
|
||||||
t, err := executeTemplate(v, platformMetadata)
|
t, err := executeTemplate(v, values)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to transformer label %q: %w", k, err)
|
return nil, fmt.Errorf("failed to transformer label %q: %w", k, err)
|
||||||
}
|
}
|
||||||
@@ -103,7 +114,7 @@ func TransformNode(terms []NodeTerm, platformMetadata *runtime.PlatformMetadataS
|
|||||||
|
|
||||||
if term.Taints != nil {
|
if term.Taints != nil {
|
||||||
for k, v := range term.Taints {
|
for k, v := range term.Taints {
|
||||||
t, err := executeTemplate(v, platformMetadata)
|
t, err := executeTemplate(v, values)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to transformer label %q: %w", k, err)
|
return nil, fmt.Errorf("failed to transformer label %q: %w", k, err)
|
||||||
}
|
}
|
||||||
@@ -129,7 +140,7 @@ func TransformNode(terms []NodeTerm, platformMetadata *runtime.PlatformMetadataS
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := executeTemplate(v, platformMetadata)
|
t, err := executeTemplate(v, values)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to transformer platform metadata %q: %w", k, err)
|
return nil, fmt.Errorf("failed to transformer platform metadata %q: %w", k, err)
|
||||||
}
|
}
|
||||||
@@ -170,7 +181,7 @@ func executeTemplate(tmpl string, data interface{}) (string, error) {
|
|||||||
return buf.String(), nil
|
return buf.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func metadataFromStruct(in *runtime.PlatformMetadataSpec) map[string]string {
|
func mapFromStruct(in interface{}) map[string]string {
|
||||||
if in == nil {
|
if in == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -317,7 +317,7 @@ func TestMatch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
node, err := transformer.TransformNode(tt.terms, &tt.metadata)
|
node, err := transformer.TransformNode(tt.terms, &tt.metadata, nil)
|
||||||
|
|
||||||
if tt.expectedError != nil {
|
if tt.expectedError != nil {
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
|
|||||||
Reference in New Issue
Block a user