Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 101 additions & 10 deletions compliance/collection/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,26 @@ func startAuditLogCollection(ctx context.Context, client sensor.ComplianceServic
return auditReader
}

func manageStream(ctx context.Context, cli sensor.ComplianceServiceClient, sig *concurrency.Signal) {
// manageSendingToSensor sends everything from sensorC channel to sensor
func manageSendingToSensor(ctx context.Context, cli sensor.ComplianceServiceClient, sensorC <-chan *sensor.MsgFromCompliance) {
for {
select {
case <-ctx.Done():
return
case sc := <-sensorC:
client, err := ensureSendingStream(ctx, cli)
if err != nil && ctx.Err() == nil {
// error even after retries
log.Fatalf("unable to establish send stream to sensor: %v", err)
}
if err := client.Send(sc); err != nil {
log.Errorf("failed sending nodeScanV2 to sensor: %v", err)
}
}
}
}

func manageReceiveStream(ctx context.Context, cli sensor.ComplianceServiceClient, sig *concurrency.Signal) {
for {
select {
case <-ctx.Done():
Expand Down Expand Up @@ -181,6 +200,34 @@ func initialClientAndConfig(ctx context.Context, cli sensor.ComplianceServiceCli
return client, config, nil
}

// ensureSendingStream returns a client for sending the data to sensor. It ensures that the stream is open and in case not,
// then the connection is being reopened with exp. back off
func ensureSendingStream(ctx context.Context, cli sensor.ComplianceServiceClient) (sensor.ComplianceService_CommunicateClient, error) {
eb := backoff.NewExponentialBackOff()
eb.MaxInterval = 30 * time.Second
eb.MaxElapsedTime = 3 * time.Minute

var client sensor.ComplianceService_CommunicateClient

operation := func() error {
var err error
client, err = cli.Communicate(ctx)
if err != nil && ctx.Err() != nil {
return backoff.Permanent(err)
}
return err
}
err := backoff.RetryNotify(operation, eb, func(err error, t time.Duration) {
log.Infof("Sleeping for %0.2f seconds between attempts to connect to Sensor for sending", t.Seconds())
})
if err != nil {
return nil, errors.Wrap(err, "Failed to initialize sensor connection for sending")
}
log.Infof("Successfully connected to Sensor for sending at %s", env.AdvertisedEndpoint.Setting())

return client, nil
}

func initializeStream(ctx context.Context, cli sensor.ComplianceServiceClient) (sensor.ComplianceService_CommunicateClient, *sensor.MsgToCompliance_ScrapeConfig, error) {
eb := backoff.NewExponentialBackOff()
eb.MaxInterval = 30 * time.Second
Expand Down Expand Up @@ -208,18 +255,47 @@ func initializeStream(ctx context.Context, cli sensor.ComplianceServiceClient) (
return client, config, nil
}

func scanNode(client sensor.ComplianceService_CommunicateClient, scanner nodescanv2.NodeScanner) error {
func manageNodeScanLoop(ctx context.Context, rescanInterval time.Duration, scanner nodescanv2.NodeScanner) <-chan *sensor.MsgFromCompliance {
sensorC := make(chan *sensor.MsgFromCompliance)
nodeName := getNode()
go func() {
defer close(sensorC)
t := time.NewTicker(rescanInterval)

// first scan should happen on start
msg, err := scanNode(nodeName, scanner)
if err != nil {
log.Errorf("error running scanNode: %v", err)
} else {
sensorC <- msg
}

for {
select {
case <-ctx.Done():
return
case <-t.C:
msg, err := scanNode(nodeName, scanner)
if err != nil {
log.Errorf("error running scanNode: %v", err)
} else {
sensorC <- msg
}
}
}
}()
return sensorC
}

func scanNode(nodeName string, scanner nodescanv2.NodeScanner) (*sensor.MsgFromCompliance, error) {
result, err := scanner.Scan(nodeName)
if err != nil {
return errors.Wrap(err, "error scanning node")
return nil, err
}
msg := sensor.MsgFromCompliance{
return &sensor.MsgFromCompliance{
Node: nodeName,
Msg: &sensor.MsgFromCompliance_NodeScanV2{NodeScanV2: result},
}
return client.Send(&msg)
}, nil
}

func main() {
Expand All @@ -243,12 +319,27 @@ func main() {

stoppedSig := concurrency.NewSignal()

go manageStream(ctx, cli, &stoppedSig)
go manageReceiveStream(ctx, cli, &stoppedSig)

if features.RHCOSNodeScanning.Enabled() {
rescanInterval := complianceUtils.VerifyAndUpdateDuration(env.NodeRescanInterval.DurationSetting())
scanner := nodescanv2.FakeNodeScanner{} // TODO(ROX-12971): Replace with real scanner
go manageNodeScanLoop(ctx, cli, &scanner, rescanInterval)
sensorC := make(chan *sensor.MsgFromCompliance)
defer close(sensorC)
go manageSendingToSensor(ctx, cli, sensorC)

// TODO(ROX-12971): Replace with real scanner
scanner := nodescanv2.FakeNodeScanner{}
rescanInterval := complianceUtils.VerifyAndUpdateDuration(env.NodeRescanInterval.DurationSetting(), time.Hour*4)
log.Infof("Node Rescan interval: %v", rescanInterval)
nodeScansC := manageNodeScanLoop(ctx, rescanInterval, &scanner)
go func() {
for {
select {
case <-ctx.Done():
return
case sensorC <- <-nodeScansC:
}
}
}()
}

signalsC := make(chan os.Signal, 1)
Expand Down
8 changes: 4 additions & 4 deletions compliance/collection/utils/verify_duration.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ var (
)

// VerifyAndUpdateDuration ensures that a given duration is positive bigger than zero and returns a default otherwise
func VerifyAndUpdateDuration(duration time.Duration) time.Duration {
if (duration) <= 0 {
func VerifyAndUpdateDuration(dur, defaultDur time.Duration) time.Duration {
if (dur) <= 0 {
log.Warn("Negative or zero duration found. Setting to 4 hours.")
return time.Hour * 4
return defaultDur
}
return duration
return dur
}
2 changes: 1 addition & 1 deletion compliance/collection/utils/verify_duration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (v *verifyDurationTestSuite) TestVerifyAndUpdateDuration() {

for caseName, testCase := range testCases {
v.Run(caseName, func() {
result := VerifyAndUpdateDuration(testCase.setDuration)
result := VerifyAndUpdateDuration(testCase.setDuration, time.Hour*4)
v.Equal(testCase.expectedDuration, result)
})
}
Expand Down
2 changes: 1 addition & 1 deletion proto/internalapi/sensor/compliance_iservice.proto
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ message MsgToCompliance {
}
}

// A Sensor service that allows Compliance to report node scrapes results and audit events.
// A Sensor service that allows Compliance to report node scrapes results, audit events, and node scans v2.
service ComplianceService {
rpc Communicate (stream MsgFromCompliance) returns (stream MsgToCompliance);
}