mirror of
https://github.com/outbackdingo/talos-cloud-controller-manager.git
synced 2026-01-27 10:20:27 +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:
|
||||
Region: "{{ .Region }}-on-metal"
|
||||
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:
|
||||
@@ -182,7 +186,32 @@ type PlatformMetadataSpec struct {
|
||||
You can use the following command to get the platform metadata:
|
||||
|
||||
```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
|
||||
@@ -277,3 +306,11 @@ You can use the following functions in the Go template:
|
||||
```yaml
|
||||
{{ 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: ""
|
||||
taints:
|
||||
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) {
|
||||
nodeSpec, err := transformer.TransformNode(client.config.Transformations, tt.meta)
|
||||
nodeSpec, err := transformer.TransformNode(client.config.Transformations, tt.meta, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
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/utils/net"
|
||||
"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"
|
||||
|
||||
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 {
|
||||
return nil, fmt.Errorf("error transforming node: %w", err)
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
talos "github.com/siderolabs/talos/pkg/machinery/client"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"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/network"
|
||||
"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.
|
||||
//
|
||||
//nolint:dupl
|
||||
func (c *Client) GetNodeMetadata(ctx context.Context, nodeIP string) (*runtime.PlatformMetadataSpec, error) {
|
||||
nodeCtx := talos.WithNode(ctx, nodeIP)
|
||||
|
||||
@@ -162,6 +165,38 @@ func (c *Client) GetNodeMetadata(ctx context.Context, nodeIP string) (*runtime.P
|
||||
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.
|
||||
func (c *Client) GetClusterName() string {
|
||||
return c.talos.GetClusterName()
|
||||
|
||||
@@ -26,6 +26,9 @@ var genericMap = map[string]interface{}{
|
||||
// Encoding functions:
|
||||
"b64enc": base64encode,
|
||||
"b64dec": base64decode,
|
||||
|
||||
// String slice functions:
|
||||
"getValue": getValue,
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
"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"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
@@ -36,6 +37,11 @@ type NodeSpec struct {
|
||||
Features NodeFeaturesFlagSpec
|
||||
}
|
||||
|
||||
type nodeTransformationValues struct {
|
||||
runtime.PlatformMetadataSpec
|
||||
hardware.SystemInformationSpec
|
||||
}
|
||||
|
||||
// NodeFeaturesFlagSpec represents the node features flags.
|
||||
type NodeFeaturesFlagSpec struct {
|
||||
// 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.
|
||||
//
|
||||
//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{
|
||||
Annotations: make(map[string]string),
|
||||
Labels: make(map[string]string),
|
||||
@@ -58,7 +64,12 @@ func TransformNode(terms []NodeTerm, platformMetadata *runtime.PlatformMetadataS
|
||||
return node, nil
|
||||
}
|
||||
|
||||
metadata := metadataFromStruct(platformMetadata)
|
||||
values := nodeTransformationValues{PlatformMetadataSpec: *platformMetadata}
|
||||
if sysinfo != nil {
|
||||
values.SystemInformationSpec = *sysinfo
|
||||
}
|
||||
|
||||
metadata := mapFromStruct(platformMetadata)
|
||||
|
||||
for _, term := range terms {
|
||||
match, err := nodeselector.Match(term.NodeSelector, metadata)
|
||||
@@ -69,7 +80,7 @@ func TransformNode(terms []NodeTerm, platformMetadata *runtime.PlatformMetadataS
|
||||
if match {
|
||||
if term.Annotations != nil {
|
||||
for k, v := range term.Annotations {
|
||||
t, err := executeTemplate(v, platformMetadata)
|
||||
t, err := executeTemplate(v, values)
|
||||
if err != nil {
|
||||
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 {
|
||||
for k, v := range term.Labels {
|
||||
t, err := executeTemplate(v, platformMetadata)
|
||||
t, err := executeTemplate(v, values)
|
||||
if err != nil {
|
||||
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 {
|
||||
for k, v := range term.Taints {
|
||||
t, err := executeTemplate(v, platformMetadata)
|
||||
t, err := executeTemplate(v, values)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to transformer label %q: %w", k, err)
|
||||
}
|
||||
@@ -129,7 +140,7 @@ func TransformNode(terms []NodeTerm, platformMetadata *runtime.PlatformMetadataS
|
||||
continue
|
||||
}
|
||||
|
||||
t, err := executeTemplate(v, platformMetadata)
|
||||
t, err := executeTemplate(v, values)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
func metadataFromStruct(in *runtime.PlatformMetadataSpec) map[string]string {
|
||||
func mapFromStruct(in interface{}) map[string]string {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@ func TestMatch(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 {
|
||||
assert.NotNil(t, err)
|
||||
|
||||
Reference in New Issue
Block a user