Skip to content

Commit 85e97a5

Browse files
committed
fix: panic when KubeProxy is disabled without image override
We can observe a controller panic when KubeProxy is disabled in new config, and there is no image override set. Signed-off-by: Mateusz Urbanek <mateusz.urbanek@siderolabs.com>
1 parent 969098c commit 85e97a5

3 files changed

Lines changed: 77 additions & 6 deletions

File tree

internal/app/machined/pkg/controllers/k8s/control_plane.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -413,8 +413,6 @@ func NewControlPlaneBootstrapManifestsController() *ControlPlaneBootstrapManifes
413413
}
414414
}
415415

416-
images := images.List(cfgProvider)
417-
418416
var (
419417
server string
420418
flannelKubeServiceHost, flannelKubeServicePort string
@@ -457,7 +455,7 @@ func NewControlPlaneBootstrapManifestsController() *ControlPlaneBootstrapManifes
457455

458456
if k8sFlannelCNIConfig := cfgProvider.K8sFlannelCNIConfig(); k8sFlannelCNIConfig != nil {
459457
res.TypedSpec().FlannelEnabled = true
460-
res.TypedSpec().FlannelImage = images.Flannel.String()
458+
res.TypedSpec().FlannelImage = images.Flannel().String()
461459
res.TypedSpec().FlannelBackendType = k8sFlannelCNIConfig.BackendType()
462460
res.TypedSpec().FlannelBackendPort = k8sFlannelCNIConfig.BackendPort().ValueOrZero()
463461

@@ -477,7 +475,7 @@ func NewControlPlaneBootstrapManifestsController() *ControlPlaneBootstrapManifes
477475
res.TypedSpec().FlannelKubeServiceHost = flannelKubeServiceHost
478476
res.TypedSpec().FlannelKubeServicePort = flannelKubeServicePort
479477
res.TypedSpec().FlannelKubeNetworkPoliciesEnabled = k8sFlannelCNIConfig.KubeNetworkPoliciesEnabled()
480-
res.TypedSpec().FlannelKubeNetworkPoliciesImage = images.KubeNetworkPolicies.String()
478+
res.TypedSpec().FlannelKubeNetworkPoliciesImage = images.KubeNetworkPolicies().String()
481479
res.TypedSpec().CNIName = constants.FlannelCNI
482480
} else {
483481
res.TypedSpec().FlannelEnabled = false

internal/app/machined/pkg/controllers/k8s/control_plane_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,62 @@ func (suite *K8sControlPlaneSuite) TestReconcileKubeProxyMode() {
896896
)
897897
}
898898

899+
// TestReconcileKubeProxyDisabled verifies that a KubeProxyConfig document with kube-proxy disabled
900+
// (and, as validation allows in that case, no image set) is handled without a panic.
901+
func (suite *K8sControlPlaneSuite) TestReconcileKubeProxyDisabled() {
902+
u, err := url.Parse("https://foo:6443")
903+
suite.Require().NoError(err)
904+
905+
clusterCfg := k8scfg.NewKubeClusterConfigV1Alpha1()
906+
clusterCfg.ClusterNameConfig = "test"
907+
clusterCfg.ClusterEndpointConfig = meta.URL{URL: u}
908+
909+
networkCfg := k8scfg.NewKubeNetworkConfigV1Alpha1()
910+
networkCfg.NetworkPodSubnets = []meta.Prefix{
911+
{Prefix: netip.MustParsePrefix(constants.DefaultIPv4PodCIDR)},
912+
}
913+
networkCfg.NetworkServiceSubnets = []meta.Prefix{
914+
{Prefix: netip.MustParsePrefix(constants.DefaultIPv4ServiceCIDR)},
915+
}
916+
917+
// kube-proxy is disabled, so no image is set: this is a valid config,
918+
// see TestKubeProxyConfigValidate/disabled
919+
proxyCfg := k8scfg.NewKubeProxyConfigV1Alpha1()
920+
proxyCfg.ProxyEnabled = new(false)
921+
922+
ctr, err := container.New(
923+
&v1alpha1.Config{
924+
ConfigVersion: "v1alpha1",
925+
MachineConfig: &v1alpha1.MachineConfig{
926+
MachineType: "controlplane",
927+
},
928+
ClusterConfig: &v1alpha1.ClusterConfig{},
929+
},
930+
clusterCfg,
931+
networkCfg,
932+
proxyCfg,
933+
k8scfg.NewKubeFlannelCNIConfigV1Alpha1(),
934+
)
935+
suite.Require().NoError(err)
936+
937+
suite.setupMachine(config.NewMachineConfig(ctr))
938+
939+
rtestutils.AssertResources(
940+
suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.BootstrapManifestsConfigID},
941+
func(cfg *k8s.BootstrapManifestsConfig, assert *assert.Assertions) {
942+
assert.False(cfg.TypedSpec().ProxyEnabled)
943+
assert.Empty(cfg.TypedSpec().ProxyImage)
944+
945+
// non-configurable images are always rendered
946+
assert.Equal("ghcr.io/siderolabs/flannel:"+constants.FlannelVersion, cfg.TypedSpec().FlannelImage)
947+
assert.Equal(
948+
"registry.k8s.io/networking/kube-network-policies:"+constants.KubeNetworkPoliciesVersion,
949+
cfg.TypedSpec().FlannelKubeNetworkPoliciesImage,
950+
)
951+
},
952+
)
953+
}
954+
899955
func TestK8sControlPlaneSuite(t *testing.T) {
900956
t.Parallel()
901957

pkg/images/list.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,34 @@ type Versions struct {
3535
// The integration test verifies that our constant is accurate.
3636
const DefaultSandboxImage = "registry.k8s.io/pause:3.10.2"
3737

38+
// Flannel returns the Flannel image built into Talos, mirrored from docker.io/flannel/flannel.
39+
//
40+
// It is not configurable, so it never depends on the machine configuration.
41+
func Flannel() name.Tag {
42+
return mustParseTag(fmt.Sprintf("ghcr.io/siderolabs/flannel:%s", constants.FlannelVersion))
43+
}
44+
45+
// KubeNetworkPolicies returns the kube-network-policies image built into Talos.
46+
//
47+
// It is not configurable, so it never depends on the machine configuration.
48+
func KubeNetworkPolicies() name.Tag {
49+
return mustParseTag(fmt.Sprintf("registry.k8s.io/networking/kube-network-policies:%s", constants.KubeNetworkPoliciesVersion))
50+
}
51+
3852
// List returns default image versions.
53+
//
54+
// It panics on any image which is empty in the config, so it should only be used with
55+
// a config which is known to have all images set (e.g. a synthetic one built in talosctl).
3956
func List(config config.Config) Versions {
4057
var images Versions
4158

4259
images.Etcd = mustParseTag(config.Cluster().Etcd().Image())
4360
images.CoreDNS = mustParseTag(config.K8sCoreDNSConfig().Image())
44-
images.Flannel = mustParseTag(fmt.Sprintf("ghcr.io/siderolabs/flannel:%s", constants.FlannelVersion)) // mirrored from docker.io/flannel/flannel
61+
images.Flannel = Flannel()
4562
images.Kubelet = mustParseTag(config.K8sKubeletConfig().Image())
4663
images.KubeAPIServer = mustParseTag(config.K8sAPIServerConfig().Image())
4764
images.KubeControllerManager = mustParseTag(config.K8sControllerManagerConfig().Image())
48-
images.KubeNetworkPolicies = mustParseTag(fmt.Sprintf("registry.k8s.io/networking/kube-network-policies:%s", constants.KubeNetworkPoliciesVersion))
65+
images.KubeNetworkPolicies = KubeNetworkPolicies()
4966
images.KubeProxy = mustParseTag(config.K8sProxyConfig().Image())
5067
images.KubeScheduler = mustParseTag(config.K8sSchedulerConfig().Image())
5168

0 commit comments

Comments
 (0)