1 // Copyright (C) 2021 Simon Ruderich
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.
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.
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/>.
27 "ruderich.org/simon/safcm"
28 "ruderich.org/simon/safcm/testutil"
31 func TestHostSyncReq(t *testing.T) {
32 cwd, err := os.Getwd()
48 // NOTE: Also update MsgSyncReq in safcm-remote test cases
49 // changing the MsgSyncReq struct!
64 Files: map[string]*safcm.File{
65 "/": &safcm.File{Path: "/",
67 Mode: fs.ModeDir | 0755 | fs.ModeSetgid,
70 TriggerCommands: []string{
77 Mode: fs.ModeDir | 0755,
81 "/etc/.hidden": &safcm.File{
84 Mode: 0100 | fs.ModeSetuid | fs.ModeSetgid | fs.ModeSticky,
89 "/etc/motd": &safcm.File{
95 Data: []byte("Welcome to Host ONE\n\n\n\n\n\nall\n\n\nhost1.example.org\n\n\n\n"),
97 "/etc/rc.local": &safcm.File{
99 Path: "/etc/rc.local",
103 Data: []byte("#!/bin/sh\n"),
104 TriggerCommands: []string{
108 "/etc/resolv.conf": &safcm.File{
110 Path: "/etc/resolv.conf",
116 Data: []byte("nameserver ::1\n"),
117 TriggerCommands: []string{
118 "echo resolv.conf updated",
121 "/etc/test": &safcm.File{
124 Mode: os.ModeSymlink | 0777,
127 Data: []byte("doesnt-exist"),
137 Commands: []*safcm.Command{
140 Cmd: "echo command one",
144 Cmd: "echo -n command two",
149 "3 false host groups: all group group3 host1.example.org remove",
150 "3 false host group priorities (descending): host1.example.org",
157 "project-conflict-file",
162 "3 false host groups: all dns host1.example.org",
163 "3 false host group priorities (descending): host1.example.org",
165 fmt.Errorf("groups dns and all both provide \"/etc/resolv.conf\"\nUse 'group_priority' in config.yaml to declare preference"),
168 "conflict: file from detected group",
169 "project-conflict-file",
176 "3 false host groups: all detected_other host2.example.org other",
177 "3 false host group priorities (descending): host2.example.org",
179 fmt.Errorf("groups other and all both provide \"/etc/resolv.conf\"\nUse 'group_priority' in config.yaml to declare preference"),
184 "project-conflict-dir",
189 "3 false host groups: all dns host1.example.org",
190 "3 false host group priorities (descending): host1.example.org",
192 fmt.Errorf("groups dns and all both provide \"/etc\"\nUse 'group_priority' in config.yaml to declare preference"),
195 "conflict: dir from detected group",
196 "project-conflict-dir",
203 "3 false host groups: all detected_other host2.example.org other",
204 "3 false host group priorities (descending): host2.example.org",
206 fmt.Errorf("groups other and all both provide \"/etc\"\nUse 'group_priority' in config.yaml to declare preference"),
211 "project-group-cycle",
216 fmt.Errorf("groups.yaml: cycle while expanding group \"group-b\""),
221 "project-group_priority",
225 Groups: []string{"all", "group-b", "group-a", "host1.example.org"},
226 Files: map[string]*safcm.File{
229 Mode: fs.ModeDir | 0755,
232 OrigGroup: "host1.example.org",
236 Mode: fs.ModeDir | 0755,
239 OrigGroup: "host1.example.org",
241 "/etc/dir-to-file": {
242 Path: "/etc/dir-to-file",
246 Data: []byte("dir-to-file: from group-a\n"),
247 OrigGroup: "group-a",
249 "/etc/dir-to-filex": {
250 OrigGroup: "group-b",
251 Path: "/etc/dir-to-filex",
255 Data: []byte("dir-to-filex\n"),
257 "/etc/dir-to-link": {
258 Path: "/etc/dir-to-link",
259 Mode: fs.ModeSymlink | 0777,
262 Data: []byte("target"),
263 OrigGroup: "group-a",
265 "/etc/dir-to-linkx": {
266 OrigGroup: "group-b",
267 Path: "/etc/dir-to-linkx",
271 Data: []byte("dir-to-linkx\n"),
273 "/etc/file-to-dir": {
274 Path: "/etc/file-to-dir",
275 Mode: fs.ModeDir | 0755,
278 OrigGroup: "group-a",
280 "/etc/file-to-dir/file": {
281 Path: "/etc/file-to-dir/file",
285 Data: []byte("file: from group-a\n"),
286 OrigGroup: "group-a",
288 "/etc/file-to-dir/dir": {
289 Path: "/etc/file-to-dir/dir",
290 Mode: fs.ModeDir | 0755,
293 OrigGroup: "group-a",
295 "/etc/file-to-dir/dir/file2": {
296 Path: "/etc/file-to-dir/dir/file2",
300 Data: []byte("file2: from group-a\n"),
301 OrigGroup: "group-a",
308 Data: []byte("motd: from host1\n"),
309 OrigGroup: "host1.example.org",
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`,
323 "group_priority (single group)",
324 "project-group_priority-single",
328 Groups: []string{"all", "group-b", "group-a", "host1.example.org"},
329 Files: map[string]*safcm.File{
332 Mode: fs.ModeDir | 0755,
335 OrigGroup: "group-a",
342 Data: []byte("file.txt: from group-a\n"),
343 OrigGroup: "group-a",
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",
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))
363 // `safcm fixperms` in case user has strict umask
364 log.SetOutput(io.Discard)
365 err := MainFixperms()
369 log.SetOutput(os.Stderr)
371 cfg, allHosts, allGroups, err := LoadBaseFiles()
378 host: allHosts.Map[tc.host],
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))
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)