Skip to content

Commit 3ea0888

Browse files
committed
fix: allow static hosts in /etc/hosts without hostname
Allow static hosts to be populated even if the hostname is not populated yet. This allows to use `ExtraHostConfig` before the hostname is established. Fixes #12792 While I'm at it, refactor the unit-tests to use modern ctest.DefaultSuite. Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com> (cherry picked from commit dab0d47)
1 parent 5ebb00f commit 3ea0888

File tree

6 files changed

+87
-197
lines changed

6 files changed

+87
-197
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ require (
143143
github.com/siderolabs/go-circular v0.2.3
144144
github.com/siderolabs/go-cmd v0.1.3
145145
github.com/siderolabs/go-copy v0.1.0
146-
github.com/siderolabs/go-debug v0.6.1
146+
github.com/siderolabs/go-debug v0.6.2
147147
github.com/siderolabs/go-kmsg v0.1.4
148148
github.com/siderolabs/go-kubeconfig v0.1.1
149149
github.com/siderolabs/go-kubernetes v0.2.28

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -631,8 +631,8 @@ github.com/siderolabs/go-cmd v0.1.3 h1:JrgZwqhJQeoec3QRON0LK+fv+0y7d0DyY7zsfkO6c
631631
github.com/siderolabs/go-cmd v0.1.3/go.mod h1:bg7HY4mRNu4zKebAgUevSwuYNtcvPMJfuhLRkVKHZ0k=
632632
github.com/siderolabs/go-copy v0.1.0 h1:OIWCtSg+rhOtnIZTpT31Gfpn17rv5kwJqQHG+QUEgC8=
633633
github.com/siderolabs/go-copy v0.1.0/go.mod h1:4bF2rZOZAR/ags/U4AVSpjFE5RPGdEeSkOq6yR9YOkU=
634-
github.com/siderolabs/go-debug v0.6.1 h1:LAkM2ADz+naL3PA6Mv0SzyXYx7aCxYlKdO2dSiZmhRc=
635-
github.com/siderolabs/go-debug v0.6.1/go.mod h1:qKGKnrkHXdgj+gPaGujFs3L/hc87FXYTp1/l6kN/1s0=
634+
github.com/siderolabs/go-debug v0.6.2 h1:zWWMTcrYDVyiNTotSxEVg++hj9mb2ctuTNVnOeCWtO8=
635+
github.com/siderolabs/go-debug v0.6.2/go.mod h1:tcHnBjzOfEC/Stfc+cpP8J9Y6y5Pp89XNBN0n3dsWD4=
636636
github.com/siderolabs/go-kmsg v0.1.4 h1:RLAa90O9bWuhA3pXPAYAdrI+kzcqTshZASRA5yso/mo=
637637
github.com/siderolabs/go-kmsg v0.1.4/go.mod h1:BLkt2N2DHT0wsFMz32lMw6vNEZL90c8ZnBjpIUoBb/M=
638638
github.com/siderolabs/go-kubeconfig v0.1.1 h1:tZlgpelj/OqrcHVUwISPN0NRgObcflpH9WtE41mtQZ0=

internal/app/machined/pkg/controllers/network/address_merge_test.go

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,11 @@
55
package network_test
66

77
import (
8-
"context"
98
"net/netip"
109
"testing"
1110
"time"
1211

1312
"github.com/cosi-project/runtime/pkg/resource"
14-
"github.com/cosi-project/runtime/pkg/resource/rtestutils"
15-
"github.com/cosi-project/runtime/pkg/state"
16-
"github.com/siderolabs/gen/xslices"
1713
"github.com/stretchr/testify/assert"
1814
"github.com/stretchr/testify/suite"
1915

@@ -140,41 +136,3 @@ func TestAddressMergeSuite(t *testing.T) {
140136
},
141137
})
142138
}
143-
144-
func assertResources[R rtestutils.ResourceWithRD](
145-
ctx context.Context,
146-
t *testing.T,
147-
state state.State,
148-
requiredIDs []string,
149-
check func(R, *assert.Assertions),
150-
opts ...rtestutils.Option,
151-
) {
152-
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
153-
defer cancel()
154-
155-
rtestutils.AssertResources(
156-
ctx,
157-
t,
158-
state,
159-
xslices.Map(requiredIDs, func(id string) resource.ID { return id }),
160-
check,
161-
opts...,
162-
)
163-
}
164-
165-
func assertNoResource[R rtestutils.ResourceWithRD](
166-
ctx context.Context,
167-
t *testing.T,
168-
state state.State,
169-
id string,
170-
) {
171-
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
172-
defer cancel()
173-
174-
rtestutils.AssertNoResource[R](
175-
ctx,
176-
t,
177-
state,
178-
id,
179-
)
180-
}

