Skip to content

Commit ebde543

Browse files
committed
feat: introduce BootID resource
This resource simply states the Linux value of boot ID. It might be used to detect reboots easily. Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
1 parent cd178b9 commit ebde543

15 files changed

Lines changed: 562 additions & 172 deletions

File tree

api/resource/definitions/runtime/runtime.proto

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ message APIServiceConfigSpec {
1717
bool skip_verifying_client_cert = 4;
1818
}
1919

20+
// BootIDSpec presents the kernel boot ID (contents of /proc/sys/kernel/random/boot_id).
21+
message BootIDSpec {
22+
string boot_id = 1;
23+
}
24+
2025
// BootedEntrySpec describes the booted entry resource properties.
2126
message BootedEntrySpec {
2227
string booted_entry = 1;
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
5+
//nolint:dupl
6+
package runtime
7+
8+
import (
9+
"context"
10+
"fmt"
11+
"os"
12+
"strings"
13+
14+
"github.com/cosi-project/runtime/pkg/controller"
15+
"github.com/cosi-project/runtime/pkg/safe"
16+
"go.uber.org/zap"
17+
18+
machineruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
19+
"github.com/siderolabs/talos/pkg/machinery/resources/runtime"
20+
)
21+
22+
// BootIDController presents /proc/sys/kernel/random/boot_id as a resource.
23+
type BootIDController struct {
24+
V1Alpha1Mode machineruntime.Mode
25+
}
26+
27+
// Name implements controller.Controller interface.
28+
func (ctrl *BootIDController) Name() string {
29+
return "runtime.BootIDController"
30+
}
31+
32+
// Inputs implements controller.Controller interface.
33+
func (ctrl *BootIDController) Inputs() []controller.Input {
34+
return nil
35+
}
36+
37+
// Outputs implements controller.Controller interface.
38+
func (ctrl *BootIDController) Outputs() []controller.Output {
39+
return []controller.Output{
40+
{
41+
Type: runtime.BootIDType,
42+
Kind: controller.OutputExclusive,
43+
},
44+
}
45+
}
46+
47+
// Run implements controller.Controller interface.
48+
func (ctrl *BootIDController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error {
49+
if ctrl.V1Alpha1Mode.InContainer() {
50+
// no boot_id in containers
51+
return nil
52+
}
53+
54+
select {
55+
case <-ctx.Done():
56+
return nil
57+
case <-r.EventCh():
58+
}
59+
60+
contents, err := os.ReadFile("/proc/sys/kernel/random/boot_id")
61+
if err != nil {
62+
return fmt.Errorf("error reading boot_id: %w", err)
63+
}
64+
65+
if err := safe.WriterModify(
66+
ctx, r,
67+
runtime.NewBootID(),
68+
func(res *runtime.BootID) error {
69+
res.TypedSpec().BootID = strings.TrimSpace(string(contents))
70+
71+
return nil
72+
},
73+
); err != nil {
74+
return fmt.Errorf("error updating BootID resource: %w", err)
75+
}
76+
77+
return nil
78+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
5+
//nolint:dupl
6+
package runtime_test
7+
8+
import (
9+
"testing"
10+
"time"
11+
12+
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/suite"
14+
15+
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest"
16+
runtimectrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime"
17+
"github.com/siderolabs/talos/pkg/machinery/resources/runtime"
18+
)
19+
20+
func TestBootIDSuite(t *testing.T) {
21+
t.Parallel()
22+
23+
suite.Run(t, &BootIDSuite{
24+
DefaultSuite: ctest.DefaultSuite{
25+
Timeout: 5 * time.Second,
26+
AfterSetup: func(suite *ctest.DefaultSuite) {
27+
suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrl.BootIDController{}))
28+
},
29+
},
30+
})
31+
}
32+
33+
type BootIDSuite struct {
34+
ctest.DefaultSuite
35+
}
36+
37+
func (suite *BootIDSuite) TestBootID() {
38+
ctest.AssertResource(suite, runtime.BootIDID, func(res *runtime.BootID, asrt *assert.Assertions) {
39+
asrt.NotEmpty(res.TypedSpec().BootID)
40+
})
41+
}

internal/app/machined/pkg/controllers/runtime/kernel_cmdline.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
44

5+
//nolint:dupl
56
package runtime
67

78
import (

internal/app/machined/pkg/controllers/runtime/kernel_cmdline_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
44

5+
//nolint:dupl
56
package runtime_test
67

78
import (

internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,9 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error
450450
},
451451
&runtimecontrollers.ExtensionStatusController{},
452452
&runtimecontrollers.ImageFactorySchematicController{},
453+
&runtimecontrollers.BootIDController{
454+
V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(),
455+
},
453456
&runtimecontrollers.KernelCmdlineController{
454457
V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(),
455458
},

internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_state.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ func NewState() (*State, error) {
216216
&cri.RegistriesConfig{},
217217
&runtime.APIServiceConfig{},
218218
&runtime.BootedEntry{},
219+
&runtime.BootID{},
219220
&runtime.DevicesStatus{},
220221
&runtime.Diagnostic{},
221222
&runtime.Environment{},

internal/integration/base/api.go

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -230,26 +230,12 @@ func (apiSuite *APISuite) ReadBootID(ctx context.Context) (string, error) {
230230
reqCtx, reqCtxCancel := context.WithTimeout(ctx, 10*time.Second)
231231
defer reqCtxCancel()
232232

233-
reader, err := apiSuite.Client.Read(reqCtx, "/proc/sys/kernel/random/boot_id")
233+
bootID, err := safe.StateGetByID[*runtimeres.BootID](reqCtx, apiSuite.Client.COSI, runtimeres.BootIDID)
234234
if err != nil {
235235
return "", err
236236
}
237237

238-
defer reader.Close() //nolint:errcheck
239-
240-
body, err := io.ReadAll(reader)
241-
if err != nil {
242-
return "", err
243-
}
244-
245-
bootID := strings.TrimSpace(string(body))
246-
247-
_, err = io.Copy(io.Discard, reader)
248-
if err != nil {
249-
return "", err
250-
}
251-
252-
return bootID, reader.Close()
238+
return bootID.TypedSpec().BootID, nil
253239
}
254240

255241
// ReadBootIDWithRetry reads node boot_id.

0 commit comments

Comments
 (0)