]> ruderich.org/simon Gitweb - safcm/safcm.git/blob - cmd/safcm/sync_sync_test.go
Use SPDX license identifiers
[safcm/safcm.git] / cmd / safcm / sync_sync_test.go
1 // SPDX-License-Identifier: GPL-3.0-or-later
2 // Copyright (C) 2021-2024  Simon Ruderich
3
4 package main
5
6 import (
7         "fmt"
8         "io"
9         "io/fs"
10         "log"
11         "os"
12         "path/filepath"
13         "testing"
14
15         "ruderich.org/simon/safcm"
16         "ruderich.org/simon/safcm/testutil"
17 )
18
19 func TestHostSyncReq(t *testing.T) {
20         cwd, err := os.Getwd()
21         if err != nil {
22                 t.Fatal(err)
23         }
24         defer os.Chdir(cwd) //nolint:errcheck
25
26         tests := []struct {
27                 name      string
28                 project   string
29                 host      string
30                 detected  []string
31                 exp       safcm.MsgSyncReq
32                 expEvents []string
33                 expErr    error
34         }{
35
36                 // NOTE: Also update MsgSyncReq in safcm-remote test cases
37                 // changing the MsgSyncReq struct!
38
39                 {
40                         "project: host1",
41                         "project",
42                         "host1.example.org",
43                         nil,
44                         safcm.MsgSyncReq{
45                                 Groups: []string{
46                                         "all",
47                                         "group",
48                                         "group3",
49                                         "remove",
50                                         "host1.example.org",
51                                 },
52                                 Files: map[string]*safcm.File{
53                                         "/": {
54                                                 OrigGroup: "group",
55                                                 Path:      "/",
56                                                 Mode:      fs.ModeDir | 0755 | fs.ModeSetgid,
57                                                 Uid:       -1,
58                                                 Gid:       -1,
59                                                 TriggerCommands: []string{
60                                                         "touch /.update",
61                                                 },
62                                         },
63                                         "/etc": {
64                                                 OrigGroup: "group",
65                                                 Path:      "/etc",
66                                                 Mode:      fs.ModeDir | 0755,
67                                                 Uid:       -1,
68                                                 Gid:       -1,
69                                         },
70                                         "/etc/.hidden": {
71                                                 OrigGroup: "group",
72                                                 Path:      "/etc/.hidden",
73                                                 Mode:      0100 | fs.ModeSetuid | fs.ModeSetgid | fs.ModeSticky,
74                                                 Uid:       -1,
75                                                 Gid:       -1,
76                                                 Data:      []byte("..."),
77                                         },
78                                         "/etc/motd": {
79                                                 OrigGroup: "group",
80                                                 Path:      "/etc/motd",
81                                                 Mode:      0644,
82                                                 Uid:       -1,
83                                                 Gid:       -1,
84                                                 Data:      []byte("Welcome to Host ONE\n\n\n\n\n\nall\n\n\nhost1.example.org\n\n\n\n"),
85                                         },
86                                         "/etc/rc.local": {
87                                                 OrigGroup: "group",
88                                                 Path:      "/etc/rc.local",
89                                                 Mode:      0700,
90                                                 Uid:       -1,
91                                                 Gid:       -1,
92                                                 Data:      []byte("#!/bin/sh\n"),
93                                                 TriggerCommands: []string{
94                                                         "/etc/rc.local",
95                                                 },
96                                         },
97                                         "/etc/resolv.conf": {
98                                                 OrigGroup: "group",
99                                                 Path:      "/etc/resolv.conf",
100                                                 Mode:      0641,
101                                                 User:      "user",
102                                                 Uid:       -1,
103                                                 Group:     "group",
104                                                 Gid:       -1,
105                                                 Data:      []byte("nameserver ::1\n"),
106                                                 TriggerCommands: []string{
107                                                         "echo resolv.conf updated",
108                                                 },
109                                         },
110                                         "/etc/test": {
111                                                 OrigGroup: "group",
112                                                 Path:      "/etc/test",
113                                                 Mode:      os.ModeSymlink | 0777,
114                                                 Uid:       -1,
115                                                 Gid:       -1,
116                                                 Data:      []byte("doesnt-exist"),
117                                         },
118                                 },
119                                 Packages: []string{
120                                         "unbound",
121                                         "unbound-anchor",
122                                 },
123                                 Services: []string{
124                                         "unbound",
125                                 },
126                                 Commands: []*safcm.Command{
127                                         {
128                                                 OrigGroup: "group",
129                                                 Cmd:       "echo command one",
130                                         },
131                                         {
132                                                 OrigGroup: "group",
133                                                 Cmd:       "echo -n command two",
134                                         },
135                                 },
136                         },
137                         []string{
138                                 "3 false host groups: all group group3 host1.example.org remove",
139                                 "3 false host group priorities (descending): host1.example.org",
140                         },
141                         nil,
142                 },
143
144                 {
145                         "conflict: file",
146                         "project-conflict-file",
147                         "host1.example.org",
148                         nil,
149                         safcm.MsgSyncReq{},
150                         []string{
151                                 "3 false host groups: all dns host1.example.org",
152                                 "3 false host group priorities (descending): host1.example.org",
153                         },
154                         fmt.Errorf("groups dns and all both provide \"/etc/resolv.conf\"\nUse 'group_priority' in config.yaml to declare preference"),
155                 },
156                 {
157                         "conflict: file from detected group",
158                         "project-conflict-file",
159                         "host2.example.org",
160                         []string{
161                                 "detected_other",
162                         },
163                         safcm.MsgSyncReq{},
164                         []string{
165                                 "3 false host groups: all detected_other host2.example.org other",
166                                 "3 false host group priorities (descending): host2.example.org",
167                         },
168                         fmt.Errorf("groups other and all both provide \"/etc/resolv.conf\"\nUse 'group_priority' in config.yaml to declare preference"),
169                 },
170
171                 {
172                         "conflict: dir",
173                         "project-conflict-dir",
174                         "host1.example.org",
175                         nil,
176                         safcm.MsgSyncReq{},
177                         []string{
178                                 "3 false host groups: all dns host1.example.org",
179                                 "3 false host group priorities (descending): host1.example.org",
180                         },
181                         fmt.Errorf("groups dns and all both provide \"/etc\"\nUse 'group_priority' in config.yaml to declare preference"),
182                 },
183                 {
184                         "conflict: dir from detected group",
185                         "project-conflict-dir",
186                         "host2.example.org",
187                         []string{
188                                 "detected_other",
189                         },
190                         safcm.MsgSyncReq{},
191                         []string{
192                                 "3 false host groups: all detected_other host2.example.org other",
193                                 "3 false host group priorities (descending): host2.example.org",
194                         },
195                         fmt.Errorf("groups other and all both provide \"/etc\"\nUse 'group_priority' in config.yaml to declare preference"),
196                 },
197
198                 {
199                         "group: cycle",
200                         "project-group-cycle",
201                         "host1.example.org",
202                         nil,
203                         safcm.MsgSyncReq{},
204                         nil,
205                         fmt.Errorf("groups.yaml: cycle while expanding group \"group-b\""),
206                 },
207
208                 {
209                         "group_priority",
210                         "project-group_priority",
211                         "host1.example.org",
212                         nil,
213                         safcm.MsgSyncReq{
214                                 Groups: []string{"all", "group-b", "group-a", "host1.example.org"},
215                                 Files: map[string]*safcm.File{
216                                         "/": {
217                                                 OrigGroup: "host1.example.org",
218                                                 Path:      "/",
219                                                 Mode:      fs.ModeDir | 0755,
220                                                 Uid:       -1,
221                                                 Gid:       -1,
222                                         },
223                                         "/etc": {
224                                                 OrigGroup: "host1.example.org",
225                                                 Path:      "/etc",
226                                                 Mode:      fs.ModeDir | 0755,
227                                                 Uid:       -1,
228                                                 Gid:       -1,
229                                         },
230                                         "/etc/dir-to-file": {
231                                                 OrigGroup: "group-a",
232                                                 Path:      "/etc/dir-to-file",
233                                                 Mode:      0644,
234                                                 Uid:       -1,
235                                                 Gid:       -1,
236                                                 Data:      []byte("dir-to-file: from group-a\n"),
237                                         },
238                                         "/etc/dir-to-filex": {
239                                                 OrigGroup: "group-b",
240                                                 Path:      "/etc/dir-to-filex",
241                                                 Mode:      0644,
242                                                 Uid:       -1,
243                                                 Gid:       -1,
244                                                 Data:      []byte("dir-to-filex\n"),
245                                         },
246                                         "/etc/dir-to-link": {
247                                                 OrigGroup: "group-a",
248                                                 Path:      "/etc/dir-to-link",
249                                                 Mode:      fs.ModeSymlink | 0777,
250                                                 Uid:       -1,
251                                                 Gid:       -1,
252                                                 Data:      []byte("target"),
253                                         },
254                                         "/etc/dir-to-linkx": {
255                                                 OrigGroup: "group-b",
256                                                 Path:      "/etc/dir-to-linkx",
257                                                 Mode:      0644,
258                                                 Uid:       -1,
259                                                 Gid:       -1,
260                                                 Data:      []byte("dir-to-linkx\n"),
261                                         },
262                                         "/etc/file-to-dir": {
263                                                 OrigGroup: "group-a",
264                                                 Path:      "/etc/file-to-dir",
265                                                 Mode:      fs.ModeDir | 0755,
266                                                 Uid:       -1,
267                                                 Gid:       -1,
268                                         },
269                                         "/etc/file-to-dir/file": {
270                                                 OrigGroup: "group-a",
271                                                 Path:      "/etc/file-to-dir/file",
272                                                 Mode:      0644,
273                                                 Uid:       -1,
274                                                 Gid:       -1,
275                                                 Data:      []byte("file: from group-a\n"),
276                                         },
277                                         "/etc/file-to-dir/dir": {
278                                                 OrigGroup: "group-a",
279                                                 Path:      "/etc/file-to-dir/dir",
280                                                 Mode:      fs.ModeDir | 0755,
281                                                 Uid:       -1,
282                                                 Gid:       -1,
283                                         },
284                                         "/etc/file-to-dir/dir/file2": {
285                                                 OrigGroup: "group-a",
286                                                 Path:      "/etc/file-to-dir/dir/file2",
287                                                 Mode:      0644,
288                                                 Uid:       -1,
289                                                 Gid:       -1,
290                                                 Data:      []byte("file2: from group-a\n"),
291                                         },
292                                         "/etc/motd": {
293                                                 OrigGroup: "host1.example.org",
294                                                 Path:      "/etc/motd",
295                                                 Mode:      0644,
296                                                 Uid:       -1,
297                                                 Gid:       -1,
298                                                 Data:      []byte("motd: from host1\n"),
299                                         },
300                                 },
301                         },
302                         []string{
303                                 "3 false host groups: all group-a group-b host1.example.org",
304                                 "3 false host group priorities (descending): host1.example.org group-a group-b all",
305                                 `4 false files: "/etc": group group-a overwrites triggers from group group-b`,
306                                 `4 false files: "/etc": group host1.example.org overwrites triggers from group group-a`,
307                         },
308                         nil,
309                 },
310
311                 {
312                         "group_priority (single group)",
313                         "project-group_priority-single",
314                         "host1.example.org",
315                         nil,
316                         safcm.MsgSyncReq{
317                                 Groups: []string{"all", "group-b", "group-a", "host1.example.org"},
318                                 Files: map[string]*safcm.File{
319                                         "/": {
320                                                 OrigGroup: "group-a",
321                                                 Path:      "/",
322                                                 Mode:      fs.ModeDir | 0755,
323                                                 Uid:       -1,
324                                                 Gid:       -1,
325                                         },
326                                         "/file.txt": {
327                                                 OrigGroup: "group-a",
328                                                 Path:      "/file.txt",
329                                                 Mode:      0644,
330                                                 Uid:       -1,
331                                                 Gid:       -1,
332                                                 Data:      []byte("file.txt: from group-a\n"),
333                                         },
334                                 },
335                         },
336                         []string{
337                                 "3 false host groups: all group-a group-b host1.example.org",
338                                 "3 false host group priorities (descending): host1.example.org group-a",
339                         },
340                         nil,
341                 },
342         }
343
344         for _, tc := range tests {
345                 t.Run(tc.name, func(t *testing.T) {
346                         err = os.Chdir(filepath.Join(cwd,
347                                 "testdata", tc.project))
348                         if err != nil {
349                                 t.Fatal(err)
350                         }
351
352                         // `safcm fixperms` in case user has strict umask
353                         log.SetOutput(io.Discard)
354                         err := MainFixperms()
355                         if err != nil {
356                                 t.Fatal(err)
357                         }
358                         log.SetOutput(os.Stderr)
359
360                         cfg, allHosts, allGroups, err := LoadBaseFiles()
361                         if err != nil {
362                                 t.Fatal(err)
363                         }
364
365                         var events []string
366                         s := &Sync{
367                                 host:      allHosts.Map[tc.host],
368                                 config:    cfg,
369                                 allHosts:  allHosts,
370                                 allGroups: allGroups,
371                                 logFunc: func(level safcm.LogLevel, escaped bool, msg string) {
372                                         events = append(events,
373                                                 fmt.Sprintf("%d %v %s", level, escaped, msg))
374                                 },
375                         }
376
377                         res, err := s.hostSyncReq(tc.detected)
378                         testutil.AssertEqual(t, "res", res, tc.exp)
379                         testutil.AssertErrorEqual(t, "err", err, tc.expErr)
380                         testutil.AssertEqual(t, "events",
381                                 events, tc.expEvents)
382                 })
383         }
384 }