internal/app/machined/pkg/controllers/network/etcfile.go

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -193,17 +193,15 @@ func (ctrl *EtcFileController) Run(ctx context.Context, r controller.Runtime, lo
193193
}
194194
}
195195

196-
if hostnameStatus != nil && nodeAddressStatus != nil {
197-
if err = safe.WriterModify(ctx, r, files.NewEtcFileSpec(files.NamespaceName, "hosts"),
198-
func(r *files.EtcFileSpec) error {
199-
r.TypedSpec().Contents, err = ctrl.renderHosts(hostnameStatus.TypedSpec(), nodeAddressStatus.TypedSpec(), cfgProvider)
200-
r.TypedSpec().Mode = 0o644
201-
r.TypedSpec().SelinuxLabel = constants.EtcSelinuxLabel
202-
203-
return err
204-
}); err != nil {
205-
return fmt.Errorf("error modifying hosts: %w", err)
206-
}
196+
if err = safe.WriterModify(ctx, r, files.NewEtcFileSpec(files.NamespaceName, "hosts"),
197+
func(r *files.EtcFileSpec) error {
198+
r.TypedSpec().Contents, err = ctrl.renderHosts(hostnameStatus, nodeAddressStatus, cfgProvider)
199+
r.TypedSpec().Mode = 0o644
200+
r.TypedSpec().SelinuxLabel = constants.EtcSelinuxLabel
201+
202+
return err
203+
}); err != nil {
204+
return fmt.Errorf("error modifying hosts: %w", err)
207205
}
208206

209207
r.ResetRestartBackoff()
@@ -240,7 +238,7 @@ func renderResolvConf(nameservers iter.Seq2[int, netip.Addr], searchDomains []st
240238
return buf.Bytes()
241239
}
242240

243-
func (ctrl *EtcFileController) renderHosts(hostnameStatus *network.HostnameStatusSpec, nodeAddressStatus *network.NodeAddressSpec, cfgProvider talosconfig.Config) ([]byte, error) {
241+
func (ctrl *EtcFileController) renderHosts(hostnameStatus *network.HostnameStatus, nodeAddressStatus *network.NodeAddress, cfgProvider talosconfig.Config) ([]byte, error) {
244242
var buf bytes.Buffer
245243

246244
tabW := tabwriter.NewWriter(&buf, 0, 0, 1, ' ', 0)
@@ -249,13 +247,15 @@ func (ctrl *EtcFileController) renderHosts(hostnameStatus *network.HostnameStatu
249247

250248
write("127.0.0.1\tlocalhost\n")
251249

252-
write(fmt.Sprintf("%s\t%s", nodeAddressStatus.Addresses[0].Addr(), hostnameStatus.FQDN()))
250+
if nodeAddressStatus != nil && hostnameStatus != nil {
251+
write(fmt.Sprintf("%s\t%s", nodeAddressStatus.TypedSpec().Addresses[0].Addr(), hostnameStatus.TypedSpec().FQDN()))
253252

254-
if hostnameStatus.Hostname != hostnameStatus.FQDN() {
255-
write(" " + hostnameStatus.Hostname)
256-
}
253+
if hostnameStatus.TypedSpec().Hostname != hostnameStatus.TypedSpec().FQDN() {
254+
write(" " + hostnameStatus.TypedSpec().Hostname)
255+
}
257256

258-
write("\n")
257+
write("\n")
258+
}
259259

260260
write("::1\tlocalhost ip6-localhost ip6-loopback\n")
261261
write("ff02::1\tip6-allnodes\n")

internal/app/machined/pkg/controllers/network/etcfile_test.go

Lines changed: 41 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,16 @@ import (
1111
"net/url"
1212
"os"
1313
"path/filepath"
14-
"sync"
1514
"testing"
1615
"time"
1716

18-
"github.com/cosi-project/runtime/pkg/controller/runtime"
1917
"github.com/cosi-project/runtime/pkg/resource"
20-
"github.com/cosi-project/runtime/pkg/state"
21-
"github.com/cosi-project/runtime/pkg/state/impl/inmem"
22-
"github.com/cosi-project/runtime/pkg/state/impl/namespaced"
2318
"github.com/siderolabs/go-pointer"
2419
"github.com/siderolabs/go-retry/retry"
2520
"github.com/stretchr/testify/assert"
2621
"github.com/stretchr/testify/suite"
27-
"go.uber.org/zap/zaptest"
2822

23+
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest"
2924
netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network"
3025
v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
3126
"github.com/siderolabs/talos/internal/pkg/mount/v3"
@@ -39,15 +34,7 @@ import (
3934
)
4035

4136
type EtcFileConfigSuite struct {
42-
suite.Suite
43-
44-
state state.State
45-
46-
runtime *runtime.Runtime
47-
wg sync.WaitGroup
48-
49-
ctx context.Context //nolint:containedctx
50-
ctxCancel context.CancelFunc
37+
ctest.DefaultSuite
5138

5239
cfg *config.MachineConfig
5340
defaultAddress *network.NodeAddress
@@ -60,18 +47,7 @@ type EtcFileConfigSuite struct {
6047
etcRoot xfs.Root
6148
}
6249

63-
func (suite *EtcFileConfigSuite) SetupTest() {
64-
suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute)
65-
66-
suite.state = state.WrapCore(namespaced.NewState(inmem.Build))
67-
68-
var err error
69-
70-
suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T()))
71-
suite.Require().NoError(err)
72-
73-
suite.startRuntime()
74-
50+
func (suite *EtcFileConfigSuite) ExtraSetup() {
7551
ok, err := v1alpha1runtime.KernelCapabilities().OpentreeOnAnonymousFS()
7652
suite.Require().NoError(err)
7753

@@ -87,7 +63,7 @@ func (suite *EtcFileConfigSuite) SetupTest() {
8763
suite.Require().NoError(suite.etcRoot.OpenFS())
8864
suite.Assert().NoFileExists(suite.podResolvConfPath)
8965

90-
suite.Require().NoError(suite.runtime.RegisterController(&netctrl.EtcFileController{
66+
suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.EtcFileController{
9167
V1Alpha1Mode: v1alpha1runtime.ModeMetal,
9268
EtcRoot: suite.etcRoot,
9369
BindMountTarget: suite.bindMountTarget,
@@ -149,12 +125,6 @@ func (suite *EtcFileConfigSuite) SetupTest() {
149125
suite.hostDNSConfig.TypedSpec().ServiceHostDNSAddress = netip.MustParseAddr("169.254.116.108")
150126
}
151127

152-
func (suite *EtcFileConfigSuite) startRuntime() {
153-
suite.wg.Go(func() {
154-
suite.Assert().NoError(suite.runtime.Run(suite.ctx))
155-
})
156-
}
157-
158128
type etcFileContents struct {
159129
hosts string
160130
resolvConf string
@@ -164,7 +134,7 @@ type etcFileContents struct {
164134
//nolint:gocyclo
165135
func (suite *EtcFileConfigSuite) testFiles(resources []resource.Resource, contents etcFileContents) {
166136
for _, r := range resources {
167-
suite.Require().NoError(suite.state.Create(suite.ctx, r))
137+
suite.Create(r)
168138
}
169139

170140
var (
@@ -184,10 +154,8 @@ func (suite *EtcFileConfigSuite) testFiles(resources []resource.Resource, conten
184154
unexpectedIDs = append(unexpectedIDs, "hosts")
185155
}
186156

187-
assertResources(
188-
suite.ctx,
189-
suite.T(),
190-
suite.state,
157+
ctest.AssertResources(
158+
suite,
191159
expectedIDs,
192160
func(r *files.EtcFileSpec, asrt *assert.Assertions) {
193161
switch r.Metadata().ID() {
@@ -198,6 +166,7 @@ func (suite *EtcFileConfigSuite) testFiles(resources []resource.Resource, conten
198166
}
199167
},
200168
)
169+
201170
suite.Assert().NoError(
202171
retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(func() error {
203172
if contents.resolvGlobalConf == "" {
@@ -231,7 +200,7 @@ func (suite *EtcFileConfigSuite) testFiles(resources []resource.Resource, conten
231200
)
232201

233202
for _, id := range unexpectedIDs {
234-
assertNoResource[*files.EtcFileSpec](suite.ctx, suite.T(), suite.state, id)
203+
ctest.AssertNoResource[*files.EtcFileSpec](suite, id)
235204
}
236205
}
237206

@@ -248,6 +217,19 @@ func (suite *EtcFileConfigSuite) TestComplete() {
248217
)
249218
}
250219

220+
func (suite *EtcFileConfigSuite) TestExtraHostsNoHostname() {
221+
suite.resolverStatus.TypedSpec().SearchDomains = []string{"foo.example.com"}
222+
223+
suite.testFiles(
224+
[]resource.Resource{suite.cfg, suite.resolverStatus, suite.hostDNSConfig},
225+
etcFileContents{
226+
hosts: "127.0.0.1 localhost\n::1 localhost ip6-localhost ip6-loopback\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n10.0.0.1 a b\n10.0.0.2 c d\n",
227+
resolvConf: "nameserver 127.0.0.53\n\nsearch foo.example.com\n",
228+
resolvGlobalConf: "nameserver 169.254.116.108\n\nsearch foo.example.com\n",
229+
},
230+
)
231+
}
232+
251233
func (suite *EtcFileConfigSuite) TestNoExtraHosts() {
252234
suite.resolverStatus.TypedSpec().SearchDomains = []string{"foo.example.com"}
253235

@@ -301,7 +283,7 @@ func (suite *EtcFileConfigSuite) TestOnlyResolvers() {
301283
suite.testFiles(
302284
[]resource.Resource{suite.resolverStatus, suite.hostDNSConfig},
303285
etcFileContents{
304-
hosts: "",
286+
hosts: "127.0.0.1 localhost\n::1 localhost ip6-localhost ip6-loopback\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n",
305287
resolvConf: "nameserver 127.0.0.53\n",
306288
resolvGlobalConf: "nameserver 169.254.116.108\n",
307289
},
@@ -319,11 +301,7 @@ func (suite *EtcFileConfigSuite) TestOnlyHostname() {
319301
)
320302
}
321303

322-
func (suite *EtcFileConfigSuite) TearDownTest() {
323-
suite.T().Log("tear down")
324-
325-
suite.ctxCancel()
326-
304+
func (suite *EtcFileConfigSuite) ExtraTearDown() {
327305
if _, err := os.Lstat(suite.podResolvConfPath); err == nil {
328306
if suite.etcRoot.FSType() == "os" {
329307
suite.Require().NoError(os.Remove(suite.podResolvConfPath))
@@ -332,8 +310,6 @@ func (suite *EtcFileConfigSuite) TearDownTest() {
332310
}
333311
}
334312

335-
suite.wg.Wait()
336-
337313
if suite.etcRoot != nil {
338314
suite.Require().NoError(os.RemoveAll(suite.bindMountTarget))
339315

@@ -342,9 +318,25 @@ func (suite *EtcFileConfigSuite) TearDownTest() {
342318
}
343319

344320
func TestEtcFileConfigSuite(t *testing.T) {
321+
t.Parallel()
322+
345323
if os.Geteuid() != 0 {
346324
t.Skip("skipping test that requires root privileges")
347325
}
348326

349-
suite.Run(t, new(EtcFileConfigSuite))
327+
s := &EtcFileConfigSuite{
328+
DefaultSuite: ctest.DefaultSuite{
329+
Timeout: 10 * time.Second,
330+
},
331+
}
332+
333+
s.AfterSetup = func(*ctest.DefaultSuite) {
334+
s.ExtraSetup()
335+
}
336+
337+
s.AfterTearDown = func(*ctest.DefaultSuite) {
338+
s.ExtraTearDown()
339+
}
340+
341+
suite.Run(t, s)
350342
}

0 commit comments

Comments
 (0)