// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package singleprocess import ( // "sync" //"github.com/hashicorp/go-hclog" // "github.com/hashicorp/go-memdb" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/hashicorp/vagrant/internal/server/proto/vagrant_server" // "github.com/hashicorp/vagrant/internal/server/singleprocess/state" ) // defaultLogLimitBacklog is the default backlog amount to send down. const defaultLogLimitBacklog = 100 // TODO: test func (s *service) GetLogStream( req *vagrant_server.GetLogStreamRequest, srv vagrant_server.Vagrant_GetLogStreamServer, ) error { //log := hclog.FromContext(srv.Context()) // Default the limit if req.LimitBacklog == 0 { req.LimitBacklog = defaultLogLimitBacklog } // var instanceFunc func(ws memdb.WatchSet) ([]*state.Instance, error) switch scope := req.Scope.(type) { // case *vagrant_server.GetLogStreamRequest_DeploymentId: // log = log.With("deployment_id", scope.DeploymentId) // instanceFunc = func(ws memdb.WatchSet) ([]*state.Instance, error) { // return s.state.InstancesByDeployment(scope.DeploymentId, ws) // } // case *vagrant_server.GetLogStreamRequest_Application_: // if scope.Application == nil || // scope.Application.Application == nil || // scope.Application.Workspace == nil { // return status.Errorf( // codes.FailedPrecondition, // "application scope requires the application and workspace fields to be set", // ) // } // log = log.With( // "project", scope.Application.Application.Project, // "application", scope.Application.Application.Application, // "workspace", scope.Application.Workspace.Workspace, // ) // instanceFunc = func(ws memdb.WatchSet) ([]*state.Instance, error) { // return s.state.InstancesByApp( // scope.Application.Application, // scope.Application.Workspace, // ws, // ) // } default: return status.Errorf( codes.FailedPrecondition, "invalid scope supplied: %T - %T", req.Scope, scope, ) } // We keep track of what instances we already have readers for here. // var instanceSetLock sync.Mutex //instanceSet := make(map[string]struct{}) // // We loop forever so that we can automatically get any new instances that // // join as we have an open log stream. // for { // // Get all our records // ws := memdb.NewWatchSet() // records, err := instanceFunc(ws) // if err != nil { // return err // } // log.Trace("instances loaded", "len", len(records)) // // For each record, start a goroutine that reads the log entries and sends them. // for _, record := range records { // instanceId := record.Id // deploymentId := record.DeploymentId // // If we already have a reader for this, then do nothing. // instanceSetLock.Lock() // _, exit := instanceSet[instanceId] // instanceSet[instanceId] = struct{}{} // instanceSetLock.Unlock() // if exit { // continue // } // // Start our reader up // r := record.LogBuffer.Reader(req.LimitBacklog) // instanceLog := log.With("instance_id", instanceId) // instanceLog.Trace("instance log stream starting") // go r.CloseContext(srv.Context()) // go func() { // defer instanceLog.Debug("instance log stream ending") // defer func() { // instanceSetLock.Lock() // defer instanceSetLock.Unlock() // delete(instanceSet, instanceId) // }() // for { // entries := r.Read(64, true) // if entries == nil { // return // } // lines := make([]*vagrant_server.LogBatch_Entry, len(entries)) // for i, v := range entries { // lines[i] = v.(*vagrant_server.LogBatch_Entry) // } // instanceLog.Trace("sending instance log data", "entries", len(entries)) // srv.Send(&vagrant_server.LogBatch{ // DeploymentId: deploymentId, // InstanceId: instanceId, // Lines: lines, // }) // } // }() // } // // Wait for changes or to be done // if err := ws.WatchCtx(srv.Context()); err != nil { // // If our context ended, exit with that // if err := srv.Context().Err(); err != nil { // return err // } // return err // } // } }