Skip to content

Commit f9bb88a

Browse files
authored
expire nodes with a custom timestamp (#2828)
1 parent 456a5d5 commit f9bb88a

16 files changed

Lines changed: 191 additions & 25 deletions

File tree

.github/workflows/test-integration.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ jobs:
7070
- TestTaildrop
7171
- TestUpdateHostnameFromClient
7272
- TestExpireNode
73+
- TestSetNodeExpiryInFuture
7374
- TestNodeOnlineStatus
7475
- TestPingAllByIPManyUpDown
7576
- Test2118DeletingOnlineNodePanics

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## Next
44

5+
### Changes
6+
7+
- Expire nodes with a custom timestamp
8+
[#2828](https://github.com/juanfont/headscale/pull/2828)
9+
510
## 0.27.0 (2025-10-27)
611

712
**Minimum supported Tailscale client version: v1.64.0**

cmd/headscale/cli/nodes.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/samber/lo"
1616
"github.com/spf13/cobra"
1717
"google.golang.org/grpc/status"
18+
"google.golang.org/protobuf/types/known/timestamppb"
1819
"tailscale.com/types/key"
1920
)
2021

@@ -51,6 +52,7 @@ func init() {
5152
nodeCmd.AddCommand(registerNodeCmd)
5253

5354
expireNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
55+
expireNodeCmd.Flags().StringP("expiry", "e", "", "Set expire to (RFC3339 format, e.g. 2025-08-27T10:00:00Z), or leave empty to expire immediately.")
5456
err = expireNodeCmd.MarkFlagRequired("identifier")
5557
if err != nil {
5658
log.Fatal(err.Error())
@@ -289,12 +291,37 @@ var expireNodeCmd = &cobra.Command{
289291
)
290292
}
291293

294+
expiry, err := cmd.Flags().GetString("expiry")
295+
if err != nil {
296+
ErrorOutput(
297+
err,
298+
fmt.Sprintf("Error converting expiry to string: %s", err),
299+
output,
300+
)
301+
302+
return
303+
}
304+
expiryTime := time.Now()
305+
if expiry != "" {
306+
expiryTime, err = time.Parse(time.RFC3339, expiry)
307+
if err != nil {
308+
ErrorOutput(
309+
err,
310+
fmt.Sprintf("Error converting expiry to string: %s", err),
311+
output,
312+
)
313+
314+
return
315+
}
316+
}
317+
292318
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
293319
defer cancel()
294320
defer conn.Close()
295321

296322
request := &v1.ExpireNodeRequest{
297323
NodeId: identifier,
324+
Expiry: timestamppb.New(expiryTime),
298325
}
299326

300327
response, err := client.ExpireNode(ctx, request)

gen/go/headscale/v1/apikey.pb.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen/go/headscale/v1/device.pb.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen/go/headscale/v1/headscale.pb.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen/go/headscale/v1/headscale.pb.gw.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen/go/headscale/v1/node.pb.go

Lines changed: 23 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen/go/headscale/v1/policy.pb.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen/go/headscale/v1/preauthkey.pb.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)