github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/client/stdinservice/stdinservice_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 stdinservice 16 17 import ( 18 "bytes" 19 "context" 20 "fmt" 21 "strings" 22 "testing" 23 24 "github.com/google/fleetspeak/fleetspeak/src/client/clitesting" 25 "github.com/google/fleetspeak/fleetspeak/src/common/anypbtest" 26 27 sspb "github.com/google/fleetspeak/fleetspeak/src/client/stdinservice/proto/fleetspeak_stdinservice" 28 fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak" 29 ) 30 31 func TestStdinServiceWithEcho(t *testing.T) { 32 s, err := Factory(&fspb.ClientServiceConfig{ 33 Name: "EchoService", 34 Config: anypbtest.New(t, &sspb.Config{ 35 Cmd: "python", 36 }), 37 }) 38 if err != nil { 39 t.Fatal(err) 40 } 41 42 outChan := make(chan *fspb.Message, 1) 43 err = s.Start(&clitesting.MockServiceContext{ 44 OutChan: outChan, 45 }) 46 if err != nil { 47 t.Fatal(err) 48 } 49 50 err = s.ProcessMessage(context.Background(), 51 &fspb.Message{ 52 MessageType: "StdinServiceInputMessage", 53 Data: anypbtest.New(t, &sspb.InputMessage{ 54 Args: []string{"-c", `print("foo bar")`}, 55 }), 56 }) 57 if err != nil { 58 t.Fatal(err) 59 } 60 61 var output *fspb.Message 62 select { 63 case output = <-outChan: 64 default: 65 t.Fatal(".ProcessMessage (/bin/echo foo bar) expected to produce message, but none found") 66 } 67 68 om := &sspb.OutputMessage{} 69 if err := output.Data.UnmarshalTo(om); err != nil { 70 t.Fatal(err) 71 } 72 73 wantStdout := []byte("foo bar\n") 74 wantStdoutWin := []byte("foo bar\r\n") 75 if !bytes.Equal(om.Stdout, wantStdout) && 76 !bytes.Equal(om.Stdout, wantStdoutWin) { 77 t.Fatalf("unexpected output; got %q, want %q", om.Stdout, wantStdout) 78 } 79 } 80 81 func TestStdinServiceWithCat(t *testing.T) { 82 s, err := Factory(&fspb.ClientServiceConfig{ 83 Name: "CatService", 84 Config: anypbtest.New(t, &sspb.Config{ 85 Cmd: "python", 86 }), 87 }) 88 if err != nil { 89 t.Fatal(err) 90 } 91 92 outChan := make(chan *fspb.Message, 1) 93 err = s.Start(&clitesting.MockServiceContext{ 94 OutChan: outChan, 95 }) 96 if err != nil { 97 t.Fatal(err) 98 } 99 100 err = s.ProcessMessage(context.Background(), 101 &fspb.Message{ 102 MessageType: "StdinServiceInputMessage", 103 Data: anypbtest.New(t, &sspb.InputMessage{ 104 Args: []string{"-c", ` 105 try: 106 my_input = raw_input # Python2 compat 107 except NameError: 108 my_input = input 109 110 try: 111 while True: 112 print(my_input()) 113 except EOFError: 114 pass 115 `}, 116 Input: []byte("foo bar"), 117 }), 118 }) 119 if err != nil { 120 t.Fatalf("s.ProcessMessage(...) = %q, want success", err) 121 } 122 123 var output *fspb.Message 124 select { 125 case output = <-outChan: 126 default: 127 t.Fatal(".ProcessMessage (/bin/cat <<< 'foo bar') expected to produce message, but none found") 128 } 129 130 om := &sspb.OutputMessage{} 131 if err := output.Data.UnmarshalTo(om); err != nil { 132 t.Fatal(err) 133 } 134 135 wantStdout := []byte("foo bar\n") 136 wantStdoutWin := []byte("foo bar\r\n") 137 if !bytes.Equal(om.Stdout, wantStdout) && 138 !bytes.Equal(om.Stdout, wantStdoutWin) { 139 t.Fatalf("unexpected output; got %q, want %q", om.Stdout, wantStdout) 140 } 141 } 142 143 func TestStdinServiceReportsResourceUsage(t *testing.T) { 144 s, err := Factory(&fspb.ClientServiceConfig{ 145 Name: "BashService", 146 Config: anypbtest.New(t, &sspb.Config{ 147 Cmd: "python", 148 }), 149 }) 150 if err != nil { 151 t.Fatal(err) 152 } 153 154 outChan := make(chan *fspb.Message, 1) 155 err = s.Start(&clitesting.MockServiceContext{ 156 OutChan: outChan, 157 }) 158 if err != nil { 159 t.Fatal(err) 160 } 161 162 err = s.ProcessMessage(context.Background(), 163 &fspb.Message{ 164 MessageType: "StdinServiceInputMessage", 165 Data: anypbtest.New(t, &sspb.InputMessage{ 166 // Generate some system (os.listdir) and user (everything else) execution time... 167 Args: []string{"-c", ` 168 import os 169 import time 170 171 t0 = time.time() 172 while time.time() - t0 < 1.: 173 os.listdir(".") 174 `}, 175 }), 176 }) 177 if err != nil { 178 t.Fatal(err) 179 } 180 181 var output *fspb.Message 182 select { 183 case output = <-outChan: 184 default: 185 t.Fatal(".ProcessMessage (/bin/bash ...) expected to produce message, but none found") 186 } 187 188 om := &sspb.OutputMessage{} 189 if err := output.Data.UnmarshalTo(om); err != nil { 190 t.Fatal(err) 191 } 192 193 if om.ResourceUsage == nil { 194 t.Fatalf("unexpected output; StdinServiceOutputMessage.resource_usage not set: %q", om) 195 } 196 197 if om.ResourceUsage.MeanUserCpuRate <= 0 { 198 t.Fatalf("unexpected output; StdinServiceOutputMessage.resource_usage.mean_user_cpu_rate not set: %q", om) 199 } 200 201 if om.ResourceUsage.MeanSystemCpuRate <= 0 { 202 t.Fatalf("unexpected output; StdinServiceOutputMessage.resource_usage.mean_system_cpu_rate not set: %q", om) 203 } 204 205 // We don't test for ResourceUsage.MeanResidentMemory because memory is currently not being 206 // queried after the process has terminated. It's only queried right after launching the command 207 // in which case it can be recorded as "0" which would be indistinguishable from it not being set 208 // at all, resulting in a flaky test case. The fact that the other resource usage metrics have 209 // been set here is good enough for now. 210 211 if om.Timestamp.Seconds <= 0 { 212 t.Fatalf("unexpected output; StdinServiceOutputMessage.timestamp.seconds not set: %q", om) 213 } 214 } 215 216 func TestStdinServiceCancellation(t *testing.T) { 217 s, err := Factory(&fspb.ClientServiceConfig{ 218 Name: "SleepService", 219 Config: anypbtest.New(t, &sspb.Config{ 220 Cmd: "python", 221 }), 222 }) 223 if err != nil { 224 t.Fatal(err) 225 } 226 227 outChan := make(chan *fspb.Message, 1) 228 err = s.Start(&clitesting.MockServiceContext{ 229 OutChan: outChan, 230 }) 231 if err != nil { 232 t.Fatal(err) 233 } 234 235 ctx, c := context.WithCancel(context.Background()) 236 c() 237 238 if err := s.ProcessMessage(ctx, 239 &fspb.Message{ 240 MessageType: "StdinServiceInputMessage", 241 Data: anypbtest.New(t, &sspb.InputMessage{ 242 Args: []string{"-c", fmt.Sprintf(` 243 import time 244 245 time.sleep(%f) 246 `, clitesting.MockCommTimeout.Seconds())}, 247 }), 248 }); err != nil && !strings.HasSuffix(err.Error(), "context canceled") { 249 t.Fatal(err) 250 } else if err == nil { 251 t.Fatal(".ProcessMessage was expected to be cancelled, but returned with no error") 252 } 253 }