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