Skip to content

Commit e652a16

Browse files
kvapsclaude
andcommitted
fix(controller): persist satellite ConnectionStatus via the Status subresource
Previous attempt set ConnectionStatus on the apiv1.Node value, but nodes.Update only writes Spec — Status was untouched. Adds a dedicated NodeStore.SetConnectionStatus method with both InMemory and CRD-backed implementations; the CRD path goes through client.Status().Update so the field actually lands. Server.Hello calls SetConnectionStatus("ONLINE") after Create/Update; linstor-csi-node's `linstor-wait-node-online` initContainer now sees connection_status: "ONLINE" and the DaemonSet rolls past Init. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
1 parent 41b5c1f commit e652a16

4 files changed

Lines changed: 65 additions & 9 deletions

File tree

pkg/satellitecontroller/server.go

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,16 +76,10 @@ func (s *Server) Hello(ctx context.Context, req *satellitepb.HelloRequest) (*sat
7676
"SatelliteEndpoint": req.GetSatelliteEndpoint(),
7777
}
7878

79-
// Hello completes only after the satellite has dialled in, so by
80-
// the time we're here the node is ONLINE in LINSTOR-speak. The
81-
// piraeus init-container `linstor-wait-node-online` checks this
82-
// exact field; without it linstor-csi-node DaemonSets stall on
83-
// satellites we know perfectly well are alive.
8479
node := apiv1.Node{
85-
Name: req.GetNodeName(),
86-
Type: apiv1.NodeTypeSatellite,
87-
Props: props,
88-
ConnectionStatus: "ONLINE",
80+
Name: req.GetNodeName(),
81+
Type: apiv1.NodeTypeSatellite,
82+
Props: props,
8983
}
9084

9185
err := s.st.Nodes().Create(ctx, &node)
@@ -101,6 +95,16 @@ func (s *Server) Hello(ctx context.Context, req *satellitepb.HelloRequest) (*sat
10195
return nil, status.Errorf(codes.Internal, "register Node %q: %v", req.GetNodeName(), err)
10296
}
10397

98+
// Hello round-trips only after the satellite has dialled in;
99+
// by the time we're here the node is ONLINE in LINSTOR-speak.
100+
// linstor-csi-node's `linstor-wait-node-online` initContainer
101+
// polls /v1/nodes/<name> for connection_status:"ONLINE" and
102+
// stalls the DaemonSet otherwise.
103+
err = s.st.Nodes().SetConnectionStatus(ctx, req.GetNodeName(), "ONLINE")
104+
if err != nil {
105+
return nil, status.Errorf(codes.Internal, "set Node %q ONLINE: %v", req.GetNodeName(), err)
106+
}
107+
104108
return &satellitepb.HelloResponse{
105109
ClusterId: s.cfg.ClusterID,
106110
ControllerEndpoint: s.cfg.ControllerEndpoint,

pkg/store/inmemory.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,21 @@ func (s *inMemoryNodes) Delete(_ context.Context, name string) error {
161161

162162
return nil
163163
}
164+
165+
// SetConnectionStatus mutates only the ConnectionStatus field on the
166+
// in-memory copy. Returns ErrNotFound if the node hasn't been
167+
// Create'd yet.
168+
func (s *inMemoryNodes) SetConnectionStatus(_ context.Context, name, status string) error {
169+
s.mu.Lock()
170+
defer s.mu.Unlock()
171+
172+
node, ok := s.m[name]
173+
if !ok {
174+
return errors.Wrapf(ErrNotFound, "node %q", name)
175+
}
176+
177+
node.ConnectionStatus = status
178+
s.m[name] = node
179+
180+
return nil
181+
}

pkg/store/k8s/nodes.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,33 @@ func (n *nodes) Update(ctx context.Context, in *apiv1.Node) error {
119119
return nil
120120
}
121121

122+
// SetConnectionStatus updates the Node CRD's
123+
// `.status.connectionStatus` via the Status subresource. Survives
124+
// subsequent Spec Update calls (which would otherwise overwrite
125+
// nothing here, but the dedicated subresource is the kubebuilder-
126+
// idiomatic place to land observed state).
127+
func (n *nodes) SetConnectionStatus(ctx context.Context, name, status string) error {
128+
var existing crdv1alpha1.Node
129+
130+
err := n.c.Get(ctx, types.NamespacedName{Name: name}, &existing)
131+
if err != nil {
132+
if apierrors.IsNotFound(err) {
133+
return errors.Wrapf(store.ErrNotFound, "node %q", name)
134+
}
135+
136+
return errors.Wrapf(err, "get Node %q", name)
137+
}
138+
139+
existing.Status.ConnectionStatus = status
140+
141+
err = n.c.Status().Update(ctx, &existing)
142+
if err != nil {
143+
return errors.Wrapf(err, "status update Node %q", name)
144+
}
145+
146+
return nil
147+
}
148+
122149
// Delete removes the named Node CRD.
123150
func (n *nodes) Delete(ctx context.Context, name string) error {
124151
crd := &crdv1alpha1.Node{ObjectMeta: metav1.ObjectMeta{Name: name}}

pkg/store/store.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ type NodeStore interface {
4848
Create(ctx context.Context, n *apiv1.Node) error
4949
Update(ctx context.Context, n *apiv1.Node) error
5050
Delete(ctx context.Context, name string) error
51+
52+
// SetConnectionStatus writes node.Status.ConnectionStatus directly
53+
// via the Status subresource so it survives a subsequent Spec
54+
// Update. linstor-csi's `linstor-wait-node-online` init container
55+
// polls /v1/nodes/<name> for connection_status:"ONLINE"; this is
56+
// where the satellite's gRPC Hello surfaces that state.
57+
SetConnectionStatus(ctx context.Context, name, status string) error
5158
}
5259

5360
// StoragePoolStore persists StoragePool objects. The composite key is

0 commit comments

Comments
 (0)