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(&register.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(&register.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(&register.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(&register.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  }