omni/client/pkg/siderolink/siderolink_test.go
Artem Chernyshev a7ac63725d
chore: rewrite join config generation
Now the machine join config is always generate when there's a `machine`
resource. It will automatically populate the correct parameters for the
machine API URL, logs and events.
If the machine is managed by an infra provider it will populate it's
request ID too.

The default provider join config is also generated, but it is not used
in the common infra provider library, because it's easier to just
generate the config at the moment it's going to be used.

The code for the siderolink join config generation was unified in all
the places, and is now in `client/pkg/siderolink`.

The new management API introduced for downloading the join config in the
UI `GetMachineJoinConfig`.

Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
2025-07-10 13:41:38 +03:00

371 lines
10 KiB
Go

// 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 siderolink_test
import (
"bytes"
"errors"
"io"
"testing"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v2"
"github.com/siderolabs/omni/client/api/omni/specs"
"github.com/siderolabs/omni/client/pkg/omni/resources"
"github.com/siderolabs/omni/client/pkg/omni/resources/infra"
"github.com/siderolabs/omni/client/pkg/omni/resources/omni"
"github.com/siderolabs/omni/client/pkg/siderolink"
)
type machine struct {
providerID string
requestID string
useGRPCTUnnel bool
}
func TestConnectionParamsKernelArgs(t *testing.T) {
//nolint:govet
for _, tt := range []struct {
machine *machine
expectedArgs []string
joinToken string
apiURL string
name string
providerID string
grpcTunnel specs.GrpcTunnelMode
eventSinkPort int
logServerPort int
expectError bool
extraOpts []siderolink.JoinConfigOption
}{
{
grpcTunnel: specs.GrpcTunnelMode_ENABLED,
apiURL: "grpc://127.0.0.1:8099",
joinToken: "abcd",
name: "insecure",
expectedArgs: []string{
"siderolink.api=grpc://127.0.0.1:8099?grpc_tunnel=true&jointoken=abcd",
"talos.events.sink=[fdae:41e4:649b:9303::1]:8091",
"talos.logging.kernel=tcp://[fdae:41e4:649b:9303::1]:8092",
},
eventSinkPort: 8091,
logServerPort: 8092,
},
{
joinToken: "abcd",
name: "secure",
apiURL: "https://127.0.0.1:8099",
expectedArgs: []string{
"siderolink.api=https://127.0.0.1:8099?jointoken=abcd",
"talos.events.sink=[fdae:41e4:649b:9303::1]:8091",
"talos.logging.kernel=tcp://[fdae:41e4:649b:9303::1]:8092",
},
eventSinkPort: 8091,
logServerPort: 8092,
},
{
joinToken: "abcd",
name: "with machine and request ID",
apiURL: "https://127.0.0.1:8099",
expectedArgs: []string{
"siderolink.api=https://127.0.0.1:8099?grpc_tunnel=true&jointoken=v1%3AeyJleHRyYV9kYXRhIjp7Im9tbmk" +
"uc2lkZXJvLmRldi9pbmZyYS1wcm92aWRlci1pZCI6InRlc3QiLCJvbW5pLnNpZGVyby5kZXYvbWFjaGluZS1yZXF1ZXN0Ijo" +
"icmVxdWVzdDEifSwic2lnbmF0dXJlIjoiellrTFRwOUVIanlwTGxrZG1mZjV2Z1A1SERXRktUUXBGR281REp2TDV4MD0ifQ%3D%3D",
"talos.events.sink=[fdae:41e4:649b:9303::1]:8091",
"talos.logging.kernel=tcp://[fdae:41e4:649b:9303::1]:8092",
},
machine: &machine{
requestID: "request1",
providerID: "test",
useGRPCTUnnel: true,
},
eventSinkPort: 8091,
logServerPort: 8092,
},
{
name: "no token",
apiURL: "https://127.0.0.1:8099",
expectError: true,
eventSinkPort: 8091,
logServerPort: 8092,
},
{
joinToken: "abcd",
name: "with provider",
apiURL: "https://127.0.0.1:8099",
expectedArgs: []string{
"siderolink.api=https://127.0.0.1:8099?jointoken=v1%3AeyJleHRyYV9kYXRhIjp7Im9tbmkuc2lkZXJvLmRldi9pbmZyYS1wcm92a" +
"WRlci1pZCI6InRlc3QifSwic2lnbmF0dXJlIjoiWTNpZ285V2xJSVZOWWpXZmgyWlg5NnpnWW5UQjlwWTI3ZEJaVnJwNDJMZz0ifQ%3D%3D",
"talos.events.sink=[fdae:41e4:649b:9303::1]:8091",
"talos.logging.kernel=tcp://[fdae:41e4:649b:9303::1]:8092",
},
providerID: "test",
eventSinkPort: 8091,
logServerPort: 8092,
},
{
joinToken: "abcd",
name: "with all params",
apiURL: "https://127.0.0.1:8099",
expectedArgs: []string{
"siderolink.api=https://127.0.0.1:8099?jointoken=abcd",
"talos.events.sink=[fdae:41e4:649b:9303::1]:8091",
"talos.logging.kernel=tcp://[fdae:41e4:649b:9303::1]:8092",
},
eventSinkPort: 8091,
logServerPort: 8092,
},
} {
t.Run(tt.name, func(t *testing.T) {
options := []siderolink.JoinConfigOption{
siderolink.WithMachineAPIURL(tt.apiURL),
siderolink.WithEventSinkPort(tt.eventSinkPort),
siderolink.WithLogServerPort(tt.logServerPort),
siderolink.WithJoinToken(tt.joinToken),
}
if tt.machine != nil {
m := omni.NewMachine(resources.DefaultNamespace, "machine-1")
m.TypedSpec().Value.UseGrpcTunnel = tt.machine.useGRPCTUnnel
if tt.machine.requestID != "" {
m.Metadata().Labels().Set(omni.LabelMachineRequest, tt.machine.requestID)
}
if tt.machine.providerID != "" {
m.Metadata().Annotations().Set(omni.LabelInfraProviderID, tt.machine.providerID)
}
options = append(options, siderolink.WithMachine(m))
}
if tt.providerID != "" {
options = append(options, siderolink.WithProvider(infra.NewProvider(tt.providerID)))
}
if tt.grpcTunnel == specs.GrpcTunnelMode_ENABLED {
options = append(options, siderolink.WithGRPCTunnel(true))
}
if tt.extraOpts != nil {
options = append(options, tt.extraOpts...)
}
opts, err := siderolink.NewJoinOptions(options...)
if tt.expectError {
require.Error(t, err)
return
}
require.NoError(t, err)
args := opts.GetKernelArgs()
require.Equal(t, tt.expectedArgs, args)
})
}
}
//nolint:gocognit
func TestConnectionParamsJoinConfig(t *testing.T) {
for _, tt := range []struct {
machine *machine
expectedConfig string
joinToken string
apiURL string
name string
providerID string
eventSinkPort int
logServerPort int
expectError bool
grpcTunnel specs.GrpcTunnelMode
}{
{
grpcTunnel: specs.GrpcTunnelMode_ENABLED,
apiURL: "grpc://127.0.0.1:8099",
joinToken: "abcd",
name: "insecure",
eventSinkPort: 8091,
logServerPort: 8092,
expectedConfig: `apiVersion: v1alpha1
kind: SideroLinkConfig
apiUrl: grpc://127.0.0.1:8099?grpc_tunnel=true&jointoken=abcd
---
apiVersion: v1alpha1
kind: EventSinkConfig
endpoint: '[fdae:41e4:649b:9303::1]:8091'
---
apiVersion: v1alpha1
kind: KmsgLogConfig
name: omni-kmsg
url: tcp://[fdae:41e4:649b:9303::1]:8092
`,
},
{
joinToken: "abcd",
name: "secure",
apiURL: "https://127.0.0.1:8099",
eventSinkPort: 8091,
logServerPort: 8092,
expectedConfig: `apiVersion: v1alpha1
kind: SideroLinkConfig
apiUrl: https://127.0.0.1:8099?jointoken=abcd
---
apiVersion: v1alpha1
kind: EventSinkConfig
endpoint: '[fdae:41e4:649b:9303::1]:8091'
---
apiVersion: v1alpha1
kind: KmsgLogConfig
name: omni-kmsg
url: tcp://[fdae:41e4:649b:9303::1]:8092
`,
},
{
joinToken: "abcd",
name: "with machine and request ID",
apiURL: "https://127.0.0.1:8099",
expectedConfig: "apiVersion: v1alpha1\n" +
"kind: SideroLinkConfig\n" +
"apiUrl: https://127.0.0.1:8099?grpc_tunnel=true&jointoken=v1%3AeyJleHRyYV9kYXRhIjp7Im9tbmkuc2lkZXJvLmRldi9pbmZyYS1wcm" +
"92aWRlci1pZCI6InRlc3QiLCJvbW5pLnNpZGVyby5kZXYvbWFjaGluZS1yZXF1ZXN0IjoicmVxdWVzdDEifSwic2lnbmF0dXJlIjoiellrTFRwOUVIanl" +
"wTGxrZG1mZjV2Z1A1SERXRktUUXBGR281REp2TDV4MD0ifQ%3D%3D\n" +
"---\n" +
"apiVersion: v1alpha1\n" +
"kind: EventSinkConfig\n" +
"endpoint: '[fdae:41e4:649b:9303::1]:8091'\n" +
"---\n" +
"apiVersion: v1alpha1\n" +
"kind: KmsgLogConfig\n" +
"name: omni-kmsg\n" +
"url: tcp://[fdae:41e4:649b:9303::1]:8092\n",
machine: &machine{
requestID: "request1",
providerID: "test",
useGRPCTUnnel: true,
},
eventSinkPort: 8091,
logServerPort: 8092,
},
{
joinToken: "abcd",
name: "with provider",
apiURL: "https://127.0.0.1:8099",
expectedConfig: "apiVersion: v1alpha1\n" +
"kind: SideroLinkConfig\n" +
"apiUrl: https://127.0.0.1:8099?jointoken=v1%3AeyJleHRyYV9kYXRhIjp7Im9tbmkuc2lkZXJvLmRldi9pbmZyYS1wcm92a" +
"WRlci1pZCI6InRlc3QifSwic2lnbmF0dXJlIjoiWTNpZ285V2xJSVZOWWpXZmgyWlg5NnpnWW5UQjlwWTI3ZEJaVnJwNDJMZz0ifQ%3D%3D\n" +
"---\n" +
"apiVersion: v1alpha1\n" +
"kind: EventSinkConfig\n" +
"endpoint: '[fdae:41e4:649b:9303::1]:8091'\n" +
"---\n" +
"apiVersion: v1alpha1\n" +
"kind: KmsgLogConfig\n" +
"name: omni-kmsg\n" +
"url: tcp://[fdae:41e4:649b:9303::1]:8092\n",
providerID: "test",
eventSinkPort: 8091,
logServerPort: 8092,
},
{
joinToken: "abcd",
name: "with all params",
apiURL: "https://127.0.0.1:8099",
expectedConfig: `apiVersion: v1alpha1
kind: SideroLinkConfig
apiUrl: https://127.0.0.1:8099?jointoken=abcd
---
apiVersion: v1alpha1
kind: EventSinkConfig
endpoint: '[fdae:41e4:649b:9303::1]:8091'
---
apiVersion: v1alpha1
kind: KmsgLogConfig
name: omni-kmsg
url: tcp://[fdae:41e4:649b:9303::1]:8092
`,
eventSinkPort: 8091,
logServerPort: 8092,
},
{
expectError: true,
},
} {
t.Run(tt.name, func(t *testing.T) {
options := []siderolink.JoinConfigOption{
siderolink.WithMachineAPIURL(tt.apiURL),
siderolink.WithEventSinkPort(tt.eventSinkPort),
siderolink.WithLogServerPort(tt.logServerPort),
siderolink.WithJoinToken(tt.joinToken),
}
if tt.machine != nil {
m := omni.NewMachine(resources.DefaultNamespace, "machine-1")
m.TypedSpec().Value.UseGrpcTunnel = tt.machine.useGRPCTUnnel
if tt.machine.requestID != "" {
m.Metadata().Labels().Set(omni.LabelMachineRequest, tt.machine.requestID)
}
if tt.machine.providerID != "" {
m.Metadata().Annotations().Set(omni.LabelInfraProviderID, tt.machine.providerID)
}
options = append(options, siderolink.WithMachine(m))
}
if tt.providerID != "" {
options = append(options, siderolink.WithProvider(infra.NewProvider(tt.providerID)))
}
if tt.grpcTunnel == specs.GrpcTunnelMode_ENABLED {
options = append(options, siderolink.WithGRPCTunnel(true))
}
opts, err := siderolink.NewJoinOptions(options...)
if tt.expectError {
require.Error(t, err)
return
}
require.NoError(t, err)
config, err := opts.RenderJoinConfig()
if tt.expectError {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tt.expectedConfig, string(config))
dec := yaml.NewDecoder(bytes.NewBuffer(config))
for i := range 5 {
var dest any
err = dec.Decode(&dest)
if errors.Is(err, io.EOF) {
break
}
require.NoError(t, err)
if i > 3 {
t.Errorf("found extra documents")
t.FailNow()
}
}
})
}
}