github.com/coreos/mantle@v0.13.0/kola/tests/coretest/core.go (about) 1 package coretest 2 3 import ( 4 "bytes" 5 "fmt" 6 "os" 7 "os/exec" 8 "strings" 9 "time" 10 11 "github.com/pborman/uuid" 12 13 "github.com/coreos/mantle/kola/register" 14 ) 15 16 const ( 17 CmdTimeout = time.Second * 20 18 DbusTimeout = time.Second * 20 19 DockerTimeout = time.Second * 60 20 PortTimeout = time.Second * 3 21 UpdateEnginePubKey = "/usr/share/update_engine/update-payload-key.pub.pem" 22 UpdateEnginePubKeyV1 = "d410d94dc56a1cba8df71c94ea6925811e44b09416f66958ab7a453f0731d80e" 23 UpdateEnginePubKeyV2 = "a76a22e6afcdfbc55dd2953aa950c7ec93b254774fca02d13ec52c59672e5982" 24 ) 25 26 // RHCOS services we expect disabled/inactive 27 var offServices = []string{ 28 "dnsmasq.service", 29 "iscsid.service", 30 "iscsid.socket", 31 "iscsiuio.service", 32 "nfs-blkmap.service", 33 "nfs-idmapd.service", 34 "nfs-mountd.service", 35 "nfs-server.service", 36 "nis-domainname.service", 37 "rbdmap.service", 38 "rdisc.service", 39 "rpc-statd.service", 40 "rpcbind.service", 41 "rpcbind.socket", 42 "sssd.service", 43 "tcsd.service", 44 } 45 46 func init() { 47 register.Register(®ister.Test{ 48 Name: "cl.basic", 49 Run: LocalTests, 50 ClusterSize: 1, 51 NativeFuncs: map[string]func() error{ 52 "CloudConfig": TestCloudinitCloudConfig, 53 "Script": TestCloudinitScript, 54 "PortSSH": TestPortSsh, 55 "DbusPerms": TestDbusPerms, 56 "Symlink": TestSymlinkResolvConf, 57 "UpdateEngineKeys": TestInstalledUpdateEngineRsaKeys, 58 "ServicesActive": TestServicesActive, 59 "ReadOnly": TestReadOnlyFs, 60 "RandomUUID": TestFsRandomUUID, 61 "Useradd": TestUseradd, 62 "MachineID": TestMachineID, 63 }, 64 Distros: []string{"cl"}, 65 }) 66 register.Register(®ister.Test{ 67 Name: "rhcos.basic", 68 Run: LocalTests, 69 ClusterSize: 1, 70 NativeFuncs: map[string]func() error{ 71 "PortSSH": TestPortSsh, 72 "DbusPerms": TestDbusPerms, 73 "ServicesActive": TestServicesActiveCoreOS, 74 "ServicesDisabled": TestServicesDisabledRHCOS, 75 "ReadOnly": TestReadOnlyFs, 76 "Useradd": TestUseradd, 77 "MachineID": TestMachineID, 78 }, 79 Distros: []string{"rhcos"}, 80 }) 81 register.Register(®ister.Test{ 82 Name: "fcos.basic", 83 Run: LocalTests, 84 ClusterSize: 1, 85 NativeFuncs: map[string]func() error{ 86 "PortSSH": TestPortSsh, 87 "DbusPerms": TestDbusPerms, 88 "ServicesActive": TestServicesActiveCoreOS, 89 "ReadOnly": TestReadOnlyFs, 90 "Useradd": TestUseradd, 91 "MachineID": TestMachineID, 92 }, 93 Distros: []string{"fcos"}, 94 }) 95 96 // tests requiring network connection to internet 97 register.Register(®ister.Test{ 98 Name: "cl.internet", 99 Run: InternetTests, 100 ClusterSize: 1, 101 Flags: []register.Flag{register.RequiresInternetAccess}, 102 NativeFuncs: map[string]func() error{ 103 "UpdateEngine": TestUpdateEngine, 104 "DockerPing": TestDockerPing, 105 "DockerEcho": TestDockerEcho, 106 "NTPDate": TestNTPDate, 107 }, 108 Distros: []string{"cl"}, 109 }) 110 } 111 112 func TestPortSsh() error { 113 //t.Parallel() 114 err := CheckPort("tcp", "127.0.0.1:22", PortTimeout) 115 if err != nil { 116 return err 117 } 118 return nil 119 } 120 121 func TestUpdateEngine() error { 122 //t.Parallel() 123 124 errc := make(chan error, 1) 125 go func() { 126 c := exec.Command("update_engine_client", "-status") 127 err := c.Run() 128 errc <- err 129 }() 130 131 select { 132 case <-time.After(CmdTimeout): 133 return fmt.Errorf("update_engine_client timed out after %s.", CmdTimeout) 134 case err := <-errc: 135 if err != nil { 136 return err 137 } 138 return nil 139 } 140 141 // FIXME(marineam): Test DBus directly 142 } 143 144 func TestDockerEcho() error { 145 //t.Parallel() 146 errc := make(chan error, 1) 147 go func() { 148 c := exec.Command("docker", "run", "busybox", "echo") 149 err := c.Run() 150 errc <- err 151 }() 152 select { 153 case <-time.After(DockerTimeout): 154 return fmt.Errorf("DockerEcho timed out after %s.", DockerTimeout) 155 case err := <-errc: 156 if err != nil { 157 return fmt.Errorf("DockerEcho: %v", err) 158 } 159 return nil 160 } 161 } 162 163 func TestDockerPing() error { 164 //t.Parallel() 165 errc := make(chan error, 1) 166 go func() { 167 c := exec.Command("docker", "run", "busybox", "ping", "-c4", "coreos.com") 168 err := c.Run() 169 errc <- err 170 }() 171 select { 172 case <-time.After(DockerTimeout): 173 return fmt.Errorf("DockerPing timed out after %s.", DockerTimeout) 174 case err := <-errc: 175 if err != nil { 176 return err 177 } 178 return nil 179 } 180 } 181 182 func TestNTPDate() error { 183 //t.Parallel() 184 errc := make(chan error, 1) 185 go func() { 186 c := exec.Command("ntpdate", "-d", "-s", "-u", "pool.ntp.org") 187 err := c.Run() 188 errc <- err 189 }() 190 select { 191 case <-time.After(CmdTimeout): 192 return fmt.Errorf("ntpdate timed out after %s.", CmdTimeout) 193 case err := <-errc: 194 if err != nil { 195 return err 196 } 197 return nil 198 } 199 } 200 201 // This execs gdbus, because we need to change uses to test perms. 202 func TestDbusPerms() error { 203 c := exec.Command( 204 "sudo", "-u", "core", 205 "gdbus", "call", "--system", 206 "--dest", "org.freedesktop.systemd1", 207 "--object-path", "/org/freedesktop/systemd1", 208 "--method", "org.freedesktop.systemd1.Manager.RestartUnit", 209 "ntpd.service", "replace", 210 ) 211 out, err := c.CombinedOutput() 212 213 if err != nil { 214 if !strings.Contains(string(out), "org.freedesktop.DBus.Error.AccessDenied") && 215 !strings.Contains(string(out), "org.freedesktop.DBus.Error.InteractiveAuthorizationRequired") { 216 return err 217 } 218 } else { 219 return fmt.Errorf("We were able to call RestartUnit as a non-root user.") 220 } 221 222 c = exec.Command( 223 "sudo", "-u", "core", 224 "gdbus", "call", "--system", 225 "--dest", "org.freedesktop.systemd1", 226 "--object-path", "/org/freedesktop/systemd1/unit/ntpd_2eservice", 227 "--method", "org.freedesktop.DBus.Properties.GetAll", 228 "org.freedesktop.systemd1.Unit", 229 ) 230 231 out, err = c.CombinedOutput() 232 if err != nil { 233 return fmt.Errorf("Err:%s\n Out:%v", err, out) 234 } 235 return nil 236 } 237 238 func TestSymlinkResolvConf() error { 239 //t.Parallel() 240 f, err := os.Lstat("/etc/resolv.conf") 241 if err != nil { 242 return fmt.Errorf("SymlinkResolvConf: %v", err) 243 } 244 if !IsLink(f) { 245 return fmt.Errorf("/etc/resolv.conf is not a symlink.") 246 } 247 return nil 248 } 249 250 func TestInstalledUpdateEngineRsaKeys() error { 251 //t.Parallel() 252 fileHash, err := Sha256File(UpdateEnginePubKey) 253 if err != nil { 254 return err 255 } 256 257 switch string(fileHash) { 258 case UpdateEnginePubKeyV1, UpdateEnginePubKeyV2: 259 return nil 260 default: 261 return fmt.Errorf("%s:%s unexpected hash.", UpdateEnginePubKey, fileHash) 262 } 263 } 264 265 func TestServicesActive() error { 266 return servicesActive([]string{ 267 "multi-user.target", 268 "docker.socket", 269 "systemd-timesyncd.service", 270 "update-engine.service", 271 }) 272 } 273 274 func TestServicesActiveCoreOS() error { 275 return servicesActive([]string{ 276 "multi-user.target", 277 }) 278 } 279 280 func servicesActive(units []string) error { 281 //t.Parallel() 282 for _, unit := range units { 283 c := exec.Command("systemctl", "is-active", unit) 284 err := c.Run() 285 if err != nil { 286 return fmt.Errorf("Services Active: %v", err) 287 } 288 } 289 return nil 290 } 291 292 func TestServicesDisabledRHCOS() error { 293 err := servicesInactive(offServices) 294 if err != nil { 295 return err 296 } 297 298 err = servicesDisabled(offServices) 299 if err != nil { 300 return err 301 } 302 return nil 303 } 304 305 func servicesInactive(units []string) error { 306 for _, unit := range units { 307 c := exec.Command("systemctl", "is-active", unit) 308 err := c.Run() 309 if err == nil { 310 return fmt.Errorf("Service Incorrectly Active: %q", unit) 311 } 312 } 313 return nil 314 } 315 316 func servicesDisabled(units []string) error { 317 for _, unit := range units { 318 c := exec.Command("systemctl", "is-enabled", unit) 319 out, err := c.Output() 320 if err == nil { 321 // "is-enabled" can return 0 in some cases when the output is not 322 // explicitly "disabled". In the case of the RHCOS services 323 // that are checked, we expect some to report "static" 324 outString := strings.TrimSuffix(string(out), "\n") 325 if (outString != "disabled") && (outString != "static") { 326 return fmt.Errorf("Service Incorrectly Enabled: %q", unit) 327 } 328 } 329 } 330 return nil 331 } 332 333 func TestReadOnlyFs() error { 334 mountModes := make(map[string]bool) 335 mounts, err := GetMountTable() 336 if err != nil { 337 return err 338 } 339 for _, m := range mounts { 340 mountModes[m.MountPoint] = m.Options[0] == "ro" 341 } 342 if mp, ok := mountModes["/usr"]; ok { 343 if mp { 344 return nil 345 } else { 346 return fmt.Errorf("/usr is not mounted read-only.") 347 } 348 } else if mp, ok := mountModes["/"]; ok { 349 if mp { 350 return nil 351 } else { 352 return fmt.Errorf("/ is not mounted read-only.") 353 } 354 } 355 return fmt.Errorf("could not find /usr or / mount points.") 356 } 357 358 // Test that the root disk's GUID was set to a random one on first boot. 359 func TestFsRandomUUID() error { 360 c := exec.Command("sh", "-ec", "sudo blkid -o value -s PTUUID /dev/$(lsblk -no PKNAME $(findmnt -vno SOURCE /))") 361 out, err := c.Output() 362 if err != nil { 363 return fmt.Errorf("findmnt: %v", err) 364 } 365 366 got, err := uuid.ParseBytes(bytes.TrimSpace(out)) 367 if err != nil { 368 return fmt.Errorf("malformed GUID: %v", err) 369 } 370 371 defaultGUID := uuid.Parse("00000000-0000-0000-0000-000000000001") 372 if uuid.Equal(defaultGUID, got) { 373 return fmt.Errorf("unexpected default GUID found") 374 } 375 376 return nil 377 } 378 379 // Test "Add User Manually", from https://coreos.com/os/docs/latest/adding-users.html 380 func TestUseradd() error { 381 u := "user1" 382 c := exec.Command("sudo", "useradd", "-p", "*", "-U", "-m", u, "-G", "sudo") 383 err := c.Run() 384 if err != nil { 385 return fmt.Errorf("useradd: %v", err) 386 } 387 388 // verify 389 c = exec.Command("id", u) 390 err = c.Run() 391 if err != nil { 392 return fmt.Errorf("id %s: %v", u, err) 393 } 394 395 return nil 396 } 397 398 // Test that /etc/machine-id isn't empty or COREOS_BLANK_MACHINE_ID 399 func TestMachineID() error { 400 id := MachineID() 401 if id == "" { 402 return fmt.Errorf("machine-id is empty") 403 } else if id == "COREOS_BLANK_MACHINE_ID" { 404 return fmt.Errorf("machine-id is %s", id) 405 } 406 return nil 407 }