feat: add support for variable substitution in talos.config kernel parameter
The URL to fetch the configuration for a talos node is given by the talos.config kernel parameter. We add support for 4 variables ${uuid}, ${serial}, ${mac} and ${hostname} which substitute the device UUID, DMI-sourced serial number, MAC address of the first network interface to be up and the hostname respectively. Fixes #3272 Signed-off-by: Philipp Sauter <philipp.sauter@siderolabs.com>
This commit is contained in:
parent
103c942256
commit
2deff6b6e1
2
go.mod
2
go.mod
@ -40,7 +40,7 @@ require (
|
||||
github.com/containernetworking/plugins v1.1.1
|
||||
github.com/coreos/go-iptables v0.6.0
|
||||
github.com/coreos/go-semver v0.3.0
|
||||
github.com/cosi-project/runtime v0.0.0-20220609185841-c0aa3e101537
|
||||
github.com/cosi-project/runtime v0.0.0-20220622192602-9483ac9a4832
|
||||
github.com/docker/distribution v2.8.1+incompatible
|
||||
github.com/docker/docker v20.10.17+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
|
4
go.sum
4
go.sum
@ -339,8 +339,8 @@ github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzA
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cosi-project/runtime v0.0.0-20220609185841-c0aa3e101537 h1:0ejVbCuG1P97SQS5A4l/gHuQ76wG3aWYATf3EMN+Grs=
|
||||
github.com/cosi-project/runtime v0.0.0-20220609185841-c0aa3e101537/go.mod h1:4WQcuQDWr7cIT4mUyqf95PkoBwgAgaa2yhMmGwndYCs=
|
||||
github.com/cosi-project/runtime v0.0.0-20220622192602-9483ac9a4832 h1:ixPX9angaYh/nmaG83TPwine+QXfYqACDvnYhjqB9Ew=
|
||||
github.com/cosi-project/runtime v0.0.0-20220622192602-9483ac9a4832/go.mod h1:4WQcuQDWr7cIT4mUyqf95PkoBwgAgaa2yhMmGwndYCs=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
|
@ -6,11 +6,13 @@ github.com/containernetworking/cni v1.1.0/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8d
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
|
||||
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
||||
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/talos-systems/siderolink v0.1.1/go.mod h1:1PLRyKRx+MAkz1vWJXIP19p5wChF0TejbIbX/CQMWuw=
|
||||
github.com/vishvananda/netlink v1.2.0-beta h1:CTNzkunO9iTkRaupF540+w47mexyQgNkA/ibnuKc39w=
|
||||
github.com/vishvananda/netlink v1.2.0-beta/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
|
||||
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489 h1:1JFLBqwIgdyHN1ZtgjTBwO+blA6gVOmZurpiMEsETKo=
|
||||
go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0=
|
||||
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
golang.org/x/net v0.0.0-20220526153639-5463443f8c37 h1:lUkvobShwKsOesNfWWlCS5q7fnbG1MEliIzwu886fn8=
|
||||
golang.org/x/net v0.0.0-20220526153639-5463443f8c37/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -42,6 +42,15 @@ See [documentation](https://www.talos.dev/v1.1/reference/configuration/#bridge)
|
||||
* Linux: 5.15.49
|
||||
"""
|
||||
|
||||
[notes.talos-config-kernel-param-variable-substitution]
|
||||
title = "Variable substitution for URL query parameter in the talos.config kernel parameter"
|
||||
description="""\
|
||||
The kernel parameter talos.config can now substitute system information into placeholders inside its URL query values. This example shows all supported variables:
|
||||
|
||||
```http://example.com/metadata?h=${hostname}&m=${mac}&s=${serial}&u=${uuid}```
|
||||
"""
|
||||
|
||||
|
||||
[make_deps]
|
||||
|
||||
[make_deps.tools]
|
||||
|
@ -0,0 +1,41 @@
|
||||
// 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 hardware
|
||||
|
||||
import (
|
||||
"github.com/talos-systems/go-smbios/smbios"
|
||||
|
||||
"github.com/talos-systems/talos/pkg/machinery/resources/hardware"
|
||||
)
|
||||
|
||||
// SystemInformation adapter provider conversion from smbios.SMBIOS.
|
||||
//
|
||||
//nolint:revive,golint
|
||||
func SystemInformation(p *hardware.SystemInformation) systemInformation {
|
||||
return systemInformation{
|
||||
SystemInformation: p,
|
||||
}
|
||||
}
|
||||
|
||||
type systemInformation struct {
|
||||
*hardware.SystemInformation
|
||||
}
|
||||
|
||||
// Update current systemInformation info.
|
||||
func (p systemInformation) Update(systemInformation *smbios.SystemInformation) {
|
||||
translateSystemInformationInfo := func(in *smbios.SystemInformation) hardware.SystemInformationSpec {
|
||||
return hardware.SystemInformationSpec{
|
||||
Manufacturer: in.Manufacturer,
|
||||
ProductName: in.ProductName,
|
||||
Version: in.Version,
|
||||
SerialNumber: in.SerialNumber,
|
||||
UUID: in.UUID,
|
||||
WakeUpType: in.WakeUpType.String(),
|
||||
SKUNumber: in.SKUNumber,
|
||||
}
|
||||
}
|
||||
|
||||
*p.SystemInformation.TypedSpec() = translateSystemInformationInfo(systemInformation)
|
||||
}
|
@ -15,6 +15,7 @@ import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
hwadapter "github.com/talos-systems/talos/internal/app/machined/pkg/adapters/hardware"
|
||||
pkgSMBIOS "github.com/talos-systems/talos/internal/pkg/smbios"
|
||||
"github.com/talos-systems/talos/pkg/machinery/resources/hardware"
|
||||
)
|
||||
|
||||
@ -44,6 +45,10 @@ func (ctrl *SystemInfoController) Outputs() []controller.Output {
|
||||
Type: hardware.MemoryModuleType,
|
||||
Kind: controller.OutputExclusive,
|
||||
},
|
||||
{
|
||||
Type: hardware.SystemInformationType,
|
||||
Kind: controller.OutputExclusive,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,7 +64,7 @@ func (ctrl *SystemInfoController) Run(ctx context.Context, r controller.Runtime,
|
||||
|
||||
// controller runs only once
|
||||
if ctrl.SMBIOS == nil {
|
||||
s, err := GetSMBIOSInfo()
|
||||
s, err := pkgSMBIOS.GetSMBIOSInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -67,6 +72,15 @@ func (ctrl *SystemInfoController) Run(ctx context.Context, r controller.Runtime,
|
||||
ctrl.SMBIOS = s
|
||||
}
|
||||
|
||||
const systemInfoID = "systeminformation"
|
||||
if err := r.Modify(ctx, hardware.NewSystemInformation(systemInfoID), func(res resource.Resource) error {
|
||||
hwadapter.SystemInformation(res.(*hardware.SystemInformation)).Update(&ctrl.SMBIOS.SystemInformation)
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error updating objects: %w", err)
|
||||
}
|
||||
|
||||
for _, p := range ctrl.SMBIOS.ProcessorInformation {
|
||||
// replaces `CPU 0` with `CPU-0`
|
||||
id := strings.ReplaceAll(p.SocketDesignation, " ", "-")
|
||||
|
@ -616,7 +616,7 @@ func (mock *platformMock) Name() string {
|
||||
return "mock"
|
||||
}
|
||||
|
||||
func (mock *platformMock) Configuration(context.Context) ([]byte, error) {
|
||||
func (mock *platformMock) Configuration(context.Context, state.State) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,6 @@ import (
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/siderolabs/go-pointer"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
"github.com/talos-systems/go-smbios/smbios"
|
||||
pb "github.com/talos-systems/siderolink/api/siderolink"
|
||||
"go.uber.org/zap"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
@ -26,6 +25,7 @@ import (
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"inet.af/netaddr"
|
||||
|
||||
"github.com/talos-systems/talos/internal/pkg/smbios"
|
||||
"github.com/talos-systems/talos/pkg/machinery/constants"
|
||||
"github.com/talos-systems/talos/pkg/machinery/nethelpers"
|
||||
"github.com/talos-systems/talos/pkg/machinery/resources/network"
|
||||
@ -116,7 +116,7 @@ func (ctrl *ManagerController) Run(ctx context.Context, r controller.Runtime, lo
|
||||
return nil
|
||||
}
|
||||
|
||||
s, err := smbios.New()
|
||||
s, err := smbios.GetSMBIOSInfo()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading node UUID: %w", err)
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ package runtime
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
"inet.af/netaddr"
|
||||
|
||||
@ -25,7 +26,7 @@ type Platform interface {
|
||||
//
|
||||
// On cloud-like platform it is user-data in metadata service.
|
||||
// For metal platform that is either `talos.config=` URL or mounted ISO image.
|
||||
Configuration(context.Context) ([]byte, error)
|
||||
Configuration(context.Context, state.State) ([]byte, error)
|
||||
|
||||
// KernelArgs returns additional kernel arguments which should be injected for the kernel boot.
|
||||
KernelArgs() procfs.Parameters
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
|
||||
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
"inet.af/netaddr"
|
||||
|
||||
@ -51,7 +52,7 @@ func (a *AWS) Name() string {
|
||||
}
|
||||
|
||||
// Configuration implements the runtime.Platform interface.
|
||||
func (a *AWS) Configuration(ctx context.Context) ([]byte, error) {
|
||||
func (a *AWS) Configuration(ctx context.Context, r state.State) ([]byte, error) {
|
||||
log.Printf("fetching machine config from AWS")
|
||||
|
||||
userdata, err := a.metadataClient.GetUserDataWithContext(ctx)
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
"golang.org/x/sys/unix"
|
||||
"inet.af/netaddr"
|
||||
@ -159,7 +160,7 @@ func (a *Azure) ParseLoadBalancerIP(lbConfig LoadBalancerMetadata, exIP []netadd
|
||||
}
|
||||
|
||||
// Configuration implements the platform.Platform interface.
|
||||
func (a *Azure) Configuration(ctx context.Context) ([]byte, error) {
|
||||
func (a *Azure) Configuration(ctx context.Context, r state.State) ([]byte, error) {
|
||||
defer func() {
|
||||
if err := linuxAgent(ctx); err != nil {
|
||||
log.Printf("failed to update instance status, err: %s", err.Error())
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
|
||||
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
|
||||
@ -28,7 +29,7 @@ func (c *Container) Name() string {
|
||||
}
|
||||
|
||||
// Configuration implements the platform.Platform interface.
|
||||
func (c *Container) Configuration(context.Context) ([]byte, error) {
|
||||
func (c *Container) Configuration(context.Context, state.State) ([]byte, error) {
|
||||
log.Printf("fetching machine config from: USERDATA environment variable")
|
||||
|
||||
s := os.Getenv("USERDATA")
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
stderrors "errors"
|
||||
"log"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
"inet.af/netaddr"
|
||||
|
||||
@ -36,7 +37,7 @@ func (d *DigitalOcean) Name() string {
|
||||
}
|
||||
|
||||
// Configuration implements the platform.Platform interface.
|
||||
func (d *DigitalOcean) Configuration(ctx context.Context) ([]byte, error) {
|
||||
func (d *DigitalOcean) Configuration(ctx context.Context, r state.State) ([]byte, error) {
|
||||
log.Printf("fetching machine config from: %q", DigitalOceanUserDataEndpoint)
|
||||
|
||||
return download.Download(ctx, DigitalOceanUserDataEndpoint,
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
"github.com/talos-systems/go-retry/retry"
|
||||
"inet.af/netaddr"
|
||||
@ -89,7 +90,7 @@ func (p *EquinixMetal) Name() string {
|
||||
}
|
||||
|
||||
// Configuration implements the platform.Platform interface.
|
||||
func (p *EquinixMetal) Configuration(ctx context.Context) ([]byte, error) {
|
||||
func (p *EquinixMetal) Configuration(ctx context.Context, r state.State) ([]byte, error) {
|
||||
log.Printf("fetching machine config from: %q", EquinixMetalUserDataEndpoint)
|
||||
|
||||
return download.Download(ctx, EquinixMetalUserDataEndpoint,
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"cloud.google.com/go/compute/metadata"
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
"inet.af/netaddr"
|
||||
|
||||
@ -26,7 +27,7 @@ func (g *GCP) Name() string {
|
||||
}
|
||||
|
||||
// Configuration implements the platform.Platform interface.
|
||||
func (g *GCP) Configuration(ctx context.Context) ([]byte, error) {
|
||||
func (g *GCP) Configuration(ctx context.Context, r state.State) ([]byte, error) {
|
||||
userdata, err := metadata.InstanceAttributeValue("user-data")
|
||||
if err != nil {
|
||||
if _, ok := err.(metadata.NotDefinedError); ok {
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
"gopkg.in/yaml.v3"
|
||||
"inet.af/netaddr"
|
||||
@ -158,7 +159,7 @@ func (h *Hcloud) ParseMetadata(unmarshalledNetworkConfig *NetworkConfig, host, e
|
||||
}
|
||||
|
||||
// Configuration implements the runtime.Platform interface.
|
||||
func (h *Hcloud) Configuration(ctx context.Context) ([]byte, error) {
|
||||
func (h *Hcloud) Configuration(ctx context.Context, r state.State) ([]byte, error) {
|
||||
log.Printf("fetching machine config from: %q", HCloudUserDataEndpoint)
|
||||
|
||||
return download.Download(ctx, HCloudUserDataEndpoint,
|
||||
|
@ -11,18 +11,24 @@ import (
|
||||
"log"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/resource"
|
||||
"github.com/cosi-project/runtime/pkg/safe"
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/talos-systems/go-blockdevice/blockdevice/filesystem"
|
||||
"github.com/talos-systems/go-blockdevice/blockdevice/probe"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
"github.com/talos-systems/go-smbios/smbios"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"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/constants"
|
||||
hardwareResource "github.com/talos-systems/talos/pkg/machinery/resources/hardware"
|
||||
"github.com/talos-systems/talos/pkg/machinery/resources/network"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -38,7 +44,7 @@ func (m *Metal) Name() string {
|
||||
}
|
||||
|
||||
// Configuration implements the platform.Platform interface.
|
||||
func (m *Metal) Configuration(ctx context.Context) ([]byte, error) {
|
||||
func (m *Metal) Configuration(ctx context.Context, r state.State) ([]byte, error) {
|
||||
var option *string
|
||||
if option = procfs.ProcCmdline().Get(constants.KernelParamConfig).First(); option == nil {
|
||||
return nil, errors.ErrNoConfigSource
|
||||
@ -48,13 +54,15 @@ func (m *Metal) Configuration(ctx context.Context) ([]byte, error) {
|
||||
return nil, errors.ErrNoConfigSource
|
||||
}
|
||||
|
||||
log.Printf("fetching machine config from: %q", *option)
|
||||
|
||||
downloadURL, err := PopulateURLParameters(*option, getSystemUUID)
|
||||
downloadURL, err := PopulateURLParameters(ctx, *option, r)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to populate talos.config fetch URL: %q ; %s", *option, err.Error())
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("fetching machine config from: %q", downloadURL)
|
||||
|
||||
switch downloadURL {
|
||||
case constants.MetalConfigISOLabel:
|
||||
return readConfigFromISO()
|
||||
@ -63,30 +71,129 @@ func (m *Metal) Configuration(ctx context.Context) ([]byte, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func keyToVar(key string) string {
|
||||
return `${` + key + `}`
|
||||
}
|
||||
|
||||
type matcher struct {
|
||||
Key string
|
||||
Regexp *regexp.Regexp
|
||||
}
|
||||
|
||||
func newMatcher(key string) *matcher {
|
||||
return &matcher{
|
||||
Key: keyToVar(key),
|
||||
Regexp: regexp.MustCompile(`(?i)` + regexp.QuoteMeta(keyToVar(key))),
|
||||
}
|
||||
}
|
||||
|
||||
type replacer struct {
|
||||
original string
|
||||
Regexp *regexp.Regexp
|
||||
Matches [][]int
|
||||
}
|
||||
|
||||
func (m *matcher) process(original string) *replacer {
|
||||
var r replacer
|
||||
r.Regexp = m.Regexp
|
||||
r.original = original
|
||||
|
||||
r.Matches = m.Regexp.FindAllStringIndex(original, -1)
|
||||
|
||||
return &r
|
||||
}
|
||||
|
||||
func (r *replacer) ReplaceMatches(replacement string) string {
|
||||
var res string
|
||||
|
||||
if len(r.Matches) < 1 {
|
||||
return res
|
||||
}
|
||||
|
||||
res += r.original[:r.Matches[0][0]]
|
||||
res += replacement
|
||||
|
||||
for i := 0; i < len(r.Matches)-1; i++ {
|
||||
res += r.original[r.Matches[i][1]:r.Matches[i+1][0]]
|
||||
res += replacement
|
||||
}
|
||||
|
||||
res += r.original[r.Matches[len(r.Matches)-1][1]:]
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// PopulateURLParameters fills in empty parameters in the download URL.
|
||||
func PopulateURLParameters(downloadURL string, getSystemUUID func() (string, error)) (string, error) {
|
||||
u, err := url.Parse(downloadURL)
|
||||
//nolint:gocyclo
|
||||
func PopulateURLParameters(ctx context.Context, downloadURL string, r state.State) (string, error) {
|
||||
populatedURL := downloadURL
|
||||
|
||||
genErr := func(varOfKey string, errToWrap error) error {
|
||||
return fmt.Errorf("error while substituting %s: %w", varOfKey, errToWrap)
|
||||
}
|
||||
|
||||
u, err := url.Parse(populatedURL)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse %s: %w", constants.KernelParamConfig, err)
|
||||
return "", fmt.Errorf("failed to parse %s: %w", populatedURL, err)
|
||||
}
|
||||
|
||||
values := u.Query()
|
||||
|
||||
for key, qValues := range values {
|
||||
switch key {
|
||||
case "uuid":
|
||||
// don't touch uuid field if it already has some value
|
||||
if !(len(qValues) == 1 && len(strings.TrimSpace(qValues[0])) > 0) {
|
||||
uid, err := getSystemUUID()
|
||||
substitute := func(varToSubstitute string, getFunc func(ctx context.Context, r state.State) (string, error)) error {
|
||||
m := newMatcher(varToSubstitute)
|
||||
|
||||
for qKey, qValues := range values {
|
||||
if len(qValues) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
qVal := qValues[0]
|
||||
|
||||
// backwards compatible behavior for the uuid key
|
||||
if qKey == constants.UUIDKey && !(len(qValues) == 1 && len(strings.TrimSpace(qVal)) > 0) {
|
||||
uid, err := getSystemUUID(ctx, r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return fmt.Errorf("error while substituting UUID: %w", err)
|
||||
}
|
||||
|
||||
values.Set("uuid", uid)
|
||||
values.Set(constants.UUIDKey, uid)
|
||||
|
||||
continue
|
||||
}
|
||||
default:
|
||||
log.Printf("unsupported query parameter: %q", key)
|
||||
|
||||
replacer := m.process(qVal)
|
||||
|
||||
if len(replacer.Matches) < 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
val, err := getFunc(ctx, r)
|
||||
if err != nil {
|
||||
return genErr(m.Key, err)
|
||||
}
|
||||
|
||||
qVal = replacer.ReplaceMatches(val)
|
||||
|
||||
values.Set(qKey, qVal)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := substitute(constants.UUIDKey, getSystemUUID); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := substitute(constants.SerialNumberKey, getSystemSerialNumber); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := substitute(constants.MacKey, getMACAddress); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := substitute(constants.HostnameKey, getHostname); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
u.RawQuery = values.Encode()
|
||||
@ -94,13 +201,88 @@ func PopulateURLParameters(downloadURL string, getSystemUUID func() (string, err
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
func getSystemUUID() (string, error) {
|
||||
s, err := smbios.New()
|
||||
func getResource[T resource.Resource](ctx context.Context, r state.State, namespace, typ, valName string, isReadyFunc func(T) bool, checkAndGetFunc func(T) string) (string, error) {
|
||||
metadata := resource.NewMetadata(namespace, typ, "", resource.VersionUndefined)
|
||||
|
||||
watchCtx, cancel := context.WithTimeout(ctx, 1*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
events := make(chan safe.WrappedStateEvent[T])
|
||||
|
||||
err := safe.StateWatchKind[T](watchCtx, r, metadata, events, state.WithBootstrapContents(true))
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", fmt.Errorf("failed to watch %s resources: %w", typ, err)
|
||||
}
|
||||
|
||||
return s.SystemInformation.UUID, nil
|
||||
var watchErr error
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-watchCtx.Done():
|
||||
err := fmt.Errorf("failed to determine %s of %s: %w", valName, typ, watchCtx.Err())
|
||||
err = fmt.Errorf("%s; %w", err.Error(), watchErr)
|
||||
|
||||
return "", err
|
||||
case event := <-events:
|
||||
eventResource, err := event.Resource()
|
||||
if err != nil {
|
||||
watchErr = fmt.Errorf("%s; invalid resource in wrapped event: %w", watchErr.Error(), err)
|
||||
}
|
||||
|
||||
if !isReadyFunc(eventResource) {
|
||||
continue
|
||||
}
|
||||
|
||||
val := checkAndGetFunc(eventResource)
|
||||
if val == "" {
|
||||
return "", fmt.Errorf("%s property of resource %s is empty", valName, typ)
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getUUIDProperty(r *hardwareResource.SystemInformation) string {
|
||||
return r.TypedSpec().UUID
|
||||
}
|
||||
|
||||
func getSerialNumberProperty(r *hardwareResource.SystemInformation) string {
|
||||
return r.TypedSpec().SerialNumber
|
||||
}
|
||||
|
||||
func getSystemUUID(ctx context.Context, r state.State) (string, error) {
|
||||
return getResource(ctx, r, hardwareResource.NamespaceName, hardwareResource.SystemInformationType, "UUID", func(*hardwareResource.SystemInformation) bool { return true }, getUUIDProperty)
|
||||
}
|
||||
|
||||
func getSystemSerialNumber(ctx context.Context, r state.State) (string, error) {
|
||||
return getResource(ctx,
|
||||
r,
|
||||
hardwareResource.NamespaceName,
|
||||
hardwareResource.SystemInformationType,
|
||||
"Serial Number",
|
||||
func(*hardwareResource.SystemInformation) bool { return true },
|
||||
getSerialNumberProperty)
|
||||
}
|
||||
|
||||
func getMACAddressProperty(r *network.LinkStatus) string {
|
||||
return r.TypedSpec().HardwareAddr.String()
|
||||
}
|
||||
|
||||
func checkLinkUp(r *network.LinkStatus) bool {
|
||||
return r.TypedSpec().LinkState
|
||||
}
|
||||
|
||||
func getMACAddress(ctx context.Context, r state.State) (string, error) {
|
||||
return getResource(ctx, r, network.NamespaceName, network.LinkStatusType, "MAC Address", checkLinkUp, getMACAddressProperty)
|
||||
}
|
||||
|
||||
func getHostnameProperty(r *network.HostnameSpec) string {
|
||||
return r.TypedSpec().Hostname
|
||||
}
|
||||
|
||||
func getHostname(ctx context.Context, r state.State) (string, error) {
|
||||
return getResource(ctx, r, network.NamespaceName, network.HostnameSpecType, "Hostname", func(*network.HostnameSpec) bool { return true }, getHostnameProperty)
|
||||
}
|
||||
|
||||
// Mode implements the platform.Platform interface.
|
||||
@ -108,10 +290,8 @@ func (m *Metal) Mode() runtime.Mode {
|
||||
return runtime.ModeMetal
|
||||
}
|
||||
|
||||
func readConfigFromISO() (b []byte, err error) {
|
||||
var dev *probe.ProbedBlockDevice
|
||||
|
||||
dev, err = probe.GetDevWithFileSystemLabel(constants.MetalConfigISOLabel)
|
||||
func readConfigFromISO() ([]byte, error) {
|
||||
dev, err := probe.GetDevWithFileSystemLabel(constants.MetalConfigISOLabel)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find %s iso: %w", constants.MetalConfigISOLabel, err)
|
||||
}
|
||||
@ -125,14 +305,14 @@ func readConfigFromISO() (b []byte, err error) {
|
||||
}
|
||||
|
||||
if sb == nil {
|
||||
return nil, fmt.Errorf("failed to get filesystem type")
|
||||
return nil, fmt.Errorf("error while substituting filesystem type")
|
||||
}
|
||||
|
||||
if err = unix.Mount(dev.Device().Name(), mnt, sb.Type(), unix.MS_RDONLY, ""); err != nil {
|
||||
return nil, fmt.Errorf("failed to mount iso: %w", err)
|
||||
}
|
||||
|
||||
b, err = ioutil.ReadFile(filepath.Join(mnt, filepath.Base(constants.ConfigPath)))
|
||||
b, err := ioutil.ReadFile(filepath.Join(mnt, filepath.Base(constants.ConfigPath)))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read config: %s", err.Error())
|
||||
}
|
||||
|
@ -5,17 +5,31 @@
|
||||
package metal_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/cosi-project/runtime/pkg/state/impl/inmem"
|
||||
"github.com/cosi-project/runtime/pkg/state/impl/namespaced"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/metal"
|
||||
"github.com/talos-systems/talos/pkg/machinery/nethelpers"
|
||||
"github.com/talos-systems/talos/pkg/machinery/resources/hardware"
|
||||
"github.com/talos-systems/talos/pkg/machinery/resources/network"
|
||||
)
|
||||
|
||||
func TestPopulateURLParameters(t *testing.T) {
|
||||
mockUUID := uuid.New().String()
|
||||
mockUUID := "40dcbd19-3b10-444e-bfff-aaee44a51fda"
|
||||
|
||||
mockMAC := "52:2f:fd:df:fc:c0"
|
||||
|
||||
mockSerialNumber := "0OCZJ19N65"
|
||||
|
||||
mockHostname := "myTestHostname"
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
@ -38,6 +52,16 @@ func TestPopulateURLParameters(t *testing.T) {
|
||||
url: "http://example.com/metadata?uuid=xyz",
|
||||
expectedURL: "http://example.com/metadata?uuid=xyz",
|
||||
},
|
||||
{
|
||||
name: "multiple uuids in one query parameter",
|
||||
url: "http://example.com/metadata?u=this-${uuid}-equals-${uuid}-exactly",
|
||||
expectedURL: fmt.Sprintf("http://example.com/metadata?u=this-%s-equals-%s-exactly", mockUUID, mockUUID),
|
||||
},
|
||||
{
|
||||
name: "uuid and mac in one query parameter",
|
||||
url: "http://example.com/metadata?u=this-${uuid}-and-${mac}-together",
|
||||
expectedURL: fmt.Sprintf("http://example.com/metadata?u=this-%s-and-%s-together", mockUUID, mockMAC),
|
||||
},
|
||||
{
|
||||
name: "other parameters",
|
||||
url: "http://example.com/metadata?foo=a",
|
||||
@ -48,18 +72,86 @@ func TestPopulateURLParameters(t *testing.T) {
|
||||
url: "http://example.com/metadata?uuid=xyz&uuid=foo",
|
||||
expectedURL: fmt.Sprintf("http://example.com/metadata?uuid=%s", mockUUID),
|
||||
},
|
||||
{
|
||||
name: "single serial number",
|
||||
url: "http://example.com/metadata?serial=${serial}",
|
||||
expectedURL: fmt.Sprintf("http://example.com/metadata?serial=%s", mockSerialNumber),
|
||||
},
|
||||
{
|
||||
name: "single MAC",
|
||||
url: "http://example.com/metadata?mac=${mac}",
|
||||
expectedURL: fmt.Sprintf("http://example.com/metadata?mac=%s", mockMAC),
|
||||
},
|
||||
{
|
||||
name: "single hostname",
|
||||
url: "http://example.com/metadata?host=${hostname}",
|
||||
expectedURL: fmt.Sprintf("http://example.com/metadata?host=%s", mockHostname),
|
||||
},
|
||||
{
|
||||
name: "serial number, MAC and hostname",
|
||||
url: "http://example.com/metadata?h=${hostname}&m=${mac}&s=${serial}",
|
||||
expectedURL: fmt.Sprintf("http://example.com/metadata?h=%s&m=%s&s=%s", mockHostname, mockMAC, mockSerialNumber),
|
||||
},
|
||||
{
|
||||
name: "uuid, serial number, MAC and hostname; case-insensitive",
|
||||
url: "http://example.com/metadata?h=${HOSTname}&m=${mAC}&s=${SERIAL}&u=${uUid}",
|
||||
expectedURL: fmt.Sprintf("http://example.com/metadata?h=%s&m=%s&s=%s&u=%s", mockHostname, mockMAC, mockSerialNumber, mockUUID),
|
||||
},
|
||||
{
|
||||
name: "MAC and UUID without variable",
|
||||
url: "http://example.com/metadata?macaddr=${mac}&uuid=",
|
||||
expectedURL: fmt.Sprintf("http://example.com/metadata?macaddr=%s&uuid=%s", mockMAC, mockUUID),
|
||||
},
|
||||
{
|
||||
name: "serial number and UUID without variable, order is not preserved",
|
||||
url: "http://example.com/metadata?uuid=&ser=${serial}",
|
||||
expectedURL: fmt.Sprintf("http://example.com/metadata?ser=%s&uuid=%s", mockSerialNumber, mockUUID),
|
||||
},
|
||||
{
|
||||
name: "UUID variable",
|
||||
url: "http://example.com/metadata?uuid=${uuid}",
|
||||
expectedURL: fmt.Sprintf("http://example.com/metadata?uuid=%s", mockUUID),
|
||||
},
|
||||
{
|
||||
name: "serial number and UUID with variable, order is not preserved",
|
||||
url: "http://example.com/metadata?uuid=${uuid}&ser=${serial}",
|
||||
expectedURL: fmt.Sprintf("http://example.com/metadata?ser=%s&uuid=%s", mockSerialNumber, mockUUID),
|
||||
},
|
||||
} {
|
||||
tt := tt
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
output, err := metal.PopulateURLParameters(tt.url, func() (string, error) {
|
||||
return mockUUID, nil
|
||||
})
|
||||
ctx := context.Background()
|
||||
|
||||
st := state.WrapCore(namespaced.NewState(inmem.Build))
|
||||
|
||||
testID := "testID"
|
||||
sysInfo := hardware.NewSystemInformation(testID)
|
||||
sysInfo.TypedSpec().UUID = mockUUID
|
||||
sysInfo.TypedSpec().SerialNumber = mockSerialNumber
|
||||
assert.NoError(t, st.Create(ctx, sysInfo))
|
||||
|
||||
hostnameSpec := network.NewHostnameSpec(network.NamespaceName, testID)
|
||||
hostnameSpec.TypedSpec().Hostname = mockHostname
|
||||
assert.NoError(t, st.Create(ctx, hostnameSpec))
|
||||
|
||||
linkStatusSpec := network.NewLinkStatus(network.NamespaceName, testID)
|
||||
parsedMockMAC, err := net.ParseMAC(mockMAC)
|
||||
assert.NoError(t, err)
|
||||
|
||||
linkStatusSpec.TypedSpec().HardwareAddr = nethelpers.HardwareAddr(parsedMockMAC)
|
||||
linkStatusSpec.TypedSpec().LinkState = true
|
||||
assert.NoError(t, st.Create(ctx, linkStatusSpec))
|
||||
|
||||
output, err := metal.PopulateURLParameters(ctx, tt.url, st)
|
||||
|
||||
if tt.expectedError != "" {
|
||||
assert.EqualError(t, err, tt.expectedError)
|
||||
} else {
|
||||
assert.Equal(t, output, tt.expectedURL)
|
||||
u, err := url.Parse(tt.expectedURL)
|
||||
assert.NoError(t, err)
|
||||
u.RawQuery = u.Query().Encode()
|
||||
assert.Equal(t, u.String(), output)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -16,12 +16,12 @@ import (
|
||||
|
||||
"github.com/talos-systems/go-blockdevice/blockdevice/filesystem"
|
||||
"github.com/talos-systems/go-blockdevice/blockdevice/probe"
|
||||
"github.com/talos-systems/go-smbios/smbios"
|
||||
"golang.org/x/sys/unix"
|
||||
"inet.af/netaddr"
|
||||
|
||||
"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/internal/pkg/smbios"
|
||||
"github.com/talos-systems/talos/pkg/download"
|
||||
"github.com/talos-systems/talos/pkg/machinery/nethelpers"
|
||||
"github.com/talos-systems/talos/pkg/machinery/resources/network"
|
||||
@ -179,7 +179,7 @@ func (n *Nocloud) configFromCD() (metaConfig []byte, networkConfig []byte, machi
|
||||
|
||||
//nolint:gocyclo
|
||||
func (n *Nocloud) acquireConfig(ctx context.Context) (metadataConfigDl, metadataNetworkConfigDl, machineConfigDl []byte, hostname string, err error) {
|
||||
s, err := smbios.New()
|
||||
s, err := smbios.GetSMBIOSInfo()
|
||||
if err != nil {
|
||||
return nil, nil, nil, "", err
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
|
||||
@ -59,7 +60,7 @@ func (n *Nocloud) ParseMetadata(unmarshalledNetworkConfig *NetworkConfig, hostna
|
||||
}
|
||||
|
||||
// Configuration implements the runtime.Platform interface.
|
||||
func (n *Nocloud) Configuration(ctx context.Context) ([]byte, error) {
|
||||
func (n *Nocloud) Configuration(ctx context.Context, r state.State) ([]byte, error) {
|
||||
_, _, machineConfigDl, _, err := n.acquireConfig(ctx) //nolint:dogsled
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
"inet.af/netaddr"
|
||||
|
||||
@ -209,7 +210,7 @@ func (o *Openstack) ParseMetadata(unmarshalledMetadataConfig *MetadataConfig, un
|
||||
}
|
||||
|
||||
// Configuration implements the runtime.Platform interface.
|
||||
func (o *Openstack) Configuration(ctx context.Context) (machineConfig []byte, err error) {
|
||||
func (o *Openstack) Configuration(ctx context.Context, r state.State) (machineConfig []byte, err error) {
|
||||
_, _, machineConfig, err = o.configFromCD()
|
||||
if err != nil {
|
||||
_, _, machineConfig, err = o.configFromNetwork(ctx)
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
|
||||
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
|
||||
@ -83,7 +84,7 @@ func (o *Oracle) ParseMetadata(interfaceAddresses []NetworkConfig, hostname stri
|
||||
}
|
||||
|
||||
// Configuration implements the platform.Platform interface.
|
||||
func (o *Oracle) Configuration(ctx context.Context) ([]byte, error) {
|
||||
func (o *Oracle) Configuration(ctx context.Context, r state.State) ([]byte, error) {
|
||||
log.Printf("fetching machine config from: %q", OracleUserDataEndpoint)
|
||||
|
||||
machineConfigDl, err := download.Download(ctx, OracleUserDataEndpoint,
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/scaleway/scaleway-sdk-go/api/instance/v1"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
"inet.af/netaddr"
|
||||
@ -141,7 +142,7 @@ func (s *Scaleway) ParseMetadata(metadataConfig *instance.Metadata) (*runtime.Pl
|
||||
}
|
||||
|
||||
// Configuration implements the runtime.Platform interface.
|
||||
func (s *Scaleway) Configuration(ctx context.Context) ([]byte, error) {
|
||||
func (s *Scaleway) Configuration(ctx context.Context, r state.State) ([]byte, error) {
|
||||
log.Printf("fetching machine config from %q", ScalewayUserDataEndpoint)
|
||||
|
||||
machineConfigDl, err := download.Download(ctx, ScalewayUserDataEndpoint,
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
"inet.af/netaddr"
|
||||
|
||||
@ -194,7 +195,7 @@ func (u *UpCloud) ParseMetadata(meta *MetaData) (*runtime.PlatformNetworkConfig,
|
||||
}
|
||||
|
||||
// Configuration implements the runtime.Platform interface.
|
||||
func (u *UpCloud) Configuration(ctx context.Context) ([]byte, error) {
|
||||
func (u *UpCloud) Configuration(ctx context.Context, r state.State) ([]byte, error) {
|
||||
log.Printf("fetching machine config from: %q", UpCloudUserDataEndpoint)
|
||||
|
||||
return download.Download(ctx, UpCloudUserDataEndpoint,
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
"github.com/vmware/govmomi/ovf"
|
||||
"github.com/vmware/vmw-guestinfo/rpcvmx"
|
||||
@ -111,7 +112,7 @@ func readConfigFromOvf(extraConfig *rpcvmx.Config, key string) ([]byte, error) {
|
||||
|
||||
// Configuration implements the platform.Platform interface.
|
||||
//nolint:gocyclo
|
||||
func (v *VMware) Configuration(context.Context) ([]byte, error) {
|
||||
func (v *VMware) Configuration(context.Context, state.State) ([]byte, error) {
|
||||
var option *string
|
||||
if option = procfs.ProcCmdline().Get(constants.KernelParamConfig).First(); option == nil {
|
||||
return nil, fmt.Errorf("%s not found", constants.KernelParamConfig)
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
|
||||
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
|
||||
@ -25,7 +26,7 @@ func (v *VMware) Name() string {
|
||||
}
|
||||
|
||||
// Configuration implements the platform.Platform interface.
|
||||
func (v *VMware) Configuration(context.Context) ([]byte, error) {
|
||||
func (v *VMware) Configuration(context.Context, state.State) ([]byte, error) {
|
||||
return nil, fmt.Errorf("arch not supported")
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/talos-systems/go-procfs/procfs"
|
||||
"github.com/vultr/metadata"
|
||||
"inet.af/netaddr"
|
||||
@ -121,7 +122,7 @@ func (v *Vultr) ParseMetadata(meta *metadata.MetaData, extIP []byte) (*runtime.P
|
||||
}
|
||||
|
||||
// Configuration implements the runtime.Platform interface.
|
||||
func (v *Vultr) Configuration(ctx context.Context) ([]byte, error) {
|
||||
func (v *Vultr) Configuration(ctx context.Context, r state.State) ([]byte, error) {
|
||||
log.Printf("fetching machine config from: %q", VultrUserDataEndpoint)
|
||||
|
||||
return download.Download(ctx, VultrUserDataEndpoint,
|
||||
|
@ -583,7 +583,7 @@ func SaveConfig(seq runtime.Sequence, data interface{}) (runtime.TaskExecutionFu
|
||||
func fetchConfig(ctx context.Context, r runtime.Runtime) (out []byte, err error) {
|
||||
var b []byte
|
||||
|
||||
if b, err = r.State().Platform().Configuration(ctx); err != nil {
|
||||
if b, err = r.State().Platform().Configuration(ctx, r.State().V1Alpha2().Resources()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -91,6 +91,7 @@ func NewState() (*State, error) {
|
||||
&files.EtcFileStatus{},
|
||||
&hardware.Processor{},
|
||||
&hardware.MemoryModule{},
|
||||
&hardware.SystemInformation{},
|
||||
&k8s.AdmissionControlConfig{},
|
||||
&k8s.APIServerConfig{},
|
||||
&k8s.ConfigStatus{},
|
||||
|
@ -7,7 +7,7 @@ package keys
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/talos-systems/go-smbios/smbios"
|
||||
"github.com/talos-systems/talos/internal/pkg/smbios"
|
||||
)
|
||||
|
||||
// NodeIDKeyHandler generates the key based on current node information
|
||||
@ -26,7 +26,7 @@ func (h *NodeIDKeyHandler) GetKey(options ...KeyOption) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s, err := smbios.New()
|
||||
s, err := smbios.GetSMBIOSInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
// 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 hardware
|
||||
package smbios
|
||||
|
||||
import (
|
||||
"sync"
|
@ -679,3 +679,11 @@ const (
|
||||
//nolint:golint
|
||||
SYSLOG_ACTION_READ_ALL = 3
|
||||
)
|
||||
|
||||
// names of variable that can be substituted in the talos.config kernel parameter.
|
||||
const (
|
||||
UUIDKey = "uuid"
|
||||
SerialNumberKey = "serial"
|
||||
HostnameKey = "hostname"
|
||||
MacKey = "mac"
|
||||
)
|
||||
|
@ -2,7 +2,7 @@
|
||||
// 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/.
|
||||
|
||||
// Code generated by "deep-copy -type ProcessorSpec -type MemorySpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT.
|
||||
// Code generated by "deep-copy -type ProcessorSpec -type MemoryModuleSpec -type SystemInformationSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT.
|
||||
|
||||
package hardware
|
||||
|
||||
@ -12,8 +12,14 @@ func (o ProcessorSpec) DeepCopy() ProcessorSpec {
|
||||
return cp
|
||||
}
|
||||
|
||||
// DeepCopy generates a deep copy of MemorySpec.
|
||||
// DeepCopy generates a deep copy of MemoryModuleSpec.
|
||||
func (o MemoryModuleSpec) DeepCopy() MemoryModuleSpec {
|
||||
var cp MemoryModuleSpec = o
|
||||
return cp
|
||||
}
|
||||
|
||||
// DeepCopy generates a deep copy of SystemInformationSpec.
|
||||
func (o SystemInformationSpec) DeepCopy() SystemInformationSpec {
|
||||
var cp SystemInformationSpec = o
|
||||
return cp
|
||||
}
|
||||
|
85
pkg/machinery/resources/hardware/system_information.go
Normal file
85
pkg/machinery/resources/hardware/system_information.go
Normal file
@ -0,0 +1,85 @@
|
||||
// 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 hardware
|
||||
|
||||
import (
|
||||
"github.com/cosi-project/runtime/pkg/resource"
|
||||
"github.com/cosi-project/runtime/pkg/resource/meta"
|
||||
"github.com/cosi-project/runtime/pkg/resource/typed"
|
||||
)
|
||||
|
||||
// SystemInformationType is type of SystemInformation resource.
|
||||
const SystemInformationType = resource.Type("SystemInformations.hardware.talos.dev")
|
||||
|
||||
// SystemInformation resource holds node SystemInformation information.
|
||||
type SystemInformation = typed.Resource[SystemInformationSpec, SystemInformationRD]
|
||||
|
||||
// SystemInformationSpec represents the system information obtained from smbios.
|
||||
type SystemInformationSpec struct {
|
||||
Manufacturer string `yaml:"manufacturer,omitempty"`
|
||||
ProductName string `yaml:"productName,omitempty"`
|
||||
Version string `yaml:"version,omitempty"`
|
||||
SerialNumber string `yaml:"serialnumber,omitempty"`
|
||||
UUID string `yaml:"uuid,omitempty"`
|
||||
WakeUpType string `yaml:"wakeUpType,omitempty"`
|
||||
SKUNumber string `yaml:"skuNumber,omitempty"`
|
||||
}
|
||||
|
||||
// NewSystemInformation initializes a SystemInformationInfo resource.
|
||||
func NewSystemInformation(id string) *SystemInformation {
|
||||
return typed.NewResource[SystemInformationSpec, SystemInformationRD](
|
||||
resource.NewMetadata(NamespaceName, SystemInformationType, id, resource.VersionUndefined),
|
||||
SystemInformationSpec{},
|
||||
)
|
||||
}
|
||||
|
||||
// SystemInformationRD provides auxiliary methods for SystemInformation.
|
||||
type SystemInformationRD struct{}
|
||||
|
||||
// ResourceDefinition implements typed.ResourceDefinition interface.
|
||||
func (c SystemInformationRD) ResourceDefinition(resource.Metadata, SystemInformationSpec) meta.ResourceDefinitionSpec {
|
||||
return meta.ResourceDefinitionSpec{
|
||||
Type: SystemInformationType,
|
||||
Aliases: []resource.Type{
|
||||
"systeminformation",
|
||||
},
|
||||
DefaultNamespace: NamespaceName,
|
||||
PrintColumns: []meta.PrintColumn{
|
||||
{
|
||||
Name: "Manufacturer",
|
||||
JSONPath: `{.manufacturer}`,
|
||||
},
|
||||
{
|
||||
Name: "ProductName",
|
||||
JSONPath: `{.productName}`,
|
||||
},
|
||||
|
||||
{
|
||||
Name: "Version",
|
||||
JSONPath: `{.version}`,
|
||||
},
|
||||
|
||||
{
|
||||
Name: "SerialNumber",
|
||||
JSONPath: `{.serialnumber}`,
|
||||
},
|
||||
|
||||
{
|
||||
Name: "UUID",
|
||||
JSONPath: `{.uuid}`,
|
||||
},
|
||||
|
||||
{
|
||||
Name: "WakeUpType",
|
||||
JSONPath: `{.wakeUpType}`,
|
||||
},
|
||||
|
||||
{
|
||||
Name: "SKUNumber",
|
||||
JSONPath: `{.skuNumber}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
@ -83,6 +83,25 @@ Several of these are enforced by the Kernel Self Protection Project [KSPP](https
|
||||
#### `talos.config`
|
||||
|
||||
The URL at which the machine configuration data may be found.
|
||||
|
||||
This parameter supports variable substitution inside URL query values for the following case-insensitive placeholders:
|
||||
|
||||
* `${uuid}` the SMBIOS UUID
|
||||
* `${serial}` the SMBIOS Serial Number
|
||||
* `${mac}` the MAC address of the first network interface attaining link state `up`
|
||||
* `${hostname}` the hostname of the machine
|
||||
|
||||
The following example
|
||||
|
||||
`http://example.com/metadata?h=${hostname}&m=${mac}&s=${serial}&u=${uuid}`
|
||||
|
||||
may translate to
|
||||
|
||||
`http://example.com/metadata?h=myTestHostname&m=52%3A2f%3Afd%3Adf%3Afc%3Ac0&s=0OCZJ19N65&u=40dcbd19-3b10-444e-bfff-aaee44a51fda`
|
||||
|
||||
For backwards compatibility we insert the system UUID into the query parameter `uuid` if its value is empty. As in
|
||||
|
||||
`http://example.com/metadata?uuid=` => `http://example.com/metadata?uuid=40dcbd19-3b10-444e-bfff-aaee44a51fda`
|
||||
|
||||
#### `talos.platform`
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user