]> ruderich.org/simon Gitweb - safcm/safcm.git/blob - remote/sync/commands_test.go
Use SPDX license identifiers
[safcm/safcm.git] / remote / sync / commands_test.go
1 // SPDX-License-Identifier: GPL-3.0-or-later
2 // Copyright (C) 2021-2024  Simon Ruderich
3
4 package sync
5
6 import (
7         "fmt"
8         "io/fs"
9         "os"
10         "os/exec"
11         "testing"
12
13         "ruderich.org/simon/safcm"
14         "ruderich.org/simon/safcm/testutil"
15 )
16
17 func TestSyncCommands(t *testing.T) {
18         exe, err := os.Executable()
19         if err != nil {
20                 t.Fatal(err)
21         }
22         env := append(os.Environ(),
23                 "SAFCM_HELPER="+exe,
24                 "SAFCM_GROUPS=all group1 group2 host.example.org",
25                 "SAFCM_GROUP_all=all",
26                 "SAFCM_GROUP_group1=group1",
27                 "SAFCM_GROUP_group2=group2",
28                 "SAFCM_GROUP_host.example.org=host.example.org",
29         )
30
31         tests := []struct {
32                 name     string
33                 req      safcm.MsgSyncReq
34                 triggers []string
35                 stdout   [][]byte
36                 stderr   [][]byte
37                 errors   []error
38                 expCmds  []*exec.Cmd
39                 expDbg   []string
40                 expResp  safcm.MsgSyncResp
41                 expErr   error
42         }{
43
44                 // NOTE: Also update MsgSyncResp in safcm test cases when
45                 // changing the MsgSyncResp struct!
46
47                 {
48                         "successful command",
49                         safcm.MsgSyncReq{
50                                 Groups: []string{
51                                         "all",
52                                         "group1",
53                                         "group2",
54                                         "host.example.org",
55                                 },
56                                 Commands: []*safcm.Command{
57                                         {
58                                                 OrigGroup: "group",
59                                                 Cmd:       "echo; env | grep SAFCM_",
60                                         },
61                                 },
62                         },
63                         nil,
64                         [][]byte{[]byte("fake stdout/stderr")},
65                         [][]byte{nil},
66                         []error{nil},
67                         []*exec.Cmd{{
68                                 Path: "/bin/sh",
69                                 Args: []string{
70                                         "/bin/sh", "-c",
71                                         "echo; env | grep SAFCM_",
72                                 },
73                                 Env: env,
74                         }},
75                         []string{
76                                 `3: commands: running "/bin/sh" "-c" "echo; env | grep SAFCM_" (group)`,
77                                 "5: commands: command output:\nfake stdout/stderr",
78                         },
79                         safcm.MsgSyncResp{
80                                 CommandChanges: []safcm.CommandChange{
81                                         {
82                                                 Command: "echo; env | grep SAFCM_",
83                                                 Output:  "fake stdout/stderr",
84                                         },
85                                 },
86                         },
87                         nil,
88                 },
89                 {
90                         "successful command (dry-run)",
91                         safcm.MsgSyncReq{
92                                 DryRun: true,
93                                 Groups: []string{
94                                         "all",
95                                         "group1",
96                                         "group2",
97                                         "host.example.org",
98                                 },
99                                 Commands: []*safcm.Command{
100                                         {
101                                                 OrigGroup: "group",
102                                                 Cmd:       "echo; env | grep SAFCM_",
103                                         },
104                                 },
105                         },
106                         nil,
107                         nil,
108                         nil,
109                         nil,
110                         nil,
111                         nil,
112                         safcm.MsgSyncResp{
113                                 CommandChanges: []safcm.CommandChange{
114                                         {
115                                                 Command: "echo; env | grep SAFCM_",
116                                         },
117                                 },
118                         },
119                         nil,
120                 },
121
122                 {
123                         "failed command",
124                         safcm.MsgSyncReq{
125                                 Groups: []string{
126                                         "all",
127                                         "group1",
128                                         "group2",
129                                         "host.example.org",
130                                 },
131                                 Commands: []*safcm.Command{
132                                         {
133                                                 OrigGroup: "group",
134                                                 Cmd:       "echo hi; false",
135                                         },
136                                 },
137                         },
138                         nil,
139                         [][]byte{[]byte("fake stdout/stderr")},
140                         [][]byte{nil},
141                         []error{fmt.Errorf("fake error")},
142                         []*exec.Cmd{{
143                                 Path: "/bin/sh",
144                                 Args: []string{
145                                         "/bin/sh", "-c",
146                                         "echo hi; false",
147                                 },
148                                 Env: env,
149                         }},
150                         []string{
151                                 `3: commands: running "/bin/sh" "-c" "echo hi; false" (group)`,
152                                 "5: commands: command output:\nfake stdout/stderr",
153                         },
154                         safcm.MsgSyncResp{
155                                 CommandChanges: []safcm.CommandChange{
156                                         {
157                                                 Command: "echo hi; false",
158                                                 Output:  "fake stdout/stderr",
159                                                 Error:   "fake error",
160                                         },
161                                 },
162                         },
163                         fmt.Errorf("\"echo hi; false\" failed: fake error"),
164                 },
165                 {
166                         "failed command (dry-run)",
167                         safcm.MsgSyncReq{
168                                 DryRun: true,
169                                 Groups: []string{
170                                         "all",
171                                         "group1",
172                                         "group2",
173                                         "host.example.org",
174                                 },
175                                 Commands: []*safcm.Command{
176                                         {
177                                                 OrigGroup: "group",
178                                                 Cmd:       "echo hi; false",
179                                         },
180                                 },
181                         },
182                         nil,
183                         nil,
184                         nil,
185                         nil,
186                         nil,
187                         nil,
188                         safcm.MsgSyncResp{
189                                 CommandChanges: []safcm.CommandChange{
190                                         {
191                                                 Command: "echo hi; false",
192                                         },
193                                 },
194                         },
195                         nil,
196                 },
197
198                 {
199                         "multiple commands, abort on first failed",
200                         safcm.MsgSyncReq{
201                                 Groups: []string{
202                                         "all",
203                                         "group1",
204                                         "group2",
205                                         "host.example.org",
206                                 },
207                                 Commands: []*safcm.Command{
208                                         {
209                                                 OrigGroup: "group1",
210                                                 Cmd:       "echo first",
211                                         }, {
212                                                 OrigGroup: "group2",
213                                                 Cmd:       "echo second",
214                                         }, {
215                                                 OrigGroup: "group3",
216                                                 Cmd:       "false",
217                                         }, {
218                                                 OrigGroup: "group4",
219                                                 Cmd:       "echo third",
220                                         },
221                                 },
222                         },
223                         nil,
224                         [][]byte{
225                                 []byte("fake stdout/stderr first"),
226                                 []byte("fake stdout/stderr second"),
227                                 nil,
228                         },
229                         [][]byte{
230                                 nil,
231                                 nil,
232                                 nil,
233                         },
234                         []error{
235                                 nil,
236                                 nil,
237                                 fmt.Errorf("fake error"),
238                         },
239                         []*exec.Cmd{{
240                                 Path: "/bin/sh",
241                                 Args: []string{
242                                         "/bin/sh", "-c",
243                                         "echo first",
244                                 },
245                                 Env: env,
246                         }, {
247                                 Path: "/bin/sh",
248                                 Args: []string{
249                                         "/bin/sh", "-c",
250                                         "echo second",
251                                 },
252                                 Env: env,
253                         }, {
254                                 Path: "/bin/sh",
255                                 Args: []string{
256                                         "/bin/sh", "-c",
257                                         "false",
258                                 },
259                                 Env: env,
260                         }},
261                         []string{
262                                 `3: commands: running "/bin/sh" "-c" "echo first" (group1)`,
263                                 "5: commands: command output:\nfake stdout/stderr first",
264                                 `3: commands: running "/bin/sh" "-c" "echo second" (group2)`,
265                                 "5: commands: command output:\nfake stdout/stderr second",
266                                 `3: commands: running "/bin/sh" "-c" "false" (group3)`,
267                         },
268                         safcm.MsgSyncResp{
269                                 CommandChanges: []safcm.CommandChange{
270                                         {
271                                                 Command: "echo first",
272                                                 Output:  "fake stdout/stderr first",
273                                         },
274                                         {
275                                                 Command: "echo second",
276                                                 Output:  "fake stdout/stderr second",
277                                         },
278                                         {
279                                                 Command: "false",
280                                                 Output:  "",
281                                                 Error:   "fake error",
282                                         },
283                                 },
284                         },
285                         fmt.Errorf("\"false\" failed: fake error"),
286                 },
287
288                 {
289                         "triggers",
290                         safcm.MsgSyncReq{
291                                 Groups: []string{
292                                         "all",
293                                         "group1",
294                                         "group2",
295                                         "host.example.org",
296                                 },
297                                 Files: map[string]*safcm.File{
298                                         ".": {
299                                                 OrigGroup: "group",
300                                                 Path:      ".",
301                                                 Mode:      fs.ModeDir | 0700,
302                                                 Uid:       -1,
303                                                 Gid:       -1,
304                                                 TriggerCommands: []string{
305                                                         "echo trigger .",
306                                                 },
307                                         },
308                                         "dir": {
309                                                 OrigGroup: "group",
310                                                 Path:      "dir",
311                                                 Mode:      fs.ModeDir | 0755,
312                                                 Uid:       -1,
313                                                 Gid:       -1,
314                                                 TriggerCommands: []string{
315                                                         "echo trigger dir",
316                                                 },
317                                         },
318                                         "dir/file": {
319                                                 OrigGroup: "group",
320                                                 Path:      "dir/file",
321                                                 Mode:      0644,
322                                                 Uid:       -1,
323                                                 Gid:       -1,
324                                                 Data:      []byte("content\n"),
325                                                 TriggerCommands: []string{
326                                                         "echo trigger dir/file",
327                                                 },
328                                         },
329                                 },
330                                 Commands: []*safcm.Command{
331                                         {
332                                                 OrigGroup: "group",
333                                                 Cmd:       "echo; env | grep SAFCM_",
334                                         },
335                                 },
336                         },
337                         []string{
338                                 ".",
339                                 "dir",
340                         },
341                         [][]byte{
342                                 []byte("fake stdout/stderr ."),
343                                 []byte("fake stdout/stderr dir"),
344                                 []byte("fake stdout/stderr"),
345                         },
346                         [][]byte{
347                                 nil,
348                                 nil,
349                                 nil,
350                         },
351                         []error{
352                                 nil,
353                                 nil,
354                                 nil,
355                         },
356                         []*exec.Cmd{{
357                                 Path: "/bin/sh",
358                                 Args: []string{
359                                         "/bin/sh", "-c",
360                                         "echo trigger .",
361                                 },
362                                 Env: env,
363                         }, {
364                                 Path: "/bin/sh",
365                                 Args: []string{
366                                         "/bin/sh", "-c",
367                                         "echo trigger dir",
368                                 },
369                                 Env: env,
370                         }, {
371                                 Path: "/bin/sh",
372                                 Args: []string{
373                                         "/bin/sh", "-c",
374                                         "echo; env | grep SAFCM_",
375                                 },
376                                 Env: env,
377                         }},
378                         []string{
379                                 `3: commands: running "/bin/sh" "-c" "echo trigger ." (".")`,
380                                 "5: commands: command output:\nfake stdout/stderr .",
381                                 `3: commands: running "/bin/sh" "-c" "echo trigger dir" ("dir")`,
382                                 "5: commands: command output:\nfake stdout/stderr dir",
383                                 `3: commands: running "/bin/sh" "-c" "echo; env | grep SAFCM_" (group)`,
384                                 "5: commands: command output:\nfake stdout/stderr",
385                         },
386                         safcm.MsgSyncResp{
387                                 CommandChanges: []safcm.CommandChange{
388                                         {
389                                                 Command: "echo trigger .",
390                                                 Trigger: ".",
391                                                 Output:  "fake stdout/stderr .",
392                                         },
393                                         {
394                                                 Command: "echo trigger dir",
395                                                 Trigger: "dir",
396                                                 Output:  "fake stdout/stderr dir",
397                                         },
398                                         {
399                                                 Command: "echo; env | grep SAFCM_",
400                                                 Output:  "fake stdout/stderr",
401                                         },
402                                 },
403                         },
404                         nil,
405                 },
406
407                 {
408                         "failed trigger",
409                         safcm.MsgSyncReq{
410                                 Groups: []string{
411                                         "all",
412                                         "group1",
413                                         "group2",
414                                         "host.example.org",
415                                 },
416                                 Files: map[string]*safcm.File{
417                                         ".": {
418                                                 OrigGroup: "group",
419                                                 Path:      ".",
420                                                 Mode:      fs.ModeDir | 0700,
421                                                 Uid:       -1,
422                                                 Gid:       -1,
423                                                 TriggerCommands: []string{
424                                                         "echo trigger .",
425                                                 },
426                                         },
427                                         "dir": {
428                                                 OrigGroup: "group",
429                                                 Path:      "dir",
430                                                 Mode:      fs.ModeDir | 0755,
431                                                 Uid:       -1,
432                                                 Gid:       -1,
433                                                 TriggerCommands: []string{
434                                                         "false",
435                                                 },
436                                         },
437                                         "dir/file": {
438                                                 OrigGroup: "group",
439                                                 Path:      "dir/file",
440                                                 Mode:      0644,
441                                                 Uid:       -1,
442                                                 Gid:       -1,
443                                                 Data:      []byte("content\n"),
444                                                 TriggerCommands: []string{
445                                                         "echo trigger dir/file",
446                                                 },
447                                         },
448                                 },
449                                 Commands: []*safcm.Command{
450                                         {
451                                                 OrigGroup: "group",
452                                                 Cmd:       "echo; env | grep SAFCM_",
453                                         },
454                                 },
455                         },
456                         []string{
457                                 ".",
458                                 "dir",
459                         },
460                         [][]byte{
461                                 []byte("fake stdout/stderr ."),
462                                 []byte("fake stdout/stderr dir"),
463                         },
464                         [][]byte{
465                                 nil,
466                                 nil,
467                         },
468                         []error{
469                                 nil,
470                                 fmt.Errorf("fake error"),
471                         },
472                         []*exec.Cmd{{
473                                 Path: "/bin/sh",
474                                 Args: []string{
475                                         "/bin/sh", "-c",
476                                         "echo trigger .",
477                                 },
478                                 Env: env,
479                         }, {
480                                 Path: "/bin/sh",
481                                 Args: []string{
482                                         "/bin/sh", "-c",
483                                         "false",
484                                 },
485                                 Env: env,
486                         }},
487                         []string{
488                                 `3: commands: running "/bin/sh" "-c" "echo trigger ." (".")`,
489                                 "5: commands: command output:\nfake stdout/stderr .",
490                                 `3: commands: running "/bin/sh" "-c" "false" ("dir")`,
491                                 "5: commands: command output:\nfake stdout/stderr dir",
492                         },
493                         safcm.MsgSyncResp{
494                                 CommandChanges: []safcm.CommandChange{
495                                         {
496                                                 Command: "echo trigger .",
497                                                 Trigger: ".",
498                                                 Output:  "fake stdout/stderr .",
499                                         },
500                                         {
501                                                 Command: "false",
502                                                 Trigger: "dir",
503                                                 Output:  "fake stdout/stderr dir",
504                                                 Error:   "fake error",
505                                         },
506                                 },
507                         },
508                         fmt.Errorf("\"false\" failed: fake error"),
509                 },
510         }
511
512         for _, tc := range tests {
513                 t.Run(tc.name, func(t *testing.T) {
514                         s, res := prepareSync(tc.req, &testRunner{
515                                 t:         t,
516                                 expCmds:   tc.expCmds,
517                                 resStdout: tc.stdout,
518                                 resStderr: tc.stderr,
519                                 resError:  tc.errors,
520                         })
521                         s.triggers = tc.triggers
522
523                         err := s.syncCommands()
524                         testutil.AssertErrorEqual(t, "err", err, tc.expErr)
525                         dbg := res.Wait()
526
527                         testutil.AssertEqual(t, "resp", s.resp, tc.expResp)
528                         testutil.AssertEqual(t, "dbg", dbg, tc.expDbg)
529                 })
530         }
531 }