예제 #1
0
func NewWindowsPlatform(
	collector boshstats.Collector,
	fs boshsys.FileSystem,
	cmdRunner boshsys.CmdRunner,
	dirProvider boshdirs.Provider,
	netManager boshnet.Manager,
	certManager boshcert.Manager,
	devicePathResolver boshdpresolv.DevicePathResolver,
	logger boshlog.Logger,
	defaultNetworkResolver boshsettings.DefaultNetworkResolver,
) Platform {
	return &WindowsPlatform{
		fs:                     fs,
		cmdRunner:              cmdRunner,
		collector:              collector,
		compressor:             boshcmd.NewTarballCompressor(cmdRunner, fs),
		copier:                 boshcmd.NewGenericCpCopier(fs, logger),
		dirProvider:            dirProvider,
		netManager:             netManager,
		devicePathResolver:     devicePathResolver,
		vitalsService:          boshvitals.NewService(collector, dirProvider),
		certManager:            certManager,
		defaultNetworkResolver: defaultNetworkResolver,
	}
}
예제 #2
0
func NewDummyPlatform(
	collector boshstats.StatsCollector,
	fs boshsys.FileSystem,
	cmdRunner boshsys.CmdRunner,
	dirProvider boshdirs.DirectoriesProvider,
	logger boshlog.Logger,
) *dummyPlatform {
	return &dummyPlatform{
		fs:            fs,
		cmdRunner:     cmdRunner,
		collector:     collector,
		compressor:    boshcmd.NewTarballCompressor(cmdRunner, fs),
		copier:        boshcmd.NewCpCopier(cmdRunner, fs, logger),
		dirProvider:   dirProvider,
		vitalsService: boshvitals.NewService(collector, dirProvider),
	}
}
예제 #3
0
func NewDummyPlatform(
	collector boshstats.Collector,
	fs boshsys.FileSystem,
	cmdRunner boshsys.CmdRunner,
	dirProvider boshdirs.Provider,
	devicePathResolver boshdpresolv.DevicePathResolver,
	logger boshlog.Logger,
) Platform {
	return &dummyPlatform{
		fs:                 fs,
		cmdRunner:          cmdRunner,
		collector:          collector,
		compressor:         boshcmd.NewTarballCompressor(cmdRunner, fs),
		copier:             boshcmd.NewCpCopier(cmdRunner, fs, logger),
		dirProvider:        dirProvider,
		devicePathResolver: devicePathResolver,
		vitalsService:      boshvitals.NewService(collector, dirProvider),
		certManager:        boshcert.NewDummyCertManager(fs, cmdRunner, 0, logger),
	}
}
예제 #4
0
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())
					})
				})
			})
		})
	})
}
예제 #5
0
func NewProvider(logger boshlog.Logger, dirProvider boshdirs.DirectoriesProvider, options ProviderOptions) (p provider) {
	runner := boshsys.NewExecCmdRunner(logger)
	fs := boshsys.NewOsFileSystem(logger)

	linuxDiskManager := boshdisk.NewLinuxDiskManager(logger, runner, fs, options.Linux.BindMountPersistentDisk)

	udev := boshudev.NewConcreteUdevDevice(runner)
	linuxCdrom := boshcdrom.NewLinuxCdrom("/dev/sr0", udev, runner)
	linuxCdutil := boshcd.NewCdUtil(dirProvider.SettingsDir(), fs, linuxCdrom)

	compressor := boshcmd.NewTarballCompressor(runner, fs)
	copier := boshcmd.NewCpCopier(runner, fs, logger)

	sigarCollector := boshstats.NewSigarStatsCollector(&sigar.ConcreteSigar{})

	// Kick of stats collection as soon as possible
	go sigarCollector.StartCollecting(SigarStatsCollectionInterval, nil)

	vitalsService := boshvitals.NewService(sigarCollector, dirProvider)

	routesSearcher := boshnet.NewCmdRoutesSearcher(runner)
	ipResolver := boship.NewIPResolver(boship.NetworkInterfaceToAddrsFunc)

	defaultNetworkResolver := boshnet.NewDefaultNetworkResolver(routesSearcher, ipResolver)
	arping := bosharp.NewArping(runner, fs, logger, ArpIterations, ArpIterationDelay, ArpInterfaceCheckDelay)

	centosNetManager := boshnet.NewCentosNetManager(fs, runner, defaultNetworkResolver, ipResolver, arping, logger)
	ubuntuNetManager := boshnet.NewUbuntuNetManager(fs, runner, defaultNetworkResolver, ipResolver, arping, logger)

	centos := NewLinuxPlatform(
		fs,
		runner,
		sigarCollector,
		compressor,
		copier,
		dirProvider,
		vitalsService,
		linuxCdutil,
		linuxDiskManager,
		centosNetManager,
		500*time.Millisecond,
		options.Linux,
		logger,
	)

	ubuntu := NewLinuxPlatform(
		fs,
		runner,
		sigarCollector,
		compressor,
		copier,
		dirProvider,
		vitalsService,
		linuxCdutil,
		linuxDiskManager,
		ubuntuNetManager,
		500*time.Millisecond,
		options.Linux,
		logger,
	)

	p.platforms = map[string]Platform{
		"ubuntu": ubuntu,
		"centos": centos,
		"dummy":  NewDummyPlatform(sigarCollector, fs, runner, dirProvider, logger),
	}
	return
}
예제 #6
0
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))
		})
	})
}
예제 #7
0
func NewProvider(logger boshlog.Logger, dirProvider boshdirs.Provider, statsCollector boshstats.Collector, fs boshsys.FileSystem, options Options, bootstrapState *BootstrapState, clock clock.Clock) Provider {
	runner := boshsys.NewExecCmdRunner(logger)
	linuxDiskManager := boshdisk.NewLinuxDiskManager(logger, runner, fs, options.Linux.BindMountPersistentDisk)

	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)

	// Kick of stats collection as soon as possible
	statsCollector.StartCollecting(SigarStatsCollectionInterval, nil)

	vitalsService := boshvitals.NewService(statsCollector, dirProvider)

	ipResolver := boship.NewResolver(boship.NetworkInterfaceToAddrsFunc)

	arping := bosharp.NewArping(runner, fs, logger, ArpIterations, ArpIterationDelay, ArpInterfaceCheckDelay)
	interfaceConfigurationCreator := boshnet.NewInterfaceConfigurationCreator(logger)

	interfaceAddressesProvider := boship.NewSystemInterfaceAddressesProvider()
	interfaceAddressesValidator := boship.NewInterfaceAddressesValidator(interfaceAddressesProvider)
	dnsValidator := boshnet.NewDNSValidator(fs)

	centosNetManager := boshnet.NewCentosNetManager(fs, runner, ipResolver, interfaceConfigurationCreator, interfaceAddressesValidator, dnsValidator, arping, logger)
	ubuntuNetManager := boshnet.NewUbuntuNetManager(fs, runner, ipResolver, interfaceConfigurationCreator, interfaceAddressesValidator, dnsValidator, arping, logger)

	windowsNetManager := boshnet.NewWindowsNetManager(runner, interfaceConfigurationCreator, boshnet.NewMACAddressDetector(), logger, clock)

	centosCertManager := boshcert.NewCentOSCertManager(fs, runner, 0, logger)
	ubuntuCertManager := boshcert.NewUbuntuCertManager(fs, runner, 60, logger)

	routesSearcher := boshnet.NewRoutesSearcher(runner)
	defaultNetworkResolver := boshnet.NewDefaultNetworkResolver(routesSearcher, ipResolver)

	monitRetryable := NewMonitRetryable(runner)
	monitRetryStrategy := boshretry.NewAttemptRetryStrategy(10, 1*time.Second, monitRetryable, logger)

	var devicePathResolver devicepathresolver.DevicePathResolver
	switch options.Linux.DevicePathResolutionType {
	case "virtio":
		udev := boshudev.NewConcreteUdevDevice(runner, logger)
		idDevicePathResolver := devicepathresolver.NewIDDevicePathResolver(500*time.Millisecond, options.Linux.VirtioDevicePrefix, udev, fs)
		mappedDevicePathResolver := devicepathresolver.NewMappedDevicePathResolver(30000*time.Millisecond, fs)
		devicePathResolver = devicepathresolver.NewVirtioDevicePathResolver(idDevicePathResolver, mappedDevicePathResolver, logger)
	case "scsi":
		scsiIDPathResolver := devicepathresolver.NewSCSIIDDevicePathResolver(50000*time.Millisecond, fs, logger)
		scsiVolumeIDPathResolver := devicepathresolver.NewSCSIVolumeIDDevicePathResolver(500*time.Millisecond, fs)
		scsiLunPathResolver := devicepathresolver.NewSCSILunDevicePathResolver(50000*time.Millisecond, fs, logger)
		devicePathResolver = devicepathresolver.NewScsiDevicePathResolver(scsiVolumeIDPathResolver, scsiIDPathResolver, scsiLunPathResolver)
	default:
		devicePathResolver = devicepathresolver.NewIdentityDevicePathResolver()
	}

	uuidGenerator := boshuuid.NewGenerator()

	centos := NewLinuxPlatform(
		fs,
		runner,
		statsCollector,
		compressor,
		copier,
		dirProvider,
		vitalsService,
		linuxCdutil,
		linuxDiskManager,
		centosNetManager,
		centosCertManager,
		monitRetryStrategy,
		devicePathResolver,
		bootstrapState,
		options.Linux,
		logger,
		defaultNetworkResolver,
		uuidGenerator,
	)

	ubuntu := NewLinuxPlatform(
		fs,
		runner,
		statsCollector,
		compressor,
		copier,
		dirProvider,
		vitalsService,
		linuxCdutil,
		linuxDiskManager,
		ubuntuNetManager,
		ubuntuCertManager,
		monitRetryStrategy,
		devicePathResolver,
		bootstrapState,
		options.Linux,
		logger,
		defaultNetworkResolver,
		uuidGenerator,
	)

	windows := NewWindowsPlatform(
		statsCollector,
		fs,
		runner,
		dirProvider,
		windowsNetManager,
		devicePathResolver,
		logger,
		defaultNetworkResolver,
	)

	return provider{
		platforms: map[string]Platform{
			"ubuntu":  ubuntu,
			"centos":  centos,
			"dummy":   NewDummyPlatform(statsCollector, fs, runner, dirProvider, devicePathResolver, logger),
			"windows": windows,
		},
	}
}