From 14a9f807de6b0759d2c4c282beddacf09f93f66b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 18 Jul 2024 14:13:29 -0400 Subject: [PATCH] incusd: Extend heartbeat data for minimum API extension count MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This exposes not only the maximum API extension count as done today to detect needed upgrades, but also the minimum value so newer servers can restrict what they expose to clients while waiting for the cluster to be consistent. Signed-off-by: Stéphane Graber --- cmd/incusd/api_1.0.go | 2 +- cmd/incusd/daemon.go | 8 ++++++++ internal/server/cluster/heartbeat.go | 16 +++++++++++----- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/cmd/incusd/api_1.0.go b/cmd/incusd/api_1.0.go index 160e55ae2a0..99c4a0c0e4a 100644 --- a/cmd/incusd/api_1.0.go +++ b/cmd/incusd/api_1.0.go @@ -223,7 +223,7 @@ func api10Get(d *Daemon, r *http.Request) response.Response { } srv := api.ServerUntrusted{ - APIExtensions: version.APIExtensions, + APIExtensions: version.APIExtensions[:d.apiExtensions], APIStatus: "stable", APIVersion: version.APIVersion, Public: false, diff --git a/cmd/incusd/daemon.go b/cmd/incusd/daemon.go index d7575b22ce1..8302c2c817f 100644 --- a/cmd/incusd/daemon.go +++ b/cmd/incusd/daemon.go @@ -168,6 +168,9 @@ type Daemon struct { // OVN clients. ovnnb *ovn.NB ovnsb *ovn.SB + + // API info. + apiExtensions int } // DaemonConfig holds configuration values for Daemon. @@ -197,6 +200,7 @@ func newDaemon(config *DaemonConfig, os *sys.OS) *Daemon { shutdownCtx: shutdownCtx, shutdownCancel: shutdownCancel, shutdownDoneCh: make(chan error), + apiExtensions: len(version.APIExtensions), } d.serverCert = func() *localtls.CertInfo { return d.serverCertInt } @@ -2384,6 +2388,10 @@ func (d *Daemon) nodeRefreshTask(heartbeatData *cluster.APIHeartbeat, isLeader b return } + if heartbeatData.Version.MinAPIExtensions > 0 && heartbeatData.Version.MinAPIExtensions != d.apiExtensions { + d.apiExtensions = heartbeatData.Version.MinAPIExtensions + } + // If the max version of the cluster has changed, check whether we need to upgrade. if d.lastNodeList == nil || d.lastNodeList.Version.APIExtensions != heartbeatData.Version.APIExtensions || d.lastNodeList.Version.Schema != heartbeatData.Version.Schema { err := cluster.MaybeUpdate(s) diff --git a/internal/server/cluster/heartbeat.go b/internal/server/cluster/heartbeat.go index 5b1e2a2cd8c..31439dd7663 100644 --- a/internal/server/cluster/heartbeat.go +++ b/internal/server/cluster/heartbeat.go @@ -46,8 +46,9 @@ type APIHeartbeatMember struct { // APIHeartbeatVersion contains max versions for all nodes in cluster. type APIHeartbeatVersion struct { - Schema int - APIExtensions int + Schema int + APIExtensions int + MinAPIExtensions int } // NewAPIHearbeat returns initialized APIHeartbeat. @@ -74,7 +75,7 @@ type APIHeartbeat struct { // Update updates an existing APIHeartbeat struct with the raft and all node states supplied. // If allNodes provided is an empty set then this is considered a non-full state list. func (hbState *APIHeartbeat) Update(fullStateList bool, raftNodes []db.RaftNode, allNodes []db.NodeInfo, offlineThreshold time.Duration) { - var maxSchemaVersion, maxAPIExtensionsVersion int + var maxSchemaVersion, maxAPIExtensionsVersion, minAPIExtensionsVersion int if hbState.Members == nil { hbState.Members = make(map[int64]APIHeartbeatMember) @@ -115,14 +116,19 @@ func (hbState *APIHeartbeat) Update(fullStateList bool, raftNodes []db.RaftNode, maxAPIExtensionsVersion = node.APIExtensions } + if minAPIExtensionsVersion == 0 || node.APIExtensions < minAPIExtensionsVersion { + minAPIExtensionsVersion = node.APIExtensions + } + if node.Schema > maxSchemaVersion { maxSchemaVersion = node.Schema } } hbState.Version = APIHeartbeatVersion{ - Schema: maxSchemaVersion, - APIExtensions: maxAPIExtensionsVersion, + Schema: maxSchemaVersion, + APIExtensions: maxAPIExtensionsVersion, + MinAPIExtensions: minAPIExtensionsVersion, } if len(raftNodeMap) > 0 && hbState.cluster != nil {