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