]> ruderich.org/simon Gitweb - safcm/safcm.git/blob - cmd/safcm-remote/sync/commands_test.go
142a2542a931d4191d56f0b5be641da6a2b00cd7
[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: []string{
69                                         "echo; env | grep SAFCM_",
70                                 },
71                         },
72                         nil,
73                         [][]byte{[]byte("fake stdout/stderr")},
74                         [][]byte{nil},
75                         []error{nil},
76                         []*exec.Cmd{{
77                                 Path: "/bin/sh",
78                                 Args: []string{
79                                         "/bin/sh", "-c",
80                                         "echo; env | grep SAFCM_",
81                                 },
82                                 Env: env,
83                         }},
84                         []string{
85                                 `3: sync remote: commands: running "/bin/sh" "-c" "echo; env | grep SAFCM_"`,
86                                 "5: sync remote: commands: command output:\nfake stdout/stderr",
87                         },
88                         safcm.MsgSyncResp{
89                                 CommandChanges: []safcm.CommandChange{
90                                         {
91                                                 Command: "echo; env | grep SAFCM_",
92                                                 Output:  "fake stdout/stderr",
93                                         },
94                                 },
95                         },
96                         nil,
97                 },
98                 {
99                         "successful command (dry-run)",
100                         safcm.MsgSyncReq{
101                                 DryRun: true,
102                                 Groups: []string{
103                                         "all",
104                                         "group1",
105                                         "group2",
106                                         "host.example.org",
107                                 },
108                                 Commands: []string{
109                                         "echo; env | grep SAFCM_",
110                                 },
111                         },
112                         nil,
113                         nil,
114                         nil,
115                         nil,
116                         nil,
117                         nil,
118                         safcm.MsgSyncResp{
119                                 CommandChanges: []safcm.CommandChange{
120                                         {
121                                                 Command: "echo; env | grep SAFCM_",
122                                         },
123                                 },
124                         },
125                         nil,
126                 },
127
128                 {
129                         "failed command",
130                         safcm.MsgSyncReq{
131                                 Groups: []string{
132                                         "all",
133                                         "group1",
134                                         "group2",
135                                         "host.example.org",
136                                 },
137                                 Commands: []string{
138                                         "echo hi; false",
139                                 },
140                         },
141                         nil,
142                         [][]byte{[]byte("fake stdout/stderr")},
143                         [][]byte{nil},
144                         []error{fmt.Errorf("fake error")},
145                         []*exec.Cmd{{
146                                 Path: "/bin/sh",
147                                 Args: []string{
148                                         "/bin/sh", "-c",
149                                         "echo hi; false",
150                                 },
151                                 Env: env,
152                         }},
153                         []string{
154                                 `3: sync remote: commands: running "/bin/sh" "-c" "echo hi; false"`,
155                                 "5: sync remote: commands: command output:\nfake stdout/stderr",
156                         },
157                         safcm.MsgSyncResp{
158                                 CommandChanges: []safcm.CommandChange{
159                                         {
160                                                 Command: "echo hi; false",
161                                                 Output:  "fake stdout/stderr",
162                                                 Error:   "fake error",
163                                         },
164                                 },
165                         },
166                         fmt.Errorf("\"echo hi; false\" failed: fake error"),
167                 },
168                 {
169                         "failed command (dry-run)",
170                         safcm.MsgSyncReq{
171                                 DryRun: true,
172                                 Groups: []string{
173                                         "all",
174                                         "group1",
175                                         "group2",
176                                         "host.example.org",
177                                 },
178                                 Commands: []string{
179                                         "echo hi; false",
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: []string{
208                                         "echo first",
209                                         "echo second",
210                                         "false",
211                                         "echo third",
212                                 },
213                         },
214                         nil,
215                         [][]byte{
216                                 []byte("fake stdout/stderr first"),
217                                 []byte("fake stdout/stderr second"),
218                                 nil,
219                         },
220                         [][]byte{
221                                 nil,
222                                 nil,
223                                 nil,
224                         },
225                         []error{
226                                 nil,
227                                 nil,
228                                 fmt.Errorf("fake error"),
229                         },
230                         []*exec.Cmd{{
231                                 Path: "/bin/sh",
232                                 Args: []string{
233                                         "/bin/sh", "-c",
234                                         "echo first",
235                                 },
236                                 Env: env,
237                         }, {
238                                 Path: "/bin/sh",
239                                 Args: []string{
240                                         "/bin/sh", "-c",
241                                         "echo second",
242                                 },
243                                 Env: env,
244                         }, {
245                                 Path: "/bin/sh",
246                                 Args: []string{
247                                         "/bin/sh", "-c",
248                                         "false",
249                                 },
250                                 Env: env,
251                         }},
252                         []string{
253                                 `3: sync remote: commands: running "/bin/sh" "-c" "echo first"`,
254                                 "5: sync remote: commands: command output:\nfake stdout/stderr first",
255                                 `3: sync remote: commands: running "/bin/sh" "-c" "echo second"`,
256                                 "5: sync remote: commands: command output:\nfake stdout/stderr second",
257                                 `3: sync remote: commands: running "/bin/sh" "-c" "false"`,
258                         },
259                         safcm.MsgSyncResp{
260                                 CommandChanges: []safcm.CommandChange{
261                                         {
262                                                 Command: "echo first",
263                                                 Output:  "fake stdout/stderr first",
264                                         },
265                                         {
266                                                 Command: "echo second",
267                                                 Output:  "fake stdout/stderr second",
268                                         },
269                                         {
270                                                 Command: "false",
271                                                 Output:  "",
272                                                 Error:   "fake error",
273                                         },
274                                 },
275                         },
276                         fmt.Errorf("\"false\" failed: fake error"),
277                 },
278
279                 {
280                         "triggers",
281                         safcm.MsgSyncReq{
282                                 Groups: []string{
283                                         "all",
284                                         "group1",
285                                         "group2",
286                                         "host.example.org",
287                                 },
288                                 Files: map[string]*safcm.File{
289                                         ".": {
290                                                 Path:      ".",
291                                                 Mode:      fs.ModeDir | 0700,
292                                                 Uid:       -1,
293                                                 Gid:       -1,
294                                                 OrigGroup: "group",
295                                                 TriggerCommands: []string{
296                                                         "echo trigger .",
297                                                 },
298                                         },
299                                         "dir": {
300                                                 Path:      "dir",
301                                                 Mode:      fs.ModeDir | 0755,
302                                                 Uid:       -1,
303                                                 Gid:       -1,
304                                                 OrigGroup: "group",
305                                                 TriggerCommands: []string{
306                                                         "echo trigger dir",
307                                                 },
308                                         },
309                                         "dir/file": {
310                                                 Path:      "dir/file",
311                                                 Mode:      0644,
312                                                 Uid:       -1,
313                                                 Gid:       -1,
314                                                 Data:      []byte("content\n"),
315                                                 OrigGroup: "group",
316                                                 TriggerCommands: []string{
317                                                         "echo trigger dir/file",
318                                                 },
319                                         },
320                                 },
321                                 Commands: []string{
322                                         "echo; env | grep SAFCM_",
323                                 },
324                         },
325                         []string{
326                                 ".",
327                                 "dir",
328                         },
329                         [][]byte{
330                                 []byte("fake stdout/stderr ."),
331                                 []byte("fake stdout/stderr dir"),
332                                 []byte("fake stdout/stderr"),
333                         },
334                         [][]byte{
335                                 nil,
336                                 nil,
337                                 nil,
338                         },
339                         []error{
340                                 nil,
341                                 nil,
342                                 nil,
343                         },
344                         []*exec.Cmd{{
345                                 Path: "/bin/sh",
346                                 Args: []string{
347                                         "/bin/sh", "-c",
348                                         "echo trigger .",
349                                 },
350                                 Env: env,
351                         }, {
352                                 Path: "/bin/sh",
353                                 Args: []string{
354                                         "/bin/sh", "-c",
355                                         "echo trigger dir",
356                                 },
357                                 Env: env,
358                         }, {
359                                 Path: "/bin/sh",
360                                 Args: []string{
361                                         "/bin/sh", "-c",
362                                         "echo; env | grep SAFCM_",
363                                 },
364                                 Env: env,
365                         }},
366                         []string{
367                                 `3: sync remote: commands: running "/bin/sh" "-c" "echo trigger ."`,
368                                 "5: sync remote: commands: command output:\nfake stdout/stderr .",
369                                 `3: sync remote: commands: running "/bin/sh" "-c" "echo trigger dir"`,
370                                 "5: sync remote: commands: command output:\nfake stdout/stderr dir",
371                                 `3: sync remote: commands: running "/bin/sh" "-c" "echo; env | grep SAFCM_"`,
372                                 "5: sync remote: commands: command output:\nfake stdout/stderr",
373                         },
374                         safcm.MsgSyncResp{
375                                 CommandChanges: []safcm.CommandChange{
376                                         {
377                                                 Command: "echo trigger .",
378                                                 Trigger: ".",
379                                                 Output:  "fake stdout/stderr .",
380                                         },
381                                         {
382                                                 Command: "echo trigger dir",
383                                                 Trigger: "dir",
384                                                 Output:  "fake stdout/stderr dir",
385                                         },
386                                         {
387                                                 Command: "echo; env | grep SAFCM_",
388                                                 Output:  "fake stdout/stderr",
389                                         },
390                                 },
391                         },
392                         nil,
393                 },
394
395                 {
396                         "failed trigger",
397                         safcm.MsgSyncReq{
398                                 Groups: []string{
399                                         "all",
400                                         "group1",
401                                         "group2",
402                                         "host.example.org",
403                                 },
404                                 Files: map[string]*safcm.File{
405                                         ".": {
406                                                 Path:      ".",
407                                                 Mode:      fs.ModeDir | 0700,
408                                                 Uid:       -1,
409                                                 Gid:       -1,
410                                                 OrigGroup: "group",
411                                                 TriggerCommands: []string{
412                                                         "echo trigger .",
413                                                 },
414                                         },
415                                         "dir": {
416                                                 Path:      "dir",
417                                                 Mode:      fs.ModeDir | 0755,
418                                                 Uid:       -1,
419                                                 Gid:       -1,
420                                                 OrigGroup: "group",
421                                                 TriggerCommands: []string{
422                                                         "false",
423                                                 },
424                                         },
425                                         "dir/file": {
426                                                 Path:      "dir/file",
427                                                 Mode:      0644,
428                                                 Uid:       -1,
429                                                 Gid:       -1,
430                                                 Data:      []byte("content\n"),
431                                                 OrigGroup: "group",
432                                                 TriggerCommands: []string{
433                                                         "echo trigger dir/file",
434                                                 },
435                                         },
436                                 },
437                                 Commands: []string{
438                                         "echo; env | grep SAFCM_",
439                                 },
440                         },
441                         []string{
442                                 ".",
443                                 "dir",
444                         },
445                         [][]byte{
446                                 []byte("fake stdout/stderr ."),
447                                 []byte("fake stdout/stderr dir"),
448                         },
449                         [][]byte{
450                                 nil,
451                                 nil,
452                         },
453                         []error{
454                                 nil,
455                                 fmt.Errorf("fake error"),
456                         },
457                         []*exec.Cmd{{
458                                 Path: "/bin/sh",
459                                 Args: []string{
460                                         "/bin/sh", "-c",
461                                         "echo trigger .",
462                                 },
463                                 Env: env,
464                         }, {
465                                 Path: "/bin/sh",
466                                 Args: []string{
467                                         "/bin/sh", "-c",
468                                         "false",
469                                 },
470                                 Env: env,
471                         }},
472                         []string{
473                                 `3: sync remote: commands: running "/bin/sh" "-c" "echo trigger ."`,
474                                 "5: sync remote: commands: command output:\nfake stdout/stderr .",
475                                 `3: sync remote: commands: running "/bin/sh" "-c" "false"`,
476                                 "5: sync remote: commands: command output:\nfake stdout/stderr dir",
477                         },
478                         safcm.MsgSyncResp{
479                                 CommandChanges: []safcm.CommandChange{
480                                         {
481                                                 Command: "echo trigger .",
482                                                 Trigger: ".",
483                                                 Output:  "fake stdout/stderr .",
484                                         },
485                                         {
486                                                 Command: "false",
487                                                 Trigger: "dir",
488                                                 Output:  "fake stdout/stderr dir",
489                                                 Error:   "fake error",
490                                         },
491                                 },
492                         },
493                         fmt.Errorf("\"false\" failed: fake error"),
494                 },
495         }
496
497         for _, tc := range tests {
498                 t.Run(tc.name, func(t *testing.T) {
499                         s, res := prepareSync(tc.req, &testRunner{
500                                 t:         t,
501                                 expCmds:   tc.expCmds,
502                                 resStdout: tc.stdout,
503                                 resStderr: tc.stderr,
504                                 resError:  tc.errors,
505                         })
506                         s.triggers = tc.triggers
507
508                         err := s.syncCommands()
509                         testutil.AssertErrorEqual(t, "err", err, tc.expErr)
510                         dbg := res.Wait()
511
512                         testutil.AssertEqual(t, "resp", s.resp, tc.expResp)
513                         testutil.AssertEqual(t, "dbg", dbg, tc.expDbg)
514                 })
515         }
516 }