Skip to content

Commit 5a62935

Browse files
gNOI: Add gNMI Server changes for Healthz Acknowledge RPC (#509)
1 parent ca045ea commit 5a62935

File tree

7 files changed

+642
-10
lines changed

7 files changed

+642
-10
lines changed

common_utils/context.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const (
4545
GNOI_REBOOT
4646
GNOI_FACTORY_RESET
4747
GNOI_OS_INSTALL
48+
GNOI_HEALTHZ_ACK
4849
GNOI_HEALTHZ_CHECK
4950
GNOI_HEALTHZ_COLLECT
5051
DBUS
@@ -85,6 +86,8 @@ func (c CounterType) String() string {
8586
return "GNOI Factory Reset"
8687
case GNOI_OS_INSTALL:
8788
return "GNOI OS Install"
89+
case GNOI_HEALTHZ_ACK:
90+
return "GNOI Healthz Ack"
8891
case GNOI_HEALTHZ_CHECK:
8992
return "GNOI Healthz Check"
9093
case GNOI_HEALTHZ_COLLECT:

gnmi_server/gnoi_healthz.go

Lines changed: 121 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,20 @@ import (
2020
)
2121

2222
const (
23-
compKey string = "name"
24-
ddComponentKey string = "component"
25-
ddComponentAll string = "all"
26-
ddLogLvlKey string = "level"
27-
ddLogLvlAlert string = "alert"
28-
ddLogLvlCritical string = "critical"
29-
ddLogLvlAll string = "all"
30-
ddLogLvlSuf string = "-info"
31-
ddFileSegSize int = 4096
32-
artifactSleepTime time.Duration = 5 * time.Second
23+
compKey string = "name"
24+
ddComponentKey string = "component"
25+
ddComponentAll string = "all"
26+
ddLogLvlKey string = "level"
27+
ddLogLvlAlert string = "alert"
28+
ddLogLvlCritical string = "critical"
29+
ddLogLvlAll string = "all"
30+
ddLogLvlSuf string = "-info"
31+
ddFileSegSize int = 4096
3332
)
3433

3534
var (
3635
artifactColTimeout time.Duration = 5 * time.Minute
36+
artifactSleepTime time.Duration = 5 * time.Second
3737
)
3838

3939
func isDebugData(p *types.Path) bool {
@@ -207,6 +207,117 @@ func (srv *HealthzServer) Get(ctx context.Context, req *healthz.GetRequest) (*he
207207
return nil, status.Errorf(codes.Unimplemented, "Healthz.Get is unimplemented for component: [%s].", path.GetElem())
208208
}
209209

210+
func (srv *HealthzServer) Artifact(req *healthz.ArtifactRequest, stream healthz.Healthz_ArtifactServer) error {
211+
log.V(1).Infof("Artifact RPC Get request ID: %+v", req.GetId())
212+
file := req.GetId()
213+
allowedDir := "/tmp/dump"
214+
cleanPath := filepath.Clean(file)
215+
if !strings.HasPrefix(cleanPath, allowedDir) {
216+
return status.Errorf(codes.InvalidArgument, "Invalid artifact path")
217+
}
218+
file_path := filepath.Join("/mnt/host", cleanPath)
219+
220+
f, err := os.Open(file_path)
221+
if err != nil {
222+
if os.IsNotExist(err) {
223+
return status.Errorf(codes.NotFound, "File not found: %v", err)
224+
}
225+
return status.Errorf(codes.Internal, "Failed to open file: %v", err)
226+
}
227+
defer f.Close()
228+
229+
hasher := sha256.New()
230+
size, err := io.Copy(hasher, f) // Streams through hasher, constant memory
231+
if err != nil {
232+
return status.Errorf(codes.Internal, "Error hashing: [%v]", err)
233+
}
234+
hashSum := hasher.Sum(nil)
235+
236+
// Reset file pointer to start for streaming chunks
237+
if _, err := f.Seek(0, io.SeekStart); err != nil {
238+
return status.Errorf(codes.Internal, "Failed to reset file pointer: %v", err)
239+
}
240+
241+
header := &healthz.ArtifactResponse{
242+
Contents: &healthz.ArtifactResponse_Header{
243+
Header: &healthz.ArtifactHeader{
244+
Id: file,
245+
ArtifactType: &healthz.ArtifactHeader_File{
246+
File: &healthz.FileArtifactType{
247+
Name: file,
248+
Size: size,
249+
Hash: &types.HashType{
250+
Method: types.HashType_SHA256,
251+
Hash: hashSum[:],
252+
},
253+
},
254+
},
255+
},
256+
},
257+
}
258+
if err := stream.Send(header); err != nil {
259+
log.Errorf("failed to send header: %v", err)
260+
return err
261+
}
262+
263+
buf := make([]byte, ddFileSegSize)
264+
265+
for {
266+
n, err := f.Read(buf)
267+
if err == io.EOF {
268+
break
269+
}
270+
if err != nil {
271+
log.Errorf("failed to send trailer: %v", err)
272+
return status.Errorf(codes.Internal, "File read error: %v", err)
273+
}
274+
content := &healthz.ArtifactResponse{
275+
Contents: &healthz.ArtifactResponse_Bytes{
276+
Bytes: buf[:n],
277+
},
278+
}
279+
if err := stream.Send(content); err != nil {
280+
log.Errorf("failed to send Artifact data: %v", err)
281+
return err
282+
}
283+
}
284+
285+
trailer := &healthz.ArtifactResponse{
286+
Contents: &healthz.ArtifactResponse_Trailer{
287+
Trailer: &healthz.ArtifactTrailer{},
288+
},
289+
}
290+
if err := stream.Send(trailer); err != nil {
291+
log.Errorf("failed to send trailer: %v", err)
292+
return err
293+
}
294+
log.Infof("Successfully streamed artifact: %s (size=%d bytes)", file_path, size)
295+
return nil
296+
}
297+
298+
// Acknowledge implements the corresponding RPC.
299+
func (srv *HealthzServer) Acknowledge(ctx context.Context, req *healthz.AcknowledgeRequest) (*healthz.AcknowledgeResponse, error) {
300+
log.V(1).Infof("Acknowledge RPC Get request ID: %+v", req.GetId())
301+
ctx, err := authenticate(srv.config, ctx, "gnoi", false)
302+
if err != nil {
303+
log.Errorf("Healthz.Acknowledge authentication failed: %v", err)
304+
return nil, err
305+
}
306+
sc, err := ssc.NewDbusClient()
307+
if err != nil {
308+
log.Errorf("NewDbusClient error: %v\n", err)
309+
return nil, err
310+
}
311+
defer sc.Close()
312+
_, err = sc.HealthzAck(req.GetId())
313+
if err != nil {
314+
log.Errorf("HealthzAck() Dbus failed: %v", err)
315+
return nil, status.Errorf(codes.Internal, "Host service error: %v", err)
316+
}
317+
318+
return &healthz.AcknowledgeResponse{}, nil
319+
}
320+
210321
func (srv *HealthzServer) List(ctx context.Context, req *healthz.ListRequest) (*healthz.ListResponse, error) {
211322
return nil, status.Errorf(codes.Unimplemented, "gNOI Healthz List not implemented")
212323
}

0 commit comments

Comments
 (0)