github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/client/system_service_test.go (about)

     1  // Copyright 2017 Google Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package client
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"fmt"
    21  	"io"
    22  	"os"
    23  	"sync"
    24  	"testing"
    25  	"time"
    26  
    27  	log "github.com/golang/glog"
    28  	"google.golang.org/protobuf/proto"
    29  
    30  	"github.com/google/fleetspeak/fleetspeak/src/client/service"
    31  	"github.com/google/fleetspeak/fleetspeak/src/common"
    32  	"github.com/google/fleetspeak/fleetspeak/src/common/anypbtest"
    33  
    34  	fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak"
    35  	mpb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak_monitoring"
    36  )
    37  
    38  type fakeServiceContext struct {
    39  	service.Context
    40  	revokedCerts *fspb.RevokedCertificateList
    41  	out          chan *fspb.Message
    42  }
    43  
    44  func (sc fakeServiceContext) Send(ctx context.Context, m service.AckMessage) error {
    45  	select {
    46  	case <-ctx.Done():
    47  		return ctx.Err()
    48  	case sc.out <- m.M:
    49  		return nil
    50  	}
    51  }
    52  
    53  func (sc fakeServiceContext) GetFileIfModified(_ context.Context, name string, _ time.Time) (data io.ReadCloser, mod time.Time, err error) {
    54  	if name != "RevokedCertificates" {
    55  		return nil, time.Time{}, fmt.Errorf("file [%v] not found", name)
    56  	}
    57  	d, err := proto.Marshal(sc.revokedCerts)
    58  	if err != nil {
    59  		log.Fatalf("Failed to marshal revokedCerts: %v", err)
    60  	}
    61  	return io.NopCloser(bytes.NewReader(d)), time.Now(), nil
    62  }
    63  
    64  type testEnv struct {
    65  	client Client
    66  	ctx    fakeServiceContext
    67  }
    68  
    69  func setUpTestEnv(t *testing.T) *testEnv {
    70  	t.Helper()
    71  
    72  	out := make(chan *fspb.Message)
    73  	env := testEnv{
    74  		client: Client{
    75  			acks:      make(chan common.MessageID, 1),
    76  			errs:      make(chan *fspb.MessageErrorData, 1),
    77  			pid:       os.Getpid(),
    78  			startTime: time.Unix(1234567890, 0),
    79  		},
    80  		ctx: fakeServiceContext{
    81  			out:          out,
    82  			revokedCerts: &fspb.RevokedCertificateList{},
    83  		},
    84  	}
    85  
    86  	srv := systemService{
    87  		client: &env.client,
    88  	}
    89  	err := srv.Start(&env.ctx)
    90  	if err != nil {
    91  		t.Fatalf("Starting test environment: %v", err)
    92  	}
    93  	t.Cleanup(func() {
    94  		// Empty the out channel in parallel,
    95  		// so that it can be written to during shutdown.
    96  		var wg sync.WaitGroup
    97  		defer wg.Wait()
    98  		wg.Add(1)
    99  		go func() {
   100  			defer wg.Done()
   101  			for range out {
   102  			}
   103  		}()
   104  
   105  		err := srv.Stop()
   106  		if err != nil {
   107  			t.Fatalf("Stopping test environment: %v", err)
   108  		}
   109  		close(out)
   110  	})
   111  	return &env
   112  }
   113  
   114  func TestAckMsg(t *testing.T) {
   115  	env := setUpTestEnv(t)
   116  	id, err := common.BytesToMessageID([]byte("00000000000000000000000000000001"))
   117  	if err != nil {
   118  		t.Fatalf("Unable to create MessageID: %v", err)
   119  	}
   120  	env.client.acks <- id
   121  	res := <-env.ctx.out
   122  	wantMessage := fspb.Message{
   123  		Destination: &fspb.Address{
   124  			ServiceName: "system",
   125  		},
   126  		MessageType: "MessageAck",
   127  		Priority:    fspb.Message_HIGH,
   128  		Background:  true,
   129  	}
   130  	wantMessage.Data = anypbtest.New(t, &fspb.MessageAckData{MessageIds: [][]byte{id.Bytes()}})
   131  	if !proto.Equal(res, &wantMessage) {
   132  		t.Errorf("got %+v for ack result, want %+v", res.String(), wantMessage.String())
   133  	}
   134  }
   135  
   136  func TestErrorMsg(t *testing.T) {
   137  	env := setUpTestEnv(t)
   138  	ed := &fspb.MessageErrorData{
   139  		MessageId: []byte("00000000000000000000000000000001"),
   140  		Error:     "a terrible test error",
   141  	}
   142  	env.client.errs <- ed
   143  	res := <-env.ctx.out
   144  	wantMessage := fspb.Message{
   145  		Destination: &fspb.Address{
   146  			ServiceName: "system",
   147  		},
   148  		MessageType: "MessageError",
   149  		Priority:    fspb.Message_HIGH,
   150  		Background:  true,
   151  	}
   152  	wantMessage.Data = anypbtest.New(t, ed)
   153  	if !proto.Equal(res, &wantMessage) {
   154  		t.Errorf("got %+v for err result, want %+v", res.String(), wantMessage.String())
   155  	}
   156  }
   157  
   158  func TestStatsMsg(t *testing.T) {
   159  	// Note: this changes global state which should rather be configurable from
   160  	// the outside.
   161  	prevPeriod := StatsSamplePeriod
   162  	prevSize := StatsSampleSize
   163  	StatsSamplePeriod = 100 * time.Millisecond
   164  	StatsSampleSize = 5
   165  	defer func() {
   166  		StatsSamplePeriod = prevPeriod
   167  		StatsSampleSize = prevSize
   168  	}()
   169  
   170  	env := setUpTestEnv(t)
   171  	statsTimeout := 2 * time.Second
   172  	ctx, cancel := context.WithTimeout(context.Background(), statsTimeout)
   173  	defer cancel()
   174  
   175  	for {
   176  		select {
   177  		case res := <-env.ctx.out:
   178  			if res.MessageType != "ResourceUsage" {
   179  				t.Errorf("Received message of unexpected type '%s'", res.MessageType)
   180  				continue
   181  			}
   182  			rud := &mpb.ResourceUsageData{}
   183  			if err := res.Data.UnmarshalTo(rud); err != nil {
   184  				t.Fatalf("Failed to unmarshal resource-usage data: %v", err)
   185  			}
   186  			if rud.Scope != "system" {
   187  				t.Errorf("Expected scope 'system', got %s instead.", rud.Scope)
   188  			}
   189  			expectedStartTime := int64(1234567890)
   190  			if rud.ProcessStartTime.Seconds != expectedStartTime {
   191  				t.Errorf("Expected process_start_time to be %d, but it was %d", expectedStartTime, rud.ProcessStartTime.Seconds)
   192  			}
   193  			if rud.ResourceUsage.MeanResidentMemory <= 0.0 {
   194  				t.Error("mean_resident_memory is not set")
   195  			}
   196  			if rud.ResourceUsage.MaxResidentMemory <= 0 {
   197  				t.Error("max_resident_memory is not set")
   198  			}
   199  			return
   200  		case <-ctx.Done():
   201  			t.Errorf("No message received after %v", statsTimeout)
   202  			return
   203  		}
   204  	}
   205  }