X-Git-Url: https://ruderich.org/simon/gitweb/?a=blobdiff_plain;f=cmd%2Fsafcm-remote%2Fsync%2Fservices_systemd_test.go;fp=cmd%2Fsafcm-remote%2Fsync%2Fservices_systemd_test.go;h=8ba0bfabed44a66e0f4ce787ce658b10b5d2649a;hb=f2f2bc47e8729548f3c10117f7f008b547c4afc5;hp=0000000000000000000000000000000000000000;hpb=dc0d431a778a50e6732b9eb91384a07a207b772d;p=safcm%2Fsafcm.git diff --git a/cmd/safcm-remote/sync/services_systemd_test.go b/cmd/safcm-remote/sync/services_systemd_test.go new file mode 100644 index 0000000..8ba0bfa --- /dev/null +++ b/cmd/safcm-remote/sync/services_systemd_test.go @@ -0,0 +1,536 @@ +// Copyright (C) 2021 Simon Ruderich +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package sync + +import ( + "bytes" + "fmt" + "os/exec" + "reflect" + "testing" + + "github.com/google/go-cmp/cmp" + + "ruderich.org/simon/safcm" +) + +func TestSyncServicesSystemd(t *testing.T) { + tests := []struct { + name string + req safcm.MsgSyncReq + stdout [][]byte + stderr [][]byte + errors []error + expCmds []*exec.Cmd + expDbg []string + expResp safcm.MsgSyncResp + expErr error + }{ + + // NOTE: Also update MsgSyncResp in safcm test cases when + // changing anything here! + + { + "no service change necessary", + safcm.MsgSyncReq{ + Services: []string{ + "service-one", + "service-two", + }, + }, + [][]byte{ + []byte(`ActiveState=active +UnitFileState=enabled +LoadError= + +ActiveState=active +UnitFileState=enabled +LoadError= +`), + }, + [][]byte{nil}, + []error{nil}, + []*exec.Cmd{&exec.Cmd{ + Path: "/bin/systemctl", + Args: []string{ + "/bin/systemctl", + "show", + "--property=ActiveState,UnitFileState,LoadError", + "--", + "service-one", + "service-two", + }, + Stdout: &bytes.Buffer{}, + Stderr: &bytes.Buffer{}, + }}, + []string{ + "4: sync remote: services: detected systemd", + "4: sync remote: services: checking service-one service-two", + `4: sync remote: services: running "/bin/systemctl" "show" "--property=ActiveState,UnitFileState,LoadError" "--" "service-one" "service-two"`, + `5: sync remote: services: command stdout: +ActiveState=active +UnitFileState=enabled +LoadError= + +ActiveState=active +UnitFileState=enabled +LoadError= +`, + }, + safcm.MsgSyncResp{}, + nil, + }, + + { + "no service change necessary (older systemd)", + safcm.MsgSyncReq{ + Services: []string{ + "service-one", + "service-two", + }, + }, + [][]byte{ + []byte(`ActiveState=active +UnitFileState=enabled +LoadError= "" + +ActiveState=active +UnitFileState=enabled +LoadError= "" +`), + }, + [][]byte{nil}, + []error{nil}, + []*exec.Cmd{&exec.Cmd{ + Path: "/bin/systemctl", + Args: []string{ + "/bin/systemctl", + "show", + "--property=ActiveState,UnitFileState,LoadError", + "--", + "service-one", + "service-two", + }, + Stdout: &bytes.Buffer{}, + Stderr: &bytes.Buffer{}, + }}, + []string{ + "4: sync remote: services: detected systemd", + "4: sync remote: services: checking service-one service-two", + `4: sync remote: services: running "/bin/systemctl" "show" "--property=ActiveState,UnitFileState,LoadError" "--" "service-one" "service-two"`, + `5: sync remote: services: command stdout: +ActiveState=active +UnitFileState=enabled +LoadError= "" + +ActiveState=active +UnitFileState=enabled +LoadError= "" +`, + }, + safcm.MsgSyncResp{}, + nil, + }, + + { + "invalid service", + safcm.MsgSyncReq{ + Services: []string{ + "service-does-not-exist", + "service-two", + }, + }, + [][]byte{ + []byte(`ActiveState=inactive +UnitFileState= +LoadError=org.freedesktop.systemd1.NoSuchUnit "Unit service-does-not-exist.service not found." + +ActiveState=active +UnitFileState=enabled +LoadError= +`), + }, + [][]byte{nil}, + []error{nil}, + []*exec.Cmd{&exec.Cmd{ + Path: "/bin/systemctl", + Args: []string{ + "/bin/systemctl", + "show", + "--property=ActiveState,UnitFileState,LoadError", + "--", + "service-does-not-exist", + "service-two", + }, + Stdout: &bytes.Buffer{}, + Stderr: &bytes.Buffer{}, + }}, + []string{ + "4: sync remote: services: detected systemd", + "4: sync remote: services: checking service-does-not-exist service-two", + `4: sync remote: services: running "/bin/systemctl" "show" "--property=ActiveState,UnitFileState,LoadError" "--" "service-does-not-exist" "service-two"`, + `5: sync remote: services: command stdout: +ActiveState=inactive +UnitFileState= +LoadError=org.freedesktop.systemd1.NoSuchUnit "Unit service-does-not-exist.service not found." + +ActiveState=active +UnitFileState=enabled +LoadError= +`, + }, + safcm.MsgSyncResp{}, + fmt.Errorf("systemd unit \"service-does-not-exist\" not found"), + }, + + { + "start/enable service", + safcm.MsgSyncReq{ + Services: []string{ + "service-one", + "service-two", + "service-three", + }, + }, + [][]byte{ + []byte(`ActiveState=inactive +UnitFileState=enabled +LoadError= + +ActiveState=active +UnitFileState=disabled +LoadError= + +ActiveState=failed +UnitFileState=disabled +LoadError= +`), + nil, + nil, + nil, + }, + [][]byte{ + nil, + nil, + nil, + []byte(`fake stderr`), + }, + []error{nil, nil, nil, nil}, + []*exec.Cmd{&exec.Cmd{ + Path: "/bin/systemctl", + Args: []string{ + "/bin/systemctl", + "show", + "--property=ActiveState,UnitFileState,LoadError", + "--", + "service-one", + "service-two", + "service-three", + }, + Stdout: &bytes.Buffer{}, + Stderr: &bytes.Buffer{}, + }, &exec.Cmd{ + Path: "/bin/systemctl", + Args: []string{ + "/bin/systemctl", + "daemon-reload", + }, + Stdout: &bytes.Buffer{}, + Stderr: &bytes.Buffer{}, + }, &exec.Cmd{ + Path: "/bin/systemctl", + Args: []string{ + "/bin/systemctl", + "start", + "--", + "service-one", + "service-three", + }, + Stdout: &bytes.Buffer{}, + Stderr: &bytes.Buffer{}, + }, &exec.Cmd{ + Path: "/bin/systemctl", + Args: []string{ + "/bin/systemctl", + "enable", + "--", + "service-two", + "service-three", + }, + Stdout: &bytes.Buffer{}, + Stderr: &bytes.Buffer{}, + }}, + []string{ + "4: sync remote: services: detected systemd", + "4: sync remote: services: checking service-one service-two service-three", + `4: sync remote: services: running "/bin/systemctl" "show" "--property=ActiveState,UnitFileState,LoadError" "--" "service-one" "service-two" "service-three"`, + `5: sync remote: services: command stdout: +ActiveState=inactive +UnitFileState=enabled +LoadError= + +ActiveState=active +UnitFileState=disabled +LoadError= + +ActiveState=failed +UnitFileState=disabled +LoadError= +`, + `4: sync remote: services: running "/bin/systemctl" "daemon-reload"`, + "3: sync remote: services: starting service-one service-three", + `4: sync remote: services: running "/bin/systemctl" "start" "--" "service-one" "service-three"`, + "3: sync remote: services: enabling service-two service-three", + `4: sync remote: services: running "/bin/systemctl" "enable" "--" "service-two" "service-three"`, + "5: sync remote: services: command stderr:\nfake stderr", + }, + safcm.MsgSyncResp{ + ServiceChanges: []safcm.ServiceChange{ + { + Name: "service-one", + Started: true, + }, + { + Name: "service-two", + Enabled: true, + }, + { + Name: "service-three", + Started: true, + Enabled: true, + }, + }, + }, + nil, + }, + + { + "start/enable service (dry-run)", + safcm.MsgSyncReq{ + DryRun: true, + Services: []string{ + "service-one", + "service-two", + "service-three", + }, + }, + [][]byte{ + []byte(`ActiveState=inactive +UnitFileState=enabled +LoadError= + +ActiveState=active +UnitFileState=disabled +LoadError= + +ActiveState=failed +UnitFileState=disabled +LoadError= +`), + }, + [][]byte{nil}, + []error{nil}, + []*exec.Cmd{&exec.Cmd{ + Path: "/bin/systemctl", + Args: []string{ + "/bin/systemctl", + "show", + "--property=ActiveState,UnitFileState,LoadError", + "--", + "service-one", + "service-two", + "service-three", + }, + Stdout: &bytes.Buffer{}, + Stderr: &bytes.Buffer{}, + }}, + []string{ + "4: sync remote: services: detected systemd", + "4: sync remote: services: checking service-one service-two service-three", + `4: sync remote: services: running "/bin/systemctl" "show" "--property=ActiveState,UnitFileState,LoadError" "--" "service-one" "service-two" "service-three"`, + `5: sync remote: services: command stdout: +ActiveState=inactive +UnitFileState=enabled +LoadError= + +ActiveState=active +UnitFileState=disabled +LoadError= + +ActiveState=failed +UnitFileState=disabled +LoadError= +`, + }, + safcm.MsgSyncResp{ + ServiceChanges: []safcm.ServiceChange{ + { + Name: "service-one", + Started: true, + }, + { + Name: "service-two", + Enabled: true, + }, + { + Name: "service-three", + Started: true, + Enabled: true, + }, + }, + }, + nil, + }, + + { + "start/enable service (error)", + safcm.MsgSyncReq{ + Services: []string{ + "service-one", + "service-two", + "service-three", + }, + }, + [][]byte{ + []byte(`ActiveState=inactive +UnitFileState=enabled +LoadError= + +ActiveState=active +UnitFileState=disabled +LoadError= + +ActiveState=failed +UnitFileState=disabled +LoadError= +`), + nil, + nil, + }, + [][]byte{ + nil, + nil, + []byte(`fake stderr`), + }, + []error{ + nil, + nil, + fmt.Errorf("fake error"), + }, + []*exec.Cmd{&exec.Cmd{ + Path: "/bin/systemctl", + Args: []string{ + "/bin/systemctl", + "show", + "--property=ActiveState,UnitFileState,LoadError", + "--", + "service-one", + "service-two", + "service-three", + }, + Stdout: &bytes.Buffer{}, + Stderr: &bytes.Buffer{}, + }, &exec.Cmd{ + Path: "/bin/systemctl", + Args: []string{ + "/bin/systemctl", + "daemon-reload", + }, + Stdout: &bytes.Buffer{}, + Stderr: &bytes.Buffer{}, + }, &exec.Cmd{ + Path: "/bin/systemctl", + Args: []string{ + "/bin/systemctl", + "start", + "--", + "service-one", + "service-three", + }, + Stdout: &bytes.Buffer{}, + Stderr: &bytes.Buffer{}, + }}, + []string{ + "4: sync remote: services: detected systemd", + "4: sync remote: services: checking service-one service-two service-three", + `4: sync remote: services: running "/bin/systemctl" "show" "--property=ActiveState,UnitFileState,LoadError" "--" "service-one" "service-two" "service-three"`, + `5: sync remote: services: command stdout: +ActiveState=inactive +UnitFileState=enabled +LoadError= + +ActiveState=active +UnitFileState=disabled +LoadError= + +ActiveState=failed +UnitFileState=disabled +LoadError= +`, + `4: sync remote: services: running "/bin/systemctl" "daemon-reload"`, + "3: sync remote: services: starting service-one service-three", + `4: sync remote: services: running "/bin/systemctl" "start" "--" "service-one" "service-three"`, + "5: sync remote: services: command stderr:\nfake stderr", + }, + safcm.MsgSyncResp{ + ServiceChanges: []safcm.ServiceChange{ + { + Name: "service-one", + Started: true, + }, + { + Name: "service-two", + Enabled: true, + }, + { + Name: "service-three", + Started: true, + Enabled: true, + }, + }, + }, + fmt.Errorf(`"/bin/systemctl" "start" "--" "service-one" "service-three" failed: fake error; stdout: "", stderr: "fake stderr"`), + }, + } + + for _, tc := range tests { + s, res := prepareSync(tc.req, &testRunner{ + t: t, + name: tc.name, + expCmds: tc.expCmds, + resStdout: tc.stdout, + resStderr: tc.stderr, + resError: tc.errors, + }) + + err := s.syncServicesSystemd() + // Ugly but the simplest way to compare errors (including nil) + if fmt.Sprintf("%s", err) != fmt.Sprintf("%s", tc.expErr) { + t.Errorf("%s: err = %#v, want %#v", + tc.name, err, tc.expErr) + } + dbg := res.Wait() + + if !reflect.DeepEqual(tc.expResp, s.resp) { + t.Errorf("%s: resp: %s", tc.name, + cmp.Diff(tc.expResp, s.resp)) + } + if !reflect.DeepEqual(tc.expDbg, dbg) { + t.Errorf("%s: dbg: %s", tc.name, + cmp.Diff(tc.expDbg, dbg)) + } + } +}