From 517640ad365e94b1587e8c1de80032b0cf04d402 Mon Sep 17 00:00:00 2001 From: budimanjojo Date: Sun, 21 Jan 2024 19:33:34 +0700 Subject: [PATCH] feat(config)!: `controlPlane` and `worker` is now type of `NodeConfigs` This will allow all `NodeConfigs` fields to be applicable on node group (`controlPlane` or `worker` depending on node type) or per node level. The config will be "merged" with everything defined on per node level take precedence, except for `patches` and `extraManifests` to keep the behavior before this commit. Patches and extaManifests defined on both node and node group level will be appended instead. There are 2 new node options to change the behavior of `patches` and `extraManifests` when defined at both the node and node group level. Which are: `overridePatches` and `overrideExtraManifests`. By setting them to `true` will make the `patches` and `extraManifests` defined in node level to override the ones defined in node group level. The config validation is now done after the "merging" is done for each node. So, i.e when you define `schematic` incorrectly in `controlPlane` struct, you might see the validation says you have incorrect `nodes[0].schematic` instead assuming `nodes[0]` is a controlPlane node. BREAKING CHANGE: This commit also remove all the deprecated fields which are: * `talosImageURL` * all `inlinePatch` * all `configPatches` * `nodes[].extensions` --- docs/docs/guides.md | 39 +++ docs/docs/reference/configuration.md | 414 ++++++--------------------- pkg/config/config.go | 66 ++--- pkg/config/defaults.go | 9 - pkg/config/loader.go | 8 +- pkg/config/loader_test.go | 19 +- pkg/config/nodeconfigs.go | 38 +++ pkg/config/nodeconfigs_test.go | 79 +++++ pkg/config/testdata/talconfig.yaml | 1 + pkg/config/validate.go | 4 - pkg/config/validate_test.go | 64 ++--- pkg/config/validator.go | 209 +++----------- pkg/generate/config.go | 74 ----- pkg/talos/input.go | 2 +- pkg/talos/nodeconfig.go | 4 - pkg/talos/nodeconfig_test.go | 6 - 16 files changed, 352 insertions(+), 684 deletions(-) create mode 100644 pkg/config/nodeconfigs.go create mode 100644 pkg/config/nodeconfigs_test.go diff --git a/docs/docs/guides.md b/docs/docs/guides.md index cdee0bff..2db034c5 100644 --- a/docs/docs/guides.md +++ b/docs/docs/guides.md @@ -40,6 +40,45 @@ Here's a more detailed example [talconfig.yaml](https://github.com/budimanjojo/t To see all the available options of the configuration file, head over to [Configuration Reference](reference/configuration.md). +## DRY (Don't Repeat Yourself) in `talconfig.yaml` + +A lot of times, you have similar configurations for all your nodes. +Instead of writing them multiple times for each node, you can make use of `controlPlane` and `worker` fields as "global configurations" for all your node group. + +```{.yaml hl_lines="12-22"} +--- +clusterName: my-cluster +nodes: + - hostname: cp1 + controlPlane: true + ipAddress: 192.168.200.11 + installDisk: /dev/sda + - hostname: cp2 + controlPlane: true + ipAddress: 192.168.200.12 + installDisk: /dev/sda +controlPlane: + schematic: + customization: + extraKernelArgs: + - net.ifnames=0 + patches: + - |- + - op: add + path: /machine/kubelet/extraArgs + value: + rotate-server-certificates: "true" +``` + +The `schematic` and `patches` defined in `controlPlane` will be applied to both `cp1` and `cp2` because they're both in the group of `controlPlane` nodes. + +!!! note + + [NodeConfigs](./reference/configuration.md#nodeconfigs) you define in `controlPlane` or `worker` will be overwritten if you define them per node in `nodes[]` section. + But, for `patches` and `extraManifests` they are appended instead because it makes more sense. + + You **can** modify the default behavior by adding `overridePatches: true` and `overrideExtraManifests: true` inside `nodes[]` for node you don't want the default behavior. + ## Adding Talos extensions and kernel arguments Talos v1.5 introduced a new unified way to generate boot assets for installer container image that you can build yourself using their `imager` container or use [image-factory](https://factory.talos.dev/) to dynamically build it for you. diff --git a/docs/docs/reference/configuration.md b/docs/docs/reference/configuration.md index 8360ee29..04fe9878 100644 --- a/docs/docs/reference/configuration.md +++ b/docs/docs/reference/configuration.md @@ -57,18 +57,6 @@ nodes: :white_check_mark: - -`talosImageURL` -string -**DEPRECATED, won't do anything, use `nodes[].talosImageURL` instead**.
*Show example* -```yaml -talosImageURL: ghcr.io/siderolabs/installer -``` -
-`"ghcr.io/siderolabs/installer"` -:negative_squared_cross_mark: - - `talosVersion` string @@ -215,10 +203,14 @@ patches: `controlPlane` -[ControlPlane](#controlplane) -Configurations targetted for controlplane nodes.
*Show example* +[NodeConfigs](#nodeconfigs) +Configurations targetted for all controlplane nodes.
*Show example* ```yaml controlPlane: + kernelModules: + - name: br_netfilter + parameters: + - nf_conntrack_max=131072 patches: - |- - op: add @@ -238,10 +230,14 @@ controlPlane: `worker` -[Worker](#worker) -Configurations targetted for worker nodes.
*Show example* +[NodeConfigs](#nodeconfigs) +Configurations targetted for all worker nodes.
*Show example* ```yaml worker: + kernelModules: + - name: br_netfilter + parameters: + - nf_conntrack_max=131072 patches: - |- - op: add @@ -306,18 +302,6 @@ installDisk: /dev/sda :white_check_mark: - -`talosImageURL` -string -Allows for supplying the node level image used to perform the installation.
*Show example* -```yaml -talosImageURL: factory.talos.dev/installer/e9c7ef96884d4fbc8c0a1304ccca4bb0287d766a8b4125997cb9dbe84262144e -``` -
-`""` -:negative_squared_cross_mark: - - `installDiskSelector` [InstallDiskSelector](#installdiskselector) @@ -334,6 +318,82 @@ installDiskSelector: :negative_squared_cross_mark: + +`controlPlane` +bool +Whether the node is a controlplane.
*Show example* +```yaml +controlPlane: true +``` + +`false` +:negative_squared_cross_mark: + + + +`overridePatches` +bool +
Whether `patches` defined here should override the one defined in node group.By default they will get appended instead.
*Show example* +```yaml +overridePatches: true +``` + +`false` +:negative_squared_cross_mark: + + + +`overrideExtraManifests` +bool +
Whether `extraManifests` defined here should override the one defined in node group.By default they will get appended instead.
*Show example* +```yaml +overrideExtraManifests: true +``` + +`false` +:negative_squared_cross_mark: + + + +- +[NodeConfigs](#nodeconfigs) +Node specific configurations that will override node group configurations.
*Show example* +```yaml +talosImageURL: factory.talos.dev/installer/e9c7ef96884d4fbc8c0a1304ccca4bb0287d766a8b4125997cb9dbe84262144e +nodeLabels: + rack: rack1a +nodeTaints: + exampleTaint: exampletaintValue:NoSchedule +disableSearchDomain: true +``` + +`nil` +:negative_squared_cross_mark: + + + + +## NodeConfigs + +`NodeConfigs` defines machine configurations. + + + + + + + + + + + + + + @@ -370,18 +430,6 @@ ingressFirewall: - - - - - - - - @@ -450,19 +498,6 @@ machineFiles: - - - - - - - - @@ -566,39 +601,6 @@ patches: - - - - - - - - - - - - - - - -
FieldTypeDescriptionDefault ValueRequired
`talosImageURL`stringAllows for supplying the node level image used to perform the installation.
*Show example* +```yaml +talosImageURL: factory.talos.dev/installer/e9c7ef96884d4fbc8c0a1304ccca4bb0287d766a8b4125997cb9dbe84262144e +``` +
`""`:negative_squared_cross_mark:
`machineSpec` [MachineSpec](#machinespec):negative_squared_cross_mark:
`controlPlane`boolWhether the node is a controlplane.
*Show example* -```yaml -controlPlane: true -``` -
`false`:negative_squared_cross_mark:
`nodeLabels` map[string]string:negative_squared_cross_mark:
`extensions`[][InstallExtensionConfig](#installextensionconfig)
**DEPRECATED, use `schematic` instead**.List of additional system extensions image to install.
*Show example* -```yaml -extensions: - - image: ghcr.io/siderolabs/tailscale:1.44.0 -``` -
`[]`:negative_squared_cross_mark:
`schematic` [Schematic](#schematic):negative_squared_cross_mark:
`configPatches`[]map[string]interface{}
**DEPRECATED, use `patches` instead**.List of RFC6902 JSON patches to be applied to the node.
*Show example* -```yaml -configPatches: - - op: add - path: /machine/install/extraKernelArgs - value: - - console=ttyS1 -``` -
`[]`:negative_squared_cross_mark:
`inlinePatch`map[string]interface{}
**DEPRECATED, use `patches` instead**.Strategic merge patches to be applied to the node.
*Show example* -```yaml -inlinePatch: - machine: - network: - interfaces: - - interface: eth0 - addresses: [192.168.200.11/24] -``` -
`map[]`:negative_squared_cross_mark:
## ImageFactory @@ -802,246 +804,6 @@ ingress: -## ControlPlane - -`ControlPlane` defines machine configurations for controlplane type nodes. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FieldTypeDescriptionDefault ValueRequired
`patches`[]string
Patches to be applied to all controlplane nodes.List of strings containing RFC6902 JSON patches, strategic merge patches,
or a file containing them.
*Show example* -```yaml -patches: - - |- - - op: add - path: /machine/kubelet/extraArgs - value: - rotate-server-certificates: "true" - - |- - machine: - env: - MYENV: value - - "@./a-patch.yaml" -``` -
`[]`:negative_squared_cross_mark:
`configPatches`[]map[string]interface{}
**DEPRECATED, use `patches` instead**.List of RFC6902 JSON patches to be applied to all controlplane nodes.
*Show example* -```yaml -configPatches: - - op: add - path: /machine/install/extraKernelArgs - value: - - console=ttyS1 -``` -
`[]`:negative_squared_cross_mark:
`inlinePatch`map[string]interface{}
**DEPRECATED, use `patches` instead**.Strategic merge patches to be applied to all controlplane nodes.
*Show example* -```yaml -inlinePatch: - machine: - network: - interfaces: - - interface: eth0 - addresses: [192.168.200.11/24] -``` -
`map[]`:negative_squared_cross_mark:
`schematic`[Schematic](#schematic)Configure Talos image customization to be applied to all controlplane nodes
*Show example* -```yaml -schematic: - customization: - extraKernelArgs: - - net.ifnames=0 - systemExtensions: - officialExtensions: - - siderolabs/intel-ucode -``` -
`nil`:negative_squared_cross_mark:
`ingressFirewall`[IngressFirewall](#ingressfirewall)Firewall specification for all controlplane nodes.
*Show example* -```yaml -ingressFirewall: - defaultAction: block - rules: - - name: kubelet-ingress - portSelector: - ports: - - 10250 - protocol: tcp - ingress: - - subnet: 172.20.0.0/24 - except: 172.20.0.1/32 -``` -
`nil`:negative_squared_cross_mark:
`extraManifests`[]stringList of manifest files to be added to all controlplane nodes.
*Show example* -```yaml -extraManifests: - - etcd-firewall.yaml - - kubelet-firewall.yaml -``` -
`[]`:negative_squared_cross_mark:
- -## Worker - -`Worker` defines machine configurations for worker type nodes. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FieldTypeDescriptionDefault ValueRequired
`patches`[]string
Patches to be applied to all worker nodes.List of strings containing RFC6902 JSON patches, strategic merge patches,
or a file containing them.
*Show example* -```yaml -patches: - - |- - - op: add - path: /machine/kubelet/extraArgs - value: - rotate-server-certificates: "true" - - |- - machine: - env: - MYENV: value - - "@./a-patch.yaml" -``` -
`[]`:negative_squared_cross_mark:
`configPatches`[]map[string]interface{}
**DEPRECATED, use `patches` instead**.List of RFC6902 JSON patches to be applied to all worker nodes.
*Show example* -```yaml -configPatches: - - op: add - path: /machine/install/extraKernelArgs - value: - - console=ttyS1 -``` -
`[]`:negative_squared_cross_mark:
`inlinePatch`map[string]interface{}
**DEPRECATED, use `patches` instead**.Strategic merge patches to be applied to all worker nodes.
*Show example* -```yaml -inlinePatch: - machine: - network: - interfaces: - - interface: eth0 - addresses: [192.168.200.11/24] -``` -
`map[]`:negative_squared_cross_mark:
`schematic`[Schematic](#schematic)Configure Talos image customization to be applied to all worker nodes
*Show example* -```yaml -schematic: - customization: - extraKernelArgs: - - net.ifnames=0 - systemExtensions: - officialExtensions: - - siderolabs/intel-ucode -``` -
`nil`:negative_squared_cross_mark:
`ingressFirewall`[IngressFirewall](#ingressfirewall)Firewall specification for all worker nodes.
*Show example* -```yaml -ingressFirewall: - defaultAction: block - rules: - - name: kubelet-ingress - portSelector: - ports: - - 10250 - protocol: tcp - ingress: - - subnet: 172.20.0.0/24 - except: 172.20.0.1/32 -``` -
`nil`:negative_squared_cross_mark:
`extraManifests`[]stringList of manifest files to be added to all worker nodes.
*Show example* -```yaml -extraManifests: - - etcd-firewall.yaml - - kubelet-firewall.yaml -``` -
`[]`:negative_squared_cross_mark:
- ## CNIConfig `CNIConfig` is type of upstream Talos `v1alpha1.CNIConfig` diff --git a/pkg/config/config.go b/pkg/config/config.go index ac77d048..2056a4e9 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -9,7 +9,6 @@ import ( type TalhelperConfig struct { ClusterName string `yaml:"clusterName" jsonschema:"required,description=Name of the cluster"` - TalosImageURL string `yaml:"talosImageURL" jsonschema:"default=ghcr.io/siderolabs/installer,description=DEPRECATED: will not do anything, use \"nodes[].talosImageURL\" instead"` TalosVersion string `yaml:"talosVersion,omitempty" jsonschema:"example=v1.5.4,description=Talos version to perform installation"` KubernetesVersion string `yaml:"kubernetesVersion,omitempty" jsonschema:"example=v1.27.0,description=Kubernetes version to use"` Endpoint string `yaml:"endpoint" jsonschema:"required,example=https://192.168.200.10:6443,description=Cluster's controlplane endpoint"` @@ -24,51 +23,36 @@ type TalhelperConfig struct { Patches []string `yaml:"patches,omitempty" jsonschema:"description=Patches to be applied to all nodes"` Nodes []Node `yaml:"nodes" jsonschema:"required,description=List of configurations for Node"` ImageFactory ImageFactory `yaml:"imageFactory,omitempty" jsonschema:"Configuration for image factory"` - ControlPlane controlPlane `yaml:"controlPlane,omitempty" jsonschema:"description=Configurations targetted for controlplane nodes"` - Worker worker `yaml:"worker,omitempty" jsonschema:"description=Configurations targetted for worker nodes"` + ControlPlane NodeConfigs `yaml:"controlPlane,omitempty" jsonschema:"description=Configurations targetted for all controlplane nodes"` + Worker NodeConfigs `yaml:"worker,omitempty" jsonschema:"description=Configurations targetted for all worker nodes"` } type Node struct { - Hostname string `yaml:"hostname" jsonschema:"required,description=Hostname of the node"` - IPAddress string `yaml:"ipAddress,omitempty" jsonschema:"required,example=192.168.200.11,description=IP address where the node can be reached"` - ControlPlane bool `yaml:"controlPlane" jsonschema:"description=Whether the node is a controlplane"` - NodeLabels map[string]string `yaml:"nodeLabels" jsonschema:"description=Labels to be added to the node"` - NodeTaints map[string]string `yaml:"nodeTaints" jsonschema:"description=Node taints for the node. Effect is optional"` - InstallDisk string `yaml:"installDisk,omitempty" jsonschema:"oneof_required=installDiskSelector,description=The disk used for installation"` - InstallDiskSelector *v1alpha1.InstallDiskSelector `yaml:"installDiskSelector,omitempty" jsonschema:"oneof_required=installDisk,description=Look up disk used for installation"` - MachineDisks []*v1alpha1.MachineDisk `yaml:"machineDisks,omitempty" jsonschema:"description=List of additional disks to partition, format, mount"` - MachineFiles []*v1alpha1.MachineFile `yaml:"machineFiles,omitempty" jsonschema:"description=List of files to create inside the node"` - Extensions []v1alpha1.InstallExtensionConfig `yaml:"extensions,omitempty" jsonschema:"description=DEPRECATED: use \"schematic\" instead"` - DisableSearchDomain bool `yaml:"disableSearchDomain,omitempty" jsonschema:"description=Whether to disable generating default search domain"` - KernelModules []*v1alpha1.KernelModuleConfig `yaml:"kernelModules,omitempty" jsonschema:"description=List of additional kernel modules to load inside the node"` - Nameservers []string `yaml:"nameservers,omitempty" jsonschema:"description=List of nameservers for the node"` - NetworkInterfaces []*v1alpha1.Device `yaml:"networkInterfaces,omitempty" jsonschema:"description=List of network interface configuration for the node"` - ExtraManifests []string `yaml:"extraManifests,omitempty" jsonschema:"description=List of manifest files to be added to the node"` - ConfigPatches []map[string]interface{} `yaml:"configPatches,omitempty" jsonschema:"description=DEPRECATED: use \"patches\" instead"` - InlinePatch map[string]interface{} `yaml:"inlinePatch,omitempty" jsonschema:"description=DEPRECATED: use \"patches\" instead"` - Patches []string `yaml:"patches,omitempty" jsonschema:"description=Patches to be applied to the node"` - TalosImageURL string `yaml:"talosImageURL" jsonschema:"example=factory.talos.dev/installer/e9c7ef96884d4fbc8c0a1304ccca4bb0287d766a8b4125997cb9dbe84262144e,description=Talos installer image url for the node"` - Schematic *schematic.Schematic `yaml:"schematic,omitempty" jsonschema:"description=Talos image customization to be used in the installer image"` - MachineSpec MachineSpec `yaml:"machineSpec,omitempty" jsonschema:"description=Machine hardware specification"` - IngressFirewall *IngressFirewall `yaml:"ingressFirewall,omitempty" jsonschema:"description=Machine firewall specification"` + Hostname string `yaml:"hostname" jsonschema:"required,description=Hostname of the node"` + IPAddress string `yaml:"ipAddress,omitempty" jsonschema:"required,example=192.168.200.11,description=IP address where the node can be reached"` + ControlPlane bool `yaml:"controlPlane" jsonschema:"description=Whether the node is a controlplane"` + InstallDisk string `yaml:"installDisk,omitempty" jsonschema:"oneof_required=installDiskSelector,description=The disk used for installation"` + InstallDiskSelector *v1alpha1.InstallDiskSelector `yaml:"installDiskSelector,omitempty" jsonschema:"oneof_required=installDisk,description=Look up disk used for installation"` + OverridePatches bool `yaml:"overridePatches,omitempty" jsonschema:"description=Whether \"patches\" defined here should override the one defined in node group"` + OverrideExtraManifests bool `yaml:"overrideExtraManifests,omitempty" jsonschema:"description=Whether \"extraManifests\" defined here should override the one defined in node group"` + NodeConfigs `yaml:",inline" jsonschema:"description=Node specific configurations that will override node group configurations"` } -type controlPlane struct { - ConfigPatches []map[string]interface{} `yaml:"configPatches,omitempty" jsonschema:"description=DEPRECATED: use \"patches\" instead"` - InlinePatch map[string]interface{} `yaml:"inlinePatch,omitempty" jsonschema:"description=DEPRECATED: use \"patches\" instead"` - Patches []string `yaml:"patches,omitempty" jsonschema:"description=Patches to be applied to all controlplane nodes"` - Schematic *schematic.Schematic `yaml:"schematic,omitempty" jsonschema:"description=Talos image customization to be applied to all controlplane nodes"` - IngressFirewall *IngressFirewall `yaml:"ingressFirewall,omitempty" jsonschema:"description=Firewall specification for all controlplane nodes"` - ExtraManifests []string `yaml:"extraManifests,omitempty" jsonschema:"description=List of manifest files to be added to all controlplane nodes"` -} - -type worker struct { - ConfigPatches []map[string]interface{} `yaml:"configPatches,omitempty" jsonschema:"description=DEPRECATED: use \"patches\" instead"` - InlinePatch map[string]interface{} `yaml:"inlinePatch,omitempty" jsonschema:"description=DEPRECATED: use \"patches\" instead"` - Patches []string `yaml:"patches,omitempty" jsonschema:"description=Patches to be applied to all worker nodes"` - Schematic *schematic.Schematic `yaml:"schematic,omitempty" jsonschema:"description=Talos image customization to be applied to all worker nodes"` - IngressFirewall *IngressFirewall `yaml:"ingressFirewall,omitempty" jsonschema:"description=Firewall specification for all worker nodes"` - ExtraManifests []string `yaml:"extraManifests,omitempty" jsonschema:"description=List of manifest files to be added to all worker nodes"` +type NodeConfigs struct { + NodeLabels map[string]string `yaml:"nodeLabels" jsonschema:"description=Labels to be added to the node"` + NodeTaints map[string]string `yaml:"nodeTaints" jsonschema:"description=Node taints for the node. Effect is optional"` + MachineDisks []*v1alpha1.MachineDisk `yaml:"machineDisks,omitempty" jsonschema:"description=List of additional disks to partition, format, mount"` + MachineFiles []*v1alpha1.MachineFile `yaml:"machineFiles,omitempty" jsonschema:"description=List of files to create inside the node"` + DisableSearchDomain bool `yaml:"disableSearchDomain,omitempty" jsonschema:"description=Whether to disable generating default search domain"` + KernelModules []*v1alpha1.KernelModuleConfig `yaml:"kernelModules,omitempty" jsonschema:"description=List of additional kernel modules to load inside the node"` + Nameservers []string `yaml:"nameservers,omitempty" jsonschema:"description=List of nameservers for the node"` + NetworkInterfaces []*v1alpha1.Device `yaml:"networkInterfaces,omitempty" jsonschema:"description=List of network interface configuration for the node"` + ExtraManifests []string `yaml:"extraManifests,omitempty" jsonschema:"description=List of manifest files to be added to the node"` + Patches []string `yaml:"patches,omitempty" jsonschema:"description=Patches to be applied to the node"` + TalosImageURL string `yaml:"talosImageURL" jsonschema:"example=factory.talos.dev/installer/e9c7ef96884d4fbc8c0a1304ccca4bb0287d766a8b4125997cb9dbe84262144e,description=Talos installer image url for the node"` + Schematic *schematic.Schematic `yaml:"schematic,omitempty" jsonschema:"description=Talos image customization to be used in the installer image"` + MachineSpec MachineSpec `yaml:"machineSpec,omitempty" jsonschema:"description=Machine hardware specification"` + IngressFirewall *IngressFirewall `yaml:"ingressFirewall,omitempty" jsonschema:"description=Machine firewall specification"` } type ImageFactory struct { diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index 9806fc15..aa9972e2 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -71,15 +71,6 @@ func (c *TalhelperConfig) GetClusterSvcNets() []string { return c.ClusterSvcNets } -// GetInstallerURL returns installer URL string. -func (c *TalhelperConfig) GetInstallerURL() string { - if c.TalosImageURL != "" { - return c.TalosImageURL + ":" + c.GetTalosVersion() - } - - return "ghcr.io/siderolabs/installer:" + c.GetTalosVersion() -} - // GetImageFactory returns default `imageFactory` if not specified. func (c *TalhelperConfig) GetImageFactory() *ImageFactory { result := &ImageFactory{ diff --git a/pkg/config/loader.go b/pkg/config/loader.go index aff1fe42..d84b60dd 100644 --- a/pkg/config/loader.go +++ b/pkg/config/loader.go @@ -35,13 +35,9 @@ func LoadAndValidateFromFile(filePath string, envPaths []string) (*TalhelperConf for k, node := range cfg.Nodes { switch node.ControlPlane { case true: - if cfg.ControlPlane.Schematic != nil && node.Schematic == nil { - cfg.Nodes[k].Schematic = cfg.ControlPlane.Schematic - } + cfg.Nodes[k].OverrideGlobalCfg(cfg.ControlPlane) case false: - if cfg.Worker.Schematic != nil && node.Schematic == nil { - cfg.Nodes[k].Schematic = cfg.Worker.Schematic - } + cfg.Nodes[k].OverrideGlobalCfg(cfg.Worker) } } diff --git a/pkg/config/loader_test.go b/pkg/config/loader_test.go index 8f95fcfa..fdc3f870 100644 --- a/pkg/config/loader_test.go +++ b/pkg/config/loader_test.go @@ -19,12 +19,15 @@ func TestLoadAndValidateFromFile(t *testing.T) { IPAddress: "192.168.200.10", ControlPlane: true, InstallDisk: "/dev/sda", - Schematic: &schematic.Schematic{ - Customization: schematic.Customization{ - SystemExtensions: schematic.SystemExtensions{ - OfficialExtensions: []string{"siderolabs/tailscale"}, + NodeConfigs: NodeConfigs{ + Schematic: &schematic.Schematic{ + Customization: schematic.Customization{ + SystemExtensions: schematic.SystemExtensions{ + OfficialExtensions: []string{"siderolabs/tailscale"}, + }, }, }, + DisableSearchDomain: true, }, } expectedNode1 := Node{ @@ -32,9 +35,11 @@ func TestLoadAndValidateFromFile(t *testing.T) { IPAddress: "192.168.200.11", ControlPlane: false, InstallDisk: "/dev/sda", - Schematic: &schematic.Schematic{ - Customization: schematic.Customization{ - ExtraKernelArgs: []string{"net.ifnames=0"}, + NodeConfigs: NodeConfigs{ + Schematic: &schematic.Schematic{ + Customization: schematic.Customization{ + ExtraKernelArgs: []string{"net.ifnames=0"}, + }, }, }, } diff --git a/pkg/config/nodeconfigs.go b/pkg/config/nodeconfigs.go new file mode 100644 index 00000000..38048148 --- /dev/null +++ b/pkg/config/nodeconfigs.go @@ -0,0 +1,38 @@ +package config + +import ( + "reflect" +) + +func (node *Node) OverrideGlobalCfg(cfg NodeConfigs) *Node { + node.NodeConfigs = mergeNodeConfigs(node.NodeConfigs, cfg, node.OverridePatches, node.OverrideExtraManifests) + + return node +} + +func mergeNodeConfigs(patch, src NodeConfigs, overridePatches, overrideExtraManifest bool) NodeConfigs { + if len(src.Patches) > 0 && !overridePatches { + patch.Patches = append(patch.Patches, src.Patches...) + } + if len(src.ExtraManifests) > 0 && !overrideExtraManifest { + patch.ExtraManifests = append(patch.ExtraManifests, src.ExtraManifests...) + } + + patchValue := reflect.ValueOf(patch) + srcValue := reflect.ValueOf(src) + + result := reflect.New(patchValue.Type()).Elem() + + for i := 0; i < patchValue.NumField(); i++ { + patchField := patchValue.Field(i) + srcField := srcValue.Field(i) + + if !patchField.IsZero() { + result.Field(i).Set(patchField) + } else { + result.Field(i).Set(srcField) + } + } + + return result.Interface().(NodeConfigs) +} diff --git a/pkg/config/nodeconfigs_test.go b/pkg/config/nodeconfigs_test.go new file mode 100644 index 00000000..7ce425de --- /dev/null +++ b/pkg/config/nodeconfigs_test.go @@ -0,0 +1,79 @@ +package config + +import ( + "reflect" + "testing" + + "github.com/siderolabs/image-factory/pkg/schematic" + "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" +) + +func TestOverrideNodeConfigs(t *testing.T) { + globalCfg := NodeConfigs{ + NodeLabels: map[string]string{ + "testkey": "testValue", + }, + Schematic: &schematic.Schematic{ + Customization: schematic.Customization{ + ExtraKernelArgs: []string{"enable=1"}, + }, + }, + } + + node := Node{ + Hostname: "test-host", + IPAddress: "123.456.789.1", + InstallDisk: "/dev/test", + ControlPlane: true, + NodeConfigs: NodeConfigs{ + NodeLabels: map[string]string{ + "testkey": "overwritten", + }, + MachineDisks: []*v1alpha1.MachineDisk{ + { + DeviceName: "/dev/sda", + DiskPartitions: []*v1alpha1.DiskPartition{ + { + DiskSize: v1alpha1.DiskSize(1), + DiskMountPoint: "/hello", + }, + }, + }, + }, + }, + } + + expectedNode := Node{ + Hostname: "test-host", + IPAddress: "123.456.789.1", + InstallDisk: "/dev/test", + ControlPlane: true, + NodeConfigs: NodeConfigs{ + NodeLabels: map[string]string{ + "testkey": "overwritten", + }, + MachineDisks: []*v1alpha1.MachineDisk{ + { + DeviceName: "/dev/sda", + DiskPartitions: []*v1alpha1.DiskPartition{ + { + DiskSize: v1alpha1.DiskSize(1), + DiskMountPoint: "/hello", + }, + }, + }, + }, + Schematic: &schematic.Schematic{ + Customization: schematic.Customization{ + ExtraKernelArgs: []string{"enable=1"}, + }, + }, + }, + } + + node.OverrideGlobalCfg(globalCfg) + + if !reflect.DeepEqual(node, expectedNode) { + t.Errorf("got:\n%v\nwant:\n%v", node, expectedNode) + } +} diff --git a/pkg/config/testdata/talconfig.yaml b/pkg/config/testdata/talconfig.yaml index c3738e18..21f62f85 100644 --- a/pkg/config/testdata/talconfig.yaml +++ b/pkg/config/testdata/talconfig.yaml @@ -16,6 +16,7 @@ nodes: installDisk: /dev/sda controlPlane: false controlPlane: + disableSearchDomain: true schematic: customization: systemExtensions: diff --git a/pkg/config/validate.go b/pkg/config/validate.go index d6076120..7dc3f4c3 100644 --- a/pkg/config/validate.go +++ b/pkg/config/validate.go @@ -44,8 +44,6 @@ func (c TalhelperConfig) Validate() (Errors, Warnings) { checkDomain(c, &result) checkClusterNets(c, &result) checkCNIConfig(c, &result) - checkControlPlane(c, &result) - checkWorker(c, &result) for k, node := range c.Nodes { checkNodeRequiredCfg(node, k, &result) checkNodeIPAddress(node, k, &result) @@ -54,11 +52,9 @@ func (c TalhelperConfig) Validate() (Errors, Warnings) { checkNodeTaints(node, k, &result) checkNodeMachineDisks(node, k, &result) checkNodeMachineFiles(node, k, &result) - checkNodeExtensions(node, k, &result, &warns) checkNodeSchematic(node, k, c.GetTalosVersion(), &result) checkNodeNameServers(node, k, &result) checkNodeNetworkInterfaces(node, k, &result) - checkNodeConfigPatches(node, k, &result) checkNodeIngressFirewall(node, k, &result) checkNodeExtraManifests(node, k, &result) } diff --git a/pkg/config/validate_test.go b/pkg/config/validate_test.go index 1de9ad9d..3a5492d2 100644 --- a/pkg/config/validate_test.go +++ b/pkg/config/validate_test.go @@ -33,8 +33,6 @@ nodes: ipAddress: 1.2.3.4.5 installDisk: /dev/sda disableSearchDomain: true - extensions: - - image: hehe nameservers: - 8.8.8.8 networkInterfaces: @@ -44,9 +42,6 @@ nodes: routes: - network: 0.0.0.0/0 gateway: 1.2.3.4.5.6 - configPatches: - - op: del - path: /cluster ingressFirewall: defaultAction: block rules: @@ -73,41 +68,34 @@ nodes: - test.yaml `) - errs, warns, err := ValidateFromByte(data) + errs, _, err := ValidateFromByte(data) if err != nil { t.Fatal(err) } expectedErrors := map[string]bool{ - "clusterName": false, - "talosVersion": true, - "kubernetesVersion": true, - "endpoint": true, - "cniConfig": true, - "clusterPodNets": false, - "clusterSvcNets": true, - "controlPlane.ingressFirewall": true, - "worker.extraManifests": true, - "nodes[0].hostname": false, - "nodes[0].ipAddress": false, - "nodes[0].controlPlane": false, - "nodes[0].installDisk": false, - "nodes[0].nameservers": false, - "nodes[0].ingressFirewall": false, - "nodes[0].networkInterfaces": true, - "nodes[0].configPatches": true, - "nodes[1].hostname": true, - "nodes[1].ipAddress": true, - "nodes[1].installDisk": true, - "nodes[1].nodeLabels": true, - "nodes[1].nodeTaints": true, - "nodes[1].machineFiles": true, - "nodes[1].schematic": true, - "nodes[1].extraManifests": true, - } - - expectedWarnings := map[string]bool{ - "nodes[0].extensions": true, + "clusterName": false, + "talosVersion": true, + "kubernetesVersion": true, + "endpoint": true, + "cniConfig": true, + "clusterPodNets": false, + "clusterSvcNets": true, + "nodes[0].hostname": false, + "nodes[0].ipAddress": false, + "nodes[0].controlPlane": false, + "nodes[0].installDisk": false, + "nodes[0].nameservers": false, + "nodes[0].ingressFirewall": false, + "nodes[0].networkInterfaces": true, + "nodes[1].hostname": true, + "nodes[1].ipAddress": true, + "nodes[1].installDisk": true, + "nodes[1].nodeLabels": true, + "nodes[1].nodeTaints": true, + "nodes[1].machineFiles": true, + "nodes[1].schematic": true, + "nodes[1].extraManifests": true, } for k, v := range expectedErrors { @@ -115,10 +103,4 @@ nodes: t.Errorf("%s: got %t, want %t", k, errs.HasField(k), v) } } - - for k, v := range expectedWarnings { - if warns.HasField(k) != v { - t.Errorf("%s: got %t, want %t", k, warns.HasField(k), v) - } - } } diff --git a/pkg/config/validator.go b/pkg/config/validator.go index 05b236fa..3850657a 100644 --- a/pkg/config/validator.go +++ b/pkg/config/validator.go @@ -193,72 +193,6 @@ func checkCNIConfig(c TalhelperConfig, result *Errors) *Errors { return result } -func checkControlPlane(c TalhelperConfig, result *Errors) *Errors { - if len(c.ControlPlane.ConfigPatches) > 0 { - if !isRFC6902List(c.ControlPlane.ConfigPatches) { - result = result.Append(&Error{ - Kind: "InvalidControlPlaneConfigPatches", - Field: getFieldYamlTag(c, "ControlPlane.ConfigPatches"), - Message: formatError(multierror.Append(fmt.Errorf("doesn't look like list of RFC6902 JSON patches"))), - }) - } - } - - if c.ControlPlane.IngressFirewall != nil { - if err := checkIngressFirewall(c.ControlPlane.IngressFirewall); err != nil { - result = result.Append(&Error{ - Kind: "InvalidControlPlaneIngressFirewall", - Field: getFieldYamlTag(c, "ControlPlane.IngressFirewall"), - Message: formatError(multierror.Append(err)), - }) - } - } - - if len(c.ControlPlane.ExtraManifests) > 0 { - if err := checkExtraManifests(c.ControlPlane.ExtraManifests); err != nil { - result = result.Append(&Error{ - Kind: "InvalidControlPlaneExtraManifests", - Field: getFieldYamlTag(c, "ControlPlane.ExtraManifests"), - Message: formatError(multierror.Append(err)), - }) - } - } - return result -} - -func checkWorker(c TalhelperConfig, result *Errors) *Errors { - if len(c.Worker.ConfigPatches) > 0 { - if !isRFC6902List(c.Worker.ConfigPatches) { - result = result.Append(&Error{ - Kind: "InvalidWorkerConfigPatches", - Field: getFieldYamlTag(c, "Worker.ConfigPatches"), - Message: formatError(multierror.Append(fmt.Errorf("doesn't look like list of RFC6902 JSON patches"))), - }) - } - } - - if c.Worker.IngressFirewall != nil { - if err := checkIngressFirewall(c.Worker.IngressFirewall); err != nil { - result = result.Append(&Error{ - Kind: "InvalidWorkerIngressFirewall", - Field: getFieldYamlTag(c, "Worker.IngressFirewall"), - Message: formatError(multierror.Append(err)), - }) - } - } - - if len(c.Worker.ExtraManifests) > 0 { - if err := checkExtraManifests(c.Worker.ExtraManifests); err != nil { - result = result.Append(&Error{ - Kind: "InvalidWorkerExtraManifests", - Field: getFieldYamlTag(c, "Worker.ExtraManifests"), - Message: formatError(multierror.Append(err)), - }) - } - } - return result -} - func checkNodeRequiredCfg(node Node, idx int, result *Errors) *Errors { if node.Hostname == "" { e := &Error{ @@ -393,35 +327,6 @@ func checkNodeMachineFiles(node Node, idx int, result *Errors) *Errors { return result } -func checkNodeExtensions(node Node, idx int, errs *Errors, warns *Warnings) (*Errors, *Warnings) { - if len(node.Extensions) > 0 { - warns.Append(&Warning{ - Kind: "DeprecatedNodeExtensions", - Field: getNodeFieldYamlTag(node, idx, "Extensions"), - Message: formatWarning("`extensions` is deprecated, please use `schematic.customization.systemExtensions` instead"), - }) - var messages *multierror.Error - extensions := map[string]struct{}{} - - for _, ext := range node.Extensions { - if _, exists := extensions[ext.Image()]; exists { - messages = multierror.Append(messages, fmt.Errorf("duplicate system extension %q", ext.Image())) - } - extensions[ext.Image()] = struct{}{} - } - - if messages.ErrorOrNil() != nil { - return errs.Append(&Error{ - Kind: "InvalidNodeExtensions", - Field: getNodeFieldYamlTag(node, idx, "Extensions"), - Message: formatError(messages), - }), warns - } - } - - return errs, warns -} - func checkNodeSchematic(node Node, idx int, talosVersion string, result *Errors) *Errors { var messages *multierror.Error extensions := map[string]struct{}{} @@ -537,23 +442,42 @@ func checkNodeNetworkInterfaces(node Node, idx int, result *Errors) *Errors { return result } -func checkNodeConfigPatches(node Node, idx int, result *Errors) *Errors { - if len(node.ConfigPatches) > 0 { - if !isRFC6902List(node.ConfigPatches) { - e := fmt.Errorf("doesn't look like list of RFC6902 JSON patches") - return result.Append(&Error{ - Kind: "InvalidNodeConfigPatches", - Field: getNodeFieldYamlTag(node, idx, "ConfigPatches"), - Message: formatError(multierror.Append(e)), - }) - } - } - return result -} - func checkNodeIngressFirewall(node Node, idx int, result *Errors) *Errors { if node.IngressFirewall != nil { - messages := checkIngressFirewall(node.IngressFirewall) + var messages *multierror.Error + + if len(node.IngressFirewall.NetworkRules) > 0 { + for k, v := range node.IngressFirewall.NetworkRules { + if v.Name == "" { + messages = multierror.Append(messages, fmt.Errorf("rules[%d]: name is required", k)) + } + + if !v.PortSelector.Protocol.IsAProtocol() { + messages = multierror.Append(messages, fmt.Errorf("rules[%d]: %q is not a valid protocol", k, v.PortSelector.Protocol)) + } + + if len(v.PortSelector.Ports) == 0 { + messages = multierror.Append(messages, fmt.Errorf("rules[%d]: portSelector.ports is required", k)) + } + + if err := v.PortSelector.Ports.Validate(); err != nil { + messages = multierror.Append(messages, fmt.Errorf("rules[%d]: %q", k, err)) + } + + for _, rule := range v.Ingress { + if !rule.Subnet.IsValid() { + messages = multierror.Append(messages, fmt.Errorf("rules[%d]: invalid subnet: %s", k, rule.Subnet)) + } + if !rule.Except.IsZero() && !rule.Except.IsValid() { + messages = multierror.Append(messages, fmt.Errorf("rules[%d]: invalid except: %s", k, rule.Except)) + } + } + } + } + if !node.IngressFirewall.DefaultAction.IsADefaultAction() { + messages = multierror.Append(messages, fmt.Errorf("%q is not a valid default action", node.IngressFirewall.DefaultAction)) + } + if messages.ErrorOrNil() != nil { return result.Append(&Error{ Kind: "InvalidNodeIngressFirewall", @@ -567,7 +491,13 @@ func checkNodeIngressFirewall(node Node, idx int, result *Errors) *Errors { func checkNodeExtraManifests(node Node, idx int, result *Errors) *Errors { if len(node.ExtraManifests) > 0 { - messages := checkExtraManifests(node.ExtraManifests) + var messages *multierror.Error + + for k, manifest := range node.ExtraManifests { + if _, osErr := os.Stat(manifest); osErr != nil { + messages = multierror.Append(messages, fmt.Errorf("extraManifests[%d], %q", k, osErr)) + } + } if messages.ErrorOrNil() != nil { return result.Append(&Error{ @@ -576,62 +506,11 @@ func checkNodeExtraManifests(node Node, idx int, result *Errors) *Errors { Message: formatError(messages), }) } - } return result } -func checkIngressFirewall(ifCfg *IngressFirewall) *multierror.Error { - var messages *multierror.Error - - if len(ifCfg.NetworkRules) > 0 { - for k, v := range ifCfg.NetworkRules { - if v.Name == "" { - messages = multierror.Append(messages, fmt.Errorf("rules[%d]: name is required", k)) - } - - if !v.PortSelector.Protocol.IsAProtocol() { - messages = multierror.Append(messages, fmt.Errorf("rules[%d]: %q is not a valid protocol", k, v.PortSelector.Protocol)) - } - - if len(v.PortSelector.Ports) == 0 { - messages = multierror.Append(messages, fmt.Errorf("rules[%d]: portSelector.ports is required", k)) - } - - if err := v.PortSelector.Ports.Validate(); err != nil { - messages = multierror.Append(messages, fmt.Errorf("rules[%d]: %q", k, err)) - } - - for _, rule := range v.Ingress { - if !rule.Subnet.IsValid() { - messages = multierror.Append(messages, fmt.Errorf("rules[%d]: invalid subnet: %s", k, rule.Subnet)) - } - if !rule.Except.IsZero() && !rule.Except.IsValid() { - messages = multierror.Append(messages, fmt.Errorf("rules[%d]: invalid except: %s", k, rule.Except)) - } - } - } - } - if !ifCfg.DefaultAction.IsADefaultAction() { - messages = multierror.Append(messages, fmt.Errorf("%q is not a valid default action", ifCfg.DefaultAction)) - } - - return messages -} - -func checkExtraManifests(extraManifests []string) *multierror.Error { - var messages *multierror.Error - - for k, manifest := range extraManifests { - if _, osErr := os.Stat(manifest); osErr != nil { - messages = multierror.Append(messages, fmt.Errorf("extraManifests[%d], %q", k, osErr)) - } - } - - return messages -} - var hostnamePattern = sync.OnceValue(func() *regexp.Regexp { return regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`) }) @@ -706,9 +585,9 @@ func formatError(e *multierror.Error) *multierror.Error { return e } -func formatWarning(w string) string { - return fmt.Sprintf(" * WARNING: %s", w) -} +// func formatWarning(w string) string { +// return fmt.Sprintf(" * WARNING: %s", w) +// } func getNodeFieldYamlTag(node Node, idx int, fieldPath string) string { return "nodes[" + fmt.Sprintf("%v", idx) + "]." + getFieldYamlTag(node, fieldPath) diff --git a/pkg/generate/config.go b/pkg/generate/config.go index 7710ab60..a96a333b 100644 --- a/pkg/generate/config.go +++ b/pkg/generate/config.go @@ -35,20 +35,6 @@ func GenerateConfig(c *config.TalhelperConfig, dryRun bool, outDir, secretFile, return err } - if node.InlinePatch != nil { - cfg, err = patcher.YAMLInlinePatcher(node.InlinePatch, cfg) - if err != nil { - return err - } - } - - if len(node.ConfigPatches) != 0 { - cfg, err = patcher.YAMLPatcher(node.ConfigPatches, cfg) - if err != nil { - return err - } - } - if len(node.Patches) != 0 { cfg, err = patcher.PatchesPatcher(node.Patches, cfg) if err != nil { @@ -56,34 +42,6 @@ func GenerateConfig(c *config.TalhelperConfig, dryRun bool, outDir, secretFile, } } - if node.ControlPlane { - cfg, err = patcher.YAMLInlinePatcher(c.ControlPlane.InlinePatch, cfg) - if err != nil { - return err - } - cfg, err = patcher.YAMLPatcher(c.ControlPlane.ConfigPatches, cfg) - if err != nil { - return err - } - cfg, err = patcher.PatchesPatcher(c.ControlPlane.Patches, cfg) - if err != nil { - return err - } - } else { - cfg, err = patcher.YAMLInlinePatcher(c.Worker.InlinePatch, cfg) - if err != nil { - return err - } - cfg, err = patcher.YAMLPatcher(c.Worker.ConfigPatches, cfg) - if err != nil { - return err - } - cfg, err = patcher.PatchesPatcher(c.Worker.Patches, cfg) - if err != nil { - return err - } - } - if len(c.Patches) > 0 { cfg, err = patcher.PatchesPatcher(c.Patches, cfg) if err != nil { @@ -117,38 +75,6 @@ func GenerateConfig(c *config.TalhelperConfig, dryRun bool, outDir, secretFile, cfg = append(cfg, content...) } - if node.ControlPlane { - if c.ControlPlane.IngressFirewall != nil { - nc, err := talos.GenerateNetworkConfigBytes(c.ControlPlane.IngressFirewall) - if err != nil { - return err - } - cfg = append(cfg, nc...) - } - if len(c.ControlPlane.ExtraManifests) > 0 { - content, err := combineExtraManifests(c.ControlPlane.ExtraManifests) - if err != nil { - return err - } - cfg = append(cfg, content...) - } - } else { - if c.Worker.IngressFirewall != nil { - nc, err := talos.GenerateNetworkConfigBytes(c.Worker.IngressFirewall) - if err != nil { - return err - } - cfg = append(cfg, nc...) - } - if len(c.Worker.ExtraManifests) > 0 { - content, err := combineExtraManifests(c.Worker.ExtraManifests) - if err != nil { - return err - } - cfg = append(cfg, content...) - } - } - if !dryRun { err = dumpFile(cfgFile, cfg) if err != nil { diff --git a/pkg/talos/input.go b/pkg/talos/input.go index 73a010e9..ca3e292d 100644 --- a/pkg/talos/input.go +++ b/pkg/talos/input.go @@ -67,7 +67,7 @@ func parseOptions(c *config.TalhelperConfig, versionContract *tconfig.VersionCon opts = append(opts, generate.WithVersionContract(versionContract)) opts = append(opts, generate.WithSecretsBundle(sb)) - opts = append(opts, generate.WithInstallImage(c.GetInstallerURL())) + opts = append(opts, generate.WithInstallImage("ghcr.io/siderolabs/installer:"+c.GetTalosVersion())) if c.AllowSchedulingOnMasters || c.AllowSchedulingOnControlPlanes { opts = append(opts, generate.WithAllowSchedulingOnControlPlanes(true)) diff --git a/pkg/talos/nodeconfig.go b/pkg/talos/nodeconfig.go index bd4d343d..f179b80a 100644 --- a/pkg/talos/nodeconfig.go +++ b/pkg/talos/nodeconfig.go @@ -92,10 +92,6 @@ func applyNodeOverride(node *config.Node, cfg taloscfg.Provider) taloscfg.Provid cfg.RawV1Alpha1().MachineConfig.MachineNodeTaints = node.NodeTaints } - if len(node.Extensions) > 0 { - cfg.RawV1Alpha1().MachineConfig.MachineInstall.InstallExtensions = node.Extensions - } - if len(node.MachineFiles) > 0 { cfg.RawV1Alpha1().MachineConfig.MachineFiles = node.MachineFiles } diff --git a/pkg/talos/nodeconfig_test.go b/pkg/talos/nodeconfig_test.go index 3fddd20b..189f3406 100644 --- a/pkg/talos/nodeconfig_test.go +++ b/pkg/talos/nodeconfig_test.go @@ -95,11 +95,6 @@ nodes: expectedNode1Hostname := "node1" expectedNode1InstallDisk := "/dev/sda" expectedNode1DisableSearchDomain := true - expectedNode1Extensions := []v1alpha1.InstallExtensionConfig{ - { - ExtensionImage: "ghcr.io/siderolabs/tailscale:1.44.0", - }, - } expectedNode1MachineFiles := []*v1alpha1.MachineFile{ { FileContent: "TS_AUTHKEY=123456", @@ -163,7 +158,6 @@ nodes: compare(cpCfg.MachineNetwork.Hostname(), expectedNode1Hostname, t) compare(cpCfg.MachineInstall.InstallDisk, expectedNode1InstallDisk, t) compare(cpCfg.MachineNetwork.DisableSearchDomain(), expectedNode1DisableSearchDomain, t) - compare(cpCfg.MachineInstall.InstallExtensions, expectedNode1Extensions, t) compare(cpCfg.MachineFiles, expectedNode1MachineFiles, t) compare(cpCfg.MachineNetwork.NetworkInterfaces, expectedNode1NetworkInterfaces, t) compare(cpCfg.MachineKernel, expectedNode1KernelModules, t)