gitee.com/mysnapcore/mysnapd@v0.1.0/interfaces/systemd/backend_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2021 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package systemd_test 21 22 import ( 23 "os" 24 "path/filepath" 25 26 . "gopkg.in/check.v1" 27 28 "gitee.com/mysnapcore/mysnapd/dirs" 29 "gitee.com/mysnapcore/mysnapd/interfaces" 30 "gitee.com/mysnapcore/mysnapd/interfaces/ifacetest" 31 "gitee.com/mysnapcore/mysnapd/interfaces/systemd" 32 "gitee.com/mysnapcore/mysnapd/snap" 33 sysd "gitee.com/mysnapcore/mysnapd/systemd" 34 "gitee.com/mysnapcore/mysnapd/testutil" 35 ) 36 37 type backendSuite struct { 38 ifacetest.BackendSuite 39 40 systemctlArgs [][]string 41 systemctlRestorer func() 42 } 43 44 var _ = Suite(&backendSuite{}) 45 46 var testedConfinementOpts = []interfaces.ConfinementOptions{ 47 {}, 48 {DevMode: true}, 49 {JailMode: true}, 50 {Classic: true}, 51 } 52 53 func (s *backendSuite) SetUpTest(c *C) { 54 s.Backend = &systemd.Backend{} 55 s.BackendSuite.SetUpTest(c) 56 c.Assert(s.Repo.AddBackend(s.Backend), IsNil) 57 s.systemctlRestorer = sysd.MockSystemctl(func(args ...string) ([]byte, error) { 58 s.systemctlArgs = append(s.systemctlArgs, append([]string{"systemctl"}, args...)) 59 return []byte("ActiveState=inactive"), nil 60 }) 61 s.systemctlArgs = nil 62 } 63 64 func (s *backendSuite) TearDownTest(c *C) { 65 s.systemctlRestorer() 66 s.BackendSuite.TearDownTest(c) 67 } 68 69 func (s *backendSuite) TestName(c *C) { 70 c.Check(s.Backend.Name(), Equals, interfaces.SecuritySystemd) 71 } 72 73 func (s *backendSuite) TestInstallingSnapWritesStartsServices(c *C) { 74 var sysdLog [][]string 75 76 r := sysd.MockSystemctl(func(cmd ...string) ([]byte, error) { 77 sysdLog = append(sysdLog, cmd) 78 if cmd[0] == "show" { 79 return []byte("ActiveState=inactive\n"), nil 80 } 81 return []byte{}, nil 82 }) 83 defer r() 84 85 s.Iface.SystemdPermanentSlotCallback = func(spec *systemd.Specification, slot *snap.SlotInfo) error { 86 return spec.AddService("foo", &systemd.Service{ExecStart: "/bin/true"}) 87 } 88 s.InstallSnap(c, interfaces.ConfinementOptions{}, "", ifacetest.SambaYamlV1, 1) 89 service := filepath.Join(dirs.SnapServicesDir, "snap.samba.interface.foo.service") 90 // the service file was created 91 _, err := os.Stat(service) 92 c.Check(err, IsNil) 93 // the service was also started (whee) 94 c.Check(sysdLog, DeepEquals, [][]string{ 95 // units added removed 96 {"daemon-reload"}, 97 {"--no-reload", "enable", "snap.samba.interface.foo.service"}, 98 {"stop", "snap.samba.interface.foo.service"}, 99 {"show", "--property=ActiveState", "snap.samba.interface.foo.service"}, 100 {"start", "snap.samba.interface.foo.service"}, 101 // update systemd's enabled/disabled state 102 {"daemon-reload"}, 103 }) 104 } 105 106 func (s *backendSuite) TestRemovingSnapRemovesAndStopsServices(c *C) { 107 s.Iface.SystemdPermanentSlotCallback = func(spec *systemd.Specification, slot *snap.SlotInfo) error { 108 return spec.AddService("foo", &systemd.Service{ExecStart: "/bin/true"}) 109 } 110 for _, opts := range testedConfinementOpts { 111 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 1) 112 s.systemctlArgs = nil 113 s.RemoveSnap(c, snapInfo) 114 service := filepath.Join(dirs.SnapServicesDir, "snap.samba.interface.foo.service") 115 // the service file was removed 116 _, err := os.Stat(service) 117 c.Check(os.IsNotExist(err), Equals, true) 118 // the service was stopped 119 c.Check(s.systemctlArgs, DeepEquals, [][]string{ 120 {"systemctl", "--no-reload", "disable", "snap.samba.interface.foo.service"}, 121 {"systemctl", "stop", "snap.samba.interface.foo.service"}, 122 {"systemctl", "show", "--property=ActiveState", "snap.samba.interface.foo.service"}, 123 {"systemctl", "daemon-reload"}, 124 }) 125 } 126 } 127 128 func (s *backendSuite) TestSettingInstallManyUpdateSecurityWithFewerServices(c *C) { 129 s.Iface.SystemdPermanentSlotCallback = func(spec *systemd.Specification, slot *snap.SlotInfo) error { 130 err := spec.AddService("foo", &systemd.Service{ExecStart: "/bin/true"}) 131 if err != nil { 132 return err 133 } 134 return spec.AddService("bar", &systemd.Service{ExecStart: "/bin/false"}) 135 } 136 serviceFoo := filepath.Join(dirs.SnapServicesDir, "snap.samba.interface.foo.service") 137 serviceBar := filepath.Join(dirs.SnapServicesDir, "snap.samba.interface.bar.service") 138 // verify known test state 139 c.Check(serviceFoo, testutil.FileAbsent) 140 c.Check(serviceBar, testutil.FileAbsent) 141 snapInfo := s.InstallSnap(c, interfaces.ConfinementOptions{}, "", ifacetest.SambaYamlV1, 1) 142 // the services were created 143 c.Check(serviceFoo, testutil.FilePresent) 144 c.Check(serviceBar, testutil.FilePresent) 145 c.Check(s.systemctlArgs, DeepEquals, [][]string{ 146 {"systemctl", "daemon-reload"}, 147 // units were added 148 {"systemctl", "--no-reload", "enable", "snap.samba.interface.bar.service", "snap.samba.interface.foo.service"}, 149 {"systemctl", "stop", "snap.samba.interface.bar.service", "snap.samba.interface.foo.service"}, 150 {"systemctl", "show", "--property=ActiveState", "snap.samba.interface.bar.service"}, 151 {"systemctl", "show", "--property=ActiveState", "snap.samba.interface.foo.service"}, 152 {"systemctl", "start", "snap.samba.interface.bar.service", "snap.samba.interface.foo.service"}, 153 // update state in systemd 154 {"systemctl", "daemon-reload"}, 155 }) 156 s.systemctlArgs = nil 157 158 // Change what the interface returns to simulate some useful change 159 s.Iface.SystemdPermanentSlotCallback = func(spec *systemd.Specification, slot *snap.SlotInfo) error { 160 return spec.AddService("foo", &systemd.Service{ExecStart: "/bin/true"}) 161 } 162 // Update over to the same snap to regenerate security 163 s.UpdateSnap(c, snapInfo, interfaces.ConfinementOptions{}, ifacetest.SambaYamlV1, 0) 164 // The bar service should have been stopped, foo service is unchanged 165 c.Check(s.systemctlArgs, DeepEquals, [][]string{ 166 {"systemctl", "--no-reload", "disable", "snap.samba.interface.bar.service"}, 167 {"systemctl", "stop", "snap.samba.interface.bar.service"}, 168 {"systemctl", "show", "--property=ActiveState", "snap.samba.interface.bar.service"}, 169 {"systemctl", "daemon-reload"}, 170 }) 171 } 172 173 func (s *backendSuite) TestSandboxFeatures(c *C) { 174 c.Assert(s.Backend.SandboxFeatures(), IsNil) 175 } 176 177 func (s *backendSuite) TestInstallingSnapWhenPreseeding(c *C) { 178 s.Backend = &systemd.Backend{} 179 opts := &interfaces.SecurityBackendOptions{Preseed: true} 180 s.Backend.Initialize(opts) 181 182 var sysdLog [][]string 183 r := sysd.MockSystemctl(func(cmd ...string) ([]byte, error) { 184 sysdLog = append(sysdLog, cmd) 185 return []byte{}, nil 186 }) 187 defer r() 188 189 s.Iface.SystemdPermanentSlotCallback = func(spec *systemd.Specification, slot *snap.SlotInfo) error { 190 return spec.AddService("foo", &systemd.Service{ExecStart: "/bin/true"}) 191 } 192 s.InstallSnap(c, interfaces.ConfinementOptions{}, "", ifacetest.SambaYamlV1, 1) 193 service := filepath.Join(dirs.SnapServicesDir, "snap.samba.interface.foo.service") 194 // the service file was created 195 _, err := os.Stat(service) 196 c.Check(err, IsNil) 197 // the service was enabled but not started 198 c.Check(sysdLog, DeepEquals, [][]string{ 199 {"--root", dirs.GlobalRootDir, "enable", "snap.samba.interface.foo.service"}, 200 }) 201 } 202 203 // not a viable scenario, but tested for completness 204 func (s *backendSuite) TestRemovingSnapWhenPreseeding(c *C) { 205 s.Backend = &systemd.Backend{} 206 opts := &interfaces.SecurityBackendOptions{Preseed: true} 207 s.Backend.Initialize(opts) 208 209 s.Iface.SystemdPermanentSlotCallback = func(spec *systemd.Specification, slot *snap.SlotInfo) error { 210 return spec.AddService("foo", &systemd.Service{ExecStart: "/bin/true"}) 211 } 212 for _, opts := range testedConfinementOpts { 213 snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 1) 214 s.systemctlArgs = nil 215 s.RemoveSnap(c, snapInfo) 216 service := filepath.Join(dirs.SnapServicesDir, "snap.samba.interface.foo.service") 217 // the service file was removed 218 _, err := os.Stat(service) 219 c.Check(os.IsNotExist(err), Equals, true) 220 // the service was disabled (but no other systemctl calls) 221 c.Check(s.systemctlArgs, DeepEquals, [][]string{ 222 {"systemctl", "--root", dirs.GlobalRootDir, "disable", "snap.samba.interface.foo.service"}, 223 }) 224 } 225 }