diff --git a/api/resource/definitions/runtime/runtime.proto b/api/resource/definitions/runtime/runtime.proto
index b5c1041e2..deb1a0199 100755
--- a/api/resource/definitions/runtime/runtime.proto
+++ b/api/resource/definitions/runtime/runtime.proto
@@ -45,6 +45,17 @@ message MountStatusSpec {
repeated string options = 4;
}
+// PlatformMetadataSpec describes platform metadata properties.
+message PlatformMetadataSpec {
+ string platform = 1;
+ string hostname = 2;
+ string region = 3;
+ string zone = 4;
+ string instance_type = 5;
+ string instance_id = 6;
+ string provider_id = 7;
+}
+
// UnmetCondition is a failure which prevents machine from being ready at the stage.
message UnmetCondition {
string name = 1;
diff --git a/internal/app/machined/pkg/controllers/network/platform_config.go b/internal/app/machined/pkg/controllers/network/platform_config.go
index cd85a7b7b..a08c0fe0a 100644
--- a/internal/app/machined/pkg/controllers/network/platform_config.go
+++ b/internal/app/machined/pkg/controllers/network/platform_config.go
@@ -92,6 +92,10 @@ func (ctrl *PlatformConfigController) Outputs() []controller.Output {
Type: network.OperatorSpecType,
Kind: controller.OutputShared,
},
+ {
+ Type: runtimeres.PlatformMetadataType,
+ Kind: controller.OutputExclusive,
+ },
}
}
@@ -173,7 +177,7 @@ func (ctrl *PlatformConfigController) Run(ctx context.Context, r controller.Runt
if stateMounted && networkConfig != nil {
if err := ctrl.storeConfig(filepath.Join(ctrl.StatePath, constants.PlatformNetworkConfigFilename), networkConfig); err != nil {
- return fmt.Errorf("error saving config: %w", err)
+ return fmt.Errorf("error saving platform network config: %w", err)
}
logger.Debug("stored cached platform network config")
@@ -198,6 +202,12 @@ func (ctrl *PlatformConfigController) Run(ctx context.Context, r controller.Runt
//nolint:dupl,gocyclo
func (ctrl *PlatformConfigController) apply(ctx context.Context, r controller.Runtime, networkConfig *v1alpha1runtime.PlatformNetworkConfig) error {
+ metadataLength := 0
+
+ if networkConfig.Metadata != nil {
+ metadataLength = 1
+ }
+
// handle all network specs in a loop as all specs can be handled in a similar way
for _, specType := range []struct {
length int
@@ -408,6 +418,28 @@ func (ctrl *PlatformConfigController) apply(ctx context.Context, r controller.Ru
status.Scope = nethelpers.ScopeGlobal
+ return nil
+ }
+ },
+ },
+ // Platform metadata
+ {
+ length: metadataLength,
+ getter: func(i int) interface{} {
+ return networkConfig.Metadata
+ },
+ idBuilder: func(spec interface{}) resource.ID {
+ return runtimeres.PlatformMetadataID
+ },
+ resourceBuilder: func(id string) resource.Resource {
+ return runtimeres.NewPlatformMetadataSpec(runtimeres.NamespaceName, id)
+ },
+ resourceModifier: func(newSpec interface{}) func(r resource.Resource) error {
+ return func(r resource.Resource) error {
+ metadata := newSpec.(*runtimeres.PlatformMetadataSpec) //nolint:errcheck,forcetypeassert
+
+ *r.(*runtimeres.PlatformMetadata).TypedSpec() = *metadata
+
return nil
}
},
diff --git a/internal/app/machined/pkg/controllers/network/platform_config_test.go b/internal/app/machined/pkg/controllers/network/platform_config_test.go
index 21bac37fc..a83d3913f 100644
--- a/internal/app/machined/pkg/controllers/network/platform_config_test.go
+++ b/internal/app/machined/pkg/controllers/network/platform_config_test.go
@@ -477,6 +477,44 @@ func (suite *PlatformConfigSuite) TestPlatformMockExternalIPs() {
)
}
+func (suite *PlatformConfigSuite) TestPlatformMockMetadata() {
+ suite.Require().NoError(
+ suite.runtime.RegisterController(
+ &netctrl.PlatformConfigController{
+ V1alpha1Platform: &platformMock{
+ metadata: &runtimeres.PlatformMetadataSpec{
+ Platform: "mock",
+ Zone: "mock-zone",
+ },
+ },
+ StatePath: suite.statePath,
+ PlatformState: suite.state,
+ },
+ ),
+ )
+
+ suite.startRuntime()
+
+ suite.Assert().NoError(
+ retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
+ func() error {
+ return suite.assertResources(
+ runtimeres.NamespaceName, runtimeres.PlatformMetadataType, []string{
+ runtimeres.PlatformMetadataID,
+ }, func(r resource.Resource) error {
+ spec := r.(*runtimeres.PlatformMetadata).TypedSpec()
+
+ suite.Assert().Equal("mock", spec.Platform)
+ suite.Assert().Equal("mock-zone", spec.Zone)
+
+ return nil
+ },
+ )
+ },
+ ),
+ )
+}
+
const sampleStoredConfig = "addresses: []\nlinks: []\nroutes: []\nhostnames:\n - hostname: talos-e2e-897b4e49-gcp-controlplane-jvcnl\n domainname: \"\"\n layer: default\nresolvers: []\ntimeServers: []\noperators: []\nexternalIPs:\n - 10.3.4.5\n - 2001:470:6d:30e:96f4:4219:5733:b860\n" //nolint:lll
func (suite *PlatformConfigSuite) TestStoreConfig() {
@@ -620,6 +658,8 @@ type platformMock struct {
resolvers []netip.Addr
timeServers []string
dhcp4Links []string
+
+ metadata *runtimeres.PlatformMetadataSpec
}
func (mock *platformMock) Name() string {
@@ -630,6 +670,10 @@ func (mock *platformMock) Configuration(context.Context, state.State) ([]byte, e
return nil, nil
}
+func (mock *platformMock) Metadata(context.Context, state.State) (runtimeres.PlatformMetadataSpec, error) {
+ return runtimeres.PlatformMetadataSpec{Platform: mock.Name()}, nil
+}
+
func (mock *platformMock) Mode() v1alpha1runtime.Mode {
return v1alpha1runtime.ModeCloud
}
@@ -741,6 +785,8 @@ func (mock *platformMock) NetworkConfiguration(
)
}
+ networkConfig.Metadata = mock.metadata
+
select {
case ch <- networkConfig:
case <-ctx.Done():
diff --git a/internal/app/machined/pkg/runtime/platform.go b/internal/app/machined/pkg/runtime/platform.go
index 017acfe8e..9d85359f5 100644
--- a/internal/app/machined/pkg/runtime/platform.go
+++ b/internal/app/machined/pkg/runtime/platform.go
@@ -12,6 +12,7 @@ import (
"github.com/talos-systems/go-procfs/procfs"
"github.com/talos-systems/talos/pkg/machinery/resources/network"
+ "github.com/talos-systems/talos/pkg/machinery/resources/runtime"
)
// Platform defines the requirements for a platform.
@@ -55,4 +56,6 @@ type PlatformNetworkConfig struct {
Operators []network.OperatorSpecSpec `yaml:"operators"`
ExternalIPs []netip.Addr `yaml:"externalIPs"`
+
+ Metadata *runtime.PlatformMetadataSpec `yaml:"metadata,omitempty"`
}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/aws/aws.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/aws/aws.go
index 7e65b57d3..31dfe536c 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/aws/aws.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/aws/aws.go
@@ -24,6 +24,7 @@ import (
"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/machinery/resources/network"
+ runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime"
)
// AWS is the concrete type that implements the runtime.Platform interface.
@@ -47,6 +48,44 @@ func NewAWS() (*AWS, error) {
return a, nil
}
+// ParseMetadata converts AWS platform metadata into platform network config.
+func (a *AWS) ParseMetadata(metadata *MetadataConfig) (*runtime.PlatformNetworkConfig, error) {
+ networkConfig := &runtime.PlatformNetworkConfig{}
+
+ if metadata.Hostname != "" {
+ hostnameSpec := network.HostnameSpecSpec{
+ ConfigLayer: network.ConfigPlatform,
+ }
+
+ if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil {
+ return nil, err
+ }
+
+ networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec)
+ }
+
+ if metadata.PublicIPv4 != "" {
+ ip, err := netip.ParseAddr(metadata.PublicIPv4)
+ if err != nil {
+ return nil, err
+ }
+
+ networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip)
+ }
+
+ networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{
+ Platform: a.Name(),
+ Hostname: metadata.Hostname,
+ Region: metadata.Region,
+ Zone: metadata.Zone,
+ InstanceType: metadata.InstanceType,
+ InstanceID: metadata.InstanceID,
+ ProviderID: fmt.Sprintf("aws://%s/%s", metadata.Zone, metadata.InstanceID),
+ }
+
+ return networkConfig, nil
+}
+
// Name implements the runtime.Platform interface.
func (a *AWS) Name() string {
return "aws"
@@ -87,57 +126,19 @@ func (a *AWS) KernelArgs() procfs.Parameters {
}
// NetworkConfiguration implements the runtime.Platform interface.
-//
-//nolint:gocyclo
func (a *AWS) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error {
- getMetadataKey := func(key string) (string, error) {
- v, err := a.metadataClient.GetMetadataWithContext(ctx, key)
- if err != nil {
- if awsErr, ok := err.(awserr.RequestFailure); ok {
- if awsErr.StatusCode() == http.StatusNotFound {
- return "", nil
- }
- }
+ log.Printf("fetching aws instance config")
- return "", fmt.Errorf("failed to fetch %q from IMDS: %w", key, err)
- }
-
- return v, nil
- }
-
- networkConfig := &runtime.PlatformNetworkConfig{}
-
- hostname, err := getMetadataKey("hostname")
+ metadata, err := a.getMetadata(ctx)
if err != nil {
return err
}
- if hostname != "" {
- hostnameSpec := network.HostnameSpecSpec{
- ConfigLayer: network.ConfigPlatform,
- }
-
- if err = hostnameSpec.ParseFQDN(hostname); err != nil {
- return err
- }
-
- networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec)
- }
-
- externalIP, err := getMetadataKey("public-ipv4")
+ networkConfig, err := a.ParseMetadata(metadata)
if err != nil {
return err
}
- if externalIP != "" {
- ip, err := netip.ParseAddr(externalIP)
- if err != nil {
- return err
- }
-
- networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip)
- }
-
select {
case ch <- networkConfig:
case <-ctx.Done():
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/aws/aws_test.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/aws/aws_test.go
index ab2cf960f..c3d065f7b 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/aws/aws_test.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/aws/aws_test.go
@@ -4,11 +4,36 @@
package aws_test
-import "testing"
+import (
+ _ "embed"
+ "encoding/json"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "gopkg.in/yaml.v3"
+
+ "github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/aws"
+)
+
+//go:embed testdata/metadata.json
+var rawMetadata []byte
+
+//go:embed testdata/expected.yaml
+var expectedNetworkConfig string
func TestEmpty(t *testing.T) {
- // added for accurate coverage estimation
- //
- // please remove it once any unit-test is added
- // for this package
+ p := &aws.AWS{}
+
+ var metadata aws.MetadataConfig
+
+ require.NoError(t, json.Unmarshal(rawMetadata, &metadata))
+
+ networkConfig, err := p.ParseMetadata(&metadata)
+ require.NoError(t, err)
+
+ marshaled, err := yaml.Marshal(networkConfig)
+ require.NoError(t, err)
+
+ assert.Equal(t, expectedNetworkConfig, string(marshaled))
}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/aws/metadata.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/aws/metadata.go
new file mode 100644
index 000000000..268cd7f27
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/aws/metadata.go
@@ -0,0 +1,77 @@
+// 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 aws
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+
+ "github.com/aws/aws-sdk-go/aws/awserr"
+)
+
+// MetadataConfig represents a metadata AWS instance.
+type MetadataConfig struct {
+ Hostname string `json:"hostname,omitempty"`
+ InstanceID string `json:"instance-id,omitempty"`
+ InstanceType string `json:"instance-type,omitempty"`
+ PublicIPv4 string `json:"public-ipv4,omitempty"`
+ PublicIPv6 string `json:"ipv6,omitempty"`
+ Region string `json:"region,omitempty"`
+ Zone string `json:"zone,omitempty"`
+}
+
+//nolint:gocyclo
+func (a *AWS) getMetadata(ctx context.Context) (*MetadataConfig, error) {
+ getMetadataKey := func(key string) (v string, err error) {
+ v, err = a.metadataClient.GetMetadataWithContext(ctx, key)
+ if err != nil {
+ if awsErr, ok := err.(awserr.RequestFailure); ok {
+ if awsErr.StatusCode() == http.StatusNotFound {
+ return "", nil
+ }
+ }
+
+ return "", fmt.Errorf("failed to fetch %q from IMDS: %w", key, err)
+ }
+
+ return v, nil
+ }
+
+ var (
+ metadata MetadataConfig
+ err error
+ )
+
+ if metadata.Hostname, err = getMetadataKey("hostname"); err != nil {
+ return nil, err
+ }
+
+ if metadata.InstanceType, err = getMetadataKey("instance-type"); err != nil {
+ return nil, err
+ }
+
+ if metadata.InstanceID, err = getMetadataKey("instance-id"); err != nil {
+ return nil, err
+ }
+
+ if metadata.PublicIPv4, err = getMetadataKey("public-ipv4"); err != nil {
+ return nil, err
+ }
+
+ if metadata.PublicIPv6, err = getMetadataKey("ipv6"); err != nil {
+ return nil, err
+ }
+
+ if metadata.Region, err = getMetadataKey("placement/region"); err != nil {
+ return nil, err
+ }
+
+ if metadata.Zone, err = getMetadataKey("placement/availability-zone"); err != nil {
+ return nil, err
+ }
+
+ return &metadata, nil
+}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/aws/testdata/expected.yaml b/internal/app/machined/pkg/runtime/v1alpha1/platform/aws/testdata/expected.yaml
new file mode 100644
index 000000000..d28facea6
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/aws/testdata/expected.yaml
@@ -0,0 +1,19 @@
+addresses: []
+links: []
+routes: []
+hostnames:
+ - hostname: talos
+ domainname: ""
+ layer: platform
+resolvers: []
+timeServers: []
+operators: []
+externalIPs:
+ - 1.2.3.4
+metadata:
+ platform: aws
+ hostname: talos
+ region: us-east-1
+ zone: us-east-1a
+ instanceId: i-0a0a0a0a0a0a0a0a0
+ providerId: aws://us-east-1a/i-0a0a0a0a0a0a0a0a0
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/aws/testdata/metadata.json b/internal/app/machined/pkg/runtime/v1alpha1/platform/aws/testdata/metadata.json
new file mode 100644
index 000000000..a0358b15e
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/aws/testdata/metadata.json
@@ -0,0 +1,7 @@
+{
+ "hostname": "talos",
+ "instance-id": "i-0a0a0a0a0a0a0a0a0",
+ "public-ipv4": "1.2.3.4",
+ "region": "us-east-1",
+ "zone": "us-east-1a"
+}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/azure.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/azure.go
index 44834e7a6..eb3da4a3a 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/azure.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/azure.go
@@ -27,20 +27,7 @@ import (
"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/resources/network"
-)
-
-const (
- // AzureInternalEndpoint is the Azure Internal Channel IP
- // https://blogs.msdn.microsoft.com/mast/2015/05/18/what-is-the-ip-address-168-63-129-16/
- AzureInternalEndpoint = "http://168.63.129.16"
- // AzureHostnameEndpoint is the local endpoint for the hostname.
- AzureHostnameEndpoint = "http://169.254.169.254/metadata/instance/compute/osProfile/computerName?api-version=2021-12-13&format=text"
- // AzureInterfacesEndpoint is the local endpoint to get external IPs.
- AzureInterfacesEndpoint = "http://169.254.169.254/metadata/instance/network/interface?api-version=2021-12-13&format=json"
- // AzureLoadbalancerEndpoint is the local endpoint for load balancer config.
- AzureLoadbalancerEndpoint = "http://169.254.169.254/metadata/loadbalancer?api-version=2021-05-01&format=json"
-
- mnt = "/mnt"
+ runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime"
)
// NetworkConfig holds network interface meta config.
@@ -86,7 +73,7 @@ func (a *Azure) Name() string {
// ParseMetadata parses Azure network metadata into the platform network config.
//
//nolint:gocyclo
-func (a *Azure) ParseMetadata(interfaceAddresses []NetworkConfig, host []byte) (*runtime.PlatformNetworkConfig, error) {
+func (a *Azure) ParseMetadata(metadata *ComputeMetadata, interfaceAddresses []NetworkConfig, host []byte) (*runtime.PlatformNetworkConfig, error) {
var networkConfig runtime.PlatformNetworkConfig
// hostname
@@ -137,6 +124,21 @@ func (a *Azure) ParseMetadata(interfaceAddresses []NetworkConfig, host []byte) (
}
}
+ zone := metadata.FaultDomain
+ if metadata.Zone != "" {
+ zone = fmt.Sprintf("%s-%s", metadata.Location, metadata.Zone)
+ }
+
+ networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{
+ Platform: a.Name(),
+ Hostname: metadata.OSProfile.ComputerName,
+ Region: strings.ToLower(metadata.Location),
+ Zone: strings.ToLower(zone),
+ InstanceType: metadata.VMSize,
+ InstanceID: metadata.ResourceID,
+ ProviderID: fmt.Sprintf("azure://%s", metadata.ResourceID),
+ }
+
return &networkConfig, nil
}
@@ -251,6 +253,13 @@ func (a *Azure) configFromCD() ([]byte, error) {
//
//nolint:gocyclo
func (a *Azure) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error {
+ log.Printf("fetching azure instance config from: %q", AzureMetadataEndpoint)
+
+ metadata, err := a.getMetadata(ctx)
+ if err != nil {
+ return err
+ }
+
log.Printf("fetching network config from %q", AzureInterfacesEndpoint)
metadataNetworkConfig, err := download.Download(ctx, AzureInterfacesEndpoint,
@@ -265,17 +274,7 @@ func (a *Azure) NetworkConfiguration(ctx context.Context, _ state.State, ch chan
return err
}
- log.Printf("fetching hostname from: %q", AzureHostnameEndpoint)
-
- host, err := download.Download(ctx, AzureHostnameEndpoint,
- download.WithHeaders(map[string]string{"Metadata": "true"}),
- download.WithErrorOnNotFound(errors.ErrNoHostname),
- download.WithErrorOnEmptyResponse(errors.ErrNoHostname))
- if err != nil && !stderrors.Is(err, errors.ErrNoHostname) {
- return fmt.Errorf("failed to fetch hostname from metadata service: %w", err)
- }
-
- networkConfig, err := a.ParseMetadata(interfaceAddresses, host)
+ networkConfig, err := a.ParseMetadata(metadata, interfaceAddresses, []byte(metadata.OSProfile.ComputerName))
if err != nil {
return fmt.Errorf("failed to parse network metadata: %w", err)
}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/azure_test.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/azure_test.go
index 86f306ff4..a77b7c3ed 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/azure_test.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/azure_test.go
@@ -16,8 +16,11 @@ import (
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/azure"
)
-//go:embed testdata/metadata.json
-var rawMetadata []byte
+//go:embed testdata/interfaces.json
+var rawInterfaces []byte
+
+//go:embed testdata/compute.json
+var rawCompute []byte
//go:embed testdata/loadbalancer.json
var rawLoadBalancerMetadata []byte
@@ -28,11 +31,15 @@ var expectedNetworkConfig string
func TestParseMetadata(t *testing.T) {
a := &azure.Azure{}
- var m []azure.NetworkConfig
+ var interfacesMetadata []azure.NetworkConfig
- require.NoError(t, json.Unmarshal(rawMetadata, &m))
+ require.NoError(t, json.Unmarshal(rawInterfaces, &interfacesMetadata))
- networkConfig, err := a.ParseMetadata(m, []byte("some.fqdn"))
+ var computeMetadata azure.ComputeMetadata
+
+ require.NoError(t, json.Unmarshal(rawCompute, &computeMetadata))
+
+ networkConfig, err := a.ParseMetadata(&computeMetadata, interfacesMetadata, []byte("some.fqdn"))
require.NoError(t, err)
var lb azure.LoadBalancerMetadata
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/metadata.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/metadata.go
new file mode 100644
index 000000000..ab081f2fb
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/metadata.go
@@ -0,0 +1,66 @@
+// 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 azure
+
+import (
+ "context"
+ "encoding/json"
+ stderrors "errors"
+ "fmt"
+
+ "github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors"
+ "github.com/talos-systems/talos/pkg/download"
+)
+
+const (
+ // AzureInternalEndpoint is the Azure Internal Channel IP
+ // https://blogs.msdn.microsoft.com/mast/2015/05/18/what-is-the-ip-address-168-63-129-16/
+ AzureInternalEndpoint = "http://168.63.129.16"
+ // AzureMetadataEndpoint is the local endpoint for the metadata.
+ AzureMetadataEndpoint = "http://169.254.169.254/metadata/instance/compute?api-version=2021-12-13&format=json"
+ // AzureInterfacesEndpoint is the local endpoint to get external IPs.
+ AzureInterfacesEndpoint = "http://169.254.169.254/metadata/instance/network/interface?api-version=2021-12-13&format=json"
+ // AzureLoadbalancerEndpoint is the local endpoint for load balancer config.
+ AzureLoadbalancerEndpoint = "http://169.254.169.254/metadata/loadbalancer?api-version=2021-05-01&format=json"
+
+ mnt = "/mnt"
+)
+
+// ComputeMetadata represents metadata compute information.
+type ComputeMetadata struct {
+ Environment string `json:"azEnvironment,omitempty"`
+ SKU string `json:"sku,omitempty"`
+ Name string `json:"name,omitempty"`
+ Zone string `json:"zone,omitempty"`
+ VMSize string `json:"vmSize,omitempty"`
+ OSType string `json:"osType,omitempty"`
+ OSProfile struct {
+ ComputerName string `json:"computerName,omitempty"`
+ } `json:"osProfile,omitempty"`
+ Location string `json:"location,omitempty"`
+ FaultDomain string `json:"platformFaultDomain,omitempty"`
+ PlatformSubFaultDomain string `json:"platformSubFaultDomain,omitempty"`
+ UpdateDomain string `json:"platformUpdateDomain,omitempty"`
+ ResourceGroup string `json:"resourceGroupName,omitempty"`
+ ResourceID string `json:"resourceId,omitempty"`
+ VMScaleSetName string `json:"vmScaleSetName,omitempty"`
+ SubscriptionID string `json:"subscriptionId,omitempty"`
+}
+
+func (a *Azure) getMetadata(ctx context.Context) (*ComputeMetadata, error) {
+ metadataDl, err := download.Download(ctx, AzureMetadataEndpoint,
+ download.WithHeaders(map[string]string{"Metadata": "true"}))
+ if err != nil && !stderrors.Is(err, errors.ErrNoHostname) {
+ return nil, fmt.Errorf("error fetching metadata: %w", err)
+ }
+
+ var metadata ComputeMetadata
+
+ if err = json.Unmarshal(metadataDl, &metadata); err != nil {
+ return nil, fmt.Errorf("failed to parse compute metadata: %w", err)
+ }
+
+ return &metadata, nil
+}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/testdata/compute.json b/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/testdata/compute.json
new file mode 100644
index 000000000..b57a05c91
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/testdata/compute.json
@@ -0,0 +1,14 @@
+{
+ "location": "CentralUS",
+ "name": "IMDSCanary",
+ "offer": "RHEL",
+ "osType": "Linux",
+ "platformFaultDomain": "0",
+ "platformUpdateDomain": "0",
+ "publisher": "RedHat",
+ "resourceId": "000-000-000-000-000",
+ "sku": "7.2",
+ "version": "7.2.20161026",
+ "vmId": "5c08b38e-4d57-4c23-ac45-aca61037f084",
+ "vmSize": "Standard_DS2"
+}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/testdata/expected.yaml b/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/testdata/expected.yaml
index 10fc7fe4d..fff77a4b0 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/testdata/expected.yaml
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/testdata/expected.yaml
@@ -18,3 +18,10 @@ externalIPs:
- 1.2.3.4
- 2603:1020:10:5::34
- 20.10.5.34
+metadata:
+ platform: azure
+ region: centralus
+ zone: "0"
+ instanceType: Standard_DS2
+ instanceId: 000-000-000-000-000
+ providerId: azure://000-000-000-000-000
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/testdata/metadata.json b/internal/app/machined/pkg/runtime/v1alpha1/platform/azure/testdata/interfaces.json
similarity index 100%
rename from internal/app/machined/pkg/runtime/v1alpha1/platform/azure/testdata/metadata.json
rename to internal/app/machined/pkg/runtime/v1alpha1/platform/azure/testdata/interfaces.json
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/container/container.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/container/container.go
index 60b7afecc..5e14e76b5 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/container/container.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/container/container.go
@@ -18,6 +18,7 @@ import (
"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/machinery/resources/network"
+ runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime"
)
// Container is a platform for installing Talos via an Container image.
@@ -64,16 +65,24 @@ func (c *Container) NetworkConfiguration(ctx context.Context, _ state.State, ch
return err
}
+ hostname = bytes.TrimSpace(hostname)
+
hostnameSpec := network.HostnameSpecSpec{
ConfigLayer: network.ConfigPlatform,
}
- if err := hostnameSpec.ParseFQDN(string(bytes.TrimSpace(hostname))); err != nil {
+ if err := hostnameSpec.ParseFQDN(string(hostname)); err != nil {
return err
}
networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec)
+ networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{
+ Platform: c.Name(),
+ Hostname: string(hostname),
+ InstanceType: os.Getenv("TALOSSKU"),
+ }
+
select {
case ch <- networkConfig:
case <-ctx.Done():
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/digitalocean.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/digitalocean.go
index c1b53d430..4bbf2cec7 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/digitalocean.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/digitalocean.go
@@ -7,26 +7,21 @@ package digitalocean
import (
"context"
- stderrors "errors"
+ "fmt"
"log"
"net/netip"
+ "strconv"
"github.com/cosi-project/runtime/pkg/state"
"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/internal/app/machined/pkg/runtime/v1alpha1/platform/utils"
"github.com/talos-systems/talos/pkg/download"
+ "github.com/talos-systems/talos/pkg/machinery/nethelpers"
"github.com/talos-systems/talos/pkg/machinery/resources/network"
-)
-
-const (
- // DigitalOceanExternalIPEndpoint displays all external addresses associated with the instance.
- DigitalOceanExternalIPEndpoint = "http://169.254.169.254/metadata/v1/interfaces/public/0/ipv4/address"
- // DigitalOceanHostnameEndpoint is the local endpoint for the hostname.
- DigitalOceanHostnameEndpoint = "http://169.254.169.254/metadata/v1/hostname"
- // DigitalOceanUserDataEndpoint is the local endpoint for the config.
- DigitalOceanUserDataEndpoint = "http://169.254.169.254/metadata/v1/user-data"
+ runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime"
)
// DigitalOcean is the concrete type that implements the platform.Platform interface.
@@ -37,6 +32,200 @@ func (d *DigitalOcean) Name() string {
return "digital-ocean"
}
+// ParseMetadata converts DigitalOcean platform metadata into platform network config.
+//
+//nolint:gocyclo,cyclop
+func (d *DigitalOcean) ParseMetadata(metadata *MetadataConfig) (*runtime.PlatformNetworkConfig, error) {
+ networkConfig := &runtime.PlatformNetworkConfig{}
+
+ if metadata.Hostname != "" {
+ hostnameSpec := network.HostnameSpecSpec{
+ ConfigLayer: network.ConfigPlatform,
+ }
+
+ if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil {
+ return nil, err
+ }
+
+ networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec)
+ }
+
+ if len(metadata.DNS.Nameservers) > 0 {
+ var dnsIPs []netip.Addr
+
+ for _, dnsIP := range metadata.DNS.Nameservers {
+ if ip, err := netip.ParseAddr(dnsIP); err == nil {
+ dnsIPs = append(dnsIPs, ip)
+ }
+ }
+
+ networkConfig.Resolvers = append(networkConfig.Resolvers, network.ResolverSpecSpec{
+ DNSServers: dnsIPs,
+ ConfigLayer: network.ConfigPlatform,
+ })
+ }
+
+ networkConfig.Links = append(networkConfig.Links, network.LinkSpecSpec{
+ Name: "eth0",
+ Up: true,
+ ConfigLayer: network.ConfigPlatform,
+ })
+
+ for _, iface := range metadata.Interfaces["public"] {
+ if iface.IPv4 != nil {
+ ifAddr, err := utils.IPPrefixFrom(iface.IPv4.IPAddress, iface.IPv4.Netmask)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse ip address: %w", err)
+ }
+
+ networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ifAddr.Addr())
+
+ networkConfig.Addresses = append(networkConfig.Addresses,
+ network.AddressSpecSpec{
+ ConfigLayer: network.ConfigPlatform,
+ LinkName: "eth0",
+ Address: ifAddr,
+ Scope: nethelpers.ScopeGlobal,
+ Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent),
+ Family: nethelpers.FamilyInet4,
+ },
+ )
+
+ if iface.IPv4.Gateway != "" {
+ gw, err := netip.ParseAddr(iface.IPv4.Gateway)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse gateway ip: %w", err)
+ }
+
+ route := network.RouteSpecSpec{
+ ConfigLayer: network.ConfigPlatform,
+ Gateway: gw,
+ OutLinkName: "eth0",
+ Table: nethelpers.TableMain,
+ Protocol: nethelpers.ProtocolStatic,
+ Type: nethelpers.TypeUnicast,
+ Family: nethelpers.FamilyInet4,
+ Priority: 1024,
+ }
+
+ route.Normalize()
+
+ networkConfig.Routes = append(networkConfig.Routes, route)
+
+ metaServer, _ := netip.ParsePrefix("169.254.169.254/32") //nolint:errcheck
+
+ networkConfig.Routes = append(networkConfig.Routes, network.RouteSpecSpec{
+ ConfigLayer: network.ConfigPlatform,
+ OutLinkName: "eth0",
+ Destination: metaServer,
+ Gateway: gw,
+ Table: nethelpers.TableMain,
+ Protocol: nethelpers.ProtocolStatic,
+ Type: nethelpers.TypeUnicast,
+ Family: nethelpers.FamilyInet4,
+ Priority: 512,
+ })
+ }
+ }
+
+ if iface.IPv6 != nil {
+ ifAddr, err := utils.IPPrefixFrom(iface.IPv6.IPAddress, strconv.Itoa(iface.IPv6.CIDR))
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse ip address: %w", err)
+ }
+
+ networkConfig.Addresses = append(networkConfig.Addresses,
+ network.AddressSpecSpec{
+ ConfigLayer: network.ConfigPlatform,
+ LinkName: "eth0",
+ Address: ifAddr,
+ Scope: nethelpers.ScopeGlobal,
+ Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent),
+ Family: nethelpers.FamilyInet6,
+ },
+ )
+
+ if iface.IPv6.Gateway != "" {
+ gw, err := netip.ParseAddr(iface.IPv6.Gateway)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse gateway ip: %w", err)
+ }
+
+ route := network.RouteSpecSpec{
+ ConfigLayer: network.ConfigPlatform,
+ Gateway: gw,
+ OutLinkName: "eth0",
+ Table: nethelpers.TableMain,
+ Protocol: nethelpers.ProtocolStatic,
+ Type: nethelpers.TypeUnicast,
+ Family: nethelpers.FamilyInet6,
+ Priority: 1024,
+ }
+
+ route.Normalize()
+
+ networkConfig.Routes = append(networkConfig.Routes, route)
+ }
+ }
+
+ if iface.AnchorIPv4 != nil {
+ ifAddr, err := utils.IPPrefixFrom(iface.AnchorIPv4.IPAddress, iface.AnchorIPv4.Netmask)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse ip address: %w", err)
+ }
+
+ networkConfig.Addresses = append(networkConfig.Addresses,
+ network.AddressSpecSpec{
+ ConfigLayer: network.ConfigPlatform,
+ LinkName: "eth0",
+ Address: ifAddr,
+ Scope: nethelpers.ScopeGlobal,
+ Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent),
+ Family: nethelpers.FamilyInet4,
+ },
+ )
+ }
+ }
+
+ for idx, iface := range metadata.Interfaces["private"] {
+ ifName := fmt.Sprintf("eth%d", idx+1)
+
+ networkConfig.Links = append(networkConfig.Links, network.LinkSpecSpec{
+ Name: ifName,
+ Up: true,
+ ConfigLayer: network.ConfigPlatform,
+ })
+
+ if iface.IPv4 != nil {
+ ifAddr, err := utils.IPPrefixFrom(iface.IPv4.IPAddress, iface.IPv4.Netmask)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse ip address: %w", err)
+ }
+
+ networkConfig.Addresses = append(networkConfig.Addresses,
+ network.AddressSpecSpec{
+ ConfigLayer: network.ConfigPlatform,
+ LinkName: ifName,
+ Address: ifAddr,
+ Scope: nethelpers.ScopeGlobal,
+ Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent),
+ Family: nethelpers.FamilyInet4,
+ },
+ )
+ }
+ }
+
+ networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{
+ Platform: d.Name(),
+ Hostname: metadata.Hostname,
+ Region: metadata.Region,
+ InstanceID: strconv.Itoa(metadata.DropletID),
+ ProviderID: fmt.Sprintf("digitalocean://%d", metadata.DropletID),
+ }
+
+ return networkConfig, nil
+}
+
// Configuration implements the platform.Platform interface.
func (d *DigitalOcean) Configuration(ctx context.Context, r state.State) ([]byte, error) {
log.Printf("fetching machine config from: %q", DigitalOceanUserDataEndpoint)
@@ -59,43 +248,19 @@ func (d *DigitalOcean) KernelArgs() procfs.Parameters {
}
// NetworkConfiguration implements the runtime.Platform interface.
-//
-//nolint:gocyclo
func (d *DigitalOcean) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error {
- host, err := download.Download(ctx, DigitalOceanHostnameEndpoint,
- download.WithErrorOnNotFound(errors.ErrNoHostname),
- download.WithErrorOnEmptyResponse(errors.ErrNoHostname))
- if err != nil && !stderrors.Is(err, errors.ErrNoHostname) {
+ log.Printf("fetching DigitalOcean instance config from: %q ", DigitalOceanMetadataEndpoint)
+
+ metadata, err := d.getMetadata(ctx)
+ if err != nil {
return err
}
- extIP, err := download.Download(ctx, DigitalOceanExternalIPEndpoint,
- download.WithErrorOnNotFound(errors.ErrNoExternalIPs),
- download.WithErrorOnEmptyResponse(errors.ErrNoExternalIPs))
- if err != nil && !stderrors.Is(err, errors.ErrNoExternalIPs) {
+ networkConfig, err := d.ParseMetadata(metadata)
+ if err != nil {
return err
}
- networkConfig := &runtime.PlatformNetworkConfig{}
-
- if len(host) > 0 {
- hostnameSpec := network.HostnameSpecSpec{
- ConfigLayer: network.ConfigPlatform,
- }
-
- if err := hostnameSpec.ParseFQDN(string(host)); err != nil {
- return err
- }
-
- networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec)
- }
-
- if len(extIP) > 0 {
- if ip, err := netip.ParseAddr(string(extIP)); err == nil {
- networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip)
- }
- }
-
select {
case ch <- networkConfig:
case <-ctx.Done():
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/digitalocean_test.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/digitalocean_test.go
index b0e5d9094..5f26c06db 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/digitalocean_test.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/digitalocean_test.go
@@ -4,11 +4,36 @@
package digitalocean_test
-import "testing"
+import (
+ _ "embed"
+ "encoding/json"
+ "testing"
-func TestEmpty(t *testing.T) {
- // added for accurate coverage estimation
- //
- // please remove it once any unit-test is added
- // for this package
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "gopkg.in/yaml.v2"
+
+ "github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean"
+)
+
+//go:embed testdata/metadata.json
+var rawMetadata []byte
+
+//go:embed testdata/expected.yaml
+var expectedNetworkConfig string
+
+func TestParseMetadata(t *testing.T) {
+ p := &digitalocean.DigitalOcean{}
+
+ var metadata digitalocean.MetadataConfig
+
+ require.NoError(t, json.Unmarshal(rawMetadata, &metadata))
+
+ networkConfig, err := p.ParseMetadata(&metadata)
+ require.NoError(t, err)
+
+ marshaled, err := yaml.Marshal(networkConfig)
+ require.NoError(t, err)
+
+ assert.Equal(t, expectedNetworkConfig, string(marshaled))
}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/metadata.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/metadata.go
new file mode 100644
index 000000000..886697094
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/metadata.go
@@ -0,0 +1,81 @@
+// 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 digitalocean
+
+import (
+ "context"
+ "encoding/json"
+ stderrors "errors"
+
+ "github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors"
+ "github.com/talos-systems/talos/pkg/download"
+)
+
+const (
+ // DigitalOceanExternalIPEndpoint displays all external addresses associated with the instance.
+ DigitalOceanExternalIPEndpoint = "http://169.254.169.254/metadata/v1/interfaces/public/0/ipv4/address"
+ // DigitalOceanMetadataEndpoint is the local endpoint for the platform metadata.
+ DigitalOceanMetadataEndpoint = "http://169.254.169.254/metadata/v1.json"
+ // DigitalOceanUserDataEndpoint is the local endpoint for the config.
+ DigitalOceanUserDataEndpoint = "http://169.254.169.254/metadata/v1/user-data"
+)
+
+// MetadataConfig represents a metadata Digital Ocean instance.
+type MetadataConfig struct {
+ Hostname string `json:"hostname,omitempty"`
+ DropletID int `json:"droplet_id,omitempty"`
+ Region string `json:"region,omitempty"`
+ PublicIPv4 string `json:"public-ipv4,omitempty"`
+ Tags []string `json:"tags,omitempty"`
+
+ DNS struct {
+ Nameservers []string `json:"nameservers,omitempty"`
+ } `json:"dns,omitempty"`
+ Interfaces map[string][]struct {
+ MACAddress string `json:"mac,omitempty"`
+ Type string `json:"type,omitempty"`
+
+ IPv4 *struct {
+ IPAddress string `json:"ip_address,omitempty"`
+ Netmask string `json:"netmask,omitempty"`
+ Gateway string `json:"gateway,omitempty"`
+ } `json:"ipv4,omitempty"`
+ IPv6 *struct {
+ IPAddress string `json:"ip_address,omitempty"`
+ CIDR int `json:"cidr,omitempty"`
+ Gateway string `json:"gateway,omitempty"`
+ } `json:"ipv6,omitempty"`
+ AnchorIPv4 *struct {
+ IPAddress string `json:"ip_address,omitempty"`
+ Netmask string `json:"netmask,omitempty"`
+ Gateway string `json:"gateway,omitempty"`
+ } `json:"anchor_ipv4,omitempty"`
+ } `json:"interfaces,omitempty"`
+}
+
+func (d *DigitalOcean) getMetadata(ctx context.Context) (*MetadataConfig, error) {
+ metaConfigDl, err := download.Download(ctx, DigitalOceanMetadataEndpoint,
+ download.WithErrorOnNotFound(errors.ErrNoHostname),
+ download.WithErrorOnEmptyResponse(errors.ErrNoHostname))
+ if err != nil && !stderrors.Is(err, errors.ErrNoHostname) {
+ return nil, err
+ }
+
+ var metadata MetadataConfig
+ if err = json.Unmarshal(metaConfigDl, &metadata); err != nil {
+ return nil, err
+ }
+
+ extIP, err := download.Download(ctx, DigitalOceanExternalIPEndpoint,
+ download.WithErrorOnNotFound(errors.ErrNoExternalIPs),
+ download.WithErrorOnEmptyResponse(errors.ErrNoExternalIPs))
+ if err != nil && !stderrors.Is(err, errors.ErrNoExternalIPs) {
+ return nil, err
+ }
+
+ metadata.PublicIPv4 = string(extIP)
+
+ return &metadata, nil
+}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/testdata/expected.yaml b/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/testdata/expected.yaml
new file mode 100644
index 000000000..cb75870cd
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/testdata/expected.yaml
@@ -0,0 +1,96 @@
+addresses:
+- address: 128.199.52.32/19
+ linkName: eth0
+ family: inet4
+ scope: global
+ flags: permanent
+ layer: platform
+- address: 2a03:b0c0:2:d0::1478:3001/64
+ linkName: eth0
+ family: inet6
+ scope: global
+ flags: permanent
+ layer: platform
+- address: 10.18.0.5/16
+ linkName: eth0
+ family: inet4
+ scope: global
+ flags: permanent
+ layer: platform
+- address: 10.133.0.2/16
+ linkName: eth1
+ family: inet4
+ scope: global
+ flags: permanent
+ layer: platform
+links:
+- name: eth0
+ logical: false
+ up: true
+ mtu: 0
+ kind: ""
+ type: netrom
+ layer: platform
+- name: eth1
+ logical: false
+ up: true
+ mtu: 0
+ kind: ""
+ type: netrom
+ layer: platform
+routes:
+- family: inet4
+ dst: ""
+ src: ""
+ gateway: 128.199.32.1
+ outLinkName: eth0
+ table: main
+ priority: 1024
+ scope: global
+ type: unicast
+ flags: ""
+ protocol: static
+ layer: platform
+- family: inet4
+ dst: 169.254.169.254/32
+ src: ""
+ gateway: 128.199.32.1
+ outLinkName: eth0
+ table: main
+ priority: 512
+ scope: global
+ type: unicast
+ flags: ""
+ protocol: static
+ layer: platform
+- family: inet6
+ dst: ""
+ src: ""
+ gateway: 2a03:b0c0:2:d0::1
+ outLinkName: eth0
+ table: main
+ priority: 1024
+ scope: global
+ type: unicast
+ flags: ""
+ protocol: static
+ layer: platform
+hostnames:
+- hostname: debian-s-1vcpu-512mb-10gb-ams3-01
+ domainname: ""
+ layer: platform
+resolvers:
+- dnsServers:
+ - 67.207.67.2
+ - 67.207.67.3
+ layer: platform
+timeServers: []
+operators: []
+externalIPs:
+- 128.199.52.32
+metadata:
+ platform: digital-ocean
+ hostname: debian-s-1vcpu-512mb-10gb-ams3-01
+ region: ams3
+ instanceId: "320206672"
+ providerId: digitalocean://320206672
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/testdata/metadata.json b/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/testdata/metadata.json
new file mode 100644
index 000000000..3667ab958
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/testdata/metadata.json
@@ -0,0 +1,70 @@
+{
+ "droplet_id": 320206672,
+ "hostname": "debian-s-1vcpu-512mb-10gb-ams3-01",
+ "user_data": "",
+ "vendor_data": "",
+ "public_keys": [],
+ "auth_key": "490eac0a2fc04503267ef85064407f2f",
+ "region": "ams3",
+ "interfaces": {
+ "private": [
+ {
+ "ipv4": {
+ "ip_address": "10.133.0.2",
+ "netmask": "255.255.0.0",
+ "gateway": "10.133.0.1"
+ },
+ "mac": "2a:3c:79:3d:f3:b7",
+ "type": "private"
+ }
+ ],
+ "public": [
+ {
+ "ipv4": {
+ "ip_address": "128.199.52.32",
+ "netmask": "255.255.224.0",
+ "gateway": "128.199.32.1"
+ },
+ "ipv6": {
+ "ip_address": "2A03:B0C0:0002:00D0:0000:0000:1478:3001",
+ "cidr": 64,
+ "gateway": "2a03:b0c0:2:d0::1"
+ },
+ "anchor_ipv4": {
+ "ip_address": "10.18.0.5",
+ "netmask": "255.255.0.0",
+ "gateway": "10.18.0.1"
+ },
+ "mac": "12:2f:49:0c:eb:c0",
+ "type": "public"
+ }
+ ]
+ },
+ "floating_ip": {
+ "ipv4": {
+ "active": false
+ }
+ },
+ "reserved_ip": {
+ "ipv4": {
+ "active": false
+ }
+ },
+ "dns": {
+ "nameservers": [
+ "67.207.67.2",
+ "67.207.67.3"
+ ]
+ },
+ "tags": [
+ "label123"
+ ],
+ "features": {
+ "dhcp_enabled": false
+ },
+ "modify_index": 113261986,
+ "dotty_status": "running",
+ "ssh_info": {
+ "port": 22
+ }
+}
\ No newline at end of file
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/equinix.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/equinix.go
index f98ce5133..3a2a6541b 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/equinix.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/equinix.go
@@ -30,6 +30,7 @@ import (
"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"
+ runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime"
)
// Event holds data to pass to the Equinix Metal event URL.
@@ -38,13 +39,6 @@ type Event struct {
Message string `json:"msg"`
}
-// Metadata holds equinixmetal metadata info.
-type Metadata struct {
- Hostname string `json:"hostname"`
- Network Network `json:"network"`
- PrivateSubnets []string `json:"private_subnets"`
-}
-
// Network holds network info from the equinixmetal metadata.
type Network struct {
Bonding Bonding `json:"bonding"`
@@ -116,7 +110,7 @@ func (p *EquinixMetal) KernelArgs() procfs.Parameters {
// ParseMetadata converts Equinix Metal metadata into Talos network configuration.
//
//nolint:gocyclo,cyclop
-func (p *EquinixMetal) ParseMetadata(ctx context.Context, equinixMetadata *Metadata, st state.State) (*runtime.PlatformNetworkConfig, error) {
+func (p *EquinixMetal) ParseMetadata(ctx context.Context, equinixMetadata *MetadataConfig, st state.State) (*runtime.PlatformNetworkConfig, error) {
networkConfig := &runtime.PlatformNetworkConfig{}
// 1. Links
@@ -325,6 +319,18 @@ func (p *EquinixMetal) ParseMetadata(ctx context.Context, equinixMetadata *Metad
networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec)
}
+ // 5. platform metadata
+
+ networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{
+ Platform: p.Name(),
+ Hostname: equinixMetadata.Hostname,
+ Region: equinixMetadata.Metro,
+ Zone: equinixMetadata.Facility,
+ InstanceType: equinixMetadata.Plan,
+ InstanceID: equinixMetadata.ID,
+ ProviderID: fmt.Sprintf("equinixmetal://%s", equinixMetadata.ID),
+ }
+
return networkConfig, nil
}
@@ -337,12 +343,12 @@ func (p *EquinixMetal) NetworkConfiguration(ctx context.Context, st state.State,
return err
}
- var equinixMetadata Metadata
- if err = json.Unmarshal(metadataConfig, &equinixMetadata); err != nil {
+ var meta MetadataConfig
+ if err = json.Unmarshal(metadataConfig, &meta); err != nil {
return err
}
- networkConfig, err := p.ParseMetadata(ctx, &equinixMetadata, st)
+ networkConfig, err := p.ParseMetadata(ctx, &meta, st)
if err != nil {
return err
}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/equinix_test.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/equinix_test.go
index 73040cf3a..a0b46d555 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/equinix_test.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/equinix_test.go
@@ -31,7 +31,7 @@ var expectedNetworkConfig string
func TestParseMetadata(t *testing.T) {
p := &equinixmetal.EquinixMetal{}
- var m equinixmetal.Metadata
+ var m equinixmetal.MetadataConfig
require.NoError(t, json.Unmarshal(rawMetadata, &m))
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/metadata.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/metadata.go
new file mode 100644
index 000000000..60dd8b147
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/metadata.go
@@ -0,0 +1,16 @@
+// 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 equinixmetal
+
+// MetadataConfig holds equinixmetal metadata info.
+type MetadataConfig struct {
+ ID string `json:"id"`
+ Hostname string `json:"hostname"`
+ Plan string `json:"plan"`
+ Metro string `json:"metro"`
+ Facility string `json:"facility"`
+ Network Network `json:"network"`
+ PrivateSubnets []string `json:"private_subnets"`
+}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/testdata/expected.yaml b/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/testdata/expected.yaml
index 68a8f4ef7..4607b41e8 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/testdata/expected.yaml
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/testdata/expected.yaml
@@ -103,3 +103,11 @@ resolvers: []
timeServers: []
operators: []
externalIPs: []
+metadata:
+ platform: equinixMetal
+ hostname: infra-green-ci
+ region: ny
+ zone: ny5
+ instanceType: c3.medium.x86
+ instanceId: X
+ providerId: equinixmetal://X
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/errors/errors.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/errors/errors.go
index b7a9c968e..2c726a086 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/errors/errors.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/errors/errors.go
@@ -18,3 +18,6 @@ var ErrNoExternalIPs = errors.New("failed to fetch external addresses from metad
// ErrNoEventURL indicates that the platform does not have an expected events URL in the kernel params.
var ErrNoEventURL = errors.New("no event URL")
+
+// ErrMetadataNotReady indicates that the platform does not have metadata yet.
+var ErrMetadataNotReady = errors.New("platform metadata is not ready")
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/gcp.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/gcp.go
index d8566d562..b3f6c2381 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/gcp.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/gcp.go
@@ -7,6 +7,8 @@ package gcp
import (
"context"
+ "fmt"
+ "log"
"net/netip"
"strings"
@@ -17,6 +19,7 @@ import (
"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/machinery/resources/network"
+ runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime"
)
// GCP is the concrete type that implements the platform.Platform interface.
@@ -27,6 +30,62 @@ func (g *GCP) Name() string {
return "gcp"
}
+// ParseMetadata converts GCP platform metadata into platform network config.
+func (g *GCP) ParseMetadata(metadata *MetadataConfig) (*runtime.PlatformNetworkConfig, error) {
+ networkConfig := &runtime.PlatformNetworkConfig{}
+
+ if metadata.Hostname != "" {
+ hostnameSpec := network.HostnameSpecSpec{
+ ConfigLayer: network.ConfigPlatform,
+ }
+
+ if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil {
+ return nil, err
+ }
+
+ networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec)
+ }
+
+ if metadata.PublicIPv4 != "" {
+ ip, err := netip.ParseAddr(metadata.PublicIPv4)
+ if err != nil {
+ return nil, err
+ }
+
+ networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip)
+ }
+
+ dns, _ := netip.ParseAddr(gcpResolverServer) //nolint:errcheck
+
+ networkConfig.Resolvers = append(networkConfig.Resolvers, network.ResolverSpecSpec{
+ DNSServers: []netip.Addr{dns},
+ ConfigLayer: network.ConfigPlatform,
+ })
+
+ networkConfig.TimeServers = append(networkConfig.TimeServers, network.TimeServerSpecSpec{
+ NTPServers: []string{gcpTimeServer},
+ ConfigLayer: network.ConfigPlatform,
+ })
+
+ region := metadata.Zone
+
+ if idx := strings.LastIndex(region, "-"); idx != -1 {
+ region = region[:idx]
+ }
+
+ networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{
+ Platform: g.Name(),
+ Hostname: metadata.Hostname,
+ Region: region,
+ Zone: metadata.Zone,
+ InstanceType: metadata.InstanceType,
+ InstanceID: metadata.InstanceID,
+ ProviderID: fmt.Sprintf("gce://%s/%s/%s", metadata.ProjectID, metadata.Zone, metadata.Name),
+ }
+
+ return networkConfig, nil
+}
+
// Configuration implements the platform.Platform interface.
func (g *GCP) Configuration(ctx context.Context, r state.State) ([]byte, error) {
userdata, err := metadata.InstanceAttributeValue("user-data")
@@ -59,41 +118,18 @@ func (g *GCP) KernelArgs() procfs.Parameters {
// NetworkConfiguration implements the runtime.Platform interface.
func (g *GCP) NetworkConfiguration(ctx context.Context, st state.State, ch chan<- *runtime.PlatformNetworkConfig) error {
- networkConfig := &runtime.PlatformNetworkConfig{}
+ log.Printf("fetching gcp instance config")
- hostname, err := metadata.Hostname()
+ metadata, err := g.getMetadata(ctx)
+ if err != nil {
+ return fmt.Errorf("failed to receive GCP metadata: %w", err)
+ }
+
+ networkConfig, err := g.ParseMetadata(metadata)
if err != nil {
return err
}
- if hostname != "" {
- hostnameSpec := network.HostnameSpecSpec{
- ConfigLayer: network.ConfigPlatform,
- }
-
- if err = hostnameSpec.ParseFQDN(hostname); err != nil {
- return err
- }
-
- networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec)
- }
-
- externalIP, err := metadata.ExternalIP()
- if err != nil {
- if _, ok := err.(metadata.NotDefinedError); !ok {
- return err
- }
- }
-
- if externalIP != "" {
- ip, err := netip.ParseAddr(externalIP)
- if err != nil {
- return err
- }
-
- networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip)
- }
-
select {
case ch <- networkConfig:
case <-ctx.Done():
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/gcp_test.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/gcp_test.go
index 661dde2d6..b7d47138a 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/gcp_test.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/gcp_test.go
@@ -4,11 +4,36 @@
package gcp_test
-import "testing"
+import (
+ _ "embed"
+ "encoding/json"
+ "testing"
-func TestEmpty(t *testing.T) {
- // added for accurate coverage estimation
- //
- // please remove it once any unit-test is added
- // for this package
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "gopkg.in/yaml.v3"
+
+ "github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp"
+)
+
+//go:embed testdata/metadata.json
+var rawMetadata []byte
+
+//go:embed testdata/expected.yaml
+var expectedNetworkConfig string
+
+func TestParseMetadata(t *testing.T) {
+ p := &gcp.GCP{}
+
+ var metadata gcp.MetadataConfig
+
+ require.NoError(t, json.Unmarshal(rawMetadata, &metadata))
+
+ networkConfig, err := p.ParseMetadata(&metadata)
+ require.NoError(t, err)
+
+ marshaled, err := yaml.Marshal(networkConfig)
+ require.NoError(t, err)
+
+ assert.Equal(t, expectedNetworkConfig, string(marshaled))
}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/metadata.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/metadata.go
new file mode 100644
index 000000000..46de9afa5
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/metadata.go
@@ -0,0 +1,70 @@
+// 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 gcp
+
+import (
+ "context"
+ "strings"
+
+ "cloud.google.com/go/compute/metadata"
+)
+
+const (
+ gcpResolverServer = "169.254.169.254"
+ gcpTimeServer = "metadata.google.internal"
+)
+
+// MetadataConfig holds meta info.
+type MetadataConfig struct {
+ ProjectID string `json:"project-id"`
+ Name string `json:"name,omitempty"`
+ Hostname string `json:"hostname,omitempty"`
+ Zone string `json:"zone,omitempty"`
+ InstanceType string `json:"machine-type"`
+ InstanceID string `json:"id"`
+ PublicIPv4 string `json:"external-ip"`
+}
+
+func (g *GCP) getMetadata(context.Context) (*MetadataConfig, error) {
+ var (
+ meta MetadataConfig
+ err error
+ )
+
+ if meta.ProjectID, err = metadata.ProjectID(); err != nil {
+ return nil, err
+ }
+
+ if meta.Name, err = metadata.InstanceName(); err != nil {
+ return nil, err
+ }
+
+ instanceType, err := metadata.Get("instance/machine-type")
+ if err != nil {
+ return nil, err
+ }
+
+ meta.InstanceType = strings.TrimSpace(instanceType[strings.LastIndex(instanceType, "/")+1:])
+
+ if meta.InstanceID, err = metadata.InstanceID(); err != nil {
+ return nil, err
+ }
+
+ if meta.Hostname, err = metadata.Hostname(); err != nil {
+ return nil, err
+ }
+
+ if meta.Zone, err = metadata.Zone(); err != nil {
+ return nil, err
+ }
+
+ if meta.PublicIPv4, err = metadata.ExternalIP(); err != nil {
+ if _, ok := err.(metadata.NotDefinedError); !ok {
+ return nil, err
+ }
+ }
+
+ return &meta, nil
+}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/testdata/expected.yaml b/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/testdata/expected.yaml
new file mode 100644
index 000000000..105e77f3a
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/testdata/expected.yaml
@@ -0,0 +1,25 @@
+addresses: []
+links: []
+routes: []
+hostnames:
+ - hostname: talos
+ domainname: ""
+ layer: platform
+resolvers:
+ - dnsServers:
+ - 169.254.169.254
+ layer: platform
+timeServers:
+ - timeServers:
+ - metadata.google.internal
+ layer: platform
+operators: []
+externalIPs: []
+metadata:
+ platform: gcp
+ hostname: talos
+ region: us-central1
+ zone: us-central1-a
+ instanceType: n1-standard-1
+ instanceId: "0"
+ providerId: gce://123/us-central1-a/my-server
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/testdata/metadata.json b/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/testdata/metadata.json
new file mode 100644
index 000000000..b579ce73d
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/testdata/metadata.json
@@ -0,0 +1,8 @@
+{
+ "project-id": "123",
+ "hostname": "talos",
+ "id": "0",
+ "zone": "us-central1-a",
+ "name": "my-server",
+ "machine-type": "n1-standard-1"
+}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/hcloud.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/hcloud.go
index 230a6e383..80c740d43 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/hcloud.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/hcloud.go
@@ -7,7 +7,6 @@ package hcloud
import (
"context"
- stderrors "errors"
"fmt"
"log"
"net/netip"
@@ -21,40 +20,9 @@ import (
"github.com/talos-systems/talos/pkg/download"
"github.com/talos-systems/talos/pkg/machinery/nethelpers"
"github.com/talos-systems/talos/pkg/machinery/resources/network"
+ runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime"
)
-const (
- // HCloudExternalIPEndpoint is the local hcloud endpoint for the external IP.
- HCloudExternalIPEndpoint = "http://169.254.169.254/hetzner/v1/metadata/public-ipv4"
-
- // HCloudNetworkEndpoint is the local hcloud endpoint for the network-config.
- HCloudNetworkEndpoint = "http://169.254.169.254/hetzner/v1/metadata/network-config"
-
- // HCloudHostnameEndpoint is the local hcloud endpoint for the hostname.
- HCloudHostnameEndpoint = "http://169.254.169.254/hetzner/v1/metadata/hostname"
-
- // HCloudUserDataEndpoint is the local hcloud endpoint for the config.
- HCloudUserDataEndpoint = "http://169.254.169.254/hetzner/v1/userdata"
-)
-
-// NetworkConfig holds hcloud network-config info.
-type NetworkConfig struct {
- Version int `yaml:"version"`
- Config []struct {
- Mac string `yaml:"mac_address"`
- Interfaces string `yaml:"name"`
- Subnets []struct {
- NameServers []string `yaml:"dns_nameservers,omitempty"`
- Address string `yaml:"address,omitempty"`
- Gateway string `yaml:"gateway,omitempty"`
- Ipv4 bool `yaml:"ipv4,omitempty"`
- Ipv6 bool `yaml:"ipv6,omitempty"`
- Type string `yaml:"type"`
- } `yaml:"subnets"`
- Type string `yaml:"type"`
- } `yaml:"config"`
-}
-
// Hcloud is the concrete type that implements the runtime.Platform interface.
type Hcloud struct{}
@@ -66,23 +34,23 @@ func (h *Hcloud) Name() string {
// ParseMetadata converts HCloud metadata to platform network configuration.
//
//nolint:gocyclo
-func (h *Hcloud) ParseMetadata(unmarshalledNetworkConfig *NetworkConfig, host, extIP []byte) (*runtime.PlatformNetworkConfig, error) {
+func (h *Hcloud) ParseMetadata(unmarshalledNetworkConfig *NetworkConfig, metadata *MetadataConfig) (*runtime.PlatformNetworkConfig, error) {
networkConfig := &runtime.PlatformNetworkConfig{}
- if len(host) > 0 {
+ if metadata.Hostname != "" {
hostnameSpec := network.HostnameSpecSpec{
ConfigLayer: network.ConfigPlatform,
}
- if err := hostnameSpec.ParseFQDN(string(host)); err != nil {
+ if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil {
return nil, err
}
networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec)
}
- if len(extIP) > 0 {
- if ip, err := netip.ParseAddr(string(extIP)); err == nil {
+ if metadata.PublicIPv4 != "" {
+ if ip, err := netip.ParseAddr(metadata.PublicIPv4); err == nil {
networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip)
}
}
@@ -156,6 +124,13 @@ func (h *Hcloud) ParseMetadata(unmarshalledNetworkConfig *NetworkConfig, host, e
}
}
+ networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{
+ Platform: h.Name(),
+ Hostname: metadata.Hostname,
+ InstanceID: metadata.InstanceID,
+ ProviderID: fmt.Sprintf("hcloud://%s", metadata.InstanceID),
+ }
+
return networkConfig, nil
}
@@ -181,9 +156,12 @@ func (h *Hcloud) KernelArgs() procfs.Parameters {
}
// NetworkConfiguration implements the runtime.Platform interface.
-//
-//nolint:gocyclo
func (h *Hcloud) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error {
+ metadata, err := h.getMetadata(ctx)
+ if err != nil {
+ return err
+ }
+
log.Printf("fetching hcloud network config from: %q", HCloudNetworkEndpoint)
metadataNetworkConfig, err := download.Download(ctx, HCloudNetworkEndpoint)
@@ -201,25 +179,7 @@ func (h *Hcloud) NetworkConfiguration(ctx context.Context, _ state.State, ch cha
return fmt.Errorf("network-config metadata version=%d is not supported", unmarshalledNetworkConfig.Version)
}
- log.Printf("fetching hostname from: %q", HCloudHostnameEndpoint)
-
- host, err := download.Download(ctx, HCloudHostnameEndpoint,
- download.WithErrorOnNotFound(errors.ErrNoHostname),
- download.WithErrorOnEmptyResponse(errors.ErrNoHostname))
- if err != nil && !stderrors.Is(err, errors.ErrNoHostname) {
- return err
- }
-
- log.Printf("fetching externalIP from: %q", HCloudExternalIPEndpoint)
-
- extIP, err := download.Download(ctx, HCloudExternalIPEndpoint,
- download.WithErrorOnNotFound(errors.ErrNoExternalIPs),
- download.WithErrorOnEmptyResponse(errors.ErrNoExternalIPs))
- if err != nil && !stderrors.Is(err, errors.ErrNoExternalIPs) {
- return err
- }
-
- networkConfig, err := h.ParseMetadata(&unmarshalledNetworkConfig, host, extIP)
+ networkConfig, err := h.ParseMetadata(&unmarshalledNetworkConfig, metadata)
if err != nil {
return err
}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/hcloud_test.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/hcloud_test.go
index e13a90075..741f8f135 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/hcloud_test.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/hcloud_test.go
@@ -6,7 +6,6 @@ package hcloud_test
import (
_ "embed"
- "fmt"
"testing"
"github.com/stretchr/testify/assert"
@@ -25,17 +24,21 @@ var expectedNetworkConfig string
func TestParseMetadata(t *testing.T) {
h := &hcloud.Hcloud{}
+ metadata := &hcloud.MetadataConfig{
+ Hostname: "talos.fqdn",
+ PublicIPv4: "1.2.3.4",
+ InstanceID: "0",
+ }
+
var m hcloud.NetworkConfig
require.NoError(t, yaml.Unmarshal(rawMetadata, &m))
- networkConfig, err := h.ParseMetadata(&m, []byte("some.fqdn"), []byte("1.2.3.4"))
+ networkConfig, err := h.ParseMetadata(&m, metadata)
require.NoError(t, err)
marshaled, err := yaml.Marshal(networkConfig)
require.NoError(t, err)
- fmt.Print(string(marshaled))
-
assert.Equal(t, expectedNetworkConfig, string(marshaled))
}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/metadata.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/metadata.go
new file mode 100644
index 000000000..fe8d3ce79
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/metadata.go
@@ -0,0 +1,99 @@
+// 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 hcloud
+
+import (
+ "context"
+ stderrors "errors"
+ "log"
+
+ "github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors"
+ "github.com/talos-systems/talos/pkg/download"
+)
+
+const (
+ // HCloudExternalIPEndpoint is the local hcloud endpoint for the external IP.
+ HCloudExternalIPEndpoint = "http://169.254.169.254/hetzner/v1/metadata/public-ipv4"
+
+ // HCloudNetworkEndpoint is the local hcloud endpoint for the network-config.
+ HCloudNetworkEndpoint = "http://169.254.169.254/hetzner/v1/metadata/network-config"
+
+ // HCloudHostnameEndpoint is the local hcloud endpoint for the hostname.
+ HCloudHostnameEndpoint = "http://169.254.169.254/hetzner/v1/metadata/hostname"
+
+ // HCloudInstanceIDEndpoint is the local hcloud endpoint for the instance-id.
+ HCloudInstanceIDEndpoint = "http://169.254.169.254/hetzner/v1/metadata/instance-id"
+
+ // HCloudRegionEndpoint is the local hcloud endpoint for the region.
+ HCloudRegionEndpoint = "http://169.254.169.254/hetzner/v1/metadata/region"
+
+ // HCloudZoneEndpoint is the local hcloud endpoint for the zone.
+ HCloudZoneEndpoint = "http://169.254.169.254/hetzner/v1/metadata/availability-zone"
+
+ // HCloudUserDataEndpoint is the local hcloud endpoint for the config.
+ HCloudUserDataEndpoint = "http://169.254.169.254/hetzner/v1/userdata"
+)
+
+// MetadataConfig holds meta info.
+type MetadataConfig struct {
+ Hostname string `yaml:"hostname,omitempty"`
+ Region string `yaml:"region,omitempty"`
+ AvailabilityZone string `json:"availability-zone,omitempty"`
+ InstanceID string `yaml:"instance-id,omitempty"`
+ PublicIPv4 string `yaml:"public-ipv4,omitempty"`
+}
+
+// NetworkConfig holds hcloud network-config info.
+type NetworkConfig struct {
+ Version int `yaml:"version"`
+ Config []struct {
+ Mac string `yaml:"mac_address"`
+ Interfaces string `yaml:"name"`
+ Subnets []struct {
+ NameServers []string `yaml:"dns_nameservers,omitempty"`
+ Address string `yaml:"address,omitempty"`
+ Gateway string `yaml:"gateway,omitempty"`
+ Ipv4 bool `yaml:"ipv4,omitempty"`
+ Ipv6 bool `yaml:"ipv6,omitempty"`
+ Type string `yaml:"type"`
+ } `yaml:"subnets"`
+ Type string `yaml:"type"`
+ } `yaml:"config"`
+}
+
+func (h *Hcloud) getMetadata(ctx context.Context) (*MetadataConfig, error) {
+ log.Printf("fetching hostname from: %q", HCloudHostnameEndpoint)
+
+ host, err := download.Download(ctx, HCloudHostnameEndpoint,
+ download.WithErrorOnNotFound(errors.ErrNoHostname),
+ download.WithErrorOnEmptyResponse(errors.ErrNoHostname))
+ if err != nil && !stderrors.Is(err, errors.ErrNoHostname) {
+ return nil, err
+ }
+
+ log.Printf("fetching instance-id from: %q", HCloudInstanceIDEndpoint)
+
+ instanceID, err := download.Download(ctx, HCloudInstanceIDEndpoint,
+ download.WithErrorOnNotFound(errors.ErrNoHostname),
+ download.WithErrorOnEmptyResponse(errors.ErrNoHostname))
+ if err != nil && !stderrors.Is(err, errors.ErrNoHostname) {
+ return nil, err
+ }
+
+ log.Printf("fetching externalIP from: %q", HCloudExternalIPEndpoint)
+
+ extIP, err := download.Download(ctx, HCloudExternalIPEndpoint,
+ download.WithErrorOnNotFound(errors.ErrNoExternalIPs),
+ download.WithErrorOnEmptyResponse(errors.ErrNoExternalIPs))
+ if err != nil && !stderrors.Is(err, errors.ErrNoExternalIPs) {
+ return nil, err
+ }
+
+ return &MetadataConfig{
+ Hostname: string(host),
+ InstanceID: string(instanceID),
+ PublicIPv4: string(extIP),
+ }, nil
+}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/testdata/expected.yaml b/internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/testdata/expected.yaml
index b6ae357c3..7fa86026e 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/testdata/expected.yaml
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/testdata/expected.yaml
@@ -26,7 +26,7 @@ routes:
protocol: static
layer: platform
hostnames:
- - hostname: some
+ - hostname: talos
domainname: fqdn
layer: platform
resolvers: []
@@ -40,3 +40,8 @@ operators:
layer: platform
externalIPs:
- 1.2.3.4
+metadata:
+ platform: hcloud
+ hostname: talos.fqdn
+ instanceId: "0"
+ providerId: hcloud://0
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/match.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/match.go
new file mode 100644
index 000000000..2bb028347
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/match.go
@@ -0,0 +1,59 @@
+// 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 metal
+
+import "regexp"
+
+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
+}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/metal.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/metal.go
index 8b4d87c4d..e6b7ec56a 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/metal.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/metal.go
@@ -9,15 +9,9 @@ import (
"context"
"fmt"
"log"
- "net/url"
"os"
"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/siderolabs/go-blockdevice/blockdevice/filesystem"
"github.com/siderolabs/go-blockdevice/blockdevice/probe"
@@ -28,8 +22,7 @@ import (
"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"
+ runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime"
)
const (
@@ -74,221 +67,6 @@ func (m *Metal) Configuration(ctx context.Context, r state.State) ([]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.
-//
-//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", populatedURL, err)
- }
-
- values := u.Query()
-
- 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 fmt.Errorf("error while substituting UUID: %w", err)
- }
-
- values.Set(constants.UUIDKey, uid)
-
- continue
- }
-
- 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()
-
- return u.String(), nil
-}
-
-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 "", fmt.Errorf("failed to watch %s resources: %w", typ, err)
- }
-
- 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.
func (m *Metal) Mode() runtime.Mode {
return runtime.ModeMetal
@@ -336,6 +114,28 @@ func (m *Metal) KernelArgs() procfs.Parameters {
}
// NetworkConfiguration implements the runtime.Platform interface.
-func (m *Metal) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error {
+func (m *Metal) NetworkConfiguration(ctx context.Context, st state.State, ch chan<- *runtime.PlatformNetworkConfig) error {
+ var metadata runtimeres.PlatformMetadataSpec
+
+ metadata.Platform = m.Name()
+
+ if option := procfs.ProcCmdline().Get(constants.KernelParamHostname).First(); option != nil {
+ metadata.Hostname = *option
+ }
+
+ if uuid, err := getSystemUUID(ctx, st); err == nil {
+ metadata.InstanceID = uuid
+ }
+
+ networkConfig := &runtime.PlatformNetworkConfig{
+ Metadata: &metadata,
+ }
+
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ case ch <- networkConfig:
+ }
+
return nil
}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/metal_test.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/metal_test.go
index b7c7ce6c1..48cd29f11 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/metal_test.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/metal_test.go
@@ -6,243 +6,42 @@ package metal_test
import (
"context"
- "fmt"
- "net"
- "net/http"
- "net/http/httptest"
- "net/url"
"testing"
"time"
- "github.com/cosi-project/runtime/pkg/resource"
"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/go-procfs/procfs"
+ "github.com/stretchr/testify/require"
+ "github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/metal"
- "github.com/talos-systems/talos/pkg/machinery/constants"
- "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 createOrUpdate(ctx context.Context, st state.State, r resource.Resource) error {
- oldRes, err := st.Get(ctx, r.Metadata())
- if err != nil && !state.IsNotFoundError(err) {
- return err
- }
+func TestNetworkConfig(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ t.Cleanup(cancel)
- if oldRes == nil {
- err = st.Create(ctx, r)
- if err != nil {
- return err
- }
- } else {
- r.Metadata().SetVersion(oldRes.Metadata().Version())
+ p := &metal.Metal{}
- err = st.Update(ctx, r)
- if err != nil {
- return err
- }
- }
+ ch := make(chan *runtime.PlatformNetworkConfig, 1)
- return nil
-}
-
-func setup(ctx context.Context, t *testing.T, st state.State, mockUUID, mockSerialNumber, mockHostname, mockMAC string) {
- testID := "testID"
- sysInfo := hardware.NewSystemInformation(testID)
- sysInfo.TypedSpec().UUID = mockUUID
- sysInfo.TypedSpec().SerialNumber = mockSerialNumber
- assert.NoError(t, createOrUpdate(ctx, st, sysInfo))
-
- hostnameSpec := network.NewHostnameSpec(network.NamespaceName, testID)
- hostnameSpec.TypedSpec().Hostname = mockHostname
- assert.NoError(t, createOrUpdate(ctx, st, 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, createOrUpdate(ctx, st, linkStatusSpec))
-}
-
-func TestPopulateURLParameters(t *testing.T) {
- mockUUID := "40dcbd19-3b10-444e-bfff-aaee44a51fda"
-
- mockMAC := "52:2f:fd:df:fc:c0"
-
- mockSerialNumber := "0OCZJ19N65"
-
- mockHostname := "myTestHostname"
-
- for _, tt := range []struct {
- name string
- url string
- expectedURL string
- expectedError string
- }{
- {
- name: "no uuid",
- url: "http://example.com/metadata",
- expectedURL: "http://example.com/metadata",
- },
- {
- name: "empty uuid",
- url: "http://example.com/metadata?uuid=",
- expectedURL: fmt.Sprintf("http://example.com/metadata?uuid=%s", mockUUID),
- },
- {
- name: "uuid present",
- 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",
- expectedURL: "http://example.com/metadata?foo=a",
- },
- {
- name: "multiple uuids",
- 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) {
- ctx := context.Background()
-
- st := state.WrapCore(namespaced.NewState(inmem.Build))
-
- setup(ctx, t, st, mockUUID, mockSerialNumber, mockHostname, mockMAC)
-
- output, err := metal.PopulateURLParameters(ctx, tt.url, st)
-
- if tt.expectedError != "" {
- assert.EqualError(t, err, tt.expectedError)
- } else {
- u, err := url.Parse(tt.expectedURL)
- assert.NoError(t, err)
- u.RawQuery = u.Query().Encode()
- assert.Equal(t, u.String(), output)
- }
- })
- }
-}
-
-func TestRepopulateOnRetry(t *testing.T) {
st := state.WrapCore(namespaced.NewState(inmem.Build))
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
- defer cancel()
+ uuid := hardware.NewSystemInformation("test")
+ uuid.TypedSpec().UUID = "0123-4567-89ab-cdef"
+ require.NoError(t, st.Create(ctx, uuid))
- nCalls := 0
+ err := p.NetworkConfiguration(ctx, st, ch)
+ require.NoError(t, err)
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- switch nCalls {
- case 0:
- assert.Equal(t, "h=myTestHostname&m=52%3A2f%3Afd%3Adf%3Afc%3Ac0&s=0OCZJ19N65&u=40dcbd19-3b10-444e-bfff-aaee44a51fda", r.URL.RawQuery)
- w.WriteHeader(http.StatusNotFound)
-
- // After the first call we change the resources that should be substituted in the next call.
- uuid2 := "9fba530f-767d-40f9-9410-bb1fed5d2134"
- mac2 := "aa:aa:bb:bb:cc:cc"
- serialNumber2 := "111AAA9N65"
- hostname2 := "anotherHostname"
-
- setup(ctx, t, st, uuid2, serialNumber2, hostname2, mac2)
- case 1:
- // Before the second call Configuration() should have resubstituted all the new parameters in the URL.
- assert.Equal(t, "h=anotherHostname&m=aa%3Aaa%3Abb%3Abb%3Acc%3Acc&s=111AAA9N65&u=9fba530f-767d-40f9-9410-bb1fed5d2134", r.URL.RawQuery)
- w.WriteHeader(http.StatusOK)
- }
-
- nCalls++
- }))
- defer server.Close()
-
- uuid1 := "40dcbd19-3b10-444e-bfff-aaee44a51fda"
- mac1 := "52:2f:fd:df:fc:c0"
- serialNumber1 := "0OCZJ19N65"
- hostname1 := "myTestHostname"
-
- setup(ctx, t, st, uuid1, serialNumber1, hostname1, mac1)
-
- downloadURL := server.URL + "/metadata?h=${hostname}&m=${mac}&s=${serial}&u=${uuid}"
-
- param := procfs.NewParameter(constants.KernelParamConfig)
- param.Append(downloadURL)
-
- procfs.ProcCmdline().Set(constants.KernelParamConfig, param)
- defer procfs.ProcCmdline().Set(constants.KernelParamConfig, nil)
-
- go func() {
- testObj := metal.Metal{}
- _, err := testObj.Configuration(ctx, st)
- assert.NoError(t, err)
-
- cancel()
- }()
-
- <-ctx.Done()
+ select {
+ case <-ctx.Done():
+ t.Error("timeout")
+ case cfg := <-ch:
+ assert.Equal(t, "metal", cfg.Metadata.Platform)
+ assert.Equal(t, uuid.TypedSpec().UUID, cfg.Metadata.InstanceID)
+ }
}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url.go
new file mode 100644
index 000000000..4dec9578c
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url.go
@@ -0,0 +1,184 @@
+// 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 metal
+
+import (
+ "context"
+ "fmt"
+ "net/url"
+ "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/talos/pkg/machinery/constants"
+ hardwareResource "github.com/talos-systems/talos/pkg/machinery/resources/hardware"
+ "github.com/talos-systems/talos/pkg/machinery/resources/network"
+)
+
+// PopulateURLParameters fills in empty parameters in the download URL.
+//
+//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", populatedURL, err)
+ }
+
+ values := u.Query()
+
+ 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 fmt.Errorf("error while substituting UUID: %w", err)
+ }
+
+ values.Set(constants.UUIDKey, uid)
+
+ continue
+ }
+
+ 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()
+
+ return u.String(), nil
+}
+
+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(watchCtx, r, metadata, events, state.WithBootstrapContents(true))
+ if err != nil {
+ return "", fmt.Errorf("failed to watch %s resources: %w", typ, err)
+ }
+
+ 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)
+}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url_test.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url_test.go
new file mode 100644
index 000000000..b7c7ce6c1
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url_test.go
@@ -0,0 +1,248 @@
+// 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 metal_test
+
+import (
+ "context"
+ "fmt"
+ "net"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "testing"
+ "time"
+
+ "github.com/cosi-project/runtime/pkg/resource"
+ "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/go-procfs/procfs"
+
+ "github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/metal"
+ "github.com/talos-systems/talos/pkg/machinery/constants"
+ "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 createOrUpdate(ctx context.Context, st state.State, r resource.Resource) error {
+ oldRes, err := st.Get(ctx, r.Metadata())
+ if err != nil && !state.IsNotFoundError(err) {
+ return err
+ }
+
+ if oldRes == nil {
+ err = st.Create(ctx, r)
+ if err != nil {
+ return err
+ }
+ } else {
+ r.Metadata().SetVersion(oldRes.Metadata().Version())
+
+ err = st.Update(ctx, r)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func setup(ctx context.Context, t *testing.T, st state.State, mockUUID, mockSerialNumber, mockHostname, mockMAC string) {
+ testID := "testID"
+ sysInfo := hardware.NewSystemInformation(testID)
+ sysInfo.TypedSpec().UUID = mockUUID
+ sysInfo.TypedSpec().SerialNumber = mockSerialNumber
+ assert.NoError(t, createOrUpdate(ctx, st, sysInfo))
+
+ hostnameSpec := network.NewHostnameSpec(network.NamespaceName, testID)
+ hostnameSpec.TypedSpec().Hostname = mockHostname
+ assert.NoError(t, createOrUpdate(ctx, st, 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, createOrUpdate(ctx, st, linkStatusSpec))
+}
+
+func TestPopulateURLParameters(t *testing.T) {
+ mockUUID := "40dcbd19-3b10-444e-bfff-aaee44a51fda"
+
+ mockMAC := "52:2f:fd:df:fc:c0"
+
+ mockSerialNumber := "0OCZJ19N65"
+
+ mockHostname := "myTestHostname"
+
+ for _, tt := range []struct {
+ name string
+ url string
+ expectedURL string
+ expectedError string
+ }{
+ {
+ name: "no uuid",
+ url: "http://example.com/metadata",
+ expectedURL: "http://example.com/metadata",
+ },
+ {
+ name: "empty uuid",
+ url: "http://example.com/metadata?uuid=",
+ expectedURL: fmt.Sprintf("http://example.com/metadata?uuid=%s", mockUUID),
+ },
+ {
+ name: "uuid present",
+ 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",
+ expectedURL: "http://example.com/metadata?foo=a",
+ },
+ {
+ name: "multiple uuids",
+ 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) {
+ ctx := context.Background()
+
+ st := state.WrapCore(namespaced.NewState(inmem.Build))
+
+ setup(ctx, t, st, mockUUID, mockSerialNumber, mockHostname, mockMAC)
+
+ output, err := metal.PopulateURLParameters(ctx, tt.url, st)
+
+ if tt.expectedError != "" {
+ assert.EqualError(t, err, tt.expectedError)
+ } else {
+ u, err := url.Parse(tt.expectedURL)
+ assert.NoError(t, err)
+ u.RawQuery = u.Query().Encode()
+ assert.Equal(t, u.String(), output)
+ }
+ })
+ }
+}
+
+func TestRepopulateOnRetry(t *testing.T) {
+ st := state.WrapCore(namespaced.NewState(inmem.Build))
+
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+
+ nCalls := 0
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ switch nCalls {
+ case 0:
+ assert.Equal(t, "h=myTestHostname&m=52%3A2f%3Afd%3Adf%3Afc%3Ac0&s=0OCZJ19N65&u=40dcbd19-3b10-444e-bfff-aaee44a51fda", r.URL.RawQuery)
+ w.WriteHeader(http.StatusNotFound)
+
+ // After the first call we change the resources that should be substituted in the next call.
+ uuid2 := "9fba530f-767d-40f9-9410-bb1fed5d2134"
+ mac2 := "aa:aa:bb:bb:cc:cc"
+ serialNumber2 := "111AAA9N65"
+ hostname2 := "anotherHostname"
+
+ setup(ctx, t, st, uuid2, serialNumber2, hostname2, mac2)
+ case 1:
+ // Before the second call Configuration() should have resubstituted all the new parameters in the URL.
+ assert.Equal(t, "h=anotherHostname&m=aa%3Aaa%3Abb%3Abb%3Acc%3Acc&s=111AAA9N65&u=9fba530f-767d-40f9-9410-bb1fed5d2134", r.URL.RawQuery)
+ w.WriteHeader(http.StatusOK)
+ }
+
+ nCalls++
+ }))
+ defer server.Close()
+
+ uuid1 := "40dcbd19-3b10-444e-bfff-aaee44a51fda"
+ mac1 := "52:2f:fd:df:fc:c0"
+ serialNumber1 := "0OCZJ19N65"
+ hostname1 := "myTestHostname"
+
+ setup(ctx, t, st, uuid1, serialNumber1, hostname1, mac1)
+
+ downloadURL := server.URL + "/metadata?h=${hostname}&m=${mac}&s=${serial}&u=${uuid}"
+
+ param := procfs.NewParameter(constants.KernelParamConfig)
+ param.Append(downloadURL)
+
+ procfs.ProcCmdline().Set(constants.KernelParamConfig, param)
+ defer procfs.ProcCmdline().Set(constants.KernelParamConfig, nil)
+
+ go func() {
+ testObj := metal.Metal{}
+ _, err := testObj.Configuration(ctx, st)
+ assert.NoError(t, err)
+
+ cancel()
+ }()
+
+ <-ctx.Done()
+}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/metadata.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/metadata.go
index 1f2c0d259..e96fa7bf1 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/metadata.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/metadata.go
@@ -19,6 +19,7 @@ import (
"github.com/siderolabs/go-blockdevice/blockdevice/filesystem"
"github.com/siderolabs/go-blockdevice/blockdevice/probe"
"golang.org/x/sys/unix"
+ yaml "gopkg.in/yaml.v3"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors"
@@ -59,8 +60,8 @@ type NetworkConfig struct {
// Ethernet holds network interface info.
type Ethernet struct {
Match struct {
- Name []string `yaml:"name,omitempty"`
- HWAddr []string `yaml:"macaddress,omitempty"`
+ Name string `yaml:"name,omitempty"`
+ HWAddr string `yaml:"macaddress,omitempty"`
} `yaml:"match,omitempty"`
DHCPv4 bool `yaml:"dhcp4,omitempty"`
DHCPv6 bool `yaml:"dhcp6,omitempty"`
@@ -179,14 +180,16 @@ 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) {
+func (n *Nocloud) acquireConfig(ctx context.Context) (metadataConfigDl, metadataNetworkConfigDl, machineConfigDl []byte, metadata *MetadataConfig, err error) {
s, err := smbios.GetSMBIOSInfo()
if err != nil {
- return nil, nil, nil, "", err
+ return nil, nil, nil, nil, err
}
- metaBaseURL := ""
- networkSource := false
+ var (
+ metaBaseURL, hostname string
+ networkSource bool
+ )
options := strings.Split(s.SystemInformation.SerialNumber, ";")
for _, option := range options {
@@ -220,7 +223,17 @@ func (n *Nocloud) acquireConfig(ctx context.Context) (metadataConfigDl, metadata
metadataConfigDl, metadataNetworkConfigDl, machineConfigDl, err = n.configFromCD()
}
- return metadataConfigDl, metadataNetworkConfigDl, machineConfigDl, hostname, err
+ metadata = &MetadataConfig{}
+
+ if metadataConfigDl != nil {
+ _ = yaml.Unmarshal(metadataConfigDl, metadata) //nolint:errcheck
+ }
+
+ if hostname != "" {
+ metadata.Hostname = hostname
+ }
+
+ return metadataConfigDl, metadataNetworkConfigDl, machineConfigDl, metadata, err
}
//nolint:gocyclo
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/nocloud.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/nocloud.go
index 72ae5b5f5..a12a08b33 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/nocloud.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/nocloud.go
@@ -17,6 +17,7 @@ import (
"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/machinery/resources/network"
+ runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime"
)
// Nocloud is the concrete type that implements the runtime.Platform interface.
@@ -28,15 +29,15 @@ func (n *Nocloud) Name() string {
}
// ParseMetadata converts nocloud metadata to platform network config.
-func (n *Nocloud) ParseMetadata(unmarshalledNetworkConfig *NetworkConfig, hostname string) (*runtime.PlatformNetworkConfig, error) {
+func (n *Nocloud) ParseMetadata(unmarshalledNetworkConfig *NetworkConfig, metadata *MetadataConfig) (*runtime.PlatformNetworkConfig, error) {
networkConfig := &runtime.PlatformNetworkConfig{}
- if hostname != "" {
+ if metadata.Hostname != "" {
hostnameSpec := network.HostnameSpecSpec{
ConfigLayer: network.ConfigPlatform,
}
- if err := hostnameSpec.ParseFQDN(hostname); err != nil {
+ if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil {
return nil, err
}
@@ -56,6 +57,12 @@ func (n *Nocloud) ParseMetadata(unmarshalledNetworkConfig *NetworkConfig, hostna
return nil, fmt.Errorf("network-config metadata version=%d is not supported", unmarshalledNetworkConfig.Version)
}
+ networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{
+ Platform: n.Name(),
+ Hostname: metadata.Hostname,
+ InstanceID: metadata.InstanceID,
+ }
+
return networkConfig, nil
}
@@ -89,7 +96,7 @@ func (n *Nocloud) KernelArgs() procfs.Parameters {
//
//nolint:gocyclo
func (n *Nocloud) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error {
- metadataConfigDl, metadataNetworkConfigDl, _, hostname, err := n.acquireConfig(ctx)
+ metadataConfigDl, metadataNetworkConfigDl, _, metadata, err := n.acquireConfig(ctx)
if stderrors.Is(err, errors.ErrNoConfigSource) {
err = nil
}
@@ -98,31 +105,19 @@ func (n *Nocloud) NetworkConfiguration(ctx context.Context, _ state.State, ch ch
return err
}
- if metadataConfigDl == nil && metadataNetworkConfigDl == nil && hostname == "" {
+ if metadataConfigDl == nil && metadataNetworkConfigDl == nil {
// no data, use cached network configuration if available
return nil
}
- var (
- unmarshalledMetadataConfig MetadataConfig
- unmarshalledNetworkConfig NetworkConfig
- )
-
- if metadataConfigDl != nil {
- _ = yaml.Unmarshal(metadataConfigDl, &unmarshalledMetadataConfig) //nolint:errcheck
- }
-
+ var unmarshalledNetworkConfig NetworkConfig
if metadataNetworkConfigDl != nil {
if err = yaml.Unmarshal(metadataNetworkConfigDl, &unmarshalledNetworkConfig); err != nil {
return err
}
}
- if hostname == "" {
- hostname = unmarshalledMetadataConfig.Hostname
- }
-
- networkConfig, err := n.ParseMetadata(&unmarshalledNetworkConfig, hostname)
+ networkConfig, err := n.ParseMetadata(&unmarshalledNetworkConfig, metadata)
if err != nil {
return err
}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/nocloud_test.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/nocloud_test.go
index 41695afe1..9858b99fa 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/nocloud_test.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/nocloud_test.go
@@ -32,13 +32,11 @@ func TestParseMetadata(t *testing.T) {
for _, tt := range []struct {
name string
raw []byte
- hostname string
expected string
}{
{
name: "V1",
raw: rawMetadataV1,
- hostname: "talos",
expected: expectedNetworkConfigV1,
},
{
@@ -56,7 +54,12 @@ func TestParseMetadata(t *testing.T) {
require.NoError(t, yaml.Unmarshal(tt.raw, &m))
- networkConfig, err := n.ParseMetadata(&m, tt.hostname)
+ mc := nocloud.MetadataConfig{
+ Hostname: "talos.fqdn",
+ InstanceID: "0",
+ }
+
+ networkConfig, err := n.ParseMetadata(&m, &mc)
require.NoError(t, err)
marshaled, err := yaml.Marshal(networkConfig)
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/expected-v1.yaml b/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/expected-v1.yaml
index 85c365bfe..33162ea50 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/expected-v1.yaml
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/expected-v1.yaml
@@ -46,7 +46,7 @@ routes:
layer: platform
hostnames:
- hostname: talos
- domainname: ""
+ domainname: fqdn
layer: platform
resolvers:
- dnsServers:
@@ -55,3 +55,7 @@ resolvers:
timeServers: []
operators: []
externalIPs: []
+metadata:
+ platform: nocloud
+ hostname: talos.fqdn
+ instanceId: "0"
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/expected-v2.yaml b/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/expected-v2.yaml
index 568ca1b57..ea28b2ab7 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/expected-v2.yaml
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/expected-v2.yaml
@@ -44,7 +44,10 @@ routes:
flags: ""
protocol: static
layer: platform
-hostnames: []
+hostnames:
+ - hostname: talos
+ domainname: fqdn
+ layer: platform
resolvers:
- dnsServers:
- 8.8.8.8
@@ -58,3 +61,7 @@ operators:
routeMetric: 1024
layer: platform
externalIPs: []
+metadata:
+ platform: nocloud
+ hostname: talos.fqdn
+ instanceId: "0"
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/metadata-v2.yaml b/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/metadata-v2.yaml
index 0467d3af5..a6ed77fff 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/metadata-v2.yaml
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/metadata-v2.yaml
@@ -1,6 +1,8 @@
version: 2
ethernets:
eth0:
+ match:
+ macaddress: '00:20:6e:1f:f9:a8'
dhcp4: true
addresses:
- 192.168.14.2/24
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/metadata.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/metadata.go
index 74f72489f..ec5d78a58 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/metadata.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/metadata.go
@@ -32,8 +32,8 @@ const (
// OpenstackExternalIPEndpoint is the local Openstack endpoint for the external IP.
OpenstackExternalIPEndpoint = "http://169.254.169.254/latest/meta-data/public-ipv4"
- // OpenstackHostnameEndpoint is the local Openstack endpoint for the hostname.
- OpenstackHostnameEndpoint = "http://169.254.169.254/latest/meta-data/hostname"
+ // OpenstackInstanceTypeEndpoint is the local Openstack endpoint for the instance-type.
+ OpenstackInstanceTypeEndpoint = "http://169.254.169.254/latest/meta-data/instance-type"
// OpenstackMetaDataEndpoint is the local Openstack endpoint for the meta config.
OpenstackMetaDataEndpoint = "http://169.254.169.254/" + configMetadataPath
// OpenstackNetworkDataEndpoint is the local Openstack endpoint for the network config.
@@ -71,7 +71,11 @@ type NetworkConfig struct {
// MetadataConfig holds meta info.
type MetadataConfig struct {
- Hostname string `json:"hostname,omitempty"`
+ UUID string `json:"uuid,omitempty"`
+ Hostname string `json:"hostname,omitempty"`
+ AvailabilityZone string `json:"availability_zone,omitempty"`
+ ProjectID string `json:"project_id"`
+ InstanceType string `json:"instance_type"`
}
func (o *Openstack) configFromNetwork(ctx context.Context) (metaConfig []byte, networkConfig []byte, machineConfig []byte, err error) {
@@ -158,20 +162,15 @@ func (o *Openstack) configFromCD() (metaConfig []byte, networkConfig []byte, mac
return metaConfig, networkConfig, machineConfig, nil
}
-func (o *Openstack) hostname(ctx context.Context) []byte {
- log.Printf("fetching hostname from: %q", OpenstackHostnameEndpoint)
+func (o *Openstack) instanceType(ctx context.Context) string {
+ log.Printf("fetching instance-type from: %q", OpenstackInstanceTypeEndpoint)
- hostname, err := download.Download(ctx, OpenstackHostnameEndpoint,
- download.WithErrorOnNotFound(errors.ErrNoHostname),
- download.WithErrorOnEmptyResponse(errors.ErrNoHostname))
+ sku, err := download.Download(ctx, OpenstackInstanceTypeEndpoint)
if err != nil {
- // Platform cannot support this endpoint, or returns timeout.
- log.Printf("failed to fetch hostname, ignored: %s", err)
-
- return nil
+ return ""
}
- return hostname
+ return string(sku)
}
func (o *Openstack) externalIPs(ctx context.Context) (addrs []netip.Addr) {
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/openstack.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/openstack.go
index 9988dec7d..51b041cb8 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/openstack.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/openstack.go
@@ -22,6 +22,7 @@ import (
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/utils"
"github.com/talos-systems/talos/pkg/machinery/nethelpers"
"github.com/talos-systems/talos/pkg/machinery/resources/network"
+ runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime"
)
// Openstack is the concrete type that implements the runtime.Platform interface.
@@ -35,19 +36,15 @@ func (o *Openstack) Name() string {
// ParseMetadata converts OpenStack metadata to platform network configuration.
//
//nolint:gocyclo,cyclop
-func (o *Openstack) ParseMetadata(unmarshalledMetadataConfig *MetadataConfig, unmarshalledNetworkConfig *NetworkConfig, hostname string, extIPs []netip.Addr) (*runtime.PlatformNetworkConfig, error) {
+func (o *Openstack) ParseMetadata(unmarshalledNetworkConfig *NetworkConfig, extIPs []netip.Addr, metadata *MetadataConfig) (*runtime.PlatformNetworkConfig, error) {
networkConfig := &runtime.PlatformNetworkConfig{}
- if hostname == "" {
- hostname = unmarshalledMetadataConfig.Hostname
- }
-
- if hostname != "" {
+ if metadata.Hostname != "" {
hostnameSpec := network.HostnameSpecSpec{
ConfigLayer: network.ConfigPlatform,
}
- if err := hostnameSpec.ParseFQDN(hostname); err != nil {
+ if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil {
return nil, err
}
@@ -210,6 +207,15 @@ func (o *Openstack) ParseMetadata(unmarshalledMetadataConfig *MetadataConfig, un
}
}
+ networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{
+ Platform: o.Name(),
+ Hostname: metadata.Hostname,
+ Zone: metadata.AvailabilityZone,
+ InstanceID: metadata.UUID,
+ InstanceType: metadata.InstanceType,
+ ProviderID: fmt.Sprintf("openstack:///%s", metadata.UUID),
+ }
+
return networkConfig, nil
}
@@ -246,6 +252,8 @@ func (o *Openstack) KernelArgs() procfs.Parameters {
// NetworkConfiguration implements the runtime.Platform interface.
func (o *Openstack) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error {
+ networkSource := false
+
metadataConfigDl, metadataNetworkConfigDl, _, err := o.configFromCD()
if err != nil {
metadataConfigDl, metadataNetworkConfigDl, _, err = o.configFromNetwork(ctx)
@@ -256,21 +264,30 @@ func (o *Openstack) NetworkConfiguration(ctx context.Context, _ state.State, ch
if err != nil {
return err
}
+
+ networkSource = true
}
- hostname := o.hostname(ctx)
- extIPs := o.externalIPs(ctx)
-
var (
- unmarshalledMetadataConfig MetadataConfig
- unmarshalledNetworkConfig NetworkConfig
+ meta MetadataConfig
+ unmarshalledNetworkConfig NetworkConfig
)
// ignore errors unmarshaling, empty configs work just fine as empty default
- _ = json.Unmarshal(metadataConfigDl, &unmarshalledMetadataConfig) //nolint:errcheck
+ _ = json.Unmarshal(metadataConfigDl, &meta) //nolint:errcheck
_ = json.Unmarshal(metadataNetworkConfigDl, &unmarshalledNetworkConfig) //nolint:errcheck
- networkConfig, err := o.ParseMetadata(&unmarshalledMetadataConfig, &unmarshalledNetworkConfig, string(hostname), extIPs)
+ var extIPs []netip.Addr
+
+ if networkSource {
+ extIPs = o.externalIPs(ctx)
+
+ if meta.InstanceType == "" {
+ meta.InstanceType = o.instanceType(ctx)
+ }
+ }
+
+ networkConfig, err := o.ParseMetadata(&unmarshalledNetworkConfig, extIPs, &meta)
if err != nil {
return err
}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/openstack_test.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/openstack_test.go
index a5780487e..7d6e46023 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/openstack_test.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/openstack_test.go
@@ -29,15 +29,15 @@ var expectedNetworkConfig string
func TestParseMetadata(t *testing.T) {
o := &openstack.Openstack{}
- var m openstack.MetadataConfig
+ var metadata openstack.MetadataConfig
- require.NoError(t, json.Unmarshal(rawMetadata, &m))
+ require.NoError(t, json.Unmarshal(rawMetadata, &metadata))
var n openstack.NetworkConfig
require.NoError(t, json.Unmarshal(rawNetwork, &n))
- networkConfig, err := o.ParseMetadata(&m, &n, "", []netip.Addr{netip.MustParseAddr("1.2.3.4")})
+ networkConfig, err := o.ParseMetadata(&n, []netip.Addr{netip.MustParseAddr("1.2.3.4")}, &metadata)
require.NoError(t, err)
marshaled, err := yaml.Marshal(networkConfig)
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/testdata/expected.yaml b/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/testdata/expected.yaml
index 162b9d17d..5b74e9fdf 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/testdata/expected.yaml
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/testdata/expected.yaml
@@ -133,3 +133,9 @@ operators:
layer: platform
externalIPs:
- 1.2.3.4
+metadata:
+ platform: openstack
+ hostname: talos
+ zone: nova
+ instanceId: 39073b0a-1234-1234-1234-5e76a4bd64b2
+ providerId: openstack:///39073b0a-1234-1234-1234-5e76a4bd64b2
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/metadata.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/metadata.go
new file mode 100644
index 000000000..1f2c42f44
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/metadata.go
@@ -0,0 +1,51 @@
+// 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/json"
+ "fmt"
+
+ "github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors"
+ "github.com/talos-systems/talos/pkg/download"
+)
+
+// Ref: https://docs.oracle.com/en-us/iaas/Content/Compute/Tasks/gettingmetadata.htm
+const (
+ // OracleMetadataEndpoint is the local metadata endpoint for the hostname.
+ OracleMetadataEndpoint = "http://169.254.169.254/opc/v2/instance/"
+ // 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/"
+)
+
+// MetadataConfig represents a metadata Oracle instance.
+type MetadataConfig struct {
+ Hostname string `json:"hostname,omitempty"`
+ ID string `json:"id,omitempty"`
+ Region string `json:"region,omitempty"`
+ AvailabilityDomain string `json:"availabilityDomain,omitempty"`
+ FaultDomain string `json:"faultDomain,omitempty"`
+ Shape string `json:"shape,omitempty"`
+}
+
+func (o *Oracle) getMetadata(ctx context.Context) (*MetadataConfig, error) {
+ metaConfigDl, err := download.Download(ctx, OracleMetadataEndpoint, //nolint:errcheck
+ download.WithHeaders(map[string]string{"Authorization": "Bearer Oracle"}),
+ download.WithErrorOnNotFound(errors.ErrNoHostname),
+ download.WithErrorOnEmptyResponse(errors.ErrNoHostname))
+ if err != nil {
+ return nil, fmt.Errorf("error fetching metadata: %w", err)
+ }
+
+ var meta MetadataConfig
+ if err = json.Unmarshal(metaConfigDl, &meta); err != nil {
+ return nil, err
+ }
+
+ return &meta, nil
+}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/oracle.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/oracle.go
index 37f9d5915..a86370d08 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/oracle.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/oracle.go
@@ -11,6 +11,7 @@ import (
"encoding/json"
"fmt"
"log"
+ "strings"
"github.com/cosi-project/runtime/pkg/state"
"github.com/talos-systems/go-procfs/procfs"
@@ -19,16 +20,7 @@ import (
"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/resources/network"
-)
-
-// 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/"
+ runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime"
)
// NetworkConfig holds network interface meta config.
@@ -50,15 +42,15 @@ func (o *Oracle) Name() string {
}
// ParseMetadata converts Oracle Cloud metadata into platform network configuration.
-func (o *Oracle) ParseMetadata(interfaceAddresses []NetworkConfig, hostname string) (*runtime.PlatformNetworkConfig, error) {
+func (o *Oracle) ParseMetadata(interfaceAddresses []NetworkConfig, metadata *MetadataConfig) (*runtime.PlatformNetworkConfig, error) {
networkConfig := &runtime.PlatformNetworkConfig{}
- if hostname != "" {
+ if metadata.Hostname != "" {
hostnameSpec := network.HostnameSpecSpec{
ConfigLayer: network.ConfigPlatform,
}
- if err := hostnameSpec.ParseFQDN(hostname); err != nil {
+ if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil {
return nil, err
}
@@ -81,6 +73,22 @@ func (o *Oracle) ParseMetadata(interfaceAddresses []NetworkConfig, hostname stri
}
}
+ zone := metadata.AvailabilityDomain
+
+ if idx := strings.LastIndex(zone, ":"); idx != -1 {
+ zone = zone[:idx]
+ }
+
+ networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{
+ Platform: o.Name(),
+ Hostname: metadata.Hostname,
+ Region: strings.ToLower(metadata.Region),
+ Zone: strings.ToLower(zone),
+ InstanceType: metadata.Shape,
+ InstanceID: metadata.ID,
+ ProviderID: fmt.Sprintf("oci://%s", metadata.ID),
+ }
+
return networkConfig, nil
}
@@ -118,28 +126,28 @@ func (o *Oracle) KernelArgs() procfs.Parameters {
// NetworkConfiguration implements the runtime.Platform interface.
func (o *Oracle) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error {
+ log.Printf("fetching oracle metadata from: %q", OracleMetadataEndpoint)
+
+ metadata, err := o.getMetadata(ctx)
+ if err != nil {
+ return err
+ }
+
log.Printf("fetching network config from %q", OracleNetworkEndpoint)
- metadataNetworkConfig, err := download.Download(ctx, OracleNetworkEndpoint,
+ metadataNetworkConfigDl, err := download.Download(ctx, OracleNetworkEndpoint,
download.WithHeaders(map[string]string{"Authorization": "Bearer Oracle"}))
if err != nil {
- return fmt.Errorf("failed to fetch network config from metadata service: %w", err)
+ return fmt.Errorf("failed to fetch network config from: %w", err)
}
var interfaceAddresses []NetworkConfig
- if err = json.Unmarshal(metadataNetworkConfig, &interfaceAddresses); err != nil {
+ if err = json.Unmarshal(metadataNetworkConfigDl, &interfaceAddresses); err != nil {
return err
}
- log.Printf("fetching hostname from: %q", OracleHostnameEndpoint)
-
- hostname, _ := download.Download(ctx, OracleHostnameEndpoint, //nolint:errcheck
- download.WithHeaders(map[string]string{"Authorization": "Bearer Oracle"}),
- download.WithErrorOnNotFound(errors.ErrNoHostname),
- download.WithErrorOnEmptyResponse(errors.ErrNoHostname))
-
- networkConfig, err := o.ParseMetadata(interfaceAddresses, string(hostname))
+ networkConfig, err := o.ParseMetadata(interfaceAddresses, metadata)
if err != nil {
return err
}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/oracle_test.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/oracle_test.go
index a1efd6834..bc73ef77a 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/oracle_test.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/oracle_test.go
@@ -19,17 +19,24 @@ import (
//go:embed testdata/metadata.json
var rawMetadata []byte
+//go:embed testdata/metadatanetwork.json
+var rawMetadataNetwork []byte
+
//go:embed testdata/expected.yaml
var expectedNetworkConfig string
func TestParseMetadata(t *testing.T) {
- o := &oracle.Oracle{}
+ p := &oracle.Oracle{}
+
+ var metadata oracle.MetadataConfig
+
+ require.NoError(t, json.Unmarshal(rawMetadata, &metadata))
var m []oracle.NetworkConfig
- require.NoError(t, json.Unmarshal(rawMetadata, &m))
+ require.NoError(t, json.Unmarshal(rawMetadataNetwork, &m))
- networkConfig, err := o.ParseMetadata(m, "talos")
+ networkConfig, err := p.ParseMetadata(m, &metadata)
require.NoError(t, err)
marshaled, err := yaml.Marshal(networkConfig)
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/testdata/expected.yaml b/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/testdata/expected.yaml
index 9ffa81af4..7ab777957 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/testdata/expected.yaml
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/testdata/expected.yaml
@@ -15,3 +15,11 @@ operators:
routeMetric: 1024
layer: platform
externalIPs: []
+metadata:
+ platform: oracle
+ hostname: talos
+ region: phx
+ zone: emir
+ instanceType: VM.Standard.E3.Flex
+ instanceId: ocid1.instance.oc1.phx.exampleuniqueID
+ providerId: oci://ocid1.instance.oc1.phx.exampleuniqueID
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/testdata/metadata.json b/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/testdata/metadata.json
index 4dc7fcd01..37ebee9d4 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/testdata/metadata.json
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/testdata/metadata.json
@@ -1,12 +1,53 @@
-[
- {
- "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"
+{
+ "availabilityDomain": "EMIr:PHX-AD-1",
+ "faultDomain": "FAULT-DOMAIN-3",
+ "compartmentId": "ocid1.tenancy.oc1..exampleuniqueID",
+ "displayName": "talos-instance",
+ "hostname": "talos",
+ "id": "ocid1.instance.oc1.phx.exampleuniqueID",
+ "image": "ocid1.image.oc1.phx.exampleuniqueID",
+ "metadata": {},
+ "region": "phx",
+ "canonicalRegionName": "us-phoenix-1",
+ "ociAdName": "phx-ad-1",
+ "regionInfo": {
+ "realmKey": "oc1",
+ "realmDomainComponent": "oraclecloud.com",
+ "regionKey": "PHX",
+ "regionIdentifier": "us-phoenix-1"
+ },
+ "shape": "VM.Standard.E3.Flex",
+ "state": "Running",
+ "timeCreated": 1600381928581,
+ "agentConfig": {
+ "monitoringDisabled": false,
+ "managementDisabled": false,
+ "allPluginsDisabled": false,
+ "pluginsConfig": [
+ {
+ "name": "OS Management Service Agent",
+ "desiredState": "ENABLED"
+ },
+ {
+ "name": "Custom Logs Monitoring",
+ "desiredState": "ENABLED"
+ },
+ {
+ "name": "Compute Instance Run Command",
+ "desiredState": "ENABLED"
+ },
+ {
+ "name": "Compute Instance Monitoring",
+ "desiredState": "ENABLED"
+ }
+ ]
+ },
+ "freeformTags": {
+ "Department": "Finance"
+ },
+ "definedTags": {
+ "Operations": {
+ "CostCenter": "42"
+ }
}
-]
+}
\ No newline at end of file
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/testdata/metadatanetwork.json b/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/testdata/metadatanetwork.json
new file mode 100644
index 000000000..c5470e24c
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/testdata/metadatanetwork.json
@@ -0,0 +1,12 @@
+[
+ {
+ "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"
+ }
+]
\ No newline at end of file
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/metadata.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/metadata.go
new file mode 100644
index 000000000..c3bd132f8
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/metadata.go
@@ -0,0 +1,36 @@
+// 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 scaleway
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+
+ "github.com/scaleway/scaleway-sdk-go/api/instance/v1"
+
+ "github.com/talos-systems/talos/pkg/download"
+)
+
+const (
+ // ScalewayMetadataEndpoint is the local Scaleway endpoint.
+ ScalewayMetadataEndpoint = "http://169.254.42.42/conf?format=json"
+ // ScalewayUserDataEndpoint is the local Scaleway endpoint for the config.
+ ScalewayUserDataEndpoint = "http://169.254.42.42/user_data/cloud-init"
+)
+
+func (u *Scaleway) getMetadata(ctx context.Context) (*instance.Metadata, error) {
+ metaConfigDl, err := download.Download(ctx, ScalewayMetadataEndpoint)
+ if err != nil {
+ return nil, fmt.Errorf("error fetching metadata: %w", err)
+ }
+
+ var meta instance.Metadata
+ if err = json.Unmarshal(metaConfigDl, &meta); err != nil {
+ return nil, err
+ }
+
+ return &meta, nil
+}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/scaleway.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/scaleway.go
index c39748179..b28f27de6 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/scaleway.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/scaleway.go
@@ -7,10 +7,11 @@ package scaleway
import (
"context"
- "encoding/json"
+ "fmt"
"log"
"net/netip"
"strconv"
+ "strings"
"github.com/cosi-project/runtime/pkg/state"
"github.com/scaleway/scaleway-sdk-go/api/instance/v1"
@@ -21,13 +22,7 @@ import (
"github.com/talos-systems/talos/pkg/download"
"github.com/talos-systems/talos/pkg/machinery/nethelpers"
"github.com/talos-systems/talos/pkg/machinery/resources/network"
-)
-
-const (
- // ScalewayMetadataEndpoint is the local Scaleway endpoint.
- ScalewayMetadataEndpoint = "http://169.254.42.42/conf?format=json"
- // ScalewayUserDataEndpoint is the local Scaleway endpoint for the config.
- ScalewayUserDataEndpoint = "http://169.254.42.42/user_data/cloud-init"
+ runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime"
)
// Scaleway is the concrete type that implements the runtime.Platform interface.
@@ -38,24 +33,24 @@ func (s *Scaleway) Name() string {
return "scaleway"
}
-// ParseMetadata converts Scaleway met.
-func (s *Scaleway) ParseMetadata(metadataConfig *instance.Metadata) (*runtime.PlatformNetworkConfig, error) {
+// ParseMetadata converts Scaleway platform metadata into platform network config.
+func (s *Scaleway) ParseMetadata(metadata *instance.Metadata) (*runtime.PlatformNetworkConfig, error) {
networkConfig := &runtime.PlatformNetworkConfig{}
- if metadataConfig.Hostname != "" {
+ if metadata.Hostname != "" {
hostnameSpec := network.HostnameSpecSpec{
ConfigLayer: network.ConfigPlatform,
}
- if err := hostnameSpec.ParseFQDN(metadataConfig.Hostname); err != nil {
+ if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil {
return nil, err
}
networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec)
}
- if metadataConfig.PublicIP.Address != "" {
- ip, err := netip.ParseAddr(metadataConfig.PublicIP.Address)
+ if metadata.PublicIP.Address != "" {
+ ip, err := netip.ParseAddr(metadata.PublicIP.Address)
if err != nil {
return nil, err
}
@@ -94,13 +89,13 @@ func (s *Scaleway) ParseMetadata(metadataConfig *instance.Metadata) (*runtime.Pl
ConfigLayer: network.ConfigPlatform,
})
- if metadataConfig.IPv6.Address != "" {
- bits, err := strconv.Atoi(metadataConfig.IPv6.Netmask)
+ if metadata.IPv6.Address != "" {
+ bits, err := strconv.Atoi(metadata.IPv6.Netmask)
if err != nil {
return nil, err
}
- ip, err := netip.ParseAddr(metadataConfig.IPv6.Address)
+ ip, err := netip.ParseAddr(metadata.IPv6.Address)
if err != nil {
return nil, err
}
@@ -118,7 +113,7 @@ func (s *Scaleway) ParseMetadata(metadataConfig *instance.Metadata) (*runtime.Pl
},
)
- gw, err := netip.ParseAddr(metadataConfig.IPv6.Gateway)
+ gw, err := netip.ParseAddr(metadata.IPv6.Gateway)
if err != nil {
return nil, err
}
@@ -139,20 +134,34 @@ func (s *Scaleway) ParseMetadata(metadataConfig *instance.Metadata) (*runtime.Pl
networkConfig.Routes = append(networkConfig.Routes, route)
}
+ zoneParts := strings.Split(metadata.Location.ZoneID, "-")
+ if len(zoneParts) > 2 {
+ zoneParts = zoneParts[:2]
+ }
+
+ networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{
+ Platform: s.Name(),
+ Hostname: metadata.Hostname,
+ Region: strings.Join(zoneParts, "-"),
+ Zone: metadata.Location.ZoneID,
+ InstanceType: metadata.CommercialType,
+ InstanceID: metadata.ID,
+ ProviderID: fmt.Sprintf("scaleway://instance/%s/%s", metadata.Location.ZoneID, metadata.ID),
+ }
+
return networkConfig, nil
}
// Configuration implements the runtime.Platform interface.
+//
+//nolint:stylecheck
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,
- download.WithLowSrcPort())
- if err != nil {
- return nil, errors.ErrNoConfigSource
- }
-
- return machineConfigDl, nil
+ return download.Download(ctx, ScalewayUserDataEndpoint,
+ download.WithLowSrcPort(),
+ download.WithErrorOnNotFound(errors.ErrNoConfigSource),
+ download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource))
}
// Mode implements the runtime.Platform interface.
@@ -171,16 +180,11 @@ func (s *Scaleway) KernelArgs() procfs.Parameters {
func (s *Scaleway) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error {
log.Printf("fetching scaleway instance config from: %q", ScalewayMetadataEndpoint)
- metadataDl, err := download.Download(ctx, ScalewayMetadataEndpoint)
+ metadata, err := s.getMetadata(ctx)
if err != nil {
return err
}
- metadata := &instance.Metadata{}
- if err = json.Unmarshal(metadataDl, metadata); err != nil {
- return err
- }
-
networkConfig, err := s.ParseMetadata(metadata)
if err != nil {
return err
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/scaleway_test.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/scaleway_test.go
index 780b54b63..f3e799acf 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/scaleway_test.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/scaleway_test.go
@@ -26,11 +26,11 @@ var expectedNetworkConfig string
func TestParseMetadata(t *testing.T) {
p := &scaleway.Scaleway{}
- var m instance.Metadata
+ var metadata instance.Metadata
- require.NoError(t, json.Unmarshal(rawMetadata, &m))
+ require.NoError(t, json.Unmarshal(rawMetadata, &metadata))
- networkConfig, err := p.ParseMetadata(&m)
+ networkConfig, err := p.ParseMetadata(&metadata)
require.NoError(t, err)
marshaled, err := yaml.Marshal(networkConfig)
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/testdata/expected.yaml b/internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/testdata/expected.yaml
index 350c38281..39b137ff0 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/testdata/expected.yaml
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/testdata/expected.yaml
@@ -53,3 +53,9 @@ operators:
layer: platform
externalIPs:
- 11.22.222.222
+metadata:
+ platform: scaleway
+ hostname: scw-talos
+ instanceType: DEV1-S
+ instanceId: 11111111-1111-1111-1111-111111111111
+ providerId: scaleway://instance//11111111-1111-1111-1111-111111111111
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/metadata.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/metadata.go
new file mode 100644
index 000000000..790b27862
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/metadata.go
@@ -0,0 +1,63 @@
+// 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 upcloud
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+
+ "github.com/talos-systems/talos/pkg/download"
+)
+
+const (
+ // UpCloudMetadataEndpoint is the local UpCloud endpoint.
+ UpCloudMetadataEndpoint = "http://169.254.169.254/metadata/v1.json"
+
+ // UpCloudUserDataEndpoint is the local UpCloud endpoint for the config.
+ UpCloudUserDataEndpoint = "http://169.254.169.254/metadata/v1/user_data"
+)
+
+// MetadataConfig represents a metadata Upcloud instance.
+type MetadataConfig struct {
+ Hostname string `json:"hostname,omitempty"`
+ InstanceID string `json:"instance_id,omitempty"`
+ PublicKeys []string `json:"public_keys,omitempty"`
+ Zone string `json:"region,omitempty"`
+ Tags []string `json:"tags,omitempty"`
+
+ Network struct {
+ Interfaces []struct {
+ Index int `json:"index,omitempty"`
+ IPAddresses []struct {
+ Address string `json:"address,omitempty"`
+ DHCP bool `json:"dhcp,omitempty"`
+ DNS []string `json:"dns,omitempty"`
+ Family string `json:"family,omitempty"`
+ Floating bool `json:"floating,omitempty"`
+ Gateway string `json:"gateway,omitempty"`
+ Network string `json:"network,omitempty"`
+ } `json:"ip_addresses,omitempty"`
+ MAC string `json:"mac,omitempty"`
+ NetworkType string `json:"type,omitempty"`
+ NetworkID string `json:"network_id,omitempty"`
+ } `json:"interfaces,omitempty"`
+ DNS []string `json:"dns,omitempty"`
+ } `json:"network,omitempty"`
+}
+
+func (u *UpCloud) getMetadata(ctx context.Context) (*MetadataConfig, error) {
+ metaConfigDl, err := download.Download(ctx, UpCloudMetadataEndpoint)
+ if err != nil {
+ return nil, fmt.Errorf("error fetching metadata: %w", err)
+ }
+
+ var meta MetadataConfig
+ if err = json.Unmarshal(metaConfigDl, &meta); err != nil {
+ return nil, err
+ }
+
+ return &meta, nil
+}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/testdata/expected.yaml b/internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/testdata/expected.yaml
index 4c5e2838a..778b31e84 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/testdata/expected.yaml
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/testdata/expected.yaml
@@ -73,3 +73,8 @@ operators:
layer: platform
externalIPs:
- 185.70.197.2
+metadata:
+ platform: upcloud
+ hostname: talos
+ instanceId: 00123456-1111-2222-3333-123456789012
+ providerId: upcloud://00123456-1111-2222-3333-123456789012
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/upcloud.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/upcloud.go
index 931994440..fdc398588 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/upcloud.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/upcloud.go
@@ -7,7 +7,6 @@ package upcloud
import (
"context"
- "encoding/json"
"fmt"
"log"
"net/netip"
@@ -20,43 +19,9 @@ import (
"github.com/talos-systems/talos/pkg/download"
"github.com/talos-systems/talos/pkg/machinery/nethelpers"
"github.com/talos-systems/talos/pkg/machinery/resources/network"
+ runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime"
)
-const (
- // UpCloudMetadataEndpoint is the local UpCloud endpoint.
- UpCloudMetadataEndpoint = "http://169.254.169.254/metadata/v1.json"
-
- // UpCloudUserDataEndpoint is the local UpCloud endpoint for the config.
- UpCloudUserDataEndpoint = "http://169.254.169.254/metadata/v1/user_data"
-)
-
-// MetaData represents a metadata Upcloud interface.
-type MetaData struct {
- Hostname string `json:"hostname,omitempty"`
- InstanceID string `json:"instance_id,omitempty"`
- PublicKeys []string `json:"public_keys,omitempty"`
- Region string `json:"region,omitempty"`
-
- Network struct {
- Interfaces []struct {
- Index int `json:"index,omitempty"`
- IPAddresses []struct {
- Address string `json:"address,omitempty"`
- DHCP bool `json:"dhcp,omitempty"`
- DNS []string `json:"dns,omitempty"`
- Family string `json:"family,omitempty"`
- Floating bool `json:"floating,omitempty"`
- Gateway string `json:"gateway,omitempty"`
- Network string `json:"network,omitempty"`
- } `json:"ip_addresses,omitempty"`
- MAC string `json:"mac,omitempty"`
- NetworkType string `json:"type,omitempty"`
- NetworkID string `json:"network_id,omitempty"`
- } `json:"interfaces,omitempty"`
- DNS []string `json:"dns,omitempty"`
- } `json:"network,omitempty"`
-}
-
// UpCloud is the concrete type that implements the runtime.Platform interface.
type UpCloud struct{}
@@ -68,15 +33,15 @@ func (u *UpCloud) Name() string {
// ParseMetadata converts Upcloud metadata into platform network configuration.
//
//nolint:gocyclo
-func (u *UpCloud) ParseMetadata(meta *MetaData) (*runtime.PlatformNetworkConfig, error) {
+func (u *UpCloud) ParseMetadata(metadata *MetadataConfig) (*runtime.PlatformNetworkConfig, error) {
networkConfig := &runtime.PlatformNetworkConfig{}
- if meta.Hostname != "" {
+ if metadata.Hostname != "" {
hostnameSpec := network.HostnameSpecSpec{
ConfigLayer: network.ConfigPlatform,
}
- if err := hostnameSpec.ParseFQDN(meta.Hostname); err != nil {
+ if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil {
return nil, err
}
@@ -87,7 +52,7 @@ func (u *UpCloud) ParseMetadata(meta *MetaData) (*runtime.PlatformNetworkConfig,
firstIP := true
- for _, addr := range meta.Network.Interfaces {
+ for _, addr := range metadata.Network.Interfaces {
if addr.Index <= 0 { // protect from negative interface name
continue
}
@@ -192,6 +157,14 @@ func (u *UpCloud) ParseMetadata(meta *MetaData) (*runtime.PlatformNetworkConfig,
})
}
+ networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{
+ Platform: u.Name(),
+ Hostname: metadata.Hostname,
+ Zone: metadata.Zone,
+ InstanceID: metadata.InstanceID,
+ ProviderID: fmt.Sprintf("upcloud://%s", metadata.InstanceID),
+ }
+
return networkConfig, nil
}
@@ -216,19 +189,14 @@ func (u *UpCloud) KernelArgs() procfs.Parameters {
// NetworkConfiguration implements the runtime.Platform interface.
func (u *UpCloud) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error {
- log.Printf("fetching UpCloud instance config from: %q ", UpCloudMetadataEndpoint)
+ log.Printf("fetching UpCloud instance config from: %q", UpCloudMetadataEndpoint)
- metaConfigDl, err := download.Download(ctx, UpCloudMetadataEndpoint)
+ metadata, err := u.getMetadata(ctx)
if err != nil {
- return fmt.Errorf("failed to fetch network config from metadata service: %w", err)
- }
-
- meta := &MetaData{}
- if err = json.Unmarshal(metaConfigDl, meta); err != nil {
return err
}
- networkConfig, err := u.ParseMetadata(meta)
+ networkConfig, err := u.ParseMetadata(metadata)
if err != nil {
return err
}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/upcloud_test.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/upcloud_test.go
index e74a37362..27ea58c9c 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/upcloud_test.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/upcloud_test.go
@@ -25,11 +25,11 @@ var expectedNetworkConfig string
func TestParseMetadata(t *testing.T) {
p := &upcloud.UpCloud{}
- var m upcloud.MetaData
+ var metadata upcloud.MetadataConfig
- require.NoError(t, json.Unmarshal(rawMetadata, &m))
+ require.NoError(t, json.Unmarshal(rawMetadata, &metadata))
- networkConfig, err := p.ParseMetadata(&m)
+ networkConfig, err := p.ParseMetadata(&metadata)
require.NoError(t, err)
marshaled, err := yaml.Marshal(networkConfig)
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/vmware/vmware_amd64.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/vmware/vmware_amd64.go
index d268f4ec4..10348dd72 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/vmware/vmware_amd64.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/vmware/vmware_amd64.go
@@ -24,6 +24,7 @@ import (
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
platformerrors "github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors"
"github.com/talos-systems/talos/pkg/machinery/constants"
+ runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime"
)
// VMware is the concrete type that implements the platform.Platform interface.
@@ -202,5 +203,15 @@ func (v *VMware) KernelArgs() procfs.Parameters {
// NetworkConfiguration implements the runtime.Platform interface.
func (v *VMware) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error {
+ networkConfig := &runtime.PlatformNetworkConfig{
+ Metadata: &runtimeres.PlatformMetadataSpec{Platform: v.Name()},
+ }
+
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ case ch <- networkConfig:
+ }
+
return nil
}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/metadata.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/metadata.go
new file mode 100644
index 000000000..4f56dbbc4
--- /dev/null
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/metadata.go
@@ -0,0 +1,38 @@
+// 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 vultr
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+
+ "github.com/vultr/metadata"
+
+ "github.com/talos-systems/talos/pkg/download"
+)
+
+const (
+ // VultrMetadataEndpoint is the local Vultr endpoint fot the instance metadata.
+ VultrMetadataEndpoint = "http://169.254.169.254/v1.json"
+ // VultrExternalIPEndpoint is the local Vultr endpoint for the external IP.
+ VultrExternalIPEndpoint = "http://169.254.169.254/latest/meta-data/public-ipv4"
+ // VultrUserDataEndpoint is the local Vultr endpoint for the config.
+ VultrUserDataEndpoint = "http://169.254.169.254/latest/user-data"
+)
+
+func (g *Vultr) getMetadata(ctx context.Context) (*metadata.MetaData, error) {
+ metaConfigDl, err := download.Download(ctx, VultrMetadataEndpoint)
+ if err != nil {
+ return nil, fmt.Errorf("error fetching metadata: %w", err)
+ }
+
+ var meta metadata.MetaData
+ if err = json.Unmarshal(metaConfigDl, &meta); err != nil {
+ return nil, err
+ }
+
+ return &meta, nil
+}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/testdata/expected.yaml b/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/testdata/expected.yaml
index 910c45952..b07ee51d2 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/testdata/expected.yaml
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/testdata/expected.yaml
@@ -36,3 +36,9 @@ operators:
layer: platform
externalIPs:
- 1.2.3.4
+metadata:
+ platform: vultr
+ hostname: talos
+ region: AMS
+ instanceId: 91b07056-af72-4551-b15b-d57d34071be9
+ providerId: vultr://91b07056-af72-4551-b15b-d57d34071be9
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/testdata/metadata.json b/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/testdata/metadata.json
index fd4097564..92803809e 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/testdata/metadata.json
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/testdata/metadata.json
@@ -58,4 +58,4 @@
"regioncode": "AMS"
},
"user-defined": []
-}
+}
\ No newline at end of file
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/vultr.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/vultr.go
index 827b97006..9938dbfe1 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/vultr.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/vultr.go
@@ -7,7 +7,6 @@ package vultr
import (
"context"
- "encoding/json"
stderrors "errors"
"fmt"
"log"
@@ -23,17 +22,7 @@ import (
"github.com/talos-systems/talos/pkg/download"
"github.com/talos-systems/talos/pkg/machinery/nethelpers"
"github.com/talos-systems/talos/pkg/machinery/resources/network"
-)
-
-const (
- // VultrMetadataEndpoint is the local Vultr endpoint fot the instance metadata.
- VultrMetadataEndpoint = "http://169.254.169.254/v1.json"
- // VultrExternalIPEndpoint is the local Vultr endpoint for the external IP.
- VultrExternalIPEndpoint = "http://169.254.169.254/latest/meta-data/public-ipv4"
- // VultrHostnameEndpoint is the local Vultr endpoint for the hostname.
- VultrHostnameEndpoint = "http://169.254.169.254/latest/meta-data/hostname"
- // VultrUserDataEndpoint is the local Vultr endpoint for the config.
- VultrUserDataEndpoint = "http://169.254.169.254/latest/user-data"
+ runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime"
)
// Vultr is the concrete type that implements the runtime.Platform interface.
@@ -45,26 +34,28 @@ func (v *Vultr) Name() string {
}
// ParseMetadata converts Vultr platform metadata into platform network config.
-func (v *Vultr) ParseMetadata(meta *metadata.MetaData, extIP []byte) (*runtime.PlatformNetworkConfig, error) {
+//
+//nolint:gocyclo
+func (v *Vultr) ParseMetadata(extIP []byte, metadata *metadata.MetaData) (*runtime.PlatformNetworkConfig, error) {
networkConfig := &runtime.PlatformNetworkConfig{}
if ip, err := netip.ParseAddr(string(extIP)); err == nil {
networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip)
}
- if meta.Hostname != "" {
+ if metadata.Hostname != "" {
hostnameSpec := network.HostnameSpecSpec{
ConfigLayer: network.ConfigPlatform,
}
- if err := hostnameSpec.ParseFQDN(meta.Hostname); err != nil {
+ if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil {
return nil, err
}
networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec)
}
- for i, addr := range meta.Interfaces {
+ for i, addr := range metadata.Interfaces {
iface := fmt.Sprintf("eth%d", i)
link := network.LinkSpecSpec{
@@ -119,10 +110,20 @@ func (v *Vultr) ParseMetadata(meta *metadata.MetaData, extIP []byte) (*runtime.P
}
}
+ networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{
+ Platform: v.Name(),
+ Hostname: metadata.Hostname,
+ Region: metadata.Region.RegionCode,
+ InstanceID: metadata.InstanceV2ID,
+ ProviderID: fmt.Sprintf("vultr://%s", metadata.InstanceV2ID),
+ }
+
return networkConfig, nil
}
// Configuration implements the runtime.Platform interface.
+//
+//nolint:stylecheck
func (v *Vultr) Configuration(ctx context.Context, r state.State) ([]byte, error) {
log.Printf("fetching machine config from: %q", VultrUserDataEndpoint)
@@ -143,15 +144,10 @@ func (v *Vultr) KernelArgs() procfs.Parameters {
// NetworkConfiguration implements the runtime.Platform interface.
func (v *Vultr) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error {
- log.Printf("fetching Vultr instance config from: %q ", VultrMetadataEndpoint)
+ log.Printf("fetching Vultr instance metadata from: %q", VultrMetadataEndpoint)
- metaConfigDl, err := download.Download(ctx, VultrMetadataEndpoint)
+ metadata, err := v.getMetadata(ctx)
if err != nil {
- return fmt.Errorf("error fetching metadata: %w", err)
- }
-
- meta := &metadata.MetaData{}
- if err = json.Unmarshal(metaConfigDl, meta); err != nil {
return err
}
@@ -162,7 +158,7 @@ func (v *Vultr) NetworkConfiguration(ctx context.Context, _ state.State, ch chan
return err
}
- networkConfig, err := v.ParseMetadata(meta, extIP)
+ networkConfig, err := v.ParseMetadata(extIP, metadata)
if err != nil {
return err
}
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/vultr_test.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/vultr_test.go
index 00fd3acc0..26137ba90 100644
--- a/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/vultr_test.go
+++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/vultr_test.go
@@ -26,11 +26,11 @@ var expectedNetworkConfig string
func TestParseMetadata(t *testing.T) {
p := &vultr.Vultr{}
- var m metadata.MetaData
+ var metadata metadata.MetaData
- require.NoError(t, json.Unmarshal(rawMetadata, &m))
+ require.NoError(t, json.Unmarshal(rawMetadata, &metadata))
- networkConfig, err := p.ParseMetadata(&m, []byte("1.2.3.4"))
+ networkConfig, err := p.ParseMetadata([]byte("1.2.3.4"), &metadata)
require.NoError(t, err)
marshaled, err := yaml.Marshal(networkConfig)
diff --git a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_state.go b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_state.go
index 36196b425..0168fd965 100644
--- a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_state.go
+++ b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_state.go
@@ -156,6 +156,7 @@ func NewState() (*State, error) {
&runtime.KernelParamStatus{},
&runtime.MachineStatus{},
&runtime.MountStatus{},
+ &runtime.PlatformMetadata{},
&secrets.API{},
&secrets.CertSAN{},
&secrets.Etcd{},
diff --git a/pkg/machinery/api/resource/definitions/runtime/runtime.pb.go b/pkg/machinery/api/resource/definitions/runtime/runtime.pb.go
index 70e956463..34f10635d 100644
--- a/pkg/machinery/api/resource/definitions/runtime/runtime.pb.go
+++ b/pkg/machinery/api/resource/definitions/runtime/runtime.pb.go
@@ -383,6 +383,102 @@ func (x *MountStatusSpec) GetOptions() []string {
return nil
}
+// PlatformMetadataSpec describes platform metadata properties.
+type PlatformMetadataSpec struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Platform string `protobuf:"bytes,1,opt,name=platform,proto3" json:"platform,omitempty"`
+ Hostname string `protobuf:"bytes,2,opt,name=hostname,proto3" json:"hostname,omitempty"`
+ Region string `protobuf:"bytes,3,opt,name=region,proto3" json:"region,omitempty"`
+ Zone string `protobuf:"bytes,4,opt,name=zone,proto3" json:"zone,omitempty"`
+ InstanceType string `protobuf:"bytes,5,opt,name=instance_type,json=instanceType,proto3" json:"instance_type,omitempty"`
+ InstanceId string `protobuf:"bytes,6,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"`
+ ProviderId string `protobuf:"bytes,7,opt,name=provider_id,json=providerId,proto3" json:"provider_id,omitempty"`
+}
+
+func (x *PlatformMetadataSpec) Reset() {
+ *x = PlatformMetadataSpec{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[6]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *PlatformMetadataSpec) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PlatformMetadataSpec) ProtoMessage() {}
+
+func (x *PlatformMetadataSpec) ProtoReflect() protoreflect.Message {
+ mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[6]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use PlatformMetadataSpec.ProtoReflect.Descriptor instead.
+func (*PlatformMetadataSpec) Descriptor() ([]byte, []int) {
+ return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{6}
+}
+
+func (x *PlatformMetadataSpec) GetPlatform() string {
+ if x != nil {
+ return x.Platform
+ }
+ return ""
+}
+
+func (x *PlatformMetadataSpec) GetHostname() string {
+ if x != nil {
+ return x.Hostname
+ }
+ return ""
+}
+
+func (x *PlatformMetadataSpec) GetRegion() string {
+ if x != nil {
+ return x.Region
+ }
+ return ""
+}
+
+func (x *PlatformMetadataSpec) GetZone() string {
+ if x != nil {
+ return x.Zone
+ }
+ return ""
+}
+
+func (x *PlatformMetadataSpec) GetInstanceType() string {
+ if x != nil {
+ return x.InstanceType
+ }
+ return ""
+}
+
+func (x *PlatformMetadataSpec) GetInstanceId() string {
+ if x != nil {
+ return x.InstanceId
+ }
+ return ""
+}
+
+func (x *PlatformMetadataSpec) GetProviderId() string {
+ if x != nil {
+ return x.ProviderId
+ }
+ return ""
+}
+
// UnmetCondition is a failure which prevents machine from being ready at the stage.
type UnmetCondition struct {
state protoimpl.MessageState
@@ -396,7 +492,7 @@ type UnmetCondition struct {
func (x *UnmetCondition) Reset() {
*x = UnmetCondition{}
if protoimpl.UnsafeEnabled {
- mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[6]
+ mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -409,7 +505,7 @@ func (x *UnmetCondition) String() string {
func (*UnmetCondition) ProtoMessage() {}
func (x *UnmetCondition) ProtoReflect() protoreflect.Message {
- mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[6]
+ mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[7]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -422,7 +518,7 @@ func (x *UnmetCondition) ProtoReflect() protoreflect.Message {
// Deprecated: Use UnmetCondition.ProtoReflect.Descriptor instead.
func (*UnmetCondition) Descriptor() ([]byte, []int) {
- return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{6}
+ return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{7}
}
func (x *UnmetCondition) GetName() string {
@@ -494,17 +590,31 @@ var file_resource_definitions_runtime_runtime_proto_rawDesc = []byte{
0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54,
0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04,
- 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x3c, 0x0a,
- 0x0e, 0x55, 0x6e, 0x6d, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12,
- 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
- 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x42, 0x4f, 0x5a, 0x4d, 0x67,
- 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2d,
- 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x2f, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2f, 0x70, 0x6b,
- 0x67, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x72, 0x79, 0x2f, 0x61, 0x70, 0x69, 0x2f,
- 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74,
- 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x62, 0x06, 0x70, 0x72,
- 0x6f, 0x74, 0x6f, 0x33,
+ 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xe1, 0x01,
+ 0x0a, 0x14, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
+ 0x74, 0x61, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f,
+ 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f,
+ 0x72, 0x6d, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16,
+ 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
+ 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x04,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x7a, 0x6f, 0x6e, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e,
+ 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12,
+ 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x06,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64,
+ 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18,
+ 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49,
+ 0x64, 0x22, 0x3c, 0x0a, 0x0e, 0x55, 0x6e, 0x6d, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74,
+ 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f,
+ 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x42,
+ 0x4f, 0x5a, 0x4d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x61,
+ 0x6c, 0x6f, 0x73, 0x2d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x2f, 0x74, 0x61, 0x6c, 0x6f,
+ 0x73, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x72, 0x79, 0x2f,
+ 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x64, 0x65, 0x66,
+ 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65,
+ 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -519,7 +629,7 @@ func file_resource_definitions_runtime_runtime_proto_rawDescGZIP() []byte {
return file_resource_definitions_runtime_runtime_proto_rawDescData
}
-var file_resource_definitions_runtime_runtime_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
+var file_resource_definitions_runtime_runtime_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
var file_resource_definitions_runtime_runtime_proto_goTypes = []interface{}{
(*KernelModuleSpecSpec)(nil), // 0: talos.resource.definitions.runtime.KernelModuleSpecSpec
(*KernelParamSpecSpec)(nil), // 1: talos.resource.definitions.runtime.KernelParamSpecSpec
@@ -527,13 +637,14 @@ var file_resource_definitions_runtime_runtime_proto_goTypes = []interface{}{
(*MachineStatusSpec)(nil), // 3: talos.resource.definitions.runtime.MachineStatusSpec
(*MachineStatusStatus)(nil), // 4: talos.resource.definitions.runtime.MachineStatusStatus
(*MountStatusSpec)(nil), // 5: talos.resource.definitions.runtime.MountStatusSpec
- (*UnmetCondition)(nil), // 6: talos.resource.definitions.runtime.UnmetCondition
- (enums.RuntimeMachineStage)(0), // 7: talos.resource.definitions.enums.RuntimeMachineStage
+ (*PlatformMetadataSpec)(nil), // 6: talos.resource.definitions.runtime.PlatformMetadataSpec
+ (*UnmetCondition)(nil), // 7: talos.resource.definitions.runtime.UnmetCondition
+ (enums.RuntimeMachineStage)(0), // 8: talos.resource.definitions.enums.RuntimeMachineStage
}
var file_resource_definitions_runtime_runtime_proto_depIdxs = []int32{
- 7, // 0: talos.resource.definitions.runtime.MachineStatusSpec.stage:type_name -> talos.resource.definitions.enums.RuntimeMachineStage
+ 8, // 0: talos.resource.definitions.runtime.MachineStatusSpec.stage:type_name -> talos.resource.definitions.enums.RuntimeMachineStage
4, // 1: talos.resource.definitions.runtime.MachineStatusSpec.status:type_name -> talos.resource.definitions.runtime.MachineStatusStatus
- 6, // 2: talos.resource.definitions.runtime.MachineStatusStatus.unmet_conditions:type_name -> talos.resource.definitions.runtime.UnmetCondition
+ 7, // 2: talos.resource.definitions.runtime.MachineStatusStatus.unmet_conditions:type_name -> talos.resource.definitions.runtime.UnmetCondition
3, // [3:3] is the sub-list for method output_type
3, // [3:3] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
@@ -620,6 +731,18 @@ func file_resource_definitions_runtime_runtime_proto_init() {
}
}
file_resource_definitions_runtime_runtime_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*PlatformMetadataSpec); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_resource_definitions_runtime_runtime_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UnmetCondition); i {
case 0:
return &v.state
@@ -638,7 +761,7 @@ func file_resource_definitions_runtime_runtime_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_resource_definitions_runtime_runtime_proto_rawDesc,
NumEnums: 0,
- NumMessages: 7,
+ NumMessages: 8,
NumExtensions: 0,
NumServices: 0,
},
diff --git a/pkg/machinery/api/resource/definitions/runtime/runtime_vtproto.pb.go b/pkg/machinery/api/resource/definitions/runtime/runtime_vtproto.pb.go
index 766ff5f0e..2cda5ba49 100644
--- a/pkg/machinery/api/resource/definitions/runtime/runtime_vtproto.pb.go
+++ b/pkg/machinery/api/resource/definitions/runtime/runtime_vtproto.pb.go
@@ -343,6 +343,88 @@ func (m *MountStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
+func (m *PlatformMetadataSpec) MarshalVT() (dAtA []byte, err error) {
+ if m == nil {
+ return nil, nil
+ }
+ size := m.SizeVT()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBufferVT(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *PlatformMetadataSpec) MarshalToVT(dAtA []byte) (int, error) {
+ size := m.SizeVT()
+ return m.MarshalToSizedBufferVT(dAtA[:size])
+}
+
+func (m *PlatformMetadataSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
+ if m == nil {
+ return 0, nil
+ }
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ if m.unknownFields != nil {
+ i -= len(m.unknownFields)
+ copy(dAtA[i:], m.unknownFields)
+ }
+ if len(m.ProviderId) > 0 {
+ i -= len(m.ProviderId)
+ copy(dAtA[i:], m.ProviderId)
+ i = encodeVarint(dAtA, i, uint64(len(m.ProviderId)))
+ i--
+ dAtA[i] = 0x3a
+ }
+ if len(m.InstanceId) > 0 {
+ i -= len(m.InstanceId)
+ copy(dAtA[i:], m.InstanceId)
+ i = encodeVarint(dAtA, i, uint64(len(m.InstanceId)))
+ i--
+ dAtA[i] = 0x32
+ }
+ if len(m.InstanceType) > 0 {
+ i -= len(m.InstanceType)
+ copy(dAtA[i:], m.InstanceType)
+ i = encodeVarint(dAtA, i, uint64(len(m.InstanceType)))
+ i--
+ dAtA[i] = 0x2a
+ }
+ if len(m.Zone) > 0 {
+ i -= len(m.Zone)
+ copy(dAtA[i:], m.Zone)
+ i = encodeVarint(dAtA, i, uint64(len(m.Zone)))
+ i--
+ dAtA[i] = 0x22
+ }
+ if len(m.Region) > 0 {
+ i -= len(m.Region)
+ copy(dAtA[i:], m.Region)
+ i = encodeVarint(dAtA, i, uint64(len(m.Region)))
+ i--
+ dAtA[i] = 0x1a
+ }
+ if len(m.Hostname) > 0 {
+ i -= len(m.Hostname)
+ copy(dAtA[i:], m.Hostname)
+ i = encodeVarint(dAtA, i, uint64(len(m.Hostname)))
+ i--
+ dAtA[i] = 0x12
+ }
+ if len(m.Platform) > 0 {
+ i -= len(m.Platform)
+ copy(dAtA[i:], m.Platform)
+ i = encodeVarint(dAtA, i, uint64(len(m.Platform)))
+ i--
+ dAtA[i] = 0xa
+ }
+ return len(dAtA) - i, nil
+}
+
func (m *UnmetCondition) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
@@ -535,6 +617,46 @@ func (m *MountStatusSpec) SizeVT() (n int) {
return n
}
+func (m *PlatformMetadataSpec) SizeVT() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ l = len(m.Platform)
+ if l > 0 {
+ n += 1 + l + sov(uint64(l))
+ }
+ l = len(m.Hostname)
+ if l > 0 {
+ n += 1 + l + sov(uint64(l))
+ }
+ l = len(m.Region)
+ if l > 0 {
+ n += 1 + l + sov(uint64(l))
+ }
+ l = len(m.Zone)
+ if l > 0 {
+ n += 1 + l + sov(uint64(l))
+ }
+ l = len(m.InstanceType)
+ if l > 0 {
+ n += 1 + l + sov(uint64(l))
+ }
+ l = len(m.InstanceId)
+ if l > 0 {
+ n += 1 + l + sov(uint64(l))
+ }
+ l = len(m.ProviderId)
+ if l > 0 {
+ n += 1 + l + sov(uint64(l))
+ }
+ if m.unknownFields != nil {
+ n += len(m.unknownFields)
+ }
+ return n
+}
+
func (m *UnmetCondition) SizeVT() (n int) {
if m == nil {
return 0
@@ -1304,6 +1426,281 @@ func (m *MountStatusSpec) UnmarshalVT(dAtA []byte) error {
}
return nil
}
+func (m *PlatformMetadataSpec) UnmarshalVT(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflow
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: PlatformMetadataSpec: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: PlatformMetadataSpec: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Platform", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflow
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLength
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLength
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Platform = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflow
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLength
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLength
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Hostname = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 3:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Region", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflow
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLength
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLength
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Region = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 4:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Zone", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflow
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLength
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLength
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Zone = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 5:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field InstanceType", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflow
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLength
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLength
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.InstanceType = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 6:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field InstanceId", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflow
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLength
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLength
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.InstanceId = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 7:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ProviderId", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflow
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLength
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLength
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.ProviderId = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skip(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLength
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
func (m *UnmetCondition) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
diff --git a/pkg/machinery/resources/runtime/deep_copy.generated.go b/pkg/machinery/resources/runtime/deep_copy.generated.go
index e46fe10fa..af44917fa 100644
--- a/pkg/machinery/resources/runtime/deep_copy.generated.go
+++ b/pkg/machinery/resources/runtime/deep_copy.generated.go
@@ -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 KernelModuleSpecSpec -type KernelParamSpecSpec -type KernelParamStatusSpec -type MachineStatusSpec -type MountStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT.
+// Code generated by "deep-copy -type KernelModuleSpecSpec -type KernelParamSpecSpec -type KernelParamStatusSpec -type MachineStatusSpec -type MountStatusSpec -type PlatformMetadataSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT.
package runtime
@@ -47,3 +47,9 @@ func (o MountStatusSpec) DeepCopy() MountStatusSpec {
}
return cp
}
+
+// DeepCopy generates a deep copy of PlatformMetadataSpec.
+func (o PlatformMetadataSpec) DeepCopy() PlatformMetadataSpec {
+ var cp PlatformMetadataSpec = o
+ return cp
+}
diff --git a/pkg/machinery/resources/runtime/platform_metadata.go b/pkg/machinery/resources/runtime/platform_metadata.go
new file mode 100644
index 000000000..7d3ee1c4c
--- /dev/null
+++ b/pkg/machinery/resources/runtime/platform_metadata.go
@@ -0,0 +1,65 @@
+// 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 runtime
+
+import (
+ "github.com/cosi-project/runtime/pkg/resource"
+ "github.com/cosi-project/runtime/pkg/resource/meta"
+ "github.com/cosi-project/runtime/pkg/resource/protobuf"
+ "github.com/cosi-project/runtime/pkg/resource/typed"
+
+ "github.com/talos-systems/talos/pkg/machinery/proto"
+)
+
+// PlatformMetadataType is type of Metadata resource.
+const PlatformMetadataType = resource.Type("PlatformMetadatas.talos.dev")
+
+// PlatformMetadataID is the ID for Metadata resource platform.
+const PlatformMetadataID resource.ID = "platformmetadata"
+
+// PlatformMetadata resource holds.
+type PlatformMetadata = typed.Resource[PlatformMetadataSpec, PlatformMetadataRD]
+
+// PlatformMetadataSpec describes platform metadata properties.
+//
+//gotagsrewrite:gen
+type PlatformMetadataSpec struct {
+ Platform string `yaml:"platform,omitempty" protobuf:"1"`
+ Hostname string `yaml:"hostname,omitempty" protobuf:"2"`
+ Region string `yaml:"region,omitempty" protobuf:"3"`
+ Zone string `yaml:"zone,omitempty" protobuf:"4"`
+ InstanceType string `yaml:"instanceType,omitempty" protobuf:"5"`
+ InstanceID string `yaml:"instanceId,omitempty" protobuf:"6"`
+ ProviderID string `yaml:"providerId,omitempty" protobuf:"7"`
+}
+
+// NewPlatformMetadataSpec initializes a MetadataSpec resource.
+func NewPlatformMetadataSpec(namespace resource.Namespace, id resource.ID) *PlatformMetadata {
+ return typed.NewResource[PlatformMetadataSpec, PlatformMetadataRD](
+ resource.NewMetadata(namespace, PlatformMetadataType, PlatformMetadataID, resource.VersionUndefined),
+ PlatformMetadataSpec{},
+ )
+}
+
+// PlatformMetadataRD provides auxiliary methods for PlatformMetadata.
+type PlatformMetadataRD struct{}
+
+// ResourceDefinition implements typed.ResourceDefinition interface.
+func (PlatformMetadataRD) ResourceDefinition(resource.Metadata, PlatformMetadataSpec) meta.ResourceDefinitionSpec {
+ return meta.ResourceDefinitionSpec{
+ Type: PlatformMetadataType,
+ DefaultNamespace: NamespaceName,
+ PrintColumns: []meta.PrintColumn{},
+ }
+}
+
+func init() {
+ proto.RegisterDefaultTypes()
+
+ err := protobuf.RegisterDynamic[PlatformMetadataSpec](PlatformMetadataType, &PlatformMetadata{})
+ if err != nil {
+ panic(err)
+ }
+}
diff --git a/pkg/machinery/resources/runtime/runtime.go b/pkg/machinery/resources/runtime/runtime.go
index 86cf5539a..ea23f36f6 100644
--- a/pkg/machinery/resources/runtime/runtime.go
+++ b/pkg/machinery/resources/runtime/runtime.go
@@ -5,4 +5,4 @@
package runtime
//nolint:lll
-//go:generate deep-copy -type KernelModuleSpecSpec -type KernelParamSpecSpec -type KernelParamStatusSpec -type MachineStatusSpec -type MountStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go .
+//go:generate deep-copy -type KernelModuleSpecSpec -type KernelParamSpecSpec -type KernelParamStatusSpec -type MachineStatusSpec -type MountStatusSpec -type PlatformMetadataSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go .
diff --git a/pkg/machinery/resources/runtime/runtime_test.go b/pkg/machinery/resources/runtime/runtime_test.go
index 6720e94e7..0aa1984a6 100644
--- a/pkg/machinery/resources/runtime/runtime_test.go
+++ b/pkg/machinery/resources/runtime/runtime_test.go
@@ -31,6 +31,7 @@ func TestRegisterResource(t *testing.T) {
&runtime.KernelParamStatus{},
&runtime.MachineStatus{},
&runtime.MountStatus{},
+ &runtime.PlatformMetadata{},
} {
assert.NoError(t, resourceRegistry.Register(ctx, resource))
}
diff --git a/pkg/provision/providers/docker/node.go b/pkg/provision/providers/docker/node.go
index 518fb50b1..a6ad429a9 100644
--- a/pkg/provision/providers/docker/node.go
+++ b/pkg/provision/providers/docker/node.go
@@ -64,7 +64,10 @@ func (p *provisioner) createNodes(ctx context.Context, clusterReq provision.Clus
//nolint:gocyclo
func (p *provisioner) createNode(ctx context.Context, clusterReq provision.ClusterRequest, nodeReq provision.NodeRequest, options *provision.Options) (provision.NodeInfo, error) {
- env := []string{"PLATFORM=container"}
+ env := []string{
+ "PLATFORM=container",
+ fmt.Sprintf("TALOSSKU=%dCPU-%dRAM", nodeReq.NanoCPUs/(1000*1000*1000), nodeReq.Memory/(1024*1024)),
+ }
if !nodeReq.SkipInjectingConfig {
cfg, err := nodeReq.Config.EncodeString()
diff --git a/website/content/v1.3/reference/api.md b/website/content/v1.3/reference/api.md
index f005ffe30..c4f0728d3 100644
--- a/website/content/v1.3/reference/api.md
+++ b/website/content/v1.3/reference/api.md
@@ -173,6 +173,7 @@ description: Talos gRPC API reference.
- [MachineStatusSpec](#talos.resource.definitions.runtime.MachineStatusSpec)
- [MachineStatusStatus](#talos.resource.definitions.runtime.MachineStatusStatus)
- [MountStatusSpec](#talos.resource.definitions.runtime.MountStatusSpec)
+ - [PlatformMetadataSpec](#talos.resource.definitions.runtime.PlatformMetadataSpec)
- [UnmetCondition](#talos.resource.definitions.runtime.UnmetCondition)
- [resource/definitions/secrets/secrets.proto](#resource/definitions/secrets/secrets.proto)
@@ -3133,6 +3134,27 @@ MountStatusSpec describes status of the defined sysctls.
+
+
+### PlatformMetadataSpec
+PlatformMetadataSpec describes platform metadata properties.
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| platform | [string](#string) | | |
+| hostname | [string](#string) | | |
+| region | [string](#string) | | |
+| zone | [string](#string) | | |
+| instance_type | [string](#string) | | |
+| instance_id | [string](#string) | | |
+| provider_id | [string](#string) | | |
+
+
+
+
+
+
### UnmetCondition