func init() { Describe("bootstrap", func() { Describe("Run", func() { var ( platform *fakeplatform.FakePlatform dirProvider boshdir.Provider settingsSource *fakeinf.FakeSettingsSource settingsService *fakesettings.FakeSettingsService ) BeforeEach(func() { platform = fakeplatform.NewFakePlatform() dirProvider = boshdir.NewProvider("/var/vcap") settingsSource = &fakeinf.FakeSettingsSource{} settingsService = &fakesettings.FakeSettingsService{} }) bootstrap := func() error { logger := boshlog.NewLogger(boshlog.LevelNone) return NewBootstrap(platform, dirProvider, settingsService, logger).Run() } It("sets up runtime configuration", func() { err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupRuntimeConfigurationWasInvoked).To(BeTrue()) }) Describe("SSH tunnel setup for registry", func() { It("returns error without configuring ssh on the platform if getting public key fails", func() { settingsService.PublicKeyErr = errors.New("fake-get-public-key-err") err := bootstrap() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-get-public-key-err")) Expect(platform.SetupSSHCalled).To(BeFalse()) }) Context("when public key is not empty", func() { BeforeEach(func() { settingsService.PublicKey = "fake-public-key" }) It("gets the public key and sets up ssh via the platform", func() { err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupSSHPublicKey).To(Equal("fake-public-key")) Expect(platform.SetupSSHUsername).To(Equal("vcap")) }) It("returns error if configuring ssh on the platform fails", func() { platform.SetupSSHErr = errors.New("fake-setup-ssh-err") err := bootstrap() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-setup-ssh-err")) }) }) Context("when public key key is empty", func() { BeforeEach(func() { settingsSource.PublicKey = "" }) It("gets the public key and does not setup SSH", func() { err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupSSHCalled).To(BeFalse()) }) }) }) It("sets up hostname", func() { settingsService.Settings.AgentID = "foo-bar-baz-123" err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupHostnameHostname).To(Equal("foo-bar-baz-123")) }) It("fetches initial settings", func() { err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(settingsService.SettingsWereLoaded).To(BeTrue()) }) It("returns error from loading initial settings", func() { settingsService.LoadSettingsError = errors.New("fake-load-error") err := bootstrap() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-load-error")) }) It("sets up networking", func() { networks := boshsettings.Networks{ "bosh": boshsettings.Network{}, } settingsService.Settings.Networks = networks err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupNetworkingNetworks).To(Equal(networks)) }) It("sets up ephemeral disk", func() { settingsService.Settings.Disks = boshsettings.Disks{ Ephemeral: "fake-ephemeral-disk-setting", } platform.GetEphemeralDiskPathRealPath = "/dev/sda" err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupEphemeralDiskWithPathDevicePath).To(Equal("/dev/sda")) Expect(platform.GetEphemeralDiskPathSettings).To(Equal(boshsettings.DiskSettings{ VolumeID: "fake-ephemeral-disk-setting", Path: "fake-ephemeral-disk-setting", })) }) It("returns error if setting ephemeral disk fails", func() { platform.SetupEphemeralDiskWithPathErr = errors.New("fake-setup-ephemeral-disk-err") err := bootstrap() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-setup-ephemeral-disk-err")) }) It("sets up raw ephemeral disks if paths exist", func() { settingsService.Settings.Disks = boshsettings.Disks{ RawEphemeral: []boshsettings.DiskSettings{{Path: "/dev/xvdb"}, {Path: "/dev/xvdc"}}, } err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupRawEphemeralDisksCallCount).To(Equal(1)) Expect(len(platform.SetupRawEphemeralDisksDevices)).To(Equal(2)) Expect(platform.SetupRawEphemeralDisksDevices[0].Path).To(Equal("/dev/xvdb")) Expect(platform.SetupRawEphemeralDisksDevices[1].Path).To(Equal("/dev/xvdc")) }) It("returns error if setting raw ephemeral disks fails", func() { platform.SetupRawEphemeralDisksErr = errors.New("fake-setup-raw-ephemeral-disks-err") err := bootstrap() Expect(platform.SetupRawEphemeralDisksCallCount).To(Equal(1)) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-setup-raw-ephemeral-disks-err")) }) It("sets up data dir", func() { err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupDataDirCalled).To(BeTrue()) }) It("returns error if set up of data dir fails", func() { platform.SetupDataDirErr = errors.New("fake-setup-data-dir-err") err := bootstrap() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-setup-data-dir-err")) }) It("sets up tmp dir", func() { err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupTmpDirCalled).To(BeTrue()) }) It("returns error if set up of tmp dir fails", func() { platform.SetupTmpDirErr = errors.New("fake-setup-tmp-dir-err") err := bootstrap() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-setup-tmp-dir-err")) }) It("mounts persistent disk", func() { settingsService.Settings.Disks = boshsettings.Disks{ Persistent: map[string]interface{}{ "vol-123": map[string]interface{}{ "volume_id": "2", "path": "/dev/sdb", }, }, } err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.MountPersistentDiskSettings).To(Equal(boshsettings.DiskSettings{ ID: "vol-123", VolumeID: "2", Path: "/dev/sdb", })) Expect(platform.MountPersistentDiskMountPoint).To(Equal(dirProvider.StoreDir())) }) It("errors if there is more than one persistent disk", func() { settingsService.Settings.Disks = boshsettings.Disks{ Persistent: map[string]interface{}{ "vol-123": "/dev/sdb", "vol-456": "/dev/sdc", }, } err := bootstrap() Expect(err).To(HaveOccurred()) }) It("does not try to mount when no persistent disk", func() { settingsService.Settings.Disks = boshsettings.Disks{ Persistent: map[string]interface{}{}, } err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.MountPersistentDiskSettings).To(Equal(boshsettings.DiskSettings{})) Expect(platform.MountPersistentDiskMountPoint).To(Equal("")) }) It("grows the root filesystem", func() { err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupRootDiskCalledTimes).To(Equal(1)) }) It("returns an error if growing the root filesystem fails", func() { platform.SetupRootDiskError = errors.New("growfs failed") err := bootstrap() Expect(err).To(HaveOccurred()) Expect(platform.SetupRootDiskCalledTimes).To(Equal(1)) Expect(err.Error()).To(ContainSubstring("growfs failed")) }) It("sets root and vcap passwords", func() { settingsService.Settings.Env.Bosh.Password = "******" settingsService.Settings.Env.Bosh.KeepRootPassword = false err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(2).To(Equal(len(platform.UserPasswords))) Expect("some-encrypted-password").To(Equal(platform.UserPasswords["root"])) Expect("some-encrypted-password").To(Equal(platform.UserPasswords["vcap"])) }) It("does not change root password if keep_root_password is set to true", func() { settingsService.Settings.Env.Bosh.Password = "******" settingsService.Settings.Env.Bosh.KeepRootPassword = true err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(1).To(Equal(len(platform.UserPasswords))) Expect("some-encrypted-password").ToNot(Equal(platform.UserPasswords["root"])) Expect("some-encrypted-password").To(Equal(platform.UserPasswords["vcap"])) }) It("does not set password if not provided", func() { settingsService.Settings.Env.Bosh.KeepRootPassword = false err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(0).To(Equal(len(platform.UserPasswords))) }) It("sets ntp", func() { settingsService.Settings.Ntp = []string{ "0.north-america.pool.ntp.org", "1.north-america.pool.ntp.org", } err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(2).To(Equal(len(platform.SetTimeWithNtpServersServers))) Expect("0.north-america.pool.ntp.org").To(Equal(platform.SetTimeWithNtpServersServers[0])) Expect("1.north-america.pool.ntp.org").To(Equal(platform.SetTimeWithNtpServersServers[1])) }) It("setups up monit user", func() { err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupMonitUserSetup).To(BeTrue()) }) It("starts monit", func() { err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.StartMonitStarted).To(BeTrue()) }) }) Describe("Network setup exercised by Run", func() { var ( settingsJSON string fs *fakesys.FakeFileSystem platform boshplatform.Platform boot Bootstrap defaultNetworkResolver boshsettings.DefaultNetworkResolver logger boshlog.Logger dirProvider boshdirs.Provider interfaceAddrsProvider *fakeip.FakeInterfaceAddressesProvider ) writeNetworkDevice := func(iface string, macAddress string, isPhysical bool) string { interfacePath := fmt.Sprintf("/sys/class/net/%s", iface) fs.WriteFile(interfacePath, []byte{}) if isPhysical { fs.WriteFile(fmt.Sprintf("/sys/class/net/%s/device", iface), []byte{}) } fs.WriteFileString(fmt.Sprintf("/sys/class/net/%s/address", iface), fmt.Sprintf("%s\n", macAddress)) return interfacePath } stubInterfaces := func(interfaces [][]string) { var interfacePaths []string for _, iface := range interfaces { interfaceName := iface[0] interfaceMAC := iface[1] interfaceType := iface[2] isPhysical := interfaceType == "physical" interfacePaths = append(interfacePaths, writeNetworkDevice(interfaceName, interfaceMAC, isPhysical)) } fs.SetGlob("/sys/class/net/*", interfacePaths) } BeforeEach(func() { fs = fakesys.NewFakeFileSystem() runner := fakesys.NewFakeCmdRunner() dirProvider = boshdirs.NewProvider("/var/vcap/bosh") linuxOptions := boshplatform.LinuxOptions{ CreatePartitionIfNoEphemeralDisk: true, } logger = boshlog.NewLogger(boshlog.LevelNone) diskManager := fakedisk.NewFakeDiskManager() diskManager.FakeMountsSearcher.SearchMountsMounts = []boshdisk.Mount{ {MountPoint: "/", PartitionPath: "rootfs"}, {MountPoint: "/", PartitionPath: "/dev/vda1"}, } // for the GrowRootFS call to findRootDevicePath runner.AddCmdResult( "readlink -f /dev/vda1", fakesys.FakeCmdResult{Stdout: "/dev/vda1"}, ) // for the createEphemeralPartitionsOnRootDevice call to findRootDevicePath runner.AddCmdResult( "readlink -f /dev/vda1", fakesys.FakeCmdResult{Stdout: "/dev/vda1"}, ) diskManager.FakeRootDevicePartitioner.GetDeviceSizeInBytesSizes["/dev/vda"] = 1024 * 1024 * 1024 udev := boshudev.NewConcreteUdevDevice(runner, logger) linuxCdrom := boshcdrom.NewLinuxCdrom("/dev/sr0", udev, runner) linuxCdutil := boshcdrom.NewCdUtil(dirProvider.SettingsDir(), fs, linuxCdrom, logger) compressor := boshcmd.NewTarballCompressor(runner, fs) copier := boshcmd.NewCpCopier(runner, fs, logger) sigarCollector := boshsigar.NewSigarStatsCollector(&sigar.ConcreteSigar{}) vitalsService := boshvitals.NewService(sigarCollector, dirProvider) ipResolver := boship.NewResolver(boship.NetworkInterfaceToAddrsFunc) arping := bosharp.NewArping(runner, fs, logger, boshplatform.ArpIterations, boshplatform.ArpIterationDelay, boshplatform.ArpInterfaceCheckDelay) interfaceConfigurationCreator := boshnet.NewInterfaceConfigurationCreator(logger) interfaceAddrsProvider = &fakeip.FakeInterfaceAddressesProvider{} interfaceAddressesValidator := boship.NewInterfaceAddressesValidator(interfaceAddrsProvider) dnsValidator := boshnet.NewDNSValidator(fs) fs.WriteFileString("/etc/resolv.conf", "8.8.8.8 4.4.4.4") ubuntuNetManager := boshnet.NewUbuntuNetManager(fs, runner, ipResolver, interfaceConfigurationCreator, interfaceAddressesValidator, dnsValidator, arping, logger) ubuntuCertManager := boshcert.NewUbuntuCertManager(fs, runner, 1, logger) monitRetryable := boshplatform.NewMonitRetryable(runner) monitRetryStrategy := boshretry.NewAttemptRetryStrategy(10, 1*time.Second, monitRetryable, logger) devicePathResolver := devicepathresolver.NewIdentityDevicePathResolver() routesSearcher := boshnet.NewCmdRoutesSearcher(runner) defaultNetworkResolver = boshnet.NewDefaultNetworkResolver(routesSearcher, ipResolver) state, err := boshplatform.NewBootstrapState(fs, "/tmp/agent_state.json") Expect(err).NotTo(HaveOccurred()) platform = boshplatform.NewLinuxPlatform( fs, runner, sigarCollector, compressor, copier, dirProvider, vitalsService, linuxCdutil, diskManager, ubuntuNetManager, ubuntuCertManager, monitRetryStrategy, devicePathResolver, 500*time.Millisecond, state, linuxOptions, logger, defaultNetworkResolver, ) }) JustBeforeEach(func() { settingsPath := filepath.Join("bosh", "settings.json") var settings boshsettings.Settings json.Unmarshal([]byte(settingsJSON), &settings) settingsSource := fakeinf.FakeSettingsSource{ PublicKey: "123", SettingsValue: settings, } settingsService := boshsettings.NewService( platform.GetFs(), settingsPath, settingsSource, platform, logger, ) boot = NewBootstrap( platform, dirProvider, settingsService, logger, ) }) Context("when a single network configuration is provided, with a MAC address", func() { BeforeEach(func() { settingsJSON = `{ "networks": { "netA": { "default": ["dns", "gateway"], "ip": "2.2.2.2", "dns": [ "8.8.8.8", "4.4.4.4" ], "netmask": "255.255.255.0", "gateway": "2.2.2.0", "mac": "aa:bb:cc" } } }` }) Context("and no physical network interfaces exist", func() { Context("and a single virtual network interface exists", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"lo", "aa:bb:cc", "virtual"}}) }) It("raises an error", func() { err := boot.Run() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Number of network settings '1' is greater than the number of network devices '0")) }) }) }) Context("and a single physical network interface exists", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"eth0", "aa:bb:cc", "physical"}}) interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("eth0", "2.2.2.2"), } }) It("succeeds", func() { err := boot.Run() Expect(err).NotTo(HaveOccurred()) }) }) Context("and extra physical network interfaces exist", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"eth0", "aa:bb:cc", "physical"}, []string{"eth1", "aa:bb:dd", "physical"}}) interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("eth0", "2.2.2.2"), } }) It("succeeds", func() { err := boot.Run() Expect(err).NotTo(HaveOccurred()) }) }) Context("and extra virtual network interfaces exist", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"eth0", "aa:bb:cc", "physical"}, []string{"lo", "aa:bb:ee", "virtual"}}) interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("eth0", "2.2.2.2"), } }) It("succeeds", func() { err := boot.Run() Expect(err).ToNot(HaveOccurred()) }) }) }) Context("when a single network configuration is provided, without a MAC address", func() { BeforeEach(func() { settingsJSON = `{ "networks": { "netA": { "default": ["dns", "gateway"], "ip": "2.2.2.2", "dns": [ "8.8.8.8", "4.4.4.4" ], "netmask": "255.255.255.0", "gateway": "2.2.2.0" } } }` }) Context("and no physical network interfaces exist", func() { Context("and a single virtual network interface exists", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"lo", "aa:bb:cc", "virtual"}}) }) It("raises an error", func() { err := boot.Run() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Number of network settings '1' is greater than the number of network devices '0")) }) }) }) Context("and a single physical network interface exists", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"eth0", "aa:bb:cc", "physical"}}) interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("eth0", "2.2.2.2"), } }) It("succeeds", func() { err := boot.Run() Expect(err).NotTo(HaveOccurred()) }) }) Context("and extra physical network interfaces exist", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"eth0", "aa:bb:cc", "physical"}, []string{"eth1", "aa:bb:dd", "physical"}}) interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("eth0", "2.2.2.2"), } }) It("succeeds", func() { err := boot.Run() Expect(err).NotTo(HaveOccurred()) }) }) Context("and an extra virtual network interface exists", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"eth0", "aa:bb:cc", "physical"}, []string{"lo", "aa:bb:dd", "virtual"}}) interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("eth0", "2.2.2.2"), } }) It("succeeds", func() { err := boot.Run() Expect(err).NotTo(HaveOccurred()) }) }) }) Context("when two network configurations are provided", func() { BeforeEach(func() { settingsJSON = `{ "networks": { "netA": { "default": ["dns", "gateway"], "ip": "2.2.2.2", "dns": [ "8.8.8.8", "4.4.4.4" ], "netmask": "255.255.255.0", "gateway": "2.2.2.0", "mac": "aa:bb:cc" }, "netB": { "default": ["dns", "gateway"], "ip": "3.3.3.3", "dns": [ "8.8.8.8", "4.4.4.4" ], "netmask": "255.255.255.0", "gateway": "3.3.3.0", "mac": "" } } }` }) Context("and a single physical network interface exists", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"eth0", "aa:bb:cc", "physical"}}) }) It("raises an error", func() { err := boot.Run() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Number of network settings '2' is greater than the number of network devices '1")) }) }) Context("and two physical network interfaces with matching MAC addresses exist", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"eth0", "aa:bb:cc", "physical"}, []string{"eth1", "aa:bb:dd", "physical"}}) interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("eth0", "2.2.2.2"), boship.NewSimpleInterfaceAddress("eth1", "3.3.3.3"), } }) It("succeeds", func() { err := boot.Run() Expect(err).ToNot(HaveOccurred()) }) }) }) }) }) }
func describeLinuxPlatform() { var ( collector *fakestats.FakeCollector fs *fakesys.FakeFileSystem cmdRunner *fakesys.FakeCmdRunner diskManager *fakedisk.FakeDiskManager dirProvider boshdirs.Provider devicePathResolver *fakedpresolv.FakeDevicePathResolver platform Platform cdutil *fakedevutil.FakeDeviceUtil compressor boshcmd.Compressor copier boshcmd.Copier vitalsService boshvitals.Service netManager *fakenet.FakeManager certManager *fakecert.FakeManager monitRetryStrategy *fakeretry.FakeRetryStrategy fakeDefaultNetworkResolver *fakenet.FakeDefaultNetworkResolver options LinuxOptions logger boshlog.Logger ) BeforeEach(func() { logger = boshlog.NewLogger(boshlog.LevelNone) collector = &fakestats.FakeCollector{} fs = fakesys.NewFakeFileSystem() cmdRunner = fakesys.NewFakeCmdRunner() diskManager = fakedisk.NewFakeDiskManager() dirProvider = boshdirs.NewProvider("/fake-dir") cdutil = fakedevutil.NewFakeDeviceUtil() compressor = boshcmd.NewTarballCompressor(cmdRunner, fs) copier = boshcmd.NewCpCopier(cmdRunner, fs, logger) vitalsService = boshvitals.NewService(collector, dirProvider) netManager = &fakenet.FakeManager{} certManager = new(fakecert.FakeManager) monitRetryStrategy = fakeretry.NewFakeRetryStrategy() devicePathResolver = fakedpresolv.NewFakeDevicePathResolver() fakeDefaultNetworkResolver = &fakenet.FakeDefaultNetworkResolver{} options = LinuxOptions{} fs.SetGlob("/sys/bus/scsi/devices/*:0:0:0/block/*", []string{ "/sys/bus/scsi/devices/0:0:0:0/block/sr0", "/sys/bus/scsi/devices/6:0:0:0/block/sdd", "/sys/bus/scsi/devices/fake-host-id:0:0:0/block/sda", }) fs.SetGlob("/sys/bus/scsi/devices/fake-host-id:0:fake-disk-id:0/block/*", []string{ "/sys/bus/scsi/devices/fake-host-id:0:fake-disk-id:0/block/sdf", }) }) JustBeforeEach(func() { platform = NewLinuxPlatform( fs, cmdRunner, collector, compressor, copier, dirProvider, vitalsService, cdutil, diskManager, netManager, certManager, monitRetryStrategy, devicePathResolver, 5*time.Millisecond, options, logger, fakeDefaultNetworkResolver, ) }) Describe("SetupRuntimeConfiguration", func() { It("setups runtime configuration", func() { err := platform.SetupRuntimeConfiguration() Expect(err).NotTo(HaveOccurred()) Expect(len(cmdRunner.RunCommands)).To(Equal(1)) Expect(cmdRunner.RunCommands[0]).To(Equal([]string{"bosh-agent-rc"})) }) }) Describe("CreateUser", func() { It("creates user", func() { expectedUseradd := []string{ "useradd", "-m", "-b", "/some/path/to/home", "-s", "/bin/bash", "-p", "bar-pwd", "foo-user", } err := platform.CreateUser("foo-user", "bar-pwd", "/some/path/to/home") Expect(err).NotTo(HaveOccurred()) basePathStat := fs.GetFileTestStat("/some/path/to/home") Expect(basePathStat.FileType).To(Equal(fakesys.FakeFileTypeDir)) Expect(basePathStat.FileMode).To(Equal(os.FileMode(0755))) Expect(cmdRunner.RunCommands).To(Equal([][]string{expectedUseradd})) }) It("creates user with an empty password", func() { expectedUseradd := []string{ "useradd", "-m", "-b", "/some/path/to/home", "-s", "/bin/bash", "foo-user", } err := platform.CreateUser("foo-user", "", "/some/path/to/home") Expect(err).NotTo(HaveOccurred()) basePathStat := fs.GetFileTestStat("/some/path/to/home") Expect(basePathStat.FileType).To(Equal(fakesys.FakeFileTypeDir)) Expect(basePathStat.FileMode).To(Equal(os.FileMode(0755))) Expect(cmdRunner.RunCommands).To(Equal([][]string{expectedUseradd})) }) }) Describe("AddUserToGroups", func() { It("adds user to groups", func() { err := platform.AddUserToGroups("foo-user", []string{"group1", "group2", "group3"}) Expect(err).NotTo(HaveOccurred()) Expect(len(cmdRunner.RunCommands)).To(Equal(1)) usermod := []string{"usermod", "-G", "group1,group2,group3", "foo-user"} Expect(cmdRunner.RunCommands[0]).To(Equal(usermod)) }) }) Describe("DeleteEphemeralUsersMatching", func() { It("deletes users with prefix and regex", func() { passwdFile := `bosh_foo:... bosh_bar:... foo:... bar:... foobar:... bosh_foobar:...` fs.WriteFileString("/etc/passwd", passwdFile) err := platform.DeleteEphemeralUsersMatching("bar$") Expect(err).NotTo(HaveOccurred()) Expect(len(cmdRunner.RunCommands)).To(Equal(2)) Expect(cmdRunner.RunCommands[0]).To(Equal([]string{"userdel", "-r", "bosh_bar"})) Expect(cmdRunner.RunCommands[1]).To(Equal([]string{"userdel", "-r", "bosh_foobar"})) }) }) Describe("SetupSSH", func() { It("setup ssh", func() { fs.HomeDirHomePath = "/some/home/dir" platform.SetupSSH("some public key", "vcap") sshDirPath := "/some/home/dir/.ssh" sshDirStat := fs.GetFileTestStat(sshDirPath) Expect("vcap").To(Equal(fs.HomeDirUsername)) Expect(sshDirStat).NotTo(BeNil()) Expect(sshDirStat.FileType).To(Equal(fakesys.FakeFileTypeDir)) Expect(os.FileMode(0700)).To(Equal(sshDirStat.FileMode)) Expect("vcap").To(Equal(sshDirStat.Username)) authKeysStat := fs.GetFileTestStat(filepath.Join(sshDirPath, "authorized_keys")) Expect(authKeysStat).NotTo(BeNil()) Expect(fakesys.FakeFileTypeFile).To(Equal(authKeysStat.FileType)) Expect(os.FileMode(0600)).To(Equal(authKeysStat.FileMode)) Expect("vcap").To(Equal(authKeysStat.Username)) Expect("some public key").To(Equal(authKeysStat.StringContents())) }) }) Describe("SetUserPassword", func() { It("set user password", func() { platform.SetUserPassword("my-user", "my-encrypted-password") Expect(len(cmdRunner.RunCommands)).To(Equal(1)) Expect(cmdRunner.RunCommands[0]).To(Equal([]string{"usermod", "-p", "my-encrypted-password", "my-user"})) }) }) Describe("SetupHostname", func() { const expectedEtcHosts = `127.0.0.1 localhost foobar.local # The following lines are desirable for IPv6 capable hosts ::1 localhost ip6-localhost ip6-loopback foobar.local fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters ff02::3 ip6-allhosts ` It("sets up hostname", func() { platform.SetupHostname("foobar.local") Expect(len(cmdRunner.RunCommands)).To(Equal(1)) Expect(cmdRunner.RunCommands[0]).To(Equal([]string{"hostname", "foobar.local"})) hostnameFileContent, err := fs.ReadFileString("/etc/hostname") Expect(err).NotTo(HaveOccurred()) Expect(hostnameFileContent).To(Equal("foobar.local")) hostsFileContent, err := fs.ReadFileString("/etc/hosts") Expect(err).NotTo(HaveOccurred()) Expect(hostsFileContent).To(Equal(expectedEtcHosts)) }) }) Describe("SetupLogrotate", func() { const expectedEtcLogrotate = `# Generated by bosh-agent fake-base-path/data/sys/log/*.log fake-base-path/data/sys/log/*/*.log fake-base-path/data/sys/log/*/*/*.log { missingok rotate 7 compress delaycompress copytruncate size=fake-size } ` It("sets up logrotate", func() { platform.SetupLogrotate("fake-group-name", "fake-base-path", "fake-size") logrotateFileContent, err := fs.ReadFileString("/etc/logrotate.d/fake-group-name") Expect(err).NotTo(HaveOccurred()) Expect(logrotateFileContent).To(Equal(expectedEtcLogrotate)) }) }) Describe("SetTimeWithNtpServers", func() { It("sets time with ntp servers", func() { platform.SetTimeWithNtpServers([]string{"0.north-america.pool.ntp.org", "1.north-america.pool.ntp.org"}) ntpConfig := fs.GetFileTestStat("/fake-dir/bosh/etc/ntpserver") Expect(ntpConfig.StringContents()).To(Equal("0.north-america.pool.ntp.org 1.north-america.pool.ntp.org")) Expect(ntpConfig.FileType).To(Equal(fakesys.FakeFileTypeFile)) Expect(len(cmdRunner.RunCommands)).To(Equal(1)) Expect(cmdRunner.RunCommands[0]).To(Equal([]string{"ntpdate"})) }) It("sets time with ntp servers is noop when no ntp server provided", func() { platform.SetTimeWithNtpServers([]string{}) Expect(len(cmdRunner.RunCommands)).To(Equal(0)) ntpConfig := fs.GetFileTestStat("/fake-dir/bosh/etc/ntpserver") Expect(ntpConfig).To(BeNil()) }) }) Describe("SetupEphemeralDiskWithPath", func() { var ( partitioner *fakedisk.FakePartitioner formatter *fakedisk.FakeFormatter mounter *fakedisk.FakeMounter ) BeforeEach(func() { partitioner = diskManager.FakePartitioner formatter = diskManager.FakeFormatter mounter = diskManager.FakeMounter }) itSetsUpEphemeralDisk := func(act func() error) { It("sets up ephemeral disk with path", func() { err := act() Expect(err).NotTo(HaveOccurred()) dataDir := fs.GetFileTestStat("/fake-dir/data") Expect(dataDir.FileType).To(Equal(fakesys.FakeFileTypeDir)) Expect(dataDir.FileMode).To(Equal(os.FileMode(0750))) }) It("creates new partition even if the data directory is not empty", func() { fs.SetGlob(path.Join("/fake-dir", "data", "*"), []string{"something"}) err := act() Expect(err).ToNot(HaveOccurred()) Expect(partitioner.PartitionCalled).To(BeTrue()) Expect(formatter.FormatCalled).To(BeTrue()) Expect(mounter.MountCalled).To(BeTrue()) }) } Context("when ephemeral disk path is provided", func() { act := func() error { return platform.SetupEphemeralDiskWithPath("/dev/xvda") } itSetsUpEphemeralDisk(act) It("returns error if creating data dir fails", func() { fs.MkdirAllError = errors.New("fake-mkdir-all-err") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-mkdir-all-err")) Expect(partitioner.PartitionCalled).To(BeFalse()) Expect(formatter.FormatCalled).To(BeFalse()) Expect(mounter.MountCalled).To(BeFalse()) }) It("returns err when the data directory cannot be globbed", func() { fs.GlobErr = errors.New("fake-glob-err") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Globbing ephemeral disk mount point `/fake-dir/data/*'")) Expect(err.Error()).To(ContainSubstring("fake-glob-err")) Expect(partitioner.PartitionCalled).To(BeFalse()) Expect(formatter.FormatCalled).To(BeFalse()) Expect(mounter.MountCalled).To(BeFalse()) }) It("returns err when mem stats are unavailable", func() { collector.MemStatsErr = errors.New("fake-memstats-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Calculating partition sizes")) Expect(err.Error()).To(ContainSubstring("fake-memstats-error")) Expect(partitioner.PartitionCalled).To(BeFalse()) Expect(formatter.FormatCalled).To(BeFalse()) Expect(mounter.MountCalled).To(BeFalse()) }) It("returns an error when partitioning fails", func() { partitioner.PartitionErr = errors.New("fake-partition-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Partitioning ephemeral disk `/dev/xvda'")) Expect(err.Error()).To(ContainSubstring("fake-partition-error")) Expect(formatter.FormatCalled).To(BeFalse()) Expect(mounter.MountCalled).To(BeFalse()) }) It("formats swap and data partitions", func() { err := act() Expect(err).NotTo(HaveOccurred()) Expect(len(formatter.FormatPartitionPaths)).To(Equal(2)) Expect(formatter.FormatPartitionPaths[0]).To(Equal("/dev/xvda1")) Expect(formatter.FormatPartitionPaths[1]).To(Equal("/dev/xvda2")) Expect(len(formatter.FormatFsTypes)).To(Equal(2)) Expect(formatter.FormatFsTypes[0]).To(Equal(boshdisk.FileSystemSwap)) Expect(formatter.FormatFsTypes[1]).To(Equal(boshdisk.FileSystemExt4)) }) It("mounts swap and data partitions", func() { err := act() Expect(err).NotTo(HaveOccurred()) Expect(len(mounter.MountMountPoints)).To(Equal(1)) Expect(mounter.MountMountPoints[0]).To(Equal("/fake-dir/data")) Expect(len(mounter.MountPartitionPaths)).To(Equal(1)) Expect(mounter.MountPartitionPaths[0]).To(Equal("/dev/xvda2")) Expect(len(mounter.SwapOnPartitionPaths)).To(Equal(1)) Expect(mounter.SwapOnPartitionPaths[0]).To(Equal("/dev/xvda1")) }) It("creates swap the size of the memory and the rest for data when disk is bigger than twice the memory", func() { memSizeInBytes := uint64(1024 * 1024 * 1024) diskSizeInBytes := 2*memSizeInBytes + 64 fakePartitioner := partitioner fakePartitioner.GetDeviceSizeInBytesSizes["/dev/xvda"] = diskSizeInBytes collector.MemStats.Total = memSizeInBytes err := act() Expect(err).NotTo(HaveOccurred()) Expect(fakePartitioner.PartitionPartitions).To(Equal([]boshdisk.Partition{ {SizeInBytes: memSizeInBytes, Type: boshdisk.PartitionTypeSwap}, {SizeInBytes: diskSizeInBytes - memSizeInBytes, Type: boshdisk.PartitionTypeLinux}, })) }) It("creates equal swap and data partitions when disk is twice the memory or smaller", func() { memSizeInBytes := uint64(1024 * 1024 * 1024) diskSizeInBytes := 2*memSizeInBytes - 64 fakePartitioner := partitioner fakePartitioner.GetDeviceSizeInBytesSizes["/dev/xvda"] = diskSizeInBytes collector.MemStats.Total = memSizeInBytes err := act() Expect(err).NotTo(HaveOccurred()) Expect(fakePartitioner.PartitionPartitions).To(Equal([]boshdisk.Partition{ {SizeInBytes: diskSizeInBytes / 2, Type: boshdisk.PartitionTypeSwap}, {SizeInBytes: diskSizeInBytes / 2, Type: boshdisk.PartitionTypeLinux}, })) }) }) Context("when ephemeral disk path is not provided", func() { act := func() error { return platform.SetupEphemeralDiskWithPath("") } Context("when agent should partition ephemeral disk on root disk", func() { BeforeEach(func() { partitioner = diskManager.FakeRootDevicePartitioner options.CreatePartitionIfNoEphemeralDisk = true }) Context("when root device fails to be determined", func() { BeforeEach(func() { diskManager.FakeMountsSearcher.SearchMountsErr = errors.New("fake-mounts-searcher-error") }) It("returns an error", func() { err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Finding root partition device")) Expect(partitioner.PartitionCalled).To(BeFalse()) Expect(formatter.FormatCalled).To(BeFalse()) Expect(mounter.MountCalled).To(BeFalse()) }) }) Context("when root partition is not the first partition", func() { BeforeEach(func() { diskManager.FakeMountsSearcher.SearchMountsMounts = []boshdisk.Mount{ {MountPoint: "/", PartitionPath: "/dev/vda2"}, } }) It("returns an error", func() { err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Root partition is not the first partition")) Expect(partitioner.PartitionCalled).To(BeFalse()) Expect(formatter.FormatCalled).To(BeFalse()) Expect(mounter.MountCalled).To(BeFalse()) }) }) Context("when root device is determined", func() { BeforeEach(func() { diskManager.FakeMountsSearcher.SearchMountsMounts = []boshdisk.Mount{ {MountPoint: "/", PartitionPath: "rootfs"}, {MountPoint: "/", PartitionPath: "/dev/vda1"}, } }) Context("when getting absolute path fails", func() { BeforeEach(func() { cmdRunner.AddCmdResult( "readlink -f /dev/vda1", fakesys.FakeCmdResult{Error: errors.New("fake-readlink-error")}, ) }) It("returns an error", func() { err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-readlink-error")) Expect(partitioner.PartitionCalled).To(BeFalse()) Expect(formatter.FormatCalled).To(BeFalse()) Expect(mounter.MountCalled).To(BeFalse()) }) }) Context("when getting absolute path suceeds", func() { BeforeEach(func() { cmdRunner.AddCmdResult( "readlink -f /dev/vda1", fakesys.FakeCmdResult{Stdout: "/dev/vda1"}, ) }) Context("when root device has insufficient space for ephemeral partitions", func() { BeforeEach(func() { partitioner.GetDeviceSizeInBytesSizes["/dev/vda"] = 1024*1024*1024 - 1 collector.MemStats.Total = 8 }) It("returns an error", func() { err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Insufficient remaining disk")) Expect(partitioner.PartitionCalled).To(BeFalse()) Expect(formatter.FormatCalled).To(BeFalse()) Expect(mounter.MountCalled).To(BeFalse()) }) }) Context("when root device has sufficient space for ephemeral partitions", func() { BeforeEach(func() { partitioner.GetDeviceSizeInBytesSizes["/dev/vda"] = 1024 * 1024 * 1024 collector.MemStats.Total = 256 * 1024 * 1024 }) itSetsUpEphemeralDisk(act) It("returns err when mem stats are unavailable", func() { collector.MemStatsErr = errors.New("fake-memstats-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Calculating partition sizes")) Expect(err.Error()).To(ContainSubstring("fake-memstats-error")) Expect(partitioner.PartitionCalled).To(BeFalse()) Expect(formatter.FormatCalled).To(BeFalse()) Expect(mounter.MountCalled).To(BeFalse()) }) It("returns an error when partitioning fails", func() { partitioner.PartitionErr = errors.New("fake-partition-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Partitioning root device `/dev/vda'")) Expect(err.Error()).To(ContainSubstring("fake-partition-error")) Expect(formatter.FormatCalled).To(BeFalse()) Expect(mounter.MountCalled).To(BeFalse()) }) It("formats swap and data partitions", func() { err := act() Expect(err).NotTo(HaveOccurred()) Expect(len(formatter.FormatPartitionPaths)).To(Equal(2)) Expect(formatter.FormatPartitionPaths[0]).To(Equal("/dev/vda2")) Expect(formatter.FormatPartitionPaths[1]).To(Equal("/dev/vda3")) Expect(len(formatter.FormatFsTypes)).To(Equal(2)) Expect(formatter.FormatFsTypes[0]).To(Equal(boshdisk.FileSystemSwap)) Expect(formatter.FormatFsTypes[1]).To(Equal(boshdisk.FileSystemExt4)) }) It("mounts swap and data partitions", func() { err := act() Expect(err).NotTo(HaveOccurred()) Expect(len(mounter.MountMountPoints)).To(Equal(1)) Expect(mounter.MountMountPoints[0]).To(Equal("/fake-dir/data")) Expect(len(mounter.MountPartitionPaths)).To(Equal(1)) Expect(mounter.MountPartitionPaths[0]).To(Equal("/dev/vda3")) Expect(len(mounter.SwapOnPartitionPaths)).To(Equal(1)) Expect(mounter.SwapOnPartitionPaths[0]).To(Equal("/dev/vda2")) }) It("creates swap the size of the memory and the rest for data when disk is bigger than twice the memory", func() { memSizeInBytes := uint64(1024 * 1024 * 1024) diskSizeInBytes := 2*memSizeInBytes + 64 partitioner.GetDeviceSizeInBytesSizes["/dev/vda"] = diskSizeInBytes collector.MemStats.Total = memSizeInBytes err := act() Expect(err).ToNot(HaveOccurred()) Expect(partitioner.PartitionDevicePath).To(Equal("/dev/vda")) Expect(partitioner.PartitionPartitions).To(ContainElement( boshdisk.Partition{ SizeInBytes: memSizeInBytes, Type: boshdisk.PartitionTypeSwap, }), ) Expect(partitioner.PartitionPartitions).To(ContainElement( boshdisk.Partition{ SizeInBytes: diskSizeInBytes - memSizeInBytes, Type: boshdisk.PartitionTypeLinux, }), ) }) It("creates equal swap and data partitions when disk is twice the memory or smaller", func() { memSizeInBytes := uint64(1024 * 1024 * 1024) diskSizeInBytes := 2*memSizeInBytes - 64 partitioner.GetDeviceSizeInBytesSizes["/dev/vda"] = diskSizeInBytes collector.MemStats.Total = memSizeInBytes err := act() Expect(err).ToNot(HaveOccurred()) Expect(partitioner.PartitionDevicePath).To(Equal("/dev/vda")) Expect(partitioner.PartitionPartitions).To(ContainElement( boshdisk.Partition{ SizeInBytes: diskSizeInBytes / 2, Type: boshdisk.PartitionTypeSwap, }), ) Expect(partitioner.PartitionPartitions).To(ContainElement( boshdisk.Partition{ SizeInBytes: diskSizeInBytes / 2, Type: boshdisk.PartitionTypeLinux, }), ) }) }) Context("when getting root device remaining size fails", func() { BeforeEach(func() { partitioner.GetDeviceSizeInBytesErr = errors.New("fake-get-remaining-size-error") }) It("returns an error", func() { err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Getting root device remaining size")) Expect(err.Error()).To(ContainSubstring("fake-get-remaining-size-error")) Expect(partitioner.PartitionCalled).To(BeFalse()) Expect(formatter.FormatCalled).To(BeFalse()) Expect(mounter.MountCalled).To(BeFalse()) }) }) }) }) It("returns error if creating data dir fails", func() { fs.MkdirAllError = errors.New("fake-mkdir-all-err") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-mkdir-all-err")) Expect(partitioner.PartitionCalled).To(BeFalse()) Expect(formatter.FormatCalled).To(BeFalse()) Expect(mounter.MountCalled).To(BeFalse()) }) It("returns err when the data directory cannot be globbed", func() { fs.GlobErr = errors.New("fake-glob-err") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Globbing ephemeral disk mount point `/fake-dir/data/*'")) Expect(err.Error()).To(ContainSubstring("fake-glob-err")) Expect(partitioner.PartitionCalled).To(BeFalse()) Expect(formatter.FormatCalled).To(BeFalse()) Expect(mounter.MountCalled).To(BeFalse()) }) }) Context("when agent should not partition ephemeral disk on root disk", func() { BeforeEach(func() { options.CreatePartitionIfNoEphemeralDisk = false }) It("returns an error", func() { err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("cannot use root partition as ephemeral disk")) }) It("does not try to partition anything", func() { err := act() Expect(err).To(HaveOccurred()) Expect(partitioner.PartitionCalled).To(BeFalse()) }) It("does not try to format anything", func() { err := act() Expect(err).To(HaveOccurred()) Expect(formatter.FormatCalled).To(BeFalse()) }) It("does not try to mount anything", func() { err := act() Expect(err).To(HaveOccurred()) Expect(mounter.MountCalled).To(BeFalse()) }) }) }) }) Describe("SetupDataDir", func() { var mounter *fakedisk.FakeMounter BeforeEach(func() { mounter = diskManager.FakeMounter }) Context("when sys/run is already mounted", func() { BeforeEach(func() { mounter.IsMountPointResult = true }) It("creates sys/log directory in data directory", func() { err := platform.SetupDataDir() Expect(err).NotTo(HaveOccurred()) sysLogStats := fs.GetFileTestStat("/fake-dir/data/sys/log") Expect(sysLogStats).ToNot(BeNil()) Expect(sysLogStats.FileType).To(Equal(fakesys.FakeFileTypeDir)) Expect(sysLogStats.FileMode).To(Equal(os.FileMode(0750))) Expect(cmdRunner.RunCommands[0]).To(Equal([]string{"chown", "root:vcap", "/fake-dir/data/sys"})) Expect(cmdRunner.RunCommands[1]).To(Equal([]string{"chown", "root:vcap", "/fake-dir/data/sys/log"})) }) It("creates symlink from sys to data/sys", func() { err := platform.SetupDataDir() Expect(err).NotTo(HaveOccurred()) sysStats := fs.GetFileTestStat("/fake-dir/sys") Expect(sysStats).ToNot(BeNil()) Expect(sysStats.FileType).To(Equal(fakesys.FakeFileTypeSymlink)) Expect(sysStats.SymlinkTarget).To(Equal("/fake-dir/data/sys")) }) It("does not create new sys/run dir", func() { err := platform.SetupDataDir() Expect(err).NotTo(HaveOccurred()) sysRunStats := fs.GetFileTestStat("/fake-dir/data/sys/run") Expect(sysRunStats).To(BeNil()) }) It("does not mount tmpfs again", func() { err := platform.SetupDataDir() Expect(err).NotTo(HaveOccurred()) Expect(len(mounter.MountPartitionPaths)).To(Equal(0)) }) }) Context("when sys/run is not yet mounted", func() { BeforeEach(func() { mounter.IsMountPointResult = false }) It("creates sys/log directory in data directory", func() { err := platform.SetupDataDir() Expect(err).NotTo(HaveOccurred()) sysLogStats := fs.GetFileTestStat("/fake-dir/data/sys/log") Expect(sysLogStats).ToNot(BeNil()) Expect(sysLogStats.FileType).To(Equal(fakesys.FakeFileTypeDir)) Expect(sysLogStats.FileMode).To(Equal(os.FileMode(0750))) Expect(cmdRunner.RunCommands[0]).To(Equal([]string{"chown", "root:vcap", "/fake-dir/data/sys"})) Expect(cmdRunner.RunCommands[1]).To(Equal([]string{"chown", "root:vcap", "/fake-dir/data/sys/log"})) }) It("creates symlink from sys to data/sys", func() { err := platform.SetupDataDir() Expect(err).NotTo(HaveOccurred()) sysStats := fs.GetFileTestStat("/fake-dir/sys") Expect(sysStats).ToNot(BeNil()) Expect(sysStats.FileType).To(Equal(fakesys.FakeFileTypeSymlink)) Expect(sysStats.SymlinkTarget).To(Equal("/fake-dir/data/sys")) }) It("creates new sys/run dir", func() { err := platform.SetupDataDir() Expect(err).NotTo(HaveOccurred()) sysRunStats := fs.GetFileTestStat("/fake-dir/data/sys/run") Expect(sysRunStats).ToNot(BeNil()) Expect(sysRunStats.FileType).To(Equal(fakesys.FakeFileTypeDir)) Expect(sysRunStats.FileMode).To(Equal(os.FileMode(0750))) Expect(cmdRunner.RunCommands[2]).To(Equal([]string{"chown", "root:vcap", "/fake-dir/data/sys/run"})) }) It("mounts tmpfs to sys/run", func() { err := platform.SetupDataDir() Expect(err).NotTo(HaveOccurred()) Expect(len(mounter.MountPartitionPaths)).To(Equal(1)) Expect(mounter.MountPartitionPaths[0]).To(Equal("tmpfs")) Expect(mounter.MountMountPoints[0]).To(Equal("/fake-dir/data/sys/run")) Expect(mounter.MountMountOptions[0]).To(Equal([]string{"-t", "tmpfs", "-o", "size=1m"})) }) It("returns an error if creation of mount point fails", func() { fs.MkdirAllError = errors.New("fake-mkdir-error") err := platform.SetupDataDir() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-mkdir-error")) }) It("returns an error if mounting tmpfs fails", func() { mounter.MountErr = errors.New("fake-mount-error") err := platform.SetupDataDir() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-mount-error")) }) }) }) Describe("SetupTmpDir", func() { act := func() error { return platform.SetupTmpDir() } var mounter *fakedisk.FakeMounter BeforeEach(func() { mounter = diskManager.FakeMounter }) It("changes permissions on /tmp", func() { err := act() Expect(err).NotTo(HaveOccurred()) Expect(cmdRunner.RunCommands[0]).To(Equal([]string{"chown", "root:vcap", "/tmp"})) Expect(cmdRunner.RunCommands[1]).To(Equal([]string{"chmod", "0770", "/tmp"})) Expect(cmdRunner.RunCommands[2]).To(Equal([]string{"chmod", "0700", "/var/tmp"})) }) It("creates new temp dir", func() { err := act() Expect(err).NotTo(HaveOccurred()) fileStats := fs.GetFileTestStat("/fake-dir/data/tmp") Expect(fileStats).NotTo(BeNil()) Expect(fileStats.FileType).To(Equal(fakesys.FakeFileType(fakesys.FakeFileTypeDir))) Expect(fileStats.FileMode).To(Equal(os.FileMode(0755))) }) It("returns error if creating new temp dir errs", func() { fs.MkdirAllError = errors.New("fake-mkdir-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-mkdir-error")) }) It("sets TMPDIR environment variable so that children of this process will use new temp dir", func() { err := act() Expect(err).NotTo(HaveOccurred()) Expect(os.Getenv("TMPDIR")).To(Equal("/fake-dir/data/tmp")) }) It("returns error if setting TMPDIR errs", func() { // uses os package; no way to trigger err }) ItDoesNotTryToUseLoopDevice := func() { It("does not create new tmp filesystem", func() { act() for _, cmd := range cmdRunner.RunCommands { Expect(cmd[0]).ToNot(Equal("truncate")) Expect(cmd[0]).ToNot(Equal("mke2fs")) } }) It("does not try to mount anything /tmp", func() { act() Expect(len(mounter.MountPartitionPaths)).To(Equal(0)) }) } Context("when UseDefaultTmpDir option is set to false", func() { BeforeEach(func() { options.UseDefaultTmpDir = false }) Context("when /tmp is not a mount point", func() { BeforeEach(func() { mounter.IsMountPointResult = false }) It("creates new tmp filesystem of 128MB placed in data dir", func() { err := act() Expect(err).NotTo(HaveOccurred()) Expect(cmdRunner.RunCommands[3]).To(Equal([]string{"truncate", "-s", "128M", "/fake-dir/data/root_tmp"})) Expect(cmdRunner.RunCommands[4]).To(Equal([]string{"chmod", "0700", "/fake-dir/data/root_tmp"})) Expect(cmdRunner.RunCommands[5]).To(Equal([]string{"mke2fs", "-t", "ext4", "-m", "1", "-F", "/fake-dir/data/root_tmp"})) }) It("mounts the new tmp filesystem over /tmp", func() { err := act() Expect(err).NotTo(HaveOccurred()) Expect(len(mounter.MountPartitionPaths)).To(Equal(1)) Expect(mounter.MountPartitionPaths[0]).To(Equal("/fake-dir/data/root_tmp")) Expect(mounter.MountMountPoints[0]).To(Equal("/tmp")) Expect(mounter.MountMountOptions[0]).To(Equal([]string{"-t", "ext4", "-o", "loop"})) }) It("returns error if mounting the new tmp filesystem fails", func() { mounter.MountErr = errors.New("fake-mount-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-mount-error")) }) It("changes permissions on /tmp again because it is a new mount", func() { err := act() Expect(err).NotTo(HaveOccurred()) Expect(cmdRunner.RunCommands[6]).To(Equal([]string{"chown", "root:vcap", "/tmp"})) Expect(cmdRunner.RunCommands[7]).To(Equal([]string{"chmod", "0770", "/tmp"})) }) }) Context("when /tmp is a mount point", func() { BeforeEach(func() { mounter.IsMountPointResult = true }) It("returns without an error", func() { err := act() Expect(err).ToNot(HaveOccurred()) }) ItDoesNotTryToUseLoopDevice() }) Context("when /tmp cannot be determined if it is a mount point", func() { BeforeEach(func() { mounter.IsMountPointErr = errors.New("fake-is-mount-point-error") }) It("returns error", func() { err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-is-mount-point-error")) }) ItDoesNotTryToUseLoopDevice() }) }) Context("when UseDefaultTmpDir option is set to true", func() { BeforeEach(func() { options.UseDefaultTmpDir = true }) It("returns without an error", func() { err := act() Expect(err).ToNot(HaveOccurred()) }) ItDoesNotTryToUseLoopDevice() }) }) Describe("MountPersistentDisk", func() { act := func() error { return platform.MountPersistentDisk( boshsettings.DiskSettings{Path: "fake-volume-id"}, "/mnt/point", ) } var ( partitioner *fakedisk.FakePartitioner formatter *fakedisk.FakeFormatter mounter *fakedisk.FakeMounter ) BeforeEach(func() { partitioner = diskManager.FakePartitioner formatter = diskManager.FakeFormatter mounter = diskManager.FakeMounter }) Context("when device path is successfully resolved", func() { BeforeEach(func() { devicePathResolver.RealDevicePath = "fake-real-device-path" }) Context("when UsePreformattedPersistentDisk set to false", func() { It("creates the mount directory with the correct permissions", func() { err := act() Expect(err).ToNot(HaveOccurred()) mountPoint := fs.GetFileTestStat("/mnt/point") Expect(mountPoint.FileType).To(Equal(fakesys.FakeFileTypeDir)) Expect(mountPoint.FileMode).To(Equal(os.FileMode(0700))) }) It("returns error when creating mount directory fails", func() { fs.MkdirAllError = errors.New("fake-mkdir-all-err") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-mkdir-all-err")) }) It("partitions the disk", func() { err := act() Expect(err).ToNot(HaveOccurred()) partitions := []boshdisk.Partition{{Type: boshdisk.PartitionTypeLinux}} Expect(partitioner.PartitionDevicePath).To(Equal("fake-real-device-path")) Expect(partitioner.PartitionPartitions).To(Equal(partitions)) }) It("formats the disk", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(formatter.FormatPartitionPaths).To(Equal([]string{"fake-real-device-path1"})) Expect(formatter.FormatFsTypes).To(Equal([]boshdisk.FileSystemType{boshdisk.FileSystemExt4})) }) It("mounts the disk", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(mounter.MountPartitionPaths).To(Equal([]string{"fake-real-device-path1"})) Expect(mounter.MountMountPoints).To(Equal([]string{"/mnt/point"})) Expect(mounter.MountMountOptions).To(Equal([][]string{nil})) }) }) Context("when UsePreformattedPersistentDisk set to true", func() { BeforeEach(func() { options.UsePreformattedPersistentDisk = true }) It("creates the mount directory with the correct permissions", func() { err := act() Expect(err).ToNot(HaveOccurred()) mountPoint := fs.GetFileTestStat("/mnt/point") Expect(mountPoint.FileType).To(Equal(fakesys.FakeFileTypeDir)) Expect(mountPoint.FileMode).To(Equal(os.FileMode(0700))) }) It("returns error when creating mount directory fails", func() { fs.MkdirAllError = errors.New("fake-mkdir-all-err") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-mkdir-all-err")) }) It("mounts volume at mount point", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(len(mounter.MountPartitionPaths)).To(Equal(1)) Expect(mounter.MountPartitionPaths).To(Equal([]string{"fake-real-device-path"})) // no '1' because no partition Expect(mounter.MountMountPoints).To(Equal([]string{"/mnt/point"})) Expect(mounter.MountMountOptions).To(Equal([][]string{nil})) }) It("returns error when mounting fails", func() { mounter.MountErr = errors.New("fake-mount-err") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-mount-err")) }) It("does not partition the disk", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(partitioner.PartitionCalled).To(BeFalse()) }) It("does not format the disk", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(formatter.FormatCalled).To(BeFalse()) }) }) }) Context("when device path is not successfully resolved", func() { It("return an error", func() { devicePathResolver.GetRealDevicePathErr = errors.New("fake-get-real-device-path-err") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-get-real-device-path-err")) }) }) }) Describe("UnmountPersistentDisk", func() { act := func() (bool, error) { return platform.UnmountPersistentDisk(boshsettings.DiskSettings{Path: "fake-device-path"}) } var mounter *fakedisk.FakeMounter BeforeEach(func() { mounter = diskManager.FakeMounter }) Context("when device path can be resolved", func() { BeforeEach(func() { devicePathResolver.RealDevicePath = "fake-real-device-path" }) ItUnmountsPersistentDisk := func(expectedUnmountMountPoint string) { It("returs true without an error if unmounting succeeded", func() { mounter.UnmountDidUnmount = true didUnmount, err := act() Expect(err).NotTo(HaveOccurred()) Expect(didUnmount).To(BeTrue()) Expect(mounter.UnmountPartitionPathOrMountPoint).To(Equal(expectedUnmountMountPoint)) }) It("returs false without an error if was already unmounted", func() { mounter.UnmountDidUnmount = false didUnmount, err := act() Expect(err).NotTo(HaveOccurred()) Expect(didUnmount).To(BeFalse()) Expect(mounter.UnmountPartitionPathOrMountPoint).To(Equal(expectedUnmountMountPoint)) }) It("returns error if unmounting fails", func() { mounter.UnmountDidUnmount = false mounter.UnmountErr = errors.New("fake-unmount-err") didUnmount, err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-unmount-err")) Expect(didUnmount).To(BeFalse()) Expect(mounter.UnmountPartitionPathOrMountPoint).To(Equal(expectedUnmountMountPoint)) }) } Context("UsePreformattedPersistentDisk is set to false", func() { ItUnmountsPersistentDisk("fake-real-device-path1") // note partition '1' }) Context("UsePreformattedPersistentDisk is set to true", func() { BeforeEach(func() { options.UsePreformattedPersistentDisk = true }) ItUnmountsPersistentDisk("fake-real-device-path") // note no '1'; no partitions }) }) Context("when device path cannot be resolved", func() { BeforeEach(func() { devicePathResolver.GetRealDevicePathErr = errors.New("fake-get-real-device-path-err") devicePathResolver.GetRealDevicePathTimedOut = false }) It("returns error", func() { isMounted, err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-get-real-device-path-err")) Expect(isMounted).To(BeFalse()) }) }) Context("when device path cannot be resolved due to timeout", func() { BeforeEach(func() { devicePathResolver.GetRealDevicePathErr = errors.New("fake-get-real-device-path-err") devicePathResolver.GetRealDevicePathTimedOut = true }) It("does not return error", func() { isMounted, err := act() Expect(err).NotTo(HaveOccurred()) Expect(isMounted).To(BeFalse()) }) }) }) Describe("GetFileContentsFromCDROM", func() { It("delegates to cdutil", func() { cdutil.GetFilesContentsContents = [][]byte{[]byte("fake-contents")} filename := "fake-env" contents, err := platform.GetFileContentsFromCDROM(filename) Expect(err).NotTo(HaveOccurred()) Expect(cdutil.GetFilesContentsFileNames[0]).To(Equal(filename)) Expect(contents).To(Equal([]byte("fake-contents"))) }) }) Describe("GetFilesContentsFromDisk", func() { It("delegates to diskutil", func() { diskManager.FakeDiskUtil.GetFilesContentsContents = [][]byte{ []byte("fake-contents-1"), []byte("fake-contents-2"), } contents, err := platform.GetFilesContentsFromDisk( "fake-disk-path", []string{"fake-file-path-1", "fake-file-path-2"}, ) Expect(err).NotTo(HaveOccurred()) Expect(diskManager.DiskUtilDiskPath).To(Equal("fake-disk-path")) Expect(diskManager.FakeDiskUtil.GetFilesContentsFileNames).To(Equal( []string{"fake-file-path-1", "fake-file-path-2"}, )) Expect(contents).To(Equal([][]byte{ []byte("fake-contents-1"), []byte("fake-contents-2"), })) }) }) Describe("GetEphemeralDiskPath", func() { Context("when device path is an empty string", func() { It("returns an empty string", func() { devicePathResolver.RealDevicePath = "non-desired-device-path" diskSettings := boshsettings.DiskSettings{ ID: "fake-id", VolumeID: "fake-volume-id", Path: "", } Expect(platform.GetEphemeralDiskPath(diskSettings)).To(BeEmpty()) }) }) Context("when real device path was resolved without an error", func() { It("returns real device path and true", func() { devicePathResolver.RealDevicePath = "fake-real-device-path" realPath := platform.GetEphemeralDiskPath(boshsettings.DiskSettings{Path: "fake-device-path"}) Expect(realPath).To(Equal("fake-real-device-path")) }) }) Context("when real device path was not resolved without an error", func() { It("returns real device path and true", func() { devicePathResolver.GetRealDevicePathErr = errors.New("fake-get-real-device-path-err") realPath := platform.GetEphemeralDiskPath(boshsettings.DiskSettings{Path: "fake-device-path"}) Expect(realPath).To(Equal("")) }) }) }) Describe("MigratePersistentDisk", func() { var mounter *fakedisk.FakeMounter BeforeEach(func() { mounter = diskManager.FakeMounter }) It("migrate persistent disk", func() { err := platform.MigratePersistentDisk("/from/path", "/to/path") Expect(err).ToNot(HaveOccurred()) Expect(mounter.RemountAsReadonlyPath).To(Equal("/from/path")) Expect(len(cmdRunner.RunCommands)).To(Equal(1)) Expect(cmdRunner.RunCommands[0]).To(Equal([]string{"sh", "-c", "(tar -C /from/path -cf - .) | (tar -C /to/path -xpf -)"})) Expect(mounter.UnmountPartitionPathOrMountPoint).To(Equal("/from/path")) Expect(mounter.RemountFromMountPoint).To(Equal("/to/path")) Expect(mounter.RemountToMountPoint).To(Equal("/from/path")) }) }) Describe("IsPersistentDiskMounted", func() { act := func() (bool, error) { return platform.IsPersistentDiskMounted(boshsettings.DiskSettings{Path: "fake-device-path"}) } var mounter *fakedisk.FakeMounter BeforeEach(func() { mounter = diskManager.FakeMounter }) Context("when device path can be resolved", func() { BeforeEach(func() { devicePathResolver.RealDevicePath = "fake-real-device-path" }) ItChecksPersistentDiskMountPoint := func(expectedCheckedMountPoint string) { Context("when checking persistent disk mount point succeeds", func() { It("returns true if mount point exists", func() { mounter.IsMountedResult = true isMounted, err := act() Expect(err).NotTo(HaveOccurred()) Expect(isMounted).To(BeTrue()) Expect(mounter.IsMountedDevicePathOrMountPoint).To(Equal(expectedCheckedMountPoint)) }) It("returns false if mount point does not exist", func() { mounter.IsMountedResult = false isMounted, err := act() Expect(err).NotTo(HaveOccurred()) Expect(isMounted).To(BeFalse()) Expect(mounter.IsMountedDevicePathOrMountPoint).To(Equal(expectedCheckedMountPoint)) }) }) Context("checking persistent disk mount points fails", func() { It("returns error", func() { mounter.IsMountedResult = false mounter.IsMountedErr = errors.New("fake-is-mounted-err") isMounted, err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-is-mounted-err")) Expect(isMounted).To(BeFalse()) Expect(mounter.IsMountedDevicePathOrMountPoint).To(Equal(expectedCheckedMountPoint)) }) }) } Context("UsePreformattedPersistentDisk is set to false", func() { ItChecksPersistentDiskMountPoint("fake-real-device-path1") // note partition '1' }) Context("UsePreformattedPersistentDisk is set to true", func() { BeforeEach(func() { options.UsePreformattedPersistentDisk = true }) ItChecksPersistentDiskMountPoint("fake-real-device-path") // note no '1'; no partitions }) }) Context("when device path cannot be resolved", func() { BeforeEach(func() { devicePathResolver.GetRealDevicePathErr = errors.New("fake-get-real-device-path-err") devicePathResolver.GetRealDevicePathTimedOut = false }) It("returns error", func() { isMounted, err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-get-real-device-path-err")) Expect(isMounted).To(BeFalse()) }) }) Context("when device path cannot be resolved due to timeout", func() { BeforeEach(func() { devicePathResolver.GetRealDevicePathErr = errors.New("fake-get-real-device-path-err") devicePathResolver.GetRealDevicePathTimedOut = true }) It("does not return error", func() { isMounted, err := act() Expect(err).NotTo(HaveOccurred()) Expect(isMounted).To(BeFalse()) }) }) }) Describe("StartMonit", func() { It("creates a symlink between /etc/service/monit and /etc/sv/monit", func() { err := platform.StartMonit() Expect(err).NotTo(HaveOccurred()) target, _ := fs.ReadLink(filepath.Join("/etc", "service", "monit")) Expect(target).To(Equal(filepath.Join("/etc", "sv", "monit"))) }) It("retries to start monit", func() { err := platform.StartMonit() Expect(err).NotTo(HaveOccurred()) Expect(monitRetryStrategy.TryCalled).To(BeTrue()) }) It("returns error if retrying to start monit fails", func() { monitRetryStrategy.TryErr = errors.New("fake-retry-monit-error") err := platform.StartMonit() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-retry-monit-error")) }) }) Describe("SetupMonitUser", func() { It("setup monit user if file does not exist", func() { err := platform.SetupMonitUser() Expect(err).NotTo(HaveOccurred()) monitUserFileStats := fs.GetFileTestStat("/fake-dir/monit/monit.user") Expect(monitUserFileStats).ToNot(BeNil()) Expect(monitUserFileStats.StringContents()).To(Equal("vcap:random-password")) }) It("setup monit user if file does exist", func() { fs.WriteFileString("/fake-dir/monit/monit.user", "vcap:other-random-password") err := platform.SetupMonitUser() Expect(err).NotTo(HaveOccurred()) monitUserFileStats := fs.GetFileTestStat("/fake-dir/monit/monit.user") Expect(monitUserFileStats).ToNot(BeNil()) Expect(monitUserFileStats.StringContents()).To(Equal("vcap:other-random-password")) }) }) Describe("GetMonitCredentials", func() { It("get monit credentials reads monit file from disk", func() { fs.WriteFileString("/fake-dir/monit/monit.user", "fake-user:fake-random-password") username, password, err := platform.GetMonitCredentials() Expect(err).NotTo(HaveOccurred()) Expect(username).To(Equal("fake-user")) Expect(password).To(Equal("fake-random-password")) }) It("get monit credentials errs when invalid file format", func() { fs.WriteFileString("/fake-dir/monit/monit.user", "fake-user") _, _, err := platform.GetMonitCredentials() Expect(err).To(HaveOccurred()) }) It("get monit credentials leaves colons in password intact", func() { fs.WriteFileString("/fake-dir/monit/monit.user", "fake-user:fake:random:password") username, password, err := platform.GetMonitCredentials() Expect(err).NotTo(HaveOccurred()) Expect(username).To(Equal("fake-user")) Expect(password).To(Equal("fake:random:password")) }) }) Describe("PrepareForNetworkingChange", func() { It("removes the network persistent rules file", func() { fs.WriteFile("/etc/udev/rules.d/70-persistent-net.rules", []byte{}) err := platform.PrepareForNetworkingChange() Expect(err).NotTo(HaveOccurred()) Expect(fs.FileExists("/etc/udev/rules.d/70-persistent-net.rules")).To(BeFalse()) }) It("returns error if removing persistent rules file fails", func() { fs.RemoveAllError = errors.New("fake-remove-all-error") err := platform.PrepareForNetworkingChange() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-remove-all-error")) }) }) Describe("SetupNetworking", func() { It("delegates to the NetManager", func() { networks := boshsettings.Networks{} err := platform.SetupNetworking(networks) Expect(err).ToNot(HaveOccurred()) Expect(netManager.SetupNetworkingNetworks).To(Equal(networks)) }) }) Describe("GetConfiguredNetworkInterfaces", func() { It("delegates to the NetManager", func() { netmanagerInterfaces := []string{"fake-eth0", "fake-eth1"} netManager.GetConfiguredNetworkInterfacesInterfaces = netmanagerInterfaces interfaces, err := platform.GetConfiguredNetworkInterfaces() Expect(err).ToNot(HaveOccurred()) Expect(interfaces).To(Equal(netmanagerInterfaces)) }) }) Describe("GetDefaultNetwork", func() { It("delegates to the defaultNetworkResolver", func() { defaultNetwork := boshsettings.Network{IP: "1.2.3.4"} fakeDefaultNetworkResolver.GetDefaultNetworkNetwork = defaultNetwork network, err := platform.GetDefaultNetwork() Expect(err).ToNot(HaveOccurred()) Expect(network).To(Equal(defaultNetwork)) }) }) }