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 }